From 728dc4075accb2821b595f650b5a6a64f42a9abe Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 13 Mar 2022 11:18:13 +0100 Subject: media: platform: rename mtk-vcodec/ to mediatek/mtk-vcodec/ As the end goal is to have platform drivers split by vendor, rename mtk-vcodec/ to mediatek/mtk-vcodec/. Acked-by: Nicolas Dufresne Signed-off-by: Mauro Carvalho Chehab --- drivers/media/platform/Kconfig | 2 +- drivers/media/platform/Makefile | 2 +- drivers/media/platform/mediatek/mtk-vcodec/Kconfig | 36 + .../media/platform/mediatek/mtk-vcodec/Makefile | 42 + .../platform/mediatek/mtk-vcodec/mtk_vcodec_dec.c | 961 +++++++++++++ .../platform/mediatek/mtk-vcodec/mtk_vcodec_dec.h | 100 ++ .../mediatek/mtk-vcodec/mtk_vcodec_dec_drv.c | 509 +++++++ .../mediatek/mtk-vcodec/mtk_vcodec_dec_hw.c | 200 +++ .../mediatek/mtk-vcodec/mtk_vcodec_dec_hw.h | 56 + .../mediatek/mtk-vcodec/mtk_vcodec_dec_pm.c | 169 +++ .../mediatek/mtk-vcodec/mtk_vcodec_dec_pm.h | 19 + .../mediatek/mtk-vcodec/mtk_vcodec_dec_stateful.c | 630 +++++++++ .../mediatek/mtk-vcodec/mtk_vcodec_dec_stateless.c | 380 +++++ .../platform/mediatek/mtk-vcodec/mtk_vcodec_drv.h | 537 ++++++++ .../platform/mediatek/mtk-vcodec/mtk_vcodec_enc.c | 1451 ++++++++++++++++++++ .../platform/mediatek/mtk-vcodec/mtk_vcodec_enc.h | 50 + .../mediatek/mtk-vcodec/mtk_vcodec_enc_drv.c | 479 +++++++ .../mediatek/mtk-vcodec/mtk_vcodec_enc_pm.c | 90 ++ .../mediatek/mtk-vcodec/mtk_vcodec_enc_pm.h | 17 + .../platform/mediatek/mtk-vcodec/mtk_vcodec_fw.c | 67 + .../platform/mediatek/mtk-vcodec/mtk_vcodec_fw.h | 43 + .../mediatek/mtk-vcodec/mtk_vcodec_fw_priv.h | 52 + .../mediatek/mtk-vcodec/mtk_vcodec_fw_scp.c | 73 + .../mediatek/mtk-vcodec/mtk_vcodec_fw_vpu.c | 112 ++ .../platform/mediatek/mtk-vcodec/mtk_vcodec_intr.c | 43 + .../platform/mediatek/mtk-vcodec/mtk_vcodec_intr.h | 19 + .../platform/mediatek/mtk-vcodec/mtk_vcodec_util.c | 135 ++ .../platform/mediatek/mtk-vcodec/mtk_vcodec_util.h | 63 + .../mediatek/mtk-vcodec/vdec/vdec_h264_if.c | 503 +++++++ .../mediatek/mtk-vcodec/vdec/vdec_h264_req_if.c | 774 +++++++++++ .../mediatek/mtk-vcodec/vdec/vdec_vp8_if.c | 616 +++++++++ .../mediatek/mtk-vcodec/vdec/vdec_vp9_if.c | 1028 ++++++++++++++ .../platform/mediatek/mtk-vcodec/vdec_drv_base.h | 46 + .../platform/mediatek/mtk-vcodec/vdec_drv_if.c | 113 ++ .../platform/mediatek/mtk-vcodec/vdec_drv_if.h | 100 ++ .../platform/mediatek/mtk-vcodec/vdec_ipi_msg.h | 117 ++ .../platform/mediatek/mtk-vcodec/vdec_msg_queue.c | 290 ++++ .../platform/mediatek/mtk-vcodec/vdec_msg_queue.h | 153 +++ .../platform/mediatek/mtk-vcodec/vdec_vpu_if.c | 243 ++++ .../platform/mediatek/mtk-vcodec/vdec_vpu_if.h | 107 ++ .../mediatek/mtk-vcodec/venc/venc_h264_if.c | 708 ++++++++++ .../mediatek/mtk-vcodec/venc/venc_vp8_if.c | 468 +++++++ .../platform/mediatek/mtk-vcodec/venc_drv_base.h | 53 + .../platform/mediatek/mtk-vcodec/venc_drv_if.c | 100 ++ .../platform/mediatek/mtk-vcodec/venc_drv_if.h | 170 +++ .../platform/mediatek/mtk-vcodec/venc_ipi_msg.h | 220 +++ .../platform/mediatek/mtk-vcodec/venc_vpu_if.c | 293 ++++ .../platform/mediatek/mtk-vcodec/venc_vpu_if.h | 51 + drivers/media/platform/mtk-vcodec/Kconfig | 36 - drivers/media/platform/mtk-vcodec/Makefile | 42 - drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c | 961 ------------- drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h | 100 -- .../media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c | 509 ------- .../media/platform/mtk-vcodec/mtk_vcodec_dec_hw.c | 200 --- .../media/platform/mtk-vcodec/mtk_vcodec_dec_hw.h | 56 - .../media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c | 169 --- .../media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h | 19 - .../platform/mtk-vcodec/mtk_vcodec_dec_stateful.c | 630 --------- .../platform/mtk-vcodec/mtk_vcodec_dec_stateless.c | 380 ----- drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h | 537 -------- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c | 1451 -------------------- drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h | 50 - .../media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c | 479 ------- .../media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c | 90 -- .../media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h | 17 - drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c | 67 - drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h | 43 - .../media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h | 52 - .../media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c | 73 - .../media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c | 112 -- .../media/platform/mtk-vcodec/mtk_vcodec_intr.c | 43 - .../media/platform/mtk-vcodec/mtk_vcodec_intr.h | 19 - .../media/platform/mtk-vcodec/mtk_vcodec_util.c | 135 -- .../media/platform/mtk-vcodec/mtk_vcodec_util.h | 63 - .../media/platform/mtk-vcodec/vdec/vdec_h264_if.c | 503 ------- .../platform/mtk-vcodec/vdec/vdec_h264_req_if.c | 774 ----------- .../media/platform/mtk-vcodec/vdec/vdec_vp8_if.c | 616 --------- .../media/platform/mtk-vcodec/vdec/vdec_vp9_if.c | 1028 -------------- drivers/media/platform/mtk-vcodec/vdec_drv_base.h | 46 - drivers/media/platform/mtk-vcodec/vdec_drv_if.c | 113 -- drivers/media/platform/mtk-vcodec/vdec_drv_if.h | 100 -- drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h | 117 -- drivers/media/platform/mtk-vcodec/vdec_msg_queue.c | 290 ---- drivers/media/platform/mtk-vcodec/vdec_msg_queue.h | 153 --- drivers/media/platform/mtk-vcodec/vdec_vpu_if.c | 243 ---- drivers/media/platform/mtk-vcodec/vdec_vpu_if.h | 107 -- .../media/platform/mtk-vcodec/venc/venc_h264_if.c | 708 ---------- .../media/platform/mtk-vcodec/venc/venc_vp8_if.c | 468 ------- drivers/media/platform/mtk-vcodec/venc_drv_base.h | 53 - drivers/media/platform/mtk-vcodec/venc_drv_if.c | 100 -- drivers/media/platform/mtk-vcodec/venc_drv_if.h | 170 --- drivers/media/platform/mtk-vcodec/venc_ipi_msg.h | 220 --- drivers/media/platform/mtk-vcodec/venc_vpu_if.c | 293 ---- drivers/media/platform/mtk-vcodec/venc_vpu_if.h | 51 - 94 files changed, 12488 insertions(+), 12488 deletions(-) create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/Kconfig create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/Makefile create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_drv.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateful.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateless.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_drv.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_drv.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_priv.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_scp.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_vpu.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_req_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp8_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp9_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_base.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_ipi_msg.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc/venc_h264_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc/venc_vp8_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc_drv_base.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc_ipi_msg.h create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.c create mode 100644 drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.h delete mode 100644 drivers/media/platform/mtk-vcodec/Kconfig delete mode 100644 drivers/media/platform/mtk-vcodec/Makefile delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c delete mode 100644 drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h delete mode 100644 drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_drv_base.h delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_drv_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_drv_if.h delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_msg_queue.c delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_msg_queue.h delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_vpu_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/vdec_vpu_if.h delete mode 100644 drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/venc_drv_base.h delete mode 100644 drivers/media/platform/mtk-vcodec/venc_drv_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/venc_drv_if.h delete mode 100644 drivers/media/platform/mtk-vcodec/venc_ipi_msg.h delete mode 100644 drivers/media/platform/mtk-vcodec/venc_vpu_if.c delete mode 100644 drivers/media/platform/mtk-vcodec/venc_vpu_if.h (limited to 'drivers/media/platform') diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 8a7ac0b8e09f..10055af0c7f8 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -80,7 +80,7 @@ source "drivers/media/platform/intel/Kconfig" source "drivers/media/platform/marvell/Kconfig" source "drivers/media/platform/mediatek/mtk-jpeg/Kconfig" source "drivers/media/platform/mediatek/mtk-mdp/Kconfig" -source "drivers/media/platform/mtk-vcodec/Kconfig" +source "drivers/media/platform/mediatek/mtk-vcodec/Kconfig" source "drivers/media/platform/mtk-vpu/Kconfig" source "drivers/media/platform/nxp/Kconfig" source "drivers/media/platform/omap/Kconfig" diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index 6f5d09cd8f9b..637a3a7ac036 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -20,7 +20,7 @@ obj-y += intel/ obj-y += marvell/ obj-y += mediatek/mtk-jpeg/ obj-y += mediatek/mtk-mdp/ -obj-y += mtk-vcodec/ +obj-y += mediatek/mtk-vcodec/ obj-y += mtk-vpu/ obj-y += nxp/ obj-y += omap/ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/Kconfig b/drivers/media/platform/mediatek/mtk-vcodec/Kconfig new file mode 100644 index 000000000000..635801a19d55 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/Kconfig @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0-only +config VIDEO_MEDIATEK_VCODEC_SCP + bool + +config VIDEO_MEDIATEK_VCODEC_VPU + bool + +config VIDEO_MEDIATEK_VCODEC + tristate "Mediatek Video Codec driver" + depends on V4L_MEM2MEM_DRIVERS + depends on MTK_IOMMU || COMPILE_TEST + depends on VIDEO_DEV && VIDEO_V4L2 + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on VIDEO_MEDIATEK_VPU || MTK_SCP + # The two following lines ensure we have the same state ("m" or "y") as + # our dependencies, to avoid missing symbols during link. + depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU + depends on MTK_SCP || !MTK_SCP + depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU + select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP + select V4L2_H264 + select MEDIA_CONTROLLER + select MEDIA_CONTROLLER_REQUEST_API + help + Mediatek video codec driver provides HW capability to + encode and decode in a range of video formats on MT8173 + and MT8183. + + Note that support for MT8173 requires VIDEO_MEDIATEK_VPU to + also be selected. Support for MT8183 depends on MTK_SCP. + + To compile this driver as modules, choose M here: the + modules will be called mtk-vcodec-dec and mtk-vcodec-enc. diff --git a/drivers/media/platform/mediatek/mtk-vcodec/Makefile b/drivers/media/platform/mediatek/mtk-vcodec/Makefile new file mode 100644 index 000000000000..359619653a0e --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/Makefile @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dec.o \ + mtk-vcodec-enc.o \ + mtk-vcodec-common.o \ + mtk-vcodec-dec-hw.o + +mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ + vdec/vdec_vp8_if.o \ + vdec/vdec_vp9_if.o \ + vdec/vdec_h264_req_if.o \ + mtk_vcodec_dec_drv.o \ + vdec_drv_if.o \ + vdec_vpu_if.o \ + vdec_msg_queue.o \ + mtk_vcodec_dec.o \ + mtk_vcodec_dec_stateful.o \ + mtk_vcodec_dec_stateless.o \ + mtk_vcodec_dec_pm.o \ + +mtk-vcodec-dec-hw-y := mtk_vcodec_dec_hw.o + +mtk-vcodec-enc-y := venc/venc_vp8_if.o \ + venc/venc_h264_if.o \ + mtk_vcodec_enc.o \ + mtk_vcodec_enc_drv.o \ + mtk_vcodec_enc_pm.o \ + venc_drv_if.o \ + venc_vpu_if.o \ + + +mtk-vcodec-common-y := mtk_vcodec_intr.o \ + mtk_vcodec_util.o \ + mtk_vcodec_fw.o \ + +ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU),) +mtk-vcodec-common-y += mtk_vcodec_fw_vpu.o +endif + +ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP),) +mtk-vcodec-common-y += mtk_vcodec_fw_scp.o +endif diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.c new file mode 100644 index 000000000000..130ecef2e766 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.c @@ -0,0 +1,961 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + * Tiffany Lin + */ + +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "vdec_drv_if.h" +#include "mtk_vcodec_dec_pm.h" + +#define DFT_CFG_WIDTH MTK_VDEC_MIN_W +#define DFT_CFG_HEIGHT MTK_VDEC_MIN_H + +static const struct mtk_video_fmt * +mtk_vdec_find_format(struct v4l2_format *f, + const struct mtk_vcodec_dec_pdata *dec_pdata) +{ + const struct mtk_video_fmt *fmt; + unsigned int k; + + for (k = 0; k < dec_pdata->num_formats; k++) { + fmt = &dec_pdata->vdec_formats[k]; + if (fmt->fourcc == f->fmt.pix_mp.pixelformat) + return fmt; + } + + return NULL; +} + +static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->q_data[MTK_Q_DATA_SRC]; + + return &ctx->q_data[MTK_Q_DATA_DST]; +} + +static int vidioc_try_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + /* Use M2M stateless helper if relevant */ + if (ctx->dev->vdec_pdata->uses_stateless_api) + return v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, + cmd); + else + return v4l2_m2m_ioctl_try_decoder_cmd(file, priv, cmd); +} + + +static int vidioc_decoder_cmd(struct file *file, void *priv, + struct v4l2_decoder_cmd *cmd) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *src_vq, *dst_vq; + int ret; + + ret = vidioc_try_decoder_cmd(file, priv, cmd); + if (ret) + return ret; + + /* Use M2M stateless helper if relevant */ + if (ctx->dev->vdec_pdata->uses_stateless_api) + return v4l2_m2m_ioctl_stateless_decoder_cmd(file, priv, cmd); + + mtk_v4l2_debug(1, "decoder cmd=%u", cmd->cmd); + dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + switch (cmd->cmd) { + case V4L2_DEC_CMD_STOP: + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!vb2_is_streaming(src_vq)) { + mtk_v4l2_debug(1, "Output stream is off. No need to flush."); + return 0; + } + if (!vb2_is_streaming(dst_vq)) { + mtk_v4l2_debug(1, "Capture stream is off. No need to flush."); + return 0; + } + v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf.vb); + v4l2_m2m_try_schedule(ctx->m2m_ctx); + break; + + case V4L2_DEC_CMD_START: + vb2_clear_last_buffer_dequeued(dst_vq); + break; + + default: + return -EINVAL; + } + + return 0; +} + +void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx) +{ + mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]); +} + +void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx) +{ + mutex_lock(&ctx->dev->dec_mutex[ctx->hw_id]); +} + +void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx) +{ + vdec_if_deinit(ctx); + ctx->state = MTK_STATE_FREE; +} + +void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_q_data *q_data; + + ctx->dev->vdec_pdata->init_vdec_params(ctx); + + ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; + ctx->fh.m2m_ctx = ctx->m2m_ctx; + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + INIT_WORK(&ctx->decode_work, ctx->dev->vdec_pdata->worker); + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + q_data = &ctx->q_data[MTK_Q_DATA_SRC]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->visible_width = DFT_CFG_WIDTH; + q_data->visible_height = DFT_CFG_HEIGHT; + q_data->fmt = ctx->dev->vdec_pdata->default_out_fmt; + q_data->field = V4L2_FIELD_NONE; + + q_data->sizeimage[0] = DFT_CFG_WIDTH * DFT_CFG_HEIGHT; + q_data->bytesperline[0] = 0; + + q_data = &ctx->q_data[MTK_Q_DATA_DST]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->visible_width = DFT_CFG_WIDTH; + q_data->visible_height = DFT_CFG_HEIGHT; + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->fmt = ctx->dev->vdec_pdata->default_cap_fmt; + q_data->field = V4L2_FIELD_NONE; + + v4l_bound_align_image(&q_data->coded_width, + MTK_VDEC_MIN_W, + MTK_VDEC_MAX_W, 4, + &q_data->coded_height, + MTK_VDEC_MIN_H, + MTK_VDEC_MAX_H, 5, 6); + + q_data->sizeimage[0] = q_data->coded_width * q_data->coded_height; + q_data->bytesperline[0] = q_data->coded_width; + q_data->sizeimage[1] = q_data->sizeimage[0] / 2; + q_data->bytesperline[1] = q_data->coded_width; +} + +static int vidioc_vdec_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_vdec_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on DQBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_vdec_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MTK_VCODEC_DEC_NAME, sizeof(cap->driver)); + strscpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); + strscpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); + + return 0; +} + +static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_src_change_event_subscribe(fh, sub); + default: + return v4l2_ctrl_subscribe_event(fh, sub); + } +} + +static int vidioc_try_fmt(struct v4l2_format *f, + const struct mtk_video_fmt *fmt) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + + pix_fmt_mp->field = V4L2_FIELD_NONE; + + pix_fmt_mp->width = + clamp(pix_fmt_mp->width, MTK_VDEC_MIN_W, MTK_VDEC_MAX_W); + pix_fmt_mp->height = + clamp(pix_fmt_mp->height, MTK_VDEC_MIN_H, MTK_VDEC_MAX_H); + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + pix_fmt_mp->num_planes = 1; + pix_fmt_mp->plane_fmt[0].bytesperline = 0; + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + int tmp_w, tmp_h; + + /* + * Find next closer width align 64, heign align 64, size align + * 64 rectangle + * Note: This only get default value, the real HW needed value + * only available when ctx in MTK_STATE_HEADER state + */ + tmp_w = pix_fmt_mp->width; + tmp_h = pix_fmt_mp->height; + v4l_bound_align_image(&pix_fmt_mp->width, + MTK_VDEC_MIN_W, + MTK_VDEC_MAX_W, 6, + &pix_fmt_mp->height, + MTK_VDEC_MIN_H, + MTK_VDEC_MAX_H, 6, 9); + + if (pix_fmt_mp->width < tmp_w && + (pix_fmt_mp->width + 64) <= MTK_VDEC_MAX_W) + pix_fmt_mp->width += 64; + if (pix_fmt_mp->height < tmp_h && + (pix_fmt_mp->height + 64) <= MTK_VDEC_MAX_H) + pix_fmt_mp->height += 64; + + mtk_v4l2_debug(0, + "before resize width=%d, height=%d, after resize width=%d, height=%d, sizeimage=%d", + tmp_w, tmp_h, pix_fmt_mp->width, + pix_fmt_mp->height, + pix_fmt_mp->width * pix_fmt_mp->height); + + pix_fmt_mp->num_planes = fmt->num_planes; + pix_fmt_mp->plane_fmt[0].sizeimage = + pix_fmt_mp->width * pix_fmt_mp->height; + pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; + + if (pix_fmt_mp->num_planes == 2) { + pix_fmt_mp->plane_fmt[1].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 2; + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->width; + } + } + + pix_fmt_mp->flags = 0; + return 0; +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; + + fmt = mtk_vdec_find_format(f, dec_pdata); + if (!fmt) { + f->fmt.pix.pixelformat = + ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc; + fmt = mtk_vdec_find_format(f, dec_pdata); + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + const struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; + + fmt = mtk_vdec_find_format(f, dec_pdata); + if (!fmt) { + f->fmt.pix.pixelformat = + ctx->q_data[MTK_Q_DATA_SRC].fmt->fourcc; + fmt = mtk_vdec_find_format(f, dec_pdata); + } + + if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { + mtk_v4l2_err("sizeimage of output format must be given"); + return -EINVAL; + } + + return vidioc_try_fmt(f, fmt); +} + +static int vidioc_vdec_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_q_data *q_data; + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + q_data = &ctx->q_data[MTK_Q_DATA_DST]; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + s->r.left = 0; + s->r.top = 0; + s->r.width = ctx->picinfo.pic_w; + s->r.height = ctx->picinfo.pic_h; + break; + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + s->r.left = 0; + s->r.top = 0; + s->r.width = ctx->picinfo.buf_w; + s->r.height = ctx->picinfo.buf_h; + break; + case V4L2_SEL_TGT_COMPOSE: + if (vdec_if_get_param(ctx, GET_PARAM_CROP_INFO, &(s->r))) { + /* set to default value if header info not ready yet*/ + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + } + break; + default: + return -EINVAL; + } + + if (ctx->state < MTK_STATE_HEADER) { + /* set to default value if header info not ready yet*/ + s->r.left = 0; + s->r.top = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + return 0; + } + + return 0; +} + +static int vidioc_vdec_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_COMPOSE: + s->r.left = 0; + s->r.top = 0; + s->r.width = ctx->picinfo.pic_w; + s->r.height = ctx->picinfo.pic_h; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_vdec_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format_mplane *pix_mp; + struct mtk_q_data *q_data; + int ret = 0; + const struct mtk_video_fmt *fmt; + const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; + + mtk_v4l2_debug(3, "[%d]", ctx->id); + + q_data = mtk_vdec_get_q_data(ctx, f->type); + if (!q_data) + return -EINVAL; + + pix_mp = &f->fmt.pix_mp; + /* + * Setting OUTPUT format after OUTPUT buffers are allocated is invalid + * if using the stateful API. + */ + if (!dec_pdata->uses_stateless_api && + f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + vb2_is_busy(&ctx->m2m_ctx->out_q_ctx.q)) { + mtk_v4l2_err("out_q_ctx buffers already requested"); + ret = -EBUSY; + } + + /* + * Setting CAPTURE format after CAPTURE buffers are allocated is + * invalid. + */ + if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && + vb2_is_busy(&ctx->m2m_ctx->cap_q_ctx.q)) { + mtk_v4l2_err("cap_q_ctx buffers already requested"); + ret = -EBUSY; + } + + fmt = mtk_vdec_find_format(f, dec_pdata); + if (fmt == NULL) { + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + f->fmt.pix.pixelformat = + dec_pdata->default_out_fmt->fourcc; + fmt = mtk_vdec_find_format(f, dec_pdata); + } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + f->fmt.pix.pixelformat = + dec_pdata->default_cap_fmt->fourcc; + fmt = mtk_vdec_find_format(f, dec_pdata); + } + } + if (fmt == NULL) + return -EINVAL; + + q_data->fmt = fmt; + vidioc_try_fmt(f, q_data->fmt); + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + q_data->sizeimage[0] = pix_mp->plane_fmt[0].sizeimage; + q_data->coded_width = pix_mp->width; + q_data->coded_height = pix_mp->height; + + ctx->colorspace = pix_mp->colorspace; + ctx->ycbcr_enc = pix_mp->ycbcr_enc; + ctx->quantization = pix_mp->quantization; + ctx->xfer_func = pix_mp->xfer_func; + + ctx->current_codec = fmt->fourcc; + if (ctx->state == MTK_STATE_FREE) { + ret = vdec_if_init(ctx, q_data->fmt->fourcc); + if (ret) { + mtk_v4l2_err("[%d]: vdec_if_init() fail ret=%d", + ctx->id, ret); + return -EINVAL; + } + ctx->state = MTK_STATE_INIT; + } + } + + /* + * If using the stateless API, S_FMT should have the effect of setting + * the CAPTURE queue resolution no matter which queue it was called on. + */ + if (dec_pdata->uses_stateless_api) { + ctx->picinfo.pic_w = pix_mp->width; + ctx->picinfo.pic_h = pix_mp->height; + + ret = vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo); + if (ret) { + mtk_v4l2_err("[%d]Error!! Get GET_PARAM_PICTURE_INFO Fail", + ctx->id); + return -EINVAL; + } + + ctx->last_decoded_picinfo = ctx->picinfo; + + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1) { + ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = + ctx->picinfo.fb_sz[0] + + ctx->picinfo.fb_sz[1]; + ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = + ctx->picinfo.buf_w; + } else { + ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = + ctx->picinfo.fb_sz[0]; + ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = + ctx->picinfo.buf_w; + ctx->q_data[MTK_Q_DATA_DST].sizeimage[1] = + ctx->picinfo.fb_sz[1]; + ctx->q_data[MTK_Q_DATA_DST].bytesperline[1] = + ctx->picinfo.buf_w; + } + + ctx->q_data[MTK_Q_DATA_DST].coded_width = ctx->picinfo.buf_w; + ctx->q_data[MTK_Q_DATA_DST].coded_height = ctx->picinfo.buf_h; + mtk_v4l2_debug(2, "[%d] vdec_if_init() num_plane = %d wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", + ctx->id, pix_mp->num_planes, ctx->picinfo.buf_w, ctx->picinfo.buf_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->q_data[MTK_Q_DATA_DST].sizeimage[0], + ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]); + } + return 0; +} + +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + int i = 0; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; + + if (fsize->index != 0) + return -EINVAL; + + for (i = 0; i < dec_pdata->num_framesizes; ++i) { + if (fsize->pixel_format != dec_pdata->vdec_framesizes[i].fourcc) + continue; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise = dec_pdata->vdec_framesizes[i].stepwise; + if (!(ctx->dev->dec_capability & + VCODEC_CAPABILITY_4K_DISABLED)) { + mtk_v4l2_debug(3, "4K is enabled"); + fsize->stepwise.max_width = + VCODEC_DEC_4K_CODED_WIDTH; + fsize->stepwise.max_height = + VCODEC_DEC_4K_CODED_HEIGHT; + } + mtk_v4l2_debug(1, "%x, %d %d %d %d %d %d", + ctx->dev->dec_capability, + fsize->stepwise.min_width, + fsize->stepwise.max_width, + fsize->stepwise.step_width, + fsize->stepwise.min_height, + fsize->stepwise.max_height, + fsize->stepwise.step_height); + return 0; + } + + return -EINVAL; +} + +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, void *priv, + bool output_queue) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; + const struct mtk_video_fmt *fmt; + int i, j = 0; + + for (i = 0; i < dec_pdata->num_formats; i++) { + if (output_queue && + dec_pdata->vdec_formats[i].type != MTK_FMT_DEC) + continue; + if (!output_queue && + dec_pdata->vdec_formats[i].type != MTK_FMT_FRAME) + continue; + + if (j == f->index) + break; + ++j; + } + + if (i == dec_pdata->num_formats) + return -EINVAL; + + fmt = &dec_pdata->vdec_formats[i]; + f->pixelformat = fmt->fourcc; + f->flags = fmt->flags; + + return 0; +} + +static int vidioc_vdec_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, priv, false); +} + +static int vidioc_vdec_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + return vidioc_enum_fmt(f, priv, true); +} + +static int vidioc_vdec_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; + struct vb2_queue *vq; + struct mtk_q_data *q_data; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + mtk_v4l2_err("no vb2 queue for type=%d", f->type); + return -EINVAL; + } + + q_data = mtk_vdec_get_q_data(ctx, f->type); + + pix_mp->field = V4L2_FIELD_NONE; + pix_mp->colorspace = ctx->colorspace; + pix_mp->ycbcr_enc = ctx->ycbcr_enc; + pix_mp->quantization = ctx->quantization; + pix_mp->xfer_func = ctx->xfer_func; + + if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && + (ctx->state >= MTK_STATE_HEADER)) { + /* Until STREAMOFF is called on the CAPTURE queue + * (acknowledging the event), the driver operates as if + * the resolution hasn't changed yet. + * So we just return picinfo yet, and update picinfo in + * stop_streaming hook function + */ + q_data->sizeimage[0] = ctx->picinfo.fb_sz[0]; + q_data->sizeimage[1] = ctx->picinfo.fb_sz[1]; + q_data->bytesperline[0] = ctx->last_decoded_picinfo.buf_w; + q_data->bytesperline[1] = ctx->last_decoded_picinfo.buf_w; + q_data->coded_width = ctx->picinfo.buf_w; + q_data->coded_height = ctx->picinfo.buf_h; + ctx->last_decoded_picinfo.cap_fourcc = q_data->fmt->fourcc; + + /* + * Width and height are set to the dimensions + * of the movie, the buffer is bigger and + * further processing stages should crop to this + * rectangle. + */ + pix_mp->width = q_data->coded_width; + pix_mp->height = q_data->coded_height; + + /* + * Set pixelformat to the format in which mt vcodec + * outputs the decoded frame + */ + pix_mp->num_planes = q_data->fmt->num_planes; + pix_mp->pixelformat = q_data->fmt->fourcc; + pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; + pix_mp->plane_fmt[1].bytesperline = q_data->bytesperline[1]; + pix_mp->plane_fmt[1].sizeimage = q_data->sizeimage[1]; + + } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + /* + * This is run on OUTPUT + * The buffer contains compressed image + * so width and height have no meaning. + * Assign value here to pass v4l2-compliance test + */ + pix_mp->width = q_data->visible_width; + pix_mp->height = q_data->visible_height; + pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; + pix_mp->pixelformat = q_data->fmt->fourcc; + pix_mp->num_planes = q_data->fmt->num_planes; + } else { + pix_mp->width = q_data->coded_width; + pix_mp->height = q_data->coded_height; + pix_mp->num_planes = q_data->fmt->num_planes; + pix_mp->pixelformat = q_data->fmt->fourcc; + pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; + pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; + pix_mp->plane_fmt[1].bytesperline = q_data->bytesperline[1]; + pix_mp->plane_fmt[1].sizeimage = q_data->sizeimage[1]; + + mtk_v4l2_debug(1, "[%d] type=%d state=%d Format information could not be read, not ready yet!", + ctx->id, f->type, ctx->state); + } + + return 0; +} + +int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); + struct mtk_q_data *q_data; + unsigned int i; + + q_data = mtk_vdec_get_q_data(ctx, vq->type); + + if (q_data == NULL) { + mtk_v4l2_err("vq->type=%d err\n", vq->type); + return -EINVAL; + } + + if (*nplanes) { + for (i = 0; i < *nplanes; i++) { + if (sizes[i] < q_data->sizeimage[i]) + return -EINVAL; + } + } else { + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + *nplanes = q_data->fmt->num_planes; + else + *nplanes = 1; + + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->sizeimage[i]; + } + + mtk_v4l2_debug(1, + "[%d]\t type = %d, get %d plane(s), %d buffer(s) of size 0x%x 0x%x ", + ctx->id, vq->type, *nplanes, *nbuffers, + sizes[0], sizes[1]); + + return 0; +} + +int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_q_data *q_data; + int i; + + mtk_v4l2_debug(3, "[%d] (%d) id=%d", + ctx->id, vb->vb2_queue->type, vb->index); + + q_data = mtk_vdec_get_q_data(ctx, vb->vb2_queue->type); + + for (i = 0; i < q_data->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { + mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", + i, vb2_plane_size(vb, i), + q_data->sizeimage[i]); + } + } + + return 0; +} + +void vb2ops_vdec_buf_finish(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2; + struct mtk_video_dec_buf *buf; + bool buf_error; + + vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, m2m_buf.vb); + mutex_lock(&ctx->lock); + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + buf->queued_in_v4l2 = false; + buf->queued_in_vb2 = false; + } + buf_error = buf->error; + mutex_unlock(&ctx->lock); + + if (buf_error) { + mtk_v4l2_err("Unrecoverable error on buffer."); + ctx->state = MTK_STATE_ABORT; + } +} + +int vb2ops_vdec_buf_init(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vb2_v4l2 = container_of(vb, + struct vb2_v4l2_buffer, vb2_buf); + struct mtk_video_dec_buf *buf = container_of(vb2_v4l2, + struct mtk_video_dec_buf, m2m_buf.vb); + + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + buf->used = false; + buf->queued_in_v4l2 = false; + } + + return 0; +} + +int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + + if (ctx->state == MTK_STATE_FLUSH) + ctx->state = MTK_STATE_HEADER; + + return 0; +} + +void vb2ops_vdec_stop_streaming(struct vb2_queue *q) +{ + struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL; + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + int ret; + + mtk_v4l2_debug(3, "[%d] (%d) state=(%x) ctx->decoded_frame_cnt=%d", + ctx->id, q->type, ctx->state, ctx->decoded_frame_cnt); + + if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { + if (src_buf != &ctx->empty_flush_buf.vb) { + struct media_request *req = + src_buf->vb2_buf.req_obj.req; + v4l2_m2m_buf_done(src_buf, + VB2_BUF_STATE_ERROR); + if (req) + v4l2_ctrl_request_complete(req, &ctx->ctrl_hdl); + } + } + return; + } + + if (ctx->state >= MTK_STATE_HEADER) { + + /* Until STREAMOFF is called on the CAPTURE queue + * (acknowledging the event), the driver operates + * as if the resolution hasn't changed yet, i.e. + * VIDIOC_G_FMT< etc. return previous resolution. + * So we update picinfo here + */ + ctx->picinfo = ctx->last_decoded_picinfo; + + mtk_v4l2_debug(2, + "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", + ctx->id, ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->last_decoded_picinfo.buf_w, + ctx->last_decoded_picinfo.buf_h); + + ret = ctx->dev->vdec_pdata->flush_decoder(ctx); + if (ret) + mtk_v4l2_err("DecodeFinal failed, ret=%d", ret); + } + ctx->state = MTK_STATE_FLUSH; + + while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } + +} + +static void m2mops_vdec_device_run(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + struct mtk_vcodec_dev *dev = ctx->dev; + + queue_work(dev->decode_workqueue, &ctx->decode_work); +} + +static int m2mops_vdec_job_ready(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + mtk_v4l2_debug(3, "[%d]", ctx->id); + + if (ctx->state == MTK_STATE_ABORT) + return 0; + + if ((ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w) || + (ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h)) + return 0; + + if (ctx->state != MTK_STATE_HEADER) + return 0; + + return 1; +} + +static void m2mops_vdec_job_abort(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + + ctx->state = MTK_STATE_ABORT; +} + +const struct v4l2_m2m_ops mtk_vdec_m2m_ops = { + .device_run = m2mops_vdec_device_run, + .job_ready = m2mops_vdec_job_ready, + .job_abort = m2mops_vdec_job_abort, +}; + +const struct v4l2_ioctl_ops mtk_vdec_ioctl_ops = { + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + + .vidioc_qbuf = vidioc_vdec_qbuf, + .vidioc_dqbuf = vidioc_vdec_dqbuf, + + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + + .vidioc_s_fmt_vid_cap_mplane = vidioc_vdec_s_fmt, + .vidioc_s_fmt_vid_out_mplane = vidioc_vdec_s_fmt, + .vidioc_g_fmt_vid_cap_mplane = vidioc_vdec_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_vdec_g_fmt, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + + .vidioc_enum_fmt_vid_cap = vidioc_vdec_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_vdec_enum_fmt_vid_out, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + + .vidioc_querycap = vidioc_vdec_querycap, + .vidioc_subscribe_event = vidioc_vdec_subscribe_evt, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_g_selection = vidioc_vdec_g_selection, + .vidioc_s_selection = vidioc_vdec_s_selection, + + .vidioc_decoder_cmd = vidioc_decoder_cmd, + .vidioc_try_decoder_cmd = vidioc_try_decoder_cmd, +}; + +int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct mtk_vcodec_ctx *ctx = priv; + int ret = 0; + + mtk_v4l2_debug(3, "[%d]", ctx->id); + + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct mtk_video_dec_buf); + src_vq->ops = ctx->dev->vdec_pdata->vdec_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = &ctx->dev->plat_dev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) { + mtk_v4l2_err("Failed to initialize videobuf2 queue(output)"); + return ret; + } + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct mtk_video_dec_buf); + dst_vq->ops = ctx->dev->vdec_pdata->vdec_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = &ctx->dev->plat_dev->dev; + + ret = vb2_queue_init(dst_vq); + if (ret) + mtk_v4l2_err("Failed to initialize videobuf2 queue(capture)"); + + return ret; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.h new file mode 100644 index 000000000000..66cd6d2242c3 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + * Tiffany Lin + */ + +#ifndef _MTK_VCODEC_DEC_H_ +#define _MTK_VCODEC_DEC_H_ + +#include +#include + +#define VCODEC_DEC_ALIGNED_64 64 +#define VCODEC_CAPABILITY_4K_DISABLED 0x10 +#define VCODEC_DEC_4K_CODED_WIDTH 4096U +#define VCODEC_DEC_4K_CODED_HEIGHT 2304U +#define MTK_VDEC_MAX_W 2048U +#define MTK_VDEC_MAX_H 1088U +#define MTK_VDEC_MIN_W 64U +#define MTK_VDEC_MIN_H 64U + +#define MTK_VDEC_IRQ_STATUS_DEC_SUCCESS 0x10000 + +/** + * struct vdec_fb - decoder frame buffer + * @base_y : Y plane memory info + * @base_c : C plane memory info + * @status : frame buffer status (vdec_fb_status) + */ +struct vdec_fb { + struct mtk_vcodec_mem base_y; + struct mtk_vcodec_mem base_c; + unsigned int status; +}; + +/** + * struct mtk_video_dec_buf - Private data related to each VB2 buffer. + * @m2m_buf: M2M buffer + * @list: link list + * @used: Capture buffer contain decoded frame data and keep in + * codec data structure + * @queued_in_vb2: Capture buffer is queue in vb2 + * @queued_in_v4l2: Capture buffer is in v4l2 driver, but not in vb2 + * queue yet + * @error: An unrecoverable error occurs on this buffer. + * @frame_buffer: Decode status, and buffer information of Capture buffer + * @bs_buffer: Output buffer info + * + * Note : These status information help us track and debug buffer state + */ +struct mtk_video_dec_buf { + struct v4l2_m2m_buffer m2m_buf; + + bool used; + bool queued_in_vb2; + bool queued_in_v4l2; + bool error; + + union { + struct vdec_fb frame_buffer; + struct mtk_vcodec_mem bs_buffer; + }; +}; + +extern const struct v4l2_ioctl_ops mtk_vdec_ioctl_ops; +extern const struct v4l2_m2m_ops mtk_vdec_m2m_ops; +extern const struct media_device_ops mtk_vcodec_media_ops; +extern const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata; +extern const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata; +extern const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata; + + +/* + * mtk_vdec_lock/mtk_vdec_unlock are for ctx instance to + * get/release lock before/after access decoder hw. + * mtk_vdec_lock get decoder hw lock and set curr_ctx + * to ctx instance that get lock + */ +void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx); +void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx); +int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx); +void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx); + +/* + * VB2 ops + */ +int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, + unsigned int *nplanes, unsigned int sizes[], + struct device *alloc_devs[]); +int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb); +void vb2ops_vdec_buf_finish(struct vb2_buffer *vb); +int vb2ops_vdec_buf_init(struct vb2_buffer *vb); +int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count); +void vb2ops_vdec_stop_streaming(struct vb2_queue *q); + + +#endif /* _MTK_VCODEC_DEC_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_drv.c new file mode 100644 index 000000000000..48dad9bb13d2 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_drv.c @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + * Tiffany Lin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec.h" +#include "mtk_vcodec_dec_hw.h" +#include "mtk_vcodec_dec_pm.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_fw.h" + +static int mtk_vcodec_get_hw_count(struct mtk_vcodec_dev *dev) +{ + switch (dev->vdec_pdata->hw_arch) { + case MTK_VDEC_PURE_SINGLE_CORE: + return MTK_VDEC_ONE_CORE; + case MTK_VDEC_LAT_SINGLE_CORE: + return MTK_VDEC_ONE_LAT_ONE_CORE; + default: + mtk_v4l2_err("hw arch %d not supported", dev->vdec_pdata->hw_arch); + return MTK_VDEC_NO_HW; + } +} + +static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + u32 cg_status = 0; + unsigned int dec_done_status = 0; + void __iomem *vdec_misc_addr = dev->reg_base[VDEC_MISC] + + VDEC_IRQ_CFG_REG; + + ctx = mtk_vcodec_get_curr_ctx(dev, MTK_VDEC_CORE); + + /* check if HW active or not */ + cg_status = readl(dev->reg_base[0]); + if ((cg_status & VDEC_HW_ACTIVE) != 0) { + mtk_v4l2_err("DEC ISR, VDEC active is not 0x0 (0x%08x)", + cg_status); + return IRQ_HANDLED; + } + + dec_done_status = readl(vdec_misc_addr); + ctx->irq_status = dec_done_status; + if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) != + MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) + return IRQ_HANDLED; + + /* clear interrupt */ + writel((readl(vdec_misc_addr) | VDEC_IRQ_CFG), + dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); + writel((readl(vdec_misc_addr) & ~VDEC_IRQ_CLR), + dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); + + mtk_v4l2_debug(3, + "mtk_vcodec_dec_irq_handler :wake up ctx %d, dec_done_status=%x", + ctx->id, dec_done_status); + + return IRQ_HANDLED; +} + +static int mtk_vcodec_get_reg_bases(struct mtk_vcodec_dev *dev) +{ + struct platform_device *pdev = dev->plat_dev; + int reg_num, i; + + /* Sizeof(u32) * 4 bytes for each register base. */ + reg_num = of_property_count_elems_of_size(pdev->dev.of_node, "reg", + sizeof(u32) * 4); + if (reg_num <= 0 || reg_num > NUM_MAX_VDEC_REG_BASE) { + dev_err(&pdev->dev, "Invalid register property size: %d\n", reg_num); + return -EINVAL; + } + + for (i = 0; i < reg_num; i++) { + dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); + if (IS_ERR(dev->reg_base[i])) + return PTR_ERR(dev->reg_base[i]); + + mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]); + } + + return 0; +} + +static int mtk_vcodec_init_dec_resources(struct mtk_vcodec_dev *dev) +{ + struct platform_device *pdev = dev->plat_dev; + int ret; + + ret = mtk_vcodec_get_reg_bases(dev); + if (ret) + return ret; + + if (dev->vdec_pdata->is_subdev_supported) + return 0; + + dev->dec_irq = platform_get_irq(pdev, 0); + if (dev->dec_irq < 0) { + dev_err(&pdev->dev, "failed to get irq number"); + return dev->dec_irq; + } + + irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, dev->dec_irq, + mtk_vcodec_dec_irq_handler, 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "failed to install dev->dec_irq %d (%d)", + dev->dec_irq, ret); + return ret; + } + + ret = mtk_vcodec_init_dec_clk(pdev, &dev->pm); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get mt vcodec clock source"); + return ret; + } + + pm_runtime_enable(&pdev->dev); + return 0; +} + +static int fops_vcodec_open(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = NULL; + int ret = 0, i, hw_count; + struct vb2_queue *src_vq; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_lock(&dev->dev_mutex); + ctx->id = dev->id_counter++; + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + INIT_LIST_HEAD(&ctx->list); + ctx->dev = dev; + if (ctx->dev->vdec_pdata->is_subdev_supported) { + hw_count = mtk_vcodec_get_hw_count(dev); + if (!hw_count || !dev->subdev_prob_done) { + ret = -EINVAL; + goto err_ctrls_setup; + } + + ret = dev->subdev_prob_done(dev); + if (ret) + goto err_ctrls_setup; + + for (i = 0; i < hw_count; i++) + init_waitqueue_head(&ctx->queue[i]); + } else { + init_waitqueue_head(&ctx->queue[0]); + } + mutex_init(&ctx->lock); + + ctx->type = MTK_INST_DECODER; + ret = dev->vdec_pdata->ctrls_setup(ctx); + if (ret) { + mtk_v4l2_err("Failed to setup mt vcodec controls"); + goto err_ctrls_setup; + } + ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_dec, ctx, + &mtk_vcodec_dec_queue_init); + if (IS_ERR((__force void *)ctx->m2m_ctx)) { + ret = PTR_ERR((__force void *)ctx->m2m_ctx); + mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", + ret); + goto err_m2m_ctx_init; + } + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + ctx->empty_flush_buf.vb.vb2_buf.vb2_queue = src_vq; + mtk_vcodec_dec_set_default_params(ctx); + + if (v4l2_fh_is_singular(&ctx->fh)) { + ret = mtk_vcodec_dec_pw_on(dev, MTK_VDEC_LAT0); + if (ret < 0) + goto err_load_fw; + /* + * Does nothing if firmware was already loaded. + */ + ret = mtk_vcodec_fw_load_firmware(dev->fw_handler); + if (ret < 0) { + /* + * Return 0 if downloading firmware successfully, + * otherwise it is failed + */ + mtk_v4l2_err("failed to load firmware!"); + goto err_load_fw; + } + + dev->dec_capability = + mtk_vcodec_fw_get_vdec_capa(dev->fw_handler); + mtk_v4l2_debug(0, "decoder capability %x", dev->dec_capability); + } + + list_add(&ctx->list, &dev->ctx_list); + + mutex_unlock(&dev->dev_mutex); + mtk_v4l2_debug(0, "%s decoder [%d]", dev_name(&dev->plat_dev->dev), + ctx->id); + return ret; + + /* Deinit when failure occurred */ +err_load_fw: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_m2m_ctx_init: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); +err_ctrls_setup: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int fops_vcodec_release(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); + + mtk_v4l2_debug(0, "[%d] decoder", ctx->id); + mutex_lock(&dev->dev_mutex); + + /* + * Call v4l2_m2m_ctx_release before mtk_vcodec_dec_release. First, it + * makes sure the worker thread is not running after vdec_if_deinit. + * Second, the decoder will be flushed and all the buffers will be + * returned in stop_streaming. + */ + v4l2_m2m_ctx_release(ctx->m2m_ctx); + mtk_vcodec_dec_release(ctx); + + if (v4l2_fh_is_singular(&ctx->fh)) + mtk_vcodec_dec_pw_off(dev, MTK_VDEC_LAT0); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + + list_del_init(&ctx->list); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + return 0; +} + +static const struct v4l2_file_operations mtk_vcodec_fops = { + .owner = THIS_MODULE, + .open = fops_vcodec_open, + .release = fops_vcodec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int mtk_vcodec_probe(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev; + struct video_device *vfd_dec; + phandle rproc_phandle; + enum mtk_vcodec_fw_type fw_type; + int i, ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->ctx_list); + dev->plat_dev = pdev; + + dev->vdec_pdata = of_device_get_match_data(&pdev->dev); + if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", + &rproc_phandle)) { + fw_type = VPU; + } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp", + &rproc_phandle)) { + fw_type = SCP; + } else { + mtk_v4l2_err("Could not get vdec IPI device"); + return -ENODEV; + } + dma_set_max_seg_size(&pdev->dev, UINT_MAX); + + dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, DECODER); + if (IS_ERR(dev->fw_handler)) + return PTR_ERR(dev->fw_handler); + + ret = mtk_vcodec_init_dec_resources(dev); + if (ret) { + dev_err(&pdev->dev, "Failed to init dec resources"); + goto err_dec_pm; + } + + if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) { + vdec_msg_queue_init_ctx(&dev->msg_queue_core_ctx, MTK_VDEC_CORE); + dev->core_workqueue = + alloc_ordered_workqueue("core-decoder", + WQ_MEM_RECLAIM | WQ_FREEZABLE); + if (!dev->core_workqueue) { + mtk_v4l2_err("Failed to create core workqueue"); + ret = -EINVAL; + goto err_res; + } + } + + if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); + if (ret) { + mtk_v4l2_err("Failed to set mask"); + goto err_core_workq; + } + } + + for (i = 0; i < MTK_VDEC_HW_MAX; i++) + mutex_init(&dev->dec_mutex[i]); + mutex_init(&dev->dev_mutex); + spin_lock_init(&dev->irqlock); + + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", + "[/MTK_V4L2_VDEC]"); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + mtk_v4l2_err("v4l2_device_register err=%d", ret); + goto err_core_workq; + } + + init_waitqueue_head(&dev->queue); + + vfd_dec = video_device_alloc(); + if (!vfd_dec) { + mtk_v4l2_err("Failed to allocate video device"); + ret = -ENOMEM; + goto err_dec_alloc; + } + vfd_dec->fops = &mtk_vcodec_fops; + vfd_dec->ioctl_ops = &mtk_vdec_ioctl_ops; + vfd_dec->release = video_device_release; + vfd_dec->lock = &dev->dev_mutex; + vfd_dec->v4l2_dev = &dev->v4l2_dev; + vfd_dec->vfl_dir = VFL_DIR_M2M; + vfd_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | + V4L2_CAP_STREAMING; + + snprintf(vfd_dec->name, sizeof(vfd_dec->name), "%s", + MTK_VCODEC_DEC_NAME); + video_set_drvdata(vfd_dec, dev); + dev->vfd_dec = vfd_dec; + platform_set_drvdata(pdev, dev); + + dev->m2m_dev_dec = v4l2_m2m_init(&mtk_vdec_m2m_ops); + if (IS_ERR((__force void *)dev->m2m_dev_dec)) { + mtk_v4l2_err("Failed to init mem2mem dec device"); + ret = PTR_ERR((__force void *)dev->m2m_dev_dec); + goto err_dec_alloc; + } + + dev->decode_workqueue = + alloc_ordered_workqueue(MTK_VCODEC_DEC_NAME, + WQ_MEM_RECLAIM | WQ_FREEZABLE); + if (!dev->decode_workqueue) { + mtk_v4l2_err("Failed to create decode workqueue"); + ret = -EINVAL; + goto err_event_workq; + } + + if (dev->vdec_pdata->is_subdev_supported) { + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, + &pdev->dev); + if (ret) { + mtk_v4l2_err("Main device of_platform_populate failed."); + goto err_reg_cont; + } + } + + ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1); + if (ret) { + mtk_v4l2_err("Failed to register video device"); + goto err_reg_cont; + } + + if (dev->vdec_pdata->uses_stateless_api) { + dev->mdev_dec.dev = &pdev->dev; + strscpy(dev->mdev_dec.model, MTK_VCODEC_DEC_NAME, + sizeof(dev->mdev_dec.model)); + + media_device_init(&dev->mdev_dec); + dev->mdev_dec.ops = &mtk_vcodec_media_ops; + dev->v4l2_dev.mdev = &dev->mdev_dec; + + ret = v4l2_m2m_register_media_controller(dev->m2m_dev_dec, dev->vfd_dec, + MEDIA_ENT_F_PROC_VIDEO_DECODER); + if (ret) { + mtk_v4l2_err("Failed to register media controller"); + goto err_dec_mem_init; + } + + ret = media_device_register(&dev->mdev_dec); + if (ret) { + mtk_v4l2_err("Failed to register media device"); + goto err_media_reg; + } + + mtk_v4l2_debug(0, "media registered as /dev/media%d", vfd_dec->minor); + } + + mtk_v4l2_debug(0, "decoder registered as /dev/video%d", vfd_dec->minor); + + return 0; + +err_media_reg: + v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec); +err_dec_mem_init: + video_unregister_device(vfd_dec); +err_reg_cont: + if (dev->vdec_pdata->uses_stateless_api) + media_device_cleanup(&dev->mdev_dec); + destroy_workqueue(dev->decode_workqueue); +err_event_workq: + v4l2_m2m_release(dev->m2m_dev_dec); +err_dec_alloc: + v4l2_device_unregister(&dev->v4l2_dev); +err_core_workq: + if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) + destroy_workqueue(dev->core_workqueue); +err_res: + pm_runtime_disable(dev->pm.dev); +err_dec_pm: + mtk_vcodec_fw_release(dev->fw_handler); + return ret; +} + +static const struct of_device_id mtk_vcodec_match[] = { + { + .compatible = "mediatek,mt8173-vcodec-dec", + .data = &mtk_vdec_8173_pdata, + }, + { + .compatible = "mediatek,mt8183-vcodec-dec", + .data = &mtk_vdec_8183_pdata, + }, + { + .compatible = "mediatek,mt8192-vcodec-dec", + .data = &mtk_lat_sig_core_pdata, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mtk_vcodec_match); + +static int mtk_vcodec_dec_remove(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); + + destroy_workqueue(dev->decode_workqueue); + + if (media_devnode_is_registered(dev->mdev_dec.devnode)) { + media_device_unregister(&dev->mdev_dec); + v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec); + media_device_cleanup(&dev->mdev_dec); + } + + if (dev->m2m_dev_dec) + v4l2_m2m_release(dev->m2m_dev_dec); + + if (dev->vfd_dec) + video_unregister_device(dev->vfd_dec); + + v4l2_device_unregister(&dev->v4l2_dev); + pm_runtime_disable(dev->pm.dev); + mtk_vcodec_fw_release(dev->fw_handler); + return 0; +} + +static struct platform_driver mtk_vcodec_dec_driver = { + .probe = mtk_vcodec_probe, + .remove = mtk_vcodec_dec_remove, + .driver = { + .name = MTK_VCODEC_DEC_NAME, + .of_match_table = mtk_vcodec_match, + }, +}; + +module_platform_driver(mtk_vcodec_dec_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video codec V4L2 decoder driver"); diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.c new file mode 100644 index 000000000000..8d2a641d92f1 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec.h" +#include "mtk_vcodec_dec_hw.h" +#include "mtk_vcodec_dec_pm.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" + +static const struct of_device_id mtk_vdec_hw_match[] = { + { + .compatible = "mediatek,mtk-vcodec-lat", + .data = (void *)MTK_VDEC_LAT0, + }, + { + .compatible = "mediatek,mtk-vcodec-core", + .data = (void *)MTK_VDEC_CORE, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vdec_hw_match); + +static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dev *vdec_dev) +{ + struct platform_device *pdev = vdec_dev->plat_dev; + struct device_node *subdev_node; + enum mtk_vdec_hw_id hw_idx; + const struct of_device_id *of_id; + int i; + + for (i = 0; i < ARRAY_SIZE(mtk_vdec_hw_match); i++) { + of_id = &mtk_vdec_hw_match[i]; + subdev_node = of_find_compatible_node(NULL, NULL, + of_id->compatible); + if (!subdev_node) + continue; + + hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; + if (!test_bit(hw_idx, vdec_dev->subdev_bitmap)) { + dev_err(&pdev->dev, "vdec %d is not ready", hw_idx); + return -EAGAIN; + } + } + + return 0; +} + +static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv) +{ + struct mtk_vdec_hw_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + u32 cg_status; + unsigned int dec_done_status; + void __iomem *vdec_misc_addr = dev->reg_base[VDEC_HW_MISC] + + VDEC_IRQ_CFG_REG; + + ctx = mtk_vcodec_get_curr_ctx(dev->main_dev, dev->hw_idx); + + /* check if HW active or not */ + cg_status = readl(dev->reg_base[VDEC_HW_SYS]); + if (cg_status & VDEC_HW_ACTIVE) { + mtk_v4l2_err("vdec active is not 0x0 (0x%08x)", + cg_status); + return IRQ_HANDLED; + } + + dec_done_status = readl(vdec_misc_addr); + if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) != + MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) + return IRQ_HANDLED; + + /* clear interrupt */ + writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr); + writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx); + + mtk_v4l2_debug(3, "wake up ctx %d, dec_done_status=%x", + ctx->id, dec_done_status); + + return IRQ_HANDLED; +} + +static int mtk_vdec_hw_init_irq(struct mtk_vdec_hw_dev *dev) +{ + struct platform_device *pdev = dev->plat_dev; + int ret; + + dev->dec_irq = platform_get_irq(pdev, 0); + if (dev->dec_irq < 0) { + dev_err(&pdev->dev, "Failed to get irq resource"); + return dev->dec_irq; + } + + irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, dev->dec_irq, + mtk_vdec_hw_irq_handler, 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)", + dev->dec_irq, ret); + return ret; + } + + return 0; +} + +static int mtk_vdec_hw_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_dev *main_dev; + const struct of_device_id *of_id; + int hw_idx; + int ret; + + if (!dev->parent) { + dev_err(dev, "no parent for hardware devices.\n"); + return -ENODEV; + } + + main_dev = dev_get_drvdata(dev->parent); + if (!main_dev) { + dev_err(dev, "failed to get parent driver data"); + return -EINVAL; + } + + subdev_dev = devm_kzalloc(dev, sizeof(*subdev_dev), GFP_KERNEL); + if (!subdev_dev) + return -ENOMEM; + + subdev_dev->plat_dev = pdev; + ret = mtk_vcodec_init_dec_clk(pdev, &subdev_dev->pm); + if (ret) + return ret; + pm_runtime_enable(&pdev->dev); + + of_id = of_match_device(mtk_vdec_hw_match, dev); + if (!of_id) { + dev_err(dev, "Can't get vdec subdev id.\n"); + ret = -EINVAL; + goto err; + } + + hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; + if (hw_idx >= MTK_VDEC_HW_MAX) { + dev_err(dev, "Hardware index %d not correct.\n", hw_idx); + ret = -EINVAL; + goto err; + } + + main_dev->subdev_dev[hw_idx] = subdev_dev; + subdev_dev->hw_idx = hw_idx; + subdev_dev->main_dev = main_dev; + subdev_dev->reg_base[VDEC_HW_SYS] = main_dev->reg_base[VDEC_HW_SYS]; + set_bit(subdev_dev->hw_idx, main_dev->subdev_bitmap); + + ret = mtk_vdec_hw_init_irq(subdev_dev); + if (ret) + goto err; + + subdev_dev->reg_base[VDEC_HW_MISC] = + devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC])) { + ret = PTR_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC]); + goto err; + } + + if (!main_dev->subdev_prob_done) + main_dev->subdev_prob_done = mtk_vdec_hw_prob_done; + + platform_set_drvdata(pdev, subdev_dev); + return 0; +err: + pm_runtime_disable(subdev_dev->pm.dev); + return ret; +} + +static struct platform_driver mtk_vdec_driver = { + .probe = mtk_vdec_hw_probe, + .driver = { + .name = "mtk-vdec-comp", + .of_match_table = mtk_vdec_hw_match, + }, +}; +module_platform_driver(mtk_vdec_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video decoder hardware driver"); diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.h new file mode 100644 index 000000000000..a63e4b1b81c3 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_hw.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong + */ + +#ifndef _MTK_VCODEC_DEC_HW_H_ +#define _MTK_VCODEC_DEC_HW_H_ + +#include +#include + +#include "mtk_vcodec_drv.h" + +#define VDEC_HW_ACTIVE 0x10 +#define VDEC_IRQ_CFG 0x11 +#define VDEC_IRQ_CLR 0x10 +#define VDEC_IRQ_CFG_REG 0xa4 + +/** + * enum mtk_vdec_hw_reg_idx - subdev hardware register base index + * @VDEC_HW_SYS : vdec soc register index + * @VDEC_HW_MISC: vdec misc register index + * @VDEC_HW_MAX : vdec supported max register index + */ +enum mtk_vdec_hw_reg_idx { + VDEC_HW_SYS, + VDEC_HW_MISC, + VDEC_HW_MAX +}; + +/** + * struct mtk_vdec_hw_dev - vdec hardware driver data + * @plat_dev: platform device + * @main_dev: main device + * @reg_base: mapped address of MTK Vcodec registers. + * + * @curr_ctx: the context that is waiting for codec hardware + * + * @dec_irq : decoder irq resource + * @pm : power management control + * @hw_idx : each hardware index + */ +struct mtk_vdec_hw_dev { + struct platform_device *plat_dev; + struct mtk_vcodec_dev *main_dev; + void __iomem *reg_base[VDEC_HW_MAX]; + + struct mtk_vcodec_ctx *curr_ctx; + + int dec_irq; + struct mtk_vcodec_pm pm; + int hw_idx; +}; + +#endif /* _MTK_VCODEC_DEC_HW_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.c new file mode 100644 index 000000000000..7e0c2644bf7b --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Tiffany Lin + */ + +#include +#include +#include +#include +#include + +#include "mtk_vcodec_dec_hw.h" +#include "mtk_vcodec_dec_pm.h" +#include "mtk_vcodec_util.h" + +int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm) +{ + struct mtk_vcodec_clk *dec_clk; + struct mtk_vcodec_clk_info *clk_info; + int i = 0, ret; + + dec_clk = &pm->vdec_clk; + pm->dev = &pdev->dev; + + dec_clk->clk_num = + of_property_count_strings(pdev->dev.of_node, "clock-names"); + if (dec_clk->clk_num > 0) { + dec_clk->clk_info = devm_kcalloc(&pdev->dev, + dec_clk->clk_num, sizeof(*clk_info), + GFP_KERNEL); + if (!dec_clk->clk_info) + return -ENOMEM; + } else { + mtk_v4l2_err("Failed to get vdec clock count"); + return -EINVAL; + } + + for (i = 0; i < dec_clk->clk_num; i++) { + clk_info = &dec_clk->clk_info[i]; + ret = of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clk_info->clk_name); + if (ret) { + mtk_v4l2_err("Failed to get clock name id = %d", i); + return ret; + } + clk_info->vcodec_clk = devm_clk_get(&pdev->dev, + clk_info->clk_name); + if (IS_ERR(clk_info->vcodec_clk)) { + mtk_v4l2_err("devm_clk_get (%d)%s fail", i, + clk_info->clk_name); + return PTR_ERR(clk_info->vcodec_clk); + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(mtk_vcodec_init_dec_clk); + +int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + int ret; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return -EINVAL; + } + pm = &subdev_dev->pm; + } else { + pm = &vdec_dev->pm; + } + + ret = pm_runtime_resume_and_get(pm->dev); + if (ret) + mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_on); + +void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + int ret; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return; + } + pm = &subdev_dev->pm; + } else { + pm = &vdec_dev->pm; + } + + ret = pm_runtime_put_sync(pm->dev); + if (ret) + mtk_v4l2_err("pm_runtime_put_sync fail %d", ret); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_off); + +void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + struct mtk_vcodec_clk *dec_clk; + int ret, i; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return; + } + pm = &subdev_dev->pm; + enable_irq(subdev_dev->dec_irq); + } else { + pm = &vdec_dev->pm; + enable_irq(vdec_dev->dec_irq); + } + + dec_clk = &pm->vdec_clk; + for (i = 0; i < dec_clk->clk_num; i++) { + ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk); + if (ret) { + mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i, + dec_clk->clk_info[i].clk_name, ret); + goto error; + } + } + + return; +error: + for (i -= 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_on); + +void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx) +{ + struct mtk_vdec_hw_dev *subdev_dev; + struct mtk_vcodec_pm *pm; + struct mtk_vcodec_clk *dec_clk; + int i; + + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev\n"); + return; + } + pm = &subdev_dev->pm; + disable_irq(subdev_dev->dec_irq); + } else { + pm = &vdec_dev->pm; + disable_irq(vdec_dev->dec_irq); + } + + dec_clk = &pm->vdec_clk; + for (i = dec_clk->clk_num - 1; i >= 0; i--) + clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_off); diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.h new file mode 100644 index 000000000000..3cc721bbfaf6 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_pm.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Tiffany Lin + */ + +#ifndef _MTK_VCODEC_DEC_PM_H_ +#define _MTK_VCODEC_DEC_PM_H_ + +#include "mtk_vcodec_drv.h" + +int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm); + +int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx); +void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx); +void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx); +void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx); + +#endif /* _MTK_VCODEC_DEC_PM_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateful.c new file mode 100644 index 000000000000..04ca43c77e5f --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateful.c @@ -0,0 +1,630 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_dec_pm.h" +#include "vdec_drv_if.h" + +static const struct mtk_video_fmt mtk_video_formats[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .type = MTK_FMT_DEC, + .num_planes = 1, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, + }, + { + .fourcc = V4L2_PIX_FMT_VP8, + .type = MTK_FMT_DEC, + .num_planes = 1, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, + }, + { + .fourcc = V4L2_PIX_FMT_VP9, + .type = MTK_FMT_DEC, + .num_planes = 1, + .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, + }, + { + .fourcc = V4L2_PIX_FMT_MT21C, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) +#define DEFAULT_OUT_FMT_IDX 0 +#define DEFAULT_CAP_FMT_IDX 3 + +static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, + MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, + }, + { + .fourcc = V4L2_PIX_FMT_VP8, + .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, + MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, + }, + { + .fourcc = V4L2_PIX_FMT_VP9, + .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, + MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, + }, +}; + +#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_vdec_framesizes) + +/* + * This function tries to clean all display buffers, the buffers will return + * in display order. + * Note the buffers returned from codec driver may still be in driver's + * reference list. + */ +static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx) +{ + struct vdec_fb *disp_frame_buffer = NULL; + struct mtk_video_dec_buf *dstbuf; + struct vb2_v4l2_buffer *vb; + + mtk_v4l2_debug(3, "[%d]", ctx->id); + if (vdec_if_get_param(ctx, GET_PARAM_DISP_FRAME_BUFFER, + &disp_frame_buffer)) { + mtk_v4l2_err("[%d]Cannot get param : GET_PARAM_DISP_FRAME_BUFFER", ctx->id); + return NULL; + } + + if (!disp_frame_buffer) { + mtk_v4l2_debug(3, "No display frame buffer"); + return NULL; + } + + dstbuf = container_of(disp_frame_buffer, struct mtk_video_dec_buf, + frame_buffer); + vb = &dstbuf->m2m_buf.vb; + mutex_lock(&ctx->lock); + if (dstbuf->used) { + vb2_set_plane_payload(&vb->vb2_buf, 0, ctx->picinfo.fb_sz[0]); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&vb->vb2_buf, 1, + ctx->picinfo.fb_sz[1]); + + mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to done_list %d", + ctx->id, disp_frame_buffer->status, + vb->vb2_buf.index, dstbuf->queued_in_vb2); + + v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE); + ctx->decoded_frame_cnt++; + } + mutex_unlock(&ctx->lock); + return &vb->vb2_buf; +} + +/* + * This function tries to clean all capture buffers that are not used as + * reference buffers by codec driver any more + * In this case, we need re-queue buffer to vb2 buffer if user space + * already returns this buffer to v4l2 or this buffer is just the output of + * previous sps/pps/resolution change decode, or do nothing if user + * space still owns this buffer + */ +static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_video_dec_buf *dstbuf; + struct vdec_fb *free_frame_buffer = NULL; + struct vb2_v4l2_buffer *vb; + + if (vdec_if_get_param(ctx, GET_PARAM_FREE_FRAME_BUFFER, + &free_frame_buffer)) { + mtk_v4l2_err("[%d] Error!! Cannot get param", ctx->id); + return NULL; + } + if (!free_frame_buffer) { + mtk_v4l2_debug(3, " No free frame buffer"); + return NULL; + } + + mtk_v4l2_debug(3, "[%d] tmp_frame_addr = 0x%p", ctx->id, + free_frame_buffer); + + dstbuf = container_of(free_frame_buffer, struct mtk_video_dec_buf, + frame_buffer); + vb = &dstbuf->m2m_buf.vb; + + mutex_lock(&ctx->lock); + if (dstbuf->used) { + if (dstbuf->queued_in_vb2 && dstbuf->queued_in_v4l2 && + free_frame_buffer->status == FB_ST_FREE) { + /* + * After decode sps/pps or non-display buffer, we don't + * need to return capture buffer to user space, but + * just re-queue this capture buffer to vb2 queue. + * This reduce overheads that dq/q unused capture + * buffer. In this case, queued_in_vb2 = true. + */ + mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to rdy_queue %d", + ctx->id, free_frame_buffer->status, + vb->vb2_buf.index, dstbuf->queued_in_vb2); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + } else if (!dstbuf->queued_in_vb2 && dstbuf->queued_in_v4l2) { + /* + * If buffer in v4l2 driver but not in vb2 queue yet, + * and we get this buffer from free_list, it means + * that codec driver do not use this buffer as + * reference buffer anymore. We should q buffer to vb2 + * queue, so later work thread could get this buffer + * for decode. In this case, queued_in_vb2 = false + * means this buffer is not from previous decode + * output. + */ + mtk_v4l2_debug(2, + "[%d]status=%x queue id=%d to rdy_queue", + ctx->id, free_frame_buffer->status, + vb->vb2_buf.index); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + dstbuf->queued_in_vb2 = true; + } else { + /* + * Codec driver do not need to reference this capture + * buffer and this buffer is not in v4l2 driver. + * Then we don't need to do any thing, just add log when + * we need to debug buffer flow. + * When this buffer q from user space, it could + * directly q to vb2 buffer + */ + mtk_v4l2_debug(3, "[%d]status=%x err queue id=%d %d %d", + ctx->id, free_frame_buffer->status, + vb->vb2_buf.index, dstbuf->queued_in_vb2, + dstbuf->queued_in_v4l2); + } + dstbuf->used = false; + } + mutex_unlock(&ctx->lock); + return &vb->vb2_buf; +} + +static void clean_display_buffer(struct mtk_vcodec_ctx *ctx) +{ + while (get_display_buffer(ctx)) + ; +} + +static void clean_free_buffer(struct mtk_vcodec_ctx *ctx) +{ + while (get_free_buffer(ctx)) + ; +} + +static void mtk_vdec_queue_res_chg_event(struct mtk_vcodec_ctx *ctx) +{ + static const struct v4l2_event ev_src_ch = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, + }; + + mtk_v4l2_debug(1, "[%d]", ctx->id); + v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); +} + +static int mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) +{ + bool res_chg; + int ret; + + ret = vdec_if_decode(ctx, NULL, NULL, &res_chg); + if (ret) + mtk_v4l2_err("DecodeFinal failed, ret=%d", ret); + + clean_display_buffer(ctx); + clean_free_buffer(ctx); + + return 0; +} + +static void mtk_vdec_update_fmt(struct mtk_vcodec_ctx *ctx, + unsigned int pixelformat) +{ + const struct mtk_video_fmt *fmt; + struct mtk_q_data *dst_q_data; + unsigned int k; + + dst_q_data = &ctx->q_data[MTK_Q_DATA_DST]; + for (k = 0; k < NUM_FORMATS; k++) { + fmt = &mtk_video_formats[k]; + if (fmt->fourcc == pixelformat) { + mtk_v4l2_debug(1, "Update cap fourcc(%d -> %d)", + dst_q_data->fmt->fourcc, pixelformat); + dst_q_data->fmt = fmt; + return; + } + } + + mtk_v4l2_err("Cannot get fourcc(%d), using init value", pixelformat); +} + +static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) +{ + unsigned int dpbsize = 0; + int ret; + + if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, + &ctx->last_decoded_picinfo)) { + mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", ctx->id); + return -EINVAL; + } + + if (ctx->last_decoded_picinfo.pic_w == 0 || + ctx->last_decoded_picinfo.pic_h == 0 || + ctx->last_decoded_picinfo.buf_w == 0 || + ctx->last_decoded_picinfo.buf_h == 0) { + mtk_v4l2_err("Cannot get correct pic info"); + return -EINVAL; + } + + if (ctx->last_decoded_picinfo.cap_fourcc != ctx->picinfo.cap_fourcc && + ctx->picinfo.cap_fourcc != 0) + mtk_vdec_update_fmt(ctx, ctx->picinfo.cap_fourcc); + + if (ctx->last_decoded_picinfo.pic_w == ctx->picinfo.pic_w || + ctx->last_decoded_picinfo.pic_h == ctx->picinfo.pic_h) + return 0; + + mtk_v4l2_debug(1, "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", ctx->id, + ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, ctx->picinfo.pic_w, + ctx->picinfo.pic_h, ctx->last_decoded_picinfo.buf_w, + ctx->last_decoded_picinfo.buf_h); + + ret = vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpbsize); + if (dpbsize == 0) + mtk_v4l2_err("Incorrect dpb size, ret=%d", ret); + + ctx->dpb_size = dpbsize; + + return ret; +} + +static void mtk_vdec_worker(struct work_struct *work) +{ + struct mtk_vcodec_ctx *ctx = + container_of(work, struct mtk_vcodec_ctx, decode_work); + struct mtk_vcodec_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct mtk_vcodec_mem buf; + struct vdec_fb *pfb; + bool res_chg = false; + int ret; + struct mtk_video_dec_buf *dst_buf_info, *src_buf_info; + + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + if (!src_buf) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + mtk_v4l2_debug(1, "[%d] src_buf empty!!", ctx->id); + return; + } + + dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + if (!dst_buf) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + mtk_v4l2_debug(1, "[%d] dst_buf empty!!", ctx->id); + return; + } + + dst_buf_info = + container_of(dst_buf, struct mtk_video_dec_buf, m2m_buf.vb); + + pfb = &dst_buf_info->frame_buffer; + pfb->base_y.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + pfb->base_y.dma_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + pfb->base_y.size = ctx->picinfo.fb_sz[0]; + + pfb->base_c.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 1); + pfb->base_c.dma_addr = + vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 1); + pfb->base_c.size = ctx->picinfo.fb_sz[1]; + pfb->status = 0; + mtk_v4l2_debug(3, "===>[%d] vdec_if_decode() ===>", ctx->id); + + mtk_v4l2_debug(3, + "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx", + dst_buf->vb2_buf.index, pfb, pfb->base_y.va, + &pfb->base_y.dma_addr, &pfb->base_c.dma_addr, pfb->base_y.size); + + if (src_buf == &ctx->empty_flush_buf.vb) { + mtk_v4l2_debug(1, "Got empty flush input buffer."); + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + + /* update dst buf status */ + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + mutex_lock(&ctx->lock); + dst_buf_info->used = false; + mutex_unlock(&ctx->lock); + + vdec_if_decode(ctx, NULL, NULL, &res_chg); + clean_display_buffer(ctx); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + clean_free_buffer(ctx); + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + return; + } + + src_buf_info = + container_of(src_buf, struct mtk_video_dec_buf, m2m_buf.vb); + + buf.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + buf.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + buf.size = (size_t)src_buf->vb2_buf.planes[0].bytesused; + if (!buf.va) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + mtk_v4l2_err("[%d] id=%d src_addr is NULL!!", ctx->id, + src_buf->vb2_buf.index); + return; + } + mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", + ctx->id, buf.va, &buf.dma_addr, buf.size, src_buf); + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; + mutex_lock(&ctx->lock); + dst_buf_info->used = true; + mutex_unlock(&ctx->lock); + src_buf_info->used = true; + + ret = vdec_if_decode(ctx, &buf, pfb, &res_chg); + + if (ret) { + mtk_v4l2_err(" <===[%d], src_buf[%d] sz=0x%zx pts=%llu dst_buf[%d] vdec_if_decode() ret=%d res_chg=%d===>", + ctx->id, src_buf->vb2_buf.index, buf.size, + src_buf->vb2_buf.timestamp, dst_buf->vb2_buf.index, ret, res_chg); + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + if (ret == -EIO) { + mutex_lock(&ctx->lock); + src_buf_info->error = true; + mutex_unlock(&ctx->lock); + } + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + } else if (!res_chg) { + /* + * we only return src buffer with VB2_BUF_STATE_DONE + * when decode success without resolution change + */ + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + clean_display_buffer(ctx); + clean_free_buffer(ctx); + + if (!ret && res_chg) { + mtk_vdec_pic_info_update(ctx); + /* + * On encountering a resolution change in the stream. + * The driver must first process and decode all + * remaining buffers from before the resolution change + * point, so call flush decode here + */ + mtk_vdec_flush_decoder(ctx); + /* + * After all buffers containing decoded frames from + * before the resolution change point ready to be + * dequeued on the CAPTURE queue, the driver sends a + * V4L2_EVENT_SOURCE_CHANGE event for source change + * type V4L2_EVENT_SRC_CH_RESOLUTION + */ + mtk_vdec_queue_res_chg_event(ctx); + } + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); +} + +static void vb2ops_vdec_stateful_buf_queue(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *src_buf; + struct mtk_vcodec_mem src_mem; + bool res_chg = false; + int ret; + unsigned int dpbsize = 1, i; + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2; + struct mtk_q_data *dst_q_data; + + mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, + vb->vb2_queue->type, vb->index, vb); + /* + * check if this buffer is ready to be used after decode + */ + if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + struct mtk_video_dec_buf *buf; + + vb2_v4l2 = to_vb2_v4l2_buffer(vb); + buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, + m2m_buf.vb); + mutex_lock(&ctx->lock); + if (!buf->used) { + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2); + buf->queued_in_vb2 = true; + buf->queued_in_v4l2 = true; + } else { + buf->queued_in_vb2 = false; + buf->queued_in_v4l2 = true; + } + mutex_unlock(&ctx->lock); + return; + } + + v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); + + if (ctx->state != MTK_STATE_INIT) { + mtk_v4l2_debug(3, "[%d] already init driver %d", ctx->id, + ctx->state); + return; + } + + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + if (!src_buf) { + mtk_v4l2_err("No src buffer"); + return; + } + + if (src_buf == &ctx->empty_flush_buf.vb) { + /* This shouldn't happen. Just in case. */ + mtk_v4l2_err("Invalid flush buffer."); + v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + return; + } + + src_mem.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); + src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); + src_mem.size = (size_t)src_buf->vb2_buf.planes[0].bytesused; + mtk_v4l2_debug(2, "[%d] buf id=%d va=%p dma=%pad size=%zx", ctx->id, + src_buf->vb2_buf.index, src_mem.va, &src_mem.dma_addr, + src_mem.size); + + ret = vdec_if_decode(ctx, &src_mem, NULL, &res_chg); + if (ret || !res_chg) { + /* + * fb == NULL means to parse SPS/PPS header or + * resolution info in src_mem. Decode can fail + * if there is no SPS header or picture info + * in bs + */ + + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + if (ret == -EIO) { + mtk_v4l2_err("[%d] Unrecoverable error in vdec_if_decode.", ctx->id); + ctx->state = MTK_STATE_ABORT; + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + } else { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + } + mtk_v4l2_debug(ret ? 0 : 1, + "[%d] vdec_if_decode() src_buf=%d, size=%zu, fail=%d, res_chg=%d", + ctx->id, src_buf->vb2_buf.index, src_mem.size, ret, res_chg); + return; + } + + if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo)) { + mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", ctx->id); + return; + } + + ctx->last_decoded_picinfo = ctx->picinfo; + dst_q_data = &ctx->q_data[MTK_Q_DATA_DST]; + for (i = 0; i < dst_q_data->fmt->num_planes; i++) { + dst_q_data->sizeimage[i] = ctx->picinfo.fb_sz[i]; + dst_q_data->bytesperline[i] = ctx->picinfo.buf_w; + } + + mtk_v4l2_debug(2, "[%d] vdec_if_init() OK wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", + ctx->id, ctx->picinfo.buf_w, ctx->picinfo.buf_h, ctx->picinfo.pic_w, + ctx->picinfo.pic_h, dst_q_data->sizeimage[0], dst_q_data->sizeimage[1]); + + ret = vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpbsize); + if (dpbsize == 0) + mtk_v4l2_err("[%d] GET_PARAM_DPB_SIZE fail=%d", ctx->id, ret); + + ctx->dpb_size = dpbsize; + ctx->state = MTK_STATE_HEADER; + mtk_v4l2_debug(1, "[%d] dpbsize=%d", ctx->id, ctx->dpb_size); + + mtk_vdec_queue_res_chg_event(ctx); +} + +static int mtk_vdec_g_v_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: + if (ctx->state >= MTK_STATE_HEADER) { + ctrl->val = ctx->dpb_size; + } else { + mtk_v4l2_debug(0, "Seqinfo not ready"); + ctrl->val = 0; + } + break; + default: + ret = -EINVAL; + } + return ret; +} + +static const struct v4l2_ctrl_ops mtk_vcodec_dec_ctrl_ops = { + .g_volatile_ctrl = mtk_vdec_g_v_ctrl, +}; + +static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) +{ + struct v4l2_ctrl *ctrl; + + v4l2_ctrl_handler_init(&ctx->ctrl_hdl, 1); + + ctrl = v4l2_ctrl_new_std(&ctx->ctrl_hdl, &mtk_vcodec_dec_ctrl_ops, + V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 0, 32, 1, 1); + ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + v4l2_ctrl_new_std_menu(&ctx->ctrl_hdl, &mtk_vcodec_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_VP9_PROFILE, + V4L2_MPEG_VIDEO_VP9_PROFILE_0, 0, + V4L2_MPEG_VIDEO_VP9_PROFILE_0); + /* + * H264. Baseline / Extended decoding is not supported. + */ + v4l2_ctrl_new_std_menu(&ctx->ctrl_hdl, &mtk_vcodec_dec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + V4L2_MPEG_VIDEO_H264_PROFILE_MAIN); + + if (ctx->ctrl_hdl.error) { + mtk_v4l2_err("Adding control failed %d", ctx->ctrl_hdl.error); + return ctx->ctrl_hdl.error; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + return 0; +} + +static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) +{ +} + +static struct vb2_ops mtk_vdec_frame_vb2_ops = { + .queue_setup = vb2ops_vdec_queue_setup, + .buf_prepare = vb2ops_vdec_buf_prepare, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vb2ops_vdec_start_streaming, + + .buf_queue = vb2ops_vdec_stateful_buf_queue, + .buf_init = vb2ops_vdec_buf_init, + .buf_finish = vb2ops_vdec_buf_finish, + .stop_streaming = vb2ops_vdec_stop_streaming, +}; + +const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata = { + .chip = MTK_MT8173, + .init_vdec_params = mtk_init_vdec_params, + .ctrls_setup = mtk_vcodec_dec_ctrls_setup, + .vdec_vb2_ops = &mtk_vdec_frame_vb2_ops, + .vdec_formats = mtk_video_formats, + .num_formats = NUM_FORMATS, + .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], + .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], + .vdec_framesizes = mtk_vdec_framesizes, + .num_framesizes = NUM_SUPPORTED_FRAMESIZE, + .worker = mtk_vdec_worker, + .flush_decoder = mtk_vdec_flush_decoder, + .is_subdev_supported = false, + .hw_arch = MTK_VDEC_PURE_SINGLE_CORE, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateless.c new file mode 100644 index 000000000000..23d997ac114d --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_dec_stateless.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_dec_pm.h" +#include "vdec_drv_if.h" + +/** + * struct mtk_stateless_control - CID control type + * @cfg: control configuration + * @codec_type: codec type (V4L2 pixel format) for CID control type + */ +struct mtk_stateless_control { + struct v4l2_ctrl_config cfg; + int codec_type; +}; + +static const struct mtk_stateless_control mtk_stateless_controls[] = { + { + .cfg = { + .id = V4L2_CID_STATELESS_H264_SPS, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_H264_PPS, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, + .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + .menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | + BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_H264_DECODE_MODE, + .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_H264_START_CODE, + .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, + }, + .codec_type = V4L2_PIX_FMT_H264_SLICE, + } +}; + +#define NUM_CTRLS ARRAY_SIZE(mtk_stateless_controls) + +static const struct mtk_video_fmt mtk_video_formats[] = { + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .type = MTK_FMT_DEC, + .num_planes = 1, + }, + { + .fourcc = V4L2_PIX_FMT_MM21, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) +#define DEFAULT_OUT_FMT_IDX 0 +#define DEFAULT_CAP_FMT_IDX 1 + +static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = { + { + .fourcc = V4L2_PIX_FMT_H264_SLICE, + .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, + MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, + }, +}; + +#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_vdec_framesizes) + +static void mtk_vdec_stateless_set_dst_payload(struct mtk_vcodec_ctx *ctx, + struct vdec_fb *fb) +{ + struct mtk_video_dec_buf *vdec_frame_buf = + container_of(fb, struct mtk_video_dec_buf, frame_buffer); + struct vb2_v4l2_buffer *vb = &vdec_frame_buf->m2m_buf.vb; + unsigned int cap_y_size = ctx->q_data[MTK_Q_DATA_DST].sizeimage[0]; + + vb2_set_plane_payload(&vb->vb2_buf, 0, cap_y_size); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) { + unsigned int cap_c_size = + ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]; + + vb2_set_plane_payload(&vb->vb2_buf, 1, cap_c_size); + } +} + +static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_ctx *ctx, + struct vb2_v4l2_buffer *vb2_v4l2) +{ + struct mtk_video_dec_buf *framebuf = + container_of(vb2_v4l2, struct mtk_video_dec_buf, m2m_buf.vb); + struct vdec_fb *pfb = &framebuf->frame_buffer; + struct vb2_buffer *dst_buf = &vb2_v4l2->vb2_buf; + + pfb->base_y.va = NULL; + pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); + pfb->base_y.size = ctx->q_data[MTK_Q_DATA_DST].sizeimage[0]; + + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) { + pfb->base_c.va = NULL; + pfb->base_c.dma_addr = + vb2_dma_contig_plane_dma_addr(dst_buf, 1); + pfb->base_c.size = ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]; + } + mtk_v4l2_debug(1, "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx frame_count = %d", + dst_buf->index, pfb, pfb->base_y.va, &pfb->base_y.dma_addr, + &pfb->base_c.dma_addr, pfb->base_y.size, ctx->decoded_frame_cnt); + + return pfb; +} + +static void vb2ops_vdec_buf_request_complete(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); +} + +static void mtk_vdec_worker(struct work_struct *work) +{ + struct mtk_vcodec_ctx *ctx = + container_of(work, struct mtk_vcodec_ctx, decode_work); + struct mtk_vcodec_dev *dev = ctx->dev; + struct vb2_v4l2_buffer *vb2_v4l2_src, *vb2_v4l2_dst; + struct vb2_buffer *vb2_src; + struct mtk_vcodec_mem *bs_src; + struct mtk_video_dec_buf *dec_buf_src; + struct media_request *src_buf_req; + struct vdec_fb *dst_buf; + bool res_chg = false; + int ret; + + vb2_v4l2_src = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + if (!vb2_v4l2_src) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + mtk_v4l2_debug(1, "[%d] no available source buffer", ctx->id); + return; + } + + vb2_v4l2_dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + if (!vb2_v4l2_dst) { + v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); + mtk_v4l2_debug(1, "[%d] no available destination buffer", ctx->id); + return; + } + + vb2_src = &vb2_v4l2_src->vb2_buf; + dec_buf_src = container_of(vb2_v4l2_src, struct mtk_video_dec_buf, + m2m_buf.vb); + bs_src = &dec_buf_src->bs_buffer; + + mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, + vb2_src->vb2_queue->type, vb2_src->index, vb2_src); + + bs_src->va = NULL; + bs_src->dma_addr = vb2_dma_contig_plane_dma_addr(vb2_src, 0); + bs_src->size = (size_t)vb2_src->planes[0].bytesused; + + mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", + ctx->id, bs_src->va, &bs_src->dma_addr, bs_src->size, vb2_src); + /* Apply request controls. */ + src_buf_req = vb2_src->req_obj.req; + if (src_buf_req) + v4l2_ctrl_request_setup(src_buf_req, &ctx->ctrl_hdl); + else + mtk_v4l2_err("vb2 buffer media request is NULL"); + + dst_buf = vdec_get_cap_buffer(ctx, vb2_v4l2_dst); + v4l2_m2m_buf_copy_metadata(vb2_v4l2_src, vb2_v4l2_dst, true); + ret = vdec_if_decode(ctx, bs_src, dst_buf, &res_chg); + if (ret) { + mtk_v4l2_err(" <===[%d], src_buf[%d] sz=0x%zx pts=%llu vdec_if_decode() ret=%d res_chg=%d===>", + ctx->id, vb2_src->index, bs_src->size, + vb2_src->timestamp, ret, res_chg); + if (ret == -EIO) { + mutex_lock(&ctx->lock); + dec_buf_src->error = true; + mutex_unlock(&ctx->lock); + } + } + + mtk_vdec_stateless_set_dst_payload(ctx, dst_buf); + + v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, + ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); + + v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl); +} + +static void vb2ops_vdec_stateless_buf_queue(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2 = to_vb2_v4l2_buffer(vb); + + mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, vb->vb2_queue->type, vb->index, vb); + + mutex_lock(&ctx->lock); + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2); + mutex_unlock(&ctx->lock); + if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return; + + /* If an OUTPUT buffer, we may need to update the state */ + if (ctx->state == MTK_STATE_INIT) { + ctx->state = MTK_STATE_HEADER; + mtk_v4l2_debug(1, "Init driver from init to header."); + } else { + mtk_v4l2_debug(3, "[%d] already init driver %d", ctx->id, ctx->state); + } +} + +static int mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) +{ + bool res_chg; + + return vdec_if_decode(ctx, NULL, NULL, &res_chg); +} + +static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) +{ + unsigned int i; + + v4l2_ctrl_handler_init(&ctx->ctrl_hdl, NUM_CTRLS); + if (ctx->ctrl_hdl.error) { + mtk_v4l2_err("v4l2_ctrl_handler_init failed\n"); + return ctx->ctrl_hdl.error; + } + + for (i = 0; i < NUM_CTRLS; i++) { + struct v4l2_ctrl_config cfg = mtk_stateless_controls[i].cfg; + + v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &cfg, NULL); + if (ctx->ctrl_hdl.error) { + mtk_v4l2_err("Adding control %d failed %d", i, ctx->ctrl_hdl.error); + return ctx->ctrl_hdl.error; + } + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + + return 0; +} + +static int fops_media_request_validate(struct media_request *mreq) +{ + const unsigned int buffer_cnt = vb2_request_buffer_cnt(mreq); + + switch (buffer_cnt) { + case 1: + /* We expect exactly one buffer with the request */ + break; + case 0: + mtk_v4l2_debug(1, "No buffer provided with the request"); + return -ENOENT; + default: + mtk_v4l2_debug(1, "Too many buffers (%d) provided with the request", + buffer_cnt); + return -EINVAL; + } + + return vb2_request_validate(mreq); +} + +const struct media_device_ops mtk_vcodec_media_ops = { + .req_validate = fops_media_request_validate, + .req_queue = v4l2_m2m_request_queue, +}; + +static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) +{ + struct vb2_queue *src_vq; + + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + + /* Support request api for output plane */ + src_vq->supports_requests = true; + src_vq->requires_requests = true; +} + +static int vb2ops_vdec_out_buf_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +static struct vb2_ops mtk_vdec_request_vb2_ops = { + .queue_setup = vb2ops_vdec_queue_setup, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vb2ops_vdec_start_streaming, + .stop_streaming = vb2ops_vdec_stop_streaming, + + .buf_queue = vb2ops_vdec_stateless_buf_queue, + .buf_out_validate = vb2ops_vdec_out_buf_validate, + .buf_init = vb2ops_vdec_buf_init, + .buf_prepare = vb2ops_vdec_buf_prepare, + .buf_finish = vb2ops_vdec_buf_finish, + .buf_request_complete = vb2ops_vdec_buf_request_complete, +}; + +const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata = { + .chip = MTK_MT8183, + .init_vdec_params = mtk_init_vdec_params, + .ctrls_setup = mtk_vcodec_dec_ctrls_setup, + .vdec_vb2_ops = &mtk_vdec_request_vb2_ops, + .vdec_formats = mtk_video_formats, + .num_formats = NUM_FORMATS, + .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], + .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], + .vdec_framesizes = mtk_vdec_framesizes, + .num_framesizes = NUM_SUPPORTED_FRAMESIZE, + .uses_stateless_api = true, + .worker = mtk_vdec_worker, + .flush_decoder = mtk_vdec_flush_decoder, + .is_subdev_supported = false, + .hw_arch = MTK_VDEC_PURE_SINGLE_CORE, +}; + +/* This platform data is used for one lat and one core architecture. */ +const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata = { + .chip = MTK_MT8192, + .init_vdec_params = mtk_init_vdec_params, + .ctrls_setup = mtk_vcodec_dec_ctrls_setup, + .vdec_vb2_ops = &mtk_vdec_request_vb2_ops, + .vdec_formats = mtk_video_formats, + .num_formats = NUM_FORMATS, + .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], + .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], + .vdec_framesizes = mtk_vdec_framesizes, + .num_framesizes = NUM_SUPPORTED_FRAMESIZE, + .uses_stateless_api = true, + .worker = mtk_vdec_worker, + .flush_decoder = mtk_vdec_flush_decoder, + .is_subdev_supported = true, + .hw_arch = MTK_VDEC_LAT_SINGLE_CORE, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_drv.h new file mode 100644 index 000000000000..813901c4be5e --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_drv.h @@ -0,0 +1,537 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +*/ + +#ifndef _MTK_VCODEC_DRV_H_ +#define _MTK_VCODEC_DRV_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vcodec_util.h" +#include "vdec_msg_queue.h" + +#define MTK_VCODEC_DRV_NAME "mtk_vcodec_drv" +#define MTK_VCODEC_DEC_NAME "mtk-vcodec-dec" +#define MTK_VCODEC_ENC_NAME "mtk-vcodec-enc" +#define MTK_PLATFORM_STR "platform:mt8173" + +#define MTK_VCODEC_MAX_PLANES 3 +#define MTK_V4L2_BENCHMARK 0 +#define WAIT_INTR_TIMEOUT_MS 1000 +#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) + +/* + * enum mtk_hw_reg_idx - MTK hw register base index + */ +enum mtk_hw_reg_idx { + VDEC_SYS, + VDEC_MISC, + VDEC_LD, + VDEC_TOP, + VDEC_CM, + VDEC_AD, + VDEC_AV, + VDEC_PP, + VDEC_HWD, + VDEC_HWQ, + VDEC_HWB, + VDEC_HWG, + NUM_MAX_VDEC_REG_BASE, + /* h264 encoder */ + VENC_SYS = NUM_MAX_VDEC_REG_BASE, + /* vp8 encoder */ + VENC_LT_SYS, + NUM_MAX_VCODEC_REG_BASE +}; + +/* + * enum mtk_instance_type - The type of an MTK Vcodec instance. + */ +enum mtk_instance_type { + MTK_INST_DECODER = 0, + MTK_INST_ENCODER = 1, +}; + +/** + * enum mtk_instance_state - The state of an MTK Vcodec instance. + * @MTK_STATE_FREE: default state when instance is created + * @MTK_STATE_INIT: vcodec instance is initialized + * @MTK_STATE_HEADER: vdec had sps/pps header parsed or venc + * had sps/pps header encoded + * @MTK_STATE_FLUSH: vdec is flushing. Only used by decoder + * @MTK_STATE_ABORT: vcodec should be aborted + */ +enum mtk_instance_state { + MTK_STATE_FREE = 0, + MTK_STATE_INIT = 1, + MTK_STATE_HEADER = 2, + MTK_STATE_FLUSH = 3, + MTK_STATE_ABORT = 4, +}; + +/* + * enum mtk_encode_param - General encoding parameters type + */ +enum mtk_encode_param { + MTK_ENCODE_PARAM_NONE = 0, + MTK_ENCODE_PARAM_BITRATE = (1 << 0), + MTK_ENCODE_PARAM_FRAMERATE = (1 << 1), + MTK_ENCODE_PARAM_INTRA_PERIOD = (1 << 2), + MTK_ENCODE_PARAM_FORCE_INTRA = (1 << 3), + MTK_ENCODE_PARAM_GOP_SIZE = (1 << 4), +}; + +enum mtk_fmt_type { + MTK_FMT_DEC = 0, + MTK_FMT_ENC = 1, + MTK_FMT_FRAME = 2, +}; + +/* + * enum mtk_vdec_hw_id - Hardware index used to separate + * different hardware + */ +enum mtk_vdec_hw_id { + MTK_VDEC_CORE, + MTK_VDEC_LAT0, + MTK_VDEC_LAT1, + MTK_VDEC_HW_MAX, +}; + +/* + * enum mtk_vdec_hw_count - Supported hardware count + */ +enum mtk_vdec_hw_count { + MTK_VDEC_NO_HW = 0, + MTK_VDEC_ONE_CORE, + MTK_VDEC_ONE_LAT_ONE_CORE, + MTK_VDEC_MAX_HW_COUNT, +}; + +/* + * struct mtk_video_fmt - Structure used to store information about pixelformats + */ +struct mtk_video_fmt { + u32 fourcc; + enum mtk_fmt_type type; + u32 num_planes; + u32 flags; +}; + +/* + * struct mtk_codec_framesizes - Structure used to store information about + * framesizes + */ +struct mtk_codec_framesizes { + u32 fourcc; + struct v4l2_frmsize_stepwise stepwise; +}; + +/* + * enum mtk_q_type - Type of queue + */ +enum mtk_q_type { + MTK_Q_DATA_SRC = 0, + MTK_Q_DATA_DST = 1, +}; + +/* + * struct mtk_q_data - Structure used to store information about queue + */ +struct mtk_q_data { + unsigned int visible_width; + unsigned int visible_height; + unsigned int coded_width; + unsigned int coded_height; + enum v4l2_field field; + unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; + unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; + const struct mtk_video_fmt *fmt; +}; + +/** + * struct mtk_enc_params - General encoding parameters + * @bitrate: target bitrate in bits per second + * @num_b_frame: number of b frames between p-frame + * @rc_frame: frame based rate control + * @rc_mb: macroblock based rate control + * @seq_hdr_mode: H.264 sequence header is encoded separately or joined + * with the first frame + * @intra_period: I frame period + * @gop_size: group of picture size, it's used as the intra frame period + * @framerate_num: frame rate numerator. ex: framerate_num=30 and + * framerate_denom=1 means FPS is 30 + * @framerate_denom: frame rate denominator. ex: framerate_num=30 and + * framerate_denom=1 means FPS is 30 + * @h264_max_qp: Max value for H.264 quantization parameter + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @force_intra: force/insert intra frame + */ +struct mtk_enc_params { + unsigned int bitrate; + unsigned int num_b_frame; + unsigned int rc_frame; + unsigned int rc_mb; + unsigned int seq_hdr_mode; + unsigned int intra_period; + unsigned int gop_size; + unsigned int framerate_num; + unsigned int framerate_denom; + unsigned int h264_max_qp; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int force_intra; +}; + +/* + * struct mtk_vcodec_clk_info - Structure used to store clock name + */ +struct mtk_vcodec_clk_info { + const char *clk_name; + struct clk *vcodec_clk; +}; + +/* + * struct mtk_vcodec_clk - Structure used to store vcodec clock information + */ +struct mtk_vcodec_clk { + struct mtk_vcodec_clk_info *clk_info; + int clk_num; +}; + +/* + * struct mtk_vcodec_pm - Power management data structure + */ +struct mtk_vcodec_pm { + struct mtk_vcodec_clk vdec_clk; + struct mtk_vcodec_clk venc_clk; + struct device *dev; +}; + +/** + * struct vdec_pic_info - picture size information + * @pic_w: picture width + * @pic_h: picture height + * @buf_w: picture buffer width (64 aligned up from pic_w) + * @buf_h: picture buffer heiht (64 aligned up from pic_h) + * @fb_sz: bitstream size of each plane + * E.g. suppose picture size is 176x144, + * buffer size will be aligned to 176x160. + * @cap_fourcc: fourcc number(may changed when resolution change) + * @reserved: align struct to 64-bit in order to adjust 32-bit and 64-bit os. + */ +struct vdec_pic_info { + unsigned int pic_w; + unsigned int pic_h; + unsigned int buf_w; + unsigned int buf_h; + unsigned int fb_sz[VIDEO_MAX_PLANES]; + unsigned int cap_fourcc; + unsigned int reserved; +}; + +/** + * struct mtk_vcodec_ctx - Context (instance) private data. + * + * @type: type of the instance - decoder or encoder + * @dev: pointer to the mtk_vcodec_dev of the device + * @list: link to ctx_list of mtk_vcodec_dev + * @fh: struct v4l2_fh + * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context + * @q_data: store information of input and output queue + * of the context + * @id: index of the context that this structure describes + * @state: state of the context + * @param_change: indicate encode parameter type + * @enc_params: encoding parameters + * @dec_if: hooked decoder driver interface + * @enc_if: hoooked encoder driver interface + * @drv_handle: driver handle for specific decode/encode instance + * + * @picinfo: store picture info after header parsing + * @dpb_size: store dpb count after header parsing + * @int_cond: variable used by the waitqueue + * @int_type: type of the last interrupt + * @queue: waitqueue that can be used to wait for this context to + * finish + * @irq_status: irq status + * + * @ctrl_hdl: handler for v4l2 framework + * @decode_work: worker for the decoding + * @encode_work: worker for the encoding + * @last_decoded_picinfo: pic information get from latest decode + * @empty_flush_buf: a fake size-0 capture buffer that indicates flush. Only + * to be used with encoder and stateful decoder. + * @is_flushing: set to true if flushing is in progress. + * @current_codec: current set input codec, in V4L2 pixel format + * + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat + * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding + * @quantization: enum v4l2_quantization, colorspace quantization + * @xfer_func: enum v4l2_xfer_func, colorspace transfer function + * @decoded_frame_cnt: number of decoded frames + * @lock: protect variables accessed by V4L2 threads and worker thread such as + * mtk_video_dec_buf. + * @hw_id: hardware index used to identify different hardware. + * + * @msg_queue: msg queue used to store lat buffer information. + */ +struct mtk_vcodec_ctx { + enum mtk_instance_type type; + struct mtk_vcodec_dev *dev; + struct list_head list; + + struct v4l2_fh fh; + struct v4l2_m2m_ctx *m2m_ctx; + struct mtk_q_data q_data[2]; + int id; + enum mtk_instance_state state; + enum mtk_encode_param param_change; + struct mtk_enc_params enc_params; + + const struct vdec_common_if *dec_if; + const struct venc_common_if *enc_if; + void *drv_handle; + + struct vdec_pic_info picinfo; + int dpb_size; + + int int_cond[MTK_VDEC_HW_MAX]; + int int_type[MTK_VDEC_HW_MAX]; + wait_queue_head_t queue[MTK_VDEC_HW_MAX]; + unsigned int irq_status; + + struct v4l2_ctrl_handler ctrl_hdl; + struct work_struct decode_work; + struct work_struct encode_work; + struct vdec_pic_info last_decoded_picinfo; + struct v4l2_m2m_buffer empty_flush_buf; + bool is_flushing; + + u32 current_codec; + + enum v4l2_colorspace colorspace; + enum v4l2_ycbcr_encoding ycbcr_enc; + enum v4l2_quantization quantization; + enum v4l2_xfer_func xfer_func; + + int decoded_frame_cnt; + struct mutex lock; + int hw_id; + + struct vdec_msg_queue msg_queue; +}; + +enum mtk_chip { + MTK_MT8173, + MTK_MT8183, + MTK_MT8192, + MTK_MT8195, +}; + +/* + * enum mtk_vdec_hw_arch - Used to separate different hardware architecture + */ +enum mtk_vdec_hw_arch { + MTK_VDEC_PURE_SINGLE_CORE, + MTK_VDEC_LAT_SINGLE_CORE, +}; + +/** + * struct mtk_vcodec_dec_pdata - compatible data for each IC + * @init_vdec_params: init vdec params + * @ctrls_setup: init vcodec dec ctrls + * @worker: worker to start a decode job + * @flush_decoder: function that flushes the decoder + * + * @vdec_vb2_ops: struct vb2_ops + * + * @vdec_formats: supported video decoder formats + * @num_formats: count of video decoder formats + * @default_out_fmt: default output buffer format + * @default_cap_fmt: default capture buffer format + * + * @vdec_framesizes: supported video decoder frame sizes + * @num_framesizes: count of video decoder frame sizes + * + * @chip: chip this decoder is compatible with + * @hw_arch: hardware arch is used to separate pure_sin_core and lat_sin_core + * + * @is_subdev_supported: whether support parent-node architecture(subdev) + * @uses_stateless_api: whether the decoder uses the stateless API with requests + */ + +struct mtk_vcodec_dec_pdata { + void (*init_vdec_params)(struct mtk_vcodec_ctx *ctx); + int (*ctrls_setup)(struct mtk_vcodec_ctx *ctx); + void (*worker)(struct work_struct *work); + int (*flush_decoder)(struct mtk_vcodec_ctx *ctx); + + struct vb2_ops *vdec_vb2_ops; + + const struct mtk_video_fmt *vdec_formats; + const int num_formats; + const struct mtk_video_fmt *default_out_fmt; + const struct mtk_video_fmt *default_cap_fmt; + + const struct mtk_codec_framesizes *vdec_framesizes; + const int num_framesizes; + + enum mtk_chip chip; + enum mtk_vdec_hw_arch hw_arch; + + bool is_subdev_supported; + bool uses_stateless_api; +}; + +/** + * struct mtk_vcodec_enc_pdata - compatible data for each IC + * + * @chip: chip this encoder is compatible with + * + * @uses_ext: whether the encoder uses the extended firmware messaging format + * @min_bitrate: minimum supported encoding bitrate + * @max_bitrate: maximum supported encoding bitrate + * @capture_formats: array of supported capture formats + * @num_capture_formats: number of entries in capture_formats + * @output_formats: array of supported output formats + * @num_output_formats: number of entries in output_formats + * @core_id: stand for h264 or vp8 encode index + */ +struct mtk_vcodec_enc_pdata { + enum mtk_chip chip; + + bool uses_ext; + unsigned long min_bitrate; + unsigned long max_bitrate; + const struct mtk_video_fmt *capture_formats; + size_t num_capture_formats; + const struct mtk_video_fmt *output_formats; + size_t num_output_formats; + int core_id; +}; + +#define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext) + +/** + * struct mtk_vcodec_dev - driver data + * @v4l2_dev: V4L2 device to register video devices for. + * @vfd_dec: Video device for decoder + * @mdev_dec: Media device for decoder + * @vfd_enc: Video device for encoder. + * + * @m2m_dev_dec: m2m device for decoder + * @m2m_dev_enc: m2m device for encoder. + * @plat_dev: platform device + * @ctx_list: list of struct mtk_vcodec_ctx + * @irqlock: protect data access by irq handler and work thread + * @curr_ctx: The context that is waiting for codec hardware + * + * @reg_base: Mapped address of MTK Vcodec registers. + * @vdec_pdata: decoder IC-specific data + * @venc_pdata: encoder IC-specific data + * + * @fw_handler: used to communicate with the firmware. + * @id_counter: used to identify current opened instance + * + * @decode_workqueue: decode work queue + * @encode_workqueue: encode work queue + * + * @int_cond: used to identify interrupt condition happen + * @int_type: used to identify what kind of interrupt condition happen + * @dev_mutex: video_device lock + * @queue: waitqueue for waiting for completion of device commands + * + * @dec_irq: decoder irq resource + * @enc_irq: h264 encoder irq resource + * + * @dec_mutex: decoder hardware lock + * @enc_mutex: encoder hardware lock. + * + * @pm: power management control + * @dec_capability: used to identify decode capability, ex: 4k + * @enc_capability: used to identify encode capability + * + * @core_workqueue: queue used for core hardware decode + * @msg_queue_core_ctx: msg queue context used for core workqueue + * + * @subdev_dev: subdev hardware device + * @subdev_prob_done: check whether all used hw device is prob done + * @subdev_bitmap: used to record hardware is ready or not + */ +struct mtk_vcodec_dev { + struct v4l2_device v4l2_dev; + struct video_device *vfd_dec; + struct media_device mdev_dec; + struct video_device *vfd_enc; + + struct v4l2_m2m_dev *m2m_dev_dec; + struct v4l2_m2m_dev *m2m_dev_enc; + struct platform_device *plat_dev; + struct list_head ctx_list; + spinlock_t irqlock; + struct mtk_vcodec_ctx *curr_ctx; + void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; + const struct mtk_vcodec_dec_pdata *vdec_pdata; + const struct mtk_vcodec_enc_pdata *venc_pdata; + + struct mtk_vcodec_fw *fw_handler; + + unsigned long id_counter; + + struct workqueue_struct *decode_workqueue; + struct workqueue_struct *encode_workqueue; + int int_cond; + int int_type; + struct mutex dev_mutex; + wait_queue_head_t queue; + + int dec_irq; + int enc_irq; + + /* decoder hardware mutex lock */ + struct mutex dec_mutex[MTK_VDEC_HW_MAX]; + struct mutex enc_mutex; + + struct mtk_vcodec_pm pm; + unsigned int dec_capability; + unsigned int enc_capability; + + struct workqueue_struct *core_workqueue; + struct vdec_msg_queue_ctx msg_queue_core_ctx; + + void *subdev_dev[MTK_VDEC_HW_MAX]; + int (*subdev_prob_done)(struct mtk_vcodec_dev *vdec_dev); + DECLARE_BITMAP(subdev_bitmap, MTK_VDEC_HW_MAX); +}; + +static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) +{ + return container_of(fh, struct mtk_vcodec_ctx, fh); +} + +static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl); +} + +/* Wake up context wait_queue */ +static inline void +wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason, unsigned int hw_id) +{ + ctx->int_cond[hw_id] = 1; + ctx->int_type[hw_id] = reason; + wake_up_interruptible(&ctx->queue[hw_id]); +} + +#endif /* _MTK_VCODEC_DRV_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.c new file mode 100644 index 000000000000..c21367038c34 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.c @@ -0,0 +1,1451 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +*/ + +#include +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "venc_drv_if.h" + +#define MTK_VENC_MIN_W 160U +#define MTK_VENC_MIN_H 128U +#define MTK_VENC_HD_MAX_W 1920U +#define MTK_VENC_HD_MAX_H 1088U +#define MTK_VENC_4K_MAX_W 3840U +#define MTK_VENC_4K_MAX_H 2176U + +#define DFT_CFG_WIDTH MTK_VENC_MIN_W +#define DFT_CFG_HEIGHT MTK_VENC_MIN_H +#define MTK_MAX_CTRLS_HINT 20 + +#define MTK_DEFAULT_FRAMERATE_NUM 1001 +#define MTK_DEFAULT_FRAMERATE_DENOM 30000 +#define MTK_VENC_4K_CAPABILITY_ENABLE BIT(0) + +static void mtk_venc_worker(struct work_struct *work); + +static const struct v4l2_frmsize_stepwise mtk_venc_hd_framesizes = { + MTK_VENC_MIN_W, MTK_VENC_HD_MAX_W, 16, + MTK_VENC_MIN_H, MTK_VENC_HD_MAX_H, 16, +}; + +static const struct v4l2_frmsize_stepwise mtk_venc_4k_framesizes = { + MTK_VENC_MIN_W, MTK_VENC_4K_MAX_W, 16, + MTK_VENC_MIN_H, MTK_VENC_4K_MAX_H, 16, +}; + +static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); + struct mtk_enc_params *p = &ctx->enc_params; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_VIDEO_BITRATE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d", + ctrl->val); + p->bitrate = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_BITRATE; + break; + case V4L2_CID_MPEG_VIDEO_B_FRAMES: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d", + ctrl->val); + p->num_b_frame = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d", + ctrl->val); + p->rc_frame = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d", + ctrl->val); + p->h264_max_qp = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_HEADER_MODE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d", + ctrl->val); + p->seq_hdr_mode = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d", + ctrl->val); + p->rc_mb = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_PROFILE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", + ctrl->val); + p->h264_profile = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_LEVEL: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", + ctrl->val); + p->h264_level = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", + ctrl->val); + p->intra_period = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_INTRA_PERIOD; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d", + ctrl->val); + p->gop_size = ctrl->val; + ctx->param_change |= MTK_ENCODE_PARAM_GOP_SIZE; + break; + case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: + /* + * FIXME - what vp8 profiles are actually supported? + * The ctrl is added (with only profile 0 supported) for now. + */ + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_VP8_PROFILE val = %d", ctrl->val); + break; + case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: + mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME"); + p->force_intra = 1; + ctx->param_change |= MTK_ENCODE_PARAM_FORCE_INTRA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = { + .s_ctrl = vidioc_venc_s_ctrl, +}; + +static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, + const struct mtk_video_fmt *formats, + size_t num_formats) +{ + if (f->index >= num_formats) + return -EINVAL; + + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +static const struct mtk_video_fmt * +mtk_venc_find_format(u32 fourcc, const struct mtk_vcodec_enc_pdata *pdata) +{ + const struct mtk_video_fmt *fmt; + unsigned int k; + + for (k = 0; k < pdata->num_capture_formats; k++) { + fmt = &pdata->capture_formats[k]; + if (fmt->fourcc == fourcc) + return fmt; + } + + for (k = 0; k < pdata->num_output_formats; k++) { + fmt = &pdata->output_formats[k]; + if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; +} + +static int vidioc_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + const struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(fh); + + if (fsize->index != 0) + return -EINVAL; + + fmt = mtk_venc_find_format(fsize->pixel_format, + ctx->dev->venc_pdata); + if (!fmt) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + + if (ctx->dev->enc_capability & MTK_VENC_4K_CAPABILITY_ENABLE) + fsize->stepwise = mtk_venc_4k_framesizes; + else + fsize->stepwise = mtk_venc_hd_framesizes; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct mtk_vcodec_enc_pdata *pdata = + fh_to_ctx(priv)->dev->venc_pdata; + + return vidioc_enum_fmt(f, pdata->capture_formats, + pdata->num_capture_formats); +} + +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct mtk_vcodec_enc_pdata *pdata = + fh_to_ctx(priv)->dev->venc_pdata; + + return vidioc_enum_fmt(f, pdata->output_formats, + pdata->num_output_formats); +} + +static int vidioc_venc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, MTK_VCODEC_ENC_NAME, sizeof(cap->driver)); + strscpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); + strscpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); + + return 0; +} + +static int vidioc_venc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct v4l2_fract *timeperframe = &a->parm.output.timeperframe; + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + if (timeperframe->numerator == 0 || timeperframe->denominator == 0) { + timeperframe->numerator = MTK_DEFAULT_FRAMERATE_NUM; + timeperframe->denominator = MTK_DEFAULT_FRAMERATE_DENOM; + } + + ctx->enc_params.framerate_num = timeperframe->denominator; + ctx->enc_params.framerate_denom = timeperframe->numerator; + ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + + return 0; +} + +static int vidioc_venc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return -EINVAL; + + a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; + a->parm.output.timeperframe.denominator = + ctx->enc_params.framerate_num; + a->parm.output.timeperframe.numerator = + ctx->enc_params.framerate_denom; + + return 0; +} + +static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, + enum v4l2_buf_type type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + return &ctx->q_data[MTK_Q_DATA_SRC]; + + return &ctx->q_data[MTK_Q_DATA_DST]; +} + +static void vidioc_try_fmt_cap(struct v4l2_format *f) +{ + f->fmt.pix_mp.field = V4L2_FIELD_NONE; + f->fmt.pix_mp.num_planes = 1; + f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; + f->fmt.pix_mp.flags = 0; +} + +/* V4L2 specification suggests the driver corrects the format struct if any of + * the dimensions is unsupported + */ +static int vidioc_try_fmt_out(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, + const struct mtk_video_fmt *fmt) +{ + struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; + int tmp_w, tmp_h; + unsigned int max_width, max_height; + + pix_fmt_mp->field = V4L2_FIELD_NONE; + + if (ctx->dev->enc_capability & MTK_VENC_4K_CAPABILITY_ENABLE) { + max_width = MTK_VENC_4K_MAX_W; + max_height = MTK_VENC_4K_MAX_H; + } else { + max_width = MTK_VENC_HD_MAX_W; + max_height = MTK_VENC_HD_MAX_H; + } + + pix_fmt_mp->height = clamp(pix_fmt_mp->height, MTK_VENC_MIN_H, max_height); + pix_fmt_mp->width = clamp(pix_fmt_mp->width, MTK_VENC_MIN_W, max_width); + + /* find next closer width align 16, heign align 32, size align + * 64 rectangle + */ + tmp_w = pix_fmt_mp->width; + tmp_h = pix_fmt_mp->height; + v4l_bound_align_image(&pix_fmt_mp->width, + MTK_VENC_MIN_W, + max_width, 4, + &pix_fmt_mp->height, + MTK_VENC_MIN_H, + max_height, 5, 6); + + if (pix_fmt_mp->width < tmp_w && (pix_fmt_mp->width + 16) <= max_width) + pix_fmt_mp->width += 16; + if (pix_fmt_mp->height < tmp_h && (pix_fmt_mp->height + 32) <= max_height) + pix_fmt_mp->height += 32; + + mtk_v4l2_debug(0, "before resize w=%d, h=%d, after resize w=%d, h=%d, sizeimage=%d %d", + tmp_w, tmp_h, pix_fmt_mp->width, + pix_fmt_mp->height, + pix_fmt_mp->plane_fmt[0].sizeimage, + pix_fmt_mp->plane_fmt[1].sizeimage); + + pix_fmt_mp->num_planes = fmt->num_planes; + pix_fmt_mp->plane_fmt[0].sizeimage = + pix_fmt_mp->width * pix_fmt_mp->height + + ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16); + pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; + + if (pix_fmt_mp->num_planes == 2) { + pix_fmt_mp->plane_fmt[1].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 2 + + (ALIGN(pix_fmt_mp->width, 16) * 16); + pix_fmt_mp->plane_fmt[2].sizeimage = 0; + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->width; + pix_fmt_mp->plane_fmt[2].bytesperline = 0; + } else if (pix_fmt_mp->num_planes == 3) { + pix_fmt_mp->plane_fmt[1].sizeimage = + pix_fmt_mp->plane_fmt[2].sizeimage = + (pix_fmt_mp->width * pix_fmt_mp->height) / 4 + + ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16); + pix_fmt_mp->plane_fmt[1].bytesperline = + pix_fmt_mp->plane_fmt[2].bytesperline = + pix_fmt_mp->width / 2; + } + + pix_fmt_mp->flags = 0; + + return 0; +} + +static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx, + struct venc_enc_param *param) +{ + struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC]; + struct mtk_enc_params *enc_params = &ctx->enc_params; + + switch (q_data_src->fmt->fourcc) { + case V4L2_PIX_FMT_YUV420M: + param->input_yuv_fmt = VENC_YUV_FORMAT_I420; + break; + case V4L2_PIX_FMT_YVU420M: + param->input_yuv_fmt = VENC_YUV_FORMAT_YV12; + break; + case V4L2_PIX_FMT_NV12M: + param->input_yuv_fmt = VENC_YUV_FORMAT_NV12; + break; + case V4L2_PIX_FMT_NV21M: + param->input_yuv_fmt = VENC_YUV_FORMAT_NV21; + break; + default: + mtk_v4l2_err("Unsupported fourcc =%d", q_data_src->fmt->fourcc); + break; + } + param->h264_profile = enc_params->h264_profile; + param->h264_level = enc_params->h264_level; + + /* Config visible resolution */ + param->width = q_data_src->visible_width; + param->height = q_data_src->visible_height; + /* Config coded resolution */ + param->buf_width = q_data_src->coded_width; + param->buf_height = q_data_src->coded_height; + param->frm_rate = enc_params->framerate_num / + enc_params->framerate_denom; + param->intra_period = enc_params->intra_period; + param->gop_size = enc_params->gop_size; + param->bitrate = enc_params->bitrate; + + mtk_v4l2_debug(0, + "fmt 0x%x, P/L %d/%d, w/h %d/%d, buf %d/%d, fps/bps %d/%d, gop %d, i_period %d", + param->input_yuv_fmt, param->h264_profile, + param->h264_level, param->width, param->height, + param->buf_width, param->buf_height, + param->frm_rate, param->bitrate, + param->gop_size, param->intra_period); +} + +static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; + struct vb2_queue *vq; + struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); + int i, ret; + const struct mtk_video_fmt *fmt; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + mtk_v4l2_err("fail to get vq"); + return -EINVAL; + } + + if (vb2_is_busy(vq)) { + mtk_v4l2_err("queue busy"); + return -EBUSY; + } + + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); + if (!fmt) { + fmt = &ctx->dev->venc_pdata->capture_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + q_data->fmt = fmt; + vidioc_try_fmt_cap(f); + + q_data->coded_width = f->fmt.pix_mp.width; + q_data->coded_height = f->fmt.pix_mp.height; + q_data->field = f->fmt.pix_mp.field; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; + q_data->bytesperline[i] = plane_fmt->bytesperline; + q_data->sizeimage[i] = plane_fmt->sizeimage; + } + + if (ctx->state == MTK_STATE_FREE) { + ret = venc_if_init(ctx, q_data->fmt->fourcc); + if (ret) { + mtk_v4l2_err("venc_if_init failed=%d, codec type=%x", + ret, q_data->fmt->fourcc); + return -EBUSY; + } + ctx->state = MTK_STATE_INIT; + } + + return 0; +} + +static int vidioc_venc_s_fmt_out(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; + struct vb2_queue *vq; + struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); + int ret, i; + const struct mtk_video_fmt *fmt; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) { + mtk_v4l2_err("fail to get vq"); + return -EINVAL; + } + + if (vb2_is_busy(vq)) { + mtk_v4l2_err("queue busy"); + return -EBUSY; + } + + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); + if (!fmt) { + fmt = &ctx->dev->venc_pdata->output_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + ret = vidioc_try_fmt_out(ctx, f, fmt); + if (ret) + return ret; + + q_data->fmt = fmt; + q_data->visible_width = f->fmt.pix_mp.width; + q_data->visible_height = f->fmt.pix_mp.height; + q_data->coded_width = f->fmt.pix_mp.width; + q_data->coded_height = f->fmt.pix_mp.height; + + q_data->field = f->fmt.pix_mp.field; + ctx->colorspace = f->fmt.pix_mp.colorspace; + ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; + ctx->quantization = f->fmt.pix_mp.quantization; + ctx->xfer_func = f->fmt.pix_mp.xfer_func; + + for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { + struct v4l2_plane_pix_format *plane_fmt; + + plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; + q_data->bytesperline[i] = plane_fmt->bytesperline; + q_data->sizeimage[i] = plane_fmt->sizeimage; + } + + return 0; +} + +static int vidioc_venc_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *vq; + struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); + int i; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + if (!vq) + return -EINVAL; + + + pix->width = q_data->coded_width; + pix->height = q_data->coded_height; + pix->pixelformat = q_data->fmt->fourcc; + pix->field = q_data->field; + pix->num_planes = q_data->fmt->num_planes; + for (i = 0; i < pix->num_planes; i++) { + pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; + pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; + } + + pix->flags = 0; + pix->colorspace = ctx->colorspace; + pix->ycbcr_enc = ctx->ycbcr_enc; + pix->quantization = ctx->quantization; + pix->xfer_func = ctx->xfer_func; + + return 0; +} + +static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; + + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); + if (!fmt) { + fmt = &ctx->dev->venc_pdata->capture_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + f->fmt.pix_mp.colorspace = ctx->colorspace; + f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; + f->fmt.pix_mp.quantization = ctx->quantization; + f->fmt.pix_mp.xfer_func = ctx->xfer_func; + + vidioc_try_fmt_cap(f); + + return 0; +} + +static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct mtk_video_fmt *fmt; + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; + + fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); + if (!fmt) { + fmt = &ctx->dev->venc_pdata->output_formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + if (!f->fmt.pix_mp.colorspace) { + f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; + f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; + f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; + } + + return vidioc_try_fmt_out(ctx, f, fmt); +} + +static int vidioc_venc_g_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, s->type); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + s->r.top = 0; + s->r.left = 0; + s->r.width = q_data->coded_width; + s->r.height = q_data->coded_height; + break; + case V4L2_SEL_TGT_CROP: + s->r.top = 0; + s->r.left = 0; + s->r.width = q_data->visible_width; + s->r.height = q_data->visible_height; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_venc_s_selection(struct file *file, void *priv, + struct v4l2_selection *s) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, s->type); + + if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + + switch (s->target) { + case V4L2_SEL_TGT_CROP: + /* Only support crop from (0,0) */ + s->r.top = 0; + s->r.left = 0; + s->r.width = min(s->r.width, q_data->coded_width); + s->r.height = min(s->r.height, q_data->coded_height); + q_data->visible_width = s->r.width; + q_data->visible_height = s->r.height; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_venc_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int vidioc_venc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + int ret; + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", + ctx->id); + return -EIO; + } + + ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); + if (ret) + return ret; + + /* + * Complete flush if the user dequeued the 0-payload LAST buffer. + * We check the payload because a buffer with the LAST flag can also + * be seen during resolution changes. If we happen to be flushing at + * that time, the last buffer before the resolution changes could be + * misinterpreted for the buffer generated by the flush and terminate + * it earlier than we want. + */ + if (!V4L2_TYPE_IS_OUTPUT(buf->type) && + buf->flags & V4L2_BUF_FLAG_LAST && + buf->m.planes[0].bytesused == 0 && + ctx->is_flushing) { + /* + * Last CAPTURE buffer is dequeued, we can allow another flush + * to take place. + */ + ctx->is_flushing = false; + } + + return 0; +} + +static int vidioc_encoder_cmd(struct file *file, void *priv, + struct v4l2_encoder_cmd *cmd) +{ + struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); + struct vb2_queue *src_vq, *dst_vq; + int ret; + + if (ctx->state == MTK_STATE_ABORT) { + mtk_v4l2_err("[%d] Call to CMD after unrecoverable error", + ctx->id); + return -EIO; + } + + ret = v4l2_m2m_ioctl_try_encoder_cmd(file, priv, cmd); + if (ret) + return ret; + + /* Calling START or STOP is invalid if a flush is in progress */ + if (ctx->is_flushing) + return -EBUSY; + + mtk_v4l2_debug(1, "encoder cmd=%u", cmd->cmd); + + dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + switch (cmd->cmd) { + case V4L2_ENC_CMD_STOP: + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + if (!vb2_is_streaming(src_vq)) { + mtk_v4l2_debug(1, "Output stream is off. No need to flush."); + return 0; + } + if (!vb2_is_streaming(dst_vq)) { + mtk_v4l2_debug(1, "Capture stream is off. No need to flush."); + return 0; + } + ctx->is_flushing = true; + v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf.vb); + v4l2_m2m_try_schedule(ctx->m2m_ctx); + break; + + case V4L2_ENC_CMD_START: + vb2_clear_last_buffer_dequeued(dst_vq); + break; + + default: + return -EINVAL; + } + + return 0; +} + +const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, + + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = vidioc_venc_qbuf, + .vidioc_dqbuf = vidioc_venc_dqbuf, + + .vidioc_querycap = vidioc_venc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + + .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, + .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, + .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_s_parm = vidioc_venc_s_parm, + .vidioc_g_parm = vidioc_venc_g_parm, + .vidioc_s_fmt_vid_cap_mplane = vidioc_venc_s_fmt_cap, + .vidioc_s_fmt_vid_out_mplane = vidioc_venc_s_fmt_out, + + .vidioc_g_fmt_vid_cap_mplane = vidioc_venc_g_fmt, + .vidioc_g_fmt_vid_out_mplane = vidioc_venc_g_fmt, + + .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, + .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, + + .vidioc_g_selection = vidioc_venc_g_selection, + .vidioc_s_selection = vidioc_venc_s_selection, + + .vidioc_encoder_cmd = vidioc_encoder_cmd, + .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, +}; + +static int vb2ops_venc_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); + struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, vq->type); + unsigned int i; + + if (q_data == NULL) + return -EINVAL; + + if (*nplanes) { + for (i = 0; i < *nplanes; i++) + if (sizes[i] < q_data->sizeimage[i]) + return -EINVAL; + } else { + *nplanes = q_data->fmt->num_planes; + for (i = 0; i < *nplanes; i++) + sizes[i] = q_data->sizeimage[i]; + } + + return 0; +} + +static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, vb->vb2_queue->type); + int i; + + for (i = 0; i < q_data->fmt->num_planes; i++) { + if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { + mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", + i, vb2_plane_size(vb, i), + q_data->sizeimage[i]); + return -EINVAL; + } + } + + return 0; +} + +static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_v4l2_buffer *vb2_v4l2 = + container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + + struct mtk_video_enc_buf *mtk_buf = + container_of(vb2_v4l2, struct mtk_video_enc_buf, + m2m_buf.vb); + + if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && + (ctx->param_change != MTK_ENCODE_PARAM_NONE)) { + mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x", + ctx->id, + vb2_v4l2->vb2_buf.index, + ctx->param_change); + mtk_buf->param_change = ctx->param_change; + mtk_buf->enc_params = ctx->enc_params; + ctx->param_change = MTK_ENCODE_PARAM_NONE; + } + + v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); +} + +static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct venc_enc_param param; + int ret, pm_ret; + int i; + + /* Once state turn into MTK_STATE_ABORT, we need stop_streaming + * to clear it + */ + if ((ctx->state == MTK_STATE_ABORT) || (ctx->state == MTK_STATE_FREE)) { + ret = -EIO; + goto err_start_stream; + } + + /* Do the initialization when both start_streaming have been called */ + if (V4L2_TYPE_IS_OUTPUT(q->type)) { + if (!vb2_start_streaming_called(&ctx->m2m_ctx->cap_q_ctx.q)) + return 0; + } else { + if (!vb2_start_streaming_called(&ctx->m2m_ctx->out_q_ctx.q)) + return 0; + } + + ret = pm_runtime_resume_and_get(&ctx->dev->plat_dev->dev); + if (ret < 0) { + mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); + goto err_start_stream; + } + + mtk_venc_set_param(ctx, ¶m); + ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, ¶m); + if (ret) { + mtk_v4l2_err("venc_if_set_param failed=%d", ret); + ctx->state = MTK_STATE_ABORT; + goto err_set_param; + } + ctx->param_change = MTK_ENCODE_PARAM_NONE; + + if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && + (ctx->enc_params.seq_hdr_mode != + V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)) { + ret = venc_if_set_param(ctx, + VENC_SET_PARAM_PREPEND_HEADER, + NULL); + if (ret) { + mtk_v4l2_err("venc_if_set_param failed=%d", ret); + ctx->state = MTK_STATE_ABORT; + goto err_set_param; + } + ctx->state = MTK_STATE_HEADER; + } + + return 0; + +err_set_param: + pm_ret = pm_runtime_put(&ctx->dev->plat_dev->dev); + if (pm_ret < 0) + mtk_v4l2_err("pm_runtime_put fail %d", pm_ret); + +err_start_stream: + for (i = 0; i < q->num_buffers; ++i) { + struct vb2_buffer *buf = vb2_get_buffer(q, i); + + /* + * FIXME: This check is not needed as only active buffers + * can be marked as done. + */ + if (buf->state == VB2_BUF_STATE_ACTIVE) { + mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED", + ctx->id, i, q->type, + (int)buf->state); + v4l2_m2m_buf_done(to_vb2_v4l2_buffer(buf), + VB2_BUF_STATE_QUEUED); + } + } + + return ret; +} + +static void vb2ops_venc_stop_streaming(struct vb2_queue *q) +{ + struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); + struct vb2_v4l2_buffer *src_buf, *dst_buf; + int ret; + + mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type); + + if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { + while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + } + /* STREAMOFF on the CAPTURE queue completes any ongoing flush */ + if (ctx->is_flushing) { + struct v4l2_m2m_buffer *b, *n; + + mtk_v4l2_debug(1, "STREAMOFF called while flushing"); + /* + * STREAMOFF could be called before the flush buffer is + * dequeued. Check whether empty flush buf is still in + * queue before removing it. + */ + v4l2_m2m_for_each_src_buf_safe(ctx->m2m_ctx, b, n) { + if (b == &ctx->empty_flush_buf) { + v4l2_m2m_src_buf_remove_by_buf(ctx->m2m_ctx, &b->vb); + break; + } + } + ctx->is_flushing = false; + } + } else { + while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { + if (src_buf != &ctx->empty_flush_buf.vb) + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + } + if (ctx->is_flushing) { + /* + * If we are in the middle of a flush, put the flush + * buffer back into the queue so the next CAPTURE + * buffer gets returned with the LAST flag set. + */ + v4l2_m2m_buf_queue(ctx->m2m_ctx, + &ctx->empty_flush_buf.vb); + } + } + + if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && + vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q)) || + (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && + vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q))) { + mtk_v4l2_debug(1, "[%d]-> q type %d out=%d cap=%d", + ctx->id, q->type, + vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q), + vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q)); + return; + } + + /* Release the encoder if both streams are stopped. */ + ret = venc_if_deinit(ctx); + if (ret) + mtk_v4l2_err("venc_if_deinit failed=%d", ret); + + ret = pm_runtime_put(&ctx->dev->plat_dev->dev); + if (ret < 0) + mtk_v4l2_err("pm_runtime_put fail %d", ret); + + ctx->state = MTK_STATE_FREE; +} + +static int vb2ops_venc_buf_out_validate(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); + + vbuf->field = V4L2_FIELD_NONE; + return 0; +} + +static const struct vb2_ops mtk_venc_vb2_ops = { + .queue_setup = vb2ops_venc_queue_setup, + .buf_out_validate = vb2ops_venc_buf_out_validate, + .buf_prepare = vb2ops_venc_buf_prepare, + .buf_queue = vb2ops_venc_buf_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vb2ops_venc_start_streaming, + .stop_streaming = vb2ops_venc_stop_streaming, +}; + +static int mtk_venc_encode_header(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + int ret; + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct mtk_vcodec_mem bs_buf; + struct venc_done_result enc_result; + + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (!dst_buf) { + mtk_v4l2_debug(1, "No dst buffer"); + return -EINVAL; + } + + bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; + + mtk_v4l2_debug(1, + "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", + ctx->id, + dst_buf->vb2_buf.index, bs_buf.va, + (u64)bs_buf.dma_addr, + bs_buf.size); + + ret = venc_if_encode(ctx, + VENC_START_OPT_ENCODE_SEQUENCE_HEADER, + NULL, &bs_buf, &enc_result); + + if (ret) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + ctx->state = MTK_STATE_ABORT; + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + mtk_v4l2_err("venc_if_encode failed=%d", ret); + return -EINVAL; + } + src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + if (src_buf) { + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; + } else { + mtk_v4l2_err("No timestamp for the header buffer."); + } + + ctx->state = MTK_STATE_HEADER; + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_result.bs_size); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + + return 0; +} + +static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) +{ + struct venc_enc_param enc_prm; + struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + struct mtk_video_enc_buf *mtk_buf; + int ret = 0; + + /* Don't upcast the empty flush buffer */ + if (vb2_v4l2 == &ctx->empty_flush_buf.vb) + return 0; + + mtk_buf = container_of(vb2_v4l2, struct mtk_video_enc_buf, m2m_buf.vb); + + memset(&enc_prm, 0, sizeof(enc_prm)); + if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE) + return 0; + + if (mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE) { + enc_prm.bitrate = mtk_buf->enc_params.bitrate; + mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d", + ctx->id, + vb2_v4l2->vb2_buf.index, + enc_prm.bitrate); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_ADJUST_BITRATE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMERATE) { + enc_prm.frm_rate = mtk_buf->enc_params.framerate_num / + mtk_buf->enc_params.framerate_denom; + mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d", + ctx->id, + vb2_v4l2->vb2_buf.index, + enc_prm.frm_rate); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_ADJUST_FRAMERATE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_GOP_SIZE) { + enc_prm.gop_size = mtk_buf->enc_params.gop_size; + mtk_v4l2_debug(1, "change param intra period=%d", + enc_prm.gop_size); + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_GOP_SIZE, + &enc_prm); + } + if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) { + mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d", + ctx->id, + vb2_v4l2->vb2_buf.index, + mtk_buf->enc_params.force_intra); + if (mtk_buf->enc_params.force_intra) + ret |= venc_if_set_param(ctx, + VENC_SET_PARAM_FORCE_INTRA, + NULL); + } + + mtk_buf->param_change = MTK_ENCODE_PARAM_NONE; + + if (ret) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_err("venc_if_set_param %d failed=%d", + mtk_buf->param_change, ret); + return -1; + } + + return 0; +} + +/* + * v4l2_m2m_streamoff() holds dev_mutex and waits mtk_venc_worker() + * to call v4l2_m2m_job_finish(). + * If mtk_venc_worker() tries to acquire dev_mutex, it will deadlock. + * So this function must not try to acquire dev->dev_mutex. + * This means v4l2 ioctls and mtk_venc_worker() can run at the same time. + * mtk_venc_worker() should be carefully implemented to avoid bugs. + */ +static void mtk_venc_worker(struct work_struct *work) +{ + struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, + encode_work); + struct vb2_v4l2_buffer *src_buf, *dst_buf; + struct venc_frm_buf frm_buf; + struct mtk_vcodec_mem bs_buf; + struct venc_done_result enc_result; + int ret, i; + + /* check dst_buf, dst_buf may be removed in device_run + * to stored encdoe header so we need check dst_buf and + * call job_finish here to prevent recursion + */ + dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + if (!dst_buf) { + v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); + return; + } + + src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + + /* + * If we see the flush buffer, send an empty buffer with the LAST flag + * to the client. is_flushing will be reset at the time the buffer + * is dequeued. + */ + if (src_buf == &ctx->empty_flush_buf.vb) { + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); + return; + } + + memset(&frm_buf, 0, sizeof(frm_buf)); + for (i = 0; i < src_buf->vb2_buf.num_planes ; i++) { + frm_buf.fb_addr[i].dma_addr = + vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, i); + frm_buf.fb_addr[i].size = + (size_t)src_buf->vb2_buf.planes[i].length; + } + bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); + bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); + bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; + + mtk_v4l2_debug(2, + "Framebuf PA=%llx Size=0x%zx;PA=0x%llx Size=0x%zx;PA=0x%llx Size=%zu", + (u64)frm_buf.fb_addr[0].dma_addr, + frm_buf.fb_addr[0].size, + (u64)frm_buf.fb_addr[1].dma_addr, + frm_buf.fb_addr[1].size, + (u64)frm_buf.fb_addr[2].dma_addr, + frm_buf.fb_addr[2].size); + + ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME, + &frm_buf, &bs_buf, &enc_result); + + dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; + dst_buf->timecode = src_buf->timecode; + + if (enc_result.is_key_frm) + dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + + if (ret) { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); + mtk_v4l2_err("venc_if_encode failed=%d", ret); + } else { + v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); + vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_result.bs_size); + v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); + mtk_v4l2_debug(2, "venc_if_encode bs size=%d", + enc_result.bs_size); + } + + v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); + + mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", + src_buf->vb2_buf.index, dst_buf->vb2_buf.index, ret, + enc_result.bs_size); +} + +static void m2mops_venc_device_run(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + + if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && + (ctx->state != MTK_STATE_HEADER)) { + /* encode h264 sps/pps header */ + mtk_venc_encode_header(ctx); + queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); + return; + } + + mtk_venc_param_change(ctx); + queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); +} + +static int m2mops_venc_job_ready(void *m2m_priv) +{ + struct mtk_vcodec_ctx *ctx = m2m_priv; + + if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) { + mtk_v4l2_debug(3, "[%d]Not ready: state=0x%x.", + ctx->id, ctx->state); + return 0; + } + + return 1; +} + +static void m2mops_venc_job_abort(void *priv) +{ + struct mtk_vcodec_ctx *ctx = priv; + + ctx->state = MTK_STATE_ABORT; +} + +const struct v4l2_m2m_ops mtk_venc_m2m_ops = { + .device_run = m2mops_venc_device_run, + .job_ready = m2mops_venc_job_ready, + .job_abort = m2mops_venc_job_abort, +}; + +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_q_data *q_data; + + ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; + ctx->fh.m2m_ctx = ctx->m2m_ctx; + ctx->fh.ctrl_handler = &ctx->ctrl_hdl; + INIT_WORK(&ctx->encode_work, mtk_venc_worker); + + ctx->colorspace = V4L2_COLORSPACE_REC709; + ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + ctx->quantization = V4L2_QUANTIZATION_DEFAULT; + ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; + + q_data = &ctx->q_data[MTK_Q_DATA_SRC]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->visible_width = DFT_CFG_WIDTH; + q_data->visible_height = DFT_CFG_HEIGHT; + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->field = V4L2_FIELD_NONE; + + q_data->fmt = &ctx->dev->venc_pdata->output_formats[0]; + + v4l_bound_align_image(&q_data->coded_width, + MTK_VENC_MIN_W, + MTK_VENC_HD_MAX_W, 4, + &q_data->coded_height, + MTK_VENC_MIN_H, + MTK_VENC_HD_MAX_H, 5, 6); + + if (q_data->coded_width < DFT_CFG_WIDTH && + (q_data->coded_width + 16) <= MTK_VENC_HD_MAX_W) + q_data->coded_width += 16; + if (q_data->coded_height < DFT_CFG_HEIGHT && + (q_data->coded_height + 32) <= MTK_VENC_HD_MAX_H) + q_data->coded_height += 32; + + q_data->sizeimage[0] = + q_data->coded_width * q_data->coded_height+ + ((ALIGN(q_data->coded_width, 16) * 2) * 16); + q_data->bytesperline[0] = q_data->coded_width; + q_data->sizeimage[1] = + (q_data->coded_width * q_data->coded_height) / 2 + + (ALIGN(q_data->coded_width, 16) * 16); + q_data->bytesperline[1] = q_data->coded_width; + + q_data = &ctx->q_data[MTK_Q_DATA_DST]; + memset(q_data, 0, sizeof(struct mtk_q_data)); + q_data->coded_width = DFT_CFG_WIDTH; + q_data->coded_height = DFT_CFG_HEIGHT; + q_data->fmt = &ctx->dev->venc_pdata->capture_formats[0]; + q_data->field = V4L2_FIELD_NONE; + ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = + DFT_CFG_WIDTH * DFT_CFG_HEIGHT; + ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0; + + ctx->enc_params.framerate_num = MTK_DEFAULT_FRAMERATE_NUM; + ctx->enc_params.framerate_denom = MTK_DEFAULT_FRAMERATE_DENOM; +} + +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) +{ + const struct v4l2_ctrl_ops *ops = &mtk_vcodec_enc_ctrl_ops; + struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl; + u8 h264_max_level; + + if (ctx->dev->enc_capability & MTK_VENC_4K_CAPABILITY_ENABLE) + h264_max_level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1; + else + h264_max_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; + + v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT); + + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, + 1, 1, 1, 1); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE, + ctx->dev->venc_pdata->min_bitrate, + ctx->dev->venc_pdata->max_bitrate, 1, 4000000); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES, + 0, 2, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, + 0, 1, 1, 1); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_MAX_QP, + 0, 51, 1, 51); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, + 0, 65535, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0, 65535, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, + 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, + 0, 0, 0, 0); + v4l2_ctrl_new_std_menu(handler, ops, + V4L2_CID_MPEG_VIDEO_HEADER_MODE, + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, + 0, V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, + V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, + 0, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, + h264_max_level, + 0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0); + v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_VP8_PROFILE, + V4L2_MPEG_VIDEO_VP8_PROFILE_0, 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); + + + if (handler->error) { + mtk_v4l2_err("Init control handler fail %d", + handler->error); + return handler->error; + } + + v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); + + return 0; +} + +int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct mtk_vcodec_ctx *ctx = priv; + int ret; + + /* Note: VB2_USERPTR works with dma-contig because mt8173 + * support iommu + * https://patchwork.kernel.org/patch/8335461/ + * https://patchwork.kernel.org/patch/7596181/ + */ + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = ctx; + src_vq->buf_struct_size = sizeof(struct mtk_video_enc_buf); + src_vq->ops = &mtk_venc_vb2_ops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->dev->dev_mutex; + src_vq->dev = &ctx->dev->plat_dev->dev; + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = ctx; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + dst_vq->ops = &mtk_venc_vb2_ops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->dev->dev_mutex; + dst_vq->dev = &ctx->dev->plat_dev->dev; + + return vb2_queue_init(dst_vq); +} + +int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_vcodec_dev *dev = ctx->dev; + + mutex_unlock(&dev->enc_mutex); + return 0; +} + +int mtk_venc_lock(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_vcodec_dev *dev = ctx->dev; + + mutex_lock(&dev->enc_mutex); + return 0; +} + +void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx) +{ + int ret = venc_if_deinit(ctx); + + if (ret) + mtk_v4l2_err("venc_if_deinit failed=%d", ret); + + ctx->state = MTK_STATE_FREE; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.h new file mode 100644 index 000000000000..513ee7993e34 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +*/ + +#ifndef _MTK_VCODEC_ENC_H_ +#define _MTK_VCODEC_ENC_H_ + +#include +#include + +#define MTK_VENC_IRQ_STATUS_SPS 0x1 +#define MTK_VENC_IRQ_STATUS_PPS 0x2 +#define MTK_VENC_IRQ_STATUS_FRM 0x4 +#define MTK_VENC_IRQ_STATUS_DRAM 0x8 +#define MTK_VENC_IRQ_STATUS_PAUSE 0x10 +#define MTK_VENC_IRQ_STATUS_SWITCH 0x20 + +#define MTK_VENC_IRQ_STATUS_OFFSET 0x05C +#define MTK_VENC_IRQ_ACK_OFFSET 0x060 + +/** + * struct mtk_video_enc_buf - Private data related to each VB2 buffer. + * @m2m_buf: M2M buffer + * @list: list that buffer link to + * @param_change: Types of encode parameter change before encoding this + * buffer + * @enc_params: Encode parameters changed before encode this buffer + */ +struct mtk_video_enc_buf { + struct v4l2_m2m_buffer m2m_buf; + + u32 param_change; + struct mtk_enc_params enc_params; +}; + +extern const struct v4l2_ioctl_ops mtk_venc_ioctl_ops; +extern const struct v4l2_m2m_ops mtk_venc_m2m_ops; + +int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx); +int mtk_venc_lock(struct mtk_vcodec_ctx *ctx); +int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq); +void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx); +int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx); +void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx); + +#endif /* _MTK_VCODEC_ENC_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_drv.c new file mode 100644 index 000000000000..5172cfe0db4a --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_drv.c @@ -0,0 +1,479 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_fw.h" + +static const struct mtk_video_fmt mtk_video_formats_output[] = { + { + .fourcc = V4L2_PIX_FMT_NV12M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_NV21M, + .type = MTK_FMT_FRAME, + .num_planes = 2, + }, + { + .fourcc = V4L2_PIX_FMT_YUV420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, + { + .fourcc = V4L2_PIX_FMT_YVU420M, + .type = MTK_FMT_FRAME, + .num_planes = 3, + }, +}; + +static const struct mtk_video_fmt mtk_video_formats_capture_h264[] = { + { + .fourcc = V4L2_PIX_FMT_H264, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, +}; + +static const struct mtk_video_fmt mtk_video_formats_capture_vp8[] = { + { + .fourcc = V4L2_PIX_FMT_VP8, + .type = MTK_FMT_ENC, + .num_planes = 1, + }, +}; + +static void clean_irq_status(unsigned int irq_status, void __iomem *addr) +{ + if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE) + writel(MTK_VENC_IRQ_STATUS_PAUSE, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_SWITCH) + writel(MTK_VENC_IRQ_STATUS_SWITCH, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_DRAM) + writel(MTK_VENC_IRQ_STATUS_DRAM, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_SPS) + writel(MTK_VENC_IRQ_STATUS_SPS, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_PPS) + writel(MTK_VENC_IRQ_STATUS_PPS, addr); + + if (irq_status & MTK_VENC_IRQ_STATUS_FRM) + writel(MTK_VENC_IRQ_STATUS_FRM, addr); + +} +static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + unsigned long flags; + void __iomem *addr; + + spin_lock_irqsave(&dev->irqlock, flags); + ctx = dev->curr_ctx; + spin_unlock_irqrestore(&dev->irqlock, flags); + + mtk_v4l2_debug(1, "id=%d coreid:%d", ctx->id, dev->venc_pdata->core_id); + addr = dev->reg_base[dev->venc_pdata->core_id] + + MTK_VENC_IRQ_ACK_OFFSET; + + ctx->irq_status = readl(dev->reg_base[dev->venc_pdata->core_id] + + (MTK_VENC_IRQ_STATUS_OFFSET)); + + clean_irq_status(ctx->irq_status, addr); + + wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); + return IRQ_HANDLED; +} + +static int fops_vcodec_open(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = NULL; + int ret = 0; + struct vb2_queue *src_vq; + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + mutex_lock(&dev->dev_mutex); + /* + * Use simple counter to uniquely identify this context. Only + * used for logging. + */ + ctx->id = dev->id_counter++; + v4l2_fh_init(&ctx->fh, video_devdata(file)); + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + INIT_LIST_HEAD(&ctx->list); + ctx->dev = dev; + init_waitqueue_head(&ctx->queue[0]); + + ctx->type = MTK_INST_ENCODER; + ret = mtk_vcodec_enc_ctrls_setup(ctx); + if (ret) { + mtk_v4l2_err("Failed to setup controls() (%d)", + ret); + goto err_ctrls_setup; + } + ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx, + &mtk_vcodec_enc_queue_init); + if (IS_ERR((__force void *)ctx->m2m_ctx)) { + ret = PTR_ERR((__force void *)ctx->m2m_ctx); + mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", + ret); + goto err_m2m_ctx_init; + } + src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, + V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); + ctx->empty_flush_buf.vb.vb2_buf.vb2_queue = src_vq; + mtk_vcodec_enc_set_default_params(ctx); + + if (v4l2_fh_is_singular(&ctx->fh)) { + /* + * load fireware to checks if it was loaded already and + * does nothing in that case + */ + ret = mtk_vcodec_fw_load_firmware(dev->fw_handler); + if (ret < 0) { + /* + * Return 0 if downloading firmware successfully, + * otherwise it is failed + */ + mtk_v4l2_err("vpu_load_firmware failed!"); + goto err_load_fw; + } + + dev->enc_capability = + mtk_vcodec_fw_get_venc_capa(dev->fw_handler); + mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability); + } + + mtk_v4l2_debug(2, "Create instance [%d]@%p m2m_ctx=%p ", + ctx->id, ctx, ctx->m2m_ctx); + + list_add(&ctx->list, &dev->ctx_list); + + mutex_unlock(&dev->dev_mutex); + mtk_v4l2_debug(0, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), + ctx->id); + return ret; + + /* Deinit when failure occurred */ +err_load_fw: + v4l2_m2m_ctx_release(ctx->m2m_ctx); +err_m2m_ctx_init: + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); +err_ctrls_setup: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + + return ret; +} + +static int fops_vcodec_release(struct file *file) +{ + struct mtk_vcodec_dev *dev = video_drvdata(file); + struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); + + mtk_v4l2_debug(1, "[%d] encoder", ctx->id); + mutex_lock(&dev->dev_mutex); + + v4l2_m2m_ctx_release(ctx->m2m_ctx); + mtk_vcodec_enc_release(ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + + list_del_init(&ctx->list); + kfree(ctx); + mutex_unlock(&dev->dev_mutex); + return 0; +} + +static const struct v4l2_file_operations mtk_vcodec_fops = { + .owner = THIS_MODULE, + .open = fops_vcodec_open, + .release = fops_vcodec_release, + .poll = v4l2_m2m_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = v4l2_m2m_fop_mmap, +}; + +static int mtk_vcodec_probe(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev; + struct video_device *vfd_enc; + struct resource *res; + phandle rproc_phandle; + enum mtk_vcodec_fw_type fw_type; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + INIT_LIST_HEAD(&dev->ctx_list); + dev->plat_dev = pdev; + + if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", + &rproc_phandle)) { + fw_type = VPU; + } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp", + &rproc_phandle)) { + fw_type = SCP; + } else { + mtk_v4l2_err("Could not get venc IPI device"); + return -ENODEV; + } + dma_set_max_seg_size(&pdev->dev, UINT_MAX); + + dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, ENCODER); + if (IS_ERR(dev->fw_handler)) + return PTR_ERR(dev->fw_handler); + + dev->venc_pdata = of_device_get_match_data(&pdev->dev); + ret = mtk_vcodec_init_enc_clk(dev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get mtk vcodec clock source!"); + goto err_enc_pm; + } + + pm_runtime_enable(&pdev->dev); + + dev->reg_base[dev->venc_pdata->core_id] = + devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(dev->reg_base[dev->venc_pdata->core_id])) { + ret = PTR_ERR(dev->reg_base[dev->venc_pdata->core_id]); + goto err_res; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "failed to get irq resource"); + ret = -ENOENT; + goto err_res; + } + + dev->enc_irq = platform_get_irq(pdev, 0); + irq_set_status_flags(dev->enc_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, dev->enc_irq, + mtk_vcodec_enc_irq_handler, + 0, pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, + "Failed to install dev->enc_irq %d (%d) core_id (%d)", + dev->enc_irq, ret, dev->venc_pdata->core_id); + ret = -EINVAL; + goto err_res; + } + + mutex_init(&dev->enc_mutex); + mutex_init(&dev->dev_mutex); + spin_lock_init(&dev->irqlock); + + snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", + "[MTK_V4L2_VENC]"); + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) { + mtk_v4l2_err("v4l2_device_register err=%d", ret); + goto err_res; + } + + init_waitqueue_head(&dev->queue); + + /* allocate video device for encoder and register it */ + vfd_enc = video_device_alloc(); + if (!vfd_enc) { + mtk_v4l2_err("Failed to allocate video device"); + ret = -ENOMEM; + goto err_enc_alloc; + } + vfd_enc->fops = &mtk_vcodec_fops; + vfd_enc->ioctl_ops = &mtk_venc_ioctl_ops; + vfd_enc->release = video_device_release; + vfd_enc->lock = &dev->dev_mutex; + vfd_enc->v4l2_dev = &dev->v4l2_dev; + vfd_enc->vfl_dir = VFL_DIR_M2M; + vfd_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | + V4L2_CAP_STREAMING; + + snprintf(vfd_enc->name, sizeof(vfd_enc->name), "%s", + MTK_VCODEC_ENC_NAME); + video_set_drvdata(vfd_enc, dev); + dev->vfd_enc = vfd_enc; + platform_set_drvdata(pdev, dev); + + dev->m2m_dev_enc = v4l2_m2m_init(&mtk_venc_m2m_ops); + if (IS_ERR((__force void *)dev->m2m_dev_enc)) { + mtk_v4l2_err("Failed to init mem2mem enc device"); + ret = PTR_ERR((__force void *)dev->m2m_dev_enc); + goto err_enc_mem_init; + } + + dev->encode_workqueue = + alloc_ordered_workqueue(MTK_VCODEC_ENC_NAME, + WQ_MEM_RECLAIM | + WQ_FREEZABLE); + if (!dev->encode_workqueue) { + mtk_v4l2_err("Failed to create encode workqueue"); + ret = -EINVAL; + goto err_event_workq; + } + + if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) + dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); + + ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, -1); + if (ret) { + mtk_v4l2_err("Failed to register video device"); + goto err_enc_reg; + } + + mtk_v4l2_debug(0, "encoder %d registered as /dev/video%d", + dev->venc_pdata->core_id, vfd_enc->num); + + return 0; + +err_enc_reg: + destroy_workqueue(dev->encode_workqueue); +err_event_workq: + v4l2_m2m_release(dev->m2m_dev_enc); +err_enc_mem_init: + video_unregister_device(vfd_enc); +err_enc_alloc: + v4l2_device_unregister(&dev->v4l2_dev); +err_res: + pm_runtime_disable(dev->pm.dev); +err_enc_pm: + mtk_vcodec_fw_release(dev->fw_handler); + return ret; +} + +static const struct mtk_vcodec_enc_pdata mt8173_avc_pdata = { + .chip = MTK_MT8173, + .capture_formats = mtk_video_formats_capture_h264, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), + .output_formats = mtk_video_formats_output, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), + .min_bitrate = 64, + .max_bitrate = 60000000, + .core_id = VENC_SYS, +}; + +static const struct mtk_vcodec_enc_pdata mt8173_vp8_pdata = { + .chip = MTK_MT8173, + .capture_formats = mtk_video_formats_capture_vp8, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_vp8), + .output_formats = mtk_video_formats_output, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), + .min_bitrate = 64, + .max_bitrate = 9000000, + .core_id = VENC_LT_SYS, +}; + +static const struct mtk_vcodec_enc_pdata mt8183_pdata = { + .chip = MTK_MT8183, + .uses_ext = true, + .capture_formats = mtk_video_formats_capture_h264, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), + .output_formats = mtk_video_formats_output, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), + .min_bitrate = 64, + .max_bitrate = 40000000, + .core_id = VENC_SYS, +}; + +static const struct mtk_vcodec_enc_pdata mt8192_pdata = { + .chip = MTK_MT8192, + .uses_ext = true, + .capture_formats = mtk_video_formats_capture_h264, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), + .output_formats = mtk_video_formats_output, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), + .min_bitrate = 64, + .max_bitrate = 100000000, + .core_id = VENC_SYS, +}; + +static const struct mtk_vcodec_enc_pdata mt8195_pdata = { + .chip = MTK_MT8195, + .uses_ext = true, + .capture_formats = mtk_video_formats_capture_h264, + .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), + .output_formats = mtk_video_formats_output, + .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), + .min_bitrate = 64, + .max_bitrate = 100000000, + .core_id = VENC_SYS, +}; + +static const struct of_device_id mtk_vcodec_enc_match[] = { + {.compatible = "mediatek,mt8173-vcodec-enc", + .data = &mt8173_avc_pdata}, + {.compatible = "mediatek,mt8173-vcodec-enc-vp8", + .data = &mt8173_vp8_pdata}, + {.compatible = "mediatek,mt8183-vcodec-enc", .data = &mt8183_pdata}, + {.compatible = "mediatek,mt8192-vcodec-enc", .data = &mt8192_pdata}, + {.compatible = "mediatek,mt8195-vcodec-enc", .data = &mt8195_pdata}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); + +static int mtk_vcodec_enc_remove(struct platform_device *pdev) +{ + struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); + + mtk_v4l2_debug_enter(); + destroy_workqueue(dev->encode_workqueue); + if (dev->m2m_dev_enc) + v4l2_m2m_release(dev->m2m_dev_enc); + + if (dev->vfd_enc) + video_unregister_device(dev->vfd_enc); + + v4l2_device_unregister(&dev->v4l2_dev); + pm_runtime_disable(dev->pm.dev); + mtk_vcodec_fw_release(dev->fw_handler); + return 0; +} + +static struct platform_driver mtk_vcodec_enc_driver = { + .probe = mtk_vcodec_probe, + .remove = mtk_vcodec_enc_remove, + .driver = { + .name = MTK_VCODEC_ENC_NAME, + .of_match_table = mtk_vcodec_enc_match, + }, +}; + +module_platform_driver(mtk_vcodec_enc_driver); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video codec V4L2 encoder driver"); diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.c new file mode 100644 index 000000000000..7055954eb2af --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +*/ + +#include +#include +#include +#include + +#include "mtk_vcodec_enc_pm.h" +#include "mtk_vcodec_util.h" + +int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *mtkdev) +{ + struct platform_device *pdev; + struct mtk_vcodec_pm *pm; + struct mtk_vcodec_clk *enc_clk; + struct mtk_vcodec_clk_info *clk_info; + int ret, i; + + pdev = mtkdev->plat_dev; + pm = &mtkdev->pm; + memset(pm, 0, sizeof(struct mtk_vcodec_pm)); + pm->dev = &pdev->dev; + enc_clk = &pm->venc_clk; + + enc_clk->clk_num = of_property_count_strings(pdev->dev.of_node, + "clock-names"); + if (enc_clk->clk_num > 0) { + enc_clk->clk_info = devm_kcalloc(&pdev->dev, + enc_clk->clk_num, sizeof(*clk_info), + GFP_KERNEL); + if (!enc_clk->clk_info) + return -ENOMEM; + } else { + mtk_v4l2_err("Failed to get venc clock count"); + return -EINVAL; + } + + for (i = 0; i < enc_clk->clk_num; i++) { + clk_info = &enc_clk->clk_info[i]; + ret = of_property_read_string_index(pdev->dev.of_node, + "clock-names", i, &clk_info->clk_name); + if (ret) { + mtk_v4l2_err("venc failed to get clk name %d", i); + return ret; + } + clk_info->vcodec_clk = devm_clk_get(&pdev->dev, + clk_info->clk_name); + if (IS_ERR(clk_info->vcodec_clk)) { + mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i, + clk_info->clk_name); + return PTR_ERR(clk_info->vcodec_clk); + } + } + + return 0; +} + +void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) +{ + struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; + int ret, i = 0; + + for (i = 0; i < enc_clk->clk_num; i++) { + ret = clk_prepare_enable(enc_clk->clk_info[i].vcodec_clk); + if (ret) { + mtk_v4l2_err("venc clk_prepare_enable %d %s fail %d", i, + enc_clk->clk_info[i].clk_name, ret); + goto clkerr; + } + } + + return; + +clkerr: + for (i -= 1; i >= 0; i--) + clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); +} + +void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm) +{ + struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; + int i = 0; + + for (i = enc_clk->clk_num - 1; i >= 0; i--) + clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.h new file mode 100644 index 000000000000..bc455cefc0cd --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_enc_pm.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +*/ + +#ifndef _MTK_VCODEC_ENC_PM_H_ +#define _MTK_VCODEC_ENC_PM_H_ + +#include "mtk_vcodec_drv.h" + +int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *dev); + +void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm); +void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm); + +#endif /* _MTK_VCODEC_ENC_PM_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.c new file mode 100644 index 000000000000..94b39ae5c2e1 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "mtk_vcodec_fw.h" +#include "mtk_vcodec_fw_priv.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_drv.h" + +struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, + enum mtk_vcodec_fw_type type, + enum mtk_vcodec_fw_use fw_use) +{ + switch (type) { + case VPU: + return mtk_vcodec_fw_vpu_init(dev, fw_use); + case SCP: + return mtk_vcodec_fw_scp_init(dev); + default: + mtk_v4l2_err("invalid vcodec fw type"); + return ERR_PTR(-EINVAL); + } +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_select); + +void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw) +{ + fw->ops->release(fw); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_release); + +int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw) +{ + return fw->ops->load_firmware(fw); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_load_firmware); + +unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw) +{ + return fw->ops->get_vdec_capa(fw); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_vdec_capa); + +unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw) +{ + return fw->ops->get_venc_capa(fw); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_venc_capa); + +void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr) +{ + return fw->ops->map_dm_addr(fw, mem_addr); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_map_dm_addr); + +int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv) +{ + return fw->ops->ipi_register(fw, id, handler, name, priv); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_register); + +int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait) +{ + return fw->ops->ipi_send(fw, id, buf, len, wait); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_send); diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.h new file mode 100644 index 000000000000..539bb626772c --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _MTK_VCODEC_FW_H_ +#define _MTK_VCODEC_FW_H_ + +#include +#include + +#include "../mtk-vpu/mtk_vpu.h" + +struct mtk_vcodec_dev; + +enum mtk_vcodec_fw_type { + VPU, + SCP, +}; + +enum mtk_vcodec_fw_use { + DECODER, + ENCODER, +}; + +struct mtk_vcodec_fw; + +typedef void (*mtk_vcodec_ipi_handler) (void *data, + unsigned int len, void *priv); + +struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, + enum mtk_vcodec_fw_type type, + enum mtk_vcodec_fw_use fw_use); +void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw); + +int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw); +unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw); +unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw); +void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr); +int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv); +int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id, + void *buf, unsigned int len, unsigned int wait); + +#endif /* _MTK_VCODEC_FW_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_priv.h new file mode 100644 index 000000000000..b41e66185cec --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_priv.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _MTK_VCODEC_FW_PRIV_H_ +#define _MTK_VCODEC_FW_PRIV_H_ + +#include "mtk_vcodec_fw.h" + +struct mtk_vcodec_dev; + +struct mtk_vcodec_fw { + enum mtk_vcodec_fw_type type; + const struct mtk_vcodec_fw_ops *ops; + struct platform_device *pdev; + struct mtk_scp *scp; +}; + +struct mtk_vcodec_fw_ops { + int (*load_firmware)(struct mtk_vcodec_fw *fw); + unsigned int (*get_vdec_capa)(struct mtk_vcodec_fw *fw); + unsigned int (*get_venc_capa)(struct mtk_vcodec_fw *fw); + void *(*map_dm_addr)(struct mtk_vcodec_fw *fw, u32 dtcm_dmem_addr); + int (*ipi_register)(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, const char *name, + void *priv); + int (*ipi_send)(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait); + void (*release)(struct mtk_vcodec_fw *fw); +}; + +#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU) +struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, + enum mtk_vcodec_fw_use fw_use); +#else +static inline struct mtk_vcodec_fw * +mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, + enum mtk_vcodec_fw_use fw_use) +{ + return ERR_PTR(-ENODEV); +} +#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_VPU */ + +#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP) +struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev); +#else +static inline struct mtk_vcodec_fw * +mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev) +{ + return ERR_PTR(-ENODEV); +} +#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_SCP */ + +#endif /* _MTK_VCODEC_FW_PRIV_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_scp.c new file mode 100644 index 000000000000..d8e66b645bd8 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_scp.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "mtk_vcodec_fw_priv.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_drv.h" + +static int mtk_vcodec_scp_load_firmware(struct mtk_vcodec_fw *fw) +{ + return rproc_boot(scp_get_rproc(fw->scp)); +} + +static unsigned int mtk_vcodec_scp_get_vdec_capa(struct mtk_vcodec_fw *fw) +{ + return scp_get_vdec_hw_capa(fw->scp); +} + +static unsigned int mtk_vcodec_scp_get_venc_capa(struct mtk_vcodec_fw *fw) +{ + return scp_get_venc_hw_capa(fw->scp); +} + +static void *mtk_vcodec_vpu_scp_dm_addr(struct mtk_vcodec_fw *fw, + u32 dtcm_dmem_addr) +{ + return scp_mapping_dm_addr(fw->scp, dtcm_dmem_addr); +} + +static int mtk_vcodec_scp_set_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv) +{ + return scp_ipi_register(fw->scp, id, handler, priv); +} + +static int mtk_vcodec_scp_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait) +{ + return scp_ipi_send(fw->scp, id, buf, len, wait); +} + +static void mtk_vcodec_scp_release(struct mtk_vcodec_fw *fw) +{ + scp_put(fw->scp); +} + +static const struct mtk_vcodec_fw_ops mtk_vcodec_rproc_msg = { + .load_firmware = mtk_vcodec_scp_load_firmware, + .get_vdec_capa = mtk_vcodec_scp_get_vdec_capa, + .get_venc_capa = mtk_vcodec_scp_get_venc_capa, + .map_dm_addr = mtk_vcodec_vpu_scp_dm_addr, + .ipi_register = mtk_vcodec_scp_set_ipi_register, + .ipi_send = mtk_vcodec_scp_ipi_send, + .release = mtk_vcodec_scp_release, +}; + +struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev) +{ + struct mtk_vcodec_fw *fw; + struct mtk_scp *scp; + + scp = scp_get(dev->plat_dev); + if (!scp) { + mtk_v4l2_err("could not get vdec scp handle"); + return ERR_PTR(-EPROBE_DEFER); + } + + fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); + fw->type = SCP; + fw->ops = &mtk_vcodec_rproc_msg; + fw->scp = scp; + + return fw; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_vpu.c new file mode 100644 index 000000000000..cfc7ebed8fb7 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_fw_vpu.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "mtk_vcodec_fw_priv.h" +#include "mtk_vcodec_util.h" +#include "mtk_vcodec_drv.h" + +static int mtk_vcodec_vpu_load_firmware(struct mtk_vcodec_fw *fw) +{ + return vpu_load_firmware(fw->pdev); +} + +static unsigned int mtk_vcodec_vpu_get_vdec_capa(struct mtk_vcodec_fw *fw) +{ + return vpu_get_vdec_hw_capa(fw->pdev); +} + +static unsigned int mtk_vcodec_vpu_get_venc_capa(struct mtk_vcodec_fw *fw) +{ + return vpu_get_venc_hw_capa(fw->pdev); +} + +static void *mtk_vcodec_vpu_map_dm_addr(struct mtk_vcodec_fw *fw, + u32 dtcm_dmem_addr) +{ + return vpu_mapping_dm_addr(fw->pdev, dtcm_dmem_addr); +} + +static int mtk_vcodec_vpu_set_ipi_register(struct mtk_vcodec_fw *fw, int id, + mtk_vcodec_ipi_handler handler, + const char *name, void *priv) +{ + /* + * The handler we receive takes a void * as its first argument. We + * cannot change this because it needs to be passed down to the rproc + * subsystem when SCP is used. VPU takes a const argument, which is + * more constrained, so the conversion below is safe. + */ + ipi_handler_t handler_const = (ipi_handler_t)handler; + + return vpu_ipi_register(fw->pdev, id, handler_const, name, priv); +} + +static int mtk_vcodec_vpu_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, + unsigned int len, unsigned int wait) +{ + return vpu_ipi_send(fw->pdev, id, buf, len); +} + +static void mtk_vcodec_vpu_release(struct mtk_vcodec_fw *fw) +{ + put_device(&fw->pdev->dev); +} + +static void mtk_vcodec_vpu_reset_handler(void *priv) +{ + struct mtk_vcodec_dev *dev = priv; + struct mtk_vcodec_ctx *ctx; + + mtk_v4l2_err("Watchdog timeout!!"); + + mutex_lock(&dev->dev_mutex); + list_for_each_entry(ctx, &dev->ctx_list, list) { + ctx->state = MTK_STATE_ABORT; + mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", + ctx->id); + } + mutex_unlock(&dev->dev_mutex); +} + +static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = { + .load_firmware = mtk_vcodec_vpu_load_firmware, + .get_vdec_capa = mtk_vcodec_vpu_get_vdec_capa, + .get_venc_capa = mtk_vcodec_vpu_get_venc_capa, + .map_dm_addr = mtk_vcodec_vpu_map_dm_addr, + .ipi_register = mtk_vcodec_vpu_set_ipi_register, + .ipi_send = mtk_vcodec_vpu_ipi_send, + .release = mtk_vcodec_vpu_release, +}; + +struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, + enum mtk_vcodec_fw_use fw_use) +{ + struct platform_device *fw_pdev; + struct mtk_vcodec_fw *fw; + enum rst_id rst_id; + + switch (fw_use) { + case ENCODER: + rst_id = VPU_RST_ENC; + break; + case DECODER: + default: + rst_id = VPU_RST_DEC; + break; + } + + fw_pdev = vpu_get_plat_device(dev->plat_dev); + if (!fw_pdev) { + mtk_v4l2_err("firmware device is not ready"); + return ERR_PTR(-EINVAL); + } + vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_handler, dev, rst_id); + + fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); + if (!fw) + return ERR_PTR(-ENOMEM); + fw->type = VPU; + fw->ops = &mtk_vcodec_vpu_msg; + fw->pdev = fw_pdev; + + return fw; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.c new file mode 100644 index 000000000000..552b4c93d972 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +*/ + +#include +#include + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_intr.h" +#include "mtk_vcodec_util.h" + +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, + int command, unsigned int timeout_ms, + unsigned int hw_id) +{ + long timeout_jiff, ret; + int status = 0; + + timeout_jiff = msecs_to_jiffies(timeout_ms); + ret = wait_event_interruptible_timeout(ctx->queue[hw_id], + ctx->int_cond[hw_id], + timeout_jiff); + + if (!ret) { + status = -1; /* timeout */ + mtk_v4l2_err("[%d] cmd=%d, type=%d, dec timeout=%ums (%d %d)", + ctx->id, command, ctx->type, timeout_ms, + ctx->int_cond[hw_id], ctx->int_type[hw_id]); + } else if (-ERESTARTSYS == ret) { + status = -1; + mtk_v4l2_err("[%d] cmd=%d, type=%d, dec inter fail (%d %d)", + ctx->id, command, ctx->type, + ctx->int_cond[hw_id], ctx->int_type[hw_id]); + } + + ctx->int_cond[hw_id] = 0; + ctx->int_type[hw_id] = 0; + + return status; +} +EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.h new file mode 100644 index 000000000000..9681f492813b --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_intr.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: Tiffany Lin +*/ + +#ifndef _MTK_VCODEC_INTR_H_ +#define _MTK_VCODEC_INTR_H_ + +#define MTK_INST_IRQ_RECEIVED 0x1 + +struct mtk_vcodec_ctx; + +/* timeout is ms */ +int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, + int command, unsigned int timeout_ms, + unsigned int hw_id); + +#endif /* _MTK_VCODEC_INTR_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.c new file mode 100644 index 000000000000..ace78c4b5b9e --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +*/ + +#include +#include +#include + +#include "mtk_vcodec_dec_hw.h" +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" + +void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, + unsigned int reg_idx) +{ + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + + if (!data || reg_idx >= NUM_MAX_VCODEC_REG_BASE) { + mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); + return NULL; + } + return ctx->dev->reg_base[reg_idx]; +} +EXPORT_SYMBOL(mtk_vcodec_get_reg_addr); + +int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL); + if (!mem->va) { + mtk_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev), + size); + return -ENOMEM; + } + + mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + + return 0; +} +EXPORT_SYMBOL(mtk_vcodec_mem_alloc); + +void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem) +{ + unsigned long size = mem->size; + struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; + struct device *dev = &ctx->dev->plat_dev->dev; + + if (!mem->va) { + mtk_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev), + size); + return; + } + + mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); + mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, + (unsigned long)mem->dma_addr); + mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); + + dma_free_coherent(dev, size, mem->va, mem->dma_addr); + mem->va = NULL; + mem->dma_addr = 0; + mem->size = 0; +} +EXPORT_SYMBOL(mtk_vcodec_mem_free); + +void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx) +{ + if (hw_idx >= MTK_VDEC_HW_MAX || hw_idx < 0 || !dev->subdev_dev[hw_idx]) { + mtk_v4l2_err("hw idx is out of range:%d", hw_idx); + return NULL; + } + + return dev->subdev_dev[hw_idx]; +} +EXPORT_SYMBOL(mtk_vcodec_get_hw_dev); + +void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + struct mtk_vcodec_ctx *ctx, int hw_idx) +{ + unsigned long flags; + struct mtk_vdec_hw_dev *subdev_dev; + + spin_lock_irqsave(&vdec_dev->irqlock, flags); + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev"); + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); + return; + } + subdev_dev->curr_ctx = ctx; + } else { + vdec_dev->curr_ctx = ctx; + } + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); +} +EXPORT_SYMBOL(mtk_vcodec_set_curr_ctx); + +struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + unsigned int hw_idx) +{ + unsigned long flags; + struct mtk_vcodec_ctx *ctx; + struct mtk_vdec_hw_dev *subdev_dev; + + spin_lock_irqsave(&vdec_dev->irqlock, flags); + if (vdec_dev->vdec_pdata->is_subdev_supported) { + subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); + if (!subdev_dev) { + mtk_v4l2_err("Failed to get hw dev"); + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); + return NULL; + } + ctx = subdev_dev->curr_ctx; + } else { + ctx = vdec_dev->curr_ctx; + } + spin_unlock_irqrestore(&vdec_dev->irqlock, flags); + return ctx; +} +EXPORT_SYMBOL(mtk_vcodec_get_curr_ctx); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video codec driver"); diff --git a/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.h new file mode 100644 index 000000000000..71956627a0e2 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/mtk_vcodec_util.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Copyright (c) 2016 MediaTek Inc. +* Author: PC Chen +* Tiffany Lin +*/ + +#ifndef _MTK_VCODEC_UTIL_H_ +#define _MTK_VCODEC_UTIL_H_ + +#include +#include + +struct mtk_vcodec_mem { + size_t size; + void *va; + dma_addr_t dma_addr; +}; + +struct mtk_vcodec_fb { + size_t size; + dma_addr_t dma_addr; +}; + +struct mtk_vcodec_ctx; +struct mtk_vcodec_dev; + +#undef pr_fmt +#define pr_fmt(fmt) "%s(),%d: " fmt, __func__, __LINE__ + +#define mtk_v4l2_err(fmt, args...) \ + pr_err("[MTK_V4L2][ERROR] " fmt "\n", ##args) + +#define mtk_vcodec_err(h, fmt, args...) \ + pr_err("[MTK_VCODEC][ERROR][%d]: " fmt "\n", \ + ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) + + +#define mtk_v4l2_debug(level, fmt, args...) pr_debug(fmt, ##args) + +#define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+") +#define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-") + +#define mtk_vcodec_debug(h, fmt, args...) \ + pr_debug("[MTK_VCODEC][%d]: " fmt "\n", \ + ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) + +#define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+") +#define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-") + +void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, + unsigned int reg_idx); +int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem); +void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, + struct mtk_vcodec_mem *mem); +void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + struct mtk_vcodec_ctx *ctx, int hw_idx); +struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, + unsigned int hw_idx); +void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx); + +#endif /* _MTK_VCODEC_UTIL_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_if.c new file mode 100644 index 000000000000..481655bb6016 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_if.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + */ + +#include +#include + +#include "../vdec_drv_if.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_dec.h" +#include "../mtk_vcodec_intr.h" +#include "../vdec_vpu_if.h" +#include "../vdec_drv_base.h" + +#define NAL_NON_IDR_SLICE 0x01 +#define NAL_IDR_SLICE 0x05 +#define NAL_H264_PPS 0x08 +#define NAL_TYPE(value) ((value) & 0x1F) + +#define BUF_PREDICTION_SZ (32 * 1024) + +#define MB_UNIT_LEN 16 + +/* motion vector size (bytes) for every macro block */ +#define HW_MB_STORE_SZ 64 + +#define H264_MAX_FB_NUM 17 +#define HDR_PARSING_BUF_SZ 1024 + +#define DEC_ERR_RET(ret) ((ret) >> 16) +#define H264_ERR_NOT_VALID 3 + +/** + * struct h264_fb - h264 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 h264_fb { + uint64_t vdec_fb_va; + uint64_t y_fb_dma; + uint64_t c_fb_dma; + int32_t poc; + uint32_t reserved; +}; + +/** + * struct h264_ring_fb_list - ring frame buffer list + * @fb_list : frame buffer array + * @read_idx : read index + * @write_idx : write index + * @count : buffer count in list + * @reserved : for 8 bytes alignment + */ +struct h264_ring_fb_list { + struct h264_fb fb_list[H264_MAX_FB_NUM]; + unsigned int read_idx; + unsigned int write_idx; + unsigned int count; + unsigned int reserved; +}; + +/** + * struct vdec_h264_dec_info - decode information + * @dpb_sz : decoding picture buffer size + * @resolution_changed : resolution change happen + * @realloc_mv_buf : flag to notify driver to re-allocate mv buffer + * @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_h264_dec_info { + uint32_t dpb_sz; + uint32_t resolution_changed; + uint32_t realloc_mv_buf; + uint32_t reserved; + uint64_t bs_dma; + uint64_t y_fb_dma; + uint64_t c_fb_dma; + uint64_t vdec_fb_va; +}; + +/** + * struct vdec_h264_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) + * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) + * @mv_buf_dma : HW working motion vector buffer dma address (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_h264_vsi { + unsigned char hdr_buf[HDR_PARSING_BUF_SZ]; + uint64_t pred_buf_dma; + uint64_t mv_buf_dma[H264_MAX_FB_NUM]; + struct h264_ring_fb_list list_free; + struct h264_ring_fb_list list_disp; + struct vdec_h264_dec_info dec; + struct vdec_pic_info pic; + struct v4l2_rect crop; +}; + +/** + * struct vdec_h264_inst - h264 decoder instance + * @num_nalu : how many nalus be decoded + * @ctx : point to mtk_vcodec_ctx + * @pred_buf : HW working predication buffer + * @mv_buf : HW working motion vector buffer + * @vpu : VPU instance + * @vsi : VPU shared information + */ +struct vdec_h264_inst { + unsigned int num_nalu; + struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_mem pred_buf; + struct mtk_vcodec_mem mv_buf[H264_MAX_FB_NUM]; + struct vdec_vpu_inst vpu; + struct vdec_h264_vsi *vsi; +}; + +static unsigned int get_mv_buf_size(unsigned int width, unsigned int height) +{ + return HW_MB_STORE_SZ * (width/MB_UNIT_LEN) * (height/MB_UNIT_LEN); +} + +static int allocate_predication_buf(struct vdec_h264_inst *inst) +{ + int err = 0; + + inst->pred_buf.size = BUF_PREDICTION_SZ; + err = mtk_vcodec_mem_alloc(inst->ctx, &inst->pred_buf); + if (err) { + mtk_vcodec_err(inst, "failed to allocate ppl buf"); + return err; + } + + inst->vsi->pred_buf_dma = inst->pred_buf.dma_addr; + return 0; +} + +static void free_predication_buf(struct vdec_h264_inst *inst) +{ + struct mtk_vcodec_mem *mem = NULL; + + mtk_vcodec_debug_enter(inst); + + inst->vsi->pred_buf_dma = 0; + mem = &inst->pred_buf; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); +} + +static int alloc_mv_buf(struct vdec_h264_inst *inst, struct vdec_pic_info *pic) +{ + int i; + int err; + struct mtk_vcodec_mem *mem = NULL; + unsigned int buf_sz = get_mv_buf_size(pic->buf_w, pic->buf_h); + + for (i = 0; i < H264_MAX_FB_NUM; i++) { + mem = &inst->mv_buf[i]; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + mem->size = buf_sz; + err = mtk_vcodec_mem_alloc(inst->ctx, mem); + if (err) { + mtk_vcodec_err(inst, "failed to allocate mv buf"); + return err; + } + inst->vsi->mv_buf_dma[i] = mem->dma_addr; + } + + return 0; +} + +static void free_mv_buf(struct vdec_h264_inst *inst) +{ + int i; + struct mtk_vcodec_mem *mem = NULL; + + for (i = 0; i < H264_MAX_FB_NUM; i++) { + inst->vsi->mv_buf_dma[i] = 0; + mem = &inst->mv_buf[i]; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + } +} + +static int check_list_validity(struct vdec_h264_inst *inst, bool disp_list) +{ + struct h264_ring_fb_list *list; + + list = disp_list ? &inst->vsi->list_disp : &inst->vsi->list_free; + + if (list->count > H264_MAX_FB_NUM || + list->read_idx >= H264_MAX_FB_NUM || + list->write_idx >= H264_MAX_FB_NUM) { + mtk_vcodec_err(inst, "%s list err: cnt=%d r_idx=%d w_idx=%d", + disp_list ? "disp" : "free", list->count, + list->read_idx, list->write_idx); + return -EINVAL; + } + + return 0; +} + +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 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, "fb size: Y(%d), C(%d)", + pic->fb_sz[0], pic->fb_sz[1]); +} + +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 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 vdec_h264_init(struct mtk_vcodec_ctx *ctx) +{ + 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.ctx = ctx; + + 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); + + ctx->drv_handle = inst; + return 0; + +error_deinit: + vpu_dec_deinit(&inst->vpu); + +error_free_inst: + kfree(inst); + return err; +} + +static void vdec_h264_deinit(void *h_vdec) +{ + struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; + + mtk_vcodec_debug_enter(inst); + + vpu_dec_deinit(&inst->vpu); + free_predication_buf(inst); + free_mv_buf(inst); + + kfree(inst); +} + +static int find_start_code(unsigned char *data, unsigned int data_sz) +{ + if (data_sz > 3 && data[0] == 0 && data[1] == 0 && data[2] == 1) + return 3; + + if (data_sz > 4 && data[0] == 0 && data[1] == 0 && data[2] == 0 && + data[3] == 1) + return 4; + + return -1; +} + +static int vdec_h264_decode(void *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) { + mtk_vcodec_err(inst, "invalid nal start code"); + err = -EIO; + 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) { + if (err > 0 && (DEC_ERR_RET(err) == H264_ERR_NOT_VALID)) { + mtk_vcodec_err(inst, "- error bitstream - err = %d -", + err); + err = -EIO; + } + 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, 0); + 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 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 int vdec_h264_get_param(void *h_vdec, enum vdec_get_param_type type, + void *out) +{ + struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; + + switch (type) { + case GET_PARAM_DISP_FRAME_BUFFER: + vdec_h264_get_fb(inst, &inst->vsi->list_disp, true, out); + break; + + case GET_PARAM_FREE_FRAME_BUFFER: + vdec_h264_get_fb(inst, &inst->vsi->list_free, false, 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: + mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + return -EINVAL; + } + + return 0; +} + +const struct vdec_common_if vdec_h264_if = { + .init = vdec_h264_init, + .decode = vdec_h264_decode, + .get_param = vdec_h264_get_param, + .deinit = vdec_h264_deinit, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_req_if.c new file mode 100644 index 000000000000..43542de11e9c --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_h264_req_if.c @@ -0,0 +1,774 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include +#include + +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_dec.h" +#include "../mtk_vcodec_intr.h" +#include "../vdec_drv_base.h" +#include "../vdec_drv_if.h" +#include "../vdec_vpu_if.h" + +#define BUF_PREDICTION_SZ (64 * 4096) +#define MB_UNIT_LEN 16 + +/* get used parameters for sps/pps */ +#define GET_MTK_VDEC_FLAG(cond, flag) \ + { dst_param->cond = ((src_param->flags & (flag)) ? (1) : (0)); } +#define GET_MTK_VDEC_PARAM(param) \ + { dst_param->param = src_param->param; } +/* motion vector size (bytes) for every macro block */ +#define HW_MB_STORE_SZ 64 + +#define H264_MAX_FB_NUM 17 +#define H264_MAX_MV_NUM 32 +#define HDR_PARSING_BUF_SZ 1024 + +/** + * struct mtk_h264_dpb_info - h264 dpb information + * @y_dma_addr: Y bitstream physical address + * @c_dma_addr: CbCr bitstream physical address + * @reference_flag: reference picture flag (short/long term reference picture) + * @field: field picture flag + */ +struct mtk_h264_dpb_info { + dma_addr_t y_dma_addr; + dma_addr_t c_dma_addr; + int reference_flag; + int field; +}; + +/* + * struct mtk_h264_sps_param - parameters for sps + */ +struct mtk_h264_sps_param { + unsigned char chroma_format_idc; + unsigned char bit_depth_luma_minus8; + unsigned char bit_depth_chroma_minus8; + unsigned char log2_max_frame_num_minus4; + unsigned char pic_order_cnt_type; + unsigned char log2_max_pic_order_cnt_lsb_minus4; + unsigned char max_num_ref_frames; + unsigned char separate_colour_plane_flag; + unsigned short pic_width_in_mbs_minus1; + unsigned short pic_height_in_map_units_minus1; + unsigned int max_frame_nums; + unsigned char qpprime_y_zero_transform_bypass_flag; + unsigned char delta_pic_order_always_zero_flag; + unsigned char frame_mbs_only_flag; + unsigned char mb_adaptive_frame_field_flag; + unsigned char direct_8x8_inference_flag; + unsigned char reserved[3]; +}; + +/* + * struct mtk_h264_pps_param - parameters for pps + */ +struct mtk_h264_pps_param { + unsigned char num_ref_idx_l0_default_active_minus1; + unsigned char num_ref_idx_l1_default_active_minus1; + unsigned char weighted_bipred_idc; + char pic_init_qp_minus26; + char chroma_qp_index_offset; + char second_chroma_qp_index_offset; + unsigned char entropy_coding_mode_flag; + unsigned char pic_order_present_flag; + unsigned char deblocking_filter_control_present_flag; + unsigned char constrained_intra_pred_flag; + unsigned char weighted_pred_flag; + unsigned char redundant_pic_cnt_present_flag; + unsigned char transform_8x8_mode_flag; + unsigned char scaling_matrix_present_flag; + unsigned char reserved[2]; +}; + +struct slice_api_h264_scaling_matrix { + unsigned char scaling_list_4x4[6][16]; + unsigned char scaling_list_8x8[6][64]; +}; + +struct slice_h264_dpb_entry { + unsigned long long reference_ts; + unsigned short frame_num; + unsigned short pic_num; + /* Note that field is indicated by v4l2_buffer.field */ + int top_field_order_cnt; + int bottom_field_order_cnt; + unsigned int flags; /* V4L2_H264_DPB_ENTRY_FLAG_* */ +}; + +/* + * struct slice_api_h264_decode_param - parameters for decode. + */ +struct slice_api_h264_decode_param { + struct slice_h264_dpb_entry dpb[16]; + unsigned short num_slices; + unsigned short nal_ref_idc; + unsigned char ref_pic_list_p0[32]; + unsigned char ref_pic_list_b0[32]; + unsigned char ref_pic_list_b1[32]; + int top_field_order_cnt; + int bottom_field_order_cnt; + unsigned int flags; /* V4L2_H264_DECODE_PARAM_FLAG_* */ +}; + +/* + * struct mtk_h264_dec_slice_param - parameters for decode current frame + */ +struct mtk_h264_dec_slice_param { + struct mtk_h264_sps_param sps; + struct mtk_h264_pps_param pps; + struct slice_api_h264_scaling_matrix scaling_matrix; + struct slice_api_h264_decode_param decode_params; + struct mtk_h264_dpb_info h264_dpb_info[16]; +}; + +/** + * struct h264_fb - h264 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 h264_fb { + u64 vdec_fb_va; + u64 y_fb_dma; + u64 c_fb_dma; + s32 poc; + u32 reserved; +}; + +/** + * struct vdec_h264_dec_info - decode information + * @dpb_sz : decoding picture buffer size + * @resolution_changed : resoltion change happen + * @realloc_mv_buf : flag to notify driver to re-allocate mv buffer + * @cap_num_planes : number planes of capture buffer + * @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_h264_dec_info { + u32 dpb_sz; + u32 resolution_changed; + u32 realloc_mv_buf; + u32 cap_num_planes; + u64 bs_dma; + u64 y_fb_dma; + u64 c_fb_dma; + u64 vdec_fb_va; +}; + +/** + * struct vdec_h264_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 + * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) + * @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R) + * @dec : decode information (AP-R, VPU-W) + * @pic : picture information (AP-R, VPU-W) + * @crop : crop information (AP-R, VPU-W) + * @h264_slice_params : the parameters that hardware use to decode + */ +struct vdec_h264_vsi { + u64 pred_buf_dma; + u64 mv_buf_dma[H264_MAX_MV_NUM]; + struct vdec_h264_dec_info dec; + struct vdec_pic_info pic; + struct v4l2_rect crop; + struct mtk_h264_dec_slice_param h264_slice_params; +}; + +/** + * struct vdec_h264_slice_inst - h264 decoder instance + * @num_nalu : how many nalus be decoded + * @ctx : point to mtk_vcodec_ctx + * @pred_buf : HW working predication buffer + * @mv_buf : HW working motion vector buffer + * @vpu : VPU instance + * @vsi_ctx : Local VSI data for this decoding context + * @h264_slice_param : the parameters that hardware use to decode + * @dpb : decoded picture buffer used to store reference buffer information + */ +struct vdec_h264_slice_inst { + unsigned int num_nalu; + struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_mem pred_buf; + struct mtk_vcodec_mem mv_buf[H264_MAX_MV_NUM]; + struct vdec_vpu_inst vpu; + struct vdec_h264_vsi vsi_ctx; + struct mtk_h264_dec_slice_param h264_slice_param; + + struct v4l2_h264_dpb_entry dpb[16]; +}; + +static void *get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) +{ + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); + + return ctrl->p_cur.p; +} + +static void get_h264_dpb_list(struct vdec_h264_slice_inst *inst, + struct mtk_h264_dec_slice_param *slice_param) +{ + struct vb2_queue *vq; + struct vb2_buffer *vb; + struct vb2_v4l2_buffer *vb2_v4l2; + u64 index; + + vq = v4l2_m2m_get_vq(inst->ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + + for (index = 0; index < ARRAY_SIZE(slice_param->decode_params.dpb); index++) { + const struct slice_h264_dpb_entry *dpb; + int vb2_index; + + dpb = &slice_param->decode_params.dpb[index]; + if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) { + slice_param->h264_dpb_info[index].reference_flag = 0; + continue; + } + + vb2_index = vb2_find_timestamp(vq, dpb->reference_ts, 0); + if (vb2_index < 0) { + mtk_vcodec_err(inst, "Reference invalid: dpb_index(%lld) reference_ts(%lld)", + index, dpb->reference_ts); + continue; + } + /* 1 for short term reference, 2 for long term reference */ + if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)) + slice_param->h264_dpb_info[index].reference_flag = 1; + else + slice_param->h264_dpb_info[index].reference_flag = 2; + + vb = vq->bufs[vb2_index]; + vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); + slice_param->h264_dpb_info[index].field = vb2_v4l2->field; + + slice_param->h264_dpb_info[index].y_dma_addr = + vb2_dma_contig_plane_dma_addr(vb, 0); + if (inst->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) { + slice_param->h264_dpb_info[index].c_dma_addr = + vb2_dma_contig_plane_dma_addr(vb, 1); + } + } +} + +static void get_h264_sps_parameters(struct mtk_h264_sps_param *dst_param, + const struct v4l2_ctrl_h264_sps *src_param) +{ + GET_MTK_VDEC_PARAM(chroma_format_idc); + GET_MTK_VDEC_PARAM(bit_depth_luma_minus8); + GET_MTK_VDEC_PARAM(bit_depth_chroma_minus8); + GET_MTK_VDEC_PARAM(log2_max_frame_num_minus4); + GET_MTK_VDEC_PARAM(pic_order_cnt_type); + GET_MTK_VDEC_PARAM(log2_max_pic_order_cnt_lsb_minus4); + GET_MTK_VDEC_PARAM(max_num_ref_frames); + GET_MTK_VDEC_PARAM(pic_width_in_mbs_minus1); + GET_MTK_VDEC_PARAM(pic_height_in_map_units_minus1); + + GET_MTK_VDEC_FLAG(separate_colour_plane_flag, + V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE); + GET_MTK_VDEC_FLAG(qpprime_y_zero_transform_bypass_flag, + V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS); + GET_MTK_VDEC_FLAG(delta_pic_order_always_zero_flag, + V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); + GET_MTK_VDEC_FLAG(frame_mbs_only_flag, + V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); + GET_MTK_VDEC_FLAG(mb_adaptive_frame_field_flag, + V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); + GET_MTK_VDEC_FLAG(direct_8x8_inference_flag, + V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE); +} + +static void get_h264_pps_parameters(struct mtk_h264_pps_param *dst_param, + const struct v4l2_ctrl_h264_pps *src_param) +{ + GET_MTK_VDEC_PARAM(num_ref_idx_l0_default_active_minus1); + GET_MTK_VDEC_PARAM(num_ref_idx_l1_default_active_minus1); + GET_MTK_VDEC_PARAM(weighted_bipred_idc); + GET_MTK_VDEC_PARAM(pic_init_qp_minus26); + GET_MTK_VDEC_PARAM(chroma_qp_index_offset); + GET_MTK_VDEC_PARAM(second_chroma_qp_index_offset); + + GET_MTK_VDEC_FLAG(entropy_coding_mode_flag, + V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE); + GET_MTK_VDEC_FLAG(pic_order_present_flag, + V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT); + GET_MTK_VDEC_FLAG(weighted_pred_flag, + V4L2_H264_PPS_FLAG_WEIGHTED_PRED); + GET_MTK_VDEC_FLAG(deblocking_filter_control_present_flag, + V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); + GET_MTK_VDEC_FLAG(constrained_intra_pred_flag, + V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED); + GET_MTK_VDEC_FLAG(redundant_pic_cnt_present_flag, + V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT); + GET_MTK_VDEC_FLAG(transform_8x8_mode_flag, + V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); + GET_MTK_VDEC_FLAG(scaling_matrix_present_flag, + V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT); +} + +static void +get_h264_scaling_matrix(struct slice_api_h264_scaling_matrix *dst_matrix, + const struct v4l2_ctrl_h264_scaling_matrix *src_matrix) +{ + memcpy(dst_matrix->scaling_list_4x4, src_matrix->scaling_list_4x4, + sizeof(dst_matrix->scaling_list_4x4)); + + memcpy(dst_matrix->scaling_list_8x8, src_matrix->scaling_list_8x8, + sizeof(dst_matrix->scaling_list_8x8)); +} + +static void +get_h264_decode_parameters(struct slice_api_h264_decode_param *dst_params, + const struct v4l2_ctrl_h264_decode_params *src_params, + const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dst_params->dpb); i++) { + struct slice_h264_dpb_entry *dst_entry = &dst_params->dpb[i]; + const struct v4l2_h264_dpb_entry *src_entry = &dpb[i]; + + dst_entry->reference_ts = src_entry->reference_ts; + dst_entry->frame_num = src_entry->frame_num; + dst_entry->pic_num = src_entry->pic_num; + dst_entry->top_field_order_cnt = src_entry->top_field_order_cnt; + dst_entry->bottom_field_order_cnt = + src_entry->bottom_field_order_cnt; + dst_entry->flags = src_entry->flags; + } + + /* + * num_slices is a leftover from the old H.264 support and is ignored + * by the firmware. + */ + dst_params->num_slices = 0; + dst_params->nal_ref_idc = src_params->nal_ref_idc; + dst_params->top_field_order_cnt = src_params->top_field_order_cnt; + dst_params->bottom_field_order_cnt = src_params->bottom_field_order_cnt; + dst_params->flags = src_params->flags; +} + +static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, + const struct v4l2_h264_dpb_entry *b) +{ + return a->top_field_order_cnt == b->top_field_order_cnt && + a->bottom_field_order_cnt == b->bottom_field_order_cnt; +} + +/* + * Move DPB entries of dec_param that refer to a frame already existing in dpb + * into the already existing slot in dpb, and move other entries into new slots. + * + * This function is an adaptation of the similarly-named function in + * hantro_h264.c. + */ +static void update_dpb(const struct v4l2_ctrl_h264_decode_params *dec_param, + struct v4l2_h264_dpb_entry *dpb) +{ + DECLARE_BITMAP(new, ARRAY_SIZE(dec_param->dpb)) = { 0, }; + DECLARE_BITMAP(in_use, ARRAY_SIZE(dec_param->dpb)) = { 0, }; + DECLARE_BITMAP(used, ARRAY_SIZE(dec_param->dpb)) = { 0, }; + unsigned int i, j; + + /* Disable all entries by default, and mark the ones in use. */ + for (i = 0; i < ARRAY_SIZE(dec_param->dpb); i++) { + if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) + set_bit(i, in_use); + dpb[i].flags &= ~V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; + } + + /* Try to match new DPB entries with existing ones by their POCs. */ + for (i = 0; i < ARRAY_SIZE(dec_param->dpb); i++) { + const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; + + if (!(ndpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) + continue; + + /* + * To cut off some comparisons, iterate only on target DPB + * entries were already used. + */ + for_each_set_bit(j, in_use, ARRAY_SIZE(dec_param->dpb)) { + struct v4l2_h264_dpb_entry *cdpb; + + cdpb = &dpb[j]; + if (!dpb_entry_match(cdpb, ndpb)) + continue; + + *cdpb = *ndpb; + set_bit(j, used); + /* Don't reiterate on this one. */ + clear_bit(j, in_use); + break; + } + + if (j == ARRAY_SIZE(dec_param->dpb)) + set_bit(i, new); + } + + /* For entries that could not be matched, use remaining free slots. */ + for_each_set_bit(i, new, ARRAY_SIZE(dec_param->dpb)) { + const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; + struct v4l2_h264_dpb_entry *cdpb; + + /* + * Both arrays are of the same sizes, so there is no way + * we can end up with no space in target array, unless + * something is buggy. + */ + j = find_first_zero_bit(used, ARRAY_SIZE(dec_param->dpb)); + if (WARN_ON(j >= ARRAY_SIZE(dec_param->dpb))) + return; + + cdpb = &dpb[j]; + *cdpb = *ndpb; + set_bit(j, used); + } +} + +/* + * The firmware expects unused reflist entries to have the value 0x20. + */ +static void fixup_ref_list(u8 *ref_list, size_t num_valid) +{ + memset(&ref_list[num_valid], 0x20, 32 - num_valid); +} + +static void get_vdec_decode_parameters(struct vdec_h264_slice_inst *inst) +{ + const struct v4l2_ctrl_h264_decode_params *dec_params = + get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); + const struct v4l2_ctrl_h264_sps *sps = + get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_SPS); + const struct v4l2_ctrl_h264_pps *pps = + get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_PPS); + const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix = + get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_SCALING_MATRIX); + struct mtk_h264_dec_slice_param *slice_param = &inst->h264_slice_param; + struct v4l2_h264_reflist_builder reflist_builder; + u8 *p0_reflist = slice_param->decode_params.ref_pic_list_p0; + u8 *b0_reflist = slice_param->decode_params.ref_pic_list_b0; + u8 *b1_reflist = slice_param->decode_params.ref_pic_list_b1; + + update_dpb(dec_params, inst->dpb); + + get_h264_sps_parameters(&slice_param->sps, sps); + get_h264_pps_parameters(&slice_param->pps, pps); + get_h264_scaling_matrix(&slice_param->scaling_matrix, scaling_matrix); + get_h264_decode_parameters(&slice_param->decode_params, dec_params, + inst->dpb); + get_h264_dpb_list(inst, slice_param); + + /* Build the reference lists */ + v4l2_h264_init_reflist_builder(&reflist_builder, dec_params, sps, + inst->dpb); + v4l2_h264_build_p_ref_list(&reflist_builder, p0_reflist); + v4l2_h264_build_b_ref_lists(&reflist_builder, b0_reflist, b1_reflist); + /* Adapt the built lists to the firmware's expectations */ + fixup_ref_list(p0_reflist, reflist_builder.num_valid); + fixup_ref_list(b0_reflist, reflist_builder.num_valid); + fixup_ref_list(b1_reflist, reflist_builder.num_valid); + + memcpy(&inst->vsi_ctx.h264_slice_params, slice_param, + sizeof(inst->vsi_ctx.h264_slice_params)); +} + +static unsigned int get_mv_buf_size(unsigned int width, unsigned int height) +{ + int unit_size = (width / MB_UNIT_LEN) * (height / MB_UNIT_LEN) + 8; + + return HW_MB_STORE_SZ * unit_size; +} + +static int allocate_predication_buf(struct vdec_h264_slice_inst *inst) +{ + int err; + + inst->pred_buf.size = BUF_PREDICTION_SZ; + err = mtk_vcodec_mem_alloc(inst->ctx, &inst->pred_buf); + if (err) { + mtk_vcodec_err(inst, "failed to allocate ppl buf"); + return err; + } + + inst->vsi_ctx.pred_buf_dma = inst->pred_buf.dma_addr; + return 0; +} + +static void free_predication_buf(struct vdec_h264_slice_inst *inst) +{ + struct mtk_vcodec_mem *mem = &inst->pred_buf; + + mtk_vcodec_debug_enter(inst); + + inst->vsi_ctx.pred_buf_dma = 0; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); +} + +static int alloc_mv_buf(struct vdec_h264_slice_inst *inst, + struct vdec_pic_info *pic) +{ + int i; + int err; + struct mtk_vcodec_mem *mem = NULL; + unsigned int buf_sz = get_mv_buf_size(pic->buf_w, pic->buf_h); + + mtk_v4l2_debug(3, "size = 0x%x", buf_sz); + for (i = 0; i < H264_MAX_MV_NUM; i++) { + mem = &inst->mv_buf[i]; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + mem->size = buf_sz; + err = mtk_vcodec_mem_alloc(inst->ctx, mem); + if (err) { + mtk_vcodec_err(inst, "failed to allocate mv buf"); + return err; + } + inst->vsi_ctx.mv_buf_dma[i] = mem->dma_addr; + } + + return 0; +} + +static void free_mv_buf(struct vdec_h264_slice_inst *inst) +{ + int i; + struct mtk_vcodec_mem *mem; + + for (i = 0; i < H264_MAX_MV_NUM; i++) { + inst->vsi_ctx.mv_buf_dma[i] = 0; + mem = &inst->mv_buf[i]; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + } +} + +static void get_pic_info(struct vdec_h264_slice_inst *inst, + struct vdec_pic_info *pic) +{ + struct mtk_vcodec_ctx *ctx = inst->ctx; + + ctx->picinfo.buf_w = ALIGN(ctx->picinfo.pic_w, VCODEC_DEC_ALIGNED_64); + ctx->picinfo.buf_h = ALIGN(ctx->picinfo.pic_h, VCODEC_DEC_ALIGNED_64); + ctx->picinfo.fb_sz[0] = ctx->picinfo.buf_w * ctx->picinfo.buf_h; + ctx->picinfo.fb_sz[1] = ctx->picinfo.fb_sz[0] >> 1; + inst->vsi_ctx.dec.cap_num_planes = + ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes; + + *pic = ctx->picinfo; + mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->picinfo.buf_w, ctx->picinfo.buf_h); + mtk_vcodec_debug(inst, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], + ctx->picinfo.fb_sz[1]); + + if (ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w || + ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h) { + inst->vsi_ctx.dec.resolution_changed = true; + if (ctx->last_decoded_picinfo.buf_w != ctx->picinfo.buf_w || + ctx->last_decoded_picinfo.buf_h != ctx->picinfo.buf_h) + inst->vsi_ctx.dec.realloc_mv_buf = true; + + mtk_v4l2_debug(1, "ResChg: (%d %d) : old(%d, %d) -> new(%d, %d)", + inst->vsi_ctx.dec.resolution_changed, + inst->vsi_ctx.dec.realloc_mv_buf, + ctx->last_decoded_picinfo.pic_w, + ctx->last_decoded_picinfo.pic_h, + ctx->picinfo.pic_w, ctx->picinfo.pic_h); + } +} + +static void get_crop_info(struct vdec_h264_slice_inst *inst, struct v4l2_rect *cr) +{ + cr->left = inst->vsi_ctx.crop.left; + cr->top = inst->vsi_ctx.crop.top; + cr->width = inst->vsi_ctx.crop.width; + cr->height = inst->vsi_ctx.crop.height; + + mtk_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_h264_slice_inst *inst, unsigned int *dpb_sz) +{ + *dpb_sz = inst->vsi_ctx.dec.dpb_sz; + mtk_vcodec_debug(inst, "sz=%d", *dpb_sz); +} + +static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) +{ + struct vdec_h264_slice_inst *inst; + int err; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + + inst->vpu.id = SCP_IPI_VDEC_H264; + inst->vpu.ctx = ctx; + + err = vpu_dec_init(&inst->vpu); + if (err) { + mtk_vcodec_err(inst, "vdec_h264 init err=%d", err); + goto error_free_inst; + } + + memcpy(&inst->vsi_ctx, inst->vpu.vsi, sizeof(inst->vsi_ctx)); + inst->vsi_ctx.dec.resolution_changed = true; + inst->vsi_ctx.dec.realloc_mv_buf = true; + + err = allocate_predication_buf(inst); + if (err) + goto error_deinit; + + mtk_vcodec_debug(inst, "struct size = %zu,%zu,%zu,%zu\n", + sizeof(struct mtk_h264_sps_param), + sizeof(struct mtk_h264_pps_param), + sizeof(struct mtk_h264_dec_slice_param), + sizeof(struct mtk_h264_dpb_info)); + + mtk_vcodec_debug(inst, "H264 Instance >> %p", inst); + + ctx->drv_handle = inst; + return 0; + +error_deinit: + vpu_dec_deinit(&inst->vpu); + +error_free_inst: + kfree(inst); + return err; +} + +static void vdec_h264_slice_deinit(void *h_vdec) +{ + struct vdec_h264_slice_inst *inst = h_vdec; + + mtk_vcodec_debug_enter(inst); + + vpu_dec_deinit(&inst->vpu); + free_predication_buf(inst); + free_mv_buf(inst); + + kfree(inst); +} + +static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_h264_slice_inst *inst = h_vdec; + const struct v4l2_ctrl_h264_decode_params *dec_params = + get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); + struct vdec_vpu_inst *vpu = &inst->vpu; + u32 data[2]; + u64 y_fb_dma; + u64 c_fb_dma; + int err; + + /* bs NULL means flush decoder */ + if (!bs) + return vpu_dec_reset(vpu); + + y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; + 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); + + inst->vsi_ctx.dec.bs_dma = (uint64_t)bs->dma_addr; + inst->vsi_ctx.dec.y_fb_dma = y_fb_dma; + inst->vsi_ctx.dec.c_fb_dma = c_fb_dma; + inst->vsi_ctx.dec.vdec_fb_va = (u64)(uintptr_t)fb; + + get_vdec_decode_parameters(inst); + data[0] = bs->size; + /* + * Reconstruct the first byte of the NAL unit, as the firmware requests + * that information to be passed even though it is present in the stream + * itself... + */ + data[1] = (dec_params->nal_ref_idc << 5) | + ((dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) + ? 0x5 : 0x1); + + *res_chg = inst->vsi_ctx.dec.resolution_changed; + if (*res_chg) { + mtk_vcodec_debug(inst, "- resolution changed -"); + if (inst->vsi_ctx.dec.realloc_mv_buf) { + err = alloc_mv_buf(inst, &inst->ctx->picinfo); + inst->vsi_ctx.dec.realloc_mv_buf = false; + if (err) + goto err_free_fb_out; + } + *res_chg = false; + } + + memcpy(inst->vpu.vsi, &inst->vsi_ctx, sizeof(inst->vsi_ctx)); + err = vpu_dec_start(vpu, data, 2); + if (err) + goto err_free_fb_out; + + /* wait decoder done interrupt */ + err = mtk_vcodec_wait_for_done_ctx(inst->ctx, + MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, 0); + if (err) + goto err_free_fb_out; + vpu_dec_end(vpu); + + memcpy(&inst->vsi_ctx, inst->vpu.vsi, sizeof(inst->vsi_ctx)); + mtk_vcodec_debug(inst, "\n - NALU[%d]", inst->num_nalu); + return 0; + +err_free_fb_out: + mtk_vcodec_err(inst, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); + return err; +} + +static int vdec_h264_slice_get_param(void *h_vdec, enum vdec_get_param_type type, void *out) +{ + struct vdec_h264_slice_inst *inst = h_vdec; + + switch (type) { + 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: + mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + return -EINVAL; + } + + return 0; +} + +const struct vdec_common_if vdec_h264_slice_if = { + .init = vdec_h264_slice_init, + .decode = vdec_h264_slice_decode, + .get_param = vdec_h264_slice_get_param, + .deinit = vdec_h264_slice_deinit, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp8_if.c new file mode 100644 index 000000000000..88c046731754 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp8_if.c @@ -0,0 +1,616 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Jungchang Tsao + * PC Chen + */ + +#include +#include "../vdec_drv_if.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_dec.h" +#include "../mtk_vcodec_intr.h" +#include "../vdec_vpu_if.h" +#include "../vdec_drv_base.h" + +/* Decoding picture buffer size (3 reference frames plus current frame) */ +#define VP8_DPB_SIZE 4 + +/* HW working buffer size (bytes) */ +#define VP8_WORKING_BUF_SZ (45 * 4096) + +/* HW control register address */ +#define VP8_SEGID_DRAM_ADDR 0x3c +#define VP8_HW_VLD_ADDR 0x93C +#define VP8_HW_VLD_VALUE 0x940 +#define VP8_BSASET 0x100 +#define VP8_BSDSET 0x104 +#define VP8_RW_CKEN_SET 0x0 +#define VP8_RW_DCM_CON 0x18 +#define VP8_WO_VLD_SRST 0x108 +#define VP8_RW_MISC_SYS_SEL 0x84 +#define VP8_RW_MISC_SPEC_CON 0xC8 +#define VP8_WO_VLD_SRST 0x108 +#define VP8_RW_VP8_CTRL 0xA4 +#define VP8_RW_MISC_DCM_CON 0xEC +#define VP8_RW_MISC_SRST 0xF4 +#define VP8_RW_MISC_FUNC_CON 0xCC + +#define VP8_MAX_FRM_BUF_NUM 5 +#define VP8_MAX_FRM_BUF_NODE_NUM (VP8_MAX_FRM_BUF_NUM * 2) + +/* required buffer size (bytes) to store decode information */ +#define VP8_HW_SEGMENT_DATA_SZ 272 +#define VP8_HW_SEGMENT_UINT 4 + +#define VP8_DEC_TABLE_PROC_LOOP 96 +#define VP8_DEC_TABLE_UNIT 3 +#define VP8_DEC_TABLE_SZ 300 +#define VP8_DEC_TABLE_OFFSET 2 +#define VP8_DEC_TABLE_RW_UNIT 4 + +/** + * struct vdec_vp8_dec_info - decode misc information + * @working_buf_dma : working buffer dma address + * @prev_y_dma : previous decoded frame buffer Y plane address + * @cur_y_fb_dma : current plane Y frame buffer dma address + * @cur_c_fb_dma : current plane C frame buffer dma address + * @bs_dma : bitstream dma address + * @bs_sz : bitstream size + * @resolution_changed: resolution change flag 1 - changed, 0 - not change + * @show_frame : display this frame or not + * @wait_key_frame : wait key frame coming + */ +struct vdec_vp8_dec_info { + uint64_t working_buf_dma; + uint64_t prev_y_dma; + uint64_t cur_y_fb_dma; + uint64_t cur_c_fb_dma; + uint64_t bs_dma; + uint32_t bs_sz; + uint32_t resolution_changed; + uint32_t show_frame; + uint32_t wait_key_frame; +}; + +/** + * struct vdec_vp8_vsi - VPU shared information + * @dec : decoding information + * @pic : picture information + * @dec_table : decoder coefficient table + * @segment_buf : segmentation buffer + * @load_data : flag to indicate reload decode data + */ +struct vdec_vp8_vsi { + struct vdec_vp8_dec_info dec; + struct vdec_pic_info pic; + uint32_t dec_table[VP8_DEC_TABLE_SZ]; + uint32_t segment_buf[VP8_HW_SEGMENT_DATA_SZ][VP8_HW_SEGMENT_UINT]; + uint32_t load_data; +}; + +/** + * struct vdec_vp8_hw_reg_base - HW register base + * @sys : base address for sys + * @misc : base address for misc + * @ld : base address for ld + * @top : base address for top + * @cm : base address for cm + * @hwd : base address for hwd + * @hwb : base address for hwb + */ +struct vdec_vp8_hw_reg_base { + void __iomem *sys; + void __iomem *misc; + void __iomem *ld; + void __iomem *top; + void __iomem *cm; + void __iomem *hwd; + void __iomem *hwb; +}; + +/** + * struct vdec_vp8_vpu_inst - VPU instance for VP8 decode + * @wq_hd : Wait queue to wait VPU message ack + * @signaled : 1 - Host has received ack message from VPU, 0 - not receive + * @failure : VPU execution result status 0 - success, others - fail + * @inst_addr : VPU decoder instance address + */ +struct vdec_vp8_vpu_inst { + wait_queue_head_t wq_hd; + int signaled; + int failure; + uint32_t inst_addr; +}; + +/* frame buffer (fb) list + * [available_fb_node_list] - decode fb are initialized to 0 and populated in + * [fb_use_list] - fb is set after decode and is moved to this list + * [fb_free_list] - fb is not needed for reference will be moved from + * [fb_use_list] to [fb_free_list] and + * once user remove fb from [fb_free_list], + * it is circulated back to [available_fb_node_list] + * [fb_disp_list] - fb is set after decode and is moved to this list + * once user remove fb from [fb_disp_list] it is + * circulated back to [available_fb_node_list] + */ + +/** + * struct vdec_vp8_inst - VP8 decoder instance + * @cur_fb : current frame buffer + * @dec_fb : decode frame buffer node + * @available_fb_node_list : list to store available frame buffer node + * @fb_use_list : list to store frame buffer in use + * @fb_free_list : list to store free frame buffer + * @fb_disp_list : list to store display ready frame buffer + * @working_buf : HW decoder working buffer + * @reg_base : HW register base address + * @frm_cnt : decode frame count + * @ctx : V4L2 context + * @vpu : VPU instance for decoder + * @vsi : VPU share information + */ +struct vdec_vp8_inst { + struct vdec_fb *cur_fb; + struct vdec_fb_node dec_fb[VP8_MAX_FRM_BUF_NODE_NUM]; + struct list_head available_fb_node_list; + struct list_head fb_use_list; + struct list_head fb_free_list; + struct list_head fb_disp_list; + struct mtk_vcodec_mem working_buf; + struct vdec_vp8_hw_reg_base reg_base; + unsigned int frm_cnt; + struct mtk_vcodec_ctx *ctx; + struct vdec_vpu_inst vpu; + struct vdec_vp8_vsi *vsi; +}; + +static void get_hw_reg_base(struct vdec_vp8_inst *inst) +{ + inst->reg_base.top = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_TOP); + inst->reg_base.cm = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_CM); + inst->reg_base.hwd = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_HWD); + inst->reg_base.sys = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_SYS); + inst->reg_base.misc = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_MISC); + inst->reg_base.ld = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_LD); + inst->reg_base.hwb = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_HWB); +} + +static void write_hw_segmentation_data(struct vdec_vp8_inst *inst) +{ + int i, j; + u32 seg_id_addr; + u32 val; + void __iomem *cm = inst->reg_base.cm; + struct vdec_vp8_vsi *vsi = inst->vsi; + + seg_id_addr = readl(inst->reg_base.top + VP8_SEGID_DRAM_ADDR) >> 4; + + for (i = 0; i < ARRAY_SIZE(vsi->segment_buf); i++) { + for (j = ARRAY_SIZE(vsi->segment_buf[i]) - 1; j >= 0; j--) { + val = (1 << 16) + ((seg_id_addr + i) << 2) + j; + writel(val, cm + VP8_HW_VLD_ADDR); + + val = vsi->segment_buf[i][j]; + writel(val, cm + VP8_HW_VLD_VALUE); + } + } +} + +static void read_hw_segmentation_data(struct vdec_vp8_inst *inst) +{ + int i, j; + u32 seg_id_addr; + u32 val; + void __iomem *cm = inst->reg_base.cm; + struct vdec_vp8_vsi *vsi = inst->vsi; + + seg_id_addr = readl(inst->reg_base.top + VP8_SEGID_DRAM_ADDR) >> 4; + + for (i = 0; i < ARRAY_SIZE(vsi->segment_buf); i++) { + for (j = ARRAY_SIZE(vsi->segment_buf[i]) - 1; j >= 0; j--) { + val = ((seg_id_addr + i) << 2) + j; + writel(val, cm + VP8_HW_VLD_ADDR); + + val = readl(cm + VP8_HW_VLD_VALUE); + vsi->segment_buf[i][j] = val; + } + } +} + +/* reset HW and enable HW read/write data function */ +static void enable_hw_rw_function(struct vdec_vp8_inst *inst) +{ + u32 val = 0; + void __iomem *sys = inst->reg_base.sys; + void __iomem *misc = inst->reg_base.misc; + void __iomem *ld = inst->reg_base.ld; + void __iomem *hwb = inst->reg_base.hwb; + void __iomem *hwd = inst->reg_base.hwd; + + writel(0x1, sys + VP8_RW_CKEN_SET); + writel(0x101, ld + VP8_WO_VLD_SRST); + writel(0x101, hwb + VP8_WO_VLD_SRST); + + writel(1, sys); + val = readl(misc + VP8_RW_MISC_SRST); + writel((val & 0xFFFFFFFE), misc + VP8_RW_MISC_SRST); + + writel(0x1, misc + VP8_RW_MISC_SYS_SEL); + writel(0x17F, misc + VP8_RW_MISC_SPEC_CON); + writel(0x71201100, misc + VP8_RW_MISC_FUNC_CON); + writel(0x0, ld + VP8_WO_VLD_SRST); + writel(0x0, hwb + VP8_WO_VLD_SRST); + writel(0x1, sys + VP8_RW_DCM_CON); + writel(0x1, misc + VP8_RW_MISC_DCM_CON); + writel(0x1, hwd + VP8_RW_VP8_CTRL); +} + +static void store_dec_table(struct vdec_vp8_inst *inst) +{ + int i, j; + u32 addr = 0, val = 0; + void __iomem *hwd = inst->reg_base.hwd; + u32 *p = &inst->vsi->dec_table[VP8_DEC_TABLE_OFFSET]; + + for (i = 0; i < VP8_DEC_TABLE_PROC_LOOP; i++) { + writel(addr, hwd + VP8_BSASET); + for (j = 0; j < VP8_DEC_TABLE_UNIT ; j++) { + val = *p++; + writel(val, hwd + VP8_BSDSET); + } + addr += VP8_DEC_TABLE_RW_UNIT; + } +} + +static void load_dec_table(struct vdec_vp8_inst *inst) +{ + int i; + u32 addr = 0; + u32 *p = &inst->vsi->dec_table[VP8_DEC_TABLE_OFFSET]; + void __iomem *hwd = inst->reg_base.hwd; + + for (i = 0; i < VP8_DEC_TABLE_PROC_LOOP; i++) { + writel(addr, hwd + VP8_BSASET); + /* read total 11 bytes */ + *p++ = readl(hwd + VP8_BSDSET); + *p++ = readl(hwd + VP8_BSDSET); + *p++ = readl(hwd + VP8_BSDSET) & 0xFFFFFF; + addr += VP8_DEC_TABLE_RW_UNIT; + } +} + +static void get_pic_info(struct vdec_vp8_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, "fb size: Y(%d), C(%d)", + pic->fb_sz[0], pic->fb_sz[1]); +} + +static void vp8_dec_finish(struct vdec_vp8_inst *inst) +{ + struct vdec_fb_node *node; + uint64_t prev_y_dma = inst->vsi->dec.prev_y_dma; + + mtk_vcodec_debug(inst, "prev fb base dma=%llx", prev_y_dma); + + /* put last decode ok frame to fb_free_list */ + if (prev_y_dma != 0) { + list_for_each_entry(node, &inst->fb_use_list, list) { + struct vdec_fb *fb = (struct vdec_fb *)node->fb; + + if (prev_y_dma == (uint64_t)fb->base_y.dma_addr) { + list_move_tail(&node->list, + &inst->fb_free_list); + break; + } + } + } + + /* available_fb_node_list -> fb_use_list */ + node = list_first_entry(&inst->available_fb_node_list, + struct vdec_fb_node, list); + node->fb = inst->cur_fb; + list_move_tail(&node->list, &inst->fb_use_list); + + /* available_fb_node_list -> fb_disp_list */ + if (inst->vsi->dec.show_frame) { + node = list_first_entry(&inst->available_fb_node_list, + struct vdec_fb_node, list); + node->fb = inst->cur_fb; + list_move_tail(&node->list, &inst->fb_disp_list); + } +} + +static void move_fb_list_use_to_free(struct vdec_vp8_inst *inst) +{ + struct vdec_fb_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &inst->fb_use_list, list) + list_move_tail(&node->list, &inst->fb_free_list); +} + +static void init_list(struct vdec_vp8_inst *inst) +{ + int i; + + INIT_LIST_HEAD(&inst->available_fb_node_list); + INIT_LIST_HEAD(&inst->fb_use_list); + INIT_LIST_HEAD(&inst->fb_free_list); + INIT_LIST_HEAD(&inst->fb_disp_list); + + for (i = 0; i < ARRAY_SIZE(inst->dec_fb); i++) { + INIT_LIST_HEAD(&inst->dec_fb[i].list); + inst->dec_fb[i].fb = NULL; + list_add_tail(&inst->dec_fb[i].list, + &inst->available_fb_node_list); + } +} + +static void add_fb_to_free_list(struct vdec_vp8_inst *inst, void *fb) +{ + struct vdec_fb_node *node; + + if (fb) { + node = list_first_entry(&inst->available_fb_node_list, + struct vdec_fb_node, list); + node->fb = fb; + list_move_tail(&node->list, &inst->fb_free_list); + } +} + +static int alloc_working_buf(struct vdec_vp8_inst *inst) +{ + int err; + struct mtk_vcodec_mem *mem = &inst->working_buf; + + mem->size = VP8_WORKING_BUF_SZ; + err = mtk_vcodec_mem_alloc(inst->ctx, mem); + if (err) { + mtk_vcodec_err(inst, "Cannot allocate working buffer"); + return err; + } + + inst->vsi->dec.working_buf_dma = (uint64_t)mem->dma_addr; + return 0; +} + +static void free_working_buf(struct vdec_vp8_inst *inst) +{ + struct mtk_vcodec_mem *mem = &inst->working_buf; + + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + + inst->vsi->dec.working_buf_dma = 0; +} + +static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) +{ + struct vdec_vp8_inst *inst; + int err; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + + inst->vpu.id = IPI_VDEC_VP8; + inst->vpu.ctx = ctx; + + err = vpu_dec_init(&inst->vpu); + if (err) { + mtk_vcodec_err(inst, "vdec_vp8 init err=%d", err); + goto error_free_inst; + } + + inst->vsi = (struct vdec_vp8_vsi *)inst->vpu.vsi; + init_list(inst); + err = alloc_working_buf(inst); + if (err) + goto error_deinit; + + get_hw_reg_base(inst); + mtk_vcodec_debug(inst, "VP8 Instance >> %p", inst); + + ctx->drv_handle = inst; + return 0; + +error_deinit: + vpu_dec_deinit(&inst->vpu); +error_free_inst: + kfree(inst); + return err; +} + +static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; + struct vdec_vp8_dec_info *dec = &inst->vsi->dec; + struct vdec_vpu_inst *vpu = &inst->vpu; + unsigned char *bs_va; + unsigned int data; + int err = 0; + uint64_t y_fb_dma; + uint64_t c_fb_dma; + + /* bs NULL means flush decoder */ + if (bs == NULL) { + move_fb_list_use_to_free(inst); + return vpu_dec_reset(vpu); + } + + y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; + c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; + + mtk_vcodec_debug(inst, "+ [%d] FB y_dma=%llx c_dma=%llx fb=%p", + inst->frm_cnt, y_fb_dma, c_fb_dma, fb); + + inst->cur_fb = fb; + dec->bs_dma = (unsigned long)bs->dma_addr; + dec->bs_sz = bs->size; + dec->cur_y_fb_dma = y_fb_dma; + dec->cur_c_fb_dma = c_fb_dma; + + mtk_vcodec_debug(inst, "\n + FRAME[%d] +\n", inst->frm_cnt); + + write_hw_segmentation_data(inst); + enable_hw_rw_function(inst); + store_dec_table(inst); + + bs_va = (unsigned char *)bs->va; + + /* retrieve width/hight and scale info from header */ + data = (*(bs_va + 9) << 24) | (*(bs_va + 8) << 16) | + (*(bs_va + 7) << 8) | *(bs_va + 6); + err = vpu_dec_start(vpu, &data, 1); + if (err) { + add_fb_to_free_list(inst, fb); + if (dec->wait_key_frame) { + mtk_vcodec_debug(inst, "wait key frame !"); + return 0; + } + + goto error; + } + + if (dec->resolution_changed) { + mtk_vcodec_debug(inst, "- resolution_changed -"); + *res_chg = true; + add_fb_to_free_list(inst, fb); + return 0; + } + + /* wait decoder done interrupt */ + mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, 0); + + if (inst->vsi->load_data) + load_dec_table(inst); + + vp8_dec_finish(inst); + read_hw_segmentation_data(inst); + + err = vpu_dec_end(vpu); + if (err) + goto error; + + mtk_vcodec_debug(inst, "\n - FRAME[%d] - show=%d\n", inst->frm_cnt, + dec->show_frame); + inst->frm_cnt++; + *res_chg = false; + return 0; + +error: + mtk_vcodec_err(inst, "\n - FRAME[%d] - err=%d\n", inst->frm_cnt, err); + return err; +} + +static void get_disp_fb(struct vdec_vp8_inst *inst, struct vdec_fb **out_fb) +{ + struct vdec_fb_node *node; + struct vdec_fb *fb; + + node = list_first_entry_or_null(&inst->fb_disp_list, + struct vdec_fb_node, list); + if (node) { + list_move_tail(&node->list, &inst->available_fb_node_list); + fb = (struct vdec_fb *)node->fb; + fb->status |= FB_ST_DISPLAY; + mtk_vcodec_debug(inst, "[FB] get disp fb %p st=%d", + node->fb, fb->status); + } else { + fb = NULL; + mtk_vcodec_debug(inst, "[FB] there is no disp fb"); + } + + *out_fb = fb; +} + +static void get_free_fb(struct vdec_vp8_inst *inst, struct vdec_fb **out_fb) +{ + struct vdec_fb_node *node; + struct vdec_fb *fb; + + node = list_first_entry_or_null(&inst->fb_free_list, + struct vdec_fb_node, list); + if (node) { + list_move_tail(&node->list, &inst->available_fb_node_list); + fb = (struct vdec_fb *)node->fb; + fb->status |= FB_ST_FREE; + mtk_vcodec_debug(inst, "[FB] get free fb %p st=%d", + node->fb, fb->status); + } else { + fb = NULL; + mtk_vcodec_debug(inst, "[FB] there is no free fb"); + } + + *out_fb = fb; +} + +static void get_crop_info(struct vdec_vp8_inst *inst, struct v4l2_rect *cr) +{ + cr->left = 0; + cr->top = 0; + cr->width = inst->vsi->pic.pic_w; + cr->height = inst->vsi->pic.pic_h; + mtk_vcodec_debug(inst, "get crop info l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); +} + +static int vdec_vp8_get_param(void *h_vdec, enum vdec_get_param_type type, + void *out) +{ + struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; + + switch (type) { + case GET_PARAM_DISP_FRAME_BUFFER: + get_disp_fb(inst, out); + break; + + case GET_PARAM_FREE_FRAME_BUFFER: + get_free_fb(inst, out); + break; + + case GET_PARAM_PIC_INFO: + get_pic_info(inst, out); + break; + + case GET_PARAM_CROP_INFO: + get_crop_info(inst, out); + break; + + case GET_PARAM_DPB_SIZE: + *((unsigned int *)out) = VP8_DPB_SIZE; + break; + + default: + mtk_vcodec_err(inst, "invalid get parameter type=%d", type); + return -EINVAL; + } + + return 0; +} + +static void vdec_vp8_deinit(void *h_vdec) +{ + struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; + + mtk_vcodec_debug_enter(inst); + + vpu_dec_deinit(&inst->vpu); + free_working_buf(inst); + kfree(inst); +} + +const struct vdec_common_if vdec_vp8_if = { + .init = vdec_vp8_init, + .decode = vdec_vp8_decode, + .get_param = vdec_vp8_get_param, + .deinit = vdec_vp8_deinit, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp9_if.c new file mode 100644 index 000000000000..70b8383f7c8e --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec/vdec_vp9_if.c @@ -0,0 +1,1028 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * Kai-Sean Yang + * Tiffany Lin + */ + +#include +#include +#include +#include +#include + +#include "../mtk_vcodec_intr.h" +#include "../vdec_drv_base.h" +#include "../vdec_vpu_if.h" + +#define VP9_SUPER_FRAME_BS_SZ 64 +#define MAX_VP9_DPB_SIZE 9 + +#define REFS_PER_FRAME 3 +#define MAX_NUM_REF_FRAMES 8 +#define VP9_MAX_FRM_BUF_NUM 9 +#define VP9_MAX_FRM_BUF_NODE_NUM (VP9_MAX_FRM_BUF_NUM * 2) +#define VP9_SEG_ID_SZ 0x12000 + +/** + * struct vp9_dram_buf - contains buffer info for vpu + * @va : cpu address + * @pa : iova address + * @sz : buffer size + * @padding : for 64 bytes alignment + */ +struct vp9_dram_buf { + unsigned long va; + unsigned long pa; + unsigned int sz; + unsigned int padding; +}; + +/** + * struct vp9_fb_info - contains frame buffer info + * @fb : frmae buffer + * @reserved : reserved field used by vpu + */ +struct vp9_fb_info { + struct vdec_fb *fb; + unsigned int reserved[32]; +}; + +/** + * struct vp9_ref_cnt_buf - contains reference buffer information + * @buf : referenced frame buffer + * @ref_cnt : referenced frame buffer's reference count. + * When reference count=0, remove it from reference list + */ +struct vp9_ref_cnt_buf { + struct vp9_fb_info buf; + unsigned int ref_cnt; +}; + +/** + * struct vp9_ref_buf - contains current frame's reference buffer information + * @buf : reference buffer + * @idx : reference buffer index to frm_bufs + * @reserved : reserved field used by vpu + */ +struct vp9_ref_buf { + struct vp9_fb_info *buf; + unsigned int idx; + unsigned int reserved[6]; +}; + +/** + * struct vp9_sf_ref_fb - contains frame buffer info + * @fb : super frame reference frame buffer + * @used : this reference frame info entry is used + * @padding : for 64 bytes size align + */ +struct vp9_sf_ref_fb { + struct vdec_fb fb; + int used; + int padding; +}; + +/* + * struct vdec_vp9_vsi - shared buffer between host and VPU firmware + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @sf_bs_buf : super frame backup buffer (AP-W, VPU-R) + * @sf_ref_fb : record supoer frame reference buffer information + * (AP-R/W, VPU-R/W) + * @sf_next_ref_fb_idx : next available super frame (AP-W, VPU-R) + * @sf_frm_cnt : super frame count, filled by vpu (AP-R, VPU-W) + * @sf_frm_offset : super frame offset, filled by vpu (AP-R, VPU-W) + * @sf_frm_sz : super frame size, filled by vpu (AP-R, VPU-W) + * @sf_frm_idx : current super frame (AP-R, VPU-W) + * @sf_init : inform super frame info already parsed by vpu (AP-R, VPU-W) + * @fb : capture buffer (AP-W, VPU-R) + * @bs : bs buffer (AP-W, VPU-R) + * @cur_fb : current show capture buffer (AP-R/W, VPU-R/W) + * @pic_w : picture width (AP-R, VPU-W) + * @pic_h : picture height (AP-R, VPU-W) + * @buf_w : codec width (AP-R, VPU-W) + * @buf_h : coded height (AP-R, VPU-W) + * @buf_sz_y_bs : ufo compressed y plane size (AP-R, VPU-W) + * @buf_sz_c_bs : ufo compressed cbcr plane size (AP-R, VPU-W) + * @buf_len_sz_y : size used to store y plane ufo info (AP-R, VPU-W) + * @buf_len_sz_c : size used to store cbcr plane ufo info (AP-R, VPU-W) + + * @profile : profile sparsed from vpu (AP-R, VPU-W) + * @show_frame : [BIT(0)] display this frame or not (AP-R, VPU-W) + * [BIT(1)] reset segment data or not (AP-R, VPU-W) + * [BIT(2)] trig decoder hardware or not (AP-R, VPU-W) + * [BIT(3)] ask VPU to set bits(0~4) accordingly (AP-W, VPU-R) + * [BIT(4)] do not reset segment data before every frame (AP-R, VPU-W) + * @show_existing_frame : inform this frame is show existing frame + * (AP-R, VPU-W) + * @frm_to_show_idx : index to show frame (AP-R, VPU-W) + + * @refresh_frm_flags : indicate when frame need to refine reference count + * (AP-R, VPU-W) + * @resolution_changed : resolution change in this frame (AP-R, VPU-W) + + * @frm_bufs : maintain reference buffer info (AP-R/W, VPU-R/W) + * @ref_frm_map : maintain reference buffer map info (AP-R/W, VPU-R/W) + * @new_fb_idx : index to frm_bufs array (AP-R, VPU-W) + * @frm_num : decoded frame number, include sub-frame count (AP-R, VPU-W) + * @mv_buf : motion vector working buffer (AP-W, VPU-R) + * @frm_refs : maintain three reference buffer info (AP-R/W, VPU-R/W) + * @seg_id_buf : segmentation map working buffer (AP-W, VPU-R) + */ +struct vdec_vp9_vsi { + unsigned char sf_bs_buf[VP9_SUPER_FRAME_BS_SZ]; + struct vp9_sf_ref_fb sf_ref_fb[VP9_MAX_FRM_BUF_NUM-1]; + int sf_next_ref_fb_idx; + unsigned int sf_frm_cnt; + unsigned int sf_frm_offset[VP9_MAX_FRM_BUF_NUM-1]; + unsigned int sf_frm_sz[VP9_MAX_FRM_BUF_NUM-1]; + unsigned int sf_frm_idx; + unsigned int sf_init; + struct vdec_fb fb; + struct mtk_vcodec_mem bs; + struct vdec_fb cur_fb; + unsigned int pic_w; + unsigned int pic_h; + unsigned int buf_w; + unsigned int buf_h; + unsigned int buf_sz_y_bs; + unsigned int buf_sz_c_bs; + unsigned int buf_len_sz_y; + unsigned int buf_len_sz_c; + unsigned int profile; + unsigned int show_frame; + unsigned int show_existing_frame; + unsigned int frm_to_show_idx; + unsigned int refresh_frm_flags; + unsigned int resolution_changed; + + struct vp9_ref_cnt_buf frm_bufs[VP9_MAX_FRM_BUF_NUM]; + int ref_frm_map[MAX_NUM_REF_FRAMES]; + unsigned int new_fb_idx; + unsigned int frm_num; + struct vp9_dram_buf mv_buf; + + struct vp9_ref_buf frm_refs[REFS_PER_FRAME]; + struct vp9_dram_buf seg_id_buf; + +}; + +/* + * struct vdec_vp9_inst - vp9 decode instance + * @mv_buf : working buffer for mv + * @seg_id_buf : working buffer for segmentation map + * @dec_fb : vdec_fb node to link fb to different fb_xxx_list + * @available_fb_node_list : current available vdec_fb node + * @fb_use_list : current used or referenced vdec_fb + * @fb_free_list : current available to free vdec_fb + * @fb_disp_list : current available to display vdec_fb + * @cur_fb : current frame buffer + * @ctx : current decode context + * @vpu : vpu instance information + * @vsi : shared buffer between host and VPU firmware + * @total_frm_cnt : total frame count, it do not include sub-frames in super + * frame + * @mem : instance memory information + */ +struct vdec_vp9_inst { + struct mtk_vcodec_mem mv_buf; + struct mtk_vcodec_mem seg_id_buf; + + struct vdec_fb_node dec_fb[VP9_MAX_FRM_BUF_NODE_NUM]; + struct list_head available_fb_node_list; + struct list_head fb_use_list; + struct list_head fb_free_list; + struct list_head fb_disp_list; + struct vdec_fb *cur_fb; + struct mtk_vcodec_ctx *ctx; + struct vdec_vpu_inst vpu; + struct vdec_vp9_vsi *vsi; + unsigned int total_frm_cnt; + struct mtk_vcodec_mem mem; +}; + +static bool vp9_is_sf_ref_fb(struct vdec_vp9_inst *inst, struct vdec_fb *fb) +{ + int i; + struct vdec_vp9_vsi *vsi = inst->vsi; + + for (i = 0; i < ARRAY_SIZE(vsi->sf_ref_fb); i++) { + if (fb == &vsi->sf_ref_fb[i].fb) + return true; + } + return false; +} + +static struct vdec_fb *vp9_rm_from_fb_use_list(struct vdec_vp9_inst + *inst, void *addr) +{ + struct vdec_fb *fb = NULL; + struct vdec_fb_node *node; + + list_for_each_entry(node, &inst->fb_use_list, list) { + fb = (struct vdec_fb *)node->fb; + if (fb->base_y.va == addr) { + list_move_tail(&node->list, + &inst->available_fb_node_list); + break; + } + } + return fb; +} + +static void vp9_add_to_fb_free_list(struct vdec_vp9_inst *inst, + struct vdec_fb *fb) +{ + struct vdec_fb_node *node; + + if (fb) { + node = list_first_entry_or_null(&inst->available_fb_node_list, + struct vdec_fb_node, list); + + if (node) { + node->fb = fb; + list_move_tail(&node->list, &inst->fb_free_list); + } + } else { + mtk_vcodec_debug(inst, "No free fb node"); + } +} + +static void vp9_free_sf_ref_fb(struct vdec_fb *fb) +{ + struct vp9_sf_ref_fb *sf_ref_fb = + container_of(fb, struct vp9_sf_ref_fb, fb); + + sf_ref_fb->used = 0; +} + +static void vp9_ref_cnt_fb(struct vdec_vp9_inst *inst, int *idx, + int new_idx) +{ + struct vdec_vp9_vsi *vsi = inst->vsi; + int ref_idx = *idx; + + if (ref_idx >= 0 && vsi->frm_bufs[ref_idx].ref_cnt > 0) { + vsi->frm_bufs[ref_idx].ref_cnt--; + + if (vsi->frm_bufs[ref_idx].ref_cnt == 0) { + if (!vp9_is_sf_ref_fb(inst, + vsi->frm_bufs[ref_idx].buf.fb)) { + struct vdec_fb *fb; + + fb = vp9_rm_from_fb_use_list(inst, + vsi->frm_bufs[ref_idx].buf.fb->base_y.va); + vp9_add_to_fb_free_list(inst, fb); + } else + vp9_free_sf_ref_fb( + vsi->frm_bufs[ref_idx].buf.fb); + } + } + + *idx = new_idx; + vsi->frm_bufs[new_idx].ref_cnt++; +} + +static void vp9_free_all_sf_ref_fb(struct vdec_vp9_inst *inst) +{ + int i; + struct vdec_vp9_vsi *vsi = inst->vsi; + + for (i = 0; i < ARRAY_SIZE(vsi->sf_ref_fb); i++) { + if (vsi->sf_ref_fb[i].fb.base_y.va) { + mtk_vcodec_mem_free(inst->ctx, + &vsi->sf_ref_fb[i].fb.base_y); + mtk_vcodec_mem_free(inst->ctx, + &vsi->sf_ref_fb[i].fb.base_c); + vsi->sf_ref_fb[i].used = 0; + } + } +} + +/* For each sub-frame except the last one, the driver will dynamically + * allocate reference buffer by calling vp9_get_sf_ref_fb() + * The last sub-frame will use the original fb provided by the + * vp9_dec_decode() interface + */ +static int vp9_get_sf_ref_fb(struct vdec_vp9_inst *inst) +{ + int idx; + struct mtk_vcodec_mem *mem_basy_y; + struct mtk_vcodec_mem *mem_basy_c; + struct vdec_vp9_vsi *vsi = inst->vsi; + + for (idx = 0; + idx < ARRAY_SIZE(vsi->sf_ref_fb); + idx++) { + if (vsi->sf_ref_fb[idx].fb.base_y.va && + vsi->sf_ref_fb[idx].used == 0) { + return idx; + } + } + + for (idx = 0; + idx < ARRAY_SIZE(vsi->sf_ref_fb); + idx++) { + if (vsi->sf_ref_fb[idx].fb.base_y.va == NULL) + break; + } + + if (idx == ARRAY_SIZE(vsi->sf_ref_fb)) { + mtk_vcodec_err(inst, "List Full"); + return -1; + } + + mem_basy_y = &vsi->sf_ref_fb[idx].fb.base_y; + mem_basy_y->size = vsi->buf_sz_y_bs + + vsi->buf_len_sz_y; + + if (mtk_vcodec_mem_alloc(inst->ctx, mem_basy_y)) { + mtk_vcodec_err(inst, "Cannot allocate sf_ref_buf y_buf"); + return -1; + } + + mem_basy_c = &vsi->sf_ref_fb[idx].fb.base_c; + mem_basy_c->size = vsi->buf_sz_c_bs + + vsi->buf_len_sz_c; + + if (mtk_vcodec_mem_alloc(inst->ctx, mem_basy_c)) { + mtk_vcodec_err(inst, "Cannot allocate sf_ref_fb c_buf"); + return -1; + } + vsi->sf_ref_fb[idx].used = 0; + + return idx; +} + +static bool vp9_alloc_work_buf(struct vdec_vp9_inst *inst) +{ + struct vdec_vp9_vsi *vsi = inst->vsi; + int result; + struct mtk_vcodec_mem *mem; + + unsigned int max_pic_w; + unsigned int max_pic_h; + + + if (!(inst->ctx->dev->dec_capability & + VCODEC_CAPABILITY_4K_DISABLED)) { + max_pic_w = VCODEC_DEC_4K_CODED_WIDTH; + max_pic_h = VCODEC_DEC_4K_CODED_HEIGHT; + } else { + max_pic_w = MTK_VDEC_MAX_W; + max_pic_h = MTK_VDEC_MAX_H; + } + + if ((vsi->pic_w > max_pic_w) || + (vsi->pic_h > max_pic_h)) { + mtk_vcodec_err(inst, "Invalid w/h %d/%d", + vsi->pic_w, vsi->pic_h); + return false; + } + + mtk_vcodec_debug(inst, "BUF CHG(%d): w/h/sb_w/sb_h=%d/%d/%d/%d", + vsi->resolution_changed, + vsi->pic_w, + vsi->pic_h, + vsi->buf_w, + vsi->buf_h); + + mem = &inst->mv_buf; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + + mem->size = ((vsi->buf_w / 64) * + (vsi->buf_h / 64) + 2) * 36 * 16; + result = mtk_vcodec_mem_alloc(inst->ctx, mem); + if (result) { + mem->size = 0; + mtk_vcodec_err(inst, "Cannot allocate mv_buf"); + return false; + } + /* Set the va again */ + vsi->mv_buf.va = (unsigned long)mem->va; + vsi->mv_buf.pa = (unsigned long)mem->dma_addr; + vsi->mv_buf.sz = (unsigned int)mem->size; + + + mem = &inst->seg_id_buf; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + + mem->size = VP9_SEG_ID_SZ; + result = mtk_vcodec_mem_alloc(inst->ctx, mem); + if (result) { + mem->size = 0; + mtk_vcodec_err(inst, "Cannot allocate seg_id_buf"); + return false; + } + /* Set the va again */ + vsi->seg_id_buf.va = (unsigned long)mem->va; + vsi->seg_id_buf.pa = (unsigned long)mem->dma_addr; + vsi->seg_id_buf.sz = (unsigned int)mem->size; + + + vp9_free_all_sf_ref_fb(inst); + vsi->sf_next_ref_fb_idx = vp9_get_sf_ref_fb(inst); + + return true; +} + +static bool vp9_add_to_fb_disp_list(struct vdec_vp9_inst *inst, + struct vdec_fb *fb) +{ + struct vdec_fb_node *node; + + if (!fb) { + mtk_vcodec_err(inst, "fb == NULL"); + return false; + } + + node = list_first_entry_or_null(&inst->available_fb_node_list, + struct vdec_fb_node, list); + if (node) { + node->fb = fb; + list_move_tail(&node->list, &inst->fb_disp_list); + } else { + mtk_vcodec_err(inst, "No available fb node"); + return false; + } + + return true; +} + +/* If any buffer updating is signaled it should be done here. */ +static void vp9_swap_frm_bufs(struct vdec_vp9_inst *inst) +{ + struct vdec_vp9_vsi *vsi = inst->vsi; + struct vp9_fb_info *frm_to_show; + int ref_index = 0, mask; + + for (mask = vsi->refresh_frm_flags; mask; mask >>= 1) { + if (mask & 1) + vp9_ref_cnt_fb(inst, &vsi->ref_frm_map[ref_index], + vsi->new_fb_idx); + ++ref_index; + } + + frm_to_show = &vsi->frm_bufs[vsi->new_fb_idx].buf; + vsi->frm_bufs[vsi->new_fb_idx].ref_cnt--; + + if (frm_to_show->fb != inst->cur_fb) { + /* This frame is show exist frame and no decode output + * copy frame data from frm_to_show to current CAPTURE + * buffer + */ + if ((frm_to_show->fb != NULL) && + (inst->cur_fb->base_y.size >= + frm_to_show->fb->base_y.size) && + (inst->cur_fb->base_c.size >= + frm_to_show->fb->base_c.size)) { + memcpy((void *)inst->cur_fb->base_y.va, + (void *)frm_to_show->fb->base_y.va, + frm_to_show->fb->base_y.size); + memcpy((void *)inst->cur_fb->base_c.va, + (void *)frm_to_show->fb->base_c.va, + frm_to_show->fb->base_c.size); + } else { + /* After resolution change case, current CAPTURE buffer + * may have less buffer size than frm_to_show buffer + * size + */ + if (frm_to_show->fb != NULL) + mtk_vcodec_err(inst, + "inst->cur_fb->base_y.size=%zu, frm_to_show->fb.base_y.size=%zu", + inst->cur_fb->base_y.size, + frm_to_show->fb->base_y.size); + } + if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) { + if (vsi->show_frame & BIT(0)) + vp9_add_to_fb_disp_list(inst, inst->cur_fb); + } + } else { + if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) { + if (vsi->show_frame & BIT(0)) + vp9_add_to_fb_disp_list(inst, frm_to_show->fb); + } + } + + /* when ref_cnt ==0, move this fb to fb_free_list. v4l2 driver will + * clean fb_free_list + */ + if (vsi->frm_bufs[vsi->new_fb_idx].ref_cnt == 0) { + if (!vp9_is_sf_ref_fb( + inst, vsi->frm_bufs[vsi->new_fb_idx].buf.fb)) { + struct vdec_fb *fb; + + fb = vp9_rm_from_fb_use_list(inst, + vsi->frm_bufs[vsi->new_fb_idx].buf.fb->base_y.va); + + vp9_add_to_fb_free_list(inst, fb); + } else { + vp9_free_sf_ref_fb( + vsi->frm_bufs[vsi->new_fb_idx].buf.fb); + } + } + + /* if this super frame and it is not last sub-frame, get next fb for + * sub-frame decode + */ + if (vsi->sf_frm_cnt > 0 && vsi->sf_frm_idx != vsi->sf_frm_cnt - 1) + vsi->sf_next_ref_fb_idx = vp9_get_sf_ref_fb(inst); +} + +static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst) +{ + struct mtk_vcodec_ctx *ctx = inst->ctx; + + mtk_vcodec_wait_for_done_ctx(inst->ctx, + MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, 0); + + if (ctx->irq_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) + return true; + else + return false; +} + +static struct vdec_vp9_inst *vp9_alloc_inst(struct mtk_vcodec_ctx *ctx) +{ + int result; + struct mtk_vcodec_mem mem; + struct vdec_vp9_inst *inst; + + memset(&mem, 0, sizeof(mem)); + mem.size = sizeof(struct vdec_vp9_inst); + result = mtk_vcodec_mem_alloc(ctx, &mem); + if (result) + return NULL; + + inst = mem.va; + inst->mem = mem; + + return inst; +} + +static void vp9_free_inst(struct vdec_vp9_inst *inst) +{ + struct mtk_vcodec_mem mem; + + mem = inst->mem; + if (mem.va) + mtk_vcodec_mem_free(inst->ctx, &mem); +} + +static bool vp9_decode_end_proc(struct vdec_vp9_inst *inst) +{ + struct vdec_vp9_vsi *vsi = inst->vsi; + bool ret = false; + + if (!vsi->show_existing_frame) { + ret = vp9_wait_dec_end(inst); + if (!ret) { + mtk_vcodec_err(inst, "Decode failed, Decode Timeout @[%d]", + vsi->frm_num); + return false; + } + + if (vpu_dec_end(&inst->vpu)) { + mtk_vcodec_err(inst, "vp9_dec_vpu_end failed"); + return false; + } + mtk_vcodec_debug(inst, "Decode Ok @%d (%d/%d)", vsi->frm_num, + vsi->pic_w, vsi->pic_h); + } else { + mtk_vcodec_debug(inst, "Decode Ok @%d (show_existing_frame)", + vsi->frm_num); + } + + vp9_swap_frm_bufs(inst); + vsi->frm_num++; + return true; +} + +static bool vp9_is_last_sub_frm(struct vdec_vp9_inst *inst) +{ + struct vdec_vp9_vsi *vsi = inst->vsi; + + if (vsi->sf_frm_cnt <= 0 || vsi->sf_frm_idx == vsi->sf_frm_cnt) + return true; + + return false; +} + +static struct vdec_fb *vp9_rm_from_fb_disp_list(struct vdec_vp9_inst *inst) +{ + struct vdec_fb_node *node; + struct vdec_fb *fb = NULL; + + node = list_first_entry_or_null(&inst->fb_disp_list, + struct vdec_fb_node, list); + if (node) { + fb = (struct vdec_fb *)node->fb; + fb->status |= FB_ST_DISPLAY; + list_move_tail(&node->list, &inst->available_fb_node_list); + mtk_vcodec_debug(inst, "[FB] get disp fb %p st=%d", + node->fb, fb->status); + } else + mtk_vcodec_debug(inst, "[FB] there is no disp fb"); + + return fb; +} + +static bool vp9_add_to_fb_use_list(struct vdec_vp9_inst *inst, + struct vdec_fb *fb) +{ + struct vdec_fb_node *node; + + if (!fb) { + mtk_vcodec_debug(inst, "fb == NULL"); + return false; + } + + node = list_first_entry_or_null(&inst->available_fb_node_list, + struct vdec_fb_node, list); + if (node) { + node->fb = fb; + list_move_tail(&node->list, &inst->fb_use_list); + } else { + mtk_vcodec_err(inst, "No free fb node"); + return false; + } + return true; +} + +static void vp9_reset(struct vdec_vp9_inst *inst) +{ + struct vdec_fb_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &inst->fb_use_list, list) + list_move_tail(&node->list, &inst->fb_free_list); + + vp9_free_all_sf_ref_fb(inst); + inst->vsi->sf_next_ref_fb_idx = vp9_get_sf_ref_fb(inst); + + if (vpu_dec_reset(&inst->vpu)) + mtk_vcodec_err(inst, "vp9_dec_vpu_reset failed"); + + /* Set the va again, since vpu_dec_reset will clear mv_buf in vpu */ + inst->vsi->mv_buf.va = (unsigned long)inst->mv_buf.va; + inst->vsi->mv_buf.pa = (unsigned long)inst->mv_buf.dma_addr; + inst->vsi->mv_buf.sz = (unsigned long)inst->mv_buf.size; + + /* Set the va again, since vpu_dec_reset will clear seg_id_buf in vpu */ + inst->vsi->seg_id_buf.va = (unsigned long)inst->seg_id_buf.va; + inst->vsi->seg_id_buf.pa = (unsigned long)inst->seg_id_buf.dma_addr; + inst->vsi->seg_id_buf.sz = (unsigned long)inst->seg_id_buf.size; + +} + +static void init_all_fb_lists(struct vdec_vp9_inst *inst) +{ + int i; + + INIT_LIST_HEAD(&inst->available_fb_node_list); + INIT_LIST_HEAD(&inst->fb_use_list); + INIT_LIST_HEAD(&inst->fb_free_list); + INIT_LIST_HEAD(&inst->fb_disp_list); + + for (i = 0; i < ARRAY_SIZE(inst->dec_fb); i++) { + INIT_LIST_HEAD(&inst->dec_fb[i].list); + inst->dec_fb[i].fb = NULL; + list_add_tail(&inst->dec_fb[i].list, + &inst->available_fb_node_list); + } +} + +static void get_pic_info(struct vdec_vp9_inst *inst, struct vdec_pic_info *pic) +{ + pic->fb_sz[0] = inst->vsi->buf_sz_y_bs + inst->vsi->buf_len_sz_y; + pic->fb_sz[1] = inst->vsi->buf_sz_c_bs + inst->vsi->buf_len_sz_c; + + pic->pic_w = inst->vsi->pic_w; + pic->pic_h = inst->vsi->pic_h; + pic->buf_w = inst->vsi->buf_w; + pic->buf_h = inst->vsi->buf_h; + + 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, "fb size: Y(%d), C(%d)", + pic->fb_sz[0], + pic->fb_sz[1]); +} + +static void get_disp_fb(struct vdec_vp9_inst *inst, struct vdec_fb **out_fb) +{ + + *out_fb = vp9_rm_from_fb_disp_list(inst); + if (*out_fb) + (*out_fb)->status |= FB_ST_DISPLAY; +} + +static void get_free_fb(struct vdec_vp9_inst *inst, struct vdec_fb **out_fb) +{ + struct vdec_fb_node *node; + struct vdec_fb *fb = NULL; + + node = list_first_entry_or_null(&inst->fb_free_list, + struct vdec_fb_node, list); + if (node) { + list_move_tail(&node->list, &inst->available_fb_node_list); + fb = (struct vdec_fb *)node->fb; + fb->status |= FB_ST_FREE; + mtk_vcodec_debug(inst, "[FB] get free fb %p st=%d", + node->fb, fb->status); + } else { + mtk_vcodec_debug(inst, "[FB] there is no free fb"); + } + + *out_fb = fb; +} + +static int validate_vsi_array_indexes(struct vdec_vp9_inst *inst, + struct vdec_vp9_vsi *vsi) { + if (vsi->sf_frm_idx >= VP9_MAX_FRM_BUF_NUM - 1) { + mtk_vcodec_err(inst, "Invalid vsi->sf_frm_idx=%u.", + vsi->sf_frm_idx); + return -EIO; + } + if (vsi->frm_to_show_idx >= VP9_MAX_FRM_BUF_NUM) { + mtk_vcodec_err(inst, "Invalid vsi->frm_to_show_idx=%u.", + vsi->frm_to_show_idx); + return -EIO; + } + if (vsi->new_fb_idx >= VP9_MAX_FRM_BUF_NUM) { + mtk_vcodec_err(inst, "Invalid vsi->new_fb_idx=%u.", + vsi->new_fb_idx); + return -EIO; + } + return 0; +} + +static void vdec_vp9_deinit(void *h_vdec) +{ + struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; + struct mtk_vcodec_mem *mem; + int ret = 0; + + ret = vpu_dec_deinit(&inst->vpu); + if (ret) + mtk_vcodec_err(inst, "vpu_dec_deinit failed"); + + mem = &inst->mv_buf; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + + mem = &inst->seg_id_buf; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + + vp9_free_all_sf_ref_fb(inst); + vp9_free_inst(inst); +} + +static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) +{ + struct vdec_vp9_inst *inst; + + inst = vp9_alloc_inst(ctx); + if (!inst) + return -ENOMEM; + + inst->total_frm_cnt = 0; + inst->ctx = ctx; + + inst->vpu.id = IPI_VDEC_VP9; + inst->vpu.ctx = ctx; + + if (vpu_dec_init(&inst->vpu)) { + mtk_vcodec_err(inst, "vp9_dec_vpu_init failed"); + goto err_deinit_inst; + } + + inst->vsi = (struct vdec_vp9_vsi *)inst->vpu.vsi; + + inst->vsi->show_frame |= BIT(3); + + init_all_fb_lists(inst); + + ctx->drv_handle = inst; + return 0; + +err_deinit_inst: + vp9_free_inst(inst); + + return -EINVAL; +} + +static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + int ret = 0; + struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; + struct vdec_vp9_vsi *vsi = inst->vsi; + u32 data[3]; + int i; + + *res_chg = false; + + if ((bs == NULL) && (fb == NULL)) { + mtk_vcodec_debug(inst, "[EOS]"); + vp9_reset(inst); + return ret; + } + + if (bs == NULL) { + mtk_vcodec_err(inst, "bs == NULL"); + return -EINVAL; + } + + mtk_vcodec_debug(inst, "Input BS Size = %zu", bs->size); + + while (1) { + struct vdec_fb *cur_fb = NULL; + + data[0] = *((unsigned int *)bs->va); + data[1] = *((unsigned int *)(bs->va + 4)); + data[2] = *((unsigned int *)(bs->va + 8)); + + vsi->bs = *bs; + + if (fb) + vsi->fb = *fb; + + if (!vsi->sf_init) { + unsigned int sf_bs_sz; + unsigned int sf_bs_off; + unsigned char *sf_bs_src; + unsigned char *sf_bs_dst; + + sf_bs_sz = bs->size > VP9_SUPER_FRAME_BS_SZ ? + VP9_SUPER_FRAME_BS_SZ : bs->size; + sf_bs_off = VP9_SUPER_FRAME_BS_SZ - sf_bs_sz; + sf_bs_src = bs->va + bs->size - sf_bs_sz; + sf_bs_dst = vsi->sf_bs_buf + sf_bs_off; + memcpy(sf_bs_dst, sf_bs_src, sf_bs_sz); + } else { + if ((vsi->sf_frm_cnt > 0) && + (vsi->sf_frm_idx < vsi->sf_frm_cnt)) { + unsigned int idx = vsi->sf_frm_idx; + + memcpy((void *)bs->va, + (void *)(bs->va + + vsi->sf_frm_offset[idx]), + vsi->sf_frm_sz[idx]); + } + } + + if (!(vsi->show_frame & BIT(4))) + memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); + + ret = vpu_dec_start(&inst->vpu, data, 3); + if (ret) { + mtk_vcodec_err(inst, "vpu_dec_start failed"); + goto DECODE_ERROR; + } + + if (vsi->show_frame & BIT(1)) { + memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); + + if (vsi->show_frame & BIT(2)) { + ret = vpu_dec_start(&inst->vpu, NULL, 0); + if (ret) { + mtk_vcodec_err(inst, "vpu trig decoder failed"); + goto DECODE_ERROR; + } + } + } + + ret = validate_vsi_array_indexes(inst, vsi); + if (ret) { + mtk_vcodec_err(inst, "Invalid values from VPU."); + goto DECODE_ERROR; + } + + if (vsi->resolution_changed) { + if (!vp9_alloc_work_buf(inst)) { + ret = -EIO; + goto DECODE_ERROR; + } + } + + if (vsi->sf_frm_cnt > 0) { + cur_fb = &vsi->sf_ref_fb[vsi->sf_next_ref_fb_idx].fb; + + if (vsi->sf_frm_idx < vsi->sf_frm_cnt) + inst->cur_fb = cur_fb; + else + inst->cur_fb = fb; + } else { + inst->cur_fb = fb; + } + + vsi->frm_bufs[vsi->new_fb_idx].buf.fb = inst->cur_fb; + if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) + vp9_add_to_fb_use_list(inst, inst->cur_fb); + + mtk_vcodec_debug(inst, "[#pic %d]", vsi->frm_num); + + if (vsi->show_existing_frame) + mtk_vcodec_debug(inst, + "drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", + vsi->new_fb_idx, vsi->frm_to_show_idx); + + if (vsi->show_existing_frame && (vsi->frm_to_show_idx < + VP9_MAX_FRM_BUF_NUM)) { + mtk_vcodec_debug(inst, + "Skip Decode drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", + vsi->new_fb_idx, vsi->frm_to_show_idx); + + vp9_ref_cnt_fb(inst, &vsi->new_fb_idx, + vsi->frm_to_show_idx); + } + + /* VPU assign the buffer pointer in its address space, + * reassign here + */ + for (i = 0; i < ARRAY_SIZE(vsi->frm_refs); i++) { + unsigned int idx = vsi->frm_refs[i].idx; + + vsi->frm_refs[i].buf = &vsi->frm_bufs[idx].buf; + } + + if (vsi->resolution_changed) { + *res_chg = true; + mtk_vcodec_debug(inst, "VDEC_ST_RESOLUTION_CHANGED"); + + ret = 0; + goto DECODE_ERROR; + } + + if (!vp9_decode_end_proc(inst)) { + mtk_vcodec_err(inst, "vp9_decode_end_proc"); + ret = -EINVAL; + goto DECODE_ERROR; + } + + if (vp9_is_last_sub_frm(inst)) + break; + + } + inst->total_frm_cnt++; + +DECODE_ERROR: + if (ret < 0) + vp9_add_to_fb_free_list(inst, fb); + + return ret; +} + +static void get_crop_info(struct vdec_vp9_inst *inst, struct v4l2_rect *cr) +{ + cr->left = 0; + cr->top = 0; + cr->width = inst->vsi->pic_w; + cr->height = inst->vsi->pic_h; + mtk_vcodec_debug(inst, "get crop info l=%d, t=%d, w=%d, h=%d\n", + cr->left, cr->top, cr->width, cr->height); +} + +static int vdec_vp9_get_param(void *h_vdec, enum vdec_get_param_type type, + void *out) +{ + struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; + int ret = 0; + + switch (type) { + case GET_PARAM_DISP_FRAME_BUFFER: + get_disp_fb(inst, out); + break; + case GET_PARAM_FREE_FRAME_BUFFER: + get_free_fb(inst, out); + break; + case GET_PARAM_PIC_INFO: + get_pic_info(inst, out); + break; + case GET_PARAM_DPB_SIZE: + *((unsigned int *)out) = MAX_VP9_DPB_SIZE; + break; + case GET_PARAM_CROP_INFO: + get_crop_info(inst, out); + break; + default: + mtk_vcodec_err(inst, "not supported param type %d", type); + ret = -EINVAL; + break; + } + + return ret; +} + +const struct vdec_common_if vdec_vp9_if = { + .init = vdec_vp9_init, + .decode = vdec_vp9_decode, + .get_param = vdec_vp9_get_param, + .deinit = vdec_vp9_deinit, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_base.h new file mode 100644 index 000000000000..e913f963b7db --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_base.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + */ + +#ifndef _VDEC_DRV_BASE_ +#define _VDEC_DRV_BASE_ + +#include "vdec_drv_if.h" + +struct vdec_common_if { + /** + * (*init)() - initialize decode driver + * @ctx : [in] mtk v4l2 context + * @h_vdec : [out] driver handle + */ + int (*init)(struct mtk_vcodec_ctx *ctx); + + /** + * (*decode)() - trigger decode + * @h_vdec : [in] driver handle + * @bs : [in] input bitstream + * @fb : [in] frame buffer to store decoded frame + * @res_chg : [out] resolution change happen + */ + int (*decode)(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg); + + /** + * (*get_param)() - get driver's parameter + * @h_vdec : [in] driver handle + * @type : [in] input parameter type + * @out : [out] buffer to store query result + */ + int (*get_param)(void *h_vdec, enum vdec_get_param_type type, + void *out); + + /** + * (*deinit)() - deinitialize driver. + * @h_vdec : [in] driver handle to be deinit + */ + void (*deinit)(void *h_vdec); +}; + +#endif diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.c new file mode 100644 index 000000000000..05a5b240e906 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + * Tiffany Lin + */ + +#include +#include +#include + +#include "vdec_drv_if.h" +#include "mtk_vcodec_dec.h" +#include "vdec_drv_base.h" +#include "mtk_vcodec_dec_pm.h" + +int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) +{ + int ret = 0; + + switch (fourcc) { + case V4L2_PIX_FMT_H264_SLICE: + ctx->dec_if = &vdec_h264_slice_if; + break; + case V4L2_PIX_FMT_H264: + ctx->dec_if = &vdec_h264_if; + ctx->hw_id = MTK_VDEC_CORE; + break; + case V4L2_PIX_FMT_VP8: + ctx->dec_if = &vdec_vp8_if; + ctx->hw_id = MTK_VDEC_CORE; + break; + case V4L2_PIX_FMT_VP9: + ctx->dec_if = &vdec_vp9_if; + ctx->hw_id = MTK_VDEC_CORE; + break; + default: + return -EINVAL; + } + + mtk_vdec_lock(ctx); + mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); + ret = ctx->dec_if->init(ctx); + mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); + mtk_vdec_unlock(ctx); + + return ret; +} + +int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + int ret = 0; + + if (bs) { + if ((bs->dma_addr & 63) != 0) { + mtk_v4l2_err("bs dma_addr should 64 byte align"); + return -EINVAL; + } + } + + if (fb) { + if (((fb->base_y.dma_addr & 511) != 0) || + ((fb->base_c.dma_addr & 511) != 0)) { + mtk_v4l2_err("frame buffer dma_addr should 512 byte align"); + return -EINVAL; + } + } + + if (!ctx->drv_handle) + return -EIO; + + mtk_vdec_lock(ctx); + + mtk_vcodec_set_curr_ctx(ctx->dev, ctx, ctx->hw_id); + mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); + ret = ctx->dec_if->decode(ctx->drv_handle, bs, fb, res_chg); + mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); + mtk_vcodec_set_curr_ctx(ctx->dev, NULL, ctx->hw_id); + + mtk_vdec_unlock(ctx); + + return ret; +} + +int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, + void *out) +{ + int ret = 0; + + if (!ctx->drv_handle) + return -EIO; + + mtk_vdec_lock(ctx); + ret = ctx->dec_if->get_param(ctx->drv_handle, type, out); + mtk_vdec_unlock(ctx); + + return ret; +} + +void vdec_if_deinit(struct mtk_vcodec_ctx *ctx) +{ + if (!ctx->drv_handle) + return; + + mtk_vdec_lock(ctx); + mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); + ctx->dec_if->deinit(ctx->drv_handle); + mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); + mtk_vdec_unlock(ctx); + + ctx->drv_handle = NULL; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.h new file mode 100644 index 000000000000..d467e8af4a84 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_drv_if.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + * Tiffany Lin + */ + +#ifndef _VDEC_DRV_IF_H_ +#define _VDEC_DRV_IF_H_ + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_dec.h" +#include "mtk_vcodec_util.h" + + +/** + * enum vdec_fb_status - decoder frame buffer status + * @FB_ST_NORMAL: initial state + * @FB_ST_DISPLAY: frame buffer is ready to be displayed + * @FB_ST_FREE: frame buffer is not used by decoder any more + */ +enum vdec_fb_status { + FB_ST_NORMAL = 0, + FB_ST_DISPLAY = (1 << 0), + FB_ST_FREE = (1 << 1) +}; + +/* For GET_PARAM_DISP_FRAME_BUFFER and GET_PARAM_FREE_FRAME_BUFFER, + * the caller does not own the returned buffer. The buffer will not be + * released before vdec_if_deinit. + * GET_PARAM_DISP_FRAME_BUFFER : get next displayable frame buffer, + * struct vdec_fb** + * GET_PARAM_FREE_FRAME_BUFFER : get non-referenced framebuffer, vdec_fb** + * GET_PARAM_PIC_INFO : get picture info, struct vdec_pic_info* + * GET_PARAM_CROP_INFO : get crop info, struct v4l2_crop* + * GET_PARAM_DPB_SIZE : get dpb size, unsigned int* + */ +enum vdec_get_param_type { + GET_PARAM_DISP_FRAME_BUFFER, + GET_PARAM_FREE_FRAME_BUFFER, + GET_PARAM_PIC_INFO, + GET_PARAM_CROP_INFO, + GET_PARAM_DPB_SIZE +}; + +/** + * struct vdec_fb_node - decoder frame buffer node + * @list : list to hold this node + * @fb : point to frame buffer (vdec_fb), fb could point to frame buffer and + * working buffer this is for maintain buffers in different state + */ +struct vdec_fb_node { + struct list_head list; + struct vdec_fb *fb; +}; + +extern const struct vdec_common_if vdec_h264_if; +extern const struct vdec_common_if vdec_h264_slice_if; +extern const struct vdec_common_if vdec_vp8_if; +extern const struct vdec_common_if vdec_vp9_if; + +/** + * vdec_if_init() - initialize decode driver + * @ctx : [in] v4l2 context + * @fourcc : [in] video format fourcc, V4L2_PIX_FMT_H264/VP8/VP9.. + */ +int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); + +/** + * vdec_if_deinit() - deinitialize decode driver + * @ctx : [in] v4l2 context + * + */ +void vdec_if_deinit(struct mtk_vcodec_ctx *ctx); + +/** + * vdec_if_decode() - trigger decode + * @ctx : [in] v4l2 context + * @bs : [in] input bitstream + * @fb : [in] frame buffer to store decoded frame, when null means parse + * header only + * @res_chg : [out] resolution change happens if current bs have different + * picture width/height + * Note: To flush the decoder when reaching EOF, set input bitstream as NULL. + * + * Return: 0 on success. -EIO on unrecoverable error. + */ +int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg); + +/** + * vdec_if_get_param() - get driver's parameter + * @ctx : [in] v4l2 context + * @type : [in] input parameter type + * @out : [out] buffer to store query result + */ +int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, + void *out); + +#endif diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_ipi_msg.h b/drivers/media/platform/mediatek/mtk-vcodec/vdec_ipi_msg.h new file mode 100644 index 000000000000..bf54d6d9a857 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_ipi_msg.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + */ + +#ifndef _VDEC_IPI_MSG_H_ +#define _VDEC_IPI_MSG_H_ + +/* + * enum vdec_ipi_msgid - message id between AP and VPU + * @AP_IPIMSG_XXX : AP to VPU cmd message id + * @VPU_IPIMSG_XXX_ACK : VPU ack AP cmd message id + */ +enum vdec_ipi_msgid { + AP_IPIMSG_DEC_INIT = 0xA000, + AP_IPIMSG_DEC_START = 0xA001, + AP_IPIMSG_DEC_END = 0xA002, + AP_IPIMSG_DEC_DEINIT = 0xA003, + AP_IPIMSG_DEC_RESET = 0xA004, + AP_IPIMSG_DEC_CORE = 0xA005, + AP_IPIMSG_DEC_CORE_END = 0xA006, + + VPU_IPIMSG_DEC_INIT_ACK = 0xB000, + VPU_IPIMSG_DEC_START_ACK = 0xB001, + VPU_IPIMSG_DEC_END_ACK = 0xB002, + VPU_IPIMSG_DEC_DEINIT_ACK = 0xB003, + VPU_IPIMSG_DEC_RESET_ACK = 0xB004, + VPU_IPIMSG_DEC_CORE_ACK = 0xB005, + VPU_IPIMSG_DEC_CORE_END_ACK = 0xB006, +}; + +/** + * struct vdec_ap_ipi_cmd - generic AP to VPU ipi command format + * @msg_id : vdec_ipi_msgid + * @vpu_inst_addr : VPU decoder instance address. Used if ABI version < 2. + * @inst_id : instance ID. Used if the ABI version >= 2. + * @codec_type : codec fourcc + * @reserved : reserved param + */ +struct vdec_ap_ipi_cmd { + uint32_t msg_id; + union { + uint32_t vpu_inst_addr; + uint32_t inst_id; + }; + u32 codec_type; + u32 reserved; +}; + +/** + * struct vdec_vpu_ipi_ack - generic VPU to AP ipi command format + * @msg_id : vdec_ipi_msgid + * @status : VPU exeuction result + * @ap_inst_addr : AP video decoder instance address + */ +struct vdec_vpu_ipi_ack { + uint32_t msg_id; + int32_t status; + uint64_t ap_inst_addr; +}; + +/** + * struct vdec_ap_ipi_init - for AP_IPIMSG_DEC_INIT + * @msg_id : AP_IPIMSG_DEC_INIT + * @codec_type : codec fourcc + * @ap_inst_addr : AP video decoder instance address + */ +struct vdec_ap_ipi_init { + uint32_t msg_id; + u32 codec_type; + uint64_t ap_inst_addr; +}; + +/** + * struct vdec_ap_ipi_dec_start - for AP_IPIMSG_DEC_START + * @msg_id : AP_IPIMSG_DEC_START + * @vpu_inst_addr : VPU decoder instance address. Used if ABI version < 2. + * @inst_id : instance ID. Used if the ABI version >= 2. + * @data : Header info + * H264 decoder [0]:buf_sz [1]:nal_start + * VP8 decoder [0]:width/height + * VP9 decoder [0]:profile, [1][2] width/height + * @codec_type : codec fourcc + */ +struct vdec_ap_ipi_dec_start { + uint32_t msg_id; + union { + uint32_t vpu_inst_addr; + uint32_t inst_id; + }; + uint32_t data[3]; + u32 codec_type; +}; + +/** + * struct vdec_vpu_ipi_init_ack - for VPU_IPIMSG_DEC_INIT_ACK + * @msg_id : VPU_IPIMSG_DEC_INIT_ACK + * @status : VPU exeuction result + * @ap_inst_addr : AP vcodec_vpu_inst instance address + * @vpu_inst_addr : VPU decoder instance address + * @vdec_abi_version: ABI version of the firmware. Kernel can use it to + * ensure that it is compatible with the firmware. + * This field is not valid for MT8173 and must not be + * accessed for this chip. + * @inst_id : instance ID. Valid only if the ABI version >= 2. + */ +struct vdec_vpu_ipi_init_ack { + uint32_t msg_id; + int32_t status; + uint64_t ap_inst_addr; + uint32_t vpu_inst_addr; + uint32_t vdec_abi_version; + uint32_t inst_id; +}; + +#endif diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.c b/drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.c new file mode 100644 index 000000000000..4b062a8128b4 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong + */ + +#include +#include +#include + +#include "mtk_vcodec_dec_pm.h" +#include "mtk_vcodec_drv.h" +#include "vdec_msg_queue.h" + +#define VDEC_MSG_QUEUE_TIMEOUT_MS 1500 + +/* the size used to store lat slice header information */ +#define VDEC_LAT_SLICE_HEADER_SZ (640 * SZ_1K) + +/* the size used to store avc error information */ +#define VDEC_ERR_MAP_SZ_AVC (17 * SZ_1K) + +/* core will read the trans buffer which decoded by lat to decode again. + * The trans buffer size of FHD and 4K bitstreams are different. + */ +static int vde_msg_queue_get_trans_size(int width, int height) +{ + if (width > 1920 || height > 1088) + return 30 * SZ_1M; + else + return 6 * SZ_1M; +} + +void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index) +{ + init_waitqueue_head(&ctx->ready_to_use); + INIT_LIST_HEAD(&ctx->ready_queue); + spin_lock_init(&ctx->ready_lock); + ctx->ready_num = 0; + ctx->hardware_index = hardware_index; +} + +static struct list_head *vdec_get_buf_list(int hardware_index, struct vdec_lat_buf *buf) +{ + switch (hardware_index) { + case MTK_VDEC_CORE: + return &buf->core_list; + case MTK_VDEC_LAT0: + return &buf->lat_list; + default: + return NULL; + } +} + +int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf *buf) +{ + struct list_head *head; + + head = vdec_get_buf_list(msg_ctx->hardware_index, buf); + if (!head) { + mtk_v4l2_err("fail to qbuf: %d", msg_ctx->hardware_index); + return -EINVAL; + } + + spin_lock(&msg_ctx->ready_lock); + list_add_tail(head, &msg_ctx->ready_queue); + msg_ctx->ready_num++; + + if (msg_ctx->hardware_index != MTK_VDEC_CORE) + wake_up_all(&msg_ctx->ready_to_use); + else + queue_work(buf->ctx->dev->core_workqueue, + &buf->ctx->msg_queue.core_work); + + mtk_v4l2_debug(3, "enqueue buf type: %d addr: 0x%p num: %d", + msg_ctx->hardware_index, buf, msg_ctx->ready_num); + spin_unlock(&msg_ctx->ready_lock); + + return 0; +} + +static bool vdec_msg_queue_wait_event(struct vdec_msg_queue_ctx *msg_ctx) +{ + int ret; + + ret = wait_event_timeout(msg_ctx->ready_to_use, + !list_empty(&msg_ctx->ready_queue), + msecs_to_jiffies(VDEC_MSG_QUEUE_TIMEOUT_MS)); + if (!ret) + return false; + + return true; +} + +struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *msg_ctx) +{ + struct vdec_lat_buf *buf; + struct list_head *head; + int ret; + + spin_lock(&msg_ctx->ready_lock); + if (list_empty(&msg_ctx->ready_queue)) { + mtk_v4l2_debug(3, "queue is NULL, type:%d num: %d", + msg_ctx->hardware_index, msg_ctx->ready_num); + spin_unlock(&msg_ctx->ready_lock); + + if (msg_ctx->hardware_index == MTK_VDEC_CORE) + return NULL; + + ret = vdec_msg_queue_wait_event(msg_ctx); + if (!ret) + return NULL; + spin_lock(&msg_ctx->ready_lock); + } + + if (msg_ctx->hardware_index == MTK_VDEC_CORE) + buf = list_first_entry(&msg_ctx->ready_queue, + struct vdec_lat_buf, core_list); + else + buf = list_first_entry(&msg_ctx->ready_queue, + struct vdec_lat_buf, lat_list); + + head = vdec_get_buf_list(msg_ctx->hardware_index, buf); + if (!head) { + spin_unlock(&msg_ctx->ready_lock); + mtk_v4l2_err("fail to dqbuf: %d", msg_ctx->hardware_index); + return NULL; + } + list_del(head); + + msg_ctx->ready_num--; + mtk_v4l2_debug(3, "dqueue buf type:%d addr: 0x%p num: %d", + msg_ctx->hardware_index, buf, msg_ctx->ready_num); + spin_unlock(&msg_ctx->ready_lock); + + return buf; +} + +void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr) +{ + spin_lock(&msg_queue->lat_ctx.ready_lock); + msg_queue->wdma_rptr_addr = ube_rptr; + mtk_v4l2_debug(3, "update ube rprt (0x%llx)", ube_rptr); + spin_unlock(&msg_queue->lat_ctx.ready_lock); +} + +void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr) +{ + spin_lock(&msg_queue->lat_ctx.ready_lock); + msg_queue->wdma_wptr_addr = ube_wptr; + mtk_v4l2_debug(3, "update ube wprt: (0x%llx 0x%llx) offset: 0x%llx", + msg_queue->wdma_rptr_addr, msg_queue->wdma_wptr_addr, + ube_wptr); + spin_unlock(&msg_queue->lat_ctx.ready_lock); +} + +bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) +{ + long timeout_jiff; + int ret; + + timeout_jiff = msecs_to_jiffies(1000 * (NUM_BUFFER_COUNT + 2)); + ret = wait_event_timeout(msg_queue->lat_ctx.ready_to_use, + msg_queue->lat_ctx.ready_num == NUM_BUFFER_COUNT, + timeout_jiff); + if (ret) { + mtk_v4l2_debug(3, "success to get lat buf: %d", + msg_queue->lat_ctx.ready_num); + return true; + } + mtk_v4l2_err("failed with lat buf isn't full: %d", + msg_queue->lat_ctx.ready_num); + return false; +} + +void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx) +{ + struct vdec_lat_buf *lat_buf; + struct mtk_vcodec_mem *mem; + int i; + + mem = &msg_queue->wdma_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + for (i = 0; i < NUM_BUFFER_COUNT; i++) { + lat_buf = &msg_queue->lat_buf[i]; + + mem = &lat_buf->wdma_err_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + + mem = &lat_buf->slice_bc_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + + kfree(lat_buf->private_data); + } +} + +static void vdec_msg_queue_core_work(struct work_struct *work) +{ + struct vdec_msg_queue *msg_queue = + container_of(work, struct vdec_msg_queue, core_work); + struct mtk_vcodec_ctx *ctx = + container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue); + struct mtk_vcodec_dev *dev = ctx->dev; + struct vdec_lat_buf *lat_buf; + + lat_buf = vdec_msg_queue_dqbuf(&dev->msg_queue_core_ctx); + if (!lat_buf) + return; + + ctx = lat_buf->ctx; + mtk_vcodec_set_curr_ctx(dev, ctx, MTK_VDEC_CORE); + + lat_buf->core_decode(lat_buf); + + mtk_vcodec_set_curr_ctx(dev, NULL, MTK_VDEC_CORE); + vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); + + if (!list_empty(&ctx->msg_queue.lat_ctx.ready_queue)) { + mtk_v4l2_debug(3, "re-schedule to decode for core: %d", + dev->msg_queue_core_ctx.ready_num); + queue_work(dev->core_workqueue, &msg_queue->core_work); + } +} + +int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, + int private_size) +{ + struct vdec_lat_buf *lat_buf; + int i, err; + + /* already init msg queue */ + if (msg_queue->wdma_addr.size) + return 0; + + vdec_msg_queue_init_ctx(&msg_queue->lat_ctx, MTK_VDEC_LAT0); + INIT_WORK(&msg_queue->core_work, vdec_msg_queue_core_work); + msg_queue->wdma_addr.size = + vde_msg_queue_get_trans_size(ctx->picinfo.buf_w, + ctx->picinfo.buf_h); + + err = mtk_vcodec_mem_alloc(ctx, &msg_queue->wdma_addr); + if (err) { + mtk_v4l2_err("failed to allocate wdma_addr buf"); + return -ENOMEM; + } + msg_queue->wdma_rptr_addr = msg_queue->wdma_addr.dma_addr; + msg_queue->wdma_wptr_addr = msg_queue->wdma_addr.dma_addr; + + for (i = 0; i < NUM_BUFFER_COUNT; i++) { + lat_buf = &msg_queue->lat_buf[i]; + + lat_buf->wdma_err_addr.size = VDEC_ERR_MAP_SZ_AVC; + err = mtk_vcodec_mem_alloc(ctx, &lat_buf->wdma_err_addr); + if (err) { + mtk_v4l2_err("failed to allocate wdma_err_addr buf[%d]", i); + goto mem_alloc_err; + } + + lat_buf->slice_bc_addr.size = VDEC_LAT_SLICE_HEADER_SZ; + err = mtk_vcodec_mem_alloc(ctx, &lat_buf->slice_bc_addr); + if (err) { + mtk_v4l2_err("failed to allocate wdma_addr buf[%d]", i); + goto mem_alloc_err; + } + + lat_buf->private_data = kzalloc(private_size, GFP_KERNEL); + if (!lat_buf->private_data) { + err = -ENOMEM; + goto mem_alloc_err; + } + + lat_buf->ctx = ctx; + lat_buf->core_decode = core_decode; + err = vdec_msg_queue_qbuf(&msg_queue->lat_ctx, lat_buf); + if (err) { + mtk_v4l2_err("failed to qbuf buf[%d]", i); + goto mem_alloc_err; + } + } + return 0; + +mem_alloc_err: + vdec_msg_queue_deinit(msg_queue, ctx); + return err; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.h b/drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.h new file mode 100644 index 000000000000..b6ba66d3e026 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_msg_queue.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Yunfei Dong + */ + +#ifndef _VDEC_MSG_QUEUE_H_ +#define _VDEC_MSG_QUEUE_H_ + +#include +#include +#include +#include + +#include "mtk_vcodec_util.h" + +#define NUM_BUFFER_COUNT 3 + +struct vdec_lat_buf; +struct mtk_vcodec_ctx; +struct mtk_vcodec_dev; +typedef int (*core_decode_cb_t)(struct vdec_lat_buf *lat_buf); + +/** + * struct vdec_msg_queue_ctx - represents a queue for buffers ready to be processed + * @ready_to_use: ready used queue used to signalize when get a job queue + * @ready_queue: list of ready lat buffer queues + * @ready_lock: spin lock to protect the lat buffer usage + * @ready_num: number of buffers ready to be processed + * @hardware_index: hardware id that this queue is used for + */ +struct vdec_msg_queue_ctx { + wait_queue_head_t ready_to_use; + struct list_head ready_queue; + /* protect lat buffer */ + spinlock_t ready_lock; + int ready_num; + int hardware_index; +}; + +/** + * struct vdec_lat_buf - lat buffer message used to store lat info for core decode + * @wdma_err_addr: wdma error address used for lat hardware + * @slice_bc_addr: slice bc address used for lat hardware + * @ts_info: need to set timestamp from output to capture + * + * @private_data: shared information used to lat and core hardware + * @ctx: mtk vcodec context information + * @core_decode: different codec use different decode callback function + * @lat_list: add lat buffer to lat head list + * @core_list: add lat buffer to core head list + */ +struct vdec_lat_buf { + struct mtk_vcodec_mem wdma_err_addr; + struct mtk_vcodec_mem slice_bc_addr; + struct vb2_v4l2_buffer ts_info; + + void *private_data; + struct mtk_vcodec_ctx *ctx; + core_decode_cb_t core_decode; + struct list_head lat_list; + struct list_head core_list; +}; + +/** + * struct vdec_msg_queue - used to store lat buffer message + * @lat_buf: lat buffer used to store lat buffer information + * @wdma_addr: wdma address used for ube + * @wdma_rptr_addr: ube read point + * @wdma_wptr_addr: ube write point + * @core_work: core hardware work + * @lat_ctx: used to store lat buffer list + */ +struct vdec_msg_queue { + struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT]; + + struct mtk_vcodec_mem wdma_addr; + u64 wdma_rptr_addr; + u64 wdma_wptr_addr; + + struct work_struct core_work; + struct vdec_msg_queue_ctx lat_ctx; +}; + +/** + * vdec_msg_queue_init - init lat buffer information. + * @msg_queue: used to store the lat buffer information + * @ctx: v4l2 ctx + * @core_decode: core decode callback for each codec + * @private_size: the private data size used to share with core + * + * Return: returns 0 if init successfully, or fail. + */ +int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, + int private_size); + +/** + * vdec_msg_queue_init_ctx - used to init msg queue context information. + * @ctx: message queue context + * @hardware_index: hardware index + */ +void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index); + +/** + * vdec_msg_queue_qbuf - enqueue lat buffer to queue list. + * @ctx: message queue context + * @buf: current lat buffer + * + * Return: returns 0 if qbuf successfully, or fail. + */ +int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *ctx, struct vdec_lat_buf *buf); + +/** + * vdec_msg_queue_dqbuf - dequeue lat buffer from queue list. + * @ctx: message queue context + * + * Return: returns not null if dq successfully, or fail. + */ +struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *ctx); + +/** + * vdec_msg_queue_update_ube_rptr - used to updata the ube read point. + * @msg_queue: used to store the lat buffer information + * @ube_rptr: current ube read point + */ +void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr); + +/** + * vdec_msg_queue_update_ube_wptr - used to updata the ube write point. + * @msg_queue: used to store the lat buffer information + * @ube_wptr: current ube write point + */ +void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr); + +/** + * vdec_msg_queue_wait_lat_buf_full - used to check whether all lat buffer + * in lat list. + * @msg_queue: used to store the lat buffer information + * + * Return: returns true if successfully, or fail. + */ +bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue); + +/** + * vdec_msg_queue_deinit - deinit lat buffer information. + * @msg_queue: used to store the lat buffer information + * @ctx: v4l2 ctx + */ +void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, + struct mtk_vcodec_ctx *ctx); + +#endif diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.c new file mode 100644 index 000000000000..dd35d2c5f920 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + */ + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" +#include "vdec_ipi_msg.h" +#include "vdec_vpu_if.h" +#include "mtk_vcodec_fw.h" + +static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) +{ + struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) + (unsigned long)msg->ap_inst_addr; + + mtk_vcodec_debug(vpu, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr); + + /* mapping VPU address to kernel virtual address */ + /* the content in vsi is initialized to 0 in VPU */ + vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler, + msg->vpu_inst_addr); + vpu->inst_addr = msg->vpu_inst_addr; + + mtk_vcodec_debug(vpu, "- vpu_inst_addr = 0x%x", vpu->inst_addr); + + /* Set default ABI version if dealing with unversioned firmware. */ + vpu->fw_abi_version = 0; + /* + * Instance ID is only used if ABI version >= 2. Initialize it with + * garbage by default. + */ + vpu->inst_id = 0xdeadbeef; + + /* Firmware version field does not exist on MT8173. */ + if (vpu->ctx->dev->vdec_pdata->chip == MTK_MT8173) + return; + + /* Check firmware version. */ + vpu->fw_abi_version = msg->vdec_abi_version; + mtk_vcodec_debug(vpu, "firmware version 0x%x\n", vpu->fw_abi_version); + switch (vpu->fw_abi_version) { + case 1: + break; + case 2: + vpu->inst_id = msg->inst_id; + break; + default: + mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n", + vpu->fw_abi_version); + vpu->failure = 1; + break; + } +} + +/* + * vpu_dec_ipi_handler - Handler for VPU ipi message. + * + * @data: ipi message + * @len : length of ipi message + * @priv: callback private data which is passed by decoder when register. + * + * This function runs in interrupt context and it means there's an IPI MSG + * from VPU. + */ +static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) +{ + const struct vdec_vpu_ipi_ack *msg = data; + struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) + (unsigned long)msg->ap_inst_addr; + + mtk_vcodec_debug(vpu, "+ id=%X", msg->msg_id); + + vpu->failure = msg->status; + vpu->signaled = 1; + + if (msg->status == 0) { + switch (msg->msg_id) { + case VPU_IPIMSG_DEC_INIT_ACK: + handle_init_ack_msg(data); + break; + + case VPU_IPIMSG_DEC_START_ACK: + case VPU_IPIMSG_DEC_END_ACK: + case VPU_IPIMSG_DEC_DEINIT_ACK: + case VPU_IPIMSG_DEC_RESET_ACK: + case VPU_IPIMSG_DEC_CORE_ACK: + case VPU_IPIMSG_DEC_CORE_END_ACK: + break; + + default: + mtk_vcodec_err(vpu, "invalid msg=%X", msg->msg_id); + break; + } + } + + mtk_vcodec_debug(vpu, "- id=%X", msg->msg_id); +} + +static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) +{ + int err, id, msgid; + + msgid = *(uint32_t *)msg; + mtk_vcodec_debug(vpu, "id=%X", msgid); + + vpu->failure = 0; + vpu->signaled = 0; + + if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) { + if (msgid == AP_IPIMSG_DEC_CORE || + msgid == AP_IPIMSG_DEC_CORE_END) + id = vpu->core_id; + else + id = vpu->id; + } else { + id = vpu->id; + } + + err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, id, msg, + len, 2000); + if (err) { + mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d", + id, msgid, err); + return err; + } + + return vpu->failure; +} + +static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id) +{ + struct vdec_ap_ipi_cmd msg; + int err = 0; + + mtk_vcodec_debug(vpu, "+ id=%X", msg_id); + + memset(&msg, 0, sizeof(msg)); + msg.msg_id = msg_id; + if (vpu->fw_abi_version < 2) + msg.vpu_inst_addr = vpu->inst_addr; + else + msg.inst_id = vpu->inst_id; + msg.codec_type = vpu->codec_type; + + err = vcodec_vpu_send_msg(vpu, &msg, sizeof(msg)); + mtk_vcodec_debug(vpu, "- id=%X ret=%d", msg_id, err); + return err; +} + +int vpu_dec_init(struct vdec_vpu_inst *vpu) +{ + struct vdec_ap_ipi_init msg; + int err; + + mtk_vcodec_debug_enter(vpu); + + init_waitqueue_head(&vpu->wq); + vpu->handler = vpu_dec_ipi_handler; + + err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, + vpu->handler, "vdec", NULL); + if (err) { + mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err); + return err; + } + + if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) { + err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, + vpu->core_id, vpu->handler, + "vdec", NULL); + if (err) { + mtk_vcodec_err(vpu, "vpu_ipi_register core fail status=%d", err); + return err; + } + } + + memset(&msg, 0, sizeof(msg)); + msg.msg_id = AP_IPIMSG_DEC_INIT; + msg.ap_inst_addr = (unsigned long)vpu; + msg.codec_type = vpu->codec_type; + + mtk_vcodec_debug(vpu, "vdec_inst=%p", vpu); + + err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); + mtk_vcodec_debug(vpu, "- ret=%d", err); + return err; +} + +int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) +{ + struct vdec_ap_ipi_dec_start msg; + int i; + int err = 0; + + mtk_vcodec_debug_enter(vpu); + + if (len > ARRAY_SIZE(msg.data)) { + mtk_vcodec_err(vpu, "invalid len = %d\n", len); + return -EINVAL; + } + + memset(&msg, 0, sizeof(msg)); + msg.msg_id = AP_IPIMSG_DEC_START; + if (vpu->fw_abi_version < 2) + msg.vpu_inst_addr = vpu->inst_addr; + else + msg.inst_id = vpu->inst_id; + + for (i = 0; i < len; i++) + msg.data[i] = data[i]; + msg.codec_type = vpu->codec_type; + + err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); + mtk_vcodec_debug(vpu, "- ret=%d", err); + return err; +} + +int vpu_dec_core(struct vdec_vpu_inst *vpu) +{ + return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE); +} + +int vpu_dec_end(struct vdec_vpu_inst *vpu) +{ + return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_END); +} + +int vpu_dec_core_end(struct vdec_vpu_inst *vpu) +{ + return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE_END); +} + +int vpu_dec_deinit(struct vdec_vpu_inst *vpu) +{ + return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_DEINIT); +} + +int vpu_dec_reset(struct vdec_vpu_inst *vpu) +{ + return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_RESET); +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.h new file mode 100644 index 000000000000..4cb3c7f5a3ad --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/vdec_vpu_if.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PC Chen + */ + +#ifndef _VDEC_VPU_IF_H_ +#define _VDEC_VPU_IF_H_ + +#include "mtk_vcodec_fw.h" + +struct mtk_vcodec_ctx; + +/** + * struct vdec_vpu_inst - VPU instance for video codec + * @id : ipi msg id for each decoder + * @core_id : core id used to separate different hardware + * @vsi : driver structure allocated by VPU side and shared to AP side + * for control and info share + * @failure : VPU execution result status, 0: success, others: fail + * @inst_addr : VPU decoder instance address + * @fw_abi_version : ABI version of the firmware. + * @inst_id : if fw_abi_version >= 2, contains the instance ID to be given + * in place of inst_addr in messages. + * @signaled : 1 - Host has received ack message from VPU, 0 - not received + * @ctx : context for v4l2 layer integration + * @dev : platform device of VPU + * @wq : wait queue to wait VPU message ack + * @handler : ipi handler for each decoder + * @codec_type : use codec type to separate different codecs + */ +struct vdec_vpu_inst { + int id; + int core_id; + void *vsi; + int32_t failure; + uint32_t inst_addr; + uint32_t fw_abi_version; + uint32_t inst_id; + unsigned int signaled; + struct mtk_vcodec_ctx *ctx; + wait_queue_head_t wq; + mtk_vcodec_ipi_handler handler; + unsigned int codec_type; +}; + +/** + * vpu_dec_init - init decoder instance and allocate required resource in VPU. + * + * @vpu: instance for vdec_vpu_inst + */ +int vpu_dec_init(struct vdec_vpu_inst *vpu); + +/** + * vpu_dec_start - start decoding, basically the function will be invoked once + * every frame. + * + * @vpu : instance for vdec_vpu_inst + * @data: meta data to pass bitstream info to VPU decoder + * @len : meta data length + */ +int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len); + +/** + * vpu_dec_end - end decoding, basically the function will be invoked once + * when HW decoding done interrupt received successfully. The + * decoder in VPU will continue to do reference frame management + * and check if there is a new decoded frame available to display. + * + * @vpu : instance for vdec_vpu_inst + */ +int vpu_dec_end(struct vdec_vpu_inst *vpu); + +/** + * vpu_dec_deinit - deinit decoder instance and resource freed in VPU. + * + * @vpu: instance for vdec_vpu_inst + */ +int vpu_dec_deinit(struct vdec_vpu_inst *vpu); + +/** + * vpu_dec_reset - reset decoder, use for flush decoder when end of stream or + * seek. Remainig non displayed frame will be pushed to display. + * + * @vpu: instance for vdec_vpu_inst + */ +int vpu_dec_reset(struct vdec_vpu_inst *vpu); + +/** + * vpu_dec_core - core start decoding, basically the function will be invoked once + * every frame. + * + * @vpu : instance for vdec_vpu_inst + */ +int vpu_dec_core(struct vdec_vpu_inst *vpu); + +/** + * vpu_dec_core_end - core end decoding, basically the function will be invoked once + * when core HW decoding done and receive interrupt successfully. The + * decoder in VPU will updata hardware information and deinit hardware + * and check if there is a new decoded frame available to display. + * + * @vpu : instance for vdec_vpu_inst + */ +int vpu_dec_core_end(struct vdec_vpu_inst *vpu); + +#endif diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mediatek/mtk-vcodec/venc/venc_h264_if.c new file mode 100644 index 000000000000..4d9b8798dffe --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc/venc_h264_if.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Jungchang Tsao + * Daniel Hsiao + * PoChun Lin + */ + +#include +#include +#include + +#include "../mtk_vcodec_drv.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc.h" +#include "../mtk_vcodec_enc_pm.h" +#include "../venc_drv_base.h" +#include "../venc_ipi_msg.h" +#include "../venc_vpu_if.h" + +static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc}; + +#define H264_FILLER_MARKER_SIZE ARRAY_SIZE(h264_filler_marker) +#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098 + +/* + * enum venc_h264_frame_type - h264 encoder output bitstream frame type + */ +enum venc_h264_frame_type { + VENC_H264_IDR_FRM, + VENC_H264_I_FRM, + VENC_H264_P_FRM, + VENC_H264_B_FRM, +}; + +/* + * enum venc_h264_vpu_work_buf - h264 encoder buffer index + */ +enum venc_h264_vpu_work_buf { + VENC_H264_VPU_WORK_BUF_RC_INFO, + VENC_H264_VPU_WORK_BUF_RC_CODE, + VENC_H264_VPU_WORK_BUF_REC_LUMA, + VENC_H264_VPU_WORK_BUF_REC_CHROMA, + VENC_H264_VPU_WORK_BUF_REF_LUMA, + VENC_H264_VPU_WORK_BUF_REF_CHROMA, + VENC_H264_VPU_WORK_BUF_MV_INFO_1, + VENC_H264_VPU_WORK_BUF_MV_INFO_2, + VENC_H264_VPU_WORK_BUF_SKIP_FRAME, + VENC_H264_VPU_WORK_BUF_MAX, +}; + +/* + * enum venc_h264_bs_mode - for bs_mode argument in h264_enc_vpu_encode + */ +enum venc_h264_bs_mode { + H264_BS_MODE_SPS, + H264_BS_MODE_PPS, + H264_BS_MODE_FRAME, +}; + +/* + * struct venc_h264_vpu_config - Structure for h264 encoder configuration + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @input_fourcc: input fourcc + * @bitrate: target bitrate (in bps) + * @pic_w: picture width. Picture size is visible stream resolution, in pixels, + * to be used for display purposes; must be smaller or equal to buffer + * size. + * @pic_h: picture height + * @buf_w: buffer width. Buffer size is stream resolution in pixels aligned to + * hardware requirements. + * @buf_h: buffer height + * @gop_size: group of picture size (idr frame) + * @intra_period: intra frame period + * @framerate: frame rate in fps + * @profile: as specified in standard + * @level: as specified in standard + * @wfd: WFD mode 1:on, 0:off + */ +struct venc_h264_vpu_config { + u32 input_fourcc; + u32 bitrate; + u32 pic_w; + u32 pic_h; + u32 buf_w; + u32 buf_h; + u32 gop_size; + u32 intra_period; + u32 framerate; + u32 profile; + u32 level; + u32 wfd; +}; + +/* + * struct venc_h264_vpu_buf - Structure for buffer information + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @iova: IO virtual address + * @vpua: VPU side memory addr which is used by RC_CODE + * @size: buffer size (in bytes) + */ +struct venc_h264_vpu_buf { + u32 iova; + u32 vpua; + u32 size; +}; + +/* + * struct venc_h264_vsi - Structure for VPU driver control and info share + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * This structure is allocated in VPU side and shared to AP side. + * @config: h264 encoder configuration + * @work_bufs: working buffer information in VPU side + * The work_bufs here is for storing the 'size' info shared to AP side. + * The similar item in struct venc_h264_inst is for memory allocation + * in AP side. The AP driver will copy the 'size' from here to the one in + * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate + * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for + * register setting in VPU side. + */ +struct venc_h264_vsi { + struct venc_h264_vpu_config config; + struct venc_h264_vpu_buf work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; +}; + +/* + * struct venc_h264_inst - h264 encoder AP driver instance + * @hw_base: h264 encoder hardware register base + * @work_bufs: working buffer + * @pps_buf: buffer to store the pps bitstream + * @work_buf_allocated: working buffer allocated flag + * @frm_cnt: encoded frame count + * @prepend_hdr: when the v4l2 layer send VENC_SET_PARAM_PREPEND_HEADER cmd + * through h264_enc_set_param interface, it will set this flag and prepend the + * sps/pps in h264_enc_encode function. + * @vpu_inst: VPU instance to exchange information between AP and VPU + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @ctx: context for v4l2 layer integration + */ +struct venc_h264_inst { + void __iomem *hw_base; + struct mtk_vcodec_mem work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; + struct mtk_vcodec_mem pps_buf; + bool work_buf_allocated; + unsigned int frm_cnt; + unsigned int skip_frm_cnt; + unsigned int prepend_hdr; + struct venc_vpu_inst vpu_inst; + struct venc_h264_vsi *vsi; + struct mtk_vcodec_ctx *ctx; +}; + +static inline u32 h264_read_reg(struct venc_h264_inst *inst, u32 addr) +{ + return readl(inst->hw_base + addr); +} + +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 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; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: + return 50; + case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: + return 51; + default: + mtk_vcodec_debug(inst, "unsupported level %d", level); + return 31; + } +} + +static void h264_enc_free_work_buf(struct venc_h264_inst *inst) +{ + int i; + + mtk_vcodec_debug_enter(inst); + + /* Except the SKIP_FRAME buffers, + * other buffers need to be freed by AP. + */ + for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { + if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME) + mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); + } + + mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf); + + mtk_vcodec_debug_leave(inst); +} + +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) { + struct mtk_vcodec_fw *handler; + + handler = inst->vpu_inst.ctx->dev->fw_handler; + inst->work_bufs[i].va = + mtk_vcodec_fw_map_dm_addr(handler, 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) { + struct mtk_vcodec_fw *handler; + void *tmp_va; + + handler = inst->vpu_inst.ctx->dev->fw_handler; + tmp_va = mtk_vcodec_fw_map_dm_addr(handler, + 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 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, 0)) { + irq_status = ctx->irq_status; + mtk_vcodec_debug(inst, "irq_status %x <-", irq_status); + } + return irq_status; +} + +static int h264_frame_type(struct venc_h264_inst *inst) +{ + if ((inst->vsi->config.gop_size != 0 && + (inst->frm_cnt % inst->vsi->config.gop_size) == 0) || + (inst->frm_cnt == 0 && inst->vsi->config.gop_size == 0)) { + /* IDR frame */ + return VENC_H264_IDR_FRM; + } else if ((inst->vsi->config.intra_period != 0 && + (inst->frm_cnt % inst->vsi->config.intra_period) == 0) || + (inst->frm_cnt == 0 && inst->vsi->config.intra_period == 0)) { + /* I frame */ + return VENC_H264_I_FRM; + } else { + return VENC_H264_P_FRM; /* Note: B frames are not supported */ + } +} +static int h264_encode_sps(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_SPS, NULL, bs_buf, NULL); + if (ret) + return ret; + + irq_status = h264_enc_wait_venc_done(inst); + if (irq_status != MTK_VENC_IRQ_STATUS_SPS) { + mtk_vcodec_err(inst, "expect irq status %d", + MTK_VENC_IRQ_STATUS_SPS); + 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 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, NULL); + 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 int h264_encode_header(struct venc_h264_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + int ret = 0; + unsigned int bs_size_sps; + unsigned int bs_size_pps; + + ret = h264_encode_sps(inst, bs_buf, &bs_size_sps); + if (ret) + return ret; + + ret = h264_encode_pps(inst, &inst->pps_buf, &bs_size_pps); + if (ret) + return ret; + + memcpy(bs_buf->va + bs_size_sps, inst->pps_buf.va, bs_size_pps); + *bs_size = bs_size_sps + bs_size_pps; + + return ret; +} + +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; + struct venc_frame_info frame_info; + + mtk_vcodec_debug_enter(inst); + mtk_vcodec_debug(inst, "frm_cnt = %d\n ", inst->frm_cnt); + frame_info.frm_count = inst->frm_cnt; + frame_info.skip_frm_count = inst->skip_frm_cnt; + frame_info.frm_type = h264_frame_type(inst); + mtk_vcodec_debug(inst, "frm_count = %d,skip_frm_count =%d,frm_type=%d.\n", + frame_info.frm_count, frame_info.skip_frm_count, + frame_info.frm_type); + ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, bs_buf, &frame_info); + 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; + ++inst->skip_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 void h264_encode_filler(struct venc_h264_inst *inst, void *buf, + int size) +{ + unsigned char *p = buf; + + if (size < H264_FILLER_MARKER_SIZE) { + mtk_vcodec_err(inst, "filler size too small %d", size); + return; + } + + memcpy(p, h264_filler_marker, ARRAY_SIZE(h264_filler_marker)); + size -= H264_FILLER_MARKER_SIZE; + p += H264_FILLER_MARKER_SIZE; + memset(p, 0xff, size); +} + +static int h264_enc_init(struct mtk_vcodec_ctx *ctx) +{ + const bool is_ext = MTK_ENC_CTX_IS_EXT(ctx); + int ret = 0; + struct venc_h264_inst *inst; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + inst->vpu_inst.ctx = ctx; + inst->vpu_inst.id = is_ext ? SCP_IPI_VENC_H264 : IPI_VENC_H264; + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS); + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_init(&inst->vpu_inst); + + inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi; + + mtk_vcodec_debug_leave(inst); + + if (ret) + kfree(inst); + else + ctx->drv_handle = inst; + + return ret; +} + +static int h264_enc_encode(void *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; +} + +static int h264_enc_set_param(void *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; + case VENC_SET_PARAM_FORCE_INTRA: + case VENC_SET_PARAM_GOP_SIZE: + case VENC_SET_PARAM_INTRA_PERIOD: + inst->frm_cnt = 0; + inst->skip_frm_cnt = 0; + fallthrough; + default: + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + break; + } + + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int h264_enc_deinit(void *handle) +{ + int ret = 0; + struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_deinit(&inst->vpu_inst); + + if (inst->work_buf_allocated) + h264_enc_free_work_buf(inst); + + mtk_vcodec_debug_leave(inst); + kfree(inst); + + return ret; +} + +const struct venc_common_if venc_h264_if = { + .init = h264_enc_init, + .encode = h264_enc_encode, + .set_param = h264_enc_set_param, + .deinit = h264_enc_deinit, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mediatek/mtk-vcodec/venc/venc_vp8_if.c new file mode 100644 index 000000000000..56ce58f761f1 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc/venc_vp8_if.c @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * PoChun Lin + */ + +#include +#include +#include + +#include "../mtk_vcodec_drv.h" +#include "../mtk_vcodec_util.h" +#include "../mtk_vcodec_intr.h" +#include "../mtk_vcodec_enc.h" +#include "../mtk_vcodec_enc_pm.h" +#include "../venc_drv_base.h" +#include "../venc_ipi_msg.h" +#include "../venc_vpu_if.h" + +#define VENC_BITSTREAM_FRAME_SIZE 0x0098 +#define VENC_BITSTREAM_HEADER_LEN 0x00e8 + +/* This ac_tag is vp8 frame tag. */ +#define MAX_AC_TAG_SIZE 10 + +/* + * enum venc_vp8_vpu_work_buf - vp8 encoder buffer index + */ +enum venc_vp8_vpu_work_buf { + VENC_VP8_VPU_WORK_BUF_LUMA, + VENC_VP8_VPU_WORK_BUF_LUMA2, + VENC_VP8_VPU_WORK_BUF_LUMA3, + VENC_VP8_VPU_WORK_BUF_CHROMA, + VENC_VP8_VPU_WORK_BUF_CHROMA2, + VENC_VP8_VPU_WORK_BUF_CHROMA3, + VENC_VP8_VPU_WORK_BUF_MV_INFO, + VENC_VP8_VPU_WORK_BUF_BS_HEADER, + VENC_VP8_VPU_WORK_BUF_PROB_BUF, + VENC_VP8_VPU_WORK_BUF_RC_INFO, + VENC_VP8_VPU_WORK_BUF_RC_CODE, + VENC_VP8_VPU_WORK_BUF_RC_CODE2, + VENC_VP8_VPU_WORK_BUF_RC_CODE3, + VENC_VP8_VPU_WORK_BUF_MAX, +}; + +/* + * struct venc_vp8_vpu_config - Structure for vp8 encoder configuration + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @input_fourcc: input fourcc + * @bitrate: target bitrate (in bps) + * @pic_w: picture width. Picture size is visible stream resolution, in pixels, + * to be used for display purposes; must be smaller or equal to buffer + * size. + * @pic_h: picture height + * @buf_w: buffer width (with 16 alignment). Buffer size is stream resolution + * in pixels aligned to hardware requirements. + * @buf_h: buffer height (with 16 alignment) + * @gop_size: group of picture size (key frame) + * @framerate: frame rate in fps + * @ts_mode: temporal scalability mode (0: disable, 1: enable) + * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. + */ +struct venc_vp8_vpu_config { + u32 input_fourcc; + u32 bitrate; + u32 pic_w; + u32 pic_h; + u32 buf_w; + u32 buf_h; + u32 gop_size; + u32 framerate; + u32 ts_mode; +}; + +/* + * struct venc_vp8_vpu_buf - Structure for buffer information + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * @iova: IO virtual address + * @vpua: VPU side memory addr which is used by RC_CODE + * @size: buffer size (in bytes) + */ +struct venc_vp8_vpu_buf { + u32 iova; + u32 vpua; + u32 size; +}; + +/* + * struct venc_vp8_vsi - Structure for VPU driver control and info share + * AP-W/R : AP is writer/reader on this item + * VPU-W/R: VPU is write/reader on this item + * This structure is allocated in VPU side and shared to AP side. + * @config: vp8 encoder configuration + * @work_bufs: working buffer information in VPU side + * The work_bufs here is for storing the 'size' info shared to AP side. + * The similar item in struct venc_vp8_inst is for memory allocation + * in AP side. The AP driver will copy the 'size' from here to the one in + * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate + * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for + * register setting in VPU side. + */ +struct venc_vp8_vsi { + struct venc_vp8_vpu_config config; + struct venc_vp8_vpu_buf work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; +}; + +/* + * struct venc_vp8_inst - vp8 encoder AP driver instance + * @hw_base: vp8 encoder hardware register base + * @work_bufs: working buffer + * @work_buf_allocated: working buffer allocated flag + * @frm_cnt: encoded frame count, it's used for I-frame judgement and + * reset when force intra cmd received. + * @ts_mode: temporal scalability mode (0: disable, 1: enable) + * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. + * @vpu_inst: VPU instance to exchange information between AP and VPU + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @ctx: context for v4l2 layer integration + */ +struct venc_vp8_inst { + void __iomem *hw_base; + struct mtk_vcodec_mem work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; + bool work_buf_allocated; + unsigned int frm_cnt; + unsigned int ts_mode; + struct venc_vpu_inst vpu_inst; + struct venc_vp8_vsi *vsi; + struct mtk_vcodec_ctx *ctx; +}; + +static inline u32 vp8_enc_read_reg(struct venc_vp8_inst *inst, u32 addr) +{ + return readl(inst->hw_base + addr); +} + +static void vp8_enc_free_work_buf(struct venc_vp8_inst *inst) +{ + int i; + + mtk_vcodec_debug_enter(inst); + + /* Buffers need to be freed by AP. */ + for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { + if (inst->work_bufs[i].size == 0) + continue; + mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); + } + + mtk_vcodec_debug_leave(inst); +} + +static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) +{ + int i; + int ret = 0; + struct venc_vp8_vpu_buf *wb = inst->vsi->work_bufs; + + mtk_vcodec_debug_enter(inst); + + for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { + if (wb[i].size == 0) + continue; + /* + * 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. For the + * RC_CODEx buffers, they are pre-allocated in the VPU side + * because they are inside VPU SRAM, 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. + */ + inst->work_bufs[i].size = wb[i].size; + ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]); + if (ret) { + mtk_vcodec_err(inst, + "cannot alloc work_bufs[%d]", i); + goto err_alloc; + } + /* + * This RC_CODEx is pre-allocated by VPU and saved in VPU addr. + * So we need use memcpy to copy RC_CODEx from VPU addr into IO + * virtual addr in 'iova' field for reg setting in VPU side. + */ + if (i == VENC_VP8_VPU_WORK_BUF_RC_CODE || + i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 || + i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) { + struct mtk_vcodec_fw *handler; + void *tmp_va; + + handler = inst->vpu_inst.ctx->dev->fw_handler; + tmp_va = mtk_vcodec_fw_map_dm_addr(handler, + 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_bufs[%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); + } + + mtk_vcodec_debug_leave(inst); + + return ret; + +err_alloc: + vp8_enc_free_work_buf(inst); + + return ret; +} + +static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_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, 0)) { + irq_status = ctx->irq_status; + mtk_vcodec_debug(inst, "isr return %x", irq_status); + } + return irq_status; +} + +/* + * Compose ac_tag, bitstream header and bitstream payload into + * one bitstream buffer. + */ +static int vp8_enc_compose_one_frame(struct venc_vp8_inst *inst, + struct mtk_vcodec_mem *bs_buf, + unsigned int *bs_size) +{ + unsigned int not_key; + u32 bs_frm_size; + u32 bs_hdr_len; + unsigned int ac_tag_size; + u8 ac_tag[MAX_AC_TAG_SIZE]; + u32 tag; + + bs_frm_size = vp8_enc_read_reg(inst, VENC_BITSTREAM_FRAME_SIZE); + bs_hdr_len = vp8_enc_read_reg(inst, VENC_BITSTREAM_HEADER_LEN); + + /* if a frame is key frame, not_key is 0 */ + not_key = !inst->vpu_inst.is_key_frm; + tag = (bs_hdr_len << 5) | 0x10 | not_key; + ac_tag[0] = tag & 0xff; + ac_tag[1] = (tag >> 8) & 0xff; + ac_tag[2] = (tag >> 16) & 0xff; + + /* key frame */ + if (not_key == 0) { + ac_tag_size = MAX_AC_TAG_SIZE; + ac_tag[3] = 0x9d; + ac_tag[4] = 0x01; + ac_tag[5] = 0x2a; + ac_tag[6] = inst->vsi->config.pic_w; + ac_tag[7] = inst->vsi->config.pic_w >> 8; + ac_tag[8] = inst->vsi->config.pic_h; + ac_tag[9] = inst->vsi->config.pic_h >> 8; + } else { + ac_tag_size = 3; + } + + if (bs_buf->size < bs_hdr_len + bs_frm_size + ac_tag_size) { + mtk_vcodec_err(inst, "bitstream buf size is too small(%zu)", + bs_buf->size); + return -EINVAL; + } + + /* + * (1) The vp8 bitstream header and body are generated by the HW vp8 + * encoder separately at the same time. We cannot know the bitstream + * header length in advance. + * (2) From the vp8 spec, there is no stuffing byte allowed between the + * ac tag, bitstream header and bitstream body. + */ + memmove(bs_buf->va + bs_hdr_len + ac_tag_size, + bs_buf->va, bs_frm_size); + memcpy(bs_buf->va + ac_tag_size, + inst->work_bufs[VENC_VP8_VPU_WORK_BUF_BS_HEADER].va, + bs_hdr_len); + memcpy(bs_buf->va, ac_tag, ac_tag_size); + *bs_size = bs_frm_size + bs_hdr_len + ac_tag_size; + + return 0; +} + +static int vp8_enc_encode_frame(struct venc_vp8_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(inst, "->frm_cnt=%d", inst->frm_cnt); + + ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, NULL); + if (ret) + return ret; + + irq_status = vp8_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; + } + + if (vp8_enc_compose_one_frame(inst, bs_buf, bs_size)) { + mtk_vcodec_err(inst, "vp8_enc_compose_one_frame failed"); + return -EINVAL; + } + + inst->frm_cnt++; + mtk_vcodec_debug(inst, "<-size=%d key_frm=%d", *bs_size, + inst->vpu_inst.is_key_frm); + + return ret; +} + +static int vp8_enc_init(struct mtk_vcodec_ctx *ctx) +{ + int ret = 0; + struct venc_vp8_inst *inst; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + inst->vpu_inst.ctx = ctx; + inst->vpu_inst.id = IPI_VENC_VP8; + inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS); + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_init(&inst->vpu_inst); + + inst->vsi = (struct venc_vp8_vsi *)inst->vpu_inst.vsi; + + mtk_vcodec_debug_leave(inst); + + if (ret) + kfree(inst); + else + ctx->drv_handle = inst; + + return ret; +} + +static int vp8_enc_encode(void *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_vp8_inst *inst = (struct venc_vp8_inst *)handle; + struct mtk_vcodec_ctx *ctx = inst->ctx; + + mtk_vcodec_debug_enter(inst); + + enable_irq(ctx->dev->enc_irq); + + switch (opt) { + case VENC_START_OPT_ENCODE_FRAME: + ret = vp8_enc_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; + + default: + mtk_vcodec_err(inst, "opt not support:%d", opt); + ret = -EINVAL; + break; + } + +encode_err: + + disable_irq(ctx->dev->enc_irq); + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int vp8_enc_set_param(void *handle, + enum venc_set_param_type type, + struct venc_enc_param *enc_prm) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_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.ts_mode = inst->ts_mode; + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + if (ret) + break; + if (inst->work_buf_allocated) { + vp8_enc_free_work_buf(inst); + inst->work_buf_allocated = false; + } + ret = vp8_enc_alloc_work_buf(inst); + if (ret) + break; + inst->work_buf_allocated = true; + break; + + /* + * VENC_SET_PARAM_TS_MODE must be called before VENC_SET_PARAM_ENC + */ + case VENC_SET_PARAM_TS_MODE: + inst->ts_mode = 1; + mtk_vcodec_debug(inst, "set ts_mode"); + break; + + default: + ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); + break; + } + + mtk_vcodec_debug_leave(inst); + + return ret; +} + +static int vp8_enc_deinit(void *handle) +{ + int ret = 0; + struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; + + mtk_vcodec_debug_enter(inst); + + ret = vpu_enc_deinit(&inst->vpu_inst); + + if (inst->work_buf_allocated) + vp8_enc_free_work_buf(inst); + + mtk_vcodec_debug_leave(inst); + kfree(inst); + + return ret; +} + +const struct venc_common_if venc_vp8_if = { + .init = vp8_enc_init, + .encode = vp8_enc_encode, + .set_param = vp8_enc_set_param, + .deinit = vp8_enc_deinit, +}; diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_base.h new file mode 100644 index 000000000000..3d718411dc73 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_base.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * Jungchang Tsao + * Tiffany Lin + */ + +#ifndef _VENC_DRV_BASE_ +#define _VENC_DRV_BASE_ + +#include "mtk_vcodec_drv.h" + +#include "venc_drv_if.h" + +struct venc_common_if { + /** + * (*init)() - initialize driver + * @ctx: [in] mtk v4l2 context + * @handle: [out] driver handle + */ + int (*init)(struct mtk_vcodec_ctx *ctx); + + /** + * (*encode)() - trigger encode + * @handle: [in] driver handle + * @opt: [in] encode option + * @frm_buf: [in] frame buffer to store input frame + * @bs_buf: [in] bitstream buffer to store output bitstream + * @result: [out] encode result + */ + int (*encode)(void *handle, enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result); + + /** + * (*set_param)() - set driver's parameter + * @handle: [in] driver handle + * @type: [in] parameter type + * @in: [in] buffer to store the parameter + */ + int (*set_param)(void *handle, enum venc_set_param_type type, + struct venc_enc_param *in); + + /** + * (*deinit)() - deinitialize driver. + * @handle: [in] driver handle + */ + int (*deinit)(void *handle); +}; + +#endif diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.c new file mode 100644 index 000000000000..ce0bce811615 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * Jungchang Tsao + * Tiffany Lin + */ + +#include +#include +#include + +#include "venc_drv_base.h" +#include "venc_drv_if.h" + +#include "mtk_vcodec_enc.h" +#include "mtk_vcodec_enc_pm.h" + +int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) +{ + int ret = 0; + + switch (fourcc) { + case V4L2_PIX_FMT_VP8: + ctx->enc_if = &venc_vp8_if; + break; + case V4L2_PIX_FMT_H264: + ctx->enc_if = &venc_h264_if; + break; + default: + return -EINVAL; + } + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->init(ctx); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + return ret; +} + +int venc_if_set_param(struct mtk_vcodec_ctx *ctx, + enum venc_set_param_type type, struct venc_enc_param *in) +{ + int ret = 0; + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->set_param(ctx->drv_handle, type, in); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + return ret; +} + +int venc_if_encode(struct mtk_vcodec_ctx *ctx, + 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; + unsigned long flags; + + mtk_venc_lock(ctx); + + spin_lock_irqsave(&ctx->dev->irqlock, flags); + ctx->dev->curr_ctx = ctx; + spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf, + bs_buf, result); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + + spin_lock_irqsave(&ctx->dev->irqlock, flags); + ctx->dev->curr_ctx = NULL; + spin_unlock_irqrestore(&ctx->dev->irqlock, flags); + + mtk_venc_unlock(ctx); + return ret; +} + +int venc_if_deinit(struct mtk_vcodec_ctx *ctx) +{ + int ret = 0; + + if (!ctx->drv_handle) + return 0; + + mtk_venc_lock(ctx); + mtk_vcodec_enc_clock_on(&ctx->dev->pm); + ret = ctx->enc_if->deinit(ctx->drv_handle); + mtk_vcodec_enc_clock_off(&ctx->dev->pm); + mtk_venc_unlock(ctx); + + ctx->drv_handle = NULL; + + return ret; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.h new file mode 100644 index 000000000000..0b04a1020873 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc_drv_if.h @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Daniel Hsiao + * Jungchang Tsao + * Tiffany Lin + */ + +#ifndef _VENC_DRV_IF_H_ +#define _VENC_DRV_IF_H_ + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" + +/* + * enum venc_yuv_fmt - The type of input yuv format + * (VPU related: If you change the order, you must also update the VPU codes.) + * @VENC_YUV_FORMAT_I420: I420 YUV format + * @VENC_YUV_FORMAT_YV12: YV12 YUV format + * @VENC_YUV_FORMAT_NV12: NV12 YUV format + * @VENC_YUV_FORMAT_NV21: NV21 YUV format + */ +enum venc_yuv_fmt { + VENC_YUV_FORMAT_I420 = 3, + VENC_YUV_FORMAT_YV12 = 5, + VENC_YUV_FORMAT_NV12 = 6, + VENC_YUV_FORMAT_NV21 = 7, +}; + +/* + * enum venc_start_opt - encode frame option used in venc_if_encode() + * @VENC_START_OPT_ENCODE_SEQUENCE_HEADER: encode SPS/PPS for H264 + * @VENC_START_OPT_ENCODE_FRAME: encode normal frame + */ +enum venc_start_opt { + VENC_START_OPT_ENCODE_SEQUENCE_HEADER, + VENC_START_OPT_ENCODE_FRAME, +}; + +/* + * enum venc_set_param_type - The type of set parameter used in + * venc_if_set_param() + * (VPU related: If you change the order, you must also update the VPU codes.) + * @VENC_SET_PARAM_ENC: set encoder parameters + * @VENC_SET_PARAM_FORCE_INTRA: force an intra frame + * @VENC_SET_PARAM_ADJUST_BITRATE: adjust bitrate (in bps) + * @VENC_SET_PARAM_ADJUST_FRAMERATE: set frame rate + * @VENC_SET_PARAM_GOP_SIZE: set IDR interval + * @VENC_SET_PARAM_INTRA_PERIOD: set I frame interval + * @VENC_SET_PARAM_SKIP_FRAME: set H264 skip one frame + * @VENC_SET_PARAM_PREPEND_HEADER: set H264 prepend SPS/PPS before IDR + * @VENC_SET_PARAM_TS_MODE: set VP8 temporal scalability mode + */ +enum venc_set_param_type { + VENC_SET_PARAM_ENC, + VENC_SET_PARAM_FORCE_INTRA, + VENC_SET_PARAM_ADJUST_BITRATE, + VENC_SET_PARAM_ADJUST_FRAMERATE, + VENC_SET_PARAM_GOP_SIZE, + VENC_SET_PARAM_INTRA_PERIOD, + VENC_SET_PARAM_SKIP_FRAME, + VENC_SET_PARAM_PREPEND_HEADER, + VENC_SET_PARAM_TS_MODE, +}; + +/* + * struct venc_enc_prm - encoder settings for VENC_SET_PARAM_ENC used in + * venc_if_set_param() + * @input_fourcc: input yuv format + * @h264_profile: V4L2 defined H.264 profile + * @h264_level: V4L2 defined H.264 level + * @width: image width + * @height: image height + * @buf_width: buffer width + * @buf_height: buffer height + * @frm_rate: frame rate in fps + * @intra_period: intra frame period + * @bitrate: target bitrate in bps + * @gop_size: group of picture size + */ +struct venc_enc_param { + enum venc_yuv_fmt input_yuv_fmt; + unsigned int h264_profile; + unsigned int h264_level; + unsigned int width; + unsigned int height; + unsigned int buf_width; + unsigned int buf_height; + unsigned int frm_rate; + unsigned int intra_period; + unsigned int bitrate; + unsigned int gop_size; +}; + +/** + * struct venc_frame_info - per-frame information to pass to the firmware. + * + * @frm_count: sequential number for this frame + * @skip_frm_count: number of frames skipped so far while decoding + * @frm_type: type of the frame, from enum venc_h264_frame_type + */ +struct venc_frame_info { + unsigned int frm_count; /* per frame update */ + unsigned int skip_frm_count; /* per frame update */ + unsigned int frm_type; /* per frame update */ +}; + +/* + * struct venc_frm_buf - frame buffer information used in venc_if_encode() + * @fb_addr: plane frame buffer addresses + */ +struct venc_frm_buf { + struct mtk_vcodec_fb fb_addr[MTK_VCODEC_MAX_PLANES]; +}; + +/* + * struct venc_done_result - This is return information used in venc_if_encode() + * @bs_size: output bitstream size + * @is_key_frm: output is key frame or not + */ +struct venc_done_result { + unsigned int bs_size; + bool is_key_frm; +}; + +extern const struct venc_common_if venc_h264_if; +extern const struct venc_common_if venc_vp8_if; + +/* + * venc_if_init - Create the driver handle + * @ctx: device context + * @fourcc: encoder input format + * Return: 0 if creating handle successfully, otherwise it is failed. + */ +int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); + +/* + * venc_if_deinit - Release the driver handle + * @ctx: device context + * Return: 0 if releasing handle successfully, otherwise it is failed. + */ +int venc_if_deinit(struct mtk_vcodec_ctx *ctx); + +/* + * venc_if_set_param - Set parameter to driver + * @ctx: device context + * @type: parameter type + * @in: input parameter + * Return: 0 if setting param successfully, otherwise it is failed. + */ +int venc_if_set_param(struct mtk_vcodec_ctx *ctx, + enum venc_set_param_type type, + struct venc_enc_param *in); + +/* + * venc_if_encode - Encode one frame + * @ctx: device context + * @opt: encode frame option + * @frm_buf: input frame buffer information + * @bs_buf: output bitstream buffer infomraiton + * @result: encode result + * Return: 0 if encoding frame successfully, otherwise it is failed. + */ +int venc_if_encode(struct mtk_vcodec_ctx *ctx, + enum venc_start_opt opt, + struct venc_frm_buf *frm_buf, + struct mtk_vcodec_mem *bs_buf, + struct venc_done_result *result); + +#endif /* _VENC_DRV_IF_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mediatek/mtk-vcodec/venc_ipi_msg.h new file mode 100644 index 000000000000..587a2cf15b76 --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc_ipi_msg.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Jungchang Tsao + * Daniel Hsiao + * Tiffany Lin + */ + +#ifndef _VENC_IPI_MSG_H_ +#define _VENC_IPI_MSG_H_ + +#define AP_IPIMSG_VENC_BASE 0xC000 +#define VPU_IPIMSG_VENC_BASE 0xD000 + +/* + * enum venc_ipi_msg_id - message id between AP and VPU + * (ipi stands for inter-processor interrupt) + * @AP_IPIMSG_ENC_XXX: AP to VPU cmd message id + * @VPU_IPIMSG_ENC_XXX_DONE: VPU ack AP cmd message id + */ +enum venc_ipi_msg_id { + AP_IPIMSG_ENC_INIT = AP_IPIMSG_VENC_BASE, + AP_IPIMSG_ENC_SET_PARAM, + AP_IPIMSG_ENC_ENCODE, + AP_IPIMSG_ENC_DEINIT, + + VPU_IPIMSG_ENC_INIT_DONE = VPU_IPIMSG_VENC_BASE, + VPU_IPIMSG_ENC_SET_PARAM_DONE, + VPU_IPIMSG_ENC_ENCODE_DONE, + VPU_IPIMSG_ENC_DEINIT_DONE, +}; + +/** + * struct venc_ap_ipi_msg_init - AP to VPU init cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_INIT) + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + * @venc_inst: AP encoder instance + * (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_ap_ipi_msg_init { + uint32_t msg_id; + uint32_t reserved; + uint64_t venc_inst; +}; + +/** + * struct venc_ap_ipi_msg_set_param - AP to VPU set_param cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_SET_PARAM) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @param_id: parameter id (venc_set_param_type) + * @data_item: number of items in the data array + * @data: data array to store the set parameters + */ +struct venc_ap_ipi_msg_set_param { + uint32_t msg_id; + uint32_t vpu_inst_addr; + uint32_t param_id; + uint32_t data_item; + uint32_t data[8]; +}; + +struct venc_ap_ipi_msg_set_param_ext { + struct venc_ap_ipi_msg_set_param base; + uint32_t data_ext[24]; +}; + +/** + * struct venc_ap_ipi_msg_enc - AP to VPU enc cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_ENCODE) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @bs_mode: bitstream mode for h264 + * (H264_BS_MODE_SPS/H264_BS_MODE_PPS/H264_BS_MODE_FRAME) + * @input_addr: pointer to input image buffer plane + * @bs_addr: pointer to output bit stream buffer + * @bs_size: bit stream buffer size + */ +struct venc_ap_ipi_msg_enc { + uint32_t msg_id; + uint32_t vpu_inst_addr; + uint32_t bs_mode; + uint32_t input_addr[3]; + uint32_t bs_addr; + uint32_t bs_size; +}; + +/** + * struct venc_ap_ipi_msg_enc_ext - AP to SCP extended enc cmd structure + * + * @base: base msg structure + * @data_item: number of items in the data array + * @data: data array to store the set parameters + */ +struct venc_ap_ipi_msg_enc_ext { + struct venc_ap_ipi_msg_enc base; + uint32_t data_item; + uint32_t data[32]; +}; + +/** + * struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure + * @msg_id: message id (AP_IPIMSG_XXX_ENC_DEINIT) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + */ +struct venc_ap_ipi_msg_deinit { + uint32_t msg_id; + uint32_t vpu_inst_addr; +}; + +/* + * enum venc_ipi_msg_status - VPU ack AP cmd status + */ +enum venc_ipi_msg_status { + VENC_IPI_MSG_STATUS_OK, + VENC_IPI_MSG_STATUS_FAIL, +}; + +/** + * struct venc_vpu_ipi_msg_common - VPU ack AP cmd common structure + * @msg_id: message id (VPU_IPIMSG_XXX_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_vpu_ipi_msg_common { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; +}; + +/** + * struct venc_vpu_ipi_msg_init - VPU ack AP init cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @vpu_inst_addr: VPU encoder instance addr + * (struct venc_vp8_vsi/venc_h264_vsi *) + * @venc_abi_version: ABI version of the firmware. Kernel can use it to + * ensure that it is compatible with the firmware. + * For MT8173 the value of this field is undefined and + * should not be used. + */ +struct venc_vpu_ipi_msg_init { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t vpu_inst_addr; + uint32_t venc_abi_version; +}; + +/** + * struct venc_vpu_ipi_msg_set_param - VPU ack AP set_param cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @param_id: parameter id (venc_set_param_type) + * @data_item: number of items in the data array + * @data: data array to store the return result + */ +struct venc_vpu_ipi_msg_set_param { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t param_id; + uint32_t data_item; + uint32_t data[6]; +}; + +/** + * enum venc_ipi_msg_enc_state - Type of encode state + * @VEN_IPI_MSG_ENC_STATE_FRAME: one frame being encoded + * @VEN_IPI_MSG_ENC_STATE_PART: bit stream buffer full + * @VEN_IPI_MSG_ENC_STATE_SKIP: encoded skip frame + * @VEN_IPI_MSG_ENC_STATE_ERROR: encounter error + */ +enum venc_ipi_msg_enc_state { + VEN_IPI_MSG_ENC_STATE_FRAME, + VEN_IPI_MSG_ENC_STATE_PART, + VEN_IPI_MSG_ENC_STATE_SKIP, + VEN_IPI_MSG_ENC_STATE_ERROR, +}; + +/** + * struct venc_vpu_ipi_msg_enc - VPU ack AP enc cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_ENCODE_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + * @state: encode state (venc_ipi_msg_enc_state) + * @is_key_frm: whether the encoded frame is key frame + * @bs_size: encoded bitstream size + * @reserved: reserved for future use. vpu is running in 32bit. Without + * this reserved field, if kernel run in 64bit. this struct size + * will be different between kernel and vpu + */ +struct venc_vpu_ipi_msg_enc { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; + uint32_t state; + uint32_t is_key_frm; + uint32_t bs_size; + uint32_t reserved; +}; + +/** + * struct venc_vpu_ipi_msg_deinit - VPU ack AP deinit cmd structure + * @msg_id: message id (VPU_IPIMSG_XXX_ENC_DEINIT_DONE) + * @status: cmd status (venc_ipi_msg_status) + * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) + */ +struct venc_vpu_ipi_msg_deinit { + uint32_t msg_id; + uint32_t status; + uint64_t venc_inst; +}; + +#endif /* _VENC_IPI_MSG_H_ */ diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.c new file mode 100644 index 000000000000..e7899d8a3e4e --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PoChun Lin + */ + +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_fw.h" +#include "venc_ipi_msg.h" +#include "venc_vpu_if.h" + +static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data) +{ + const struct venc_vpu_ipi_msg_init *msg = data; + + vpu->inst_addr = msg->vpu_inst_addr; + vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler, + msg->vpu_inst_addr); + + /* Firmware version field value is unspecified on MT8173. */ + if (vpu->ctx->dev->venc_pdata->chip == MTK_MT8173) + return; + + /* Check firmware version. */ + mtk_vcodec_debug(vpu, "firmware version: 0x%x\n", + msg->venc_abi_version); + switch (msg->venc_abi_version) { + case 1: + break; + default: + mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n", + msg->venc_abi_version); + vpu->failure = 1; + break; + } +} + +static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data) +{ + const struct venc_vpu_ipi_msg_enc *msg = data; + + vpu->state = msg->state; + vpu->bs_size = msg->bs_size; + vpu->is_key_frm = msg->is_key_frm; +} + +static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) +{ + const 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); + + vpu->signaled = 1; + vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); + if (vpu->failure) + goto failure; + + 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; + } + +failure: + mtk_vcodec_debug_leave(vpu); +} + +static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, + int len) +{ + int status; + + mtk_vcodec_debug_enter(vpu); + + if (!vpu->ctx->dev->fw_handler) { + mtk_vcodec_err(vpu, "inst dev is NULL"); + return -EINVAL; + } + + status = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg, + len, 2000); + if (status) { + mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d", + *(uint32_t *)msg, len, status); + return -EINVAL; + } + if (vpu->failure) + return -EINVAL; + + mtk_vcodec_debug_leave(vpu); + + return 0; +} + +int vpu_enc_init(struct venc_vpu_inst *vpu) +{ + int status; + struct venc_ap_ipi_msg_init out; + + mtk_vcodec_debug_enter(vpu); + + init_waitqueue_head(&vpu->wq_hd); + vpu->signaled = 0; + vpu->failure = 0; + + status = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, + vpu_enc_ipi_handler, "venc", NULL); + + if (status) { + mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status); + return -EINVAL; + } + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_INIT; + out.venc_inst = (unsigned long)vpu; + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_INIT fail"); + return -EINVAL; + } + + mtk_vcodec_debug_leave(vpu); + + return 0; +} + +static unsigned int venc_enc_param_crop_right(struct venc_vpu_inst *vpu, + struct venc_enc_param *enc_prm) +{ + unsigned int img_crop_right = enc_prm->buf_width - enc_prm->width; + + return img_crop_right % 16; +} + +static unsigned int venc_enc_param_crop_bottom(struct venc_enc_param *enc_prm) +{ + return round_up(enc_prm->height, 16) - enc_prm->height; +} + +static unsigned int venc_enc_param_num_mb(struct venc_enc_param *enc_prm) +{ + return DIV_ROUND_UP(enc_prm->width, 16) * + DIV_ROUND_UP(enc_prm->height, 16); +} + +int vpu_enc_set_param(struct venc_vpu_inst *vpu, + enum venc_set_param_type id, + struct venc_enc_param *enc_param) +{ + const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx); + size_t msg_size = is_ext ? + sizeof(struct venc_ap_ipi_msg_set_param_ext) : + sizeof(struct venc_ap_ipi_msg_set_param); + struct venc_ap_ipi_msg_set_param_ext out; + + mtk_vcodec_debug(vpu, "id %d ->", id); + + memset(&out, 0, sizeof(out)); + out.base.msg_id = AP_IPIMSG_ENC_SET_PARAM; + out.base.vpu_inst_addr = vpu->inst_addr; + out.base.param_id = id; + switch (id) { + case VENC_SET_PARAM_ENC: + if (is_ext) { + out.base.data_item = 3; + out.base.data[0] = + venc_enc_param_crop_right(vpu, enc_param); + out.base.data[1] = + venc_enc_param_crop_bottom(enc_param); + out.base.data[2] = venc_enc_param_num_mb(enc_param); + } else { + out.base.data_item = 0; + } + break; + case VENC_SET_PARAM_FORCE_INTRA: + out.base.data_item = 0; + break; + case VENC_SET_PARAM_ADJUST_BITRATE: + out.base.data_item = 1; + out.base.data[0] = enc_param->bitrate; + break; + case VENC_SET_PARAM_ADJUST_FRAMERATE: + out.base.data_item = 1; + out.base.data[0] = enc_param->frm_rate; + break; + case VENC_SET_PARAM_GOP_SIZE: + out.base.data_item = 1; + out.base.data[0] = enc_param->gop_size; + break; + case VENC_SET_PARAM_INTRA_PERIOD: + out.base.data_item = 1; + out.base.data[0] = enc_param->intra_period; + break; + case VENC_SET_PARAM_SKIP_FRAME: + out.base.data_item = 0; + break; + default: + mtk_vcodec_err(vpu, "id %d not supported", id); + return -EINVAL; + } + if (vpu_enc_send_msg(vpu, &out, msg_size)) { + mtk_vcodec_err(vpu, + "AP_IPIMSG_ENC_SET_PARAM %d fail", id); + return -EINVAL; + } + + mtk_vcodec_debug(vpu, "id %d <-", id); + + return 0; +} + +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, + struct venc_frame_info *frame_info) +{ + const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx); + size_t msg_size = is_ext ? + sizeof(struct venc_ap_ipi_msg_enc_ext) : + sizeof(struct venc_ap_ipi_msg_enc); + struct venc_ap_ipi_msg_enc_ext out; + + mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); + + memset(&out, 0, sizeof(out)); + out.base.msg_id = AP_IPIMSG_ENC_ENCODE; + out.base.vpu_inst_addr = vpu->inst_addr; + out.base.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.base.input_addr[0] = frm_buf->fb_addr[0].dma_addr; + out.base.input_addr[1] = frm_buf->fb_addr[1].dma_addr; + out.base.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.base.bs_addr = bs_buf->dma_addr; + out.base.bs_size = bs_buf->size; + } + if (is_ext && frame_info) { + out.data_item = 3; + out.data[0] = frame_info->frm_count; + out.data[1] = frame_info->skip_frm_count; + out.data[2] = frame_info->frm_type; + } + if (vpu_enc_send_msg(vpu, &out, msg_size)) { + 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; +} + +int vpu_enc_deinit(struct venc_vpu_inst *vpu) +{ + struct venc_ap_ipi_msg_deinit out; + + mtk_vcodec_debug_enter(vpu); + + memset(&out, 0, sizeof(out)); + out.msg_id = AP_IPIMSG_ENC_DEINIT; + out.vpu_inst_addr = vpu->inst_addr; + if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { + mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_DEINIT fail"); + return -EINVAL; + } + + mtk_vcodec_debug_leave(vpu); + + return 0; +} diff --git a/drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.h new file mode 100644 index 000000000000..f83bc1b3f2bf --- /dev/null +++ b/drivers/media/platform/mediatek/mtk-vcodec/venc_vpu_if.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: PoChun Lin + */ + +#ifndef _VENC_VPU_IF_H_ +#define _VENC_VPU_IF_H_ + +#include "mtk_vcodec_fw.h" +#include "venc_drv_if.h" + +/* + * struct venc_vpu_inst - encoder VPU driver instance + * @wq_hd: wait queue used for vpu cmd trigger then wait vpu interrupt done + * @signaled: flag used for checking vpu interrupt done + * @failure: flag to show vpu cmd succeeds or not + * @state: enum venc_ipi_msg_enc_state + * @bs_size: bitstream size for skip frame case usage + * @is_key_frm: key frame flag + * @inst_addr: VPU instance addr + * @vsi: driver structure allocated by VPU side and shared to AP side for + * control and info share + * @id: the id of inter-processor interrupt + * @ctx: context for v4l2 layer integration + * @dev: device for v4l2 layer integration + */ +struct venc_vpu_inst { + wait_queue_head_t wq_hd; + int signaled; + int failure; + int state; + int bs_size; + int is_key_frm; + unsigned int inst_addr; + void *vsi; + int id; + struct mtk_vcodec_ctx *ctx; +}; + +int vpu_enc_init(struct venc_vpu_inst *vpu); +int vpu_enc_set_param(struct venc_vpu_inst *vpu, + enum venc_set_param_type id, + struct venc_enc_param *param); +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, + struct venc_frame_info *frame_info); +int vpu_enc_deinit(struct venc_vpu_inst *vpu); + +#endif diff --git a/drivers/media/platform/mtk-vcodec/Kconfig b/drivers/media/platform/mtk-vcodec/Kconfig deleted file mode 100644 index 635801a19d55..000000000000 --- a/drivers/media/platform/mtk-vcodec/Kconfig +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_MEDIATEK_VCODEC_SCP - bool - -config VIDEO_MEDIATEK_VCODEC_VPU - bool - -config VIDEO_MEDIATEK_VCODEC - tristate "Mediatek Video Codec driver" - depends on V4L_MEM2MEM_DRIVERS - depends on MTK_IOMMU || COMPILE_TEST - depends on VIDEO_DEV && VIDEO_V4L2 - depends on ARCH_MEDIATEK || COMPILE_TEST - depends on VIDEO_MEDIATEK_VPU || MTK_SCP - # The two following lines ensure we have the same state ("m" or "y") as - # our dependencies, to avoid missing symbols during link. - depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU - depends on MTK_SCP || !MTK_SCP - depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n) - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU - select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP - select V4L2_H264 - select MEDIA_CONTROLLER - select MEDIA_CONTROLLER_REQUEST_API - help - Mediatek video codec driver provides HW capability to - encode and decode in a range of video formats on MT8173 - and MT8183. - - Note that support for MT8173 requires VIDEO_MEDIATEK_VPU to - also be selected. Support for MT8183 depends on MTK_SCP. - - To compile this driver as modules, choose M here: the - modules will be called mtk-vcodec-dec and mtk-vcodec-enc. diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mtk-vcodec/Makefile deleted file mode 100644 index 359619653a0e..000000000000 --- a/drivers/media/platform/mtk-vcodec/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dec.o \ - mtk-vcodec-enc.o \ - mtk-vcodec-common.o \ - mtk-vcodec-dec-hw.o - -mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ - vdec/vdec_vp8_if.o \ - vdec/vdec_vp9_if.o \ - vdec/vdec_h264_req_if.o \ - mtk_vcodec_dec_drv.o \ - vdec_drv_if.o \ - vdec_vpu_if.o \ - vdec_msg_queue.o \ - mtk_vcodec_dec.o \ - mtk_vcodec_dec_stateful.o \ - mtk_vcodec_dec_stateless.o \ - mtk_vcodec_dec_pm.o \ - -mtk-vcodec-dec-hw-y := mtk_vcodec_dec_hw.o - -mtk-vcodec-enc-y := venc/venc_vp8_if.o \ - venc/venc_h264_if.o \ - mtk_vcodec_enc.o \ - mtk_vcodec_enc_drv.o \ - mtk_vcodec_enc_pm.o \ - venc_drv_if.o \ - venc_vpu_if.o \ - - -mtk-vcodec-common-y := mtk_vcodec_intr.o \ - mtk_vcodec_util.o \ - mtk_vcodec_fw.o \ - -ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU),) -mtk-vcodec-common-y += mtk_vcodec_fw_vpu.o -endif - -ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP),) -mtk-vcodec-common-y += mtk_vcodec_fw_scp.o -endif diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c deleted file mode 100644 index 130ecef2e766..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c +++ /dev/null @@ -1,961 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - * Tiffany Lin - */ - -#include -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_dec.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "vdec_drv_if.h" -#include "mtk_vcodec_dec_pm.h" - -#define DFT_CFG_WIDTH MTK_VDEC_MIN_W -#define DFT_CFG_HEIGHT MTK_VDEC_MIN_H - -static const struct mtk_video_fmt * -mtk_vdec_find_format(struct v4l2_format *f, - const struct mtk_vcodec_dec_pdata *dec_pdata) -{ - const struct mtk_video_fmt *fmt; - unsigned int k; - - for (k = 0; k < dec_pdata->num_formats; k++) { - fmt = &dec_pdata->vdec_formats[k]; - if (fmt->fourcc == f->fmt.pix_mp.pixelformat) - return fmt; - } - - return NULL; -} - -static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_ctx *ctx, - enum v4l2_buf_type type) -{ - if (V4L2_TYPE_IS_OUTPUT(type)) - return &ctx->q_data[MTK_Q_DATA_SRC]; - - return &ctx->q_data[MTK_Q_DATA_DST]; -} - -static int vidioc_try_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - - /* Use M2M stateless helper if relevant */ - if (ctx->dev->vdec_pdata->uses_stateless_api) - return v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, - cmd); - else - return v4l2_m2m_ioctl_try_decoder_cmd(file, priv, cmd); -} - - -static int vidioc_decoder_cmd(struct file *file, void *priv, - struct v4l2_decoder_cmd *cmd) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct vb2_queue *src_vq, *dst_vq; - int ret; - - ret = vidioc_try_decoder_cmd(file, priv, cmd); - if (ret) - return ret; - - /* Use M2M stateless helper if relevant */ - if (ctx->dev->vdec_pdata->uses_stateless_api) - return v4l2_m2m_ioctl_stateless_decoder_cmd(file, priv, cmd); - - mtk_v4l2_debug(1, "decoder cmd=%u", cmd->cmd); - dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - switch (cmd->cmd) { - case V4L2_DEC_CMD_STOP: - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!vb2_is_streaming(src_vq)) { - mtk_v4l2_debug(1, "Output stream is off. No need to flush."); - return 0; - } - if (!vb2_is_streaming(dst_vq)) { - mtk_v4l2_debug(1, "Capture stream is off. No need to flush."); - return 0; - } - v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf.vb); - v4l2_m2m_try_schedule(ctx->m2m_ctx); - break; - - case V4L2_DEC_CMD_START: - vb2_clear_last_buffer_dequeued(dst_vq); - break; - - default: - return -EINVAL; - } - - return 0; -} - -void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx) -{ - mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]); -} - -void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx) -{ - mutex_lock(&ctx->dev->dec_mutex[ctx->hw_id]); -} - -void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx) -{ - vdec_if_deinit(ctx); - ctx->state = MTK_STATE_FREE; -} - -void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx) -{ - struct mtk_q_data *q_data; - - ctx->dev->vdec_pdata->init_vdec_params(ctx); - - ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; - ctx->fh.m2m_ctx = ctx->m2m_ctx; - ctx->fh.ctrl_handler = &ctx->ctrl_hdl; - INIT_WORK(&ctx->decode_work, ctx->dev->vdec_pdata->worker); - ctx->colorspace = V4L2_COLORSPACE_REC709; - ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - ctx->quantization = V4L2_QUANTIZATION_DEFAULT; - ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; - - q_data = &ctx->q_data[MTK_Q_DATA_SRC]; - memset(q_data, 0, sizeof(struct mtk_q_data)); - q_data->visible_width = DFT_CFG_WIDTH; - q_data->visible_height = DFT_CFG_HEIGHT; - q_data->fmt = ctx->dev->vdec_pdata->default_out_fmt; - q_data->field = V4L2_FIELD_NONE; - - q_data->sizeimage[0] = DFT_CFG_WIDTH * DFT_CFG_HEIGHT; - q_data->bytesperline[0] = 0; - - q_data = &ctx->q_data[MTK_Q_DATA_DST]; - memset(q_data, 0, sizeof(struct mtk_q_data)); - q_data->visible_width = DFT_CFG_WIDTH; - q_data->visible_height = DFT_CFG_HEIGHT; - q_data->coded_width = DFT_CFG_WIDTH; - q_data->coded_height = DFT_CFG_HEIGHT; - q_data->fmt = ctx->dev->vdec_pdata->default_cap_fmt; - q_data->field = V4L2_FIELD_NONE; - - v4l_bound_align_image(&q_data->coded_width, - MTK_VDEC_MIN_W, - MTK_VDEC_MAX_W, 4, - &q_data->coded_height, - MTK_VDEC_MIN_H, - MTK_VDEC_MAX_H, 5, 6); - - q_data->sizeimage[0] = q_data->coded_width * q_data->coded_height; - q_data->bytesperline[0] = q_data->coded_width; - q_data->sizeimage[1] = q_data->sizeimage[0] / 2; - q_data->bytesperline[1] = q_data->coded_width; -} - -static int vidioc_vdec_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - - if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", - ctx->id); - return -EIO; - } - - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_vdec_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - - if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on DQBUF after unrecoverable error", - ctx->id); - return -EIO; - } - - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_vdec_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, MTK_VCODEC_DEC_NAME, sizeof(cap->driver)); - strscpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); - strscpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); - - return 0; -} - -static int vidioc_vdec_subscribe_evt(struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_EOS: - return v4l2_event_subscribe(fh, sub, 2, NULL); - case V4L2_EVENT_SOURCE_CHANGE: - return v4l2_src_change_event_subscribe(fh, sub); - default: - return v4l2_ctrl_subscribe_event(fh, sub); - } -} - -static int vidioc_try_fmt(struct v4l2_format *f, - const struct mtk_video_fmt *fmt) -{ - struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - - pix_fmt_mp->field = V4L2_FIELD_NONE; - - pix_fmt_mp->width = - clamp(pix_fmt_mp->width, MTK_VDEC_MIN_W, MTK_VDEC_MAX_W); - pix_fmt_mp->height = - clamp(pix_fmt_mp->height, MTK_VDEC_MIN_H, MTK_VDEC_MAX_H); - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - pix_fmt_mp->num_planes = 1; - pix_fmt_mp->plane_fmt[0].bytesperline = 0; - } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - int tmp_w, tmp_h; - - /* - * Find next closer width align 64, heign align 64, size align - * 64 rectangle - * Note: This only get default value, the real HW needed value - * only available when ctx in MTK_STATE_HEADER state - */ - tmp_w = pix_fmt_mp->width; - tmp_h = pix_fmt_mp->height; - v4l_bound_align_image(&pix_fmt_mp->width, - MTK_VDEC_MIN_W, - MTK_VDEC_MAX_W, 6, - &pix_fmt_mp->height, - MTK_VDEC_MIN_H, - MTK_VDEC_MAX_H, 6, 9); - - if (pix_fmt_mp->width < tmp_w && - (pix_fmt_mp->width + 64) <= MTK_VDEC_MAX_W) - pix_fmt_mp->width += 64; - if (pix_fmt_mp->height < tmp_h && - (pix_fmt_mp->height + 64) <= MTK_VDEC_MAX_H) - pix_fmt_mp->height += 64; - - mtk_v4l2_debug(0, - "before resize width=%d, height=%d, after resize width=%d, height=%d, sizeimage=%d", - tmp_w, tmp_h, pix_fmt_mp->width, - pix_fmt_mp->height, - pix_fmt_mp->width * pix_fmt_mp->height); - - pix_fmt_mp->num_planes = fmt->num_planes; - pix_fmt_mp->plane_fmt[0].sizeimage = - pix_fmt_mp->width * pix_fmt_mp->height; - pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; - - if (pix_fmt_mp->num_planes == 2) { - pix_fmt_mp->plane_fmt[1].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 2; - pix_fmt_mp->plane_fmt[1].bytesperline = - pix_fmt_mp->width; - } - } - - pix_fmt_mp->flags = 0; - return 0; -} - -static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; - - fmt = mtk_vdec_find_format(f, dec_pdata); - if (!fmt) { - f->fmt.pix.pixelformat = - ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc; - fmt = mtk_vdec_find_format(f, dec_pdata); - } - - return vidioc_try_fmt(f, fmt); -} - -static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; - - fmt = mtk_vdec_find_format(f, dec_pdata); - if (!fmt) { - f->fmt.pix.pixelformat = - ctx->q_data[MTK_Q_DATA_SRC].fmt->fourcc; - fmt = mtk_vdec_find_format(f, dec_pdata); - } - - if (pix_fmt_mp->plane_fmt[0].sizeimage == 0) { - mtk_v4l2_err("sizeimage of output format must be given"); - return -EINVAL; - } - - return vidioc_try_fmt(f, fmt); -} - -static int vidioc_vdec_g_selection(struct file *file, void *priv, - struct v4l2_selection *s) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct mtk_q_data *q_data; - - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - q_data = &ctx->q_data[MTK_Q_DATA_DST]; - - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - s->r.left = 0; - s->r.top = 0; - s->r.width = ctx->picinfo.pic_w; - s->r.height = ctx->picinfo.pic_h; - break; - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - s->r.left = 0; - s->r.top = 0; - s->r.width = ctx->picinfo.buf_w; - s->r.height = ctx->picinfo.buf_h; - break; - case V4L2_SEL_TGT_COMPOSE: - if (vdec_if_get_param(ctx, GET_PARAM_CROP_INFO, &(s->r))) { - /* set to default value if header info not ready yet*/ - s->r.left = 0; - s->r.top = 0; - s->r.width = q_data->visible_width; - s->r.height = q_data->visible_height; - } - break; - default: - return -EINVAL; - } - - if (ctx->state < MTK_STATE_HEADER) { - /* set to default value if header info not ready yet*/ - s->r.left = 0; - s->r.top = 0; - s->r.width = q_data->visible_width; - s->r.height = q_data->visible_height; - return 0; - } - - return 0; -} - -static int vidioc_vdec_s_selection(struct file *file, void *priv, - struct v4l2_selection *s) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - - if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE: - s->r.left = 0; - s->r.top = 0; - s->r.width = ctx->picinfo.pic_w; - s->r.height = ctx->picinfo.pic_h; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_vdec_s_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct v4l2_pix_format_mplane *pix_mp; - struct mtk_q_data *q_data; - int ret = 0; - const struct mtk_video_fmt *fmt; - const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - - q_data = mtk_vdec_get_q_data(ctx, f->type); - if (!q_data) - return -EINVAL; - - pix_mp = &f->fmt.pix_mp; - /* - * Setting OUTPUT format after OUTPUT buffers are allocated is invalid - * if using the stateful API. - */ - if (!dec_pdata->uses_stateless_api && - f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - vb2_is_busy(&ctx->m2m_ctx->out_q_ctx.q)) { - mtk_v4l2_err("out_q_ctx buffers already requested"); - ret = -EBUSY; - } - - /* - * Setting CAPTURE format after CAPTURE buffers are allocated is - * invalid. - */ - if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && - vb2_is_busy(&ctx->m2m_ctx->cap_q_ctx.q)) { - mtk_v4l2_err("cap_q_ctx buffers already requested"); - ret = -EBUSY; - } - - fmt = mtk_vdec_find_format(f, dec_pdata); - if (fmt == NULL) { - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - f->fmt.pix.pixelformat = - dec_pdata->default_out_fmt->fourcc; - fmt = mtk_vdec_find_format(f, dec_pdata); - } else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - f->fmt.pix.pixelformat = - dec_pdata->default_cap_fmt->fourcc; - fmt = mtk_vdec_find_format(f, dec_pdata); - } - } - if (fmt == NULL) - return -EINVAL; - - q_data->fmt = fmt; - vidioc_try_fmt(f, q_data->fmt); - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - q_data->sizeimage[0] = pix_mp->plane_fmt[0].sizeimage; - q_data->coded_width = pix_mp->width; - q_data->coded_height = pix_mp->height; - - ctx->colorspace = pix_mp->colorspace; - ctx->ycbcr_enc = pix_mp->ycbcr_enc; - ctx->quantization = pix_mp->quantization; - ctx->xfer_func = pix_mp->xfer_func; - - ctx->current_codec = fmt->fourcc; - if (ctx->state == MTK_STATE_FREE) { - ret = vdec_if_init(ctx, q_data->fmt->fourcc); - if (ret) { - mtk_v4l2_err("[%d]: vdec_if_init() fail ret=%d", - ctx->id, ret); - return -EINVAL; - } - ctx->state = MTK_STATE_INIT; - } - } - - /* - * If using the stateless API, S_FMT should have the effect of setting - * the CAPTURE queue resolution no matter which queue it was called on. - */ - if (dec_pdata->uses_stateless_api) { - ctx->picinfo.pic_w = pix_mp->width; - ctx->picinfo.pic_h = pix_mp->height; - - ret = vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo); - if (ret) { - mtk_v4l2_err("[%d]Error!! Get GET_PARAM_PICTURE_INFO Fail", - ctx->id); - return -EINVAL; - } - - ctx->last_decoded_picinfo = ctx->picinfo; - - if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1) { - ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = - ctx->picinfo.fb_sz[0] + - ctx->picinfo.fb_sz[1]; - ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = - ctx->picinfo.buf_w; - } else { - ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = - ctx->picinfo.fb_sz[0]; - ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = - ctx->picinfo.buf_w; - ctx->q_data[MTK_Q_DATA_DST].sizeimage[1] = - ctx->picinfo.fb_sz[1]; - ctx->q_data[MTK_Q_DATA_DST].bytesperline[1] = - ctx->picinfo.buf_w; - } - - ctx->q_data[MTK_Q_DATA_DST].coded_width = ctx->picinfo.buf_w; - ctx->q_data[MTK_Q_DATA_DST].coded_height = ctx->picinfo.buf_h; - mtk_v4l2_debug(2, "[%d] vdec_if_init() num_plane = %d wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", - ctx->id, pix_mp->num_planes, ctx->picinfo.buf_w, ctx->picinfo.buf_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->q_data[MTK_Q_DATA_DST].sizeimage[0], - ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]); - } - return 0; -} - -static int vidioc_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - int i = 0; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; - - if (fsize->index != 0) - return -EINVAL; - - for (i = 0; i < dec_pdata->num_framesizes; ++i) { - if (fsize->pixel_format != dec_pdata->vdec_framesizes[i].fourcc) - continue; - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise = dec_pdata->vdec_framesizes[i].stepwise; - if (!(ctx->dev->dec_capability & - VCODEC_CAPABILITY_4K_DISABLED)) { - mtk_v4l2_debug(3, "4K is enabled"); - fsize->stepwise.max_width = - VCODEC_DEC_4K_CODED_WIDTH; - fsize->stepwise.max_height = - VCODEC_DEC_4K_CODED_HEIGHT; - } - mtk_v4l2_debug(1, "%x, %d %d %d %d %d %d", - ctx->dev->dec_capability, - fsize->stepwise.min_width, - fsize->stepwise.max_width, - fsize->stepwise.step_width, - fsize->stepwise.min_height, - fsize->stepwise.max_height, - fsize->stepwise.step_height); - return 0; - } - - return -EINVAL; -} - -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, void *priv, - bool output_queue) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_dec_pdata *dec_pdata = ctx->dev->vdec_pdata; - const struct mtk_video_fmt *fmt; - int i, j = 0; - - for (i = 0; i < dec_pdata->num_formats; i++) { - if (output_queue && - dec_pdata->vdec_formats[i].type != MTK_FMT_DEC) - continue; - if (!output_queue && - dec_pdata->vdec_formats[i].type != MTK_FMT_FRAME) - continue; - - if (j == f->index) - break; - ++j; - } - - if (i == dec_pdata->num_formats) - return -EINVAL; - - fmt = &dec_pdata->vdec_formats[i]; - f->pixelformat = fmt->fourcc; - f->flags = fmt->flags; - - return 0; -} - -static int vidioc_vdec_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(f, priv, false); -} - -static int vidioc_vdec_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - return vidioc_enum_fmt(f, priv, true); -} - -static int vidioc_vdec_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp; - struct vb2_queue *vq; - struct mtk_q_data *q_data; - - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) { - mtk_v4l2_err("no vb2 queue for type=%d", f->type); - return -EINVAL; - } - - q_data = mtk_vdec_get_q_data(ctx, f->type); - - pix_mp->field = V4L2_FIELD_NONE; - pix_mp->colorspace = ctx->colorspace; - pix_mp->ycbcr_enc = ctx->ycbcr_enc; - pix_mp->quantization = ctx->quantization; - pix_mp->xfer_func = ctx->xfer_func; - - if ((f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) && - (ctx->state >= MTK_STATE_HEADER)) { - /* Until STREAMOFF is called on the CAPTURE queue - * (acknowledging the event), the driver operates as if - * the resolution hasn't changed yet. - * So we just return picinfo yet, and update picinfo in - * stop_streaming hook function - */ - q_data->sizeimage[0] = ctx->picinfo.fb_sz[0]; - q_data->sizeimage[1] = ctx->picinfo.fb_sz[1]; - q_data->bytesperline[0] = ctx->last_decoded_picinfo.buf_w; - q_data->bytesperline[1] = ctx->last_decoded_picinfo.buf_w; - q_data->coded_width = ctx->picinfo.buf_w; - q_data->coded_height = ctx->picinfo.buf_h; - ctx->last_decoded_picinfo.cap_fourcc = q_data->fmt->fourcc; - - /* - * Width and height are set to the dimensions - * of the movie, the buffer is bigger and - * further processing stages should crop to this - * rectangle. - */ - pix_mp->width = q_data->coded_width; - pix_mp->height = q_data->coded_height; - - /* - * Set pixelformat to the format in which mt vcodec - * outputs the decoded frame - */ - pix_mp->num_planes = q_data->fmt->num_planes; - pix_mp->pixelformat = q_data->fmt->fourcc; - pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; - pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; - pix_mp->plane_fmt[1].bytesperline = q_data->bytesperline[1]; - pix_mp->plane_fmt[1].sizeimage = q_data->sizeimage[1]; - - } else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - /* - * This is run on OUTPUT - * The buffer contains compressed image - * so width and height have no meaning. - * Assign value here to pass v4l2-compliance test - */ - pix_mp->width = q_data->visible_width; - pix_mp->height = q_data->visible_height; - pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; - pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; - pix_mp->pixelformat = q_data->fmt->fourcc; - pix_mp->num_planes = q_data->fmt->num_planes; - } else { - pix_mp->width = q_data->coded_width; - pix_mp->height = q_data->coded_height; - pix_mp->num_planes = q_data->fmt->num_planes; - pix_mp->pixelformat = q_data->fmt->fourcc; - pix_mp->plane_fmt[0].bytesperline = q_data->bytesperline[0]; - pix_mp->plane_fmt[0].sizeimage = q_data->sizeimage[0]; - pix_mp->plane_fmt[1].bytesperline = q_data->bytesperline[1]; - pix_mp->plane_fmt[1].sizeimage = q_data->sizeimage[1]; - - mtk_v4l2_debug(1, "[%d] type=%d state=%d Format information could not be read, not ready yet!", - ctx->id, f->type, ctx->state); - } - - return 0; -} - -int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); - struct mtk_q_data *q_data; - unsigned int i; - - q_data = mtk_vdec_get_q_data(ctx, vq->type); - - if (q_data == NULL) { - mtk_v4l2_err("vq->type=%d err\n", vq->type); - return -EINVAL; - } - - if (*nplanes) { - for (i = 0; i < *nplanes; i++) { - if (sizes[i] < q_data->sizeimage[i]) - return -EINVAL; - } - } else { - if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - *nplanes = q_data->fmt->num_planes; - else - *nplanes = 1; - - for (i = 0; i < *nplanes; i++) - sizes[i] = q_data->sizeimage[i]; - } - - mtk_v4l2_debug(1, - "[%d]\t type = %d, get %d plane(s), %d buffer(s) of size 0x%x 0x%x ", - ctx->id, vq->type, *nplanes, *nbuffers, - sizes[0], sizes[1]); - - return 0; -} - -int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct mtk_q_data *q_data; - int i; - - mtk_v4l2_debug(3, "[%d] (%d) id=%d", - ctx->id, vb->vb2_queue->type, vb->index); - - q_data = mtk_vdec_get_q_data(ctx, vb->vb2_queue->type); - - for (i = 0; i < q_data->fmt->num_planes; i++) { - if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { - mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", - i, vb2_plane_size(vb, i), - q_data->sizeimage[i]); - } - } - - return 0; -} - -void vb2ops_vdec_buf_finish(struct vb2_buffer *vb) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vb2_v4l2; - struct mtk_video_dec_buf *buf; - bool buf_error; - - vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); - buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, m2m_buf.vb); - mutex_lock(&ctx->lock); - if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - buf->queued_in_v4l2 = false; - buf->queued_in_vb2 = false; - } - buf_error = buf->error; - mutex_unlock(&ctx->lock); - - if (buf_error) { - mtk_v4l2_err("Unrecoverable error on buffer."); - ctx->state = MTK_STATE_ABORT; - } -} - -int vb2ops_vdec_buf_init(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vb2_v4l2 = container_of(vb, - struct vb2_v4l2_buffer, vb2_buf); - struct mtk_video_dec_buf *buf = container_of(vb2_v4l2, - struct mtk_video_dec_buf, m2m_buf.vb); - - if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - buf->used = false; - buf->queued_in_v4l2 = false; - } - - return 0; -} - -int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); - - if (ctx->state == MTK_STATE_FLUSH) - ctx->state = MTK_STATE_HEADER; - - return 0; -} - -void vb2ops_vdec_stop_streaming(struct vb2_queue *q) -{ - struct vb2_v4l2_buffer *src_buf = NULL, *dst_buf = NULL; - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); - int ret; - - mtk_v4l2_debug(3, "[%d] (%d) state=(%x) ctx->decoded_frame_cnt=%d", - ctx->id, q->type, ctx->state, ctx->decoded_frame_cnt); - - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { - if (src_buf != &ctx->empty_flush_buf.vb) { - struct media_request *req = - src_buf->vb2_buf.req_obj.req; - v4l2_m2m_buf_done(src_buf, - VB2_BUF_STATE_ERROR); - if (req) - v4l2_ctrl_request_complete(req, &ctx->ctrl_hdl); - } - } - return; - } - - if (ctx->state >= MTK_STATE_HEADER) { - - /* Until STREAMOFF is called on the CAPTURE queue - * (acknowledging the event), the driver operates - * as if the resolution hasn't changed yet, i.e. - * VIDIOC_G_FMT< etc. return previous resolution. - * So we update picinfo here - */ - ctx->picinfo = ctx->last_decoded_picinfo; - - mtk_v4l2_debug(2, - "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", - ctx->id, ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->last_decoded_picinfo.buf_w, - ctx->last_decoded_picinfo.buf_h); - - ret = ctx->dev->vdec_pdata->flush_decoder(ctx); - if (ret) - mtk_v4l2_err("DecodeFinal failed, ret=%d", ret); - } - ctx->state = MTK_STATE_FLUSH; - - while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) - vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); - } - -} - -static void m2mops_vdec_device_run(void *priv) -{ - struct mtk_vcodec_ctx *ctx = priv; - struct mtk_vcodec_dev *dev = ctx->dev; - - queue_work(dev->decode_workqueue, &ctx->decode_work); -} - -static int m2mops_vdec_job_ready(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - - if (ctx->state == MTK_STATE_ABORT) - return 0; - - if ((ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w) || - (ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h)) - return 0; - - if (ctx->state != MTK_STATE_HEADER) - return 0; - - return 1; -} - -static void m2mops_vdec_job_abort(void *priv) -{ - struct mtk_vcodec_ctx *ctx = priv; - - ctx->state = MTK_STATE_ABORT; -} - -const struct v4l2_m2m_ops mtk_vdec_m2m_ops = { - .device_run = m2mops_vdec_device_run, - .job_ready = m2mops_vdec_job_ready, - .job_abort = m2mops_vdec_job_abort, -}; - -const struct v4l2_ioctl_ops mtk_vdec_ioctl_ops = { - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - - .vidioc_qbuf = vidioc_vdec_qbuf, - .vidioc_dqbuf = vidioc_vdec_dqbuf, - - .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, - .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, - - .vidioc_s_fmt_vid_cap_mplane = vidioc_vdec_s_fmt, - .vidioc_s_fmt_vid_out_mplane = vidioc_vdec_s_fmt, - .vidioc_g_fmt_vid_cap_mplane = vidioc_vdec_g_fmt, - .vidioc_g_fmt_vid_out_mplane = vidioc_vdec_g_fmt, - - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - - .vidioc_enum_fmt_vid_cap = vidioc_vdec_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out = vidioc_vdec_enum_fmt_vid_out, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - - .vidioc_querycap = vidioc_vdec_querycap, - .vidioc_subscribe_event = vidioc_vdec_subscribe_evt, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - .vidioc_g_selection = vidioc_vdec_g_selection, - .vidioc_s_selection = vidioc_vdec_s_selection, - - .vidioc_decoder_cmd = vidioc_decoder_cmd, - .vidioc_try_decoder_cmd = vidioc_try_decoder_cmd, -}; - -int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct mtk_vcodec_ctx *ctx = priv; - int ret = 0; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP; - src_vq->drv_priv = ctx; - src_vq->buf_struct_size = sizeof(struct mtk_video_dec_buf); - src_vq->ops = ctx->dev->vdec_pdata->vdec_vb2_ops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->dev_mutex; - src_vq->dev = &ctx->dev->plat_dev->dev; - - ret = vb2_queue_init(src_vq); - if (ret) { - mtk_v4l2_err("Failed to initialize videobuf2 queue(output)"); - return ret; - } - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP; - dst_vq->drv_priv = ctx; - dst_vq->buf_struct_size = sizeof(struct mtk_video_dec_buf); - dst_vq->ops = ctx->dev->vdec_pdata->vdec_vb2_ops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->dev_mutex; - dst_vq->dev = &ctx->dev->plat_dev->dev; - - ret = vb2_queue_init(dst_vq); - if (ret) - mtk_v4l2_err("Failed to initialize videobuf2 queue(capture)"); - - return ret; -} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h deleted file mode 100644 index 66cd6d2242c3..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h +++ /dev/null @@ -1,100 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - * Tiffany Lin - */ - -#ifndef _MTK_VCODEC_DEC_H_ -#define _MTK_VCODEC_DEC_H_ - -#include -#include - -#define VCODEC_DEC_ALIGNED_64 64 -#define VCODEC_CAPABILITY_4K_DISABLED 0x10 -#define VCODEC_DEC_4K_CODED_WIDTH 4096U -#define VCODEC_DEC_4K_CODED_HEIGHT 2304U -#define MTK_VDEC_MAX_W 2048U -#define MTK_VDEC_MAX_H 1088U -#define MTK_VDEC_MIN_W 64U -#define MTK_VDEC_MIN_H 64U - -#define MTK_VDEC_IRQ_STATUS_DEC_SUCCESS 0x10000 - -/** - * struct vdec_fb - decoder frame buffer - * @base_y : Y plane memory info - * @base_c : C plane memory info - * @status : frame buffer status (vdec_fb_status) - */ -struct vdec_fb { - struct mtk_vcodec_mem base_y; - struct mtk_vcodec_mem base_c; - unsigned int status; -}; - -/** - * struct mtk_video_dec_buf - Private data related to each VB2 buffer. - * @m2m_buf: M2M buffer - * @list: link list - * @used: Capture buffer contain decoded frame data and keep in - * codec data structure - * @queued_in_vb2: Capture buffer is queue in vb2 - * @queued_in_v4l2: Capture buffer is in v4l2 driver, but not in vb2 - * queue yet - * @error: An unrecoverable error occurs on this buffer. - * @frame_buffer: Decode status, and buffer information of Capture buffer - * @bs_buffer: Output buffer info - * - * Note : These status information help us track and debug buffer state - */ -struct mtk_video_dec_buf { - struct v4l2_m2m_buffer m2m_buf; - - bool used; - bool queued_in_vb2; - bool queued_in_v4l2; - bool error; - - union { - struct vdec_fb frame_buffer; - struct mtk_vcodec_mem bs_buffer; - }; -}; - -extern const struct v4l2_ioctl_ops mtk_vdec_ioctl_ops; -extern const struct v4l2_m2m_ops mtk_vdec_m2m_ops; -extern const struct media_device_ops mtk_vcodec_media_ops; -extern const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata; -extern const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata; -extern const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata; - - -/* - * mtk_vdec_lock/mtk_vdec_unlock are for ctx instance to - * get/release lock before/after access decoder hw. - * mtk_vdec_lock get decoder hw lock and set curr_ctx - * to ctx instance that get lock - */ -void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx); -void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx); -int mtk_vcodec_dec_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); -void mtk_vcodec_dec_set_default_params(struct mtk_vcodec_ctx *ctx); -void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx); - -/* - * VB2 ops - */ -int vb2ops_vdec_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - struct device *alloc_devs[]); -int vb2ops_vdec_buf_prepare(struct vb2_buffer *vb); -void vb2ops_vdec_buf_finish(struct vb2_buffer *vb); -int vb2ops_vdec_buf_init(struct vb2_buffer *vb); -int vb2ops_vdec_start_streaming(struct vb2_queue *q, unsigned int count); -void vb2ops_vdec_stop_streaming(struct vb2_queue *q); - - -#endif /* _MTK_VCODEC_DEC_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c deleted file mode 100644 index 48dad9bb13d2..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c +++ /dev/null @@ -1,509 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - * Tiffany Lin - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_dec.h" -#include "mtk_vcodec_dec_hw.h" -#include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_fw.h" - -static int mtk_vcodec_get_hw_count(struct mtk_vcodec_dev *dev) -{ - switch (dev->vdec_pdata->hw_arch) { - case MTK_VDEC_PURE_SINGLE_CORE: - return MTK_VDEC_ONE_CORE; - case MTK_VDEC_LAT_SINGLE_CORE: - return MTK_VDEC_ONE_LAT_ONE_CORE; - default: - mtk_v4l2_err("hw arch %d not supported", dev->vdec_pdata->hw_arch); - return MTK_VDEC_NO_HW; - } -} - -static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv) -{ - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; - u32 cg_status = 0; - unsigned int dec_done_status = 0; - void __iomem *vdec_misc_addr = dev->reg_base[VDEC_MISC] + - VDEC_IRQ_CFG_REG; - - ctx = mtk_vcodec_get_curr_ctx(dev, MTK_VDEC_CORE); - - /* check if HW active or not */ - cg_status = readl(dev->reg_base[0]); - if ((cg_status & VDEC_HW_ACTIVE) != 0) { - mtk_v4l2_err("DEC ISR, VDEC active is not 0x0 (0x%08x)", - cg_status); - return IRQ_HANDLED; - } - - dec_done_status = readl(vdec_misc_addr); - ctx->irq_status = dec_done_status; - if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) != - MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) - return IRQ_HANDLED; - - /* clear interrupt */ - writel((readl(vdec_misc_addr) | VDEC_IRQ_CFG), - dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); - writel((readl(vdec_misc_addr) & ~VDEC_IRQ_CLR), - dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG); - - wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); - - mtk_v4l2_debug(3, - "mtk_vcodec_dec_irq_handler :wake up ctx %d, dec_done_status=%x", - ctx->id, dec_done_status); - - return IRQ_HANDLED; -} - -static int mtk_vcodec_get_reg_bases(struct mtk_vcodec_dev *dev) -{ - struct platform_device *pdev = dev->plat_dev; - int reg_num, i; - - /* Sizeof(u32) * 4 bytes for each register base. */ - reg_num = of_property_count_elems_of_size(pdev->dev.of_node, "reg", - sizeof(u32) * 4); - if (reg_num <= 0 || reg_num > NUM_MAX_VDEC_REG_BASE) { - dev_err(&pdev->dev, "Invalid register property size: %d\n", reg_num); - return -EINVAL; - } - - for (i = 0; i < reg_num; i++) { - dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i); - if (IS_ERR(dev->reg_base[i])) - return PTR_ERR(dev->reg_base[i]); - - mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]); - } - - return 0; -} - -static int mtk_vcodec_init_dec_resources(struct mtk_vcodec_dev *dev) -{ - struct platform_device *pdev = dev->plat_dev; - int ret; - - ret = mtk_vcodec_get_reg_bases(dev); - if (ret) - return ret; - - if (dev->vdec_pdata->is_subdev_supported) - return 0; - - dev->dec_irq = platform_get_irq(pdev, 0); - if (dev->dec_irq < 0) { - dev_err(&pdev->dev, "failed to get irq number"); - return dev->dec_irq; - } - - irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); - ret = devm_request_irq(&pdev->dev, dev->dec_irq, - mtk_vcodec_dec_irq_handler, 0, pdev->name, dev); - if (ret) { - dev_err(&pdev->dev, "failed to install dev->dec_irq %d (%d)", - dev->dec_irq, ret); - return ret; - } - - ret = mtk_vcodec_init_dec_clk(pdev, &dev->pm); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get mt vcodec clock source"); - return ret; - } - - pm_runtime_enable(&pdev->dev); - return 0; -} - -static int fops_vcodec_open(struct file *file) -{ - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = NULL; - int ret = 0, i, hw_count; - struct vb2_queue *src_vq; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - mutex_lock(&dev->dev_mutex); - ctx->id = dev->id_counter++; - v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - INIT_LIST_HEAD(&ctx->list); - ctx->dev = dev; - if (ctx->dev->vdec_pdata->is_subdev_supported) { - hw_count = mtk_vcodec_get_hw_count(dev); - if (!hw_count || !dev->subdev_prob_done) { - ret = -EINVAL; - goto err_ctrls_setup; - } - - ret = dev->subdev_prob_done(dev); - if (ret) - goto err_ctrls_setup; - - for (i = 0; i < hw_count; i++) - init_waitqueue_head(&ctx->queue[i]); - } else { - init_waitqueue_head(&ctx->queue[0]); - } - mutex_init(&ctx->lock); - - ctx->type = MTK_INST_DECODER; - ret = dev->vdec_pdata->ctrls_setup(ctx); - if (ret) { - mtk_v4l2_err("Failed to setup mt vcodec controls"); - goto err_ctrls_setup; - } - ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_dec, ctx, - &mtk_vcodec_dec_queue_init); - if (IS_ERR((__force void *)ctx->m2m_ctx)) { - ret = PTR_ERR((__force void *)ctx->m2m_ctx); - mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", - ret); - goto err_m2m_ctx_init; - } - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - ctx->empty_flush_buf.vb.vb2_buf.vb2_queue = src_vq; - mtk_vcodec_dec_set_default_params(ctx); - - if (v4l2_fh_is_singular(&ctx->fh)) { - ret = mtk_vcodec_dec_pw_on(dev, MTK_VDEC_LAT0); - if (ret < 0) - goto err_load_fw; - /* - * Does nothing if firmware was already loaded. - */ - ret = mtk_vcodec_fw_load_firmware(dev->fw_handler); - if (ret < 0) { - /* - * Return 0 if downloading firmware successfully, - * otherwise it is failed - */ - mtk_v4l2_err("failed to load firmware!"); - goto err_load_fw; - } - - dev->dec_capability = - mtk_vcodec_fw_get_vdec_capa(dev->fw_handler); - mtk_v4l2_debug(0, "decoder capability %x", dev->dec_capability); - } - - list_add(&ctx->list, &dev->ctx_list); - - mutex_unlock(&dev->dev_mutex); - mtk_v4l2_debug(0, "%s decoder [%d]", dev_name(&dev->plat_dev->dev), - ctx->id); - return ret; - - /* Deinit when failure occurred */ -err_load_fw: - v4l2_m2m_ctx_release(ctx->m2m_ctx); -err_m2m_ctx_init: - v4l2_ctrl_handler_free(&ctx->ctrl_hdl); -err_ctrls_setup: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); - mutex_unlock(&dev->dev_mutex); - - return ret; -} - -static int fops_vcodec_release(struct file *file) -{ - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); - - mtk_v4l2_debug(0, "[%d] decoder", ctx->id); - mutex_lock(&dev->dev_mutex); - - /* - * Call v4l2_m2m_ctx_release before mtk_vcodec_dec_release. First, it - * makes sure the worker thread is not running after vdec_if_deinit. - * Second, the decoder will be flushed and all the buffers will be - * returned in stop_streaming. - */ - v4l2_m2m_ctx_release(ctx->m2m_ctx); - mtk_vcodec_dec_release(ctx); - - if (v4l2_fh_is_singular(&ctx->fh)) - mtk_vcodec_dec_pw_off(dev, MTK_VDEC_LAT0); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - v4l2_ctrl_handler_free(&ctx->ctrl_hdl); - - list_del_init(&ctx->list); - kfree(ctx); - mutex_unlock(&dev->dev_mutex); - return 0; -} - -static const struct v4l2_file_operations mtk_vcodec_fops = { - .owner = THIS_MODULE, - .open = fops_vcodec_open, - .release = fops_vcodec_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int mtk_vcodec_probe(struct platform_device *pdev) -{ - struct mtk_vcodec_dev *dev; - struct video_device *vfd_dec; - phandle rproc_phandle; - enum mtk_vcodec_fw_type fw_type; - int i, ret; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - INIT_LIST_HEAD(&dev->ctx_list); - dev->plat_dev = pdev; - - dev->vdec_pdata = of_device_get_match_data(&pdev->dev); - if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", - &rproc_phandle)) { - fw_type = VPU; - } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp", - &rproc_phandle)) { - fw_type = SCP; - } else { - mtk_v4l2_err("Could not get vdec IPI device"); - return -ENODEV; - } - dma_set_max_seg_size(&pdev->dev, UINT_MAX); - - dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, DECODER); - if (IS_ERR(dev->fw_handler)) - return PTR_ERR(dev->fw_handler); - - ret = mtk_vcodec_init_dec_resources(dev); - if (ret) { - dev_err(&pdev->dev, "Failed to init dec resources"); - goto err_dec_pm; - } - - if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) { - vdec_msg_queue_init_ctx(&dev->msg_queue_core_ctx, MTK_VDEC_CORE); - dev->core_workqueue = - alloc_ordered_workqueue("core-decoder", - WQ_MEM_RECLAIM | WQ_FREEZABLE); - if (!dev->core_workqueue) { - mtk_v4l2_err("Failed to create core workqueue"); - ret = -EINVAL; - goto err_res; - } - } - - if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) { - ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); - if (ret) { - mtk_v4l2_err("Failed to set mask"); - goto err_core_workq; - } - } - - for (i = 0; i < MTK_VDEC_HW_MAX; i++) - mutex_init(&dev->dec_mutex[i]); - mutex_init(&dev->dev_mutex); - spin_lock_init(&dev->irqlock); - - snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", - "[/MTK_V4L2_VDEC]"); - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) { - mtk_v4l2_err("v4l2_device_register err=%d", ret); - goto err_core_workq; - } - - init_waitqueue_head(&dev->queue); - - vfd_dec = video_device_alloc(); - if (!vfd_dec) { - mtk_v4l2_err("Failed to allocate video device"); - ret = -ENOMEM; - goto err_dec_alloc; - } - vfd_dec->fops = &mtk_vcodec_fops; - vfd_dec->ioctl_ops = &mtk_vdec_ioctl_ops; - vfd_dec->release = video_device_release; - vfd_dec->lock = &dev->dev_mutex; - vfd_dec->v4l2_dev = &dev->v4l2_dev; - vfd_dec->vfl_dir = VFL_DIR_M2M; - vfd_dec->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | - V4L2_CAP_STREAMING; - - snprintf(vfd_dec->name, sizeof(vfd_dec->name), "%s", - MTK_VCODEC_DEC_NAME); - video_set_drvdata(vfd_dec, dev); - dev->vfd_dec = vfd_dec; - platform_set_drvdata(pdev, dev); - - dev->m2m_dev_dec = v4l2_m2m_init(&mtk_vdec_m2m_ops); - if (IS_ERR((__force void *)dev->m2m_dev_dec)) { - mtk_v4l2_err("Failed to init mem2mem dec device"); - ret = PTR_ERR((__force void *)dev->m2m_dev_dec); - goto err_dec_alloc; - } - - dev->decode_workqueue = - alloc_ordered_workqueue(MTK_VCODEC_DEC_NAME, - WQ_MEM_RECLAIM | WQ_FREEZABLE); - if (!dev->decode_workqueue) { - mtk_v4l2_err("Failed to create decode workqueue"); - ret = -EINVAL; - goto err_event_workq; - } - - if (dev->vdec_pdata->is_subdev_supported) { - ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, - &pdev->dev); - if (ret) { - mtk_v4l2_err("Main device of_platform_populate failed."); - goto err_reg_cont; - } - } - - ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1); - if (ret) { - mtk_v4l2_err("Failed to register video device"); - goto err_reg_cont; - } - - if (dev->vdec_pdata->uses_stateless_api) { - dev->mdev_dec.dev = &pdev->dev; - strscpy(dev->mdev_dec.model, MTK_VCODEC_DEC_NAME, - sizeof(dev->mdev_dec.model)); - - media_device_init(&dev->mdev_dec); - dev->mdev_dec.ops = &mtk_vcodec_media_ops; - dev->v4l2_dev.mdev = &dev->mdev_dec; - - ret = v4l2_m2m_register_media_controller(dev->m2m_dev_dec, dev->vfd_dec, - MEDIA_ENT_F_PROC_VIDEO_DECODER); - if (ret) { - mtk_v4l2_err("Failed to register media controller"); - goto err_dec_mem_init; - } - - ret = media_device_register(&dev->mdev_dec); - if (ret) { - mtk_v4l2_err("Failed to register media device"); - goto err_media_reg; - } - - mtk_v4l2_debug(0, "media registered as /dev/media%d", vfd_dec->minor); - } - - mtk_v4l2_debug(0, "decoder registered as /dev/video%d", vfd_dec->minor); - - return 0; - -err_media_reg: - v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec); -err_dec_mem_init: - video_unregister_device(vfd_dec); -err_reg_cont: - if (dev->vdec_pdata->uses_stateless_api) - media_device_cleanup(&dev->mdev_dec); - destroy_workqueue(dev->decode_workqueue); -err_event_workq: - v4l2_m2m_release(dev->m2m_dev_dec); -err_dec_alloc: - v4l2_device_unregister(&dev->v4l2_dev); -err_core_workq: - if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) - destroy_workqueue(dev->core_workqueue); -err_res: - pm_runtime_disable(dev->pm.dev); -err_dec_pm: - mtk_vcodec_fw_release(dev->fw_handler); - return ret; -} - -static const struct of_device_id mtk_vcodec_match[] = { - { - .compatible = "mediatek,mt8173-vcodec-dec", - .data = &mtk_vdec_8173_pdata, - }, - { - .compatible = "mediatek,mt8183-vcodec-dec", - .data = &mtk_vdec_8183_pdata, - }, - { - .compatible = "mediatek,mt8192-vcodec-dec", - .data = &mtk_lat_sig_core_pdata, - }, - {}, -}; - -MODULE_DEVICE_TABLE(of, mtk_vcodec_match); - -static int mtk_vcodec_dec_remove(struct platform_device *pdev) -{ - struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); - - destroy_workqueue(dev->decode_workqueue); - - if (media_devnode_is_registered(dev->mdev_dec.devnode)) { - media_device_unregister(&dev->mdev_dec); - v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec); - media_device_cleanup(&dev->mdev_dec); - } - - if (dev->m2m_dev_dec) - v4l2_m2m_release(dev->m2m_dev_dec); - - if (dev->vfd_dec) - video_unregister_device(dev->vfd_dec); - - v4l2_device_unregister(&dev->v4l2_dev); - pm_runtime_disable(dev->pm.dev); - mtk_vcodec_fw_release(dev->fw_handler); - return 0; -} - -static struct platform_driver mtk_vcodec_dec_driver = { - .probe = mtk_vcodec_probe, - .remove = mtk_vcodec_dec_remove, - .driver = { - .name = MTK_VCODEC_DEC_NAME, - .of_match_table = mtk_vcodec_match, - }, -}; - -module_platform_driver(mtk_vcodec_dec_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Mediatek video codec V4L2 decoder driver"); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.c deleted file mode 100644 index 8d2a641d92f1..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.c +++ /dev/null @@ -1,200 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2021 MediaTek Inc. - * Author: Yunfei Dong - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_dec.h" -#include "mtk_vcodec_dec_hw.h" -#include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" - -static const struct of_device_id mtk_vdec_hw_match[] = { - { - .compatible = "mediatek,mtk-vcodec-lat", - .data = (void *)MTK_VDEC_LAT0, - }, - { - .compatible = "mediatek,mtk-vcodec-core", - .data = (void *)MTK_VDEC_CORE, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, mtk_vdec_hw_match); - -static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dev *vdec_dev) -{ - struct platform_device *pdev = vdec_dev->plat_dev; - struct device_node *subdev_node; - enum mtk_vdec_hw_id hw_idx; - const struct of_device_id *of_id; - int i; - - for (i = 0; i < ARRAY_SIZE(mtk_vdec_hw_match); i++) { - of_id = &mtk_vdec_hw_match[i]; - subdev_node = of_find_compatible_node(NULL, NULL, - of_id->compatible); - if (!subdev_node) - continue; - - hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; - if (!test_bit(hw_idx, vdec_dev->subdev_bitmap)) { - dev_err(&pdev->dev, "vdec %d is not ready", hw_idx); - return -EAGAIN; - } - } - - return 0; -} - -static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv) -{ - struct mtk_vdec_hw_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; - u32 cg_status; - unsigned int dec_done_status; - void __iomem *vdec_misc_addr = dev->reg_base[VDEC_HW_MISC] + - VDEC_IRQ_CFG_REG; - - ctx = mtk_vcodec_get_curr_ctx(dev->main_dev, dev->hw_idx); - - /* check if HW active or not */ - cg_status = readl(dev->reg_base[VDEC_HW_SYS]); - if (cg_status & VDEC_HW_ACTIVE) { - mtk_v4l2_err("vdec active is not 0x0 (0x%08x)", - cg_status); - return IRQ_HANDLED; - } - - dec_done_status = readl(vdec_misc_addr); - if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) != - MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) - return IRQ_HANDLED; - - /* clear interrupt */ - writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr); - writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr); - - wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx); - - mtk_v4l2_debug(3, "wake up ctx %d, dec_done_status=%x", - ctx->id, dec_done_status); - - return IRQ_HANDLED; -} - -static int mtk_vdec_hw_init_irq(struct mtk_vdec_hw_dev *dev) -{ - struct platform_device *pdev = dev->plat_dev; - int ret; - - dev->dec_irq = platform_get_irq(pdev, 0); - if (dev->dec_irq < 0) { - dev_err(&pdev->dev, "Failed to get irq resource"); - return dev->dec_irq; - } - - irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN); - ret = devm_request_irq(&pdev->dev, dev->dec_irq, - mtk_vdec_hw_irq_handler, 0, pdev->name, dev); - if (ret) { - dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)", - dev->dec_irq, ret); - return ret; - } - - return 0; -} - -static int mtk_vdec_hw_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct mtk_vdec_hw_dev *subdev_dev; - struct mtk_vcodec_dev *main_dev; - const struct of_device_id *of_id; - int hw_idx; - int ret; - - if (!dev->parent) { - dev_err(dev, "no parent for hardware devices.\n"); - return -ENODEV; - } - - main_dev = dev_get_drvdata(dev->parent); - if (!main_dev) { - dev_err(dev, "failed to get parent driver data"); - return -EINVAL; - } - - subdev_dev = devm_kzalloc(dev, sizeof(*subdev_dev), GFP_KERNEL); - if (!subdev_dev) - return -ENOMEM; - - subdev_dev->plat_dev = pdev; - ret = mtk_vcodec_init_dec_clk(pdev, &subdev_dev->pm); - if (ret) - return ret; - pm_runtime_enable(&pdev->dev); - - of_id = of_match_device(mtk_vdec_hw_match, dev); - if (!of_id) { - dev_err(dev, "Can't get vdec subdev id.\n"); - ret = -EINVAL; - goto err; - } - - hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data; - if (hw_idx >= MTK_VDEC_HW_MAX) { - dev_err(dev, "Hardware index %d not correct.\n", hw_idx); - ret = -EINVAL; - goto err; - } - - main_dev->subdev_dev[hw_idx] = subdev_dev; - subdev_dev->hw_idx = hw_idx; - subdev_dev->main_dev = main_dev; - subdev_dev->reg_base[VDEC_HW_SYS] = main_dev->reg_base[VDEC_HW_SYS]; - set_bit(subdev_dev->hw_idx, main_dev->subdev_bitmap); - - ret = mtk_vdec_hw_init_irq(subdev_dev); - if (ret) - goto err; - - subdev_dev->reg_base[VDEC_HW_MISC] = - devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC])) { - ret = PTR_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC]); - goto err; - } - - if (!main_dev->subdev_prob_done) - main_dev->subdev_prob_done = mtk_vdec_hw_prob_done; - - platform_set_drvdata(pdev, subdev_dev); - return 0; -err: - pm_runtime_disable(subdev_dev->pm.dev); - return ret; -} - -static struct platform_driver mtk_vdec_driver = { - .probe = mtk_vdec_hw_probe, - .driver = { - .name = "mtk-vdec-comp", - .of_match_table = mtk_vdec_hw_match, - }, -}; -module_platform_driver(mtk_vdec_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Mediatek video decoder hardware driver"); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.h deleted file mode 100644 index a63e4b1b81c3..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_hw.h +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2021 MediaTek Inc. - * Author: Yunfei Dong - */ - -#ifndef _MTK_VCODEC_DEC_HW_H_ -#define _MTK_VCODEC_DEC_HW_H_ - -#include -#include - -#include "mtk_vcodec_drv.h" - -#define VDEC_HW_ACTIVE 0x10 -#define VDEC_IRQ_CFG 0x11 -#define VDEC_IRQ_CLR 0x10 -#define VDEC_IRQ_CFG_REG 0xa4 - -/** - * enum mtk_vdec_hw_reg_idx - subdev hardware register base index - * @VDEC_HW_SYS : vdec soc register index - * @VDEC_HW_MISC: vdec misc register index - * @VDEC_HW_MAX : vdec supported max register index - */ -enum mtk_vdec_hw_reg_idx { - VDEC_HW_SYS, - VDEC_HW_MISC, - VDEC_HW_MAX -}; - -/** - * struct mtk_vdec_hw_dev - vdec hardware driver data - * @plat_dev: platform device - * @main_dev: main device - * @reg_base: mapped address of MTK Vcodec registers. - * - * @curr_ctx: the context that is waiting for codec hardware - * - * @dec_irq : decoder irq resource - * @pm : power management control - * @hw_idx : each hardware index - */ -struct mtk_vdec_hw_dev { - struct platform_device *plat_dev; - struct mtk_vcodec_dev *main_dev; - void __iomem *reg_base[VDEC_HW_MAX]; - - struct mtk_vcodec_ctx *curr_ctx; - - int dec_irq; - struct mtk_vcodec_pm pm; - int hw_idx; -}; - -#endif /* _MTK_VCODEC_DEC_HW_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c deleted file mode 100644 index 7e0c2644bf7b..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Tiffany Lin - */ - -#include -#include -#include -#include -#include - -#include "mtk_vcodec_dec_hw.h" -#include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_util.h" - -int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm) -{ - struct mtk_vcodec_clk *dec_clk; - struct mtk_vcodec_clk_info *clk_info; - int i = 0, ret; - - dec_clk = &pm->vdec_clk; - pm->dev = &pdev->dev; - - dec_clk->clk_num = - of_property_count_strings(pdev->dev.of_node, "clock-names"); - if (dec_clk->clk_num > 0) { - dec_clk->clk_info = devm_kcalloc(&pdev->dev, - dec_clk->clk_num, sizeof(*clk_info), - GFP_KERNEL); - if (!dec_clk->clk_info) - return -ENOMEM; - } else { - mtk_v4l2_err("Failed to get vdec clock count"); - return -EINVAL; - } - - for (i = 0; i < dec_clk->clk_num; i++) { - clk_info = &dec_clk->clk_info[i]; - ret = of_property_read_string_index(pdev->dev.of_node, - "clock-names", i, &clk_info->clk_name); - if (ret) { - mtk_v4l2_err("Failed to get clock name id = %d", i); - return ret; - } - clk_info->vcodec_clk = devm_clk_get(&pdev->dev, - clk_info->clk_name); - if (IS_ERR(clk_info->vcodec_clk)) { - mtk_v4l2_err("devm_clk_get (%d)%s fail", i, - clk_info->clk_name); - return PTR_ERR(clk_info->vcodec_clk); - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(mtk_vcodec_init_dec_clk); - -int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx) -{ - struct mtk_vdec_hw_dev *subdev_dev; - struct mtk_vcodec_pm *pm; - int ret; - - if (vdec_dev->vdec_pdata->is_subdev_supported) { - subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); - if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev\n"); - return -EINVAL; - } - pm = &subdev_dev->pm; - } else { - pm = &vdec_dev->pm; - } - - ret = pm_runtime_resume_and_get(pm->dev); - if (ret) - mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); - - return ret; -} -EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_on); - -void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx) -{ - struct mtk_vdec_hw_dev *subdev_dev; - struct mtk_vcodec_pm *pm; - int ret; - - if (vdec_dev->vdec_pdata->is_subdev_supported) { - subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); - if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev\n"); - return; - } - pm = &subdev_dev->pm; - } else { - pm = &vdec_dev->pm; - } - - ret = pm_runtime_put_sync(pm->dev); - if (ret) - mtk_v4l2_err("pm_runtime_put_sync fail %d", ret); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_off); - -void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx) -{ - struct mtk_vdec_hw_dev *subdev_dev; - struct mtk_vcodec_pm *pm; - struct mtk_vcodec_clk *dec_clk; - int ret, i; - - if (vdec_dev->vdec_pdata->is_subdev_supported) { - subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); - if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev\n"); - return; - } - pm = &subdev_dev->pm; - enable_irq(subdev_dev->dec_irq); - } else { - pm = &vdec_dev->pm; - enable_irq(vdec_dev->dec_irq); - } - - dec_clk = &pm->vdec_clk; - for (i = 0; i < dec_clk->clk_num; i++) { - ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk); - if (ret) { - mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i, - dec_clk->clk_info[i].clk_name, ret); - goto error; - } - } - - return; -error: - for (i -= 1; i >= 0; i--) - clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_on); - -void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx) -{ - struct mtk_vdec_hw_dev *subdev_dev; - struct mtk_vcodec_pm *pm; - struct mtk_vcodec_clk *dec_clk; - int i; - - if (vdec_dev->vdec_pdata->is_subdev_supported) { - subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); - if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev\n"); - return; - } - pm = &subdev_dev->pm; - disable_irq(subdev_dev->dec_irq); - } else { - pm = &vdec_dev->pm; - disable_irq(vdec_dev->dec_irq); - } - - dec_clk = &pm->vdec_clk; - for (i = dec_clk->clk_num - 1; i >= 0; i--) - clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_off); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h deleted file mode 100644 index 3cc721bbfaf6..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Tiffany Lin - */ - -#ifndef _MTK_VCODEC_DEC_PM_H_ -#define _MTK_VCODEC_DEC_PM_H_ - -#include "mtk_vcodec_drv.h" - -int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm); - -int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx); -void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx); -void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx); -void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx); - -#endif /* _MTK_VCODEC_DEC_PM_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c deleted file mode 100644 index 04ca43c77e5f..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c +++ /dev/null @@ -1,630 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_dec.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_dec_pm.h" -#include "vdec_drv_if.h" - -static const struct mtk_video_fmt mtk_video_formats[] = { - { - .fourcc = V4L2_PIX_FMT_H264, - .type = MTK_FMT_DEC, - .num_planes = 1, - .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, - }, - { - .fourcc = V4L2_PIX_FMT_VP8, - .type = MTK_FMT_DEC, - .num_planes = 1, - .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, - }, - { - .fourcc = V4L2_PIX_FMT_VP9, - .type = MTK_FMT_DEC, - .num_planes = 1, - .flags = V4L2_FMT_FLAG_DYN_RESOLUTION, - }, - { - .fourcc = V4L2_PIX_FMT_MT21C, - .type = MTK_FMT_FRAME, - .num_planes = 2, - }, -}; - -#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) -#define DEFAULT_OUT_FMT_IDX 0 -#define DEFAULT_CAP_FMT_IDX 3 - -static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = { - { - .fourcc = V4L2_PIX_FMT_H264, - .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, - MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, - }, - { - .fourcc = V4L2_PIX_FMT_VP8, - .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, - MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, - }, - { - .fourcc = V4L2_PIX_FMT_VP9, - .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, - MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, - }, -}; - -#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_vdec_framesizes) - -/* - * This function tries to clean all display buffers, the buffers will return - * in display order. - * Note the buffers returned from codec driver may still be in driver's - * reference list. - */ -static struct vb2_buffer *get_display_buffer(struct mtk_vcodec_ctx *ctx) -{ - struct vdec_fb *disp_frame_buffer = NULL; - struct mtk_video_dec_buf *dstbuf; - struct vb2_v4l2_buffer *vb; - - mtk_v4l2_debug(3, "[%d]", ctx->id); - if (vdec_if_get_param(ctx, GET_PARAM_DISP_FRAME_BUFFER, - &disp_frame_buffer)) { - mtk_v4l2_err("[%d]Cannot get param : GET_PARAM_DISP_FRAME_BUFFER", ctx->id); - return NULL; - } - - if (!disp_frame_buffer) { - mtk_v4l2_debug(3, "No display frame buffer"); - return NULL; - } - - dstbuf = container_of(disp_frame_buffer, struct mtk_video_dec_buf, - frame_buffer); - vb = &dstbuf->m2m_buf.vb; - mutex_lock(&ctx->lock); - if (dstbuf->used) { - vb2_set_plane_payload(&vb->vb2_buf, 0, ctx->picinfo.fb_sz[0]); - if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) - vb2_set_plane_payload(&vb->vb2_buf, 1, - ctx->picinfo.fb_sz[1]); - - mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to done_list %d", - ctx->id, disp_frame_buffer->status, - vb->vb2_buf.index, dstbuf->queued_in_vb2); - - v4l2_m2m_buf_done(vb, VB2_BUF_STATE_DONE); - ctx->decoded_frame_cnt++; - } - mutex_unlock(&ctx->lock); - return &vb->vb2_buf; -} - -/* - * This function tries to clean all capture buffers that are not used as - * reference buffers by codec driver any more - * In this case, we need re-queue buffer to vb2 buffer if user space - * already returns this buffer to v4l2 or this buffer is just the output of - * previous sps/pps/resolution change decode, or do nothing if user - * space still owns this buffer - */ -static struct vb2_buffer *get_free_buffer(struct mtk_vcodec_ctx *ctx) -{ - struct mtk_video_dec_buf *dstbuf; - struct vdec_fb *free_frame_buffer = NULL; - struct vb2_v4l2_buffer *vb; - - if (vdec_if_get_param(ctx, GET_PARAM_FREE_FRAME_BUFFER, - &free_frame_buffer)) { - mtk_v4l2_err("[%d] Error!! Cannot get param", ctx->id); - return NULL; - } - if (!free_frame_buffer) { - mtk_v4l2_debug(3, " No free frame buffer"); - return NULL; - } - - mtk_v4l2_debug(3, "[%d] tmp_frame_addr = 0x%p", ctx->id, - free_frame_buffer); - - dstbuf = container_of(free_frame_buffer, struct mtk_video_dec_buf, - frame_buffer); - vb = &dstbuf->m2m_buf.vb; - - mutex_lock(&ctx->lock); - if (dstbuf->used) { - if (dstbuf->queued_in_vb2 && dstbuf->queued_in_v4l2 && - free_frame_buffer->status == FB_ST_FREE) { - /* - * After decode sps/pps or non-display buffer, we don't - * need to return capture buffer to user space, but - * just re-queue this capture buffer to vb2 queue. - * This reduce overheads that dq/q unused capture - * buffer. In this case, queued_in_vb2 = true. - */ - mtk_v4l2_debug(2, "[%d]status=%x queue id=%d to rdy_queue %d", - ctx->id, free_frame_buffer->status, - vb->vb2_buf.index, dstbuf->queued_in_vb2); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); - } else if (!dstbuf->queued_in_vb2 && dstbuf->queued_in_v4l2) { - /* - * If buffer in v4l2 driver but not in vb2 queue yet, - * and we get this buffer from free_list, it means - * that codec driver do not use this buffer as - * reference buffer anymore. We should q buffer to vb2 - * queue, so later work thread could get this buffer - * for decode. In this case, queued_in_vb2 = false - * means this buffer is not from previous decode - * output. - */ - mtk_v4l2_debug(2, - "[%d]status=%x queue id=%d to rdy_queue", - ctx->id, free_frame_buffer->status, - vb->vb2_buf.index); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); - dstbuf->queued_in_vb2 = true; - } else { - /* - * Codec driver do not need to reference this capture - * buffer and this buffer is not in v4l2 driver. - * Then we don't need to do any thing, just add log when - * we need to debug buffer flow. - * When this buffer q from user space, it could - * directly q to vb2 buffer - */ - mtk_v4l2_debug(3, "[%d]status=%x err queue id=%d %d %d", - ctx->id, free_frame_buffer->status, - vb->vb2_buf.index, dstbuf->queued_in_vb2, - dstbuf->queued_in_v4l2); - } - dstbuf->used = false; - } - mutex_unlock(&ctx->lock); - return &vb->vb2_buf; -} - -static void clean_display_buffer(struct mtk_vcodec_ctx *ctx) -{ - while (get_display_buffer(ctx)) - ; -} - -static void clean_free_buffer(struct mtk_vcodec_ctx *ctx) -{ - while (get_free_buffer(ctx)) - ; -} - -static void mtk_vdec_queue_res_chg_event(struct mtk_vcodec_ctx *ctx) -{ - static const struct v4l2_event ev_src_ch = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, - }; - - mtk_v4l2_debug(1, "[%d]", ctx->id); - v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); -} - -static int mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) -{ - bool res_chg; - int ret; - - ret = vdec_if_decode(ctx, NULL, NULL, &res_chg); - if (ret) - mtk_v4l2_err("DecodeFinal failed, ret=%d", ret); - - clean_display_buffer(ctx); - clean_free_buffer(ctx); - - return 0; -} - -static void mtk_vdec_update_fmt(struct mtk_vcodec_ctx *ctx, - unsigned int pixelformat) -{ - const struct mtk_video_fmt *fmt; - struct mtk_q_data *dst_q_data; - unsigned int k; - - dst_q_data = &ctx->q_data[MTK_Q_DATA_DST]; - for (k = 0; k < NUM_FORMATS; k++) { - fmt = &mtk_video_formats[k]; - if (fmt->fourcc == pixelformat) { - mtk_v4l2_debug(1, "Update cap fourcc(%d -> %d)", - dst_q_data->fmt->fourcc, pixelformat); - dst_q_data->fmt = fmt; - return; - } - } - - mtk_v4l2_err("Cannot get fourcc(%d), using init value", pixelformat); -} - -static int mtk_vdec_pic_info_update(struct mtk_vcodec_ctx *ctx) -{ - unsigned int dpbsize = 0; - int ret; - - if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, - &ctx->last_decoded_picinfo)) { - mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", ctx->id); - return -EINVAL; - } - - if (ctx->last_decoded_picinfo.pic_w == 0 || - ctx->last_decoded_picinfo.pic_h == 0 || - ctx->last_decoded_picinfo.buf_w == 0 || - ctx->last_decoded_picinfo.buf_h == 0) { - mtk_v4l2_err("Cannot get correct pic info"); - return -EINVAL; - } - - if (ctx->last_decoded_picinfo.cap_fourcc != ctx->picinfo.cap_fourcc && - ctx->picinfo.cap_fourcc != 0) - mtk_vdec_update_fmt(ctx, ctx->picinfo.cap_fourcc); - - if (ctx->last_decoded_picinfo.pic_w == ctx->picinfo.pic_w || - ctx->last_decoded_picinfo.pic_h == ctx->picinfo.pic_h) - return 0; - - mtk_v4l2_debug(1, "[%d]-> new(%d,%d), old(%d,%d), real(%d,%d)", ctx->id, - ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, ctx->picinfo.pic_w, - ctx->picinfo.pic_h, ctx->last_decoded_picinfo.buf_w, - ctx->last_decoded_picinfo.buf_h); - - ret = vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpbsize); - if (dpbsize == 0) - mtk_v4l2_err("Incorrect dpb size, ret=%d", ret); - - ctx->dpb_size = dpbsize; - - return ret; -} - -static void mtk_vdec_worker(struct work_struct *work) -{ - struct mtk_vcodec_ctx *ctx = - container_of(work, struct mtk_vcodec_ctx, decode_work); - struct mtk_vcodec_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct mtk_vcodec_mem buf; - struct vdec_fb *pfb; - bool res_chg = false; - int ret; - struct mtk_video_dec_buf *dst_buf_info, *src_buf_info; - - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - if (!src_buf) { - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_debug(1, "[%d] src_buf empty!!", ctx->id); - return; - } - - dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - if (!dst_buf) { - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_debug(1, "[%d] dst_buf empty!!", ctx->id); - return; - } - - dst_buf_info = - container_of(dst_buf, struct mtk_video_dec_buf, m2m_buf.vb); - - pfb = &dst_buf_info->frame_buffer; - pfb->base_y.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - pfb->base_y.dma_addr = - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - pfb->base_y.size = ctx->picinfo.fb_sz[0]; - - pfb->base_c.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 1); - pfb->base_c.dma_addr = - vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 1); - pfb->base_c.size = ctx->picinfo.fb_sz[1]; - pfb->status = 0; - mtk_v4l2_debug(3, "===>[%d] vdec_if_decode() ===>", ctx->id); - - mtk_v4l2_debug(3, - "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx", - dst_buf->vb2_buf.index, pfb, pfb->base_y.va, - &pfb->base_y.dma_addr, &pfb->base_c.dma_addr, pfb->base_y.size); - - if (src_buf == &ctx->empty_flush_buf.vb) { - mtk_v4l2_debug(1, "Got empty flush input buffer."); - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - - /* update dst buf status */ - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - mutex_lock(&ctx->lock); - dst_buf_info->used = false; - mutex_unlock(&ctx->lock); - - vdec_if_decode(ctx, NULL, NULL, &res_chg); - clean_display_buffer(ctx); - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) - vb2_set_plane_payload(&dst_buf->vb2_buf, 1, 0); - dst_buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); - clean_free_buffer(ctx); - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - return; - } - - src_buf_info = - container_of(src_buf, struct mtk_video_dec_buf, m2m_buf.vb); - - buf.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); - buf.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - buf.size = (size_t)src_buf->vb2_buf.planes[0].bytesused; - if (!buf.va) { - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_err("[%d] id=%d src_addr is NULL!!", ctx->id, - src_buf->vb2_buf.index); - return; - } - mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", - ctx->id, buf.va, &buf.dma_addr, buf.size, src_buf); - dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; - dst_buf->timecode = src_buf->timecode; - mutex_lock(&ctx->lock); - dst_buf_info->used = true; - mutex_unlock(&ctx->lock); - src_buf_info->used = true; - - ret = vdec_if_decode(ctx, &buf, pfb, &res_chg); - - if (ret) { - mtk_v4l2_err(" <===[%d], src_buf[%d] sz=0x%zx pts=%llu dst_buf[%d] vdec_if_decode() ret=%d res_chg=%d===>", - ctx->id, src_buf->vb2_buf.index, buf.size, - src_buf->vb2_buf.timestamp, dst_buf->vb2_buf.index, ret, res_chg); - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - if (ret == -EIO) { - mutex_lock(&ctx->lock); - src_buf_info->error = true; - mutex_unlock(&ctx->lock); - } - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - } else if (!res_chg) { - /* - * we only return src buffer with VB2_BUF_STATE_DONE - * when decode success without resolution change - */ - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - clean_display_buffer(ctx); - clean_free_buffer(ctx); - - if (!ret && res_chg) { - mtk_vdec_pic_info_update(ctx); - /* - * On encountering a resolution change in the stream. - * The driver must first process and decode all - * remaining buffers from before the resolution change - * point, so call flush decode here - */ - mtk_vdec_flush_decoder(ctx); - /* - * After all buffers containing decoded frames from - * before the resolution change point ready to be - * dequeued on the CAPTURE queue, the driver sends a - * V4L2_EVENT_SOURCE_CHANGE event for source change - * type V4L2_EVENT_SRC_CH_RESOLUTION - */ - mtk_vdec_queue_res_chg_event(ctx); - } - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); -} - -static void vb2ops_vdec_stateful_buf_queue(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *src_buf; - struct mtk_vcodec_mem src_mem; - bool res_chg = false; - int ret; - unsigned int dpbsize = 1, i; - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vb2_v4l2; - struct mtk_q_data *dst_q_data; - - mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, - vb->vb2_queue->type, vb->index, vb); - /* - * check if this buffer is ready to be used after decode - */ - if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - struct mtk_video_dec_buf *buf; - - vb2_v4l2 = to_vb2_v4l2_buffer(vb); - buf = container_of(vb2_v4l2, struct mtk_video_dec_buf, - m2m_buf.vb); - mutex_lock(&ctx->lock); - if (!buf->used) { - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2); - buf->queued_in_vb2 = true; - buf->queued_in_v4l2 = true; - } else { - buf->queued_in_vb2 = false; - buf->queued_in_v4l2 = true; - } - mutex_unlock(&ctx->lock); - return; - } - - v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); - - if (ctx->state != MTK_STATE_INIT) { - mtk_v4l2_debug(3, "[%d] already init driver %d", ctx->id, - ctx->state); - return; - } - - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - if (!src_buf) { - mtk_v4l2_err("No src buffer"); - return; - } - - if (src_buf == &ctx->empty_flush_buf.vb) { - /* This shouldn't happen. Just in case. */ - mtk_v4l2_err("Invalid flush buffer."); - v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - return; - } - - src_mem.va = vb2_plane_vaddr(&src_buf->vb2_buf, 0); - src_mem.dma_addr = vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, 0); - src_mem.size = (size_t)src_buf->vb2_buf.planes[0].bytesused; - mtk_v4l2_debug(2, "[%d] buf id=%d va=%p dma=%pad size=%zx", ctx->id, - src_buf->vb2_buf.index, src_mem.va, &src_mem.dma_addr, - src_mem.size); - - ret = vdec_if_decode(ctx, &src_mem, NULL, &res_chg); - if (ret || !res_chg) { - /* - * fb == NULL means to parse SPS/PPS header or - * resolution info in src_mem. Decode can fail - * if there is no SPS header or picture info - * in bs - */ - - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - if (ret == -EIO) { - mtk_v4l2_err("[%d] Unrecoverable error in vdec_if_decode.", ctx->id); - ctx->state = MTK_STATE_ABORT; - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - } else { - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - } - mtk_v4l2_debug(ret ? 0 : 1, - "[%d] vdec_if_decode() src_buf=%d, size=%zu, fail=%d, res_chg=%d", - ctx->id, src_buf->vb2_buf.index, src_mem.size, ret, res_chg); - return; - } - - if (vdec_if_get_param(ctx, GET_PARAM_PIC_INFO, &ctx->picinfo)) { - mtk_v4l2_err("[%d]Error!! Cannot get param : GET_PARAM_PICTURE_INFO ERR", ctx->id); - return; - } - - ctx->last_decoded_picinfo = ctx->picinfo; - dst_q_data = &ctx->q_data[MTK_Q_DATA_DST]; - for (i = 0; i < dst_q_data->fmt->num_planes; i++) { - dst_q_data->sizeimage[i] = ctx->picinfo.fb_sz[i]; - dst_q_data->bytesperline[i] = ctx->picinfo.buf_w; - } - - mtk_v4l2_debug(2, "[%d] vdec_if_init() OK wxh=%dx%d pic wxh=%dx%d sz[0]=0x%x sz[1]=0x%x", - ctx->id, ctx->picinfo.buf_w, ctx->picinfo.buf_h, ctx->picinfo.pic_w, - ctx->picinfo.pic_h, dst_q_data->sizeimage[0], dst_q_data->sizeimage[1]); - - ret = vdec_if_get_param(ctx, GET_PARAM_DPB_SIZE, &dpbsize); - if (dpbsize == 0) - mtk_v4l2_err("[%d] GET_PARAM_DPB_SIZE fail=%d", ctx->id, ret); - - ctx->dpb_size = dpbsize; - ctx->state = MTK_STATE_HEADER; - mtk_v4l2_debug(1, "[%d] dpbsize=%d", ctx->id, ctx->dpb_size); - - mtk_vdec_queue_res_chg_event(ctx); -} - -static int mtk_vdec_g_v_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: - if (ctx->state >= MTK_STATE_HEADER) { - ctrl->val = ctx->dpb_size; - } else { - mtk_v4l2_debug(0, "Seqinfo not ready"); - ctrl->val = 0; - } - break; - default: - ret = -EINVAL; - } - return ret; -} - -static const struct v4l2_ctrl_ops mtk_vcodec_dec_ctrl_ops = { - .g_volatile_ctrl = mtk_vdec_g_v_ctrl, -}; - -static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) -{ - struct v4l2_ctrl *ctrl; - - v4l2_ctrl_handler_init(&ctx->ctrl_hdl, 1); - - ctrl = v4l2_ctrl_new_std(&ctx->ctrl_hdl, &mtk_vcodec_dec_ctrl_ops, - V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 0, 32, 1, 1); - ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; - v4l2_ctrl_new_std_menu(&ctx->ctrl_hdl, &mtk_vcodec_dec_ctrl_ops, - V4L2_CID_MPEG_VIDEO_VP9_PROFILE, - V4L2_MPEG_VIDEO_VP9_PROFILE_0, 0, - V4L2_MPEG_VIDEO_VP9_PROFILE_0); - /* - * H264. Baseline / Extended decoding is not supported. - */ - v4l2_ctrl_new_std_menu(&ctx->ctrl_hdl, &mtk_vcodec_dec_ctrl_ops, - V4L2_CID_MPEG_VIDEO_H264_PROFILE, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), - V4L2_MPEG_VIDEO_H264_PROFILE_MAIN); - - if (ctx->ctrl_hdl.error) { - mtk_v4l2_err("Adding control failed %d", ctx->ctrl_hdl.error); - return ctx->ctrl_hdl.error; - } - - v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); - return 0; -} - -static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) -{ -} - -static struct vb2_ops mtk_vdec_frame_vb2_ops = { - .queue_setup = vb2ops_vdec_queue_setup, - .buf_prepare = vb2ops_vdec_buf_prepare, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = vb2ops_vdec_start_streaming, - - .buf_queue = vb2ops_vdec_stateful_buf_queue, - .buf_init = vb2ops_vdec_buf_init, - .buf_finish = vb2ops_vdec_buf_finish, - .stop_streaming = vb2ops_vdec_stop_streaming, -}; - -const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata = { - .chip = MTK_MT8173, - .init_vdec_params = mtk_init_vdec_params, - .ctrls_setup = mtk_vcodec_dec_ctrls_setup, - .vdec_vb2_ops = &mtk_vdec_frame_vb2_ops, - .vdec_formats = mtk_video_formats, - .num_formats = NUM_FORMATS, - .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], - .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], - .vdec_framesizes = mtk_vdec_framesizes, - .num_framesizes = NUM_SUPPORTED_FRAMESIZE, - .worker = mtk_vdec_worker, - .flush_decoder = mtk_vdec_flush_decoder, - .is_subdev_supported = false, - .hw_arch = MTK_VDEC_PURE_SINGLE_CORE, -}; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c deleted file mode 100644 index 23d997ac114d..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c +++ /dev/null @@ -1,380 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_dec.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_dec_pm.h" -#include "vdec_drv_if.h" - -/** - * struct mtk_stateless_control - CID control type - * @cfg: control configuration - * @codec_type: codec type (V4L2 pixel format) for CID control type - */ -struct mtk_stateless_control { - struct v4l2_ctrl_config cfg; - int codec_type; -}; - -static const struct mtk_stateless_control mtk_stateless_controls[] = { - { - .cfg = { - .id = V4L2_CID_STATELESS_H264_SPS, - }, - .codec_type = V4L2_PIX_FMT_H264_SLICE, - }, - { - .cfg = { - .id = V4L2_CID_STATELESS_H264_PPS, - }, - .codec_type = V4L2_PIX_FMT_H264_SLICE, - }, - { - .cfg = { - .id = V4L2_CID_STATELESS_H264_SCALING_MATRIX, - }, - .codec_type = V4L2_PIX_FMT_H264_SLICE, - }, - { - .cfg = { - .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, - }, - .codec_type = V4L2_PIX_FMT_H264_SLICE, - }, - { - .cfg = { - .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE, - .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN, - .max = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - .menu_skip_mask = - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) | - BIT(V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED), - }, - .codec_type = V4L2_PIX_FMT_H264_SLICE, - }, - { - .cfg = { - .id = V4L2_CID_STATELESS_H264_DECODE_MODE, - .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED, - }, - .codec_type = V4L2_PIX_FMT_H264_SLICE, - }, - { - .cfg = { - .id = V4L2_CID_STATELESS_H264_START_CODE, - .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B, - }, - .codec_type = V4L2_PIX_FMT_H264_SLICE, - } -}; - -#define NUM_CTRLS ARRAY_SIZE(mtk_stateless_controls) - -static const struct mtk_video_fmt mtk_video_formats[] = { - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .type = MTK_FMT_DEC, - .num_planes = 1, - }, - { - .fourcc = V4L2_PIX_FMT_MM21, - .type = MTK_FMT_FRAME, - .num_planes = 2, - }, -}; - -#define NUM_FORMATS ARRAY_SIZE(mtk_video_formats) -#define DEFAULT_OUT_FMT_IDX 0 -#define DEFAULT_CAP_FMT_IDX 1 - -static const struct mtk_codec_framesizes mtk_vdec_framesizes[] = { - { - .fourcc = V4L2_PIX_FMT_H264_SLICE, - .stepwise = { MTK_VDEC_MIN_W, MTK_VDEC_MAX_W, 16, - MTK_VDEC_MIN_H, MTK_VDEC_MAX_H, 16 }, - }, -}; - -#define NUM_SUPPORTED_FRAMESIZE ARRAY_SIZE(mtk_vdec_framesizes) - -static void mtk_vdec_stateless_set_dst_payload(struct mtk_vcodec_ctx *ctx, - struct vdec_fb *fb) -{ - struct mtk_video_dec_buf *vdec_frame_buf = - container_of(fb, struct mtk_video_dec_buf, frame_buffer); - struct vb2_v4l2_buffer *vb = &vdec_frame_buf->m2m_buf.vb; - unsigned int cap_y_size = ctx->q_data[MTK_Q_DATA_DST].sizeimage[0]; - - vb2_set_plane_payload(&vb->vb2_buf, 0, cap_y_size); - if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) { - unsigned int cap_c_size = - ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]; - - vb2_set_plane_payload(&vb->vb2_buf, 1, cap_c_size); - } -} - -static struct vdec_fb *vdec_get_cap_buffer(struct mtk_vcodec_ctx *ctx, - struct vb2_v4l2_buffer *vb2_v4l2) -{ - struct mtk_video_dec_buf *framebuf = - container_of(vb2_v4l2, struct mtk_video_dec_buf, m2m_buf.vb); - struct vdec_fb *pfb = &framebuf->frame_buffer; - struct vb2_buffer *dst_buf = &vb2_v4l2->vb2_buf; - - pfb->base_y.va = NULL; - pfb->base_y.dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0); - pfb->base_y.size = ctx->q_data[MTK_Q_DATA_DST].sizeimage[0]; - - if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) { - pfb->base_c.va = NULL; - pfb->base_c.dma_addr = - vb2_dma_contig_plane_dma_addr(dst_buf, 1); - pfb->base_c.size = ctx->q_data[MTK_Q_DATA_DST].sizeimage[1]; - } - mtk_v4l2_debug(1, "id=%d Framebuf pfb=%p VA=%p Y_DMA=%pad C_DMA=%pad Size=%zx frame_count = %d", - dst_buf->index, pfb, pfb->base_y.va, &pfb->base_y.dma_addr, - &pfb->base_c.dma_addr, pfb->base_y.size, ctx->decoded_frame_cnt); - - return pfb; -} - -static void vb2ops_vdec_buf_request_complete(struct vb2_buffer *vb) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->ctrl_hdl); -} - -static void mtk_vdec_worker(struct work_struct *work) -{ - struct mtk_vcodec_ctx *ctx = - container_of(work, struct mtk_vcodec_ctx, decode_work); - struct mtk_vcodec_dev *dev = ctx->dev; - struct vb2_v4l2_buffer *vb2_v4l2_src, *vb2_v4l2_dst; - struct vb2_buffer *vb2_src; - struct mtk_vcodec_mem *bs_src; - struct mtk_video_dec_buf *dec_buf_src; - struct media_request *src_buf_req; - struct vdec_fb *dst_buf; - bool res_chg = false; - int ret; - - vb2_v4l2_src = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - if (!vb2_v4l2_src) { - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_debug(1, "[%d] no available source buffer", ctx->id); - return; - } - - vb2_v4l2_dst = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - if (!vb2_v4l2_dst) { - v4l2_m2m_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx); - mtk_v4l2_debug(1, "[%d] no available destination buffer", ctx->id); - return; - } - - vb2_src = &vb2_v4l2_src->vb2_buf; - dec_buf_src = container_of(vb2_v4l2_src, struct mtk_video_dec_buf, - m2m_buf.vb); - bs_src = &dec_buf_src->bs_buffer; - - mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, - vb2_src->vb2_queue->type, vb2_src->index, vb2_src); - - bs_src->va = NULL; - bs_src->dma_addr = vb2_dma_contig_plane_dma_addr(vb2_src, 0); - bs_src->size = (size_t)vb2_src->planes[0].bytesused; - - mtk_v4l2_debug(3, "[%d] Bitstream VA=%p DMA=%pad Size=%zx vb=%p", - ctx->id, bs_src->va, &bs_src->dma_addr, bs_src->size, vb2_src); - /* Apply request controls. */ - src_buf_req = vb2_src->req_obj.req; - if (src_buf_req) - v4l2_ctrl_request_setup(src_buf_req, &ctx->ctrl_hdl); - else - mtk_v4l2_err("vb2 buffer media request is NULL"); - - dst_buf = vdec_get_cap_buffer(ctx, vb2_v4l2_dst); - v4l2_m2m_buf_copy_metadata(vb2_v4l2_src, vb2_v4l2_dst, true); - ret = vdec_if_decode(ctx, bs_src, dst_buf, &res_chg); - if (ret) { - mtk_v4l2_err(" <===[%d], src_buf[%d] sz=0x%zx pts=%llu vdec_if_decode() ret=%d res_chg=%d===>", - ctx->id, vb2_src->index, bs_src->size, - vb2_src->timestamp, ret, res_chg); - if (ret == -EIO) { - mutex_lock(&ctx->lock); - dec_buf_src->error = true; - mutex_unlock(&ctx->lock); - } - } - - mtk_vdec_stateless_set_dst_payload(ctx, dst_buf); - - v4l2_m2m_buf_done_and_job_finish(dev->m2m_dev_dec, ctx->m2m_ctx, - ret ? VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - - v4l2_ctrl_request_complete(src_buf_req, &ctx->ctrl_hdl); -} - -static void vb2ops_vdec_stateless_buf_queue(struct vb2_buffer *vb) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vb2_v4l2 = to_vb2_v4l2_buffer(vb); - - mtk_v4l2_debug(3, "[%d] (%d) id=%d, vb=%p", ctx->id, vb->vb2_queue->type, vb->index, vb); - - mutex_lock(&ctx->lock); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb2_v4l2); - mutex_unlock(&ctx->lock); - if (vb->vb2_queue->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return; - - /* If an OUTPUT buffer, we may need to update the state */ - if (ctx->state == MTK_STATE_INIT) { - ctx->state = MTK_STATE_HEADER; - mtk_v4l2_debug(1, "Init driver from init to header."); - } else { - mtk_v4l2_debug(3, "[%d] already init driver %d", ctx->id, ctx->state); - } -} - -static int mtk_vdec_flush_decoder(struct mtk_vcodec_ctx *ctx) -{ - bool res_chg; - - return vdec_if_decode(ctx, NULL, NULL, &res_chg); -} - -static int mtk_vcodec_dec_ctrls_setup(struct mtk_vcodec_ctx *ctx) -{ - unsigned int i; - - v4l2_ctrl_handler_init(&ctx->ctrl_hdl, NUM_CTRLS); - if (ctx->ctrl_hdl.error) { - mtk_v4l2_err("v4l2_ctrl_handler_init failed\n"); - return ctx->ctrl_hdl.error; - } - - for (i = 0; i < NUM_CTRLS; i++) { - struct v4l2_ctrl_config cfg = mtk_stateless_controls[i].cfg; - - v4l2_ctrl_new_custom(&ctx->ctrl_hdl, &cfg, NULL); - if (ctx->ctrl_hdl.error) { - mtk_v4l2_err("Adding control %d failed %d", i, ctx->ctrl_hdl.error); - return ctx->ctrl_hdl.error; - } - } - - v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); - - return 0; -} - -static int fops_media_request_validate(struct media_request *mreq) -{ - const unsigned int buffer_cnt = vb2_request_buffer_cnt(mreq); - - switch (buffer_cnt) { - case 1: - /* We expect exactly one buffer with the request */ - break; - case 0: - mtk_v4l2_debug(1, "No buffer provided with the request"); - return -ENOENT; - default: - mtk_v4l2_debug(1, "Too many buffers (%d) provided with the request", - buffer_cnt); - return -EINVAL; - } - - return vb2_request_validate(mreq); -} - -const struct media_device_ops mtk_vcodec_media_ops = { - .req_validate = fops_media_request_validate, - .req_queue = v4l2_m2m_request_queue, -}; - -static void mtk_init_vdec_params(struct mtk_vcodec_ctx *ctx) -{ - struct vb2_queue *src_vq; - - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - - /* Support request api for output plane */ - src_vq->supports_requests = true; - src_vq->requires_requests = true; -} - -static int vb2ops_vdec_out_buf_validate(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - vbuf->field = V4L2_FIELD_NONE; - return 0; -} - -static struct vb2_ops mtk_vdec_request_vb2_ops = { - .queue_setup = vb2ops_vdec_queue_setup, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = vb2ops_vdec_start_streaming, - .stop_streaming = vb2ops_vdec_stop_streaming, - - .buf_queue = vb2ops_vdec_stateless_buf_queue, - .buf_out_validate = vb2ops_vdec_out_buf_validate, - .buf_init = vb2ops_vdec_buf_init, - .buf_prepare = vb2ops_vdec_buf_prepare, - .buf_finish = vb2ops_vdec_buf_finish, - .buf_request_complete = vb2ops_vdec_buf_request_complete, -}; - -const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata = { - .chip = MTK_MT8183, - .init_vdec_params = mtk_init_vdec_params, - .ctrls_setup = mtk_vcodec_dec_ctrls_setup, - .vdec_vb2_ops = &mtk_vdec_request_vb2_ops, - .vdec_formats = mtk_video_formats, - .num_formats = NUM_FORMATS, - .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], - .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], - .vdec_framesizes = mtk_vdec_framesizes, - .num_framesizes = NUM_SUPPORTED_FRAMESIZE, - .uses_stateless_api = true, - .worker = mtk_vdec_worker, - .flush_decoder = mtk_vdec_flush_decoder, - .is_subdev_supported = false, - .hw_arch = MTK_VDEC_PURE_SINGLE_CORE, -}; - -/* This platform data is used for one lat and one core architecture. */ -const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata = { - .chip = MTK_MT8192, - .init_vdec_params = mtk_init_vdec_params, - .ctrls_setup = mtk_vcodec_dec_ctrls_setup, - .vdec_vb2_ops = &mtk_vdec_request_vb2_ops, - .vdec_formats = mtk_video_formats, - .num_formats = NUM_FORMATS, - .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX], - .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX], - .vdec_framesizes = mtk_vdec_framesizes, - .num_framesizes = NUM_SUPPORTED_FRAMESIZE, - .uses_stateless_api = true, - .worker = mtk_vdec_worker, - .flush_decoder = mtk_vdec_flush_decoder, - .is_subdev_supported = true, - .hw_arch = MTK_VDEC_LAT_SINGLE_CORE, -}; diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h deleted file mode 100644 index 813901c4be5e..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h +++ /dev/null @@ -1,537 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#ifndef _MTK_VCODEC_DRV_H_ -#define _MTK_VCODEC_DRV_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include "mtk_vcodec_util.h" -#include "vdec_msg_queue.h" - -#define MTK_VCODEC_DRV_NAME "mtk_vcodec_drv" -#define MTK_VCODEC_DEC_NAME "mtk-vcodec-dec" -#define MTK_VCODEC_ENC_NAME "mtk-vcodec-enc" -#define MTK_PLATFORM_STR "platform:mt8173" - -#define MTK_VCODEC_MAX_PLANES 3 -#define MTK_V4L2_BENCHMARK 0 -#define WAIT_INTR_TIMEOUT_MS 1000 -#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE) - -/* - * enum mtk_hw_reg_idx - MTK hw register base index - */ -enum mtk_hw_reg_idx { - VDEC_SYS, - VDEC_MISC, - VDEC_LD, - VDEC_TOP, - VDEC_CM, - VDEC_AD, - VDEC_AV, - VDEC_PP, - VDEC_HWD, - VDEC_HWQ, - VDEC_HWB, - VDEC_HWG, - NUM_MAX_VDEC_REG_BASE, - /* h264 encoder */ - VENC_SYS = NUM_MAX_VDEC_REG_BASE, - /* vp8 encoder */ - VENC_LT_SYS, - NUM_MAX_VCODEC_REG_BASE -}; - -/* - * enum mtk_instance_type - The type of an MTK Vcodec instance. - */ -enum mtk_instance_type { - MTK_INST_DECODER = 0, - MTK_INST_ENCODER = 1, -}; - -/** - * enum mtk_instance_state - The state of an MTK Vcodec instance. - * @MTK_STATE_FREE: default state when instance is created - * @MTK_STATE_INIT: vcodec instance is initialized - * @MTK_STATE_HEADER: vdec had sps/pps header parsed or venc - * had sps/pps header encoded - * @MTK_STATE_FLUSH: vdec is flushing. Only used by decoder - * @MTK_STATE_ABORT: vcodec should be aborted - */ -enum mtk_instance_state { - MTK_STATE_FREE = 0, - MTK_STATE_INIT = 1, - MTK_STATE_HEADER = 2, - MTK_STATE_FLUSH = 3, - MTK_STATE_ABORT = 4, -}; - -/* - * enum mtk_encode_param - General encoding parameters type - */ -enum mtk_encode_param { - MTK_ENCODE_PARAM_NONE = 0, - MTK_ENCODE_PARAM_BITRATE = (1 << 0), - MTK_ENCODE_PARAM_FRAMERATE = (1 << 1), - MTK_ENCODE_PARAM_INTRA_PERIOD = (1 << 2), - MTK_ENCODE_PARAM_FORCE_INTRA = (1 << 3), - MTK_ENCODE_PARAM_GOP_SIZE = (1 << 4), -}; - -enum mtk_fmt_type { - MTK_FMT_DEC = 0, - MTK_FMT_ENC = 1, - MTK_FMT_FRAME = 2, -}; - -/* - * enum mtk_vdec_hw_id - Hardware index used to separate - * different hardware - */ -enum mtk_vdec_hw_id { - MTK_VDEC_CORE, - MTK_VDEC_LAT0, - MTK_VDEC_LAT1, - MTK_VDEC_HW_MAX, -}; - -/* - * enum mtk_vdec_hw_count - Supported hardware count - */ -enum mtk_vdec_hw_count { - MTK_VDEC_NO_HW = 0, - MTK_VDEC_ONE_CORE, - MTK_VDEC_ONE_LAT_ONE_CORE, - MTK_VDEC_MAX_HW_COUNT, -}; - -/* - * struct mtk_video_fmt - Structure used to store information about pixelformats - */ -struct mtk_video_fmt { - u32 fourcc; - enum mtk_fmt_type type; - u32 num_planes; - u32 flags; -}; - -/* - * struct mtk_codec_framesizes - Structure used to store information about - * framesizes - */ -struct mtk_codec_framesizes { - u32 fourcc; - struct v4l2_frmsize_stepwise stepwise; -}; - -/* - * enum mtk_q_type - Type of queue - */ -enum mtk_q_type { - MTK_Q_DATA_SRC = 0, - MTK_Q_DATA_DST = 1, -}; - -/* - * struct mtk_q_data - Structure used to store information about queue - */ -struct mtk_q_data { - unsigned int visible_width; - unsigned int visible_height; - unsigned int coded_width; - unsigned int coded_height; - enum v4l2_field field; - unsigned int bytesperline[MTK_VCODEC_MAX_PLANES]; - unsigned int sizeimage[MTK_VCODEC_MAX_PLANES]; - const struct mtk_video_fmt *fmt; -}; - -/** - * struct mtk_enc_params - General encoding parameters - * @bitrate: target bitrate in bits per second - * @num_b_frame: number of b frames between p-frame - * @rc_frame: frame based rate control - * @rc_mb: macroblock based rate control - * @seq_hdr_mode: H.264 sequence header is encoded separately or joined - * with the first frame - * @intra_period: I frame period - * @gop_size: group of picture size, it's used as the intra frame period - * @framerate_num: frame rate numerator. ex: framerate_num=30 and - * framerate_denom=1 means FPS is 30 - * @framerate_denom: frame rate denominator. ex: framerate_num=30 and - * framerate_denom=1 means FPS is 30 - * @h264_max_qp: Max value for H.264 quantization parameter - * @h264_profile: V4L2 defined H.264 profile - * @h264_level: V4L2 defined H.264 level - * @force_intra: force/insert intra frame - */ -struct mtk_enc_params { - unsigned int bitrate; - unsigned int num_b_frame; - unsigned int rc_frame; - unsigned int rc_mb; - unsigned int seq_hdr_mode; - unsigned int intra_period; - unsigned int gop_size; - unsigned int framerate_num; - unsigned int framerate_denom; - unsigned int h264_max_qp; - unsigned int h264_profile; - unsigned int h264_level; - unsigned int force_intra; -}; - -/* - * struct mtk_vcodec_clk_info - Structure used to store clock name - */ -struct mtk_vcodec_clk_info { - const char *clk_name; - struct clk *vcodec_clk; -}; - -/* - * struct mtk_vcodec_clk - Structure used to store vcodec clock information - */ -struct mtk_vcodec_clk { - struct mtk_vcodec_clk_info *clk_info; - int clk_num; -}; - -/* - * struct mtk_vcodec_pm - Power management data structure - */ -struct mtk_vcodec_pm { - struct mtk_vcodec_clk vdec_clk; - struct mtk_vcodec_clk venc_clk; - struct device *dev; -}; - -/** - * struct vdec_pic_info - picture size information - * @pic_w: picture width - * @pic_h: picture height - * @buf_w: picture buffer width (64 aligned up from pic_w) - * @buf_h: picture buffer heiht (64 aligned up from pic_h) - * @fb_sz: bitstream size of each plane - * E.g. suppose picture size is 176x144, - * buffer size will be aligned to 176x160. - * @cap_fourcc: fourcc number(may changed when resolution change) - * @reserved: align struct to 64-bit in order to adjust 32-bit and 64-bit os. - */ -struct vdec_pic_info { - unsigned int pic_w; - unsigned int pic_h; - unsigned int buf_w; - unsigned int buf_h; - unsigned int fb_sz[VIDEO_MAX_PLANES]; - unsigned int cap_fourcc; - unsigned int reserved; -}; - -/** - * struct mtk_vcodec_ctx - Context (instance) private data. - * - * @type: type of the instance - decoder or encoder - * @dev: pointer to the mtk_vcodec_dev of the device - * @list: link to ctx_list of mtk_vcodec_dev - * @fh: struct v4l2_fh - * @m2m_ctx: pointer to the v4l2_m2m_ctx of the context - * @q_data: store information of input and output queue - * of the context - * @id: index of the context that this structure describes - * @state: state of the context - * @param_change: indicate encode parameter type - * @enc_params: encoding parameters - * @dec_if: hooked decoder driver interface - * @enc_if: hoooked encoder driver interface - * @drv_handle: driver handle for specific decode/encode instance - * - * @picinfo: store picture info after header parsing - * @dpb_size: store dpb count after header parsing - * @int_cond: variable used by the waitqueue - * @int_type: type of the last interrupt - * @queue: waitqueue that can be used to wait for this context to - * finish - * @irq_status: irq status - * - * @ctrl_hdl: handler for v4l2 framework - * @decode_work: worker for the decoding - * @encode_work: worker for the encoding - * @last_decoded_picinfo: pic information get from latest decode - * @empty_flush_buf: a fake size-0 capture buffer that indicates flush. Only - * to be used with encoder and stateful decoder. - * @is_flushing: set to true if flushing is in progress. - * @current_codec: current set input codec, in V4L2 pixel format - * - * @colorspace: enum v4l2_colorspace; supplemental to pixelformat - * @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding - * @quantization: enum v4l2_quantization, colorspace quantization - * @xfer_func: enum v4l2_xfer_func, colorspace transfer function - * @decoded_frame_cnt: number of decoded frames - * @lock: protect variables accessed by V4L2 threads and worker thread such as - * mtk_video_dec_buf. - * @hw_id: hardware index used to identify different hardware. - * - * @msg_queue: msg queue used to store lat buffer information. - */ -struct mtk_vcodec_ctx { - enum mtk_instance_type type; - struct mtk_vcodec_dev *dev; - struct list_head list; - - struct v4l2_fh fh; - struct v4l2_m2m_ctx *m2m_ctx; - struct mtk_q_data q_data[2]; - int id; - enum mtk_instance_state state; - enum mtk_encode_param param_change; - struct mtk_enc_params enc_params; - - const struct vdec_common_if *dec_if; - const struct venc_common_if *enc_if; - void *drv_handle; - - struct vdec_pic_info picinfo; - int dpb_size; - - int int_cond[MTK_VDEC_HW_MAX]; - int int_type[MTK_VDEC_HW_MAX]; - wait_queue_head_t queue[MTK_VDEC_HW_MAX]; - unsigned int irq_status; - - struct v4l2_ctrl_handler ctrl_hdl; - struct work_struct decode_work; - struct work_struct encode_work; - struct vdec_pic_info last_decoded_picinfo; - struct v4l2_m2m_buffer empty_flush_buf; - bool is_flushing; - - u32 current_codec; - - enum v4l2_colorspace colorspace; - enum v4l2_ycbcr_encoding ycbcr_enc; - enum v4l2_quantization quantization; - enum v4l2_xfer_func xfer_func; - - int decoded_frame_cnt; - struct mutex lock; - int hw_id; - - struct vdec_msg_queue msg_queue; -}; - -enum mtk_chip { - MTK_MT8173, - MTK_MT8183, - MTK_MT8192, - MTK_MT8195, -}; - -/* - * enum mtk_vdec_hw_arch - Used to separate different hardware architecture - */ -enum mtk_vdec_hw_arch { - MTK_VDEC_PURE_SINGLE_CORE, - MTK_VDEC_LAT_SINGLE_CORE, -}; - -/** - * struct mtk_vcodec_dec_pdata - compatible data for each IC - * @init_vdec_params: init vdec params - * @ctrls_setup: init vcodec dec ctrls - * @worker: worker to start a decode job - * @flush_decoder: function that flushes the decoder - * - * @vdec_vb2_ops: struct vb2_ops - * - * @vdec_formats: supported video decoder formats - * @num_formats: count of video decoder formats - * @default_out_fmt: default output buffer format - * @default_cap_fmt: default capture buffer format - * - * @vdec_framesizes: supported video decoder frame sizes - * @num_framesizes: count of video decoder frame sizes - * - * @chip: chip this decoder is compatible with - * @hw_arch: hardware arch is used to separate pure_sin_core and lat_sin_core - * - * @is_subdev_supported: whether support parent-node architecture(subdev) - * @uses_stateless_api: whether the decoder uses the stateless API with requests - */ - -struct mtk_vcodec_dec_pdata { - void (*init_vdec_params)(struct mtk_vcodec_ctx *ctx); - int (*ctrls_setup)(struct mtk_vcodec_ctx *ctx); - void (*worker)(struct work_struct *work); - int (*flush_decoder)(struct mtk_vcodec_ctx *ctx); - - struct vb2_ops *vdec_vb2_ops; - - const struct mtk_video_fmt *vdec_formats; - const int num_formats; - const struct mtk_video_fmt *default_out_fmt; - const struct mtk_video_fmt *default_cap_fmt; - - const struct mtk_codec_framesizes *vdec_framesizes; - const int num_framesizes; - - enum mtk_chip chip; - enum mtk_vdec_hw_arch hw_arch; - - bool is_subdev_supported; - bool uses_stateless_api; -}; - -/** - * struct mtk_vcodec_enc_pdata - compatible data for each IC - * - * @chip: chip this encoder is compatible with - * - * @uses_ext: whether the encoder uses the extended firmware messaging format - * @min_bitrate: minimum supported encoding bitrate - * @max_bitrate: maximum supported encoding bitrate - * @capture_formats: array of supported capture formats - * @num_capture_formats: number of entries in capture_formats - * @output_formats: array of supported output formats - * @num_output_formats: number of entries in output_formats - * @core_id: stand for h264 or vp8 encode index - */ -struct mtk_vcodec_enc_pdata { - enum mtk_chip chip; - - bool uses_ext; - unsigned long min_bitrate; - unsigned long max_bitrate; - const struct mtk_video_fmt *capture_formats; - size_t num_capture_formats; - const struct mtk_video_fmt *output_formats; - size_t num_output_formats; - int core_id; -}; - -#define MTK_ENC_CTX_IS_EXT(ctx) ((ctx)->dev->venc_pdata->uses_ext) - -/** - * struct mtk_vcodec_dev - driver data - * @v4l2_dev: V4L2 device to register video devices for. - * @vfd_dec: Video device for decoder - * @mdev_dec: Media device for decoder - * @vfd_enc: Video device for encoder. - * - * @m2m_dev_dec: m2m device for decoder - * @m2m_dev_enc: m2m device for encoder. - * @plat_dev: platform device - * @ctx_list: list of struct mtk_vcodec_ctx - * @irqlock: protect data access by irq handler and work thread - * @curr_ctx: The context that is waiting for codec hardware - * - * @reg_base: Mapped address of MTK Vcodec registers. - * @vdec_pdata: decoder IC-specific data - * @venc_pdata: encoder IC-specific data - * - * @fw_handler: used to communicate with the firmware. - * @id_counter: used to identify current opened instance - * - * @decode_workqueue: decode work queue - * @encode_workqueue: encode work queue - * - * @int_cond: used to identify interrupt condition happen - * @int_type: used to identify what kind of interrupt condition happen - * @dev_mutex: video_device lock - * @queue: waitqueue for waiting for completion of device commands - * - * @dec_irq: decoder irq resource - * @enc_irq: h264 encoder irq resource - * - * @dec_mutex: decoder hardware lock - * @enc_mutex: encoder hardware lock. - * - * @pm: power management control - * @dec_capability: used to identify decode capability, ex: 4k - * @enc_capability: used to identify encode capability - * - * @core_workqueue: queue used for core hardware decode - * @msg_queue_core_ctx: msg queue context used for core workqueue - * - * @subdev_dev: subdev hardware device - * @subdev_prob_done: check whether all used hw device is prob done - * @subdev_bitmap: used to record hardware is ready or not - */ -struct mtk_vcodec_dev { - struct v4l2_device v4l2_dev; - struct video_device *vfd_dec; - struct media_device mdev_dec; - struct video_device *vfd_enc; - - struct v4l2_m2m_dev *m2m_dev_dec; - struct v4l2_m2m_dev *m2m_dev_enc; - struct platform_device *plat_dev; - struct list_head ctx_list; - spinlock_t irqlock; - struct mtk_vcodec_ctx *curr_ctx; - void __iomem *reg_base[NUM_MAX_VCODEC_REG_BASE]; - const struct mtk_vcodec_dec_pdata *vdec_pdata; - const struct mtk_vcodec_enc_pdata *venc_pdata; - - struct mtk_vcodec_fw *fw_handler; - - unsigned long id_counter; - - struct workqueue_struct *decode_workqueue; - struct workqueue_struct *encode_workqueue; - int int_cond; - int int_type; - struct mutex dev_mutex; - wait_queue_head_t queue; - - int dec_irq; - int enc_irq; - - /* decoder hardware mutex lock */ - struct mutex dec_mutex[MTK_VDEC_HW_MAX]; - struct mutex enc_mutex; - - struct mtk_vcodec_pm pm; - unsigned int dec_capability; - unsigned int enc_capability; - - struct workqueue_struct *core_workqueue; - struct vdec_msg_queue_ctx msg_queue_core_ctx; - - void *subdev_dev[MTK_VDEC_HW_MAX]; - int (*subdev_prob_done)(struct mtk_vcodec_dev *vdec_dev); - DECLARE_BITMAP(subdev_bitmap, MTK_VDEC_HW_MAX); -}; - -static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) -{ - return container_of(fh, struct mtk_vcodec_ctx, fh); -} - -static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl) -{ - return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl); -} - -/* Wake up context wait_queue */ -static inline void -wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason, unsigned int hw_id) -{ - ctx->int_cond[hw_id] = 1; - ctx->int_type[hw_id] = reason; - wake_up_interruptible(&ctx->queue[hw_id]); -} - -#endif /* _MTK_VCODEC_DRV_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c deleted file mode 100644 index c21367038c34..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c +++ /dev/null @@ -1,1451 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#include -#include -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_enc.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "venc_drv_if.h" - -#define MTK_VENC_MIN_W 160U -#define MTK_VENC_MIN_H 128U -#define MTK_VENC_HD_MAX_W 1920U -#define MTK_VENC_HD_MAX_H 1088U -#define MTK_VENC_4K_MAX_W 3840U -#define MTK_VENC_4K_MAX_H 2176U - -#define DFT_CFG_WIDTH MTK_VENC_MIN_W -#define DFT_CFG_HEIGHT MTK_VENC_MIN_H -#define MTK_MAX_CTRLS_HINT 20 - -#define MTK_DEFAULT_FRAMERATE_NUM 1001 -#define MTK_DEFAULT_FRAMERATE_DENOM 30000 -#define MTK_VENC_4K_CAPABILITY_ENABLE BIT(0) - -static void mtk_venc_worker(struct work_struct *work); - -static const struct v4l2_frmsize_stepwise mtk_venc_hd_framesizes = { - MTK_VENC_MIN_W, MTK_VENC_HD_MAX_W, 16, - MTK_VENC_MIN_H, MTK_VENC_HD_MAX_H, 16, -}; - -static const struct v4l2_frmsize_stepwise mtk_venc_4k_framesizes = { - MTK_VENC_MIN_W, MTK_VENC_4K_MAX_W, 16, - MTK_VENC_MIN_H, MTK_VENC_4K_MAX_H, 16, -}; - -static int vidioc_venc_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mtk_vcodec_ctx *ctx = ctrl_to_ctx(ctrl); - struct mtk_enc_params *p = &ctx->enc_params; - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_MPEG_VIDEO_BITRATE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_BITRATE val = %d", - ctrl->val); - p->bitrate = ctrl->val; - ctx->param_change |= MTK_ENCODE_PARAM_BITRATE; - break; - case V4L2_CID_MPEG_VIDEO_B_FRAMES: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_B_FRAMES val = %d", - ctrl->val); - p->num_b_frame = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE val = %d", - ctrl->val); - p->rc_frame = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_MAX_QP val = %d", - ctrl->val); - p->h264_max_qp = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_HEADER_MODE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_HEADER_MODE val = %d", - ctrl->val); - p->seq_hdr_mode = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE val = %d", - ctrl->val); - p->rc_mb = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_PROFILE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_PROFILE val = %d", - ctrl->val); - p->h264_profile = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_LEVEL: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_LEVEL val = %d", - ctrl->val); - p->h264_level = ctrl->val; - break; - case V4L2_CID_MPEG_VIDEO_H264_I_PERIOD: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_H264_I_PERIOD val = %d", - ctrl->val); - p->intra_period = ctrl->val; - ctx->param_change |= MTK_ENCODE_PARAM_INTRA_PERIOD; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_GOP_SIZE val = %d", - ctrl->val); - p->gop_size = ctrl->val; - ctx->param_change |= MTK_ENCODE_PARAM_GOP_SIZE; - break; - case V4L2_CID_MPEG_VIDEO_VP8_PROFILE: - /* - * FIXME - what vp8 profiles are actually supported? - * The ctrl is added (with only profile 0 supported) for now. - */ - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_VP8_PROFILE val = %d", ctrl->val); - break; - case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME: - mtk_v4l2_debug(2, "V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME"); - p->force_intra = 1; - ctx->param_change |= MTK_ENCODE_PARAM_FORCE_INTRA; - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static const struct v4l2_ctrl_ops mtk_vcodec_enc_ctrl_ops = { - .s_ctrl = vidioc_venc_s_ctrl, -}; - -static int vidioc_enum_fmt(struct v4l2_fmtdesc *f, - const struct mtk_video_fmt *formats, - size_t num_formats) -{ - if (f->index >= num_formats) - return -EINVAL; - - f->pixelformat = formats[f->index].fourcc; - - return 0; -} - -static const struct mtk_video_fmt * -mtk_venc_find_format(u32 fourcc, const struct mtk_vcodec_enc_pdata *pdata) -{ - const struct mtk_video_fmt *fmt; - unsigned int k; - - for (k = 0; k < pdata->num_capture_formats; k++) { - fmt = &pdata->capture_formats[k]; - if (fmt->fourcc == fourcc) - return fmt; - } - - for (k = 0; k < pdata->num_output_formats; k++) { - fmt = &pdata->output_formats[k]; - if (fmt->fourcc == fourcc) - return fmt; - } - - return NULL; -} - -static int vidioc_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(fh); - - if (fsize->index != 0) - return -EINVAL; - - fmt = mtk_venc_find_format(fsize->pixel_format, - ctx->dev->venc_pdata); - if (!fmt) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - - if (ctx->dev->enc_capability & MTK_VENC_4K_CAPABILITY_ENABLE) - fsize->stepwise = mtk_venc_4k_framesizes; - else - fsize->stepwise = mtk_venc_hd_framesizes; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - const struct mtk_vcodec_enc_pdata *pdata = - fh_to_ctx(priv)->dev->venc_pdata; - - return vidioc_enum_fmt(f, pdata->capture_formats, - pdata->num_capture_formats); -} - -static int vidioc_enum_fmt_vid_out(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - const struct mtk_vcodec_enc_pdata *pdata = - fh_to_ctx(priv)->dev->venc_pdata; - - return vidioc_enum_fmt(f, pdata->output_formats, - pdata->num_output_formats); -} - -static int vidioc_venc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - strscpy(cap->driver, MTK_VCODEC_ENC_NAME, sizeof(cap->driver)); - strscpy(cap->bus_info, MTK_PLATFORM_STR, sizeof(cap->bus_info)); - strscpy(cap->card, MTK_PLATFORM_STR, sizeof(cap->card)); - - return 0; -} - -static int vidioc_venc_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *a) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct v4l2_fract *timeperframe = &a->parm.output.timeperframe; - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return -EINVAL; - - if (timeperframe->numerator == 0 || timeperframe->denominator == 0) { - timeperframe->numerator = MTK_DEFAULT_FRAMERATE_NUM; - timeperframe->denominator = MTK_DEFAULT_FRAMERATE_DENOM; - } - - ctx->enc_params.framerate_num = timeperframe->denominator; - ctx->enc_params.framerate_denom = timeperframe->numerator; - ctx->param_change |= MTK_ENCODE_PARAM_FRAMERATE; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - - return 0; -} - -static int vidioc_venc_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *a) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - - if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return -EINVAL; - - a->parm.output.capability = V4L2_CAP_TIMEPERFRAME; - a->parm.output.timeperframe.denominator = - ctx->enc_params.framerate_num; - a->parm.output.timeperframe.numerator = - ctx->enc_params.framerate_denom; - - return 0; -} - -static struct mtk_q_data *mtk_venc_get_q_data(struct mtk_vcodec_ctx *ctx, - enum v4l2_buf_type type) -{ - if (V4L2_TYPE_IS_OUTPUT(type)) - return &ctx->q_data[MTK_Q_DATA_SRC]; - - return &ctx->q_data[MTK_Q_DATA_DST]; -} - -static void vidioc_try_fmt_cap(struct v4l2_format *f) -{ - f->fmt.pix_mp.field = V4L2_FIELD_NONE; - f->fmt.pix_mp.num_planes = 1; - f->fmt.pix_mp.plane_fmt[0].bytesperline = 0; - f->fmt.pix_mp.flags = 0; -} - -/* V4L2 specification suggests the driver corrects the format struct if any of - * the dimensions is unsupported - */ -static int vidioc_try_fmt_out(struct mtk_vcodec_ctx *ctx, struct v4l2_format *f, - const struct mtk_video_fmt *fmt) -{ - struct v4l2_pix_format_mplane *pix_fmt_mp = &f->fmt.pix_mp; - int tmp_w, tmp_h; - unsigned int max_width, max_height; - - pix_fmt_mp->field = V4L2_FIELD_NONE; - - if (ctx->dev->enc_capability & MTK_VENC_4K_CAPABILITY_ENABLE) { - max_width = MTK_VENC_4K_MAX_W; - max_height = MTK_VENC_4K_MAX_H; - } else { - max_width = MTK_VENC_HD_MAX_W; - max_height = MTK_VENC_HD_MAX_H; - } - - pix_fmt_mp->height = clamp(pix_fmt_mp->height, MTK_VENC_MIN_H, max_height); - pix_fmt_mp->width = clamp(pix_fmt_mp->width, MTK_VENC_MIN_W, max_width); - - /* find next closer width align 16, heign align 32, size align - * 64 rectangle - */ - tmp_w = pix_fmt_mp->width; - tmp_h = pix_fmt_mp->height; - v4l_bound_align_image(&pix_fmt_mp->width, - MTK_VENC_MIN_W, - max_width, 4, - &pix_fmt_mp->height, - MTK_VENC_MIN_H, - max_height, 5, 6); - - if (pix_fmt_mp->width < tmp_w && (pix_fmt_mp->width + 16) <= max_width) - pix_fmt_mp->width += 16; - if (pix_fmt_mp->height < tmp_h && (pix_fmt_mp->height + 32) <= max_height) - pix_fmt_mp->height += 32; - - mtk_v4l2_debug(0, "before resize w=%d, h=%d, after resize w=%d, h=%d, sizeimage=%d %d", - tmp_w, tmp_h, pix_fmt_mp->width, - pix_fmt_mp->height, - pix_fmt_mp->plane_fmt[0].sizeimage, - pix_fmt_mp->plane_fmt[1].sizeimage); - - pix_fmt_mp->num_planes = fmt->num_planes; - pix_fmt_mp->plane_fmt[0].sizeimage = - pix_fmt_mp->width * pix_fmt_mp->height + - ((ALIGN(pix_fmt_mp->width, 16) * 2) * 16); - pix_fmt_mp->plane_fmt[0].bytesperline = pix_fmt_mp->width; - - if (pix_fmt_mp->num_planes == 2) { - pix_fmt_mp->plane_fmt[1].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 2 + - (ALIGN(pix_fmt_mp->width, 16) * 16); - pix_fmt_mp->plane_fmt[2].sizeimage = 0; - pix_fmt_mp->plane_fmt[1].bytesperline = - pix_fmt_mp->width; - pix_fmt_mp->plane_fmt[2].bytesperline = 0; - } else if (pix_fmt_mp->num_planes == 3) { - pix_fmt_mp->plane_fmt[1].sizeimage = - pix_fmt_mp->plane_fmt[2].sizeimage = - (pix_fmt_mp->width * pix_fmt_mp->height) / 4 + - ((ALIGN(pix_fmt_mp->width, 16) / 2) * 16); - pix_fmt_mp->plane_fmt[1].bytesperline = - pix_fmt_mp->plane_fmt[2].bytesperline = - pix_fmt_mp->width / 2; - } - - pix_fmt_mp->flags = 0; - - return 0; -} - -static void mtk_venc_set_param(struct mtk_vcodec_ctx *ctx, - struct venc_enc_param *param) -{ - struct mtk_q_data *q_data_src = &ctx->q_data[MTK_Q_DATA_SRC]; - struct mtk_enc_params *enc_params = &ctx->enc_params; - - switch (q_data_src->fmt->fourcc) { - case V4L2_PIX_FMT_YUV420M: - param->input_yuv_fmt = VENC_YUV_FORMAT_I420; - break; - case V4L2_PIX_FMT_YVU420M: - param->input_yuv_fmt = VENC_YUV_FORMAT_YV12; - break; - case V4L2_PIX_FMT_NV12M: - param->input_yuv_fmt = VENC_YUV_FORMAT_NV12; - break; - case V4L2_PIX_FMT_NV21M: - param->input_yuv_fmt = VENC_YUV_FORMAT_NV21; - break; - default: - mtk_v4l2_err("Unsupported fourcc =%d", q_data_src->fmt->fourcc); - break; - } - param->h264_profile = enc_params->h264_profile; - param->h264_level = enc_params->h264_level; - - /* Config visible resolution */ - param->width = q_data_src->visible_width; - param->height = q_data_src->visible_height; - /* Config coded resolution */ - param->buf_width = q_data_src->coded_width; - param->buf_height = q_data_src->coded_height; - param->frm_rate = enc_params->framerate_num / - enc_params->framerate_denom; - param->intra_period = enc_params->intra_period; - param->gop_size = enc_params->gop_size; - param->bitrate = enc_params->bitrate; - - mtk_v4l2_debug(0, - "fmt 0x%x, P/L %d/%d, w/h %d/%d, buf %d/%d, fps/bps %d/%d, gop %d, i_period %d", - param->input_yuv_fmt, param->h264_profile, - param->h264_level, param->width, param->height, - param->buf_width, param->buf_height, - param->frm_rate, param->bitrate, - param->gop_size, param->intra_period); -} - -static int vidioc_venc_s_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; - struct vb2_queue *vq; - struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); - int i, ret; - const struct mtk_video_fmt *fmt; - - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) { - mtk_v4l2_err("fail to get vq"); - return -EINVAL; - } - - if (vb2_is_busy(vq)) { - mtk_v4l2_err("queue busy"); - return -EBUSY; - } - - fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); - if (!fmt) { - fmt = &ctx->dev->venc_pdata->capture_formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - q_data->fmt = fmt; - vidioc_try_fmt_cap(f); - - q_data->coded_width = f->fmt.pix_mp.width; - q_data->coded_height = f->fmt.pix_mp.height; - q_data->field = f->fmt.pix_mp.field; - - for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { - struct v4l2_plane_pix_format *plane_fmt; - - plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; - q_data->bytesperline[i] = plane_fmt->bytesperline; - q_data->sizeimage[i] = plane_fmt->sizeimage; - } - - if (ctx->state == MTK_STATE_FREE) { - ret = venc_if_init(ctx, q_data->fmt->fourcc); - if (ret) { - mtk_v4l2_err("venc_if_init failed=%d, codec type=%x", - ret, q_data->fmt->fourcc); - return -EBUSY; - } - ctx->state = MTK_STATE_INIT; - } - - return 0; -} - -static int vidioc_venc_s_fmt_out(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; - struct vb2_queue *vq; - struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); - int ret, i; - const struct mtk_video_fmt *fmt; - - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) { - mtk_v4l2_err("fail to get vq"); - return -EINVAL; - } - - if (vb2_is_busy(vq)) { - mtk_v4l2_err("queue busy"); - return -EBUSY; - } - - fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); - if (!fmt) { - fmt = &ctx->dev->venc_pdata->output_formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - ret = vidioc_try_fmt_out(ctx, f, fmt); - if (ret) - return ret; - - q_data->fmt = fmt; - q_data->visible_width = f->fmt.pix_mp.width; - q_data->visible_height = f->fmt.pix_mp.height; - q_data->coded_width = f->fmt.pix_mp.width; - q_data->coded_height = f->fmt.pix_mp.height; - - q_data->field = f->fmt.pix_mp.field; - ctx->colorspace = f->fmt.pix_mp.colorspace; - ctx->ycbcr_enc = f->fmt.pix_mp.ycbcr_enc; - ctx->quantization = f->fmt.pix_mp.quantization; - ctx->xfer_func = f->fmt.pix_mp.xfer_func; - - for (i = 0; i < f->fmt.pix_mp.num_planes; i++) { - struct v4l2_plane_pix_format *plane_fmt; - - plane_fmt = &f->fmt.pix_mp.plane_fmt[i]; - q_data->bytesperline[i] = plane_fmt->bytesperline; - q_data->sizeimage[i] = plane_fmt->sizeimage; - } - - return 0; -} - -static int vidioc_venc_g_fmt(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct vb2_queue *vq; - struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, f->type); - int i; - - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - if (!vq) - return -EINVAL; - - - pix->width = q_data->coded_width; - pix->height = q_data->coded_height; - pix->pixelformat = q_data->fmt->fourcc; - pix->field = q_data->field; - pix->num_planes = q_data->fmt->num_planes; - for (i = 0; i < pix->num_planes; i++) { - pix->plane_fmt[i].bytesperline = q_data->bytesperline[i]; - pix->plane_fmt[i].sizeimage = q_data->sizeimage[i]; - } - - pix->flags = 0; - pix->colorspace = ctx->colorspace; - pix->ycbcr_enc = ctx->ycbcr_enc; - pix->quantization = ctx->quantization; - pix->xfer_func = ctx->xfer_func; - - return 0; -} - -static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; - - fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); - if (!fmt) { - fmt = &ctx->dev->venc_pdata->capture_formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - f->fmt.pix_mp.colorspace = ctx->colorspace; - f->fmt.pix_mp.ycbcr_enc = ctx->ycbcr_enc; - f->fmt.pix_mp.quantization = ctx->quantization; - f->fmt.pix_mp.xfer_func = ctx->xfer_func; - - vidioc_try_fmt_cap(f); - - return 0; -} - -static int vidioc_try_fmt_vid_out_mplane(struct file *file, void *priv, - struct v4l2_format *f) -{ - const struct mtk_video_fmt *fmt; - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - const struct mtk_vcodec_enc_pdata *pdata = ctx->dev->venc_pdata; - - fmt = mtk_venc_find_format(f->fmt.pix.pixelformat, pdata); - if (!fmt) { - fmt = &ctx->dev->venc_pdata->output_formats[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - if (!f->fmt.pix_mp.colorspace) { - f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709; - f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT; - f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT; - } - - return vidioc_try_fmt_out(ctx, f, fmt); -} - -static int vidioc_venc_g_selection(struct file *file, void *priv, - struct v4l2_selection *s) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, s->type); - - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - switch (s->target) { - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_BOUNDS: - s->r.top = 0; - s->r.left = 0; - s->r.width = q_data->coded_width; - s->r.height = q_data->coded_height; - break; - case V4L2_SEL_TGT_CROP: - s->r.top = 0; - s->r.left = 0; - s->r.width = q_data->visible_width; - s->r.height = q_data->visible_height; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_venc_s_selection(struct file *file, void *priv, - struct v4l2_selection *s) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, s->type); - - if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) - return -EINVAL; - - switch (s->target) { - case V4L2_SEL_TGT_CROP: - /* Only support crop from (0,0) */ - s->r.top = 0; - s->r.left = 0; - s->r.width = min(s->r.width, q_data->coded_width); - s->r.height = min(s->r.height, q_data->coded_height); - q_data->visible_width = s->r.width; - q_data->visible_height = s->r.height; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_venc_qbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - - if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", - ctx->id); - return -EIO; - } - - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int vidioc_venc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - int ret; - - if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call on QBUF after unrecoverable error", - ctx->id); - return -EIO; - } - - ret = v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); - if (ret) - return ret; - - /* - * Complete flush if the user dequeued the 0-payload LAST buffer. - * We check the payload because a buffer with the LAST flag can also - * be seen during resolution changes. If we happen to be flushing at - * that time, the last buffer before the resolution changes could be - * misinterpreted for the buffer generated by the flush and terminate - * it earlier than we want. - */ - if (!V4L2_TYPE_IS_OUTPUT(buf->type) && - buf->flags & V4L2_BUF_FLAG_LAST && - buf->m.planes[0].bytesused == 0 && - ctx->is_flushing) { - /* - * Last CAPTURE buffer is dequeued, we can allow another flush - * to take place. - */ - ctx->is_flushing = false; - } - - return 0; -} - -static int vidioc_encoder_cmd(struct file *file, void *priv, - struct v4l2_encoder_cmd *cmd) -{ - struct mtk_vcodec_ctx *ctx = fh_to_ctx(priv); - struct vb2_queue *src_vq, *dst_vq; - int ret; - - if (ctx->state == MTK_STATE_ABORT) { - mtk_v4l2_err("[%d] Call to CMD after unrecoverable error", - ctx->id); - return -EIO; - } - - ret = v4l2_m2m_ioctl_try_encoder_cmd(file, priv, cmd); - if (ret) - return ret; - - /* Calling START or STOP is invalid if a flush is in progress */ - if (ctx->is_flushing) - return -EBUSY; - - mtk_v4l2_debug(1, "encoder cmd=%u", cmd->cmd); - - dst_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, - V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - switch (cmd->cmd) { - case V4L2_ENC_CMD_STOP: - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - if (!vb2_is_streaming(src_vq)) { - mtk_v4l2_debug(1, "Output stream is off. No need to flush."); - return 0; - } - if (!vb2_is_streaming(dst_vq)) { - mtk_v4l2_debug(1, "Capture stream is off. No need to flush."); - return 0; - } - ctx->is_flushing = true; - v4l2_m2m_buf_queue(ctx->m2m_ctx, &ctx->empty_flush_buf.vb); - v4l2_m2m_try_schedule(ctx->m2m_ctx); - break; - - case V4L2_ENC_CMD_START: - vb2_clear_last_buffer_dequeued(dst_vq); - break; - - default: - return -EINVAL; - } - - return 0; -} - -const struct v4l2_ioctl_ops mtk_venc_ioctl_ops = { - .vidioc_streamon = v4l2_m2m_ioctl_streamon, - .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - - .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, - .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, - .vidioc_qbuf = vidioc_venc_qbuf, - .vidioc_dqbuf = vidioc_venc_dqbuf, - - .vidioc_querycap = vidioc_venc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - - .vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane, - .vidioc_try_fmt_vid_out_mplane = vidioc_try_fmt_vid_out_mplane, - .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, - - .vidioc_s_parm = vidioc_venc_s_parm, - .vidioc_g_parm = vidioc_venc_g_parm, - .vidioc_s_fmt_vid_cap_mplane = vidioc_venc_s_fmt_cap, - .vidioc_s_fmt_vid_out_mplane = vidioc_venc_s_fmt_out, - - .vidioc_g_fmt_vid_cap_mplane = vidioc_venc_g_fmt, - .vidioc_g_fmt_vid_out_mplane = vidioc_venc_g_fmt, - - .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, - .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, - - .vidioc_g_selection = vidioc_venc_g_selection, - .vidioc_s_selection = vidioc_venc_s_selection, - - .vidioc_encoder_cmd = vidioc_encoder_cmd, - .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd, -}; - -static int vb2ops_venc_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, - unsigned int *nplanes, - unsigned int sizes[], - struct device *alloc_devs[]) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vq); - struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, vq->type); - unsigned int i; - - if (q_data == NULL) - return -EINVAL; - - if (*nplanes) { - for (i = 0; i < *nplanes; i++) - if (sizes[i] < q_data->sizeimage[i]) - return -EINVAL; - } else { - *nplanes = q_data->fmt->num_planes; - for (i = 0; i < *nplanes; i++) - sizes[i] = q_data->sizeimage[i]; - } - - return 0; -} - -static int vb2ops_venc_buf_prepare(struct vb2_buffer *vb) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct mtk_q_data *q_data = mtk_venc_get_q_data(ctx, vb->vb2_queue->type); - int i; - - for (i = 0; i < q_data->fmt->num_planes; i++) { - if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { - mtk_v4l2_err("data will not fit into plane %d (%lu < %d)", - i, vb2_plane_size(vb, i), - q_data->sizeimage[i]); - return -EINVAL; - } - } - - return 0; -} - -static void vb2ops_venc_buf_queue(struct vb2_buffer *vb) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct vb2_v4l2_buffer *vb2_v4l2 = - container_of(vb, struct vb2_v4l2_buffer, vb2_buf); - - struct mtk_video_enc_buf *mtk_buf = - container_of(vb2_v4l2, struct mtk_video_enc_buf, - m2m_buf.vb); - - if ((vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) && - (ctx->param_change != MTK_ENCODE_PARAM_NONE)) { - mtk_v4l2_debug(1, "[%d] Before id=%d encode parameter change %x", - ctx->id, - vb2_v4l2->vb2_buf.index, - ctx->param_change); - mtk_buf->param_change = ctx->param_change; - mtk_buf->enc_params = ctx->enc_params; - ctx->param_change = MTK_ENCODE_PARAM_NONE; - } - - v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb)); -} - -static int vb2ops_venc_start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); - struct venc_enc_param param; - int ret, pm_ret; - int i; - - /* Once state turn into MTK_STATE_ABORT, we need stop_streaming - * to clear it - */ - if ((ctx->state == MTK_STATE_ABORT) || (ctx->state == MTK_STATE_FREE)) { - ret = -EIO; - goto err_start_stream; - } - - /* Do the initialization when both start_streaming have been called */ - if (V4L2_TYPE_IS_OUTPUT(q->type)) { - if (!vb2_start_streaming_called(&ctx->m2m_ctx->cap_q_ctx.q)) - return 0; - } else { - if (!vb2_start_streaming_called(&ctx->m2m_ctx->out_q_ctx.q)) - return 0; - } - - ret = pm_runtime_resume_and_get(&ctx->dev->plat_dev->dev); - if (ret < 0) { - mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret); - goto err_start_stream; - } - - mtk_venc_set_param(ctx, ¶m); - ret = venc_if_set_param(ctx, VENC_SET_PARAM_ENC, ¶m); - if (ret) { - mtk_v4l2_err("venc_if_set_param failed=%d", ret); - ctx->state = MTK_STATE_ABORT; - goto err_set_param; - } - ctx->param_change = MTK_ENCODE_PARAM_NONE; - - if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && - (ctx->enc_params.seq_hdr_mode != - V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE)) { - ret = venc_if_set_param(ctx, - VENC_SET_PARAM_PREPEND_HEADER, - NULL); - if (ret) { - mtk_v4l2_err("venc_if_set_param failed=%d", ret); - ctx->state = MTK_STATE_ABORT; - goto err_set_param; - } - ctx->state = MTK_STATE_HEADER; - } - - return 0; - -err_set_param: - pm_ret = pm_runtime_put(&ctx->dev->plat_dev->dev); - if (pm_ret < 0) - mtk_v4l2_err("pm_runtime_put fail %d", pm_ret); - -err_start_stream: - for (i = 0; i < q->num_buffers; ++i) { - struct vb2_buffer *buf = vb2_get_buffer(q, i); - - /* - * FIXME: This check is not needed as only active buffers - * can be marked as done. - */ - if (buf->state == VB2_BUF_STATE_ACTIVE) { - mtk_v4l2_debug(0, "[%d] id=%d, type=%d, %d -> VB2_BUF_STATE_QUEUED", - ctx->id, i, q->type, - (int)buf->state); - v4l2_m2m_buf_done(to_vb2_v4l2_buffer(buf), - VB2_BUF_STATE_QUEUED); - } - } - - return ret; -} - -static void vb2ops_venc_stop_streaming(struct vb2_queue *q) -{ - struct mtk_vcodec_ctx *ctx = vb2_get_drv_priv(q); - struct vb2_v4l2_buffer *src_buf, *dst_buf; - int ret; - - mtk_v4l2_debug(2, "[%d]-> type=%d", ctx->id, q->type); - - if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { - while ((dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx))) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); - } - /* STREAMOFF on the CAPTURE queue completes any ongoing flush */ - if (ctx->is_flushing) { - struct v4l2_m2m_buffer *b, *n; - - mtk_v4l2_debug(1, "STREAMOFF called while flushing"); - /* - * STREAMOFF could be called before the flush buffer is - * dequeued. Check whether empty flush buf is still in - * queue before removing it. - */ - v4l2_m2m_for_each_src_buf_safe(ctx->m2m_ctx, b, n) { - if (b == &ctx->empty_flush_buf) { - v4l2_m2m_src_buf_remove_by_buf(ctx->m2m_ctx, &b->vb); - break; - } - } - ctx->is_flushing = false; - } - } else { - while ((src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx))) { - if (src_buf != &ctx->empty_flush_buf.vb) - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - } - if (ctx->is_flushing) { - /* - * If we are in the middle of a flush, put the flush - * buffer back into the queue so the next CAPTURE - * buffer gets returned with the LAST flag set. - */ - v4l2_m2m_buf_queue(ctx->m2m_ctx, - &ctx->empty_flush_buf.vb); - } - } - - if ((q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE && - vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q)) || - (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE && - vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q))) { - mtk_v4l2_debug(1, "[%d]-> q type %d out=%d cap=%d", - ctx->id, q->type, - vb2_is_streaming(&ctx->m2m_ctx->out_q_ctx.q), - vb2_is_streaming(&ctx->m2m_ctx->cap_q_ctx.q)); - return; - } - - /* Release the encoder if both streams are stopped. */ - ret = venc_if_deinit(ctx); - if (ret) - mtk_v4l2_err("venc_if_deinit failed=%d", ret); - - ret = pm_runtime_put(&ctx->dev->plat_dev->dev); - if (ret < 0) - mtk_v4l2_err("pm_runtime_put fail %d", ret); - - ctx->state = MTK_STATE_FREE; -} - -static int vb2ops_venc_buf_out_validate(struct vb2_buffer *vb) -{ - struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); - - vbuf->field = V4L2_FIELD_NONE; - return 0; -} - -static const struct vb2_ops mtk_venc_vb2_ops = { - .queue_setup = vb2ops_venc_queue_setup, - .buf_out_validate = vb2ops_venc_buf_out_validate, - .buf_prepare = vb2ops_venc_buf_prepare, - .buf_queue = vb2ops_venc_buf_queue, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, - .start_streaming = vb2ops_venc_start_streaming, - .stop_streaming = vb2ops_venc_stop_streaming, -}; - -static int mtk_venc_encode_header(void *priv) -{ - struct mtk_vcodec_ctx *ctx = priv; - int ret; - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct mtk_vcodec_mem bs_buf; - struct venc_done_result enc_result; - - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - if (!dst_buf) { - mtk_v4l2_debug(1, "No dst buffer"); - return -EINVAL; - } - - bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; - - mtk_v4l2_debug(1, - "[%d] buf id=%d va=0x%p dma_addr=0x%llx size=%zu", - ctx->id, - dst_buf->vb2_buf.index, bs_buf.va, - (u64)bs_buf.dma_addr, - bs_buf.size); - - ret = venc_if_encode(ctx, - VENC_START_OPT_ENCODE_SEQUENCE_HEADER, - NULL, &bs_buf, &enc_result); - - if (ret) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - ctx->state = MTK_STATE_ABORT; - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); - mtk_v4l2_err("venc_if_encode failed=%d", ret); - return -EINVAL; - } - src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - if (src_buf) { - dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; - dst_buf->timecode = src_buf->timecode; - } else { - mtk_v4l2_err("No timestamp for the header buffer."); - } - - ctx->state = MTK_STATE_HEADER; - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_result.bs_size); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); - - return 0; -} - -static int mtk_venc_param_change(struct mtk_vcodec_ctx *ctx) -{ - struct venc_enc_param enc_prm; - struct vb2_v4l2_buffer *vb2_v4l2 = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - struct mtk_video_enc_buf *mtk_buf; - int ret = 0; - - /* Don't upcast the empty flush buffer */ - if (vb2_v4l2 == &ctx->empty_flush_buf.vb) - return 0; - - mtk_buf = container_of(vb2_v4l2, struct mtk_video_enc_buf, m2m_buf.vb); - - memset(&enc_prm, 0, sizeof(enc_prm)); - if (mtk_buf->param_change == MTK_ENCODE_PARAM_NONE) - return 0; - - if (mtk_buf->param_change & MTK_ENCODE_PARAM_BITRATE) { - enc_prm.bitrate = mtk_buf->enc_params.bitrate; - mtk_v4l2_debug(1, "[%d] id=%d, change param br=%d", - ctx->id, - vb2_v4l2->vb2_buf.index, - enc_prm.bitrate); - ret |= venc_if_set_param(ctx, - VENC_SET_PARAM_ADJUST_BITRATE, - &enc_prm); - } - if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FRAMERATE) { - enc_prm.frm_rate = mtk_buf->enc_params.framerate_num / - mtk_buf->enc_params.framerate_denom; - mtk_v4l2_debug(1, "[%d] id=%d, change param fr=%d", - ctx->id, - vb2_v4l2->vb2_buf.index, - enc_prm.frm_rate); - ret |= venc_if_set_param(ctx, - VENC_SET_PARAM_ADJUST_FRAMERATE, - &enc_prm); - } - if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_GOP_SIZE) { - enc_prm.gop_size = mtk_buf->enc_params.gop_size; - mtk_v4l2_debug(1, "change param intra period=%d", - enc_prm.gop_size); - ret |= venc_if_set_param(ctx, - VENC_SET_PARAM_GOP_SIZE, - &enc_prm); - } - if (!ret && mtk_buf->param_change & MTK_ENCODE_PARAM_FORCE_INTRA) { - mtk_v4l2_debug(1, "[%d] id=%d, change param force I=%d", - ctx->id, - vb2_v4l2->vb2_buf.index, - mtk_buf->enc_params.force_intra); - if (mtk_buf->enc_params.force_intra) - ret |= venc_if_set_param(ctx, - VENC_SET_PARAM_FORCE_INTRA, - NULL); - } - - mtk_buf->param_change = MTK_ENCODE_PARAM_NONE; - - if (ret) { - ctx->state = MTK_STATE_ABORT; - mtk_v4l2_err("venc_if_set_param %d failed=%d", - mtk_buf->param_change, ret); - return -1; - } - - return 0; -} - -/* - * v4l2_m2m_streamoff() holds dev_mutex and waits mtk_venc_worker() - * to call v4l2_m2m_job_finish(). - * If mtk_venc_worker() tries to acquire dev_mutex, it will deadlock. - * So this function must not try to acquire dev->dev_mutex. - * This means v4l2 ioctls and mtk_venc_worker() can run at the same time. - * mtk_venc_worker() should be carefully implemented to avoid bugs. - */ -static void mtk_venc_worker(struct work_struct *work) -{ - struct mtk_vcodec_ctx *ctx = container_of(work, struct mtk_vcodec_ctx, - encode_work); - struct vb2_v4l2_buffer *src_buf, *dst_buf; - struct venc_frm_buf frm_buf; - struct mtk_vcodec_mem bs_buf; - struct venc_done_result enc_result; - int ret, i; - - /* check dst_buf, dst_buf may be removed in device_run - * to stored encdoe header so we need check dst_buf and - * call job_finish here to prevent recursion - */ - dst_buf = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - if (!dst_buf) { - v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); - return; - } - - src_buf = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - - /* - * If we see the flush buffer, send an empty buffer with the LAST flag - * to the client. is_flushing will be reset at the time the buffer - * is dequeued. - */ - if (src_buf == &ctx->empty_flush_buf.vb) { - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - dst_buf->flags |= V4L2_BUF_FLAG_LAST; - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); - v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); - return; - } - - memset(&frm_buf, 0, sizeof(frm_buf)); - for (i = 0; i < src_buf->vb2_buf.num_planes ; i++) { - frm_buf.fb_addr[i].dma_addr = - vb2_dma_contig_plane_dma_addr(&src_buf->vb2_buf, i); - frm_buf.fb_addr[i].size = - (size_t)src_buf->vb2_buf.planes[i].length; - } - bs_buf.va = vb2_plane_vaddr(&dst_buf->vb2_buf, 0); - bs_buf.dma_addr = vb2_dma_contig_plane_dma_addr(&dst_buf->vb2_buf, 0); - bs_buf.size = (size_t)dst_buf->vb2_buf.planes[0].length; - - mtk_v4l2_debug(2, - "Framebuf PA=%llx Size=0x%zx;PA=0x%llx Size=0x%zx;PA=0x%llx Size=%zu", - (u64)frm_buf.fb_addr[0].dma_addr, - frm_buf.fb_addr[0].size, - (u64)frm_buf.fb_addr[1].dma_addr, - frm_buf.fb_addr[1].size, - (u64)frm_buf.fb_addr[2].dma_addr, - frm_buf.fb_addr[2].size); - - ret = venc_if_encode(ctx, VENC_START_OPT_ENCODE_FRAME, - &frm_buf, &bs_buf, &enc_result); - - dst_buf->vb2_buf.timestamp = src_buf->vb2_buf.timestamp; - dst_buf->timecode = src_buf->timecode; - - if (enc_result.is_key_frm) - dst_buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - - if (ret) { - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR); - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, 0); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR); - mtk_v4l2_err("venc_if_encode failed=%d", ret); - } else { - v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE); - vb2_set_plane_payload(&dst_buf->vb2_buf, 0, enc_result.bs_size); - v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE); - mtk_v4l2_debug(2, "venc_if_encode bs size=%d", - enc_result.bs_size); - } - - v4l2_m2m_job_finish(ctx->dev->m2m_dev_enc, ctx->m2m_ctx); - - mtk_v4l2_debug(1, "<=== src_buf[%d] dst_buf[%d] venc_if_encode ret=%d Size=%u===>", - src_buf->vb2_buf.index, dst_buf->vb2_buf.index, ret, - enc_result.bs_size); -} - -static void m2mops_venc_device_run(void *priv) -{ - struct mtk_vcodec_ctx *ctx = priv; - - if ((ctx->q_data[MTK_Q_DATA_DST].fmt->fourcc == V4L2_PIX_FMT_H264) && - (ctx->state != MTK_STATE_HEADER)) { - /* encode h264 sps/pps header */ - mtk_venc_encode_header(ctx); - queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); - return; - } - - mtk_venc_param_change(ctx); - queue_work(ctx->dev->encode_workqueue, &ctx->encode_work); -} - -static int m2mops_venc_job_ready(void *m2m_priv) -{ - struct mtk_vcodec_ctx *ctx = m2m_priv; - - if (ctx->state == MTK_STATE_ABORT || ctx->state == MTK_STATE_FREE) { - mtk_v4l2_debug(3, "[%d]Not ready: state=0x%x.", - ctx->id, ctx->state); - return 0; - } - - return 1; -} - -static void m2mops_venc_job_abort(void *priv) -{ - struct mtk_vcodec_ctx *ctx = priv; - - ctx->state = MTK_STATE_ABORT; -} - -const struct v4l2_m2m_ops mtk_venc_m2m_ops = { - .device_run = m2mops_venc_device_run, - .job_ready = m2mops_venc_job_ready, - .job_abort = m2mops_venc_job_abort, -}; - -void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx) -{ - struct mtk_q_data *q_data; - - ctx->m2m_ctx->q_lock = &ctx->dev->dev_mutex; - ctx->fh.m2m_ctx = ctx->m2m_ctx; - ctx->fh.ctrl_handler = &ctx->ctrl_hdl; - INIT_WORK(&ctx->encode_work, mtk_venc_worker); - - ctx->colorspace = V4L2_COLORSPACE_REC709; - ctx->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; - ctx->quantization = V4L2_QUANTIZATION_DEFAULT; - ctx->xfer_func = V4L2_XFER_FUNC_DEFAULT; - - q_data = &ctx->q_data[MTK_Q_DATA_SRC]; - memset(q_data, 0, sizeof(struct mtk_q_data)); - q_data->visible_width = DFT_CFG_WIDTH; - q_data->visible_height = DFT_CFG_HEIGHT; - q_data->coded_width = DFT_CFG_WIDTH; - q_data->coded_height = DFT_CFG_HEIGHT; - q_data->field = V4L2_FIELD_NONE; - - q_data->fmt = &ctx->dev->venc_pdata->output_formats[0]; - - v4l_bound_align_image(&q_data->coded_width, - MTK_VENC_MIN_W, - MTK_VENC_HD_MAX_W, 4, - &q_data->coded_height, - MTK_VENC_MIN_H, - MTK_VENC_HD_MAX_H, 5, 6); - - if (q_data->coded_width < DFT_CFG_WIDTH && - (q_data->coded_width + 16) <= MTK_VENC_HD_MAX_W) - q_data->coded_width += 16; - if (q_data->coded_height < DFT_CFG_HEIGHT && - (q_data->coded_height + 32) <= MTK_VENC_HD_MAX_H) - q_data->coded_height += 32; - - q_data->sizeimage[0] = - q_data->coded_width * q_data->coded_height+ - ((ALIGN(q_data->coded_width, 16) * 2) * 16); - q_data->bytesperline[0] = q_data->coded_width; - q_data->sizeimage[1] = - (q_data->coded_width * q_data->coded_height) / 2 + - (ALIGN(q_data->coded_width, 16) * 16); - q_data->bytesperline[1] = q_data->coded_width; - - q_data = &ctx->q_data[MTK_Q_DATA_DST]; - memset(q_data, 0, sizeof(struct mtk_q_data)); - q_data->coded_width = DFT_CFG_WIDTH; - q_data->coded_height = DFT_CFG_HEIGHT; - q_data->fmt = &ctx->dev->venc_pdata->capture_formats[0]; - q_data->field = V4L2_FIELD_NONE; - ctx->q_data[MTK_Q_DATA_DST].sizeimage[0] = - DFT_CFG_WIDTH * DFT_CFG_HEIGHT; - ctx->q_data[MTK_Q_DATA_DST].bytesperline[0] = 0; - - ctx->enc_params.framerate_num = MTK_DEFAULT_FRAMERATE_NUM; - ctx->enc_params.framerate_denom = MTK_DEFAULT_FRAMERATE_DENOM; -} - -int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx) -{ - const struct v4l2_ctrl_ops *ops = &mtk_vcodec_enc_ctrl_ops; - struct v4l2_ctrl_handler *handler = &ctx->ctrl_hdl; - u8 h264_max_level; - - if (ctx->dev->enc_capability & MTK_VENC_4K_CAPABILITY_ENABLE) - h264_max_level = V4L2_MPEG_VIDEO_H264_LEVEL_5_1; - else - h264_max_level = V4L2_MPEG_VIDEO_H264_LEVEL_4_2; - - v4l2_ctrl_handler_init(handler, MTK_MAX_CTRLS_HINT); - - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, - 1, 1, 1, 1); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_BITRATE, - ctx->dev->venc_pdata->min_bitrate, - ctx->dev->venc_pdata->max_bitrate, 1, 4000000); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_B_FRAMES, - 0, 2, 1, 0); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, - 0, 1, 1, 1); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_MAX_QP, - 0, 51, 1, 51); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, - 0, 65535, 1, 0); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 0, 65535, 1, 0); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_MB_RC_ENABLE, - 0, 1, 1, 0); - v4l2_ctrl_new_std(handler, ops, V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, - 0, 0, 0, 0); - v4l2_ctrl_new_std_menu(handler, ops, - V4L2_CID_MPEG_VIDEO_HEADER_MODE, - V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, - 0, V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); - v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_PROFILE, - V4L2_MPEG_VIDEO_H264_PROFILE_HIGH, - 0, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH); - v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_H264_LEVEL, - h264_max_level, - 0, V4L2_MPEG_VIDEO_H264_LEVEL_4_0); - v4l2_ctrl_new_std_menu(handler, ops, V4L2_CID_MPEG_VIDEO_VP8_PROFILE, - V4L2_MPEG_VIDEO_VP8_PROFILE_0, 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0); - - - if (handler->error) { - mtk_v4l2_err("Init control handler fail %d", - handler->error); - return handler->error; - } - - v4l2_ctrl_handler_setup(&ctx->ctrl_hdl); - - return 0; -} - -int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct mtk_vcodec_ctx *ctx = priv; - int ret; - - /* Note: VB2_USERPTR works with dma-contig because mt8173 - * support iommu - * https://patchwork.kernel.org/patch/8335461/ - * https://patchwork.kernel.org/patch/7596181/ - */ - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = ctx; - src_vq->buf_struct_size = sizeof(struct mtk_video_enc_buf); - src_vq->ops = &mtk_venc_vb2_ops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - src_vq->lock = &ctx->dev->dev_mutex; - src_vq->dev = &ctx->dev->plat_dev->dev; - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = ctx; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - dst_vq->ops = &mtk_venc_vb2_ops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - dst_vq->lock = &ctx->dev->dev_mutex; - dst_vq->dev = &ctx->dev->plat_dev->dev; - - return vb2_queue_init(dst_vq); -} - -int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx) -{ - struct mtk_vcodec_dev *dev = ctx->dev; - - mutex_unlock(&dev->enc_mutex); - return 0; -} - -int mtk_venc_lock(struct mtk_vcodec_ctx *ctx) -{ - struct mtk_vcodec_dev *dev = ctx->dev; - - mutex_lock(&dev->enc_mutex); - return 0; -} - -void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx) -{ - int ret = venc_if_deinit(ctx); - - if (ret) - mtk_v4l2_err("venc_if_deinit failed=%d", ret); - - ctx->state = MTK_STATE_FREE; -} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h deleted file mode 100644 index 513ee7993e34..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#ifndef _MTK_VCODEC_ENC_H_ -#define _MTK_VCODEC_ENC_H_ - -#include -#include - -#define MTK_VENC_IRQ_STATUS_SPS 0x1 -#define MTK_VENC_IRQ_STATUS_PPS 0x2 -#define MTK_VENC_IRQ_STATUS_FRM 0x4 -#define MTK_VENC_IRQ_STATUS_DRAM 0x8 -#define MTK_VENC_IRQ_STATUS_PAUSE 0x10 -#define MTK_VENC_IRQ_STATUS_SWITCH 0x20 - -#define MTK_VENC_IRQ_STATUS_OFFSET 0x05C -#define MTK_VENC_IRQ_ACK_OFFSET 0x060 - -/** - * struct mtk_video_enc_buf - Private data related to each VB2 buffer. - * @m2m_buf: M2M buffer - * @list: list that buffer link to - * @param_change: Types of encode parameter change before encoding this - * buffer - * @enc_params: Encode parameters changed before encode this buffer - */ -struct mtk_video_enc_buf { - struct v4l2_m2m_buffer m2m_buf; - - u32 param_change; - struct mtk_enc_params enc_params; -}; - -extern const struct v4l2_ioctl_ops mtk_venc_ioctl_ops; -extern const struct v4l2_m2m_ops mtk_venc_m2m_ops; - -int mtk_venc_unlock(struct mtk_vcodec_ctx *ctx); -int mtk_venc_lock(struct mtk_vcodec_ctx *ctx); -int mtk_vcodec_enc_queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq); -void mtk_vcodec_enc_release(struct mtk_vcodec_ctx *ctx); -int mtk_vcodec_enc_ctrls_setup(struct mtk_vcodec_ctx *ctx); -void mtk_vcodec_enc_set_default_params(struct mtk_vcodec_ctx *ctx); - -#endif /* _MTK_VCODEC_ENC_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c deleted file mode 100644 index 5172cfe0db4a..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c +++ /dev/null @@ -1,479 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_enc.h" -#include "mtk_vcodec_enc_pm.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_fw.h" - -static const struct mtk_video_fmt mtk_video_formats_output[] = { - { - .fourcc = V4L2_PIX_FMT_NV12M, - .type = MTK_FMT_FRAME, - .num_planes = 2, - }, - { - .fourcc = V4L2_PIX_FMT_NV21M, - .type = MTK_FMT_FRAME, - .num_planes = 2, - }, - { - .fourcc = V4L2_PIX_FMT_YUV420M, - .type = MTK_FMT_FRAME, - .num_planes = 3, - }, - { - .fourcc = V4L2_PIX_FMT_YVU420M, - .type = MTK_FMT_FRAME, - .num_planes = 3, - }, -}; - -static const struct mtk_video_fmt mtk_video_formats_capture_h264[] = { - { - .fourcc = V4L2_PIX_FMT_H264, - .type = MTK_FMT_ENC, - .num_planes = 1, - }, -}; - -static const struct mtk_video_fmt mtk_video_formats_capture_vp8[] = { - { - .fourcc = V4L2_PIX_FMT_VP8, - .type = MTK_FMT_ENC, - .num_planes = 1, - }, -}; - -static void clean_irq_status(unsigned int irq_status, void __iomem *addr) -{ - if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE) - writel(MTK_VENC_IRQ_STATUS_PAUSE, addr); - - if (irq_status & MTK_VENC_IRQ_STATUS_SWITCH) - writel(MTK_VENC_IRQ_STATUS_SWITCH, addr); - - if (irq_status & MTK_VENC_IRQ_STATUS_DRAM) - writel(MTK_VENC_IRQ_STATUS_DRAM, addr); - - if (irq_status & MTK_VENC_IRQ_STATUS_SPS) - writel(MTK_VENC_IRQ_STATUS_SPS, addr); - - if (irq_status & MTK_VENC_IRQ_STATUS_PPS) - writel(MTK_VENC_IRQ_STATUS_PPS, addr); - - if (irq_status & MTK_VENC_IRQ_STATUS_FRM) - writel(MTK_VENC_IRQ_STATUS_FRM, addr); - -} -static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv) -{ - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; - unsigned long flags; - void __iomem *addr; - - spin_lock_irqsave(&dev->irqlock, flags); - ctx = dev->curr_ctx; - spin_unlock_irqrestore(&dev->irqlock, flags); - - mtk_v4l2_debug(1, "id=%d coreid:%d", ctx->id, dev->venc_pdata->core_id); - addr = dev->reg_base[dev->venc_pdata->core_id] + - MTK_VENC_IRQ_ACK_OFFSET; - - ctx->irq_status = readl(dev->reg_base[dev->venc_pdata->core_id] + - (MTK_VENC_IRQ_STATUS_OFFSET)); - - clean_irq_status(ctx->irq_status, addr); - - wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0); - return IRQ_HANDLED; -} - -static int fops_vcodec_open(struct file *file) -{ - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = NULL; - int ret = 0; - struct vb2_queue *src_vq; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - mutex_lock(&dev->dev_mutex); - /* - * Use simple counter to uniquely identify this context. Only - * used for logging. - */ - ctx->id = dev->id_counter++; - v4l2_fh_init(&ctx->fh, video_devdata(file)); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - INIT_LIST_HEAD(&ctx->list); - ctx->dev = dev; - init_waitqueue_head(&ctx->queue[0]); - - ctx->type = MTK_INST_ENCODER; - ret = mtk_vcodec_enc_ctrls_setup(ctx); - if (ret) { - mtk_v4l2_err("Failed to setup controls() (%d)", - ret); - goto err_ctrls_setup; - } - ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev_enc, ctx, - &mtk_vcodec_enc_queue_init); - if (IS_ERR((__force void *)ctx->m2m_ctx)) { - ret = PTR_ERR((__force void *)ctx->m2m_ctx); - mtk_v4l2_err("Failed to v4l2_m2m_ctx_init() (%d)", - ret); - goto err_m2m_ctx_init; - } - src_vq = v4l2_m2m_get_vq(ctx->m2m_ctx, - V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); - ctx->empty_flush_buf.vb.vb2_buf.vb2_queue = src_vq; - mtk_vcodec_enc_set_default_params(ctx); - - if (v4l2_fh_is_singular(&ctx->fh)) { - /* - * load fireware to checks if it was loaded already and - * does nothing in that case - */ - ret = mtk_vcodec_fw_load_firmware(dev->fw_handler); - if (ret < 0) { - /* - * Return 0 if downloading firmware successfully, - * otherwise it is failed - */ - mtk_v4l2_err("vpu_load_firmware failed!"); - goto err_load_fw; - } - - dev->enc_capability = - mtk_vcodec_fw_get_venc_capa(dev->fw_handler); - mtk_v4l2_debug(0, "encoder capability %x", dev->enc_capability); - } - - mtk_v4l2_debug(2, "Create instance [%d]@%p m2m_ctx=%p ", - ctx->id, ctx, ctx->m2m_ctx); - - list_add(&ctx->list, &dev->ctx_list); - - mutex_unlock(&dev->dev_mutex); - mtk_v4l2_debug(0, "%s encoder [%d]", dev_name(&dev->plat_dev->dev), - ctx->id); - return ret; - - /* Deinit when failure occurred */ -err_load_fw: - v4l2_m2m_ctx_release(ctx->m2m_ctx); -err_m2m_ctx_init: - v4l2_ctrl_handler_free(&ctx->ctrl_hdl); -err_ctrls_setup: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); - mutex_unlock(&dev->dev_mutex); - - return ret; -} - -static int fops_vcodec_release(struct file *file) -{ - struct mtk_vcodec_dev *dev = video_drvdata(file); - struct mtk_vcodec_ctx *ctx = fh_to_ctx(file->private_data); - - mtk_v4l2_debug(1, "[%d] encoder", ctx->id); - mutex_lock(&dev->dev_mutex); - - v4l2_m2m_ctx_release(ctx->m2m_ctx); - mtk_vcodec_enc_release(ctx); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - v4l2_ctrl_handler_free(&ctx->ctrl_hdl); - - list_del_init(&ctx->list); - kfree(ctx); - mutex_unlock(&dev->dev_mutex); - return 0; -} - -static const struct v4l2_file_operations mtk_vcodec_fops = { - .owner = THIS_MODULE, - .open = fops_vcodec_open, - .release = fops_vcodec_release, - .poll = v4l2_m2m_fop_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = v4l2_m2m_fop_mmap, -}; - -static int mtk_vcodec_probe(struct platform_device *pdev) -{ - struct mtk_vcodec_dev *dev; - struct video_device *vfd_enc; - struct resource *res; - phandle rproc_phandle; - enum mtk_vcodec_fw_type fw_type; - int ret; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - INIT_LIST_HEAD(&dev->ctx_list); - dev->plat_dev = pdev; - - if (!of_property_read_u32(pdev->dev.of_node, "mediatek,vpu", - &rproc_phandle)) { - fw_type = VPU; - } else if (!of_property_read_u32(pdev->dev.of_node, "mediatek,scp", - &rproc_phandle)) { - fw_type = SCP; - } else { - mtk_v4l2_err("Could not get venc IPI device"); - return -ENODEV; - } - dma_set_max_seg_size(&pdev->dev, UINT_MAX); - - dev->fw_handler = mtk_vcodec_fw_select(dev, fw_type, ENCODER); - if (IS_ERR(dev->fw_handler)) - return PTR_ERR(dev->fw_handler); - - dev->venc_pdata = of_device_get_match_data(&pdev->dev); - ret = mtk_vcodec_init_enc_clk(dev); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get mtk vcodec clock source!"); - goto err_enc_pm; - } - - pm_runtime_enable(&pdev->dev); - - dev->reg_base[dev->venc_pdata->core_id] = - devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dev->reg_base[dev->venc_pdata->core_id])) { - ret = PTR_ERR(dev->reg_base[dev->venc_pdata->core_id]); - goto err_res; - } - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get irq resource"); - ret = -ENOENT; - goto err_res; - } - - dev->enc_irq = platform_get_irq(pdev, 0); - irq_set_status_flags(dev->enc_irq, IRQ_NOAUTOEN); - ret = devm_request_irq(&pdev->dev, dev->enc_irq, - mtk_vcodec_enc_irq_handler, - 0, pdev->name, dev); - if (ret) { - dev_err(&pdev->dev, - "Failed to install dev->enc_irq %d (%d) core_id (%d)", - dev->enc_irq, ret, dev->venc_pdata->core_id); - ret = -EINVAL; - goto err_res; - } - - mutex_init(&dev->enc_mutex); - mutex_init(&dev->dev_mutex); - spin_lock_init(&dev->irqlock); - - snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s", - "[MTK_V4L2_VENC]"); - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) { - mtk_v4l2_err("v4l2_device_register err=%d", ret); - goto err_res; - } - - init_waitqueue_head(&dev->queue); - - /* allocate video device for encoder and register it */ - vfd_enc = video_device_alloc(); - if (!vfd_enc) { - mtk_v4l2_err("Failed to allocate video device"); - ret = -ENOMEM; - goto err_enc_alloc; - } - vfd_enc->fops = &mtk_vcodec_fops; - vfd_enc->ioctl_ops = &mtk_venc_ioctl_ops; - vfd_enc->release = video_device_release; - vfd_enc->lock = &dev->dev_mutex; - vfd_enc->v4l2_dev = &dev->v4l2_dev; - vfd_enc->vfl_dir = VFL_DIR_M2M; - vfd_enc->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | - V4L2_CAP_STREAMING; - - snprintf(vfd_enc->name, sizeof(vfd_enc->name), "%s", - MTK_VCODEC_ENC_NAME); - video_set_drvdata(vfd_enc, dev); - dev->vfd_enc = vfd_enc; - platform_set_drvdata(pdev, dev); - - dev->m2m_dev_enc = v4l2_m2m_init(&mtk_venc_m2m_ops); - if (IS_ERR((__force void *)dev->m2m_dev_enc)) { - mtk_v4l2_err("Failed to init mem2mem enc device"); - ret = PTR_ERR((__force void *)dev->m2m_dev_enc); - goto err_enc_mem_init; - } - - dev->encode_workqueue = - alloc_ordered_workqueue(MTK_VCODEC_ENC_NAME, - WQ_MEM_RECLAIM | - WQ_FREEZABLE); - if (!dev->encode_workqueue) { - mtk_v4l2_err("Failed to create encode workqueue"); - ret = -EINVAL; - goto err_event_workq; - } - - if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) - dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34)); - - ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, -1); - if (ret) { - mtk_v4l2_err("Failed to register video device"); - goto err_enc_reg; - } - - mtk_v4l2_debug(0, "encoder %d registered as /dev/video%d", - dev->venc_pdata->core_id, vfd_enc->num); - - return 0; - -err_enc_reg: - destroy_workqueue(dev->encode_workqueue); -err_event_workq: - v4l2_m2m_release(dev->m2m_dev_enc); -err_enc_mem_init: - video_unregister_device(vfd_enc); -err_enc_alloc: - v4l2_device_unregister(&dev->v4l2_dev); -err_res: - pm_runtime_disable(dev->pm.dev); -err_enc_pm: - mtk_vcodec_fw_release(dev->fw_handler); - return ret; -} - -static const struct mtk_vcodec_enc_pdata mt8173_avc_pdata = { - .chip = MTK_MT8173, - .capture_formats = mtk_video_formats_capture_h264, - .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), - .output_formats = mtk_video_formats_output, - .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), - .min_bitrate = 64, - .max_bitrate = 60000000, - .core_id = VENC_SYS, -}; - -static const struct mtk_vcodec_enc_pdata mt8173_vp8_pdata = { - .chip = MTK_MT8173, - .capture_formats = mtk_video_formats_capture_vp8, - .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_vp8), - .output_formats = mtk_video_formats_output, - .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), - .min_bitrate = 64, - .max_bitrate = 9000000, - .core_id = VENC_LT_SYS, -}; - -static const struct mtk_vcodec_enc_pdata mt8183_pdata = { - .chip = MTK_MT8183, - .uses_ext = true, - .capture_formats = mtk_video_formats_capture_h264, - .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), - .output_formats = mtk_video_formats_output, - .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), - .min_bitrate = 64, - .max_bitrate = 40000000, - .core_id = VENC_SYS, -}; - -static const struct mtk_vcodec_enc_pdata mt8192_pdata = { - .chip = MTK_MT8192, - .uses_ext = true, - .capture_formats = mtk_video_formats_capture_h264, - .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), - .output_formats = mtk_video_formats_output, - .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), - .min_bitrate = 64, - .max_bitrate = 100000000, - .core_id = VENC_SYS, -}; - -static const struct mtk_vcodec_enc_pdata mt8195_pdata = { - .chip = MTK_MT8195, - .uses_ext = true, - .capture_formats = mtk_video_formats_capture_h264, - .num_capture_formats = ARRAY_SIZE(mtk_video_formats_capture_h264), - .output_formats = mtk_video_formats_output, - .num_output_formats = ARRAY_SIZE(mtk_video_formats_output), - .min_bitrate = 64, - .max_bitrate = 100000000, - .core_id = VENC_SYS, -}; - -static const struct of_device_id mtk_vcodec_enc_match[] = { - {.compatible = "mediatek,mt8173-vcodec-enc", - .data = &mt8173_avc_pdata}, - {.compatible = "mediatek,mt8173-vcodec-enc-vp8", - .data = &mt8173_vp8_pdata}, - {.compatible = "mediatek,mt8183-vcodec-enc", .data = &mt8183_pdata}, - {.compatible = "mediatek,mt8192-vcodec-enc", .data = &mt8192_pdata}, - {.compatible = "mediatek,mt8195-vcodec-enc", .data = &mt8195_pdata}, - {}, -}; -MODULE_DEVICE_TABLE(of, mtk_vcodec_enc_match); - -static int mtk_vcodec_enc_remove(struct platform_device *pdev) -{ - struct mtk_vcodec_dev *dev = platform_get_drvdata(pdev); - - mtk_v4l2_debug_enter(); - destroy_workqueue(dev->encode_workqueue); - if (dev->m2m_dev_enc) - v4l2_m2m_release(dev->m2m_dev_enc); - - if (dev->vfd_enc) - video_unregister_device(dev->vfd_enc); - - v4l2_device_unregister(&dev->v4l2_dev); - pm_runtime_disable(dev->pm.dev); - mtk_vcodec_fw_release(dev->fw_handler); - return 0; -} - -static struct platform_driver mtk_vcodec_enc_driver = { - .probe = mtk_vcodec_probe, - .remove = mtk_vcodec_enc_remove, - .driver = { - .name = MTK_VCODEC_ENC_NAME, - .of_match_table = mtk_vcodec_enc_match, - }, -}; - -module_platform_driver(mtk_vcodec_enc_driver); - - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Mediatek video codec V4L2 encoder driver"); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c deleted file mode 100644 index 7055954eb2af..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: Tiffany Lin -*/ - -#include -#include -#include -#include - -#include "mtk_vcodec_enc_pm.h" -#include "mtk_vcodec_util.h" - -int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *mtkdev) -{ - struct platform_device *pdev; - struct mtk_vcodec_pm *pm; - struct mtk_vcodec_clk *enc_clk; - struct mtk_vcodec_clk_info *clk_info; - int ret, i; - - pdev = mtkdev->plat_dev; - pm = &mtkdev->pm; - memset(pm, 0, sizeof(struct mtk_vcodec_pm)); - pm->dev = &pdev->dev; - enc_clk = &pm->venc_clk; - - enc_clk->clk_num = of_property_count_strings(pdev->dev.of_node, - "clock-names"); - if (enc_clk->clk_num > 0) { - enc_clk->clk_info = devm_kcalloc(&pdev->dev, - enc_clk->clk_num, sizeof(*clk_info), - GFP_KERNEL); - if (!enc_clk->clk_info) - return -ENOMEM; - } else { - mtk_v4l2_err("Failed to get venc clock count"); - return -EINVAL; - } - - for (i = 0; i < enc_clk->clk_num; i++) { - clk_info = &enc_clk->clk_info[i]; - ret = of_property_read_string_index(pdev->dev.of_node, - "clock-names", i, &clk_info->clk_name); - if (ret) { - mtk_v4l2_err("venc failed to get clk name %d", i); - return ret; - } - clk_info->vcodec_clk = devm_clk_get(&pdev->dev, - clk_info->clk_name); - if (IS_ERR(clk_info->vcodec_clk)) { - mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i, - clk_info->clk_name); - return PTR_ERR(clk_info->vcodec_clk); - } - } - - return 0; -} - -void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm) -{ - struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; - int ret, i = 0; - - for (i = 0; i < enc_clk->clk_num; i++) { - ret = clk_prepare_enable(enc_clk->clk_info[i].vcodec_clk); - if (ret) { - mtk_v4l2_err("venc clk_prepare_enable %d %s fail %d", i, - enc_clk->clk_info[i].clk_name, ret); - goto clkerr; - } - } - - return; - -clkerr: - for (i -= 1; i >= 0; i--) - clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); -} - -void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm) -{ - struct mtk_vcodec_clk *enc_clk = &pm->venc_clk; - int i = 0; - - for (i = enc_clk->clk_num - 1; i >= 0; i--) - clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk); -} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h deleted file mode 100644 index bc455cefc0cd..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h +++ /dev/null @@ -1,17 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: Tiffany Lin -*/ - -#ifndef _MTK_VCODEC_ENC_PM_H_ -#define _MTK_VCODEC_ENC_PM_H_ - -#include "mtk_vcodec_drv.h" - -int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *dev); - -void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm); -void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm); - -#endif /* _MTK_VCODEC_ENC_PM_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c deleted file mode 100644 index 94b39ae5c2e1..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "mtk_vcodec_fw.h" -#include "mtk_vcodec_fw_priv.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_drv.h" - -struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_type type, - enum mtk_vcodec_fw_use fw_use) -{ - switch (type) { - case VPU: - return mtk_vcodec_fw_vpu_init(dev, fw_use); - case SCP: - return mtk_vcodec_fw_scp_init(dev); - default: - mtk_v4l2_err("invalid vcodec fw type"); - return ERR_PTR(-EINVAL); - } -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_select); - -void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw) -{ - fw->ops->release(fw); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_release); - -int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw) -{ - return fw->ops->load_firmware(fw); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_load_firmware); - -unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw) -{ - return fw->ops->get_vdec_capa(fw); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_vdec_capa); - -unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw) -{ - return fw->ops->get_venc_capa(fw); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_get_venc_capa); - -void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr) -{ - return fw->ops->map_dm_addr(fw, mem_addr); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_map_dm_addr); - -int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id, - mtk_vcodec_ipi_handler handler, - const char *name, void *priv) -{ - return fw->ops->ipi_register(fw, id, handler, name, priv); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_register); - -int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, - unsigned int len, unsigned int wait) -{ - return fw->ops->ipi_send(fw, id, buf, len, wait); -} -EXPORT_SYMBOL_GPL(mtk_vcodec_fw_ipi_send); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h deleted file mode 100644 index 539bb626772c..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef _MTK_VCODEC_FW_H_ -#define _MTK_VCODEC_FW_H_ - -#include -#include - -#include "../mtk-vpu/mtk_vpu.h" - -struct mtk_vcodec_dev; - -enum mtk_vcodec_fw_type { - VPU, - SCP, -}; - -enum mtk_vcodec_fw_use { - DECODER, - ENCODER, -}; - -struct mtk_vcodec_fw; - -typedef void (*mtk_vcodec_ipi_handler) (void *data, - unsigned int len, void *priv); - -struct mtk_vcodec_fw *mtk_vcodec_fw_select(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_type type, - enum mtk_vcodec_fw_use fw_use); -void mtk_vcodec_fw_release(struct mtk_vcodec_fw *fw); - -int mtk_vcodec_fw_load_firmware(struct mtk_vcodec_fw *fw); -unsigned int mtk_vcodec_fw_get_vdec_capa(struct mtk_vcodec_fw *fw); -unsigned int mtk_vcodec_fw_get_venc_capa(struct mtk_vcodec_fw *fw); -void *mtk_vcodec_fw_map_dm_addr(struct mtk_vcodec_fw *fw, u32 mem_addr); -int mtk_vcodec_fw_ipi_register(struct mtk_vcodec_fw *fw, int id, - mtk_vcodec_ipi_handler handler, - const char *name, void *priv); -int mtk_vcodec_fw_ipi_send(struct mtk_vcodec_fw *fw, int id, - void *buf, unsigned int len, unsigned int wait); - -#endif /* _MTK_VCODEC_FW_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h deleted file mode 100644 index b41e66185cec..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ - -#ifndef _MTK_VCODEC_FW_PRIV_H_ -#define _MTK_VCODEC_FW_PRIV_H_ - -#include "mtk_vcodec_fw.h" - -struct mtk_vcodec_dev; - -struct mtk_vcodec_fw { - enum mtk_vcodec_fw_type type; - const struct mtk_vcodec_fw_ops *ops; - struct platform_device *pdev; - struct mtk_scp *scp; -}; - -struct mtk_vcodec_fw_ops { - int (*load_firmware)(struct mtk_vcodec_fw *fw); - unsigned int (*get_vdec_capa)(struct mtk_vcodec_fw *fw); - unsigned int (*get_venc_capa)(struct mtk_vcodec_fw *fw); - void *(*map_dm_addr)(struct mtk_vcodec_fw *fw, u32 dtcm_dmem_addr); - int (*ipi_register)(struct mtk_vcodec_fw *fw, int id, - mtk_vcodec_ipi_handler handler, const char *name, - void *priv); - int (*ipi_send)(struct mtk_vcodec_fw *fw, int id, void *buf, - unsigned int len, unsigned int wait); - void (*release)(struct mtk_vcodec_fw *fw); -}; - -#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_VPU) -struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_use fw_use); -#else -static inline struct mtk_vcodec_fw * -mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_use fw_use) -{ - return ERR_PTR(-ENODEV); -} -#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_VPU */ - -#if IS_ENABLED(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP) -struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev); -#else -static inline struct mtk_vcodec_fw * -mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev) -{ - return ERR_PTR(-ENODEV); -} -#endif /* CONFIG_VIDEO_MEDIATEK_VCODEC_SCP */ - -#endif /* _MTK_VCODEC_FW_PRIV_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c deleted file mode 100644 index d8e66b645bd8..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c +++ /dev/null @@ -1,73 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "mtk_vcodec_fw_priv.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_drv.h" - -static int mtk_vcodec_scp_load_firmware(struct mtk_vcodec_fw *fw) -{ - return rproc_boot(scp_get_rproc(fw->scp)); -} - -static unsigned int mtk_vcodec_scp_get_vdec_capa(struct mtk_vcodec_fw *fw) -{ - return scp_get_vdec_hw_capa(fw->scp); -} - -static unsigned int mtk_vcodec_scp_get_venc_capa(struct mtk_vcodec_fw *fw) -{ - return scp_get_venc_hw_capa(fw->scp); -} - -static void *mtk_vcodec_vpu_scp_dm_addr(struct mtk_vcodec_fw *fw, - u32 dtcm_dmem_addr) -{ - return scp_mapping_dm_addr(fw->scp, dtcm_dmem_addr); -} - -static int mtk_vcodec_scp_set_ipi_register(struct mtk_vcodec_fw *fw, int id, - mtk_vcodec_ipi_handler handler, - const char *name, void *priv) -{ - return scp_ipi_register(fw->scp, id, handler, priv); -} - -static int mtk_vcodec_scp_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, - unsigned int len, unsigned int wait) -{ - return scp_ipi_send(fw->scp, id, buf, len, wait); -} - -static void mtk_vcodec_scp_release(struct mtk_vcodec_fw *fw) -{ - scp_put(fw->scp); -} - -static const struct mtk_vcodec_fw_ops mtk_vcodec_rproc_msg = { - .load_firmware = mtk_vcodec_scp_load_firmware, - .get_vdec_capa = mtk_vcodec_scp_get_vdec_capa, - .get_venc_capa = mtk_vcodec_scp_get_venc_capa, - .map_dm_addr = mtk_vcodec_vpu_scp_dm_addr, - .ipi_register = mtk_vcodec_scp_set_ipi_register, - .ipi_send = mtk_vcodec_scp_ipi_send, - .release = mtk_vcodec_scp_release, -}; - -struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(struct mtk_vcodec_dev *dev) -{ - struct mtk_vcodec_fw *fw; - struct mtk_scp *scp; - - scp = scp_get(dev->plat_dev); - if (!scp) { - mtk_v4l2_err("could not get vdec scp handle"); - return ERR_PTR(-EPROBE_DEFER); - } - - fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); - fw->type = SCP; - fw->ops = &mtk_vcodec_rproc_msg; - fw->scp = scp; - - return fw; -} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c deleted file mode 100644 index cfc7ebed8fb7..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "mtk_vcodec_fw_priv.h" -#include "mtk_vcodec_util.h" -#include "mtk_vcodec_drv.h" - -static int mtk_vcodec_vpu_load_firmware(struct mtk_vcodec_fw *fw) -{ - return vpu_load_firmware(fw->pdev); -} - -static unsigned int mtk_vcodec_vpu_get_vdec_capa(struct mtk_vcodec_fw *fw) -{ - return vpu_get_vdec_hw_capa(fw->pdev); -} - -static unsigned int mtk_vcodec_vpu_get_venc_capa(struct mtk_vcodec_fw *fw) -{ - return vpu_get_venc_hw_capa(fw->pdev); -} - -static void *mtk_vcodec_vpu_map_dm_addr(struct mtk_vcodec_fw *fw, - u32 dtcm_dmem_addr) -{ - return vpu_mapping_dm_addr(fw->pdev, dtcm_dmem_addr); -} - -static int mtk_vcodec_vpu_set_ipi_register(struct mtk_vcodec_fw *fw, int id, - mtk_vcodec_ipi_handler handler, - const char *name, void *priv) -{ - /* - * The handler we receive takes a void * as its first argument. We - * cannot change this because it needs to be passed down to the rproc - * subsystem when SCP is used. VPU takes a const argument, which is - * more constrained, so the conversion below is safe. - */ - ipi_handler_t handler_const = (ipi_handler_t)handler; - - return vpu_ipi_register(fw->pdev, id, handler_const, name, priv); -} - -static int mtk_vcodec_vpu_ipi_send(struct mtk_vcodec_fw *fw, int id, void *buf, - unsigned int len, unsigned int wait) -{ - return vpu_ipi_send(fw->pdev, id, buf, len); -} - -static void mtk_vcodec_vpu_release(struct mtk_vcodec_fw *fw) -{ - put_device(&fw->pdev->dev); -} - -static void mtk_vcodec_vpu_reset_handler(void *priv) -{ - struct mtk_vcodec_dev *dev = priv; - struct mtk_vcodec_ctx *ctx; - - mtk_v4l2_err("Watchdog timeout!!"); - - mutex_lock(&dev->dev_mutex); - list_for_each_entry(ctx, &dev->ctx_list, list) { - ctx->state = MTK_STATE_ABORT; - mtk_v4l2_debug(0, "[%d] Change to state MTK_STATE_ABORT", - ctx->id); - } - mutex_unlock(&dev->dev_mutex); -} - -static const struct mtk_vcodec_fw_ops mtk_vcodec_vpu_msg = { - .load_firmware = mtk_vcodec_vpu_load_firmware, - .get_vdec_capa = mtk_vcodec_vpu_get_vdec_capa, - .get_venc_capa = mtk_vcodec_vpu_get_venc_capa, - .map_dm_addr = mtk_vcodec_vpu_map_dm_addr, - .ipi_register = mtk_vcodec_vpu_set_ipi_register, - .ipi_send = mtk_vcodec_vpu_ipi_send, - .release = mtk_vcodec_vpu_release, -}; - -struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev, - enum mtk_vcodec_fw_use fw_use) -{ - struct platform_device *fw_pdev; - struct mtk_vcodec_fw *fw; - enum rst_id rst_id; - - switch (fw_use) { - case ENCODER: - rst_id = VPU_RST_ENC; - break; - case DECODER: - default: - rst_id = VPU_RST_DEC; - break; - } - - fw_pdev = vpu_get_plat_device(dev->plat_dev); - if (!fw_pdev) { - mtk_v4l2_err("firmware device is not ready"); - return ERR_PTR(-EINVAL); - } - vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_handler, dev, rst_id); - - fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL); - if (!fw) - return ERR_PTR(-ENOMEM); - fw->type = VPU; - fw->ops = &mtk_vcodec_vpu_msg; - fw->pdev = fw_pdev; - - return fw; -} diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c deleted file mode 100644 index 552b4c93d972..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: Tiffany Lin -*/ - -#include -#include - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_intr.h" -#include "mtk_vcodec_util.h" - -int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, - int command, unsigned int timeout_ms, - unsigned int hw_id) -{ - long timeout_jiff, ret; - int status = 0; - - timeout_jiff = msecs_to_jiffies(timeout_ms); - ret = wait_event_interruptible_timeout(ctx->queue[hw_id], - ctx->int_cond[hw_id], - timeout_jiff); - - if (!ret) { - status = -1; /* timeout */ - mtk_v4l2_err("[%d] cmd=%d, type=%d, dec timeout=%ums (%d %d)", - ctx->id, command, ctx->type, timeout_ms, - ctx->int_cond[hw_id], ctx->int_type[hw_id]); - } else if (-ERESTARTSYS == ret) { - status = -1; - mtk_v4l2_err("[%d] cmd=%d, type=%d, dec inter fail (%d %d)", - ctx->id, command, ctx->type, - ctx->int_cond[hw_id], ctx->int_type[hw_id]); - } - - ctx->int_cond[hw_id] = 0; - ctx->int_type[hw_id] = 0; - - return status; -} -EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h deleted file mode 100644 index 9681f492813b..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: Tiffany Lin -*/ - -#ifndef _MTK_VCODEC_INTR_H_ -#define _MTK_VCODEC_INTR_H_ - -#define MTK_INST_IRQ_RECEIVED 0x1 - -struct mtk_vcodec_ctx; - -/* timeout is ms */ -int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, - int command, unsigned int timeout_ms, - unsigned int hw_id); - -#endif /* _MTK_VCODEC_INTR_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c deleted file mode 100644 index ace78c4b5b9e..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#include -#include -#include - -#include "mtk_vcodec_dec_hw.h" -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_util.h" - -void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, - unsigned int reg_idx) -{ - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; - - if (!data || reg_idx >= NUM_MAX_VCODEC_REG_BASE) { - mtk_v4l2_err("Invalid arguments, reg_idx=%d", reg_idx); - return NULL; - } - return ctx->dev->reg_base[reg_idx]; -} -EXPORT_SYMBOL(mtk_vcodec_get_reg_addr); - -int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem) -{ - unsigned long size = mem->size; - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; - struct device *dev = &ctx->dev->plat_dev->dev; - - mem->va = dma_alloc_coherent(dev, size, &mem->dma_addr, GFP_KERNEL); - if (!mem->va) { - mtk_v4l2_err("%s dma_alloc size=%ld failed!", dev_name(dev), - size); - return -ENOMEM; - } - - mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); - mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, - (unsigned long)mem->dma_addr); - mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); - - return 0; -} -EXPORT_SYMBOL(mtk_vcodec_mem_alloc); - -void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem) -{ - unsigned long size = mem->size; - struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)data; - struct device *dev = &ctx->dev->plat_dev->dev; - - if (!mem->va) { - mtk_v4l2_err("%s dma_free size=%ld failed!", dev_name(dev), - size); - return; - } - - mtk_v4l2_debug(3, "[%d] - va = %p", ctx->id, mem->va); - mtk_v4l2_debug(3, "[%d] - dma = 0x%lx", ctx->id, - (unsigned long)mem->dma_addr); - mtk_v4l2_debug(3, "[%d] size = 0x%lx", ctx->id, size); - - dma_free_coherent(dev, size, mem->va, mem->dma_addr); - mem->va = NULL; - mem->dma_addr = 0; - mem->size = 0; -} -EXPORT_SYMBOL(mtk_vcodec_mem_free); - -void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx) -{ - if (hw_idx >= MTK_VDEC_HW_MAX || hw_idx < 0 || !dev->subdev_dev[hw_idx]) { - mtk_v4l2_err("hw idx is out of range:%d", hw_idx); - return NULL; - } - - return dev->subdev_dev[hw_idx]; -} -EXPORT_SYMBOL(mtk_vcodec_get_hw_dev); - -void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - struct mtk_vcodec_ctx *ctx, int hw_idx) -{ - unsigned long flags; - struct mtk_vdec_hw_dev *subdev_dev; - - spin_lock_irqsave(&vdec_dev->irqlock, flags); - if (vdec_dev->vdec_pdata->is_subdev_supported) { - subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); - if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev"); - spin_unlock_irqrestore(&vdec_dev->irqlock, flags); - return; - } - subdev_dev->curr_ctx = ctx; - } else { - vdec_dev->curr_ctx = ctx; - } - spin_unlock_irqrestore(&vdec_dev->irqlock, flags); -} -EXPORT_SYMBOL(mtk_vcodec_set_curr_ctx); - -struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - unsigned int hw_idx) -{ - unsigned long flags; - struct mtk_vcodec_ctx *ctx; - struct mtk_vdec_hw_dev *subdev_dev; - - spin_lock_irqsave(&vdec_dev->irqlock, flags); - if (vdec_dev->vdec_pdata->is_subdev_supported) { - subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx); - if (!subdev_dev) { - mtk_v4l2_err("Failed to get hw dev"); - spin_unlock_irqrestore(&vdec_dev->irqlock, flags); - return NULL; - } - ctx = subdev_dev->curr_ctx; - } else { - ctx = vdec_dev->curr_ctx; - } - spin_unlock_irqrestore(&vdec_dev->irqlock, flags); - return ctx; -} -EXPORT_SYMBOL(mtk_vcodec_get_curr_ctx); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Mediatek video codec driver"); diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h deleted file mode 100644 index 71956627a0e2..000000000000 --- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h +++ /dev/null @@ -1,63 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* -* Copyright (c) 2016 MediaTek Inc. -* Author: PC Chen -* Tiffany Lin -*/ - -#ifndef _MTK_VCODEC_UTIL_H_ -#define _MTK_VCODEC_UTIL_H_ - -#include -#include - -struct mtk_vcodec_mem { - size_t size; - void *va; - dma_addr_t dma_addr; -}; - -struct mtk_vcodec_fb { - size_t size; - dma_addr_t dma_addr; -}; - -struct mtk_vcodec_ctx; -struct mtk_vcodec_dev; - -#undef pr_fmt -#define pr_fmt(fmt) "%s(),%d: " fmt, __func__, __LINE__ - -#define mtk_v4l2_err(fmt, args...) \ - pr_err("[MTK_V4L2][ERROR] " fmt "\n", ##args) - -#define mtk_vcodec_err(h, fmt, args...) \ - pr_err("[MTK_VCODEC][ERROR][%d]: " fmt "\n", \ - ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) - - -#define mtk_v4l2_debug(level, fmt, args...) pr_debug(fmt, ##args) - -#define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+") -#define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-") - -#define mtk_vcodec_debug(h, fmt, args...) \ - pr_debug("[MTK_VCODEC][%d]: " fmt "\n", \ - ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) - -#define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+") -#define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-") - -void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, - unsigned int reg_idx); -int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem); -void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data, - struct mtk_vcodec_mem *mem); -void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - struct mtk_vcodec_ctx *ctx, int hw_idx); -struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev, - unsigned int hw_idx); -void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx); - -#endif /* _MTK_VCODEC_UTIL_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c deleted file mode 100644 index 481655bb6016..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c +++ /dev/null @@ -1,503 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - */ - -#include -#include - -#include "../vdec_drv_if.h" -#include "../mtk_vcodec_util.h" -#include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" -#include "../vdec_vpu_if.h" -#include "../vdec_drv_base.h" - -#define NAL_NON_IDR_SLICE 0x01 -#define NAL_IDR_SLICE 0x05 -#define NAL_H264_PPS 0x08 -#define NAL_TYPE(value) ((value) & 0x1F) - -#define BUF_PREDICTION_SZ (32 * 1024) - -#define MB_UNIT_LEN 16 - -/* motion vector size (bytes) for every macro block */ -#define HW_MB_STORE_SZ 64 - -#define H264_MAX_FB_NUM 17 -#define HDR_PARSING_BUF_SZ 1024 - -#define DEC_ERR_RET(ret) ((ret) >> 16) -#define H264_ERR_NOT_VALID 3 - -/** - * struct h264_fb - h264 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 h264_fb { - uint64_t vdec_fb_va; - uint64_t y_fb_dma; - uint64_t c_fb_dma; - int32_t poc; - uint32_t reserved; -}; - -/** - * struct h264_ring_fb_list - ring frame buffer list - * @fb_list : frame buffer array - * @read_idx : read index - * @write_idx : write index - * @count : buffer count in list - * @reserved : for 8 bytes alignment - */ -struct h264_ring_fb_list { - struct h264_fb fb_list[H264_MAX_FB_NUM]; - unsigned int read_idx; - unsigned int write_idx; - unsigned int count; - unsigned int reserved; -}; - -/** - * struct vdec_h264_dec_info - decode information - * @dpb_sz : decoding picture buffer size - * @resolution_changed : resolution change happen - * @realloc_mv_buf : flag to notify driver to re-allocate mv buffer - * @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_h264_dec_info { - uint32_t dpb_sz; - uint32_t resolution_changed; - uint32_t realloc_mv_buf; - uint32_t reserved; - uint64_t bs_dma; - uint64_t y_fb_dma; - uint64_t c_fb_dma; - uint64_t vdec_fb_va; -}; - -/** - * struct vdec_h264_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) - * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) - * @mv_buf_dma : HW working motion vector buffer dma address (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_h264_vsi { - unsigned char hdr_buf[HDR_PARSING_BUF_SZ]; - uint64_t pred_buf_dma; - uint64_t mv_buf_dma[H264_MAX_FB_NUM]; - struct h264_ring_fb_list list_free; - struct h264_ring_fb_list list_disp; - struct vdec_h264_dec_info dec; - struct vdec_pic_info pic; - struct v4l2_rect crop; -}; - -/** - * struct vdec_h264_inst - h264 decoder instance - * @num_nalu : how many nalus be decoded - * @ctx : point to mtk_vcodec_ctx - * @pred_buf : HW working predication buffer - * @mv_buf : HW working motion vector buffer - * @vpu : VPU instance - * @vsi : VPU shared information - */ -struct vdec_h264_inst { - unsigned int num_nalu; - struct mtk_vcodec_ctx *ctx; - struct mtk_vcodec_mem pred_buf; - struct mtk_vcodec_mem mv_buf[H264_MAX_FB_NUM]; - struct vdec_vpu_inst vpu; - struct vdec_h264_vsi *vsi; -}; - -static unsigned int get_mv_buf_size(unsigned int width, unsigned int height) -{ - return HW_MB_STORE_SZ * (width/MB_UNIT_LEN) * (height/MB_UNIT_LEN); -} - -static int allocate_predication_buf(struct vdec_h264_inst *inst) -{ - int err = 0; - - inst->pred_buf.size = BUF_PREDICTION_SZ; - err = mtk_vcodec_mem_alloc(inst->ctx, &inst->pred_buf); - if (err) { - mtk_vcodec_err(inst, "failed to allocate ppl buf"); - return err; - } - - inst->vsi->pred_buf_dma = inst->pred_buf.dma_addr; - return 0; -} - -static void free_predication_buf(struct vdec_h264_inst *inst) -{ - struct mtk_vcodec_mem *mem = NULL; - - mtk_vcodec_debug_enter(inst); - - inst->vsi->pred_buf_dma = 0; - mem = &inst->pred_buf; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); -} - -static int alloc_mv_buf(struct vdec_h264_inst *inst, struct vdec_pic_info *pic) -{ - int i; - int err; - struct mtk_vcodec_mem *mem = NULL; - unsigned int buf_sz = get_mv_buf_size(pic->buf_w, pic->buf_h); - - for (i = 0; i < H264_MAX_FB_NUM; i++) { - mem = &inst->mv_buf[i]; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - mem->size = buf_sz; - err = mtk_vcodec_mem_alloc(inst->ctx, mem); - if (err) { - mtk_vcodec_err(inst, "failed to allocate mv buf"); - return err; - } - inst->vsi->mv_buf_dma[i] = mem->dma_addr; - } - - return 0; -} - -static void free_mv_buf(struct vdec_h264_inst *inst) -{ - int i; - struct mtk_vcodec_mem *mem = NULL; - - for (i = 0; i < H264_MAX_FB_NUM; i++) { - inst->vsi->mv_buf_dma[i] = 0; - mem = &inst->mv_buf[i]; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - } -} - -static int check_list_validity(struct vdec_h264_inst *inst, bool disp_list) -{ - struct h264_ring_fb_list *list; - - list = disp_list ? &inst->vsi->list_disp : &inst->vsi->list_free; - - if (list->count > H264_MAX_FB_NUM || - list->read_idx >= H264_MAX_FB_NUM || - list->write_idx >= H264_MAX_FB_NUM) { - mtk_vcodec_err(inst, "%s list err: cnt=%d r_idx=%d w_idx=%d", - disp_list ? "disp" : "free", list->count, - list->read_idx, list->write_idx); - return -EINVAL; - } - - return 0; -} - -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 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, "fb size: Y(%d), C(%d)", - pic->fb_sz[0], pic->fb_sz[1]); -} - -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 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 vdec_h264_init(struct mtk_vcodec_ctx *ctx) -{ - 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.ctx = ctx; - - 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); - - ctx->drv_handle = inst; - return 0; - -error_deinit: - vpu_dec_deinit(&inst->vpu); - -error_free_inst: - kfree(inst); - return err; -} - -static void vdec_h264_deinit(void *h_vdec) -{ - struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; - - mtk_vcodec_debug_enter(inst); - - vpu_dec_deinit(&inst->vpu); - free_predication_buf(inst); - free_mv_buf(inst); - - kfree(inst); -} - -static int find_start_code(unsigned char *data, unsigned int data_sz) -{ - if (data_sz > 3 && data[0] == 0 && data[1] == 0 && data[2] == 1) - return 3; - - if (data_sz > 4 && data[0] == 0 && data[1] == 0 && data[2] == 0 && - data[3] == 1) - return 4; - - return -1; -} - -static int vdec_h264_decode(void *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) { - mtk_vcodec_err(inst, "invalid nal start code"); - err = -EIO; - 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) { - if (err > 0 && (DEC_ERR_RET(err) == H264_ERR_NOT_VALID)) { - mtk_vcodec_err(inst, "- error bitstream - err = %d -", - err); - err = -EIO; - } - 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, 0); - 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 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 int vdec_h264_get_param(void *h_vdec, enum vdec_get_param_type type, - void *out) -{ - struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec; - - switch (type) { - case GET_PARAM_DISP_FRAME_BUFFER: - vdec_h264_get_fb(inst, &inst->vsi->list_disp, true, out); - break; - - case GET_PARAM_FREE_FRAME_BUFFER: - vdec_h264_get_fb(inst, &inst->vsi->list_free, false, 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: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); - return -EINVAL; - } - - return 0; -} - -const struct vdec_common_if vdec_h264_if = { - .init = vdec_h264_init, - .decode = vdec_h264_decode, - .get_param = vdec_h264_get_param, - .deinit = vdec_h264_deinit, -}; diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c deleted file mode 100644 index 43542de11e9c..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c +++ /dev/null @@ -1,774 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include -#include -#include - -#include "../mtk_vcodec_util.h" -#include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" -#include "../vdec_drv_base.h" -#include "../vdec_drv_if.h" -#include "../vdec_vpu_if.h" - -#define BUF_PREDICTION_SZ (64 * 4096) -#define MB_UNIT_LEN 16 - -/* get used parameters for sps/pps */ -#define GET_MTK_VDEC_FLAG(cond, flag) \ - { dst_param->cond = ((src_param->flags & (flag)) ? (1) : (0)); } -#define GET_MTK_VDEC_PARAM(param) \ - { dst_param->param = src_param->param; } -/* motion vector size (bytes) for every macro block */ -#define HW_MB_STORE_SZ 64 - -#define H264_MAX_FB_NUM 17 -#define H264_MAX_MV_NUM 32 -#define HDR_PARSING_BUF_SZ 1024 - -/** - * struct mtk_h264_dpb_info - h264 dpb information - * @y_dma_addr: Y bitstream physical address - * @c_dma_addr: CbCr bitstream physical address - * @reference_flag: reference picture flag (short/long term reference picture) - * @field: field picture flag - */ -struct mtk_h264_dpb_info { - dma_addr_t y_dma_addr; - dma_addr_t c_dma_addr; - int reference_flag; - int field; -}; - -/* - * struct mtk_h264_sps_param - parameters for sps - */ -struct mtk_h264_sps_param { - unsigned char chroma_format_idc; - unsigned char bit_depth_luma_minus8; - unsigned char bit_depth_chroma_minus8; - unsigned char log2_max_frame_num_minus4; - unsigned char pic_order_cnt_type; - unsigned char log2_max_pic_order_cnt_lsb_minus4; - unsigned char max_num_ref_frames; - unsigned char separate_colour_plane_flag; - unsigned short pic_width_in_mbs_minus1; - unsigned short pic_height_in_map_units_minus1; - unsigned int max_frame_nums; - unsigned char qpprime_y_zero_transform_bypass_flag; - unsigned char delta_pic_order_always_zero_flag; - unsigned char frame_mbs_only_flag; - unsigned char mb_adaptive_frame_field_flag; - unsigned char direct_8x8_inference_flag; - unsigned char reserved[3]; -}; - -/* - * struct mtk_h264_pps_param - parameters for pps - */ -struct mtk_h264_pps_param { - unsigned char num_ref_idx_l0_default_active_minus1; - unsigned char num_ref_idx_l1_default_active_minus1; - unsigned char weighted_bipred_idc; - char pic_init_qp_minus26; - char chroma_qp_index_offset; - char second_chroma_qp_index_offset; - unsigned char entropy_coding_mode_flag; - unsigned char pic_order_present_flag; - unsigned char deblocking_filter_control_present_flag; - unsigned char constrained_intra_pred_flag; - unsigned char weighted_pred_flag; - unsigned char redundant_pic_cnt_present_flag; - unsigned char transform_8x8_mode_flag; - unsigned char scaling_matrix_present_flag; - unsigned char reserved[2]; -}; - -struct slice_api_h264_scaling_matrix { - unsigned char scaling_list_4x4[6][16]; - unsigned char scaling_list_8x8[6][64]; -}; - -struct slice_h264_dpb_entry { - unsigned long long reference_ts; - unsigned short frame_num; - unsigned short pic_num; - /* Note that field is indicated by v4l2_buffer.field */ - int top_field_order_cnt; - int bottom_field_order_cnt; - unsigned int flags; /* V4L2_H264_DPB_ENTRY_FLAG_* */ -}; - -/* - * struct slice_api_h264_decode_param - parameters for decode. - */ -struct slice_api_h264_decode_param { - struct slice_h264_dpb_entry dpb[16]; - unsigned short num_slices; - unsigned short nal_ref_idc; - unsigned char ref_pic_list_p0[32]; - unsigned char ref_pic_list_b0[32]; - unsigned char ref_pic_list_b1[32]; - int top_field_order_cnt; - int bottom_field_order_cnt; - unsigned int flags; /* V4L2_H264_DECODE_PARAM_FLAG_* */ -}; - -/* - * struct mtk_h264_dec_slice_param - parameters for decode current frame - */ -struct mtk_h264_dec_slice_param { - struct mtk_h264_sps_param sps; - struct mtk_h264_pps_param pps; - struct slice_api_h264_scaling_matrix scaling_matrix; - struct slice_api_h264_decode_param decode_params; - struct mtk_h264_dpb_info h264_dpb_info[16]; -}; - -/** - * struct h264_fb - h264 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 h264_fb { - u64 vdec_fb_va; - u64 y_fb_dma; - u64 c_fb_dma; - s32 poc; - u32 reserved; -}; - -/** - * struct vdec_h264_dec_info - decode information - * @dpb_sz : decoding picture buffer size - * @resolution_changed : resoltion change happen - * @realloc_mv_buf : flag to notify driver to re-allocate mv buffer - * @cap_num_planes : number planes of capture buffer - * @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_h264_dec_info { - u32 dpb_sz; - u32 resolution_changed; - u32 realloc_mv_buf; - u32 cap_num_planes; - u64 bs_dma; - u64 y_fb_dma; - u64 c_fb_dma; - u64 vdec_fb_va; -}; - -/** - * struct vdec_h264_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 - * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R) - * @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R) - * @dec : decode information (AP-R, VPU-W) - * @pic : picture information (AP-R, VPU-W) - * @crop : crop information (AP-R, VPU-W) - * @h264_slice_params : the parameters that hardware use to decode - */ -struct vdec_h264_vsi { - u64 pred_buf_dma; - u64 mv_buf_dma[H264_MAX_MV_NUM]; - struct vdec_h264_dec_info dec; - struct vdec_pic_info pic; - struct v4l2_rect crop; - struct mtk_h264_dec_slice_param h264_slice_params; -}; - -/** - * struct vdec_h264_slice_inst - h264 decoder instance - * @num_nalu : how many nalus be decoded - * @ctx : point to mtk_vcodec_ctx - * @pred_buf : HW working predication buffer - * @mv_buf : HW working motion vector buffer - * @vpu : VPU instance - * @vsi_ctx : Local VSI data for this decoding context - * @h264_slice_param : the parameters that hardware use to decode - * @dpb : decoded picture buffer used to store reference buffer information - */ -struct vdec_h264_slice_inst { - unsigned int num_nalu; - struct mtk_vcodec_ctx *ctx; - struct mtk_vcodec_mem pred_buf; - struct mtk_vcodec_mem mv_buf[H264_MAX_MV_NUM]; - struct vdec_vpu_inst vpu; - struct vdec_h264_vsi vsi_ctx; - struct mtk_h264_dec_slice_param h264_slice_param; - - struct v4l2_h264_dpb_entry dpb[16]; -}; - -static void *get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) -{ - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); - - return ctrl->p_cur.p; -} - -static void get_h264_dpb_list(struct vdec_h264_slice_inst *inst, - struct mtk_h264_dec_slice_param *slice_param) -{ - struct vb2_queue *vq; - struct vb2_buffer *vb; - struct vb2_v4l2_buffer *vb2_v4l2; - u64 index; - - vq = v4l2_m2m_get_vq(inst->ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); - - for (index = 0; index < ARRAY_SIZE(slice_param->decode_params.dpb); index++) { - const struct slice_h264_dpb_entry *dpb; - int vb2_index; - - dpb = &slice_param->decode_params.dpb[index]; - if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) { - slice_param->h264_dpb_info[index].reference_flag = 0; - continue; - } - - vb2_index = vb2_find_timestamp(vq, dpb->reference_ts, 0); - if (vb2_index < 0) { - mtk_vcodec_err(inst, "Reference invalid: dpb_index(%lld) reference_ts(%lld)", - index, dpb->reference_ts); - continue; - } - /* 1 for short term reference, 2 for long term reference */ - if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)) - slice_param->h264_dpb_info[index].reference_flag = 1; - else - slice_param->h264_dpb_info[index].reference_flag = 2; - - vb = vq->bufs[vb2_index]; - vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf); - slice_param->h264_dpb_info[index].field = vb2_v4l2->field; - - slice_param->h264_dpb_info[index].y_dma_addr = - vb2_dma_contig_plane_dma_addr(vb, 0); - if (inst->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) { - slice_param->h264_dpb_info[index].c_dma_addr = - vb2_dma_contig_plane_dma_addr(vb, 1); - } - } -} - -static void get_h264_sps_parameters(struct mtk_h264_sps_param *dst_param, - const struct v4l2_ctrl_h264_sps *src_param) -{ - GET_MTK_VDEC_PARAM(chroma_format_idc); - GET_MTK_VDEC_PARAM(bit_depth_luma_minus8); - GET_MTK_VDEC_PARAM(bit_depth_chroma_minus8); - GET_MTK_VDEC_PARAM(log2_max_frame_num_minus4); - GET_MTK_VDEC_PARAM(pic_order_cnt_type); - GET_MTK_VDEC_PARAM(log2_max_pic_order_cnt_lsb_minus4); - GET_MTK_VDEC_PARAM(max_num_ref_frames); - GET_MTK_VDEC_PARAM(pic_width_in_mbs_minus1); - GET_MTK_VDEC_PARAM(pic_height_in_map_units_minus1); - - GET_MTK_VDEC_FLAG(separate_colour_plane_flag, - V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE); - GET_MTK_VDEC_FLAG(qpprime_y_zero_transform_bypass_flag, - V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS); - GET_MTK_VDEC_FLAG(delta_pic_order_always_zero_flag, - V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO); - GET_MTK_VDEC_FLAG(frame_mbs_only_flag, - V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY); - GET_MTK_VDEC_FLAG(mb_adaptive_frame_field_flag, - V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD); - GET_MTK_VDEC_FLAG(direct_8x8_inference_flag, - V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE); -} - -static void get_h264_pps_parameters(struct mtk_h264_pps_param *dst_param, - const struct v4l2_ctrl_h264_pps *src_param) -{ - GET_MTK_VDEC_PARAM(num_ref_idx_l0_default_active_minus1); - GET_MTK_VDEC_PARAM(num_ref_idx_l1_default_active_minus1); - GET_MTK_VDEC_PARAM(weighted_bipred_idc); - GET_MTK_VDEC_PARAM(pic_init_qp_minus26); - GET_MTK_VDEC_PARAM(chroma_qp_index_offset); - GET_MTK_VDEC_PARAM(second_chroma_qp_index_offset); - - GET_MTK_VDEC_FLAG(entropy_coding_mode_flag, - V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE); - GET_MTK_VDEC_FLAG(pic_order_present_flag, - V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT); - GET_MTK_VDEC_FLAG(weighted_pred_flag, - V4L2_H264_PPS_FLAG_WEIGHTED_PRED); - GET_MTK_VDEC_FLAG(deblocking_filter_control_present_flag, - V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); - GET_MTK_VDEC_FLAG(constrained_intra_pred_flag, - V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED); - GET_MTK_VDEC_FLAG(redundant_pic_cnt_present_flag, - V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT); - GET_MTK_VDEC_FLAG(transform_8x8_mode_flag, - V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE); - GET_MTK_VDEC_FLAG(scaling_matrix_present_flag, - V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT); -} - -static void -get_h264_scaling_matrix(struct slice_api_h264_scaling_matrix *dst_matrix, - const struct v4l2_ctrl_h264_scaling_matrix *src_matrix) -{ - memcpy(dst_matrix->scaling_list_4x4, src_matrix->scaling_list_4x4, - sizeof(dst_matrix->scaling_list_4x4)); - - memcpy(dst_matrix->scaling_list_8x8, src_matrix->scaling_list_8x8, - sizeof(dst_matrix->scaling_list_8x8)); -} - -static void -get_h264_decode_parameters(struct slice_api_h264_decode_param *dst_params, - const struct v4l2_ctrl_h264_decode_params *src_params, - const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(dst_params->dpb); i++) { - struct slice_h264_dpb_entry *dst_entry = &dst_params->dpb[i]; - const struct v4l2_h264_dpb_entry *src_entry = &dpb[i]; - - dst_entry->reference_ts = src_entry->reference_ts; - dst_entry->frame_num = src_entry->frame_num; - dst_entry->pic_num = src_entry->pic_num; - dst_entry->top_field_order_cnt = src_entry->top_field_order_cnt; - dst_entry->bottom_field_order_cnt = - src_entry->bottom_field_order_cnt; - dst_entry->flags = src_entry->flags; - } - - /* - * num_slices is a leftover from the old H.264 support and is ignored - * by the firmware. - */ - dst_params->num_slices = 0; - dst_params->nal_ref_idc = src_params->nal_ref_idc; - dst_params->top_field_order_cnt = src_params->top_field_order_cnt; - dst_params->bottom_field_order_cnt = src_params->bottom_field_order_cnt; - dst_params->flags = src_params->flags; -} - -static bool dpb_entry_match(const struct v4l2_h264_dpb_entry *a, - const struct v4l2_h264_dpb_entry *b) -{ - return a->top_field_order_cnt == b->top_field_order_cnt && - a->bottom_field_order_cnt == b->bottom_field_order_cnt; -} - -/* - * Move DPB entries of dec_param that refer to a frame already existing in dpb - * into the already existing slot in dpb, and move other entries into new slots. - * - * This function is an adaptation of the similarly-named function in - * hantro_h264.c. - */ -static void update_dpb(const struct v4l2_ctrl_h264_decode_params *dec_param, - struct v4l2_h264_dpb_entry *dpb) -{ - DECLARE_BITMAP(new, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - DECLARE_BITMAP(in_use, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - DECLARE_BITMAP(used, ARRAY_SIZE(dec_param->dpb)) = { 0, }; - unsigned int i, j; - - /* Disable all entries by default, and mark the ones in use. */ - for (i = 0; i < ARRAY_SIZE(dec_param->dpb); i++) { - if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE) - set_bit(i, in_use); - dpb[i].flags &= ~V4L2_H264_DPB_ENTRY_FLAG_ACTIVE; - } - - /* Try to match new DPB entries with existing ones by their POCs. */ - for (i = 0; i < ARRAY_SIZE(dec_param->dpb); i++) { - const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; - - if (!(ndpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) - continue; - - /* - * To cut off some comparisons, iterate only on target DPB - * entries were already used. - */ - for_each_set_bit(j, in_use, ARRAY_SIZE(dec_param->dpb)) { - struct v4l2_h264_dpb_entry *cdpb; - - cdpb = &dpb[j]; - if (!dpb_entry_match(cdpb, ndpb)) - continue; - - *cdpb = *ndpb; - set_bit(j, used); - /* Don't reiterate on this one. */ - clear_bit(j, in_use); - break; - } - - if (j == ARRAY_SIZE(dec_param->dpb)) - set_bit(i, new); - } - - /* For entries that could not be matched, use remaining free slots. */ - for_each_set_bit(i, new, ARRAY_SIZE(dec_param->dpb)) { - const struct v4l2_h264_dpb_entry *ndpb = &dec_param->dpb[i]; - struct v4l2_h264_dpb_entry *cdpb; - - /* - * Both arrays are of the same sizes, so there is no way - * we can end up with no space in target array, unless - * something is buggy. - */ - j = find_first_zero_bit(used, ARRAY_SIZE(dec_param->dpb)); - if (WARN_ON(j >= ARRAY_SIZE(dec_param->dpb))) - return; - - cdpb = &dpb[j]; - *cdpb = *ndpb; - set_bit(j, used); - } -} - -/* - * The firmware expects unused reflist entries to have the value 0x20. - */ -static void fixup_ref_list(u8 *ref_list, size_t num_valid) -{ - memset(&ref_list[num_valid], 0x20, 32 - num_valid); -} - -static void get_vdec_decode_parameters(struct vdec_h264_slice_inst *inst) -{ - const struct v4l2_ctrl_h264_decode_params *dec_params = - get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); - const struct v4l2_ctrl_h264_sps *sps = - get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_SPS); - const struct v4l2_ctrl_h264_pps *pps = - get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_PPS); - const struct v4l2_ctrl_h264_scaling_matrix *scaling_matrix = - get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_SCALING_MATRIX); - struct mtk_h264_dec_slice_param *slice_param = &inst->h264_slice_param; - struct v4l2_h264_reflist_builder reflist_builder; - u8 *p0_reflist = slice_param->decode_params.ref_pic_list_p0; - u8 *b0_reflist = slice_param->decode_params.ref_pic_list_b0; - u8 *b1_reflist = slice_param->decode_params.ref_pic_list_b1; - - update_dpb(dec_params, inst->dpb); - - get_h264_sps_parameters(&slice_param->sps, sps); - get_h264_pps_parameters(&slice_param->pps, pps); - get_h264_scaling_matrix(&slice_param->scaling_matrix, scaling_matrix); - get_h264_decode_parameters(&slice_param->decode_params, dec_params, - inst->dpb); - get_h264_dpb_list(inst, slice_param); - - /* Build the reference lists */ - v4l2_h264_init_reflist_builder(&reflist_builder, dec_params, sps, - inst->dpb); - v4l2_h264_build_p_ref_list(&reflist_builder, p0_reflist); - v4l2_h264_build_b_ref_lists(&reflist_builder, b0_reflist, b1_reflist); - /* Adapt the built lists to the firmware's expectations */ - fixup_ref_list(p0_reflist, reflist_builder.num_valid); - fixup_ref_list(b0_reflist, reflist_builder.num_valid); - fixup_ref_list(b1_reflist, reflist_builder.num_valid); - - memcpy(&inst->vsi_ctx.h264_slice_params, slice_param, - sizeof(inst->vsi_ctx.h264_slice_params)); -} - -static unsigned int get_mv_buf_size(unsigned int width, unsigned int height) -{ - int unit_size = (width / MB_UNIT_LEN) * (height / MB_UNIT_LEN) + 8; - - return HW_MB_STORE_SZ * unit_size; -} - -static int allocate_predication_buf(struct vdec_h264_slice_inst *inst) -{ - int err; - - inst->pred_buf.size = BUF_PREDICTION_SZ; - err = mtk_vcodec_mem_alloc(inst->ctx, &inst->pred_buf); - if (err) { - mtk_vcodec_err(inst, "failed to allocate ppl buf"); - return err; - } - - inst->vsi_ctx.pred_buf_dma = inst->pred_buf.dma_addr; - return 0; -} - -static void free_predication_buf(struct vdec_h264_slice_inst *inst) -{ - struct mtk_vcodec_mem *mem = &inst->pred_buf; - - mtk_vcodec_debug_enter(inst); - - inst->vsi_ctx.pred_buf_dma = 0; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); -} - -static int alloc_mv_buf(struct vdec_h264_slice_inst *inst, - struct vdec_pic_info *pic) -{ - int i; - int err; - struct mtk_vcodec_mem *mem = NULL; - unsigned int buf_sz = get_mv_buf_size(pic->buf_w, pic->buf_h); - - mtk_v4l2_debug(3, "size = 0x%x", buf_sz); - for (i = 0; i < H264_MAX_MV_NUM; i++) { - mem = &inst->mv_buf[i]; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - mem->size = buf_sz; - err = mtk_vcodec_mem_alloc(inst->ctx, mem); - if (err) { - mtk_vcodec_err(inst, "failed to allocate mv buf"); - return err; - } - inst->vsi_ctx.mv_buf_dma[i] = mem->dma_addr; - } - - return 0; -} - -static void free_mv_buf(struct vdec_h264_slice_inst *inst) -{ - int i; - struct mtk_vcodec_mem *mem; - - for (i = 0; i < H264_MAX_MV_NUM; i++) { - inst->vsi_ctx.mv_buf_dma[i] = 0; - mem = &inst->mv_buf[i]; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - } -} - -static void get_pic_info(struct vdec_h264_slice_inst *inst, - struct vdec_pic_info *pic) -{ - struct mtk_vcodec_ctx *ctx = inst->ctx; - - ctx->picinfo.buf_w = ALIGN(ctx->picinfo.pic_w, VCODEC_DEC_ALIGNED_64); - ctx->picinfo.buf_h = ALIGN(ctx->picinfo.pic_h, VCODEC_DEC_ALIGNED_64); - ctx->picinfo.fb_sz[0] = ctx->picinfo.buf_w * ctx->picinfo.buf_h; - ctx->picinfo.fb_sz[1] = ctx->picinfo.fb_sz[0] >> 1; - inst->vsi_ctx.dec.cap_num_planes = - ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes; - - *pic = ctx->picinfo; - mtk_vcodec_debug(inst, "pic(%d, %d), buf(%d, %d)", - ctx->picinfo.pic_w, ctx->picinfo.pic_h, - ctx->picinfo.buf_w, ctx->picinfo.buf_h); - mtk_vcodec_debug(inst, "Y/C(%d, %d)", ctx->picinfo.fb_sz[0], - ctx->picinfo.fb_sz[1]); - - if (ctx->last_decoded_picinfo.pic_w != ctx->picinfo.pic_w || - ctx->last_decoded_picinfo.pic_h != ctx->picinfo.pic_h) { - inst->vsi_ctx.dec.resolution_changed = true; - if (ctx->last_decoded_picinfo.buf_w != ctx->picinfo.buf_w || - ctx->last_decoded_picinfo.buf_h != ctx->picinfo.buf_h) - inst->vsi_ctx.dec.realloc_mv_buf = true; - - mtk_v4l2_debug(1, "ResChg: (%d %d) : old(%d, %d) -> new(%d, %d)", - inst->vsi_ctx.dec.resolution_changed, - inst->vsi_ctx.dec.realloc_mv_buf, - ctx->last_decoded_picinfo.pic_w, - ctx->last_decoded_picinfo.pic_h, - ctx->picinfo.pic_w, ctx->picinfo.pic_h); - } -} - -static void get_crop_info(struct vdec_h264_slice_inst *inst, struct v4l2_rect *cr) -{ - cr->left = inst->vsi_ctx.crop.left; - cr->top = inst->vsi_ctx.crop.top; - cr->width = inst->vsi_ctx.crop.width; - cr->height = inst->vsi_ctx.crop.height; - - mtk_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_h264_slice_inst *inst, unsigned int *dpb_sz) -{ - *dpb_sz = inst->vsi_ctx.dec.dpb_sz; - mtk_vcodec_debug(inst, "sz=%d", *dpb_sz); -} - -static int vdec_h264_slice_init(struct mtk_vcodec_ctx *ctx) -{ - struct vdec_h264_slice_inst *inst; - int err; - - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) - return -ENOMEM; - - inst->ctx = ctx; - - inst->vpu.id = SCP_IPI_VDEC_H264; - inst->vpu.ctx = ctx; - - err = vpu_dec_init(&inst->vpu); - if (err) { - mtk_vcodec_err(inst, "vdec_h264 init err=%d", err); - goto error_free_inst; - } - - memcpy(&inst->vsi_ctx, inst->vpu.vsi, sizeof(inst->vsi_ctx)); - inst->vsi_ctx.dec.resolution_changed = true; - inst->vsi_ctx.dec.realloc_mv_buf = true; - - err = allocate_predication_buf(inst); - if (err) - goto error_deinit; - - mtk_vcodec_debug(inst, "struct size = %zu,%zu,%zu,%zu\n", - sizeof(struct mtk_h264_sps_param), - sizeof(struct mtk_h264_pps_param), - sizeof(struct mtk_h264_dec_slice_param), - sizeof(struct mtk_h264_dpb_info)); - - mtk_vcodec_debug(inst, "H264 Instance >> %p", inst); - - ctx->drv_handle = inst; - return 0; - -error_deinit: - vpu_dec_deinit(&inst->vpu); - -error_free_inst: - kfree(inst); - return err; -} - -static void vdec_h264_slice_deinit(void *h_vdec) -{ - struct vdec_h264_slice_inst *inst = h_vdec; - - mtk_vcodec_debug_enter(inst); - - vpu_dec_deinit(&inst->vpu); - free_predication_buf(inst); - free_mv_buf(inst); - - kfree(inst); -} - -static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, - struct vdec_fb *fb, bool *res_chg) -{ - struct vdec_h264_slice_inst *inst = h_vdec; - const struct v4l2_ctrl_h264_decode_params *dec_params = - get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS); - struct vdec_vpu_inst *vpu = &inst->vpu; - u32 data[2]; - u64 y_fb_dma; - u64 c_fb_dma; - int err; - - /* bs NULL means flush decoder */ - if (!bs) - return vpu_dec_reset(vpu); - - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; - 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); - - inst->vsi_ctx.dec.bs_dma = (uint64_t)bs->dma_addr; - inst->vsi_ctx.dec.y_fb_dma = y_fb_dma; - inst->vsi_ctx.dec.c_fb_dma = c_fb_dma; - inst->vsi_ctx.dec.vdec_fb_va = (u64)(uintptr_t)fb; - - get_vdec_decode_parameters(inst); - data[0] = bs->size; - /* - * Reconstruct the first byte of the NAL unit, as the firmware requests - * that information to be passed even though it is present in the stream - * itself... - */ - data[1] = (dec_params->nal_ref_idc << 5) | - ((dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) - ? 0x5 : 0x1); - - *res_chg = inst->vsi_ctx.dec.resolution_changed; - if (*res_chg) { - mtk_vcodec_debug(inst, "- resolution changed -"); - if (inst->vsi_ctx.dec.realloc_mv_buf) { - err = alloc_mv_buf(inst, &inst->ctx->picinfo); - inst->vsi_ctx.dec.realloc_mv_buf = false; - if (err) - goto err_free_fb_out; - } - *res_chg = false; - } - - memcpy(inst->vpu.vsi, &inst->vsi_ctx, sizeof(inst->vsi_ctx)); - err = vpu_dec_start(vpu, data, 2); - if (err) - goto err_free_fb_out; - - /* wait decoder done interrupt */ - err = mtk_vcodec_wait_for_done_ctx(inst->ctx, - MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS, 0); - if (err) - goto err_free_fb_out; - vpu_dec_end(vpu); - - memcpy(&inst->vsi_ctx, inst->vpu.vsi, sizeof(inst->vsi_ctx)); - mtk_vcodec_debug(inst, "\n - NALU[%d]", inst->num_nalu); - return 0; - -err_free_fb_out: - mtk_vcodec_err(inst, "\n - NALU[%d] err=%d -\n", inst->num_nalu, err); - return err; -} - -static int vdec_h264_slice_get_param(void *h_vdec, enum vdec_get_param_type type, void *out) -{ - struct vdec_h264_slice_inst *inst = h_vdec; - - switch (type) { - 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: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); - return -EINVAL; - } - - return 0; -} - -const struct vdec_common_if vdec_h264_slice_if = { - .init = vdec_h264_slice_init, - .decode = vdec_h264_slice_decode, - .get_param = vdec_h264_slice_get_param, - .deinit = vdec_h264_slice_deinit, -}; diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c deleted file mode 100644 index 88c046731754..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c +++ /dev/null @@ -1,616 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Jungchang Tsao - * PC Chen - */ - -#include -#include "../vdec_drv_if.h" -#include "../mtk_vcodec_util.h" -#include "../mtk_vcodec_dec.h" -#include "../mtk_vcodec_intr.h" -#include "../vdec_vpu_if.h" -#include "../vdec_drv_base.h" - -/* Decoding picture buffer size (3 reference frames plus current frame) */ -#define VP8_DPB_SIZE 4 - -/* HW working buffer size (bytes) */ -#define VP8_WORKING_BUF_SZ (45 * 4096) - -/* HW control register address */ -#define VP8_SEGID_DRAM_ADDR 0x3c -#define VP8_HW_VLD_ADDR 0x93C -#define VP8_HW_VLD_VALUE 0x940 -#define VP8_BSASET 0x100 -#define VP8_BSDSET 0x104 -#define VP8_RW_CKEN_SET 0x0 -#define VP8_RW_DCM_CON 0x18 -#define VP8_WO_VLD_SRST 0x108 -#define VP8_RW_MISC_SYS_SEL 0x84 -#define VP8_RW_MISC_SPEC_CON 0xC8 -#define VP8_WO_VLD_SRST 0x108 -#define VP8_RW_VP8_CTRL 0xA4 -#define VP8_RW_MISC_DCM_CON 0xEC -#define VP8_RW_MISC_SRST 0xF4 -#define VP8_RW_MISC_FUNC_CON 0xCC - -#define VP8_MAX_FRM_BUF_NUM 5 -#define VP8_MAX_FRM_BUF_NODE_NUM (VP8_MAX_FRM_BUF_NUM * 2) - -/* required buffer size (bytes) to store decode information */ -#define VP8_HW_SEGMENT_DATA_SZ 272 -#define VP8_HW_SEGMENT_UINT 4 - -#define VP8_DEC_TABLE_PROC_LOOP 96 -#define VP8_DEC_TABLE_UNIT 3 -#define VP8_DEC_TABLE_SZ 300 -#define VP8_DEC_TABLE_OFFSET 2 -#define VP8_DEC_TABLE_RW_UNIT 4 - -/** - * struct vdec_vp8_dec_info - decode misc information - * @working_buf_dma : working buffer dma address - * @prev_y_dma : previous decoded frame buffer Y plane address - * @cur_y_fb_dma : current plane Y frame buffer dma address - * @cur_c_fb_dma : current plane C frame buffer dma address - * @bs_dma : bitstream dma address - * @bs_sz : bitstream size - * @resolution_changed: resolution change flag 1 - changed, 0 - not change - * @show_frame : display this frame or not - * @wait_key_frame : wait key frame coming - */ -struct vdec_vp8_dec_info { - uint64_t working_buf_dma; - uint64_t prev_y_dma; - uint64_t cur_y_fb_dma; - uint64_t cur_c_fb_dma; - uint64_t bs_dma; - uint32_t bs_sz; - uint32_t resolution_changed; - uint32_t show_frame; - uint32_t wait_key_frame; -}; - -/** - * struct vdec_vp8_vsi - VPU shared information - * @dec : decoding information - * @pic : picture information - * @dec_table : decoder coefficient table - * @segment_buf : segmentation buffer - * @load_data : flag to indicate reload decode data - */ -struct vdec_vp8_vsi { - struct vdec_vp8_dec_info dec; - struct vdec_pic_info pic; - uint32_t dec_table[VP8_DEC_TABLE_SZ]; - uint32_t segment_buf[VP8_HW_SEGMENT_DATA_SZ][VP8_HW_SEGMENT_UINT]; - uint32_t load_data; -}; - -/** - * struct vdec_vp8_hw_reg_base - HW register base - * @sys : base address for sys - * @misc : base address for misc - * @ld : base address for ld - * @top : base address for top - * @cm : base address for cm - * @hwd : base address for hwd - * @hwb : base address for hwb - */ -struct vdec_vp8_hw_reg_base { - void __iomem *sys; - void __iomem *misc; - void __iomem *ld; - void __iomem *top; - void __iomem *cm; - void __iomem *hwd; - void __iomem *hwb; -}; - -/** - * struct vdec_vp8_vpu_inst - VPU instance for VP8 decode - * @wq_hd : Wait queue to wait VPU message ack - * @signaled : 1 - Host has received ack message from VPU, 0 - not receive - * @failure : VPU execution result status 0 - success, others - fail - * @inst_addr : VPU decoder instance address - */ -struct vdec_vp8_vpu_inst { - wait_queue_head_t wq_hd; - int signaled; - int failure; - uint32_t inst_addr; -}; - -/* frame buffer (fb) list - * [available_fb_node_list] - decode fb are initialized to 0 and populated in - * [fb_use_list] - fb is set after decode and is moved to this list - * [fb_free_list] - fb is not needed for reference will be moved from - * [fb_use_list] to [fb_free_list] and - * once user remove fb from [fb_free_list], - * it is circulated back to [available_fb_node_list] - * [fb_disp_list] - fb is set after decode and is moved to this list - * once user remove fb from [fb_disp_list] it is - * circulated back to [available_fb_node_list] - */ - -/** - * struct vdec_vp8_inst - VP8 decoder instance - * @cur_fb : current frame buffer - * @dec_fb : decode frame buffer node - * @available_fb_node_list : list to store available frame buffer node - * @fb_use_list : list to store frame buffer in use - * @fb_free_list : list to store free frame buffer - * @fb_disp_list : list to store display ready frame buffer - * @working_buf : HW decoder working buffer - * @reg_base : HW register base address - * @frm_cnt : decode frame count - * @ctx : V4L2 context - * @vpu : VPU instance for decoder - * @vsi : VPU share information - */ -struct vdec_vp8_inst { - struct vdec_fb *cur_fb; - struct vdec_fb_node dec_fb[VP8_MAX_FRM_BUF_NODE_NUM]; - struct list_head available_fb_node_list; - struct list_head fb_use_list; - struct list_head fb_free_list; - struct list_head fb_disp_list; - struct mtk_vcodec_mem working_buf; - struct vdec_vp8_hw_reg_base reg_base; - unsigned int frm_cnt; - struct mtk_vcodec_ctx *ctx; - struct vdec_vpu_inst vpu; - struct vdec_vp8_vsi *vsi; -}; - -static void get_hw_reg_base(struct vdec_vp8_inst *inst) -{ - inst->reg_base.top = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_TOP); - inst->reg_base.cm = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_CM); - inst->reg_base.hwd = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_HWD); - inst->reg_base.sys = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_SYS); - inst->reg_base.misc = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_MISC); - inst->reg_base.ld = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_LD); - inst->reg_base.hwb = mtk_vcodec_get_reg_addr(inst->ctx, VDEC_HWB); -} - -static void write_hw_segmentation_data(struct vdec_vp8_inst *inst) -{ - int i, j; - u32 seg_id_addr; - u32 val; - void __iomem *cm = inst->reg_base.cm; - struct vdec_vp8_vsi *vsi = inst->vsi; - - seg_id_addr = readl(inst->reg_base.top + VP8_SEGID_DRAM_ADDR) >> 4; - - for (i = 0; i < ARRAY_SIZE(vsi->segment_buf); i++) { - for (j = ARRAY_SIZE(vsi->segment_buf[i]) - 1; j >= 0; j--) { - val = (1 << 16) + ((seg_id_addr + i) << 2) + j; - writel(val, cm + VP8_HW_VLD_ADDR); - - val = vsi->segment_buf[i][j]; - writel(val, cm + VP8_HW_VLD_VALUE); - } - } -} - -static void read_hw_segmentation_data(struct vdec_vp8_inst *inst) -{ - int i, j; - u32 seg_id_addr; - u32 val; - void __iomem *cm = inst->reg_base.cm; - struct vdec_vp8_vsi *vsi = inst->vsi; - - seg_id_addr = readl(inst->reg_base.top + VP8_SEGID_DRAM_ADDR) >> 4; - - for (i = 0; i < ARRAY_SIZE(vsi->segment_buf); i++) { - for (j = ARRAY_SIZE(vsi->segment_buf[i]) - 1; j >= 0; j--) { - val = ((seg_id_addr + i) << 2) + j; - writel(val, cm + VP8_HW_VLD_ADDR); - - val = readl(cm + VP8_HW_VLD_VALUE); - vsi->segment_buf[i][j] = val; - } - } -} - -/* reset HW and enable HW read/write data function */ -static void enable_hw_rw_function(struct vdec_vp8_inst *inst) -{ - u32 val = 0; - void __iomem *sys = inst->reg_base.sys; - void __iomem *misc = inst->reg_base.misc; - void __iomem *ld = inst->reg_base.ld; - void __iomem *hwb = inst->reg_base.hwb; - void __iomem *hwd = inst->reg_base.hwd; - - writel(0x1, sys + VP8_RW_CKEN_SET); - writel(0x101, ld + VP8_WO_VLD_SRST); - writel(0x101, hwb + VP8_WO_VLD_SRST); - - writel(1, sys); - val = readl(misc + VP8_RW_MISC_SRST); - writel((val & 0xFFFFFFFE), misc + VP8_RW_MISC_SRST); - - writel(0x1, misc + VP8_RW_MISC_SYS_SEL); - writel(0x17F, misc + VP8_RW_MISC_SPEC_CON); - writel(0x71201100, misc + VP8_RW_MISC_FUNC_CON); - writel(0x0, ld + VP8_WO_VLD_SRST); - writel(0x0, hwb + VP8_WO_VLD_SRST); - writel(0x1, sys + VP8_RW_DCM_CON); - writel(0x1, misc + VP8_RW_MISC_DCM_CON); - writel(0x1, hwd + VP8_RW_VP8_CTRL); -} - -static void store_dec_table(struct vdec_vp8_inst *inst) -{ - int i, j; - u32 addr = 0, val = 0; - void __iomem *hwd = inst->reg_base.hwd; - u32 *p = &inst->vsi->dec_table[VP8_DEC_TABLE_OFFSET]; - - for (i = 0; i < VP8_DEC_TABLE_PROC_LOOP; i++) { - writel(addr, hwd + VP8_BSASET); - for (j = 0; j < VP8_DEC_TABLE_UNIT ; j++) { - val = *p++; - writel(val, hwd + VP8_BSDSET); - } - addr += VP8_DEC_TABLE_RW_UNIT; - } -} - -static void load_dec_table(struct vdec_vp8_inst *inst) -{ - int i; - u32 addr = 0; - u32 *p = &inst->vsi->dec_table[VP8_DEC_TABLE_OFFSET]; - void __iomem *hwd = inst->reg_base.hwd; - - for (i = 0; i < VP8_DEC_TABLE_PROC_LOOP; i++) { - writel(addr, hwd + VP8_BSASET); - /* read total 11 bytes */ - *p++ = readl(hwd + VP8_BSDSET); - *p++ = readl(hwd + VP8_BSDSET); - *p++ = readl(hwd + VP8_BSDSET) & 0xFFFFFF; - addr += VP8_DEC_TABLE_RW_UNIT; - } -} - -static void get_pic_info(struct vdec_vp8_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, "fb size: Y(%d), C(%d)", - pic->fb_sz[0], pic->fb_sz[1]); -} - -static void vp8_dec_finish(struct vdec_vp8_inst *inst) -{ - struct vdec_fb_node *node; - uint64_t prev_y_dma = inst->vsi->dec.prev_y_dma; - - mtk_vcodec_debug(inst, "prev fb base dma=%llx", prev_y_dma); - - /* put last decode ok frame to fb_free_list */ - if (prev_y_dma != 0) { - list_for_each_entry(node, &inst->fb_use_list, list) { - struct vdec_fb *fb = (struct vdec_fb *)node->fb; - - if (prev_y_dma == (uint64_t)fb->base_y.dma_addr) { - list_move_tail(&node->list, - &inst->fb_free_list); - break; - } - } - } - - /* available_fb_node_list -> fb_use_list */ - node = list_first_entry(&inst->available_fb_node_list, - struct vdec_fb_node, list); - node->fb = inst->cur_fb; - list_move_tail(&node->list, &inst->fb_use_list); - - /* available_fb_node_list -> fb_disp_list */ - if (inst->vsi->dec.show_frame) { - node = list_first_entry(&inst->available_fb_node_list, - struct vdec_fb_node, list); - node->fb = inst->cur_fb; - list_move_tail(&node->list, &inst->fb_disp_list); - } -} - -static void move_fb_list_use_to_free(struct vdec_vp8_inst *inst) -{ - struct vdec_fb_node *node, *tmp; - - list_for_each_entry_safe(node, tmp, &inst->fb_use_list, list) - list_move_tail(&node->list, &inst->fb_free_list); -} - -static void init_list(struct vdec_vp8_inst *inst) -{ - int i; - - INIT_LIST_HEAD(&inst->available_fb_node_list); - INIT_LIST_HEAD(&inst->fb_use_list); - INIT_LIST_HEAD(&inst->fb_free_list); - INIT_LIST_HEAD(&inst->fb_disp_list); - - for (i = 0; i < ARRAY_SIZE(inst->dec_fb); i++) { - INIT_LIST_HEAD(&inst->dec_fb[i].list); - inst->dec_fb[i].fb = NULL; - list_add_tail(&inst->dec_fb[i].list, - &inst->available_fb_node_list); - } -} - -static void add_fb_to_free_list(struct vdec_vp8_inst *inst, void *fb) -{ - struct vdec_fb_node *node; - - if (fb) { - node = list_first_entry(&inst->available_fb_node_list, - struct vdec_fb_node, list); - node->fb = fb; - list_move_tail(&node->list, &inst->fb_free_list); - } -} - -static int alloc_working_buf(struct vdec_vp8_inst *inst) -{ - int err; - struct mtk_vcodec_mem *mem = &inst->working_buf; - - mem->size = VP8_WORKING_BUF_SZ; - err = mtk_vcodec_mem_alloc(inst->ctx, mem); - if (err) { - mtk_vcodec_err(inst, "Cannot allocate working buffer"); - return err; - } - - inst->vsi->dec.working_buf_dma = (uint64_t)mem->dma_addr; - return 0; -} - -static void free_working_buf(struct vdec_vp8_inst *inst) -{ - struct mtk_vcodec_mem *mem = &inst->working_buf; - - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - - inst->vsi->dec.working_buf_dma = 0; -} - -static int vdec_vp8_init(struct mtk_vcodec_ctx *ctx) -{ - struct vdec_vp8_inst *inst; - int err; - - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) - return -ENOMEM; - - inst->ctx = ctx; - - inst->vpu.id = IPI_VDEC_VP8; - inst->vpu.ctx = ctx; - - err = vpu_dec_init(&inst->vpu); - if (err) { - mtk_vcodec_err(inst, "vdec_vp8 init err=%d", err); - goto error_free_inst; - } - - inst->vsi = (struct vdec_vp8_vsi *)inst->vpu.vsi; - init_list(inst); - err = alloc_working_buf(inst); - if (err) - goto error_deinit; - - get_hw_reg_base(inst); - mtk_vcodec_debug(inst, "VP8 Instance >> %p", inst); - - ctx->drv_handle = inst; - return 0; - -error_deinit: - vpu_dec_deinit(&inst->vpu); -error_free_inst: - kfree(inst); - return err; -} - -static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs, - struct vdec_fb *fb, bool *res_chg) -{ - struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; - struct vdec_vp8_dec_info *dec = &inst->vsi->dec; - struct vdec_vpu_inst *vpu = &inst->vpu; - unsigned char *bs_va; - unsigned int data; - int err = 0; - uint64_t y_fb_dma; - uint64_t c_fb_dma; - - /* bs NULL means flush decoder */ - if (bs == NULL) { - move_fb_list_use_to_free(inst); - return vpu_dec_reset(vpu); - } - - y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0; - c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0; - - mtk_vcodec_debug(inst, "+ [%d] FB y_dma=%llx c_dma=%llx fb=%p", - inst->frm_cnt, y_fb_dma, c_fb_dma, fb); - - inst->cur_fb = fb; - dec->bs_dma = (unsigned long)bs->dma_addr; - dec->bs_sz = bs->size; - dec->cur_y_fb_dma = y_fb_dma; - dec->cur_c_fb_dma = c_fb_dma; - - mtk_vcodec_debug(inst, "\n + FRAME[%d] +\n", inst->frm_cnt); - - write_hw_segmentation_data(inst); - enable_hw_rw_function(inst); - store_dec_table(inst); - - bs_va = (unsigned char *)bs->va; - - /* retrieve width/hight and scale info from header */ - data = (*(bs_va + 9) << 24) | (*(bs_va + 8) << 16) | - (*(bs_va + 7) << 8) | *(bs_va + 6); - err = vpu_dec_start(vpu, &data, 1); - if (err) { - add_fb_to_free_list(inst, fb); - if (dec->wait_key_frame) { - mtk_vcodec_debug(inst, "wait key frame !"); - return 0; - } - - goto error; - } - - if (dec->resolution_changed) { - mtk_vcodec_debug(inst, "- resolution_changed -"); - *res_chg = true; - add_fb_to_free_list(inst, fb); - return 0; - } - - /* wait decoder done interrupt */ - mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS, 0); - - if (inst->vsi->load_data) - load_dec_table(inst); - - vp8_dec_finish(inst); - read_hw_segmentation_data(inst); - - err = vpu_dec_end(vpu); - if (err) - goto error; - - mtk_vcodec_debug(inst, "\n - FRAME[%d] - show=%d\n", inst->frm_cnt, - dec->show_frame); - inst->frm_cnt++; - *res_chg = false; - return 0; - -error: - mtk_vcodec_err(inst, "\n - FRAME[%d] - err=%d\n", inst->frm_cnt, err); - return err; -} - -static void get_disp_fb(struct vdec_vp8_inst *inst, struct vdec_fb **out_fb) -{ - struct vdec_fb_node *node; - struct vdec_fb *fb; - - node = list_first_entry_or_null(&inst->fb_disp_list, - struct vdec_fb_node, list); - if (node) { - list_move_tail(&node->list, &inst->available_fb_node_list); - fb = (struct vdec_fb *)node->fb; - fb->status |= FB_ST_DISPLAY; - mtk_vcodec_debug(inst, "[FB] get disp fb %p st=%d", - node->fb, fb->status); - } else { - fb = NULL; - mtk_vcodec_debug(inst, "[FB] there is no disp fb"); - } - - *out_fb = fb; -} - -static void get_free_fb(struct vdec_vp8_inst *inst, struct vdec_fb **out_fb) -{ - struct vdec_fb_node *node; - struct vdec_fb *fb; - - node = list_first_entry_or_null(&inst->fb_free_list, - struct vdec_fb_node, list); - if (node) { - list_move_tail(&node->list, &inst->available_fb_node_list); - fb = (struct vdec_fb *)node->fb; - fb->status |= FB_ST_FREE; - mtk_vcodec_debug(inst, "[FB] get free fb %p st=%d", - node->fb, fb->status); - } else { - fb = NULL; - mtk_vcodec_debug(inst, "[FB] there is no free fb"); - } - - *out_fb = fb; -} - -static void get_crop_info(struct vdec_vp8_inst *inst, struct v4l2_rect *cr) -{ - cr->left = 0; - cr->top = 0; - cr->width = inst->vsi->pic.pic_w; - cr->height = inst->vsi->pic.pic_h; - mtk_vcodec_debug(inst, "get crop info l=%d, t=%d, w=%d, h=%d", - cr->left, cr->top, cr->width, cr->height); -} - -static int vdec_vp8_get_param(void *h_vdec, enum vdec_get_param_type type, - void *out) -{ - struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; - - switch (type) { - case GET_PARAM_DISP_FRAME_BUFFER: - get_disp_fb(inst, out); - break; - - case GET_PARAM_FREE_FRAME_BUFFER: - get_free_fb(inst, out); - break; - - case GET_PARAM_PIC_INFO: - get_pic_info(inst, out); - break; - - case GET_PARAM_CROP_INFO: - get_crop_info(inst, out); - break; - - case GET_PARAM_DPB_SIZE: - *((unsigned int *)out) = VP8_DPB_SIZE; - break; - - default: - mtk_vcodec_err(inst, "invalid get parameter type=%d", type); - return -EINVAL; - } - - return 0; -} - -static void vdec_vp8_deinit(void *h_vdec) -{ - struct vdec_vp8_inst *inst = (struct vdec_vp8_inst *)h_vdec; - - mtk_vcodec_debug_enter(inst); - - vpu_dec_deinit(&inst->vpu); - free_working_buf(inst); - kfree(inst); -} - -const struct vdec_common_if vdec_vp8_if = { - .init = vdec_vp8_init, - .decode = vdec_vp8_decode, - .get_param = vdec_vp8_get_param, - .deinit = vdec_vp8_deinit, -}; diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c deleted file mode 100644 index 70b8383f7c8e..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c +++ /dev/null @@ -1,1028 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Daniel Hsiao - * Kai-Sean Yang - * Tiffany Lin - */ - -#include -#include -#include -#include -#include - -#include "../mtk_vcodec_intr.h" -#include "../vdec_drv_base.h" -#include "../vdec_vpu_if.h" - -#define VP9_SUPER_FRAME_BS_SZ 64 -#define MAX_VP9_DPB_SIZE 9 - -#define REFS_PER_FRAME 3 -#define MAX_NUM_REF_FRAMES 8 -#define VP9_MAX_FRM_BUF_NUM 9 -#define VP9_MAX_FRM_BUF_NODE_NUM (VP9_MAX_FRM_BUF_NUM * 2) -#define VP9_SEG_ID_SZ 0x12000 - -/** - * struct vp9_dram_buf - contains buffer info for vpu - * @va : cpu address - * @pa : iova address - * @sz : buffer size - * @padding : for 64 bytes alignment - */ -struct vp9_dram_buf { - unsigned long va; - unsigned long pa; - unsigned int sz; - unsigned int padding; -}; - -/** - * struct vp9_fb_info - contains frame buffer info - * @fb : frmae buffer - * @reserved : reserved field used by vpu - */ -struct vp9_fb_info { - struct vdec_fb *fb; - unsigned int reserved[32]; -}; - -/** - * struct vp9_ref_cnt_buf - contains reference buffer information - * @buf : referenced frame buffer - * @ref_cnt : referenced frame buffer's reference count. - * When reference count=0, remove it from reference list - */ -struct vp9_ref_cnt_buf { - struct vp9_fb_info buf; - unsigned int ref_cnt; -}; - -/** - * struct vp9_ref_buf - contains current frame's reference buffer information - * @buf : reference buffer - * @idx : reference buffer index to frm_bufs - * @reserved : reserved field used by vpu - */ -struct vp9_ref_buf { - struct vp9_fb_info *buf; - unsigned int idx; - unsigned int reserved[6]; -}; - -/** - * struct vp9_sf_ref_fb - contains frame buffer info - * @fb : super frame reference frame buffer - * @used : this reference frame info entry is used - * @padding : for 64 bytes size align - */ -struct vp9_sf_ref_fb { - struct vdec_fb fb; - int used; - int padding; -}; - -/* - * struct vdec_vp9_vsi - shared buffer between host and VPU firmware - * AP-W/R : AP is writer/reader on this item - * VPU-W/R: VPU is write/reader on this item - * @sf_bs_buf : super frame backup buffer (AP-W, VPU-R) - * @sf_ref_fb : record supoer frame reference buffer information - * (AP-R/W, VPU-R/W) - * @sf_next_ref_fb_idx : next available super frame (AP-W, VPU-R) - * @sf_frm_cnt : super frame count, filled by vpu (AP-R, VPU-W) - * @sf_frm_offset : super frame offset, filled by vpu (AP-R, VPU-W) - * @sf_frm_sz : super frame size, filled by vpu (AP-R, VPU-W) - * @sf_frm_idx : current super frame (AP-R, VPU-W) - * @sf_init : inform super frame info already parsed by vpu (AP-R, VPU-W) - * @fb : capture buffer (AP-W, VPU-R) - * @bs : bs buffer (AP-W, VPU-R) - * @cur_fb : current show capture buffer (AP-R/W, VPU-R/W) - * @pic_w : picture width (AP-R, VPU-W) - * @pic_h : picture height (AP-R, VPU-W) - * @buf_w : codec width (AP-R, VPU-W) - * @buf_h : coded height (AP-R, VPU-W) - * @buf_sz_y_bs : ufo compressed y plane size (AP-R, VPU-W) - * @buf_sz_c_bs : ufo compressed cbcr plane size (AP-R, VPU-W) - * @buf_len_sz_y : size used to store y plane ufo info (AP-R, VPU-W) - * @buf_len_sz_c : size used to store cbcr plane ufo info (AP-R, VPU-W) - - * @profile : profile sparsed from vpu (AP-R, VPU-W) - * @show_frame : [BIT(0)] display this frame or not (AP-R, VPU-W) - * [BIT(1)] reset segment data or not (AP-R, VPU-W) - * [BIT(2)] trig decoder hardware or not (AP-R, VPU-W) - * [BIT(3)] ask VPU to set bits(0~4) accordingly (AP-W, VPU-R) - * [BIT(4)] do not reset segment data before every frame (AP-R, VPU-W) - * @show_existing_frame : inform this frame is show existing frame - * (AP-R, VPU-W) - * @frm_to_show_idx : index to show frame (AP-R, VPU-W) - - * @refresh_frm_flags : indicate when frame need to refine reference count - * (AP-R, VPU-W) - * @resolution_changed : resolution change in this frame (AP-R, VPU-W) - - * @frm_bufs : maintain reference buffer info (AP-R/W, VPU-R/W) - * @ref_frm_map : maintain reference buffer map info (AP-R/W, VPU-R/W) - * @new_fb_idx : index to frm_bufs array (AP-R, VPU-W) - * @frm_num : decoded frame number, include sub-frame count (AP-R, VPU-W) - * @mv_buf : motion vector working buffer (AP-W, VPU-R) - * @frm_refs : maintain three reference buffer info (AP-R/W, VPU-R/W) - * @seg_id_buf : segmentation map working buffer (AP-W, VPU-R) - */ -struct vdec_vp9_vsi { - unsigned char sf_bs_buf[VP9_SUPER_FRAME_BS_SZ]; - struct vp9_sf_ref_fb sf_ref_fb[VP9_MAX_FRM_BUF_NUM-1]; - int sf_next_ref_fb_idx; - unsigned int sf_frm_cnt; - unsigned int sf_frm_offset[VP9_MAX_FRM_BUF_NUM-1]; - unsigned int sf_frm_sz[VP9_MAX_FRM_BUF_NUM-1]; - unsigned int sf_frm_idx; - unsigned int sf_init; - struct vdec_fb fb; - struct mtk_vcodec_mem bs; - struct vdec_fb cur_fb; - unsigned int pic_w; - unsigned int pic_h; - unsigned int buf_w; - unsigned int buf_h; - unsigned int buf_sz_y_bs; - unsigned int buf_sz_c_bs; - unsigned int buf_len_sz_y; - unsigned int buf_len_sz_c; - unsigned int profile; - unsigned int show_frame; - unsigned int show_existing_frame; - unsigned int frm_to_show_idx; - unsigned int refresh_frm_flags; - unsigned int resolution_changed; - - struct vp9_ref_cnt_buf frm_bufs[VP9_MAX_FRM_BUF_NUM]; - int ref_frm_map[MAX_NUM_REF_FRAMES]; - unsigned int new_fb_idx; - unsigned int frm_num; - struct vp9_dram_buf mv_buf; - - struct vp9_ref_buf frm_refs[REFS_PER_FRAME]; - struct vp9_dram_buf seg_id_buf; - -}; - -/* - * struct vdec_vp9_inst - vp9 decode instance - * @mv_buf : working buffer for mv - * @seg_id_buf : working buffer for segmentation map - * @dec_fb : vdec_fb node to link fb to different fb_xxx_list - * @available_fb_node_list : current available vdec_fb node - * @fb_use_list : current used or referenced vdec_fb - * @fb_free_list : current available to free vdec_fb - * @fb_disp_list : current available to display vdec_fb - * @cur_fb : current frame buffer - * @ctx : current decode context - * @vpu : vpu instance information - * @vsi : shared buffer between host and VPU firmware - * @total_frm_cnt : total frame count, it do not include sub-frames in super - * frame - * @mem : instance memory information - */ -struct vdec_vp9_inst { - struct mtk_vcodec_mem mv_buf; - struct mtk_vcodec_mem seg_id_buf; - - struct vdec_fb_node dec_fb[VP9_MAX_FRM_BUF_NODE_NUM]; - struct list_head available_fb_node_list; - struct list_head fb_use_list; - struct list_head fb_free_list; - struct list_head fb_disp_list; - struct vdec_fb *cur_fb; - struct mtk_vcodec_ctx *ctx; - struct vdec_vpu_inst vpu; - struct vdec_vp9_vsi *vsi; - unsigned int total_frm_cnt; - struct mtk_vcodec_mem mem; -}; - -static bool vp9_is_sf_ref_fb(struct vdec_vp9_inst *inst, struct vdec_fb *fb) -{ - int i; - struct vdec_vp9_vsi *vsi = inst->vsi; - - for (i = 0; i < ARRAY_SIZE(vsi->sf_ref_fb); i++) { - if (fb == &vsi->sf_ref_fb[i].fb) - return true; - } - return false; -} - -static struct vdec_fb *vp9_rm_from_fb_use_list(struct vdec_vp9_inst - *inst, void *addr) -{ - struct vdec_fb *fb = NULL; - struct vdec_fb_node *node; - - list_for_each_entry(node, &inst->fb_use_list, list) { - fb = (struct vdec_fb *)node->fb; - if (fb->base_y.va == addr) { - list_move_tail(&node->list, - &inst->available_fb_node_list); - break; - } - } - return fb; -} - -static void vp9_add_to_fb_free_list(struct vdec_vp9_inst *inst, - struct vdec_fb *fb) -{ - struct vdec_fb_node *node; - - if (fb) { - node = list_first_entry_or_null(&inst->available_fb_node_list, - struct vdec_fb_node, list); - - if (node) { - node->fb = fb; - list_move_tail(&node->list, &inst->fb_free_list); - } - } else { - mtk_vcodec_debug(inst, "No free fb node"); - } -} - -static void vp9_free_sf_ref_fb(struct vdec_fb *fb) -{ - struct vp9_sf_ref_fb *sf_ref_fb = - container_of(fb, struct vp9_sf_ref_fb, fb); - - sf_ref_fb->used = 0; -} - -static void vp9_ref_cnt_fb(struct vdec_vp9_inst *inst, int *idx, - int new_idx) -{ - struct vdec_vp9_vsi *vsi = inst->vsi; - int ref_idx = *idx; - - if (ref_idx >= 0 && vsi->frm_bufs[ref_idx].ref_cnt > 0) { - vsi->frm_bufs[ref_idx].ref_cnt--; - - if (vsi->frm_bufs[ref_idx].ref_cnt == 0) { - if (!vp9_is_sf_ref_fb(inst, - vsi->frm_bufs[ref_idx].buf.fb)) { - struct vdec_fb *fb; - - fb = vp9_rm_from_fb_use_list(inst, - vsi->frm_bufs[ref_idx].buf.fb->base_y.va); - vp9_add_to_fb_free_list(inst, fb); - } else - vp9_free_sf_ref_fb( - vsi->frm_bufs[ref_idx].buf.fb); - } - } - - *idx = new_idx; - vsi->frm_bufs[new_idx].ref_cnt++; -} - -static void vp9_free_all_sf_ref_fb(struct vdec_vp9_inst *inst) -{ - int i; - struct vdec_vp9_vsi *vsi = inst->vsi; - - for (i = 0; i < ARRAY_SIZE(vsi->sf_ref_fb); i++) { - if (vsi->sf_ref_fb[i].fb.base_y.va) { - mtk_vcodec_mem_free(inst->ctx, - &vsi->sf_ref_fb[i].fb.base_y); - mtk_vcodec_mem_free(inst->ctx, - &vsi->sf_ref_fb[i].fb.base_c); - vsi->sf_ref_fb[i].used = 0; - } - } -} - -/* For each sub-frame except the last one, the driver will dynamically - * allocate reference buffer by calling vp9_get_sf_ref_fb() - * The last sub-frame will use the original fb provided by the - * vp9_dec_decode() interface - */ -static int vp9_get_sf_ref_fb(struct vdec_vp9_inst *inst) -{ - int idx; - struct mtk_vcodec_mem *mem_basy_y; - struct mtk_vcodec_mem *mem_basy_c; - struct vdec_vp9_vsi *vsi = inst->vsi; - - for (idx = 0; - idx < ARRAY_SIZE(vsi->sf_ref_fb); - idx++) { - if (vsi->sf_ref_fb[idx].fb.base_y.va && - vsi->sf_ref_fb[idx].used == 0) { - return idx; - } - } - - for (idx = 0; - idx < ARRAY_SIZE(vsi->sf_ref_fb); - idx++) { - if (vsi->sf_ref_fb[idx].fb.base_y.va == NULL) - break; - } - - if (idx == ARRAY_SIZE(vsi->sf_ref_fb)) { - mtk_vcodec_err(inst, "List Full"); - return -1; - } - - mem_basy_y = &vsi->sf_ref_fb[idx].fb.base_y; - mem_basy_y->size = vsi->buf_sz_y_bs + - vsi->buf_len_sz_y; - - if (mtk_vcodec_mem_alloc(inst->ctx, mem_basy_y)) { - mtk_vcodec_err(inst, "Cannot allocate sf_ref_buf y_buf"); - return -1; - } - - mem_basy_c = &vsi->sf_ref_fb[idx].fb.base_c; - mem_basy_c->size = vsi->buf_sz_c_bs + - vsi->buf_len_sz_c; - - if (mtk_vcodec_mem_alloc(inst->ctx, mem_basy_c)) { - mtk_vcodec_err(inst, "Cannot allocate sf_ref_fb c_buf"); - return -1; - } - vsi->sf_ref_fb[idx].used = 0; - - return idx; -} - -static bool vp9_alloc_work_buf(struct vdec_vp9_inst *inst) -{ - struct vdec_vp9_vsi *vsi = inst->vsi; - int result; - struct mtk_vcodec_mem *mem; - - unsigned int max_pic_w; - unsigned int max_pic_h; - - - if (!(inst->ctx->dev->dec_capability & - VCODEC_CAPABILITY_4K_DISABLED)) { - max_pic_w = VCODEC_DEC_4K_CODED_WIDTH; - max_pic_h = VCODEC_DEC_4K_CODED_HEIGHT; - } else { - max_pic_w = MTK_VDEC_MAX_W; - max_pic_h = MTK_VDEC_MAX_H; - } - - if ((vsi->pic_w > max_pic_w) || - (vsi->pic_h > max_pic_h)) { - mtk_vcodec_err(inst, "Invalid w/h %d/%d", - vsi->pic_w, vsi->pic_h); - return false; - } - - mtk_vcodec_debug(inst, "BUF CHG(%d): w/h/sb_w/sb_h=%d/%d/%d/%d", - vsi->resolution_changed, - vsi->pic_w, - vsi->pic_h, - vsi->buf_w, - vsi->buf_h); - - mem = &inst->mv_buf; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - - mem->size = ((vsi->buf_w / 64) * - (vsi->buf_h / 64) + 2) * 36 * 16; - result = mtk_vcodec_mem_alloc(inst->ctx, mem); - if (result) { - mem->size = 0; - mtk_vcodec_err(inst, "Cannot allocate mv_buf"); - return false; - } - /* Set the va again */ - vsi->mv_buf.va = (unsigned long)mem->va; - vsi->mv_buf.pa = (unsigned long)mem->dma_addr; - vsi->mv_buf.sz = (unsigned int)mem->size; - - - mem = &inst->seg_id_buf; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - - mem->size = VP9_SEG_ID_SZ; - result = mtk_vcodec_mem_alloc(inst->ctx, mem); - if (result) { - mem->size = 0; - mtk_vcodec_err(inst, "Cannot allocate seg_id_buf"); - return false; - } - /* Set the va again */ - vsi->seg_id_buf.va = (unsigned long)mem->va; - vsi->seg_id_buf.pa = (unsigned long)mem->dma_addr; - vsi->seg_id_buf.sz = (unsigned int)mem->size; - - - vp9_free_all_sf_ref_fb(inst); - vsi->sf_next_ref_fb_idx = vp9_get_sf_ref_fb(inst); - - return true; -} - -static bool vp9_add_to_fb_disp_list(struct vdec_vp9_inst *inst, - struct vdec_fb *fb) -{ - struct vdec_fb_node *node; - - if (!fb) { - mtk_vcodec_err(inst, "fb == NULL"); - return false; - } - - node = list_first_entry_or_null(&inst->available_fb_node_list, - struct vdec_fb_node, list); - if (node) { - node->fb = fb; - list_move_tail(&node->list, &inst->fb_disp_list); - } else { - mtk_vcodec_err(inst, "No available fb node"); - return false; - } - - return true; -} - -/* If any buffer updating is signaled it should be done here. */ -static void vp9_swap_frm_bufs(struct vdec_vp9_inst *inst) -{ - struct vdec_vp9_vsi *vsi = inst->vsi; - struct vp9_fb_info *frm_to_show; - int ref_index = 0, mask; - - for (mask = vsi->refresh_frm_flags; mask; mask >>= 1) { - if (mask & 1) - vp9_ref_cnt_fb(inst, &vsi->ref_frm_map[ref_index], - vsi->new_fb_idx); - ++ref_index; - } - - frm_to_show = &vsi->frm_bufs[vsi->new_fb_idx].buf; - vsi->frm_bufs[vsi->new_fb_idx].ref_cnt--; - - if (frm_to_show->fb != inst->cur_fb) { - /* This frame is show exist frame and no decode output - * copy frame data from frm_to_show to current CAPTURE - * buffer - */ - if ((frm_to_show->fb != NULL) && - (inst->cur_fb->base_y.size >= - frm_to_show->fb->base_y.size) && - (inst->cur_fb->base_c.size >= - frm_to_show->fb->base_c.size)) { - memcpy((void *)inst->cur_fb->base_y.va, - (void *)frm_to_show->fb->base_y.va, - frm_to_show->fb->base_y.size); - memcpy((void *)inst->cur_fb->base_c.va, - (void *)frm_to_show->fb->base_c.va, - frm_to_show->fb->base_c.size); - } else { - /* After resolution change case, current CAPTURE buffer - * may have less buffer size than frm_to_show buffer - * size - */ - if (frm_to_show->fb != NULL) - mtk_vcodec_err(inst, - "inst->cur_fb->base_y.size=%zu, frm_to_show->fb.base_y.size=%zu", - inst->cur_fb->base_y.size, - frm_to_show->fb->base_y.size); - } - if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) { - if (vsi->show_frame & BIT(0)) - vp9_add_to_fb_disp_list(inst, inst->cur_fb); - } - } else { - if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) { - if (vsi->show_frame & BIT(0)) - vp9_add_to_fb_disp_list(inst, frm_to_show->fb); - } - } - - /* when ref_cnt ==0, move this fb to fb_free_list. v4l2 driver will - * clean fb_free_list - */ - if (vsi->frm_bufs[vsi->new_fb_idx].ref_cnt == 0) { - if (!vp9_is_sf_ref_fb( - inst, vsi->frm_bufs[vsi->new_fb_idx].buf.fb)) { - struct vdec_fb *fb; - - fb = vp9_rm_from_fb_use_list(inst, - vsi->frm_bufs[vsi->new_fb_idx].buf.fb->base_y.va); - - vp9_add_to_fb_free_list(inst, fb); - } else { - vp9_free_sf_ref_fb( - vsi->frm_bufs[vsi->new_fb_idx].buf.fb); - } - } - - /* if this super frame and it is not last sub-frame, get next fb for - * sub-frame decode - */ - if (vsi->sf_frm_cnt > 0 && vsi->sf_frm_idx != vsi->sf_frm_cnt - 1) - vsi->sf_next_ref_fb_idx = vp9_get_sf_ref_fb(inst); -} - -static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst) -{ - struct mtk_vcodec_ctx *ctx = inst->ctx; - - mtk_vcodec_wait_for_done_ctx(inst->ctx, - MTK_INST_IRQ_RECEIVED, - WAIT_INTR_TIMEOUT_MS, 0); - - if (ctx->irq_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) - return true; - else - return false; -} - -static struct vdec_vp9_inst *vp9_alloc_inst(struct mtk_vcodec_ctx *ctx) -{ - int result; - struct mtk_vcodec_mem mem; - struct vdec_vp9_inst *inst; - - memset(&mem, 0, sizeof(mem)); - mem.size = sizeof(struct vdec_vp9_inst); - result = mtk_vcodec_mem_alloc(ctx, &mem); - if (result) - return NULL; - - inst = mem.va; - inst->mem = mem; - - return inst; -} - -static void vp9_free_inst(struct vdec_vp9_inst *inst) -{ - struct mtk_vcodec_mem mem; - - mem = inst->mem; - if (mem.va) - mtk_vcodec_mem_free(inst->ctx, &mem); -} - -static bool vp9_decode_end_proc(struct vdec_vp9_inst *inst) -{ - struct vdec_vp9_vsi *vsi = inst->vsi; - bool ret = false; - - if (!vsi->show_existing_frame) { - ret = vp9_wait_dec_end(inst); - if (!ret) { - mtk_vcodec_err(inst, "Decode failed, Decode Timeout @[%d]", - vsi->frm_num); - return false; - } - - if (vpu_dec_end(&inst->vpu)) { - mtk_vcodec_err(inst, "vp9_dec_vpu_end failed"); - return false; - } - mtk_vcodec_debug(inst, "Decode Ok @%d (%d/%d)", vsi->frm_num, - vsi->pic_w, vsi->pic_h); - } else { - mtk_vcodec_debug(inst, "Decode Ok @%d (show_existing_frame)", - vsi->frm_num); - } - - vp9_swap_frm_bufs(inst); - vsi->frm_num++; - return true; -} - -static bool vp9_is_last_sub_frm(struct vdec_vp9_inst *inst) -{ - struct vdec_vp9_vsi *vsi = inst->vsi; - - if (vsi->sf_frm_cnt <= 0 || vsi->sf_frm_idx == vsi->sf_frm_cnt) - return true; - - return false; -} - -static struct vdec_fb *vp9_rm_from_fb_disp_list(struct vdec_vp9_inst *inst) -{ - struct vdec_fb_node *node; - struct vdec_fb *fb = NULL; - - node = list_first_entry_or_null(&inst->fb_disp_list, - struct vdec_fb_node, list); - if (node) { - fb = (struct vdec_fb *)node->fb; - fb->status |= FB_ST_DISPLAY; - list_move_tail(&node->list, &inst->available_fb_node_list); - mtk_vcodec_debug(inst, "[FB] get disp fb %p st=%d", - node->fb, fb->status); - } else - mtk_vcodec_debug(inst, "[FB] there is no disp fb"); - - return fb; -} - -static bool vp9_add_to_fb_use_list(struct vdec_vp9_inst *inst, - struct vdec_fb *fb) -{ - struct vdec_fb_node *node; - - if (!fb) { - mtk_vcodec_debug(inst, "fb == NULL"); - return false; - } - - node = list_first_entry_or_null(&inst->available_fb_node_list, - struct vdec_fb_node, list); - if (node) { - node->fb = fb; - list_move_tail(&node->list, &inst->fb_use_list); - } else { - mtk_vcodec_err(inst, "No free fb node"); - return false; - } - return true; -} - -static void vp9_reset(struct vdec_vp9_inst *inst) -{ - struct vdec_fb_node *node, *tmp; - - list_for_each_entry_safe(node, tmp, &inst->fb_use_list, list) - list_move_tail(&node->list, &inst->fb_free_list); - - vp9_free_all_sf_ref_fb(inst); - inst->vsi->sf_next_ref_fb_idx = vp9_get_sf_ref_fb(inst); - - if (vpu_dec_reset(&inst->vpu)) - mtk_vcodec_err(inst, "vp9_dec_vpu_reset failed"); - - /* Set the va again, since vpu_dec_reset will clear mv_buf in vpu */ - inst->vsi->mv_buf.va = (unsigned long)inst->mv_buf.va; - inst->vsi->mv_buf.pa = (unsigned long)inst->mv_buf.dma_addr; - inst->vsi->mv_buf.sz = (unsigned long)inst->mv_buf.size; - - /* Set the va again, since vpu_dec_reset will clear seg_id_buf in vpu */ - inst->vsi->seg_id_buf.va = (unsigned long)inst->seg_id_buf.va; - inst->vsi->seg_id_buf.pa = (unsigned long)inst->seg_id_buf.dma_addr; - inst->vsi->seg_id_buf.sz = (unsigned long)inst->seg_id_buf.size; - -} - -static void init_all_fb_lists(struct vdec_vp9_inst *inst) -{ - int i; - - INIT_LIST_HEAD(&inst->available_fb_node_list); - INIT_LIST_HEAD(&inst->fb_use_list); - INIT_LIST_HEAD(&inst->fb_free_list); - INIT_LIST_HEAD(&inst->fb_disp_list); - - for (i = 0; i < ARRAY_SIZE(inst->dec_fb); i++) { - INIT_LIST_HEAD(&inst->dec_fb[i].list); - inst->dec_fb[i].fb = NULL; - list_add_tail(&inst->dec_fb[i].list, - &inst->available_fb_node_list); - } -} - -static void get_pic_info(struct vdec_vp9_inst *inst, struct vdec_pic_info *pic) -{ - pic->fb_sz[0] = inst->vsi->buf_sz_y_bs + inst->vsi->buf_len_sz_y; - pic->fb_sz[1] = inst->vsi->buf_sz_c_bs + inst->vsi->buf_len_sz_c; - - pic->pic_w = inst->vsi->pic_w; - pic->pic_h = inst->vsi->pic_h; - pic->buf_w = inst->vsi->buf_w; - pic->buf_h = inst->vsi->buf_h; - - 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, "fb size: Y(%d), C(%d)", - pic->fb_sz[0], - pic->fb_sz[1]); -} - -static void get_disp_fb(struct vdec_vp9_inst *inst, struct vdec_fb **out_fb) -{ - - *out_fb = vp9_rm_from_fb_disp_list(inst); - if (*out_fb) - (*out_fb)->status |= FB_ST_DISPLAY; -} - -static void get_free_fb(struct vdec_vp9_inst *inst, struct vdec_fb **out_fb) -{ - struct vdec_fb_node *node; - struct vdec_fb *fb = NULL; - - node = list_first_entry_or_null(&inst->fb_free_list, - struct vdec_fb_node, list); - if (node) { - list_move_tail(&node->list, &inst->available_fb_node_list); - fb = (struct vdec_fb *)node->fb; - fb->status |= FB_ST_FREE; - mtk_vcodec_debug(inst, "[FB] get free fb %p st=%d", - node->fb, fb->status); - } else { - mtk_vcodec_debug(inst, "[FB] there is no free fb"); - } - - *out_fb = fb; -} - -static int validate_vsi_array_indexes(struct vdec_vp9_inst *inst, - struct vdec_vp9_vsi *vsi) { - if (vsi->sf_frm_idx >= VP9_MAX_FRM_BUF_NUM - 1) { - mtk_vcodec_err(inst, "Invalid vsi->sf_frm_idx=%u.", - vsi->sf_frm_idx); - return -EIO; - } - if (vsi->frm_to_show_idx >= VP9_MAX_FRM_BUF_NUM) { - mtk_vcodec_err(inst, "Invalid vsi->frm_to_show_idx=%u.", - vsi->frm_to_show_idx); - return -EIO; - } - if (vsi->new_fb_idx >= VP9_MAX_FRM_BUF_NUM) { - mtk_vcodec_err(inst, "Invalid vsi->new_fb_idx=%u.", - vsi->new_fb_idx); - return -EIO; - } - return 0; -} - -static void vdec_vp9_deinit(void *h_vdec) -{ - struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; - struct mtk_vcodec_mem *mem; - int ret = 0; - - ret = vpu_dec_deinit(&inst->vpu); - if (ret) - mtk_vcodec_err(inst, "vpu_dec_deinit failed"); - - mem = &inst->mv_buf; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - - mem = &inst->seg_id_buf; - if (mem->va) - mtk_vcodec_mem_free(inst->ctx, mem); - - vp9_free_all_sf_ref_fb(inst); - vp9_free_inst(inst); -} - -static int vdec_vp9_init(struct mtk_vcodec_ctx *ctx) -{ - struct vdec_vp9_inst *inst; - - inst = vp9_alloc_inst(ctx); - if (!inst) - return -ENOMEM; - - inst->total_frm_cnt = 0; - inst->ctx = ctx; - - inst->vpu.id = IPI_VDEC_VP9; - inst->vpu.ctx = ctx; - - if (vpu_dec_init(&inst->vpu)) { - mtk_vcodec_err(inst, "vp9_dec_vpu_init failed"); - goto err_deinit_inst; - } - - inst->vsi = (struct vdec_vp9_vsi *)inst->vpu.vsi; - - inst->vsi->show_frame |= BIT(3); - - init_all_fb_lists(inst); - - ctx->drv_handle = inst; - return 0; - -err_deinit_inst: - vp9_free_inst(inst); - - return -EINVAL; -} - -static int vdec_vp9_decode(void *h_vdec, struct mtk_vcodec_mem *bs, - struct vdec_fb *fb, bool *res_chg) -{ - int ret = 0; - struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; - struct vdec_vp9_vsi *vsi = inst->vsi; - u32 data[3]; - int i; - - *res_chg = false; - - if ((bs == NULL) && (fb == NULL)) { - mtk_vcodec_debug(inst, "[EOS]"); - vp9_reset(inst); - return ret; - } - - if (bs == NULL) { - mtk_vcodec_err(inst, "bs == NULL"); - return -EINVAL; - } - - mtk_vcodec_debug(inst, "Input BS Size = %zu", bs->size); - - while (1) { - struct vdec_fb *cur_fb = NULL; - - data[0] = *((unsigned int *)bs->va); - data[1] = *((unsigned int *)(bs->va + 4)); - data[2] = *((unsigned int *)(bs->va + 8)); - - vsi->bs = *bs; - - if (fb) - vsi->fb = *fb; - - if (!vsi->sf_init) { - unsigned int sf_bs_sz; - unsigned int sf_bs_off; - unsigned char *sf_bs_src; - unsigned char *sf_bs_dst; - - sf_bs_sz = bs->size > VP9_SUPER_FRAME_BS_SZ ? - VP9_SUPER_FRAME_BS_SZ : bs->size; - sf_bs_off = VP9_SUPER_FRAME_BS_SZ - sf_bs_sz; - sf_bs_src = bs->va + bs->size - sf_bs_sz; - sf_bs_dst = vsi->sf_bs_buf + sf_bs_off; - memcpy(sf_bs_dst, sf_bs_src, sf_bs_sz); - } else { - if ((vsi->sf_frm_cnt > 0) && - (vsi->sf_frm_idx < vsi->sf_frm_cnt)) { - unsigned int idx = vsi->sf_frm_idx; - - memcpy((void *)bs->va, - (void *)(bs->va + - vsi->sf_frm_offset[idx]), - vsi->sf_frm_sz[idx]); - } - } - - if (!(vsi->show_frame & BIT(4))) - memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); - - ret = vpu_dec_start(&inst->vpu, data, 3); - if (ret) { - mtk_vcodec_err(inst, "vpu_dec_start failed"); - goto DECODE_ERROR; - } - - if (vsi->show_frame & BIT(1)) { - memset(inst->seg_id_buf.va, 0, inst->seg_id_buf.size); - - if (vsi->show_frame & BIT(2)) { - ret = vpu_dec_start(&inst->vpu, NULL, 0); - if (ret) { - mtk_vcodec_err(inst, "vpu trig decoder failed"); - goto DECODE_ERROR; - } - } - } - - ret = validate_vsi_array_indexes(inst, vsi); - if (ret) { - mtk_vcodec_err(inst, "Invalid values from VPU."); - goto DECODE_ERROR; - } - - if (vsi->resolution_changed) { - if (!vp9_alloc_work_buf(inst)) { - ret = -EIO; - goto DECODE_ERROR; - } - } - - if (vsi->sf_frm_cnt > 0) { - cur_fb = &vsi->sf_ref_fb[vsi->sf_next_ref_fb_idx].fb; - - if (vsi->sf_frm_idx < vsi->sf_frm_cnt) - inst->cur_fb = cur_fb; - else - inst->cur_fb = fb; - } else { - inst->cur_fb = fb; - } - - vsi->frm_bufs[vsi->new_fb_idx].buf.fb = inst->cur_fb; - if (!vp9_is_sf_ref_fb(inst, inst->cur_fb)) - vp9_add_to_fb_use_list(inst, inst->cur_fb); - - mtk_vcodec_debug(inst, "[#pic %d]", vsi->frm_num); - - if (vsi->show_existing_frame) - mtk_vcodec_debug(inst, - "drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", - vsi->new_fb_idx, vsi->frm_to_show_idx); - - if (vsi->show_existing_frame && (vsi->frm_to_show_idx < - VP9_MAX_FRM_BUF_NUM)) { - mtk_vcodec_debug(inst, - "Skip Decode drv->new_fb_idx=%d, drv->frm_to_show_idx=%d", - vsi->new_fb_idx, vsi->frm_to_show_idx); - - vp9_ref_cnt_fb(inst, &vsi->new_fb_idx, - vsi->frm_to_show_idx); - } - - /* VPU assign the buffer pointer in its address space, - * reassign here - */ - for (i = 0; i < ARRAY_SIZE(vsi->frm_refs); i++) { - unsigned int idx = vsi->frm_refs[i].idx; - - vsi->frm_refs[i].buf = &vsi->frm_bufs[idx].buf; - } - - if (vsi->resolution_changed) { - *res_chg = true; - mtk_vcodec_debug(inst, "VDEC_ST_RESOLUTION_CHANGED"); - - ret = 0; - goto DECODE_ERROR; - } - - if (!vp9_decode_end_proc(inst)) { - mtk_vcodec_err(inst, "vp9_decode_end_proc"); - ret = -EINVAL; - goto DECODE_ERROR; - } - - if (vp9_is_last_sub_frm(inst)) - break; - - } - inst->total_frm_cnt++; - -DECODE_ERROR: - if (ret < 0) - vp9_add_to_fb_free_list(inst, fb); - - return ret; -} - -static void get_crop_info(struct vdec_vp9_inst *inst, struct v4l2_rect *cr) -{ - cr->left = 0; - cr->top = 0; - cr->width = inst->vsi->pic_w; - cr->height = inst->vsi->pic_h; - mtk_vcodec_debug(inst, "get crop info l=%d, t=%d, w=%d, h=%d\n", - cr->left, cr->top, cr->width, cr->height); -} - -static int vdec_vp9_get_param(void *h_vdec, enum vdec_get_param_type type, - void *out) -{ - struct vdec_vp9_inst *inst = (struct vdec_vp9_inst *)h_vdec; - int ret = 0; - - switch (type) { - case GET_PARAM_DISP_FRAME_BUFFER: - get_disp_fb(inst, out); - break; - case GET_PARAM_FREE_FRAME_BUFFER: - get_free_fb(inst, out); - break; - case GET_PARAM_PIC_INFO: - get_pic_info(inst, out); - break; - case GET_PARAM_DPB_SIZE: - *((unsigned int *)out) = MAX_VP9_DPB_SIZE; - break; - case GET_PARAM_CROP_INFO: - get_crop_info(inst, out); - break; - default: - mtk_vcodec_err(inst, "not supported param type %d", type); - ret = -EINVAL; - break; - } - - return ret; -} - -const struct vdec_common_if vdec_vp9_if = { - .init = vdec_vp9_init, - .decode = vdec_vp9_decode, - .get_param = vdec_vp9_get_param, - .deinit = vdec_vp9_deinit, -}; diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mtk-vcodec/vdec_drv_base.h deleted file mode 100644 index e913f963b7db..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h +++ /dev/null @@ -1,46 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - */ - -#ifndef _VDEC_DRV_BASE_ -#define _VDEC_DRV_BASE_ - -#include "vdec_drv_if.h" - -struct vdec_common_if { - /** - * (*init)() - initialize decode driver - * @ctx : [in] mtk v4l2 context - * @h_vdec : [out] driver handle - */ - int (*init)(struct mtk_vcodec_ctx *ctx); - - /** - * (*decode)() - trigger decode - * @h_vdec : [in] driver handle - * @bs : [in] input bitstream - * @fb : [in] frame buffer to store decoded frame - * @res_chg : [out] resolution change happen - */ - int (*decode)(void *h_vdec, struct mtk_vcodec_mem *bs, - struct vdec_fb *fb, bool *res_chg); - - /** - * (*get_param)() - get driver's parameter - * @h_vdec : [in] driver handle - * @type : [in] input parameter type - * @out : [out] buffer to store query result - */ - int (*get_param)(void *h_vdec, enum vdec_get_param_type type, - void *out); - - /** - * (*deinit)() - deinitialize driver. - * @h_vdec : [in] driver handle to be deinit - */ - void (*deinit)(void *h_vdec); -}; - -#endif diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mtk-vcodec/vdec_drv_if.c deleted file mode 100644 index 05a5b240e906..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - * Tiffany Lin - */ - -#include -#include -#include - -#include "vdec_drv_if.h" -#include "mtk_vcodec_dec.h" -#include "vdec_drv_base.h" -#include "mtk_vcodec_dec_pm.h" - -int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) -{ - int ret = 0; - - switch (fourcc) { - case V4L2_PIX_FMT_H264_SLICE: - ctx->dec_if = &vdec_h264_slice_if; - break; - case V4L2_PIX_FMT_H264: - ctx->dec_if = &vdec_h264_if; - ctx->hw_id = MTK_VDEC_CORE; - break; - case V4L2_PIX_FMT_VP8: - ctx->dec_if = &vdec_vp8_if; - ctx->hw_id = MTK_VDEC_CORE; - break; - case V4L2_PIX_FMT_VP9: - ctx->dec_if = &vdec_vp9_if; - ctx->hw_id = MTK_VDEC_CORE; - break; - default: - return -EINVAL; - } - - mtk_vdec_lock(ctx); - mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); - ret = ctx->dec_if->init(ctx); - mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); - mtk_vdec_unlock(ctx); - - return ret; -} - -int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, - struct vdec_fb *fb, bool *res_chg) -{ - int ret = 0; - - if (bs) { - if ((bs->dma_addr & 63) != 0) { - mtk_v4l2_err("bs dma_addr should 64 byte align"); - return -EINVAL; - } - } - - if (fb) { - if (((fb->base_y.dma_addr & 511) != 0) || - ((fb->base_c.dma_addr & 511) != 0)) { - mtk_v4l2_err("frame buffer dma_addr should 512 byte align"); - return -EINVAL; - } - } - - if (!ctx->drv_handle) - return -EIO; - - mtk_vdec_lock(ctx); - - mtk_vcodec_set_curr_ctx(ctx->dev, ctx, ctx->hw_id); - mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); - ret = ctx->dec_if->decode(ctx->drv_handle, bs, fb, res_chg); - mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); - mtk_vcodec_set_curr_ctx(ctx->dev, NULL, ctx->hw_id); - - mtk_vdec_unlock(ctx); - - return ret; -} - -int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, - void *out) -{ - int ret = 0; - - if (!ctx->drv_handle) - return -EIO; - - mtk_vdec_lock(ctx); - ret = ctx->dec_if->get_param(ctx->drv_handle, type, out); - mtk_vdec_unlock(ctx); - - return ret; -} - -void vdec_if_deinit(struct mtk_vcodec_ctx *ctx) -{ - if (!ctx->drv_handle) - return; - - mtk_vdec_lock(ctx); - mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id); - ctx->dec_if->deinit(ctx->drv_handle); - mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id); - mtk_vdec_unlock(ctx); - - ctx->drv_handle = NULL; -} diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mtk-vcodec/vdec_drv_if.h deleted file mode 100644 index d467e8af4a84..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h +++ /dev/null @@ -1,100 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - * Tiffany Lin - */ - -#ifndef _VDEC_DRV_IF_H_ -#define _VDEC_DRV_IF_H_ - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_dec.h" -#include "mtk_vcodec_util.h" - - -/** - * enum vdec_fb_status - decoder frame buffer status - * @FB_ST_NORMAL: initial state - * @FB_ST_DISPLAY: frame buffer is ready to be displayed - * @FB_ST_FREE: frame buffer is not used by decoder any more - */ -enum vdec_fb_status { - FB_ST_NORMAL = 0, - FB_ST_DISPLAY = (1 << 0), - FB_ST_FREE = (1 << 1) -}; - -/* For GET_PARAM_DISP_FRAME_BUFFER and GET_PARAM_FREE_FRAME_BUFFER, - * the caller does not own the returned buffer. The buffer will not be - * released before vdec_if_deinit. - * GET_PARAM_DISP_FRAME_BUFFER : get next displayable frame buffer, - * struct vdec_fb** - * GET_PARAM_FREE_FRAME_BUFFER : get non-referenced framebuffer, vdec_fb** - * GET_PARAM_PIC_INFO : get picture info, struct vdec_pic_info* - * GET_PARAM_CROP_INFO : get crop info, struct v4l2_crop* - * GET_PARAM_DPB_SIZE : get dpb size, unsigned int* - */ -enum vdec_get_param_type { - GET_PARAM_DISP_FRAME_BUFFER, - GET_PARAM_FREE_FRAME_BUFFER, - GET_PARAM_PIC_INFO, - GET_PARAM_CROP_INFO, - GET_PARAM_DPB_SIZE -}; - -/** - * struct vdec_fb_node - decoder frame buffer node - * @list : list to hold this node - * @fb : point to frame buffer (vdec_fb), fb could point to frame buffer and - * working buffer this is for maintain buffers in different state - */ -struct vdec_fb_node { - struct list_head list; - struct vdec_fb *fb; -}; - -extern const struct vdec_common_if vdec_h264_if; -extern const struct vdec_common_if vdec_h264_slice_if; -extern const struct vdec_common_if vdec_vp8_if; -extern const struct vdec_common_if vdec_vp9_if; - -/** - * vdec_if_init() - initialize decode driver - * @ctx : [in] v4l2 context - * @fourcc : [in] video format fourcc, V4L2_PIX_FMT_H264/VP8/VP9.. - */ -int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); - -/** - * vdec_if_deinit() - deinitialize decode driver - * @ctx : [in] v4l2 context - * - */ -void vdec_if_deinit(struct mtk_vcodec_ctx *ctx); - -/** - * vdec_if_decode() - trigger decode - * @ctx : [in] v4l2 context - * @bs : [in] input bitstream - * @fb : [in] frame buffer to store decoded frame, when null means parse - * header only - * @res_chg : [out] resolution change happens if current bs have different - * picture width/height - * Note: To flush the decoder when reaching EOF, set input bitstream as NULL. - * - * Return: 0 on success. -EIO on unrecoverable error. - */ -int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs, - struct vdec_fb *fb, bool *res_chg); - -/** - * vdec_if_get_param() - get driver's parameter - * @ctx : [in] v4l2 context - * @type : [in] input parameter type - * @out : [out] buffer to store query result - */ -int vdec_if_get_param(struct mtk_vcodec_ctx *ctx, enum vdec_get_param_type type, - void *out); - -#endif diff --git a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h b/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h deleted file mode 100644 index bf54d6d9a857..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h +++ /dev/null @@ -1,117 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - */ - -#ifndef _VDEC_IPI_MSG_H_ -#define _VDEC_IPI_MSG_H_ - -/* - * enum vdec_ipi_msgid - message id between AP and VPU - * @AP_IPIMSG_XXX : AP to VPU cmd message id - * @VPU_IPIMSG_XXX_ACK : VPU ack AP cmd message id - */ -enum vdec_ipi_msgid { - AP_IPIMSG_DEC_INIT = 0xA000, - AP_IPIMSG_DEC_START = 0xA001, - AP_IPIMSG_DEC_END = 0xA002, - AP_IPIMSG_DEC_DEINIT = 0xA003, - AP_IPIMSG_DEC_RESET = 0xA004, - AP_IPIMSG_DEC_CORE = 0xA005, - AP_IPIMSG_DEC_CORE_END = 0xA006, - - VPU_IPIMSG_DEC_INIT_ACK = 0xB000, - VPU_IPIMSG_DEC_START_ACK = 0xB001, - VPU_IPIMSG_DEC_END_ACK = 0xB002, - VPU_IPIMSG_DEC_DEINIT_ACK = 0xB003, - VPU_IPIMSG_DEC_RESET_ACK = 0xB004, - VPU_IPIMSG_DEC_CORE_ACK = 0xB005, - VPU_IPIMSG_DEC_CORE_END_ACK = 0xB006, -}; - -/** - * struct vdec_ap_ipi_cmd - generic AP to VPU ipi command format - * @msg_id : vdec_ipi_msgid - * @vpu_inst_addr : VPU decoder instance address. Used if ABI version < 2. - * @inst_id : instance ID. Used if the ABI version >= 2. - * @codec_type : codec fourcc - * @reserved : reserved param - */ -struct vdec_ap_ipi_cmd { - uint32_t msg_id; - union { - uint32_t vpu_inst_addr; - uint32_t inst_id; - }; - u32 codec_type; - u32 reserved; -}; - -/** - * struct vdec_vpu_ipi_ack - generic VPU to AP ipi command format - * @msg_id : vdec_ipi_msgid - * @status : VPU exeuction result - * @ap_inst_addr : AP video decoder instance address - */ -struct vdec_vpu_ipi_ack { - uint32_t msg_id; - int32_t status; - uint64_t ap_inst_addr; -}; - -/** - * struct vdec_ap_ipi_init - for AP_IPIMSG_DEC_INIT - * @msg_id : AP_IPIMSG_DEC_INIT - * @codec_type : codec fourcc - * @ap_inst_addr : AP video decoder instance address - */ -struct vdec_ap_ipi_init { - uint32_t msg_id; - u32 codec_type; - uint64_t ap_inst_addr; -}; - -/** - * struct vdec_ap_ipi_dec_start - for AP_IPIMSG_DEC_START - * @msg_id : AP_IPIMSG_DEC_START - * @vpu_inst_addr : VPU decoder instance address. Used if ABI version < 2. - * @inst_id : instance ID. Used if the ABI version >= 2. - * @data : Header info - * H264 decoder [0]:buf_sz [1]:nal_start - * VP8 decoder [0]:width/height - * VP9 decoder [0]:profile, [1][2] width/height - * @codec_type : codec fourcc - */ -struct vdec_ap_ipi_dec_start { - uint32_t msg_id; - union { - uint32_t vpu_inst_addr; - uint32_t inst_id; - }; - uint32_t data[3]; - u32 codec_type; -}; - -/** - * struct vdec_vpu_ipi_init_ack - for VPU_IPIMSG_DEC_INIT_ACK - * @msg_id : VPU_IPIMSG_DEC_INIT_ACK - * @status : VPU exeuction result - * @ap_inst_addr : AP vcodec_vpu_inst instance address - * @vpu_inst_addr : VPU decoder instance address - * @vdec_abi_version: ABI version of the firmware. Kernel can use it to - * ensure that it is compatible with the firmware. - * This field is not valid for MT8173 and must not be - * accessed for this chip. - * @inst_id : instance ID. Valid only if the ABI version >= 2. - */ -struct vdec_vpu_ipi_init_ack { - uint32_t msg_id; - int32_t status; - uint64_t ap_inst_addr; - uint32_t vpu_inst_addr; - uint32_t vdec_abi_version; - uint32_t inst_id; -}; - -#endif diff --git a/drivers/media/platform/mtk-vcodec/vdec_msg_queue.c b/drivers/media/platform/mtk-vcodec/vdec_msg_queue.c deleted file mode 100644 index 4b062a8128b4..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_msg_queue.c +++ /dev/null @@ -1,290 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2021 MediaTek Inc. - * Author: Yunfei Dong - */ - -#include -#include -#include - -#include "mtk_vcodec_dec_pm.h" -#include "mtk_vcodec_drv.h" -#include "vdec_msg_queue.h" - -#define VDEC_MSG_QUEUE_TIMEOUT_MS 1500 - -/* the size used to store lat slice header information */ -#define VDEC_LAT_SLICE_HEADER_SZ (640 * SZ_1K) - -/* the size used to store avc error information */ -#define VDEC_ERR_MAP_SZ_AVC (17 * SZ_1K) - -/* core will read the trans buffer which decoded by lat to decode again. - * The trans buffer size of FHD and 4K bitstreams are different. - */ -static int vde_msg_queue_get_trans_size(int width, int height) -{ - if (width > 1920 || height > 1088) - return 30 * SZ_1M; - else - return 6 * SZ_1M; -} - -void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index) -{ - init_waitqueue_head(&ctx->ready_to_use); - INIT_LIST_HEAD(&ctx->ready_queue); - spin_lock_init(&ctx->ready_lock); - ctx->ready_num = 0; - ctx->hardware_index = hardware_index; -} - -static struct list_head *vdec_get_buf_list(int hardware_index, struct vdec_lat_buf *buf) -{ - switch (hardware_index) { - case MTK_VDEC_CORE: - return &buf->core_list; - case MTK_VDEC_LAT0: - return &buf->lat_list; - default: - return NULL; - } -} - -int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf *buf) -{ - struct list_head *head; - - head = vdec_get_buf_list(msg_ctx->hardware_index, buf); - if (!head) { - mtk_v4l2_err("fail to qbuf: %d", msg_ctx->hardware_index); - return -EINVAL; - } - - spin_lock(&msg_ctx->ready_lock); - list_add_tail(head, &msg_ctx->ready_queue); - msg_ctx->ready_num++; - - if (msg_ctx->hardware_index != MTK_VDEC_CORE) - wake_up_all(&msg_ctx->ready_to_use); - else - queue_work(buf->ctx->dev->core_workqueue, - &buf->ctx->msg_queue.core_work); - - mtk_v4l2_debug(3, "enqueue buf type: %d addr: 0x%p num: %d", - msg_ctx->hardware_index, buf, msg_ctx->ready_num); - spin_unlock(&msg_ctx->ready_lock); - - return 0; -} - -static bool vdec_msg_queue_wait_event(struct vdec_msg_queue_ctx *msg_ctx) -{ - int ret; - - ret = wait_event_timeout(msg_ctx->ready_to_use, - !list_empty(&msg_ctx->ready_queue), - msecs_to_jiffies(VDEC_MSG_QUEUE_TIMEOUT_MS)); - if (!ret) - return false; - - return true; -} - -struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *msg_ctx) -{ - struct vdec_lat_buf *buf; - struct list_head *head; - int ret; - - spin_lock(&msg_ctx->ready_lock); - if (list_empty(&msg_ctx->ready_queue)) { - mtk_v4l2_debug(3, "queue is NULL, type:%d num: %d", - msg_ctx->hardware_index, msg_ctx->ready_num); - spin_unlock(&msg_ctx->ready_lock); - - if (msg_ctx->hardware_index == MTK_VDEC_CORE) - return NULL; - - ret = vdec_msg_queue_wait_event(msg_ctx); - if (!ret) - return NULL; - spin_lock(&msg_ctx->ready_lock); - } - - if (msg_ctx->hardware_index == MTK_VDEC_CORE) - buf = list_first_entry(&msg_ctx->ready_queue, - struct vdec_lat_buf, core_list); - else - buf = list_first_entry(&msg_ctx->ready_queue, - struct vdec_lat_buf, lat_list); - - head = vdec_get_buf_list(msg_ctx->hardware_index, buf); - if (!head) { - spin_unlock(&msg_ctx->ready_lock); - mtk_v4l2_err("fail to dqbuf: %d", msg_ctx->hardware_index); - return NULL; - } - list_del(head); - - msg_ctx->ready_num--; - mtk_v4l2_debug(3, "dqueue buf type:%d addr: 0x%p num: %d", - msg_ctx->hardware_index, buf, msg_ctx->ready_num); - spin_unlock(&msg_ctx->ready_lock); - - return buf; -} - -void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr) -{ - spin_lock(&msg_queue->lat_ctx.ready_lock); - msg_queue->wdma_rptr_addr = ube_rptr; - mtk_v4l2_debug(3, "update ube rprt (0x%llx)", ube_rptr); - spin_unlock(&msg_queue->lat_ctx.ready_lock); -} - -void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr) -{ - spin_lock(&msg_queue->lat_ctx.ready_lock); - msg_queue->wdma_wptr_addr = ube_wptr; - mtk_v4l2_debug(3, "update ube wprt: (0x%llx 0x%llx) offset: 0x%llx", - msg_queue->wdma_rptr_addr, msg_queue->wdma_wptr_addr, - ube_wptr); - spin_unlock(&msg_queue->lat_ctx.ready_lock); -} - -bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) -{ - long timeout_jiff; - int ret; - - timeout_jiff = msecs_to_jiffies(1000 * (NUM_BUFFER_COUNT + 2)); - ret = wait_event_timeout(msg_queue->lat_ctx.ready_to_use, - msg_queue->lat_ctx.ready_num == NUM_BUFFER_COUNT, - timeout_jiff); - if (ret) { - mtk_v4l2_debug(3, "success to get lat buf: %d", - msg_queue->lat_ctx.ready_num); - return true; - } - mtk_v4l2_err("failed with lat buf isn't full: %d", - msg_queue->lat_ctx.ready_num); - return false; -} - -void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx) -{ - struct vdec_lat_buf *lat_buf; - struct mtk_vcodec_mem *mem; - int i; - - mem = &msg_queue->wdma_addr; - if (mem->va) - mtk_vcodec_mem_free(ctx, mem); - for (i = 0; i < NUM_BUFFER_COUNT; i++) { - lat_buf = &msg_queue->lat_buf[i]; - - mem = &lat_buf->wdma_err_addr; - if (mem->va) - mtk_vcodec_mem_free(ctx, mem); - - mem = &lat_buf->slice_bc_addr; - if (mem->va) - mtk_vcodec_mem_free(ctx, mem); - - kfree(lat_buf->private_data); - } -} - -static void vdec_msg_queue_core_work(struct work_struct *work) -{ - struct vdec_msg_queue *msg_queue = - container_of(work, struct vdec_msg_queue, core_work); - struct mtk_vcodec_ctx *ctx = - container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue); - struct mtk_vcodec_dev *dev = ctx->dev; - struct vdec_lat_buf *lat_buf; - - lat_buf = vdec_msg_queue_dqbuf(&dev->msg_queue_core_ctx); - if (!lat_buf) - return; - - ctx = lat_buf->ctx; - mtk_vcodec_set_curr_ctx(dev, ctx, MTK_VDEC_CORE); - - lat_buf->core_decode(lat_buf); - - mtk_vcodec_set_curr_ctx(dev, NULL, MTK_VDEC_CORE); - vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); - - if (!list_empty(&ctx->msg_queue.lat_ctx.ready_queue)) { - mtk_v4l2_debug(3, "re-schedule to decode for core: %d", - dev->msg_queue_core_ctx.ready_num); - queue_work(dev->core_workqueue, &msg_queue->core_work); - } -} - -int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, - int private_size) -{ - struct vdec_lat_buf *lat_buf; - int i, err; - - /* already init msg queue */ - if (msg_queue->wdma_addr.size) - return 0; - - vdec_msg_queue_init_ctx(&msg_queue->lat_ctx, MTK_VDEC_LAT0); - INIT_WORK(&msg_queue->core_work, vdec_msg_queue_core_work); - msg_queue->wdma_addr.size = - vde_msg_queue_get_trans_size(ctx->picinfo.buf_w, - ctx->picinfo.buf_h); - - err = mtk_vcodec_mem_alloc(ctx, &msg_queue->wdma_addr); - if (err) { - mtk_v4l2_err("failed to allocate wdma_addr buf"); - return -ENOMEM; - } - msg_queue->wdma_rptr_addr = msg_queue->wdma_addr.dma_addr; - msg_queue->wdma_wptr_addr = msg_queue->wdma_addr.dma_addr; - - for (i = 0; i < NUM_BUFFER_COUNT; i++) { - lat_buf = &msg_queue->lat_buf[i]; - - lat_buf->wdma_err_addr.size = VDEC_ERR_MAP_SZ_AVC; - err = mtk_vcodec_mem_alloc(ctx, &lat_buf->wdma_err_addr); - if (err) { - mtk_v4l2_err("failed to allocate wdma_err_addr buf[%d]", i); - goto mem_alloc_err; - } - - lat_buf->slice_bc_addr.size = VDEC_LAT_SLICE_HEADER_SZ; - err = mtk_vcodec_mem_alloc(ctx, &lat_buf->slice_bc_addr); - if (err) { - mtk_v4l2_err("failed to allocate wdma_addr buf[%d]", i); - goto mem_alloc_err; - } - - lat_buf->private_data = kzalloc(private_size, GFP_KERNEL); - if (!lat_buf->private_data) { - err = -ENOMEM; - goto mem_alloc_err; - } - - lat_buf->ctx = ctx; - lat_buf->core_decode = core_decode; - err = vdec_msg_queue_qbuf(&msg_queue->lat_ctx, lat_buf); - if (err) { - mtk_v4l2_err("failed to qbuf buf[%d]", i); - goto mem_alloc_err; - } - } - return 0; - -mem_alloc_err: - vdec_msg_queue_deinit(msg_queue, ctx); - return err; -} diff --git a/drivers/media/platform/mtk-vcodec/vdec_msg_queue.h b/drivers/media/platform/mtk-vcodec/vdec_msg_queue.h deleted file mode 100644 index b6ba66d3e026..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_msg_queue.h +++ /dev/null @@ -1,153 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2021 MediaTek Inc. - * Author: Yunfei Dong - */ - -#ifndef _VDEC_MSG_QUEUE_H_ -#define _VDEC_MSG_QUEUE_H_ - -#include -#include -#include -#include - -#include "mtk_vcodec_util.h" - -#define NUM_BUFFER_COUNT 3 - -struct vdec_lat_buf; -struct mtk_vcodec_ctx; -struct mtk_vcodec_dev; -typedef int (*core_decode_cb_t)(struct vdec_lat_buf *lat_buf); - -/** - * struct vdec_msg_queue_ctx - represents a queue for buffers ready to be processed - * @ready_to_use: ready used queue used to signalize when get a job queue - * @ready_queue: list of ready lat buffer queues - * @ready_lock: spin lock to protect the lat buffer usage - * @ready_num: number of buffers ready to be processed - * @hardware_index: hardware id that this queue is used for - */ -struct vdec_msg_queue_ctx { - wait_queue_head_t ready_to_use; - struct list_head ready_queue; - /* protect lat buffer */ - spinlock_t ready_lock; - int ready_num; - int hardware_index; -}; - -/** - * struct vdec_lat_buf - lat buffer message used to store lat info for core decode - * @wdma_err_addr: wdma error address used for lat hardware - * @slice_bc_addr: slice bc address used for lat hardware - * @ts_info: need to set timestamp from output to capture - * - * @private_data: shared information used to lat and core hardware - * @ctx: mtk vcodec context information - * @core_decode: different codec use different decode callback function - * @lat_list: add lat buffer to lat head list - * @core_list: add lat buffer to core head list - */ -struct vdec_lat_buf { - struct mtk_vcodec_mem wdma_err_addr; - struct mtk_vcodec_mem slice_bc_addr; - struct vb2_v4l2_buffer ts_info; - - void *private_data; - struct mtk_vcodec_ctx *ctx; - core_decode_cb_t core_decode; - struct list_head lat_list; - struct list_head core_list; -}; - -/** - * struct vdec_msg_queue - used to store lat buffer message - * @lat_buf: lat buffer used to store lat buffer information - * @wdma_addr: wdma address used for ube - * @wdma_rptr_addr: ube read point - * @wdma_wptr_addr: ube write point - * @core_work: core hardware work - * @lat_ctx: used to store lat buffer list - */ -struct vdec_msg_queue { - struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT]; - - struct mtk_vcodec_mem wdma_addr; - u64 wdma_rptr_addr; - u64 wdma_wptr_addr; - - struct work_struct core_work; - struct vdec_msg_queue_ctx lat_ctx; -}; - -/** - * vdec_msg_queue_init - init lat buffer information. - * @msg_queue: used to store the lat buffer information - * @ctx: v4l2 ctx - * @core_decode: core decode callback for each codec - * @private_size: the private data size used to share with core - * - * Return: returns 0 if init successfully, or fail. - */ -int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode, - int private_size); - -/** - * vdec_msg_queue_init_ctx - used to init msg queue context information. - * @ctx: message queue context - * @hardware_index: hardware index - */ -void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index); - -/** - * vdec_msg_queue_qbuf - enqueue lat buffer to queue list. - * @ctx: message queue context - * @buf: current lat buffer - * - * Return: returns 0 if qbuf successfully, or fail. - */ -int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *ctx, struct vdec_lat_buf *buf); - -/** - * vdec_msg_queue_dqbuf - dequeue lat buffer from queue list. - * @ctx: message queue context - * - * Return: returns not null if dq successfully, or fail. - */ -struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *ctx); - -/** - * vdec_msg_queue_update_ube_rptr - used to updata the ube read point. - * @msg_queue: used to store the lat buffer information - * @ube_rptr: current ube read point - */ -void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr); - -/** - * vdec_msg_queue_update_ube_wptr - used to updata the ube write point. - * @msg_queue: used to store the lat buffer information - * @ube_wptr: current ube write point - */ -void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr); - -/** - * vdec_msg_queue_wait_lat_buf_full - used to check whether all lat buffer - * in lat list. - * @msg_queue: used to store the lat buffer information - * - * Return: returns true if successfully, or fail. - */ -bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue); - -/** - * vdec_msg_queue_deinit - deinit lat buffer information. - * @msg_queue: used to store the lat buffer information - * @ctx: v4l2 ctx - */ -void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, - struct mtk_vcodec_ctx *ctx); - -#endif diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c deleted file mode 100644 index dd35d2c5f920..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c +++ /dev/null @@ -1,243 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - */ - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_util.h" -#include "vdec_ipi_msg.h" -#include "vdec_vpu_if.h" -#include "mtk_vcodec_fw.h" - -static void handle_init_ack_msg(const struct vdec_vpu_ipi_init_ack *msg) -{ - struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) - (unsigned long)msg->ap_inst_addr; - - mtk_vcodec_debug(vpu, "+ ap_inst_addr = 0x%llx", msg->ap_inst_addr); - - /* mapping VPU address to kernel virtual address */ - /* the content in vsi is initialized to 0 in VPU */ - vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler, - msg->vpu_inst_addr); - vpu->inst_addr = msg->vpu_inst_addr; - - mtk_vcodec_debug(vpu, "- vpu_inst_addr = 0x%x", vpu->inst_addr); - - /* Set default ABI version if dealing with unversioned firmware. */ - vpu->fw_abi_version = 0; - /* - * Instance ID is only used if ABI version >= 2. Initialize it with - * garbage by default. - */ - vpu->inst_id = 0xdeadbeef; - - /* Firmware version field does not exist on MT8173. */ - if (vpu->ctx->dev->vdec_pdata->chip == MTK_MT8173) - return; - - /* Check firmware version. */ - vpu->fw_abi_version = msg->vdec_abi_version; - mtk_vcodec_debug(vpu, "firmware version 0x%x\n", vpu->fw_abi_version); - switch (vpu->fw_abi_version) { - case 1: - break; - case 2: - vpu->inst_id = msg->inst_id; - break; - default: - mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n", - vpu->fw_abi_version); - vpu->failure = 1; - break; - } -} - -/* - * vpu_dec_ipi_handler - Handler for VPU ipi message. - * - * @data: ipi message - * @len : length of ipi message - * @priv: callback private data which is passed by decoder when register. - * - * This function runs in interrupt context and it means there's an IPI MSG - * from VPU. - */ -static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv) -{ - const struct vdec_vpu_ipi_ack *msg = data; - struct vdec_vpu_inst *vpu = (struct vdec_vpu_inst *) - (unsigned long)msg->ap_inst_addr; - - mtk_vcodec_debug(vpu, "+ id=%X", msg->msg_id); - - vpu->failure = msg->status; - vpu->signaled = 1; - - if (msg->status == 0) { - switch (msg->msg_id) { - case VPU_IPIMSG_DEC_INIT_ACK: - handle_init_ack_msg(data); - break; - - case VPU_IPIMSG_DEC_START_ACK: - case VPU_IPIMSG_DEC_END_ACK: - case VPU_IPIMSG_DEC_DEINIT_ACK: - case VPU_IPIMSG_DEC_RESET_ACK: - case VPU_IPIMSG_DEC_CORE_ACK: - case VPU_IPIMSG_DEC_CORE_END_ACK: - break; - - default: - mtk_vcodec_err(vpu, "invalid msg=%X", msg->msg_id); - break; - } - } - - mtk_vcodec_debug(vpu, "- id=%X", msg->msg_id); -} - -static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len) -{ - int err, id, msgid; - - msgid = *(uint32_t *)msg; - mtk_vcodec_debug(vpu, "id=%X", msgid); - - vpu->failure = 0; - vpu->signaled = 0; - - if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) { - if (msgid == AP_IPIMSG_DEC_CORE || - msgid == AP_IPIMSG_DEC_CORE_END) - id = vpu->core_id; - else - id = vpu->id; - } else { - id = vpu->id; - } - - err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, id, msg, - len, 2000); - if (err) { - mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d", - id, msgid, err); - return err; - } - - return vpu->failure; -} - -static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id) -{ - struct vdec_ap_ipi_cmd msg; - int err = 0; - - mtk_vcodec_debug(vpu, "+ id=%X", msg_id); - - memset(&msg, 0, sizeof(msg)); - msg.msg_id = msg_id; - if (vpu->fw_abi_version < 2) - msg.vpu_inst_addr = vpu->inst_addr; - else - msg.inst_id = vpu->inst_id; - msg.codec_type = vpu->codec_type; - - err = vcodec_vpu_send_msg(vpu, &msg, sizeof(msg)); - mtk_vcodec_debug(vpu, "- id=%X ret=%d", msg_id, err); - return err; -} - -int vpu_dec_init(struct vdec_vpu_inst *vpu) -{ - struct vdec_ap_ipi_init msg; - int err; - - mtk_vcodec_debug_enter(vpu); - - init_waitqueue_head(&vpu->wq); - vpu->handler = vpu_dec_ipi_handler; - - err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, - vpu->handler, "vdec", NULL); - if (err) { - mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err); - return err; - } - - if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) { - err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, - vpu->core_id, vpu->handler, - "vdec", NULL); - if (err) { - mtk_vcodec_err(vpu, "vpu_ipi_register core fail status=%d", err); - return err; - } - } - - memset(&msg, 0, sizeof(msg)); - msg.msg_id = AP_IPIMSG_DEC_INIT; - msg.ap_inst_addr = (unsigned long)vpu; - msg.codec_type = vpu->codec_type; - - mtk_vcodec_debug(vpu, "vdec_inst=%p", vpu); - - err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); - mtk_vcodec_debug(vpu, "- ret=%d", err); - return err; -} - -int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len) -{ - struct vdec_ap_ipi_dec_start msg; - int i; - int err = 0; - - mtk_vcodec_debug_enter(vpu); - - if (len > ARRAY_SIZE(msg.data)) { - mtk_vcodec_err(vpu, "invalid len = %d\n", len); - return -EINVAL; - } - - memset(&msg, 0, sizeof(msg)); - msg.msg_id = AP_IPIMSG_DEC_START; - if (vpu->fw_abi_version < 2) - msg.vpu_inst_addr = vpu->inst_addr; - else - msg.inst_id = vpu->inst_id; - - for (i = 0; i < len; i++) - msg.data[i] = data[i]; - msg.codec_type = vpu->codec_type; - - err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg)); - mtk_vcodec_debug(vpu, "- ret=%d", err); - return err; -} - -int vpu_dec_core(struct vdec_vpu_inst *vpu) -{ - return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE); -} - -int vpu_dec_end(struct vdec_vpu_inst *vpu) -{ - return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_END); -} - -int vpu_dec_core_end(struct vdec_vpu_inst *vpu) -{ - return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE_END); -} - -int vpu_dec_deinit(struct vdec_vpu_inst *vpu) -{ - return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_DEINIT); -} - -int vpu_dec_reset(struct vdec_vpu_inst *vpu) -{ - return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_RESET); -} diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h deleted file mode 100644 index 4cb3c7f5a3ad..000000000000 --- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h +++ /dev/null @@ -1,107 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PC Chen - */ - -#ifndef _VDEC_VPU_IF_H_ -#define _VDEC_VPU_IF_H_ - -#include "mtk_vcodec_fw.h" - -struct mtk_vcodec_ctx; - -/** - * struct vdec_vpu_inst - VPU instance for video codec - * @id : ipi msg id for each decoder - * @core_id : core id used to separate different hardware - * @vsi : driver structure allocated by VPU side and shared to AP side - * for control and info share - * @failure : VPU execution result status, 0: success, others: fail - * @inst_addr : VPU decoder instance address - * @fw_abi_version : ABI version of the firmware. - * @inst_id : if fw_abi_version >= 2, contains the instance ID to be given - * in place of inst_addr in messages. - * @signaled : 1 - Host has received ack message from VPU, 0 - not received - * @ctx : context for v4l2 layer integration - * @dev : platform device of VPU - * @wq : wait queue to wait VPU message ack - * @handler : ipi handler for each decoder - * @codec_type : use codec type to separate different codecs - */ -struct vdec_vpu_inst { - int id; - int core_id; - void *vsi; - int32_t failure; - uint32_t inst_addr; - uint32_t fw_abi_version; - uint32_t inst_id; - unsigned int signaled; - struct mtk_vcodec_ctx *ctx; - wait_queue_head_t wq; - mtk_vcodec_ipi_handler handler; - unsigned int codec_type; -}; - -/** - * vpu_dec_init - init decoder instance and allocate required resource in VPU. - * - * @vpu: instance for vdec_vpu_inst - */ -int vpu_dec_init(struct vdec_vpu_inst *vpu); - -/** - * vpu_dec_start - start decoding, basically the function will be invoked once - * every frame. - * - * @vpu : instance for vdec_vpu_inst - * @data: meta data to pass bitstream info to VPU decoder - * @len : meta data length - */ -int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len); - -/** - * vpu_dec_end - end decoding, basically the function will be invoked once - * when HW decoding done interrupt received successfully. The - * decoder in VPU will continue to do reference frame management - * and check if there is a new decoded frame available to display. - * - * @vpu : instance for vdec_vpu_inst - */ -int vpu_dec_end(struct vdec_vpu_inst *vpu); - -/** - * vpu_dec_deinit - deinit decoder instance and resource freed in VPU. - * - * @vpu: instance for vdec_vpu_inst - */ -int vpu_dec_deinit(struct vdec_vpu_inst *vpu); - -/** - * vpu_dec_reset - reset decoder, use for flush decoder when end of stream or - * seek. Remainig non displayed frame will be pushed to display. - * - * @vpu: instance for vdec_vpu_inst - */ -int vpu_dec_reset(struct vdec_vpu_inst *vpu); - -/** - * vpu_dec_core - core start decoding, basically the function will be invoked once - * every frame. - * - * @vpu : instance for vdec_vpu_inst - */ -int vpu_dec_core(struct vdec_vpu_inst *vpu); - -/** - * vpu_dec_core_end - core end decoding, basically the function will be invoked once - * when core HW decoding done and receive interrupt successfully. The - * decoder in VPU will updata hardware information and deinit hardware - * and check if there is a new decoded frame available to display. - * - * @vpu : instance for vdec_vpu_inst - */ -int vpu_dec_core_end(struct vdec_vpu_inst *vpu); - -#endif diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c deleted file mode 100644 index 4d9b8798dffe..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c +++ /dev/null @@ -1,708 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Jungchang Tsao - * Daniel Hsiao - * PoChun Lin - */ - -#include -#include -#include - -#include "../mtk_vcodec_drv.h" -#include "../mtk_vcodec_util.h" -#include "../mtk_vcodec_intr.h" -#include "../mtk_vcodec_enc.h" -#include "../mtk_vcodec_enc_pm.h" -#include "../venc_drv_base.h" -#include "../venc_ipi_msg.h" -#include "../venc_vpu_if.h" - -static const char h264_filler_marker[] = {0x0, 0x0, 0x0, 0x1, 0xc}; - -#define H264_FILLER_MARKER_SIZE ARRAY_SIZE(h264_filler_marker) -#define VENC_PIC_BITSTREAM_BYTE_CNT 0x0098 - -/* - * enum venc_h264_frame_type - h264 encoder output bitstream frame type - */ -enum venc_h264_frame_type { - VENC_H264_IDR_FRM, - VENC_H264_I_FRM, - VENC_H264_P_FRM, - VENC_H264_B_FRM, -}; - -/* - * enum venc_h264_vpu_work_buf - h264 encoder buffer index - */ -enum venc_h264_vpu_work_buf { - VENC_H264_VPU_WORK_BUF_RC_INFO, - VENC_H264_VPU_WORK_BUF_RC_CODE, - VENC_H264_VPU_WORK_BUF_REC_LUMA, - VENC_H264_VPU_WORK_BUF_REC_CHROMA, - VENC_H264_VPU_WORK_BUF_REF_LUMA, - VENC_H264_VPU_WORK_BUF_REF_CHROMA, - VENC_H264_VPU_WORK_BUF_MV_INFO_1, - VENC_H264_VPU_WORK_BUF_MV_INFO_2, - VENC_H264_VPU_WORK_BUF_SKIP_FRAME, - VENC_H264_VPU_WORK_BUF_MAX, -}; - -/* - * enum venc_h264_bs_mode - for bs_mode argument in h264_enc_vpu_encode - */ -enum venc_h264_bs_mode { - H264_BS_MODE_SPS, - H264_BS_MODE_PPS, - H264_BS_MODE_FRAME, -}; - -/* - * struct venc_h264_vpu_config - Structure for h264 encoder configuration - * AP-W/R : AP is writer/reader on this item - * VPU-W/R: VPU is write/reader on this item - * @input_fourcc: input fourcc - * @bitrate: target bitrate (in bps) - * @pic_w: picture width. Picture size is visible stream resolution, in pixels, - * to be used for display purposes; must be smaller or equal to buffer - * size. - * @pic_h: picture height - * @buf_w: buffer width. Buffer size is stream resolution in pixels aligned to - * hardware requirements. - * @buf_h: buffer height - * @gop_size: group of picture size (idr frame) - * @intra_period: intra frame period - * @framerate: frame rate in fps - * @profile: as specified in standard - * @level: as specified in standard - * @wfd: WFD mode 1:on, 0:off - */ -struct venc_h264_vpu_config { - u32 input_fourcc; - u32 bitrate; - u32 pic_w; - u32 pic_h; - u32 buf_w; - u32 buf_h; - u32 gop_size; - u32 intra_period; - u32 framerate; - u32 profile; - u32 level; - u32 wfd; -}; - -/* - * struct venc_h264_vpu_buf - Structure for buffer information - * AP-W/R : AP is writer/reader on this item - * VPU-W/R: VPU is write/reader on this item - * @iova: IO virtual address - * @vpua: VPU side memory addr which is used by RC_CODE - * @size: buffer size (in bytes) - */ -struct venc_h264_vpu_buf { - u32 iova; - u32 vpua; - u32 size; -}; - -/* - * struct venc_h264_vsi - Structure for VPU driver control and info share - * AP-W/R : AP is writer/reader on this item - * VPU-W/R: VPU is write/reader on this item - * This structure is allocated in VPU side and shared to AP side. - * @config: h264 encoder configuration - * @work_bufs: working buffer information in VPU side - * The work_bufs here is for storing the 'size' info shared to AP side. - * The similar item in struct venc_h264_inst is for memory allocation - * in AP side. The AP driver will copy the 'size' from here to the one in - * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate - * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for - * register setting in VPU side. - */ -struct venc_h264_vsi { - struct venc_h264_vpu_config config; - struct venc_h264_vpu_buf work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; -}; - -/* - * struct venc_h264_inst - h264 encoder AP driver instance - * @hw_base: h264 encoder hardware register base - * @work_bufs: working buffer - * @pps_buf: buffer to store the pps bitstream - * @work_buf_allocated: working buffer allocated flag - * @frm_cnt: encoded frame count - * @prepend_hdr: when the v4l2 layer send VENC_SET_PARAM_PREPEND_HEADER cmd - * through h264_enc_set_param interface, it will set this flag and prepend the - * sps/pps in h264_enc_encode function. - * @vpu_inst: VPU instance to exchange information between AP and VPU - * @vsi: driver structure allocated by VPU side and shared to AP side for - * control and info share - * @ctx: context for v4l2 layer integration - */ -struct venc_h264_inst { - void __iomem *hw_base; - struct mtk_vcodec_mem work_bufs[VENC_H264_VPU_WORK_BUF_MAX]; - struct mtk_vcodec_mem pps_buf; - bool work_buf_allocated; - unsigned int frm_cnt; - unsigned int skip_frm_cnt; - unsigned int prepend_hdr; - struct venc_vpu_inst vpu_inst; - struct venc_h264_vsi *vsi; - struct mtk_vcodec_ctx *ctx; -}; - -static inline u32 h264_read_reg(struct venc_h264_inst *inst, u32 addr) -{ - return readl(inst->hw_base + addr); -} - -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 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; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_0: - return 50; - case V4L2_MPEG_VIDEO_H264_LEVEL_5_1: - return 51; - default: - mtk_vcodec_debug(inst, "unsupported level %d", level); - return 31; - } -} - -static void h264_enc_free_work_buf(struct venc_h264_inst *inst) -{ - int i; - - mtk_vcodec_debug_enter(inst); - - /* Except the SKIP_FRAME buffers, - * other buffers need to be freed by AP. - */ - for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) { - if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME) - mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); - } - - mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf); - - mtk_vcodec_debug_leave(inst); -} - -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) { - struct mtk_vcodec_fw *handler; - - handler = inst->vpu_inst.ctx->dev->fw_handler; - inst->work_bufs[i].va = - mtk_vcodec_fw_map_dm_addr(handler, 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) { - struct mtk_vcodec_fw *handler; - void *tmp_va; - - handler = inst->vpu_inst.ctx->dev->fw_handler; - tmp_va = mtk_vcodec_fw_map_dm_addr(handler, - 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 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, 0)) { - irq_status = ctx->irq_status; - mtk_vcodec_debug(inst, "irq_status %x <-", irq_status); - } - return irq_status; -} - -static int h264_frame_type(struct venc_h264_inst *inst) -{ - if ((inst->vsi->config.gop_size != 0 && - (inst->frm_cnt % inst->vsi->config.gop_size) == 0) || - (inst->frm_cnt == 0 && inst->vsi->config.gop_size == 0)) { - /* IDR frame */ - return VENC_H264_IDR_FRM; - } else if ((inst->vsi->config.intra_period != 0 && - (inst->frm_cnt % inst->vsi->config.intra_period) == 0) || - (inst->frm_cnt == 0 && inst->vsi->config.intra_period == 0)) { - /* I frame */ - return VENC_H264_I_FRM; - } else { - return VENC_H264_P_FRM; /* Note: B frames are not supported */ - } -} -static int h264_encode_sps(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_SPS, NULL, bs_buf, NULL); - if (ret) - return ret; - - irq_status = h264_enc_wait_venc_done(inst); - if (irq_status != MTK_VENC_IRQ_STATUS_SPS) { - mtk_vcodec_err(inst, "expect irq status %d", - MTK_VENC_IRQ_STATUS_SPS); - 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 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, NULL); - 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 int h264_encode_header(struct venc_h264_inst *inst, - struct mtk_vcodec_mem *bs_buf, - unsigned int *bs_size) -{ - int ret = 0; - unsigned int bs_size_sps; - unsigned int bs_size_pps; - - ret = h264_encode_sps(inst, bs_buf, &bs_size_sps); - if (ret) - return ret; - - ret = h264_encode_pps(inst, &inst->pps_buf, &bs_size_pps); - if (ret) - return ret; - - memcpy(bs_buf->va + bs_size_sps, inst->pps_buf.va, bs_size_pps); - *bs_size = bs_size_sps + bs_size_pps; - - return ret; -} - -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; - struct venc_frame_info frame_info; - - mtk_vcodec_debug_enter(inst); - mtk_vcodec_debug(inst, "frm_cnt = %d\n ", inst->frm_cnt); - frame_info.frm_count = inst->frm_cnt; - frame_info.skip_frm_count = inst->skip_frm_cnt; - frame_info.frm_type = h264_frame_type(inst); - mtk_vcodec_debug(inst, "frm_count = %d,skip_frm_count =%d,frm_type=%d.\n", - frame_info.frm_count, frame_info.skip_frm_count, - frame_info.frm_type); - ret = vpu_enc_encode(&inst->vpu_inst, H264_BS_MODE_FRAME, frm_buf, bs_buf, &frame_info); - 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; - ++inst->skip_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 void h264_encode_filler(struct venc_h264_inst *inst, void *buf, - int size) -{ - unsigned char *p = buf; - - if (size < H264_FILLER_MARKER_SIZE) { - mtk_vcodec_err(inst, "filler size too small %d", size); - return; - } - - memcpy(p, h264_filler_marker, ARRAY_SIZE(h264_filler_marker)); - size -= H264_FILLER_MARKER_SIZE; - p += H264_FILLER_MARKER_SIZE; - memset(p, 0xff, size); -} - -static int h264_enc_init(struct mtk_vcodec_ctx *ctx) -{ - const bool is_ext = MTK_ENC_CTX_IS_EXT(ctx); - int ret = 0; - struct venc_h264_inst *inst; - - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) - return -ENOMEM; - - inst->ctx = ctx; - inst->vpu_inst.ctx = ctx; - inst->vpu_inst.id = is_ext ? SCP_IPI_VENC_H264 : IPI_VENC_H264; - inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_SYS); - - mtk_vcodec_debug_enter(inst); - - ret = vpu_enc_init(&inst->vpu_inst); - - inst->vsi = (struct venc_h264_vsi *)inst->vpu_inst.vsi; - - mtk_vcodec_debug_leave(inst); - - if (ret) - kfree(inst); - else - ctx->drv_handle = inst; - - return ret; -} - -static int h264_enc_encode(void *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; -} - -static int h264_enc_set_param(void *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; - case VENC_SET_PARAM_FORCE_INTRA: - case VENC_SET_PARAM_GOP_SIZE: - case VENC_SET_PARAM_INTRA_PERIOD: - inst->frm_cnt = 0; - inst->skip_frm_cnt = 0; - fallthrough; - default: - ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); - break; - } - - mtk_vcodec_debug_leave(inst); - - return ret; -} - -static int h264_enc_deinit(void *handle) -{ - int ret = 0; - struct venc_h264_inst *inst = (struct venc_h264_inst *)handle; - - mtk_vcodec_debug_enter(inst); - - ret = vpu_enc_deinit(&inst->vpu_inst); - - if (inst->work_buf_allocated) - h264_enc_free_work_buf(inst); - - mtk_vcodec_debug_leave(inst); - kfree(inst); - - return ret; -} - -const struct venc_common_if venc_h264_if = { - .init = h264_enc_init, - .encode = h264_enc_encode, - .set_param = h264_enc_set_param, - .deinit = h264_enc_deinit, -}; diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c deleted file mode 100644 index 56ce58f761f1..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c +++ /dev/null @@ -1,468 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Daniel Hsiao - * PoChun Lin - */ - -#include -#include -#include - -#include "../mtk_vcodec_drv.h" -#include "../mtk_vcodec_util.h" -#include "../mtk_vcodec_intr.h" -#include "../mtk_vcodec_enc.h" -#include "../mtk_vcodec_enc_pm.h" -#include "../venc_drv_base.h" -#include "../venc_ipi_msg.h" -#include "../venc_vpu_if.h" - -#define VENC_BITSTREAM_FRAME_SIZE 0x0098 -#define VENC_BITSTREAM_HEADER_LEN 0x00e8 - -/* This ac_tag is vp8 frame tag. */ -#define MAX_AC_TAG_SIZE 10 - -/* - * enum venc_vp8_vpu_work_buf - vp8 encoder buffer index - */ -enum venc_vp8_vpu_work_buf { - VENC_VP8_VPU_WORK_BUF_LUMA, - VENC_VP8_VPU_WORK_BUF_LUMA2, - VENC_VP8_VPU_WORK_BUF_LUMA3, - VENC_VP8_VPU_WORK_BUF_CHROMA, - VENC_VP8_VPU_WORK_BUF_CHROMA2, - VENC_VP8_VPU_WORK_BUF_CHROMA3, - VENC_VP8_VPU_WORK_BUF_MV_INFO, - VENC_VP8_VPU_WORK_BUF_BS_HEADER, - VENC_VP8_VPU_WORK_BUF_PROB_BUF, - VENC_VP8_VPU_WORK_BUF_RC_INFO, - VENC_VP8_VPU_WORK_BUF_RC_CODE, - VENC_VP8_VPU_WORK_BUF_RC_CODE2, - VENC_VP8_VPU_WORK_BUF_RC_CODE3, - VENC_VP8_VPU_WORK_BUF_MAX, -}; - -/* - * struct venc_vp8_vpu_config - Structure for vp8 encoder configuration - * AP-W/R : AP is writer/reader on this item - * VPU-W/R: VPU is write/reader on this item - * @input_fourcc: input fourcc - * @bitrate: target bitrate (in bps) - * @pic_w: picture width. Picture size is visible stream resolution, in pixels, - * to be used for display purposes; must be smaller or equal to buffer - * size. - * @pic_h: picture height - * @buf_w: buffer width (with 16 alignment). Buffer size is stream resolution - * in pixels aligned to hardware requirements. - * @buf_h: buffer height (with 16 alignment) - * @gop_size: group of picture size (key frame) - * @framerate: frame rate in fps - * @ts_mode: temporal scalability mode (0: disable, 1: enable) - * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. - */ -struct venc_vp8_vpu_config { - u32 input_fourcc; - u32 bitrate; - u32 pic_w; - u32 pic_h; - u32 buf_w; - u32 buf_h; - u32 gop_size; - u32 framerate; - u32 ts_mode; -}; - -/* - * struct venc_vp8_vpu_buf - Structure for buffer information - * AP-W/R : AP is writer/reader on this item - * VPU-W/R: VPU is write/reader on this item - * @iova: IO virtual address - * @vpua: VPU side memory addr which is used by RC_CODE - * @size: buffer size (in bytes) - */ -struct venc_vp8_vpu_buf { - u32 iova; - u32 vpua; - u32 size; -}; - -/* - * struct venc_vp8_vsi - Structure for VPU driver control and info share - * AP-W/R : AP is writer/reader on this item - * VPU-W/R: VPU is write/reader on this item - * This structure is allocated in VPU side and shared to AP side. - * @config: vp8 encoder configuration - * @work_bufs: working buffer information in VPU side - * The work_bufs here is for storing the 'size' info shared to AP side. - * The similar item in struct venc_vp8_inst is for memory allocation - * in AP side. The AP driver will copy the 'size' from here to the one in - * struct mtk_vcodec_mem, then invoke mtk_vcodec_mem_alloc to allocate - * the buffer. After that, bypass the 'dma_addr' to the 'iova' field here for - * register setting in VPU side. - */ -struct venc_vp8_vsi { - struct venc_vp8_vpu_config config; - struct venc_vp8_vpu_buf work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; -}; - -/* - * struct venc_vp8_inst - vp8 encoder AP driver instance - * @hw_base: vp8 encoder hardware register base - * @work_bufs: working buffer - * @work_buf_allocated: working buffer allocated flag - * @frm_cnt: encoded frame count, it's used for I-frame judgement and - * reset when force intra cmd received. - * @ts_mode: temporal scalability mode (0: disable, 1: enable) - * support three temporal layers - 0: 7.5fps 1: 7.5fps 2: 15fps. - * @vpu_inst: VPU instance to exchange information between AP and VPU - * @vsi: driver structure allocated by VPU side and shared to AP side for - * control and info share - * @ctx: context for v4l2 layer integration - */ -struct venc_vp8_inst { - void __iomem *hw_base; - struct mtk_vcodec_mem work_bufs[VENC_VP8_VPU_WORK_BUF_MAX]; - bool work_buf_allocated; - unsigned int frm_cnt; - unsigned int ts_mode; - struct venc_vpu_inst vpu_inst; - struct venc_vp8_vsi *vsi; - struct mtk_vcodec_ctx *ctx; -}; - -static inline u32 vp8_enc_read_reg(struct venc_vp8_inst *inst, u32 addr) -{ - return readl(inst->hw_base + addr); -} - -static void vp8_enc_free_work_buf(struct venc_vp8_inst *inst) -{ - int i; - - mtk_vcodec_debug_enter(inst); - - /* Buffers need to be freed by AP. */ - for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { - if (inst->work_bufs[i].size == 0) - continue; - mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]); - } - - mtk_vcodec_debug_leave(inst); -} - -static int vp8_enc_alloc_work_buf(struct venc_vp8_inst *inst) -{ - int i; - int ret = 0; - struct venc_vp8_vpu_buf *wb = inst->vsi->work_bufs; - - mtk_vcodec_debug_enter(inst); - - for (i = 0; i < VENC_VP8_VPU_WORK_BUF_MAX; i++) { - if (wb[i].size == 0) - continue; - /* - * 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. For the - * RC_CODEx buffers, they are pre-allocated in the VPU side - * because they are inside VPU SRAM, 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. - */ - inst->work_bufs[i].size = wb[i].size; - ret = mtk_vcodec_mem_alloc(inst->ctx, &inst->work_bufs[i]); - if (ret) { - mtk_vcodec_err(inst, - "cannot alloc work_bufs[%d]", i); - goto err_alloc; - } - /* - * This RC_CODEx is pre-allocated by VPU and saved in VPU addr. - * So we need use memcpy to copy RC_CODEx from VPU addr into IO - * virtual addr in 'iova' field for reg setting in VPU side. - */ - if (i == VENC_VP8_VPU_WORK_BUF_RC_CODE || - i == VENC_VP8_VPU_WORK_BUF_RC_CODE2 || - i == VENC_VP8_VPU_WORK_BUF_RC_CODE3) { - struct mtk_vcodec_fw *handler; - void *tmp_va; - - handler = inst->vpu_inst.ctx->dev->fw_handler; - tmp_va = mtk_vcodec_fw_map_dm_addr(handler, - 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_bufs[%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); - } - - mtk_vcodec_debug_leave(inst); - - return ret; - -err_alloc: - vp8_enc_free_work_buf(inst); - - return ret; -} - -static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_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, 0)) { - irq_status = ctx->irq_status; - mtk_vcodec_debug(inst, "isr return %x", irq_status); - } - return irq_status; -} - -/* - * Compose ac_tag, bitstream header and bitstream payload into - * one bitstream buffer. - */ -static int vp8_enc_compose_one_frame(struct venc_vp8_inst *inst, - struct mtk_vcodec_mem *bs_buf, - unsigned int *bs_size) -{ - unsigned int not_key; - u32 bs_frm_size; - u32 bs_hdr_len; - unsigned int ac_tag_size; - u8 ac_tag[MAX_AC_TAG_SIZE]; - u32 tag; - - bs_frm_size = vp8_enc_read_reg(inst, VENC_BITSTREAM_FRAME_SIZE); - bs_hdr_len = vp8_enc_read_reg(inst, VENC_BITSTREAM_HEADER_LEN); - - /* if a frame is key frame, not_key is 0 */ - not_key = !inst->vpu_inst.is_key_frm; - tag = (bs_hdr_len << 5) | 0x10 | not_key; - ac_tag[0] = tag & 0xff; - ac_tag[1] = (tag >> 8) & 0xff; - ac_tag[2] = (tag >> 16) & 0xff; - - /* key frame */ - if (not_key == 0) { - ac_tag_size = MAX_AC_TAG_SIZE; - ac_tag[3] = 0x9d; - ac_tag[4] = 0x01; - ac_tag[5] = 0x2a; - ac_tag[6] = inst->vsi->config.pic_w; - ac_tag[7] = inst->vsi->config.pic_w >> 8; - ac_tag[8] = inst->vsi->config.pic_h; - ac_tag[9] = inst->vsi->config.pic_h >> 8; - } else { - ac_tag_size = 3; - } - - if (bs_buf->size < bs_hdr_len + bs_frm_size + ac_tag_size) { - mtk_vcodec_err(inst, "bitstream buf size is too small(%zu)", - bs_buf->size); - return -EINVAL; - } - - /* - * (1) The vp8 bitstream header and body are generated by the HW vp8 - * encoder separately at the same time. We cannot know the bitstream - * header length in advance. - * (2) From the vp8 spec, there is no stuffing byte allowed between the - * ac tag, bitstream header and bitstream body. - */ - memmove(bs_buf->va + bs_hdr_len + ac_tag_size, - bs_buf->va, bs_frm_size); - memcpy(bs_buf->va + ac_tag_size, - inst->work_bufs[VENC_VP8_VPU_WORK_BUF_BS_HEADER].va, - bs_hdr_len); - memcpy(bs_buf->va, ac_tag, ac_tag_size); - *bs_size = bs_frm_size + bs_hdr_len + ac_tag_size; - - return 0; -} - -static int vp8_enc_encode_frame(struct venc_vp8_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(inst, "->frm_cnt=%d", inst->frm_cnt); - - ret = vpu_enc_encode(&inst->vpu_inst, 0, frm_buf, bs_buf, NULL); - if (ret) - return ret; - - irq_status = vp8_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; - } - - if (vp8_enc_compose_one_frame(inst, bs_buf, bs_size)) { - mtk_vcodec_err(inst, "vp8_enc_compose_one_frame failed"); - return -EINVAL; - } - - inst->frm_cnt++; - mtk_vcodec_debug(inst, "<-size=%d key_frm=%d", *bs_size, - inst->vpu_inst.is_key_frm); - - return ret; -} - -static int vp8_enc_init(struct mtk_vcodec_ctx *ctx) -{ - int ret = 0; - struct venc_vp8_inst *inst; - - inst = kzalloc(sizeof(*inst), GFP_KERNEL); - if (!inst) - return -ENOMEM; - - inst->ctx = ctx; - inst->vpu_inst.ctx = ctx; - inst->vpu_inst.id = IPI_VENC_VP8; - inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx, VENC_LT_SYS); - - mtk_vcodec_debug_enter(inst); - - ret = vpu_enc_init(&inst->vpu_inst); - - inst->vsi = (struct venc_vp8_vsi *)inst->vpu_inst.vsi; - - mtk_vcodec_debug_leave(inst); - - if (ret) - kfree(inst); - else - ctx->drv_handle = inst; - - return ret; -} - -static int vp8_enc_encode(void *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_vp8_inst *inst = (struct venc_vp8_inst *)handle; - struct mtk_vcodec_ctx *ctx = inst->ctx; - - mtk_vcodec_debug_enter(inst); - - enable_irq(ctx->dev->enc_irq); - - switch (opt) { - case VENC_START_OPT_ENCODE_FRAME: - ret = vp8_enc_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; - - default: - mtk_vcodec_err(inst, "opt not support:%d", opt); - ret = -EINVAL; - break; - } - -encode_err: - - disable_irq(ctx->dev->enc_irq); - mtk_vcodec_debug_leave(inst); - - return ret; -} - -static int vp8_enc_set_param(void *handle, - enum venc_set_param_type type, - struct venc_enc_param *enc_prm) -{ - int ret = 0; - struct venc_vp8_inst *inst = (struct venc_vp8_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.ts_mode = inst->ts_mode; - ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); - if (ret) - break; - if (inst->work_buf_allocated) { - vp8_enc_free_work_buf(inst); - inst->work_buf_allocated = false; - } - ret = vp8_enc_alloc_work_buf(inst); - if (ret) - break; - inst->work_buf_allocated = true; - break; - - /* - * VENC_SET_PARAM_TS_MODE must be called before VENC_SET_PARAM_ENC - */ - case VENC_SET_PARAM_TS_MODE: - inst->ts_mode = 1; - mtk_vcodec_debug(inst, "set ts_mode"); - break; - - default: - ret = vpu_enc_set_param(&inst->vpu_inst, type, enc_prm); - break; - } - - mtk_vcodec_debug_leave(inst); - - return ret; -} - -static int vp8_enc_deinit(void *handle) -{ - int ret = 0; - struct venc_vp8_inst *inst = (struct venc_vp8_inst *)handle; - - mtk_vcodec_debug_enter(inst); - - ret = vpu_enc_deinit(&inst->vpu_inst); - - if (inst->work_buf_allocated) - vp8_enc_free_work_buf(inst); - - mtk_vcodec_debug_leave(inst); - kfree(inst); - - return ret; -} - -const struct venc_common_if venc_vp8_if = { - .init = vp8_enc_init, - .encode = vp8_enc_encode, - .set_param = vp8_enc_set_param, - .deinit = vp8_enc_deinit, -}; diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mtk-vcodec/venc_drv_base.h deleted file mode 100644 index 3d718411dc73..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc_drv_base.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Daniel Hsiao - * Jungchang Tsao - * Tiffany Lin - */ - -#ifndef _VENC_DRV_BASE_ -#define _VENC_DRV_BASE_ - -#include "mtk_vcodec_drv.h" - -#include "venc_drv_if.h" - -struct venc_common_if { - /** - * (*init)() - initialize driver - * @ctx: [in] mtk v4l2 context - * @handle: [out] driver handle - */ - int (*init)(struct mtk_vcodec_ctx *ctx); - - /** - * (*encode)() - trigger encode - * @handle: [in] driver handle - * @opt: [in] encode option - * @frm_buf: [in] frame buffer to store input frame - * @bs_buf: [in] bitstream buffer to store output bitstream - * @result: [out] encode result - */ - int (*encode)(void *handle, enum venc_start_opt opt, - struct venc_frm_buf *frm_buf, - struct mtk_vcodec_mem *bs_buf, - struct venc_done_result *result); - - /** - * (*set_param)() - set driver's parameter - * @handle: [in] driver handle - * @type: [in] parameter type - * @in: [in] buffer to store the parameter - */ - int (*set_param)(void *handle, enum venc_set_param_type type, - struct venc_enc_param *in); - - /** - * (*deinit)() - deinitialize driver. - * @handle: [in] driver handle - */ - int (*deinit)(void *handle); -}; - -#endif diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mtk-vcodec/venc_drv_if.c deleted file mode 100644 index ce0bce811615..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Daniel Hsiao - * Jungchang Tsao - * Tiffany Lin - */ - -#include -#include -#include - -#include "venc_drv_base.h" -#include "venc_drv_if.h" - -#include "mtk_vcodec_enc.h" -#include "mtk_vcodec_enc_pm.h" - -int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) -{ - int ret = 0; - - switch (fourcc) { - case V4L2_PIX_FMT_VP8: - ctx->enc_if = &venc_vp8_if; - break; - case V4L2_PIX_FMT_H264: - ctx->enc_if = &venc_h264_if; - break; - default: - return -EINVAL; - } - - mtk_venc_lock(ctx); - mtk_vcodec_enc_clock_on(&ctx->dev->pm); - ret = ctx->enc_if->init(ctx); - mtk_vcodec_enc_clock_off(&ctx->dev->pm); - mtk_venc_unlock(ctx); - - return ret; -} - -int venc_if_set_param(struct mtk_vcodec_ctx *ctx, - enum venc_set_param_type type, struct venc_enc_param *in) -{ - int ret = 0; - - mtk_venc_lock(ctx); - mtk_vcodec_enc_clock_on(&ctx->dev->pm); - ret = ctx->enc_if->set_param(ctx->drv_handle, type, in); - mtk_vcodec_enc_clock_off(&ctx->dev->pm); - mtk_venc_unlock(ctx); - - return ret; -} - -int venc_if_encode(struct mtk_vcodec_ctx *ctx, - 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; - unsigned long flags; - - mtk_venc_lock(ctx); - - spin_lock_irqsave(&ctx->dev->irqlock, flags); - ctx->dev->curr_ctx = ctx; - spin_unlock_irqrestore(&ctx->dev->irqlock, flags); - - mtk_vcodec_enc_clock_on(&ctx->dev->pm); - ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf, - bs_buf, result); - mtk_vcodec_enc_clock_off(&ctx->dev->pm); - - spin_lock_irqsave(&ctx->dev->irqlock, flags); - ctx->dev->curr_ctx = NULL; - spin_unlock_irqrestore(&ctx->dev->irqlock, flags); - - mtk_venc_unlock(ctx); - return ret; -} - -int venc_if_deinit(struct mtk_vcodec_ctx *ctx) -{ - int ret = 0; - - if (!ctx->drv_handle) - return 0; - - mtk_venc_lock(ctx); - mtk_vcodec_enc_clock_on(&ctx->dev->pm); - ret = ctx->enc_if->deinit(ctx->drv_handle); - mtk_vcodec_enc_clock_off(&ctx->dev->pm); - mtk_venc_unlock(ctx); - - ctx->drv_handle = NULL; - - return ret; -} diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mtk-vcodec/venc_drv_if.h deleted file mode 100644 index 0b04a1020873..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc_drv_if.h +++ /dev/null @@ -1,170 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Daniel Hsiao - * Jungchang Tsao - * Tiffany Lin - */ - -#ifndef _VENC_DRV_IF_H_ -#define _VENC_DRV_IF_H_ - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_util.h" - -/* - * enum venc_yuv_fmt - The type of input yuv format - * (VPU related: If you change the order, you must also update the VPU codes.) - * @VENC_YUV_FORMAT_I420: I420 YUV format - * @VENC_YUV_FORMAT_YV12: YV12 YUV format - * @VENC_YUV_FORMAT_NV12: NV12 YUV format - * @VENC_YUV_FORMAT_NV21: NV21 YUV format - */ -enum venc_yuv_fmt { - VENC_YUV_FORMAT_I420 = 3, - VENC_YUV_FORMAT_YV12 = 5, - VENC_YUV_FORMAT_NV12 = 6, - VENC_YUV_FORMAT_NV21 = 7, -}; - -/* - * enum venc_start_opt - encode frame option used in venc_if_encode() - * @VENC_START_OPT_ENCODE_SEQUENCE_HEADER: encode SPS/PPS for H264 - * @VENC_START_OPT_ENCODE_FRAME: encode normal frame - */ -enum venc_start_opt { - VENC_START_OPT_ENCODE_SEQUENCE_HEADER, - VENC_START_OPT_ENCODE_FRAME, -}; - -/* - * enum venc_set_param_type - The type of set parameter used in - * venc_if_set_param() - * (VPU related: If you change the order, you must also update the VPU codes.) - * @VENC_SET_PARAM_ENC: set encoder parameters - * @VENC_SET_PARAM_FORCE_INTRA: force an intra frame - * @VENC_SET_PARAM_ADJUST_BITRATE: adjust bitrate (in bps) - * @VENC_SET_PARAM_ADJUST_FRAMERATE: set frame rate - * @VENC_SET_PARAM_GOP_SIZE: set IDR interval - * @VENC_SET_PARAM_INTRA_PERIOD: set I frame interval - * @VENC_SET_PARAM_SKIP_FRAME: set H264 skip one frame - * @VENC_SET_PARAM_PREPEND_HEADER: set H264 prepend SPS/PPS before IDR - * @VENC_SET_PARAM_TS_MODE: set VP8 temporal scalability mode - */ -enum venc_set_param_type { - VENC_SET_PARAM_ENC, - VENC_SET_PARAM_FORCE_INTRA, - VENC_SET_PARAM_ADJUST_BITRATE, - VENC_SET_PARAM_ADJUST_FRAMERATE, - VENC_SET_PARAM_GOP_SIZE, - VENC_SET_PARAM_INTRA_PERIOD, - VENC_SET_PARAM_SKIP_FRAME, - VENC_SET_PARAM_PREPEND_HEADER, - VENC_SET_PARAM_TS_MODE, -}; - -/* - * struct venc_enc_prm - encoder settings for VENC_SET_PARAM_ENC used in - * venc_if_set_param() - * @input_fourcc: input yuv format - * @h264_profile: V4L2 defined H.264 profile - * @h264_level: V4L2 defined H.264 level - * @width: image width - * @height: image height - * @buf_width: buffer width - * @buf_height: buffer height - * @frm_rate: frame rate in fps - * @intra_period: intra frame period - * @bitrate: target bitrate in bps - * @gop_size: group of picture size - */ -struct venc_enc_param { - enum venc_yuv_fmt input_yuv_fmt; - unsigned int h264_profile; - unsigned int h264_level; - unsigned int width; - unsigned int height; - unsigned int buf_width; - unsigned int buf_height; - unsigned int frm_rate; - unsigned int intra_period; - unsigned int bitrate; - unsigned int gop_size; -}; - -/** - * struct venc_frame_info - per-frame information to pass to the firmware. - * - * @frm_count: sequential number for this frame - * @skip_frm_count: number of frames skipped so far while decoding - * @frm_type: type of the frame, from enum venc_h264_frame_type - */ -struct venc_frame_info { - unsigned int frm_count; /* per frame update */ - unsigned int skip_frm_count; /* per frame update */ - unsigned int frm_type; /* per frame update */ -}; - -/* - * struct venc_frm_buf - frame buffer information used in venc_if_encode() - * @fb_addr: plane frame buffer addresses - */ -struct venc_frm_buf { - struct mtk_vcodec_fb fb_addr[MTK_VCODEC_MAX_PLANES]; -}; - -/* - * struct venc_done_result - This is return information used in venc_if_encode() - * @bs_size: output bitstream size - * @is_key_frm: output is key frame or not - */ -struct venc_done_result { - unsigned int bs_size; - bool is_key_frm; -}; - -extern const struct venc_common_if venc_h264_if; -extern const struct venc_common_if venc_vp8_if; - -/* - * venc_if_init - Create the driver handle - * @ctx: device context - * @fourcc: encoder input format - * Return: 0 if creating handle successfully, otherwise it is failed. - */ -int venc_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc); - -/* - * venc_if_deinit - Release the driver handle - * @ctx: device context - * Return: 0 if releasing handle successfully, otherwise it is failed. - */ -int venc_if_deinit(struct mtk_vcodec_ctx *ctx); - -/* - * venc_if_set_param - Set parameter to driver - * @ctx: device context - * @type: parameter type - * @in: input parameter - * Return: 0 if setting param successfully, otherwise it is failed. - */ -int venc_if_set_param(struct mtk_vcodec_ctx *ctx, - enum venc_set_param_type type, - struct venc_enc_param *in); - -/* - * venc_if_encode - Encode one frame - * @ctx: device context - * @opt: encode frame option - * @frm_buf: input frame buffer information - * @bs_buf: output bitstream buffer infomraiton - * @result: encode result - * Return: 0 if encoding frame successfully, otherwise it is failed. - */ -int venc_if_encode(struct mtk_vcodec_ctx *ctx, - enum venc_start_opt opt, - struct venc_frm_buf *frm_buf, - struct mtk_vcodec_mem *bs_buf, - struct venc_done_result *result); - -#endif /* _VENC_DRV_IF_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h deleted file mode 100644 index 587a2cf15b76..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h +++ /dev/null @@ -1,220 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: Jungchang Tsao - * Daniel Hsiao - * Tiffany Lin - */ - -#ifndef _VENC_IPI_MSG_H_ -#define _VENC_IPI_MSG_H_ - -#define AP_IPIMSG_VENC_BASE 0xC000 -#define VPU_IPIMSG_VENC_BASE 0xD000 - -/* - * enum venc_ipi_msg_id - message id between AP and VPU - * (ipi stands for inter-processor interrupt) - * @AP_IPIMSG_ENC_XXX: AP to VPU cmd message id - * @VPU_IPIMSG_ENC_XXX_DONE: VPU ack AP cmd message id - */ -enum venc_ipi_msg_id { - AP_IPIMSG_ENC_INIT = AP_IPIMSG_VENC_BASE, - AP_IPIMSG_ENC_SET_PARAM, - AP_IPIMSG_ENC_ENCODE, - AP_IPIMSG_ENC_DEINIT, - - VPU_IPIMSG_ENC_INIT_DONE = VPU_IPIMSG_VENC_BASE, - VPU_IPIMSG_ENC_SET_PARAM_DONE, - VPU_IPIMSG_ENC_ENCODE_DONE, - VPU_IPIMSG_ENC_DEINIT_DONE, -}; - -/** - * struct venc_ap_ipi_msg_init - AP to VPU init cmd structure - * @msg_id: message id (AP_IPIMSG_XXX_ENC_INIT) - * @reserved: reserved for future use. vpu is running in 32bit. Without - * this reserved field, if kernel run in 64bit. this struct size - * will be different between kernel and vpu - * @venc_inst: AP encoder instance - * (struct venc_vp8_inst/venc_h264_inst *) - */ -struct venc_ap_ipi_msg_init { - uint32_t msg_id; - uint32_t reserved; - uint64_t venc_inst; -}; - -/** - * struct venc_ap_ipi_msg_set_param - AP to VPU set_param cmd structure - * @msg_id: message id (AP_IPIMSG_XXX_ENC_SET_PARAM) - * @vpu_inst_addr: VPU encoder instance addr - * (struct venc_vp8_vsi/venc_h264_vsi *) - * @param_id: parameter id (venc_set_param_type) - * @data_item: number of items in the data array - * @data: data array to store the set parameters - */ -struct venc_ap_ipi_msg_set_param { - uint32_t msg_id; - uint32_t vpu_inst_addr; - uint32_t param_id; - uint32_t data_item; - uint32_t data[8]; -}; - -struct venc_ap_ipi_msg_set_param_ext { - struct venc_ap_ipi_msg_set_param base; - uint32_t data_ext[24]; -}; - -/** - * struct venc_ap_ipi_msg_enc - AP to VPU enc cmd structure - * @msg_id: message id (AP_IPIMSG_XXX_ENC_ENCODE) - * @vpu_inst_addr: VPU encoder instance addr - * (struct venc_vp8_vsi/venc_h264_vsi *) - * @bs_mode: bitstream mode for h264 - * (H264_BS_MODE_SPS/H264_BS_MODE_PPS/H264_BS_MODE_FRAME) - * @input_addr: pointer to input image buffer plane - * @bs_addr: pointer to output bit stream buffer - * @bs_size: bit stream buffer size - */ -struct venc_ap_ipi_msg_enc { - uint32_t msg_id; - uint32_t vpu_inst_addr; - uint32_t bs_mode; - uint32_t input_addr[3]; - uint32_t bs_addr; - uint32_t bs_size; -}; - -/** - * struct venc_ap_ipi_msg_enc_ext - AP to SCP extended enc cmd structure - * - * @base: base msg structure - * @data_item: number of items in the data array - * @data: data array to store the set parameters - */ -struct venc_ap_ipi_msg_enc_ext { - struct venc_ap_ipi_msg_enc base; - uint32_t data_item; - uint32_t data[32]; -}; - -/** - * struct venc_ap_ipi_msg_deinit - AP to VPU deinit cmd structure - * @msg_id: message id (AP_IPIMSG_XXX_ENC_DEINIT) - * @vpu_inst_addr: VPU encoder instance addr - * (struct venc_vp8_vsi/venc_h264_vsi *) - */ -struct venc_ap_ipi_msg_deinit { - uint32_t msg_id; - uint32_t vpu_inst_addr; -}; - -/* - * enum venc_ipi_msg_status - VPU ack AP cmd status - */ -enum venc_ipi_msg_status { - VENC_IPI_MSG_STATUS_OK, - VENC_IPI_MSG_STATUS_FAIL, -}; - -/** - * struct venc_vpu_ipi_msg_common - VPU ack AP cmd common structure - * @msg_id: message id (VPU_IPIMSG_XXX_DONE) - * @status: cmd status (venc_ipi_msg_status) - * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) - */ -struct venc_vpu_ipi_msg_common { - uint32_t msg_id; - uint32_t status; - uint64_t venc_inst; -}; - -/** - * struct venc_vpu_ipi_msg_init - VPU ack AP init cmd structure - * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) - * @status: cmd status (venc_ipi_msg_status) - * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) - * @vpu_inst_addr: VPU encoder instance addr - * (struct venc_vp8_vsi/venc_h264_vsi *) - * @venc_abi_version: ABI version of the firmware. Kernel can use it to - * ensure that it is compatible with the firmware. - * For MT8173 the value of this field is undefined and - * should not be used. - */ -struct venc_vpu_ipi_msg_init { - uint32_t msg_id; - uint32_t status; - uint64_t venc_inst; - uint32_t vpu_inst_addr; - uint32_t venc_abi_version; -}; - -/** - * struct venc_vpu_ipi_msg_set_param - VPU ack AP set_param cmd structure - * @msg_id: message id (VPU_IPIMSG_XXX_ENC_SET_PARAM_DONE) - * @status: cmd status (venc_ipi_msg_status) - * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) - * @param_id: parameter id (venc_set_param_type) - * @data_item: number of items in the data array - * @data: data array to store the return result - */ -struct venc_vpu_ipi_msg_set_param { - uint32_t msg_id; - uint32_t status; - uint64_t venc_inst; - uint32_t param_id; - uint32_t data_item; - uint32_t data[6]; -}; - -/** - * enum venc_ipi_msg_enc_state - Type of encode state - * @VEN_IPI_MSG_ENC_STATE_FRAME: one frame being encoded - * @VEN_IPI_MSG_ENC_STATE_PART: bit stream buffer full - * @VEN_IPI_MSG_ENC_STATE_SKIP: encoded skip frame - * @VEN_IPI_MSG_ENC_STATE_ERROR: encounter error - */ -enum venc_ipi_msg_enc_state { - VEN_IPI_MSG_ENC_STATE_FRAME, - VEN_IPI_MSG_ENC_STATE_PART, - VEN_IPI_MSG_ENC_STATE_SKIP, - VEN_IPI_MSG_ENC_STATE_ERROR, -}; - -/** - * struct venc_vpu_ipi_msg_enc - VPU ack AP enc cmd structure - * @msg_id: message id (VPU_IPIMSG_XXX_ENC_ENCODE_DONE) - * @status: cmd status (venc_ipi_msg_status) - * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) - * @state: encode state (venc_ipi_msg_enc_state) - * @is_key_frm: whether the encoded frame is key frame - * @bs_size: encoded bitstream size - * @reserved: reserved for future use. vpu is running in 32bit. Without - * this reserved field, if kernel run in 64bit. this struct size - * will be different between kernel and vpu - */ -struct venc_vpu_ipi_msg_enc { - uint32_t msg_id; - uint32_t status; - uint64_t venc_inst; - uint32_t state; - uint32_t is_key_frm; - uint32_t bs_size; - uint32_t reserved; -}; - -/** - * struct venc_vpu_ipi_msg_deinit - VPU ack AP deinit cmd structure - * @msg_id: message id (VPU_IPIMSG_XXX_ENC_DEINIT_DONE) - * @status: cmd status (venc_ipi_msg_status) - * @venc_inst: AP encoder instance (struct venc_vp8_inst/venc_h264_inst *) - */ -struct venc_vpu_ipi_msg_deinit { - uint32_t msg_id; - uint32_t status; - uint64_t venc_inst; -}; - -#endif /* _VENC_IPI_MSG_H_ */ diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mtk-vcodec/venc_vpu_if.c deleted file mode 100644 index e7899d8a3e4e..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c +++ /dev/null @@ -1,293 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PoChun Lin - */ - -#include "mtk_vcodec_drv.h" -#include "mtk_vcodec_fw.h" -#include "venc_ipi_msg.h" -#include "venc_vpu_if.h" - -static void handle_enc_init_msg(struct venc_vpu_inst *vpu, const void *data) -{ - const struct venc_vpu_ipi_msg_init *msg = data; - - vpu->inst_addr = msg->vpu_inst_addr; - vpu->vsi = mtk_vcodec_fw_map_dm_addr(vpu->ctx->dev->fw_handler, - msg->vpu_inst_addr); - - /* Firmware version field value is unspecified on MT8173. */ - if (vpu->ctx->dev->venc_pdata->chip == MTK_MT8173) - return; - - /* Check firmware version. */ - mtk_vcodec_debug(vpu, "firmware version: 0x%x\n", - msg->venc_abi_version); - switch (msg->venc_abi_version) { - case 1: - break; - default: - mtk_vcodec_err(vpu, "unhandled firmware version 0x%x\n", - msg->venc_abi_version); - vpu->failure = 1; - break; - } -} - -static void handle_enc_encode_msg(struct venc_vpu_inst *vpu, const void *data) -{ - const struct venc_vpu_ipi_msg_enc *msg = data; - - vpu->state = msg->state; - vpu->bs_size = msg->bs_size; - vpu->is_key_frm = msg->is_key_frm; -} - -static void vpu_enc_ipi_handler(void *data, unsigned int len, void *priv) -{ - const 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); - - vpu->signaled = 1; - vpu->failure = (msg->status != VENC_IPI_MSG_STATUS_OK); - if (vpu->failure) - goto failure; - - 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; - } - -failure: - mtk_vcodec_debug_leave(vpu); -} - -static int vpu_enc_send_msg(struct venc_vpu_inst *vpu, void *msg, - int len) -{ - int status; - - mtk_vcodec_debug_enter(vpu); - - if (!vpu->ctx->dev->fw_handler) { - mtk_vcodec_err(vpu, "inst dev is NULL"); - return -EINVAL; - } - - status = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg, - len, 2000); - if (status) { - mtk_vcodec_err(vpu, "vpu_ipi_send msg_id %x len %d fail %d", - *(uint32_t *)msg, len, status); - return -EINVAL; - } - if (vpu->failure) - return -EINVAL; - - mtk_vcodec_debug_leave(vpu); - - return 0; -} - -int vpu_enc_init(struct venc_vpu_inst *vpu) -{ - int status; - struct venc_ap_ipi_msg_init out; - - mtk_vcodec_debug_enter(vpu); - - init_waitqueue_head(&vpu->wq_hd); - vpu->signaled = 0; - vpu->failure = 0; - - status = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id, - vpu_enc_ipi_handler, "venc", NULL); - - if (status) { - mtk_vcodec_err(vpu, "vpu_ipi_register fail %d", status); - return -EINVAL; - } - - memset(&out, 0, sizeof(out)); - out.msg_id = AP_IPIMSG_ENC_INIT; - out.venc_inst = (unsigned long)vpu; - if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { - mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_INIT fail"); - return -EINVAL; - } - - mtk_vcodec_debug_leave(vpu); - - return 0; -} - -static unsigned int venc_enc_param_crop_right(struct venc_vpu_inst *vpu, - struct venc_enc_param *enc_prm) -{ - unsigned int img_crop_right = enc_prm->buf_width - enc_prm->width; - - return img_crop_right % 16; -} - -static unsigned int venc_enc_param_crop_bottom(struct venc_enc_param *enc_prm) -{ - return round_up(enc_prm->height, 16) - enc_prm->height; -} - -static unsigned int venc_enc_param_num_mb(struct venc_enc_param *enc_prm) -{ - return DIV_ROUND_UP(enc_prm->width, 16) * - DIV_ROUND_UP(enc_prm->height, 16); -} - -int vpu_enc_set_param(struct venc_vpu_inst *vpu, - enum venc_set_param_type id, - struct venc_enc_param *enc_param) -{ - const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx); - size_t msg_size = is_ext ? - sizeof(struct venc_ap_ipi_msg_set_param_ext) : - sizeof(struct venc_ap_ipi_msg_set_param); - struct venc_ap_ipi_msg_set_param_ext out; - - mtk_vcodec_debug(vpu, "id %d ->", id); - - memset(&out, 0, sizeof(out)); - out.base.msg_id = AP_IPIMSG_ENC_SET_PARAM; - out.base.vpu_inst_addr = vpu->inst_addr; - out.base.param_id = id; - switch (id) { - case VENC_SET_PARAM_ENC: - if (is_ext) { - out.base.data_item = 3; - out.base.data[0] = - venc_enc_param_crop_right(vpu, enc_param); - out.base.data[1] = - venc_enc_param_crop_bottom(enc_param); - out.base.data[2] = venc_enc_param_num_mb(enc_param); - } else { - out.base.data_item = 0; - } - break; - case VENC_SET_PARAM_FORCE_INTRA: - out.base.data_item = 0; - break; - case VENC_SET_PARAM_ADJUST_BITRATE: - out.base.data_item = 1; - out.base.data[0] = enc_param->bitrate; - break; - case VENC_SET_PARAM_ADJUST_FRAMERATE: - out.base.data_item = 1; - out.base.data[0] = enc_param->frm_rate; - break; - case VENC_SET_PARAM_GOP_SIZE: - out.base.data_item = 1; - out.base.data[0] = enc_param->gop_size; - break; - case VENC_SET_PARAM_INTRA_PERIOD: - out.base.data_item = 1; - out.base.data[0] = enc_param->intra_period; - break; - case VENC_SET_PARAM_SKIP_FRAME: - out.base.data_item = 0; - break; - default: - mtk_vcodec_err(vpu, "id %d not supported", id); - return -EINVAL; - } - if (vpu_enc_send_msg(vpu, &out, msg_size)) { - mtk_vcodec_err(vpu, - "AP_IPIMSG_ENC_SET_PARAM %d fail", id); - return -EINVAL; - } - - mtk_vcodec_debug(vpu, "id %d <-", id); - - return 0; -} - -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, - struct venc_frame_info *frame_info) -{ - const bool is_ext = MTK_ENC_CTX_IS_EXT(vpu->ctx); - size_t msg_size = is_ext ? - sizeof(struct venc_ap_ipi_msg_enc_ext) : - sizeof(struct venc_ap_ipi_msg_enc); - struct venc_ap_ipi_msg_enc_ext out; - - mtk_vcodec_debug(vpu, "bs_mode %d ->", bs_mode); - - memset(&out, 0, sizeof(out)); - out.base.msg_id = AP_IPIMSG_ENC_ENCODE; - out.base.vpu_inst_addr = vpu->inst_addr; - out.base.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.base.input_addr[0] = frm_buf->fb_addr[0].dma_addr; - out.base.input_addr[1] = frm_buf->fb_addr[1].dma_addr; - out.base.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.base.bs_addr = bs_buf->dma_addr; - out.base.bs_size = bs_buf->size; - } - if (is_ext && frame_info) { - out.data_item = 3; - out.data[0] = frame_info->frm_count; - out.data[1] = frame_info->skip_frm_count; - out.data[2] = frame_info->frm_type; - } - if (vpu_enc_send_msg(vpu, &out, msg_size)) { - 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; -} - -int vpu_enc_deinit(struct venc_vpu_inst *vpu) -{ - struct venc_ap_ipi_msg_deinit out; - - mtk_vcodec_debug_enter(vpu); - - memset(&out, 0, sizeof(out)); - out.msg_id = AP_IPIMSG_ENC_DEINIT; - out.vpu_inst_addr = vpu->inst_addr; - if (vpu_enc_send_msg(vpu, &out, sizeof(out))) { - mtk_vcodec_err(vpu, "AP_IPIMSG_ENC_DEINIT fail"); - return -EINVAL; - } - - mtk_vcodec_debug_leave(vpu); - - return 0; -} diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mtk-vcodec/venc_vpu_if.h deleted file mode 100644 index f83bc1b3f2bf..000000000000 --- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (c) 2016 MediaTek Inc. - * Author: PoChun Lin - */ - -#ifndef _VENC_VPU_IF_H_ -#define _VENC_VPU_IF_H_ - -#include "mtk_vcodec_fw.h" -#include "venc_drv_if.h" - -/* - * struct venc_vpu_inst - encoder VPU driver instance - * @wq_hd: wait queue used for vpu cmd trigger then wait vpu interrupt done - * @signaled: flag used for checking vpu interrupt done - * @failure: flag to show vpu cmd succeeds or not - * @state: enum venc_ipi_msg_enc_state - * @bs_size: bitstream size for skip frame case usage - * @is_key_frm: key frame flag - * @inst_addr: VPU instance addr - * @vsi: driver structure allocated by VPU side and shared to AP side for - * control and info share - * @id: the id of inter-processor interrupt - * @ctx: context for v4l2 layer integration - * @dev: device for v4l2 layer integration - */ -struct venc_vpu_inst { - wait_queue_head_t wq_hd; - int signaled; - int failure; - int state; - int bs_size; - int is_key_frm; - unsigned int inst_addr; - void *vsi; - int id; - struct mtk_vcodec_ctx *ctx; -}; - -int vpu_enc_init(struct venc_vpu_inst *vpu); -int vpu_enc_set_param(struct venc_vpu_inst *vpu, - enum venc_set_param_type id, - struct venc_enc_param *param); -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, - struct venc_frame_info *frame_info); -int vpu_enc_deinit(struct venc_vpu_inst *vpu); - -#endif -- cgit v1.2.3