Commit 61890cca authored by Moudy Ho's avatar Moudy Ho Committed by Mauro Carvalho Chehab

media: platform: mtk-mdp3: add MediaTek MDP3 driver

This patch adds driver for MediaTek's Media Data Path ver.3 (MDP3).
It provides the following functions:
  color transform, format conversion, resize, crop, rotate, flip
  and additional image quality enhancement.

The MDP3 driver is mainly used for Google Chromebook products to
import the new architecture to set the HW settings as shown below:
  User -> V4L2 framework
    -> MDP3 driver -> SCP (setting calculations)
      -> MDP3 driver -> CMDQ (GCE driver) -> HW

Each modules' related operation control is sited in mtk-mdp3-comp.c
Each modules' register table is defined in file with "mdp_reg_" prefix
GCE related API, operation control  sited in mtk-mdp3-cmdq.c
V4L2 m2m device functions are implemented in mtk-mdp3-m2m.c
Probe, power, suspend/resume, system level functions are defined in
mtk-mdp3-core.c

[hverkuil: add 'depends on REMOTEPROC']
Signed-off-by: default avatarPing-Hsun Wu <ping-hsun.wu@mediatek.com>
Signed-off-by: default avatardaoyuan huang <daoyuan.huang@mediatek.com>
Signed-off-by: default avatarMoudy Ho <moudy.ho@mediatek.com>
Tested-by: default avatarAngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@kernel.org>
parent 8bbdead4
......@@ -6,3 +6,4 @@ source "drivers/media/platform/mediatek/jpeg/Kconfig"
source "drivers/media/platform/mediatek/mdp/Kconfig"
source "drivers/media/platform/mediatek/vcodec/Kconfig"
source "drivers/media/platform/mediatek/vpu/Kconfig"
source "drivers/media/platform/mediatek/mdp3/Kconfig"
......@@ -3,3 +3,4 @@ obj-y += jpeg/
obj-y += mdp/
obj-y += vcodec/
obj-y += vpu/
obj-y += mdp3/
# 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 ARCH_MEDIATEK || COMPILE_TEST
depends on HAS_DMA
depends on REMOTEPROC
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select MTK_MMSYS
select VIDEO_MEDIATEK_VPU
select MTK_CMDQ
select MTK_SCP
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.
# SPDX-License-Identifier: GPL-2.0-only
mtk-mdp3-y += 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
/* 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__
/* 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_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_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__
/* 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
/* 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
#endif // __MDP_REG_RSZ_H__
/* 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__
/* 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_DITHER 0x054
#define VIDO_STRIDE_V 0x06c
#define VIDO_OFST_ADDR_V 0x068
#define VIDO_RSV_1 0x070
#define VIDO_IN_SIZE 0x078
#define VIDO_ROT_EN 0x07c
#define VIDO_FIFO_TEST 0x080
#define VIDO_MAT_CTRL 0x084
#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_DITHER_MASK 0xff000001
#define VIDO_STRIDE_V_MASK 0x0000ffff
#define VIDO_OFST_ADDR_V_MASK 0x0fffffff
#define VIDO_RSV_1_MASK 0xffffffff
#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_BASE_ADDR_MASK 0xffffffff
#define VIDO_BASE_ADDR_C_MASK 0xffffffff
#define VIDO_BASE_ADDR_V_MASK 0xffffffff
#endif // __MDP_REG_WROT_H__
/* 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/types.h>
/*
* ISP-MDP generic input information
* MD5 of the target SCP blob:
* 6da52bdcf4bf76a0983b313e1d4745d6
*/
#define IMG_MAX_HW_INPUTS 3
#define IMG_MAX_HW_OUTPUTS 4
#define IMG_MAX_PLANES 3
#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;
u16 stride;
} __packed;
struct img_pix_format {
u16 width;
u16 height;
u32 colorformat; /* enum mdp_color */
u16 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
struct img_crop {
s16 left;
s16 top;
u16 width;
u16 height;
u32 left_subpix;
u32 top_subpix;
u32 width_subpix;
u32 height_subpix;
} __packed;
#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;
u16 flags; /* HDR, DRE, dither */
} __packed;
struct img_output {
struct img_image_buffer buffer;
struct img_crop crop;
s16 rotation;
u16 flags; /* H-flip, sharpness, dither */
} __packed;
struct img_ipi_frameparam {
u32 index;
u32 frame_no;
struct img_timeval timestamp;
u8 type; /* enum mdp_stream_type */
u8 state;
u8 num_inputs;
u8 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 {
u8 usage;
struct img_sw_buffer frm_param;
} __packed;
struct img_frameparam {
struct list_head list_entry;
struct img_ipi_frameparam frameparam;
};
/* ISP-MDP generic output information */
struct img_comp_frame {
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_region {
s16 left;
s16 right;
s16 top;
s16 bottom;
} __packed;
struct img_offset {
s16 left;
s16 top;
u32 left_subpix;
u32 top_subpix;
} __packed;
struct img_comp_subfrm {
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;
#define IMG_MAX_SUBFRAMES 14
struct mdp_rdma_subfrm {
u32 offset[IMG_MAX_PLANES];
u32 offset_0_p;
u32 src;
u32 clip;
u32 clip_ofst;
} __packed;
struct mdp_rdma_data {
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 subfrms[IMG_MAX_SUBFRAMES];
} __packed;
struct mdp_rsz_subfrm {
u32 control2;
u32 src;
u32 clip;
} __packed;
struct mdp_rsz_data {
u32 coeff_step_x;
u32 coeff_step_y;
u32 control1;
u32 control2;
struct mdp_rsz_subfrm subfrms[IMG_MAX_SUBFRAMES];
} __packed;
struct mdp_wrot_subfrm {
u32 offset[IMG_MAX_PLANES];
u32 src;
u32 clip;
u32 clip_ofst;
u32 main_buf;
} __packed;
struct mdp_wrot_data {
u32 iova[IMG_MAX_PLANES];
u32 control;
u32 stride[IMG_MAX_PLANES];
u32 mat_ctrl;
u32 fifo_test;
u32 filter;
struct mdp_wrot_subfrm subfrms[IMG_MAX_SUBFRAMES];
} __packed;
struct mdp_wdma_subfrm {
u32 offset[IMG_MAX_PLANES];
u32 src;
u32 clip;
u32 clip_ofst;
} __packed;
struct mdp_wdma_data {
u32 wdma_cfg;
u32 iova[IMG_MAX_PLANES];
u32 w_in_byte;
u32 uv_stride;
struct mdp_wdma_subfrm subfrms[IMG_MAX_SUBFRAMES];
} __packed;
struct isp_data {
u64 dl_flags; /* 1 << (enum mdp_comp_type) */
u32 smxi_iova[4];
u32 cq_idx;
u32 cq_iova;
u32 tpipe_iova[IMG_MAX_SUBFRAMES];
} __packed;
struct img_compparam {
u16 type; /* enum mdp_comp_type */
u16 id; /* enum mtk_mdp_comp_id */
u32 input;
u32 outputs[IMG_MAX_HW_OUTPUTS];
u32 num_outputs;
struct img_comp_frame frame;
struct img_comp_subfrm subfrms[IMG_MAX_SUBFRAMES];
u32 num_subfrms;
union {
struct mdp_rdma_data rdma;
struct mdp_rsz_data rsz;
struct mdp_wrot_data wrot;
struct mdp_wdma_data wdma;
struct isp_data isp;
};
} __packed;
#define IMG_MAX_COMPONENTS 20
struct img_mux {
u32 reg;
u32 value;
u32 subsys_id;
};
struct img_mmsys_ctrl {
struct img_mux sets[IMG_MAX_COMPONENTS * 2];
u32 num_sets;
};
struct img_config {
struct img_compparam components[IMG_MAX_COMPONENTS];
u32 num_components;
struct img_mmsys_ctrl ctrls[IMG_MAX_SUBFRAMES];
u32 num_subfrms;
} __packed;
#endif /* __MTK_IMG_IPI_H__ */
This diff is collapsed.
/* 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 platform_device *mdp_get_plat_device(struct platform_device *pdev);
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;
void (*user_cmdq_cb)(struct cmdq_cb_data data);
void *user_cb_data;
struct mdp_comp *comps;
void *mdp_ctx;
u8 num_comps;
};
struct mdp_dev;
int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param);
#endif /* __MTK_MDP3_CMDQ_H__ */
This diff is collapsed.
/* 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, ...) \
cmdq_pkt_write_mask(&((cmd)->pkt), id, \
(base) + (ofst), (val), (mask), ##__VA_ARGS__)
#define MM_REG_WRITE(cmd, id, base, ofst, val, mask, ...) \
do { \
typeof(mask) (m) = (mask); \
MM_REG_WRITE_MASK(cmd, id, base, ofst, val, \
(((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
(0xffffffff) : (m), ##__VA_ARGS__); \
} while (0)
#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), ##__VA_ARGS__); \
} while (0)
#define MM_REG_POLL(cmd, id, base, ofst, val, mask, ...) \
do { \
typeof(mask) (m) = (mask); \
MM_REG_POLL_MASK((cmd), id, base, ofst, val, \
(((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
(0xffffffff) : (m), ##__VA_ARGS__); \
} while (0)
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_AAL0, /* 12 */
MDP_COMP_CCORR0, /* 13 */
MDP_COMP_RSZ0, /* 14 */
MDP_COMP_RSZ1, /* 15 */
MDP_COMP_TDSHP0, /* 16 */
MDP_COMP_COLOR0, /* 17 */
MDP_COMP_PATH0_SOUT, /* 18 */
MDP_COMP_PATH1_SOUT, /* 19 */
MDP_COMP_WROT0, /* 20 */
MDP_COMP_WDMA, /* 21 */
/* Dummy Engine */
MDP_COMP_RDMA1, /* 22 */
MDP_COMP_RSZ2, /* 23 */
MDP_COMP_TDSHP1, /* 24 */
MDP_COMP_WROT1, /* 25 */
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_HDR,
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_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_ops;
struct mdp_comp {
struct mdp_dev *mdp_dev;
void __iomem *regs;
phys_addr_t reg_base;
u8 subsys_id;
struct clk *clks[6];
struct device *comp_dev;
enum mdp_comp_type type;
enum mtk_mdp_comp_id 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__ */
// 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-m2m.h"
static const struct mdp_platform_config mt8183_plat_cfg = {
.rdma_support_10bit = true,
.rdma_rsz1_sram_sharing = true,
.rdma_upsample_repeat_only = true,
.rsz_disable_dcm_small_sample = false,
.wrot_filter_constraint = false,
};
static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = {
[MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8183-mmsys" },
[MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8183-disp-mutex" },
[MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" }
};
static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = {
[MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0,
[MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0,
[MDP_COMP_RSZ1] = MUTEX_MOD_IDX_MDP_RSZ1,
[MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0,
[MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0,
[MDP_COMP_WDMA] = MUTEX_MOD_IDX_MDP_WDMA,
[MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0,
[MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0,
};
static const struct mtk_mdp_driver_data mt8183_mdp_driver_data = {
.mdp_probe_infra = mt8183_mdp_probe_infra,
.mdp_cfg = &mt8183_plat_cfg,
.mdp_mutex_table_idx = mt8183_mutex_idx,
};
static const struct of_device_id mdp_of_ids[] = {
{ .compatible = "mediatek,mt8183-mdp3-rdma",
.data = &mt8183_mdp_driver_data,
},
{},
};
MODULE_DEVICE_TABLE(of, mdp_of_ids);
static struct platform_device *__get_pdev_by_id(struct platform_device *pdev,
enum mdp_infra_id id)
{
struct device_node *node;
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;
node = of_find_compatible_node(NULL, 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;
}
struct platform_device *mdp_get_plat_device(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *mdp_node;
struct platform_device *mdp_pdev;
mdp_node = of_parse_phandle(dev->of_node, MDP_PHANDLE_NAME, 0);
if (!mdp_node) {
dev_err(dev, "can't get node %s\n", MDP_PHANDLE_NAME);
return NULL;
}
mdp_pdev = of_find_device_by_node(mdp_node);
of_node_put(mdp_node);
return mdp_pdev;
}
EXPORT_SYMBOL_GPL(mdp_get_plat_device);
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;
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_PIPE_MAX; i++)
mtk_mutex_put(mdp->mdp_mutex[i]);
mdp_vpu_shared_mem_free(&mdp->vpu);
v4l2_m2m_release(mdp->m2m_dev);
kfree(mdp);
}
static int mdp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mdp_dev *mdp;
struct platform_device *mm_pdev;
int ret, i;
mdp = kzalloc(sizeof(*mdp), GFP_KERNEL);
if (!mdp) {
ret = -ENOMEM;
goto err_return;
}
mdp->pdev = pdev;
mdp->mdp_data = of_device_get_match_data(&pdev->dev);
mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_MMSYS);
if (!mm_pdev) {
ret = -ENODEV;
goto err_return;
}
mdp->mdp_mmsys = &mm_pdev->dev;
mm_pdev = __get_pdev_by_id(pdev, MDP_INFRA_MUTEX);
if (WARN_ON(!mm_pdev)) {
ret = -ENODEV;
goto err_return;
}
for (i = 0; i < MDP_PIPE_MAX; i++) {
mdp->mdp_mutex[i] = mtk_mutex_get(&mm_pdev->dev);
if (!mdp->mdp_mutex[i]) {
ret = -ENODEV;
goto err_return;
}
}
ret = mdp_comp_config(mdp);
if (ret) {
dev_err(dev, "Failed to config mdp components\n");
goto err_return;
}
mdp->job_wq = alloc_workqueue(MDP_MODULE_NAME, WQ_FREEZABLE, 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,
0);
if (!mdp->clock_wq) {
dev_err(dev, "Unable to create clock workqueue\n");
ret = -ENOMEM;
goto err_destroy_job_wq;
}
mm_pdev = __get_pdev_by_id(pdev, 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);
mdp->rproc_handle = scp_get_rproc(mdp->scp);
dev_dbg(&pdev->dev, "MDP rproc_handle: %pK", mdp->rproc_handle);
mutex_init(&mdp->vpu_lock);
mutex_init(&mdp->m2m_lock);
mdp->cmdq_clt = cmdq_mbox_create(dev, 0);
if (IS_ERR(mdp->cmdq_clt)) {
ret = PTR_ERR(mdp->cmdq_clt);
goto err_put_scp;
}
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;
}
dev_dbg(dev, "mdp-%d registered successfully\n", pdev->id);
return 0;
err_unregister_device:
v4l2_device_unregister(&mdp->v4l2_dev);
err_mbox_destroy:
cmdq_mbox_destroy(mdp->cmdq_clt);
err_put_scp:
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_return:
for (i = 0; i < MDP_PIPE_MAX; i++)
mtk_mutex_put(mdp->mdp_mutex[i]);
kfree(mdp);
dev_dbg(dev, "Errno %d\n", ret);
return ret;
}
static int 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);
return 0;
}
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 (atomic_read(&mdp->job_count)) {
ret = wait_event_timeout(mdp->callback_wq,
!atomic_read(&mdp->job_count),
2 * HZ);
if (ret == 0) {
dev_err(dev,
"%s:flushed cmdq task incomplete, count=%d\n",
__func__, atomic_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 = of_match_ptr(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");
/* 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 {
MDP_INFRA_MMSYS,
MDP_INFRA_MUTEX,
MDP_INFRA_SCP,
MDP_INFRA_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 rsz_disable_dcm_small_sample;
bool wrot_filter_constraint;
};
/* indicate which mutex is used by each pipepline */
enum mdp_pipe_id {
MDP_PIPE_RDMA0,
MDP_PIPE_IMGI,
MDP_PIPE_WPEI,
MDP_PIPE_WPEI2,
MDP_PIPE_MAX
};
struct mtk_mdp_driver_data {
const struct of_device_id *mdp_probe_infra;
const struct mdp_platform_config *mdp_cfg;
const u32 *mdp_mutex_table_idx;
};
struct mdp_dev {
struct platform_device *pdev;
struct device *mdp_mmsys;
struct mtk_mutex *mdp_mutex[MDP_PIPE_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;
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;
atomic_t job_count;
};
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__ */
This diff is collapsed.
/* 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;
struct mdp_vpu_ctx vpu;
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__ */
This diff is collapsed.
This diff is collapsed.
// 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
#define vpu_alloc_size 0x600000
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)
{
if (vpu->work && vpu->work_addr)
return 0;
vpu->work = dma_alloc_coherent(scp_get_device(vpu->scp), vpu_alloc_size,
&vpu->work_addr, GFP_KERNEL);
if (!vpu->work)
return -ENOMEM;
else
return 0;
}
void mdp_vpu_shared_mem_free(struct mdp_vpu_dev *vpu)
{
if (vpu->work && vpu->work_addr)
dma_free_coherent(scp_get_device(vpu->scp), vpu_alloc_size,
vpu->work, vpu->work_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_ctx *ctx =
(struct mdp_vpu_ctx *)(unsigned long)param->drv_data;
if (param->state) {
struct mdp_dev *mdp = vpu_to_mdp(ctx->vpu_dev);
dev_err(&mdp->pdev->dev, "VPU MDP failure:%d\n", param->state);
}
ctx->vpu_dev->status = param->state;
complete(&ctx->vpu_dev->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,
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,
};
size_t mem_size;
phys_addr_t pool;
const size_t pool_size = sizeof(struct mdp_config_pool);
struct mdp_dev *mdp = vpu_to_mdp(vpu);
int err;
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 */
mem_size = vpu_alloc_size;
if (mdp_vpu_shared_mem_alloc(vpu)) {
dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
goto err_mem_alloc;
}
pool = ALIGN((uintptr_t)vpu->work + vpu->work_size, 8);
if (pool + pool_size - (uintptr_t)vpu->work > mem_size) {
dev_err(&mdp->pdev->dev,
"VPU memory insufficient: %zx + %zx > %zx",
vpu->work_size, pool_size, mem_size);
err = -ENOMEM;
goto err_mem_size;
}
dev_dbg(&mdp->pdev->dev,
"VPU work:%pK pa:%pad sz:%zx pool:%pa sz:%zx (mem sz:%zx)",
vpu->work, &vpu->work_addr, vpu->work_size,
&pool, pool_size, mem_size);
vpu->pool = (struct mdp_config_pool *)(uintptr_t)pool;
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;
memset(vpu->pool, 0, sizeof(*vpu->pool));
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_size:
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));
}
static struct img_config *mdp_config_get(struct mdp_vpu_dev *vpu,
enum mdp_config_id id, uint32_t *addr)
{
struct img_config *config;
if (id < 0 || id >= MDP_CONFIG_POOL_SIZE)
return ERR_PTR(-EINVAL);
mutex_lock(vpu->lock);
vpu->pool->cfg_count[id]++;
config = &vpu->pool->configs[id];
*addr = vpu->work_addr + ((uintptr_t)config - (uintptr_t)vpu->work);
mutex_unlock(vpu->lock);
return config;
}
static int mdp_config_put(struct mdp_vpu_dev *vpu,
enum mdp_config_id id,
const struct img_config *config)
{
int err = 0;
if (id < 0 || id >= MDP_CONFIG_POOL_SIZE)
return -EINVAL;
if (vpu->lock)
mutex_lock(vpu->lock);
if (!vpu->pool->cfg_count[id] || config != &vpu->pool->configs[id])
err = -EINVAL;
else
vpu->pool->cfg_count[id]--;
if (vpu->lock)
mutex_unlock(vpu->lock);
return err;
}
int mdp_vpu_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu,
enum mdp_config_id id)
{
ctx->config = mdp_config_get(vpu, id, &ctx->inst_addr);
if (IS_ERR(ctx->config)) {
int err = PTR_ERR(ctx->config);
ctx->config = NULL;
return err;
}
ctx->config_id = id;
ctx->vpu_dev = vpu;
return 0;
}
int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx)
{
int err = mdp_config_put(ctx->vpu_dev, ctx->config_id, ctx->config);
ctx->config_id = 0;
ctx->config = NULL;
ctx->inst_addr = 0;
return err;
}
int mdp_vpu_process(struct mdp_vpu_ctx *ctx, struct img_ipi_frameparam *param)
{
struct mdp_vpu_dev *vpu = ctx->vpu_dev;
struct mdp_dev *mdp = vpu_to_mdp(vpu);
struct img_sw_addr addr;
if (!ctx->vpu_dev->work || !ctx->vpu_dev->work_addr) {
if (mdp_vpu_shared_mem_alloc(vpu)) {
dev_err(&mdp->pdev->dev, "VPU memory alloc fail!");
return -ENOMEM;
}
}
memset((void *)ctx->vpu_dev->work, 0, ctx->vpu_dev->work_size);
memset(ctx->config, 0, sizeof(*ctx->config));
param->config_data.va = (unsigned long)ctx->config;
param->config_data.pa = ctx->inst_addr;
param->drv_data = (unsigned long)ctx;
memcpy((void *)ctx->vpu_dev->work, param, sizeof(*param));
addr.pa = ctx->vpu_dev->work_addr;
addr.va = (uintptr_t)ctx->vpu_dev->work;
return mdp_vpu_sendmsg(ctx->vpu_dev, SCP_IPI_MDP_FRAME,
&addr, sizeof(addr));
}
/* 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;
enum mdp_config_id {
MDP_DEV_M2M = 0,
MDP_CONFIG_POOL_SIZE /* ALWAYS keep at the end */
};
struct mdp_config_pool {
u64 cfg_count[MDP_CONFIG_POOL_SIZE];
struct img_config configs[MDP_CONFIG_POOL_SIZE];
};
struct mdp_vpu_dev {
/* synchronization protect for accessing vpu working buffer info */
struct mutex *lock;
struct mtk_scp *scp;
struct completion ipi_acked;
void *work;
dma_addr_t work_addr;
size_t work_size;
struct mdp_config_pool *pool;
u32 status;
};
struct mdp_vpu_ctx {
struct mdp_vpu_dev *vpu_dev;
u32 config_id;
struct img_config *config;
u32 inst_addr;
};
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_ctx_init(struct mdp_vpu_ctx *ctx, struct mdp_vpu_dev *vpu,
enum mdp_config_id id);
int mdp_vpu_ctx_deinit(struct mdp_vpu_ctx *ctx);
int mdp_vpu_process(struct mdp_vpu_ctx *vpu, struct img_ipi_frameparam *param);
#endif /* __MTK_MDP3_VPU_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment