Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_MEDIATEK_MDP3
tristate "MediaTek MDP v3 driver"
depends on MTK_IOMMU || COMPILE_TEST
depends on VIDEO_DEV
depends on HAS_DMA
depends on REMOTEPROC
depends on MTK_MMSYS
depends on MTK_CMDQ
depends on MTK_SCP
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
default n
help
It is a v4l2 driver and present in MediaTek MT8183 SoC.
The driver supports scaling and color space conversion.
To compile this driver as a module, choose M here: the
module will be called mtk-mdp3.
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
mtk-mdp3-y += mdp_cfg_data.o mtk-mdp3-core.o mtk-mdp3-vpu.o mtk-mdp3-regs.o
mtk-mdp3-y += mtk-mdp3-m2m.o
mtk-mdp3-y += mtk-mdp3-comp.o mtk-mdp3-cmdq.o
obj-$(CONFIG_VIDEO_MEDIATEK_MDP3) += mtk-mdp3.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_AAL_H__
#define __MDP_REG_AAL_H__
#define MDP_AAL_EN (0x000)
#define MDP_AAL_CFG (0x020)
#define MDP_AAL_SIZE (0x030)
#define MDP_AAL_OUTPUT_SIZE (0x034)
#define MDP_AAL_OUTPUT_OFFSET (0x038)
#define MDP_AAL_CFG_MAIN (0x200)
/* MASK */
#define MDP_AAL_EN_MASK (0x01)
#define MDP_AAL_CFG_MASK (0x70FF00B3)
#define MDP_AAL_SIZE_MASK (0x1FFF1FFF)
#define MDP_AAL_OUTPUT_SIZE_MASK (0x1FFF1FFF)
#define MDP_AAL_OUTPUT_OFFSET_MASK (0x0FF00FF)
#define MDP_AAL_CFG_MAIN_MASK (0x0FE)
#endif // __MDP_REG_AAL_H__
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_CCORR_H__
#define __MDP_REG_CCORR_H__
#define MDP_CCORR_EN 0x000
#define MDP_CCORR_CFG 0x020
#define MDP_CCORR_SIZE 0x030
/* MASK */
#define MDP_CCORR_EN_MASK 0x00000001
#define MDP_CCORR_CFG_MASK 0x70001317
#define MDP_CCORR_SIZE_MASK 0x1fff1fff
#endif // __MDP_REG_CCORR_H__
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_COLOR_H__
#define __MDP_REG_COLOR_H__
#define MDP_COLOR_WIN_X_MAIN (0x40C)
#define MDP_COLOR_WIN_Y_MAIN (0x410)
#define MDP_COLOR_START (0xC00)
#define MDP_COLOR_INTEN (0xC04)
#define MDP_COLOR_OUT_SEL (0xC0C)
#define MDP_COLOR_INTERNAL_IP_WIDTH (0xC50)
#define MDP_COLOR_INTERNAL_IP_HEIGHT (0xC54)
#define MDP_COLOR_CM1_EN (0xC60)
#define MDP_COLOR_CM2_EN (0xCA0)
/* MASK */
#define MDP_COLOR_WIN_X_MAIN_MASK (0xFFFFFFFF)
#define MDP_COLOR_WIN_Y_MAIN_MASK (0xFFFFFFFF)
#define MDP_COLOR_START_MASK (0x0FF013F)
#define MDP_COLOR_INTEN_MASK (0x07)
#define MDP_COLOR_OUT_SEL_MASK (0x0777)
#define MDP_COLOR_INTERNAL_IP_WIDTH_MASK (0x03FFF)
#define MDP_COLOR_INTERNAL_IP_HEIGHT_MASK (0x03FFF)
#define MDP_COLOR_CM1_EN_MASK (0x03)
#define MDP_COLOR_CM2_EN_MASK (0x017)
#endif // __MDP_REG_COLOR_H__
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_FG_H__
#define __MDP_REG_FG_H__
#define MDP_FG_TRIGGER (0x0)
#define MDP_FG_FG_CTRL_0 (0x20)
#define MDP_FG_FG_CK_EN (0x24)
#define MDP_FG_TILE_INFO_0 (0x418)
#define MDP_FG_TILE_INFO_1 (0x41c)
/* MASK */
#define MDP_FG_TRIGGER_MASK (0x00000007)
#define MDP_FG_FG_CTRL_0_MASK (0x00000033)
#define MDP_FG_FG_CK_EN_MASK (0x0000000F)
#define MDP_FG_TILE_INFO_0_MASK (0xFFFFFFFF)
#define MDP_FG_TILE_INFO_1_MASK (0xFFFFFFFF)
#endif //__MDP_REG_FG_H__
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_HDR_H__
#define __MDP_REG_HDR_H__
#define MDP_HDR_TOP (0x000)
#define MDP_HDR_RELAY (0x004)
#define MDP_HDR_SIZE_0 (0x014)
#define MDP_HDR_SIZE_1 (0x018)
#define MDP_HDR_SIZE_2 (0x01C)
#define MDP_HDR_HIST_CTRL_0 (0x020)
#define MDP_HDR_HIST_CTRL_1 (0x024)
#define MDP_HDR_HIST_ADDR (0x0DC)
#define MDP_HDR_TILE_POS (0x118)
/* MASK */
#define MDP_HDR_RELAY_MASK (0x01)
#define MDP_HDR_TOP_MASK (0xFF0FEB6D)
#define MDP_HDR_SIZE_0_MASK (0x1FFF1FFF)
#define MDP_HDR_SIZE_1_MASK (0x1FFF1FFF)
#define MDP_HDR_SIZE_2_MASK (0x1FFF1FFF)
#define MDP_HDR_HIST_CTRL_0_MASK (0x1FFF1FFF)
#define MDP_HDR_HIST_CTRL_1_MASK (0x1FFF1FFF)
#define MDP_HDR_HIST_ADDR_MASK (0xBF3F2F3F)
#define MDP_HDR_TILE_POS_MASK (0x1FFF1FFF)
#endif // __MDP_REG_HDR_H__
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_MERGE_H__
#define __MDP_REG_MERGE_H__
#define MDP_MERGE_ENABLE (0x000)
#define MDP_MERGE_CFG_0 (0x010)
#define MDP_MERGE_CFG_4 (0x020)
#define MDP_MERGE_CFG_12 (0x040)
#define MDP_MERGE_CFG_24 (0x070)
#define MDP_MERGE_CFG_25 (0x074)
/* MASK */
#define MDP_MERGE_ENABLE_MASK (0xFFFFFFFF)
#define MDP_MERGE_CFG_0_MASK (0xFFFFFFFF)
#define MDP_MERGE_CFG_4_MASK (0xFFFFFFFF)
#define MDP_MERGE_CFG_12_MASK (0xFFFFFFFF)
#define MDP_MERGE_CFG_24_MASK (0xFFFFFFFF)
#define MDP_MERGE_CFG_25_MASK (0xFFFFFFFF)
#endif //__MDP_REG_MERGE_H__
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_OVL_H__
#define __MDP_REG_OVL_H__
#define MDP_OVL_EN (0x00c)
#define MDP_OVL_ROI_SIZE (0x020)
#define MDP_OVL_DP_CON (0x024)
#define MDP_OVL_SRC_CON (0x02c)
#define MDP_OVL_L0_CON (0x030)
#define MDP_OVL_L0_SRC_SIZE (0x038)
/* MASK */
#define MDP_OVL_DP_CON_MASK (0x0FFFFFFF)
#define MDP_OVL_EN_MASK (0xB07D07B1)
#define MDP_OVL_L0_CON_MASK (0xFFFFFFFF)
#define MDP_OVL_L0_SRC_SIZE_MASK (0x1FFF1FFF)
#define MDP_OVL_ROI_SIZE_MASK (0x1FFF1FFF)
#define MDP_OVL_SRC_CON_MASK (0x0000031F)
#endif //__MDP_REG_OVL_H__
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_PAD_H__
#define __MDP_REG_PAD_H__
#define MDP_PAD_CON (0x000)
#define MDP_PAD_PIC_SIZE (0x004)
#define MDP_PAD_W_SIZE (0x008)
#define MDP_PAD_H_SIZE (0x00c)
/* MASK */
#define MDP_PAD_CON_MASK (0x00000007)
#define MDP_PAD_PIC_SIZE_MASK (0xFFFFFFFF)
#define MDP_PAD_W_SIZE_MASK (0x1FFF1FFF)
#define MDP_PAD_H_SIZE_MASK (0x1FFF1FFF)
#endif // __MDP_REG_PAD_H__
@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_RDMA_H__
#define __MDP_REG_RDMA_H__
#define MDP_RDMA_EN 0x000
#define MDP_RDMA_RESET 0x008
#define MDP_RDMA_CON 0x020
#define MDP_RDMA_GMCIF_CON 0x028
#define MDP_RDMA_SRC_CON 0x030
#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE 0x060
#define MDP_RDMA_MF_BKGD_SIZE_IN_PXL 0x068
#define MDP_RDMA_MF_SRC_SIZE 0x070
#define MDP_RDMA_MF_CLIP_SIZE 0x078
#define MDP_RDMA_MF_OFFSET_1 0x080
#define MDP_RDMA_SF_BKGD_SIZE_IN_BYTE 0x090
#define MDP_RDMA_SRC_END_0 0x100
#define MDP_RDMA_SRC_END_1 0x108
#define MDP_RDMA_SRC_END_2 0x110
#define MDP_RDMA_SRC_OFFSET_0 0x118
#define MDP_RDMA_SRC_OFFSET_1 0x120
#define MDP_RDMA_SRC_OFFSET_2 0x128
#define MDP_RDMA_SRC_OFFSET_0_P 0x148
#define MDP_RDMA_TRANSFORM_0 0x200
#define MDP_RDMA_DMABUF_CON_0 0x240
#define MDP_RDMA_ULTRA_TH_HIGH_CON_0 0x248
#define MDP_RDMA_ULTRA_TH_LOW_CON_0 0x250
#define MDP_RDMA_DMABUF_CON_1 0x258
#define MDP_RDMA_ULTRA_TH_HIGH_CON_1 0x260
#define MDP_RDMA_ULTRA_TH_LOW_CON_1 0x268
#define MDP_RDMA_DMABUF_CON_2 0x270
#define MDP_RDMA_ULTRA_TH_HIGH_CON_2 0x278
#define MDP_RDMA_ULTRA_TH_LOW_CON_2 0x280
#define MDP_RDMA_DMABUF_CON_3 0x288
#define MDP_RDMA_ULTRA_TH_HIGH_CON_3 0x290
#define MDP_RDMA_ULTRA_TH_LOW_CON_3 0x298
#define MDP_RDMA_RESV_DUMMY_0 0x2a0
#define MDP_RDMA_MON_STA_1 0x408
#define MDP_RDMA_SRC_BASE_0 0xf00
#define MDP_RDMA_SRC_BASE_1 0xf08
#define MDP_RDMA_SRC_BASE_2 0xf10
#define MDP_RDMA_UFO_DEC_LENGTH_BASE_Y 0xf20
#define MDP_RDMA_UFO_DEC_LENGTH_BASE_C 0xf28
/* MASK */
#define MDP_RDMA_EN_MASK 0x00000001
#define MDP_RDMA_RESET_MASK 0x00000001
#define MDP_RDMA_CON_MASK 0x00001110
#define MDP_RDMA_GMCIF_CON_MASK 0xfffb3771
#define MDP_RDMA_SRC_CON_MASK 0xf3ffffff
#define MDP_RDMA_MF_BKGD_SIZE_IN_BYTE_MASK 0x001fffff
#define MDP_RDMA_MF_BKGD_SIZE_IN_PXL_MASK 0x001fffff
#define MDP_RDMA_MF_SRC_SIZE_MASK 0x1fff1fff
#define MDP_RDMA_MF_CLIP_SIZE_MASK 0x1fff1fff
#define MDP_RDMA_MF_OFFSET_1_MASK 0x003f001f
#define MDP_RDMA_SF_BKGD_SIZE_IN_BYTE_MASK 0x001fffff
#define MDP_RDMA_SRC_END_0_MASK 0xffffffff
#define MDP_RDMA_SRC_END_1_MASK 0xffffffff
#define MDP_RDMA_SRC_END_2_MASK 0xffffffff
#define MDP_RDMA_SRC_OFFSET_0_MASK 0xffffffff
#define MDP_RDMA_SRC_OFFSET_1_MASK 0xffffffff
#define MDP_RDMA_SRC_OFFSET_2_MASK 0xffffffff
#define MDP_RDMA_SRC_OFFSET_0_P_MASK 0xffffffff
#define MDP_RDMA_TRANSFORM_0_MASK 0xff110777
#define MDP_RDMA_DMABUF_CON_0_MASK 0x0fff00ff
#define MDP_RDMA_ULTRA_TH_HIGH_CON_0_MASK 0x3fffffff
#define MDP_RDMA_ULTRA_TH_LOW_CON_0_MASK 0x3fffffff
#define MDP_RDMA_DMABUF_CON_1_MASK 0x0f7f007f
#define MDP_RDMA_ULTRA_TH_HIGH_CON_1_MASK 0x3fffffff
#define MDP_RDMA_ULTRA_TH_LOW_CON_1_MASK 0x3fffffff
#define MDP_RDMA_DMABUF_CON_2_MASK 0x0f3f003f
#define MDP_RDMA_ULTRA_TH_HIGH_CON_2_MASK 0x3fffffff
#define MDP_RDMA_ULTRA_TH_LOW_CON_2_MASK 0x3fffffff
#define MDP_RDMA_DMABUF_CON_3_MASK 0x0f3f003f
#define MDP_RDMA_ULTRA_TH_HIGH_CON_3_MASK 0x3fffffff
#define MDP_RDMA_ULTRA_TH_LOW_CON_3_MASK 0x3fffffff
#define MDP_RDMA_RESV_DUMMY_0_MASK 0xffffffff
#define MDP_RDMA_MON_STA_1_MASK 0xffffffff
#define MDP_RDMA_SRC_BASE_0_MASK 0xffffffff
#define MDP_RDMA_SRC_BASE_1_MASK 0xffffffff
#define MDP_RDMA_SRC_BASE_2_MASK 0xffffffff
#define MDP_RDMA_UFO_DEC_LENGTH_BASE_Y_MASK 0xffffffff
#define MDP_RDMA_UFO_DEC_LENGTH_BASE_C_MASK 0xffffffff
#endif // __MDP_REG_RDMA_H__
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_RSZ_H__
#define __MDP_REG_RSZ_H__
#define PRZ_ENABLE 0x000
#define PRZ_CONTROL_1 0x004
#define PRZ_CONTROL_2 0x008
#define PRZ_INPUT_IMAGE 0x010
#define PRZ_OUTPUT_IMAGE 0x014
#define PRZ_HORIZONTAL_COEFF_STEP 0x018
#define PRZ_VERTICAL_COEFF_STEP 0x01c
#define PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET 0x020
#define PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET 0x024
#define PRZ_LUMA_VERTICAL_INTEGER_OFFSET 0x028
#define PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET 0x02c
#define PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET 0x030
#define PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET 0x034
#define RSZ_ETC_CONTROL 0x22c
/* MASK */
#define PRZ_ENABLE_MASK 0x00010001
#define PRZ_CONTROL_1_MASK 0xfffffff3
#define PRZ_CONTROL_2_MASK 0x0ffffaff
#define PRZ_INPUT_IMAGE_MASK 0xffffffff
#define PRZ_OUTPUT_IMAGE_MASK 0xffffffff
#define PRZ_HORIZONTAL_COEFF_STEP_MASK 0x007fffff
#define PRZ_VERTICAL_COEFF_STEP_MASK 0x007fffff
#define PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET_MASK 0x0000ffff
#define PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET_MASK 0x001fffff
#define PRZ_LUMA_VERTICAL_INTEGER_OFFSET_MASK 0x0000ffff
#define PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET_MASK 0x001fffff
#define PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET_MASK 0x0000ffff
#define PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET_MASK 0x001fffff
#define RSZ_ETC_CONTROL_MASK 0xff770000
#endif // __MDP_REG_RSZ_H__
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_TDSHP_H__
#define __MDP_REG_TDSHP_H__
#define MDP_HIST_CFG_00 (0x064)
#define MDP_HIST_CFG_01 (0x068)
#define MDP_TDSHP_CTRL (0x100)
#define MDP_TDSHP_CFG (0x110)
#define MDP_TDSHP_INPUT_SIZE (0x120)
#define MDP_TDSHP_OUTPUT_OFFSET (0x124)
#define MDP_TDSHP_OUTPUT_SIZE (0x128)
#define MDP_LUMA_HIST_INIT (0x200)
#define MDP_DC_TWO_D_W1_RESULT_INIT (0x260)
#define MDP_CONTOUR_HIST_INIT (0x398)
/* MASK */
#define MDP_HIST_CFG_00_MASK (0xFFFFFFFF)
#define MDP_HIST_CFG_01_MASK (0xFFFFFFFF)
#define MDP_LUMA_HIST_MASK (0xFFFFFFFF)
#define MDP_TDSHP_CTRL_MASK (0x07)
#define MDP_TDSHP_CFG_MASK (0x03F7)
#define MDP_TDSHP_INPUT_SIZE_MASK (0x1FFF1FFF)
#define MDP_TDSHP_OUTPUT_OFFSET_MASK (0x0FF00FF)
#define MDP_TDSHP_OUTPUT_SIZE_MASK (0x1FFF1FFF)
#define MDP_LUMA_HIST_INIT_MASK (0xFFFFFFFF)
#define MDP_DC_TWO_D_W1_RESULT_INIT_MASK (0x007FFFFF)
#define MDP_CONTOUR_HIST_INIT_MASK (0xFFFFFFFF)
#endif // __MDP_REG_TDSHP_H__
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_WDMA_H__
#define __MDP_REG_WDMA_H__
#define WDMA_EN 0x008
#define WDMA_RST 0x00c
#define WDMA_CFG 0x014
#define WDMA_SRC_SIZE 0x018
#define WDMA_CLIP_SIZE 0x01c
#define WDMA_CLIP_COORD 0x020
#define WDMA_DST_W_IN_BYTE 0x028
#define WDMA_ALPHA 0x02c
#define WDMA_BUF_CON2 0x03c
#define WDMA_DST_UV_PITCH 0x078
#define WDMA_DST_ADDR_OFFSET 0x080
#define WDMA_DST_U_ADDR_OFFSET 0x084
#define WDMA_DST_V_ADDR_OFFSET 0x088
#define WDMA_FLOW_CTRL_DBG 0x0a0
#define WDMA_DST_ADDR 0xf00
#define WDMA_DST_U_ADDR 0xf04
#define WDMA_DST_V_ADDR 0xf08
/* MASK */
#define WDMA_EN_MASK 0x00000001
#define WDMA_RST_MASK 0x00000001
#define WDMA_CFG_MASK 0xff03bff0
#define WDMA_SRC_SIZE_MASK 0x3fff3fff
#define WDMA_CLIP_SIZE_MASK 0x3fff3fff
#define WDMA_CLIP_COORD_MASK 0x3fff3fff
#define WDMA_DST_W_IN_BYTE_MASK 0x0000ffff
#define WDMA_ALPHA_MASK 0x800000ff
#define WDMA_BUF_CON2_MASK 0xffffffff
#define WDMA_DST_UV_PITCH_MASK 0x0000ffff
#define WDMA_DST_ADDR_OFFSET_MASK 0x0fffffff
#define WDMA_DST_U_ADDR_OFFSET_MASK 0x0fffffff
#define WDMA_DST_V_ADDR_OFFSET_MASK 0x0fffffff
#define WDMA_FLOW_CTRL_DBG_MASK 0x0000f3ff
#define WDMA_DST_ADDR_MASK 0xffffffff
#define WDMA_DST_U_ADDR_MASK 0xffffffff
#define WDMA_DST_V_ADDR_MASK 0xffffffff
#endif // __MDP_REG_WDMA_H__
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_REG_WROT_H__
#define __MDP_REG_WROT_H__
#define VIDO_CTRL 0x000
#define VIDO_MAIN_BUF_SIZE 0x008
#define VIDO_SOFT_RST 0x010
#define VIDO_SOFT_RST_STAT 0x014
#define VIDO_CROP_OFST 0x020
#define VIDO_TAR_SIZE 0x024
#define VIDO_OFST_ADDR 0x02c
#define VIDO_STRIDE 0x030
#define VIDO_OFST_ADDR_C 0x038
#define VIDO_STRIDE_C 0x03c
#define VIDO_CTRL_2 0x048
#define VIDO_DITHER 0x054
#define VIDO_STRIDE_V 0x06c
#define VIDO_OFST_ADDR_V 0x068
#define VIDO_RSV_1 0x070
#define VIDO_DMA_PREULTRA 0x074
#define VIDO_IN_SIZE 0x078
#define VIDO_ROT_EN 0x07c
#define VIDO_FIFO_TEST 0x080
#define VIDO_MAT_CTRL 0x084
#define VIDO_SCAN_10BIT 0x0dc
#define VIDO_PENDING_ZERO 0x0e0
#define VIDO_BASE_ADDR 0xf00
#define VIDO_BASE_ADDR_C 0xf04
#define VIDO_BASE_ADDR_V 0xf08
/* MASK */
#define VIDO_CTRL_MASK 0xf530711f
#define VIDO_MAIN_BUF_SIZE_MASK 0x1fff7f77
#define VIDO_SOFT_RST_MASK 0x00000001
#define VIDO_SOFT_RST_STAT_MASK 0x00000001
#define VIDO_TAR_SIZE_MASK 0x1fff1fff
#define VIDO_CROP_OFST_MASK 0x1fff1fff
#define VIDO_OFST_ADDR_MASK 0x0fffffff
#define VIDO_STRIDE_MASK 0x0000ffff
#define VIDO_OFST_ADDR_C_MASK 0x0fffffff
#define VIDO_STRIDE_C_MASK 0x0000ffff
#define VIDO_CTRL_2_MASK 0x0000000f
#define VIDO_DITHER_MASK 0xff000001
#define VIDO_STRIDE_V_MASK 0x0000ffff
#define VIDO_OFST_ADDR_V_MASK 0x0fffffff
#define VIDO_RSV_1_MASK 0xffffffff
#define VIDO_DMA_PREULTRA_MASK 0x00ffffff
#define VIDO_IN_SIZE_MASK 0x1fff1fff
#define VIDO_ROT_EN_MASK 0x00000001
#define VIDO_FIFO_TEST_MASK 0x00000fff
#define VIDO_MAT_CTRL_MASK 0x000000f3
#define VIDO_SCAN_10BIT_MASK 0x0000000f
#define VIDO_PENDING_ZERO_MASK 0x07ffffff
#define VIDO_BASE_ADDR_MASK 0xffffffff
#define VIDO_BASE_ADDR_C_MASK 0xffffffff
#define VIDO_BASE_ADDR_V_MASK 0xffffffff
#endif // __MDP_REG_WROT_H__
@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_SM_MT8183_H__
#define __MDP_SM_MT8183_H__
#include "mtk-mdp3-type.h"
/*
* ISP-MDP generic output information
* MD5 of the target SCP prebuild:
* 2d995ddb5c3b0cf26e96d6a823481886
*/
#define IMG_MAX_SUBFRAMES_8183 14
struct img_comp_frame_8183 {
u32 output_disable:1;
u32 bypass:1;
u16 in_width;
u16 in_height;
u16 out_width;
u16 out_height;
struct img_crop crop;
u16 in_total_width;
u16 out_total_width;
} __packed;
struct img_comp_subfrm_8183 {
u32 tile_disable:1;
struct img_region in;
struct img_region out;
struct img_offset luma;
struct img_offset chroma;
s16 out_vertical; /* Output vertical index */
s16 out_horizontal; /* Output horizontal index */
} __packed;
struct mdp_rdma_subfrm_8183 {
u32 offset[IMG_MAX_PLANES];
u32 offset_0_p;
u32 src;
u32 clip;
u32 clip_ofst;
} __packed;
struct mdp_rdma_data_8183 {
u32 src_ctrl;
u32 control;
u32 iova[IMG_MAX_PLANES];
u32 iova_end[IMG_MAX_PLANES];
u32 mf_bkgd;
u32 mf_bkgd_in_pxl;
u32 sf_bkgd;
u32 ufo_dec_y;
u32 ufo_dec_c;
u32 transform;
struct mdp_rdma_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183];
} __packed;
struct mdp_rsz_subfrm_8183 {
u32 control2;
u32 src;
u32 clip;
} __packed;
struct mdp_rsz_data_8183 {
u32 coeff_step_x;
u32 coeff_step_y;
u32 control1;
u32 control2;
struct mdp_rsz_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183];
} __packed;
struct mdp_wrot_subfrm_8183 {
u32 offset[IMG_MAX_PLANES];
u32 src;
u32 clip;
u32 clip_ofst;
u32 main_buf;
} __packed;
struct mdp_wrot_data_8183 {
u32 iova[IMG_MAX_PLANES];
u32 control;
u32 stride[IMG_MAX_PLANES];
u32 mat_ctrl;
u32 fifo_test;
u32 filter;
struct mdp_wrot_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183];
} __packed;
struct mdp_wdma_subfrm_8183 {
u32 offset[IMG_MAX_PLANES];
u32 src;
u32 clip;
u32 clip_ofst;
} __packed;
struct mdp_wdma_data_8183 {
u32 wdma_cfg;
u32 iova[IMG_MAX_PLANES];
u32 w_in_byte;
u32 uv_stride;
struct mdp_wdma_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183];
} __packed;
struct isp_data_8183 {
u64 dl_flags; /* 1 << (enum mdp_comp_type) */
u32 smxi_iova[4];
u32 cq_idx;
u32 cq_iova;
u32 tpipe_iova[IMG_MAX_SUBFRAMES_8183];
} __packed;
struct img_compparam_8183 {
u16 type; /* enum mdp_comp_id */
u16 id; /* engine alias_id */
u32 input;
u32 outputs[IMG_MAX_HW_OUTPUTS];
u32 num_outputs;
struct img_comp_frame_8183 frame;
struct img_comp_subfrm_8183 subfrms[IMG_MAX_SUBFRAMES_8183];
u32 num_subfrms;
union {
struct mdp_rdma_data_8183 rdma;
struct mdp_rsz_data_8183 rsz;
struct mdp_wrot_data_8183 wrot;
struct mdp_wdma_data_8183 wdma;
struct isp_data_8183 isp;
};
} __packed;
struct img_config_8183 {
struct img_compparam_8183 components[IMG_MAX_COMPONENTS];
u32 num_components;
struct img_mmsys_ctrl ctrls[IMG_MAX_SUBFRAMES_8183];
u32 num_subfrms;
} __packed;
#endif /* __MDP_SM_MT8183_H__ */
@@ -0,0 +1,283 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MDP_SM_MT8195_H__
#define __MDP_SM_MT8195_H__
#include "mtk-mdp3-type.h"
/*
* ISP-MDP generic output information
* MD5 of the target SCP prebuild:
* a49ec487e458b5971880f1b63dc2a9d5
*/
#define IMG_MAX_SUBFRAMES_8195 20
struct img_comp_frame_8195 {
u32 output_disable;
u32 bypass;
u32 in_width;
u32 in_height;
u32 out_width;
u32 out_height;
struct img_crop crop;
u32 in_total_width;
u32 out_total_width;
} __packed;
struct img_comp_subfrm_8195 {
u32 tile_disable;
struct img_region in;
struct img_region out;
struct img_offset luma;
struct img_offset chroma;
s32 out_vertical; /* Output vertical index */
s32 out_horizontal; /* Output horizontal index */
} __packed;
struct mdp_rdma_subfrm_8195 {
u32 offset[IMG_MAX_PLANES];
u32 offset_0_p;
u32 src;
u32 clip;
u32 clip_ofst;
u32 in_tile_xleft;
u32 in_tile_ytop;
} __packed;
struct mdp_rdma_data_8195 {
u32 src_ctrl;
u32 comp_ctrl;
u32 control;
u32 iova[IMG_MAX_PLANES];
u32 iova_end[IMG_MAX_PLANES];
u32 mf_bkgd;
u32 mf_bkgd_in_pxl;
u32 sf_bkgd;
u32 ufo_dec_y;
u32 ufo_dec_c;
u32 transform;
u32 dmabuf_con0;
u32 ultra_th_high_con0;
u32 ultra_th_low_con0;
u32 dmabuf_con1;
u32 ultra_th_high_con1;
u32 ultra_th_low_con1;
u32 dmabuf_con2;
u32 ultra_th_high_con2;
u32 ultra_th_low_con2;
u32 dmabuf_con3;
struct mdp_rdma_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_fg_subfrm_8195 {
u32 info_0;
u32 info_1;
} __packed;
struct mdp_fg_data_8195 {
u32 ctrl_0;
u32 ck_en;
struct mdp_fg_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_hdr_subfrm_8195 {
u32 win_size;
u32 src;
u32 clip_ofst0;
u32 clip_ofst1;
u32 hist_ctrl_0;
u32 hist_ctrl_1;
u32 hdr_top;
u32 hist_addr;
} __packed;
struct mdp_hdr_data_8195 {
u32 top;
u32 relay;
struct mdp_hdr_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_aal_subfrm_8195 {
u32 src;
u32 clip;
u32 clip_ofst;
} __packed;
struct mdp_aal_data_8195 {
u32 cfg_main;
u32 cfg;
struct mdp_aal_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_rsz_subfrm_8195 {
u32 control2;
u32 src;
u32 clip;
u32 hdmirx_en;
u32 luma_h_int_ofst;
u32 luma_h_sub_ofst;
u32 luma_v_int_ofst;
u32 luma_v_sub_ofst;
u32 chroma_h_int_ofst;
u32 chroma_h_sub_ofst;
u32 rsz_switch;
u32 merge_cfg;
} __packed;
struct mdp_rsz_data_8195 {
u32 coeff_step_x;
u32 coeff_step_y;
u32 control1;
u32 control2;
u32 etc_control;
u32 prz_enable;
u32 ibse_softclip;
u32 tap_adapt;
u32 ibse_gaincontrol1;
u32 ibse_gaincontrol2;
u32 ibse_ylevel_1;
u32 ibse_ylevel_2;
u32 ibse_ylevel_3;
u32 ibse_ylevel_4;
u32 ibse_ylevel_5;
struct mdp_rsz_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_tdshp_subfrm_8195 {
u32 src;
u32 clip;
u32 clip_ofst;
u32 hist_cfg_0;
u32 hist_cfg_1;
} __packed;
struct mdp_tdshp_data_8195 {
u32 cfg;
struct mdp_tdshp_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_color_subfrm_8195 {
u32 in_hsize;
u32 in_vsize;
} __packed;
struct mdp_color_data_8195 {
u32 start;
struct mdp_color_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_ovl_subfrm_8195 {
u32 L0_src_size;
u32 roi_size;
} __packed;
struct mdp_ovl_data_8195 {
u32 L0_con;
u32 src_con;
struct mdp_ovl_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_pad_subfrm_8195 {
u32 pic_size;
} __packed;
struct mdp_pad_data_8195 {
struct mdp_pad_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_tcc_subfrm_8195 {
u32 pic_size;
} __packed;
struct mdp_tcc_data_8195 {
struct mdp_tcc_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_wrot_subfrm_8195 {
u32 offset[IMG_MAX_PLANES];
u32 src;
u32 clip;
u32 clip_ofst;
u32 main_buf;
} __packed;
struct mdp_wrot_data_8195 {
u32 iova[IMG_MAX_PLANES];
u32 control;
u32 stride[IMG_MAX_PLANES];
u32 mat_ctrl;
u32 fifo_test;
u32 filter;
u32 pre_ultra;
u32 framesize;
u32 afbc_yuvtrans;
u32 scan_10bit;
u32 pending_zero;
u32 bit_number;
u32 pvric;
u32 vpp02vpp1;
struct mdp_wrot_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct mdp_wdma_subfrm_8195 {
u32 offset[IMG_MAX_PLANES];
u32 src;
u32 clip;
u32 clip_ofst;
} __packed;
struct mdp_wdma_data_8195 {
u32 wdma_cfg;
u32 iova[IMG_MAX_PLANES];
u32 w_in_byte;
u32 uv_stride;
struct mdp_wdma_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct isp_data_8195 {
u64 dl_flags; /* 1 << (enum mdp_comp_type) */
u32 smxi_iova[4];
u32 cq_idx;
u32 cq_iova;
u32 tpipe_iova[IMG_MAX_SUBFRAMES_8195];
} __packed;
struct img_compparam_8195 {
u32 type; /* enum mdp_comp_id */
u32 id; /* engine alias_id */
u32 input;
u32 outputs[IMG_MAX_HW_OUTPUTS];
u32 num_outputs;
struct img_comp_frame_8195 frame;
struct img_comp_subfrm_8195 subfrms[IMG_MAX_SUBFRAMES_8195];
u32 num_subfrms;
union {
struct mdp_rdma_data_8195 rdma;
struct mdp_fg_data_8195 fg;
struct mdp_hdr_data_8195 hdr;
struct mdp_aal_data_8195 aal;
struct mdp_rsz_data_8195 rsz;
struct mdp_tdshp_data_8195 tdshp;
struct mdp_color_data_8195 color;
struct mdp_ovl_data_8195 ovl;
struct mdp_pad_data_8195 pad;
struct mdp_tcc_data_8195 tcc;
struct mdp_wrot_data_8195 wrot;
struct mdp_wdma_data_8195 wdma;
struct isp_data_8195 isp;
};
} __packed;
struct img_config_8195 {
struct img_compparam_8195 components[IMG_MAX_COMPONENTS];
u32 num_components;
struct img_mmsys_ctrl ctrls[IMG_MAX_SUBFRAMES_8195];
u32 num_subfrms;
} __packed;
#endif /* __MDP_SM_MT8195_H__ */
@@ -0,0 +1,154 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Holmes Chiou <holmes.chiou@mediatek.com>
* Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_IMG_IPI_H__
#define __MTK_IMG_IPI_H__
#include <linux/err.h>
#include "mdp_sm_mt8183.h"
#include "mdp_sm_mt8195.h"
#include "mtk-mdp3-type.h"
/* ISP-MDP generic input information */
#define IMG_IPI_INIT 1
#define IMG_IPI_DEINIT 2
#define IMG_IPI_FRAME 3
#define IMG_IPI_DEBUG 4
struct img_timeval {
u32 tv_sec;
u32 tv_usec;
} __packed;
struct img_addr {
u64 va; /* Used for Linux OS access */
u32 pa; /* Used for CM4 access */
u32 iova; /* Used for IOMMU HW access */
} __packed;
struct tuning_addr {
u64 present;
u32 pa; /* Used for CM4 access */
u32 iova; /* Used for IOMMU HW access */
} __packed;
struct img_sw_addr {
u64 va; /* Used for APMCU access */
u32 pa; /* Used for CM4 access */
} __packed;
struct img_plane_format {
u32 size;
u32 stride;
} __packed;
struct img_pix_format {
u32 width;
u32 height;
u32 colorformat; /* enum mdp_color */
u32 ycbcr_prof; /* enum mdp_ycbcr_profile */
struct img_plane_format plane_fmt[IMG_MAX_PLANES];
} __packed;
struct img_image_buffer {
struct img_pix_format format;
u32 iova[IMG_MAX_PLANES];
/* enum mdp_buffer_usage, FD or advanced ISP usages */
u32 usage;
} __packed;
#define IMG_SUBPIXEL_SHIFT 20
#define IMG_CTRL_FLAG_HFLIP BIT(0)
#define IMG_CTRL_FLAG_DITHER BIT(1)
#define IMG_CTRL_FLAG_SHARPNESS BIT(4)
#define IMG_CTRL_FLAG_HDR BIT(5)
#define IMG_CTRL_FLAG_DRE BIT(6)
struct img_input {
struct img_image_buffer buffer;
u32 flags; /* HDR, DRE, dither */
} __packed;
struct img_output {
struct img_image_buffer buffer;
struct img_crop crop;
s32 rotation;
u32 flags; /* H-flip, sharpness, dither */
} __packed;
struct img_ipi_frameparam {
u32 index;
u32 frame_no;
struct img_timeval timestamp;
u32 type; /* enum mdp_stream_type */
u32 state;
u32 num_inputs;
u32 num_outputs;
u64 drv_data;
struct img_input inputs[IMG_MAX_HW_INPUTS];
struct img_output outputs[IMG_MAX_HW_OUTPUTS];
struct tuning_addr tuning_data;
struct img_addr subfrm_data;
struct img_sw_addr config_data;
struct img_sw_addr self_data;
} __packed;
struct img_sw_buffer {
u64 handle; /* Used for APMCU access */
u32 scp_addr; /* Used for CM4 access */
} __packed;
struct img_ipi_param {
u32 usage;
struct img_sw_buffer frm_param;
} __packed;
struct img_frameparam {
struct list_head list_entry;
struct img_ipi_frameparam frameparam;
} __packed;
/* Platform config indicator */
#define MT8183 8183
#define MT8188 8195
#define MT8195 8195
#define CFG_CHECK(plat, p_id) ((plat) == (p_id))
#define _CFG_OFST(plat, cfg, ofst) ((void *)(&((cfg)->config_##plat) + (ofst)))
#define CFG_OFST(plat, cfg, ofst) \
(IS_ERR_OR_NULL(cfg) ? NULL : _CFG_OFST(plat, cfg, ofst))
#define _CFG_ADDR(plat, cfg, mem) (&((cfg)->config_##plat.mem))
#define CFG_ADDR(plat, cfg, mem) \
(IS_ERR_OR_NULL(cfg) ? NULL : _CFG_ADDR(plat, cfg, mem))
#define _CFG_GET(plat, cfg, mem) ((cfg)->config_##plat.mem)
#define CFG_GET(plat, cfg, mem) \
(IS_ERR_OR_NULL(cfg) ? 0 : _CFG_GET(plat, cfg, mem))
#define _CFG_COMP(plat, comp, mem) ((comp)->comp_##plat.mem)
#define CFG_COMP(plat, comp, mem) \
(IS_ERR_OR_NULL(comp) ? 0 : _CFG_COMP(plat, comp, mem))
struct img_config {
union {
struct img_config_8183 config_8183;
struct img_config_8195 config_8195;
};
} __packed;
struct img_compparam {
union {
struct img_compparam_8183 comp_8183;
struct img_compparam_8195 comp_8195;
};
} __packed;
#endif /* __MTK_IMG_IPI_H__ */
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_CFG_H__
#define __MTK_MDP3_CFG_H__
#include <linux/types.h>
extern const struct mtk_mdp_driver_data mt8183_mdp_driver_data;
extern const struct mtk_mdp_driver_data mt8188_mdp_driver_data;
extern const struct mtk_mdp_driver_data mt8195_mdp_driver_data;
struct mdp_dev;
enum mtk_mdp_comp_id;
s32 mdp_cfg_get_id_inner(struct mdp_dev *mdp_dev, enum mtk_mdp_comp_id id);
enum mtk_mdp_comp_id mdp_cfg_get_id_public(struct mdp_dev *mdp_dev, s32 id);
bool mdp_cfg_comp_is_dummy(struct mdp_dev *mdp_dev, s32 inner_id);
#endif /* __MTK_MDP3_CFG_H__ */
@@ -0,0 +1,724 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#include <linux/mailbox_controller.h>
#include <linux/platform_device.h>
#include "mtk-mdp3-cfg.h"
#include "mtk-mdp3-cmdq.h"
#include "mtk-mdp3-comp.h"
#include "mtk-mdp3-core.h"
#include "mtk-mdp3-m2m.h"
#include "mtk-img-ipi.h"
#define MDP_PATH_MAX_COMPS IMG_MAX_COMPONENTS
struct mdp_path {
struct mdp_dev *mdp_dev;
struct mdp_comp_ctx comps[MDP_PATH_MAX_COMPS];
u32 num_comps;
const struct img_config *config;
const struct img_ipi_frameparam *param;
const struct v4l2_rect *composes[IMG_MAX_HW_OUTPUTS];
struct v4l2_rect bounds[IMG_MAX_HW_OUTPUTS];
};
#define has_op(ctx, op) \
((ctx)->comp->ops && (ctx)->comp->ops->op)
#define call_op(ctx, op, ...) \
(has_op(ctx, op) ? (ctx)->comp->ops->op(ctx, ##__VA_ARGS__) : 0)
static bool is_output_disabled(int p_id, const struct img_compparam *param, u32 count)
{
u32 num = 0;
bool dis_output = false;
bool dis_tile = false;
if (CFG_CHECK(MT8183, p_id)) {
num = CFG_COMP(MT8183, param, num_subfrms);
dis_output = CFG_COMP(MT8183, param, frame.output_disable);
dis_tile = CFG_COMP(MT8183, param, frame.output_disable);
} else if (CFG_CHECK(MT8195, p_id)) {
num = CFG_COMP(MT8195, param, num_subfrms);
dis_output = CFG_COMP(MT8195, param, frame.output_disable);
dis_tile = CFG_COMP(MT8195, param, frame.output_disable);
}
return (count < num) ? (dis_output || dis_tile) : true;
}
static struct mtk_mutex *__get_mutex(const struct mdp_dev *mdp_dev,
const struct mdp_pipe_info *p)
{
return mdp_dev->mm_subsys[p->sub_id].mdp_mutex[p->mutex_id];
}
static u8 __get_pp_num(enum mdp_stream_type type)
{
switch (type) {
case MDP_STREAM_TYPE_DUAL_BITBLT:
return MDP_PP_USED_2;
default:
return MDP_PP_USED_1;
}
}
static enum mdp_pipe_id __get_pipe(const struct mdp_dev *mdp_dev,
enum mtk_mdp_comp_id id)
{
enum mdp_pipe_id pipe_id;
switch (id) {
case MDP_COMP_RDMA0:
pipe_id = MDP_PIPE_RDMA0;
break;
case MDP_COMP_ISP_IMGI:
pipe_id = MDP_PIPE_IMGI;
break;
case MDP_COMP_WPEI:
pipe_id = MDP_PIPE_WPEI;
break;
case MDP_COMP_WPEI2:
pipe_id = MDP_PIPE_WPEI2;
break;
case MDP_COMP_RDMA1:
pipe_id = MDP_PIPE_RDMA1;
break;
case MDP_COMP_RDMA2:
pipe_id = MDP_PIPE_RDMA2;
break;
case MDP_COMP_RDMA3:
pipe_id = MDP_PIPE_RDMA3;
break;
default:
/* Avoid exceptions when operating MUTEX */
pipe_id = MDP_PIPE_RDMA0;
dev_err(&mdp_dev->pdev->dev, "Unknown pipeline id %d", id);
break;
}
return pipe_id;
}
static struct img_config *__get_config_offset(struct mdp_dev *mdp,
struct mdp_cmdq_param *param,
u8 pp_idx)
{
const int p_id = mdp->mdp_data->mdp_plat_id;
struct device *dev = &mdp->pdev->dev;
void *cfg_c, *cfg_n;
long bound = mdp->vpu.config_size;
if (pp_idx >= mdp->mdp_data->pp_used)
goto err_param;
if (CFG_CHECK(MT8183, p_id)) {
cfg_c = CFG_OFST(MT8183, param->config, pp_idx);
cfg_n = CFG_OFST(MT8183, param->config, pp_idx + 1);
} else if (CFG_CHECK(MT8195, p_id)) {
cfg_c = CFG_OFST(MT8195, param->config, pp_idx);
cfg_n = CFG_OFST(MT8195, param->config, pp_idx + 1);
} else {
goto err_param;
}
if ((long)cfg_n - (long)mdp->vpu.config > bound) {
dev_err(dev, "config offset %ld OOB %ld\n", (long)cfg_n, bound);
cfg_c = ERR_PTR(-EFAULT);
}
return (struct img_config *)cfg_c;
err_param:
cfg_c = ERR_PTR(-EINVAL);
return (struct img_config *)cfg_c;
}
static int mdp_path_subfrm_require(const struct mdp_path *path,
struct mdp_cmdq_cmd *cmd,
struct mdp_pipe_info *p, u32 count)
{
const int p_id = path->mdp_dev->mdp_data->mdp_plat_id;
const struct mdp_comp_ctx *ctx;
const struct mtk_mdp_driver_data *data = path->mdp_dev->mdp_data;
struct mtk_mutex *mutex;
int id, index;
u32 num_comp = 0;
if (CFG_CHECK(MT8183, p_id))
num_comp = CFG_GET(MT8183, path->config, num_components);
else if (CFG_CHECK(MT8195, p_id))
num_comp = CFG_GET(MT8195, path->config, num_components);
/* Decide which mutex to use based on the current pipeline */
index = __get_pipe(path->mdp_dev, path->comps[0].comp->public_id);
memcpy(p, &data->pipe_info[index], sizeof(struct mdp_pipe_info));
mutex = __get_mutex(path->mdp_dev, p);
/* Set mutex mod */
for (index = 0; index < num_comp; index++) {
s32 inner_id = MDP_COMP_NONE;
const u32 *mutex_idx;
const struct mdp_comp_blend *b;
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
if (is_output_disabled(p_id, ctx->param, count))
continue;
mutex_idx = data->mdp_mutex_table_idx;
id = ctx->comp->public_id;
mtk_mutex_write_mod(mutex, mutex_idx[id], false);
b = &data->comp_data[id].blend;
if (b && b->aid_mod)
mtk_mutex_write_mod(mutex, mutex_idx[b->b_id], false);
}
mtk_mutex_write_sof(mutex, MUTEX_SOF_IDX_SINGLE_MODE);
return 0;
}
static int mdp_path_subfrm_run(const struct mdp_path *path,
struct mdp_cmdq_cmd *cmd,
struct mdp_pipe_info *p, u32 count)
{
const int p_id = path->mdp_dev->mdp_data->mdp_plat_id;
const struct mdp_comp_ctx *ctx;
struct device *dev = &path->mdp_dev->pdev->dev;
struct mtk_mutex *mutex;
int index;
u32 num_comp = 0;
s32 event;
s32 inner_id = MDP_COMP_NONE;
if (-1 == p->mutex_id) {
dev_err(dev, "Incorrect mutex id");
return -EINVAL;
}
if (CFG_CHECK(MT8183, p_id))
num_comp = CFG_GET(MT8183, path->config, num_components);
else if (CFG_CHECK(MT8195, p_id))
num_comp = CFG_GET(MT8195, path->config, num_components);
/* Wait WROT SRAM shared to DISP RDMA */
/* Clear SOF event for each engine */
for (index = 0; index < num_comp; index++) {
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
if (is_output_disabled(p_id, ctx->param, count))
continue;
event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF];
if (event != MDP_GCE_NO_EVENT)
MM_REG_CLEAR(cmd, event);
}
/* Enable the mutex */
mutex = __get_mutex(path->mdp_dev, p);
mtk_mutex_enable_by_cmdq(mutex, (void *)&cmd->pkt);
/* Wait SOF events and clear mutex modules (optional) */
for (index = 0; index < num_comp; index++) {
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
if (is_output_disabled(p_id, ctx->param, count))
continue;
event = ctx->comp->gce_event[MDP_GCE_EVENT_SOF];
if (event != MDP_GCE_NO_EVENT)
MM_REG_WAIT(cmd, event);
}
return 0;
}
static int mdp_path_ctx_init(struct mdp_dev *mdp, struct mdp_path *path)
{
const int p_id = mdp->mdp_data->mdp_plat_id;
void *param = NULL;
int index, ret;
u32 num_comp = 0;
if (CFG_CHECK(MT8183, p_id))
num_comp = CFG_GET(MT8183, path->config, num_components);
else if (CFG_CHECK(MT8195, p_id))
num_comp = CFG_GET(MT8195, path->config, num_components);
if (num_comp < 1)
return -EINVAL;
for (index = 0; index < num_comp; index++) {
s32 inner_id = MDP_COMP_NONE;
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
if (CFG_CHECK(MT8183, p_id))
param = (void *)CFG_ADDR(MT8183, path->config, components[index]);
else if (CFG_CHECK(MT8195, p_id))
param = (void *)CFG_ADDR(MT8195, path->config, components[index]);
ret = mdp_comp_ctx_config(mdp, &path->comps[index],
param, path->param);
if (ret)
return ret;
}
return 0;
}
static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd,
struct mdp_path *path, u32 count)
{
const int p_id = path->mdp_dev->mdp_data->mdp_plat_id;
const struct img_mmsys_ctrl *ctrl = NULL;
const struct img_mux *set;
struct mdp_comp_ctx *ctx;
struct mdp_pipe_info pipe;
int index, ret;
u32 num_comp = 0;
s32 inner_id = MDP_COMP_NONE;
if (CFG_CHECK(MT8183, p_id))
num_comp = CFG_GET(MT8183, path->config, num_components);
else if (CFG_CHECK(MT8195, p_id))
num_comp = CFG_GET(MT8195, path->config, num_components);
if (CFG_CHECK(MT8183, p_id))
ctrl = CFG_ADDR(MT8183, path->config, ctrls[count]);
else if (CFG_CHECK(MT8195, p_id))
ctrl = CFG_ADDR(MT8195, path->config, ctrls[count]);
/* Acquire components */
ret = mdp_path_subfrm_require(path, cmd, &pipe, count);
if (ret)
return ret;
/* Enable mux settings */
for (index = 0; index < ctrl->num_sets; index++) {
set = &ctrl->sets[index];
cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, set->value);
}
/* Config sub-frame information */
for (index = (num_comp - 1); index >= 0; index--) {
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
if (is_output_disabled(p_id, ctx->param, count))
continue;
ret = call_op(ctx, config_subfrm, cmd, count);
if (ret)
return ret;
}
/* Run components */
ret = mdp_path_subfrm_run(path, cmd, &pipe, count);
if (ret)
return ret;
/* Wait components done */
for (index = 0; index < num_comp; index++) {
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
if (is_output_disabled(p_id, ctx->param, count))
continue;
ret = call_op(ctx, wait_comp_event, cmd);
if (ret)
return ret;
}
/* Advance to the next sub-frame */
for (index = 0; index < num_comp; index++) {
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
ret = call_op(ctx, advance_subfrm, cmd, count);
if (ret)
return ret;
}
/* Disable mux settings */
for (index = 0; index < ctrl->num_sets; index++) {
set = &ctrl->sets[index];
cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, 0);
}
return 0;
}
static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd,
struct mdp_path *path)
{
const int p_id = mdp->mdp_data->mdp_plat_id;
struct mdp_comp_ctx *ctx;
int index, count, ret;
u32 num_comp = 0;
u32 num_sub = 0;
s32 inner_id = MDP_COMP_NONE;
if (CFG_CHECK(MT8183, p_id))
num_comp = CFG_GET(MT8183, path->config, num_components);
else if (CFG_CHECK(MT8195, p_id))
num_comp = CFG_GET(MT8195, path->config, num_components);
if (CFG_CHECK(MT8183, p_id))
num_sub = CFG_GET(MT8183, path->config, num_subfrms);
else if (CFG_CHECK(MT8195, p_id))
num_sub = CFG_GET(MT8195, path->config, num_subfrms);
/* Config path frame */
/* Reset components */
for (index = 0; index < num_comp; index++) {
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
ret = call_op(ctx, init_comp, cmd);
if (ret)
return ret;
}
/* Config frame mode */
for (index = 0; index < num_comp; index++) {
const struct v4l2_rect *compose;
u32 out = 0;
ctx = &path->comps[index];
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
if (CFG_CHECK(MT8183, p_id))
out = CFG_COMP(MT8183, ctx->param, outputs[0]);
else if (CFG_CHECK(MT8195, p_id))
out = CFG_COMP(MT8195, ctx->param, outputs[0]);
compose = path->composes[out];
ret = call_op(ctx, config_frame, cmd, compose);
if (ret)
return ret;
}
/* Config path sub-frames */
for (count = 0; count < num_sub; count++) {
ret = mdp_path_config_subfrm(cmd, path, count);
if (ret)
return ret;
}
/* Post processing information */
for (index = 0; index < num_comp; index++) {
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[index].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[index].type);
if (mdp_cfg_comp_is_dummy(path->mdp_dev, inner_id))
continue;
ctx = &path->comps[index];
ret = call_op(ctx, post_process, cmd);
if (ret)
return ret;
}
return 0;
}
static void mdp_auto_release_work(struct work_struct *work)
{
struct mdp_cmdq_cmd *cmd;
struct mdp_dev *mdp;
struct mtk_mutex *mutex;
enum mdp_pipe_id pipe_id;
cmd = container_of(work, struct mdp_cmdq_cmd, auto_release_work);
mdp = cmd->mdp;
pipe_id = __get_pipe(mdp, cmd->comps[0].public_id);
mutex = __get_mutex(mdp, &mdp->mdp_data->pipe_info[pipe_id]);
mtk_mutex_unprepare(mutex);
mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps,
cmd->num_comps);
if (refcount_dec_and_test(&mdp->job_count)) {
if (cmd->mdp_ctx)
mdp_m2m_job_finish(cmd->mdp_ctx);
if (cmd->user_cmdq_cb) {
struct cmdq_cb_data user_cb_data;
user_cb_data.sta = cmd->data->sta;
user_cb_data.pkt = cmd->data->pkt;
cmd->user_cmdq_cb(user_cb_data);
}
wake_up(&mdp->callback_wq);
}
cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt);
kfree(cmd->comps);
cmd->comps = NULL;
kfree(cmd);
cmd = NULL;
}
static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg)
{
struct mdp_cmdq_cmd *cmd;
struct cmdq_cb_data *data;
struct mdp_dev *mdp;
struct device *dev;
enum mdp_pipe_id pipe_id;
if (!mssg) {
pr_info("%s:no callback data\n", __func__);
return;
}
data = (struct cmdq_cb_data *)mssg;
cmd = container_of(data->pkt, struct mdp_cmdq_cmd, pkt);
cmd->data = data;
mdp = cmd->mdp;
dev = &mdp->pdev->dev;
INIT_WORK(&cmd->auto_release_work, mdp_auto_release_work);
if (!queue_work(mdp->clock_wq, &cmd->auto_release_work)) {
struct mtk_mutex *mutex;
dev_err(dev, "%s:queue_work fail!\n", __func__);
pipe_id = __get_pipe(mdp, cmd->comps[0].public_id);
mutex = __get_mutex(mdp, &mdp->mdp_data->pipe_info[pipe_id]);
mtk_mutex_unprepare(mutex);
mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps,
cmd->num_comps);
if (refcount_dec_and_test(&mdp->job_count))
wake_up(&mdp->callback_wq);
cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt);
kfree(cmd->comps);
cmd->comps = NULL;
kfree(cmd);
cmd = NULL;
}
}
static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp,
struct mdp_cmdq_param *param,
u8 pp_idx)
{
struct mdp_path *path = NULL;
struct mdp_cmdq_cmd *cmd = NULL;
struct mdp_comp *comps = NULL;
struct device *dev = &mdp->pdev->dev;
const int p_id = mdp->mdp_data->mdp_plat_id;
struct img_config *config;
struct mtk_mutex *mutex = NULL;
enum mdp_pipe_id pipe_id;
int i, ret = -ECANCELED;
u32 num_comp;
config = __get_config_offset(mdp, param, pp_idx);
if (IS_ERR(config)) {
ret = PTR_ERR(config);
goto err_uninit;
}
cmd = kzalloc_obj(*cmd);
if (!cmd) {
ret = -ENOMEM;
goto err_uninit;
}
ret = cmdq_pkt_create(mdp->cmdq_clt[pp_idx], &cmd->pkt, SZ_16K);
if (ret)
goto err_free_cmd;
if (CFG_CHECK(MT8183, p_id)) {
num_comp = CFG_GET(MT8183, param->config, num_components);
} else if (CFG_CHECK(MT8195, p_id)) {
num_comp = CFG_GET(MT8195, param->config, num_components);
} else {
ret = -EINVAL;
goto err_destroy_pkt;
}
comps = kzalloc_objs(*comps, num_comp);
if (!comps) {
ret = -ENOMEM;
goto err_destroy_pkt;
}
path = kzalloc_obj(*path);
if (!path) {
ret = -ENOMEM;
goto err_free_comps;
}
path->mdp_dev = mdp;
path->config = config;
path->param = param->param;
for (i = 0; i < param->param->num_outputs; i++) {
path->bounds[i].left = 0;
path->bounds[i].top = 0;
path->bounds[i].width =
param->param->outputs[i].buffer.format.width;
path->bounds[i].height =
param->param->outputs[i].buffer.format.height;
path->composes[i] = param->composes[i] ?
param->composes[i] : &path->bounds[i];
}
ret = mdp_path_ctx_init(mdp, path);
if (ret) {
dev_err(dev, "mdp_path_ctx_init error %d\n", pp_idx);
goto err_free_path;
}
pipe_id = __get_pipe(mdp, path->comps[0].comp->public_id);
mutex = __get_mutex(mdp, &mdp->mdp_data->pipe_info[pipe_id]);
ret = mtk_mutex_prepare(mutex);
if (ret) {
dev_err(dev, "Fail to enable mutex %d clk\n", pp_idx);
goto err_free_path;
}
ret = mdp_path_config(mdp, cmd, path);
if (ret) {
dev_err(dev, "mdp_path_config error %d\n", pp_idx);
goto err_free_path;
}
cmdq_pkt_eoc(&cmd->pkt);
cmdq_pkt_jump_rel(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]);
for (i = 0; i < num_comp; i++) {
s32 inner_id = MDP_COMP_NONE;
if (CFG_CHECK(MT8183, p_id))
inner_id = CFG_GET(MT8183, path->config, components[i].type);
else if (CFG_CHECK(MT8195, p_id))
inner_id = CFG_GET(MT8195, path->config, components[i].type);
if (mdp_cfg_comp_is_dummy(mdp, inner_id))
continue;
memcpy(&comps[i], path->comps[i].comp,
sizeof(struct mdp_comp));
}
mdp->cmdq_clt[pp_idx]->client.rx_callback = mdp_handle_cmdq_callback;
cmd->mdp = mdp;
cmd->user_cmdq_cb = param->cmdq_cb;
cmd->user_cb_data = param->cb_data;
cmd->comps = comps;
cmd->num_comps = num_comp;
cmd->mdp_ctx = param->mdp_ctx;
cmd->pp_idx = pp_idx;
kfree(path);
return cmd;
err_free_path:
if (mutex)
mtk_mutex_unprepare(mutex);
kfree(path);
err_free_comps:
kfree(comps);
err_destroy_pkt:
cmdq_pkt_destroy(mdp->cmdq_clt[pp_idx], &cmd->pkt);
err_free_cmd:
kfree(cmd);
err_uninit:
return ERR_PTR(ret);
}
int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param)
{
struct mdp_cmdq_cmd *cmd[MDP_PP_MAX] = {NULL};
struct device *dev = &mdp->pdev->dev;
int i, ret;
u8 pp_used = __get_pp_num(param->param->type);
refcount_set(&mdp->job_count, pp_used);
if (atomic_read(&mdp->suspended)) {
refcount_set(&mdp->job_count, 0);
return -ECANCELED;
}
for (i = 0; i < pp_used; i++) {
cmd[i] = mdp_cmdq_prepare(mdp, param, i);
if (IS_ERR_OR_NULL(cmd[i])) {
ret = PTR_ERR(cmd[i]);
goto err_cancel_job;
}
}
for (i = 0; i < pp_used; i++) {
ret = mdp_comp_clocks_on(&mdp->pdev->dev, cmd[i]->comps, cmd[i]->num_comps);
if (ret)
goto err_clock_off;
}
for (i = 0; i < pp_used; i++) {
dma_sync_single_for_device(mdp->cmdq_clt[i]->chan->mbox->dev,
cmd[i]->pkt.pa_base, cmd[i]->pkt.cmd_buf_size,
DMA_TO_DEVICE);
ret = mbox_send_message(mdp->cmdq_clt[i]->chan, &cmd[i]->pkt);
if (ret < 0) {
dev_err(dev, "mbox send message fail %d!\n", ret);
i = pp_used;
goto err_clock_off;
}
mbox_client_txdone(mdp->cmdq_clt[i]->chan, 0);
}
return 0;
err_clock_off:
while (--i >= 0)
mdp_comp_clocks_off(&mdp->pdev->dev, cmd[i]->comps,
cmd[i]->num_comps);
err_cancel_job:
refcount_set(&mdp->job_count, 0);
return ret;
}
EXPORT_SYMBOL_GPL(mdp_cmdq_send);
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_CMDQ_H__
#define __MTK_MDP3_CMDQ_H__
#include <linux/platform_device.h>
#include <linux/videodev2.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk-img-ipi.h"
struct mdp_cmdq_param {
struct img_config *config;
struct img_ipi_frameparam *param;
const struct v4l2_rect *composes[IMG_MAX_HW_OUTPUTS];
void (*cmdq_cb)(struct cmdq_cb_data data);
void *cb_data;
void *mdp_ctx;
};
struct mdp_cmdq_cmd {
struct work_struct auto_release_work;
struct cmdq_pkt pkt;
s32 *event;
struct mdp_dev *mdp;
struct cmdq_cb_data *data;
void (*user_cmdq_cb)(struct cmdq_cb_data data);
void *user_cb_data;
struct mdp_comp *comps;
void *mdp_ctx;
u8 num_comps;
u8 pp_idx;
};
struct mdp_dev;
int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param);
#endif /* __MTK_MDP3_CMDQ_H__ */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,264 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_COMP_H__
#define __MTK_MDP3_COMP_H__
#include "mtk-mdp3-cmdq.h"
#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask) \
do { \
typeof(mask) (m) = (mask); \
cmdq_pkt_write_mask(&((cmd)->pkt), id, (base) + (ofst), \
(val), \
(((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
(0xffffffff) : (m)); \
} while (0)
#define MM_REG_WRITE(cmd, id, base, ofst, val) \
cmdq_pkt_write(&((cmd)->pkt), id, (base) + (ofst), (val))
#define MM_REG_WAIT(cmd, evt) \
do { \
typeof(cmd) (c) = (cmd); \
typeof(evt) (e) = (evt); \
cmdq_pkt_wfe(&((c)->pkt), (e), true); \
} while (0)
#define MM_REG_WAIT_NO_CLEAR(cmd, evt) \
do { \
typeof(cmd) (c) = (cmd); \
typeof(evt) (e) = (evt); \
cmdq_pkt_wfe(&((c)->pkt), (e), false); \
} while (0)
#define MM_REG_CLEAR(cmd, evt) \
do { \
typeof(cmd) (c) = (cmd); \
typeof(evt) (e) = (evt); \
cmdq_pkt_clear_event(&((c)->pkt), (e)); \
} while (0)
#define MM_REG_SET_EVENT(cmd, evt) \
do { \
typeof(cmd) (c) = (cmd); \
typeof(evt) (e) = (evt); \
cmdq_pkt_set_event(&((c)->pkt), (e)); \
} while (0)
#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask) \
do { \
typeof(_mask) (_m) = (_mask); \
cmdq_pkt_poll_mask(&((cmd)->pkt), id, \
(base) + (ofst), (val), \
(((_m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
(0xffffffff) : (_m)); \
} while (0)
#define MM_REG_POLL(cmd, id, base, ofst, val) \
cmdq_pkt_poll(&((cmd)->pkt), id, (base) + (ofst), (val))
enum mtk_mdp_comp_id {
MDP_COMP_NONE = -1, /* Invalid engine */
/* ISP */
MDP_COMP_WPEI = 0,
MDP_COMP_WPEO, /* 1 */
MDP_COMP_WPEI2, /* 2 */
MDP_COMP_WPEO2, /* 3 */
MDP_COMP_ISP_IMGI, /* 4 */
MDP_COMP_ISP_IMGO, /* 5 */
MDP_COMP_ISP_IMG2O, /* 6 */
/* IPU */
MDP_COMP_IPUI, /* 7 */
MDP_COMP_IPUO, /* 8 */
/* MDP */
MDP_COMP_CAMIN, /* 9 */
MDP_COMP_CAMIN2, /* 10 */
MDP_COMP_RDMA0, /* 11 */
MDP_COMP_RDMA1, /* 12 */
MDP_COMP_RDMA2, /* 13 */
MDP_COMP_RDMA3, /* 14 */
MDP_COMP_AAL0, /* 15 */
MDP_COMP_AAL1, /* 16 */
MDP_COMP_AAL2, /* 17 */
MDP_COMP_AAL3, /* 18 */
MDP_COMP_CCORR0, /* 19 */
MDP_COMP_RSZ0, /* 20 */
MDP_COMP_RSZ1, /* 21 */
MDP_COMP_RSZ2, /* 22 */
MDP_COMP_RSZ3, /* 23 */
MDP_COMP_TDSHP0, /* 24 */
MDP_COMP_TDSHP1, /* 25 */
MDP_COMP_TDSHP2, /* 26 */
MDP_COMP_TDSHP3, /* 27 */
MDP_COMP_COLOR0, /* 28 */
MDP_COMP_COLOR1, /* 29 */
MDP_COMP_COLOR2, /* 30 */
MDP_COMP_COLOR3, /* 31 */
MDP_COMP_PATH0_SOUT, /* 32 */
MDP_COMP_PATH1_SOUT, /* 33 */
MDP_COMP_WROT0, /* 34 */
MDP_COMP_WROT1, /* 35 */
MDP_COMP_WROT2, /* 36 */
MDP_COMP_WROT3, /* 37 */
MDP_COMP_WDMA, /* 38 */
MDP_COMP_SPLIT, /* 39 */
MDP_COMP_SPLIT2, /* 40 */
MDP_COMP_STITCH, /* 41 */
MDP_COMP_FG0, /* 42 */
MDP_COMP_FG1, /* 43 */
MDP_COMP_FG2, /* 44 */
MDP_COMP_FG3, /* 45 */
MDP_COMP_TO_SVPP2MOUT, /* 46 */
MDP_COMP_TO_SVPP3MOUT, /* 47 */
MDP_COMP_TO_WARP0MOUT, /* 48 */
MDP_COMP_TO_WARP1MOUT, /* 49 */
MDP_COMP_VPP0_SOUT, /* 50 */
MDP_COMP_VPP1_SOUT, /* 51 */
MDP_COMP_PQ0_SOUT, /* 52 */
MDP_COMP_PQ1_SOUT, /* 53 */
MDP_COMP_HDR0, /* 54 */
MDP_COMP_HDR1, /* 55 */
MDP_COMP_HDR2, /* 56 */
MDP_COMP_HDR3, /* 57 */
MDP_COMP_OVL0, /* 58 */
MDP_COMP_OVL1, /* 59 */
MDP_COMP_PAD0, /* 60 */
MDP_COMP_PAD1, /* 61 */
MDP_COMP_PAD2, /* 62 */
MDP_COMP_PAD3, /* 63 */
MDP_COMP_TCC0, /* 64 */
MDP_COMP_TCC1, /* 65 */
MDP_COMP_MERGE2, /* 66 */
MDP_COMP_MERGE3, /* 67 */
MDP_COMP_VDO0DL0, /* 68 */
MDP_COMP_VDO1DL0, /* 69 */
MDP_COMP_VDO0DL1, /* 70 */
MDP_COMP_VDO1DL1, /* 71 */
MDP_MAX_COMP_COUNT /* ALWAYS keep at the end */
};
enum mdp_comp_type {
MDP_COMP_TYPE_INVALID = 0,
MDP_COMP_TYPE_RDMA,
MDP_COMP_TYPE_RSZ,
MDP_COMP_TYPE_WROT,
MDP_COMP_TYPE_WDMA,
MDP_COMP_TYPE_PATH,
MDP_COMP_TYPE_TDSHP,
MDP_COMP_TYPE_COLOR,
MDP_COMP_TYPE_DRE,
MDP_COMP_TYPE_CCORR,
MDP_COMP_TYPE_AAL,
MDP_COMP_TYPE_TCC,
MDP_COMP_TYPE_HDR,
MDP_COMP_TYPE_SPLIT,
MDP_COMP_TYPE_STITCH,
MDP_COMP_TYPE_FG,
MDP_COMP_TYPE_OVL,
MDP_COMP_TYPE_PAD,
MDP_COMP_TYPE_MERGE,
MDP_COMP_TYPE_IMGI,
MDP_COMP_TYPE_WPEI,
MDP_COMP_TYPE_EXTO, /* External path */
MDP_COMP_TYPE_DL_PATH, /* Direct-link path */
MDP_COMP_TYPE_DUMMY,
MDP_COMP_TYPE_COUNT /* ALWAYS keep at the end */
};
#define MDP_GCE_NO_EVENT (-1)
enum {
MDP_GCE_EVENT_SOF = 0,
MDP_GCE_EVENT_EOF = 1,
MDP_GCE_EVENT_MAX,
};
struct mdp_comp_match {
enum mdp_comp_type type;
u32 alias_id;
s32 inner_id;
s32 subsys_id;
};
/* Used to describe the item order in MDP property */
struct mdp_comp_info {
u32 clk_num;
u32 clk_ofst;
u32 dts_reg_ofst;
};
struct mdp_comp_blend {
enum mtk_mdp_comp_id b_id;
bool aid_mod;
bool aid_clk;
};
struct mdp_comp_data {
struct mdp_comp_match match;
struct mdp_comp_info info;
struct mdp_comp_blend blend;
};
struct mdp_comp_ops;
struct mdp_comp {
struct mdp_dev *mdp_dev;
void __iomem *regs;
phys_addr_t reg_base;
u8 subsys_id;
u8 clk_num;
struct clk **clks;
struct device *comp_dev;
enum mdp_comp_type type;
enum mtk_mdp_comp_id public_id;
s32 inner_id;
u32 alias_id;
s32 gce_event[MDP_GCE_EVENT_MAX];
const struct mdp_comp_ops *ops;
};
struct mdp_comp_ctx {
struct mdp_comp *comp;
const struct img_compparam *param;
const struct img_input *input;
const struct img_output *outputs[IMG_MAX_HW_OUTPUTS];
};
struct mdp_comp_ops {
s64 (*get_comp_flag)(const struct mdp_comp_ctx *ctx);
int (*init_comp)(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd);
int (*config_frame)(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd,
const struct v4l2_rect *compose);
int (*config_subfrm)(struct mdp_comp_ctx *ctx,
struct mdp_cmdq_cmd *cmd, u32 index);
int (*wait_comp_event)(struct mdp_comp_ctx *ctx,
struct mdp_cmdq_cmd *cmd);
int (*advance_subfrm)(struct mdp_comp_ctx *ctx,
struct mdp_cmdq_cmd *cmd, u32 index);
int (*post_process)(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd);
};
struct mdp_dev;
int mdp_comp_config(struct mdp_dev *mdp);
void mdp_comp_destroy(struct mdp_dev *mdp);
int mdp_comp_clock_on(struct device *dev, struct mdp_comp *comp);
void mdp_comp_clock_off(struct device *dev, struct mdp_comp *comp);
int mdp_comp_clocks_on(struct device *dev, struct mdp_comp *comps, int num);
void mdp_comp_clocks_off(struct device *dev, struct mdp_comp *comps, int num);
int mdp_comp_ctx_config(struct mdp_dev *mdp, struct mdp_comp_ctx *ctx,
const struct img_compparam *param,
const struct img_ipi_frameparam *frame);
#endif /* __MTK_MDP3_COMP_H__ */
@@ -0,0 +1,427 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/remoteproc.h>
#include <linux/remoteproc/mtk_scp.h>
#include <media/videobuf2-dma-contig.h>
#include "mtk-mdp3-core.h"
#include "mtk-mdp3-cfg.h"
#include "mtk-mdp3-m2m.h"
static const struct of_device_id mdp_of_ids[] = {
{ .compatible = "mediatek,mt8183-mdp3-rdma",
.data = &mt8183_mdp_driver_data,
},
{ .compatible = "mediatek,mt8188-mdp3-rdma",
.data = &mt8188_mdp_driver_data,
},
{ .compatible = "mediatek,mt8195-mdp3-rdma",
.data = &mt8195_mdp_driver_data,
},
{ .compatible = "mediatek,mt8195-mdp3-wrot",
.data = &mt8195_mdp_driver_data,
},
{},
};
MODULE_DEVICE_TABLE(of, mdp_of_ids);
static struct platform_device *__get_pdev_by_id(struct platform_device *pdev,
struct platform_device *from,
enum mdp_infra_id id)
{
struct device_node *node, *f = NULL;
struct platform_device *mdp_pdev = NULL;
const struct mtk_mdp_driver_data *mdp_data;
const char *compat;
if (!pdev)
return NULL;
if (id < MDP_INFRA_MMSYS || id >= MDP_INFRA_MAX) {
dev_err(&pdev->dev, "Illegal infra id %d\n", id);
return NULL;
}
mdp_data = of_device_get_match_data(&pdev->dev);
if (!mdp_data) {
dev_err(&pdev->dev, "have no driver data to find node\n");
return NULL;
}
compat = mdp_data->mdp_probe_infra[id].compatible;
if (strlen(compat) == 0)
return NULL;
if (from)
f = from->dev.of_node;
node = of_find_compatible_node(f, NULL, compat);
if (WARN_ON(!node)) {
dev_err(&pdev->dev, "find node from id %d failed\n", id);
return NULL;
}
mdp_pdev = of_find_device_by_node(node);
of_node_put(node);
if (WARN_ON(!mdp_pdev)) {
dev_err(&pdev->dev, "find pdev from id %d failed\n", id);
return NULL;
}
return mdp_pdev;
}
int mdp_vpu_get_locked(struct mdp_dev *mdp)
{
int ret = 0;
if (mdp->vpu_count++ == 0) {
ret = rproc_boot(mdp->rproc_handle);
if (ret) {
dev_err(&mdp->pdev->dev,
"vpu_load_firmware failed %d\n", ret);
goto err_load_vpu;
}
ret = mdp_vpu_register(mdp);
if (ret) {
dev_err(&mdp->pdev->dev,
"mdp_vpu register failed %d\n", ret);
goto err_reg_vpu;
}
ret = mdp_vpu_dev_init(&mdp->vpu, mdp->scp, &mdp->vpu_lock);
if (ret) {
dev_err(&mdp->pdev->dev,
"mdp_vpu device init failed %d\n", ret);
goto err_init_vpu;
}
}
return 0;
err_init_vpu:
mdp_vpu_unregister(mdp);
err_reg_vpu:
err_load_vpu:
mdp->vpu_count--;
return ret;
}
void mdp_vpu_put_locked(struct mdp_dev *mdp)
{
if (--mdp->vpu_count == 0) {
mdp_vpu_dev_deinit(&mdp->vpu);
mdp_vpu_unregister(mdp);
}
}
void mdp_video_device_release(struct video_device *vdev)
{
struct mdp_dev *mdp = (struct mdp_dev *)video_get_drvdata(vdev);
int i;
for (i = 0; i < mdp->mdp_data->pp_used; i++)
if (mdp->cmdq_clt[i])
cmdq_mbox_destroy(mdp->cmdq_clt[i]);
scp_put(mdp->scp);
destroy_workqueue(mdp->job_wq);
destroy_workqueue(mdp->clock_wq);
pm_runtime_disable(&mdp->pdev->dev);
vb2_dma_contig_clear_max_seg_size(&mdp->pdev->dev);
mdp_comp_destroy(mdp);
for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
enum mdp_mm_subsys_id idx;
struct mtk_mutex *m;
u32 m_id;
idx = mdp->mdp_data->pipe_info[i].sub_id;
m_id = mdp->mdp_data->pipe_info[i].mutex_id;
m = mdp->mm_subsys[idx].mdp_mutex[m_id];
if (!IS_ERR_OR_NULL(m))
mtk_mutex_put(m);
}
mdp_vpu_shared_mem_free(&mdp->vpu);
v4l2_m2m_release(mdp->m2m_dev);
kfree(mdp);
}
static void mdp_put_device(void *_dev)
{
struct device *dev = _dev;
put_device(dev);
}
static int mdp_mm_subsys_deploy(struct mdp_dev *mdp, enum mdp_infra_id id)
{
struct platform_device *mm_pdev = NULL;
struct device **dev;
int ret;
int i;
if (!mdp)
return -EINVAL;
for (i = 0; i < MDP_MM_SUBSYS_MAX; i++) {
const char *compat;
enum mdp_infra_id sub_id = id + i;
switch (id) {
case MDP_INFRA_MMSYS:
dev = &mdp->mm_subsys[i].mmsys;
break;
case MDP_INFRA_MUTEX:
dev = &mdp->mm_subsys[i].mutex;
break;
default:
dev_err(&mdp->pdev->dev, "Unknown infra id %d", id);
return -EINVAL;
}
/*
* Not every chip has multiple multimedia subsystems, so
* the config may be null.
*/
compat = mdp->mdp_data->mdp_probe_infra[sub_id].compatible;
if (strlen(compat) == 0)
continue;
mm_pdev = __get_pdev_by_id(mdp->pdev, mm_pdev, sub_id);
if (WARN_ON(!mm_pdev))
return -ENODEV;
ret = devm_add_action_or_reset(&mdp->pdev->dev, mdp_put_device,
&mm_pdev->dev);
if (ret)
return ret;
*dev = &mm_pdev->dev;
}
return 0;
}
static int mdp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mdp_dev *mdp;
struct platform_device *mm_pdev;
struct resource *res;
int ret, i, mutex_id;
mdp = kzalloc_obj(*mdp);
if (!mdp) {
ret = -ENOMEM;
goto err_return;
}
mdp->pdev = pdev;
mdp->mdp_data = of_device_get_match_data(&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res->start != mdp->mdp_data->mdp_con_res) {
platform_set_drvdata(pdev, mdp);
goto success_return;
}
ret = mdp_mm_subsys_deploy(mdp, MDP_INFRA_MMSYS);
if (ret)
goto err_destroy_device;
ret = mdp_mm_subsys_deploy(mdp, MDP_INFRA_MUTEX);
if (ret)
goto err_destroy_device;
for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
enum mdp_mm_subsys_id idx;
struct mtk_mutex **m;
idx = mdp->mdp_data->pipe_info[i].sub_id;
mutex_id = mdp->mdp_data->pipe_info[i].mutex_id;
m = &mdp->mm_subsys[idx].mdp_mutex[mutex_id];
if (!IS_ERR_OR_NULL(*m))
continue;
*m = mtk_mutex_get(mdp->mm_subsys[idx].mutex);
if (IS_ERR(*m)) {
ret = PTR_ERR(*m);
goto err_free_mutex;
}
}
ret = mdp_comp_config(mdp);
if (ret) {
dev_err(dev, "Failed to config mdp components\n");
goto err_free_mutex;
}
mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME,
WQ_FREEZABLE | WQ_PERCPU, 0);
if (!mdp->job_wq) {
dev_err(dev, "Unable to create job workqueue\n");
ret = -ENOMEM;
goto err_deinit_comp;
}
mdp->clock_wq = alloc_workqueue(MDP_MODULE_NAME "-clock",
WQ_FREEZABLE | WQ_PERCPU,
0);
if (!mdp->clock_wq) {
dev_err(dev, "Unable to create clock workqueue\n");
ret = -ENOMEM;
goto err_destroy_job_wq;
}
mdp->scp = scp_get(pdev);
if (!mdp->scp) {
mm_pdev = __get_pdev_by_id(pdev, NULL, MDP_INFRA_SCP);
if (WARN_ON(!mm_pdev)) {
dev_err(&pdev->dev, "Could not get scp device\n");
ret = -ENODEV;
goto err_destroy_clock_wq;
}
mdp->scp = platform_get_drvdata(mm_pdev);
put_device(&mm_pdev->dev);
}
mdp->rproc_handle = scp_get_rproc(mdp->scp);
dev_dbg(&pdev->dev, "MDP rproc_handle: %p", mdp->rproc_handle);
mutex_init(&mdp->vpu_lock);
mutex_init(&mdp->m2m_lock);
for (i = 0; i < mdp->mdp_data->pp_used; i++) {
mdp->cmdq_clt[i] = cmdq_mbox_create(dev, i);
if (IS_ERR(mdp->cmdq_clt[i])) {
ret = PTR_ERR(mdp->cmdq_clt[i]);
goto err_mbox_destroy;
}
mdp->cmdq_shift_pa[i] = cmdq_get_shift_pa(mdp->cmdq_clt[i]->chan);
}
init_waitqueue_head(&mdp->callback_wq);
ida_init(&mdp->mdp_ida);
platform_set_drvdata(pdev, mdp);
vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
ret = v4l2_device_register(dev, &mdp->v4l2_dev);
if (ret) {
dev_err(dev, "Failed to register v4l2 device\n");
ret = -EINVAL;
goto err_mbox_destroy;
}
ret = mdp_m2m_device_register(mdp);
if (ret) {
v4l2_err(&mdp->v4l2_dev, "Failed to register m2m device\n");
goto err_unregister_device;
}
success_return:
dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);
return 0;
err_unregister_device:
v4l2_device_unregister(&mdp->v4l2_dev);
err_mbox_destroy:
while (--i >= 0)
cmdq_mbox_destroy(mdp->cmdq_clt[i]);
scp_put(mdp->scp);
err_destroy_clock_wq:
destroy_workqueue(mdp->clock_wq);
err_destroy_job_wq:
destroy_workqueue(mdp->job_wq);
err_deinit_comp:
mdp_comp_destroy(mdp);
err_free_mutex:
for (i = 0; i < mdp->mdp_data->pipe_info_len; i++) {
enum mdp_mm_subsys_id idx;
struct mtk_mutex *m;
idx = mdp->mdp_data->pipe_info[i].sub_id;
mutex_id = mdp->mdp_data->pipe_info[i].mutex_id;
m = mdp->mm_subsys[idx].mdp_mutex[mutex_id];
if (!IS_ERR_OR_NULL(m))
mtk_mutex_put(m);
}
err_destroy_device:
kfree(mdp);
err_return:
dev_dbg(dev, "Errno %d\n", ret);
return ret;
}
static void mdp_remove(struct platform_device *pdev)
{
struct mdp_dev *mdp = platform_get_drvdata(pdev);
v4l2_device_unregister(&mdp->v4l2_dev);
dev_dbg(&pdev->dev, "%s driver unloaded\n", pdev->name);
}
static int __maybe_unused mdp_suspend(struct device *dev)
{
struct mdp_dev *mdp = dev_get_drvdata(dev);
int ret;
atomic_set(&mdp->suspended, 1);
if (refcount_read(&mdp->job_count)) {
ret = wait_event_timeout(mdp->callback_wq,
!refcount_read(&mdp->job_count),
2 * HZ);
if (ret == 0) {
dev_err(dev,
"%s:flushed cmdq task incomplete, count=%d\n",
__func__, refcount_read(&mdp->job_count));
return -EBUSY;
}
}
return 0;
}
static int __maybe_unused mdp_resume(struct device *dev)
{
struct mdp_dev *mdp = dev_get_drvdata(dev);
atomic_set(&mdp->suspended, 0);
return 0;
}
static const struct dev_pm_ops mdp_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(mdp_suspend, mdp_resume)
};
static struct platform_driver mdp_driver = {
.probe = mdp_probe,
.remove = mdp_remove,
.driver = {
.name = MDP_MODULE_NAME,
.pm = &mdp_pm_ops,
.of_match_table = mdp_of_ids,
},
};
module_platform_driver(mdp_driver);
MODULE_AUTHOR("Ping-Hsun Wu <ping-hsun.wu@mediatek.com>");
MODULE_DESCRIPTION("MediaTek image processor 3 driver");
MODULE_LICENSE("GPL");
@@ -0,0 +1,153 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_CORE_H__
#define __MTK_MDP3_CORE_H__
#include <media/v4l2-device.h>
#include <media/v4l2-mem2mem.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include <linux/soc/mediatek/mtk-mutex.h>
#include "mtk-mdp3-comp.h"
#include "mtk-mdp3-vpu.h"
#define MDP_MODULE_NAME "mtk-mdp3"
#define MDP_DEVICE_NAME "MediaTek MDP3"
#define MDP_PHANDLE_NAME "mediatek,mdp3"
enum mdp_infra_id {
/*
* Due to the sequential nature of function "mdp_mm_subsys_deploy",
* adding new enum. necessitates careful consideration.
*/
MDP_INFRA_MMSYS,
MDP_INFRA_MMSYS2,
MDP_INFRA_MUTEX,
MDP_INFRA_MUTEX2,
MDP_INFRA_SCP,
MDP_INFRA_MAX
};
enum mdp_mm_subsys_id {
MDP_MM_SUBSYS_0,
MDP_MM_SUBSYS_1,
MDP_MM_SUBSYS_MAX,
};
enum mdp_buffer_usage {
MDP_BUFFER_USAGE_HW_READ,
MDP_BUFFER_USAGE_MDP,
MDP_BUFFER_USAGE_MDP2,
MDP_BUFFER_USAGE_ISP,
MDP_BUFFER_USAGE_WPE,
};
struct mdp_platform_config {
bool rdma_support_10bit;
bool rdma_rsz1_sram_sharing;
bool rdma_upsample_repeat_only;
bool rdma_esl_setting;
u32 rdma_event_num;
bool rsz_disable_dcm_small_sample;
bool rsz_etc_control;
bool wrot_filter_constraint;
bool wrot_support_10bit;
u32 wrot_event_num;
u32 tdshp_hist_num;
bool tdshp_constrain;
bool tdshp_contour;
};
/* indicate which mutex is used by each pipepline */
enum mdp_pipe_id {
MDP_PIPE_WPEI,
MDP_PIPE_WPEI2,
MDP_PIPE_IMGI,
MDP_PIPE_RDMA0,
MDP_PIPE_RDMA1,
MDP_PIPE_RDMA2,
MDP_PIPE_RDMA3,
MDP_PIPE_SPLIT,
MDP_PIPE_SPLIT2,
MDP_PIPE_VPP0_SOUT,
MDP_PIPE_VPP1_SOUT,
MDP_PIPE_MAX
};
/* MDP parallel pipe control */
enum {
MDP_PP_USED_1 = 1,
MDP_PP_USED_2 = 2,
};
#define MDP_PP_MAX MDP_PP_USED_2
struct mtk_mdp_driver_data {
const int mdp_plat_id;
const resource_size_t mdp_con_res;
const struct of_device_id *mdp_probe_infra;
const struct mdp_platform_config *mdp_cfg;
const u32 *mdp_mutex_table_idx;
const struct mdp_comp_data *comp_data;
unsigned int comp_data_len;
const struct of_device_id *mdp_sub_comp_dt_ids;
const struct mdp_format *format;
unsigned int format_len;
const struct mdp_limit *def_limit;
const struct mdp_pipe_info *pipe_info;
unsigned int pipe_info_len;
const struct v4l2_rect *pp_criteria;
const u8 pp_used;
};
struct mdp_mm_subsys {
struct device *mmsys;
struct device *mutex;
struct mtk_mutex *mdp_mutex[MDP_PIPE_MAX];
};
struct mdp_dev {
struct platform_device *pdev;
struct mdp_mm_subsys mm_subsys[MDP_MM_SUBSYS_MAX];
struct mdp_comp *comp[MDP_MAX_COMP_COUNT];
const struct mtk_mdp_driver_data *mdp_data;
struct workqueue_struct *job_wq;
struct workqueue_struct *clock_wq;
struct mdp_vpu_dev vpu;
struct mtk_scp *scp;
struct rproc *rproc_handle;
/* synchronization protect for accessing vpu working buffer info */
struct mutex vpu_lock;
s32 vpu_count;
u32 id_count;
struct ida mdp_ida;
struct cmdq_client *cmdq_clt[MDP_PP_MAX];
u8 cmdq_shift_pa[MDP_PP_MAX];
wait_queue_head_t callback_wq;
struct v4l2_device v4l2_dev;
struct video_device *m2m_vdev;
struct v4l2_m2m_dev *m2m_dev;
/* synchronization protect for m2m device operation */
struct mutex m2m_lock;
atomic_t suspended;
refcount_t job_count;
};
struct mdp_pipe_info {
enum mdp_pipe_id pipe_id;
enum mdp_mm_subsys_id sub_id;
u32 mutex_id;
};
int mdp_vpu_get_locked(struct mdp_dev *mdp);
void mdp_vpu_put_locked(struct mdp_dev *mdp);
int mdp_vpu_register(struct mdp_dev *mdp);
void mdp_vpu_unregister(struct mdp_dev *mdp);
void mdp_video_device_release(struct video_device *vdev);
#endif /* __MTK_MDP3_CORE_H__ */
@@ -0,0 +1,738 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#include <linux/platform_device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-event.h>
#include <media/videobuf2-dma-contig.h>
#include "mtk-mdp3-m2m.h"
static inline struct mdp_m2m_ctx *file_to_ctx(struct file *filp)
{
return container_of(file_to_v4l2_fh(filp), struct mdp_m2m_ctx, fh);
}
static inline struct mdp_m2m_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
{
return container_of(ctrl->handler, struct mdp_m2m_ctx, ctrl_handler);
}
static inline struct mdp_frame *ctx_get_frame(struct mdp_m2m_ctx *ctx,
enum v4l2_buf_type type)
{
if (V4L2_TYPE_IS_OUTPUT(type))
return &ctx->curr_param.output;
else
return &ctx->curr_param.captures[0];
}
static inline void mdp_m2m_ctx_set_state(struct mdp_m2m_ctx *ctx, u32 state)
{
atomic_or(state, &ctx->curr_param.state);
}
static inline bool mdp_m2m_ctx_is_state_set(struct mdp_m2m_ctx *ctx, u32 mask)
{
return ((atomic_read(&ctx->curr_param.state) & mask) == mask);
}
static void mdp_m2m_process_done(void *priv, int vb_state)
{
struct mdp_m2m_ctx *ctx = priv;
struct vb2_v4l2_buffer *src_vbuf, *dst_vbuf;
src_vbuf = (struct vb2_v4l2_buffer *)
v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
dst_vbuf = (struct vb2_v4l2_buffer *)
v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
ctx->curr_param.frame_no = ctx->frame_count[MDP_M2M_SRC];
src_vbuf->sequence = ctx->frame_count[MDP_M2M_SRC]++;
dst_vbuf->sequence = ctx->frame_count[MDP_M2M_DST]++;
v4l2_m2m_buf_copy_metadata(src_vbuf, dst_vbuf);
v4l2_m2m_buf_done(src_vbuf, vb_state);
v4l2_m2m_buf_done(dst_vbuf, vb_state);
v4l2_m2m_job_finish(ctx->mdp_dev->m2m_dev, ctx->m2m_ctx);
}
static void mdp_m2m_device_run(void *priv)
{
struct mdp_m2m_ctx *ctx = priv;
struct mdp_frame *frame;
struct vb2_v4l2_buffer *src_vb, *dst_vb;
struct img_ipi_frameparam param = {};
struct mdp_cmdq_param task = {};
enum vb2_buffer_state vb_state = VB2_BUF_STATE_ERROR;
int ret;
if (mdp_m2m_ctx_is_state_set(ctx, MDP_M2M_CTX_ERROR)) {
dev_err(&ctx->mdp_dev->pdev->dev,
"mdp_m2m_ctx is in error state\n");
goto worker_end;
}
param.frame_no = ctx->curr_param.frame_no;
param.type = ctx->curr_param.type;
param.num_inputs = 1;
param.num_outputs = 1;
frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
src_vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
mdp_set_src_config(&param.inputs[0], frame, &src_vb->vb2_buf);
frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
dst_vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
mdp_set_dst_config(&param.outputs[0], frame, &dst_vb->vb2_buf);
if (mdp_check_pp_enable(ctx->mdp_dev, frame))
param.type = MDP_STREAM_TYPE_DUAL_BITBLT;
ret = mdp_vpu_process(&ctx->mdp_dev->vpu, &param);
if (ret) {
dev_err(&ctx->mdp_dev->pdev->dev,
"VPU MDP process failed: %d\n", ret);
goto worker_end;
}
task.config = ctx->mdp_dev->vpu.config;
task.param = &param;
task.composes[0] = &frame->compose;
task.cmdq_cb = NULL;
task.cb_data = NULL;
task.mdp_ctx = ctx;
if (refcount_read(&ctx->mdp_dev->job_count)) {
ret = wait_event_timeout(ctx->mdp_dev->callback_wq,
!refcount_read(&ctx->mdp_dev->job_count),
2 * HZ);
if (ret == 0) {
dev_err(&ctx->mdp_dev->pdev->dev,
"%d jobs not yet done\n",
refcount_read(&ctx->mdp_dev->job_count));
goto worker_end;
}
}
ret = mdp_cmdq_send(ctx->mdp_dev, &task);
if (ret) {
dev_err(&ctx->mdp_dev->pdev->dev,
"CMDQ sendtask failed: %d\n", ret);
goto worker_end;
}
return;
worker_end:
mdp_m2m_process_done(ctx, vb_state);
}
static int mdp_m2m_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
struct mdp_frame *capture;
struct vb2_queue *vq;
int ret;
bool out_streaming, cap_streaming;
if (V4L2_TYPE_IS_OUTPUT(q->type))
ctx->frame_count[MDP_M2M_SRC] = 0;
if (V4L2_TYPE_IS_CAPTURE(q->type))
ctx->frame_count[MDP_M2M_DST] = 0;
capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
vq = v4l2_m2m_get_src_vq(ctx->m2m_ctx);
out_streaming = vb2_is_streaming(vq);
vq = v4l2_m2m_get_dst_vq(ctx->m2m_ctx);
cap_streaming = vb2_is_streaming(vq);
/* Check to see if scaling ratio is within supported range */
if ((V4L2_TYPE_IS_OUTPUT(q->type) && cap_streaming) ||
(V4L2_TYPE_IS_CAPTURE(q->type) && out_streaming)) {
ret = mdp_check_scaling_ratio(&capture->crop.c,
&capture->compose,
capture->rotation,
ctx->curr_param.limit);
if (ret) {
dev_err(&ctx->mdp_dev->pdev->dev,
"Out of scaling range\n");
return ret;
}
}
if (!mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT)) {
ret = mdp_vpu_get_locked(ctx->mdp_dev);
if (ret) {
dev_err(&ctx->mdp_dev->pdev->dev,
"VPU init failed %d\n", ret);
return -EINVAL;
}
mdp_m2m_ctx_set_state(ctx, MDP_VPU_INIT);
}
return 0;
}
static struct vb2_v4l2_buffer *mdp_m2m_buf_remove(struct mdp_m2m_ctx *ctx,
unsigned int type)
{
if (V4L2_TYPE_IS_OUTPUT(type))
return (struct vb2_v4l2_buffer *)
v4l2_m2m_src_buf_remove(ctx->m2m_ctx);
else
return (struct vb2_v4l2_buffer *)
v4l2_m2m_dst_buf_remove(ctx->m2m_ctx);
}
static void mdp_m2m_stop_streaming(struct vb2_queue *q)
{
struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
struct vb2_v4l2_buffer *vb;
vb = mdp_m2m_buf_remove(ctx, q->type);
while (vb) {
v4l2_m2m_buf_done(vb, VB2_BUF_STATE_ERROR);
vb = mdp_m2m_buf_remove(ctx, q->type);
}
}
static int mdp_m2m_queue_setup(struct vb2_queue *q,
unsigned int *num_buffers,
unsigned int *num_planes, unsigned int sizes[],
struct device *alloc_devs[])
{
struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(q);
struct v4l2_pix_format_mplane *pix_mp;
u32 i;
pix_mp = &ctx_get_frame(ctx, q->type)->format.fmt.pix_mp;
/* from VIDIOC_CREATE_BUFS */
if (*num_planes) {
if (*num_planes != pix_mp->num_planes)
return -EINVAL;
for (i = 0; i < pix_mp->num_planes; ++i)
if (sizes[i] < pix_mp->plane_fmt[i].sizeimage)
return -EINVAL;
} else {/* from VIDIOC_REQBUFS */
*num_planes = pix_mp->num_planes;
for (i = 0; i < pix_mp->num_planes; ++i)
sizes[i] = pix_mp->plane_fmt[i].sizeimage;
}
return 0;
}
static int mdp_m2m_buf_prepare(struct vb2_buffer *vb)
{
struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct v4l2_pix_format_mplane *pix_mp;
struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
u32 i;
v4l2_buf->field = V4L2_FIELD_NONE;
if (V4L2_TYPE_IS_CAPTURE(vb->type)) {
pix_mp = &ctx_get_frame(ctx, vb->type)->format.fmt.pix_mp;
for (i = 0; i < pix_mp->num_planes; ++i) {
vb2_set_plane_payload(vb, i,
pix_mp->plane_fmt[i].sizeimage);
}
}
return 0;
}
static int mdp_m2m_buf_out_validate(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
v4l2_buf->field = V4L2_FIELD_NONE;
return 0;
}
static void mdp_m2m_buf_queue(struct vb2_buffer *vb)
{
struct mdp_m2m_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
v4l2_buf->field = V4L2_FIELD_NONE;
v4l2_m2m_buf_queue(ctx->m2m_ctx, to_vb2_v4l2_buffer(vb));
}
static const struct vb2_ops mdp_m2m_qops = {
.queue_setup = mdp_m2m_queue_setup,
.buf_prepare = mdp_m2m_buf_prepare,
.start_streaming = mdp_m2m_start_streaming,
.stop_streaming = mdp_m2m_stop_streaming,
.buf_queue = mdp_m2m_buf_queue,
.buf_out_validate = mdp_m2m_buf_out_validate,
};
static int mdp_m2m_querycap(struct file *file, void *fh,
struct v4l2_capability *cap)
{
strscpy(cap->driver, MDP_MODULE_NAME, sizeof(cap->driver));
strscpy(cap->card, MDP_DEVICE_NAME, sizeof(cap->card));
return 0;
}
static int mdp_m2m_enum_fmt_mplane(struct file *file, void *fh,
struct v4l2_fmtdesc *f)
{
struct mdp_m2m_ctx *ctx = file_to_ctx(file);
return mdp_enum_fmt_mplane(ctx->mdp_dev, f);
}
static int mdp_m2m_g_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct mdp_m2m_ctx *ctx = file_to_ctx(file);
struct mdp_frame *frame;
struct v4l2_pix_format_mplane *pix_mp;
frame = ctx_get_frame(ctx, f->type);
*f = frame->format;
pix_mp = &f->fmt.pix_mp;
pix_mp->colorspace = ctx->curr_param.colorspace;
pix_mp->xfer_func = ctx->curr_param.xfer_func;
pix_mp->ycbcr_enc = ctx->curr_param.ycbcr_enc;
pix_mp->quantization = ctx->curr_param.quant;
return 0;
}
static int mdp_m2m_s_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct mdp_m2m_ctx *ctx = file_to_ctx(file);
struct mdp_frame *frame = ctx_get_frame(ctx, f->type);
struct mdp_frame *capture;
const struct mdp_format *fmt;
struct vb2_queue *vq;
fmt = mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id);
if (!fmt)
return -EINVAL;
vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
if (vb2_is_busy(vq))
return -EBUSY;
frame->format = *f;
frame->mdp_fmt = fmt;
frame->ycbcr_prof = mdp_map_ycbcr_prof_mplane(f, fmt->mdp_color);
frame->usage = V4L2_TYPE_IS_OUTPUT(f->type) ?
MDP_BUFFER_USAGE_HW_READ : MDP_BUFFER_USAGE_MDP;
capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (V4L2_TYPE_IS_OUTPUT(f->type)) {
capture->crop.c.left = 0;
capture->crop.c.top = 0;
capture->crop.c.width = f->fmt.pix_mp.width;
capture->crop.c.height = f->fmt.pix_mp.height;
ctx->curr_param.colorspace = f->fmt.pix_mp.colorspace;
ctx->curr_param.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
ctx->curr_param.quant = f->fmt.pix_mp.quantization;
ctx->curr_param.xfer_func = f->fmt.pix_mp.xfer_func;
} else {
capture->compose.left = 0;
capture->compose.top = 0;
capture->compose.width = f->fmt.pix_mp.width;
capture->compose.height = f->fmt.pix_mp.height;
}
return 0;
}
static int mdp_m2m_try_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct mdp_m2m_ctx *ctx = file_to_ctx(file);
if (!mdp_try_fmt_mplane(ctx->mdp_dev, f, &ctx->curr_param, ctx->id))
return -EINVAL;
return 0;
}
static int mdp_m2m_g_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct mdp_m2m_ctx *ctx = file_to_ctx(file);
struct mdp_frame *frame;
bool valid = false;
if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
valid = mdp_target_is_crop(s->target);
else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
valid = mdp_target_is_compose(s->target);
if (!valid)
return -EINVAL;
switch (s->target) {
case V4L2_SEL_TGT_CROP:
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
s->r = frame->crop.c;
return 0;
case V4L2_SEL_TGT_COMPOSE:
if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
frame = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
s->r = frame->compose;
return 0;
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
return -EINVAL;
frame = ctx_get_frame(ctx, s->type);
s->r.left = 0;
s->r.top = 0;
s->r.width = frame->format.fmt.pix_mp.width;
s->r.height = frame->format.fmt.pix_mp.height;
return 0;
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
frame = ctx_get_frame(ctx, s->type);
s->r.left = 0;
s->r.top = 0;
s->r.width = frame->format.fmt.pix_mp.width;
s->r.height = frame->format.fmt.pix_mp.height;
return 0;
}
return -EINVAL;
}
static int mdp_m2m_s_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct mdp_m2m_ctx *ctx = file_to_ctx(file);
struct mdp_frame *frame = ctx_get_frame(ctx, s->type);
struct mdp_frame *capture;
struct v4l2_rect r;
struct device *dev = &ctx->mdp_dev->pdev->dev;
bool valid = false;
int ret;
if (s->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
valid = (s->target == V4L2_SEL_TGT_CROP);
else if (s->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
valid = (s->target == V4L2_SEL_TGT_COMPOSE);
if (!valid) {
dev_dbg(dev, "[%s:%d] invalid type:%u target:%u", __func__,
ctx->id, s->type, s->target);
return -EINVAL;
}
ret = mdp_try_crop(ctx, &r, s, frame);
if (ret)
return ret;
capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (mdp_target_is_crop(s->target))
capture->crop.c = r;
else
capture->compose = r;
s->r = r;
return 0;
}
static const struct v4l2_ioctl_ops mdp_m2m_ioctl_ops = {
.vidioc_querycap = mdp_m2m_querycap,
.vidioc_enum_fmt_vid_cap = mdp_m2m_enum_fmt_mplane,
.vidioc_enum_fmt_vid_out = mdp_m2m_enum_fmt_mplane,
.vidioc_g_fmt_vid_cap_mplane = mdp_m2m_g_fmt_mplane,
.vidioc_g_fmt_vid_out_mplane = mdp_m2m_g_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = mdp_m2m_s_fmt_mplane,
.vidioc_s_fmt_vid_out_mplane = mdp_m2m_s_fmt_mplane,
.vidioc_try_fmt_vid_cap_mplane = mdp_m2m_try_fmt_mplane,
.vidioc_try_fmt_vid_out_mplane = mdp_m2m_try_fmt_mplane,
.vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
.vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
.vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
.vidioc_g_selection = mdp_m2m_g_selection,
.vidioc_s_selection = mdp_m2m_s_selection,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
static int mdp_m2m_queue_init(void *priv,
struct vb2_queue *src_vq,
struct vb2_queue *dst_vq)
{
struct mdp_m2m_ctx *ctx = priv;
int ret;
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->ops = &mdp_m2m_qops;
src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->drv_priv = ctx;
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->dev = &ctx->mdp_dev->pdev->dev;
src_vq->lock = &ctx->ctx_lock;
ret = vb2_queue_init(src_vq);
if (ret)
return ret;
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
dst_vq->ops = &mdp_m2m_qops;
dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->drv_priv = ctx;
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
dst_vq->dev = &ctx->mdp_dev->pdev->dev;
dst_vq->lock = &ctx->ctx_lock;
return vb2_queue_init(dst_vq);
}
static int mdp_m2m_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct mdp_m2m_ctx *ctx = ctrl_to_ctx(ctrl);
struct mdp_frame *capture;
capture = ctx_get_frame(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
switch (ctrl->id) {
case V4L2_CID_HFLIP:
capture->hflip = ctrl->val;
break;
case V4L2_CID_VFLIP:
capture->vflip = ctrl->val;
break;
case V4L2_CID_ROTATE:
capture->rotation = ctrl->val;
break;
}
return 0;
}
static const struct v4l2_ctrl_ops mdp_m2m_ctrl_ops = {
.s_ctrl = mdp_m2m_s_ctrl,
};
static int mdp_m2m_ctrls_create(struct mdp_m2m_ctx *ctx)
{
v4l2_ctrl_handler_init(&ctx->ctrl_handler, MDP_MAX_CTRLS);
ctx->ctrls.hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
&mdp_m2m_ctrl_ops, V4L2_CID_HFLIP,
0, 1, 1, 0);
ctx->ctrls.vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler,
&mdp_m2m_ctrl_ops, V4L2_CID_VFLIP,
0, 1, 1, 0);
ctx->ctrls.rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler,
&mdp_m2m_ctrl_ops,
V4L2_CID_ROTATE, 0, 270, 90, 0);
if (ctx->ctrl_handler.error) {
int err = ctx->ctrl_handler.error;
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
dev_err(&ctx->mdp_dev->pdev->dev,
"Failed to register controls\n");
return err;
}
return 0;
}
static int mdp_m2m_open(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct mdp_dev *mdp = video_get_drvdata(vdev);
struct mdp_m2m_ctx *ctx;
struct device *dev = &mdp->pdev->dev;
int ret;
struct v4l2_format default_format = {};
const struct mdp_limit *limit = mdp->mdp_data->def_limit;
ctx = kzalloc_obj(*ctx);
if (!ctx)
return -ENOMEM;
if (mutex_lock_interruptible(&mdp->m2m_lock)) {
ret = -ERESTARTSYS;
goto err_free_ctx;
}
ret = ida_alloc(&mdp->mdp_ida, GFP_KERNEL);
if (ret < 0)
goto err_unlock_mutex;
ctx->id = ret;
ctx->mdp_dev = mdp;
v4l2_fh_init(&ctx->fh, vdev);
ret = mdp_m2m_ctrls_create(ctx);
if (ret)
goto err_exit_fh;
/* Use separate control handler per file handle */
ctx->fh.ctrl_handler = &ctx->ctrl_handler;
v4l2_fh_add(&ctx->fh, file);
mutex_init(&ctx->ctx_lock);
ctx->m2m_ctx = v4l2_m2m_ctx_init(mdp->m2m_dev, ctx, mdp_m2m_queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
dev_err(dev, "Failed to initialize m2m context\n");
ret = PTR_ERR(ctx->m2m_ctx);
goto err_release_handler;
}
ctx->fh.m2m_ctx = ctx->m2m_ctx;
ctx->curr_param.ctx = ctx;
ret = mdp_frameparam_init(mdp, &ctx->curr_param);
if (ret) {
dev_err(dev, "Failed to initialize mdp parameter\n");
goto err_release_m2m_ctx;
}
mutex_unlock(&mdp->m2m_lock);
/* Default format */
default_format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
default_format.fmt.pix_mp.width = limit->out_limit.wmin;
default_format.fmt.pix_mp.height = limit->out_limit.hmin;
default_format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
default_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
mdp_m2m_s_fmt_mplane(file, &ctx->fh, &default_format);
dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
return 0;
err_release_m2m_ctx:
v4l2_m2m_ctx_release(ctx->m2m_ctx);
err_release_handler:
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
v4l2_fh_del(&ctx->fh, file);
err_exit_fh:
v4l2_fh_exit(&ctx->fh);
ida_free(&mdp->mdp_ida, ctx->id);
err_unlock_mutex:
mutex_unlock(&mdp->m2m_lock);
err_free_ctx:
kfree(ctx);
return ret;
}
static int mdp_m2m_release(struct file *file)
{
struct mdp_m2m_ctx *ctx = file_to_ctx(file);
struct mdp_dev *mdp = video_drvdata(file);
struct device *dev = &mdp->pdev->dev;
mutex_lock(&mdp->m2m_lock);
v4l2_m2m_ctx_release(ctx->m2m_ctx);
if (mdp_m2m_ctx_is_state_set(ctx, MDP_VPU_INIT))
mdp_vpu_put_locked(mdp);
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
v4l2_fh_del(&ctx->fh, file);
v4l2_fh_exit(&ctx->fh);
ida_free(&mdp->mdp_ida, ctx->id);
mutex_unlock(&mdp->m2m_lock);
dev_dbg(dev, "%s:[%d]", __func__, ctx->id);
kfree(ctx);
return 0;
}
static const struct v4l2_file_operations mdp_m2m_fops = {
.owner = THIS_MODULE,
.poll = v4l2_m2m_fop_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = v4l2_m2m_fop_mmap,
.open = mdp_m2m_open,
.release = mdp_m2m_release,
};
static const struct v4l2_m2m_ops mdp_m2m_ops = {
.device_run = mdp_m2m_device_run,
};
int mdp_m2m_device_register(struct mdp_dev *mdp)
{
struct device *dev = &mdp->pdev->dev;
int ret = 0;
mdp->m2m_vdev = video_device_alloc();
if (!mdp->m2m_vdev) {
dev_err(dev, "Failed to allocate video device\n");
ret = -ENOMEM;
goto err_video_alloc;
}
mdp->m2m_vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_STREAMING;
mdp->m2m_vdev->fops = &mdp_m2m_fops;
mdp->m2m_vdev->ioctl_ops = &mdp_m2m_ioctl_ops;
mdp->m2m_vdev->release = mdp_video_device_release;
mdp->m2m_vdev->lock = &mdp->m2m_lock;
mdp->m2m_vdev->vfl_dir = VFL_DIR_M2M;
mdp->m2m_vdev->v4l2_dev = &mdp->v4l2_dev;
snprintf(mdp->m2m_vdev->name, sizeof(mdp->m2m_vdev->name), "%s:m2m",
MDP_MODULE_NAME);
video_set_drvdata(mdp->m2m_vdev, mdp);
mdp->m2m_dev = v4l2_m2m_init(&mdp_m2m_ops);
if (IS_ERR(mdp->m2m_dev)) {
dev_err(dev, "Failed to initialize v4l2-m2m device\n");
ret = PTR_ERR(mdp->m2m_dev);
goto err_m2m_init;
}
ret = video_register_device(mdp->m2m_vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(dev, "Failed to register video device\n");
goto err_video_register;
}
v4l2_info(&mdp->v4l2_dev, "Driver registered as /dev/video%d",
mdp->m2m_vdev->num);
return 0;
err_video_register:
v4l2_m2m_release(mdp->m2m_dev);
err_m2m_init:
video_device_release(mdp->m2m_vdev);
err_video_alloc:
return ret;
}
void mdp_m2m_device_unregister(struct mdp_dev *mdp)
{
video_unregister_device(mdp->m2m_vdev);
}
void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx)
{
enum vb2_buffer_state vb_state = VB2_BUF_STATE_DONE;
mdp_m2m_process_done(ctx, vb_state);
}
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_M2M_H__
#define __MTK_MDP3_M2M_H__
#include <media/v4l2-ctrls.h>
#include "mtk-mdp3-core.h"
#include "mtk-mdp3-vpu.h"
#include "mtk-mdp3-regs.h"
#define MDP_MAX_CTRLS 10
enum {
MDP_M2M_SRC = 0,
MDP_M2M_DST = 1,
MDP_M2M_MAX,
};
struct mdp_m2m_ctrls {
struct v4l2_ctrl *hflip;
struct v4l2_ctrl *vflip;
struct v4l2_ctrl *rotate;
};
struct mdp_m2m_ctx {
u32 id;
struct mdp_dev *mdp_dev;
struct v4l2_fh fh;
struct v4l2_ctrl_handler ctrl_handler;
struct mdp_m2m_ctrls ctrls;
struct v4l2_m2m_ctx *m2m_ctx;
u32 frame_count[MDP_M2M_MAX];
struct mdp_frameparam curr_param;
/* synchronization protect for mdp m2m context */
struct mutex ctx_lock;
};
int mdp_m2m_device_register(struct mdp_dev *mdp);
void mdp_m2m_device_unregister(struct mdp_dev *mdp);
void mdp_m2m_job_finish(struct mdp_m2m_ctx *ctx);
#endif /* __MTK_MDP3_M2M_H__ */
@@ -0,0 +1,514 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#include <linux/math64.h>
#include <media/v4l2-common.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#include "mtk-mdp3-core.h"
#include "mtk-mdp3-regs.h"
#include "mtk-mdp3-m2m.h"
static const struct mdp_format *mdp_find_fmt(const struct mtk_mdp_driver_data *mdp_data,
u32 pixelformat, u32 type)
{
u32 i, flag;
flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT :
MDP_FMT_FLAG_CAPTURE;
for (i = 0; i < mdp_data->format_len; ++i) {
if (!(mdp_data->format[i].flags & flag))
continue;
if (mdp_data->format[i].pixelformat == pixelformat)
return &mdp_data->format[i];
}
return NULL;
}
static const struct mdp_format *mdp_find_fmt_by_index(const struct mtk_mdp_driver_data *mdp_data,
u32 index, u32 type)
{
u32 i, flag, num = 0;
flag = V4L2_TYPE_IS_OUTPUT(type) ? MDP_FMT_FLAG_OUTPUT :
MDP_FMT_FLAG_CAPTURE;
for (i = 0; i < mdp_data->format_len; ++i) {
if (!(mdp_data->format[i].flags & flag))
continue;
if (index == num)
return &mdp_data->format[i];
num++;
}
return NULL;
}
enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
u32 mdp_color)
{
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
if (MDP_COLOR_IS_RGB(mdp_color))
return MDP_YCBCR_PROFILE_FULL_BT601;
switch (pix_mp->colorspace) {
case V4L2_COLORSPACE_JPEG:
return MDP_YCBCR_PROFILE_JPEG;
case V4L2_COLORSPACE_REC709:
case V4L2_COLORSPACE_DCI_P3:
if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
return MDP_YCBCR_PROFILE_FULL_BT709;
return MDP_YCBCR_PROFILE_BT709;
case V4L2_COLORSPACE_BT2020:
if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
return MDP_YCBCR_PROFILE_FULL_BT2020;
return MDP_YCBCR_PROFILE_BT2020;
default:
if (pix_mp->quantization == V4L2_QUANTIZATION_FULL_RANGE)
return MDP_YCBCR_PROFILE_FULL_BT601;
return MDP_YCBCR_PROFILE_BT601;
}
}
static void mdp_bound_align_image(u32 *w, u32 *h,
struct v4l2_frmsize_stepwise *s,
unsigned int salign)
{
unsigned int org_w, org_h;
org_w = *w;
org_h = *h;
v4l_bound_align_image(w, s->min_width, s->max_width, s->step_width,
h, s->min_height, s->max_height, s->step_height,
salign);
s->min_width = org_w;
s->min_height = org_h;
v4l2_apply_frmsize_constraints(w, h, s);
}
static int mdp_clamp_align(s32 *x, int min, int max, unsigned int align)
{
unsigned int mask;
if (min < 0 || max < 0)
return -ERANGE;
/* Bits that must be zero to be aligned */
mask = ~((1 << align) - 1);
min = 0 ? 0 : ((min + ~mask) & mask);
max = max & mask;
if ((unsigned int)min > (unsigned int)max)
return -ERANGE;
/* Clamp to aligned min and max */
*x = clamp(*x, min, max);
/* Round to nearest aligned value */
if (align)
*x = (*x + (1 << (align - 1))) & mask;
return 0;
}
int mdp_enum_fmt_mplane(struct mdp_dev *mdp, struct v4l2_fmtdesc *f)
{
const struct mdp_format *fmt;
fmt = mdp_find_fmt_by_index(mdp->mdp_data, f->index, f->type);
if (!fmt)
return -EINVAL;
f->pixelformat = fmt->pixelformat;
return 0;
}
const struct mdp_format *mdp_try_fmt_mplane(struct mdp_dev *mdp,
struct v4l2_format *f,
struct mdp_frameparam *param,
u32 ctx_id)
{
struct device *dev = &param->ctx->mdp_dev->pdev->dev;
struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
const struct mdp_format *fmt;
const struct mdp_pix_limit *pix_limit;
struct v4l2_frmsize_stepwise s;
u32 org_w, org_h;
unsigned int i;
fmt = mdp_find_fmt(mdp->mdp_data, pix_mp->pixelformat, f->type);
if (!fmt) {
fmt = mdp_find_fmt_by_index(mdp->mdp_data, 0, f->type);
if (!fmt) {
dev_dbg(dev, "%d: pixelformat %c%c%c%c invalid", ctx_id,
(pix_mp->pixelformat & 0xff),
(pix_mp->pixelformat >> 8) & 0xff,
(pix_mp->pixelformat >> 16) & 0xff,
(pix_mp->pixelformat >> 24) & 0xff);
return NULL;
}
}
pix_mp->field = V4L2_FIELD_NONE;
pix_mp->flags = 0;
pix_mp->pixelformat = fmt->pixelformat;
if (V4L2_TYPE_IS_CAPTURE(f->type)) {
pix_mp->colorspace = param->colorspace;
pix_mp->xfer_func = param->xfer_func;
pix_mp->ycbcr_enc = param->ycbcr_enc;
pix_mp->quantization = param->quant;
}
pix_limit = V4L2_TYPE_IS_OUTPUT(f->type) ? &param->limit->out_limit :
&param->limit->cap_limit;
s.min_width = pix_limit->wmin;
s.max_width = pix_limit->wmax;
s.step_width = fmt->walign;
s.min_height = pix_limit->hmin;
s.max_height = pix_limit->hmax;
s.step_height = fmt->halign;
org_w = pix_mp->width;
org_h = pix_mp->height;
mdp_bound_align_image(&pix_mp->width, &pix_mp->height, &s, fmt->salign);
if (org_w != pix_mp->width || org_h != pix_mp->height)
dev_dbg(dev, "%d: size change: %ux%u to %ux%u", ctx_id,
org_w, org_h, pix_mp->width, pix_mp->height);
if (pix_mp->num_planes && pix_mp->num_planes != fmt->num_planes)
dev_dbg(dev, "%d num of planes change: %u to %u", ctx_id,
pix_mp->num_planes, fmt->num_planes);
pix_mp->num_planes = fmt->num_planes;
for (i = 0; i < pix_mp->num_planes; ++i) {
u32 min_bpl = (pix_mp->width * fmt->row_depth[i]) >> 3;
u32 max_bpl = (pix_limit->wmax * fmt->row_depth[i]) >> 3;
u32 bpl = pix_mp->plane_fmt[i].bytesperline;
u32 min_si, max_si;
u32 si = pix_mp->plane_fmt[i].sizeimage;
u64 di;
bpl = clamp(bpl, min_bpl, max_bpl);
pix_mp->plane_fmt[i].bytesperline = bpl;
di = (u64)bpl * pix_mp->height * fmt->depth[i];
min_si = (u32)div_u64(di, fmt->row_depth[i]);
di = (u64)bpl * s.max_height * fmt->depth[i];
max_si = (u32)div_u64(di, fmt->row_depth[i]);
si = clamp(si, min_si, max_si);
pix_mp->plane_fmt[i].sizeimage = si;
dev_dbg(dev, "%d: p%u, bpl:%u [%u, %u], sizeimage:%u [%u, %u]",
ctx_id, i, bpl, min_bpl, max_bpl, si, min_si, max_si);
}
return fmt;
}
static int mdp_clamp_start(s32 *x, int min, int max, unsigned int align,
u32 flags)
{
if (flags & V4L2_SEL_FLAG_GE)
max = *x;
if (flags & V4L2_SEL_FLAG_LE)
min = *x;
return mdp_clamp_align(x, min, max, align);
}
static int mdp_clamp_end(s32 *x, int min, int max, unsigned int align,
u32 flags)
{
if (flags & V4L2_SEL_FLAG_GE)
min = *x;
if (flags & V4L2_SEL_FLAG_LE)
max = *x;
return mdp_clamp_align(x, min, max, align);
}
int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r,
const struct v4l2_selection *s, struct mdp_frame *frame)
{
struct device *dev = &ctx->mdp_dev->pdev->dev;
s32 left, top, right, bottom;
u32 framew, frameh, walign, halign;
int ret;
dev_dbg(dev, "%d target:%d, set:(%d,%d)/%ux%u", ctx->id,
s->target, s->r.left, s->r.top, s->r.width, s->r.height);
left = s->r.left;
top = s->r.top;
right = s->r.left + s->r.width;
bottom = s->r.top + s->r.height;
framew = frame->format.fmt.pix_mp.width;
frameh = frame->format.fmt.pix_mp.height;
if (mdp_target_is_crop(s->target)) {
walign = 1;
halign = 1;
} else {
walign = frame->mdp_fmt->walign;
halign = frame->mdp_fmt->halign;
}
dev_dbg(dev, "%d align:%u,%u, bound:%ux%u", ctx->id,
walign, halign, framew, frameh);
ret = mdp_clamp_start(&left, 0, right, walign, s->flags);
if (ret)
return ret;
ret = mdp_clamp_start(&top, 0, bottom, halign, s->flags);
if (ret)
return ret;
ret = mdp_clamp_end(&right, left, framew, walign, s->flags);
if (ret)
return ret;
ret = mdp_clamp_end(&bottom, top, frameh, halign, s->flags);
if (ret)
return ret;
r->left = left;
r->top = top;
r->width = right - left;
r->height = bottom - top;
dev_dbg(dev, "%d crop:(%d,%d)/%ux%u", ctx->id,
r->left, r->top, r->width, r->height);
return 0;
}
int mdp_check_scaling_ratio(const struct v4l2_rect *crop,
const struct v4l2_rect *compose, s32 rotation,
const struct mdp_limit *limit)
{
u32 crop_w, crop_h, comp_w, comp_h;
crop_w = crop->width;
crop_h = crop->height;
if (90 == rotation || 270 == rotation) {
comp_w = compose->height;
comp_h = compose->width;
} else {
comp_w = compose->width;
comp_h = compose->height;
}
if ((crop_w / comp_w) > limit->h_scale_down_max ||
(crop_h / comp_h) > limit->v_scale_down_max ||
(comp_w / crop_w) > limit->h_scale_up_max ||
(comp_h / crop_h) > limit->v_scale_up_max)
return -ERANGE;
return 0;
}
bool mdp_check_pp_enable(struct mdp_dev *mdp, struct mdp_frame *frame)
{
u32 s, r1, r2;
if (!mdp || !frame)
return false;
if (!mdp->mdp_data->pp_criteria)
return false;
s = mdp->mdp_data->pp_criteria->width *
mdp->mdp_data->pp_criteria->height;
r1 = frame->crop.c.width * frame->crop.c.height;
r2 = frame->compose.width * frame->compose.height;
return (r1 >= s || r2 >= s);
}
/* Stride that is accepted by MDP HW */
static u32 mdp_fmt_get_stride(const struct mdp_format *fmt,
u32 bytesperline, unsigned int plane)
{
enum mdp_color c = fmt->mdp_color;
u32 stride;
stride = (bytesperline * MDP_COLOR_BITS_PER_PIXEL(c))
/ fmt->row_depth[0];
if (plane == 0)
return stride;
if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
if (MDP_COLOR_IS_BLOCK_MODE(c))
stride = stride / 2;
return stride;
}
return 0;
}
/* Stride that is accepted by MDP HW of format with contiguous planes */
static u32 mdp_fmt_get_stride_contig(const struct mdp_format *fmt,
u32 pix_stride, unsigned int plane)
{
enum mdp_color c = fmt->mdp_color;
u32 stride = pix_stride;
if (plane == 0)
return stride;
if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
stride = stride >> MDP_COLOR_GET_H_SUBSAMPLE(c);
if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c))
stride = stride * 2;
return stride;
}
return 0;
}
/* Plane size that is accepted by MDP HW */
static u32 mdp_fmt_get_plane_size(const struct mdp_format *fmt,
u32 stride, u32 height, unsigned int plane)
{
enum mdp_color c = fmt->mdp_color;
u32 bytesperline;
bytesperline = (stride * fmt->row_depth[0])
/ MDP_COLOR_BITS_PER_PIXEL(c);
if (plane == 0)
return bytesperline * height;
if (plane < MDP_COLOR_GET_PLANE_COUNT(c)) {
height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c);
if (MDP_COLOR_IS_BLOCK_MODE(c))
bytesperline = bytesperline * 2;
return bytesperline * height;
}
return 0;
}
static void mdp_prepare_buffer(struct img_image_buffer *b,
struct mdp_frame *frame, struct vb2_buffer *vb)
{
struct v4l2_pix_format_mplane *pix_mp = &frame->format.fmt.pix_mp;
unsigned int i;
b->format.colorformat = frame->mdp_fmt->mdp_color;
b->format.ycbcr_prof = frame->ycbcr_prof;
for (i = 0; i < pix_mp->num_planes; ++i) {
u32 stride = mdp_fmt_get_stride(frame->mdp_fmt,
pix_mp->plane_fmt[i].bytesperline, i);
b->format.plane_fmt[i].stride = stride;
b->format.plane_fmt[i].size =
mdp_fmt_get_plane_size(frame->mdp_fmt, stride,
pix_mp->height, i);
b->iova[i] = vb2_dma_contig_plane_dma_addr(vb, i);
}
for (; i < MDP_COLOR_GET_PLANE_COUNT(b->format.colorformat); ++i) {
u32 stride = mdp_fmt_get_stride_contig(frame->mdp_fmt,
b->format.plane_fmt[0].stride, i);
b->format.plane_fmt[i].stride = stride;
b->format.plane_fmt[i].size =
mdp_fmt_get_plane_size(frame->mdp_fmt, stride,
pix_mp->height, i);
b->iova[i] = b->iova[i - 1] + b->format.plane_fmt[i - 1].size;
}
b->usage = frame->usage;
}
void mdp_set_src_config(struct img_input *in,
struct mdp_frame *frame, struct vb2_buffer *vb)
{
in->buffer.format.width = frame->format.fmt.pix_mp.width;
in->buffer.format.height = frame->format.fmt.pix_mp.height;
mdp_prepare_buffer(&in->buffer, frame, vb);
}
static u32 mdp_to_fixed(u32 *r, struct v4l2_fract *f)
{
u32 q;
if (f->denominator == 0) {
*r = 0;
return 0;
}
q = f->numerator / f->denominator;
*r = div_u64(((u64)f->numerator - q * f->denominator) <<
IMG_SUBPIXEL_SHIFT, f->denominator);
return q;
}
static void mdp_set_src_crop(struct img_crop *c, struct mdp_crop *crop)
{
c->left = crop->c.left
+ mdp_to_fixed(&c->left_subpix, &crop->left_subpix);
c->top = crop->c.top
+ mdp_to_fixed(&c->top_subpix, &crop->top_subpix);
c->width = crop->c.width
+ mdp_to_fixed(&c->width_subpix, &crop->width_subpix);
c->height = crop->c.height
+ mdp_to_fixed(&c->height_subpix, &crop->height_subpix);
}
static void mdp_set_orientation(struct img_output *out,
s32 rotation, bool hflip, bool vflip)
{
u8 flip = 0;
if (hflip)
flip ^= 1;
if (vflip) {
/*
* A vertical flip is equivalent to
* a 180-degree rotation with a horizontal flip
*/
rotation += 180;
flip ^= 1;
}
out->rotation = rotation % 360;
if (flip != 0)
out->flags |= IMG_CTRL_FLAG_HFLIP;
else
out->flags &= ~IMG_CTRL_FLAG_HFLIP;
}
void mdp_set_dst_config(struct img_output *out,
struct mdp_frame *frame, struct vb2_buffer *vb)
{
out->buffer.format.width = frame->compose.width;
out->buffer.format.height = frame->compose.height;
mdp_prepare_buffer(&out->buffer, frame, vb);
mdp_set_src_crop(&out->crop, &frame->crop);
mdp_set_orientation(out, frame->rotation, frame->hflip, frame->vflip);
}
int mdp_frameparam_init(struct mdp_dev *mdp, struct mdp_frameparam *param)
{
struct mdp_frame *frame;
if (!param)
return -EINVAL;
INIT_LIST_HEAD(&param->list);
param->limit = mdp->mdp_data->def_limit;
param->type = MDP_STREAM_TYPE_BITBLT;
frame = &param->output;
frame->format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
frame->mdp_fmt = mdp_try_fmt_mplane(mdp, &frame->format, param, 0);
frame->ycbcr_prof =
mdp_map_ycbcr_prof_mplane(&frame->format,
frame->mdp_fmt->mdp_color);
frame->usage = MDP_BUFFER_USAGE_HW_READ;
param->num_captures = 1;
frame = &param->captures[0];
frame->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
frame->mdp_fmt = mdp_try_fmt_mplane(mdp, &frame->format, param, 0);
frame->ycbcr_prof =
mdp_map_ycbcr_prof_mplane(&frame->format,
frame->mdp_fmt->mdp_color);
frame->usage = MDP_BUFFER_USAGE_MDP;
frame->crop.c.width = param->output.format.fmt.pix_mp.width;
frame->crop.c.height = param->output.format.fmt.pix_mp.height;
frame->compose.width = frame->format.fmt.pix_mp.width;
frame->compose.height = frame->format.fmt.pix_mp.height;
return 0;
}
@@ -0,0 +1,378 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_REGS_H__
#define __MTK_MDP3_REGS_H__
#include <linux/videodev2.h>
#include <media/videobuf2-core.h>
#include "mtk-img-ipi.h"
/*
* MDP native color code
* Plane count: 1, 2, 3
* H-subsample: 0, 1, 2
* V-subsample: 0, 1
* Color group: 0-RGB, 1-YUV, 2-raw
*/
#define MDP_COLOR(COMPRESS, PACKED, LOOSE, VIDEO, PLANE, HF, VF, BITS, GROUP, SWAP, ID)\
(((COMPRESS) << 29) | ((PACKED) << 27) | ((LOOSE) << 26) | ((VIDEO) << 23) |\
((PLANE) << 21) | ((HF) << 19) | ((VF) << 18) | ((BITS) << 8) |\
((GROUP) << 6) | ((SWAP) << 5) | ((ID) << 0))
#define MDP_COLOR_IS_COMPRESS(c) ((0x20000000 & (c)) >> 29)
#define MDP_COLOR_IS_10BIT_PACKED(c) ((0x08000000 & (c)) >> 27)
#define MDP_COLOR_IS_10BIT_LOOSE(c) (((0x0c000000 & (c)) >> 26) == 1)
#define MDP_COLOR_IS_10BIT_TILE(c) (((0x0c000000 & (c)) >> 26) == 3)
#define MDP_COLOR_IS_UFP(c) ((0x02000000 & (c)) >> 25)
#define MDP_COLOR_IS_INTERLACED(c) ((0x01000000 & (c)) >> 24)
#define MDP_COLOR_IS_BLOCK_MODE(c) ((0x00800000 & (c)) >> 23)
#define MDP_COLOR_GET_PLANE_COUNT(c) ((0x00600000 & (c)) >> 21)
#define MDP_COLOR_GET_H_SUBSAMPLE(c) ((0x00180000 & (c)) >> 19)
#define MDP_COLOR_GET_V_SUBSAMPLE(c) ((0x00040000 & (c)) >> 18)
#define MDP_COLOR_BITS_PER_PIXEL(c) ((0x0003ff00 & (c)) >> 8)
#define MDP_COLOR_GET_GROUP(c) ((0x000000c0 & (c)) >> 6)
#define MDP_COLOR_IS_SWAPPED(c) ((0x00000020 & (c)) >> 5)
#define MDP_COLOR_GET_UNIQUE_ID(c) ((0x0000001f & (c)) >> 0)
#define MDP_COLOR_GET_HW_FORMAT(c) ((0x0000001f & (c)) >> 0)
#define MDP_COLOR_IS_RGB(c) (MDP_COLOR_GET_GROUP(c) == 0)
#define MDP_COLOR_IS_YUV(c) (MDP_COLOR_GET_GROUP(c) == 1)
enum mdp_color {
MDP_COLOR_UNKNOWN = 0,
/* MDP_COLOR_FULLG8 */
MDP_COLOR_FULLG8_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 21),
MDP_COLOR_FULLG8_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 8, 2, 0, 21),
MDP_COLOR_FULLG8_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 8, 2, 0, 21),
MDP_COLOR_FULLG8_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 8, 2, 0, 21),
MDP_COLOR_FULLG8 = MDP_COLOR_FULLG8_BGGR,
/* MDP_COLOR_FULLG10 */
MDP_COLOR_FULLG10_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 21),
MDP_COLOR_FULLG10_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 10, 2, 0, 21),
MDP_COLOR_FULLG10_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 10, 2, 0, 21),
MDP_COLOR_FULLG10_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 10, 2, 0, 21),
MDP_COLOR_FULLG10 = MDP_COLOR_FULLG10_BGGR,
/* MDP_COLOR_FULLG12 */
MDP_COLOR_FULLG12_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 12, 2, 0, 21),
MDP_COLOR_FULLG12_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 12, 2, 0, 21),
MDP_COLOR_FULLG12_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 12, 2, 0, 21),
MDP_COLOR_FULLG12_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 12, 2, 0, 21),
MDP_COLOR_FULLG12 = MDP_COLOR_FULLG12_BGGR,
/* MDP_COLOR_FULLG14 */
MDP_COLOR_FULLG14_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 14, 2, 0, 21),
MDP_COLOR_FULLG14_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 14, 2, 0, 21),
MDP_COLOR_FULLG14_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 14, 2, 0, 21),
MDP_COLOR_FULLG14_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 14, 2, 0, 21),
MDP_COLOR_FULLG14 = MDP_COLOR_FULLG14_BGGR,
MDP_COLOR_UFO10 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 24),
/* MDP_COLOR_BAYER8 */
MDP_COLOR_BAYER8_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 20),
MDP_COLOR_BAYER8_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 8, 2, 0, 20),
MDP_COLOR_BAYER8_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 8, 2, 0, 20),
MDP_COLOR_BAYER8_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 8, 2, 0, 20),
MDP_COLOR_BAYER8 = MDP_COLOR_BAYER8_BGGR,
/* MDP_COLOR_BAYER10 */
MDP_COLOR_BAYER10_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 20),
MDP_COLOR_BAYER10_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 10, 2, 0, 20),
MDP_COLOR_BAYER10_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 10, 2, 0, 20),
MDP_COLOR_BAYER10_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 10, 2, 0, 20),
MDP_COLOR_BAYER10 = MDP_COLOR_BAYER10_BGGR,
/* MDP_COLOR_BAYER12 */
MDP_COLOR_BAYER12_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 12, 2, 0, 20),
MDP_COLOR_BAYER12_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 12, 2, 0, 20),
MDP_COLOR_BAYER12_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 12, 2, 0, 20),
MDP_COLOR_BAYER12_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 12, 2, 0, 20),
MDP_COLOR_BAYER12 = MDP_COLOR_BAYER12_BGGR,
/* MDP_COLOR_BAYER14 */
MDP_COLOR_BAYER14_RGGB = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 14, 2, 0, 20),
MDP_COLOR_BAYER14_GRBG = MDP_COLOR(0, 0, 0, 0, 1, 0, 1, 14, 2, 0, 20),
MDP_COLOR_BAYER14_GBRG = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 14, 2, 0, 20),
MDP_COLOR_BAYER14_BGGR = MDP_COLOR(0, 0, 0, 0, 1, 1, 1, 14, 2, 0, 20),
MDP_COLOR_BAYER14 = MDP_COLOR_BAYER14_BGGR,
MDP_COLOR_RGB48 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 48, 0, 0, 23),
/* For bayer+mono raw-16 */
MDP_COLOR_RGB565_RAW = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 16, 2, 0, 0),
MDP_COLOR_BAYER8_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 2, 0, 22),
MDP_COLOR_BAYER10_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 10, 2, 0, 22),
MDP_COLOR_BAYER12_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 12, 2, 0, 22),
MDP_COLOR_BAYER14_UNPAK = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 14, 2, 0, 22),
/* Unified formats */
MDP_COLOR_GREY = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 8, 1, 0, 7),
MDP_COLOR_RGB565 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 16, 0, 0, 0),
MDP_COLOR_BGR565 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 16, 0, 1, 0),
MDP_COLOR_RGB888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 0, 1, 1),
MDP_COLOR_BGR888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 0, 0, 1),
MDP_COLOR_RGBA8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 1, 2),
MDP_COLOR_BGRA8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 0, 2),
MDP_COLOR_ARGB8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 1, 3),
MDP_COLOR_ABGR8888 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 32, 0, 0, 3),
MDP_COLOR_UYVY = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 0, 4),
MDP_COLOR_VYUY = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 1, 4),
MDP_COLOR_YUYV = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 0, 5),
MDP_COLOR_YVYU = MDP_COLOR(0, 0, 0, 0, 1, 1, 0, 16, 1, 1, 5),
MDP_COLOR_I420 = MDP_COLOR(0, 0, 0, 0, 3, 1, 1, 8, 1, 0, 8),
MDP_COLOR_YV12 = MDP_COLOR(0, 0, 0, 0, 3, 1, 1, 8, 1, 1, 8),
MDP_COLOR_I422 = MDP_COLOR(0, 0, 0, 0, 3, 1, 0, 8, 1, 0, 9),
MDP_COLOR_YV16 = MDP_COLOR(0, 0, 0, 0, 3, 1, 0, 8, 1, 1, 9),
MDP_COLOR_I444 = MDP_COLOR(0, 0, 0, 0, 3, 0, 0, 8, 1, 0, 10),
MDP_COLOR_YV24 = MDP_COLOR(0, 0, 0, 0, 3, 0, 0, 8, 1, 1, 10),
MDP_COLOR_NV12 = MDP_COLOR(0, 0, 0, 0, 2, 1, 1, 8, 1, 0, 12),
MDP_COLOR_NV21 = MDP_COLOR(0, 0, 0, 0, 2, 1, 1, 8, 1, 1, 12),
MDP_COLOR_NV16 = MDP_COLOR(0, 0, 0, 0, 2, 1, 0, 8, 1, 0, 13),
MDP_COLOR_NV61 = MDP_COLOR(0, 0, 0, 0, 2, 1, 0, 8, 1, 1, 13),
MDP_COLOR_NV24 = MDP_COLOR(0, 0, 0, 0, 2, 0, 0, 8, 1, 0, 14),
MDP_COLOR_NV42 = MDP_COLOR(0, 0, 0, 0, 2, 0, 0, 8, 1, 1, 14),
/* MediaTek proprietary formats */
/* UFO encoded block mode */
MDP_COLOR_420_BLK_UFO = MDP_COLOR(0, 0, 0, 5, 2, 1, 1, 256, 1, 0, 12),
/* Block mode */
MDP_COLOR_420_BLK = MDP_COLOR(0, 0, 0, 1, 2, 1, 1, 256, 1, 0, 12),
/* Block mode + field mode */
MDP_COLOR_420_BLKI = MDP_COLOR(0, 0, 0, 3, 2, 1, 1, 256, 1, 0, 12),
/* Block mode */
MDP_COLOR_422_BLK = MDP_COLOR(0, 0, 0, 1, 1, 1, 0, 512, 1, 0, 4),
MDP_COLOR_IYU2 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 1, 0, 25),
MDP_COLOR_YUV444 = MDP_COLOR(0, 0, 0, 0, 1, 0, 0, 24, 1, 0, 30),
/* Packed 10-bit formats */
MDP_COLOR_RGBA1010102 = MDP_COLOR(0, 1, 0, 0, 1, 0, 0, 32, 0, 1, 2),
MDP_COLOR_BGRA1010102 = MDP_COLOR(0, 1, 0, 0, 1, 0, 0, 32, 0, 0, 2),
/* Packed 10-bit UYVY */
MDP_COLOR_UYVY_10P = MDP_COLOR(0, 1, 0, 0, 1, 1, 0, 20, 1, 0, 4),
/* Packed 10-bit NV21 */
MDP_COLOR_NV21_10P = MDP_COLOR(0, 1, 0, 0, 2, 1, 1, 10, 1, 1, 12),
/* 10-bit block mode */
MDP_COLOR_420_BLK_10_H = MDP_COLOR(0, 1, 0, 1, 2, 1, 1, 320, 1, 0, 12),
/* 10-bit HEVC tile mode */
MDP_COLOR_420_BLK_10_V = MDP_COLOR(0, 1, 1, 1, 2, 1, 1, 320, 1, 0, 12),
/* UFO encoded 10-bit block mode */
MDP_COLOR_420_BLK_U10_H = MDP_COLOR(0, 1, 0, 5, 2, 1, 1, 320, 1, 0, 12),
/* UFO encoded 10-bit HEVC tile mode */
MDP_COLOR_420_BLK_U10_V = MDP_COLOR(0, 1, 1, 5, 2, 1, 1, 320, 1, 0, 12),
/* Loose 10-bit formats */
MDP_COLOR_UYVY_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 0, 4),
MDP_COLOR_VYUY_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 1, 4),
MDP_COLOR_YUYV_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 0, 5),
MDP_COLOR_YVYU_10L = MDP_COLOR(0, 0, 1, 0, 1, 1, 0, 20, 1, 1, 5),
MDP_COLOR_NV12_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 1, 10, 1, 0, 12),
MDP_COLOR_NV21_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 1, 10, 1, 1, 12),
MDP_COLOR_NV16_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 0, 10, 1, 0, 13),
MDP_COLOR_NV61_10L = MDP_COLOR(0, 0, 1, 0, 2, 1, 0, 10, 1, 1, 13),
MDP_COLOR_YV12_10L = MDP_COLOR(0, 0, 1, 0, 3, 1, 1, 10, 1, 1, 8),
MDP_COLOR_I420_10L = MDP_COLOR(0, 0, 1, 0, 3, 1, 1, 10, 1, 0, 8),
};
static inline bool MDP_COLOR_IS_UV_COPLANE(enum mdp_color c)
{
return (MDP_COLOR_GET_PLANE_COUNT(c) == 2 && MDP_COLOR_IS_YUV(c));
}
/* Minimum Y stride that is accepted by MDP HW */
static inline u32 mdp_color_get_min_y_stride(enum mdp_color c, u32 width)
{
return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) + 4) >> 3;
}
/* Minimum UV stride that is accepted by MDP HW */
static inline u32 mdp_color_get_min_uv_stride(enum mdp_color c, u32 width)
{
u32 min_stride;
if (MDP_COLOR_GET_PLANE_COUNT(c) == 1)
return 0;
min_stride = mdp_color_get_min_y_stride(c, width)
>> MDP_COLOR_GET_H_SUBSAMPLE(c);
if (MDP_COLOR_IS_UV_COPLANE(c) && !MDP_COLOR_IS_BLOCK_MODE(c))
min_stride = min_stride * 2;
return min_stride;
}
/* Minimum Y plane size that is necessary in buffer */
static inline u32 mdp_color_get_min_y_size(enum mdp_color c,
u32 width, u32 height)
{
if (MDP_COLOR_IS_BLOCK_MODE(c))
return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height;
return mdp_color_get_min_y_stride(c, width) * height;
}
/* Minimum UV plane size that is necessary in buffer */
static inline u32 mdp_color_get_min_uv_size(enum mdp_color c,
u32 width, u32 height)
{
height = height >> MDP_COLOR_GET_V_SUBSAMPLE(c);
if (MDP_COLOR_IS_BLOCK_MODE(c) && (MDP_COLOR_GET_PLANE_COUNT(c) > 1))
return ((MDP_COLOR_BITS_PER_PIXEL(c) * width) >> 8) * height;
return mdp_color_get_min_uv_stride(c, width) * height;
}
/* Combine colorspace, xfer_func, ycbcr_encoding, and quantization */
enum mdp_ycbcr_profile {
/* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_LIM_RANGE */
MDP_YCBCR_PROFILE_BT601,
/* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_LIM_RANGE */
MDP_YCBCR_PROFILE_BT709,
/* V4L2_YCBCR_ENC_601 and V4L2_QUANTIZATION_FULL_RANGE */
MDP_YCBCR_PROFILE_JPEG,
MDP_YCBCR_PROFILE_FULL_BT601 = MDP_YCBCR_PROFILE_JPEG,
/* Colorspaces not support for capture */
/* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_LIM_RANGE */
MDP_YCBCR_PROFILE_BT2020,
/* V4L2_YCBCR_ENC_709 and V4L2_QUANTIZATION_FULL_RANGE */
MDP_YCBCR_PROFILE_FULL_BT709,
/* V4L2_YCBCR_ENC_BT2020 and V4L2_QUANTIZATION_FULL_RANGE */
MDP_YCBCR_PROFILE_FULL_BT2020,
};
#define MDP_FMT_FLAG_OUTPUT BIT(0)
#define MDP_FMT_FLAG_CAPTURE BIT(1)
struct mdp_format {
u32 pixelformat;
u32 mdp_color;
u8 depth[VIDEO_MAX_PLANES];
u8 row_depth[VIDEO_MAX_PLANES];
u8 num_planes;
u8 walign;
u8 halign;
u8 salign;
u32 flags;
};
struct mdp_pix_limit {
u32 wmin;
u32 hmin;
u32 wmax;
u32 hmax;
};
struct mdp_limit {
struct mdp_pix_limit out_limit;
struct mdp_pix_limit cap_limit;
u32 h_scale_up_max;
u32 v_scale_up_max;
u32 h_scale_down_max;
u32 v_scale_down_max;
};
enum mdp_stream_type {
MDP_STREAM_TYPE_UNKNOWN,
MDP_STREAM_TYPE_BITBLT,
MDP_STREAM_TYPE_GPU_BITBLT,
MDP_STREAM_TYPE_DUAL_BITBLT,
MDP_STREAM_TYPE_2ND_BITBLT,
MDP_STREAM_TYPE_ISP_IC,
MDP_STREAM_TYPE_ISP_VR,
MDP_STREAM_TYPE_ISP_ZSD,
MDP_STREAM_TYPE_ISP_IP,
MDP_STREAM_TYPE_ISP_VSS,
MDP_STREAM_TYPE_ISP_ZSD_SLOW,
MDP_STREAM_TYPE_WPE,
MDP_STREAM_TYPE_WPE2,
};
struct mdp_crop {
struct v4l2_rect c;
struct v4l2_fract left_subpix;
struct v4l2_fract top_subpix;
struct v4l2_fract width_subpix;
struct v4l2_fract height_subpix;
};
struct mdp_frame {
struct v4l2_format format;
const struct mdp_format *mdp_fmt;
u32 ycbcr_prof; /* enum mdp_ycbcr_profile */
u32 usage; /* enum mdp_buffer_usage */
struct mdp_crop crop;
struct v4l2_rect compose;
s32 rotation;
u32 hflip:1;
u32 vflip:1;
u32 hdr:1;
u32 dre:1;
u32 sharpness:1;
u32 dither:1;
};
static inline bool mdp_target_is_crop(u32 target)
{
return (target == V4L2_SEL_TGT_CROP) ||
(target == V4L2_SEL_TGT_CROP_DEFAULT) ||
(target == V4L2_SEL_TGT_CROP_BOUNDS);
}
static inline bool mdp_target_is_compose(u32 target)
{
return (target == V4L2_SEL_TGT_COMPOSE) ||
(target == V4L2_SEL_TGT_COMPOSE_DEFAULT) ||
(target == V4L2_SEL_TGT_COMPOSE_BOUNDS);
}
#define MDP_MAX_CAPTURES IMG_MAX_HW_OUTPUTS
#define MDP_VPU_INIT BIT(0)
#define MDP_M2M_CTX_ERROR BIT(1)
struct mdp_frameparam {
struct list_head list;
struct mdp_m2m_ctx *ctx;
atomic_t state;
const struct mdp_limit *limit;
u32 type; /* enum mdp_stream_type */
u32 frame_no;
struct mdp_frame output;
struct mdp_frame captures[MDP_MAX_CAPTURES];
u32 num_captures;
enum v4l2_colorspace colorspace;
enum v4l2_ycbcr_encoding ycbcr_enc;
enum v4l2_xfer_func xfer_func;
enum v4l2_quantization quant;
};
struct mdp_dev;
int mdp_enum_fmt_mplane(struct mdp_dev *mdp, struct v4l2_fmtdesc *f);
const struct mdp_format *mdp_try_fmt_mplane(struct mdp_dev *mdp,
struct v4l2_format *f,
struct mdp_frameparam *param,
u32 ctx_id);
enum mdp_ycbcr_profile mdp_map_ycbcr_prof_mplane(struct v4l2_format *f,
u32 mdp_color);
int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r,
const struct v4l2_selection *s, struct mdp_frame *frame);
int mdp_check_scaling_ratio(const struct v4l2_rect *crop,
const struct v4l2_rect *compose, s32 rotation,
const struct mdp_limit *limit);
bool mdp_check_pp_enable(struct mdp_dev *mdp, struct mdp_frame *frame);
void mdp_set_src_config(struct img_input *in,
struct mdp_frame *frame, struct vb2_buffer *vb);
void mdp_set_dst_config(struct img_output *out,
struct mdp_frame *frame, struct vb2_buffer *vb);
int mdp_frameparam_init(struct mdp_dev *mdp, struct mdp_frameparam *param);
#endif /* __MTK_MDP3_REGS_H__ */
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2023 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_TYPE_H__
#define __MTK_MDP3_TYPE_H__
#include <linux/types.h>
#define IMG_MAX_HW_INPUTS 3
#define IMG_MAX_HW_OUTPUTS 4
#define IMG_MAX_PLANES 3
#define IMG_MAX_COMPONENTS 20
struct img_crop {
s32 left;
s32 top;
u32 width;
u32 height;
u32 left_subpix;
u32 top_subpix;
u32 width_subpix;
u32 height_subpix;
} __packed;
struct img_region {
s32 left;
s32 right;
s32 top;
s32 bottom;
} __packed;
struct img_offset {
s32 left;
s32 top;
u32 left_subpix;
u32 top_subpix;
} __packed;
struct img_mux {
u32 reg;
u32 value;
u32 subsys_id;
} __packed;
struct img_mmsys_ctrl {
struct img_mux sets[IMG_MAX_COMPONENTS * 2];
u32 num_sets;
} __packed;
#endif /* __MTK_MDP3_TYPE_H__ */
@@ -0,0 +1,288 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#include <linux/remoteproc.h>
#include <linux/remoteproc/mtk_scp.h>
#include "mtk-mdp3-vpu.h"
#include "mtk-mdp3-core.h"
#define MDP_VPU_MESSAGE_TIMEOUT 500U
static inline struct mdp_dev *vpu_to_mdp(struct mdp_vpu_dev *vpu)
{
return container_of(vpu, struct mdp_dev, vpu);
}
static int mdp_vpu_shared_mem_alloc(struct mdp_vpu_dev *vpu)
{
struct device *dev;
if (IS_ERR_OR_NULL(vpu))
goto err_return;
dev = scp_get_device(vpu->scp);
if (!vpu->param) {
vpu->param = dma_alloc_wc(dev, vpu->param_size,
&vpu->param_addr, GFP_KERNEL);
if (!vpu->param)
goto err_return;
}
if (!vpu->work) {
vpu->work = dma_alloc_wc(dev, vpu->work_size,
&vpu->work_addr, GFP_KERNEL);
if (!vpu->work)
goto err_free_param;
}
if (!vpu->config) {
vpu->config = dma_alloc_wc(dev, vpu->config_size,
&vpu->config_addr, GFP_KERNEL);
if (!vpu->config)
goto err_free_work;
}
return 0;
err_free_work:
dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr);
vpu->work = NULL;
err_free_param:
dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr);
vpu->param = NULL;
err_return:
return -ENOMEM;
}
void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu)
{
struct device *dev;
if (IS_ERR_OR_NULL(vpu))
return;
dev = scp_get_device(vpu->scp);
if (vpu->param && vpu->param_addr)
dma_free_wc(dev, vpu->param_size, vpu->param, vpu->param_addr);
if (vpu->work && vpu->work_addr)
dma_free_wc(dev, vpu->work_size, vpu->work, vpu->work_addr);
if (vpu->config && vpu->config_addr)
dma_free_wc(dev, vpu->config_size, vpu->config, vpu->config_addr);
}
static void mdp_vpu_ipi_handle_init_ack(void *data, unsigned int len,
void *priv)
{
struct mdp_ipi_init_msg *msg = (struct mdp_ipi_init_msg *)data;
struct mdp_vpu_dev *vpu =
(struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
if (!vpu->work_size)
vpu->work_size = msg->work_size;
vpu->status = msg->status;
complete(&vpu->ipi_acked);
}
static void mdp_vpu_ipi_handle_deinit_ack(void *data, unsigned int len,
void *priv)
{
struct mdp_ipi_deinit_msg *msg = (struct mdp_ipi_deinit_msg *)data;
struct mdp_vpu_dev *vpu =
(struct mdp_vpu_dev *)(unsigned long)msg->drv_data;
vpu->status = msg->status;
complete(&vpu->ipi_acked);
}
static void mdp_vpu_ipi_handle_frame_ack(void *data, unsigned int len,
void *priv)
{
struct img_sw_addr *addr = (struct img_sw_addr *)data;
struct img_ipi_frameparam *param =
(struct img_ipi_frameparam *)(unsigned long)addr->va;
struct mdp_vpu_dev *vpu =
(struct mdp_vpu_dev *)(unsigned long)param->drv_data;
if (param->state) {
struct mdp_dev *mdp = vpu_to_mdp(vpu);
dev_err(&mdp->pdev->dev, "VPU MDP failure:%d\n", param->state);
}
vpu->status = param->state;
complete(&vpu->ipi_acked);
}
int mdp_vpu_register(struct mdp_dev *mdp)
{
int err;
struct mtk_scp *scp = mdp->scp;
struct device *dev = &mdp->pdev->dev;
err = scp_ipi_register(scp, SCP_IPI_MDP_INIT,
mdp_vpu_ipi_handle_init_ack, NULL);
if (err) {
dev_err(dev, "scp_ipi_register failed %d\n", err);
goto err_ipi_init;
}
err = scp_ipi_register(scp, SCP_IPI_MDP_DEINIT,
mdp_vpu_ipi_handle_deinit_ack, NULL);
if (err) {
dev_err(dev, "scp_ipi_register failed %d\n", err);
goto err_ipi_deinit;
}
err = scp_ipi_register(scp, SCP_IPI_MDP_FRAME,
mdp_vpu_ipi_handle_frame_ack, NULL);
if (err) {
dev_err(dev, "scp_ipi_register failed %d\n", err);
goto err_ipi_frame;
}
return 0;
err_ipi_frame:
scp_ipi_unregister(scp, SCP_IPI_MDP_DEINIT);
err_ipi_deinit:
scp_ipi_unregister(scp, SCP_IPI_MDP_INIT);
err_ipi_init:
return err;
}
void mdp_vpu_unregister(struct mdp_dev *mdp)
{
scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_INIT);
scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_DEINIT);
scp_ipi_unregister(mdp->scp, SCP_IPI_MDP_FRAME);
}
static int mdp_vpu_sendmsg(struct mdp_vpu_dev *vpu, enum scp_ipi_id id,
const void *buf, unsigned int len)
{
struct mdp_dev *mdp = vpu_to_mdp(vpu);
unsigned int t = MDP_VPU_MESSAGE_TIMEOUT;
int ret;
if (!vpu->scp) {
dev_dbg(&mdp->pdev->dev, "vpu scp is NULL");
return -EINVAL;
}
ret = scp_ipi_send(vpu->scp, id, buf, len, 2000);
if (ret) {
dev_err(&mdp->pdev->dev, "scp_ipi_send failed %d\n", ret);
return -EPERM;
}
ret = wait_for_completion_timeout(&vpu->ipi_acked,
msecs_to_jiffies(t));
if (!ret)
ret = -ETIME;
else if (vpu->status)
ret = -EINVAL;
else
ret = 0;
return ret;
}
int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp,
struct mutex *lock)
{
struct mdp_ipi_init_msg msg = {
.drv_data = (unsigned long)vpu,
};
struct mdp_dev *mdp = vpu_to_mdp(vpu);
int err;
u8 pp_num = mdp->mdp_data->pp_used;
init_completion(&vpu->ipi_acked);
vpu->scp = scp;
vpu->lock = lock;
vpu->work_size = 0;
err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));
if (err)
goto err_work_size;
/* vpu work_size was set in mdp_vpu_ipi_handle_init_ack */
mutex_lock(vpu->lock);
vpu->work_size = ALIGN(vpu->work_size, 64);
vpu->param_size = ALIGN(sizeof(struct img_ipi_frameparam), 64);
vpu->config_size = ALIGN(sizeof(struct img_config) * pp_num, 64);
err = mdp_vpu_shared_mem_alloc(vpu);
mutex_unlock(vpu->lock);
if (err) {
dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
goto err_mem_alloc;
}
dev_dbg(&mdp->pdev->dev,
"VPU param:%p pa:%pad sz:%zx, work:%p pa:%pad sz:%zx, config:%p pa:%pad sz:%zx",
vpu->param, &vpu->param_addr, vpu->param_size,
vpu->work, &vpu->work_addr, vpu->work_size,
vpu->config, &vpu->config_addr, vpu->config_size);
msg.work_addr = vpu->work_addr;
msg.work_size = vpu->work_size;
err = mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_INIT, &msg, sizeof(msg));
if (err)
goto err_work_size;
return 0;
err_work_size:
switch (vpu->status) {
case -MDP_IPI_EBUSY:
err = -EBUSY;
break;
case -MDP_IPI_ENOMEM:
err = -ENOSPC; /* -ENOMEM */
break;
}
return err;
err_mem_alloc:
return err;
}
int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu)
{
struct mdp_ipi_deinit_msg msg = {
.drv_data = (unsigned long)vpu,
.work_addr = vpu->work_addr,
};
return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_DEINIT, &msg, sizeof(msg));
}
int mdp_vpu_process(struct mdp_vpu_dev *vpu, struct img_ipi_frameparam *param)
{
struct mdp_dev *mdp = vpu_to_mdp(vpu);
struct img_sw_addr addr;
mutex_lock(vpu->lock);
if (mdp_vpu_shared_mem_alloc(vpu)) {
dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
mutex_unlock(vpu->lock);
return -ENOMEM;
}
memset(vpu->param, 0, vpu->param_size);
memset(vpu->work, 0, vpu->work_size);
memset(vpu->config, 0, vpu->config_size);
param->self_data.va = (unsigned long)vpu->work;
param->self_data.pa = vpu->work_addr;
param->config_data.va = (unsigned long)vpu->config;
param->config_data.pa = vpu->config_addr;
param->drv_data = (unsigned long)vpu;
memcpy(vpu->param, param, sizeof(*param));
addr.pa = vpu->param_addr;
addr.va = (unsigned long)vpu->param;
mutex_unlock(vpu->lock);
return mdp_vpu_sendmsg(vpu, SCP_IPI_MDP_FRAME, &addr, sizeof(addr));
}
@@ -0,0 +1,63 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022 MediaTek Inc.
* Author: Ping-Hsun Wu <ping-hsun.wu@mediatek.com>
*/
#ifndef __MTK_MDP3_VPU_H__
#define __MTK_MDP3_VPU_H__
#include <linux/platform_device.h>
#include "mtk-img-ipi.h"
enum mdp_ipi_result {
MDP_IPI_SUCCESS = 0,
MDP_IPI_ENOMEM = 12,
MDP_IPI_EBUSY = 16,
MDP_IPI_EINVAL = 22,
MDP_IPI_EMINST = 24,
MDP_IPI_ERANGE = 34,
MDP_IPI_NR_ERRNO,
MDP_IPI_EOTHER = MDP_IPI_NR_ERRNO,
MDP_IPI_PATH_CANT_MERGE,
MDP_IPI_OP_FAIL,
};
struct mdp_ipi_init_msg {
u32 status;
u64 drv_data;
u32 work_addr; /* [in] working buffer address */
u32 work_size; /* [in] working buffer size */
} __packed;
struct mdp_ipi_deinit_msg {
u32 status;
u64 drv_data;
u32 work_addr;
} __packed;
struct mdp_vpu_dev {
/* synchronization protect for accessing vpu working buffer info */
struct mutex *lock;
struct mtk_scp *scp;
struct completion ipi_acked;
void *param;
dma_addr_t param_addr;
size_t param_size;
void *work;
dma_addr_t work_addr;
size_t work_size;
void *config;
dma_addr_t config_addr;
size_t config_size;
u32 status;
};
void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu);
int mdp_vpu_dev_init(struct mdp_vpu_dev *vpu, struct mtk_scp *scp,
struct mutex *lock /* for sync */);
int mdp_vpu_dev_deinit(struct mdp_vpu_dev *vpu);
int mdp_vpu_process(struct mdp_vpu_dev *vpu, struct img_ipi_frameparam *param);
#endif /* __MTK_MDP3_VPU_H__ */