summaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig1
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-core.c1
-rw-r--r--drivers/media/platform/amlogic/Kconfig1
-rw-r--r--drivers/media/platform/amlogic/Makefile2
-rw-r--r--drivers/media/platform/amlogic/c3/Kconfig5
-rw-r--r--drivers/media/platform/amlogic/c3/Makefile5
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Kconfig18
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Makefile10
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c804
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-common.h340
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-core.c641
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c421
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-params.c1008
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h618
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c892
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c326
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c842
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c828
-rw-r--r--drivers/media/platform/amphion/vdec.c2
-rw-r--r--drivers/media/platform/amphion/vpu.h1
-rw-r--r--drivers/media/platform/amphion/vpu_core.c7
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c39
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c8
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c4
-rw-r--r--drivers/media/platform/chips-media/coda/coda-common.c1
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-hw.c2
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c31
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.c8
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.c10
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.c9
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c5
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h4
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c73
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h1
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h8
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c33
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h7
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c2
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h2
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c19
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c5
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c652
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c8
-rw-r--r--drivers/media/platform/nuvoton/npcm-video.c25
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c8
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h1
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c132
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h5
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c5
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c14
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c11
-rw-r--r--drivers/media/platform/qcom/Kconfig1
-rw-r--r--drivers/media/platform/qcom/Makefile1
-rw-r--r--drivers/media/platform/qcom/camss/Makefile4
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-1.c19
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-7.c42
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-680.c422
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-780.c337
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-780.h25
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.c60
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c258
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h55
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c6
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c929
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c32
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h9
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-17x.c112
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c9
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-7.c11
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-8.c11
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-480.c274
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-680.c244
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-780.c159
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.c9
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c277
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h60
-rw-r--r--drivers/media/platform/qcom/camss/camss.c954
-rw-r--r--drivers/media/platform/qcom/camss/camss.h7
-rw-r--r--drivers/media/platform/qcom/iris/Kconfig13
-rw-r--r--drivers/media/platform/qcom/iris/Makefile31
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c623
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h117
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.c96
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.h113
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c259
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.h22
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c118
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.c176
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h155
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1.h16
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c826
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h448
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c666
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2.h41
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c957
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h161
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c292
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h125
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c934
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.c318
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.h182
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h77
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h190
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c385
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h124
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8250.c149
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8650.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.c140
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c372
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.c131
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.h18
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.c276
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.h144
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.c90
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.h53
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c335
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.h19
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c659
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.h25
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c453
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu2.c39
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu3x.c275
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c270
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.h91
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c369
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.h31
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_register_defines.h17
-rw-r--r--drivers/media/platform/qcom/venus/Kconfig2
-rw-r--r--drivers/media/platform/qcom/venus/core.c16
-rw-r--r--drivers/media/platform/qcom/venus/core.h2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c100
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c18
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c38
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c18
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c9
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.c7
-rw-r--r--drivers/media/platform/renesas/Kconfig18
-rw-r--r--drivers/media/platform/renesas/Makefile2
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c126
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Kconfig18
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Makefile4
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c (renamed from drivers/media/platform/renesas/rcar-isp.c)219
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c8
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c186
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c23
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h41
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c139
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h91
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h39
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c165
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c13
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c295
-rw-r--r--drivers/media/platform/renesas/vsp1/Makefile2
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1.h4
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c9
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c7
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c30
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.h8
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c70
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c30
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c11
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.c121
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.h29
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c187
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.h6
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_regs.h8
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rpf.c38
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c51
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c9
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c9
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c50
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c53
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c7
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.c2
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c2
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h7
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-capture.c6
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.c8
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.h4
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-capture.c12
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h1
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c22
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h1
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c10
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-debug.c8
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c14
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c7
-rw-r--r--drivers/media/platform/st/sti/delta/delta-debug.c8
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.c3
-rw-r--r--drivers/media/platform/st/stm32/stm32-csi.c106
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c18
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c10
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c23
-rw-r--r--drivers/media/platform/synopsys/Kconfig3
-rw-r--r--drivers/media/platform/synopsys/Makefile2
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Kconfig36
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Makefile4
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c2746
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h394
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c275
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h43
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.c2
-rw-r--r--drivers/media/platform/ti/cal/cal-camerarx.c269
-rw-r--r--drivers/media/platform/ti/cal/cal-video.c157
-rw-r--r--drivers/media/platform/ti/cal/cal.c49
-rw-r--r--drivers/media/platform/ti/cal/cal.h4
-rw-r--r--drivers/media/platform/ti/davinci/vpif.c4
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.c54
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.h2
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.c8
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.c6
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c19
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c1
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c8
-rw-r--r--drivers/media/platform/verisilicon/hantro_postproc.c4
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.c1
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c4
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu_hw.c34
-rw-r--r--drivers/media/platform/xilinx/xilinx-tpg.c2
234 files changed, 28323 insertions, 2180 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 85d2627776b6..9287faafdce5 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -85,6 +85,7 @@ source "drivers/media/platform/rockchip/Kconfig"
source "drivers/media/platform/samsung/Kconfig"
source "drivers/media/platform/st/Kconfig"
source "drivers/media/platform/sunxi/Kconfig"
+source "drivers/media/platform/synopsys/Kconfig"
source "drivers/media/platform/ti/Kconfig"
source "drivers/media/platform/verisilicon/Kconfig"
source "drivers/media/platform/via/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index ace4e34483dd..6fd7db0541c7 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -28,6 +28,7 @@ obj-y += rockchip/
obj-y += samsung/
obj-y += st/
obj-y += sunxi/
+obj-y += synopsys/
obj-y += ti/
obj-y += verisilicon/
obj-y += via/
diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c
index e491399afcc9..eb03df0d8652 100644
--- a/drivers/media/platform/allegro-dvt/allegro-core.c
+++ b/drivers/media/platform/allegro-dvt/allegro-core.c
@@ -3912,6 +3912,7 @@ static int allegro_probe(struct platform_device *pdev)
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"failed to request firmware: %d\n", ret);
+ v4l2_device_unregister(&dev->v4l2_dev);
return ret;
}
diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platform/amlogic/Kconfig
index 5014957404e9..458acf3d5fa8 100644
--- a/drivers/media/platform/amlogic/Kconfig
+++ b/drivers/media/platform/amlogic/Kconfig
@@ -2,4 +2,5 @@
comment "Amlogic media platform drivers"
+source "drivers/media/platform/amlogic/c3/Kconfig"
source "drivers/media/platform/amlogic/meson-ge2d/Kconfig"
diff --git a/drivers/media/platform/amlogic/Makefile b/drivers/media/platform/amlogic/Makefile
index d3cdb8fa4ddb..c744afcd1b9e 100644
--- a/drivers/media/platform/amlogic/Makefile
+++ b/drivers/media/platform/amlogic/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += c3/
obj-y += meson-ge2d/
diff --git a/drivers/media/platform/amlogic/c3/Kconfig b/drivers/media/platform/amlogic/c3/Kconfig
new file mode 100644
index 000000000000..d355d3a9358d
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/media/platform/amlogic/c3/isp/Kconfig"
+source "drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig"
+source "drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig"
diff --git a/drivers/media/platform/amlogic/c3/Makefile b/drivers/media/platform/amlogic/c3/Makefile
new file mode 100644
index 000000000000..14f305a493d2
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += isp/
+obj-y += mipi-adapter/
+obj-y += mipi-csi2/
diff --git a/drivers/media/platform/amlogic/c3/isp/Kconfig b/drivers/media/platform/amlogic/c3/isp/Kconfig
new file mode 100644
index 000000000000..02c62a50a5e8
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_C3_ISP
+ tristate "Amlogic C3 Image Signal Processor (ISP) driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on OF
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ help
+ Video4Linux2 driver for Amlogic C3 ISP pipeline.
+ The C3 ISP is used for processing raw images and
+ outputing results to memory.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amlogic/c3/isp/Makefile b/drivers/media/platform/amlogic/c3/isp/Makefile
new file mode 100644
index 000000000000..b1b064170b57
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+c3-isp-objs := c3-isp-dev.o \
+ c3-isp-params.o \
+ c3-isp-stats.o \
+ c3-isp-capture.o \
+ c3-isp-core.o \
+ c3-isp-resizer.o
+
+obj-$(CONFIG_VIDEO_C3_ISP) += c3-isp.o
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c
new file mode 100644
index 000000000000..11d85f5342f0
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/cleanup.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+#define C3_ISP_WRMIFX3_REG(addr, id) ((addr) + (id) * 0x100)
+
+static const struct c3_isp_cap_format_info cap_formats[] = {
+ /* YUV formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_Y_ONLY,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 2,
+ .vdiv = 2,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 2,
+ .vdiv = 2,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 1,
+ .vdiv = 2
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 1,
+ .vdiv = 2,
+ },
+ /* RAW formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ },
+};
+
+/* Hardware configuration */
+
+/* Set the address of wrmifx3(write memory interface) */
+static void c3_isp_cap_wrmifx3_buff(struct c3_isp_capture *cap)
+{
+ dma_addr_t y_dma_addr;
+ dma_addr_t uv_dma_addr;
+
+ if (cap->buff) {
+ y_dma_addr = cap->buff->dma_addr[C3_ISP_PLANE_Y];
+ uv_dma_addr = cap->buff->dma_addr[C3_ISP_PLANE_UV];
+ } else {
+ y_dma_addr = cap->dummy_buff.dma_addr;
+ uv_dma_addr = cap->dummy_buff.dma_addr;
+ }
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_BADDR, cap->id),
+ ISP_WRMIFX3_0_CH0_BASE_ADDR(y_dma_addr));
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_BADDR, cap->id),
+ ISP_WRMIFX3_0_CH1_BASE_ADDR(uv_dma_addr));
+}
+
+static void c3_isp_cap_wrmifx3_format(struct c3_isp_capture *cap)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp;
+ const struct c3_isp_cap_format_info *info = cap->format.info;
+ u32 stride;
+ u32 chrom_h;
+ u32 chrom_v;
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_SIZE, cap->id),
+ ISP_WRMIFX3_0_FMT_SIZE_HSIZE(pix_mp->width) |
+ ISP_WRMIFX3_0_FMT_SIZE_VSIZE(pix_mp->height));
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_MASK, info->format);
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_MASK,
+ info->in_bits);
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_MASK, info->planes);
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_MASK,
+ info->uv_swap);
+
+ stride = DIV_ROUND_UP(pix_mp->plane_fmt[C3_ISP_PLANE_Y].bytesperline,
+ C3_ISP_DMA_SIZE_ALIGN_BYTES);
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_CTRL0, cap->id),
+ ISP_WRMIFX3_0_CH0_CTRL0_STRIDE_MASK,
+ ISP_WRMIFX3_0_CH0_CTRL0_STRIDE(stride));
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_CTRL1, cap->id),
+ ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_MODE_MASK,
+ info->ch0_pix_bits);
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_LUMA_H, cap->id),
+ ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND(pix_mp->width));
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_LUMA_V, cap->id),
+ ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND(pix_mp->height));
+
+ stride = DIV_ROUND_UP(pix_mp->plane_fmt[C3_ISP_PLANE_UV].bytesperline,
+ C3_ISP_DMA_SIZE_ALIGN_BYTES);
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_CTRL0, cap->id),
+ ISP_WRMIFX3_0_CH1_CTRL0_STRIDE_MASK,
+ ISP_WRMIFX3_0_CH1_CTRL0_STRIDE(stride));
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_CTRL1, cap->id),
+ ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_MODE_MASK,
+ ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_16BITS);
+
+ chrom_h = DIV_ROUND_UP(pix_mp->width, info->hdiv);
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_CHROM_H, cap->id),
+ ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND(chrom_h));
+
+ chrom_v = DIV_ROUND_UP(pix_mp->height, info->vdiv);
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_CHROM_V, cap->id),
+ ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND(chrom_v));
+}
+
+static int c3_isp_cap_dummy_buff_create(struct c3_isp_capture *cap)
+{
+ struct c3_isp_dummy_buffer *dummy_buff = &cap->dummy_buff;
+ struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp;
+
+ if (pix_mp->num_planes == 1)
+ dummy_buff->size = pix_mp->plane_fmt[C3_ISP_PLANE_Y].sizeimage;
+ else
+ dummy_buff->size =
+ max(pix_mp->plane_fmt[C3_ISP_PLANE_Y].sizeimage,
+ pix_mp->plane_fmt[C3_ISP_PLANE_UV].sizeimage);
+
+ /* The driver never access vaddr, no mapping is required */
+ dummy_buff->vaddr = dma_alloc_attrs(cap->isp->dev, dummy_buff->size,
+ &dummy_buff->dma_addr, GFP_KERNEL,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+ if (!dummy_buff->vaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void c3_isp_cap_dummy_buff_destroy(struct c3_isp_capture *cap)
+{
+ dma_free_attrs(cap->isp->dev, cap->dummy_buff.size,
+ cap->dummy_buff.vaddr, cap->dummy_buff.dma_addr,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+}
+
+static void c3_isp_cap_cfg_buff(struct c3_isp_capture *cap)
+{
+ cap->buff = list_first_entry_or_null(&cap->pending,
+ struct c3_isp_cap_buffer, list);
+
+ c3_isp_cap_wrmifx3_buff(cap);
+
+ if (cap->buff)
+ list_del(&cap->buff->list);
+}
+
+static void c3_isp_cap_start(struct c3_isp_capture *cap)
+{
+ u32 mask;
+ u32 val;
+
+ scoped_guard(spinlock_irqsave, &cap->buff_lock)
+ c3_isp_cap_cfg_buff(cap);
+
+ c3_isp_cap_wrmifx3_format(cap);
+
+ if (cap->id == C3_ISP_CAP_DEV_0) {
+ mask = ISP_TOP_PATH_EN_WRMIF0_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF0_EN;
+ } else if (cap->id == C3_ISP_CAP_DEV_1) {
+ mask = ISP_TOP_PATH_EN_WRMIF1_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF1_EN;
+ } else {
+ mask = ISP_TOP_PATH_EN_WRMIF2_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF2_EN;
+ }
+
+ c3_isp_update_bits(cap->isp, ISP_TOP_PATH_EN, mask, val);
+}
+
+static void c3_isp_cap_stop(struct c3_isp_capture *cap)
+{
+ u32 mask;
+ u32 val;
+
+ if (cap->id == C3_ISP_CAP_DEV_0) {
+ mask = ISP_TOP_PATH_EN_WRMIF0_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF0_DIS;
+ } else if (cap->id == C3_ISP_CAP_DEV_1) {
+ mask = ISP_TOP_PATH_EN_WRMIF1_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF1_DIS;
+ } else {
+ mask = ISP_TOP_PATH_EN_WRMIF2_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF2_DIS;
+ }
+
+ c3_isp_update_bits(cap->isp, ISP_TOP_PATH_EN, mask, val);
+}
+
+static void c3_isp_cap_done(struct c3_isp_capture *cap)
+{
+ struct c3_isp_cap_buffer *buff = cap->buff;
+
+ guard(spinlock_irqsave)(&cap->buff_lock);
+
+ if (buff) {
+ buff->vb.sequence = cap->isp->frm_sequence;
+ buff->vb.vb2_buf.timestamp = ktime_get();
+ buff->vb.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ c3_isp_cap_cfg_buff(cap);
+}
+
+/* V4L2 video operations */
+
+static const struct c3_isp_cap_format_info *c3_cap_find_fmt(u32 fourcc)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(cap_formats); i++) {
+ if (cap_formats[i].fourcc == fourcc)
+ return &cap_formats[i];
+ }
+
+ return NULL;
+}
+
+static void c3_cap_try_fmt(struct v4l2_pix_format_mplane *pix_mp)
+{
+ const struct c3_isp_cap_format_info *fmt;
+ const struct v4l2_format_info *info;
+ struct v4l2_plane_pix_format *plane;
+
+ fmt = c3_cap_find_fmt(pix_mp->pixelformat);
+ if (!fmt)
+ fmt = &cap_formats[0];
+
+ pix_mp->width = clamp(pix_mp->width, C3_ISP_MIN_WIDTH,
+ C3_ISP_MAX_WIDTH);
+ pix_mp->height = clamp(pix_mp->height, C3_ISP_MIN_HEIGHT,
+ C3_ISP_MAX_HEIGHT);
+ pix_mp->pixelformat = fmt->fourcc;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ pix_mp->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ info = v4l2_format_info(fmt->fourcc);
+ pix_mp->num_planes = info->mem_planes;
+ memset(pix_mp->plane_fmt, 0, sizeof(pix_mp->plane_fmt));
+
+ for (unsigned int i = 0; i < info->comp_planes; i++) {
+ unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+ unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+ plane = &pix_mp->plane_fmt[i];
+
+ plane->bytesperline = DIV_ROUND_UP(pix_mp->width, hdiv) *
+ info->bpp[i] / info->bpp_div[i];
+ plane->bytesperline = ALIGN(plane->bytesperline,
+ C3_ISP_DMA_SIZE_ALIGN_BYTES);
+ plane->sizeimage = plane->bytesperline *
+ DIV_ROUND_UP(pix_mp->height, vdiv);
+ }
+}
+
+static void c3_isp_cap_return_buffers(struct c3_isp_capture *cap,
+ enum vb2_buffer_state state)
+{
+ struct c3_isp_cap_buffer *buff;
+
+ guard(spinlock_irqsave)(&cap->buff_lock);
+
+ if (cap->buff) {
+ vb2_buffer_done(&cap->buff->vb.vb2_buf, state);
+ cap->buff = NULL;
+ }
+
+ while (!list_empty(&cap->pending)) {
+ buff = list_first_entry(&cap->pending,
+ struct c3_isp_cap_buffer, list);
+ list_del(&buff->list);
+ vb2_buffer_done(&buff->vb.vb2_buf, state);
+ }
+}
+
+static int c3_isp_cap_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "AML C3 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static int c3_isp_cap_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ const struct c3_isp_cap_format_info *fmt;
+ unsigned int index = 0;
+ unsigned int i;
+
+ if (!f->mbus_code) {
+ if (f->index >= ARRAY_SIZE(cap_formats))
+ return -EINVAL;
+
+ fmt = &cap_formats[f->index];
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cap_formats); i++) {
+ fmt = &cap_formats[i];
+ if (f->mbus_code != fmt->mbus_code)
+ continue;
+
+ if (index++ == f->index) {
+ f->pixelformat = cap_formats[i].fourcc;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int c3_isp_cap_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_capture *cap = video_drvdata(file);
+
+ f->fmt.pix_mp = cap->format.pix_mp;
+
+ return 0;
+}
+
+static int c3_isp_cap_s_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_capture *cap = video_drvdata(file);
+
+ c3_cap_try_fmt(&f->fmt.pix_mp);
+
+ cap->format.pix_mp = f->fmt.pix_mp;
+ cap->format.info = c3_cap_find_fmt(f->fmt.pix_mp.pixelformat);
+
+ return 0;
+}
+
+static int c3_isp_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ c3_cap_try_fmt(&f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int c3_isp_cap_enum_frmsize(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ const struct c3_isp_cap_format_info *fmt;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fmt = c3_cap_find_fmt(fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = C3_ISP_MIN_WIDTH;
+ fsize->stepwise.min_height = C3_ISP_MIN_HEIGHT;
+ fsize->stepwise.max_width = C3_ISP_MAX_WIDTH;
+ fsize->stepwise.max_height = C3_ISP_MAX_HEIGHT;
+ fsize->stepwise.step_width = 2;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp_cap_v4l2_ioctl_ops = {
+ .vidioc_querycap = c3_isp_cap_querycap,
+ .vidioc_enum_fmt_vid_cap = c3_isp_cap_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = c3_isp_cap_g_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = c3_isp_cap_s_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = c3_isp_cap_try_fmt_mplane,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_framesizes = c3_isp_cap_enum_frmsize,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations isp_cap_v4l2_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int c3_isp_cap_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(link->source->entity);
+ struct c3_isp_capture *cap = video_get_drvdata(vdev);
+ struct v4l2_subdev_format src_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = link->source->index,
+ };
+ int ret;
+
+ ret = v4l2_subdev_call_state_active(sd, pad, get_fmt, &src_fmt);
+ if (ret)
+ return ret;
+
+ if (src_fmt.format.width != cap->format.pix_mp.width ||
+ src_fmt.format.height != cap->format.pix_mp.height ||
+ src_fmt.format.code != cap->format.info->mbus_code) {
+ dev_err(cap->isp->dev,
+ "link %s: %u -> %s: %u not valid: 0x%04x/%ux%u not match 0x%04x/%ux%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ src_fmt.format.code, src_fmt.format.width,
+ src_fmt.format.height, cap->format.info->mbus_code,
+ cap->format.pix_mp.width, cap->format.pix_mp.height);
+
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations isp_cap_entity_ops = {
+ .link_validate = c3_isp_cap_link_validate,
+};
+
+static int c3_isp_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(q);
+ const struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp;
+ unsigned int i;
+
+ 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;
+
+ return 0;
+ }
+
+ *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 void c3_isp_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_cap_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_cap_buffer, vb);
+ struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue);
+
+ guard(spinlock_irqsave)(&cap->buff_lock);
+
+ list_add_tail(&buf->list, &cap->pending);
+}
+
+static int c3_isp_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size;
+
+ for (unsigned int i = 0; i < cap->format.pix_mp.num_planes; i++) {
+ size = cap->format.pix_mp.plane_fmt[i].sizeimage;
+ if (vb2_plane_size(vb, i) < size) {
+ dev_err(cap->isp->dev,
+ "User buffer too small (%ld < %lu)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static int c3_isp_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_cap_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_cap_buffer, vb);
+
+ for (unsigned int i = 0; i < cap->format.pix_mp.num_planes; i++)
+ buf->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ return 0;
+}
+
+static int c3_isp_vb2_start_streaming(struct vb2_queue *q,
+ unsigned int count)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(q);
+ int ret;
+
+ ret = video_device_pipeline_start(&cap->vdev, &cap->isp->pipe);
+ if (ret) {
+ dev_err(cap->isp->dev,
+ "Failed to start cap%u pipeline: %d\n", cap->id, ret);
+ goto err_return_buffers;
+ }
+
+ ret = c3_isp_cap_dummy_buff_create(cap);
+ if (ret)
+ goto err_pipeline_stop;
+
+ ret = pm_runtime_resume_and_get(cap->isp->dev);
+ if (ret)
+ goto err_dummy_destroy;
+
+ c3_isp_cap_start(cap);
+
+ ret = v4l2_subdev_enable_streams(&cap->rsz->sd, C3_ISP_RSZ_PAD_SOURCE,
+ BIT(0));
+ if (ret)
+ goto err_pm_put;
+
+ return 0;
+
+err_pm_put:
+ pm_runtime_put(cap->isp->dev);
+err_dummy_destroy:
+ c3_isp_cap_dummy_buff_destroy(cap);
+err_pipeline_stop:
+ video_device_pipeline_stop(&cap->vdev);
+err_return_buffers:
+ c3_isp_cap_return_buffers(cap, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void c3_isp_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(q);
+
+ c3_isp_cap_stop(cap);
+
+ c3_isp_cap_return_buffers(cap, VB2_BUF_STATE_ERROR);
+
+ v4l2_subdev_disable_streams(&cap->rsz->sd, C3_ISP_RSZ_PAD_SOURCE,
+ BIT(0));
+
+ pm_runtime_put(cap->isp->dev);
+
+ c3_isp_cap_dummy_buff_destroy(cap);
+
+ video_device_pipeline_stop(&cap->vdev);
+}
+
+static const struct vb2_ops isp_video_vb2_ops = {
+ .queue_setup = c3_isp_vb2_queue_setup,
+ .buf_queue = c3_isp_vb2_buf_queue,
+ .buf_prepare = c3_isp_vb2_buf_prepare,
+ .buf_init = c3_isp_vb2_buf_init,
+ .start_streaming = c3_isp_vb2_start_streaming,
+ .stop_streaming = c3_isp_vb2_stop_streaming,
+};
+
+static int c3_isp_register_capture(struct c3_isp_capture *cap)
+{
+ struct video_device *vdev = &cap->vdev;
+ struct vb2_queue *vb2_q = &cap->vb2_q;
+ int ret;
+
+ snprintf(vdev->name, sizeof(vdev->name), "c3-isp-cap%u", cap->id);
+ vdev->fops = &isp_cap_v4l2_fops;
+ vdev->ioctl_ops = &isp_cap_v4l2_ioctl_ops;
+ vdev->v4l2_dev = &cap->isp->v4l2_dev;
+ vdev->entity.ops = &isp_cap_entity_ops;
+ vdev->lock = &cap->lock;
+ vdev->minor = -1;
+ vdev->queue = vb2_q;
+ vdev->release = video_device_release_empty;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ video_set_drvdata(vdev, cap);
+
+ vb2_q->drv_priv = cap;
+ vb2_q->mem_ops = &vb2_dma_contig_memops;
+ vb2_q->ops = &isp_video_vb2_ops;
+ vb2_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ vb2_q->io_modes = VB2_DMABUF | VB2_MMAP;
+ vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2_q->buf_struct_size = sizeof(struct c3_isp_cap_buffer);
+ vb2_q->dev = cap->isp->dev;
+ vb2_q->lock = &cap->lock;
+
+ ret = vb2_queue_init(vb2_q);
+ if (ret)
+ goto err_destroy;
+
+ cap->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, &cap->pad);
+ if (ret)
+ goto err_queue_release;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(cap->isp->dev,
+ "Failed to register %s: %d\n", vdev->name, ret);
+ goto err_entity_cleanup;
+ }
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_queue_release:
+ vb2_queue_release(vb2_q);
+err_destroy:
+ mutex_destroy(&cap->lock);
+ return ret;
+}
+
+int c3_isp_captures_register(struct c3_isp_device *isp)
+{
+ int ret;
+ unsigned int i;
+ struct c3_isp_capture *cap;
+
+ for (i = C3_ISP_CAP_DEV_0; i < C3_ISP_NUM_CAP_DEVS; i++) {
+ cap = &isp->caps[i];
+ memset(cap, 0, sizeof(*cap));
+
+ cap->format.pix_mp.width = C3_ISP_DEFAULT_WIDTH;
+ cap->format.pix_mp.height = C3_ISP_DEFAULT_HEIGHT;
+ cap->format.pix_mp.field = V4L2_FIELD_NONE;
+ cap->format.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
+ cap->format.pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
+ cap->format.info =
+ c3_cap_find_fmt(cap->format.pix_mp.pixelformat);
+
+ c3_cap_try_fmt(&cap->format.pix_mp);
+
+ cap->id = i;
+ cap->rsz = &isp->resizers[i];
+ cap->isp = isp;
+ INIT_LIST_HEAD(&cap->pending);
+ spin_lock_init(&cap->buff_lock);
+ mutex_init(&cap->lock);
+
+ ret = c3_isp_register_capture(cap);
+ if (ret) {
+ cap->isp = NULL;
+ mutex_destroy(&cap->lock);
+ c3_isp_captures_unregister(isp);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void c3_isp_captures_unregister(struct c3_isp_device *isp)
+{
+ unsigned int i;
+ struct c3_isp_capture *cap;
+
+ for (i = C3_ISP_CAP_DEV_0; i < C3_ISP_NUM_CAP_DEVS; i++) {
+ cap = &isp->caps[i];
+
+ if (!cap->isp)
+ continue;
+ vb2_queue_release(&cap->vb2_q);
+ media_entity_cleanup(&cap->vdev.entity);
+ video_unregister_device(&cap->vdev);
+ mutex_destroy(&cap->lock);
+ }
+}
+
+void c3_isp_captures_isr(struct c3_isp_device *isp)
+{
+ c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_0]);
+ c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_1]);
+ c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_2]);
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h b/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h
new file mode 100644
index 000000000000..cb470802e61e
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef __C3_ISP_COMMON_H__
+#define __C3_ISP_COMMON_H__
+
+#include <linux/clk.h>
+
+#include <media/media-device.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-v4l2.h>
+
+#define C3_ISP_DRIVER_NAME "c3-isp"
+#define C3_ISP_CLOCK_NUM_MAX 3
+
+#define C3_ISP_DEFAULT_WIDTH 1920
+#define C3_ISP_DEFAULT_HEIGHT 1080
+#define C3_ISP_MAX_WIDTH 2888
+#define C3_ISP_MAX_HEIGHT 2240
+#define C3_ISP_MIN_WIDTH 160
+#define C3_ISP_MIN_HEIGHT 120
+
+#define C3_ISP_DMA_SIZE_ALIGN_BYTES 16
+
+enum c3_isp_core_pads {
+ C3_ISP_CORE_PAD_SINK_VIDEO,
+ C3_ISP_CORE_PAD_SINK_PARAMS,
+ C3_ISP_CORE_PAD_SOURCE_STATS,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_0,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_1,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_2,
+ C3_ISP_CORE_PAD_MAX
+};
+
+enum c3_isp_resizer_ids {
+ C3_ISP_RSZ_0,
+ C3_ISP_RSZ_1,
+ C3_ISP_RSZ_2,
+ C3_ISP_NUM_RSZ
+};
+
+enum c3_isp_resizer_pads {
+ C3_ISP_RSZ_PAD_SINK,
+ C3_ISP_RSZ_PAD_SOURCE,
+ C3_ISP_RSZ_PAD_MAX
+};
+
+enum c3_isp_cap_devs {
+ C3_ISP_CAP_DEV_0,
+ C3_ISP_CAP_DEV_1,
+ C3_ISP_CAP_DEV_2,
+ C3_ISP_NUM_CAP_DEVS
+};
+
+enum c3_isp_planes {
+ C3_ISP_PLANE_Y,
+ C3_ISP_PLANE_UV,
+ C3_ISP_NUM_PLANES
+};
+
+/*
+ * struct c3_isp_cap_format_info - The image format of capture device
+ *
+ * @mbus_code: the mbus code
+ * @fourcc: the pixel format
+ * @format: defines the output format of hardware
+ * @planes: defines the mutil plane of hardware
+ * @ch0_pix_bits: defines the channel 0 pixel bits mode of hardware
+ * @uv_swap: defines the uv swap flag of hardware
+ * @in_bits: defines the input bits of hardware
+ * @hdiv: horizontal chroma subsampling factor of hardware
+ * @vdiv: vertical chroma subsampling factor of hardware
+ */
+struct c3_isp_cap_format_info {
+ u32 mbus_code;
+ u32 fourcc;
+ u32 format;
+ u32 planes;
+ u32 ch0_pix_bits;
+ u8 uv_swap;
+ u8 in_bits;
+ u8 hdiv;
+ u8 vdiv;
+};
+
+/*
+ * struct c3_isp_cap_buffer - A container of vb2 buffer used by the video
+ * devices: capture video devices
+ *
+ * @vb: vb2 buffer
+ * @dma_addr: buffer physical address
+ * @list: entry of the buffer in the queue
+ */
+struct c3_isp_cap_buffer {
+ struct vb2_v4l2_buffer vb;
+ dma_addr_t dma_addr[C3_ISP_NUM_PLANES];
+ struct list_head list;
+};
+
+/*
+ * struct c3_isp_stats_dma_buffer - A container of vb2 buffer used by the video
+ * devices: stats video devices
+ *
+ * @vb: vb2 buffer
+ * @dma_addr: buffer physical address
+ * @list: entry of the buffer in the queue
+ */
+struct c3_isp_stats_buffer {
+ struct vb2_v4l2_buffer vb;
+ dma_addr_t dma_addr;
+ struct list_head list;
+};
+
+/*
+ * struct c3_isp_params_buffer - A container of vb2 buffer used by the
+ * params video device
+ *
+ * @vb: vb2 buffer
+ * @cfg: scratch buffer used for caching the ISP configuration parameters
+ * @list: entry of the buffer in the queue
+ */
+struct c3_isp_params_buffer {
+ struct vb2_v4l2_buffer vb;
+ void *cfg;
+ struct list_head list;
+};
+
+/*
+ * struct c3_isp_dummy_buffer - A buffer to write the next frame to in case
+ * there are no vb2 buffers available.
+ *
+ * @vaddr: return value of call to dma_alloc_attrs
+ * @dma_addr: dma address of the buffer
+ * @size: size of the buffer
+ */
+struct c3_isp_dummy_buffer {
+ void *vaddr;
+ dma_addr_t dma_addr;
+ u32 size;
+};
+
+/*
+ * struct c3_isp_core - ISP core subdev
+ *
+ * @sd: ISP sub-device
+ * @pads: ISP sub-device pads
+ * @src_pad: source sub-device pad
+ * @isp: pointer to c3_isp_device
+ */
+struct c3_isp_core {
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_ISP_CORE_PAD_MAX];
+ struct media_pad *src_pad;
+ struct c3_isp_device *isp;
+};
+
+/*
+ * struct c3_isp_resizer - ISP resizer subdev
+ *
+ * @id: resizer id
+ * @sd: resizer sub-device
+ * @pads: resizer sub-device pads
+ * @src_sd: source sub-device
+ * @isp: pointer to c3_isp_device
+ * @src_pad: the pad of source sub-device
+ */
+struct c3_isp_resizer {
+ enum c3_isp_resizer_ids id;
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_ISP_RSZ_PAD_MAX];
+ struct v4l2_subdev *src_sd;
+ struct c3_isp_device *isp;
+ u32 src_pad;
+};
+
+/*
+ * struct c3_isp_stats - ISP statistics device
+ *
+ * @vb2_q: vb2 buffer queue
+ * @vdev: video node
+ * @vfmt: v4l2_format of the metadata format
+ * @pad: media pad
+ * @lock: protects vb2_q, vdev
+ * @isp: pointer to c3_isp_device
+ * @buff: in use buffer
+ * @buff_lock: protects stats buffer
+ * @pending: stats buffer list head
+ */
+struct c3_isp_stats {
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct v4l2_format vfmt;
+ struct media_pad pad;
+
+ struct mutex lock; /* Protects vb2_q, vdev */
+ struct c3_isp_device *isp;
+
+ struct c3_isp_stats_buffer *buff;
+ spinlock_t buff_lock; /* Protects stats buffer */
+ struct list_head pending;
+};
+
+/*
+ * struct c3_isp_params - ISP parameters device
+ *
+ * @vb2_q: vb2 buffer queue
+ * @vdev: video node
+ * @vfmt: v4l2_format of the metadata format
+ * @pad: media pad
+ * @lock: protects vb2_q, vdev
+ * @isp: pointer to c3_isp_device
+ * @buff: in use buffer
+ * @buff_lock: protects stats buffer
+ * @pending: stats buffer list head
+ */
+struct c3_isp_params {
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct v4l2_format vfmt;
+ struct media_pad pad;
+
+ struct mutex lock; /* Protects vb2_q, vdev */
+ struct c3_isp_device *isp;
+
+ struct c3_isp_params_buffer *buff;
+ spinlock_t buff_lock; /* Protects params buffer */
+ struct list_head pending;
+};
+
+/*
+ * struct c3_isp_capture - ISP capture device
+ *
+ * @id: capture device ID
+ * @vb2_q: vb2 buffer queue
+ * @vdev: video node
+ * @pad: media pad
+ * @lock: protects vb2_q, vdev
+ * @isp: pointer to c3_isp_device
+ * @rsz: pointer to c3_isp_resizer
+ * @buff: in use buffer
+ * @buff_lock: protects capture buffer
+ * @pending: capture buffer list head
+ * @format.info: a pointer to the c3_isp_capture_format of the pixel format
+ * @format.fmt: buffer format
+ */
+struct c3_isp_capture {
+ enum c3_isp_cap_devs id;
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct media_pad pad;
+
+ struct mutex lock; /* Protects vb2_q, vdev */
+ struct c3_isp_device *isp;
+ struct c3_isp_resizer *rsz;
+
+ struct c3_isp_dummy_buffer dummy_buff;
+ struct c3_isp_cap_buffer *buff;
+ spinlock_t buff_lock; /* Protects stream buffer */
+ struct list_head pending;
+ struct {
+ const struct c3_isp_cap_format_info *info;
+ struct v4l2_pix_format_mplane pix_mp;
+ } format;
+};
+
+/**
+ * struct c3_isp_info - ISP information
+ *
+ * @clocks: array of ISP clock names
+ * @clock_num: actual clock number
+ */
+struct c3_isp_info {
+ char *clocks[C3_ISP_CLOCK_NUM_MAX];
+ u32 clock_num;
+};
+
+/**
+ * struct c3_isp_device - ISP platform device
+ *
+ * @dev: pointer to the struct device
+ * @base: base register address
+ * @clks: array of clocks
+ * @notifier: notifier to register on the v4l2-async API
+ * @v4l2_dev: v4l2_device variable
+ * @media_dev: media device variable
+ * @pipe: media pipeline
+ * @core: ISP core subdev
+ * @resizers: ISP resizer subdev
+ * @stats: ISP stats device
+ * @params: ISP params device
+ * @caps: array of ISP capture device
+ * @frm_sequence: used to record frame id
+ * @info: version-specific ISP information
+ */
+struct c3_isp_device {
+ struct device *dev;
+ void __iomem *base;
+ struct clk_bulk_data clks[C3_ISP_CLOCK_NUM_MAX];
+
+ struct v4l2_async_notifier notifier;
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+ struct media_pipeline pipe;
+
+ struct c3_isp_core core;
+ struct c3_isp_resizer resizers[C3_ISP_NUM_RSZ];
+ struct c3_isp_stats stats;
+ struct c3_isp_params params;
+ struct c3_isp_capture caps[C3_ISP_NUM_CAP_DEVS];
+
+ u32 frm_sequence;
+ const struct c3_isp_info *info;
+};
+
+u32 c3_isp_read(struct c3_isp_device *isp, u32 reg);
+void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val);
+void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val);
+
+void c3_isp_core_queue_sof(struct c3_isp_device *isp);
+int c3_isp_core_register(struct c3_isp_device *isp);
+void c3_isp_core_unregister(struct c3_isp_device *isp);
+int c3_isp_resizers_register(struct c3_isp_device *isp);
+void c3_isp_resizers_unregister(struct c3_isp_device *isp);
+int c3_isp_captures_register(struct c3_isp_device *isp);
+void c3_isp_captures_unregister(struct c3_isp_device *isp);
+void c3_isp_captures_isr(struct c3_isp_device *isp);
+void c3_isp_stats_pre_cfg(struct c3_isp_device *isp);
+int c3_isp_stats_register(struct c3_isp_device *isp);
+void c3_isp_stats_unregister(struct c3_isp_device *isp);
+void c3_isp_stats_isr(struct c3_isp_device *isp);
+void c3_isp_params_pre_cfg(struct c3_isp_device *isp);
+int c3_isp_params_register(struct c3_isp_device *isp);
+void c3_isp_params_unregister(struct c3_isp_device *isp);
+void c3_isp_params_isr(struct c3_isp_device *isp);
+
+#endif
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c
new file mode 100644
index 000000000000..ff6413fff889
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/media/amlogic/c3-isp-config.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-event.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+#define C3_ISP_CORE_SUBDEV_NAME "c3-isp-core"
+
+#define C3_ISP_PHASE_OFFSET_0 0
+#define C3_ISP_PHASE_OFFSET_1 1
+#define C3_ISP_PHASE_OFFSET_NONE 0xff
+
+#define C3_ISP_CORE_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+#define C3_ISP_CORE_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUV10_1X30
+
+/*
+ * struct c3_isp_core_format_info - ISP core format information
+ *
+ * @mbus_code: the mbus code
+ * @pads: bitmask detailing valid pads for this mbus_code
+ * @xofst: horizontal phase offset of hardware
+ * @yofst: vertical phase offset of hardware
+ * @is_raw: the raw format flag of mbus code
+ */
+struct c3_isp_core_format_info {
+ u32 mbus_code;
+ u32 pads;
+ u8 xofst;
+ u8 yofst;
+ bool is_raw;
+};
+
+static const struct c3_isp_core_format_info c3_isp_core_fmts[] = {
+ /* RAW formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ },
+ /* YUV formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) |
+ BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) |
+ BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = false,
+ },
+};
+
+static const struct c3_isp_core_format_info
+*core_find_format_by_code(u32 code, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) {
+ const struct c3_isp_core_format_info *info =
+ &c3_isp_core_fmts[i];
+
+ if (info->mbus_code == code && info->pads & BIT(pad))
+ return info;
+ }
+
+ return NULL;
+}
+
+static const struct c3_isp_core_format_info
+*core_find_format_by_index(u32 index, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) {
+ const struct c3_isp_core_format_info *info =
+ &c3_isp_core_fmts[i];
+
+ if (!(info->pads & BIT(pad)))
+ continue;
+
+ if (!index)
+ return info;
+
+ index--;
+ }
+
+ return NULL;
+}
+
+static void c3_isp_core_enable(struct c3_isp_device *isp)
+{
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK,
+ ISP_TOP_IRQ_EN_FRM_END_EN);
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK,
+ ISP_TOP_IRQ_EN_FRM_RST_EN);
+
+ /* Enable image data to ISP core */
+ c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK,
+ ISP_TOP_PATH_SEL_CORE_MIPI_CORE);
+}
+
+static void c3_isp_core_disable(struct c3_isp_device *isp)
+{
+ /* Disable image data to ISP core */
+ c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK,
+ ISP_TOP_PATH_SEL_CORE_CORE_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK,
+ ISP_TOP_IRQ_EN_FRM_END_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK,
+ ISP_TOP_IRQ_EN_FRM_RST_DIS);
+}
+
+/* Set the phase offset of blc, wb and lns */
+static void c3_isp_core_lswb_ofst(struct c3_isp_device *isp,
+ u8 xofst, u8 yofst)
+{
+ c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST,
+ ISP_LSWB_BLC_PHSOFST_HORIZ_OFST_MASK,
+ ISP_LSWB_BLC_PHSOFST_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST,
+ ISP_LSWB_BLC_PHSOFST_VERT_OFST_MASK,
+ ISP_LSWB_BLC_PHSOFST_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST,
+ ISP_LSWB_WB_PHSOFST_HORIZ_OFST_MASK,
+ ISP_LSWB_WB_PHSOFST_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST,
+ ISP_LSWB_WB_PHSOFST_VERT_OFST_MASK,
+ ISP_LSWB_WB_PHSOFST_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST,
+ ISP_LSWB_LNS_PHSOFST_HORIZ_OFST_MASK,
+ ISP_LSWB_LNS_PHSOFST_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST,
+ ISP_LSWB_LNS_PHSOFST_VERT_OFST_MASK,
+ ISP_LSWB_LNS_PHSOFST_VERT_OFST(yofst));
+}
+
+/* Set the phase offset of af, ae and awb */
+static void c3_isp_core_3a_ofst(struct c3_isp_device *isp,
+ u8 xofst, u8 yofst)
+{
+ c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_HORIZ_OFST_MASK,
+ ISP_AF_CTRL_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_VERT_OFST_MASK,
+ ISP_AF_CTRL_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_HORIZ_OFST_MASK,
+ ISP_AE_CTRL_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_VERT_OFST_MASK,
+ ISP_AE_CTRL_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_HORIZ_OFST_MASK,
+ ISP_AWB_CTRL_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_VERT_OFST_MASK,
+ ISP_AWB_CTRL_VERT_OFST(yofst));
+}
+
+/* Set the phase offset of demosaic */
+static void c3_isp_core_dms_ofst(struct c3_isp_device *isp,
+ u8 xofst, u8 yofst)
+{
+ c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0,
+ ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST_MASK,
+ ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0,
+ ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST_MASK,
+ ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST(yofst));
+}
+
+static void c3_isp_core_cfg_format(struct c3_isp_device *isp,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ const struct c3_isp_core_format_info *isp_fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO);
+ isp_fmt = core_find_format_by_code(fmt->code,
+ C3_ISP_CORE_PAD_SINK_VIDEO);
+
+ c3_isp_write(isp, ISP_TOP_INPUT_SIZE,
+ ISP_TOP_INPUT_SIZE_HORIZ_SIZE(fmt->width) |
+ ISP_TOP_INPUT_SIZE_VERT_SIZE(fmt->height));
+ c3_isp_write(isp, ISP_TOP_FRM_SIZE,
+ ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE(fmt->width) |
+ ISP_TOP_FRM_SIZE_CORE_VERT_SIZE(fmt->height));
+
+ c3_isp_update_bits(isp, ISP_TOP_HOLD_SIZE,
+ ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE_MASK,
+ ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE(fmt->width));
+
+ c3_isp_write(isp, ISP_AF_HV_SIZE,
+ ISP_AF_HV_SIZE_GLB_WIN_XSIZE(fmt->width) |
+ ISP_AF_HV_SIZE_GLB_WIN_YSIZE(fmt->height));
+ c3_isp_write(isp, ISP_AE_HV_SIZE,
+ ISP_AE_HV_SIZE_HORIZ_SIZE(fmt->width) |
+ ISP_AE_HV_SIZE_VERT_SIZE(fmt->height));
+ c3_isp_write(isp, ISP_AWB_HV_SIZE,
+ ISP_AWB_HV_SIZE_HORIZ_SIZE(fmt->width) |
+ ISP_AWB_HV_SIZE_VERT_SIZE(fmt->height));
+
+ c3_isp_core_lswb_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
+ c3_isp_core_3a_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
+ c3_isp_core_dms_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
+}
+
+static bool c3_isp_core_streams_ready(struct c3_isp_core *core)
+{
+ unsigned int n_links = 0;
+ struct media_link *link;
+
+ for_each_media_entity_data_link(&core->sd.entity, link) {
+ if ((link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 ||
+ link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 ||
+ link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_2) &&
+ link->flags == MEDIA_LNK_FL_ENABLED)
+ n_links++;
+ }
+
+ return n_links == core->isp->pipe.start_count;
+}
+
+static int c3_isp_core_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_core *core = v4l2_get_subdevdata(sd);
+ struct media_pad *sink_pad;
+ struct v4l2_subdev *src_sd;
+ int ret;
+
+ if (!c3_isp_core_streams_ready(core))
+ return 0;
+
+ core->isp->frm_sequence = 0;
+ c3_isp_core_cfg_format(core->isp, state);
+ c3_isp_core_enable(core->isp);
+
+ sink_pad = &core->pads[C3_ISP_CORE_PAD_SINK_VIDEO];
+ core->src_pad = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(core->src_pad)) {
+ dev_dbg(core->isp->dev,
+ "Failed to get source pad for ISP core\n");
+ return -EPIPE;
+ }
+
+ src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity);
+
+ ret = v4l2_subdev_enable_streams(src_sd, core->src_pad->index, BIT(0));
+ if (ret) {
+ c3_isp_core_disable(core->isp);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int c3_isp_core_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_core *core = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd;
+
+ if (core->isp->pipe.start_count != 1)
+ return 0;
+
+ if (core->src_pad) {
+ src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity);
+ v4l2_subdev_disable_streams(src_sd, core->src_pad->index,
+ BIT(0));
+ }
+ core->src_pad = NULL;
+
+ c3_isp_core_disable(core->isp);
+
+ return 0;
+}
+
+static int c3_isp_core_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct c3_isp_core_format_info *info;
+
+ switch (code->pad) {
+ case C3_ISP_CORE_PAD_SINK_VIDEO:
+ case C3_ISP_CORE_PAD_SOURCE_VIDEO_0:
+ case C3_ISP_CORE_PAD_SOURCE_VIDEO_1:
+ case C3_ISP_CORE_PAD_SOURCE_VIDEO_2:
+ info = core_find_format_by_index(code->index, code->pad);
+ if (!info)
+ return -EINVAL;
+
+ code->code = info->mbus_code;
+
+ break;
+ case C3_ISP_CORE_PAD_SINK_PARAMS:
+ case C3_ISP_CORE_PAD_SOURCE_STATS:
+ if (code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void c3_isp_core_set_sink_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ const struct c3_isp_core_format_info *isp_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+ isp_fmt = core_find_format_by_code(format->format.code, format->pad);
+ if (!isp_fmt)
+ sink_fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT;
+ else
+ sink_fmt->code = format->format.code;
+
+ sink_fmt->width = clamp_t(u32, format->format.width,
+ C3_ISP_MIN_WIDTH, C3_ISP_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, format->format.height,
+ C3_ISP_MIN_HEIGHT, C3_ISP_MAX_HEIGHT);
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0;
+ i < C3_ISP_CORE_PAD_MAX; i++) {
+ src_fmt = v4l2_subdev_state_get_format(state, i);
+
+ src_fmt->width = sink_fmt->width;
+ src_fmt->height = sink_fmt->height;
+ }
+
+ format->format = *sink_fmt;
+}
+
+static void c3_isp_core_set_source_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ const struct c3_isp_core_format_info *isp_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_mbus_framefmt *sink_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ C3_ISP_CORE_PAD_SINK_VIDEO);
+ src_fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+ isp_fmt = core_find_format_by_code(format->format.code, format->pad);
+ if (!isp_fmt)
+ src_fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT;
+ else
+ src_fmt->code = format->format.code;
+
+ src_fmt->width = sink_fmt->width;
+ src_fmt->height = sink_fmt->height;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ if (isp_fmt && isp_fmt->is_raw) {
+ src_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ } else {
+ src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ format->format = *src_fmt;
+}
+
+static int c3_isp_core_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ if (format->pad == C3_ISP_CORE_PAD_SINK_VIDEO)
+ c3_isp_core_set_sink_fmt(state, format);
+ else if (format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 ||
+ format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 ||
+ format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_2)
+ c3_isp_core_set_source_fmt(state, format);
+ else
+ format->format =
+ *v4l2_subdev_state_get_format(state, format->pad);
+
+ return 0;
+}
+
+static int c3_isp_core_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ /* Video sink pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO);
+ fmt->width = C3_ISP_DEFAULT_WIDTH;
+ fmt->height = C3_ISP_DEFAULT_HEIGHT;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ /* Video source pad */
+ for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0;
+ i < C3_ISP_CORE_PAD_MAX; i++) {
+ fmt = v4l2_subdev_state_get_format(state, i);
+ fmt->width = C3_ISP_DEFAULT_WIDTH;
+ fmt->height = C3_ISP_DEFAULT_HEIGHT;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ /* Parameters pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_PARAMS);
+ fmt->width = 0;
+ fmt->height = 0;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ /* Statistics pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SOURCE_STATS);
+ fmt->width = 0;
+ fmt->height = 0;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ return 0;
+}
+
+static int c3_isp_core_subscribe_event(struct v4l2_subdev *sd,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* V4L2_EVENT_FRAME_SYNC doesn't need id, so should set 0 */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
+static const struct v4l2_subdev_pad_ops c3_isp_core_pad_ops = {
+ .enum_mbus_code = c3_isp_core_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_isp_core_set_fmt,
+ .enable_streams = c3_isp_core_enable_streams,
+ .disable_streams = c3_isp_core_disable_streams,
+};
+
+static const struct v4l2_subdev_core_ops c3_isp_core_core_ops = {
+ .subscribe_event = c3_isp_core_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops c3_isp_core_subdev_ops = {
+ .core = &c3_isp_core_core_ops,
+ .pad = &c3_isp_core_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_isp_core_internal_ops = {
+ .init_state = c3_isp_core_init_state,
+};
+
+static int c3_isp_core_link_validate(struct media_link *link)
+{
+ if (link->sink->index == C3_ISP_CORE_PAD_SINK_PARAMS)
+ return 0;
+
+ return v4l2_subdev_link_validate(link);
+}
+
+/* Media entity operations */
+static const struct media_entity_operations c3_isp_core_entity_ops = {
+ .link_validate = c3_isp_core_link_validate,
+};
+
+void c3_isp_core_queue_sof(struct c3_isp_device *isp)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+
+ event.u.frame_sync.frame_sequence = isp->frm_sequence;
+ v4l2_event_queue(isp->core.sd.devnode, &event);
+}
+
+int c3_isp_core_register(struct c3_isp_device *isp)
+{
+ struct c3_isp_core *core = &isp->core;
+ struct v4l2_subdev *sd = &core->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_isp_core_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->internal_ops = &c3_isp_core_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "%s", C3_ISP_CORE_SUBDEV_NAME);
+
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->entity.ops = &c3_isp_core_entity_ops;
+
+ core->isp = isp;
+ sd->dev = isp->dev;
+ v4l2_set_subdevdata(sd, core);
+
+ core->pads[C3_ISP_CORE_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK;
+ core->pads[C3_ISP_CORE_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_0].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_1].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_2].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_ISP_CORE_PAD_MAX,
+ core->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(&isp->v4l2_dev, sd);
+ if (ret)
+ goto err_subdev_cleanup;
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+void c3_isp_core_unregister(struct c3_isp_device *isp)
+{
+ struct c3_isp_core *core = &isp->core;
+ struct v4l2_subdev *sd = &core->sd;
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c
new file mode 100644
index 000000000000..c3b779f63088
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+u32 c3_isp_read(struct c3_isp_device *isp, u32 reg)
+{
+ return readl(isp->base + reg);
+}
+
+void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val)
+{
+ writel(val, isp->base + reg);
+}
+
+void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val)
+{
+ u32 orig, tmp;
+
+ orig = c3_isp_read(isp, reg);
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ if (tmp != orig)
+ c3_isp_write(isp, reg, tmp);
+}
+
+/* PM runtime suspend */
+static int c3_isp_runtime_suspend(struct device *dev)
+{
+ struct c3_isp_device *isp = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(isp->info->clock_num, isp->clks);
+
+ return 0;
+}
+
+/* PM runtime resume */
+static int c3_isp_runtime_resume(struct device *dev)
+{
+ struct c3_isp_device *isp = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(isp->info->clock_num, isp->clks);
+}
+
+static const struct dev_pm_ops c3_isp_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ RUNTIME_PM_OPS(c3_isp_runtime_suspend,
+ c3_isp_runtime_resume, NULL)
+};
+
+/* IRQ handling */
+static irqreturn_t c3_isp_irq_handler(int irq, void *dev)
+{
+ struct c3_isp_device *isp = dev;
+ u32 status;
+
+ /* Get irq status and clear irq status */
+ status = c3_isp_read(isp, ISP_TOP_RO_IRQ_STAT);
+ c3_isp_write(isp, ISP_TOP_IRQ_CLR, status);
+
+ if (status & ISP_TOP_RO_IRQ_STAT_FRM_END_MASK) {
+ c3_isp_stats_isr(isp);
+ c3_isp_params_isr(isp);
+ c3_isp_captures_isr(isp);
+ isp->frm_sequence++;
+ }
+
+ if (status & ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK)
+ c3_isp_core_queue_sof(isp);
+
+ return IRQ_HANDLED;
+}
+
+/* Subdev notifier register */
+static int c3_isp_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct c3_isp_device *isp =
+ container_of(notifier, struct c3_isp_device, notifier);
+ struct media_pad *sink =
+ &isp->core.sd.entity.pads[C3_ISP_CORE_PAD_SINK_VIDEO];
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static int c3_isp_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct c3_isp_device *isp =
+ container_of(notifier, struct c3_isp_device, notifier);
+
+ return v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations c3_isp_notify_ops = {
+ .bound = c3_isp_notify_bound,
+ .complete = c3_isp_notify_complete,
+};
+
+static int c3_isp_async_nf_register(struct c3_isp_device *isp)
+{
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ asc = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep,
+ struct v4l2_async_connection);
+ fwnode_handle_put(ep);
+
+ if (IS_ERR(asc))
+ return PTR_ERR(asc);
+
+ isp->notifier.ops = &c3_isp_notify_ops;
+ ret = v4l2_async_nf_register(&isp->notifier);
+ if (ret)
+ v4l2_async_nf_cleanup(&isp->notifier);
+
+ return ret;
+}
+
+static void c3_isp_async_nf_unregister(struct c3_isp_device *isp)
+{
+ v4l2_async_nf_unregister(&isp->notifier);
+ v4l2_async_nf_cleanup(&isp->notifier);
+}
+
+static int c3_isp_media_register(struct c3_isp_device *isp)
+{
+ struct media_device *media_dev = &isp->media_dev;
+ struct v4l2_device *v4l2_dev = &isp->v4l2_dev;
+ int ret;
+
+ /* Initialize media device */
+ strscpy(media_dev->model, C3_ISP_DRIVER_NAME, sizeof(media_dev->model));
+ media_dev->dev = isp->dev;
+
+ media_device_init(media_dev);
+
+ /* Initialize v4l2 device */
+ v4l2_dev->mdev = media_dev;
+ strscpy(v4l2_dev->name, C3_ISP_DRIVER_NAME, sizeof(v4l2_dev->name));
+
+ ret = v4l2_device_register(isp->dev, v4l2_dev);
+ if (ret)
+ goto err_media_dev_cleanup;
+
+ ret = media_device_register(&isp->media_dev);
+ if (ret) {
+ dev_err(isp->dev, "Failed to register media device: %d\n", ret);
+ goto err_unreg_v4l2_dev;
+ }
+
+ return 0;
+
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&isp->v4l2_dev);
+err_media_dev_cleanup:
+ media_device_cleanup(media_dev);
+ return ret;
+}
+
+static void c3_isp_media_unregister(struct c3_isp_device *isp)
+{
+ media_device_unregister(&isp->media_dev);
+ v4l2_device_unregister(&isp->v4l2_dev);
+ media_device_cleanup(&isp->media_dev);
+}
+
+static void c3_isp_remove_links(struct c3_isp_device *isp)
+{
+ unsigned int i;
+
+ media_entity_remove_links(&isp->core.sd.entity);
+
+ for (i = 0; i < C3_ISP_NUM_RSZ; i++)
+ media_entity_remove_links(&isp->resizers[i].sd.entity);
+
+ for (i = 0; i < C3_ISP_NUM_CAP_DEVS; i++)
+ media_entity_remove_links(&isp->caps[i].vdev.entity);
+}
+
+static int c3_isp_create_links(struct c3_isp_device *isp)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < C3_ISP_NUM_RSZ; i++) {
+ ret = media_create_pad_link(&isp->resizers[i].sd.entity,
+ C3_ISP_RSZ_PAD_SOURCE,
+ &isp->caps[i].vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err(isp->dev,
+ "Failed to link rsz %u and cap %u\n", i, i);
+ goto err_remove_links;
+ }
+
+ ret = media_create_pad_link(&isp->core.sd.entity,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i,
+ &isp->resizers[i].sd.entity,
+ C3_ISP_RSZ_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(isp->dev,
+ "Failed to link core and rsz %u\n", i);
+ goto err_remove_links;
+ }
+ }
+
+ ret = media_create_pad_link(&isp->core.sd.entity,
+ C3_ISP_CORE_PAD_SOURCE_STATS,
+ &isp->stats.vdev.entity,
+ 0, MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(isp->dev, "Failed to link core and stats\n");
+ goto err_remove_links;
+ }
+
+ ret = media_create_pad_link(&isp->params.vdev.entity, 0,
+ &isp->core.sd.entity,
+ C3_ISP_CORE_PAD_SINK_PARAMS,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(isp->dev, "Failed to link params and core\n");
+ goto err_remove_links;
+ }
+
+ return 0;
+
+err_remove_links:
+ c3_isp_remove_links(isp);
+ return ret;
+}
+
+static int c3_isp_videos_register(struct c3_isp_device *isp)
+{
+ int ret;
+
+ ret = c3_isp_captures_register(isp);
+ if (ret)
+ return ret;
+
+ ret = c3_isp_stats_register(isp);
+ if (ret)
+ goto err_captures_unregister;
+
+ ret = c3_isp_params_register(isp);
+ if (ret)
+ goto err_stats_unregister;
+
+ ret = c3_isp_create_links(isp);
+ if (ret)
+ goto err_params_unregister;
+
+ return 0;
+
+err_params_unregister:
+ c3_isp_params_unregister(isp);
+err_stats_unregister:
+ c3_isp_stats_unregister(isp);
+err_captures_unregister:
+ c3_isp_captures_unregister(isp);
+ return ret;
+}
+
+static void c3_isp_videos_unregister(struct c3_isp_device *isp)
+{
+ c3_isp_remove_links(isp);
+ c3_isp_params_unregister(isp);
+ c3_isp_stats_unregister(isp);
+ c3_isp_captures_unregister(isp);
+}
+
+static int c3_isp_get_clocks(struct c3_isp_device *isp)
+{
+ const struct c3_isp_info *info = isp->info;
+
+ for (unsigned int i = 0; i < info->clock_num; i++)
+ isp->clks[i].id = info->clocks[i];
+
+ return devm_clk_bulk_get(isp->dev, info->clock_num, isp->clks);
+}
+
+static int c3_isp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct c3_isp_device *isp;
+ int irq;
+ int ret;
+
+ isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
+ if (!isp)
+ return -ENOMEM;
+
+ isp->info = of_device_get_match_data(dev);
+ isp->dev = dev;
+
+ isp->base = devm_platform_ioremap_resource_byname(pdev, "isp");
+ if (IS_ERR(isp->base))
+ return dev_err_probe(dev, PTR_ERR(isp->base),
+ "Failed to ioremap resource\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = c3_isp_get_clocks(isp);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ platform_set_drvdata(pdev, isp);
+
+ pm_runtime_enable(dev);
+
+ ret = c3_isp_media_register(isp);
+ if (ret)
+ goto err_runtime_disable;
+
+ ret = c3_isp_core_register(isp);
+ if (ret)
+ goto err_v4l2_unregister;
+
+ ret = c3_isp_resizers_register(isp);
+ if (ret)
+ goto err_core_unregister;
+
+ ret = c3_isp_async_nf_register(isp);
+ if (ret)
+ goto err_resizers_unregister;
+
+ ret = devm_request_irq(dev, irq,
+ c3_isp_irq_handler, IRQF_SHARED,
+ dev_driver_string(dev), isp);
+ if (ret)
+ goto err_nf_unregister;
+
+ ret = c3_isp_videos_register(isp);
+ if (ret)
+ goto err_nf_unregister;
+
+ return 0;
+
+err_nf_unregister:
+ c3_isp_async_nf_unregister(isp);
+err_resizers_unregister:
+ c3_isp_resizers_unregister(isp);
+err_core_unregister:
+ c3_isp_core_unregister(isp);
+err_v4l2_unregister:
+ c3_isp_media_unregister(isp);
+err_runtime_disable:
+ pm_runtime_disable(dev);
+ return ret;
+};
+
+static void c3_isp_remove(struct platform_device *pdev)
+{
+ struct c3_isp_device *isp = platform_get_drvdata(pdev);
+
+ c3_isp_videos_unregister(isp);
+ c3_isp_async_nf_unregister(isp);
+ c3_isp_core_unregister(isp);
+ c3_isp_resizers_unregister(isp);
+ c3_isp_media_unregister(isp);
+ pm_runtime_disable(isp->dev);
+};
+
+static const struct c3_isp_info isp_info = {
+ .clocks = {"vapb", "isp0"},
+ .clock_num = 2
+};
+
+static const struct of_device_id c3_isp_of_match[] = {
+ { .compatible = "amlogic,c3-isp",
+ .data = &isp_info },
+ { },
+};
+MODULE_DEVICE_TABLE(of, c3_isp_of_match);
+
+static struct platform_driver c3_isp_driver = {
+ .probe = c3_isp_probe,
+ .remove = c3_isp_remove,
+ .driver = {
+ .name = "c3-isp",
+ .of_match_table = c3_isp_of_match,
+ .pm = pm_ptr(&c3_isp_pm_ops),
+ },
+};
+
+module_platform_driver(c3_isp_driver);
+
+MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic C3 ISP pipeline");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
new file mode 100644
index 000000000000..c80667dd7662
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
@@ -0,0 +1,1008 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/cleanup.h>
+#include <linux/media/amlogic/c3-isp-config.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+/*
+ * union c3_isp_params_block - Generalisation of a parameter block
+ *
+ * This union allows the driver to treat a block as a generic struct to this
+ * union and safely access the header and block-specific struct without having
+ * to resort to casting. The header member is accessed first, and the type field
+ * checked which allows the driver to determine which of the other members
+ * should be used.
+ *
+ * @header: The shared header struct embedded as the first member
+ * of all the possible other members. This member would be
+ * accessed first and the type field checked to determine
+ * which of the other members should be accessed.
+ * @awb_gains: For header.type == C3_ISP_PARAMS_BLOCK_AWB_GAINS
+ * @awb_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AWB_CONFIG
+ * @ae_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AE_CONFIG
+ * @af_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AF_CONFIG
+ * @pst_gamma: For header.type == C3_ISP_PARAMS_BLOCK_PST_GAMMA
+ * @ccm: For header.type == C3_ISP_PARAMS_BLOCK_CCM
+ * @csc: For header.type == C3_ISP_PARAMS_BLOCK_CSC
+ * @blc: For header.type == C3_ISP_PARAMS_BLOCK_BLC
+ */
+union c3_isp_params_block {
+ struct c3_isp_params_block_header header;
+ struct c3_isp_params_awb_gains awb_gains;
+ struct c3_isp_params_awb_config awb_cfg;
+ struct c3_isp_params_ae_config ae_cfg;
+ struct c3_isp_params_af_config af_cfg;
+ struct c3_isp_params_pst_gamma pst_gamma;
+ struct c3_isp_params_ccm ccm;
+ struct c3_isp_params_csc csc;
+ struct c3_isp_params_blc blc;
+};
+
+typedef void (*c3_isp_block_handler)(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block);
+
+struct c3_isp_params_handler {
+ size_t size;
+ c3_isp_block_handler handler;
+};
+
+#define to_c3_isp_params_buffer(vbuf) \
+ container_of(vbuf, struct c3_isp_params_buffer, vb)
+
+/* Hardware configuration */
+
+static void c3_isp_params_cfg_awb_gains(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_awb_gains *awb_gains = &block->awb_gains;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_WB_EN_MASK,
+ ISP_TOP_BEO_CTRL_WB_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0,
+ ISP_LSWB_WB_GAIN0_GR_GAIN_MASK,
+ ISP_LSWB_WB_GAIN0_GR_GAIN(awb_gains->gr_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0,
+ ISP_LSWB_WB_GAIN0_R_GAIN_MASK,
+ ISP_LSWB_WB_GAIN0_R_GAIN(awb_gains->r_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1,
+ ISP_LSWB_WB_GAIN1_B_GAIN_MASK,
+ ISP_LSWB_WB_GAIN1_B_GAIN(awb_gains->b_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1,
+ ISP_LSWB_WB_GAIN1_GB_GAIN_MASK,
+ ISP_LSWB_WB_GAIN1_GB_GAIN(awb_gains->gb_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN2,
+ ISP_LSWB_WB_GAIN2_IR_GAIN_MASK,
+ ISP_LSWB_WB_GAIN2_IR_GAIN(awb_gains->gb_gain));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_WB_EN_MASK,
+ ISP_TOP_BEO_CTRL_WB_EN);
+}
+
+static void c3_isp_params_awb_wt(struct c3_isp_device *isp,
+ const struct c3_isp_params_awb_config *cfg)
+{
+ unsigned int zones_num;
+ unsigned int base;
+ unsigned int data;
+ unsigned int i;
+
+ /* Set the weight address to 0 position */
+ c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0);
+
+ zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+
+ /* Need to write 8 weights at once */
+ for (i = 0; i < zones_num / 8; i++) {
+ base = i * 8;
+ data = ISP_AWB_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) |
+ ISP_AWB_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) |
+ ISP_AWB_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) |
+ ISP_AWB_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) |
+ ISP_AWB_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) |
+ ISP_AWB_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) |
+ ISP_AWB_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) |
+ ISP_AWB_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]);
+ c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data);
+ }
+
+ if (zones_num % 8 == 0)
+ return;
+
+ data = 0;
+ base = i * 8;
+
+ for (i = 0; i < zones_num % 8; i++)
+ data |= ISP_AWB_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]);
+
+ c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data);
+}
+
+static void c3_isp_params_awb_cood(struct c3_isp_device *isp,
+ const struct c3_isp_params_awb_config *cfg)
+{
+ unsigned int max_point_num;
+
+ /* The number of points is one more than the number of edges */
+ max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
+
+ /* Set the index address to 0 position */
+ c3_isp_write(isp, ISP_AWB_IDX_ADDR, 0);
+
+ for (unsigned int i = 0; i < max_point_num; i++)
+ c3_isp_write(isp, ISP_AWB_IDX_DATA,
+ ISP_AWB_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
+ ISP_AWB_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
+}
+
+static void c3_isp_params_cfg_awb_config(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_awb_config *awb_cfg = &block->awb_cfg;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_POINT_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_POINT(awb_cfg->tap_point));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_CTRL2,
+ ISP_AWB_STAT_CTRL2_SATUR_CTRL_MASK,
+ ISP_AWB_STAT_CTRL2_SATUR_CTRL(awb_cfg->satur_vald));
+
+ c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM,
+ ISP_AWB_HV_BLKNUM_H_NUM_MASK,
+ ISP_AWB_HV_BLKNUM_H_NUM(awb_cfg->horiz_zones_num));
+ c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM,
+ ISP_AWB_HV_BLKNUM_V_NUM_MASK,
+ ISP_AWB_HV_BLKNUM_V_NUM(awb_cfg->vert_zones_num));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MIN_VALUE_MASK,
+ ISP_AWB_STAT_RG_MIN_VALUE(awb_cfg->rg_min));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MAX_VALUE_MASK,
+ ISP_AWB_STAT_RG_MAX_VALUE(awb_cfg->rg_max));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MIN_VALUE_MASK,
+ ISP_AWB_STAT_BG_MIN_VALUE(awb_cfg->bg_min));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MAX_VALUE_MASK,
+ ISP_AWB_STAT_BG_MAX_VALUE(awb_cfg->bg_max));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL,
+ ISP_AWB_STAT_RG_HL_LOW_VALUE_MASK,
+ ISP_AWB_STAT_RG_HL_LOW_VALUE(awb_cfg->rg_low));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL,
+ ISP_AWB_STAT_RG_HL_HIGH_VALUE_MASK,
+ ISP_AWB_STAT_RG_HL_HIGH_VALUE(awb_cfg->rg_high));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL,
+ ISP_AWB_STAT_BG_HL_LOW_VALUE_MASK,
+ ISP_AWB_STAT_BG_HL_LOW_VALUE(awb_cfg->bg_low));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL,
+ ISP_AWB_STAT_BG_HL_HIGH_VALUE_MASK,
+ ISP_AWB_STAT_BG_HL_HIGH_VALUE(awb_cfg->bg_high));
+
+ c3_isp_params_awb_wt(isp, awb_cfg);
+ c3_isp_params_awb_cood(isp, awb_cfg);
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN);
+}
+
+static void c3_isp_params_ae_wt(struct c3_isp_device *isp,
+ const struct c3_isp_params_ae_config *cfg)
+{
+ unsigned int zones_num;
+ unsigned int base;
+ unsigned int data;
+ unsigned int i;
+
+ /* Set the weight address to 0 position */
+ c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0);
+
+ zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+
+ /* Need to write 8 weights at once */
+ for (i = 0; i < zones_num / 8; i++) {
+ base = i * 8;
+ data = ISP_AE_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) |
+ ISP_AE_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) |
+ ISP_AE_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) |
+ ISP_AE_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) |
+ ISP_AE_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) |
+ ISP_AE_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) |
+ ISP_AE_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) |
+ ISP_AE_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]);
+ c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data);
+ }
+
+ if (zones_num % 8 == 0)
+ return;
+
+ data = 0;
+ base = i * 8;
+
+ /* Write the last weights data */
+ for (i = 0; i < zones_num % 8; i++)
+ data |= ISP_AE_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]);
+
+ c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data);
+}
+
+static void c3_isp_params_ae_cood(struct c3_isp_device *isp,
+ const struct c3_isp_params_ae_config *cfg)
+{
+ unsigned int max_point_num;
+
+ /* The number of points is one more than the number of edges */
+ max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
+
+ /* Set the index address to 0 position */
+ c3_isp_write(isp, ISP_AE_IDX_ADDR, 0);
+
+ for (unsigned int i = 0; i < max_point_num; i++)
+ c3_isp_write(isp, ISP_AE_IDX_DATA,
+ ISP_AE_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
+ ISP_AE_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
+}
+
+static void c3_isp_params_cfg_ae_config(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_ae_config *ae_cfg = &block->ae_cfg;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_POINT_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_POINT(ae_cfg->tap_point));
+
+ if (ae_cfg->tap_point == C3_ISP_AE_STATS_TAP_GE)
+ c3_isp_update_bits(isp, ISP_AE_CTRL,
+ ISP_AE_CTRL_INPUT_2LINE_MASK,
+ ISP_AE_CTRL_INPUT_2LINE_EN);
+ else
+ c3_isp_update_bits(isp, ISP_AE_CTRL,
+ ISP_AE_CTRL_INPUT_2LINE_MASK,
+ ISP_AE_CTRL_INPUT_2LINE_DIS);
+
+ c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM,
+ ISP_AE_HV_BLKNUM_H_NUM_MASK,
+ ISP_AE_HV_BLKNUM_H_NUM(ae_cfg->horiz_zones_num));
+ c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM,
+ ISP_AE_HV_BLKNUM_V_NUM_MASK,
+ ISP_AE_HV_BLKNUM_V_NUM(ae_cfg->vert_zones_num));
+
+ c3_isp_params_ae_wt(isp, ae_cfg);
+ c3_isp_params_ae_cood(isp, ae_cfg);
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN);
+}
+
+static void c3_isp_params_af_cood(struct c3_isp_device *isp,
+ const struct c3_isp_params_af_config *cfg)
+{
+ unsigned int max_point_num;
+
+ /* The number of points is one more than the number of edges */
+ max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
+
+ /* Set the index address to 0 position */
+ c3_isp_write(isp, ISP_AF_IDX_ADDR, 0);
+
+ for (unsigned int i = 0; i < max_point_num; i++)
+ c3_isp_write(isp, ISP_AF_IDX_DATA,
+ ISP_AF_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
+ ISP_AF_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
+}
+
+static void c3_isp_params_cfg_af_config(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_af_config *af_cfg = &block->af_cfg;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_POINT_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_POINT(af_cfg->tap_point));
+
+ c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM,
+ ISP_AF_HV_BLKNUM_H_NUM_MASK,
+ ISP_AF_HV_BLKNUM_H_NUM(af_cfg->horiz_zones_num));
+ c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM,
+ ISP_AF_HV_BLKNUM_V_NUM_MASK,
+ ISP_AF_HV_BLKNUM_V_NUM(af_cfg->vert_zones_num));
+
+ c3_isp_params_af_cood(isp, af_cfg);
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN);
+}
+
+static void c3_isp_params_cfg_pst_gamma(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_pst_gamma *gm = &block->pst_gamma;
+ unsigned int base;
+ unsigned int i;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK,
+ ISP_TOP_BED_CTRL_PST_GAMMA_DIS);
+ return;
+ }
+
+ /* R, G and B channels use the same gamma lut */
+ for (unsigned int j = 0; j < 3; j++) {
+ /* Set the channel lut address */
+ c3_isp_write(isp, ISP_PST_GAMMA_LUT_ADDR,
+ ISP_PST_GAMMA_LUT_ADDR_IDX_ADDR(j));
+
+ /* Need to write 2 lut values at once */
+ for (i = 0; i < ARRAY_SIZE(gm->lut) / 2; i++) {
+ base = i * 2;
+ c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA,
+ ISP_PST_GM_LUT_DATA0(gm->lut[base]) |
+ ISP_PST_GM_LUT_DATA1(gm->lut[base + 1]));
+ }
+
+ /* Write the last one */
+ if (ARRAY_SIZE(gm->lut) % 2) {
+ base = i * 2;
+ c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA,
+ ISP_PST_GM_LUT_DATA0(gm->lut[base]));
+ }
+ }
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK,
+ ISP_TOP_BED_CTRL_PST_GAMMA_EN);
+}
+
+/* Configure 3 x 3 ccm matrix */
+static void c3_isp_params_cfg_ccm(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_ccm *ccm = &block->ccm;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CCM_EN_MASK,
+ ISP_TOP_BED_CTRL_CCM_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_CCM_MTX_00_01,
+ ISP_CCM_MTX_00_01_MTX_00_MASK,
+ ISP_CCM_MTX_00_01_MTX_00(ccm->matrix[0][0]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_00_01,
+ ISP_CCM_MTX_00_01_MTX_01_MASK,
+ ISP_CCM_MTX_00_01_MTX_01(ccm->matrix[0][1]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_02_03,
+ ISP_CCM_MTX_02_03_MTX_02_MASK,
+ ISP_CCM_MTX_02_03_MTX_02(ccm->matrix[0][2]));
+
+ c3_isp_update_bits(isp, ISP_CCM_MTX_10_11,
+ ISP_CCM_MTX_10_11_MTX_10_MASK,
+ ISP_CCM_MTX_10_11_MTX_10(ccm->matrix[1][0]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_10_11,
+ ISP_CCM_MTX_10_11_MTX_11_MASK,
+ ISP_CCM_MTX_10_11_MTX_11(ccm->matrix[1][1]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_12_13,
+ ISP_CCM_MTX_12_13_MTX_12_MASK,
+ ISP_CCM_MTX_12_13_MTX_12(ccm->matrix[1][2]));
+
+ c3_isp_update_bits(isp, ISP_CCM_MTX_20_21,
+ ISP_CCM_MTX_20_21_MTX_20_MASK,
+ ISP_CCM_MTX_20_21_MTX_20(ccm->matrix[2][0]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_20_21,
+ ISP_CCM_MTX_20_21_MTX_21_MASK,
+ ISP_CCM_MTX_20_21_MTX_21(ccm->matrix[2][1]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_22_23_RS,
+ ISP_CCM_MTX_22_23_RS_MTX_22_MASK,
+ ISP_CCM_MTX_22_23_RS_MTX_22(ccm->matrix[2][2]));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CCM_EN_MASK,
+ ISP_TOP_BED_CTRL_CCM_EN);
+}
+
+/* Configure color space conversion matrix parameters */
+static void c3_isp_params_cfg_csc(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_csc *csc = &block->csc;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CM0_EN_MASK,
+ ISP_TOP_BED_CTRL_CM0_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_CM0_COEF00_01,
+ ISP_CM0_COEF00_01_MTX_00_MASK,
+ ISP_CM0_COEF00_01_MTX_00(csc->matrix[0][0]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF00_01,
+ ISP_CM0_COEF00_01_MTX_01_MASK,
+ ISP_CM0_COEF00_01_MTX_01(csc->matrix[0][1]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF02_10,
+ ISP_CM0_COEF02_10_MTX_02_MASK,
+ ISP_CM0_COEF02_10_MTX_02(csc->matrix[0][2]));
+
+ c3_isp_update_bits(isp, ISP_CM0_COEF02_10,
+ ISP_CM0_COEF02_10_MTX_10_MASK,
+ ISP_CM0_COEF02_10_MTX_10(csc->matrix[1][0]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF11_12,
+ ISP_CM0_COEF11_12_MTX_11_MASK,
+ ISP_CM0_COEF11_12_MTX_11(csc->matrix[1][1]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF11_12,
+ ISP_CM0_COEF11_12_MTX_12_MASK,
+ ISP_CM0_COEF11_12_MTX_12(csc->matrix[1][2]));
+
+ c3_isp_update_bits(isp, ISP_CM0_COEF20_21,
+ ISP_CM0_COEF20_21_MTX_20_MASK,
+ ISP_CM0_COEF20_21_MTX_20(csc->matrix[2][0]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF20_21,
+ ISP_CM0_COEF20_21_MTX_21_MASK,
+ ISP_CM0_COEF20_21_MTX_21(csc->matrix[2][1]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF22_OUP_OFST0,
+ ISP_CM0_COEF22_OUP_OFST0_MTX_22_MASK,
+ ISP_CM0_COEF22_OUP_OFST0_MTX_22(csc->matrix[2][2]));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CM0_EN_MASK,
+ ISP_TOP_BED_CTRL_CM0_EN);
+}
+
+/* Set blc offset of each color channel */
+static void c3_isp_params_cfg_blc(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_blc *blc = &block->blc;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_BLC_EN_MASK,
+ ISP_TOP_BEO_CTRL_BLC_DIS);
+ return;
+ }
+
+ c3_isp_write(isp, ISP_LSWB_BLC_OFST0,
+ ISP_LSWB_BLC_OFST0_R_OFST(blc->r_ofst) |
+ ISP_LSWB_BLC_OFST0_GR_OFST(blc->gr_ofst));
+ c3_isp_write(isp, ISP_LSWB_BLC_OFST1,
+ ISP_LSWB_BLC_OFST1_GB_OFST(blc->gb_ofst) |
+ ISP_LSWB_BLC_OFST1_B_OFST(blc->b_ofst));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_BLC_EN_MASK,
+ ISP_TOP_BEO_CTRL_BLC_EN);
+}
+
+static const struct c3_isp_params_handler c3_isp_params_handlers[] = {
+ [C3_ISP_PARAMS_BLOCK_AWB_GAINS] = {
+ .size = sizeof(struct c3_isp_params_awb_gains),
+ .handler = c3_isp_params_cfg_awb_gains,
+ },
+ [C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = {
+ .size = sizeof(struct c3_isp_params_awb_config),
+ .handler = c3_isp_params_cfg_awb_config,
+ },
+ [C3_ISP_PARAMS_BLOCK_AE_CONFIG] = {
+ .size = sizeof(struct c3_isp_params_ae_config),
+ .handler = c3_isp_params_cfg_ae_config,
+ },
+ [C3_ISP_PARAMS_BLOCK_AF_CONFIG] = {
+ .size = sizeof(struct c3_isp_params_af_config),
+ .handler = c3_isp_params_cfg_af_config,
+ },
+ [C3_ISP_PARAMS_BLOCK_PST_GAMMA] = {
+ .size = sizeof(struct c3_isp_params_pst_gamma),
+ .handler = c3_isp_params_cfg_pst_gamma,
+ },
+ [C3_ISP_PARAMS_BLOCK_CCM] = {
+ .size = sizeof(struct c3_isp_params_ccm),
+ .handler = c3_isp_params_cfg_ccm,
+ },
+ [C3_ISP_PARAMS_BLOCK_CSC] = {
+ .size = sizeof(struct c3_isp_params_csc),
+ .handler = c3_isp_params_cfg_csc,
+ },
+ [C3_ISP_PARAMS_BLOCK_BLC] = {
+ .size = sizeof(struct c3_isp_params_blc),
+ .handler = c3_isp_params_cfg_blc,
+ },
+};
+
+static void c3_isp_params_cfg_blocks(struct c3_isp_params *params)
+{
+ struct c3_isp_params_cfg *config = params->buff->cfg;
+ size_t block_offset = 0;
+
+ if (WARN_ON(!config))
+ return;
+
+ /* Walk the list of parameter blocks and process them */
+ while (block_offset < config->data_size) {
+ const struct c3_isp_params_handler *block_handler;
+ const union c3_isp_params_block *block;
+
+ block = (const union c3_isp_params_block *)
+ &config->data[block_offset];
+
+ block_handler = &c3_isp_params_handlers[block->header.type];
+ block_handler->handler(params->isp, block);
+
+ block_offset += block->header.size;
+ }
+}
+
+void c3_isp_params_pre_cfg(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+
+ /* Disable some unused modules */
+ c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL0,
+ ISP_TOP_FEO_CTRL0_INPUT_FMT_EN_MASK,
+ ISP_TOP_FEO_CTRL0_INPUT_FMT_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0,
+ ISP_TOP_FEO_CTRL1_0_DPC_EN_MASK,
+ ISP_TOP_FEO_CTRL1_0_DPC_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0,
+ ISP_TOP_FEO_CTRL1_0_OG_EN_MASK,
+ ISP_TOP_FEO_CTRL1_0_OG_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_PDPC_EN_MASK,
+ ISP_TOP_FED_CTRL_PDPC_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
+ ISP_TOP_FED_CTRL_RAWCNR_EN_MASK,
+ ISP_TOP_FED_CTRL_RAWCNR_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SNR1_EN_MASK,
+ ISP_TOP_FED_CTRL_SNR1_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_TNR0_EN_MASK,
+ ISP_TOP_FED_CTRL_TNR0_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
+ ISP_TOP_FED_CTRL_CUBIC_CS_EN_MASK,
+ ISP_TOP_FED_CTRL_CUBIC_CS_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SQRT_EN_MASK,
+ ISP_TOP_FED_CTRL_SQRT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
+ ISP_TOP_FED_CTRL_DGAIN_EN_MASK,
+ ISP_TOP_FED_CTRL_DGAIN_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_INV_DGAIN_EN_MASK,
+ ISP_TOP_BEO_CTRL_INV_DGAIN_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, ISP_TOP_BEO_CTRL_EOTF_EN_MASK,
+ ISP_TOP_BEO_CTRL_EOTF_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_YHS_STAT_EN_MASK,
+ ISP_TOP_BED_CTRL_YHS_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_GRPH_STAT_EN_MASK,
+ ISP_TOP_BED_CTRL_GRPH_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_FMETER_EN_MASK,
+ ISP_TOP_BED_CTRL_FMETER_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_BSC_EN_MASK,
+ ISP_TOP_BED_CTRL_BSC_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CNR2_EN_MASK,
+ ISP_TOP_BED_CTRL_CNR2_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CM1_EN_MASK,
+ ISP_TOP_BED_CTRL_CM1_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_LUT3D_EN_MASK,
+ ISP_TOP_BED_CTRL_LUT3D_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_PST_TNR_LITE_EN_MASK,
+ ISP_TOP_BED_CTRL_PST_TNR_LITE_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_AMCM_EN_MASK,
+ ISP_TOP_BED_CTRL_AMCM_DIS);
+
+ /*
+ * Disable AE, AF and AWB stat module. Please configure the parameters
+ * in userspace algorithm if need to enable these switch.
+ */
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS);
+
+ c3_isp_write(isp, ISP_LSWB_WB_LIMIT0,
+ ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MAX |
+ ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MAX);
+ c3_isp_write(isp, ISP_LSWB_WB_LIMIT1,
+ ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MAX |
+ ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MAX);
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ /* Only use the first buffer to initialize ISP */
+ params->buff =
+ list_first_entry_or_null(&params->pending,
+ struct c3_isp_params_buffer, list);
+ if (params->buff)
+ c3_isp_params_cfg_blocks(params);
+}
+
+/* V4L2 video operations */
+
+static int c3_isp_params_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "AML C3 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static int c3_isp_params_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_C3ISP_PARAMS;
+
+ return 0;
+}
+
+static int c3_isp_params_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_params *params = video_drvdata(file);
+
+ f->fmt.meta = params->vfmt.fmt.meta;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp_params_v4l2_ioctl_ops = {
+ .vidioc_querycap = c3_isp_params_querycap,
+ .vidioc_enum_fmt_meta_out = c3_isp_params_enum_fmt,
+ .vidioc_g_fmt_meta_out = c3_isp_params_g_fmt,
+ .vidioc_s_fmt_meta_out = c3_isp_params_g_fmt,
+ .vidioc_try_fmt_meta_out = c3_isp_params_g_fmt,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct v4l2_file_operations isp_params_v4l2_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int c3_isp_params_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+
+ if (sizes[0] < sizeof(struct c3_isp_params_cfg))
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *num_planes = 1;
+ sizes[0] = sizeof(struct c3_isp_params_cfg);
+
+ return 0;
+}
+
+static void c3_isp_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
+ struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ list_add_tail(&buf->list, &params->pending);
+}
+
+static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(vbuf);
+ struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
+ struct c3_isp_params_cfg *cfg = buf->cfg;
+ struct c3_isp_params_cfg *usr_cfg = vb2_plane_vaddr(vb, 0);
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+ size_t header_size = offsetof(struct c3_isp_params_cfg, data);
+ size_t block_offset = 0;
+ size_t cfg_size;
+
+ /* Payload size can't be greater than the destination buffer size */
+ if (payload_size > params->vfmt.fmt.meta.buffersize) {
+ dev_dbg(params->isp->dev,
+ "Payload size is too large: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ /* Payload size can't be smaller than the header size */
+ if (payload_size < header_size) {
+ dev_dbg(params->isp->dev,
+ "Payload size is too small: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ /*
+ * Use the internal scratch buffer to avoid userspace modifying
+ * the buffer content while the driver is processing it.
+ */
+ memcpy(cfg, usr_cfg, payload_size);
+
+ /* Only v0 is supported at the moment */
+ if (cfg->version != C3_ISP_PARAMS_BUFFER_V0) {
+ dev_dbg(params->isp->dev,
+ "Invalid params buffer version: %u\n", cfg->version);
+ return -EINVAL;
+ }
+
+ /* Validate the size reported in the parameter buffer header */
+ cfg_size = header_size + cfg->data_size;
+ if (cfg_size != payload_size) {
+ dev_dbg(params->isp->dev,
+ "Data size %zu and payload size %zu are different\n",
+ cfg_size, payload_size);
+ return -EINVAL;
+ }
+
+ /* Walk the list of parameter blocks and validate them */
+ cfg_size = cfg->data_size;
+ while (cfg_size >= sizeof(struct c3_isp_params_block_header)) {
+ const struct c3_isp_params_block_header *block;
+ const struct c3_isp_params_handler *handler;
+
+ block = (struct c3_isp_params_block_header *)
+ &cfg->data[block_offset];
+
+ if (block->type >= ARRAY_SIZE(c3_isp_params_handlers)) {
+ dev_dbg(params->isp->dev,
+ "Invalid params block type\n");
+ return -EINVAL;
+ }
+
+ if (block->size > cfg_size) {
+ dev_dbg(params->isp->dev,
+ "Block size is greater than cfg size\n");
+ return -EINVAL;
+ }
+
+ if ((block->flags & (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
+ C3_ISP_PARAMS_BLOCK_FL_DISABLE)) ==
+ (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
+ C3_ISP_PARAMS_BLOCK_FL_DISABLE)) {
+ dev_dbg(params->isp->dev,
+ "Invalid parameters block flags\n");
+ return -EINVAL;
+ }
+
+ handler = &c3_isp_params_handlers[block->type];
+ if (block->size != handler->size) {
+ dev_dbg(params->isp->dev,
+ "Invalid params block size\n");
+ return -EINVAL;
+ }
+
+ block_offset += block->size;
+ cfg_size -= block->size;
+ }
+
+ if (cfg_size) {
+ dev_dbg(params->isp->dev,
+ "Unexpected data after the params buffer end\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_isp_params_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
+
+ buf->cfg = kvmalloc(params->vfmt.fmt.meta.buffersize, GFP_KERNEL);
+ if (!buf->cfg)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void c3_isp_params_vb2_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
+
+ kvfree(buf->cfg);
+ buf->cfg = NULL;
+}
+
+static void c3_isp_params_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct c3_isp_params *params = vb2_get_drv_priv(q);
+ struct c3_isp_params_buffer *buff;
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ while (!list_empty(&params->pending)) {
+ buff = list_first_entry(&params->pending,
+ struct c3_isp_params_buffer, list);
+ list_del(&buff->list);
+ vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops isp_params_vb2_ops = {
+ .queue_setup = c3_isp_params_vb2_queue_setup,
+ .buf_queue = c3_isp_params_vb2_buf_queue,
+ .buf_prepare = c3_isp_params_vb2_buf_prepare,
+ .buf_init = c3_isp_params_vb2_buf_init,
+ .buf_cleanup = c3_isp_params_vb2_buf_cleanup,
+ .stop_streaming = c3_isp_params_vb2_stop_streaming,
+};
+
+int c3_isp_params_register(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+ struct video_device *vdev = &params->vdev;
+ struct vb2_queue *vb2_q = &params->vb2_q;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ params->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_PARAMS;
+ params->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_params_cfg);
+ params->isp = isp;
+ INIT_LIST_HEAD(&params->pending);
+ spin_lock_init(&params->buff_lock);
+ mutex_init(&params->lock);
+
+ snprintf(vdev->name, sizeof(vdev->name), "c3-isp-params");
+ vdev->fops = &isp_params_v4l2_fops;
+ vdev->ioctl_ops = &isp_params_v4l2_ioctl_ops;
+ vdev->v4l2_dev = &isp->v4l2_dev;
+ vdev->lock = &params->lock;
+ vdev->minor = -1;
+ vdev->queue = vb2_q;
+ vdev->release = video_device_release_empty;
+ vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_TX;
+ video_set_drvdata(vdev, params);
+
+ vb2_q->drv_priv = params;
+ vb2_q->mem_ops = &vb2_vmalloc_memops;
+ vb2_q->ops = &isp_params_vb2_ops;
+ vb2_q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ vb2_q->io_modes = VB2_DMABUF | VB2_MMAP;
+ vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2_q->buf_struct_size = sizeof(struct c3_isp_params_buffer);
+ vb2_q->dev = isp->dev;
+ vb2_q->lock = &params->lock;
+ vb2_q->min_queued_buffers = 1;
+
+ ret = vb2_queue_init(vb2_q);
+ if (ret)
+ goto err_detroy;
+
+ params->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&vdev->entity, 1, &params->pad);
+ if (ret)
+ goto err_queue_release;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ dev_err(isp->dev,
+ "Failed to register %s: %d\n", vdev->name, ret);
+ goto err_entity_cleanup;
+ }
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_queue_release:
+ vb2_queue_release(vb2_q);
+err_detroy:
+ mutex_destroy(&params->lock);
+ return ret;
+}
+
+void c3_isp_params_unregister(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+
+ vb2_queue_release(&params->vb2_q);
+ media_entity_cleanup(&params->vdev.entity);
+ video_unregister_device(&params->vdev);
+ mutex_destroy(&params->lock);
+}
+
+void c3_isp_params_isr(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ params->buff =
+ list_first_entry_or_null(&params->pending,
+ struct c3_isp_params_buffer, list);
+ if (!params->buff)
+ return;
+
+ list_del(&params->buff->list);
+
+ c3_isp_params_cfg_blocks(params);
+
+ params->buff->vb.sequence = params->isp->frm_sequence;
+ params->buff->vb.vb2_buf.timestamp = ktime_get();
+ params->buff->vb.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&params->buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h b/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h
new file mode 100644
index 000000000000..fa249985a771
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h
@@ -0,0 +1,618 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef __C3_ISP_REGS_H__
+#define __C3_ISP_REGS_H__
+
+#define ISP_TOP_INPUT_SIZE 0x0000
+#define ISP_TOP_INPUT_SIZE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_TOP_INPUT_SIZE_VERT_SIZE(x) ((x) << 0)
+#define ISP_TOP_INPUT_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_TOP_INPUT_SIZE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_TOP_FRM_SIZE 0x0004
+#define ISP_TOP_FRM_SIZE_CORE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_TOP_FRM_SIZE_CORE_VERT_SIZE(x) ((x) << 0)
+#define ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_TOP_HOLD_SIZE 0x0008
+#define ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_TOP_PATH_EN 0x0010
+#define ISP_TOP_PATH_EN_DISP0_EN_MASK BIT(0)
+#define ISP_TOP_PATH_EN_DISP0_EN BIT(0)
+#define ISP_TOP_PATH_EN_DISP0_DIS (0 << 0)
+#define ISP_TOP_PATH_EN_DISP1_EN_MASK BIT(1)
+#define ISP_TOP_PATH_EN_DISP1_EN BIT(1)
+#define ISP_TOP_PATH_EN_DISP1_DIS (0 << 1)
+#define ISP_TOP_PATH_EN_DISP2_EN_MASK BIT(2)
+#define ISP_TOP_PATH_EN_DISP2_EN BIT(2)
+#define ISP_TOP_PATH_EN_DISP2_DIS (0 << 2)
+#define ISP_TOP_PATH_EN_WRMIF0_EN_MASK BIT(8)
+#define ISP_TOP_PATH_EN_WRMIF0_EN BIT(8)
+#define ISP_TOP_PATH_EN_WRMIF0_DIS (0 << 8)
+#define ISP_TOP_PATH_EN_WRMIF1_EN_MASK BIT(9)
+#define ISP_TOP_PATH_EN_WRMIF1_EN BIT(9)
+#define ISP_TOP_PATH_EN_WRMIF1_DIS (0 << 9)
+#define ISP_TOP_PATH_EN_WRMIF2_EN_MASK BIT(10)
+#define ISP_TOP_PATH_EN_WRMIF2_EN BIT(10)
+#define ISP_TOP_PATH_EN_WRMIF2_DIS (0 << 10)
+
+#define ISP_TOP_PATH_SEL 0x0014
+#define ISP_TOP_PATH_SEL_CORE_MASK GENMASK(18, 16)
+#define ISP_TOP_PATH_SEL_CORE_CORE_DIS (0 << 16)
+#define ISP_TOP_PATH_SEL_CORE_MIPI_CORE BIT(16)
+
+#define ISP_TOP_DISPIN_SEL 0x0018
+#define ISP_TOP_DISPIN_SEL_DISP0_MASK GENMASK(3, 0)
+#define ISP_TOP_DISPIN_SEL_DISP0_CORE_OUT (0 << 0)
+#define ISP_TOP_DISPIN_SEL_DISP0_MIPI_OUT (2 << 0)
+#define ISP_TOP_DISPIN_SEL_DISP1_MASK GENMASK(7, 4)
+#define ISP_TOP_DISPIN_SEL_DISP1_CORE_OUT (0 << 4)
+#define ISP_TOP_DISPIN_SEL_DISP1_MIPI_OUT (2 << 4)
+#define ISP_TOP_DISPIN_SEL_DISP2_MASK GENMASK(11, 8)
+#define ISP_TOP_DISPIN_SEL_DISP2_CORE_OUT (0 << 8)
+#define ISP_TOP_DISPIN_SEL_DISP2_MIPI_OUT (2 << 8)
+
+#define ISP_TOP_IRQ_EN 0x0080
+#define ISP_TOP_IRQ_EN_FRM_END_MASK BIT(0)
+#define ISP_TOP_IRQ_EN_FRM_END_EN BIT(0)
+#define ISP_TOP_IRQ_EN_FRM_END_DIS (0 << 0)
+#define ISP_TOP_IRQ_EN_FRM_RST_MASK BIT(1)
+#define ISP_TOP_IRQ_EN_FRM_RST_EN BIT(1)
+#define ISP_TOP_IRQ_EN_FRM_RST_DIS (0 << 1)
+#define ISP_TOP_IRQ_EN_3A_DMA_ERR_MASK BIT(5)
+#define ISP_TOP_IRQ_EN_3A_DMA_ERR_EN BIT(5)
+#define ISP_TOP_IRQ_EN_3A_DMA_ERR_DIS (0 << 5)
+
+#define ISP_TOP_IRQ_CLR 0x0084
+#define ISP_TOP_RO_IRQ_STAT 0x01c4
+#define ISP_TOP_RO_IRQ_STAT_FRM_END_MASK BIT(0)
+#define ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK BIT(1)
+#define ISP_TOP_RO_IRQ_STAT_3A_DMA_ERR_MASK BIT(5)
+
+#define ISP_TOP_MODE_CTRL 0x0400
+#define ISP_TOP_FEO_CTRL0 0x040c
+#define ISP_TOP_FEO_CTRL0_INPUT_FMT_EN_MASK BIT(8)
+#define ISP_TOP_FEO_CTRL0_INPUT_FMT_DIS (0 << 8)
+#define ISP_TOP_FEO_CTRL0_INPUT_FMT_EN BIT(8)
+
+#define ISP_TOP_FEO_CTRL1_0 0x0410
+#define ISP_TOP_FEO_CTRL1_0_DPC_EN_MASK BIT(3)
+#define ISP_TOP_FEO_CTRL1_0_DPC_DIS (0 << 3)
+#define ISP_TOP_FEO_CTRL1_0_DPC_EN BIT(3)
+#define ISP_TOP_FEO_CTRL1_0_OG_EN_MASK BIT(5)
+#define ISP_TOP_FEO_CTRL1_0_OG_DIS (0 << 5)
+#define ISP_TOP_FEO_CTRL1_0_OG_EN BIT(5)
+
+#define ISP_TOP_FED_CTRL 0x0418
+#define ISP_TOP_FED_CTRL_PDPC_EN_MASK BIT(1)
+#define ISP_TOP_FED_CTRL_PDPC_DIS (0 << 1)
+#define ISP_TOP_FED_CTRL_PDPC_EN BIT(1)
+#define ISP_TOP_FED_CTRL_RAWCNR_EN_MASK GENMASK(6, 5)
+#define ISP_TOP_FED_CTRL_RAWCNR_DIS (0 << 5)
+#define ISP_TOP_FED_CTRL_RAWCNR_EN BIT(5)
+#define ISP_TOP_FED_CTRL_SNR1_EN_MASK BIT(9)
+#define ISP_TOP_FED_CTRL_SNR1_DIS (0 << 9)
+#define ISP_TOP_FED_CTRL_SNR1_EN BIT(9)
+#define ISP_TOP_FED_CTRL_TNR0_EN_MASK BIT(11)
+#define ISP_TOP_FED_CTRL_TNR0_DIS (0 << 11)
+#define ISP_TOP_FED_CTRL_TNR0_EN BIT(11)
+#define ISP_TOP_FED_CTRL_CUBIC_CS_EN_MASK BIT(12)
+#define ISP_TOP_FED_CTRL_CUBIC_CS_DIS (0 << 12)
+#define ISP_TOP_FED_CTRL_CUBIC_CS_EN BIT(12)
+#define ISP_TOP_FED_CTRL_SQRT_EN_MASK BIT(14)
+#define ISP_TOP_FED_CTRL_SQRT_DIS (0 << 14)
+#define ISP_TOP_FED_CTRL_SQRT_EN BIT(14)
+#define ISP_TOP_FED_CTRL_DGAIN_EN_MASK BIT(16)
+#define ISP_TOP_FED_CTRL_DGAIN_DIS (0 << 16)
+#define ISP_TOP_FED_CTRL_DGAIN_EN BIT(16)
+
+#define ISP_TOP_BEO_CTRL 0x041c
+#define ISP_TOP_BEO_CTRL_WB_EN_MASK BIT(6)
+#define ISP_TOP_BEO_CTRL_WB_DIS (0 << 6)
+#define ISP_TOP_BEO_CTRL_WB_EN BIT(6)
+#define ISP_TOP_BEO_CTRL_BLC_EN_MASK BIT(7)
+#define ISP_TOP_BEO_CTRL_BLC_DIS (0 << 7)
+#define ISP_TOP_BEO_CTRL_BLC_EN BIT(7)
+#define ISP_TOP_BEO_CTRL_INV_DGAIN_EN_MASK BIT(8)
+#define ISP_TOP_BEO_CTRL_INV_DGAIN_DIS (0 << 8)
+#define ISP_TOP_BEO_CTRL_INV_DGAIN_EN BIT(8)
+#define ISP_TOP_BEO_CTRL_EOTF_EN_MASK BIT(9)
+#define ISP_TOP_BEO_CTRL_EOTF_DIS (0 << 9)
+#define ISP_TOP_BEO_CTRL_EOTF_EN BIT(9)
+
+#define ISP_TOP_BED_CTRL 0x0420
+#define ISP_TOP_BED_CTRL_YHS_STAT_EN_MASK GENMASK(1, 0)
+#define ISP_TOP_BED_CTRL_YHS_STAT_DIS (0 << 0)
+#define ISP_TOP_BED_CTRL_YHS_STAT_EN BIT(0)
+#define ISP_TOP_BED_CTRL_GRPH_STAT_EN_MASK BIT(2)
+#define ISP_TOP_BED_CTRL_GRPH_STAT_DIS (0 << 2)
+#define ISP_TOP_BED_CTRL_GRPH_STAT_EN BIT(2)
+#define ISP_TOP_BED_CTRL_FMETER_EN_MASK BIT(3)
+#define ISP_TOP_BED_CTRL_FMETER_DIS (0 << 3)
+#define ISP_TOP_BED_CTRL_FMETER_EN BIT(3)
+#define ISP_TOP_BED_CTRL_BSC_EN_MASK BIT(10)
+#define ISP_TOP_BED_CTRL_BSC_DIS (0 << 10)
+#define ISP_TOP_BED_CTRL_BSC_EN BIT(10)
+#define ISP_TOP_BED_CTRL_CNR2_EN_MASK BIT(11)
+#define ISP_TOP_BED_CTRL_CNR2_DIS (0 << 11)
+#define ISP_TOP_BED_CTRL_CNR2_EN BIT(11)
+#define ISP_TOP_BED_CTRL_CM1_EN_MASK BIT(13)
+#define ISP_TOP_BED_CTRL_CM1_DIS (0 << 13)
+#define ISP_TOP_BED_CTRL_CM1_EN BIT(13)
+#define ISP_TOP_BED_CTRL_CM0_EN_MASK BIT(14)
+#define ISP_TOP_BED_CTRL_CM0_DIS (0 << 14)
+#define ISP_TOP_BED_CTRL_CM0_EN BIT(14)
+#define ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK BIT(16)
+#define ISP_TOP_BED_CTRL_PST_GAMMA_DIS (0 << 16)
+#define ISP_TOP_BED_CTRL_PST_GAMMA_EN BIT(16)
+#define ISP_TOP_BED_CTRL_LUT3D_EN_MASK BIT(17)
+#define ISP_TOP_BED_CTRL_LUT3D_DIS (0 << 17)
+#define ISP_TOP_BED_CTRL_LUT3D_EN BIT(17)
+#define ISP_TOP_BED_CTRL_CCM_EN_MASK BIT(18)
+#define ISP_TOP_BED_CTRL_CCM_DIS (0 << 18)
+#define ISP_TOP_BED_CTRL_CCM_EN BIT(18)
+#define ISP_TOP_BED_CTRL_PST_TNR_LITE_EN_MASK BIT(21)
+#define ISP_TOP_BED_CTRL_PST_TNR_LITE_DIS (0 << 21)
+#define ISP_TOP_BED_CTRL_PST_TNR_LITE_EN BIT(21)
+#define ISP_TOP_BED_CTRL_AMCM_EN_MASK BIT(25)
+#define ISP_TOP_BED_CTRL_AMCM_DIS (0 << 25)
+#define ISP_TOP_BED_CTRL_AMCM_EN BIT(25)
+
+#define ISP_TOP_3A_STAT_CRTL 0x0424
+#define ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK BIT(0)
+#define ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS (0 << 0)
+#define ISP_TOP_3A_STAT_CRTL_AE_STAT_EN BIT(0)
+#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK BIT(1)
+#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS (0 << 1)
+#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN BIT(1)
+#define ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK BIT(2)
+#define ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS (0 << 2)
+#define ISP_TOP_3A_STAT_CRTL_AF_STAT_EN BIT(2)
+#define ISP_TOP_3A_STAT_CRTL_AWB_POINT_MASK GENMASK(6, 4)
+#define ISP_TOP_3A_STAT_CRTL_AWB_POINT(x) ((x) << 4)
+#define ISP_TOP_3A_STAT_CRTL_AE_POINT_MASK GENMASK(9, 8)
+#define ISP_TOP_3A_STAT_CRTL_AE_POINT(x) ((x) << 8)
+#define ISP_TOP_3A_STAT_CRTL_AF_POINT_MASK GENMASK(13, 12)
+#define ISP_TOP_3A_STAT_CRTL_AF_POINT(x) ((x) << 12)
+
+#define ISP_LSWB_BLC_OFST0 0x4028
+#define ISP_LSWB_BLC_OFST0_R_OFST_MASK GENMASK(15, 0)
+#define ISP_LSWB_BLC_OFST0_R_OFST(x) ((x) << 0)
+#define ISP_LSWB_BLC_OFST0_GR_OFST_MASK GENMASK(31, 16)
+#define ISP_LSWB_BLC_OFST0_GR_OFST(x) ((x) << 16)
+
+#define ISP_LSWB_BLC_OFST1 0x402c
+#define ISP_LSWB_BLC_OFST1_GB_OFST_MASK GENMASK(15, 0)
+#define ISP_LSWB_BLC_OFST1_GB_OFST(x) ((x) << 0)
+#define ISP_LSWB_BLC_OFST1_B_OFST_MASK GENMASK(31, 16)
+#define ISP_LSWB_BLC_OFST1_B_OFST(x) ((x) << 16)
+
+#define ISP_LSWB_BLC_PHSOFST 0x4034
+#define ISP_LSWB_BLC_PHSOFST_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_LSWB_BLC_PHSOFST_VERT_OFST(x) ((x) << 0)
+#define ISP_LSWB_BLC_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_LSWB_BLC_PHSOFST_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_LSWB_WB_GAIN0 0x4038
+#define ISP_LSWB_WB_GAIN0_R_GAIN_MASK GENMASK(11, 0)
+#define ISP_LSWB_WB_GAIN0_R_GAIN(x) ((x) << 0)
+#define ISP_LSWB_WB_GAIN0_GR_GAIN_MASK GENMASK(27, 16)
+#define ISP_LSWB_WB_GAIN0_GR_GAIN(x) ((x) << 16)
+
+#define ISP_LSWB_WB_GAIN1 0x403c
+#define ISP_LSWB_WB_GAIN1_GB_GAIN_MASK GENMASK(11, 0)
+#define ISP_LSWB_WB_GAIN1_GB_GAIN(x) ((x) << 0)
+#define ISP_LSWB_WB_GAIN1_B_GAIN_MASK GENMASK(27, 16)
+#define ISP_LSWB_WB_GAIN1_B_GAIN(x) ((x) << 16)
+
+#define ISP_LSWB_WB_GAIN2 0x4040
+#define ISP_LSWB_WB_GAIN2_IR_GAIN_MASK GENMASK(11, 0)
+#define ISP_LSWB_WB_GAIN2_IR_GAIN(x) ((x) << 0)
+
+#define ISP_LSWB_WB_LIMIT0 0x4044
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MASK GENMASK(15, 0)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R(x) ((x) << 0)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MAX (0x8fff << 0)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MASK GENMASK(31, 16)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR(x) ((x) << 16)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MAX (0x8fff << 16)
+
+#define ISP_LSWB_WB_LIMIT1 0x4048
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MASK GENMASK(15, 0)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB(x) ((x) << 0)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MAX (0x8fff << 0)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MASK GENMASK(31, 16)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B(x) ((x) << 16)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MAX (0x8fff << 16)
+
+#define ISP_LSWB_WB_PHSOFST 0x4050
+#define ISP_LSWB_WB_PHSOFST_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_LSWB_WB_PHSOFST_VERT_OFST(x) ((x) << 0)
+#define ISP_LSWB_WB_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_LSWB_WB_PHSOFST_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_LSWB_LNS_PHSOFST 0x4054
+#define ISP_LSWB_LNS_PHSOFST_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_LSWB_LNS_PHSOFST_VERT_OFST(x) ((x) << 0)
+#define ISP_LSWB_LNS_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_LSWB_LNS_PHSOFST_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_DMS_COMMON_PARAM0 0x5000
+#define ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST_MASK GENMASK(1, 0)
+#define ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST(x) ((x) << 0)
+#define ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST_MASK GENMASK(3, 2)
+#define ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST(x) ((x) << 2)
+
+#define ISP_CM0_COEF00_01 0x6048
+#define ISP_CM0_COEF00_01_MTX_00_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF00_01_MTX_00(x) ((x) << 0)
+#define ISP_CM0_COEF00_01_MTX_01_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF00_01_MTX_01(x) ((x) << 16)
+
+#define ISP_CM0_COEF02_10 0x604c
+#define ISP_CM0_COEF02_10_MTX_02_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF02_10_MTX_02(x) ((x) << 0)
+#define ISP_CM0_COEF02_10_MTX_10_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF02_10_MTX_10(x) ((x) << 16)
+
+#define ISP_CM0_COEF11_12 0x6050
+#define ISP_CM0_COEF11_12_MTX_11_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF11_12_MTX_11(x) ((x) << 0)
+#define ISP_CM0_COEF11_12_MTX_12_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF11_12_MTX_12(x) ((x) << 16)
+
+#define ISP_CM0_COEF20_21 0x6054
+#define ISP_CM0_COEF20_21_MTX_20_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF20_21_MTX_20(x) ((x) << 0)
+#define ISP_CM0_COEF20_21_MTX_21_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF20_21_MTX_21(x) ((x) << 16)
+
+#define ISP_CM0_COEF22_OUP_OFST0 0x6058
+#define ISP_CM0_COEF22_OUP_OFST0_MTX_22_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF22_OUP_OFST0_MTX_22(x) ((x) << 0)
+
+#define ISP_CCM_MTX_00_01 0x6098
+#define ISP_CCM_MTX_00_01_MTX_00_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_00_01_MTX_00(x) ((x) << 0)
+#define ISP_CCM_MTX_00_01_MTX_01_MASK GENMASK(28, 16)
+#define ISP_CCM_MTX_00_01_MTX_01(x) ((x) << 16)
+
+#define ISP_CCM_MTX_02_03 0x609c
+#define ISP_CCM_MTX_02_03_MTX_02_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_02_03_MTX_02(x) ((x) << 0)
+
+#define ISP_CCM_MTX_10_11 0x60A0
+#define ISP_CCM_MTX_10_11_MTX_10_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_10_11_MTX_10(x) ((x) << 0)
+#define ISP_CCM_MTX_10_11_MTX_11_MASK GENMASK(28, 16)
+#define ISP_CCM_MTX_10_11_MTX_11(x) ((x) << 16)
+
+#define ISP_CCM_MTX_12_13 0x60A4
+#define ISP_CCM_MTX_12_13_MTX_12_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_12_13_MTX_12(x) ((x) << 0)
+
+#define ISP_CCM_MTX_20_21 0x60A8
+#define ISP_CCM_MTX_20_21_MTX_20_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_20_21_MTX_20(x) ((x) << 0)
+#define ISP_CCM_MTX_20_21_MTX_21_MASK GENMASK(28, 16)
+#define ISP_CCM_MTX_20_21_MTX_21(x) ((x) << 16)
+
+#define ISP_CCM_MTX_22_23_RS 0x60Ac
+#define ISP_CCM_MTX_22_23_RS_MTX_22_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_22_23_RS_MTX_22(x) ((x) << 0)
+
+#define ISP_PST_GAMMA_LUT_ADDR 0x60cc
+#define ISP_PST_GAMMA_LUT_ADDR_IDX_ADDR(x) ((x) << 7)
+
+#define ISP_PST_GAMMA_LUT_DATA 0x60d0
+#define ISP_PST_GM_LUT_DATA0(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_PST_GM_LUT_DATA1(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define DISP0_TOP_TOP_CTRL 0x8000
+#define DISP0_TOP_TOP_CTRL_CROP2_EN_MASK BIT(5)
+#define DISP0_TOP_TOP_CTRL_CROP2_EN BIT(5)
+#define DISP0_TOP_TOP_CTRL_CROP2_DIS (0 << 5)
+
+#define DISP0_TOP_CRP2_START 0x8004
+#define DISP0_TOP_CRP2_START_V_START_MASK GENMASK(15, 0)
+#define DISP0_TOP_CRP2_START_V_START(x) ((x) << 0)
+#define DISP0_TOP_CRP2_START_H_START_MASK GENMASK(31, 16)
+#define DISP0_TOP_CRP2_START_H_START(x) ((x) << 16)
+
+#define DISP0_TOP_CRP2_SIZE 0x8008
+#define DISP0_TOP_CRP2_SIZE_V_SIZE_MASK GENMASK(15, 0)
+#define DISP0_TOP_CRP2_SIZE_V_SIZE(x) ((x) << 0)
+#define DISP0_TOP_CRP2_SIZE_H_SIZE_MASK GENMASK(31, 16)
+#define DISP0_TOP_CRP2_SIZE_H_SIZE(x) ((x) << 16)
+
+#define DISP0_TOP_OUT_SIZE 0x800c
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT_MASK GENMASK(12, 0)
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT(x) ((x) << 0)
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH_MASK GENMASK(28, 16)
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH(x) ((x) << 16)
+
+#define ISP_DISP0_TOP_IN_SIZE 0x804c
+#define ISP_DISP0_TOP_IN_SIZE_VSIZE_MASK GENMASK(12, 0)
+#define ISP_DISP0_TOP_IN_SIZE_VSIZE(x) ((x) << 0)
+#define ISP_DISP0_TOP_IN_SIZE_HSIZE_MASK GENMASK(28, 16)
+#define ISP_DISP0_TOP_IN_SIZE_HSIZE(x) ((x) << 16)
+
+#define DISP0_PPS_SCALE_EN 0x8200
+#define DISP0_PPS_SCALE_EN_VSC_TAP_NUM_MASK GENMASK(3, 0)
+#define DISP0_PPS_SCALE_EN_VSC_TAP_NUM(x) ((x) << 0)
+#define DISP0_PPS_SCALE_EN_HSC_TAP_NUM_MASK GENMASK(7, 4)
+#define DISP0_PPS_SCALE_EN_HSC_TAP_NUM(x) ((x) << 4)
+#define DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM_MASK GENMASK(11, 8)
+#define DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM(x) ((x) << 8)
+#define DISP0_PPS_SCALE_EN_PREHSC_FLT_NUM_MASK GENMASK(15, 12)
+#define DISP0_PPS_SCALE_EN_PREHSC_FLT_NUM(x) ((x) << 12)
+#define DISP0_PPS_SCALE_EN_PREVSC_RATE_MASK GENMASK(17, 16)
+#define DISP0_PPS_SCALE_EN_PREVSC_RATE(x) ((x) << 16)
+#define DISP0_PPS_SCALE_EN_PREHSC_RATE_MASK GENMASK(19, 18)
+#define DISP0_PPS_SCALE_EN_PREHSC_RATE(x) ((x) << 18)
+#define DISP0_PPS_SCALE_EN_HSC_EN_MASK BIT(20)
+#define DISP0_PPS_SCALE_EN_HSC_EN(x) ((x) << 20)
+#define DISP0_PPS_SCALE_EN_HSC_DIS (0 << 20)
+#define DISP0_PPS_SCALE_EN_VSC_EN_MASK BIT(21)
+#define DISP0_PPS_SCALE_EN_VSC_EN(x) ((x) << 21)
+#define DISP0_PPS_SCALE_EN_VSC_DIS (0 << 21)
+#define DISP0_PPS_SCALE_EN_PREVSC_EN_MASK BIT(22)
+#define DISP0_PPS_SCALE_EN_PREVSC_EN(x) ((x) << 22)
+#define DISP0_PPS_SCALE_EN_PREVSC_DIS (0 << 22)
+#define DISP0_PPS_SCALE_EN_PREHSC_EN_MASK BIT(23)
+#define DISP0_PPS_SCALE_EN_PREHSC_EN(x) ((x) << 23)
+#define DISP0_PPS_SCALE_EN_PREHSC_DIS (0 << 23)
+#define DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS_MASK GENMASK(27, 24)
+#define DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS(x) ((x) << 24)
+#define DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS_MASK GENMASK(31, 28)
+#define DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS(x) ((x) << 28)
+
+#define DISP0_PPS_VSC_START_PHASE_STEP 0x8224
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC_MASK GENMASK(23, 0)
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC(x) ((x) << 0)
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE_MASK GENMASK(27, 24)
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE(x) ((x) << 24)
+
+#define DISP0_PPS_HSC_START_PHASE_STEP 0x8230
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC_MASK GENMASK(23, 0)
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC(x) ((x) << 0)
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE_MASK GENMASK(27, 24)
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE(x) ((x) << 24)
+
+#define DISP0_PPS_444TO422 0x823c
+#define DISP0_PPS_444TO422_EN_MASK BIT(0)
+#define DISP0_PPS_444TO422_EN(x) ((x) << 0)
+
+#define ISP_SCALE0_COEF_IDX_LUMA 0x8240
+#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_MASK BIT(9)
+#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_EN BIT(9)
+#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_DIS (0 << 9)
+#define ISP_SCALE0_COEF_IDX_LUMA_CTYPE_MASK GENMASK(12, 10)
+#define ISP_SCALE0_COEF_IDX_LUMA_CTYPE(x) ((x) << 10)
+
+#define ISP_SCALE0_COEF_LUMA 0x8244
+#define ISP_SCALE0_COEF_LUMA_DATA1(x) (((x) & GENMASK(10, 0)) << 0)
+#define ISP_SCALE0_COEF_LUMA_DATA0(x) (((x) & GENMASK(10, 0)) << 16)
+
+#define ISP_SCALE0_COEF_IDX_CHRO 0x8248
+#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_MASK BIT(9)
+#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_EN BIT(9)
+#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_DIS (0 << 9)
+#define ISP_SCALE0_COEF_IDX_CHRO_CTYPE_MASK GENMASK(12, 10)
+#define ISP_SCALE0_COEF_IDX_CHRO_CTYPE(x) ((x) << 10)
+
+#define ISP_SCALE0_COEF_CHRO 0x824c
+#define ISP_SCALE0_COEF_CHRO_DATA1(x) (((x) & GENMASK(10, 0)) << 0)
+#define ISP_SCALE0_COEF_CHRO_DATA0(x) (((x) & GENMASK(10, 0)) << 16)
+
+#define ISP_AF_CTRL 0xa044
+#define ISP_AF_CTRL_VERT_OFST_MASK GENMASK(15, 14)
+#define ISP_AF_CTRL_VERT_OFST(x) ((x) << 14)
+#define ISP_AF_CTRL_HORIZ_OFST_MASK GENMASK(17, 16)
+#define ISP_AF_CTRL_HORIZ_OFST(x) ((x) << 16)
+
+#define ISP_AF_HV_SIZE 0xa04c
+#define ISP_AF_HV_SIZE_GLB_WIN_YSIZE_MASK GENMASK(15, 0)
+#define ISP_AF_HV_SIZE_GLB_WIN_YSIZE(x) ((x) << 0)
+#define ISP_AF_HV_SIZE_GLB_WIN_XSIZE_MASK GENMASK(31, 16)
+#define ISP_AF_HV_SIZE_GLB_WIN_XSIZE(x) ((x) << 16)
+
+#define ISP_AF_HV_BLKNUM 0xa050
+#define ISP_AF_HV_BLKNUM_V_NUM_MASK GENMASK(5, 0)
+#define ISP_AF_HV_BLKNUM_V_NUM(x) ((x) << 0)
+#define ISP_AF_HV_BLKNUM_H_NUM_MASK GENMASK(21, 16)
+#define ISP_AF_HV_BLKNUM_H_NUM(x) ((x) << 16)
+
+#define ISP_AF_EN_CTRL 0xa054
+#define ISP_AF_EN_CTRL_STAT_SEL_MASK BIT(21)
+#define ISP_AF_EN_CTRL_STAT_SEL_OLD (0 << 21)
+#define ISP_AF_EN_CTRL_STAT_SEL_NEW BIT(21)
+
+#define ISP_AF_IDX_ADDR 0xa1c0
+#define ISP_AF_IDX_DATA 0xa1c4
+#define ISP_AF_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_AF_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define ISP_AE_CTRL 0xa448
+#define ISP_AE_CTRL_INPUT_2LINE_MASK BIT(7)
+#define ISP_AE_CTRL_INPUT_2LINE_EN BIT(7)
+#define ISP_AE_CTRL_INPUT_2LINE_DIS (0 << 7)
+#define ISP_AE_CTRL_LUMA_MODE_MASK GENMASK(9, 8)
+#define ISP_AE_CTRL_LUMA_MODE_CUR (0 << 8)
+#define ISP_AE_CTRL_LUMA_MODE_MAX BIT(8)
+#define ISP_AE_CTRL_LUMA_MODE_FILTER (2 << 8)
+#define ISP_AE_CTRL_VERT_OFST_MASK GENMASK(25, 24)
+#define ISP_AE_CTRL_VERT_OFST(x) ((x) << 24)
+#define ISP_AE_CTRL_HORIZ_OFST_MASK GENMASK(27, 26)
+#define ISP_AE_CTRL_HORIZ_OFST(x) ((x) << 26)
+
+#define ISP_AE_HV_SIZE 0xa464
+#define ISP_AE_HV_SIZE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_AE_HV_SIZE_VERT_SIZE(x) ((x) << 0)
+#define ISP_AE_HV_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_AE_HV_SIZE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_AE_HV_BLKNUM 0xa468
+#define ISP_AE_HV_BLKNUM_V_NUM_MASK GENMASK(6, 0)
+#define ISP_AE_HV_BLKNUM_V_NUM(x) ((x) << 0)
+#define ISP_AE_HV_BLKNUM_H_NUM_MASK GENMASK(22, 16)
+#define ISP_AE_HV_BLKNUM_H_NUM(x) ((x) << 16)
+
+#define ISP_AE_IDX_ADDR 0xa600
+#define ISP_AE_IDX_DATA 0xa604
+#define ISP_AE_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_AE_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define ISP_AE_BLK_WT_ADDR 0xa608
+#define ISP_AE_BLK_WT_DATA 0xa60c
+#define ISP_AE_BLK_WT_DATA_WT(i, x) (((x) & GENMASK(3, 0)) << ((i) * 4))
+
+#define ISP_AWB_CTRL 0xa834
+#define ISP_AWB_CTRL_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_AWB_CTRL_VERT_OFST(x) ((x) << 0)
+#define ISP_AWB_CTRL_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_AWB_CTRL_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_AWB_HV_SIZE 0xa83c
+#define ISP_AWB_HV_SIZE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_AWB_HV_SIZE_VERT_SIZE(x) ((x) << 0)
+#define ISP_AWB_HV_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_AWB_HV_SIZE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_AWB_HV_BLKNUM 0xa840
+#define ISP_AWB_HV_BLKNUM_V_NUM_MASK GENMASK(5, 0)
+#define ISP_AWB_HV_BLKNUM_V_NUM(x) ((x) << 0)
+#define ISP_AWB_HV_BLKNUM_H_NUM_MASK GENMASK(21, 16)
+#define ISP_AWB_HV_BLKNUM_H_NUM(x) ((x) << 16)
+
+#define ISP_AWB_STAT_RG 0xa848
+#define ISP_AWB_STAT_RG_MIN_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_RG_MIN_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_RG_MAX_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_RG_MAX_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_BG 0xa84c
+#define ISP_AWB_STAT_BG_MIN_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_BG_MIN_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_BG_MAX_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_BG_MAX_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_RG_HL 0xa850
+#define ISP_AWB_STAT_RG_HL_LOW_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_RG_HL_LOW_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_RG_HL_HIGH_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_RG_HL_HIGH_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_BG_HL 0xa854
+#define ISP_AWB_STAT_BG_HL_LOW_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_BG_HL_LOW_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_BG_HL_HIGH_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_BG_HL_HIGH_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_CTRL2 0xa858
+#define ISP_AWB_STAT_CTRL2_SATUR_CTRL_MASK BIT(0)
+#define ISP_AWB_STAT_CTRL2_SATUR_CTRL(x) ((x) << 0)
+
+#define ISP_AWB_IDX_ADDR 0xaa00
+#define ISP_AWB_IDX_DATA 0xaa04
+#define ISP_AWB_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_AWB_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define ISP_AWB_BLK_WT_ADDR 0xaa08
+#define ISP_AWB_BLK_WT_DATA 0xaa0c
+#define ISP_AWB_BLK_WT_DATA_WT(i, x) (((x) & GENMASK(3, 0)) << ((i) * 4))
+
+#define ISP_WRMIFX3_0_CH0_CTRL0 0xc400
+#define ISP_WRMIFX3_0_CH0_CTRL0_STRIDE_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_CH0_CTRL0_STRIDE(x) ((x) << 16)
+
+#define ISP_WRMIFX3_0_CH0_CTRL1 0xc404
+#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_MODE_MASK GENMASK(30, 27)
+#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS BIT(27)
+#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS (2 << 27)
+
+#define ISP_WRMIFX3_0_CH1_CTRL0 0xc408
+#define ISP_WRMIFX3_0_CH1_CTRL0_STRIDE_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_CH1_CTRL0_STRIDE(x) ((x) << 16)
+
+#define ISP_WRMIFX3_0_CH1_CTRL1 0xc40c
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_MODE_MASK GENMASK(30, 27)
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_8BITS BIT(27)
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_16BITS (2 << 27)
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_32BITS (3 << 27)
+
+#define ISP_WRMIFX3_0_WIN_LUMA_H 0xc420
+#define ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_WIN_LUMA_V 0xc424
+#define ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_WIN_CHROM_H 0xc428
+#define ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_WIN_CHROM_V 0xc42c
+#define ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_CH0_BADDR 0xc440
+#define ISP_WRMIFX3_0_CH0_BASE_ADDR(x) ((x) >> 4)
+
+#define ISP_WRMIFX3_0_CH1_BADDR 0xc444
+#define ISP_WRMIFX3_0_CH1_BASE_ADDR(x) ((x) >> 4)
+
+#define ISP_WRMIFX3_0_FMT_SIZE 0xc464
+#define ISP_WRMIFX3_0_FMT_SIZE_HSIZE_MASK GENMASK(15, 0)
+#define ISP_WRMIFX3_0_FMT_SIZE_HSIZE(x) ((x) << 0)
+#define ISP_WRMIFX3_0_FMT_SIZE_VSIZE_MASK GENMASK(31, 16)
+#define ISP_WRMIFX3_0_FMT_SIZE_VSIZE(x) ((x) << 16)
+
+#define ISP_WRMIFX3_0_FMT_CTRL 0xc468
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_MASK GENMASK(1, 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT (0 << 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_10BIT BIT(0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_12BIT (2 << 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT (3 << 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_MASK BIT(2)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU (0 << 2)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV BIT(2)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_MASK GENMASK(5, 4)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1 (0 << 4)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2 BIT(4)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_MASK GENMASK(18, 16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422 BIT(16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420 (2 << 16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_Y_ONLY (3 << 16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW (4 << 16)
+
+#define VIU_DMAWR_BADDR0 0xc840
+#define VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR_MASK GENMASK(27, 0)
+#define VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR(x) ((x) >> 4)
+
+#define VIU_DMAWR_BADDR1 0xc844
+#define VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR_MASK GENMASK(27, 0)
+#define VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR(x) ((x) >> 4)
+
+#define VIU_DMAWR_BADDR2 0xc848
+#define VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR_MASK GENMASK(27, 0)
+#define VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR(x) ((x) >> 4)
+
+#define VIU_DMAWR_SIZE0 0xc854
+#define VIU_DMAWR_SIZE0_AF_STATS_SIZE_MASK GENMASK(15, 0)
+#define VIU_DMAWR_SIZE0_AF_STATS_SIZE(x) ((x) << 0)
+#define VIU_DMAWR_SIZE0_AWB_STATS_SIZE_MASK GENMASK(31, 16)
+#define VIU_DMAWR_SIZE0_AWB_STATS_SIZE(x) ((x) << 16)
+
+#define VIU_DMAWR_SIZE1 0xc858
+#define VIU_DMAWR_SIZE1_AE_STATS_SIZE_MASK GENMASK(15, 0)
+#define VIU_DMAWR_SIZE1_AE_STATS_SIZE(x) ((x) << 0)
+
+#endif
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c
new file mode 100644
index 000000000000..453a889e0b27
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c
@@ -0,0 +1,892 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+#define C3_ISP_RSZ_DEF_PAD_FMT MEDIA_BUS_FMT_YUV10_1X30
+#define C3_ISP_DISP_REG(base, id) ((base) + (id) * 0x400)
+#define C3_ISP_PPS_LUT_H_NUM 33
+#define C3_ISP_PPS_LUT_CTYPE_0 0
+#define C3_ISP_PPS_LUT_CTYPE_2 2
+#define C3_ISP_SCL_EN 1
+#define C3_ISP_SCL_DIS 0
+
+/*
+ * struct c3_isp_rsz_format_info - ISP resizer format information
+ *
+ * @mbus_code: the mbus code
+ * @pads: bitmask detailing valid pads for this mbus_code
+ * @is_raw: the raw format flag of mbus code
+ */
+struct c3_isp_rsz_format_info {
+ u32 mbus_code;
+ u32 pads;
+ bool is_raw;
+};
+
+static const struct c3_isp_rsz_format_info c3_isp_rsz_fmts[] = {
+ /* RAW formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ },
+ /* YUV formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = false,
+ },
+};
+
+/*
+ * struct c3_isp_pps_io_size - ISP scaler input and output size
+ *
+ * @thsize: input horizontal size of after preprocessing
+ * @tvsize: input vertical size of after preprocessing
+ * @ohsize: output horizontal size
+ * @ovsize: output vertical size
+ * @ihsize: input horizontal size
+ * @max_hsize: maximum horizontal size
+ */
+struct c3_isp_pps_io_size {
+ u32 thsize;
+ u32 tvsize;
+ u32 ohsize;
+ u32 ovsize;
+ u32 ihsize;
+ u32 max_hsize;
+};
+
+/* The normal parameters of pps module */
+static const int c3_isp_pps_lut[C3_ISP_PPS_LUT_H_NUM][4] = {
+ { 0, 511, 0, 0}, { -5, 511, 5, 0}, {-10, 511, 11, 0},
+ {-14, 510, 17, -1}, {-18, 508, 23, -1}, {-22, 506, 29, -1},
+ {-25, 503, 36, -2}, {-28, 500, 43, -3}, {-32, 496, 51, -3},
+ {-34, 491, 59, -4}, {-37, 487, 67, -5}, {-39, 482, 75, -6},
+ {-41, 476, 84, -7}, {-42, 470, 92, -8}, {-44, 463, 102, -9},
+ {-45, 456, 111, -10}, {-45, 449, 120, -12}, {-47, 442, 130, -13},
+ {-47, 434, 140, -15}, {-47, 425, 151, -17}, {-47, 416, 161, -18},
+ {-47, 407, 172, -20}, {-47, 398, 182, -21}, {-47, 389, 193, -23},
+ {-46, 379, 204, -25}, {-45, 369, 215, -27}, {-44, 358, 226, -28},
+ {-43, 348, 237, -30}, {-43, 337, 249, -31}, {-41, 326, 260, -33},
+ {-40, 316, 271, -35}, {-39, 305, 282, -36}, {-37, 293, 293, -37}
+};
+
+static const struct c3_isp_rsz_format_info
+*rsz_find_format_by_code(u32 code, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_rsz_fmts); i++) {
+ const struct c3_isp_rsz_format_info *info = &c3_isp_rsz_fmts[i];
+
+ if (info->mbus_code == code && info->pads & BIT(pad))
+ return info;
+ }
+
+ return NULL;
+}
+
+static const struct c3_isp_rsz_format_info
+*rsz_find_format_by_index(u32 index, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_rsz_fmts); i++) {
+ const struct c3_isp_rsz_format_info *info = &c3_isp_rsz_fmts[i];
+
+ if (!(info->pads & BIT(pad)))
+ continue;
+
+ if (!index)
+ return info;
+
+ index--;
+ }
+
+ return NULL;
+}
+
+static void c3_isp_rsz_pps_size(struct c3_isp_resizer *rsz,
+ struct c3_isp_pps_io_size *io_size)
+{
+ int thsize = io_size->thsize;
+ int tvsize = io_size->tvsize;
+ u32 ohsize = io_size->ohsize;
+ u32 ovsize = io_size->ovsize;
+ u32 ihsize = io_size->ihsize;
+ u32 max_hsize = io_size->max_hsize;
+ int h_int;
+ int v_int;
+ int h_fract;
+ int v_fract;
+ int yuv444to422_en;
+
+ /* Calculate the integer part of horizonal scaler step */
+ h_int = thsize / ohsize;
+
+ /* Calculate the vertical part of horizonal scaler step */
+ v_int = tvsize / ovsize;
+
+ /*
+ * Calculate the fraction part of horizonal scaler step.
+ * step_h_fraction = (source / dest) * 2^24,
+ * so step_h_fraction = ((source << 12) / dest) << 12.
+ */
+ h_fract = ((thsize << 12) / ohsize) << 12;
+
+ /*
+ * Calculate the fraction part of vertical scaler step
+ * step_v_fraction = (source / dest) * 2^24,
+ * so step_v_fraction = ((source << 12) / dest) << 12.
+ */
+ v_fract = ((tvsize << 12) / ovsize) << 12;
+
+ yuv444to422_en = ihsize > (max_hsize / 2) ? 1 : 0;
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_444TO422, rsz->id),
+ DISP0_PPS_444TO422_EN_MASK,
+ DISP0_PPS_444TO422_EN(yuv444to422_en));
+
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_VSC_START_PHASE_STEP, rsz->id),
+ DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC(v_fract) |
+ DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE(v_int));
+
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_HSC_START_PHASE_STEP, rsz->id),
+ DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC(h_fract) |
+ DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE(h_int));
+}
+
+static void c3_isp_rsz_pps_lut(struct c3_isp_resizer *rsz, u32 ctype)
+{
+ unsigned int i;
+
+ /*
+ * Default value of this register is 0, so only need to set
+ * SCALE_LUMA_COEF_S11_MODE and SCALE_LUMA_CTYPE. This register needs
+ * to be written in one time.
+ */
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_IDX_LUMA, rsz->id),
+ ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_EN |
+ ISP_SCALE0_COEF_IDX_LUMA_CTYPE(ctype));
+
+ for (i = 0; i < C3_ISP_PPS_LUT_H_NUM; i++) {
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_LUMA, rsz->id),
+ ISP_SCALE0_COEF_LUMA_DATA0(c3_isp_pps_lut[i][0]) |
+ ISP_SCALE0_COEF_LUMA_DATA1(c3_isp_pps_lut[i][1]));
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_LUMA, rsz->id),
+ ISP_SCALE0_COEF_LUMA_DATA0(c3_isp_pps_lut[i][2]) |
+ ISP_SCALE0_COEF_LUMA_DATA1(c3_isp_pps_lut[i][3]));
+ }
+
+ /*
+ * Default value of this register is 0, so only need to set
+ * SCALE_CHRO_COEF_S11_MODE and SCALE_CHRO_CTYPE. This register needs
+ * to be written in one time.
+ */
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_IDX_CHRO, rsz->id),
+ ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_EN |
+ ISP_SCALE0_COEF_IDX_CHRO_CTYPE(ctype));
+
+ for (i = 0; i < C3_ISP_PPS_LUT_H_NUM; i++) {
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_CHRO, rsz->id),
+ ISP_SCALE0_COEF_CHRO_DATA0(c3_isp_pps_lut[i][0]) |
+ ISP_SCALE0_COEF_CHRO_DATA1(c3_isp_pps_lut[i][1]));
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_CHRO, rsz->id),
+ ISP_SCALE0_COEF_CHRO_DATA0(c3_isp_pps_lut[i][2]) |
+ ISP_SCALE0_COEF_CHRO_DATA1(c3_isp_pps_lut[i][3]));
+ }
+}
+
+static void c3_isp_rsz_pps_disable(struct c3_isp_resizer *rsz)
+{
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_HSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_HSC_DIS);
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_VSC_DIS);
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_DIS);
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREHSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREHSC_DIS);
+}
+
+static int c3_isp_rsz_pps_enable(struct c3_isp_resizer *rsz,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+ int max_hsize;
+ int hsc_en, vsc_en;
+ int preh_en, prev_en;
+ u32 prehsc_rate;
+ u32 prevsc_flt_num;
+ int pre_vscale_max_hsize;
+ u32 ihsize_after_pre_hsc;
+ u32 ihsize_after_pre_hsc_alt;
+ u32 vsc_tap_num_alt;
+ u32 ihsize;
+ u32 ivsize;
+ struct c3_isp_pps_io_size io_size;
+
+ crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK);
+ cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK);
+
+ ihsize = crop->width;
+ ivsize = crop->height;
+
+ hsc_en = (ihsize == cmps->width) ? C3_ISP_SCL_DIS : C3_ISP_SCL_EN;
+ vsc_en = (ivsize == cmps->height) ? C3_ISP_SCL_DIS : C3_ISP_SCL_EN;
+
+ /* Disable pps when there no need to use pps */
+ if (!hsc_en && !vsc_en) {
+ c3_isp_rsz_pps_disable(rsz);
+ return 0;
+ }
+
+ /* Pre-scale needs to be enable if the down scaling factor exceeds 4 */
+ preh_en = (ihsize > cmps->width * 4) ? C3_ISP_SCL_EN : C3_ISP_SCL_DIS;
+ prev_en = (ivsize > cmps->height * 4) ? C3_ISP_SCL_EN : C3_ISP_SCL_DIS;
+
+ if (rsz->id == C3_ISP_RSZ_2) {
+ max_hsize = C3_ISP_MAX_WIDTH;
+
+ /* Set vertical tap number */
+ prevsc_flt_num = 4;
+
+ /* Set the max hsize of pre-vertical scale */
+ pre_vscale_max_hsize = max_hsize / 2;
+ } else {
+ max_hsize = C3_ISP_DEFAULT_WIDTH;
+
+ /* Set vertical tap number and the max hsize of pre-vertical */
+ if (ihsize > (max_hsize / 2) &&
+ ihsize <= max_hsize && prev_en) {
+ prevsc_flt_num = 2;
+ pre_vscale_max_hsize = max_hsize;
+ } else {
+ prevsc_flt_num = 4;
+ pre_vscale_max_hsize = max_hsize / 2;
+ }
+ }
+
+ /*
+ * Set pre-horizonal scale rate and the hsize of after
+ * pre-horizonal scale.
+ */
+ if (preh_en) {
+ prehsc_rate = 1;
+ ihsize_after_pre_hsc = DIV_ROUND_UP(ihsize, 2);
+ } else {
+ prehsc_rate = 0;
+ ihsize_after_pre_hsc = ihsize;
+ }
+
+ /* Change pre-horizonal scale rate */
+ if (prev_en && ihsize_after_pre_hsc >= pre_vscale_max_hsize)
+ prehsc_rate += 1;
+
+ /* Set the actual hsize of after pre-horizonal scale */
+ if (preh_en)
+ ihsize_after_pre_hsc_alt =
+ DIV_ROUND_UP(ihsize, 1 << prehsc_rate);
+ else
+ ihsize_after_pre_hsc_alt = ihsize;
+
+ /* Set vertical scaler bank length */
+ if (ihsize_after_pre_hsc_alt <= (max_hsize / 2))
+ vsc_tap_num_alt = 4;
+ else if (ihsize_after_pre_hsc_alt <= max_hsize)
+ vsc_tap_num_alt = prev_en ? 2 : 4;
+ else
+ vsc_tap_num_alt = prev_en ? 4 : 2;
+
+ io_size.thsize = ihsize_after_pre_hsc_alt;
+ io_size.tvsize = prev_en ? DIV_ROUND_UP(ivsize, 2) : ivsize;
+ io_size.ohsize = cmps->width;
+ io_size.ovsize = cmps->height;
+ io_size.ihsize = ihsize;
+ io_size.max_hsize = max_hsize;
+
+ c3_isp_rsz_pps_size(rsz, &io_size);
+ c3_isp_rsz_pps_lut(rsz, C3_ISP_PPS_LUT_CTYPE_0);
+ c3_isp_rsz_pps_lut(rsz, C3_ISP_PPS_LUT_CTYPE_2);
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_TAP_NUM_MASK,
+ DISP0_PPS_SCALE_EN_VSC_TAP_NUM(vsc_tap_num_alt));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM(prevsc_flt_num));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_RATE_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_RATE(prev_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREHSC_RATE_MASK,
+ DISP0_PPS_SCALE_EN_PREHSC_RATE(prehsc_rate));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_HSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_HSC_EN(hsc_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_VSC_EN(vsc_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_EN(prev_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREHSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREHSC_EN(preh_en));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS_MASK,
+ DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS(9));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS_MASK,
+ DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS(9));
+
+ return 0;
+}
+
+static void c3_isp_rsz_start(struct c3_isp_resizer *rsz,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ const struct c3_isp_rsz_format_info *rsz_fmt;
+ struct v4l2_rect *sink_crop;
+ u32 mask;
+ u32 val;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK);
+ sink_crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE);
+ rsz_fmt = rsz_find_format_by_code(sink_fmt->code, C3_ISP_RSZ_PAD_SINK);
+
+ if (rsz->id == C3_ISP_RSZ_0) {
+ mask = ISP_TOP_DISPIN_SEL_DISP0_MASK;
+ val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP0_MIPI_OUT
+ : ISP_TOP_DISPIN_SEL_DISP0_CORE_OUT;
+ } else if (rsz->id == C3_ISP_RSZ_1) {
+ mask = ISP_TOP_DISPIN_SEL_DISP1_MASK;
+ val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP1_MIPI_OUT
+ : ISP_TOP_DISPIN_SEL_DISP1_CORE_OUT;
+ } else {
+ mask = ISP_TOP_DISPIN_SEL_DISP2_MASK;
+ val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP2_MIPI_OUT
+ : ISP_TOP_DISPIN_SEL_DISP2_CORE_OUT;
+ }
+
+ c3_isp_update_bits(rsz->isp, ISP_TOP_DISPIN_SEL, mask, val);
+
+ c3_isp_write(rsz->isp, C3_ISP_DISP_REG(ISP_DISP0_TOP_IN_SIZE, rsz->id),
+ ISP_DISP0_TOP_IN_SIZE_HSIZE(sink_fmt->width) |
+ ISP_DISP0_TOP_IN_SIZE_VSIZE(sink_fmt->height));
+
+ c3_isp_write(rsz->isp, C3_ISP_DISP_REG(DISP0_TOP_CRP2_START, rsz->id),
+ DISP0_TOP_CRP2_START_V_START(sink_crop->top) |
+ DISP0_TOP_CRP2_START_H_START(sink_crop->left));
+
+ c3_isp_write(rsz->isp, C3_ISP_DISP_REG(DISP0_TOP_CRP2_SIZE, rsz->id),
+ DISP0_TOP_CRP2_SIZE_V_SIZE(sink_crop->height) |
+ DISP0_TOP_CRP2_SIZE_H_SIZE(sink_crop->width));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_TOP_CTRL, rsz->id),
+ DISP0_TOP_TOP_CTRL_CROP2_EN_MASK,
+ DISP0_TOP_TOP_CTRL_CROP2_EN);
+
+ if (!rsz_fmt->is_raw)
+ c3_isp_rsz_pps_enable(rsz, state);
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_OUT_SIZE, rsz->id),
+ DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT_MASK,
+ DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT(src_fmt->height));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_OUT_SIZE, rsz->id),
+ DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH_MASK,
+ DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH(src_fmt->width));
+
+ if (rsz->id == C3_ISP_RSZ_0) {
+ mask = ISP_TOP_PATH_EN_DISP0_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP0_EN;
+ } else if (rsz->id == C3_ISP_RSZ_1) {
+ mask = ISP_TOP_PATH_EN_DISP1_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP1_EN;
+ } else {
+ mask = ISP_TOP_PATH_EN_DISP2_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP2_EN;
+ }
+
+ c3_isp_update_bits(rsz->isp, ISP_TOP_PATH_EN, mask, val);
+}
+
+static void c3_isp_rsz_stop(struct c3_isp_resizer *rsz)
+{
+ u32 mask;
+ u32 val;
+
+ if (rsz->id == C3_ISP_RSZ_0) {
+ mask = ISP_TOP_PATH_EN_DISP0_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP0_DIS;
+ } else if (rsz->id == C3_ISP_RSZ_1) {
+ mask = ISP_TOP_PATH_EN_DISP1_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP1_DIS;
+ } else {
+ mask = ISP_TOP_PATH_EN_DISP2_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP2_DIS;
+ }
+
+ c3_isp_update_bits(rsz->isp, ISP_TOP_PATH_EN, mask, val);
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_TOP_CTRL, rsz->id),
+ DISP0_TOP_TOP_CTRL_CROP2_EN_MASK,
+ DISP0_TOP_TOP_CTRL_CROP2_DIS);
+
+ c3_isp_rsz_pps_disable(rsz);
+}
+
+static int c3_isp_rsz_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_resizer *rsz = v4l2_get_subdevdata(sd);
+
+ c3_isp_rsz_start(rsz, state);
+
+ c3_isp_params_pre_cfg(rsz->isp);
+ c3_isp_stats_pre_cfg(rsz->isp);
+
+ return v4l2_subdev_enable_streams(rsz->src_sd, rsz->src_pad, BIT(0));
+}
+
+static int c3_isp_rsz_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_resizer *rsz = v4l2_get_subdevdata(sd);
+
+ c3_isp_rsz_stop(rsz);
+
+ return v4l2_subdev_disable_streams(rsz->src_sd, rsz->src_pad, BIT(0));
+}
+
+static int c3_isp_rsz_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct c3_isp_rsz_format_info *info;
+
+ info = rsz_find_format_by_index(code->index, code->pad);
+ if (!info)
+ return -EINVAL;
+
+ code->code = info->mbus_code;
+
+ return 0;
+}
+
+static void c3_isp_rsz_set_sink_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_rect *sink_crop;
+ struct v4l2_rect *sink_cmps;
+ const struct c3_isp_rsz_format_info *rsz_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, format->pad);
+ sink_crop = v4l2_subdev_state_get_crop(state, format->pad);
+ sink_cmps = v4l2_subdev_state_get_compose(state, format->pad);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE);
+
+ rsz_fmt = rsz_find_format_by_code(format->format.code, format->pad);
+ if (rsz_fmt)
+ sink_fmt->code = format->format.code;
+ else
+ sink_fmt->code = C3_ISP_RSZ_DEF_PAD_FMT;
+
+ sink_fmt->width = clamp_t(u32, format->format.width,
+ C3_ISP_MIN_WIDTH, C3_ISP_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, format->format.height,
+ C3_ISP_MIN_HEIGHT, C3_ISP_MAX_HEIGHT);
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ if (rsz_fmt && rsz_fmt->is_raw) {
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ } else {
+ sink_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ sink_crop->width = sink_fmt->width;
+ sink_crop->height = sink_fmt->height;
+ sink_crop->left = 0;
+ sink_crop->top = 0;
+
+ sink_cmps->width = sink_crop->width;
+ sink_cmps->height = sink_crop->height;
+ sink_cmps->left = 0;
+ sink_cmps->top = 0;
+
+ src_fmt->code = sink_fmt->code;
+ src_fmt->width = sink_cmps->width;
+ src_fmt->height = sink_cmps->height;
+
+ format->format = *sink_fmt;
+}
+
+static void c3_isp_rsz_set_source_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *sink_cmps;
+ const struct c3_isp_rsz_format_info *rsz_fmt;
+
+ src_fmt = v4l2_subdev_state_get_format(state, format->pad);
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK);
+ sink_cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK);
+
+ src_fmt->code = sink_fmt->code;
+ src_fmt->width = sink_cmps->width;
+ src_fmt->height = sink_cmps->height;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ rsz_fmt = rsz_find_format_by_code(src_fmt->code, format->pad);
+ if (rsz_fmt->is_raw) {
+ src_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ } else {
+ src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ format->format = *src_fmt;
+}
+
+static int c3_isp_rsz_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ if (format->pad == C3_ISP_RSZ_PAD_SINK)
+ c3_isp_rsz_set_sink_fmt(state, format);
+ else
+ c3_isp_rsz_set_source_fmt(state, format);
+
+ return 0;
+}
+
+static int c3_isp_rsz_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+
+ if (sel->pad == C3_ISP_RSZ_PAD_SOURCE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ fmt = v4l2_subdev_state_get_format(state, sel->pad);
+ sel->r.width = fmt->width;
+ sel->r.height = fmt->height;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ sel->r.width = crop->width;
+ sel->r.height = crop->height;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ sel->r = *crop;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ cmps = v4l2_subdev_state_get_compose(state, sel->pad);
+ sel->r = *cmps;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_isp_rsz_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+
+ if (sel->pad == C3_ISP_RSZ_PAD_SOURCE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ fmt = v4l2_subdev_state_get_format(state, sel->pad);
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ cmps = v4l2_subdev_state_get_compose(state, sel->pad);
+ src_fmt = v4l2_subdev_state_get_format(state,
+ C3_ISP_RSZ_PAD_SOURCE);
+
+ sel->r.left = clamp_t(s32, sel->r.left, 0, fmt->width - 1);
+ sel->r.top = clamp_t(s32, sel->r.top, 0, fmt->height - 1);
+ sel->r.width = clamp(sel->r.width, C3_ISP_MIN_WIDTH,
+ fmt->width - sel->r.left);
+ sel->r.height = clamp(sel->r.height, C3_ISP_MIN_HEIGHT,
+ fmt->height - sel->r.top);
+
+ crop->width = ALIGN(sel->r.width, 2);
+ crop->height = ALIGN(sel->r.height, 2);
+ crop->left = sel->r.left;
+ crop->top = sel->r.top;
+
+ *cmps = *crop;
+
+ src_fmt->code = fmt->code;
+ src_fmt->width = cmps->width;
+ src_fmt->height = cmps->height;
+
+ sel->r = *crop;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ cmps = v4l2_subdev_state_get_compose(state, sel->pad);
+
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = clamp(sel->r.width, C3_ISP_MIN_WIDTH,
+ crop->width);
+ sel->r.height = clamp(sel->r.height, C3_ISP_MIN_HEIGHT,
+ crop->height);
+
+ cmps->width = ALIGN(sel->r.width, 2);
+ cmps->height = ALIGN(sel->r.height, 2);
+ cmps->left = sel->r.left;
+ cmps->top = sel->r.top;
+
+ sel->r = *cmps;
+
+ fmt = v4l2_subdev_state_get_format(state,
+ C3_ISP_RSZ_PAD_SOURCE);
+ fmt->width = cmps->width;
+ fmt->height = cmps->height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_isp_rsz_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK);
+ fmt->width = C3_ISP_DEFAULT_WIDTH;
+ fmt->height = C3_ISP_DEFAULT_HEIGHT;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_RSZ_DEF_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK);
+ crop->width = C3_ISP_DEFAULT_WIDTH;
+ crop->height = C3_ISP_DEFAULT_HEIGHT;
+ crop->left = 0;
+ crop->top = 0;
+
+ cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK);
+ cmps->width = C3_ISP_DEFAULT_WIDTH;
+ cmps->height = C3_ISP_DEFAULT_HEIGHT;
+ cmps->left = 0;
+ cmps->top = 0;
+
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE);
+ fmt->width = cmps->width;
+ fmt->height = cmps->height;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_RSZ_DEF_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops c3_isp_rsz_pad_ops = {
+ .enum_mbus_code = c3_isp_rsz_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_isp_rsz_set_fmt,
+ .get_selection = c3_isp_rsz_get_selection,
+ .set_selection = c3_isp_rsz_set_selection,
+ .enable_streams = c3_isp_rsz_enable_streams,
+ .disable_streams = c3_isp_rsz_disable_streams,
+};
+
+static const struct v4l2_subdev_ops c3_isp_rsz_subdev_ops = {
+ .pad = &c3_isp_rsz_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_isp_rsz_internal_ops = {
+ .init_state = c3_isp_rsz_init_state,
+};
+
+/* Media entity operations */
+static const struct media_entity_operations c3_isp_rsz_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int c3_isp_rsz_register(struct c3_isp_resizer *rsz)
+{
+ struct v4l2_subdev *sd = &rsz->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_isp_rsz_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &c3_isp_rsz_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "c3-isp-resizer%u", rsz->id);
+
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+ sd->entity.ops = &c3_isp_rsz_entity_ops;
+
+ sd->dev = rsz->isp->dev;
+ v4l2_set_subdevdata(sd, rsz);
+
+ rsz->pads[C3_ISP_RSZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ rsz->pads[C3_ISP_RSZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_ISP_RSZ_PAD_MAX,
+ rsz->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(&rsz->isp->v4l2_dev, sd);
+ if (ret)
+ goto err_subdev_cleanup;
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+static void c3_isp_rsz_unregister(struct c3_isp_resizer *rsz)
+{
+ struct v4l2_subdev *sd = &rsz->sd;
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+}
+
+int c3_isp_resizers_register(struct c3_isp_device *isp)
+{
+ int ret;
+
+ for (unsigned int i = C3_ISP_RSZ_0; i < C3_ISP_NUM_RSZ; i++) {
+ struct c3_isp_resizer *rsz = &isp->resizers[i];
+
+ rsz->id = i;
+ rsz->isp = isp;
+ rsz->src_sd = &isp->core.sd;
+ rsz->src_pad = C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i;
+
+ ret = c3_isp_rsz_register(rsz);
+ if (ret) {
+ rsz->isp = NULL;
+ c3_isp_resizers_unregister(isp);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void c3_isp_resizers_unregister(struct c3_isp_device *isp)
+{
+ for (unsigned int i = C3_ISP_RSZ_0; i < C3_ISP_NUM_RSZ; i++) {
+ struct c3_isp_resizer *rsz = &isp->resizers[i];
+
+ if (rsz->isp)
+ c3_isp_rsz_unregister(rsz);
+ }
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c
new file mode 100644
index 000000000000..8a5d7e1a30c9
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/cleanup.h>
+#include <linux/media/amlogic/c3-isp-config.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+/* Hardware configuration */
+
+static void c3_isp_stats_cfg_dmawr_addr(struct c3_isp_stats *stats)
+{
+ u32 awb_dma_size = sizeof(struct c3_isp_awb_stats);
+ u32 ae_dma_size = sizeof(struct c3_isp_ae_stats);
+ u32 awb_dma_addr = stats->buff->dma_addr;
+ u32 af_dma_addr;
+ u32 ae_dma_addr;
+
+ ae_dma_addr = awb_dma_addr + awb_dma_size;
+ af_dma_addr = ae_dma_addr + ae_dma_size;
+
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR0,
+ VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR_MASK,
+ VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR(af_dma_addr));
+
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR1,
+ VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR_MASK,
+ VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR(awb_dma_addr));
+
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR2,
+ VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR_MASK,
+ VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR(ae_dma_addr));
+}
+
+static void c3_isp_stats_cfg_buff(struct c3_isp_stats *stats)
+{
+ stats->buff =
+ list_first_entry_or_null(&stats->pending,
+ struct c3_isp_stats_buffer, list);
+ if (stats->buff) {
+ c3_isp_stats_cfg_dmawr_addr(stats);
+ list_del(&stats->buff->list);
+ }
+}
+
+void c3_isp_stats_pre_cfg(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+ u32 dma_size;
+
+ c3_isp_update_bits(stats->isp, ISP_AF_EN_CTRL,
+ ISP_AF_EN_CTRL_STAT_SEL_MASK,
+ ISP_AF_EN_CTRL_STAT_SEL_NEW);
+ c3_isp_update_bits(stats->isp, ISP_AE_CTRL,
+ ISP_AE_CTRL_LUMA_MODE_MASK,
+ ISP_AE_CTRL_LUMA_MODE_FILTER);
+
+ /* The unit of dma_size is 16 bytes */
+ dma_size = sizeof(struct c3_isp_af_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES;
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE0,
+ VIU_DMAWR_SIZE0_AF_STATS_SIZE_MASK,
+ VIU_DMAWR_SIZE0_AF_STATS_SIZE(dma_size));
+
+ dma_size = sizeof(struct c3_isp_awb_stats) /
+ C3_ISP_DMA_SIZE_ALIGN_BYTES;
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE0,
+ VIU_DMAWR_SIZE0_AWB_STATS_SIZE_MASK,
+ VIU_DMAWR_SIZE0_AWB_STATS_SIZE(dma_size));
+
+ dma_size = sizeof(struct c3_isp_ae_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES;
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE1,
+ VIU_DMAWR_SIZE1_AE_STATS_SIZE_MASK,
+ VIU_DMAWR_SIZE1_AE_STATS_SIZE(dma_size));
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ c3_isp_stats_cfg_buff(stats);
+}
+
+static int c3_isp_stats_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "AML C3 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static int c3_isp_stats_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct c3_isp_stats *stats = video_drvdata(file);
+
+ if (f->index > 0 || f->type != stats->vb2_q.type)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_C3ISP_STATS;
+
+ return 0;
+}
+
+static int c3_isp_stats_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_stats *stats = video_drvdata(file);
+
+ f->fmt.meta = stats->vfmt.fmt.meta;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp_stats_v4l2_ioctl_ops = {
+ .vidioc_querycap = c3_isp_stats_querycap,
+ .vidioc_enum_fmt_meta_cap = c3_isp_stats_enum_fmt,
+ .vidioc_g_fmt_meta_cap = c3_isp_stats_g_fmt,
+ .vidioc_s_fmt_meta_cap = c3_isp_stats_g_fmt,
+ .vidioc_try_fmt_meta_cap = c3_isp_stats_g_fmt,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct v4l2_file_operations isp_stats_v4l2_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int c3_isp_stats_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+
+ if (sizes[0] < sizeof(struct c3_isp_stats_info))
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *num_planes = 1;
+ sizes[0] = sizeof(struct c3_isp_stats_info);
+
+ return 0;
+}
+
+static void c3_isp_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_stats_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_stats_buffer, vb);
+ struct c3_isp_stats *stats = vb2_get_drv_priv(vb->vb2_queue);
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ list_add_tail(&buf->list, &stats->pending);
+}
+
+static int c3_isp_stats_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct c3_isp_stats *stats = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned int size = stats->vfmt.fmt.meta.buffersize;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(stats->isp->dev,
+ "User buffer too small (%ld < %u)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ return 0;
+}
+
+static int c3_isp_stats_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_stats_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_stats_buffer, vb);
+
+ buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ return 0;
+}
+
+static void c3_isp_stats_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct c3_isp_stats *stats = vb2_get_drv_priv(q);
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ if (stats->buff) {
+ vb2_buffer_done(&stats->buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ stats->buff = NULL;
+ }
+
+ while (!list_empty(&stats->pending)) {
+ struct c3_isp_stats_buffer *buff;
+
+ buff = list_first_entry(&stats->pending,
+ struct c3_isp_stats_buffer, list);
+ list_del(&buff->list);
+ vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops isp_stats_vb2_ops = {
+ .queue_setup = c3_isp_stats_vb2_queue_setup,
+ .buf_queue = c3_isp_stats_vb2_buf_queue,
+ .buf_prepare = c3_isp_stats_vb2_buf_prepare,
+ .buf_init = c3_isp_stats_vb2_buf_init,
+ .stop_streaming = c3_isp_stats_vb2_stop_streaming,
+};
+
+int c3_isp_stats_register(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+ struct video_device *vdev = &stats->vdev;
+ struct vb2_queue *vb2_q = &stats->vb2_q;
+ int ret;
+
+ memset(stats, 0, sizeof(*stats));
+ stats->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_STATS;
+ stats->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_stats_info);
+ stats->isp = isp;
+ INIT_LIST_HEAD(&stats->pending);
+ spin_lock_init(&stats->buff_lock);
+
+ mutex_init(&stats->lock);
+
+ snprintf(vdev->name, sizeof(vdev->name), "c3-isp-stats");
+ vdev->fops = &isp_stats_v4l2_fops;
+ vdev->ioctl_ops = &isp_stats_v4l2_ioctl_ops;
+ vdev->v4l2_dev = &isp->v4l2_dev;
+ vdev->lock = &stats->lock;
+ vdev->minor = -1;
+ vdev->queue = vb2_q;
+ vdev->release = video_device_release_empty;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ video_set_drvdata(vdev, stats);
+
+ vb2_q->drv_priv = stats;
+ vb2_q->mem_ops = &vb2_dma_contig_memops;
+ vb2_q->ops = &isp_stats_vb2_ops;
+ vb2_q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ vb2_q->io_modes = VB2_DMABUF | VB2_MMAP;
+ vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2_q->buf_struct_size = sizeof(struct c3_isp_stats_buffer);
+ vb2_q->dev = isp->dev;
+ vb2_q->lock = &stats->lock;
+ vb2_q->min_queued_buffers = 2;
+
+ ret = vb2_queue_init(vb2_q);
+ if (ret)
+ goto err_destroy;
+
+ stats->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, &stats->pad);
+ if (ret)
+ goto err_queue_release;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(isp->dev,
+ "Failed to register %s: %d\n", vdev->name, ret);
+ goto err_entity_cleanup;
+ }
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_queue_release:
+ vb2_queue_release(vb2_q);
+err_destroy:
+ mutex_destroy(&stats->lock);
+ return ret;
+}
+
+void c3_isp_stats_unregister(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+
+ vb2_queue_release(&stats->vb2_q);
+ media_entity_cleanup(&stats->vdev.entity);
+ video_unregister_device(&stats->vdev);
+ mutex_destroy(&stats->lock);
+}
+
+void c3_isp_stats_isr(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ if (stats->buff) {
+ stats->buff->vb.sequence = stats->isp->frm_sequence;
+ stats->buff->vb.vb2_buf.timestamp = ktime_get();
+ stats->buff->vb.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&stats->buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ c3_isp_stats_cfg_buff(stats);
+}
diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig b/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig
new file mode 100644
index 000000000000..bf19059b3543
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_C3_MIPI_ADAPTER
+ tristate "Amlogic C3 MIPI adapter"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on OF
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ Video4Linux2 driver for Amlogic C3 MIPI adapter.
+ C3 MIPI adapter mainly responsible for organizing
+ MIPI data and sending raw data to ISP pipeline.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile b/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile
new file mode 100644
index 000000000000..216fc310c5b4
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_VIDEO_C3_MIPI_ADAPTER) += c3-mipi-adap.o
diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c
new file mode 100644
index 000000000000..4bd98fb9c7e9
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c
@@ -0,0 +1,842 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/*
+ * Adapter Block Diagram
+ * ---------------------
+ *
+ * +--------------------------------------------+
+ * | Adapter |
+ * |--------------------------------------------|
+ * +------------+ | | | | | +-----+
+ * | MIPI CSI-2 |--->| Frontend -> DDR_RD0 -> PIXEL0 -> ALIGNMENT |--->| ISP |
+ * +------------+ | | | | | +-----+
+ * +--------------------------------------------+
+ *
+ */
+
+/* C3 adapter submodule definition */
+enum {
+ SUBMD_TOP,
+ SUBMD_FD,
+ SUBMD_RD,
+};
+
+#define ADAP_SUBMD_MASK GENMASK(17, 16)
+#define ADAP_SUBMD_SHIFT 16
+#define ADAP_SUBMD(x) (((x) & (ADAP_SUBMD_MASK)) >> (ADAP_SUBMD_SHIFT))
+#define ADAP_REG_ADDR_MASK GENMASK(15, 0)
+#define ADAP_REG_ADDR(x) ((x) & (ADAP_REG_ADDR_MASK))
+#define ADAP_REG_T(x) ((SUBMD_TOP << ADAP_SUBMD_SHIFT) | (x))
+#define ADAP_REG_F(x) ((SUBMD_FD << ADAP_SUBMD_SHIFT) | (x))
+#define ADAP_REG_R(x) ((SUBMD_RD << ADAP_SUBMD_SHIFT) | (x))
+
+#define MIPI_ADAP_CLOCK_NUM_MAX 3
+#define MIPI_ADAP_SUBDEV_NAME "c3-mipi-adapter"
+
+/* C3 MIPI adapter TOP register */
+#define MIPI_TOP_CTRL0 ADAP_REG_T(0x00)
+#define MIPI_TOP_CTRL0_RST_ADAPTER_MASK BIT(1)
+#define MIPI_TOP_CTRL0_RST_ADAPTER_APPLY BIT(1)
+#define MIPI_TOP_CTRL0_RST_ADAPTER_EXIT (0 << 1)
+
+#define MIPI_ADAPT_DE_CTRL0 ADAP_REG_T(0x40)
+#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK BIT(3)
+#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN BIT(3)
+#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_DIS (0 << 3)
+#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK BIT(7)
+#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN BIT(7)
+#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_DIS (0 << 7)
+
+/* C3 MIPI adapter FRONTEND register */
+#define CSI2_CLK_RESET ADAP_REG_F(0x00)
+#define CSI2_CLK_RESET_SW_RESET_MASK BIT(0)
+#define CSI2_CLK_RESET_SW_RESET_APPLY BIT(0)
+#define CSI2_CLK_RESET_SW_RESET_RELEASE (0 << 0)
+#define CSI2_CLK_RESET_CLK_ENABLE_MASK BIT(1)
+#define CSI2_CLK_RESET_CLK_ENABLE_EN BIT(1)
+#define CSI2_CLK_RESET_CLK_ENABLE_DIS (0 << 1)
+
+#define CSI2_GEN_CTRL0 ADAP_REG_F(0x04)
+#define CSI2_GEN_CTRL0_VC0_MASK BIT(0)
+#define CSI2_GEN_CTRL0_VC0_EN BIT(0)
+#define CSI2_GEN_CTRL0_VC0_DIS (0 << 0)
+#define CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK GENMASK(20, 16)
+#define CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW BIT(16)
+#define CSI2_GEN_CTRL0_ENABLE_PACKETS_YUV (2 << 16)
+
+#define CSI2_X_START_END_ISP ADAP_REG_F(0x0c)
+#define CSI2_X_START_END_ISP_X_START_MASK GENMASK(15, 0)
+#define CSI2_X_START_END_ISP_X_START(x) ((x) << 0)
+#define CSI2_X_START_END_ISP_X_END_MASK GENMASK(31, 16)
+#define CSI2_X_START_END_ISP_X_END(x) (((x) - 1) << 16)
+
+#define CSI2_Y_START_END_ISP ADAP_REG_F(0x10)
+#define CSI2_Y_START_END_ISP_Y_START_MASK GENMASK(15, 0)
+#define CSI2_Y_START_END_ISP_Y_START(x) ((x) << 0)
+#define CSI2_Y_START_END_ISP_Y_END_MASK GENMASK(31, 16)
+#define CSI2_Y_START_END_ISP_Y_END(x) (((x) - 1) << 16)
+
+#define CSI2_VC_MODE ADAP_REG_F(0x1c)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_MASK GENMASK(19, 16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_0 BIT(16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_1 (2 << 16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_2 (4 << 16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_3 (8 << 16)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_MASK GENMASK(23, 20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_0 BIT(20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_1 (2 << 20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_2 (4 << 20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_3 (8 << 20)
+
+/* C3 MIPI adapter READER register */
+#define MIPI_ADAPT_DDR_RD0_CNTL0 ADAP_REG_R(0x00)
+#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK BIT(0)
+#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN BIT(0)
+#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_DIS (0 << 0)
+
+#define MIPI_ADAPT_DDR_RD0_CNTL1 ADAP_REG_R(0x04)
+#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK GENMASK(31, 30)
+#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE (0 << 30)
+#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DDR_MODE BIT(30)
+
+#define MIPI_ADAPT_PIXEL0_CNTL0 ADAP_REG_R(0x80)
+#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK GENMASK(17, 16)
+#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DDR (0 << 16)
+#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT BIT(16)
+#define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK GENMASK(25, 20)
+#define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(x) ((x) << 20)
+#define MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK BIT(31)
+#define MIPI_ADAPT_PIXEL0_CNTL0_START_EN BIT(31)
+
+#define MIPI_ADAPT_ALIG_CNTL0 ADAP_REG_R(0x100)
+#define MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK GENMASK(15, 0)
+#define MIPI_ADAPT_ALIG_CNTL0_H_NUM(x) ((x) << 0)
+#define MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK GENMASK(31, 16)
+#define MIPI_ADAPT_ALIG_CNTL0_V_NUM(x) ((x) << 16)
+
+#define MIPI_ADAPT_ALIG_CNTL1 ADAP_REG_R(0x104)
+#define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK GENMASK(31, 16)
+#define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(x) ((x) << 16)
+
+#define MIPI_ADAPT_ALIG_CNTL2 ADAP_REG_R(0x108)
+#define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK GENMASK(31, 16)
+#define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(x) ((x) << 16)
+
+#define MIPI_ADAPT_ALIG_CNTL6 ADAP_REG_R(0x118)
+#define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK BIT(0)
+#define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN BIT(0)
+#define MIPI_ADAPT_ALIG_CNTL6_PATH0_DIS (0 << 0)
+#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK BIT(4)
+#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DDR (0 << 4)
+#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT BIT(4)
+#define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL6_DATA0_DIS (0 << 12)
+
+#define MIPI_ADAPT_ALIG_CNTL8 ADAP_REG_R(0x120)
+#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK BIT(5)
+#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN BIT(5)
+#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_DIS (0 << 5)
+#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_HOLD (0 << 12)
+#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK BIT(31)
+#define MIPI_ADAPT_ALIG_CNTL8_START_EN BIT(31)
+
+#define MIPI_ADAP_MAX_WIDTH 2888
+#define MIPI_ADAP_MIN_WIDTH 160
+#define MIPI_ADAP_MAX_HEIGHT 2240
+#define MIPI_ADAP_MIN_HEIGHT 120
+#define MIPI_ADAP_DEFAULT_WIDTH 1920
+#define MIPI_ADAP_DEFAULT_HEIGHT 1080
+#define MIPI_ADAP_DEFAULT_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+
+/* C3 MIPI adapter pad list */
+enum {
+ C3_MIPI_ADAP_PAD_SINK,
+ C3_MIPI_ADAP_PAD_SRC,
+ C3_MIPI_ADAP_PAD_MAX
+};
+
+/*
+ * struct c3_adap_info - mipi adapter information
+ *
+ * @clocks: array of mipi adapter clock names
+ * @clock_num: actual clock number
+ */
+struct c3_adap_info {
+ char *clocks[MIPI_ADAP_CLOCK_NUM_MAX];
+ u32 clock_num;
+};
+
+/*
+ * struct c3_adap_device - mipi adapter platform device
+ *
+ * @dev: pointer to the struct device
+ * @top: mipi adapter top register address
+ * @fd: mipi adapter frontend register address
+ * @rd: mipi adapter reader register address
+ * @clks: array of MIPI adapter clocks
+ * @sd: mipi adapter sub-device
+ * @pads: mipi adapter sub-device pads
+ * @notifier: notifier to register on the v4l2-async API
+ * @src_sd: source sub-device pad
+ * @info: version-specific MIPI adapter information
+ */
+struct c3_adap_device {
+ struct device *dev;
+ void __iomem *top;
+ void __iomem *fd;
+ void __iomem *rd;
+ struct clk_bulk_data clks[MIPI_ADAP_CLOCK_NUM_MAX];
+
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_MIPI_ADAP_PAD_MAX];
+ struct v4l2_async_notifier notifier;
+ struct media_pad *src_pad;
+
+ const struct c3_adap_info *info;
+};
+
+/* Format helpers */
+
+struct c3_adap_pix_format {
+ u32 code;
+ u8 type;
+};
+
+static const struct c3_adap_pix_format c3_mipi_adap_formats[] = {
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 },
+};
+
+static const struct c3_adap_pix_format *c3_mipi_adap_find_format(u32 code)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_mipi_adap_formats); i++)
+ if (code == c3_mipi_adap_formats[i].code)
+ return &c3_mipi_adap_formats[i];
+
+ return NULL;
+}
+
+/* Hardware configuration */
+
+static void c3_mipi_adap_update_bits(struct c3_adap_device *adap, u32 reg,
+ u32 mask, u32 val)
+{
+ void __iomem *addr;
+ u32 orig, tmp;
+
+ switch (ADAP_SUBMD(reg)) {
+ case SUBMD_TOP:
+ addr = adap->top + ADAP_REG_ADDR(reg);
+ break;
+ case SUBMD_FD:
+ addr = adap->fd + ADAP_REG_ADDR(reg);
+ break;
+ case SUBMD_RD:
+ addr = adap->rd + ADAP_REG_ADDR(reg);
+ break;
+ default:
+ dev_err(adap->dev,
+ "Invalid sub-module: %lu\n", ADAP_SUBMD(reg));
+ return;
+ }
+
+ orig = readl(addr);
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ if (tmp != orig)
+ writel(tmp, addr);
+}
+
+/* Configure adapter top sub module */
+static void c3_mipi_adap_cfg_top(struct c3_adap_device *adap)
+{
+ /* Reset adapter */
+ c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0,
+ MIPI_TOP_CTRL0_RST_ADAPTER_MASK,
+ MIPI_TOP_CTRL0_RST_ADAPTER_APPLY);
+ c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0,
+ MIPI_TOP_CTRL0_RST_ADAPTER_MASK,
+ MIPI_TOP_CTRL0_RST_ADAPTER_EXIT);
+
+ /* Bypass decompress */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0,
+ MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK,
+ MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN);
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0,
+ MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK,
+ MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN);
+}
+
+/* Configure adapter frontend sub module */
+static void c3_mipi_adap_cfg_frontend(struct c3_adap_device *adap,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ /* Reset frontend module */
+ c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET,
+ CSI2_CLK_RESET_SW_RESET_MASK,
+ CSI2_CLK_RESET_SW_RESET_APPLY);
+ c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET,
+ CSI2_CLK_RESET_SW_RESET_MASK,
+ CSI2_CLK_RESET_SW_RESET_RELEASE);
+ c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET,
+ CSI2_CLK_RESET_CLK_ENABLE_MASK,
+ CSI2_CLK_RESET_CLK_ENABLE_EN);
+
+ c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP,
+ CSI2_X_START_END_ISP_X_START_MASK,
+ CSI2_X_START_END_ISP_X_START(0));
+ c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP,
+ CSI2_X_START_END_ISP_X_END_MASK,
+ CSI2_X_START_END_ISP_X_END(fmt->width));
+
+ c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP,
+ CSI2_Y_START_END_ISP_Y_START_MASK,
+ CSI2_Y_START_END_ISP_Y_START(0));
+ c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP,
+ CSI2_Y_START_END_ISP_Y_END_MASK,
+ CSI2_Y_START_END_ISP_Y_END(fmt->height));
+
+ /* Select VS and HS signal for direct path */
+ c3_mipi_adap_update_bits(adap, CSI2_VC_MODE,
+ CSI2_VC_MODE_VS_ISP_SEL_VC_MASK,
+ CSI2_VC_MODE_VS_ISP_SEL_VC_0);
+ c3_mipi_adap_update_bits(adap, CSI2_VC_MODE,
+ CSI2_VC_MODE_HS_ISP_SEL_VC_MASK,
+ CSI2_VC_MODE_HS_ISP_SEL_VC_0);
+
+ /* Enable to receive RAW packet */
+ c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0,
+ CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK,
+ CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW);
+
+ /* Enable virtual channel 0 */
+ c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0,
+ CSI2_GEN_CTRL0_VC0_MASK,
+ CSI2_GEN_CTRL0_VC0_EN);
+}
+
+static void c3_mipi_adap_cfg_rd0(struct c3_adap_device *adap)
+{
+ /* Select direct mode for DDR_RD0 mode */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL1,
+ MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK,
+ MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE);
+
+ /* Data can't bypass DDR_RD0 in direct mode, so enable DDR_RD0 here */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL0,
+ MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK,
+ MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN);
+}
+
+static void c3_mipi_adap_cfg_pixel0(struct c3_adap_device *adap,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ const struct c3_adap_pix_format *pix;
+
+ pix = c3_mipi_adap_find_format(fmt->code);
+
+ /* Set work mode and data type for PIXEL0 module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0,
+ MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK,
+ MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT);
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0,
+ MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK,
+ MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(pix->type));
+
+ /* Start PIXEL0 module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0,
+ MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK,
+ MIPI_ADAPT_PIXEL0_CNTL0_START_EN);
+}
+
+static void c3_mipi_adap_cfg_alig(struct c3_adap_device *adap,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ /*
+ * ISP hardware requires the number of horizonal blanks greater than
+ * 64 cycles, so adding 64 here.
+ */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0,
+ MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL0_H_NUM(fmt->width + 64));
+
+ /*
+ * ISP hardware requires the number of vertical blanks greater than
+ * 40 lines, so adding 40 here.
+ */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0,
+ MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL0_V_NUM(fmt->height + 40));
+
+ /* End pixel in a line */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL1,
+ MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(fmt->width));
+
+ /* End line in a frame */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL2,
+ MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(fmt->height));
+
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6,
+ MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK,
+ MIPI_ADAPT_ALIG_CNTL6_PATH0_EN);
+
+ /* Select direct mode for ALIG module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6,
+ MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK,
+ MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT);
+
+ /* Enable to send raw data */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6,
+ MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK,
+ MIPI_ADAPT_ALIG_CNTL6_DATA0_EN);
+
+ /* Set continue mode and disable hold counter */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8,
+ MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK,
+ MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN);
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8,
+ MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK,
+ MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD);
+
+ /* Start ALIG module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8,
+ MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK,
+ MIPI_ADAPT_ALIG_CNTL8_START_EN);
+}
+
+/* V4L2 subdev operations */
+
+static int c3_mipi_adap_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_adap_device *adap = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ struct media_pad *sink_pad;
+ struct v4l2_subdev *src_sd;
+ int ret;
+
+ sink_pad = &adap->pads[C3_MIPI_ADAP_PAD_SINK];
+ adap->src_pad = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(adap->src_pad)) {
+ dev_dbg(adap->dev, "Failed to get source pad for MIPI adap\n");
+ return -EPIPE;
+ }
+
+ src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity);
+
+ pm_runtime_resume_and_get(adap->dev);
+
+ fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK);
+
+ c3_mipi_adap_cfg_top(adap);
+ c3_mipi_adap_cfg_frontend(adap, fmt);
+ c3_mipi_adap_cfg_rd0(adap);
+ c3_mipi_adap_cfg_pixel0(adap, fmt);
+ c3_mipi_adap_cfg_alig(adap, fmt);
+
+ ret = v4l2_subdev_enable_streams(src_sd, adap->src_pad->index, BIT(0));
+ if (ret) {
+ pm_runtime_put(adap->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_adap_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_adap_device *adap = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd;
+
+ if (adap->src_pad) {
+ src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity);
+ v4l2_subdev_disable_streams(src_sd, adap->src_pad->index,
+ BIT(0));
+ }
+ adap->src_pad = NULL;
+
+ pm_runtime_put(adap->dev);
+
+ return 0;
+}
+
+static int c3_mipi_adap_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ switch (code->pad) {
+ case C3_MIPI_ADAP_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(c3_mipi_adap_formats))
+ return -EINVAL;
+
+ code->code = c3_mipi_adap_formats[code->index].code;
+ break;
+ case C3_MIPI_ADAP_PAD_SRC:
+ if (code->index)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(state, code->pad);
+ code->code = fmt->code;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_adap_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ const struct c3_adap_pix_format *pix_format;
+
+ if (format->pad != C3_MIPI_ADAP_PAD_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ pix_format = c3_mipi_adap_find_format(format->format.code);
+ if (!pix_format)
+ pix_format = &c3_mipi_adap_formats[0];
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+ fmt->code = pix_format->code;
+ fmt->width = clamp_t(u32, format->format.width,
+ MIPI_ADAP_MIN_WIDTH, MIPI_ADAP_MAX_WIDTH);
+ fmt->height = clamp_t(u32, format->format.height,
+ MIPI_ADAP_MIN_HEIGHT, MIPI_ADAP_MAX_HEIGHT);
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ format->format = *fmt;
+
+ /* Synchronize the format to source pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC);
+ *fmt = format->format;
+
+ return 0;
+}
+
+static int c3_mipi_adap_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC);
+
+ sink_fmt->width = MIPI_ADAP_DEFAULT_WIDTH;
+ sink_fmt->height = MIPI_ADAP_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = MIPI_ADAP_DEFAULT_FMT;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops c3_mipi_adap_pad_ops = {
+ .enum_mbus_code = c3_mipi_adap_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_mipi_adap_set_fmt,
+ .enable_streams = c3_mipi_adap_enable_streams,
+ .disable_streams = c3_mipi_adap_disable_streams,
+};
+
+static const struct v4l2_subdev_ops c3_mipi_adap_subdev_ops = {
+ .pad = &c3_mipi_adap_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_mipi_adap_internal_ops = {
+ .init_state = c3_mipi_adap_init_state,
+};
+
+/* Media entity operations */
+static const struct media_entity_operations c3_mipi_adap_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* PM runtime */
+
+static int c3_mipi_adap_runtime_suspend(struct device *dev)
+{
+ struct c3_adap_device *adap = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(adap->info->clock_num, adap->clks);
+
+ return 0;
+}
+
+static int c3_mipi_adap_runtime_resume(struct device *dev)
+{
+ struct c3_adap_device *adap = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(adap->info->clock_num, adap->clks);
+}
+
+static const struct dev_pm_ops c3_mipi_adap_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ RUNTIME_PM_OPS(c3_mipi_adap_runtime_suspend,
+ c3_mipi_adap_runtime_resume, NULL)
+};
+
+/* Probe/remove & platform driver */
+
+static int c3_mipi_adap_subdev_init(struct c3_adap_device *adap)
+{
+ struct v4l2_subdev *sd = &adap->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_mipi_adap_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &c3_mipi_adap_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "%s", MIPI_ADAP_SUBDEV_NAME);
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &c3_mipi_adap_entity_ops;
+
+ sd->dev = adap->dev;
+ v4l2_set_subdevdata(sd, adap);
+
+ adap->pads[C3_MIPI_ADAP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ adap->pads[C3_MIPI_ADAP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_MIPI_ADAP_PAD_MAX,
+ adap->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ media_entity_cleanup(&sd->entity);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void c3_mipi_adap_subdev_deinit(struct c3_adap_device *adap)
+{
+ v4l2_subdev_cleanup(&adap->sd);
+ media_entity_cleanup(&adap->sd.entity);
+}
+
+/* Subdev notifier register */
+static int c3_mipi_adap_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct c3_adap_device *adap = v4l2_get_subdevdata(notifier->sd);
+ struct media_pad *sink = &adap->sd.entity.pads[C3_MIPI_ADAP_PAD_SINK];
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static const struct v4l2_async_notifier_operations c3_mipi_adap_notify_ops = {
+ .bound = c3_mipi_adap_notify_bound,
+};
+
+static int c3_mipi_adap_async_register(struct c3_adap_device *adap)
+{
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_subdev_nf_init(&adap->notifier, &adap->sd);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(adap->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ asc = v4l2_async_nf_add_fwnode_remote(&adap->notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_put_handle;
+ }
+
+ adap->notifier.ops = &c3_mipi_adap_notify_ops;
+ ret = v4l2_async_nf_register(&adap->notifier);
+ if (ret)
+ goto err_cleanup_nf;
+
+ ret = v4l2_async_register_subdev(&adap->sd);
+ if (ret)
+ goto err_unregister_nf;
+
+ fwnode_handle_put(ep);
+
+ return 0;
+
+err_unregister_nf:
+ v4l2_async_nf_unregister(&adap->notifier);
+err_cleanup_nf:
+ v4l2_async_nf_cleanup(&adap->notifier);
+err_put_handle:
+ fwnode_handle_put(ep);
+ return ret;
+}
+
+static void c3_mipi_adap_async_unregister(struct c3_adap_device *adap)
+{
+ v4l2_async_unregister_subdev(&adap->sd);
+ v4l2_async_nf_unregister(&adap->notifier);
+ v4l2_async_nf_cleanup(&adap->notifier);
+}
+
+static int c3_mipi_adap_ioremap_resource(struct c3_adap_device *adap)
+{
+ struct device *dev = adap->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ adap->top = devm_platform_ioremap_resource_byname(pdev, "top");
+ if (IS_ERR(adap->top))
+ return PTR_ERR(adap->top);
+
+ adap->fd = devm_platform_ioremap_resource_byname(pdev, "fd");
+ if (IS_ERR(adap->fd))
+ return PTR_ERR(adap->fd);
+
+ adap->rd = devm_platform_ioremap_resource_byname(pdev, "rd");
+ if (IS_ERR(adap->rd))
+ return PTR_ERR(adap->rd);
+
+ return 0;
+}
+
+static int c3_mipi_adap_get_clocks(struct c3_adap_device *adap)
+{
+ const struct c3_adap_info *info = adap->info;
+
+ for (unsigned int i = 0; i < info->clock_num; i++)
+ adap->clks[i].id = info->clocks[i];
+
+ return devm_clk_bulk_get(adap->dev, info->clock_num, adap->clks);
+}
+
+static int c3_mipi_adap_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct c3_adap_device *adap;
+ int ret;
+
+ adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL);
+ if (!adap)
+ return -ENOMEM;
+
+ adap->info = of_device_get_match_data(dev);
+ adap->dev = dev;
+
+ ret = c3_mipi_adap_ioremap_resource(adap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to ioremap resource\n");
+
+ ret = c3_mipi_adap_get_clocks(adap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ platform_set_drvdata(pdev, adap);
+
+ pm_runtime_enable(dev);
+
+ ret = c3_mipi_adap_subdev_init(adap);
+ if (ret)
+ goto err_disable_runtime_pm;
+
+ ret = c3_mipi_adap_async_register(adap);
+ if (ret)
+ goto err_deinit_subdev;
+
+ return 0;
+
+err_deinit_subdev:
+ c3_mipi_adap_subdev_deinit(adap);
+err_disable_runtime_pm:
+ pm_runtime_disable(dev);
+ return ret;
+};
+
+static void c3_mipi_adap_remove(struct platform_device *pdev)
+{
+ struct c3_adap_device *adap = platform_get_drvdata(pdev);
+
+ c3_mipi_adap_async_unregister(adap);
+ c3_mipi_adap_subdev_deinit(adap);
+
+ pm_runtime_disable(&pdev->dev);
+};
+
+static const struct c3_adap_info c3_mipi_adap_info = {
+ .clocks = {"vapb", "isp0"},
+ .clock_num = 2
+};
+
+static const struct of_device_id c3_mipi_adap_of_match[] = {
+ {
+ .compatible = "amlogic,c3-mipi-adapter",
+ .data = &c3_mipi_adap_info
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, c3_mipi_adap_of_match);
+
+static struct platform_driver c3_mipi_adap_driver = {
+ .probe = c3_mipi_adap_probe,
+ .remove = c3_mipi_adap_remove,
+ .driver = {
+ .name = "c3-mipi-adapter",
+ .of_match_table = c3_mipi_adap_of_match,
+ .pm = pm_ptr(&c3_mipi_adap_pm_ops),
+ },
+};
+
+module_platform_driver(c3_mipi_adap_driver);
+
+MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic C3 MIPI adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig b/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig
new file mode 100644
index 000000000000..0d7b2e203273
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_C3_MIPI_CSI2
+ tristate "Amlogic C3 MIPI CSI-2 receiver"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on OF
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ Video4Linux2 driver for Amlogic C3 MIPI CSI-2 receiver.
+ C3 MIPI CSI-2 receiver is used to receive MIPI data from
+ image sensor.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile b/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile
new file mode 100644
index 000000000000..cc08fc722bfd
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_VIDEO_C3_MIPI_CSI2) += c3-mipi-csi2.o
diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c
new file mode 100644
index 000000000000..1011ab3ebac7
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c
@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/* C3 CSI-2 submodule definition */
+enum {
+ SUBMD_APHY,
+ SUBMD_DPHY,
+ SUBMD_HOST,
+};
+
+#define CSI2_SUBMD_MASK GENMASK(17, 16)
+#define CSI2_SUBMD_SHIFT 16
+#define CSI2_SUBMD(x) (((x) & (CSI2_SUBMD_MASK)) >> (CSI2_SUBMD_SHIFT))
+#define CSI2_REG_ADDR_MASK GENMASK(15, 0)
+#define CSI2_REG_ADDR(x) ((x) & (CSI2_REG_ADDR_MASK))
+#define CSI2_REG_A(x) ((SUBMD_APHY << CSI2_SUBMD_SHIFT) | (x))
+#define CSI2_REG_D(x) ((SUBMD_DPHY << CSI2_SUBMD_SHIFT) | (x))
+#define CSI2_REG_H(x) ((SUBMD_HOST << CSI2_SUBMD_SHIFT) | (x))
+
+#define MIPI_CSI2_CLOCK_NUM_MAX 3
+#define MIPI_CSI2_SUBDEV_NAME "c3-mipi-csi2"
+
+/* C3 CSI-2 APHY register */
+#define CSI_PHY_CNTL0 CSI2_REG_A(0x44)
+#define CSI_PHY_CNTL0_HS_LP_BIAS_EN BIT(10)
+#define CSI_PHY_CNTL0_HS_RX_TRIM_11 (11 << 11)
+#define CSI_PHY_CNTL0_LP_LOW_VTH_2 (2 << 16)
+#define CSI_PHY_CNTL0_LP_HIGH_VTH_4 (4 << 20)
+#define CSI_PHY_CNTL0_DATA_LANE0_HS_DIG_EN BIT(24)
+#define CSI_PHY_CNTL0_DATA_LANE1_HS_DIG_EN BIT(25)
+#define CSI_PHY_CNTL0_CLK0_LANE_HS_DIG_EN BIT(26)
+#define CSI_PHY_CNTL0_DATA_LANE2_HS_DIG_EN BIT(27)
+#define CSI_PHY_CNTL0_DATA_LANE3_HS_DIG_EN BIT(28)
+
+#define CSI_PHY_CNTL1 CSI2_REG_A(0x48)
+#define CSI_PHY_CNTL1_HS_EQ_CAP_SMALL (2 << 16)
+#define CSI_PHY_CNTL1_HS_EQ_CAP_BIG (3 << 16)
+#define CSI_PHY_CNTL1_HS_EQ_RES_MIN (3 << 18)
+#define CSI_PHY_CNTL1_HS_EQ_RES_MED (2 << 18)
+#define CSI_PHY_CNTL1_HS_EQ_RES_MAX BIT(18)
+#define CSI_PHY_CNTL1_CLK_CHN_EQ_MAX_GAIN BIT(20)
+#define CSI_PHY_CNTL1_DATA_CHN_EQ_MAX_GAIN BIT(21)
+#define CSI_PHY_CNTL1_COM_BG_EN BIT(24)
+#define CSI_PHY_CNTL1_HS_SYNC_EN BIT(25)
+
+/* C3 CSI-2 DPHY register */
+#define MIPI_PHY_CTRL CSI2_REG_D(0x00)
+#define MIPI_PHY_CTRL_DATA_LANE0_EN (0 << 0)
+#define MIPI_PHY_CTRL_DATA_LANE0_DIS BIT(0)
+#define MIPI_PHY_CTRL_DATA_LANE1_EN (0 << 1)
+#define MIPI_PHY_CTRL_DATA_LANE1_DIS BIT(1)
+#define MIPI_PHY_CTRL_DATA_LANE2_EN (0 << 2)
+#define MIPI_PHY_CTRL_DATA_LANE2_DIS BIT(2)
+#define MIPI_PHY_CTRL_DATA_LANE3_EN (0 << 3)
+#define MIPI_PHY_CTRL_DATA_LANE3_DIS BIT(3)
+#define MIPI_PHY_CTRL_CLOCK_LANE_EN (0 << 4)
+#define MIPI_PHY_CTRL_CLOCK_LANE_DIS BIT(4)
+
+#define MIPI_PHY_CLK_LANE_CTRL CSI2_REG_D(0x04)
+#define MIPI_PHY_CLK_LANE_CTRL_FORCE_ULPS_ENTER BIT(0)
+#define MIPI_PHY_CLK_LANE_CTRL_FORCE_ULPS_EXIT BIT(1)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS (0 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_2 BIT(3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_4 (2 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_8 (3 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_16 (4 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_EN BIT(6)
+#define MIPI_PHY_CLK_LANE_CTRL_LPEN_DIS BIT(7)
+#define MIPI_PHY_CLK_LANE_CTRL_END_EN BIT(8)
+#define MIPI_PHY_CLK_LANE_CTRL_HS_RX_EN BIT(9)
+
+#define MIPI_PHY_DATA_LANE_CTRL1 CSI2_REG_D(0x0c)
+#define MIPI_PHY_DATA_LANE_CTRL1_INSERT_ERRESC BIT(0)
+#define MIPI_PHY_DATA_LANE_CTRL1_HS_SYNC_CHK_EN BIT(1)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_MASK GENMASK(6, 2)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_ALL_EN (0x1f << 2)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_MASK GENMASK(9, 7)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_3 (3 << 7)
+
+#define MIPI_PHY_TCLK_MISS CSI2_REG_D(0x10)
+#define MIPI_PHY_TCLK_MISS_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_TCLK_MISS_CYCLES_9 (9 << 0)
+
+#define MIPI_PHY_TCLK_SETTLE CSI2_REG_D(0x14)
+#define MIPI_PHY_TCLK_SETTLE_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_TCLK_SETTLE_CYCLES_31 (31 << 0)
+
+#define MIPI_PHY_THS_EXIT CSI2_REG_D(0x18)
+#define MIPI_PHY_THS_EXIT_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_THS_EXIT_CYCLES_8 (8 << 0)
+
+#define MIPI_PHY_THS_SKIP CSI2_REG_D(0x1c)
+#define MIPI_PHY_THS_SKIP_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_THS_SKIP_CYCLES_10 (10 << 0)
+
+#define MIPI_PHY_THS_SETTLE CSI2_REG_D(0x20)
+#define MIPI_PHY_THS_SETTLE_CYCLES_MASK GENMASK(7, 0)
+
+#define MIPI_PHY_TINIT CSI2_REG_D(0x24)
+#define MIPI_PHY_TINIT_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TINIT_CYCLES_20000 (20000 << 0)
+
+#define MIPI_PHY_TULPS_C CSI2_REG_D(0x28)
+#define MIPI_PHY_TULPS_C_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TULPS_C_CYCLES_4096 (4096 << 0)
+
+#define MIPI_PHY_TULPS_S CSI2_REG_D(0x2c)
+#define MIPI_PHY_TULPS_S_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TULPS_S_CYCLES_256 (256 << 0)
+
+#define MIPI_PHY_TMBIAS CSI2_REG_D(0x30)
+#define MIPI_PHY_TMBIAS_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TMBIAS_CYCLES_256 (256 << 0)
+
+#define MIPI_PHY_TLP_EN_W CSI2_REG_D(0x34)
+#define MIPI_PHY_TLP_EN_W_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TLP_EN_W_CYCLES_12 (12 << 0)
+
+#define MIPI_PHY_TLPOK CSI2_REG_D(0x38)
+#define MIPI_PHY_TLPOK_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TLPOK_CYCLES_256 (256 << 0)
+
+#define MIPI_PHY_TWD_INIT CSI2_REG_D(0x3c)
+#define MIPI_PHY_TWD_INIT_DOG_MASK GENMASK(31, 0)
+#define MIPI_PHY_TWD_INIT_DOG_0X400000 (0x400000 << 0)
+
+#define MIPI_PHY_TWD_HS CSI2_REG_D(0x40)
+#define MIPI_PHY_TWD_HS_DOG_MASK GENMASK(31, 0)
+#define MIPI_PHY_TWD_HS_DOG_0X400000 (0x400000 << 0)
+
+#define MIPI_PHY_MUX_CTRL0 CSI2_REG_D(0x284)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_MASK GENMASK(3, 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE0 (0 << 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE1 BIT(0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE2 (2 << 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE3 (3 << 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_MASK GENMASK(7, 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE0 (0 << 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE1 BIT(4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE2 (2 << 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE3 (3 << 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_MASK GENMASK(11, 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE0 (0 << 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE1 BIT(8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE2 (2 << 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE3 (3 << 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_MASK GENMASK(14, 12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE0 (0 << 12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE1 BIT(12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE2 (2 << 12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE3 (3 << 12)
+
+#define MIPI_PHY_MUX_CTRL1 CSI2_REG_D(0x288)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_MASK GENMASK(3, 0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN0 (0 << 0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN1 BIT(0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN2 (2 << 0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN3 (3 << 0)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_MASK GENMASK(7, 4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN0 (0 << 4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN1 BIT(4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN2 (2 << 4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN3 (3 << 4)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_MASK GENMASK(11, 8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN0 (0 << 8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN1 BIT(8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN2 (2 << 8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN3 (3 << 8)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_MASK GENMASK(14, 12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN0 (0 << 12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN1 BIT(12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN2 (2 << 12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN3 (3 << 12)
+
+/* C3 CSI-2 HOST register */
+#define CSI2_HOST_N_LANES CSI2_REG_H(0x04)
+#define CSI2_HOST_N_LANES_MASK GENMASK(1, 0)
+#define CSI2_HOST_N_LANES_1 (0 << 0)
+#define CSI2_HOST_N_LANES_2 BIT(0)
+#define CSI2_HOST_N_LANES_3 (2 << 0)
+#define CSI2_HOST_N_LANES_4 (3 << 0)
+
+#define CSI2_HOST_CSI2_RESETN CSI2_REG_H(0x10)
+#define CSI2_HOST_CSI2_RESETN_MASK BIT(0)
+#define CSI2_HOST_CSI2_RESETN_ACTIVE (0 << 0)
+#define CSI2_HOST_CSI2_RESETN_EXIT BIT(0)
+
+#define C3_MIPI_CSI2_MAX_WIDTH 2888
+#define C3_MIPI_CSI2_MIN_WIDTH 160
+#define C3_MIPI_CSI2_MAX_HEIGHT 2240
+#define C3_MIPI_CSI2_MIN_HEIGHT 120
+#define C3_MIPI_CSI2_DEFAULT_WIDTH 1920
+#define C3_MIPI_CSI2_DEFAULT_HEIGHT 1080
+#define C3_MIPI_CSI2_DEFAULT_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+
+/* C3 CSI-2 pad list */
+enum {
+ C3_MIPI_CSI2_PAD_SINK,
+ C3_MIPI_CSI2_PAD_SRC,
+ C3_MIPI_CSI2_PAD_MAX
+};
+
+/*
+ * struct c3_csi_info - MIPI CSI2 information
+ *
+ * @clocks: array of MIPI CSI2 clock names
+ * @clock_num: actual clock number
+ */
+struct c3_csi_info {
+ char *clocks[MIPI_CSI2_CLOCK_NUM_MAX];
+ u32 clock_num;
+};
+
+/*
+ * struct c3_csi_device - MIPI CSI2 platform device
+ *
+ * @dev: pointer to the struct device
+ * @aphy: MIPI CSI2 aphy register address
+ * @dphy: MIPI CSI2 dphy register address
+ * @host: MIPI CSI2 host register address
+ * @clks: array of MIPI CSI2 clocks
+ * @sd: MIPI CSI2 sub-device
+ * @pads: MIPI CSI2 sub-device pads
+ * @notifier: notifier to register on the v4l2-async API
+ * @src_pad: source sub-device pad
+ * @bus: MIPI CSI2 bus information
+ * @info: version-specific MIPI CSI2 information
+ */
+struct c3_csi_device {
+ struct device *dev;
+ void __iomem *aphy;
+ void __iomem *dphy;
+ void __iomem *host;
+ struct clk_bulk_data clks[MIPI_CSI2_CLOCK_NUM_MAX];
+
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_MIPI_CSI2_PAD_MAX];
+ struct v4l2_async_notifier notifier;
+ struct media_pad *src_pad;
+ struct v4l2_mbus_config_mipi_csi2 bus;
+
+ const struct c3_csi_info *info;
+};
+
+static const u32 c3_mipi_csi_formats[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+};
+
+/* Hardware configuration */
+
+static void c3_mipi_csi_write(struct c3_csi_device *csi, u32 reg, u32 val)
+{
+ void __iomem *addr;
+
+ switch (CSI2_SUBMD(reg)) {
+ case SUBMD_APHY:
+ addr = csi->aphy + CSI2_REG_ADDR(reg);
+ break;
+ case SUBMD_DPHY:
+ addr = csi->dphy + CSI2_REG_ADDR(reg);
+ break;
+ case SUBMD_HOST:
+ addr = csi->host + CSI2_REG_ADDR(reg);
+ break;
+ default:
+ dev_err(csi->dev, "Invalid sub-module: %lu\n", CSI2_SUBMD(reg));
+ return;
+ }
+
+ writel(val, addr);
+}
+
+static void c3_mipi_csi_cfg_aphy(struct c3_csi_device *csi)
+{
+ c3_mipi_csi_write(csi, CSI_PHY_CNTL0,
+ CSI_PHY_CNTL0_HS_LP_BIAS_EN |
+ CSI_PHY_CNTL0_HS_RX_TRIM_11 |
+ CSI_PHY_CNTL0_LP_LOW_VTH_2 |
+ CSI_PHY_CNTL0_LP_HIGH_VTH_4 |
+ CSI_PHY_CNTL0_DATA_LANE0_HS_DIG_EN |
+ CSI_PHY_CNTL0_DATA_LANE1_HS_DIG_EN |
+ CSI_PHY_CNTL0_CLK0_LANE_HS_DIG_EN |
+ CSI_PHY_CNTL0_DATA_LANE2_HS_DIG_EN |
+ CSI_PHY_CNTL0_DATA_LANE3_HS_DIG_EN);
+
+ c3_mipi_csi_write(csi, CSI_PHY_CNTL1,
+ CSI_PHY_CNTL1_HS_EQ_CAP_SMALL |
+ CSI_PHY_CNTL1_HS_EQ_RES_MED |
+ CSI_PHY_CNTL1_CLK_CHN_EQ_MAX_GAIN |
+ CSI_PHY_CNTL1_DATA_CHN_EQ_MAX_GAIN |
+ CSI_PHY_CNTL1_COM_BG_EN |
+ CSI_PHY_CNTL1_HS_SYNC_EN);
+}
+
+static void c3_mipi_csi_cfg_dphy(struct c3_csi_device *csi, s64 rate)
+{
+ u32 val;
+ u32 settle;
+
+ /* Calculate the high speed settle */
+ val = DIV_ROUND_UP_ULL(1000000000, rate);
+ settle = (16 * val + 230) / 10;
+
+ c3_mipi_csi_write(csi, MIPI_PHY_CLK_LANE_CTRL,
+ MIPI_PHY_CLK_LANE_CTRL_HS_RX_EN |
+ MIPI_PHY_CLK_LANE_CTRL_END_EN |
+ MIPI_PHY_CLK_LANE_CTRL_LPEN_DIS |
+ MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_EN |
+ MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_8);
+
+ c3_mipi_csi_write(csi, MIPI_PHY_TCLK_MISS, MIPI_PHY_TCLK_MISS_CYCLES_9);
+ c3_mipi_csi_write(csi, MIPI_PHY_TCLK_SETTLE,
+ MIPI_PHY_TCLK_SETTLE_CYCLES_31);
+ c3_mipi_csi_write(csi, MIPI_PHY_THS_EXIT, MIPI_PHY_THS_EXIT_CYCLES_8);
+ c3_mipi_csi_write(csi, MIPI_PHY_THS_SKIP, MIPI_PHY_THS_SKIP_CYCLES_10);
+ c3_mipi_csi_write(csi, MIPI_PHY_THS_SETTLE, settle);
+ c3_mipi_csi_write(csi, MIPI_PHY_TINIT, MIPI_PHY_TINIT_CYCLES_20000);
+ c3_mipi_csi_write(csi, MIPI_PHY_TMBIAS, MIPI_PHY_TMBIAS_CYCLES_256);
+ c3_mipi_csi_write(csi, MIPI_PHY_TULPS_C, MIPI_PHY_TULPS_C_CYCLES_4096);
+ c3_mipi_csi_write(csi, MIPI_PHY_TULPS_S, MIPI_PHY_TULPS_S_CYCLES_256);
+ c3_mipi_csi_write(csi, MIPI_PHY_TLP_EN_W, MIPI_PHY_TLP_EN_W_CYCLES_12);
+ c3_mipi_csi_write(csi, MIPI_PHY_TLPOK, MIPI_PHY_TLPOK_CYCLES_256);
+ c3_mipi_csi_write(csi, MIPI_PHY_TWD_INIT,
+ MIPI_PHY_TWD_INIT_DOG_0X400000);
+ c3_mipi_csi_write(csi, MIPI_PHY_TWD_HS, MIPI_PHY_TWD_HS_DOG_0X400000);
+
+ c3_mipi_csi_write(csi, MIPI_PHY_DATA_LANE_CTRL1,
+ MIPI_PHY_DATA_LANE_CTRL1_INSERT_ERRESC |
+ MIPI_PHY_DATA_LANE_CTRL1_HS_SYNC_CHK_EN |
+ MIPI_PHY_DATA_LANE_CTRL1_PIPE_ALL_EN |
+ MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_3);
+
+ /* Set the order of lanes */
+ c3_mipi_csi_write(csi, MIPI_PHY_MUX_CTRL0,
+ MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE3 |
+ MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE2 |
+ MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE1 |
+ MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE0);
+
+ c3_mipi_csi_write(csi, MIPI_PHY_MUX_CTRL1,
+ MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN3 |
+ MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN2 |
+ MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN1 |
+ MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN0);
+
+ /* Enable digital data and clock lanes */
+ c3_mipi_csi_write(csi, MIPI_PHY_CTRL,
+ MIPI_PHY_CTRL_DATA_LANE0_EN |
+ MIPI_PHY_CTRL_DATA_LANE1_EN |
+ MIPI_PHY_CTRL_DATA_LANE2_EN |
+ MIPI_PHY_CTRL_DATA_LANE3_EN |
+ MIPI_PHY_CTRL_CLOCK_LANE_EN);
+}
+
+static void c3_mipi_csi_cfg_host(struct c3_csi_device *csi)
+{
+ /* Reset CSI-2 controller output */
+ c3_mipi_csi_write(csi, CSI2_HOST_CSI2_RESETN,
+ CSI2_HOST_CSI2_RESETN_ACTIVE);
+ c3_mipi_csi_write(csi, CSI2_HOST_CSI2_RESETN,
+ CSI2_HOST_CSI2_RESETN_EXIT);
+
+ /* Set data lane number */
+ c3_mipi_csi_write(csi, CSI2_HOST_N_LANES, csi->bus.num_data_lanes - 1);
+}
+
+static int c3_mipi_csi_start_stream(struct c3_csi_device *csi,
+ struct v4l2_subdev *src_sd)
+{
+ s64 link_freq;
+ s64 lane_rate;
+
+ link_freq = v4l2_get_link_freq(src_sd->ctrl_handler, 0, 0);
+ if (link_freq < 0) {
+ dev_err(csi->dev,
+ "Unable to obtain link frequency: %lld\n", link_freq);
+ return link_freq;
+ }
+
+ lane_rate = link_freq * 2;
+ if (lane_rate > 1500000000) {
+ dev_err(csi->dev, "Invalid lane rate: %lld\n", lane_rate);
+ return -EINVAL;
+ }
+
+ c3_mipi_csi_cfg_aphy(csi);
+ c3_mipi_csi_cfg_dphy(csi, lane_rate);
+ c3_mipi_csi_cfg_host(csi);
+
+ return 0;
+}
+
+static int c3_mipi_csi_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_csi_device *csi = v4l2_get_subdevdata(sd);
+ struct media_pad *sink_pad;
+ struct v4l2_subdev *src_sd;
+ int ret;
+
+ sink_pad = &csi->pads[C3_MIPI_CSI2_PAD_SINK];
+ csi->src_pad = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(csi->src_pad)) {
+ dev_dbg(csi->dev, "Failed to get source pad for MIPI CSI-2\n");
+ return -EPIPE;
+ }
+
+ src_sd = media_entity_to_v4l2_subdev(csi->src_pad->entity);
+
+ pm_runtime_resume_and_get(csi->dev);
+
+ c3_mipi_csi_start_stream(csi, src_sd);
+
+ ret = v4l2_subdev_enable_streams(src_sd, csi->src_pad->index, BIT(0));
+ if (ret) {
+ pm_runtime_put(csi->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_csi_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_csi_device *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd;
+
+ if (csi->src_pad) {
+ src_sd = media_entity_to_v4l2_subdev(csi->src_pad->entity);
+ v4l2_subdev_disable_streams(src_sd, csi->src_pad->index,
+ BIT(0));
+ }
+ csi->src_pad = NULL;
+
+ pm_runtime_put(csi->dev);
+
+ return 0;
+}
+
+static int c3_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ switch (code->pad) {
+ case C3_MIPI_CSI2_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(c3_mipi_csi_formats))
+ return -EINVAL;
+
+ code->code = c3_mipi_csi_formats[code->index];
+ break;
+ case C3_MIPI_CSI2_PAD_SRC:
+ if (code->index)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(state, code->pad);
+ code->code = fmt->code;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_csi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int i;
+
+ if (format->pad != C3_MIPI_CSI2_PAD_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+ for (i = 0; i < ARRAY_SIZE(c3_mipi_csi_formats); i++) {
+ if (format->format.code == c3_mipi_csi_formats[i]) {
+ fmt->code = c3_mipi_csi_formats[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(c3_mipi_csi_formats))
+ fmt->code = c3_mipi_csi_formats[0];
+
+ fmt->width = clamp_t(u32, format->format.width,
+ C3_MIPI_CSI2_MIN_WIDTH, C3_MIPI_CSI2_MAX_WIDTH);
+ fmt->height = clamp_t(u32, format->format.height,
+ C3_MIPI_CSI2_MIN_HEIGHT, C3_MIPI_CSI2_MAX_HEIGHT);
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ format->format = *fmt;
+
+ /* Synchronize the format to source pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SRC);
+ *fmt = format->format;
+
+ return 0;
+}
+
+static int c3_mipi_csi_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SINK);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SRC);
+
+ sink_fmt->width = C3_MIPI_CSI2_DEFAULT_WIDTH;
+ sink_fmt->height = C3_MIPI_CSI2_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = C3_MIPI_CSI2_DEFAULT_FMT;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops c3_mipi_csi_pad_ops = {
+ .enum_mbus_code = c3_mipi_csi_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_mipi_csi_set_fmt,
+ .enable_streams = c3_mipi_csi_enable_streams,
+ .disable_streams = c3_mipi_csi_disable_streams,
+};
+
+static const struct v4l2_subdev_ops c3_mipi_csi_subdev_ops = {
+ .pad = &c3_mipi_csi_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_mipi_csi_internal_ops = {
+ .init_state = c3_mipi_csi_init_state,
+};
+
+/* Media entity operations */
+static const struct media_entity_operations c3_mipi_csi_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* PM runtime */
+
+static int c3_mipi_csi_runtime_suspend(struct device *dev)
+{
+ struct c3_csi_device *csi = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(csi->info->clock_num, csi->clks);
+
+ return 0;
+}
+
+static int c3_mipi_csi_runtime_resume(struct device *dev)
+{
+ struct c3_csi_device *csi = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(csi->info->clock_num, csi->clks);
+}
+
+static const struct dev_pm_ops c3_mipi_csi_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ RUNTIME_PM_OPS(c3_mipi_csi_runtime_suspend,
+ c3_mipi_csi_runtime_resume, NULL)
+};
+
+/* Probe/remove & platform driver */
+
+static int c3_mipi_csi_subdev_init(struct c3_csi_device *csi)
+{
+ struct v4l2_subdev *sd = &csi->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_mipi_csi_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &c3_mipi_csi_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "%s", MIPI_CSI2_SUBDEV_NAME);
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &c3_mipi_csi_entity_ops;
+
+ sd->dev = csi->dev;
+ v4l2_set_subdevdata(sd, csi);
+
+ csi->pads[C3_MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ csi->pads[C3_MIPI_CSI2_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_MIPI_CSI2_PAD_MAX,
+ csi->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ media_entity_cleanup(&sd->entity);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void c3_mipi_csi_subdev_deinit(struct c3_csi_device *csi)
+{
+ v4l2_subdev_cleanup(&csi->sd);
+ media_entity_cleanup(&csi->sd.entity);
+}
+
+/* Subdev notifier register */
+static int c3_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct c3_csi_device *csi = v4l2_get_subdevdata(notifier->sd);
+ struct media_pad *sink = &csi->sd.entity.pads[C3_MIPI_CSI2_PAD_SINK];
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static const struct v4l2_async_notifier_operations c3_mipi_csi_notify_ops = {
+ .bound = c3_mipi_csi_notify_bound,
+};
+
+static int c3_mipi_csi_async_register(struct c3_csi_device *csi)
+{
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_subdev_nf_init(&csi->notifier, &csi->sd);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ goto err_put_handle;
+
+ csi->bus = vep.bus.mipi_csi2;
+
+ asc = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_put_handle;
+ }
+
+ csi->notifier.ops = &c3_mipi_csi_notify_ops;
+ ret = v4l2_async_nf_register(&csi->notifier);
+ if (ret)
+ goto err_cleanup_nf;
+
+ ret = v4l2_async_register_subdev(&csi->sd);
+ if (ret)
+ goto err_unregister_nf;
+
+ fwnode_handle_put(ep);
+
+ return 0;
+
+err_unregister_nf:
+ v4l2_async_nf_unregister(&csi->notifier);
+err_cleanup_nf:
+ v4l2_async_nf_cleanup(&csi->notifier);
+err_put_handle:
+ fwnode_handle_put(ep);
+ return ret;
+}
+
+static void c3_mipi_csi_async_unregister(struct c3_csi_device *csi)
+{
+ v4l2_async_unregister_subdev(&csi->sd);
+ v4l2_async_nf_unregister(&csi->notifier);
+ v4l2_async_nf_cleanup(&csi->notifier);
+}
+
+static int c3_mipi_csi_ioremap_resource(struct c3_csi_device *csi)
+{
+ struct device *dev = csi->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ csi->aphy = devm_platform_ioremap_resource_byname(pdev, "aphy");
+ if (IS_ERR(csi->aphy))
+ return PTR_ERR(csi->aphy);
+
+ csi->dphy = devm_platform_ioremap_resource_byname(pdev, "dphy");
+ if (IS_ERR(csi->dphy))
+ return PTR_ERR(csi->dphy);
+
+ csi->host = devm_platform_ioremap_resource_byname(pdev, "host");
+ if (IS_ERR(csi->host))
+ return PTR_ERR(csi->host);
+
+ return 0;
+}
+
+static int c3_mipi_csi_get_clocks(struct c3_csi_device *csi)
+{
+ const struct c3_csi_info *info = csi->info;
+
+ for (unsigned int i = 0; i < info->clock_num; i++)
+ csi->clks[i].id = info->clocks[i];
+
+ return devm_clk_bulk_get(csi->dev, info->clock_num, csi->clks);
+}
+
+static int c3_mipi_csi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct c3_csi_device *csi;
+ int ret;
+
+ csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL);
+ if (!csi)
+ return -ENOMEM;
+
+ csi->info = of_device_get_match_data(dev);
+ csi->dev = dev;
+
+ ret = c3_mipi_csi_ioremap_resource(csi);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to ioremap resource\n");
+
+ ret = c3_mipi_csi_get_clocks(csi);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ platform_set_drvdata(pdev, csi);
+
+ pm_runtime_enable(dev);
+
+ ret = c3_mipi_csi_subdev_init(csi);
+ if (ret)
+ goto err_disable_runtime_pm;
+
+ ret = c3_mipi_csi_async_register(csi);
+ if (ret)
+ goto err_deinit_subdev;
+
+ return 0;
+
+err_deinit_subdev:
+ c3_mipi_csi_subdev_deinit(csi);
+err_disable_runtime_pm:
+ pm_runtime_disable(dev);
+ return ret;
+};
+
+static void c3_mipi_csi_remove(struct platform_device *pdev)
+{
+ struct c3_csi_device *csi = platform_get_drvdata(pdev);
+
+ c3_mipi_csi_async_unregister(csi);
+ c3_mipi_csi_subdev_deinit(csi);
+
+ pm_runtime_disable(&pdev->dev);
+};
+
+static const struct c3_csi_info c3_mipi_csi_info = {
+ .clocks = {"vapb", "phy0"},
+ .clock_num = 2
+};
+
+static const struct of_device_id c3_mipi_csi_of_match[] = {
+ {
+ .compatible = "amlogic,c3-mipi-csi2",
+ .data = &c3_mipi_csi_info,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, c3_mipi_csi_of_match);
+
+static struct platform_driver c3_mipi_csi_driver = {
+ .probe = c3_mipi_csi_probe,
+ .remove = c3_mipi_csi_remove,
+ .driver = {
+ .name = "c3-mipi-csi2",
+ .of_match_table = c3_mipi_csi_of_match,
+ .pm = pm_ptr(&c3_mipi_csi_pm_ops),
+ },
+};
+
+module_platform_driver(c3_mipi_csi_driver);
+
+MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic C3 MIPI CSI-2 receiver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
index 6a38a0fa0e2d..85d518823159 100644
--- a/drivers/media/platform/amphion/vdec.c
+++ b/drivers/media/platform/amphion/vdec.c
@@ -805,7 +805,7 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
cur_fmt = vpu_get_format(inst, inst->cap_format.type);
vbuf = &vpu_buf->m2m_buf.vb;
if (vbuf->vb2_buf.index != frame->id)
- dev_err(inst->dev, "[%d] buffer id(%d, %d) dismatch\n",
+ dev_err(inst->dev, "[%d] buffer id(%d, %d) mismatch\n",
inst->id, vbuf->vb2_buf.index, frame->id);
if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_READY && vdec->params.display_delay_enable)
diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h
index 22f0da26ccec..1451549c9dd2 100644
--- a/drivers/media/platform/amphion/vpu.h
+++ b/drivers/media/platform/amphion/vpu.h
@@ -162,7 +162,6 @@ struct vpu_core {
struct delayed_work msg_delayed_work;
struct kfifo msg_fifo;
void *msg_buffer;
- unsigned int msg_buffer_size;
struct vpu_dev *vpu;
void *iface;
diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c
index 8df85c14ab3f..da00f5fc0e5d 100644
--- a/drivers/media/platform/amphion/vpu_core.c
+++ b/drivers/media/platform/amphion/vpu_core.c
@@ -250,6 +250,7 @@ static void vpu_core_get_vpu(struct vpu_core *core)
static int vpu_core_register(struct device *dev, struct vpu_core *core)
{
struct vpu_dev *vpu = dev_get_drvdata(dev);
+ unsigned int buffer_size;
int ret = 0;
dev_dbg(core->dev, "register core %s\n", vpu_core_type_desc(core->type));
@@ -263,14 +264,14 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core)
}
INIT_WORK(&core->msg_work, vpu_msg_run_work);
INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work);
- core->msg_buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE);
- core->msg_buffer = vzalloc(core->msg_buffer_size);
+ buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE);
+ core->msg_buffer = vzalloc(buffer_size);
if (!core->msg_buffer) {
dev_err(core->dev, "failed allocate buffer for fifo\n");
ret = -ENOMEM;
goto error;
}
- ret = kfifo_init(&core->msg_fifo, core->msg_buffer, core->msg_buffer_size);
+ ret = kfifo_init(&core->msg_fifo, core->msg_buffer, buffer_size);
if (ret) {
dev_err(core->dev, "failed init kfifo\n");
goto error;
diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c
index 4769c053c6c2..feca7d4220ed 100644
--- a/drivers/media/platform/amphion/vpu_malone.c
+++ b/drivers/media/platform/amphion/vpu_malone.c
@@ -3,6 +3,7 @@
* Copyright 2020-2021 NXP
*/
+#include <linux/bitfield.h>
#include <linux/init.h>
#include <linux/interconnect.h>
#include <linux/ioctl.h>
@@ -25,6 +26,10 @@
#include "vpu_imx8q.h"
#include "vpu_malone.h"
+static bool low_latency;
+module_param(low_latency, bool, 0644);
+MODULE_PARM_DESC(low_latency, "Set low latency frame flush mode: 0 (disable) or 1 (enable)");
+
#define CMD_SIZE 25600
#define MSG_SIZE 25600
#define CODEC_SIZE 0x1000
@@ -68,6 +73,12 @@
#define MALONE_DEC_FMT_RV_MASK BIT(21)
+#define MALONE_VERSION_MASK 0xFFFFF
+#define MALONE_VERSION(maj, min, inc) \
+ (FIELD_PREP(0xF0000, maj) | FIELD_PREP(0xFF00, min) | FIELD_PREP(0xFF, inc))
+#define CHECK_VERSION(iface, maj, min) \
+ (FIELD_GET(MALONE_VERSION_MASK, (iface)->fw_version) >= MALONE_VERSION(maj, min, 0))
+
enum vpu_malone_stream_input_mode {
INVALID_MODE = 0,
FRAME_LVL,
@@ -332,6 +343,8 @@ struct vpu_dec_ctrl {
u32 buf_addr[VID_API_NUM_STREAMS];
};
+static const struct malone_padding_scode *get_padding_scode(u32 type, u32 fmt);
+
u32 vpu_malone_get_data_size(void)
{
return sizeof(struct vpu_dec_ctrl);
@@ -654,9 +667,15 @@ static int vpu_malone_set_params(struct vpu_shared_addr *shared,
hc->jpg[instance].jpg_mjpeg_interlaced = 0;
}
- hc->codec_param[instance].disp_imm = params->display_delay_enable ? 1 : 0;
- if (malone_format != MALONE_FMT_AVC)
+ if (params->display_delay_enable &&
+ get_padding_scode(SCODE_PADDING_BUFFLUSH, params->codec_format))
+ hc->codec_param[instance].disp_imm = 1;
+ else
+ hc->codec_param[instance].disp_imm = 0;
+
+ if (params->codec_format == V4L2_PIX_FMT_HEVC && !CHECK_VERSION(iface, 1, 9))
hc->codec_param[instance].disp_imm = 0;
+
hc->codec_param[instance].dbglog_enable = 0;
iface->dbglog_desc.level = 0;
@@ -1023,6 +1042,7 @@ static const struct malone_padding_scode padding_scodes[] = {
{SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0x0, 0x0}},
{SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264, {0x15010000, 0x0}},
{SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264_MVC, {0x15010000, 0x0}},
+ {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_HEVC, {0x3e010000, 0x20}},
};
static const struct malone_padding_scode padding_scode_dft = {0x0, 0x0};
@@ -1057,8 +1077,11 @@ static int vpu_malone_add_padding_scode(struct vpu_buffer *stream_buffer,
int ret;
ps = get_padding_scode(scode_type, pixelformat);
- if (!ps)
+ if (!ps) {
+ if (scode_type == SCODE_PADDING_BUFFLUSH)
+ return 0;
return -EINVAL;
+ }
wptr = readl(&str_buf->wptr);
if (wptr < stream_buffer->phys || wptr > stream_buffer->phys + stream_buffer->length)
@@ -1562,7 +1585,15 @@ static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str
vpu_malone_update_wptr(str_buf, wptr);
- if (disp_imm && !vpu_vb_is_codecconfig(vbuf)) {
+ /*
+ * Enable the low latency flush mode if display delay is set to 0
+ * or the low latency frame flush mode if it is set to 1.
+ * The low latency flush mode requires some padding data to be appended to each frame,
+ * but there must not be any padding data between the sequence header and the frame.
+ * This module is currently only supported for the H264 and HEVC formats,
+ * for other formats, vpu_malone_add_scode() will return 0.
+ */
+ if ((disp_imm || low_latency) && !vpu_vb_is_codecconfig(vbuf)) {
ret = vpu_malone_add_scode(inst->core->iface,
inst->id,
&inst->stream_buffer,
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index 0d1c39347529..a05a744cbb75 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -1072,16 +1072,12 @@ static int isi_formats_init(struct atmel_isi *isi)
return -ENXIO;
isi->num_user_formats = num_fmts;
- isi->user_formats = devm_kcalloc(isi->dev,
- num_fmts, sizeof(struct isi_format *),
- GFP_KERNEL);
+ isi->user_formats = devm_kmemdup_array(isi->dev, isi_fmts, num_fmts,
+ sizeof(*isi_fmts), GFP_KERNEL);
if (!isi->user_formats)
return -ENOMEM;
- memcpy(isi->user_formats, isi_fmts,
- num_fmts * sizeof(struct isi_format *));
isi->current_fmt = isi->user_formats[0];
-
return 0;
}
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 4d64df829e75..cebcae196eec 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -164,6 +164,8 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx)
static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
{
+ struct media_pad *src_pad =
+ &csi2rx->source_subdev->entity.pads[csi2rx->source_pad];
union phy_configure_opts opts = { };
struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
struct v4l2_subdev_format sd_fmt = {
@@ -181,7 +183,7 @@ static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code);
- link_freq = v4l2_get_link_freq(csi2rx->source_subdev->ctrl_handler,
+ link_freq = v4l2_get_link_freq(src_pad,
fmt->bpp, 2 * csi2rx->num_lanes);
if (link_freq < 0)
return link_freq;
diff --git a/drivers/media/platform/chips-media/coda/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c
index 289a076c3bcc..e6e3f5ec24f6 100644
--- a/drivers/media/platform/chips-media/coda/coda-common.c
+++ b/drivers/media/platform/chips-media/coda/coda-common.c
@@ -3340,6 +3340,7 @@ static int coda_runtime_resume(struct device *dev)
static const struct dev_pm_ops coda_pm_ops = {
SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver coda_driver = {
diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c
index c8a905994109..d94cf84c3ee5 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-hw.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c
@@ -585,7 +585,7 @@ int wave5_vpu_build_up_dec_param(struct vpu_instance *inst,
vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1,
WAVE521_COMMAND_QUEUE_DEPTH - 1);
}
-
+ vpu_write_reg(inst->dev, W5_CMD_ERR_CONCEAL, 0);
ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
if (ret) {
wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
index d3ff420c52ce..fd71f0c43ac3 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -1345,10 +1345,24 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
if (ret)
goto free_bitstream_vbuf;
} else if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ struct dec_initial_info *initial_info =
+ &inst->codec_info->dec_info.initial_info;
+
if (inst->state == VPU_INST_STATE_STOP)
ret = switch_state(inst, VPU_INST_STATE_INIT_SEQ);
if (ret)
goto return_buffers;
+
+ if (inst->state == VPU_INST_STATE_INIT_SEQ &&
+ inst->dev->product_code == WAVE521C_CODE) {
+ if (initial_info->luma_bitdepth != 8) {
+ dev_info(inst->dev->dev, "%s: no support for %d bit depth",
+ __func__, initial_info->luma_bitdepth);
+ ret = -EINVAL;
+ goto return_buffers;
+ }
+ }
+
}
pm_runtime_mark_last_busy(inst->dev->dev);
pm_runtime_put_autosuspend(inst->dev->dev);
@@ -1369,6 +1383,16 @@ static int streamoff_output(struct vb2_queue *q)
struct vb2_v4l2_buffer *buf;
int ret;
dma_addr_t new_rd_ptr;
+ struct dec_output_info dec_info;
+ unsigned int i;
+
+ for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) {
+ ret = wave5_vpu_dec_set_disp_flag(inst, i);
+ if (ret)
+ dev_dbg(inst->dev->dev,
+ "%s: Setting display flag of buf index: %u, fail: %d\n",
+ __func__, i, ret);
+ }
while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
dev_dbg(inst->dev->dev, "%s: (Multiplanar) buf type %4u | index %4u\n",
@@ -1376,6 +1400,11 @@ static int streamoff_output(struct vb2_queue *q)
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
}
+ while (wave5_vpu_dec_get_output_info(inst, &dec_info) == 0) {
+ if (dec_info.index_frame_display >= 0)
+ wave5_vpu_dec_set_disp_flag(inst, dec_info.index_frame_display);
+ }
+
ret = wave5_vpu_flush_instance(inst);
if (ret)
return ret;
@@ -1459,7 +1488,7 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
break;
if (wave5_vpu_dec_get_output_info(inst, &dec_output_info))
- dev_dbg(inst->dev->dev, "Getting decoding results from fw, fail\n");
+ dev_dbg(inst->dev->dev, "there is no output info\n");
}
v4l2_m2m_update_stop_streaming_state(m2m_ctx, q);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
index d1320298a0f7..e1715d3f43b0 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
@@ -55,12 +55,12 @@ static void wave5_vpu_handle_irq(void *dev_id)
struct vpu_device *dev = dev_id;
irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
+ seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
+ cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason);
wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
list_for_each_entry(inst, &dev->instances, list) {
- seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
- cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) {
@@ -269,8 +269,8 @@ static int wave5_vpu_probe(struct platform_device *pdev)
dev->irq = platform_get_irq(pdev, 0);
if (dev->irq < 0) {
dev_err(&pdev->dev, "failed to get irq resource, falling back to polling\n");
- hrtimer_init(&dev->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
- dev->hrtimer.function = &wave5_vpu_timer_callback;
+ hrtimer_setup(&dev->hrtimer, &wave5_vpu_timer_callback, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL_PINNED);
dev->worker = kthread_run_worker(0, "vpu_irq_thread");
if (IS_ERR(dev->worker)) {
dev_err(&pdev->dev, "failed to create vpu irq worker\n");
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
index e16b990041c2..e5e879a13e8b 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
@@ -75,6 +75,16 @@ int wave5_vpu_flush_instance(struct vpu_instance *inst)
inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id);
mutex_unlock(&inst->dev->hw_lock);
return -ETIMEDOUT;
+ } else if (ret == -EBUSY) {
+ struct dec_output_info dec_info;
+
+ mutex_unlock(&inst->dev->hw_lock);
+ wave5_vpu_dec_get_output_info(inst, &dec_info);
+ ret = mutex_lock_interruptible(&inst->dev->hw_lock);
+ if (ret)
+ return ret;
+ if (dec_info.index_frame_display > 0)
+ wave5_vpu_dec_set_disp_flag(inst, dec_info.index_frame_display);
}
} while (ret != 0);
mutex_unlock(&inst->dev->hw_lock);
diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c
index c194f830577f..ae868d9f73e1 100644
--- a/drivers/media/platform/imagination/e5010-jpeg-enc.c
+++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c
@@ -1057,8 +1057,11 @@ static int e5010_probe(struct platform_device *pdev)
e5010->vdev->lock = &e5010->mutex;
ret = v4l2_device_register(dev, &e5010->v4l2_dev);
- if (ret)
- return dev_err_probe(dev, ret, "failed to register v4l2 device\n");
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register v4l2 device\n");
+ goto fail_after_video_device_alloc;
+ }
+
e5010->m2m_dev = v4l2_m2m_init(&e5010_m2m_ops);
if (IS_ERR(e5010->m2m_dev)) {
@@ -1118,6 +1121,8 @@ fail_after_video_register_device:
v4l2_m2m_release(e5010->m2m_dev);
fail_after_v4l2_register:
v4l2_device_unregister(&e5010->v4l2_dev);
+fail_after_video_device_alloc:
+ video_device_release(e5010->vdev);
return ret;
}
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index 834d2a354692..7eb12449b63a 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -1026,6 +1026,7 @@ static void mtk_jpeg_dec_device_run(void *priv)
spin_lock_irqsave(&jpeg->hw_lock, flags);
mtk_jpeg_dec_reset(jpeg->reg_base);
mtk_jpeg_dec_set_config(jpeg->reg_base,
+ jpeg->variant->support_34bit,
&jpeg_src_buf->dec_param,
jpeg_src_buf->bs_size,
&bs,
@@ -1570,7 +1571,8 @@ static irqreturn_t mtk_jpeg_enc_done(struct mtk_jpeg_dev *jpeg)
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base);
+ result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base,
+ jpeg->variant->support_34bit);
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size);
buf_state = VB2_BUF_STATE_DONE;
@@ -1770,6 +1772,7 @@ retry_select:
ctx->total_frame_num++;
mtk_jpeg_dec_reset(comp_jpeg[hw_id]->reg_base);
mtk_jpeg_dec_set_config(comp_jpeg[hw_id]->reg_base,
+ jpeg->variant->support_34bit,
&jpeg_src_buf->dec_param,
jpeg_src_buf->bs_size,
&bs,
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
index 8877eb39e807..02ed0ed5b736 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
@@ -34,6 +34,8 @@
#define MTK_JPEG_MAX_EXIF_SIZE (64 * 1024)
+#define MTK_JPEG_ADDR_MASK GENMASK(1, 0)
+
/**
* enum mtk_jpeg_ctx_state - states of the context state machine
* @MTK_JPEG_INIT: current state is initialized
@@ -62,6 +64,7 @@ enum mtk_jpeg_ctx_state {
* @cap_q_default_fourcc: capture queue default fourcc
* @multi_core: mark jpeg hw is multi_core or not
* @jpeg_worker: jpeg dec or enc worker
+ * @support_34bit: flag to check support for 34-bit DMA address
*/
struct mtk_jpeg_variant {
struct clk_bulk_data *clks;
@@ -78,6 +81,7 @@ struct mtk_jpeg_variant {
u32 cap_q_default_fourcc;
bool multi_core;
void (*jpeg_worker)(struct work_struct *work);
+ bool support_34bit;
};
struct mtk_jpeg_src_buf {
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
index 2c5d74939d0a..e78e1d11093c 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
@@ -5,6 +5,8 @@
* Rick Chang <rick.chang@mediatek.com>
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -279,23 +281,43 @@ static void mtk_jpeg_dec_set_brz_factor(void __iomem *base, u8 yscale_w,
writel(val, base + JPGDEC_REG_BRZ_FACTOR);
}
-static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, u32 addr_y,
- u32 addr_u, u32 addr_v)
+static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, bool support_34bit,
+ dma_addr_t addr_y, dma_addr_t addr_u, dma_addr_t addr_v)
{
+ u32 val;
+
mtk_jpeg_verify_align(addr_y, 16, JPGDEC_REG_DEST_ADDR0_Y);
- writel(addr_y, base + JPGDEC_REG_DEST_ADDR0_Y);
+ writel(lower_32_bits(addr_y), base + JPGDEC_REG_DEST_ADDR0_Y);
mtk_jpeg_verify_align(addr_u, 16, JPGDEC_REG_DEST_ADDR0_U);
- writel(addr_u, base + JPGDEC_REG_DEST_ADDR0_U);
+ writel(lower_32_bits(addr_u), base + JPGDEC_REG_DEST_ADDR0_U);
mtk_jpeg_verify_align(addr_v, 16, JPGDEC_REG_DEST_ADDR0_V);
- writel(addr_v, base + JPGDEC_REG_DEST_ADDR0_V);
+ writel(lower_32_bits(addr_v), base + JPGDEC_REG_DEST_ADDR0_V);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_y));
+ writel(val, base + JPGDEC_REG_DEST_ADDR0_Y_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_u));
+ writel(val, base + JPGDEC_REG_DEST_ADDR0_U_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_v));
+ writel(val, base + JPGDEC_REG_DEST_ADDR0_V_EXT);
+ }
}
-static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, u32 addr_y,
- u32 addr_u, u32 addr_v)
+static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, bool support_34bit,
+ dma_addr_t addr_y, dma_addr_t addr_u, dma_addr_t addr_v)
{
- writel(addr_y, base + JPGDEC_REG_DEST_ADDR1_Y);
- writel(addr_u, base + JPGDEC_REG_DEST_ADDR1_U);
- writel(addr_v, base + JPGDEC_REG_DEST_ADDR1_V);
+ u32 val;
+
+ writel(lower_32_bits(addr_y), base + JPGDEC_REG_DEST_ADDR1_Y);
+ writel(lower_32_bits(addr_u), base + JPGDEC_REG_DEST_ADDR1_U);
+ writel(lower_32_bits(addr_v), base + JPGDEC_REG_DEST_ADDR1_V);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_y));
+ writel(val, base + JPGDEC_REG_DEST_ADDR1_Y_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_u));
+ writel(val, base + JPGDEC_REG_DEST_ADDR1_U_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_v));
+ writel(val, base + JPGDEC_REG_DEST_ADDR1_V_EXT);
+ }
}
static void mtk_jpeg_dec_set_mem_stride(void __iomem *base, u32 stride_y,
@@ -322,18 +344,30 @@ static void mtk_jpeg_dec_set_dec_mode(void __iomem *base, u32 mode)
writel(mode & 0x03, base + JPGDEC_REG_OPERATION_MODE);
}
-static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, u32 ptr)
+static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, bool support_34bit, dma_addr_t ptr)
{
+ u32 val;
+
mtk_jpeg_verify_align(ptr, 16, JPGDEC_REG_FILE_BRP);
- writel(ptr, base + JPGDEC_REG_FILE_BRP);
+ writel(lower_32_bits(ptr), base + JPGDEC_REG_FILE_BRP);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(ptr));
+ writel(val, base + JPGDEC_REG_FILE_BRP_EXT);
+ }
}
-static void mtk_jpeg_dec_set_bs_info(void __iomem *base, u32 addr, u32 size,
- u32 bitstream_size)
+static void mtk_jpeg_dec_set_bs_info(void __iomem *base, bool support_34bit,
+ dma_addr_t addr, u32 size, u32 bitstream_size)
{
+ u32 val;
+
mtk_jpeg_verify_align(addr, 16, JPGDEC_REG_FILE_ADDR);
mtk_jpeg_verify_align(size, 128, JPGDEC_REG_FILE_TOTAL_SIZE);
- writel(addr, base + JPGDEC_REG_FILE_ADDR);
+ writel(lower_32_bits(addr), base + JPGDEC_REG_FILE_ADDR);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr));
+ writel(val, base + JPGDEC_REG_FILE_ADDR_EXT);
+ }
writel(size, base + JPGDEC_REG_FILE_TOTAL_SIZE);
writel(bitstream_size, base + JPGDEC_REG_BIT_STREAM_SIZE);
}
@@ -404,6 +438,7 @@ static void mtk_jpeg_dec_set_sampling_factor(void __iomem *base, u32 comp_num,
}
void mtk_jpeg_dec_set_config(void __iomem *base,
+ bool support_34bits,
struct mtk_jpeg_dec_param *cfg,
u32 bitstream_size,
struct mtk_jpeg_bs *bs,
@@ -413,8 +448,8 @@ void mtk_jpeg_dec_set_config(void __iomem *base,
mtk_jpeg_dec_set_dec_mode(base, 0);
mtk_jpeg_dec_set_comp0_du(base, cfg->unit_num);
mtk_jpeg_dec_set_total_mcu(base, cfg->total_mcu);
- mtk_jpeg_dec_set_bs_info(base, bs->str_addr, bs->size, bitstream_size);
- mtk_jpeg_dec_set_bs_write_ptr(base, bs->end_addr);
+ mtk_jpeg_dec_set_bs_info(base, support_34bits, bs->str_addr, bs->size, bitstream_size);
+ mtk_jpeg_dec_set_bs_write_ptr(base, support_34bits, bs->end_addr);
mtk_jpeg_dec_set_du_membership(base, cfg->membership, 1,
(cfg->comp_num == 1) ? 1 : 0);
mtk_jpeg_dec_set_comp_id(base, cfg->comp_id[0], cfg->comp_id[1],
@@ -432,9 +467,9 @@ void mtk_jpeg_dec_set_config(void __iomem *base,
cfg->mem_stride[1]);
mtk_jpeg_dec_set_img_stride(base, cfg->img_stride[0],
cfg->img_stride[1]);
- mtk_jpeg_dec_set_dst_bank0(base, fb->plane_addr[0],
+ mtk_jpeg_dec_set_dst_bank0(base, support_34bits, fb->plane_addr[0],
fb->plane_addr[1], fb->plane_addr[2]);
- mtk_jpeg_dec_set_dst_bank1(base, 0, 0, 0);
+ mtk_jpeg_dec_set_dst_bank1(base, support_34bits, 0, 0, 0);
mtk_jpeg_dec_set_dma_group(base, cfg->dma_mcu, cfg->dma_group,
cfg->dma_last_mcu);
mtk_jpeg_dec_set_pause_mcu_idx(base, cfg->total_mcu);
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
index 8c31c6b12417..2948c9c300a4 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
@@ -71,6 +71,7 @@ int mtk_jpeg_dec_fill_param(struct mtk_jpeg_dec_param *param);
u32 mtk_jpeg_dec_get_int_status(void __iomem *dec_reg_base);
u32 mtk_jpeg_dec_enum_result(u32 irq_result);
void mtk_jpeg_dec_set_config(void __iomem *base,
+ bool support_34bits,
struct mtk_jpeg_dec_param *cfg,
u32 bitstream_size,
struct mtk_jpeg_bs *bs,
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
index 27b7711ca341..e94f52de7c69 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
@@ -46,5 +46,13 @@
#define JPGDEC_REG_INTERRUPT_STATUS 0x0274
#define JPGDEC_REG_STATUS 0x0278
#define JPGDEC_REG_BIT_STREAM_SIZE 0x0344
+#define JPGDEC_REG_DEST_ADDR0_Y_EXT 0x0360
+#define JPGDEC_REG_DEST_ADDR0_U_EXT 0x0364
+#define JPGDEC_REG_DEST_ADDR0_V_EXT 0x0368
+#define JPGDEC_REG_DEST_ADDR1_Y_EXT 0x036c
+#define JPGDEC_REG_DEST_ADDR1_U_EXT 0x0370
+#define JPGDEC_REG_DEST_ADDR1_V_EXT 0x0374
+#define JPGDEC_REG_FILE_ADDR_EXT 0x0378
+#define JPGDEC_REG_FILE_BRP_EXT 0x037c
#endif /* _MTK_JPEG_REG_H */
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
index f8fa3b841ccf..9ab27aee302a 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
@@ -5,6 +5,8 @@
*
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -62,9 +64,9 @@ void mtk_jpeg_enc_reset(void __iomem *base)
}
EXPORT_SYMBOL_GPL(mtk_jpeg_enc_reset);
-u32 mtk_jpeg_enc_get_file_size(void __iomem *base)
+u32 mtk_jpeg_enc_get_file_size(void __iomem *base, bool support_34bit)
{
- return readl(base + JPEG_ENC_DMA_ADDR0) -
+ return (readl(base + JPEG_ENC_DMA_ADDR0) << ((support_34bit) ? 2 : 0)) -
readl(base + JPEG_ENC_DST_ADDR0);
}
EXPORT_SYMBOL_GPL(mtk_jpeg_enc_get_file_size);
@@ -84,14 +86,24 @@ void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base,
{
int i;
dma_addr_t dma_addr;
+ u32 addr_ext;
+ bool support_34bit = ctx->jpeg->variant->support_34bit;
for (i = 0; i < src_buf->num_planes; i++) {
dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, i) +
src_buf->planes[i].data_offset;
- if (!i)
- writel(dma_addr, base + JPEG_ENC_SRC_LUMA_ADDR);
+ if (i == 0)
+ writel(lower_32_bits(dma_addr), base + JPEG_ENC_SRC_LUMA_ADDR);
else
- writel(dma_addr, base + JPEG_ENC_SRC_CHROMA_ADDR);
+ writel(lower_32_bits(dma_addr), base + JPEG_ENC_SRC_CHROMA_ADDR);
+
+ if (support_34bit) {
+ addr_ext = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(dma_addr));
+ if (i == 0)
+ writel(addr_ext, base + JPEG_ENC_SRC_LUMA_ADDR_EXT);
+ else
+ writel(addr_ext, base + JPEG_ENC_SRC_CHRO_ADDR_EXT);
+ }
}
}
EXPORT_SYMBOL_GPL(mtk_jpeg_set_enc_src);
@@ -103,6 +115,8 @@ void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base,
size_t size;
u32 dma_addr_offset;
u32 dma_addr_offsetmask;
+ u32 addr_ext;
+ bool support_34bit = ctx->jpeg->variant->support_34bit;
dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
dma_addr_offset = ctx->enable_exif ? MTK_JPEG_MAX_EXIF_SIZE : 0;
@@ -113,6 +127,12 @@ void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base,
writel(dma_addr_offsetmask & 0xf, base + JPEG_ENC_BYTE_OFFSET_MASK);
writel(dma_addr & ~0xf, base + JPEG_ENC_DST_ADDR0);
writel((dma_addr + size) & ~0xf, base + JPEG_ENC_STALL_ADDR0);
+
+ if (support_34bit) {
+ addr_ext = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(dma_addr));
+ writel(addr_ext, base + JPEG_ENC_DEST_ADDR0_EXT);
+ writel(addr_ext + size, base + JPEG_ENC_STALL_ADDR0_EXT);
+ }
}
EXPORT_SYMBOL_GPL(mtk_jpeg_set_enc_dst);
@@ -278,7 +298,8 @@ static irqreturn_t mtk_jpegenc_hw_irq_handler(int irq, void *priv)
if (!(irq_status & JPEG_ENC_INT_STATUS_DONE))
dev_warn(jpeg->dev, "Jpg Enc occurs unknown Err.");
- result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base);
+ result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base,
+ ctx->jpeg->variant->support_34bit);
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size);
buf_state = VB2_BUF_STATE_DONE;
v4l2_m2m_buf_done(src_buf, buf_state);
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
index 61c60e4e58ea..31ec9030ae88 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
@@ -68,6 +68,11 @@
#define JPEG_ENC_DCM_CTRL 0x300
#define JPEG_ENC_CODEC_SEL 0x314
#define JPEG_ENC_ULTRA_THRES 0x318
+#define JPEG_ENC_SRC_LUMA_ADDR_EXT 0x584
+#define JPEG_ENC_SRC_CHRO_ADDR_EXT 0x588
+#define JPEG_ENC_Q_TBL_ADDR_EXT 0x58C
+#define JPEG_ENC_DEST_ADDR0_EXT 0x590
+#define JPEG_ENC_STALL_ADDR0_EXT 0x594
/**
* struct mtk_jpeg_enc_qlt - JPEG encoder quality data
@@ -80,7 +85,7 @@ struct mtk_jpeg_enc_qlt {
};
void mtk_jpeg_enc_reset(void __iomem *base);
-u32 mtk_jpeg_enc_get_file_size(void __iomem *base);
+u32 mtk_jpeg_enc_get_file_size(void __iomem *base, bool support_34bit);
void mtk_jpeg_enc_start(void __iomem *enc_reg_base);
void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base,
struct vb2_buffer *src_buf);
diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
index 28c998bd3a81..d0fd77dcf8e2 100644
--- a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
@@ -342,7 +342,7 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type,
if (r->left & 1)
r->left -= 1;
- mtk_mdp_dbg(2, "[%d] crop l,t,w,h:%d,%d,%d,%d, max:%dx%d", ctx->id,
+ mtk_mdp_dbg(2, "[%d] crop (%d,%d)/%ux%u, max:%dx%d", ctx->id,
r->left, r->top, r->width,
r->height, max_w, max_h);
return 0;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h
index 935ae9825728..222611e03a06 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h
@@ -12,8 +12,6 @@
#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;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
index f571f561f070..8de2c8e4d333 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
@@ -79,25 +79,6 @@ static struct platform_device *__get_pdev_by_id(struct platform_device *pdev,
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;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
index 657356f87743..644b223b2877 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
@@ -236,7 +236,7 @@ int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r,
u32 framew, frameh, walign, halign;
int ret;
- dev_dbg(dev, "%d target:%d, set:(%d,%d) %ux%u", ctx->id,
+ 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;
@@ -275,7 +275,7 @@ int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r,
r->width = right - left;
r->height = bottom - top;
- dev_dbg(dev, "%d crop:(%d,%d) %ux%u", ctx->id,
+ dev_dbg(dev, "%d crop:(%d,%d)/%ux%u", ctx->id,
r->left, r->top, r->width, r->height);
return 0;
}
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
index ff23b225db70..1b0bc47355c0 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
@@ -79,8 +79,11 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use
}
fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL);
- if (!fw)
+ if (!fw) {
+ scp_put(scp);
return ERR_PTR(-ENOMEM);
+ }
+
fw->type = SCP;
fw->ops = &mtk_vcodec_rproc_msg;
fw->scp = scp;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
index ac568ed14fa2..aececca7ecf8 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
@@ -17,6 +17,7 @@
#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE)
#define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING)
+#define IS_VDEC_SUPPORT_EXT(capability) ((capability) & MTK_VDEC_IS_SUPPORT_EXT)
enum mtk_vcodec_dec_chip_name {
MTK_VDEC_INVAL = 0,
@@ -42,6 +43,7 @@ enum mtk_vdec_format_types {
MTK_VDEC_FORMAT_HEVC_FRAME = 0x1000,
MTK_VCODEC_INNER_RACING = 0x20000,
MTK_VDEC_IS_SUPPORT_10BIT = 0x40000,
+ MTK_VDEC_IS_SUPPORT_EXT = 0x80000,
};
/*
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
index afa224da0f41..d873159b9b30 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
@@ -152,8 +152,6 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
- .menu_skip_mask =
- BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
},
.codec_type = V4L2_PIX_FMT_HEVC_SLICE,
},
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
index 1ed0ccec5665..5b25e1679b51 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
@@ -30,6 +30,7 @@ enum vdec_h264_core_dec_err_type {
/**
* struct vdec_h264_slice_lat_dec_param - parameters for decode current frame
+ * (shared data between host and firmware)
*
* @sps: h264 sps syntax parameters
* @pps: h264 pps syntax parameters
@@ -48,7 +49,7 @@ struct vdec_h264_slice_lat_dec_param {
};
/**
- * struct vdec_h264_slice_info - decode information
+ * struct vdec_h264_slice_info - decode information (shared data between host and firmware)
*
* @nal_info: nal info of current picture
* @timeout: Decode timeout: 1 timeout, 0 no timeout
@@ -72,23 +73,22 @@ struct vdec_h264_slice_info {
/**
* struct vdec_h264_slice_vsi - shared memory for decode information exchange
- * between SCP and Host.
+ * between SCP and Host (shared data between host and firmware).
*
- * @wdma_err_addr: wdma error dma address
- * @wdma_start_addr: wdma start dma address
- * @wdma_end_addr: wdma end dma address
- * @slice_bc_start_addr: slice bc start dma address
- * @slice_bc_end_addr: slice bc end dma address
- * @row_info_start_addr: row info start dma address
- * @row_info_end_addr: row info end dma address
- * @trans_start: trans start dma address
- * @trans_end: trans end dma address
- * @wdma_end_addr_offset: wdma end address offset
+ * @wdma_err_addr: wdma error dma address
+ * @wdma_start_addr: wdma start dma address
+ * @wdma_end_addr: wdma end dma address
+ * @slice_bc_start_addr: slice bc start dma address
+ * @slice_bc_end_addr: slice bc end dma address
+ * @row_info_start_addr: row info start dma address
+ * @row_info_end_addr: row info end dma address
+ * @trans_start: trans start dma address
+ * @trans_end: trans end dma address
+ * @wdma_end_addr_offset: wdma end address offset
*
- * @mv_buf_dma: HW working motion vector buffer
- * dma address (AP-W, VPU-R)
- * @dec: decode information (AP-R, VPU-W)
- * @h264_slice_params: decode parameters for hw used
+ * @mv_buf_dma: HW working motion vector buffer
+ * @dec: decode information (AP-R, VPU-W)
+ * @h264_slice_params: decode parameters for hw used
*/
struct vdec_h264_slice_vsi {
/* LAT dec addr */
@@ -112,12 +112,12 @@ struct vdec_h264_slice_vsi {
* struct vdec_h264_slice_share_info - shared information used to exchange
* message between lat and core
*
- * @sps: sequence header information from user space
- * @dec_params: decoder params from user space
- * @h264_slice_params: decoder params used for hardware
- * @trans_start: trans start dma address
- * @trans_end: trans end dma address
- * @nal_info: nal info of current picture
+ * @sps: sequence header information from user space
+ * @dec_params: decoder params from user space
+ * @h264_slice_params: decoder params used for hardware
+ * @trans_start: trans start dma address
+ * @trans_end: trans end dma address
+ * @nal_info: nal info of current picture
*/
struct vdec_h264_slice_share_info {
struct v4l2_ctrl_h264_sps sps;
@@ -128,6 +128,86 @@ struct vdec_h264_slice_share_info {
u16 nal_info;
};
+/*
+ * struct vdec_h264_slice_mem - memory address and size
+ * (shared data between host and firmware)
+ */
+struct vdec_h264_slice_mem {
+ union {
+ u64 buf;
+ u64 dma_addr;
+ };
+ union {
+ size_t size;
+ u64 dma_addr_end;
+ };
+};
+
+/**
+ * struct vdec_h264_slice_fb - frame buffer for decoding
+ * (shared data between host and firmware)
+ *
+ * @y: current luma buffer address info
+ * @c: current chroma buffer address info
+ */
+struct vdec_h264_slice_fb {
+ struct vdec_h264_slice_mem y;
+ struct vdec_h264_slice_mem c;
+};
+
+/**
+ * struct vdec_h264_slice_info_ext - extend decode information
+ * (shared data between host and firmware)
+ *
+ * @wdma_end_addr_offset: offset from buffer start
+ * @nal_info: nal info of current picture
+ * @timeout: toggles whether a decode operation is timeout
+ * @reserved: reserved
+ * @vdec_fb_va: vdec frame buffer struct virtual address
+ * @crc: displays the hardware status
+ */
+struct vdec_h264_slice_info_ext {
+ u64 wdma_end_addr_offset;
+ u16 nal_info;
+ u16 timeout;
+ u32 reserved;
+ u64 vdec_fb_va;
+ u32 crc[8];
+};
+
+/**
+ * struct vdec_h264_slice_vsi_ext - extend shared memory for decode information exchange
+ * between SCP and Host (shared data between host and firmware).
+ *
+ * @bs: input buffer info
+ * @fb: current y/c buffer
+ *
+ * @ube: buffer used to share date between lat and core
+ * @trans: transcoded buffer used for core decode
+ * @row_info: row info buffer
+ * @err_map: error map buffer
+ * @slice_bc: slice buffer
+ *
+ * @mv_buf_dma: store hardware motion vector data
+ * @dec: decode information (AP-R, VPU-W)
+ * @h264_slice_params: decode parameters used for the hw
+ */
+struct vdec_h264_slice_vsi_ext {
+ /* LAT dec addr */
+ struct vdec_h264_slice_mem bs;
+ struct vdec_h264_slice_fb fb;
+
+ struct vdec_h264_slice_mem ube;
+ struct vdec_h264_slice_mem trans;
+ struct vdec_h264_slice_mem row_info;
+ struct vdec_h264_slice_mem err_map;
+ struct vdec_h264_slice_mem slice_bc;
+
+ struct vdec_h264_slice_mem mv_buf_dma[H264_MAX_MV_NUM];
+ struct vdec_h264_slice_info_ext dec;
+ struct vdec_h264_slice_lat_dec_param h264_slice_params;
+};
+
/**
* struct vdec_h264_slice_inst - h264 decoder instance
*
@@ -138,17 +218,21 @@ struct vdec_h264_slice_share_info {
* @vpu: VPU instance
* @vsi: vsi used for lat
* @vsi_core: vsi used for core
- *
- * @vsi_ctx: Local VSI data for this decoding context
+ * @vsi_ctx: vsi data for this decoding context
+ * @vsi_ext: extended vsi used for lat
+ * @vsi_core_ext: extended vsi used for core
+ * @vsi_ctx_ext: extended vsi data for this decoding context
* @h264_slice_param: the parameters that hardware use to decode
*
- * @resolution_changed:resolution changed
+ * @resolution_changed: resolution changed
* @realloc_mv_buf: reallocate mv buffer
* @cap_num_planes: number of capture queue plane
*
* @dpb: decoded picture buffer used to store reference
* buffer information
- *@is_field_bitstream: is field bitstream
+ * @is_field_bitstream: not support field bitstream, only support frame
+ *
+ * @decode: lat decoder pointer for different architectures
*/
struct vdec_h264_slice_inst {
unsigned int slice_dec_num;
@@ -156,10 +240,18 @@ struct vdec_h264_slice_inst {
struct mtk_vcodec_mem pred_buf;
struct mtk_vcodec_mem mv_buf[H264_MAX_MV_NUM];
struct vdec_vpu_inst vpu;
- struct vdec_h264_slice_vsi *vsi;
- struct vdec_h264_slice_vsi *vsi_core;
-
- struct vdec_h264_slice_vsi vsi_ctx;
+ union {
+ struct {
+ struct vdec_h264_slice_vsi *vsi;
+ struct vdec_h264_slice_vsi *vsi_core;
+ struct vdec_h264_slice_vsi vsi_ctx;
+ };
+ struct {
+ struct vdec_h264_slice_vsi_ext *vsi_ext;
+ struct vdec_h264_slice_vsi_ext *vsi_core_ext;
+ struct vdec_h264_slice_vsi_ext vsi_ctx_ext;
+ };
+ };
struct vdec_h264_slice_lat_dec_param h264_slice_param;
unsigned int resolution_changed;
@@ -168,12 +260,15 @@ struct vdec_h264_slice_inst {
struct v4l2_h264_dpb_entry dpb[16];
bool is_field_bitstream;
+
+ int (*decode)(void *h_vdec, struct mtk_vcodec_mem *bs,
+ struct vdec_fb *unused, bool *res_chg);
};
static int vdec_h264_slice_fill_decode_parameters(struct vdec_h264_slice_inst *inst,
- struct vdec_h264_slice_share_info *share_info)
+ struct vdec_h264_slice_share_info *share_info,
+ struct vdec_h264_slice_lat_dec_param *slice_param)
{
- struct vdec_h264_slice_lat_dec_param *slice_param = &inst->vsi->h264_slice_params;
const struct v4l2_ctrl_h264_decode_params *dec_params;
const struct v4l2_ctrl_h264_scaling_matrix *src_matrix;
const struct v4l2_ctrl_h264_sps *sps;
@@ -266,9 +361,6 @@ static int get_vdec_sig_decode_parameters(struct vdec_h264_slice_inst *inst)
mtk_vdec_h264_get_ref_list(b0_reflist, v4l2_b0_reflist, reflist_builder.num_valid);
mtk_vdec_h264_get_ref_list(b1_reflist, v4l2_b1_reflist, reflist_builder.num_valid);
- memcpy(&inst->vsi_ctx.h264_slice_params, slice_param,
- sizeof(inst->vsi_ctx.h264_slice_params));
-
return 0;
}
@@ -392,68 +484,148 @@ static void vdec_h264_slice_get_crop_info(struct vdec_h264_slice_inst *inst,
cr->left, cr->top, cr->width, cr->height);
}
-static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
+static void vdec_h264_slice_setup_lat_buffer_ext(struct vdec_h264_slice_inst *inst,
+ struct mtk_vcodec_mem *bs,
+ struct vdec_lat_buf *lat_buf)
{
- struct vdec_h264_slice_inst *inst;
- int err, vsi_size;
+ struct mtk_vcodec_mem *mem;
+ int i;
- inst = kzalloc(sizeof(*inst), GFP_KERNEL);
- if (!inst)
- return -ENOMEM;
+ inst->vsi_ext->bs.dma_addr = (u64)bs->dma_addr;
+ inst->vsi_ext->bs.size = bs->size;
- inst->ctx = ctx;
+ for (i = 0; i < H264_MAX_MV_NUM; i++) {
+ mem = &inst->mv_buf[i];
+ inst->vsi_ext->mv_buf_dma[i].dma_addr = mem->dma_addr;
+ inst->vsi_ext->mv_buf_dma[i].size = mem->size;
+ }
+ inst->vsi_ext->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr;
+ inst->vsi_ext->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size;
- inst->vpu.id = SCP_IPI_VDEC_LAT;
- inst->vpu.core_id = SCP_IPI_VDEC_CORE;
- inst->vpu.ctx = ctx;
- inst->vpu.codec_type = ctx->current_codec;
- inst->vpu.capture_type = ctx->capture_fourcc;
+ inst->vsi_ext->row_info.dma_addr = 0;
+ inst->vsi_ext->row_info.size = 0;
- err = vpu_dec_init(&inst->vpu);
- if (err) {
- mtk_vdec_err(ctx, "vdec_h264 init err=%d", err);
- goto error_free_inst;
+ inst->vsi_ext->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr;
+ inst->vsi_ext->err_map.size = lat_buf->wdma_err_addr.size;
+
+ inst->vsi_ext->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr;
+ inst->vsi_ext->slice_bc.size = lat_buf->slice_bc_addr.size;
+
+ inst->vsi_ext->trans.dma_addr_end = inst->ctx->msg_queue.wdma_rptr_addr;
+ inst->vsi_ext->trans.dma_addr = inst->ctx->msg_queue.wdma_wptr_addr;
+}
+
+static int vdec_h264_slice_setup_core_buffer_ext(struct vdec_h264_slice_inst *inst,
+ struct vdec_h264_slice_share_info *share_info,
+ struct vdec_lat_buf *lat_buf)
+{
+ struct mtk_vcodec_mem *mem;
+ struct mtk_vcodec_dec_ctx *ctx = inst->ctx;
+ struct vb2_v4l2_buffer *vb2_v4l2;
+ struct vdec_fb *fb;
+ u64 y_fb_dma, c_fb_dma = 0;
+ int i;
+
+ fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx);
+ if (!fb) {
+ mtk_vdec_err(ctx, "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
+ return -EBUSY;
}
- vsi_size = round_up(sizeof(struct vdec_h264_slice_vsi), VCODEC_DEC_ALIGNED_64);
- inst->vsi = inst->vpu.vsi;
- inst->vsi_core =
- (struct vdec_h264_slice_vsi *)(((char *)inst->vpu.vsi) + vsi_size);
- inst->resolution_changed = true;
- inst->realloc_mv_buf = true;
+ y_fb_dma = (u64)fb->base_y.dma_addr;
+ if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1)
+ c_fb_dma = y_fb_dma + ctx->picinfo.fb_sz[0];
+ else
+ c_fb_dma = (u64)fb->base_c.dma_addr;
- mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n",
- (int)sizeof(struct mtk_h264_sps_param),
- (int)sizeof(struct mtk_h264_pps_param),
- (int)sizeof(struct vdec_h264_slice_lat_dec_param),
- (int)sizeof(struct mtk_h264_dpb_info),
- vsi_size);
- mtk_vdec_debug(ctx, "lat H264 instance >> %p, codec_type = 0x%x",
- inst, inst->vpu.codec_type);
+ mtk_vdec_debug(ctx, "[h264-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, c_fb_dma);
- ctx->drv_handle = inst;
- return 0;
+ inst->vsi_core_ext->fb.y.dma_addr = y_fb_dma;
+ inst->vsi_core_ext->fb.y.size = ctx->picinfo.fb_sz[0];
+ inst->vsi_core_ext->fb.c.dma_addr = c_fb_dma;
+ inst->vsi_core_ext->fb.c.size = ctx->picinfo.fb_sz[1];
-error_free_inst:
- kfree(inst);
- return err;
+ inst->vsi_core_ext->dec.vdec_fb_va = (unsigned long)fb;
+ inst->vsi_core_ext->dec.nal_info = share_info->nal_info;
+
+ inst->vsi_core_ext->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr;
+ inst->vsi_core_ext->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size;
+
+ inst->vsi_core_ext->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr;
+ inst->vsi_core_ext->err_map.size = lat_buf->wdma_err_addr.size;
+
+ inst->vsi_core_ext->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr;
+ inst->vsi_core_ext->slice_bc.size = lat_buf->slice_bc_addr.size;
+
+ inst->vsi_core_ext->row_info.dma_addr = 0;
+ inst->vsi_core_ext->row_info.size = 0;
+
+ inst->vsi_core_ext->trans.dma_addr = share_info->trans_start;
+ inst->vsi_core_ext->trans.dma_addr_end = share_info->trans_end;
+
+ for (i = 0; i < H264_MAX_MV_NUM; i++) {
+ mem = &inst->mv_buf[i];
+ inst->vsi_core_ext->mv_buf_dma[i].dma_addr = mem->dma_addr;
+ inst->vsi_core_ext->mv_buf_dma[i].size = mem->size;
+ }
+
+ vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true);
+
+ return 0;
}
-static void vdec_h264_slice_deinit(void *h_vdec)
+static int vdec_h264_slice_core_decode_ext(struct vdec_lat_buf *lat_buf)
{
- struct vdec_h264_slice_inst *inst = h_vdec;
+ int err, timeout;
+ struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx;
+ struct vdec_h264_slice_inst *inst = ctx->drv_handle;
+ struct vdec_h264_slice_share_info *share_info = lat_buf->private_data;
+ struct vdec_vpu_inst *vpu = &inst->vpu;
- vpu_dec_deinit(&inst->vpu);
- vdec_h264_slice_free_mv_buf(inst);
- vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx);
+ memcpy(&inst->vsi_core_ext->h264_slice_params, &share_info->h264_slice_params,
+ sizeof(share_info->h264_slice_params));
- kfree(inst);
+ err = vdec_h264_slice_setup_core_buffer_ext(inst, share_info, lat_buf);
+ if (err)
+ goto vdec_dec_end;
+
+ vdec_h264_slice_fill_decode_reflist(inst, &inst->vsi_core_ext->h264_slice_params,
+ share_info);
+ err = vpu_dec_core(vpu);
+ if (err) {
+ mtk_vdec_err(ctx, "core decode err=%d", err);
+ goto vdec_dec_end;
+ }
+
+ /* wait decoder done interrupt */
+ timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED,
+ WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE);
+ if (timeout)
+ mtk_vdec_err(ctx, "core decode timeout: pic_%d", ctx->decoded_frame_cnt);
+ inst->vsi_core_ext->dec.timeout = !!timeout;
+
+ vpu_dec_core_end(vpu);
+
+ /* crc is hardware checksum, can be used to check whether the decoder result is right.*/
+ mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+ ctx->decoded_frame_cnt,
+ inst->vsi_core_ext->dec.crc[0], inst->vsi_core_ext->dec.crc[1],
+ inst->vsi_core_ext->dec.crc[2], inst->vsi_core_ext->dec.crc[3],
+ inst->vsi_core_ext->dec.crc[4], inst->vsi_core_ext->dec.crc[5],
+ inst->vsi_core_ext->dec.crc[6], inst->vsi_core_ext->dec.crc[7]);
+
+vdec_dec_end:
+ vdec_msg_queue_update_ube_rptr(&lat_buf->ctx->msg_queue, share_info->trans_end);
+ ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req);
+ mtk_vdec_debug(ctx, "core decode done err=%d", err);
+ ctx->decoded_frame_cnt++;
+ return 0;
}
static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
{
struct vdec_fb *fb;
- u64 vdec_fb_va;
u64 y_fb_dma, c_fb_dma;
int err, timeout, i;
struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx;
@@ -463,22 +635,19 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
struct mtk_vcodec_mem *mem;
struct vdec_vpu_inst *vpu = &inst->vpu;
- mtk_vdec_debug(ctx, "[h264-core] vdec_h264 core decode");
memcpy(&inst->vsi_core->h264_slice_params, &share_info->h264_slice_params,
sizeof(share_info->h264_slice_params));
fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx);
if (!fb) {
err = -EBUSY;
- mtk_vdec_err(ctx, "fb buffer is NULL");
+ mtk_vdec_err(ctx, "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
goto vdec_dec_end;
}
- vdec_fb_va = (unsigned long)fb;
y_fb_dma = (u64)fb->base_y.dma_addr;
if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1)
- c_fb_dma =
- y_fb_dma + inst->ctx->picinfo.buf_w * inst->ctx->picinfo.buf_h;
+ c_fb_dma = y_fb_dma + ctx->picinfo.fb_sz[0];
else
c_fb_dma = (u64)fb->base_c.dma_addr;
@@ -486,7 +655,7 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
inst->vsi_core->dec.y_fb_dma = y_fb_dma;
inst->vsi_core->dec.c_fb_dma = c_fb_dma;
- inst->vsi_core->dec.vdec_fb_va = vdec_fb_va;
+ inst->vsi_core->dec.vdec_fb_va = (unsigned long)fb;
inst->vsi_core->dec.nal_info = share_info->nal_info;
inst->vsi_core->wdma_start_addr =
lat_buf->ctx->msg_queue.wdma_addr.dma_addr;
@@ -524,6 +693,8 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
inst->vsi_core->dec.timeout = !!timeout;
vpu_dec_core_end(vpu);
+
+ /* crc is hardware checksum, can be used to check whether the decoder result is right.*/
mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
ctx->decoded_frame_cnt,
inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1],
@@ -536,6 +707,7 @@ vdec_dec_end:
ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req);
mtk_vdec_debug(ctx, "core decode done err=%d", err);
ctx->decoded_frame_cnt++;
+
return 0;
}
@@ -562,6 +734,128 @@ static void vdec_h264_insert_startcode(struct mtk_vcodec_dec_dev *vcodec_dev, un
(*bs_size) += 4;
}
+static int vdec_h264_slice_lat_decode_ext(void *h_vdec, struct mtk_vcodec_mem *bs,
+ struct vdec_fb *fb, bool *res_chg)
+{
+ struct vdec_h264_slice_inst *inst = h_vdec;
+ struct vdec_vpu_inst *vpu = &inst->vpu;
+ struct mtk_video_dec_buf *src_buf_info;
+ int err, timeout = 0;
+ unsigned int data[2];
+ struct vdec_lat_buf *lat_buf;
+ struct vdec_h264_slice_share_info *share_info;
+
+ if (vdec_msg_queue_init(&inst->ctx->msg_queue, inst->ctx,
+ vdec_h264_slice_core_decode_ext,
+ sizeof(*share_info)))
+ return -ENOMEM;
+
+ /* bs NULL means flush decoder */
+ if (!bs) {
+ vdec_msg_queue_wait_lat_buf_full(&inst->ctx->msg_queue);
+ return vpu_dec_reset(vpu);
+ }
+
+ if (inst->is_field_bitstream)
+ return -EINVAL;
+
+ lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx);
+ if (!lat_buf) {
+ mtk_vdec_debug(inst->ctx, "failed to get lat buffer");
+ return -EAGAIN;
+ }
+ share_info = lat_buf->private_data;
+ src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
+
+ lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req;
+ v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true);
+
+ err = vdec_h264_slice_fill_decode_parameters(inst, share_info,
+ &inst->vsi_ext->h264_slice_params);
+ if (err)
+ goto err_free_fb_out;
+
+ vdec_h264_insert_startcode(inst->ctx->dev, bs->va, &bs->size,
+ &share_info->h264_slice_params.pps);
+
+ *res_chg = inst->resolution_changed;
+ if (inst->resolution_changed) {
+ mtk_vdec_debug(inst->ctx, "- resolution changed -");
+ if (inst->realloc_mv_buf) {
+ err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo);
+ inst->realloc_mv_buf = false;
+ if (err)
+ goto err_free_fb_out;
+ }
+ inst->resolution_changed = false;
+ }
+
+ vdec_h264_slice_setup_lat_buffer_ext(inst, bs, lat_buf);
+ mtk_vdec_debug(inst->ctx, "lat:trans(0x%llx 0x%lx) err:0x%llx",
+ inst->vsi_ext->ube.dma_addr, (unsigned long)inst->vsi_ext->ube.size,
+ inst->vsi_ext->err_map.dma_addr);
+
+ mtk_vdec_debug(inst->ctx, "slice(0x%llx 0x%lx) rprt((0x%llx 0x%llx))",
+ inst->vsi_ext->slice_bc.dma_addr,
+ (unsigned long)inst->vsi_ext->slice_bc.size,
+ inst->vsi_ext->trans.dma_addr, inst->vsi_ext->trans.dma_addr_end);
+
+ err = vpu_dec_start(vpu, data, 2);
+ if (err) {
+ mtk_vdec_debug(inst->ctx, "lat decode err: %d", err);
+ goto err_free_fb_out;
+ }
+
+ share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr +
+ inst->vsi_ext->dec.wdma_end_addr_offset;
+
+ share_info->trans_start = inst->ctx->msg_queue.wdma_wptr_addr;
+ share_info->nal_info = inst->vsi_ext->dec.nal_info;
+
+ if (IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) {
+ memcpy(&share_info->h264_slice_params, &inst->vsi_ext->h264_slice_params,
+ sizeof(share_info->h264_slice_params));
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf);
+ }
+
+ /* wait decoder done interrupt */
+ timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED,
+ WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0);
+ if (timeout)
+ mtk_vdec_err(inst->ctx, "lat decode timeout: pic_%d", inst->slice_dec_num);
+ inst->vsi_ext->dec.timeout = !!timeout;
+
+ err = vpu_dec_end(vpu);
+ if (err == SLICE_HEADER_FULL || err == TRANS_BUFFER_FULL) {
+ if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability))
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf);
+ inst->slice_dec_num++;
+ mtk_vdec_err(inst->ctx, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err);
+ return -EINVAL;
+ }
+
+ share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr +
+ inst->vsi_ext->dec.wdma_end_addr_offset;
+
+ vdec_msg_queue_update_ube_wptr(&lat_buf->ctx->msg_queue, share_info->trans_end);
+
+ if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) {
+ memcpy(&share_info->h264_slice_params, &inst->vsi_ext->h264_slice_params,
+ sizeof(share_info->h264_slice_params));
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf);
+ }
+ mtk_vdec_debug(inst->ctx, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num,
+ inst->vsi_ext->dec.crc[0], inst->vsi_ext->dec.crc[1],
+ inst->vsi_ext->dec.crc[2]);
+
+ inst->slice_dec_num++;
+ return 0;
+err_free_fb_out:
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf);
+ mtk_vdec_err(inst->ctx, "slice dec number: %d err: %d", inst->slice_dec_num, err);
+ return err;
+}
+
static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
struct vdec_fb *fb, bool *res_chg)
{
@@ -608,7 +902,8 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req;
v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true);
- err = vdec_h264_slice_fill_decode_parameters(inst, share_info);
+ err = vdec_h264_slice_fill_decode_parameters(inst, share_info,
+ &inst->vsi->h264_slice_params);
if (err)
goto err_free_fb_out;
@@ -706,6 +1001,101 @@ err_free_fb_out:
return err;
}
+static int vdec_h264_slice_single_decode_ext(void *h_vdec, struct mtk_vcodec_mem *bs,
+ struct vdec_fb *unused, bool *res_chg)
+{
+ struct vdec_h264_slice_inst *inst = h_vdec;
+ struct vdec_vpu_inst *vpu = &inst->vpu;
+ struct mtk_video_dec_buf *src_buf_info, *dst_buf_info;
+ struct vdec_fb *fb;
+ unsigned int data[2], i;
+ u64 y_fb_dma, c_fb_dma;
+ struct mtk_vcodec_mem *mem;
+ int err;
+
+ /* bs NULL means flush decoder */
+ if (!bs)
+ return vpu_dec_reset(vpu);
+
+ fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx);
+ if (!fb) {
+ mtk_vdec_err(inst->ctx,
+ "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
+ return -ENOMEM;
+ }
+
+ src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
+ dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer);
+
+ y_fb_dma = fb->base_y.dma_addr;
+ c_fb_dma = fb->base_c.dma_addr;
+ mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx c_dma=%llx",
+ inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma);
+
+ inst->vsi_ctx_ext.bs.dma_addr = (u64)bs->dma_addr;
+ inst->vsi_ctx_ext.bs.size = bs->size;
+ inst->vsi_ctx_ext.fb.y.dma_addr = y_fb_dma;
+ inst->vsi_ctx_ext.fb.c.dma_addr = c_fb_dma;
+ inst->vsi_ctx_ext.dec.vdec_fb_va = (u64)(uintptr_t)fb;
+
+ v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
+ &dst_buf_info->m2m_buf.vb, true);
+ err = get_vdec_sig_decode_parameters(inst);
+ if (err)
+ goto err_free_fb_out;
+
+ memcpy(&inst->vsi_ctx_ext.h264_slice_params, &inst->h264_slice_param,
+ sizeof(inst->vsi_ctx_ext.h264_slice_params));
+
+ *res_chg = inst->resolution_changed;
+ if (inst->resolution_changed) {
+ mtk_vdec_debug(inst->ctx, "- resolution changed -");
+ if (inst->realloc_mv_buf) {
+ err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo);
+ inst->realloc_mv_buf = false;
+ if (err)
+ goto err_free_fb_out;
+ }
+ inst->resolution_changed = false;
+
+ for (i = 0; i < H264_MAX_MV_NUM; i++) {
+ mem = &inst->mv_buf[i];
+ inst->vsi_ctx_ext.mv_buf_dma[i].dma_addr = mem->dma_addr;
+ }
+ }
+
+ memcpy(inst->vpu.vsi, &inst->vsi_ctx_ext, sizeof(inst->vsi_ctx_ext));
+ err = vpu_dec_start(vpu, data, 2);
+ if (err)
+ goto err_free_fb_out;
+
+ /* wait decoder done interrupt */
+ err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED,
+ WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE);
+ if (err)
+ mtk_vdec_err(inst->ctx, "decode timeout: pic_%d", inst->ctx->decoded_frame_cnt);
+
+ inst->vsi_ext->dec.timeout = !!err;
+ err = vpu_dec_end(vpu);
+ if (err)
+ goto err_free_fb_out;
+
+ memcpy(&inst->vsi_ctx_ext, inst->vpu.vsi, sizeof(inst->vsi_ctx_ext));
+ mtk_vdec_debug(inst->ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+ inst->ctx->decoded_frame_cnt,
+ inst->vsi_ctx_ext.dec.crc[0], inst->vsi_ctx_ext.dec.crc[1],
+ inst->vsi_ctx_ext.dec.crc[2], inst->vsi_ctx_ext.dec.crc[3],
+ inst->vsi_ctx_ext.dec.crc[4], inst->vsi_ctx_ext.dec.crc[5],
+ inst->vsi_ctx_ext.dec.crc[6], inst->vsi_ctx_ext.dec.crc[7]);
+
+ inst->ctx->decoded_frame_cnt++;
+ return 0;
+
+err_free_fb_out:
+ mtk_vdec_err(inst->ctx, "dec frame number: %d err: %d", inst->ctx->decoded_frame_cnt, err);
+ return err;
+}
+
static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
struct vdec_fb *unused, bool *res_chg)
{
@@ -725,7 +1115,8 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs
fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx);
if (!fb) {
- mtk_vdec_err(inst->ctx, "fb buffer is NULL");
+ mtk_vdec_err(inst->ctx,
+ "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
return -ENOMEM;
}
@@ -749,6 +1140,9 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs
if (err)
goto err_free_fb_out;
+ memcpy(&inst->vsi_ctx.h264_slice_params, &inst->h264_slice_param,
+ sizeof(inst->vsi_ctx.h264_slice_params));
+
buf = (unsigned char *)bs->va;
nal_start_idx = mtk_vdec_h264_find_start_code(buf, bs->size);
if (nal_start_idx < 0) {
@@ -806,21 +1200,95 @@ err_free_fb_out:
return err;
}
+static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
+{
+ struct vdec_h264_slice_inst *inst;
+ int err, vsi_size;
+ unsigned char *temp;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ inst->ctx = ctx;
+
+ inst->vpu.id = SCP_IPI_VDEC_LAT;
+ inst->vpu.core_id = SCP_IPI_VDEC_CORE;
+ inst->vpu.ctx = ctx;
+ inst->vpu.codec_type = ctx->current_codec;
+ inst->vpu.capture_type = ctx->capture_fourcc;
+
+ err = vpu_dec_init(&inst->vpu);
+ if (err) {
+ mtk_vdec_err(ctx, "vdec_h264 init err=%d", err);
+ goto error_free_inst;
+ }
+
+ if (IS_VDEC_SUPPORT_EXT(ctx->dev->dec_capability)) {
+ vsi_size = sizeof(struct vdec_h264_slice_vsi_ext);
+
+ vsi_size = round_up(vsi_size, VCODEC_DEC_ALIGNED_64);
+ inst->vsi_ext = inst->vpu.vsi;
+ temp = (unsigned char *)inst->vsi_ext;
+ inst->vsi_core_ext = (struct vdec_h264_slice_vsi_ext *)(temp + vsi_size);
+
+ if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
+ inst->decode = vdec_h264_slice_single_decode_ext;
+ else
+ inst->decode = vdec_h264_slice_lat_decode_ext;
+ } else {
+ vsi_size = sizeof(struct vdec_h264_slice_vsi);
+
+ vsi_size = round_up(vsi_size, VCODEC_DEC_ALIGNED_64);
+ inst->vsi = inst->vpu.vsi;
+ temp = (unsigned char *)inst->vsi;
+ inst->vsi_core = (struct vdec_h264_slice_vsi *)(temp + vsi_size);
+
+ if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
+ inst->decode = vdec_h264_slice_single_decode;
+ else
+ inst->decode = vdec_h264_slice_lat_decode;
+ }
+ inst->resolution_changed = true;
+ inst->realloc_mv_buf = true;
+
+ mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n",
+ (int)sizeof(struct mtk_h264_sps_param),
+ (int)sizeof(struct mtk_h264_pps_param),
+ (int)sizeof(struct vdec_h264_slice_lat_dec_param),
+ (int)sizeof(struct mtk_h264_dpb_info),
+ vsi_size);
+ mtk_vdec_debug(ctx, "lat H264 instance >> %p, codec_type = 0x%x",
+ inst, inst->vpu.codec_type);
+
+ ctx->drv_handle = inst;
+ return 0;
+
+error_free_inst:
+ kfree(inst);
+ return err;
+}
+
+static void vdec_h264_slice_deinit(void *h_vdec)
+{
+ struct vdec_h264_slice_inst *inst = h_vdec;
+
+ vpu_dec_deinit(&inst->vpu);
+ vdec_h264_slice_free_mv_buf(inst);
+ vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx);
+
+ kfree(inst);
+}
+
static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
struct vdec_fb *unused, bool *res_chg)
{
struct vdec_h264_slice_inst *inst = h_vdec;
- int ret;
if (!h_vdec)
return -EINVAL;
- if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
- ret = vdec_h264_slice_single_decode(h_vdec, bs, unused, res_chg);
- else
- ret = vdec_h264_slice_lat_decode(h_vdec, bs, unused, res_chg);
-
- return ret;
+ return inst->decode(h_vdec, bs, unused, res_chg);
}
static int vdec_h264_slice_get_param(void *h_vdec, enum vdec_get_param_type type,
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
index aa721cc43647..2725db882e5b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
@@ -821,7 +821,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst,
inst->vsi_core->fb.y.dma_addr = y_fb_dma;
inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[0];
inst->vsi_core->fb.c.dma_addr = c_fb_dma;
- inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[1];
+ inst->vsi_core->fb.c.size = ctx->picinfo.fb_sz[1];
inst->vsi_core->dec.vdec_fb_va = (unsigned long)fb;
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
index f8145998fcaf..0f63657d8bad 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
@@ -515,7 +515,7 @@ static int h264_encode_frame(struct venc_h264_inst *inst,
struct venc_frame_info frame_info;
struct mtk_vcodec_enc_ctx *ctx = inst->ctx;
- mtk_venc_debug(ctx, "frm_cnt = %d\n ", inst->frm_cnt);
+ mtk_venc_debug(ctx, "frm_cnt = %d\n", inst->frm_cnt);
if (MTK_ENC_IOVA_IS_34BIT(ctx)) {
gop_size = inst->vsi_34->config.gop_size;
@@ -594,7 +594,11 @@ static int h264_enc_init(struct mtk_vcodec_enc_ctx *ctx)
inst->ctx = ctx;
inst->vpu_inst.ctx = ctx;
- inst->vpu_inst.id = is_ext ? SCP_IPI_VENC_H264 : IPI_VENC_H264;
+ if (is_ext)
+ inst->vpu_inst.id = SCP_IPI_VENC_H264;
+ else
+ inst->vpu_inst.id = IPI_VENC_H264;
+
inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx->dev->reg_base, VENC_SYS);
ret = vpu_enc_init(&inst->vpu_inst);
diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c
index 024cd8ee1709..44e904e61801 100644
--- a/drivers/media/platform/nuvoton/npcm-video.c
+++ b/drivers/media/platform/nuvoton/npcm-video.c
@@ -578,7 +578,7 @@ static unsigned int npcm_video_hres(struct npcm_video *video)
regmap_read(gfxi, HVCNTL, &hvcntl);
apb_hor_res = (((hvcnth & HVCNTH_MASK) << 8) + (hvcntl & HVCNTL_MASK) + 1);
- return apb_hor_res;
+ return (apb_hor_res > MAX_WIDTH) ? MAX_WIDTH : apb_hor_res;
}
static unsigned int npcm_video_vres(struct npcm_video *video)
@@ -591,7 +591,7 @@ static unsigned int npcm_video_vres(struct npcm_video *video)
apb_ver_res = (((vvcnth & VVCNTH_MASK) << 8) + (vvcntl & VVCNTL_MASK));
- return apb_ver_res;
+ return (apb_ver_res > MAX_HEIGHT) ? MAX_HEIGHT : apb_ver_res;
}
static int npcm_video_capres(struct npcm_video *video, unsigned int hor_res,
@@ -863,7 +863,6 @@ static void npcm_video_detect_resolution(struct npcm_video *video)
struct regmap *gfxi = video->gfx_regmap;
unsigned int dispst;
- video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
det->width = npcm_video_hres(video);
det->height = npcm_video_vres(video);
@@ -892,12 +891,16 @@ static void npcm_video_detect_resolution(struct npcm_video *video)
clear_bit(VIDEO_RES_CHANGING, &video->flags);
}
- if (det->width && det->height)
+ if (det->width && det->height) {
video->v4l2_input_status = 0;
-
- dev_dbg(video->dev, "Got resolution[%dx%d] -> [%dx%d], status %d\n",
- act->width, act->height, det->width, det->height,
- video->v4l2_input_status);
+ dev_dbg(video->dev, "Got resolution[%dx%d] -> [%dx%d], status %d\n",
+ act->width, act->height, det->width, det->height,
+ video->v4l2_input_status);
+ } else {
+ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
+ dev_err(video->dev, "Got invalid resolution[%dx%d]\n", det->width,
+ det->height);
+ }
}
static int npcm_video_set_resolution(struct npcm_video *video,
@@ -1648,8 +1651,8 @@ rel_ctrl_handler:
static int npcm_video_ece_init(struct npcm_video *video)
{
+ struct device_node *ece_node __free(device_node) = NULL;
struct device *dev = video->dev;
- struct device_node *ece_node;
struct platform_device *ece_pdev;
void __iomem *regs;
@@ -1669,7 +1672,7 @@ static int npcm_video_ece_init(struct npcm_video *video)
dev_err(dev, "Failed to find ECE device\n");
return -ENODEV;
}
- of_node_put(ece_node);
+ struct device *ece_dev __free(put_device) = &ece_pdev->dev;
regs = devm_platform_ioremap_resource(ece_pdev, 0);
if (IS_ERR(regs)) {
@@ -1684,7 +1687,7 @@ static int npcm_video_ece_init(struct npcm_video *video)
return PTR_ERR(video->ece.regmap);
}
- video->ece.reset = devm_reset_control_get(&ece_pdev->dev, NULL);
+ video->ece.reset = devm_reset_control_get(ece_dev, NULL);
if (IS_ERR(video->ece.reset)) {
dev_err(dev, "Failed to get ECE reset control in DTS\n");
return PTR_ERR(video->ece.reset);
diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c
index 66582e7f92fc..3d1db1121bf9 100644
--- a/drivers/media/platform/nxp/dw100/dw100.c
+++ b/drivers/media/platform/nxp/dw100/dw100.c
@@ -961,9 +961,9 @@ static int dw100_s_selection(struct file *file, void *fh,
src_q_data = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
dev_dbg(&ctx->dw_dev->pdev->dev,
- ">>> Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n",
+ ">>> Buffer Type: %u Target: %u Rect: (%d,%d)/%ux%u\n",
sel->type, sel->target,
- sel->r.width, sel->r.height, sel->r.left, sel->r.top);
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
@@ -1025,9 +1025,9 @@ static int dw100_s_selection(struct file *file, void *fh,
}
dev_dbg(&ctx->dw_dev->pdev->dev,
- "<<< Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n",
+ "<<< Buffer Type: %u Target: %u Rect: (%d,%d)/%ux%u\n",
sel->type, sel->target,
- sel->r.width, sel->r.height, sel->r.left, sel->r.top);
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
return 0;
}
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
index d579c804b047..adb93e977be9 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
@@ -89,6 +89,7 @@
/* SLOT_STATUS fields for slots 0..3 */
#define SLOT_STATUS_FRMDONE (0x1 << 3)
#define SLOT_STATUS_ENC_CONFIG_ERR (0x1 << 8)
+#define SLOT_STATUS_ONGOING (0x1 << 31)
/* SLOT_IRQ_EN fields TBD */
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 1221b309a916..5c17bc58181e 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -535,7 +535,18 @@ static const unsigned char jpeg_sos_maximal[] = {
};
static const unsigned char jpeg_image_red[] = {
- 0xFC, 0x5F, 0xA2, 0xBF, 0xCA, 0x73, 0xFE, 0xFE,
+ 0xF9, 0xFE, 0x8A, 0xFC, 0x34, 0xFD, 0xC4, 0x28,
+ 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A,
+ 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0,
+ 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00,
+ 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
+ 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28,
+ 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A,
+ 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0,
+ 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00,
+ 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
+ 0x8A, 0x00, 0x28, 0xA0, 0x0F, 0xFF, 0xD0, 0xF9,
+ 0xFE, 0x8A, 0xFC, 0x34, 0xFD, 0xC4, 0x28, 0xA0,
0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00,
0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28,
@@ -545,7 +556,7 @@ static const unsigned char jpeg_image_red[] = {
0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28,
0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A,
- 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00
+ 0x00, 0x28, 0xA0, 0x0F
};
static const unsigned char jpeg_eoi[] = {
@@ -752,6 +763,39 @@ static int mxc_get_free_slot(struct mxc_jpeg_slot_data *slot_data)
return -1;
}
+static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg)
+{
+ /* free descriptor for decoding/encoding phase */
+ dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data.desc,
+ jpeg->slot_data.desc_handle);
+ jpeg->slot_data.desc = NULL;
+ jpeg->slot_data.desc_handle = 0;
+
+ /* free descriptor for encoder configuration phase / decoder DHT */
+ dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data.cfg_desc,
+ jpeg->slot_data.cfg_desc_handle);
+ jpeg->slot_data.cfg_desc_handle = 0;
+ jpeg->slot_data.cfg_desc = NULL;
+
+ /* free configuration stream */
+ dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM,
+ jpeg->slot_data.cfg_stream_vaddr,
+ jpeg->slot_data.cfg_stream_handle);
+ jpeg->slot_data.cfg_stream_vaddr = NULL;
+ jpeg->slot_data.cfg_stream_handle = 0;
+
+ dma_free_coherent(jpeg->dev, jpeg->slot_data.cfg_dec_size,
+ jpeg->slot_data.cfg_dec_vaddr,
+ jpeg->slot_data.cfg_dec_daddr);
+ jpeg->slot_data.cfg_dec_size = 0;
+ jpeg->slot_data.cfg_dec_vaddr = NULL;
+ jpeg->slot_data.cfg_dec_daddr = 0;
+
+ jpeg->slot_data.used = false;
+}
+
static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg)
{
struct mxc_jpeg_desc *desc;
@@ -788,36 +832,25 @@ static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg)
goto err;
jpeg->slot_data.cfg_stream_vaddr = cfg_stm;
+ jpeg->slot_data.cfg_dec_size = MXC_JPEG_PATTERN_WIDTH * MXC_JPEG_PATTERN_HEIGHT * 2;
+ jpeg->slot_data.cfg_dec_vaddr = dma_alloc_coherent(jpeg->dev,
+ jpeg->slot_data.cfg_dec_size,
+ &jpeg->slot_data.cfg_dec_daddr,
+ GFP_ATOMIC);
+ if (!jpeg->slot_data.cfg_dec_vaddr)
+ goto err;
+
skip_alloc:
jpeg->slot_data.used = true;
return true;
err:
dev_err(jpeg->dev, "Could not allocate descriptors for slot %d", jpeg->slot_data.slot);
+ mxc_jpeg_free_slot_data(jpeg);
return false;
}
-static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg)
-{
- /* free descriptor for decoding/encoding phase */
- dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
- jpeg->slot_data.desc,
- jpeg->slot_data.desc_handle);
-
- /* free descriptor for encoder configuration phase / decoder DHT */
- dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
- jpeg->slot_data.cfg_desc,
- jpeg->slot_data.cfg_desc_handle);
-
- /* free configuration stream */
- dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM,
- jpeg->slot_data.cfg_stream_vaddr,
- jpeg->slot_data.cfg_stream_handle);
-
- jpeg->slot_data.used = false;
-}
-
static void mxc_jpeg_check_and_set_last_buffer(struct mxc_jpeg_ctx *ctx,
struct vb2_v4l2_buffer *src_buf,
struct vb2_v4l2_buffer *dst_buf)
@@ -877,6 +910,34 @@ static u32 mxc_jpeg_get_plane_size(struct mxc_jpeg_q_data *q_data, u32 plane_no)
return size;
}
+static bool mxc_dec_is_ongoing(struct mxc_jpeg_ctx *ctx)
+{
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ u32 curr_desc;
+ u32 slot_status;
+
+ curr_desc = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_CUR_DESCPT_PTR));
+ if (curr_desc == jpeg->slot_data.cfg_desc_handle)
+ return true;
+
+ slot_status = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_STATUS));
+ if (slot_status & SLOT_STATUS_ONGOING)
+ return true;
+
+ /*
+ * The curr_desc register is updated when next_descpt_ptr is loaded,
+ * the ongoing bit of slot_status is set when the 32 bytes descriptor is loaded.
+ * So there will be a short time interval in between, which may cause fake false.
+ * Consider read register is quite slow compared with IP read 32byte from memory,
+ * read twice slot_status can avoid this situation.
+ */
+ slot_status = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_STATUS));
+ if (slot_status & SLOT_STATUS_ONGOING)
+ return true;
+
+ return false;
+}
+
static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
{
struct mxc_jpeg_dev *jpeg = priv;
@@ -946,7 +1007,8 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
mxc_jpeg_enc_mode_go(dev, reg, mxc_jpeg_is_extended_sequential(q_data->fmt));
goto job_unlock;
}
- if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed) {
+ if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed &&
+ mxc_dec_is_ongoing(ctx)) {
jpeg_src_buf->dht_needed = false;
dev_dbg(dev, "Decoder DHT cfg finished. Start decoding...\n");
goto job_unlock;
@@ -1209,14 +1271,14 @@ static void mxc_jpeg_config_dec_desc(struct vb2_buffer *out_buf,
*/
*cfg_size = mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr,
V4L2_PIX_FMT_YUYV,
- MXC_JPEG_MIN_WIDTH,
- MXC_JPEG_MIN_HEIGHT);
+ MXC_JPEG_PATTERN_WIDTH,
+ MXC_JPEG_PATTERN_HEIGHT);
cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN;
- cfg_desc->buf_base0 = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ cfg_desc->buf_base0 = jpeg->slot_data.cfg_dec_daddr;
cfg_desc->buf_base1 = 0;
- cfg_desc->imgsize = MXC_JPEG_MIN_WIDTH << 16;
- cfg_desc->imgsize |= MXC_JPEG_MIN_HEIGHT;
- cfg_desc->line_pitch = MXC_JPEG_MIN_WIDTH * 2;
+ cfg_desc->imgsize = MXC_JPEG_PATTERN_WIDTH << 16;
+ cfg_desc->imgsize |= MXC_JPEG_PATTERN_HEIGHT;
+ cfg_desc->line_pitch = MXC_JPEG_PATTERN_WIDTH * 2;
cfg_desc->stm_ctrl = STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV422);
cfg_desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1);
cfg_desc->stm_bufbase = cfg_stream_handle;
@@ -1918,9 +1980,19 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb)
jpeg_src_buf = vb2_to_mxc_buf(vb);
jpeg_src_buf->jpeg_parse_error = false;
ret = mxc_jpeg_parse(ctx, vb);
- if (ret)
+ if (ret) {
jpeg_src_buf->jpeg_parse_error = true;
+ /*
+ * if the capture queue is not setup, the device_run() won't be scheduled,
+ * need to drop the error buffer, so that the decoding can continue
+ */
+ if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) {
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ return;
+ }
+ }
+
end:
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index 86e324b21aed..fdde45f7e163 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -28,6 +28,8 @@
#define MXC_JPEG_W_ALIGN 3
#define MXC_JPEG_MAX_SIZEIMAGE 0xFFFFFC00
#define MXC_JPEG_MAX_PLANES 2
+#define MXC_JPEG_PATTERN_WIDTH 128
+#define MXC_JPEG_PATTERN_HEIGHT 64
enum mxc_jpeg_enc_state {
MXC_JPEG_ENCODING = 0, /* jpeg encode phase */
@@ -117,6 +119,9 @@ struct mxc_jpeg_slot_data {
dma_addr_t desc_handle;
dma_addr_t cfg_desc_handle; // configuration descriptor dma address
dma_addr_t cfg_stream_handle; // configuration bitstream dma address
+ dma_addr_t cfg_dec_size;
+ void *cfg_dec_vaddr;
+ dma_addr_t cfg_dec_daddr;
};
struct mxc_jpeg_dev {
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index 29523bb84d95..d060eadebc7a 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -597,12 +597,13 @@ static void __mipi_csis_set_format(struct mipi_csis_device *csis,
static int mipi_csis_calculate_params(struct mipi_csis_device *csis,
const struct csis_pix_format *csis_fmt)
{
+ struct media_pad *src_pad =
+ &csis->source.sd->entity.pads[csis->source.pad->index];
s64 link_freq;
u32 lane_rate;
/* Calculate the line rate from the pixel rate. */
- link_freq = v4l2_get_link_freq(csis->source.sd->ctrl_handler,
- csis_fmt->width,
+ link_freq = v4l2_get_link_freq(src_pad, csis_fmt->width,
csis->bus.num_data_lanes * 2);
if (link_freq < 0) {
dev_err(csis->dev, "Unable to obtain link frequency: %d\n",
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
index 794050a6a919..22e49d3a1287 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
@@ -43,6 +43,7 @@ struct mxc_isi_m2m_ctx_queue_data {
struct v4l2_pix_format_mplane format;
const struct mxc_isi_format_info *info;
u32 sequence;
+ bool streaming;
};
struct mxc_isi_m2m_ctx {
@@ -484,15 +485,18 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh,
enum v4l2_buf_type type)
{
struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
+ struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type);
const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format;
const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format;
const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info;
const struct mxc_isi_format_info *out_info = ctx->queues.out.info;
struct mxc_isi_m2m *m2m = ctx->m2m;
bool bypass;
-
int ret;
+ if (q->streaming)
+ return 0;
+
mutex_lock(&m2m->lock);
if (m2m->usage_count == INT_MAX) {
@@ -545,6 +549,8 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh,
goto unchain;
}
+ q->streaming = true;
+
return 0;
unchain:
@@ -567,10 +573,14 @@ static int mxc_isi_m2m_streamoff(struct file *file, void *fh,
enum v4l2_buf_type type)
{
struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
+ struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type);
struct mxc_isi_m2m *m2m = ctx->m2m;
v4l2_m2m_ioctl_streamoff(file, fh, type);
+ if (!q->streaming)
+ return 0;
+
mutex_lock(&m2m->lock);
/*
@@ -596,6 +606,8 @@ static int mxc_isi_m2m_streamoff(struct file *file, void *fh,
mutex_unlock(&m2m->lock);
+ q->streaming = false;
+
return 0;
}
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index 1f2657cf6e82..a8bcf60e2f37 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -287,6 +287,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
struct v4l2_subdev_state *sd_state,
u32 *hs_settle)
{
+ struct media_pad *src_pad;
s64 link_freq;
u32 lane_rate;
unsigned long esc_clk_rate;
@@ -294,13 +295,19 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
const struct v4l2_mbus_framefmt *fmt;
const struct csi2_pix_format *csi2_fmt;
+ src_pad = media_entity_remote_source_pad_unique(&sd_state->sd->entity);
+ if (IS_ERR(src_pad)) {
+ dev_err(state->dev, "can't get source pad of %s (%ld)\n",
+ sd_state->sd->name, PTR_ERR(src_pad));
+ return PTR_ERR(src_pad);
+ }
+
/* Calculate the line rate from the pixel rate. */
fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK);
csi2_fmt = find_csi2_format(fmt->code);
- link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler,
- csi2_fmt->width,
+ link_freq = v4l2_get_link_freq(src_pad, csi2_fmt->width,
state->bus.num_data_lanes * 2);
if (link_freq < 0) {
dev_err(state->dev, "Unable to obtain link frequency: %d\n",
diff --git a/drivers/media/platform/qcom/Kconfig b/drivers/media/platform/qcom/Kconfig
index cc5799b9ea00..4f4d3a68e6e5 100644
--- a/drivers/media/platform/qcom/Kconfig
+++ b/drivers/media/platform/qcom/Kconfig
@@ -3,4 +3,5 @@
comment "Qualcomm media platform drivers"
source "drivers/media/platform/qcom/camss/Kconfig"
+source "drivers/media/platform/qcom/iris/Kconfig"
source "drivers/media/platform/qcom/venus/Kconfig"
diff --git a/drivers/media/platform/qcom/Makefile b/drivers/media/platform/qcom/Makefile
index 4f055c396e04..ea2221a202c0 100644
--- a/drivers/media/platform/qcom/Makefile
+++ b/drivers/media/platform/qcom/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += camss/
+obj-y += iris/
obj-y += venus/
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index e636968a1126..d26a9c24a430 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -6,7 +6,9 @@ qcom-camss-objs += \
camss-csid.o \
camss-csid-4-1.o \
camss-csid-4-7.o \
+ camss-csid-680.o \
camss-csid-gen2.o \
+ camss-csid-780.o \
camss-csiphy-2ph-1-0.o \
camss-csiphy-3ph-1-0.o \
camss-csiphy.o \
@@ -16,6 +18,8 @@ qcom-camss-objs += \
camss-vfe-4-8.o \
camss-vfe-17x.o \
camss-vfe-480.o \
+ camss-vfe-680.o \
+ camss-vfe-780.o \
camss-vfe-gen1.o \
camss-vfe.o \
camss-video.o \
diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-1.c b/drivers/media/platform/qcom/camss/camss-csid-4-1.c
index c95861420502..6998e1c52895 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-4-1.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-4-1.c
@@ -17,7 +17,6 @@
#include "camss-csid-gen1.h"
#include "camss.h"
-#define CAMSS_CSID_HW_VERSION 0x0
#define CAMSS_CSID_CORE_CTRL_0 0x004
#define CAMSS_CSID_CORE_CTRL_1 0x008
#define CAMSS_CSID_RST_CMD 0x00c
@@ -139,15 +138,6 @@ static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
return 0;
}
-static u32 csid_hw_version(struct csid_device *csid)
-{
- u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
-
- dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static irqreturn_t csid_isr(int irq, void *dev)
{
struct csid_device *csid = dev;
@@ -180,15 +170,6 @@ static int csid_reset(struct csid_device *csid)
return 0;
}
-static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
- unsigned int match_format_idx, u32 match_code)
-{
- if (match_format_idx > 0)
- return 0;
-
- return sink_code;
-}
-
static void csid_subdev_init(struct csid_device *csid)
{
csid->testgen.modes = csid_testgen_modes;
diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-7.c b/drivers/media/platform/qcom/camss/camss-csid-4-7.c
index 08578a143688..66054d4872e6 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-4-7.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-4-7.c
@@ -16,7 +16,6 @@
#include "camss-csid-gen1.h"
#include "camss.h"
-#define CAMSS_CSID_HW_VERSION 0x0
#define CAMSS_CSID_CORE_CTRL_0 0x004
#define CAMSS_CSID_CORE_CTRL_1 0x008
#define CAMSS_CSID_RST_CMD 0x010
@@ -151,15 +150,6 @@ static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
return 0;
}
-static u32 csid_hw_version(struct csid_device *csid)
-{
- u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
-
- dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
/*
* isr - CSID module interrupt service routine
* @irq: Interrupt line
@@ -205,38 +195,6 @@ static int csid_reset(struct csid_device *csid)
return 0;
}
-static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
- unsigned int match_format_idx, u32 match_code)
-{
- switch (sink_code) {
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- case MEDIA_BUS_FMT_Y10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_Y10_1X10,
- MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- default:
- if (match_format_idx > 0)
- return 0;
-
- return sink_code;
- }
-}
-
static void csid_subdev_init(struct csid_device *csid)
{
csid->testgen.modes = csid_testgen_modes;
diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c
new file mode 100644
index 000000000000..3ad3a174bcfb
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-680.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
+ *
+ * Copyright (C) 2020-2025 Linaro Ltd.
+ */
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include "camss.h"
+#include "camss-csid.h"
+#include "camss-csid-gen2.h"
+
+#define CSID_TOP_IO_PATH_CFG0(csid) (0x4 * (csid))
+#define CSID_TOP_IO_PATH_CFG0_INTERNAL_CSID BIT(0)
+#define CSID_TOP_IO_PATH_CFG0_SFE_0 BIT(1)
+#define CSID_TOP_IO_PATH_CFG0_SFE_1 GENMASK(1, 0)
+#define CSID_TOP_IO_PATH_CFG0_SBI_0 BIT(4)
+#define CSID_TOP_IO_PATH_CFG0_SBI_1 GENMASK(3, 0)
+#define CSID_TOP_IO_PATH_CFG0_SBI_2 GENMASK(3, 1)
+#define CSID_TOP_IO_PATH_CFG0_OUTPUT_IFE_EN BIT(8)
+#define CSID_TOP_IO_PATH_CFG0_SFE_OFFLINE_EN BIT(12)
+
+#define CSID_RESET_CMD 0x10
+#define CSID_RESET_CMD_HW_RESET BIT(0)
+#define CSID_RESET_CMD_SW_RESET BIT(1)
+#define CSID_RESET_CMD_IRQ_CTRL BIT(2)
+
+#define CSID_IRQ_CMD 0x14
+#define CSID_IRQ_CMD_CLEAR BIT(0)
+#define CSID_IRQ_CMD_SET BIT(4)
+
+#define CSID_REG_UPDATE_CMD 0x18
+
+#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) (0xec + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_CCIF_VIOLATION BIT(29)
+#define CSID_CSI2_RDIN_SENSOR_SWITCH_OUT_OF_SYNC_FRAME_DROP BIT(28)
+#define CSID_CSI2_RDIN_ERROR_REC_WIDTH_VIOLATION BIT(27)
+#define CSID_CSI2_RDIN_ERROR_REC_HEIGHT_VIOLATION BIT(26)
+#define CSID_CSI2_RDIN_BATCH_END_MISSING_VIOLATION BIT(25)
+#define CSID_CSI2_RDIN_ILLEGAL_BATCH_ID_IRQ BIT(24)
+#define CSID_CSI2_RDIN_RUP_DONE BIT(23)
+#define CSID_CSI2_RDIN_CAMIF_EPOCH_1_IRQ BIT(22)
+#define CSID_CSI2_RDIN_CAMIF_EPOCH_0_IRQ BIT(21)
+#define CSID_CSI2_RDIN_ERROR_REC_OVERFLOW_IRQ BIT(19)
+#define CSID_CSI2_RDIN_ERROR_REC_FRAME_DROP BIT(18)
+#define CSID_CSI2_RDIN_VCDT_GRP_CHANG BIT(17)
+#define CSID_CSI2_RDIN_VCDT_GRP_0_SEL BIT(16)
+#define CSID_CSI2_RDIN_VCDT_GRP_1_SEL BIT(15)
+#define CSID_CSI2_RDIN_ERROR_LINE_COUNT BIT(14)
+#define CSID_CSI2_RDIN_ERROR_PIX_COUNT BIT(13)
+#define CSID_CSI2_RDIN_INFO_INPUT_SOF BIT(12)
+#define CSID_CSI2_RDIN_INFO_INPUT_SOL BIT(11)
+#define CSID_CSI2_RDIN_INFO_INPUT_EOL BIT(10)
+#define CSID_CSI2_RDIN_INFO_INPUT_EOF BIT(9)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_SOF BIT(8)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_SOL BIT(7)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_EOL BIT(6)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_EOF BIT(5)
+#define CSID_CSI2_RDIN_INFO_CAMIF_SOF BIT(4)
+#define CSID_CSI2_RDIN_INFO_CAMIF_EOF BIT(3)
+#define CSID_CSI2_RDIN_INFO_FIFO_OVERFLOW BIT(2)
+#define CSID_CSI2_RDIN_RES1 BIT(1)
+#define CSID_CSI2_RDIN_RES0 BIT(0)
+
+#define CSID_CSI2_RDIN_IRQ_MASK(rdi) (0xf0 + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) (0xf4 + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_SET(rdi) (0xf8 + 0x10 * (rdi))
+
+#define CSID_TOP_IRQ_STATUS 0x7c
+#define CSID_TOP_IRQ_MASK 0x80
+#define CSID_TOP_IRQ_CLEAR 0x84
+#define CSID_TOP_IRQ_RESET BIT(0)
+#define CSID_TOP_IRQ_RX BIT(2)
+#define CSID_TOP_IRQ_LONG_PKT(rdi) (BIT(8) << (rdi))
+#define CSID_TOP_IRQ_BUF_DONE BIT(13)
+
+#define CSID_BUF_DONE_IRQ_STATUS 0x8c
+#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? 1 : 14)
+#define CSID_BUF_DONE_IRQ_MASK 0x90
+#define CSID_BUF_DONE_IRQ_CLEAR 0x94
+
+#define CSID_CSI2_RX_IRQ_STATUS 0x9c
+#define CSID_CSI2_RX_IRQ_MASK 0xa0
+#define CSID_CSI2_RX_IRQ_CLEAR 0xa4
+
+#define CSID_RESET_CFG 0xc
+#define CSID_RESET_CFG_MODE_IMMEDIATE BIT(0)
+#define CSID_RESET_CFG_LOCATION_COMPLETE BIT(4)
+
+#define CSID_CSI2_RDI_IRQ_STATUS(rdi) (0xec + 0x10 * (rdi))
+#define CSID_CSI2_RDI_IRQ_MASK(rdi) (0xf0 + 0x10 * (rdi))
+#define CSID_CSI2_RDI_IRQ_CLEAR(rdi) (0xf4 + 0x10 * (rdi))
+
+#define CSID_CSI2_RX_CFG0 0x200
+#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0
+#define CSI2_RX_CFG0_DL0_INPUT_SEL 4
+#define CSI2_RX_CFG0_DL1_INPUT_SEL 8
+#define CSI2_RX_CFG0_DL2_INPUT_SEL 12
+#define CSI2_RX_CFG0_DL3_INPUT_SEL 16
+#define CSI2_RX_CFG0_PHY_NUM_SEL 20
+#define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1
+#define CSI2_RX_CFG0_PHY_TYPE_SEL 24
+
+#define CSID_CSI2_RX_CFG1 0x204
+#define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN BIT(0)
+#define CSI2_RX_CFG1_DE_SCRAMBLE_EN BIT(1)
+#define CSI2_RX_CFG1_VC_MODE BIT(2)
+#define CSI2_RX_CFG1_COMPLETE_STREAM_EN BIT(4)
+#define CSI2_RX_CFG1_COMPLETE_STREAM_FRAME_TIMING BIT(5)
+#define CSI2_RX_CFG1_MISR_EN BIT(6)
+#define CSI2_RX_CFG1_CGC_MODE BIT(7)
+
+#define CSID_CSI2_RX_CAPTURE_CTRL 0x208
+#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_EN BIT(0)
+#define CSI2_RX_CAPTURE_CTRL_SHORT_PKT_EN BIT(1)
+#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_EN BIT(2)
+#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_DT GENMASK(9, 4)
+#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_VC GENMASK(14, 10)
+#define CSI2_RX_CAPTURE_CTRL_SHORT_PKT_VC GENMASK(19, 15)
+#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_DT GENMASK(20, 25)
+#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_VC GENMASK(30, 26)
+
+#define CSID_CSI2_RX_TOTAL_PKTS_RCVD 0x240
+#define CSID_CSI2_RX_STATS_ECC 0x244
+#define CSID_CSI2_RX_CRC_ERRORS 0x248
+
+#define CSID_RDI_CFG0(rdi) (0x500 + 0x100 * (rdi))
+#define RDI_CFG0_DECODE_FORMAT 12
+#define RDI_CFG0_DATA_TYPE 16
+#define RDI_CFG0_VIRTUAL_CHANNEL 22
+#define RDI_CFG0_DT_ID 27
+#define RDI_CFG0_ENABLE BIT(31)
+
+#define CSID_RDI_CTRL(rdi) (0x504 + 0x100 * (rdi))
+#define CSID_RDI_CTRL_HALT_CMD_HALT_AT_FRAME_BOUNDARY 0
+#define CSID_RDI_CTRL_HALT_CMD_RESUME_AT_FRAME_BOUNDARY 1
+
+#define CSID_RDI_CFG1(rdi) (0x510 + 0x100 * (rdi))
+#define RDI_CFG1_TIMESTAMP_STB_FRAME BIT(0)
+#define RDI_CFG1_TIMESTAMP_STB_IRQ BIT(1)
+#define RDI_CFG1_BYTE_CNTR_EN BIT(2)
+#define RDI_CFG1_TIMESTAMP_EN BIT(4)
+#define RDI_CFG1_DROP_H_EN BIT(5)
+#define RDI_CFG1_DROP_V_EN BIT(6)
+#define RDI_CFG1_CROP_H_EN BIT(7)
+#define RDI_CFG1_CROP_V_EN BIT(8)
+#define RDI_CFG1_MISR_EN BIT(9)
+#define RDI_CFG1_PLAIN_ALIGN_MSB BIT(11)
+#define RDI_CFG1_EARLY_EOF_EN BIT(14)
+#define RDI_CFG1_PACKING_MIPI BIT(15)
+
+#define CSID_RDI_ERR_RECOVERY_CFG0(rdi) (0x514 + 0x100 * (rdi))
+#define CSID_RDI_EPOCH_IRQ_CFG(rdi) (0x52c + 0x100 * (rdi))
+#define CSID_RDI_FRM_DROP_PATTERN(rdi) (0x540 + 0x100 * (rdi))
+#define CSID_RDI_FRM_DROP_PERIOD(rdi) (0x544 + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) (0x548 + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) (0x54c + 0x100 * (rdi))
+#define CSID_RDI_PIX_DROP_PATTERN(rdi) (0x558 + 0x100 * (rdi))
+#define CSID_RDI_PIX_DROP_PERIOD(rdi) (0x55c + 0x100 * (rdi))
+#define CSID_RDI_LINE_DROP_PATTERN(rdi) (0x560 + 0x100 * (rdi))
+#define CSID_RDI_LINE_DROP_PERIOD(rdi) (0x564 + 0x100 * (rdi))
+
+static inline int reg_update_rdi(struct csid_device *csid, int n)
+{
+ return BIT(4 + n) + BIT(20 + n);
+}
+
+static void csid_reg_update(struct csid_device *csid, int port_id)
+{
+ csid->reg_update |= reg_update_rdi(csid, port_id);
+ writel(csid->reg_update, csid->base + CSID_REG_UPDATE_CMD);
+}
+
+static inline void csid_reg_update_clear(struct csid_device *csid,
+ int port_id)
+{
+ csid->reg_update &= ~reg_update_rdi(csid, port_id);
+ writel(csid->reg_update, csid->base + CSID_REG_UPDATE_CMD);
+}
+
+static void __csid_configure_rx(struct csid_device *csid,
+ struct csid_phy_config *phy, int vc)
+{
+ u32 val;
+
+ val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
+ val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
+ val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL;
+
+ writel(val, csid->base + CSID_CSI2_RX_CFG0);
+
+ val = CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN;
+ if (vc > 3)
+ val |= CSI2_RX_CFG1_VC_MODE;
+ writel(val, csid->base + CSID_CSI2_RX_CFG1);
+}
+
+static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
+{
+ u32 val;
+
+ if (enable)
+ val = CSID_RDI_CTRL_HALT_CMD_RESUME_AT_FRAME_BOUNDARY;
+ else
+ val = CSID_RDI_CTRL_HALT_CMD_HALT_AT_FRAME_BOUNDARY;
+
+ writel(val, csid->base + CSID_RDI_CTRL(rdi));
+}
+
+static void __csid_configure_top(struct csid_device *csid)
+{
+ u32 val;
+
+ val = CSID_TOP_IO_PATH_CFG0_OUTPUT_IFE_EN | CSID_TOP_IO_PATH_CFG0_INTERNAL_CSID;
+ writel(val, csid->camss->csid_wrapper_base +
+ CSID_TOP_IO_PATH_CFG0(csid->id));
+}
+
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+{
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
+ input_format->code);
+ u8 lane_cnt = csid->phy.lane_cnt;
+ u8 dt_id;
+ u32 val;
+
+ if (!lane_cnt)
+ lane_cnt = 4;
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+
+ /*
+ * DT_ID is a two bit bitfield that is concatenated with
+ * the four least significant bits of the five bit VC
+ * bitfield to generate an internal CID value.
+ *
+ * CSID_RDI_CFG0(vc)
+ * DT_ID : 28:27
+ * VC : 26:22
+ * DT : 21:16
+ *
+ * CID : VC 3:0 << 2 | DT_ID 1:0
+ */
+ dt_id = vc & 0x03;
+
+ /* note: for non-RDI path, this should be format->decode_format */
+ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
+ val |= format->data_type << RDI_CFG0_DATA_TYPE;
+ val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
+ val |= dt_id << RDI_CFG0_DT_ID;
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+
+ val = RDI_CFG1_TIMESTAMP_STB_FRAME;
+ val |= RDI_CFG1_BYTE_CNTR_EN;
+ val |= RDI_CFG1_TIMESTAMP_EN;
+ val |= RDI_CFG1_DROP_H_EN;
+ val |= RDI_CFG1_DROP_V_EN;
+ val |= RDI_CFG1_CROP_H_EN;
+ val |= RDI_CFG1_CROP_V_EN;
+ val |= RDI_CFG1_PACKING_MIPI;
+
+ writel(val, csid->base + CSID_RDI_CFG1(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+
+ val = 1;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_CTRL(vc));
+
+ val = readl(csid->base + CSID_RDI_CFG0(vc));
+ if (enable)
+ val |= RDI_CFG0_ENABLE;
+ else
+ val &= ~RDI_CFG0_ENABLE;
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+}
+
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
+{
+ int i;
+
+ __csid_configure_top(csid);
+
+ /* Loop through all enabled VCs and configure stream for each */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+ if (csid->phy.en_vc & BIT(i)) {
+ __csid_configure_rdi_stream(csid, enable, i);
+ __csid_configure_rx(csid, &csid->phy, i);
+ __csid_ctrl_rdi(csid, enable, i);
+ }
+ }
+}
+
+/*
+ * csid_reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_reset(struct csid_device *csid)
+{
+ unsigned long time;
+ u32 val;
+ int i;
+
+ reinit_completion(&csid->reset_complete);
+
+ writel(CSID_IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+
+ /* preserve registers */
+ val = CSID_RESET_CFG_MODE_IMMEDIATE | CSID_RESET_CFG_LOCATION_COMPLETE;
+ writel(val, csid->base + CSID_RESET_CFG);
+
+ val = CSID_RESET_CMD_HW_RESET | CSID_RESET_CMD_SW_RESET;
+ writel(val, csid->base + CSID_RESET_CMD);
+
+ time = wait_for_completion_timeout(&csid->reset_complete,
+ msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+ if (!time) {
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+ /* Enable RUP done for the client port */
+ writel(CSID_CSI2_RDIN_RUP_DONE, csid->base + CSID_CSI2_RDIN_IRQ_MASK(i));
+ }
+
+ /* Clear RDI status */
+ writel(~0u, csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+
+ /* Enable BUF_DONE bit for all write-master client ports */
+ writel(~0u, csid->base + CSID_BUF_DONE_IRQ_MASK);
+
+ /* Unmask all TOP interrupts */
+ writel(~0u, csid->base + CSID_TOP_IRQ_MASK);
+
+ return 0;
+}
+
+static void csid_rup_complete(struct csid_device *csid, int rdi)
+{
+ csid_reg_update_clear(csid, rdi);
+}
+
+/*
+ * csid_isr - CSID module interrupt service routine
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+ struct csid_device *csid = dev;
+ u32 buf_done_val, val, val_top;
+ int i;
+
+ /* Latch and clear TOP status */
+ val_top = readl(csid->base + CSID_TOP_IRQ_STATUS);
+ writel(val_top, csid->base + CSID_TOP_IRQ_CLEAR);
+
+ /* Latch and clear CSID_CSI2 status */
+ val = readl(csid->base + CSID_CSI2_RX_IRQ_STATUS);
+ writel(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR);
+
+ /* Latch and clear top level BUF_DONE status */
+ buf_done_val = readl(csid->base + CSID_BUF_DONE_IRQ_STATUS);
+ writel(buf_done_val, csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+
+ /* Process state for each RDI channel */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+ val = readl(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
+ if (val)
+ writel(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+
+ if (val & CSID_CSI2_RDIN_RUP_DONE)
+ csid_rup_complete(csid, i);
+
+ if (buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i))
+ camss_buf_done(csid->camss, csid->id, i);
+ }
+
+ /* Issue clear command */
+ writel(CSID_IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+
+ /* Reset complete */
+ if (val_top & CSID_TOP_IRQ_RESET)
+ complete(&csid->reset_complete);
+
+ return IRQ_HANDLED;
+}
+
+static void csid_subdev_reg_update(struct csid_device *csid, int port_id, bool is_clear)
+{
+ if (is_clear)
+ csid_reg_update_clear(csid, port_id);
+ else
+ csid_reg_update(csid, port_id);
+}
+
+static void csid_subdev_init(struct csid_device *csid) {}
+
+const struct csid_hw_ops csid_ops_680 = {
+ .configure_testgen_pattern = NULL,
+ .configure_stream = csid_configure_stream,
+ .hw_version = csid_hw_version,
+ .isr = csid_isr,
+ .reset = csid_reset,
+ .src_pad_code = csid_src_pad_code,
+ .subdev_init = csid_subdev_init,
+ .reg_update = csid_subdev_reg_update,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csid-780.c b/drivers/media/platform/qcom/camss/camss-csid-780.c
new file mode 100644
index 000000000000..4c720d177731
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-780.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
+ *
+ * Copyright (c) 2024 Qualcomm Technologies, Inc.
+ */
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include "camss.h"
+#include "camss-csid.h"
+#include "camss-csid-780.h"
+
+#define CSID_IO_PATH_CFG0(csid) (0x4 * (csid))
+#define OUTPUT_IFE_EN 0x100
+#define INTERNAL_CSID 1
+
+#define CSID_RST_CFG 0xC
+#define RST_MODE BIT(0)
+#define RST_LOCATION BIT(4)
+
+#define CSID_RST_CMD 0x10
+#define SELECT_HW_RST BIT(0)
+#define SELECT_IRQ_RST BIT(2)
+
+#define CSID_IRQ_CMD 0x14
+#define IRQ_CMD_CLEAR BIT(0)
+
+#define CSID_RUP_AUP_CMD 0x18
+#define CSID_RUP_AUP_RDI(rdi) ((BIT(4) | BIT(20)) << (rdi))
+
+#define CSID_TOP_IRQ_STATUS 0x7C
+#define TOP_IRQ_STATUS_RESET_DONE BIT(0)
+
+#define CSID_TOP_IRQ_MASK 0x80
+#define CSID_TOP_IRQ_CLEAR 0x84
+#define CSID_TOP_IRQ_SET 0x88
+
+#define CSID_CSI2_RX_IRQ_STATUS 0x9C
+#define CSID_CSI2_RX_IRQ_MASK 0xA0
+#define CSID_CSI2_RX_IRQ_CLEAR 0xA4
+#define CSID_CSI2_RX_IRQ_SET 0xA8
+
+#define CSID_BUF_DONE_IRQ_STATUS 0x8C
+#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? 1 : 14)
+#define CSID_BUF_DONE_IRQ_MASK 0x90
+#define CSID_BUF_DONE_IRQ_CLEAR 0x94
+#define CSID_BUF_DONE_IRQ_SET 0x98
+
+#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) (0xEC + 0x10 * (rdi))
+#define RUP_DONE_IRQ_STATUS BIT(23)
+
+#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) (0xF4 + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_SET(rdi) (0xF8 + 0x10 * (rdi))
+
+#define CSID_CSI2_RX_CFG0 0x200
+#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0
+#define CSI2_RX_CFG0_DL0_INPUT_SEL 4
+#define CSI2_RX_CFG0_PHY_NUM_SEL 20
+
+#define CSID_CSI2_RX_CFG1 0x204
+#define CSI2_RX_CFG1_ECC_CORRECTION_EN BIT(0)
+#define CSI2_RX_CFG1_VC_MODE BIT(2)
+
+#define CSID_RDI_CFG0(rdi) (0x500 + 0x100 * (rdi))
+#define RDI_CFG0_TIMESTAMP_EN BIT(6)
+#define RDI_CFG0_TIMESTAMP_STB_SEL BIT(8)
+#define RDI_CFG0_DECODE_FORMAT 12
+#define RDI_CFG0_DT 16
+#define RDI_CFG0_VC 22
+#define RDI_CFG0_DT_ID 27
+#define RDI_CFG0_EN BIT(31)
+
+#define CSID_RDI_CTRL(rdi) (0x504 + 0x100 * (rdi))
+#define RDI_CTRL_START_CMD BIT(0)
+
+#define CSID_RDI_CFG1(rdi) (0x510 + 0x100 * (rdi))
+#define RDI_CFG1_DROP_H_EN BIT(5)
+#define RDI_CFG1_DROP_V_EN BIT(6)
+#define RDI_CFG1_CROP_H_EN BIT(7)
+#define RDI_CFG1_CROP_V_EN BIT(8)
+#define RDI_CFG1_PIX_STORE BIT(10)
+#define RDI_CFG1_PACKING_FORMAT_MIPI BIT(15)
+
+#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) (0x548 + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) (0x54C + 0x100 * (rdi))
+
+#define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1
+
+static void __csid_configure_rx(struct csid_device *csid,
+ struct csid_phy_config *phy, int vc)
+{
+ int val;
+
+ val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
+ val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
+ val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL;
+
+ writel(val, csid->base + CSID_CSI2_RX_CFG0);
+
+ val = CSI2_RX_CFG1_ECC_CORRECTION_EN;
+ if (vc > 3)
+ val |= CSI2_RX_CFG1_VC_MODE;
+
+ writel(val, csid->base + CSID_CSI2_RX_CFG1);
+}
+
+static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
+{
+ int val = 0;
+
+ if (enable)
+ val = RDI_CTRL_START_CMD;
+
+ writel(val, csid->base + CSID_RDI_CTRL(rdi));
+}
+
+static void __csid_configure_wrapper(struct csid_device *csid)
+{
+ u32 val;
+
+ /* csid lite doesn't need to configure top register */
+ if (csid->res->is_lite)
+ return;
+
+ val = OUTPUT_IFE_EN | INTERNAL_CSID;
+ writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id));
+}
+
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+{
+ u32 val;
+ u8 lane_cnt = csid->phy.lane_cnt;
+ /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
+ input_format->code);
+
+ if (!lane_cnt)
+ lane_cnt = 4;
+
+ /*
+ * DT_ID is a two bit bitfield that is concatenated with
+ * the four least significant bits of the five bit VC
+ * bitfield to generate an internal CID value.
+ *
+ * CSID_RDI_CFG0(vc)
+ * DT_ID : 28:27
+ * VC : 26:22
+ * DT : 21:16
+ *
+ * CID : VC 3:0 << 2 | DT_ID 1:0
+ */
+ u8 dt_id = vc & 0x03;
+
+ val = RDI_CFG0_TIMESTAMP_EN;
+ val |= RDI_CFG0_TIMESTAMP_STB_SEL;
+ /* note: for non-RDI path, this should be format->decode_format */
+ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
+ val |= vc << RDI_CFG0_VC;
+ val |= format->data_type << RDI_CFG0_DT;
+ val |= dt_id << RDI_CFG0_DT_ID;
+
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+
+ val = RDI_CFG1_PACKING_FORMAT_MIPI;
+ val |= RDI_CFG1_PIX_STORE;
+ val |= RDI_CFG1_DROP_H_EN;
+ val |= RDI_CFG1_DROP_V_EN;
+ val |= RDI_CFG1_CROP_H_EN;
+ val |= RDI_CFG1_CROP_V_EN;
+
+ writel(val, csid->base + CSID_RDI_CFG1(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+
+ val = 1;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_CTRL(vc));
+
+ val = readl(csid->base + CSID_RDI_CFG0(vc));
+
+ if (enable)
+ val |= RDI_CFG0_EN;
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+}
+
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
+{
+ u8 i;
+
+ __csid_configure_wrapper(csid);
+
+ /* Loop through all enabled VCs and configure stream for each */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
+ if (csid->phy.en_vc & BIT(i)) {
+ __csid_configure_rdi_stream(csid, enable, i);
+ __csid_configure_rx(csid, &csid->phy, i);
+ __csid_ctrl_rdi(csid, enable, i);
+ }
+}
+
+static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
+{
+ return 0;
+}
+
+static void csid_subdev_reg_update(struct csid_device *csid, int port_id, bool clear)
+{
+ if (clear) {
+ csid->reg_update &= ~CSID_RUP_AUP_RDI(port_id);
+ } else {
+ csid->reg_update |= CSID_RUP_AUP_RDI(port_id);
+ writel(csid->reg_update, csid->base + CSID_RUP_AUP_CMD);
+ }
+}
+
+/*
+ * csid_isr - CSID module interrupt service routine
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+ struct csid_device *csid = dev;
+ u32 val, buf_done_val;
+ u8 reset_done;
+ int i;
+
+ val = readl(csid->base + CSID_TOP_IRQ_STATUS);
+ writel(val, csid->base + CSID_TOP_IRQ_CLEAR);
+ reset_done = val & TOP_IRQ_STATUS_RESET_DONE;
+
+ val = readl(csid->base + CSID_CSI2_RX_IRQ_STATUS);
+ writel(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR);
+
+ buf_done_val = readl(csid->base + CSID_BUF_DONE_IRQ_STATUS);
+ writel(buf_done_val, csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+
+ /* Read and clear IRQ status for each enabled RDI channel */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
+ if (csid->phy.en_vc & BIT(i)) {
+ val = readl(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
+ writel(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+
+ if (val & RUP_DONE_IRQ_STATUS)
+ /* clear the reg update bit */
+ csid_subdev_reg_update(csid, i, true);
+
+ if (buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i)) {
+ /*
+ * For Titan 780, bus done and RUP IRQ have been moved to
+ * CSID from VFE. Once CSID received bus done, need notify
+ * VFE of this event. Trigger VFE to handle bus done process.
+ */
+ camss_buf_done(csid->camss, csid->id, i);
+ }
+ }
+
+ val = IRQ_CMD_CLEAR;
+ writel(val, csid->base + CSID_IRQ_CMD);
+
+ if (reset_done)
+ complete(&csid->reset_complete);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csid_reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_reset(struct csid_device *csid)
+{
+ unsigned long time;
+ u32 val;
+ int i;
+
+ reinit_completion(&csid->reset_complete);
+
+ writel(1, csid->base + CSID_TOP_IRQ_CLEAR);
+ writel(1, csid->base + CSID_IRQ_CMD);
+ writel(1, csid->base + CSID_TOP_IRQ_MASK);
+
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
+ if (csid->phy.en_vc & BIT(i)) {
+ writel(BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i),
+ csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+ writel(IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+ writel(BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i),
+ csid->base + CSID_BUF_DONE_IRQ_MASK);
+ }
+
+ /* preserve registers */
+ val = RST_LOCATION | RST_MODE;
+ writel(val, csid->base + CSID_RST_CFG);
+
+ val = SELECT_HW_RST | SELECT_IRQ_RST;
+ writel(val, csid->base + CSID_RST_CMD);
+
+ time = wait_for_completion_timeout(&csid->reset_complete,
+ msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+ if (!time) {
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void csid_subdev_init(struct csid_device *csid)
+{
+ csid->testgen.nmodes = CSID_PAYLOAD_MODE_DISABLED;
+}
+
+const struct csid_hw_ops csid_ops_780 = {
+ .configure_stream = csid_configure_stream,
+ .configure_testgen_pattern = csid_configure_testgen_pattern,
+ .hw_version = csid_hw_version,
+ .isr = csid_isr,
+ .reset = csid_reset,
+ .src_pad_code = csid_src_pad_code,
+ .subdev_init = csid_subdev_init,
+ .reg_update = csid_subdev_reg_update,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csid-780.h b/drivers/media/platform/qcom/camss/camss-csid-780.h
new file mode 100644
index 000000000000..a990c66a60ff
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-780.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-csid-780.h
+ *
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module Generation 3
+ *
+ * Copyright (c) 2024 Qualcomm Technologies, Inc.
+ */
+#ifndef __QC_MSM_CAMSS_CSID_780_H__
+#define __QC_MSM_CAMSS_CSID_780_H__
+
+#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1
+#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2
+#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3
+#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x4
+#define DECODE_FORMAT_UNCOMPRESSED_16_BIT 0x5
+#define DECODE_FORMAT_UNCOMPRESSED_20_BIT 0x6
+#define DECODE_FORMAT_UNCOMPRESSED_24_BIT 0x7
+#define DECODE_FORMAT_PAYLOAD_ONLY 0xf
+
+#define PLAIN_FORMAT_PLAIN8 0x0 /* supports DPCM, UNCOMPRESSED_6/8_BIT */
+#define PLAIN_FORMAT_PLAIN16 0x1 /* supports DPCM, UNCOMPRESSED_10/16_BIT */
+#define PLAIN_FORMAT_PLAIN32 0x2 /* supports UNCOMPRESSED_20_BIT */
+
+#endif /* __QC_MSM_CAMSS_CSID_780_H__ */
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
index e1c757933e27..2a1746dcc1c5 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
@@ -22,11 +22,6 @@
* alternate register layout.
*/
-#define CSID_HW_VERSION 0x0
-#define HW_VERSION_STEPPING 0
-#define HW_VERSION_REVISION 16
-#define HW_VERSION_GENERATION 28
-
#define CSID_RST_STROBES 0x10
#define RST_STROBES 0
@@ -352,29 +347,6 @@ static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
}
/*
- * csid_hw_version - CSID hardware version query
- * @csid: CSID device
- *
- * Return HW version or error
- */
-static u32 csid_hw_version(struct csid_device *csid)
-{
- u32 hw_version;
- u32 hw_gen;
- u32 hw_rev;
- u32 hw_step;
-
- hw_version = readl_relaxed(csid->base + CSID_HW_VERSION);
- hw_gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
- hw_rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
- hw_step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
- dev_dbg(csid->camss->dev, "CSID HW Version = %u.%u.%u\n",
- hw_gen, hw_rev, hw_step);
-
- return hw_version;
-}
-
-/*
* csid_isr - CSID module interrupt service routine
* @irq: Interrupt line
* @dev: CSID device
@@ -443,38 +415,6 @@ static int csid_reset(struct csid_device *csid)
return 0;
}
-static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
- unsigned int match_format_idx, u32 match_code)
-{
- switch (sink_code) {
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- case MEDIA_BUS_FMT_Y10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_Y10_1X10,
- MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- default:
- if (match_format_idx > 0)
- return 0;
-
- return sink_code;
- }
-}
-
static void csid_subdev_init(struct csid_device *csid)
{
csid->testgen.modes = csid_testgen_modes;
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index 858db5d4ca75..5284b5857368 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -17,6 +17,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/media-entity.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-subdev.h>
@@ -29,6 +30,11 @@
#define VFE_480_CSID_OFFSET 0x1200
#define VFE_480_LITE_CSID_OFFSET 0x200
+#define CSID_HW_VERSION 0x0
+#define HW_VERSION_STEPPING 0
+#define HW_VERSION_REVISION 16
+#define HW_VERSION_GENERATION 28
+
#define MSM_CSID_NAME "msm_csid"
const char * const csid_testgen_modes[] = {
@@ -48,119 +54,119 @@ const char * const csid_testgen_modes[] = {
static const struct csid_format_info formats_4_1[] = {
{
MEDIA_BUS_FMT_UYVY8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_VYUY8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_YUYV8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_YVYU8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_Y10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
@@ -170,147 +176,147 @@ static const struct csid_format_info formats_4_1[] = {
static const struct csid_format_info formats_4_7[] = {
{
MEDIA_BUS_FMT_UYVY8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_VYUY8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_YUYV8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_YVYU8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SBGGR14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
},
{
MEDIA_BUS_FMT_SGBRG14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
},
{
MEDIA_BUS_FMT_SGRBG14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
},
{
MEDIA_BUS_FMT_SRGGB14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
},
{
MEDIA_BUS_FMT_Y10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
@@ -320,154 +326,154 @@ static const struct csid_format_info formats_4_7[] = {
static const struct csid_format_info formats_gen2[] = {
{
MEDIA_BUS_FMT_UYVY8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_VYUY8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_YUYV8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_YVYU8_1X16,
- DATA_TYPE_YUV422_8BIT,
+ MIPI_CSI2_DT_YUV422_8B,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
2,
},
{
MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_Y8_1X8,
- DATA_TYPE_RAW_8BIT,
+ MIPI_CSI2_DT_RAW8,
DECODE_FORMAT_UNCOMPRESSED_8_BIT,
8,
1,
},
{
MEDIA_BUS_FMT_Y10_1X10,
- DATA_TYPE_RAW_10BIT,
+ MIPI_CSI2_DT_RAW10,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
10,
1,
},
{
MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
+ MIPI_CSI2_DT_RAW12,
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
},
{
MEDIA_BUS_FMT_SBGGR14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
},
{
MEDIA_BUS_FMT_SGBRG14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
},
{
MEDIA_BUS_FMT_SGRBG14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
},
{
MEDIA_BUS_FMT_SRGGB14_1X14,
- DATA_TYPE_RAW_14BIT,
+ MIPI_CSI2_DT_RAW14,
DECODE_FORMAT_UNCOMPRESSED_14_BIT,
14,
1,
@@ -591,6 +597,78 @@ static int csid_set_clock_rates(struct csid_device *csid)
}
/*
+ * csid_hw_version - CSID hardware version query
+ * @csid: CSID device
+ *
+ * Return HW version or error
+ */
+u32 csid_hw_version(struct csid_device *csid)
+{
+ u32 hw_version;
+ u32 hw_gen;
+ u32 hw_rev;
+ u32 hw_step;
+
+ hw_version = readl_relaxed(csid->base + CSID_HW_VERSION);
+ hw_gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
+ hw_rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
+ hw_step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
+ dev_dbg(csid->camss->dev, "CSID:%d HW Version = %u.%u.%u\n",
+ csid->id, hw_gen, hw_rev, hw_step);
+
+ return hw_version;
+}
+
+/*
+ * csid_src_pad_code - Pick an output/src format based on the input/sink format
+ * @csid: CSID device
+ * @sink_code: The sink format of the input
+ * @match_format_idx: Request preferred index, as defined by subdevice csid
+ * format. Set @match_code to 0 if used.
+ * @match_code: Request preferred code, set @match_format_idx to 0 if used
+ *
+ * Return 0 on failure or src format code otherwise
+ */
+u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code)
+{
+ if (csid->camss->res->version == CAMSS_8x16) {
+ if (match_format_idx > 0)
+ return 0;
+
+ return sink_code;
+ }
+
+ switch (sink_code) {
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ case MEDIA_BUS_FMT_Y10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ default:
+ if (match_format_idx > 0)
+ return 0;
+
+ return sink_code;
+ }
+}
+
+/*
* csid_set_power - Power on/off CSID module
* @sd: CSID V4L2 subdevice
* @on: Requested power state
@@ -683,11 +761,13 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
int ret;
if (enable) {
- ret = v4l2_ctrl_handler_setup(&csid->ctrls);
- if (ret < 0) {
- dev_err(csid->camss->dev,
- "could not sync v4l2 controls: %d\n", ret);
- return ret;
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED) {
+ ret = v4l2_ctrl_handler_setup(&csid->ctrls);
+ if (ret < 0) {
+ dev_err(csid->camss->dev,
+ "could not sync v4l2 controls: %d\n", ret);
+ return ret;
+ }
}
if (!csid->testgen.enabled &&
@@ -761,7 +841,8 @@ static void csid_try_format(struct csid_device *csid,
break;
case MSM_CSID_PAD_SRC:
- if (csid->testgen_mode->cur.val == 0) {
+ if (csid->testgen.nmodes == CSID_PAYLOAD_MODE_DISABLED ||
+ csid->testgen_mode->cur.val == 0) {
/* Test generator is disabled, */
/* keep pad formats in sync */
u32 code = fmt->code;
@@ -811,7 +892,8 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd,
code->code = csid->res->formats->formats[code->index].code;
} else {
- if (csid->testgen_mode->cur.val == 0) {
+ if (csid->testgen.nmodes == CSID_PAYLOAD_MODE_DISABLED ||
+ csid->testgen_mode->cur.val == 0) {
struct v4l2_mbus_framefmt *sink_fmt;
sink_fmt = __csid_get_format(csid, sd_state,
@@ -1190,7 +1272,8 @@ static int csid_link_setup(struct media_entity *entity,
/* If test generator is enabled */
/* do not allow a link from CSIPHY to CSID */
- if (csid->testgen_mode->cur.val != 0)
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED &&
+ csid->testgen_mode->cur.val != 0)
return -EBUSY;
sd = media_entity_to_v4l2_subdev(remote->entity);
@@ -1283,24 +1366,27 @@ int msm_csid_register_entity(struct csid_device *csid,
MSM_CSID_NAME, csid->id);
v4l2_set_subdevdata(sd, csid);
- ret = v4l2_ctrl_handler_init(&csid->ctrls, 1);
- if (ret < 0) {
- dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
- return ret;
- }
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED) {
+ ret = v4l2_ctrl_handler_init(&csid->ctrls, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
+ return ret;
+ }
- csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls,
- &csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
- csid->testgen.nmodes, 0, 0,
- csid->testgen.modes);
+ csid->testgen_mode =
+ v4l2_ctrl_new_std_menu_items(&csid->ctrls,
+ &csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ csid->testgen.nmodes, 0, 0,
+ csid->testgen.modes);
- if (csid->ctrls.error) {
- dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
- ret = csid->ctrls.error;
- goto free_ctrl;
- }
+ if (csid->ctrls.error) {
+ dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
+ ret = csid->ctrls.error;
+ goto free_ctrl;
+ }
- csid->subdev.ctrl_handler = &csid->ctrls;
+ csid->subdev.ctrl_handler = &csid->ctrls;
+ }
ret = csid_init_formats(sd, NULL);
if (ret < 0) {
@@ -1331,7 +1417,8 @@ int msm_csid_register_entity(struct csid_device *csid,
media_cleanup:
media_entity_cleanup(&sd->entity);
free_ctrl:
- v4l2_ctrl_handler_free(&csid->ctrls);
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED)
+ v4l2_ctrl_handler_free(&csid->ctrls);
return ret;
}
@@ -1344,7 +1431,8 @@ void msm_csid_unregister_entity(struct csid_device *csid)
{
v4l2_device_unregister_subdev(&csid->subdev);
media_entity_cleanup(&csid->subdev.entity);
- v4l2_ctrl_handler_free(&csid->ctrls);
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED)
+ v4l2_ctrl_handler_free(&csid->ctrls);
}
inline bool csid_is_lite(struct csid_device *csid)
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index 8cdae98e4dca..9dc826d8c8f6 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -27,29 +27,6 @@
/* CSID hardware can demultiplex up to 4 outputs */
#define MSM_CSID_MAX_SRC_STREAMS 4
-#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12
-#define DATA_TYPE_YUV420_8BIT 0x18
-#define DATA_TYPE_YUV420_10BIT 0x19
-#define DATA_TYPE_YUV420_8BIT_LEGACY 0x1a
-#define DATA_TYPE_YUV420_8BIT_SHIFTED 0x1c /* Chroma Shifted Pixel Sampling */
-#define DATA_TYPE_YUV420_10BIT_SHIFTED 0x1d /* Chroma Shifted Pixel Sampling */
-#define DATA_TYPE_YUV422_8BIT 0x1e
-#define DATA_TYPE_YUV422_10BIT 0x1f
-#define DATA_TYPE_RGB444 0x20
-#define DATA_TYPE_RGB555 0x21
-#define DATA_TYPE_RGB565 0x22
-#define DATA_TYPE_RGB666 0x23
-#define DATA_TYPE_RGB888 0x24
-#define DATA_TYPE_RAW_24BIT 0x27
-#define DATA_TYPE_RAW_6BIT 0x28
-#define DATA_TYPE_RAW_7BIT 0x29
-#define DATA_TYPE_RAW_8BIT 0x2a
-#define DATA_TYPE_RAW_10BIT 0x2b
-#define DATA_TYPE_RAW_12BIT 0x2c
-#define DATA_TYPE_RAW_14BIT 0x2d
-#define DATA_TYPE_RAW_16BIT 0x2e
-#define DATA_TYPE_RAW_20BIT 0x2f
-
#define CSID_RESET_TIMEOUT_MS 500
enum csid_testgen_mode {
@@ -152,6 +129,14 @@ struct csid_hw_ops {
* @csid: CSID device
*/
void (*subdev_init)(struct csid_device *csid);
+
+ /*
+ * reg_update - receive message from other sub device
+ * @csid: CSID device
+ * @port_id: Port id
+ * @is_clear: Indicate if it is clearing reg update or setting reg update
+ */
+ void (*reg_update)(struct csid_device *csid, int port_id, bool is_clear);
};
struct csid_subdev_resources {
@@ -169,6 +154,7 @@ struct csid_device {
void __iomem *base;
u32 irq;
char irq_name[30];
+ u32 reg_update;
struct camss_clock *clock;
int nclocks;
struct regulator_bulk_data *supplies;
@@ -227,7 +213,9 @@ extern const struct csid_formats csid_formats_gen2;
extern const struct csid_hw_ops csid_ops_4_1;
extern const struct csid_hw_ops csid_ops_4_7;
+extern const struct csid_hw_ops csid_ops_680;
extern const struct csid_hw_ops csid_ops_gen2;
+extern const struct csid_hw_ops csid_ops_780;
/*
* csid_is_lite - Check if CSID is CSID lite.
@@ -237,4 +225,25 @@ extern const struct csid_hw_ops csid_ops_gen2;
*/
bool csid_is_lite(struct csid_device *csid);
+/*
+ * csid_hw_version - CSID hardware version query
+ * @csid: CSID device
+ *
+ * Return HW version or error
+ */
+u32 csid_hw_version(struct csid_device *csid);
+
+/*
+ * csid_src_pad_code - Pick an output/src format based on the input/sink format
+ * @csid: CSID device
+ * @sink_code: The sink format of the input
+ * @match_format_idx: Request preferred index, as defined by subdevice csid
+ * format. Set @match_code to 0 if used.
+ * @match_code: Request preferred code, set @match_format_idx to 0 if used
+ *
+ * Return 0 on failure or src format code otherwise
+ */
+u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code);
+
#endif /* QC_MSM_CAMSS_CSID_H */
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
index cd4a8c369234..9d67e7fa6366 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
@@ -180,6 +180,11 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
return IRQ_HANDLED;
}
+static int csiphy_init(struct csiphy_device *csiphy)
+{
+ return 0;
+}
+
const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = {
.get_lane_mask = csiphy_get_lane_mask,
.hw_version_read = csiphy_hw_version_read,
@@ -187,4 +192,5 @@ const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = {
.lanes_enable = csiphy_lanes_enable,
.lanes_disable = csiphy_lanes_disable,
.isr = csiphy_isr,
+ .init = csiphy_init,
};
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index f341f7b7fd8a..f732a76de93e 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -42,328 +42,522 @@
#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15(n) (0x03c + 0x100 * (n))
#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8
-#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(n) (0x800 + 0x4 * (n))
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(offset, n) ((offset) + 0x4 * (n))
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7)
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0)
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
-#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n))
-
-#define CSIPHY_DEFAULT_PARAMS 0
-#define CSIPHY_LANE_ENABLE 1
-#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
-#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3
-#define CSIPHY_DNP_PARAMS 4
-#define CSIPHY_2PH_REGS 5
-#define CSIPHY_3PH_REGS 6
-
-struct csiphy_reg_t {
+#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n))
+
+#define CSIPHY_DEFAULT_PARAMS 0
+#define CSIPHY_LANE_ENABLE 1
+#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
+#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3
+#define CSIPHY_DNP_PARAMS 4
+#define CSIPHY_2PH_REGS 5
+#define CSIPHY_3PH_REGS 6
+#define CSIPHY_SKEW_CAL 7
+
+struct csiphy_lane_regs {
s32 reg_addr;
s32 reg_data;
- s32 delay;
+ u32 delay_us;
u32 csiphy_param_type;
};
/* GEN2 1.0 2PH */
static const struct
-csiphy_reg_t lane_regs_sdm845[5][14] = {
- {
- {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
+csiphy_lane_regs lane_regs_sdm845[] = {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
};
/* GEN2 1.1 2PH */
static const struct
-csiphy_reg_t lane_regs_sc8280xp[5][14] = {
- {
- {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0000, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0008, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x000C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0708, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0200, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0208, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0400, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0408, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0600, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0608, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
+csiphy_lane_regs lane_regs_sc8280xp[] = {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x000C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
};
/* GEN2 1.2.1 2PH */
static const struct
-csiphy_reg_t lane_regs_sm8250[5][20] = {
- {
- {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0900, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0908, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0034, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0010, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x001C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0000, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0024, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C80, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C88, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0734, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0710, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x071C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0708, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x070c, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0724, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0234, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0210, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x021C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0208, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0200, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x020c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0224, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0434, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0410, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x041C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0400, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x040c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0424, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0634, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0610, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x061C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0608, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0600, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x060c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0624, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
+csiphy_lane_regs lane_regs_sm8250[] = {
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0900, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0908, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0010, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0000, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0024, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C80, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C88, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x070c, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0724, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0210, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0200, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x020c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0224, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0410, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0400, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x040c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0424, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0610, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0600, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x060c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0624, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+/* GEN2 2.1.2 2PH DPHY mode */
+static const struct
+csiphy_lane_regs lane_regs_sm8550[] = {
+ {0x0E90, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x00A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0090, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x04A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0490, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x08A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0890, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0CA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C90, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0E30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E0C, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E38, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0E10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0800, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0838, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x082C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0834, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x081C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x083C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0804, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0808, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C00, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C38, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0C10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x005C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x045C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x085C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0860, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0864, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C5C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C60, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C64, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */
+static const struct
+csiphy_lane_regs lane_regs_x1e80100[] = {
+ /* Power up lanes 2ph mode */
+ {0x1014, 0xD5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x101C, 0x7A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x1018, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0094, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x00A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0090, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x005C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0060, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0064, 0x7F, 0x00, CSIPHY_SKEW_CAL},
+
+ {0x0E94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0EA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0E30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E0C, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E38, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0E10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x04A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0490, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x045C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0460, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0464, 0x7F, 0x00, CSIPHY_SKEW_CAL},
+
+ {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x08A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0890, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0800, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0838, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x082C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0834, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x081C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x083C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0804, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0808, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x085C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0860, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0864, 0x7F, 0x00, CSIPHY_SKEW_CAL},
+
+ {0x0C94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0CA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0C30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C00, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C38, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0C10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x0C5C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0C60, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0C64, 0x7F, 0x00, CSIPHY_SKEW_CAL},
};
static void csiphy_hw_version_read(struct csiphy_device *csiphy,
struct device *dev)
{
+ struct csiphy_device_regs *regs = csiphy->regs;
u32 hw_version;
- writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID,
- csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+ writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6));
hw_version = readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(12));
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 12));
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(13)) << 8;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 13)) << 8;
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(14)) << 16;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 14)) << 16;
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(15)) << 24;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 15)) << 24;
dev_dbg(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version);
}
@@ -374,31 +568,39 @@ static void csiphy_hw_version_read(struct csiphy_device *csiphy,
*/
static void csiphy_reset(struct csiphy_device *csiphy)
{
- writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+ struct csiphy_device_regs *regs = csiphy->regs;
+
+ writel_relaxed(0x1, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0));
usleep_range(5000, 8000);
- writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+ writel_relaxed(0x0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0));
}
static irqreturn_t csiphy_isr(int irq, void *dev)
{
struct csiphy_device *csiphy = dev;
+ struct csiphy_device_regs *regs = csiphy->regs;
int i;
for (i = 0; i < 11; i++) {
int c = i + 22;
u8 val = readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(i));
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, i));
writel_relaxed(val, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(c));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, c));
}
- writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10));
- writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10));
+ writel_relaxed(0x1, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10));
+ writel_relaxed(0x0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10));
- for (i = 22; i < 33; i++)
+ for (i = 22; i < 33; i++) {
writel_relaxed(0x0, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, i));
+ }
return IRQ_HANDLED;
}
@@ -500,46 +702,27 @@ static void csiphy_gen1_config_lanes(struct csiphy_device *csiphy,
static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy,
u8 settle_cnt)
{
- const struct csiphy_reg_t *r;
- int i, l, array_size;
+ const struct csiphy_lane_regs *r = csiphy->regs->lane_regs;
+ int i, array_size = csiphy->regs->lane_array_size;
u32 val;
- switch (csiphy->camss->res->version) {
- case CAMSS_7280:
- r = &lane_regs_sm8250[0][0];
- array_size = ARRAY_SIZE(lane_regs_sm8250[0]);
- break;
- case CAMSS_8250:
- r = &lane_regs_sm8250[0][0];
- array_size = ARRAY_SIZE(lane_regs_sm8250[0]);
- break;
- case CAMSS_8280XP:
- r = &lane_regs_sc8280xp[0][0];
- array_size = ARRAY_SIZE(lane_regs_sc8280xp[0]);
- break;
- case CAMSS_845:
- r = &lane_regs_sdm845[0][0];
- array_size = ARRAY_SIZE(lane_regs_sdm845[0]);
- break;
- default:
- WARN(1, "unknown cspi version\n");
- return;
- }
-
- for (l = 0; l < 5; l++) {
- for (i = 0; i < array_size; i++, r++) {
- switch (r->csiphy_param_type) {
- case CSIPHY_SETTLE_CNT_LOWER_BYTE:
- val = settle_cnt & 0xff;
- break;
- case CSIPHY_DNP_PARAMS:
- continue;
- default:
- val = r->reg_data;
- break;
- }
- writel_relaxed(val, csiphy->base + r->reg_addr);
+ for (i = 0; i < array_size; i++, r++) {
+ switch (r->csiphy_param_type) {
+ case CSIPHY_SETTLE_CNT_LOWER_BYTE:
+ val = settle_cnt & 0xff;
+ break;
+ case CSIPHY_SKEW_CAL:
+ /* TODO: support application of skew from dt flag */
+ continue;
+ case CSIPHY_DNP_PARAMS:
+ continue;
+ default:
+ val = r->reg_data;
+ break;
}
+ writel_relaxed(val, csiphy->base + r->reg_addr);
+ if (r->delay_us)
+ udelay(r->delay_us);
}
}
@@ -565,6 +748,8 @@ static bool csiphy_is_gen2(u32 version)
case CAMSS_8250:
case CAMSS_8280XP:
case CAMSS_845:
+ case CAMSS_8550:
+ case CAMSS_X1E80100:
ret = true;
break;
}
@@ -577,6 +762,7 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
s64 link_freq, u8 lane_mask)
{
struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
+ struct csiphy_device_regs *regs = csiphy->regs;
u8 settle_cnt;
u8 val;
int i;
@@ -587,16 +773,20 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
for (i = 0; i < c->num_data; i++)
val |= BIT(c->data[i].pos * 2);
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5));
val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6));
val = 0x02;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(7));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 7));
val = 0x00;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0));
if (csiphy_is_gen2(csiphy->camss->res->version))
csiphy_gen2_config_lanes(csiphy, settle_cnt);
@@ -604,18 +794,66 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt);
/* IRQ_MASK registers - disable all interrupts */
- for (i = 11; i < 22; i++)
- writel_relaxed(0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i));
+ for (i = 11; i < 22; i++) {
+ writel_relaxed(0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, i));
+ }
}
static void csiphy_lanes_disable(struct csiphy_device *csiphy,
struct csiphy_config *cfg)
{
+ struct csiphy_device_regs *regs = csiphy->regs;
+
writel_relaxed(0, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5));
writel_relaxed(0, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6));
+}
+
+static int csiphy_init(struct csiphy_device *csiphy)
+{
+ struct device *dev = csiphy->camss->dev;
+ struct csiphy_device_regs *regs;
+
+ regs = devm_kmalloc(dev, sizeof(*regs), GFP_KERNEL);
+ if (!regs)
+ return -ENOMEM;
+
+ csiphy->regs = regs;
+ regs->offset = 0x800;
+
+ switch (csiphy->camss->res->version) {
+ case CAMSS_845:
+ regs->lane_regs = &lane_regs_sdm845[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sdm845);
+ break;
+ case CAMSS_7280:
+ case CAMSS_8250:
+ regs->lane_regs = &lane_regs_sm8250[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8250);
+ break;
+ case CAMSS_8280XP:
+ regs->lane_regs = &lane_regs_sc8280xp[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sc8280xp);
+ break;
+ case CAMSS_X1E80100:
+ regs->lane_regs = &lane_regs_x1e80100[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_x1e80100);
+ regs->offset = 0x1000;
+ break;
+ case CAMSS_8550:
+ regs->lane_regs = &lane_regs_sm8550[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8550);
+ regs->offset = 0x1000;
+ break;
+ default:
+ WARN(1, "unknown csiphy version\n");
+ return -ENODEV;
+ }
+
+ return 0;
}
const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = {
@@ -625,4 +863,5 @@ const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = {
.lanes_enable = csiphy_lanes_enable,
.lanes_disable = csiphy_lanes_disable,
.isr = csiphy_isr,
+ .init = csiphy_init,
};
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 3791c2d8a6cf..c622efcc92ff 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -586,7 +586,7 @@ int msm_csiphy_subdev_init(struct camss *camss,
{
struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
- int i, j, k;
+ int i, j;
int ret;
csiphy->camss = camss;
@@ -594,6 +594,10 @@ int msm_csiphy_subdev_init(struct camss *camss,
csiphy->cfg.combo_mode = 0;
csiphy->res = &res->csiphy;
+ ret = csiphy->res->hw_ops->init(csiphy);
+ if (ret)
+ return ret;
+
/* Memory */
csiphy->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
@@ -676,23 +680,21 @@ int msm_csiphy_subdev_init(struct camss *camss,
for (j = 0; j < clock->nfreqs; j++)
clock->freq[j] = res->clock_rate[i][j];
- for (k = 0; k < camss->res->csiphy_num; k++) {
- csiphy->rate_set[i] = csiphy_match_clock_name(clock->name,
- "csiphy%d_timer", k);
- if (csiphy->rate_set[i])
- break;
-
- if (camss->res->version == CAMSS_660) {
- csiphy->rate_set[i] = csiphy_match_clock_name(clock->name,
- "csi%d_phy", k);
- if (csiphy->rate_set[i])
- break;
- }
+ csiphy->rate_set[i] = csiphy_match_clock_name(clock->name,
+ "csiphy%d_timer",
+ csiphy->id);
+ if (csiphy->rate_set[i])
+ continue;
- csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, "csiphy%d", k);
+ if (camss->res->version == CAMSS_660) {
+ csiphy->rate_set[i] = csiphy_match_clock_name(clock->name,
+ "csi%d_phy",
+ csiphy->id);
if (csiphy->rate_set[i])
- break;
+ continue;
}
+
+ csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, "csiphy%d", csiphy->id);
}
/* CSIPHY supplies */
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index 90cc3f976643..ab91273303b9 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -77,13 +77,21 @@ struct csiphy_hw_ops {
void (*lanes_disable)(struct csiphy_device *csiphy,
struct csiphy_config *cfg);
irqreturn_t (*isr)(int irq, void *dev);
+ int (*init)(struct csiphy_device *csiphy);
};
struct csiphy_subdev_resources {
+ u8 id;
const struct csiphy_hw_ops *hw_ops;
const struct csiphy_formats *formats;
};
+struct csiphy_device_regs {
+ const struct csiphy_lane_regs *lane_regs;
+ int lane_array_size;
+ u32 offset;
+};
+
struct csiphy_device {
struct camss *camss;
u8 id;
@@ -102,6 +110,7 @@ struct csiphy_device {
struct csiphy_config cfg;
struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM];
const struct csiphy_subdev_resources *res;
+ struct csiphy_device_regs *regs;
};
struct camss_subdev_resources;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-17x.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index 380c99321030..e5ee7e717b3b 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-17x.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
@@ -14,8 +14,6 @@
#include "camss.h"
#include "camss-vfe.h"
-#define VFE_HW_VERSION (0x000)
-
#define VFE_GLOBAL_RESET_CMD (0x018)
#define GLOBAL_RESET_CMD_CORE BIT(0)
#define GLOBAL_RESET_CMD_CAMIF BIT(1)
@@ -176,20 +174,6 @@
#define VFE_BUS_WM_FRAME_INC(n) (0x2258 + (n) * 0x100)
#define VFE_BUS_WM_BURST_LIMIT(n) (0x225c + (n) * 0x100)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
-
- u32 gen = (hw_version >> 28) & 0xF;
- u32 rev = (hw_version >> 16) & 0xFFF;
- u32 step = hw_version & 0xFFFF;
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n",
- gen, rev, step);
-
- return hw_version;
-}
-
static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
{
u32 bits = readl_relaxed(vfe->base + reg);
@@ -438,62 +422,6 @@ error:
return -EINVAL;
}
-static int vfe_enable_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output = &line->output;
- const struct vfe_hw_ops *ops = vfe->res->hw_ops;
- struct media_entity *sensor;
- unsigned long flags;
- unsigned int frame_skip = 0;
- unsigned int i;
-
- sensor = camss_find_sensor(&line->subdev.entity);
- if (sensor) {
- struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
-
- v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
- /* Max frame skip is 29 frames */
- if (frame_skip > VFE_FRAME_DROP_VAL - 1)
- frame_skip = VFE_FRAME_DROP_VAL - 1;
- }
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- ops->reg_update_clear(vfe, line->id);
-
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
- output->state);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- return -EINVAL;
- }
-
- WARN_ON(output->gen2.active_num);
-
- output->state = VFE_OUTPUT_ON;
-
- output->sequence = 0;
- output->wait_reg_update = 0;
- reinit_completion(&output->reg_update);
-
- vfe_wm_start(vfe, output->wm_idx[0], line);
-
- for (i = 0; i < 2; i++) {
- output->buf[i] = vfe_buf_get_pending(output);
- if (!output->buf[i])
- break;
- output->gen2.active_num++;
- vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line);
- }
-
- ops->reg_update(vfe, line->id);
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
/*
* vfe_enable - Enable streaming on VFE line
* @line: VFE line
@@ -518,7 +446,7 @@ static int vfe_enable(struct vfe_line *line)
if (ret < 0)
goto error_get_output;
- ret = vfe_enable_output(line);
+ ret = vfe_enable_output_v2(line);
if (ret < 0)
goto error_enable_output;
@@ -627,40 +555,6 @@ out_unlock:
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
-/*
- * vfe_queue_buffer - Add empty buffer
- * @vid: Video device structure
- * @buf: Buffer to be enqueued
- *
- * Add an empty buffer - depending on the current number of buffers it will be
- * put in pending buffer queue or directly given to the hardware to be filled.
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_queue_buffer(struct camss_video *vid,
- struct camss_buffer *buf)
-{
- struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
-
- output = &line->output;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) {
- output->buf[output->gen2.active_num++] = buf;
- vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line);
- } else {
- vfe_buf_add_pending(output, buf);
- }
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
static const struct vfe_isr_ops vfe_isr_ops_170 = {
.reset_ack = vfe_isr_reset_ack,
.halt_ack = vfe_isr_halt_ack,
@@ -671,7 +565,7 @@ static const struct vfe_isr_ops vfe_isr_ops_170 = {
};
static const struct camss_video_ops vfe_video_ops_170 = {
- .queue_buffer = vfe_queue_buffer,
+ .queue_buffer = vfe_queue_buffer_v2,
.flush_buffers = vfe_flush_buffers,
};
@@ -695,5 +589,7 @@ const struct vfe_hw_ops vfe_ops_170 = {
.vfe_enable = vfe_enable,
.vfe_halt = vfe_halt,
.violation_read = vfe_violation_read,
+ .vfe_wm_start = vfe_wm_start,
.vfe_wm_stop = vfe_wm_stop,
+ .vfe_wm_update = vfe_wm_update,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
index 9a9007c3ff33..901677293d97 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
@@ -210,15 +210,6 @@
#define MSM_VFE_VFE0_UB_SIZE 1023
#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static u16 vfe_get_ub_size(u8 vfe_id)
{
if (vfe_id == 0)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
index ce0719106bd3..76729607db02 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
@@ -18,8 +18,6 @@
#include "camss-vfe-gen1.h"
-#define VFE_0_HW_VERSION 0x000
-
#define VFE_0_GLOBAL_RESET_CMD 0x018
#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
@@ -254,15 +252,6 @@
#define MSM_VFE_VFE1_UB_SIZE 1535
#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static u16 vfe_get_ub_size(u8 vfe_id)
{
if (vfe_id == 0)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
index 6b59c8107a3c..b2f7d855d8dd 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
@@ -17,8 +17,6 @@
#include "camss-vfe.h"
#include "camss-vfe-gen1.h"
-#define VFE_0_HW_VERSION 0x000
-
#define VFE_0_GLOBAL_RESET_CMD 0x018
#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
@@ -247,15 +245,6 @@
#define MSM_VFE_VFE1_UB_SIZE 1535
#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
{
u32 bits = readl_relaxed(vfe->base + reg);
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c
index dc2735476c82..4feea590a47b 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-480.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c
@@ -15,8 +15,6 @@
#include "camss.h"
#include "camss-vfe.h"
-#define VFE_HW_VERSION (0x00)
-
#define VFE_GLOBAL_RESET_CMD (vfe_is_lite(vfe) ? 0x0c : 0x1c)
#define GLOBAL_RESET_HW_AND_REG (vfe_is_lite(vfe) ? BIT(1) : BIT(0))
@@ -92,19 +90,6 @@ static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n)
#define MAX_VFE_OUTPUT_LINES 4
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
-
- u32 gen = (hw_version >> 28) & 0xF;
- u32 rev = (hw_version >> 16) & 0xFFF;
- u32 step = hw_version & 0xFFFF;
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step);
-
- return hw_version;
-}
-
static void vfe_global_reset(struct vfe_device *vfe)
{
writel_relaxed(IRQ_MASK_0_RESET_ACK, vfe->base + VFE_IRQ_MASK(0));
@@ -167,18 +152,16 @@ static inline void vfe_reg_update_clear(struct vfe_device *vfe,
vfe->reg_update &= ~REG_UPDATE_RDI(vfe, line_id);
}
-static void vfe_enable_irq_common(struct vfe_device *vfe)
-{
- /* enable reset ack IRQ and top BUS status IRQ */
- writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ,
- vfe->base + VFE_IRQ_MASK(0));
-}
-
-static void vfe_enable_lines_irq(struct vfe_device *vfe)
+static void vfe_enable_irq(struct vfe_device *vfe)
{
int i;
u32 bus_irq_mask = 0;
+ if (!vfe->stream_count)
+ /* enable reset ack IRQ and top BUS status IRQ */
+ writel(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ,
+ vfe->base + VFE_IRQ_MASK(0));
+
for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) {
/* Enable IRQ for newly added lines, but also keep already running lines's IRQ */
if (vfe->line[i].output.state == VFE_OUTPUT_RESERVED ||
@@ -188,11 +171,10 @@ static void vfe_enable_lines_irq(struct vfe_device *vfe)
}
}
- writel_relaxed(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0));
+ writel(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0));
}
static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id);
-static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm);
/*
* vfe_isr - VFE module interrupt handler
@@ -226,7 +208,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe_isr_reg_update(vfe, i);
if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i)))
- vfe_isr_wm_done(vfe, i);
+ vfe_buf_done(vfe, i);
}
}
@@ -245,132 +227,6 @@ static int vfe_halt(struct vfe_device *vfe)
return 0;
}
-static int vfe_get_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- output = &line->output;
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is running\n");
- goto error;
- }
-
- output->wm_num = 1;
-
- /* Correspondence between VFE line number and WM number.
- * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3
- * Note this 1:1 mapping will not work for PIX streams.
- */
- output->wm_idx[0] = line->id;
- vfe->wm_output_map[line->id] = line->id;
-
- output->drop_update_idx = 0;
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-
-error:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- output->state = VFE_OUTPUT_OFF;
-
- return -EINVAL;
-}
-
-static int vfe_enable_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output = &line->output;
- unsigned long flags;
- unsigned int i;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- vfe_reg_update_clear(vfe, line->id);
-
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
- output->state);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- return -EINVAL;
- }
-
- WARN_ON(output->gen2.active_num);
-
- output->state = VFE_OUTPUT_ON;
-
- output->sequence = 0;
- output->wait_reg_update = 0;
- reinit_completion(&output->reg_update);
-
- vfe_wm_start(vfe, output->wm_idx[0], line);
-
- for (i = 0; i < 2; i++) {
- output->buf[i] = vfe_buf_get_pending(output);
- if (!output->buf[i])
- break;
- output->gen2.active_num++;
- vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line);
- }
-
- vfe_reg_update(vfe, line->id);
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
-/*
- * vfe_enable - Enable streaming on VFE line
- * @line: VFE line
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_enable(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- int ret;
-
- mutex_lock(&vfe->stream_lock);
-
- if (!vfe->stream_count)
- vfe_enable_irq_common(vfe);
-
- vfe->stream_count++;
-
- vfe_enable_lines_irq(vfe);
-
- mutex_unlock(&vfe->stream_lock);
-
- ret = vfe_get_output(line);
- if (ret < 0)
- goto error_get_output;
-
- ret = vfe_enable_output(line);
- if (ret < 0)
- goto error_enable_output;
-
- vfe->was_streaming = 1;
-
- return 0;
-
-error_enable_output:
- vfe_put_output(line);
-
-error_get_output:
- mutex_lock(&vfe->stream_lock);
-
- vfe->stream_count--;
-
- mutex_unlock(&vfe->stream_lock);
-
- return ret;
-}
-
/*
* vfe_isr_reg_update - Process reg update interrupt
* @vfe: VFE Device
@@ -394,114 +250,48 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
-/*
- * vfe_isr_wm_done - Process write master done interrupt
- * @vfe: VFE Device
- * @wm: Write master id
- */
-static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
-{
- struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
- struct camss_buffer *ready_buf;
- struct vfe_output *output;
- unsigned long flags;
- u32 index;
- u64 ts = ktime_get_ns();
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
- dev_err_ratelimited(vfe->camss->dev,
- "Received wm done for unmapped index\n");
- goto out_unlock;
- }
- output = &vfe->line[vfe->wm_output_map[wm]].output;
-
- ready_buf = output->buf[0];
- if (!ready_buf) {
- dev_err_ratelimited(vfe->camss->dev,
- "Missing ready buf %d!\n", output->state);
- goto out_unlock;
- }
-
- ready_buf->vb.vb2_buf.timestamp = ts;
- ready_buf->vb.sequence = output->sequence++;
-
- index = 0;
- output->buf[0] = output->buf[1];
- if (output->buf[0])
- index = 1;
-
- output->buf[index] = vfe_buf_get_pending(output);
-
- if (output->buf[index])
- vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line);
- else
- output->gen2.active_num--;
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
-
- return;
+static const struct camss_video_ops vfe_video_ops_480 = {
+ .queue_buffer = vfe_queue_buffer_v2,
+ .flush_buffers = vfe_flush_buffers,
+};
-out_unlock:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->video_ops = vfe_video_ops_480;
}
-/*
- * vfe_queue_buffer - Add empty buffer
- * @vid: Video device structure
- * @buf: Buffer to be enqueued
- *
- * Add an empty buffer - depending on the current number of buffers it will be
- * put in pending buffer queue or directly given to the hardware to be filled.
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_queue_buffer(struct camss_video *vid,
- struct camss_buffer *buf)
+static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
{
- struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
-
- output = &line->output;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) {
- output->buf[output->gen2.active_num++] = buf;
- vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line);
- } else {
- vfe_buf_add_pending(output, buf);
- }
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
+ /* nop */
}
-static const struct camss_video_ops vfe_video_ops_480 = {
- .queue_buffer = vfe_queue_buffer,
- .flush_buffers = vfe_flush_buffers,
-};
+static void vfe_violation_read(struct vfe_device *vfe)
+{
+ /* nop */
+}
-static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+static void vfe_buf_done_480(struct vfe_device *vfe, int port_id)
{
- vfe->video_ops = vfe_video_ops_480;
+ /* nop */
}
const struct vfe_hw_ops vfe_ops_480 = {
+ .enable_irq = vfe_enable_irq,
.global_reset = vfe_global_reset,
.hw_version = vfe_hw_version,
.isr = vfe_isr,
+ .isr_read = vfe_isr_read,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
.pm_domain_off = vfe_pm_domain_off,
.pm_domain_on = vfe_pm_domain_on,
.subdev_init = vfe_subdev_init,
.vfe_disable = vfe_disable,
- .vfe_enable = vfe_enable,
+ .vfe_enable = vfe_enable_v2,
.vfe_halt = vfe_halt,
+ .violation_read = vfe_violation_read,
+ .vfe_wm_start = vfe_wm_start,
.vfe_wm_stop = vfe_wm_stop,
+ .vfe_buf_done = vfe_buf_done_480,
+ .vfe_wm_update = vfe_wm_update,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-680.c b/drivers/media/platform/qcom/camss/camss-vfe-680.c
new file mode 100644
index 000000000000..99036e7c1e76
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-680.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-680.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v680
+ *
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+
+#define VFE_TOP_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x1c : 0x44) + (n) * 4)
+#define VFE_TOP_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x24 : 0x34) + (n) * 4)
+#define VFE_TOP_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x2c : 0x3c) + (n) * 4)
+#define VFE_IRQ1_SOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(2) : BIT(8)) << ((n) * 2))
+#define VFE_IRQ1_EOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(3) : BIT(9)) << ((n) * 2))
+#define VFE_TOP_IRQ_CMD(vfe) (vfe_is_lite(vfe) ? 0x38 : 0x30)
+#define VFE_TOP_IRQ_CMD_GLOBAL_CLEAR BIT(0)
+#define VFE_TOP_DIAG_CONFIG (vfe_is_lite(vfe) ? 0x40 : 0x50)
+
+#define VFE_TOP_DEBUG_11(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xcc)
+#define VFE_TOP_DEBUG_12(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd0)
+#define VFE_TOP_DEBUG_13(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd4)
+
+#define VFE_BUS_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x218 : 0xc18) + (n) * 4)
+#define VFE_BUS_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x220 : 0xc20) + (n) * 4)
+#define VFE_BUS_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x228 : 0xc28) + (n) * 4)
+#define VFE_BUS_IRQ_GLOBAL_CLEAR(vfe) (vfe_is_lite(vfe) ? 0x230 : 0xc30)
+#define VFE_BUS_WR_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x264 : 0xc64)
+#define VFE_BUS_WR_OVERFLOW_STATUS(vfe) (vfe_is_lite(vfe) ? 0x268 : 0xc68)
+#define VFE_BUS_WR_IMAGE_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x270 : 0xc70)
+
+#define VFE_BUS_WRITE_CLIENT_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x400 : 0xe00) + (c) * 0x100)
+#define VFE_BUS_WRITE_CLIENT_CFG_EN BIT(0)
+#define VFE_BUS_IMAGE_ADDR(vfe, c) ((vfe_is_lite(vfe) ? 0x404 : 0xe04) + (c) * 0x100)
+#define VFE_BUS_FRAME_INCR(vfe, c) ((vfe_is_lite(vfe) ? 0x408 : 0xe08) + (c) * 0x100)
+#define VFE_BUS_IMAGE_CFG0(vfe, c) ((vfe_is_lite(vfe) ? 0x40c : 0xe0c) + (c) * 0x100)
+#define VFE_BUS_IMAGE_CFG0_DATA(h, s) (((h) << 16) | ((s) >> 4))
+#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF)
+
+#define VFE_BUS_IMAGE_CFG1(vfe, c) ((vfe_is_lite(vfe) ? 0x410 : 0xe10) + (c) * 0x100)
+#define VFE_BUS_IMAGE_CFG2(vfe, c) ((vfe_is_lite(vfe) ? 0x414 : 0xe14) + (c) * 0x100)
+#define VFE_BUS_PACKER_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x418 : 0xe18) + (c) * 0x100)
+#define VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x430 : 0xe30) + (c) * 0x100)
+#define VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x434 : 0xe34) + (c) * 0x100)
+#define VFE_BUS_FRAMEDROP_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x438 : 0xe38) + (c) * 0x100)
+#define VFE_BUS_FRAMEDROP_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x43c : 0xe3c) + (c) * 0x100)
+#define VFE_BUS_MMU_PREFETCH_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x460 : 0xe60) + (c) * 0x100)
+#define VFE_BUS_MMU_PREFETCH_CFG_EN BIT(0)
+#define VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, c) ((vfe_is_lite(vfe) ? 0x464 : 0xe64) + (c) * 0x100)
+#define VFE_BUS_ADDR_STATUS0(vfe, c) ((vfe_is_lite(vfe) ? 0x470 : 0xe70) + (c) * 0x100)
+
+/*
+ * TODO: differentiate the port id based on requested type of RDI, BHIST etc
+ *
+ * IFE write master IDs
+ *
+ * VIDEO_FULL_Y 0
+ * VIDEO_FULL_C 1
+ * VIDEO_DS_4:1 2
+ * VIDEO_DS_16:1 3
+ * DISPLAY_FULL_Y 4
+ * DISPLAY_FULL_C 5
+ * DISPLAY_DS_4:1 6
+ * DISPLAY_DS_16:1 7
+ * FD_Y 8
+ * FD_C 9
+ * PIXEL_RAW 10
+ * STATS_BE0 11
+ * STATS_BHIST0 12
+ * STATS_TINTLESS_BG 13
+ * STATS_AWB_BG 14
+ * STATS_AWB_BFW 15
+ * STATS_BAF 16
+ * STATS_BHIST 17
+ * STATS_RS 18
+ * STATS_IHIST 19
+ * SPARSE_PD 20
+ * PDAF_V2.0_PD_DATA 21
+ * PDAF_V2.0_SAD 22
+ * LCR 23
+ * RDI0 24
+ * RDI1 25
+ * RDI2 26
+ * LTM_STATS 27
+ *
+ * IFE Lite write master IDs
+ *
+ * RDI0 0
+ * RDI1 1
+ * RDI2 2
+ * RDI3 3
+ * GAMMA 4
+ * BE 5
+ */
+
+/* TODO: assign an ENUM in resources and use the provided master
+ * id directly for RDI, STATS, AWB_BG, BHIST.
+ * This macro only works because RDI is all we support right now.
+ */
+#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0 : 24) + (n))
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ /* VFE680 has no global reset, simply report a completion */
+ complete(&vfe->reset_complete);
+}
+
+/*
+ * vfe_isr - VFE module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ return IRQ_HANDLED;
+}
+
+/*
+ * vfe_halt - Trigger halt on VFE module and wait to complete
+ * @vfe: VFE device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_halt(struct vfe_device *vfe)
+{
+ /* rely on vfe_disable_output() to stop the VFE */
+ return 0;
+}
+
+static void vfe_disable_irq(struct vfe_device *vfe)
+{
+ writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 0));
+ writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 1));
+ writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 0));
+ writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 1));
+}
+
+static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr,
+ struct vfe_line *line)
+{
+ u8 wm = RDI_WM(rdi);
+
+ writel(addr, vfe->base + VFE_BUS_IMAGE_ADDR(vfe, wm));
+}
+
+static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line)
+{
+ struct v4l2_pix_format_mplane *pix =
+ &line->video_out.active_fmt.fmt.pix_mp;
+ u32 stride = pix->plane_fmt[0].bytesperline;
+ u32 cfg;
+ u8 wm;
+
+ cfg = VFE_BUS_IMAGE_CFG0_DATA(pix->height, stride);
+ wm = RDI_WM(rdi);
+
+ writel(cfg, vfe->base + VFE_BUS_IMAGE_CFG0(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_IMAGE_CFG1(vfe, wm));
+ writel(stride, vfe->base + VFE_BUS_IMAGE_CFG2(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_PACKER_CFG(vfe, wm));
+
+ /* Set total frame increment value */
+ writel(pix->plane_fmt[0].bytesperline * pix->height,
+ vfe->base + VFE_BUS_FRAME_INCR(vfe, wm));
+
+ /* MMU */
+ writel(VFE_BUS_MMU_PREFETCH_CFG_EN, vfe->base + VFE_BUS_MMU_PREFETCH_CFG(vfe, wm));
+ writel(~0u, vfe->base + VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, wm));
+
+ /* no dropped frames, one irq per frame */
+ writel(1, vfe->base + VFE_BUS_FRAMEDROP_PATTERN(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_FRAMEDROP_PERIOD(vfe, wm));
+ writel(1, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, wm));
+
+ /* We don't process IRQs for VFE in RDI mode at the moment */
+ vfe_disable_irq(vfe);
+
+ /* Enable WM */
+ writel(VFE_BUS_WRITE_CLIENT_CFG_EN,
+ vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm));
+
+ dev_dbg(vfe->camss->dev, "RDI%d WM:%d width %d height %d stride %d\n",
+ rdi, wm, pix->width, pix->height, stride);
+}
+
+static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi)
+{
+ u8 wm = RDI_WM(rdi);
+
+ writel(0, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm));
+}
+
+static const struct camss_video_ops vfe_video_ops_680 = {
+ .queue_buffer = vfe_queue_buffer_v2,
+ .flush_buffers = vfe_flush_buffers,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->video_ops = vfe_video_ops_680;
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, false);
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, true);
+}
+
+const struct vfe_hw_ops vfe_ops_680 = {
+ .global_reset = vfe_global_reset,
+ .hw_version = vfe_hw_version,
+ .isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_disable,
+ .vfe_enable = vfe_enable_v2,
+ .vfe_halt = vfe_halt,
+ .vfe_wm_start = vfe_wm_start,
+ .vfe_wm_stop = vfe_wm_stop,
+ .vfe_buf_done = vfe_buf_done,
+ .vfe_wm_update = vfe_wm_update,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-780.c b/drivers/media/platform/qcom/camss/camss-vfe-780.c
new file mode 100644
index 000000000000..b9812d70f91b
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-780.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v780 (SM8550)
+ *
+ * Copyright (c) 2024 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+
+#define BUS_REG_BASE (vfe_is_lite(vfe) ? 0x200 : 0xC00)
+
+#define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08)
+#define WM_CGC_OVERRIDE_ALL (0x7FFFFFF)
+
+#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0xDC)
+
+#define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x200 + (n) * 0x100)
+#define WM_CFG_EN BIT(0)
+#define WM_VIR_FRM_EN BIT(1)
+#define WM_CFG_MODE BIT(16)
+#define VFE_BUS_WM_IMAGE_ADDR(n) (BUS_REG_BASE + 0x204 + (n) * 0x100)
+#define VFE_BUS_WM_FRAME_INCR(n) (BUS_REG_BASE + 0x208 + (n) * 0x100)
+#define VFE_BUS_WM_IMAGE_CFG_0(n) (BUS_REG_BASE + 0x20c + (n) * 0x100)
+#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF)
+#define VFE_BUS_WM_IMAGE_CFG_2(n) (BUS_REG_BASE + 0x214 + (n) * 0x100)
+#define WM_IMAGE_CFG_2_DEFAULT_STRIDE (0xFFFF)
+#define VFE_BUS_WM_PACKER_CFG(n) (BUS_REG_BASE + 0x218 + (n) * 0x100)
+
+#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (BUS_REG_BASE + 0x230 + (n) * 0x100)
+#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (BUS_REG_BASE + 0x234 + (n) * 0x100)
+#define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (BUS_REG_BASE + 0x238 + (n) * 0x100)
+#define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (BUS_REG_BASE + 0x23c + (n) * 0x100)
+
+#define VFE_BUS_WM_MMU_PREFETCH_CFG(n) (BUS_REG_BASE + 0x260 + (n) * 0x100)
+#define VFE_BUS_WM_MMU_PREFETCH_MAX_OFFSET(n) (BUS_REG_BASE + 0x264 + (n) * 0x100)
+
+/*
+ * Bus client mapping:
+ *
+ * Full VFE:
+ * 23 = RDI0, 24 = RDI1, 25 = RDI2
+ *
+ * VFE LITE:
+ * 0 = RDI0, 1 = RDI1, 2 = RDI3, 4 = RDI4
+ */
+#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0x0 : 0x17) + (n))
+
+static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
+{
+ struct v4l2_pix_format_mplane *pix =
+ &line->video_out.active_fmt.fmt.pix_mp;
+
+ wm = RDI_WM(wm);
+
+ /* no clock gating at bus input */
+ writel(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
+
+ writel(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
+
+ writel(ALIGN(pix->plane_fmt[0].bytesperline, 16) * pix->height >> 8,
+ vfe->base + VFE_BUS_WM_FRAME_INCR(wm));
+ writel((WM_IMAGE_CFG_0_DEFAULT_WIDTH & 0xFFFF),
+ vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm));
+ writel(WM_IMAGE_CFG_2_DEFAULT_STRIDE,
+ vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm));
+ writel(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm));
+
+ /* no dropped frames, one irq per frame */
+ writel(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm));
+ writel(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm));
+ writel(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm));
+ writel(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm));
+
+ writel(1, vfe->base + VFE_BUS_WM_MMU_PREFETCH_CFG(wm));
+ writel(0xFFFFFFFF, vfe->base + VFE_BUS_WM_MMU_PREFETCH_MAX_OFFSET(wm));
+
+ writel(WM_CFG_EN | WM_CFG_MODE, vfe->base + VFE_BUS_WM_CFG(wm));
+}
+
+static void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
+{
+ wm = RDI_WM(wm);
+ writel(0, vfe->base + VFE_BUS_WM_CFG(wm));
+}
+
+static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
+ struct vfe_line *line)
+{
+ wm = RDI_WM(wm);
+ writel((addr >> 8) & 0xFFFFFFFF, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
+
+ dev_dbg(vfe->camss->dev, "wm:%d, image buf addr:0x%x\n",
+ wm, addr);
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, false);
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, true);
+}
+
+static const struct camss_video_ops vfe_video_ops_780 = {
+ .queue_buffer = vfe_queue_buffer_v2,
+ .flush_buffers = vfe_flush_buffers,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->video_ops = vfe_video_ops_780;
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ vfe_isr_reset_ack(vfe);
+}
+
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ /* nop */
+ return IRQ_HANDLED;
+}
+
+static int vfe_halt(struct vfe_device *vfe)
+{
+ /* rely on vfe_disable_output() to stop the VFE */
+ return 0;
+}
+
+const struct vfe_hw_ops vfe_ops_780 = {
+ .global_reset = vfe_global_reset,
+ .hw_version = vfe_hw_version,
+ .isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_disable,
+ .vfe_enable = vfe_enable_v2,
+ .vfe_halt = vfe_halt,
+ .vfe_wm_start = vfe_wm_start,
+ .vfe_wm_stop = vfe_wm_stop,
+ .vfe_buf_done = vfe_buf_done,
+ .vfe_wm_update = vfe_wm_update,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
index eb33c03df27e..d84a375e3318 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
@@ -170,7 +170,7 @@ static int vfe_enable_output(struct vfe_line *line)
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
const struct vfe_hw_ops *ops = vfe->res->hw_ops;
- struct media_entity *sensor;
+ struct media_pad *sensor_pad;
unsigned long flags;
unsigned int frame_skip = 0;
unsigned int i;
@@ -180,9 +180,10 @@ static int vfe_enable_output(struct vfe_line *line)
if (!ub_size)
return -EINVAL;
- sensor = camss_find_sensor(&line->subdev.entity);
- if (sensor) {
- struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
+ sensor_pad = camss_find_sensor_pad(&line->subdev.entity);
+ if (sensor_pad) {
+ struct v4l2_subdev *subdev =
+ media_entity_to_v4l2_subdev(sensor_pad->entity);
v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
/* Max frame skip is 29 frames */
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 95f6a1ac7eaf..4bca6c3abaff 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -32,6 +32,11 @@
#define SCALER_RATIO_MAX 16
+#define VFE_HW_VERSION 0x0
+#define HW_VERSION_STEPPING 0
+#define HW_VERSION_REVISION 16
+#define HW_VERSION_GENERATION 28
+
static const struct camss_format_info formats_rdi_8x16[] = {
{ MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
@@ -340,6 +345,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
case CAMSS_8250:
case CAMSS_8280XP:
case CAMSS_845:
+ case CAMSS_8550:
+ case CAMSS_X1E80100:
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
{
@@ -400,10 +407,278 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
return sink_code;
}
break;
+ default:
+ WARN(1, "Unsupported HW version: %x\n",
+ vfe->camss->res->version);
+ break;
}
return 0;
}
+/*
+ * vfe_hw_version - Process write master done interrupt
+ * @vfe: VFE Device
+ *
+ * Return vfe hw version
+ */
+u32 vfe_hw_version(struct vfe_device *vfe)
+{
+ u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
+
+ u32 gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
+ u32 rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
+ u32 step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
+
+ dev_dbg(vfe->camss->dev, "VFE:%d HW Version = %u.%u.%u\n",
+ vfe->id, gen, rev, step);
+
+ return hw_version;
+}
+
+/*
+ * vfe_buf_done - Process write master done interrupt
+ * @vfe: VFE Device
+ * @wm: Write master id
+ */
+void vfe_buf_done(struct vfe_device *vfe, int wm)
+{
+ struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ struct camss_buffer *ready_buf;
+ struct vfe_output *output;
+ unsigned long flags;
+ u32 index;
+ u64 ts = ktime_get_ns();
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Received wm done for unmapped index\n");
+ goto out_unlock;
+ }
+ output = &vfe->line[vfe->wm_output_map[wm]].output;
+
+ ready_buf = output->buf[0];
+ if (!ready_buf) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Missing ready buf %d!\n", output->state);
+ goto out_unlock;
+ }
+
+ ready_buf->vb.vb2_buf.timestamp = ts;
+ ready_buf->vb.sequence = output->sequence++;
+
+ index = 0;
+ output->buf[0] = output->buf[1];
+ if (output->buf[0])
+ index = 1;
+
+ output->buf[index] = vfe_buf_get_pending(output);
+
+ if (output->buf[index]) {
+ ops->vfe_wm_update(vfe, output->wm_idx[0],
+ output->buf[index]->addr[0],
+ line);
+ ops->reg_update(vfe, line->id);
+ } else {
+ output->gen2.active_num--;
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+ return;
+
+out_unlock:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+int vfe_enable_output_v2(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output = &line->output;
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ struct media_pad *sensor_pad;
+ unsigned long flags;
+ unsigned int frame_skip = 0;
+ unsigned int i;
+
+ sensor_pad = camss_find_sensor_pad(&line->subdev.entity);
+ if (sensor_pad) {
+ struct v4l2_subdev *subdev =
+ media_entity_to_v4l2_subdev(sensor_pad->entity);
+
+ v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
+ /* Max frame skip is 29 frames */
+ if (frame_skip > VFE_FRAME_DROP_VAL - 1)
+ frame_skip = VFE_FRAME_DROP_VAL - 1;
+ }
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ ops->reg_update_clear(vfe, line->id);
+
+ if (output->state > VFE_OUTPUT_RESERVED) {
+ dev_err(vfe->camss->dev,
+ "Output is not in reserved state %d\n",
+ output->state);
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ return -EINVAL;
+ }
+
+ WARN_ON(output->gen2.active_num);
+
+ output->state = VFE_OUTPUT_ON;
+
+ output->sequence = 0;
+ output->wait_reg_update = 0;
+ reinit_completion(&output->reg_update);
+
+ ops->vfe_wm_start(vfe, output->wm_idx[0], line);
+
+ for (i = 0; i < 2; i++) {
+ output->buf[i] = vfe_buf_get_pending(output);
+ if (!output->buf[i])
+ break;
+ output->gen2.active_num++;
+ ops->vfe_wm_update(vfe, output->wm_idx[0],
+ output->buf[i]->addr[0], line);
+ ops->reg_update(vfe, line->id);
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+/*
+ * vfe_queue_buffer_v2 - Add empty buffer
+ * @vid: Video device structure
+ * @buf: Buffer to be enqueued
+ *
+ * Add an empty buffer - depending on the current number of buffers it will be
+ * put in pending buffer queue or directly given to the hardware to be filled.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_queue_buffer_v2(struct camss_video *vid,
+ struct camss_buffer *buf)
+{
+ struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
+ struct vfe_device *vfe = to_vfe(line);
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ struct vfe_output *output;
+ unsigned long flags;
+
+ output = &line->output;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (output->state == VFE_OUTPUT_ON &&
+ output->gen2.active_num < 2) {
+ output->buf[output->gen2.active_num++] = buf;
+ ops->vfe_wm_update(vfe, output->wm_idx[0],
+ buf->addr[0], line);
+ ops->reg_update(vfe, line->id);
+ } else {
+ vfe_buf_add_pending(output, buf);
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+/*
+ * vfe_enable_v2 - Enable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_enable_v2(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ int ret;
+
+ mutex_lock(&vfe->stream_lock);
+
+ if (vfe->res->hw_ops->enable_irq)
+ ops->enable_irq(vfe);
+
+ vfe->stream_count++;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ ret = vfe_get_output_v2(line);
+ if (ret < 0)
+ goto error_get_output;
+
+ ret = vfe_enable_output_v2(line);
+ if (ret < 0)
+ goto error_enable_output;
+
+ vfe->was_streaming = 1;
+
+ return 0;
+
+error_enable_output:
+ vfe_put_output(line);
+
+error_get_output:
+ mutex_lock(&vfe->stream_lock);
+
+ vfe->stream_count--;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ return ret;
+}
+
+/*
+ * vfe_get_output_v2 - Get vfe output port for corresponding VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_get_output_v2(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ output = &line->output;
+ if (output->state > VFE_OUTPUT_RESERVED) {
+ dev_err(vfe->camss->dev, "Output is running\n");
+ goto error;
+ }
+
+ output->wm_num = 1;
+
+ /* Correspondence between VFE line number and WM number.
+ * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3
+ * Note this 1:1 mapping will not work for PIX streams.
+ */
+ output->wm_idx[0] = line->id;
+ vfe->wm_output_map[line->id] = line->id;
+
+ output->drop_update_idx = 0;
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+
+error:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ output->state = VFE_OUTPUT_OFF;
+
+ return -EINVAL;
+}
+
int vfe_reset(struct vfe_device *vfe)
{
unsigned long time;
@@ -1698,6 +1973,8 @@ static int vfe_bpl_align(struct vfe_device *vfe)
case CAMSS_8250:
case CAMSS_8280XP:
case CAMSS_845:
+ case CAMSS_8550:
+ case CAMSS_X1E80100:
ret = 16;
break;
default:
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 10e2cc3c0b83..a23f666be753 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -99,7 +99,7 @@ struct vfe_line {
struct vfe_device;
struct vfe_hw_ops {
- void (*enable_irq_common)(struct vfe_device *vfe);
+ void (*enable_irq)(struct vfe_device *vfe);
void (*global_reset)(struct vfe_device *vfe);
u32 (*hw_version)(struct vfe_device *vfe);
irqreturn_t (*isr)(int irq, void *dev);
@@ -114,7 +114,12 @@ struct vfe_hw_ops {
int (*vfe_enable)(struct vfe_line *line);
int (*vfe_halt)(struct vfe_device *vfe);
void (*violation_read)(struct vfe_device *vfe);
+ void (*vfe_wm_start)(struct vfe_device *vfe, u8 wm,
+ struct vfe_line *line);
void (*vfe_wm_stop)(struct vfe_device *vfe, u8 wm);
+ void (*vfe_buf_done)(struct vfe_device *vfe, int port_id);
+ void (*vfe_wm_update)(struct vfe_device *vfe, u8 wm, u32 addr,
+ struct vfe_line *line);
};
struct vfe_isr_ops {
@@ -238,6 +243,8 @@ extern const struct vfe_hw_ops vfe_ops_4_7;
extern const struct vfe_hw_ops vfe_ops_4_8;
extern const struct vfe_hw_ops vfe_ops_170;
extern const struct vfe_hw_ops vfe_ops_480;
+extern const struct vfe_hw_ops vfe_ops_680;
+extern const struct vfe_hw_ops vfe_ops_780;
int vfe_get(struct vfe_device *vfe);
void vfe_put(struct vfe_device *vfe);
@@ -252,4 +259,55 @@ void vfe_put(struct vfe_device *vfe);
*/
bool vfe_is_lite(struct vfe_device *vfe);
+/*
+ * vfe_hw_version - Process write master done interrupt
+ * @vfe: VFE Device
+ *
+ * Return vfe hw version
+ */
+u32 vfe_hw_version(struct vfe_device *vfe);
+/*
+ * vfe_enable - Enable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_enable_v2(struct vfe_line *line);
+
+/*
+ * vfe_buf_done - Process write master done interrupt
+ * @vfe: VFE Device
+ * @wm: Write master id
+ */
+void vfe_buf_done(struct vfe_device *vfe, int wm);
+
+/*
+ * vfe_get_output_v2 - Get vfe output line
+ * line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_get_output_v2(struct vfe_line *line);
+
+/*
+ * vfe_enable_output_v2 - Enable vfe output line
+ * line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_enable_output_v2(struct vfe_line *line);
+
+/*
+ * vfe_queue_buffer_v2 - Add empty buffer
+ * @vid: Video device structure
+ * @buf: Buffer to be enqueued
+ *
+ * Add an empty buffer - depending on the current number of buffers it will be
+ * put in pending buffer queue or directly given to the hardware to be filled.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_queue_buffer_v2(struct camss_video *vid,
+ struct camss_buffer *buf);
+
#endif /* QC_MSM_CAMSS_VFE_H */
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index a85e9df0f301..06f42875702f 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -46,6 +46,7 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = {
.reg = { "csiphy0", "csiphy0_clk_mux" },
.interrupt = { "csiphy0" },
.csiphy = {
+ .id = 0,
.hw_ops = &csiphy_ops_2ph_1_0,
.formats = &csiphy_formats_8x16
}
@@ -62,6 +63,7 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = {
.reg = { "csiphy1", "csiphy1_clk_mux" },
.interrupt = { "csiphy1" },
.csiphy = {
+ .id = 1,
.hw_ops = &csiphy_ops_2ph_1_0,
.formats = &csiphy_formats_8x16
}
@@ -318,6 +320,7 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = {
.reg = { "csiphy0", "csiphy0_clk_mux" },
.interrupt = { "csiphy0" },
.csiphy = {
+ .id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_8x96
}
@@ -334,6 +337,7 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = {
.reg = { "csiphy1", "csiphy1_clk_mux" },
.interrupt = { "csiphy1" },
.csiphy = {
+ .id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_8x96
}
@@ -350,6 +354,7 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = {
.reg = { "csiphy2", "csiphy2_clk_mux" },
.interrupt = { "csiphy2" },
.csiphy = {
+ .id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_8x96
}
@@ -524,6 +529,7 @@ static const struct camss_subdev_resources csiphy_res_660[] = {
.reg = { "csiphy0", "csiphy0_clk_mux" },
.interrupt = { "csiphy0" },
.csiphy = {
+ .id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_8x96
}
@@ -542,6 +548,7 @@ static const struct camss_subdev_resources csiphy_res_660[] = {
.reg = { "csiphy1", "csiphy1_clk_mux" },
.interrupt = { "csiphy1" },
.csiphy = {
+ .id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_8x96
}
@@ -560,6 +567,7 @@ static const struct camss_subdev_resources csiphy_res_660[] = {
.reg = { "csiphy2", "csiphy2_clk_mux" },
.interrupt = { "csiphy2" },
.csiphy = {
+ .id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_8x96
}
@@ -738,6 +746,188 @@ static const struct camss_subdev_resources vfe_res_660[] = {
}
};
+static const struct camss_subdev_resources csiphy_res_670[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "soc_ahb", "cpas_ahb",
+ "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "soc_ahb", "cpas_ahb",
+ "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "soc_ahb", "cpas_ahb",
+ "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ }
+};
+
+static const struct camss_subdev_resources csid_res_670[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "soc_ahb", "vfe0",
+ "vfe0_cphy_rx", "csi0" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 384000000 },
+ { 19200000, 75000000, 384000000, 538666667 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "soc_ahb", "vfe1",
+ "vfe1_cphy_rx", "csi1" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 384000000 },
+ { 19200000, 75000000, 384000000, 538666667 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "soc_ahb", "vfe_lite",
+ "vfe_lite_cphy_rx", "csi2" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 384000000 },
+ { 19200000, 75000000, 384000000, 538666667 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ }
+};
+
+static const struct camss_subdev_resources vfe_res_670[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe0", "vfe0_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 4,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe1", "vfe1_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 0 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 4,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+
+ /* VFE-lite */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe_lite" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 } },
+ .reg = { "vfe_lite" },
+ .interrupt = { "vfe_lite" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ }
+};
+
static const struct camss_subdev_resources csiphy_res_845[] = {
/* CSIPHY0 */
{
@@ -756,6 +946,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
.reg = { "csiphy0" },
.interrupt = { "csiphy0" },
.csiphy = {
+ .id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -778,6 +969,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
.reg = { "csiphy1" },
.interrupt = { "csiphy1" },
.csiphy = {
+ .id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -800,6 +992,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
.reg = { "csiphy2" },
.interrupt = { "csiphy2" },
.csiphy = {
+ .id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -822,6 +1015,7 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
.reg = { "csiphy3" },
.interrupt = { "csiphy3" },
.csiphy = {
+ .id = 3,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -927,6 +1121,7 @@ static const struct camss_subdev_resources vfe_res_845[] = {
.interrupt = { "vfe0" },
.vfe = {
.line_num = 4,
+ .pd_name = "ife0",
.has_pd = true,
.hw_ops = &vfe_ops_170,
.formats_rdi = &vfe_formats_rdi_845,
@@ -954,6 +1149,7 @@ static const struct camss_subdev_resources vfe_res_845[] = {
.interrupt = { "vfe1" },
.vfe = {
.line_num = 4,
+ .pd_name = "ife1",
.has_pd = true,
.hw_ops = &vfe_ops_170,
.formats_rdi = &vfe_formats_rdi_845,
@@ -998,6 +1194,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
.reg = { "csiphy0" },
.interrupt = { "csiphy0" },
.csiphy = {
+ .id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1011,6 +1208,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
.reg = { "csiphy1" },
.interrupt = { "csiphy1" },
.csiphy = {
+ .id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1024,6 +1222,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
.reg = { "csiphy2" },
.interrupt = { "csiphy2" },
.csiphy = {
+ .id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1037,6 +1236,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
.reg = { "csiphy3" },
.interrupt = { "csiphy3" },
.csiphy = {
+ .id = 3,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1050,6 +1250,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
.reg = { "csiphy4" },
.interrupt = { "csiphy4" },
.csiphy = {
+ .id = 4,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1063,6 +1264,7 @@ static const struct camss_subdev_resources csiphy_res_8250[] = {
.reg = { "csiphy5" },
.interrupt = { "csiphy5" },
.csiphy = {
+ .id = 5,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1277,6 +1479,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.reg = { "csiphy0" },
.interrupt = { "csiphy0" },
.csiphy = {
+ .id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sc7280
}
@@ -1291,6 +1494,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.reg = { "csiphy1" },
.interrupt = { "csiphy1" },
.csiphy = {
+ .id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sc7280
}
@@ -1305,6 +1509,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.reg = { "csiphy2" },
.interrupt = { "csiphy2" },
.csiphy = {
+ .id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sc7280
}
@@ -1319,6 +1524,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.reg = { "csiphy3" },
.interrupt = { "csiphy3" },
.csiphy = {
+ .id = 3,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sc7280
}
@@ -1333,6 +1539,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.reg = { "csiphy4" },
.interrupt = { "csiphy4" },
.csiphy = {
+ .id = 4,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sc7280
}
@@ -1443,12 +1650,13 @@ static const struct camss_subdev_resources vfe_res_7280[] = {
.regulators = {},
.clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe0",
- "vfe0_axi", "gcc_cam_hf_axi" },
+ "vfe0_axi", "gcc_axi_hf", "gcc_axi_sf" },
.clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
{ 80000000 },
{ 0 },
{ 380000000, 510000000, 637000000, 760000000 },
{ 0 },
+ { 0 },
{ 0 } },
.reg = { "vfe0" },
@@ -1468,12 +1676,13 @@ static const struct camss_subdev_resources vfe_res_7280[] = {
.regulators = {},
.clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe1",
- "vfe1_axi", "gcc_cam_hf_axi" },
+ "vfe1_axi", "gcc_axi_hf", "gcc_axi_sf" },
.clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
{ 80000000 },
{ 0 },
{ 380000000, 510000000, 637000000, 760000000 },
{ 0 },
+ { 0 },
{ 0 } },
.reg = { "vfe1" },
@@ -1493,12 +1702,13 @@ static const struct camss_subdev_resources vfe_res_7280[] = {
.regulators = {},
.clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe2",
- "vfe2_axi", "gcc_cam_hf_axi" },
+ "vfe2_axi", "gcc_axi_hf", "gcc_axi_sf" },
.clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
{ 80000000 },
{ 0 },
{ 380000000, 510000000, 637000000, 760000000 },
{ 0 },
+ { 0 },
{ 0 } },
.reg = { "vfe2" },
@@ -1516,11 +1726,12 @@ static const struct camss_subdev_resources vfe_res_7280[] = {
/* VFE3 (lite) */
{
.clock = { "camnoc_axi", "cpas_ahb", "icp_ahb",
- "vfe_lite0", "gcc_cam_hf_axi" },
+ "vfe_lite0", "gcc_axi_hf", "gcc_axi_sf" },
.clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
{ 80000000 },
{ 0 },
{ 320000000, 400000000, 480000000, 600000000 },
+ { 0 },
{ 0 } },
.regulators = {},
@@ -1537,11 +1748,12 @@ static const struct camss_subdev_resources vfe_res_7280[] = {
/* VFE4 (lite) */
{
.clock = { "camnoc_axi", "cpas_ahb", "icp_ahb",
- "vfe_lite1", "gcc_cam_hf_axi" },
+ "vfe_lite1", "gcc_axi_hf", "gcc_axi_sf" },
.clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
{ 80000000 },
{ 0 },
{ 320000000, 400000000, 480000000, 600000000 },
+ { 0 },
{ 0 } },
.regulators = {},
@@ -1580,6 +1792,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = {
.reg = { "csiphy0" },
.interrupt = { "csiphy0" },
.csiphy = {
+ .id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1593,6 +1806,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = {
.reg = { "csiphy1" },
.interrupt = { "csiphy1" },
.csiphy = {
+ .id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1606,6 +1820,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = {
.reg = { "csiphy2" },
.interrupt = { "csiphy2" },
.csiphy = {
+ .id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1619,6 +1834,7 @@ static const struct camss_subdev_resources csiphy_res_sc8280xp[] = {
.reg = { "csiphy3" },
.interrupt = { "csiphy3" },
.csiphy = {
+ .id = 3,
.hw_ops = &csiphy_ops_3ph_1_0,
.formats = &csiphy_formats_sdm845
}
@@ -1938,6 +2154,628 @@ static const struct resources_icc icc_res_sc8280xp[] = {
},
};
+static const struct camss_subdev_resources csiphy_res_8550[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" },
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY4 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy4", "csiphy4_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy4" },
+ .interrupt = { "csiphy4" },
+ .csiphy = {
+ .id = 4,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY5 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy5", "csiphy5_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy5" },
+ .interrupt = { "csiphy5" },
+ .csiphy = {
+ .id = 5,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY6 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy6", "csiphy6_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy6" },
+ .interrupt = { "csiphy6" },
+ .csiphy = {
+ .id = 6,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY7 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy7", "csiphy7_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy7" },
+ .interrupt = { "csiphy7" },
+ .csiphy = {
+ .id = 7,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ }
+};
+
+static const struct resources_wrapper csid_wrapper_res_sm8550 = {
+ .reg = "csid_wrapper",
+};
+
+static const struct camss_subdev_resources csid_res_8550[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .is_lite = false,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .is_lite = false,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .is_lite = false,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID3 */
+ {
+ .regulators = {},
+ .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid_lite0" },
+ .interrupt = { "csid_lite0" },
+ .csid = {
+ .is_lite = true,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID4 */
+ {
+ .regulators = {},
+ .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid_lite1" },
+ .interrupt = { "csid_lite1" },
+ .csid = {
+ .is_lite = true,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ }
+};
+
+static const struct camss_subdev_resources vfe_res_8550[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe0_fast_ahb",
+ "vfe0", "cpas_vfe0", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe1_fast_ahb",
+ "vfe1", "cpas_vfe1", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe2_fast_ahb",
+ "vfe2", "cpas_vfe2", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe2" },
+ .interrupt = { "vfe2" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife2",
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE3 lite */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
+ "vfe_lite", "cpas_ife_lite", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 400000000, 480000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE4 lite */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
+ "vfe_lite", "cpas_ife_lite", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 400000000, 480000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+};
+
+static const struct resources_icc icc_res_sm8550[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "hf_0_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
+static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+ /* CSIPHY4 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy4", "csiphy4_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy4" },
+ .interrupt = { "csiphy4" },
+ .csiphy = {
+ .id = 4,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+};
+
+static const struct camss_subdev_resources csid_res_x1e80100[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ },
+ },
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ },
+ },
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ },
+ },
+ /* CSID_LITE0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid_lite0" },
+ .interrupt = { "csid_lite0" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID_LITE1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+
+ .reg = { "csid_lite1" },
+ .interrupt = { "csid_lite1" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+};
+
+static const struct camss_subdev_resources vfe_res_x1e80100[] = {
+ /* IFE0 */
+ {
+ .regulators = {},
+ .clock = {"camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "cpas_fast_ahb", "cpas_vfe0", "vfe0_fast_ahb",
+ "vfe0" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 345600000, 432000000, 594000000, 675000000,
+ 727000000 }, },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* IFE1 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "cpas_fast_ahb", "cpas_vfe1", "vfe1_fast_ahb",
+ "vfe1" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 345600000, 432000000, 594000000, 675000000,
+ 727000000 }, },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* IFE_LITE_0 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite",
+ "vfe_lite_csid" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 266666667, 400000000, 480000000 },
+ { 266666667, 400000000, 480000000 }, },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* IFE_LITE_1 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite",
+ "vfe_lite_csid" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 266666667, 400000000, 480000000 },
+ { 266666667, 400000000, 480000000 }, },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+};
+
+static const struct resources_icc icc_res_x1e80100[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 150000,
+ .icc_bw_tbl.peak = 300000,
+ },
+ {
+ .name = "hf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "sf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "sf_icp_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
+static const struct resources_wrapper csid_wrapper_res_x1e80100 = {
+ .reg = "csid_wrapper",
+};
+
/*
* camss_add_clock_margin - Add margin to clock frequency rate
* @rate: Clock frequency rate
@@ -1996,12 +2834,12 @@ void camss_disable_clocks(int nclocks, struct camss_clock *clock)
}
/*
- * camss_find_sensor - Find a linked media entity which represents a sensor
+ * camss_find_sensor_pad - Find the media pad via which the sensor is linked
* @entity: Media entity to start searching from
*
- * Return a pointer to sensor media entity or NULL if not found
+ * Return a pointer to sensor media pad or NULL if not found
*/
-struct media_entity *camss_find_sensor(struct media_entity *entity)
+struct media_pad *camss_find_sensor_pad(struct media_entity *entity)
{
struct media_pad *pad;
@@ -2017,7 +2855,7 @@ struct media_entity *camss_find_sensor(struct media_entity *entity)
entity = pad->entity;
if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
- return entity;
+ return pad;
}
}
@@ -2032,16 +2870,13 @@ struct media_entity *camss_find_sensor(struct media_entity *entity)
s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
unsigned int lanes)
{
- struct media_entity *sensor;
- struct v4l2_subdev *subdev;
+ struct media_pad *sensor_pad;
- sensor = camss_find_sensor(entity);
- if (!sensor)
+ sensor_pad = camss_find_sensor_pad(entity);
+ if (!sensor_pad)
return -ENODEV;
- subdev = media_entity_to_v4l2_subdev(sensor);
-
- return v4l2_get_link_freq(subdev->ctrl_handler, bpp, 2 * lanes);
+ return v4l2_get_link_freq(sensor_pad, bpp, 2 * lanes);
}
/*
@@ -2053,15 +2888,15 @@ s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
*/
int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock)
{
- struct media_entity *sensor;
+ struct media_pad *sensor_pad;
struct v4l2_subdev *subdev;
struct v4l2_ctrl *ctrl;
- sensor = camss_find_sensor(entity);
- if (!sensor)
+ sensor_pad = camss_find_sensor_pad(entity);
+ if (!sensor_pad)
return -ENODEV;
- subdev = media_entity_to_v4l2_subdev(sensor);
+ subdev = media_entity_to_v4l2_subdev(sensor_pad->entity);
ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
@@ -2159,6 +2994,15 @@ static int camss_of_parse_endpoint_node(struct device *dev,
if (ret)
return ret;
+ /*
+ * Most SoCs support both D-PHY and C-PHY standards, but currently only
+ * D-PHY is supported in the driver.
+ */
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
+ dev_err(dev, "Unsupported bus type %d\n", vep.bus_type);
+ return -EINVAL;
+ }
+
csd->interface.csiphy_id = vep.base.port;
mipi_csi2 = &vep.bus.mipi_csi2;
@@ -2245,7 +3089,8 @@ static int camss_init_subdevices(struct camss *camss)
for (i = 0; i < camss->res->csiphy_num; i++) {
ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i],
- &res->csiphy_res[i], i);
+ &res->csiphy_res[i],
+ res->csiphy_res[i].csiphy.id);
if (ret < 0) {
dev_err(camss->dev,
"Failed to init csiphy%d sub-device: %d\n",
@@ -2403,6 +3248,28 @@ static int camss_link_entities(struct camss *camss)
return 0;
}
+void camss_reg_update(struct camss *camss, int hw_id, int port_id, bool is_clear)
+{
+ struct csid_device *csid;
+
+ if (hw_id < camss->res->csid_num) {
+ csid = &camss->csid[hw_id];
+
+ csid->res->hw_ops->reg_update(csid, port_id, is_clear);
+ }
+}
+
+void camss_buf_done(struct camss *camss, int hw_id, int port_id)
+{
+ struct vfe_device *vfe;
+
+ if (hw_id < camss->res->vfe_num) {
+ vfe = &camss->vfe[hw_id];
+
+ vfe->res->hw_ops->vfe_buf_done(vfe, port_id);
+ }
+}
+
/*
* camss_register_entities - Register subdev nodes and create links
* @camss: CAMSS device
@@ -2898,8 +3765,20 @@ static const struct camss_resources sdm660_resources = {
.link_entities = camss_link_entities
};
+static const struct camss_resources sdm670_resources = {
+ .version = CAMSS_845,
+ .csiphy_res = csiphy_res_670,
+ .csid_res = csid_res_670,
+ .vfe_res = vfe_res_670,
+ .csiphy_num = ARRAY_SIZE(csiphy_res_670),
+ .csid_num = ARRAY_SIZE(csid_res_670),
+ .vfe_num = ARRAY_SIZE(vfe_res_670),
+ .link_entities = camss_link_entities
+};
+
static const struct camss_resources sdm845_resources = {
.version = CAMSS_845,
+ .pd_name = "top",
.csiphy_res = csiphy_res_845,
.csid_res = csid_res_845,
.vfe_res = vfe_res_845,
@@ -2952,6 +3831,36 @@ static const struct camss_resources sc7280_resources = {
.link_entities = camss_link_entities
};
+static const struct camss_resources sm8550_resources = {
+ .version = CAMSS_8550,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_8550,
+ .csid_res = csid_res_8550,
+ .vfe_res = vfe_res_8550,
+ .csid_wrapper_res = &csid_wrapper_res_sm8550,
+ .icc_res = icc_res_sm8550,
+ .icc_path_num = ARRAY_SIZE(icc_res_sm8550),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_8550),
+ .csid_num = ARRAY_SIZE(csid_res_8550),
+ .vfe_num = ARRAY_SIZE(vfe_res_8550),
+ .link_entities = camss_link_entities
+};
+
+static const struct camss_resources x1e80100_resources = {
+ .version = CAMSS_X1E80100,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_x1e80100,
+ .csid_res = csid_res_x1e80100,
+ .vfe_res = vfe_res_x1e80100,
+ .csid_wrapper_res = &csid_wrapper_res_x1e80100,
+ .icc_res = icc_res_x1e80100,
+ .icc_path_num = ARRAY_SIZE(icc_res_x1e80100),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_x1e80100),
+ .csid_num = ARRAY_SIZE(csid_res_x1e80100),
+ .vfe_num = ARRAY_SIZE(vfe_res_x1e80100),
+ .link_entities = camss_link_entities
+};
+
static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,msm8916-camss", .data = &msm8916_resources },
{ .compatible = "qcom,msm8953-camss", .data = &msm8953_resources },
@@ -2959,8 +3868,11 @@ static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,sc7280-camss", .data = &sc7280_resources },
{ .compatible = "qcom,sc8280xp-camss", .data = &sc8280xp_resources },
{ .compatible = "qcom,sdm660-camss", .data = &sdm660_resources },
+ { .compatible = "qcom,sdm670-camss", .data = &sdm670_resources },
{ .compatible = "qcom,sdm845-camss", .data = &sdm845_resources },
{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
+ { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
+ { .compatible = "qcom,x1e80100-camss", .data = &x1e80100_resources },
{ }
};
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 9a046eea334f..63c0afee154a 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -85,6 +85,8 @@ enum camss_version {
CAMSS_8250,
CAMSS_8280XP,
CAMSS_845,
+ CAMSS_8550,
+ CAMSS_X1E80100,
};
enum icc_count {
@@ -153,7 +155,7 @@ void camss_add_clock_margin(u64 *rate);
int camss_enable_clocks(int nclocks, struct camss_clock *clock,
struct device *dev);
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
-struct media_entity *camss_find_sensor(struct media_entity *entity);
+struct media_pad *camss_find_sensor_pad(struct media_entity *entity);
s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
unsigned int lanes);
int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock);
@@ -162,5 +164,8 @@ void camss_pm_domain_off(struct camss *camss, int id);
int camss_vfe_get(struct camss *camss, int id);
void camss_vfe_put(struct camss *camss, int id);
void camss_delete(struct camss *camss);
+void camss_buf_done(struct camss *camss, int hw_id, int port_id);
+void camss_reg_update(struct camss *camss, int hw_id,
+ int port_id, bool is_clear);
#endif /* QC_MSM_CAMSS_H */
diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig
new file mode 100644
index 000000000000..3c803a05305a
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/Kconfig
@@ -0,0 +1,13 @@
+config VIDEO_QCOM_IRIS
+ tristate "Qualcomm iris V4L2 decoder driver"
+ depends on VIDEO_DEV
+ depends on ARCH_QCOM || COMPILE_TEST
+ select V4L2_MEM2MEM_DEV
+ select QCOM_MDT_LOADER if ARCH_QCOM
+ select QCOM_SCM
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ This is a V4L2 driver for Qualcomm iris video accelerator
+ hardware. It accelerates decoding operations on various
+ Qualcomm SoCs.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile
new file mode 100644
index 000000000000..e86d00ee6f15
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -0,0 +1,31 @@
+qcom-iris-objs += \
+ iris_buffer.o \
+ iris_core.o \
+ iris_ctrls.o \
+ iris_firmware.o \
+ iris_hfi_common.o \
+ iris_hfi_gen1_command.o \
+ iris_hfi_gen1_response.o \
+ iris_hfi_gen2_command.o \
+ iris_hfi_gen2_packet.o \
+ iris_hfi_gen2_response.o \
+ iris_hfi_queue.o \
+ iris_platform_gen2.o \
+ iris_power.o \
+ iris_probe.o \
+ iris_resources.o \
+ iris_state.o \
+ iris_utils.o \
+ iris_vidc.o \
+ iris_vb2.o \
+ iris_vdec.o \
+ iris_vpu2.o \
+ iris_vpu3x.o \
+ iris_vpu_buffer.o \
+ iris_vpu_common.o \
+
+ifeq ($(CONFIG_VIDEO_QCOM_VENUS),)
+qcom-iris-objs += iris_platform_sm8250.o
+endif
+
+obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
new file mode 100644
index 000000000000..e5c5a564fcb8
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_buffer.h"
+#include "iris_instance.h"
+#include "iris_power.h"
+#include "iris_vpu_buffer.h"
+
+#define PIXELS_4K 4096
+#define MAX_WIDTH 4096
+#define MAX_HEIGHT 2304
+#define Y_STRIDE_ALIGN 128
+#define UV_STRIDE_ALIGN 128
+#define Y_SCANLINE_ALIGN 32
+#define UV_SCANLINE_ALIGN 16
+#define UV_SCANLINE_ALIGN_QC08C 32
+#define META_STRIDE_ALIGNED 64
+#define META_SCANLINE_ALIGNED 16
+#define NUM_MBS_4K (DIV_ROUND_UP(MAX_WIDTH, 16) * DIV_ROUND_UP(MAX_HEIGHT, 16))
+
+/*
+ * NV12:
+ * YUV 4:2:0 image with a plane of 8 bit Y samples followed
+ * by an interleaved U/V plane containing 8 bit 2x2 subsampled
+ * colour difference samples.
+ *
+ * <-Y/UV_Stride (aligned to 128)->
+ * <------- Width ------->
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | y_scanlines (aligned to 32)
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * U V U V U V U V U V U V . . . . ^
+ * U V U V U V U V U V U V . . . . |
+ * U V U V U V U V U V U V . . . . |
+ * U V U V U V U V U V U V . . . . uv_scanlines (aligned to 16)
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . . . --> Buffer size aligned to 4K
+ *
+ * y_stride : Width aligned to 128
+ * uv_stride : Width aligned to 128
+ * y_scanlines: Height aligned to 32
+ * uv_scanlines: Height/2 aligned to 16
+ * Total size = align((y_stride * y_scanlines
+ * + uv_stride * uv_scanlines , 4096)
+ *
+ * Note: All the alignments are hardware requirements.
+ */
+static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst)
+{
+ u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines;
+ struct v4l2_format *f = inst->fmt_dst;
+
+ y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
+ uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
+ y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN);
+ uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN);
+ y_plane = y_stride * y_scanlines;
+ uv_plane = uv_stride * uv_scanlines;
+
+ return ALIGN(y_plane + uv_plane, PIXELS_4K);
+}
+
+/*
+ * QC08C:
+ * Compressed Macro-tile format for NV12.
+ * Contains 4 planes in the following order -
+ * (A) Y_Meta_Plane
+ * (B) Y_UBWC_Plane
+ * (C) UV_Meta_Plane
+ * (D) UV_UBWC_Plane
+ *
+ * Y_Meta_Plane consists of meta information to decode compressed
+ * tile data in Y_UBWC_Plane.
+ * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+ * UBWC decoder block will use the Y_Meta_Plane data together with
+ * Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples.
+ *
+ * UV_Meta_Plane consists of meta information to decode compressed
+ * tile data in UV_UBWC_Plane.
+ * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+ * UBWC decoder block will use UV_Meta_Plane data together with
+ * UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2
+ * subsampled color difference samples.
+ *
+ * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+ * and randomly accessible. There is no dependency between tiles.
+ *
+ * <----- y_meta_stride ----> (aligned to 64)
+ * <-------- Width ------>
+ * M M M M M M M M M M M M . . ^ ^
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . Height |
+ * M M M M M M M M M M M M . . | y_meta_scanlines (aligned to 16)
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . V |
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * . . . . . . . . . . . . . . V
+ * <--Compressed tile y_stride---> (aligned to 128)
+ * <------- Width ------->
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile y_scanlines (aligned to 32)
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * . . . . . . . . . . . . . . . . V
+ * <----- uv_meta_stride ----> (aligned to 64)
+ * M M M M M M M M M M M M . . ^
+ * M M M M M M M M M M M M . . |
+ * M M M M M M M M M M M M . . |
+ * M M M M M M M M M M M M . . uv_meta_scanlines (aligned to 16)
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * <--Compressed tile uv_stride---> (aligned to 128)
+ * U* V* U* V* U* V* U* V* . . . . ^
+ * U* V* U* V* U* V* U* V* . . . . |
+ * U* V* U* V* U* V* U* V* . . . . |
+ * U* V* U* V* U* V* U* V* . . . . uv_scanlines (aligned to 32)
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ *
+ * y_stride: width aligned to 128
+ * uv_stride: width aligned to 128
+ * y_scanlines: height aligned to 32
+ * uv_scanlines: height aligned to 32
+ * y_plane: buffer size aligned to 4096
+ * uv_plane: buffer size aligned to 4096
+ * y_meta_stride: width aligned to 64
+ * y_meta_scanlines: height aligned to 16
+ * y_meta_plane: buffer size aligned to 4096
+ * uv_meta_stride: width aligned to 64
+ * uv_meta_scanlines: height aligned to 16
+ * uv_meta_plane: buffer size aligned to 4096
+ *
+ * Total size = align( y_plane + uv_plane +
+ * y_meta_plane + uv_meta_plane, 4096)
+ *
+ * Note: All the alignments are hardware requirements.
+ */
+static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst)
+{
+ u32 y_plane, uv_plane, y_stride, uv_stride;
+ struct v4l2_format *f = inst->fmt_dst;
+ u32 uv_meta_stride, uv_meta_plane;
+ u32 y_meta_stride, y_meta_plane;
+
+ y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, META_STRIDE_ALIGNED >> 1),
+ META_STRIDE_ALIGNED);
+ y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height,
+ META_SCANLINE_ALIGNED >> 1),
+ META_SCANLINE_ALIGNED);
+ y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K);
+
+ y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
+ y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN), PIXELS_4K);
+
+ uv_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width / 2, META_STRIDE_ALIGNED >> 2),
+ META_STRIDE_ALIGNED);
+ uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height / 2,
+ META_SCANLINE_ALIGNED >> 1),
+ META_SCANLINE_ALIGNED);
+ uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K);
+
+ uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
+ uv_plane = ALIGN(uv_stride * ALIGN(f->fmt.pix_mp.height / 2, UV_SCANLINE_ALIGN_QC08C),
+ PIXELS_4K);
+
+ return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K);
+}
+
+static u32 iris_bitstream_buffer_size(struct iris_inst *inst)
+{
+ struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
+ u32 base_res_mbs = NUM_MBS_4K;
+ u32 frame_size, num_mbs;
+ u32 div_factor = 2;
+
+ num_mbs = iris_get_mbpf(inst);
+ if (num_mbs > NUM_MBS_4K) {
+ div_factor = 4;
+ base_res_mbs = caps->max_mbpf;
+ }
+
+ /*
+ * frame_size = YUVsize / div_factor
+ * where YUVsize = resolution_in_MBs * MBs_in_pixel * 3 / 2
+ */
+ frame_size = base_res_mbs * (16 * 16) * 3 / 2 / div_factor;
+
+ return ALIGN(frame_size, PIXELS_4K);
+}
+
+int iris_get_buffer_size(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return iris_bitstream_buffer_size(inst);
+ case BUF_OUTPUT:
+ return iris_yuv_buffer_size_nv12(inst);
+ case BUF_DPB:
+ return iris_yuv_buffer_size_qc08c(inst);
+ default:
+ return 0;
+ }
+}
+
+static void iris_fill_internal_buf_info(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ struct iris_buffers *buffers = &inst->buffers[buffer_type];
+
+ buffers->size = iris_vpu_buf_size(inst, buffer_type);
+ buffers->min_count = iris_vpu_buf_count(inst, buffer_type);
+}
+
+void iris_get_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ for (i = 0; i < internal_buffer_count; i++)
+ iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ for (i = 0; i < internal_buffer_count; i++)
+ iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ }
+}
+
+static int iris_create_internal_buffer(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type, u32 index)
+{
+ struct iris_buffers *buffers = &inst->buffers[buffer_type];
+ struct iris_core *core = inst->core;
+ struct iris_buffer *buffer;
+
+ if (!buffers->size)
+ return 0;
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&buffer->list);
+ buffer->type = buffer_type;
+ buffer->index = index;
+ buffer->buffer_size = buffers->size;
+ buffer->dma_attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING;
+ list_add_tail(&buffer->list, &buffers->list);
+
+ buffer->kvaddr = dma_alloc_attrs(core->dev, buffer->buffer_size,
+ &buffer->device_addr, GFP_KERNEL, buffer->dma_attrs);
+ if (!buffer->kvaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ u32 internal_buffer_count, i, j;
+ struct iris_buffers *buffers;
+ const u32 *internal_buf_type;
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ }
+
+ for (i = 0; i < internal_buffer_count; i++) {
+ buffers = &inst->buffers[internal_buf_type[i]];
+ for (j = 0; j < buffers->min_count; j++) {
+ ret = iris_create_internal_buffer(inst, internal_buf_type[i], j);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->session_queue_buf(inst, buf);
+ if (ret)
+ return ret;
+
+ buf->attr &= ~BUF_ATTR_DEFERRED;
+ buf->attr |= BUF_ATTR_QUEUED;
+
+ return 0;
+}
+
+int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ struct iris_buffer *buffer, *next;
+ struct iris_buffers *buffers;
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ }
+
+ for (i = 0; i < internal_buffer_count; i++) {
+ buffers = &inst->buffers[internal_buf_type[i]];
+ list_for_each_entry_safe(buffer, next, &buffers->list, list) {
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ continue;
+ if (buffer->attr & BUF_ATTR_QUEUED)
+ continue;
+ ret = iris_queue_buffer(inst, buffer);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
+{
+ struct iris_core *core = inst->core;
+
+ list_del(&buffer->list);
+ dma_free_attrs(core->dev, buffer->buffer_size, buffer->kvaddr,
+ buffer->device_addr, buffer->dma_attrs);
+ kfree(buffer);
+
+ return 0;
+}
+
+int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ struct iris_buffer *buf, *next;
+ struct iris_buffers *buffers;
+ const u32 *internal_buf_type;
+ u32 i, len;
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ len = platform_data->dec_ip_int_buf_tbl_size;
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ len = platform_data->dec_op_int_buf_tbl_size;
+ }
+
+ for (i = 0; i < len; i++) {
+ buffers = &inst->buffers[internal_buf_type[i]];
+ list_for_each_entry_safe(buf, next, &buffers->list, list) {
+ ret = iris_destroy_internal_buffer(inst, buf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int iris_release_internal_buffers(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ struct iris_buffers *buffers = &inst->buffers[buffer_type];
+ struct iris_buffer *buffer, *next;
+ int ret;
+
+ list_for_each_entry_safe(buffer, next, &buffers->list, list) {
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ continue;
+ if (!(buffer->attr & BUF_ATTR_QUEUED))
+ continue;
+ ret = hfi_ops->session_release_buf(inst, buffer);
+ if (ret)
+ return ret;
+ buffer->attr |= BUF_ATTR_PENDING_RELEASE;
+ }
+
+ return 0;
+}
+
+static int iris_release_input_internal_buffers(struct iris_inst *inst)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+ int ret;
+
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+
+ for (i = 0; i < internal_buffer_count; i++) {
+ ret = iris_release_internal_buffers(inst, internal_buf_type[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst)
+{
+ struct iris_buffers *buffers = &inst->buffers[BUF_PERSIST];
+ struct iris_buffer *buffer, *next;
+ int ret;
+ u32 i;
+
+ if (!list_empty(&buffers->list))
+ return 0;
+
+ iris_fill_internal_buf_info(inst, BUF_PERSIST);
+
+ for (i = 0; i < buffers->min_count; i++) {
+ ret = iris_create_internal_buffer(inst, BUF_PERSIST, i);
+ if (ret)
+ return ret;
+ }
+
+ list_for_each_entry_safe(buffer, next, &buffers->list, list) {
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ continue;
+ if (buffer->attr & BUF_ATTR_QUEUED)
+ continue;
+ ret = iris_queue_buffer(inst, buffer);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst)
+{
+ int ret;
+
+ iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ ret = iris_release_input_internal_buffers(inst);
+ if (ret)
+ return ret;
+
+ ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+ int ret;
+
+ iris_scale_power(inst);
+
+ if (buf_type == BUF_INPUT) {
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_DEFERRED))
+ continue;
+ ret = iris_queue_buffer(inst, buf);
+ if (ret)
+ return ret;
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_DEFERRED))
+ continue;
+ ret = iris_queue_buffer(inst, buf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void iris_vb2_queue_error(struct iris_inst *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct vb2_queue *q;
+
+ q = v4l2_m2m_get_src_vq(m2m_ctx);
+ vb2_queue_error(q);
+ q = v4l2_m2m_get_dst_vq(m2m_ctx);
+ vb2_queue_error(q);
+}
+
+static struct vb2_v4l2_buffer *
+iris_helper_find_buf(struct iris_inst *inst, u32 type, u32 idx)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, idx);
+ else
+ return v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, idx);
+}
+
+static void iris_get_ts_metadata(struct iris_inst *inst, u64 timestamp_ns,
+ struct vb2_v4l2_buffer *vbuf)
+{
+ u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(inst->tss); ++i) {
+ if (inst->tss[i].ts_ns != timestamp_ns)
+ continue;
+
+ vbuf->flags &= ~mask;
+ vbuf->flags |= inst->tss[i].flags;
+ vbuf->timecode = inst->tss[i].tc;
+ return;
+ }
+
+ vbuf->flags &= ~mask;
+ vbuf->flags |= inst->tss[inst->metadata_idx].flags;
+ vbuf->timecode = inst->tss[inst->metadata_idx].tc;
+}
+
+int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct vb2_v4l2_buffer *vbuf;
+ struct vb2_buffer *vb2;
+ u32 type, state;
+
+ switch (buf->type) {
+ case BUF_INPUT:
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ break;
+ case BUF_OUTPUT:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ break;
+ default:
+ return 0; /* Internal DPB Buffers */
+ }
+
+ vbuf = iris_helper_find_buf(inst, type, buf->index);
+ if (!vbuf)
+ return -EINVAL;
+
+ vb2 = &vbuf->vb2_buf;
+
+ if (buf->flags & V4L2_BUF_FLAG_ERROR)
+ state = VB2_BUF_STATE_ERROR;
+ else
+ state = VB2_BUF_STATE_DONE;
+
+ vbuf->flags |= buf->flags;
+
+ if (V4L2_TYPE_IS_CAPTURE(type)) {
+ vb2_set_plane_payload(vb2, 0, buf->data_size);
+ vbuf->sequence = inst->sequence_cap++;
+ iris_get_ts_metadata(inst, buf->timestamp, vbuf);
+ } else {
+ vbuf->sequence = inst->sequence_out++;
+ }
+
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST) {
+ if (!v4l2_m2m_has_stopped(m2m_ctx)) {
+ const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
+
+ v4l2_event_queue_fh(&inst->fh, &ev);
+ v4l2_m2m_mark_stopped(m2m_ctx);
+ }
+ }
+ vb2->timestamp = buf->timestamp;
+ v4l2_m2m_buf_done(vbuf, state);
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h
new file mode 100644
index 000000000000..c36b6347b077
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_buffer.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_BUFFER_H__
+#define __IRIS_BUFFER_H__
+
+#include <media/videobuf2-v4l2.h>
+
+struct iris_inst;
+
+#define to_iris_buffer(ptr) container_of(ptr, struct iris_buffer, vb2)
+
+/**
+ * enum iris_buffer_type
+ *
+ * @BUF_INPUT: input buffer to the iris hardware
+ * @BUF_OUTPUT: output buffer from the iris hardware
+ * @BUF_BIN: buffer to store intermediate bin data
+ * @BUF_ARP: buffer for auto register programming
+ * @BUF_COMV: buffer to store colocated motion vectors
+ * @BUF_NON_COMV: buffer to hold config data for HW
+ * @BUF_LINE: buffer to store decoding/encoding context data for HW
+ * @BUF_DPB: buffer to store display picture buffers for reference
+ * @BUF_PERSIST: buffer to store session context data
+ * @BUF_SCRATCH_1: buffer to store decoding/encoding context data for HW
+ * @BUF_TYPE_MAX: max buffer types
+ */
+enum iris_buffer_type {
+ BUF_INPUT = 1,
+ BUF_OUTPUT,
+ BUF_BIN,
+ BUF_ARP,
+ BUF_COMV,
+ BUF_NON_COMV,
+ BUF_LINE,
+ BUF_DPB,
+ BUF_PERSIST,
+ BUF_SCRATCH_1,
+ BUF_TYPE_MAX,
+};
+
+/*
+ * enum iris_buffer_attributes
+ *
+ * BUF_ATTR_DEFERRED: buffer queued by client but not submitted to firmware.
+ * BUF_ATTR_PENDING_RELEASE: buffers requested to be released from firmware.
+ * BUF_ATTR_QUEUED: buffers submitted to firmware.
+ * BUF_ATTR_DEQUEUED: buffers received from firmware.
+ * BUF_ATTR_BUFFER_DONE: buffers sent back to vb2.
+ */
+enum iris_buffer_attributes {
+ BUF_ATTR_DEFERRED = BIT(0),
+ BUF_ATTR_PENDING_RELEASE = BIT(1),
+ BUF_ATTR_QUEUED = BIT(2),
+ BUF_ATTR_DEQUEUED = BIT(3),
+ BUF_ATTR_BUFFER_DONE = BIT(4),
+};
+
+/**
+ * struct iris_buffer
+ *
+ * @vb2: v4l2 vb2 buffer
+ * @list: list head for the iris_buffers structure
+ * @inst: iris instance structure
+ * @type: enum for type of iris buffer
+ * @index: identifier for the iris buffer
+ * @fd: file descriptor of the buffer
+ * @buffer_size: accessible buffer size in bytes starting from addr_offset
+ * @data_offset: accessible buffer offset from base address
+ * @data_size: data size in bytes
+ * @device_addr: device address of the buffer
+ * @kvaddr: kernel virtual address of the buffer
+ * @dma_attrs: dma attributes
+ * @flags: buffer flags. It is represented as bit masks.
+ * @timestamp: timestamp of the buffer in nano seconds (ns)
+ * @attr: enum for iris buffer attributes
+ */
+struct iris_buffer {
+ struct vb2_v4l2_buffer vb2;
+ struct list_head list;
+ struct iris_inst *inst;
+ enum iris_buffer_type type;
+ u32 index;
+ int fd;
+ size_t buffer_size;
+ u32 data_offset;
+ size_t data_size;
+ dma_addr_t device_addr;
+ void *kvaddr;
+ unsigned long dma_attrs;
+ u32 flags; /* V4L2_BUF_FLAG_* */
+ u64 timestamp;
+ enum iris_buffer_attributes attr;
+};
+
+struct iris_buffers {
+ struct list_head list;
+ u32 min_count;
+ u32 size;
+};
+
+int iris_get_buffer_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+void iris_get_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_create_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer);
+int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst);
+int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst);
+int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf);
+int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type);
+int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf);
+void iris_vb2_queue_error(struct iris_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c
new file mode 100644
index 000000000000..0fa0a3b549a2
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_core.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "iris_core.h"
+#include "iris_firmware.h"
+#include "iris_state.h"
+#include "iris_vpu_common.h"
+
+void iris_core_deinit(struct iris_core *core)
+{
+ pm_runtime_resume_and_get(core->dev);
+
+ mutex_lock(&core->lock);
+ iris_fw_unload(core);
+ iris_vpu_power_off(core);
+ iris_hfi_queues_deinit(core);
+ core->state = IRIS_CORE_DEINIT;
+ mutex_unlock(&core->lock);
+
+ pm_runtime_put_sync(core->dev);
+}
+
+static int iris_wait_for_system_response(struct iris_core *core)
+{
+ u32 hw_response_timeout_val = core->iris_platform_data->hw_response_timeout;
+ int ret;
+
+ if (core->state == IRIS_CORE_ERROR)
+ return -EIO;
+
+ ret = wait_for_completion_timeout(&core->core_init_done,
+ msecs_to_jiffies(hw_response_timeout_val));
+ if (!ret) {
+ core->state = IRIS_CORE_ERROR;
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int iris_core_init(struct iris_core *core)
+{
+ int ret;
+
+ mutex_lock(&core->lock);
+ if (core->state == IRIS_CORE_INIT) {
+ ret = 0;
+ goto exit;
+ } else if (core->state == IRIS_CORE_ERROR) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ core->state = IRIS_CORE_INIT;
+
+ ret = iris_hfi_queues_init(core);
+ if (ret)
+ goto error;
+
+ ret = iris_vpu_power_on(core);
+ if (ret)
+ goto error_queue_deinit;
+
+ ret = iris_fw_load(core);
+ if (ret)
+ goto error_power_off;
+
+ ret = iris_vpu_boot_firmware(core);
+ if (ret)
+ goto error_unload_fw;
+
+ ret = iris_hfi_core_init(core);
+ if (ret)
+ goto error_unload_fw;
+
+ mutex_unlock(&core->lock);
+
+ return iris_wait_for_system_response(core);
+
+error_unload_fw:
+ iris_fw_unload(core);
+error_power_off:
+ iris_vpu_power_off(core);
+error_queue_deinit:
+ iris_hfi_queues_deinit(core);
+error:
+ core->state = IRIS_CORE_DEINIT;
+exit:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h
new file mode 100644
index 000000000000..aeeac32a1f6d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_core.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_CORE_H__
+#define __IRIS_CORE_H__
+
+#include <linux/types.h>
+#include <linux/pm_domain.h>
+#include <media/v4l2-device.h>
+
+#include "iris_hfi_common.h"
+#include "iris_hfi_queue.h"
+#include "iris_platform_common.h"
+#include "iris_resources.h"
+#include "iris_state.h"
+
+struct icc_info {
+ const char *name;
+ u32 bw_min_kbps;
+ u32 bw_max_kbps;
+};
+
+#define IRIS_FW_VERSION_LENGTH 128
+#define IFACEQ_CORE_PKT_SIZE (1024 * 4)
+
+/**
+ * struct iris_core - holds core parameters valid for all instances
+ *
+ * @dev: reference to device structure
+ * @reg_base: IO memory base address
+ * @irq: iris irq
+ * @v4l2_dev: a holder for v4l2 device structure
+ * @vdev_dec: iris video device structure for decoder
+ * @iris_v4l2_file_ops: iris v4l2 file ops
+ * @iris_v4l2_ioctl_ops: iris v4l2 ioctl ops
+ * @iris_vb2_ops: iris vb2 ops
+ * @icc_tbl: table of iris interconnects
+ * @icc_count: count of iris interconnects
+ * @pmdomain_tbl: table of iris power domains
+ * @opp_pmdomain_tbl: table of opp power domains
+ * @clock_tbl: table of iris clocks
+ * @clk_count: count of iris clocks
+ * @resets: table of iris reset clocks
+ * @controller_resets: table of controller reset clocks
+ * @iris_platform_data: a structure for platform data
+ * @state: current state of core
+ * @iface_q_table_daddr: device address for interface queue table memory
+ * @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory
+ * @iface_q_table_vaddr: virtual address for interface queue table memory
+ * @sfr_vaddr: virtual address for SFR (Sub System Failure Reason) register memory
+ * @command_queue: shared interface queue to send commands to firmware
+ * @message_queue: shared interface queue to receive responses from firmware
+ * @debug_queue: shared interface queue to receive debug info from firmware
+ * @lock: a lock for this strucure
+ * @response_packet: a pointer to response packet from fw to driver
+ * @header_id: id of packet header
+ * @packet_id: id of packet
+ * @power: a structure for clock and bw information
+ * @hfi_ops: iris hfi command ops
+ * @hfi_response_ops: iris hfi response ops
+ * @core_init_done: structure of signal completion for system response
+ * @intr_status: interrupt status
+ * @sys_error_handler: a delayed work for handling system fatal error
+ * @instances: a list_head of all instances
+ * @inst_fw_caps: an array of supported instance capabilities
+ */
+
+struct iris_core {
+ struct device *dev;
+ void __iomem *reg_base;
+ int irq;
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev_dec;
+ const struct v4l2_file_operations *iris_v4l2_file_ops;
+ const struct v4l2_ioctl_ops *iris_v4l2_ioctl_ops;
+ const struct vb2_ops *iris_vb2_ops;
+ struct icc_bulk_data *icc_tbl;
+ u32 icc_count;
+ struct dev_pm_domain_list *pmdomain_tbl;
+ struct dev_pm_domain_list *opp_pmdomain_tbl;
+ struct clk_bulk_data *clock_tbl;
+ u32 clk_count;
+ struct reset_control_bulk_data *resets;
+ struct reset_control_bulk_data *controller_resets;
+ const struct iris_platform_data *iris_platform_data;
+ enum iris_core_state state;
+ dma_addr_t iface_q_table_daddr;
+ dma_addr_t sfr_daddr;
+ void *iface_q_table_vaddr;
+ void *sfr_vaddr;
+ struct iris_iface_q_info command_queue;
+ struct iris_iface_q_info message_queue;
+ struct iris_iface_q_info debug_queue;
+ struct mutex lock; /* lock for core related operations */
+ u8 *response_packet;
+ u32 header_id;
+ u32 packet_id;
+ struct iris_core_power power;
+ const struct iris_hfi_command_ops *hfi_ops;
+ const struct iris_hfi_response_ops *hfi_response_ops;
+ struct completion core_init_done;
+ u32 intr_status;
+ struct delayed_work sys_error_handler;
+ struct list_head instances;
+ struct platform_inst_fw_cap inst_fw_caps[INST_FW_CAP_MAX];
+};
+
+int iris_core_init(struct iris_core *core);
+void iris_core_deinit(struct iris_core *core);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
new file mode 100644
index 000000000000..b690578256d5
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_ctrls.h"
+#include "iris_instance.h"
+
+static inline bool iris_valid_cap_id(enum platform_inst_fw_cap_type cap_id)
+{
+ return cap_id >= 1 && cap_id < INST_FW_CAP_MAX;
+}
+
+static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ return DEBLOCK;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ return PROFILE;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ return LEVEL;
+ default:
+ return INST_FW_CAP_MAX;
+ }
+}
+
+static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
+{
+ if (!iris_valid_cap_id(cap_id))
+ return 0;
+
+ switch (cap_id) {
+ case DEBLOCK:
+ return V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER;
+ case PROFILE:
+ return V4L2_CID_MPEG_VIDEO_H264_PROFILE;
+ case LEVEL:
+ return V4L2_CID_MPEG_VIDEO_H264_LEVEL;
+ default:
+ return 0;
+ }
+}
+
+static int iris_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct iris_inst *inst = container_of(ctrl->handler, struct iris_inst, ctrl_handler);
+ enum platform_inst_fw_cap_type cap_id;
+ struct platform_inst_fw_cap *cap;
+ struct vb2_queue *q;
+
+ cap = &inst->fw_caps[0];
+ cap_id = iris_get_cap_id(ctrl->id);
+ if (!iris_valid_cap_id(cap_id))
+ return -EINVAL;
+
+ q = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ if (vb2_is_streaming(q) &&
+ (!(inst->fw_caps[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)))
+ return -EINVAL;
+
+ cap[cap_id].flags |= CAP_FLAG_CLIENT_SET;
+
+ inst->fw_caps[cap_id].value = ctrl->val;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops iris_ctrl_ops = {
+ .s_ctrl = iris_vdec_op_s_ctrl,
+};
+
+int iris_ctrls_init(struct iris_inst *inst)
+{
+ struct platform_inst_fw_cap *cap = &inst->fw_caps[0];
+ u32 num_ctrls = 0, ctrl_idx = 0, idx = 0;
+ u32 v4l2_id;
+ int ret;
+
+ for (idx = 1; idx < INST_FW_CAP_MAX; idx++) {
+ if (iris_get_v4l2_id(cap[idx].cap_id))
+ num_ctrls++;
+ }
+ if (!num_ctrls)
+ return -EINVAL;
+
+ /* Adding 1 to num_ctrls to include V4L2_CID_MIN_BUFFERS_FOR_CAPTURE */
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, num_ctrls + 1);
+ if (ret)
+ return ret;
+
+ for (idx = 1; idx < INST_FW_CAP_MAX; idx++) {
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_id = iris_get_v4l2_id(cap[idx].cap_id);
+ if (!v4l2_id)
+ continue;
+
+ if (ctrl_idx >= num_ctrls) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (cap[idx].flags & CAP_FLAG_MENU) {
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler,
+ &iris_ctrl_ops,
+ v4l2_id,
+ cap[idx].max,
+ ~(cap[idx].step_or_mask),
+ cap[idx].value);
+ } else {
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
+ &iris_ctrl_ops,
+ v4l2_id,
+ cap[idx].min,
+ cap[idx].max,
+ cap[idx].step_or_mask,
+ cap[idx].value);
+ }
+ if (!ctrl) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ctrl_idx++;
+ }
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, NULL,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 4);
+
+ ret = inst->ctrl_handler.error;
+ if (ret)
+ goto error;
+
+ return 0;
+error:
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+
+ return ret;
+}
+
+void iris_session_init_caps(struct iris_core *core)
+{
+ struct platform_inst_fw_cap *caps;
+ u32 i, num_cap, cap_id;
+
+ caps = core->iris_platform_data->inst_fw_caps;
+ num_cap = core->iris_platform_data->inst_fw_caps_size;
+
+ for (i = 0; i < num_cap; i++) {
+ cap_id = caps[i].cap_id;
+ if (!iris_valid_cap_id(cap_id))
+ continue;
+
+ core->inst_fw_caps[cap_id].cap_id = caps[i].cap_id;
+ core->inst_fw_caps[cap_id].min = caps[i].min;
+ core->inst_fw_caps[cap_id].max = caps[i].max;
+ core->inst_fw_caps[cap_id].step_or_mask = caps[i].step_or_mask;
+ core->inst_fw_caps[cap_id].value = caps[i].value;
+ core->inst_fw_caps[cap_id].flags = caps[i].flags;
+ core->inst_fw_caps[cap_id].hfi_id = caps[i].hfi_id;
+ }
+}
+
+static u32 iris_get_port_info(struct iris_inst *inst,
+ enum platform_inst_fw_cap_type cap_id)
+{
+ if (inst->fw_caps[cap_id].flags & CAP_FLAG_INPUT_PORT)
+ return HFI_PORT_BITSTREAM;
+ else if (inst->fw_caps[cap_id].flags & CAP_FLAG_OUTPUT_PORT)
+ return HFI_PORT_RAW;
+
+ return HFI_PORT_NONE;
+}
+
+int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 hfi_value = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32_ENUM,
+ &hfi_value, sizeof(u32));
+}
+
+int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 hfi_value = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &hfi_value, sizeof(u32));
+}
+
+int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 height = inp_f->fmt.pix_mp.height;
+ u32 width = inp_f->fmt.pix_mp.width;
+ u32 work_mode = STAGE_2;
+
+ if (iris_res_is_less_than(width, height, 1280, 720))
+ work_mode = STAGE_1;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &work_mode, sizeof(u32));
+}
+
+int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 work_route = inst->fw_caps[PIPE].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &work_route, sizeof(u32));
+}
+
+int iris_set_properties(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ struct platform_inst_fw_cap *cap;
+ int ret;
+ u32 i;
+
+ ret = hfi_ops->session_set_config_params(inst, plane);
+ if (ret)
+ return ret;
+
+ for (i = 1; i < INST_FW_CAP_MAX; i++) {
+ cap = &inst->fw_caps[i];
+ if (!iris_valid_cap_id(cap->cap_id))
+ continue;
+
+ if (cap->cap_id && cap->set)
+ cap->set(inst, i);
+ }
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h
new file mode 100644
index 000000000000..9b5741868933
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_CTRLS_H__
+#define __IRIS_CTRLS_H__
+
+#include "iris_platform_common.h"
+
+struct iris_core;
+struct iris_inst;
+
+int iris_ctrls_init(struct iris_inst *inst);
+void iris_session_init_caps(struct iris_core *core);
+int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_properties(struct iris_inst *inst, u32 plane);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
new file mode 100644
index 000000000000..f1b5cd56db32
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/firmware.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+#include "iris_core.h"
+#include "iris_firmware.h"
+
+#define MAX_FIRMWARE_NAME_SIZE 128
+
+static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
+{
+ u32 pas_id = core->iris_platform_data->pas_id;
+ const struct firmware *firmware = NULL;
+ struct device *dev = core->dev;
+ struct reserved_mem *rmem;
+ struct device_node *node;
+ phys_addr_t mem_phys;
+ size_t res_size;
+ ssize_t fw_size;
+ void *mem_virt;
+ int ret;
+
+ if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4)
+ return -EINVAL;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!node)
+ return -EINVAL;
+
+ rmem = of_reserved_mem_lookup(node);
+ of_node_put(node);
+ if (!rmem)
+ return -EINVAL;
+
+ mem_phys = rmem->base;
+ res_size = rmem->size;
+
+ ret = request_firmware(&firmware, fw_name, dev);
+ if (ret)
+ return ret;
+
+ fw_size = qcom_mdt_get_size(firmware);
+ if (fw_size < 0 || res_size < (size_t)fw_size) {
+ ret = -EINVAL;
+ goto err_release_fw;
+ }
+
+ mem_virt = memremap(mem_phys, res_size, MEMREMAP_WC);
+ if (!mem_virt) {
+ ret = -ENOMEM;
+ goto err_release_fw;
+ }
+
+ ret = qcom_mdt_load(dev, firmware, fw_name,
+ pas_id, mem_virt, mem_phys, res_size, NULL);
+ if (ret)
+ goto err_mem_unmap;
+
+ ret = qcom_scm_pas_auth_and_reset(pas_id);
+ if (ret)
+ goto err_mem_unmap;
+
+ return ret;
+
+err_mem_unmap:
+ memunmap(mem_virt);
+err_release_fw:
+ release_firmware(firmware);
+
+ return ret;
+}
+
+int iris_fw_load(struct iris_core *core)
+{
+ struct tz_cp_config *cp_config = core->iris_platform_data->tz_cp_config_data;
+ const char *fwpath = NULL;
+ int ret;
+
+ ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0,
+ &fwpath);
+ if (ret)
+ fwpath = core->iris_platform_data->fwname;
+
+ ret = iris_load_fw_to_memory(core, fwpath);
+ if (ret) {
+ dev_err(core->dev, "firmware download failed\n");
+ return -ENOMEM;
+ }
+
+ ret = qcom_scm_mem_protect_video_var(cp_config->cp_start,
+ cp_config->cp_size,
+ cp_config->cp_nonpixel_start,
+ cp_config->cp_nonpixel_size);
+ if (ret) {
+ dev_err(core->dev, "protect memory failed\n");
+ qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+ return ret;
+ }
+
+ return ret;
+}
+
+int iris_fw_unload(struct iris_core *core)
+{
+ return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+}
+
+int iris_set_hw_state(struct iris_core *core, bool resume)
+{
+ return qcom_scm_set_remote_state(resume, 0);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.h b/drivers/media/platform/qcom/iris/iris_firmware.h
new file mode 100644
index 000000000000..e833ecd34887
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_firmware.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_FIRMWARE_H__
+#define __IRIS_FIRMWARE_H__
+
+struct iris_core;
+
+int iris_fw_load(struct iris_core *core);
+int iris_fw_unload(struct iris_core *core);
+int iris_set_hw_state(struct iris_core *core, bool resume);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c
new file mode 100644
index 000000000000..92112eb16c11
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "iris_firmware.h"
+#include "iris_core.h"
+#include "iris_hfi_common.h"
+#include "iris_vpu_common.h"
+
+u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries)
+{
+ switch (hfi_primaries) {
+ case HFI_PRIMARIES_RESERVED:
+ return V4L2_COLORSPACE_DEFAULT;
+ case HFI_PRIMARIES_BT709:
+ return V4L2_COLORSPACE_REC709;
+ case HFI_PRIMARIES_BT470_SYSTEM_M:
+ return V4L2_COLORSPACE_470_SYSTEM_M;
+ case HFI_PRIMARIES_BT470_SYSTEM_BG:
+ return V4L2_COLORSPACE_470_SYSTEM_BG;
+ case HFI_PRIMARIES_BT601_525:
+ return V4L2_COLORSPACE_SMPTE170M;
+ case HFI_PRIMARIES_SMPTE_ST240M:
+ return V4L2_COLORSPACE_SMPTE240M;
+ case HFI_PRIMARIES_BT2020:
+ return V4L2_COLORSPACE_BT2020;
+ case V4L2_COLORSPACE_DCI_P3:
+ return HFI_PRIMARIES_SMPTE_RP431_2;
+ default:
+ return V4L2_COLORSPACE_DEFAULT;
+ }
+}
+
+u32 iris_hfi_get_v4l2_transfer_char(u32 hfi_characterstics)
+{
+ switch (hfi_characterstics) {
+ case HFI_TRANSFER_RESERVED:
+ return V4L2_XFER_FUNC_DEFAULT;
+ case HFI_TRANSFER_BT709:
+ return V4L2_XFER_FUNC_709;
+ case HFI_TRANSFER_SMPTE_ST240M:
+ return V4L2_XFER_FUNC_SMPTE240M;
+ case HFI_TRANSFER_SRGB_SYCC:
+ return V4L2_XFER_FUNC_SRGB;
+ case HFI_TRANSFER_SMPTE_ST2084_PQ:
+ return V4L2_XFER_FUNC_SMPTE2084;
+ default:
+ return V4L2_XFER_FUNC_DEFAULT;
+ }
+}
+
+u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients)
+{
+ switch (hfi_coefficients) {
+ case HFI_MATRIX_COEFF_RESERVED:
+ return V4L2_YCBCR_ENC_DEFAULT;
+ case HFI_MATRIX_COEFF_BT709:
+ return V4L2_YCBCR_ENC_709;
+ case HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625:
+ return V4L2_YCBCR_ENC_XV601;
+ case HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625:
+ return V4L2_YCBCR_ENC_601;
+ case HFI_MATRIX_COEFF_SMPTE_ST240:
+ return V4L2_YCBCR_ENC_SMPTE240M;
+ case HFI_MATRIX_COEFF_BT2020_NON_CONSTANT:
+ return V4L2_YCBCR_ENC_BT2020;
+ case HFI_MATRIX_COEFF_BT2020_CONSTANT:
+ return V4L2_YCBCR_ENC_BT2020_CONST_LUM;
+ default:
+ return V4L2_YCBCR_ENC_DEFAULT;
+ }
+}
+
+int iris_hfi_core_init(struct iris_core *core)
+{
+ const struct iris_hfi_command_ops *hfi_ops = core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->sys_init(core);
+ if (ret)
+ return ret;
+
+ ret = hfi_ops->sys_image_version(core);
+ if (ret)
+ return ret;
+
+ return hfi_ops->sys_interframe_powercollapse(core);
+}
+
+irqreturn_t iris_hfi_isr(int irq, void *data)
+{
+ disable_irq_nosync(irq);
+
+ return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t iris_hfi_isr_handler(int irq, void *data)
+{
+ struct iris_core *core = data;
+
+ if (!core)
+ return IRQ_NONE;
+
+ mutex_lock(&core->lock);
+ pm_runtime_mark_last_busy(core->dev);
+ iris_vpu_clear_interrupt(core);
+ mutex_unlock(&core->lock);
+
+ core->hfi_response_ops->hfi_response_handler(core);
+
+ if (!iris_vpu_watchdog(core, core->intr_status))
+ enable_irq(irq);
+
+ return IRQ_HANDLED;
+}
+
+int iris_hfi_pm_suspend(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_vpu_prepare_pc(core);
+ if (ret) {
+ pm_runtime_mark_last_busy(core->dev);
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ ret = iris_set_hw_state(core, false);
+ if (ret)
+ goto error;
+
+ iris_vpu_power_off(core);
+
+ return 0;
+
+error:
+ dev_err(core->dev, "failed to suspend\n");
+
+ return ret;
+}
+
+int iris_hfi_pm_resume(struct iris_core *core)
+{
+ const struct iris_hfi_command_ops *ops = core->hfi_ops;
+ int ret;
+
+ ret = iris_vpu_power_on(core);
+ if (ret)
+ goto error;
+
+ ret = iris_set_hw_state(core, true);
+ if (ret)
+ goto err_power_off;
+
+ ret = iris_vpu_boot_firmware(core);
+ if (ret)
+ goto err_suspend_hw;
+
+ ret = ops->sys_interframe_powercollapse(core);
+ if (ret)
+ goto err_suspend_hw;
+
+ return 0;
+
+err_suspend_hw:
+ iris_set_hw_state(core, false);
+err_power_off:
+ iris_vpu_power_off(core);
+error:
+ dev_err(core->dev, "failed to resume\n");
+
+ return -EBUSY;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h
new file mode 100644
index 000000000000..b2c541367fc6
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_COMMON_H__
+#define __IRIS_HFI_COMMON_H__
+
+#include <linux/types.h>
+#include <media/v4l2-device.h>
+
+#include "iris_buffer.h"
+
+struct iris_inst;
+struct iris_core;
+
+enum hfi_packet_port_type {
+ HFI_PORT_NONE = 0x00000000,
+ HFI_PORT_BITSTREAM = 0x00000001,
+ HFI_PORT_RAW = 0x00000002,
+};
+
+enum hfi_packet_payload_info {
+ HFI_PAYLOAD_NONE = 0x00000000,
+ HFI_PAYLOAD_U32 = 0x00000001,
+ HFI_PAYLOAD_S32 = 0x00000002,
+ HFI_PAYLOAD_U64 = 0x00000003,
+ HFI_PAYLOAD_S64 = 0x00000004,
+ HFI_PAYLOAD_STRUCTURE = 0x00000005,
+ HFI_PAYLOAD_BLOB = 0x00000006,
+ HFI_PAYLOAD_STRING = 0x00000007,
+ HFI_PAYLOAD_Q16 = 0x00000008,
+ HFI_PAYLOAD_U32_ENUM = 0x00000009,
+ HFI_PAYLOAD_32_PACKED = 0x0000000a,
+ HFI_PAYLOAD_U32_ARRAY = 0x0000000b,
+ HFI_PAYLOAD_S32_ARRAY = 0x0000000c,
+ HFI_PAYLOAD_64_PACKED = 0x0000000d,
+};
+
+enum hfi_packet_host_flags {
+ HFI_HOST_FLAGS_NONE = 0x00000000,
+ HFI_HOST_FLAGS_INTR_REQUIRED = 0x00000001,
+ HFI_HOST_FLAGS_RESPONSE_REQUIRED = 0x00000002,
+ HFI_HOST_FLAGS_NON_DISCARDABLE = 0x00000004,
+ HFI_HOST_FLAGS_GET_PROPERTY = 0x00000008,
+};
+
+enum hfi_color_primaries {
+ HFI_PRIMARIES_RESERVED = 0,
+ HFI_PRIMARIES_BT709 = 1,
+ HFI_PRIMARIES_UNSPECIFIED = 2,
+ HFI_PRIMARIES_BT470_SYSTEM_M = 4,
+ HFI_PRIMARIES_BT470_SYSTEM_BG = 5,
+ HFI_PRIMARIES_BT601_525 = 6,
+ HFI_PRIMARIES_SMPTE_ST240M = 7,
+ HFI_PRIMARIES_GENERIC_FILM = 8,
+ HFI_PRIMARIES_BT2020 = 9,
+ HFI_PRIMARIES_SMPTE_ST428_1 = 10,
+ HFI_PRIMARIES_SMPTE_RP431_2 = 11,
+ HFI_PRIMARIES_SMPTE_EG431_1 = 12,
+ HFI_PRIMARIES_SMPTE_EBU_TECH = 22,
+};
+
+enum hfi_transfer_characteristics {
+ HFI_TRANSFER_RESERVED = 0,
+ HFI_TRANSFER_BT709 = 1,
+ HFI_TRANSFER_UNSPECIFIED = 2,
+ HFI_TRANSFER_BT470_SYSTEM_M = 4,
+ HFI_TRANSFER_BT470_SYSTEM_BG = 5,
+ HFI_TRANSFER_BT601_525_OR_625 = 6,
+ HFI_TRANSFER_SMPTE_ST240M = 7,
+ HFI_TRANSFER_LINEAR = 8,
+ HFI_TRANSFER_LOG_100_1 = 9,
+ HFI_TRANSFER_LOG_SQRT = 10,
+ HFI_TRANSFER_XVYCC = 11,
+ HFI_TRANSFER_BT1361_0 = 12,
+ HFI_TRANSFER_SRGB_SYCC = 13,
+ HFI_TRANSFER_BT2020_14 = 14,
+ HFI_TRANSFER_BT2020_15 = 15,
+ HFI_TRANSFER_SMPTE_ST2084_PQ = 16,
+ HFI_TRANSFER_SMPTE_ST428_1 = 17,
+ HFI_TRANSFER_BT2100_2_HLG = 18,
+};
+
+enum hfi_matrix_coefficients {
+ HFI_MATRIX_COEFF_SRGB_SMPTE_ST428_1 = 0,
+ HFI_MATRIX_COEFF_BT709 = 1,
+ HFI_MATRIX_COEFF_UNSPECIFIED = 2,
+ HFI_MATRIX_COEFF_RESERVED = 3,
+ HFI_MATRIX_COEFF_FCC_TITLE_47 = 4,
+ HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625 = 5,
+ HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625 = 6,
+ HFI_MATRIX_COEFF_SMPTE_ST240 = 7,
+ HFI_MATRIX_COEFF_YCGCO = 8,
+ HFI_MATRIX_COEFF_BT2020_NON_CONSTANT = 9,
+ HFI_MATRIX_COEFF_BT2020_CONSTANT = 10,
+ HFI_MATRIX_COEFF_SMPTE_ST2085 = 11,
+ HFI_MATRIX_COEFF_SMPTE_CHROM_DERV_NON_CONSTANT = 12,
+ HFI_MATRIX_COEFF_SMPTE_CHROM_DERV_CONSTANT = 13,
+ HFI_MATRIX_COEFF_BT2100 = 14,
+};
+
+struct iris_hfi_prop_type_handle {
+ u32 type;
+ int (*handle)(struct iris_inst *inst);
+};
+
+struct iris_hfi_command_ops {
+ int (*sys_init)(struct iris_core *core);
+ int (*sys_image_version)(struct iris_core *core);
+ int (*sys_interframe_powercollapse)(struct iris_core *core);
+ int (*sys_pc_prep)(struct iris_core *core);
+ int (*session_set_config_params)(struct iris_inst *inst, u32 plane);
+ int (*session_set_property)(struct iris_inst *inst,
+ u32 packet_type, u32 flag, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size);
+ int (*session_open)(struct iris_inst *inst);
+ int (*session_start)(struct iris_inst *inst, u32 plane);
+ int (*session_queue_buf)(struct iris_inst *inst, struct iris_buffer *buffer);
+ int (*session_release_buf)(struct iris_inst *inst, struct iris_buffer *buffer);
+ int (*session_pause)(struct iris_inst *inst, u32 plane);
+ int (*session_resume_drc)(struct iris_inst *inst, u32 plane);
+ int (*session_stop)(struct iris_inst *inst, u32 plane);
+ int (*session_drain)(struct iris_inst *inst, u32 plane);
+ int (*session_resume_drain)(struct iris_inst *inst, u32 plane);
+ int (*session_close)(struct iris_inst *inst);
+};
+
+struct iris_hfi_response_ops {
+ void (*hfi_response_handler)(struct iris_core *core);
+};
+
+struct hfi_subscription_params {
+ u32 bitstream_resolution;
+ u32 crop_offsets[2];
+ u32 bit_depth;
+ u32 coded_frames;
+ u32 fw_min_count;
+ u32 pic_order_cnt;
+ u32 color_info;
+ u32 profile;
+ u32 level;
+};
+
+u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries);
+u32 iris_hfi_get_v4l2_transfer_char(u32 hfi_characterstics);
+u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients);
+int iris_hfi_core_init(struct iris_core *core);
+int iris_hfi_pm_suspend(struct iris_core *core);
+int iris_hfi_pm_resume(struct iris_core *core);
+
+irqreturn_t iris_hfi_isr(int irq, void *data);
+irqreturn_t iris_hfi_isr_handler(int irq, void *data);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h
new file mode 100644
index 000000000000..19b8e9054a75
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN1_H__
+#define __IRIS_HFI_GEN1_H__
+
+struct iris_core;
+struct iris_inst;
+
+void iris_hfi_gen1_command_ops_init(struct iris_core *core);
+void iris_hfi_gen1_response_ops_init(struct iris_core *core);
+struct iris_inst *iris_hfi_gen1_get_instance(void);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
new file mode 100644
index 000000000000..64f887d9a17d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -0,0 +1,826 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_hfi_gen1.h"
+#include "iris_hfi_gen1_defines.h"
+#include "iris_instance.h"
+#include "iris_vpu_buffer.h"
+
+static u32 iris_hfi_gen1_buf_type_from_driver(enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return HFI_BUFFER_INPUT;
+ case BUF_OUTPUT:
+ return HFI_BUFFER_OUTPUT;
+ case BUF_PERSIST:
+ return HFI_BUFFER_INTERNAL_PERSIST_1;
+ case BUF_BIN:
+ return HFI_BUFFER_INTERNAL_SCRATCH;
+ case BUF_SCRATCH_1:
+ return HFI_BUFFER_INTERNAL_SCRATCH_1;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int iris_hfi_gen1_sys_init(struct iris_core *core)
+{
+ struct hfi_sys_init_pkt sys_init_pkt;
+
+ sys_init_pkt.hdr.size = sizeof(sys_init_pkt);
+ sys_init_pkt.hdr.pkt_type = HFI_CMD_SYS_INIT;
+ sys_init_pkt.arch_type = HFI_VIDEO_ARCH_OX;
+
+ return iris_hfi_queue_cmd_write_locked(core, &sys_init_pkt, sys_init_pkt.hdr.size);
+}
+
+static int iris_hfi_gen1_sys_image_version(struct iris_core *core)
+{
+ struct hfi_sys_get_property_pkt packet;
+
+ packet.hdr.size = sizeof(packet);
+ packet.hdr.pkt_type = HFI_CMD_SYS_GET_PROPERTY;
+ packet.num_properties = 1;
+ packet.data = HFI_PROPERTY_SYS_IMAGE_VERSION;
+
+ return iris_hfi_queue_cmd_write_locked(core, &packet, packet.hdr.size);
+}
+
+static int iris_hfi_gen1_sys_interframe_powercollapse(struct iris_core *core)
+{
+ struct hfi_sys_set_property_pkt *pkt;
+ struct hfi_enable *hfi;
+ u32 packet_size;
+ int ret;
+
+ packet_size = struct_size(pkt, data, 1) + sizeof(*hfi);
+ pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+
+ hfi = (struct hfi_enable *)&pkt->data[1];
+
+ pkt->hdr.size = packet_size;
+ pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL;
+ hfi->enable = true;
+
+ ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt->hdr.size);
+ kfree(pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_sys_pc_prep(struct iris_core *core)
+{
+ struct hfi_sys_pc_prep_pkt pkt;
+
+ pkt.hdr.size = sizeof(struct hfi_sys_pc_prep_pkt);
+ pkt.hdr.pkt_type = HFI_CMD_SYS_PC_PREP;
+
+ return iris_hfi_queue_cmd_write_locked(core, &pkt, pkt.hdr.size);
+}
+
+static int iris_hfi_gen1_session_open(struct iris_inst *inst)
+{
+ struct hfi_session_open_pkt packet;
+ int ret;
+
+ if (inst->state != IRIS_INST_DEINIT)
+ return -EALREADY;
+
+ packet.shdr.hdr.size = sizeof(struct hfi_session_open_pkt);
+ packet.shdr.hdr.pkt_type = HFI_CMD_SYS_SESSION_INIT;
+ packet.shdr.session_id = inst->session_id;
+ packet.session_domain = HFI_SESSION_TYPE_DEC;
+ packet.session_codec = HFI_VIDEO_CODEC_H264;
+
+ reinit_completion(&inst->completion);
+
+ ret = iris_hfi_queue_cmd_write(inst->core, &packet, packet.shdr.hdr.size);
+ if (ret)
+ return ret;
+
+ return iris_wait_for_session_response(inst, false);
+}
+
+static void iris_hfi_gen1_packet_session_cmd(struct iris_inst *inst,
+ struct hfi_session_pkt *packet,
+ u32 ptype)
+{
+ packet->shdr.hdr.size = sizeof(*packet);
+ packet->shdr.hdr.pkt_type = ptype;
+ packet->shdr.session_id = inst->session_id;
+}
+
+static int iris_hfi_gen1_session_close(struct iris_inst *inst)
+{
+ struct hfi_session_pkt packet;
+
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SYS_SESSION_END);
+
+ return iris_hfi_queue_cmd_write(inst->core, &packet, packet.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_session_start(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ struct hfi_session_pkt packet;
+ int ret;
+
+ if (!V4L2_TYPE_IS_OUTPUT(plane))
+ return 0;
+
+ if (inst->sub_state & IRIS_INST_SUB_LOAD_RESOURCES)
+ return 0;
+
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_LOAD_RESOURCES);
+
+ ret = iris_hfi_queue_cmd_write(core, &packet, packet.shdr.hdr.size);
+ if (ret)
+ return ret;
+
+ ret = iris_wait_for_session_response(inst, false);
+ if (ret)
+ return ret;
+
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_START);
+
+ ret = iris_hfi_queue_cmd_write(core, &packet, packet.shdr.hdr.size);
+ if (ret)
+ return ret;
+
+ ret = iris_wait_for_session_response(inst, false);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_LOAD_RESOURCES);
+}
+
+static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane)
+{
+ struct hfi_session_flush_pkt flush_pkt;
+ struct iris_core *core = inst->core;
+ struct hfi_session_pkt pkt;
+ u32 flush_type = 0;
+ int ret = 0;
+
+ if ((V4L2_TYPE_IS_OUTPUT(plane) &&
+ inst->state == IRIS_INST_INPUT_STREAMING) ||
+ (V4L2_TYPE_IS_CAPTURE(plane) &&
+ inst->state == IRIS_INST_OUTPUT_STREAMING) ||
+ inst->state == IRIS_INST_ERROR) {
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &pkt, HFI_CMD_SESSION_STOP);
+ ret = iris_hfi_queue_cmd_write(core, &pkt, pkt.shdr.hdr.size);
+ if (!ret)
+ ret = iris_wait_for_session_response(inst, false);
+
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &pkt, HFI_CMD_SESSION_RELEASE_RESOURCES);
+ ret = iris_hfi_queue_cmd_write(core, &pkt, pkt.shdr.hdr.size);
+ if (!ret)
+ ret = iris_wait_for_session_response(inst, false);
+
+ iris_inst_change_sub_state(inst, IRIS_INST_SUB_LOAD_RESOURCES, 0);
+
+ iris_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ VB2_BUF_STATE_ERROR);
+ iris_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ VB2_BUF_STATE_ERROR);
+ } else if (inst->state == IRIS_INST_STREAMING) {
+ if (V4L2_TYPE_IS_OUTPUT(plane))
+ flush_type = HFI_FLUSH_ALL;
+ else if (V4L2_TYPE_IS_CAPTURE(plane))
+ flush_type = HFI_FLUSH_OUTPUT;
+
+ reinit_completion(&inst->flush_completion);
+
+ flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
+ flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+ flush_pkt.shdr.session_id = inst->session_id;
+ flush_pkt.flush_type = flush_type;
+
+ ret = iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ if (!ret)
+ ret = iris_wait_for_session_response(inst, true);
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_continue(struct iris_inst *inst, u32 plane)
+{
+ struct hfi_session_pkt packet;
+
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_CONTINUE);
+
+ return iris_hfi_queue_cmd_write(inst->core, &packet, packet.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_queue_input_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_empty_buffer_compressed_pkt ip_pkt;
+
+ ip_pkt.shdr.hdr.size = sizeof(struct hfi_session_empty_buffer_compressed_pkt);
+ ip_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+ ip_pkt.shdr.session_id = inst->session_id;
+ ip_pkt.time_stamp_hi = upper_32_bits(buf->timestamp);
+ ip_pkt.time_stamp_lo = lower_32_bits(buf->timestamp);
+ ip_pkt.flags = buf->flags;
+ ip_pkt.mark_target = 0;
+ ip_pkt.mark_data = 0;
+ ip_pkt.offset = buf->data_offset;
+ ip_pkt.alloc_len = buf->buffer_size;
+ ip_pkt.filled_len = buf->data_size;
+ ip_pkt.input_tag = buf->index;
+ ip_pkt.packet_buffer = buf->device_addr;
+
+ return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_queue_output_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_fill_buffer_pkt op_pkt;
+
+ op_pkt.shdr.hdr.size = sizeof(struct hfi_session_fill_buffer_pkt);
+ op_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FILL_BUFFER;
+ op_pkt.shdr.session_id = inst->session_id;
+ op_pkt.output_tag = buf->index;
+ op_pkt.packet_buffer = buf->device_addr;
+ op_pkt.extradata_buffer = 0;
+ op_pkt.alloc_len = buf->buffer_size;
+ op_pkt.filled_len = buf->data_size;
+ op_pkt.offset = buf->data_offset;
+ op_pkt.data = 0;
+
+ if (buf->type == BUF_OUTPUT && iris_split_mode_enabled(inst))
+ op_pkt.stream_id = 1;
+ else
+ op_pkt.stream_id = 0;
+
+ return iris_hfi_queue_cmd_write(inst->core, &op_pkt, op_pkt.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_queue_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_set_buffers_pkt *int_pkt;
+ u32 buffer_type, i;
+ u32 packet_size;
+ int ret;
+
+ packet_size = struct_size(int_pkt, buffer_info, 1);
+ int_pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!int_pkt)
+ return -ENOMEM;
+
+ int_pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_BUFFERS;
+ int_pkt->shdr.session_id = inst->session_id;
+ int_pkt->buffer_size = buf->buffer_size;
+ int_pkt->min_buffer_size = buf->buffer_size;
+ int_pkt->num_buffers = 1;
+ int_pkt->extradata_size = 0;
+ int_pkt->shdr.hdr.size = packet_size;
+ for (i = 0; i < int_pkt->num_buffers; i++)
+ int_pkt->buffer_info[i] = buf->device_addr;
+ buffer_type = iris_hfi_gen1_buf_type_from_driver(buf->type);
+ if (buffer_type == -EINVAL) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ int_pkt->buffer_type = buffer_type;
+ ret = iris_hfi_queue_cmd_write(inst->core, int_pkt, int_pkt->shdr.hdr.size);
+
+exit:
+ kfree(int_pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ switch (buf->type) {
+ case BUF_INPUT:
+ return iris_hfi_gen1_queue_input_buffer(inst, buf);
+ case BUF_OUTPUT:
+ case BUF_DPB:
+ return iris_hfi_gen1_queue_output_buffer(inst, buf);
+ case BUF_PERSIST:
+ case BUF_BIN:
+ case BUF_SCRATCH_1:
+ return iris_hfi_gen1_queue_internal_buffer(inst, buf);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int iris_hfi_gen1_session_unset_buffers(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_release_buffer_pkt *pkt;
+ u32 packet_size, buffer_type, i;
+ int ret;
+
+ buffer_type = iris_hfi_gen1_buf_type_from_driver(buf->type);
+ if (buffer_type == -EINVAL)
+ return -EINVAL;
+
+ if (buffer_type == HFI_BUFFER_INPUT)
+ return 0;
+
+ packet_size = sizeof(*pkt) + sizeof(struct hfi_buffer_info);
+ pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_RELEASE_BUFFERS;
+ pkt->shdr.session_id = inst->session_id;
+ pkt->buffer_size = buf->buffer_size;
+ pkt->num_buffers = 1;
+
+ if (buffer_type == HFI_BUFFER_OUTPUT ||
+ buffer_type == HFI_BUFFER_OUTPUT2) {
+ struct hfi_buffer_info *bi;
+
+ bi = (struct hfi_buffer_info *)pkt->buffer_info;
+ for (i = 0; i < pkt->num_buffers; i++) {
+ bi->buffer_addr = buf->device_addr;
+ bi->extradata_addr = 0;
+ }
+ pkt->shdr.hdr.size = packet_size;
+ } else {
+ for (i = 0; i < pkt->num_buffers; i++)
+ pkt->buffer_info[i] = buf->device_addr;
+ pkt->extradata_size = 0;
+ pkt->shdr.hdr.size =
+ sizeof(struct hfi_session_set_buffers_pkt) +
+ ((pkt->num_buffers) * sizeof(u32));
+ }
+
+ pkt->response_req = true;
+ pkt->buffer_type = buffer_type;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, pkt, pkt->shdr.hdr.size);
+ if (ret)
+ goto exit;
+
+ ret = iris_wait_for_session_response(inst, false);
+
+exit:
+ kfree(pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_drain(struct iris_inst *inst, u32 plane)
+{
+ struct hfi_session_empty_buffer_compressed_pkt ip_pkt = {0};
+
+ ip_pkt.shdr.hdr.size = sizeof(struct hfi_session_empty_buffer_compressed_pkt);
+ ip_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+ ip_pkt.shdr.session_id = inst->session_id;
+ ip_pkt.flags = HFI_BUFFERFLAG_EOS;
+
+ return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size);
+}
+
+static int
+iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *packet,
+ struct iris_inst *inst, u32 ptype, void *pdata)
+{
+ void *prop_data = &packet->data[1];
+
+ packet->shdr.hdr.size = sizeof(*packet);
+ packet->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY;
+ packet->shdr.session_id = inst->session_id;
+ packet->num_properties = 1;
+ packet->data[0] = ptype;
+
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_FRAME_SIZE: {
+ struct hfi_framesize *in = pdata, *fsize = prop_data;
+
+ fsize->buffer_type = in->buffer_type;
+ fsize->height = in->height;
+ fsize->width = in->width;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*fsize);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: {
+ struct hfi_videocores_usage_type *in = pdata, *cu = prop_data;
+
+ cu->video_core_enable_mask = in->video_core_enable_mask;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*cu);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: {
+ struct hfi_uncompressed_format_select *in = pdata;
+ struct hfi_uncompressed_format_select *hfi = prop_data;
+
+ hfi->buffer_type = in->buffer_type;
+ hfi->format = in->format;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*hfi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: {
+ struct hfi_uncompressed_plane_actual_constraints_info *info = prop_data;
+
+ info->buffer_type = HFI_BUFFER_OUTPUT2;
+ info->num_planes = 2;
+ info->plane_format[0].stride_multiples = 128;
+ info->plane_format[0].max_stride = 8192;
+ info->plane_format[0].min_plane_buffer_height_multiple = 32;
+ info->plane_format[0].buffer_alignment = 256;
+ if (info->num_planes > 1) {
+ info->plane_format[1].stride_multiples = 128;
+ info->plane_format[1].max_stride = 8192;
+ info->plane_format[1].min_plane_buffer_height_multiple = 16;
+ info->plane_format[1].buffer_alignment = 256;
+ }
+
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*info);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: {
+ struct hfi_buffer_count_actual *in = pdata;
+ struct hfi_buffer_count_actual *count = prop_data;
+
+ count->type = in->type;
+ count->count_actual = in->count_actual;
+ count->count_min_host = in->count_min_host;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*count);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: {
+ struct hfi_multi_stream *in = pdata;
+ struct hfi_multi_stream *multi = prop_data;
+
+ multi->buffer_type = in->buffer_type;
+ multi->enable = in->enable;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*multi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: {
+ struct hfi_buffer_size_actual *in = pdata, *sz = prop_data;
+
+ sz->size = in->size;
+ sz->type = in->type;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*sz);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_WORK_ROUTE: {
+ struct hfi_video_work_route *wr = prop_data;
+ u32 *in = pdata;
+
+ wr->video_work_route = *in;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*wr);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_WORK_MODE: {
+ struct hfi_video_work_mode *wm = prop_data;
+ u32 *in = pdata;
+
+ wm->video_work_mode = *in;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: {
+ struct hfi_enable *en = prop_data;
+ u32 *in = pdata;
+
+ en->enable = *in;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hfi_gen1_set_property(struct iris_inst *inst, u32 packet_type,
+ void *payload, u32 payload_size)
+{
+ struct hfi_session_set_property_pkt *pkt;
+ u32 packet_size;
+ int ret;
+
+ packet_size = sizeof(*pkt) + sizeof(u32) + payload_size;
+ pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+
+ ret = iris_hfi_gen1_packet_session_set_property(pkt, inst, packet_type, payload);
+ if (ret == -EOPNOTSUPP) {
+ ret = 0;
+ goto exit;
+ }
+ if (ret)
+ goto exit;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, pkt, pkt->shdr.hdr.size);
+
+exit:
+ kfree(pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_set_property(struct iris_inst *inst, u32 packet_type,
+ u32 flag, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size)
+{
+ return hfi_gen1_set_property(inst, packet_type, payload, payload_size);
+}
+
+static int iris_hfi_gen1_set_resolution(struct iris_inst *inst)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE;
+ struct hfi_framesize fs;
+ int ret;
+
+ fs.buffer_type = HFI_BUFFER_INPUT;
+ fs.width = inst->fmt_src->fmt.pix_mp.width;
+ fs.height = inst->fmt_src->fmt.pix_mp.height;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs));
+ if (ret)
+ return ret;
+
+ fs.buffer_type = HFI_BUFFER_OUTPUT2;
+ fs.width = inst->fmt_dst->fmt.pix_mp.width;
+ fs.height = inst->fmt_dst->fmt.pix_mp.height;
+
+ return hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs));
+}
+
+static int iris_hfi_gen1_decide_core(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE;
+ struct hfi_videocores_usage_type cu;
+
+ cu.video_core_enable_mask = HFI_CORE_ID_1;
+
+ return hfi_gen1_set_property(inst, ptype, &cu, sizeof(cu));
+}
+
+static int iris_hfi_gen1_set_raw_format(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
+ u32 pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ struct hfi_uncompressed_format_select fmt;
+ int ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ fmt.buffer_type = HFI_BUFFER_OUTPUT;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12_UBWC : 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
+ if (ret)
+ return ret;
+
+ fmt.buffer_type = HFI_BUFFER_OUTPUT2;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
+ } else {
+ fmt.buffer_type = HFI_BUFFER_OUTPUT;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_set_format_constraints(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO;
+ struct hfi_uncompressed_plane_actual_constraints_info pconstraint;
+
+ pconstraint.buffer_type = HFI_BUFFER_OUTPUT2;
+ pconstraint.num_planes = 2;
+ pconstraint.plane_format[0].stride_multiples = 128;
+ pconstraint.plane_format[0].max_stride = 8192;
+ pconstraint.plane_format[0].min_plane_buffer_height_multiple = 32;
+ pconstraint.plane_format[0].buffer_alignment = 256;
+
+ pconstraint.plane_format[1].stride_multiples = 128;
+ pconstraint.plane_format[1].max_stride = 8192;
+ pconstraint.plane_format[1].min_plane_buffer_height_multiple = 16;
+ pconstraint.plane_format[1].buffer_alignment = 256;
+
+ return hfi_gen1_set_property(inst, ptype, &pconstraint, sizeof(pconstraint));
+}
+
+static int iris_hfi_gen1_set_num_bufs(struct iris_inst *inst)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL;
+ struct hfi_buffer_count_actual buf_count;
+ int ret;
+
+ buf_count.type = HFI_BUFFER_INPUT;
+ buf_count.count_actual = VIDEO_MAX_FRAME;
+ buf_count.count_min_host = VIDEO_MAX_FRAME;
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ if (ret)
+ return ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ buf_count.type = HFI_BUFFER_OUTPUT;
+ buf_count.count_actual = VIDEO_MAX_FRAME;
+ buf_count.count_min_host = VIDEO_MAX_FRAME;
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ if (ret)
+ return ret;
+
+ buf_count.type = HFI_BUFFER_OUTPUT2;
+ buf_count.count_actual = iris_vpu_buf_count(inst, BUF_DPB);
+ buf_count.count_min_host = iris_vpu_buf_count(inst, BUF_DPB);
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ } else {
+ buf_count.type = HFI_BUFFER_OUTPUT;
+ buf_count.count_actual = VIDEO_MAX_FRAME;
+ buf_count.count_min_host = VIDEO_MAX_FRAME;
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_set_multistream(struct iris_inst *inst)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+ struct hfi_multi_stream multi = {0};
+ int ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ multi.buffer_type = HFI_BUFFER_OUTPUT;
+ multi.enable = 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ if (ret)
+ return ret;
+
+ multi.buffer_type = HFI_BUFFER_OUTPUT2;
+ multi.enable = 1;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ } else {
+ multi.buffer_type = HFI_BUFFER_OUTPUT;
+ multi.enable = 1;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ if (ret)
+ return ret;
+
+ multi.buffer_type = HFI_BUFFER_OUTPUT2;
+ multi.enable = 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_set_bufsize(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL;
+ struct hfi_buffer_size_actual bufsz;
+ int ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ bufsz.type = HFI_BUFFER_OUTPUT;
+ bufsz.size = iris_vpu_buf_size(inst, BUF_DPB);
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ if (ret)
+ return ret;
+
+ bufsz.type = HFI_BUFFER_OUTPUT2;
+ bufsz.size = inst->buffers[BUF_OUTPUT].size;
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ } else {
+ bufsz.type = HFI_BUFFER_OUTPUT;
+ bufsz.size = inst->buffers[BUF_OUTPUT].size;
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ if (ret)
+ return ret;
+
+ bufsz.type = HFI_BUFFER_OUTPUT2;
+ bufsz.size = 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ u32 config_params_size, i, j;
+ const u32 *config_params;
+ int ret;
+
+ static const struct iris_hfi_prop_type_handle prop_type_handle_inp_arr[] = {
+ {HFI_PROPERTY_PARAM_FRAME_SIZE,
+ iris_hfi_gen1_set_resolution},
+ {HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE,
+ iris_hfi_gen1_decide_core},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ iris_hfi_gen1_set_raw_format},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+ iris_hfi_gen1_set_format_constraints},
+ {HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
+ iris_hfi_gen1_set_num_bufs},
+ {HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
+ iris_hfi_gen1_set_multistream},
+ {HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL,
+ iris_hfi_gen1_set_bufsize},
+ };
+
+ static const struct iris_hfi_prop_type_handle prop_type_handle_out_arr[] = {
+ {HFI_PROPERTY_PARAM_FRAME_SIZE,
+ iris_hfi_gen1_set_resolution},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ iris_hfi_gen1_set_raw_format},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+ iris_hfi_gen1_set_format_constraints},
+ {HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
+ iris_hfi_gen1_set_num_bufs},
+ {HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
+ iris_hfi_gen1_set_multistream},
+ {HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL,
+ iris_hfi_gen1_set_bufsize},
+ };
+
+ config_params = core->iris_platform_data->input_config_params;
+ config_params_size = core->iris_platform_data->input_config_params_size;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ for (i = 0; i < config_params_size; i++) {
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_inp_arr); j++) {
+ if (prop_type_handle_inp_arr[j].type == config_params[i]) {
+ ret = prop_type_handle_inp_arr[j].handle(inst);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ }
+ } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+ for (i = 0; i < config_params_size; i++) {
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_out_arr); j++) {
+ if (prop_type_handle_out_arr[j].type == config_params[i]) {
+ ret = prop_type_handle_out_arr[j].handle(inst);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = {
+ .sys_init = iris_hfi_gen1_sys_init,
+ .sys_image_version = iris_hfi_gen1_sys_image_version,
+ .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse,
+ .sys_pc_prep = iris_hfi_gen1_sys_pc_prep,
+ .session_open = iris_hfi_gen1_session_open,
+ .session_set_config_params = iris_hfi_gen1_session_set_config_params,
+ .session_set_property = iris_hfi_gen1_session_set_property,
+ .session_start = iris_hfi_gen1_session_start,
+ .session_queue_buf = iris_hfi_gen1_session_queue_buffer,
+ .session_release_buf = iris_hfi_gen1_session_unset_buffers,
+ .session_resume_drc = iris_hfi_gen1_session_continue,
+ .session_stop = iris_hfi_gen1_session_stop,
+ .session_drain = iris_hfi_gen1_session_drain,
+ .session_close = iris_hfi_gen1_session_close,
+};
+
+void iris_hfi_gen1_command_ops_init(struct iris_core *core)
+{
+ core->hfi_ops = &iris_hfi_gen1_command_ops;
+}
+
+struct iris_inst *iris_hfi_gen1_get_instance(void)
+{
+ return kzalloc(sizeof(struct iris_inst), GFP_KERNEL);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
new file mode 100644
index 000000000000..9f246816a286
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN1_DEFINES_H__
+#define __IRIS_HFI_GEN1_DEFINES_H__
+
+#include <linux/types.h>
+
+#define HFI_VIDEO_ARCH_OX 0x1
+
+#define HFI_SESSION_TYPE_DEC 2
+
+#define HFI_VIDEO_CODEC_H264 0x00000002
+
+#define HFI_ERR_NONE 0x0
+
+#define HFI_CMD_SYS_INIT 0x10001
+#define HFI_CMD_SYS_PC_PREP 0x10002
+#define HFI_CMD_SYS_SET_PROPERTY 0x10005
+#define HFI_CMD_SYS_GET_PROPERTY 0x10006
+#define HFI_CMD_SYS_SESSION_INIT 0x10007
+#define HFI_CMD_SYS_SESSION_END 0x10008
+
+#define HFI_CMD_SESSION_SET_PROPERTY 0x11001
+#define HFI_CMD_SESSION_SET_BUFFERS 0x11002
+
+#define HFI_CMD_SESSION_LOAD_RESOURCES 0x211001
+#define HFI_CMD_SESSION_START 0x211002
+#define HFI_CMD_SESSION_STOP 0x211003
+#define HFI_CMD_SESSION_EMPTY_BUFFER 0x211004
+#define HFI_CMD_SESSION_FILL_BUFFER 0x211005
+#define HFI_CMD_SESSION_FLUSH 0x211008
+#define HFI_CMD_SESSION_RELEASE_BUFFERS 0x21100b
+#define HFI_CMD_SESSION_RELEASE_RESOURCES 0x21100c
+#define HFI_CMD_SESSION_CONTINUE 0x21100d
+
+#define HFI_ERR_SESSION_UNSUPPORTED_SETTING 0x1008
+#define HFI_ERR_SESSION_UNSUPPORTED_STREAM 0x100d
+#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE 0x1010
+#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR 0x1012
+#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED 0x1013
+
+#define HFI_EVENT_SYS_ERROR 0x1
+#define HFI_EVENT_SESSION_ERROR 0x2
+
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES 0x1000001
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES 0x1000002
+#define HFI_EVENT_SESSION_SEQUENCE_CHANGED 0x1000003
+
+#define HFI_BUFFERFLAG_EOS 0x00000001
+#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
+
+#define HFI_FLUSH_OUTPUT 0x1000002
+#define HFI_FLUSH_OUTPUT2 0x1000003
+#define HFI_FLUSH_ALL 0x1000004
+
+#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e
+
+#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL 0x201001
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO 0x201002
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE 0x201008
+#define HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL 0x20100c
+
+#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS 0x202001
+
+#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER 0x1200001
+#define HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS 0x120300e
+#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY 0x1204004
+
+#define HFI_BUFFER_INPUT 0x1
+#define HFI_BUFFER_OUTPUT 0x2
+#define HFI_BUFFER_OUTPUT2 0x3
+#define HFI_BUFFER_INTERNAL_PERSIST_1 0x5
+#define HFI_BUFFER_INTERNAL_SCRATCH 0x6
+#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x7
+
+#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL 0x5
+#define HFI_PROPERTY_SYS_IMAGE_VERSION 0x6
+
+#define HFI_PROPERTY_PARAM_FRAME_SIZE 0x1001
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT 0x1003
+#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT 0x1005
+#define HFI_PROPERTY_PARAM_WORK_MODE 0x1015
+#define HFI_PROPERTY_PARAM_WORK_ROUTE 0x1017
+#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE 0x2002
+
+#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001
+#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH 0x1003007
+#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT 0x1003009
+#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE 0x100300a
+#define HFI_CORE_ID_1 1
+#define HFI_COLOR_FORMAT_NV12 0x02
+#define HFI_COLOR_FORMAT_NV12_UBWC 0x8002
+
+#define HFI_MSG_SYS_INIT 0x20001
+#define HFI_MSG_SYS_SESSION_INIT 0x20006
+#define HFI_MSG_SYS_SESSION_END 0x20007
+#define HFI_MSG_SYS_COV 0x20009
+#define HFI_MSG_SYS_PROPERTY_INFO 0x2000a
+
+#define HFI_MSG_EVENT_NOTIFY 0x21001
+#define HFI_MSG_SESSION_LOAD_RESOURCES 0x221001
+#define HFI_MSG_SESSION_START 0x221002
+#define HFI_MSG_SESSION_STOP 0x221003
+#define HFI_MSG_SESSION_FLUSH 0x221006
+#define HFI_MSG_SESSION_EMPTY_BUFFER 0x221007
+#define HFI_MSG_SESSION_FILL_BUFFER 0x221008
+#define HFI_MSG_SESSION_RELEASE_RESOURCES 0x22100a
+#define HFI_MSG_SESSION_RELEASE_BUFFERS 0x22100c
+
+#define HFI_PICTURE_I 0x00000001
+#define HFI_PICTURE_P 0x00000002
+#define HFI_PICTURE_B 0x00000004
+#define HFI_PICTURE_IDR 0x00000008
+#define HFI_FRAME_NOTCODED 0x7f002000
+#define HFI_FRAME_YUV 0x7f004000
+#define HFI_UNUSED_PICT 0x10000000
+
+struct hfi_pkt_hdr {
+ u32 size;
+ u32 pkt_type;
+};
+
+struct hfi_session_hdr_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 session_id;
+};
+
+struct hfi_session_open_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 session_domain;
+ u32 session_codec;
+};
+
+struct hfi_session_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_sys_init_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 arch_type;
+};
+
+struct hfi_sys_set_property_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_sys_get_property_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 data;
+};
+
+struct hfi_session_set_property_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_sys_pc_prep_pkt {
+ struct hfi_pkt_hdr hdr;
+};
+
+struct hfi_session_set_buffers_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 extradata_size;
+ u32 min_buffer_size;
+ u32 num_buffers;
+ u32 buffer_info[];
+};
+
+struct hfi_session_empty_buffer_compressed_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 input_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data;
+};
+
+struct hfi_session_fill_buffer_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 stream_id;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 output_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data;
+};
+
+struct hfi_session_flush_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 flush_type;
+};
+
+struct hfi_session_release_buffer_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 extradata_size;
+ u32 response_req;
+ u32 num_buffers;
+ u32 buffer_info[];
+};
+
+struct hfi_buffer_info {
+ u32 buffer_addr;
+ u32 extradata_addr;
+};
+
+struct hfi_msg_event_notify_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 event_id;
+ u32 event_data1;
+ u32 event_data2;
+ u32 ext_event_data[];
+};
+
+struct hfi_msg_sys_init_done_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 error_type;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_msg_session_hdr_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_init_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_msg_sys_property_info_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 property;
+ u8 data[];
+};
+
+struct hfi_msg_session_flush_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 flush_type;
+};
+
+struct hfi_enable {
+ u32 enable;
+};
+
+struct hfi_profile_level {
+ u32 profile;
+ u32 level;
+};
+
+struct hfi_framesize {
+ u32 buffer_type;
+ u32 width;
+ u32 height;
+};
+
+struct hfi_videocores_usage_type {
+ u32 video_core_enable_mask;
+};
+
+struct hfi_video_work_mode {
+ u32 video_work_mode;
+};
+
+struct hfi_video_work_route {
+ u32 video_work_route;
+};
+
+struct hfi_bit_depth {
+ u32 buffer_type;
+ u32 bit_depth;
+};
+
+struct hfi_pic_struct {
+ u32 progressive_only;
+};
+
+struct hfi_colour_space {
+ u32 colour_space;
+};
+
+struct hfi_extradata_input_crop {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
+struct hfi_dpb_counts {
+ u32 max_dpb_count;
+ u32 max_ref_frames;
+ u32 max_dec_buffering;
+ u32 max_reorder_frames;
+ u32 fw_min_count;
+};
+
+struct hfi_uncompressed_format_select {
+ u32 buffer_type;
+ u32 format;
+};
+
+struct hfi_uncompressed_plane_constraints {
+ u32 stride_multiples;
+ u32 max_stride;
+ u32 min_plane_buffer_height_multiple;
+ u32 buffer_alignment;
+};
+
+struct hfi_uncompressed_plane_actual_constraints_info {
+ u32 buffer_type;
+ u32 num_planes;
+ struct hfi_uncompressed_plane_constraints plane_format[2];
+};
+
+struct hfi_buffer_count_actual {
+ u32 type;
+ u32 count_actual;
+ u32 count_min_host;
+};
+
+struct hfi_buffer_size_actual {
+ u32 type;
+ u32 size;
+};
+
+struct hfi_multi_stream {
+ u32 buffer_type;
+ u32 enable;
+};
+
+struct hfi_buffer_requirements {
+ u32 type;
+ u32 size;
+ u32 region_size;
+ u32 hold_count;
+ u32 count_min;
+ u32 count_actual;
+ u32 contiguous;
+ u32 alignment;
+};
+
+struct hfi_event_data {
+ u32 error;
+ u32 height;
+ u32 width;
+ u32 event_type;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 tag;
+ u32 profile;
+ u32 level;
+ u32 bit_depth;
+ u32 pic_struct;
+ u32 colour_space;
+ u32 entropy_mode;
+ u32 buf_count;
+ struct {
+ u32 left, top;
+ u32 width, height;
+ } input_crop;
+};
+
+struct hfi_msg_session_empty_buffer_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 offset;
+ u32 filled_len;
+ u32 input_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[];
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane0_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 stream_id;
+ u32 view_id;
+ u32 error_type;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 stats;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 frame_width;
+ u32 frame_height;
+ u32 start_x_coord;
+ u32 start_y_coord;
+ u32 input_tag;
+ u32 input_tag2;
+ u32 output_tag;
+ u32 picture_type;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[];
+};
+
+struct hfi_msg_session_release_buffers_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 num_buffers;
+ u32 buffer_info[];
+};
+
+struct hfi_msg_sys_debug_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 msg_type;
+ u32 msg_size;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u8 msg_data[];
+};
+
+struct hfi_msg_sys_coverage_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 msg_size;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u8 msg_data[];
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
new file mode 100644
index 000000000000..b72d503dd740
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_hfi_gen1.h"
+#include "iris_hfi_gen1_defines.h"
+#include "iris_instance.h"
+#include "iris_vdec.h"
+#include "iris_vpu_buffer.h"
+
+static void iris_hfi_gen1_read_changed_params(struct iris_inst *inst,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp;
+ u32 num_properties_changed = pkt->event_data2;
+ u8 *data_ptr = (u8 *)&pkt->ext_event_data[0];
+ u32 primaries, matrix_coeff, transfer_char;
+ struct hfi_dpb_counts *iris_vpu_dpb_count;
+ struct hfi_profile_level *profile_level;
+ struct hfi_buffer_requirements *bufreq;
+ struct hfi_extradata_input_crop *crop;
+ struct hfi_colour_space *colour_info;
+ struct iris_core *core = inst->core;
+ u32 colour_description_present_flag;
+ u32 video_signal_type_present_flag;
+ struct hfi_event_data event = {0};
+ struct hfi_bit_depth *pixel_depth;
+ struct hfi_pic_struct *pic_struct;
+ struct hfi_framesize *frame_sz;
+ struct vb2_queue *dst_q;
+ struct v4l2_ctrl *ctrl;
+ u32 full_range, ptype;
+
+ do {
+ ptype = *((u32 *)data_ptr);
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_FRAME_SIZE:
+ data_ptr += sizeof(u32);
+ frame_sz = (struct hfi_framesize *)data_ptr;
+ event.width = frame_sz->width;
+ event.height = frame_sz->height;
+ data_ptr += sizeof(*frame_sz);
+ break;
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
+ data_ptr += sizeof(u32);
+ profile_level = (struct hfi_profile_level *)data_ptr;
+ event.profile = profile_level->profile;
+ event.level = profile_level->level;
+ data_ptr += sizeof(*profile_level);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
+ data_ptr += sizeof(u32);
+ pixel_depth = (struct hfi_bit_depth *)data_ptr;
+ event.bit_depth = pixel_depth->bit_depth;
+ data_ptr += sizeof(*pixel_depth);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
+ data_ptr += sizeof(u32);
+ pic_struct = (struct hfi_pic_struct *)data_ptr;
+ event.pic_struct = pic_struct->progressive_only;
+ data_ptr += sizeof(*pic_struct);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
+ data_ptr += sizeof(u32);
+ colour_info = (struct hfi_colour_space *)data_ptr;
+ event.colour_space = colour_info->colour_space;
+ data_ptr += sizeof(*colour_info);
+ break;
+ case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
+ data_ptr += sizeof(u32);
+ event.entropy_mode = *(u32 *)data_ptr;
+ data_ptr += sizeof(u32);
+ break;
+ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+ data_ptr += sizeof(u32);
+ bufreq = (struct hfi_buffer_requirements *)data_ptr;
+ event.buf_count = bufreq->count_min;
+ data_ptr += sizeof(*bufreq);
+ break;
+ case HFI_INDEX_EXTRADATA_INPUT_CROP:
+ data_ptr += sizeof(u32);
+ crop = (struct hfi_extradata_input_crop *)data_ptr;
+ event.input_crop.left = crop->left;
+ event.input_crop.top = crop->top;
+ event.input_crop.width = crop->width;
+ event.input_crop.height = crop->height;
+ data_ptr += sizeof(*crop);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS:
+ data_ptr += sizeof(u32);
+ iris_vpu_dpb_count = (struct hfi_dpb_counts *)data_ptr;
+ event.buf_count = iris_vpu_dpb_count->fw_min_count;
+ data_ptr += sizeof(*iris_vpu_dpb_count);
+ break;
+ default:
+ break;
+ }
+ num_properties_changed--;
+ } while (num_properties_changed > 0);
+
+ pixmp_ip->width = event.width;
+ pixmp_ip->height = event.height;
+
+ pixmp_op->width = ALIGN(event.width, 128);
+ pixmp_op->height = ALIGN(event.height, 32);
+ pixmp_op->plane_fmt[0].bytesperline = ALIGN(event.width, 128);
+ pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+
+ matrix_coeff = FIELD_GET(GENMASK(7, 0), event.colour_space);
+ transfer_char = FIELD_GET(GENMASK(15, 8), event.colour_space);
+ primaries = FIELD_GET(GENMASK(23, 16), event.colour_space);
+ colour_description_present_flag = FIELD_GET(GENMASK(24, 24), event.colour_space);
+ full_range = FIELD_GET(GENMASK(25, 25), event.colour_space);
+ video_signal_type_present_flag = FIELD_GET(GENMASK(29, 29), event.colour_space);
+
+ pixmp_op->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pixmp_op->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pixmp_op->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pixmp_op->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ if (video_signal_type_present_flag) {
+ pixmp_op->quantization =
+ full_range ?
+ V4L2_QUANTIZATION_FULL_RANGE :
+ V4L2_QUANTIZATION_LIM_RANGE;
+ if (colour_description_present_flag) {
+ pixmp_op->colorspace =
+ iris_hfi_get_v4l2_color_primaries(primaries);
+ pixmp_op->xfer_func =
+ iris_hfi_get_v4l2_transfer_char(transfer_char);
+ pixmp_op->ycbcr_enc =
+ iris_hfi_get_v4l2_matrix_coefficients(matrix_coeff);
+ }
+ }
+
+ pixmp_ip->colorspace = pixmp_op->colorspace;
+ pixmp_ip->xfer_func = pixmp_op->xfer_func;
+ pixmp_ip->ycbcr_enc = pixmp_op->ycbcr_enc;
+ pixmp_ip->quantization = pixmp_op->quantization;
+
+ if (event.input_crop.width > 0 && event.input_crop.height > 0) {
+ inst->crop.left = event.input_crop.left;
+ inst->crop.top = event.input_crop.top;
+ inst->crop.width = event.input_crop.width;
+ inst->crop.height = event.input_crop.height;
+ } else {
+ inst->crop.left = 0;
+ inst->crop.top = 0;
+ inst->crop.width = event.width;
+ inst->crop.height = event.height;
+ }
+
+ inst->fw_min_count = event.buf_count;
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage;
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, inst->buffers[BUF_OUTPUT].min_count);
+
+ dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ dst_q->min_reqbufs_allocation = inst->buffers[BUF_OUTPUT].min_count;
+
+ if (event.bit_depth || !event.pic_struct) {
+ dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n",
+ event.bit_depth, event.pic_struct);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+}
+
+static void iris_hfi_gen1_event_seq_changed(struct iris_inst *inst,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ struct hfi_session_flush_pkt flush_pkt;
+ u32 num_properties_changed;
+ int ret;
+
+ ret = iris_inst_sub_state_change_drc(inst);
+ if (ret)
+ return;
+
+ switch (pkt->event_data1) {
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
+ break;
+ default:
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return;
+ }
+
+ num_properties_changed = pkt->event_data2;
+ if (!num_properties_changed) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return;
+ }
+
+ iris_hfi_gen1_read_changed_params(inst, pkt);
+
+ if (inst->state != IRIS_INST_ERROR) {
+ reinit_completion(&inst->flush_completion);
+
+ flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
+ flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+ flush_pkt.shdr.session_id = inst->session_id;
+ flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
+ iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ }
+
+ iris_vdec_src_change(inst);
+ iris_inst_sub_state_change_drc_last(inst);
+}
+
+static void
+iris_hfi_gen1_sys_event_notify(struct iris_core *core, void *packet)
+{
+ struct hfi_msg_event_notify_pkt *pkt = packet;
+ struct iris_inst *instance;
+
+ if (pkt->event_id == HFI_EVENT_SYS_ERROR)
+ dev_err(core->dev, "sys error (type: %x, session id:%x, data1:%x, data2:%x)\n",
+ pkt->event_id, pkt->shdr.session_id, pkt->event_data1,
+ pkt->event_data2);
+
+ core->state = IRIS_CORE_ERROR;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list)
+ iris_inst_change_state(instance, IRIS_INST_ERROR);
+ mutex_unlock(&core->lock);
+
+ schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10));
+}
+
+static void
+iris_hfi_gen1_event_session_error(struct iris_inst *inst, struct hfi_msg_event_notify_pkt *pkt)
+{
+ switch (pkt->event_data1) {
+ /* non fatal session errors */
+ case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
+ case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
+ case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
+ case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED:
+ dev_dbg(inst->core->dev, "session error: event id:%x, session id:%x\n",
+ pkt->event_data1, pkt->shdr.session_id);
+ break;
+ /* fatal session errors */
+ default:
+ /*
+ * firmware fills event_data2 as an additional information about the
+ * hfi command for which session error has ouccured.
+ */
+ dev_err(inst->core->dev,
+ "session error for command: %x, event id:%x, session id:%x\n",
+ pkt->event_data2, pkt->event_data1,
+ pkt->shdr.session_id);
+ iris_vb2_queue_error(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ break;
+ }
+}
+
+static void iris_hfi_gen1_session_event_notify(struct iris_inst *inst, void *packet)
+{
+ struct hfi_msg_event_notify_pkt *pkt = packet;
+
+ switch (pkt->event_id) {
+ case HFI_EVENT_SESSION_ERROR:
+ iris_hfi_gen1_event_session_error(inst, pkt);
+ break;
+ case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
+ iris_hfi_gen1_event_seq_changed(inst, pkt);
+ break;
+ default:
+ break;
+ }
+}
+
+static void iris_hfi_gen1_sys_init_done(struct iris_core *core, void *packet)
+{
+ struct hfi_msg_sys_init_done_pkt *pkt = packet;
+
+ if (pkt->error_type != HFI_ERR_NONE) {
+ core->state = IRIS_CORE_ERROR;
+ return;
+ }
+
+ complete(&core->core_init_done);
+}
+
+static void
+iris_hfi_gen1_sys_get_prop_image_version(struct iris_core *core,
+ struct hfi_msg_sys_property_info_pkt *pkt)
+{
+ int req_bytes = pkt->hdr.size - sizeof(*pkt);
+ char fw_version[IRIS_FW_VERSION_LENGTH];
+ u8 *str_image_version;
+ u32 i;
+
+ if (req_bytes < IRIS_FW_VERSION_LENGTH - 1 || !pkt->data[0] || pkt->num_properties > 1) {
+ dev_err(core->dev, "bad packet\n");
+ return;
+ }
+
+ str_image_version = pkt->data;
+ if (!str_image_version) {
+ dev_err(core->dev, "firmware version not available\n");
+ return;
+ }
+
+ for (i = 0; i < IRIS_FW_VERSION_LENGTH - 1; i++) {
+ if (str_image_version[i] != '\0')
+ fw_version[i] = str_image_version[i];
+ else
+ fw_version[i] = ' ';
+ }
+ fw_version[i] = '\0';
+ dev_dbg(core->dev, "firmware version: %s\n", fw_version);
+}
+
+static void iris_hfi_gen1_sys_property_info(struct iris_core *core, void *packet)
+{
+ struct hfi_msg_sys_property_info_pkt *pkt = packet;
+
+ if (!pkt->num_properties) {
+ dev_dbg(core->dev, "no properties\n");
+ return;
+ }
+
+ switch (pkt->property) {
+ case HFI_PROPERTY_SYS_IMAGE_VERSION:
+ iris_hfi_gen1_sys_get_prop_image_version(core, pkt);
+ break;
+ default:
+ dev_dbg(core->dev, "unknown property data\n");
+ break;
+ }
+}
+
+static void iris_hfi_gen1_session_etb_done(struct iris_inst *inst, void *packet)
+{
+ struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct iris_buffer *buf = NULL;
+ bool found = false;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (buf->index == pkt->input_tag) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ goto error;
+
+ if (pkt->shdr.error_type == HFI_ERR_SESSION_UNSUPPORTED_STREAM) {
+ buf->flags = V4L2_BUF_FLAG_ERROR;
+ iris_vb2_queue_error(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ return;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ iris_vb2_buffer_done(inst, buf);
+ }
+
+ return;
+
+error:
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ dev_err(inst->core->dev, "error in etb done\n");
+}
+
+static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
+{
+ struct hfi_msg_session_fbd_uncompressed_plane0_pkt *pkt = packet;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct hfi_session_flush_pkt flush_pkt;
+ u32 timestamp_hi = pkt->time_stamp_hi;
+ u32 timestamp_lo = pkt->time_stamp_lo;
+ struct iris_core *core = inst->core;
+ u32 filled_len = pkt->filled_len;
+ u32 pic_type = pkt->picture_type;
+ u32 output_tag = pkt->output_tag;
+ struct iris_buffer *buf, *iter;
+ struct iris_buffers *buffers;
+ u32 hfi_flags = pkt->flags;
+ u32 offset = pkt->offset;
+ u64 timestamp_us = 0;
+ bool found = false;
+ u32 flags = 0;
+
+ if ((hfi_flags & HFI_BUFFERFLAG_EOS) && !filled_len) {
+ reinit_completion(&inst->flush_completion);
+
+ flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
+ flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+ flush_pkt.shdr.session_id = inst->session_id;
+ flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
+ iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ iris_inst_sub_state_change_drain_last(inst);
+
+ return;
+ }
+
+ if (iris_split_mode_enabled(inst) && pkt->stream_id == 0) {
+ buffers = &inst->buffers[BUF_DPB];
+ if (!buffers)
+ goto error;
+
+ found = false;
+ list_for_each_entry(iter, &buffers->list, list) {
+ if (!(iter->attr & BUF_ATTR_QUEUED))
+ continue;
+
+ found = (iter->index == output_tag &&
+ iter->data_offset == offset);
+
+ if (found) {
+ buf = iter;
+ break;
+ }
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ continue;
+
+ found = (buf->index == output_tag &&
+ buf->data_offset == offset);
+
+ if (found)
+ break;
+ }
+ }
+ if (!found)
+ goto error;
+
+ buf->data_offset = offset;
+ buf->data_size = filled_len;
+
+ if (filled_len) {
+ timestamp_us = timestamp_hi;
+ timestamp_us = (timestamp_us << 32) | timestamp_lo;
+ } else {
+ flags |= V4L2_BUF_FLAG_LAST;
+ }
+ buf->timestamp = timestamp_us;
+
+ switch (pic_type) {
+ case HFI_PICTURE_IDR:
+ case HFI_PICTURE_I:
+ flags |= V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case HFI_PICTURE_P:
+ flags |= V4L2_BUF_FLAG_PFRAME;
+ break;
+ case HFI_PICTURE_B:
+ flags |= V4L2_BUF_FLAG_BFRAME;
+ break;
+ case HFI_FRAME_NOTCODED:
+ case HFI_UNUSED_PICT:
+ case HFI_FRAME_YUV:
+ default:
+ break;
+ }
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ buf->attr |= BUF_ATTR_DEQUEUED;
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+
+ buf->flags |= flags;
+
+ iris_vb2_buffer_done(inst, buf);
+
+ return;
+
+error:
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ dev_err(core->dev, "error in ftb done\n");
+}
+
+struct iris_hfi_gen1_response_pkt_info {
+ u32 pkt;
+ u32 pkt_sz;
+};
+
+static const struct iris_hfi_gen1_response_pkt_info pkt_infos[] = {
+ {
+ .pkt = HFI_MSG_EVENT_NOTIFY,
+ .pkt_sz = sizeof(struct hfi_msg_event_notify_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_INIT,
+ .pkt_sz = sizeof(struct hfi_msg_sys_init_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_PROPERTY_INFO,
+ .pkt_sz = sizeof(struct hfi_msg_sys_property_info_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_SESSION_INIT,
+ .pkt_sz = sizeof(struct hfi_msg_session_init_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_SESSION_END,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_LOAD_RESOURCES,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_START,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_STOP,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_EMPTY_BUFFER,
+ .pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_FILL_BUFFER,
+ .pkt_sz = sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_FLUSH,
+ .pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_RELEASE_RESOURCES,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_RELEASE_BUFFERS,
+ .pkt_sz = sizeof(struct hfi_msg_session_release_buffers_done_pkt),
+ },
+};
+
+static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response)
+{
+ struct hfi_pkt_hdr *hdr = (struct hfi_pkt_hdr *)response;
+ const struct iris_hfi_gen1_response_pkt_info *pkt_info;
+ struct device *dev = core->dev;
+ struct hfi_session_pkt *pkt;
+ struct completion *done;
+ struct iris_inst *inst;
+ bool found = false;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(pkt_infos); i++) {
+ pkt_info = &pkt_infos[i];
+ if (pkt_info->pkt != hdr->pkt_type)
+ continue;
+ found = true;
+ break;
+ }
+
+ if (!found || hdr->size < pkt_info->pkt_sz) {
+ dev_err(dev, "bad packet size (%d should be %d, pkt type:%x, found %d)\n",
+ hdr->size, pkt_info->pkt_sz, hdr->pkt_type, found);
+
+ return;
+ }
+
+ switch (hdr->pkt_type) {
+ case HFI_MSG_SYS_INIT:
+ iris_hfi_gen1_sys_init_done(core, hdr);
+ break;
+ case HFI_MSG_SYS_PROPERTY_INFO:
+ iris_hfi_gen1_sys_property_info(core, hdr);
+ break;
+ case HFI_MSG_EVENT_NOTIFY:
+ pkt = (struct hfi_session_pkt *)hdr;
+ inst = iris_get_instance(core, pkt->shdr.session_id);
+ if (inst) {
+ mutex_lock(&inst->lock);
+ iris_hfi_gen1_session_event_notify(inst, hdr);
+ mutex_unlock(&inst->lock);
+ } else {
+ iris_hfi_gen1_sys_event_notify(core, hdr);
+ }
+
+ break;
+ default:
+ pkt = (struct hfi_session_pkt *)hdr;
+ inst = iris_get_instance(core, pkt->shdr.session_id);
+ if (!inst) {
+ dev_warn(dev, "no valid instance(pkt session_id:%x, pkt:%x)\n",
+ pkt->shdr.session_id,
+ pkt_info ? pkt_info->pkt : 0);
+ return;
+ }
+
+ mutex_lock(&inst->lock);
+ if (hdr->pkt_type == HFI_MSG_SESSION_EMPTY_BUFFER) {
+ iris_hfi_gen1_session_etb_done(inst, hdr);
+ } else if (hdr->pkt_type == HFI_MSG_SESSION_FILL_BUFFER) {
+ iris_hfi_gen1_session_ftb_done(inst, hdr);
+ } else {
+ struct hfi_msg_session_hdr_pkt *shdr;
+
+ shdr = (struct hfi_msg_session_hdr_pkt *)hdr;
+ if (shdr->error_type != HFI_ERR_NONE)
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+
+ done = pkt_info->pkt == HFI_MSG_SESSION_FLUSH ?
+ &inst->flush_completion : &inst->completion;
+ complete(done);
+ }
+ mutex_unlock(&inst->lock);
+
+ break;
+ }
+}
+
+static void iris_hfi_gen1_flush_debug_queue(struct iris_core *core, u8 *packet)
+{
+ struct hfi_msg_sys_coverage_pkt *pkt;
+
+ while (!iris_hfi_queue_dbg_read(core, packet)) {
+ pkt = (struct hfi_msg_sys_coverage_pkt *)packet;
+
+ if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) {
+ struct hfi_msg_sys_debug_pkt *pkt =
+ (struct hfi_msg_sys_debug_pkt *)packet;
+
+ dev_dbg(core->dev, "%s", pkt->msg_data);
+ }
+ }
+}
+
+static void iris_hfi_gen1_response_handler(struct iris_core *core)
+{
+ memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr));
+ while (!iris_hfi_queue_msg_read(core, core->response_packet)) {
+ iris_hfi_gen1_handle_response(core, core->response_packet);
+ memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr));
+ }
+
+ iris_hfi_gen1_flush_debug_queue(core, core->response_packet);
+}
+
+static const struct iris_hfi_response_ops iris_hfi_gen1_response_ops = {
+ .hfi_response_handler = iris_hfi_gen1_response_handler,
+};
+
+void iris_hfi_gen1_response_ops_init(struct iris_core *core)
+{
+ core->hfi_response_ops = &iris_hfi_gen1_response_ops;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
new file mode 100644
index 000000000000..b9d3749a10ef
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN2_H__
+#define __IRIS_HFI_GEN2_H__
+
+#include "iris_instance.h"
+
+struct iris_core;
+
+#define to_iris_inst_hfi_gen2(ptr) \
+ container_of(ptr, struct iris_inst_hfi_gen2, inst)
+
+/**
+ * struct iris_inst_hfi_gen2 - holds per video instance parameters for hfi_gen2
+ *
+ * @inst: pointer to iris_instance structure
+ * @packet: HFI packet
+ * @ipsc_properties_set: boolean to set ipsc properties to fw
+ * @opsc_properties_set: boolean to set opsc properties to fw
+ * @hfi_frame_info: structure of frame info
+ * @src_subcr_params: subscription params to fw on input port
+ * @dst_subcr_params: subscription params to fw on output port
+ */
+struct iris_inst_hfi_gen2 {
+ struct iris_inst inst;
+ struct iris_hfi_header *packet;
+ bool ipsc_properties_set;
+ bool opsc_properties_set;
+ struct iris_hfi_frame_info hfi_frame_info;
+ struct hfi_subscription_params src_subcr_params;
+ struct hfi_subscription_params dst_subcr_params;
+};
+
+void iris_hfi_gen2_command_ops_init(struct iris_core *core);
+void iris_hfi_gen2_response_ops_init(struct iris_core *core);
+struct iris_inst *iris_hfi_gen2_get_instance(void);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
new file mode 100644
index 000000000000..a908b41e2868
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -0,0 +1,957 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_packet.h"
+
+#define UNSPECIFIED_COLOR_FORMAT 5
+#define NUM_SYS_INIT_PACKETS 8
+
+#define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \
+ NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32)))
+
+#define SYS_IFPC_PKT_SIZE (sizeof(struct iris_hfi_header) + \
+ sizeof(struct iris_hfi_packet) + sizeof(u32))
+
+#define SYS_NO_PAYLOAD_PKT_SIZE (sizeof(struct iris_hfi_header) + \
+ sizeof(struct iris_hfi_packet))
+
+static int iris_hfi_gen2_sys_init(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_INIT_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_sys_init(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_sys_image_version(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_image_version(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_sys_interframe_powercollapse(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_IFPC_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_sys_interframe_powercollapse(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_sys_pc_prep(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_sys_pc_prep(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static u32 iris_hfi_gen2_get_port(u32 plane)
+{
+ switch (plane) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return HFI_PORT_BITSTREAM;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ return HFI_PORT_RAW;
+ default:
+ return HFI_PORT_NONE;
+ }
+}
+
+static u32 iris_hfi_gen2_get_port_from_buf_type(enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ case BUF_BIN:
+ case BUF_COMV:
+ case BUF_NON_COMV:
+ case BUF_LINE:
+ return HFI_PORT_BITSTREAM;
+ case BUF_OUTPUT:
+ case BUF_DPB:
+ return HFI_PORT_RAW;
+ case BUF_PERSIST:
+ default:
+ return HFI_PORT_NONE;
+ }
+}
+
+static int iris_hfi_gen2_session_set_property(struct iris_inst *inst, u32 packet_type, u32 flag,
+ u32 plane, u32 payload_type, void *payload,
+ u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ iris_hfi_gen2_packet_session_property(inst,
+ packet_type,
+ flag,
+ plane,
+ payload_type,
+ payload,
+ payload_size);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_set_bitstream_resolution(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 resolution = inst->fmt_src->fmt.pix_mp.width << 16 |
+ inst->fmt_src->fmt.pix_mp.height;
+
+ inst_hfi_gen2->src_subcr_params.bitstream_resolution = resolution;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &resolution,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst)
+{
+ u32 bottom_offset = (inst->fmt_src->fmt.pix_mp.height - inst->crop.height);
+ u32 right_offset = (inst->fmt_src->fmt.pix_mp.width - inst->crop.width);
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 left_offset = inst->crop.left;
+ u32 top_offset = inst->crop.top;
+ u32 payload[2];
+
+ payload[0] = FIELD_PREP(GENMASK(31, 16), left_offset) | top_offset;
+ payload[1] = FIELD_PREP(GENMASK(31, 16), right_offset) | bottom_offset;
+ inst_hfi_gen2->src_subcr_params.crop_offsets[0] = payload[0];
+ inst_hfi_gen2->src_subcr_params.crop_offsets[1] = payload[1];
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_64_PACKED,
+ &payload,
+ sizeof(u64));
+}
+
+static int iris_hfi_gen2_set_bit_dpeth(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 bitdepth = BIT_DEPTH_8;
+
+ inst_hfi_gen2->src_subcr_params.bit_depth = bitdepth;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &bitdepth,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_coded_frames(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 coded_frames = 0;
+
+ if (inst->fw_caps[CODED_FRAMES].value == CODED_FRAMES_PROGRESSIVE)
+ coded_frames = HFI_BITMASK_FRAME_MBS_ONLY_FLAG;
+ inst_hfi_gen2->src_subcr_params.coded_frames = coded_frames;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_CODED_FRAMES,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &coded_frames,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_min_output_count(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 min_output = inst->buffers[BUF_OUTPUT].min_count;
+
+ inst_hfi_gen2->src_subcr_params.fw_min_count = min_output;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &min_output,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_picture_order_count(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 poc = 0;
+
+ inst_hfi_gen2->src_subcr_params.pic_order_cnt = poc;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_PIC_ORDER_CNT_TYPE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &poc,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_colorspace(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ struct v4l2_pix_format_mplane *pixmp = &inst->fmt_src->fmt.pix_mp;
+ u32 video_signal_type_present_flag = 0, color_info;
+ u32 matrix_coeff = HFI_MATRIX_COEFF_RESERVED;
+ u32 video_format = UNSPECIFIED_COLOR_FORMAT;
+ u32 full_range = V4L2_QUANTIZATION_DEFAULT;
+ u32 transfer_char = HFI_TRANSFER_RESERVED;
+ u32 colour_description_present_flag = 0;
+ u32 primaries = HFI_PRIMARIES_RESERVED;
+
+ if (pixmp->colorspace != V4L2_COLORSPACE_DEFAULT ||
+ pixmp->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT ||
+ pixmp->xfer_func != V4L2_XFER_FUNC_DEFAULT) {
+ colour_description_present_flag = 1;
+ video_signal_type_present_flag = 1;
+ primaries = iris_hfi_gen2_get_color_primaries(pixmp->colorspace);
+ matrix_coeff = iris_hfi_gen2_get_matrix_coefficients(pixmp->ycbcr_enc);
+ transfer_char = iris_hfi_gen2_get_transfer_char(pixmp->xfer_func);
+ }
+
+ if (pixmp->quantization != V4L2_QUANTIZATION_DEFAULT) {
+ video_signal_type_present_flag = 1;
+ full_range = pixmp->quantization == V4L2_QUANTIZATION_FULL_RANGE ? 1 : 0;
+ }
+
+ color_info = iris_hfi_gen2_get_color_info(matrix_coeff, transfer_char, primaries,
+ colour_description_present_flag, full_range,
+ video_format, video_signal_type_present_flag);
+
+ inst_hfi_gen2->src_subcr_params.color_info = color_info;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_32_PACKED,
+ &color_info,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_profile(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 profile = inst->fw_caps[PROFILE].value;
+
+ inst_hfi_gen2->src_subcr_params.profile = profile;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_PROFILE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &profile,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_level(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 level = inst->fw_caps[LEVEL].value;
+
+ inst_hfi_gen2->src_subcr_params.level = level;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_LEVEL,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &level,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst)
+{
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ u32 hfi_colorformat, pixelformat;
+
+ pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FMT_NV12 : 0;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_COLOR_FORMAT,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &hfi_colorformat,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst)
+{
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ u32 pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ u32 scanline_y = inst->fmt_dst->fmt.pix_mp.height;
+ u32 stride_y = inst->fmt_dst->fmt.pix_mp.width;
+ u32 scanline_uv = scanline_y / 2;
+ u32 stride_uv = stride_y;
+ u32 payload[2];
+
+ if (pixelformat != V4L2_PIX_FMT_NV12)
+ return 0;
+
+ payload[0] = stride_y << 16 | scanline_y;
+ payload[1] = stride_uv << 16 | scanline_uv;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_LINEAR_STRIDE_SCANLINE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U64,
+ &payload,
+ sizeof(u64));
+}
+
+static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ u32 config_params_size, i, j;
+ const u32 *config_params;
+ int ret;
+
+ static const struct iris_hfi_prop_type_handle prop_type_handle_arr[] = {
+ {HFI_PROP_BITSTREAM_RESOLUTION, iris_hfi_gen2_set_bitstream_resolution },
+ {HFI_PROP_CROP_OFFSETS, iris_hfi_gen2_set_crop_offsets },
+ {HFI_PROP_CODED_FRAMES, iris_hfi_gen2_set_coded_frames },
+ {HFI_PROP_LUMA_CHROMA_BIT_DEPTH, iris_hfi_gen2_set_bit_dpeth },
+ {HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, iris_hfi_gen2_set_min_output_count },
+ {HFI_PROP_PIC_ORDER_CNT_TYPE, iris_hfi_gen2_set_picture_order_count },
+ {HFI_PROP_SIGNAL_COLOR_INFO, iris_hfi_gen2_set_colorspace },
+ {HFI_PROP_PROFILE, iris_hfi_gen2_set_profile },
+ {HFI_PROP_LEVEL, iris_hfi_gen2_set_level },
+ {HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat },
+ {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline },
+ };
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ config_params = core->iris_platform_data->input_config_params;
+ config_params_size = core->iris_platform_data->input_config_params_size;
+ } else {
+ config_params = core->iris_platform_data->output_config_params;
+ config_params_size = core->iris_platform_data->output_config_params_size;
+ }
+
+ if (!config_params || !config_params_size)
+ return -EINVAL;
+
+ for (i = 0; i < config_params_size; i++) {
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_arr); j++) {
+ if (prop_type_handle_arr[j].type == config_params[i]) {
+ ret = prop_type_handle_arr[j].handle(inst);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 codec = HFI_CODEC_DECODE_AVC;
+
+ iris_hfi_gen2_packet_session_property(inst,
+ HFI_PROP_CODEC,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_NONE,
+ HFI_PAYLOAD_U32_ENUM,
+ &codec,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_set_default_header(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 default_header = false;
+
+ iris_hfi_gen2_packet_session_property(inst,
+ HFI_PROP_DEC_DEFAULT_HEADER,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_U32,
+ &default_header,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_open(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret;
+
+ if (inst->state != IRIS_INST_DEINIT)
+ return -EALREADY;
+
+ inst_hfi_gen2->ipsc_properties_set = false;
+ inst_hfi_gen2->opsc_properties_set = false;
+
+ inst_hfi_gen2->packet = kzalloc(4096, GFP_KERNEL);
+ if (!inst_hfi_gen2->packet)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_OPEN,
+ HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED,
+ HFI_PORT_NONE,
+ 0,
+ HFI_PAYLOAD_U32,
+ &inst->session_id,
+ sizeof(u32));
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+ if (ret)
+ goto fail_free_packet;
+
+ ret = iris_hfi_gen2_session_set_codec(inst);
+ if (ret)
+ goto fail_free_packet;
+
+ ret = iris_hfi_gen2_session_set_default_header(inst);
+ if (ret)
+ goto fail_free_packet;
+
+ return 0;
+
+fail_free_packet:
+ kfree(inst_hfi_gen2->packet);
+ inst_hfi_gen2->packet = NULL;
+
+ return ret;
+}
+
+static int iris_hfi_gen2_session_close(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret;
+
+ if (!inst_hfi_gen2->packet)
+ return -EINVAL;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_CLOSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ HFI_PORT_NONE,
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+
+ kfree(inst_hfi_gen2->packet);
+ inst_hfi_gen2->packet = NULL;
+
+ return ret;
+}
+
+static int iris_hfi_gen2_session_subscribe_mode(struct iris_inst *inst,
+ u32 cmd, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ cmd,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ payload_type,
+ payload,
+ payload_size);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct hfi_subscription_params subsc_params;
+ u32 prop_type, payload_size, payload_type;
+ struct iris_core *core = inst->core;
+ const u32 *change_param;
+ u32 change_param_size;
+ u32 payload[32] = {0};
+ u32 hfi_port = 0, i;
+ int ret;
+
+ if ((V4L2_TYPE_IS_OUTPUT(plane) && inst_hfi_gen2->ipsc_properties_set) ||
+ (V4L2_TYPE_IS_CAPTURE(plane) && inst_hfi_gen2->opsc_properties_set)) {
+ dev_err(core->dev, "invalid plane\n");
+ return 0;
+ }
+
+ change_param = core->iris_platform_data->input_config_params;
+ change_param_size = core->iris_platform_data->input_config_params_size;
+
+ payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE;
+
+ for (i = 0; i < change_param_size; i++)
+ payload[i + 1] = change_param[i];
+
+ ret = iris_hfi_gen2_session_subscribe_mode(inst,
+ HFI_CMD_SUBSCRIBE_MODE,
+ plane,
+ HFI_PAYLOAD_U32_ARRAY,
+ &payload[0],
+ ((change_param_size + 1) * sizeof(u32)));
+ if (ret)
+ return ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ inst_hfi_gen2->ipsc_properties_set = true;
+ } else {
+ hfi_port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ memcpy(&inst_hfi_gen2->dst_subcr_params,
+ &inst_hfi_gen2->src_subcr_params,
+ sizeof(inst_hfi_gen2->src_subcr_params));
+ subsc_params = inst_hfi_gen2->dst_subcr_params;
+ for (i = 0; i < change_param_size; i++) {
+ payload[0] = 0;
+ payload[1] = 0;
+ payload_size = 0;
+ payload_type = 0;
+ prop_type = change_param[i];
+ switch (prop_type) {
+ case HFI_PROP_BITSTREAM_RESOLUTION:
+ payload[0] = subsc_params.bitstream_resolution;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_CROP_OFFSETS:
+ payload[0] = subsc_params.crop_offsets[0];
+ payload[1] = subsc_params.crop_offsets[1];
+ payload_size = sizeof(u64);
+ payload_type = HFI_PAYLOAD_64_PACKED;
+ break;
+ case HFI_PROP_CODED_FRAMES:
+ payload[0] = subsc_params.coded_frames;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT:
+ payload[0] = subsc_params.fw_min_count;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_PIC_ORDER_CNT_TYPE:
+ payload[0] = subsc_params.pic_order_cnt;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_SIGNAL_COLOR_INFO:
+ payload[0] = subsc_params.color_info;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_PROFILE:
+ payload[0] = subsc_params.profile;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_LEVEL:
+ payload[0] = subsc_params.level;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ default:
+ prop_type = 0;
+ ret = -EINVAL;
+ break;
+ }
+ if (prop_type) {
+ ret = iris_hfi_gen2_session_set_property(inst,
+ prop_type,
+ HFI_HOST_FLAGS_NONE,
+ hfi_port,
+ payload_type,
+ &payload,
+ payload_size);
+ if (ret)
+ return ret;
+ }
+ }
+ inst_hfi_gen2->opsc_properties_set = true;
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ u32 subscribe_prop_size, i;
+ const u32 *subcribe_prop;
+ u32 payload[32] = {0};
+
+ payload[0] = HFI_MODE_PROPERTY;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ subscribe_prop_size = core->iris_platform_data->dec_input_prop_size;
+ subcribe_prop = core->iris_platform_data->dec_input_prop;
+ } else {
+ subscribe_prop_size = core->iris_platform_data->dec_output_prop_size;
+ subcribe_prop = core->iris_platform_data->dec_output_prop;
+ }
+
+ for (i = 0; i < subscribe_prop_size; i++)
+ payload[i + 1] = subcribe_prop[i];
+
+ return iris_hfi_gen2_session_subscribe_mode(inst,
+ HFI_CMD_SUBSCRIBE_MODE,
+ plane,
+ HFI_PAYLOAD_U32_ARRAY,
+ &payload[0],
+ (subscribe_prop_size + 1) * sizeof(u32));
+}
+
+static int iris_hfi_gen2_session_start(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret = 0;
+
+ ret = iris_hfi_gen2_subscribe_change_param(inst, plane);
+ if (ret)
+ return ret;
+
+ ret = iris_hfi_gen2_subscribe_property(inst, plane);
+ if (ret)
+ return ret;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_START,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_stop(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret = 0;
+
+ reinit_completion(&inst->completion);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_STOP,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+ if (ret)
+ return ret;
+
+ return iris_wait_for_session_response(inst, false);
+}
+
+static int iris_hfi_gen2_session_pause(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_PAUSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_resume_drc(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 payload = HFI_CMD_SETTINGS_CHANGE;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_RESUME,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_U32,
+ &payload,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_resume_drain(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 payload = HFI_CMD_DRAIN;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_RESUME,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_U32,
+ &payload,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_drain(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ if (!V4L2_TYPE_IS_OUTPUT(plane))
+ return 0;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_DRAIN,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static u32 iris_hfi_gen2_buf_type_from_driver(enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return HFI_BUFFER_BITSTREAM;
+ case BUF_OUTPUT:
+ return HFI_BUFFER_RAW;
+ case BUF_BIN:
+ return HFI_BUFFER_BIN;
+ case BUF_COMV:
+ return HFI_BUFFER_COMV;
+ case BUF_NON_COMV:
+ return HFI_BUFFER_NON_COMV;
+ case BUF_LINE:
+ return HFI_BUFFER_LINE;
+ case BUF_DPB:
+ return HFI_BUFFER_DPB;
+ case BUF_PERSIST:
+ return HFI_BUFFER_PERSIST;
+ default:
+ return 0;
+ }
+}
+
+static int iris_set_num_comv(struct iris_inst *inst)
+{
+ struct platform_inst_caps *caps;
+ struct iris_core *core = inst->core;
+ u32 num_comv;
+
+ caps = core->iris_platform_data->inst_caps;
+ num_comv = caps->num_comv;
+
+ return core->hfi_ops->session_set_property(inst,
+ HFI_PROP_COMV_BUFFER_COUNT,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_U32,
+ &num_comv, sizeof(u32));
+}
+
+static void iris_hfi_gen2_get_buffer(struct iris_buffer *buffer, struct iris_hfi_buffer *buf)
+{
+ memset(buf, 0, sizeof(*buf));
+ buf->type = iris_hfi_gen2_buf_type_from_driver(buffer->type);
+ buf->index = buffer->index;
+ buf->base_address = buffer->device_addr;
+ buf->addr_offset = 0;
+ buf->buffer_size = buffer->buffer_size;
+
+ if (buffer->type == BUF_INPUT)
+ buf->buffer_size = ALIGN(buffer->buffer_size, 256);
+ buf->data_offset = buffer->data_offset;
+ buf->data_size = buffer->data_size;
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ buf->flags |= HFI_BUF_HOST_FLAG_RELEASE;
+ buf->flags |= HFI_BUF_HOST_FLAGS_CB_NON_SECURE;
+ buf->timestamp = buffer->timestamp;
+}
+
+static int iris_hfi_gen2_session_queue_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_hfi_buffer hfi_buffer;
+ u32 port;
+ int ret;
+
+ iris_hfi_gen2_get_buffer(buffer, &hfi_buffer);
+ if (buffer->type == BUF_COMV) {
+ ret = iris_set_num_comv(inst);
+ if (ret)
+ return ret;
+ }
+
+ port = iris_hfi_gen2_get_port_from_buf_type(buffer->type);
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_BUFFER,
+ HFI_HOST_FLAGS_INTR_REQUIRED,
+ port,
+ inst->session_id,
+ HFI_PAYLOAD_STRUCTURE,
+ &hfi_buffer,
+ sizeof(hfi_buffer));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_release_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_hfi_buffer hfi_buffer;
+ u32 port;
+
+ iris_hfi_gen2_get_buffer(buffer, &hfi_buffer);
+ hfi_buffer.flags |= HFI_BUF_HOST_FLAG_RELEASE;
+ port = iris_hfi_gen2_get_port_from_buf_type(buffer->type);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_BUFFER,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ port,
+ inst->session_id,
+ HFI_PAYLOAD_STRUCTURE,
+ &hfi_buffer,
+ sizeof(hfi_buffer));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = {
+ .sys_init = iris_hfi_gen2_sys_init,
+ .sys_image_version = iris_hfi_gen2_sys_image_version,
+ .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse,
+ .sys_pc_prep = iris_hfi_gen2_sys_pc_prep,
+ .session_open = iris_hfi_gen2_session_open,
+ .session_set_config_params = iris_hfi_gen2_session_set_config_params,
+ .session_set_property = iris_hfi_gen2_session_set_property,
+ .session_start = iris_hfi_gen2_session_start,
+ .session_queue_buf = iris_hfi_gen2_session_queue_buffer,
+ .session_release_buf = iris_hfi_gen2_session_release_buffer,
+ .session_pause = iris_hfi_gen2_session_pause,
+ .session_resume_drc = iris_hfi_gen2_session_resume_drc,
+ .session_stop = iris_hfi_gen2_session_stop,
+ .session_drain = iris_hfi_gen2_session_drain,
+ .session_resume_drain = iris_hfi_gen2_session_resume_drain,
+ .session_close = iris_hfi_gen2_session_close,
+};
+
+void iris_hfi_gen2_command_ops_init(struct iris_core *core)
+{
+ core->hfi_ops = &iris_hfi_gen2_command_ops;
+}
+
+struct iris_inst *iris_hfi_gen2_get_instance(void)
+{
+ return kzalloc(sizeof(struct iris_inst_hfi_gen2), GFP_KERNEL);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
new file mode 100644
index 000000000000..806f8bb7f505
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN2_DEFINES_H__
+#define __IRIS_HFI_GEN2_DEFINES_H__
+
+#include <linux/types.h>
+
+#define HFI_VIDEO_ARCH_LX 0x1
+
+#define HFI_CMD_BEGIN 0x01000000
+#define HFI_CMD_INIT 0x01000001
+#define HFI_CMD_POWER_COLLAPSE 0x01000002
+#define HFI_CMD_OPEN 0x01000003
+#define HFI_CMD_CLOSE 0x01000004
+#define HFI_CMD_START 0x01000005
+#define HFI_CMD_STOP 0x01000006
+#define HFI_CMD_DRAIN 0x01000007
+#define HFI_CMD_RESUME 0x01000008
+#define HFI_CMD_BUFFER 0x01000009
+#define HFI_CMD_SUBSCRIBE_MODE 0x0100000B
+#define HFI_CMD_SETTINGS_CHANGE 0x0100000C
+#define HFI_CMD_PAUSE 0x01000011
+#define HFI_CMD_END 0x01FFFFFF
+
+#define HFI_BITMASK_BITSTREAM_WIDTH 0xffff0000
+#define HFI_BITMASK_BITSTREAM_HEIGHT 0x0000ffff
+#define HFI_BITMASK_FRAME_MBS_ONLY_FLAG 0x00000001
+
+#define HFI_PROP_BEGIN 0x03000000
+#define HFI_PROP_IMAGE_VERSION 0x03000001
+#define HFI_PROP_INTRA_FRAME_POWER_COLLAPSE 0x03000002
+#define HFI_PROP_UBWC_MAX_CHANNELS 0x03000003
+#define HFI_PROP_UBWC_MAL_LENGTH 0x03000004
+#define HFI_PROP_UBWC_HBB 0x03000005
+#define HFI_PROP_UBWC_BANK_SWZL_LEVEL1 0x03000006
+#define HFI_PROP_UBWC_BANK_SWZL_LEVEL2 0x03000007
+#define HFI_PROP_UBWC_BANK_SWZL_LEVEL3 0x03000008
+#define HFI_PROP_UBWC_BANK_SPREADING 0x03000009
+#define HFI_PROP_CODEC 0x03000100
+#define HFI_PROP_COLOR_FORMAT 0x03000101
+#define HFI_PROP_BITSTREAM_RESOLUTION 0x03000103
+#define HFI_PROP_LINEAR_STRIDE_SCANLINE 0x03000104
+#define HFI_PROP_CROP_OFFSETS 0x03000105
+#define HFI_PROP_PROFILE 0x03000107
+#define HFI_PROP_LEVEL 0x03000108
+#define HFI_PROP_STAGE 0x0300010a
+#define HFI_PROP_PIPE 0x0300010b
+#define HFI_PROP_LUMA_CHROMA_BIT_DEPTH 0x0300010f
+#define HFI_PROP_CODED_FRAMES 0x03000120
+#define HFI_PROP_CABAC_SESSION 0x03000121
+#define HFI_PROP_BUFFER_HOST_MAX_COUNT 0x03000123
+#define HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT 0x03000124
+#define HFI_PROP_PIC_ORDER_CNT_TYPE 0x03000128
+#define HFI_PROP_QUALITY_MODE 0x03000148
+#define HFI_PROP_SIGNAL_COLOR_INFO 0x03000155
+#define HFI_PROP_PICTURE_TYPE 0x03000162
+#define HFI_PROP_DEC_DEFAULT_HEADER 0x03000168
+#define HFI_PROP_DEC_START_FROM_RAP_FRAME 0x03000169
+#define HFI_PROP_NO_OUTPUT 0x0300016a
+#define HFI_PROP_COMV_BUFFER_COUNT 0x03000193
+#define HFI_PROP_END 0x03FFFFFF
+
+#define HFI_SESSION_ERROR_BEGIN 0x04000000
+#define HFI_ERROR_UNKNOWN_SESSION 0x04000001
+#define HFI_ERROR_MAX_SESSIONS 0x04000002
+#define HFI_ERROR_FATAL 0x04000003
+#define HFI_ERROR_INVALID_STATE 0x04000004
+#define HFI_ERROR_INSUFFICIENT_RESOURCES 0x04000005
+#define HFI_ERROR_BUFFER_NOT_SET 0x04000006
+#define HFI_ERROR_STREAM_UNSUPPORTED 0x04000008
+#define HFI_SESSION_ERROR_END 0x04FFFFFF
+
+#define HFI_SYSTEM_ERROR_BEGIN 0x05000000
+#define HFI_SYS_ERROR_WD_TIMEOUT 0x05000001
+#define HFI_SYSTEM_ERROR_END 0x05FFFFFF
+
+#define HFI_INFORMATION_BEGIN 0x06000000
+#define HFI_INFO_UNSUPPORTED 0x06000001
+#define HFI_INFO_DATA_CORRUPT 0x06000002
+#define HFI_INFO_BUFFER_OVERFLOW 0x06000004
+#define HFI_INFO_HFI_FLAG_DRAIN_LAST 0x06000006
+#define HFI_INFO_HFI_FLAG_PSC_LAST 0x06000007
+#define HFI_INFORMATION_END 0x06FFFFFF
+
+enum hfi_property_mode_type {
+ HFI_MODE_PORT_SETTINGS_CHANGE = 0x00000001,
+ HFI_MODE_PROPERTY = 0x00000002,
+};
+
+enum hfi_color_format {
+ HFI_COLOR_FMT_OPAQUE = 0,
+ HFI_COLOR_FMT_NV12 = 1,
+ HFI_COLOR_FMT_NV12_UBWC = 2,
+ HFI_COLOR_FMT_P010 = 3,
+ HFI_COLOR_FMT_TP10_UBWC = 4,
+ HFI_COLOR_FMT_RGBA8888 = 5,
+ HFI_COLOR_FMT_RGBA8888_UBWC = 6,
+ HFI_COLOR_FMT_NV21 = 7,
+};
+
+enum hfi_codec_type {
+ HFI_CODEC_DECODE_AVC = 1,
+ HFI_CODEC_ENCODE_AVC = 2,
+};
+
+enum hfi_picture_type {
+ HFI_PICTURE_IDR = 0x00000001,
+ HFI_PICTURE_P = 0x00000002,
+ HFI_PICTURE_B = 0x00000004,
+ HFI_PICTURE_I = 0x00000008,
+ HFI_PICTURE_CRA = 0x00000010,
+ HFI_PICTURE_BLA = 0x00000020,
+};
+
+enum hfi_buffer_type {
+ HFI_BUFFER_BITSTREAM = 0x00000001,
+ HFI_BUFFER_RAW = 0x00000002,
+ HFI_BUFFER_METADATA = 0x00000003,
+ HFI_BUFFER_SUBCACHE = 0x00000004,
+ HFI_BUFFER_PARTIAL_DATA = 0x00000005,
+ HFI_BUFFER_DPB = 0x00000006,
+ HFI_BUFFER_BIN = 0x00000007,
+ HFI_BUFFER_LINE = 0x00000008,
+ HFI_BUFFER_ARP = 0x00000009,
+ HFI_BUFFER_COMV = 0x0000000A,
+ HFI_BUFFER_NON_COMV = 0x0000000B,
+ HFI_BUFFER_PERSIST = 0x0000000C,
+ HFI_BUFFER_VPSS = 0x0000000D,
+};
+
+enum hfi_buffer_host_flags {
+ HFI_BUF_HOST_FLAG_RELEASE = 0x00000001,
+ HFI_BUF_HOST_FLAG_READONLY = 0x00000010,
+ HFI_BUF_HOST_FLAG_CODEC_CONFIG = 0x00000100,
+ HFI_BUF_HOST_FLAGS_CB_NON_SECURE = 0x00000200,
+};
+
+enum hfi_buffer_firmware_flags {
+ HFI_BUF_FW_FLAG_RELEASE_DONE = 0x00000001,
+ HFI_BUF_FW_FLAG_READONLY = 0x00000010,
+ HFI_BUF_FW_FLAG_LAST = 0x10000000,
+ HFI_BUF_FW_FLAG_PSC_LAST = 0x20000000,
+};
+
+enum hfi_packet_firmware_flags {
+ HFI_FW_FLAGS_SUCCESS = 0x00000001,
+ HFI_FW_FLAGS_INFORMATION = 0x00000002,
+ HFI_FW_FLAGS_SESSION_ERROR = 0x00000004,
+ HFI_FW_FLAGS_SYSTEM_ERROR = 0x00000008,
+};
+
+struct hfi_debug_header {
+ u32 size;
+ u32 debug_level;
+ u32 reserved[2];
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
new file mode 100644
index 000000000000..d77fa29f44fc
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_hfi_common.h"
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_packet.h"
+
+u32 iris_hfi_gen2_get_color_primaries(u32 primaries)
+{
+ switch (primaries) {
+ case V4L2_COLORSPACE_DEFAULT:
+ return HFI_PRIMARIES_RESERVED;
+ case V4L2_COLORSPACE_REC709:
+ return HFI_PRIMARIES_BT709;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ return HFI_PRIMARIES_BT470_SYSTEM_M;
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ return HFI_PRIMARIES_BT470_SYSTEM_BG;
+ case V4L2_COLORSPACE_SMPTE170M:
+ return HFI_PRIMARIES_BT601_525;
+ case V4L2_COLORSPACE_SMPTE240M:
+ return HFI_PRIMARIES_SMPTE_ST240M;
+ case V4L2_COLORSPACE_BT2020:
+ return HFI_PRIMARIES_BT2020;
+ case V4L2_COLORSPACE_DCI_P3:
+ return HFI_PRIMARIES_SMPTE_RP431_2;
+ default:
+ return HFI_PRIMARIES_RESERVED;
+ }
+}
+
+u32 iris_hfi_gen2_get_transfer_char(u32 characterstics)
+{
+ switch (characterstics) {
+ case V4L2_XFER_FUNC_DEFAULT:
+ return HFI_TRANSFER_RESERVED;
+ case V4L2_XFER_FUNC_709:
+ return HFI_TRANSFER_BT709;
+ case V4L2_XFER_FUNC_SMPTE240M:
+ return HFI_TRANSFER_SMPTE_ST240M;
+ case V4L2_XFER_FUNC_SRGB:
+ return HFI_TRANSFER_SRGB_SYCC;
+ case V4L2_XFER_FUNC_SMPTE2084:
+ return HFI_TRANSFER_SMPTE_ST2084_PQ;
+ default:
+ return HFI_TRANSFER_RESERVED;
+ }
+}
+
+u32 iris_hfi_gen2_get_matrix_coefficients(u32 coefficients)
+{
+ switch (coefficients) {
+ case V4L2_YCBCR_ENC_DEFAULT:
+ return HFI_MATRIX_COEFF_RESERVED;
+ case V4L2_YCBCR_ENC_709:
+ return HFI_MATRIX_COEFF_BT709;
+ case V4L2_YCBCR_ENC_XV709:
+ return HFI_MATRIX_COEFF_BT709;
+ case V4L2_YCBCR_ENC_XV601:
+ return HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625;
+ case V4L2_YCBCR_ENC_601:
+ return HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ return HFI_MATRIX_COEFF_SMPTE_ST240;
+ case V4L2_YCBCR_ENC_BT2020:
+ return HFI_MATRIX_COEFF_BT2020_NON_CONSTANT;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ return HFI_MATRIX_COEFF_BT2020_CONSTANT;
+ default:
+ return HFI_MATRIX_COEFF_RESERVED;
+ }
+}
+
+u32 iris_hfi_gen2_get_color_info(u32 matrix_coeff, u32 transfer_char, u32 primaries,
+ u32 colour_description_present_flag, u32 full_range,
+ u32 video_format, u32 video_signal_type_present_flag)
+{
+ return (matrix_coeff & 0xFF) |
+ ((transfer_char << 8) & 0xFF00) |
+ ((primaries << 16) & 0xFF0000) |
+ ((colour_description_present_flag << 24) & 0x1000000) |
+ ((full_range << 25) & 0x2000000) |
+ ((video_format << 26) & 0x1C000000) |
+ ((video_signal_type_present_flag << 29) & 0x20000000);
+}
+
+static void iris_hfi_gen2_create_header(struct iris_hfi_header *hdr,
+ u32 session_id, u32 header_id)
+{
+ memset(hdr, 0, sizeof(*hdr));
+
+ hdr->size = sizeof(*hdr);
+ hdr->session_id = session_id;
+ hdr->header_id = header_id;
+ hdr->num_packets = 0;
+}
+
+static void iris_hfi_gen2_create_packet(struct iris_hfi_header *hdr, u32 pkt_type,
+ u32 pkt_flags, u32 payload_type, u32 port,
+ u32 packet_id, void *payload, u32 payload_size)
+{
+ struct iris_hfi_packet *pkt = (struct iris_hfi_packet *)((u8 *)hdr + hdr->size);
+ u32 pkt_size = sizeof(*pkt) + payload_size;
+
+ memset(pkt, 0, pkt_size);
+ pkt->size = pkt_size;
+ pkt->type = pkt_type;
+ pkt->flags = pkt_flags;
+ pkt->payload_info = payload_type;
+ pkt->port = port;
+ pkt->packet_id = packet_id;
+ if (payload_size)
+ memcpy(&pkt->payload[0], payload, payload_size);
+
+ hdr->num_packets++;
+ hdr->size += pkt->size;
+}
+
+void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ u32 payload = 0;
+
+ iris_hfi_gen2_create_header(hdr, 0, core->header_id++);
+
+ payload = HFI_VIDEO_ARCH_LX;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_CMD_INIT,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->max_channels;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_MAX_CHANNELS,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->mal_length;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_MAL_LENGTH,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->highest_bank_bit;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_HBB,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_swzl_level;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SWZL_LEVEL1,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_swz2_level;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SWZL_LEVEL2,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_swz3_level;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SWZL_LEVEL3,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_spreading;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SPREADING,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+}
+
+void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ iris_hfi_gen2_create_header(hdr, 0, core->header_id++);
+
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_IMAGE_VERSION,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_GET_PROPERTY),
+ HFI_PAYLOAD_NONE,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ NULL, 0);
+}
+
+void iris_hfi_gen2_packet_session_command(struct iris_inst *inst, u32 pkt_type,
+ u32 flags, u32 port, u32 session_id,
+ u32 payload_type, void *payload,
+ u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_core *core = inst->core;
+
+ iris_hfi_gen2_create_header(inst_hfi_gen2->packet, session_id, core->header_id++);
+
+ iris_hfi_gen2_create_packet(inst_hfi_gen2->packet,
+ pkt_type,
+ flags,
+ payload_type,
+ port,
+ core->packet_id++,
+ payload,
+ payload_size);
+}
+
+void iris_hfi_gen2_packet_session_property(struct iris_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload, u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_core *core = inst->core;
+
+ iris_hfi_gen2_create_header(inst_hfi_gen2->packet, inst->session_id, core->header_id++);
+
+ iris_hfi_gen2_create_packet(inst_hfi_gen2->packet,
+ pkt_type,
+ flags,
+ payload_type,
+ port,
+ core->packet_id++,
+ payload,
+ payload_size);
+}
+
+void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
+ struct iris_hfi_header *hdr)
+{
+ u32 payload = 1; /* HFI_TRUE */
+
+ iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++);
+
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_INTRA_FRAME_POWER_COLLAPSE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+}
+
+void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++);
+
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_CMD_POWER_COLLAPSE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_NONE,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ NULL, 0);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
new file mode 100644
index 000000000000..25b9582349ca
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN2_PACKET_H__
+#define __IRIS_HFI_GEN2_PACKET_H__
+
+#include "iris_hfi_gen2_defines.h"
+
+struct iris_core;
+
+/**
+ * struct iris_hfi_header
+ *
+ * @size: size of the total packet in bytes including hfi_header
+ * @session_id: For session level hfi_header session_id is non-zero.
+ * For system level hfi_header session_id is zero.
+ * @header_id: unique header id for each hfi_header
+ * @reserved: reserved for future use
+ * @num_packets: number of hfi_packet that are included with the hfi_header
+ */
+struct iris_hfi_header {
+ u32 size;
+ u32 session_id;
+ u32 header_id;
+ u32 reserved[4];
+ u32 num_packets;
+};
+
+/**
+ * struct iris_hfi_packet
+ *
+ * @size: size of the hfi_packet in bytes including payload
+ * @type: one of the below hfi_packet types:
+ * HFI_CMD_*,
+ * HFI_PROP_*,
+ * HFI_ERROR_*,
+ * HFI_INFO_*,
+ * HFI_SYS_ERROR_*
+ * @flags: hfi_packet flags. It is represented as bit masks.
+ * host packet flags are "enum hfi_packet_host_flags"
+ * firmware packet flags are "enum hfi_packet_firmware_flags"
+ * @payload_info: payload information indicated by "enum hfi_packet_payload_info"
+ * @port: hfi_packet port type indicated by "enum hfi_packet_port_type"
+ * This is bitmask and may be applicable to multiple ports.
+ * @packet_id: host hfi_packet contains unique packet id.
+ * firmware returns host packet id in response packet
+ * wherever applicable. If not applicable firmware sets it to zero.
+ * @reserved: reserved for future use.
+ * @payload: flexible array of payload having additional packet information.
+ */
+struct iris_hfi_packet {
+ u32 size;
+ u32 type;
+ u32 flags;
+ u32 payload_info;
+ u32 port;
+ u32 packet_id;
+ u32 reserved[2];
+ u32 payload[];
+};
+
+/**
+ * struct iris_hfi_buffer
+ *
+ * @type: buffer type indicated by "enum hfi_buffer_type"
+ * FW needs to return proper type for any buffer command.
+ * @index: index of the buffer
+ * @base_address: base address of the buffer.
+ * This buffer address is always 4KBytes aligned.
+ * @addr_offset: accessible buffer offset from base address
+ * Decoder bitstream buffer: 256 Bytes aligned
+ * Firmware can uniquely identify a buffer based on
+ * base_address & addr_offset.
+ * HW can read memory only from base_address+addr_offset.
+ * @buffer_size: accessible buffer size in bytes starting from addr_offset
+ * @data_offset: data starts from "base_address + addr_offset + data_offset"
+ * RAW buffer: data_offset is 0. Restriction: 4KBytes aligned
+ * decoder bitstream buffer: no restriction (can be any value)
+ * @data_size: data size in bytes
+ * @flags: buffer flags. It is represented as bit masks.
+ * host buffer flags are "enum hfi_buffer_host_flags"
+ * firmware buffer flags are "enum hfi_buffer_firmware_flags"
+ * @timestamp: timestamp of the buffer in nano seconds (ns)
+ * It is Presentation timestamp (PTS) for encoder & decoder.
+ * Decoder: it is pass through from bitstream to raw buffer.
+ * firmware does not need to return as part of input buffer done.
+ * For any internal buffers: there is no timestamp. Host sets as 0.
+ * @reserved: reserved for future use
+ */
+struct iris_hfi_buffer {
+ u32 type;
+ u32 index;
+ u64 base_address;
+ u32 addr_offset;
+ u32 buffer_size;
+ u32 data_offset;
+ u32 data_size;
+ u64 timestamp;
+ u32 flags;
+ u32 reserved[5];
+};
+
+u32 iris_hfi_gen2_get_color_primaries(u32 primaries);
+u32 iris_hfi_gen2_get_transfer_char(u32 characterstics);
+u32 iris_hfi_gen2_get_matrix_coefficients(u32 coefficients);
+u32 iris_hfi_gen2_get_color_info(u32 matrix_coeff, u32 transfer_char, u32 primaries,
+ u32 colour_description_present_flag, u32 full_range,
+ u32 video_format, u32 video_signal_type_present_flag);
+
+void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr);
+void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct iris_hfi_header *hdr);
+void iris_hfi_gen2_packet_session_command(struct iris_inst *inst, u32 pkt_type,
+ u32 flags, u32 port, u32 session_id,
+ u32 payload_type, void *payload,
+ u32 payload_size);
+void iris_hfi_gen2_packet_session_property(struct iris_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload, u32 payload_size);
+void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
+ struct iris_hfi_header *hdr);
+void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
new file mode 100644
index 000000000000..b75a01641d5d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -0,0 +1,934 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_defines.h"
+#include "iris_hfi_gen2_packet.h"
+#include "iris_vdec.h"
+#include "iris_vpu_buffer.h"
+#include "iris_vpu_common.h"
+
+struct iris_hfi_gen2_core_hfi_range {
+ u32 begin;
+ u32 end;
+ int (*handle)(struct iris_core *core, struct iris_hfi_packet *pkt);
+};
+
+struct iris_hfi_gen2_inst_hfi_range {
+ u32 begin;
+ u32 end;
+ int (*handle)(struct iris_inst *inst, struct iris_hfi_packet *pkt);
+};
+
+struct iris_hfi_gen2_packet_handle {
+ enum hfi_buffer_type type;
+ int (*handle)(struct iris_inst *inst, struct iris_hfi_packet *pkt);
+};
+
+static u32 iris_hfi_gen2_buf_type_to_driver(enum hfi_buffer_type buf_type)
+{
+ switch (buf_type) {
+ case HFI_BUFFER_BITSTREAM:
+ return BUF_INPUT;
+ case HFI_BUFFER_RAW:
+ return BUF_OUTPUT;
+ case HFI_BUFFER_BIN:
+ return BUF_BIN;
+ case HFI_BUFFER_ARP:
+ return BUF_ARP;
+ case HFI_BUFFER_COMV:
+ return BUF_COMV;
+ case HFI_BUFFER_NON_COMV:
+ return BUF_NON_COMV;
+ case HFI_BUFFER_LINE:
+ return BUF_LINE;
+ case HFI_BUFFER_DPB:
+ return BUF_DPB;
+ case HFI_BUFFER_PERSIST:
+ return BUF_PERSIST;
+ default:
+ return 0;
+ }
+}
+
+static bool iris_hfi_gen2_is_valid_hfi_buffer_type(u32 buffer_type)
+{
+ switch (buffer_type) {
+ case HFI_BUFFER_BITSTREAM:
+ case HFI_BUFFER_RAW:
+ case HFI_BUFFER_BIN:
+ case HFI_BUFFER_ARP:
+ case HFI_BUFFER_COMV:
+ case HFI_BUFFER_NON_COMV:
+ case HFI_BUFFER_LINE:
+ case HFI_BUFFER_DPB:
+ case HFI_BUFFER_PERSIST:
+ case HFI_BUFFER_VPSS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool iris_hfi_gen2_is_valid_hfi_port(u32 port, u32 buffer_type)
+{
+ if (port == HFI_PORT_NONE && buffer_type != HFI_BUFFER_PERSIST)
+ return false;
+
+ if (port != HFI_PORT_BITSTREAM && port != HFI_PORT_RAW)
+ return false;
+
+ return true;
+}
+
+static int iris_hfi_gen2_get_driver_buffer_flags(struct iris_inst *inst, u32 hfi_flags)
+{
+ u32 keyframe = HFI_PICTURE_IDR | HFI_PICTURE_I | HFI_PICTURE_CRA | HFI_PICTURE_BLA;
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 driver_flags = 0;
+
+ if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe)
+ driver_flags |= V4L2_BUF_FLAG_KEYFRAME;
+ else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_P)
+ driver_flags |= V4L2_BUF_FLAG_PFRAME;
+ else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_B)
+ driver_flags |= V4L2_BUF_FLAG_BFRAME;
+
+ if (inst_hfi_gen2->hfi_frame_info.data_corrupt || inst_hfi_gen2->hfi_frame_info.overflow)
+ driver_flags |= V4L2_BUF_FLAG_ERROR;
+
+ if (hfi_flags & HFI_BUF_FW_FLAG_LAST ||
+ hfi_flags & HFI_BUF_FW_FLAG_PSC_LAST)
+ driver_flags |= V4L2_BUF_FLAG_LAST;
+
+ return driver_flags;
+}
+
+static bool iris_hfi_gen2_validate_packet_payload(struct iris_hfi_packet *pkt)
+{
+ u32 payload_size = 0;
+
+ switch (pkt->payload_info) {
+ case HFI_PAYLOAD_U32:
+ case HFI_PAYLOAD_S32:
+ case HFI_PAYLOAD_Q16:
+ case HFI_PAYLOAD_U32_ENUM:
+ case HFI_PAYLOAD_32_PACKED:
+ payload_size = 4;
+ break;
+ case HFI_PAYLOAD_U64:
+ case HFI_PAYLOAD_S64:
+ case HFI_PAYLOAD_64_PACKED:
+ payload_size = 8;
+ break;
+ case HFI_PAYLOAD_STRUCTURE:
+ if (pkt->type == HFI_CMD_BUFFER)
+ payload_size = sizeof(struct iris_hfi_buffer);
+ break;
+ default:
+ payload_size = 0;
+ break;
+ }
+
+ if (pkt->size < sizeof(struct iris_hfi_packet) + payload_size)
+ return false;
+
+ return true;
+}
+
+static int iris_hfi_gen2_validate_packet(u8 *response_pkt, u8 *core_resp_pkt)
+{
+ u8 *response_limit = core_resp_pkt + IFACEQ_CORE_PKT_SIZE;
+ u32 response_pkt_size = *(u32 *)response_pkt;
+
+ if (!response_pkt_size)
+ return -EINVAL;
+
+ if (response_pkt_size < sizeof(struct iris_hfi_packet))
+ return -EINVAL;
+
+ if (response_pkt + response_pkt_size > response_limit)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int iris_hfi_gen2_validate_hdr_packet(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ struct iris_hfi_packet *packet;
+ int ret;
+ u8 *pkt;
+ u32 i;
+
+ if (hdr->size < sizeof(*hdr) + sizeof(*packet))
+ return -EINVAL;
+
+ pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+
+ for (i = 0; i < hdr->num_packets; i++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ ret = iris_hfi_gen2_validate_packet(pkt, core->response_packet);
+ if (ret)
+ return ret;
+
+ pkt += packet->size;
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_session_info(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_core *core = inst->core;
+ int ret = 0;
+ char *info;
+
+ switch (pkt->type) {
+ case HFI_INFO_UNSUPPORTED:
+ info = "unsupported";
+ break;
+ case HFI_INFO_DATA_CORRUPT:
+ info = "data corrupt";
+ inst_hfi_gen2->hfi_frame_info.data_corrupt = 1;
+ break;
+ case HFI_INFO_BUFFER_OVERFLOW:
+ info = "buffer overflow";
+ inst_hfi_gen2->hfi_frame_info.overflow = 1;
+ break;
+ case HFI_INFO_HFI_FLAG_DRAIN_LAST:
+ info = "drain last flag";
+ ret = iris_inst_sub_state_change_drain_last(inst);
+ break;
+ case HFI_INFO_HFI_FLAG_PSC_LAST:
+ info = "drc last flag";
+ ret = iris_inst_sub_state_change_drc_last(inst);
+ break;
+ default:
+ info = "unknown";
+ break;
+ }
+
+ dev_dbg(core->dev, "session info received %#x: %s\n",
+ pkt->type, info);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_error(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_core *core = inst->core;
+ char *error;
+
+ switch (pkt->type) {
+ case HFI_ERROR_MAX_SESSIONS:
+ error = "exceeded max sessions";
+ break;
+ case HFI_ERROR_UNKNOWN_SESSION:
+ error = "unknown session id";
+ break;
+ case HFI_ERROR_INVALID_STATE:
+ error = "invalid operation for current state";
+ break;
+ case HFI_ERROR_INSUFFICIENT_RESOURCES:
+ error = "insufficient resources";
+ break;
+ case HFI_ERROR_BUFFER_NOT_SET:
+ error = "internal buffers not set";
+ break;
+ case HFI_ERROR_FATAL:
+ error = "fatal error";
+ break;
+ case HFI_ERROR_STREAM_UNSUPPORTED:
+ error = "unsupported stream";
+ break;
+ default:
+ error = "unknown";
+ break;
+ }
+
+ dev_err(core->dev, "session error received %#x: %s\n", pkt->type, error);
+ iris_vb2_queue_error(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_system_error(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_inst *instance;
+
+ dev_err(core->dev, "received system error of type %#x\n", pkt->type);
+
+ core->state = IRIS_CORE_ERROR;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list)
+ iris_inst_change_state(instance, IRIS_INST_ERROR);
+ mutex_unlock(&core->lock);
+
+ schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10));
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_system_init(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+ core->state = IRIS_CORE_ERROR;
+ return 0;
+ }
+
+ complete(&core->core_init_done);
+
+ return 0;
+}
+
+static void iris_hfi_gen2_handle_session_close(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return;
+ }
+
+ complete(&inst->completion);
+}
+
+static int iris_hfi_gen2_handle_input_buffer(struct iris_inst *inst,
+ struct iris_hfi_buffer *buffer)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct iris_buffer *buf;
+ bool found = false;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (buf->index == buffer->index) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ return -EINVAL;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ buf->attr |= BUF_ATTR_DEQUEUED;
+
+ buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, buffer->flags);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_output_buffer(struct iris_inst *inst,
+ struct iris_hfi_buffer *hfi_buffer)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct iris_buffer *buf;
+ bool found = false;
+ int ret;
+
+ if (hfi_buffer->flags & HFI_BUF_FW_FLAG_LAST) {
+ ret = iris_inst_sub_state_change_drain_last(inst);
+ if (ret)
+ return ret;
+ }
+
+ if (hfi_buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST) {
+ ret = iris_inst_sub_state_change_drc_last(inst);
+ if (ret)
+ return ret;
+ }
+
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (buf->index == hfi_buffer->index &&
+ buf->device_addr == hfi_buffer->base_address &&
+ buf->data_offset == hfi_buffer->data_offset) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ return -EINVAL;
+
+ buf->data_offset = hfi_buffer->data_offset;
+ buf->data_size = hfi_buffer->data_size;
+ buf->timestamp = hfi_buffer->timestamp;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ buf->attr |= BUF_ATTR_DEQUEUED;
+
+ buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, hfi_buffer->flags);
+
+ return 0;
+}
+
+static void iris_hfi_gen2_handle_dequeue_buffers(struct iris_inst *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf = NULL;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEQUEUED) {
+ buf->attr &= ~BUF_ATTR_DEQUEUED;
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEQUEUED) {
+ buf->attr &= ~BUF_ATTR_DEQUEUED;
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+}
+
+static int iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst,
+ struct iris_hfi_buffer *buffer)
+{
+ u32 buf_type = iris_hfi_gen2_buf_type_to_driver(buffer->type);
+ struct iris_buffers *buffers = &inst->buffers[buf_type];
+ struct iris_buffer *buf, *iter;
+ bool found = false;
+ int ret = 0;
+
+ list_for_each_entry(iter, &buffers->list, list) {
+ if (iter->device_addr == buffer->base_address) {
+ found = true;
+ buf = iter;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ if (buf->attr & BUF_ATTR_PENDING_RELEASE)
+ ret = iris_destroy_internal_buffer(inst, buf);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_stop(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret = 0;
+
+ if (pkt->port == HFI_PORT_RAW)
+ ret = iris_inst_sub_state_change_pause(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ else if (pkt->port == HFI_PORT_BITSTREAM)
+ ret = iris_inst_sub_state_change_pause(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ complete(&inst->completion);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_buffer(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_hfi_buffer *buffer;
+
+ if (pkt->payload_info == HFI_PAYLOAD_NONE)
+ return 0;
+
+ if (!iris_hfi_gen2_validate_packet_payload(pkt)) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return 0;
+ }
+
+ buffer = (struct iris_hfi_buffer *)((u8 *)pkt + sizeof(*pkt));
+ if (!iris_hfi_gen2_is_valid_hfi_buffer_type(buffer->type))
+ return 0;
+
+ if (!iris_hfi_gen2_is_valid_hfi_port(pkt->port, buffer->type))
+ return 0;
+
+ if (buffer->type == HFI_BUFFER_BITSTREAM)
+ return iris_hfi_gen2_handle_input_buffer(inst, buffer);
+ else if (buffer->type == HFI_BUFFER_RAW)
+ return iris_hfi_gen2_handle_output_buffer(inst, buffer);
+ else
+ return iris_hfi_gen2_handle_release_internal_buffer(inst, buffer);
+}
+
+static int iris_hfi_gen2_handle_session_drain(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret = 0;
+
+ if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return 0;
+ }
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN)
+ ret = iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_INPUT_PAUSE);
+
+ return ret;
+}
+
+static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp;
+ u32 primaries, matrix_coeff, transfer_char;
+ struct hfi_subscription_params subsc_params;
+ u32 colour_description_present_flag;
+ u32 video_signal_type_present_flag;
+ struct iris_core *core = inst->core;
+ u32 full_range, width, height;
+ struct vb2_queue *dst_q;
+ struct v4l2_ctrl *ctrl;
+
+ subsc_params = inst_hfi_gen2->src_subcr_params;
+ width = (subsc_params.bitstream_resolution &
+ HFI_BITMASK_BITSTREAM_WIDTH) >> 16;
+ height = subsc_params.bitstream_resolution &
+ HFI_BITMASK_BITSTREAM_HEIGHT;
+
+ pixmp_ip->width = width;
+ pixmp_ip->height = height;
+
+ pixmp_op->width = ALIGN(width, 128);
+ pixmp_op->height = ALIGN(height, 32);
+ pixmp_op->plane_fmt[0].bytesperline = ALIGN(width, 128);
+ pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+
+ matrix_coeff = subsc_params.color_info & 0xFF;
+ transfer_char = (subsc_params.color_info & 0xFF00) >> 8;
+ primaries = (subsc_params.color_info & 0xFF0000) >> 16;
+ colour_description_present_flag =
+ (subsc_params.color_info & 0x1000000) >> 24;
+ full_range = (subsc_params.color_info & 0x2000000) >> 25;
+ video_signal_type_present_flag =
+ (subsc_params.color_info & 0x20000000) >> 29;
+
+ pixmp_op->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pixmp_op->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pixmp_op->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pixmp_op->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ if (video_signal_type_present_flag) {
+ pixmp_op->quantization =
+ full_range ?
+ V4L2_QUANTIZATION_FULL_RANGE :
+ V4L2_QUANTIZATION_LIM_RANGE;
+ if (colour_description_present_flag) {
+ pixmp_op->colorspace =
+ iris_hfi_get_v4l2_color_primaries(primaries);
+ pixmp_op->xfer_func =
+ iris_hfi_get_v4l2_transfer_char(transfer_char);
+ pixmp_op->ycbcr_enc =
+ iris_hfi_get_v4l2_matrix_coefficients(matrix_coeff);
+ }
+ }
+
+ pixmp_ip->colorspace = pixmp_op->colorspace;
+ pixmp_ip->xfer_func = pixmp_op->xfer_func;
+ pixmp_ip->ycbcr_enc = pixmp_op->ycbcr_enc;
+ pixmp_ip->quantization = pixmp_op->quantization;
+
+ inst->crop.top = subsc_params.crop_offsets[0] & 0xFFFF;
+ inst->crop.left = (subsc_params.crop_offsets[0] >> 16) & 0xFFFF;
+ inst->crop.height = pixmp_ip->height -
+ (subsc_params.crop_offsets[1] & 0xFFFF) - inst->crop.top;
+ inst->crop.width = pixmp_ip->width -
+ ((subsc_params.crop_offsets[1] >> 16) & 0xFFFF) - inst->crop.left;
+
+ inst->fw_caps[PROFILE].value = subsc_params.profile;
+ inst->fw_caps[LEVEL].value = subsc_params.level;
+ inst->fw_caps[POC].value = subsc_params.pic_order_cnt;
+
+ if (subsc_params.bit_depth != BIT_DEPTH_8 ||
+ !(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) {
+ dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n",
+ subsc_params.bit_depth, subsc_params.coded_frames);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+
+ inst->fw_min_count = subsc_params.fw_min_count;
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage;
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, inst->buffers[BUF_OUTPUT].min_count);
+
+ dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ dst_q->min_reqbufs_allocation = inst->buffers[BUF_OUTPUT].min_count;
+}
+
+static int iris_hfi_gen2_handle_src_change(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret;
+
+ if (pkt->port != HFI_PORT_BITSTREAM)
+ return 0;
+
+ ret = iris_inst_sub_state_change_drc(inst);
+ if (ret)
+ return ret;
+
+ iris_hfi_gen2_read_input_subcr_params(inst);
+ iris_vdec_src_change(inst);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_session_command(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret = 0;
+
+ switch (pkt->type) {
+ case HFI_CMD_CLOSE:
+ iris_hfi_gen2_handle_session_close(inst, pkt);
+ break;
+ case HFI_CMD_STOP:
+ iris_hfi_gen2_handle_session_stop(inst, pkt);
+ break;
+ case HFI_CMD_BUFFER:
+ ret = iris_hfi_gen2_handle_session_buffer(inst, pkt);
+ break;
+ case HFI_CMD_SETTINGS_CHANGE:
+ ret = iris_hfi_gen2_handle_src_change(inst, pkt);
+ break;
+ case HFI_CMD_DRAIN:
+ ret = iris_hfi_gen2_handle_session_drain(inst, pkt);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ if (pkt->port != HFI_PORT_BITSTREAM)
+ return 0;
+
+ if (pkt->flags & HFI_FW_FLAGS_INFORMATION)
+ return 0;
+
+ switch (pkt->type) {
+ case HFI_PROP_BITSTREAM_RESOLUTION:
+ inst_hfi_gen2->src_subcr_params.bitstream_resolution = pkt->payload[0];
+ break;
+ case HFI_PROP_CROP_OFFSETS:
+ inst_hfi_gen2->src_subcr_params.crop_offsets[0] = pkt->payload[0];
+ inst_hfi_gen2->src_subcr_params.crop_offsets[1] = pkt->payload[1];
+ break;
+ case HFI_PROP_CODED_FRAMES:
+ inst_hfi_gen2->src_subcr_params.coded_frames = pkt->payload[0];
+ break;
+ case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT:
+ inst_hfi_gen2->src_subcr_params.fw_min_count = pkt->payload[0];
+ break;
+ case HFI_PROP_PIC_ORDER_CNT_TYPE:
+ inst_hfi_gen2->src_subcr_params.pic_order_cnt = pkt->payload[0];
+ break;
+ case HFI_PROP_SIGNAL_COLOR_INFO:
+ inst_hfi_gen2->src_subcr_params.color_info = pkt->payload[0];
+ break;
+ case HFI_PROP_PROFILE:
+ inst_hfi_gen2->src_subcr_params.profile = pkt->payload[0];
+ break;
+ case HFI_PROP_LEVEL:
+ inst_hfi_gen2->src_subcr_params.level = pkt->payload[0];
+ break;
+ case HFI_PROP_PICTURE_TYPE:
+ inst_hfi_gen2->hfi_frame_info.picture_type = pkt->payload[0];
+ break;
+ case HFI_PROP_NO_OUTPUT:
+ inst_hfi_gen2->hfi_frame_info.no_output = 1;
+ break;
+ case HFI_PROP_QUALITY_MODE:
+ case HFI_PROP_STAGE:
+ case HFI_PROP_PIPE:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_image_version_property(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ u8 *str_image_version = (u8 *)pkt + sizeof(*pkt);
+ u32 req_bytes = pkt->size - sizeof(*pkt);
+ char fw_version[IRIS_FW_VERSION_LENGTH];
+ u32 i;
+
+ if (req_bytes < IRIS_FW_VERSION_LENGTH - 1)
+ return -EINVAL;
+
+ for (i = 0; i < IRIS_FW_VERSION_LENGTH - 1; i++) {
+ if (str_image_version[i] != '\0')
+ fw_version[i] = str_image_version[i];
+ else
+ fw_version[i] = ' ';
+ }
+ fw_version[i] = '\0';
+ dev_dbg(core->dev, "firmware version: %s\n", fw_version);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_system_property(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ switch (pkt->type) {
+ case HFI_PROP_IMAGE_VERSION:
+ return iris_hfi_gen2_handle_image_version_property(core, pkt);
+ default:
+ return 0;
+ }
+}
+
+static int iris_hfi_gen2_handle_system_response(struct iris_core *core,
+ struct iris_hfi_header *hdr)
+{
+ u8 *start_pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ struct iris_hfi_packet *packet;
+ u32 i, j;
+ u8 *pkt;
+ int ret;
+ static const struct iris_hfi_gen2_core_hfi_range range[] = {
+ {HFI_SYSTEM_ERROR_BEGIN, HFI_SYSTEM_ERROR_END, iris_hfi_gen2_handle_system_error },
+ {HFI_PROP_BEGIN, HFI_PROP_END, iris_hfi_gen2_handle_system_property },
+ {HFI_CMD_BEGIN, HFI_CMD_END, iris_hfi_gen2_handle_system_init },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(range); i++) {
+ pkt = start_pkt;
+ for (j = 0; j < hdr->num_packets; j++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ if (packet->flags & HFI_FW_FLAGS_SYSTEM_ERROR) {
+ ret = iris_hfi_gen2_handle_system_error(core, packet);
+ return ret;
+ }
+
+ if (packet->type > range[i].begin && packet->type < range[i].end) {
+ ret = range[i].handle(core, packet);
+ if (ret)
+ return ret;
+
+ if (packet->type > HFI_SYSTEM_ERROR_BEGIN &&
+ packet->type < HFI_SYSTEM_ERROR_END)
+ return 0;
+ }
+ pkt += packet->size;
+ }
+ }
+
+ return 0;
+}
+
+static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp;
+ u32 bottom_offset = (pixmp_ip->height - inst->crop.height);
+ u32 right_offset = (pixmp_ip->width - inst->crop.width);
+ struct hfi_subscription_params *subsc_params;
+ u32 primaries, matrix_coeff, transfer_char;
+ u32 colour_description_present_flag = 0;
+ u32 video_signal_type_present_flag = 0;
+ u32 full_range, video_format = 0;
+ u32 left_offset = inst->crop.left;
+ u32 top_offset = inst->crop.top;
+
+ subsc_params = &inst_hfi_gen2->src_subcr_params;
+ subsc_params->bitstream_resolution =
+ pixmp_ip->width << 16 | pixmp_ip->height;
+ subsc_params->crop_offsets[0] =
+ left_offset << 16 | top_offset;
+ subsc_params->crop_offsets[1] =
+ right_offset << 16 | bottom_offset;
+ subsc_params->fw_min_count = inst->buffers[BUF_OUTPUT].min_count;
+
+ primaries = iris_hfi_gen2_get_color_primaries(pixmp_op->colorspace);
+ matrix_coeff = iris_hfi_gen2_get_matrix_coefficients(pixmp_op->ycbcr_enc);
+ transfer_char = iris_hfi_gen2_get_transfer_char(pixmp_op->xfer_func);
+ full_range = pixmp_op->quantization == V4L2_QUANTIZATION_FULL_RANGE ? 1 : 0;
+ subsc_params->color_info =
+ iris_hfi_gen2_get_color_info(matrix_coeff, transfer_char, primaries,
+ colour_description_present_flag,
+ full_range, video_format,
+ video_signal_type_present_flag);
+
+ subsc_params->profile = inst->fw_caps[PROFILE].value;
+ subsc_params->level = inst->fw_caps[LEVEL].value;
+ subsc_params->pic_order_cnt = inst->fw_caps[POC].value;
+ subsc_params->bit_depth = inst->fw_caps[BIT_DEPTH].value;
+ if (inst->fw_caps[CODED_FRAMES].value ==
+ CODED_FRAMES_PROGRESSIVE)
+ subsc_params->coded_frames = HFI_BITMASK_FRAME_MBS_ONLY_FLAG;
+ else
+ subsc_params->coded_frames = 0;
+}
+
+static int iris_hfi_gen2_handle_session_response(struct iris_core *core,
+ struct iris_hfi_header *hdr)
+{
+ u8 *pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2;
+ struct iris_hfi_packet *packet;
+ struct iris_inst *inst;
+ bool dequeue = false;
+ int ret = 0;
+ u32 i, j;
+ static const struct iris_hfi_gen2_inst_hfi_range range[] = {
+ {HFI_SESSION_ERROR_BEGIN, HFI_SESSION_ERROR_END,
+ iris_hfi_gen2_handle_session_error},
+ {HFI_INFORMATION_BEGIN, HFI_INFORMATION_END,
+ iris_hfi_gen2_handle_session_info},
+ {HFI_PROP_BEGIN, HFI_PROP_END,
+ iris_hfi_gen2_handle_session_property},
+ {HFI_CMD_BEGIN, HFI_CMD_END,
+ iris_hfi_gen2_handle_session_command },
+ };
+
+ inst = iris_get_instance(core, hdr->session_id);
+ if (!inst)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ memset(&inst_hfi_gen2->hfi_frame_info, 0, sizeof(struct iris_hfi_frame_info));
+
+ for (i = 0; i < hdr->num_packets; i++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ if (packet->type == HFI_CMD_SETTINGS_CHANGE) {
+ if (packet->port == HFI_PORT_BITSTREAM) {
+ iris_hfi_gen2_init_src_change_param(inst);
+ break;
+ }
+ }
+ pkt += packet->size;
+ }
+
+ pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ for (i = 0; i < ARRAY_SIZE(range); i++) {
+ pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ for (j = 0; j < hdr->num_packets; j++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ if (packet->flags & HFI_FW_FLAGS_SESSION_ERROR)
+ iris_hfi_gen2_handle_session_error(inst, packet);
+
+ if (packet->type > range[i].begin && packet->type < range[i].end) {
+ dequeue |= (packet->type == HFI_CMD_BUFFER);
+ ret = range[i].handle(inst, packet);
+ if (ret)
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+ pkt += packet->size;
+ }
+ }
+
+ if (dequeue)
+ iris_hfi_gen2_handle_dequeue_buffers(inst);
+
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_response(struct iris_core *core, void *response)
+{
+ struct iris_hfi_header *hdr = (struct iris_hfi_header *)response;
+ int ret;
+
+ ret = iris_hfi_gen2_validate_hdr_packet(core, hdr);
+ if (ret)
+ return iris_hfi_gen2_handle_system_error(core, NULL);
+
+ if (!hdr->session_id)
+ return iris_hfi_gen2_handle_system_response(core, hdr);
+ else
+ return iris_hfi_gen2_handle_session_response(core, hdr);
+}
+
+static void iris_hfi_gen2_flush_debug_queue(struct iris_core *core, u8 *packet)
+{
+ struct hfi_debug_header *pkt;
+ u8 *log;
+
+ while (!iris_hfi_queue_dbg_read(core, packet)) {
+ pkt = (struct hfi_debug_header *)packet;
+
+ if (pkt->size < sizeof(*pkt))
+ continue;
+
+ if (pkt->size >= IFACEQ_CORE_PKT_SIZE)
+ continue;
+
+ packet[pkt->size] = '\0';
+ log = (u8 *)packet + sizeof(*pkt) + 1;
+ dev_dbg(core->dev, "%s", log);
+ }
+}
+
+static void iris_hfi_gen2_response_handler(struct iris_core *core)
+{
+ if (iris_vpu_watchdog(core, core->intr_status)) {
+ struct iris_hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT};
+
+ dev_err(core->dev, "cpu watchdog error received\n");
+ core->state = IRIS_CORE_ERROR;
+ iris_hfi_gen2_handle_system_error(core, &pkt);
+
+ return;
+ }
+
+ memset(core->response_packet, 0, sizeof(struct iris_hfi_header));
+ while (!iris_hfi_queue_msg_read(core, core->response_packet)) {
+ iris_hfi_gen2_handle_response(core, core->response_packet);
+ memset(core->response_packet, 0, sizeof(struct iris_hfi_header));
+ }
+
+ iris_hfi_gen2_flush_debug_queue(core, core->response_packet);
+}
+
+static const struct iris_hfi_response_ops iris_hfi_gen2_response_ops = {
+ .hfi_response_handler = iris_hfi_gen2_response_handler,
+};
+
+void iris_hfi_gen2_response_ops_init(struct iris_core *core)
+{
+ core->hfi_response_ops = &iris_hfi_gen2_response_ops;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
new file mode 100644
index 000000000000..fac7df0c4d1a
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "iris_core.h"
+#include "iris_hfi_queue.h"
+#include "iris_vpu_common.h"
+
+static int iris_hfi_queue_write(struct iris_iface_q_info *qinfo, void *packet, u32 packet_size)
+{
+ struct iris_hfi_queue_header *queue = qinfo->qhdr;
+ u32 write_idx = queue->write_idx * sizeof(u32);
+ u32 read_idx = queue->read_idx * sizeof(u32);
+ u32 empty_space, new_write_idx, residue;
+ u32 *write_ptr;
+
+ if (write_idx < read_idx)
+ empty_space = read_idx - write_idx;
+ else
+ empty_space = IFACEQ_QUEUE_SIZE - (write_idx - read_idx);
+ if (empty_space < packet_size)
+ return -ENOSPC;
+
+ queue->tx_req = 0;
+
+ new_write_idx = write_idx + packet_size;
+ write_ptr = (u32 *)((u8 *)qinfo->kernel_vaddr + write_idx);
+
+ if (write_ptr < (u32 *)qinfo->kernel_vaddr ||
+ write_ptr > (u32 *)(qinfo->kernel_vaddr +
+ IFACEQ_QUEUE_SIZE))
+ return -EINVAL;
+
+ if (new_write_idx < IFACEQ_QUEUE_SIZE) {
+ memcpy(write_ptr, packet, packet_size);
+ } else {
+ residue = new_write_idx - IFACEQ_QUEUE_SIZE;
+ memcpy(write_ptr, packet, (packet_size - residue));
+ memcpy(qinfo->kernel_vaddr,
+ packet + (packet_size - residue), residue);
+ new_write_idx = residue;
+ }
+
+ /* Make sure packet is written before updating the write index */
+ mb();
+ queue->write_idx = new_write_idx / sizeof(u32);
+
+ /* Make sure write index is updated before an interrupt is raised */
+ mb();
+
+ return 0;
+}
+
+static int iris_hfi_queue_read(struct iris_iface_q_info *qinfo, void *packet)
+{
+ struct iris_hfi_queue_header *queue = qinfo->qhdr;
+ u32 write_idx = queue->write_idx * sizeof(u32);
+ u32 read_idx = queue->read_idx * sizeof(u32);
+ u32 packet_size, receive_request = 0;
+ u32 new_read_idx, residue;
+ u32 *read_ptr;
+ int ret = 0;
+
+ if (queue->queue_type == IFACEQ_MSGQ_ID)
+ receive_request = 1;
+
+ if (read_idx == write_idx) {
+ queue->rx_req = receive_request;
+ /* Ensure qhdr is updated in main memory */
+ mb();
+ return -ENODATA;
+ }
+
+ read_ptr = qinfo->kernel_vaddr + read_idx;
+ if (read_ptr < (u32 *)qinfo->kernel_vaddr ||
+ read_ptr > (u32 *)(qinfo->kernel_vaddr +
+ IFACEQ_QUEUE_SIZE - sizeof(*read_ptr)))
+ return -ENODATA;
+
+ packet_size = *read_ptr;
+ if (!packet_size)
+ return -EINVAL;
+
+ new_read_idx = read_idx + packet_size;
+ if (packet_size <= IFACEQ_CORE_PKT_SIZE) {
+ if (new_read_idx < IFACEQ_QUEUE_SIZE) {
+ memcpy(packet, read_ptr, packet_size);
+ } else {
+ residue = new_read_idx - IFACEQ_QUEUE_SIZE;
+ memcpy(packet, read_ptr, (packet_size - residue));
+ memcpy((packet + (packet_size - residue)),
+ qinfo->kernel_vaddr, residue);
+ new_read_idx = residue;
+ }
+ } else {
+ new_read_idx = write_idx;
+ ret = -EBADMSG;
+ }
+
+ queue->rx_req = receive_request;
+
+ queue->read_idx = new_read_idx / sizeof(u32);
+ /* Ensure qhdr is updated in main memory */
+ mb();
+
+ return ret;
+}
+
+int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_size)
+{
+ struct iris_iface_q_info *q_info = &core->command_queue;
+
+ if (core->state == IRIS_CORE_ERROR)
+ return -EINVAL;
+
+ if (!iris_hfi_queue_write(q_info, pkt, pkt_size)) {
+ iris_vpu_raise_interrupt(core);
+ } else {
+ dev_err(core->dev, "queue full\n");
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt, u32 pkt_size)
+{
+ int ret;
+
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ goto exit;
+
+ mutex_lock(&core->lock);
+ ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt_size);
+ if (ret) {
+ mutex_unlock(&core->lock);
+ goto exit;
+ }
+ mutex_unlock(&core->lock);
+
+ pm_runtime_mark_last_busy(core->dev);
+ pm_runtime_put_autosuspend(core->dev);
+
+ return 0;
+
+exit:
+ pm_runtime_put_sync(core->dev);
+
+ return ret;
+}
+
+int iris_hfi_queue_msg_read(struct iris_core *core, void *pkt)
+{
+ struct iris_iface_q_info *q_info = &core->message_queue;
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (iris_hfi_queue_read(q_info, pkt)) {
+ ret = -ENODATA;
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+int iris_hfi_queue_dbg_read(struct iris_core *core, void *pkt)
+{
+ struct iris_iface_q_info *q_info = &core->debug_queue;
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (iris_hfi_queue_read(q_info, pkt)) {
+ ret = -ENODATA;
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static void iris_hfi_queue_set_header(struct iris_core *core, u32 queue_id,
+ struct iris_iface_q_info *iface_q)
+{
+ iface_q->qhdr->status = 0x1;
+ iface_q->qhdr->start_addr = iface_q->device_addr;
+ iface_q->qhdr->header_type = IFACEQ_DFLT_QHDR;
+ iface_q->qhdr->queue_type = queue_id;
+ iface_q->qhdr->q_size = IFACEQ_QUEUE_SIZE / sizeof(u32);
+ iface_q->qhdr->pkt_size = 0; /* variable packet size */
+ iface_q->qhdr->rx_wm = 0x1;
+ iface_q->qhdr->tx_wm = 0x1;
+ iface_q->qhdr->rx_req = 0x1;
+ iface_q->qhdr->tx_req = 0x0;
+ iface_q->qhdr->rx_irq_status = 0x0;
+ iface_q->qhdr->tx_irq_status = 0x0;
+ iface_q->qhdr->read_idx = 0x0;
+ iface_q->qhdr->write_idx = 0x0;
+
+ /*
+ * Set receive request to zero on debug queue as there is no
+ * need of interrupt from video hardware for debug messages
+ */
+ if (queue_id == IFACEQ_DBGQ_ID)
+ iface_q->qhdr->rx_req = 0;
+}
+
+static void
+iris_hfi_queue_init(struct iris_core *core, u32 queue_id, struct iris_iface_q_info *iface_q)
+{
+ struct iris_hfi_queue_table_header *q_tbl_hdr = core->iface_q_table_vaddr;
+ u32 offset = sizeof(*q_tbl_hdr) + (queue_id * IFACEQ_QUEUE_SIZE);
+
+ iface_q->device_addr = core->iface_q_table_daddr + offset;
+ iface_q->kernel_vaddr =
+ (void *)((char *)core->iface_q_table_vaddr + offset);
+ iface_q->qhdr = &q_tbl_hdr->q_hdr[queue_id];
+
+ iris_hfi_queue_set_header(core, queue_id, iface_q);
+}
+
+static void iris_hfi_queue_deinit(struct iris_iface_q_info *iface_q)
+{
+ iface_q->qhdr = NULL;
+ iface_q->kernel_vaddr = NULL;
+ iface_q->device_addr = 0;
+}
+
+int iris_hfi_queues_init(struct iris_core *core)
+{
+ struct iris_hfi_queue_table_header *q_tbl_hdr;
+ u32 queue_size;
+
+ /* Iris hardware requires 4K queue alignment */
+ queue_size = ALIGN((sizeof(*q_tbl_hdr) + (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ)), SZ_4K);
+ core->iface_q_table_vaddr = dma_alloc_attrs(core->dev, queue_size,
+ &core->iface_q_table_daddr,
+ GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
+ if (!core->iface_q_table_vaddr) {
+ dev_err(core->dev, "queues alloc and map failed\n");
+ return -ENOMEM;
+ }
+
+ core->sfr_vaddr = dma_alloc_attrs(core->dev, SFR_SIZE,
+ &core->sfr_daddr,
+ GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
+ if (!core->sfr_vaddr) {
+ dev_err(core->dev, "sfr alloc and map failed\n");
+ dma_free_attrs(core->dev, sizeof(*q_tbl_hdr), core->iface_q_table_vaddr,
+ core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
+ return -ENOMEM;
+ }
+
+ iris_hfi_queue_init(core, IFACEQ_CMDQ_ID, &core->command_queue);
+ iris_hfi_queue_init(core, IFACEQ_MSGQ_ID, &core->message_queue);
+ iris_hfi_queue_init(core, IFACEQ_DBGQ_ID, &core->debug_queue);
+
+ q_tbl_hdr = (struct iris_hfi_queue_table_header *)core->iface_q_table_vaddr;
+ q_tbl_hdr->version = 0;
+ q_tbl_hdr->device_addr = (void *)core;
+ strscpy(q_tbl_hdr->name, "iris-hfi-queues", sizeof(q_tbl_hdr->name));
+ q_tbl_hdr->size = sizeof(*q_tbl_hdr);
+ q_tbl_hdr->qhdr0_offset = sizeof(*q_tbl_hdr) -
+ (IFACEQ_NUMQ * sizeof(struct iris_hfi_queue_header));
+ q_tbl_hdr->qhdr_size = sizeof(q_tbl_hdr->q_hdr[0]);
+ q_tbl_hdr->num_q = IFACEQ_NUMQ;
+ q_tbl_hdr->num_active_q = IFACEQ_NUMQ;
+
+ /* Write sfr size in first word to be used by firmware */
+ *((u32 *)core->sfr_vaddr) = SFR_SIZE;
+
+ return 0;
+}
+
+void iris_hfi_queues_deinit(struct iris_core *core)
+{
+ u32 queue_size;
+
+ if (!core->iface_q_table_vaddr)
+ return;
+
+ iris_hfi_queue_deinit(&core->debug_queue);
+ iris_hfi_queue_deinit(&core->message_queue);
+ iris_hfi_queue_deinit(&core->command_queue);
+
+ dma_free_attrs(core->dev, SFR_SIZE, core->sfr_vaddr,
+ core->sfr_daddr, DMA_ATTR_WRITE_COMBINE);
+
+ core->sfr_vaddr = NULL;
+ core->sfr_daddr = 0;
+
+ queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) +
+ (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K);
+
+ dma_free_attrs(core->dev, queue_size, core->iface_q_table_vaddr,
+ core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
+
+ core->iface_q_table_vaddr = NULL;
+ core->iface_q_table_daddr = 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.h b/drivers/media/platform/qcom/iris/iris_hfi_queue.h
new file mode 100644
index 000000000000..2174fc5ce618
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_QUEUE_H__
+#define __IRIS_HFI_QUEUE_H__
+
+struct iris_core;
+
+/*
+ * Max 64 Buffers ( 32 input buffers and 32 output buffers)
+ * can be queued by v4l2 framework at any given time.
+ */
+#define IFACEQ_MAX_BUF_COUNT 64
+/*
+ * Max session supported are 16.
+ * this value is used to calcualte the size of
+ * individual shared queue.
+ */
+#define IFACE_MAX_PARALLEL_SESSIONS 16
+#define IFACEQ_DFLT_QHDR 0x0101
+#define IFACEQ_MAX_PKT_SIZE 1024 /* Maximum size of a packet in the queue */
+
+/*
+ * SFR: Subsystem Failure Reason
+ * when hardware goes into bad state/failure, firmware fills this memory
+ * and driver will get to know the actual failure reason from this SFR buffer.
+ */
+#define SFR_SIZE SZ_4K /* Iris hardware requires 4K queue alignment */
+
+#define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \
+ IFACEQ_MAX_BUF_COUNT * IFACE_MAX_PARALLEL_SESSIONS)
+
+/*
+ * Memory layout of the shared queues:
+ *
+ * ||=================|| ^ ^ ^
+ * || || | | |
+ * || Queue Table || 288 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||-----------------|| V | |
+ * ||-----------------|| ^ | |
+ * || || | | |
+ * || Command Queue || 56 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||-----------------|| V 456 Bytes |
+ * ||-----------------|| ^ | |
+ * || || | | |
+ * || Message Queue || 56 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||-----------------|| V | Buffer size aligned to 4k
+ * ||-----------------|| ^ | Overall Queue Size = 2,404 KB
+ * || || | | |
+ * || Debug Queue || 56 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||=================|| V V |
+ * ||=================|| ^ |
+ * || || | |
+ * || Command || 800 KB |
+ * || Queue || | |
+ * || || | |
+ * ||=================|| V |
+ * ||=================|| ^ |
+ * || || | |
+ * || Message || 800 KB |
+ * || Queue || | |
+ * || || | |
+ * ||=================|| V |
+ * ||=================|| ^ |
+ * || || | |
+ * || Debug || 800 KB |
+ * || Queue || | |
+ * || || | |
+ * ||=================|| V |
+ * || || |
+ * ||=================|| V
+ */
+
+/*
+ * Shared queues are used for communication between driver and firmware.
+ * There are 3 types of queues:
+ * Command queue - driver to write any command to firmware.
+ * Message queue - firmware to send any response to driver.
+ * Debug queue - firmware to write debug message.
+ */
+
+/* Host-firmware shared queue ids */
+enum iris_iface_queue {
+ IFACEQ_CMDQ_ID,
+ IFACEQ_MSGQ_ID,
+ IFACEQ_DBGQ_ID,
+ IFACEQ_NUMQ, /* not an index */
+};
+
+/**
+ * struct iris_hfi_queue_header
+ *
+ * @status: Queue status, bits (7:0), 0x1 - active, 0x0 - inactive
+ * @start_addr: Queue start address in non cached memory
+ * @queue_type: Queue ID
+ * @header_type: Default queue header
+ * @q_size: Queue size
+ * Number of queue packets if pkt_size is non-zero
+ * Queue size in bytes if pkt_size is zero
+ * @pkt_size: Size of queue packet entries
+ * 0x0: variable queue packet size
+ * non zero: size of queue packet entry, fixed
+ * @pkt_drop_cnt: Number of packets dropped by sender
+ * @rx_wm: Receiver watermark, applicable in event driven mode
+ * @tx_wm: Sender watermark, applicable in event driven mode
+ * @rx_req: Receiver sets this bit if queue is empty
+ * @tx_req: Sender sets this bit if queue is full
+ * @rx_irq_status: Receiver sets this bit and triggers an interrupt to
+ * the sender after packets are dequeued. Sender clears this bit
+ * @tx_irq_status: Sender sets this bit and triggers an interrupt to
+ * the receiver after packets are queued. Receiver clears this bit
+ * @read_idx: Index till where receiver has consumed the packets from the queue.
+ * @write_idx: Index till where sender has written the packets into the queue.
+ */
+struct iris_hfi_queue_header {
+ u32 status;
+ u32 start_addr;
+ u16 queue_type;
+ u16 header_type;
+ u32 q_size;
+ u32 pkt_size;
+ u32 pkt_drop_cnt;
+ u32 rx_wm;
+ u32 tx_wm;
+ u32 rx_req;
+ u32 tx_req;
+ u32 rx_irq_status;
+ u32 tx_irq_status;
+ u32 read_idx;
+ u32 write_idx;
+};
+
+/**
+ * struct iris_hfi_queue_table_header
+ *
+ * @version: Queue table version number
+ * @size: Queue table size from version to last parametr in qhdr entry
+ * @qhdr0_offset: Offset to the start of first qhdr
+ * @qhdr_size: Queue header size in bytes
+ * @num_q: Total number of queues in Queue table
+ * @num_active_q: Total number of active queues
+ * @device_addr: Device address of the queue
+ * @name: Queue name in characters
+ * @q_hdr: Array of queue headers
+ */
+struct iris_hfi_queue_table_header {
+ u32 version;
+ u32 size;
+ u32 qhdr0_offset;
+ u32 qhdr_size;
+ u32 num_q;
+ u32 num_active_q;
+ void *device_addr;
+ char name[256]; /* NUL-terminated array of characters */
+ struct iris_hfi_queue_header q_hdr[IFACEQ_NUMQ];
+};
+
+struct iris_iface_q_info {
+ struct iris_hfi_queue_header *qhdr;
+ dma_addr_t device_addr;
+ void *kernel_vaddr;
+};
+
+int iris_hfi_queues_init(struct iris_core *core);
+void iris_hfi_queues_deinit(struct iris_core *core);
+
+int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_size);
+int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt, u32 pkt_size);
+int iris_hfi_queue_msg_read(struct iris_core *core, void *pkt);
+int iris_hfi_queue_dbg_read(struct iris_core *core, void *pkt);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
new file mode 100644
index 000000000000..caa3c6507006
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_INSTANCE_H__
+#define __IRIS_INSTANCE_H__
+
+#include <media/v4l2-ctrls.h>
+
+#include "iris_buffer.h"
+#include "iris_core.h"
+#include "iris_utils.h"
+
+/**
+ * struct iris_inst - holds per video instance parameters
+ *
+ * @list: used for attach an instance to the core
+ * @core: pointer to core structure
+ * @session_id: id of current video session
+ * @ctx_q_lock: lock to serialize queues related ioctls
+ * @lock: lock to seralise forward and reverse threads
+ * @fh: reference of v4l2 file handler
+ * @fmt_src: structure of v4l2_format for source
+ * @fmt_dst: structure of v4l2_format for destination
+ * @ctrl_handler: reference of v4l2 ctrl handler
+ * @crop: structure of crop info
+ * @completion: structure of signal completions
+ * @flush_completion: structure of signal completions for flush cmd
+ * @fw_caps: array of supported instance firmware capabilities
+ * @buffers: array of different iris buffers
+ * @fw_min_count: minimnum count of buffers needed by fw
+ * @state: instance state
+ * @sub_state: instance sub state
+ * @once_per_session_set: boolean to set once per session property
+ * @max_input_data_size: max size of input data
+ * @power: structure of power info
+ * @icc_data: structure of interconnect data
+ * @m2m_dev: a reference to m2m device structure
+ * @m2m_ctx: a reference to m2m context structure
+ * @sequence_cap: a sequence counter for capture queue
+ * @sequence_out: a sequence counter for output queue
+ * @tss: timestamp metadata
+ * @metadata_idx: index for metadata buffer
+ */
+
+struct iris_inst {
+ struct list_head list;
+ struct iris_core *core;
+ u32 session_id;
+ struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */
+ struct mutex lock; /* lock to serialize forward and reverse threads */
+ struct v4l2_fh fh;
+ struct v4l2_format *fmt_src;
+ struct v4l2_format *fmt_dst;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct iris_hfi_rect_desc crop;
+ struct completion completion;
+ struct completion flush_completion;
+ struct platform_inst_fw_cap fw_caps[INST_FW_CAP_MAX];
+ struct iris_buffers buffers[BUF_TYPE_MAX];
+ u32 fw_min_count;
+ enum iris_inst_state state;
+ enum iris_inst_sub_state sub_state;
+ bool once_per_session_set;
+ size_t max_input_data_size;
+ struct iris_inst_power power;
+ struct icc_vote_data icc_data;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ u32 sequence_cap;
+ u32 sequence_out;
+ struct iris_ts_metadata tss[VIDEO_MAX_FRAME];
+ u32 metadata_idx;
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
new file mode 100644
index 000000000000..ac76d9e1ef9c
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_PLATFORM_COMMON_H__
+#define __IRIS_PLATFORM_COMMON_H__
+
+#include <linux/bits.h>
+
+struct iris_core;
+struct iris_inst;
+
+#define IRIS_PAS_ID 9
+#define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */
+#define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */
+
+#define REGISTER_BIT_DEPTH(luma, chroma) ((luma) << 16 | (chroma))
+#define BIT_DEPTH_8 REGISTER_BIT_DEPTH(8, 8)
+#define CODED_FRAMES_PROGRESSIVE 0x0
+#define DEFAULT_MAX_HOST_BUF_COUNT 64
+#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
+#define DEFAULT_FPS 30
+
+enum stage_type {
+ STAGE_1 = 1,
+ STAGE_2 = 2,
+};
+
+enum pipe_type {
+ PIPE_1 = 1,
+ PIPE_2 = 2,
+ PIPE_4 = 4,
+};
+
+extern struct iris_platform_data qcs8300_data;
+extern struct iris_platform_data sm8250_data;
+extern struct iris_platform_data sm8550_data;
+extern struct iris_platform_data sm8650_data;
+
+enum platform_clk_type {
+ IRIS_AXI_CLK,
+ IRIS_CTRL_CLK,
+ IRIS_HW_CLK,
+};
+
+struct platform_clk_data {
+ enum platform_clk_type clk_type;
+ const char *clk_name;
+};
+
+struct tz_cp_config {
+ u32 cp_start;
+ u32 cp_size;
+ u32 cp_nonpixel_start;
+ u32 cp_nonpixel_size;
+};
+
+struct ubwc_config_data {
+ u32 max_channels;
+ u32 mal_length;
+ u32 highest_bank_bit;
+ u32 bank_swzl_level;
+ u32 bank_swz2_level;
+ u32 bank_swz3_level;
+ u32 bank_spreading;
+};
+
+struct platform_inst_caps {
+ u32 min_frame_width;
+ u32 max_frame_width;
+ u32 min_frame_height;
+ u32 max_frame_height;
+ u32 max_mbpf;
+ u32 mb_cycles_vsp;
+ u32 mb_cycles_vpp;
+ u32 mb_cycles_fw;
+ u32 mb_cycles_fw_vpp;
+ u32 num_comv;
+};
+
+enum platform_inst_fw_cap_type {
+ PROFILE = 1,
+ LEVEL,
+ INPUT_BUF_HOST_MAX_COUNT,
+ STAGE,
+ PIPE,
+ POC,
+ CODED_FRAMES,
+ BIT_DEPTH,
+ RAP_FRAME,
+ DEBLOCK,
+ INST_FW_CAP_MAX,
+};
+
+enum platform_inst_fw_cap_flags {
+ CAP_FLAG_DYNAMIC_ALLOWED = BIT(0),
+ CAP_FLAG_MENU = BIT(1),
+ CAP_FLAG_INPUT_PORT = BIT(2),
+ CAP_FLAG_OUTPUT_PORT = BIT(3),
+ CAP_FLAG_CLIENT_SET = BIT(4),
+ CAP_FLAG_BITMASK = BIT(5),
+ CAP_FLAG_VOLATILE = BIT(6),
+};
+
+struct platform_inst_fw_cap {
+ enum platform_inst_fw_cap_type cap_id;
+ s64 min;
+ s64 max;
+ s64 step_or_mask;
+ s64 value;
+ u32 hfi_id;
+ enum platform_inst_fw_cap_flags flags;
+ int (*set)(struct iris_inst *inst,
+ enum platform_inst_fw_cap_type cap_id);
+};
+
+struct bw_info {
+ u32 mbs_per_sec;
+ u32 bw_ddr;
+};
+
+struct iris_core_power {
+ u64 clk_freq;
+ u64 icc_bw;
+};
+
+struct iris_inst_power {
+ u64 min_freq;
+ u32 icc_bw;
+};
+
+struct icc_vote_data {
+ u32 height, width;
+ u32 fps;
+};
+
+enum platform_pm_domain_type {
+ IRIS_CTRL_POWER_DOMAIN,
+ IRIS_HW_POWER_DOMAIN,
+};
+
+struct iris_platform_data {
+ void (*init_hfi_command_ops)(struct iris_core *core);
+ void (*init_hfi_response_ops)(struct iris_core *core);
+ struct iris_inst *(*get_instance)(void);
+ const struct vpu_ops *vpu_ops;
+ void (*set_preset_registers)(struct iris_core *core);
+ const struct icc_info *icc_tbl;
+ unsigned int icc_tbl_size;
+ const struct bw_info *bw_tbl_dec;
+ unsigned int bw_tbl_dec_size;
+ const char * const *pmdomain_tbl;
+ unsigned int pmdomain_tbl_size;
+ const char * const *opp_pd_tbl;
+ unsigned int opp_pd_tbl_size;
+ const struct platform_clk_data *clk_tbl;
+ unsigned int clk_tbl_size;
+ const char * const *clk_rst_tbl;
+ unsigned int clk_rst_tbl_size;
+ const char * const *controller_rst_tbl;
+ unsigned int controller_rst_tbl_size;
+ u64 dma_mask;
+ const char *fwname;
+ u32 pas_id;
+ struct platform_inst_caps *inst_caps;
+ struct platform_inst_fw_cap *inst_fw_caps;
+ u32 inst_fw_caps_size;
+ struct tz_cp_config *tz_cp_config_data;
+ u32 core_arch;
+ u32 hw_response_timeout;
+ struct ubwc_config_data *ubwc_config;
+ u32 num_vpp_pipe;
+ u32 max_session_count;
+ u32 max_core_mbpf;
+ const u32 *input_config_params;
+ unsigned int input_config_params_size;
+ const u32 *output_config_params;
+ unsigned int output_config_params_size;
+ const u32 *dec_input_prop;
+ unsigned int dec_input_prop_size;
+ const u32 *dec_output_prop;
+ unsigned int dec_output_prop_size;
+ const u32 *dec_ip_int_buf_tbl;
+ unsigned int dec_ip_int_buf_tbl_size;
+ const u32 *dec_op_int_buf_tbl;
+ unsigned int dec_op_int_buf_tbl_size;
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
new file mode 100644
index 000000000000..1e69ba15db0f
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_defines.h"
+#include "iris_platform_common.h"
+#include "iris_vpu_common.h"
+
+#include "iris_platform_qcs8300.h"
+#include "iris_platform_sm8650.h"
+
+#define VIDEO_ARCH_LX 1
+
+static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
+ {
+ .cap_id = PROFILE,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH),
+ .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = INPUT_BUF_HOST_MAX_COUNT,
+ .min = DEFAULT_MAX_HOST_BUF_COUNT,
+ .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
+ .step_or_mask = 1,
+ .value = DEFAULT_MAX_HOST_BUF_COUNT,
+ .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROP_STAGE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = PIPE,
+ .min = PIPE_1,
+ .max = PIPE_4,
+ .step_or_mask = 1,
+ .value = PIPE_4,
+ .hfi_id = HFI_PROP_PIPE,
+ .set = iris_set_pipe,
+ },
+ {
+ .cap_id = POC,
+ .min = 0,
+ .max = 2,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE,
+ },
+ {
+ .cap_id = CODED_FRAMES,
+ .min = CODED_FRAMES_PROGRESSIVE,
+ .max = CODED_FRAMES_PROGRESSIVE,
+ .step_or_mask = 0,
+ .value = CODED_FRAMES_PROGRESSIVE,
+ .hfi_id = HFI_PROP_CODED_FRAMES,
+ },
+ {
+ .cap_id = BIT_DEPTH,
+ .min = BIT_DEPTH_8,
+ .max = BIT_DEPTH_8,
+ .step_or_mask = 1,
+ .value = BIT_DEPTH_8,
+ .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ },
+ {
+ .cap_id = RAP_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+};
+
+static struct platform_inst_caps platform_inst_cap_sm8550 = {
+ .min_frame_width = 96,
+ .max_frame_width = 8192,
+ .min_frame_height = 96,
+ .max_frame_height = 8192,
+ .max_mbpf = (8192 * 4352) / 256,
+ .mb_cycles_vpp = 200,
+ .mb_cycles_fw = 489583,
+ .mb_cycles_fw_vpp = 66234,
+ .num_comv = 0,
+};
+
+static void iris_set_sm8550_preset_registers(struct iris_core *core)
+{
+ writel(0x0, core->reg_base + 0xB0088);
+}
+
+static const struct icc_info sm8550_icc_table[] = {
+ { "cpu-cfg", 1000, 1000 },
+ { "video-mem", 1000, 15000000 },
+};
+
+static const char * const sm8550_clk_reset_table[] = { "bus" };
+
+static const struct bw_info sm8550_bw_table_dec[] = {
+ { ((4096 * 2160) / 256) * 60, 1608000 },
+ { ((4096 * 2160) / 256) * 30, 826000 },
+ { ((1920 * 1080) / 256) * 60, 567000 },
+ { ((1920 * 1080) / 256) * 30, 294000 },
+};
+
+static const char * const sm8550_pmdomain_table[] = { "venus", "vcodec0" };
+
+static const char * const sm8550_opp_pd_table[] = { "mxc", "mmcx" };
+
+static const struct platform_clk_data sm8550_clk_table[] = {
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_HW_CLK, "vcodec0_core" },
+};
+
+static struct ubwc_config_data ubwc_config_sm8550 = {
+ .max_channels = 8,
+ .mal_length = 32,
+ .highest_bank_bit = 16,
+ .bank_swzl_level = 0,
+ .bank_swz2_level = 1,
+ .bank_swz3_level = 1,
+ .bank_spreading = 1,
+};
+
+static struct tz_cp_config tz_cp_config_sm8550 = {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+};
+
+static const u32 sm8550_vdec_input_config_params[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_CODED_FRAMES,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_PROP_PIC_ORDER_CNT_TYPE,
+ HFI_PROP_PROFILE,
+ HFI_PROP_LEVEL,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+};
+
+static const u32 sm8550_vdec_output_config_params[] = {
+ HFI_PROP_COLOR_FORMAT,
+ HFI_PROP_LINEAR_STRIDE_SCANLINE,
+};
+
+static const u32 sm8550_vdec_subscribe_input_properties[] = {
+ HFI_PROP_NO_OUTPUT,
+};
+
+static const u32 sm8550_vdec_subscribe_output_properties[] = {
+ HFI_PROP_PICTURE_TYPE,
+ HFI_PROP_CABAC_SESSION,
+};
+
+static const u32 sm8550_dec_ip_int_buf_tbl[] = {
+ BUF_BIN,
+ BUF_COMV,
+ BUF_NON_COMV,
+ BUF_LINE,
+};
+
+static const u32 sm8550_dec_op_int_buf_tbl[] = {
+ BUF_DPB,
+};
+
+struct iris_platform_data sm8550_data = {
+ .get_instance = iris_hfi_gen2_get_instance,
+ .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+ .vpu_ops = &iris_vpu3_ops,
+ .set_preset_registers = iris_set_sm8550_preset_registers,
+ .icc_tbl = sm8550_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
+ .clk_rst_tbl = sm8550_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
+ .bw_tbl_dec = sm8550_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
+ .pmdomain_tbl = sm8550_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
+ .opp_pd_tbl = sm8550_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu/vpu30_p4.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_sm8550,
+ .inst_fw_caps = inst_fw_cap_sm8550,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8550),
+ .tz_cp_config_data = &tz_cp_config_sm8550,
+ .core_arch = VIDEO_ARCH_LX,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .ubwc_config = &ubwc_config_sm8550,
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = ((8192 * 4352) / 256) * 2,
+ .input_config_params =
+ sm8550_vdec_input_config_params,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .output_config_params =
+ sm8550_vdec_output_config_params,
+ .output_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_output_config_params),
+ .dec_input_prop = sm8550_vdec_subscribe_input_properties,
+ .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties),
+ .dec_output_prop = sm8550_vdec_subscribe_output_properties,
+ .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties),
+
+ .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+};
+
+/*
+ * Shares most of SM8550 data except:
+ * - vpu_ops to iris_vpu33_ops
+ * - clk_rst_tbl to sm8650_clk_reset_table
+ * - controller_rst_tbl to sm8650_controller_reset_table
+ * - fwname to "qcom/vpu/vpu33_p4.mbn"
+ */
+struct iris_platform_data sm8650_data = {
+ .get_instance = iris_hfi_gen2_get_instance,
+ .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+ .vpu_ops = &iris_vpu33_ops,
+ .set_preset_registers = iris_set_sm8550_preset_registers,
+ .icc_tbl = sm8550_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
+ .clk_rst_tbl = sm8650_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table),
+ .controller_rst_tbl = sm8650_controller_reset_table,
+ .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table),
+ .bw_tbl_dec = sm8550_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
+ .pmdomain_tbl = sm8550_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
+ .opp_pd_tbl = sm8550_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu/vpu33_p4.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_sm8550,
+ .inst_fw_caps = inst_fw_cap_sm8550,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8550),
+ .tz_cp_config_data = &tz_cp_config_sm8550,
+ .core_arch = VIDEO_ARCH_LX,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .ubwc_config = &ubwc_config_sm8550,
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = ((8192 * 4352) / 256) * 2,
+ .input_config_params =
+ sm8550_vdec_input_config_params,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .output_config_params =
+ sm8550_vdec_output_config_params,
+ .output_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_output_config_params),
+ .dec_input_prop = sm8550_vdec_subscribe_input_properties,
+ .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties),
+ .dec_output_prop = sm8550_vdec_subscribe_output_properties,
+ .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties),
+
+ .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+};
+
+/*
+ * Shares most of SM8550 data except:
+ * - inst_caps to platform_inst_cap_qcs8300
+ * - inst_fw_caps to inst_fw_cap_qcs8300
+ */
+struct iris_platform_data qcs8300_data = {
+ .get_instance = iris_hfi_gen2_get_instance,
+ .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+ .vpu_ops = &iris_vpu3_ops,
+ .set_preset_registers = iris_set_sm8550_preset_registers,
+ .icc_tbl = sm8550_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
+ .clk_rst_tbl = sm8550_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
+ .bw_tbl_dec = sm8550_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
+ .pmdomain_tbl = sm8550_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
+ .opp_pd_tbl = sm8550_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu/vpu30_p4_s6.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_qcs8300,
+ .inst_fw_caps = inst_fw_cap_qcs8300,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_qcs8300),
+ .tz_cp_config_data = &tz_cp_config_sm8550,
+ .core_arch = VIDEO_ARCH_LX,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .ubwc_config = &ubwc_config_sm8550,
+ .num_vpp_pipe = 2,
+ .max_session_count = 16,
+ .max_core_mbpf = ((4096 * 2176) / 256) * 4,
+ .input_config_params =
+ sm8550_vdec_input_config_params,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .output_config_params =
+ sm8550_vdec_output_config_params,
+ .output_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_output_config_params),
+ .dec_input_prop = sm8550_vdec_subscribe_input_properties,
+ .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties),
+ .dec_output_prop = sm8550_vdec_subscribe_output_properties,
+ .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties),
+
+ .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
new file mode 100644
index 000000000000..f82355d72fcf
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+static struct platform_inst_fw_cap inst_fw_cap_qcs8300[] = {
+ {
+ .cap_id = PROFILE,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH),
+ .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = INPUT_BUF_HOST_MAX_COUNT,
+ .min = DEFAULT_MAX_HOST_BUF_COUNT,
+ .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
+ .step_or_mask = 1,
+ .value = DEFAULT_MAX_HOST_BUF_COUNT,
+ .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROP_STAGE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = PIPE,
+ .min = PIPE_1,
+ .max = PIPE_2,
+ .step_or_mask = 1,
+ .value = PIPE_2,
+ .hfi_id = HFI_PROP_PIPE,
+ .set = iris_set_pipe,
+ },
+ {
+ .cap_id = POC,
+ .min = 0,
+ .max = 2,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE,
+ },
+ {
+ .cap_id = CODED_FRAMES,
+ .min = CODED_FRAMES_PROGRESSIVE,
+ .max = CODED_FRAMES_PROGRESSIVE,
+ .step_or_mask = 0,
+ .value = CODED_FRAMES_PROGRESSIVE,
+ .hfi_id = HFI_PROP_CODED_FRAMES,
+ },
+ {
+ .cap_id = BIT_DEPTH,
+ .min = BIT_DEPTH_8,
+ .max = BIT_DEPTH_8,
+ .step_or_mask = 1,
+ .value = BIT_DEPTH_8,
+ .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ },
+ {
+ .cap_id = RAP_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+};
+
+static struct platform_inst_caps platform_inst_cap_qcs8300 = {
+ .min_frame_width = 96,
+ .max_frame_width = 4096,
+ .min_frame_height = 96,
+ .max_frame_height = 4096,
+ .max_mbpf = (4096 * 2176) / 256,
+ .mb_cycles_vpp = 200,
+ .mb_cycles_fw = 326389,
+ .mb_cycles_fw_vpp = 44156,
+ .num_comv = 0,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
new file mode 100644
index 000000000000..5c86fd7b7b6f
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_platform_common.h"
+#include "iris_resources.h"
+#include "iris_hfi_gen1.h"
+#include "iris_hfi_gen1_defines.h"
+#include "iris_vpu_common.h"
+
+static struct platform_inst_fw_cap inst_fw_cap_sm8250[] = {
+ {
+ .cap_id = PIPE,
+ .min = PIPE_1,
+ .max = PIPE_4,
+ .step_or_mask = 1,
+ .value = PIPE_4,
+ .hfi_id = HFI_PROPERTY_PARAM_WORK_ROUTE,
+ .set = iris_set_pipe,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROPERTY_PARAM_WORK_MODE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = DEBLOCK,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER,
+ .set = iris_set_u32,
+ },
+};
+
+static struct platform_inst_caps platform_inst_cap_sm8250 = {
+ .min_frame_width = 128,
+ .max_frame_width = 8192,
+ .min_frame_height = 128,
+ .max_frame_height = 8192,
+ .max_mbpf = 138240,
+ .mb_cycles_vsp = 25,
+ .mb_cycles_vpp = 200,
+};
+
+static void iris_set_sm8250_preset_registers(struct iris_core *core)
+{
+ writel(0x0, core->reg_base + 0xB0088);
+}
+
+static const struct icc_info sm8250_icc_table[] = {
+ { "cpu-cfg", 1000, 1000 },
+ { "video-mem", 1000, 15000000 },
+};
+
+static const char * const sm8250_clk_reset_table[] = { "bus", "core" };
+
+static const struct bw_info sm8250_bw_table_dec[] = {
+ { ((4096 * 2160) / 256) * 60, 2403000 },
+ { ((4096 * 2160) / 256) * 30, 1224000 },
+ { ((1920 * 1080) / 256) * 60, 812000 },
+ { ((1920 * 1080) / 256) * 30, 416000 },
+};
+
+static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" };
+
+static const char * const sm8250_opp_pd_table[] = { "mx" };
+
+static const struct platform_clk_data sm8250_clk_table[] = {
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_HW_CLK, "vcodec0_core" },
+};
+
+static struct tz_cp_config tz_cp_config_sm8250 = {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+};
+
+static const u32 sm8250_vdec_input_config_param_default[] = {
+ HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE,
+ HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+ HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
+ HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
+ HFI_PROPERTY_PARAM_FRAME_SIZE,
+ HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL,
+ HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE,
+};
+
+static const u32 sm8250_dec_ip_int_buf_tbl[] = {
+ BUF_BIN,
+ BUF_SCRATCH_1,
+};
+
+static const u32 sm8250_dec_op_int_buf_tbl[] = {
+ BUF_DPB,
+};
+
+struct iris_platform_data sm8250_data = {
+ .get_instance = iris_hfi_gen1_get_instance,
+ .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen1_response_ops_init,
+ .vpu_ops = &iris_vpu2_ops,
+ .set_preset_registers = iris_set_sm8250_preset_registers,
+ .icc_tbl = sm8250_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table),
+ .clk_rst_tbl = sm8250_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8250_clk_reset_table),
+ .bw_tbl_dec = sm8250_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec),
+ .pmdomain_tbl = sm8250_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table),
+ .opp_pd_tbl = sm8250_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table),
+ .clk_tbl = sm8250_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu-1.0/venus.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_sm8250,
+ .inst_fw_caps = inst_fw_cap_sm8250,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8250),
+ .tz_cp_config_data = &tz_cp_config_sm8250,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = (8192 * 4352) / 256,
+ .input_config_params =
+ sm8250_vdec_input_config_param_default,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8250_vdec_input_config_param_default),
+
+ .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl),
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8650.h b/drivers/media/platform/qcom/iris/iris_platform_sm8650.h
new file mode 100644
index 000000000000..75e9d572e788
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8650.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_PLATFORM_SM8650_H__
+#define __IRIS_PLATFORM_SM8650_H__
+
+static const char * const sm8650_clk_reset_table[] = { "bus", "core" };
+
+static const char * const sm8650_controller_reset_table[] = { "xo" };
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_power.c b/drivers/media/platform/qcom/iris/iris_power.c
new file mode 100644
index 000000000000..dbca42df0910
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_power.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_buffer.h"
+#include "iris_instance.h"
+#include "iris_power.h"
+#include "iris_resources.h"
+#include "iris_vpu_common.h"
+
+static u32 iris_calc_bw(struct iris_inst *inst, struct icc_vote_data *data)
+{
+ const struct bw_info *bw_tbl = NULL;
+ struct iris_core *core = inst->core;
+ u32 num_rows, i, mbs, mbps;
+ u32 icc_bw = 0;
+
+ mbs = DIV_ROUND_UP(data->height, 16) * DIV_ROUND_UP(data->width, 16);
+ mbps = mbs * data->fps;
+ if (mbps == 0)
+ goto exit;
+
+ bw_tbl = core->iris_platform_data->bw_tbl_dec;
+ num_rows = core->iris_platform_data->bw_tbl_dec_size;
+
+ for (i = 0; i < num_rows; i++) {
+ if (i != 0 && mbps > bw_tbl[i].mbs_per_sec)
+ break;
+
+ icc_bw = bw_tbl[i].bw_ddr;
+ }
+
+exit:
+ return icc_bw;
+}
+
+static int iris_set_interconnects(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance;
+ u64 total_bw_ddr = 0;
+ int ret;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list) {
+ if (!instance->max_input_data_size)
+ continue;
+
+ total_bw_ddr += instance->power.icc_bw;
+ }
+
+ ret = iris_set_icc_bw(core, total_bw_ddr);
+
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int iris_vote_interconnects(struct iris_inst *inst)
+{
+ struct icc_vote_data *vote_data = &inst->icc_data;
+ struct v4l2_format *inp_f = inst->fmt_src;
+
+ vote_data->width = inp_f->fmt.pix_mp.width;
+ vote_data->height = inp_f->fmt.pix_mp.height;
+ vote_data->fps = DEFAULT_FPS;
+
+ inst->power.icc_bw = iris_calc_bw(inst, vote_data);
+
+ return iris_set_interconnects(inst);
+}
+
+static int iris_set_clocks(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance;
+ u64 freq = 0;
+ int ret;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list) {
+ if (!instance->max_input_data_size)
+ continue;
+
+ freq += instance->power.min_freq;
+ }
+
+ core->power.clk_freq = freq;
+ ret = dev_pm_opp_set_rate(core->dev, freq);
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int iris_scale_clocks(struct iris_inst *inst)
+{
+ const struct vpu_ops *vpu_ops = inst->core->iris_platform_data->vpu_ops;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+ size_t data_size = 0;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ data_size = max(data_size, buf->data_size);
+ }
+
+ inst->max_input_data_size = data_size;
+ if (!inst->max_input_data_size)
+ return 0;
+
+ inst->power.min_freq = vpu_ops->calc_freq(inst, inst->max_input_data_size);
+
+ return iris_set_clocks(inst);
+}
+
+int iris_scale_power(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ int ret;
+
+ if (pm_runtime_suspended(core->dev)) {
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_put_autosuspend(core->dev);
+ }
+
+ ret = iris_scale_clocks(inst);
+ if (ret)
+ return ret;
+
+ return iris_vote_interconnects(inst);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_power.h b/drivers/media/platform/qcom/iris/iris_power.h
new file mode 100644
index 000000000000..55212660e72d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_power.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_POWER_H__
+#define __IRIS_POWER_H__
+
+struct iris_inst;
+
+int iris_scale_power(struct iris_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
new file mode 100644
index 000000000000..9a7ce142f700
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/interconnect.h>
+#include <linux/module.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_vidc.h"
+
+static int iris_init_icc(struct iris_core *core)
+{
+ const struct icc_info *icc_tbl;
+ u32 i = 0;
+
+ icc_tbl = core->iris_platform_data->icc_tbl;
+
+ core->icc_count = core->iris_platform_data->icc_tbl_size;
+ core->icc_tbl = devm_kzalloc(core->dev,
+ sizeof(struct icc_bulk_data) * core->icc_count,
+ GFP_KERNEL);
+ if (!core->icc_tbl)
+ return -ENOMEM;
+
+ for (i = 0; i < core->icc_count; i++) {
+ core->icc_tbl[i].name = icc_tbl[i].name;
+ core->icc_tbl[i].avg_bw = icc_tbl[i].bw_min_kbps;
+ core->icc_tbl[i].peak_bw = 0;
+ }
+
+ return devm_of_icc_bulk_get(core->dev, core->icc_count, core->icc_tbl);
+}
+
+static int iris_init_power_domains(struct iris_core *core)
+{
+ const struct platform_clk_data *clk_tbl;
+ u32 clk_cnt, i;
+ int ret;
+
+ struct dev_pm_domain_attach_data iris_pd_data = {
+ .pd_names = core->iris_platform_data->pmdomain_tbl,
+ .num_pd_names = core->iris_platform_data->pmdomain_tbl_size,
+ .pd_flags = PD_FLAG_NO_DEV_LINK,
+ };
+
+ struct dev_pm_domain_attach_data iris_opp_pd_data = {
+ .pd_names = core->iris_platform_data->opp_pd_tbl,
+ .num_pd_names = core->iris_platform_data->opp_pd_tbl_size,
+ .pd_flags = PD_FLAG_DEV_LINK_ON,
+ };
+
+ ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl);
+ if (ret < 0)
+ return ret;
+
+ clk_tbl = core->iris_platform_data->clk_tbl;
+ clk_cnt = core->iris_platform_data->clk_tbl_size;
+
+ for (i = 0; i < clk_cnt; i++) {
+ if (clk_tbl[i].clk_type == IRIS_HW_CLK) {
+ ret = devm_pm_opp_set_clkname(core->dev, clk_tbl[i].clk_name);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return devm_pm_opp_of_add_table(core->dev);
+}
+
+static int iris_init_clocks(struct iris_core *core)
+{
+ int ret;
+
+ ret = devm_clk_bulk_get_all(core->dev, &core->clock_tbl);
+ if (ret < 0)
+ return ret;
+
+ core->clk_count = ret;
+
+ return 0;
+}
+
+static int iris_init_reset_table(struct iris_core *core,
+ struct reset_control_bulk_data **resets,
+ const char * const *rst_tbl, u32 rst_tbl_size)
+{
+ u32 i = 0;
+
+ *resets = devm_kzalloc(core->dev,
+ sizeof(struct reset_control_bulk_data) * rst_tbl_size,
+ GFP_KERNEL);
+ if (!*resets)
+ return -ENOMEM;
+
+ for (i = 0; i < rst_tbl_size; i++)
+ (*resets)[i].id = rst_tbl[i];
+
+ return devm_reset_control_bulk_get_exclusive(core->dev, rst_tbl_size, *resets);
+}
+
+static int iris_init_resets(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_init_reset_table(core, &core->resets,
+ core->iris_platform_data->clk_rst_tbl,
+ core->iris_platform_data->clk_rst_tbl_size);
+ if (ret)
+ return ret;
+
+ if (!core->iris_platform_data->controller_rst_tbl_size)
+ return 0;
+
+ return iris_init_reset_table(core, &core->controller_resets,
+ core->iris_platform_data->controller_rst_tbl,
+ core->iris_platform_data->controller_rst_tbl_size);
+}
+
+static int iris_init_resources(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_init_icc(core);
+ if (ret)
+ return ret;
+
+ ret = iris_init_power_domains(core);
+ if (ret)
+ return ret;
+
+ ret = iris_init_clocks(core);
+ if (ret)
+ return ret;
+
+ return iris_init_resets(core);
+}
+
+static int iris_register_video_device(struct iris_core *core)
+{
+ struct video_device *vdev;
+ int ret;
+
+ vdev = video_device_alloc();
+ if (!vdev)
+ return -ENOMEM;
+
+ strscpy(vdev->name, "qcom-iris-decoder", sizeof(vdev->name));
+ vdev->release = video_device_release;
+ vdev->fops = core->iris_v4l2_file_ops;
+ vdev->ioctl_ops = core->iris_v4l2_ioctl_ops;
+ vdev->vfl_dir = VFL_DIR_M2M;
+ vdev->v4l2_dev = &core->v4l2_dev;
+ vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ goto err_vdev_release;
+
+ core->vdev_dec = vdev;
+ video_set_drvdata(vdev, core);
+
+ return 0;
+
+err_vdev_release:
+ video_device_release(vdev);
+
+ return ret;
+}
+
+static void iris_remove(struct platform_device *pdev)
+{
+ struct iris_core *core;
+
+ core = platform_get_drvdata(pdev);
+ if (!core)
+ return;
+
+ iris_core_deinit(core);
+
+ video_unregister_device(core->vdev_dec);
+
+ v4l2_device_unregister(&core->v4l2_dev);
+
+ mutex_destroy(&core->lock);
+}
+
+static void iris_sys_error_handler(struct work_struct *work)
+{
+ struct iris_core *core =
+ container_of(work, struct iris_core, sys_error_handler.work);
+
+ iris_core_deinit(core);
+ iris_core_init(core);
+}
+
+static int iris_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iris_core *core;
+ u64 dma_mask;
+ int ret;
+
+ core = devm_kzalloc(&pdev->dev, sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+ core->dev = dev;
+
+ core->state = IRIS_CORE_DEINIT;
+ mutex_init(&core->lock);
+ init_completion(&core->core_init_done);
+
+ core->response_packet = devm_kzalloc(core->dev, IFACEQ_CORE_PKT_SIZE, GFP_KERNEL);
+ if (!core->response_packet)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&core->instances);
+ INIT_DELAYED_WORK(&core->sys_error_handler, iris_sys_error_handler);
+
+ core->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(core->reg_base))
+ return PTR_ERR(core->reg_base);
+
+ core->irq = platform_get_irq(pdev, 0);
+ if (core->irq < 0)
+ return core->irq;
+
+ core->iris_platform_data = of_device_get_match_data(core->dev);
+
+ ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr,
+ iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core);
+ if (ret)
+ return ret;
+
+ disable_irq_nosync(core->irq);
+
+ iris_init_ops(core);
+ core->iris_platform_data->init_hfi_command_ops(core);
+ core->iris_platform_data->init_hfi_response_ops(core);
+
+ ret = iris_init_resources(core);
+ if (ret)
+ return ret;
+
+ iris_session_init_caps(core);
+
+ ret = v4l2_device_register(dev, &core->v4l2_dev);
+ if (ret)
+ return ret;
+
+ ret = iris_register_video_device(core);
+ if (ret)
+ goto err_v4l2_unreg;
+
+ platform_set_drvdata(pdev, core);
+
+ dma_mask = core->iris_platform_data->dma_mask;
+
+ ret = dma_set_mask_and_coherent(dev, dma_mask);
+ if (ret)
+ goto err_vdev_unreg;
+
+ dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
+ dma_set_seg_boundary(&pdev->dev, DMA_BIT_MASK(32));
+
+ pm_runtime_set_autosuspend_delay(core->dev, AUTOSUSPEND_DELAY_VALUE);
+ pm_runtime_use_autosuspend(core->dev);
+ ret = devm_pm_runtime_enable(core->dev);
+ if (ret)
+ goto err_vdev_unreg;
+
+ return 0;
+
+err_vdev_unreg:
+ video_unregister_device(core->vdev_dec);
+err_v4l2_unreg:
+ v4l2_device_unregister(&core->v4l2_dev);
+
+ return ret;
+}
+
+static int __maybe_unused iris_pm_suspend(struct device *dev)
+{
+ struct iris_core *core;
+ int ret = 0;
+
+ core = dev_get_drvdata(dev);
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT)
+ goto exit;
+
+ ret = iris_hfi_pm_suspend(core);
+
+exit:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int __maybe_unused iris_pm_resume(struct device *dev)
+{
+ struct iris_core *core;
+ int ret = 0;
+
+ core = dev_get_drvdata(dev);
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT)
+ goto exit;
+
+ ret = iris_hfi_pm_resume(core);
+ pm_runtime_mark_last_busy(core->dev);
+
+exit:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static const struct dev_pm_ops iris_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(iris_pm_suspend, iris_pm_resume, NULL)
+};
+
+static const struct of_device_id iris_dt_match[] = {
+ {
+ .compatible = "qcom,qcs8300-iris",
+ .data = &qcs8300_data,
+ },
+#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS))
+ {
+ .compatible = "qcom,sm8250-venus",
+ .data = &sm8250_data,
+ },
+#endif
+ {
+ .compatible = "qcom,sm8550-iris",
+ .data = &sm8550_data,
+ },
+ {
+ .compatible = "qcom,sm8650-iris",
+ .data = &sm8650_data,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, iris_dt_match);
+
+static struct platform_driver qcom_iris_driver = {
+ .probe = iris_probe,
+ .remove = iris_remove,
+ .driver = {
+ .name = "qcom-iris",
+ .of_match_table = iris_dt_match,
+ .pm = &iris_pm_ops,
+ },
+};
+
+module_platform_driver(qcom_iris_driver);
+MODULE_DESCRIPTION("Qualcomm iris video driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c
new file mode 100644
index 000000000000..cf32f268b703
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_resources.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/interconnect.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "iris_core.h"
+#include "iris_resources.h"
+
+#define BW_THRESHOLD 50000
+
+int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw)
+{
+ unsigned long bw_kbps = 0, bw_prev = 0;
+ const struct icc_info *icc_tbl;
+ int ret = 0, i;
+
+ icc_tbl = core->iris_platform_data->icc_tbl;
+
+ for (i = 0; i < core->icc_count; i++) {
+ if (!strcmp(core->icc_tbl[i].name, "video-mem")) {
+ bw_kbps = icc_bw;
+ bw_prev = core->power.icc_bw;
+
+ bw_kbps = clamp_t(typeof(bw_kbps), bw_kbps,
+ icc_tbl[i].bw_min_kbps, icc_tbl[i].bw_max_kbps);
+
+ if (abs(bw_kbps - bw_prev) < BW_THRESHOLD && bw_prev)
+ return ret;
+
+ core->icc_tbl[i].avg_bw = bw_kbps;
+
+ core->power.icc_bw = bw_kbps;
+ break;
+ }
+ }
+
+ return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
+}
+
+int iris_unset_icc_bw(struct iris_core *core)
+{
+ u32 i;
+
+ core->power.icc_bw = 0;
+
+ for (i = 0; i < core->icc_count; i++) {
+ core->icc_tbl[i].avg_bw = 0;
+ core->icc_tbl[i].peak_bw = 0;
+ }
+
+ return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
+}
+
+int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev)
+{
+ int ret;
+
+ ret = dev_pm_opp_set_rate(core->dev, ULONG_MAX);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(pd_dev);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev)
+{
+ int ret;
+
+ ret = dev_pm_opp_set_rate(core->dev, 0);
+ if (ret)
+ return ret;
+
+ pm_runtime_put_sync(pd_dev);
+
+ return 0;
+}
+
+static struct clk *iris_get_clk_by_type(struct iris_core *core, enum platform_clk_type clk_type)
+{
+ const struct platform_clk_data *clk_tbl;
+ u32 clk_cnt, i, j;
+
+ clk_tbl = core->iris_platform_data->clk_tbl;
+ clk_cnt = core->iris_platform_data->clk_tbl_size;
+
+ for (i = 0; i < clk_cnt; i++) {
+ if (clk_tbl[i].clk_type == clk_type) {
+ for (j = 0; core->clock_tbl && j < core->clk_count; j++) {
+ if (!strcmp(core->clock_tbl[j].id, clk_tbl[i].clk_name))
+ return core->clock_tbl[j].clk;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type)
+{
+ struct clk *clock;
+
+ clock = iris_get_clk_by_type(core, clk_type);
+ if (!clock)
+ return -EINVAL;
+
+ return clk_prepare_enable(clock);
+}
+
+int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type)
+{
+ struct clk *clock;
+
+ clock = iris_get_clk_by_type(core, clk_type);
+ if (!clock)
+ return -EINVAL;
+
+ clk_disable_unprepare(clock);
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_resources.h b/drivers/media/platform/qcom/iris/iris_resources.h
new file mode 100644
index 000000000000..f723dfe5bd81
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_resources.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_RESOURCES_H__
+#define __IRIS_RESOURCES_H__
+
+struct iris_core;
+
+int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev);
+int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev);
+int iris_unset_icc_bw(struct iris_core *core);
+int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw);
+int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type);
+int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c
new file mode 100644
index 000000000000..5976e926c83d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_state.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_instance.h"
+
+static bool iris_allow_inst_state_change(struct iris_inst *inst,
+ enum iris_inst_state req_state)
+{
+ switch (inst->state) {
+ case IRIS_INST_INIT:
+ if (req_state == IRIS_INST_INPUT_STREAMING ||
+ req_state == IRIS_INST_OUTPUT_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_INPUT_STREAMING:
+ if (req_state == IRIS_INST_INIT ||
+ req_state == IRIS_INST_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_OUTPUT_STREAMING:
+ if (req_state == IRIS_INST_INIT ||
+ req_state == IRIS_INST_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_STREAMING:
+ if (req_state == IRIS_INST_INPUT_STREAMING ||
+ req_state == IRIS_INST_OUTPUT_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_DEINIT:
+ if (req_state == IRIS_INST_INIT)
+ return true;
+ return false;
+ default:
+ return false;
+ }
+}
+
+int iris_inst_change_state(struct iris_inst *inst,
+ enum iris_inst_state request_state)
+{
+ if (inst->state == IRIS_INST_ERROR)
+ return 0;
+
+ if (inst->state == request_state)
+ return 0;
+
+ if (request_state == IRIS_INST_ERROR)
+ goto change_state;
+
+ if (!iris_allow_inst_state_change(inst, request_state))
+ return -EINVAL;
+
+change_state:
+ inst->state = request_state;
+ dev_dbg(inst->core->dev, "state changed from %x to %x\n",
+ inst->state, request_state);
+
+ return 0;
+}
+
+int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane)
+{
+ enum iris_inst_state new_state = IRIS_INST_ERROR;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ if (inst->state == IRIS_INST_INIT)
+ new_state = IRIS_INST_INPUT_STREAMING;
+ else if (inst->state == IRIS_INST_OUTPUT_STREAMING)
+ new_state = IRIS_INST_STREAMING;
+ } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+ if (inst->state == IRIS_INST_INIT)
+ new_state = IRIS_INST_OUTPUT_STREAMING;
+ else if (inst->state == IRIS_INST_INPUT_STREAMING)
+ new_state = IRIS_INST_STREAMING;
+ }
+
+ return iris_inst_change_state(inst, new_state);
+}
+
+int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane)
+{
+ enum iris_inst_state new_state = IRIS_INST_ERROR;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ if (inst->state == IRIS_INST_INPUT_STREAMING)
+ new_state = IRIS_INST_INIT;
+ else if (inst->state == IRIS_INST_STREAMING)
+ new_state = IRIS_INST_OUTPUT_STREAMING;
+ } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+ if (inst->state == IRIS_INST_OUTPUT_STREAMING)
+ new_state = IRIS_INST_INIT;
+ else if (inst->state == IRIS_INST_STREAMING)
+ new_state = IRIS_INST_INPUT_STREAMING;
+ }
+
+ return iris_inst_change_state(inst, new_state);
+}
+
+static bool iris_inst_allow_sub_state(struct iris_inst *inst, enum iris_inst_sub_state sub_state)
+{
+ if (!sub_state)
+ return true;
+
+ switch (inst->state) {
+ case IRIS_INST_INIT:
+ if (sub_state & IRIS_INST_SUB_LOAD_RESOURCES)
+ return true;
+ return false;
+ case IRIS_INST_INPUT_STREAMING:
+ if (sub_state & (IRIS_INST_SUB_FIRST_IPSC | IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_INPUT_PAUSE))
+ return true;
+ return false;
+ case IRIS_INST_OUTPUT_STREAMING:
+ if (sub_state & (IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE))
+ return true;
+ return false;
+ case IRIS_INST_STREAMING:
+ if (sub_state & (IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE | IRIS_INST_SUB_OUTPUT_PAUSE))
+ return true;
+ return false;
+ case IRIS_INST_DEINIT:
+ if (sub_state & (IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE | IRIS_INST_SUB_OUTPUT_PAUSE))
+ return true;
+ return false;
+ default:
+ return false;
+ }
+}
+
+int iris_inst_change_sub_state(struct iris_inst *inst,
+ enum iris_inst_sub_state clear_sub_state,
+ enum iris_inst_sub_state set_sub_state)
+{
+ enum iris_inst_sub_state prev_sub_state;
+
+ if (inst->state == IRIS_INST_ERROR)
+ return 0;
+
+ if (!clear_sub_state && !set_sub_state)
+ return 0;
+
+ if ((clear_sub_state & set_sub_state) ||
+ set_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE ||
+ clear_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE)
+ return -EINVAL;
+
+ prev_sub_state = inst->sub_state;
+
+ if (!iris_inst_allow_sub_state(inst, set_sub_state))
+ return -EINVAL;
+
+ inst->sub_state |= set_sub_state;
+ inst->sub_state &= ~clear_sub_state;
+
+ if (inst->sub_state != prev_sub_state)
+ dev_dbg(inst->core->dev, "sub_state changed from %x to %x\n",
+ prev_sub_state, inst->sub_state);
+
+ return 0;
+}
+
+int iris_inst_sub_state_change_drc(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state set_sub_state = 0;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC)
+ return -EINVAL;
+
+ if (inst->state == IRIS_INST_INPUT_STREAMING ||
+ inst->state == IRIS_INST_INIT)
+ set_sub_state = IRIS_INST_SUB_FIRST_IPSC | IRIS_INST_SUB_INPUT_PAUSE;
+ else
+ set_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_INPUT_PAUSE;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_inst_sub_state_change_drain_last(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state set_sub_state;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)
+ return -EINVAL;
+
+ if (!(inst->sub_state & IRIS_INST_SUB_DRAIN))
+ return -EINVAL;
+
+ set_sub_state = IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_inst_sub_state_change_drc_last(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state set_sub_state;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC_LAST)
+ return -EINVAL;
+
+ if (!(inst->sub_state & IRIS_INST_SUB_DRC) ||
+ !(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE))
+ return -EINVAL;
+
+ if (inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)
+ return 0;
+
+ set_sub_state = IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane)
+{
+ enum iris_inst_sub_state set_sub_state;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ !(inst->sub_state & IRIS_INST_SUB_DRC_LAST))
+ return -EINVAL;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ !(inst->sub_state & IRIS_INST_SUB_DRAIN_LAST))
+ return -EINVAL;
+
+ set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
+ } else {
+ set_sub_state = IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+static inline bool iris_drc_pending(struct iris_inst *inst)
+{
+ return inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST;
+}
+
+static inline bool iris_drain_pending(struct iris_inst *inst)
+{
+ return inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+}
+
+bool iris_allow_cmd(struct iris_inst *inst, u32 cmd)
+{
+ struct vb2_queue *src_q = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+
+ if (cmd == V4L2_DEC_CMD_START) {
+ if (vb2_is_streaming(src_q) || vb2_is_streaming(dst_q))
+ if (iris_drc_pending(inst) || iris_drain_pending(inst))
+ return true;
+ } else if (cmd == V4L2_DEC_CMD_STOP) {
+ if (vb2_is_streaming(src_q))
+ if (inst->sub_state != IRIS_INST_SUB_DRAIN)
+ return true;
+ }
+
+ return false;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_state.h b/drivers/media/platform/qcom/iris/iris_state.h
new file mode 100644
index 000000000000..78c61aac5e7e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_state.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_STATE_H__
+#define __IRIS_STATE_H__
+
+struct iris_inst;
+
+/**
+ * enum iris_core_state
+ *
+ * @IRIS_CORE_DEINIT: default state.
+ * @IRIS_CORE_INIT: core state with core initialized. FW loaded and
+ * HW brought out of reset, shared queues established
+ * between host driver and firmware.
+ * @IRIS_CORE_ERROR: error state.
+ *
+ * -----------
+ * |
+ * V
+ * -----------
+ * +--->| DEINIT |<---+
+ * | ----------- |
+ * | | |
+ * | v |
+ * | ----------- |
+ * | / \ |
+ * | / \ |
+ * | / \ |
+ * | v v v
+ * ----------- -----------
+ * | INIT |--->| ERROR |
+ * ----------- -----------
+ */
+enum iris_core_state {
+ IRIS_CORE_DEINIT,
+ IRIS_CORE_INIT,
+ IRIS_CORE_ERROR,
+};
+
+/**
+ * enum iris_inst_state
+ *
+ * @IRIS_INST_INIT: video instance is opened.
+ * @IRIS_INST_INPUT_STREAMING: stream on is completed on output plane.
+ * @IRIS_INST_OUTPUT_STREAMING: stream on is completed on capture plane.
+ * @IRIS_INST_STREAMING: stream on is completed on both output and capture planes.
+ * @IRIS_INST_DEINIT: video instance is closed.
+ * @IRIS_INST_ERROR: error state.
+ * |
+ * V
+ * -------------
+ * +--------| INIT |----------+
+ * | ------------- |
+ * | ^ ^ |
+ * | / \ |
+ * | / \ |
+ * | v v |
+ * | ----------- ----------- |
+ * | | INPUT OUTPUT | |
+ * |---| STREAMING STREAMING |---|
+ * | ----------- ----------- |
+ * | ^ ^ |
+ * | \ / |
+ * | \ / |
+ * | v v |
+ * | ------------- |
+ * |--------| STREAMING |-----------|
+ * | ------------- |
+ * | | |
+ * | | |
+ * | v |
+ * | ----------- |
+ * +-------->| DEINIT |<----------+
+ * | ----------- |
+ * | | |
+ * | | |
+ * | v |
+ * | ---------- |
+ * +-------->| ERROR |<------------+
+ * ----------
+ */
+enum iris_inst_state {
+ IRIS_INST_DEINIT,
+ IRIS_INST_INIT,
+ IRIS_INST_INPUT_STREAMING,
+ IRIS_INST_OUTPUT_STREAMING,
+ IRIS_INST_STREAMING,
+ IRIS_INST_ERROR,
+};
+
+#define IRIS_INST_SUB_STATES 8
+#define IRIS_INST_MAX_SUB_STATE_VALUE ((1 << IRIS_INST_SUB_STATES) - 1)
+
+/**
+ * enum iris_inst_sub_state
+ *
+ * @IRIS_INST_SUB_FIRST_IPSC: indicates source change is received from firmware
+ * when output port is not yet streaming.
+ * @IRIS_INST_SUB_DRC: indicates source change is received from firmware
+ * when output port is streaming and source change event is
+ * sent to client.
+ * @IRIS_INST_SUB_DRC_LAST: indicates last buffer is received from firmware
+ * as part of source change.
+ * @IRIS_INST_SUB_DRAIN: indicates drain is in progress.
+ * @IRIS_INST_SUB_DRAIN_LAST: indicates last buffer is received from firmware
+ * as part of drain sequence.
+ * @IRIS_INST_SUB_INPUT_PAUSE: source change is received form firmware. This
+ * indicates that firmware is paused to process
+ * any further input frames.
+ * @IRIS_INST_SUB_OUTPUT_PAUSE: last buffer is received form firmware as part
+ * of drc sequence. This indicates that
+ * firmware is paused to process any further output frames.
+ * @IRIS_INST_SUB_LOAD_RESOURCES: indicates all the resources have been loaded by the
+ * firmware and it is ready for processing.
+ */
+enum iris_inst_sub_state {
+ IRIS_INST_SUB_FIRST_IPSC = BIT(0),
+ IRIS_INST_SUB_DRC = BIT(1),
+ IRIS_INST_SUB_DRC_LAST = BIT(2),
+ IRIS_INST_SUB_DRAIN = BIT(3),
+ IRIS_INST_SUB_DRAIN_LAST = BIT(4),
+ IRIS_INST_SUB_INPUT_PAUSE = BIT(5),
+ IRIS_INST_SUB_OUTPUT_PAUSE = BIT(6),
+ IRIS_INST_SUB_LOAD_RESOURCES = BIT(7),
+};
+
+int iris_inst_change_state(struct iris_inst *inst,
+ enum iris_inst_state request_state);
+int iris_inst_change_sub_state(struct iris_inst *inst,
+ enum iris_inst_sub_state clear_sub_state,
+ enum iris_inst_sub_state set_sub_state);
+
+int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane);
+int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane);
+int iris_inst_sub_state_change_drc(struct iris_inst *inst);
+int iris_inst_sub_state_change_drain_last(struct iris_inst *inst);
+int iris_inst_sub_state_change_drc_last(struct iris_inst *inst);
+int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane);
+bool iris_allow_cmd(struct iris_inst *inst, u32 cmd);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c
new file mode 100644
index 000000000000..83c70d6a2d90
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_utils.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_instance.h"
+#include "iris_utils.h"
+
+bool iris_res_is_less_than(u32 width, u32 height,
+ u32 ref_width, u32 ref_height)
+{
+ u32 num_mbs = NUM_MBS_PER_FRAME(height, width);
+ u32 max_side = max(ref_width, ref_height);
+
+ if (num_mbs < NUM_MBS_PER_FRAME(ref_height, ref_width) &&
+ width < max_side &&
+ height < max_side)
+ return true;
+
+ return false;
+}
+
+int iris_get_mbpf(struct iris_inst *inst)
+{
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+ u32 width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+
+ return NUM_MBS_PER_FRAME(height, width);
+}
+
+bool iris_split_mode_enabled(struct iris_inst *inst)
+{
+ return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12;
+}
+
+void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
+ enum vb2_buffer_state state)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct vb2_v4l2_buffer *buf;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx)))
+ v4l2_m2m_buf_done(buf, state);
+ } else if (V4L2_TYPE_IS_CAPTURE(type)) {
+ while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx)))
+ v4l2_m2m_buf_done(buf, state);
+ }
+}
+
+int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush)
+{
+ struct iris_core *core = inst->core;
+ u32 hw_response_timeout_val;
+ struct completion *done;
+ int ret;
+
+ hw_response_timeout_val = core->iris_platform_data->hw_response_timeout;
+ done = is_flush ? &inst->flush_completion : &inst->completion;
+
+ mutex_unlock(&inst->lock);
+ ret = wait_for_completion_timeout(done, msecs_to_jiffies(hw_response_timeout_val));
+ mutex_lock(&inst->lock);
+ if (!ret) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id)
+{
+ struct iris_inst *inst;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst->session_id == session_id) {
+ mutex_unlock(&core->lock);
+ return inst;
+ }
+ }
+
+ mutex_unlock(&core->lock);
+ return NULL;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h
new file mode 100644
index 000000000000..49869cf7a376
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_utils.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_UTILS_H__
+#define __IRIS_UTILS_H__
+
+struct iris_core;
+#include "iris_buffer.h"
+
+struct iris_hfi_rect_desc {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
+struct iris_hfi_frame_info {
+ u32 picture_type;
+ u32 no_output;
+ u32 data_corrupt;
+ u32 overflow;
+};
+
+struct iris_ts_metadata {
+ u64 ts_ns;
+ u64 ts_us;
+ u32 flags;
+ struct v4l2_timecode tc;
+};
+
+#define NUM_MBS_PER_FRAME(height, width) \
+ (DIV_ROUND_UP(height, 16) * DIV_ROUND_UP(width, 16))
+
+static inline enum iris_buffer_type iris_v4l2_type_to_driver(u32 type)
+{
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return BUF_INPUT;
+ else
+ return BUF_OUTPUT;
+}
+
+bool iris_res_is_less_than(u32 width, u32 height,
+ u32 ref_width, u32 ref_height);
+int iris_get_mbpf(struct iris_inst *inst);
+bool iris_split_mode_enabled(struct iris_inst *inst);
+struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id);
+void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
+ enum vb2_buffer_state state);
+int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
new file mode 100644
index 000000000000..cdf11feb590b
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_instance.h"
+#include "iris_vb2.h"
+#include "iris_vdec.h"
+#include "iris_power.h"
+
+static int iris_check_core_mbpf(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance;
+ u32 total_mbpf = 0;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list)
+ total_mbpf += iris_get_mbpf(instance);
+ mutex_unlock(&core->lock);
+
+ if (total_mbpf > core->iris_platform_data->max_core_mbpf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int iris_check_inst_mbpf(struct iris_inst *inst)
+{
+ struct platform_inst_caps *caps;
+ u32 mbpf, max_mbpf;
+
+ caps = inst->core->iris_platform_data->inst_caps;
+ max_mbpf = caps->max_mbpf;
+ mbpf = iris_get_mbpf(inst);
+ if (mbpf > max_mbpf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int iris_check_resolution_supported(struct iris_inst *inst)
+{
+ u32 width, height, min_width, min_height, max_width, max_height;
+ struct platform_inst_caps *caps;
+
+ caps = inst->core->iris_platform_data->inst_caps;
+ width = inst->fmt_src->fmt.pix_mp.width;
+ height = inst->fmt_src->fmt.pix_mp.height;
+
+ min_width = caps->min_frame_width;
+ max_width = caps->max_frame_width;
+ min_height = caps->min_frame_height;
+ max_height = caps->max_frame_height;
+
+ if (!(min_width <= width && width <= max_width) ||
+ !(min_height <= height && height <= max_height))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int iris_check_session_supported(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance = NULL;
+ bool found = false;
+ int ret;
+
+ list_for_each_entry(instance, &core->instances, list) {
+ if (instance == inst)
+ found = true;
+ }
+
+ if (!found) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = iris_check_core_mbpf(inst);
+ if (ret)
+ goto exit;
+
+ ret = iris_check_inst_mbpf(inst);
+ if (ret)
+ goto exit;
+
+ ret = iris_check_resolution_supported(inst);
+ if (ret)
+ goto exit;
+
+ return 0;
+exit:
+ dev_err(inst->core->dev, "current session not supported(%d)\n", ret);
+
+ return ret;
+}
+
+int iris_vb2_buf_init(struct vb2_buffer *vb2)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
+ struct iris_buffer *buf = to_iris_buffer(vbuf);
+
+ buf->device_addr = vb2_dma_contig_plane_dma_addr(vb2, 0);
+
+ return 0;
+}
+
+int iris_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct iris_inst *inst;
+ struct iris_core *core;
+ struct v4l2_format *f;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(q);
+
+ mutex_lock(&inst->lock);
+ if (inst->state == IRIS_INST_ERROR) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ core = inst->core;
+ f = V4L2_TYPE_IS_OUTPUT(q->type) ? inst->fmt_src : inst->fmt_dst;
+
+ if (*num_planes) {
+ if (*num_planes != f->fmt.pix_mp.num_planes ||
+ sizes[0] < f->fmt.pix_mp.plane_fmt[0].sizeimage)
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = iris_check_session_supported(inst);
+ if (ret)
+ goto unlock;
+
+ if (!inst->once_per_session_set) {
+ inst->once_per_session_set = true;
+
+ ret = core->hfi_ops->session_open(inst);
+ if (ret) {
+ ret = -EINVAL;
+ dev_err(core->dev, "session open failed\n");
+ goto unlock;
+ }
+
+ ret = iris_inst_change_state(inst, IRIS_INST_INIT);
+ if (ret)
+ goto unlock;
+ }
+
+ *num_planes = 1;
+ sizes[0] = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ enum iris_buffer_type buf_type;
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(q);
+
+ if (V4L2_TYPE_IS_CAPTURE(q->type) && inst->state == IRIS_INST_INIT)
+ return 0;
+
+ mutex_lock(&inst->lock);
+ if (inst->state == IRIS_INST_ERROR) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
+ !V4L2_TYPE_IS_CAPTURE(q->type)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ iris_scale_power(inst);
+
+ ret = iris_check_session_supported(inst);
+ if (ret)
+ goto error;
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ ret = iris_vdec_streamon_input(inst);
+ else if (V4L2_TYPE_IS_CAPTURE(q->type))
+ ret = iris_vdec_streamon_output(inst);
+ if (ret)
+ goto error;
+
+ buf_type = iris_v4l2_type_to_driver(q->type);
+
+ ret = iris_queue_deferred_buffers(inst, buf_type);
+ if (ret)
+ goto error;
+
+ mutex_unlock(&inst->lock);
+
+ return ret;
+
+error:
+ iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+void iris_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(q);
+
+ if (V4L2_TYPE_IS_CAPTURE(q->type) && inst->state == IRIS_INST_INIT)
+ return;
+
+ mutex_lock(&inst->lock);
+
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
+ !V4L2_TYPE_IS_CAPTURE(q->type))
+ goto exit;
+
+ ret = iris_vdec_session_streamoff(inst, q->type);
+ if (ret)
+ goto exit;
+
+exit:
+ iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
+ if (ret)
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+
+ mutex_unlock(&inst->lock);
+}
+
+int iris_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct iris_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ }
+
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT))
+ return -EINVAL;
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT))
+ return -EINVAL;
+
+ return 0;
+}
+
+int iris_vb2_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;
+}
+
+void iris_vb2_buf_queue(struct vb2_buffer *vb2)
+{
+ static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS };
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(vb2->vb2_queue);
+
+ mutex_lock(&inst->lock);
+ if (inst->state == IRIS_INST_ERROR) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+
+ m2m_ctx = inst->m2m_ctx;
+
+ if (!vb2->planes[0].bytesused && V4L2_TYPE_IS_OUTPUT(vb2->type)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) {
+ if ((inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST) ||
+ (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)) {
+ vbuf->flags |= V4L2_BUF_FLAG_LAST;
+ vbuf->sequence = inst->sequence_cap++;
+ vbuf->field = V4L2_FIELD_NONE;
+ vb2_set_plane_payload(vb2, 0, 0);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ if (!v4l2_m2m_has_stopped(m2m_ctx)) {
+ v4l2_event_queue_fh(&inst->fh, &eos);
+ v4l2_m2m_mark_stopped(m2m_ctx);
+ }
+ goto exit;
+ }
+ }
+
+ v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+
+ ret = iris_vdec_qbuf(inst, vbuf);
+
+exit:
+ if (ret) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ }
+ mutex_unlock(&inst->lock);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.h b/drivers/media/platform/qcom/iris/iris_vb2.h
new file mode 100644
index 000000000000..a88565fdd3e4
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vb2.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VB2_H__
+#define __IRIS_VB2_H__
+
+int iris_vb2_buf_init(struct vb2_buffer *vb2);
+int iris_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[]);
+int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count);
+void iris_vb2_stop_streaming(struct vb2_queue *q);
+int iris_vb2_buf_prepare(struct vb2_buffer *vb);
+int iris_vb2_buf_out_validate(struct vb2_buffer *vb);
+void iris_vb2_buf_queue(struct vb2_buffer *vb2);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
new file mode 100644
index 000000000000..4143acedfc57
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_buffer.h"
+#include "iris_ctrls.h"
+#include "iris_instance.h"
+#include "iris_power.h"
+#include "iris_vdec.h"
+#include "iris_vpu_buffer.h"
+
+#define DEFAULT_WIDTH 320
+#define DEFAULT_HEIGHT 240
+#define DEFAULT_CODEC_ALIGNMENT 16
+
+int iris_vdec_inst_init(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct v4l2_format *f;
+
+ inst->fmt_src = kzalloc(sizeof(*inst->fmt_src), GFP_KERNEL);
+ inst->fmt_dst = kzalloc(sizeof(*inst->fmt_dst), GFP_KERNEL);
+
+ inst->fw_min_count = MIN_BUFFERS;
+
+ f = inst->fmt_src;
+ f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f->fmt.pix_mp.width = DEFAULT_WIDTH;
+ f->fmt.pix_mp.height = DEFAULT_HEIGHT;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
+ inst->buffers[BUF_INPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ f = inst->fmt_dst;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+ f->fmt.pix_mp.width = ALIGN(DEFAULT_WIDTH, 128);
+ f->fmt.pix_mp.height = ALIGN(DEFAULT_HEIGHT, 32);
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128);
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
+ f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ memcpy(&inst->fw_caps[0], &core->inst_fw_caps[0],
+ INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap));
+
+ return iris_ctrls_init(inst);
+}
+
+void iris_vdec_inst_deinit(struct iris_inst *inst)
+{
+ kfree(inst->fmt_dst);
+ kfree(inst->fmt_src);
+}
+
+int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
+{
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ f->pixelformat = V4L2_PIX_FMT_H264;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ f->pixelformat = V4L2_PIX_FMT_NV12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_format *f_inst;
+ struct vb2_queue *src_q;
+
+ memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264) {
+ f_inst = inst->fmt_src;
+ f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
+ f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
+ f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) {
+ f_inst = inst->fmt_dst;
+ f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
+ f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
+ f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
+ }
+
+ src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+ if (vb2_is_streaming(src_q)) {
+ f_inst = inst->fmt_src;
+ f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
+ f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (pixmp->field == V4L2_FIELD_ANY)
+ pixmp->field = V4L2_FIELD_NONE;
+
+ pixmp->num_planes = 1;
+
+ return 0;
+}
+
+int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_format *fmt, *output_fmt;
+ struct vb2_queue *q;
+ u32 codec_align;
+
+ q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
+ if (!q)
+ return -EINVAL;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ iris_vdec_try_fmt(inst, f);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264)
+ return -EINVAL;
+
+ fmt = inst->fmt_src;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+ codec_align = DEFAULT_CODEC_ALIGNMENT;
+ fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align);
+ fmt->fmt.pix_mp.num_planes = 1;
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
+ inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
+ inst->buffers[BUF_INPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+
+ output_fmt = inst->fmt_dst;
+ output_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ output_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ output_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ output_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+
+ inst->crop.left = 0;
+ inst->crop.top = 0;
+ inst->crop.width = f->fmt.pix_mp.width;
+ inst->crop.height = f->fmt.pix_mp.height;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ fmt = inst->fmt_dst;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12)
+ return -EINVAL;
+ fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
+ fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
+ fmt->fmt.pix_mp.num_planes = 1;
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(f->fmt.pix_mp.width, 128);
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ inst->crop.top = 0;
+ inst->crop.left = 0;
+ inst->crop.width = f->fmt.pix_mp.width;
+ inst->crop.height = f->fmt.pix_mp.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+ memcpy(f, fmt, sizeof(*fmt));
+
+ return 0;
+}
+
+int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub)
+{
+ int ret = 0;
+
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ ret = v4l2_event_subscribe(&inst->fh, sub, 0, NULL);
+ break;
+ case V4L2_EVENT_SOURCE_CHANGE:
+ ret = v4l2_src_change_event_subscribe(&inst->fh, sub);
+ break;
+ case V4L2_EVENT_CTRL:
+ ret = v4l2_ctrl_subscribe_event(&inst->fh, sub);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void iris_vdec_src_change(struct iris_inst *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_event event = {0};
+ struct vb2_queue *src_q;
+
+ src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+ if (!vb2_is_streaming(src_q))
+ return;
+
+ event.type = V4L2_EVENT_SOURCE_CHANGE;
+ event.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION;
+ v4l2_event_queue_fh(&inst->fh, &event);
+}
+
+static int iris_vdec_get_num_queued_buffers(struct iris_inst *inst,
+ enum iris_buffer_type type)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+ u32 count = 0;
+
+ switch (type) {
+ case BUF_INPUT:
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ continue;
+ count++;
+ }
+ return count;
+ case BUF_OUTPUT:
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ continue;
+ count++;
+ }
+ return count;
+ default:
+ return count;
+ }
+}
+
+static void iris_vdec_flush_deferred_buffers(struct iris_inst *inst,
+ enum iris_buffer_type type)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+
+ if (type == BUF_INPUT) {
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEFERRED) {
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ buf->data_size = 0;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEFERRED) {
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ buf->data_size = 0;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+ }
+}
+
+static void iris_vdec_kill_session(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+
+ if (!inst->session_id)
+ return;
+
+ hfi_ops->session_close(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+}
+
+int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ enum iris_buffer_type buffer_type;
+ u32 count;
+ int ret;
+
+ switch (plane) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ buffer_type = BUF_INPUT;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ buffer_type = BUF_OUTPUT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = hfi_ops->session_stop(inst, plane);
+ if (ret)
+ goto error;
+
+ count = iris_vdec_get_num_queued_buffers(inst, buffer_type);
+ if (count) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = iris_inst_state_change_streamoff(inst, plane);
+ if (ret)
+ goto error;
+
+ iris_vdec_flush_deferred_buffers(inst, buffer_type);
+
+ return 0;
+
+error:
+ iris_vdec_kill_session(inst);
+ iris_vdec_flush_deferred_buffers(inst, buffer_type);
+
+ return ret;
+}
+
+static int iris_vdec_process_streamon_input(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ enum iris_inst_sub_state set_sub_state = 0;
+ int ret;
+
+ iris_scale_power(inst);
+
+ ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_inst_change_sub_state(inst, IRIS_INST_SUB_INPUT_PAUSE, 0);
+ if (ret)
+ return ret;
+ }
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC ||
+ inst->sub_state & IRIS_INST_SUB_DRAIN ||
+ inst->sub_state & IRIS_INST_SUB_FIRST_IPSC) {
+ if (!(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE)) {
+ if (hfi_ops->session_pause) {
+ ret = hfi_ops->session_pause(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ }
+ set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ }
+
+ ret = iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_vdec_streamon_input(struct iris_inst *inst)
+{
+ int ret;
+
+ ret = iris_set_properties(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_alloc_and_queue_persist_bufs(inst);
+ if (ret)
+ return ret;
+
+ iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_vdec_process_streamon_input(inst);
+}
+
+static int iris_vdec_process_streamon_output(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ bool drain_active = false, drc_active = false;
+ enum iris_inst_sub_state clear_sub_state = 0;
+ int ret = 0;
+
+ iris_scale_power(inst);
+
+ drain_active = inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+
+ drc_active = inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST;
+
+ if (drc_active)
+ clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+ else if (drain_active)
+ clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_alloc_and_queue_input_int_bufs(inst);
+ if (ret)
+ return ret;
+ ret = iris_set_stage(inst, STAGE);
+ if (ret)
+ return ret;
+ ret = iris_set_pipe(inst, PIPE);
+ if (ret)
+ return ret;
+ }
+
+ if (inst->state == IRIS_INST_INPUT_STREAMING &&
+ inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ if (!drain_active)
+ ret = hfi_ops->session_resume_drc(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ else if (hfi_ops->session_resume_drain)
+ ret = hfi_ops->session_resume_drain(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+
+ if (inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)
+ clear_sub_state |= IRIS_INST_SUB_FIRST_IPSC;
+
+ ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE)
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+
+ ret = iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, clear_sub_state, 0);
+}
+
+int iris_vdec_streamon_output(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_vdec_process_streamon_output(inst);
+ if (ret)
+ goto error;
+
+ ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ goto error;
+
+ return ret;
+
+error:
+ iris_vdec_session_streamoff(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ return ret;
+}
+
+static int
+iris_vdec_vb2_buffer_to_driver(struct vb2_buffer *vb2, struct iris_buffer *buf)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
+
+ buf->type = iris_v4l2_type_to_driver(vb2->type);
+ buf->index = vb2->index;
+ buf->fd = vb2->planes[0].m.fd;
+ buf->buffer_size = vb2->planes[0].length;
+ buf->data_offset = vb2->planes[0].data_offset;
+ buf->data_size = vb2->planes[0].bytesused - vb2->planes[0].data_offset;
+ buf->flags = vbuf->flags;
+ buf->timestamp = vb2->timestamp;
+ buf->attr = 0;
+
+ return 0;
+}
+
+static void
+iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ struct vb2_buffer *vb = &vbuf->vb2_buf;
+ u64 ts_us = vb->timestamp;
+
+ if (inst->metadata_idx >= ARRAY_SIZE(inst->tss))
+ inst->metadata_idx = 0;
+
+ do_div(ts_us, NSEC_PER_USEC);
+
+ inst->tss[inst->metadata_idx].flags = vbuf->flags & mask;
+ inst->tss[inst->metadata_idx].tc = vbuf->timecode;
+ inst->tss[inst->metadata_idx].ts_us = ts_us;
+ inst->tss[inst->metadata_idx].ts_ns = vb->timestamp;
+
+ inst->metadata_idx++;
+}
+
+int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ struct iris_buffer *buf = to_iris_buffer(vbuf);
+ struct vb2_buffer *vb2 = &vbuf->vb2_buf;
+ struct vb2_queue *q;
+ int ret;
+
+ ret = iris_vdec_vb2_buffer_to_driver(vb2, buf);
+ if (ret)
+ return ret;
+
+ if (buf->type == BUF_INPUT)
+ iris_set_ts_metadata(inst, vbuf);
+
+ q = v4l2_m2m_get_vq(inst->m2m_ctx, vb2->type);
+ if (!vb2_is_streaming(q)) {
+ buf->attr |= BUF_ATTR_DEFERRED;
+ return 0;
+ }
+
+ iris_scale_power(inst);
+
+ return iris_queue_buffer(inst, buf);
+}
+
+int iris_vdec_start_cmd(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ enum iris_inst_sub_state clear_sub_state = 0;
+ struct vb2_queue *dst_vq;
+ int ret;
+
+ dst_vq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST) {
+ vb2_clear_last_buffer_dequeued(dst_vq);
+ clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = hfi_ops->session_resume_drc(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ ret = hfi_ops->session_resume_drc(inst,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+ } else if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) {
+ vb2_clear_last_buffer_dequeued(dst_vq);
+ clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ if (hfi_ops->session_resume_drain) {
+ ret =
+ hfi_ops->session_resume_drain(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ }
+
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ if (hfi_ops->session_resume_drain) {
+ ret =
+ hfi_ops->session_resume_drain(inst,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+ }
+
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+ } else {
+ dev_err(inst->core->dev, "start called before receiving last_flag\n");
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return -EBUSY;
+ }
+
+ return iris_inst_change_sub_state(inst, clear_sub_state, 0);
+}
+
+int iris_vdec_stop_cmd(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h
new file mode 100644
index 000000000000..b24932dc511a
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vdec.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VDEC_H__
+#define __IRIS_VDEC_H__
+
+struct iris_inst;
+
+int iris_vdec_inst_init(struct iris_inst *inst);
+void iris_vdec_inst_deinit(struct iris_inst *inst);
+int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f);
+int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f);
+int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f);
+int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub);
+void iris_vdec_src_change(struct iris_inst *inst);
+int iris_vdec_streamon_input(struct iris_inst *inst);
+int iris_vdec_streamon_output(struct iris_inst *inst);
+int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf);
+int iris_vdec_start_cmd(struct iris_inst *inst);
+int iris_vdec_stop_cmd(struct iris_inst *inst);
+int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
new file mode 100644
index 000000000000..ca0f4e310f77
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "iris_vidc.h"
+#include "iris_instance.h"
+#include "iris_vdec.h"
+#include "iris_vb2.h"
+#include "iris_vpu_buffer.h"
+#include "iris_platform_common.h"
+
+#define IRIS_DRV_NAME "iris_driver"
+#define IRIS_BUS_NAME "platform:iris_icc"
+#define STEP_WIDTH 1
+#define STEP_HEIGHT 1
+
+static void iris_v4l2_fh_init(struct iris_inst *inst)
+{
+ v4l2_fh_init(&inst->fh, inst->core->vdev_dec);
+ inst->fh.ctrl_handler = &inst->ctrl_handler;
+ v4l2_fh_add(&inst->fh);
+}
+
+static void iris_v4l2_fh_deinit(struct iris_inst *inst)
+{
+ v4l2_fh_del(&inst->fh);
+ inst->fh.ctrl_handler = NULL;
+ v4l2_fh_exit(&inst->fh);
+}
+
+static void iris_add_session(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *iter;
+ u32 count = 0;
+
+ mutex_lock(&core->lock);
+
+ list_for_each_entry(iter, &core->instances, list)
+ count++;
+
+ if (count < core->iris_platform_data->max_session_count)
+ list_add_tail(&inst->list, &core->instances);
+
+ mutex_unlock(&core->lock);
+}
+
+static void iris_remove_session(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *iter, *temp;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry_safe(iter, temp, &core->instances, list) {
+ if (iter->session_id == inst->session_id) {
+ list_del_init(&iter->list);
+ break;
+ }
+ }
+ mutex_unlock(&core->lock);
+}
+
+static inline struct iris_inst *iris_get_inst(struct file *filp, void *fh)
+{
+ return container_of(filp->private_data, struct iris_inst, fh);
+}
+
+static void iris_m2m_device_run(void *priv)
+{
+}
+
+static void iris_m2m_job_abort(void *priv)
+{
+ struct iris_inst *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+
+ v4l2_m2m_job_finish(inst->m2m_dev, m2m_ctx);
+}
+
+static const struct v4l2_m2m_ops iris_m2m_ops = {
+ .device_run = iris_m2m_device_run,
+ .job_abort = iris_m2m_job_abort,
+};
+
+static int
+iris_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ struct iris_inst *inst = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->ops = inst->core->iris_vb2_ops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->drv_priv = inst;
+ src_vq->buf_struct_size = sizeof(struct iris_buffer);
+ src_vq->min_reqbufs_allocation = MIN_BUFFERS;
+ src_vq->dev = inst->core->dev;
+ src_vq->lock = &inst->ctx_q_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->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->ops = inst->core->iris_vb2_ops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->drv_priv = inst;
+ dst_vq->buf_struct_size = sizeof(struct iris_buffer);
+ dst_vq->min_reqbufs_allocation = MIN_BUFFERS;
+ dst_vq->dev = inst->core->dev;
+ dst_vq->lock = &inst->ctx_q_lock;
+
+ return vb2_queue_init(dst_vq);
+}
+
+int iris_open(struct file *filp)
+{
+ struct iris_core *core = video_drvdata(filp);
+ struct iris_inst *inst;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = iris_core_init(core);
+ if (ret) {
+ dev_err(core->dev, "core init failed\n");
+ pm_runtime_put_sync(core->dev);
+ return ret;
+ }
+
+ pm_runtime_put_sync(core->dev);
+
+ inst = core->iris_platform_data->get_instance();
+ if (!inst)
+ return -ENOMEM;
+
+ inst->core = core;
+ inst->session_id = hash32_ptr(inst);
+ inst->state = IRIS_INST_DEINIT;
+
+ mutex_init(&inst->lock);
+ mutex_init(&inst->ctx_q_lock);
+
+ INIT_LIST_HEAD(&inst->buffers[BUF_BIN].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_ARP].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_COMV].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_NON_COMV].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_LINE].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_DPB].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_PERSIST].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_1].list);
+ init_completion(&inst->completion);
+ init_completion(&inst->flush_completion);
+
+ iris_v4l2_fh_init(inst);
+
+ inst->m2m_dev = v4l2_m2m_init(&iris_m2m_ops);
+ if (IS_ERR_OR_NULL(inst->m2m_dev)) {
+ ret = -EINVAL;
+ goto fail_v4l2_fh_deinit;
+ }
+
+ inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, iris_m2m_queue_init);
+ if (IS_ERR_OR_NULL(inst->m2m_ctx)) {
+ ret = -EINVAL;
+ goto fail_m2m_release;
+ }
+
+ ret = iris_vdec_inst_init(inst);
+ if (ret)
+ goto fail_m2m_ctx_release;
+
+ iris_add_session(inst);
+
+ inst->fh.m2m_ctx = inst->m2m_ctx;
+ filp->private_data = &inst->fh;
+
+ return 0;
+
+fail_m2m_ctx_release:
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+fail_m2m_release:
+ v4l2_m2m_release(inst->m2m_dev);
+fail_v4l2_fh_deinit:
+ iris_v4l2_fh_deinit(inst);
+ mutex_destroy(&inst->ctx_q_lock);
+ mutex_destroy(&inst->lock);
+ kfree(inst);
+
+ return ret;
+}
+
+static void iris_session_close(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ bool wait_for_response = true;
+ int ret;
+
+ if (inst->state == IRIS_INST_DEINIT)
+ return;
+
+ reinit_completion(&inst->completion);
+
+ ret = hfi_ops->session_close(inst);
+ if (ret)
+ wait_for_response = false;
+
+ if (wait_for_response)
+ iris_wait_for_session_response(inst, false);
+}
+
+int iris_close(struct file *filp)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+ v4l2_m2m_release(inst->m2m_dev);
+ mutex_lock(&inst->lock);
+ iris_vdec_inst_deinit(inst);
+ iris_session_close(inst);
+ iris_inst_change_state(inst, IRIS_INST_DEINIT);
+ iris_v4l2_fh_deinit(inst);
+ iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ iris_remove_session(inst);
+ mutex_unlock(&inst->lock);
+ mutex_destroy(&inst->ctx_q_lock);
+ mutex_destroy(&inst->lock);
+ kfree(inst);
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+static int iris_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+
+ if (f->index)
+ return -EINVAL;
+
+ return iris_vdec_enum_fmt(inst, f);
+}
+
+static int iris_try_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret;
+
+ mutex_lock(&inst->lock);
+ ret = iris_vdec_try_fmt(inst, f);
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_s_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret;
+
+ mutex_lock(&inst->lock);
+ ret = iris_vdec_s_fmt(inst, f);
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_g_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret = 0;
+
+ mutex_lock(&inst->lock);
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ *f = *inst->fmt_src;
+ else if (V4L2_TYPE_IS_CAPTURE(f->type))
+ *f = *inst->fmt_dst;
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_enum_framesizes(struct file *filp, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ struct platform_inst_caps *caps;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ if (fsize->pixel_format != V4L2_PIX_FMT_H264 &&
+ fsize->pixel_format != V4L2_PIX_FMT_NV12)
+ return -EINVAL;
+
+ caps = inst->core->iris_platform_data->inst_caps;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = caps->min_frame_width;
+ fsize->stepwise.max_width = caps->max_frame_width;
+ fsize->stepwise.step_width = STEP_WIDTH;
+ fsize->stepwise.min_height = caps->min_frame_height;
+ fsize->stepwise.max_height = caps->max_frame_height;
+ fsize->stepwise.step_height = STEP_HEIGHT;
+
+ return 0;
+}
+
+static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, IRIS_DRV_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "Iris Decoder", sizeof(cap->card));
+
+ return 0;
+}
+
+static int iris_g_selection(struct file *filp, void *fh, struct v4l2_selection *s)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.left = inst->crop.left;
+ s->r.top = inst->crop.top;
+ s->r.width = inst->crop.width;
+ s->r.height = inst->crop.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iris_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+ struct iris_inst *inst = container_of(fh, struct iris_inst, fh);
+
+ return iris_vdec_subscribe_event(inst, sub);
+}
+
+static int iris_dec_cmd(struct file *filp, void *fh,
+ struct v4l2_decoder_cmd *dec)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret = 0;
+
+ mutex_lock(&inst->lock);
+
+ ret = v4l2_m2m_ioctl_decoder_cmd(filp, fh, dec);
+ if (ret)
+ goto unlock;
+
+ if (inst->state == IRIS_INST_DEINIT)
+ goto unlock;
+
+ if (!iris_allow_cmd(inst, dec->cmd)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (dec->cmd == V4L2_DEC_CMD_START)
+ ret = iris_vdec_start_cmd(inst);
+ else if (dec->cmd == V4L2_DEC_CMD_STOP)
+ ret = iris_vdec_stop_cmd(inst);
+ else
+ ret = -EINVAL;
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static struct v4l2_file_operations iris_v4l2_file_ops = {
+ .owner = THIS_MODULE,
+ .open = iris_open,
+ .release = iris_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct vb2_ops iris_vb2_ops = {
+ .buf_init = iris_vb2_buf_init,
+ .queue_setup = iris_vb2_queue_setup,
+ .start_streaming = iris_vb2_start_streaming,
+ .stop_streaming = iris_vb2_stop_streaming,
+ .buf_prepare = iris_vb2_buf_prepare,
+ .buf_out_validate = iris_vb2_buf_out_validate,
+ .buf_queue = iris_vb2_buf_queue,
+};
+
+static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops = {
+ .vidioc_enum_fmt_vid_cap = iris_enum_fmt,
+ .vidioc_enum_fmt_vid_out = iris_enum_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = iris_try_fmt_vid_mplane,
+ .vidioc_try_fmt_vid_out_mplane = iris_try_fmt_vid_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = iris_s_fmt_vid_mplane,
+ .vidioc_s_fmt_vid_out_mplane = iris_s_fmt_vid_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = iris_g_fmt_vid_mplane,
+ .vidioc_g_fmt_vid_out_mplane = iris_g_fmt_vid_mplane,
+ .vidioc_enum_framesizes = iris_enum_framesizes,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
+ .vidioc_querycap = iris_querycap,
+ .vidioc_g_selection = iris_g_selection,
+ .vidioc_subscribe_event = iris_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
+ .vidioc_decoder_cmd = iris_dec_cmd,
+};
+
+void iris_init_ops(struct iris_core *core)
+{
+ core->iris_v4l2_file_ops = &iris_v4l2_file_ops;
+ core->iris_vb2_ops = &iris_vb2_ops;
+ core->iris_v4l2_ioctl_ops = &iris_v4l2_ioctl_ops;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.h b/drivers/media/platform/qcom/iris/iris_vidc.h
new file mode 100644
index 000000000000..a26054ff55b5
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vidc.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VIDC_H__
+#define __IRIS_VIDC_H__
+
+struct iris_core;
+
+void iris_init_ops(struct iris_core *core);
+int iris_open(struct file *filp);
+int iris_close(struct file *filp);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c
new file mode 100644
index 000000000000..7cf1bfc352d3
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu2.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+
+static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size)
+{
+ struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 mbs_per_second, mbpf, height, width;
+ unsigned long vpp_freq, vsp_freq;
+ u32 fps = DEFAULT_FPS;
+
+ width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+ height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+
+ mbpf = NUM_MBS_PER_FRAME(height, width);
+ mbs_per_second = mbpf * fps;
+
+ vpp_freq = mbs_per_second * caps->mb_cycles_vpp;
+
+ /* 21 / 20 is overhead factor */
+ vpp_freq += vpp_freq / 20;
+ vsp_freq = mbs_per_second * caps->mb_cycles_vsp;
+
+ /* 10 / 7 is overhead factor */
+ vsp_freq += ((fps * data_size * 8) * 10) / 7;
+
+ return max(vpp_freq, vsp_freq);
+}
+
+const struct vpu_ops iris_vpu2_ops = {
+ .power_off_hw = iris_vpu_power_off_hw,
+ .power_off_controller = iris_vpu_power_off_controller,
+ .calc_freq = iris_vpu2_calc_freq,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c
new file mode 100644
index 000000000000..9b7c9a1495ee
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
+#define WRAPPER_TZ_BASE_OFFS 0x000C0000
+#define AON_BASE_OFFS 0x000E0000
+#define AON_MVP_NOC_RESET 0x0001F000
+
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
+#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
+#define REQ_POWER_DOWN_PREP BIT(0)
+#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
+#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88)
+#define CORE_CLK_RUN 0x0
+
+#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
+#define CTL_AXI_CLK_HALT BIT(0)
+#define CTL_CLK_HALT BIT(1)
+
+#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
+#define RESET_HIGH BIT(0)
+
+#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160)
+#define CORE_BRIDGE_SW_RESET BIT(0)
+#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1)
+
+#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
+#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
+#define MSK_CORE_POWER_ON BIT(1)
+
+#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
+#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1))
+
+#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004)
+
+#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70)
+
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
+
+#define AON_WRAPPER_MVP_NOC_CORE_SW_RESET (AON_BASE_OFFS + 0x18)
+#define SW_RESET BIT(0)
+#define AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL (AON_BASE_OFFS + 0x20)
+#define NOC_HALT BIT(0)
+#define AON_WRAPPER_SPARE (AON_BASE_OFFS + 0x28)
+
+static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core)
+{
+ u32 value, pwr_status;
+
+ value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
+ pwr_status = value & BIT(1);
+
+ return pwr_status ? false : true;
+}
+
+static void iris_vpu3_power_off_hardware(struct iris_core *core)
+{
+ u32 reg_val = 0, value, i;
+ int ret;
+
+ if (iris_vpu3x_hw_power_collapsed(core))
+ goto disable_power;
+
+ dev_err(core->dev, "video hw is power on\n");
+
+ value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+ if (value)
+ writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
+ ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i,
+ reg_val, reg_val & 0x400000, 2000, 20000);
+ if (ret)
+ goto disable_power;
+ }
+
+ writel(VIDEO_NOC_RESET_REQ, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ reg_val, reg_val & 0x3, 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ reg_val, !(reg_val & 0x3), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE,
+ core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+
+disable_power:
+ iris_vpu_power_off_hw(core);
+}
+
+static void iris_vpu33_power_off_hardware(struct iris_core *core)
+{
+ u32 reg_val = 0, value, i;
+ int ret;
+
+ if (iris_vpu3x_hw_power_collapsed(core))
+ goto disable_power;
+
+ dev_err(core->dev, "video hw is power on\n");
+
+ value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+ if (value)
+ writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
+ ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i,
+ reg_val, reg_val & 0x400000, 2000, 20000);
+ if (ret)
+ goto disable_power;
+ }
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ reg_val, reg_val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ /* set MNoC to low power, set PD_NOC_QREQ (bit 0) */
+ writel(BIT(0), core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE,
+ core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+
+disable_power:
+ iris_vpu_power_off_hw(core);
+}
+
+static int iris_vpu33_power_off_controller(struct iris_core *core)
+{
+ u32 xo_rst_tbl_size = core->iris_platform_data->controller_rst_tbl_size;
+ u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
+ u32 val = 0;
+ int ret;
+
+ writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
+
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
+ val, val == 0, 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT,
+ core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+ writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+
+ reset_control_bulk_reset(clk_rst_tbl_size, core->resets);
+
+ /* Disable MVP NoC clock */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+ val |= NOC_HALT;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+
+ /* enable MVP NoC reset */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+ val |= SW_RESET;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+
+ /* poll AON spare register bit0 to become zero with 50ms timeout */
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_SPARE,
+ val, (val & BIT(0)) == 0, 1000, 50000);
+ if (ret)
+ goto disable_power;
+
+ /* enable bit(1) to avoid cvp noc xo reset */
+ val = readl(core->reg_base + AON_WRAPPER_SPARE);
+ val |= BIT(1);
+ writel(val, core->reg_base + AON_WRAPPER_SPARE);
+
+ reset_control_bulk_assert(xo_rst_tbl_size, core->controller_resets);
+
+ /* De-assert MVP NoC reset */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+ val &= ~SW_RESET;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+
+ usleep_range(80, 100);
+
+ reset_control_bulk_deassert(xo_rst_tbl_size, core->controller_resets);
+
+ /* reset AON spare register */
+ writel(0, core->reg_base + AON_WRAPPER_SPARE);
+
+ /* Enable MVP NoC clock */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+ val &= ~NOC_HALT;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+
+disable_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+
+ return 0;
+}
+
+static u64 iris_vpu3x_calculate_frequency(struct iris_inst *inst, size_t data_size)
+{
+ struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 height, width, mbs_per_second, mbpf;
+ u64 fw_cycles, fw_vpp_cycles;
+ u64 vsp_cycles, vpp_cycles;
+ u32 fps = DEFAULT_FPS;
+
+ width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+ height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+
+ mbpf = NUM_MBS_PER_FRAME(height, width);
+ mbs_per_second = mbpf * fps;
+
+ fw_cycles = fps * caps->mb_cycles_fw;
+ fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp;
+
+ vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value);
+ /* 21 / 20 is minimum overhead factor */
+ vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles);
+
+ /* 1.059 is multi-pipe overhead */
+ if (inst->fw_caps[PIPE].value > 1)
+ vpp_cycles += div_u64(vpp_cycles * 59, 1000);
+
+ vsp_cycles = fps * data_size * 8;
+ vsp_cycles = div_u64(vsp_cycles, 2);
+ /* VSP FW overhead 1.05 */
+ vsp_cycles = div_u64(vsp_cycles * 21, 20);
+
+ if (inst->fw_caps[STAGE].value == STAGE_1)
+ vsp_cycles = vsp_cycles * 3;
+
+ return max3(vpp_cycles, vsp_cycles, fw_cycles);
+}
+
+const struct vpu_ops iris_vpu3_ops = {
+ .power_off_hw = iris_vpu3_power_off_hardware,
+ .power_off_controller = iris_vpu_power_off_controller,
+ .calc_freq = iris_vpu3x_calculate_frequency,
+};
+
+const struct vpu_ops iris_vpu33_ops = {
+ .power_off_hw = iris_vpu33_power_off_hardware,
+ .power_off_controller = iris_vpu33_power_off_controller,
+ .calc_freq = iris_vpu3x_calculate_frequency,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
new file mode 100644
index 000000000000..dce25e410d80
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_instance.h"
+#include "iris_vpu_buffer.h"
+
+static u32 size_h264d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 size_yuv, size_bin_hdr, size_bin_res;
+
+ size_yuv = ((frame_width * frame_height) <= BIN_BUFFER_THRESHOLD) ?
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1) :
+ ((frame_width * frame_height * 3) >> 1);
+ size_bin_hdr = size_yuv * H264_CABAC_HDR_RATIO_HD_TOT;
+ size_bin_res = size_yuv * H264_CABAC_RES_RATIO_HD_TOT;
+ size_bin_hdr = ALIGN(size_bin_hdr / num_vpp_pipes,
+ DMA_ALIGNMENT) * num_vpp_pipes;
+ size_bin_res = ALIGN(size_bin_res / num_vpp_pipes,
+ DMA_ALIGNMENT) * num_vpp_pipes;
+
+ return size_bin_hdr + size_bin_res;
+}
+
+static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 n_aligned_h = ALIGN(frame_height, 16);
+ u32 n_aligned_w = ALIGN(frame_width, 16);
+
+ return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
+}
+
+static u32 hfi_buffer_comv_h264d(u32 frame_width, u32 frame_height, u32 _comv_bufcount)
+{
+ u32 frame_height_in_mbs = DIV_ROUND_UP(frame_height, 16);
+ u32 frame_width_in_mbs = DIV_ROUND_UP(frame_width, 16);
+ u32 col_zero_aligned_width = (frame_width_in_mbs << 2);
+ u32 col_mv_aligned_width = (frame_width_in_mbs << 7);
+ u32 col_zero_size, size_colloc;
+
+ col_mv_aligned_width = ALIGN(col_mv_aligned_width, 16);
+ col_zero_aligned_width = ALIGN(col_zero_aligned_width, 16);
+ col_zero_size = col_zero_aligned_width *
+ ((frame_height_in_mbs + 1) >> 1);
+ col_zero_size = ALIGN(col_zero_size, 64);
+ col_zero_size <<= 1;
+ col_zero_size = ALIGN(col_zero_size, 512);
+ size_colloc = col_mv_aligned_width * ((frame_height_in_mbs + 1) >> 1);
+ size_colloc = ALIGN(size_colloc, 64);
+ size_colloc <<= 1;
+ size_colloc = ALIGN(size_colloc, 512);
+ size_colloc += (col_zero_size + SIZE_H264D_BUFTAB_T * 2);
+
+ return (size_colloc * (_comv_bufcount)) + 512;
+}
+
+static u32 size_h264d_bse_cmd_buf(u32 frame_height)
+{
+ u32 height = ALIGN(frame_height, 32);
+
+ return min_t(u32, (DIV_ROUND_UP(height, 16) * 48), H264D_MAX_SLICE) *
+ SIZE_H264D_BSE_CMD_PER_BUF;
+}
+
+static u32 size_h264d_vpp_cmd_buf(u32 frame_height)
+{
+ u32 size, height = ALIGN(frame_height, 32);
+
+ size = min_t(u32, (DIV_ROUND_UP(height, 16) * 48), H264D_MAX_SLICE) *
+ SIZE_H264D_VPP_CMD_PER_BUF;
+
+ return size > VPP_CMD_MAX_SIZE ? VPP_CMD_MAX_SIZE : size;
+}
+
+static u32 hfi_buffer_persist_h264d(void)
+{
+ return ALIGN(SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264 +
+ H264_DISPLAY_BUF_SIZE * H264_NUM_FRM_INFO +
+ NUM_HW_PIC_BUF * SIZE_SEI_USERDATA,
+ DMA_ALIGNMENT);
+}
+
+static u32 hfi_buffer_non_comv_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 size_bse, size_vpp, size;
+
+ size_bse = size_h264d_bse_cmd_buf(frame_height);
+ size_vpp = size_h264d_vpp_cmd_buf(frame_height);
+ size = ALIGN(size_bse, DMA_ALIGNMENT) +
+ ALIGN(size_vpp, DMA_ALIGNMENT) +
+ ALIGN(SIZE_HW_PIC(SIZE_H264D_HW_PIC_T), DMA_ALIGNMENT);
+
+ return ALIGN(size, DMA_ALIGNMENT);
+}
+
+static u32 size_vpss_lb(u32 frame_width, u32 frame_height)
+{
+ u32 opb_lb_wr_llb_y_buffer_size, opb_lb_wr_llb_uv_buffer_size;
+ u32 opb_wr_top_line_chroma_buffer_size;
+ u32 opb_wr_top_line_luma_buffer_size;
+ u32 macrotiling_size = 32;
+
+ opb_wr_top_line_luma_buffer_size =
+ ALIGN(frame_width, macrotiling_size) / macrotiling_size * 256;
+ opb_wr_top_line_luma_buffer_size =
+ ALIGN(opb_wr_top_line_luma_buffer_size, DMA_ALIGNMENT) +
+ (MAX_TILE_COLUMNS - 1) * 256;
+ opb_wr_top_line_luma_buffer_size =
+ max_t(u32, opb_wr_top_line_luma_buffer_size, (32 * ALIGN(frame_height, 8)));
+ opb_wr_top_line_chroma_buffer_size = opb_wr_top_line_luma_buffer_size;
+ opb_lb_wr_llb_uv_buffer_size =
+ ALIGN((ALIGN(frame_height, 8) / (4 / 2)) * 64, 32);
+ opb_lb_wr_llb_y_buffer_size =
+ ALIGN((ALIGN(frame_height, 8) / (4 / 2)) * 64, 32);
+ return opb_wr_top_line_luma_buffer_size +
+ opb_wr_top_line_chroma_buffer_size +
+ opb_lb_wr_llb_uv_buffer_size +
+ opb_lb_wr_llb_y_buffer_size;
+}
+
+static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height,
+ bool is_opb, u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0;
+ u32 size;
+
+ size = ALIGN(size_h264d_lb_fe_top_data(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_fe_top_ctrl(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_fe_left_ctrl(frame_height), DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h264d_lb_se_top_ctrl(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_se_left_ctrl(frame_height), DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h264d_lb_pe_top_data(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_vsp_top(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_recon_dma_metadata_wr(frame_height), DMA_ALIGNMENT) * 2 +
+ ALIGN(size_h264d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+ size = ALIGN(size, DMA_ALIGNMENT);
+ if (is_opb)
+ vpss_lb_size = size_vpss_lb(frame_width, frame_height);
+
+ return ALIGN((size + vpss_lb_size), DMA_ALIGNMENT);
+}
+
+static u32 iris_vpu_dec_bin_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_buffer_bin_h264d(width, height, num_vpp_pipes);
+}
+
+static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
+{
+ u32 num_comv = VIDEO_MAX_FRAME;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_buffer_comv_h264d(width, height, num_comv);
+}
+
+static u32 iris_vpu_dec_persist_size(struct iris_inst *inst)
+{
+ return hfi_buffer_persist_h264d();
+}
+
+static u32 iris_vpu_dec_dpb_size(struct iris_inst *inst)
+{
+ if (iris_split_mode_enabled(inst))
+ return iris_get_buffer_size(inst, BUF_DPB);
+ else
+ return 0;
+}
+
+static u32 iris_vpu_dec_non_comv_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_buffer_non_comv_h264d(width, height, num_vpp_pipes);
+}
+
+static u32 iris_vpu_dec_line_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+ bool is_opb = false;
+
+ if (iris_split_mode_enabled(inst))
+ is_opb = true;
+
+ return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes);
+}
+
+static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst)
+{
+ return iris_vpu_dec_comv_size(inst) +
+ iris_vpu_dec_non_comv_size(inst) +
+ iris_vpu_dec_line_size(inst);
+}
+
+struct iris_vpu_buf_type_handle {
+ enum iris_buffer_type type;
+ u32 (*handle)(struct iris_inst *inst);
+};
+
+int iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
+{
+ const struct iris_vpu_buf_type_handle *buf_type_handle_arr;
+ u32 size = 0, buf_type_handle_size, i;
+
+ static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = {
+ {BUF_BIN, iris_vpu_dec_bin_size },
+ {BUF_COMV, iris_vpu_dec_comv_size },
+ {BUF_NON_COMV, iris_vpu_dec_non_comv_size },
+ {BUF_LINE, iris_vpu_dec_line_size },
+ {BUF_PERSIST, iris_vpu_dec_persist_size },
+ {BUF_DPB, iris_vpu_dec_dpb_size },
+ {BUF_SCRATCH_1, iris_vpu_dec_scratch1_size },
+ };
+
+ buf_type_handle_size = ARRAY_SIZE(dec_internal_buf_type_handle);
+ buf_type_handle_arr = dec_internal_buf_type_handle;
+
+ for (i = 0; i < buf_type_handle_size; i++) {
+ if (buf_type_handle_arr[i].type == buffer_type) {
+ size = buf_type_handle_arr[i].handle(inst);
+ break;
+ }
+ }
+
+ return size;
+}
+
+static inline int iris_vpu_dpb_count(struct iris_inst *inst)
+{
+ if (iris_split_mode_enabled(inst)) {
+ return inst->fw_min_count ?
+ inst->fw_min_count : inst->buffers[BUF_OUTPUT].min_count;
+ }
+
+ return 0;
+}
+
+int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return MIN_BUFFERS;
+ case BUF_OUTPUT:
+ return inst->fw_min_count;
+ case BUF_BIN:
+ case BUF_COMV:
+ case BUF_NON_COMV:
+ case BUF_LINE:
+ case BUF_PERSIST:
+ case BUF_SCRATCH_1:
+ return 1; /* internal buffer count needed by firmware is 1 */
+ case BUF_DPB:
+ return iris_vpu_dpb_count(inst);
+ default:
+ return 0;
+ }
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
new file mode 100644
index 000000000000..62af6ea6ba1f
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VPU_BUFFER_H__
+#define __IRIS_VPU_BUFFER_H__
+
+struct iris_inst;
+
+#define MIN_BUFFERS 4
+
+#define DMA_ALIGNMENT 256
+
+#define NUM_HW_PIC_BUF 32
+#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf))
+
+#define MAX_TILE_COLUMNS 32
+#define BIN_BUFFER_THRESHOLD (1280 * 736)
+#define VPP_CMD_MAX_SIZE (BIT(20))
+#define H264D_MAX_SLICE 1800
+
+#define SIZE_H264D_BUFTAB_T 256
+#define SIZE_H264D_BSE_CMD_PER_BUF (32 * 4)
+#define SIZE_H264D_VPP_CMD_PER_BUF 512
+
+#define NUM_SLIST_BUF_H264 (256 + 32)
+#define SIZE_SLIST_BUF_H264 512
+#define H264_DISPLAY_BUF_SIZE 3328
+#define H264_NUM_FRM_INFO 66
+
+#define SIZE_SEI_USERDATA 4096
+
+#define H264_CABAC_HDR_RATIO_HD_TOT 1
+#define H264_CABAC_RES_RATIO_HD_TOT 3
+#define SIZE_H264D_HW_PIC_T (BIT(11))
+
+#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
+#define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 16
+#define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE 384
+#define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640
+
+static inline u32 size_h264d_lb_fe_top_data(u32 frame_width)
+{
+ return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN(frame_width, 16) * 3;
+}
+
+static inline u32 size_h264d_lb_fe_top_ctrl(u32 frame_width)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
+}
+
+static inline u32 size_h264d_lb_fe_left_ctrl(u32 frame_height)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_height, 16);
+}
+
+static inline u32 size_h264d_lb_se_top_ctrl(u32 frame_width)
+{
+ return MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
+}
+
+static inline u32 size_h264d_lb_se_left_ctrl(u32 frame_height)
+{
+ return MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_height, 16);
+}
+
+static inline u32 size_h264d_lb_pe_top_data(u32 frame_width)
+{
+ return MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
+}
+
+static inline u32 size_h264d_lb_vsp_top(u32 frame_width)
+{
+ return (DIV_ROUND_UP(frame_width, 16) << 7);
+}
+
+static inline u32 size_h264d_lb_recon_dma_metadata_wr(u32 frame_height)
+{
+ return ALIGN(frame_height, 16) * 32;
+}
+
+static inline u32 size_h264d_qp(u32 frame_width, u32 frame_height)
+{
+ return DIV_ROUND_UP(frame_width, 64) * DIV_ROUND_UP(frame_height, 64) * 128;
+}
+
+int iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c
new file mode 100644
index 000000000000..268e45acaa7c
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/pm_opp.h>
+#include <linux/reset.h>
+
+#include "iris_core.h"
+#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
+#define WRAPPER_TZ_BASE_OFFS 0x000C0000
+#define AON_BASE_OFFS 0x000E0000
+
+#define CPU_IC_BASE_OFFS (CPU_BASE_OFFS)
+
+#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C)
+#define CLEAR_XTENSA2HOST_INTR BIT(0)
+
+#define CTRL_INIT (CPU_CS_BASE_OFFS + 0x48)
+#define CTRL_STATUS (CPU_CS_BASE_OFFS + 0x4C)
+
+#define CTRL_INIT_IDLE_MSG_BMSK 0x40000000
+#define CTRL_ERROR_STATUS__M 0xfe
+#define CTRL_STATUS_PC_READY 0x100
+
+#define QTBL_INFO (CPU_CS_BASE_OFFS + 0x50)
+#define QTBL_ENABLE BIT(0)
+
+#define QTBL_ADDR (CPU_CS_BASE_OFFS + 0x54)
+#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE_OFFS + 0x58)
+#define SFR_ADDR (CPU_CS_BASE_OFFS + 0x5C)
+#define UC_REGION_ADDR (CPU_CS_BASE_OFFS + 0x64)
+#define UC_REGION_SIZE (CPU_CS_BASE_OFFS + 0x68)
+
+#define CPU_CS_H2XSOFTINTEN (CPU_CS_BASE_OFFS + 0x148)
+#define HOST2XTENSA_INTR_ENABLE BIT(0)
+
+#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
+#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
+#define MSK_CORE_POWER_ON BIT(1)
+
+#define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150)
+#define CPU_IC_SOFTINT_H2A_SHFT 0x0
+
+#define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C)
+#define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3)
+#define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2)
+
+#define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10)
+#define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3)
+#define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2)
+
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
+#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
+#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
+
+#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10)
+#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
+#define CTL_AXI_CLK_HALT BIT(0)
+#define CTL_CLK_HALT BIT(1)
+
+#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
+#define RESET_HIGH BIT(0)
+
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
+#define REQ_POWER_DOWN_PREP BIT(0)
+
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
+
+static void iris_vpu_interrupt_init(struct iris_core *core)
+{
+ u32 mask_val;
+
+ mask_val = readl(core->reg_base + WRAPPER_INTR_MASK);
+ mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK |
+ WRAPPER_INTR_MASK_A2HCPU_BMSK);
+ writel(mask_val, core->reg_base + WRAPPER_INTR_MASK);
+}
+
+static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core)
+{
+ u32 queue_size, value;
+
+ /* Iris hardware requires 4K queue alignment */
+ queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) +
+ (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K);
+
+ value = (u32)core->iface_q_table_daddr;
+ writel(value, core->reg_base + UC_REGION_ADDR);
+
+ /* Iris hardware requires 1M queue alignment */
+ value = ALIGN(SFR_SIZE + queue_size, SZ_1M);
+ writel(value, core->reg_base + UC_REGION_SIZE);
+
+ value = (u32)core->iface_q_table_daddr;
+ writel(value, core->reg_base + QTBL_ADDR);
+
+ writel(QTBL_ENABLE, core->reg_base + QTBL_INFO);
+
+ if (core->sfr_daddr) {
+ value = (u32)core->sfr_daddr + core->iris_platform_data->core_arch;
+ writel(value, core->reg_base + SFR_ADDR);
+ }
+}
+
+int iris_vpu_boot_firmware(struct iris_core *core)
+{
+ u32 ctrl_init = BIT(0), ctrl_status = 0, count = 0, max_tries = 1000;
+
+ iris_vpu_setup_ucregion_memory_map(core);
+
+ writel(ctrl_init, core->reg_base + CTRL_INIT);
+ writel(0x1, core->reg_base + CPU_CS_SCIACMDARG3);
+
+ while (!ctrl_status && count < max_tries) {
+ ctrl_status = readl(core->reg_base + CTRL_STATUS);
+ if ((ctrl_status & CTRL_ERROR_STATUS__M) == 0x4) {
+ dev_err(core->dev, "invalid setting for uc_region\n");
+ break;
+ }
+
+ usleep_range(50, 100);
+ count++;
+ }
+
+ if (count >= max_tries) {
+ dev_err(core->dev, "error booting up iris firmware\n");
+ return -ETIME;
+ }
+
+ writel(HOST2XTENSA_INTR_ENABLE, core->reg_base + CPU_CS_H2XSOFTINTEN);
+ writel(0x0, core->reg_base + CPU_CS_X2RPMH);
+
+ return 0;
+}
+
+void iris_vpu_raise_interrupt(struct iris_core *core)
+{
+ writel(1 << CPU_IC_SOFTINT_H2A_SHFT, core->reg_base + CPU_IC_SOFTINT);
+}
+
+void iris_vpu_clear_interrupt(struct iris_core *core)
+{
+ u32 intr_status, mask;
+
+ intr_status = readl(core->reg_base + WRAPPER_INTR_STATUS);
+ mask = (WRAPPER_INTR_STATUS_A2H_BMSK |
+ WRAPPER_INTR_STATUS_A2HWD_BMSK |
+ CTRL_INIT_IDLE_MSG_BMSK);
+
+ if (intr_status & mask)
+ core->intr_status |= intr_status;
+
+ writel(CLEAR_XTENSA2HOST_INTR, core->reg_base + CPU_CS_A2HSOFTINTCLR);
+}
+
+int iris_vpu_watchdog(struct iris_core *core, u32 intr_status)
+{
+ if (intr_status & WRAPPER_INTR_STATUS_A2HWD_BMSK) {
+ dev_err(core->dev, "received watchdog interrupt\n");
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+int iris_vpu_prepare_pc(struct iris_core *core)
+{
+ u32 wfi_status, idle_status, pc_ready;
+ u32 ctrl_status, val = 0;
+ int ret;
+
+ ctrl_status = readl(core->reg_base + CTRL_STATUS);
+ pc_ready = ctrl_status & CTRL_STATUS_PC_READY;
+ idle_status = ctrl_status & BIT(30);
+ if (pc_ready)
+ return 0;
+
+ wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
+ wfi_status &= BIT(0);
+ if (!wfi_status || !idle_status)
+ goto skip_power_off;
+
+ ret = core->hfi_ops->sys_pc_prep(core);
+ if (ret)
+ goto skip_power_off;
+
+ ret = readl_poll_timeout(core->reg_base + CTRL_STATUS, val,
+ val & CTRL_STATUS_PC_READY, 250, 2500);
+ if (ret)
+ goto skip_power_off;
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_TZ_CPU_STATUS,
+ val, val & BIT(0), 250, 2500);
+ if (ret)
+ goto skip_power_off;
+
+ return 0;
+
+skip_power_off:
+ ctrl_status = readl(core->reg_base + CTRL_STATUS);
+ wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
+ wfi_status &= BIT(0);
+ dev_err(core->dev, "skip power collapse, wfi=%#x, idle=%#x, pcr=%#x, ctrl=%#x)\n",
+ wfi_status, idle_status, pc_ready, ctrl_status);
+
+ return -EAGAIN;
+}
+
+int iris_vpu_power_off_controller(struct iris_core *core)
+{
+ u32 val = 0;
+ int ret;
+
+ writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
+
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
+ val, val == 0, 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT,
+ core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+ writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+
+disable_power:
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ return 0;
+}
+
+void iris_vpu_power_off_hw(struct iris_core *core)
+{
+ dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false);
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+}
+
+void iris_vpu_power_off(struct iris_core *core)
+{
+ dev_pm_opp_set_rate(core->dev, 0);
+ core->iris_platform_data->vpu_ops->power_off_hw(core);
+ core->iris_platform_data->vpu_ops->power_off_controller(core);
+ iris_unset_icc_bw(core);
+
+ if (!iris_vpu_watchdog(core, core->intr_status))
+ disable_irq_nosync(core->irq);
+}
+
+static int iris_vpu_power_on_controller(struct iris_core *core)
+{
+ u32 rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
+ int ret;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = reset_control_bulk_reset(rst_tbl_size, core->resets);
+ if (ret)
+ goto err_disable_power;
+
+ ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK);
+ if (ret)
+ goto err_disable_power;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
+ if (ret)
+ goto err_disable_clock;
+
+ return 0;
+
+err_disable_clock:
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+err_disable_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ return ret;
+}
+
+static int iris_vpu_power_on_hw(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_HW_CLK);
+ if (ret)
+ goto err_disable_power;
+
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true);
+ if (ret)
+ goto err_disable_clock;
+
+ return 0;
+
+err_disable_clock:
+ iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+err_disable_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+
+ return ret;
+}
+
+int iris_vpu_power_on(struct iris_core *core)
+{
+ u32 freq;
+ int ret;
+
+ ret = iris_set_icc_bw(core, INT_MAX);
+ if (ret)
+ goto err;
+
+ ret = iris_vpu_power_on_controller(core);
+ if (ret)
+ goto err_unvote_icc;
+
+ ret = iris_vpu_power_on_hw(core);
+ if (ret)
+ goto err_power_off_ctrl;
+
+ freq = core->power.clk_freq ? core->power.clk_freq :
+ (u32)ULONG_MAX;
+
+ dev_pm_opp_set_rate(core->dev, freq);
+
+ core->iris_platform_data->set_preset_registers(core);
+
+ iris_vpu_interrupt_init(core);
+ core->intr_status = 0;
+ enable_irq(core->irq);
+
+ return 0;
+
+err_power_off_ctrl:
+ iris_vpu_power_off_controller(core);
+err_unvote_icc:
+ iris_unset_icc_bw(core);
+err:
+ dev_err(core->dev, "power on failed\n");
+
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h
new file mode 100644
index 000000000000..93b7fa27be3b
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VPU_COMMON_H__
+#define __IRIS_VPU_COMMON_H__
+
+struct iris_core;
+
+extern const struct vpu_ops iris_vpu2_ops;
+extern const struct vpu_ops iris_vpu3_ops;
+extern const struct vpu_ops iris_vpu33_ops;
+
+struct vpu_ops {
+ void (*power_off_hw)(struct iris_core *core);
+ int (*power_off_controller)(struct iris_core *core);
+ u64 (*calc_freq)(struct iris_inst *inst, size_t data_size);
+};
+
+int iris_vpu_boot_firmware(struct iris_core *core);
+void iris_vpu_raise_interrupt(struct iris_core *core);
+void iris_vpu_clear_interrupt(struct iris_core *core);
+int iris_vpu_watchdog(struct iris_core *core, u32 intr_status);
+int iris_vpu_prepare_pc(struct iris_core *core);
+int iris_vpu_power_on(struct iris_core *core);
+int iris_vpu_power_off_controller(struct iris_core *core);
+void iris_vpu_power_off_hw(struct iris_core *core);
+void iris_vpu_power_off(struct iris_core *core);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
new file mode 100644
index 000000000000..fe8a39e5e5a3
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VPU_REGISTER_DEFINES_H__
+#define __IRIS_VPU_REGISTER_DEFINES_H__
+
+#define VCODEC_BASE_OFFS 0x00000000
+#define CPU_BASE_OFFS 0x000A0000
+#define WRAPPER_BASE_OFFS 0x000B0000
+
+#define CPU_CS_BASE_OFFS (CPU_BASE_OFFS)
+
+#define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS + 0x80)
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig
index bc2e410b29cb..ffb731ecd48c 100644
--- a/drivers/media/platform/qcom/venus/Kconfig
+++ b/drivers/media/platform/qcom/venus/Kconfig
@@ -2,7 +2,7 @@ config VIDEO_QCOM_VENUS
tristate "Qualcomm Venus V4L2 encoder/decoder driver"
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV && QCOM_SMEM
- depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+ depends on (ARCH_QCOM && ARM64 && IOMMU_API) || COMPILE_TEST
select OF_DYNAMIC if ARCH_QCOM
select QCOM_MDT_LOADER if ARCH_QCOM
select QCOM_SCM
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index 77d48578ecd2..d305d74bb152 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -438,7 +438,7 @@ static int venus_probe(struct platform_device *pdev)
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret)
- goto err_core_deinit;
+ goto err_hfi_destroy;
platform_set_drvdata(pdev, core);
@@ -476,24 +476,24 @@ static int venus_probe(struct platform_device *pdev)
ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC);
if (ret)
- goto err_venus_shutdown;
+ goto err_core_deinit;
ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC);
if (ret)
- goto err_venus_shutdown;
+ goto err_core_deinit;
ret = pm_runtime_put_sync(dev);
if (ret) {
pm_runtime_get_noresume(dev);
- goto err_dev_unregister;
+ goto err_core_deinit;
}
venus_dbgfs_init(core);
return 0;
-err_dev_unregister:
- v4l2_device_unregister(&core->v4l2_dev);
+err_core_deinit:
+ hfi_core_deinit(core, false);
err_venus_shutdown:
venus_shutdown(core);
err_firmware_deinit:
@@ -506,9 +506,9 @@ err_runtime_disable:
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
+ v4l2_device_unregister(&core->v4l2_dev);
+err_hfi_destroy:
hfi_destroy(core);
-err_core_deinit:
- hfi_core_deinit(core, false);
err_core_put:
if (core->pm_ops->core_put)
core->pm_ops->core_put(core);
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index abeeafa86697..b412e0c5515a 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -172,6 +172,7 @@ struct venus_format {
* @venus_ver: the venus firmware version
* @dump_core: a flag indicating that a core dump is required
* @ocs: OF changeset pointer
+ * @hwmode_dev: a flag indicating that HW_CTRL_TRIGGER is used in clock driver
*/
struct venus_core {
void __iomem *base;
@@ -235,6 +236,7 @@ struct venus_core {
} venus_ver;
unsigned long dump_core;
struct of_changeset *ocs;
+ bool hwmode_dev;
};
struct vdec_controls {
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index 3df241dc3a11..1b3db2caa99f 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -19,6 +19,8 @@ static void init_codecs(struct venus_core *core)
struct hfi_plat_caps *caps = core->caps, *cap;
unsigned long bit;
+ core->codecs_count = 0;
+
if (hweight_long(core->dec_codecs) + hweight_long(core->enc_codecs) > MAX_CODEC_NUM)
return;
@@ -62,7 +64,7 @@ fill_buf_mode(struct hfi_plat_caps *cap, const void *data, unsigned int num)
cap->cap_bufs_mode_dynamic = true;
}
-static void
+static int
parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_buffer_alloc_mode_supported *mode = data;
@@ -70,7 +72,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
u32 *type;
if (num_entries > MAX_ALLOC_MODE_ENTRIES)
- return;
+ return -EINVAL;
type = mode->data;
@@ -82,6 +84,8 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
type++;
}
+
+ return sizeof(*mode);
}
static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
@@ -96,7 +100,7 @@ static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
cap->num_pl += num;
}
-static void
+static int
parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_profile_level_supported *pl = data;
@@ -104,12 +108,14 @@ parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data)
struct hfi_profile_level pl_arr[HFI_MAX_PROFILE_COUNT] = {};
if (pl->profile_count > HFI_MAX_PROFILE_COUNT)
- return;
+ return -EINVAL;
memcpy(pl_arr, proflevel, pl->profile_count * sizeof(*proflevel));
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_profile_level, pl_arr, pl->profile_count);
+
+ return pl->profile_count * sizeof(*proflevel) + sizeof(u32);
}
static void
@@ -124,7 +130,7 @@ fill_caps(struct hfi_plat_caps *cap, const void *data, unsigned int num)
cap->num_caps += num;
}
-static void
+static int
parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_capabilities *caps = data;
@@ -133,12 +139,14 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
struct hfi_capability caps_arr[MAX_CAP_ENTRIES] = {};
if (num_caps > MAX_CAP_ENTRIES)
- return;
+ return -EINVAL;
memcpy(caps_arr, cap, num_caps * sizeof(*cap));
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_caps, caps_arr, num_caps);
+
+ return sizeof(*caps);
}
static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
@@ -153,7 +161,7 @@ static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
cap->num_fmts += num_fmts;
}
-static void
+static int
parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_uncompressed_format_supported *fmt = data;
@@ -162,7 +170,8 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {};
u32 entries = fmt->format_entries;
unsigned int i = 0;
- u32 num_planes;
+ u32 num_planes = 0;
+ u32 size;
while (entries) {
num_planes = pinfo->num_planes;
@@ -172,7 +181,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
i++;
if (i >= MAX_FMT_ENTRIES)
- return;
+ return -EINVAL;
if (pinfo->num_planes > MAX_PLANES)
break;
@@ -184,9 +193,13 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_raw_fmts, rawfmts, i);
+ size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32))
+ + 2 * sizeof(u32);
+
+ return size;
}
-static void parse_codecs(struct venus_core *core, void *data)
+static int parse_codecs(struct venus_core *core, void *data)
{
struct hfi_codec_supported *codecs = data;
@@ -198,21 +211,27 @@ static void parse_codecs(struct venus_core *core, void *data)
core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK;
core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC;
}
+
+ return sizeof(*codecs);
}
-static void parse_max_sessions(struct venus_core *core, const void *data)
+static int parse_max_sessions(struct venus_core *core, const void *data)
{
const struct hfi_max_sessions_supported *sessions = data;
core->max_sessions_supported = sessions->max_sessions;
+
+ return sizeof(*sessions);
}
-static void parse_codecs_mask(u32 *codecs, u32 *domain, void *data)
+static int parse_codecs_mask(u32 *codecs, u32 *domain, void *data)
{
struct hfi_codec_mask_supported *mask = data;
*codecs = mask->codecs;
*domain = mask->video_domains;
+
+ return sizeof(*mask);
}
static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain)
@@ -281,8 +300,9 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst)
u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
u32 size)
{
- unsigned int words_count = size >> 2;
- u32 *word = buf, *data, codecs = 0, domain = 0;
+ u32 *words = buf, *payload, codecs = 0, domain = 0;
+ u32 *frame_size = buf + size;
+ u32 rem_bytes = size;
int ret;
ret = hfi_platform_parser(core, inst);
@@ -299,38 +319,66 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
memset(core->caps, 0, sizeof(core->caps));
}
- while (words_count) {
- data = word + 1;
+ while (words < frame_size) {
+ payload = words + 1;
- switch (*word) {
+ switch (*words) {
case HFI_PROPERTY_PARAM_CODEC_SUPPORTED:
- parse_codecs(core, data);
+ if (rem_bytes <= sizeof(struct hfi_codec_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_codecs(core, payload);
+ if (ret < 0)
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
init_codecs(core);
break;
case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED:
- parse_max_sessions(core, data);
+ if (rem_bytes <= sizeof(struct hfi_max_sessions_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_max_sessions(core, payload);
break;
case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED:
- parse_codecs_mask(&codecs, &domain, data);
+ if (rem_bytes <= sizeof(struct hfi_codec_mask_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_codecs_mask(&codecs, &domain, payload);
break;
case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
- parse_raw_formats(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_uncompressed_format_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_raw_formats(core, codecs, domain, payload);
break;
case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED:
- parse_caps(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_capabilities))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_caps(core, codecs, domain, payload);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED:
- parse_profile_level(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_profile_level_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_profile_level(core, codecs, domain, payload);
break;
case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED:
- parse_alloc_mode(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_buffer_alloc_mode_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_alloc_mode(core, codecs, domain, payload);
break;
default:
+ ret = sizeof(u32);
break;
}
- word++;
- words_count--;
+ if (ret < 0)
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ words += ret / sizeof(u32);
+ rem_bytes -= ret;
}
if (!core->max_sessions_supported)
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index a9167867063c..b5f2ea879950 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -187,6 +187,9 @@ static int venus_write_queue(struct venus_hfi_device *hdev,
/* ensure rd/wr indices's are read from memory */
rmb();
+ if (qsize > IFACEQ_QUEUE_SIZE / 4)
+ return -EINVAL;
+
if (wr_idx >= rd_idx)
empty_space = qsize - (wr_idx - rd_idx);
else
@@ -255,6 +258,9 @@ static int venus_read_queue(struct venus_hfi_device *hdev,
wr_idx = qhdr->write_idx;
qsize = qhdr->q_size;
+ if (qsize > IFACEQ_QUEUE_SIZE / 4)
+ return -EINVAL;
+
/* make sure data is valid before using it */
rmb();
@@ -1035,18 +1041,26 @@ static void venus_sfr_print(struct venus_hfi_device *hdev)
{
struct device *dev = hdev->core->dev;
struct hfi_sfr *sfr = hdev->sfr.kva;
+ u32 size;
void *p;
if (!sfr)
return;
- p = memchr(sfr->data, '\0', sfr->buf_size);
+ size = sfr->buf_size;
+ if (!size)
+ return;
+
+ if (size > ALIGNED_SFR_SIZE)
+ size = ALIGNED_SFR_SIZE;
+
+ p = memchr(sfr->data, '\0', size);
/*
* SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates
* that Venus is in the process of crashing.
*/
if (!p)
- sfr->data[sfr->buf_size - 1] = '\0';
+ sfr->data[size - 1] = '\0';
dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data);
}
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index 33a5a659c0ad..409aa9bd0b5d 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -412,9 +412,17 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable)
u32 val;
int ret;
- if (IS_V6(core))
- return dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable);
- else if (coreid == VIDC_CORE_ID_1) {
+ ret = dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable);
+ if (ret == -EOPNOTSUPP) {
+ core->hwmode_dev = false;
+ goto legacy;
+ }
+
+ core->hwmode_dev = true;
+ return ret;
+
+legacy:
+ if (coreid == VIDC_CORE_ID_1) {
ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL;
stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS;
} else {
@@ -450,7 +458,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask)
vcodec_clks_disable(core, core->vcodec0_clks);
- if (!IS_V6(core)) {
+ if (!core->hwmode_dev) {
ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false);
if (ret)
return ret;
@@ -468,7 +476,7 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask)
vcodec_clks_disable(core, core->vcodec1_clks);
- if (!IS_V6(core)) {
+ if (!core->hwmode_dev) {
ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false);
if (ret)
return ret;
@@ -491,11 +499,9 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask)
if (ret < 0)
return ret;
- if (!IS_V6(core)) {
- ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true);
- if (ret)
- return ret;
- }
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, true);
+ if (ret)
+ return ret;
ret = vcodec_clks_enable(core, core->vcodec0_clks);
if (ret)
@@ -511,11 +517,9 @@ static int poweron_coreid(struct venus_core *core, unsigned int coreid_mask)
if (ret < 0)
return ret;
- if (!IS_V6(core)) {
- ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true);
- if (ret)
- return ret;
- }
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, true);
+ if (ret)
+ return ret;
ret = vcodec_clks_enable(core, core->vcodec1_clks);
if (ret)
@@ -811,7 +815,7 @@ static int vdec_power_v4(struct device *dev, int on)
else
vcodec_clks_disable(core, core->vcodec0_clks);
- vcodec_control_v4(core, VIDC_CORE_ID_1, false);
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false);
return ret;
}
@@ -856,7 +860,7 @@ static int venc_power_v4(struct device *dev, int on)
else
vcodec_clks_disable(core, core->vcodec1_clks);
- vcodec_control_v4(core, VIDC_CORE_ID_2, false);
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false);
return ret;
}
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 9f82882b77bc..99ce5fd41577 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -154,14 +154,14 @@ find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type)
return NULL;
for (i = 0; i < size; i++) {
- bool valid;
+ bool valid = false;
if (fmt[i].type != type)
continue;
if (V4L2_TYPE_IS_OUTPUT(type)) {
valid = venus_helper_check_codec(inst, fmt[i].pixfmt);
- } else if (V4L2_TYPE_IS_CAPTURE(type)) {
+ } else {
valid = venus_helper_check_format(inst, fmt[i].pixfmt);
if (fmt[i].pixfmt == V4L2_PIX_FMT_QC10C &&
@@ -1110,10 +1110,20 @@ static int vdec_start_output(struct venus_inst *inst)
if (inst->codec_state == VENUS_DEC_STATE_SEEK) {
ret = venus_helper_process_initial_out_bufs(inst);
- if (inst->next_buf_last)
+ if (ret)
+ return ret;
+
+ if (inst->next_buf_last) {
inst->codec_state = VENUS_DEC_STATE_DRC;
- else
+ } else {
inst->codec_state = VENUS_DEC_STATE_DECODING;
+
+ if (inst->streamon_cap) {
+ ret = venus_helper_queue_dpb_bufs(inst);
+ if (ret)
+ return ret;
+ }
+ }
goto done;
}
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index 51801a962ed2..4d36c44f9d44 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -662,11 +662,16 @@ int venc_ctrl_init(struct venus_inst *inst)
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
- v4l2_ctrl_ptr_create(&p_hdr10_cll));
+ v4l2_ctrl_ptr_create(&p_hdr10_cll),
+ v4l2_ctrl_ptr_create(NULL),
+ v4l2_ctrl_ptr_create(NULL));
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
- v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering));
+ v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
+ v4l2_ctrl_ptr_create(NULL),
+ v4l2_ctrl_ptr_create(NULL));
+
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
index 12660087b12f..fcadb2143c88 100644
--- a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
@@ -12,7 +12,6 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
-#include <linux/fwnode.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -1102,6 +1101,8 @@ static void cfe_buffer_queue(struct vb2_buffer *vb)
static s64 cfe_get_source_link_freq(struct cfe_device *cfe)
{
+ struct media_pad *src_pad =
+ &cfe->source_sd->entity.pads[cfe->source_pad];
struct v4l2_subdev_state *state;
s64 link_freq;
u32 bpp;
@@ -1136,7 +1137,7 @@ static s64 cfe_get_source_link_freq(struct cfe_device *cfe)
bpp = 0;
}
- link_freq = v4l2_get_link_freq(cfe->source_sd->ctrl_handler, bpp,
+ link_freq = v4l2_get_link_freq(src_pad, bpp,
2 * cfe->csi2.dphy.active_lanes);
if (link_freq < 0)
cfe_err(cfe, "failed to get link freq for subdev '%s'\n",
@@ -1315,8 +1316,6 @@ static void cfe_stop_streaming(struct vb2_queue *vq)
}
static const struct vb2_ops cfe_video_qops = {
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.queue_setup = cfe_queue_setup,
.buf_prepare = cfe_buffer_prepare,
.buf_queue = cfe_buffer_queue,
diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig
index c7fc718a30a5..27a54fa79083 100644
--- a/drivers/media/platform/renesas/Kconfig
+++ b/drivers/media/platform/renesas/Kconfig
@@ -30,23 +30,6 @@ config VIDEO_RCAR_CSI2
To compile this driver as a module, choose M here: the
module will be called rcar-csi2.
-config VIDEO_RCAR_ISP
- tristate "R-Car Image Signal Processor (ISP)"
- depends on V4L_PLATFORM_DRIVERS
- depends on VIDEO_DEV && OF
- depends on ARCH_RENESAS || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select RESET_CONTROLLER
- select V4L2_FWNODE
- help
- Support for Renesas R-Car Image Signal Processor (ISP).
- Enable this to support the Renesas R-Car Image Signal
- Processor (ISP).
-
- To compile this driver as a module, choose M here: the
- module will be called rcar-isp.
-
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on V4L_PLATFORM_DRIVERS
@@ -56,6 +39,7 @@ config VIDEO_SH_VOU
help
Support for the Video Output Unit (VOU) on SuperH SoCs.
+source "drivers/media/platform/renesas/rcar-isp/Kconfig"
source "drivers/media/platform/renesas/rcar-vin/Kconfig"
source "drivers/media/platform/renesas/rzg2l-cru/Kconfig"
diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile
index 50774a20330c..1127259c09d6 100644
--- a/drivers/media/platform/renesas/Makefile
+++ b/drivers/media/platform/renesas/Makefile
@@ -3,13 +3,13 @@
# Makefile for the Renesas capture/playback device drivers.
#
+obj-y += rcar-isp/
obj-y += rcar-vin/
obj-y += rzg2l-cru/
obj-y += vsp1/
obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o
obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o
-obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o
obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o
obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o
diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index 0a53dd47d7bf..9979de4f6ef1 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-csi2.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
@@ -15,6 +16,7 @@
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sys_soc.h>
+#include <linux/units.h>
#include <media/mipi-csi2.h>
#include <media/v4l2-ctrls.h>
@@ -637,6 +639,10 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = {
.datatype = MIPI_CSI2_DT_YUV422_8B,
.bpp = 20,
}, {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 8,
+ }, {
.code = MEDIA_BUS_FMT_Y10_1X10,
.datatype = MIPI_CSI2_DT_RAW10,
.bpp = 10,
@@ -657,9 +663,37 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = {
.datatype = MIPI_CSI2_DT_RAW8,
.bpp = 8,
}, {
- .code = MEDIA_BUS_FMT_Y8_1X8,
- .datatype = MIPI_CSI2_DT_RAW8,
- .bpp = 8,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
},
};
@@ -921,7 +955,7 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
unsigned int lanes)
{
struct v4l2_subdev *source;
- struct v4l2_ctrl *ctrl;
+ s64 freq;
u64 mbps;
if (!priv->remote)
@@ -929,21 +963,17 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
source = priv->remote;
- /* Read the pixel rate control from remote. */
- ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE);
- if (!ctrl) {
- dev_err(priv->dev, "no pixel rate control in subdev %s\n",
- source->name);
- return -EINVAL;
+ freq = v4l2_get_link_freq(source->ctrl_handler, bpp, 2 * lanes);
+ if (freq < 0) {
+ int ret = (int)freq;
+
+ dev_err(priv->dev, "failed to get link freq for %s: %d\n",
+ source->name, ret);
+
+ return ret;
}
- /*
- * Calculate the phypll in mbps.
- * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes)
- * bps = link_freq * 2
- */
- mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp;
- do_div(mbps, lanes * 1000000);
+ mbps = div_u64(freq * 2, MEGA);
/* Adjust for C-PHY, divide by 2.8. */
if (priv->cphy)
@@ -1045,16 +1075,10 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
vcdt2 |= vcdt_part << ((i % 2) * 16);
}
- if (fmt->field == V4L2_FIELD_ALTERNATE) {
+ if (fmt->field == V4L2_FIELD_ALTERNATE)
fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2
| FLD_FLD_EN;
- if (fmt->height == 240)
- fld |= FLD_FLD_NUM(0);
- else
- fld |= FLD_FLD_NUM(1);
- }
-
/*
* Get the number of active data lanes inspecting the remote mbus
* configuration.
@@ -1547,7 +1571,8 @@ static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state)
return ret;
}
- ret = v4l2_subdev_call(priv->remote, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(priv->remote, priv->remote_pad,
+ BIT_ULL(0));
if (ret) {
rcsi2_enter_standby(priv);
return ret;
@@ -1559,31 +1584,50 @@ static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state)
static void rcsi2_stop(struct rcar_csi2 *priv)
{
rcsi2_enter_standby(priv);
- v4l2_subdev_call(priv->remote, video, s_stream, 0);
+ v4l2_subdev_disable_streams(priv->remote, priv->remote_pad, BIT_ULL(0));
}
-static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable)
+static int rcsi2_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 source_pad,
+ u64 source_streams_mask)
{
struct rcar_csi2 *priv = sd_to_csi2(sd);
- struct v4l2_subdev_state *state;
int ret = 0;
+ if (source_streams_mask != 1)
+ return -EINVAL;
+
if (!priv->remote)
return -ENODEV;
- state = v4l2_subdev_lock_and_get_active_state(&priv->subdev);
-
- if (enable && priv->stream_count == 0) {
+ if (priv->stream_count == 0) {
ret = rcsi2_start(priv, state);
if (ret)
- goto out;
- } else if (!enable && priv->stream_count == 1) {
- rcsi2_stop(priv);
+ return ret;
}
- priv->stream_count += enable ? 1 : -1;
-out:
- v4l2_subdev_unlock_state(state);
+ priv->stream_count += 1;
+
+ return ret;
+}
+
+static int rcsi2_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 source_pad, u64 source_streams_mask)
+{
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+ int ret = 0;
+
+ if (source_streams_mask != 1)
+ return -EINVAL;
+
+ if (!priv->remote)
+ return -ENODEV;
+
+ if (priv->stream_count == 1)
+ rcsi2_stop(priv);
+
+ priv->stream_count -= 1;
return ret;
}
@@ -1610,17 +1654,15 @@ static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
return 0;
}
-static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = {
- .s_stream = rcsi2_s_stream,
-};
-
static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = {
+ .enable_streams = rcsi2_enable_streams,
+ .disable_streams = rcsi2_disable_streams,
+
.set_fmt = rcsi2_set_pad_format,
.get_fmt = v4l2_subdev_get_fmt,
};
static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
- .video = &rcar_csi2_video_ops,
.pad = &rcar_csi2_pad_ops,
};
diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig
new file mode 100644
index 000000000000..242f6a23851f
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_RCAR_ISP
+ tristate "R-Car Image Signal Processor (ISP)"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select RESET_CONTROLLER
+ select V4L2_FWNODE
+ help
+ Support for Renesas R-Car Image Signal Processor (ISP).
+ Enable this to support the Renesas R-Car Image Signal
+ Processor (ISP).
+
+ To compile this driver as a module, choose M here: the
+ module will be called rcar-isp.
diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile
new file mode 100644
index 000000000000..b542118c831e
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+rcar-isp-objs = csisp.o
+
+obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
index c515278e3be5..1eb29a0b774a 100644
--- a/drivers/media/platform/renesas/rcar-isp.c
+++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
@@ -76,6 +76,54 @@ static const struct rcar_isp_format rcar_isp_formats[] = {
.code = MEDIA_BUS_FMT_YUYV10_2X10,
.datatype = MIPI_CSI2_DT_YUV422_8B,
.procmode = 0x0c,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
},
};
@@ -111,7 +159,7 @@ enum rcar_isp_pads {
struct rcar_isp {
struct device *dev;
- void __iomem *base;
+ void __iomem *csbase;
struct reset_control *rstc;
enum rcar_isp_input csi_input;
@@ -121,9 +169,8 @@ struct rcar_isp {
struct v4l2_async_notifier notifier;
struct v4l2_subdev *remote;
+ unsigned int remote_pad;
- struct mutex lock; /* Protects mf and stream_count. */
- struct v4l2_mbus_framefmt mf;
int stream_count;
};
@@ -137,14 +184,14 @@ static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n)
return container_of(n, struct rcar_isp, notifier);
}
-static void risp_write(struct rcar_isp *isp, u32 offset, u32 value)
+static void risp_write_cs(struct rcar_isp *isp, u32 offset, u32 value)
{
- iowrite32(value, isp->base + offset);
+ iowrite32(value, isp->csbase + offset);
}
-static u32 risp_read(struct rcar_isp *isp, u32 offset)
+static u32 risp_read_cs(struct rcar_isp *isp, u32 offset)
{
- return ioread32(isp->base + offset);
+ return ioread32(isp->csbase + offset);
}
static int risp_power_on(struct rcar_isp *isp)
@@ -170,14 +217,19 @@ static void risp_power_off(struct rcar_isp *isp)
pm_runtime_put(isp->dev);
}
-static int risp_start(struct rcar_isp *isp)
+static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state)
{
+ const struct v4l2_mbus_framefmt *fmt;
const struct rcar_isp_format *format;
unsigned int vc;
u32 sel_csi = 0;
int ret;
- format = risp_code_to_fmt(isp->mf.code);
+ fmt = v4l2_subdev_state_get_format(state, RCAR_ISP_SINK);
+ if (!fmt)
+ return -EINVAL;
+
+ format = risp_code_to_fmt(fmt->code);
if (!format) {
dev_err(isp->dev, "Unsupported bus format\n");
return -EINVAL;
@@ -193,33 +245,34 @@ static int risp_start(struct rcar_isp *isp)
if (isp->csi_input == RISP_CSI_INPUT1)
sel_csi = ISPINPUTSEL0_SEL_CSI0;
- risp_write(isp, ISPINPUTSEL0_REG,
- risp_read(isp, ISPINPUTSEL0_REG) | sel_csi);
+ risp_write_cs(isp, ISPINPUTSEL0_REG,
+ risp_read_cs(isp, ISPINPUTSEL0_REG) | sel_csi);
/* Configure Channel Selector. */
for (vc = 0; vc < 4; vc++) {
u8 ch = vc + 4;
u8 dt = format->datatype;
- risp_write(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
- risp_write(isp, ISPCS_DT_CODE03_CH_REG(ch),
- ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |
- ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |
- ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |
- ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
+ risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
+ risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch),
+ ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |
+ ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |
+ ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |
+ ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
}
/* Setup processing method. */
- risp_write(isp, ISPPROCMODE_DT_REG(format->datatype),
- ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));
+ risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype),
+ ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));
/* Start ISP. */
- risp_write(isp, ISPSTART_REG, ISPSTART_START);
+ risp_write_cs(isp, ISPSTART_REG, ISPSTART_START);
- ret = v4l2_subdev_call(isp->remote, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad,
+ BIT_ULL(0));
if (ret)
risp_power_off(isp);
@@ -228,95 +281,87 @@ static int risp_start(struct rcar_isp *isp)
static void risp_stop(struct rcar_isp *isp)
{
- v4l2_subdev_call(isp->remote, video, s_stream, 0);
+ v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, BIT_ULL(0));
/* Stop ISP. */
- risp_write(isp, ISPSTART_REG, ISPSTART_STOP);
+ risp_write_cs(isp, ISPSTART_REG, ISPSTART_STOP);
risp_power_off(isp);
}
-static int risp_s_stream(struct v4l2_subdev *sd, int enable)
+static int risp_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 source_pad,
+ u64 source_streams_mask)
{
struct rcar_isp *isp = sd_to_isp(sd);
int ret = 0;
- mutex_lock(&isp->lock);
+ if (source_streams_mask != 1)
+ return -EINVAL;
- if (!isp->remote) {
- ret = -ENODEV;
- goto out;
- }
+ if (!isp->remote)
+ return -ENODEV;
- if (enable && isp->stream_count == 0) {
- ret = risp_start(isp);
+ if (isp->stream_count == 0) {
+ ret = risp_start(isp, state);
if (ret)
- goto out;
- } else if (!enable && isp->stream_count == 1) {
- risp_stop(isp);
+ return ret;
}
- isp->stream_count += enable ? 1 : -1;
-out:
- mutex_unlock(&isp->lock);
+ isp->stream_count += 1;
return ret;
}
-static const struct v4l2_subdev_video_ops risp_video_ops = {
- .s_stream = risp_s_stream,
-};
-
-static int risp_set_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
+static int risp_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 source_pad,
+ u64 source_streams_mask)
{
struct rcar_isp *isp = sd_to_isp(sd);
- struct v4l2_mbus_framefmt *framefmt;
- mutex_lock(&isp->lock);
+ if (source_streams_mask != 1)
+ return -EINVAL;
- if (!risp_code_to_fmt(format->format.code))
- format->format.code = rcar_isp_formats[0].code;
+ if (!isp->remote)
+ return -ENODEV;
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- isp->mf = format->format;
- } else {
- framefmt = v4l2_subdev_state_get_format(sd_state, 0);
- *framefmt = format->format;
- }
+ if (isp->stream_count == 1)
+ risp_stop(isp);
- mutex_unlock(&isp->lock);
+ isp->stream_count -= 1;
return 0;
}
-static int risp_get_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
+static int risp_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- struct rcar_isp *isp = sd_to_isp(sd);
+ struct v4l2_mbus_framefmt *framefmt;
- mutex_lock(&isp->lock);
+ if (format->pad > RCAR_ISP_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- format->format = isp->mf;
- else
- format->format = *v4l2_subdev_state_get_format(sd_state, 0);
+ if (!risp_code_to_fmt(format->format.code))
+ format->format.code = rcar_isp_formats[0].code;
- mutex_unlock(&isp->lock);
+ for (unsigned int i = 0; i < RCAR_ISP_NUM_PADS; i++) {
+ framefmt = v4l2_subdev_state_get_format(state, i);
+ *framefmt = format->format;
+ }
return 0;
}
static const struct v4l2_subdev_pad_ops risp_pad_ops = {
+ .enable_streams = risp_enable_streams,
+ .disable_streams = risp_disable_streams,
.set_fmt = risp_set_pad_format,
- .get_fmt = risp_get_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
.link_validate = v4l2_subdev_link_validate_default,
};
static const struct v4l2_subdev_ops rcar_isp_subdev_ops = {
- .video = &risp_video_ops,
.pad = &risp_pad_ops,
};
@@ -339,6 +384,7 @@ static int risp_notify_bound(struct v4l2_async_notifier *notifier,
}
isp->remote = subdev;
+ isp->remote_pad = pad;
dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad);
@@ -419,9 +465,20 @@ static const struct media_entity_operations risp_entity_ops = {
static int risp_probe_resources(struct rcar_isp *isp,
struct platform_device *pdev)
{
- isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
- if (IS_ERR(isp->base))
- return PTR_ERR(isp->base);
+ struct resource *res;
+
+ /*
+ * For backward compatibility allow cs base to be the only reg if no
+ * reg-names are set in DT.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs");
+ if (!res)
+ isp->csbase = devm_platform_ioremap_resource(pdev, 0);
+ else
+ isp->csbase = devm_ioremap_resource(&pdev->dev, res);
+
+ if (IS_ERR(isp->csbase))
+ return PTR_ERR(isp->csbase);
isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
@@ -449,12 +506,10 @@ static int risp_probe(struct platform_device *pdev)
isp->dev = &pdev->dev;
- mutex_init(&isp->lock);
-
ret = risp_probe_resources(isp, pdev);
if (ret) {
dev_err(isp->dev, "Failed to get resources\n");
- goto error_mutex;
+ return ret;
}
platform_set_drvdata(pdev, isp);
@@ -485,20 +540,25 @@ static int risp_probe(struct platform_device *pdev)
if (ret)
goto error_notifier;
+ ret = v4l2_subdev_init_finalize(&isp->subdev);
+ if (ret)
+ goto error_notifier;
+
ret = v4l2_async_register_subdev(&isp->subdev);
if (ret < 0)
- goto error_notifier;
+ goto error_subdev;
dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
return 0;
+
+error_subdev:
+ v4l2_subdev_cleanup(&isp->subdev);
error_notifier:
v4l2_async_nf_unregister(&isp->notifier);
v4l2_async_nf_cleanup(&isp->notifier);
error_pm:
pm_runtime_disable(&pdev->dev);
-error_mutex:
- mutex_destroy(&isp->lock);
return ret;
}
@@ -511,10 +571,9 @@ static void risp_remove(struct platform_device *pdev)
v4l2_async_nf_cleanup(&isp->notifier);
v4l2_async_unregister_subdev(&isp->subdev);
+ v4l2_subdev_cleanup(&isp->subdev);
pm_runtime_disable(&pdev->dev);
-
- mutex_destroy(&isp->lock);
}
static struct platform_driver rcar_isp_driver = {
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
index ddfb18e6e7a4..846ae7989b1d 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
@@ -1080,13 +1080,11 @@ static int __maybe_unused rvin_suspend(struct device *dev)
{
struct rvin_dev *vin = dev_get_drvdata(dev);
- if (vin->state != RUNNING)
+ if (!vin->running)
return 0;
rvin_stop_streaming(vin);
- vin->state = SUSPENDED;
-
return 0;
}
@@ -1094,7 +1092,7 @@ static int __maybe_unused rvin_resume(struct device *dev)
{
struct rvin_dev *vin = dev_get_drvdata(dev);
- if (vin->state != SUSPENDED)
+ if (!vin->running)
return 0;
/*
@@ -1275,7 +1273,7 @@ static const struct rvin_info rcar_info_r8a77995 = {
};
static const struct rvin_info rcar_info_gen4 = {
- .model = RCAR_GEN3,
+ .model = RCAR_GEN4,
.use_mc = true,
.use_isp = true,
.nv12 = true,
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index 8773998101ff..5c08ee2c9807 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -94,6 +94,7 @@
#define VNMC_INF_YUV16 (5 << 16)
#define VNMC_INF_RGB888 (6 << 16)
#define VNMC_INF_RGB666 (7 << 16)
+#define VNMC_EXINF_RAW8 (1 << 12) /* Gen4 specific */
#define VNMC_VUP (1 << 10)
#define VNMC_IM_ODD (0 << 3)
#define VNMC_IM_ODD_EVEN (1 << 3)
@@ -642,8 +643,6 @@ void rvin_scaler_gen3(struct rvin_dev *vin)
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
clip_size |= vin->compose.height / 2;
break;
default:
@@ -679,22 +678,6 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
fmt = rvin_format_from_pixel(vin, vin->format.pixelformat);
stride = vin->format.bytesperline / fmt->bpp;
-
- /* For RAW8 format bpp is 1, but the hardware process RAW8
- * format in 2 pixel unit hence configure VNIS_REG as stride / 2.
- */
- switch (vin->format.pixelformat) {
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- case V4L2_PIX_FMT_GREY:
- stride /= 2;
- break;
- default:
- break;
- }
-
rvin_write(vin, stride, VNIS_REG);
}
@@ -727,8 +710,6 @@ static int rvin_setup(struct rvin_dev *vin)
case V4L2_FIELD_INTERLACED_BT:
vnmc = VNMC_IM_FULL | VNMC_FOC;
break;
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
case V4L2_FIELD_NONE:
case V4L2_FIELD_ALTERNATE:
vnmc = VNMC_IM_ODD_EVEN;
@@ -791,6 +772,8 @@ static int rvin_setup(struct rvin_dev *vin)
case MEDIA_BUS_FMT_SRGGB8_1X8:
case MEDIA_BUS_FMT_Y8_1X8:
vnmc |= VNMC_INF_RAW8;
+ if (vin->info->model == RCAR_GEN4)
+ vnmc |= VNMC_EXINF_RAW8;
break;
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SGBRG10_1X10:
@@ -802,31 +785,8 @@ static int rvin_setup(struct rvin_dev *vin)
break;
}
- /* Make sure input interface and input format is valid. */
- if (vin->info->model == RCAR_GEN3) {
- switch (vnmc & VNMC_INF_MASK) {
- case VNMC_INF_YUV8_BT656:
- case VNMC_INF_YUV10_BT656:
- case VNMC_INF_YUV16:
- case VNMC_INF_RGB666:
- if (vin->is_csi) {
- vin_err(vin, "Invalid setting in MIPI CSI2\n");
- return -EINVAL;
- }
- break;
- case VNMC_INF_RAW8:
- if (!vin->is_csi) {
- vin_err(vin, "Invalid setting in Digital Pins\n");
- return -EINVAL;
- }
- break;
- default:
- break;
- }
- }
-
/* Enable VSYNC Field Toggle mode after one VSYNC input */
- if (vin->info->model == RCAR_GEN3)
+ if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4)
dmr2 = VNDMR2_FTEV;
else
dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
@@ -910,7 +870,7 @@ static int rvin_setup(struct rvin_dev *vin)
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SRGGB10:
- dmr = VNDMR_RMODE_RAW10 | VNDMR_YC_THR;
+ dmr = VNDMR_RMODE_RAW10;
break;
default:
vin_err(vin, "Invalid pixelformat (0x%x)\n",
@@ -926,7 +886,7 @@ static int rvin_setup(struct rvin_dev *vin)
if (input_is_yuv == output_is_yuv)
vnmc |= VNMC_BPS;
- if (vin->info->model == RCAR_GEN3) {
+ if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) {
/* Select between CSI-2 and parallel input */
if (vin->is_csi)
vnmc &= ~VNMC_DPINE;
@@ -1021,33 +981,13 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
struct rvin_buffer *buf;
struct vb2_v4l2_buffer *vbuf;
dma_addr_t phys_addr;
- int prev;
/* A already populated slot shall never be overwritten. */
if (WARN_ON(vin->buf_hw[slot].buffer))
return;
- prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1;
-
- if (vin->buf_hw[prev].type == HALF_TOP) {
- vbuf = vin->buf_hw[prev].buffer;
- vin->buf_hw[slot].buffer = vbuf;
- vin->buf_hw[slot].type = HALF_BOTTOM;
- switch (vin->format.pixelformat) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV16:
- phys_addr = vin->buf_hw[prev].phys +
- vin->format.sizeimage / 4;
- break;
- default:
- phys_addr = vin->buf_hw[prev].phys +
- vin->format.sizeimage / 2;
- break;
- }
- } else if ((vin->state != STOPPED && vin->state != RUNNING) ||
- list_empty(&vin->buf_list)) {
+ if (list_empty(&vin->buf_list)) {
vin->buf_hw[slot].buffer = NULL;
- vin->buf_hw[slot].type = FULL;
phys_addr = vin->scratch_phys;
} else {
/* Keep track of buffer we give to HW */
@@ -1056,16 +996,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
list_del_init(to_buf_list(vbuf));
vin->buf_hw[slot].buffer = vbuf;
- vin->buf_hw[slot].type =
- V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ?
- HALF_TOP : FULL;
-
/* Setup DMA */
phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
}
- vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n",
- slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer);
+ vin_dbg(vin, "Filling HW slot: %d buffer: %p\n",
+ slot, vin->buf_hw[slot].buffer);
vin->buf_hw[slot].phys = phys_addr;
rvin_set_slot_addr(vin, slot, phys_addr);
@@ -1073,15 +1009,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
static int rvin_capture_start(struct rvin_dev *vin)
{
- int slot, ret;
+ int ret;
- for (slot = 0; slot < HW_BUFFER_NUM; slot++) {
+ for (unsigned int slot = 0; slot < HW_BUFFER_NUM; slot++) {
vin->buf_hw[slot].buffer = NULL;
- vin->buf_hw[slot].type = FULL;
- }
-
- for (slot = 0; slot < HW_BUFFER_NUM; slot++)
rvin_fill_hw_slot(vin, slot);
+ }
ret = rvin_setup(vin);
if (ret)
@@ -1094,8 +1027,6 @@ static int rvin_capture_start(struct rvin_dev *vin)
/* Continuous Frame Capture Mode */
rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
- vin->state = STARTING;
-
return 0;
}
@@ -1136,9 +1067,9 @@ static irqreturn_t rvin_irq(int irq, void *data)
if (!(int_status & VNINTS_FIS))
goto done;
- /* Nothing to do if capture status is 'STOPPED' */
- if (vin->state == STOPPED) {
- vin_dbg(vin, "IRQ while state stopped\n");
+ /* Nothing to do if not running. */
+ if (!vin->running) {
+ vin_dbg(vin, "IRQ while not running, ignoring\n");
goto done;
}
@@ -1150,28 +1081,17 @@ static irqreturn_t rvin_irq(int irq, void *data)
* To hand buffers back in a known order to userspace start
* to capture first from slot 0.
*/
- if (vin->state == STARTING) {
+ if (!vin->sequence) {
if (slot != 0) {
vin_dbg(vin, "Starting sync slot: %d\n", slot);
goto done;
}
vin_dbg(vin, "Capture start synced!\n");
- vin->state = RUNNING;
}
/* Capture frame */
if (vin->buf_hw[slot].buffer) {
- /*
- * Nothing to do but refill the hardware slot if
- * capture only filled first half of vb2 buffer.
- */
- if (vin->buf_hw[slot].type == HALF_TOP) {
- vin->buf_hw[slot].buffer = NULL;
- rvin_fill_hw_slot(vin, slot);
- goto done;
- }
-
vin->buf_hw[slot].buffer->field =
rvin_get_active_field(vin, vnms);
vin->buf_hw[slot].buffer->sequence = vin->sequence;
@@ -1322,8 +1242,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
/* Supported natively */
break;
case V4L2_FIELD_ALTERNATE:
@@ -1336,8 +1254,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
/* Use VIN hardware to combine the two fields */
fmt.format.height *= 2;
break;
@@ -1351,7 +1267,7 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
if (rvin_scaler_needed(vin)) {
/* Gen3 can't scale NV12 */
- if (vin->info->model == RCAR_GEN3 &&
+ if ((vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) &&
vin->format.pixelformat == V4L2_PIX_FMT_NV12)
return -EPIPE;
@@ -1397,7 +1313,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
if (!on) {
video_device_pipeline_stop(&vin->vdev);
- return v4l2_subdev_call(sd, video, s_stream, 0);
+ return v4l2_subdev_disable_streams(sd, pad->index, BIT_ULL(0));
}
ret = rvin_mc_validate_format(vin, sd, pad);
@@ -1408,7 +1324,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
if (ret)
return ret;
- ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(sd, pad->index, BIT_ULL(0));
if (ret == -ENOIOCTLCMD)
ret = 0;
if (ret)
@@ -1434,6 +1350,8 @@ int rvin_start_streaming(struct rvin_dev *vin)
if (ret)
rvin_set_stream(vin, 0);
+ vin->running = true;
+
spin_unlock_irqrestore(&vin->qlock, flags);
return ret;
@@ -1466,44 +1384,21 @@ err_scratch:
void rvin_stop_streaming(struct rvin_dev *vin)
{
- unsigned int i, retries;
unsigned long flags;
- bool buffersFreed;
spin_lock_irqsave(&vin->qlock, flags);
- if (vin->state == STOPPED) {
+ if (!vin->running) {
spin_unlock_irqrestore(&vin->qlock, flags);
return;
}
- vin->state = STOPPING;
-
- /* Wait until only scratch buffer is used, max 3 interrupts. */
- retries = 0;
- while (retries++ < RVIN_RETRIES) {
- buffersFreed = true;
- for (i = 0; i < HW_BUFFER_NUM; i++)
- if (vin->buf_hw[i].buffer)
- buffersFreed = false;
-
- if (buffersFreed)
- break;
-
- spin_unlock_irqrestore(&vin->qlock, flags);
- msleep(RVIN_TIMEOUT_MS);
- spin_lock_irqsave(&vin->qlock, flags);
- }
-
/* Wait for streaming to stop */
- retries = 0;
- while (retries++ < RVIN_RETRIES) {
-
+ for (unsigned int i = 0; i < RVIN_RETRIES; i++) {
rvin_capture_stop(vin);
/* Check if HW is stopped */
if (!rvin_capture_active(vin)) {
- vin->state = STOPPED;
break;
}
@@ -1512,32 +1407,25 @@ void rvin_stop_streaming(struct rvin_dev *vin)
spin_lock_irqsave(&vin->qlock, flags);
}
- if (!buffersFreed || vin->state != STOPPED) {
- /*
- * If this happens something have gone horribly wrong.
- * Set state to stopped to prevent the interrupt handler
- * to make things worse...
- */
- vin_err(vin, "Failed stop HW, something is seriously broken\n");
- vin->state = STOPPED;
- }
+ if (rvin_capture_active(vin))
+ vin_err(vin, "Hardware did not stop\n");
- spin_unlock_irqrestore(&vin->qlock, flags);
+ vin->running = false;
- /* If something went wrong, free buffers with an error. */
- if (!buffersFreed) {
- return_unused_buffers(vin, VB2_BUF_STATE_ERROR);
- for (i = 0; i < HW_BUFFER_NUM; i++) {
- if (vin->buf_hw[i].buffer)
- vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
- }
+ spin_unlock_irqrestore(&vin->qlock, flags);
rvin_set_stream(vin, 0);
/* disable interrupts */
rvin_disable_interrupts(vin);
+
+ /* Return unprocessed buffers from hardware. */
+ for (unsigned int i = 0; i < HW_BUFFER_NUM; i++) {
+ if (vin->buf_hw[i].buffer)
+ vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+
}
static void rvin_stop_streaming_vq(struct vb2_queue *vq)
@@ -1583,8 +1471,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq)
spin_lock_init(&vin->qlock);
- vin->state = STOPPED;
-
for (i = 0; i < HW_BUFFER_NUM; i++)
vin->buf_hw[i].buffer = NULL;
@@ -1687,7 +1573,7 @@ void rvin_set_alpha(struct rvin_dev *vin, unsigned int alpha)
vin->alpha = alpha;
- if (vin->state == STOPPED)
+ if (!vin->running)
goto out;
switch (vin->format.pixelformat) {
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
index 756fdfdbce61..db091af57c19 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
@@ -88,19 +88,19 @@ static const struct rvin_video_format rvin_formats[] = {
},
{
.fourcc = V4L2_PIX_FMT_SBGGR10,
- .bpp = 4,
+ .bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_SGBRG10,
- .bpp = 4,
+ .bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_SGRBG10,
- .bpp = 4,
+ .bpp = 2,
},
{
.fourcc = V4L2_PIX_FMT_SRGGB10,
- .bpp = 4,
+ .bpp = 2,
},
};
@@ -161,9 +161,6 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin,
break;
}
- if (V4L2_FIELD_IS_SEQUENTIAL(pix->field))
- align = 0x80;
-
return ALIGN(pix->width, align) * fmt->bpp;
}
@@ -194,8 +191,6 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
case V4L2_FIELD_ALTERNATE:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
break;
default:
pix->field = RVIN_DEFAULT_FIELD;
@@ -504,8 +499,6 @@ static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect)
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
rect->height *= 2;
break;
}
@@ -591,8 +584,8 @@ static int rvin_s_selection(struct file *file, void *fh,
vin->crop = s->r = r;
- vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n",
- r.width, r.height, r.left, r.top,
+ vin_dbg(vin, "Cropped (%d,%d)/%ux%u of %dx%d\n",
+ r.left, r.top, r.width, r.height,
max_rect.width, max_rect.height);
break;
case V4L2_SEL_TGT_COMPOSE:
@@ -616,8 +609,8 @@ static int rvin_s_selection(struct file *file, void *fh,
vin->compose = s->r = r;
- vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n",
- r.width, r.height, r.left, r.top,
+ vin_dbg(vin, "Compose (%d,%d)/%ux%u in %dx%d\n",
+ r.left, r.top, r.width, r.height,
vin->format.width, vin->format.height);
break;
default:
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
index f87d4bc9e53e..83d1b2734c41 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
@@ -39,6 +39,7 @@ enum model_id {
RCAR_M1,
RCAR_GEN2,
RCAR_GEN3,
+ RCAR_GEN4,
};
enum rvin_csi_id {
@@ -62,39 +63,6 @@ enum rvin_isp_id {
(unsigned int)RVIN_CSI_MAX : (unsigned int)RVIN_ISP_MAX)
/**
- * enum rvin_dma_state - DMA states
- * @STOPPED: No operation in progress
- * @STARTING: Capture starting up
- * @RUNNING: Operation in progress have buffers
- * @STOPPING: Stopping operation
- * @SUSPENDED: Capture is suspended
- */
-enum rvin_dma_state {
- STOPPED = 0,
- STARTING,
- RUNNING,
- STOPPING,
- SUSPENDED,
-};
-
-/**
- * enum rvin_buffer_type
- *
- * Describes how a buffer is given to the hardware. To be able
- * to capture SEQ_TB/BT it's needed to capture to the same vb2
- * buffer twice so the type of buffer needs to be kept.
- *
- * @FULL: One capture fills the whole vb2 buffer
- * @HALF_TOP: One capture fills the top half of the vb2 buffer
- * @HALF_BOTTOM: One capture fills the bottom half of the vb2 buffer
- */
-enum rvin_buffer_type {
- FULL,
- HALF_TOP,
- HALF_BOTTOM,
-};
-
-/**
* struct rvin_video_format - Data format stored in memory
* @fourcc: Pixelformat
* @bpp: Bytes per pixel
@@ -194,11 +162,11 @@ struct rvin_info {
* @scratch: cpu address for scratch buffer
* @scratch_phys: physical address of the scratch buffer
*
- * @qlock: protects @buf_hw, @buf_list, @sequence and @state
+ * @qlock: Protects @buf_hw, @buf_list, @sequence and @running
* @buf_hw: Keeps track of buffers given to HW slot
* @buf_list: list of queued buffers
* @sequence: V4L2 buffers sequence number
- * @state: keeps track of operation state
+ * @running: Keeps track of if the VIN is running
*
* @is_csi: flag to mark the VIN as using a CSI-2 subdevice
* @chsel: Cached value of the current CSI-2 channel selection
@@ -237,12 +205,11 @@ struct rvin_dev {
spinlock_t qlock;
struct {
struct vb2_v4l2_buffer *buffer;
- enum rvin_buffer_type type;
dma_addr_t phys;
} buf_hw[HW_BUFFER_NUM];
struct list_head buf_list;
unsigned int sequence;
- enum rvin_dma_state state;
+ bool running;
bool is_csi;
unsigned int chsel;
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
index 89be584a4988..5fa73ab2db53 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
@@ -22,6 +22,7 @@
#include <media/v4l2-mc.h>
#include "rzg2l-cru.h"
+#include "rzg2l-cru-regs.h"
static inline struct rzg2l_cru_dev *notifier_to_cru(struct v4l2_async_notifier *n)
{
@@ -240,10 +241,11 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru)
static int rzg2l_cru_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct rzg2l_cru_dev *cru;
int irq, ret;
- cru = devm_kzalloc(&pdev->dev, sizeof(*cru), GFP_KERNEL);
+ cru = devm_kzalloc(dev, sizeof(*cru), GFP_KERNEL);
if (!cru)
return -ENOMEM;
@@ -251,32 +253,32 @@ static int rzg2l_cru_probe(struct platform_device *pdev)
if (IS_ERR(cru->base))
return PTR_ERR(cru->base);
- cru->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn");
+ cru->presetn = devm_reset_control_get_shared(dev, "presetn");
if (IS_ERR(cru->presetn))
- return dev_err_probe(&pdev->dev, PTR_ERR(cru->presetn),
+ return dev_err_probe(dev, PTR_ERR(cru->presetn),
"Failed to get cpg presetn\n");
- cru->aresetn = devm_reset_control_get_exclusive(&pdev->dev, "aresetn");
+ cru->aresetn = devm_reset_control_get_exclusive(dev, "aresetn");
if (IS_ERR(cru->aresetn))
- return dev_err_probe(&pdev->dev, PTR_ERR(cru->aresetn),
+ return dev_err_probe(dev, PTR_ERR(cru->aresetn),
"Failed to get cpg aresetn\n");
- cru->vclk = devm_clk_get(&pdev->dev, "video");
+ cru->vclk = devm_clk_get(dev, "video");
if (IS_ERR(cru->vclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(cru->vclk),
+ return dev_err_probe(dev, PTR_ERR(cru->vclk),
"Failed to get video clock\n");
- cru->dev = &pdev->dev;
- cru->info = of_device_get_match_data(&pdev->dev);
+ cru->dev = dev;
+ cru->info = of_device_get_match_data(dev);
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
- ret = devm_request_irq(&pdev->dev, irq, rzg2l_cru_irq, 0,
+ ret = devm_request_irq(dev, irq, cru->info->irq_handler, 0,
KBUILD_MODNAME, cru);
if (ret)
- return dev_err_probe(&pdev->dev, ret, "failed to request irq\n");
+ return dev_err_probe(dev, ret, "failed to request irq\n");
platform_set_drvdata(pdev, cru);
@@ -285,8 +287,10 @@ static int rzg2l_cru_probe(struct platform_device *pdev)
return ret;
cru->num_buf = RZG2L_CRU_HW_BUFFER_DEFAULT;
- pm_suspend_ignore_children(&pdev->dev, true);
- pm_runtime_enable(&pdev->dev);
+ pm_suspend_ignore_children(dev, true);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ goto error_dma_unregister;
ret = rzg2l_cru_media_init(cru);
if (ret)
@@ -296,7 +300,6 @@ static int rzg2l_cru_probe(struct platform_device *pdev)
error_dma_unregister:
rzg2l_cru_dma_unregister(cru);
- pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -305,8 +308,6 @@ static void rzg2l_cru_remove(struct platform_device *pdev)
{
struct rzg2l_cru_dev *cru = platform_get_drvdata(pdev);
- pm_runtime_disable(&pdev->dev);
-
v4l2_async_nf_unregister(&cru->notifier);
v4l2_async_nf_cleanup(&cru->notifier);
@@ -317,8 +318,112 @@ static void rzg2l_cru_remove(struct platform_device *pdev)
rzg2l_cru_dma_unregister(cru);
}
+static const u16 rzg3e_cru_regs[] = {
+ [CRUnCTRL] = 0x0,
+ [CRUnIE] = 0x4,
+ [CRUnIE2] = 0x8,
+ [CRUnINTS] = 0xc,
+ [CRUnINTS2] = 0x10,
+ [CRUnRST] = 0x18,
+ [AMnMB1ADDRL] = 0x40,
+ [AMnMB1ADDRH] = 0x44,
+ [AMnMB2ADDRL] = 0x48,
+ [AMnMB2ADDRH] = 0x4c,
+ [AMnMB3ADDRL] = 0x50,
+ [AMnMB3ADDRH] = 0x54,
+ [AMnMB4ADDRL] = 0x58,
+ [AMnMB4ADDRH] = 0x5c,
+ [AMnMB5ADDRL] = 0x60,
+ [AMnMB5ADDRH] = 0x64,
+ [AMnMB6ADDRL] = 0x68,
+ [AMnMB6ADDRH] = 0x6c,
+ [AMnMB7ADDRL] = 0x70,
+ [AMnMB7ADDRH] = 0x74,
+ [AMnMB8ADDRL] = 0x78,
+ [AMnMB8ADDRH] = 0x7c,
+ [AMnMBVALID] = 0x88,
+ [AMnMADRSL] = 0x8c,
+ [AMnMADRSH] = 0x90,
+ [AMnAXIATTR] = 0xec,
+ [AMnFIFOPNTR] = 0xf8,
+ [AMnAXISTP] = 0x110,
+ [AMnAXISTPACK] = 0x114,
+ [AMnIS] = 0x128,
+ [ICnEN] = 0x1f0,
+ [ICnSVCNUM] = 0x1f8,
+ [ICnSVC] = 0x1fc,
+ [ICnIPMC_C0] = 0x200,
+ [ICnMS] = 0x2d8,
+ [ICnDMR] = 0x304,
+};
+
+static const struct rzg2l_cru_info rzg3e_cru_info = {
+ .max_width = 4095,
+ .max_height = 4095,
+ .image_conv = ICnIPMC_C0,
+ .has_stride = true,
+ .regs = rzg3e_cru_regs,
+ .irq_handler = rzg3e_cru_irq,
+ .enable_interrupts = rzg3e_cru_enable_interrupts,
+ .disable_interrupts = rzg3e_cru_disable_interrupts,
+ .fifo_empty = rz3e_fifo_empty,
+ .csi_setup = rzg3e_cru_csi2_setup,
+};
+
+static const u16 rzg2l_cru_regs[] = {
+ [CRUnCTRL] = 0x0,
+ [CRUnIE] = 0x4,
+ [CRUnINTS] = 0x8,
+ [CRUnRST] = 0xc,
+ [AMnMB1ADDRL] = 0x100,
+ [AMnMB1ADDRH] = 0x104,
+ [AMnMB2ADDRL] = 0x108,
+ [AMnMB2ADDRH] = 0x10c,
+ [AMnMB3ADDRL] = 0x110,
+ [AMnMB3ADDRH] = 0x114,
+ [AMnMB4ADDRL] = 0x118,
+ [AMnMB4ADDRH] = 0x11c,
+ [AMnMB5ADDRL] = 0x120,
+ [AMnMB5ADDRH] = 0x124,
+ [AMnMB6ADDRL] = 0x128,
+ [AMnMB6ADDRH] = 0x12c,
+ [AMnMB7ADDRL] = 0x130,
+ [AMnMB7ADDRH] = 0x134,
+ [AMnMB8ADDRL] = 0x138,
+ [AMnMB8ADDRH] = 0x13c,
+ [AMnMBVALID] = 0x148,
+ [AMnMBS] = 0x14c,
+ [AMnAXIATTR] = 0x158,
+ [AMnFIFOPNTR] = 0x168,
+ [AMnAXISTP] = 0x174,
+ [AMnAXISTPACK] = 0x178,
+ [ICnEN] = 0x200,
+ [ICnMC] = 0x208,
+ [ICnMS] = 0x254,
+ [ICnDMR] = 0x26c,
+};
+
+static const struct rzg2l_cru_info rzgl2_cru_info = {
+ .max_width = 2800,
+ .max_height = 4095,
+ .image_conv = ICnMC,
+ .regs = rzg2l_cru_regs,
+ .irq_handler = rzg2l_cru_irq,
+ .enable_interrupts = rzg2l_cru_enable_interrupts,
+ .disable_interrupts = rzg2l_cru_disable_interrupts,
+ .fifo_empty = rzg2l_fifo_empty,
+ .csi_setup = rzg2l_cru_csi2_setup,
+};
+
static const struct of_device_id rzg2l_cru_of_id_table[] = {
- { .compatible = "renesas,rzg2l-cru", },
+ {
+ .compatible = "renesas,r9a09g047-cru",
+ .data = &rzg3e_cru_info,
+ },
+ {
+ .compatible = "renesas,rzg2l-cru",
+ .data = &rzgl2_cru_info,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg2l_cru_of_id_table);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
index 1c9f22118a5d..a5a57369ef0e 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
@@ -10,71 +10,102 @@
/* HW CRU Registers Definition */
-/* CRU Control Register */
-#define CRUnCTRL 0x0
#define CRUnCTRL_VINSEL(x) ((x) << 0)
-/* CRU Interrupt Enable Register */
-#define CRUnIE 0x4
#define CRUnIE_EFE BIT(17)
-/* CRU Interrupt Status Register */
-#define CRUnINTS 0x8
+#define CRUnIE2_FSxE(x) BIT(((x) * 3))
+#define CRUnIE2_FExE(x) BIT(((x) * 3) + 1)
+
#define CRUnINTS_SFS BIT(16)
-/* CRU Reset Register */
-#define CRUnRST 0xc
+#define CRUnINTS2_FSxS(x) BIT(((x) * 3))
+
#define CRUnRST_VRESETN BIT(0)
/* Memory Bank Base Address (Lower) Register for CRU Image Data */
-#define AMnMBxADDRL(x) (0x100 + ((x) * 8))
+#define AMnMBxADDRL(x) (AMnMB1ADDRL + (x) * 2)
/* Memory Bank Base Address (Higher) Register for CRU Image Data */
-#define AMnMBxADDRH(x) (0x104 + ((x) * 8))
+#define AMnMBxADDRH(x) (AMnMB1ADDRH + (x) * 2)
-/* Memory Bank Enable Register for CRU Image Data */
-#define AMnMBVALID 0x148
#define AMnMBVALID_MBVALID(x) GENMASK(x, 0)
-/* Memory Bank Status Register for CRU Image Data */
-#define AMnMBS 0x14c
#define AMnMBS_MBSTS 0x7
-/* AXI Master Transfer Setting Register for CRU Image Data */
-#define AMnAXIATTR 0x158
#define AMnAXIATTR_AXILEN_MASK GENMASK(3, 0)
#define AMnAXIATTR_AXILEN (0xf)
-/* AXI Master FIFO Pointer Register for CRU Image Data */
-#define AMnFIFOPNTR 0x168
#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0)
+#define AMnFIFOPNTR_FIFOWPNTR_B0 AMnFIFOPNTR_FIFOWPNTR
+#define AMnFIFOPNTR_FIFOWPNTR_B1 GENMASK(15, 8)
#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16)
+#define AMnFIFOPNTR_FIFORPNTR_B0 AMnFIFOPNTR_FIFORPNTR_Y
+#define AMnFIFOPNTR_FIFORPNTR_B1 GENMASK(31, 24)
+
+#define AMnIS_IS_MASK GENMASK(14, 7)
+#define AMnIS_IS(x) ((x) << 7)
-/* AXI Master Transfer Stop Register for CRU Image Data */
-#define AMnAXISTP 0x174
#define AMnAXISTP_AXI_STOP BIT(0)
-/* AXI Master Transfer Stop Status Register for CRU Image Data */
-#define AMnAXISTPACK 0x178
#define AMnAXISTPACK_AXI_STOP_ACK BIT(0)
-/* CRU Image Processing Enable Register */
-#define ICnEN 0x200
#define ICnEN_ICEN BIT(0)
-/* CRU Image Processing Main Control Register */
-#define ICnMC 0x208
+#define ICnSVC_SVC0(x) (x)
+#define ICnSVC_SVC1(x) ((x) << 4)
+#define ICnSVC_SVC2(x) ((x) << 8)
+#define ICnSVC_SVC3(x) ((x) << 12)
+
#define ICnMC_CSCTHR BIT(5)
#define ICnMC_INF(x) ((x) << 16)
#define ICnMC_VCSEL(x) ((x) << 22)
#define ICnMC_INF_MASK GENMASK(21, 16)
-/* CRU Module Status Register */
-#define ICnMS 0x254
#define ICnMS_IA BIT(2)
-/* CRU Data Output Mode Register */
-#define ICnDMR 0x26c
#define ICnDMR_YCMODE_UYVY (1 << 4)
+enum rzg2l_cru_common_regs {
+ CRUnCTRL, /* CRU Control */
+ CRUnIE, /* CRU Interrupt Enable */
+ CRUnIE2, /* CRU Interrupt Enable(2) */
+ CRUnINTS, /* CRU Interrupt Status */
+ CRUnINTS2, /* CRU Interrupt Status(2) */
+ CRUnRST, /* CRU Reset */
+ AMnMB1ADDRL, /* Bank 1 Address (Lower) for CRU Image Data */
+ AMnMB1ADDRH, /* Bank 1 Address (Higher) for CRU Image Data */
+ AMnMB2ADDRL, /* Bank 2 Address (Lower) for CRU Image Data */
+ AMnMB2ADDRH, /* Bank 2 Address (Higher) for CRU Image Data */
+ AMnMB3ADDRL, /* Bank 3 Address (Lower) for CRU Image Data */
+ AMnMB3ADDRH, /* Bank 3 Address (Higher) for CRU Image Data */
+ AMnMB4ADDRL, /* Bank 4 Address (Lower) for CRU Image Data */
+ AMnMB4ADDRH, /* Bank 4 Address (Higher) for CRU Image Data */
+ AMnMB5ADDRL, /* Bank 5 Address (Lower) for CRU Image Data */
+ AMnMB5ADDRH, /* Bank 5 Address (Higher) for CRU Image Data */
+ AMnMB6ADDRL, /* Bank 6 Address (Lower) for CRU Image Data */
+ AMnMB6ADDRH, /* Bank 6 Address (Higher) for CRU Image Data */
+ AMnMB7ADDRL, /* Bank 7 Address (Lower) for CRU Image Data */
+ AMnMB7ADDRH, /* Bank 7 Address (Higher) for CRU Image Data */
+ AMnMB8ADDRL, /* Bank 8 Address (Lower) for CRU Image Data */
+ AMnMB8ADDRH, /* Bank 8 Address (Higher) for CRU Image Data */
+ AMnMBVALID, /* Memory Bank Enable for CRU Image Data */
+ AMnMBS, /* Memory Bank Status for CRU Image Data */
+ AMnMADRSL, /* VD Memory Address Lower Status Register */
+ AMnMADRSH, /* VD Memory Address Higher Status Register */
+ AMnAXIATTR, /* AXI Master Transfer Setting Register for CRU Image Data */
+ AMnFIFOPNTR, /* AXI Master FIFO Pointer for CRU Image Data */
+ AMnAXISTP, /* AXI Master Transfer Stop for CRU Image Data */
+ AMnAXISTPACK, /* AXI Master Transfer Stop Status for CRU Image Data */
+ AMnIS, /* Image Stride Setting Register */
+ ICnEN, /* CRU Image Processing Enable */
+ ICnSVCNUM, /* CRU SVC Number Register */
+ ICnSVC, /* CRU VC Select Register */
+ ICnMC, /* CRU Image Processing Main Control */
+ ICnIPMC_C0, /* CRU Image Converter Main Control 0 */
+ ICnMS, /* CRU Module Status */
+ ICnDMR, /* CRU Data Output Mode */
+ RZG2L_CRU_MAX_REG,
+};
+
#endif /* __RZG2L_CRU_REGS_H__ */
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
index 8b898ce05b84..c30f3b281284 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
@@ -27,15 +27,15 @@
#define RZG2L_CRU_CSI2_VCHANNEL 4
#define RZG2L_CRU_MIN_INPUT_WIDTH 320
-#define RZG2L_CRU_MAX_INPUT_WIDTH 2800
#define RZG2L_CRU_MIN_INPUT_HEIGHT 240
-#define RZG2L_CRU_MAX_INPUT_HEIGHT 4095
enum rzg2l_csi2_pads {
RZG2L_CRU_IP_SINK = 0,
RZG2L_CRU_IP_SOURCE,
};
+struct rzg2l_cru_dev;
+
/**
* enum rzg2l_cru_dma_state - DMA states
* @RZG2L_CRU_DMA_STOPPED: No operation in progress
@@ -80,6 +80,21 @@ struct rzg2l_cru_ip_format {
bool yuv;
};
+struct rzg2l_cru_info {
+ unsigned int max_width;
+ unsigned int max_height;
+ u16 image_conv;
+ const u16 *regs;
+ bool has_stride;
+ irqreturn_t (*irq_handler)(int irq, void *data);
+ void (*enable_interrupts)(struct rzg2l_cru_dev *cru);
+ void (*disable_interrupts)(struct rzg2l_cru_dev *cru);
+ bool (*fifo_empty)(struct rzg2l_cru_dev *cru);
+ void (*csi_setup)(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc);
+};
+
/**
* struct rzg2l_cru_dev - Renesas CRU device structure
* @dev: (OF) device
@@ -94,6 +109,8 @@ struct rzg2l_cru_ip_format {
* @vdev: V4L2 video device associated with CRU
* @v4l2_dev: V4L2 device
* @num_buf: Holds the current number of buffers enabled
+ * @svc_channel: SVC0/1/2/3 to use for RZ/G3E
+ * @buf_addr: Memory addresses where current video data is written.
* @notifier: V4L2 asynchronous subdevs notifier
*
* @ip: Image processing subdev info
@@ -130,6 +147,9 @@ struct rzg2l_cru_dev {
struct v4l2_device v4l2_dev;
u8 num_buf;
+ u8 svc_channel;
+ dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT];
+
struct v4l2_async_notifier notifier;
struct rzg2l_cru_ip ip;
@@ -161,6 +181,7 @@ void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru);
int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru);
void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru);
irqreturn_t rzg2l_cru_irq(int irq, void *data);
+irqreturn_t rzg3e_cru_irq(int irq, void *data);
const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format);
@@ -172,4 +193,18 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code);
const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format);
const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index);
+void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru);
+void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru);
+void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru);
+void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru);
+
+bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru);
+bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru);
+void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc);
+void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc);
+
#endif
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
index 881e910dce02..9243306e2aa9 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
@@ -85,6 +85,15 @@
CSIDPHYSKW0_UTIL_DL2_SKW_ADJ(1) | \
CSIDPHYSKW0_UTIL_DL3_SKW_ADJ(1))
+/* DPHY registers on RZ/V2H(P) SoC */
+#define CRUm_S_TIMCTL 0x41c
+#define CRUm_S_TIMCTL_S_HSSETTLECTL(x) ((x) << 8)
+
+#define CRUm_S_DPHYCTL_MSB 0x434
+#define CRUm_S_DPHYCTL_MSB_DESKEW BIT(1)
+
+#define CRUm_SWAPCTL 0x438
+
#define VSRSTS_RETRIES 20
#define RZG2L_CSI2_MIN_WIDTH 320
@@ -107,6 +116,7 @@ struct rzg2l_csi2 {
void __iomem *base;
struct reset_control *presetn;
struct reset_control *cmn_rstb;
+ const struct rzg2l_csi2_info *info;
struct clk *sysclk;
struct clk *vclk;
unsigned long vclk_rate;
@@ -123,6 +133,12 @@ struct rzg2l_csi2 {
bool dphy_enabled;
};
+struct rzg2l_csi2_info {
+ int (*dphy_enable)(struct rzg2l_csi2 *csi2);
+ int (*dphy_disable)(struct rzg2l_csi2 *csi2);
+ bool has_system_clk;
+};
+
struct rzg2l_csi2_timings {
u32 t_init;
u32 tclk_miss;
@@ -133,6 +149,30 @@ struct rzg2l_csi2_timings {
u32 max_hsfreq;
};
+struct rzv2h_csi2_s_hssettlectl {
+ unsigned int hsfreq;
+ u16 s_hssettlectl;
+};
+
+static const struct rzv2h_csi2_s_hssettlectl rzv2h_s_hssettlectl[] = {
+ { 90, 1 }, { 130, 2 }, { 180, 3 },
+ { 220, 4 }, { 270, 5 }, { 310, 6 },
+ { 360, 7 }, { 400, 8 }, { 450, 9 },
+ { 490, 10 }, { 540, 11 }, { 580, 12 },
+ { 630, 13 }, { 670, 14 }, { 720, 15 },
+ { 760, 16 }, { 810, 17 }, { 850, 18 },
+ { 900, 19 }, { 940, 20 }, { 990, 21 },
+ { 1030, 22 }, { 1080, 23 }, { 1120, 24 },
+ { 1170, 25 }, { 1220, 26 }, { 1260, 27 },
+ { 1310, 28 }, { 1350, 29 }, { 1400, 30 },
+ { 1440, 31 }, { 1490, 32 }, { 1530, 33 },
+ { 1580, 34 }, { 1620, 35 }, { 1670, 36 },
+ { 1710, 37 }, { 1760, 38 }, { 1800, 39 },
+ { 1850, 40 }, { 1890, 41 }, { 1940, 42 },
+ { 1980, 43 }, { 2030, 44 }, { 2070, 45 },
+ { 2100, 46 },
+};
+
static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = {
{
.max_hsfreq = 80,
@@ -355,14 +395,20 @@ static int rzg2l_csi2_dphy_enable(struct rzg2l_csi2 *csi2)
return ret;
}
+static const struct rzg2l_csi2_info rzg2l_csi2_info = {
+ .dphy_enable = rzg2l_csi2_dphy_enable,
+ .dphy_disable = rzg2l_csi2_dphy_disable,
+ .has_system_clk = true,
+};
+
static int rzg2l_csi2_dphy_setting(struct v4l2_subdev *sd, bool on)
{
struct rzg2l_csi2 *csi2 = sd_to_csi2(sd);
if (on)
- return rzg2l_csi2_dphy_enable(csi2);
+ return csi2->info->dphy_enable(csi2);
- return rzg2l_csi2_dphy_disable(csi2);
+ return csi2->info->dphy_disable(csi2);
}
static int rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2)
@@ -421,6 +467,64 @@ static int rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2)
return 0;
}
+static int rzv2h_csi2_dphy_disable(struct rzg2l_csi2 *csi2)
+{
+ int ret;
+
+ /* Reset the CRU (D-PHY) */
+ ret = reset_control_assert(csi2->cmn_rstb);
+ if (ret)
+ return ret;
+
+ csi2->dphy_enabled = false;
+
+ return 0;
+}
+
+static int rzv2h_csi2_dphy_enable(struct rzg2l_csi2 *csi2)
+{
+ unsigned int i;
+ u16 hssettle;
+ int mbps;
+
+ mbps = rzg2l_csi2_calc_mbps(csi2);
+ if (mbps < 0)
+ return mbps;
+
+ csi2->hsfreq = mbps;
+
+ for (i = 0; i < ARRAY_SIZE(rzv2h_s_hssettlectl); i++) {
+ if (csi2->hsfreq <= rzv2h_s_hssettlectl[i].hsfreq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(rzv2h_s_hssettlectl))
+ return -EINVAL;
+
+ rzg2l_csi2_write(csi2, CRUm_SWAPCTL, 0);
+
+ hssettle = rzv2h_s_hssettlectl[i].s_hssettlectl;
+ rzg2l_csi2_write(csi2, CRUm_S_TIMCTL,
+ CRUm_S_TIMCTL_S_HSSETTLECTL(hssettle));
+
+ if (csi2->hsfreq > 1500)
+ rzg2l_csi2_set(csi2, CRUm_S_DPHYCTL_MSB,
+ CRUm_S_DPHYCTL_MSB_DESKEW);
+ else
+ rzg2l_csi2_clr(csi2, CRUm_S_DPHYCTL_MSB,
+ CRUm_S_DPHYCTL_MSB_DESKEW);
+
+ csi2->dphy_enabled = true;
+
+ return 0;
+}
+
+static const struct rzg2l_csi2_info rzv2h_csi2_info = {
+ .dphy_enable = rzv2h_csi2_dphy_enable,
+ .dphy_disable = rzv2h_csi2_dphy_disable,
+ .has_system_clk = false,
+};
+
static int rzg2l_csi2_mipi_link_setting(struct v4l2_subdev *sd, bool on)
{
struct rzg2l_csi2 *csi2 = sd_to_csi2(sd);
@@ -764,39 +868,46 @@ static const struct media_entity_operations rzg2l_csi2_entity_ops = {
static int rzg2l_csi2_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct rzg2l_csi2 *csi2;
int ret;
- csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
+ csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL);
if (!csi2)
return -ENOMEM;
+ csi2->info = of_device_get_match_data(dev);
+ if (!csi2->info)
+ return dev_err_probe(dev, -EINVAL, "Failed to get OF match data\n");
+
csi2->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(csi2->base))
return PTR_ERR(csi2->base);
- csi2->cmn_rstb = devm_reset_control_get_exclusive(&pdev->dev, "cmn-rstb");
+ csi2->cmn_rstb = devm_reset_control_get_exclusive(dev, "cmn-rstb");
if (IS_ERR(csi2->cmn_rstb))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->cmn_rstb),
+ return dev_err_probe(dev, PTR_ERR(csi2->cmn_rstb),
"Failed to get cpg cmn-rstb\n");
- csi2->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn");
+ csi2->presetn = devm_reset_control_get_shared(dev, "presetn");
if (IS_ERR(csi2->presetn))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->presetn),
+ return dev_err_probe(dev, PTR_ERR(csi2->presetn),
"Failed to get cpg presetn\n");
- csi2->sysclk = devm_clk_get(&pdev->dev, "system");
- if (IS_ERR(csi2->sysclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->sysclk),
- "Failed to get system clk\n");
+ if (csi2->info->has_system_clk) {
+ csi2->sysclk = devm_clk_get(dev, "system");
+ if (IS_ERR(csi2->sysclk))
+ return dev_err_probe(dev, PTR_ERR(csi2->sysclk),
+ "Failed to get system clk\n");
+ }
- csi2->vclk = devm_clk_get(&pdev->dev, "video");
+ csi2->vclk = devm_clk_get(dev, "video");
if (IS_ERR(csi2->vclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->vclk),
+ return dev_err_probe(dev, PTR_ERR(csi2->vclk),
"Failed to get video clock\n");
csi2->vclk_rate = clk_get_rate(csi2->vclk);
- csi2->dev = &pdev->dev;
+ csi2->dev = dev;
platform_set_drvdata(pdev, csi2);
@@ -804,18 +915,20 @@ static int rzg2l_csi2_probe(struct platform_device *pdev)
if (ret)
return ret;
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
ret = rzg2l_validate_csi2_lanes(csi2);
if (ret)
- goto error_pm;
+ return ret;
- csi2->subdev.dev = &pdev->dev;
+ csi2->subdev.dev = dev;
v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops);
csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops;
- v4l2_set_subdevdata(&csi2->subdev, &pdev->dev);
+ v4l2_set_subdevdata(&csi2->subdev, dev);
snprintf(csi2->subdev.name, sizeof(csi2->subdev.name),
- "csi-%s", dev_name(&pdev->dev));
+ "csi-%s", dev_name(dev));
csi2->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
csi2->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
@@ -833,7 +946,7 @@ static int rzg2l_csi2_probe(struct platform_device *pdev)
ret = media_entity_pads_init(&csi2->subdev.entity, ARRAY_SIZE(csi2->pads),
csi2->pads);
if (ret)
- goto error_pm;
+ return ret;
ret = v4l2_subdev_init_finalize(&csi2->subdev);
if (ret < 0)
@@ -851,8 +964,6 @@ error_async:
v4l2_async_nf_unregister(&csi2->notifier);
v4l2_async_nf_cleanup(&csi2->notifier);
media_entity_cleanup(&csi2->subdev.entity);
-error_pm:
- pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -866,7 +977,6 @@ static void rzg2l_csi2_remove(struct platform_device *pdev)
v4l2_async_unregister_subdev(&csi2->subdev);
v4l2_subdev_cleanup(&csi2->subdev);
media_entity_cleanup(&csi2->subdev.entity);
- pm_runtime_disable(&pdev->dev);
}
static int rzg2l_csi2_pm_runtime_suspend(struct device *dev)
@@ -891,7 +1001,14 @@ static const struct dev_pm_ops rzg2l_csi2_pm_ops = {
};
static const struct of_device_id rzg2l_csi2_of_table[] = {
- { .compatible = "renesas,rzg2l-csi2", },
+ {
+ .compatible = "renesas,r9a09g057-csi2",
+ .data = &rzv2h_csi2_info,
+ },
+ {
+ .compatible = "renesas,rzg2l-csi2",
+ .data = &rzg2l_csi2_info,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
index 76a2b451f1da..7836c7cd53dc 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
@@ -148,6 +148,8 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
+ struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd);
+ const struct rzg2l_cru_info *info = cru->info;
struct v4l2_mbus_framefmt *src_format;
struct v4l2_mbus_framefmt *sink_format;
@@ -170,9 +172,9 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
sink_format->ycbcr_enc = fmt->format.ycbcr_enc;
sink_format->quantization = fmt->format.quantization;
sink_format->width = clamp_t(u32, fmt->format.width,
- RZG2L_CRU_MIN_INPUT_WIDTH, RZG2L_CRU_MAX_INPUT_WIDTH);
+ RZG2L_CRU_MIN_INPUT_WIDTH, info->max_width);
sink_format->height = clamp_t(u32, fmt->format.height,
- RZG2L_CRU_MIN_INPUT_HEIGHT, RZG2L_CRU_MAX_INPUT_HEIGHT);
+ RZG2L_CRU_MIN_INPUT_HEIGHT, info->max_height);
fmt->format = *sink_format;
@@ -197,6 +199,9 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_size_enum *fse)
{
+ struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd);
+ const struct rzg2l_cru_info *info = cru->info;
+
if (fse->index != 0)
return -EINVAL;
@@ -205,8 +210,8 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd,
fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH;
fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT;
- fse->max_width = RZG2L_CRU_MAX_INPUT_WIDTH;
- fse->max_height = RZG2L_CRU_MAX_INPUT_HEIGHT;
+ fse->max_width = info->max_width;
+ fse->max_height = info->max_height;
return 0;
}
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index cd69c8a686d3..067c6af14e95 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -31,6 +31,9 @@
#define RZG2L_CRU_DEFAULT_FIELD V4L2_FIELD_NONE
#define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB
+#define RZG2L_CRU_STRIDE_MAX 32640
+#define RZG2L_CRU_STRIDE_ALIGN 128
+
struct rzg2l_cru_buffer {
struct vb2_v4l2_buffer vb;
struct list_head list;
@@ -42,16 +45,66 @@ struct rzg2l_cru_buffer {
/* -----------------------------------------------------------------------------
* DMA operations
*/
-static void rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value)
+static void __rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value)
{
- iowrite32(value, cru->base + offset);
+ const u16 *regs = cru->info->regs;
+
+ /*
+ * CRUnCTRL is a first register on all CRU supported SoCs so validate
+ * rest of the registers have valid offset being set in cru->info->regs.
+ */
+ if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) ||
+ WARN_ON(offset != CRUnCTRL && regs[offset] == 0))
+ return;
+
+ iowrite32(value, cru->base + regs[offset]);
}
-static u32 rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset)
+static u32 __rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset)
{
- return ioread32(cru->base + offset);
+ const u16 *regs = cru->info->regs;
+
+ /*
+ * CRUnCTRL is a first register on all CRU supported SoCs so validate
+ * rest of the registers have valid offset being set in cru->info->regs.
+ */
+ if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) ||
+ WARN_ON(offset != CRUnCTRL && regs[offset] == 0))
+ return 0;
+
+ return ioread32(cru->base + regs[offset]);
}
+static __always_inline void
+__rzg2l_cru_write_constant(struct rzg2l_cru_dev *cru, u32 offset, u32 value)
+{
+ const u16 *regs = cru->info->regs;
+
+ BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG);
+
+ iowrite32(value, cru->base + regs[offset]);
+}
+
+static __always_inline u32
+__rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset)
+{
+ const u16 *regs = cru->info->regs;
+
+ BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG);
+
+ return ioread32(cru->base + regs[offset]);
+}
+
+#define rzg2l_cru_write(cru, offset, value) \
+ (__builtin_constant_p(offset) ? \
+ __rzg2l_cru_write_constant(cru, offset, value) : \
+ __rzg2l_cru_write(cru, offset, value))
+
+#define rzg2l_cru_read(cru, offset) \
+ (__builtin_constant_p(offset) ? \
+ __rzg2l_cru_read_constant(cru, offset) : \
+ __rzg2l_cru_read(cru, offset))
+
/* Need to hold qlock before calling */
static void return_unused_buffers(struct rzg2l_cru_dev *cru,
enum vb2_buffer_state state)
@@ -134,6 +187,8 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru,
/* Currently, we just use the buffer in 32 bits address */
rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr);
rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0);
+
+ cru->buf_addr[slot] = addr;
}
/*
@@ -174,6 +229,7 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot)
static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru)
{
+ const struct rzg2l_cru_info *info = cru->info;
unsigned int slot;
u32 amnaxiattr;
@@ -186,35 +242,64 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru)
for (slot = 0; slot < cru->num_buf; slot++)
rzg2l_cru_fill_hw_slot(cru, slot);
+ if (info->has_stride) {
+ u32 stride = cru->format.bytesperline;
+ u32 amnis;
+
+ stride /= RZG2L_CRU_STRIDE_ALIGN;
+ amnis = rzg2l_cru_read(cru, AMnIS) & ~AMnIS_IS_MASK;
+ rzg2l_cru_write(cru, AMnIS, amnis | AMnIS_IS(stride));
+ }
+
/* Set AXI burst max length to recommended setting */
amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK;
amnaxiattr |= AMnAXIATTR_AXILEN;
rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr);
}
-static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
- const struct rzg2l_cru_ip_format *ip_fmt,
- u8 csi_vc)
+void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc)
{
+ const struct rzg2l_cru_info *info = cru->info;
u32 icnmc = ICnMC_INF(ip_fmt->datatype);
- icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK);
+ icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK;
/* Set virtual channel CSI2 */
icnmc |= ICnMC_VCSEL(csi_vc);
- rzg2l_cru_write(cru, ICnMC, icnmc);
+ rzg2l_cru_write(cru, ICnSVCNUM, csi_vc);
+ rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) |
+ ICnSVC_SVC2(2) | ICnSVC_SVC3(3));
+ rzg2l_cru_write(cru, info->image_conv, icnmc);
+}
+
+void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc)
+{
+ const struct rzg2l_cru_info *info = cru->info;
+ u32 icnmc = ICnMC_INF(ip_fmt->datatype);
+
+ icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK;
+
+ /* Set virtual channel CSI2 */
+ icnmc |= ICnMC_VCSEL(csi_vc);
+
+ rzg2l_cru_write(cru, info->image_conv, icnmc);
}
static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru,
struct v4l2_mbus_framefmt *ip_sd_fmt,
u8 csi_vc)
{
+ const struct rzg2l_cru_info *info = cru->info;
const struct rzg2l_cru_ip_format *cru_video_fmt;
const struct rzg2l_cru_ip_format *cru_ip_fmt;
cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code);
- rzg2l_cru_csi2_setup(cru, cru_ip_fmt, csi_vc);
+ info->csi_setup(cru, cru_ip_fmt, csi_vc);
/* Output format */
cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat);
@@ -226,11 +311,11 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru,
/* If input and output use same colorspace, do bypass mode */
if (cru_ip_fmt->yuv == cru_video_fmt->yuv)
- rzg2l_cru_write(cru, ICnMC,
- rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR);
+ rzg2l_cru_write(cru, info->image_conv,
+ rzg2l_cru_read(cru, info->image_conv) | ICnMC_CSCTHR);
else
- rzg2l_cru_write(cru, ICnMC,
- rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR));
+ rzg2l_cru_write(cru, info->image_conv,
+ rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_CSCTHR);
/* Set output data format */
rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr);
@@ -238,9 +323,36 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru,
return 0;
}
-void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
+bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru)
+{
+ u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
+
+ if ((((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B1) >> 24) ==
+ ((amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B1) >> 8)) &&
+ (((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B0) >> 16) ==
+ (amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B0)))
+ return true;
+
+ return false;
+}
+
+bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru)
{
u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y;
+
+ amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
+
+ amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR;
+ amnfifopntr_r_y =
+ (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16;
+ if (amnfifopntr_w == amnfifopntr_r_y)
+ return true;
+
+ return amnfifopntr_w == amnfifopntr_r_y;
+}
+
+void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
+{
unsigned int retries = 0;
unsigned long flags;
u32 icnms;
@@ -248,8 +360,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
spin_lock_irqsave(&cru->qlock, flags);
/* Disable and clear the interrupt */
- rzg2l_cru_write(cru, CRUnIE, 0);
- rzg2l_cru_write(cru, CRUnINTS, 0x001F0F0F);
+ cru->info->disable_interrupts(cru);
/* Stop the operation of image conversion */
rzg2l_cru_write(cru, ICnEN, 0);
@@ -269,12 +380,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
/* Wait until the FIFO becomes empty */
for (retries = 5; retries > 0; retries--) {
- amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
-
- amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR;
- amnfifopntr_r_y =
- (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16;
- if (amnfifopntr_w == amnfifopntr_r_y)
+ if (cru->info->fifo_empty(cru))
break;
usleep_range(10, 20);
@@ -341,6 +447,31 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru)
return fd.entry[0].bus.csi2.vc;
}
+void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel));
+ rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel));
+}
+
+void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE, 0);
+ rzg2l_cru_write(cru, CRUnIE2, 0);
+ rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS));
+ rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2));
+}
+
+void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE);
+}
+
+void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE, 0);
+ rzg2l_cru_write(cru, CRUnINTS, 0x001f000f);
+}
+
int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
{
struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru);
@@ -352,6 +483,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
if (ret < 0)
return ret;
csi_vc = ret;
+ cru->svc_channel = csi_vc;
spin_lock_irqsave(&cru->qlock, flags);
@@ -362,8 +494,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN);
/* Disable and clear the interrupt before using */
- rzg2l_cru_write(cru, CRUnIE, 0);
- rzg2l_cru_write(cru, CRUnINTS, 0x001f000f);
+ cru->info->disable_interrupts(cru);
/* Initialize the AXI master */
rzg2l_cru_initialize_axi(cru);
@@ -376,7 +507,7 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
}
/* Enable interrupt */
- rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE);
+ cru->info->enable_interrupts(cru);
/* Enable image processing reception */
rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN);
@@ -531,6 +662,104 @@ done:
return IRQ_RETVAL(handled);
}
+static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru)
+{
+ u64 amnmadrs;
+ int slot;
+
+ /*
+ * When AMnMADRSL is read, AMnMADRSH of the higher-order
+ * address also latches the address.
+ *
+ * AMnMADRSH must be read after AMnMADRSL has been read.
+ */
+ amnmadrs = rzg2l_cru_read(cru, AMnMADRSL);
+ amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32;
+
+ /* Ensure amnmadrs is within this buffer range */
+ for (slot = 0; slot < cru->num_buf; slot++) {
+ if (amnmadrs >= cru->buf_addr[slot] &&
+ amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage)
+ return slot;
+ }
+
+ dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs);
+ return -EINVAL;
+}
+
+irqreturn_t rzg3e_cru_irq(int irq, void *data)
+{
+ struct rzg2l_cru_dev *cru = data;
+ u32 irq_status;
+ int slot;
+
+ scoped_guard(spinlock, &cru->qlock) {
+ irq_status = rzg2l_cru_read(cru, CRUnINTS2);
+ if (!irq_status)
+ return IRQ_NONE;
+
+ dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status);
+
+ rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2));
+
+ /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */
+ if (cru->state == RZG2L_CRU_DMA_STOPPED) {
+ dev_dbg(cru->dev, "IRQ while state stopped\n");
+ return IRQ_HANDLED;
+ }
+
+ if (cru->state == RZG2L_CRU_DMA_STOPPING) {
+ if (irq_status & CRUnINTS2_FSxS(0) ||
+ irq_status & CRUnINTS2_FSxS(1) ||
+ irq_status & CRUnINTS2_FSxS(2) ||
+ irq_status & CRUnINTS2_FSxS(3))
+ dev_dbg(cru->dev, "IRQ while state stopping\n");
+ return IRQ_HANDLED;
+ }
+
+ slot = rzg3e_cru_get_current_slot(cru);
+ if (slot < 0)
+ return IRQ_HANDLED;
+
+ dev_dbg(cru->dev, "Current written slot: %d\n", slot);
+ cru->buf_addr[slot] = 0;
+
+ /*
+ * To hand buffers back in a known order to userspace start
+ * to capture first from slot 0.
+ */
+ if (cru->state == RZG2L_CRU_DMA_STARTING) {
+ if (slot != 0) {
+ dev_dbg(cru->dev, "Starting sync slot: %d\n", slot);
+ return IRQ_HANDLED;
+ }
+ dev_dbg(cru->dev, "Capture start synced!\n");
+ cru->state = RZG2L_CRU_DMA_RUNNING;
+ }
+
+ /* Capture frame */
+ if (cru->queue_buf[slot]) {
+ struct vb2_v4l2_buffer *buf = cru->queue_buf[slot];
+
+ buf->field = cru->format.field;
+ buf->sequence = cru->sequence;
+ buf->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE);
+ cru->queue_buf[slot] = NULL;
+ } else {
+ /* Scratch buffer was used, dropping frame. */
+ dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence);
+ }
+
+ cru->sequence++;
+
+ /* Prepare for next frame */
+ rzg2l_cru_fill_hw_slot(cru, slot);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count)
{
struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq);
@@ -686,6 +915,7 @@ error:
static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
struct v4l2_pix_format *pix)
{
+ const struct rzg2l_cru_info *info = cru->info;
const struct rzg2l_cru_ip_format *fmt;
fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat);
@@ -708,10 +938,17 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
}
/* Limit to CRU capabilities */
- v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1,
- &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0);
+ v4l_bound_align_image(&pix->width, 320, info->max_width, 1,
+ &pix->height, 240, info->max_height, 2, 0);
+
+ if (info->has_stride) {
+ u32 stride = clamp(pix->bytesperline, pix->width * fmt->bpp,
+ RZG2L_CRU_STRIDE_MAX);
+ pix->bytesperline = round_up(stride, RZG2L_CRU_STRIDE_ALIGN);
+ } else {
+ pix->bytesperline = pix->width * fmt->bpp;
+ }
- pix->bytesperline = pix->width * fmt->bpp;
pix->sizeimage = pix->bytesperline * pix->height;
dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n",
diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile
index 4bb4dcbef7b5..de8c802e1d1a 100644
--- a/drivers/media/platform/renesas/vsp1/Makefile
+++ b/drivers/media/platform/renesas/vsp1/Makefile
@@ -5,6 +5,6 @@ vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o
vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o
vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o
-vsp1-y += vsp1_lif.o vsp1_uif.o
+vsp1-y += vsp1_iif.o vsp1_lif.o vsp1_uif.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o
diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h
index 2f6f0c6ae555..f97a1a31bfab 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1.h
@@ -32,6 +32,7 @@ struct vsp1_clu;
struct vsp1_hgo;
struct vsp1_hgt;
struct vsp1_hsit;
+struct vsp1_iif;
struct vsp1_lif;
struct vsp1_lut;
struct vsp1_rwpf;
@@ -56,6 +57,8 @@ struct vsp1_uif;
#define VSP1_HAS_BRS BIT(9)
#define VSP1_HAS_EXT_DL BIT(10)
#define VSP1_HAS_NON_ZERO_LBA BIT(11)
+#define VSP1_HAS_IIF BIT(12)
+#define VSP1_HAS_HSIT BIT(13)
struct vsp1_device_info {
u32 version;
@@ -91,6 +94,7 @@ struct vsp1_device {
struct vsp1_hgt *hgt;
struct vsp1_hsit *hsi;
struct vsp1_hsit *hst;
+ struct vsp1_iif *iif;
struct vsp1_lif *lif[VSP1_MAX_LIF];
struct vsp1_lut *lut;
struct vsp1_rwpf *rpf[VSP1_MAX_RPF];
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
index 5dee0490c593..5fc2e5a3bb30 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
@@ -15,6 +15,7 @@
#include "vsp1.h"
#include "vsp1_brx.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
@@ -108,6 +109,8 @@ static void brx_try_format(struct vsp1_brx *brx,
if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+
+ vsp1_entity_adjust_color_space(fmt);
break;
default:
@@ -115,13 +118,17 @@ static void brx_try_format(struct vsp1_brx *brx,
format = v4l2_subdev_state_get_format(sd_state,
BRX_PAD_SINK(0));
fmt->code = format->code;
+
+ fmt->colorspace = format->colorspace;
+ fmt->xfer_func = format->xfer_func;
+ fmt->ycbcr_enc = format->ycbcr_enc;
+ fmt->quantization = format->quantization;
break;
}
fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE);
fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE);
fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int brx_set_format(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index ad3fa1c9cc73..bb8228b19824 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
@@ -1099,7 +1099,12 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
return NULL;
dlm->index = index;
- dlm->singleshot = vsp1->info->uapi;
+ /*
+ * uapi = single shot mode;
+ * DRM = continuous mode;
+ * VSPX = single shot mode;
+ */
+ dlm->singleshot = vsp1->info->uapi || vsp1->iif;
dlm->vsp1 = vsp1;
spin_lock_init(&dlm->lock);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index b5d1f238f7be..fe55e8747b05 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
@@ -118,26 +118,26 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
struct vsp1_entity *uif,
unsigned int brx_input)
{
+ const struct vsp1_drm_input *input = &vsp1->drm->inputs[rpf->entity.index];
struct v4l2_subdev_selection sel = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
- const struct v4l2_rect *crop;
int ret;
/*
* Configure the format on the RPF sink pad and propagate it up to the
* BRx sink pad.
*/
- crop = &vsp1->drm->inputs[rpf->entity.index].crop;
-
format.pad = RWPF_PAD_SINK;
- format.format.width = crop->width + crop->left;
- format.format.height = crop->height + crop->top;
+ format.format.width = input->crop.width + input->crop.left;
+ format.format.height = input->crop.height + input->crop.top;
format.format.code = rpf->fmtinfo->mbus;
format.format.field = V4L2_FIELD_NONE;
+ format.format.ycbcr_enc = input->ycbcr_enc;
+ format.format.quantization = input->quantization;
ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
&format);
@@ -151,7 +151,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
sel.pad = RWPF_PAD_SINK;
sel.target = V4L2_SEL_TGT_CROP;
- sel.r = *crop;
+ sel.r = input->crop;
ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
&sel);
@@ -593,8 +593,8 @@ static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1,
fmtinfo = vsp1_get_format_info(vsp1, pixelformat);
if (!fmtinfo) {
- dev_dbg(vsp1->dev, "Unsupported pixel format %08x\n",
- pixelformat);
+ dev_dbg(vsp1->dev, "Unsupported pixel format %p4cc\n",
+ &pixelformat);
return -EINVAL;
}
@@ -826,12 +826,14 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
+ struct vsp1_drm_input *input;
struct vsp1_rwpf *rpf;
int ret;
if (rpf_index >= vsp1->info->rpf_count)
return -EINVAL;
+ input = &vsp1->drm->inputs[rpf_index];
rpf = vsp1->rpf[rpf_index];
if (!cfg) {
@@ -849,11 +851,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
}
dev_dbg(vsp1->dev,
- "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n",
+ "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%p4cc), pitch %u dma { %pad, %pad, %pad } zpos %u\n",
__func__, rpf_index,
cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height,
cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height,
- cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
+ &cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
&cfg->mem[2], cfg->zpos);
/*
@@ -873,9 +875,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
rpf->format.flags = cfg->premult ? V4L2_PIX_FMT_FLAG_PREMUL_ALPHA : 0;
- vsp1->drm->inputs[rpf_index].crop = cfg->src;
- vsp1->drm->inputs[rpf_index].compose = cfg->dst;
- vsp1->drm->inputs[rpf_index].zpos = cfg->zpos;
+ input->crop = cfg->src;
+ input->compose = cfg->dst;
+ input->zpos = cfg->zpos;
+ input->ycbcr_enc = cfg->color_encoding;
+ input->quantization = cfg->color_range;
drm_pipe->pipe.inputs[rpf_index] = rpf;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h
index 3fd95b53f27e..07a5d0adbd08 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h
@@ -52,17 +52,19 @@ struct vsp1_drm_pipeline {
* struct vsp1_drm - State for the API exposed to the DRM driver
* @pipe: the VSP1 DRM pipeline used for display
* @lock: protects the BRU and BRS allocation
- * @inputs: source crop rectangle, destination compose rectangle and z-order
- * position for every input (indexed by RPF index)
+ * @inputs: source crop rectangle, destination compose rectangle, z-order
+ * position and colorspace for every input (indexed by RPF index)
*/
struct vsp1_drm {
struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF];
struct mutex lock;
- struct {
+ struct vsp1_drm_input {
struct v4l2_rect crop;
struct v4l2_rect compose;
unsigned int zpos;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_quantization quantization;
} inputs[VSP1_MAX_RPF];
};
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index 9fc6bf624a52..8270a9d207cb 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -29,6 +29,7 @@
#include "vsp1_hgo.h"
#include "vsp1_hgt.h"
#include "vsp1_hsit.h"
+#include "vsp1_iif.h"
#include "vsp1_lif.h"
#include "vsp1_lut.h"
#include "vsp1_pipe.h"
@@ -302,22 +303,6 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities);
}
- vsp1->hsi = vsp1_hsit_create(vsp1, true);
- if (IS_ERR(vsp1->hsi)) {
- ret = PTR_ERR(vsp1->hsi);
- goto done;
- }
-
- list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
-
- vsp1->hst = vsp1_hsit_create(vsp1, false);
- if (IS_ERR(vsp1->hst)) {
- ret = PTR_ERR(vsp1->hst);
- goto done;
- }
-
- list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
-
if (vsp1_feature(vsp1, VSP1_HAS_HGO) && vsp1->info->uapi) {
vsp1->hgo = vsp1_hgo_create(vsp1);
if (IS_ERR(vsp1->hgo)) {
@@ -340,6 +325,34 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
&vsp1->entities);
}
+ if (vsp1_feature(vsp1, VSP1_HAS_IIF)) {
+ vsp1->iif = vsp1_iif_create(vsp1);
+ if (IS_ERR(vsp1->iif)) {
+ ret = PTR_ERR(vsp1->iif);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->iif->entity.list_dev, &vsp1->entities);
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ vsp1->hsi = vsp1_hsit_create(vsp1, true);
+ if (IS_ERR(vsp1->hsi)) {
+ ret = PTR_ERR(vsp1->hsi);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
+
+ vsp1->hst = vsp1_hsit_create(vsp1, false);
+ if (IS_ERR(vsp1->hst)) {
+ ret = PTR_ERR(vsp1->hst);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
+ }
+
/*
* The LIFs are only supported when used in conjunction with the DU, in
* which case the userspace API is disabled. If the userspace API is
@@ -683,8 +696,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.model = "VSP1-S",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO
- | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU
- | VSP1_HAS_WPF_VFLIP,
+ | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT
+ | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 3,
.wpf_count = 4,
@@ -694,7 +707,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPR_H2,
.model = "VSP1-R",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
+ .features = VSP1_HAS_BRU | VSP1_HAS_HSIT | VSP1_HAS_SRU
+ | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 3,
.wpf_count = 4,
@@ -704,7 +718,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN2,
.model = "VSP1-D",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_LUT,
+ .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_HSIT
+ | VSP1_HAS_LUT,
.lif_count = 1,
.rpf_count = 4,
.uds_count = 1,
@@ -716,8 +731,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.model = "VSP1-S",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO
- | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU
- | VSP1_HAS_WPF_VFLIP,
+ | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT
+ | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 1,
.wpf_count = 4,
@@ -727,8 +742,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPS_V2H,
.model = "VSP1V-S",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
- | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT
+ | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 4,
.uds_count = 1,
.wpf_count = 4,
@@ -738,7 +753,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPD_V2H,
.model = "VSP1V-D",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT,
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT
+ | VSP1_HAS_LUT,
.lif_count = 1,
.rpf_count = 4,
.uds_count = 1,
@@ -750,8 +766,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.model = "VSP2-I",
.gen = 3,
.features = VSP1_HAS_CLU | VSP1_HAS_HGO | VSP1_HAS_HGT
- | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_HFLIP
- | VSP1_HAS_WPF_VFLIP,
+ | VSP1_HAS_HSIT | VSP1_HAS_LUT | VSP1_HAS_SRU
+ | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP,
.rpf_count = 1,
.uds_count = 1,
.wpf_count = 1,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
index 8b8945bd8f10..a6680d531872 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
@@ -63,9 +63,14 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
/*
* The ILV and BRS share the same data path route. The extra BRSSEL bit
* selects between the ILV and BRS.
+ *
+ * The BRU and IIF share the same data path route. The extra IIFSEL bit
+ * selects between the IIF and BRU.
*/
if (source->type == VSP1_ENTITY_BRS)
route |= VI6_DPR_ROUTE_BRSSEL;
+ else if (source->type == VSP1_ENTITY_IIF)
+ route |= VI6_DPR_ROUTE_IIFSEL;
vsp1_dl_body_write(dlb, source->route->reg, route);
}
@@ -99,6 +104,20 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity,
dl, dlb);
}
+void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format)
+{
+ u8 xfer_func = format->xfer_func;
+ u8 ycbcr_enc = format->ycbcr_enc;
+ u8 quantization = format->quantization;
+
+ vsp1_adjust_color_space(format->code, &format->colorspace, &xfer_func,
+ &ycbcr_enc, &quantization);
+
+ format->xfer_func = xfer_func;
+ format->ycbcr_enc = ycbcr_enc;
+ format->quantization = quantization;
+}
+
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
@@ -329,7 +348,13 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
format->height = clamp_t(unsigned int, fmt->format.height,
min_height, max_height);
format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ format->colorspace = fmt->format.colorspace;
+ format->xfer_func = fmt->format.xfer_func;
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+
+ vsp1_entity_adjust_color_space(format);
fmt->format = *format;
@@ -528,6 +553,9 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad)
{ VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
static const struct vsp1_route vsp1_routes[] = {
+ { VSP1_ENTITY_IIF, 0, VI6_DPR_BRU_ROUTE,
+ { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
+ VI6_DPR_NODE_BRU_IN(3) }, VI6_DPR_NODE_WPF(0) },
{ VSP1_ENTITY_BRS, 0, VI6_DPR_ILV_BRS_ROUTE,
{ VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1) }, 0 },
{ VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
index 1bcc9e27dfdc..b7c72d0b7f8e 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
@@ -28,6 +28,7 @@ enum vsp1_entity_type {
VSP1_ENTITY_HGT,
VSP1_ENTITY_HSI,
VSP1_ENTITY_HST,
+ VSP1_ENTITY_IIF,
VSP1_ENTITY_LIF,
VSP1_ENTITY_LUT,
VSP1_ENTITY_RPF,
@@ -170,6 +171,8 @@ void vsp1_entity_configure_partition(struct vsp1_entity *entity,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb);
+void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format);
+
struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad);
int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
index 8ba2a7c7305c..1fcd1967d3b2 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
@@ -14,6 +14,7 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_hsit.h"
#define HSIT_MIN_SIZE 4U
@@ -96,7 +97,13 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
format->height = clamp_t(unsigned int, fmt->format.height,
HSIT_MIN_SIZE, HSIT_MAX_SIZE);
format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ format->colorspace = fmt->format.colorspace;
+ format->xfer_func = fmt->format.xfer_func;
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+
+ vsp1_entity_adjust_color_space(format);
fmt->format = *format;
@@ -106,6 +113,8 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32
: MEDIA_BUS_FMT_AHSV8888_1X32;
+ vsp1_entity_adjust_color_space(format);
+
done:
mutex_unlock(&hsit->entity.lock);
return ret;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.c b/drivers/media/platform/renesas/vsp1/vsp1_iif.c
new file mode 100644
index 000000000000..5dd62bebbe8c
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vsp1_iif.c -- R-Car VSP1 IIF (ISP Interface)
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Corporation
+ */
+
+#include "vsp1.h"
+#include "vsp1_dl.h"
+#include "vsp1_iif.h"
+
+#define IIF_MIN_WIDTH 128U
+#define IIF_MIN_HEIGHT 32U
+#define IIF_MAX_WIDTH 5120U
+#define IIF_MAX_HEIGHT 4096U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline void vsp1_iif_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
+{
+ vsp1_dl_body_write(dlb, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static const unsigned int iif_codes[] = {
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y12_1X12,
+ MEDIA_BUS_FMT_Y16_1X16,
+ MEDIA_BUS_FMT_METADATA_FIXED
+};
+
+static int iif_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, iif_codes,
+ ARRAY_SIZE(iif_codes));
+}
+
+static int iif_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
+ IIF_MIN_WIDTH, IIF_MIN_HEIGHT,
+ IIF_MAX_WIDTH, IIF_MAX_HEIGHT);
+}
+
+static int iif_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, iif_codes,
+ ARRAY_SIZE(iif_codes),
+ IIF_MIN_WIDTH, IIF_MIN_HEIGHT,
+ IIF_MAX_WIDTH, IIF_MAX_HEIGHT);
+}
+
+static const struct v4l2_subdev_pad_ops iif_pad_ops = {
+ .enum_mbus_code = iif_enum_mbus_code,
+ .enum_frame_size = iif_enum_frame_size,
+ .get_fmt = vsp1_subdev_get_pad_format,
+ .set_fmt = iif_set_format,
+};
+
+static const struct v4l2_subdev_ops iif_ops = {
+ .pad = &iif_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * VSP1 Entity Operations
+ */
+
+static void iif_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ vsp1_iif_write(dlb, VI6_IIF_CTRL, VI6_IIF_CTRL_CTRL);
+}
+
+static const struct vsp1_entity_operations iif_entity_ops = {
+ .configure_stream = iif_configure_stream,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1)
+{
+ struct vsp1_iif *iif;
+ int ret;
+
+ iif = devm_kzalloc(vsp1->dev, sizeof(*iif), GFP_KERNEL);
+ if (!iif)
+ return ERR_PTR(-ENOMEM);
+
+ iif->entity.ops = &iif_entity_ops;
+ iif->entity.type = VSP1_ENTITY_IIF;
+
+ /*
+ * The IIF is never exposed to userspace, but media entity registration
+ * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to
+ * avoid triggering a WARN_ON(), the value won't be seen anywhere.
+ */
+ ret = vsp1_entity_init(vsp1, &iif->entity, "iif", 3, &iif_ops,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return iif;
+}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.h b/drivers/media/platform/renesas/vsp1/vsp1_iif.h
new file mode 100644
index 000000000000..46f327851c35
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_iif.h -- R-Car VSP1 IIF (ISP Interface)
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Corporation
+ */
+#ifndef __VSP1_IIF_H__
+#define __VSP1_IIF_H__
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+#define VSPX_IIF_SINK_PAD_IMG 0
+#define VSPX_IIF_SINK_PAD_CONFIG 2
+
+struct vsp1_iif {
+ struct vsp1_entity entity;
+};
+
+static inline struct vsp1_iif *to_iif(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_iif, entity.subdev);
+}
+
+struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_IIF_H__ */
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
index bb0739f684f3..3cbb768cf6ad 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
@@ -138,14 +138,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 32, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_1X32,
- VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 24, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_1X32,
- VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 32, 0, 0 }, false, false, 1, 1, false },
{ V4L2_PIX_FMT_RGBX1010102, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB10_RGB10A2_A2RGB10,
VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
@@ -162,10 +154,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 16, 0, 0 }, false, false, 2, 1, false },
- { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, false, true, 2, 1, false },
{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
@@ -222,6 +210,24 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
1, { 32, 0, 0 }, false, false, 2, 1, false },
};
+static const struct vsp1_format_info vsp1_video_gen2_formats[] = {
+ { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, true, 2, 1, false },
+};
+
+static const struct vsp1_format_info vsp1_video_hsit_formats[] = {
+ { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_1X32,
+ VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, false },
+};
+
/**
* vsp1_get_format_info - Retrieve format information for a 4CC
* @vsp1: the VSP1 device
@@ -235,26 +241,164 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
{
unsigned int i;
- /* Special case, the VYUY and HSV formats are supported on Gen2 only. */
- if (vsp1->info->gen != 2) {
- switch (fourcc) {
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_HSV24:
- case V4L2_PIX_FMT_HSV32:
- return NULL;
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
+ const struct vsp1_format_info *info = &vsp1_video_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+
+ if (vsp1->info->gen == 2) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_gen2_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_hsit_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
}
}
+ return NULL;
+}
+
+/**
+ * vsp1_get_format_info_by_index - Enumerate format information
+ * @vsp1: the VSP1 device
+ * @index: the format index
+ * @code: media bus code to limit enumeration
+ *
+ * Return a pointer to the format information structure corresponding to the
+ * given index, or NULL if the index exceeds the supported formats list. If the
+ * @code parameter is not zero, only formats compatible with the media bus code
+ * will be enumerated.
+ */
+const struct vsp1_format_info *
+vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
+ u32 code)
+{
+ unsigned int i;
+
+ if (!code) {
+ if (index < ARRAY_SIZE(vsp1_video_formats))
+ return &vsp1_video_formats[index];
+
+ if (vsp1->info->gen == 2) {
+ index -= ARRAY_SIZE(vsp1_video_formats);
+ if (index < ARRAY_SIZE(vsp1_video_gen2_formats))
+ return &vsp1_video_gen2_formats[index];
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ index -= ARRAY_SIZE(vsp1_video_gen2_formats);
+ if (index < ARRAY_SIZE(vsp1_video_hsit_formats))
+ return &vsp1_video_hsit_formats[index];
+ }
+
+ return NULL;
+ }
+
for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
const struct vsp1_format_info *info = &vsp1_video_formats[i];
- if (info->fourcc == fourcc)
- return info;
+ if (info->mbus == code) {
+ if (!index)
+ return info;
+ index--;
+ }
+ }
+
+ if (vsp1->info->gen == 2) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_gen2_formats[i];
+
+ if (info->mbus == code) {
+ if (!index)
+ return info;
+ index--;
+ }
+ }
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_hsit_formats[i];
+
+ if (info->mbus == code) {
+ if (!index)
+ return info;
+ index--;
+ }
+ }
}
return NULL;
}
+/**
+ * vsp1_adjust_color_space - Adjust color space fields in a format
+ * @code: the media bus code
+ * @colorspace: the colorspace
+ * @xfer_func: the transfer function
+ * @encoding: the encoding
+ * @quantization: the quantization
+ *
+ * This function adjusts all color space fields of a video device of subdev
+ * format structure, taking into account the requested format, requested color
+ * space and limitations of the VSP1. It should be used in the video device and
+ * subdev set format handlers.
+ *
+ * The colorspace and xfer_func fields are freely configurable, as they are out
+ * of scope for VSP processing. The encoding and quantization is hardcoded for
+ * non-YUV formats, and can be configured for YUV formats.
+ */
+void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func,
+ u8 *encoding, u8 *quantization)
+{
+ if (*colorspace == V4L2_COLORSPACE_DEFAULT ||
+ *colorspace >= V4L2_COLORSPACE_LAST)
+ *colorspace = code == MEDIA_BUS_FMT_AYUV8_1X32
+ ? V4L2_COLORSPACE_SMPTE170M
+ : V4L2_COLORSPACE_SRGB;
+
+ if (*xfer_func == V4L2_XFER_FUNC_DEFAULT ||
+ *xfer_func >= V4L2_XFER_FUNC_LAST)
+ *xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(*colorspace);
+
+ switch (code) {
+ case MEDIA_BUS_FMT_ARGB8888_1X32:
+ default:
+ *encoding = V4L2_YCBCR_ENC_601;
+ *quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+
+ case MEDIA_BUS_FMT_AHSV8888_1X32:
+ *encoding = V4L2_HSV_ENC_256;
+ *quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+
+ case MEDIA_BUS_FMT_AYUV8_1X32:
+ if (*encoding != V4L2_YCBCR_ENC_601 &&
+ *encoding != V4L2_YCBCR_ENC_709)
+ *encoding = V4L2_YCBCR_ENC_601;
+ if (*quantization != V4L2_QUANTIZATION_FULL_RANGE &&
+ *quantization != V4L2_QUANTIZATION_LIM_RANGE)
+ *quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ }
+}
+
/* -----------------------------------------------------------------------------
* Pipeline Management
*/
@@ -286,6 +430,7 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
pipe->brx = NULL;
pipe->hgo = NULL;
pipe->hgt = NULL;
+ pipe->iif = NULL;
pipe->lif = NULL;
pipe->uds = NULL;
}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
index 1ba7bdbad5a8..7f623b8cbe5c 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
@@ -119,6 +119,7 @@ struct vsp1_pipeline {
struct vsp1_entity *brx;
struct vsp1_entity *hgo;
struct vsp1_entity *hgt;
+ struct vsp1_entity *iif;
struct vsp1_entity *lif;
struct vsp1_entity *uds;
struct vsp1_entity *uds_input;
@@ -179,5 +180,10 @@ void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe,
const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
u32 fourcc);
+const struct vsp1_format_info *
+vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
+ u32 code);
+void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func,
+ u8 *encoding, u8 *quantization);
#endif /* __VSP1_PIPE_H__ */
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
index 7eca82e0ba7e..86e47c2d991f 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
@@ -253,6 +253,13 @@
#define VI6_RPF_BRDITH_CTRL_CBRM BIT(0)
/* -----------------------------------------------------------------------------
+ * IIF Control Registers
+ */
+
+#define VI6_IIF_CTRL 0x0608
+#define VI6_IIF_CTRL_CTRL 0x13
+
+/* -----------------------------------------------------------------------------
* WPF Control Registers
*/
@@ -388,6 +395,7 @@
#define VI6_DPR_HST_ROUTE 0x2044
#define VI6_DPR_HSI_ROUTE 0x2048
#define VI6_DPR_BRU_ROUTE 0x204c
+#define VI6_DPR_ROUTE_IIFSEL BIT(28)
#define VI6_DPR_ILV_BRS_ROUTE 0x2050
#define VI6_DPR_ROUTE_BRSSEL BIT(28)
#define VI6_DPR_ROUTE_FXA_MASK (0xff << 16)
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
index 5c8b3ba1bd3c..811f2b7c5cc5 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
@@ -84,7 +84,7 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
- infmt = VI6_RPF_INFMT_CIPM
+ infmt = (pipe->iif ? 0 : VI6_RPF_INFMT_CIPM)
| (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
if (fmtinfo->swap_yc)
@@ -92,12 +92,44 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
if (fmtinfo->swap_uv)
infmt |= VI6_RPF_INFMT_SPUVS;
- if (sink_format->code != source_format->code)
- infmt |= VI6_RPF_INFMT_CSC;
+ if (sink_format->code != source_format->code) {
+ u16 ycbcr_enc;
+ u16 quantization;
+ u32 rdtm;
+
+ if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
+ ycbcr_enc = sink_format->ycbcr_enc;
+ quantization = sink_format->quantization;
+ } else {
+ ycbcr_enc = source_format->ycbcr_enc;
+ quantization = source_format->quantization;
+ }
+
+ if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ rdtm = VI6_RPF_INFMT_RDTM_BT601;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ rdtm = VI6_RPF_INFMT_RDTM_BT601_EXT;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_709 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ rdtm = VI6_RPF_INFMT_RDTM_BT709;
+ else
+ rdtm = VI6_RPF_INFMT_RDTM_BT709_EXT;
+
+ infmt |= VI6_RPF_INFMT_CSC | rdtm;
+ }
vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt);
vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap);
+ /* No further configuration for VSPX. */
+ if (pipe->iif) {
+ /* VSPX wants alpha_sel to be set to 0. */
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, 0);
+ return;
+ }
+
if (entity->vsp1->info->gen == 4) {
u32 ext_infmt0;
u32 ext_infmt1;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
index 9d38203e73d0..9c8085d5d306 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
@@ -10,6 +10,7 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
+#include "vsp1_entity.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
@@ -35,6 +36,11 @@ static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
code->code = codes[code->index];
+ if (code->pad == RWPF_PAD_SOURCE &&
+ code->code == MEDIA_BUS_FMT_AYUV8_1X32)
+ code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC
+ | V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
+
return 0;
}
@@ -76,12 +82,45 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
format = v4l2_subdev_state_get_format(state, fmt->pad);
if (fmt->pad == RWPF_PAD_SOURCE) {
+ const struct v4l2_mbus_framefmt *sink_format =
+ v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
+ u16 flags = fmt->format.flags & V4L2_MBUS_FRAMEFMT_SET_CSC;
+ bool csc;
+
/*
* The RWPF performs format conversion but can't scale, only the
- * format code can be changed on the source pad.
+ * format code, encoding and quantization can be changed on the
+ * source pad when converting between RGB and YUV.
+ */
+ if (sink_format->code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
+ fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32)
+ format->code = fmt->format.code;
+ else
+ format->code = sink_format->code;
+
+ /*
+ * Encoding and quantization can only be configured when YCbCr
+ * <-> RGB is enabled. The V4L2 API requires userspace to set
+ * the V4L2_MBUS_FRAMEFMT_SET_CSC flag. If either of these
+ * conditions is not met, use the encoding and quantization
+ * values from the sink pad.
*/
- format->code = fmt->format.code;
+ csc = (format->code == MEDIA_BUS_FMT_AYUV8_1X32) !=
+ (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32);
+
+ if (csc && (flags & V4L2_MBUS_FRAMEFMT_SET_CSC)) {
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+ } else {
+ format->ycbcr_enc = sink_format->ycbcr_enc;
+ format->quantization = sink_format->quantization;
+ }
+
+ vsp1_entity_adjust_color_space(format);
+
fmt->format = *format;
+ fmt->format.flags = flags;
+
goto done;
}
@@ -91,7 +130,13 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
format->height = clamp_t(unsigned int, fmt->format.height,
RWPF_MIN_HEIGHT, rwpf->max_height);
format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ format->colorspace = fmt->format.colorspace;
+ format->xfer_func = fmt->format.xfer_func;
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+
+ vsp1_entity_adjust_color_space(format);
fmt->format = *format;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
index 1759ce642e6e..bba2872afaf2 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
@@ -14,6 +14,7 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_pipe.h"
#include "vsp1_sru.h"
@@ -178,6 +179,8 @@ static void sru_try_format(struct vsp1_sru *sru,
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+ vsp1_entity_adjust_color_space(fmt);
+
fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
break;
@@ -187,6 +190,11 @@ static void sru_try_format(struct vsp1_sru *sru,
format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK);
fmt->code = format->code;
+ fmt->colorspace = format->colorspace;
+ fmt->xfer_func = format->xfer_func;
+ fmt->ycbcr_enc = format->ycbcr_enc;
+ fmt->quantization = format->quantization;
+
/*
* We can upscale by 2 in both direction, but not independently.
* Compare the input and output rectangles areas (avoiding
@@ -211,7 +219,6 @@ static void sru_try_format(struct vsp1_sru *sru,
}
fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int sru_set_format(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
index c5a38478cf8c..2db473b6f83c 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
@@ -14,6 +14,7 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_pipe.h"
#include "vsp1_uds.h"
@@ -177,6 +178,8 @@ static void uds_try_format(struct vsp1_uds *uds,
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+ vsp1_entity_adjust_color_space(fmt);
+
fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);
fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);
break;
@@ -186,6 +189,11 @@ static void uds_try_format(struct vsp1_uds *uds,
format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK);
fmt->code = format->code;
+ fmt->colorspace = format->colorspace;
+ fmt->xfer_func = format->xfer_func;
+ fmt->ycbcr_enc = format->ycbcr_enc;
+ fmt->quantization = format->quantization;
+
uds_output_limits(format->width, &minimum, &maximum);
fmt->width = clamp(fmt->width, minimum, maximum);
uds_output_limits(format->height, &minimum, &maximum);
@@ -194,7 +202,6 @@ static void uds_try_format(struct vsp1_uds *uds,
}
fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int uds_set_format(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c
index 03f4efd6b82b..bc66fbdde3cc 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_video.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c
@@ -127,12 +127,24 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT);
pix->pixelformat = info->fourcc;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
pix->field = V4L2_FIELD_NONE;
- if (info->fourcc == V4L2_PIX_FMT_HSV24 ||
- info->fourcc == V4L2_PIX_FMT_HSV32)
- pix->hsv_enc = V4L2_HSV_ENC_256;
+ /*
+ * Adjust the colour space fields. On capture devices, userspace needs
+ * to set the V4L2_PIX_FMT_FLAG_SET_CSC to override the defaults. Reset
+ * all fields to *_DEFAULT if the flag isn't set, to then handle
+ * capture and output devices in the same way.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ !(pix->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
+ pix->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix->quantization = V4L2_QUANTIZATION_DEFAULT;
+ }
+
+ vsp1_adjust_color_space(info->mbus, &pix->colorspace, &pix->xfer_func,
+ &pix->ycbcr_enc, &pix->quantization);
memset(pix->reserved, 0, sizeof(pix->reserved));
@@ -888,16 +900,36 @@ vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
struct vsp1_video *video = to_vsp1_video(vfh->vdev);
cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
- | V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_IO_MC | V4L2_CAP_VIDEO_CAPTURE_MPLANE
| V4L2_CAP_VIDEO_OUTPUT_MPLANE;
-
strscpy(cap->driver, "vsp1", sizeof(cap->driver));
strscpy(cap->card, video->video.name, sizeof(cap->card));
return 0;
}
+static int vsp1_video_enum_format(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+ const struct vsp1_format_info *info;
+
+ info = vsp1_get_format_info_by_index(video->vsp1, f->index, f->mbus_code);
+ if (!info)
+ return -EINVAL;
+
+ f->pixelformat = info->fourcc;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ info->mbus == MEDIA_BUS_FMT_AYUV8_1X32)
+ f->flags = V4L2_FMT_FLAG_CSC_YCBCR_ENC
+ | V4L2_FMT_FLAG_CSC_QUANTIZATION;
+
+ return 0;
+}
+
static int
vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
{
@@ -1013,6 +1045,8 @@ err_pipe:
static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = {
.vidioc_querycap = vsp1_video_querycap,
+ .vidioc_enum_fmt_vid_cap = vsp1_video_enum_format,
+ .vidioc_enum_fmt_vid_out = vsp1_video_enum_format,
.vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format,
.vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format,
.vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format,
@@ -1207,14 +1241,14 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
video->pad.flags = MEDIA_PAD_FL_SOURCE;
video->video.vfl_dir = VFL_DIR_TX;
video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
- V4L2_CAP_STREAMING;
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
} else {
direction = "output";
video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
video->pad.flags = MEDIA_PAD_FL_SINK;
video->video.vfl_dir = VFL_DIR_RX;
video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_STREAMING;
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
}
mutex_init(&video->lock);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
index f176750ccd98..30662cfdf837 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
@@ -133,6 +133,7 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf)
{
struct vsp1_device *vsp1 = wpf->entity.vsp1;
unsigned int num_flip_ctrls;
+ int ret;
spin_lock_init(&wpf->flip.lock);
@@ -156,7 +157,9 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf)
num_flip_ctrls = 0;
}
- vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
+ ret = vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
+ if (ret < 0)
+ return ret;
if (num_flip_ctrls >= 1) {
wpf->flip.ctrls.vflip =
@@ -174,11 +177,8 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf)
v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip);
}
- if (wpf->ctrls.error) {
- dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
- wpf->entity.index);
+ if (wpf->ctrls.error)
return wpf->ctrls.error;
- }
return 0;
}
@@ -247,8 +247,11 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
- /* Format */
- if (!pipe->lif || wpf->writeback) {
+ /*
+ * Format configuration. Skip for IIF (VSPX) or if the pipe doesn't
+ * write to memory.
+ */
+ if (!pipe->iif && (!pipe->lif || wpf->writeback)) {
const struct v4l2_pix_format_mplane *format = &wpf->format;
const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
@@ -279,8 +282,33 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
(256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
}
- if (sink_format->code != source_format->code)
- outfmt |= VI6_WPF_OUTFMT_CSC;
+ if (sink_format->code != source_format->code) {
+ u16 ycbcr_enc;
+ u16 quantization;
+ u32 wrtm;
+
+ if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
+ ycbcr_enc = sink_format->ycbcr_enc;
+ quantization = sink_format->quantization;
+ } else {
+ ycbcr_enc = source_format->ycbcr_enc;
+ quantization = source_format->quantization;
+ }
+
+ if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT601;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT601_EXT;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_709 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT709;
+ else
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT709_EXT;
+
+ outfmt |= VI6_WPF_OUTFMT_CSC | wrtm;
+ }
wpf->outfmt = outfmt;
@@ -291,7 +319,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
* Sources. If the pipeline has a single input and BRx is not used,
* configure it as the master layer. Otherwise configure all
* inputs as sub-layers and select the virtual RPF as the master
- * layer.
+ * layer. For VSPX configure the enabled sources as masters.
*/
for (i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *input = pipe->inputs[i];
@@ -299,7 +327,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
if (!input)
continue;
- srcrpf |= (!pipe->brx && pipe->num_inputs == 1)
+ srcrpf |= (pipe->iif || (!pipe->brx && pipe->num_inputs == 1))
? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
}
@@ -316,6 +344,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index),
VI6_WPF_IRQ_ENB_DFEE);
+ if (pipe->iif)
+ return;
+
/*
* Configure writeback for display pipelines (the wpf writeback flag is
* never set for memory-to-memory pipelines). Start by adding a chained
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index 4396348811c8..730bdf98565a 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -119,6 +119,13 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
if (IS_ERR(f))
return PTR_ERR(f);
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ }
+
for (i = 0; i < vb->num_planes; i++) {
vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage);
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index bf55beec0fac..43ed742a1649 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -376,7 +376,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
* Configure the dest framebuffer base address with pixel offset.
*/
offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h);
- dst_offset = rga_lookup_draw_pos(&offsets, mir_mode, rot_mode);
+ dst_offset = rga_lookup_draw_pos(&offsets, rot_mode, mir_mode);
dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
dst_offset->y_off;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
index d94917211828..8c29a1c9309a 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
@@ -881,7 +881,7 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd,
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
- dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__,
+ dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__,
sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO)
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
index bf0260600a19..139177db9c6d 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -327,13 +327,6 @@
#define RKISP1_CIF_IMG_EFF_CTRL_CFG_UPD BIT(4)
#define RKISP1_CIF_IMG_EFF_CTRL_YCBCR_FULL BIT(5)
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE_SHIFT 0
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE_SHIFT 1
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA_SHIFT 2
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL_SHIFT 3
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS_SHIFT 4
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SKETCH_SHIFT 5
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SHARPEN_SHIFT 6
#define RKISP1_CIF_IMG_EFF_CTRL_MODE_MASK 0xe
/* IMG_EFF_COLOR_SEL */
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
index f073e72a0d37..8e6b753d3081 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
@@ -600,7 +600,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd,
if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC)
return -EINVAL;
- dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__,
+ dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__,
sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r);
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
index c3c2e474a18a..5b412afd7d60 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
@@ -700,7 +700,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
r->left = round_down(r->left, var->hor_offs_align);
- dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d",
+ dbg("target %#x: (%d,%d)/%ux%u, sink fmt: %dx%d",
target, r->left, r->top, r->width, r->height,
sink->f_width, sink->f_height);
}
@@ -1622,7 +1622,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
r->height = f->height;
}
- dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
+ dbg("target %#x: (%d,%d)/%ux%u, f_w: %d, f_h: %d",
sel->pad, r->left, r->top, r->width, r->height,
f->f_width, f->f_height);
@@ -1671,7 +1671,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
spin_unlock_irqrestore(&fimc->slock, flags);
}
- dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top,
+ dbg("target %#x: (%d,%d)/%ux%u", sel->target, r->left, r->top,
r->width, r->height);
mutex_unlock(&fimc->lock);
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
index 366e6393817d..5f9c44e825a5 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
@@ -164,6 +164,7 @@ int fimc_is_hw_change_mode(struct fimc_is *is)
if (WARN_ON(is->config_index >= ARRAY_SIZE(cmd)))
return -EINVAL;
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0));
mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2));
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
index f23e51e3da2f..0ce293b0718b 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
@@ -611,7 +611,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r)
r->left = round_down(r->left, fimc->dd->win_hor_offs_align);
r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height);
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n",
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%ux%u, sink fmt: %dx%d\n",
r->left, r->top, r->width, r->height,
frame->f_width, frame->f_height);
}
@@ -631,7 +631,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
r->left = round_down(r->left, fimc->dd->out_hor_offs_align);
r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height);
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n",
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%ux%u, source fmt: %dx%d\n",
r->left, r->top, r->width, r->height,
frame->f_width, frame->f_height);
}
@@ -1140,7 +1140,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&fimc->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %d, f_h: %d\n",
__func__, f->rect.left, f->rect.top, f->rect.width,
f->rect.height, f->f_width, f->f_height);
@@ -1174,7 +1174,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&fimc->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %d, f_h: %d\n",
__func__, f->rect.left, f->rect.top, f->rect.width,
f->rect.height, f->f_width, f->f_height);
diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h
index a50e58ab7ef7..ea496670d4b5 100644
--- a/drivers/media/platform/samsung/exynos4-is/media-dev.h
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h
@@ -179,8 +179,8 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
static inline bool fimc_md_is_isp_available(struct device_node *node)
{
struct device_node *child __free(device_node) =
- of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME);
- return child ? of_device_is_available(child) : false;
+ of_get_available_child_by_name(node, FIMC_IS_OF_NODE_NAME);
+ return child;
}
#else
#define fimc_md_is_isp_available(node) (false)
diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
index bd1149e8abc2..3e566b65f417 100644
--- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
@@ -1030,9 +1030,9 @@ static int s3c_camif_s_selection(struct file *file, void *priv,
vp->state |= ST_VP_CONFIG;
spin_unlock_irqrestore(&camif->slock, flags);
- pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n",
- sel->type, sel->target, sel->flags,
- sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+ pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%ux%u\n",
+ sel->type, sel->target, sel->flags,
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
return 0;
}
@@ -1372,7 +1372,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd,
mutex_unlock(&camif->lock);
- v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n",
+ v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d)/%ux%u, size: %ux%u\n",
__func__, crop->left, crop->top, crop->width,
crop->height, mf->width, mf->height);
@@ -1424,7 +1424,7 @@ static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r)
}
}
- v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n",
+ v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%ux%u, fmt: %ux%u\n",
r->left, r->top, r->width, r->height, mf->width, mf->height);
}
@@ -1464,7 +1464,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&camif->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %u, f_h: %u\n",
__func__, crop->left, crop->top, crop->width, crop->height,
camif->mbus_fmt.width, camif->mbus_fmt.height);
diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
index fa49fe580e1a..075a58b50b8c 100644
--- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
+++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
@@ -45,6 +45,7 @@
#define S5P_FIMV_H2R_CMD_WAKEUP_V6 8
#define S5P_FIMV_CH_LAST_FRAME_V6 9
#define S5P_FIMV_H2R_CMD_FLUSH_V6 10
+#define S5P_FIMV_H2R_CMD_NAL_ABORT_V6 11
/* RMVME: REALLOC used? */
#define S5P_FIMV_CH_FRAME_START_REALLOC_V6 5
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
index 5f80931f056d..73fdcd362265 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
@@ -143,7 +143,7 @@ void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq)
static void s5p_mfc_watchdog(struct timer_list *t)
{
- struct s5p_mfc_dev *dev = from_timer(dev, t, watchdog_timer);
+ struct s5p_mfc_dev *dev = timer_container_of(dev, t, watchdog_timer);
if (test_bit(0, &dev->hw_lock))
atomic_inc(&dev->watchdog_cnt);
@@ -739,6 +739,20 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
ctx->state = MFCINST_RUNNING;
goto irq_cleanup_hw;
+ case S5P_MFC_R2H_CMD_ENC_BUFFER_FUL_RET:
+ ctx->state = MFCINST_NAL_ABORT;
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ set_work_bit(ctx);
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ break;
+
+ case S5P_MFC_R2H_CMD_NAL_ABORT_RET:
+ ctx->state = MFCINST_ERROR;
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ goto irq_cleanup_hw;
+
default:
mfc_debug(2, "Unknown int reason\n");
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
@@ -935,7 +949,7 @@ err_pwr_enable:
if (dev->num_inst == 1) {
if (s5p_mfc_power_off(dev) < 0)
mfc_err("power off failed\n");
- del_timer_sync(&dev->watchdog_timer);
+ timer_delete_sync(&dev->watchdog_timer);
}
err_ctrls_setup:
s5p_mfc_dec_ctrls_delete(ctx);
@@ -985,7 +999,7 @@ static int s5p_mfc_release(struct file *file)
if (dev->num_inst == 0) {
mfc_debug(2, "Last instance\n");
s5p_mfc_deinit_hw(dev);
- del_timer_sync(&dev->watchdog_timer);
+ timer_delete_sync(&dev->watchdog_timer);
s5p_mfc_clock_off(dev);
if (s5p_mfc_power_off(dev) < 0)
mfc_err("Power off failed\n");
@@ -1461,7 +1475,7 @@ static void s5p_mfc_remove(struct platform_device *pdev)
}
mutex_unlock(&dev->mfc_mutex);
- del_timer_sync(&dev->watchdog_timer);
+ timer_delete_sync(&dev->watchdog_timer);
flush_work(&dev->watchdog_work);
video_unregister_device(dev->vfd_enc);
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
index 3cc2a4f5c40a..86c316c1ff8f 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
@@ -141,6 +141,7 @@ enum s5p_mfc_inst_state {
MFCINST_RES_CHANGE_INIT,
MFCINST_RES_CHANGE_FLUSH,
MFCINST_RES_CHANGE_END,
+ MFCINST_NAL_ABORT,
};
/*
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
index 73f7af674c01..98f8292b3173 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
@@ -549,8 +549,9 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
case V4L2_PIX_FMT_NV21M:
ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6);
ctx->stride[1] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6);
- ctx->luma_size = ctx->stride[0] * ALIGN(ctx->img_height, 16);
- ctx->chroma_size = ctx->stride[0] * ALIGN(ctx->img_height / 2, 16);
+ ctx->luma_size = ALIGN(ctx->stride[0] * ALIGN(ctx->img_height, 16), 256);
+ ctx->chroma_size = ALIGN(ctx->stride[0] * ALIGN(ctx->img_height / 2, 16),
+ 256);
break;
case V4L2_PIX_FMT_YUV420M:
case V4L2_PIX_FMT_YVU420M:
@@ -2228,6 +2229,11 @@ static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
case MFCINST_HEAD_PRODUCED:
ret = s5p_mfc_run_init_enc_buffers(ctx);
break;
+ case MFCINST_NAL_ABORT:
+ mfc_write(dev, ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc,
+ dev, S5P_FIMV_H2R_CMD_NAL_ABORT_V6, NULL);
+ break;
default:
ret = -EAGAIN;
}
diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
index a27f638df11c..f9348aeacc11 100644
--- a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
@@ -455,11 +455,11 @@ static int last_request_show(struct seq_file *s, void *data)
seq_printf(s, "Format: %s\t\t\t%s\n",
bdisp_fmt_to_str(src), bdisp_fmt_to_str(dst));
- seq_printf(s, "Crop area: %dx%d @ %d,%d ==>\t%dx%d @ %d,%d\n",
- src.crop.width, src.crop.height,
+ seq_printf(s, "Crop area: (%d,%d)/%ux%u ==>\t(%d,%d)/%ux%u\n",
src.crop.left, src.crop.top,
- dst.crop.width, dst.crop.height,
- dst.crop.left, dst.crop.top);
+ src.crop.width, src.crop.height,
+ dst.crop.left, dst.crop.top,
+ dst.crop.width, dst.crop.height);
seq_printf(s, "Buff size: %dx%d\t\t%dx%d\n\n",
src.width, src.height, dst.width, dst.height);
diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
index 73ad66ed20f2..1eb934490c0b 100644
--- a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
@@ -953,8 +953,8 @@ static int bdisp_s_selection(struct file *file, void *fh,
if ((out.left < 0) || (out.left >= frame->width) ||
(out.top < 0) || (out.top >= frame->height)) {
dev_err(ctx->bdisp_dev->dev,
- "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n",
- out.width, out.height, out.left, out.top,
+ "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n",
+ out.left, out.top, out.width, out.height,
frame->width, frame->height);
return -EINVAL;
}
@@ -966,8 +966,8 @@ static int bdisp_s_selection(struct file *file, void *fh,
if (((out.left + out.width) > frame->width) ||
((out.top + out.height) > frame->height)) {
dev_err(ctx->bdisp_dev->dev,
- "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n",
- out.width, out.height, out.left, out.top,
+ "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n",
+ out.left, out.top, out.width, out.height,
frame->width, frame->height);
return -EINVAL;
}
@@ -982,9 +982,9 @@ static int bdisp_s_selection(struct file *file, void *fh,
if ((out.left != in->left) || (out.top != in->top) ||
(out.width != in->width) || (out.height != in->height)) {
dev_dbg(ctx->bdisp_dev->dev,
- "%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n",
- __func__, in->width, in->height, in->left, in->top,
- out.width, out.height, out.left, out.top);
+ "%s crop updated: (%d,%d)/%ux%u -> (%d,%d)/%ux%u\n",
+ __func__, in->left, in->top, in->width, in->height,
+ out.left, out.top, out.width, out.height);
*in = out;
}
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
index 7b3a37957e3a..602c37cbe177 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
@@ -62,7 +62,7 @@ static int load_c8sectpfe_fw(struct c8sectpfei *fei);
static void c8sectpfe_timer_interrupt(struct timer_list *t)
{
- struct c8sectpfei *fei = from_timer(fei, t, timer);
+ struct c8sectpfei *fei = timer_container_of(fei, t, timer);
struct channel_info *channel;
int chan_num;
@@ -351,7 +351,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n"
, __func__, __LINE__, fei->global_feed_count);
- del_timer(&fei->timer);
+ timer_delete(&fei->timer);
}
mutex_unlock(&fei->lock);
@@ -797,13 +797,12 @@ static int c8sectpfe_probe(struct platform_device *pdev)
}
tsin->i2c_adapter =
of_find_i2c_adapter_by_node(i2c_bus);
+ of_node_put(i2c_bus);
if (!tsin->i2c_adapter) {
dev_err(&pdev->dev, "No i2c adapter found\n");
- of_node_put(i2c_bus);
ret = -ENODEV;
goto err_node_put;
}
- of_node_put(i2c_bus);
/* Acquire reset GPIO and activate it */
tsin->rst_gpio = devm_fwnode_gpiod_get(dev,
diff --git a/drivers/media/platform/st/sti/delta/delta-debug.c b/drivers/media/platform/st/sti/delta/delta-debug.c
index 4b2eb6b63aa2..6acf46913cda 100644
--- a/drivers/media/platform/st/sti/delta/delta-debug.c
+++ b/drivers/media/platform/st/sti/delta/delta-debug.c
@@ -16,14 +16,14 @@ char *delta_streaminfo_str(struct delta_streaminfo *s, char *str,
return NULL;
snprintf(str, len,
- "%4.4s %dx%d %s %s dpb=%d %s %s %s%dx%d@(%d,%d) %s%d/%d",
+ "%4.4s %dx%d %s %s dpb=%d %s %s %s(%d,%d)/%ux%u %s%d/%d",
(char *)&s->streamformat, s->width, s->height,
s->profile, s->level, s->dpb,
(s->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced",
s->other,
s->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "",
- s->crop.width, s->crop.height,
s->crop.left, s->crop.top,
+ s->crop.width, s->crop.height,
s->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "",
s->pixelaspect.numerator,
s->pixelaspect.denominator);
@@ -38,13 +38,13 @@ char *delta_frameinfo_str(struct delta_frameinfo *f, char *str,
return NULL;
snprintf(str, len,
- "%4.4s %dx%d aligned %dx%d %s %s%dx%d@(%d,%d) %s%d/%d",
+ "%4.4s %dx%d aligned %dx%d %s %s(%d,%d)/%ux%u %s%d/%d",
(char *)&f->pixelformat, f->width, f->height,
f->aligned_width, f->aligned_height,
(f->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced",
f->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "",
- f->crop.width, f->crop.height,
f->crop.left, f->crop.top,
+ f->crop.width, f->crop.height,
f->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "",
f->pixelaspect.numerator,
f->pixelaspect.denominator);
diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c
index b6c8400fb92d..48fa781aab06 100644
--- a/drivers/media/platform/st/stm32/dma2d/dma2d.c
+++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c
@@ -490,7 +490,8 @@ static void device_run(void *prv)
dst->sequence = frm_cap->sequence++;
v4l2_m2m_buf_copy_metadata(src, dst, true);
- clk_enable(dev->gate);
+ if (clk_enable(dev->gate))
+ goto end;
dma2d_config_fg(dev, frm_out,
vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
diff --git a/drivers/media/platform/st/stm32/stm32-csi.c b/drivers/media/platform/st/stm32/stm32-csi.c
index 48941aae8c9b..b69048144cc1 100644
--- a/drivers/media/platform/st/stm32/stm32-csi.c
+++ b/drivers/media/platform/st/stm32/stm32-csi.c
@@ -325,7 +325,6 @@ static const struct stm32_csi_mbps_phy_reg snps_stm32mp25[] = {
{ .mbps = 2400, .hsfreqrange = 0x47, .osc_freq_target = 442 },
{ .mbps = 2450, .hsfreqrange = 0x48, .osc_freq_target = 451 },
{ .mbps = 2500, .hsfreqrange = 0x49, .osc_freq_target = 460 },
- { /* sentinel */ }
};
static const struct v4l2_mbus_framefmt fmt_default = {
@@ -358,7 +357,7 @@ static inline struct stm32_csi_dev *to_csidev(struct v4l2_subdev *sd)
static int stm32_csi_setup_lane_merger(struct stm32_csi_dev *csidev)
{
u32 lmcfgr = 0;
- int i;
+ unsigned int i;
for (i = 0; i < csidev->num_lanes; i++) {
if (!csidev->lanes[i] || csidev->lanes[i] > STM32_CSI_LANES_MAX) {
@@ -444,13 +443,15 @@ static void stm32_csi_phy_reg_write(struct stm32_csi_dev *csidev,
static int stm32_csi_start(struct stm32_csi_dev *csidev,
struct v4l2_subdev_state *state)
{
- const struct stm32_csi_mbps_phy_reg *phy_regs;
+ struct media_pad *src_pad =
+ &csidev->s_subdev->entity.pads[csidev->s_subdev_pad_nb];
+ const struct stm32_csi_mbps_phy_reg *phy_regs = NULL;
struct v4l2_mbus_framefmt *sink_fmt;
const struct stm32_csi_fmts *fmt;
unsigned long phy_clk_frate;
+ u32 lanes_ie, lanes_en;
unsigned int mbps;
- u32 lanes_ie = 0;
- u32 lanes_en = 0;
+ unsigned int i;
s64 link_freq;
int ret;
u32 ccfr;
@@ -465,7 +466,7 @@ static int stm32_csi_start(struct stm32_csi_dev *csidev,
if (!csidev->s_subdev)
return -EIO;
- link_freq = v4l2_get_link_freq(csidev->s_subdev->ctrl_handler,
+ link_freq = v4l2_get_link_freq(src_pad,
fmt->bpp, 2 * csidev->num_lanes);
if (link_freq < 0)
return link_freq;
@@ -474,11 +475,14 @@ static int stm32_csi_start(struct stm32_csi_dev *csidev,
mbps = div_s64(link_freq, 500000);
dev_dbg(csidev->dev, "Computed Mbps: %u\n", mbps);
- for (phy_regs = snps_stm32mp25; phy_regs->mbps != 0; phy_regs++)
- if (phy_regs->mbps >= mbps)
+ for (i = 0; i < ARRAY_SIZE(snps_stm32mp25); i++) {
+ if (snps_stm32mp25[i].mbps >= mbps) {
+ phy_regs = &snps_stm32mp25[i];
break;
+ }
+ }
- if (!phy_regs->mbps) {
+ if (!phy_regs) {
dev_err(csidev->dev, "Unsupported PHY speed (%u Mbps)", mbps);
return -ERANGE;
}
@@ -488,8 +492,8 @@ static int stm32_csi_start(struct stm32_csi_dev *csidev,
phy_regs->osc_freq_target);
/* Prepare lanes related configuration bits */
- lanes_ie |= STM32_CSI_SR1_DL0_ERRORS;
- lanes_en |= STM32_CSI_PCR_DL0EN;
+ lanes_ie = STM32_CSI_SR1_DL0_ERRORS;
+ lanes_en = STM32_CSI_PCR_DL0EN;
if (csidev->num_lanes == 2) {
lanes_ie |= STM32_CSI_SR1_DL1_ERRORS;
lanes_en |= STM32_CSI_PCR_DL1EN;
@@ -497,21 +501,19 @@ static int stm32_csi_start(struct stm32_csi_dev *csidev,
ret = pm_runtime_get_sync(csidev->dev);
if (ret < 0)
- return ret;
+ goto error_put;
/* Retrieve CSI2PHY clock rate to compute CCFR value */
phy_clk_frate = clk_get_rate(csidev->clks[STM32_CSI_CLK_CSI2PHY].clk);
if (!phy_clk_frate) {
- pm_runtime_put(csidev->dev);
dev_err(csidev->dev, "CSI2PHY clock rate invalid (0)\n");
- return ret;
+ ret = -EINVAL;
+ goto error_put;
}
ret = stm32_csi_setup_lane_merger(csidev);
- if (ret) {
- pm_runtime_put(csidev->dev);
- return ret;
- }
+ if (ret)
+ goto error_put;
/* Enable the CSI */
writel_relaxed(STM32_CSI_CR_CSIEN, csidev->base + STM32_CSI_CR);
@@ -567,6 +569,10 @@ static int stm32_csi_start(struct stm32_csi_dev *csidev,
writel_relaxed(0, csidev->base + STM32_CSI_PMCR);
return ret;
+
+error_put:
+ pm_runtime_put(csidev->dev);
+ return ret;
}
static void stm32_csi_stop(struct stm32_csi_dev *csidev)
@@ -591,20 +597,20 @@ static int stm32_csi_start_vc(struct stm32_csi_dev *csidev,
{
struct v4l2_mbus_framefmt *mbus_fmt;
const struct stm32_csi_fmts *fmt;
- u32 cfgr1 = 0;
- int ret = 0;
u32 status;
+ u32 cfgr1;
+ int ret;
mbus_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE);
fmt = stm32_csi_code_to_fmt(mbus_fmt->code);
/* If the mbus code is JPEG, don't enable filtering */
if (mbus_fmt->code == MEDIA_BUS_FMT_JPEG_1X8) {
- cfgr1 |= STM32_CSI_VCXCFGR1_ALLDT;
+ cfgr1 = STM32_CSI_VCXCFGR1_ALLDT;
cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_CDTFT_SHIFT;
dev_dbg(csidev->dev, "VC%d: enable AllDT mode\n", vc);
} else {
- cfgr1 |= fmt->datatype << STM32_CSI_VCXCFGR1_DT0_SHIFT;
+ cfgr1 = fmt->datatype << STM32_CSI_VCXCFGR1_DT0_SHIFT;
cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_DT0FT_SHIFT;
cfgr1 |= STM32_CSI_VCXCFGR1_DT0EN;
dev_dbg(csidev->dev, "VC%d: enable DT0(0x%x)/DT0FT(0x%x)\n",
@@ -630,8 +636,8 @@ static int stm32_csi_start_vc(struct stm32_csi_dev *csidev,
static int stm32_csi_stop_vc(struct stm32_csi_dev *csidev, u32 vc)
{
- int ret = 0;
u32 status;
+ int ret;
/* Stop the Virtual Channel */
writel_relaxed(STM32_CSI_CR_VCXSTOP(vc) | STM32_CSI_CR_CSIEN,
@@ -690,25 +696,27 @@ static int stm32_csi_enable_streams(struct v4l2_subdev *sd,
ret = stm32_csi_start_vc(csidev, state, 0);
if (ret) {
dev_err(csidev->dev, "Failed to start VC0\n");
- stm32_csi_stop(csidev);
- return ret;
+ goto failed_start_vc;
}
ret = v4l2_subdev_enable_streams(csidev->s_subdev,
csidev->s_subdev_pad_nb, BIT_ULL(0));
- if (ret) {
- stm32_csi_stop_vc(csidev, 0);
- stm32_csi_stop(csidev);
- return ret;
- }
+ if (ret)
+ goto failed_enable_streams;
return 0;
+
+failed_enable_streams:
+ stm32_csi_stop_vc(csidev, 0);
+failed_start_vc:
+ stm32_csi_stop(csidev);
+ return ret;
}
static int stm32_csi_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
- int i;
+ unsigned int i;
for (i = 0; i < sd->entity.num_pads; i++)
*v4l2_subdev_state_get_format(state, i) = fmt_default;
@@ -873,7 +881,8 @@ static irqreturn_t stm32_csi_irq_thread(int irq, void *arg)
static int stm32_csi_get_resources(struct stm32_csi_dev *csidev,
struct platform_device *pdev)
{
- int irq, ret, i;
+ unsigned int i;
+ int irq, ret;
csidev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(csidev->base))
@@ -926,38 +935,32 @@ static int stm32_csi_parse_dt(struct stm32_csi_dev *csidev)
}
ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
- fwnode_handle_put(ep);
if (ret) {
dev_err(csidev->dev, "Could not parse v4l2 endpoint\n");
- return ret;
+ goto out;
}
csidev->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
if (csidev->num_lanes > STM32_CSI_LANES_MAX) {
dev_err(csidev->dev, "Unsupported number of data-lanes: %d\n",
csidev->num_lanes);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
memcpy(csidev->lanes, v4l2_ep.bus.mipi_csi2.data_lanes,
sizeof(csidev->lanes));
- ep = fwnode_graph_get_next_endpoint(dev_fwnode(csidev->dev), NULL);
- if (!ep) {
- dev_err(csidev->dev, "Failed to get next endpoint\n");
- return -EINVAL;
- }
-
v4l2_async_subdev_nf_init(&csidev->notifier, &csidev->sd);
asd = v4l2_async_nf_add_fwnode_remote(&csidev->notifier, ep,
struct v4l2_async_connection);
- fwnode_handle_put(ep);
if (IS_ERR(asd)) {
dev_err(csidev->dev, "Failed to add fwnode remote subdev\n");
- return PTR_ERR(asd);
+ ret = PTR_ERR(asd);
+ goto out;
}
csidev->notifier.ops = &stm32_csi_notifier_ops;
@@ -966,9 +969,11 @@ static int stm32_csi_parse_dt(struct stm32_csi_dev *csidev)
if (ret) {
dev_err(csidev->dev, "Failed to register notifier\n");
v4l2_async_nf_cleanup(&csidev->notifier);
- return ret;
+ goto out;
}
+out:
+ fwnode_handle_put(ep);
return ret;
}
@@ -989,11 +994,11 @@ static int stm32_csi_probe(struct platform_device *pdev)
ret = stm32_csi_get_resources(csidev, pdev);
if (ret)
- goto err_free_priv;
+ return ret;
ret = stm32_csi_parse_dt(csidev);
if (ret)
- goto err_free_priv;
+ return ret;
csidev->sd.owner = THIS_MODULE;
csidev->sd.dev = &pdev->dev;
@@ -1018,10 +1023,6 @@ static int stm32_csi_probe(struct platform_device *pdev)
if (ret < 0)
goto err_cleanup;
- ret = v4l2_async_register_subdev(&csidev->sd);
- if (ret < 0)
- goto err_cleanup;
-
/* Reset device */
rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
if (IS_ERR(rstc)) {
@@ -1048,6 +1049,10 @@ static int stm32_csi_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
+ ret = v4l2_async_register_subdev(&csidev->sd);
+ if (ret < 0)
+ goto err_cleanup;
+
dev_info(&pdev->dev,
"Probed CSI with %u lanes\n", csidev->num_lanes);
@@ -1055,7 +1060,6 @@ static int stm32_csi_probe(struct platform_device *pdev)
err_cleanup:
v4l2_async_nf_cleanup(&csidev->notifier);
-err_free_priv:
return ret;
}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index 9b699ee2b1e0..d94c61b8569d 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -388,9 +388,9 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi)
((dcmi->crop.left << 1));
reg_write(dcmi->regs, DCMI_CWSTRT, start);
- dev_dbg(dcmi->dev, "Cropping to %ux%u@%u:%u\n",
- dcmi->crop.width, dcmi->crop.height,
- dcmi->crop.left, dcmi->crop.top);
+ dev_dbg(dcmi->dev, "Cropping to (%d,%d)/%ux%u\n",
+ dcmi->crop.left, dcmi->crop.top,
+ dcmi->crop.width, dcmi->crop.height);
/* Enable crop */
reg_set(dcmi->regs, DCMI_CR, CR_CROP);
@@ -1292,8 +1292,8 @@ static int dcmi_s_selection(struct file *file, void *priv,
/* Crop if request is different than sensor resolution */
dcmi->do_crop = true;
dcmi->crop = r;
- dev_dbg(dcmi->dev, "s_selection: crop %ux%u@(%u,%u) from %ux%u\n",
- r.width, r.height, r.left, r.top,
+ dev_dbg(dcmi->dev, "s_selection: crop (%d,%d)/%ux%u from %ux%u\n",
+ r.left, r.top, r.width, r.height,
pix.width, pix.height);
} else {
/* Disable crop */
@@ -1682,18 +1682,14 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi)
return -ENXIO;
dcmi->num_of_sd_formats = num_fmts;
- dcmi->sd_formats = devm_kcalloc(dcmi->dev,
- num_fmts, sizeof(struct dcmi_format *),
- GFP_KERNEL);
+ dcmi->sd_formats = devm_kmemdup_array(dcmi->dev, sd_fmts, num_fmts,
+ sizeof(*sd_fmts), GFP_KERNEL);
if (!dcmi->sd_formats) {
dev_err(dcmi->dev, "Could not allocate memory\n");
return -ENOMEM;
}
- memcpy(dcmi->sd_formats, sd_fmts,
- num_fmts * sizeof(struct dcmi_format *));
dcmi->sd_format = dcmi->sd_formats[0];
-
return 0;
}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
index 3c742a546441..db76a02a1848 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
@@ -373,8 +373,8 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,
mf->width = s->r.width;
mf->height = s->r.height;
- dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n",
- crop->width, crop->height, crop->left, crop->top);
+ dev_dbg(byteproc->dev, "s_selection: crop (%d,%d)/%ux%u\n",
+ crop->left, crop->top, crop->width, crop->height);
break;
case V4L2_SEL_TGT_COMPOSE:
mf = v4l2_subdev_state_get_format(sd_state, 0);
@@ -386,9 +386,9 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,
mf->width = s->r.width;
mf->height = s->r.height;
- dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n",
- compose->width, compose->height,
- compose->left, compose->top);
+ dev_dbg(byteproc->dev, "s_selection: compose (%d,%d)/%ux%u\n",
+ compose->left, compose->top,
+ compose->width, compose->height);
break;
default:
return -EINVAL;
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index 71acf539e1f3..1b7bae3266c8 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -89,6 +89,8 @@ struct dcmipp_pipeline_config {
const struct dcmipp_ent_link *links;
size_t num_links;
u32 hw_revision;
+ bool has_csi2;
+ bool needs_mclk;
};
/* --------------------------------------------------------------------------
@@ -164,7 +166,9 @@ static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = {
.num_ents = ARRAY_SIZE(stm32mp25_ent_config),
.links = stm32mp25_ent_links,
.num_links = ARRAY_SIZE(stm32mp25_ent_links),
- .hw_revision = DCMIPP_STM32MP25_VERR
+ .hw_revision = DCMIPP_STM32MP25_VERR,
+ .has_csi2 = true,
+ .needs_mclk = true
};
#define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\
@@ -296,7 +300,7 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier,
struct v4l2_async_connection *asd)
{
struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier);
- unsigned int ret;
+ int ret = -EINVAL;
int src_pad, i;
struct dcmipp_ent_device *sink;
struct v4l2_fwnode_endpoint vep = { 0 };
@@ -304,15 +308,9 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier,
enum v4l2_mbus_type supported_types[] = {
V4L2_MBUS_PARALLEL, V4L2_MBUS_BT656, V4L2_MBUS_CSI2_DPHY
};
- int supported_types_nb = ARRAY_SIZE(supported_types);
dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name);
- /* Only MP25 supports CSI input */
- if (!of_device_is_compatible(dcmipp->dev->of_node,
- "st,stm32mp25-dcmipp"))
- supported_types_nb--;
-
/*
* Link this sub-device to DCMIPP, it could be
* a parallel camera sensor or a CSI-2 to parallel bridge
@@ -330,7 +328,12 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier,
}
/* Check for supported MBUS type */
- for (i = 0; i < supported_types_nb; i++) {
+ for (i = 0; i < ARRAY_SIZE(supported_types); i++) {
+ /* Only MP25 supports CSI input */
+ if (supported_types[i] == V4L2_MBUS_CSI2_DPHY &&
+ !dcmipp->pipe_cfg->has_csi2)
+ continue;
+
vep.bus_type = supported_types[i];
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (!ret)
@@ -529,7 +532,7 @@ static int dcmipp_probe(struct platform_device *pdev)
"Unable to get kclk\n");
dcmipp->kclk = kclk;
- if (!of_device_is_compatible(pdev->dev.of_node, "st,stm32mp13-dcmipp")) {
+ if (dcmipp->pipe_cfg->needs_mclk) {
mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(mclk))
return dev_err_probe(&pdev->dev, PTR_ERR(mclk),
diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
new file mode 100644
index 000000000000..4fd521f78425
--- /dev/null
+++ b/drivers/media/platform/synopsys/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/media/platform/synopsys/hdmirx/Kconfig"
diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
new file mode 100644
index 000000000000..3b12c574dd67
--- /dev/null
+++ b/drivers/media/platform/synopsys/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += hdmirx/
diff --git a/drivers/media/platform/synopsys/hdmirx/Kconfig b/drivers/media/platform/synopsys/hdmirx/Kconfig
new file mode 100644
index 000000000000..4321f985f632
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_SYNOPSYS_HDMIRX
+ tristate "Synopsys DesignWare HDMI Receiver driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select CEC_CORE
+ select HDMI
+ help
+ Support for Synopsys HDMI HDMI RX Controller.
+ This driver supports HDMI 2.0 version.
+
+ To compile this driver as a module, choose M here. The module
+ will be called synopsys_hdmirx.
+
+config VIDEO_SYNOPSYS_HDMIRX_LOAD_DEFAULT_EDID
+ bool "Load default EDID"
+ depends on VIDEO_SYNOPSYS_HDMIRX
+ help
+ Preload default EDID (Extended Display Identification Data)
+ branded by Linux Foundation that exposes display modes up
+ to 4k@30Hz, which have best compatibility with HDMI transmitters.
+
+ Enabling this option is recommended for a non-production use-cases.
+ It will make driver usable out-of-the-box.
+
+ For a higher display modes you will need to load customized EDID
+ from userspace using v4l2-ctl tool or by other means.
+
+ Without enabling this option driver will be practically
+ non-functional until EDID will be loaded from userspace.
+ Which is a wanted behavior when using this driver in a
+ commercial product that should utilize own branded EDID.
diff --git a/drivers/media/platform/synopsys/hdmirx/Makefile b/drivers/media/platform/synopsys/hdmirx/Makefile
new file mode 100644
index 000000000000..2fa2d9e25300
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+synopsys-hdmirx-objs := snps_hdmirx.o snps_hdmirx_cec.o
+
+obj-$(CONFIG_VIDEO_SYNOPSYS_HDMIRX) += synopsys-hdmirx.o
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
new file mode 100644
index 000000000000..7af6765532e3
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -0,0 +1,2746 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Author: Shreeya Patel <shreeya.patel@collabora.com>
+ * Author: Dmitry Osipenko <dmitry.osipenko@collabora.com>
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ * Author: Dingxian Wen <shawn.wen@rock-chips.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/workqueue.h>
+
+#include <media/cec.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "snps_hdmirx.h"
+#include "snps_hdmirx_cec.h"
+
+#define EDID_NUM_BLOCKS_MAX 4
+#define EDID_BLOCK_SIZE 128
+#define HDMIRX_PLANE_Y 0
+#define HDMIRX_PLANE_CBCR 1
+#define FILTER_FRAME_CNT 6
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-3)");
+
+enum hdmirx_pix_fmt {
+ HDMIRX_RGB888 = 0,
+ HDMIRX_YUV422 = 1,
+ HDMIRX_YUV444 = 2,
+ HDMIRX_YUV420 = 3,
+};
+
+enum ddr_store_fmt {
+ STORE_RGB888 = 0,
+ STORE_RGBA_ARGB,
+ STORE_YUV420_8BIT,
+ STORE_YUV420_10BIT,
+ STORE_YUV422_8BIT,
+ STORE_YUV422_10BIT,
+ STORE_YUV444_8BIT,
+ STORE_YUV420_16BIT = 8,
+ STORE_YUV422_16BIT = 9,
+};
+
+enum hdmirx_reg_attr {
+ HDMIRX_ATTR_RW = 0,
+ HDMIRX_ATTR_RO = 1,
+ HDMIRX_ATTR_WO = 2,
+ HDMIRX_ATTR_RE = 3,
+};
+
+enum {
+ HDMIRX_RST_A,
+ HDMIRX_RST_P,
+ HDMIRX_RST_REF,
+ HDMIRX_RST_BIU,
+ HDMIRX_NUM_RST,
+};
+
+static const char *const pix_fmt_str[] = {
+ "RGB888",
+ "YUV422",
+ "YUV444",
+ "YUV420",
+};
+
+struct hdmirx_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ u32 buff_addr[VIDEO_MAX_PLANES];
+};
+
+struct hdmirx_stream {
+ struct snps_hdmirx_dev *hdmirx_dev;
+ struct video_device vdev;
+ struct vb2_queue buf_queue;
+ struct list_head buf_head;
+ struct hdmirx_buffer *curr_buf;
+ struct hdmirx_buffer *next_buf;
+ struct v4l2_pix_format_mplane pixm;
+ const struct v4l2_format_info *out_finfo;
+ struct mutex vlock; /* to lock resources associated with video buffer and video device */
+ spinlock_t vbq_lock; /* to lock video buffer queue */
+ bool stopping;
+ wait_queue_head_t wq_stopped;
+ u32 sequence;
+ u32 line_flag_int_cnt;
+ u32 irq_stat;
+};
+
+struct snps_hdmirx_dev {
+ struct device *dev;
+ struct hdmirx_stream stream;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *detect_tx_5v_ctrl;
+ struct v4l2_ctrl *rgb_range;
+ struct v4l2_ctrl *content_type;
+ struct v4l2_dv_timings timings;
+ struct gpio_desc *detect_5v_gpio;
+ struct delayed_work delayed_work_hotplug;
+ struct delayed_work delayed_work_res_change;
+ struct hdmirx_cec *cec;
+ struct mutex stream_lock; /* to lock video stream capture */
+ struct mutex work_lock; /* to lock the critical section of hotplug event */
+ struct reset_control_bulk_data resets[HDMIRX_NUM_RST];
+ struct clk_bulk_data *clks;
+ struct regmap *grf;
+ struct regmap *vo1_grf;
+ struct completion cr_write_done;
+ struct completion timer_base_lock;
+ struct completion avi_pkt_rcv;
+ struct dentry *debugfs_dir;
+ struct v4l2_debugfs_if *infoframes;
+ enum hdmirx_pix_fmt pix_fmt;
+ void __iomem *regs;
+ int hdmi_irq;
+ int dma_irq;
+ int det_irq;
+ bool hpd_trigger_level_high;
+ bool tmds_clk_ratio;
+ bool plugged;
+ int num_clks;
+ u32 edid_blocks_written;
+ u32 cur_fmt_fourcc;
+ u32 color_depth;
+ spinlock_t rst_lock; /* to lock register access */
+ u8 edid[EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE];
+};
+
+static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94;
+
+static const struct v4l2_dv_timings_cap hdmirx_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ .reserved = { 0 },
+ V4L2_INIT_BT_TIMINGS(640, 4096, /* min/max width */
+ 480, 2160, /* min/max height */
+ 20000000, 600000000, /* min/max pixelclock */
+ /* standards */
+ V4L2_DV_BT_STD_CEA861,
+ /* capabilities */
+ V4L2_DV_BT_CAP_PROGRESSIVE |
+ V4L2_DV_BT_CAP_INTERLACED)
+};
+
+static void hdmirx_writel(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val)
+{
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ writel(val, hdmirx_dev->regs + reg);
+}
+
+static u32 hdmirx_readl(struct snps_hdmirx_dev *hdmirx_dev, int reg)
+{
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ return readl(hdmirx_dev->regs + reg);
+}
+
+static void hdmirx_reset_dma(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ reset_control_reset(hdmirx_dev->resets[0].rstc);
+}
+
+static void hdmirx_update_bits(struct snps_hdmirx_dev *hdmirx_dev, int reg,
+ u32 mask, u32 data)
+{
+ u32 val;
+
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ val = readl(hdmirx_dev->regs + reg) & ~mask;
+ val |= (data & mask);
+ writel(val, hdmirx_dev->regs + reg);
+}
+
+static int hdmirx_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ const unsigned int detection_threshold = 7;
+ int val, i, cnt = 0;
+ bool ret;
+
+ for (i = 0; i < 10; i++) {
+ usleep_range(1000, 1100);
+ val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
+ if (val > 0)
+ cnt++;
+ if (cnt >= detection_threshold)
+ break;
+ }
+
+ ret = (cnt >= detection_threshold) ? true : false;
+ v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static bool signal_not_lock(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ u32 mu_status, dma_st10, cmu_st;
+
+ mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
+ dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
+ cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
+
+ if ((mu_status & TMDSVALID_STABLE_ST) &&
+ (dma_st10 & HDMIRX_LOCK) &&
+ (cmu_st & TMDSQPCLK_LOCKED_ST))
+ return false;
+
+ return true;
+}
+
+static void hdmirx_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_bt_timings *bt)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 hact, vact, htotal, vtotal, fps;
+ u32 hfp, hs, hbp, vfp, vs, vbp;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS2);
+ hact = (val >> 16) & 0xffff;
+ vact = val & 0xffff;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS3);
+ htotal = (val >> 16) & 0xffff;
+ vtotal = val & 0xffff;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS4);
+ hs = (val >> 16) & 0xffff;
+ vs = val & 0xffff;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS5);
+ hbp = (val >> 16) & 0xffff;
+ vbp = val & 0xffff;
+
+ if (hdmirx_dev->pix_fmt == HDMIRX_YUV420) {
+ htotal *= 2;
+ hbp *= 2;
+ hs *= 2;
+ }
+
+ hfp = htotal - hact - hs - hbp;
+ vfp = vtotal - vact - vs - vbp;
+
+ fps = div_u64(bt->pixelclock + (htotal * vtotal) / 2, htotal * vtotal);
+ bt->width = hact;
+ bt->height = vact;
+ bt->hfrontporch = hfp;
+ bt->hsync = hs;
+ bt->hbackporch = hbp;
+ bt->vfrontporch = vfp;
+ bt->vsync = vs;
+ bt->vbackporch = vbp;
+
+ v4l2_dbg(1, debug, v4l2_dev, "get timings from dma\n");
+ v4l2_dbg(1, debug, v4l2_dev,
+ "act:%ux%u%s, total:%ux%u, fps:%u, pixclk:%llu\n",
+ bt->width, bt->height, bt->interlaced ? "i" : "p",
+ htotal, vtotal, fps, bt->pixelclock);
+
+ v4l2_dbg(2, debug, v4l2_dev,
+ "hfp:%u, hact:%u, hs:%u, hbp:%u, vfp:%u, vact:%u, vs:%u, vbp:%u\n",
+ bt->hfrontporch, hact, bt->hsync, bt->hbackporch,
+ bt->vfrontporch, vact, bt->vsync, bt->vbackporch);
+
+ if (bt->interlaced == V4L2_DV_INTERLACED) {
+ bt->height *= 2;
+ bt->il_vfrontporch = bt->vfrontporch;
+ bt->il_vsync = bt->vsync + 1;
+ bt->il_vbackporch = bt->vbackporch;
+ }
+}
+
+static bool hdmirx_check_timing_valid(struct v4l2_bt_timings *bt)
+{
+ /*
+ * Sanity-check timing values. Some of the values will be outside
+ * of a valid range till hardware becomes ready to perform capture.
+ */
+ if (bt->width < 100 || bt->width > 5000 ||
+ bt->height < 100 || bt->height > 5000)
+ return false;
+
+ if (!bt->hsync || bt->hsync > 200 ||
+ !bt->vsync || bt->vsync > 100)
+ return false;
+
+ /*
+ * According to the CEA-861, 1280x720p25 Hblank timing is up to 2680,
+ * and all standard video format timings are less than 3000.
+ */
+ if (!bt->hbackporch || bt->hbackporch > 3000 ||
+ !bt->vbackporch || bt->vbackporch > 3000)
+ return false;
+
+ if (!bt->hfrontporch || bt->hfrontporch > 3000 ||
+ !bt->vfrontporch || bt->vfrontporch > 3000)
+ return false;
+
+ return true;
+}
+
+static void hdmirx_toggle_polarity(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ u32 val = hdmirx_readl(hdmirx_dev, DMA_CONFIG6);
+
+ if (!(val & (VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN))) {
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN);
+ hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
+ VPROC_VSYNC_POL_OVR_VALUE |
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_VALUE |
+ VPROC_HSYNC_POL_OVR_EN,
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_EN);
+ return;
+ }
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN, 0);
+
+ hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
+ VPROC_VSYNC_POL_OVR_VALUE |
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_VALUE |
+ VPROC_HSYNC_POL_OVR_EN, 0);
+}
+
+/*
+ * When querying DV timings during preview, if the DMA's timing is stable,
+ * we retrieve the timings directly from the DMA. However, if the current
+ * resolution is negative, obtaining the timing from CTRL may require a
+ * change in the sync polarity, potentially leading to DMA errors.
+ */
+static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_dv_timings *timings)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_bt_timings *bt = &timings->bt;
+ u32 val, tmdsqpclk_freq, pix_clk;
+ unsigned int num_retries = 0;
+ u32 field_type, deframer_st;
+ u64 tmp_data, tmds_clk;
+ bool is_dvi_mode;
+ int ret;
+
+ mutex_lock(&hdmirx_dev->work_lock);
+retry:
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+ timings->type = V4L2_DV_BT_656_1120;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ field_type = (val & HDMIRX_TYPE_MASK) >> 7;
+
+ if (field_type & BIT(0))
+ bt->interlaced = V4L2_DV_INTERLACED;
+ else
+ bt->interlaced = V4L2_DV_PROGRESSIVE;
+
+ deframer_st = hdmirx_readl(hdmirx_dev, DEFRAMER_STATUS);
+ is_dvi_mode = !(deframer_st & OPMODE_STS_MASK);
+
+ tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
+ tmds_clk = tmdsqpclk_freq * 4 * 1000;
+ tmp_data = tmds_clk * 24;
+ do_div(tmp_data, hdmirx_dev->color_depth);
+ pix_clk = tmp_data;
+ bt->pixelclock = pix_clk;
+
+ if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
+ bt->pixelclock *= 2;
+
+ hdmirx_get_timings(hdmirx_dev, bt);
+
+ v4l2_dbg(2, debug, v4l2_dev, "tmds_clk:%llu, pix_clk:%d\n", tmds_clk, pix_clk);
+ v4l2_dbg(1, debug, v4l2_dev, "interlace:%d, fmt:%d, color:%d, mode:%s\n",
+ bt->interlaced, hdmirx_dev->pix_fmt,
+ hdmirx_dev->color_depth,
+ is_dvi_mode ? "dvi" : "hdmi");
+ v4l2_dbg(2, debug, v4l2_dev, "deframer_st:%#x\n", deframer_st);
+
+ /*
+ * Timing will be invalid until it's latched by HW or if signal's
+ * polarity doesn't match.
+ */
+ if (!hdmirx_check_timing_valid(bt)) {
+ if (num_retries++ < 20) {
+ if (num_retries == 10)
+ hdmirx_toggle_polarity(hdmirx_dev);
+
+ usleep_range(10 * 1000, 10 * 1100);
+ goto retry;
+ }
+
+ ret = -ERANGE;
+ } else {
+ ret = 0;
+ }
+
+ mutex_unlock(&hdmirx_dev->work_lock);
+
+ return ret;
+}
+
+static bool port_no_link(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ return !tx_5v_power_present(hdmirx_dev);
+}
+
+static int hdmirx_query_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ if (port_no_link(hdmirx_dev)) {
+ v4l2_err(v4l2_dev, "%s: port has no link\n", __func__);
+ return -ENOLINK;
+ }
+
+ if (signal_not_lock(hdmirx_dev)) {
+ v4l2_err(v4l2_dev, "%s: signal is not locked\n", __func__);
+ return -ENOLCK;
+ }
+
+ ret = hdmirx_get_detected_timings(hdmirx_dev, timings);
+ if (ret)
+ return ret;
+
+ if (debug)
+ v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
+ "query_dv_timings: ", timings, false);
+
+ if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: timings out of range\n", __func__);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: %sable, hpd_trigger_level_high:%d\n",
+ __func__, en ? "en" : "dis", hdmirx_dev->hpd_trigger_level_high);
+
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, HPDLOW, en ? 0 : HPDLOW);
+ hdmirx_writel(hdmirx_dev, CORE_CONFIG,
+ hdmirx_dev->hpd_trigger_level_high ? en : !en);
+
+ /* 100ms delay as per HDMI spec */
+ if (!en)
+ msleep(100);
+}
+
+static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev,
+ u8 *edid, unsigned int num_blocks)
+{
+ static u8 data[EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE];
+ unsigned int edid_len = num_blocks * EDID_BLOCK_SIZE;
+ unsigned int i;
+
+ cec_s_phys_addr_from_edid(hdmirx_dev->cec->adap,
+ (const struct edid *)edid);
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK |
+ EDID_WRITE_EN_MASK |
+ EDID_SLAVE_ADDR_MASK,
+ EDID_READ_EN(0) |
+ EDID_WRITE_EN(1) |
+ EDID_SLAVE_ADDR(0x50));
+ for (i = 0; i < edid_len; i++)
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG10, edid[i]);
+
+ /* read out for debug */
+ if (debug >= 2) {
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK |
+ EDID_WRITE_EN_MASK,
+ EDID_READ_EN(1) |
+ EDID_WRITE_EN(0));
+
+ for (i = 0; i < edid_len; i++)
+ data[i] = hdmirx_readl(hdmirx_dev, DMA_STATUS14);
+
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, data,
+ edid_len, false);
+ }
+
+ /*
+ * Must set EDID_READ_EN & EDID_WRITE_EN bit to 0,
+ * when the read/write edid operation is completed. Otherwise, it
+ * will affect the reading and writing of other registers.
+ */
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK | EDID_WRITE_EN_MASK,
+ EDID_READ_EN(0) | EDID_WRITE_EN(0));
+}
+
+static void hdmirx_write_edid(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_edid *edid)
+{
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+ memset(hdmirx_dev->edid, 0, sizeof(hdmirx_dev->edid));
+
+ hdmirx_write_edid_data(hdmirx_dev, edid->edid, edid->blocks);
+
+ hdmirx_dev->edid_blocks_written = edid->blocks;
+ memcpy(hdmirx_dev->edid, edid->edid, edid->blocks * EDID_BLOCK_SIZE);
+}
+
+/*
+ * Before clearing interrupt, we need to read the interrupt status.
+ */
+static inline void hdmirx_clear_interrupt(struct snps_hdmirx_dev *hdmirx_dev,
+ u32 reg, u32 val)
+{
+ /* (interrupt status register) = (interrupt clear register) - 0x8 */
+ hdmirx_readl(hdmirx_dev, reg - 0x8);
+ hdmirx_writel(hdmirx_dev, reg, val);
+}
+
+static void hdmirx_interrupts_setup(struct snps_hdmirx_dev *hdmirx_dev, bool en)
+{
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: %sable\n",
+ __func__, en ? "en" : "dis");
+
+ disable_irq(hdmirx_dev->hdmi_irq);
+
+ /* Note: In DVI mode, it needs to be written twice to take effect. */
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+
+ if (en) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG,
+ TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG);
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ TMDSVALID_STABLE_CHG, TMDSVALID_STABLE_CHG);
+ hdmirx_update_bits(hdmirx_dev, AVPUNIT_0_INT_MASK_N,
+ CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ,
+ CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ);
+ } else {
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
+ }
+
+ enable_irq(hdmirx_dev->hdmi_irq);
+}
+
+static void hdmirx_plugout(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ if (!hdmirx_dev->plugged)
+ return;
+
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0);
+ hdmirx_interrupts_setup(hdmirx_dev, false);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ hdmirx_reset_dma(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE | PHY_RESET |
+ PHY_PDDQ, HDMI_DISABLE);
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x0);
+ cancel_delayed_work(&hdmirx_dev->delayed_work_res_change);
+
+ /* will be NULL on driver removal */
+ if (hdmirx_dev->rgb_range)
+ v4l2_ctrl_s_ctrl(hdmirx_dev->rgb_range, V4L2_DV_RGB_RANGE_AUTO);
+
+ if (hdmirx_dev->content_type)
+ v4l2_ctrl_s_ctrl(hdmirx_dev->content_type,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+
+ hdmirx_dev->plugged = false;
+}
+
+static int hdmirx_set_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ u16 phys_addr;
+ int err;
+
+ if (edid->pad)
+ return -EINVAL;
+
+ if (edid->start_block)
+ return -EINVAL;
+
+ if (edid->blocks > EDID_NUM_BLOCKS_MAX) {
+ edid->blocks = EDID_NUM_BLOCKS_MAX;
+ return -E2BIG;
+ }
+
+ if (edid->blocks) {
+ phys_addr = cec_get_edid_phys_addr(edid->edid,
+ edid->blocks * EDID_BLOCK_SIZE,
+ NULL);
+
+ err = v4l2_phys_addr_validate(phys_addr, &phys_addr, NULL);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Touching HW registers in parallel with plugin/out handlers
+ * will bring hardware into a bad state.
+ */
+ mutex_lock(&hdmirx_dev->work_lock);
+
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+
+ if (edid->blocks) {
+ hdmirx_write_edid(hdmirx_dev, edid);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+ } else {
+ cec_phys_addr_invalidate(hdmirx_dev->cec->adap);
+ hdmirx_dev->edid_blocks_written = 0;
+ }
+
+ mutex_unlock(&hdmirx_dev->work_lock);
+
+ return 0;
+}
+
+static int hdmirx_get_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->pad)
+ return -EINVAL;
+
+ if (!edid->start_block && !edid->blocks) {
+ edid->blocks = hdmirx_dev->edid_blocks_written;
+ return 0;
+ }
+
+ if (!hdmirx_dev->edid_blocks_written)
+ return -ENODATA;
+
+ if (edid->start_block >= hdmirx_dev->edid_blocks_written || !edid->blocks)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > hdmirx_dev->edid_blocks_written)
+ edid->blocks = hdmirx_dev->edid_blocks_written - edid->start_block;
+
+ memcpy(edid->edid, hdmirx_dev->edid, edid->blocks * EDID_BLOCK_SIZE);
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: read EDID:\n", __func__);
+ if (debug > 0)
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
+ edid->edid, edid->blocks * EDID_BLOCK_SIZE, false);
+
+ return 0;
+}
+
+static int hdmirx_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ parm->parm.capture.timeperframe = v4l2_calc_timeperframe(&hdmirx_dev->timings);
+
+ return 0;
+}
+
+static int hdmirx_dv_timings_cap(struct file *file, void *fh,
+ struct v4l2_dv_timings_cap *cap)
+{
+ *cap = hdmirx_timings_cap;
+ return 0;
+}
+
+static int hdmirx_enum_dv_timings(struct file *file, void *_fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ return v4l2_enum_dv_timings_cap(timings, &hdmirx_timings_cap, NULL, NULL);
+}
+
+static void hdmirx_scdc_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, I2C_SLAVE_CONFIG1,
+ I2C_SDA_OUT_HOLD_VALUE_QST_MASK |
+ I2C_SDA_IN_HOLD_VALUE_QST_MASK,
+ I2C_SDA_OUT_HOLD_VALUE_QST(0x80) |
+ I2C_SDA_IN_HOLD_VALUE_QST(0x15));
+ hdmirx_update_bits(hdmirx_dev, SCDC_REGBANK_CONFIG0,
+ SCDC_SINKVERSION_QST_MASK,
+ SCDC_SINKVERSION_QST(1));
+}
+
+static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
+ u32 bit_mask, u32 expect_val, bool is_grf,
+ u32 ms)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 i, val;
+
+ for (i = 0; i < ms; i++) {
+ if (is_grf)
+ regmap_read(hdmirx_dev->grf, reg, &val);
+ else
+ val = hdmirx_readl(hdmirx_dev, reg);
+
+ if ((val & bit_mask) == expect_val) {
+ v4l2_dbg(2, debug, v4l2_dev,
+ "%s: i:%d, time: %dms\n", __func__, i, ms);
+ break;
+ }
+ usleep_range(1000, 1010);
+ }
+
+ if (i == ms)
+ return -1;
+
+ return 0;
+}
+
+static int hdmirx_phy_register_write(struct snps_hdmirx_dev *hdmirx_dev,
+ u32 phy_reg, u32 val)
+{
+ struct device *dev = hdmirx_dev->dev;
+
+ reinit_completion(&hdmirx_dev->cr_write_done);
+ /* clear irq status */
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ /* en irq */
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_WRITE_DONE, PHYCREG_CR_WRITE_DONE);
+ /* write phy reg addr */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG1, phy_reg);
+ /* write phy reg val */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG2, val);
+ /* config write enable */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONTROL, PHYCREG_CR_PARA_WRITE_P);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->cr_write_done,
+ msecs_to_jiffies(20))) {
+ dev_err(dev, "%s wait cr write done failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void hdmirx_tmds_clk_ratio_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS1);
+ v4l2_dbg(3, debug, v4l2_dev, "%s: scdc_regbank_st:%#x\n", __func__, val);
+ hdmirx_dev->tmds_clk_ratio = (val & SCDC_TMDSBITCLKRATIO) > 0;
+
+ if (hdmirx_dev->tmds_clk_ratio) {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX greater than 3.4Gbps\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
+ TMDS_CLOCK_RATIO, TMDS_CLOCK_RATIO);
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX less than 3.4Gbps\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
+ TMDS_CLOCK_RATIO, 0);
+ }
+}
+
+static void hdmirx_phy_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct device *dev = hdmirx_dev->dev;
+
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, SCDC_INT_MASK_N, SCDCTMDSCCFG_CHG,
+ SCDCTMDSCCFG_CHG);
+ /* cr_para_clk 24M */
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, REFFREQ_SEL_MASK, REFFREQ_SEL(0));
+ /* rx data width 40bit valid */
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, RXDATA_WIDTH, RXDATA_WIDTH);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, PHY_RESET);
+ usleep_range(100, 110);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, 0);
+ usleep_range(100, 110);
+ /* select cr para interface */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x3);
+
+ if (wait_reg_bit_status(hdmirx_dev, SYS_GRF_SOC_STATUS1,
+ HDMIRXPHY_SRAM_INIT_DONE,
+ HDMIRXPHY_SRAM_INIT_DONE, true, 10))
+ dev_err(dev, "%s: phy SRAM init failed\n", __func__);
+
+ regmap_write(hdmirx_dev->grf, SYS_GRF_SOC_CON1,
+ (HDMIRXPHY_SRAM_EXT_LD_DONE << 16) |
+ HDMIRXPHY_SRAM_EXT_LD_DONE);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 1);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG,
+ CDR_SETTING_BOUNDARY_3_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG,
+ CDR_SETTING_BOUNDARY_4_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG,
+ CDR_SETTING_BOUNDARY_5_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG,
+ CDR_SETTING_BOUNDARY_6_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG,
+ CDR_SETTING_BOUNDARY_7_DEFAULT);
+
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_PDDQ, 0);
+ if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, PDDQ_ACK, 0, false, 10))
+ dev_err(dev, "%s: wait pddq ack failed\n", __func__);
+
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE, 0);
+ if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, HDMI_DISABLE_ACK, 0,
+ false, 50))
+ dev_err(dev, "%s: wait hdmi disable ack failed\n", __func__);
+
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+}
+
+static void hdmirx_controller_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ const unsigned long iref_clk_freq_hz = 428571429;
+ struct device *dev = hdmirx_dev->dev;
+
+ reinit_completion(&hdmirx_dev->timer_base_lock);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ /* en irq */
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TIMER_BASE_LOCKED_IRQ, TIMER_BASE_LOCKED_IRQ);
+ /* write irefclk freq */
+ hdmirx_writel(hdmirx_dev, GLOBAL_TIMER_REF_BASE, iref_clk_freq_hz);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->timer_base_lock,
+ msecs_to_jiffies(20)))
+ dev_err(dev, "%s wait timer base lock failed\n", __func__);
+
+ hdmirx_update_bits(hdmirx_dev, CMU_CONFIG0,
+ TMDSQPCLK_STABLE_FREQ_MARGIN_MASK |
+ AUDCLK_STABLE_FREQ_MARGIN_MASK,
+ TMDSQPCLK_STABLE_FREQ_MARGIN(2) |
+ AUDCLK_STABLE_FREQ_MARGIN(1));
+ hdmirx_update_bits(hdmirx_dev, DESCRAND_EN_CONTROL,
+ SCRAMB_EN_SEL_QST_MASK, SCRAMB_EN_SEL_QST(1));
+ hdmirx_update_bits(hdmirx_dev, CED_CONFIG,
+ CED_VIDDATACHECKEN_QST |
+ CED_DATAISCHECKEN_QST |
+ CED_GBCHECKEN_QST |
+ CED_CTRLCHECKEN_QST |
+ CED_CHLOCKMAXER_QST_MASK,
+ CED_VIDDATACHECKEN_QST |
+ CED_GBCHECKEN_QST |
+ CED_CTRLCHECKEN_QST |
+ CED_CHLOCKMAXER_QST(0x10));
+ hdmirx_update_bits(hdmirx_dev, DEFRAMER_CONFIG0,
+ VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST_MASK,
+ VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST(0x3));
+}
+
+static void hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val, color_depth_reg;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
+
+ switch (color_depth_reg) {
+ case 0x4:
+ hdmirx_dev->color_depth = 24;
+ break;
+ case 0x5:
+ hdmirx_dev->color_depth = 30;
+ break;
+ case 0x6:
+ hdmirx_dev->color_depth = 36;
+ break;
+ case 0x7:
+ hdmirx_dev->color_depth = 48;
+ break;
+ default:
+ hdmirx_dev->color_depth = 24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
+ __func__, hdmirx_dev->color_depth, color_depth_reg);
+}
+
+static void hdmirx_get_pix_fmt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ hdmirx_dev->pix_fmt = val & HDMIRX_FORMAT_MASK;
+
+ switch (hdmirx_dev->pix_fmt) {
+ case HDMIRX_RGB888:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ break;
+ case HDMIRX_YUV422:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV16;
+ break;
+ case HDMIRX_YUV444:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV24;
+ break;
+ case HDMIRX_YUV420:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV12;
+ break;
+ default:
+ v4l2_err(v4l2_dev,
+ "%s: err pix_fmt: %d, set RGB888 as default\n",
+ __func__, hdmirx_dev->pix_fmt);
+ hdmirx_dev->pix_fmt = HDMIRX_RGB888;
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s\n", __func__,
+ pix_fmt_str[hdmirx_dev->pix_fmt]);
+}
+
+static void hdmirx_read_avi_infoframe(struct snps_hdmirx_dev *hdmirx_dev,
+ u8 *aviif)
+{
+ unsigned int i, b, itr = 0;
+ u32 val;
+
+ aviif[itr++] = HDMI_INFOFRAME_TYPE_AVI;
+ val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PH2_1);
+ aviif[itr++] = val & 0xff;
+ aviif[itr++] = (val >> 8) & 0xff;
+
+ for (i = 0; i < 7; i++) {
+ val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB3_0 + 4 * i);
+
+ for (b = 0; b < 4; b++)
+ aviif[itr++] = (val >> (8 * b)) & 0xff;
+ }
+}
+
+static void hdmirx_get_avi_infoframe(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ union hdmi_infoframe frame = {};
+ u8 aviif[3 + 7 * 4];
+ int err;
+
+ hdmirx_read_avi_infoframe(hdmirx_dev, aviif);
+
+ err = hdmi_infoframe_unpack(&frame, aviif, sizeof(aviif));
+ if (err) {
+ v4l2_err(v4l2_dev, "failed to unpack AVI infoframe\n");
+ return;
+ }
+
+ v4l2_ctrl_s_ctrl(hdmirx_dev->rgb_range, frame.avi.quantization_range);
+
+ if (frame.avi.itc)
+ v4l2_ctrl_s_ctrl(hdmirx_dev->content_type,
+ frame.avi.content_type);
+ else
+ v4l2_ctrl_s_ctrl(hdmirx_dev->content_type,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+}
+
+static ssize_t
+hdmirx_debugfs_if_read(u32 type, void *priv, struct file *filp,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = priv;
+ u8 aviif[V4L2_DEBUGFS_IF_MAX_LEN] = {};
+ int len;
+
+ if (type != V4L2_DEBUGFS_IF_AVI)
+ return 0;
+
+ hdmirx_read_avi_infoframe(hdmirx_dev, aviif);
+
+ len = aviif[2] + 4;
+ if (len > V4L2_DEBUGFS_IF_MAX_LEN)
+ len = -ENOENT;
+ else
+ len = simple_read_from_buffer(ubuf, count, ppos, aviif, len);
+
+ return len < 0 ? 0 : len;
+}
+
+static void hdmirx_format_change(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ static const struct v4l2_event ev_src_chg = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
+ hdmirx_get_pix_fmt(hdmirx_dev);
+ hdmirx_get_colordepth(hdmirx_dev);
+ hdmirx_get_avi_infoframe(hdmirx_dev);
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: queue res_chg_event\n", __func__);
+ v4l2_event_queue(&stream->vdev, &ev_src_chg);
+}
+
+static void hdmirx_set_ddr_store_fmt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ enum ddr_store_fmt store_fmt;
+ u32 dma_cfg1;
+
+ switch (hdmirx_dev->pix_fmt) {
+ case HDMIRX_RGB888:
+ store_fmt = STORE_RGB888;
+ break;
+ case HDMIRX_YUV444:
+ store_fmt = STORE_YUV444_8BIT;
+ break;
+ case HDMIRX_YUV422:
+ store_fmt = STORE_YUV422_8BIT;
+ break;
+ case HDMIRX_YUV420:
+ store_fmt = STORE_YUV420_8BIT;
+ break;
+ default:
+ store_fmt = STORE_RGB888;
+ break;
+ }
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
+ DDR_STORE_FORMAT_MASK, DDR_STORE_FORMAT(store_fmt));
+ dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
+ __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
+}
+
+static void hdmirx_dma_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_set_ddr_store_fmt(hdmirx_dev);
+
+ /* Note: uv_swap, rb can not swap, doc err */
+ if (hdmirx_dev->cur_fmt_fourcc != V4L2_PIX_FMT_NV16)
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, RB_SWAP_EN);
+ else
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, 0);
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
+ LOCK_FRAME_NUM_MASK,
+ LOCK_FRAME_NUM(2));
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
+ UV_WID_MASK | Y_WID_MASK | ABANDON_EN,
+ UV_WID(1) | Y_WID(2) | ABANDON_EN);
+}
+
+static void hdmirx_submodule_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ /* Note: if not config HDCP2_CONFIG, there will be some errors; */
+ hdmirx_update_bits(hdmirx_dev, HDCP2_CONFIG,
+ HDCP2_SWITCH_OVR_VALUE |
+ HDCP2_SWITCH_OVR_EN,
+ HDCP2_SWITCH_OVR_EN);
+ hdmirx_scdc_init(hdmirx_dev);
+ hdmirx_controller_init(hdmirx_dev);
+}
+
+static int hdmirx_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = 0;
+ strscpy(input->name, "HDMI IN", sizeof(input->name));
+ input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+
+ return 0;
+}
+
+static int hdmirx_get_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int hdmirx_set_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i)
+ return -EINVAL;
+ return 0;
+}
+
+static void hdmirx_set_fmt(struct hdmirx_stream *stream,
+ struct v4l2_pix_format_mplane *pixm, bool try)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_bt_timings *bt = &hdmirx_dev->timings.bt;
+ const struct v4l2_format_info *finfo;
+ unsigned int imagesize = 0;
+ unsigned int i;
+
+ memset(&pixm->plane_fmt[0], 0, sizeof(struct v4l2_plane_pix_format));
+ finfo = v4l2_format_info(pixm->pixelformat);
+ if (!finfo) {
+ finfo = v4l2_format_info(V4L2_PIX_FMT_BGR24);
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: set_fmt:%#x not supported, use def_fmt:%x\n",
+ __func__, pixm->pixelformat, finfo->format);
+ }
+
+ if (!bt->width || !bt->height)
+ v4l2_dbg(1, debug, v4l2_dev, "%s: invalid resolution:%#xx%#x\n",
+ __func__, bt->width, bt->height);
+
+ pixm->pixelformat = finfo->format;
+ pixm->width = bt->width;
+ pixm->height = bt->height;
+ pixm->num_planes = finfo->mem_planes;
+ pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
+ pixm->colorspace = V4L2_COLORSPACE_SRGB;
+ pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+
+ if (bt->interlaced == V4L2_DV_INTERLACED)
+ pixm->field = V4L2_FIELD_INTERLACED_TB;
+ else
+ pixm->field = V4L2_FIELD_NONE;
+
+ memset(pixm->reserved, 0, sizeof(pixm->reserved));
+
+ v4l2_fill_pixfmt_mp(pixm, finfo->format, pixm->width, pixm->height);
+
+ for (i = 0; i < finfo->comp_planes; i++) {
+ struct v4l2_plane_pix_format *plane_fmt;
+ int width, height, bpl, size, bpp = 0;
+ const unsigned int hw_align = 64;
+
+ if (!i) {
+ width = pixm->width;
+ height = pixm->height;
+ } else {
+ width = pixm->width / finfo->hdiv;
+ height = pixm->height / finfo->vdiv;
+ }
+
+ switch (finfo->format) {
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_BGR24:
+ bpp = finfo->bpp[i];
+ break;
+ default:
+ v4l2_dbg(1, debug, v4l2_dev,
+ "fourcc: %#x is not supported\n",
+ finfo->format);
+ break;
+ }
+
+ bpl = ALIGN(width * bpp, hw_align);
+ size = bpl * height;
+ imagesize += size;
+
+ if (finfo->mem_planes > i) {
+ /* Set bpl and size for each mplane */
+ plane_fmt = pixm->plane_fmt + i;
+ plane_fmt->bytesperline = bpl;
+ plane_fmt->sizeimage = size;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev,
+ "C-Plane %u size: %d, Total imagesize: %d\n",
+ i, size, imagesize);
+ }
+
+ /* Convert to non-MPLANE format as we want to unify non-MPLANE and MPLANE */
+ if (finfo->mem_planes == 1)
+ pixm->plane_fmt[0].sizeimage = imagesize;
+
+ if (!try) {
+ stream->out_finfo = finfo;
+ stream->pixm = *pixm;
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: req(%d, %d), out(%d, %d), fmt:%#x\n", __func__,
+ pixm->width, pixm->height, stream->pixm.width,
+ stream->pixm.height, finfo->format);
+ }
+}
+
+static int hdmirx_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ if (f->index >= 1)
+ return -EINVAL;
+
+ f->pixelformat = hdmirx_dev->cur_fmt_fourcc;
+
+ return 0;
+}
+
+static int hdmirx_s_fmt_vid_cap_mplane(struct file *file,
+ void *priv, struct v4l2_format *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (vb2_is_busy(&stream->buf_queue)) {
+ v4l2_err(v4l2_dev, "%s: queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ hdmirx_set_fmt(stream, &f->fmt.pix_mp, false);
+
+ return 0;
+}
+
+static int hdmirx_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_pix_format_mplane pixm = {};
+
+ pixm.pixelformat = hdmirx_dev->cur_fmt_fourcc;
+ hdmirx_set_fmt(stream, &pixm, true);
+ f->fmt.pix_mp = pixm;
+
+ return 0;
+}
+
+static int hdmirx_g_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 dma_cfg1;
+
+ *timings = hdmirx_dev->timings;
+ dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
+ __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
+
+ return 0;
+}
+
+static int hdmirx_s_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (!timings)
+ return -EINVAL;
+
+ if (debug)
+ v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
+ "s_dv_timings: ", timings, false);
+
+ if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: timings out of range\n", __func__);
+ return -ERANGE;
+ }
+
+ /* Check if the timings are part of the CEA-861 timings. */
+ v4l2_find_dv_timings_cap(timings, &hdmirx_timings_cap, 0, NULL, NULL);
+
+ if (v4l2_match_dv_timings(&hdmirx_dev->timings, timings, 0, false)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: no change\n", __func__);
+ return 0;
+ }
+
+ /*
+ * Changing the timings implies a format change, which is not allowed
+ * while buffers for use with streaming have already been allocated.
+ */
+ if (vb2_is_busy(&stream->buf_queue))
+ return -EBUSY;
+
+ hdmirx_dev->timings = *timings;
+ /* Update the internal format */
+ hdmirx_set_fmt(stream, &stream->pixm, false);
+
+ return 0;
+}
+
+static int hdmirx_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct device *dev = stream->hdmirx_dev->dev;
+
+ strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
+ strscpy(cap->card, dev->driver->name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int hdmirx_queue_setup(struct vb2_queue *queue,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_ctxs[])
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ const struct v4l2_format_info *out_finfo;
+ u32 i;
+
+ pixm = &stream->pixm;
+ out_finfo = stream->out_finfo;
+
+ if (!out_finfo) {
+ v4l2_err(v4l2_dev, "%s: out_fmt not set\n", __func__);
+ return -EINVAL;
+ }
+
+ if (*num_planes) {
+ if (*num_planes != pixm->num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < *num_planes; i++)
+ if (sizes[i] < pixm->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ return 0;
+ }
+
+ *num_planes = out_finfo->mem_planes;
+
+ for (i = 0; i < out_finfo->mem_planes; i++)
+ sizes[i] = pixm->plane_fmt[i].sizeimage;
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: count %d, size %d\n",
+ v4l2_type_names[queue->type], *num_buffers, sizes[0]);
+
+ return 0;
+}
+
+/*
+ * The vb2_buffer are stored in hdmirx_buffer, in order to unify
+ * mplane buffer and none-mplane buffer.
+ */
+static void hdmirx_buf_queue(struct vb2_buffer *vb)
+{
+ const struct v4l2_pix_format_mplane *pixm;
+ const struct v4l2_format_info *out_finfo;
+ struct hdmirx_buffer *hdmirx_buf;
+ struct vb2_v4l2_buffer *vbuf;
+ struct hdmirx_stream *stream;
+ struct vb2_queue *queue;
+ unsigned long flags;
+ unsigned int i;
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ hdmirx_buf = container_of(vbuf, struct hdmirx_buffer, vb);
+ queue = vb->vb2_queue;
+ stream = vb2_get_drv_priv(queue);
+ pixm = &stream->pixm;
+ out_finfo = stream->out_finfo;
+
+ memset(hdmirx_buf->buff_addr, 0, sizeof(hdmirx_buf->buff_addr));
+
+ /*
+ * If mplanes > 1, every c-plane has its own m-plane,
+ * otherwise, multiple c-planes are in the same m-plane
+ */
+ for (i = 0; i < out_finfo->mem_planes; i++)
+ hdmirx_buf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ if (out_finfo->mem_planes == 1) {
+ if (out_finfo->comp_planes == 1) {
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_CBCR] =
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_Y];
+ } else {
+ for (i = 0; i < out_finfo->comp_planes - 1; i++)
+ hdmirx_buf->buff_addr[i + 1] =
+ hdmirx_buf->buff_addr[i] +
+ pixm->plane_fmt[i].bytesperline *
+ pixm->height;
+ }
+ }
+
+ spin_lock_irqsave(&stream->vbq_lock, flags);
+ list_add_tail(&hdmirx_buf->queue, &stream->buf_head);
+ spin_unlock_irqrestore(&stream->vbq_lock, flags);
+}
+
+static void return_all_buffers(struct hdmirx_stream *stream,
+ enum vb2_buffer_state state)
+{
+ struct hdmirx_buffer *buf, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stream->vbq_lock, flags);
+ if (stream->curr_buf)
+ list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
+ if (stream->next_buf && stream->next_buf != stream->curr_buf)
+ list_add_tail(&stream->next_buf->queue, &stream->buf_head);
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+
+ list_for_each_entry_safe(buf, tmp, &stream->buf_head, queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, flags);
+}
+
+static void hdmirx_stop_streaming(struct vb2_queue *queue)
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, v4l2_dev, "stream start stopping\n");
+ mutex_lock(&hdmirx_dev->stream_lock);
+ WRITE_ONCE(stream->stopping, true);
+
+ /* wait last irq to return the buffer */
+ ret = wait_event_timeout(stream->wq_stopped, !stream->stopping,
+ msecs_to_jiffies(500));
+ if (!ret)
+ v4l2_dbg(1, debug, v4l2_dev, "%s: timeout waiting last irq\n",
+ __func__);
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ return_all_buffers(stream, VB2_BUF_STATE_ERROR);
+ mutex_unlock(&hdmirx_dev->stream_lock);
+ v4l2_dbg(1, debug, v4l2_dev, "stream stopping finished\n");
+}
+
+static int hdmirx_start_streaming(struct vb2_queue *queue, unsigned int count)
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ unsigned long lock_flags = 0;
+ int line_flag;
+
+ mutex_lock(&hdmirx_dev->stream_lock);
+ stream->sequence = 0;
+ stream->line_flag_int_cnt = 0;
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+ stream->irq_stat = 0;
+
+ WRITE_ONCE(stream->stopping, false);
+
+ spin_lock_irqsave(&stream->vbq_lock, lock_flags);
+ if (!stream->curr_buf) {
+ if (!list_empty(&stream->buf_head)) {
+ stream->curr_buf = list_first_entry(&stream->buf_head,
+ struct hdmirx_buffer,
+ queue);
+ list_del(&stream->curr_buf->queue);
+ } else {
+ stream->curr_buf = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
+
+ if (!stream->curr_buf) {
+ mutex_unlock(&hdmirx_dev->stream_lock);
+ return -ENOMEM;
+ }
+
+ v4l2_dbg(2, debug, v4l2_dev,
+ "%s: start_stream cur_buf y_addr:%#x, uv_addr:%#x\n",
+ __func__, stream->curr_buf->buff_addr[HDMIRX_PLANE_Y],
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_Y]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+
+ if (bt->height) {
+ if (bt->interlaced == V4L2_DV_INTERLACED)
+ line_flag = bt->height / 4;
+ else
+ line_flag = bt->height / 2;
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
+ LINE_FLAG_NUM_MASK,
+ LINE_FLAG_NUM(line_flag));
+ } else {
+ v4l2_err(v4l2_dev, "invalid BT timing height=%d\n", bt->height);
+ }
+
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, CED_DYN_CONTROL, 0x1);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, HDMIRX_DMA_EN);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: enable dma", __func__);
+ mutex_unlock(&hdmirx_dev->stream_lock);
+
+ return 0;
+}
+
+/* vb2 queue */
+static const struct vb2_ops hdmirx_vb2_ops = {
+ .queue_setup = hdmirx_queue_setup,
+ .buf_queue = hdmirx_buf_queue,
+ .stop_streaming = hdmirx_stop_streaming,
+ .start_streaming = hdmirx_start_streaming,
+};
+
+static int hdmirx_init_vb2_queue(struct vb2_queue *q,
+ struct hdmirx_stream *stream,
+ enum v4l2_buf_type buf_type)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ q->type = buf_type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stream;
+ q->ops = &hdmirx_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct hdmirx_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &stream->vlock;
+ q->dev = hdmirx_dev->dev;
+ q->min_queued_buffers = 1;
+
+ return vb2_queue_init(q);
+}
+
+/* video device */
+static const struct v4l2_ioctl_ops hdmirx_v4l2_ioctl_ops = {
+ .vidioc_querycap = hdmirx_querycap,
+ .vidioc_try_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = hdmirx_s_fmt_vid_cap_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_cap = hdmirx_enum_fmt_vid_cap_mplane,
+
+ .vidioc_s_dv_timings = hdmirx_s_dv_timings,
+ .vidioc_g_dv_timings = hdmirx_g_dv_timings,
+ .vidioc_enum_dv_timings = hdmirx_enum_dv_timings,
+ .vidioc_query_dv_timings = hdmirx_query_dv_timings,
+ .vidioc_dv_timings_cap = hdmirx_dv_timings_cap,
+ .vidioc_enum_input = hdmirx_enum_input,
+ .vidioc_g_input = hdmirx_get_input,
+ .vidioc_s_input = hdmirx_set_input,
+ .vidioc_g_edid = hdmirx_get_edid,
+ .vidioc_s_edid = hdmirx_set_edid,
+ .vidioc_g_parm = hdmirx_g_parm,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = hdmirx_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations hdmirx_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static int hdmirx_register_stream_vdev(struct hdmirx_stream *stream)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct video_device *vdev = &stream->vdev;
+ int ret;
+
+ strscpy(vdev->name, "stream_hdmirx", sizeof(vdev->name));
+ INIT_LIST_HEAD(&stream->buf_head);
+ spin_lock_init(&stream->vbq_lock);
+ mutex_init(&stream->vlock);
+ init_waitqueue_head(&stream->wq_stopped);
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+
+ vdev->ioctl_ops = &hdmirx_v4l2_ioctl_ops;
+ vdev->release = video_device_release_empty;
+ vdev->fops = &hdmirx_fops;
+ vdev->minor = -1;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->lock = &stream->vlock;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+
+ video_set_drvdata(vdev, stream);
+
+ hdmirx_init_vb2_queue(&stream->buf_queue, stream,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ vdev->queue = &stream->buf_queue;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "video_register_device failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ hdmirx_reset_dma(hdmirx_dev);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_res_change,
+ msecs_to_jiffies(50));
+}
+
+static void avpunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (status & (CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ)) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: avp0_st:%#x\n",
+ __func__, status);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_FORCE, 0x0);
+}
+
+static void avpunit_1_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (status & DEFRAMER_VSYNC_THR_REACHED_IRQ) {
+ v4l2_dbg(2, debug, v4l2_dev,
+ "Vertical Sync threshold reached interrupt %#x", status);
+ hdmirx_update_bits(hdmirx_dev, AVPUNIT_1_INT_MASK_N,
+ DEFRAMER_VSYNC_THR_REACHED_MASK_N, 0);
+ *handled = true;
+ }
+}
+
+static void mainunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "mu0_st:%#x\n", status);
+ if (status & TIMER_BASE_LOCKED_IRQ) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TIMER_BASE_LOCKED_IRQ, 0);
+ complete(&hdmirx_dev->timer_base_lock);
+ *handled = true;
+ }
+
+ if (status & TMDSQPCLK_OFF_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_OFF_CHG\n", __func__);
+ *handled = true;
+ }
+
+ if (status & TMDSQPCLK_LOCKED_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_LOCKED_CHG\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_FORCE, 0x0);
+}
+
+static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "mu2_st:%#x\n", status);
+ if (status & PHYCREG_CR_WRITE_DONE) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_WRITE_DONE, 0);
+ complete(&hdmirx_dev->cr_write_done);
+ *handled = true;
+ }
+
+ if (status & TMDSVALID_STABLE_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSVALID_STABLE_CHG\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_FORCE, 0x0);
+}
+
+static void pkt_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: pk2_st:%#x\n", __func__, status);
+ if (status & PKTDEC_AVIIF_RCV_IRQ) {
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, 0);
+ complete(&hdmirx_dev->avi_pkt_rcv);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: AVIIF_RCV_IRQ\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+}
+
+static void scdc_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: scdc_st:%#x\n", __func__, status);
+ if (status & SCDCTMDSCCFG_CHG) {
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+}
+
+static irqreturn_t hdmirx_hdmi_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 mu0_st, mu2_st, pk2_st, scdc_st, avp1_st, avp0_st;
+ u32 mu0_mask, mu2_mask, pk2_mask, scdc_mask, avp1_msk, avp0_msk;
+ bool handled = false;
+
+ mu0_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_MASK_N);
+ mu2_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_MASK_N);
+ pk2_mask = hdmirx_readl(hdmirx_dev, PKT_2_INT_MASK_N);
+ scdc_mask = hdmirx_readl(hdmirx_dev, SCDC_INT_MASK_N);
+ mu0_st = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_STATUS);
+ mu2_st = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_STATUS);
+ pk2_st = hdmirx_readl(hdmirx_dev, PKT_2_INT_STATUS);
+ scdc_st = hdmirx_readl(hdmirx_dev, SCDC_INT_STATUS);
+ avp0_st = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_STATUS);
+ avp1_st = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_STATUS);
+ avp0_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_MASK_N);
+ avp1_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_MASK_N);
+ mu0_st &= mu0_mask;
+ mu2_st &= mu2_mask;
+ pk2_st &= pk2_mask;
+ avp1_st &= avp1_msk;
+ avp0_st &= avp0_msk;
+ scdc_st &= scdc_mask;
+
+ if (avp0_st)
+ avpunit_0_int_handler(hdmirx_dev, avp0_st, &handled);
+ if (avp1_st)
+ avpunit_1_int_handler(hdmirx_dev, avp1_st, &handled);
+ if (mu0_st)
+ mainunit_0_int_handler(hdmirx_dev, mu0_st, &handled);
+ if (mu2_st)
+ mainunit_2_int_handler(hdmirx_dev, mu2_st, &handled);
+ if (pk2_st)
+ pkt_2_int_handler(hdmirx_dev, pk2_st, &handled);
+ if (scdc_st)
+ scdc_int_handler(hdmirx_dev, scdc_st, &handled);
+
+ if (!handled) {
+ v4l2_dbg(2, debug, v4l2_dev, "%s: hdmi irq not handled", __func__);
+ v4l2_dbg(2, debug, v4l2_dev,
+ "avp0:%#x, avp1:%#x, mu0:%#x, mu2:%#x, pk2:%#x, scdc:%#x\n",
+ avp0_st, avp1_st, mu0_st, mu2_st, pk2_st, scdc_st);
+ }
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: en_fiq", __func__);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void hdmirx_vb_done(struct hdmirx_stream *stream,
+ struct vb2_v4l2_buffer *vb_done)
+{
+ const struct v4l2_format_info *finfo = stream->out_finfo;
+ u32 i;
+
+ /* Dequeue a filled buffer */
+ for (i = 0; i < finfo->mem_planes; i++) {
+ vb2_set_plane_payload(&vb_done->vb2_buf, i,
+ stream->pixm.plane_fmt[i].sizeimage);
+ }
+
+ vb_done->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ bool *handled)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ struct vb2_v4l2_buffer *vb_done = NULL;
+
+ if (!(stream->irq_stat) && !(stream->irq_stat & LINE_FLAG_INT_EN))
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: last time have no line_flag_irq\n", __func__);
+
+ /* skip first frames that are expected to come out zeroed from DMA */
+ if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
+ goto DMA_IDLE_OUT;
+
+ if (bt->interlaced != V4L2_DV_INTERLACED ||
+ !(stream->line_flag_int_cnt % 2)) {
+ if (stream->next_buf) {
+ if (stream->curr_buf)
+ vb_done = &stream->curr_buf->vb;
+
+ if (vb_done) {
+ vb_done->vb2_buf.timestamp = ktime_get_ns();
+ vb_done->sequence = stream->sequence;
+
+ if (bt->interlaced)
+ vb_done->field = V4L2_FIELD_INTERLACED_TB;
+ else
+ vb_done->field = V4L2_FIELD_NONE;
+
+ hdmirx_vb_done(stream, vb_done);
+ }
+
+ stream->curr_buf = NULL;
+ if (stream->next_buf) {
+ stream->curr_buf = stream->next_buf;
+ stream->next_buf = NULL;
+ }
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: next_buf NULL, skip vb_done\n", __func__);
+ }
+
+ stream->sequence++;
+ if (stream->sequence == 30)
+ v4l2_dbg(1, debug, v4l2_dev, "rcv frames\n");
+ }
+
+DMA_IDLE_OUT:
+ *handled = true;
+}
+
+static void line_flag_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ bool *handled)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ u32 dma_cfg6;
+
+ stream->line_flag_int_cnt++;
+ if (!(stream->irq_stat) && !(stream->irq_stat & HDMIRX_DMA_IDLE_INT))
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: last have no dma_idle_irq\n", __func__);
+ dma_cfg6 = hdmirx_readl(hdmirx_dev, DMA_CONFIG6);
+ if (!(dma_cfg6 & HDMIRX_DMA_EN)) {
+ v4l2_dbg(2, debug, v4l2_dev, "%s: dma not on\n", __func__);
+ goto LINE_FLAG_OUT;
+ }
+
+ if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
+ goto LINE_FLAG_OUT;
+
+ if (bt->interlaced != V4L2_DV_INTERLACED ||
+ !(stream->line_flag_int_cnt % 2)) {
+ if (!stream->next_buf) {
+ spin_lock(&stream->vbq_lock);
+ if (!list_empty(&stream->buf_head)) {
+ stream->next_buf = list_first_entry(&stream->buf_head,
+ struct hdmirx_buffer,
+ queue);
+ list_del(&stream->next_buf->queue);
+ } else {
+ stream->next_buf = NULL;
+ }
+ spin_unlock(&stream->vbq_lock);
+
+ if (stream->next_buf) {
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
+ stream->next_buf->buff_addr[HDMIRX_PLANE_Y]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
+ stream->next_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: no buffer is available\n", __func__);
+ }
+ }
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: interlace:%d, line_flag_int_cnt:%d\n",
+ __func__, bt->interlaced, stream->line_flag_int_cnt);
+ }
+
+LINE_FLAG_OUT:
+ *handled = true;
+}
+
+static irqreturn_t hdmirx_dma_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 dma_stat1, dma_stat13;
+ bool handled = false;
+
+ dma_stat1 = hdmirx_readl(hdmirx_dev, DMA_STATUS1);
+ dma_stat13 = hdmirx_readl(hdmirx_dev, DMA_STATUS13);
+ v4l2_dbg(3, debug, v4l2_dev, "dma_irq st1:%#x, st13:%d\n",
+ dma_stat1, dma_stat13);
+
+ if (READ_ONCE(stream->stopping)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: stop stream\n", __func__);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ WRITE_ONCE(stream->stopping, false);
+ wake_up(&stream->wq_stopped);
+ return IRQ_HANDLED;
+ }
+
+ if (dma_stat1 & HDMIRX_DMA_IDLE_INT)
+ dma_idle_int_handler(hdmirx_dev, &handled);
+
+ if (dma_stat1 & LINE_FLAG_INT_EN)
+ line_flag_int_handler(hdmirx_dev, &handled);
+
+ if (!handled)
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: dma irq not handled, dma_stat1:%#x\n",
+ __func__, dma_stat1);
+
+ stream->irq_stat = dma_stat1;
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+
+ return IRQ_HANDLED;
+}
+
+static int hdmirx_wait_signal_lock(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 mu_status, scdc_status, dma_st10, cmu_st;
+ u32 i;
+
+ for (i = 0; i < 300; i++) {
+ mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
+ scdc_status = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS3);
+ dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
+ cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
+
+ if ((mu_status & TMDSVALID_STABLE_ST) &&
+ (dma_st10 & HDMIRX_LOCK) &&
+ (cmu_st & TMDSQPCLK_LOCKED_ST))
+ break;
+
+ if (!tx_5v_power_present(hdmirx_dev)) {
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: HDMI pull out, return\n", __func__);
+ return -1;
+ }
+
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+ }
+
+ if (i == 300) {
+ v4l2_err(v4l2_dev, "%s: signal not lock, tmds_clk_ratio:%d\n",
+ __func__, hdmirx_dev->tmds_clk_ratio);
+ v4l2_err(v4l2_dev, "%s: mu_st:%#x, scdc_st:%#x, dma_st10:%#x\n",
+ __func__, mu_status, scdc_status, dma_st10);
+ return -1;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: signal lock ok, i:%d\n", __func__, i);
+ hdmirx_writel(hdmirx_dev, GLOBAL_SWRESET_REQUEST, DATAPATH_SWRESETREQ);
+
+ reinit_completion(&hdmirx_dev->avi_pkt_rcv);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, PKTDEC_AVIIF_RCV_IRQ);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->avi_pkt_rcv,
+ msecs_to_jiffies(300))) {
+ v4l2_err(v4l2_dev, "%s wait avi_pkt_rcv failed\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, 0);
+ }
+
+ msleep(50);
+ hdmirx_format_change(hdmirx_dev);
+
+ return 0;
+}
+
+static void hdmirx_plugin(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ if (hdmirx_dev->plugged)
+ return;
+
+ hdmirx_submodule_init(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
+ POWERPROVIDED);
+ hdmirx_phy_config(hdmirx_dev);
+ hdmirx_interrupts_setup(hdmirx_dev, true);
+
+ hdmirx_dev->plugged = true;
+}
+
+static void hdmirx_delayed_work_hotplug(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ bool plugin;
+
+ hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
+ delayed_work_hotplug.work);
+
+ mutex_lock(&hdmirx_dev->work_lock);
+ plugin = tx_5v_power_present(hdmirx_dev);
+ v4l2_ctrl_s_ctrl(hdmirx_dev->detect_tx_5v_ctrl, plugin);
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
+ __func__, plugin);
+
+ hdmirx_plugout(hdmirx_dev);
+
+ if (plugin)
+ hdmirx_plugin(hdmirx_dev);
+
+ mutex_unlock(&hdmirx_dev->work_lock);
+}
+
+static void hdmirx_delayed_work_res_change(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ bool plugin;
+
+ hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
+ delayed_work_res_change.work);
+
+ mutex_lock(&hdmirx_dev->work_lock);
+ plugin = tx_5v_power_present(hdmirx_dev);
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
+ __func__, plugin);
+ if (plugin) {
+ hdmirx_interrupts_setup(hdmirx_dev, false);
+ hdmirx_submodule_init(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
+ POWERPROVIDED);
+ hdmirx_phy_config(hdmirx_dev);
+
+ if (hdmirx_wait_signal_lock(hdmirx_dev)) {
+ hdmirx_plugout(hdmirx_dev);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(200));
+ } else {
+ hdmirx_dma_config(hdmirx_dev);
+ hdmirx_interrupts_setup(hdmirx_dev, true);
+ }
+ }
+ mutex_unlock(&hdmirx_dev->work_lock);
+}
+
+static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ u32 val;
+
+ val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
+ v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: 5v:%d\n", __func__, val);
+
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(10));
+
+ return IRQ_HANDLED;
+}
+
+static const struct hdmirx_cec_ops hdmirx_cec_ops = {
+ .write = hdmirx_writel,
+ .read = hdmirx_readl,
+};
+
+static void devm_hdmirx_of_reserved_mem_device_release(void *dev)
+{
+ of_reserved_mem_device_release(dev);
+}
+
+static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ int ret;
+
+ hdmirx_dev->num_clks = devm_clk_bulk_get_all(dev, &hdmirx_dev->clks);
+ if (hdmirx_dev->num_clks < 1)
+ return -ENODEV;
+
+ hdmirx_dev->resets[HDMIRX_RST_A].id = "axi";
+ hdmirx_dev->resets[HDMIRX_RST_P].id = "apb";
+ hdmirx_dev->resets[HDMIRX_RST_REF].id = "ref";
+ hdmirx_dev->resets[HDMIRX_RST_BIU].id = "biu";
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, HDMIRX_NUM_RST,
+ hdmirx_dev->resets);
+ if (ret < 0) {
+ dev_err(dev, "failed to get reset controls\n");
+ return ret;
+ }
+
+ hdmirx_dev->detect_5v_gpio =
+ devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
+
+ if (IS_ERR(hdmirx_dev->detect_5v_gpio)) {
+ dev_err(dev, "failed to get hdmirx hot plug detection gpio\n");
+ return PTR_ERR(hdmirx_dev->detect_5v_gpio);
+ }
+
+ hdmirx_dev->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(hdmirx_dev->grf)) {
+ dev_err(dev, "failed to get rockchip,grf\n");
+ return PTR_ERR(hdmirx_dev->grf);
+ }
+
+ hdmirx_dev->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,vo1-grf");
+ if (IS_ERR(hdmirx_dev->vo1_grf)) {
+ dev_err(dev, "failed to get rockchip,vo1-grf\n");
+ return PTR_ERR(hdmirx_dev->vo1_grf);
+ }
+
+ if (!device_property_read_bool(dev, "hpd-is-active-low"))
+ hdmirx_dev->hpd_trigger_level_high = true;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_warn(dev, "no reserved memory for HDMIRX, use default CMA\n");
+ } else {
+ ret = devm_add_action_or_reset(dev,
+ devm_hdmirx_of_reserved_mem_device_release,
+ dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void hdmirx_disable_all_interrupts(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, SCDC_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, CEC_INT_MASK_N, 0);
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, HDCP_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, HDCP_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, CEC_INT_CLEAR, 0xffffffff);
+}
+
+static void hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET | PHY_PDDQ, 0);
+
+ regmap_write(hdmirx_dev->vo1_grf, VO1_GRF_VO1_CON2,
+ (HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) |
+ ((HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) << 16));
+ /*
+ * Some interrupts are enabled by default, so we disable
+ * all interrupts and clear interrupts status first.
+ */
+ hdmirx_disable_all_interrupts(hdmirx_dev);
+}
+
+/* hdmi-4k-300mhz EDID produced by v4l2-ctl tool */
+static u8 __maybe_unused edid_default[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
+ 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59,
+ 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40,
+ 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x04, 0x74,
+ 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58,
+ 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
+ 0x87, 0x1e, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,
+ 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x33,
+ 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc5,
+
+ 0x02, 0x03, 0x40, 0xf1, 0x4f, 0x5f, 0x5e, 0x5d,
+ 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, 0x20, 0x05,
+ 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, 0x07, 0x07,
+ 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, 0x0c, 0x00,
+ 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x60, 0x01,
+ 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, 0x01, 0x00,
+ 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00,
+ 0x00, 0xe3, 0x06, 0x01, 0x00, 0xe2, 0x0d, 0x5f,
+ 0xa3, 0x66, 0x00, 0xa0, 0xf0, 0x70, 0x1f, 0x80,
+ 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00,
+ 0x00, 0x1e, 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38,
+ 0x1f, 0x40, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c,
+ 0x32, 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80,
+ 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00,
+ 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1,
+};
+
+static void hdmirx_load_default_edid(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_edid def_edid = {};
+
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+
+ if (!IS_ENABLED(CONFIG_VIDEO_SYNOPSYS_HDMIRX_LOAD_DEFAULT_EDID))
+ return;
+
+ /* disable hpd and write edid */
+ def_edid.blocks = sizeof(edid_default) / EDID_BLOCK_SIZE;
+ def_edid.edid = edid_default;
+
+ hdmirx_write_edid(hdmirx_dev, &def_edid);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+}
+
+static int hdmirx_disable(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ hdmirx_plugout(hdmirx_dev);
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+
+ clk_bulk_disable_unprepare(hdmirx_dev->num_clks, hdmirx_dev->clks);
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: suspend\n", __func__);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int hdmirx_enable(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: resume\n", __func__);
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(hdmirx_dev->num_clks, hdmirx_dev->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable hdmirx bulk clks: %d\n", ret);
+ return ret;
+ }
+
+ reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+ usleep_range(150, 160);
+ reset_control_bulk_deassert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+ usleep_range(150, 160);
+
+ return 0;
+}
+
+static void hdmirx_disable_irq(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ disable_irq(hdmirx_dev->det_irq);
+ disable_irq(hdmirx_dev->dma_irq);
+ disable_irq(hdmirx_dev->hdmi_irq);
+
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_hotplug);
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_res_change);
+}
+
+static void hdmirx_enable_irq(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ enable_irq(hdmirx_dev->hdmi_irq);
+ enable_irq(hdmirx_dev->dma_irq);
+ enable_irq(hdmirx_dev->det_irq);
+
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(110));
+}
+
+static __maybe_unused int hdmirx_suspend(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ hdmirx_disable_irq(dev);
+
+ /* TODO store CEC HW state */
+ disable_irq(hdmirx_dev->cec->irq);
+
+ return hdmirx_disable(dev);
+}
+
+static __maybe_unused int hdmirx_resume(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ int ret = hdmirx_enable(dev);
+
+ if (ret)
+ return ret;
+
+ if (hdmirx_dev->edid_blocks_written) {
+ hdmirx_write_edid_data(hdmirx_dev, hdmirx_dev->edid,
+ hdmirx_dev->edid_blocks_written);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+ }
+
+ /* TODO restore CEC HW state */
+ enable_irq(hdmirx_dev->cec->irq);
+
+ hdmirx_enable_irq(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops snps_hdmirx_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(hdmirx_suspend, hdmirx_resume)
+};
+
+static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
+ struct platform_device *pdev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ int ret, irq;
+
+ irq = platform_get_irq_byname(pdev, "hdmi");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get hdmi irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->hdmi_irq = irq;
+ ret = devm_request_irq(dev, irq, hdmirx_hdmi_irq_handler, 0,
+ "rk_hdmirx-hdmi", hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request hdmi irq\n");
+ return ret;
+ }
+
+ irq = platform_get_irq_byname(pdev, "dma");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get dma irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->dma_irq = irq;
+ ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
+ IRQF_ONESHOT, "rk_hdmirx-dma",
+ hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request dma irq\n");
+ return ret;
+ }
+
+ irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get hdmirx-5v irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->det_irq = irq;
+ ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "rk_hdmirx-5v", hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
+ struct platform_device *pdev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ struct hdmirx_cec_data cec_data;
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, "cec");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get cec irq\n");
+ return irq;
+ }
+
+ cec_data.hdmirx = hdmirx_dev;
+ cec_data.dev = hdmirx_dev->dev;
+ cec_data.ops = &hdmirx_cec_ops;
+ cec_data.irq = irq;
+
+ hdmirx_dev->cec = snps_hdmirx_cec_register(&cec_data);
+ if (IS_ERR(hdmirx_dev->cec))
+ return dev_err_probe(dev, PTR_ERR(hdmirx_dev->cec),
+ "failed to register cec\n");
+
+ return 0;
+}
+
+static int hdmirx_probe(struct platform_device *pdev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ struct device *dev = &pdev->dev;
+ struct v4l2_ctrl_handler *hdl;
+ struct hdmirx_stream *stream;
+ struct v4l2_device *v4l2_dev;
+ int ret;
+
+ hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
+ if (!hdmirx_dev)
+ return -ENOMEM;
+
+ /*
+ * RK3588 HDMIRX SoC integration doesn't use IOMMU and can
+ * address only first 32bit of the physical address space.
+ */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ hdmirx_dev->dev = dev;
+ dev_set_drvdata(dev, hdmirx_dev);
+
+ ret = hdmirx_parse_dt(hdmirx_dev);
+ if (ret)
+ return ret;
+
+ ret = hdmirx_setup_irq(hdmirx_dev, pdev);
+ if (ret)
+ return ret;
+
+ hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hdmirx_dev->regs))
+ return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
+ "failed to remap regs resource\n");
+
+ mutex_init(&hdmirx_dev->stream_lock);
+ mutex_init(&hdmirx_dev->work_lock);
+ spin_lock_init(&hdmirx_dev->rst_lock);
+
+ init_completion(&hdmirx_dev->cr_write_done);
+ init_completion(&hdmirx_dev->timer_base_lock);
+ init_completion(&hdmirx_dev->avi_pkt_rcv);
+
+ INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
+ hdmirx_delayed_work_hotplug);
+ INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
+ hdmirx_delayed_work_res_change);
+
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ hdmirx_dev->timings = cea640x480;
+
+ hdmirx_enable(dev);
+ hdmirx_init(hdmirx_dev);
+
+ v4l2_dev = &hdmirx_dev->v4l2_dev;
+ strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
+
+ hdl = &hdmirx_dev->hdl;
+ v4l2_ctrl_handler_init(hdl, 3);
+
+ hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_RX_POWER_PRESENT,
+ 0, 1, 0, 0);
+
+ hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, NULL,
+ V4L2_CID_DV_RX_RGB_RANGE,
+ V4L2_DV_RGB_RANGE_FULL, 0,
+ V4L2_DV_RGB_RANGE_AUTO);
+
+ hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ hdmirx_dev->content_type =
+ v4l2_ctrl_new_std_menu(hdl, NULL, V4L2_CID_DV_RX_IT_CONTENT_TYPE,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ dev_err_probe(dev, ret, "v4l2 ctrl handler init failed\n");
+ goto err_pm;
+ }
+ hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
+
+ ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "v4l2 device registration failed\n");
+ goto err_hdl;
+ }
+
+ stream = &hdmirx_dev->stream;
+ stream->hdmirx_dev = hdmirx_dev;
+ ret = hdmirx_register_stream_vdev(stream);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "video device registration failed\n");
+ goto err_unreg_v4l2_dev;
+ }
+
+ ret = hdmirx_register_cec(hdmirx_dev, pdev);
+ if (ret)
+ goto err_unreg_video_dev;
+
+ hdmirx_load_default_edid(hdmirx_dev);
+
+ hdmirx_enable_irq(dev);
+
+ hdmirx_dev->debugfs_dir = debugfs_create_dir(hdmirx_dev->v4l2_dev.name,
+ v4l2_debugfs_root());
+
+ hdmirx_dev->infoframes = v4l2_debugfs_if_alloc(hdmirx_dev->debugfs_dir,
+ V4L2_DEBUGFS_IF_AVI, hdmirx_dev,
+ hdmirx_debugfs_if_read);
+
+ return 0;
+
+err_unreg_video_dev:
+ vb2_video_unregister_device(&hdmirx_dev->stream.vdev);
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
+err_hdl:
+ v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
+err_pm:
+ hdmirx_disable(dev);
+
+ return ret;
+}
+
+static void hdmirx_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ v4l2_debugfs_if_free(hdmirx_dev->infoframes);
+ debugfs_remove_recursive(hdmirx_dev->debugfs_dir);
+
+ snps_hdmirx_cec_unregister(hdmirx_dev->cec);
+
+ hdmirx_disable_irq(dev);
+
+ vb2_video_unregister_device(&hdmirx_dev->stream.vdev);
+ v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
+ v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
+
+ /* touched by hdmirx_disable()->hdmirx_plugout() */
+ hdmirx_dev->rgb_range = NULL;
+ hdmirx_dev->content_type = NULL;
+
+ hdmirx_disable(dev);
+
+ reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+}
+
+static const struct of_device_id hdmirx_id[] = {
+ { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hdmirx_id);
+
+static struct platform_driver hdmirx_driver = {
+ .probe = hdmirx_probe,
+ .remove = hdmirx_remove,
+ .driver = {
+ .name = "snps_hdmirx",
+ .of_match_table = hdmirx_id,
+ .pm = &snps_hdmirx_pm_ops,
+ }
+};
+module_platform_driver(hdmirx_driver);
+
+MODULE_DESCRIPTION("Synopsys HDMI Receiver Driver");
+MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
+MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
+MODULE_AUTHOR("Dmitry Osipenko <dmitry.osipenko@collabora.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
new file mode 100644
index 000000000000..220ab99ca611
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Dingxian Wen <shawn.wen@rock-chips.com>
+ */
+
+#ifndef DW_HDMIRX_H
+#define DW_HDMIRX_H
+
+#include <linux/bitops.h>
+
+#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
+#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
+
+/* SYS_GRF */
+#define SYS_GRF_SOC_CON1 0x0304
+#define HDMIRXPHY_SRAM_EXT_LD_DONE BIT(1)
+#define HDMIRXPHY_SRAM_BYPASS BIT(0)
+#define SYS_GRF_SOC_STATUS1 0x0384
+#define HDMIRXPHY_SRAM_INIT_DONE BIT(10)
+#define SYS_GRF_CHIP_ID 0x0600
+
+/* VO1_GRF */
+#define VO1_GRF_VO1_CON2 0x0008
+#define HDMIRX_SDAIN_MSK BIT(2)
+#define HDMIRX_SCLIN_MSK BIT(1)
+
+/* HDMIRX PHY */
+#define SUP_DIG_ANA_CREGS_SUP_ANA_NC 0x004f
+
+#define LANE0_DIG_ASIC_RX_OVRD_OUT_0 0x100f
+#define LANE1_DIG_ASIC_RX_OVRD_OUT_0 0x110f
+#define LANE2_DIG_ASIC_RX_OVRD_OUT_0 0x120f
+#define LANE3_DIG_ASIC_RX_OVRD_OUT_0 0x130f
+#define ASIC_ACK_OVRD_EN BIT(1)
+#define ASIC_ACK BIT(0)
+
+#define LANE0_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x104a
+#define LANE1_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x114a
+#define LANE2_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x124a
+#define LANE3_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x134a
+#define FREQ_TUNE_START_VAL_MASK GENMASK(9, 0)
+#define FREQ_TUNE_START_VAL(x) UPDATE(x, 9, 0)
+
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_FSM_CONFIG 0x20c4
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_ADAPT_REF_FOM 0x20c7
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG 0x20e9
+#define CDR_SETTING_BOUNDARY_3_DEFAULT 0x52da
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG 0x20ea
+#define CDR_SETTING_BOUNDARY_4_DEFAULT 0x43cd
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG 0x20eb
+#define CDR_SETTING_BOUNDARY_5_DEFAULT 0x35b3
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG 0x20fb
+#define CDR_SETTING_BOUNDARY_6_DEFAULT 0x2799
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG 0x20fc
+#define CDR_SETTING_BOUNDARY_7_DEFAULT 0x1b65
+
+#define RAWLANE0_DIG_PCS_XF_RX_OVRD_OUT 0x300e
+#define RAWLANE1_DIG_PCS_XF_RX_OVRD_OUT 0x310e
+#define RAWLANE2_DIG_PCS_XF_RX_OVRD_OUT 0x320e
+#define RAWLANE3_DIG_PCS_XF_RX_OVRD_OUT 0x330e
+#define PCS_ACK_WRITE_SELECT BIT(14)
+#define PCS_EN_CTL BIT(1)
+#define PCS_ACK BIT(0)
+
+#define RAWLANE0_DIG_AON_FAST_FLAGS 0x305c
+#define RAWLANE1_DIG_AON_FAST_FLAGS 0x315c
+#define RAWLANE2_DIG_AON_FAST_FLAGS 0x325c
+#define RAWLANE3_DIG_AON_FAST_FLAGS 0x335c
+
+/* HDMIRX Ctrler */
+#define GLOBAL_SWRESET_REQUEST 0x0020
+#define DATAPATH_SWRESETREQ BIT(12)
+#define GLOBAL_SWENABLE 0x0024
+#define PHYCTRL_ENABLE BIT(21)
+#define CEC_ENABLE BIT(16)
+#define TMDS_ENABLE BIT(13)
+#define DATAPATH_ENABLE BIT(12)
+#define PKTFIFO_ENABLE BIT(11)
+#define AVPUNIT_ENABLE BIT(8)
+#define MAIN_ENABLE BIT(0)
+#define GLOBAL_TIMER_REF_BASE 0x0028
+#define CORE_CONFIG 0x0050
+#define CMU_CONFIG0 0x0060
+#define TMDSQPCLK_STABLE_FREQ_MARGIN_MASK GENMASK(30, 16)
+#define TMDSQPCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 30, 16)
+#define AUDCLK_STABLE_FREQ_MARGIN_MASK GENMASK(11, 9)
+#define AUDCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 11, 9)
+#define CMU_STATUS 0x007c
+#define TMDSQPCLK_LOCKED_ST BIT(4)
+#define CMU_TMDSQPCLK_FREQ 0x0084
+#define PHY_CONFIG 0x00c0
+#define LDO_AFE_PROG_MASK GENMASK(24, 23)
+#define LDO_AFE_PROG(x) UPDATE(x, 24, 23)
+#define LDO_PWRDN BIT(21)
+#define TMDS_CLOCK_RATIO BIT(16)
+#define RXDATA_WIDTH BIT(15)
+#define REFFREQ_SEL_MASK GENMASK(11, 9)
+#define REFFREQ_SEL(x) UPDATE(x, 11, 9)
+#define HDMI_DISABLE BIT(8)
+#define PHY_PDDQ BIT(1)
+#define PHY_RESET BIT(0)
+#define PHY_STATUS 0x00c8
+#define HDMI_DISABLE_ACK BIT(1)
+#define PDDQ_ACK BIT(0)
+#define PHYCREG_CONFIG0 0x00e0
+#define PHYCREG_CR_PARA_SELECTION_MODE_MASK GENMASK(1, 0)
+#define PHYCREG_CR_PARA_SELECTION_MODE(x) UPDATE(x, 1, 0)
+#define PHYCREG_CONFIG1 0x00e4
+#define PHYCREG_CONFIG2 0x00e8
+#define PHYCREG_CONFIG3 0x00ec
+#define PHYCREG_CONTROL 0x00f0
+#define PHYCREG_CR_PARA_WRITE_P BIT(1)
+#define PHYCREG_CR_PARA_READ_P BIT(0)
+#define PHYCREG_STATUS 0x00f4
+
+#define MAINUNIT_STATUS 0x0150
+#define TMDSVALID_STABLE_ST BIT(1)
+#define DESCRAND_EN_CONTROL 0x0210
+#define SCRAMB_EN_SEL_QST_MASK GENMASK(1, 0)
+#define SCRAMB_EN_SEL_QST(x) UPDATE(x, 1, 0)
+#define DESCRAND_SYNC_CONTROL 0x0214
+#define RECOVER_UNSYNC_STREAM_QST BIT(0)
+#define DESCRAND_SYNC_SEQ_CONFIG 0x022c
+#define DESCRAND_SYNC_SEQ_ERR_CNT_EN BIT(0)
+#define DESCRAND_SYNC_SEQ_STATUS 0x0234
+#define DEFRAMER_CONFIG0 0x0270
+#define VS_CNT_THR_QST_MASK GENMASK(27, 20)
+#define VS_CNT_THR_QST(x) UPDATE(x, 27, 20)
+#define HS_POL_QST_MASK GENMASK(19, 18)
+#define HS_POL_QST(x) UPDATE(x, 19, 18)
+#define VS_POL_QST_MASK GENMASK(17, 16)
+#define VS_POL_QST(x) UPDATE(x, 17, 16)
+#define VS_REMAPFILTER_EN_QST BIT(8)
+#define VS_FILTER_ORDER_QST_MASK GENMASK(1, 0)
+#define VS_FILTER_ORDER_QST(x) UPDATE(x, 1, 0)
+#define DEFRAMER_VSYNC_CNT_CLEAR 0x0278
+#define VSYNC_CNT_CLR_P BIT(0)
+#define DEFRAMER_STATUS 0x027c
+#define OPMODE_STS_MASK GENMASK(6, 4)
+#define I2C_SLAVE_CONFIG1 0x0164
+#define I2C_SDA_OUT_HOLD_VALUE_QST_MASK GENMASK(15, 8)
+#define I2C_SDA_OUT_HOLD_VALUE_QST(x) UPDATE(x, 15, 8)
+#define I2C_SDA_IN_HOLD_VALUE_QST_MASK GENMASK(7, 0)
+#define I2C_SDA_IN_HOLD_VALUE_QST(x) UPDATE(x, 7, 0)
+#define OPMODE_STS_MASK GENMASK(6, 4)
+#define REPEATER_QST BIT(28)
+#define FASTREAUTH_QST BIT(27)
+#define FEATURES_1DOT1_QST BIT(26)
+#define FASTI2C_QST BIT(25)
+#define EESS_CTL_THR_QST_MASK GENMASK(19, 16)
+#define EESS_CTL_THR_QST(x) UPDATE(x, 19, 16)
+#define OESS_CTL3_THR_QST_MASK GENMASK(11, 8)
+#define OESS_CTL3_THR_QST(x) UPDATE(x, 11, 8)
+#define EESS_OESS_SEL_QST_MASK GENMASK(5, 4)
+#define EESS_OESS_SEL_QST(x) UPDATE(x, 5, 4)
+#define KEY_DECRYPT_EN_QST BIT(0)
+#define KEY_DECRYPT_SEED_QST_MASK GENMASK(15, 0)
+#define KEY_DECRYPT_SEED_QST(x) UPDATE(x, 15, 0)
+#define HDCP_INT_CLEAR 0x50d8
+#define HDCP_1_INT_CLEAR 0x50e8
+#define HDCP2_CONFIG 0x02f0
+#define HDCP2_SWITCH_OVR_VALUE BIT(2)
+#define HDCP2_SWITCH_OVR_EN BIT(1)
+
+#define VIDEO_CONFIG2 0x042c
+#define VPROC_VSYNC_POL_OVR_VALUE BIT(19)
+#define VPROC_VSYNC_POL_OVR_EN BIT(18)
+#define VPROC_HSYNC_POL_OVR_VALUE BIT(17)
+#define VPROC_HSYNC_POL_OVR_EN BIT(16)
+#define VPROC_FMT_OVR_VALUE_MASK GENMASK(6, 4)
+#define VPROC_FMT_OVR_VALUE(x) UPDATE(x, 6, 4)
+#define VPROC_FMT_OVR_EN BIT(0)
+
+#define AFIFO_FILL_RESTART BIT(0)
+#define AFIFO_INIT_P BIT(0)
+#define AFIFO_THR_LOW_QST_MASK GENMASK(25, 16)
+#define AFIFO_THR_LOW_QST(x) UPDATE(x, 25, 16)
+#define AFIFO_THR_HIGH_QST_MASK GENMASK(9, 0)
+#define AFIFO_THR_HIGH_QST(x) UPDATE(x, 9, 0)
+#define AFIFO_THR_MUTE_LOW_QST_MASK GENMASK(25, 16)
+#define AFIFO_THR_MUTE_LOW_QST(x) UPDATE(x, 25, 16)
+#define AFIFO_THR_MUTE_HIGH_QST_MASK GENMASK(9, 0)
+#define AFIFO_THR_MUTE_HIGH_QST(x) UPDATE(x, 9, 0)
+
+#define AFIFO_UNDERFLOW_ST BIT(25)
+#define AFIFO_OVERFLOW_ST BIT(24)
+
+#define SPEAKER_ALLOC_OVR_EN BIT(16)
+#define I2S_BPCUV_EN BIT(4)
+#define SPDIF_EN BIT(2)
+#define I2S_EN BIT(1)
+#define AFIFO_THR_PASS_DEMUTEMASK_N BIT(24)
+#define AVMUTE_DEMUTEMASK_N BIT(16)
+#define AFIFO_THR_MUTE_LOW_MUTEMASK_N BIT(9)
+#define AFIFO_THR_MUTE_HIGH_MUTEMASK_N BIT(8)
+#define AVMUTE_MUTEMASK_N BIT(0)
+#define SCDC_CONFIG 0x0580
+#define HPDLOW BIT(1)
+#define POWERPROVIDED BIT(0)
+#define SCDC_REGBANK_STATUS1 0x058c
+#define SCDC_TMDSBITCLKRATIO BIT(1)
+#define SCDC_REGBANK_STATUS3 0x0594
+#define SCDC_REGBANK_CONFIG0 0x05c0
+#define SCDC_SINKVERSION_QST_MASK GENMASK(7, 0)
+#define SCDC_SINKVERSION_QST(x) UPDATE(x, 7, 0)
+#define AGEN_LAYOUT BIT(4)
+#define AGEN_SPEAKER_ALLOC GENMASK(15, 8)
+
+#define CED_CONFIG 0x0760
+#define CED_VIDDATACHECKEN_QST BIT(27)
+#define CED_DATAISCHECKEN_QST BIT(26)
+#define CED_GBCHECKEN_QST BIT(25)
+#define CED_CTRLCHECKEN_QST BIT(24)
+#define CED_CHLOCKMAXER_QST_MASK GENMASK(14, 0)
+#define CED_CHLOCKMAXER_QST(x) UPDATE(x, 14, 0)
+#define CED_DYN_CONFIG 0x0768
+#define CED_DYN_CONTROL 0x076c
+#define PKTEX_BCH_ERRFILT_CONFIG 0x07c4
+#define PKTEX_CHKSUM_ERRFILT_CONFIG 0x07c8
+
+#define PKTDEC_ACR_PH2_1 0x1100
+#define PKTDEC_ACR_PB3_0 0x1104
+#define PKTDEC_ACR_PB7_4 0x1108
+#define PKTDEC_AVIIF_PH2_1 0x1200
+#define PKTDEC_AVIIF_PB3_0 0x1204
+#define PKTDEC_AVIIF_PB7_4 0x1208
+#define VIC_VAL_MASK GENMASK(6, 0)
+#define PKTDEC_AVIIF_PB11_8 0x120c
+#define PKTDEC_AVIIF_PB15_12 0x1210
+#define PKTDEC_AVIIF_PB19_16 0x1214
+#define PKTDEC_AVIIF_PB23_20 0x1218
+#define PKTDEC_AVIIF_PB27_24 0x121c
+
+#define PKTFIFO_CONFIG 0x1500
+#define PKTFIFO_STORE_FILT_CONFIG 0x1504
+#define PKTFIFO_THR_CONFIG0 0x1508
+#define PKTFIFO_THR_CONFIG1 0x150c
+#define PKTFIFO_CONTROL 0x1510
+
+#define VMON_STATUS1 0x1580
+#define VMON_STATUS2 0x1584
+#define VMON_STATUS3 0x1588
+#define VMON_STATUS4 0x158c
+#define VMON_STATUS5 0x1590
+#define VMON_STATUS6 0x1594
+#define VMON_STATUS7 0x1598
+#define VMON_ILACE_DETECT BIT(4)
+
+#define CEC_TX_CONTROL 0x2000
+#define CEC_STATUS 0x2004
+#define CEC_CONFIG 0x2008
+#define RX_AUTO_DRIVE_ACKNOWLEDGE BIT(9)
+#define CEC_ADDR 0x200c
+#define CEC_TX_COUNT 0x2020
+#define CEC_TX_DATA3_0 0x2024
+#define CEC_RX_COUNT_STATUS 0x2040
+#define CEC_RX_DATA3_0 0x2044
+#define CEC_LOCK_CONTROL 0x2054
+#define CEC_RXQUAL_BITTIME_CONFIG 0x2060
+#define CEC_RX_BITTIME_CONFIG 0x2064
+#define CEC_TX_BITTIME_CONFIG 0x2068
+
+#define DMA_CONFIG1 0x4400
+#define UV_WID_MASK GENMASK(31, 28)
+#define UV_WID(x) UPDATE(x, 31, 28)
+#define Y_WID_MASK GENMASK(27, 24)
+#define Y_WID(x) UPDATE(x, 27, 24)
+#define DDR_STORE_FORMAT_MASK GENMASK(15, 12)
+#define DDR_STORE_FORMAT(x) UPDATE(x, 15, 12)
+#define ABANDON_EN BIT(0)
+#define DMA_CONFIG2 0x4404
+#define DMA_CONFIG3 0x4408
+#define DMA_CONFIG4 0x440c // dma irq en
+#define DMA_CONFIG5 0x4410 // dma irq clear status
+#define LINE_FLAG_INT_EN BIT(8)
+#define HDMIRX_DMA_IDLE_INT BIT(7)
+#define HDMIRX_LOCK_DISABLE_INT BIT(6)
+#define LAST_FRAME_AXI_UNFINISH_INT_EN BIT(5)
+#define FIFO_OVERFLOW_INT_EN BIT(2)
+#define FIFO_UNDERFLOW_INT_EN BIT(1)
+#define HDMIRX_AXI_ERROR_INT_EN BIT(0)
+#define DMA_CONFIG6 0x4414
+#define RB_SWAP_EN BIT(9)
+#define HSYNC_TOGGLE_EN BIT(5)
+#define VSYNC_TOGGLE_EN BIT(4)
+#define HDMIRX_DMA_EN BIT(1)
+#define DMA_CONFIG7 0x4418
+#define LINE_FLAG_NUM_MASK GENMASK(31, 16)
+#define LINE_FLAG_NUM(x) UPDATE(x, 31, 16)
+#define LOCK_FRAME_NUM_MASK GENMASK(11, 0)
+#define LOCK_FRAME_NUM(x) UPDATE(x, 11, 0)
+#define DMA_CONFIG8 0x441c
+#define REG_MIRROR_EN BIT(0)
+#define DMA_CONFIG9 0x4420
+#define DMA_CONFIG10 0x4424
+#define DMA_CONFIG11 0x4428
+#define EDID_READ_EN_MASK BIT(8)
+#define EDID_READ_EN(x) UPDATE(x, 8, 8)
+#define EDID_WRITE_EN_MASK BIT(7)
+#define EDID_WRITE_EN(x) UPDATE(x, 7, 7)
+#define EDID_SLAVE_ADDR_MASK GENMASK(6, 0)
+#define EDID_SLAVE_ADDR(x) UPDATE(x, 6, 0)
+#define DMA_STATUS1 0x4430 // dma irq status
+#define DMA_STATUS2 0x4434
+#define DMA_STATUS3 0x4438
+#define DMA_STATUS4 0x443c
+#define DMA_STATUS5 0x4440
+#define DMA_STATUS6 0x4444
+#define DMA_STATUS7 0x4448
+#define DMA_STATUS8 0x444c
+#define DMA_STATUS9 0x4450
+#define DMA_STATUS10 0x4454
+#define HDMIRX_LOCK BIT(3)
+#define DMA_STATUS11 0x4458
+#define HDMIRX_TYPE_MASK GENMASK(8, 7)
+#define HDMIRX_COLOR_DEPTH_MASK GENMASK(6, 3)
+#define HDMIRX_FORMAT_MASK GENMASK(2, 0)
+#define DMA_STATUS12 0x445c
+#define DMA_STATUS13 0x4460
+#define DMA_STATUS14 0x4464
+
+#define MAINUNIT_INTVEC_INDEX 0x5000
+#define MAINUNIT_0_INT_STATUS 0x5010
+#define CECRX_NOTIFY_ERR BIT(12)
+#define CECRX_EOM BIT(11)
+#define CECTX_DRIVE_ERR BIT(10)
+#define CECRX_BUSY BIT(9)
+#define CECTX_BUSY BIT(8)
+#define CECTX_FRAME_DISCARDED BIT(5)
+#define CECTX_NRETRANSMIT_FAIL BIT(4)
+#define CECTX_LINE_ERR BIT(3)
+#define CECTX_ARBLOST BIT(2)
+#define CECTX_NACK BIT(1)
+#define CECTX_DONE BIT(0)
+#define MAINUNIT_0_INT_MASK_N 0x5014
+#define MAINUNIT_0_INT_CLEAR 0x5018
+#define MAINUNIT_0_INT_FORCE 0x501c
+#define TIMER_BASE_LOCKED_IRQ BIT(26)
+#define TMDSQPCLK_OFF_CHG BIT(5)
+#define TMDSQPCLK_LOCKED_CHG BIT(4)
+#define MAINUNIT_1_INT_STATUS 0x5020
+#define MAINUNIT_1_INT_MASK_N 0x5024
+#define MAINUNIT_1_INT_CLEAR 0x5028
+#define MAINUNIT_1_INT_FORCE 0x502c
+#define MAINUNIT_2_INT_STATUS 0x5030
+#define MAINUNIT_2_INT_MASK_N 0x5034
+#define MAINUNIT_2_INT_CLEAR 0x5038
+#define MAINUNIT_2_INT_FORCE 0x503c
+#define PHYCREG_CR_READ_DONE BIT(11)
+#define PHYCREG_CR_WRITE_DONE BIT(10)
+#define TMDSVALID_STABLE_CHG BIT(1)
+
+#define AVPUNIT_0_INT_STATUS 0x5040
+#define AVPUNIT_0_INT_MASK_N 0x5044
+#define AVPUNIT_0_INT_CLEAR 0x5048
+#define AVPUNIT_0_INT_FORCE 0x504c
+#define CED_DYN_CNT_CH2_IRQ BIT(22)
+#define CED_DYN_CNT_CH1_IRQ BIT(21)
+#define CED_DYN_CNT_CH0_IRQ BIT(20)
+#define AVPUNIT_1_INT_STATUS 0x5050
+#define DEFRAMER_VSYNC_THR_REACHED_IRQ BIT(1)
+#define AVPUNIT_1_INT_MASK_N 0x5054
+#define DEFRAMER_VSYNC_THR_REACHED_MASK_N BIT(1)
+#define DEFRAMER_VSYNC_MASK_N BIT(0)
+#define AVPUNIT_1_INT_CLEAR 0x5058
+#define DEFRAMER_VSYNC_THR_REACHED_CLEAR BIT(1)
+#define PKT_0_INT_STATUS 0x5080
+#define PKTDEC_ACR_CHG_IRQ BIT(3)
+#define PKT_0_INT_MASK_N 0x5084
+#define PKTDEC_ACR_CHG_MASK_N BIT(3)
+#define PKT_0_INT_CLEAR 0x5088
+#define PKT_1_INT_STATUS 0x5090
+#define PKT_1_INT_MASK_N 0x5094
+#define PKT_1_INT_CLEAR 0x5098
+#define PKT_2_INT_STATUS 0x50a0
+#define PKTDEC_ACR_RCV_IRQ BIT(3)
+#define PKT_2_INT_MASK_N 0x50a4
+#define PKTDEC_AVIIF_RCV_IRQ BIT(11)
+#define PKTDEC_ACR_RCV_MASK_N BIT(3)
+#define PKT_2_INT_CLEAR 0x50a8
+#define PKTDEC_AVIIF_RCV_CLEAR BIT(11)
+#define PKTDEC_ACR_RCV_CLEAR BIT(3)
+#define SCDC_INT_STATUS 0x50c0
+#define SCDC_INT_MASK_N 0x50c4
+#define SCDC_INT_CLEAR 0x50c8
+#define SCDCTMDSCCFG_CHG BIT(2)
+
+#define CEC_INT_STATUS 0x5100
+#define CEC_INT_MASK_N 0x5104
+#define CEC_INT_CLEAR 0x5108
+
+#endif
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
new file mode 100644
index 000000000000..8e470c0376d6
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chips.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/cec.h>
+
+#include "snps_hdmirx.h"
+#include "snps_hdmirx_cec.h"
+
+static void hdmirx_cec_write(struct hdmirx_cec *cec, int reg, u32 val)
+{
+ cec->ops->write(cec->hdmirx, reg, val);
+}
+
+static u32 hdmirx_cec_read(struct hdmirx_cec *cec, int reg)
+{
+ return cec->ops->read(cec->hdmirx, reg);
+}
+
+static void hdmirx_cec_update_bits(struct hdmirx_cec *cec, int reg, u32 mask,
+ u32 data)
+{
+ u32 val = hdmirx_cec_read(cec, reg) & ~mask;
+
+ val |= (data & mask);
+ hdmirx_cec_write(cec, reg, val);
+}
+
+static int hdmirx_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ cec->addresses = 0;
+ else
+ cec->addresses |= BIT(logical_addr) | BIT(15);
+
+ hdmirx_cec_write(cec, CEC_ADDR, cec->addresses);
+
+ return 0;
+}
+
+/* signal_free_time is handled by the Synopsys Designware
+ * HDMIRX Controller hardware.
+ */
+static int hdmirx_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+ u32 data[4] = {0};
+ int i, data_len, msg_len;
+
+ msg_len = msg->len;
+
+ hdmirx_cec_write(cec, CEC_TX_COUNT, msg_len - 1);
+ for (i = 0; i < msg_len; i++)
+ data[i / 4] |= msg->msg[i] << (i % 4) * 8;
+
+ data_len = DIV_ROUND_UP(msg_len, 4);
+
+ for (i = 0; i < data_len; i++)
+ hdmirx_cec_write(cec, CEC_TX_DATA3_0 + i * 4, data[i]);
+
+ hdmirx_cec_write(cec, CEC_TX_CONTROL, 0x1);
+
+ return 0;
+}
+
+static irqreturn_t hdmirx_cec_hardirq(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+ u32 stat = hdmirx_cec_read(cec, CEC_INT_STATUS);
+ irqreturn_t ret = IRQ_HANDLED;
+ u32 val;
+
+ if (!stat)
+ return IRQ_NONE;
+
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, stat);
+
+ if (stat & CECTX_LINE_ERR) {
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_DONE) {
+ cec->tx_status = CEC_TX_STATUS_OK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_NACK) {
+ cec->tx_status = CEC_TX_STATUS_NACK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_ARBLOST) {
+ cec->tx_status = CEC_TX_STATUS_ARB_LOST;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (stat & CECRX_EOM) {
+ unsigned int len, i;
+
+ val = hdmirx_cec_read(cec, CEC_RX_COUNT_STATUS);
+ /* rxbuffer locked status */
+ if ((val & 0x80))
+ return ret;
+
+ len = (val & 0xf) + 1;
+ if (len > sizeof(cec->rx_msg.msg))
+ len = sizeof(cec->rx_msg.msg);
+
+ for (i = 0; i < len; i++) {
+ if (!(i % 4))
+ val = hdmirx_cec_read(cec, CEC_RX_DATA3_0 + i / 4 * 4);
+ cec->rx_msg.msg[i] = (val >> ((i % 4) * 8)) & 0xff;
+ }
+
+ cec->rx_msg.len = len;
+ smp_wmb(); /* receive RX msg */
+ cec->rx_done = true;
+ hdmirx_cec_write(cec, CEC_LOCK_CONTROL, 0x1);
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static irqreturn_t hdmirx_cec_thread(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (cec->tx_done) {
+ cec->tx_done = false;
+ cec_transmit_attempt_done(adap, cec->tx_status);
+ }
+ if (cec->rx_done) {
+ cec->rx_done = false;
+ smp_rmb(); /* RX msg has been received */
+ cec_received_msg(adap, &cec->rx_msg);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hdmirx_cec_enable(struct cec_adapter *adap, bool enable)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (!enable) {
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, 0);
+ if (cec->ops->disable)
+ cec->ops->disable(cec->hdmirx);
+ } else {
+ unsigned int irqs;
+
+ hdmirx_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+ if (cec->ops->enable)
+ cec->ops->enable(cec->hdmirx);
+ hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
+
+ irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
+ }
+
+ return 0;
+}
+
+static const struct cec_adap_ops hdmirx_cec_ops = {
+ .adap_enable = hdmirx_cec_enable,
+ .adap_log_addr = hdmirx_cec_log_addr,
+ .adap_transmit = hdmirx_cec_transmit,
+};
+
+static void hdmirx_cec_del(void *data)
+{
+ struct hdmirx_cec *cec = data;
+
+ cec_delete_adapter(cec->adap);
+}
+
+struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data)
+{
+ struct hdmirx_cec *cec;
+ unsigned int irqs;
+ int ret;
+
+ /*
+ * Our device is just a convenience - we want to link to the real
+ * hardware device here, so that userspace can see the association
+ * between the HDMI hardware and its associated CEC chardev.
+ */
+ cec = devm_kzalloc(data->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return ERR_PTR(-ENOMEM);
+
+ cec->dev = data->dev;
+ cec->irq = data->irq;
+ cec->ops = data->ops;
+ cec->hdmirx = data->hdmirx;
+
+ hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
+ hdmirx_cec_update_bits(cec, CEC_CONFIG, RX_AUTO_DRIVE_ACKNOWLEDGE,
+ RX_AUTO_DRIVE_ACKNOWLEDGE);
+
+ hdmirx_cec_write(cec, CEC_TX_COUNT, 0);
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, ~0);
+
+ cec->adap = cec_allocate_adapter(&hdmirx_cec_ops, cec, "snps-hdmirx",
+ CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(cec->adap)) {
+ dev_err(cec->dev, "cec adapter allocation failed\n");
+ return ERR_CAST(cec->adap);
+ }
+
+ /* override the module pointer */
+ cec->adap->owner = THIS_MODULE;
+
+ ret = devm_add_action(cec->dev, hdmirx_cec_del, cec);
+ if (ret) {
+ cec_delete_adapter(cec->adap);
+ return ERR_PTR(ret);
+ }
+
+ irq_set_status_flags(cec->irq, IRQ_NOAUTOEN);
+
+ ret = devm_request_threaded_irq(cec->dev, cec->irq,
+ hdmirx_cec_hardirq,
+ hdmirx_cec_thread, IRQF_ONESHOT,
+ "rk_hdmirx_cec", cec->adap);
+ if (ret) {
+ dev_err(cec->dev, "cec irq request failed\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = cec_register_adapter(cec->adap, cec->dev);
+ if (ret < 0) {
+ dev_err(cec->dev, "cec adapter registration failed\n");
+ return ERR_PTR(ret);
+ }
+
+ irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
+
+ /*
+ * CEC documentation says we must not call cec_delete_adapter
+ * after a successful call to cec_register_adapter().
+ */
+ devm_remove_action(cec->dev, hdmirx_cec_del, cec);
+
+ enable_irq(cec->irq);
+
+ return cec;
+}
+
+void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec)
+{
+ disable_irq(cec->irq);
+
+ cec_unregister_adapter(cec->adap);
+}
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
new file mode 100644
index 000000000000..1b10da5b8fd4
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chips.com>
+ */
+
+#ifndef DW_HDMI_RX_CEC_H
+#define DW_HDMI_RX_CEC_H
+
+struct snps_hdmirx_dev;
+
+struct hdmirx_cec_ops {
+ void (*write)(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val);
+ u32 (*read)(struct snps_hdmirx_dev *hdmirx_dev, int reg);
+ void (*enable)(struct snps_hdmirx_dev *hdmirx);
+ void (*disable)(struct snps_hdmirx_dev *hdmirx);
+};
+
+struct hdmirx_cec_data {
+ struct snps_hdmirx_dev *hdmirx;
+ const struct hdmirx_cec_ops *ops;
+ struct device *dev;
+ int irq;
+};
+
+struct hdmirx_cec {
+ struct snps_hdmirx_dev *hdmirx;
+ struct device *dev;
+ const struct hdmirx_cec_ops *ops;
+ u32 addresses;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+ unsigned int tx_status;
+ bool tx_done;
+ bool rx_done;
+ int irq;
+};
+
+struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data);
+void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec);
+
+#endif /* DW_HDMI_RX_CEC_H */
diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c
index 44cdccb89377..1ca559df7e59 100644
--- a/drivers/media/platform/ti/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c
@@ -2030,7 +2030,7 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline *
vpfe->fmt.fmt.pix.height;
- vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n",
+ vpfe_dbg(1, vpfe, "cropped (%d,%d)/%ux%u of %dx%d\n",
r.left, r.top, r.width, r.height, cr.width, cr.height);
return 0;
diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c
index 42dfe08b765f..00a71dac0ff4 100644
--- a/drivers/media/platform/ti/cal/cal-camerarx.c
+++ b/drivers/media/platform/ti/cal/cal-camerarx.c
@@ -49,23 +49,41 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy)
{
struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2;
u32 num_lanes = mipi_csi2->num_data_lanes;
- const struct cal_format_info *fmtinfo;
struct v4l2_subdev_state *state;
- struct v4l2_mbus_framefmt *fmt;
u32 bpp;
s64 freq;
+ /*
+ * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back
+ * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available.
+ *
+ * With multistream input there is no single pixel rate, and thus we
+ * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which
+ * causes v4l2_get_link_freq() to return an error if it falls back to
+ * V4L2_CID_PIXEL_RATE.
+ */
+
state = v4l2_subdev_get_locked_active_state(&phy->subdev);
- fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK);
+ if (state->routing.num_routes > 1) {
+ bpp = 0;
+ } else {
+ struct v4l2_subdev_route *route = &state->routing.routes[0];
+ const struct cal_format_info *fmtinfo;
+ struct v4l2_mbus_framefmt *fmt;
- fmtinfo = cal_format_by_code(fmt->code);
- if (!fmtinfo)
- return -EINVAL;
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
- bpp = fmtinfo->bpp;
+ fmtinfo = cal_format_by_code(fmt->code);
+ if (!fmtinfo)
+ return -EINVAL;
- freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes);
+ bpp = fmtinfo->bpp;
+ }
+
+ freq = v4l2_get_link_freq(&phy->source->entity.pads[phy->source_pad],
+ bpp, 2 * num_lanes);
if (freq < 0) {
phy_err(phy, "failed to get link freq for subdev '%s'\n",
phy->source->name);
@@ -284,15 +302,32 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy)
0, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
}
-static int cal_camerarx_start(struct cal_camerarx *phy)
+static int cal_camerarx_start(struct cal_camerarx *phy, u32 sink_stream)
{
+ struct media_pad *remote_pad;
s64 link_freq;
u32 sscounter;
u32 val;
int ret;
+ remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);
+
+ /*
+ * We need to enable the PHY hardware when enabling the first stream,
+ * but for the following streams we just propagate the enable_streams
+ * to the source.
+ */
+
if (phy->enable_count > 0) {
+ ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index,
+ BIT(sink_stream));
+ if (ret) {
+ phy_err(phy, "enable streams failed in source: %d\n", ret);
+ return ret;
+ }
+
phy->enable_count++;
+
return 0;
}
@@ -394,7 +429,8 @@ static int cal_camerarx_start(struct cal_camerarx *phy)
* Start the source to enable the CSI-2 HS clock. We can now wait for
* CSI-2 PHY reset to complete.
*/
- ret = v4l2_subdev_call(phy->source, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index,
+ BIT(sink_stream));
if (ret) {
v4l2_subdev_call(phy->source, core, s_power, 0);
cal_camerarx_disable_irqs(phy);
@@ -425,12 +461,22 @@ static int cal_camerarx_start(struct cal_camerarx *phy)
return 0;
}
-static void cal_camerarx_stop(struct cal_camerarx *phy)
+static void cal_camerarx_stop(struct cal_camerarx *phy, u32 sink_stream)
{
+ struct media_pad *remote_pad;
int ret;
- if (--phy->enable_count > 0)
+ remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);
+
+ if (--phy->enable_count > 0) {
+ ret = v4l2_subdev_disable_streams(phy->source,
+ remote_pad->index,
+ BIT(sink_stream));
+ if (ret)
+ phy_err(phy, "stream off failed in subdev\n");
+
return;
+ }
cal_camerarx_ppi_disable(phy);
@@ -450,7 +496,9 @@ static void cal_camerarx_stop(struct cal_camerarx *phy)
/* Disable the phy */
cal_camerarx_disable(phy);
- if (v4l2_subdev_call(phy->source, video, s_stream, 0))
+ ret = v4l2_subdev_disable_streams(phy->source, remote_pad->index,
+ BIT(sink_stream));
+ if (ret)
phy_err(phy, "stream off failed in subdev\n");
ret = v4l2_subdev_call(phy->source, core, s_power, 0);
@@ -599,22 +647,50 @@ static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd)
return container_of(sd, struct cal_camerarx, subdev);
}
-static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable)
+struct cal_camerarx *
+cal_camerarx_get_phy_from_entity(struct media_entity *entity)
+{
+ struct v4l2_subdev *sd;
+
+ sd = media_entity_to_v4l2_subdev(entity);
+ if (!sd)
+ return NULL;
+
+ return to_cal_camerarx(sd);
+}
+
+static int cal_camerarx_sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
{
struct cal_camerarx *phy = to_cal_camerarx(sd);
- struct v4l2_subdev_state *state;
- int ret = 0;
+ u32 sink_stream;
+ int ret;
- state = v4l2_subdev_lock_and_get_active_state(sd);
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ NULL, &sink_stream);
+ if (ret)
+ return ret;
- if (enable)
- ret = cal_camerarx_start(phy);
- else
- cal_camerarx_stop(phy);
+ return cal_camerarx_start(phy, sink_stream);
+}
- v4l2_subdev_unlock_state(state);
+static int cal_camerarx_sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct cal_camerarx *phy = to_cal_camerarx(sd);
+ u32 sink_stream;
+ int ret;
- return ret;
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ NULL, &sink_stream);
+ if (ret)
+ return ret;
+
+ cal_camerarx_stop(phy, sink_stream);
+
+ return 0;
}
static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
@@ -628,8 +704,12 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index > 0)
return -EINVAL;
- fmt = v4l2_subdev_state_get_format(state,
- CAL_CAMERARX_PAD_SINK);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ code->pad,
+ code->stream);
+ if (!fmt)
+ return -EINVAL;
+
code->code = fmt->code;
} else {
if (code->index >= cal_num_formats)
@@ -654,8 +734,12 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd,
if (cal_rx_pad_is_source(fse->pad)) {
struct v4l2_mbus_framefmt *fmt;
- fmt = v4l2_subdev_state_get_format(state,
- CAL_CAMERARX_PAD_SINK);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ fse->pad,
+ fse->stream);
+ if (!fmt)
+ return -EINVAL;
+
if (fse->code != fmt->code)
return -EINVAL;
@@ -711,36 +795,77 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd,
/* Store the format and propagate it to the source pad. */
- fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK);
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
*fmt = format->format;
- fmt = v4l2_subdev_state_get_format(state,
- CAL_CAMERARX_PAD_FIRST_SOURCE);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
*fmt = format->format;
return 0;
}
+static int cal_camerarx_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ static const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cal_camerarx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ return cal_camerarx_set_routing(sd, state, routing);
+}
+
static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
- struct v4l2_subdev_format format = {
- .which = state ? V4L2_SUBDEV_FORMAT_TRY
- : V4L2_SUBDEV_FORMAT_ACTIVE,
- .pad = CAL_CAMERARX_PAD_SINK,
- .format = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .ycbcr_enc = V4L2_YCBCR_ENC_601,
- .quantization = V4L2_QUANTIZATION_LIM_RANGE,
- .xfer_func = V4L2_XFER_FUNC_SRGB,
- },
+ struct v4l2_subdev_route routes[] = { {
+ .sink_pad = 0,
+ .sink_stream = 0,
+ .source_pad = 1,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ } };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = 1,
+ .routes = routes,
};
- return cal_camerarx_sd_set_fmt(sd, state, &format);
+ /* Initialize routing to single route to the fist source pad */
+ return cal_camerarx_set_routing(sd, state, &routing);
}
static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
@@ -749,48 +874,71 @@ static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
struct cal_camerarx *phy = to_cal_camerarx(sd);
struct v4l2_mbus_frame_desc remote_desc;
const struct media_pad *remote_pad;
+ struct v4l2_subdev_state *state;
+ u32 sink_stream;
+ unsigned int i;
int ret;
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ pad, 0,
+ NULL, &sink_stream);
+ if (ret)
+ goto out_unlock;
+
remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);
- if (!remote_pad)
- return -EPIPE;
+ if (!remote_pad) {
+ ret = -EPIPE;
+ goto out_unlock;
+ }
ret = v4l2_subdev_call(phy->source, pad, get_frame_desc,
remote_pad->index, &remote_desc);
if (ret)
- return ret;
+ goto out_unlock;
if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
cal_err(phy->cal,
"Frame descriptor does not describe CSI-2 link");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_unlock;
}
- if (remote_desc.num_entries > 1)
- cal_err(phy->cal,
- "Multiple streams not supported in remote frame descriptor, using the first one\n");
+ for (i = 0; i < remote_desc.num_entries; i++) {
+ if (remote_desc.entry[i].stream == sink_stream)
+ break;
+ }
+
+ if (i == remote_desc.num_entries) {
+ cal_err(phy->cal, "Stream %u not found in remote frame desc\n",
+ sink_stream);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
fd->num_entries = 1;
- fd->entry[0] = remote_desc.entry[0];
+ fd->entry[0] = remote_desc.entry[i];
- return 0;
-}
+out_unlock:
+ v4l2_subdev_unlock_state(state);
-static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = {
- .s_stream = cal_camerarx_sd_s_stream,
-};
+ return ret;
+}
static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = {
+ .enable_streams = cal_camerarx_sd_enable_streams,
+ .disable_streams = cal_camerarx_sd_disable_streams,
.enum_mbus_code = cal_camerarx_sd_enum_mbus_code,
.enum_frame_size = cal_camerarx_sd_enum_frame_size,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = cal_camerarx_sd_set_fmt,
+ .set_routing = cal_camerarx_sd_set_routing,
.get_frame_desc = cal_camerarx_get_frame_desc,
};
static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = {
- .video = &cal_camerarx_video_ops,
.pad = &cal_camerarx_pad_ops,
};
@@ -800,6 +948,7 @@ static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = {
static const struct media_entity_operations cal_camerarx_media_ops = {
.link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
};
/* ------------------------------------------------------------------
@@ -851,7 +1000,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
v4l2_subdev_init(sd, &cal_camerarx_subdev_ops);
sd->internal_ops = &cal_camerarx_internal_ops;
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance);
sd->dev = cal->dev;
diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c
index e29743ae61e2..d40e24ab1127 100644
--- a/drivers/media/platform/ti/cal/cal-video.c
+++ b/drivers/media/platform/ti/cal/cal-video.c
@@ -25,20 +25,6 @@
#include "cal.h"
-/* Print Four-character-code (FOURCC) */
-static char *fourcc_to_str(u32 fmt)
-{
- static char code[5];
-
- code[0] = (unsigned char)(fmt & 0xff);
- code[1] = (unsigned char)((fmt >> 8) & 0xff);
- code[2] = (unsigned char)((fmt >> 16) & 0xff);
- code[3] = (unsigned char)((fmt >> 24) & 0xff);
- code[4] = '\0';
-
- return code;
-}
-
/* ------------------------------------------------------------------
* V4L2 Common IOCTLs
* ------------------------------------------------------------------
@@ -122,9 +108,10 @@ static int __subdev_get_format(struct cal_ctx *ctx,
.pad = 0,
};
struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ struct v4l2_subdev *sd = ctx->phy->source;
int ret;
- ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt);
+ ret = v4l2_subdev_call_state_active(sd, pad, get_fmt, &sd_fmt);
if (ret)
return ret;
@@ -144,11 +131,12 @@ static int __subdev_set_format(struct cal_ctx *ctx,
.pad = 0,
};
struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ struct v4l2_subdev *sd = ctx->phy->source;
int ret;
*mbus_fmt = *fmt;
- ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt);
+ ret = v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt);
if (ret)
return ret;
@@ -180,8 +168,8 @@ static void cal_calc_format_size(struct cal_ctx *ctx,
f->fmt.pix.sizeimage = f->fmt.pix.height *
f->fmt.pix.bytesperline;
- ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
- __func__, fourcc_to_str(f->fmt.pix.pixelformat),
+ ctx_dbg(3, ctx, "%s: fourcc: %p4cc size: %dx%d bpl:%d img_size:%d\n",
+ __func__, &f->fmt.pix.pixelformat,
f->fmt.pix.width, f->fmt.pix.height,
f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
}
@@ -190,6 +178,7 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = ctx->phy->source;
const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_size_enum fse = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
@@ -215,8 +204,8 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv,
for (fse.index = 0; ; fse.index++) {
int ret;
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size,
- NULL, &fse);
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_size,
+ &fse);
if (ret)
break;
@@ -252,6 +241,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = &ctx->phy->subdev;
struct vb2_queue *q = &ctx->vb_vidq;
struct v4l2_subdev_format sd_fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
@@ -291,7 +281,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv,
ctx->v_fmt.fmt.pix.field = sd_fmt.format.field;
cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
- v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt);
+ v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt);
ctx->fmtinfo = fmtinfo;
*f = ctx->v_fmt;
@@ -303,6 +293,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = ctx->phy->source;
const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_size_enum fse = {
.index = fsize->index,
@@ -321,8 +312,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh,
fse.code = fmtinfo->code;
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL,
- &fse);
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_size, &fse);
if (ret)
return ret;
@@ -364,6 +354,7 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv,
struct v4l2_frmivalenum *fival)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = ctx->phy->source;
const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_interval_enum fie = {
.index = fival->index,
@@ -378,8 +369,8 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv,
return -EINVAL;
fie.code = fmtinfo->code;
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval,
- NULL, &fie);
+
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_interval, &fie);
if (ret)
return ret;
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
@@ -446,6 +437,9 @@ static int cal_mc_enum_fmt_vid_cap(struct file *file, void *priv,
idx = 0;
for (i = 0; i < cal_num_formats; ++i) {
+ if (cal_formats[i].meta)
+ continue;
+
if (f->mbus_code && cal_formats[i].code != f->mbus_code)
continue;
@@ -473,7 +467,7 @@ static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f,
* supported.
*/
fmtinfo = cal_format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmtinfo)
+ if (!fmtinfo || fmtinfo->meta)
fmtinfo = &cal_formats[0];
/*
@@ -509,8 +503,8 @@ static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f,
if (info)
*info = fmtinfo;
- ctx_dbg(3, ctx, "%s: %s %ux%u (bytesperline %u sizeimage %u)\n",
- __func__, fourcc_to_str(format->pixelformat),
+ ctx_dbg(3, ctx, "%s: %p4cc %ux%u (bytesperline %u sizeimage %u)\n",
+ __func__, &format->pixelformat,
format->width, format->height,
format->bytesperline, format->sizeimage);
}
@@ -689,16 +683,16 @@ static int cal_video_check_format(struct cal_ctx *ctx)
{
const struct v4l2_mbus_framefmt *format;
struct v4l2_subdev_state *state;
- struct media_pad *remote_pad;
+ struct media_pad *phy_source_pad;
int ret = 0;
- remote_pad = media_pad_remote_pad_first(&ctx->pad);
- if (!remote_pad)
+ phy_source_pad = media_pad_remote_pad_first(&ctx->pad);
+ if (!phy_source_pad)
return -ENODEV;
state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev);
- format = v4l2_subdev_state_get_format(state, remote_pad->index);
+ format = v4l2_subdev_state_get_format(state, phy_source_pad->index, 0);
if (!format) {
ret = -EINVAL;
goto out;
@@ -721,16 +715,28 @@ out:
static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct media_pad *phy_source_pad;
struct cal_buffer *buf;
dma_addr_t addr;
int ret;
+ phy_source_pad = media_pad_remote_pad_first(&ctx->pad);
+ if (!phy_source_pad) {
+ ctx_err(ctx, "Context not connected\n");
+ ret = -ENODEV;
+ goto error_release_buffers;
+ }
+
ret = video_device_pipeline_alloc_start(&ctx->vdev);
if (ret < 0) {
ctx_err(ctx, "Failed to start media pipeline: %d\n", ret);
goto error_release_buffers;
}
+ /* Find the PHY connected to this video device */
+ if (cal_mc_api)
+ ctx->phy = cal_camerarx_get_phy_from_entity(phy_source_pad->entity);
+
/*
* Verify that the currently configured format matches the output of
* the connected CAMERARX.
@@ -758,12 +764,13 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
ret = pm_runtime_resume_and_get(ctx->cal->dev);
if (ret < 0)
- goto error_pipeline;
+ goto error_unprepare;
cal_ctx_set_dma_addr(ctx, addr);
cal_ctx_start(ctx);
- ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(&ctx->phy->subdev,
+ phy_source_pad->index, BIT(0));
if (ret)
goto error_stop;
@@ -775,8 +782,8 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
error_stop:
cal_ctx_stop(ctx);
pm_runtime_put_sync(ctx->cal->dev);
+error_unprepare:
cal_ctx_unprepare(ctx);
-
error_pipeline:
video_device_pipeline_stop(&ctx->vdev);
error_release_buffers:
@@ -788,10 +795,14 @@ error_release_buffers:
static void cal_stop_streaming(struct vb2_queue *vq)
{
struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct media_pad *phy_source_pad;
cal_ctx_stop(ctx);
- v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0);
+ phy_source_pad = media_pad_remote_pad_first(&ctx->pad);
+
+ v4l2_subdev_disable_streams(&ctx->phy->subdev, phy_source_pad->index,
+ BIT(0));
pm_runtime_put_sync(ctx->cal->dev);
@@ -800,6 +811,9 @@ static void cal_stop_streaming(struct vb2_queue *vq)
cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
video_device_pipeline_stop(&ctx->vdev);
+
+ if (cal_mc_api)
+ ctx->phy = NULL;
}
static const struct vb2_ops cal_video_qops = {
@@ -826,6 +840,7 @@ static const struct v4l2_file_operations cal_fops = {
static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
{
+ struct v4l2_subdev *sd = ctx->phy->source;
struct v4l2_mbus_framefmt mbus_fmt;
const struct cal_format_info *fmtinfo;
unsigned int i, j, k;
@@ -845,20 +860,20 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code,
- NULL, &mbus_code);
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_mbus_code,
+ &mbus_code);
if (ret == -EINVAL)
break;
if (ret) {
ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n",
- ctx->phy->source->name, ret);
+ sd->name, ret);
return ret;
}
ctx_dbg(2, ctx,
"subdev %s: code: %04x idx: %u\n",
- ctx->phy->source->name, mbus_code.code, j);
+ sd->name, mbus_code.code, j);
for (k = 0; k < cal_num_formats; k++) {
fmtinfo = &cal_formats[k];
@@ -866,8 +881,8 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
if (mbus_code.code == fmtinfo->code) {
ctx->active_fmt[i] = fmtinfo;
ctx_dbg(2, ctx,
- "matched fourcc: %s: code: %04x idx: %u\n",
- fourcc_to_str(fmtinfo->fourcc),
+ "matched fourcc: %p4cc: code: %04x idx: %u\n",
+ &fmtinfo->fourcc,
fmtinfo->code, i);
ctx->num_active_fmt = ++i;
}
@@ -876,7 +891,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
if (i == 0) {
ctx_err(ctx, "No suitable format reported by subdev %s\n",
- ctx->phy->source->name);
+ sd->name);
return -EINVAL;
}
@@ -962,16 +977,52 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx)
return ret;
}
- ret = media_create_pad_link(&ctx->phy->subdev.entity,
- CAL_CAMERARX_PAD_FIRST_SOURCE,
- &vfd->entity, 0,
- MEDIA_LNK_FL_IMMUTABLE |
- MEDIA_LNK_FL_ENABLED);
- if (ret) {
- ctx_err(ctx, "Failed to create media link for context %u\n",
- ctx->dma_ctx);
- video_unregister_device(vfd);
- return ret;
+ if (cal_mc_api) {
+ u16 phy_idx;
+ u16 pad_idx;
+
+ /* Create links from all video nodes to all PHYs */
+
+ for (phy_idx = 0; phy_idx < ctx->cal->data->num_csi2_phy;
+ ++phy_idx) {
+ struct media_entity *phy_entity =
+ &ctx->cal->phy[phy_idx]->subdev.entity;
+
+ for (pad_idx = 1; pad_idx < CAL_CAMERARX_NUM_PADS;
+ ++pad_idx) {
+ /*
+ * Enable only links from video0 to PHY0 pad 1,
+ * and video1 to PHY1 pad 1.
+ */
+ bool enable = (ctx->dma_ctx == 0 &&
+ phy_idx == 0 && pad_idx == 1) ||
+ (ctx->dma_ctx == 1 &&
+ phy_idx == 1 && pad_idx == 1);
+
+ ret = media_create_pad_link(phy_entity, pad_idx,
+ &vfd->entity, 0,
+ enable ? MEDIA_LNK_FL_ENABLED : 0);
+ if (ret) {
+ ctx_err(ctx,
+ "Failed to create media link for context %u\n",
+ ctx->dma_ctx);
+ video_unregister_device(vfd);
+ return ret;
+ }
+ }
+ }
+ } else {
+ ret = media_create_pad_link(&ctx->phy->subdev.entity,
+ CAL_CAMERARX_PAD_FIRST_SOURCE,
+ &vfd->entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ ctx_err(ctx,
+ "Failed to create media link for context %u\n",
+ ctx->dma_ctx);
+ video_unregister_device(vfd);
+ return ret;
+ }
}
ctx_info(ctx, "V4L2 device registered as %s\n",
diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c
index 4bd2092e0255..b644ed890412 100644
--- a/drivers/media/platform/ti/cal/cal.c
+++ b/drivers/media/platform/ti/cal/cal.c
@@ -481,8 +481,9 @@ int cal_ctx_prepare(struct cal_ctx *ctx)
ctx->vc = 0;
ctx->datatype = CAL_CSI2_CTX_DT_ANY;
} else if (!ret) {
- ctx_dbg(2, ctx, "Framedesc: len %u, vc %u, dt %#x\n",
- entry.length, entry.bus.csi2.vc, entry.bus.csi2.dt);
+ ctx_dbg(2, ctx, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
+ entry.stream, entry.length, entry.bus.csi2.vc,
+ entry.bus.csi2.dt);
ctx->vc = entry.bus.csi2.vc;
ctx->datatype = entry.bus.csi2.dt;
@@ -490,7 +491,7 @@ int cal_ctx_prepare(struct cal_ctx *ctx)
return ret;
}
- ctx->use_pix_proc = !ctx->fmtinfo->meta;
+ ctx->use_pix_proc = ctx->vb_vidq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ctx->use_pix_proc) {
ret = cal_reserve_pix_proc(ctx->cal);
@@ -798,7 +799,6 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
return 0;
}
- phy->source = subdev;
phy_dbg(1, phy, "Using source %s for capture\n", subdev->name);
pad = media_entity_get_fwnode_pad(&subdev->entity,
@@ -820,6 +820,9 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
return ret;
}
+ phy->source = subdev;
+ phy->source_pad = pad;
+
return 0;
}
@@ -1014,7 +1017,6 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst)
return NULL;
ctx->cal = cal;
- ctx->phy = cal->phy[inst];
ctx->dma_ctx = inst;
ctx->csi2_ctx = inst;
ctx->cport = inst;
@@ -1226,18 +1228,37 @@ static int cal_probe(struct platform_device *pdev)
}
/* Create contexts. */
- for (i = 0; i < cal->data->num_csi2_phy; ++i) {
- if (!cal->phy[i]->source_node)
- continue;
+ if (!cal_mc_api) {
+ for (i = 0; i < cal->data->num_csi2_phy; ++i) {
+ struct cal_ctx *ctx;
+
+ if (!cal->phy[i]->source_node)
+ continue;
- cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i);
- if (!cal->ctx[cal->num_contexts]) {
- cal_err(cal, "Failed to create context %u\n", cal->num_contexts);
- ret = -ENODEV;
- goto error_context;
+ ctx = cal_ctx_create(cal, i);
+ if (!ctx) {
+ cal_err(cal, "Failed to create context %u\n", cal->num_contexts);
+ ret = -ENODEV;
+ goto error_context;
+ }
+
+ ctx->phy = cal->phy[i];
+
+ cal->ctx[cal->num_contexts++] = ctx;
}
+ } else {
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
+ struct cal_ctx *ctx;
+
+ ctx = cal_ctx_create(cal, i);
+ if (!ctx) {
+ cal_err(cal, "Failed to create context %u\n", i);
+ ret = -ENODEV;
+ goto error_context;
+ }
- cal->num_contexts++;
+ cal->ctx[cal->num_contexts++] = ctx;
+ }
}
/* Register the media device. */
diff --git a/drivers/media/platform/ti/cal/cal.h b/drivers/media/platform/ti/cal/cal.h
index 0856297adc0b..33b1885aafe1 100644
--- a/drivers/media/platform/ti/cal/cal.h
+++ b/drivers/media/platform/ti/cal/cal.h
@@ -45,7 +45,7 @@
#define CAL_CAMERARX_PAD_SINK 0
#define CAL_CAMERARX_PAD_FIRST_SOURCE 1
-#define CAL_CAMERARX_NUM_SOURCE_PADS 1
+#define CAL_CAMERARX_NUM_SOURCE_PADS 8
#define CAL_CAMERARX_NUM_PADS (1 + CAL_CAMERARX_NUM_SOURCE_PADS)
static inline bool cal_rx_pad_is_sink(u32 pad)
@@ -174,6 +174,7 @@ struct cal_camerarx {
struct device_node *source_ep_node;
struct device_node *source_node;
struct v4l2_subdev *source;
+ unsigned int source_pad;
struct v4l2_subdev subdev;
struct media_pad pads[CAL_CAMERARX_NUM_PADS];
@@ -319,6 +320,7 @@ const struct cal_format_info *cal_format_by_code(u32 code);
void cal_quickdump_regs(struct cal_dev *cal);
+struct cal_camerarx *cal_camerarx_get_phy_from_entity(struct media_entity *entity);
void cal_camerarx_disable(struct cal_camerarx *phy);
void cal_camerarx_i913_errata(struct cal_camerarx *phy);
struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
diff --git a/drivers/media/platform/ti/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c
index a81719702a22..969d623fc842 100644
--- a/drivers/media/platform/ti/davinci/vpif.c
+++ b/drivers/media/platform/ti/davinci/vpif.c
@@ -504,7 +504,7 @@ static int vpif_probe(struct platform_device *pdev)
pdev_display = kzalloc(sizeof(*pdev_display), GFP_KERNEL);
if (!pdev_display) {
ret = -ENOMEM;
- goto err_put_pdev_capture;
+ goto err_del_pdev_capture;
}
pdev_display->name = "vpif_display";
@@ -527,6 +527,8 @@ static int vpif_probe(struct platform_device *pdev)
err_put_pdev_display:
platform_device_put(pdev_display);
+err_del_pdev_capture:
+ platform_device_del(pdev_capture);
err_put_pdev_capture:
platform_device_put(pdev_capture);
err_put_rpm:
diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c
index 405ca215179d..f51cf6119e97 100644
--- a/drivers/media/platform/ti/omap3isp/isp.c
+++ b/drivers/media/platform/ti/omap3isp/isp.c
@@ -1475,43 +1475,6 @@ void omap3isp_put(struct isp_device *isp)
* Platform device driver
*/
-/*
- * omap3isp_print_status - Prints the values of the ISP Control Module registers
- * @isp: OMAP3 ISP device
- */
-#define ISP_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name))
-#define SBL_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name))
-
-void omap3isp_print_status(struct isp_device *isp)
-{
- dev_dbg(isp->dev, "-------------ISP Register dump--------------\n");
-
- ISP_PRINT_REGISTER(isp, SYSCONFIG);
- ISP_PRINT_REGISTER(isp, SYSSTATUS);
- ISP_PRINT_REGISTER(isp, IRQ0ENABLE);
- ISP_PRINT_REGISTER(isp, IRQ0STATUS);
- ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY);
- ISP_PRINT_REGISTER(isp, CTRL);
- ISP_PRINT_REGISTER(isp, TCTRL_CTRL);
- ISP_PRINT_REGISTER(isp, TCTRL_FRAME);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH);
-
- SBL_PRINT_REGISTER(isp, PCR);
- SBL_PRINT_REGISTER(isp, SDR_REQ_EXP);
-
- dev_dbg(isp->dev, "--------------------------------------------\n");
-}
-
#ifdef CONFIG_PM
/*
@@ -1961,6 +1924,13 @@ static int isp_attach_iommu(struct isp_device *isp)
struct dma_iommu_mapping *mapping;
int ret;
+ /* We always want to replace any default mapping from the arch code */
+ mapping = to_dma_iommu_mapping(isp->dev);
+ if (mapping) {
+ arm_iommu_detach_device(isp->dev);
+ arm_iommu_release_mapping(mapping);
+ }
+
/*
* Create the ARM mapping, used by the ARM DMA mapping core to allocate
* VAs. This will allocate a corresponding IOMMU domain.
@@ -2272,18 +2242,14 @@ static int isp_probe(struct platform_device *pdev)
if (ret)
goto error_release_isp;
- isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
- "syscon");
+ isp->syscon = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node,
+ "syscon", 1,
+ &isp->syscon_offset);
if (IS_ERR(isp->syscon)) {
ret = PTR_ERR(isp->syscon);
goto error_release_isp;
}
- ret = of_property_read_u32_index(pdev->dev.of_node,
- "syscon", 1, &isp->syscon_offset);
- if (ret)
- goto error_release_isp;
-
isp->autoidle = autoidle;
mutex_init(&isp->isp_mutex);
diff --git a/drivers/media/platform/ti/omap3isp/isp.h b/drivers/media/platform/ti/omap3isp/isp.h
index b4793631ad97..60acf3401ac9 100644
--- a/drivers/media/platform/ti/omap3isp/isp.h
+++ b/drivers/media/platform/ti/omap3isp/isp.h
@@ -260,8 +260,6 @@ void omap3isp_configure_bridge(struct isp_device *isp,
struct isp_device *omap3isp_get(struct isp_device *isp);
void omap3isp_put(struct isp_device *isp);
-void omap3isp_print_status(struct isp_device *isp);
-
void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res);
void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res);
diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c
index dd375c4e180d..7d0c723dcd11 100644
--- a/drivers/media/platform/ti/omap3isp/ispccdc.c
+++ b/drivers/media/platform/ti/omap3isp/ispccdc.c
@@ -446,8 +446,8 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
if (ret < 0)
goto done;
- dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl,
- req->table.sgt.nents, DMA_TO_DEVICE);
+ dma_sync_sgtable_for_cpu(isp->dev, &req->table.sgt,
+ DMA_TO_DEVICE);
if (copy_from_user(req->table.addr, config->lsc,
req->config.size)) {
@@ -455,8 +455,8 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
goto done;
}
- dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl,
- req->table.sgt.nents, DMA_TO_DEVICE);
+ dma_sync_sgtable_for_device(isp->dev, &req->table.sgt,
+ DMA_TO_DEVICE);
}
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
diff --git a/drivers/media/platform/ti/omap3isp/ispstat.c b/drivers/media/platform/ti/omap3isp/ispstat.c
index 359a846205b0..d3da68408ecb 100644
--- a/drivers/media/platform/ti/omap3isp/ispstat.c
+++ b/drivers/media/platform/ti/omap3isp/ispstat.c
@@ -161,8 +161,7 @@ static void isp_stat_buf_sync_for_device(struct ispstat *stat,
if (ISP_STAT_USES_DMAENGINE(stat))
return;
- dma_sync_sg_for_device(stat->isp->dev, buf->sgt.sgl,
- buf->sgt.nents, DMA_FROM_DEVICE);
+ dma_sync_sgtable_for_device(stat->isp->dev, &buf->sgt, DMA_FROM_DEVICE);
}
static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
@@ -171,8 +170,7 @@ static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
if (ISP_STAT_USES_DMAENGINE(stat))
return;
- dma_sync_sg_for_cpu(stat->isp->dev, buf->sgt.sgl,
- buf->sgt.nents, DMA_FROM_DEVICE);
+ dma_sync_sgtable_for_cpu(stat->isp->dev, &buf->sgt, DMA_FROM_DEVICE);
}
static void isp_stat_buf_clear(struct ispstat *stat)
diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c
index 5c9aa80023fd..78e30298c7ad 100644
--- a/drivers/media/platform/ti/omap3isp/ispvideo.c
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.c
@@ -480,29 +480,11 @@ static int isp_video_start_streaming(struct vb2_queue *queue,
return 0;
}
-static void omap3isp_wait_prepare(struct vb2_queue *vq)
-{
- struct isp_video_fh *vfh = vb2_get_drv_priv(vq);
- struct isp_video *video = vfh->video;
-
- mutex_unlock(&video->queue_lock);
-}
-
-static void omap3isp_wait_finish(struct vb2_queue *vq)
-{
- struct isp_video_fh *vfh = vb2_get_drv_priv(vq);
- struct isp_video *video = vfh->video;
-
- mutex_lock(&video->queue_lock);
-}
-
static const struct vb2_ops isp_video_queue_ops = {
.queue_setup = isp_video_queue_setup,
.buf_prepare = isp_video_buffer_prepare,
.buf_queue = isp_video_buffer_queue,
.start_streaming = isp_video_start_streaming,
- .wait_prepare = omap3isp_wait_prepare,
- .wait_finish = omap3isp_wait_finish,
};
/*
@@ -1338,6 +1320,7 @@ static int isp_video_open(struct file *file)
queue->buf_struct_size = sizeof(struct isp_buffer);
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
queue->dev = video->isp->dev;
+ queue->lock = &video->queue_lock;
ret = vb2_queue_init(&handle->queue);
if (ret < 0) {
diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
index 85a44143b378..0e212198dd65 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
@@ -518,6 +518,7 @@ static void set_buffers(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &g2_stream_len, src_len);
hantro_reg_write(vpu, &g2_strm_buffer_len, src_buf_len);
hantro_reg_write(vpu, &g2_strm_start_offset, 0);
+ hantro_reg_write(vpu, &g2_start_bit, 0);
hantro_reg_write(vpu, &g2_write_mvs_e, 1);
hantro_write_addr(vpu, G2_TILE_SIZES_ADDR, ctx->hevc_dec.tile_sizes.dma);
diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
index 342e543dee4c..82a478ac645e 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
@@ -776,15 +776,15 @@ config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_para
struct vb2_v4l2_buffer *vb2_src)
{
dma_addr_t stream_base, tmp_addr;
- unsigned int headres_size;
+ unsigned int headers_size;
u32 src_len, start_bit, src_buf_len;
- headres_size = dec_params->uncompressed_header_size
+ headers_size = dec_params->uncompressed_header_size
+ dec_params->compressed_header_size;
stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0);
- tmp_addr = stream_base + headres_size;
+ tmp_addr = stream_base + headers_size;
if (ctx->dev->variant->legacy_regs)
hantro_write_addr(ctx->dev, G2_STREAM_ADDR, (tmp_addr & ~0xf));
else
@@ -794,7 +794,7 @@ config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_para
hantro_reg_write(ctx->dev, &g2_start_bit, start_bit);
src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0);
- src_len += start_bit / 8 - headres_size;
+ src_len += start_bit / 8 - headers_size;
hantro_reg_write(ctx->dev, &g2_stream_len, src_len);
if (!ctx->dev->variant->legacy_regs) {
diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c
index c435a393e0cb..9f559a13d409 100644
--- a/drivers/media/platform/verisilicon/hantro_postproc.c
+++ b/drivers/media/platform/verisilicon/hantro_postproc.c
@@ -250,8 +250,10 @@ int hantro_postproc_init(struct hantro_ctx *ctx)
for (i = 0; i < num_buffers; i++) {
ret = hantro_postproc_alloc(ctx, i);
- if (ret)
+ if (ret) {
+ hantro_postproc_free(ctx);
return ret;
+ }
}
return 0;
diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c
index 2bce940a5822..7c3515cf7d64 100644
--- a/drivers/media/platform/verisilicon/hantro_v4l2.c
+++ b/drivers/media/platform/verisilicon/hantro_v4l2.c
@@ -77,6 +77,7 @@ int hantro_get_format_depth(u32 fourcc)
switch (fourcc) {
case V4L2_PIX_FMT_P010:
case V4L2_PIX_FMT_P010_4L4:
+ case V4L2_PIX_FMT_NV15:
case V4L2_PIX_FMT_NV15_4L4:
return 10;
default:
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
index 69b5d9e12926..e4703bb6be7c 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
@@ -2202,6 +2202,10 @@ static void rockchip_vpu981_postproc_enable(struct hantro_ctx *ctx)
case V4L2_PIX_FMT_NV12:
hantro_reg_write(vpu, &av1_pp_out_format, 3);
break;
+ case V4L2_PIX_FMT_NV15:
+ /* this mapping is RK specific */
+ hantro_reg_write(vpu, &av1_pp_out_format, 10);
+ break;
default:
hantro_reg_write(vpu, &av1_pp_out_format, 0);
}
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index 964122e7c355..acd29fa41d2d 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -85,10 +85,24 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = {
.postprocessed = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
+ .step_height = MB_DIM,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV15,
+ .codec_mode = HANTRO_MODE_NONE,
+ .match_depth = true,
+ .postprocessed = true,
+ .frmsize = {
+ .min_width = ROCKCHIP_VPU981_MIN_SIZE,
+ .max_width = FMT_4K_WIDTH,
+ .step_width = MB_DIM,
+ .min_height = ROCKCHIP_VPU981_MIN_SIZE,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -99,10 +113,10 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = {
.postprocessed = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -318,10 +332,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = {
.match_depth = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -331,10 +345,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = {
.match_depth = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -344,10 +358,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = {
.max_depth = 2,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c
index cb93711ea3e3..7deec6e37edc 100644
--- a/drivers/media/platform/xilinx/xilinx-tpg.c
+++ b/drivers/media/platform/xilinx/xilinx-tpg.c
@@ -722,7 +722,6 @@ static int xtpg_parse_of(struct xtpg_device *xtpg)
format = xvip_of_get_format(port);
if (IS_ERR(format)) {
dev_err(dev, "invalid format in DT");
- of_node_put(port);
return PTR_ERR(format);
}
@@ -731,7 +730,6 @@ static int xtpg_parse_of(struct xtpg_device *xtpg)
xtpg->vip_format = format;
} else if (xtpg->vip_format != format) {
dev_err(dev, "in/out format mismatch in DT");
- of_node_put(port);
return -EINVAL;
}