diff options
author | Maxime Ripard <mripard@kernel.org> | 2023-07-11 10:23:20 +0300 |
---|---|---|
committer | Maxime Ripard <mripard@kernel.org> | 2023-07-11 10:23:20 +0300 |
commit | 2f98e686ef59b5d19af5847d755798e2031bee3a (patch) | |
tree | 7b47919242853d088decf898ca79d6cda0d49381 /drivers/media/platform/mediatek/vcodec | |
parent | a2848d08742c8e8494675892c02c0d22acbe3cf8 (diff) | |
parent | 06c2afb862f9da8dc5efa4b6076a0e48c3fbaaa5 (diff) | |
download | linux-2f98e686ef59b5d19af5847d755798e2031bee3a.tar.xz |
Merge v6.5-rc1 into drm-misc-fixes
Boris needs 6.5-rc1 in drm-misc-fixes to prevent a conflict.
Signed-off-by: Maxime Ripard <mripard@kernel.org>
Diffstat (limited to 'drivers/media/platform/mediatek/vcodec')
19 files changed, 3874 insertions, 100 deletions
diff --git a/drivers/media/platform/mediatek/vcodec/Makefile b/drivers/media/platform/mediatek/vcodec/Makefile index 93e7a343b5b0..5f4c30fec85a 100644 --- a/drivers/media/platform/mediatek/vcodec/Makefile +++ b/drivers/media/platform/mediatek/vcodec/Makefile @@ -10,9 +10,11 @@ mtk-vcodec-dec-y := vdec/vdec_h264_if.o \ vdec/vdec_vp8_req_if.o \ vdec/vdec_vp9_if.o \ vdec/vdec_vp9_req_lat_if.o \ + vdec/vdec_av1_req_lat_if.o \ vdec/vdec_h264_req_if.o \ vdec/vdec_h264_req_common.o \ vdec/vdec_h264_req_multi_if.o \ + vdec/vdec_hevc_req_multi_if.o \ mtk_vcodec_dec_drv.o \ vdec_drv_if.o \ vdec_vpu_if.o \ @@ -44,3 +46,9 @@ endif ifneq ($(CONFIG_VIDEO_MEDIATEK_VCODEC_SCP),) mtk-vcodec-common-y += mtk_vcodec_fw_scp.o endif + +ifneq ($(CONFIG_DEBUG_FS),) +obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dbgfs.o + +mtk-vcodec-dbgfs-y := mtk_vcodec_dbgfs.o +endif
\ No newline at end of file diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.c new file mode 100644 index 000000000000..b5cdbbfcc388 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Yunfei Dong <yunfei.dong@mediatek.com> + */ + +#include <linux/debugfs.h> + +#include "mtk_vcodec_dbgfs.h" +#include "mtk_vcodec_drv.h" +#include "mtk_vcodec_util.h" + +static void mtk_vdec_dbgfs_get_format_type(struct mtk_vcodec_ctx *ctx, char *buf, + int *used, int total) +{ + int curr_len; + + switch (ctx->current_codec) { + case V4L2_PIX_FMT_H264_SLICE: + curr_len = snprintf(buf + *used, total - *used, + "\toutput format: h264 slice\n"); + break; + case V4L2_PIX_FMT_VP8_FRAME: + curr_len = snprintf(buf + *used, total - *used, + "\toutput format: vp8 slice\n"); + break; + case V4L2_PIX_FMT_VP9_FRAME: + curr_len = snprintf(buf + *used, total - *used, + "\toutput format: vp9 slice\n"); + break; + default: + curr_len = snprintf(buf + *used, total - *used, + "\tunsupported output format: 0x%x\n", + ctx->current_codec); + } + *used += curr_len; + + switch (ctx->capture_fourcc) { + case V4L2_PIX_FMT_MM21: + curr_len = snprintf(buf + *used, total - *used, + "\tcapture format: MM21\n"); + break; + case V4L2_PIX_FMT_MT21C: + curr_len = snprintf(buf + *used, total - *used, + "\tcapture format: MT21C\n"); + break; + default: + curr_len = snprintf(buf + *used, total - *used, + "\tunsupported capture format: 0x%x\n", + ctx->capture_fourcc); + } + *used += curr_len; +} + +static void mtk_vdec_dbgfs_get_help(char *buf, int *used, int total) +{ + int curr_len; + + curr_len = snprintf(buf + *used, total - *used, + "help: (1: echo -'info' > vdec 2: cat vdec)\n"); + *used += curr_len; + + curr_len = snprintf(buf + *used, total - *used, + "\t-picinfo: get resolution\n"); + *used += curr_len; + + curr_len = snprintf(buf + *used, total - *used, + "\t-format: get output & capture queue format\n"); + *used += curr_len; +} + +static ssize_t mtk_vdec_dbgfs_write(struct file *filp, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mtk_vcodec_dev *vcodec_dev = filp->private_data; + struct mtk_vcodec_dbgfs *dbgfs = &vcodec_dev->dbgfs; + + mutex_lock(&dbgfs->dbgfs_lock); + dbgfs->buf_size = simple_write_to_buffer(dbgfs->dbgfs_buf, sizeof(dbgfs->dbgfs_buf), + ppos, ubuf, count); + mutex_unlock(&dbgfs->dbgfs_lock); + if (dbgfs->buf_size > 0) + return count; + + return dbgfs->buf_size; +} + +static ssize_t mtk_vdec_dbgfs_read(struct file *filp, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mtk_vcodec_dev *vcodec_dev = filp->private_data; + struct mtk_vcodec_dbgfs *dbgfs = &vcodec_dev->dbgfs; + struct mtk_vcodec_dbgfs_inst *dbgfs_inst; + struct mtk_vcodec_ctx *ctx; + int total_len = 200 * (dbgfs->inst_count == 0 ? 1 : dbgfs->inst_count); + int used_len = 0, curr_len, ret; + bool dbgfs_index[MTK_VDEC_DBGFS_MAX] = {0}; + char *buf = kmalloc(total_len, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + if (strstr(dbgfs->dbgfs_buf, "-help") || dbgfs->buf_size == 1) { + mtk_vdec_dbgfs_get_help(buf, &used_len, total_len); + goto read_buffer; + } + + if (strstr(dbgfs->dbgfs_buf, "-picinfo")) + dbgfs_index[MTK_VDEC_DBGFS_PICINFO] = true; + + if (strstr(dbgfs->dbgfs_buf, "-format")) + dbgfs_index[MTK_VDEC_DBGFS_FORMAT] = true; + + mutex_lock(&dbgfs->dbgfs_lock); + list_for_each_entry(dbgfs_inst, &dbgfs->dbgfs_head, node) { + ctx = dbgfs_inst->vcodec_ctx; + + curr_len = snprintf(buf + used_len, total_len - used_len, + "inst[%d]:\n ", ctx->id); + used_len += curr_len; + + if (dbgfs_index[MTK_VDEC_DBGFS_PICINFO]) { + curr_len = snprintf(buf + used_len, total_len - used_len, + "\treal(%dx%d)=>align(%dx%d)\n", + ctx->picinfo.pic_w, ctx->picinfo.pic_h, + ctx->picinfo.buf_w, ctx->picinfo.buf_h); + used_len += curr_len; + } + + if (dbgfs_index[MTK_VDEC_DBGFS_FORMAT]) + mtk_vdec_dbgfs_get_format_type(ctx, buf, &used_len, total_len); + } + mutex_unlock(&dbgfs->dbgfs_lock); +read_buffer: + ret = simple_read_from_buffer(ubuf, count, ppos, buf, used_len); + kfree(buf); + return ret; +} + +static const struct file_operations vdec_fops = { + .open = simple_open, + .write = mtk_vdec_dbgfs_write, + .read = mtk_vdec_dbgfs_read, +}; + +void mtk_vcodec_dbgfs_create(struct mtk_vcodec_ctx *ctx) +{ + struct mtk_vcodec_dbgfs_inst *dbgfs_inst; + struct mtk_vcodec_dev *vcodec_dev = ctx->dev; + + dbgfs_inst = kzalloc(sizeof(*dbgfs_inst), GFP_KERNEL); + if (!dbgfs_inst) + return; + + list_add_tail(&dbgfs_inst->node, &vcodec_dev->dbgfs.dbgfs_head); + + vcodec_dev->dbgfs.inst_count++; + + dbgfs_inst->inst_id = ctx->id; + dbgfs_inst->vcodec_ctx = ctx; +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_create); + +void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dev *vcodec_dev, int ctx_id) +{ + struct mtk_vcodec_dbgfs_inst *dbgfs_inst; + + list_for_each_entry(dbgfs_inst, &vcodec_dev->dbgfs.dbgfs_head, node) { + if (dbgfs_inst->inst_id == ctx_id) { + vcodec_dev->dbgfs.inst_count--; + break; + } + } + + if (dbgfs_inst) { + list_del(&dbgfs_inst->node); + kfree(dbgfs_inst); + } +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_remove); + +void mtk_vcodec_dbgfs_init(struct mtk_vcodec_dev *vcodec_dev, bool is_encode) +{ + struct dentry *vcodec_root; + + if (is_encode) + vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-enc", NULL); + else + vcodec_dev->dbgfs.vcodec_root = debugfs_create_dir("vcodec-dec", NULL); + if (IS_ERR(vcodec_dev->dbgfs.vcodec_root)) + dev_err(&vcodec_dev->plat_dev->dev, "create vcodec dir err:%d\n", + IS_ERR(vcodec_dev->dbgfs.vcodec_root)); + + vcodec_root = vcodec_dev->dbgfs.vcodec_root; + debugfs_create_x32("mtk_v4l2_dbg_level", 0644, vcodec_root, &mtk_v4l2_dbg_level); + debugfs_create_x32("mtk_vcodec_dbg", 0644, vcodec_root, &mtk_vcodec_dbg); + + vcodec_dev->dbgfs.inst_count = 0; + if (is_encode) + return; + + INIT_LIST_HEAD(&vcodec_dev->dbgfs.dbgfs_head); + debugfs_create_file("vdec", 0200, vcodec_root, vcodec_dev, &vdec_fops); + mutex_init(&vcodec_dev->dbgfs.dbgfs_lock); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_init); + +void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dev *vcodec_dev) +{ + debugfs_remove_recursive(vcodec_dev->dbgfs.vcodec_root); +} +EXPORT_SYMBOL_GPL(mtk_vcodec_dbgfs_deinit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Mediatek video codec driver"); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.h new file mode 100644 index 000000000000..241ff8197e73 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dbgfs.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Yunfei Dong <yunfei.dong@mediatek.com> + */ + +#ifndef __MTK_VCODEC_DBGFS_H__ +#define __MTK_VCODEC_DBGFS_H__ + +struct mtk_vcodec_dev; +struct mtk_vcodec_ctx; + +/* + * enum mtk_vdec_dbgfs_log_index - used to get different debug information + */ +enum mtk_vdec_dbgfs_log_index { + MTK_VDEC_DBGFS_PICINFO, + MTK_VDEC_DBGFS_FORMAT, + MTK_VDEC_DBGFS_MAX, +}; + +/** + * struct mtk_vcodec_dbgfs_inst - debugfs information for each inst + * @node: list node for each inst + * @vcodec_ctx: struct mtk_vcodec_ctx + * @inst_id: index of the context that the same with ctx->id + */ +struct mtk_vcodec_dbgfs_inst { + struct list_head node; + struct mtk_vcodec_ctx *vcodec_ctx; + int inst_id; +}; + +/** + * struct mtk_vcodec_dbgfs - dbgfs information + * @dbgfs_head: list head used to link each instance + * @vcodec_root: vcodec dbgfs entry + * @dbgfs_lock: dbgfs lock used to protect dbgfs_buf + * @dbgfs_buf: dbgfs buf used to store dbgfs cmd + * @buf_size: buffer size of dbgfs + * @inst_count: the count of total instance + */ +struct mtk_vcodec_dbgfs { + struct list_head dbgfs_head; + struct dentry *vcodec_root; + struct mutex dbgfs_lock; + char dbgfs_buf[1024]; + int buf_size; + int inst_count; +}; + +#if defined(CONFIG_DEBUG_FS) +void mtk_vcodec_dbgfs_create(struct mtk_vcodec_ctx *ctx); +void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dev *vcodec_dev, int ctx_id); +void mtk_vcodec_dbgfs_init(struct mtk_vcodec_dev *vcodec_dev, bool is_encode); +void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dev *vcodec_dev); +#else +static inline void mtk_vcodec_dbgfs_create(struct mtk_vcodec_ctx *ctx) +{ +} + +static inline void mtk_vcodec_dbgfs_remove(struct mtk_vcodec_dev *vcodec_dev, int ctx_id) +{ +} + +static inline void mtk_vcodec_dbgfs_init(struct mtk_vcodec_dev *vcodec_dev, bool is_encode) +{ +} + +static inline void mtk_vcodec_dbgfs_deinit(struct mtk_vcodec_dev *vcodec_dev) +{ +} +#endif +#endif diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c index 9c652beb3f19..d41f2121b94f 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c @@ -215,6 +215,7 @@ static int fops_vcodec_open(struct file *file) ctx->dev->vdec_pdata->init_vdec_params(ctx); list_add(&ctx->list, &dev->ctx_list); + mtk_vcodec_dbgfs_create(ctx); mutex_unlock(&dev->dev_mutex); mtk_v4l2_debug(0, "%s decoder [%d]", dev_name(&dev->plat_dev->dev), @@ -256,6 +257,7 @@ static int fops_vcodec_release(struct file *file) v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->ctrl_hdl); + mtk_vcodec_dbgfs_remove(dev, ctx->id); list_del_init(&ctx->list); kfree(ctx); mutex_unlock(&dev->dev_mutex); @@ -310,7 +312,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev) } 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); @@ -423,6 +424,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) mtk_v4l2_debug(0, "media registered as /dev/media%d", vfd_dec->minor); } + mtk_vcodec_dbgfs_init(dev, false); mtk_v4l2_debug(0, "decoder registered as /dev/video%d", vfd_dec->minor); return 0; @@ -498,6 +500,7 @@ static void mtk_vcodec_dec_remove(struct platform_device *pdev) if (dev->vfd_dec) video_unregister_device(dev->vfd_dec); + mtk_vcodec_dbgfs_deinit(dev); v4l2_device_unregister(&dev->v4l2_dev); if (!dev->vdec_pdata->is_subdev_supported) pm_runtime_disable(dev->pm.dev); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c index b753bf54ebd9..e1cb2f8dca33 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c @@ -148,20 +148,21 @@ static int mtk_vdec_hw_probe(struct platform_device *pdev) ret = mtk_vcodec_init_dec_clk(pdev, &subdev_dev->pm); if (ret) return ret; - pm_runtime_enable(&pdev->dev); + + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; 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; + return -EINVAL; } 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; + return -EINVAL; } main_dev->subdev_dev[hw_idx] = subdev_dev; @@ -173,14 +174,14 @@ static int mtk_vdec_hw_probe(struct platform_device *pdev) if (IS_SUPPORT_VDEC_HW_IRQ(hw_idx)) { ret = mtk_vdec_hw_init_irq(subdev_dev); if (ret) - goto err; + return ret; } 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; + return ret; } if (!main_dev->subdev_prob_done) @@ -188,21 +189,10 @@ static int mtk_vdec_hw_probe(struct platform_device *pdev) platform_set_drvdata(pdev, subdev_dev); return 0; -err: - pm_runtime_disable(subdev_dev->pm.dev); - return ret; -} - -static int mtk_vdec_hw_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - - return 0; } static struct platform_driver mtk_vdec_driver = { .probe = mtk_vdec_hw_probe, - .remove = mtk_vdec_hw_remove, .driver = { .name = "mtk-vdec-comp", .of_match_table = mtk_vdec_hw_match, diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c index 3000db975e5f..db1e14a1bd6c 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c @@ -107,11 +107,103 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = { }, .codec_type = V4L2_PIX_FMT_VP9_FRAME, }, + { + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_SPS, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_PPS, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE, + .def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10, + .menu_skip_mask = + BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE), + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_DECODE_MODE, + .min = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .def = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + .max = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_HEVC_START_CODE, + .min = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .def = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + .max = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B, + }, + .codec_type = V4L2_PIX_FMT_HEVC_SLICE, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_AV1_SEQUENCE, + + }, + .codec_type = V4L2_PIX_FMT_AV1_FRAME, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_AV1_FRAME, + + }, + .codec_type = V4L2_PIX_FMT_AV1_FRAME, + }, + { + .cfg = { + .id = V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY, + .dims = { V4L2_AV1_MAX_TILE_COUNT }, + + }, + .codec_type = V4L2_PIX_FMT_AV1_FRAME, + }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_AV1_PROFILE, + .min = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN, + .def = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN, + .max = V4L2_MPEG_VIDEO_AV1_PROFILE_MAIN, + }, + .codec_type = V4L2_PIX_FMT_AV1_FRAME, + }, + { + .cfg = { + .id = V4L2_CID_MPEG_VIDEO_AV1_LEVEL, + .min = V4L2_MPEG_VIDEO_AV1_LEVEL_2_0, + .def = V4L2_MPEG_VIDEO_AV1_LEVEL_4_0, + .max = V4L2_MPEG_VIDEO_AV1_LEVEL_5_1, + }, + .codec_type = V4L2_PIX_FMT_AV1_FRAME, + }, }; #define NUM_CTRLS ARRAY_SIZE(mtk_stateless_controls) -static struct mtk_video_fmt mtk_video_formats[5]; +static struct mtk_video_fmt mtk_video_formats[7]; static struct mtk_video_fmt default_out_format; static struct mtk_video_fmt default_cap_format; @@ -240,7 +332,7 @@ static void mtk_vdec_worker(struct work_struct *work) mtk_v4l2_err("vb2 buffer media request is NULL"); ret = vdec_if_decode(ctx, bs_src, NULL, &res_chg); - if (ret) { + if (ret && ret != -EAGAIN) { 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); @@ -356,6 +448,8 @@ static void mtk_vcodec_add_formats(unsigned int fourcc, case V4L2_PIX_FMT_H264_SLICE: case V4L2_PIX_FMT_VP8_FRAME: case V4L2_PIX_FMT_VP9_FRAME: + case V4L2_PIX_FMT_HEVC_SLICE: + case V4L2_PIX_FMT_AV1_FRAME: mtk_video_formats[count_formats].fourcc = fourcc; mtk_video_formats[count_formats].type = MTK_FMT_DEC; mtk_video_formats[count_formats].num_planes = 1; @@ -412,6 +506,14 @@ static void mtk_vcodec_get_supported_formats(struct mtk_vcodec_ctx *ctx) mtk_vcodec_add_formats(V4L2_PIX_FMT_VP9_FRAME, ctx); out_format_count++; } + if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_HEVC_FRAME) { + mtk_vcodec_add_formats(V4L2_PIX_FMT_HEVC_SLICE, ctx); + out_format_count++; + } + if (ctx->dev->dec_capability & MTK_VDEC_FORMAT_AV1_FRAME) { + mtk_vcodec_add_formats(V4L2_PIX_FMT_AV1_FRAME, ctx); + out_format_count++; + } if (cap_format_count) default_cap_format = mtk_video_formats[cap_format_count - 1]; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h index 9acab54fd650..f17d67e781c9 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h @@ -16,6 +16,7 @@ #include <media/v4l2-mem2mem.h> #include <media/videobuf2-core.h> +#include "mtk_vcodec_dbgfs.h" #include "mtk_vcodec_util.h" #include "vdec_msg_queue.h" @@ -347,6 +348,8 @@ enum mtk_vdec_format_types { MTK_VDEC_FORMAT_H264_SLICE = 0x100, MTK_VDEC_FORMAT_VP8_FRAME = 0x200, MTK_VDEC_FORMAT_VP9_FRAME = 0x400, + MTK_VDEC_FORMAT_AV1_FRAME = 0x800, + MTK_VDEC_FORMAT_HEVC_FRAME = 0x1000, MTK_VCODEC_INNER_RACING = 0x20000, }; @@ -461,7 +464,6 @@ struct mtk_vcodec_enc_pdata { * @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 @@ -470,6 +472,7 @@ struct mtk_vcodec_enc_pdata { * @dec_active_cnt: used to mark whether need to record register value * @vdec_racing_info: record register value * @dec_racing_info_mutex: mutex lock used for inner racing mode + * @dbgfs: debug log related information */ struct mtk_vcodec_dev { struct v4l2_device v4l2_dev; @@ -510,7 +513,6 @@ struct mtk_vcodec_dev { 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); @@ -520,6 +522,8 @@ struct mtk_vcodec_dev { u32 vdec_racing_info[132]; /* Protects access to vdec_racing_info data */ struct mutex dec_racing_info_mutex; + + struct mtk_vcodec_dbgfs dbgfs; }; static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh) diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c index db65e77bd373..9ff439a50f53 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c @@ -505,13 +505,13 @@ static int vidioc_venc_s_fmt_out(struct file *file, void *priv, f->fmt.pix.pixelformat = fmt->fourcc; } - q_data->visible_width = f->fmt.pix_mp.width; - q_data->visible_height = f->fmt.pix_mp.height; - q_data->fmt = fmt; - ret = vidioc_try_fmt_out(ctx, f, q_data->fmt); + 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; diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c index 168004a08888..5df0a22ff3b5 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c @@ -358,6 +358,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev) goto err_enc_reg; } + mtk_vcodec_dbgfs_init(dev, true); mtk_v4l2_debug(0, "encoder %d registered as /dev/video%d", dev->venc_pdata->core_id, vfd_enc->num); @@ -468,6 +469,7 @@ static void mtk_vcodec_enc_remove(struct platform_device *pdev) if (dev->vfd_enc) video_unregister_device(dev->vfd_enc); + mtk_vcodec_dbgfs_deinit(dev); v4l2_device_unregister(&dev->v4l2_dev); pm_runtime_disable(dev->pm.dev); mtk_vcodec_fw_release(dev->fw_handler); diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c index ace78c4b5b9e..f214e6f67005 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c @@ -13,6 +13,14 @@ #include "mtk_vcodec_drv.h" #include "mtk_vcodec_util.h" +#if defined(CONFIG_DEBUG_FS) +int mtk_vcodec_dbg; +EXPORT_SYMBOL(mtk_vcodec_dbg); + +int mtk_v4l2_dbg_level; +EXPORT_SYMBOL(mtk_v4l2_dbg_level); +#endif + void __iomem *mtk_vcodec_get_reg_addr(struct mtk_vcodec_ctx *data, unsigned int reg_idx) { diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h index 71956627a0e2..88d389b65f13 100644 --- a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h +++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h @@ -35,15 +35,35 @@ struct mtk_vcodec_dev; pr_err("[MTK_VCODEC][ERROR][%d]: " fmt "\n", \ ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) +#if defined(CONFIG_DEBUG_FS) +extern int mtk_v4l2_dbg_level; +extern int mtk_vcodec_dbg; -#define mtk_v4l2_debug(level, fmt, args...) pr_debug(fmt, ##args) +#define mtk_v4l2_debug(level, fmt, args...) \ + do { \ + if (mtk_v4l2_dbg_level >= (level)) \ + pr_debug("[MTK_V4L2] %s, %d: " fmt "\n", \ + __func__, __LINE__, ##args); \ + } while (0) -#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...) \ + do { \ + if (mtk_vcodec_dbg) \ + dev_dbg(&(((struct mtk_vcodec_ctx *)(h)->ctx)->dev->plat_dev->dev), \ + "[MTK_VCODEC][%d]: %s, %d " fmt "\n", \ + ((struct mtk_vcodec_ctx *)(h)->ctx)->id, \ + __func__, __LINE__, ##args); \ + } while (0) +#else +#define mtk_v4l2_debug(level, fmt, args...) pr_debug(fmt, ##args) #define mtk_vcodec_debug(h, fmt, args...) \ pr_debug("[MTK_VCODEC][%d]: " fmt "\n", \ ((struct mtk_vcodec_ctx *)(h)->ctx)->id, ##args) +#endif + +#define mtk_v4l2_debug_enter() mtk_v4l2_debug(3, "+") +#define mtk_v4l2_debug_leave() mtk_v4l2_debug(3, "-") #define mtk_vcodec_debug_enter(h) mtk_vcodec_debug(h, "+") #define mtk_vcodec_debug_leave(h) mtk_vcodec_debug(h, "-") diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_av1_req_lat_if.c new file mode 100644 index 000000000000..404a1a23fd40 --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_av1_req_lat_if.c @@ -0,0 +1,2207 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Xiaoyong Lu <xiaoyong.lu@mediatek.com> + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <media/videobuf2-dma-contig.h> + +#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 AV1_MAX_FRAME_BUF_COUNT (V4L2_AV1_TOTAL_REFS_PER_FRAME + 1) +#define AV1_TILE_BUF_SIZE 64 +#define AV1_SCALE_SUBPEL_BITS 10 +#define AV1_REF_SCALE_SHIFT 14 +#define AV1_REF_NO_SCALE BIT(AV1_REF_SCALE_SHIFT) +#define AV1_REF_INVALID_SCALE -1 +#define AV1_CDF_TABLE_BUFFER_SIZE 16384 +#define AV1_PRIMARY_REF_NONE 7 + +#define AV1_INVALID_IDX -1 + +#define AV1_DIV_ROUND_UP_POW2(value, n) \ +({ \ + typeof(n) _n = n; \ + typeof(value) _value = value; \ + (_value + (BIT(_n) >> 1)) >> _n; \ +}) + +#define AV1_DIV_ROUND_UP_POW2_SIGNED(value, n) \ +({ \ + typeof(n) _n_ = n; \ + typeof(value) _value_ = value; \ + (((_value_) < 0) ? -AV1_DIV_ROUND_UP_POW2(-(_value_), (_n_)) \ + : AV1_DIV_ROUND_UP_POW2((_value_), (_n_))); \ +}) + +#define BIT_FLAG(x, bit) (!!((x)->flags & (bit))) +#define SEGMENTATION_FLAG(x, name) (!!((x)->flags & V4L2_AV1_SEGMENTATION_FLAG_##name)) +#define QUANT_FLAG(x, name) (!!((x)->flags & V4L2_AV1_QUANTIZATION_FLAG_##name)) +#define SEQUENCE_FLAG(x, name) (!!((x)->flags & V4L2_AV1_SEQUENCE_FLAG_##name)) +#define FH_FLAG(x, name) (!!((x)->flags & V4L2_AV1_FRAME_FLAG_##name)) + +#define MINQ 0 +#define MAXQ 255 + +#define DIV_LUT_PREC_BITS 14 +#define DIV_LUT_BITS 8 +#define DIV_LUT_NUM BIT(DIV_LUT_BITS) +#define WARP_PARAM_REDUCE_BITS 6 +#define WARPEDMODEL_PREC_BITS 16 + +#define SEG_LVL_ALT_Q 0 +#define SECONDARY_FILTER_STRENGTH_NUM_BITS 2 + +static const short div_lut[DIV_LUT_NUM + 1] = { + 16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768, + 15709, 15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142, + 15087, 15033, 14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564, + 14513, 14463, 14413, 14364, 14315, 14266, 14218, 14170, 14122, 14075, 14028, + 13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662, 13618, 13574, 13530, + 13487, 13443, 13400, 13358, 13315, 13273, 13231, 13190, 13148, 13107, 13066, + 13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633, + 12596, 12558, 12520, 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228, + 12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848, + 11815, 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555, 11523, 11491, + 11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185, 11155, + 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838, + 10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538, + 10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255, + 10230, 10205, 10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986, + 9963, 9939, 9916, 9892, 9869, 9846, 9823, 9800, 9777, 9754, 9732, + 9709, 9687, 9664, 9642, 9620, 9598, 9576, 9554, 9533, 9511, 9489, + 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300, 9279, 9259, + 9239, 9218, 9198, 9178, 9158, 9138, 9118, 9098, 9079, 9059, 9039, + 9020, 9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867, 8849, 8830, + 8812, 8793, 8775, 8756, 8738, 8720, 8702, 8684, 8666, 8648, 8630, + 8613, 8595, 8577, 8560, 8542, 8525, 8508, 8490, 8473, 8456, 8439, + 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306, 8289, 8273, 8257, + 8240, 8224, 8208, 8192, +}; + +/** + * struct vdec_av1_slice_init_vsi - VSI used to initialize instance + * @architecture: architecture type + * @reserved: reserved + * @core_vsi: for core vsi + * @cdf_table_addr: cdf table addr + * @cdf_table_size: cdf table size + * @iq_table_addr: iq table addr + * @iq_table_size: iq table size + * @vsi_size: share vsi structure size + */ +struct vdec_av1_slice_init_vsi { + u32 architecture; + u32 reserved; + u64 core_vsi; + u64 cdf_table_addr; + u32 cdf_table_size; + u64 iq_table_addr; + u32 iq_table_size; + u32 vsi_size; +}; + +/** + * struct vdec_av1_slice_mem - memory address and size + * @buf: dma_addr padding + * @dma_addr: buffer address + * @size: buffer size + * @dma_addr_end: buffer end address + * @padding: for padding + */ +struct vdec_av1_slice_mem { + union { + u64 buf; + dma_addr_t dma_addr; + }; + union { + size_t size; + dma_addr_t dma_addr_end; + u64 padding; + }; +}; + +/** + * struct vdec_av1_slice_state - decoding state + * @err : err type for decode + * @full : transcoded buffer is full or not + * @timeout : decode timeout or not + * @perf : performance enable + * @crc : hw checksum + * @out_size : hw output size + */ +struct vdec_av1_slice_state { + int err; + u32 full; + u32 timeout; + u32 perf; + u32 crc[16]; + u32 out_size; +}; + +/* + * enum vdec_av1_slice_resolution_level - resolution level + */ +enum vdec_av1_slice_resolution_level { + AV1_RES_NONE, + AV1_RES_FHD, + AV1_RES_4K, + AV1_RES_8K, +}; + +/* + * enum vdec_av1_slice_frame_type - av1 frame type + */ +enum vdec_av1_slice_frame_type { + AV1_KEY_FRAME = 0, + AV1_INTER_FRAME, + AV1_INTRA_ONLY_FRAME, + AV1_SWITCH_FRAME, + AV1_FRAME_TYPES, +}; + +/* + * enum vdec_av1_slice_reference_mode - reference mode type + */ +enum vdec_av1_slice_reference_mode { + AV1_SINGLE_REFERENCE = 0, + AV1_COMPOUND_REFERENCE, + AV1_REFERENCE_MODE_SELECT, + AV1_REFERENCE_MODES, +}; + +/** + * struct vdec_av1_slice_tile_group - info for each tile + * @num_tiles: tile number + * @tile_size: input size for each tile + * @tile_start_offset: tile offset to input buffer + */ +struct vdec_av1_slice_tile_group { + u32 num_tiles; + u32 tile_size[V4L2_AV1_MAX_TILE_COUNT]; + u32 tile_start_offset[V4L2_AV1_MAX_TILE_COUNT]; +}; + +/** + * struct vdec_av1_slice_scale_factors - scale info for each ref frame + * @is_scaled: frame is scaled or not + * @x_scale: frame width scale coefficient + * @y_scale: frame height scale coefficient + * @x_step: width step for x_scale + * @y_step: height step for y_scale + */ +struct vdec_av1_slice_scale_factors { + u8 is_scaled; + int x_scale; + int y_scale; + int x_step; + int y_step; +}; + +/** + * struct vdec_av1_slice_frame_refs - ref frame info + * @ref_fb_idx: ref slot index + * @ref_map_idx: ref frame index + * @scale_factors: scale factors for each ref frame + */ +struct vdec_av1_slice_frame_refs { + int ref_fb_idx; + int ref_map_idx; + struct vdec_av1_slice_scale_factors scale_factors; +}; + +/** + * struct vdec_av1_slice_gm - AV1 Global Motion parameters + * @wmtype: The type of global motion transform used + * @wmmat: gm_params + * @alpha: alpha info + * @beta: beta info + * @gamma: gamma info + * @delta: delta info + * @invalid: is invalid or not + */ +struct vdec_av1_slice_gm { + int wmtype; + int wmmat[8]; + short alpha; + short beta; + short gamma; + short delta; + char invalid; +}; + +/** + * struct vdec_av1_slice_sm - AV1 Skip Mode parameters + * @skip_mode_allowed: Skip Mode is allowed or not + * @skip_mode_present: specified that the skip_mode will be present or not + * @skip_mode_frame: specifies the frames to use for compound prediction + */ +struct vdec_av1_slice_sm { + u8 skip_mode_allowed; + u8 skip_mode_present; + int skip_mode_frame[2]; +}; + +/** + * struct vdec_av1_slice_seg - AV1 Segmentation params + * @segmentation_enabled: this frame makes use of the segmentation tool or not + * @segmentation_update_map: segmentation map are updated during the decoding frame + * @segmentation_temporal_update:segmentation map are coded relative the existing segmentaion map + * @segmentation_update_data: new parameters are about to be specified for each segment + * @feature_data: specifies the feature data for a segment feature + * @feature_enabled_mask: the corresponding feature value is coded or not. + * @segid_preskip: segment id will be read before the skip syntax element. + * @last_active_segid: the highest numbered segment id that has some enabled feature + */ +struct vdec_av1_slice_seg { + u8 segmentation_enabled; + u8 segmentation_update_map; + u8 segmentation_temporal_update; + u8 segmentation_update_data; + int feature_data[V4L2_AV1_MAX_SEGMENTS][V4L2_AV1_SEG_LVL_MAX]; + u16 feature_enabled_mask[V4L2_AV1_MAX_SEGMENTS]; + int segid_preskip; + int last_active_segid; +}; + +/** + * struct vdec_av1_slice_delta_q_lf - AV1 Loop Filter delta parameters + * @delta_q_present: specified whether quantizer index delta values are present + * @delta_q_res: specifies the left shift which should be applied to decoded quantizer index + * @delta_lf_present: specifies whether loop filter delta values are present + * @delta_lf_res: specifies the left shift which should be applied to decoded + * loop filter delta values + * @delta_lf_multi: specifies that separate loop filter deltas are sent for horizontal + * luma edges,vertical luma edges,the u edges, and the v edges. + */ +struct vdec_av1_slice_delta_q_lf { + u8 delta_q_present; + u8 delta_q_res; + u8 delta_lf_present; + u8 delta_lf_res; + u8 delta_lf_multi; +}; + +/** + * struct vdec_av1_slice_quantization - AV1 Quantization params + * @base_q_idx: indicates the base frame qindex. This is used for Y AC + * coefficients and as the base value for the other quantizers. + * @qindex: qindex + * @delta_qydc: indicates the Y DC quantizer relative to base_q_idx + * @delta_qudc: indicates the U DC quantizer relative to base_q_idx. + * @delta_quac: indicates the U AC quantizer relative to base_q_idx + * @delta_qvdc: indicates the V DC quantizer relative to base_q_idx + * @delta_qvac: indicates the V AC quantizer relative to base_q_idx + * @using_qmatrix: specifies that the quantizer matrix will be used to + * compute quantizers + * @qm_y: specifies the level in the quantizer matrix that should + * be used for luma plane decoding + * @qm_u: specifies the level in the quantizer matrix that should + * be used for chroma U plane decoding. + * @qm_v: specifies the level in the quantizer matrix that should be + * used for chroma V plane decoding + */ +struct vdec_av1_slice_quantization { + int base_q_idx; + int qindex[V4L2_AV1_MAX_SEGMENTS]; + int delta_qydc; + int delta_qudc; + int delta_quac; + int delta_qvdc; + int delta_qvac; + u8 using_qmatrix; + u8 qm_y; + u8 qm_u; + u8 qm_v; +}; + +/** + * struct vdec_av1_slice_lr - AV1 Loop Restauration parameters + * @use_lr: whether to use loop restoration + * @use_chroma_lr: whether to use chroma loop restoration + * @frame_restoration_type: specifies the type of restoration used for each plane + * @loop_restoration_size: pecifies the size of loop restoration units in units + * of samples in the current plane + */ +struct vdec_av1_slice_lr { + u8 use_lr; + u8 use_chroma_lr; + u8 frame_restoration_type[V4L2_AV1_NUM_PLANES_MAX]; + u32 loop_restoration_size[V4L2_AV1_NUM_PLANES_MAX]; +}; + +/** + * struct vdec_av1_slice_loop_filter - AV1 Loop filter parameters + * @loop_filter_level: an array containing loop filter strength values. + * @loop_filter_ref_deltas: contains the adjustment needed for the filter + * level based on the chosen reference frame + * @loop_filter_mode_deltas: contains the adjustment needed for the filter + * level based on the chosen mode + * @loop_filter_sharpness: indicates the sharpness level. The loop_filter_level + * and loop_filter_sharpness together determine when + * a block edge is filtered, and by how much the + * filtering can change the sample values + * @loop_filter_delta_enabled: filetr level depends on the mode and reference + * frame used to predict a block + */ +struct vdec_av1_slice_loop_filter { + u8 loop_filter_level[4]; + int loop_filter_ref_deltas[V4L2_AV1_TOTAL_REFS_PER_FRAME]; + int loop_filter_mode_deltas[2]; + u8 loop_filter_sharpness; + u8 loop_filter_delta_enabled; +}; + +/** + * struct vdec_av1_slice_cdef - AV1 CDEF parameters + * @cdef_damping: controls the amount of damping in the deringing filter + * @cdef_y_strength: specifies the strength of the primary filter and secondary filter + * @cdef_uv_strength: specifies the strength of the primary filter and secondary filter + * @cdef_bits: specifies the number of bits needed to specify which + * CDEF filter to apply + */ +struct vdec_av1_slice_cdef { + u8 cdef_damping; + u8 cdef_y_strength[8]; + u8 cdef_uv_strength[8]; + u8 cdef_bits; +}; + +/** + * struct vdec_av1_slice_mfmv - AV1 mfmv parameters + * @mfmv_valid_ref: mfmv_valid_ref + * @mfmv_dir: mfmv_dir + * @mfmv_ref_to_cur: mfmv_ref_to_cur + * @mfmv_ref_frame_idx: mfmv_ref_frame_idx + * @mfmv_count: mfmv_count + */ +struct vdec_av1_slice_mfmv { + u32 mfmv_valid_ref[3]; + u32 mfmv_dir[3]; + int mfmv_ref_to_cur[3]; + int mfmv_ref_frame_idx[3]; + int mfmv_count; +}; + +/** + * struct vdec_av1_slice_tile - AV1 Tile info + * @tile_cols: specifies the number of tiles across the frame + * @tile_rows: pecifies the number of tiles down the frame + * @mi_col_starts: an array specifying the start column + * @mi_row_starts: an array specifying the start row + * @context_update_tile_id: specifies which tile to use for the CDF update + * @uniform_tile_spacing_flag: tiles are uniformly spaced across the frame + * or the tile sizes are coded + */ +struct vdec_av1_slice_tile { + u8 tile_cols; + u8 tile_rows; + int mi_col_starts[V4L2_AV1_MAX_TILE_COLS + 1]; + int mi_row_starts[V4L2_AV1_MAX_TILE_ROWS + 1]; + u8 context_update_tile_id; + u8 uniform_tile_spacing_flag; +}; + +/** + * struct vdec_av1_slice_uncompressed_header - Represents an AV1 Frame Header OBU + * @use_ref_frame_mvs: use_ref_frame_mvs flag + * @order_hint: specifies OrderHintBits least significant bits of the expected + * @gm: global motion param + * @upscaled_width: the upscaled width + * @frame_width: frame's width + * @frame_height: frame's height + * @reduced_tx_set: frame is restricted to a reduced subset of the full + * set of transform types + * @tx_mode: specifies how the transform size is determined + * @uniform_tile_spacing_flag: tiles are uniformly spaced across the frame + * or the tile sizes are coded + * @interpolation_filter: specifies the filter selection used for performing inter prediction + * @allow_warped_motion: motion_mode may be present or not + * @is_motion_mode_switchable : euqlt to 0 specifies that only the SIMPLE motion mode will be used + * @reference_mode : frame reference mode selected + * @allow_high_precision_mv: specifies that motion vectors are specified to + * quarter pel precision or to eighth pel precision + * @allow_intra_bc: ubducates that intra block copy may be used in this frame + * @force_integer_mv: specifies motion vectors will always be integers or + * can contain fractional bits + * @allow_screen_content_tools: intra blocks may use palette encoding + * @error_resilient_mode: error resislent mode is enable/disable + * @frame_type: specifies the AV1 frame type + * @primary_ref_frame: specifies which reference frame contains the CDF values + * and other state that should be loaded at the start of the frame + * slots will be updated with the current frame after it is decoded + * @disable_frame_end_update_cdf:indicates the end of frame CDF update is disable or enable + * @disable_cdf_update: specified whether the CDF update in the symbol + * decoding process should be disables + * @skip_mode: av1 skip mode parameters + * @seg: av1 segmentaon parameters + * @delta_q_lf: av1 delta loop fileter + * @quant: av1 Quantization params + * @lr: av1 Loop Restauration parameters + * @superres_denom: the denominator for the upscaling ratio + * @loop_filter: av1 Loop filter parameters + * @cdef: av1 CDEF parameters + * @mfmv: av1 mfmv parameters + * @tile: av1 Tile info + * @frame_is_intra: intra frame + * @loss_less_array: loss less array + * @coded_loss_less: coded lsss less + * @mi_rows: size of mi unit in rows + * @mi_cols: size of mi unit in cols + */ +struct vdec_av1_slice_uncompressed_header { + u8 use_ref_frame_mvs; + int order_hint; + struct vdec_av1_slice_gm gm[V4L2_AV1_TOTAL_REFS_PER_FRAME]; + u32 upscaled_width; + u32 frame_width; + u32 frame_height; + u8 reduced_tx_set; + u8 tx_mode; + u8 uniform_tile_spacing_flag; + u8 interpolation_filter; + u8 allow_warped_motion; + u8 is_motion_mode_switchable; + u8 reference_mode; + u8 allow_high_precision_mv; + u8 allow_intra_bc; + u8 force_integer_mv; + u8 allow_screen_content_tools; + u8 error_resilient_mode; + u8 frame_type; + u8 primary_ref_frame; + u8 disable_frame_end_update_cdf; + u32 disable_cdf_update; + struct vdec_av1_slice_sm skip_mode; + struct vdec_av1_slice_seg seg; + struct vdec_av1_slice_delta_q_lf delta_q_lf; + struct vdec_av1_slice_quantization quant; + struct vdec_av1_slice_lr lr; + u32 superres_denom; + struct vdec_av1_slice_loop_filter loop_filter; + struct vdec_av1_slice_cdef cdef; + struct vdec_av1_slice_mfmv mfmv; + struct vdec_av1_slice_tile tile; + u8 frame_is_intra; + u8 loss_less_array[V4L2_AV1_MAX_SEGMENTS]; + u8 coded_loss_less; + u32 mi_rows; + u32 mi_cols; +}; + +/** + * struct vdec_av1_slice_seq_header - Represents an AV1 Sequence OBU + * @bitdepth: the bitdepth to use for the sequence + * @enable_superres: specifies whether the use_superres syntax element may be present + * @enable_filter_intra: specifies the use_filter_intra syntax element may be present + * @enable_intra_edge_filter: whether the intra edge filtering process should be enabled + * @enable_interintra_compound: specifies the mode info fo rinter blocks may + * contain the syntax element interintra + * @enable_masked_compound: specifies the mode info fo rinter blocks may + * contain the syntax element compound_type + * @enable_dual_filter: the inter prediction filter type may be specified independently + * @enable_jnt_comp: distance weights process may be used for inter prediction + * @mono_chrome: indicates the video does not contain U and V color planes + * @enable_order_hint: tools based on the values of order hints may be used + * @order_hint_bits: the number of bits used for the order_hint field at each frame + * @use_128x128_superblock: indicates superblocks contain 128*128 luma samples + * @subsampling_x: the chroma subsamling format + * @subsampling_y: the chroma subsamling format + * @max_frame_width: the maximum frame width for the frames represented by sequence + * @max_frame_height: the maximum frame height for the frames represented by sequence + */ +struct vdec_av1_slice_seq_header { + u8 bitdepth; + u8 enable_superres; + u8 enable_filter_intra; + u8 enable_intra_edge_filter; + u8 enable_interintra_compound; + u8 enable_masked_compound; + u8 enable_dual_filter; + u8 enable_jnt_comp; + u8 mono_chrome; + u8 enable_order_hint; + u8 order_hint_bits; + u8 use_128x128_superblock; + u8 subsampling_x; + u8 subsampling_y; + u32 max_frame_width; + u32 max_frame_height; +}; + +/** + * struct vdec_av1_slice_frame - Represents current Frame info + * @uh: uncompressed header info + * @seq: sequence header info + * @large_scale_tile: is large scale mode + * @cur_ts: current frame timestamp + * @prev_fb_idx: prev slot id + * @ref_frame_sign_bias: arrays for ref_frame sign bias + * @order_hints: arrays for ref_frame order hint + * @ref_frame_valid: arrays for valid ref_frame + * @ref_frame_map: map to slot frame info + * @frame_refs: ref_frame info + */ +struct vdec_av1_slice_frame { + struct vdec_av1_slice_uncompressed_header uh; + struct vdec_av1_slice_seq_header seq; + u8 large_scale_tile; + u64 cur_ts; + int prev_fb_idx; + u8 ref_frame_sign_bias[V4L2_AV1_TOTAL_REFS_PER_FRAME]; + u32 order_hints[V4L2_AV1_REFS_PER_FRAME]; + u32 ref_frame_valid[V4L2_AV1_REFS_PER_FRAME]; + int ref_frame_map[V4L2_AV1_TOTAL_REFS_PER_FRAME]; + struct vdec_av1_slice_frame_refs frame_refs[V4L2_AV1_REFS_PER_FRAME]; +}; + +/** + * struct vdec_av1_slice_work_buffer - work buffer for lat + * @mv_addr: mv buffer memory info + * @cdf_addr: cdf buffer memory info + * @segid_addr: segid buffer memory info + */ +struct vdec_av1_slice_work_buffer { + struct vdec_av1_slice_mem mv_addr; + struct vdec_av1_slice_mem cdf_addr; + struct vdec_av1_slice_mem segid_addr; +}; + +/** + * struct vdec_av1_slice_frame_info - frame info for each slot + * @frame_type: frame type + * @frame_is_intra: is intra frame + * @order_hint: order hint + * @order_hints: referece frame order hint + * @upscaled_width: upscale width + * @pic_pitch: buffer pitch + * @frame_width: frane width + * @frame_height: frame height + * @mi_rows: rows in mode info + * @mi_cols: cols in mode info + * @ref_count: mark to reference frame counts + */ +struct vdec_av1_slice_frame_info { + u8 frame_type; + u8 frame_is_intra; + int order_hint; + u32 order_hints[V4L2_AV1_REFS_PER_FRAME]; + u32 upscaled_width; + u32 pic_pitch; + u32 frame_width; + u32 frame_height; + u32 mi_rows; + u32 mi_cols; + int ref_count; +}; + +/** + * struct vdec_av1_slice_slot - slot info that needs to be saved in the global instance + * @frame_info: frame info for each slot + * @timestamp: time stamp info + */ +struct vdec_av1_slice_slot { + struct vdec_av1_slice_frame_info frame_info[AV1_MAX_FRAME_BUF_COUNT]; + u64 timestamp[AV1_MAX_FRAME_BUF_COUNT]; +}; + +/** + * struct vdec_av1_slice_fb - frame buffer for decoding + * @y: current y buffer address info + * @c: current c buffer address info + */ +struct vdec_av1_slice_fb { + struct vdec_av1_slice_mem y; + struct vdec_av1_slice_mem c; +}; + +/** + * struct vdec_av1_slice_vsi - exchange frame information between Main CPU and MicroP + * @bs: input buffer info + * @work_buffer: working buffe for hw + * @cdf_table: cdf_table buffer + * @cdf_tmp: cdf temp buffer + * @rd_mv: mv buffer for lat output , core input + * @ube: ube buffer + * @trans: transcoded buffer + * @err_map: err map buffer + * @row_info: row info buffer + * @fb: current y/c buffer + * @ref: ref y/c buffer + * @iq_table: iq table buffer + * @tile: tile buffer + * @slots: slots info for each frame + * @slot_id: current frame slot id + * @frame: current frame info + * @state: status after decode done + * @cur_lst_tile_id: tile id for large scale + */ +struct vdec_av1_slice_vsi { + /* lat */ + struct vdec_av1_slice_mem bs; + struct vdec_av1_slice_work_buffer work_buffer[AV1_MAX_FRAME_BUF_COUNT]; + struct vdec_av1_slice_mem cdf_table; + struct vdec_av1_slice_mem cdf_tmp; + /* LAT stage's output, Core stage's input */ + struct vdec_av1_slice_mem rd_mv; + struct vdec_av1_slice_mem ube; + struct vdec_av1_slice_mem trans; + struct vdec_av1_slice_mem err_map; + struct vdec_av1_slice_mem row_info; + /* core */ + struct vdec_av1_slice_fb fb; + struct vdec_av1_slice_fb ref[V4L2_AV1_REFS_PER_FRAME]; + struct vdec_av1_slice_mem iq_table; + /* lat and core share*/ + struct vdec_av1_slice_mem tile; + struct vdec_av1_slice_slot slots; + s8 slot_id; + struct vdec_av1_slice_frame frame; + struct vdec_av1_slice_state state; + u32 cur_lst_tile_id; +}; + +/** + * struct vdec_av1_slice_pfc - per-frame context that contains a local vsi. + * pass it from lat to core + * @vsi: local vsi. copy to/from remote vsi before/after decoding + * @ref_idx: reference buffer timestamp + * @seq: picture sequence + */ +struct vdec_av1_slice_pfc { + struct vdec_av1_slice_vsi vsi; + u64 ref_idx[V4L2_AV1_REFS_PER_FRAME]; + int seq; +}; + +/** + * struct vdec_av1_slice_instance - represent one av1 instance + * @ctx: pointer to codec's context + * @vpu: VPU instance + * @iq_table: iq table buffer + * @cdf_table: cdf table buffer + * @mv: mv working buffer + * @cdf: cdf working buffer + * @seg: segmentation working buffer + * @cdf_temp: cdf temp buffer + * @tile: tile buffer + * @slots: slots info + * @tile_group: tile_group entry + * @level: level of current resolution + * @width: width of last picture + * @height: height of last picture + * @frame_type: frame_type of last picture + * @irq_enabled: irq to Main CPU or MicroP + * @inneracing_mode: is inneracing mode + * @init_vsi: vsi used for initialized AV1 instance + * @vsi: vsi used for decoding/flush ... + * @core_vsi: vsi used for Core stage + * @seq: global picture sequence + */ +struct vdec_av1_slice_instance { + struct mtk_vcodec_ctx *ctx; + struct vdec_vpu_inst vpu; + + struct mtk_vcodec_mem iq_table; + struct mtk_vcodec_mem cdf_table; + + struct mtk_vcodec_mem mv[AV1_MAX_FRAME_BUF_COUNT]; + struct mtk_vcodec_mem cdf[AV1_MAX_FRAME_BUF_COUNT]; + struct mtk_vcodec_mem seg[AV1_MAX_FRAME_BUF_COUNT]; + struct mtk_vcodec_mem cdf_temp; + struct mtk_vcodec_mem tile; + struct vdec_av1_slice_slot slots; + struct vdec_av1_slice_tile_group tile_group; + + /* for resolution change and get_pic_info */ + enum vdec_av1_slice_resolution_level level; + u32 width; + u32 height; + + u32 frame_type; + u32 irq_enabled; + u32 inneracing_mode; + + /* MicroP vsi */ + union { + struct vdec_av1_slice_init_vsi *init_vsi; + struct vdec_av1_slice_vsi *vsi; + }; + struct vdec_av1_slice_vsi *core_vsi; + int seq; +}; + +static int vdec_av1_slice_core_decode(struct vdec_lat_buf *lat_buf); + +static inline int vdec_av1_slice_get_msb(u32 n) +{ + if (n == 0) + return 0; + return 31 ^ __builtin_clz(n); +} + +static inline bool vdec_av1_slice_need_scale(u32 ref_width, u32 ref_height, + u32 this_width, u32 this_height) +{ + return ((this_width << 1) >= ref_width) && + ((this_height << 1) >= ref_height) && + (this_width <= (ref_width << 4)) && + (this_height <= (ref_height << 4)); +} + +static void *vdec_av1_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) +{ + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); + + if (!ctrl) + return ERR_PTR(-EINVAL); + + return ctrl->p_cur.p; +} + +static int vdec_av1_slice_init_cdf_table(struct vdec_av1_slice_instance *instance) +{ + u8 *remote_cdf_table; + struct mtk_vcodec_ctx *ctx; + struct vdec_av1_slice_init_vsi *vsi; + int ret; + + ctx = instance->ctx; + vsi = instance->vpu.vsi; + remote_cdf_table = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, + (u32)vsi->cdf_table_addr); + if (IS_ERR(remote_cdf_table)) { + mtk_vcodec_err(instance, "failed to map cdf table\n"); + return PTR_ERR(remote_cdf_table); + } + + mtk_vcodec_debug(instance, "map cdf table to 0x%p\n", + remote_cdf_table); + + if (instance->cdf_table.va) + mtk_vcodec_mem_free(ctx, &instance->cdf_table); + instance->cdf_table.size = vsi->cdf_table_size; + + ret = mtk_vcodec_mem_alloc(ctx, &instance->cdf_table); + if (ret) + return ret; + + memcpy(instance->cdf_table.va, remote_cdf_table, vsi->cdf_table_size); + + return 0; +} + +static int vdec_av1_slice_init_iq_table(struct vdec_av1_slice_instance *instance) +{ + u8 *remote_iq_table; + struct mtk_vcodec_ctx *ctx; + struct vdec_av1_slice_init_vsi *vsi; + int ret; + + ctx = instance->ctx; + vsi = instance->vpu.vsi; + remote_iq_table = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, + (u32)vsi->iq_table_addr); + if (IS_ERR(remote_iq_table)) { + mtk_vcodec_err(instance, "failed to map iq table\n"); + return PTR_ERR(remote_iq_table); + } + + mtk_vcodec_debug(instance, "map iq table to 0x%p\n", remote_iq_table); + + if (instance->iq_table.va) + mtk_vcodec_mem_free(ctx, &instance->iq_table); + instance->iq_table.size = vsi->iq_table_size; + + ret = mtk_vcodec_mem_alloc(ctx, &instance->iq_table); + if (ret) + return ret; + + memcpy(instance->iq_table.va, remote_iq_table, vsi->iq_table_size); + + return 0; +} + +static int vdec_av1_slice_get_new_slot(struct vdec_av1_slice_vsi *vsi) +{ + struct vdec_av1_slice_slot *slots = &vsi->slots; + int new_slot_idx = AV1_INVALID_IDX; + int i; + + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) { + if (slots->frame_info[i].ref_count == 0) { + new_slot_idx = i; + break; + } + } + + if (new_slot_idx != AV1_INVALID_IDX) { + slots->frame_info[new_slot_idx].ref_count++; + slots->timestamp[new_slot_idx] = vsi->frame.cur_ts; + } + + return new_slot_idx; +} + +static inline void vdec_av1_slice_clear_fb(struct vdec_av1_slice_frame_info *frame_info) +{ + memset((void *)frame_info, 0, sizeof(struct vdec_av1_slice_frame_info)); +} + +static void vdec_av1_slice_decrease_ref_count(struct vdec_av1_slice_slot *slots, int fb_idx) +{ + struct vdec_av1_slice_frame_info *frame_info = slots->frame_info; + + frame_info[fb_idx].ref_count--; + if (frame_info[fb_idx].ref_count < 0) { + frame_info[fb_idx].ref_count = 0; + mtk_v4l2_err("av1_error: %s() fb_idx %d decrease ref_count error\n", + __func__, fb_idx); + } + + vdec_av1_slice_clear_fb(&frame_info[fb_idx]); +} + +static void vdec_av1_slice_cleanup_slots(struct vdec_av1_slice_slot *slots, + struct vdec_av1_slice_frame *frame, + struct v4l2_ctrl_av1_frame *ctrl_fh) +{ + int slot_id, ref_id; + + for (ref_id = 0; ref_id < V4L2_AV1_TOTAL_REFS_PER_FRAME; ref_id++) + frame->ref_frame_map[ref_id] = AV1_INVALID_IDX; + + for (slot_id = 0; slot_id < AV1_MAX_FRAME_BUF_COUNT; slot_id++) { + u64 timestamp = slots->timestamp[slot_id]; + bool ref_used = false; + + /* ignored unused slots */ + if (slots->frame_info[slot_id].ref_count == 0) + continue; + + for (ref_id = 0; ref_id < V4L2_AV1_TOTAL_REFS_PER_FRAME; ref_id++) { + if (ctrl_fh->reference_frame_ts[ref_id] == timestamp) { + frame->ref_frame_map[ref_id] = slot_id; + ref_used = true; + } + } + + if (!ref_used) + vdec_av1_slice_decrease_ref_count(slots, slot_id); + } +} + +static void vdec_av1_slice_setup_slot(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_vsi *vsi, + struct v4l2_ctrl_av1_frame *ctrl_fh) +{ + struct vdec_av1_slice_frame_info *cur_frame_info; + struct vdec_av1_slice_uncompressed_header *uh = &vsi->frame.uh; + int ref_id; + + memcpy(&vsi->slots, &instance->slots, sizeof(instance->slots)); + vdec_av1_slice_cleanup_slots(&vsi->slots, &vsi->frame, ctrl_fh); + vsi->slot_id = vdec_av1_slice_get_new_slot(vsi); + + if (vsi->slot_id == AV1_INVALID_IDX) { + mtk_v4l2_err("warning:av1 get invalid index slot\n"); + vsi->slot_id = 0; + } + cur_frame_info = &vsi->slots.frame_info[vsi->slot_id]; + cur_frame_info->frame_type = uh->frame_type; + cur_frame_info->frame_is_intra = ((uh->frame_type == AV1_INTRA_ONLY_FRAME) || + (uh->frame_type == AV1_KEY_FRAME)); + cur_frame_info->order_hint = uh->order_hint; + cur_frame_info->upscaled_width = uh->upscaled_width; + cur_frame_info->pic_pitch = 0; + cur_frame_info->frame_width = uh->frame_width; + cur_frame_info->frame_height = uh->frame_height; + cur_frame_info->mi_cols = ((uh->frame_width + 7) >> 3) << 1; + cur_frame_info->mi_rows = ((uh->frame_height + 7) >> 3) << 1; + + /* ensure current frame is properly mapped if referenced */ + for (ref_id = 0; ref_id < V4L2_AV1_TOTAL_REFS_PER_FRAME; ref_id++) { + u64 timestamp = vsi->slots.timestamp[vsi->slot_id]; + + if (ctrl_fh->reference_frame_ts[ref_id] == timestamp) + vsi->frame.ref_frame_map[ref_id] = vsi->slot_id; + } +} + +static int vdec_av1_slice_alloc_working_buffer(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_vsi *vsi) +{ + struct mtk_vcodec_ctx *ctx = instance->ctx; + enum vdec_av1_slice_resolution_level level; + u32 max_sb_w, max_sb_h, max_w, max_h, w, h; + int i, ret; + + w = vsi->frame.uh.frame_width; + h = vsi->frame.uh.frame_height; + + if (w > VCODEC_DEC_4K_CODED_WIDTH || h > VCODEC_DEC_4K_CODED_HEIGHT) + /* 8K */ + return -EINVAL; + + if (w > MTK_VDEC_MAX_W || h > MTK_VDEC_MAX_H) { + /* 4K */ + level = AV1_RES_4K; + max_w = VCODEC_DEC_4K_CODED_WIDTH; + max_h = VCODEC_DEC_4K_CODED_HEIGHT; + } else { + /* FHD */ + level = AV1_RES_FHD; + max_w = MTK_VDEC_MAX_W; + max_h = MTK_VDEC_MAX_H; + } + + if (level == instance->level) + return 0; + + mtk_vcodec_debug(instance, "resolution level changed from %u to %u, %ux%u", + instance->level, level, w, h); + + max_sb_w = DIV_ROUND_UP(max_w, 128); + max_sb_h = DIV_ROUND_UP(max_h, 128); + + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) { + if (instance->mv[i].va) + mtk_vcodec_mem_free(ctx, &instance->mv[i]); + instance->mv[i].size = max_sb_w * max_sb_h * SZ_1K; + ret = mtk_vcodec_mem_alloc(ctx, &instance->mv[i]); + if (ret) + goto err; + + if (instance->seg[i].va) + mtk_vcodec_mem_free(ctx, &instance->seg[i]); + instance->seg[i].size = max_sb_w * max_sb_h * 512; + ret = mtk_vcodec_mem_alloc(ctx, &instance->seg[i]); + if (ret) + goto err; + + if (instance->cdf[i].va) + mtk_vcodec_mem_free(ctx, &instance->cdf[i]); + instance->cdf[i].size = AV1_CDF_TABLE_BUFFER_SIZE; + ret = mtk_vcodec_mem_alloc(ctx, &instance->cdf[i]); + if (ret) + goto err; + } + + if (!instance->cdf_temp.va) { + instance->cdf_temp.size = (SZ_1K * 16 * 100); + ret = mtk_vcodec_mem_alloc(ctx, &instance->cdf_temp); + if (ret) + goto err; + vsi->cdf_tmp.buf = instance->cdf_temp.dma_addr; + vsi->cdf_tmp.size = instance->cdf_temp.size; + } + + if (instance->tile.va) + mtk_vcodec_mem_free(ctx, &instance->tile); + + instance->tile.size = AV1_TILE_BUF_SIZE * V4L2_AV1_MAX_TILE_COUNT; + ret = mtk_vcodec_mem_alloc(ctx, &instance->tile); + if (ret) + goto err; + + instance->level = level; + return 0; + +err: + instance->level = AV1_RES_NONE; + return ret; +} + +static void vdec_av1_slice_free_working_buffer(struct vdec_av1_slice_instance *instance) +{ + struct mtk_vcodec_ctx *ctx = instance->ctx; + int i; + + for (i = 0; i < ARRAY_SIZE(instance->mv); i++) + mtk_vcodec_mem_free(ctx, &instance->mv[i]); + + for (i = 0; i < ARRAY_SIZE(instance->seg); i++) + mtk_vcodec_mem_free(ctx, &instance->seg[i]); + + for (i = 0; i < ARRAY_SIZE(instance->cdf); i++) + mtk_vcodec_mem_free(ctx, &instance->cdf[i]); + + mtk_vcodec_mem_free(ctx, &instance->tile); + mtk_vcodec_mem_free(ctx, &instance->cdf_temp); + mtk_vcodec_mem_free(ctx, &instance->cdf_table); + mtk_vcodec_mem_free(ctx, &instance->iq_table); + + instance->level = AV1_RES_NONE; +} + +static inline void vdec_av1_slice_vsi_from_remote(struct vdec_av1_slice_vsi *vsi, + struct vdec_av1_slice_vsi *remote_vsi) +{ + memcpy(&vsi->trans, &remote_vsi->trans, sizeof(vsi->trans)); + memcpy(&vsi->state, &remote_vsi->state, sizeof(vsi->state)); +} + +static inline void vdec_av1_slice_vsi_to_remote(struct vdec_av1_slice_vsi *vsi, + struct vdec_av1_slice_vsi *remote_vsi) +{ + memcpy(remote_vsi, vsi, sizeof(*vsi)); +} + +static int vdec_av1_slice_setup_lat_from_src_buf(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_vsi *vsi, + struct vdec_lat_buf *lat_buf) +{ + struct vb2_v4l2_buffer *src; + struct vb2_v4l2_buffer *dst; + + src = v4l2_m2m_next_src_buf(instance->ctx->m2m_ctx); + if (!src) + return -EINVAL; + + lat_buf->src_buf_req = src->vb2_buf.req_obj.req; + dst = &lat_buf->ts_info; + v4l2_m2m_buf_copy_metadata(src, dst, true); + vsi->frame.cur_ts = dst->vb2_buf.timestamp; + + return 0; +} + +static short vdec_av1_slice_resolve_divisor_32(u32 D, short *shift) +{ + int f; + int e; + + *shift = vdec_av1_slice_get_msb(D); + /* e is obtained from D after resetting the most significant 1 bit. */ + e = D - ((u32)1 << *shift); + /* Get the most significant DIV_LUT_BITS (8) bits of e into f */ + if (*shift > DIV_LUT_BITS) + f = AV1_DIV_ROUND_UP_POW2(e, *shift - DIV_LUT_BITS); + else + f = e << (DIV_LUT_BITS - *shift); + if (f > DIV_LUT_NUM) + return -1; + *shift += DIV_LUT_PREC_BITS; + /* Use f as lookup into the precomputed table of multipliers */ + return div_lut[f]; +} + +static void vdec_av1_slice_get_shear_params(struct vdec_av1_slice_gm *gm_params) +{ + const int *mat = gm_params->wmmat; + short shift; + short y; + long long gv, dv; + + if (gm_params->wmmat[2] <= 0) + return; + + gm_params->alpha = clamp_val(mat[2] - (1 << WARPEDMODEL_PREC_BITS), S16_MIN, S16_MAX); + gm_params->beta = clamp_val(mat[3], S16_MIN, S16_MAX); + + y = vdec_av1_slice_resolve_divisor_32(abs(mat[2]), &shift) * (mat[2] < 0 ? -1 : 1); + + gv = ((long long)mat[4] * (1 << WARPEDMODEL_PREC_BITS)) * y; + gm_params->gamma = clamp_val((int)AV1_DIV_ROUND_UP_POW2_SIGNED(gv, shift), + S16_MIN, S16_MAX); + + dv = ((long long)mat[3] * mat[4]) * y; + gm_params->delta = clamp_val(mat[5] - (int)AV1_DIV_ROUND_UP_POW2_SIGNED(dv, shift) - + (1 << WARPEDMODEL_PREC_BITS), S16_MIN, S16_MAX); + + gm_params->alpha = AV1_DIV_ROUND_UP_POW2_SIGNED(gm_params->alpha, WARP_PARAM_REDUCE_BITS) * + (1 << WARP_PARAM_REDUCE_BITS); + gm_params->beta = AV1_DIV_ROUND_UP_POW2_SIGNED(gm_params->beta, WARP_PARAM_REDUCE_BITS) * + (1 << WARP_PARAM_REDUCE_BITS); + gm_params->gamma = AV1_DIV_ROUND_UP_POW2_SIGNED(gm_params->gamma, WARP_PARAM_REDUCE_BITS) * + (1 << WARP_PARAM_REDUCE_BITS); + gm_params->delta = AV1_DIV_ROUND_UP_POW2_SIGNED(gm_params->delta, WARP_PARAM_REDUCE_BITS) * + (1 << WARP_PARAM_REDUCE_BITS); +} + +static void vdec_av1_slice_setup_gm(struct vdec_av1_slice_gm *gm, + struct v4l2_av1_global_motion *ctrl_gm) +{ + u32 i, j; + + for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++) { + gm[i].wmtype = ctrl_gm->type[i]; + for (j = 0; j < 6; j++) + gm[i].wmmat[j] = ctrl_gm->params[i][j]; + + gm[i].invalid = !!(ctrl_gm->invalid & BIT(i)); + gm[i].alpha = 0; + gm[i].beta = 0; + gm[i].gamma = 0; + gm[i].delta = 0; + if (gm[i].wmtype <= V4L2_AV1_WARP_MODEL_AFFINE) + vdec_av1_slice_get_shear_params(&gm[i]); + } +} + +static void vdec_av1_slice_setup_seg(struct vdec_av1_slice_seg *seg, + struct v4l2_av1_segmentation *ctrl_seg) +{ + u32 i, j; + + seg->segmentation_enabled = SEGMENTATION_FLAG(ctrl_seg, ENABLED); + seg->segmentation_update_map = SEGMENTATION_FLAG(ctrl_seg, UPDATE_MAP); + seg->segmentation_temporal_update = SEGMENTATION_FLAG(ctrl_seg, TEMPORAL_UPDATE); + seg->segmentation_update_data = SEGMENTATION_FLAG(ctrl_seg, UPDATE_DATA); + seg->segid_preskip = SEGMENTATION_FLAG(ctrl_seg, SEG_ID_PRE_SKIP); + seg->last_active_segid = ctrl_seg->last_active_seg_id; + + for (i = 0; i < V4L2_AV1_MAX_SEGMENTS; i++) { + seg->feature_enabled_mask[i] = ctrl_seg->feature_enabled[i]; + for (j = 0; j < V4L2_AV1_SEG_LVL_MAX; j++) + seg->feature_data[i][j] = ctrl_seg->feature_data[i][j]; + } +} + +static void vdec_av1_slice_setup_quant(struct vdec_av1_slice_quantization *quant, + struct v4l2_av1_quantization *ctrl_quant) +{ + quant->base_q_idx = ctrl_quant->base_q_idx; + quant->delta_qydc = ctrl_quant->delta_q_y_dc; + quant->delta_qudc = ctrl_quant->delta_q_u_dc; + quant->delta_quac = ctrl_quant->delta_q_u_ac; + quant->delta_qvdc = ctrl_quant->delta_q_v_dc; + quant->delta_qvac = ctrl_quant->delta_q_v_ac; + quant->qm_y = ctrl_quant->qm_y; + quant->qm_u = ctrl_quant->qm_u; + quant->qm_v = ctrl_quant->qm_v; + quant->using_qmatrix = QUANT_FLAG(ctrl_quant, USING_QMATRIX); +} + +static int vdec_av1_slice_get_qindex(struct vdec_av1_slice_uncompressed_header *uh, + int segmentation_id) +{ + struct vdec_av1_slice_seg *seg = &uh->seg; + struct vdec_av1_slice_quantization *quant = &uh->quant; + int data = 0, qindex = 0; + + if (seg->segmentation_enabled && + (seg->feature_enabled_mask[segmentation_id] & BIT(SEG_LVL_ALT_Q))) { + data = seg->feature_data[segmentation_id][SEG_LVL_ALT_Q]; + qindex = quant->base_q_idx + data; + return clamp_val(qindex, 0, MAXQ); + } + + return quant->base_q_idx; +} + +static void vdec_av1_slice_setup_lr(struct vdec_av1_slice_lr *lr, + struct v4l2_av1_loop_restoration *ctrl_lr) +{ + int i; + + lr->use_lr = 0; + lr->use_chroma_lr = 0; + for (i = 0; i < V4L2_AV1_NUM_PLANES_MAX; i++) { + lr->frame_restoration_type[i] = ctrl_lr->frame_restoration_type[i]; + lr->loop_restoration_size[i] = ctrl_lr->loop_restoration_size[i]; + if (lr->frame_restoration_type[i]) { + lr->use_lr = 1; + if (i > 0) + lr->use_chroma_lr = 1; + } + } +} + +static void vdec_av1_slice_setup_lf(struct vdec_av1_slice_loop_filter *lf, + struct v4l2_av1_loop_filter *ctrl_lf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lf->loop_filter_level); i++) + lf->loop_filter_level[i] = ctrl_lf->level[i]; + + for (i = 0; i < V4L2_AV1_TOTAL_REFS_PER_FRAME; i++) + lf->loop_filter_ref_deltas[i] = ctrl_lf->ref_deltas[i]; + + for (i = 0; i < ARRAY_SIZE(lf->loop_filter_mode_deltas); i++) + lf->loop_filter_mode_deltas[i] = ctrl_lf->mode_deltas[i]; + + lf->loop_filter_sharpness = ctrl_lf->sharpness; + lf->loop_filter_delta_enabled = + BIT_FLAG(ctrl_lf, V4L2_AV1_LOOP_FILTER_FLAG_DELTA_ENABLED); +} + +static void vdec_av1_slice_setup_cdef(struct vdec_av1_slice_cdef *cdef, + struct v4l2_av1_cdef *ctrl_cdef) +{ + int i; + + cdef->cdef_damping = ctrl_cdef->damping_minus_3 + 3; + cdef->cdef_bits = ctrl_cdef->bits; + + for (i = 0; i < V4L2_AV1_CDEF_MAX; i++) { + if (ctrl_cdef->y_sec_strength[i] == 4) + ctrl_cdef->y_sec_strength[i] -= 1; + + if (ctrl_cdef->uv_sec_strength[i] == 4) + ctrl_cdef->uv_sec_strength[i] -= 1; + + cdef->cdef_y_strength[i] = + ctrl_cdef->y_pri_strength[i] << SECONDARY_FILTER_STRENGTH_NUM_BITS | + ctrl_cdef->y_sec_strength[i]; + cdef->cdef_uv_strength[i] = + ctrl_cdef->uv_pri_strength[i] << SECONDARY_FILTER_STRENGTH_NUM_BITS | + ctrl_cdef->uv_sec_strength[i]; + } +} + +static void vdec_av1_slice_setup_seq(struct vdec_av1_slice_seq_header *seq, + struct v4l2_ctrl_av1_sequence *ctrl_seq) +{ + seq->bitdepth = ctrl_seq->bit_depth; + seq->max_frame_width = ctrl_seq->max_frame_width_minus_1 + 1; + seq->max_frame_height = ctrl_seq->max_frame_height_minus_1 + 1; + seq->enable_superres = SEQUENCE_FLAG(ctrl_seq, ENABLE_SUPERRES); + seq->enable_filter_intra = SEQUENCE_FLAG(ctrl_seq, ENABLE_FILTER_INTRA); + seq->enable_intra_edge_filter = SEQUENCE_FLAG(ctrl_seq, ENABLE_INTRA_EDGE_FILTER); + seq->enable_interintra_compound = SEQUENCE_FLAG(ctrl_seq, ENABLE_INTERINTRA_COMPOUND); + seq->enable_masked_compound = SEQUENCE_FLAG(ctrl_seq, ENABLE_MASKED_COMPOUND); + seq->enable_dual_filter = SEQUENCE_FLAG(ctrl_seq, ENABLE_DUAL_FILTER); + seq->enable_jnt_comp = SEQUENCE_FLAG(ctrl_seq, ENABLE_JNT_COMP); + seq->mono_chrome = SEQUENCE_FLAG(ctrl_seq, MONO_CHROME); + seq->enable_order_hint = SEQUENCE_FLAG(ctrl_seq, ENABLE_ORDER_HINT); + seq->order_hint_bits = ctrl_seq->order_hint_bits; + seq->use_128x128_superblock = SEQUENCE_FLAG(ctrl_seq, USE_128X128_SUPERBLOCK); + seq->subsampling_x = SEQUENCE_FLAG(ctrl_seq, SUBSAMPLING_X); + seq->subsampling_y = SEQUENCE_FLAG(ctrl_seq, SUBSAMPLING_Y); +} + +static void vdec_av1_slice_setup_tile(struct vdec_av1_slice_frame *frame, + struct v4l2_av1_tile_info *ctrl_tile) +{ + struct vdec_av1_slice_seq_header *seq = &frame->seq; + struct vdec_av1_slice_tile *tile = &frame->uh.tile; + u32 mib_size_log2 = seq->use_128x128_superblock ? 5 : 4; + int i; + + tile->tile_cols = ctrl_tile->tile_cols; + tile->tile_rows = ctrl_tile->tile_rows; + tile->context_update_tile_id = ctrl_tile->context_update_tile_id; + tile->uniform_tile_spacing_flag = + BIT_FLAG(ctrl_tile, V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING); + + for (i = 0; i < tile->tile_cols + 1; i++) + tile->mi_col_starts[i] = + ALIGN(ctrl_tile->mi_col_starts[i], BIT(mib_size_log2)) >> mib_size_log2; + + for (i = 0; i < tile->tile_rows + 1; i++) + tile->mi_row_starts[i] = + ALIGN(ctrl_tile->mi_row_starts[i], BIT(mib_size_log2)) >> mib_size_log2; +} + +static void vdec_av1_slice_setup_uh(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_frame *frame, + struct v4l2_ctrl_av1_frame *ctrl_fh) +{ + struct vdec_av1_slice_uncompressed_header *uh = &frame->uh; + int i; + + uh->use_ref_frame_mvs = FH_FLAG(ctrl_fh, USE_REF_FRAME_MVS); + uh->order_hint = ctrl_fh->order_hint; + vdec_av1_slice_setup_gm(uh->gm, &ctrl_fh->global_motion); + uh->upscaled_width = ctrl_fh->upscaled_width; + uh->frame_width = ctrl_fh->frame_width_minus_1 + 1; + uh->frame_height = ctrl_fh->frame_height_minus_1 + 1; + uh->mi_cols = ((uh->frame_width + 7) >> 3) << 1; + uh->mi_rows = ((uh->frame_height + 7) >> 3) << 1; + uh->reduced_tx_set = FH_FLAG(ctrl_fh, REDUCED_TX_SET); + uh->tx_mode = ctrl_fh->tx_mode; + uh->uniform_tile_spacing_flag = + BIT_FLAG(&ctrl_fh->tile_info, V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING); + uh->interpolation_filter = ctrl_fh->interpolation_filter; + uh->allow_warped_motion = FH_FLAG(ctrl_fh, ALLOW_WARPED_MOTION); + uh->is_motion_mode_switchable = FH_FLAG(ctrl_fh, IS_MOTION_MODE_SWITCHABLE); + uh->frame_type = ctrl_fh->frame_type; + uh->frame_is_intra = (uh->frame_type == V4L2_AV1_INTRA_ONLY_FRAME || + uh->frame_type == V4L2_AV1_KEY_FRAME); + + if (!uh->frame_is_intra && FH_FLAG(ctrl_fh, REFERENCE_SELECT)) + uh->reference_mode = AV1_REFERENCE_MODE_SELECT; + else + uh->reference_mode = AV1_SINGLE_REFERENCE; + + uh->allow_high_precision_mv = FH_FLAG(ctrl_fh, ALLOW_HIGH_PRECISION_MV); + uh->allow_intra_bc = FH_FLAG(ctrl_fh, ALLOW_INTRABC); + uh->force_integer_mv = FH_FLAG(ctrl_fh, FORCE_INTEGER_MV); + uh->allow_screen_content_tools = FH_FLAG(ctrl_fh, ALLOW_SCREEN_CONTENT_TOOLS); + uh->error_resilient_mode = FH_FLAG(ctrl_fh, ERROR_RESILIENT_MODE); + uh->primary_ref_frame = ctrl_fh->primary_ref_frame; + uh->disable_frame_end_update_cdf = + FH_FLAG(ctrl_fh, DISABLE_FRAME_END_UPDATE_CDF); + uh->disable_cdf_update = FH_FLAG(ctrl_fh, DISABLE_CDF_UPDATE); + uh->skip_mode.skip_mode_present = FH_FLAG(ctrl_fh, SKIP_MODE_PRESENT); + uh->skip_mode.skip_mode_frame[0] = + ctrl_fh->skip_mode_frame[0] - V4L2_AV1_REF_LAST_FRAME; + uh->skip_mode.skip_mode_frame[1] = + ctrl_fh->skip_mode_frame[1] - V4L2_AV1_REF_LAST_FRAME; + uh->skip_mode.skip_mode_allowed = ctrl_fh->skip_mode_frame[0] ? 1 : 0; + + vdec_av1_slice_setup_seg(&uh->seg, &ctrl_fh->segmentation); + uh->delta_q_lf.delta_q_present = QUANT_FLAG(&ctrl_fh->quantization, DELTA_Q_PRESENT); + uh->delta_q_lf.delta_q_res = 1 << ctrl_fh->quantization.delta_q_res; + uh->delta_q_lf.delta_lf_present = + BIT_FLAG(&ctrl_fh->loop_filter, V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_PRESENT); + uh->delta_q_lf.delta_lf_res = ctrl_fh->loop_filter.delta_lf_res; + uh->delta_q_lf.delta_lf_multi = + BIT_FLAG(&ctrl_fh->loop_filter, V4L2_AV1_LOOP_FILTER_FLAG_DELTA_LF_MULTI); + vdec_av1_slice_setup_quant(&uh->quant, &ctrl_fh->quantization); + + uh->coded_loss_less = 1; + for (i = 0; i < V4L2_AV1_MAX_SEGMENTS; i++) { + uh->quant.qindex[i] = vdec_av1_slice_get_qindex(uh, i); + uh->loss_less_array[i] = + (uh->quant.qindex[i] == 0 && uh->quant.delta_qydc == 0 && + uh->quant.delta_quac == 0 && uh->quant.delta_qudc == 0 && + uh->quant.delta_qvac == 0 && uh->quant.delta_qvdc == 0); + + if (!uh->loss_less_array[i]) + uh->coded_loss_less = 0; + } + + vdec_av1_slice_setup_lr(&uh->lr, &ctrl_fh->loop_restoration); + uh->superres_denom = ctrl_fh->superres_denom; + vdec_av1_slice_setup_lf(&uh->loop_filter, &ctrl_fh->loop_filter); + vdec_av1_slice_setup_cdef(&uh->cdef, &ctrl_fh->cdef); + vdec_av1_slice_setup_tile(frame, &ctrl_fh->tile_info); +} + +static int vdec_av1_slice_setup_tile_group(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_vsi *vsi) +{ + struct v4l2_ctrl_av1_tile_group_entry *ctrl_tge; + struct vdec_av1_slice_tile_group *tile_group = &instance->tile_group; + struct vdec_av1_slice_uncompressed_header *uh = &vsi->frame.uh; + struct vdec_av1_slice_tile *tile = &uh->tile; + struct v4l2_ctrl *ctrl; + u32 tge_size; + int i; + + ctrl = v4l2_ctrl_find(&instance->ctx->ctrl_hdl, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY); + if (!ctrl) + return -EINVAL; + + tge_size = ctrl->elems; + ctrl_tge = (struct v4l2_ctrl_av1_tile_group_entry *)ctrl->p_cur.p; + + tile_group->num_tiles = tile->tile_cols * tile->tile_rows; + + if (tile_group->num_tiles != tge_size || + tile_group->num_tiles > V4L2_AV1_MAX_TILE_COUNT) { + mtk_vcodec_err(instance, "invalid tge_size %d, tile_num:%d\n", + tge_size, tile_group->num_tiles); + return -EINVAL; + } + + for (i = 0; i < tge_size; i++) { + if (i != ctrl_tge[i].tile_row * vsi->frame.uh.tile.tile_cols + + ctrl_tge[i].tile_col) { + mtk_vcodec_err(instance, "invalid tge info %d, %d %d %d\n", + i, ctrl_tge[i].tile_row, ctrl_tge[i].tile_col, + vsi->frame.uh.tile.tile_rows); + return -EINVAL; + } + tile_group->tile_size[i] = ctrl_tge[i].tile_size; + tile_group->tile_start_offset[i] = ctrl_tge[i].tile_offset; + } + + return 0; +} + +static inline void vdec_av1_slice_setup_state(struct vdec_av1_slice_vsi *vsi) +{ + memset(&vsi->state, 0, sizeof(vsi->state)); +} + +static void vdec_av1_slice_setup_scale_factors(struct vdec_av1_slice_frame_refs *frame_ref, + struct vdec_av1_slice_frame_info *ref_frame_info, + struct vdec_av1_slice_uncompressed_header *uh) +{ + struct vdec_av1_slice_scale_factors *scale_factors = &frame_ref->scale_factors; + u32 ref_upscaled_width = ref_frame_info->upscaled_width; + u32 ref_frame_height = ref_frame_info->frame_height; + u32 frame_width = uh->frame_width; + u32 frame_height = uh->frame_height; + + if (!vdec_av1_slice_need_scale(ref_upscaled_width, ref_frame_height, + frame_width, frame_height)) { + scale_factors->x_scale = -1; + scale_factors->y_scale = -1; + scale_factors->is_scaled = 0; + return; + } + + scale_factors->x_scale = + ((ref_upscaled_width << AV1_REF_SCALE_SHIFT) + (frame_width >> 1)) / frame_width; + scale_factors->y_scale = + ((ref_frame_height << AV1_REF_SCALE_SHIFT) + (frame_height >> 1)) / frame_height; + scale_factors->is_scaled = + (scale_factors->x_scale != AV1_REF_INVALID_SCALE) && + (scale_factors->y_scale != AV1_REF_INVALID_SCALE) && + (scale_factors->x_scale != AV1_REF_NO_SCALE || + scale_factors->y_scale != AV1_REF_NO_SCALE); + scale_factors->x_step = + AV1_DIV_ROUND_UP_POW2(scale_factors->x_scale, + AV1_REF_SCALE_SHIFT - AV1_SCALE_SUBPEL_BITS); + scale_factors->y_step = + AV1_DIV_ROUND_UP_POW2(scale_factors->y_scale, + AV1_REF_SCALE_SHIFT - AV1_SCALE_SUBPEL_BITS); +} + +static unsigned char vdec_av1_slice_get_sign_bias(int a, + int b, + u8 enable_order_hint, + u8 order_hint_bits) +{ + int diff = 0; + int m = 0; + unsigned char result = 0; + + if (!enable_order_hint) + return 0; + + diff = a - b; + m = 1 << (order_hint_bits - 1); + diff = (diff & (m - 1)) - (diff & m); + + if (diff > 0) + result = 1; + + return result; +} + +static void vdec_av1_slice_setup_ref(struct vdec_av1_slice_pfc *pfc, + struct v4l2_ctrl_av1_frame *ctrl_fh) +{ + struct vdec_av1_slice_vsi *vsi = &pfc->vsi; + struct vdec_av1_slice_frame *frame = &vsi->frame; + struct vdec_av1_slice_slot *slots = &vsi->slots; + struct vdec_av1_slice_uncompressed_header *uh = &frame->uh; + struct vdec_av1_slice_seq_header *seq = &frame->seq; + struct vdec_av1_slice_frame_info *cur_frame_info = + &slots->frame_info[vsi->slot_id]; + struct vdec_av1_slice_frame_info *frame_info; + int i, slot_id; + + if (uh->frame_is_intra) + return; + + for (i = 0; i < V4L2_AV1_REFS_PER_FRAME; i++) { + int ref_idx = ctrl_fh->ref_frame_idx[i]; + + pfc->ref_idx[i] = ctrl_fh->reference_frame_ts[ref_idx]; + slot_id = frame->ref_frame_map[ref_idx]; + frame_info = &slots->frame_info[slot_id]; + if (slot_id == AV1_INVALID_IDX) { + mtk_v4l2_err("cannot match reference[%d] 0x%llx\n", i, + ctrl_fh->reference_frame_ts[ref_idx]); + frame->order_hints[i] = 0; + frame->ref_frame_valid[i] = 0; + continue; + } + + frame->frame_refs[i].ref_fb_idx = slot_id; + vdec_av1_slice_setup_scale_factors(&frame->frame_refs[i], + frame_info, uh); + if (!seq->enable_order_hint) + frame->ref_frame_sign_bias[i + 1] = 0; + else + frame->ref_frame_sign_bias[i + 1] = + vdec_av1_slice_get_sign_bias(frame_info->order_hint, + uh->order_hint, + seq->enable_order_hint, + seq->order_hint_bits); + + frame->order_hints[i] = ctrl_fh->order_hints[i + 1]; + cur_frame_info->order_hints[i] = frame->order_hints[i]; + frame->ref_frame_valid[i] = 1; + } +} + +static void vdec_av1_slice_get_previous(struct vdec_av1_slice_vsi *vsi) +{ + struct vdec_av1_slice_frame *frame = &vsi->frame; + + if (frame->uh.primary_ref_frame == AV1_PRIMARY_REF_NONE) + frame->prev_fb_idx = AV1_INVALID_IDX; + else + frame->prev_fb_idx = frame->frame_refs[frame->uh.primary_ref_frame].ref_fb_idx; +} + +static inline void vdec_av1_slice_setup_operating_mode(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_frame *frame) +{ + frame->large_scale_tile = 0; +} + +static int vdec_av1_slice_setup_pfc(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_pfc *pfc) +{ + struct v4l2_ctrl_av1_frame *ctrl_fh; + struct v4l2_ctrl_av1_sequence *ctrl_seq; + struct vdec_av1_slice_vsi *vsi = &pfc->vsi; + int ret = 0; + + /* frame header */ + ctrl_fh = (struct v4l2_ctrl_av1_frame *) + vdec_av1_get_ctrl_ptr(instance->ctx, + V4L2_CID_STATELESS_AV1_FRAME); + if (IS_ERR(ctrl_fh)) + return PTR_ERR(ctrl_fh); + + ctrl_seq = (struct v4l2_ctrl_av1_sequence *) + vdec_av1_get_ctrl_ptr(instance->ctx, + V4L2_CID_STATELESS_AV1_SEQUENCE); + if (IS_ERR(ctrl_seq)) + return PTR_ERR(ctrl_seq); + + /* setup vsi information */ + vdec_av1_slice_setup_seq(&vsi->frame.seq, ctrl_seq); + vdec_av1_slice_setup_uh(instance, &vsi->frame, ctrl_fh); + vdec_av1_slice_setup_operating_mode(instance, &vsi->frame); + + vdec_av1_slice_setup_state(vsi); + vdec_av1_slice_setup_slot(instance, vsi, ctrl_fh); + vdec_av1_slice_setup_ref(pfc, ctrl_fh); + vdec_av1_slice_get_previous(vsi); + + pfc->seq = instance->seq; + instance->seq++; + + return ret; +} + +static void vdec_av1_slice_setup_lat_buffer(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_vsi *vsi, + struct mtk_vcodec_mem *bs, + struct vdec_lat_buf *lat_buf) +{ + struct vdec_av1_slice_work_buffer *work_buffer; + int i; + + vsi->bs.dma_addr = bs->dma_addr; + vsi->bs.size = bs->size; + + vsi->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr; + vsi->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size; + vsi->trans.dma_addr = lat_buf->ctx->msg_queue.wdma_wptr_addr; + /* used to store trans end */ + vsi->trans.dma_addr_end = lat_buf->ctx->msg_queue.wdma_rptr_addr; + vsi->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr; + vsi->err_map.size = lat_buf->wdma_err_addr.size; + vsi->rd_mv.dma_addr = lat_buf->rd_mv_addr.dma_addr; + vsi->rd_mv.size = lat_buf->rd_mv_addr.size; + + vsi->row_info.buf = 0; + vsi->row_info.size = 0; + + work_buffer = vsi->work_buffer; + + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) { + work_buffer[i].mv_addr.buf = instance->mv[i].dma_addr; + work_buffer[i].mv_addr.size = instance->mv[i].size; + work_buffer[i].segid_addr.buf = instance->seg[i].dma_addr; + work_buffer[i].segid_addr.size = instance->seg[i].size; + work_buffer[i].cdf_addr.buf = instance->cdf[i].dma_addr; + work_buffer[i].cdf_addr.size = instance->cdf[i].size; + } + + vsi->cdf_tmp.buf = instance->cdf_temp.dma_addr; + vsi->cdf_tmp.size = instance->cdf_temp.size; + + vsi->tile.buf = instance->tile.dma_addr; + vsi->tile.size = instance->tile.size; + memcpy(lat_buf->tile_addr.va, instance->tile.va, 64 * instance->tile_group.num_tiles); + + vsi->cdf_table.buf = instance->cdf_table.dma_addr; + vsi->cdf_table.size = instance->cdf_table.size; + vsi->iq_table.buf = instance->iq_table.dma_addr; + vsi->iq_table.size = instance->iq_table.size; +} + +static void vdec_av1_slice_setup_seg_buffer(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_vsi *vsi) +{ + struct vdec_av1_slice_uncompressed_header *uh = &vsi->frame.uh; + struct mtk_vcodec_mem *buf; + + /* reset segment buffer */ + if (uh->primary_ref_frame == AV1_PRIMARY_REF_NONE || !uh->seg.segmentation_enabled) { + mtk_vcodec_debug(instance, "reset seg %d\n", vsi->slot_id); + if (vsi->slot_id != AV1_INVALID_IDX) { + buf = &instance->seg[vsi->slot_id]; + memset(buf->va, 0, buf->size); + } + } +} + +static void vdec_av1_slice_setup_tile_buffer(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_vsi *vsi, + struct mtk_vcodec_mem *bs) +{ + struct vdec_av1_slice_tile_group *tile_group = &instance->tile_group; + struct vdec_av1_slice_uncompressed_header *uh = &vsi->frame.uh; + struct vdec_av1_slice_tile *tile = &uh->tile; + u32 tile_num, tile_row, tile_col; + u32 allow_update_cdf = 0; + u32 sb_boundary_x_m1 = 0, sb_boundary_y_m1 = 0; + int tile_info_base; + u32 tile_buf_pa; + u32 *tile_info_buf = instance->tile.va; + u32 pa = (u32)bs->dma_addr; + + if (uh->disable_cdf_update == 0) + allow_update_cdf = 1; + + for (tile_num = 0; tile_num < tile_group->num_tiles; tile_num++) { + /* each uint32 takes place of 4 bytes */ + tile_info_base = (AV1_TILE_BUF_SIZE * tile_num) >> 2; + tile_row = tile_num / tile->tile_cols; + tile_col = tile_num % tile->tile_cols; + tile_info_buf[tile_info_base + 0] = (tile_group->tile_size[tile_num] << 3); + tile_buf_pa = pa + tile_group->tile_start_offset[tile_num]; + + tile_info_buf[tile_info_base + 1] = (tile_buf_pa >> 4) << 4; + tile_info_buf[tile_info_base + 2] = (tile_buf_pa % 16) << 3; + + sb_boundary_x_m1 = + (tile->mi_col_starts[tile_col + 1] - tile->mi_col_starts[tile_col] - 1) & + 0x3f; + sb_boundary_y_m1 = + (tile->mi_row_starts[tile_row + 1] - tile->mi_row_starts[tile_row] - 1) & + 0x1ff; + + tile_info_buf[tile_info_base + 3] = (sb_boundary_y_m1 << 7) | sb_boundary_x_m1; + tile_info_buf[tile_info_base + 4] = ((allow_update_cdf << 18) | (1 << 16)); + + if (tile_num == tile->context_update_tile_id && + uh->disable_frame_end_update_cdf == 0) + tile_info_buf[tile_info_base + 4] |= (1 << 17); + + mtk_vcodec_debug(instance, "// tile buf %d pos(%dx%d) offset 0x%x\n", + tile_num, tile_row, tile_col, tile_info_base); + mtk_vcodec_debug(instance, "// %08x %08x %08x %08x\n", + tile_info_buf[tile_info_base + 0], + tile_info_buf[tile_info_base + 1], + tile_info_buf[tile_info_base + 2], + tile_info_buf[tile_info_base + 3]); + mtk_vcodec_debug(instance, "// %08x %08x %08x %08x\n", + tile_info_buf[tile_info_base + 4], + tile_info_buf[tile_info_base + 5], + tile_info_buf[tile_info_base + 6], + tile_info_buf[tile_info_base + 7]); + } +} + +static int vdec_av1_slice_setup_lat(struct vdec_av1_slice_instance *instance, + struct mtk_vcodec_mem *bs, + struct vdec_lat_buf *lat_buf, + struct vdec_av1_slice_pfc *pfc) +{ + struct vdec_av1_slice_vsi *vsi = &pfc->vsi; + int ret; + + ret = vdec_av1_slice_setup_lat_from_src_buf(instance, vsi, lat_buf); + if (ret) + return ret; + + ret = vdec_av1_slice_setup_pfc(instance, pfc); + if (ret) + return ret; + + ret = vdec_av1_slice_setup_tile_group(instance, vsi); + if (ret) + return ret; + + ret = vdec_av1_slice_alloc_working_buffer(instance, vsi); + if (ret) + return ret; + + vdec_av1_slice_setup_seg_buffer(instance, vsi); + vdec_av1_slice_setup_tile_buffer(instance, vsi, bs); + vdec_av1_slice_setup_lat_buffer(instance, vsi, bs, lat_buf); + + return 0; +} + +static int vdec_av1_slice_update_lat(struct vdec_av1_slice_instance *instance, + struct vdec_lat_buf *lat_buf, + struct vdec_av1_slice_pfc *pfc) +{ + struct vdec_av1_slice_vsi *vsi; + + vsi = &pfc->vsi; + mtk_vcodec_debug(instance, "frame %u LAT CRC 0x%08x, output size is %d\n", + pfc->seq, vsi->state.crc[0], vsi->state.out_size); + + /* buffer full, need to re-decode */ + if (vsi->state.full) { + /* buffer not enough */ + if (vsi->trans.dma_addr_end - vsi->trans.dma_addr == vsi->ube.size) + return -ENOMEM; + return -EAGAIN; + } + + instance->width = vsi->frame.uh.upscaled_width; + instance->height = vsi->frame.uh.frame_height; + instance->frame_type = vsi->frame.uh.frame_type; + + return 0; +} + +static int vdec_av1_slice_setup_core_to_dst_buf(struct vdec_av1_slice_instance *instance, + struct vdec_lat_buf *lat_buf) +{ + struct vb2_v4l2_buffer *dst; + + dst = v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx); + if (!dst) + return -EINVAL; + + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, dst, true); + + return 0; +} + +static int vdec_av1_slice_setup_core_buffer(struct vdec_av1_slice_instance *instance, + struct vdec_av1_slice_pfc *pfc, + struct vdec_av1_slice_vsi *vsi, + struct vdec_fb *fb, + struct vdec_lat_buf *lat_buf) +{ + struct vb2_buffer *vb; + struct vb2_queue *vq; + int w, h, plane, size; + int i; + + plane = instance->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes; + w = vsi->frame.uh.upscaled_width; + h = vsi->frame.uh.frame_height; + size = ALIGN(w, VCODEC_DEC_ALIGNED_64) * ALIGN(h, VCODEC_DEC_ALIGNED_64); + + /* frame buffer */ + vsi->fb.y.dma_addr = fb->base_y.dma_addr; + if (plane == 1) + vsi->fb.c.dma_addr = fb->base_y.dma_addr + size; + else + vsi->fb.c.dma_addr = fb->base_c.dma_addr; + + /* reference buffers */ + vq = v4l2_m2m_get_vq(instance->ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + if (!vq) + return -EINVAL; + + /* get current output buffer */ + vb = &v4l2_m2m_next_dst_buf(instance->ctx->m2m_ctx)->vb2_buf; + if (!vb) + return -EINVAL; + + /* get buffer address from vb2buf */ + for (i = 0; i < V4L2_AV1_REFS_PER_FRAME; i++) { + struct vdec_av1_slice_fb *vref = &vsi->ref[i]; + + vb = vb2_find_buffer(vq, pfc->ref_idx[i]); + if (!vb) { + memset(vref, 0, sizeof(*vref)); + continue; + } + + vref->y.dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (plane == 1) + vref->c.dma_addr = vref->y.dma_addr + size; + else + vref->c.dma_addr = vb2_dma_contig_plane_dma_addr(vb, 1); + } + vsi->tile.dma_addr = lat_buf->tile_addr.dma_addr; + vsi->tile.size = lat_buf->tile_addr.size; + + return 0; +} + +static int vdec_av1_slice_setup_core(struct vdec_av1_slice_instance *instance, + struct vdec_fb *fb, + struct vdec_lat_buf *lat_buf, + struct vdec_av1_slice_pfc *pfc) +{ + struct vdec_av1_slice_vsi *vsi = &pfc->vsi; + int ret; + + ret = vdec_av1_slice_setup_core_to_dst_buf(instance, lat_buf); + if (ret) + return ret; + + ret = vdec_av1_slice_setup_core_buffer(instance, pfc, vsi, fb, lat_buf); + if (ret) + return ret; + + return 0; +} + +static int vdec_av1_slice_update_core(struct vdec_av1_slice_instance *instance, + struct vdec_lat_buf *lat_buf, + struct vdec_av1_slice_pfc *pfc) +{ + struct vdec_av1_slice_vsi *vsi = instance->core_vsi; + + mtk_vcodec_debug(instance, "frame %u Y_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[0], vsi->state.crc[1], + vsi->state.crc[2], vsi->state.crc[3]); + mtk_vcodec_debug(instance, "frame %u C_CRC %08x %08x %08x %08x\n", + pfc->seq, vsi->state.crc[8], vsi->state.crc[9], + vsi->state.crc[10], vsi->state.crc[11]); + + return 0; +} + +static int vdec_av1_slice_init(struct mtk_vcodec_ctx *ctx) +{ + struct vdec_av1_slice_instance *instance; + struct vdec_av1_slice_init_vsi *vsi; + int ret; + + instance = kzalloc(sizeof(*instance), GFP_KERNEL); + if (!instance) + return -ENOMEM; + + instance->ctx = ctx; + instance->vpu.id = SCP_IPI_VDEC_LAT; + instance->vpu.core_id = SCP_IPI_VDEC_CORE; + instance->vpu.ctx = ctx; + instance->vpu.codec_type = ctx->current_codec; + + ret = vpu_dec_init(&instance->vpu); + if (ret) { + mtk_vcodec_err(instance, "failed to init vpu dec, ret %d\n", ret); + goto error_vpu_init; + } + + /* init vsi and global flags */ + vsi = instance->vpu.vsi; + if (!vsi) { + mtk_vcodec_err(instance, "failed to get AV1 vsi\n"); + ret = -EINVAL; + goto error_vsi; + } + instance->init_vsi = vsi; + instance->core_vsi = mtk_vcodec_fw_map_dm_addr(ctx->dev->fw_handler, (u32)vsi->core_vsi); + + if (!instance->core_vsi) { + mtk_vcodec_err(instance, "failed to get AV1 core vsi\n"); + ret = -EINVAL; + goto error_vsi; + } + + if (vsi->vsi_size != sizeof(struct vdec_av1_slice_vsi)) + mtk_vcodec_err(instance, "remote vsi size 0x%x mismatch! expected: 0x%zx\n", + vsi->vsi_size, sizeof(struct vdec_av1_slice_vsi)); + + instance->irq_enabled = 1; + instance->inneracing_mode = IS_VDEC_INNER_RACING(instance->ctx->dev->dec_capability); + + mtk_vcodec_debug(instance, "vsi 0x%p core_vsi 0x%llx 0x%p, inneracing_mode %d\n", + vsi, vsi->core_vsi, instance->core_vsi, instance->inneracing_mode); + + ret = vdec_av1_slice_init_cdf_table(instance); + if (ret) + goto error_vsi; + + ret = vdec_av1_slice_init_iq_table(instance); + if (ret) + goto error_vsi; + + ctx->drv_handle = instance; + + return 0; +error_vsi: + vpu_dec_deinit(&instance->vpu); +error_vpu_init: + kfree(instance); + + return ret; +} + +static void vdec_av1_slice_deinit(void *h_vdec) +{ + struct vdec_av1_slice_instance *instance = h_vdec; + + if (!instance) + return; + mtk_vcodec_debug(instance, "h_vdec 0x%p\n", h_vdec); + vpu_dec_deinit(&instance->vpu); + vdec_av1_slice_free_working_buffer(instance); + vdec_msg_queue_deinit(&instance->ctx->msg_queue, instance->ctx); + kfree(instance); +} + +static int vdec_av1_slice_flush(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_av1_slice_instance *instance = h_vdec; + int i; + + mtk_vcodec_debug(instance, "flush ...\n"); + + vdec_msg_queue_wait_lat_buf_full(&instance->ctx->msg_queue); + + for (i = 0; i < AV1_MAX_FRAME_BUF_COUNT; i++) + vdec_av1_slice_clear_fb(&instance->slots.frame_info[i]); + + return vpu_dec_reset(&instance->vpu); +} + +static void vdec_av1_slice_get_pic_info(struct vdec_av1_slice_instance *instance) +{ + struct mtk_vcodec_ctx *ctx = instance->ctx; + u32 data[3]; + + mtk_vcodec_debug(instance, "w %u h %u\n", ctx->picinfo.pic_w, ctx->picinfo.pic_h); + + data[0] = ctx->picinfo.pic_w; + data[1] = ctx->picinfo.pic_h; + data[2] = ctx->capture_fourcc; + vpu_dec_get_param(&instance->vpu, data, 3, GET_PARAM_PIC_INFO); + + 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] = instance->vpu.fb_sz[0]; + ctx->picinfo.fb_sz[1] = instance->vpu.fb_sz[1]; +} + +static inline void vdec_av1_slice_get_dpb_size(struct vdec_av1_slice_instance *instance, + u32 *dpb_sz) +{ + /* refer av1 specification */ + *dpb_sz = V4L2_AV1_TOTAL_REFS_PER_FRAME + 1; +} + +static void vdec_av1_slice_get_crop_info(struct vdec_av1_slice_instance *instance, + struct v4l2_rect *cr) +{ + struct mtk_vcodec_ctx *ctx = instance->ctx; + + cr->left = 0; + cr->top = 0; + cr->width = ctx->picinfo.pic_w; + cr->height = ctx->picinfo.pic_h; + + mtk_vcodec_debug(instance, "l=%d, t=%d, w=%d, h=%d\n", + cr->left, cr->top, cr->width, cr->height); +} + +static int vdec_av1_slice_get_param(void *h_vdec, enum vdec_get_param_type type, void *out) +{ + struct vdec_av1_slice_instance *instance = h_vdec; + + switch (type) { + case GET_PARAM_PIC_INFO: + vdec_av1_slice_get_pic_info(instance); + break; + case GET_PARAM_DPB_SIZE: + vdec_av1_slice_get_dpb_size(instance, out); + break; + case GET_PARAM_CROP_INFO: + vdec_av1_slice_get_crop_info(instance, out); + break; + default: + mtk_vcodec_err(instance, "invalid get parameter type=%d\n", type); + return -EINVAL; + } + + return 0; +} + +static int vdec_av1_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_av1_slice_instance *instance = h_vdec; + struct vdec_lat_buf *lat_buf; + struct vdec_av1_slice_pfc *pfc; + struct vdec_av1_slice_vsi *vsi; + struct mtk_vcodec_ctx *ctx; + int ret; + + if (!instance || !instance->ctx) + return -EINVAL; + + ctx = instance->ctx; + /* init msgQ for the first time */ + if (vdec_msg_queue_init(&ctx->msg_queue, ctx, + vdec_av1_slice_core_decode, sizeof(*pfc))) { + mtk_vcodec_err(instance, "failed to init AV1 msg queue\n"); + return -ENOMEM; + } + + /* bs NULL means flush decoder */ + if (!bs) + return vdec_av1_slice_flush(h_vdec, bs, fb, res_chg); + + lat_buf = vdec_msg_queue_dqbuf(&ctx->msg_queue.lat_ctx); + if (!lat_buf) { + mtk_vcodec_err(instance, "failed to get AV1 lat buf\n"); + return -EAGAIN; + } + pfc = (struct vdec_av1_slice_pfc *)lat_buf->private_data; + if (!pfc) { + ret = -EINVAL; + goto err_free_fb_out; + } + vsi = &pfc->vsi; + + ret = vdec_av1_slice_setup_lat(instance, bs, lat_buf, pfc); + if (ret) { + mtk_vcodec_err(instance, "failed to setup AV1 lat ret %d\n", ret); + goto err_free_fb_out; + } + + vdec_av1_slice_vsi_to_remote(vsi, instance->vsi); + ret = vpu_dec_start(&instance->vpu, NULL, 0); + if (ret) { + mtk_vcodec_err(instance, "failed to dec AV1 ret %d\n", ret); + goto err_free_fb_out; + } + if (instance->inneracing_mode) + vdec_msg_queue_qbuf(&ctx->msg_queue.core_ctx, lat_buf); + + if (instance->irq_enabled) { + ret = mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, + MTK_VDEC_LAT0); + /* update remote vsi if decode timeout */ + if (ret) { + mtk_vcodec_err(instance, "AV1 Frame %d decode timeout %d\n", pfc->seq, ret); + WRITE_ONCE(instance->vsi->state.timeout, 1); + } + vpu_dec_end(&instance->vpu); + } + + vdec_av1_slice_vsi_from_remote(vsi, instance->vsi); + ret = vdec_av1_slice_update_lat(instance, lat_buf, pfc); + + /* LAT trans full, re-decode */ + if (ret == -EAGAIN) { + mtk_vcodec_err(instance, "AV1 Frame %d trans full\n", pfc->seq); + if (!instance->inneracing_mode) + vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); + return 0; + } + + /* LAT trans full, no more UBE or decode timeout */ + if (ret == -ENOMEM || vsi->state.timeout) { + mtk_vcodec_err(instance, "AV1 Frame %d insufficient buffer or timeout\n", pfc->seq); + if (!instance->inneracing_mode) + vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); + return -EBUSY; + } + vsi->trans.dma_addr_end += ctx->msg_queue.wdma_addr.dma_addr; + mtk_vcodec_debug(instance, "lat dma 1 0x%pad 0x%pad\n", + &pfc->vsi.trans.dma_addr, &pfc->vsi.trans.dma_addr_end); + + vdec_msg_queue_update_ube_wptr(&ctx->msg_queue, vsi->trans.dma_addr_end); + + if (!instance->inneracing_mode) + vdec_msg_queue_qbuf(&ctx->msg_queue.core_ctx, lat_buf); + memcpy(&instance->slots, &vsi->slots, sizeof(instance->slots)); + + return 0; + +err_free_fb_out: + vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); + + if (pfc) + mtk_vcodec_err(instance, "slice dec number: %d err: %d", pfc->seq, ret); + + return ret; +} + +static int vdec_av1_slice_core_decode(struct vdec_lat_buf *lat_buf) +{ + struct vdec_av1_slice_instance *instance; + struct vdec_av1_slice_pfc *pfc; + struct mtk_vcodec_ctx *ctx = NULL; + struct vdec_fb *fb = NULL; + int ret = -EINVAL; + + if (!lat_buf) + return -EINVAL; + + pfc = lat_buf->private_data; + ctx = lat_buf->ctx; + if (!pfc || !ctx) + return -EINVAL; + + instance = ctx->drv_handle; + if (!instance) + goto err; + + fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx); + if (!fb) { + ret = -EBUSY; + goto err; + } + + ret = vdec_av1_slice_setup_core(instance, fb, lat_buf, pfc); + if (ret) { + mtk_vcodec_err(instance, "vdec_av1_slice_setup_core\n"); + goto err; + } + vdec_av1_slice_vsi_to_remote(&pfc->vsi, instance->core_vsi); + ret = vpu_dec_core(&instance->vpu); + if (ret) { + mtk_vcodec_err(instance, "vpu_dec_core\n"); + goto err; + } + + if (instance->irq_enabled) { + ret = mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, + MTK_VDEC_CORE); + /* update remote vsi if decode timeout */ + if (ret) { + mtk_vcodec_err(instance, "AV1 frame %d core timeout\n", pfc->seq); + WRITE_ONCE(instance->vsi->state.timeout, 1); + } + vpu_dec_core_end(&instance->vpu); + } + + ret = vdec_av1_slice_update_core(instance, lat_buf, pfc); + if (ret) { + mtk_vcodec_err(instance, "vdec_av1_slice_update_core\n"); + goto err; + } + + mtk_vcodec_debug(instance, "core dma_addr_end 0x%pad\n", + &instance->core_vsi->trans.dma_addr_end); + vdec_msg_queue_update_ube_rptr(&ctx->msg_queue, instance->core_vsi->trans.dma_addr_end); + + ctx->dev->vdec_pdata->cap_to_disp(ctx, 0, lat_buf->src_buf_req); + + return 0; + +err: + /* always update read pointer */ + vdec_msg_queue_update_ube_rptr(&ctx->msg_queue, pfc->vsi.trans.dma_addr_end); + + if (fb) + ctx->dev->vdec_pdata->cap_to_disp(ctx, 1, lat_buf->src_buf_req); + + return ret; +} + +const struct vdec_common_if vdec_av1_slice_lat_if = { + .init = vdec_av1_slice_init, + .decode = vdec_av1_slice_lat_decode, + .get_param = vdec_av1_slice_get_param, + .deinit = vdec_av1_slice_deinit, +}; diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c index 999ce7ee5fdc..a7e8e3257b7f 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_multi_if.c @@ -596,7 +596,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx); if (!lat_buf) { - mtk_vcodec_err(inst, "failed to get lat buffer"); + mtk_vcodec_debug(inst, "failed to get lat buffer"); return -EAGAIN; } share_info = lat_buf->private_data; @@ -672,7 +672,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { memcpy(&share_info->h264_slice_params, &inst->vsi->h264_slice_params, sizeof(share_info->h264_slice_params)); - vdec_msg_queue_qbuf(&inst->ctx->dev->msg_queue_core_ctx, lat_buf); + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); } /* wait decoder done interrupt */ @@ -698,7 +698,7 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { memcpy(&share_info->h264_slice_params, &inst->vsi->h264_slice_params, sizeof(share_info->h264_slice_params)); - vdec_msg_queue_qbuf(&inst->ctx->dev->msg_queue_core_ctx, lat_buf); + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); } mtk_vcodec_debug(inst, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], inst->vsi->dec.crc[2]); diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_hevc_req_multi_if.c new file mode 100644 index 000000000000..1e6ab138b0bb --- /dev/null +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_hevc_req_multi_if.c @@ -0,0 +1,1097 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 MediaTek Inc. + * Author: Yunfei Dong <yunfei.dong@mediatek.com> + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <media/videobuf2-dma-contig.h> + +#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" + +/* the size used to store hevc wrap information */ +#define VDEC_HEVC_WRAP_SZ (532 * SZ_1K) + +#define HEVC_MAX_MV_NUM 32 + +/* get used parameters for sps/pps */ +#define GET_HEVC_VDEC_FLAG(cond, flag) \ + { dst_param->cond = ((src_param->flags & (flag)) ? (1) : (0)); } +#define GET_HEVC_VDEC_PARAM(param) \ + { dst_param->param = src_param->param; } + +/** + * enum vdec_hevc_core_dec_err_type - core decode error type + * + * @TRANS_BUFFER_FULL: trans buffer is full + * @SLICE_HEADER_FULL: slice header buffer is full + */ +enum vdec_hevc_core_dec_err_type { + TRANS_BUFFER_FULL = 1, + SLICE_HEADER_FULL, +}; + +/** + * struct mtk_hevc_dpb_info - hevc dpb information + * + * @y_dma_addr: Y plane physical address + * @c_dma_addr: CbCr plane physical address + * @reference_flag: reference picture flag (short/long term reference picture) + * @field: field picture flag + */ +struct mtk_hevc_dpb_info { + dma_addr_t y_dma_addr; + dma_addr_t c_dma_addr; + int reference_flag; + int field; +}; + +/* + * struct mtk_hevc_sps_param - parameters for sps + */ +struct mtk_hevc_sps_param { + unsigned char video_parameter_set_id; + unsigned char seq_parameter_set_id; + unsigned short pic_width_in_luma_samples; + unsigned short pic_height_in_luma_samples; + unsigned char bit_depth_luma_minus8; + unsigned char bit_depth_chroma_minus8; + unsigned char log2_max_pic_order_cnt_lsb_minus4; + unsigned char sps_max_dec_pic_buffering_minus1; + unsigned char sps_max_num_reorder_pics; + unsigned char sps_max_latency_increase_plus1; + unsigned char log2_min_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_luma_coding_block_size; + unsigned char log2_min_luma_transform_block_size_minus2; + unsigned char log2_diff_max_min_luma_transform_block_size; + unsigned char max_transform_hierarchy_depth_inter; + unsigned char max_transform_hierarchy_depth_intra; + unsigned char pcm_sample_bit_depth_luma_minus1; + unsigned char pcm_sample_bit_depth_chroma_minus1; + unsigned char log2_min_pcm_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_pcm_luma_coding_block_size; + unsigned char num_short_term_ref_pic_sets; + unsigned char num_long_term_ref_pics_sps; + unsigned char chroma_format_idc; + unsigned char sps_max_sub_layers_minus1; + unsigned char separate_colour_plane; + unsigned char scaling_list_enabled; + unsigned char amp_enabled; + unsigned char sample_adaptive_offset; + unsigned char pcm_enabled; + unsigned char pcm_loop_filter_disabled; + unsigned char long_term_ref_pics_enabled; + unsigned char sps_temporal_mvp_enabled; + unsigned char strong_intra_smoothing_enabled; + unsigned char reserved[5]; +}; + +/* + * struct mtk_hevc_pps_param - parameters for pps + */ +struct mtk_hevc_pps_param { + unsigned char pic_parameter_set_id; + unsigned char num_extra_slice_header_bits; + unsigned char num_ref_idx_l0_default_active_minus1; + unsigned char num_ref_idx_l1_default_active_minus1; + char init_qp_minus26; + unsigned char diff_cu_qp_delta_depth; + char pps_cb_qp_offset; + char pps_cr_qp_offset; + unsigned char num_tile_columns_minus1; + unsigned char num_tile_rows_minus1; + unsigned char column_width_minus1[20]; + unsigned char row_height_minus1[22]; + char pps_beta_offset_div2; + char pps_tc_offset_div2; + unsigned char log2_parallel_merge_level_minus2; + char dependent_slice_segment_enabled; + char output_flag_present; + char sign_data_hiding_enabled; + char cabac_init_present; + char constrained_intra_pred; + char transform_skip_enabled; + char cu_qp_delta_enabled; + char pps_slice_chroma_qp_offsets_present; + char weighted_pred; + char weighted_bipred; + char transquant_bypass_enabled; + char pps_flag_tiles_enabled; + char entropy_coding_sync_enabled; + char loop_filter_across_tiles_enabled; + char pps_loop_filter_across_slices_enabled; + char deblocking_filter_override_enabled; + char pps_disable_deflocking_filter; + char lists_modification_present; + char slice_segment_header_extersion_present; + char deblocking_filter_control_present; + char uniform_spacing; + char reserved[6]; +}; + +/* + * struct mtk_hevc_slice_header_param - parameters for slice header + */ +struct mtk_hevc_slice_header_param { + unsigned int slice_type; + unsigned int num_active_ref_layer_pics; + int slice_qp; + int slice_qp_delta_cb; + int slice_qp_delta_cr; + int num_ref_idx[3]; + unsigned int col_ref_idx; + unsigned int five_minus_max_num_merge_cand; + int slice_deblocking_filter_beta_offset_div2; + int slice_deblocking_filter_tc_offset_div2; + unsigned char sao_enable_flag; + unsigned char sao_enable_flag_chroma; + unsigned char cabac_init_flag; + unsigned char slice_tmvp_flags_present; + unsigned char col_from_l0_flag; + unsigned char mvd_l1_zero_flag; + unsigned char slice_loop_filter_across_slices_enabled_flag; + unsigned char deblocking_filter_disable_flag; + unsigned int slice_reg0; + unsigned int slice_reg1; + unsigned int slice_reg2; + unsigned int num_rps_curr_temp_list; + unsigned int ref_list_mode; + int str_num_delta_pocs; + int str_num_negtive_pos_pics; + int num_long_term; + int num_long_term_sps; + unsigned int max_cu_width; + unsigned int max_cu_height; + unsigned int num_entry_point_offsets; + unsigned int last_lcu_x_in_tile[17]; + unsigned int last_lcu_y_in_tile[17]; + unsigned char nal_unit_type; +}; + +/* + * struct slice_api_hevc_scaling_matrix - parameters for scaling list + */ +struct slice_api_hevc_scaling_matrix { + unsigned char scaling_list_4x4[6][16]; + unsigned char scaling_list_8x8[6][64]; + unsigned char scaling_list_16x16[6][64]; + unsigned char scaling_list_32x32[2][64]; + unsigned char scaling_list_dc_coef_16x16[6]; + unsigned char scaling_list_dc_coef_32x32[2]; +}; + +/* + * struct slice_hevc_dpb_entry - each dpb information + */ +struct slice_hevc_dpb_entry { + u64 timestamp; + unsigned char flags; + unsigned char field_pic; + int pic_order_cnt_val; +}; + +/* + * struct slice_api_hevc_decode_param - parameters for decode. + */ +struct slice_api_hevc_decode_param { + struct slice_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; + int pic_order_cnt_val; + unsigned short short_term_ref_pic_set_size; + unsigned short long_term_ref_pic_set_size; + unsigned char num_active_dpb_entries; + unsigned char num_poc_st_curr_before; + unsigned char num_poc_st_curr_after; + unsigned char num_poc_lt_curr; + unsigned char poc_st_curr_before[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; + unsigned char poc_st_curr_after[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; + unsigned char poc_lt_curr[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; + unsigned char num_delta_pocs_of_ref_rps_idx; + int flags; +}; + +/** + * struct hevc_fb - hevc 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 hevc_fb { + u64 vdec_fb_va; + u64 y_fb_dma; + u64 c_fb_dma; + s32 poc; + u32 reserved; +}; + +/** + * struct vdec_hevc_slice_lat_dec_param - parameters for decode current frame + * + * @sps: hevc sps syntax parameters + * @pps: hevc pps syntax parameters + * @slice_header: hevc slice header syntax parameters + * @scaling_matrix: hevc scaling list parameters + * @decode_params: decoder parameters of each frame used for hardware decode + * @hevc_dpb_info: dpb reference list + */ +struct vdec_hevc_slice_lat_dec_param { + struct mtk_hevc_sps_param sps; + struct mtk_hevc_pps_param pps; + struct mtk_hevc_slice_header_param slice_header; + struct slice_api_hevc_scaling_matrix scaling_matrix; + struct slice_api_hevc_decode_param decode_params; + struct mtk_hevc_dpb_info hevc_dpb_info[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]; +}; + +/** + * struct vdec_hevc_slice_info - decode information + * + * @wdma_end_addr_offset: wdma end address offset + * @timeout: Decode timeout: 1 timeout, 0 no timeount + * @vdec_fb_va: VDEC frame buffer struct virtual address + * @crc: Used to check whether hardware's status is right + */ +struct vdec_hevc_slice_info { + u64 wdma_end_addr_offset; + u64 timeout; + u64 vdec_fb_va; + u32 crc[8]; +}; + +/* + * struct vdec_hevc_slice_mem - memory address and size + */ +struct vdec_hevc_slice_mem { + union { + u64 buf; + dma_addr_t dma_addr; + }; + union { + size_t size; + dma_addr_t dma_addr_end; + u64 padding; + }; +}; + +/** + * struct vdec_hevc_slice_fb - frame buffer for decoding + * @y: current y buffer address info + * @c: current c buffer address info + */ +struct vdec_hevc_slice_fb { + struct vdec_hevc_slice_mem y; + struct vdec_hevc_slice_mem c; +}; + +/** + * struct vdec_hevc_slice_vsi - shared memory for decode information exchange + * between SCP and Host. + * + * @bs: input buffer info + * + * @ube: ube buffer + * @trans: transcoded buffer + * @err_map: err map buffer + * @slice_bc: slice bc buffer + * @wrap: temp buffer + * + * @fb: current y/c buffer + * @mv_buf_dma: HW working motion vector buffer + * @dec: decode information (AP-R, VPU-W) + * @hevc_slice_params: decode parameters for hw used + */ +struct vdec_hevc_slice_vsi { + /* used in LAT stage */ + struct vdec_hevc_slice_mem bs; + + struct vdec_hevc_slice_mem ube; + struct vdec_hevc_slice_mem trans; + struct vdec_hevc_slice_mem err_map; + struct vdec_hevc_slice_mem slice_bc; + struct vdec_hevc_slice_mem wrap; + + struct vdec_hevc_slice_fb fb; + struct vdec_hevc_slice_mem mv_buf_dma[HEVC_MAX_MV_NUM]; + struct vdec_hevc_slice_info dec; + struct vdec_hevc_slice_lat_dec_param hevc_slice_params; +}; + +/** + * struct vdec_hevc_slice_share_info - shared information used to exchange + * message between lat and core + * + * @sps: sequence header information from user space + * @dec_params: decoder params from user space + * @hevc_slice_params: decoder params used for hardware + * @trans: trans buffer dma address + */ +struct vdec_hevc_slice_share_info { + struct v4l2_ctrl_hevc_sps sps; + struct v4l2_ctrl_hevc_decode_params dec_params; + struct vdec_hevc_slice_lat_dec_param hevc_slice_params; + struct vdec_hevc_slice_mem trans; +}; + +/** + * struct vdec_hevc_slice_inst - hevc decoder instance + * + * @slice_dec_num: how many picture be decoded + * @ctx: point to mtk_vcodec_ctx + * @mv_buf: HW working motion vector buffer + * @vpu: VPU instance + * @vsi: vsi used for lat + * @vsi_core: vsi used for core + * @wrap_addr: wrap address used for hevc + * + * @hevc_slice_param: the parameters that hardware use to decode + * + * @resolution_changed: resolution changed + * @realloc_mv_buf: reallocate mv buffer + * @cap_num_planes: number of capture queue plane + */ +struct vdec_hevc_slice_inst { + unsigned int slice_dec_num; + struct mtk_vcodec_ctx *ctx; + struct mtk_vcodec_mem mv_buf[HEVC_MAX_MV_NUM]; + struct vdec_vpu_inst vpu; + struct vdec_hevc_slice_vsi *vsi; + struct vdec_hevc_slice_vsi *vsi_core; + struct mtk_vcodec_mem wrap_addr; + + struct vdec_hevc_slice_lat_dec_param hevc_slice_param; + + unsigned int resolution_changed; + unsigned int realloc_mv_buf; + unsigned int cap_num_planes; +}; + +static unsigned int vdec_hevc_get_mv_buf_size(unsigned int width, unsigned int height) +{ + const unsigned int unit_size = (width / 16) * (height / 16) + 8; + + return 64 * unit_size; +} + +static void *vdec_hevc_get_ctrl_ptr(struct mtk_vcodec_ctx *ctx, int id) +{ + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&ctx->ctrl_hdl, id); + + if (!ctrl) + return ERR_PTR(-EINVAL); + + return ctrl->p_cur.p; +} + +static void vdec_hevc_fill_dpb_info(struct mtk_vcodec_ctx *ctx, + struct slice_api_hevc_decode_param *decode_params, + struct mtk_hevc_dpb_info *hevc_dpb_info) +{ + const struct slice_hevc_dpb_entry *dpb; + struct vb2_queue *vq; + struct vb2_buffer *vb; + int index; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); + for (index = 0; index < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; index++) { + dpb = &decode_params->dpb[index]; + if (index >= decode_params->num_active_dpb_entries) + continue; + + vb = vb2_find_buffer(vq, dpb->timestamp); + if (!vb) { + dev_err(&ctx->dev->plat_dev->dev, + "Reference invalid: dpb_index(%d) timestamp(%lld)", + index, dpb->timestamp); + continue; + } + + hevc_dpb_info[index].field = dpb->field_pic; + + hevc_dpb_info[index].y_dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 2) + hevc_dpb_info[index].c_dma_addr = vb2_dma_contig_plane_dma_addr(vb, 1); + else + hevc_dpb_info[index].c_dma_addr = + hevc_dpb_info[index].y_dma_addr + ctx->picinfo.fb_sz[0]; + } +} + +static void vdec_hevc_copy_sps_params(struct mtk_hevc_sps_param *dst_param, + const struct v4l2_ctrl_hevc_sps *src_param) +{ + GET_HEVC_VDEC_PARAM(video_parameter_set_id); + GET_HEVC_VDEC_PARAM(seq_parameter_set_id); + GET_HEVC_VDEC_PARAM(pic_width_in_luma_samples); + GET_HEVC_VDEC_PARAM(pic_height_in_luma_samples); + GET_HEVC_VDEC_PARAM(bit_depth_luma_minus8); + GET_HEVC_VDEC_PARAM(bit_depth_chroma_minus8); + GET_HEVC_VDEC_PARAM(log2_max_pic_order_cnt_lsb_minus4); + GET_HEVC_VDEC_PARAM(sps_max_dec_pic_buffering_minus1); + GET_HEVC_VDEC_PARAM(sps_max_num_reorder_pics); + GET_HEVC_VDEC_PARAM(sps_max_latency_increase_plus1); + GET_HEVC_VDEC_PARAM(log2_min_luma_coding_block_size_minus3); + GET_HEVC_VDEC_PARAM(log2_diff_max_min_luma_coding_block_size); + GET_HEVC_VDEC_PARAM(log2_min_luma_transform_block_size_minus2); + GET_HEVC_VDEC_PARAM(log2_diff_max_min_luma_transform_block_size); + GET_HEVC_VDEC_PARAM(max_transform_hierarchy_depth_inter); + GET_HEVC_VDEC_PARAM(max_transform_hierarchy_depth_intra); + GET_HEVC_VDEC_PARAM(pcm_sample_bit_depth_luma_minus1); + GET_HEVC_VDEC_PARAM(pcm_sample_bit_depth_chroma_minus1); + GET_HEVC_VDEC_PARAM(log2_min_pcm_luma_coding_block_size_minus3); + GET_HEVC_VDEC_PARAM(log2_diff_max_min_pcm_luma_coding_block_size); + GET_HEVC_VDEC_PARAM(num_short_term_ref_pic_sets); + GET_HEVC_VDEC_PARAM(num_long_term_ref_pics_sps); + GET_HEVC_VDEC_PARAM(chroma_format_idc); + GET_HEVC_VDEC_PARAM(sps_max_sub_layers_minus1); + + GET_HEVC_VDEC_FLAG(separate_colour_plane, + V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE); + GET_HEVC_VDEC_FLAG(scaling_list_enabled, + V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED); + GET_HEVC_VDEC_FLAG(amp_enabled, + V4L2_HEVC_SPS_FLAG_AMP_ENABLED); + GET_HEVC_VDEC_FLAG(sample_adaptive_offset, + V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET); + GET_HEVC_VDEC_FLAG(pcm_enabled, + V4L2_HEVC_SPS_FLAG_PCM_ENABLED); + GET_HEVC_VDEC_FLAG(pcm_loop_filter_disabled, + V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED); + GET_HEVC_VDEC_FLAG(long_term_ref_pics_enabled, + V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT); + GET_HEVC_VDEC_FLAG(sps_temporal_mvp_enabled, + V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED); + GET_HEVC_VDEC_FLAG(strong_intra_smoothing_enabled, + V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED); +} + +static void vdec_hevc_copy_pps_params(struct mtk_hevc_pps_param *dst_param, + const struct v4l2_ctrl_hevc_pps *src_param) +{ + int i; + + GET_HEVC_VDEC_PARAM(pic_parameter_set_id); + GET_HEVC_VDEC_PARAM(num_extra_slice_header_bits); + GET_HEVC_VDEC_PARAM(num_ref_idx_l0_default_active_minus1); + GET_HEVC_VDEC_PARAM(num_ref_idx_l1_default_active_minus1); + GET_HEVC_VDEC_PARAM(init_qp_minus26); + GET_HEVC_VDEC_PARAM(diff_cu_qp_delta_depth); + GET_HEVC_VDEC_PARAM(pps_cb_qp_offset); + GET_HEVC_VDEC_PARAM(pps_cr_qp_offset); + GET_HEVC_VDEC_PARAM(num_tile_columns_minus1); + GET_HEVC_VDEC_PARAM(num_tile_rows_minus1); + GET_HEVC_VDEC_PARAM(init_qp_minus26); + GET_HEVC_VDEC_PARAM(diff_cu_qp_delta_depth); + GET_HEVC_VDEC_PARAM(pic_parameter_set_id); + GET_HEVC_VDEC_PARAM(num_extra_slice_header_bits); + GET_HEVC_VDEC_PARAM(num_ref_idx_l0_default_active_minus1); + GET_HEVC_VDEC_PARAM(num_ref_idx_l1_default_active_minus1); + GET_HEVC_VDEC_PARAM(pps_beta_offset_div2); + GET_HEVC_VDEC_PARAM(pps_tc_offset_div2); + GET_HEVC_VDEC_PARAM(log2_parallel_merge_level_minus2); + + for (i = 0; i < ARRAY_SIZE(src_param->column_width_minus1); i++) + GET_HEVC_VDEC_PARAM(column_width_minus1[i]); + for (i = 0; i < ARRAY_SIZE(src_param->row_height_minus1); i++) + GET_HEVC_VDEC_PARAM(row_height_minus1[i]); + + GET_HEVC_VDEC_FLAG(dependent_slice_segment_enabled, + V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED); + GET_HEVC_VDEC_FLAG(output_flag_present, + V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT); + GET_HEVC_VDEC_FLAG(sign_data_hiding_enabled, + V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED); + GET_HEVC_VDEC_FLAG(cabac_init_present, + V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT); + GET_HEVC_VDEC_FLAG(constrained_intra_pred, + V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED); + GET_HEVC_VDEC_FLAG(transform_skip_enabled, + V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED); + GET_HEVC_VDEC_FLAG(cu_qp_delta_enabled, + V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED); + GET_HEVC_VDEC_FLAG(pps_slice_chroma_qp_offsets_present, + V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT); + GET_HEVC_VDEC_FLAG(weighted_pred, + V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED); + GET_HEVC_VDEC_FLAG(weighted_bipred, + V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED); + GET_HEVC_VDEC_FLAG(transquant_bypass_enabled, + V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED); + GET_HEVC_VDEC_FLAG(pps_flag_tiles_enabled, + V4L2_HEVC_PPS_FLAG_TILES_ENABLED); + GET_HEVC_VDEC_FLAG(entropy_coding_sync_enabled, + V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED); + GET_HEVC_VDEC_FLAG(loop_filter_across_tiles_enabled, + V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED); + GET_HEVC_VDEC_FLAG(pps_loop_filter_across_slices_enabled, + V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED); + GET_HEVC_VDEC_FLAG(deblocking_filter_override_enabled, + V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED); + GET_HEVC_VDEC_FLAG(pps_disable_deflocking_filter, + V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER); + GET_HEVC_VDEC_FLAG(lists_modification_present, + V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT); + GET_HEVC_VDEC_FLAG(slice_segment_header_extersion_present, + V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT); + GET_HEVC_VDEC_FLAG(deblocking_filter_control_present, + V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT); + GET_HEVC_VDEC_FLAG(uniform_spacing, + V4L2_HEVC_PPS_FLAG_UNIFORM_SPACING); +} + +static void vdec_hevc_copy_scaling_matrix(struct slice_api_hevc_scaling_matrix *dst_matrix, + const struct v4l2_ctrl_hevc_scaling_matrix *src_matrix) +{ + memcpy(dst_matrix, src_matrix, sizeof(*src_matrix)); +} + +static void +vdec_hevc_copy_decode_params(struct slice_api_hevc_decode_param *dst_param, + const struct v4l2_ctrl_hevc_decode_params *src_param, + const struct v4l2_hevc_dpb_entry dpb[V4L2_HEVC_DPB_ENTRIES_NUM_MAX]) +{ + struct slice_hevc_dpb_entry *dst_entry; + const struct v4l2_hevc_dpb_entry *src_entry; + int i; + + for (i = 0; i < ARRAY_SIZE(dst_param->dpb); i++) { + dst_entry = &dst_param->dpb[i]; + src_entry = &dpb[i]; + + dst_entry->timestamp = src_entry->timestamp; + dst_entry->flags = src_entry->flags; + dst_entry->field_pic = src_entry->field_pic; + dst_entry->pic_order_cnt_val = src_entry->pic_order_cnt_val; + + GET_HEVC_VDEC_PARAM(poc_st_curr_before[i]); + GET_HEVC_VDEC_PARAM(poc_st_curr_after[i]); + GET_HEVC_VDEC_PARAM(poc_lt_curr[i]); + } + + GET_HEVC_VDEC_PARAM(pic_order_cnt_val); + GET_HEVC_VDEC_PARAM(short_term_ref_pic_set_size); + GET_HEVC_VDEC_PARAM(long_term_ref_pic_set_size); + GET_HEVC_VDEC_PARAM(num_active_dpb_entries); + GET_HEVC_VDEC_PARAM(num_poc_st_curr_before); + GET_HEVC_VDEC_PARAM(num_poc_st_curr_after); + GET_HEVC_VDEC_PARAM(num_delta_pocs_of_ref_rps_idx); + GET_HEVC_VDEC_PARAM(num_poc_lt_curr); + GET_HEVC_VDEC_PARAM(flags); +} + +static int vdec_hevc_slice_fill_decode_parameters(struct vdec_hevc_slice_inst *inst, + struct vdec_hevc_slice_share_info *share_info) +{ + struct vdec_hevc_slice_lat_dec_param *slice_param = &inst->vsi->hevc_slice_params; + const struct v4l2_ctrl_hevc_decode_params *dec_params; + const struct v4l2_ctrl_hevc_scaling_matrix *src_matrix; + const struct v4l2_ctrl_hevc_sps *sps; + const struct v4l2_ctrl_hevc_pps *pps; + + dec_params = + vdec_hevc_get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_HEVC_DECODE_PARAMS); + if (IS_ERR(dec_params)) + return PTR_ERR(dec_params); + + src_matrix = + vdec_hevc_get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_HEVC_SCALING_MATRIX); + if (IS_ERR(src_matrix)) + return PTR_ERR(src_matrix); + + sps = vdec_hevc_get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_HEVC_SPS); + if (IS_ERR(sps)) + return PTR_ERR(sps); + + pps = vdec_hevc_get_ctrl_ptr(inst->ctx, V4L2_CID_STATELESS_HEVC_PPS); + if (IS_ERR(pps)) + return PTR_ERR(pps); + + vdec_hevc_copy_sps_params(&slice_param->sps, sps); + vdec_hevc_copy_pps_params(&slice_param->pps, pps); + vdec_hevc_copy_scaling_matrix(&slice_param->scaling_matrix, src_matrix); + + memcpy(&share_info->sps, sps, sizeof(*sps)); + memcpy(&share_info->dec_params, dec_params, sizeof(*dec_params)); + + slice_param->decode_params.num_poc_st_curr_before = dec_params->num_poc_st_curr_before; + slice_param->decode_params.num_poc_st_curr_after = dec_params->num_poc_st_curr_after; + slice_param->decode_params.num_poc_lt_curr = dec_params->num_poc_lt_curr; + slice_param->decode_params.num_delta_pocs_of_ref_rps_idx = + dec_params->num_delta_pocs_of_ref_rps_idx; + + return 0; +} + +static void vdec_hevc_slice_fill_decode_reflist(struct vdec_hevc_slice_inst *inst, + struct vdec_hevc_slice_lat_dec_param *slice_param, + struct vdec_hevc_slice_share_info *share_info) +{ + struct v4l2_ctrl_hevc_decode_params *dec_params = &share_info->dec_params; + + vdec_hevc_copy_decode_params(&slice_param->decode_params, dec_params, + share_info->dec_params.dpb); + + vdec_hevc_fill_dpb_info(inst->ctx, &slice_param->decode_params, + slice_param->hevc_dpb_info); +} + +static int vdec_hevc_slice_alloc_mv_buf(struct vdec_hevc_slice_inst *inst, + struct vdec_pic_info *pic) +{ + unsigned int buf_sz = vdec_hevc_get_mv_buf_size(pic->buf_w, pic->buf_h); + struct mtk_vcodec_mem *mem; + int i, err; + + mtk_v4l2_debug(3, "allocate mv buffer size = 0x%x", buf_sz); + for (i = 0; i < HEVC_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; + } + } + + return 0; +} + +static void vdec_hevc_slice_free_mv_buf(struct vdec_hevc_slice_inst *inst) +{ + int i; + struct mtk_vcodec_mem *mem; + + for (i = 0; i < HEVC_MAX_MV_NUM; i++) { + mem = &inst->mv_buf[i]; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + } +} + +static void vdec_hevc_slice_get_pic_info(struct vdec_hevc_slice_inst *inst) +{ + struct mtk_vcodec_ctx *ctx = inst->ctx; + u32 data[3]; + + data[0] = ctx->picinfo.pic_w; + data[1] = ctx->picinfo.pic_h; + data[2] = ctx->capture_fourcc; + vpu_dec_get_param(&inst->vpu, data, 3, GET_PARAM_PIC_INFO); + + 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] = inst->vpu.fb_sz[0]; + ctx->picinfo.fb_sz[1] = inst->vpu.fb_sz[1]; + inst->cap_num_planes = + ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes; + + 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->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->realloc_mv_buf = true; + + mtk_v4l2_debug(1, "resChg: (%d %d) : old(%d, %d) -> new(%d, %d)", + inst->resolution_changed, + inst->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 vdec_hevc_slice_get_crop_info(struct vdec_hevc_slice_inst *inst, + struct v4l2_rect *cr) +{ + cr->left = 0; + cr->top = 0; + cr->width = inst->ctx->picinfo.pic_w; + cr->height = inst->ctx->picinfo.pic_h; + + mtk_vcodec_debug(inst, "l=%d, t=%d, w=%d, h=%d", + cr->left, cr->top, cr->width, cr->height); +} + +static int vdec_hevc_slice_setup_lat_buffer(struct vdec_hevc_slice_inst *inst, + struct mtk_vcodec_mem *bs, + struct vdec_lat_buf *lat_buf, + bool *res_chg) +{ + struct mtk_vcodec_mem *mem; + struct mtk_video_dec_buf *src_buf_info; + struct vdec_hevc_slice_share_info *share_info; + int i, err; + + inst->vsi->bs.dma_addr = (u64)bs->dma_addr; + inst->vsi->bs.size = bs->size; + + src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer); + lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req; + v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true); + + *res_chg = inst->resolution_changed; + if (inst->resolution_changed) { + mtk_vcodec_debug(inst, "- resolution changed -"); + if (inst->realloc_mv_buf) { + err = vdec_hevc_slice_alloc_mv_buf(inst, &inst->ctx->picinfo); + inst->realloc_mv_buf = false; + if (err) + return err; + } + inst->resolution_changed = false; + } + + for (i = 0; i < HEVC_MAX_MV_NUM; i++) { + mem = &inst->mv_buf[i]; + inst->vsi->mv_buf_dma[i].dma_addr = mem->dma_addr; + inst->vsi->mv_buf_dma[i].size = mem->size; + } + + inst->vsi->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr; + inst->vsi->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size; + + inst->vsi->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr; + inst->vsi->err_map.size = lat_buf->wdma_err_addr.size; + + inst->vsi->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr; + inst->vsi->slice_bc.size = lat_buf->slice_bc_addr.size; + + inst->vsi->trans.dma_addr_end = inst->ctx->msg_queue.wdma_rptr_addr; + inst->vsi->trans.dma_addr = inst->ctx->msg_queue.wdma_wptr_addr; + + share_info = lat_buf->private_data; + share_info->trans.dma_addr = inst->vsi->trans.dma_addr; + share_info->trans.dma_addr_end = inst->vsi->trans.dma_addr_end; + + mtk_vcodec_debug(inst, "lat: ube addr/size(0x%llx 0x%llx) err:0x%llx", + inst->vsi->ube.buf, + inst->vsi->ube.padding, + inst->vsi->err_map.buf); + + mtk_vcodec_debug(inst, "slice addr/size(0x%llx 0x%llx) trans start/end((0x%llx 0x%llx))", + inst->vsi->slice_bc.buf, + inst->vsi->slice_bc.padding, + inst->vsi->trans.buf, + inst->vsi->trans.padding); + + return 0; +} + +static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst, + struct vdec_hevc_slice_share_info *share_info, + struct vdec_lat_buf *lat_buf) +{ + struct mtk_vcodec_mem *mem; + struct mtk_vcodec_ctx *ctx = inst->ctx; + struct vb2_v4l2_buffer *vb2_v4l2; + struct vdec_fb *fb; + u64 y_fb_dma, c_fb_dma; + int i; + + fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx); + if (!fb) { + mtk_vcodec_err(inst, "fb buffer is NULL"); + return -EBUSY; + } + + y_fb_dma = (u64)fb->base_y.dma_addr; + if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1) + c_fb_dma = + y_fb_dma + inst->ctx->picinfo.buf_w * inst->ctx->picinfo.buf_h; + else + c_fb_dma = (u64)fb->base_c.dma_addr; + + mtk_vcodec_debug(inst, "[hevc-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, + c_fb_dma); + + inst->vsi_core->fb.y.dma_addr = y_fb_dma; + inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[0]; + inst->vsi_core->fb.c.dma_addr = c_fb_dma; + inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[1]; + + inst->vsi_core->dec.vdec_fb_va = (unsigned long)fb; + + inst->vsi_core->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr; + inst->vsi_core->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size; + + inst->vsi_core->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr; + inst->vsi_core->err_map.size = lat_buf->wdma_err_addr.size; + + inst->vsi_core->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr; + inst->vsi_core->slice_bc.size = lat_buf->slice_bc_addr.size; + + inst->vsi_core->trans.dma_addr = share_info->trans.dma_addr; + inst->vsi_core->trans.dma_addr_end = share_info->trans.dma_addr_end; + + inst->vsi_core->wrap.dma_addr = inst->wrap_addr.dma_addr; + inst->vsi_core->wrap.size = inst->wrap_addr.size; + + for (i = 0; i < HEVC_MAX_MV_NUM; i++) { + mem = &inst->mv_buf[i]; + inst->vsi_core->mv_buf_dma[i].dma_addr = mem->dma_addr; + inst->vsi_core->mv_buf_dma[i].size = mem->size; + } + + vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true); + + return 0; +} + +static int vdec_hevc_slice_init(struct mtk_vcodec_ctx *ctx) +{ + struct vdec_hevc_slice_inst *inst; + int err, vsi_size; + + inst = kzalloc(sizeof(*inst), GFP_KERNEL); + if (!inst) + return -ENOMEM; + + inst->ctx = ctx; + + inst->vpu.id = SCP_IPI_VDEC_LAT; + inst->vpu.core_id = SCP_IPI_VDEC_CORE; + inst->vpu.ctx = ctx; + inst->vpu.codec_type = ctx->current_codec; + inst->vpu.capture_type = ctx->capture_fourcc; + + ctx->drv_handle = inst; + err = vpu_dec_init(&inst->vpu); + if (err) { + mtk_vcodec_err(inst, "vdec_hevc init err=%d", err); + goto error_free_inst; + } + + vsi_size = round_up(sizeof(struct vdec_hevc_slice_vsi), VCODEC_DEC_ALIGNED_64); + inst->vsi = inst->vpu.vsi; + inst->vsi_core = + (struct vdec_hevc_slice_vsi *)(((char *)inst->vpu.vsi) + vsi_size); + + inst->resolution_changed = true; + inst->realloc_mv_buf = true; + + inst->wrap_addr.size = VDEC_HEVC_WRAP_SZ; + err = mtk_vcodec_mem_alloc(ctx, &inst->wrap_addr); + if (err) + goto error_free_inst; + + mtk_vcodec_debug(inst, "lat struct size = %d,%d,%d,%d vsi: %d\n", + (int)sizeof(struct mtk_hevc_sps_param), + (int)sizeof(struct mtk_hevc_pps_param), + (int)sizeof(struct vdec_hevc_slice_lat_dec_param), + (int)sizeof(struct mtk_hevc_dpb_info), + vsi_size); + mtk_vcodec_debug(inst, "lat hevc instance >> %p, codec_type = 0x%x", + inst, inst->vpu.codec_type); + + return 0; +error_free_inst: + kfree(inst); + return err; +} + +static void vdec_hevc_slice_deinit(void *h_vdec) +{ + struct vdec_hevc_slice_inst *inst = h_vdec; + struct mtk_vcodec_mem *mem; + + mtk_vcodec_debug_enter(inst); + + vpu_dec_deinit(&inst->vpu); + vdec_hevc_slice_free_mv_buf(inst); + + mem = &inst->wrap_addr; + if (mem->va) + mtk_vcodec_mem_free(inst->ctx, mem); + + vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx); + kfree(inst); +} + +static int vdec_hevc_slice_core_decode(struct vdec_lat_buf *lat_buf) +{ + int err, timeout; + struct mtk_vcodec_ctx *ctx = lat_buf->ctx; + struct vdec_hevc_slice_inst *inst = ctx->drv_handle; + struct vdec_hevc_slice_share_info *share_info = lat_buf->private_data; + struct vdec_vpu_inst *vpu = &inst->vpu; + + mtk_vcodec_debug(inst, "[hevc-core] vdec_hevc core decode"); + memcpy(&inst->vsi_core->hevc_slice_params, &share_info->hevc_slice_params, + sizeof(share_info->hevc_slice_params)); + + err = vdec_hevc_slice_setup_core_buffer(inst, share_info, lat_buf); + if (err) + goto vdec_dec_end; + + vdec_hevc_slice_fill_decode_reflist(inst, &inst->vsi_core->hevc_slice_params, + share_info); + err = vpu_dec_core(vpu); + if (err) { + mtk_vcodec_err(inst, "core decode err=%d", err); + goto vdec_dec_end; + } + + /* wait decoder done interrupt */ + timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE); + if (timeout) + mtk_vcodec_err(inst, "core decode timeout: pic_%d", + ctx->decoded_frame_cnt); + inst->vsi_core->dec.timeout = !!timeout; + + vpu_dec_core_end(vpu); + mtk_vcodec_debug(inst, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", + ctx->decoded_frame_cnt, + inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1], + inst->vsi_core->dec.crc[2], inst->vsi_core->dec.crc[3], + inst->vsi_core->dec.crc[4], inst->vsi_core->dec.crc[5], + inst->vsi_core->dec.crc[6], inst->vsi_core->dec.crc[7]); + +vdec_dec_end: + vdec_msg_queue_update_ube_rptr(&lat_buf->ctx->msg_queue, share_info->trans.dma_addr_end); + ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req); + mtk_vcodec_debug(inst, "core decode done err=%d", err); + ctx->decoded_frame_cnt++; + return 0; +} + +static int vdec_hevc_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *fb, bool *res_chg) +{ + struct vdec_hevc_slice_inst *inst = h_vdec; + struct vdec_vpu_inst *vpu = &inst->vpu; + int err, timeout = 0; + unsigned int data[2]; + struct vdec_lat_buf *lat_buf; + struct vdec_hevc_slice_share_info *share_info; + + if (vdec_msg_queue_init(&inst->ctx->msg_queue, inst->ctx, + vdec_hevc_slice_core_decode, + sizeof(*share_info))) + return -ENOMEM; + + /* bs NULL means flush decoder */ + if (!bs) { + vdec_msg_queue_wait_lat_buf_full(&inst->ctx->msg_queue); + return vpu_dec_reset(vpu); + } + + lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx); + if (!lat_buf) { + mtk_vcodec_debug(inst, "failed to get lat buffer"); + return -EAGAIN; + } + + share_info = lat_buf->private_data; + err = vdec_hevc_slice_fill_decode_parameters(inst, share_info); + if (err) + goto err_free_fb_out; + + err = vdec_hevc_slice_setup_lat_buffer(inst, bs, lat_buf, res_chg); + if (err) + goto err_free_fb_out; + + err = vpu_dec_start(vpu, data, 2); + if (err) { + mtk_vcodec_debug(inst, "lat decode err: %d", err); + goto err_free_fb_out; + } + + if (IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { + memcpy(&share_info->hevc_slice_params, &inst->vsi->hevc_slice_params, + sizeof(share_info->hevc_slice_params)); + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); + } + + /* wait decoder done interrupt */ + timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED, + WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0); + if (timeout) + mtk_vcodec_err(inst, "lat decode timeout: pic_%d", inst->slice_dec_num); + inst->vsi->dec.timeout = !!timeout; + + err = vpu_dec_end(vpu); + if (err == SLICE_HEADER_FULL || err == TRANS_BUFFER_FULL) { + if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); + inst->slice_dec_num++; + mtk_vcodec_err(inst, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err); + return -EINVAL; + } + + share_info->trans.dma_addr_end = inst->ctx->msg_queue.wdma_addr.dma_addr + + inst->vsi->dec.wdma_end_addr_offset; + vdec_msg_queue_update_ube_wptr(&lat_buf->ctx->msg_queue, share_info->trans.dma_addr_end); + + if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) { + memcpy(&share_info->hevc_slice_params, &inst->vsi->hevc_slice_params, + sizeof(share_info->hevc_slice_params)); + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf); + } + mtk_vcodec_debug(inst, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num, + inst->vsi->dec.crc[0], inst->vsi->dec.crc[1], inst->vsi->dec.crc[2]); + + inst->slice_dec_num++; + return 0; +err_free_fb_out: + vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf); + mtk_vcodec_err(inst, "slice dec number: %d err: %d", inst->slice_dec_num, err); + return err; +} + +static int vdec_hevc_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs, + struct vdec_fb *unused, bool *res_chg) +{ + struct vdec_hevc_slice_inst *inst = h_vdec; + + if (!h_vdec || inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE) + return -EINVAL; + + return vdec_hevc_slice_lat_decode(h_vdec, bs, unused, res_chg); +} + +static int vdec_hevc_slice_get_param(void *h_vdec, enum vdec_get_param_type type, + void *out) +{ + struct vdec_hevc_slice_inst *inst = h_vdec; + + switch (type) { + case GET_PARAM_PIC_INFO: + vdec_hevc_slice_get_pic_info(inst); + break; + case GET_PARAM_DPB_SIZE: + *(unsigned int *)out = 6; + break; + case GET_PARAM_CROP_INFO: + vdec_hevc_slice_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_hevc_slice_multi_if = { + .init = vdec_hevc_slice_init, + .decode = vdec_hevc_slice_decode, + .get_param = vdec_hevc_slice_get_param, + .deinit = vdec_hevc_slice_deinit, +}; diff --git a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c index cf16cf2807f0..c2f90848f498 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_req_lat_if.c @@ -2069,7 +2069,7 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, lat_buf = vdec_msg_queue_dqbuf(&instance->ctx->msg_queue.lat_ctx); if (!lat_buf) { - mtk_vcodec_err(instance, "Failed to get VP9 lat buf\n"); + mtk_vcodec_debug(instance, "Failed to get VP9 lat buf\n"); return -EAGAIN; } pfc = (struct vdec_vp9_slice_pfc *)lat_buf->private_data; @@ -2119,7 +2119,7 @@ static int vdec_vp9_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs, vdec_msg_queue_update_ube_wptr(&ctx->msg_queue, vsi->trans.dma_addr_end + ctx->msg_queue.wdma_addr.dma_addr); - vdec_msg_queue_qbuf(&ctx->dev->msg_queue_core_ctx, lat_buf); + vdec_msg_queue_qbuf(&ctx->msg_queue.core_ctx, lat_buf); return 0; err_free_fb_out: diff --git a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c index f3807f03d880..06d393174cc2 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c @@ -49,6 +49,14 @@ int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc) ctx->dec_if = &vdec_vp9_slice_lat_if; ctx->hw_id = IS_VDEC_LAT_ARCH(hw_arch) ? MTK_VDEC_LAT0 : MTK_VDEC_CORE; break; + case V4L2_PIX_FMT_HEVC_SLICE: + ctx->dec_if = &vdec_hevc_slice_multi_if; + ctx->hw_id = MTK_VDEC_LAT0; + break; + case V4L2_PIX_FMT_AV1_FRAME: + ctx->dec_if = &vdec_av1_slice_lat_if; + ctx->hw_id = MTK_VDEC_LAT0; + break; default: return -EINVAL; } diff --git a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h index 076306ff2dd4..a8da6a59a6a5 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h @@ -61,6 +61,8 @@ extern const struct vdec_common_if vdec_vp8_if; extern const struct vdec_common_if vdec_vp8_slice_if; extern const struct vdec_common_if vdec_vp9_if; extern const struct vdec_common_if vdec_vp9_slice_lat_if; +extern const struct vdec_common_if vdec_hevc_slice_multi_if; +extern const struct vdec_common_if vdec_av1_slice_lat_if; /** * vdec_if_init() - initialize decode driver diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c index f3073d1e7f42..f555341ae708 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c @@ -20,6 +20,9 @@ /* the size used to store avc error information */ #define VDEC_ERR_MAP_SZ_AVC (17 * SZ_1K) +#define VDEC_RD_MV_BUFFER_SZ (((SZ_4K * 2304 >> 4) + SZ_1K) << 1) +#define VDEC_LAT_TILE_SZ (64 * V4L2_AV1_MAX_TILE_COUNT) + /* core will read the trans buffer which decoded by lat to decode again. * The trans buffer size of FHD and 4K bitstreams are different. */ @@ -71,7 +74,6 @@ static void vdec_msg_queue_dec(struct vdec_msg_queue *msg_queue, int hardware_in int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf *buf) { struct list_head *head; - int status; head = vdec_get_buf_list(msg_ctx->hardware_index, buf); if (!head) { @@ -87,12 +89,9 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf if (msg_ctx->hardware_index != MTK_VDEC_CORE) { wake_up_all(&msg_ctx->ready_to_use); } else { - if (buf->ctx->msg_queue.core_work_cnt < - atomic_read(&buf->ctx->msg_queue.core_list_cnt)) { - status = queue_work(buf->ctx->dev->core_workqueue, - &buf->ctx->msg_queue.core_work); - if (status) - buf->ctx->msg_queue.core_work_cnt++; + if (!(buf->ctx->msg_queue.status & CONTEXT_LIST_QUEUED)) { + queue_work(buf->ctx->dev->core_workqueue, &buf->ctx->msg_queue.core_work); + buf->ctx->msg_queue.status |= CONTEXT_LIST_QUEUED; } } @@ -181,49 +180,23 @@ void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t u bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue) { - struct vdec_lat_buf *buf, *tmp; - struct list_head *list_core[3]; - struct vdec_msg_queue_ctx *core_ctx; - int ret, i, in_core_count = 0, count = 0; - long timeout_jiff; - - core_ctx = &msg_queue->ctx->dev->msg_queue_core_ctx; - spin_lock(&core_ctx->ready_lock); - list_for_each_entry_safe(buf, tmp, &core_ctx->ready_queue, core_list) { - if (buf && buf->ctx == msg_queue->ctx) { - list_core[in_core_count++] = &buf->core_list; - list_del(&buf->core_list); - } - } - - for (i = 0; i < in_core_count; i++) { - list_add(list_core[in_core_count - (1 + i)], &core_ctx->ready_queue); - queue_work(msg_queue->ctx->dev->core_workqueue, &msg_queue->core_work); - } - spin_unlock(&core_ctx->ready_lock); - - timeout_jiff = msecs_to_jiffies(1000 * (NUM_BUFFER_COUNT + 2)); - ret = wait_event_timeout(msg_queue->ctx->msg_queue.core_dec_done, - 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); + if (atomic_read(&msg_queue->lat_list_cnt) == NUM_BUFFER_COUNT) { + mtk_v4l2_debug(3, "wait buf full: list(%d %d) ready_num:%d status:%d", + atomic_read(&msg_queue->lat_list_cnt), + atomic_read(&msg_queue->core_list_cnt), + msg_queue->lat_ctx.ready_num, + msg_queue->status); return true; } - spin_lock(&core_ctx->ready_lock); - list_for_each_entry_safe(buf, tmp, &core_ctx->ready_queue, core_list) { - if (buf && buf->ctx == msg_queue->ctx) { - count++; - list_del(&buf->core_list); - } - } - spin_unlock(&core_ctx->ready_lock); + msg_queue->flush_done = false; + vdec_msg_queue_qbuf(&msg_queue->core_ctx, &msg_queue->empty_lat_buf); + wait_event(msg_queue->core_dec_done, msg_queue->flush_done); - mtk_v4l2_err("failed with lat buf isn't full: list(%d %d) count:%d", - atomic_read(&msg_queue->lat_list_cnt), - atomic_read(&msg_queue->core_list_cnt), count); + mtk_v4l2_debug(3, "flush done => ready_num:%d status:%d list(%d %d)", + msg_queue->lat_ctx.ready_num, msg_queue->status, + atomic_read(&msg_queue->lat_list_cnt), + atomic_read(&msg_queue->core_list_cnt)); return false; } @@ -249,8 +222,18 @@ void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue, if (mem->va) mtk_vcodec_mem_free(ctx, mem); + mem = &lat_buf->rd_mv_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + + mem = &lat_buf->tile_addr; + if (mem->va) + mtk_vcodec_mem_free(ctx, mem); + kfree(lat_buf->private_data); } + + cancel_work_sync(&msg_queue->core_work); } static void vdec_msg_queue_core_work(struct work_struct *work) @@ -261,12 +244,23 @@ static void vdec_msg_queue_core_work(struct work_struct *work) container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue); struct mtk_vcodec_dev *dev = ctx->dev; struct vdec_lat_buf *lat_buf; - int status; - lat_buf = vdec_msg_queue_dqbuf(&dev->msg_queue_core_ctx); + spin_lock(&msg_queue->core_ctx.ready_lock); + ctx->msg_queue.status &= ~CONTEXT_LIST_QUEUED; + spin_unlock(&msg_queue->core_ctx.ready_lock); + + lat_buf = vdec_msg_queue_dqbuf(&msg_queue->core_ctx); if (!lat_buf) return; + if (lat_buf->is_last_frame) { + ctx->msg_queue.status = CONTEXT_LIST_DEC_DONE; + msg_queue->flush_done = true; + wake_up(&ctx->msg_queue.core_dec_done); + + return; + } + ctx = lat_buf->ctx; mtk_vcodec_dec_enable_hardware(ctx, MTK_VDEC_CORE); mtk_vcodec_set_curr_ctx(dev, ctx, MTK_VDEC_CORE); @@ -277,18 +271,13 @@ static void vdec_msg_queue_core_work(struct work_struct *work) mtk_vcodec_dec_disable_hardware(ctx, MTK_VDEC_CORE); vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf); - wake_up_all(&ctx->msg_queue.core_dec_done); - spin_lock(&dev->msg_queue_core_ctx.ready_lock); - lat_buf->ctx->msg_queue.core_work_cnt--; - - if (lat_buf->ctx->msg_queue.core_work_cnt < - atomic_read(&lat_buf->ctx->msg_queue.core_list_cnt)) { - status = queue_work(lat_buf->ctx->dev->core_workqueue, - &lat_buf->ctx->msg_queue.core_work); - if (status) - lat_buf->ctx->msg_queue.core_work_cnt++; + if (!(ctx->msg_queue.status & CONTEXT_LIST_QUEUED) && + atomic_read(&msg_queue->core_list_cnt)) { + spin_lock(&msg_queue->core_ctx.ready_lock); + ctx->msg_queue.status |= CONTEXT_LIST_QUEUED; + spin_unlock(&msg_queue->core_ctx.ready_lock); + queue_work(ctx->dev->core_workqueue, &msg_queue->core_work); } - spin_unlock(&dev->msg_queue_core_ctx.ready_lock); } int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, @@ -302,14 +291,14 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, if (msg_queue->wdma_addr.size) return 0; - msg_queue->ctx = ctx; - msg_queue->core_work_cnt = 0; vdec_msg_queue_init_ctx(&msg_queue->lat_ctx, MTK_VDEC_LAT0); + vdec_msg_queue_init_ctx(&msg_queue->core_ctx, MTK_VDEC_CORE); INIT_WORK(&msg_queue->core_work, vdec_msg_queue_core_work); atomic_set(&msg_queue->lat_list_cnt, 0); atomic_set(&msg_queue->core_list_cnt, 0); init_waitqueue_head(&msg_queue->core_dec_done); + msg_queue->status = CONTEXT_LIST_EMPTY; msg_queue->wdma_addr.size = vde_msg_queue_get_trans_size(ctx->picinfo.buf_w, @@ -322,6 +311,10 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, msg_queue->wdma_rptr_addr = msg_queue->wdma_addr.dma_addr; msg_queue->wdma_wptr_addr = msg_queue->wdma_addr.dma_addr; + msg_queue->empty_lat_buf.ctx = ctx; + msg_queue->empty_lat_buf.core_decode = NULL; + msg_queue->empty_lat_buf.is_last_frame = true; + for (i = 0; i < NUM_BUFFER_COUNT; i++) { lat_buf = &msg_queue->lat_buf[i]; @@ -339,6 +332,22 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, goto mem_alloc_err; } + if (ctx->current_codec == V4L2_PIX_FMT_AV1_FRAME) { + lat_buf->rd_mv_addr.size = VDEC_RD_MV_BUFFER_SZ; + err = mtk_vcodec_mem_alloc(ctx, &lat_buf->rd_mv_addr); + if (err) { + mtk_v4l2_err("failed to allocate rd_mv_addr buf[%d]", i); + return -ENOMEM; + } + + lat_buf->tile_addr.size = VDEC_LAT_TILE_SZ; + err = mtk_vcodec_mem_alloc(ctx, &lat_buf->tile_addr); + if (err) { + mtk_v4l2_err("failed to allocate tile_addr buf[%d]", i); + return -ENOMEM; + } + } + lat_buf->private_data = kzalloc(private_size, GFP_KERNEL); if (!lat_buf->private_data) { err = -ENOMEM; @@ -347,6 +356,7 @@ int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue, lat_buf->ctx = ctx; lat_buf->core_decode = core_decode; + lat_buf->is_last_frame = false; err = vdec_msg_queue_qbuf(&msg_queue->lat_ctx, lat_buf); if (err) { mtk_v4l2_err("failed to qbuf buf[%d]", i); diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h index a5d44bc97c16..2a745e902ad1 100644 --- a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h +++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h @@ -22,6 +22,18 @@ struct mtk_vcodec_dev; typedef int (*core_decode_cb_t)(struct vdec_lat_buf *lat_buf); /** + * enum core_ctx_status - Context decode status for core hardwre. + * @CONTEXT_LIST_EMPTY: No buffer queued on core hardware(must always be 0) + * @CONTEXT_LIST_QUEUED: Buffer queued to core work list + * @CONTEXT_LIST_DEC_DONE: context decode done + */ +enum core_ctx_status { + CONTEXT_LIST_EMPTY = 0, + CONTEXT_LIST_QUEUED, + CONTEXT_LIST_DEC_DONE, +}; + +/** * 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 @@ -42,6 +54,8 @@ struct vdec_msg_queue_ctx { * 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 + * @rd_mv_addr: mv addr for av1 lat hardware output, core hardware input + * @tile_addr: tile buffer for av1 core input * @ts_info: need to set timestamp from output to capture * @src_buf_req: output buffer media request object * @@ -50,10 +64,14 @@ struct vdec_msg_queue_ctx { * @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 + * + * @is_last_frame: meaning this buffer is the last frame */ struct vdec_lat_buf { struct mtk_vcodec_mem wdma_err_addr; struct mtk_vcodec_mem slice_bc_addr; + struct mtk_vcodec_mem rd_mv_addr; + struct mtk_vcodec_mem tile_addr; struct vb2_v4l2_buffer ts_info; struct media_request *src_buf_req; @@ -62,6 +80,8 @@ struct vdec_lat_buf { core_decode_cb_t core_decode; struct list_head lat_list; struct list_head core_list; + + bool is_last_frame; }; /** @@ -72,12 +92,14 @@ struct vdec_lat_buf { * @wdma_wptr_addr: ube write point * @core_work: core hardware work * @lat_ctx: used to store lat buffer list - * @ctx: point to mtk_vcodec_ctx + * @core_ctx: used to store core buffer list * * @lat_list_cnt: used to record each instance lat list count * @core_list_cnt: used to record each instance core list count + * @flush_done: core flush done status + * @empty_lat_buf: the last lat buf used to flush decode * @core_dec_done: core work queue decode done event - * @core_work_cnt: the number of core work in work queue + * @status: current context decode status for core hardware */ struct vdec_msg_queue { struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT]; @@ -88,12 +110,14 @@ struct vdec_msg_queue { struct work_struct core_work; struct vdec_msg_queue_ctx lat_ctx; - struct mtk_vcodec_ctx *ctx; + struct vdec_msg_queue_ctx core_ctx; atomic_t lat_list_cnt; atomic_t core_list_cnt; + bool flush_done; + struct vdec_lat_buf empty_lat_buf; wait_queue_head_t core_dec_done; - int core_work_cnt; + int status; }; /** |