aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig696
-rw-r--r--drivers/media/platform/Makefile110
-rw-r--r--drivers/media/platform/allegro-dvt/Kconfig19
-rw-r--r--drivers/media/platform/amlogic/Kconfig5
-rw-r--r--drivers/media/platform/amlogic/Makefile2
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/Kconfig14
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/Makefile (renamed from drivers/media/platform/meson/ge2d/Makefile)0
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/ge2d-regs.h (renamed from drivers/media/platform/meson/ge2d/ge2d-regs.h)0
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/ge2d.c (renamed from drivers/media/platform/meson/ge2d/ge2d.c)24
-rw-r--r--drivers/media/platform/amphion/Kconfig22
-rw-r--r--drivers/media/platform/amphion/Makefile20
-rw-r--r--drivers/media/platform/amphion/vdec.c1656
-rw-r--r--drivers/media/platform/amphion/venc.c1358
-rw-r--r--drivers/media/platform/amphion/vpu.h362
-rw-r--r--drivers/media/platform/amphion/vpu_cmds.c433
-rw-r--r--drivers/media/platform/amphion/vpu_cmds.h25
-rw-r--r--drivers/media/platform/amphion/vpu_codec.h68
-rw-r--r--drivers/media/platform/amphion/vpu_color.c183
-rw-r--r--drivers/media/platform/amphion/vpu_core.c879
-rw-r--r--drivers/media/platform/amphion/vpu_core.h15
-rw-r--r--drivers/media/platform/amphion/vpu_dbg.c494
-rw-r--r--drivers/media/platform/amphion/vpu_defs.h187
-rw-r--r--drivers/media/platform/amphion/vpu_drv.c261
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.c414
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.h71
-rw-r--r--drivers/media/platform/amphion/vpu_imx8q.c271
-rw-r--r--drivers/media/platform/amphion/vpu_imx8q.h115
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c1644
-rw-r--r--drivers/media/platform/amphion/vpu_malone.h44
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.c118
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.h16
-rw-r--r--drivers/media/platform/amphion/vpu_msgs.c385
-rw-r--r--drivers/media/platform/amphion/vpu_msgs.h14
-rw-r--r--drivers/media/platform/amphion/vpu_rpc.c259
-rw-r--r--drivers/media/platform/amphion/vpu_rpc.h461
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c713
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.h55
-rw-r--r--drivers/media/platform/amphion/vpu_windsor.c1173
-rw-r--r--drivers/media/platform/amphion/vpu_windsor.h37
-rw-r--r--drivers/media/platform/aspeed/Kconfig13
-rw-r--r--drivers/media/platform/aspeed/Makefile2
-rw-r--r--drivers/media/platform/aspeed/aspeed-video.c (renamed from drivers/media/platform/aspeed-video.c)301
-rw-r--r--drivers/media/platform/atmel/Kconfig28
-rw-r--r--drivers/media/platform/atmel/Makefile4
-rw-r--r--drivers/media/platform/atmel/atmel-isc-base.c397
-rw-r--r--drivers/media/platform/atmel/atmel-isc-clk.c311
-rw-r--r--drivers/media/platform/atmel/atmel-isc.h11
-rw-r--r--drivers/media/platform/atmel/atmel-sama5d2-isc.c24
-rw-r--r--drivers/media/platform/atmel/atmel-sama7g5-isc.c32
-rw-r--r--drivers/media/platform/atmel/microchip-csi2dc.c792
-rw-r--r--drivers/media/platform/cadence/Kconfig16
-rw-r--r--drivers/media/platform/chips-media/Kconfig20
-rw-r--r--drivers/media/platform/chips-media/Makefile (renamed from drivers/media/platform/coda/Makefile)0
-rw-r--r--drivers/media/platform/chips-media/coda-bit.c (renamed from drivers/media/platform/coda/coda-bit.c)0
-rw-r--r--drivers/media/platform/chips-media/coda-common.c (renamed from drivers/media/platform/coda/coda-common.c)1
-rw-r--r--drivers/media/platform/chips-media/coda-gdi.c (renamed from drivers/media/platform/coda/coda-gdi.c)0
-rw-r--r--drivers/media/platform/chips-media/coda-h264.c (renamed from drivers/media/platform/coda/coda-h264.c)0
-rw-r--r--drivers/media/platform/chips-media/coda-jpeg.c (renamed from drivers/media/platform/coda/coda-jpeg.c)0
-rw-r--r--drivers/media/platform/chips-media/coda-mpeg2.c (renamed from drivers/media/platform/coda/coda-mpeg2.c)0
-rw-r--r--drivers/media/platform/chips-media/coda-mpeg4.c (renamed from drivers/media/platform/coda/coda-mpeg4.c)0
-rw-r--r--drivers/media/platform/chips-media/coda.h (renamed from drivers/media/platform/coda/coda.h)0
-rw-r--r--drivers/media/platform/chips-media/coda_regs.h (renamed from drivers/media/platform/coda/coda_regs.h)2
-rw-r--r--drivers/media/platform/chips-media/imx-vdoa.c (renamed from drivers/media/platform/coda/imx-vdoa.c)9
-rw-r--r--drivers/media/platform/chips-media/imx-vdoa.h (renamed from drivers/media/platform/coda/imx-vdoa.h)0
-rw-r--r--drivers/media/platform/chips-media/trace.h (renamed from drivers/media/platform/coda/trace.h)2
-rw-r--r--drivers/media/platform/intel/Kconfig14
-rw-r--r--drivers/media/platform/intel/Makefile2
-rw-r--r--drivers/media/platform/intel/pxa_camera.c (renamed from drivers/media/platform/pxa_camera.c)21
-rw-r--r--drivers/media/platform/marvell/Kconfig (renamed from drivers/media/platform/marvell-ccic/Kconfig)9
-rw-r--r--drivers/media/platform/marvell/Makefile (renamed from drivers/media/platform/marvell-ccic/Makefile)0
-rw-r--r--drivers/media/platform/marvell/cafe-driver.c (renamed from drivers/media/platform/marvell-ccic/cafe-driver.c)0
-rw-r--r--drivers/media/platform/marvell/mcam-core.c (renamed from drivers/media/platform/marvell-ccic/mcam-core.c)0
-rw-r--r--drivers/media/platform/marvell/mcam-core.h (renamed from drivers/media/platform/marvell-ccic/mcam-core.h)0
-rw-r--r--drivers/media/platform/marvell/mmp-driver.c (renamed from drivers/media/platform/marvell-ccic/mmp-driver.c)8
-rw-r--r--drivers/media/platform/mediatek/Kconfig8
-rw-r--r--drivers/media/platform/mediatek/Makefile5
-rw-r--r--drivers/media/platform/mediatek/jpeg/Kconfig16
-rw-r--r--drivers/media/platform/mediatek/jpeg/Makefile (renamed from drivers/media/platform/mtk-jpeg/Makefile)0
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c)47
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h)2
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c)0
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h)0
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.c (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c)0
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.h (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h)0
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h)0
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c)0
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h (renamed from drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h)0
-rw-r--r--drivers/media/platform/mediatek/mdp/Kconfig17
-rw-r--r--drivers/media/platform/mediatek/mdp/Makefile (renamed from drivers/media/platform/mtk-mdp/Makefile)2
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_comp.c)40
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_comp.h (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_comp.h)2
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_core.c (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_core.c)3
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_core.h (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_core.h)0
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_ipi.h)0
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c)0
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.h (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_m2m.h)0
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_regs.c (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_regs.c)0
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_regs.h (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_regs.h)0
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.c (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c)0
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.h (renamed from drivers/media/platform/mtk-mdp/mtk_mdp_vpu.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/Kconfig36
-rw-r--r--drivers/media/platform/mediatek/vcodec/Makefile (renamed from drivers/media/platform/mtk-vcodec/Makefile)6
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c)4
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h)1
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c)199
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c200
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h56
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c169
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h19
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c)21
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h)77
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c)1
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c)22
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c)55
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h)3
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c43
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h)5
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c)59
-rw-r--r--drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h (renamed from drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h)8
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c (renamed from drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c (renamed from drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c (renamed from drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c (renamed from drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_drv_base.h (renamed from drivers/media/platform/mtk-vcodec/vdec_drv_base.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_drv_if.c (renamed from drivers/media/platform/mtk-vcodec/vdec_drv_if.c)21
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_drv_if.h (renamed from drivers/media/platform/mtk-vcodec/vdec_drv_if.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h (renamed from drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h)16
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c290
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h153
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c (renamed from drivers/media/platform/mtk-vcodec/vdec_vpu_if.c)46
-rw-r--r--drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h (renamed from drivers/media/platform/mtk-vcodec/vdec_vpu_if.h)22
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c (renamed from drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c (renamed from drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c)2
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc_drv_base.h (renamed from drivers/media/platform/mtk-vcodec/venc_drv_base.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc_drv_if.c (renamed from drivers/media/platform/mtk-vcodec/venc_drv_if.c)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc_drv_if.h (renamed from drivers/media/platform/mtk-vcodec/venc_drv_if.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h (renamed from drivers/media/platform/mtk-vcodec/venc_ipi_msg.h)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc_vpu_if.c (renamed from drivers/media/platform/mtk-vcodec/venc_vpu_if.c)0
-rw-r--r--drivers/media/platform/mediatek/vcodec/venc_vpu_if.h (renamed from drivers/media/platform/mtk-vcodec/venc_vpu_if.h)0
-rw-r--r--drivers/media/platform/mediatek/vpu/Kconfig15
-rw-r--r--drivers/media/platform/mediatek/vpu/Makefile (renamed from drivers/media/platform/mtk-vpu/Makefile)0
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.c (renamed from drivers/media/platform/mtk-vpu/mtk_vpu.c)10
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.h (renamed from drivers/media/platform/mtk-vpu/mtk_vpu.h)0
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c145
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h20
-rw-r--r--drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c45
-rw-r--r--drivers/media/platform/nvidia/Kconfig5
-rw-r--r--drivers/media/platform/nvidia/Makefile3
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/Kconfig17
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/Makefile3
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c229
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/h264.c946
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/iommu.c157
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/trace.h95
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/v4l2.c1018
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.c551
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.h242
-rw-r--r--drivers/media/platform/nxp/Kconfig55
-rw-r--r--drivers/media/platform/nxp/Makefile8
-rw-r--r--drivers/media/platform/nxp/fsl-viu.c (renamed from drivers/media/platform/fsl-viu.c)2
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/Kconfig (renamed from drivers/media/platform/imx-jpeg/Kconfig)3
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/Makefile (renamed from drivers/media/platform/imx-jpeg/Makefile)0
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c (renamed from drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c)0
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h (renamed from drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h)0
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c (renamed from drivers/media/platform/imx-jpeg/mxc-jpeg.c)63
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h (renamed from drivers/media/platform/imx-jpeg/mxc-jpeg.h)1
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c1581
-rw-r--r--drivers/media/platform/nxp/imx-pxp.c (renamed from drivers/media/platform/imx-pxp.c)0
-rw-r--r--drivers/media/platform/nxp/imx-pxp.h (renamed from drivers/media/platform/imx-pxp.h)0
-rw-r--r--drivers/media/platform/nxp/mx2_emmaprp.c (renamed from drivers/media/platform/mx2_emmaprp.c)0
-rw-r--r--drivers/media/platform/qcom/Kconfig6
-rw-r--r--drivers/media/platform/qcom/Makefile3
-rw-r--r--drivers/media/platform/qcom/camss/Kconfig9
-rw-r--r--drivers/media/platform/qcom/camss/Makefile3
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.c (renamed from drivers/media/platform/qcom/camss/camss-csid-170.c)34
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c91
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h5
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c19
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c199
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c42
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h7
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c12
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-170.c12
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-480.c564
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c29
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h4
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c5
-rw-r--r--drivers/media/platform/qcom/camss/camss.c368
-rw-r--r--drivers/media/platform/qcom/camss/camss.h20
-rw-r--r--drivers/media/platform/qcom/venus/Kconfig14
-rw-r--r--drivers/media/platform/qcom/venus/core.h4
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c2
-rw-r--r--drivers/media/platform/qcom/venus/venc.c4
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c6
-rw-r--r--drivers/media/platform/renesas/Kconfig121
-rw-r--r--drivers/media/platform/renesas/Makefile15
-rw-r--r--drivers/media/platform/renesas/rcar-fcp.c (renamed from drivers/media/platform/rcar-fcp.c)0
-rw-r--r--drivers/media/platform/renesas/rcar-isp.c (renamed from drivers/media/platform/rcar-isp.c)32
-rw-r--r--drivers/media/platform/renesas/rcar-vin/Kconfig (renamed from drivers/media/platform/rcar-vin/Kconfig)6
-rw-r--r--drivers/media/platform/renesas/rcar-vin/Makefile (renamed from drivers/media/platform/rcar-vin/Makefile)0
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c (renamed from drivers/media/platform/rcar-vin/rcar-core.c)389
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-csi2.c (renamed from drivers/media/platform/rcar-vin/rcar-csi2.c)132
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c (renamed from drivers/media/platform/rcar-vin/rcar-dma.c)2
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c (renamed from drivers/media/platform/rcar-vin/rcar-v4l2.c)0
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h (renamed from drivers/media/platform/rcar-vin/rcar-vin.h)20
-rw-r--r--drivers/media/platform/renesas/rcar_drif.c (renamed from drivers/media/platform/rcar_drif.c)0
-rw-r--r--drivers/media/platform/renesas/rcar_fdp1.c (renamed from drivers/media/platform/rcar_fdp1.c)0
-rw-r--r--drivers/media/platform/renesas/rcar_jpu.c (renamed from drivers/media/platform/rcar_jpu.c)2
-rw-r--r--drivers/media/platform/renesas/renesas-ceu.c (renamed from drivers/media/platform/renesas-ceu.c)0
-rw-r--r--drivers/media/platform/renesas/sh_vou.c (renamed from drivers/media/platform/sh_vou.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/Makefile (renamed from drivers/media/platform/vsp1/Makefile)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1.h (renamed from drivers/media/platform/vsp1/vsp1.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c (renamed from drivers/media/platform/vsp1/vsp1_brx.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.h (renamed from drivers/media/platform/vsp1/vsp1_brx.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c (renamed from drivers/media/platform/vsp1/vsp1_clu.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.h (renamed from drivers/media/platform/vsp1/vsp1_clu.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c (renamed from drivers/media/platform/vsp1/vsp1_dl.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.h (renamed from drivers/media/platform/vsp1/vsp1_dl.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c (renamed from drivers/media/platform/vsp1/vsp1_drm.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.h (renamed from drivers/media/platform/vsp1/vsp1_drm.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c (renamed from drivers/media/platform/vsp1/vsp1_drv.c)47
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c (renamed from drivers/media/platform/vsp1/vsp1_entity.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h (renamed from drivers/media/platform/vsp1/vsp1_entity.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.c (renamed from drivers/media/platform/vsp1/vsp1_hgo.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.h (renamed from drivers/media/platform/vsp1/vsp1_hgo.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.c (renamed from drivers/media/platform/vsp1/vsp1_hgt.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.h (renamed from drivers/media/platform/vsp1/vsp1_hgt.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c (renamed from drivers/media/platform/vsp1/vsp1_histo.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.h (renamed from drivers/media/platform/vsp1/vsp1_histo.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c (renamed from drivers/media/platform/vsp1/vsp1_hsit.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.h (renamed from drivers/media/platform/vsp1/vsp1_hsit.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.c (renamed from drivers/media/platform/vsp1/vsp1_lif.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.h (renamed from drivers/media/platform/vsp1/vsp1_lif.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c (renamed from drivers/media/platform/vsp1/vsp1_lut.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.h (renamed from drivers/media/platform/vsp1/vsp1_lut.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c (renamed from drivers/media/platform/vsp1/vsp1_pipe.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.h (renamed from drivers/media/platform/vsp1/vsp1_pipe.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_regs.h (renamed from drivers/media/platform/vsp1/vsp1_regs.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rpf.c (renamed from drivers/media/platform/vsp1/vsp1_rpf.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c (renamed from drivers/media/platform/vsp1/vsp1_rwpf.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.h (renamed from drivers/media/platform/vsp1/vsp1_rwpf.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c (renamed from drivers/media/platform/vsp1/vsp1_sru.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.h (renamed from drivers/media/platform/vsp1/vsp1_sru.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c (renamed from drivers/media/platform/vsp1/vsp1_uds.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.h (renamed from drivers/media/platform/vsp1/vsp1_uds.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c (renamed from drivers/media/platform/vsp1/vsp1_uif.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.h (renamed from drivers/media/platform/vsp1/vsp1_uif.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c (renamed from drivers/media/platform/vsp1/vsp1_video.c)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.h (renamed from drivers/media/platform/vsp1/vsp1_video.h)0
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c (renamed from drivers/media/platform/vsp1/vsp1_wpf.c)0
-rw-r--r--drivers/media/platform/rockchip/Kconfig6
-rw-r--r--drivers/media/platform/rockchip/Makefile3
-rw-r--r--drivers/media/platform/rockchip/rga/Kconfig14
-rw-r--r--drivers/media/platform/rockchip/rkisp1/Kconfig19
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c28
-rw-r--r--drivers/media/platform/samsung/Kconfig10
-rw-r--r--drivers/media/platform/samsung/Makefile7
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/Kconfig10
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/Makefile (renamed from drivers/media/platform/exynos-gsc/Makefile)0
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-core.c (renamed from drivers/media/platform/exynos-gsc/gsc-core.c)14
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-core.h (renamed from drivers/media/platform/exynos-gsc/gsc-core.h)0
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c (renamed from drivers/media/platform/exynos-gsc/gsc-m2m.c)0
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-regs.c (renamed from drivers/media/platform/exynos-gsc/gsc-regs.c)0
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-regs.h (renamed from drivers/media/platform/exynos-gsc/gsc-regs.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/Kconfig (renamed from drivers/media/platform/exynos4-is/Kconfig)3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/Makefile (renamed from drivers/media/platform/exynos4-is/Makefile)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/common.c (renamed from drivers/media/platform/exynos4-is/common.c)5
-rw-r--r--drivers/media/platform/samsung/exynos4-is/common.h (renamed from drivers/media/platform/exynos4-is/common.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-capture.c (renamed from drivers/media/platform/exynos4-is/fimc-capture.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.c (renamed from drivers/media/platform/exynos4-is/fimc-core.c)11
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.h (renamed from drivers/media/platform/exynos4-is/fimc-core.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-command.h (renamed from drivers/media/platform/exynos4-is/fimc-is-command.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c (renamed from drivers/media/platform/exynos4-is/fimc-is-errno.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h (renamed from drivers/media/platform/exynos4-is/fimc-is-errno.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c (renamed from drivers/media/platform/exynos4-is/fimc-is-i2c.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h (renamed from drivers/media/platform/exynos4-is/fimc-is-i2c.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.c (renamed from drivers/media/platform/exynos4-is/fimc-is-param.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.h (renamed from drivers/media/platform/exynos4-is/fimc-is-param.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c (renamed from drivers/media/platform/exynos4-is/fimc-is-regs.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-regs.h (renamed from drivers/media/platform/exynos4-is/fimc-is-regs.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.c (renamed from drivers/media/platform/exynos4-is/fimc-is-sensor.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.h (renamed from drivers/media/platform/exynos4-is/fimc-is-sensor.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c (renamed from drivers/media/platform/exynos4-is/fimc-is.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.h (renamed from drivers/media/platform/exynos4-is/fimc-is.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c (renamed from drivers/media/platform/exynos4-is/fimc-isp-video.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp-video.h (renamed from drivers/media/platform/exynos4-is/fimc-isp-video.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp.c (renamed from drivers/media/platform/exynos4-is/fimc-isp.c)2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp.h (renamed from drivers/media/platform/exynos4-is/fimc-isp.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.c (renamed from drivers/media/platform/exynos4-is/fimc-lite-reg.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.h (renamed from drivers/media/platform/exynos4-is/fimc-lite-reg.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.c (renamed from drivers/media/platform/exynos4-is/fimc-lite.c)17
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.h (renamed from drivers/media/platform/exynos4-is/fimc-lite.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-m2m.c (renamed from drivers/media/platform/exynos4-is/fimc-m2m.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-reg.c (renamed from drivers/media/platform/exynos4-is/fimc-reg.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-reg.h (renamed from drivers/media/platform/exynos4-is/fimc-reg.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.c (renamed from drivers/media/platform/exynos4-is/media-dev.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.h (renamed from drivers/media/platform/exynos4-is/media-dev.h)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/mipi-csis.c (renamed from drivers/media/platform/exynos4-is/mipi-csis.c)0
-rw-r--r--drivers/media/platform/samsung/exynos4-is/mipi-csis.h (renamed from drivers/media/platform/exynos4-is/mipi-csis.h)0
-rw-r--r--drivers/media/platform/samsung/s3c-camif/Kconfig15
-rw-r--r--drivers/media/platform/samsung/s3c-camif/Makefile (renamed from drivers/media/platform/s3c-camif/Makefile)0
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-capture.c (renamed from drivers/media/platform/s3c-camif/camif-capture.c)0
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.c (renamed from drivers/media/platform/s3c-camif/camif-core.c)0
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.h (renamed from drivers/media/platform/s3c-camif/camif-core.h)0
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-regs.c (renamed from drivers/media/platform/s3c-camif/camif-regs.c)0
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-regs.h (renamed from drivers/media/platform/s3c-camif/camif-regs.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/Kconfig11
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/Makefile (renamed from drivers/media/platform/s5p-g2d/Makefile)0
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d-hw.c (renamed from drivers/media/platform/s5p-g2d/g2d-hw.c)0
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d-regs.h (renamed from drivers/media/platform/s5p-g2d/g2d-regs.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d.c (renamed from drivers/media/platform/s5p-g2d/g2d.c)10
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d.h (renamed from drivers/media/platform/s5p-g2d/g2d.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/Kconfig12
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/Makefile (renamed from drivers/media/platform/s5p-jpeg/Makefile)0
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c (renamed from drivers/media/platform/s5p-jpeg/jpeg-core.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h (renamed from drivers/media/platform/s5p-jpeg/jpeg-core.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c (renamed from drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c)0
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h (renamed from drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c (renamed from drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c)0
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h (renamed from drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c (renamed from drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h (renamed from drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h (renamed from drivers/media/platform/s5p-jpeg/jpeg-regs.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/Kconfig9
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/Makefile (renamed from drivers/media/platform/s5p-mfc/Makefile)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v10.h (renamed from drivers/media/platform/s5p-mfc/regs-mfc-v10.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h (renamed from drivers/media/platform/s5p-mfc/regs-mfc-v6.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h (renamed from drivers/media/platform/s5p-mfc/regs-mfc-v7.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h (renamed from drivers/media/platform/s5p-mfc/regs-mfc-v8.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc.h (renamed from drivers/media/platform/s5p-mfc/regs-mfc.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc.c)11
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_common.h)1
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_debug.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_dec.c)20
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_dec.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_enc.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_enc.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_intr.c)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_intr.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_iommu.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_opr.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_opr.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.h)0
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_pm.c)2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h (renamed from drivers/media/platform/s5p-mfc/s5p_mfc_pm.h)2
-rw-r--r--drivers/media/platform/st/Kconfig6
-rw-r--r--drivers/media/platform/st/Makefile7
-rw-r--r--drivers/media/platform/st/sti/Kconfig5
-rw-r--r--drivers/media/platform/st/sti/Makefile6
-rw-r--r--drivers/media/platform/st/sti/bdisp/Kconfig10
-rw-r--r--drivers/media/platform/st/sti/bdisp/Makefile (renamed from drivers/media/platform/sti/bdisp/Makefile)0
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-debug.c (renamed from drivers/media/platform/sti/bdisp/bdisp-debug.c)0
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-filter.h (renamed from drivers/media/platform/sti/bdisp/bdisp-filter.h)0
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-hw.c (renamed from drivers/media/platform/sti/bdisp/bdisp-hw.c)0
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-reg.h (renamed from drivers/media/platform/sti/bdisp/bdisp-reg.h)0
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c (renamed from drivers/media/platform/sti/bdisp/bdisp-v4l2.c)10
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp.h (renamed from drivers/media/platform/sti/bdisp/bdisp.h)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Kconfig (renamed from drivers/media/platform/sti/c8sectpfe/Kconfig)1
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Makefile (renamed from drivers/media/platform/sti/c8sectpfe/Makefile)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c)0
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h (renamed from drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h)0
-rw-r--r--drivers/media/platform/st/sti/delta/Kconfig36
-rw-r--r--drivers/media/platform/st/sti/delta/Makefile (renamed from drivers/media/platform/sti/delta/Makefile)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-cfg.h (renamed from drivers/media/platform/sti/delta/delta-cfg.h)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-debug.c (renamed from drivers/media/platform/sti/delta/delta-debug.c)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-debug.h (renamed from drivers/media/platform/sti/delta/delta-debug.h)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-ipc.c (renamed from drivers/media/platform/sti/delta/delta-ipc.c)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-ipc.h (renamed from drivers/media/platform/sti/delta/delta-ipc.h)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mem.c (renamed from drivers/media/platform/sti/delta/delta-mem.c)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mem.h (renamed from drivers/media/platform/sti/delta/delta-mem.h)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c (renamed from drivers/media/platform/sti/delta/delta-mjpeg-dec.c)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg-fw.h (renamed from drivers/media/platform/sti/delta/delta-mjpeg-fw.h)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c (renamed from drivers/media/platform/sti/delta/delta-mjpeg-hdr.c)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-mjpeg.h (renamed from drivers/media/platform/sti/delta/delta-mjpeg.h)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta-v4l2.c (renamed from drivers/media/platform/sti/delta/delta-v4l2.c)0
-rw-r--r--drivers/media/platform/st/sti/delta/delta.h (renamed from drivers/media/platform/sti/delta/delta.h)0
-rw-r--r--drivers/media/platform/st/sti/hva/Kconfig26
-rw-r--r--drivers/media/platform/st/sti/hva/Makefile (renamed from drivers/media/platform/sti/hva/Makefile)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva-debugfs.c (renamed from drivers/media/platform/sti/hva/hva-debugfs.c)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva-h264.c (renamed from drivers/media/platform/sti/hva/hva-h264.c)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva-hw.c (renamed from drivers/media/platform/sti/hva/hva-hw.c)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva-hw.h (renamed from drivers/media/platform/sti/hva/hva-hw.h)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva-mem.c (renamed from drivers/media/platform/sti/hva/hva-mem.c)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva-mem.h (renamed from drivers/media/platform/sti/hva/hva-mem.h)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva-v4l2.c (renamed from drivers/media/platform/sti/hva/hva-v4l2.c)0
-rw-r--r--drivers/media/platform/st/sti/hva/hva.h (renamed from drivers/media/platform/sti/hva/hva.h)0
-rw-r--r--drivers/media/platform/st/stm32/Kconfig31
-rw-r--r--drivers/media/platform/st/stm32/Makefile (renamed from drivers/media/platform/stm32/Makefile)0
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d-hw.c (renamed from drivers/media/platform/stm32/dma2d/dma2d-hw.c)0
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d-regs.h (renamed from drivers/media/platform/stm32/dma2d/dma2d-regs.h)0
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.c (renamed from drivers/media/platform/stm32/dma2d/dma2d.c)9
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.h (renamed from drivers/media/platform/stm32/dma2d/dma2d.h)0
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c (renamed from drivers/media/platform/stm32/stm32-dcmi.c)53
-rw-r--r--drivers/media/platform/sunxi/Kconfig4
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/Kconfig3
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h2
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c2
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/Kconfig3
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c6
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/Kconfig14
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/Kconfig14
-rw-r--r--drivers/media/platform/ti/Kconfig69
-rw-r--r--drivers/media/platform/ti/Makefile7
-rw-r--r--drivers/media/platform/ti/am437x/Kconfig (renamed from drivers/media/platform/am437x/Kconfig)3
-rw-r--r--drivers/media/platform/ti/am437x/Makefile (renamed from drivers/media/platform/am437x/Makefile)0
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.c (renamed from drivers/media/platform/am437x/am437x-vpfe.c)0
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.h (renamed from drivers/media/platform/am437x/am437x-vpfe.h)0
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe_regs.h (renamed from drivers/media/platform/am437x/am437x-vpfe_regs.h)0
-rw-r--r--drivers/media/platform/ti/cal/Makefile3
-rw-r--r--drivers/media/platform/ti/cal/cal-camerarx.c (renamed from drivers/media/platform/ti-vpe/cal-camerarx.c)6
-rw-r--r--drivers/media/platform/ti/cal/cal-video.c (renamed from drivers/media/platform/ti-vpe/cal-video.c)3
-rw-r--r--drivers/media/platform/ti/cal/cal.c (renamed from drivers/media/platform/ti-vpe/cal.c)0
-rw-r--r--drivers/media/platform/ti/cal/cal.h (renamed from drivers/media/platform/ti-vpe/cal.h)0
-rw-r--r--drivers/media/platform/ti/cal/cal_regs.h (renamed from drivers/media/platform/ti-vpe/cal_regs.h)0
-rw-r--r--drivers/media/platform/ti/davinci/Kconfig (renamed from drivers/media/platform/davinci/Kconfig)18
-rw-r--r--drivers/media/platform/ti/davinci/Makefile (renamed from drivers/media/platform/davinci/Makefile)0
-rw-r--r--drivers/media/platform/ti/davinci/ccdc_hw_device.h (renamed from drivers/media/platform/davinci/ccdc_hw_device.h)0
-rw-r--r--drivers/media/platform/ti/davinci/dm355_ccdc.c (renamed from drivers/media/platform/davinci/dm355_ccdc.c)0
-rw-r--r--drivers/media/platform/ti/davinci/dm355_ccdc_regs.h (renamed from drivers/media/platform/davinci/dm355_ccdc_regs.h)0
-rw-r--r--drivers/media/platform/ti/davinci/dm644x_ccdc.c (renamed from drivers/media/platform/davinci/dm644x_ccdc.c)0
-rw-r--r--drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h (renamed from drivers/media/platform/davinci/dm644x_ccdc_regs.h)0
-rw-r--r--drivers/media/platform/ti/davinci/isif.c (renamed from drivers/media/platform/davinci/isif.c)0
-rw-r--r--drivers/media/platform/ti/davinci/isif_regs.h (renamed from drivers/media/platform/davinci/isif_regs.h)0
-rw-r--r--drivers/media/platform/ti/davinci/vpbe.c (renamed from drivers/media/platform/davinci/vpbe.c)0
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_display.c (renamed from drivers/media/platform/davinci/vpbe_display.c)0
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_osd.c (renamed from drivers/media/platform/davinci/vpbe_osd.c)0
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_osd_regs.h (renamed from drivers/media/platform/davinci/vpbe_osd_regs.h)0
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_venc.c (renamed from drivers/media/platform/davinci/vpbe_venc.c)0
-rw-r--r--drivers/media/platform/ti/davinci/vpbe_venc_regs.h (renamed from drivers/media/platform/davinci/vpbe_venc_regs.h)0
-rw-r--r--drivers/media/platform/ti/davinci/vpfe_capture.c (renamed from drivers/media/platform/davinci/vpfe_capture.c)0
-rw-r--r--drivers/media/platform/ti/davinci/vpif.c (renamed from drivers/media/platform/davinci/vpif.c)123
-rw-r--r--drivers/media/platform/ti/davinci/vpif.h (renamed from drivers/media/platform/davinci/vpif.h)0
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c (renamed from drivers/media/platform/davinci/vpif_capture.c)27
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.h (renamed from drivers/media/platform/davinci/vpif_capture.h)0
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.c (renamed from drivers/media/platform/davinci/vpif_display.c)24
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.h (renamed from drivers/media/platform/davinci/vpif_display.h)0
-rw-r--r--drivers/media/platform/ti/davinci/vpss.c (renamed from drivers/media/platform/davinci/vpss.c)0
-rw-r--r--drivers/media/platform/ti/omap/Kconfig (renamed from drivers/media/platform/omap/Kconfig)3
-rw-r--r--drivers/media/platform/ti/omap/Makefile (renamed from drivers/media/platform/omap/Makefile)0
-rw-r--r--drivers/media/platform/ti/omap/omap_vout.c (renamed from drivers/media/platform/omap/omap_vout.c)0
-rw-r--r--drivers/media/platform/ti/omap/omap_vout_vrfb.c (renamed from drivers/media/platform/omap/omap_vout_vrfb.c)0
-rw-r--r--drivers/media/platform/ti/omap/omap_vout_vrfb.h (renamed from drivers/media/platform/omap/omap_vout_vrfb.h)0
-rw-r--r--drivers/media/platform/ti/omap/omap_voutdef.h (renamed from drivers/media/platform/omap/omap_voutdef.h)0
-rw-r--r--drivers/media/platform/ti/omap/omap_voutlib.c (renamed from drivers/media/platform/omap/omap_voutlib.c)0
-rw-r--r--drivers/media/platform/ti/omap/omap_voutlib.h (renamed from drivers/media/platform/omap/omap_voutlib.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/Kconfig21
-rw-r--r--drivers/media/platform/ti/omap3isp/Makefile (renamed from drivers/media/platform/omap3isp/Makefile)0
-rw-r--r--drivers/media/platform/ti/omap3isp/cfa_coef_table.h (renamed from drivers/media/platform/omap3isp/cfa_coef_table.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/gamma_table.h (renamed from drivers/media/platform/omap3isp/gamma_table.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.c (renamed from drivers/media/platform/omap3isp/isp.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.h (renamed from drivers/media/platform/omap3isp/isp.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.c (renamed from drivers/media/platform/omap3isp/ispccdc.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.h (renamed from drivers/media/platform/omap3isp/ispccdc.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccp2.c (renamed from drivers/media/platform/omap3isp/ispccp2.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccp2.h (renamed from drivers/media/platform/omap3isp/ispccp2.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsi2.c (renamed from drivers/media/platform/omap3isp/ispcsi2.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsi2.h (renamed from drivers/media/platform/omap3isp/ispcsi2.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsiphy.c (renamed from drivers/media/platform/omap3isp/ispcsiphy.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsiphy.h (renamed from drivers/media/platform/omap3isp/ispcsiphy.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a.h (renamed from drivers/media/platform/omap3isp/isph3a.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a_aewb.c (renamed from drivers/media/platform/omap3isp/isph3a_aewb.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isph3a_af.c (renamed from drivers/media/platform/omap3isp/isph3a_af.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isphist.c (renamed from drivers/media/platform/omap3isp/isphist.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isphist.h (renamed from drivers/media/platform/omap3isp/isphist.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isppreview.c (renamed from drivers/media/platform/omap3isp/isppreview.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/isppreview.h (renamed from drivers/media/platform/omap3isp/isppreview.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispreg.h (renamed from drivers/media/platform/omap3isp/ispreg.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispresizer.c (renamed from drivers/media/platform/omap3isp/ispresizer.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispresizer.h (renamed from drivers/media/platform/omap3isp/ispresizer.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.c (renamed from drivers/media/platform/omap3isp/ispstat.c)5
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.h (renamed from drivers/media/platform/omap3isp/ispstat.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c (renamed from drivers/media/platform/omap3isp/ispvideo.c)0
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.h (renamed from drivers/media/platform/omap3isp/ispvideo.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/luma_enhance_table.h (renamed from drivers/media/platform/omap3isp/luma_enhance_table.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/noise_filter_table.h (renamed from drivers/media/platform/omap3isp/noise_filter_table.h)0
-rw-r--r--drivers/media/platform/ti/omap3isp/omap3isp.h (renamed from drivers/media/platform/omap3isp/omap3isp.h)0
-rw-r--r--drivers/media/platform/ti/vpe/Makefile (renamed from drivers/media/platform/ti-vpe/Makefile)4
-rw-r--r--drivers/media/platform/ti/vpe/csc.c (renamed from drivers/media/platform/ti-vpe/csc.c)0
-rw-r--r--drivers/media/platform/ti/vpe/csc.h (renamed from drivers/media/platform/ti-vpe/csc.h)0
-rw-r--r--drivers/media/platform/ti/vpe/sc.c (renamed from drivers/media/platform/ti-vpe/sc.c)0
-rw-r--r--drivers/media/platform/ti/vpe/sc.h (renamed from drivers/media/platform/ti-vpe/sc.h)0
-rw-r--r--drivers/media/platform/ti/vpe/sc_coeff.h (renamed from drivers/media/platform/ti-vpe/sc_coeff.h)0
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.c (renamed from drivers/media/platform/ti-vpe/vpdma.c)0
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.h (renamed from drivers/media/platform/ti-vpe/vpdma.h)0
-rw-r--r--drivers/media/platform/ti/vpe/vpdma_priv.h (renamed from drivers/media/platform/ti-vpe/vpdma_priv.h)0
-rw-r--r--drivers/media/platform/ti/vpe/vpe.c (renamed from drivers/media/platform/ti-vpe/vpe.c)0
-rw-r--r--drivers/media/platform/ti/vpe/vpe_regs.h (renamed from drivers/media/platform/ti-vpe/vpe_regs.h)0
-rw-r--r--drivers/media/platform/via/Kconfig14
-rw-r--r--drivers/media/platform/via/Makefile2
-rw-r--r--drivers/media/platform/via/via-camera.c (renamed from drivers/media/platform/via-camera.c)0
-rw-r--r--drivers/media/platform/via/via-camera.h (renamed from drivers/media/platform/via-camera.h)0
-rw-r--r--drivers/media/platform/xilinx/Kconfig10
-rw-r--r--drivers/media/platform/xilinx/xilinx-csi2rxss.c106
518 files changed, 22278 insertions, 2482 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 9fbdba0fd1e7..f1056ceaf5a8 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -3,677 +3,85 @@
# Platform drivers
# Most drivers here are currently for webcam support
-menuconfig V4L_PLATFORM_DRIVERS
- bool "V4L platform devices"
- help
- Say Y here to enable support for platform-specific V4L drivers.
-
-if V4L_PLATFORM_DRIVERS
-
-source "drivers/media/platform/marvell-ccic/Kconfig"
-
-config VIDEO_VIA_CAMERA
- tristate "VIAFB camera controller support"
- depends on FB_VIA && VIDEO_V4L2
- select VIDEOBUF2_DMA_SG
- select VIDEO_OV7670
- help
- Driver support for the integrated camera controller in VIA
- Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
- with ov7670 sensors.
-
-#
-# Platform multimedia device configuration
-#
-source "drivers/media/platform/cadence/Kconfig"
-
-source "drivers/media/platform/davinci/Kconfig"
-
-source "drivers/media/platform/omap/Kconfig"
-
-config VIDEO_ASPEED
- tristate "Aspeed AST2400 and AST2500 Video Engine driver"
- depends on VIDEO_V4L2
- select VIDEOBUF2_DMA_CONTIG
- help
- Support for the Aspeed Video Engine (VE) embedded in the Aspeed
- AST2400 and AST2500 SOCs. The VE can capture and compress video data
- from digital or analog sources.
-
-config VIDEO_SH_VOU
- tristate "SuperH VOU video output driver"
- depends on VIDEO_DEV && I2C
- depends on ARCH_SHMOBILE || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- help
- Support for the Video Output Unit (VOU) on SuperH SoCs.
-
-config VIDEO_VIU
- tristate "Freescale VIU Video Driver"
- depends on VIDEO_V4L2 && (PPC_MPC512x || COMPILE_TEST) && I2C
- select VIDEOBUF_DMA_CONTIG
- default y
- help
- Support for Freescale VIU video driver. This device captures
- video data, or overlays video on DIU frame buffer.
-
- Say Y here if you want to enable VIU device on MPC5121e Rev2+.
- In doubt, say N.
-
-config VIDEO_MUX
- tristate "Video Multiplexer"
- select MULTIPLEXER
- depends on VIDEO_V4L2 && OF
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select REGMAP
- select V4L2_FWNODE
- help
- This driver provides support for N:1 video bus multiplexers.
-
-config VIDEO_OMAP3
- tristate "OMAP 3 Camera support"
- depends on VIDEO_V4L2 && I2C
- depends on (ARCH_OMAP3 && OMAP_IOMMU) || COMPILE_TEST
- depends on COMMON_CLK && OF
- select ARM_DMA_USE_IOMMU if OMAP_IOMMU
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select VIDEOBUF2_DMA_CONTIG
- select MFD_SYSCON
- select V4L2_FWNODE
- help
- Driver for an OMAP 3 camera controller.
-
-config VIDEO_OMAP3_DEBUG
- bool "OMAP 3 Camera debug messages"
- depends on VIDEO_OMAP3
- help
- Enable debug messages on OMAP 3 camera controller driver.
-
-config VIDEO_PXA27x
- tristate "PXA27x Quick Capture Interface driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on PXA27x || COMPILE_TEST
- select VIDEOBUF2_DMA_SG
- select SG_SPLIT
- select V4L2_FWNODE
- help
- This is a v4l2 driver for the PXA27x Quick Capture Interface
-
-config VIDEO_QCOM_CAMSS
- tristate "Qualcomm V4L2 Camera Subsystem driver"
- depends on VIDEO_V4L2
- depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select VIDEOBUF2_DMA_SG
- select V4L2_FWNODE
-
-config VIDEO_S3C_CAMIF
- tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
- depends on VIDEO_V4L2 && I2C && PM
- depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select VIDEOBUF2_DMA_CONTIG
- help
- This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
- host interface (CAMIF).
-
- To compile this driver as a module, choose M here: the module
- will be called s3c-camif.
-
-config VIDEO_STM32_DCMI
- tristate "STM32 Digital Camera Memory Interface (DCMI) support"
- depends on VIDEO_V4L2 && OF
- depends on ARCH_STM32 || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select MEDIA_CONTROLLER
- select V4L2_FWNODE
- help
- This module makes the STM32 Digital Camera Memory Interface (DCMI)
- available as a v4l2 device.
-
- To compile this driver as a module, choose M here: the module
- will be called stm32-dcmi.
-
-config VIDEO_RENESAS_CEU
- tristate "Renesas Capture Engine Unit (CEU) driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_FWNODE
+menuconfig MEDIA_PLATFORM_DRIVERS
+ bool "Media platform devices"
+ default "y"
help
- This is a v4l2 driver for the Renesas CEU Interface
+ Say Y here to enable support for platform-specific media drivers.
-config VIDEO_ROCKCHIP_ISP1
- tristate "Rockchip Image Signal Processing v1 Unit driver"
- depends on VIDEO_V4L2 && OF
- depends on ARCH_ROCKCHIP || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select VIDEOBUF2_DMA_CONTIG
- select VIDEOBUF2_VMALLOC
- select V4L2_FWNODE
- select GENERIC_PHY_MIPI_DPHY
- default n
- help
- Enable this to support the Image Signal Processing (ISP) module
- present in RK3399 SoCs.
-
- To compile this driver as a module, choose M here: the module
- will be called rockchip-isp1.
-
-source "drivers/media/platform/exynos4-is/Kconfig"
-source "drivers/media/platform/am437x/Kconfig"
-source "drivers/media/platform/xilinx/Kconfig"
-source "drivers/media/platform/rcar-vin/Kconfig"
-source "drivers/media/platform/atmel/Kconfig"
-source "drivers/media/platform/sunxi/Kconfig"
+if MEDIA_PLATFORM_DRIVERS
-config VIDEO_TI_CAL
- tristate "TI CAL (Camera Adaptation Layer) driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- depends on SOC_DRA7XX || ARCH_K3 || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_FWNODE
+config V4L_PLATFORM_DRIVERS
+ bool "V4L platform devices"
help
- Support for the TI CAL (Camera Adaptation Layer) block
- found on DRA72X SoC.
- In TI Technical Reference Manual this module is referred as
- Camera Interface Subsystem (CAMSS).
-
-if VIDEO_TI_CAL
+ Say Y here to enable support for platform-specific V4L drivers.
-config VIDEO_TI_CAL_MC
- bool "Media Controller centric mode by default"
- default n
+config SDR_PLATFORM_DRIVERS
+ bool "SDR platform devices"
+ depends on MEDIA_SDR_SUPPORT
help
- Enables Media Controller centric mode by default.
-
- If set, CAL driver will start in Media Controller mode by
- default. Note that this behavior can be overridden via
- module parameter 'mc_api'.
-
-endif # VIDEO_TI_CAL
+ Say Y here to enable support for platform-specific SDR Drivers.
-config VIDEO_RCAR_ISP
- tristate "R-Car Image Signal Processor (ISP)"
- depends on VIDEO_V4L2 && OF
- depends on ARCH_RENESAS || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select RESET_CONTROLLER
- select V4L2_FWNODE
+config DVB_PLATFORM_DRIVERS
+ bool "DVB platform devices"
+ depends on MEDIA_DIGITAL_TV_SUPPORT
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.
-
-endif # V4L_PLATFORM_DRIVERS
+ Say Y here to enable support for platform-specific Digital TV drivers.
-menuconfig V4L_MEM2MEM_DRIVERS
+config V4L_MEM2MEM_DRIVERS
bool "Memory-to-memory multimedia devices"
- depends on VIDEO_V4L2
+ depends on VIDEO_DEV
help
Say Y here to enable selecting drivers for V4L devices that
use system memory for both source and destination buffers, as opposed
to capture and output drivers, which use memory buffers for just
one of those.
-if V4L_MEM2MEM_DRIVERS
-
-config VIDEO_ALLEGRO_DVT
- tristate "Allegro DVT Video IP Core"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_ZYNQMP || COMPILE_TEST
- select V4L2_MEM2MEM_DEV
- select VIDEOBUF2_DMA_CONTIG
- select REGMAP_MMIO
- help
- Support for the encoder video IP core by Allegro DVT. This core is
- found for example on the Xilinx ZynqMP SoC in the EV family and is
- called VCU in the reference manual.
-
- To compile this driver as a module, choose M here: the module
- will be called allegro.
-
-config VIDEO_CODA
- tristate "Chips&Media Coda multi-standard codec IP"
- depends on VIDEO_DEV && VIDEO_V4L2 && OF && (ARCH_MXC || COMPILE_TEST)
- select SRAM
- select VIDEOBUF2_DMA_CONTIG
- select VIDEOBUF2_VMALLOC
- select V4L2_JPEG_HELPER
- select V4L2_MEM2MEM_DEV
- select GENERIC_ALLOCATOR
- help
- Coda is a range of video codec IPs that supports
- H.264, MPEG-4, and other video formats.
-
-config VIDEO_IMX_VDOA
- def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST
-
-config VIDEO_IMX_PXP
- tristate "i.MX Pixel Pipeline (PXP)"
- depends on VIDEO_DEV && VIDEO_V4L2 && (ARCH_MXC || COMPILE_TEST)
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- The i.MX Pixel Pipeline is a memory-to-memory engine for scaling,
- color space conversion, and rotation.
-
-source "drivers/media/platform/imx-jpeg/Kconfig"
-
-config VIDEO_MEDIATEK_JPEG
- tristate "Mediatek JPEG Codec driver"
- depends on MTK_IOMMU_V1 || MTK_IOMMU || COMPILE_TEST
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- Mediatek jpeg codec driver provides HW capability to decode
- JPEG format
-
- To compile this driver as a module, choose M here: the
- module will be called mtk-jpeg
-
-config VIDEO_MEDIATEK_VPU
- tristate "Mediatek Video Processor Unit"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_MEDIATEK || COMPILE_TEST
- help
- This driver provides downloading VPU firmware and
- communicating with VPU. This driver for hw video
- codec embedded in Mediatek's MT8173 SOCs. It is able
- to handle video decoding/encoding in a range of formats.
-
- To compile this driver as a module, choose M here: the
- module will be called mtk-vpu.
-
-config VIDEO_MEDIATEK_MDP
- tristate "Mediatek MDP driver"
- depends on MTK_IOMMU || COMPILE_TEST
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- select VIDEO_MEDIATEK_VPU
- help
- It is a v4l2 driver and present in Mediatek MT8173 SoCs.
- The driver supports for scaling and color space conversion.
-
- To compile this driver as a module, choose M here: the
- module will be called mtk-mdp.
-
-config VIDEO_MEDIATEK_VCODEC
- tristate "Mediatek Video Codec driver"
- depends on MTK_IOMMU || COMPILE_TEST
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on VIDEO_MEDIATEK_VPU || MTK_SCP
- # The two following lines ensure we have the same state ("m" or "y") as
- # our dependencies, to avoid missing symbols during link.
- depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU
- depends on MTK_SCP || !MTK_SCP
- depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU
- select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP
- select V4L2_H264
- select MEDIA_CONTROLLER
- select MEDIA_CONTROLLER_REQUEST_API
- help
- Mediatek video codec driver provides HW capability to
- encode and decode in a range of video formats on MT8173
- and MT8183.
-
- Note that support for MT8173 requires VIDEO_MEDIATEK_VPU to
- also be selected. Support for MT8183 depends on MTK_SCP.
-
- To compile this driver as modules, choose M here: the
- modules will be called mtk-vcodec-dec and mtk-vcodec-enc.
-
-config VIDEO_MEDIATEK_VCODEC_VPU
- bool
-
-config VIDEO_MEDIATEK_VCODEC_SCP
- bool
+# Ancillary drivers
config VIDEO_MEM2MEM_DEINTERLACE
tristate "Deinterlace support"
- depends on VIDEO_DEV && VIDEO_V4L2
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
depends on HAS_DMA
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
help
Generic deinterlacing V4L2 driver.
-config VIDEO_MESON_GE2D
- tristate "Amlogic 2D Graphic Acceleration Unit"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_MESON || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a v4l2 driver for Amlogic GE2D 2D graphics accelerator.
- GE2D is a standalone 2D graphic acceleration unit, with color converter,
- image scaling, BitBLT & alpha blending operations.
-
- To compile this driver as a module choose m here.
-
-config VIDEO_SAMSUNG_S5P_G2D
- tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D
- 2d graphics accelerator.
-
-config VIDEO_SAMSUNG_S5P_JPEG
- tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a v4l2 driver for Samsung S5P, EXYNOS3250
- and EXYNOS4 JPEG codec
-
-config VIDEO_SAMSUNG_S5P_MFC
- tristate "Samsung S5P MFC Video Codec"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- help
- MFC 5.1 and 6.x driver for V4L2
-
-config VIDEO_MX2_EMMAPRP
- tristate "MX2 eMMa-PrP support"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on SOC_IMX27 || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- MX2X chips have a PrP that can be used to process buffers from
- memory to memory. Operations include resizing and format
- conversion.
-
-config VIDEO_SAMSUNG_EXYNOS_GSC
- tristate "Samsung Exynos G-Scaler driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_EXYNOS || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler.
-
-config VIDEO_STI_BDISP
- tristate "STMicroelectronics BDISP 2D blitter driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_STI || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
-
-config VIDEO_STI_HVA
- tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_STI || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This V4L2 driver enables HVA (Hardware Video Accelerator) multi-format
- video encoder of STMicroelectronics SoC, allowing hardware encoding of
- raw uncompressed formats in various compressed video bitstreams format.
-
- To compile this driver as a module, choose M here:
- the module will be called st-hva.
-
-config VIDEO_STI_HVA_DEBUGFS
- bool "Export STMicroelectronics HVA internals in debugfs"
- depends on VIDEO_STI_HVA
- depends on DEBUG_FS
- help
- Select this to see information about the internal state and the last
- operation of STMicroelectronics HVA multi-format video encoder in
- debugfs.
-
- Choose N unless you know you need this.
-
-config VIDEO_STI_DELTA
- tristate "STMicroelectronics DELTA multi-format video decoder V4L2 driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_STI || COMPILE_TEST
- help
- This V4L2 driver enables DELTA multi-format video decoder
- of STMicroelectronics STiH4xx SoC series allowing hardware
- decoding of various compressed video bitstream format in
- raw uncompressed format.
-
- Use this option to see the decoders available for such
- hardware.
-
- Please notice that the driver will only be built if
- at least one of the DELTA decoder below is selected.
-
-if VIDEO_STI_DELTA
-
-config VIDEO_STI_DELTA_MJPEG
- bool "STMicroelectronics DELTA MJPEG support"
- default y
- help
- Enables DELTA MJPEG hardware support.
-
- To compile this driver as a module, choose M here:
- the module will be called st-delta.
-
-config VIDEO_STI_DELTA_DRIVER
- tristate
- depends on VIDEO_STI_DELTA
- depends on VIDEO_STI_DELTA_MJPEG
- default VIDEO_STI_DELTA_MJPEG
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- select RPMSG
-
-endif # VIDEO_STI_DELTA
-
-config VIDEO_STM32_DMA2D
- tristate "STM32 Chrom-Art Accelerator (DMA2D)"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_STM32 || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- Enables DMA2D hardware support on stm32.
-
- The STM32 DMA2D is a memory-to-memory engine for pixel conversion
- and specialized DMA dedicated to image manipulation.
-
-config VIDEO_RENESAS_FDP1
- tristate "Renesas Fine Display Processor"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_RENESAS || COMPILE_TEST
- depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a V4L2 driver for the Renesas Fine Display Processor
- providing colour space conversion, and de-interlacing features.
-
- To compile this driver as a module, choose M here: the module
- will be called rcar_fdp1.
-
-config VIDEO_RENESAS_JPU
- tristate "Renesas JPEG Processing Unit"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_RENESAS || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a V4L2 driver for the Renesas JPEG Processing Unit.
-
- To compile this driver as a module, choose M here: the module
- will be called rcar_jpu.
-
-config VIDEO_RENESAS_FCP
- tristate "Renesas Frame Compression Processor"
- depends on ARCH_RENESAS || COMPILE_TEST
- depends on OF
- help
- This is a driver for the Renesas Frame Compression Processor (FCP).
- The FCP is a companion module of video processing modules in the
- Renesas R-Car Gen3 and RZ/G2 SoCs. It handles memory access for
- the codec, VSP and FDP modules.
-
- To compile this driver as a module, choose M here: the module
- will be called rcar-fcp.
-
-config VIDEO_RENESAS_VSP1
- tristate "Renesas VSP1 Video Processing Engine"
- depends on VIDEO_V4L2
- depends on ARCH_RENESAS || COMPILE_TEST
- depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP
+config VIDEO_MUX
+ tristate "Video Multiplexer"
+ depends on V4L_PLATFORM_DRIVERS
+ select MULTIPLEXER
+ depends on VIDEO_DEV && OF
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
- select VIDEOBUF2_DMA_CONTIG
- select VIDEOBUF2_VMALLOC
- help
- This is a V4L2 driver for the Renesas VSP1 video processing engine.
-
- To compile this driver as a module, choose M here: the module
- will be called vsp1.
-
-config VIDEO_ROCKCHIP_RGA
- tristate "Rockchip Raster 2d Graphic Acceleration Unit"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_ROCKCHIP || COMPILE_TEST
- select VIDEOBUF2_DMA_SG
- select V4L2_MEM2MEM_DEV
- help
- This is a v4l2 driver for Rockchip SOC RGA 2d graphics accelerator.
- Rockchip RGA is a separate 2D raster graphic acceleration unit.
- It accelerates 2D graphics operations, such as point/line drawing,
- image scaling, rotation, BitBLT, alpha blending and image blur/sharpness.
-
- To compile this driver as a module choose m here.
-
-config VIDEO_TI_VPE
- tristate "TI VPE (Video Processing Engine) driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on SOC_DRA7XX || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- select VIDEO_TI_VPDMA
- select VIDEO_TI_SC
- select VIDEO_TI_CSC
- help
- Support for the TI VPE(Video Processing Engine) block
- found on DRA7XX SoC.
-
-config VIDEO_TI_VPE_DEBUG
- bool "VPE debug messages"
- depends on VIDEO_TI_VPE
- help
- Enable debug messages on VPE driver.
-
-config VIDEO_QCOM_VENUS
- tristate "Qualcomm Venus V4L2 encoder/decoder driver"
- depends on VIDEO_DEV && VIDEO_V4L2 && QCOM_SMEM
- depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
- select QCOM_MDT_LOADER if ARCH_QCOM
- select QCOM_SCM
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- This is a V4L2 driver for Qualcomm Venus video accelerator
- hardware. It accelerates encoding and decoding operations
- on various Qualcomm SoCs.
- To compile this driver as a module choose m here.
-
-config VIDEO_SUN8I_DEINTERLACE
- tristate "Allwinner Deinterlace driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_SUNXI || COMPILE_TEST
- depends on COMMON_CLK && OF
- depends on PM
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- Support for the Allwinner deinterlace unit with scaling
- capability found on some SoCs, like H3.
- To compile this driver as a module choose m here.
-
-config VIDEO_SUN8I_ROTATE
- tristate "Allwinner DE2 rotation driver"
- depends on VIDEO_DEV && VIDEO_V4L2
- depends on ARCH_SUNXI || COMPILE_TEST
- depends on COMMON_CLK && OF
- depends on PM
- select VIDEOBUF2_DMA_CONTIG
- select V4L2_MEM2MEM_DEV
- help
- Support for the Allwinner DE2 rotation unit.
- To compile this driver as a module choose m here.
-
-endif # V4L_MEM2MEM_DRIVERS
-
-# TI VIDEO PORT Helper Modules
-# These will be selected by VPE and VIP
-config VIDEO_TI_VPDMA
- tristate
-
-config VIDEO_TI_SC
- tristate
-
-config VIDEO_TI_CSC
- tristate
-
-menuconfig DVB_PLATFORM_DRIVERS
- bool "DVB platform devices"
- depends on MEDIA_DIGITAL_TV_SUPPORT
- help
- Say Y here to enable support for platform-specific Digital TV drivers.
-
-if DVB_PLATFORM_DRIVERS
-source "drivers/media/platform/sti/c8sectpfe/Kconfig"
-endif #DVB_PLATFORM_DRIVERS
-
-menuconfig SDR_PLATFORM_DRIVERS
- bool "SDR platform devices"
- depends on MEDIA_SDR_SUPPORT
- help
- Say Y here to enable support for platform-specific SDR Drivers.
-
-if SDR_PLATFORM_DRIVERS
-
-config VIDEO_RCAR_DRIF
- tristate "Renesas Digital Radio Interface (DRIF)"
- depends on VIDEO_V4L2
- depends on ARCH_RENESAS || COMPILE_TEST
- select VIDEOBUF2_VMALLOC
- select V4L2_ASYNC
+ select REGMAP
+ select V4L2_FWNODE
help
- Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital
- Radio Interface that interfaces with an RF front end chip. It is a
- receiver of digital data which uses DMA to transfer received data to
- a configured location for an application to use.
+ This driver provides support for N:1 video bus multiplexers.
- To compile this driver as a module, choose M here; the module
- will be called rcar_drif.
+# Platform drivers - Please keep it alphabetically sorted
+source "drivers/media/platform/allegro-dvt/Kconfig"
+source "drivers/media/platform/amlogic/Kconfig"
+source "drivers/media/platform/amphion/Kconfig"
+source "drivers/media/platform/aspeed/Kconfig"
+source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/cadence/Kconfig"
+source "drivers/media/platform/chips-media/Kconfig"
+source "drivers/media/platform/intel/Kconfig"
+source "drivers/media/platform/marvell/Kconfig"
+source "drivers/media/platform/mediatek/Kconfig"
+source "drivers/media/platform/nvidia/Kconfig"
+source "drivers/media/platform/nxp/Kconfig"
+source "drivers/media/platform/qcom/Kconfig"
+source "drivers/media/platform/renesas/Kconfig"
+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/ti/Kconfig"
+source "drivers/media/platform/via/Kconfig"
+source "drivers/media/platform/xilinx/Kconfig"
-endif # SDR_PLATFORM_DRIVERS
+endif # MEDIA_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 19bcbced7382..a881e97bae95 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -3,88 +3,32 @@
# Makefile for the video capture/playback device drivers.
#
-obj-$(CONFIG_VIDEO_ALLEGRO_DVT) += allegro-dvt/
-obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o
-obj-$(CONFIG_VIDEO_CADENCE) += cadence/
-obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
-obj-$(CONFIG_VIDEO_CAFE_CCIC) += marvell-ccic/
-obj-$(CONFIG_VIDEO_MMP_CAMERA) += marvell-ccic/
-
-obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/
-obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
-
-obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
-
-obj-y += ti-vpe/
-
-obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
-obj-$(CONFIG_VIDEO_CODA) += coda/
-
-obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
-obj-$(CONFIG_VIDEO_IMX8_JPEG) += imx-jpeg/
-
+# Place here, alphabetically sorted by directory
+# (e. g. LC_ALL=C sort Makefile)
+obj-y += allegro-dvt/
+obj-y += amlogic/
+obj-y += amphion/
+obj-y += aspeed/
+obj-y += atmel/
+obj-y += cadence/
+obj-y += chips-media/
+obj-y += intel/
+obj-y += marvell/
+obj-y += mediatek/
+obj-y += nvidia/
+obj-y += nxp/
+obj-y += qcom/
+obj-y += renesas/
+obj-y += rockchip/
+obj-y += samsung/
+obj-y += st/
+obj-y += sunxi/
+obj-y += ti/
+obj-y += via/
+obj-y += xilinx/
+
+# Please place here only ancillary drivers that aren't SoC-specific
+# Please keep it alphabetically sorted by Kconfig name
+# (e. g. LC_ALL=C sort Makefile)
obj-$(CONFIG_VIDEO_MEM2MEM_DEINTERLACE) += m2m-deinterlace.o
-
obj-$(CONFIG_VIDEO_MUX) += video-mux.o
-
-obj-$(CONFIG_VIDEO_S3C_CAMIF) += s3c-camif/
-obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS4_IS) += exynos4-is/
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
-
-obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
-obj-$(CONFIG_VIDEO_SAMSUNG_EXYNOS_GSC) += exynos-gsc/
-
-obj-$(CONFIG_VIDEO_STI_BDISP) += sti/bdisp/
-obj-$(CONFIG_VIDEO_STI_HVA) += sti/hva/
-obj-$(CONFIG_DVB_C8SECTPFE) += sti/c8sectpfe/
-
-obj-$(CONFIG_VIDEO_STI_DELTA) += sti/delta/
-
-obj-y += stm32/
-
-obj-y += davinci/
-
-obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
-
-obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.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
-obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
-obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/
-
-obj-$(CONFIG_VIDEO_ROCKCHIP_ISP1) += rockchip/rkisp1/
-obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip/rga/
-
-obj-y += omap/
-
-obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/
-
-obj-$(CONFIG_VIDEO_XILINX) += xilinx/
-
-obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
-obj-$(CONFIG_VIDEO_RCAR_VIN) += rcar-vin/
-
-obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel/
-obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel/
-obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel/
-
-obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32/
-obj-$(CONFIG_VIDEO_STM32_DMA2D) += stm32/
-
-obj-$(CONFIG_VIDEO_MEDIATEK_VPU) += mtk-vpu/
-
-obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec/
-
-obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp/
-
-obj-$(CONFIG_VIDEO_MEDIATEK_JPEG) += mtk-jpeg/
-
-obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom/camss/
-
-obj-$(CONFIG_VIDEO_QCOM_VENUS) += qcom/venus/
-
-obj-y += sunxi/
-
-obj-$(CONFIG_VIDEO_MESON_GE2D) += meson/ge2d/
diff --git a/drivers/media/platform/allegro-dvt/Kconfig b/drivers/media/platform/allegro-dvt/Kconfig
new file mode 100644
index 000000000000..2182e1277568
--- /dev/null
+++ b/drivers/media/platform/allegro-dvt/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Allegro DVT media platform drivers"
+
+config VIDEO_ALLEGRO_DVT
+ tristate "Allegro DVT Video IP Core"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_ZYNQMP || COMPILE_TEST
+ select V4L2_MEM2MEM_DEV
+ select VIDEOBUF2_DMA_CONTIG
+ select REGMAP_MMIO
+ help
+ Support for the encoder video IP core by Allegro DVT. This core is
+ found for example on the Xilinx ZynqMP SoC in the EV family and is
+ called VCU in the reference manual.
+
+ To compile this driver as a module, choose M here: the module
+ will be called allegro.
diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platform/amlogic/Kconfig
new file mode 100644
index 000000000000..5014957404e9
--- /dev/null
+++ b/drivers/media/platform/amlogic/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Amlogic media platform drivers"
+
+source "drivers/media/platform/amlogic/meson-ge2d/Kconfig"
diff --git a/drivers/media/platform/amlogic/Makefile b/drivers/media/platform/amlogic/Makefile
new file mode 100644
index 000000000000..d3cdb8fa4ddb
--- /dev/null
+++ b/drivers/media/platform/amlogic/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += meson-ge2d/
diff --git a/drivers/media/platform/amlogic/meson-ge2d/Kconfig b/drivers/media/platform/amlogic/meson-ge2d/Kconfig
new file mode 100644
index 000000000000..312c4169e3c2
--- /dev/null
+++ b/drivers/media/platform/amlogic/meson-ge2d/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_MESON_GE2D
+ tristate "Amlogic 2D Graphic Acceleration Unit"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_MESON || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Amlogic GE2D 2D graphics accelerator.
+ GE2D is a standalone 2D graphic acceleration unit, with color converter,
+ image scaling, BitBLT & alpha blending operations.
+
+ To compile this driver as a module choose m here.
+
diff --git a/drivers/media/platform/meson/ge2d/Makefile b/drivers/media/platform/amlogic/meson-ge2d/Makefile
index 450586df27d7..450586df27d7 100644
--- a/drivers/media/platform/meson/ge2d/Makefile
+++ b/drivers/media/platform/amlogic/meson-ge2d/Makefile
diff --git a/drivers/media/platform/meson/ge2d/ge2d-regs.h b/drivers/media/platform/amlogic/meson-ge2d/ge2d-regs.h
index 2a76dd4c0ccb..2a76dd4c0ccb 100644
--- a/drivers/media/platform/meson/ge2d/ge2d-regs.h
+++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d-regs.h
diff --git a/drivers/media/platform/meson/ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
index ccda18e5a377..5e7b319f300d 100644
--- a/drivers/media/platform/meson/ge2d/ge2d.c
+++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
@@ -215,35 +215,35 @@ static void ge2d_hw_start(struct meson_ge2d *ge2d)
regmap_write(ge2d->map, GE2D_SRC1_CLIPY_START_END,
FIELD_PREP(GE2D_START, ctx->in.crop.top) |
- FIELD_PREP(GE2D_END, ctx->in.crop.top + ctx->in.crop.height));
+ FIELD_PREP(GE2D_END, ctx->in.crop.top + ctx->in.crop.height - 1));
regmap_write(ge2d->map, GE2D_SRC1_CLIPX_START_END,
FIELD_PREP(GE2D_START, ctx->in.crop.left) |
- FIELD_PREP(GE2D_END, ctx->in.crop.left + ctx->in.crop.width));
+ FIELD_PREP(GE2D_END, ctx->in.crop.left + ctx->in.crop.width - 1));
regmap_write(ge2d->map, GE2D_SRC2_CLIPY_START_END,
FIELD_PREP(GE2D_START, ctx->out.crop.top) |
- FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height));
+ FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1));
regmap_write(ge2d->map, GE2D_SRC2_CLIPX_START_END,
FIELD_PREP(GE2D_START, ctx->out.crop.left) |
- FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width));
+ FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1));
regmap_write(ge2d->map, GE2D_DST_CLIPY_START_END,
FIELD_PREP(GE2D_START, ctx->out.crop.top) |
- FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height));
+ FIELD_PREP(GE2D_END, ctx->out.crop.top + ctx->out.crop.height - 1));
regmap_write(ge2d->map, GE2D_DST_CLIPX_START_END,
FIELD_PREP(GE2D_START, ctx->out.crop.left) |
- FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width));
+ FIELD_PREP(GE2D_END, ctx->out.crop.left + ctx->out.crop.width - 1));
regmap_write(ge2d->map, GE2D_SRC1_Y_START_END,
- FIELD_PREP(GE2D_END, ctx->in.pix_fmt.height));
+ FIELD_PREP(GE2D_END, ctx->in.pix_fmt.height - 1));
regmap_write(ge2d->map, GE2D_SRC1_X_START_END,
- FIELD_PREP(GE2D_END, ctx->in.pix_fmt.width));
+ FIELD_PREP(GE2D_END, ctx->in.pix_fmt.width - 1));
regmap_write(ge2d->map, GE2D_SRC2_Y_START_END,
- FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height));
+ FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1));
regmap_write(ge2d->map, GE2D_SRC2_X_START_END,
- FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width));
+ FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1));
regmap_write(ge2d->map, GE2D_DST_Y_START_END,
- FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height));
+ FIELD_PREP(GE2D_END, ctx->out.pix_fmt.height - 1));
regmap_write(ge2d->map, GE2D_DST_X_START_END,
- FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width));
+ FIELD_PREP(GE2D_END, ctx->out.pix_fmt.width - 1));
/* Color, no blend, use source color */
reg = GE2D_ALU_DO_COLOR_OPERATION_LOGIC(LOGIC_OPERATION_COPY,
diff --git a/drivers/media/platform/amphion/Kconfig b/drivers/media/platform/amphion/Kconfig
new file mode 100644
index 000000000000..4a363e07ccc9
--- /dev/null
+++ b/drivers/media/platform/amphion/Kconfig
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Amphion drivers"
+
+config VIDEO_AMPHION_VPU
+ tristate "Amphion VPU (Video Processing Unit) Codec IP"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on MEDIA_SUPPORT
+ depends on VIDEO_DEV && MAILBOX
+ select MEDIA_CONTROLLER
+ select V4L2_MEM2MEM_DEV
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ help
+ Amphion VPU Codec IP contains two parts: Windsor and Malone.
+ Windsor is encoder that supports H.264, and Malone is decoder
+ that supports H.264, HEVC, and other video formats.
+ This is a V4L2 driver for NXP MXC 8Q video accelerator hardware.
+ It accelerates encoding and decoding operations on
+ various NXP SoCs.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amphion/Makefile b/drivers/media/platform/amphion/Makefile
new file mode 100644
index 000000000000..80717312835f
--- /dev/null
+++ b/drivers/media/platform/amphion/Makefile
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+# Makefile for NXP VPU driver
+
+amphion-vpu-objs += vpu_drv.o \
+ vpu_core.o \
+ vpu_mbox.o \
+ vpu_v4l2.o \
+ vpu_helpers.o \
+ vpu_cmds.o \
+ vpu_msgs.o \
+ vpu_rpc.o \
+ vpu_imx8q.o \
+ vpu_windsor.o \
+ vpu_malone.o \
+ vpu_color.o \
+ vdec.o \
+ venc.o \
+ vpu_dbg.o
+
+obj-$(CONFIG_VIDEO_AMPHION_VPU) += amphion-vpu.o
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
new file mode 100644
index 000000000000..8f8dfd6ce2c6
--- /dev/null
+++ b/drivers/media/platform/amphion/vdec.c
@@ -0,0 +1,1656 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include "vpu.h"
+#include "vpu_defs.h"
+#include "vpu_core.h"
+#include "vpu_helpers.h"
+#include "vpu_v4l2.h"
+#include "vpu_cmds.h"
+#include "vpu_rpc.h"
+
+#define VDEC_FRAME_DEPTH 256
+#define VDEC_MIN_BUFFER_CAP 8
+
+struct vdec_fs_info {
+ char name[8];
+ u32 type;
+ u32 max_count;
+ u32 req_count;
+ u32 count;
+ u32 index;
+ u32 size;
+ struct vpu_buffer buffer[32];
+ u32 tag;
+};
+
+struct vdec_t {
+ u32 seq_hdr_found;
+ struct vpu_buffer udata;
+ struct vpu_decode_params params;
+ struct vpu_dec_codec_info codec_info;
+ enum vpu_codec_state state;
+
+ struct vpu_vb2_buffer *slots[VB2_MAX_FRAME];
+ u32 req_frame_count;
+ struct vdec_fs_info mbi;
+ struct vdec_fs_info dcp;
+ u32 seq_tag;
+
+ bool reset_codec;
+ bool fixed_fmt;
+ u32 decoded_frame_count;
+ u32 display_frame_count;
+ u32 sequence;
+ u32 eos_received;
+ bool is_source_changed;
+ u32 source_change;
+ u32 drain;
+ u32 ts_pre_count;
+ u32 frame_depth;
+};
+
+static const struct vpu_format vdec_formats[] = {
+ {
+ .pixfmt = V4L2_PIX_FMT_NV12M_8L128,
+ .num_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_NV12M_10BE_8L128,
+ .num_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_H264_MVC,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_HEVC,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_G,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_VC1_ANNEX_L,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_MPEG2,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_MPEG4,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_XVID,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_VP8,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_H263,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION
+ },
+ {0, 0, 0, 0},
+};
+
+static const struct v4l2_ctrl_ops vdec_ctrl_ops = {
+ .g_volatile_ctrl = vpu_helper_g_volatile_ctrl,
+};
+
+static int vdec_ctrl_init(struct vpu_inst *inst)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 20);
+ if (ret)
+ return ret;
+
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 2);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler);
+ if (ret) {
+ dev_err(inst->dev, "[%d] setup ctrls fail, ret = %d\n", inst->id, ret);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void vdec_set_last_buffer_dequeued(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (vdec->eos_received) {
+ if (!vpu_set_last_buffer_dequeued(inst))
+ vdec->eos_received--;
+ }
+}
+
+static void vdec_handle_resolution_change(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vb2_queue *q;
+
+ if (!inst->fh.m2m_ctx)
+ return;
+
+ if (inst->state != VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE)
+ return;
+ if (!vdec->source_change)
+ return;
+
+ q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
+ if (!list_empty(&q->done_list))
+ return;
+
+ vdec->source_change--;
+ vpu_notify_source_change(inst);
+}
+
+static int vdec_update_state(struct vpu_inst *inst, enum vpu_codec_state state, u32 force)
+{
+ struct vdec_t *vdec = inst->priv;
+ enum vpu_codec_state pre_state = inst->state;
+
+ if (state == VPU_CODEC_STATE_SEEK) {
+ if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE)
+ vdec->state = inst->state;
+ else
+ vdec->state = VPU_CODEC_STATE_ACTIVE;
+ }
+ if (inst->state != VPU_CODEC_STATE_SEEK || force)
+ inst->state = state;
+ else if (state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE)
+ vdec->state = VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE;
+
+ if (inst->state != pre_state)
+ vpu_trace(inst->dev, "[%d] %d -> %d\n", inst->id, pre_state, inst->state);
+
+ if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE)
+ vdec_handle_resolution_change(inst);
+
+ return 0;
+}
+
+static int vdec_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, "amphion-vpu", sizeof(cap->driver));
+ strscpy(cap->card, "amphion vpu decoder", sizeof(cap->card));
+ strscpy(cap->bus_info, "platform: amphion-vpu", sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int vdec_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct vdec_t *vdec = inst->priv;
+ const struct vpu_format *fmt;
+ int ret = -EINVAL;
+
+ vpu_inst_lock(inst);
+ if (!V4L2_TYPE_IS_OUTPUT(f->type) && vdec->fixed_fmt) {
+ if (f->index == 0) {
+ f->pixelformat = inst->cap_format.pixfmt;
+ f->flags = inst->cap_format.flags;
+ ret = 0;
+ }
+ } else {
+ fmt = vpu_helper_enum_format(inst, f->type, f->index);
+ memset(f->reserved, 0, sizeof(f->reserved));
+ if (!fmt)
+ goto exit;
+
+ f->pixelformat = fmt->pixfmt;
+ f->flags = fmt->flags;
+ ret = 0;
+ }
+
+exit:
+ vpu_inst_unlock(inst);
+ return ret;
+}
+
+static int vdec_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct vdec_t *vdec = inst->priv;
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct vpu_format *cur_fmt;
+ int i;
+
+ cur_fmt = vpu_get_format(inst, f->type);
+
+ pixmp->pixelformat = cur_fmt->pixfmt;
+ pixmp->num_planes = cur_fmt->num_planes;
+ pixmp->width = cur_fmt->width;
+ pixmp->height = cur_fmt->height;
+ pixmp->field = cur_fmt->field;
+ pixmp->flags = cur_fmt->flags;
+ for (i = 0; i < pixmp->num_planes; i++) {
+ pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i];
+ pixmp->plane_fmt[i].sizeimage = cur_fmt->sizeimage[i];
+ }
+
+ f->fmt.pix_mp.colorspace = vdec->codec_info.color_primaries;
+ f->fmt.pix_mp.xfer_func = vdec->codec_info.transfer_chars;
+ f->fmt.pix_mp.ycbcr_enc = vdec->codec_info.matrix_coeffs;
+ f->fmt.pix_mp.quantization = vdec->codec_info.full_range;
+
+ return 0;
+}
+
+static int vdec_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct vdec_t *vdec = inst->priv;
+
+ vpu_try_fmt_common(inst, f);
+
+ vpu_inst_lock(inst);
+ if (vdec->fixed_fmt) {
+ f->fmt.pix_mp.colorspace = vdec->codec_info.color_primaries;
+ f->fmt.pix_mp.xfer_func = vdec->codec_info.transfer_chars;
+ f->fmt.pix_mp.ycbcr_enc = vdec->codec_info.matrix_coeffs;
+ f->fmt.pix_mp.quantization = vdec->codec_info.full_range;
+ } else {
+ 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;
+ }
+ vpu_inst_unlock(inst);
+
+ return 0;
+}
+
+static int vdec_s_fmt_common(struct vpu_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ const struct vpu_format *fmt;
+ struct vpu_format *cur_fmt;
+ struct vb2_queue *q;
+ struct vdec_t *vdec = inst->priv;
+ int i;
+
+ if (!inst->fh.m2m_ctx)
+ return -EINVAL;
+
+ q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
+ if (!q)
+ return -EINVAL;
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ fmt = vpu_try_fmt_common(inst, f);
+ if (!fmt)
+ return -EINVAL;
+
+ cur_fmt = vpu_get_format(inst, f->type);
+ if (V4L2_TYPE_IS_OUTPUT(f->type) && inst->state != VPU_CODEC_STATE_DEINIT) {
+ if (cur_fmt->pixfmt != fmt->pixfmt) {
+ vdec->reset_codec = true;
+ vdec->fixed_fmt = false;
+ }
+ }
+ cur_fmt->pixfmt = fmt->pixfmt;
+ if (V4L2_TYPE_IS_OUTPUT(f->type) || !vdec->fixed_fmt) {
+ cur_fmt->num_planes = fmt->num_planes;
+ cur_fmt->flags = fmt->flags;
+ cur_fmt->width = pixmp->width;
+ cur_fmt->height = pixmp->height;
+ for (i = 0; i < fmt->num_planes; i++) {
+ cur_fmt->sizeimage[i] = pixmp->plane_fmt[i].sizeimage;
+ cur_fmt->bytesperline[i] = pixmp->plane_fmt[i].bytesperline;
+ }
+ if (pixmp->field != V4L2_FIELD_ANY)
+ cur_fmt->field = pixmp->field;
+ } else {
+ pixmp->num_planes = cur_fmt->num_planes;
+ pixmp->width = cur_fmt->width;
+ pixmp->height = cur_fmt->height;
+ for (i = 0; i < pixmp->num_planes; i++) {
+ pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i];
+ pixmp->plane_fmt[i].sizeimage = cur_fmt->sizeimage[i];
+ }
+ pixmp->field = cur_fmt->field;
+ }
+
+ if (!vdec->fixed_fmt) {
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ vdec->params.codec_format = cur_fmt->pixfmt;
+ vdec->codec_info.color_primaries = f->fmt.pix_mp.colorspace;
+ vdec->codec_info.transfer_chars = f->fmt.pix_mp.xfer_func;
+ vdec->codec_info.matrix_coeffs = f->fmt.pix_mp.ycbcr_enc;
+ vdec->codec_info.full_range = f->fmt.pix_mp.quantization;
+ } else {
+ vdec->params.output_format = cur_fmt->pixfmt;
+ inst->crop.left = 0;
+ inst->crop.top = 0;
+ inst->crop.width = cur_fmt->width;
+ inst->crop.height = cur_fmt->height;
+ }
+ }
+
+ vpu_trace(inst->dev, "[%d] %c%c%c%c %dx%d\n", inst->id,
+ f->fmt.pix_mp.pixelformat,
+ f->fmt.pix_mp.pixelformat >> 8,
+ f->fmt.pix_mp.pixelformat >> 16,
+ f->fmt.pix_mp.pixelformat >> 24,
+ f->fmt.pix_mp.width,
+ f->fmt.pix_mp.height);
+
+ return 0;
+}
+
+static int vdec_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct vdec_t *vdec = inst->priv;
+ int ret = 0;
+
+ vpu_inst_lock(inst);
+ ret = vdec_s_fmt_common(inst, f);
+ if (ret)
+ goto exit;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type) && !vdec->fixed_fmt) {
+ struct v4l2_format fc;
+
+ memset(&fc, 0, sizeof(fc));
+ fc.type = inst->cap_format.type;
+ fc.fmt.pix_mp.pixelformat = inst->cap_format.pixfmt;
+ fc.fmt.pix_mp.width = pixmp->width;
+ fc.fmt.pix_mp.height = pixmp->height;
+ vdec_s_fmt_common(inst, &fc);
+ }
+
+ f->fmt.pix_mp.colorspace = vdec->codec_info.color_primaries;
+ f->fmt.pix_mp.xfer_func = vdec->codec_info.transfer_chars;
+ f->fmt.pix_mp.ycbcr_enc = vdec->codec_info.matrix_coeffs;
+ f->fmt.pix_mp.quantization = vdec->codec_info.full_range;
+
+exit:
+ vpu_inst_unlock(inst);
+ return ret;
+}
+
+static int vdec_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_inst *inst = to_inst(file);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ s->r = inst->crop;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->cap_format.width;
+ s->r.height = inst->cap_format.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vdec_drain(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (!inst->fh.m2m_ctx)
+ return 0;
+
+ if (!vdec->drain)
+ return 0;
+
+ if (v4l2_m2m_num_src_bufs_ready(inst->fh.m2m_ctx))
+ return 0;
+
+ if (!vdec->params.frame_count) {
+ vpu_set_last_buffer_dequeued(inst);
+ return 0;
+ }
+
+ vpu_iface_add_scode(inst, SCODE_PADDING_EOS);
+ vdec->params.end_flag = 1;
+ vpu_iface_set_decode_params(inst, &vdec->params, 1);
+ vdec->drain = 0;
+ vpu_trace(inst->dev, "[%d] frame_count = %d\n", inst->id, vdec->params.frame_count);
+
+ return 0;
+}
+
+static int vdec_cmd_start(struct vpu_inst *inst)
+{
+ switch (inst->state) {
+ case VPU_CODEC_STATE_STARTED:
+ case VPU_CODEC_STATE_DRAIN:
+ case VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE:
+ vdec_update_state(inst, VPU_CODEC_STATE_ACTIVE, 0);
+ break;
+ default:
+ break;
+ }
+ vpu_process_capture_buffer(inst);
+ return 0;
+}
+
+static int vdec_cmd_stop(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+
+ if (inst->state == VPU_CODEC_STATE_DEINIT) {
+ vpu_set_last_buffer_dequeued(inst);
+ } else {
+ vdec->drain = 1;
+ vdec_drain(inst);
+ }
+
+ return 0;
+}
+
+static int vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd)
+{
+ struct vpu_inst *inst = to_inst(file);
+ int ret;
+
+ ret = v4l2_m2m_ioctl_try_decoder_cmd(file, fh, cmd);
+ if (ret)
+ return ret;
+
+ vpu_inst_lock(inst);
+ switch (cmd->cmd) {
+ case V4L2_DEC_CMD_START:
+ vdec_cmd_start(inst);
+ break;
+ case V4L2_DEC_CMD_STOP:
+ vdec_cmd_stop(inst);
+ break;
+ default:
+ break;
+ }
+ vpu_inst_unlock(inst);
+
+ return 0;
+}
+
+static int vdec_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ 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:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops vdec_ioctl_ops = {
+ .vidioc_querycap = vdec_querycap,
+ .vidioc_enum_fmt_vid_cap = vdec_enum_fmt,
+ .vidioc_enum_fmt_vid_out = vdec_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = vdec_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = vdec_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = vdec_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = vdec_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = vdec_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = vdec_s_fmt,
+ .vidioc_g_selection = vdec_g_selection,
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
+ .vidioc_decoder_cmd = vdec_decoder_cmd,
+ .vidioc_subscribe_event = vdec_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+};
+
+static bool vdec_check_ready(struct vpu_inst *inst, unsigned int type)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (vdec->ts_pre_count >= vdec->frame_depth)
+ return false;
+ return true;
+ }
+
+ if (vdec->req_frame_count)
+ return true;
+
+ return false;
+}
+
+static int vdec_frame_decoded(struct vpu_inst *inst, void *arg)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vpu_dec_pic_info *info = arg;
+ struct vpu_vb2_buffer *vpu_buf;
+ struct vb2_v4l2_buffer *vbuf;
+ int ret = 0;
+
+ if (!info || info->id >= ARRAY_SIZE(vdec->slots))
+ return -EINVAL;
+
+ vpu_inst_lock(inst);
+ vpu_buf = vdec->slots[info->id];
+ if (!vpu_buf) {
+ dev_err(inst->dev, "[%d] decoded invalid frame[%d]\n", inst->id, info->id);
+ ret = -EINVAL;
+ goto exit;
+ }
+ vbuf = &vpu_buf->m2m_buf.vb;
+ if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED)
+ dev_info(inst->dev, "[%d] buf[%d] has been decoded\n", inst->id, info->id);
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_DECODED);
+ vdec->decoded_frame_count++;
+ if (vdec->ts_pre_count >= info->consumed_count)
+ vdec->ts_pre_count -= info->consumed_count;
+ else
+ vdec->ts_pre_count = 0;
+exit:
+ vpu_inst_unlock(inst);
+
+ return ret;
+}
+
+static struct vpu_vb2_buffer *vdec_find_buffer(struct vpu_inst *inst, u32 luma)
+{
+ struct vdec_t *vdec = inst->priv;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) {
+ if (!vdec->slots[i])
+ continue;
+ if (luma == vdec->slots[i]->luma)
+ return vdec->slots[i];
+ }
+
+ return NULL;
+}
+
+static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vpu_vb2_buffer *vpu_buf;
+ struct vb2_v4l2_buffer *vbuf;
+ u32 sequence;
+
+ if (!frame)
+ return;
+
+ vpu_inst_lock(inst);
+ sequence = vdec->sequence++;
+ vpu_buf = vdec_find_buffer(inst, frame->luma);
+ vpu_inst_unlock(inst);
+ if (!vpu_buf) {
+ dev_err(inst->dev, "[%d] can't find buffer, id = %d, addr = 0x%x\n",
+ inst->id, frame->id, frame->luma);
+ return;
+ }
+ if (frame->skipped) {
+ dev_dbg(inst->dev, "[%d] frame skip\n", inst->id);
+ return;
+ }
+
+ vbuf = &vpu_buf->m2m_buf.vb;
+ if (vbuf->vb2_buf.index != frame->id)
+ dev_err(inst->dev, "[%d] buffer id(%d, %d) dismatch\n",
+ inst->id, vbuf->vb2_buf.index, frame->id);
+
+ if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_DECODED)
+ dev_err(inst->dev, "[%d] buffer(%d) ready without decoded\n", inst->id, frame->id);
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_READY);
+ vb2_set_plane_payload(&vbuf->vb2_buf, 0, inst->cap_format.sizeimage[0]);
+ vb2_set_plane_payload(&vbuf->vb2_buf, 1, inst->cap_format.sizeimage[1]);
+ vbuf->vb2_buf.timestamp = frame->timestamp;
+ vbuf->field = inst->cap_format.field;
+ vbuf->sequence = sequence;
+ dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, frame->timestamp);
+
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ vpu_inst_lock(inst);
+ vdec->display_frame_count++;
+ vpu_inst_unlock(inst);
+ dev_dbg(inst->dev, "[%d] decoded : %d, display : %d, sequence : %d\n",
+ inst->id, vdec->decoded_frame_count, vdec->display_frame_count, vdec->sequence);
+}
+
+static void vdec_stop_done(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vpu_inst_lock(inst);
+ vdec_update_state(inst, VPU_CODEC_STATE_DEINIT, 0);
+ vdec->seq_hdr_found = 0;
+ vdec->req_frame_count = 0;
+ vdec->reset_codec = false;
+ vdec->fixed_fmt = false;
+ vdec->params.end_flag = 0;
+ vdec->drain = 0;
+ vdec->ts_pre_count = 0;
+ vdec->params.frame_count = 0;
+ vdec->decoded_frame_count = 0;
+ vdec->display_frame_count = 0;
+ vdec->sequence = 0;
+ vdec->eos_received = 0;
+ vdec->is_source_changed = false;
+ vdec->source_change = 0;
+ vpu_inst_unlock(inst);
+}
+
+static bool vdec_check_source_change(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+ const struct vpu_format *fmt;
+ int i;
+
+ if (!inst->fh.m2m_ctx)
+ return false;
+
+ if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx)))
+ return true;
+ fmt = vpu_helper_find_format(inst, inst->cap_format.type, vdec->codec_info.pixfmt);
+ if (inst->cap_format.pixfmt != vdec->codec_info.pixfmt)
+ return true;
+ if (inst->cap_format.width != vdec->codec_info.decoded_width)
+ return true;
+ if (inst->cap_format.height != vdec->codec_info.decoded_height)
+ return true;
+ if (vpu_get_num_buffers(inst, inst->cap_format.type) < inst->min_buffer_cap)
+ return true;
+ if (inst->crop.left != vdec->codec_info.offset_x)
+ return true;
+ if (inst->crop.top != vdec->codec_info.offset_y)
+ return true;
+ if (inst->crop.width != vdec->codec_info.width)
+ return true;
+ if (inst->crop.height != vdec->codec_info.height)
+ return true;
+ if (fmt && inst->cap_format.num_planes != fmt->num_planes)
+ return true;
+ for (i = 0; i < inst->cap_format.num_planes; i++) {
+ if (inst->cap_format.bytesperline[i] != vdec->codec_info.bytesperline[i])
+ return true;
+ if (inst->cap_format.sizeimage[i] != vdec->codec_info.sizeimage[i])
+ return true;
+ }
+
+ return false;
+}
+
+static void vdec_init_fmt(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+ const struct vpu_format *fmt;
+ int i;
+
+ fmt = vpu_helper_find_format(inst, inst->cap_format.type, vdec->codec_info.pixfmt);
+ inst->out_format.width = vdec->codec_info.width;
+ inst->out_format.height = vdec->codec_info.height;
+ inst->cap_format.width = vdec->codec_info.decoded_width;
+ inst->cap_format.height = vdec->codec_info.decoded_height;
+ inst->cap_format.pixfmt = vdec->codec_info.pixfmt;
+ if (fmt) {
+ inst->cap_format.num_planes = fmt->num_planes;
+ inst->cap_format.flags = fmt->flags;
+ }
+ for (i = 0; i < inst->cap_format.num_planes; i++) {
+ inst->cap_format.bytesperline[i] = vdec->codec_info.bytesperline[i];
+ inst->cap_format.sizeimage[i] = vdec->codec_info.sizeimage[i];
+ }
+ if (vdec->codec_info.progressive)
+ inst->cap_format.field = V4L2_FIELD_NONE;
+ else
+ inst->cap_format.field = V4L2_FIELD_SEQ_BT;
+ if (vdec->codec_info.color_primaries == V4L2_COLORSPACE_DEFAULT)
+ vdec->codec_info.color_primaries = V4L2_COLORSPACE_REC709;
+ if (vdec->codec_info.transfer_chars == V4L2_XFER_FUNC_DEFAULT)
+ vdec->codec_info.transfer_chars = V4L2_XFER_FUNC_709;
+ if (vdec->codec_info.matrix_coeffs == V4L2_YCBCR_ENC_DEFAULT)
+ vdec->codec_info.matrix_coeffs = V4L2_YCBCR_ENC_709;
+ if (vdec->codec_info.full_range == V4L2_QUANTIZATION_DEFAULT)
+ vdec->codec_info.full_range = V4L2_QUANTIZATION_LIM_RANGE;
+}
+
+static void vdec_init_crop(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ inst->crop.left = vdec->codec_info.offset_x;
+ inst->crop.top = vdec->codec_info.offset_y;
+ inst->crop.width = vdec->codec_info.width;
+ inst->crop.height = vdec->codec_info.height;
+}
+
+static void vdec_init_mbi(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vdec->mbi.size = vdec->codec_info.mbi_size;
+ vdec->mbi.max_count = ARRAY_SIZE(vdec->mbi.buffer);
+ scnprintf(vdec->mbi.name, sizeof(vdec->mbi.name), "mbi");
+ vdec->mbi.type = MEM_RES_MBI;
+ vdec->mbi.tag = vdec->seq_tag;
+}
+
+static void vdec_init_dcp(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vdec->dcp.size = vdec->codec_info.dcp_size;
+ vdec->dcp.max_count = ARRAY_SIZE(vdec->dcp.buffer);
+ scnprintf(vdec->dcp.name, sizeof(vdec->dcp.name), "dcp");
+ vdec->dcp.type = MEM_RES_DCP;
+ vdec->dcp.tag = vdec->seq_tag;
+}
+
+static void vdec_request_one_fs(struct vdec_fs_info *fs)
+{
+ fs->req_count++;
+ if (fs->req_count > fs->max_count)
+ fs->req_count = fs->max_count;
+}
+
+static int vdec_alloc_fs_buffer(struct vpu_inst *inst, struct vdec_fs_info *fs)
+{
+ struct vpu_buffer *buffer;
+
+ if (!fs->size)
+ return -EINVAL;
+
+ if (fs->count >= fs->req_count)
+ return -EINVAL;
+
+ buffer = &fs->buffer[fs->count];
+ if (buffer->virt && buffer->length >= fs->size)
+ return 0;
+
+ vpu_free_dma(buffer);
+ buffer->length = fs->size;
+ return vpu_alloc_dma(inst->core, buffer);
+}
+
+static void vdec_alloc_fs(struct vpu_inst *inst, struct vdec_fs_info *fs)
+{
+ int ret;
+
+ while (fs->count < fs->req_count) {
+ ret = vdec_alloc_fs_buffer(inst, fs);
+ if (ret)
+ break;
+ fs->count++;
+ }
+}
+
+static void vdec_clear_fs(struct vdec_fs_info *fs)
+{
+ u32 i;
+
+ if (!fs)
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(fs->buffer); i++)
+ vpu_free_dma(&fs->buffer[i]);
+ memset(fs, 0, sizeof(*fs));
+}
+
+static int vdec_response_fs(struct vpu_inst *inst, struct vdec_fs_info *fs)
+{
+ struct vpu_fs_info info;
+ int ret;
+
+ if (fs->index >= fs->count)
+ return 0;
+
+ memset(&info, 0, sizeof(info));
+ info.id = fs->index;
+ info.type = fs->type;
+ info.tag = fs->tag;
+ info.luma_addr = fs->buffer[fs->index].phys;
+ info.luma_size = fs->buffer[fs->index].length;
+ ret = vpu_session_alloc_fs(inst, &info);
+ if (ret)
+ return ret;
+
+ fs->index++;
+ return 0;
+}
+
+static int vdec_response_frame_abnormal(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vpu_fs_info info;
+
+ if (!vdec->req_frame_count)
+ return 0;
+
+ memset(&info, 0, sizeof(info));
+ info.type = MEM_RES_FRAME;
+ info.tag = vdec->seq_tag + 0xf0;
+ vpu_session_alloc_fs(inst, &info);
+ vdec->req_frame_count--;
+
+ return 0;
+}
+
+static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vpu_vb2_buffer *vpu_buf;
+ struct vpu_fs_info info;
+ int ret;
+
+ if (inst->state != VPU_CODEC_STATE_ACTIVE)
+ return -EINVAL;
+
+ if (!vdec->req_frame_count)
+ return -EINVAL;
+
+ if (!vbuf)
+ return -EINVAL;
+
+ if (vdec->slots[vbuf->vb2_buf.index]) {
+ dev_err(inst->dev, "[%d] repeat alloc fs %d\n",
+ inst->id, vbuf->vb2_buf.index);
+ return -EINVAL;
+ }
+
+ dev_dbg(inst->dev, "[%d] state = %d, alloc fs %d, tag = 0x%x\n",
+ inst->id, inst->state, vbuf->vb2_buf.index, vdec->seq_tag);
+ vpu_buf = to_vpu_vb2_buffer(vbuf);
+
+ memset(&info, 0, sizeof(info));
+ info.id = vbuf->vb2_buf.index;
+ info.type = MEM_RES_FRAME;
+ info.tag = vdec->seq_tag;
+ info.luma_addr = vpu_get_vb_phy_addr(&vbuf->vb2_buf, 0);
+ info.luma_size = inst->cap_format.sizeimage[0];
+ info.chroma_addr = vpu_get_vb_phy_addr(&vbuf->vb2_buf, 1);
+ info.chromau_size = inst->cap_format.sizeimage[1];
+ info.bytesperline = inst->cap_format.bytesperline[0];
+ ret = vpu_session_alloc_fs(inst, &info);
+ if (ret)
+ return ret;
+
+ vpu_buf->tag = info.tag;
+ vpu_buf->luma = info.luma_addr;
+ vpu_buf->chroma_u = info.chromau_size;
+ vpu_buf->chroma_v = 0;
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE);
+ vdec->slots[info.id] = vpu_buf;
+ vdec->req_frame_count--;
+
+ return 0;
+}
+
+static void vdec_response_fs_request(struct vpu_inst *inst, bool force)
+{
+ struct vdec_t *vdec = inst->priv;
+ int i;
+ int ret;
+
+ if (force) {
+ for (i = vdec->req_frame_count; i > 0; i--)
+ vdec_response_frame_abnormal(inst);
+ return;
+ }
+
+ for (i = vdec->req_frame_count; i > 0; i--) {
+ ret = vpu_process_capture_buffer(inst);
+ if (ret)
+ break;
+ if (vdec->eos_received)
+ break;
+ }
+
+ for (i = vdec->mbi.index; i < vdec->mbi.count; i++) {
+ if (vdec_response_fs(inst, &vdec->mbi))
+ break;
+ if (vdec->eos_received)
+ break;
+ }
+ for (i = vdec->dcp.index; i < vdec->dcp.count; i++) {
+ if (vdec_response_fs(inst, &vdec->dcp))
+ break;
+ if (vdec->eos_received)
+ break;
+ }
+}
+
+static void vdec_response_fs_release(struct vpu_inst *inst, u32 id, u32 tag)
+{
+ struct vpu_fs_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.id = id;
+ info.tag = tag;
+ vpu_session_release_fs(inst, &info);
+}
+
+static void vdec_recycle_buffer(struct vpu_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ if (!inst->fh.m2m_ctx)
+ return;
+ if (vbuf->vb2_buf.state != VB2_BUF_STATE_ACTIVE)
+ return;
+ if (vpu_find_buf_by_idx(inst, vbuf->vb2_buf.type, vbuf->vb2_buf.index))
+ return;
+ v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf);
+}
+
+static void vdec_clear_slots(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vpu_vb2_buffer *vpu_buf;
+ struct vb2_v4l2_buffer *vbuf;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) {
+ if (!vdec->slots[i])
+ continue;
+
+ vpu_buf = vdec->slots[i];
+ vbuf = &vpu_buf->m2m_buf.vb;
+
+ vdec_recycle_buffer(inst, vbuf);
+ vdec->slots[i]->state = VPU_BUF_STATE_IDLE;
+ vdec->slots[i] = NULL;
+ }
+}
+
+static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info *hdr)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vpu_inst_lock(inst);
+ memcpy(&vdec->codec_info, hdr, sizeof(vdec->codec_info));
+
+ vpu_trace(inst->dev, "[%d] %d x %d, crop : (%d, %d) %d x %d, %d, %d\n",
+ inst->id,
+ vdec->codec_info.decoded_width,
+ vdec->codec_info.decoded_height,
+ vdec->codec_info.offset_x,
+ vdec->codec_info.offset_y,
+ vdec->codec_info.width,
+ vdec->codec_info.height,
+ hdr->num_ref_frms,
+ hdr->num_dpb_frms);
+ inst->min_buffer_cap = hdr->num_ref_frms + hdr->num_dpb_frms;
+ vdec->is_source_changed = vdec_check_source_change(inst);
+ vdec_init_fmt(inst);
+ vdec_init_crop(inst);
+ vdec_init_mbi(inst);
+ vdec_init_dcp(inst);
+ if (!vdec->seq_hdr_found) {
+ vdec->seq_tag = vdec->codec_info.tag;
+ if (vdec->is_source_changed) {
+ vdec_update_state(inst, VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE, 0);
+ vpu_notify_source_change(inst);
+ vdec->is_source_changed = false;
+ }
+ }
+ if (vdec->seq_tag != vdec->codec_info.tag) {
+ vdec_response_fs_request(inst, true);
+ vpu_trace(inst->dev, "[%d] seq tag change: %d -> %d\n",
+ inst->id, vdec->seq_tag, vdec->codec_info.tag);
+ }
+ vdec->seq_hdr_found++;
+ vdec->fixed_fmt = true;
+ vpu_inst_unlock(inst);
+}
+
+static void vdec_event_resolution_change(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+ vpu_inst_lock(inst);
+ vdec->seq_tag = vdec->codec_info.tag;
+ vdec_clear_fs(&vdec->mbi);
+ vdec_clear_fs(&vdec->dcp);
+ vdec_clear_slots(inst);
+ vdec_init_mbi(inst);
+ vdec_init_dcp(inst);
+ if (vdec->is_source_changed) {
+ vdec_update_state(inst, VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE, 0);
+ vdec->source_change++;
+ vdec_handle_resolution_change(inst);
+ vdec->is_source_changed = false;
+ }
+ vpu_inst_unlock(inst);
+}
+
+static void vdec_event_req_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (!fs)
+ return;
+
+ vpu_inst_lock(inst);
+
+ switch (fs->type) {
+ case MEM_RES_FRAME:
+ vdec->req_frame_count++;
+ break;
+ case MEM_RES_MBI:
+ vdec_request_one_fs(&vdec->mbi);
+ break;
+ case MEM_RES_DCP:
+ vdec_request_one_fs(&vdec->dcp);
+ break;
+ default:
+ break;
+ }
+
+ vdec_alloc_fs(inst, &vdec->mbi);
+ vdec_alloc_fs(inst, &vdec->dcp);
+
+ vdec_response_fs_request(inst, false);
+
+ vpu_inst_unlock(inst);
+}
+
+static void vdec_evnet_rel_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vpu_vb2_buffer *vpu_buf;
+ struct vb2_v4l2_buffer *vbuf;
+
+ if (!fs || fs->id >= ARRAY_SIZE(vdec->slots))
+ return;
+ if (fs->type != MEM_RES_FRAME)
+ return;
+
+ if (fs->id >= vpu_get_num_buffers(inst, inst->cap_format.type)) {
+ dev_err(inst->dev, "[%d] invalid fs(%d) to release\n", inst->id, fs->id);
+ return;
+ }
+
+ vpu_inst_lock(inst);
+ vpu_buf = vdec->slots[fs->id];
+ vdec->slots[fs->id] = NULL;
+
+ if (!vpu_buf) {
+ dev_dbg(inst->dev, "[%d] fs[%d] has bee released\n", inst->id, fs->id);
+ goto exit;
+ }
+
+ vbuf = &vpu_buf->m2m_buf.vb;
+ if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED) {
+ dev_dbg(inst->dev, "[%d] frame skip\n", inst->id);
+ vdec->sequence++;
+ }
+
+ vdec_response_fs_release(inst, fs->id, vpu_buf->tag);
+ if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_READY)
+ vdec_recycle_buffer(inst, vbuf);
+
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ vpu_process_capture_buffer(inst);
+
+exit:
+ vpu_inst_unlock(inst);
+}
+
+static void vdec_event_eos(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vpu_trace(inst->dev, "[%d] input : %d, decoded : %d, display : %d, sequence : %d\n",
+ inst->id,
+ vdec->params.frame_count,
+ vdec->decoded_frame_count,
+ vdec->display_frame_count,
+ vdec->sequence);
+ vpu_inst_lock(inst);
+ vdec->eos_received++;
+ vdec->fixed_fmt = false;
+ inst->min_buffer_cap = VDEC_MIN_BUFFER_CAP;
+ vdec_update_state(inst, VPU_CODEC_STATE_DRAIN, 0);
+ vdec_set_last_buffer_dequeued(inst);
+ vpu_inst_unlock(inst);
+}
+
+static void vdec_event_notify(struct vpu_inst *inst, u32 event, void *data)
+{
+ switch (event) {
+ case VPU_MSG_ID_SEQ_HDR_FOUND:
+ vdec_event_seq_hdr(inst, data);
+ break;
+ case VPU_MSG_ID_RES_CHANGE:
+ vdec_event_resolution_change(inst);
+ break;
+ case VPU_MSG_ID_FRAME_REQ:
+ vdec_event_req_fs(inst, data);
+ break;
+ case VPU_MSG_ID_FRAME_RELEASE:
+ vdec_evnet_rel_fs(inst, data);
+ break;
+ case VPU_MSG_ID_PIC_EOS:
+ vdec_event_eos(inst);
+ break;
+ default:
+ break;
+ }
+}
+
+static int vdec_process_output(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vb2_v4l2_buffer *vbuf;
+ struct vpu_rpc_buffer_desc desc;
+ u32 free_space;
+ int ret;
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ dev_dbg(inst->dev, "[%d] dec output [%d] %d : %ld\n",
+ inst->id, vbuf->sequence, vb->index, vb2_get_plane_payload(vb, 0));
+
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ return -EINVAL;
+ if (vdec->reset_codec)
+ return -EINVAL;
+
+ if (inst->state == VPU_CODEC_STATE_STARTED)
+ vdec_update_state(inst, VPU_CODEC_STATE_ACTIVE, 0);
+
+ ret = vpu_iface_get_stream_buffer_desc(inst, &desc);
+ if (ret)
+ return ret;
+
+ free_space = vpu_helper_get_free_space(inst);
+ if (free_space < vb2_get_plane_payload(vb, 0) + 0x40000)
+ return -ENOMEM;
+
+ ret = vpu_iface_input_frame(inst, vb);
+ if (ret < 0)
+ return -ENOMEM;
+
+ dev_dbg(inst->dev, "[%d][INPUT TS]%32lld\n", inst->id, vb->timestamp);
+ vdec->ts_pre_count++;
+ vdec->params.frame_count++;
+
+ v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+
+ if (vdec->drain)
+ vdec_drain(inst);
+
+ return 0;
+}
+
+static int vdec_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ int ret;
+
+ if (inst->state == VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE)
+ return -EINVAL;
+ if (vdec->reset_codec)
+ return -EINVAL;
+
+ ret = vdec_response_frame(inst, vbuf);
+ if (ret)
+ return ret;
+ v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+ return 0;
+}
+
+static void vdec_on_queue_empty(struct vpu_inst *inst, u32 type)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return;
+
+ vdec_handle_resolution_change(inst);
+ if (vdec->eos_received)
+ vdec_set_last_buffer_dequeued(inst);
+}
+
+static void vdec_abort(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+ struct vpu_rpc_buffer_desc desc;
+ int ret;
+
+ vpu_trace(inst->dev, "[%d] state = %d\n", inst->id, inst->state);
+ vpu_iface_add_scode(inst, SCODE_PADDING_ABORT);
+ vdec->params.end_flag = 1;
+ vpu_iface_set_decode_params(inst, &vdec->params, 1);
+
+ vpu_session_abort(inst);
+
+ ret = vpu_iface_get_stream_buffer_desc(inst, &desc);
+ if (!ret)
+ vpu_iface_update_stream_buffer(inst, desc.rptr, 1);
+
+ vpu_session_rst_buf(inst);
+ vpu_trace(inst->dev, "[%d] input : %d, decoded : %d, display : %d, sequence : %d\n",
+ inst->id,
+ vdec->params.frame_count,
+ vdec->decoded_frame_count,
+ vdec->display_frame_count,
+ vdec->sequence);
+ vdec->params.end_flag = 0;
+ vdec->drain = 0;
+ vdec->ts_pre_count = 0;
+ vdec->params.frame_count = 0;
+ vdec->decoded_frame_count = 0;
+ vdec->display_frame_count = 0;
+ vdec->sequence = 0;
+}
+
+static void vdec_stop(struct vpu_inst *inst, bool free)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ vdec_clear_slots(inst);
+ if (inst->state != VPU_CODEC_STATE_DEINIT)
+ vpu_session_stop(inst);
+ vdec_clear_fs(&vdec->mbi);
+ vdec_clear_fs(&vdec->dcp);
+ if (free) {
+ vpu_free_dma(&vdec->udata);
+ vpu_free_dma(&inst->stream_buffer);
+ }
+ vdec_update_state(inst, VPU_CODEC_STATE_DEINIT, 1);
+ vdec->reset_codec = false;
+}
+
+static void vdec_release(struct vpu_inst *inst)
+{
+ if (inst->id != VPU_INST_NULL_ID)
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+ vpu_inst_lock(inst);
+ vdec_stop(inst, true);
+ vpu_inst_unlock(inst);
+}
+
+static void vdec_cleanup(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec;
+
+ if (!inst)
+ return;
+
+ vdec = inst->priv;
+ if (vdec)
+ vfree(vdec);
+ inst->priv = NULL;
+ vfree(inst);
+}
+
+static void vdec_init_params(struct vdec_t *vdec)
+{
+ vdec->params.frame_count = 0;
+ vdec->params.end_flag = 0;
+}
+
+static int vdec_start(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+ int stream_buffer_size;
+ int ret;
+
+ if (inst->state != VPU_CODEC_STATE_DEINIT)
+ return 0;
+
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+ if (!vdec->udata.virt) {
+ vdec->udata.length = 0x1000;
+ ret = vpu_alloc_dma(inst->core, &vdec->udata);
+ if (ret) {
+ dev_err(inst->dev, "[%d] alloc udata fail\n", inst->id);
+ goto error;
+ }
+ }
+
+ if (!inst->stream_buffer.virt) {
+ stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core);
+ if (stream_buffer_size > 0) {
+ inst->stream_buffer.length = stream_buffer_size;
+ ret = vpu_alloc_dma(inst->core, &inst->stream_buffer);
+ if (ret) {
+ dev_err(inst->dev, "[%d] alloc stream buffer fail\n", inst->id);
+ goto error;
+ }
+ inst->use_stream_buffer = true;
+ }
+ }
+
+ if (inst->use_stream_buffer)
+ vpu_iface_config_stream_buffer(inst, &inst->stream_buffer);
+ vpu_iface_init_instance(inst);
+ vdec->params.udata.base = vdec->udata.phys;
+ vdec->params.udata.size = vdec->udata.length;
+ ret = vpu_iface_set_decode_params(inst, &vdec->params, 0);
+ if (ret) {
+ dev_err(inst->dev, "[%d] set decode params fail\n", inst->id);
+ goto error;
+ }
+
+ vdec_init_params(vdec);
+ ret = vpu_session_start(inst);
+ if (ret) {
+ dev_err(inst->dev, "[%d] start fail\n", inst->id);
+ goto error;
+ }
+
+ vdec_update_state(inst, VPU_CODEC_STATE_STARTED, 0);
+
+ return 0;
+error:
+ vpu_free_dma(&vdec->udata);
+ vpu_free_dma(&inst->stream_buffer);
+ return ret;
+}
+
+static int vdec_start_session(struct vpu_inst *inst, u32 type)
+{
+ struct vdec_t *vdec = inst->priv;
+ int ret = 0;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (vdec->reset_codec)
+ vdec_stop(inst, false);
+ if (inst->state == VPU_CODEC_STATE_DEINIT) {
+ ret = vdec_start(inst);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (inst->state == VPU_CODEC_STATE_SEEK)
+ vdec_update_state(inst, vdec->state, 1);
+ vdec->eos_received = 0;
+ vpu_process_output_buffer(inst);
+ } else {
+ vdec_cmd_start(inst);
+ }
+ if (inst->state == VPU_CODEC_STATE_ACTIVE)
+ vdec_response_fs_request(inst, false);
+
+ return ret;
+}
+
+static int vdec_stop_session(struct vpu_inst *inst, u32 type)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ return 0;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ vdec_update_state(inst, VPU_CODEC_STATE_SEEK, 0);
+ vdec->drain = 0;
+ } else {
+ if (inst->state != VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE)
+ vdec_abort(inst);
+
+ vdec->eos_received = 0;
+ vdec_clear_slots(inst);
+ }
+
+ return 0;
+}
+
+static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
+{
+ struct vdec_t *vdec = inst->priv;
+ int num = -1;
+
+ switch (i) {
+ case 0:
+ num = scnprintf(str, size,
+ "req_frame_count = %d\ninterlaced = %d\n",
+ vdec->req_frame_count,
+ vdec->codec_info.progressive ? 0 : 1);
+ break;
+ case 1:
+ num = scnprintf(str, size,
+ "mbi: size = 0x%x request = %d, alloc = %d, response = %d\n",
+ vdec->mbi.size,
+ vdec->mbi.req_count,
+ vdec->mbi.count,
+ vdec->mbi.index);
+ break;
+ case 2:
+ num = scnprintf(str, size,
+ "dcp: size = 0x%x request = %d, alloc = %d, response = %d\n",
+ vdec->dcp.size,
+ vdec->dcp.req_count,
+ vdec->dcp.count,
+ vdec->dcp.index);
+ break;
+ case 3:
+ num = scnprintf(str, size, "input_frame_count = %d\n", vdec->params.frame_count);
+ break;
+ case 4:
+ num = scnprintf(str, size, "decoded_frame_count = %d\n", vdec->decoded_frame_count);
+ break;
+ case 5:
+ num = scnprintf(str, size, "display_frame_count = %d\n", vdec->display_frame_count);
+ break;
+ case 6:
+ num = scnprintf(str, size, "sequence = %d\n", vdec->sequence);
+ break;
+ case 7:
+ num = scnprintf(str, size, "drain = %d, eos = %d, source_change = %d\n",
+ vdec->drain, vdec->eos_received, vdec->source_change);
+ break;
+ case 8:
+ num = scnprintf(str, size, "ts_pre_count = %d, frame_depth = %d\n",
+ vdec->ts_pre_count, vdec->frame_depth);
+ break;
+ case 9:
+ num = scnprintf(str, size, "fps = %d/%d\n",
+ vdec->codec_info.frame_rate.numerator,
+ vdec->codec_info.frame_rate.denominator);
+ break;
+ default:
+ break;
+ }
+
+ return num;
+}
+
+static struct vpu_inst_ops vdec_inst_ops = {
+ .ctrl_init = vdec_ctrl_init,
+ .check_ready = vdec_check_ready,
+ .buf_done = vdec_buf_done,
+ .get_one_frame = vdec_frame_decoded,
+ .stop_done = vdec_stop_done,
+ .event_notify = vdec_event_notify,
+ .release = vdec_release,
+ .cleanup = vdec_cleanup,
+ .start = vdec_start_session,
+ .stop = vdec_stop_session,
+ .process_output = vdec_process_output,
+ .process_capture = vdec_process_capture,
+ .on_queue_empty = vdec_on_queue_empty,
+ .get_debug_info = vdec_get_debug_info,
+ .wait_prepare = vpu_inst_unlock,
+ .wait_finish = vpu_inst_lock,
+};
+
+static void vdec_init(struct file *file)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct vdec_t *vdec;
+ struct v4l2_format f;
+
+ vdec = inst->priv;
+ vdec->frame_depth = VDEC_FRAME_DEPTH;
+
+ memset(&f, 0, sizeof(f));
+ f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ f.fmt.pix_mp.width = 1280;
+ f.fmt.pix_mp.height = 720;
+ f.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ vdec_s_fmt(file, &inst->fh, &f);
+
+ memset(&f, 0, sizeof(f));
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M_8L128;
+ f.fmt.pix_mp.width = 1280;
+ f.fmt.pix_mp.height = 720;
+ f.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ vdec_s_fmt(file, &inst->fh, &f);
+}
+
+static int vdec_open(struct file *file)
+{
+ struct vpu_inst *inst;
+ struct vdec_t *vdec;
+ int ret;
+
+ inst = vzalloc(sizeof(*inst));
+ if (!inst)
+ return -ENOMEM;
+
+ vdec = vzalloc(sizeof(*vdec));
+ if (!vdec) {
+ vfree(inst);
+ return -ENOMEM;
+ }
+
+ inst->ops = &vdec_inst_ops;
+ inst->formats = vdec_formats;
+ inst->type = VPU_CORE_TYPE_DEC;
+ inst->priv = vdec;
+
+ ret = vpu_v4l2_open(file, inst);
+ if (ret)
+ return ret;
+
+ vdec->fixed_fmt = false;
+ inst->min_buffer_cap = VDEC_MIN_BUFFER_CAP;
+ vdec_init(file);
+
+ return 0;
+}
+
+static __poll_t vdec_poll(struct file *file, poll_table *wait)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct vb2_queue *src_q, *dst_q;
+ __poll_t ret;
+
+ ret = v4l2_m2m_fop_poll(file, wait);
+ src_q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
+ dst_q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
+ if (vb2_is_streaming(src_q) && !vb2_is_streaming(dst_q))
+ ret &= (~EPOLLERR);
+ if (!src_q->error && !dst_q->error &&
+ (vb2_is_streaming(src_q) && list_empty(&src_q->queued_list)) &&
+ (vb2_is_streaming(dst_q) && list_empty(&dst_q->queued_list)))
+ ret &= (~EPOLLERR);
+
+ return ret;
+}
+
+static const struct v4l2_file_operations vdec_fops = {
+ .owner = THIS_MODULE,
+ .open = vdec_open,
+ .release = vpu_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vdec_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+const struct v4l2_ioctl_ops *vdec_get_ioctl_ops(void)
+{
+ return &vdec_ioctl_ops;
+}
+
+const struct v4l2_file_operations *vdec_get_fops(void)
+{
+ return &vdec_fops;
+}
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
new file mode 100644
index 000000000000..d33c2748e4b7
--- /dev/null
+++ b/drivers/media/platform/amphion/venc.c
@@ -0,0 +1,1358 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/ktime.h>
+#include <linux/rational.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include "vpu.h"
+#include "vpu_defs.h"
+#include "vpu_core.h"
+#include "vpu_helpers.h"
+#include "vpu_v4l2.h"
+#include "vpu_cmds.h"
+#include "vpu_rpc.h"
+
+#define VENC_OUTPUT_ENABLE BIT(0)
+#define VENC_CAPTURE_ENABLE BIT(1)
+#define VENC_ENABLE_MASK (VENC_OUTPUT_ENABLE | VENC_CAPTURE_ENABLE)
+#define VENC_MAX_BUF_CNT 8
+
+struct venc_t {
+ struct vpu_encode_params params;
+ u32 request_key_frame;
+ u32 input_ready;
+ u32 cpb_size;
+ bool bitrate_change;
+
+ struct vpu_buffer enc[VENC_MAX_BUF_CNT];
+ struct vpu_buffer ref[VENC_MAX_BUF_CNT];
+ struct vpu_buffer act[VENC_MAX_BUF_CNT];
+ struct list_head frames;
+ u32 frame_count;
+ u32 encode_count;
+ u32 ready_count;
+ u32 enable;
+ u32 stopped;
+
+ u32 skipped_count;
+ u32 skipped_bytes;
+
+ wait_queue_head_t wq;
+};
+
+struct venc_frame_t {
+ struct list_head list;
+ struct vpu_enc_pic_info info;
+ u32 bytesused;
+ s64 timestamp;
+};
+
+static const struct vpu_format venc_formats[] = {
+ {
+ .pixfmt = V4L2_PIX_FMT_NV12M,
+ .num_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ },
+ {0, 0, 0, 0},
+};
+
+static int venc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, "amphion-vpu", sizeof(cap->driver));
+ strscpy(cap->card, "amphion vpu encoder", sizeof(cap->card));
+ strscpy(cap->bus_info, "platform: amphion-vpu", sizeof(cap->bus_info));
+
+ return 0;
+}
+
+static int venc_enum_fmt(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_format *fmt;
+
+ memset(f->reserved, 0, sizeof(f->reserved));
+ fmt = vpu_helper_enum_format(inst, f->type, f->index);
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = fmt->pixfmt;
+ f->flags = fmt->flags;
+
+ return 0;
+}
+
+static int venc_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_core_resources *res;
+
+ if (!fsize || fsize->index)
+ return -EINVAL;
+
+ if (!vpu_helper_find_format(inst, 0, fsize->pixel_format))
+ return -EINVAL;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return -EINVAL;
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.max_width = res->max_width;
+ fsize->stepwise.max_height = res->max_height;
+ fsize->stepwise.min_width = res->min_width;
+ fsize->stepwise.min_height = res->min_height;
+ fsize->stepwise.step_width = res->step_width;
+ fsize->stepwise.step_height = res->step_height;
+
+ return 0;
+}
+
+static int venc_enum_frameintervals(struct file *file, void *fh, struct v4l2_frmivalenum *fival)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_core_resources *res;
+
+ if (!fival || fival->index)
+ return -EINVAL;
+
+ if (!vpu_helper_find_format(inst, 0, fival->pixel_format))
+ return -EINVAL;
+
+ if (!fival->width || !fival->height)
+ return -EINVAL;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return -EINVAL;
+ if (fival->width < res->min_width || fival->width > res->max_width ||
+ fival->height < res->min_height || fival->height > res->max_height)
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
+ fival->stepwise.min.numerator = 1;
+ fival->stepwise.min.denominator = USHRT_MAX;
+ fival->stepwise.max.numerator = USHRT_MAX;
+ fival->stepwise.max.denominator = 1;
+ fival->stepwise.step.numerator = 1;
+ fival->stepwise.step.denominator = 1;
+
+ return 0;
+}
+
+static int venc_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct vpu_format *cur_fmt;
+ int i;
+
+ cur_fmt = vpu_get_format(inst, f->type);
+
+ pixmp->pixelformat = cur_fmt->pixfmt;
+ pixmp->num_planes = cur_fmt->num_planes;
+ pixmp->width = cur_fmt->width;
+ pixmp->height = cur_fmt->height;
+ pixmp->field = cur_fmt->field;
+ pixmp->flags = cur_fmt->flags;
+ for (i = 0; i < pixmp->num_planes; i++) {
+ pixmp->plane_fmt[i].bytesperline = cur_fmt->bytesperline[i];
+ pixmp->plane_fmt[i].sizeimage = cur_fmt->sizeimage[i];
+ }
+
+ f->fmt.pix_mp.colorspace = venc->params.color.primaries;
+ f->fmt.pix_mp.xfer_func = venc->params.color.transfer;
+ f->fmt.pix_mp.ycbcr_enc = venc->params.color.matrix;
+ f->fmt.pix_mp.quantization = venc->params.color.full_range;
+
+ return 0;
+}
+
+static int venc_try_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+
+ vpu_try_fmt_common(inst, f);
+
+ return 0;
+}
+
+static int venc_s_fmt(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_format *fmt;
+ struct vpu_format *cur_fmt;
+ struct vb2_queue *q;
+ struct venc_t *venc = inst->priv;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ int i;
+
+ q = v4l2_m2m_get_vq(inst->fh.m2m_ctx, f->type);
+ if (!q)
+ return -EINVAL;
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ fmt = vpu_try_fmt_common(inst, f);
+ if (!fmt)
+ return -EINVAL;
+
+ cur_fmt = vpu_get_format(inst, f->type);
+
+ cur_fmt->pixfmt = fmt->pixfmt;
+ cur_fmt->num_planes = fmt->num_planes;
+ cur_fmt->flags = fmt->flags;
+ cur_fmt->width = pix_mp->width;
+ cur_fmt->height = pix_mp->height;
+ for (i = 0; i < fmt->num_planes; i++) {
+ cur_fmt->sizeimage[i] = pix_mp->plane_fmt[i].sizeimage;
+ cur_fmt->bytesperline[i] = pix_mp->plane_fmt[i].bytesperline;
+ }
+
+ if (pix_mp->field != V4L2_FIELD_ANY)
+ cur_fmt->field = pix_mp->field;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ venc->params.input_format = cur_fmt->pixfmt;
+ venc->params.src_stride = cur_fmt->bytesperline[0];
+ venc->params.src_width = cur_fmt->width;
+ venc->params.src_height = cur_fmt->height;
+ venc->params.crop.left = 0;
+ venc->params.crop.top = 0;
+ venc->params.crop.width = cur_fmt->width;
+ venc->params.crop.height = cur_fmt->height;
+ } else {
+ venc->params.codec_format = cur_fmt->pixfmt;
+ venc->params.out_width = cur_fmt->width;
+ venc->params.out_height = cur_fmt->height;
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ if (!vpu_color_check_primaries(pix_mp->colorspace)) {
+ venc->params.color.primaries = pix_mp->colorspace;
+ vpu_color_get_default(venc->params.color.primaries,
+ &venc->params.color.transfer,
+ &venc->params.color.matrix,
+ &venc->params.color.full_range);
+ }
+ if (!vpu_color_check_transfers(pix_mp->xfer_func))
+ venc->params.color.transfer = pix_mp->xfer_func;
+ if (!vpu_color_check_matrix(pix_mp->ycbcr_enc))
+ venc->params.color.matrix = pix_mp->ycbcr_enc;
+ if (!vpu_color_check_full_range(pix_mp->quantization))
+ venc->params.color.full_range = pix_mp->quantization;
+ }
+
+ pix_mp->colorspace = venc->params.color.primaries;
+ pix_mp->xfer_func = venc->params.color.transfer;
+ pix_mp->ycbcr_enc = venc->params.color.matrix;
+ pix_mp->quantization = venc->params.color.full_range;
+
+ return 0;
+}
+
+static int venc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+ struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe;
+
+ if (!parm)
+ return -EINVAL;
+
+ if (!vpu_helper_check_type(inst, parm->type))
+ return -EINVAL;
+
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ parm->parm.capture.readbuffers = 0;
+ timeperframe->numerator = venc->params.frame_rate.numerator;
+ timeperframe->denominator = venc->params.frame_rate.denominator;
+
+ return 0;
+}
+
+static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *parm)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+ struct v4l2_fract *timeperframe = &parm->parm.capture.timeperframe;
+ unsigned long n, d;
+
+ if (!parm)
+ return -EINVAL;
+
+ if (!vpu_helper_check_type(inst, parm->type))
+ return -EINVAL;
+
+ if (!timeperframe->numerator)
+ timeperframe->numerator = venc->params.frame_rate.numerator;
+ if (!timeperframe->denominator)
+ timeperframe->denominator = venc->params.frame_rate.denominator;
+
+ venc->params.frame_rate.numerator = timeperframe->numerator;
+ venc->params.frame_rate.denominator = timeperframe->denominator;
+
+ rational_best_approximation(venc->params.frame_rate.numerator,
+ venc->params.frame_rate.denominator,
+ venc->params.frame_rate.numerator,
+ venc->params.frame_rate.denominator,
+ &n, &d);
+ venc->params.frame_rate.numerator = n;
+ venc->params.frame_rate.denominator = d;
+
+ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
+ memset(parm->parm.capture.reserved, 0, sizeof(parm->parm.capture.reserved));
+
+ return 0;
+}
+
+static int venc_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc = inst->priv;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->out_format.width;
+ s->r.height = inst->out_format.height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ s->r = venc->params.crop;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int venc_valid_crop(struct venc_t *venc, const struct vpu_core_resources *res)
+{
+ struct v4l2_rect *rect = NULL;
+ u32 min_width;
+ u32 min_height;
+ u32 src_width;
+ u32 src_height;
+
+ rect = &venc->params.crop;
+ min_width = res->min_width;
+ min_height = res->min_height;
+ src_width = venc->params.src_width;
+ src_height = venc->params.src_height;
+
+ if (rect->width == 0 || rect->height == 0)
+ return -EINVAL;
+ if (rect->left > src_width - min_width || rect->top > src_height - min_height)
+ return -EINVAL;
+
+ rect->width = min(rect->width, src_width - rect->left);
+ rect->width = max_t(u32, rect->width, min_width);
+
+ rect->height = min(rect->height, src_height - rect->top);
+ rect->height = max_t(u32, rect->height, min_height);
+
+ return 0;
+}
+
+static int venc_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct vpu_inst *inst = to_inst(file);
+ const struct vpu_core_resources *res;
+ struct venc_t *venc = inst->priv;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return -EINVAL;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT && s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return -EINVAL;
+ if (s->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ venc->params.crop.left = ALIGN(s->r.left, res->step_width);
+ venc->params.crop.top = ALIGN(s->r.top, res->step_height);
+ venc->params.crop.width = ALIGN(s->r.width, res->step_width);
+ venc->params.crop.height = ALIGN(s->r.height, res->step_height);
+ if (venc_valid_crop(venc, res)) {
+ venc->params.crop.left = 0;
+ venc->params.crop.top = 0;
+ venc->params.crop.width = venc->params.src_width;
+ venc->params.crop.height = venc->params.src_height;
+ }
+
+ inst->crop = venc->params.crop;
+
+ return 0;
+}
+
+static int venc_drain(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+ int ret;
+
+ if (!inst->fh.m2m_ctx)
+ return 0;
+
+ if (inst->state != VPU_CODEC_STATE_DRAIN)
+ return 0;
+
+ if (v4l2_m2m_num_src_bufs_ready(inst->fh.m2m_ctx))
+ return 0;
+
+ if (!venc->input_ready)
+ return 0;
+
+ venc->input_ready = false;
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+ ret = vpu_session_stop(inst);
+ if (ret)
+ return ret;
+ inst->state = VPU_CODEC_STATE_STOP;
+ wake_up_all(&venc->wq);
+
+ return 0;
+}
+
+static int venc_request_eos(struct vpu_inst *inst)
+{
+ inst->state = VPU_CODEC_STATE_DRAIN;
+ venc_drain(inst);
+
+ return 0;
+}
+
+static int venc_encoder_cmd(struct file *file, void *fh, struct v4l2_encoder_cmd *cmd)
+{
+ struct vpu_inst *inst = to_inst(file);
+ int ret;
+
+ ret = v4l2_m2m_ioctl_try_encoder_cmd(file, fh, cmd);
+ if (ret)
+ return ret;
+
+ vpu_inst_lock(inst);
+ if (cmd->cmd == V4L2_ENC_CMD_STOP) {
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ vpu_set_last_buffer_dequeued(inst);
+ else
+ venc_request_eos(inst);
+ }
+ vpu_inst_unlock(inst);
+
+ return 0;
+}
+
+static int venc_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops venc_ioctl_ops = {
+ .vidioc_querycap = venc_querycap,
+ .vidioc_enum_fmt_vid_cap = venc_enum_fmt,
+ .vidioc_enum_fmt_vid_out = venc_enum_fmt,
+ .vidioc_enum_framesizes = venc_enum_framesizes,
+ .vidioc_enum_frameintervals = venc_enum_frameintervals,
+ .vidioc_g_fmt_vid_cap_mplane = venc_g_fmt,
+ .vidioc_g_fmt_vid_out_mplane = venc_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = venc_try_fmt,
+ .vidioc_try_fmt_vid_out_mplane = venc_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = venc_s_fmt,
+ .vidioc_s_fmt_vid_out_mplane = venc_s_fmt,
+ .vidioc_g_parm = venc_g_parm,
+ .vidioc_s_parm = venc_s_parm,
+ .vidioc_g_selection = venc_g_selection,
+ .vidioc_s_selection = venc_s_selection,
+ .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+ .vidioc_encoder_cmd = venc_encoder_cmd,
+ .vidioc_subscribe_event = venc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .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_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+};
+
+static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpu_inst *inst = ctrl_to_inst(ctrl);
+ struct venc_t *venc = inst->priv;
+ int ret = 0;
+
+ vpu_inst_lock(inst);
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ venc->params.profile = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ venc->params.level = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE:
+ venc->params.rc_enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ venc->params.rc_mode = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
+ if (ctrl->val != venc->params.bitrate)
+ venc->bitrate_change = true;
+ venc->params.bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+ venc->params.bitrate_max = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+ venc->params.gop_length = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ venc->params.bframes = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP:
+ venc->params.i_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP:
+ venc->params.p_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP:
+ venc->params.b_frame_qp = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME:
+ venc->request_key_frame = 1;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE:
+ venc->cpb_size = ctrl->val * 1024;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE:
+ venc->params.sar.enable = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
+ venc->params.sar.idc = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH:
+ venc->params.sar.width = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT:
+ venc->params.sar.height = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ vpu_inst_unlock(inst);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops venc_ctrl_ops = {
+ .s_ctrl = venc_op_s_ctrl,
+ .g_volatile_ctrl = vpu_helper_g_volatile_ctrl,
+};
+
+static int venc_ctrl_init(struct vpu_inst *inst)
+{
+ struct v4l2_ctrl *ctrl;
+ int ret;
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 20);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)),
+ V4L2_MPEG_VIDEO_H264_PROFILE_HIGH);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ 0x0,
+ V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FRAME_RC_ENABLE, 0, 1, 1, 1);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+ ~((1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
+ (1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)),
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ BITRATE_MIN,
+ BITRATE_MAX,
+ BITRATE_STEP,
+ BITRATE_DEFAULT);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP,
+ BITRATE_DEFAULT_PEAK);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_GOP_SIZE, 0, (1 << 16) - 1, 1, 30);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES, 0, 4, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP, 1, 51, 1, 28);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP, 1, 51, 1, 30);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME, 0, 0, 0, 0);
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 2);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_CPB_SIZE, 64, 10240, 1, 1024);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE, 0, 1, 1, 1);
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED,
+ 0x0,
+ V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_1x1);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH,
+ 0, USHRT_MAX, 1, 1);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT,
+ 0, USHRT_MAX, 1, 1);
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+ ~(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME),
+ V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
+
+ ret = v4l2_ctrl_handler_setup(&inst->ctrl_handler);
+ if (ret) {
+ dev_err(inst->dev, "[%d] setup ctrls fail, ret = %d\n", inst->id, ret);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool venc_check_ready(struct vpu_inst *inst, unsigned int type)
+{
+ struct venc_t *venc = inst->priv;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ if (vpu_helper_get_free_space(inst) < venc->cpb_size)
+ return false;
+ return venc->input_ready;
+ }
+
+ if (list_empty(&venc->frames))
+ return false;
+ return true;
+}
+
+static u32 venc_get_enable_mask(u32 type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return VENC_OUTPUT_ENABLE;
+ else
+ return VENC_CAPTURE_ENABLE;
+}
+
+static void venc_set_enable(struct venc_t *venc, u32 type, int enable)
+{
+ u32 mask = venc_get_enable_mask(type);
+
+ if (enable)
+ venc->enable |= mask;
+ else
+ venc->enable &= ~mask;
+}
+
+static u32 venc_get_enable(struct venc_t *venc, u32 type)
+{
+ return venc->enable & venc_get_enable_mask(type);
+}
+
+static void venc_input_done(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+
+ vpu_inst_lock(inst);
+ venc->input_ready = true;
+ vpu_process_output_buffer(inst);
+ if (inst->state == VPU_CODEC_STATE_DRAIN)
+ venc_drain(inst);
+ vpu_inst_unlock(inst);
+}
+
+/*
+ * It's hardware limitation, that there may be several bytes
+ * redundant data at the beginning of frame.
+ * For android platform, the redundant data may cause cts test fail
+ * So driver will strip them
+ */
+static int venc_precheck_encoded_frame(struct vpu_inst *inst, struct venc_frame_t *frame)
+{
+ struct venc_t *venc;
+ int skipped;
+
+ if (!frame || !frame->bytesused)
+ return -EINVAL;
+
+ venc = inst->priv;
+ skipped = vpu_helper_find_startcode(&inst->stream_buffer,
+ inst->cap_format.pixfmt,
+ frame->info.wptr - inst->stream_buffer.phys,
+ frame->bytesused);
+ if (skipped > 0) {
+ frame->bytesused -= skipped;
+ frame->info.wptr = vpu_helper_step_walk(&inst->stream_buffer,
+ frame->info.wptr, skipped);
+ venc->skipped_bytes += skipped;
+ venc->skipped_count++;
+ }
+
+ return 0;
+}
+
+static int venc_get_one_encoded_frame(struct vpu_inst *inst,
+ struct venc_frame_t *frame,
+ struct vb2_v4l2_buffer *vbuf)
+{
+ struct venc_t *venc = inst->priv;
+
+ if (!vbuf)
+ return -EAGAIN;
+
+ if (!venc_get_enable(inst->priv, vbuf->vb2_buf.type)) {
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ return 0;
+ }
+ if (frame->bytesused > vbuf->vb2_buf.planes[0].length) {
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ return -ENOMEM;
+ }
+
+ venc_precheck_encoded_frame(inst, frame);
+
+ if (frame->bytesused) {
+ u32 rptr = frame->info.wptr;
+ void *dst = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+
+ vpu_helper_copy_from_stream_buffer(&inst->stream_buffer,
+ &rptr, frame->bytesused, dst);
+ vpu_iface_update_stream_buffer(inst, rptr, 0);
+ }
+ vb2_set_plane_payload(&vbuf->vb2_buf, 0, frame->bytesused);
+ vbuf->sequence = frame->info.frame_id;
+ vbuf->vb2_buf.timestamp = frame->info.timestamp;
+ vbuf->field = inst->cap_format.field;
+ vbuf->flags |= frame->info.pic_type;
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, frame->info.timestamp);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ venc->ready_count++;
+
+ if (vbuf->flags & V4L2_BUF_FLAG_KEYFRAME)
+ dev_dbg(inst->dev, "[%d][%d]key frame\n", inst->id, frame->info.frame_id);
+
+ return 0;
+}
+
+static int venc_get_encoded_frames(struct vpu_inst *inst)
+{
+ struct venc_t *venc;
+ struct venc_frame_t *frame;
+ struct venc_frame_t *tmp;
+
+ if (!inst->fh.m2m_ctx)
+ return 0;
+ venc = inst->priv;
+ list_for_each_entry_safe(frame, tmp, &venc->frames, list) {
+ if (venc_get_one_encoded_frame(inst, frame,
+ v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx)))
+ break;
+ list_del_init(&frame->list);
+ vfree(frame);
+ }
+
+ return 0;
+}
+
+static int venc_frame_encoded(struct vpu_inst *inst, void *arg)
+{
+ struct vpu_enc_pic_info *info = arg;
+ struct venc_frame_t *frame;
+ struct venc_t *venc;
+ int ret = 0;
+
+ if (!info)
+ return -EINVAL;
+ venc = inst->priv;
+ frame = vzalloc(sizeof(*frame));
+ if (!frame)
+ return -ENOMEM;
+
+ memcpy(&frame->info, info, sizeof(frame->info));
+ frame->bytesused = info->frame_size;
+
+ vpu_inst_lock(inst);
+ list_add_tail(&frame->list, &venc->frames);
+ venc->encode_count++;
+ venc_get_encoded_frames(inst);
+ vpu_inst_unlock(inst);
+
+ return ret;
+}
+
+static void venc_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
+{
+ struct vb2_v4l2_buffer *vbuf;
+
+ if (!inst->fh.m2m_ctx)
+ return;
+
+ vpu_inst_lock(inst);
+ if (!venc_get_enable(inst->priv, frame->type))
+ goto exit;
+ vbuf = vpu_find_buf_by_sequence(inst, frame->type, frame->sequence);
+ if (!vbuf) {
+ dev_err(inst->dev, "[%d] can't find buf: type %d, sequence %d\n",
+ inst->id, frame->type, frame->sequence);
+ goto exit;
+ }
+
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ if (V4L2_TYPE_IS_OUTPUT(frame->type))
+ v4l2_m2m_src_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+ else
+ v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+exit:
+ vpu_inst_unlock(inst);
+}
+
+static void venc_set_last_buffer_dequeued(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+
+ if (venc->stopped && list_empty(&venc->frames))
+ vpu_set_last_buffer_dequeued(inst);
+}
+
+static void venc_stop_done(struct vpu_inst *inst)
+{
+ struct venc_t *venc = inst->priv;
+
+ vpu_inst_lock(inst);
+ venc->stopped = true;
+ venc_set_last_buffer_dequeued(inst);
+ vpu_inst_unlock(inst);
+
+ wake_up_all(&venc->wq);
+}
+
+static void venc_event_notify(struct vpu_inst *inst, u32 event, void *data)
+{
+}
+
+static void venc_release(struct vpu_inst *inst)
+{
+}
+
+static void venc_cleanup(struct vpu_inst *inst)
+{
+ struct venc_t *venc;
+
+ if (!inst)
+ return;
+
+ venc = inst->priv;
+ if (venc)
+ vfree(venc);
+ inst->priv = NULL;
+ vfree(inst);
+}
+
+static int venc_start_session(struct vpu_inst *inst, u32 type)
+{
+ struct venc_t *venc = inst->priv;
+ int stream_buffer_size;
+ int ret;
+
+ venc_set_enable(venc, type, 1);
+ if ((venc->enable & VENC_ENABLE_MASK) != VENC_ENABLE_MASK)
+ return 0;
+
+ vpu_iface_init_instance(inst);
+ stream_buffer_size = vpu_iface_get_stream_buffer_size(inst->core);
+ if (stream_buffer_size > 0) {
+ inst->stream_buffer.length = max_t(u32, stream_buffer_size, venc->cpb_size * 3);
+ ret = vpu_alloc_dma(inst->core, &inst->stream_buffer);
+ if (ret)
+ goto error;
+
+ inst->use_stream_buffer = true;
+ vpu_iface_config_stream_buffer(inst, &inst->stream_buffer);
+ }
+
+ ret = vpu_iface_set_encode_params(inst, &venc->params, 0);
+ if (ret)
+ goto error;
+ ret = vpu_session_configure_codec(inst);
+ if (ret)
+ goto error;
+
+ inst->state = VPU_CODEC_STATE_CONFIGURED;
+ /*vpu_iface_config_memory_resource*/
+
+ /*config enc expert mode parameter*/
+ ret = vpu_iface_set_encode_params(inst, &venc->params, 1);
+ if (ret)
+ goto error;
+
+ ret = vpu_session_start(inst);
+ if (ret)
+ goto error;
+ inst->state = VPU_CODEC_STATE_STARTED;
+
+ venc->bitrate_change = false;
+ venc->input_ready = true;
+ venc->frame_count = 0;
+ venc->encode_count = 0;
+ venc->ready_count = 0;
+ venc->stopped = false;
+ vpu_process_output_buffer(inst);
+ if (venc->frame_count == 0)
+ dev_err(inst->dev, "[%d] there is no input when starting\n", inst->id);
+
+ return 0;
+error:
+ venc_set_enable(venc, type, 0);
+ inst->state = VPU_CODEC_STATE_DEINIT;
+
+ vpu_free_dma(&inst->stream_buffer);
+ return ret;
+}
+
+static void venc_cleanup_mem_resource(struct vpu_inst *inst)
+{
+ struct venc_t *venc;
+ u32 i;
+
+ venc = inst->priv;
+
+ for (i = 0; i < ARRAY_SIZE(venc->enc); i++)
+ vpu_free_dma(&venc->enc[i]);
+ for (i = 0; i < ARRAY_SIZE(venc->ref); i++)
+ vpu_free_dma(&venc->ref[i]);
+}
+
+static void venc_request_mem_resource(struct vpu_inst *inst,
+ u32 enc_frame_size,
+ u32 enc_frame_num,
+ u32 ref_frame_size,
+ u32 ref_frame_num,
+ u32 act_frame_size,
+ u32 act_frame_num)
+{
+ struct venc_t *venc;
+ u32 i;
+ int ret;
+
+ venc = inst->priv;
+ if (enc_frame_num > ARRAY_SIZE(venc->enc)) {
+ dev_err(inst->dev, "[%d] enc num(%d) is out of range\n", inst->id, enc_frame_num);
+ return;
+ }
+ if (ref_frame_num > ARRAY_SIZE(venc->ref)) {
+ dev_err(inst->dev, "[%d] ref num(%d) is out of range\n", inst->id, ref_frame_num);
+ return;
+ }
+ if (act_frame_num > ARRAY_SIZE(venc->act)) {
+ dev_err(inst->dev, "[%d] act num(%d) is out of range\n", inst->id, act_frame_num);
+ return;
+ }
+
+ for (i = 0; i < enc_frame_num; i++) {
+ venc->enc[i].length = enc_frame_size;
+ ret = vpu_alloc_dma(inst->core, &venc->enc[i]);
+ if (ret) {
+ venc_cleanup_mem_resource(inst);
+ return;
+ }
+ }
+ for (i = 0; i < ref_frame_num; i++) {
+ venc->ref[i].length = ref_frame_size;
+ ret = vpu_alloc_dma(inst->core, &venc->ref[i]);
+ if (ret) {
+ venc_cleanup_mem_resource(inst);
+ return;
+ }
+ }
+ if (act_frame_num != 1 || act_frame_size > inst->act.length) {
+ venc_cleanup_mem_resource(inst);
+ return;
+ }
+ venc->act[0].length = act_frame_size;
+ venc->act[0].phys = inst->act.phys;
+ venc->act[0].virt = inst->act.virt;
+
+ for (i = 0; i < enc_frame_num; i++)
+ vpu_iface_config_memory_resource(inst, MEM_RES_ENC, i, &venc->enc[i]);
+ for (i = 0; i < ref_frame_num; i++)
+ vpu_iface_config_memory_resource(inst, MEM_RES_REF, i, &venc->ref[i]);
+ for (i = 0; i < act_frame_num; i++)
+ vpu_iface_config_memory_resource(inst, MEM_RES_ACT, i, &venc->act[i]);
+}
+
+static void venc_cleanup_frames(struct venc_t *venc)
+{
+ struct venc_frame_t *frame;
+ struct venc_frame_t *tmp;
+
+ list_for_each_entry_safe(frame, tmp, &venc->frames, list) {
+ list_del_init(&frame->list);
+ vfree(frame);
+ }
+}
+
+static int venc_stop_session(struct vpu_inst *inst, u32 type)
+{
+ struct venc_t *venc = inst->priv;
+
+ venc_set_enable(venc, type, 0);
+ if (venc->enable & VENC_ENABLE_MASK)
+ return 0;
+
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ return 0;
+
+ if (inst->state != VPU_CODEC_STATE_STOP)
+ venc_request_eos(inst);
+
+ call_void_vop(inst, wait_prepare);
+ if (!wait_event_timeout(venc->wq, venc->stopped, VPU_TIMEOUT)) {
+ set_bit(inst->id, &inst->core->hang_mask);
+ vpu_session_debug(inst);
+ }
+ call_void_vop(inst, wait_finish);
+
+ inst->state = VPU_CODEC_STATE_DEINIT;
+ venc_cleanup_frames(inst->priv);
+ vpu_free_dma(&inst->stream_buffer);
+ venc_cleanup_mem_resource(inst);
+
+ return 0;
+}
+
+static int venc_process_output(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct venc_t *venc = inst->priv;
+ struct vb2_v4l2_buffer *vbuf;
+ u32 flags;
+
+ if (inst->state == VPU_CODEC_STATE_DEINIT)
+ return -EINVAL;
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ if (inst->state == VPU_CODEC_STATE_STARTED)
+ inst->state = VPU_CODEC_STATE_ACTIVE;
+
+ flags = vbuf->flags;
+ if (venc->request_key_frame) {
+ vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+ venc->request_key_frame = 0;
+ }
+ if (venc->bitrate_change) {
+ vpu_session_update_parameters(inst, &venc->params);
+ venc->bitrate_change = false;
+ }
+ dev_dbg(inst->dev, "[%d][INPUT TS]%32lld\n", inst->id, vb->timestamp);
+ vpu_iface_input_frame(inst, vb);
+ vbuf->flags = flags;
+ venc->input_ready = false;
+ venc->frame_count++;
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE);
+
+ return 0;
+}
+
+static int venc_process_capture(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct venc_t *venc;
+ struct venc_frame_t *frame = NULL;
+ struct vb2_v4l2_buffer *vbuf;
+ int ret;
+
+ venc = inst->priv;
+ if (list_empty(&venc->frames))
+ return -EINVAL;
+
+ frame = list_first_entry(&venc->frames, struct venc_frame_t, list);
+ vbuf = to_vb2_v4l2_buffer(vb);
+ v4l2_m2m_dst_buf_remove_by_buf(inst->fh.m2m_ctx, vbuf);
+ ret = venc_get_one_encoded_frame(inst, frame, vbuf);
+ if (ret)
+ return ret;
+
+ list_del_init(&frame->list);
+ vfree(frame);
+ return 0;
+}
+
+static void venc_on_queue_empty(struct vpu_inst *inst, u32 type)
+{
+ struct venc_t *venc = inst->priv;
+
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return;
+
+ if (venc->stopped)
+ venc_set_last_buffer_dequeued(inst);
+}
+
+static int venc_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
+{
+ struct venc_t *venc = inst->priv;
+ int num = -1;
+
+ switch (i) {
+ case 0:
+ num = scnprintf(str, size, "profile = %d\n", venc->params.profile);
+ break;
+ case 1:
+ num = scnprintf(str, size, "level = %d\n", venc->params.level);
+ break;
+ case 2:
+ num = scnprintf(str, size, "fps = %d/%d\n",
+ venc->params.frame_rate.numerator,
+ venc->params.frame_rate.denominator);
+ break;
+ case 3:
+ num = scnprintf(str, size, "%d x %d -> %d x %d\n",
+ venc->params.src_width,
+ venc->params.src_height,
+ venc->params.out_width,
+ venc->params.out_height);
+ break;
+ case 4:
+ num = scnprintf(str, size, "(%d, %d) %d x %d\n",
+ venc->params.crop.left,
+ venc->params.crop.top,
+ venc->params.crop.width,
+ venc->params.crop.height);
+ break;
+ case 5:
+ num = scnprintf(str, size,
+ "enable = 0x%x, input = %d, encode = %d, ready = %d, stopped = %d\n",
+ venc->enable,
+ venc->frame_count, venc->encode_count,
+ venc->ready_count,
+ venc->stopped);
+ break;
+ case 6:
+ num = scnprintf(str, size, "gop = %d\n", venc->params.gop_length);
+ break;
+ case 7:
+ num = scnprintf(str, size, "bframes = %d\n", venc->params.bframes);
+ break;
+ case 8:
+ num = scnprintf(str, size, "rc: %s, mode = %d, bitrate = %d(%d), qp = %d\n",
+ venc->params.rc_enable ? "enable" : "disable",
+ venc->params.rc_mode,
+ venc->params.bitrate,
+ venc->params.bitrate_max,
+ venc->params.i_frame_qp);
+ break;
+ case 9:
+ num = scnprintf(str, size, "sar: enable = %d, idc = %d, %d x %d\n",
+ venc->params.sar.enable,
+ venc->params.sar.idc,
+ venc->params.sar.width,
+ venc->params.sar.height);
+
+ break;
+ case 10:
+ num = scnprintf(str, size,
+ "colorspace: primaries = %d, transfer = %d, matrix = %d, full_range = %d\n",
+ venc->params.color.primaries,
+ venc->params.color.transfer,
+ venc->params.color.matrix,
+ venc->params.color.full_range);
+ break;
+ case 11:
+ num = scnprintf(str, size, "skipped: count = %d, bytes = %d\n",
+ venc->skipped_count, venc->skipped_bytes);
+ break;
+ default:
+ break;
+ }
+
+ return num;
+}
+
+static struct vpu_inst_ops venc_inst_ops = {
+ .ctrl_init = venc_ctrl_init,
+ .check_ready = venc_check_ready,
+ .input_done = venc_input_done,
+ .get_one_frame = venc_frame_encoded,
+ .buf_done = venc_buf_done,
+ .stop_done = venc_stop_done,
+ .event_notify = venc_event_notify,
+ .release = venc_release,
+ .cleanup = venc_cleanup,
+ .start = venc_start_session,
+ .mem_request = venc_request_mem_resource,
+ .stop = venc_stop_session,
+ .process_output = venc_process_output,
+ .process_capture = venc_process_capture,
+ .on_queue_empty = venc_on_queue_empty,
+ .get_debug_info = venc_get_debug_info,
+ .wait_prepare = vpu_inst_unlock,
+ .wait_finish = vpu_inst_lock,
+};
+
+static void venc_init(struct file *file)
+{
+ struct vpu_inst *inst = to_inst(file);
+ struct venc_t *venc;
+ struct v4l2_format f;
+ struct v4l2_streamparm parm;
+
+ venc = inst->priv;
+ venc->params.qp_min = 1;
+ venc->params.qp_max = 51;
+ venc->params.qp_min_i = 1;
+ venc->params.qp_max_i = 51;
+ venc->params.bitrate_min = BITRATE_MIN;
+
+ memset(&f, 0, sizeof(f));
+ f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
+ f.fmt.pix_mp.width = 1280;
+ f.fmt.pix_mp.height = 720;
+ f.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f.fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
+ venc_s_fmt(file, &inst->fh, &f);
+
+ memset(&f, 0, sizeof(f));
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ f.fmt.pix_mp.width = 1280;
+ f.fmt.pix_mp.height = 720;
+ f.fmt.pix_mp.field = V4L2_FIELD_NONE;
+ venc_s_fmt(file, &inst->fh, &f);
+
+ memset(&parm, 0, sizeof(parm));
+ parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ parm.parm.capture.timeperframe.numerator = 1;
+ parm.parm.capture.timeperframe.denominator = 30;
+ venc_s_parm(file, &inst->fh, &parm);
+}
+
+static int venc_open(struct file *file)
+{
+ struct vpu_inst *inst;
+ struct venc_t *venc;
+ int ret;
+
+ inst = vzalloc(sizeof(*inst));
+ if (!inst)
+ return -ENOMEM;
+
+ venc = vzalloc(sizeof(*venc));
+ if (!venc) {
+ vfree(inst);
+ return -ENOMEM;
+ }
+
+ inst->ops = &venc_inst_ops;
+ inst->formats = venc_formats;
+ inst->type = VPU_CORE_TYPE_ENC;
+ inst->priv = venc;
+ INIT_LIST_HEAD(&venc->frames);
+ init_waitqueue_head(&venc->wq);
+
+ ret = vpu_v4l2_open(file, inst);
+ if (ret)
+ return ret;
+
+ venc_init(file);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations venc_fops = {
+ .owner = THIS_MODULE,
+ .open = venc_open,
+ .release = vpu_v4l2_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+const struct v4l2_ioctl_ops *venc_get_ioctl_ops(void)
+{
+ return &venc_ioctl_ops;
+}
+
+const struct v4l2_file_operations *venc_get_fops(void)
+{
+ return &venc_fops;
+}
diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h
new file mode 100644
index 000000000000..e56b96a7e5d3
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu.h
@@ -0,0 +1,362 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_H
+#define _AMPHION_VPU_H
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-mem2mem.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox_controller.h>
+#include <linux/kfifo.h>
+
+#define VPU_TIMEOUT msecs_to_jiffies(1000)
+#define VPU_INST_NULL_ID (-1L)
+#define VPU_MSG_BUFFER_SIZE (8192)
+
+enum imx_plat_type {
+ IMX8QXP = 0,
+ IMX8QM = 1,
+ IMX8DM,
+ IMX8DX,
+ PLAT_TYPE_RESERVED
+};
+
+enum vpu_core_type {
+ VPU_CORE_TYPE_ENC = 0,
+ VPU_CORE_TYPE_DEC = 0x10,
+};
+
+struct vpu_dev;
+struct vpu_resources {
+ enum imx_plat_type plat_type;
+ u32 mreg_base;
+ int (*setup)(struct vpu_dev *vpu);
+ int (*setup_encoder)(struct vpu_dev *vpu);
+ int (*setup_decoder)(struct vpu_dev *vpu);
+ int (*reset)(struct vpu_dev *vpu);
+};
+
+struct vpu_buffer {
+ void *virt;
+ dma_addr_t phys;
+ u32 length;
+ u32 bytesused;
+ struct device *dev;
+};
+
+struct vpu_func {
+ struct video_device *vfd;
+ struct v4l2_m2m_dev *m2m_dev;
+ enum vpu_core_type type;
+ int function;
+};
+
+struct vpu_dev {
+ void __iomem *base;
+ struct platform_device *pdev;
+ struct device *dev;
+ struct mutex lock; /* protect vpu device */
+ const struct vpu_resources *res;
+ struct list_head cores;
+
+ struct v4l2_device v4l2_dev;
+ struct vpu_func encoder;
+ struct vpu_func decoder;
+ struct media_device mdev;
+
+ struct delayed_work watchdog_work;
+ void (*get_vpu)(struct vpu_dev *vpu);
+ void (*put_vpu)(struct vpu_dev *vpu);
+ void (*get_enc)(struct vpu_dev *vpu);
+ void (*put_enc)(struct vpu_dev *vpu);
+ void (*get_dec)(struct vpu_dev *vpu);
+ void (*put_dec)(struct vpu_dev *vpu);
+ atomic_t ref_vpu;
+ atomic_t ref_enc;
+ atomic_t ref_dec;
+
+ struct dentry *debugfs;
+};
+
+struct vpu_format {
+ u32 pixfmt;
+ unsigned int num_planes;
+ u32 type;
+ u32 flags;
+ u32 width;
+ u32 height;
+ u32 sizeimage[VIDEO_MAX_PLANES];
+ u32 bytesperline[VIDEO_MAX_PLANES];
+ u32 field;
+};
+
+struct vpu_core_resources {
+ enum vpu_core_type type;
+ const char *fwname;
+ u32 stride;
+ u32 max_width;
+ u32 min_width;
+ u32 step_width;
+ u32 max_height;
+ u32 min_height;
+ u32 step_height;
+ u32 rpc_size;
+ u32 fwlog_size;
+ u32 act_size;
+};
+
+struct vpu_mbox {
+ char name[20];
+ struct mbox_client cl;
+ struct mbox_chan *ch;
+ bool block;
+};
+
+enum vpu_core_state {
+ VPU_CORE_DEINIT = 0,
+ VPU_CORE_ACTIVE,
+ VPU_CORE_SNAPSHOT,
+ VPU_CORE_HANG
+};
+
+struct vpu_core {
+ void __iomem *base;
+ struct platform_device *pdev;
+ struct device *dev;
+ struct device *parent;
+ struct device *pd;
+ struct device_link *pd_link;
+ struct mutex lock; /* protect vpu core */
+ struct mutex cmd_lock; /* Lock vpu command */
+ struct list_head list;
+ enum vpu_core_type type;
+ int id;
+ const struct vpu_core_resources *res;
+ unsigned long instance_mask;
+ u32 supported_instance_count;
+ unsigned long hang_mask;
+ u32 request_count;
+ struct list_head instances;
+ enum vpu_core_state state;
+ u32 fw_version;
+
+ struct vpu_buffer fw;
+ struct vpu_buffer rpc;
+ struct vpu_buffer log;
+ struct vpu_buffer act;
+
+ struct vpu_mbox tx_type;
+ struct vpu_mbox tx_data;
+ struct vpu_mbox rx;
+ unsigned long cmd_seq;
+
+ wait_queue_head_t ack_wq;
+ struct completion cmp;
+ struct workqueue_struct *workqueue;
+ struct work_struct msg_work;
+ struct delayed_work msg_delayed_work;
+ struct kfifo msg_fifo;
+ void *msg_buffer;
+ unsigned int msg_buffer_size;
+
+ struct vpu_dev *vpu;
+ void *iface;
+
+ struct dentry *debugfs;
+ struct dentry *debugfs_fwlog;
+};
+
+enum vpu_codec_state {
+ VPU_CODEC_STATE_DEINIT = 1,
+ VPU_CODEC_STATE_CONFIGURED,
+ VPU_CODEC_STATE_START,
+ VPU_CODEC_STATE_STARTED,
+ VPU_CODEC_STATE_ACTIVE,
+ VPU_CODEC_STATE_SEEK,
+ VPU_CODEC_STATE_STOP,
+ VPU_CODEC_STATE_DRAIN,
+ VPU_CODEC_STATE_DYAMIC_RESOLUTION_CHANGE,
+};
+
+struct vpu_frame_info {
+ u32 type;
+ u32 id;
+ u32 sequence;
+ u32 luma;
+ u32 chroma_u;
+ u32 chroma_v;
+ u32 data_offset;
+ u32 flags;
+ u32 skipped;
+ s64 timestamp;
+};
+
+struct vpu_inst;
+struct vpu_inst_ops {
+ int (*ctrl_init)(struct vpu_inst *inst);
+ int (*start)(struct vpu_inst *inst, u32 type);
+ int (*stop)(struct vpu_inst *inst, u32 type);
+ int (*abort)(struct vpu_inst *inst);
+ bool (*check_ready)(struct vpu_inst *inst, unsigned int type);
+ void (*buf_done)(struct vpu_inst *inst, struct vpu_frame_info *frame);
+ void (*event_notify)(struct vpu_inst *inst, u32 event, void *data);
+ void (*release)(struct vpu_inst *inst);
+ void (*cleanup)(struct vpu_inst *inst);
+ void (*mem_request)(struct vpu_inst *inst,
+ u32 enc_frame_size,
+ u32 enc_frame_num,
+ u32 ref_frame_size,
+ u32 ref_frame_num,
+ u32 act_frame_size,
+ u32 act_frame_num);
+ void (*input_done)(struct vpu_inst *inst);
+ void (*stop_done)(struct vpu_inst *inst);
+ int (*process_output)(struct vpu_inst *inst, struct vb2_buffer *vb);
+ int (*process_capture)(struct vpu_inst *inst, struct vb2_buffer *vb);
+ int (*get_one_frame)(struct vpu_inst *inst, void *info);
+ void (*on_queue_empty)(struct vpu_inst *inst, u32 type);
+ int (*get_debug_info)(struct vpu_inst *inst, char *str, u32 size, u32 i);
+ void (*wait_prepare)(struct vpu_inst *inst);
+ void (*wait_finish)(struct vpu_inst *inst);
+};
+
+struct vpu_inst {
+ struct list_head list;
+ struct mutex lock; /* v4l2 and videobuf2 lock */
+ struct vpu_dev *vpu;
+ struct vpu_core *core;
+ struct device *dev;
+ int id;
+
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler ctrl_handler;
+ atomic_t ref_count;
+ int (*release)(struct vpu_inst *inst);
+
+ enum vpu_codec_state state;
+ enum vpu_core_type type;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct msg_work;
+ struct kfifo msg_fifo;
+ u8 msg_buffer[VPU_MSG_BUFFER_SIZE];
+
+ struct vpu_buffer stream_buffer;
+ bool use_stream_buffer;
+ struct vpu_buffer act;
+
+ struct list_head cmd_q;
+ void *pending;
+
+ struct vpu_inst_ops *ops;
+ const struct vpu_format *formats;
+ struct vpu_format out_format;
+ struct vpu_format cap_format;
+ u32 min_buffer_cap;
+ u32 min_buffer_out;
+
+ struct v4l2_rect crop;
+ u32 colorspace;
+ u8 ycbcr_enc;
+ u8 quantization;
+ u8 xfer_func;
+ u32 sequence;
+ u32 extra_size;
+
+ u32 flows[16];
+ u32 flow_idx;
+
+ pid_t pid;
+ pid_t tgid;
+ struct dentry *debugfs;
+
+ void *priv;
+};
+
+#define call_vop(inst, op, args...) \
+ ((inst)->ops->op ? (inst)->ops->op(inst, ##args) : 0) \
+
+#define call_void_vop(inst, op, args...) \
+ do { \
+ if ((inst)->ops->op) \
+ (inst)->ops->op(inst, ##args); \
+ } while (0)
+
+enum {
+ VPU_BUF_STATE_IDLE = 0,
+ VPU_BUF_STATE_INUSE,
+ VPU_BUF_STATE_DECODED,
+ VPU_BUF_STATE_READY,
+ VPU_BUF_STATE_SKIP,
+ VPU_BUF_STATE_ERROR
+};
+
+struct vpu_vb2_buffer {
+ struct v4l2_m2m_buffer m2m_buf;
+ dma_addr_t luma;
+ dma_addr_t chroma_u;
+ dma_addr_t chroma_v;
+ unsigned int state;
+ u32 tag;
+};
+
+void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val);
+u32 vpu_readl(struct vpu_dev *vpu, u32 reg);
+
+static inline struct vpu_vb2_buffer *to_vpu_vb2_buffer(struct vb2_v4l2_buffer *vbuf)
+{
+ struct v4l2_m2m_buffer *m2m_buf = container_of(vbuf, struct v4l2_m2m_buffer, vb);
+
+ return container_of(m2m_buf, struct vpu_vb2_buffer, m2m_buf);
+}
+
+static inline const char *vpu_core_type_desc(enum vpu_core_type type)
+{
+ return type == VPU_CORE_TYPE_ENC ? "encoder" : "decoder";
+}
+
+static inline struct vpu_inst *to_inst(struct file *filp)
+{
+ return container_of(filp->private_data, struct vpu_inst, fh);
+}
+
+#define ctrl_to_inst(ctrl) \
+ container_of((ctrl)->handler, struct vpu_inst, ctrl_handler)
+
+const struct v4l2_ioctl_ops *venc_get_ioctl_ops(void);
+const struct v4l2_file_operations *venc_get_fops(void);
+const struct v4l2_ioctl_ops *vdec_get_ioctl_ops(void);
+const struct v4l2_file_operations *vdec_get_fops(void);
+
+int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func);
+void vpu_remove_func(struct vpu_func *func);
+
+struct vpu_inst *vpu_inst_get(struct vpu_inst *inst);
+void vpu_inst_put(struct vpu_inst *inst);
+struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type);
+void vpu_release_core(struct vpu_core *core);
+int vpu_inst_register(struct vpu_inst *inst);
+int vpu_inst_unregister(struct vpu_inst *inst);
+const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst);
+
+int vpu_inst_create_dbgfs_file(struct vpu_inst *inst);
+int vpu_inst_remove_dbgfs_file(struct vpu_inst *inst);
+int vpu_core_create_dbgfs_file(struct vpu_core *core);
+int vpu_core_remove_dbgfs_file(struct vpu_core *core);
+void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow);
+
+int vpu_core_driver_init(void);
+void vpu_core_driver_exit(void);
+
+extern bool debug;
+#define vpu_trace(dev, fmt, arg...) \
+ do { \
+ if (debug) \
+ dev_info(dev, "%s: " fmt, __func__, ## arg); \
+ } while (0)
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_cmds.c b/drivers/media/platform/amphion/vpu_cmds.c
new file mode 100644
index 000000000000..9b39d77a178d
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_cmds.c
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include "vpu.h"
+#include "vpu_defs.h"
+#include "vpu_cmds.h"
+#include "vpu_rpc.h"
+#include "vpu_mbox.h"
+
+struct vpu_cmd_request {
+ u32 request;
+ u32 response;
+ u32 handled;
+};
+
+struct vpu_cmd_t {
+ struct list_head list;
+ u32 id;
+ struct vpu_cmd_request *request;
+ struct vpu_rpc_event *pkt;
+ unsigned long key;
+};
+
+static struct vpu_cmd_request vpu_cmd_requests[] = {
+ {
+ .request = VPU_CMD_ID_CONFIGURE_CODEC,
+ .response = VPU_MSG_ID_MEM_REQUEST,
+ .handled = 1,
+ },
+ {
+ .request = VPU_CMD_ID_START,
+ .response = VPU_MSG_ID_START_DONE,
+ .handled = 0,
+ },
+ {
+ .request = VPU_CMD_ID_STOP,
+ .response = VPU_MSG_ID_STOP_DONE,
+ .handled = 0,
+ },
+ {
+ .request = VPU_CMD_ID_ABORT,
+ .response = VPU_MSG_ID_ABORT_DONE,
+ .handled = 0,
+ },
+ {
+ .request = VPU_CMD_ID_RST_BUF,
+ .response = VPU_MSG_ID_BUF_RST,
+ .handled = 1,
+ },
+};
+
+static int vpu_cmd_send(struct vpu_core *core, struct vpu_rpc_event *pkt)
+{
+ int ret = 0;
+
+ ret = vpu_iface_send_cmd(core, pkt);
+ if (ret)
+ return ret;
+
+ /*write cmd data to cmd buffer before trigger a cmd interrupt*/
+ mb();
+ vpu_mbox_send_type(core, COMMAND);
+
+ return ret;
+}
+
+static struct vpu_cmd_t *vpu_alloc_cmd(struct vpu_inst *inst, u32 id, void *data)
+{
+ struct vpu_cmd_t *cmd;
+ int i;
+ int ret;
+
+ cmd = vzalloc(sizeof(*cmd));
+ if (!cmd)
+ return NULL;
+
+ cmd->pkt = vzalloc(sizeof(*cmd->pkt));
+ if (!cmd->pkt) {
+ vfree(cmd);
+ return NULL;
+ }
+
+ cmd->id = id;
+ ret = vpu_iface_pack_cmd(inst->core, cmd->pkt, inst->id, id, data);
+ if (ret) {
+ dev_err(inst->dev, "iface pack cmd(%d) fail\n", id);
+ vfree(cmd->pkt);
+ vfree(cmd);
+ return NULL;
+ }
+ for (i = 0; i < ARRAY_SIZE(vpu_cmd_requests); i++) {
+ if (vpu_cmd_requests[i].request == id) {
+ cmd->request = &vpu_cmd_requests[i];
+ break;
+ }
+ }
+
+ return cmd;
+}
+
+static void vpu_free_cmd(struct vpu_cmd_t *cmd)
+{
+ if (!cmd)
+ return;
+ if (cmd->pkt)
+ vfree(cmd->pkt);
+ vfree(cmd);
+}
+
+static int vpu_session_process_cmd(struct vpu_inst *inst, struct vpu_cmd_t *cmd)
+{
+ int ret;
+
+ dev_dbg(inst->dev, "[%d]send cmd(0x%x)\n", inst->id, cmd->id);
+ vpu_iface_pre_send_cmd(inst);
+ ret = vpu_cmd_send(inst->core, cmd->pkt);
+ if (!ret) {
+ vpu_iface_post_send_cmd(inst);
+ vpu_inst_record_flow(inst, cmd->id);
+ } else {
+ dev_err(inst->dev, "[%d] iface send cmd(0x%x) fail\n", inst->id, cmd->id);
+ }
+
+ return ret;
+}
+
+static void vpu_process_cmd_request(struct vpu_inst *inst)
+{
+ struct vpu_cmd_t *cmd;
+ struct vpu_cmd_t *tmp;
+
+ if (!inst || inst->pending)
+ return;
+
+ list_for_each_entry_safe(cmd, tmp, &inst->cmd_q, list) {
+ list_del_init(&cmd->list);
+ if (vpu_session_process_cmd(inst, cmd))
+ dev_err(inst->dev, "[%d] process cmd(%d) fail\n", inst->id, cmd->id);
+ if (cmd->request) {
+ inst->pending = (void *)cmd;
+ break;
+ }
+ vpu_free_cmd(cmd);
+ }
+}
+
+static int vpu_request_cmd(struct vpu_inst *inst, u32 id, void *data,
+ unsigned long *key, int *sync)
+{
+ struct vpu_core *core;
+ struct vpu_cmd_t *cmd;
+
+ if (!inst || !inst->core)
+ return -EINVAL;
+
+ core = inst->core;
+ cmd = vpu_alloc_cmd(inst, id, data);
+ if (!cmd)
+ return -ENOMEM;
+
+ mutex_lock(&core->cmd_lock);
+ cmd->key = core->cmd_seq++;
+ if (key)
+ *key = cmd->key;
+ if (sync)
+ *sync = cmd->request ? true : false;
+ list_add_tail(&cmd->list, &inst->cmd_q);
+ vpu_process_cmd_request(inst);
+ mutex_unlock(&core->cmd_lock);
+
+ return 0;
+}
+
+static void vpu_clear_pending(struct vpu_inst *inst)
+{
+ if (!inst || !inst->pending)
+ return;
+
+ vpu_free_cmd(inst->pending);
+ wake_up_all(&inst->core->ack_wq);
+ inst->pending = NULL;
+}
+
+static bool vpu_check_response(struct vpu_cmd_t *cmd, u32 response, u32 handled)
+{
+ struct vpu_cmd_request *request;
+
+ if (!cmd || !cmd->request)
+ return false;
+
+ request = cmd->request;
+ if (request->response != response)
+ return false;
+ if (request->handled != handled)
+ return false;
+
+ return true;
+}
+
+int vpu_response_cmd(struct vpu_inst *inst, u32 response, u32 handled)
+{
+ struct vpu_core *core;
+
+ if (!inst || !inst->core)
+ return -EINVAL;
+
+ core = inst->core;
+ mutex_lock(&core->cmd_lock);
+ if (vpu_check_response(inst->pending, response, handled))
+ vpu_clear_pending(inst);
+
+ vpu_process_cmd_request(inst);
+ mutex_unlock(&core->cmd_lock);
+
+ return 0;
+}
+
+void vpu_clear_request(struct vpu_inst *inst)
+{
+ struct vpu_cmd_t *cmd;
+ struct vpu_cmd_t *tmp;
+
+ mutex_lock(&inst->core->cmd_lock);
+ if (inst->pending)
+ vpu_clear_pending(inst);
+
+ list_for_each_entry_safe(cmd, tmp, &inst->cmd_q, list) {
+ list_del_init(&cmd->list);
+ vpu_free_cmd(cmd);
+ }
+ mutex_unlock(&inst->core->cmd_lock);
+}
+
+static bool check_is_responsed(struct vpu_inst *inst, unsigned long key)
+{
+ struct vpu_core *core = inst->core;
+ struct vpu_cmd_t *cmd;
+ bool flag = true;
+
+ mutex_lock(&core->cmd_lock);
+ cmd = inst->pending;
+ if (cmd && key == cmd->key) {
+ flag = false;
+ goto exit;
+ }
+ list_for_each_entry(cmd, &inst->cmd_q, list) {
+ if (key == cmd->key) {
+ flag = false;
+ break;
+ }
+ }
+exit:
+ mutex_unlock(&core->cmd_lock);
+
+ return flag;
+}
+
+static int sync_session_response(struct vpu_inst *inst, unsigned long key)
+{
+ struct vpu_core *core;
+
+ if (!inst || !inst->core)
+ return -EINVAL;
+
+ core = inst->core;
+
+ call_void_vop(inst, wait_prepare);
+ wait_event_timeout(core->ack_wq, check_is_responsed(inst, key), VPU_TIMEOUT);
+ call_void_vop(inst, wait_finish);
+
+ if (!check_is_responsed(inst, key)) {
+ dev_err(inst->dev, "[%d] sync session timeout\n", inst->id);
+ set_bit(inst->id, &core->hang_mask);
+ mutex_lock(&inst->core->cmd_lock);
+ vpu_clear_pending(inst);
+ mutex_unlock(&inst->core->cmd_lock);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vpu_session_send_cmd(struct vpu_inst *inst, u32 id, void *data)
+{
+ unsigned long key;
+ int sync = false;
+ int ret = -EINVAL;
+
+ if (inst->id < 0)
+ return -EINVAL;
+
+ ret = vpu_request_cmd(inst, id, data, &key, &sync);
+ if (!ret && sync)
+ ret = sync_session_response(inst, key);
+
+ if (ret)
+ dev_err(inst->dev, "[%d] send cmd(0x%x) fail\n", inst->id, id);
+
+ return ret;
+}
+
+int vpu_session_configure_codec(struct vpu_inst *inst)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_CONFIGURE_CODEC, NULL);
+}
+
+int vpu_session_start(struct vpu_inst *inst)
+{
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_START, NULL);
+}
+
+int vpu_session_stop(struct vpu_inst *inst)
+{
+ int ret;
+
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+
+ ret = vpu_session_send_cmd(inst, VPU_CMD_ID_STOP, NULL);
+ /* workaround for a firmware bug,
+ * if the next command is too close after stop cmd,
+ * the firmware may enter wfi wrongly.
+ */
+ usleep_range(3000, 5000);
+ return ret;
+}
+
+int vpu_session_encode_frame(struct vpu_inst *inst, s64 timestamp)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_FRAME_ENCODE, &timestamp);
+}
+
+int vpu_session_alloc_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_FS_ALLOC, fs);
+}
+
+int vpu_session_release_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_FS_RELEASE, fs);
+}
+
+int vpu_session_abort(struct vpu_inst *inst)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_ABORT, NULL);
+}
+
+int vpu_session_rst_buf(struct vpu_inst *inst)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_RST_BUF, NULL);
+}
+
+int vpu_session_fill_timestamp(struct vpu_inst *inst, struct vpu_ts_info *info)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_TIMESTAMP, info);
+}
+
+int vpu_session_update_parameters(struct vpu_inst *inst, void *arg)
+{
+ if (inst->type & VPU_CORE_TYPE_DEC)
+ vpu_iface_set_decode_params(inst, arg, 1);
+ else
+ vpu_iface_set_encode_params(inst, arg, 1);
+
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_UPDATE_PARAMETER, arg);
+}
+
+int vpu_session_debug(struct vpu_inst *inst)
+{
+ return vpu_session_send_cmd(inst, VPU_CMD_ID_DEBUG, NULL);
+}
+
+int vpu_core_snapshot(struct vpu_core *core)
+{
+ struct vpu_inst *inst;
+ int ret;
+
+ if (!core || list_empty(&core->instances))
+ return 0;
+
+ inst = list_first_entry(&core->instances, struct vpu_inst, list);
+
+ reinit_completion(&core->cmp);
+ ret = vpu_session_send_cmd(inst, VPU_CMD_ID_SNAPSHOT, NULL);
+ if (ret)
+ return ret;
+ ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT);
+ if (!ret) {
+ dev_err(core->dev, "snapshot timeout\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vpu_core_sw_reset(struct vpu_core *core)
+{
+ struct vpu_rpc_event pkt;
+ int ret;
+
+ memset(&pkt, 0, sizeof(pkt));
+ vpu_iface_pack_cmd(core, &pkt, 0, VPU_CMD_ID_FIRM_RESET, NULL);
+
+ reinit_completion(&core->cmp);
+ mutex_lock(&core->cmd_lock);
+ ret = vpu_cmd_send(core, &pkt);
+ mutex_unlock(&core->cmd_lock);
+ if (ret)
+ return ret;
+ ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT);
+ if (!ret) {
+ dev_err(core->dev, "sw reset timeout\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/drivers/media/platform/amphion/vpu_cmds.h b/drivers/media/platform/amphion/vpu_cmds.h
new file mode 100644
index 000000000000..bc538d277bc9
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_cmds.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_CMDS_H
+#define _AMPHION_VPU_CMDS_H
+
+int vpu_session_configure_codec(struct vpu_inst *inst);
+int vpu_session_start(struct vpu_inst *inst);
+int vpu_session_stop(struct vpu_inst *inst);
+int vpu_session_abort(struct vpu_inst *inst);
+int vpu_session_rst_buf(struct vpu_inst *inst);
+int vpu_session_encode_frame(struct vpu_inst *inst, s64 timestamp);
+int vpu_session_alloc_fs(struct vpu_inst *inst, struct vpu_fs_info *fs);
+int vpu_session_release_fs(struct vpu_inst *inst, struct vpu_fs_info *fs);
+int vpu_session_fill_timestamp(struct vpu_inst *inst, struct vpu_ts_info *info);
+int vpu_session_update_parameters(struct vpu_inst *inst, void *arg);
+int vpu_core_snapshot(struct vpu_core *core);
+int vpu_core_sw_reset(struct vpu_core *core);
+int vpu_response_cmd(struct vpu_inst *inst, u32 response, u32 handled);
+void vpu_clear_request(struct vpu_inst *inst);
+int vpu_session_debug(struct vpu_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_codec.h b/drivers/media/platform/amphion/vpu_codec.h
new file mode 100644
index 000000000000..528a93f08ecd
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_codec.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_CODEC_H
+#define _AMPHION_VPU_CODEC_H
+
+struct vpu_encode_params {
+ u32 input_format;
+ u32 codec_format;
+ u32 profile;
+ u32 tier;
+ u32 level;
+ struct v4l2_fract frame_rate;
+ u32 src_stride;
+ u32 src_width;
+ u32 src_height;
+ struct v4l2_rect crop;
+ u32 out_width;
+ u32 out_height;
+
+ u32 gop_length;
+ u32 bframes;
+
+ u32 rc_enable;
+ u32 rc_mode;
+ u32 bitrate;
+ u32 bitrate_min;
+ u32 bitrate_max;
+
+ u32 i_frame_qp;
+ u32 p_frame_qp;
+ u32 b_frame_qp;
+ u32 qp_min;
+ u32 qp_max;
+ u32 qp_min_i;
+ u32 qp_max_i;
+
+ struct {
+ u32 enable;
+ u32 idc;
+ u32 width;
+ u32 height;
+ } sar;
+
+ struct {
+ u32 primaries;
+ u32 transfer;
+ u32 matrix;
+ u32 full_range;
+ } color;
+};
+
+struct vpu_decode_params {
+ u32 codec_format;
+ u32 output_format;
+ u32 b_dis_reorder;
+ u32 b_non_frame;
+ u32 frame_count;
+ u32 end_flag;
+ struct {
+ u32 base;
+ u32 size;
+ } udata;
+};
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_color.c b/drivers/media/platform/amphion/vpu_color.c
new file mode 100644
index 000000000000..80b9a53fd1c1
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_color.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <media/v4l2-device.h>
+#include "vpu.h"
+#include "vpu_helpers.h"
+
+static const u8 colorprimaries[] = {
+ 0,
+ V4L2_COLORSPACE_REC709, /*Rec. ITU-R BT.709-6*/
+ 0,
+ 0,
+ V4L2_COLORSPACE_470_SYSTEM_M, /*Rec. ITU-R BT.470-6 System M*/
+ V4L2_COLORSPACE_470_SYSTEM_BG, /*Rec. ITU-R BT.470-6 System B, G*/
+ V4L2_COLORSPACE_SMPTE170M, /*SMPTE170M*/
+ V4L2_COLORSPACE_SMPTE240M, /*SMPTE240M*/
+ 0, /*Generic film*/
+ V4L2_COLORSPACE_BT2020, /*Rec. ITU-R BT.2020-2*/
+ 0, /*SMPTE ST 428-1*/
+};
+
+static const u8 colortransfers[] = {
+ 0,
+ V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.709-6*/
+ 0,
+ 0,
+ 0, /*Rec. ITU-R BT.470-6 System M*/
+ 0, /*Rec. ITU-R BT.470-6 System B, G*/
+ V4L2_XFER_FUNC_709, /*SMPTE170M*/
+ V4L2_XFER_FUNC_SMPTE240M, /*SMPTE240M*/
+ V4L2_XFER_FUNC_NONE, /*Linear transfer characteristics*/
+ 0,
+ 0,
+ 0, /*IEC 61966-2-4*/
+ 0, /*Rec. ITU-R BT.1361-0 extended colour gamut*/
+ V4L2_XFER_FUNC_SRGB, /*IEC 61966-2-1 sRGB or sYCC*/
+ V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (10 bit system)*/
+ V4L2_XFER_FUNC_709, /*Rec. ITU-R BT.2020-2 (12 bit system)*/
+ V4L2_XFER_FUNC_SMPTE2084, /*SMPTE ST 2084*/
+ 0, /*SMPTE ST 428-1*/
+ 0 /*Rec. ITU-R BT.2100-0 hybrid log-gamma (HLG)*/
+};
+
+static const u8 colormatrixcoefs[] = {
+ 0,
+ V4L2_YCBCR_ENC_709, /*Rec. ITU-R BT.709-6*/
+ 0,
+ 0,
+ 0, /*Title 47 Code of Federal Regulations*/
+ V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 625*/
+ V4L2_YCBCR_ENC_601, /*Rec. ITU-R BT.601-7 525*/
+ V4L2_YCBCR_ENC_SMPTE240M, /*SMPTE240M*/
+ 0,
+ V4L2_YCBCR_ENC_BT2020, /*Rec. ITU-R BT.2020-2*/
+ V4L2_YCBCR_ENC_BT2020_CONST_LUM /*Rec. ITU-R BT.2020-2 constant*/
+};
+
+u32 vpu_color_cvrt_primaries_v2i(u32 primaries)
+{
+ return vpu_helper_find_in_array_u8(colorprimaries, ARRAY_SIZE(colorprimaries), primaries);
+}
+
+u32 vpu_color_cvrt_primaries_i2v(u32 primaries)
+{
+ return primaries < ARRAY_SIZE(colorprimaries) ? colorprimaries[primaries] : 0;
+}
+
+u32 vpu_color_cvrt_transfers_v2i(u32 transfers)
+{
+ return vpu_helper_find_in_array_u8(colortransfers, ARRAY_SIZE(colortransfers), transfers);
+}
+
+u32 vpu_color_cvrt_transfers_i2v(u32 transfers)
+{
+ return transfers < ARRAY_SIZE(colortransfers) ? colortransfers[transfers] : 0;
+}
+
+u32 vpu_color_cvrt_matrix_v2i(u32 matrix)
+{
+ return vpu_helper_find_in_array_u8(colormatrixcoefs, ARRAY_SIZE(colormatrixcoefs), matrix);
+}
+
+u32 vpu_color_cvrt_matrix_i2v(u32 matrix)
+{
+ return matrix < ARRAY_SIZE(colormatrixcoefs) ? colormatrixcoefs[matrix] : 0;
+}
+
+u32 vpu_color_cvrt_full_range_v2i(u32 full_range)
+{
+ return (full_range == V4L2_QUANTIZATION_FULL_RANGE);
+}
+
+u32 vpu_color_cvrt_full_range_i2v(u32 full_range)
+{
+ if (full_range)
+ return V4L2_QUANTIZATION_FULL_RANGE;
+
+ return V4L2_QUANTIZATION_LIM_RANGE;
+}
+
+int vpu_color_check_primaries(u32 primaries)
+{
+ return vpu_color_cvrt_primaries_v2i(primaries) ? 0 : -EINVAL;
+}
+
+int vpu_color_check_transfers(u32 transfers)
+{
+ return vpu_color_cvrt_transfers_v2i(transfers) ? 0 : -EINVAL;
+}
+
+int vpu_color_check_matrix(u32 matrix)
+{
+ return vpu_color_cvrt_matrix_v2i(matrix) ? 0 : -EINVAL;
+}
+
+int vpu_color_check_full_range(u32 full_range)
+{
+ int ret = -EINVAL;
+
+ switch (full_range) {
+ case V4L2_QUANTIZATION_FULL_RANGE:
+ case V4L2_QUANTIZATION_LIM_RANGE:
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range)
+{
+ u32 transfers;
+ u32 matrix;
+ u32 full_range;
+
+ switch (primaries) {
+ case V4L2_COLORSPACE_REC709:
+ transfers = V4L2_XFER_FUNC_709;
+ matrix = V4L2_YCBCR_ENC_709;
+ break;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ case V4L2_COLORSPACE_SMPTE170M:
+ transfers = V4L2_XFER_FUNC_709;
+ matrix = V4L2_YCBCR_ENC_601;
+ break;
+ case V4L2_COLORSPACE_SMPTE240M:
+ transfers = V4L2_XFER_FUNC_SMPTE240M;
+ matrix = V4L2_YCBCR_ENC_SMPTE240M;
+ break;
+ case V4L2_COLORSPACE_BT2020:
+ transfers = V4L2_XFER_FUNC_709;
+ matrix = V4L2_YCBCR_ENC_BT2020;
+ break;
+ default:
+ transfers = V4L2_XFER_FUNC_DEFAULT;
+ matrix = V4L2_YCBCR_ENC_DEFAULT;
+ break;
+ }
+ full_range = V4L2_QUANTIZATION_LIM_RANGE;
+
+ if (ptransfers)
+ *ptransfers = transfers;
+ if (pmatrix)
+ *pmatrix = matrix;
+ if (pfull_range)
+ *pfull_range = full_range;
+
+ return 0;
+}
diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c
new file mode 100644
index 000000000000..68ad183925fd
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_core.c
@@ -0,0 +1,879 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/firmware.h>
+#include <linux/vmalloc.h>
+#include "vpu.h"
+#include "vpu_defs.h"
+#include "vpu_core.h"
+#include "vpu_mbox.h"
+#include "vpu_msgs.h"
+#include "vpu_rpc.h"
+#include "vpu_cmds.h"
+
+void csr_writel(struct vpu_core *core, u32 reg, u32 val)
+{
+ writel(val, core->base + reg);
+}
+
+u32 csr_readl(struct vpu_core *core, u32 reg)
+{
+ return readl(core->base + reg);
+}
+
+static int vpu_core_load_firmware(struct vpu_core *core)
+{
+ const struct firmware *pfw = NULL;
+ int ret = 0;
+
+ if (!core->fw.virt) {
+ dev_err(core->dev, "firmware buffer is not ready\n");
+ return -EINVAL;
+ }
+
+ ret = request_firmware(&pfw, core->res->fwname, core->dev);
+ dev_dbg(core->dev, "request_firmware %s : %d\n", core->res->fwname, ret);
+ if (ret) {
+ dev_err(core->dev, "request firmware %s failed, ret = %d\n",
+ core->res->fwname, ret);
+ return ret;
+ }
+
+ if (core->fw.length < pfw->size) {
+ dev_err(core->dev, "firmware buffer size want %zu, but %d\n",
+ pfw->size, core->fw.length);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ memset(core->fw.virt, 0, core->fw.length);
+ memcpy(core->fw.virt, pfw->data, pfw->size);
+ core->fw.bytesused = pfw->size;
+ ret = vpu_iface_on_firmware_loaded(core);
+exit:
+ release_firmware(pfw);
+ pfw = NULL;
+
+ return ret;
+}
+
+static int vpu_core_boot_done(struct vpu_core *core)
+{
+ u32 fw_version;
+
+ fw_version = vpu_iface_get_version(core);
+ dev_info(core->dev, "%s firmware version : %d.%d.%d\n",
+ vpu_core_type_desc(core->type),
+ (fw_version >> 16) & 0xff,
+ (fw_version >> 8) & 0xff,
+ fw_version & 0xff);
+ core->supported_instance_count = vpu_iface_get_max_instance_count(core);
+ if (core->res->act_size) {
+ u32 count = core->act.length / core->res->act_size;
+
+ core->supported_instance_count = min(core->supported_instance_count, count);
+ }
+ core->fw_version = fw_version;
+ core->state = VPU_CORE_ACTIVE;
+
+ return 0;
+}
+
+static int vpu_core_wait_boot_done(struct vpu_core *core)
+{
+ int ret;
+
+ ret = wait_for_completion_timeout(&core->cmp, VPU_TIMEOUT);
+ if (!ret) {
+ dev_err(core->dev, "boot timeout\n");
+ return -EINVAL;
+ }
+ return vpu_core_boot_done(core);
+}
+
+static int vpu_core_boot(struct vpu_core *core, bool load)
+{
+ int ret;
+
+ reinit_completion(&core->cmp);
+ if (load) {
+ ret = vpu_core_load_firmware(core);
+ if (ret)
+ return ret;
+ }
+
+ vpu_iface_boot_core(core);
+ return vpu_core_wait_boot_done(core);
+}
+
+static int vpu_core_shutdown(struct vpu_core *core)
+{
+ return vpu_iface_shutdown_core(core);
+}
+
+static int vpu_core_restore(struct vpu_core *core)
+{
+ int ret;
+
+ ret = vpu_core_sw_reset(core);
+ if (ret)
+ return ret;
+
+ vpu_core_boot_done(core);
+ return vpu_iface_restore_core(core);
+}
+
+static int __vpu_alloc_dma(struct device *dev, struct vpu_buffer *buf)
+{
+ gfp_t gfp = GFP_KERNEL | GFP_DMA32;
+
+ if (!buf->length)
+ return 0;
+
+ buf->virt = dma_alloc_coherent(dev, buf->length, &buf->phys, gfp);
+ if (!buf->virt)
+ return -ENOMEM;
+
+ buf->dev = dev;
+
+ return 0;
+}
+
+void vpu_free_dma(struct vpu_buffer *buf)
+{
+ if (!buf->virt || !buf->dev)
+ return;
+
+ dma_free_coherent(buf->dev, buf->length, buf->virt, buf->phys);
+ buf->virt = NULL;
+ buf->phys = 0;
+ buf->length = 0;
+ buf->bytesused = 0;
+ buf->dev = NULL;
+}
+
+int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf)
+{
+ return __vpu_alloc_dma(core->dev, buf);
+}
+
+static void vpu_core_check_hang(struct vpu_core *core)
+{
+ if (core->hang_mask)
+ core->state = VPU_CORE_HANG;
+}
+
+static struct vpu_core *vpu_core_find_proper_by_type(struct vpu_dev *vpu, u32 type)
+{
+ struct vpu_core *core = NULL;
+ int request_count = INT_MAX;
+ struct vpu_core *c;
+
+ list_for_each_entry(c, &vpu->cores, list) {
+ dev_dbg(c->dev, "instance_mask = 0x%lx, state = %d\n", c->instance_mask, c->state);
+ if (c->type != type)
+ continue;
+ if (c->state == VPU_CORE_DEINIT) {
+ core = c;
+ break;
+ }
+ vpu_core_check_hang(c);
+ if (c->state != VPU_CORE_ACTIVE)
+ continue;
+ if (c->request_count < request_count) {
+ request_count = c->request_count;
+ core = c;
+ }
+ if (!request_count)
+ break;
+ }
+
+ return core;
+}
+
+static bool vpu_core_is_exist(struct vpu_dev *vpu, struct vpu_core *core)
+{
+ struct vpu_core *c;
+
+ list_for_each_entry(c, &vpu->cores, list) {
+ if (c == core)
+ return true;
+ }
+
+ return false;
+}
+
+static void vpu_core_get_vpu(struct vpu_core *core)
+{
+ core->vpu->get_vpu(core->vpu);
+ if (core->type == VPU_CORE_TYPE_ENC)
+ core->vpu->get_enc(core->vpu);
+ if (core->type == VPU_CORE_TYPE_DEC)
+ core->vpu->get_dec(core->vpu);
+}
+
+static int vpu_core_register(struct device *dev, struct vpu_core *core)
+{
+ struct vpu_dev *vpu = dev_get_drvdata(dev);
+ int ret = 0;
+
+ dev_dbg(core->dev, "register core %s\n", vpu_core_type_desc(core->type));
+ if (vpu_core_is_exist(vpu, core))
+ return 0;
+
+ core->workqueue = alloc_workqueue("vpu", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ if (!core->workqueue) {
+ dev_err(core->dev, "fail to alloc workqueue\n");
+ return -ENOMEM;
+ }
+ 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);
+ 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);
+ if (ret) {
+ dev_err(core->dev, "failed init kfifo\n");
+ goto error;
+ }
+
+ list_add_tail(&core->list, &vpu->cores);
+
+ vpu_core_get_vpu(core);
+
+ if (vpu_iface_get_power_state(core))
+ ret = vpu_core_restore(core);
+ if (ret)
+ goto error;
+
+ return 0;
+error:
+ if (core->msg_buffer) {
+ vfree(core->msg_buffer);
+ core->msg_buffer = NULL;
+ }
+ if (core->workqueue) {
+ destroy_workqueue(core->workqueue);
+ core->workqueue = NULL;
+ }
+ return ret;
+}
+
+static void vpu_core_put_vpu(struct vpu_core *core)
+{
+ if (core->type == VPU_CORE_TYPE_ENC)
+ core->vpu->put_enc(core->vpu);
+ if (core->type == VPU_CORE_TYPE_DEC)
+ core->vpu->put_dec(core->vpu);
+ core->vpu->put_vpu(core->vpu);
+}
+
+static int vpu_core_unregister(struct device *dev, struct vpu_core *core)
+{
+ list_del_init(&core->list);
+
+ vpu_core_put_vpu(core);
+ core->vpu = NULL;
+ vfree(core->msg_buffer);
+ core->msg_buffer = NULL;
+
+ if (core->workqueue) {
+ cancel_work_sync(&core->msg_work);
+ cancel_delayed_work_sync(&core->msg_delayed_work);
+ destroy_workqueue(core->workqueue);
+ core->workqueue = NULL;
+ }
+
+ return 0;
+}
+
+static int vpu_core_acquire_instance(struct vpu_core *core)
+{
+ int id;
+
+ id = ffz(core->instance_mask);
+ if (id >= core->supported_instance_count)
+ return -EINVAL;
+
+ set_bit(id, &core->instance_mask);
+
+ return id;
+}
+
+static void vpu_core_release_instance(struct vpu_core *core, int id)
+{
+ if (id < 0 || id >= core->supported_instance_count)
+ return;
+
+ clear_bit(id, &core->instance_mask);
+}
+
+struct vpu_inst *vpu_inst_get(struct vpu_inst *inst)
+{
+ if (!inst)
+ return NULL;
+
+ atomic_inc(&inst->ref_count);
+
+ return inst;
+}
+
+void vpu_inst_put(struct vpu_inst *inst)
+{
+ if (!inst)
+ return;
+ if (atomic_dec_and_test(&inst->ref_count)) {
+ if (inst->release)
+ inst->release(inst);
+ }
+}
+
+struct vpu_core *vpu_request_core(struct vpu_dev *vpu, enum vpu_core_type type)
+{
+ struct vpu_core *core = NULL;
+ int ret;
+
+ mutex_lock(&vpu->lock);
+
+ core = vpu_core_find_proper_by_type(vpu, type);
+ if (!core)
+ goto exit;
+
+ mutex_lock(&core->lock);
+ pm_runtime_resume_and_get(core->dev);
+
+ if (core->state == VPU_CORE_DEINIT) {
+ ret = vpu_core_boot(core, true);
+ if (ret) {
+ pm_runtime_put_sync(core->dev);
+ mutex_unlock(&core->lock);
+ core = NULL;
+ goto exit;
+ }
+ }
+
+ core->request_count++;
+
+ mutex_unlock(&core->lock);
+exit:
+ mutex_unlock(&vpu->lock);
+
+ return core;
+}
+
+void vpu_release_core(struct vpu_core *core)
+{
+ if (!core)
+ return;
+
+ mutex_lock(&core->lock);
+ pm_runtime_put_sync(core->dev);
+ if (core->request_count)
+ core->request_count--;
+ mutex_unlock(&core->lock);
+}
+
+int vpu_inst_register(struct vpu_inst *inst)
+{
+ struct vpu_dev *vpu;
+ struct vpu_core *core;
+ int ret = 0;
+
+ vpu = inst->vpu;
+ core = inst->core;
+ if (!core) {
+ core = vpu_request_core(vpu, inst->type);
+ if (!core) {
+ dev_err(vpu->dev, "there is no vpu core for %s\n",
+ vpu_core_type_desc(inst->type));
+ return -EINVAL;
+ }
+ inst->core = core;
+ inst->dev = get_device(core->dev);
+ }
+
+ mutex_lock(&core->lock);
+ if (inst->id >= 0 && inst->id < core->supported_instance_count)
+ goto exit;
+
+ ret = vpu_core_acquire_instance(core);
+ if (ret < 0)
+ goto exit;
+
+ vpu_trace(inst->dev, "[%d] %p\n", ret, inst);
+ inst->id = ret;
+ list_add_tail(&inst->list, &core->instances);
+ ret = 0;
+ if (core->res->act_size) {
+ inst->act.phys = core->act.phys + core->res->act_size * inst->id;
+ inst->act.virt = core->act.virt + core->res->act_size * inst->id;
+ inst->act.length = core->res->act_size;
+ }
+ vpu_inst_create_dbgfs_file(inst);
+exit:
+ mutex_unlock(&core->lock);
+
+ if (ret)
+ dev_err(core->dev, "register instance fail\n");
+ return ret;
+}
+
+int vpu_inst_unregister(struct vpu_inst *inst)
+{
+ struct vpu_core *core;
+
+ if (!inst->core)
+ return 0;
+
+ core = inst->core;
+ vpu_clear_request(inst);
+ mutex_lock(&core->lock);
+ if (inst->id >= 0 && inst->id < core->supported_instance_count) {
+ vpu_inst_remove_dbgfs_file(inst);
+ list_del_init(&inst->list);
+ vpu_core_release_instance(core, inst->id);
+ inst->id = VPU_INST_NULL_ID;
+ }
+ vpu_core_check_hang(core);
+ if (core->state == VPU_CORE_HANG && !core->instance_mask) {
+ dev_info(core->dev, "reset hang core\n");
+ if (!vpu_core_sw_reset(core)) {
+ core->state = VPU_CORE_ACTIVE;
+ core->hang_mask = 0;
+ }
+ }
+ mutex_unlock(&core->lock);
+
+ return 0;
+}
+
+struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index)
+{
+ struct vpu_inst *inst = NULL;
+ struct vpu_inst *tmp;
+
+ mutex_lock(&core->lock);
+ if (index >= core->supported_instance_count || !test_bit(index, &core->instance_mask))
+ goto exit;
+ list_for_each_entry(tmp, &core->instances, list) {
+ if (tmp->id == index) {
+ inst = vpu_inst_get(tmp);
+ break;
+ }
+ }
+exit:
+ mutex_unlock(&core->lock);
+
+ return inst;
+}
+
+const struct vpu_core_resources *vpu_get_resource(struct vpu_inst *inst)
+{
+ struct vpu_dev *vpu;
+ struct vpu_core *core = NULL;
+ const struct vpu_core_resources *res = NULL;
+
+ if (!inst || !inst->vpu)
+ return NULL;
+
+ if (inst->core && inst->core->res)
+ return inst->core->res;
+
+ vpu = inst->vpu;
+ mutex_lock(&vpu->lock);
+ list_for_each_entry(core, &vpu->cores, list) {
+ if (core->type == inst->type) {
+ res = core->res;
+ break;
+ }
+ }
+ mutex_unlock(&vpu->lock);
+
+ return res;
+}
+
+static int vpu_core_parse_dt(struct vpu_core *core, struct device_node *np)
+{
+ struct device_node *node;
+ struct resource res;
+ int ret;
+
+ if (of_count_phandle_with_args(np, "memory-region", NULL) < 2) {
+ dev_err(core->dev, "need 2 memory-region for boot and rpc\n");
+ return -ENODEV;
+ }
+
+ node = of_parse_phandle(np, "memory-region", 0);
+ if (!node) {
+ dev_err(core->dev, "boot-region of_parse_phandle error\n");
+ return -ENODEV;
+ }
+ if (of_address_to_resource(node, 0, &res)) {
+ dev_err(core->dev, "boot-region of_address_to_resource error\n");
+ of_node_put(node);
+ return -EINVAL;
+ }
+ core->fw.phys = res.start;
+ core->fw.length = resource_size(&res);
+
+ of_node_put(node);
+
+ node = of_parse_phandle(np, "memory-region", 1);
+ if (!node) {
+ dev_err(core->dev, "rpc-region of_parse_phandle error\n");
+ return -ENODEV;
+ }
+ if (of_address_to_resource(node, 0, &res)) {
+ dev_err(core->dev, "rpc-region of_address_to_resource error\n");
+ of_node_put(node);
+ return -EINVAL;
+ }
+ core->rpc.phys = res.start;
+ core->rpc.length = resource_size(&res);
+
+ if (core->rpc.length < core->res->rpc_size + core->res->fwlog_size) {
+ dev_err(core->dev, "the rpc-region <%pad, 0x%x> is not enough\n",
+ &core->rpc.phys, core->rpc.length);
+ of_node_put(node);
+ return -EINVAL;
+ }
+
+ core->fw.virt = memremap(core->fw.phys, core->fw.length, MEMREMAP_WC);
+ core->rpc.virt = memremap(core->rpc.phys, core->rpc.length, MEMREMAP_WC);
+ memset(core->rpc.virt, 0, core->rpc.length);
+
+ ret = vpu_iface_check_memory_region(core, core->rpc.phys, core->rpc.length);
+ if (ret != VPU_CORE_MEMORY_UNCACHED) {
+ dev_err(core->dev, "rpc region<%pad, 0x%x> isn't uncached\n",
+ &core->rpc.phys, core->rpc.length);
+ of_node_put(node);
+ return -EINVAL;
+ }
+
+ core->log.phys = core->rpc.phys + core->res->rpc_size;
+ core->log.virt = core->rpc.virt + core->res->rpc_size;
+ core->log.length = core->res->fwlog_size;
+ core->act.phys = core->log.phys + core->log.length;
+ core->act.virt = core->log.virt + core->log.length;
+ core->act.length = core->rpc.length - core->res->rpc_size - core->log.length;
+ core->rpc.length = core->res->rpc_size;
+
+ of_node_put(node);
+
+ return 0;
+}
+
+static int vpu_core_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vpu_core *core;
+ struct vpu_dev *vpu = dev_get_drvdata(dev->parent);
+ struct vpu_shared_addr *iface;
+ u32 iface_data_size;
+ int ret;
+
+ dev_dbg(dev, "probe\n");
+ if (!vpu)
+ return -EINVAL;
+ core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+
+ core->pdev = pdev;
+ core->dev = dev;
+ platform_set_drvdata(pdev, core);
+ core->vpu = vpu;
+ INIT_LIST_HEAD(&core->instances);
+ mutex_init(&core->lock);
+ mutex_init(&core->cmd_lock);
+ init_completion(&core->cmp);
+ init_waitqueue_head(&core->ack_wq);
+ core->state = VPU_CORE_DEINIT;
+
+ core->res = of_device_get_match_data(dev);
+ if (!core->res)
+ return -ENODEV;
+
+ core->type = core->res->type;
+ core->id = of_alias_get_id(dev->of_node, "vpu_core");
+ if (core->id < 0) {
+ dev_err(dev, "can't get vpu core id\n");
+ return core->id;
+ }
+ dev_info(core->dev, "[%d] = %s\n", core->id, vpu_core_type_desc(core->type));
+ ret = vpu_core_parse_dt(core, dev->of_node);
+ if (ret)
+ return ret;
+
+ core->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(core->base))
+ return PTR_ERR(core->base);
+
+ if (!vpu_iface_check_codec(core)) {
+ dev_err(core->dev, "is not supported\n");
+ return -EINVAL;
+ }
+
+ ret = vpu_mbox_init(core);
+ if (ret)
+ return ret;
+
+ iface = devm_kzalloc(dev, sizeof(*iface), GFP_KERNEL);
+ if (!iface)
+ return -ENOMEM;
+
+ iface_data_size = vpu_iface_get_data_size(core);
+ if (iface_data_size) {
+ iface->priv = devm_kzalloc(dev, iface_data_size, GFP_KERNEL);
+ if (!iface->priv)
+ return -ENOMEM;
+ }
+
+ ret = vpu_iface_init(core, iface, &core->rpc, core->fw.phys);
+ if (ret) {
+ dev_err(core->dev, "init iface fail, ret = %d\n", ret);
+ return ret;
+ }
+
+ vpu_iface_config_system(core, vpu->res->mreg_base, vpu->base);
+ vpu_iface_set_log_buf(core, &core->log);
+
+ pm_runtime_enable(dev);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ pm_runtime_put_noidle(dev);
+ pm_runtime_set_suspended(dev);
+ goto err_runtime_disable;
+ }
+
+ ret = vpu_core_register(dev->parent, core);
+ if (ret)
+ goto err_core_register;
+ core->parent = dev->parent;
+
+ pm_runtime_put_sync(dev);
+ vpu_core_create_dbgfs_file(core);
+
+ return 0;
+
+err_core_register:
+ pm_runtime_put_sync(dev);
+err_runtime_disable:
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static int vpu_core_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vpu_core *core = platform_get_drvdata(pdev);
+ int ret;
+
+ vpu_core_remove_dbgfs_file(core);
+ ret = pm_runtime_resume_and_get(dev);
+ WARN_ON(ret < 0);
+
+ vpu_core_shutdown(core);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+
+ vpu_core_unregister(core->parent, core);
+ memunmap(core->fw.virt);
+ memunmap(core->rpc.virt);
+ mutex_destroy(&core->lock);
+ mutex_destroy(&core->cmd_lock);
+
+ return 0;
+}
+
+static int __maybe_unused vpu_core_runtime_resume(struct device *dev)
+{
+ struct vpu_core *core = dev_get_drvdata(dev);
+
+ return vpu_mbox_request(core);
+}
+
+static int __maybe_unused vpu_core_runtime_suspend(struct device *dev)
+{
+ struct vpu_core *core = dev_get_drvdata(dev);
+
+ vpu_mbox_free(core);
+ return 0;
+}
+
+static void vpu_core_cancel_work(struct vpu_core *core)
+{
+ struct vpu_inst *inst = NULL;
+
+ cancel_work_sync(&core->msg_work);
+ cancel_delayed_work_sync(&core->msg_delayed_work);
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list)
+ cancel_work_sync(&inst->msg_work);
+ mutex_unlock(&core->lock);
+}
+
+static void vpu_core_resume_work(struct vpu_core *core)
+{
+ struct vpu_inst *inst = NULL;
+ unsigned long delay = msecs_to_jiffies(10);
+
+ queue_work(core->workqueue, &core->msg_work);
+ queue_delayed_work(core->workqueue, &core->msg_delayed_work, delay);
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list)
+ queue_work(inst->workqueue, &inst->msg_work);
+ mutex_unlock(&core->lock);
+}
+
+static int __maybe_unused vpu_core_resume(struct device *dev)
+{
+ struct vpu_core *core = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ pm_runtime_resume_and_get(dev);
+ vpu_core_get_vpu(core);
+ if (core->state != VPU_CORE_SNAPSHOT)
+ goto exit;
+
+ if (!vpu_iface_get_power_state(core)) {
+ if (!list_empty(&core->instances)) {
+ ret = vpu_core_boot(core, false);
+ if (ret) {
+ dev_err(core->dev, "%s boot fail\n", __func__);
+ core->state = VPU_CORE_DEINIT;
+ goto exit;
+ }
+ } else {
+ core->state = VPU_CORE_DEINIT;
+ }
+ } else {
+ if (!list_empty(&core->instances)) {
+ ret = vpu_core_sw_reset(core);
+ if (ret) {
+ dev_err(core->dev, "%s sw_reset fail\n", __func__);
+ core->state = VPU_CORE_HANG;
+ goto exit;
+ }
+ }
+ core->state = VPU_CORE_ACTIVE;
+ }
+
+exit:
+ pm_runtime_put_sync(dev);
+ mutex_unlock(&core->lock);
+
+ vpu_core_resume_work(core);
+ return ret;
+}
+
+static int __maybe_unused vpu_core_suspend(struct device *dev)
+{
+ struct vpu_core *core = dev_get_drvdata(dev);
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ if (core->state == VPU_CORE_ACTIVE) {
+ if (!list_empty(&core->instances)) {
+ ret = vpu_core_snapshot(core);
+ if (ret) {
+ mutex_unlock(&core->lock);
+ return ret;
+ }
+ }
+
+ core->state = VPU_CORE_SNAPSHOT;
+ }
+ mutex_unlock(&core->lock);
+
+ vpu_core_cancel_work(core);
+
+ mutex_lock(&core->lock);
+ vpu_core_put_vpu(core);
+ mutex_unlock(&core->lock);
+ return ret;
+}
+
+static const struct dev_pm_ops vpu_core_pm_ops = {
+ SET_RUNTIME_PM_OPS(vpu_core_runtime_suspend, vpu_core_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(vpu_core_suspend, vpu_core_resume)
+};
+
+static struct vpu_core_resources imx8q_enc = {
+ .type = VPU_CORE_TYPE_ENC,
+ .fwname = "vpu/vpu_fw_imx8_enc.bin",
+ .stride = 16,
+ .max_width = 1920,
+ .max_height = 1920,
+ .min_width = 64,
+ .min_height = 48,
+ .step_width = 2,
+ .step_height = 2,
+ .rpc_size = 0x80000,
+ .fwlog_size = 0x80000,
+ .act_size = 0xc0000,
+};
+
+static struct vpu_core_resources imx8q_dec = {
+ .type = VPU_CORE_TYPE_DEC,
+ .fwname = "vpu/vpu_fw_imx8_dec.bin",
+ .stride = 256,
+ .max_width = 8188,
+ .max_height = 8188,
+ .min_width = 16,
+ .min_height = 16,
+ .step_width = 1,
+ .step_height = 1,
+ .rpc_size = 0x80000,
+ .fwlog_size = 0x80000,
+};
+
+static const struct of_device_id vpu_core_dt_match[] = {
+ { .compatible = "nxp,imx8q-vpu-encoder", .data = &imx8q_enc },
+ { .compatible = "nxp,imx8q-vpu-decoder", .data = &imx8q_dec },
+ {}
+};
+MODULE_DEVICE_TABLE(of, vpu_core_dt_match);
+
+static struct platform_driver amphion_vpu_core_driver = {
+ .probe = vpu_core_probe,
+ .remove = vpu_core_remove,
+ .driver = {
+ .name = "amphion-vpu-core",
+ .of_match_table = vpu_core_dt_match,
+ .pm = &vpu_core_pm_ops,
+ },
+};
+
+int __init vpu_core_driver_init(void)
+{
+ return platform_driver_register(&amphion_vpu_core_driver);
+}
+
+void __exit vpu_core_driver_exit(void)
+{
+ platform_driver_unregister(&amphion_vpu_core_driver);
+}
diff --git a/drivers/media/platform/amphion/vpu_core.h b/drivers/media/platform/amphion/vpu_core.h
new file mode 100644
index 000000000000..00a662997da4
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_core.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_CORE_H
+#define _AMPHION_VPU_CORE_H
+
+void csr_writel(struct vpu_core *core, u32 reg, u32 val);
+u32 csr_readl(struct vpu_core *core, u32 reg);
+int vpu_alloc_dma(struct vpu_core *core, struct vpu_buffer *buf);
+void vpu_free_dma(struct vpu_buffer *buf);
+struct vpu_inst *vpu_core_find_instance(struct vpu_core *core, u32 index);
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c
new file mode 100644
index 000000000000..376196bea178
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_dbg.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-device.h>
+#include <linux/debugfs.h>
+#include "vpu.h"
+#include "vpu_defs.h"
+#include "vpu_helpers.h"
+#include "vpu_cmds.h"
+#include "vpu_rpc.h"
+#include "vpu_v4l2.h"
+
+struct print_buf_desc {
+ u32 start_h_phy;
+ u32 start_h_vir;
+ u32 start_m;
+ u32 bytes;
+ u32 read;
+ u32 write;
+ char buffer[0];
+};
+
+static char *vb2_stat_name[] = {
+ [VB2_BUF_STATE_DEQUEUED] = "dequeued",
+ [VB2_BUF_STATE_IN_REQUEST] = "in_request",
+ [VB2_BUF_STATE_PREPARING] = "preparing",
+ [VB2_BUF_STATE_QUEUED] = "queued",
+ [VB2_BUF_STATE_ACTIVE] = "active",
+ [VB2_BUF_STATE_DONE] = "done",
+ [VB2_BUF_STATE_ERROR] = "error",
+};
+
+static char *vpu_stat_name[] = {
+ [VPU_BUF_STATE_IDLE] = "idle",
+ [VPU_BUF_STATE_INUSE] = "inuse",
+ [VPU_BUF_STATE_DECODED] = "decoded",
+ [VPU_BUF_STATE_READY] = "ready",
+ [VPU_BUF_STATE_SKIP] = "skip",
+ [VPU_BUF_STATE_ERROR] = "error",
+};
+
+static int vpu_dbg_instance(struct seq_file *s, void *data)
+{
+ struct vpu_inst *inst = s->private;
+ char str[128];
+ int num;
+ struct vb2_queue *vq;
+ int i;
+
+ if (!inst->fh.m2m_ctx)
+ return 0;
+ num = scnprintf(str, sizeof(str), "[%s]\n", vpu_core_type_desc(inst->type));
+ if (seq_write(s, str, num))
+ return 0;
+
+ num = scnprintf(str, sizeof(str), "tgig = %d,pid = %d\n", inst->tgid, inst->pid);
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str), "state = %d\n", inst->state);
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str),
+ "min_buffer_out = %d, min_buffer_cap = %d\n",
+ inst->min_buffer_out, inst->min_buffer_cap);
+ if (seq_write(s, str, num))
+ return 0;
+
+ vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
+ num = scnprintf(str, sizeof(str),
+ "output (%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",
+ vb2_is_streaming(vq),
+ vq->num_buffers,
+ inst->out_format.pixfmt,
+ inst->out_format.pixfmt >> 8,
+ inst->out_format.pixfmt >> 16,
+ inst->out_format.pixfmt >> 24,
+ inst->out_format.width,
+ inst->out_format.height,
+ vq->last_buffer_dequeued);
+ if (seq_write(s, str, num))
+ return 0;
+ for (i = 0; i < inst->out_format.num_planes; i++) {
+ num = scnprintf(str, sizeof(str), " %d(%d)",
+ inst->out_format.sizeimage[i],
+ inst->out_format.bytesperline[i]);
+ if (seq_write(s, str, num))
+ return 0;
+ }
+ if (seq_write(s, "\n", 1))
+ return 0;
+
+ vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
+ num = scnprintf(str, sizeof(str),
+ "capture(%2d, %2d): fmt = %c%c%c%c %d x %d, %d;",
+ vb2_is_streaming(vq),
+ vq->num_buffers,
+ inst->cap_format.pixfmt,
+ inst->cap_format.pixfmt >> 8,
+ inst->cap_format.pixfmt >> 16,
+ inst->cap_format.pixfmt >> 24,
+ inst->cap_format.width,
+ inst->cap_format.height,
+ vq->last_buffer_dequeued);
+ if (seq_write(s, str, num))
+ return 0;
+ for (i = 0; i < inst->cap_format.num_planes; i++) {
+ num = scnprintf(str, sizeof(str), " %d(%d)",
+ inst->cap_format.sizeimage[i],
+ inst->cap_format.bytesperline[i]);
+ if (seq_write(s, str, num))
+ return 0;
+ }
+ if (seq_write(s, "\n", 1))
+ return 0;
+ num = scnprintf(str, sizeof(str), "crop: (%d, %d) %d x %d\n",
+ inst->crop.left,
+ inst->crop.top,
+ inst->crop.width,
+ inst->crop.height);
+ if (seq_write(s, str, num))
+ return 0;
+
+ vq = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
+ for (i = 0; i < vq->num_buffers; i++) {
+ struct vb2_buffer *vb = vq->bufs[i];
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (vb->state == VB2_BUF_STATE_DEQUEUED)
+ continue;
+ num = scnprintf(str, sizeof(str),
+ "output [%2d] state = %10s, %8s\n",
+ i, vb2_stat_name[vb->state],
+ vpu_stat_name[vpu_get_buffer_state(vbuf)]);
+ if (seq_write(s, str, num))
+ return 0;
+ }
+
+ vq = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
+ for (i = 0; i < vq->num_buffers; i++) {
+ struct vb2_buffer *vb = vq->bufs[i];
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (vb->state == VB2_BUF_STATE_DEQUEUED)
+ continue;
+ num = scnprintf(str, sizeof(str),
+ "capture[%2d] state = %10s, %8s\n",
+ i, vb2_stat_name[vb->state],
+ vpu_stat_name[vpu_get_buffer_state(vbuf)]);
+ if (seq_write(s, str, num))
+ return 0;
+ }
+
+ num = scnprintf(str, sizeof(str), "sequence = %d\n", inst->sequence);
+ if (seq_write(s, str, num))
+ return 0;
+
+ if (inst->use_stream_buffer) {
+ num = scnprintf(str, sizeof(str), "stream_buffer = %d / %d, <%pad, 0x%x>\n",
+ vpu_helper_get_used_space(inst),
+ inst->stream_buffer.length,
+ &inst->stream_buffer.phys,
+ inst->stream_buffer.length);
+ if (seq_write(s, str, num))
+ return 0;
+ }
+ num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&inst->msg_fifo));
+ if (seq_write(s, str, num))
+ return 0;
+
+ num = scnprintf(str, sizeof(str), "flow :\n");
+ if (seq_write(s, str, num))
+ return 0;
+
+ mutex_lock(&inst->core->cmd_lock);
+ for (i = 0; i < ARRAY_SIZE(inst->flows); i++) {
+ u32 idx = (inst->flow_idx + i) % (ARRAY_SIZE(inst->flows));
+
+ if (!inst->flows[idx])
+ continue;
+ num = scnprintf(str, sizeof(str), "\t[%s]0x%x\n",
+ inst->flows[idx] >= VPU_MSG_ID_NOOP ? "M" : "C",
+ inst->flows[idx]);
+ if (seq_write(s, str, num)) {
+ mutex_unlock(&inst->core->cmd_lock);
+ return 0;
+ }
+ }
+ mutex_unlock(&inst->core->cmd_lock);
+
+ i = 0;
+ while (true) {
+ num = call_vop(inst, get_debug_info, str, sizeof(str), i++);
+ if (num <= 0)
+ break;
+ if (seq_write(s, str, num))
+ return 0;
+ }
+
+ return 0;
+}
+
+static int vpu_dbg_core(struct seq_file *s, void *data)
+{
+ struct vpu_core *core = s->private;
+ struct vpu_shared_addr *iface = core->iface;
+ char str[128];
+ int num;
+
+ num = scnprintf(str, sizeof(str), "[%s]\n", vpu_core_type_desc(core->type));
+ if (seq_write(s, str, num))
+ return 0;
+
+ num = scnprintf(str, sizeof(str), "boot_region = <%pad, 0x%x>\n",
+ &core->fw.phys, core->fw.length);
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str), "rpc_region = <%pad, 0x%x> used = 0x%x\n",
+ &core->rpc.phys, core->rpc.length, core->rpc.bytesused);
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str), "fwlog_region = <%pad, 0x%x>\n",
+ &core->log.phys, core->log.length);
+ if (seq_write(s, str, num))
+ return 0;
+
+ num = scnprintf(str, sizeof(str), "state = %d\n", core->state);
+ if (seq_write(s, str, num))
+ return 0;
+ if (core->state == VPU_CORE_DEINIT)
+ return 0;
+ num = scnprintf(str, sizeof(str), "fw version = %d.%d.%d\n",
+ (core->fw_version >> 16) & 0xff,
+ (core->fw_version >> 8) & 0xff,
+ core->fw_version & 0xff);
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str), "instances = %d/%d (0x%02lx), %d\n",
+ hweight32(core->instance_mask),
+ core->supported_instance_count,
+ core->instance_mask,
+ core->request_count);
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str), "kfifo len = 0x%x\n", kfifo_len(&core->msg_fifo));
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str),
+ "cmd_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n",
+ iface->cmd_desc->start,
+ iface->cmd_desc->end,
+ iface->cmd_desc->wptr,
+ iface->cmd_desc->rptr);
+ if (seq_write(s, str, num))
+ return 0;
+ num = scnprintf(str, sizeof(str),
+ "msg_buf:[0x%x, 0x%x], wptr = 0x%x, rptr = 0x%x\n",
+ iface->msg_desc->start,
+ iface->msg_desc->end,
+ iface->msg_desc->wptr,
+ iface->msg_desc->rptr);
+ if (seq_write(s, str, num))
+ return 0;
+
+ return 0;
+}
+
+static int vpu_dbg_fwlog(struct seq_file *s, void *data)
+{
+ struct vpu_core *core = s->private;
+ struct print_buf_desc *print_buf;
+ int length;
+ u32 rptr;
+ u32 wptr;
+ int ret = 0;
+
+ if (!core->log.virt || core->state == VPU_CORE_DEINIT)
+ return 0;
+
+ print_buf = core->log.virt;
+ rptr = print_buf->read;
+ wptr = print_buf->write;
+
+ if (rptr == wptr)
+ return 0;
+ else if (rptr < wptr)
+ length = wptr - rptr;
+ else
+ length = print_buf->bytes + wptr - rptr;
+
+ if (s->count + length >= s->size) {
+ s->count = s->size;
+ return 0;
+ }
+
+ if (rptr + length >= print_buf->bytes) {
+ int num = print_buf->bytes - rptr;
+
+ if (seq_write(s, print_buf->buffer + rptr, num))
+ ret = -1;
+ length -= num;
+ rptr = 0;
+ }
+
+ if (length) {
+ if (seq_write(s, print_buf->buffer + rptr, length))
+ ret = -1;
+ rptr += length;
+ }
+ if (!ret)
+ print_buf->read = rptr;
+
+ return 0;
+}
+
+static int vpu_dbg_inst_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, vpu_dbg_instance, inode->i_private);
+}
+
+static ssize_t vpu_dbg_inst_write(struct file *file,
+ const char __user *user_buf, size_t size, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct vpu_inst *inst = s->private;
+
+ vpu_session_debug(inst);
+
+ return size;
+}
+
+static ssize_t vpu_dbg_core_write(struct file *file,
+ const char __user *user_buf, size_t size, loff_t *ppos)
+{
+ struct seq_file *s = file->private_data;
+ struct vpu_core *core = s->private;
+
+ pm_runtime_resume_and_get(core->dev);
+ mutex_lock(&core->lock);
+ if (core->state != VPU_CORE_DEINIT && !core->instance_mask) {
+ dev_info(core->dev, "reset\n");
+ if (!vpu_core_sw_reset(core)) {
+ core->state = VPU_CORE_ACTIVE;
+ core->hang_mask = 0;
+ }
+ }
+ mutex_unlock(&core->lock);
+ pm_runtime_put_sync(core->dev);
+
+ return size;
+}
+
+static int vpu_dbg_core_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, vpu_dbg_core, inode->i_private);
+}
+
+static int vpu_dbg_fwlog_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, vpu_dbg_fwlog, inode->i_private);
+}
+
+static const struct file_operations vpu_dbg_inst_fops = {
+ .owner = THIS_MODULE,
+ .open = vpu_dbg_inst_open,
+ .release = single_release,
+ .read = seq_read,
+ .write = vpu_dbg_inst_write,
+};
+
+static const struct file_operations vpu_dbg_core_fops = {
+ .owner = THIS_MODULE,
+ .open = vpu_dbg_core_open,
+ .release = single_release,
+ .read = seq_read,
+ .write = vpu_dbg_core_write,
+};
+
+static const struct file_operations vpu_dbg_fwlog_fops = {
+ .owner = THIS_MODULE,
+ .open = vpu_dbg_fwlog_open,
+ .release = single_release,
+ .read = seq_read,
+};
+
+int vpu_inst_create_dbgfs_file(struct vpu_inst *inst)
+{
+ struct vpu_dev *vpu;
+ char name[64];
+
+ if (!inst || !inst->core || !inst->core->vpu)
+ return -EINVAL;
+
+ vpu = inst->core->vpu;
+ if (!vpu->debugfs)
+ return -EINVAL;
+
+ if (inst->debugfs)
+ return 0;
+
+ scnprintf(name, sizeof(name), "instance.%d.%d", inst->core->id, inst->id);
+ inst->debugfs = debugfs_create_file((const char *)name,
+ VERIFY_OCTAL_PERMISSIONS(0644),
+ vpu->debugfs,
+ inst,
+ &vpu_dbg_inst_fops);
+ if (!inst->debugfs) {
+ dev_err(inst->dev, "vpu create debugfs %s fail\n", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vpu_inst_remove_dbgfs_file(struct vpu_inst *inst)
+{
+ if (!inst)
+ return 0;
+
+ debugfs_remove(inst->debugfs);
+ inst->debugfs = NULL;
+
+ return 0;
+}
+
+int vpu_core_create_dbgfs_file(struct vpu_core *core)
+{
+ struct vpu_dev *vpu;
+ char name[64];
+
+ if (!core || !core->vpu)
+ return -EINVAL;
+
+ vpu = core->vpu;
+ if (!vpu->debugfs)
+ return -EINVAL;
+
+ if (!core->debugfs) {
+ scnprintf(name, sizeof(name), "core.%d", core->id);
+ core->debugfs = debugfs_create_file((const char *)name,
+ VERIFY_OCTAL_PERMISSIONS(0644),
+ vpu->debugfs,
+ core,
+ &vpu_dbg_core_fops);
+ if (!core->debugfs) {
+ dev_err(core->dev, "vpu create debugfs %s fail\n", name);
+ return -EINVAL;
+ }
+ }
+ if (!core->debugfs_fwlog) {
+ scnprintf(name, sizeof(name), "fwlog.%d", core->id);
+ core->debugfs_fwlog = debugfs_create_file((const char *)name,
+ VERIFY_OCTAL_PERMISSIONS(0444),
+ vpu->debugfs,
+ core,
+ &vpu_dbg_fwlog_fops);
+ if (!core->debugfs_fwlog) {
+ dev_err(core->dev, "vpu create debugfs %s fail\n", name);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int vpu_core_remove_dbgfs_file(struct vpu_core *core)
+{
+ if (!core)
+ return 0;
+ debugfs_remove(core->debugfs);
+ core->debugfs = NULL;
+ debugfs_remove(core->debugfs_fwlog);
+ core->debugfs_fwlog = NULL;
+
+ return 0;
+}
+
+void vpu_inst_record_flow(struct vpu_inst *inst, u32 flow)
+{
+ if (!inst)
+ return;
+
+ inst->flows[inst->flow_idx] = flow;
+ inst->flow_idx = (inst->flow_idx + 1) % (ARRAY_SIZE(inst->flows));
+}
diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h
new file mode 100644
index 000000000000..282664202dcf
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_defs.h
@@ -0,0 +1,187 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_DEFS_H
+#define _AMPHION_VPU_DEFS_H
+
+enum MSG_TYPE {
+ INIT_DONE = 1,
+ PRC_BUF_OFFSET,
+ BOOT_ADDRESS,
+ COMMAND,
+ EVENT,
+};
+
+enum {
+ VPU_IRQ_CODE_BOOT_DONE = 0x55,
+ VPU_IRQ_CODE_SNAPSHOT_DONE = 0xa5,
+ VPU_IRQ_CODE_SYNC = 0xaa,
+};
+
+enum {
+ VPU_CMD_ID_NOOP = 0x0,
+ VPU_CMD_ID_CONFIGURE_CODEC,
+ VPU_CMD_ID_START,
+ VPU_CMD_ID_STOP,
+ VPU_CMD_ID_ABORT,
+ VPU_CMD_ID_RST_BUF,
+ VPU_CMD_ID_SNAPSHOT,
+ VPU_CMD_ID_FIRM_RESET,
+ VPU_CMD_ID_UPDATE_PARAMETER,
+ VPU_CMD_ID_FRAME_ENCODE,
+ VPU_CMD_ID_SKIP,
+ VPU_CMD_ID_PARSE_NEXT_SEQ,
+ VPU_CMD_ID_PARSE_NEXT_I,
+ VPU_CMD_ID_PARSE_NEXT_IP,
+ VPU_CMD_ID_PARSE_NEXT_ANY,
+ VPU_CMD_ID_DEC_PIC,
+ VPU_CMD_ID_FS_ALLOC,
+ VPU_CMD_ID_FS_RELEASE,
+ VPU_CMD_ID_TIMESTAMP,
+ VPU_CMD_ID_DEBUG
+};
+
+enum {
+ VPU_MSG_ID_NOOP = 0x100,
+ VPU_MSG_ID_RESET_DONE,
+ VPU_MSG_ID_START_DONE,
+ VPU_MSG_ID_STOP_DONE,
+ VPU_MSG_ID_ABORT_DONE,
+ VPU_MSG_ID_BUF_RST,
+ VPU_MSG_ID_MEM_REQUEST,
+ VPU_MSG_ID_PARAM_UPD_DONE,
+ VPU_MSG_ID_FRAME_INPUT_DONE,
+ VPU_MSG_ID_ENC_DONE,
+ VPU_MSG_ID_DEC_DONE,
+ VPU_MSG_ID_FRAME_REQ,
+ VPU_MSG_ID_FRAME_RELEASE,
+ VPU_MSG_ID_SEQ_HDR_FOUND,
+ VPU_MSG_ID_RES_CHANGE,
+ VPU_MSG_ID_PIC_HDR_FOUND,
+ VPU_MSG_ID_PIC_DECODED,
+ VPU_MSG_ID_PIC_EOS,
+ VPU_MSG_ID_FIFO_LOW,
+ VPU_MSG_ID_FIFO_HIGH,
+ VPU_MSG_ID_FIFO_EMPTY,
+ VPU_MSG_ID_FIFO_FULL,
+ VPU_MSG_ID_BS_ERROR,
+ VPU_MSG_ID_UNSUPPORTED,
+ VPU_MSG_ID_TIMESTAMP_INFO,
+
+ VPU_MSG_ID_FIRMWARE_XCPT,
+};
+
+enum VPU_ENC_MEMORY_RESOURSE {
+ MEM_RES_ENC,
+ MEM_RES_REF,
+ MEM_RES_ACT
+};
+
+enum VPU_DEC_MEMORY_RESOURCE {
+ MEM_RES_FRAME,
+ MEM_RES_MBI,
+ MEM_RES_DCP
+};
+
+enum VPU_SCODE_TYPE {
+ SCODE_PADDING_EOS = 1,
+ SCODE_PADDING_BUFFLUSH = 2,
+ SCODE_PADDING_ABORT = 3,
+ SCODE_SEQUENCE = 0x31,
+ SCODE_PICTURE = 0x32,
+ SCODE_SLICE = 0x33
+};
+
+struct vpu_pkt_mem_req_data {
+ u32 enc_frame_size;
+ u32 enc_frame_num;
+ u32 ref_frame_size;
+ u32 ref_frame_num;
+ u32 act_buf_size;
+ u32 act_buf_num;
+};
+
+struct vpu_enc_pic_info {
+ u32 frame_id;
+ u32 pic_type;
+ u32 skipped_frame;
+ u32 error_flag;
+ u32 psnr;
+ u32 frame_size;
+ u32 wptr;
+ u32 crc;
+ s64 timestamp;
+};
+
+struct vpu_dec_codec_info {
+ u32 pixfmt;
+ u32 num_ref_frms;
+ u32 num_dpb_frms;
+ u32 num_dfe_area;
+ u32 color_primaries;
+ u32 transfer_chars;
+ u32 matrix_coeffs;
+ u32 full_range;
+ u32 vui_present;
+ u32 progressive;
+ u32 width;
+ u32 height;
+ u32 decoded_width;
+ u32 decoded_height;
+ struct v4l2_fract frame_rate;
+ u32 dsp_asp_ratio;
+ u32 level_idc;
+ u32 bit_depth_luma;
+ u32 bit_depth_chroma;
+ u32 chroma_fmt;
+ u32 mvc_num_views;
+ u32 offset_x;
+ u32 offset_y;
+ u32 tag;
+ u32 sizeimage[VIDEO_MAX_PLANES];
+ u32 bytesperline[VIDEO_MAX_PLANES];
+ u32 mbi_size;
+ u32 dcp_size;
+ u32 stride;
+};
+
+struct vpu_dec_pic_info {
+ u32 id;
+ u32 luma;
+ u32 start;
+ u32 end;
+ u32 pic_size;
+ u32 stride;
+ u32 skipped;
+ s64 timestamp;
+ u32 consumed_count;
+};
+
+struct vpu_fs_info {
+ u32 id;
+ u32 type;
+ u32 tag;
+ u32 luma_addr;
+ u32 luma_size;
+ u32 chroma_addr;
+ u32 chromau_size;
+ u32 chromav_addr;
+ u32 chromav_size;
+ u32 bytesperline;
+ u32 not_displayed;
+};
+
+struct vpu_ts_info {
+ s64 timestamp;
+ u32 size;
+};
+
+#define BITRATE_STEP (1024)
+#define BITRATE_MIN (16 * BITRATE_STEP)
+#define BITRATE_MAX (240 * 1024 * BITRATE_STEP)
+#define BITRATE_DEFAULT (2 * 1024 * BITRATE_STEP)
+#define BITRATE_DEFAULT_PEAK (BITRATE_DEFAULT * 2)
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c
new file mode 100644
index 000000000000..9d5a5075343d
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_drv.c
@@ -0,0 +1,261 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dma-map-ops.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <linux/of_reserved_mem.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/debugfs.h>
+#include "vpu.h"
+#include "vpu_imx8q.h"
+
+bool debug;
+module_param(debug, bool, 0644);
+
+void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val)
+{
+ writel(val, vpu->base + reg);
+}
+
+u32 vpu_readl(struct vpu_dev *vpu, u32 reg)
+{
+ return readl(vpu->base + reg);
+}
+
+static void vpu_dev_get(struct vpu_dev *vpu)
+{
+ if (atomic_inc_return(&vpu->ref_vpu) == 1 && vpu->res->setup)
+ vpu->res->setup(vpu);
+}
+
+static void vpu_dev_put(struct vpu_dev *vpu)
+{
+ atomic_dec(&vpu->ref_vpu);
+}
+
+static void vpu_enc_get(struct vpu_dev *vpu)
+{
+ if (atomic_inc_return(&vpu->ref_enc) == 1 && vpu->res->setup_encoder)
+ vpu->res->setup_encoder(vpu);
+}
+
+static void vpu_enc_put(struct vpu_dev *vpu)
+{
+ atomic_dec(&vpu->ref_enc);
+}
+
+static void vpu_dec_get(struct vpu_dev *vpu)
+{
+ if (atomic_inc_return(&vpu->ref_dec) == 1 && vpu->res->setup_decoder)
+ vpu->res->setup_decoder(vpu);
+}
+
+static void vpu_dec_put(struct vpu_dev *vpu)
+{
+ atomic_dec(&vpu->ref_dec);
+}
+
+static int vpu_init_media_device(struct vpu_dev *vpu)
+{
+ vpu->mdev.dev = vpu->dev;
+ strscpy(vpu->mdev.model, "amphion-vpu", sizeof(vpu->mdev.model));
+ strscpy(vpu->mdev.bus_info, "platform: amphion-vpu", sizeof(vpu->mdev.bus_info));
+ media_device_init(&vpu->mdev);
+ vpu->v4l2_dev.mdev = &vpu->mdev;
+
+ return 0;
+}
+
+static int vpu_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vpu_dev *vpu;
+ int ret;
+
+ dev_dbg(dev, "probe\n");
+ vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL);
+ if (!vpu)
+ return -ENOMEM;
+
+ vpu->pdev = pdev;
+ vpu->dev = dev;
+ mutex_init(&vpu->lock);
+ INIT_LIST_HEAD(&vpu->cores);
+ platform_set_drvdata(pdev, vpu);
+ atomic_set(&vpu->ref_vpu, 0);
+ atomic_set(&vpu->ref_enc, 0);
+ atomic_set(&vpu->ref_dec, 0);
+ vpu->get_vpu = vpu_dev_get;
+ vpu->put_vpu = vpu_dev_put;
+ vpu->get_enc = vpu_enc_get;
+ vpu->put_enc = vpu_enc_put;
+ vpu->get_dec = vpu_dec_get;
+ vpu->put_dec = vpu_dec_put;
+
+ vpu->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(vpu->base))
+ return PTR_ERR(vpu->base);
+
+ vpu->res = of_device_get_match_data(dev);
+ if (!vpu->res)
+ return -ENODEV;
+
+ pm_runtime_enable(dev);
+
+ ret = v4l2_device_register(dev, &vpu->v4l2_dev);
+ if (ret)
+ goto err_vpu_deinit;
+
+ vpu_init_media_device(vpu);
+ vpu->encoder.type = VPU_CORE_TYPE_ENC;
+ vpu->encoder.function = MEDIA_ENT_F_PROC_VIDEO_ENCODER;
+ vpu->decoder.type = VPU_CORE_TYPE_DEC;
+ vpu->decoder.function = MEDIA_ENT_F_PROC_VIDEO_DECODER;
+ ret = vpu_add_func(vpu, &vpu->decoder);
+ if (ret)
+ goto err_add_decoder;
+ ret = vpu_add_func(vpu, &vpu->encoder);
+ if (ret)
+ goto err_add_encoder;
+ ret = media_device_register(&vpu->mdev);
+ if (ret)
+ goto err_vpu_media;
+ vpu->debugfs = debugfs_create_dir("amphion_vpu", NULL);
+
+ of_platform_populate(dev->of_node, NULL, NULL, dev);
+
+ return 0;
+
+err_vpu_media:
+ vpu_remove_func(&vpu->encoder);
+err_add_encoder:
+ vpu_remove_func(&vpu->decoder);
+err_add_decoder:
+ media_device_cleanup(&vpu->mdev);
+ v4l2_device_unregister(&vpu->v4l2_dev);
+err_vpu_deinit:
+ pm_runtime_set_suspended(dev);
+ pm_runtime_disable(dev);
+
+ return ret;
+}
+
+static int vpu_remove(struct platform_device *pdev)
+{
+ struct vpu_dev *vpu = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ debugfs_remove_recursive(vpu->debugfs);
+ vpu->debugfs = NULL;
+
+ pm_runtime_disable(dev);
+
+ media_device_unregister(&vpu->mdev);
+ vpu_remove_func(&vpu->decoder);
+ vpu_remove_func(&vpu->encoder);
+ media_device_cleanup(&vpu->mdev);
+ v4l2_device_unregister(&vpu->v4l2_dev);
+ mutex_destroy(&vpu->lock);
+
+ return 0;
+}
+
+static int __maybe_unused vpu_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused vpu_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused vpu_resume(struct device *dev)
+{
+ return 0;
+}
+
+static int __maybe_unused vpu_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops vpu_pm_ops = {
+ SET_RUNTIME_PM_OPS(vpu_runtime_suspend, vpu_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(vpu_suspend, vpu_resume)
+};
+
+static struct vpu_resources imx8qxp_res = {
+ .plat_type = IMX8QXP,
+ .mreg_base = 0x40000000,
+ .setup = vpu_imx8q_setup,
+ .setup_encoder = vpu_imx8q_setup_enc,
+ .setup_decoder = vpu_imx8q_setup_dec,
+ .reset = vpu_imx8q_reset
+};
+
+static struct vpu_resources imx8qm_res = {
+ .plat_type = IMX8QM,
+ .mreg_base = 0x40000000,
+ .setup = vpu_imx8q_setup,
+ .setup_encoder = vpu_imx8q_setup_enc,
+ .setup_decoder = vpu_imx8q_setup_dec,
+ .reset = vpu_imx8q_reset
+};
+
+static const struct of_device_id vpu_dt_match[] = {
+ { .compatible = "nxp,imx8qxp-vpu", .data = &imx8qxp_res },
+ { .compatible = "nxp,imx8qm-vpu", .data = &imx8qm_res },
+ {}
+};
+MODULE_DEVICE_TABLE(of, vpu_dt_match);
+
+static struct platform_driver amphion_vpu_driver = {
+ .probe = vpu_probe,
+ .remove = vpu_remove,
+ .driver = {
+ .name = "amphion-vpu",
+ .of_match_table = vpu_dt_match,
+ .pm = &vpu_pm_ops,
+ },
+};
+
+static int __init vpu_driver_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&amphion_vpu_driver);
+ if (ret)
+ return ret;
+
+ return vpu_core_driver_init();
+}
+
+static void __exit vpu_driver_exit(void)
+{
+ vpu_core_driver_exit();
+ platform_driver_unregister(&amphion_vpu_driver);
+}
+module_init(vpu_driver_init);
+module_exit(vpu_driver_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Linux VPU driver for Freescale i.MX8Q");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/amphion/vpu_helpers.c b/drivers/media/platform/amphion/vpu_helpers.c
new file mode 100644
index 000000000000..e9aeb3453dfc
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_helpers.c
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "vpu.h"
+#include "vpu_core.h"
+#include "vpu_rpc.h"
+#include "vpu_helpers.h"
+
+int vpu_helper_find_in_array_u8(const u8 *array, u32 size, u32 x)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (array[i] == x)
+ return i;
+ }
+
+ return 0;
+}
+
+bool vpu_helper_check_type(struct vpu_inst *inst, u32 type)
+{
+ const struct vpu_format *pfmt;
+
+ for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) {
+ if (!vpu_iface_check_format(inst, pfmt->pixfmt))
+ continue;
+ if (pfmt->type == type)
+ return true;
+ }
+
+ return false;
+}
+
+const struct vpu_format *vpu_helper_find_format(struct vpu_inst *inst, u32 type, u32 pixelfmt)
+{
+ const struct vpu_format *pfmt;
+
+ if (!inst || !inst->formats)
+ return NULL;
+
+ if (!vpu_iface_check_format(inst, pixelfmt))
+ return NULL;
+
+ for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) {
+ if (pfmt->pixfmt == pixelfmt && (!type || type == pfmt->type))
+ return pfmt;
+ }
+
+ return NULL;
+}
+
+const struct vpu_format *vpu_helper_enum_format(struct vpu_inst *inst, u32 type, int index)
+{
+ const struct vpu_format *pfmt;
+ int i = 0;
+
+ if (!inst || !inst->formats)
+ return NULL;
+
+ for (pfmt = inst->formats; pfmt->pixfmt; pfmt++) {
+ if (!vpu_iface_check_format(inst, pfmt->pixfmt))
+ continue;
+
+ if (pfmt->type == type) {
+ if (index == i)
+ return pfmt;
+ i++;
+ }
+ }
+
+ return NULL;
+}
+
+u32 vpu_helper_valid_frame_width(struct vpu_inst *inst, u32 width)
+{
+ const struct vpu_core_resources *res;
+
+ if (!inst)
+ return width;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return width;
+ if (res->max_width)
+ width = clamp(width, res->min_width, res->max_width);
+ if (res->step_width)
+ width = ALIGN(width, res->step_width);
+
+ return width;
+}
+
+u32 vpu_helper_valid_frame_height(struct vpu_inst *inst, u32 height)
+{
+ const struct vpu_core_resources *res;
+
+ if (!inst)
+ return height;
+
+ res = vpu_get_resource(inst);
+ if (!res)
+ return height;
+ if (res->max_height)
+ height = clamp(height, res->min_height, res->max_height);
+ if (res->step_height)
+ height = ALIGN(height, res->step_height);
+
+ return height;
+}
+
+static u32 get_nv12_plane_size(u32 width, u32 height, int plane_no,
+ u32 stride, u32 interlaced, u32 *pbl)
+{
+ u32 bytesperline;
+ u32 size = 0;
+
+ bytesperline = ALIGN(width, stride);
+ if (pbl)
+ bytesperline = max(bytesperline, *pbl);
+ height = ALIGN(height, 2);
+ if (plane_no == 0)
+ size = bytesperline * height;
+ else if (plane_no == 1)
+ size = bytesperline * height >> 1;
+ if (pbl)
+ *pbl = bytesperline;
+
+ return size;
+}
+
+static u32 get_tiled_8l128_plane_size(u32 fmt, u32 width, u32 height, int plane_no,
+ u32 stride, u32 interlaced, u32 *pbl)
+{
+ u32 ws = 3;
+ u32 hs = 7;
+ u32 bitdepth = 8;
+ u32 bytesperline;
+ u32 size = 0;
+
+ if (interlaced)
+ hs++;
+ if (fmt == V4L2_PIX_FMT_NV12M_10BE_8L128)
+ bitdepth = 10;
+ bytesperline = DIV_ROUND_UP(width * bitdepth, BITS_PER_BYTE);
+ bytesperline = ALIGN(bytesperline, 1 << ws);
+ bytesperline = ALIGN(bytesperline, stride);
+ if (pbl)
+ bytesperline = max(bytesperline, *pbl);
+ height = ALIGN(height, 1 << hs);
+ if (plane_no == 0)
+ size = bytesperline * height;
+ else if (plane_no == 1)
+ size = (bytesperline * ALIGN(height, 1 << (hs + 1))) >> 1;
+ if (pbl)
+ *pbl = bytesperline;
+
+ return size;
+}
+
+static u32 get_default_plane_size(u32 width, u32 height, int plane_no,
+ u32 stride, u32 interlaced, u32 *pbl)
+{
+ u32 bytesperline;
+ u32 size = 0;
+
+ bytesperline = ALIGN(width, stride);
+ if (pbl)
+ bytesperline = max(bytesperline, *pbl);
+ if (plane_no == 0)
+ size = bytesperline * height;
+ if (pbl)
+ *pbl = bytesperline;
+
+ return size;
+}
+
+u32 vpu_helper_get_plane_size(u32 fmt, u32 w, u32 h, int plane_no,
+ u32 stride, u32 interlaced, u32 *pbl)
+{
+ switch (fmt) {
+ case V4L2_PIX_FMT_NV12M:
+ return get_nv12_plane_size(w, h, plane_no, stride, interlaced, pbl);
+ case V4L2_PIX_FMT_NV12M_8L128:
+ case V4L2_PIX_FMT_NV12M_10BE_8L128:
+ return get_tiled_8l128_plane_size(fmt, w, h, plane_no, stride, interlaced, pbl);
+ default:
+ return get_default_plane_size(w, h, plane_no, stride, interlaced, pbl);
+ }
+}
+
+int vpu_helper_copy_from_stream_buffer(struct vpu_buffer *stream_buffer,
+ u32 *rptr, u32 size, void *dst)
+{
+ u32 offset;
+ u32 start;
+ u32 end;
+ void *virt;
+
+ if (!stream_buffer || !rptr || !dst)
+ return -EINVAL;
+
+ if (!size)
+ return 0;
+
+ offset = *rptr;
+ start = stream_buffer->phys;
+ end = start + stream_buffer->length;
+ virt = stream_buffer->virt;
+
+ if (offset < start || offset > end)
+ return -EINVAL;
+
+ if (offset + size <= end) {
+ memcpy(dst, virt + (offset - start), size);
+ } else {
+ memcpy(dst, virt + (offset - start), end - offset);
+ memcpy(dst + end - offset, virt, size + offset - end);
+ }
+
+ *rptr = vpu_helper_step_walk(stream_buffer, offset, size);
+
+ return 0;
+}
+
+int vpu_helper_copy_to_stream_buffer(struct vpu_buffer *stream_buffer,
+ u32 *wptr, u32 size, void *src)
+{
+ u32 offset;
+ u32 start;
+ u32 end;
+ void *virt;
+
+ if (!stream_buffer || !wptr || !src)
+ return -EINVAL;
+
+ if (!size)
+ return 0;
+
+ offset = *wptr;
+ start = stream_buffer->phys;
+ end = start + stream_buffer->length;
+ virt = stream_buffer->virt;
+ if (offset < start || offset > end)
+ return -EINVAL;
+
+ if (offset + size <= end) {
+ memcpy(virt + (offset - start), src, size);
+ } else {
+ memcpy(virt + (offset - start), src, end - offset);
+ memcpy(virt, src + end - offset, size + offset - end);
+ }
+
+ *wptr = vpu_helper_step_walk(stream_buffer, offset, size);
+
+ return 0;
+}
+
+int vpu_helper_memset_stream_buffer(struct vpu_buffer *stream_buffer,
+ u32 *wptr, u8 val, u32 size)
+{
+ u32 offset;
+ u32 start;
+ u32 end;
+ void *virt;
+
+ if (!stream_buffer || !wptr)
+ return -EINVAL;
+
+ if (!size)
+ return 0;
+
+ offset = *wptr;
+ start = stream_buffer->phys;
+ end = start + stream_buffer->length;
+ virt = stream_buffer->virt;
+ if (offset < start || offset > end)
+ return -EINVAL;
+
+ if (offset + size <= end) {
+ memset(virt + (offset - start), val, size);
+ } else {
+ memset(virt + (offset - start), val, end - offset);
+ memset(virt, val, size + offset - end);
+ }
+
+ offset += size;
+ if (offset >= end)
+ offset -= stream_buffer->length;
+
+ *wptr = offset;
+
+ return 0;
+}
+
+u32 vpu_helper_get_free_space(struct vpu_inst *inst)
+{
+ struct vpu_rpc_buffer_desc desc;
+
+ if (vpu_iface_get_stream_buffer_desc(inst, &desc))
+ return 0;
+
+ if (desc.rptr > desc.wptr)
+ return desc.rptr - desc.wptr;
+ else if (desc.rptr < desc.wptr)
+ return (desc.end - desc.start + desc.rptr - desc.wptr);
+ else
+ return desc.end - desc.start;
+}
+
+u32 vpu_helper_get_used_space(struct vpu_inst *inst)
+{
+ struct vpu_rpc_buffer_desc desc;
+
+ if (vpu_iface_get_stream_buffer_desc(inst, &desc))
+ return 0;
+
+ if (desc.wptr > desc.rptr)
+ return desc.wptr - desc.rptr;
+ else if (desc.wptr < desc.rptr)
+ return (desc.end - desc.start + desc.wptr - desc.rptr);
+ else
+ return 0;
+}
+
+int vpu_helper_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct vpu_inst *inst = ctrl_to_inst(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
+ ctrl->val = inst->min_buffer_cap;
+ break;
+ case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
+ ctrl->val = inst->min_buffer_out;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int vpu_helper_find_startcode(struct vpu_buffer *stream_buffer,
+ u32 pixelformat, u32 offset, u32 bytesused)
+{
+ u32 start_code;
+ int start_code_size;
+ u32 val = 0;
+ int i;
+ int ret = -EINVAL;
+
+ if (!stream_buffer || !stream_buffer->virt)
+ return -EINVAL;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_H264:
+ start_code_size = 4;
+ start_code = 0x00000001;
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < bytesused; i++) {
+ val = (val << 8) | vpu_helper_read_byte(stream_buffer, offset + i);
+ if (i < start_code_size - 1)
+ continue;
+ if (val == start_code) {
+ ret = i + 1 - start_code_size;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src)
+{
+ u32 i;
+
+ if (!pairs || !cnt)
+ return -EINVAL;
+
+ for (i = 0; i < cnt; i++) {
+ if (pairs[i].src == src)
+ return pairs[i].dst;
+ }
+
+ return -EINVAL;
+}
+
+int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst)
+{
+ u32 i;
+
+ if (!pairs || !cnt)
+ return -EINVAL;
+
+ for (i = 0; i < cnt; i++) {
+ if (pairs[i].dst == dst)
+ return pairs[i].src;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/media/platform/amphion/vpu_helpers.h b/drivers/media/platform/amphion/vpu_helpers.h
new file mode 100644
index 000000000000..bc28350958be
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_helpers.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_HELPERS_H
+#define _AMPHION_VPU_HELPERS_H
+
+struct vpu_pair {
+ u32 src;
+ u32 dst;
+};
+
+int vpu_helper_find_in_array_u8(const u8 *array, u32 size, u32 x);
+bool vpu_helper_check_type(struct vpu_inst *inst, u32 type);
+const struct vpu_format *vpu_helper_find_format(struct vpu_inst *inst, u32 type, u32 pixelfmt);
+const struct vpu_format *vpu_helper_enum_format(struct vpu_inst *inst, u32 type, int index);
+u32 vpu_helper_valid_frame_width(struct vpu_inst *inst, u32 width);
+u32 vpu_helper_valid_frame_height(struct vpu_inst *inst, u32 height);
+u32 vpu_helper_get_plane_size(u32 fmt, u32 width, u32 height, int plane_no,
+ u32 stride, u32 interlaced, u32 *pbl);
+int vpu_helper_copy_from_stream_buffer(struct vpu_buffer *stream_buffer,
+ u32 *rptr, u32 size, void *dst);
+int vpu_helper_copy_to_stream_buffer(struct vpu_buffer *stream_buffer,
+ u32 *wptr, u32 size, void *src);
+int vpu_helper_memset_stream_buffer(struct vpu_buffer *stream_buffer,
+ u32 *wptr, u8 val, u32 size);
+u32 vpu_helper_get_free_space(struct vpu_inst *inst);
+u32 vpu_helper_get_used_space(struct vpu_inst *inst);
+int vpu_helper_g_volatile_ctrl(struct v4l2_ctrl *ctrl);
+void vpu_helper_get_kmp_next(const u8 *pattern, int *next, int size);
+int vpu_helper_kmp_search(u8 *s, int s_len, const u8 *p, int p_len, int *next);
+int vpu_helper_kmp_search_in_stream_buffer(struct vpu_buffer *stream_buffer,
+ u32 offset, int bytesused,
+ const u8 *p, int p_len, int *next);
+int vpu_helper_find_startcode(struct vpu_buffer *stream_buffer,
+ u32 pixelformat, u32 offset, u32 bytesused);
+
+static inline u32 vpu_helper_step_walk(struct vpu_buffer *stream_buffer, u32 pos, u32 step)
+{
+ pos += step;
+ if (pos > stream_buffer->phys + stream_buffer->length)
+ pos -= stream_buffer->length;
+
+ return pos;
+}
+
+static inline u8 vpu_helper_read_byte(struct vpu_buffer *stream_buffer, u32 pos)
+{
+ u8 *pdata = (u8 *)stream_buffer->virt;
+
+ return pdata[pos % stream_buffer->length];
+}
+
+int vpu_color_check_primaries(u32 primaries);
+int vpu_color_check_transfers(u32 transfers);
+int vpu_color_check_matrix(u32 matrix);
+int vpu_color_check_full_range(u32 full_range);
+u32 vpu_color_cvrt_primaries_v2i(u32 primaries);
+u32 vpu_color_cvrt_primaries_i2v(u32 primaries);
+u32 vpu_color_cvrt_transfers_v2i(u32 transfers);
+u32 vpu_color_cvrt_transfers_i2v(u32 transfers);
+u32 vpu_color_cvrt_matrix_v2i(u32 matrix);
+u32 vpu_color_cvrt_matrix_i2v(u32 matrix);
+u32 vpu_color_cvrt_full_range_v2i(u32 full_range);
+u32 vpu_color_cvrt_full_range_i2v(u32 full_range);
+int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range);
+
+int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src);
+int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst);
+#endif
diff --git a/drivers/media/platform/amphion/vpu_imx8q.c b/drivers/media/platform/amphion/vpu_imx8q.c
new file mode 100644
index 000000000000..f14c2b8312a8
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_imx8q.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include "vpu.h"
+#include "vpu_core.h"
+#include "vpu_imx8q.h"
+#include "vpu_rpc.h"
+
+#define IMX8Q_CSR_CM0Px_ADDR_OFFSET 0x00000000
+#define IMX8Q_CSR_CM0Px_CPUWAIT 0x00000004
+
+#ifdef CONFIG_IMX_SCU
+#include <linux/firmware/imx/ipc.h>
+#include <linux/firmware/imx/svc/misc.h>
+
+#define VPU_DISABLE_BITS 0x7
+#define VPU_IMX_DECODER_FUSE_OFFSET 14
+#define VPU_ENCODER_MASK 0x1
+#define VPU_DECODER_MASK 0x3UL
+#define VPU_DECODER_H264_MASK 0x2UL
+#define VPU_DECODER_HEVC_MASK 0x1UL
+
+static u32 imx8q_fuse;
+
+struct vpu_sc_msg_misc {
+ struct imx_sc_rpc_msg hdr;
+ u32 word;
+} __packed;
+#endif
+
+int vpu_imx8q_setup_dec(struct vpu_dev *vpu)
+{
+ const off_t offset = DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL;
+
+ vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_SET, 0x1f);
+ vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_RESET_SET, 0xffffffff);
+
+ return 0;
+}
+
+int vpu_imx8q_setup_enc(struct vpu_dev *vpu)
+{
+ return 0;
+}
+
+int vpu_imx8q_setup(struct vpu_dev *vpu)
+{
+ const off_t offset = SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL;
+
+ vpu_readl(vpu, offset + 0x108);
+
+ vpu_writel(vpu, offset + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET, 0x1);
+ vpu_writel(vpu, offset + 0x190, 0xffffffff);
+ vpu_writel(vpu, offset + SCB_BLK_CTRL_XMEM_RESET_SET, 0xffffffff);
+ vpu_writel(vpu, offset + SCB_BLK_CTRL_SCB_CLK_ENABLE_SET, 0xE);
+ vpu_writel(vpu, offset + SCB_BLK_CTRL_CACHE_RESET_SET, 0x7);
+ vpu_writel(vpu, XMEM_CONTROL, 0x102);
+
+ vpu_readl(vpu, offset + 0x108);
+
+ return 0;
+}
+
+static int vpu_imx8q_reset_enc(struct vpu_dev *vpu)
+{
+ return 0;
+}
+
+static int vpu_imx8q_reset_dec(struct vpu_dev *vpu)
+{
+ const off_t offset = DEC_MFD_XREG_SLV_BASE + MFD_BLK_CTRL;
+
+ vpu_writel(vpu, offset + MFD_BLK_CTRL_MFD_SYS_RESET_CLR, 0xffffffff);
+
+ return 0;
+}
+
+int vpu_imx8q_reset(struct vpu_dev *vpu)
+{
+ const off_t offset = SCB_XREG_SLV_BASE + SCB_SCB_BLK_CTRL;
+
+ vpu_writel(vpu, offset + SCB_BLK_CTRL_CACHE_RESET_CLR, 0x7);
+ vpu_imx8q_reset_enc(vpu);
+ vpu_imx8q_reset_dec(vpu);
+
+ return 0;
+}
+
+int vpu_imx8q_set_system_cfg_common(struct vpu_rpc_system_config *config, u32 regs, u32 core_id)
+{
+ if (!config)
+ return -EINVAL;
+
+ switch (core_id) {
+ case 0:
+ config->malone_base_addr[0] = regs + DEC_MFD_XREG_SLV_BASE;
+ config->num_malones = 1;
+ config->num_windsors = 0;
+ break;
+ case 1:
+ config->windsor_base_addr[0] = regs + ENC_MFD_XREG_SLV_0_BASE;
+ config->num_windsors = 1;
+ config->num_malones = 0;
+ break;
+ case 2:
+ config->windsor_base_addr[0] = regs + ENC_MFD_XREG_SLV_1_BASE;
+ config->num_windsors = 1;
+ config->num_malones = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (config->num_windsors) {
+ config->windsor_irq_pin[0x0][0x0] = WINDSOR_PAL_IRQ_PIN_L;
+ config->windsor_irq_pin[0x0][0x1] = WINDSOR_PAL_IRQ_PIN_H;
+ }
+
+ config->malone_base_addr[0x1] = 0x0;
+ config->hif_offset[0x0] = MFD_HIF;
+ config->hif_offset[0x1] = 0x0;
+
+ config->dpv_base_addr = 0x0;
+ config->dpv_irq_pin = 0x0;
+ config->pixif_base_addr = regs + DEC_MFD_XREG_SLV_BASE + MFD_PIX_IF;
+ config->cache_base_addr[0] = regs + MC_CACHE_0_BASE;
+ config->cache_base_addr[1] = regs + MC_CACHE_1_BASE;
+
+ return 0;
+}
+
+int vpu_imx8q_boot_core(struct vpu_core *core)
+{
+ csr_writel(core, IMX8Q_CSR_CM0Px_ADDR_OFFSET, core->fw.phys);
+ csr_writel(core, IMX8Q_CSR_CM0Px_CPUWAIT, 0);
+ return 0;
+}
+
+int vpu_imx8q_get_power_state(struct vpu_core *core)
+{
+ if (csr_readl(core, IMX8Q_CSR_CM0Px_CPUWAIT) == 1)
+ return 0;
+ return 1;
+}
+
+int vpu_imx8q_on_firmware_loaded(struct vpu_core *core)
+{
+ u8 *p;
+
+ p = core->fw.virt;
+ p[16] = core->vpu->res->plat_type;
+ p[17] = core->id;
+ p[18] = 1;
+
+ return 0;
+}
+
+int vpu_imx8q_check_memory_region(dma_addr_t base, dma_addr_t addr, u32 size)
+{
+ const struct vpu_rpc_region_t imx8q_regions[] = {
+ {0x00000000, 0x08000000, VPU_CORE_MEMORY_CACHED},
+ {0x08000000, 0x10000000, VPU_CORE_MEMORY_UNCACHED},
+ {0x10000000, 0x20000000, VPU_CORE_MEMORY_CACHED},
+ {0x20000000, 0x40000000, VPU_CORE_MEMORY_UNCACHED}
+ };
+ int i;
+
+ if (addr < base)
+ return VPU_CORE_MEMORY_INVALID;
+
+ addr -= base;
+ for (i = 0; i < ARRAY_SIZE(imx8q_regions); i++) {
+ const struct vpu_rpc_region_t *region = &imx8q_regions[i];
+
+ if (addr >= region->start && addr + size < region->end)
+ return region->type;
+ }
+
+ return VPU_CORE_MEMORY_INVALID;
+}
+
+#ifdef CONFIG_IMX_SCU
+static u32 vpu_imx8q_get_fuse(void)
+{
+ static u32 fuse_got;
+ struct imx_sc_ipc *ipc;
+ struct vpu_sc_msg_misc msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+
+ if (fuse_got)
+ return imx8q_fuse;
+
+ ret = imx_scu_get_handle(&ipc);
+ if (ret) {
+ pr_err("error: get sct handle fail: %d\n", ret);
+ return 0;
+ }
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_MISC;
+ hdr->func = IMX_SC_MISC_FUNC_OTP_FUSE_READ;
+ hdr->size = 2;
+
+ msg.word = VPU_DISABLE_BITS;
+
+ ret = imx_scu_call_rpc(ipc, &msg, true);
+ if (ret)
+ return 0;
+
+ imx8q_fuse = msg.word;
+ fuse_got = 1;
+ return imx8q_fuse;
+}
+
+bool vpu_imx8q_check_codec(enum vpu_core_type type)
+{
+ u32 fuse = vpu_imx8q_get_fuse();
+
+ if (type == VPU_CORE_TYPE_ENC) {
+ if (fuse & VPU_ENCODER_MASK)
+ return false;
+ } else if (type == VPU_CORE_TYPE_DEC) {
+ fuse >>= VPU_IMX_DECODER_FUSE_OFFSET;
+ fuse &= VPU_DECODER_MASK;
+
+ if (fuse == VPU_DECODER_MASK)
+ return false;
+ }
+ return true;
+}
+
+bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt)
+{
+ u32 fuse = vpu_imx8q_get_fuse();
+
+ if (type == VPU_CORE_TYPE_DEC) {
+ fuse >>= VPU_IMX_DECODER_FUSE_OFFSET;
+ fuse &= VPU_DECODER_MASK;
+
+ if (fuse == VPU_DECODER_HEVC_MASK && pixelfmt == V4L2_PIX_FMT_HEVC)
+ return false;
+ if (fuse == VPU_DECODER_H264_MASK && pixelfmt == V4L2_PIX_FMT_H264)
+ return false;
+ if (fuse == VPU_DECODER_MASK)
+ return false;
+ }
+
+ return true;
+}
+#else
+bool vpu_imx8q_check_codec(enum vpu_core_type type)
+{
+ return true;
+}
+
+bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt)
+{
+ return true;
+}
+#endif
diff --git a/drivers/media/platform/amphion/vpu_imx8q.h b/drivers/media/platform/amphion/vpu_imx8q.h
new file mode 100644
index 000000000000..9deffd7dde42
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_imx8q.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_IMX8Q_H
+#define _AMPHION_VPU_IMX8Q_H
+
+#define SCB_XREG_SLV_BASE 0x00000000
+#define SCB_SCB_BLK_CTRL 0x00070000
+#define SCB_BLK_CTRL_XMEM_RESET_SET 0x00000090
+#define SCB_BLK_CTRL_CACHE_RESET_SET 0x000000A0
+#define SCB_BLK_CTRL_CACHE_RESET_CLR 0x000000A4
+#define SCB_BLK_CTRL_SCB_CLK_ENABLE_SET 0x00000100
+
+#define XMEM_CONTROL 0x00041000
+
+#define MC_CACHE_0_BASE 0x00060000
+#define MC_CACHE_1_BASE 0x00068000
+
+#define DEC_MFD_XREG_SLV_BASE 0x00180000
+#define ENC_MFD_XREG_SLV_0_BASE 0x00800000
+#define ENC_MFD_XREG_SLV_1_BASE 0x00A00000
+
+#define MFD_HIF 0x0001C000
+#define MFD_HIF_MSD_REG_INTERRUPT_STATUS 0x00000018
+#define MFD_SIF 0x0001D000
+#define MFD_SIF_CTRL_STATUS 0x000000F0
+#define MFD_SIF_INTR_STATUS 0x000000F4
+#define MFD_MCX 0x00020800
+#define MFD_MCX_OFF 0x00000020
+#define MFD_PIX_IF 0x00020000
+
+#define MFD_BLK_CTRL 0x00030000
+#define MFD_BLK_CTRL_MFD_SYS_RESET_SET 0x00000000
+#define MFD_BLK_CTRL_MFD_SYS_RESET_CLR 0x00000004
+#define MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_SET 0x00000100
+#define MFD_BLK_CTRL_MFD_SYS_CLOCK_ENABLE_CLR 0x00000104
+
+#define VID_API_NUM_STREAMS 8
+#define VID_API_MAX_BUF_PER_STR 3
+#define VID_API_MAX_NUM_MVC_VIEWS 4
+#define MEDIAIP_MAX_NUM_MALONES 2
+#define MEDIAIP_MAX_NUM_MALONE_IRQ_PINS 2
+#define MEDIAIP_MAX_NUM_WINDSORS 1
+#define MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS 2
+#define MEDIAIP_MAX_NUM_CMD_IRQ_PINS 2
+#define MEDIAIP_MAX_NUM_MSG_IRQ_PINS 1
+#define MEDIAIP_MAX_NUM_TIMER_IRQ_PINS 4
+#define MEDIAIP_MAX_NUM_TIMER_IRQ_SLOTS 4
+
+#define WINDSOR_PAL_IRQ_PIN_L 0x4
+#define WINDSOR_PAL_IRQ_PIN_H 0x5
+
+struct vpu_rpc_system_config {
+ u32 cfg_cookie;
+
+ u32 num_malones;
+ u32 malone_base_addr[MEDIAIP_MAX_NUM_MALONES];
+ u32 hif_offset[MEDIAIP_MAX_NUM_MALONES];
+ u32 malone_irq_pin[MEDIAIP_MAX_NUM_MALONES][MEDIAIP_MAX_NUM_MALONE_IRQ_PINS];
+ u32 malone_irq_target[MEDIAIP_MAX_NUM_MALONES][MEDIAIP_MAX_NUM_MALONE_IRQ_PINS];
+
+ u32 num_windsors;
+ u32 windsor_base_addr[MEDIAIP_MAX_NUM_WINDSORS];
+ u32 windsor_irq_pin[MEDIAIP_MAX_NUM_WINDSORS][MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS];
+ u32 windsor_irq_target[MEDIAIP_MAX_NUM_WINDSORS][MEDIAIP_MAX_NUM_WINDSOR_IRQ_PINS];
+
+ u32 cmd_irq_pin[MEDIAIP_MAX_NUM_CMD_IRQ_PINS];
+ u32 cmd_irq_target[MEDIAIP_MAX_NUM_CMD_IRQ_PINS];
+
+ u32 msg_irq_pin[MEDIAIP_MAX_NUM_MSG_IRQ_PINS];
+ u32 msg_irq_target[MEDIAIP_MAX_NUM_MSG_IRQ_PINS];
+
+ u32 sys_clk_freq;
+ u32 num_timers;
+ u32 timer_base_addr;
+ u32 timer_irq_pin[MEDIAIP_MAX_NUM_TIMER_IRQ_PINS];
+ u32 timer_irq_target[MEDIAIP_MAX_NUM_TIMER_IRQ_PINS];
+ u32 timer_slots[MEDIAIP_MAX_NUM_TIMER_IRQ_SLOTS];
+
+ u32 gic_base_addr;
+ u32 uart_base_addr;
+
+ u32 dpv_base_addr;
+ u32 dpv_irq_pin;
+ u32 dpv_irq_target;
+
+ u32 pixif_base_addr;
+
+ u32 pal_trace_level;
+ u32 pal_trace_destination;
+
+ u32 pal_trace_level1;
+ u32 pal_trace_destination1;
+
+ u32 heap_base;
+ u32 heap_size;
+
+ u32 cache_base_addr[2];
+};
+
+int vpu_imx8q_setup_dec(struct vpu_dev *vpu);
+int vpu_imx8q_setup_enc(struct vpu_dev *vpu);
+int vpu_imx8q_setup(struct vpu_dev *vpu);
+int vpu_imx8q_reset(struct vpu_dev *vpu);
+int vpu_imx8q_set_system_cfg_common(struct vpu_rpc_system_config *config, u32 regs, u32 core_id);
+int vpu_imx8q_boot_core(struct vpu_core *core);
+int vpu_imx8q_get_power_state(struct vpu_core *core);
+int vpu_imx8q_on_firmware_loaded(struct vpu_core *core);
+int vpu_imx8q_check_memory_region(dma_addr_t base, dma_addr_t addr, u32 size);
+bool vpu_imx8q_check_codec(enum vpu_core_type type);
+bool vpu_imx8q_check_fmt(enum vpu_core_type type, u32 pixelfmt);
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c
new file mode 100644
index 000000000000..446a9de0cc11
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_malone.c
@@ -0,0 +1,1644 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/rational.h>
+#include <linux/time64.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <linux/videodev2.h>
+#include "vpu.h"
+#include "vpu_rpc.h"
+#include "vpu_defs.h"
+#include "vpu_helpers.h"
+#include "vpu_v4l2.h"
+#include "vpu_cmds.h"
+#include "vpu_imx8q.h"
+#include "vpu_malone.h"
+
+#define CMD_SIZE 25600
+#define MSG_SIZE 25600
+#define CODEC_SIZE 0x1000
+#define JPEG_SIZE 0x1000
+#define SEQ_SIZE 0x1000
+#define GOP_SIZE 0x1000
+#define PIC_SIZE 0x1000
+#define QMETER_SIZE 0x1000
+#define DBGLOG_SIZE 0x10000
+#define DEBUG_SIZE 0x80000
+#define ENG_SIZE 0x1000
+#define MALONE_SKIPPED_FRAME_ID 0x555
+
+#define MALONE_ALIGN_MBI 0x800
+#define MALONE_DCP_CHUNK_BIT 16
+#define MALONE_DCP_SIZE_MAX 0x3000000
+#define MALONE_DCP_SIZE_MIN 0x100000
+#define MALONE_DCP_FIXED_MB_ALLOC 250
+
+#define CONFIG_SET(val, cfg, pos, mask) \
+ (*(cfg) |= (((val) << (pos)) & (mask)))
+//x means source data , y means destination data
+#define STREAM_CONFIG_FORMAT_SET(x, y) CONFIG_SET(x, y, 0, 0x0000000F)
+#define STREAM_CONFIG_STRBUFIDX_SET(x, y) CONFIG_SET(x, y, 8, 0x00000300)
+#define STREAM_CONFIG_NOSEQ_SET(x, y) CONFIG_SET(x, y, 10, 0x00000400)
+#define STREAM_CONFIG_DEBLOCK_SET(x, y) CONFIG_SET(x, y, 11, 0x00000800)
+#define STREAM_CONFIG_DERING_SET(x, y) CONFIG_SET(x, y, 12, 0x00001000)
+#define STREAM_CONFIG_IBWAIT_SET(x, y) CONFIG_SET(x, y, 13, 0x00002000)
+#define STREAM_CONFIG_FBC_SET(x, y) CONFIG_SET(x, y, 14, 0x00004000)
+#define STREAM_CONFIG_PLAY_MODE_SET(x, y) CONFIG_SET(x, y, 16, 0x00030000)
+#define STREAM_CONFIG_ENABLE_DCP_SET(x, y) CONFIG_SET(x, y, 20, 0x00100000)
+#define STREAM_CONFIG_NUM_STR_BUF_SET(x, y) CONFIG_SET(x, y, 21, 0x00600000)
+#define STREAM_CONFIG_MALONE_USAGE_SET(x, y) CONFIG_SET(x, y, 23, 0x01800000)
+#define STREAM_CONFIG_MULTI_VID_SET(x, y) CONFIG_SET(x, y, 25, 0x02000000)
+#define STREAM_CONFIG_OBFUSC_EN_SET(x, y) CONFIG_SET(x, y, 26, 0x04000000)
+#define STREAM_CONFIG_RC4_EN_SET(x, y) CONFIG_SET(x, y, 27, 0x08000000)
+#define STREAM_CONFIG_MCX_SET(x, y) CONFIG_SET(x, y, 28, 0x10000000)
+#define STREAM_CONFIG_PES_SET(x, y) CONFIG_SET(x, y, 29, 0x20000000)
+#define STREAM_CONFIG_NUM_DBE_SET(x, y) CONFIG_SET(x, y, 30, 0x40000000)
+#define STREAM_CONFIG_FS_CTRL_MODE_SET(x, y) CONFIG_SET(x, y, 31, 0x80000000)
+
+enum vpu_malone_stream_input_mode {
+ INVALID_MODE = 0,
+ FRAME_LVL,
+ NON_FRAME_LVL
+};
+
+enum vpu_malone_format {
+ MALONE_FMT_NULL = 0x0,
+ MALONE_FMT_AVC = 0x1,
+ MALONE_FMT_MP2 = 0x2,
+ MALONE_FMT_VC1 = 0x3,
+ MALONE_FMT_AVS = 0x4,
+ MALONE_FMT_ASP = 0x5,
+ MALONE_FMT_JPG = 0x6,
+ MALONE_FMT_RV = 0x7,
+ MALONE_FMT_VP6 = 0x8,
+ MALONE_FMT_SPK = 0x9,
+ MALONE_FMT_VP8 = 0xA,
+ MALONE_FMT_HEVC = 0xB,
+ MALONE_FMT_LAST = MALONE_FMT_HEVC
+};
+
+enum {
+ VID_API_CMD_NULL = 0x00,
+ VID_API_CMD_PARSE_NEXT_SEQ = 0x01,
+ VID_API_CMD_PARSE_NEXT_I = 0x02,
+ VID_API_CMD_PARSE_NEXT_IP = 0x03,
+ VID_API_CMD_PARSE_NEXT_ANY = 0x04,
+ VID_API_CMD_DEC_PIC = 0x05,
+ VID_API_CMD_UPDATE_ES_WR_PTR = 0x06,
+ VID_API_CMD_UPDATE_ES_RD_PTR = 0x07,
+ VID_API_CMD_UPDATE_UDATA = 0x08,
+ VID_API_CMD_GET_FSINFO = 0x09,
+ VID_API_CMD_SKIP_PIC = 0x0a,
+ VID_API_CMD_DEC_CHUNK = 0x0b,
+ VID_API_CMD_START = 0x10,
+ VID_API_CMD_STOP = 0x11,
+ VID_API_CMD_ABORT = 0x12,
+ VID_API_CMD_RST_BUF = 0x13,
+ VID_API_CMD_FS_RELEASE = 0x15,
+ VID_API_CMD_MEM_REGION_ATTACH = 0x16,
+ VID_API_CMD_MEM_REGION_DETACH = 0x17,
+ VID_API_CMD_MVC_VIEW_SELECT = 0x18,
+ VID_API_CMD_FS_ALLOC = 0x19,
+ VID_API_CMD_DBG_GET_STATUS = 0x1C,
+ VID_API_CMD_DBG_START_LOG = 0x1D,
+ VID_API_CMD_DBG_STOP_LOG = 0x1E,
+ VID_API_CMD_DBG_DUMP_LOG = 0x1F,
+ VID_API_CMD_YUV_READY = 0x20,
+ VID_API_CMD_TS = 0x21,
+
+ VID_API_CMD_FIRM_RESET = 0x40,
+
+ VID_API_CMD_SNAPSHOT = 0xAA,
+ VID_API_CMD_ROLL_SNAPSHOT = 0xAB,
+ VID_API_CMD_LOCK_SCHEDULER = 0xAC,
+ VID_API_CMD_UNLOCK_SCHEDULER = 0xAD,
+ VID_API_CMD_CQ_FIFO_DUMP = 0xAE,
+ VID_API_CMD_DBG_FIFO_DUMP = 0xAF,
+ VID_API_CMD_SVC_ILP = 0xBB,
+ VID_API_CMD_FW_STATUS = 0xF0,
+ VID_API_CMD_INVALID = 0xFF
+};
+
+enum {
+ VID_API_EVENT_NULL = 0x00,
+ VID_API_EVENT_RESET_DONE = 0x01,
+ VID_API_EVENT_SEQ_HDR_FOUND = 0x02,
+ VID_API_EVENT_PIC_HDR_FOUND = 0x03,
+ VID_API_EVENT_PIC_DECODED = 0x04,
+ VID_API_EVENT_FIFO_LOW = 0x05,
+ VID_API_EVENT_FIFO_HIGH = 0x06,
+ VID_API_EVENT_FIFO_EMPTY = 0x07,
+ VID_API_EVENT_FIFO_FULL = 0x08,
+ VID_API_EVENT_BS_ERROR = 0x09,
+ VID_API_EVENT_UDATA_FIFO_UPTD = 0x0A,
+ VID_API_EVENT_RES_CHANGE = 0x0B,
+ VID_API_EVENT_FIFO_OVF = 0x0C,
+ VID_API_EVENT_CHUNK_DECODED = 0x0D,
+ VID_API_EVENT_REQ_FRAME_BUFF = 0x10,
+ VID_API_EVENT_FRAME_BUFF_RDY = 0x11,
+ VID_API_EVENT_REL_FRAME_BUFF = 0x12,
+ VID_API_EVENT_STR_BUF_RST = 0x13,
+ VID_API_EVENT_RET_PING = 0x14,
+ VID_API_EVENT_QMETER = 0x15,
+ VID_API_EVENT_STR_FMT_CHANGE = 0x16,
+ VID_API_EVENT_FIRMWARE_XCPT = 0x17,
+ VID_API_EVENT_START_DONE = 0x18,
+ VID_API_EVENT_STOPPED = 0x19,
+ VID_API_EVENT_ABORT_DONE = 0x1A,
+ VID_API_EVENT_FINISHED = 0x1B,
+ VID_API_EVENT_DBG_STAT_UPDATE = 0x1C,
+ VID_API_EVENT_DBG_LOG_STARTED = 0x1D,
+ VID_API_EVENT_DBG_LOG_STOPPED = 0x1E,
+ VID_API_EVENT_DBG_LOG_UPDATED = 0x1F,
+ VID_API_EVENT_DBG_MSG_DEC = 0x20,
+ VID_API_EVENT_DEC_SC_ERR = 0x21,
+ VID_API_EVENT_CQ_FIFO_DUMP = 0x22,
+ VID_API_EVENT_DBG_FIFO_DUMP = 0x23,
+ VID_API_EVENT_DEC_CHECK_RES = 0x24,
+ VID_API_EVENT_DEC_CFG_INFO = 0x25,
+ VID_API_EVENT_UNSUPPORTED_STREAM = 0x26,
+ VID_API_EVENT_STR_SUSPENDED = 0x30,
+ VID_API_EVENT_SNAPSHOT_DONE = 0x40,
+ VID_API_EVENT_FW_STATUS = 0xF0,
+ VID_API_EVENT_INVALID = 0xFF
+};
+
+struct vpu_malone_buffer_desc {
+ struct vpu_rpc_buffer_desc buffer;
+ u32 low;
+ u32 high;
+};
+
+struct vpu_malone_str_buffer {
+ u32 wptr;
+ u32 rptr;
+ u32 start;
+ u32 end;
+ u32 lwm;
+};
+
+struct vpu_malone_picth_info {
+ u32 frame_pitch;
+};
+
+struct vpu_malone_table_desc {
+ u32 array_base;
+ u32 size;
+};
+
+struct vpu_malone_dbglog_desc {
+ u32 addr;
+ u32 size;
+ u32 level;
+ u32 reserved;
+};
+
+struct vpu_malone_frame_buffer {
+ u32 addr;
+ u32 size;
+};
+
+struct vpu_malone_udata {
+ u32 base;
+ u32 total_size;
+ u32 slot_size;
+};
+
+struct vpu_malone_buffer_info {
+ u32 stream_input_mode;
+ u32 stream_pic_input_count;
+ u32 stream_pic_parsed_count;
+ u32 stream_buffer_threshold;
+ u32 stream_pic_end_flag;
+};
+
+struct vpu_malone_encrypt_info {
+ u32 rec4key[8];
+ u32 obfusc;
+};
+
+struct malone_iface {
+ u32 exec_base_addr;
+ u32 exec_area_size;
+ struct vpu_malone_buffer_desc cmd_buffer_desc;
+ struct vpu_malone_buffer_desc msg_buffer_desc;
+ u32 cmd_int_enable[VID_API_NUM_STREAMS];
+ struct vpu_malone_picth_info stream_pitch_info[VID_API_NUM_STREAMS];
+ u32 stream_config[VID_API_NUM_STREAMS];
+ struct vpu_malone_table_desc codec_param_tab_desc;
+ struct vpu_malone_table_desc jpeg_param_tab_desc;
+ u32 stream_buffer_desc[VID_API_NUM_STREAMS][VID_API_MAX_BUF_PER_STR];
+ struct vpu_malone_table_desc seq_info_tab_desc;
+ struct vpu_malone_table_desc pic_info_tab_desc;
+ struct vpu_malone_table_desc gop_info_tab_desc;
+ struct vpu_malone_table_desc qmeter_info_tab_desc;
+ u32 stream_error[VID_API_NUM_STREAMS];
+ u32 fw_version;
+ u32 fw_offset;
+ u32 max_streams;
+ struct vpu_malone_dbglog_desc dbglog_desc;
+ struct vpu_rpc_buffer_desc api_cmd_buffer_desc[VID_API_NUM_STREAMS];
+ struct vpu_malone_udata udata_buffer[VID_API_NUM_STREAMS];
+ struct vpu_malone_buffer_desc debug_buffer_desc;
+ struct vpu_malone_buffer_desc eng_access_buff_desc[VID_API_NUM_STREAMS];
+ u32 encrypt_info[VID_API_NUM_STREAMS];
+ struct vpu_rpc_system_config system_cfg;
+ u32 api_version;
+ struct vpu_malone_buffer_info stream_buff_info[VID_API_NUM_STREAMS];
+};
+
+struct malone_jpg_params {
+ u32 rotation_angle;
+ u32 horiz_scale_factor;
+ u32 vert_scale_factor;
+ u32 rotation_mode;
+ u32 rgb_mode;
+ u32 chunk_mode; /* 0 ~ 1 */
+ u32 last_chunk; /* 0 ~ 1 */
+ u32 chunk_rows; /* 0 ~ 255 */
+ u32 num_bytes;
+ u32 jpg_crop_x;
+ u32 jpg_crop_y;
+ u32 jpg_crop_width;
+ u32 jpg_crop_height;
+ u32 jpg_mjpeg_mode;
+ u32 jpg_mjpeg_interlaced;
+};
+
+struct malone_codec_params {
+ u32 disp_imm;
+ u32 fourcc;
+ u32 codec_version;
+ u32 frame_rate;
+ u32 dbglog_enable;
+ u32 bsdma_lwm;
+ u32 bbd_coring;
+ u32 bbd_s_thr_row;
+ u32 bbd_p_thr_row;
+ u32 bbd_s_thr_logo_row;
+ u32 bbd_p_thr_logo_row;
+ u32 bbd_s_thr_col;
+ u32 bbd_p_thr_col;
+ u32 bbd_chr_thr_row;
+ u32 bbd_chr_thr_col;
+ u32 bbd_uv_mid_level;
+ u32 bbd_excl_win_mb_left;
+ u32 bbd_excl_win_mb_right;
+};
+
+struct malone_padding_scode {
+ u32 scode_type;
+ u32 pixelformat;
+ u32 data[2];
+};
+
+struct malone_fmt_mapping {
+ u32 pixelformat;
+ enum vpu_malone_format malone_format;
+};
+
+struct malone_scode_t {
+ struct vpu_inst *inst;
+ struct vb2_buffer *vb;
+ u32 wptr;
+ u32 need_data;
+};
+
+struct malone_scode_handler {
+ u32 pixelformat;
+ int (*insert_scode_seq)(struct malone_scode_t *scode);
+ int (*insert_scode_pic)(struct malone_scode_t *scode);
+};
+
+struct vpu_dec_ctrl {
+ struct malone_codec_params *codec_param;
+ struct malone_jpg_params *jpg;
+ void *seq_mem;
+ void *pic_mem;
+ void *gop_mem;
+ void *qmeter_mem;
+ void *dbglog_mem;
+ struct vpu_malone_str_buffer __iomem *str_buf[VID_API_NUM_STREAMS];
+ u32 buf_addr[VID_API_NUM_STREAMS];
+};
+
+u32 vpu_malone_get_data_size(void)
+{
+ return sizeof(struct vpu_dec_ctrl);
+}
+
+void vpu_malone_init_rpc(struct vpu_shared_addr *shared,
+ struct vpu_buffer *rpc, dma_addr_t boot_addr)
+{
+ struct malone_iface *iface;
+ struct vpu_dec_ctrl *hc;
+ unsigned long base_phy_addr;
+ unsigned long phy_addr;
+ unsigned long offset;
+ unsigned int i;
+
+ if (rpc->phys < boot_addr)
+ return;
+
+ iface = rpc->virt;
+ base_phy_addr = rpc->phys - boot_addr;
+ hc = shared->priv;
+
+ shared->iface = iface;
+ shared->boot_addr = boot_addr;
+
+ iface->exec_base_addr = base_phy_addr;
+ iface->exec_area_size = rpc->length;
+
+ offset = sizeof(struct malone_iface);
+ phy_addr = base_phy_addr + offset;
+
+ shared->cmd_desc = &iface->cmd_buffer_desc.buffer;
+ shared->cmd_mem_vir = rpc->virt + offset;
+ iface->cmd_buffer_desc.buffer.start =
+ iface->cmd_buffer_desc.buffer.rptr =
+ iface->cmd_buffer_desc.buffer.wptr = phy_addr;
+ iface->cmd_buffer_desc.buffer.end = iface->cmd_buffer_desc.buffer.start + CMD_SIZE;
+ offset += CMD_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ shared->msg_desc = &iface->msg_buffer_desc.buffer;
+ shared->msg_mem_vir = rpc->virt + offset;
+ iface->msg_buffer_desc.buffer.start =
+ iface->msg_buffer_desc.buffer.wptr =
+ iface->msg_buffer_desc.buffer.rptr = phy_addr;
+ iface->msg_buffer_desc.buffer.end = iface->msg_buffer_desc.buffer.start + MSG_SIZE;
+ offset += MSG_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ iface->codec_param_tab_desc.array_base = phy_addr;
+ hc->codec_param = rpc->virt + offset;
+ offset += CODEC_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ iface->jpeg_param_tab_desc.array_base = phy_addr;
+ hc->jpg = rpc->virt + offset;
+ offset += JPEG_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ iface->seq_info_tab_desc.array_base = phy_addr;
+ hc->seq_mem = rpc->virt + offset;
+ offset += SEQ_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ iface->pic_info_tab_desc.array_base = phy_addr;
+ hc->pic_mem = rpc->virt + offset;
+ offset += PIC_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ iface->gop_info_tab_desc.array_base = phy_addr;
+ hc->gop_mem = rpc->virt + offset;
+ offset += GOP_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ iface->qmeter_info_tab_desc.array_base = phy_addr;
+ hc->qmeter_mem = rpc->virt + offset;
+ offset += QMETER_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ iface->dbglog_desc.addr = phy_addr;
+ iface->dbglog_desc.size = DBGLOG_SIZE;
+ hc->dbglog_mem = rpc->virt + offset;
+ offset += DBGLOG_SIZE;
+ phy_addr = base_phy_addr + offset;
+
+ for (i = 0; i < VID_API_NUM_STREAMS; i++) {
+ iface->eng_access_buff_desc[i].buffer.start =
+ iface->eng_access_buff_desc[i].buffer.wptr =
+ iface->eng_access_buff_desc[i].buffer.rptr = phy_addr;
+ iface->eng_access_buff_desc[i].buffer.end =
+ iface->eng_access_buff_desc[i].buffer.start + ENG_SIZE;
+ offset += ENG_SIZE;
+ phy_addr = base_phy_addr + offset;
+ }
+
+ for (i = 0; i < VID_API_NUM_STREAMS; i++) {
+ iface->encrypt_info[i] = phy_addr;
+ offset += sizeof(struct vpu_malone_encrypt_info);
+ phy_addr = base_phy_addr + offset;
+ }
+
+ rpc->bytesused = offset;
+}
+
+void vpu_malone_set_log_buf(struct vpu_shared_addr *shared,
+ struct vpu_buffer *log)
+{
+ struct malone_iface *iface = shared->iface;
+
+ iface->debug_buffer_desc.buffer.start =
+ iface->debug_buffer_desc.buffer.wptr =
+ iface->debug_buffer_desc.buffer.rptr = log->phys - shared->boot_addr;
+ iface->debug_buffer_desc.buffer.end = iface->debug_buffer_desc.buffer.start + log->length;
+}
+
+static u32 get_str_buffer_offset(u32 instance)
+{
+ return DEC_MFD_XREG_SLV_BASE + MFD_MCX + MFD_MCX_OFF * instance;
+}
+
+void vpu_malone_set_system_cfg(struct vpu_shared_addr *shared,
+ u32 regs_base, void __iomem *regs, u32 core_id)
+{
+ struct malone_iface *iface = shared->iface;
+ struct vpu_rpc_system_config *config = &iface->system_cfg;
+ struct vpu_dec_ctrl *hc = shared->priv;
+ int i;
+
+ vpu_imx8q_set_system_cfg_common(config, regs_base, core_id);
+ for (i = 0; i < VID_API_NUM_STREAMS; i++) {
+ u32 offset = get_str_buffer_offset(i);
+
+ hc->buf_addr[i] = regs_base + offset;
+ hc->str_buf[i] = regs + offset;
+ }
+}
+
+u32 vpu_malone_get_version(struct vpu_shared_addr *shared)
+{
+ struct malone_iface *iface = shared->iface;
+
+ return iface->fw_version;
+}
+
+int vpu_malone_get_stream_buffer_size(struct vpu_shared_addr *shared)
+{
+ return 0xc00000;
+}
+
+int vpu_malone_config_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_buffer *buf)
+{
+ struct malone_iface *iface = shared->iface;
+ struct vpu_dec_ctrl *hc = shared->priv;
+ struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance];
+
+ writel(buf->phys, &str_buf->start);
+ writel(buf->phys, &str_buf->rptr);
+ writel(buf->phys, &str_buf->wptr);
+ writel(buf->phys + buf->length, &str_buf->end);
+ writel(0x1, &str_buf->lwm);
+
+ iface->stream_buffer_desc[instance][0] = hc->buf_addr[instance];
+
+ return 0;
+}
+
+int vpu_malone_get_stream_buffer_desc(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_rpc_buffer_desc *desc)
+{
+ struct vpu_dec_ctrl *hc = shared->priv;
+ struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance];
+
+ if (desc) {
+ desc->wptr = readl(&str_buf->wptr);
+ desc->rptr = readl(&str_buf->rptr);
+ desc->start = readl(&str_buf->start);
+ desc->end = readl(&str_buf->end);
+ }
+
+ return 0;
+}
+
+static void vpu_malone_update_wptr(struct vpu_malone_str_buffer __iomem *str_buf, u32 wptr)
+{
+ /*update wptr after data is written*/
+ mb();
+ writel(wptr, &str_buf->wptr);
+}
+
+static void vpu_malone_update_rptr(struct vpu_malone_str_buffer __iomem *str_buf, u32 rptr)
+{
+ /*update rptr after data is read*/
+ mb();
+ writel(rptr, &str_buf->rptr);
+}
+
+int vpu_malone_update_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance, u32 ptr, bool write)
+{
+ struct vpu_dec_ctrl *hc = shared->priv;
+ struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance];
+
+ if (write)
+ vpu_malone_update_wptr(str_buf, ptr);
+ else
+ vpu_malone_update_rptr(str_buf, ptr);
+
+ return 0;
+}
+
+static struct malone_fmt_mapping fmt_mappings[] = {
+ {V4L2_PIX_FMT_H264, MALONE_FMT_AVC},
+ {V4L2_PIX_FMT_H264_MVC, MALONE_FMT_AVC},
+ {V4L2_PIX_FMT_HEVC, MALONE_FMT_HEVC},
+ {V4L2_PIX_FMT_VC1_ANNEX_G, MALONE_FMT_VC1},
+ {V4L2_PIX_FMT_VC1_ANNEX_L, MALONE_FMT_VC1},
+ {V4L2_PIX_FMT_MPEG2, MALONE_FMT_MP2},
+ {V4L2_PIX_FMT_MPEG4, MALONE_FMT_ASP},
+ {V4L2_PIX_FMT_XVID, MALONE_FMT_ASP},
+ {V4L2_PIX_FMT_H263, MALONE_FMT_ASP},
+ {V4L2_PIX_FMT_JPEG, MALONE_FMT_JPG},
+ {V4L2_PIX_FMT_VP8, MALONE_FMT_VP8},
+};
+
+static enum vpu_malone_format vpu_malone_format_remap(u32 pixelformat)
+{
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(fmt_mappings); i++) {
+ if (pixelformat == fmt_mappings[i].pixelformat)
+ return fmt_mappings[i].malone_format;
+ }
+
+ return MALONE_FMT_NULL;
+}
+
+static void vpu_malone_set_stream_cfg(struct vpu_shared_addr *shared,
+ u32 instance,
+ enum vpu_malone_format malone_format)
+{
+ struct malone_iface *iface = shared->iface;
+ u32 *curr_str_cfg = &iface->stream_config[instance];
+
+ *curr_str_cfg = 0;
+ STREAM_CONFIG_FORMAT_SET(malone_format, curr_str_cfg);
+ STREAM_CONFIG_STRBUFIDX_SET(0, curr_str_cfg);
+ STREAM_CONFIG_NOSEQ_SET(0, curr_str_cfg);
+ STREAM_CONFIG_DEBLOCK_SET(0, curr_str_cfg);
+ STREAM_CONFIG_DERING_SET(0, curr_str_cfg);
+ STREAM_CONFIG_PLAY_MODE_SET(0x3, curr_str_cfg);
+ STREAM_CONFIG_FS_CTRL_MODE_SET(0x1, curr_str_cfg);
+ STREAM_CONFIG_ENABLE_DCP_SET(1, curr_str_cfg);
+ STREAM_CONFIG_NUM_STR_BUF_SET(1, curr_str_cfg);
+ STREAM_CONFIG_MALONE_USAGE_SET(1, curr_str_cfg);
+ STREAM_CONFIG_MULTI_VID_SET(0, curr_str_cfg);
+ STREAM_CONFIG_OBFUSC_EN_SET(0, curr_str_cfg);
+ STREAM_CONFIG_RC4_EN_SET(0, curr_str_cfg);
+ STREAM_CONFIG_MCX_SET(1, curr_str_cfg);
+ STREAM_CONFIG_PES_SET(0, curr_str_cfg);
+ STREAM_CONFIG_NUM_DBE_SET(1, curr_str_cfg);
+}
+
+static int vpu_malone_set_params(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_decode_params *params)
+{
+ struct malone_iface *iface = shared->iface;
+ struct vpu_dec_ctrl *hc = shared->priv;
+ enum vpu_malone_format malone_format;
+
+ malone_format = vpu_malone_format_remap(params->codec_format);
+ iface->udata_buffer[instance].base = params->udata.base;
+ iface->udata_buffer[instance].slot_size = params->udata.size;
+
+ vpu_malone_set_stream_cfg(shared, instance, malone_format);
+
+ if (malone_format == MALONE_FMT_JPG) {
+ //1:JPGD_MJPEG_MODE_A; 2:JPGD_MJPEG_MODE_B
+ hc->jpg[instance].jpg_mjpeg_mode = 1;
+ //0: JPGD_MJPEG_PROGRESSIVE
+ hc->jpg[instance].jpg_mjpeg_interlaced = 0;
+ }
+
+ hc->codec_param[instance].disp_imm = params->b_dis_reorder ? 1 : 0;
+ hc->codec_param[instance].dbglog_enable = 0;
+ iface->dbglog_desc.level = 0;
+
+ if (params->b_non_frame)
+ iface->stream_buff_info[instance].stream_input_mode = NON_FRAME_LVL;
+ else
+ iface->stream_buff_info[instance].stream_input_mode = FRAME_LVL;
+ iface->stream_buff_info[instance].stream_buffer_threshold = 0;
+ iface->stream_buff_info[instance].stream_pic_input_count = 0;
+
+ return 0;
+}
+
+static bool vpu_malone_is_non_frame_mode(struct vpu_shared_addr *shared, u32 instance)
+{
+ struct malone_iface *iface = shared->iface;
+
+ if (iface->stream_buff_info[instance].stream_input_mode == NON_FRAME_LVL)
+ return true;
+
+ return false;
+}
+
+static int vpu_malone_update_params(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_decode_params *params)
+{
+ struct malone_iface *iface = shared->iface;
+
+ if (params->end_flag)
+ iface->stream_buff_info[instance].stream_pic_end_flag = params->end_flag;
+ params->end_flag = 0;
+
+ return 0;
+}
+
+int vpu_malone_set_decode_params(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_decode_params *params,
+ u32 update)
+{
+ if (!params)
+ return -EINVAL;
+
+ if (!update)
+ return vpu_malone_set_params(shared, instance, params);
+ else
+ return vpu_malone_update_params(shared, instance, params);
+}
+
+static struct vpu_pair malone_cmds[] = {
+ {VPU_CMD_ID_START, VID_API_CMD_START},
+ {VPU_CMD_ID_STOP, VID_API_CMD_STOP},
+ {VPU_CMD_ID_ABORT, VID_API_CMD_ABORT},
+ {VPU_CMD_ID_RST_BUF, VID_API_CMD_RST_BUF},
+ {VPU_CMD_ID_SNAPSHOT, VID_API_CMD_SNAPSHOT},
+ {VPU_CMD_ID_FIRM_RESET, VID_API_CMD_FIRM_RESET},
+ {VPU_CMD_ID_FS_ALLOC, VID_API_CMD_FS_ALLOC},
+ {VPU_CMD_ID_FS_RELEASE, VID_API_CMD_FS_RELEASE},
+ {VPU_CMD_ID_TIMESTAMP, VID_API_CMD_TS},
+ {VPU_CMD_ID_DEBUG, VID_API_CMD_FW_STATUS},
+};
+
+static struct vpu_pair malone_msgs[] = {
+ {VPU_MSG_ID_RESET_DONE, VID_API_EVENT_RESET_DONE},
+ {VPU_MSG_ID_START_DONE, VID_API_EVENT_START_DONE},
+ {VPU_MSG_ID_STOP_DONE, VID_API_EVENT_STOPPED},
+ {VPU_MSG_ID_ABORT_DONE, VID_API_EVENT_ABORT_DONE},
+ {VPU_MSG_ID_BUF_RST, VID_API_EVENT_STR_BUF_RST},
+ {VPU_MSG_ID_PIC_EOS, VID_API_EVENT_FINISHED},
+ {VPU_MSG_ID_SEQ_HDR_FOUND, VID_API_EVENT_SEQ_HDR_FOUND},
+ {VPU_MSG_ID_RES_CHANGE, VID_API_EVENT_RES_CHANGE},
+ {VPU_MSG_ID_PIC_HDR_FOUND, VID_API_EVENT_PIC_HDR_FOUND},
+ {VPU_MSG_ID_PIC_DECODED, VID_API_EVENT_PIC_DECODED},
+ {VPU_MSG_ID_DEC_DONE, VID_API_EVENT_FRAME_BUFF_RDY},
+ {VPU_MSG_ID_FRAME_REQ, VID_API_EVENT_REQ_FRAME_BUFF},
+ {VPU_MSG_ID_FRAME_RELEASE, VID_API_EVENT_REL_FRAME_BUFF},
+ {VPU_MSG_ID_FIFO_LOW, VID_API_EVENT_FIFO_LOW},
+ {VPU_MSG_ID_BS_ERROR, VID_API_EVENT_BS_ERROR},
+ {VPU_MSG_ID_UNSUPPORTED, VID_API_EVENT_UNSUPPORTED_STREAM},
+ {VPU_MSG_ID_FIRMWARE_XCPT, VID_API_EVENT_FIRMWARE_XCPT},
+};
+
+static void vpu_malone_pack_fs_alloc(struct vpu_rpc_event *pkt,
+ struct vpu_fs_info *fs)
+{
+ const u32 fs_type[] = {
+ [MEM_RES_FRAME] = 0,
+ [MEM_RES_MBI] = 1,
+ [MEM_RES_DCP] = 2,
+ };
+
+ pkt->hdr.num = 7;
+ pkt->data[0] = fs->id | (fs->tag << 24);
+ pkt->data[1] = fs->luma_addr;
+ if (fs->type == MEM_RES_FRAME) {
+ /*
+ * if luma_addr equal to chroma_addr,
+ * means luma(plane[0]) and chromau(plane[1]) used the
+ * same fd -- usage of NXP codec2. Need to manually
+ * offset chroma addr.
+ */
+ if (fs->luma_addr == fs->chroma_addr)
+ fs->chroma_addr = fs->luma_addr + fs->luma_size;
+ pkt->data[2] = fs->luma_addr + fs->luma_size / 2;
+ pkt->data[3] = fs->chroma_addr;
+ pkt->data[4] = fs->chroma_addr + fs->chromau_size / 2;
+ pkt->data[5] = fs->bytesperline;
+ } else {
+ pkt->data[2] = fs->luma_size;
+ pkt->data[3] = 0;
+ pkt->data[4] = 0;
+ pkt->data[5] = 0;
+ }
+ pkt->data[6] = fs_type[fs->type];
+}
+
+static void vpu_malone_pack_fs_release(struct vpu_rpc_event *pkt,
+ struct vpu_fs_info *fs)
+{
+ pkt->hdr.num = 1;
+ pkt->data[0] = fs->id | (fs->tag << 24);
+}
+
+static void vpu_malone_pack_timestamp(struct vpu_rpc_event *pkt,
+ struct vpu_ts_info *info)
+{
+ struct timespec64 ts = ns_to_timespec64(info->timestamp);
+
+ pkt->hdr.num = 3;
+
+ pkt->data[0] = ts.tv_sec;
+ pkt->data[1] = ts.tv_nsec;
+ pkt->data[2] = info->size;
+}
+
+int vpu_malone_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data)
+{
+ int ret;
+
+ ret = vpu_find_dst_by_src(malone_cmds, ARRAY_SIZE(malone_cmds), id);
+ if (ret < 0)
+ return ret;
+
+ pkt->hdr.id = ret;
+ pkt->hdr.num = 0;
+ pkt->hdr.index = index;
+
+ switch (id) {
+ case VPU_CMD_ID_FS_ALLOC:
+ vpu_malone_pack_fs_alloc(pkt, data);
+ break;
+ case VPU_CMD_ID_FS_RELEASE:
+ vpu_malone_pack_fs_release(pkt, data);
+ break;
+ case VPU_CMD_ID_TIMESTAMP:
+ vpu_malone_pack_timestamp(pkt, data);
+ break;
+ }
+
+ pkt->hdr.index = index;
+ return 0;
+}
+
+int vpu_malone_convert_msg_id(u32 id)
+{
+ return vpu_find_src_by_dst(malone_msgs, ARRAY_SIZE(malone_msgs), id);
+}
+
+static void vpu_malone_fill_planes(struct vpu_dec_codec_info *info)
+{
+ u32 interlaced = info->progressive ? 0 : 1;
+
+ info->bytesperline[0] = 0;
+ info->sizeimage[0] = vpu_helper_get_plane_size(info->pixfmt,
+ info->decoded_width,
+ info->decoded_height,
+ 0,
+ info->stride,
+ interlaced,
+ &info->bytesperline[0]);
+ info->bytesperline[1] = 0;
+ info->sizeimage[1] = vpu_helper_get_plane_size(info->pixfmt,
+ info->decoded_width,
+ info->decoded_height,
+ 1,
+ info->stride,
+ interlaced,
+ &info->bytesperline[1]);
+}
+
+static void vpu_malone_init_seq_hdr(struct vpu_dec_codec_info *info)
+{
+ u32 chunks = info->num_dfe_area >> MALONE_DCP_CHUNK_BIT;
+
+ vpu_malone_fill_planes(info);
+
+ info->mbi_size = (info->sizeimage[0] + info->sizeimage[1]) >> 2;
+ info->mbi_size = ALIGN(info->mbi_size, MALONE_ALIGN_MBI);
+
+ info->dcp_size = MALONE_DCP_SIZE_MAX;
+ if (chunks) {
+ u32 mb_num;
+ u32 mb_w;
+ u32 mb_h;
+
+ mb_w = DIV_ROUND_UP(info->decoded_width, 16);
+ mb_h = DIV_ROUND_UP(info->decoded_height, 16);
+ mb_num = mb_w * mb_h;
+ info->dcp_size = mb_num * MALONE_DCP_FIXED_MB_ALLOC * chunks;
+ info->dcp_size = clamp_t(u32, info->dcp_size,
+ MALONE_DCP_SIZE_MIN, MALONE_DCP_SIZE_MAX);
+ }
+}
+
+static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt,
+ struct vpu_dec_codec_info *info)
+{
+ info->num_ref_frms = pkt->data[0];
+ info->num_dpb_frms = pkt->data[1];
+ info->num_dfe_area = pkt->data[2];
+ info->progressive = pkt->data[3];
+ info->width = pkt->data[5];
+ info->height = pkt->data[4];
+ info->decoded_width = pkt->data[12];
+ info->decoded_height = pkt->data[11];
+ info->frame_rate.numerator = 1000;
+ info->frame_rate.denominator = pkt->data[8];
+ info->dsp_asp_ratio = pkt->data[9];
+ info->level_idc = pkt->data[10];
+ info->bit_depth_luma = pkt->data[13];
+ info->bit_depth_chroma = pkt->data[14];
+ info->chroma_fmt = pkt->data[15];
+ info->color_primaries = vpu_color_cvrt_primaries_i2v(pkt->data[16]);
+ info->transfer_chars = vpu_color_cvrt_transfers_i2v(pkt->data[17]);
+ info->matrix_coeffs = vpu_color_cvrt_matrix_i2v(pkt->data[18]);
+ info->full_range = vpu_color_cvrt_full_range_i2v(pkt->data[19]);
+ info->vui_present = pkt->data[20];
+ info->mvc_num_views = pkt->data[21];
+ info->offset_x = pkt->data[23];
+ info->offset_y = pkt->data[25];
+ info->tag = pkt->data[27];
+ if (info->bit_depth_luma > 8)
+ info->pixfmt = V4L2_PIX_FMT_NV12M_10BE_8L128;
+ else
+ info->pixfmt = V4L2_PIX_FMT_NV12M_8L128;
+ if (info->frame_rate.numerator && info->frame_rate.denominator) {
+ unsigned long n, d;
+
+ rational_best_approximation(info->frame_rate.numerator,
+ info->frame_rate.denominator,
+ info->frame_rate.numerator,
+ info->frame_rate.denominator,
+ &n, &d);
+ info->frame_rate.numerator = n;
+ info->frame_rate.denominator = d;
+ }
+ vpu_malone_init_seq_hdr(info);
+}
+
+static void vpu_malone_unpack_pic_info(struct vpu_rpc_event *pkt,
+ struct vpu_dec_pic_info *info)
+{
+ info->id = pkt->data[7];
+ info->luma = pkt->data[0];
+ info->start = pkt->data[10];
+ info->end = pkt->data[12];
+ info->pic_size = pkt->data[11];
+ info->stride = pkt->data[5];
+ info->consumed_count = pkt->data[13];
+ if (info->id == MALONE_SKIPPED_FRAME_ID)
+ info->skipped = 1;
+ else
+ info->skipped = 0;
+}
+
+static void vpu_malone_unpack_req_frame(struct vpu_rpc_event *pkt,
+ struct vpu_fs_info *info)
+{
+ info->type = pkt->data[1];
+}
+
+static void vpu_malone_unpack_rel_frame(struct vpu_rpc_event *pkt,
+ struct vpu_fs_info *info)
+{
+ info->id = pkt->data[0];
+ info->type = pkt->data[1];
+ info->not_displayed = pkt->data[2];
+}
+
+static void vpu_malone_unpack_buff_rdy(struct vpu_rpc_event *pkt,
+ struct vpu_dec_pic_info *info)
+{
+ struct timespec64 ts = { pkt->data[9], pkt->data[10] };
+
+ info->id = pkt->data[0];
+ info->luma = pkt->data[1];
+ info->stride = pkt->data[3];
+ if (info->id == MALONE_SKIPPED_FRAME_ID)
+ info->skipped = 1;
+ else
+ info->skipped = 0;
+
+ info->timestamp = timespec64_to_ns(&ts);
+}
+
+int vpu_malone_unpack_msg_data(struct vpu_rpc_event *pkt, void *data)
+{
+ if (!pkt || !data)
+ return -EINVAL;
+
+ switch (pkt->hdr.id) {
+ case VID_API_EVENT_SEQ_HDR_FOUND:
+ vpu_malone_unpack_seq_hdr(pkt, data);
+ break;
+ case VID_API_EVENT_PIC_DECODED:
+ vpu_malone_unpack_pic_info(pkt, data);
+ break;
+ case VID_API_EVENT_REQ_FRAME_BUFF:
+ vpu_malone_unpack_req_frame(pkt, data);
+ break;
+ case VID_API_EVENT_REL_FRAME_BUFF:
+ vpu_malone_unpack_rel_frame(pkt, data);
+ break;
+ case VID_API_EVENT_FRAME_BUFF_RDY:
+ vpu_malone_unpack_buff_rdy(pkt, data);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct malone_padding_scode padding_scodes[] = {
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_H264, {0x0B010000, 0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_H264_MVC, {0x0B010000, 0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_HEVC, {0x4A010000, 0x20}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_VC1_ANNEX_G, {0x0a010000, 0x0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_VC1_ANNEX_L, {0x0a010000, 0x0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_MPEG2, {0xCC010000, 0x0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_MPEG4, {0xb1010000, 0x0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_XVID, {0xb1010000, 0x0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_H263, {0xb1010000, 0x0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_VP8, {0x34010000, 0x0}},
+ {SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0xefff0000, 0x0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H264, {0x0B010000, 0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H264_MVC, {0x0B010000, 0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_HEVC, {0x4A010000, 0x20}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_VC1_ANNEX_G, {0x0a010000, 0x0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_VC1_ANNEX_L, {0x0a010000, 0x0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_MPEG2, {0xb7010000, 0x0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_MPEG4, {0xb1010000, 0x0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_XVID, {0xb1010000, 0x0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_H263, {0xb1010000, 0x0}},
+ {SCODE_PADDING_ABORT, V4L2_PIX_FMT_VP8, {0x34010000, 0x0}},
+ {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}},
+};
+
+static const struct malone_padding_scode padding_scode_dft = {0x0, 0x0};
+
+static const struct malone_padding_scode *get_padding_scode(u32 type, u32 fmt)
+{
+ const struct malone_padding_scode *s;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(padding_scodes); i++) {
+ s = &padding_scodes[i];
+
+ if (s->scode_type == type && s->pixelformat == fmt)
+ return s;
+ }
+
+ if (type != SCODE_PADDING_BUFFLUSH)
+ return &padding_scode_dft;
+
+ return NULL;
+}
+
+static int vpu_malone_add_padding_scode(struct vpu_buffer *stream_buffer,
+ struct vpu_malone_str_buffer __iomem *str_buf,
+ u32 pixelformat, u32 scode_type)
+{
+ u32 wptr;
+ int size;
+ int total_size = 0;
+ const struct malone_padding_scode *ps;
+ const u32 padding_size = 4096;
+ int ret;
+
+ ps = get_padding_scode(scode_type, pixelformat);
+ if (!ps)
+ return -EINVAL;
+
+ wptr = readl(&str_buf->wptr);
+ if (wptr < stream_buffer->phys || wptr > stream_buffer->phys + stream_buffer->length)
+ return -EINVAL;
+ if (wptr == stream_buffer->phys + stream_buffer->length)
+ wptr = stream_buffer->phys;
+ size = ALIGN(wptr, 4) - wptr;
+ if (size)
+ vpu_helper_memset_stream_buffer(stream_buffer, &wptr, 0, size);
+ total_size += size;
+
+ size = sizeof(ps->data);
+ ret = vpu_helper_copy_to_stream_buffer(stream_buffer, &wptr, size, (void *)ps->data);
+ if (ret < 0)
+ return -EINVAL;
+ total_size += size;
+
+ size = padding_size - sizeof(ps->data);
+ vpu_helper_memset_stream_buffer(stream_buffer, &wptr, 0, size);
+ total_size += size;
+
+ vpu_malone_update_wptr(str_buf, wptr);
+ return total_size;
+}
+
+int vpu_malone_add_scode(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_buffer *stream_buffer,
+ u32 pixelformat,
+ u32 scode_type)
+{
+ struct vpu_dec_ctrl *hc = shared->priv;
+ struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[instance];
+ int ret = -EINVAL;
+
+ switch (scode_type) {
+ case SCODE_PADDING_EOS:
+ case SCODE_PADDING_ABORT:
+ case SCODE_PADDING_BUFFLUSH:
+ ret = vpu_malone_add_padding_scode(stream_buffer, str_buf, pixelformat, scode_type);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+#define MALONE_PAYLOAD_HEADER_SIZE 16
+#define MALONE_CODEC_VERSION_ID 0x1
+#define MALONE_CODEC_ID_VC1_SIMPLE 0x10
+#define MALONE_CODEC_ID_VC1_MAIN 0x11
+#define MALONE_CODEC_ID_ARV8 0x28
+#define MALONE_CODEC_ID_ARV9 0x29
+#define MALONE_CODEC_ID_VP6 0x36
+#define MALONE_CODEC_ID_VP8 0x36
+#define MALONE_CODEC_ID_DIVX3 0x38
+#define MALONE_CODEC_ID_SPK 0x39
+
+#define MALONE_VP8_IVF_SEQ_HEADER_LEN 32
+#define MALONE_VP8_IVF_FRAME_HEADER_LEN 8
+
+#define MALONE_VC1_RCV_CODEC_V1_VERSION 0x85
+#define MALONE_VC1_RCV_CODEC_V2_VERSION 0xC5
+#define MALONE_VC1_RCV_NUM_FRAMES 0xFF
+#define MALONE_VC1_RCV_SEQ_EXT_DATA_SIZE 4
+#define MALONE_VC1_RCV_SEQ_HEADER_LEN 20
+#define MALONE_VC1_RCV_PIC_HEADER_LEN 4
+#define MALONE_VC1_NAL_HEADER_LEN 4
+#define MALONE_VC1_CONTAIN_NAL(data) (((data) & 0x00FFFFFF) == 0x00010000)
+
+static void set_payload_hdr(u8 *dst, u32 scd_type, u32 codec_id,
+ u32 buffer_size, u32 width, u32 height)
+{
+ unsigned int payload_size;
+ /* payload_size = buffer_size + itself_size(16) - start_code(4) */
+ payload_size = buffer_size + 12;
+
+ dst[0] = 0x00;
+ dst[1] = 0x00;
+ dst[2] = 0x01;
+ dst[3] = scd_type;
+
+ /* length */
+ dst[4] = ((payload_size >> 16) & 0xff);
+ dst[5] = ((payload_size >> 8) & 0xff);
+ dst[6] = 0x4e;
+ dst[7] = ((payload_size >> 0) & 0xff);
+
+ /* Codec ID and Version */
+ dst[8] = codec_id;
+ dst[9] = MALONE_CODEC_VERSION_ID;
+
+ /* width */
+ dst[10] = ((width >> 8) & 0xff);
+ dst[11] = ((width >> 0) & 0xff);
+ dst[12] = 0x58;
+
+ /* height */
+ dst[13] = ((height >> 8) & 0xff);
+ dst[14] = ((height >> 0) & 0xff);
+ dst[15] = 0x50;
+}
+
+static void set_vp8_ivf_seqhdr(u8 *dst, u32 width, u32 height)
+{
+ /* 0-3byte signature "DKIF" */
+ dst[0] = 0x44;
+ dst[1] = 0x4b;
+ dst[2] = 0x49;
+ dst[3] = 0x46;
+ /* 4-5byte version: should be 0*/
+ dst[4] = 0x00;
+ dst[5] = 0x00;
+ /* 6-7 length of Header */
+ dst[6] = MALONE_VP8_IVF_SEQ_HEADER_LEN;
+ dst[7] = MALONE_VP8_IVF_SEQ_HEADER_LEN >> 8;
+ /* 8-11 VP8 fourcc */
+ dst[8] = 0x56;
+ dst[9] = 0x50;
+ dst[10] = 0x38;
+ dst[11] = 0x30;
+ /* 12-13 width in pixels */
+ dst[12] = width;
+ dst[13] = width >> 8;
+ /* 14-15 height in pixels */
+ dst[14] = height;
+ dst[15] = height >> 8;
+ /* 16-19 frame rate */
+ dst[16] = 0xe8;
+ dst[17] = 0x03;
+ dst[18] = 0x00;
+ dst[19] = 0x00;
+ /* 20-23 time scale */
+ dst[20] = 0x01;
+ dst[21] = 0x00;
+ dst[22] = 0x00;
+ dst[23] = 0x00;
+ /* 24-27 number frames */
+ dst[24] = 0xdf;
+ dst[25] = 0xf9;
+ dst[26] = 0x09;
+ dst[27] = 0x00;
+ /* 28-31 reserved */
+}
+
+static void set_vp8_ivf_pichdr(u8 *dst, u32 frame_size)
+{
+ /*
+ * firmware just parse 64-bit timestamp(8 bytes).
+ * As not transfer timestamp to firmware, use default value(ZERO).
+ * No need to do anything here
+ */
+}
+
+static void set_vc1_rcv_seqhdr(u8 *dst, u8 *src, u32 width, u32 height)
+{
+ u32 frames = MALONE_VC1_RCV_NUM_FRAMES;
+ u32 ext_data_size = MALONE_VC1_RCV_SEQ_EXT_DATA_SIZE;
+
+ /* 0-2 Number of frames, used default value 0xFF */
+ dst[0] = frames;
+ dst[1] = frames >> 8;
+ dst[2] = frames >> 16;
+
+ /* 3 RCV version, used V1 */
+ dst[3] = MALONE_VC1_RCV_CODEC_V1_VERSION;
+
+ /* 4-7 extension data size */
+ dst[4] = ext_data_size;
+ dst[5] = ext_data_size >> 8;
+ dst[6] = ext_data_size >> 16;
+ dst[7] = ext_data_size >> 24;
+ /* 8-11 extension data */
+ dst[8] = src[0];
+ dst[9] = src[1];
+ dst[10] = src[2];
+ dst[11] = src[3];
+
+ /* height */
+ dst[12] = height;
+ dst[13] = (height >> 8) & 0xff;
+ dst[14] = (height >> 16) & 0xff;
+ dst[15] = (height >> 24) & 0xff;
+ /* width */
+ dst[16] = width;
+ dst[17] = (width >> 8) & 0xff;
+ dst[18] = (width >> 16) & 0xff;
+ dst[19] = (width >> 24) & 0xff;
+}
+
+static void set_vc1_rcv_pichdr(u8 *dst, u32 buffer_size)
+{
+ dst[0] = buffer_size;
+ dst[1] = buffer_size >> 8;
+ dst[2] = buffer_size >> 16;
+ dst[3] = buffer_size >> 24;
+}
+
+static void create_vc1_nal_pichdr(u8 *dst)
+{
+ /* need insert nal header: special ID */
+ dst[0] = 0x0;
+ dst[1] = 0x0;
+ dst[2] = 0x01;
+ dst[3] = 0x0D;
+}
+
+static int vpu_malone_insert_scode_seq(struct malone_scode_t *scode, u32 codec_id, u32 ext_size)
+{
+ u8 hdr[MALONE_PAYLOAD_HEADER_SIZE];
+ int ret;
+
+ set_payload_hdr(hdr,
+ SCODE_SEQUENCE,
+ codec_id,
+ ext_size,
+ scode->inst->out_format.width,
+ scode->inst->out_format.height);
+ ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer,
+ &scode->wptr,
+ sizeof(hdr),
+ hdr);
+ if (ret < 0)
+ return ret;
+ return sizeof(hdr);
+}
+
+static int vpu_malone_insert_scode_pic(struct malone_scode_t *scode, u32 codec_id, u32 ext_size)
+{
+ u8 hdr[MALONE_PAYLOAD_HEADER_SIZE];
+ int ret;
+
+ set_payload_hdr(hdr,
+ SCODE_PICTURE,
+ codec_id,
+ ext_size + vb2_get_plane_payload(scode->vb, 0),
+ scode->inst->out_format.width,
+ scode->inst->out_format.height);
+ ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer,
+ &scode->wptr,
+ sizeof(hdr),
+ hdr);
+ if (ret < 0)
+ return ret;
+ return sizeof(hdr);
+}
+
+static int vpu_malone_insert_scode_vc1_g_pic(struct malone_scode_t *scode)
+{
+ struct vb2_v4l2_buffer *vbuf;
+ u8 nal_hdr[MALONE_VC1_NAL_HEADER_LEN];
+ u32 *data = NULL;
+ int ret;
+
+ vbuf = to_vb2_v4l2_buffer(scode->vb);
+ data = vb2_plane_vaddr(scode->vb, 0);
+
+ if (vbuf->sequence == 0 || vpu_vb_is_codecconfig(vbuf))
+ return 0;
+ if (MALONE_VC1_CONTAIN_NAL(*data))
+ return 0;
+
+ create_vc1_nal_pichdr(nal_hdr);
+ ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer,
+ &scode->wptr,
+ sizeof(nal_hdr),
+ nal_hdr);
+ if (ret < 0)
+ return ret;
+ return sizeof(nal_hdr);
+}
+
+static int vpu_malone_insert_scode_vc1_l_seq(struct malone_scode_t *scode)
+{
+ int ret;
+ int size = 0;
+ u8 rcv_seqhdr[MALONE_VC1_RCV_SEQ_HEADER_LEN];
+
+ scode->need_data = 0;
+
+ ret = vpu_malone_insert_scode_seq(scode, MALONE_CODEC_ID_VC1_SIMPLE, sizeof(rcv_seqhdr));
+ if (ret < 0)
+ return ret;
+ size = ret;
+
+ set_vc1_rcv_seqhdr(rcv_seqhdr,
+ vb2_plane_vaddr(scode->vb, 0),
+ scode->inst->out_format.width,
+ scode->inst->out_format.height);
+ ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer,
+ &scode->wptr,
+ sizeof(rcv_seqhdr),
+ rcv_seqhdr);
+
+ if (ret < 0)
+ return ret;
+ size += sizeof(rcv_seqhdr);
+ return size;
+}
+
+static int vpu_malone_insert_scode_vc1_l_pic(struct malone_scode_t *scode)
+{
+ int ret;
+ int size = 0;
+ u8 rcv_pichdr[MALONE_VC1_RCV_PIC_HEADER_LEN];
+
+ ret = vpu_malone_insert_scode_pic(scode, MALONE_CODEC_ID_VC1_SIMPLE,
+ sizeof(rcv_pichdr));
+ if (ret < 0)
+ return ret;
+ size = ret;
+
+ set_vc1_rcv_pichdr(rcv_pichdr, vb2_get_plane_payload(scode->vb, 0));
+ ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer,
+ &scode->wptr,
+ sizeof(rcv_pichdr),
+ rcv_pichdr);
+ if (ret < 0)
+ return ret;
+ size += sizeof(rcv_pichdr);
+ return size;
+}
+
+static int vpu_malone_insert_scode_vp8_seq(struct malone_scode_t *scode)
+{
+ int ret;
+ int size = 0;
+ u8 ivf_hdr[MALONE_VP8_IVF_SEQ_HEADER_LEN];
+
+ ret = vpu_malone_insert_scode_seq(scode, MALONE_CODEC_ID_VP8, sizeof(ivf_hdr));
+ if (ret < 0)
+ return ret;
+ size = ret;
+
+ set_vp8_ivf_seqhdr(ivf_hdr,
+ scode->inst->out_format.width,
+ scode->inst->out_format.height);
+ ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer,
+ &scode->wptr,
+ sizeof(ivf_hdr),
+ ivf_hdr);
+ if (ret < 0)
+ return ret;
+ size += sizeof(ivf_hdr);
+
+ return size;
+}
+
+static int vpu_malone_insert_scode_vp8_pic(struct malone_scode_t *scode)
+{
+ int ret;
+ int size = 0;
+ u8 ivf_hdr[MALONE_VP8_IVF_FRAME_HEADER_LEN] = {0};
+
+ ret = vpu_malone_insert_scode_pic(scode, MALONE_CODEC_ID_VP8, sizeof(ivf_hdr));
+ if (ret < 0)
+ return ret;
+ size = ret;
+
+ set_vp8_ivf_pichdr(ivf_hdr, vb2_get_plane_payload(scode->vb, 0));
+ ret = vpu_helper_copy_to_stream_buffer(&scode->inst->stream_buffer,
+ &scode->wptr,
+ sizeof(ivf_hdr),
+ ivf_hdr);
+ if (ret < 0)
+ return ret;
+ size += sizeof(ivf_hdr);
+
+ return size;
+}
+
+static const struct malone_scode_handler scode_handlers[] = {
+ {
+ /* fix me, need to swap return operation after gstreamer swap */
+ .pixelformat = V4L2_PIX_FMT_VC1_ANNEX_L,
+ .insert_scode_seq = vpu_malone_insert_scode_vc1_l_seq,
+ .insert_scode_pic = vpu_malone_insert_scode_vc1_l_pic,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_VC1_ANNEX_G,
+ .insert_scode_pic = vpu_malone_insert_scode_vc1_g_pic,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_VP8,
+ .insert_scode_seq = vpu_malone_insert_scode_vp8_seq,
+ .insert_scode_pic = vpu_malone_insert_scode_vp8_pic,
+ },
+};
+
+static const struct malone_scode_handler *get_scode_handler(u32 pixelformat)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(scode_handlers); i++) {
+ if (scode_handlers[i].pixelformat == pixelformat)
+ return &scode_handlers[i];
+ }
+
+ return NULL;
+}
+
+static int vpu_malone_insert_scode(struct malone_scode_t *scode, u32 type)
+{
+ const struct malone_scode_handler *handler;
+ int ret = 0;
+
+ if (!scode || !scode->inst || !scode->vb)
+ return 0;
+
+ scode->need_data = 1;
+ handler = get_scode_handler(scode->inst->out_format.pixfmt);
+ if (!handler)
+ return 0;
+
+ switch (type) {
+ case SCODE_SEQUENCE:
+ if (handler->insert_scode_seq)
+ ret = handler->insert_scode_seq(scode);
+ break;
+ case SCODE_PICTURE:
+ if (handler->insert_scode_pic)
+ ret = handler->insert_scode_pic(scode);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str_buf,
+ struct vpu_inst *inst, struct vb2_buffer *vb,
+ u32 disp_imm)
+{
+ struct malone_scode_t scode;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ u32 wptr = readl(&str_buf->wptr);
+ int size = 0;
+ int ret = 0;
+
+ /*add scode: SCODE_SEQUENCE, SCODE_PICTURE, SCODE_SLICE*/
+ scode.inst = inst;
+ scode.vb = vb;
+ scode.wptr = wptr;
+ scode.need_data = 1;
+ if (vbuf->sequence == 0 || vpu_vb_is_codecconfig(vbuf))
+ ret = vpu_malone_insert_scode(&scode, SCODE_SEQUENCE);
+
+ if (ret < 0)
+ return -ENOMEM;
+ size += ret;
+ wptr = scode.wptr;
+ if (!scode.need_data) {
+ vpu_malone_update_wptr(str_buf, wptr);
+ return size;
+ }
+
+ ret = vpu_malone_insert_scode(&scode, SCODE_PICTURE);
+ if (ret < 0)
+ return -ENOMEM;
+ size += ret;
+ wptr = scode.wptr;
+
+ ret = vpu_helper_copy_to_stream_buffer(&inst->stream_buffer,
+ &wptr,
+ vb2_get_plane_payload(vb, 0),
+ vb2_plane_vaddr(vb, 0));
+ if (ret < 0)
+ return -ENOMEM;
+ size += vb2_get_plane_payload(vb, 0);
+
+ vpu_malone_update_wptr(str_buf, wptr);
+
+ if (disp_imm && !vpu_vb_is_codecconfig(vbuf)) {
+ ret = vpu_malone_add_scode(inst->core->iface,
+ inst->id,
+ &inst->stream_buffer,
+ inst->out_format.pixfmt,
+ SCODE_PADDING_BUFFLUSH);
+ if (ret < 0)
+ return ret;
+ size += ret;
+ }
+
+ return size;
+}
+
+static int vpu_malone_input_stream_data(struct vpu_malone_str_buffer __iomem *str_buf,
+ struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ u32 wptr = readl(&str_buf->wptr);
+ int ret = 0;
+
+ ret = vpu_helper_copy_to_stream_buffer(&inst->stream_buffer,
+ &wptr,
+ vb2_get_plane_payload(vb, 0),
+ vb2_plane_vaddr(vb, 0));
+ if (ret < 0)
+ return -ENOMEM;
+
+ vpu_malone_update_wptr(str_buf, wptr);
+
+ return ret;
+}
+
+static int vpu_malone_input_ts(struct vpu_inst *inst, s64 timestamp, u32 size)
+{
+ struct vpu_ts_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.timestamp = timestamp;
+ info.size = size;
+
+ return vpu_session_fill_timestamp(inst, &info);
+}
+
+int vpu_malone_input_frame(struct vpu_shared_addr *shared,
+ struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct vpu_dec_ctrl *hc = shared->priv;
+ struct vb2_v4l2_buffer *vbuf;
+ struct vpu_malone_str_buffer __iomem *str_buf = hc->str_buf[inst->id];
+ u32 disp_imm = hc->codec_param[inst->id].disp_imm;
+ u32 size;
+ int ret;
+
+ if (vpu_malone_is_non_frame_mode(shared, inst->id))
+ ret = vpu_malone_input_stream_data(str_buf, inst, vb);
+ else
+ ret = vpu_malone_input_frame_data(str_buf, inst, vb, disp_imm);
+ if (ret < 0)
+ return ret;
+ size = ret;
+
+ /*
+ * if buffer only contain codec data, and the timestamp is invalid,
+ * don't put the invalid timestamp to resync
+ * merge the data to next frame
+ */
+ vbuf = to_vb2_v4l2_buffer(vb);
+ if (vpu_vb_is_codecconfig(vbuf) && (s64)vb->timestamp < 0) {
+ inst->extra_size += size;
+ return 0;
+ }
+ if (inst->extra_size) {
+ size += inst->extra_size;
+ inst->extra_size = 0;
+ }
+
+ ret = vpu_malone_input_ts(inst, vb->timestamp, size);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static bool vpu_malone_check_ready(struct vpu_shared_addr *shared, u32 instance)
+{
+ struct malone_iface *iface = shared->iface;
+ struct vpu_rpc_buffer_desc *desc = &iface->api_cmd_buffer_desc[instance];
+ u32 size = desc->end - desc->start;
+ u32 rptr = desc->rptr;
+ u32 wptr = desc->wptr;
+ u32 used;
+
+ if (!size)
+ return true;
+
+ used = (wptr + size - rptr) % size;
+ if (used < (size / 2))
+ return true;
+
+ return false;
+}
+
+bool vpu_malone_is_ready(struct vpu_shared_addr *shared, u32 instance)
+{
+ u32 cnt = 0;
+
+ while (!vpu_malone_check_ready(shared, instance)) {
+ if (cnt > 30)
+ return false;
+ mdelay(1);
+ cnt++;
+ }
+ return true;
+}
+
+int vpu_malone_pre_cmd(struct vpu_shared_addr *shared, u32 instance)
+{
+ if (!vpu_malone_is_ready(shared, instance))
+ return -EINVAL;
+
+ return 0;
+}
+
+int vpu_malone_post_cmd(struct vpu_shared_addr *shared, u32 instance)
+{
+ struct malone_iface *iface = shared->iface;
+ struct vpu_rpc_buffer_desc *desc = &iface->api_cmd_buffer_desc[instance];
+
+ desc->wptr++;
+ if (desc->wptr == desc->end)
+ desc->wptr = desc->start;
+
+ return 0;
+}
+
+int vpu_malone_init_instance(struct vpu_shared_addr *shared, u32 instance)
+{
+ struct malone_iface *iface = shared->iface;
+ struct vpu_rpc_buffer_desc *desc = &iface->api_cmd_buffer_desc[instance];
+
+ desc->wptr = desc->rptr;
+ if (desc->wptr == desc->end)
+ desc->wptr = desc->start;
+
+ return 0;
+}
+
+u32 vpu_malone_get_max_instance_count(struct vpu_shared_addr *shared)
+{
+ struct malone_iface *iface = shared->iface;
+
+ return iface->max_streams;
+}
diff --git a/drivers/media/platform/amphion/vpu_malone.h b/drivers/media/platform/amphion/vpu_malone.h
new file mode 100644
index 000000000000..e5a5cbe9843e
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_malone.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_MALONE_H
+#define _AMPHION_VPU_MALONE_H
+
+u32 vpu_malone_get_data_size(void);
+void vpu_malone_init_rpc(struct vpu_shared_addr *shared,
+ struct vpu_buffer *rpc, dma_addr_t boot_addr);
+void vpu_malone_set_log_buf(struct vpu_shared_addr *shared,
+ struct vpu_buffer *log);
+void vpu_malone_set_system_cfg(struct vpu_shared_addr *shared,
+ u32 regs_base, void __iomem *regs, u32 core_id);
+u32 vpu_malone_get_version(struct vpu_shared_addr *shared);
+int vpu_malone_get_stream_buffer_size(struct vpu_shared_addr *shared);
+int vpu_malone_config_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_buffer *buf);
+int vpu_malone_get_stream_buffer_desc(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_rpc_buffer_desc *desc);
+int vpu_malone_update_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance, u32 ptr, bool write);
+int vpu_malone_set_decode_params(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_decode_params *params, u32 update);
+int vpu_malone_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data);
+int vpu_malone_convert_msg_id(u32 msg_id);
+int vpu_malone_unpack_msg_data(struct vpu_rpc_event *pkt, void *data);
+int vpu_malone_add_scode(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_buffer *stream_buffer,
+ u32 pixelformat,
+ u32 scode_type);
+int vpu_malone_input_frame(struct vpu_shared_addr *shared,
+ struct vpu_inst *inst, struct vb2_buffer *vb);
+bool vpu_malone_is_ready(struct vpu_shared_addr *shared, u32 instance);
+int vpu_malone_pre_cmd(struct vpu_shared_addr *shared, u32 instance);
+int vpu_malone_post_cmd(struct vpu_shared_addr *shared, u32 instance);
+int vpu_malone_init_instance(struct vpu_shared_addr *shared, u32 instance);
+u32 vpu_malone_get_max_instance_count(struct vpu_shared_addr *shared);
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_mbox.c b/drivers/media/platform/amphion/vpu_mbox.c
new file mode 100644
index 000000000000..bf759eb2fd46
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_mbox.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include "vpu.h"
+#include "vpu_mbox.h"
+#include "vpu_msgs.h"
+
+static void vpu_mbox_rx_callback(struct mbox_client *cl, void *msg)
+{
+ struct vpu_mbox *rx = container_of(cl, struct vpu_mbox, cl);
+ struct vpu_core *core = container_of(rx, struct vpu_core, rx);
+
+ vpu_isr(core, *(u32 *)msg);
+}
+
+static int vpu_mbox_request_channel(struct device *dev, struct vpu_mbox *mbox)
+{
+ struct mbox_chan *ch;
+ struct mbox_client *cl;
+
+ if (!dev || !mbox)
+ return -EINVAL;
+ if (mbox->ch)
+ return 0;
+
+ cl = &mbox->cl;
+ cl->dev = dev;
+ if (mbox->block) {
+ cl->tx_block = true;
+ cl->tx_tout = 1000;
+ } else {
+ cl->tx_block = false;
+ }
+ cl->knows_txdone = false;
+ cl->rx_callback = vpu_mbox_rx_callback;
+
+ ch = mbox_request_channel_byname(cl, mbox->name);
+ if (IS_ERR(ch)) {
+ dev_err(dev, "Failed to request mbox chan %s, ret : %ld\n",
+ mbox->name, PTR_ERR(ch));
+ return PTR_ERR(ch);
+ }
+
+ mbox->ch = ch;
+ return 0;
+}
+
+int vpu_mbox_init(struct vpu_core *core)
+{
+ scnprintf(core->tx_type.name, sizeof(core->tx_type.name) - 1, "tx0");
+ core->tx_type.block = true;
+
+ scnprintf(core->tx_data.name, sizeof(core->tx_data.name) - 1, "tx1");
+ core->tx_data.block = false;
+
+ scnprintf(core->rx.name, sizeof(core->rx.name) - 1, "rx");
+ core->rx.block = true;
+
+ return 0;
+}
+
+int vpu_mbox_request(struct vpu_core *core)
+{
+ int ret;
+
+ ret = vpu_mbox_request_channel(core->dev, &core->tx_type);
+ if (ret)
+ goto error;
+ ret = vpu_mbox_request_channel(core->dev, &core->tx_data);
+ if (ret)
+ goto error;
+ ret = vpu_mbox_request_channel(core->dev, &core->rx);
+ if (ret)
+ goto error;
+
+ dev_dbg(core->dev, "%s request mbox\n", vpu_core_type_desc(core->type));
+ return 0;
+error:
+ vpu_mbox_free(core);
+ return ret;
+}
+
+void vpu_mbox_free(struct vpu_core *core)
+{
+ mbox_free_channel(core->tx_type.ch);
+ mbox_free_channel(core->tx_data.ch);
+ mbox_free_channel(core->rx.ch);
+ core->tx_type.ch = NULL;
+ core->tx_data.ch = NULL;
+ core->rx.ch = NULL;
+ dev_dbg(core->dev, "%s free mbox\n", vpu_core_type_desc(core->type));
+}
+
+void vpu_mbox_send_type(struct vpu_core *core, u32 type)
+{
+ mbox_send_message(core->tx_type.ch, &type);
+}
+
+void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data)
+{
+ mbox_send_message(core->tx_data.ch, &data);
+ mbox_send_message(core->tx_type.ch, &type);
+}
+
+void vpu_mbox_enable_rx(struct vpu_dev *dev)
+{
+}
diff --git a/drivers/media/platform/amphion/vpu_mbox.h b/drivers/media/platform/amphion/vpu_mbox.h
new file mode 100644
index 000000000000..79cfd874e92b
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_mbox.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_MBOX_H
+#define _AMPHION_VPU_MBOX_H
+
+int vpu_mbox_init(struct vpu_core *core);
+int vpu_mbox_request(struct vpu_core *core);
+void vpu_mbox_free(struct vpu_core *core);
+void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data);
+void vpu_mbox_send_type(struct vpu_core *core, u32 type);
+void vpu_mbox_enable_rx(struct vpu_dev *dev);
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_msgs.c b/drivers/media/platform/amphion/vpu_msgs.c
new file mode 100644
index 000000000000..58502c51ddb3
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_msgs.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "vpu.h"
+#include "vpu_core.h"
+#include "vpu_rpc.h"
+#include "vpu_mbox.h"
+#include "vpu_defs.h"
+#include "vpu_cmds.h"
+#include "vpu_msgs.h"
+#include "vpu_v4l2.h"
+
+#define VPU_PKT_HEADER_LENGTH 3
+
+struct vpu_msg_handler {
+ u32 id;
+ void (*done)(struct vpu_inst *inst, struct vpu_rpc_event *pkt);
+};
+
+static void vpu_session_handle_start_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+}
+
+static void vpu_session_handle_mem_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ struct vpu_pkt_mem_req_data req_data;
+
+ vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&req_data);
+ vpu_trace(inst->dev, "[%d] %d:%d %d:%d %d:%d\n",
+ inst->id,
+ req_data.enc_frame_size,
+ req_data.enc_frame_num,
+ req_data.ref_frame_size,
+ req_data.ref_frame_num,
+ req_data.act_buf_size,
+ req_data.act_buf_num);
+ call_void_vop(inst, mem_request,
+ req_data.enc_frame_size,
+ req_data.enc_frame_num,
+ req_data.ref_frame_size,
+ req_data.ref_frame_num,
+ req_data.act_buf_size,
+ req_data.act_buf_num);
+}
+
+static void vpu_session_handle_stop_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+
+ call_void_vop(inst, stop_done);
+}
+
+static void vpu_session_handle_seq_hdr(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ struct vpu_dec_codec_info info;
+ const struct vpu_core_resources *res;
+
+ memset(&info, 0, sizeof(info));
+ res = vpu_get_resource(inst);
+ info.stride = res ? res->stride : 1;
+ vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
+ call_void_vop(inst, event_notify, VPU_MSG_ID_SEQ_HDR_FOUND, &info);
+}
+
+static void vpu_session_handle_resolution_change(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ call_void_vop(inst, event_notify, VPU_MSG_ID_RES_CHANGE, NULL);
+}
+
+static void vpu_session_handle_enc_frame_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ struct vpu_enc_pic_info info;
+
+ vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
+ dev_dbg(inst->dev, "[%d] frame id = %d, wptr = 0x%x, size = %d\n",
+ inst->id, info.frame_id, info.wptr, info.frame_size);
+ call_void_vop(inst, get_one_frame, &info);
+}
+
+static void vpu_session_handle_frame_request(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ struct vpu_fs_info fs;
+
+ vpu_iface_unpack_msg_data(inst->core, pkt, &fs);
+ call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_REQ, &fs);
+}
+
+static void vpu_session_handle_frame_release(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ if (inst->core->type == VPU_CORE_TYPE_ENC) {
+ struct vpu_frame_info info;
+
+ memset(&info, 0, sizeof(info));
+ vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info.sequence);
+ dev_dbg(inst->dev, "[%d] %d\n", inst->id, info.sequence);
+ info.type = inst->out_format.type;
+ call_void_vop(inst, buf_done, &info);
+ } else if (inst->core->type == VPU_CORE_TYPE_DEC) {
+ struct vpu_fs_info fs;
+
+ vpu_iface_unpack_msg_data(inst->core, pkt, &fs);
+ call_void_vop(inst, event_notify, VPU_MSG_ID_FRAME_RELEASE, &fs);
+ }
+}
+
+static void vpu_session_handle_input_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ dev_dbg(inst->dev, "[%d]\n", inst->id);
+ call_void_vop(inst, input_done);
+}
+
+static void vpu_session_handle_pic_decoded(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ struct vpu_dec_pic_info info;
+
+ vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
+ call_void_vop(inst, get_one_frame, &info);
+}
+
+static void vpu_session_handle_pic_done(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ struct vpu_dec_pic_info info;
+ struct vpu_frame_info frame;
+
+ memset(&frame, 0, sizeof(frame));
+ vpu_iface_unpack_msg_data(inst->core, pkt, (void *)&info);
+ if (inst->core->type == VPU_CORE_TYPE_DEC)
+ frame.type = inst->cap_format.type;
+ frame.id = info.id;
+ frame.luma = info.luma;
+ frame.skipped = info.skipped;
+ frame.timestamp = info.timestamp;
+
+ call_void_vop(inst, buf_done, &frame);
+}
+
+static void vpu_session_handle_eos(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ call_void_vop(inst, event_notify, VPU_MSG_ID_PIC_EOS, NULL);
+}
+
+static void vpu_session_handle_error(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ dev_err(inst->dev, "unsupported stream\n");
+ call_void_vop(inst, event_notify, VPU_MSG_ID_UNSUPPORTED, NULL);
+ vpu_v4l2_set_error(inst);
+}
+
+static void vpu_session_handle_firmware_xcpt(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ char *str = (char *)pkt->data;
+
+ dev_err(inst->dev, "%s firmware xcpt: %s\n",
+ vpu_core_type_desc(inst->core->type), str);
+ call_void_vop(inst, event_notify, VPU_MSG_ID_FIRMWARE_XCPT, NULL);
+ set_bit(inst->id, &inst->core->hang_mask);
+ vpu_v4l2_set_error(inst);
+}
+
+static struct vpu_msg_handler handlers[] = {
+ {VPU_MSG_ID_START_DONE, vpu_session_handle_start_done},
+ {VPU_MSG_ID_STOP_DONE, vpu_session_handle_stop_done},
+ {VPU_MSG_ID_MEM_REQUEST, vpu_session_handle_mem_request},
+ {VPU_MSG_ID_SEQ_HDR_FOUND, vpu_session_handle_seq_hdr},
+ {VPU_MSG_ID_RES_CHANGE, vpu_session_handle_resolution_change},
+ {VPU_MSG_ID_FRAME_INPUT_DONE, vpu_session_handle_input_done},
+ {VPU_MSG_ID_FRAME_REQ, vpu_session_handle_frame_request},
+ {VPU_MSG_ID_FRAME_RELEASE, vpu_session_handle_frame_release},
+ {VPU_MSG_ID_ENC_DONE, vpu_session_handle_enc_frame_done},
+ {VPU_MSG_ID_PIC_DECODED, vpu_session_handle_pic_decoded},
+ {VPU_MSG_ID_DEC_DONE, vpu_session_handle_pic_done},
+ {VPU_MSG_ID_PIC_EOS, vpu_session_handle_eos},
+ {VPU_MSG_ID_UNSUPPORTED, vpu_session_handle_error},
+ {VPU_MSG_ID_FIRMWARE_XCPT, vpu_session_handle_firmware_xcpt},
+};
+
+static int vpu_session_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *msg)
+{
+ int ret;
+ u32 msg_id;
+ struct vpu_msg_handler *handler = NULL;
+ unsigned int i;
+
+ ret = vpu_iface_convert_msg_id(inst->core, msg->hdr.id);
+ if (ret < 0)
+ return -EINVAL;
+
+ msg_id = ret;
+ dev_dbg(inst->dev, "[%d] receive event(0x%x)\n", inst->id, msg_id);
+
+ for (i = 0; i < ARRAY_SIZE(handlers); i++) {
+ if (handlers[i].id == msg_id) {
+ handler = &handlers[i];
+ break;
+ }
+ }
+
+ if (handler && handler->done)
+ handler->done(inst, msg);
+
+ vpu_response_cmd(inst, msg_id, 1);
+
+ return 0;
+}
+
+static bool vpu_inst_receive_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ unsigned long bytes = sizeof(struct vpu_rpc_event_header);
+ u32 ret;
+
+ memset(pkt, 0, sizeof(*pkt));
+ if (kfifo_len(&inst->msg_fifo) < bytes)
+ return false;
+
+ ret = kfifo_out(&inst->msg_fifo, pkt, bytes);
+ if (ret != bytes)
+ return false;
+
+ if (pkt->hdr.num > 0) {
+ bytes = pkt->hdr.num * sizeof(u32);
+ ret = kfifo_out(&inst->msg_fifo, pkt->data, bytes);
+ if (ret != bytes)
+ return false;
+ }
+
+ return true;
+}
+
+void vpu_inst_run_work(struct work_struct *work)
+{
+ struct vpu_inst *inst = container_of(work, struct vpu_inst, msg_work);
+ struct vpu_rpc_event pkt;
+
+ while (vpu_inst_receive_msg(inst, &pkt))
+ vpu_session_handle_msg(inst, &pkt);
+}
+
+static void vpu_inst_handle_msg(struct vpu_inst *inst, struct vpu_rpc_event *pkt)
+{
+ unsigned long bytes;
+ u32 id = pkt->hdr.id;
+ int ret;
+
+ if (!inst->workqueue)
+ return;
+
+ bytes = sizeof(pkt->hdr) + pkt->hdr.num * sizeof(u32);
+ ret = kfifo_in(&inst->msg_fifo, pkt, bytes);
+ if (ret != bytes)
+ dev_err(inst->dev, "[%d:%d]overflow: %d\n", inst->core->id, inst->id, id);
+ queue_work(inst->workqueue, &inst->msg_work);
+}
+
+static int vpu_handle_msg(struct vpu_core *core)
+{
+ struct vpu_rpc_event pkt;
+ struct vpu_inst *inst;
+ int ret;
+
+ memset(&pkt, 0, sizeof(pkt));
+ while (!vpu_iface_receive_msg(core, &pkt)) {
+ dev_dbg(core->dev, "event index = %d, id = %d, num = %d\n",
+ pkt.hdr.index, pkt.hdr.id, pkt.hdr.num);
+
+ ret = vpu_iface_convert_msg_id(core, pkt.hdr.id);
+ if (ret < 0)
+ continue;
+
+ inst = vpu_core_find_instance(core, pkt.hdr.index);
+ if (inst) {
+ vpu_response_cmd(inst, ret, 0);
+ mutex_lock(&core->cmd_lock);
+ vpu_inst_record_flow(inst, ret);
+ mutex_unlock(&core->cmd_lock);
+
+ vpu_inst_handle_msg(inst, &pkt);
+ vpu_inst_put(inst);
+ }
+ memset(&pkt, 0, sizeof(pkt));
+ }
+
+ return 0;
+}
+
+static int vpu_isr_thread(struct vpu_core *core, u32 irq_code)
+{
+ dev_dbg(core->dev, "irq code = 0x%x\n", irq_code);
+ switch (irq_code) {
+ case VPU_IRQ_CODE_SYNC:
+ vpu_mbox_send_msg(core, PRC_BUF_OFFSET, core->rpc.phys - core->fw.phys);
+ vpu_mbox_send_msg(core, BOOT_ADDRESS, core->fw.phys);
+ vpu_mbox_send_msg(core, INIT_DONE, 2);
+ break;
+ case VPU_IRQ_CODE_BOOT_DONE:
+ break;
+ case VPU_IRQ_CODE_SNAPSHOT_DONE:
+ break;
+ default:
+ vpu_handle_msg(core);
+ break;
+ }
+
+ return 0;
+}
+
+static void vpu_core_run_msg_work(struct vpu_core *core)
+{
+ const unsigned int SIZE = sizeof(u32);
+
+ while (kfifo_len(&core->msg_fifo) >= SIZE) {
+ u32 data = 0;
+
+ if (kfifo_out(&core->msg_fifo, &data, SIZE) == SIZE)
+ vpu_isr_thread(core, data);
+ }
+}
+
+void vpu_msg_run_work(struct work_struct *work)
+{
+ struct vpu_core *core = container_of(work, struct vpu_core, msg_work);
+ unsigned long delay = msecs_to_jiffies(10);
+
+ vpu_core_run_msg_work(core);
+ queue_delayed_work(core->workqueue, &core->msg_delayed_work, delay);
+}
+
+void vpu_msg_delayed_work(struct work_struct *work)
+{
+ struct vpu_core *core;
+ struct delayed_work *dwork;
+ unsigned long bytes = sizeof(u32);
+ u32 i;
+
+ if (!work)
+ return;
+
+ dwork = to_delayed_work(work);
+ core = container_of(dwork, struct vpu_core, msg_delayed_work);
+ if (kfifo_len(&core->msg_fifo) >= bytes)
+ vpu_core_run_msg_work(core);
+
+ bytes = sizeof(struct vpu_rpc_event_header);
+ for (i = 0; i < core->supported_instance_count; i++) {
+ struct vpu_inst *inst = vpu_core_find_instance(core, i);
+
+ if (!inst)
+ continue;
+
+ if (inst->workqueue && kfifo_len(&inst->msg_fifo) >= bytes)
+ queue_work(inst->workqueue, &inst->msg_work);
+
+ vpu_inst_put(inst);
+ }
+}
+
+int vpu_isr(struct vpu_core *core, u32 irq)
+{
+ switch (irq) {
+ case VPU_IRQ_CODE_SYNC:
+ break;
+ case VPU_IRQ_CODE_BOOT_DONE:
+ complete(&core->cmp);
+ break;
+ case VPU_IRQ_CODE_SNAPSHOT_DONE:
+ complete(&core->cmp);
+ break;
+ default:
+ break;
+ }
+
+ if (kfifo_in(&core->msg_fifo, &irq, sizeof(irq)) != sizeof(irq))
+ dev_err(core->dev, "[%d]overflow: %d\n", core->id, irq);
+ queue_work(core->workqueue, &core->msg_work);
+
+ return 0;
+}
diff --git a/drivers/media/platform/amphion/vpu_msgs.h b/drivers/media/platform/amphion/vpu_msgs.h
new file mode 100644
index 000000000000..c466b4f62aad
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_msgs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_MSGS_H
+#define _AMPHION_VPU_MSGS_H
+
+int vpu_isr(struct vpu_core *core, u32 irq);
+void vpu_inst_run_work(struct work_struct *work);
+void vpu_msg_run_work(struct work_struct *work);
+void vpu_msg_delayed_work(struct work_struct *work);
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_rpc.c b/drivers/media/platform/amphion/vpu_rpc.c
new file mode 100644
index 000000000000..18a164766409
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_rpc.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/firmware/imx/ipc.h>
+#include <linux/firmware/imx/svc/misc.h>
+#include "vpu.h"
+#include "vpu_rpc.h"
+#include "vpu_imx8q.h"
+#include "vpu_windsor.h"
+#include "vpu_malone.h"
+
+int vpu_iface_check_memory_region(struct vpu_core *core, dma_addr_t addr, u32 size)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->check_memory_region)
+ return VPU_CORE_MEMORY_INVALID;
+
+ return ops->check_memory_region(core->fw.phys, addr, size);
+}
+
+static u32 vpu_rpc_check_buffer_space(struct vpu_rpc_buffer_desc *desc, bool write)
+{
+ u32 ptr1;
+ u32 ptr2;
+ u32 size;
+
+ size = desc->end - desc->start;
+ if (write) {
+ ptr1 = desc->wptr;
+ ptr2 = desc->rptr;
+ } else {
+ ptr1 = desc->rptr;
+ ptr2 = desc->wptr;
+ }
+
+ if (ptr1 == ptr2) {
+ if (!write)
+ return 0;
+ else
+ return size;
+ }
+
+ return (ptr2 + size - ptr1) % size;
+}
+
+static int vpu_rpc_send_cmd_buf(struct vpu_shared_addr *shared, struct vpu_rpc_event *cmd)
+{
+ struct vpu_rpc_buffer_desc *desc;
+ u32 space = 0;
+ u32 *data;
+ u32 wptr;
+ u32 i;
+
+ if (cmd->hdr.num > 0xff || cmd->hdr.num >= ARRAY_SIZE(cmd->data))
+ return -EINVAL;
+ desc = shared->cmd_desc;
+ space = vpu_rpc_check_buffer_space(desc, true);
+ if (space < (((cmd->hdr.num + 1) << 2) + 16))
+ return -EINVAL;
+ wptr = desc->wptr;
+ data = (u32 *)(shared->cmd_mem_vir + desc->wptr - desc->start);
+ *data = 0;
+ *data |= ((cmd->hdr.index & 0xff) << 24);
+ *data |= ((cmd->hdr.num & 0xff) << 16);
+ *data |= (cmd->hdr.id & 0x3fff);
+ wptr += 4;
+ data++;
+ if (wptr >= desc->end) {
+ wptr = desc->start;
+ data = shared->cmd_mem_vir;
+ }
+
+ for (i = 0; i < cmd->hdr.num; i++) {
+ *data = cmd->data[i];
+ wptr += 4;
+ data++;
+ if (wptr >= desc->end) {
+ wptr = desc->start;
+ data = shared->cmd_mem_vir;
+ }
+ }
+
+ /*update wptr after data is written*/
+ mb();
+ desc->wptr = wptr;
+
+ return 0;
+}
+
+static bool vpu_rpc_check_msg(struct vpu_shared_addr *shared)
+{
+ struct vpu_rpc_buffer_desc *desc;
+ u32 space = 0;
+ u32 msgword;
+ u32 msgnum;
+
+ desc = shared->msg_desc;
+ space = vpu_rpc_check_buffer_space(desc, 0);
+ space = (space >> 2);
+
+ if (space) {
+ msgword = *(u32 *)(shared->msg_mem_vir + desc->rptr - desc->start);
+ msgnum = (msgword & 0xff0000) >> 16;
+ if (msgnum <= space)
+ return true;
+ }
+
+ return false;
+}
+
+static int vpu_rpc_receive_msg_buf(struct vpu_shared_addr *shared, struct vpu_rpc_event *msg)
+{
+ struct vpu_rpc_buffer_desc *desc;
+ u32 *data;
+ u32 msgword;
+ u32 rptr;
+ u32 i;
+
+ if (!vpu_rpc_check_msg(shared))
+ return -EINVAL;
+
+ desc = shared->msg_desc;
+ data = (u32 *)(shared->msg_mem_vir + desc->rptr - desc->start);
+ rptr = desc->rptr;
+ msgword = *data;
+ data++;
+ rptr += 4;
+ if (rptr >= desc->end) {
+ rptr = desc->start;
+ data = shared->msg_mem_vir;
+ }
+
+ msg->hdr.index = (msgword >> 24) & 0xff;
+ msg->hdr.num = (msgword >> 16) & 0xff;
+ msg->hdr.id = msgword & 0x3fff;
+
+ if (msg->hdr.num > ARRAY_SIZE(msg->data))
+ return -EINVAL;
+
+ for (i = 0; i < msg->hdr.num; i++) {
+ msg->data[i] = *data;
+ data++;
+ rptr += 4;
+ if (rptr >= desc->end) {
+ rptr = desc->start;
+ data = shared->msg_mem_vir;
+ }
+ }
+
+ /*update rptr after data is read*/
+ mb();
+ desc->rptr = rptr;
+
+ return 0;
+}
+
+static struct vpu_iface_ops imx8q_rpc_ops[] = {
+ [VPU_CORE_TYPE_ENC] = {
+ .check_codec = vpu_imx8q_check_codec,
+ .check_fmt = vpu_imx8q_check_fmt,
+ .boot_core = vpu_imx8q_boot_core,
+ .get_power_state = vpu_imx8q_get_power_state,
+ .on_firmware_loaded = vpu_imx8q_on_firmware_loaded,
+ .get_data_size = vpu_windsor_get_data_size,
+ .check_memory_region = vpu_imx8q_check_memory_region,
+ .init_rpc = vpu_windsor_init_rpc,
+ .set_log_buf = vpu_windsor_set_log_buf,
+ .set_system_cfg = vpu_windsor_set_system_cfg,
+ .get_version = vpu_windsor_get_version,
+ .send_cmd_buf = vpu_rpc_send_cmd_buf,
+ .receive_msg_buf = vpu_rpc_receive_msg_buf,
+ .pack_cmd = vpu_windsor_pack_cmd,
+ .convert_msg_id = vpu_windsor_convert_msg_id,
+ .unpack_msg_data = vpu_windsor_unpack_msg_data,
+ .config_memory_resource = vpu_windsor_config_memory_resource,
+ .get_stream_buffer_size = vpu_windsor_get_stream_buffer_size,
+ .config_stream_buffer = vpu_windsor_config_stream_buffer,
+ .get_stream_buffer_desc = vpu_windsor_get_stream_buffer_desc,
+ .update_stream_buffer = vpu_windsor_update_stream_buffer,
+ .set_encode_params = vpu_windsor_set_encode_params,
+ .input_frame = vpu_windsor_input_frame,
+ .get_max_instance_count = vpu_windsor_get_max_instance_count,
+ },
+ [VPU_CORE_TYPE_DEC] = {
+ .check_codec = vpu_imx8q_check_codec,
+ .check_fmt = vpu_imx8q_check_fmt,
+ .boot_core = vpu_imx8q_boot_core,
+ .get_power_state = vpu_imx8q_get_power_state,
+ .on_firmware_loaded = vpu_imx8q_on_firmware_loaded,
+ .get_data_size = vpu_malone_get_data_size,
+ .check_memory_region = vpu_imx8q_check_memory_region,
+ .init_rpc = vpu_malone_init_rpc,
+ .set_log_buf = vpu_malone_set_log_buf,
+ .set_system_cfg = vpu_malone_set_system_cfg,
+ .get_version = vpu_malone_get_version,
+ .send_cmd_buf = vpu_rpc_send_cmd_buf,
+ .receive_msg_buf = vpu_rpc_receive_msg_buf,
+ .get_stream_buffer_size = vpu_malone_get_stream_buffer_size,
+ .config_stream_buffer = vpu_malone_config_stream_buffer,
+ .set_decode_params = vpu_malone_set_decode_params,
+ .pack_cmd = vpu_malone_pack_cmd,
+ .convert_msg_id = vpu_malone_convert_msg_id,
+ .unpack_msg_data = vpu_malone_unpack_msg_data,
+ .get_stream_buffer_desc = vpu_malone_get_stream_buffer_desc,
+ .update_stream_buffer = vpu_malone_update_stream_buffer,
+ .add_scode = vpu_malone_add_scode,
+ .input_frame = vpu_malone_input_frame,
+ .pre_send_cmd = vpu_malone_pre_cmd,
+ .post_send_cmd = vpu_malone_post_cmd,
+ .init_instance = vpu_malone_init_instance,
+ .get_max_instance_count = vpu_malone_get_max_instance_count,
+ },
+};
+
+static struct vpu_iface_ops *vpu_get_iface(struct vpu_dev *vpu, enum vpu_core_type type)
+{
+ struct vpu_iface_ops *rpc_ops = NULL;
+ u32 size = 0;
+
+ switch (vpu->res->plat_type) {
+ case IMX8QXP:
+ case IMX8QM:
+ rpc_ops = imx8q_rpc_ops;
+ size = ARRAY_SIZE(imx8q_rpc_ops);
+ break;
+ default:
+ return NULL;
+ }
+
+ if (type >= size)
+ return NULL;
+
+ return &rpc_ops[type];
+}
+
+struct vpu_iface_ops *vpu_core_get_iface(struct vpu_core *core)
+{
+ return vpu_get_iface(core->vpu, core->type);
+}
+
+struct vpu_iface_ops *vpu_inst_get_iface(struct vpu_inst *inst)
+{
+ if (inst->core)
+ return vpu_core_get_iface(inst->core);
+
+ return vpu_get_iface(inst->vpu, inst->type);
+}
diff --git a/drivers/media/platform/amphion/vpu_rpc.h b/drivers/media/platform/amphion/vpu_rpc.h
new file mode 100644
index 000000000000..25119e5e807e
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_rpc.h
@@ -0,0 +1,461 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_RPC_H
+#define _AMPHION_VPU_RPC_H
+
+#include <media/videobuf2-core.h>
+#include "vpu_codec.h"
+
+struct vpu_rpc_buffer_desc {
+ u32 wptr;
+ u32 rptr;
+ u32 start;
+ u32 end;
+};
+
+struct vpu_shared_addr {
+ void *iface;
+ struct vpu_rpc_buffer_desc *cmd_desc;
+ void *cmd_mem_vir;
+ struct vpu_rpc_buffer_desc *msg_desc;
+ void *msg_mem_vir;
+
+ unsigned long boot_addr;
+ struct vpu_core *core;
+ void *priv;
+};
+
+struct vpu_rpc_event_header {
+ u32 index;
+ u32 id;
+ u32 num;
+};
+
+struct vpu_rpc_event {
+ struct vpu_rpc_event_header hdr;
+ u32 data[128];
+};
+
+struct vpu_iface_ops {
+ bool (*check_codec)(enum vpu_core_type type);
+ bool (*check_fmt)(enum vpu_core_type type, u32 pixelfmt);
+ u32 (*get_data_size)(void);
+ int (*check_memory_region)(dma_addr_t base, dma_addr_t addr, u32 size);
+ int (*boot_core)(struct vpu_core *core);
+ int (*shutdown_core)(struct vpu_core *core);
+ int (*restore_core)(struct vpu_core *core);
+ int (*get_power_state)(struct vpu_core *core);
+ int (*on_firmware_loaded)(struct vpu_core *core);
+ void (*init_rpc)(struct vpu_shared_addr *shared,
+ struct vpu_buffer *rpc, dma_addr_t boot_addr);
+ void (*set_log_buf)(struct vpu_shared_addr *shared,
+ struct vpu_buffer *log);
+ void (*set_system_cfg)(struct vpu_shared_addr *shared,
+ u32 regs_base, void __iomem *regs, u32 index);
+ void (*set_stream_cfg)(struct vpu_shared_addr *shared, u32 index);
+ u32 (*get_version)(struct vpu_shared_addr *shared);
+ u32 (*get_max_instance_count)(struct vpu_shared_addr *shared);
+ int (*get_stream_buffer_size)(struct vpu_shared_addr *shared);
+ int (*send_cmd_buf)(struct vpu_shared_addr *shared,
+ struct vpu_rpc_event *cmd);
+ int (*receive_msg_buf)(struct vpu_shared_addr *shared,
+ struct vpu_rpc_event *msg);
+ int (*pack_cmd)(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data);
+ int (*convert_msg_id)(u32 msg_id);
+ int (*unpack_msg_data)(struct vpu_rpc_event *pkt, void *data);
+ int (*input_frame)(struct vpu_shared_addr *shared,
+ struct vpu_inst *inst, struct vb2_buffer *vb);
+ int (*config_memory_resource)(struct vpu_shared_addr *shared,
+ u32 instance,
+ u32 type,
+ u32 index,
+ struct vpu_buffer *buf);
+ int (*config_stream_buffer)(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_buffer *buf);
+ int (*update_stream_buffer)(struct vpu_shared_addr *shared,
+ u32 instance, u32 ptr, bool write);
+ int (*get_stream_buffer_desc)(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_rpc_buffer_desc *desc);
+ int (*set_encode_params)(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_encode_params *params,
+ u32 update);
+ int (*set_decode_params)(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_decode_params *params,
+ u32 update);
+ int (*add_scode)(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_buffer *stream_buffer,
+ u32 pixelformat,
+ u32 scode_type);
+ int (*pre_send_cmd)(struct vpu_shared_addr *shared, u32 instance);
+ int (*post_send_cmd)(struct vpu_shared_addr *shared, u32 instance);
+ int (*init_instance)(struct vpu_shared_addr *shared, u32 instance);
+};
+
+enum {
+ VPU_CORE_MEMORY_INVALID = 0,
+ VPU_CORE_MEMORY_CACHED,
+ VPU_CORE_MEMORY_UNCACHED
+};
+
+struct vpu_rpc_region_t {
+ dma_addr_t start;
+ dma_addr_t end;
+ dma_addr_t type;
+};
+
+struct vpu_iface_ops *vpu_core_get_iface(struct vpu_core *core);
+struct vpu_iface_ops *vpu_inst_get_iface(struct vpu_inst *inst);
+int vpu_iface_check_memory_region(struct vpu_core *core, dma_addr_t addr, u32 size);
+
+static inline bool vpu_iface_check_codec(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (ops && ops->check_codec)
+ return ops->check_codec(core->type);
+
+ return true;
+}
+
+static inline bool vpu_iface_check_format(struct vpu_inst *inst, u32 pixelfmt)
+{
+ struct vpu_iface_ops *ops = vpu_inst_get_iface(inst);
+
+ if (ops && ops->check_fmt)
+ return ops->check_fmt(inst->type, pixelfmt);
+
+ return true;
+}
+
+static inline int vpu_iface_boot_core(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (ops && ops->boot_core)
+ return ops->boot_core(core);
+ return 0;
+}
+
+static inline int vpu_iface_get_power_state(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (ops && ops->get_power_state)
+ return ops->get_power_state(core);
+ return 1;
+}
+
+static inline int vpu_iface_shutdown_core(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (ops && ops->shutdown_core)
+ return ops->shutdown_core(core);
+ return 0;
+}
+
+static inline int vpu_iface_restore_core(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (ops && ops->restore_core)
+ return ops->restore_core(core);
+ return 0;
+}
+
+static inline int vpu_iface_on_firmware_loaded(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (ops && ops->on_firmware_loaded)
+ return ops->on_firmware_loaded(core);
+
+ return 0;
+}
+
+static inline u32 vpu_iface_get_data_size(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->get_data_size)
+ return 0;
+
+ return ops->get_data_size();
+}
+
+static inline int vpu_iface_init(struct vpu_core *core,
+ struct vpu_shared_addr *shared,
+ struct vpu_buffer *rpc,
+ dma_addr_t boot_addr)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->init_rpc)
+ return -EINVAL;
+
+ ops->init_rpc(shared, rpc, boot_addr);
+ core->iface = shared;
+ shared->core = core;
+ if (rpc->bytesused > rpc->length)
+ return -ENOSPC;
+ return 0;
+}
+
+static inline int vpu_iface_set_log_buf(struct vpu_core *core,
+ struct vpu_buffer *log)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops)
+ return -EINVAL;
+
+ if (ops->set_log_buf)
+ ops->set_log_buf(core->iface, log);
+
+ return 0;
+}
+
+static inline int vpu_iface_config_system(struct vpu_core *core, u32 regs_base, void __iomem *regs)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops)
+ return -EINVAL;
+ if (ops->set_system_cfg)
+ ops->set_system_cfg(core->iface, regs_base, regs, core->id);
+
+ return 0;
+}
+
+static inline int vpu_iface_get_stream_buffer_size(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->get_stream_buffer_size)
+ return 0;
+
+ return ops->get_stream_buffer_size(core->iface);
+}
+
+static inline int vpu_iface_config_stream(struct vpu_inst *inst)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || inst->id < 0)
+ return -EINVAL;
+ if (ops->set_stream_cfg)
+ ops->set_stream_cfg(inst->core->iface, inst->id);
+ return 0;
+}
+
+static inline int vpu_iface_send_cmd(struct vpu_core *core, struct vpu_rpc_event *cmd)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->send_cmd_buf)
+ return -EINVAL;
+
+ return ops->send_cmd_buf(core->iface, cmd);
+}
+
+static inline int vpu_iface_receive_msg(struct vpu_core *core, struct vpu_rpc_event *msg)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->receive_msg_buf)
+ return -EINVAL;
+
+ return ops->receive_msg_buf(core->iface, msg);
+}
+
+static inline int vpu_iface_pack_cmd(struct vpu_core *core,
+ struct vpu_rpc_event *pkt,
+ u32 index, u32 id, void *data)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->pack_cmd)
+ return -EINVAL;
+ return ops->pack_cmd(pkt, index, id, data);
+}
+
+static inline int vpu_iface_convert_msg_id(struct vpu_core *core, u32 msg_id)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->convert_msg_id)
+ return -EINVAL;
+
+ return ops->convert_msg_id(msg_id);
+}
+
+static inline int vpu_iface_unpack_msg_data(struct vpu_core *core,
+ struct vpu_rpc_event *pkt, void *data)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->unpack_msg_data)
+ return -EINVAL;
+
+ return ops->unpack_msg_data(pkt, data);
+}
+
+static inline int vpu_iface_input_frame(struct vpu_inst *inst,
+ struct vb2_buffer *vb)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->input_frame)
+ return -EINVAL;
+
+ return ops->input_frame(inst->core->iface, inst, vb);
+}
+
+static inline int vpu_iface_config_memory_resource(struct vpu_inst *inst,
+ u32 type,
+ u32 index,
+ struct vpu_buffer *buf)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->config_memory_resource || inst->id < 0)
+ return -EINVAL;
+
+ return ops->config_memory_resource(inst->core->iface,
+ inst->id,
+ type, index, buf);
+}
+
+static inline int vpu_iface_config_stream_buffer(struct vpu_inst *inst,
+ struct vpu_buffer *buf)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->config_stream_buffer || inst->id < 0)
+ return -EINVAL;
+
+ if ((buf->phys % 4) || (buf->length % 4))
+ return -EINVAL;
+ if (buf->phys + buf->length > (u64)UINT_MAX)
+ return -EINVAL;
+
+ return ops->config_stream_buffer(inst->core->iface, inst->id, buf);
+}
+
+static inline int vpu_iface_update_stream_buffer(struct vpu_inst *inst,
+ u32 ptr, bool write)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->update_stream_buffer || inst->id < 0)
+ return -EINVAL;
+
+ return ops->update_stream_buffer(inst->core->iface, inst->id, ptr, write);
+}
+
+static inline int vpu_iface_get_stream_buffer_desc(struct vpu_inst *inst,
+ struct vpu_rpc_buffer_desc *desc)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->get_stream_buffer_desc || inst->id < 0)
+ return -EINVAL;
+
+ if (!desc)
+ return 0;
+
+ return ops->get_stream_buffer_desc(inst->core->iface, inst->id, desc);
+}
+
+static inline u32 vpu_iface_get_version(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->get_version)
+ return 0;
+
+ return ops->get_version(core->iface);
+}
+
+static inline u32 vpu_iface_get_max_instance_count(struct vpu_core *core)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(core);
+
+ if (!ops || !ops->get_max_instance_count)
+ return 0;
+
+ return ops->get_max_instance_count(core->iface);
+}
+
+static inline int vpu_iface_set_encode_params(struct vpu_inst *inst,
+ struct vpu_encode_params *params, u32 update)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->set_encode_params || inst->id < 0)
+ return -EINVAL;
+
+ return ops->set_encode_params(inst->core->iface, inst->id, params, update);
+}
+
+static inline int vpu_iface_set_decode_params(struct vpu_inst *inst,
+ struct vpu_decode_params *params, u32 update)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->set_decode_params || inst->id < 0)
+ return -EINVAL;
+
+ return ops->set_decode_params(inst->core->iface, inst->id, params, update);
+}
+
+static inline int vpu_iface_add_scode(struct vpu_inst *inst, u32 scode_type)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (!ops || !ops->add_scode || inst->id < 0)
+ return -EINVAL;
+
+ return ops->add_scode(inst->core->iface, inst->id,
+ &inst->stream_buffer,
+ inst->out_format.pixfmt,
+ scode_type);
+}
+
+static inline int vpu_iface_pre_send_cmd(struct vpu_inst *inst)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (ops && ops->pre_send_cmd && inst->id >= 0)
+ return ops->pre_send_cmd(inst->core->iface, inst->id);
+ return 0;
+}
+
+static inline int vpu_iface_post_send_cmd(struct vpu_inst *inst)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (ops && ops->post_send_cmd && inst->id >= 0)
+ return ops->post_send_cmd(inst->core->iface, inst->id);
+ return 0;
+}
+
+static inline int vpu_iface_init_instance(struct vpu_inst *inst)
+{
+ struct vpu_iface_ops *ops = vpu_core_get_iface(inst->core);
+
+ if (ops && ops->init_instance && inst->id >= 0)
+ return ops->init_instance(inst->core->iface, inst->id);
+
+ return 0;
+}
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
new file mode 100644
index 000000000000..9c0704cd5766
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -0,0 +1,713 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+#include "vpu.h"
+#include "vpu_core.h"
+#include "vpu_v4l2.h"
+#include "vpu_msgs.h"
+#include "vpu_helpers.h"
+
+void vpu_inst_lock(struct vpu_inst *inst)
+{
+ mutex_lock(&inst->lock);
+}
+
+void vpu_inst_unlock(struct vpu_inst *inst)
+{
+ mutex_unlock(&inst->lock);
+}
+
+dma_addr_t vpu_get_vb_phy_addr(struct vb2_buffer *vb, u32 plane_no)
+{
+ if (plane_no >= vb->num_planes)
+ return 0;
+ return vb2_dma_contig_plane_dma_addr(vb, plane_no) +
+ vb->planes[plane_no].data_offset;
+}
+
+unsigned int vpu_get_vb_length(struct vb2_buffer *vb, u32 plane_no)
+{
+ if (plane_no >= vb->num_planes)
+ return 0;
+ return vb2_plane_size(vb, plane_no) - vb->planes[plane_no].data_offset;
+}
+
+void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state)
+{
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+
+ vpu_buf->state = state;
+}
+
+unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf)
+{
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+
+ return vpu_buf->state;
+}
+
+void vpu_v4l2_set_error(struct vpu_inst *inst)
+{
+ struct vb2_queue *src_q;
+ struct vb2_queue *dst_q;
+
+ vpu_inst_lock(inst);
+ dev_err(inst->dev, "some error occurs in codec\n");
+ if (inst->fh.m2m_ctx) {
+ src_q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
+ dst_q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
+ if (src_q)
+ src_q->error = 1;
+ if (dst_q)
+ dst_q->error = 1;
+ }
+ vpu_inst_unlock(inst);
+}
+
+int vpu_notify_eos(struct vpu_inst *inst)
+{
+ static const struct v4l2_event ev = {
+ .id = 0,
+ .type = V4L2_EVENT_EOS
+ };
+
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+ v4l2_event_queue_fh(&inst->fh, &ev);
+
+ return 0;
+}
+
+int vpu_notify_source_change(struct vpu_inst *inst)
+{
+ static const struct v4l2_event ev = {
+ .id = 0,
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION
+ };
+
+ vpu_trace(inst->dev, "[%d]\n", inst->id);
+ v4l2_event_queue_fh(&inst->fh, &ev);
+ return 0;
+}
+
+int vpu_set_last_buffer_dequeued(struct vpu_inst *inst)
+{
+ struct vb2_queue *q;
+
+ if (!inst || !inst->fh.m2m_ctx)
+ return -EINVAL;
+
+ q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
+ if (!list_empty(&q->done_list))
+ return -EINVAL;
+
+ if (q->last_buffer_dequeued)
+ return 0;
+ vpu_trace(inst->dev, "last buffer dequeued\n");
+ q->last_buffer_dequeued = true;
+ wake_up(&q->done_wq);
+ vpu_notify_eos(inst);
+ return 0;
+}
+
+const struct vpu_format *vpu_try_fmt_common(struct vpu_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ u32 type = f->type;
+ u32 stride = 1;
+ u32 bytesperline;
+ u32 sizeimage;
+ const struct vpu_format *fmt;
+ const struct vpu_core_resources *res;
+ int i;
+
+ fmt = vpu_helper_find_format(inst, type, pixmp->pixelformat);
+ if (!fmt) {
+ fmt = vpu_helper_enum_format(inst, type, 0);
+ if (!fmt)
+ return NULL;
+ pixmp->pixelformat = fmt->pixfmt;
+ }
+
+ res = vpu_get_resource(inst);
+ if (res)
+ stride = res->stride;
+ if (pixmp->width)
+ pixmp->width = vpu_helper_valid_frame_width(inst, pixmp->width);
+ if (pixmp->height)
+ pixmp->height = vpu_helper_valid_frame_height(inst, pixmp->height);
+ pixmp->flags = fmt->flags;
+ pixmp->num_planes = fmt->num_planes;
+ if (pixmp->field == V4L2_FIELD_ANY)
+ pixmp->field = V4L2_FIELD_NONE;
+ for (i = 0; i < pixmp->num_planes; i++) {
+ bytesperline = max_t(s32, pixmp->plane_fmt[i].bytesperline, 0);
+ sizeimage = vpu_helper_get_plane_size(pixmp->pixelformat,
+ pixmp->width,
+ pixmp->height,
+ i,
+ stride,
+ pixmp->field > V4L2_FIELD_NONE ? 1 : 0,
+ &bytesperline);
+ sizeimage = max_t(s32, pixmp->plane_fmt[i].sizeimage, sizeimage);
+ pixmp->plane_fmt[i].bytesperline = bytesperline;
+ pixmp->plane_fmt[i].sizeimage = sizeimage;
+ }
+
+ return fmt;
+}
+
+static bool vpu_check_ready(struct vpu_inst *inst, u32 type)
+{
+ if (!inst)
+ return false;
+ if (inst->state == VPU_CODEC_STATE_DEINIT || inst->id < 0)
+ return false;
+ if (!inst->ops->check_ready)
+ return true;
+ return call_vop(inst, check_ready, type);
+}
+
+int vpu_process_output_buffer(struct vpu_inst *inst)
+{
+ struct v4l2_m2m_buffer *buf = NULL;
+ struct vb2_v4l2_buffer *vbuf = NULL;
+
+ if (!inst || !inst->fh.m2m_ctx)
+ return -EINVAL;
+
+ if (!vpu_check_ready(inst, inst->out_format.type))
+ return -EINVAL;
+
+ v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) {
+ vbuf = &buf->vb;
+ if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_IDLE)
+ break;
+ vbuf = NULL;
+ }
+
+ if (!vbuf)
+ return -EINVAL;
+
+ dev_dbg(inst->dev, "[%d]frame id = %d / %d\n",
+ inst->id, vbuf->sequence, inst->sequence);
+ return call_vop(inst, process_output, &vbuf->vb2_buf);
+}
+
+int vpu_process_capture_buffer(struct vpu_inst *inst)
+{
+ struct v4l2_m2m_buffer *buf = NULL;
+ struct vb2_v4l2_buffer *vbuf = NULL;
+
+ if (!inst || !inst->fh.m2m_ctx)
+ return -EINVAL;
+
+ if (!vpu_check_ready(inst, inst->cap_format.type))
+ return -EINVAL;
+
+ v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) {
+ vbuf = &buf->vb;
+ if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_IDLE)
+ break;
+ vbuf = NULL;
+ }
+ if (!vbuf)
+ return -EINVAL;
+
+ return call_vop(inst, process_capture, &vbuf->vb2_buf);
+}
+
+struct vb2_v4l2_buffer *vpu_find_buf_by_sequence(struct vpu_inst *inst, u32 type, u32 sequence)
+{
+ struct v4l2_m2m_buffer *buf = NULL;
+ struct vb2_v4l2_buffer *vbuf = NULL;
+
+ if (!inst || !inst->fh.m2m_ctx)
+ return NULL;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) {
+ vbuf = &buf->vb;
+ if (vbuf->sequence == sequence)
+ break;
+ vbuf = NULL;
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) {
+ vbuf = &buf->vb;
+ if (vbuf->sequence == sequence)
+ break;
+ vbuf = NULL;
+ }
+ }
+
+ return vbuf;
+}
+
+struct vb2_v4l2_buffer *vpu_find_buf_by_idx(struct vpu_inst *inst, u32 type, u32 idx)
+{
+ struct v4l2_m2m_buffer *buf = NULL;
+ struct vb2_v4l2_buffer *vbuf = NULL;
+
+ if (!inst || !inst->fh.m2m_ctx)
+ return NULL;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ v4l2_m2m_for_each_src_buf(inst->fh.m2m_ctx, buf) {
+ vbuf = &buf->vb;
+ if (vbuf->vb2_buf.index == idx)
+ break;
+ vbuf = NULL;
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf(inst->fh.m2m_ctx, buf) {
+ vbuf = &buf->vb;
+ if (vbuf->vb2_buf.index == idx)
+ break;
+ vbuf = NULL;
+ }
+ }
+
+ return vbuf;
+}
+
+int vpu_get_num_buffers(struct vpu_inst *inst, u32 type)
+{
+ struct vb2_queue *q;
+
+ if (!inst || !inst->fh.m2m_ctx)
+ return -EINVAL;
+
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ q = v4l2_m2m_get_src_vq(inst->fh.m2m_ctx);
+ else
+ q = v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx);
+
+ return q->num_buffers;
+}
+
+static void vpu_m2m_device_run(void *priv)
+{
+}
+
+static void vpu_m2m_job_abort(void *priv)
+{
+ struct vpu_inst *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->fh.m2m_ctx;
+
+ v4l2_m2m_job_finish(m2m_ctx->m2m_dev, m2m_ctx);
+}
+
+static const struct v4l2_m2m_ops vpu_m2m_ops = {
+ .device_run = vpu_m2m_device_run,
+ .job_abort = vpu_m2m_job_abort
+};
+
+static int vpu_vb2_queue_setup(struct vb2_queue *vq,
+ unsigned int *buf_count,
+ unsigned int *plane_count,
+ unsigned int psize[],
+ struct device *allocators[])
+{
+ struct vpu_inst *inst = vb2_get_drv_priv(vq);
+ struct vpu_format *cur_fmt;
+ int i;
+
+ cur_fmt = vpu_get_format(inst, vq->type);
+
+ if (*plane_count) {
+ if (*plane_count != cur_fmt->num_planes)
+ return -EINVAL;
+ for (i = 0; i < cur_fmt->num_planes; i++) {
+ if (psize[i] < cur_fmt->sizeimage[i])
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ *plane_count = cur_fmt->num_planes;
+ for (i = 0; i < cur_fmt->num_planes; i++)
+ psize[i] = cur_fmt->sizeimage[i];
+
+ return 0;
+}
+
+static int vpu_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ return 0;
+}
+
+static int vpu_vb2_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int vpu_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_format *cur_fmt;
+ u32 i;
+
+ cur_fmt = vpu_get_format(inst, vb->type);
+ for (i = 0; i < cur_fmt->num_planes; i++) {
+ if (vpu_get_vb_length(vb, i) < cur_fmt->sizeimage[i]) {
+ dev_dbg(inst->dev, "[%d] %s buf[%d] is invalid\n",
+ inst->id, vpu_type_name(vb->type), vb->index);
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_ERROR);
+ }
+ }
+
+ return 0;
+}
+
+static void vpu_vb2_buf_finish(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST)
+ vpu_notify_eos(inst);
+
+ if (list_empty(&q->done_list))
+ call_void_vop(inst, on_queue_empty, q->type);
+}
+
+void vpu_vb2_buffers_return(struct vpu_inst *inst, unsigned int type, enum vb2_buffer_state state)
+{
+ struct vb2_v4l2_buffer *buf;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ while ((buf = v4l2_m2m_src_buf_remove(inst->fh.m2m_ctx))) {
+ vpu_set_buffer_state(buf, VPU_BUF_STATE_IDLE);
+ v4l2_m2m_buf_done(buf, state);
+ }
+ } else {
+ while ((buf = v4l2_m2m_dst_buf_remove(inst->fh.m2m_ctx))) {
+ vpu_set_buffer_state(buf, VPU_BUF_STATE_IDLE);
+ v4l2_m2m_buf_done(buf, state);
+ }
+ }
+}
+
+static int vpu_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct vpu_inst *inst = vb2_get_drv_priv(q);
+ struct vpu_format *fmt = vpu_get_format(inst, q->type);
+ int ret;
+
+ vpu_inst_unlock(inst);
+ ret = vpu_inst_register(inst);
+ vpu_inst_lock(inst);
+ if (ret) {
+ vpu_vb2_buffers_return(inst, q->type, VB2_BUF_STATE_QUEUED);
+ return ret;
+ }
+
+ vpu_trace(inst->dev, "[%d] %s %c%c%c%c %dx%d %u(%u) %u(%u) %u(%u) %d\n",
+ inst->id, vpu_type_name(q->type),
+ fmt->pixfmt,
+ fmt->pixfmt >> 8,
+ fmt->pixfmt >> 16,
+ fmt->pixfmt >> 24,
+ fmt->width, fmt->height,
+ fmt->sizeimage[0], fmt->bytesperline[0],
+ fmt->sizeimage[1], fmt->bytesperline[1],
+ fmt->sizeimage[2], fmt->bytesperline[2],
+ q->num_buffers);
+ call_void_vop(inst, start, q->type);
+ vb2_clear_last_buffer_dequeued(q);
+
+ return 0;
+}
+
+static void vpu_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct vpu_inst *inst = vb2_get_drv_priv(q);
+
+ vpu_trace(inst->dev, "[%d] %s\n", inst->id, vpu_type_name(q->type));
+
+ call_void_vop(inst, stop, q->type);
+ vpu_vb2_buffers_return(inst, q->type, VB2_BUF_STATE_ERROR);
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ inst->sequence = 0;
+}
+
+static void vpu_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->type))
+ vbuf->sequence = inst->sequence++;
+
+ v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf);
+ vpu_process_output_buffer(inst);
+ vpu_process_capture_buffer(inst);
+}
+
+static const struct vb2_ops vpu_vb2_ops = {
+ .queue_setup = vpu_vb2_queue_setup,
+ .buf_init = vpu_vb2_buf_init,
+ .buf_out_validate = vpu_vb2_buf_out_validate,
+ .buf_prepare = vpu_vb2_buf_prepare,
+ .buf_finish = vpu_vb2_buf_finish,
+ .start_streaming = vpu_vb2_start_streaming,
+ .stop_streaming = vpu_vb2_stop_streaming,
+ .buf_queue = vpu_vb2_buf_queue,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int vpu_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ struct vpu_inst *inst = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ inst->out_format.type = src_vq->type;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->ops = &vpu_vb2_ops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ if (inst->type == VPU_CORE_TYPE_DEC && inst->use_stream_buffer)
+ src_vq->mem_ops = &vb2_vmalloc_memops;
+ src_vq->drv_priv = inst;
+ src_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer);
+ src_vq->min_buffers_needed = 1;
+ src_vq->dev = inst->vpu->dev;
+ src_vq->lock = &inst->lock;
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ inst->cap_format.type = dst_vq->type;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->ops = &vpu_vb2_ops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ if (inst->type == VPU_CORE_TYPE_ENC && inst->use_stream_buffer)
+ dst_vq->mem_ops = &vb2_vmalloc_memops;
+ dst_vq->drv_priv = inst;
+ dst_vq->buf_struct_size = sizeof(struct vpu_vb2_buffer);
+ dst_vq->min_buffers_needed = 1;
+ dst_vq->dev = inst->vpu->dev;
+ dst_vq->lock = &inst->lock;
+ ret = vb2_queue_init(dst_vq);
+ if (ret) {
+ vb2_queue_release(src_vq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int vpu_v4l2_release(struct vpu_inst *inst)
+{
+ vpu_trace(inst->vpu->dev, "%p\n", inst);
+
+ vpu_release_core(inst->core);
+ put_device(inst->dev);
+
+ if (inst->workqueue) {
+ cancel_work_sync(&inst->msg_work);
+ destroy_workqueue(inst->workqueue);
+ inst->workqueue = NULL;
+ }
+
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ mutex_destroy(&inst->lock);
+ v4l2_fh_del(&inst->fh);
+ v4l2_fh_exit(&inst->fh);
+
+ call_void_vop(inst, cleanup);
+
+ return 0;
+}
+
+int vpu_v4l2_open(struct file *file, struct vpu_inst *inst)
+{
+ struct vpu_dev *vpu = video_drvdata(file);
+ struct vpu_func *func;
+ int ret = 0;
+
+ if (!inst || !inst->ops)
+ return -EINVAL;
+
+ if (inst->type == VPU_CORE_TYPE_ENC)
+ func = &vpu->encoder;
+ else
+ func = &vpu->decoder;
+
+ atomic_set(&inst->ref_count, 0);
+ vpu_inst_get(inst);
+ inst->vpu = vpu;
+ inst->core = vpu_request_core(vpu, inst->type);
+ if (inst->core)
+ inst->dev = get_device(inst->core->dev);
+ mutex_init(&inst->lock);
+ INIT_LIST_HEAD(&inst->cmd_q);
+ inst->id = VPU_INST_NULL_ID;
+ inst->release = vpu_v4l2_release;
+ inst->pid = current->pid;
+ inst->tgid = current->tgid;
+ inst->min_buffer_cap = 2;
+ inst->min_buffer_out = 2;
+ v4l2_fh_init(&inst->fh, func->vfd);
+ v4l2_fh_add(&inst->fh);
+
+ ret = call_vop(inst, ctrl_init);
+ if (ret)
+ goto error;
+
+ inst->fh.m2m_ctx = v4l2_m2m_ctx_init(func->m2m_dev, inst, vpu_m2m_queue_init);
+ if (IS_ERR(inst->fh.m2m_ctx)) {
+ dev_err(vpu->dev, "v4l2_m2m_ctx_init fail\n");
+ ret = PTR_ERR(inst->fh.m2m_ctx);
+ goto error;
+ }
+
+ inst->fh.ctrl_handler = &inst->ctrl_handler;
+ file->private_data = &inst->fh;
+ inst->state = VPU_CODEC_STATE_DEINIT;
+ inst->workqueue = alloc_workqueue("vpu_inst", WQ_UNBOUND | WQ_MEM_RECLAIM, 1);
+ if (inst->workqueue) {
+ INIT_WORK(&inst->msg_work, vpu_inst_run_work);
+ ret = kfifo_init(&inst->msg_fifo,
+ inst->msg_buffer,
+ rounddown_pow_of_two(sizeof(inst->msg_buffer)));
+ if (ret) {
+ destroy_workqueue(inst->workqueue);
+ inst->workqueue = NULL;
+ }
+ }
+ vpu_trace(vpu->dev, "tgid = %d, pid = %d, type = %s, inst = %p\n",
+ inst->tgid, inst->pid, vpu_core_type_desc(inst->type), inst);
+
+ return 0;
+error:
+ vpu_inst_put(inst);
+ return ret;
+}
+
+int vpu_v4l2_close(struct file *file)
+{
+ struct vpu_dev *vpu = video_drvdata(file);
+ struct vpu_inst *inst = to_inst(file);
+
+ vpu_trace(vpu->dev, "tgid = %d, pid = %d, inst = %p\n", inst->tgid, inst->pid, inst);
+
+ vpu_inst_lock(inst);
+ if (inst->fh.m2m_ctx) {
+ v4l2_m2m_ctx_release(inst->fh.m2m_ctx);
+ inst->fh.m2m_ctx = NULL;
+ }
+ vpu_inst_unlock(inst);
+
+ call_void_vop(inst, release);
+ vpu_inst_unregister(inst);
+ vpu_inst_put(inst);
+
+ return 0;
+}
+
+int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func)
+{
+ struct video_device *vfd;
+ int ret;
+
+ if (!vpu || !func)
+ return -EINVAL;
+
+ if (func->vfd)
+ return 0;
+
+ func->m2m_dev = v4l2_m2m_init(&vpu_m2m_ops);
+ if (IS_ERR(func->m2m_dev)) {
+ dev_err(vpu->dev, "v4l2_m2m_init fail\n");
+ func->vfd = NULL;
+ return PTR_ERR(func->m2m_dev);
+ }
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_m2m_release(func->m2m_dev);
+ dev_err(vpu->dev, "alloc vpu decoder video device fail\n");
+ return -ENOMEM;
+ }
+ vfd->release = video_device_release;
+ vfd->vfl_dir = VFL_DIR_M2M;
+ vfd->v4l2_dev = &vpu->v4l2_dev;
+ vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ if (func->type == VPU_CORE_TYPE_ENC) {
+ strscpy(vfd->name, "amphion-vpu-encoder", sizeof(vfd->name));
+ vfd->fops = venc_get_fops();
+ vfd->ioctl_ops = venc_get_ioctl_ops();
+ } else {
+ strscpy(vfd->name, "amphion-vpu-decoder", sizeof(vfd->name));
+ vfd->fops = vdec_get_fops();
+ vfd->ioctl_ops = vdec_get_ioctl_ops();
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ video_device_release(vfd);
+ v4l2_m2m_release(func->m2m_dev);
+ return ret;
+ }
+ video_set_drvdata(vfd, vpu);
+ func->vfd = vfd;
+
+ ret = v4l2_m2m_register_media_controller(func->m2m_dev, func->vfd, func->function);
+ if (ret) {
+ v4l2_m2m_release(func->m2m_dev);
+ func->m2m_dev = NULL;
+ video_unregister_device(func->vfd);
+ func->vfd = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+void vpu_remove_func(struct vpu_func *func)
+{
+ if (!func)
+ return;
+
+ if (func->m2m_dev) {
+ v4l2_m2m_unregister_media_controller(func->m2m_dev);
+ v4l2_m2m_release(func->m2m_dev);
+ func->m2m_dev = NULL;
+ }
+ if (func->vfd) {
+ video_unregister_device(func->vfd);
+ func->vfd = NULL;
+ }
+}
diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h
new file mode 100644
index 000000000000..90fa7ea67495
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_v4l2.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_V4L2_H
+#define _AMPHION_VPU_V4L2_H
+
+#include <linux/videodev2.h>
+
+void vpu_inst_lock(struct vpu_inst *inst);
+void vpu_inst_unlock(struct vpu_inst *inst);
+void vpu_set_buffer_state(struct vb2_v4l2_buffer *vbuf, unsigned int state);
+unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf);
+
+int vpu_v4l2_open(struct file *file, struct vpu_inst *inst);
+int vpu_v4l2_close(struct file *file);
+
+const struct vpu_format *vpu_try_fmt_common(struct vpu_inst *inst, struct v4l2_format *f);
+int vpu_process_output_buffer(struct vpu_inst *inst);
+int vpu_process_capture_buffer(struct vpu_inst *inst);
+struct vb2_v4l2_buffer *vpu_find_buf_by_sequence(struct vpu_inst *inst, u32 type, u32 sequence);
+struct vb2_v4l2_buffer *vpu_find_buf_by_idx(struct vpu_inst *inst, u32 type, u32 idx);
+void vpu_v4l2_set_error(struct vpu_inst *inst);
+int vpu_notify_eos(struct vpu_inst *inst);
+int vpu_notify_source_change(struct vpu_inst *inst);
+int vpu_set_last_buffer_dequeued(struct vpu_inst *inst);
+void vpu_vb2_buffers_return(struct vpu_inst *inst, unsigned int type, enum vb2_buffer_state state);
+int vpu_get_num_buffers(struct vpu_inst *inst, u32 type);
+
+dma_addr_t vpu_get_vb_phy_addr(struct vb2_buffer *vb, u32 plane_no);
+unsigned int vpu_get_vb_length(struct vb2_buffer *vb, u32 plane_no);
+static inline struct vpu_format *vpu_get_format(struct vpu_inst *inst, u32 type)
+{
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return &inst->out_format;
+ else
+ return &inst->cap_format;
+}
+
+static inline char *vpu_type_name(u32 type)
+{
+ return V4L2_TYPE_IS_OUTPUT(type) ? "output" : "capture";
+}
+
+static inline int vpu_vb_is_codecconfig(struct vb2_v4l2_buffer *vbuf)
+{
+#ifdef V4L2_BUF_FLAG_CODECCONFIG
+ return (vbuf->flags & V4L2_BUF_FLAG_CODECCONFIG) ? 1 : 0;
+#else
+ return 0;
+#endif
+}
+
+#endif
diff --git a/drivers/media/platform/amphion/vpu_windsor.c b/drivers/media/platform/amphion/vpu_windsor.c
new file mode 100644
index 000000000000..1526af2ef9da
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_windsor.c
@@ -0,0 +1,1173 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <linux/init.h>
+#include <linux/interconnect.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/time64.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-dma-contig.h>
+#include "vpu.h"
+#include "vpu_rpc.h"
+#include "vpu_defs.h"
+#include "vpu_helpers.h"
+#include "vpu_cmds.h"
+#include "vpu_v4l2.h"
+#include "vpu_imx8q.h"
+#include "vpu_windsor.h"
+
+#define CMD_SIZE 2560
+#define MSG_SIZE 25600
+#define WINDSOR_USER_DATA_WORDS 16
+#define WINDSOR_MAX_SRC_FRAMES 0x6
+#define WINDSOR_MAX_REF_FRAMES 0x3
+#define WINDSOR_BITRATE_UNIT 1024
+#define WINDSOR_H264_EXTENDED_SAR 255
+
+enum {
+ GTB_ENC_CMD_NOOP = 0x0,
+ GTB_ENC_CMD_STREAM_START,
+ GTB_ENC_CMD_FRAME_ENCODE,
+ GTB_ENC_CMD_FRAME_SKIP,
+ GTB_ENC_CMD_STREAM_STOP,
+ GTB_ENC_CMD_PARAMETER_UPD,
+ GTB_ENC_CMD_TERMINATE,
+ GTB_ENC_CMD_SNAPSHOT,
+ GTB_ENC_CMD_ROLL_SNAPSHOT,
+ GTB_ENC_CMD_LOCK_SCHEDULER,
+ GTB_ENC_CMD_UNLOCK_SCHEDULER,
+ GTB_ENC_CMD_CONFIGURE_CODEC,
+ GTB_ENC_CMD_DEAD_MARK,
+ GTB_ENC_CMD_FIRM_RESET,
+ GTB_ENC_CMD_FW_STATUS,
+ GTB_ENC_CMD_RESERVED
+};
+
+enum {
+ VID_API_EVENT_UNDEFINED = 0x0,
+ VID_API_ENC_EVENT_RESET_DONE = 0x1,
+ VID_API_ENC_EVENT_START_DONE,
+ VID_API_ENC_EVENT_STOP_DONE,
+ VID_API_ENC_EVENT_TERMINATE_DONE,
+ VID_API_ENC_EVENT_FRAME_INPUT_DONE,
+ VID_API_ENC_EVENT_FRAME_DONE,
+ VID_API_ENC_EVENT_FRAME_RELEASE,
+ VID_API_ENC_EVENT_PARA_UPD_DONE,
+ VID_API_ENC_EVENT_MEM_REQUEST,
+ VID_API_ENC_EVENT_FIRMWARE_XCPT,
+ VID_API_ENC_EVENT_RESERVED
+};
+
+enum {
+ MEDIAIP_ENC_PIC_TYPE_B_FRAME = 0,
+ MEDIAIP_ENC_PIC_TYPE_P_FRAME,
+ MEDIAIP_ENC_PIC_TYPE_I_FRAME,
+ MEDIAIP_ENC_PIC_TYPE_IDR_FRAME,
+ MEDIAIP_ENC_PIC_TYPE_BI_FRAME
+};
+
+struct windsor_iface {
+ u32 exec_base_addr;
+ u32 exec_area_size;
+ struct vpu_rpc_buffer_desc cmd_buffer_desc;
+ struct vpu_rpc_buffer_desc msg_buffer_desc;
+ u32 cmd_int_enable[VID_API_NUM_STREAMS];
+ u32 fw_version;
+ u32 mvd_fw_offset;
+ u32 max_streams;
+ u32 ctrl_iface[VID_API_NUM_STREAMS];
+ struct vpu_rpc_system_config system_config;
+ u32 api_version;
+ struct vpu_rpc_buffer_desc log_buffer_desc;
+};
+
+struct windsor_ctrl_iface {
+ u32 enc_yuv_buffer_desc;
+ u32 enc_stream_buffer_desc;
+ u32 enc_expert_mode_param;
+ u32 enc_param;
+ u32 enc_mem_pool;
+ u32 enc_encoding_status;
+ u32 enc_dsa_status;
+};
+
+struct vpu_enc_yuv_desc {
+ u32 frame_id;
+ u32 luma_base;
+ u32 chroma_base;
+ u32 param_idx;
+ u32 key_frame;
+};
+
+struct vpu_enc_calib_params {
+ u32 use_ame;
+
+ u32 cme_mvx_max;
+ u32 cme_mvy_max;
+ u32 ame_prefresh_y0;
+ u32 ame_prefresh_y1;
+ u32 fme_min_sad;
+ u32 cme_min_sad;
+
+ u32 fme_pred_int_weight;
+ u32 fme_pred_hp_weight;
+ u32 fme_pred_qp_weight;
+ u32 fme_cost_weight;
+ u32 fme_act_thold;
+ u32 fme_sad_thold;
+ u32 fme_zero_sad_thold;
+
+ u32 fme_lrg_mvx_lmt;
+ u32 fme_lrg_mvy_lmt;
+ u32 fme_force_mode;
+ u32 fme_force4mvcost;
+ u32 fme_force2mvcost;
+
+ u32 h264_inter_thrd;
+
+ u32 i16x16_mode_cost;
+ u32 i4x4_mode_lambda;
+ u32 i8x8_mode_lambda;
+
+ u32 inter_mod_mult;
+ u32 inter_sel_mult;
+ u32 inter_bid_cost;
+ u32 inter_bwd_cost;
+ u32 inter_4mv_cost;
+ s32 one_mv_i16_cost;
+ s32 one_mv_i4x4_cost;
+ s32 one_mv_i8x8_cost;
+ s32 two_mv_i16_cost;
+ s32 two_mv_i4x4_cost;
+ s32 two_mv_i8x8_cost;
+ s32 four_mv_i16_cost;
+ s32 four_mv_i4x4_cost;
+ s32 four_mv_i8x8_cost;
+
+ u32 intra_pred_enab;
+ u32 intra_chr_pred;
+ u32 intra16_pred;
+ u32 intra4x4_pred;
+ u32 intra8x8_pred;
+
+ u32 cb_base;
+ u32 cb_size;
+ u32 cb_head_room;
+
+ u32 mem_page_width;
+ u32 mem_page_height;
+ u32 mem_total_size;
+ u32 mem_chunk_phys_addr;
+ u32 mem_chunk_virt_addr;
+ u32 mem_chunk_size;
+ u32 mem_y_stride;
+ u32 mem_uv_stride;
+
+ u32 split_wr_enab;
+ u32 split_wr_req_size;
+ u32 split_rd_enab;
+ u32 split_rd_req_size;
+};
+
+struct vpu_enc_config_params {
+ u32 param_change;
+ u32 start_frame;
+ u32 end_frame;
+ u32 userdata_enable;
+ u32 userdata_id[4];
+ u32 userdata_message[WINDSOR_USER_DATA_WORDS];
+ u32 userdata_length;
+ u32 h264_profile_idc;
+ u32 h264_level_idc;
+ u32 h264_au_delimiter;
+ u32 h264_seq_end_code;
+ u32 h264_recovery_points;
+ u32 h264_vui_parameters;
+ u32 h264_aspect_ratio_present;
+ u32 h264_aspect_ratio_sar_width;
+ u32 h264_aspect_ratio_sar_height;
+ u32 h264_overscan_present;
+ u32 h264_video_type_present;
+ u32 h264_video_format;
+ u32 h264_video_full_range;
+ u32 h264_video_colour_descriptor;
+ u32 h264_video_colour_primaries;
+ u32 h264_video_transfer_char;
+ u32 h264_video_matrix_coeff;
+ u32 h264_chroma_loc_info_present;
+ u32 h264_chroma_loc_type_top;
+ u32 h264_chroma_loc_type_bot;
+ u32 h264_timing_info_present;
+ u32 h264_buffering_period_present;
+ u32 h264_low_delay_hrd_flag;
+ u32 aspect_ratio;
+ u32 test_mode; // Automated firmware test mode
+ u32 dsa_test_mode; // Automated test mode for the DSA.
+ u32 fme_test_mode; // Automated test mode for the fme
+ u32 cbr_row_mode; //0: FW mode; 1: HW mode
+ u32 windsor_mode; //0: normal mode; 1: intra only mode; 2: intra+0MV mode
+ u32 encode_mode; // H264, VC1, MPEG2, DIVX
+ u32 frame_width; // display width
+ u32 frame_height; // display height
+ u32 enc_frame_width; // encoding width, should be 16-pix align
+ u32 enc_frame_height; // encoding height, should be 16-pix aligned
+ u32 frame_rate_num;
+ u32 frame_rate_den;
+ u32 vi_field_source;
+ u32 vi_frame_width;
+ u32 vi_frame_height;
+ u32 crop_frame_width;
+ u32 crop_frame_height;
+ u32 crop_x_start_posn;
+ u32 crop_y_start_posn;
+ u32 mode422;
+ u32 mode_yuy2;
+ u32 dsa_luma_en;
+ u32 dsa_chroma_en;
+ u32 dsa_ext_hfilt_en;
+ u32 dsa_di_en;
+ u32 dsa_di_top_ref;
+ u32 dsa_vertf_disable;
+ u32 dsa_disable_pwb;
+ u32 dsa_hor_phase;
+ u32 dsa_ver_phase;
+ u32 dsa_iac_enable;
+ u32 iac_sc_threshold;
+ u32 iac_vm_threshold;
+ u32 iac_skip_mode;
+ u32 iac_grp_width;
+ u32 iac_grp_height;
+ u32 rate_control_mode;
+ u32 rate_control_resolution;
+ u32 buffer_size;
+ u32 buffer_level_init;
+ u32 buffer_I_bit_budget;
+ u32 top_field_first;
+ u32 intra_lum_qoffset;
+ u32 intra_chr_qoffset;
+ u32 inter_lum_qoffset;
+ u32 inter_chr_qoffset;
+ u32 use_def_scaling_mtx;
+ u32 inter_8x8_enab;
+ u32 inter_4x4_enab;
+ u32 fme_enable_qpel;
+ u32 fme_enable_hpel;
+ u32 fme_nozeromv;
+ u32 fme_predmv_en;
+ u32 fme_pred_2mv4mv;
+ u32 fme_smallsadthresh;
+ u32 ame_en_lmvc;
+ u32 ame_x_mult;
+ u32 cme_enable_4mv;
+ u32 cme_enable_1mv;
+ u32 hme_enable_16x8mv;
+ u32 hme_enable_8x16mv;
+ u32 cme_mv_weight;
+ u32 cme_mv_cost;
+ u32 ame_mult_mv;
+ u32 ame_shift_mv;
+ u32 hme_forceto1mv_en;
+ u32 hme_2mv_cost;
+ u32 hme_pred_mode;
+ u32 hme_sc_rnge;
+ u32 hme_sw_rnge;
+ u32 output_format;
+ u32 timestamp_enab;
+ u32 initial_pts_enab;
+ u32 initial_pts;
+};
+
+struct vpu_enc_static_params {
+ u32 param_change;
+ u32 gop_length;
+ u32 rate_control_bitrate;
+ u32 rate_control_bitrate_min;
+ u32 rate_control_bitrate_max;
+ u32 rate_control_content_models;
+ u32 rate_control_iframe_maxsize;
+ u32 rate_control_qp_init;
+ u32 rate_control_islice_qp;
+ u32 rate_control_pslice_qp;
+ u32 rate_control_bslice_qp;
+ u32 adaptive_quantization;
+ u32 aq_variance;
+ u32 cost_optimization;
+ u32 fdlp_mode;
+ u32 enable_isegbframes;
+ u32 enable_adaptive_keyratio;
+ u32 keyratio_imin;
+ u32 keyratio_imax;
+ u32 keyratio_pmin;
+ u32 keyratio_pmax;
+ u32 keyratio_bmin;
+ u32 keyratio_bmax;
+ s32 keyratio_istep;
+ s32 keyratio_pstep;
+ s32 keyratio_bstep;
+ u32 enable_paff;
+ u32 enable_b_frame_ref;
+ u32 enable_adaptive_gop;
+ u32 enable_closed_gop;
+ u32 open_gop_refresh_freq;
+ u32 enable_adaptive_sc;
+ u32 enable_fade_detection;
+ s32 fade_detection_threshold;
+ u32 enable_repeat_b;
+ u32 enable_low_delay_b;
+};
+
+struct vpu_enc_dynamic_params {
+ u32 param_change;
+ u32 rows_per_slice;
+ u32 mbaff_enable;
+ u32 dbf_enable;
+ u32 field_source;
+ u32 gop_b_length;
+ u32 mb_group_size;
+ u32 cbr_rows_per_group;
+ u32 skip_enable;
+ u32 pts_bits_0_to_31;
+ u32 pts_bit_32;
+ u32 rm_expsv_cff;
+ u32 const_ipred;
+ s32 chr_qp_offset;
+ u32 intra_mb_qp_offset;
+ u32 h264_cabac_init_method;
+ u32 h264_cabac_init_idc;
+ u32 h264_cabac_enable;
+ s32 alpha_c0_offset_div2;
+ s32 beta_offset_div2;
+ u32 intra_prefresh_y0;
+ u32 intra_prefresh_y1;
+ u32 dbg_dump_rec_src;
+};
+
+struct vpu_enc_expert_mode_param {
+ struct vpu_enc_calib_params calib_param;
+ struct vpu_enc_config_params config_param;
+ struct vpu_enc_static_params static_param;
+ struct vpu_enc_dynamic_params dynamic_param;
+};
+
+enum MEDIAIP_ENC_FMT {
+ MEDIAIP_ENC_FMT_H264 = 0,
+ MEDIAIP_ENC_FMT_VC1,
+ MEDIAIP_ENC_FMT_MPEG2,
+ MEDIAIP_ENC_FMT_MPEG4SP,
+ MEDIAIP_ENC_FMT_H263,
+ MEDIAIP_ENC_FMT_MPEG1,
+ MEDIAIP_ENC_FMT_SHORT_HEADER,
+ MEDIAIP_ENC_FMT_NULL
+};
+
+enum MEDIAIP_ENC_PROFILE {
+ MEDIAIP_ENC_PROF_MPEG2_SP = 0,
+ MEDIAIP_ENC_PROF_MPEG2_MP,
+ MEDIAIP_ENC_PROF_MPEG2_HP,
+ MEDIAIP_ENC_PROF_H264_BP,
+ MEDIAIP_ENC_PROF_H264_MP,
+ MEDIAIP_ENC_PROF_H264_HP,
+ MEDIAIP_ENC_PROF_MPEG4_SP,
+ MEDIAIP_ENC_PROF_MPEG4_ASP,
+ MEDIAIP_ENC_PROF_VC1_SP,
+ MEDIAIP_ENC_PROF_VC1_MP,
+ MEDIAIP_ENC_PROF_VC1_AP
+};
+
+enum MEDIAIP_ENC_BITRATE_MODE {
+ MEDIAIP_ENC_BITRATE_MODE_VBR = 0x00000001,
+ MEDIAIP_ENC_BITRATE_MODE_CBR = 0x00000002,
+ MEDIAIP_ENC_BITRATE_MODE_CONSTANT_QP = 0x00000004
+};
+
+struct vpu_enc_memory_resource {
+ u32 phys;
+ u32 virt;
+ u32 size;
+};
+
+struct vpu_enc_param {
+ enum MEDIAIP_ENC_FMT codec_mode;
+ enum MEDIAIP_ENC_PROFILE profile;
+ u32 level;
+
+ struct vpu_enc_memory_resource enc_mem_desc;
+
+ u32 frame_rate;
+ u32 src_stride;
+ u32 src_width;
+ u32 src_height;
+ u32 src_offset_x;
+ u32 src_offset_y;
+ u32 src_crop_width;
+ u32 src_crop_height;
+ u32 out_width;
+ u32 out_height;
+ u32 iframe_interval;
+ u32 bframes;
+ u32 low_latency_mode;
+
+ enum MEDIAIP_ENC_BITRATE_MODE bitrate_mode;
+ u32 target_bitrate;
+ u32 max_bitrate;
+ u32 min_bitrate;
+ u32 init_slice_qp;
+};
+
+struct vpu_enc_mem_pool {
+ struct vpu_enc_memory_resource enc_frames[WINDSOR_MAX_SRC_FRAMES];
+ struct vpu_enc_memory_resource ref_frames[WINDSOR_MAX_REF_FRAMES];
+ struct vpu_enc_memory_resource act_frame;
+};
+
+struct vpu_enc_encoding_status {
+ u32 frame_id;
+ u32 error_flag; //Error type
+ u32 mb_y;
+ u32 mb_x;
+ u32 reserved[12];
+
+};
+
+struct vpu_enc_dsa_status {
+ u32 frame_id;
+ u32 dsa_cyle;
+ u32 mb_y;
+ u32 mb_x;
+ u32 reserved[4];
+};
+
+struct vpu_enc_ctrl {
+ struct vpu_enc_yuv_desc *yuv_desc;
+ struct vpu_rpc_buffer_desc *stream_desc;
+ struct vpu_enc_expert_mode_param *expert;
+ struct vpu_enc_param *param;
+ struct vpu_enc_mem_pool *pool;
+ struct vpu_enc_encoding_status *status;
+ struct vpu_enc_dsa_status *dsa;
+};
+
+struct vpu_enc_host_ctrls {
+ struct vpu_enc_ctrl ctrls[VID_API_NUM_STREAMS];
+};
+
+struct windsor_pic_info {
+ u32 frame_id;
+ u32 pic_encod_done;
+ u32 pic_type;
+ u32 skipped_frame;
+ u32 error_flag;
+ u32 psnr;
+ u32 flush_done;
+ u32 mb_y;
+ u32 mb_x;
+ u32 frame_size;
+ u32 frame_enc_ttl_cycles;
+ u32 frame_enc_ttl_frm_cycles;
+ u32 frame_enc_ttl_slc_cycles;
+ u32 frame_enc_ttl_enc_cycles;
+ u32 frame_enc_ttl_hme_cycles;
+ u32 frame_enc_ttl_dsa_cycles;
+ u32 frame_enc_fw_cycles;
+ u32 frame_crc;
+ u32 num_interrupts_1;
+ u32 num_interrupts_2;
+ u32 poc;
+ u32 ref_info;
+ u32 pic_num;
+ u32 pic_activity;
+ u32 scene_change;
+ u32 mb_stats;
+ u32 enc_cache_count0;
+ u32 enc_cache_count1;
+ u32 mtl_wr_strb_cnt;
+ u32 mtl_rd_strb_cnt;
+ u32 str_buff_wptr;
+ u32 diagnosticEvents;
+ u32 proc_iacc_tot_rd_cnt;
+ u32 proc_dacc_tot_rd_cnt;
+ u32 proc_dacc_tot_wr_cnt;
+ u32 proc_dacc_reg_rd_cnt;
+ u32 proc_dacc_reg_wr_cnt;
+ u32 proc_dacc_rng_rd_cnt;
+ u32 proc_dacc_rng_wr_cnt;
+ s32 tv_s;
+ u32 tv_ns;
+};
+
+u32 vpu_windsor_get_data_size(void)
+{
+ return sizeof(struct vpu_enc_host_ctrls);
+}
+
+static struct vpu_enc_yuv_desc *get_yuv_desc(struct vpu_shared_addr *shared,
+ u32 instance)
+{
+ struct vpu_enc_host_ctrls *hcs = shared->priv;
+
+ return hcs->ctrls[instance].yuv_desc;
+}
+
+static struct vpu_enc_mem_pool *get_mem_pool(struct vpu_shared_addr *shared,
+ u32 instance)
+{
+ struct vpu_enc_host_ctrls *hcs = shared->priv;
+
+ return hcs->ctrls[instance].pool;
+}
+
+static struct vpu_rpc_buffer_desc *get_stream_buf_desc(struct vpu_shared_addr *shared,
+ u32 instance)
+{
+ struct vpu_enc_host_ctrls *hcs = shared->priv;
+
+ return hcs->ctrls[instance].stream_desc;
+}
+
+static struct vpu_enc_expert_mode_param *get_expert_param(struct vpu_shared_addr *shared,
+ u32 instance)
+{
+ struct vpu_enc_host_ctrls *hcs = shared->priv;
+
+ return hcs->ctrls[instance].expert;
+}
+
+static struct vpu_enc_param *get_enc_param(struct vpu_shared_addr *shared, u32 instance)
+{
+ struct vpu_enc_host_ctrls *hcs = shared->priv;
+
+ return hcs->ctrls[instance].param;
+}
+
+static u32 get_ptr(u32 ptr)
+{
+ return (ptr | 0x80000000);
+}
+
+void vpu_windsor_init_rpc(struct vpu_shared_addr *shared,
+ struct vpu_buffer *rpc, dma_addr_t boot_addr)
+{
+ unsigned long base_phy_addr;
+ unsigned long phy_addr;
+ unsigned long offset;
+ struct windsor_iface *iface;
+ struct windsor_ctrl_iface *ctrl;
+ struct vpu_enc_host_ctrls *hcs;
+ unsigned int i;
+
+ if (rpc->phys < boot_addr)
+ return;
+
+ base_phy_addr = rpc->phys - boot_addr;
+ iface = rpc->virt;
+ shared->iface = iface;
+ shared->boot_addr = boot_addr;
+ hcs = shared->priv;
+
+ iface->exec_base_addr = base_phy_addr;
+ iface->exec_area_size = rpc->length;
+
+ offset = sizeof(struct windsor_iface);
+ phy_addr = base_phy_addr + offset;
+ shared->cmd_desc = &iface->cmd_buffer_desc;
+ shared->cmd_mem_vir = rpc->virt + offset;
+ iface->cmd_buffer_desc.start =
+ iface->cmd_buffer_desc.rptr =
+ iface->cmd_buffer_desc.wptr = phy_addr;
+ iface->cmd_buffer_desc.end = iface->cmd_buffer_desc.start + CMD_SIZE;
+
+ offset += CMD_SIZE;
+ phy_addr = base_phy_addr + offset;
+ shared->msg_desc = &iface->msg_buffer_desc;
+ shared->msg_mem_vir = rpc->virt + offset;
+ iface->msg_buffer_desc.start =
+ iface->msg_buffer_desc.wptr =
+ iface->msg_buffer_desc.rptr = phy_addr;
+ iface->msg_buffer_desc.end = iface->msg_buffer_desc.start + MSG_SIZE;
+
+ offset += MSG_SIZE;
+ for (i = 0; i < ARRAY_SIZE(iface->ctrl_iface); i++) {
+ iface->ctrl_iface[i] = base_phy_addr + offset;
+ offset += sizeof(struct windsor_ctrl_iface);
+ }
+ for (i = 0; i < ARRAY_SIZE(iface->ctrl_iface); i++) {
+ ctrl = rpc->virt + (iface->ctrl_iface[i] - base_phy_addr);
+
+ ctrl->enc_yuv_buffer_desc = base_phy_addr + offset;
+ hcs->ctrls[i].yuv_desc = rpc->virt + offset;
+ offset += sizeof(struct vpu_enc_yuv_desc);
+
+ ctrl->enc_stream_buffer_desc = base_phy_addr + offset;
+ hcs->ctrls[i].stream_desc = rpc->virt + offset;
+ offset += sizeof(struct vpu_rpc_buffer_desc);
+
+ ctrl->enc_expert_mode_param = base_phy_addr + offset;
+ hcs->ctrls[i].expert = rpc->virt + offset;
+ offset += sizeof(struct vpu_enc_expert_mode_param);
+
+ ctrl->enc_param = base_phy_addr + offset;
+ hcs->ctrls[i].param = rpc->virt + offset;
+ offset += sizeof(struct vpu_enc_param);
+
+ ctrl->enc_mem_pool = base_phy_addr + offset;
+ hcs->ctrls[i].pool = rpc->virt + offset;
+ offset += sizeof(struct vpu_enc_mem_pool);
+
+ ctrl->enc_encoding_status = base_phy_addr + offset;
+ hcs->ctrls[i].status = rpc->virt + offset;
+ offset += sizeof(struct vpu_enc_encoding_status);
+
+ ctrl->enc_dsa_status = base_phy_addr + offset;
+ hcs->ctrls[i].dsa = rpc->virt + offset;
+ offset += sizeof(struct vpu_enc_dsa_status);
+ }
+
+ rpc->bytesused = offset;
+}
+
+void vpu_windsor_set_log_buf(struct vpu_shared_addr *shared, struct vpu_buffer *log)
+{
+ struct windsor_iface *iface = shared->iface;
+
+ iface->log_buffer_desc.start =
+ iface->log_buffer_desc.wptr =
+ iface->log_buffer_desc.rptr = log->phys - shared->boot_addr;
+ iface->log_buffer_desc.end = iface->log_buffer_desc.start + log->length;
+}
+
+void vpu_windsor_set_system_cfg(struct vpu_shared_addr *shared,
+ u32 regs_base, void __iomem *regs, u32 core_id)
+{
+ struct windsor_iface *iface = shared->iface;
+ struct vpu_rpc_system_config *config = &iface->system_config;
+
+ vpu_imx8q_set_system_cfg_common(config, regs_base, core_id);
+}
+
+int vpu_windsor_get_stream_buffer_size(struct vpu_shared_addr *shared)
+{
+ return 0x300000;
+}
+
+static struct vpu_pair windsor_cmds[] = {
+ {VPU_CMD_ID_CONFIGURE_CODEC, GTB_ENC_CMD_CONFIGURE_CODEC},
+ {VPU_CMD_ID_START, GTB_ENC_CMD_STREAM_START},
+ {VPU_CMD_ID_STOP, GTB_ENC_CMD_STREAM_STOP},
+ {VPU_CMD_ID_FRAME_ENCODE, GTB_ENC_CMD_FRAME_ENCODE},
+ {VPU_CMD_ID_SNAPSHOT, GTB_ENC_CMD_SNAPSHOT},
+ {VPU_CMD_ID_FIRM_RESET, GTB_ENC_CMD_FIRM_RESET},
+ {VPU_CMD_ID_UPDATE_PARAMETER, GTB_ENC_CMD_PARAMETER_UPD},
+ {VPU_CMD_ID_DEBUG, GTB_ENC_CMD_FW_STATUS}
+};
+
+static struct vpu_pair windsor_msgs[] = {
+ {VPU_MSG_ID_RESET_DONE, VID_API_ENC_EVENT_RESET_DONE},
+ {VPU_MSG_ID_START_DONE, VID_API_ENC_EVENT_START_DONE},
+ {VPU_MSG_ID_STOP_DONE, VID_API_ENC_EVENT_STOP_DONE},
+ {VPU_MSG_ID_FRAME_INPUT_DONE, VID_API_ENC_EVENT_FRAME_INPUT_DONE},
+ {VPU_MSG_ID_ENC_DONE, VID_API_ENC_EVENT_FRAME_DONE},
+ {VPU_MSG_ID_FRAME_RELEASE, VID_API_ENC_EVENT_FRAME_RELEASE},
+ {VPU_MSG_ID_MEM_REQUEST, VID_API_ENC_EVENT_MEM_REQUEST},
+ {VPU_MSG_ID_PARAM_UPD_DONE, VID_API_ENC_EVENT_PARA_UPD_DONE},
+ {VPU_MSG_ID_FIRMWARE_XCPT, VID_API_ENC_EVENT_FIRMWARE_XCPT},
+};
+
+int vpu_windsor_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data)
+{
+ int ret;
+
+ ret = vpu_find_dst_by_src(windsor_cmds, ARRAY_SIZE(windsor_cmds), id);
+ if (ret < 0)
+ return ret;
+ pkt->hdr.id = ret;
+ pkt->hdr.num = 0;
+ pkt->hdr.index = index;
+ if (id == VPU_CMD_ID_FRAME_ENCODE) {
+ s64 timestamp = *(s64 *)data;
+ struct timespec64 ts = ns_to_timespec64(timestamp);
+
+ pkt->hdr.num = 2;
+ pkt->data[0] = ts.tv_sec;
+ pkt->data[1] = ts.tv_nsec;
+ }
+
+ return 0;
+}
+
+int vpu_windsor_convert_msg_id(u32 id)
+{
+ return vpu_find_src_by_dst(windsor_msgs, ARRAY_SIZE(windsor_msgs), id);
+}
+
+static void vpu_windsor_unpack_pic_info(struct vpu_rpc_event *pkt, void *data)
+{
+ struct vpu_enc_pic_info *info = data;
+ struct windsor_pic_info *windsor = (struct windsor_pic_info *)pkt->data;
+ struct timespec64 ts = { windsor->tv_s, windsor->tv_ns };
+
+ info->frame_id = windsor->frame_id;
+ switch (windsor->pic_type) {
+ case MEDIAIP_ENC_PIC_TYPE_I_FRAME:
+ case MEDIAIP_ENC_PIC_TYPE_IDR_FRAME:
+ info->pic_type = V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case MEDIAIP_ENC_PIC_TYPE_P_FRAME:
+ info->pic_type = V4L2_BUF_FLAG_PFRAME;
+ break;
+ case MEDIAIP_ENC_PIC_TYPE_B_FRAME:
+ info->pic_type = V4L2_BUF_FLAG_BFRAME;
+ break;
+ default:
+ break;
+ }
+ info->skipped_frame = windsor->skipped_frame;
+ info->error_flag = windsor->error_flag;
+ info->psnr = windsor->psnr;
+ info->frame_size = windsor->frame_size;
+ info->wptr = get_ptr(windsor->str_buff_wptr);
+ info->crc = windsor->frame_crc;
+ info->timestamp = timespec64_to_ns(&ts);
+}
+
+static void vpu_windsor_unpack_mem_req(struct vpu_rpc_event *pkt, void *data)
+{
+ struct vpu_pkt_mem_req_data *req_data = data;
+
+ req_data->enc_frame_size = pkt->data[0];
+ req_data->enc_frame_num = pkt->data[1];
+ req_data->ref_frame_size = pkt->data[2];
+ req_data->ref_frame_num = pkt->data[3];
+ req_data->act_buf_size = pkt->data[4];
+ req_data->act_buf_num = 1;
+}
+
+int vpu_windsor_unpack_msg_data(struct vpu_rpc_event *pkt, void *data)
+{
+ if (!pkt || !data)
+ return -EINVAL;
+
+ switch (pkt->hdr.id) {
+ case VID_API_ENC_EVENT_FRAME_DONE:
+ vpu_windsor_unpack_pic_info(pkt, data);
+ break;
+ case VID_API_ENC_EVENT_MEM_REQUEST:
+ vpu_windsor_unpack_mem_req(pkt, data);
+ break;
+ case VID_API_ENC_EVENT_FRAME_RELEASE:
+ *(u32 *)data = pkt->data[0];
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int vpu_windsor_fill_yuv_frame(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vb2_buffer *vb)
+{
+ struct vpu_enc_yuv_desc *desc;
+ struct vb2_v4l2_buffer *vbuf;
+
+ if (instance >= VID_API_NUM_STREAMS)
+ return -EINVAL;
+
+ desc = get_yuv_desc(shared, instance);
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ desc->frame_id = vbuf->sequence;
+ if (vbuf->flags & V4L2_BUF_FLAG_KEYFRAME)
+ desc->key_frame = 1;
+ else
+ desc->key_frame = 0;
+ desc->luma_base = vpu_get_vb_phy_addr(vb, 0);
+ desc->chroma_base = vpu_get_vb_phy_addr(vb, 1);
+
+ return 0;
+}
+
+int vpu_windsor_input_frame(struct vpu_shared_addr *shared,
+ struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ vpu_windsor_fill_yuv_frame(shared, inst->id, vb);
+ return vpu_session_encode_frame(inst, vb->timestamp);
+}
+
+int vpu_windsor_config_memory_resource(struct vpu_shared_addr *shared,
+ u32 instance,
+ u32 type,
+ u32 index,
+ struct vpu_buffer *buf)
+{
+ struct vpu_enc_mem_pool *pool;
+ struct vpu_enc_memory_resource *res;
+
+ if (instance >= VID_API_NUM_STREAMS)
+ return -EINVAL;
+
+ pool = get_mem_pool(shared, instance);
+
+ switch (type) {
+ case MEM_RES_ENC:
+ if (index >= ARRAY_SIZE(pool->enc_frames))
+ return -EINVAL;
+ res = &pool->enc_frames[index];
+ break;
+ case MEM_RES_REF:
+ if (index >= ARRAY_SIZE(pool->ref_frames))
+ return -EINVAL;
+ res = &pool->ref_frames[index];
+ break;
+ case MEM_RES_ACT:
+ if (index)
+ return -EINVAL;
+ res = &pool->act_frame;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ res->phys = buf->phys;
+ res->virt = buf->phys - shared->boot_addr;
+ res->size = buf->length;
+
+ return 0;
+}
+
+int vpu_windsor_config_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_buffer *buf)
+{
+ struct vpu_rpc_buffer_desc *desc;
+ struct vpu_enc_expert_mode_param *expert;
+
+ desc = get_stream_buf_desc(shared, instance);
+ expert = get_expert_param(shared, instance);
+
+ desc->start = buf->phys;
+ desc->wptr = buf->phys;
+ desc->rptr = buf->phys;
+ desc->end = buf->phys + buf->length;
+
+ expert->calib_param.mem_chunk_phys_addr = 0;
+ expert->calib_param.mem_chunk_virt_addr = 0;
+ expert->calib_param.mem_chunk_size = 0;
+ expert->calib_param.cb_base = buf->phys;
+ expert->calib_param.cb_size = buf->length;
+
+ return 0;
+}
+
+int vpu_windsor_update_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance, u32 ptr, bool write)
+{
+ struct vpu_rpc_buffer_desc *desc;
+
+ desc = get_stream_buf_desc(shared, instance);
+
+ /*update wptr/rptr after data is written or read*/
+ mb();
+ if (write)
+ desc->wptr = ptr;
+ else
+ desc->rptr = ptr;
+
+ return 0;
+}
+
+int vpu_windsor_get_stream_buffer_desc(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_rpc_buffer_desc *desc)
+{
+ struct vpu_rpc_buffer_desc *rpc_desc;
+
+ rpc_desc = get_stream_buf_desc(shared, instance);
+ if (desc) {
+ desc->wptr = get_ptr(rpc_desc->wptr);
+ desc->rptr = get_ptr(rpc_desc->rptr);
+ desc->start = get_ptr(rpc_desc->start);
+ desc->end = get_ptr(rpc_desc->end);
+ }
+
+ return 0;
+}
+
+u32 vpu_windsor_get_version(struct vpu_shared_addr *shared)
+{
+ struct windsor_iface *iface = shared->iface;
+
+ return iface->fw_version;
+}
+
+static int vpu_windsor_set_frame_rate(struct vpu_enc_expert_mode_param *expert,
+ struct vpu_encode_params *params)
+{
+ expert->config_param.frame_rate_num = params->frame_rate.numerator;
+ expert->config_param.frame_rate_den = params->frame_rate.denominator;
+
+ return 0;
+}
+
+static int vpu_windsor_set_format(struct vpu_enc_param *param, u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_H264:
+ param->codec_mode = MEDIAIP_ENC_FMT_H264;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vpu_windsor_set_profile(struct vpu_enc_param *param, u32 profile)
+{
+ switch (profile) {
+ case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+ param->profile = MEDIAIP_ENC_PROF_H264_BP;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+ param->profile = MEDIAIP_ENC_PROF_H264_MP;
+ break;
+ case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+ param->profile = MEDIAIP_ENC_PROF_H264_HP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const u32 h264_level[] = {
+ [V4L2_MPEG_VIDEO_H264_LEVEL_1_0] = 10,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_1B] = 14,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_1_1] = 11,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_1_2] = 12,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_1_3] = 13,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_2_0] = 20,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_2_1] = 21,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_2_2] = 22,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_3_0] = 30,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_3_1] = 31,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_3_2] = 32,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_4_0] = 40,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_4_1] = 41,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_4_2] = 42,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_5_0] = 50,
+ [V4L2_MPEG_VIDEO_H264_LEVEL_5_1] = 51
+};
+
+static int vpu_windsor_set_level(struct vpu_enc_param *param, u32 level)
+{
+ if (level >= ARRAY_SIZE(h264_level))
+ return -EINVAL;
+
+ param->level = h264_level[level];
+
+ return 0;
+}
+
+static int vpu_windsor_set_size(struct vpu_enc_param *windsor,
+ struct vpu_encode_params *params)
+{
+ windsor->src_stride = params->src_stride;
+ windsor->src_width = params->src_width;
+ windsor->src_height = params->src_height;
+ windsor->src_offset_x = params->crop.left;
+ windsor->src_offset_y = params->crop.top;
+ windsor->src_crop_width = params->crop.width;
+ windsor->src_crop_height = params->crop.height;
+ windsor->out_width = params->out_width;
+ windsor->out_height = params->out_height;
+
+ return 0;
+}
+
+static int vpu_windsor_set_gop(struct vpu_enc_param *param, u32 gop)
+{
+ param->iframe_interval = gop;
+
+ return 0;
+}
+
+static int vpu_windsor_set_bframes(struct vpu_enc_param *param, u32 bframes)
+{
+ if (bframes) {
+ param->low_latency_mode = 0;
+ param->bframes = bframes;
+ } else {
+ param->low_latency_mode = 1;
+ param->bframes = 0;
+ }
+
+ return 0;
+}
+
+static int vpu_windsor_set_bitrate_mode(struct vpu_enc_param *param, u32 rc_enable, u32 mode)
+{
+ if (!rc_enable)
+ param->bitrate_mode = MEDIAIP_ENC_BITRATE_MODE_CONSTANT_QP;
+ else if (mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+ param->bitrate_mode = MEDIAIP_ENC_BITRATE_MODE_VBR;
+ else
+ param->bitrate_mode = MEDIAIP_ENC_BITRATE_MODE_CBR;
+
+ return 0;
+}
+
+static u32 vpu_windsor_bitrate(u32 bitrate)
+{
+ return DIV_ROUND_CLOSEST(bitrate, WINDSOR_BITRATE_UNIT);
+}
+
+static int vpu_windsor_set_bitrate(struct vpu_enc_param *windsor,
+ struct vpu_encode_params *params)
+{
+ windsor->target_bitrate = vpu_windsor_bitrate(params->bitrate);
+ windsor->min_bitrate = vpu_windsor_bitrate(params->bitrate_min);
+ windsor->max_bitrate = vpu_windsor_bitrate(params->bitrate_max);
+
+ return 0;
+}
+
+static int vpu_windsor_set_qp(struct vpu_enc_expert_mode_param *expert,
+ struct vpu_encode_params *params)
+{
+ expert->static_param.rate_control_islice_qp = params->i_frame_qp;
+ expert->static_param.rate_control_pslice_qp = params->p_frame_qp;
+ expert->static_param.rate_control_bslice_qp = params->b_frame_qp;
+
+ return 0;
+}
+
+static int vpu_windsor_set_sar(struct vpu_enc_expert_mode_param *expert,
+ struct vpu_encode_params *params)
+{
+ expert->config_param.h264_aspect_ratio_present = params->sar.enable;
+ if (params->sar.idc == V4L2_MPEG_VIDEO_H264_VUI_SAR_IDC_EXTENDED)
+ expert->config_param.aspect_ratio = WINDSOR_H264_EXTENDED_SAR;
+ else
+ expert->config_param.aspect_ratio = params->sar.idc;
+ expert->config_param.h264_aspect_ratio_sar_width = params->sar.width;
+ expert->config_param.h264_aspect_ratio_sar_height = params->sar.height;
+
+ return 0;
+}
+
+static int vpu_windsor_set_color(struct vpu_enc_expert_mode_param *expert,
+ struct vpu_encode_params *params)
+{
+ expert->config_param.h264_video_type_present = 1;
+ expert->config_param.h264_video_format = 5;
+ expert->config_param.h264_video_colour_descriptor = 1;
+ expert->config_param.h264_video_colour_primaries =
+ vpu_color_cvrt_primaries_v2i(params->color.primaries);
+ expert->config_param.h264_video_transfer_char =
+ vpu_color_cvrt_transfers_v2i(params->color.transfer);
+ expert->config_param.h264_video_matrix_coeff =
+ vpu_color_cvrt_matrix_v2i(params->color.matrix);
+ expert->config_param.h264_video_full_range =
+ vpu_color_cvrt_full_range_v2i(params->color.full_range);
+ return 0;
+}
+
+static int vpu_windsor_update_bitrate(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_encode_params *params)
+{
+ struct vpu_enc_param *windsor;
+ struct vpu_enc_expert_mode_param *expert;
+
+ windsor = get_enc_param(shared, instance);
+ expert = get_expert_param(shared, instance);
+
+ if (windsor->bitrate_mode != MEDIAIP_ENC_BITRATE_MODE_CBR)
+ return 0;
+ if (!params->rc_enable)
+ return 0;
+ if (vpu_windsor_bitrate(params->bitrate) == windsor->target_bitrate)
+ return 0;
+
+ vpu_windsor_set_bitrate(windsor, params);
+ expert->static_param.rate_control_bitrate = windsor->target_bitrate;
+ expert->static_param.rate_control_bitrate_min = windsor->min_bitrate;
+ expert->static_param.rate_control_bitrate_max = windsor->max_bitrate;
+
+ return 0;
+}
+
+static int vpu_windsor_set_params(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_encode_params *params)
+{
+ struct vpu_enc_param *windsor;
+ int ret;
+
+ windsor = get_enc_param(shared, instance);
+
+ if (params->input_format != V4L2_PIX_FMT_NV12 &&
+ params->input_format != V4L2_PIX_FMT_NV12M)
+ return -EINVAL;
+
+ ret = vpu_windsor_set_format(windsor, params->codec_format);
+ if (ret)
+ return ret;
+ vpu_windsor_set_profile(windsor, params->profile);
+ vpu_windsor_set_level(windsor, params->level);
+ vpu_windsor_set_size(windsor, params);
+ vpu_windsor_set_gop(windsor, params->gop_length);
+ vpu_windsor_set_bframes(windsor, params->bframes);
+ vpu_windsor_set_bitrate_mode(windsor, params->rc_enable, params->rc_mode);
+ vpu_windsor_set_bitrate(windsor, params);
+ windsor->init_slice_qp = params->i_frame_qp;
+
+ if (!params->frame_rate.numerator)
+ return -EINVAL;
+ windsor->frame_rate = params->frame_rate.denominator / params->frame_rate.numerator;
+
+ return 0;
+}
+
+static int vpu_windsor_update_params(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_encode_params *params)
+{
+ struct vpu_enc_expert_mode_param *expert;
+
+ expert = get_expert_param(shared, instance);
+
+ vpu_windsor_set_frame_rate(expert, params);
+ vpu_windsor_set_qp(expert, params);
+ vpu_windsor_set_sar(expert, params);
+ vpu_windsor_set_color(expert, params);
+ vpu_windsor_update_bitrate(shared, instance, params);
+ /*expert->config_param.iac_sc_threshold = 0;*/
+
+ return 0;
+}
+
+int vpu_windsor_set_encode_params(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_encode_params *params, u32 update)
+{
+ if (!params)
+ return -EINVAL;
+
+ if (!update)
+ return vpu_windsor_set_params(shared, instance, params);
+ else
+ return vpu_windsor_update_params(shared, instance, params);
+}
+
+u32 vpu_windsor_get_max_instance_count(struct vpu_shared_addr *shared)
+{
+ struct windsor_iface *iface = shared->iface;
+
+ return iface->max_streams;
+}
diff --git a/drivers/media/platform/amphion/vpu_windsor.h b/drivers/media/platform/amphion/vpu_windsor.h
new file mode 100644
index 000000000000..3fbb6556dbca
--- /dev/null
+++ b/drivers/media/platform/amphion/vpu_windsor.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#ifndef _AMPHION_VPU_WINDSOR_H
+#define _AMPHION_VPU_WINDSOR_H
+
+u32 vpu_windsor_get_data_size(void);
+void vpu_windsor_init_rpc(struct vpu_shared_addr *shared,
+ struct vpu_buffer *rpc, dma_addr_t boot_addr);
+void vpu_windsor_set_log_buf(struct vpu_shared_addr *shared, struct vpu_buffer *log);
+void vpu_windsor_set_system_cfg(struct vpu_shared_addr *shared,
+ u32 regs_base, void __iomem *regs, u32 core_id);
+int vpu_windsor_get_stream_buffer_size(struct vpu_shared_addr *shared);
+int vpu_windsor_pack_cmd(struct vpu_rpc_event *pkt, u32 index, u32 id, void *data);
+int vpu_windsor_convert_msg_id(u32 msg_id);
+int vpu_windsor_unpack_msg_data(struct vpu_rpc_event *pkt, void *data);
+int vpu_windsor_config_memory_resource(struct vpu_shared_addr *shared,
+ u32 instance, u32 type, u32 index,
+ struct vpu_buffer *buf);
+int vpu_windsor_config_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_buffer *buf);
+int vpu_windsor_update_stream_buffer(struct vpu_shared_addr *shared,
+ u32 instance, u32 ptr, bool write);
+int vpu_windsor_get_stream_buffer_desc(struct vpu_shared_addr *shared,
+ u32 instance, struct vpu_rpc_buffer_desc *desc);
+u32 vpu_windsor_get_version(struct vpu_shared_addr *shared);
+int vpu_windsor_set_encode_params(struct vpu_shared_addr *shared,
+ u32 instance,
+ struct vpu_encode_params *params,
+ u32 update);
+int vpu_windsor_input_frame(struct vpu_shared_addr *shared,
+ struct vpu_inst *inst, struct vb2_buffer *vb);
+u32 vpu_windsor_get_max_instance_count(struct vpu_shared_addr *shared);
+
+#endif
diff --git a/drivers/media/platform/aspeed/Kconfig b/drivers/media/platform/aspeed/Kconfig
new file mode 100644
index 000000000000..c871eda33570
--- /dev/null
+++ b/drivers/media/platform/aspeed/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Aspeed media platform drivers"
+
+config VIDEO_ASPEED
+ tristate "Aspeed AST2400 and AST2500 Video Engine driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Support for the Aspeed Video Engine (VE) embedded in the Aspeed
+ AST2400 and AST2500 SOCs. The VE can capture and compress video data
+ from digital or analog sources.
diff --git a/drivers/media/platform/aspeed/Makefile b/drivers/media/platform/aspeed/Makefile
new file mode 100644
index 000000000000..1979af63dadd
--- /dev/null
+++ b/drivers/media/platform/aspeed/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_VIDEO_ASPEED) += aspeed-video.o
diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c
index 7a24daf7165a..b937dbcbe9e0 100644
--- a/drivers/media/platform/aspeed-video.c
+++ b/drivers/media/platform/aspeed/aspeed-video.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/of_reserved_mem.h>
#include <linux/platform_device.h>
@@ -33,6 +34,8 @@
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
+#define ASPEED_VIDEO_V4L2_MIN_BUF_REQ 3
+
#define DEVICE_NAME "aspeed-video"
#define ASPEED_VIDEO_JPEG_NUM_QUALITIES 12
@@ -85,8 +88,6 @@
#define VE_CTRL_SOURCE BIT(2)
#define VE_CTRL_INT_DE BIT(4)
#define VE_CTRL_DIRECT_FETCH BIT(5)
-#define VE_CTRL_YUV BIT(6)
-#define VE_CTRL_RGB BIT(7)
#define VE_CTRL_CAPTURE_FMT GENMASK(7, 6)
#define VE_CTRL_AUTO_OR_CURSOR BIT(8)
#define VE_CTRL_CLK_INVERSE BIT(11)
@@ -143,26 +144,29 @@
#define VE_SRC_LR_EDGE_DET_NO_H BIT(13)
#define VE_SRC_LR_EDGE_DET_NO_DISP BIT(14)
#define VE_SRC_LR_EDGE_DET_NO_CLK BIT(15)
-#define VE_SRC_LR_EDGE_DET_RT_SHF 16
-#define VE_SRC_LR_EDGE_DET_RT GENMASK(27, VE_SRC_LR_EDGE_DET_RT_SHF)
+#define VE_SRC_LR_EDGE_DET_RT GENMASK(27, 16)
#define VE_SRC_LR_EDGE_DET_INTERLACE BIT(31)
#define VE_SRC_TB_EDGE_DET 0x094
#define VE_SRC_TB_EDGE_DET_TOP GENMASK(12, 0)
-#define VE_SRC_TB_EDGE_DET_BOT_SHF 16
-#define VE_SRC_TB_EDGE_DET_BOT GENMASK(28, VE_SRC_TB_EDGE_DET_BOT_SHF)
+#define VE_SRC_TB_EDGE_DET_BOT GENMASK(28, 16)
#define VE_MODE_DETECT_STATUS 0x098
-#define VE_MODE_DETECT_H_PIXELS GENMASK(11, 0)
-#define VE_MODE_DETECT_V_LINES_SHF 16
-#define VE_MODE_DETECT_V_LINES GENMASK(27, VE_MODE_DETECT_V_LINES_SHF)
+#define VE_MODE_DETECT_H_PERIOD GENMASK(11, 0)
+#define VE_MODE_DETECT_EXTSRC_ADC BIT(12)
+#define VE_MODE_DETECT_H_STABLE BIT(13)
+#define VE_MODE_DETECT_V_STABLE BIT(14)
+#define VE_MODE_DETECT_V_LINES GENMASK(27, 16)
#define VE_MODE_DETECT_STATUS_VSYNC BIT(28)
#define VE_MODE_DETECT_STATUS_HSYNC BIT(29)
+#define VE_MODE_DETECT_VSYNC_RDY BIT(30)
+#define VE_MODE_DETECT_HSYNC_RDY BIT(31)
#define VE_SYNC_STATUS 0x09c
#define VE_SYNC_STATUS_HSYNC GENMASK(11, 0)
-#define VE_SYNC_STATUS_VSYNC_SHF 16
-#define VE_SYNC_STATUS_VSYNC GENMASK(27, VE_SYNC_STATUS_VSYNC_SHF)
+#define VE_SYNC_STATUS_VSYNC GENMASK(27, 16)
+
+#define VE_H_TOTAL_PIXELS 0x0A0
#define VE_INTERRUPT_CTRL 0x304
#define VE_INTERRUPT_STATUS 0x308
@@ -179,9 +183,24 @@
#define VE_INTERRUPT_VSYNC_DESC BIT(11)
#define VE_MODE_DETECT 0x30c
+#define VE_MODE_DT_HOR_TOLER GENMASK(31, 28)
+#define VE_MODE_DT_VER_TOLER GENMASK(27, 24)
+#define VE_MODE_DT_HOR_STABLE GENMASK(23, 20)
+#define VE_MODE_DT_VER_STABLE GENMASK(19, 16)
+#define VE_MODE_DT_EDG_THROD GENMASK(15, 8)
+
#define VE_MEM_RESTRICT_START 0x310
#define VE_MEM_RESTRICT_END 0x314
+/*
+ * VIDEO_MODE_DETECT_DONE: a flag raised if signal lock
+ * VIDEO_RES_CHANGE: a flag raised if res_change work on-going
+ * VIDEO_RES_DETECT: a flag raised if res. detection on-going
+ * VIDEO_STREAMING: a flag raised if user requires stream-on
+ * VIDEO_FRAME_INPRG: a flag raised if hw working on a frame
+ * VIDEO_STOPPED: a flag raised if device release
+ * VIDEO_CLOCKS_ON: a flag raised if clk is on
+ */
enum {
VIDEO_MODE_DETECT_DONE,
VIDEO_RES_CHANGE,
@@ -192,6 +211,15 @@ enum {
VIDEO_CLOCKS_ON,
};
+// for VE_CTRL_CAPTURE_FMT
+enum aspeed_video_capture_format {
+ VIDEO_CAP_FMT_YUV_STUDIO_SWING = 0,
+ VIDEO_CAP_FMT_YUV_FULL_SWING,
+ VIDEO_CAP_FMT_RGB,
+ VIDEO_CAP_FMT_GRAY,
+ VIDEO_CAP_FMT_MAX
+};
+
struct aspeed_video_addr {
unsigned int size;
dma_addr_t dma;
@@ -214,6 +242,25 @@ struct aspeed_video_perf {
#define to_aspeed_video_buffer(x) \
container_of((x), struct aspeed_video_buffer, vb)
+/*
+ * struct aspeed_video - driver data
+ *
+ * res_work: holds the delayed_work for res-detection if unlock
+ * buffers: holds the list of buffer queued from user
+ * flags: holds the state of video
+ * sequence: holds the last number of frame completed
+ * max_compressed_size: holds max compressed stream's size
+ * srcs: holds the buffer information for srcs
+ * jpeg: holds the buffer information for jpeg header
+ * yuv420: a flag raised if JPEG subsampling is 420
+ * frame_rate: holds the frame_rate
+ * jpeg_quality: holds jpeq's quality (0~11)
+ * frame_bottom: end position of video data in vertical direction
+ * frame_left: start position of video data in horizontal direction
+ * frame_right: end position of video data in horizontal direction
+ * frame_top: start position of video data in vertical direction
+ * perf: holds the statistics primary for debugfs
+ */
struct aspeed_video {
void __iomem *base;
struct clk *eclk;
@@ -411,6 +458,8 @@ static const struct v4l2_dv_timings_cap aspeed_video_timings_cap = {
},
};
+static unsigned int debug;
+
static void aspeed_video_init_jpeg_table(u32 *table, bool yuv420)
{
int i;
@@ -458,33 +507,38 @@ static void aspeed_video_update(struct aspeed_video *video, u32 reg, u32 clear,
t &= ~clear;
t |= bits;
writel(t, video->base + reg);
- dev_dbg(video->dev, "update %03x[%08x -> %08x]\n", reg, before,
- readl(video->base + reg));
+ v4l2_dbg(3, debug, &video->v4l2_dev, "update %03x[%08x -> %08x]\n",
+ reg, before, readl(video->base + reg));
}
static u32 aspeed_video_read(struct aspeed_video *video, u32 reg)
{
u32 t = readl(video->base + reg);
- dev_dbg(video->dev, "read %03x[%08x]\n", reg, t);
+ v4l2_dbg(3, debug, &video->v4l2_dev, "read %03x[%08x]\n", reg, t);
return t;
}
static void aspeed_video_write(struct aspeed_video *video, u32 reg, u32 val)
{
writel(val, video->base + reg);
- dev_dbg(video->dev, "write %03x[%08x]\n", reg,
- readl(video->base + reg));
+ v4l2_dbg(3, debug, &video->v4l2_dev, "write %03x[%08x]\n", reg,
+ readl(video->base + reg));
}
static void update_perf(struct aspeed_video_perf *p)
{
+ struct aspeed_video *v = container_of(p, struct aspeed_video,
+ perf);
+
p->duration =
ktime_to_ms(ktime_sub(ktime_get(), p->last_sample));
p->totaltime += p->duration;
p->duration_max = max(p->duration, p->duration_max);
p->duration_min = min(p->duration, p->duration_min);
+ v4l2_dbg(2, debug, &v->v4l2_dev, "time consumed: %d ms\n",
+ p->duration);
}
static int aspeed_video_start_frame(struct aspeed_video *video)
@@ -495,13 +549,13 @@ static int aspeed_video_start_frame(struct aspeed_video *video)
u32 seq_ctrl = aspeed_video_read(video, VE_SEQ_CTRL);
if (video->v4l2_input_status) {
- dev_dbg(video->dev, "No signal; don't start frame\n");
+ v4l2_warn(&video->v4l2_dev, "No signal; don't start frame\n");
return 0;
}
if (!(seq_ctrl & VE_SEQ_CTRL_COMP_BUSY) ||
!(seq_ctrl & VE_SEQ_CTRL_CAP_BUSY)) {
- dev_dbg(video->dev, "Engine busy; don't start frame\n");
+ v4l2_warn(&video->v4l2_dev, "Engine busy; don't start frame\n");
return -EBUSY;
}
@@ -510,7 +564,7 @@ static int aspeed_video_start_frame(struct aspeed_video *video)
struct aspeed_video_buffer, link);
if (!buf) {
spin_unlock_irqrestore(&video->lock, flags);
- dev_dbg(video->dev, "No buffers; don't start frame\n");
+ v4l2_warn(&video->v4l2_dev, "No buffers; don't start frame\n");
return -EPROTO;
}
@@ -590,7 +644,7 @@ static void aspeed_video_bufs_done(struct aspeed_video *video,
static void aspeed_video_irq_res_change(struct aspeed_video *video, ulong delay)
{
- dev_dbg(video->dev, "Resolution changed; resetting\n");
+ v4l2_dbg(1, debug, &video->v4l2_dev, "Resolution changed; resetting\n");
set_bit(VIDEO_RES_CHANGE, &video->flags);
clear_bit(VIDEO_FRAME_INPRG, &video->flags);
@@ -614,6 +668,12 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg)
*/
sts &= aspeed_video_read(video, VE_INTERRUPT_CTRL);
+ v4l2_dbg(2, debug, &video->v4l2_dev, "irq sts=%#x %s%s%s%s\n", sts,
+ sts & VE_INTERRUPT_MODE_DETECT_WD ? ", unlock" : "",
+ sts & VE_INTERRUPT_MODE_DETECT ? ", lock" : "",
+ sts & VE_INTERRUPT_CAPTURE_COMPLETE ? ", capture-done" : "",
+ sts & VE_INTERRUPT_COMP_COMPLETE ? ", comp-done" : "");
+
/*
* Resolution changed or signal was lost; reset the engine and
* re-initialize
@@ -787,8 +847,101 @@ static void aspeed_video_calc_compressed_size(struct aspeed_video *video,
aspeed_video_write(video, VE_STREAM_BUF_SIZE,
compression_buffer_size_reg);
- dev_dbg(video->dev, "Max compressed size: %x\n",
- video->max_compressed_size);
+ v4l2_dbg(1, debug, &video->v4l2_dev, "Max compressed size: %#x\n",
+ video->max_compressed_size);
+}
+
+/*
+ * Update v4l2_bt_timings per current status.
+ * frame_top/frame_bottom/frame_left/frame_right need to be ready.
+ *
+ * The following registers start counting from sync's rising edge:
+ * 1. VR090: frame edge's left and right
+ * 2. VR094: frame edge's top and bottom
+ * 3. VR09C: counting from sync's rising edge to falling edge
+ *
+ * [Vertical timing]
+ * +--+ +-------------------+ +--+
+ * | | | v i d e o | | |
+ * +--+ +-----+ +-----+ +---+
+ * vsync+--+
+ * frame_top+--------+
+ * frame_bottom+----------------------------+
+ *
+ * +-------------------+
+ * | v i d e o |
+ * +--+ +-----+ +-----+ +---+
+ * | | | |
+ * +--+ +--+
+ * vsync+-------------------------------+
+ * frame_top+-----+
+ * frame_bottom+-------------------------+
+ *
+ * [Horizontal timing]
+ * +--+ +-------------------+ +--+
+ * | | | v i d e o | | |
+ * +--+ +-----+ +-----+ +---+
+ * hsync+--+
+ * frame_left+--------+
+ * frame_right+----------------------------+
+ *
+ * +-------------------+
+ * | v i d e o |
+ * +--+ +-----+ +-----+ +---+
+ * | | | |
+ * +--+ +--+
+ * hsync+-------------------------------+
+ * frame_left+-----+
+ * frame_right+-------------------------+
+ *
+ * @v: the struct of aspeed_video
+ * @det: v4l2_bt_timings to be updated.
+ */
+static void aspeed_video_get_timings(struct aspeed_video *v,
+ struct v4l2_bt_timings *det)
+{
+ u32 mds, sync, htotal, vtotal, vsync, hsync;
+
+ mds = aspeed_video_read(v, VE_MODE_DETECT_STATUS);
+ sync = aspeed_video_read(v, VE_SYNC_STATUS);
+ htotal = aspeed_video_read(v, VE_H_TOTAL_PIXELS);
+ vtotal = FIELD_GET(VE_MODE_DETECT_V_LINES, mds);
+ vsync = FIELD_GET(VE_SYNC_STATUS_VSYNC, sync);
+ hsync = FIELD_GET(VE_SYNC_STATUS_HSYNC, sync);
+
+ /*
+ * This is a workaround for polarity detection.
+ * Because ast-soc counts sync from sync's rising edge, the reg value
+ * of sync would be larger than video's active area if negative.
+ */
+ if (vsync > det->height)
+ det->polarities &= ~V4L2_DV_VSYNC_POS_POL;
+ else
+ det->polarities |= V4L2_DV_VSYNC_POS_POL;
+ if (hsync > det->width)
+ det->polarities &= ~V4L2_DV_HSYNC_POS_POL;
+ else
+ det->polarities |= V4L2_DV_HSYNC_POS_POL;
+
+ if (det->polarities & V4L2_DV_VSYNC_POS_POL) {
+ det->vbackporch = v->frame_top - vsync;
+ det->vfrontporch = vtotal - v->frame_bottom;
+ det->vsync = vsync;
+ } else {
+ det->vbackporch = v->frame_top;
+ det->vfrontporch = vsync - v->frame_bottom;
+ det->vsync = vtotal - vsync;
+ }
+
+ if (det->polarities & V4L2_DV_HSYNC_POS_POL) {
+ det->hbackporch = v->frame_left - hsync;
+ det->hfrontporch = htotal - v->frame_right;
+ det->hsync = hsync;
+ } else {
+ det->hbackporch = v->frame_left;
+ det->hfrontporch = hsync - v->frame_right;
+ det->hsync = htotal - hsync;
+ }
}
#define res_check(v) test_and_clear_bit(VIDEO_MODE_DETECT_DONE, &(v)->flags)
@@ -801,7 +954,6 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
u32 mds;
u32 src_lr_edge;
u32 src_tb_edge;
- u32 sync;
struct v4l2_bt_timings *det = &video->detected_timings;
det->width = MIN_WIDTH;
@@ -825,11 +977,18 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
res_check(video),
MODE_DETECT_TIMEOUT);
if (!rc) {
- dev_dbg(video->dev, "Timed out; first mode detect\n");
+ v4l2_warn(&video->v4l2_dev, "Timed out; first mode detect\n");
clear_bit(VIDEO_RES_DETECT, &video->flags);
return;
}
+ mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
+ // try detection again if current signal isn't stable
+ if (!(mds & VE_MODE_DETECT_H_STABLE) ||
+ !(mds & VE_MODE_DETECT_V_STABLE) ||
+ (mds & VE_MODE_DETECT_EXTSRC_ADC))
+ continue;
+
aspeed_video_check_and_set_polarity(video);
aspeed_video_enable_mode_detect(video);
@@ -839,33 +998,22 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
MODE_DETECT_TIMEOUT);
clear_bit(VIDEO_RES_DETECT, &video->flags);
if (!rc) {
- dev_dbg(video->dev, "Timed out; second mode detect\n");
+ v4l2_warn(&video->v4l2_dev, "Timed out; second mode detect\n");
return;
}
src_lr_edge = aspeed_video_read(video, VE_SRC_LR_EDGE_DET);
src_tb_edge = aspeed_video_read(video, VE_SRC_TB_EDGE_DET);
- mds = aspeed_video_read(video, VE_MODE_DETECT_STATUS);
- sync = aspeed_video_read(video, VE_SYNC_STATUS);
-
- video->frame_bottom = (src_tb_edge & VE_SRC_TB_EDGE_DET_BOT) >>
- VE_SRC_TB_EDGE_DET_BOT_SHF;
- video->frame_top = src_tb_edge & VE_SRC_TB_EDGE_DET_TOP;
- det->vfrontporch = video->frame_top;
- det->vbackporch = ((mds & VE_MODE_DETECT_V_LINES) >>
- VE_MODE_DETECT_V_LINES_SHF) - video->frame_bottom;
- det->vsync = (sync & VE_SYNC_STATUS_VSYNC) >>
- VE_SYNC_STATUS_VSYNC_SHF;
+
+ video->frame_bottom = FIELD_GET(VE_SRC_TB_EDGE_DET_BOT, src_tb_edge);
+ video->frame_top = FIELD_GET(VE_SRC_TB_EDGE_DET_TOP, src_tb_edge);
+
if (video->frame_top > video->frame_bottom)
continue;
- video->frame_right = (src_lr_edge & VE_SRC_LR_EDGE_DET_RT) >>
- VE_SRC_LR_EDGE_DET_RT_SHF;
- video->frame_left = src_lr_edge & VE_SRC_LR_EDGE_DET_LEFT;
- det->hfrontporch = video->frame_left;
- det->hbackporch = (mds & VE_MODE_DETECT_H_PIXELS) -
- video->frame_right;
- det->hsync = sync & VE_SYNC_STATUS_HSYNC;
+ video->frame_right = FIELD_GET(VE_SRC_LR_EDGE_DET_RT, src_lr_edge);
+ video->frame_left = FIELD_GET(VE_SRC_LR_EDGE_DET_LEFT, src_lr_edge);
+
if (video->frame_left > video->frame_right)
continue;
@@ -873,7 +1021,7 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
} while (invalid_resolution && (tries++ < INVALID_RESOLUTION_RETRIES));
if (invalid_resolution) {
- dev_dbg(video->dev, "Invalid resolution detected\n");
+ v4l2_warn(&video->v4l2_dev, "Invalid resolution detected\n");
return;
}
@@ -881,6 +1029,8 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
det->width = (video->frame_right - video->frame_left) + 1;
video->v4l2_input_status = 0;
+ aspeed_video_get_timings(video, det);
+
/*
* Enable mode-detect watchdog, resolution-change watchdog and
* automatic compression after frame capture.
@@ -890,8 +1040,8 @@ static void aspeed_video_get_resolution(struct aspeed_video *video)
aspeed_video_update(video, VE_SEQ_CTRL, 0,
VE_SEQ_CTRL_AUTO_COMP | VE_SEQ_CTRL_EN_WATCHDOG);
- dev_dbg(video->dev, "Got resolution: %dx%d\n", det->width,
- det->height);
+ v4l2_dbg(1, debug, &video->v4l2_dev, "Got resolution: %dx%d\n",
+ det->width, det->height);
}
static void aspeed_video_set_resolution(struct aspeed_video *video)
@@ -902,18 +1052,19 @@ static void aspeed_video_set_resolution(struct aspeed_video *video)
/* Set capture/compression frame sizes */
aspeed_video_calc_compressed_size(video, size);
- if (video->active_timings.width == 1680) {
+ if (!IS_ALIGNED(act->width, 64)) {
/*
- * This is a workaround to fix a silicon bug on A1 and A2
- * revisions. Since it doesn't break capturing operation of
+ * This is a workaround to fix a AST2500 silicon bug on A1 and
+ * A2 revisions. Since it doesn't break capturing operation of
* other revisions, use it for all revisions without checking
- * the revision ID. It picked 1728 which is a very next
- * 64-pixels aligned value to 1680 to minimize memory bandwidth
+ * the revision ID. It picked new width which is a very next
+ * 64-pixels aligned value to minimize memory bandwidth
* and to get better access speed from video engine.
*/
- aspeed_video_write(video, VE_CAP_WINDOW,
- 1728 << 16 | act->height);
- size += (1728 - 1680) * video->active_timings.height;
+ u32 width = ALIGN(act->width, 64);
+
+ aspeed_video_write(video, VE_CAP_WINDOW, width << 16 | act->height);
+ size = width * act->height;
} else {
aspeed_video_write(video, VE_CAP_WINDOW,
act->width << 16 | act->height);
@@ -924,6 +1075,7 @@ static void aspeed_video_set_resolution(struct aspeed_video *video)
/* Don't use direct mode below 1024 x 768 (irqs don't fire) */
if (size < DIRECT_FETCH_THRESHOLD) {
+ v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Sync Mode\n");
aspeed_video_write(video, VE_TGS_0,
FIELD_PREP(VE_TGS_FIRST,
video->frame_left - 1) |
@@ -935,6 +1087,7 @@ static void aspeed_video_set_resolution(struct aspeed_video *video)
video->frame_bottom + 1));
aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_INT_DE);
} else {
+ v4l2_dbg(1, debug, &video->v4l2_dev, "Capture: Direct Mode\n");
aspeed_video_update(video, VE_CTRL, 0, VE_CTRL_DIRECT_FETCH);
}
@@ -951,6 +1104,10 @@ static void aspeed_video_set_resolution(struct aspeed_video *video)
if (!aspeed_video_alloc_buf(video, &video->srcs[1], size))
goto err_mem;
+ v4l2_dbg(1, debug, &video->v4l2_dev, "src buf0 addr(%pad) size(%d)\n",
+ &video->srcs[0].dma, video->srcs[0].size);
+ v4l2_dbg(1, debug, &video->v4l2_dev, "src buf1 addr(%pad) size(%d)\n",
+ &video->srcs[1].dma, video->srcs[1].size);
aspeed_video_write(video, VE_SRC0_ADDR, video->srcs[0].dma);
aspeed_video_write(video, VE_SRC1_ADDR, video->srcs[1].dma);
}
@@ -969,7 +1126,8 @@ static void aspeed_video_init_regs(struct aspeed_video *video)
u32 comp_ctrl = VE_COMP_CTRL_RSVD |
FIELD_PREP(VE_COMP_CTRL_DCT_LUM, video->jpeg_quality) |
FIELD_PREP(VE_COMP_CTRL_DCT_CHR, video->jpeg_quality | 0x10);
- u32 ctrl = VE_CTRL_AUTO_OR_CURSOR;
+ u32 ctrl = VE_CTRL_AUTO_OR_CURSOR |
+ FIELD_PREP(VE_CTRL_CAPTURE_FMT, VIDEO_CAP_FMT_YUV_FULL_SWING);
u32 seq_ctrl = video->jpeg_mode;
if (video->frame_rate)
@@ -1004,7 +1162,12 @@ static void aspeed_video_init_regs(struct aspeed_video *video)
aspeed_video_write(video, VE_SCALING_FILTER3, 0x00200000);
/* Set mode detection defaults */
- aspeed_video_write(video, VE_MODE_DETECT, 0x22666500);
+ aspeed_video_write(video, VE_MODE_DETECT,
+ FIELD_PREP(VE_MODE_DT_HOR_TOLER, 2) |
+ FIELD_PREP(VE_MODE_DT_VER_TOLER, 2) |
+ FIELD_PREP(VE_MODE_DT_HOR_STABLE, 6) |
+ FIELD_PREP(VE_MODE_DT_VER_STABLE, 6) |
+ FIELD_PREP(VE_MODE_DT_EDG_THROD, 0x65));
}
static void aspeed_video_start(struct aspeed_video *video)
@@ -1111,7 +1274,7 @@ static int aspeed_video_get_parm(struct file *file, void *fh,
struct aspeed_video *video = video_drvdata(file);
a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- a->parm.capture.readbuffers = 3;
+ a->parm.capture.readbuffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ;
a->parm.capture.timeperframe.numerator = 1;
if (!video->frame_rate)
a->parm.capture.timeperframe.denominator = MAX_FRAME_RATE;
@@ -1128,7 +1291,7 @@ static int aspeed_video_set_parm(struct file *file, void *fh,
struct aspeed_video *video = video_drvdata(file);
a->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
- a->parm.capture.readbuffers = 3;
+ a->parm.capture.readbuffers = ASPEED_VIDEO_V4L2_MIN_BUF_REQ;
if (a->parm.capture.timeperframe.numerator)
frame_rate = a->parm.capture.timeperframe.denominator /
@@ -1215,6 +1378,9 @@ static int aspeed_video_set_dv_timings(struct file *file, void *fh,
timings->type = V4L2_DV_BT_656_1120;
+ v4l2_dbg(1, debug, &video->v4l2_dev, "set new timings(%dx%d)\n",
+ timings->bt.width, timings->bt.height);
+
return 0;
}
@@ -1395,6 +1561,7 @@ static void aspeed_video_resolution_work(struct work_struct *work)
.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
};
+ v4l2_dbg(1, debug, &video->v4l2_dev, "fire source change event\n");
v4l2_event_queue(&video->vdev, &ev);
} else if (test_bit(VIDEO_STREAMING, &video->flags)) {
/* No resolution change so just restart streaming */
@@ -1516,7 +1683,7 @@ static void aspeed_video_stop_streaming(struct vb2_queue *q)
!test_bit(VIDEO_FRAME_INPRG, &video->flags),
STOP_TIMEOUT);
if (!rc) {
- dev_dbg(video->dev, "Timed out when stopping streaming\n");
+ v4l2_warn(&video->v4l2_dev, "Timed out when stopping streaming\n");
/*
* Need to force stop any DMA and try and get HW into a good
@@ -1676,7 +1843,7 @@ static int aspeed_video_setup_video(struct aspeed_video *video)
vbq->drv_priv = video;
vbq->buf_struct_size = sizeof(struct aspeed_video_buffer);
vbq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- vbq->min_buffers_needed = 3;
+ vbq->min_buffers_needed = ASPEED_VIDEO_V4L2_MIN_BUF_REQ;
rc = vb2_queue_init(vbq);
if (rc) {
@@ -1730,6 +1897,7 @@ static int aspeed_video_init(struct aspeed_video *video)
dev_err(dev, "Unable to request IRQ %d\n", irq);
return rc;
}
+ dev_info(video->dev, "irq %d\n", irq);
video->eclk = devm_clk_get(dev, "eclk");
if (IS_ERR(video->eclk)) {
@@ -1766,6 +1934,8 @@ static int aspeed_video_init(struct aspeed_video *video)
rc = -ENOMEM;
goto err_release_reserved_mem;
}
+ dev_info(video->dev, "alloc mem size(%d) at %pad for jpeg header\n",
+ VE_JPEG_HEADER_SIZE, &video->jpeg.dma);
aspeed_video_init_jpeg_table(video->jpeg.virt, video->yuv420);
@@ -1791,7 +1961,6 @@ MODULE_DEVICE_TABLE(of, aspeed_video_of_match);
static int aspeed_video_probe(struct platform_device *pdev)
{
const struct aspeed_video_config *config;
- const struct of_device_id *match;
struct aspeed_video *video;
int rc;
@@ -1803,11 +1972,10 @@ static int aspeed_video_probe(struct platform_device *pdev)
if (IS_ERR(video->base))
return PTR_ERR(video->base);
- match = of_match_node(aspeed_video_of_match, pdev->dev.of_node);
- if (!match)
- return -EINVAL;
+ config = of_device_get_match_data(&pdev->dev);
+ if (!config)
+ return -ENODEV;
- config = match->data;
video->jpeg_mode = config->jpeg_mode;
video->comp_size_read = config->comp_size_read;
@@ -1875,6 +2043,9 @@ static struct platform_driver aspeed_video_driver = {
module_platform_driver(aspeed_video_driver);
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Debug level (0=off,1=info,2=debug,3=reg ops)");
+
MODULE_DESCRIPTION("ASPEED Video Engine Driver");
MODULE_AUTHOR("Eddie James");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/atmel/Kconfig b/drivers/media/platform/atmel/Kconfig
index dda2f27da317..83aebee0c8eb 100644
--- a/drivers/media/platform/atmel/Kconfig
+++ b/drivers/media/platform/atmel/Kconfig
@@ -1,7 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Atmel media platform drivers"
+
config VIDEO_ATMEL_ISC
tristate "ATMEL Image Sensor Controller (ISC) support"
- depends on VIDEO_V4L2 && COMMON_CLK
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && COMMON_CLK
depends on ARCH_AT91 || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
@@ -15,7 +19,8 @@ config VIDEO_ATMEL_ISC
config VIDEO_ATMEL_XISC
tristate "ATMEL eXtended Image Sensor Controller (XISC) support"
- depends on VIDEO_V4L2 && COMMON_CLK && VIDEO_V4L2_SUBDEV_API
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && COMMON_CLK && VIDEO_V4L2_SUBDEV_API
depends on ARCH_AT91 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select REGMAP_MMIO
@@ -33,10 +38,27 @@ config VIDEO_ATMEL_ISC_BASE
config VIDEO_ATMEL_ISI
tristate "ATMEL Image Sensor Interface (ISI) support"
- depends on VIDEO_V4L2 && OF
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
depends on ARCH_AT91 || COMPILE_TEST
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
help
This module makes the ATMEL Image Sensor Interface available
as a v4l2 device.
+
+config VIDEO_MICROCHIP_CSI2DC
+ tristate "Microchip CSI2 Demux Controller"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && COMMON_CLK && OF
+ depends on ARCH_AT91 || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ CSI2 Demux Controller driver. CSI2DC is a helper chip
+ that converts IDI interface byte stream to a parallel pixel stream.
+ It supports various RAW formats as input.
+
+ To compile this driver as a module, choose M here: the
+ module will be called microchip-csi2dc.
diff --git a/drivers/media/platform/atmel/Makefile b/drivers/media/platform/atmel/Makefile
index 46d264ab7948..794e8f739287 100644
--- a/drivers/media/platform/atmel/Makefile
+++ b/drivers/media/platform/atmel/Makefile
@@ -1,8 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
atmel-isc-objs = atmel-sama5d2-isc.o
atmel-xisc-objs = atmel-sama7g5-isc.o
+atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
-obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-base.o
+obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o
obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o
obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o
+obj-$(CONFIG_VIDEO_MICROCHIP_CSI2DC) += microchip-csi2dc.o
diff --git a/drivers/media/platform/atmel/atmel-isc-base.c b/drivers/media/platform/atmel/atmel-isc-base.c
index 660cd0ab6749..db15770d5b88 100644
--- a/drivers/media/platform/atmel/atmel-isc-base.c
+++ b/drivers/media/platform/atmel/atmel-isc-base.c
@@ -8,10 +8,6 @@
* Author: Eugen Hristev <eugen.hristev@microchip.com>
*
*/
-
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/math64.h>
@@ -100,297 +96,6 @@ static inline void isc_reset_awb_ctrls(struct isc_device *isc)
}
}
-static int isc_wait_clk_stable(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- struct regmap *regmap = isc_clk->regmap;
- unsigned long timeout = jiffies + usecs_to_jiffies(1000);
- unsigned int status;
-
- while (time_before(jiffies, timeout)) {
- regmap_read(regmap, ISC_CLKSR, &status);
- if (!(status & ISC_CLKSR_SIP))
- return 0;
-
- usleep_range(10, 250);
- }
-
- return -ETIMEDOUT;
-}
-
-static int isc_clk_prepare(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- int ret;
-
- ret = pm_runtime_resume_and_get(isc_clk->dev);
- if (ret < 0)
- return ret;
-
- return isc_wait_clk_stable(hw);
-}
-
-static void isc_clk_unprepare(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- isc_wait_clk_stable(hw);
-
- pm_runtime_put_sync(isc_clk->dev);
-}
-
-static int isc_clk_enable(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 id = isc_clk->id;
- struct regmap *regmap = isc_clk->regmap;
- unsigned long flags;
- unsigned int status;
-
- dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n",
- __func__, id, isc_clk->div, isc_clk->parent_id);
-
- spin_lock_irqsave(&isc_clk->lock, flags);
- regmap_update_bits(regmap, ISC_CLKCFG,
- ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),
- (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |
- (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));
-
- regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));
- spin_unlock_irqrestore(&isc_clk->lock, flags);
-
- regmap_read(regmap, ISC_CLKSR, &status);
- if (status & ISC_CLK(id))
- return 0;
- else
- return -EINVAL;
-}
-
-static void isc_clk_disable(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 id = isc_clk->id;
- unsigned long flags;
-
- spin_lock_irqsave(&isc_clk->lock, flags);
- regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id));
- spin_unlock_irqrestore(&isc_clk->lock, flags);
-}
-
-static int isc_clk_is_enabled(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 status;
- int ret;
-
- ret = pm_runtime_resume_and_get(isc_clk->dev);
- if (ret < 0)
- return 0;
-
- regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
-
- pm_runtime_put_sync(isc_clk->dev);
-
- return status & ISC_CLK(isc_clk->id) ? 1 : 0;
-}
-
-static unsigned long
-isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);
-}
-
-static int isc_clk_determine_rate(struct clk_hw *hw,
- struct clk_rate_request *req)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- long best_rate = -EINVAL;
- int best_diff = -1;
- unsigned int i, div;
-
- for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
- struct clk_hw *parent;
- unsigned long parent_rate;
-
- parent = clk_hw_get_parent_by_index(hw, i);
- if (!parent)
- continue;
-
- parent_rate = clk_hw_get_rate(parent);
- if (!parent_rate)
- continue;
-
- for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {
- unsigned long rate;
- int diff;
-
- rate = DIV_ROUND_CLOSEST(parent_rate, div);
- diff = abs(req->rate - rate);
-
- if (best_diff < 0 || best_diff > diff) {
- best_rate = rate;
- best_diff = diff;
- req->best_parent_rate = parent_rate;
- req->best_parent_hw = parent;
- }
-
- if (!best_diff || rate < req->rate)
- break;
- }
-
- if (!best_diff)
- break;
- }
-
- dev_dbg(isc_clk->dev,
- "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
- __func__, best_rate,
- __clk_get_name((req->best_parent_hw)->clk),
- req->best_parent_rate);
-
- if (best_rate < 0)
- return best_rate;
-
- req->rate = best_rate;
-
- return 0;
-}
-
-static int isc_clk_set_parent(struct clk_hw *hw, u8 index)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- if (index >= clk_hw_get_num_parents(hw))
- return -EINVAL;
-
- isc_clk->parent_id = index;
-
- return 0;
-}
-
-static u8 isc_clk_get_parent(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- return isc_clk->parent_id;
-}
-
-static int isc_clk_set_rate(struct clk_hw *hw,
- unsigned long rate,
- unsigned long parent_rate)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 div;
-
- if (!rate)
- return -EINVAL;
-
- div = DIV_ROUND_CLOSEST(parent_rate, rate);
- if (div > (ISC_CLK_MAX_DIV + 1) || !div)
- return -EINVAL;
-
- isc_clk->div = div - 1;
-
- return 0;
-}
-
-static const struct clk_ops isc_clk_ops = {
- .prepare = isc_clk_prepare,
- .unprepare = isc_clk_unprepare,
- .enable = isc_clk_enable,
- .disable = isc_clk_disable,
- .is_enabled = isc_clk_is_enabled,
- .recalc_rate = isc_clk_recalc_rate,
- .determine_rate = isc_clk_determine_rate,
- .set_parent = isc_clk_set_parent,
- .get_parent = isc_clk_get_parent,
- .set_rate = isc_clk_set_rate,
-};
-
-static int isc_clk_register(struct isc_device *isc, unsigned int id)
-{
- struct regmap *regmap = isc->regmap;
- struct device_node *np = isc->dev->of_node;
- struct isc_clk *isc_clk;
- struct clk_init_data init;
- const char *clk_name = np->name;
- const char *parent_names[3];
- int num_parents;
-
- if (id == ISC_ISPCK && !isc->ispck_required)
- return 0;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents < 1 || num_parents > 3)
- return -EINVAL;
-
- if (num_parents > 2 && id == ISC_ISPCK)
- num_parents = 2;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- if (id == ISC_MCK)
- of_property_read_string(np, "clock-output-names", &clk_name);
- else
- clk_name = "isc-ispck";
-
- init.parent_names = parent_names;
- init.num_parents = num_parents;
- init.name = clk_name;
- init.ops = &isc_clk_ops;
- init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
-
- isc_clk = &isc->isc_clks[id];
- isc_clk->hw.init = &init;
- isc_clk->regmap = regmap;
- isc_clk->id = id;
- isc_clk->dev = isc->dev;
- spin_lock_init(&isc_clk->lock);
-
- isc_clk->clk = clk_register(isc->dev, &isc_clk->hw);
- if (IS_ERR(isc_clk->clk)) {
- dev_err(isc->dev, "%s: clock register fail\n", clk_name);
- return PTR_ERR(isc_clk->clk);
- } else if (id == ISC_MCK)
- of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);
-
- return 0;
-}
-
-int isc_clk_init(struct isc_device *isc)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)
- isc->isc_clks[i].clk = ERR_PTR(-EINVAL);
-
- for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
- ret = isc_clk_register(isc, i);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(isc_clk_init);
-
-void isc_clk_cleanup(struct isc_device *isc)
-{
- unsigned int i;
-
- of_clk_del_provider(isc->dev->of_node);
-
- for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
- struct isc_clk *isc_clk = &isc->isc_clks[i];
-
- if (!IS_ERR(isc_clk->clk))
- clk_unregister(isc_clk->clk);
- }
-}
-EXPORT_SYMBOL_GPL(isc_clk_cleanup);
static int isc_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
@@ -912,6 +617,7 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 8;
+ isc->try_config.bpp_v4l2 = 8;
break;
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
@@ -921,6 +627,7 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
@@ -930,24 +637,28 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_RGB565:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_ARGB444:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_ARGB555:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_ABGR32:
case V4L2_PIX_FMT_XBGR32:
@@ -955,42 +666,49 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 32;
+ isc->try_config.bpp_v4l2 = 32;
break;
case V4L2_PIX_FMT_YUV420:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
isc->try_config.bpp = 12;
+ isc->try_config.bpp_v4l2 = 8; /* only first plane */
break;
case V4L2_PIX_FMT_YUV422P:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 8; /* only first plane */
break;
case V4L2_PIX_FMT_YUYV:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_UYVY:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_VYUY:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
case V4L2_PIX_FMT_GREY:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8;
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 8;
+ isc->try_config.bpp_v4l2 = 8;
break;
case V4L2_PIX_FMT_Y16:
isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH;
@@ -1000,6 +718,7 @@ static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
isc->try_config.bpp = 16;
+ isc->try_config.bpp_v4l2 = 16;
break;
default:
return -EINVAL;
@@ -1248,8 +967,9 @@ static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
pixfmt->height = isc->max_height;
pixfmt->field = V4L2_FIELD_NONE;
- pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp) >> 3;
- pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+ pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3;
+ pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) *
+ pixfmt->height;
if (code)
*code = mbus_code;
@@ -1369,14 +1089,12 @@ static int isc_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct isc_device *isc = video_drvdata(file);
- struct v4l2_subdev_frame_size_enum fse = {
- .code = isc->config.sd_format->mbus_code,
- .index = fsize->index,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
int ret = -EINVAL;
int i;
+ if (fsize->index)
+ return -EINVAL;
+
for (i = 0; i < isc->num_user_formats; i++)
if (isc->user_formats[i]->fourcc == fsize->pixel_format)
ret = 0;
@@ -1388,50 +1106,14 @@ static int isc_enum_framesizes(struct file *file, void *fh,
if (ret)
return ret;
- ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
- NULL, &fse);
- if (ret)
- return ret;
-
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = fse.max_width;
- fsize->discrete.height = fse.max_height;
-
- return 0;
-}
-
-static int isc_enum_frameintervals(struct file *file, void *fh,
- struct v4l2_frmivalenum *fival)
-{
- struct isc_device *isc = video_drvdata(file);
- struct v4l2_subdev_frame_interval_enum fie = {
- .code = isc->config.sd_format->mbus_code,
- .index = fival->index,
- .width = fival->width,
- .height = fival->height,
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- int ret = -EINVAL;
- unsigned int i;
-
- for (i = 0; i < isc->num_user_formats; i++)
- if (isc->user_formats[i]->fourcc == fival->pixel_format)
- ret = 0;
-
- for (i = 0; i < isc->controller_formats_size; i++)
- if (isc->controller_formats[i].fourcc == fival->pixel_format)
- ret = 0;
-
- if (ret)
- return ret;
-
- ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
- enum_frame_interval, NULL, &fie);
- if (ret)
- return ret;
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete = fie.interval;
+ fsize->stepwise.min_width = 16;
+ fsize->stepwise.max_width = isc->max_width;
+ fsize->stepwise.min_height = 16;
+ fsize->stepwise.max_height = isc->max_height;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
return 0;
}
@@ -1460,7 +1142,6 @@ static const struct v4l2_ioctl_ops isc_ioctl_ops = {
.vidioc_g_parm = isc_g_parm,
.vidioc_s_parm = isc_s_parm,
.vidioc_enum_framesizes = isc_enum_framesizes,
- .vidioc_enum_frameintervals = isc_enum_frameintervals,
.vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
@@ -1608,10 +1289,15 @@ static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max)
if (!*min)
*min = 1;
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: hist_id %u, hist_count %u",
+ ctrls->hist_id, *hist_count);
}
static void isc_wb_update(struct isc_ctrls *ctrls)
{
+ struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls);
u32 *hist_count = &ctrls->hist_count[0];
u32 c, offset[4];
u64 avg = 0;
@@ -1628,6 +1314,9 @@ static void isc_wb_update(struct isc_ctrls *ctrls)
(u64)hist_count[ISC_HIS_CFG_MODE_GB];
avg >>= 1;
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: green components average %llu\n", avg);
+
/* Green histogram is null, nothing to do */
if (!avg)
return;
@@ -1680,9 +1369,19 @@ static void isc_wb_update(struct isc_ctrls *ctrls)
else
gw_gain[c] = 1 << 9;
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: component %d, s_gain %u, gw_gain %u\n",
+ c, s_gain[c], gw_gain[c]);
/* multiply both gains and adjust for decimals */
ctrls->gain[c] = s_gain[c] * gw_gain[c];
ctrls->gain[c] >>= 9;
+
+ /* make sure we are not out of range */
+ ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0));
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb: component %d, final gain %u\n",
+ c, ctrls->gain[c]);
}
}
@@ -1706,6 +1405,10 @@ static void isc_awb_work(struct work_struct *w)
return;
isc_hist_count(isc, &min, &max);
+
+ v4l2_dbg(1, debug, &isc->v4l2_dev,
+ "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max);
+
ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min;
ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max;
@@ -2181,7 +1884,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
}
/* Register video device */
- strscpy(vdev->name, "microchip-isc", sizeof(vdev->name));
+ strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
vdev->release = video_device_release_empty;
vdev->fops = &isc_fops;
vdev->ioctl_ops = &isc_ioctl_ops;
diff --git a/drivers/media/platform/atmel/atmel-isc-clk.c b/drivers/media/platform/atmel/atmel-isc-clk.c
new file mode 100644
index 000000000000..2059fe376b00
--- /dev/null
+++ b/drivers/media/platform/atmel/atmel-isc-clk.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Microchip Image Sensor Controller (ISC) common clock driver setup
+ *
+ * Copyright (C) 2016 Microchip Technology, Inc.
+ *
+ * Author: Songjun Wu
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include "atmel-isc-regs.h"
+#include "atmel-isc.h"
+
+static int isc_wait_clk_stable(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ struct regmap *regmap = isc_clk->regmap;
+ unsigned long timeout = jiffies + usecs_to_jiffies(1000);
+ unsigned int status;
+
+ while (time_before(jiffies, timeout)) {
+ regmap_read(regmap, ISC_CLKSR, &status);
+ if (!(status & ISC_CLKSR_SIP))
+ return 0;
+
+ usleep_range(10, 250);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int isc_clk_prepare(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(isc_clk->dev);
+ if (ret < 0)
+ return ret;
+
+ return isc_wait_clk_stable(hw);
+}
+
+static void isc_clk_unprepare(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ isc_wait_clk_stable(hw);
+
+ pm_runtime_put_sync(isc_clk->dev);
+}
+
+static int isc_clk_enable(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 id = isc_clk->id;
+ struct regmap *regmap = isc_clk->regmap;
+ unsigned long flags;
+ unsigned int status;
+
+ dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n",
+ __func__, id, isc_clk->div, isc_clk->parent_id);
+
+ spin_lock_irqsave(&isc_clk->lock, flags);
+ regmap_update_bits(regmap, ISC_CLKCFG,
+ ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),
+ (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |
+ (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));
+
+ regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));
+ spin_unlock_irqrestore(&isc_clk->lock, flags);
+
+ regmap_read(regmap, ISC_CLKSR, &status);
+ if (status & ISC_CLK(id))
+ return 0;
+ else
+ return -EINVAL;
+}
+
+static void isc_clk_disable(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 id = isc_clk->id;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isc_clk->lock, flags);
+ regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id));
+ spin_unlock_irqrestore(&isc_clk->lock, flags);
+}
+
+static int isc_clk_is_enabled(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 status;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(isc_clk->dev);
+ if (ret < 0)
+ return 0;
+
+ regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
+
+ pm_runtime_put_sync(isc_clk->dev);
+
+ return status & ISC_CLK(isc_clk->id) ? 1 : 0;
+}
+
+static unsigned long
+isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);
+}
+
+static int isc_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ long best_rate = -EINVAL;
+ int best_diff = -1;
+ unsigned int i, div;
+
+ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+ struct clk_hw *parent;
+ unsigned long parent_rate;
+
+ parent = clk_hw_get_parent_by_index(hw, i);
+ if (!parent)
+ continue;
+
+ parent_rate = clk_hw_get_rate(parent);
+ if (!parent_rate)
+ continue;
+
+ for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {
+ unsigned long rate;
+ int diff;
+
+ rate = DIV_ROUND_CLOSEST(parent_rate, div);
+ diff = abs(req->rate - rate);
+
+ if (best_diff < 0 || best_diff > diff) {
+ best_rate = rate;
+ best_diff = diff;
+ req->best_parent_rate = parent_rate;
+ req->best_parent_hw = parent;
+ }
+
+ if (!best_diff || rate < req->rate)
+ break;
+ }
+
+ if (!best_diff)
+ break;
+ }
+
+ dev_dbg(isc_clk->dev,
+ "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
+ __func__, best_rate,
+ __clk_get_name((req->best_parent_hw)->clk),
+ req->best_parent_rate);
+
+ if (best_rate < 0)
+ return best_rate;
+
+ req->rate = best_rate;
+
+ return 0;
+}
+
+static int isc_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ if (index >= clk_hw_get_num_parents(hw))
+ return -EINVAL;
+
+ isc_clk->parent_id = index;
+
+ return 0;
+}
+
+static u8 isc_clk_get_parent(struct clk_hw *hw)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+
+ return isc_clk->parent_id;
+}
+
+static int isc_clk_set_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct isc_clk *isc_clk = to_isc_clk(hw);
+ u32 div;
+
+ if (!rate)
+ return -EINVAL;
+
+ div = DIV_ROUND_CLOSEST(parent_rate, rate);
+ if (div > (ISC_CLK_MAX_DIV + 1) || !div)
+ return -EINVAL;
+
+ isc_clk->div = div - 1;
+
+ return 0;
+}
+
+static const struct clk_ops isc_clk_ops = {
+ .prepare = isc_clk_prepare,
+ .unprepare = isc_clk_unprepare,
+ .enable = isc_clk_enable,
+ .disable = isc_clk_disable,
+ .is_enabled = isc_clk_is_enabled,
+ .recalc_rate = isc_clk_recalc_rate,
+ .determine_rate = isc_clk_determine_rate,
+ .set_parent = isc_clk_set_parent,
+ .get_parent = isc_clk_get_parent,
+ .set_rate = isc_clk_set_rate,
+};
+
+static int isc_clk_register(struct isc_device *isc, unsigned int id)
+{
+ struct regmap *regmap = isc->regmap;
+ struct device_node *np = isc->dev->of_node;
+ struct isc_clk *isc_clk;
+ struct clk_init_data init;
+ const char *clk_name = np->name;
+ const char *parent_names[3];
+ int num_parents;
+
+ if (id == ISC_ISPCK && !isc->ispck_required)
+ return 0;
+
+ num_parents = of_clk_get_parent_count(np);
+ if (num_parents < 1 || num_parents > 3)
+ return -EINVAL;
+
+ if (num_parents > 2 && id == ISC_ISPCK)
+ num_parents = 2;
+
+ of_clk_parent_fill(np, parent_names, num_parents);
+
+ if (id == ISC_MCK)
+ of_property_read_string(np, "clock-output-names", &clk_name);
+ else
+ clk_name = "isc-ispck";
+
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.name = clk_name;
+ init.ops = &isc_clk_ops;
+ init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+ isc_clk = &isc->isc_clks[id];
+ isc_clk->hw.init = &init;
+ isc_clk->regmap = regmap;
+ isc_clk->id = id;
+ isc_clk->dev = isc->dev;
+ spin_lock_init(&isc_clk->lock);
+
+ isc_clk->clk = clk_register(isc->dev, &isc_clk->hw);
+ if (IS_ERR(isc_clk->clk)) {
+ dev_err(isc->dev, "%s: clock register fail\n", clk_name);
+ return PTR_ERR(isc_clk->clk);
+ } else if (id == ISC_MCK) {
+ of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);
+ }
+
+ return 0;
+}
+
+int isc_clk_init(struct isc_device *isc)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)
+ isc->isc_clks[i].clk = ERR_PTR(-EINVAL);
+
+ for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
+ ret = isc_clk_register(isc, i);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(isc_clk_init);
+
+void isc_clk_cleanup(struct isc_device *isc)
+{
+ unsigned int i;
+
+ of_clk_del_provider(isc->dev->of_node);
+
+ for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
+ struct isc_clk *isc_clk = &isc->isc_clks[i];
+
+ if (!IS_ERR(isc_clk->clk))
+ clk_unregister(isc_clk->clk);
+ }
+}
+EXPORT_SYMBOL_GPL(isc_clk_cleanup);
diff --git a/drivers/media/platform/atmel/atmel-isc.h b/drivers/media/platform/atmel/atmel-isc.h
index 2bfcb135ef13..07fa6dbf8460 100644
--- a/drivers/media/platform/atmel/atmel-isc.h
+++ b/drivers/media/platform/atmel/atmel-isc.h
@@ -10,6 +10,13 @@
*/
#ifndef _ATMEL_ISC_H_
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-contig.h>
+
#define ISC_CLK_MAX_DIV 255
enum isc_clk_id {
@@ -95,6 +102,9 @@ struct isc_format {
configuration.
* @fourcc: Fourcc code for this format.
* @bpp: Bytes per pixel in the current format.
+ * @bpp_v4l2: Bytes per pixel in the current format, for v4l2.
+ This differs from 'bpp' in the sense that in planar
+ formats, it refers only to the first plane.
* @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging)
* @dcfg_imode: Configuration of the input of the DMA module
* @dctrl_dview: Configuration of the output of the DMA module
@@ -105,6 +115,7 @@ struct fmt_config {
u32 fourcc;
u8 bpp;
+ u8 bpp_v4l2;
u32 rlp_cfg_mode;
u32 dcfg_imode;
diff --git a/drivers/media/platform/atmel/atmel-sama5d2-isc.c b/drivers/media/platform/atmel/atmel-sama5d2-isc.c
index 1b2063cce0f7..c5b9563e36cb 100644
--- a/drivers/media/platform/atmel/atmel-sama5d2-isc.c
+++ b/drivers/media/platform/atmel/atmel-sama5d2-isc.c
@@ -88,6 +88,30 @@ static const struct isc_format sama5d2_controller_formats[] = {
{
.fourcc = V4L2_PIX_FMT_Y10,
},
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ },
};
/* This is a list of formats that the ISC can receive as *input* */
diff --git a/drivers/media/platform/atmel/atmel-sama7g5-isc.c b/drivers/media/platform/atmel/atmel-sama7g5-isc.c
index 5d1c76f680f3..07a80b08bc54 100644
--- a/drivers/media/platform/atmel/atmel-sama7g5-isc.c
+++ b/drivers/media/platform/atmel/atmel-sama7g5-isc.c
@@ -100,6 +100,30 @@ static const struct isc_format sama7g5_controller_formats[] = {
{
.fourcc = V4L2_PIX_FMT_Y16,
},
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ },
};
/* This is a list of formats that the ISC can receive as *input* */
@@ -188,7 +212,7 @@ static struct isc_format sama7g5_formats_list[] = {
},
{
.fourcc = V4L2_PIX_FMT_UYVY,
- .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
.pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
},
{
@@ -556,7 +580,6 @@ static int microchip_xisc_remove(struct platform_device *pdev)
v4l2_device_unregister(&isc->v4l2_dev);
- clk_disable_unprepare(isc->ispck);
clk_disable_unprepare(isc->hclock);
isc_clk_cleanup(isc);
@@ -568,7 +591,6 @@ static int __maybe_unused xisc_runtime_suspend(struct device *dev)
{
struct isc_device *isc = dev_get_drvdata(dev);
- clk_disable_unprepare(isc->ispck);
clk_disable_unprepare(isc->hclock);
return 0;
@@ -583,10 +605,6 @@ static int __maybe_unused xisc_runtime_resume(struct device *dev)
if (ret)
return ret;
- ret = clk_prepare_enable(isc->ispck);
- if (ret)
- clk_disable_unprepare(isc->hclock);
-
return ret;
}
diff --git a/drivers/media/platform/atmel/microchip-csi2dc.c b/drivers/media/platform/atmel/microchip-csi2dc.c
new file mode 100644
index 000000000000..2487978db1f1
--- /dev/null
+++ b/drivers/media/platform/atmel/microchip-csi2dc.c
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Microchip CSI2 Demux Controller (CSI2DC) driver
+ *
+ * Copyright (C) 2018 Microchip Technology, Inc.
+ *
+ * Author: Eugen Hristev <eugen.hristev@microchip.com>
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Global configuration register */
+#define CSI2DC_GCFG 0x0
+
+/* MIPI sensor pixel clock is free running */
+#define CSI2DC_GCFG_MIPIFRN BIT(0)
+/* GPIO parallel interface selection */
+#define CSI2DC_GCFG_GPIOSEL BIT(1)
+/* Output waveform inter-line minimum delay */
+#define CSI2DC_GCFG_HLC(v) ((v) << 4)
+#define CSI2DC_GCFG_HLC_MASK GENMASK(7, 4)
+/* SAMA7G5 requires a HLC delay of 15 */
+#define SAMA7G5_HLC (15)
+
+/* Global control register */
+#define CSI2DC_GCTLR 0x04
+#define CSI2DC_GCTLR_SWRST BIT(0)
+
+/* Global status register */
+#define CSI2DC_GS 0x08
+
+/* SSP interrupt status register */
+#define CSI2DC_SSPIS 0x28
+/* Pipe update register */
+#define CSI2DC_PU 0xc0
+/* Video pipe attributes update */
+#define CSI2DC_PU_VP BIT(0)
+
+/* Pipe update status register */
+#define CSI2DC_PUS 0xc4
+
+/* Video pipeline Interrupt Status Register */
+#define CSI2DC_VPISR 0xf4
+
+/* Video pipeline enable register */
+#define CSI2DC_VPE 0xf8
+#define CSI2DC_VPE_ENABLE BIT(0)
+
+/* Video pipeline configuration register */
+#define CSI2DC_VPCFG 0xfc
+/* Data type */
+#define CSI2DC_VPCFG_DT(v) ((v) << 0)
+#define CSI2DC_VPCFG_DT_MASK GENMASK(5, 0)
+/* Virtual channel identifier */
+#define CSI2DC_VPCFG_VC(v) ((v) << 6)
+#define CSI2DC_VPCFG_VC_MASK GENMASK(7, 6)
+/* Decompression enable */
+#define CSI2DC_VPCFG_DE BIT(8)
+/* Decoder mode */
+#define CSI2DC_VPCFG_DM(v) ((v) << 9)
+#define CSI2DC_VPCFG_DM_DECODER8TO12 0
+/* Decoder predictor 2 selection */
+#define CSI2DC_VPCFG_DP2 BIT(12)
+/* Recommended memory storage */
+#define CSI2DC_VPCFG_RMS BIT(13)
+/* Post adjustment */
+#define CSI2DC_VPCFG_PA BIT(14)
+
+/* Video pipeline column register */
+#define CSI2DC_VPCOL 0x100
+/* Column number */
+#define CSI2DC_VPCOL_COL(v) ((v) << 0)
+#define CSI2DC_VPCOL_COL_MASK GENMASK(15, 0)
+
+/* Video pipeline row register */
+#define CSI2DC_VPROW 0x104
+/* Row number */
+#define CSI2DC_VPROW_ROW(v) ((v) << 0)
+#define CSI2DC_VPROW_ROW_MASK GENMASK(15, 0)
+
+/* Version register */
+#define CSI2DC_VERSION 0x1fc
+
+/* register read/write helpers */
+#define csi2dc_readl(st, reg) readl_relaxed((st)->base + (reg))
+#define csi2dc_writel(st, reg, val) writel_relaxed((val), \
+ (st)->base + (reg))
+
+/* supported RAW data types */
+#define CSI2DC_DT_RAW6 0x28
+#define CSI2DC_DT_RAW7 0x29
+#define CSI2DC_DT_RAW8 0x2a
+#define CSI2DC_DT_RAW10 0x2b
+#define CSI2DC_DT_RAW12 0x2c
+#define CSI2DC_DT_RAW14 0x2d
+/* YUV data types */
+#define CSI2DC_DT_YUV422_8B 0x1e
+
+/*
+ * struct csi2dc_format - CSI2DC format type struct
+ * @mbus_code: Media bus code for the format
+ * @dt: Data type constant for this format
+ */
+struct csi2dc_format {
+ u32 mbus_code;
+ u32 dt;
+};
+
+static const struct csi2dc_format csi2dc_formats[] = {
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .dt = CSI2DC_DT_RAW8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .dt = CSI2DC_DT_RAW8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .dt = CSI2DC_DT_RAW8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .dt = CSI2DC_DT_RAW8,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .dt = CSI2DC_DT_RAW10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .dt = CSI2DC_DT_RAW10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .dt = CSI2DC_DT_RAW10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .dt = CSI2DC_DT_RAW10,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
+ .dt = CSI2DC_DT_YUV422_8B,
+ },
+};
+
+enum mipi_csi_pads {
+ CSI2DC_PAD_SINK = 0,
+ CSI2DC_PAD_SOURCE = 1,
+ CSI2DC_PADS_NUM = 2,
+};
+
+/*
+ * struct csi2dc_device - CSI2DC device driver data/config struct
+ * @base: Register map base address
+ * @csi2dc_sd: v4l2 subdevice for the csi2dc device
+ * This is the subdevice that the csi2dc device itself
+ * registers in v4l2 subsystem
+ * @dev: struct device for this csi2dc device
+ * @pclk: Peripheral clock reference
+ * Input clock that clocks the hardware block internal
+ * logic
+ * @scck: Sensor Controller clock reference
+ * Input clock that is used to generate the pixel clock
+ * @format: Current saved format used in g/s fmt
+ * @cur_fmt: Current state format
+ * @try_fmt: Try format that is being tried
+ * @pads: Media entity pads for the csi2dc subdevice
+ * @clk_gated: Whether the clock is gated or free running
+ * @video_pipe: Whether video pipeline is configured
+ * @parallel_mode: The underlying subdevice is connected on a parallel bus
+ * @vc: Current set virtual channel
+ * @notifier: Async notifier that is used to bound the underlying
+ * subdevice to the csi2dc subdevice
+ * @input_sd: Reference to the underlying subdevice bound to the
+ * csi2dc subdevice
+ * @remote_pad: Pad number of the underlying subdevice that is linked
+ * to the csi2dc subdevice sink pad.
+ */
+struct csi2dc_device {
+ void __iomem *base;
+ struct v4l2_subdev csi2dc_sd;
+ struct device *dev;
+ struct clk *pclk;
+ struct clk *scck;
+
+ struct v4l2_mbus_framefmt format;
+
+ const struct csi2dc_format *cur_fmt;
+ const struct csi2dc_format *try_fmt;
+
+ struct media_pad pads[CSI2DC_PADS_NUM];
+
+ bool clk_gated;
+ bool video_pipe;
+ bool parallel_mode;
+ u32 vc;
+
+ struct v4l2_async_notifier notifier;
+
+ struct v4l2_subdev *input_sd;
+
+ u32 remote_pad;
+};
+
+static inline struct csi2dc_device *
+csi2dc_sd_to_csi2dc_device(struct v4l2_subdev *csi2dc_sd)
+{
+ return container_of(csi2dc_sd, struct csi2dc_device, csi2dc_sd);
+}
+
+static int csi2dc_enum_mbus_code(struct v4l2_subdev *csi2dc_sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(csi2dc_formats))
+ return -EINVAL;
+
+ code->code = csi2dc_formats[code->index].mbus_code;
+
+ return 0;
+}
+
+static int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd);
+ struct v4l2_mbus_framefmt *v4l2_try_fmt;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state,
+ format->pad);
+ format->format = *v4l2_try_fmt;
+
+ return 0;
+ }
+
+ format->format = csi2dc->format;
+
+ return 0;
+}
+
+static int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *req_fmt)
+{
+ struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd);
+ const struct csi2dc_format *fmt, *try_fmt = NULL;
+ struct v4l2_mbus_framefmt *v4l2_try_fmt;
+ unsigned int i;
+
+ /*
+ * Setting the source pad is disabled.
+ * The same format is being propagated from the sink to source.
+ */
+ if (req_fmt->pad == CSI2DC_PAD_SOURCE)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(csi2dc_formats); i++) {
+ fmt = &csi2dc_formats[i];
+ if (req_fmt->format.code == fmt->mbus_code)
+ try_fmt = fmt;
+ fmt++;
+ }
+
+ /* in case we could not find the desired format, default to something */
+ if (!try_fmt) {
+ try_fmt = &csi2dc_formats[0];
+
+ dev_dbg(csi2dc->dev,
+ "CSI2DC unsupported format 0x%x, defaulting to 0x%x\n",
+ req_fmt->format.code, csi2dc_formats[0].mbus_code);
+ }
+
+ req_fmt->format.code = try_fmt->mbus_code;
+ req_fmt->format.colorspace = V4L2_COLORSPACE_SRGB;
+ req_fmt->format.field = V4L2_FIELD_NONE;
+
+ if (req_fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+ v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd, sd_state,
+ req_fmt->pad);
+ *v4l2_try_fmt = req_fmt->format;
+ /* Trying on the sink pad makes the source pad change too */
+ v4l2_try_fmt = v4l2_subdev_get_try_format(csi2dc_sd,
+ sd_state,
+ CSI2DC_PAD_SOURCE);
+ *v4l2_try_fmt = req_fmt->format;
+
+ /* if we are just trying, we are done */
+ return 0;
+ }
+
+ /* save the format for later requests */
+ csi2dc->format = req_fmt->format;
+
+ /* update config */
+ csi2dc->cur_fmt = try_fmt;
+
+ dev_dbg(csi2dc->dev, "new format set: 0x%x @%dx%d\n",
+ csi2dc->format.code, csi2dc->format.width,
+ csi2dc->format.height);
+
+ return 0;
+}
+
+static int csi2dc_power(struct csi2dc_device *csi2dc, int on)
+{
+ int ret = 0;
+
+ if (on) {
+ ret = clk_prepare_enable(csi2dc->pclk);
+ if (ret) {
+ dev_err(csi2dc->dev, "failed to enable pclk:%d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(csi2dc->scck);
+ if (ret) {
+ dev_err(csi2dc->dev, "failed to enable scck:%d\n", ret);
+ clk_disable_unprepare(csi2dc->pclk);
+ return ret;
+ }
+
+ /* if powering up, deassert reset line */
+ csi2dc_writel(csi2dc, CSI2DC_GCTLR, CSI2DC_GCTLR_SWRST);
+ } else {
+ /* if powering down, assert reset line */
+ csi2dc_writel(csi2dc, CSI2DC_GCTLR, 0);
+
+ clk_disable_unprepare(csi2dc->scck);
+ clk_disable_unprepare(csi2dc->pclk);
+ }
+
+ return ret;
+}
+
+static int csi2dc_get_mbus_config(struct csi2dc_device *csi2dc)
+{
+ struct v4l2_mbus_config mbus_config = { 0 };
+ int ret;
+
+ ret = v4l2_subdev_call(csi2dc->input_sd, pad, get_mbus_config,
+ csi2dc->remote_pad, &mbus_config);
+ if (ret == -ENOIOCTLCMD) {
+ dev_dbg(csi2dc->dev,
+ "no remote mbus configuration available\n");
+ return 0;
+ }
+
+ if (ret) {
+ dev_err(csi2dc->dev,
+ "failed to get remote mbus configuration\n");
+ return 0;
+ }
+
+ dev_dbg(csi2dc->dev, "subdev sending on channel %d\n", csi2dc->vc);
+
+ csi2dc->clk_gated = mbus_config.bus.parallel.flags &
+ V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
+
+ dev_dbg(csi2dc->dev, "mbus_config: %s clock\n",
+ csi2dc->clk_gated ? "gated" : "free running");
+
+ return 0;
+}
+
+static void csi2dc_vp_update(struct csi2dc_device *csi2dc)
+{
+ u32 vp, gcfg;
+
+ if (!csi2dc->video_pipe) {
+ dev_err(csi2dc->dev, "video pipeline unavailable\n");
+ return;
+ }
+
+ if (csi2dc->parallel_mode) {
+ /* In parallel mode, GPIO parallel interface must be selected */
+ gcfg = csi2dc_readl(csi2dc, CSI2DC_GCFG);
+ gcfg |= CSI2DC_GCFG_GPIOSEL;
+ csi2dc_writel(csi2dc, CSI2DC_GCFG, gcfg);
+ return;
+ }
+
+ /* serial video pipeline */
+
+ csi2dc_writel(csi2dc, CSI2DC_GCFG,
+ (SAMA7G5_HLC & CSI2DC_GCFG_HLC_MASK) |
+ (csi2dc->clk_gated ? 0 : CSI2DC_GCFG_MIPIFRN));
+
+ vp = CSI2DC_VPCFG_DT(csi2dc->cur_fmt->dt) & CSI2DC_VPCFG_DT_MASK;
+ vp |= CSI2DC_VPCFG_VC(csi2dc->vc) & CSI2DC_VPCFG_VC_MASK;
+ vp &= ~CSI2DC_VPCFG_DE;
+ vp |= CSI2DC_VPCFG_DM(CSI2DC_VPCFG_DM_DECODER8TO12);
+ vp &= ~CSI2DC_VPCFG_DP2;
+ vp &= ~CSI2DC_VPCFG_RMS;
+ vp |= CSI2DC_VPCFG_PA;
+
+ csi2dc_writel(csi2dc, CSI2DC_VPCFG, vp);
+ csi2dc_writel(csi2dc, CSI2DC_VPE, CSI2DC_VPE_ENABLE);
+ csi2dc_writel(csi2dc, CSI2DC_PU, CSI2DC_PU_VP);
+}
+
+static int csi2dc_s_stream(struct v4l2_subdev *csi2dc_sd, int enable)
+{
+ struct csi2dc_device *csi2dc = csi2dc_sd_to_csi2dc_device(csi2dc_sd);
+ int ret;
+
+ if (enable) {
+ ret = pm_runtime_resume_and_get(csi2dc->dev);
+ if (ret < 0)
+ return ret;
+
+ csi2dc_get_mbus_config(csi2dc);
+
+ csi2dc_vp_update(csi2dc);
+
+ return v4l2_subdev_call(csi2dc->input_sd, video, s_stream,
+ true);
+ }
+
+ dev_dbg(csi2dc->dev,
+ "Last frame received: VPCOLR = %u, VPROWR= %u, VPISR = %x\n",
+ csi2dc_readl(csi2dc, CSI2DC_VPCOL),
+ csi2dc_readl(csi2dc, CSI2DC_VPROW),
+ csi2dc_readl(csi2dc, CSI2DC_VPISR));
+
+ /* stop streaming scenario */
+ ret = v4l2_subdev_call(csi2dc->input_sd, video, s_stream, false);
+
+ pm_runtime_put_sync(csi2dc->dev);
+
+ return ret;
+}
+
+static int csi2dc_init_cfg(struct v4l2_subdev *csi2dc_sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_mbus_framefmt *v4l2_try_fmt =
+ v4l2_subdev_get_try_format(csi2dc_sd, sd_state, 0);
+
+ v4l2_try_fmt->height = 480;
+ v4l2_try_fmt->width = 640;
+ v4l2_try_fmt->code = csi2dc_formats[0].mbus_code;
+ v4l2_try_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ v4l2_try_fmt->field = V4L2_FIELD_NONE;
+ v4l2_try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ v4l2_try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT;
+ v4l2_try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops csi2dc_pad_ops = {
+ .enum_mbus_code = csi2dc_enum_mbus_code,
+ .set_fmt = csi2dc_set_fmt,
+ .get_fmt = csi2dc_get_fmt,
+ .init_cfg = csi2dc_init_cfg,
+};
+
+static const struct v4l2_subdev_video_ops csi2dc_video_ops = {
+ .s_stream = csi2dc_s_stream,
+};
+
+static const struct v4l2_subdev_ops csi2dc_subdev_ops = {
+ .pad = &csi2dc_pad_ops,
+ .video = &csi2dc_video_ops,
+};
+
+static int csi2dc_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_subdev *asd)
+{
+ struct csi2dc_device *csi2dc = container_of(notifier,
+ struct csi2dc_device, notifier);
+ int pad;
+ int ret;
+
+ csi2dc->input_sd = subdev;
+
+ pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (pad < 0) {
+ dev_err(csi2dc->dev, "Failed to find pad for %s\n",
+ subdev->name);
+ return pad;
+ }
+
+ csi2dc->remote_pad = pad;
+
+ ret = media_create_pad_link(&csi2dc->input_sd->entity,
+ csi2dc->remote_pad,
+ &csi2dc->csi2dc_sd.entity, 0,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(csi2dc->dev,
+ "Failed to create pad link: %s to %s\n",
+ csi2dc->input_sd->entity.name,
+ csi2dc->csi2dc_sd.entity.name);
+ return ret;
+ }
+
+ dev_dbg(csi2dc->dev, "link with %s pad: %d\n",
+ csi2dc->input_sd->name, csi2dc->remote_pad);
+
+ return ret;
+}
+
+static const struct v4l2_async_notifier_operations csi2dc_async_ops = {
+ .bound = csi2dc_async_bound,
+};
+
+static int csi2dc_prepare_notifier(struct csi2dc_device *csi2dc,
+ struct fwnode_handle *input_fwnode)
+{
+ struct v4l2_async_subdev *asd;
+ int ret = 0;
+
+ v4l2_async_nf_init(&csi2dc->notifier);
+
+ asd = v4l2_async_nf_add_fwnode_remote(&csi2dc->notifier,
+ input_fwnode,
+ struct v4l2_async_subdev);
+
+ fwnode_handle_put(input_fwnode);
+
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ dev_err(csi2dc->dev,
+ "failed to add async notifier for node %pOF: %d\n",
+ to_of_node(input_fwnode), ret);
+ v4l2_async_nf_cleanup(&csi2dc->notifier);
+ return ret;
+ }
+
+ csi2dc->notifier.ops = &csi2dc_async_ops;
+
+ ret = v4l2_async_subdev_nf_register(&csi2dc->csi2dc_sd,
+ &csi2dc->notifier);
+ if (ret) {
+ dev_err(csi2dc->dev, "fail to register async notifier: %d\n",
+ ret);
+ v4l2_async_nf_cleanup(&csi2dc->notifier);
+ }
+
+ return ret;
+}
+
+static int csi2dc_of_parse(struct csi2dc_device *csi2dc,
+ struct device_node *of_node)
+{
+ struct fwnode_handle *input_fwnode, *output_fwnode;
+ struct v4l2_fwnode_endpoint input_endpoint = { 0 },
+ output_endpoint = { 0 };
+ int ret;
+
+ input_fwnode = fwnode_graph_get_next_endpoint(of_fwnode_handle(of_node),
+ NULL);
+ if (!input_fwnode) {
+ dev_err(csi2dc->dev,
+ "missing port node at %pOF, input node is mandatory.\n",
+ of_node);
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(input_fwnode, &input_endpoint);
+ if (ret) {
+ dev_err(csi2dc->dev, "endpoint not defined at %pOF\n", of_node);
+ goto csi2dc_of_parse_err;
+ }
+
+ if (input_endpoint.bus_type == V4L2_MBUS_PARALLEL ||
+ input_endpoint.bus_type == V4L2_MBUS_BT656) {
+ csi2dc->parallel_mode = true;
+ dev_dbg(csi2dc->dev,
+ "subdevice connected on parallel interface\n");
+ }
+
+ if (input_endpoint.bus_type == V4L2_MBUS_CSI2_DPHY) {
+ csi2dc->clk_gated = input_endpoint.bus.mipi_csi2.flags &
+ V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
+ dev_dbg(csi2dc->dev,
+ "subdevice connected on serial interface\n");
+ dev_dbg(csi2dc->dev, "DT: %s clock\n",
+ csi2dc->clk_gated ? "gated" : "free running");
+ }
+
+ output_fwnode = fwnode_graph_get_next_endpoint
+ (of_fwnode_handle(of_node), input_fwnode);
+
+ if (output_fwnode)
+ ret = v4l2_fwnode_endpoint_parse(output_fwnode,
+ &output_endpoint);
+
+ fwnode_handle_put(output_fwnode);
+
+ if (!output_fwnode || ret) {
+ dev_info(csi2dc->dev,
+ "missing output node at %pOF, data pipe available only.\n",
+ of_node);
+ } else {
+ if (output_endpoint.bus_type != V4L2_MBUS_PARALLEL &&
+ output_endpoint.bus_type != V4L2_MBUS_BT656) {
+ dev_err(csi2dc->dev,
+ "output port must be parallel/bt656.\n");
+ ret = -EINVAL;
+ goto csi2dc_of_parse_err;
+ }
+
+ csi2dc->video_pipe = true;
+
+ dev_dbg(csi2dc->dev,
+ "block %pOF [%d.%d]->[%d.%d] video pipeline\n",
+ of_node, input_endpoint.base.port,
+ input_endpoint.base.id, output_endpoint.base.port,
+ output_endpoint.base.id);
+ }
+
+ /* prepare async notifier for subdevice completion */
+ return csi2dc_prepare_notifier(csi2dc, input_fwnode);
+
+csi2dc_of_parse_err:
+ fwnode_handle_put(input_fwnode);
+ return ret;
+}
+
+static void csi2dc_default_format(struct csi2dc_device *csi2dc)
+{
+ csi2dc->cur_fmt = &csi2dc_formats[0];
+
+ csi2dc->format.height = 480;
+ csi2dc->format.width = 640;
+ csi2dc->format.code = csi2dc_formats[0].mbus_code;
+ csi2dc->format.colorspace = V4L2_COLORSPACE_SRGB;
+ csi2dc->format.field = V4L2_FIELD_NONE;
+ csi2dc->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ csi2dc->format.quantization = V4L2_QUANTIZATION_DEFAULT;
+ csi2dc->format.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int csi2dc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct csi2dc_device *csi2dc;
+ int ret = 0;
+ u32 ver;
+
+ csi2dc = devm_kzalloc(dev, sizeof(*csi2dc), GFP_KERNEL);
+ if (!csi2dc)
+ return -ENOMEM;
+
+ csi2dc->dev = dev;
+
+ csi2dc->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(csi2dc->base)) {
+ dev_err(dev, "base address not set\n");
+ return PTR_ERR(csi2dc->base);
+ }
+
+ csi2dc->pclk = devm_clk_get(dev, "pclk");
+ if (IS_ERR(csi2dc->pclk)) {
+ ret = PTR_ERR(csi2dc->pclk);
+ dev_err(dev, "failed to get pclk: %d\n", ret);
+ return ret;
+ }
+
+ csi2dc->scck = devm_clk_get(dev, "scck");
+ if (IS_ERR(csi2dc->scck)) {
+ ret = PTR_ERR(csi2dc->scck);
+ dev_err(dev, "failed to get scck: %d\n", ret);
+ return ret;
+ }
+
+ v4l2_subdev_init(&csi2dc->csi2dc_sd, &csi2dc_subdev_ops);
+
+ csi2dc->csi2dc_sd.owner = THIS_MODULE;
+ csi2dc->csi2dc_sd.dev = dev;
+ snprintf(csi2dc->csi2dc_sd.name, sizeof(csi2dc->csi2dc_sd.name),
+ "csi2dc");
+
+ csi2dc->csi2dc_sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ csi2dc->csi2dc_sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+
+ platform_set_drvdata(pdev, csi2dc);
+
+ ret = csi2dc_of_parse(csi2dc, dev->of_node);
+ if (ret)
+ goto csi2dc_probe_cleanup_entity;
+
+ csi2dc->pads[CSI2DC_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ if (csi2dc->video_pipe)
+ csi2dc->pads[CSI2DC_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&csi2dc->csi2dc_sd.entity,
+ csi2dc->video_pipe ? CSI2DC_PADS_NUM : 1,
+ csi2dc->pads);
+ if (ret < 0) {
+ dev_err(dev, "media entity init failed\n");
+ goto csi2dc_probe_cleanup_notifier;
+ }
+
+ csi2dc_default_format(csi2dc);
+
+ /* turn power on to validate capabilities */
+ ret = csi2dc_power(csi2dc, true);
+ if (ret < 0)
+ goto csi2dc_probe_cleanup_notifier;
+
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ ver = csi2dc_readl(csi2dc, CSI2DC_VERSION);
+
+ /*
+ * we must register the subdev after PM runtime has been requested,
+ * otherwise we might bound immediately and request pm_runtime_resume
+ * before runtime_enable.
+ */
+ ret = v4l2_async_register_subdev(&csi2dc->csi2dc_sd);
+ if (ret) {
+ dev_err(csi2dc->dev, "failed to register the subdevice\n");
+ goto csi2dc_probe_cleanup_notifier;
+ }
+
+ dev_info(dev, "Microchip CSI2DC version %x\n", ver);
+
+ return 0;
+
+csi2dc_probe_cleanup_notifier:
+ v4l2_async_nf_cleanup(&csi2dc->notifier);
+csi2dc_probe_cleanup_entity:
+ media_entity_cleanup(&csi2dc->csi2dc_sd.entity);
+
+ return ret;
+}
+
+static int csi2dc_remove(struct platform_device *pdev)
+{
+ struct csi2dc_device *csi2dc = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(&pdev->dev);
+
+ v4l2_async_unregister_subdev(&csi2dc->csi2dc_sd);
+ v4l2_async_nf_unregister(&csi2dc->notifier);
+ v4l2_async_nf_cleanup(&csi2dc->notifier);
+ media_entity_cleanup(&csi2dc->csi2dc_sd.entity);
+
+ return 0;
+}
+
+static int __maybe_unused csi2dc_runtime_suspend(struct device *dev)
+{
+ struct csi2dc_device *csi2dc = dev_get_drvdata(dev);
+
+ return csi2dc_power(csi2dc, false);
+}
+
+static int __maybe_unused csi2dc_runtime_resume(struct device *dev)
+{
+ struct csi2dc_device *csi2dc = dev_get_drvdata(dev);
+
+ return csi2dc_power(csi2dc, true);
+}
+
+static const struct dev_pm_ops csi2dc_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(csi2dc_runtime_suspend, csi2dc_runtime_resume, NULL)
+};
+
+static const struct of_device_id csi2dc_of_match[] = {
+ { .compatible = "microchip,sama7g5-csi2dc" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, csi2dc_of_match);
+
+static struct platform_driver csi2dc_driver = {
+ .probe = csi2dc_probe,
+ .remove = csi2dc_remove,
+ .driver = {
+ .name = "microchip-csi2dc",
+ .pm = &csi2dc_dev_pm_ops,
+ .of_match_table = of_match_ptr(csi2dc_of_match),
+ },
+};
+
+module_platform_driver(csi2dc_driver);
+
+MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
+MODULE_DESCRIPTION("Microchip CSI2 Demux Controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig
index 80cf601323ce..480325d053de 100644
--- a/drivers/media/platform/cadence/Kconfig
+++ b/drivers/media/platform/cadence/Kconfig
@@ -1,18 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
-config VIDEO_CADENCE
- bool "Cadence Video Devices"
- help
- If you have a media device designed by Cadence, say Y.
-
- Note that this option doesn't include new drivers in the kernel:
- saying N will just cause Kconfig to skip all the questions about
- Cadence media devices.
-if VIDEO_CADENCE
+comment "Cadence media platform drivers"
config VIDEO_CADENCE_CSI2RX
tristate "Cadence MIPI-CSI2 RX Controller"
- depends on VIDEO_V4L2
+ depends on VIDEO_DEV
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
@@ -24,7 +16,7 @@ config VIDEO_CADENCE_CSI2RX
config VIDEO_CADENCE_CSI2TX
tristate "Cadence MIPI-CSI2 TX Controller"
- depends on VIDEO_V4L2
+ depends on VIDEO_DEV
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
@@ -33,5 +25,3 @@ config VIDEO_CADENCE_CSI2TX
To compile this driver as a module, choose M here: the module will be
called cdns-csi2tx.
-
-endif
diff --git a/drivers/media/platform/chips-media/Kconfig b/drivers/media/platform/chips-media/Kconfig
new file mode 100644
index 000000000000..57f8f8a22df8
--- /dev/null
+++ b/drivers/media/platform/chips-media/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Chips&Media media platform drivers"
+
+config VIDEO_CODA
+ tristate "Chips&Media Coda multi-standard codec IP"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV && OF && (ARCH_MXC || COMPILE_TEST)
+ select SRAM
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_JPEG_HELPER
+ select V4L2_MEM2MEM_DEV
+ select GENERIC_ALLOCATOR
+ help
+ Coda is a range of video codec IPs that supports
+ H.264, MPEG-4, and other video formats.
+
+config VIDEO_IMX_VDOA
+ def_tristate VIDEO_CODA if SOC_IMX6Q || COMPILE_TEST
diff --git a/drivers/media/platform/coda/Makefile b/drivers/media/platform/chips-media/Makefile
index bbb16425a875..bbb16425a875 100644
--- a/drivers/media/platform/coda/Makefile
+++ b/drivers/media/platform/chips-media/Makefile
diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/chips-media/coda-bit.c
index c484c008ab02..c484c008ab02 100644
--- a/drivers/media/platform/coda/coda-bit.c
+++ b/drivers/media/platform/chips-media/coda-bit.c
diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/chips-media/coda-common.c
index 3cd47ba26357..a57822b05070 100644
--- a/drivers/media/platform/coda/coda-common.c
+++ b/drivers/media/platform/chips-media/coda-common.c
@@ -409,6 +409,7 @@ static struct vdoa_data *coda_get_vdoa_data(void)
if (!vdoa_data)
vdoa_data = ERR_PTR(-EPROBE_DEFER);
+ put_device(&vdoa_pdev->dev);
out:
of_node_put(vdoa_node);
diff --git a/drivers/media/platform/coda/coda-gdi.c b/drivers/media/platform/chips-media/coda-gdi.c
index 59d65daca153..59d65daca153 100644
--- a/drivers/media/platform/coda/coda-gdi.c
+++ b/drivers/media/platform/chips-media/coda-gdi.c
diff --git a/drivers/media/platform/coda/coda-h264.c b/drivers/media/platform/chips-media/coda-h264.c
index 8bd0aa8af114..8bd0aa8af114 100644
--- a/drivers/media/platform/coda/coda-h264.c
+++ b/drivers/media/platform/chips-media/coda-h264.c
diff --git a/drivers/media/platform/coda/coda-jpeg.c b/drivers/media/platform/chips-media/coda-jpeg.c
index a72f4655e5ad..a72f4655e5ad 100644
--- a/drivers/media/platform/coda/coda-jpeg.c
+++ b/drivers/media/platform/chips-media/coda-jpeg.c
diff --git a/drivers/media/platform/coda/coda-mpeg2.c b/drivers/media/platform/chips-media/coda-mpeg2.c
index 6f3f6721d286..6f3f6721d286 100644
--- a/drivers/media/platform/coda/coda-mpeg2.c
+++ b/drivers/media/platform/chips-media/coda-mpeg2.c
diff --git a/drivers/media/platform/coda/coda-mpeg4.c b/drivers/media/platform/chips-media/coda-mpeg4.c
index 483a4fba1b4f..483a4fba1b4f 100644
--- a/drivers/media/platform/coda/coda-mpeg4.c
+++ b/drivers/media/platform/chips-media/coda-mpeg4.c
diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/chips-media/coda.h
index dcf35641c603..dcf35641c603 100644
--- a/drivers/media/platform/coda/coda.h
+++ b/drivers/media/platform/chips-media/coda.h
diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/chips-media/coda_regs.h
index da5bb3212528..db81a904cf3f 100644
--- a/drivers/media/platform/coda/coda_regs.h
+++ b/drivers/media/platform/chips-media/coda_regs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/coda/coda_regs.h
+ * linux/drivers/media/platform/chips-media/coda_regs.h
*
* Copyright (C) 2012 Vista Silicon SL
* Javier Martin <javier.martin@vista-silicon.com>
diff --git a/drivers/media/platform/coda/imx-vdoa.c b/drivers/media/platform/chips-media/imx-vdoa.c
index 00643f37b3e6..c70871bae193 100644
--- a/drivers/media/platform/coda/imx-vdoa.c
+++ b/drivers/media/platform/chips-media/imx-vdoa.c
@@ -284,7 +284,6 @@ EXPORT_SYMBOL(vdoa_context_configure);
static int vdoa_probe(struct platform_device *pdev)
{
struct vdoa_data *vdoa;
- struct resource *res;
int ret;
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
@@ -309,10 +308,10 @@ static int vdoa_probe(struct platform_device *pdev)
if (IS_ERR(vdoa->regs))
return PTR_ERR(vdoa->regs);
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res)
- return -EINVAL;
- ret = devm_request_threaded_irq(&pdev->dev, res->start, NULL,
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ ret = devm_request_threaded_irq(&pdev->dev, ret, NULL,
vdoa_irq_handler, IRQF_ONESHOT,
"vdoa", vdoa);
if (ret < 0) {
diff --git a/drivers/media/platform/coda/imx-vdoa.h b/drivers/media/platform/chips-media/imx-vdoa.h
index a62eab476d58..a62eab476d58 100644
--- a/drivers/media/platform/coda/imx-vdoa.h
+++ b/drivers/media/platform/chips-media/imx-vdoa.h
diff --git a/drivers/media/platform/coda/trace.h b/drivers/media/platform/chips-media/trace.h
index c0791c847f7c..19f98e6dafb9 100644
--- a/drivers/media/platform/coda/trace.h
+++ b/drivers/media/platform/chips-media/trace.h
@@ -167,7 +167,7 @@ DEFINE_EVENT(coda_buf_class, coda_jpeg_done,
#endif /* __CODA_TRACE_H__ */
#undef TRACE_INCLUDE_PATH
-#define TRACE_INCLUDE_PATH ../../drivers/media/platform/coda
+#define TRACE_INCLUDE_PATH ../../drivers/media/platform/chips-media
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace
diff --git a/drivers/media/platform/intel/Kconfig b/drivers/media/platform/intel/Kconfig
new file mode 100644
index 000000000000..724e80a9086d
--- /dev/null
+++ b/drivers/media/platform/intel/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Intel media platform drivers"
+
+config VIDEO_PXA27x
+ tristate "PXA27x Quick Capture Interface driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
+ depends on PXA27x || COMPILE_TEST
+ select VIDEOBUF2_DMA_SG
+ select SG_SPLIT
+ select V4L2_FWNODE
+ help
+ This is a v4l2 driver for the PXA27x Quick Capture Interface
diff --git a/drivers/media/platform/intel/Makefile b/drivers/media/platform/intel/Makefile
new file mode 100644
index 000000000000..7e8889cbd2df
--- /dev/null
+++ b/drivers/media/platform/intel/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
diff --git a/drivers/media/platform/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c
index 3ba00b0f9320..35145e3348f0 100644
--- a/drivers/media/platform/pxa_camera.c
+++ b/drivers/media/platform/intel/pxa_camera.c
@@ -1573,39 +1573,40 @@ static int pxa_camera_set_bus_param(struct pxa_camera_dev *pcdev)
mbus_config |= V4L2_MBUS_PCLK_SAMPLE_FALLING;
mbus_config |= V4L2_MBUS_DATA_ACTIVE_HIGH;
- cfg.flags = mbus_config;
- ret = sensor_call(pcdev, pad, set_mbus_config, 0, &cfg);
+ ret = sensor_call(pcdev, pad, get_mbus_config, 0, &cfg);
if (ret < 0 && ret != -ENOIOCTLCMD) {
dev_err(pcdev_to_dev(pcdev),
- "Failed to call set_mbus_config: %d\n", ret);
+ "Failed to call get_mbus_config: %d\n", ret);
return ret;
}
/*
- * If the requested media bus configuration has not been fully applied
- * make sure it is supported by the platform.
+ * If the media bus configuration of the sensor differs, make sure it
+ * is supported by the platform.
*
* PXA does not support V4L2_MBUS_DATA_ACTIVE_LOW and the bus mastering
* roles should match.
*/
- if (cfg.flags != mbus_config) {
+ if (cfg.bus.parallel.flags != mbus_config) {
unsigned int pxa_mbus_role = mbus_config & (V4L2_MBUS_MASTER |
V4L2_MBUS_SLAVE);
- if (pxa_mbus_role != (cfg.flags & (V4L2_MBUS_MASTER |
- V4L2_MBUS_SLAVE))) {
+ unsigned int flags = cfg.bus.parallel.flags;
+
+ if (pxa_mbus_role != (flags & (V4L2_MBUS_MASTER |
+ V4L2_MBUS_SLAVE))) {
dev_err(pcdev_to_dev(pcdev),
"Unsupported mbus configuration: bus mastering\n");
return -EINVAL;
}
- if (cfg.flags & V4L2_MBUS_DATA_ACTIVE_LOW) {
+ if (flags & V4L2_MBUS_DATA_ACTIVE_LOW) {
dev_err(pcdev_to_dev(pcdev),
"Unsupported mbus configuration: DATA_ACTIVE_LOW\n");
return -EINVAL;
}
}
- pxa_camera_setup_cicr(pcdev, cfg.flags, pixfmt);
+ pxa_camera_setup_cicr(pcdev, cfg.bus.parallel.flags, pixfmt);
return 0;
}
diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell/Kconfig
index 3e3f86264762..ec1a16734a28 100644
--- a/drivers/media/platform/marvell-ccic/Kconfig
+++ b/drivers/media/platform/marvell/Kconfig
@@ -1,7 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Marvell media platform drivers"
+
config VIDEO_CAFE_CCIC
tristate "Marvell 88ALP01 (Cafe) CMOS Camera Controller support"
- depends on PCI && I2C && VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on PCI && I2C && VIDEO_DEV
depends on COMMON_CLK
select VIDEO_OV7670
select VIDEOBUF2_VMALLOC
@@ -14,7 +18,8 @@ config VIDEO_CAFE_CCIC
config VIDEO_MMP_CAMERA
tristate "Marvell Armada 610 integrated camera controller support"
- depends on I2C && VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on I2C && VIDEO_DEV
depends on ARCH_MMP || COMPILE_TEST
depends on COMMON_CLK
select VIDEO_OV7670
diff --git a/drivers/media/platform/marvell-ccic/Makefile b/drivers/media/platform/marvell/Makefile
index 90c3c2bc6dde..90c3c2bc6dde 100644
--- a/drivers/media/platform/marvell-ccic/Makefile
+++ b/drivers/media/platform/marvell/Makefile
diff --git a/drivers/media/platform/marvell-ccic/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c
index 03dcf8bf705e..03dcf8bf705e 100644
--- a/drivers/media/platform/marvell-ccic/cafe-driver.c
+++ b/drivers/media/platform/marvell/cafe-driver.c
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c
index ad4a7922d0d7..ad4a7922d0d7 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.c
+++ b/drivers/media/platform/marvell/mcam-core.c
diff --git a/drivers/media/platform/marvell-ccic/mcam-core.h b/drivers/media/platform/marvell/mcam-core.h
index f324d808d737..f324d808d737 100644
--- a/drivers/media/platform/marvell-ccic/mcam-core.h
+++ b/drivers/media/platform/marvell/mcam-core.h
diff --git a/drivers/media/platform/marvell-ccic/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c
index 343ab4f7d807..df16899ab1cb 100644
--- a/drivers/media/platform/marvell-ccic/mmp-driver.c
+++ b/drivers/media/platform/marvell/mmp-driver.c
@@ -270,12 +270,10 @@ static int mmpcam_probe(struct platform_device *pdev)
* Finally, set up our IRQ now that the core is ready to
* deal with it.
*/
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- ret = -ENODEV;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
goto out;
- }
- cam->irq = res->start;
+ cam->irq = ret;
ret = devm_request_irq(&pdev->dev, cam->irq, mmpcam_irq, IRQF_SHARED,
"mmp-camera", mcam);
if (ret)
diff --git a/drivers/media/platform/mediatek/Kconfig b/drivers/media/platform/mediatek/Kconfig
new file mode 100644
index 000000000000..af47d9888552
--- /dev/null
+++ b/drivers/media/platform/mediatek/Kconfig
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Mediatek media platform drivers"
+
+source "drivers/media/platform/mediatek/jpeg/Kconfig"
+source "drivers/media/platform/mediatek/mdp/Kconfig"
+source "drivers/media/platform/mediatek/vcodec/Kconfig"
+source "drivers/media/platform/mediatek/vpu/Kconfig"
diff --git a/drivers/media/platform/mediatek/Makefile b/drivers/media/platform/mediatek/Makefile
new file mode 100644
index 000000000000..d3850a13f128
--- /dev/null
+++ b/drivers/media/platform/mediatek/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += jpeg/
+obj-y += mdp/
+obj-y += vcodec/
+obj-y += vpu/
diff --git a/drivers/media/platform/mediatek/jpeg/Kconfig b/drivers/media/platform/mediatek/jpeg/Kconfig
new file mode 100644
index 000000000000..39c4d1bc66ce
--- /dev/null
+++ b/drivers/media/platform/mediatek/jpeg/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_MEDIATEK_JPEG
+ tristate "Mediatek JPEG Codec driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on MTK_IOMMU_V1 || MTK_IOMMU || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Mediatek jpeg codec driver provides HW capability to decode
+ JPEG format
+
+ To compile this driver as a module, choose M here: the
+ module will be called mtk-jpeg
diff --git a/drivers/media/platform/mtk-jpeg/Makefile b/drivers/media/platform/mediatek/jpeg/Makefile
index 76c33aad0f3f..76c33aad0f3f 100644
--- a/drivers/media/platform/mtk-jpeg/Makefile
+++ b/drivers/media/platform/mediatek/jpeg/Makefile
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index f332beb06d51..ab5485dfc20c 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -22,7 +22,6 @@
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-core.h>
#include <media/videobuf2-dma-contig.h>
-#include <soc/mediatek/smi.h>
#include "mtk_jpeg_enc_hw.h"
#include "mtk_jpeg_dec_hw.h"
@@ -681,7 +680,7 @@ static int mtk_jpeg_buf_prepare(struct vb2_buffer *vb)
{
struct mtk_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct mtk_jpeg_q_data *q_data = NULL;
- struct v4l2_plane_pix_format plane_fmt = {};
+ struct v4l2_plane_pix_format plane_fmt;
int i;
q_data = mtk_jpeg_get_q_data(ctx, vb->vb2_queue->type);
@@ -1055,10 +1054,6 @@ static void mtk_jpeg_clk_on(struct mtk_jpeg_dev *jpeg)
{
int ret;
- ret = mtk_smi_larb_get(jpeg->larb);
- if (ret)
- dev_err(jpeg->dev, "mtk_smi_larb_get larbvdec fail %d\n", ret);
-
ret = clk_bulk_prepare_enable(jpeg->variant->num_clks,
jpeg->variant->clks);
if (ret)
@@ -1069,7 +1064,6 @@ static void mtk_jpeg_clk_off(struct mtk_jpeg_dev *jpeg)
{
clk_bulk_disable_unprepare(jpeg->variant->num_clks,
jpeg->variant->clks);
- mtk_smi_larb_put(jpeg->larb);
}
static irqreturn_t mtk_jpeg_enc_done(struct mtk_jpeg_dev *jpeg)
@@ -1284,35 +1278,6 @@ static struct clk_bulk_data mtk_jpeg_clocks[] = {
{ .id = "jpgenc" },
};
-static int mtk_jpeg_clk_init(struct mtk_jpeg_dev *jpeg)
-{
- struct device_node *node;
- struct platform_device *pdev;
- int ret;
-
- node = of_parse_phandle(jpeg->dev->of_node, "mediatek,larb", 0);
- if (!node)
- return -EINVAL;
- pdev = of_find_device_by_node(node);
- if (WARN_ON(!pdev)) {
- of_node_put(node);
- return -EINVAL;
- }
- of_node_put(node);
-
- jpeg->larb = &pdev->dev;
-
- ret = devm_clk_bulk_get(jpeg->dev, jpeg->variant->num_clks,
- jpeg->variant->clks);
- if (ret) {
- dev_err(&pdev->dev, "failed to get jpeg clock:%d\n", ret);
- put_device(&pdev->dev);
- return ret;
- }
-
- return 0;
-}
-
static void mtk_jpeg_job_timeout_work(struct work_struct *work)
{
struct mtk_jpeg_dev *jpeg = container_of(work, struct mtk_jpeg_dev,
@@ -1333,11 +1298,6 @@ static void mtk_jpeg_job_timeout_work(struct work_struct *work)
v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
}
-static inline void mtk_jpeg_clk_release(struct mtk_jpeg_dev *jpeg)
-{
- put_device(jpeg->larb);
-}
-
static int mtk_jpeg_probe(struct platform_device *pdev)
{
struct mtk_jpeg_dev *jpeg;
@@ -1372,7 +1332,8 @@ static int mtk_jpeg_probe(struct platform_device *pdev)
goto err_req_irq;
}
- ret = mtk_jpeg_clk_init(jpeg);
+ ret = devm_clk_bulk_get(jpeg->dev, jpeg->variant->num_clks,
+ jpeg->variant->clks);
if (ret) {
dev_err(&pdev->dev, "Failed to init clk, err %d\n", ret);
goto err_clk_init;
@@ -1438,7 +1399,6 @@ err_m2m_init:
v4l2_device_unregister(&jpeg->v4l2_dev);
err_dev_register:
- mtk_jpeg_clk_release(jpeg);
err_clk_init:
@@ -1456,7 +1416,6 @@ static int mtk_jpeg_remove(struct platform_device *pdev)
video_device_release(jpeg->vdev);
v4l2_m2m_release(jpeg->m2m_dev);
v4l2_device_unregister(&jpeg->v4l2_dev);
- mtk_jpeg_clk_release(jpeg);
return 0;
}
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
index 595f7f10c9fd..3e4811a41ba2 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
@@ -85,7 +85,6 @@ struct mtk_jpeg_variant {
* @alloc_ctx: videobuf2 memory allocator's context
* @vdev: video device node for jpeg mem2mem mode
* @reg_base: JPEG registers mapping
- * @larb: SMI device
* @job_timeout_work: IRQ timeout structure
* @variant: driver variant to be used
*/
@@ -99,7 +98,6 @@ struct mtk_jpeg_dev {
void *alloc_ctx;
struct video_device *vdev;
void __iomem *reg_base;
- struct device *larb;
struct delayed_work job_timeout_work;
const struct mtk_jpeg_variant *variant;
};
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
index afbbfd5d02bc..afbbfd5d02bc 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
index fa0d45fd7c34..fa0d45fd7c34 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_hw.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.c
index b95c45791c29..b95c45791c29 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.c
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.h
index 2918f15811f8..2918f15811f8 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_parse.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_parse.h
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
index 21ec8f96797f..21ec8f96797f 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_dec_reg.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
index 1cf037bf72dd..1cf037bf72dd 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
index 61c60e4e58ea..61c60e4e58ea 100644
--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_enc_hw.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
diff --git a/drivers/media/platform/mediatek/mdp/Kconfig b/drivers/media/platform/mediatek/mdp/Kconfig
new file mode 100644
index 000000000000..9f13a42899bd
--- /dev/null
+++ b/drivers/media/platform/mediatek/mdp/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_MEDIATEK_MDP
+ tristate "Mediatek MDP driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on MTK_IOMMU || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select VIDEO_MEDIATEK_VPU
+ help
+ It is a v4l2 driver and present in Mediatek MT8173 SoCs.
+ The driver supports for scaling and color space conversion.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mtk-mdp.
diff --git a/drivers/media/platform/mtk-mdp/Makefile b/drivers/media/platform/mediatek/mdp/Makefile
index 5982d65c9971..b7c16ebecc80 100644
--- a/drivers/media/platform/mtk-mdp/Makefile
+++ b/drivers/media/platform/mediatek/mdp/Makefile
@@ -7,4 +7,4 @@ mtk-mdp-y += mtk_mdp_vpu.o
obj-$(CONFIG_VIDEO_MEDIATEK_MDP) += mtk-mdp.o
-ccflags-y += -I$(srctree)/drivers/media/platform/mtk-vpu
+ccflags-y += -I$(srctree)/drivers/media/platform/mediatek/vpu
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c
index b3426a551bea..1e3833f1c9ae 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.c
@@ -9,7 +9,6 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
-#include <soc/mediatek/smi.h>
#include "mtk_mdp_comp.h"
@@ -18,14 +17,6 @@ void mtk_mdp_comp_clock_on(struct device *dev, struct mtk_mdp_comp *comp)
{
int i, err;
- if (comp->larb_dev) {
- err = mtk_smi_larb_get(comp->larb_dev);
- if (err)
- dev_err(dev,
- "failed to get larb, err %d. type:%d\n",
- err, comp->type);
- }
-
for (i = 0; i < ARRAY_SIZE(comp->clk); i++) {
if (IS_ERR(comp->clk[i]))
continue;
@@ -46,17 +37,12 @@ void mtk_mdp_comp_clock_off(struct device *dev, struct mtk_mdp_comp *comp)
continue;
clk_disable_unprepare(comp->clk[i]);
}
-
- if (comp->larb_dev)
- mtk_smi_larb_put(comp->larb_dev);
}
int mtk_mdp_comp_init(struct device *dev, struct device_node *node,
struct mtk_mdp_comp *comp,
enum mtk_mdp_comp_type comp_type)
{
- struct device_node *larb_node;
- struct platform_device *larb_pdev;
int ret;
int i;
@@ -77,32 +63,6 @@ int mtk_mdp_comp_init(struct device *dev, struct device_node *node,
break;
}
- /* Only DMA capable components need the LARB property */
- comp->larb_dev = NULL;
- if (comp->type != MTK_MDP_RDMA &&
- comp->type != MTK_MDP_WDMA &&
- comp->type != MTK_MDP_WROT)
- return 0;
-
- larb_node = of_parse_phandle(node, "mediatek,larb", 0);
- if (!larb_node) {
- dev_err(dev,
- "Missing mediadek,larb phandle in %pOF node\n", node);
- ret = -EINVAL;
- goto put_dev;
- }
-
- larb_pdev = of_find_device_by_node(larb_node);
- if (!larb_pdev) {
- dev_warn(dev, "Waiting for larb device %pOF\n", larb_node);
- of_node_put(larb_node);
- ret = -EPROBE_DEFER;
- goto put_dev;
- }
- of_node_put(larb_node);
-
- comp->larb_dev = &larb_pdev->dev;
-
return 0;
put_dev:
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.h
index 7897766c96bb..ae41dd3cd72a 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_comp.h
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_comp.h
@@ -26,14 +26,12 @@ enum mtk_mdp_comp_type {
* @node: list node to track sibing MDP components
* @dev_node: component device node
* @clk: clocks required for component
- * @larb_dev: SMI device required for component
* @type: component type
*/
struct mtk_mdp_comp {
struct list_head node;
struct device_node *dev_node;
struct clk *clk[2];
- struct device *larb_dev;
enum mtk_mdp_comp_type type;
};
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
index 3d38793aaa25..d83c4964eaf9 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
@@ -17,7 +17,6 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/workqueue.h>
-#include <soc/mediatek/smi.h>
#include "mtk_mdp_core.h"
#include "mtk_mdp_m2m.h"
@@ -246,10 +245,8 @@ static int mtk_mdp_remove(struct platform_device *pdev)
mtk_mdp_unregister_m2m_device(mdp);
v4l2_device_unregister(&mdp->v4l2_dev);
- flush_workqueue(mdp->wdt_wq);
destroy_workqueue(mdp->wdt_wq);
- flush_workqueue(mdp->job_wq);
destroy_workqueue(mdp->job_wq);
list_for_each_entry_safe(comp, comp_temp, &mdp->comp_list, node) {
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.h
index a6e6dc36307b..a6e6dc36307b 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_core.h
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.h
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_ipi.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h
index 2cb8cecb3077..2cb8cecb3077 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_ipi.h
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
index f14779e7596e..f14779e7596e 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.h
index 485dbdbbf51d..485dbdbbf51d 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_m2m.h
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.h
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.c
index ba476d50ae43..ba476d50ae43 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.c
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.h
index 32cf202f2399..32cf202f2399 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_regs.h
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_regs.h
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.c
index b065ccd06914..b065ccd06914 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.c
diff --git a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.h
index 5a1020508446..5a1020508446 100644
--- a/drivers/media/platform/mtk-mdp/mtk_mdp_vpu.h
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_vpu.h
diff --git a/drivers/media/platform/mediatek/vcodec/Kconfig b/drivers/media/platform/mediatek/vcodec/Kconfig
new file mode 100644
index 000000000000..c5c76753c626
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_MEDIATEK_VCODEC_SCP
+ bool
+
+config VIDEO_MEDIATEK_VCODEC_VPU
+ bool
+
+config VIDEO_MEDIATEK_VCODEC
+ tristate "Mediatek Video Codec driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on MTK_IOMMU || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on VIDEO_MEDIATEK_VPU || MTK_SCP
+ # The two following lines ensure we have the same state ("m" or "y") as
+ # our dependencies, to avoid missing symbols during link.
+ depends on VIDEO_MEDIATEK_VPU || !VIDEO_MEDIATEK_VPU
+ depends on MTK_SCP || !MTK_SCP
+ depends on MTK_SMI || (COMPILE_TEST && MTK_SMI=n)
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select VIDEO_MEDIATEK_VCODEC_VPU if VIDEO_MEDIATEK_VPU
+ select VIDEO_MEDIATEK_VCODEC_SCP if MTK_SCP
+ select V4L2_H264
+ select MEDIA_CONTROLLER
+ select MEDIA_CONTROLLER_REQUEST_API
+ help
+ Mediatek video codec driver provides HW capability to
+ encode and decode in a range of video formats on MT8173
+ and MT8183.
+
+ Note that support for MT8173 requires VIDEO_MEDIATEK_VPU to
+ also be selected. Support for MT8183 depends on MTK_SCP.
+
+ To compile this driver as modules, choose M here: the
+ modules will be called mtk-vcodec-dec and mtk-vcodec-enc.
diff --git a/drivers/media/platform/mtk-vcodec/Makefile b/drivers/media/platform/mediatek/vcodec/Makefile
index ca8e9e7a9c4e..359619653a0e 100644
--- a/drivers/media/platform/mtk-vcodec/Makefile
+++ b/drivers/media/platform/mediatek/vcodec/Makefile
@@ -2,7 +2,8 @@
obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC) += mtk-vcodec-dec.o \
mtk-vcodec-enc.o \
- mtk-vcodec-common.o
+ mtk-vcodec-common.o \
+ mtk-vcodec-dec-hw.o
mtk-vcodec-dec-y := vdec/vdec_h264_if.o \
vdec/vdec_vp8_if.o \
@@ -11,11 +12,14 @@ mtk-vcodec-dec-y := vdec/vdec_h264_if.o \
mtk_vcodec_dec_drv.o \
vdec_drv_if.o \
vdec_vpu_if.o \
+ vdec_msg_queue.o \
mtk_vcodec_dec.o \
mtk_vcodec_dec_stateful.o \
mtk_vcodec_dec_stateless.o \
mtk_vcodec_dec_pm.o \
+mtk-vcodec-dec-hw-y := mtk_vcodec_dec_hw.o
+
mtk-vcodec-enc-y := venc/venc_vp8_if.o \
venc/venc_h264_if.o \
mtk_vcodec_enc.o \
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c
index 2b334a8a81c6..130ecef2e766 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.c
@@ -105,12 +105,12 @@ static int vidioc_decoder_cmd(struct file *file, void *priv,
void mtk_vdec_unlock(struct mtk_vcodec_ctx *ctx)
{
- mutex_unlock(&ctx->dev->dec_mutex);
+ mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]);
}
void mtk_vdec_lock(struct mtk_vcodec_ctx *ctx)
{
- mutex_lock(&ctx->dev->dec_mutex);
+ mutex_lock(&ctx->dev->dec_mutex[ctx->hw_id]);
}
void mtk_vcodec_dec_release(struct mtk_vcodec_ctx *ctx)
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h
index e08886a600a3..66cd6d2242c3 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec.h
@@ -68,6 +68,7 @@ extern const struct v4l2_m2m_ops mtk_vdec_m2m_ops;
extern const struct media_device_ops mtk_vcodec_media_ops;
extern const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata;
extern const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata;
+extern const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata;
/*
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c
index 40c39e1e596b..df7b25e9cbc8 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_drv.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
@@ -18,21 +19,23 @@
#include "mtk_vcodec_drv.h"
#include "mtk_vcodec_dec.h"
+#include "mtk_vcodec_dec_hw.h"
#include "mtk_vcodec_dec_pm.h"
#include "mtk_vcodec_intr.h"
#include "mtk_vcodec_util.h"
#include "mtk_vcodec_fw.h"
-#define VDEC_HW_ACTIVE 0x10
-#define VDEC_IRQ_CFG 0x11
-#define VDEC_IRQ_CLR 0x10
-#define VDEC_IRQ_CFG_REG 0xa4
-
-/* Wake up context wait_queue */
-static void wake_up_ctx(struct mtk_vcodec_ctx *ctx)
+static int mtk_vcodec_get_hw_count(struct mtk_vcodec_dev *dev)
{
- ctx->int_cond = 1;
- wake_up_interruptible(&ctx->queue);
+ switch (dev->vdec_pdata->hw_arch) {
+ case MTK_VDEC_PURE_SINGLE_CORE:
+ return MTK_VDEC_ONE_CORE;
+ case MTK_VDEC_LAT_SINGLE_CORE:
+ return MTK_VDEC_ONE_LAT_ONE_CORE;
+ default:
+ mtk_v4l2_err("hw arch %d not supported", dev->vdec_pdata->hw_arch);
+ return MTK_VDEC_NO_HW;
+ }
}
static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv)
@@ -44,7 +47,7 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv)
void __iomem *vdec_misc_addr = dev->reg_base[VDEC_MISC] +
VDEC_IRQ_CFG_REG;
- ctx = mtk_vcodec_get_curr_ctx(dev);
+ ctx = mtk_vcodec_get_curr_ctx(dev, MTK_VDEC_CORE);
/* check if HW active or not */
cg_status = readl(dev->reg_base[0]);
@@ -66,7 +69,7 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv)
writel((readl(vdec_misc_addr) & ~VDEC_IRQ_CLR),
dev->reg_base[VDEC_MISC] + VDEC_IRQ_CFG_REG);
- wake_up_ctx(ctx);
+ wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0);
mtk_v4l2_debug(3,
"mtk_vcodec_dec_irq_handler :wake up ctx %d, dec_done_status=%x",
@@ -75,11 +78,70 @@ static irqreturn_t mtk_vcodec_dec_irq_handler(int irq, void *priv)
return IRQ_HANDLED;
}
+static int mtk_vcodec_get_reg_bases(struct mtk_vcodec_dev *dev)
+{
+ struct platform_device *pdev = dev->plat_dev;
+ int reg_num, i;
+
+ /* Sizeof(u32) * 4 bytes for each register base. */
+ reg_num = of_property_count_elems_of_size(pdev->dev.of_node, "reg",
+ sizeof(u32) * 4);
+ if (reg_num <= 0 || reg_num > NUM_MAX_VDEC_REG_BASE) {
+ dev_err(&pdev->dev, "Invalid register property size: %d\n", reg_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < reg_num; i++) {
+ dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i);
+ if (IS_ERR(dev->reg_base[i]))
+ return PTR_ERR(dev->reg_base[i]);
+
+ mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]);
+ }
+
+ return 0;
+}
+
+static int mtk_vcodec_init_dec_resources(struct mtk_vcodec_dev *dev)
+{
+ struct platform_device *pdev = dev->plat_dev;
+ int ret;
+
+ ret = mtk_vcodec_get_reg_bases(dev);
+ if (ret)
+ return ret;
+
+ if (dev->vdec_pdata->is_subdev_supported)
+ return 0;
+
+ dev->dec_irq = platform_get_irq(pdev, 0);
+ if (dev->dec_irq < 0)
+ return dev->dec_irq;
+
+ irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN);
+ ret = devm_request_irq(&pdev->dev, dev->dec_irq,
+ mtk_vcodec_dec_irq_handler, 0, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to install dev->dec_irq %d (%d)",
+ dev->dec_irq, ret);
+ return ret;
+ }
+
+ ret = mtk_vcodec_init_dec_clk(pdev, &dev->pm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get mt vcodec clock source");
+ return ret;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ return 0;
+}
+
static int fops_vcodec_open(struct file *file)
{
struct mtk_vcodec_dev *dev = video_drvdata(file);
struct mtk_vcodec_ctx *ctx = NULL;
- int ret = 0;
+ int ret = 0, i, hw_count;
struct vb2_queue *src_vq;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -93,7 +155,22 @@ static int fops_vcodec_open(struct file *file)
v4l2_fh_add(&ctx->fh);
INIT_LIST_HEAD(&ctx->list);
ctx->dev = dev;
- init_waitqueue_head(&ctx->queue);
+ if (ctx->dev->vdec_pdata->is_subdev_supported) {
+ hw_count = mtk_vcodec_get_hw_count(dev);
+ if (!hw_count || !dev->subdev_prob_done) {
+ ret = -EINVAL;
+ goto err_ctrls_setup;
+ }
+
+ ret = dev->subdev_prob_done(dev);
+ if (ret)
+ goto err_ctrls_setup;
+
+ for (i = 0; i < hw_count; i++)
+ init_waitqueue_head(&ctx->queue[i]);
+ } else {
+ init_waitqueue_head(&ctx->queue[0]);
+ }
mutex_init(&ctx->lock);
ctx->type = MTK_INST_DECODER;
@@ -116,7 +193,7 @@ static int fops_vcodec_open(struct file *file)
mtk_vcodec_dec_set_default_params(ctx);
if (v4l2_fh_is_singular(&ctx->fh)) {
- ret = mtk_vcodec_dec_pw_on(&dev->pm);
+ ret = mtk_vcodec_dec_pw_on(dev, MTK_VDEC_LAT0);
if (ret < 0)
goto err_load_fw;
/*
@@ -176,7 +253,7 @@ static int fops_vcodec_release(struct file *file)
mtk_vcodec_dec_release(ctx);
if (v4l2_fh_is_singular(&ctx->fh))
- mtk_vcodec_dec_pw_off(&dev->pm);
+ mtk_vcodec_dec_pw_off(dev, MTK_VDEC_LAT0);
v4l2_fh_del(&ctx->fh);
v4l2_fh_exit(&ctx->fh);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
@@ -200,7 +277,6 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
{
struct mtk_vcodec_dev *dev;
struct video_device *vfd_dec;
- struct resource *res;
phandle rproc_phandle;
enum mtk_vcodec_fw_type fw_type;
int i, ret;
@@ -229,40 +305,34 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
if (IS_ERR(dev->fw_handler))
return PTR_ERR(dev->fw_handler);
- ret = mtk_vcodec_init_dec_pm(dev);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed to get mt vcodec clock source");
+ ret = mtk_vcodec_init_dec_resources(dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to init dec resources");
goto err_dec_pm;
}
- for (i = 0; i < NUM_MAX_VDEC_REG_BASE; i++) {
- dev->reg_base[i] = devm_platform_ioremap_resource(pdev, i);
- if (IS_ERR((__force void *)dev->reg_base[i])) {
- ret = PTR_ERR((__force void *)dev->reg_base[i]);
+ if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch)) {
+ vdec_msg_queue_init_ctx(&dev->msg_queue_core_ctx, MTK_VDEC_CORE);
+ dev->core_workqueue =
+ alloc_ordered_workqueue("core-decoder",
+ WQ_MEM_RECLAIM | WQ_FREEZABLE);
+ if (!dev->core_workqueue) {
+ mtk_v4l2_err("Failed to create core workqueue");
+ ret = -EINVAL;
goto err_res;
}
- mtk_v4l2_debug(2, "reg[%d] base=%p", i, dev->reg_base[i]);
- }
-
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get irq resource");
- ret = -ENOENT;
- goto err_res;
}
- dev->dec_irq = platform_get_irq(pdev, 0);
- irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN);
- ret = devm_request_irq(&pdev->dev, dev->dec_irq,
- mtk_vcodec_dec_irq_handler, 0, pdev->name, dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)",
- dev->dec_irq,
- ret);
- goto err_res;
+ if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL)) {
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
+ if (ret) {
+ mtk_v4l2_err("Failed to set mask");
+ goto err_core_workq;
+ }
}
- mutex_init(&dev->dec_mutex);
+ for (i = 0; i < MTK_VDEC_HW_MAX; i++)
+ mutex_init(&dev->dec_mutex[i]);
mutex_init(&dev->dev_mutex);
spin_lock_init(&dev->irqlock);
@@ -272,7 +342,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) {
mtk_v4l2_err("v4l2_device_register err=%d", ret);
- goto err_res;
+ goto err_core_workq;
}
init_waitqueue_head(&dev->queue);
@@ -302,7 +372,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
if (IS_ERR((__force void *)dev->m2m_dev_dec)) {
mtk_v4l2_err("Failed to init mem2mem dec device");
ret = PTR_ERR((__force void *)dev->m2m_dev_dec);
- goto err_dec_mem_init;
+ goto err_dec_alloc;
}
dev->decode_workqueue =
@@ -314,6 +384,21 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
goto err_event_workq;
}
+ if (dev->vdec_pdata->is_subdev_supported) {
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL,
+ &pdev->dev);
+ if (ret) {
+ mtk_v4l2_err("Main device of_platform_populate failed.");
+ goto err_reg_cont;
+ }
+ }
+
+ ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ mtk_v4l2_err("Failed to register video device");
+ goto err_reg_cont;
+ }
+
if (dev->vdec_pdata->uses_stateless_api) {
dev->mdev_dec.dev = &pdev->dev;
strscpy(dev->mdev_dec.model, MTK_VCODEC_DEC_NAME,
@@ -327,7 +412,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
MEDIA_ENT_F_PROC_VIDEO_DECODER);
if (ret) {
mtk_v4l2_err("Failed to register media controller");
- goto err_reg_cont;
+ goto err_dec_mem_init;
}
ret = media_device_register(&dev->mdev_dec);
@@ -338,34 +423,28 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
mtk_v4l2_debug(0, "media registered as /dev/media%d", vfd_dec->minor);
}
- ret = video_register_device(vfd_dec, VFL_TYPE_VIDEO, 0);
- if (ret) {
- mtk_v4l2_err("Failed to register video device");
- goto err_dec_reg;
- }
mtk_v4l2_debug(0, "decoder registered as /dev/video%d", vfd_dec->minor);
return 0;
-err_dec_reg:
- if (dev->vdec_pdata->uses_stateless_api)
- media_device_unregister(&dev->mdev_dec);
err_media_reg:
- if (dev->vdec_pdata->uses_stateless_api)
- v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec);
+ v4l2_m2m_unregister_media_controller(dev->m2m_dev_dec);
+err_dec_mem_init:
+ video_unregister_device(vfd_dec);
err_reg_cont:
if (dev->vdec_pdata->uses_stateless_api)
media_device_cleanup(&dev->mdev_dec);
destroy_workqueue(dev->decode_workqueue);
err_event_workq:
v4l2_m2m_release(dev->m2m_dev_dec);
-err_dec_mem_init:
- video_unregister_device(vfd_dec);
err_dec_alloc:
v4l2_device_unregister(&dev->v4l2_dev);
+err_core_workq:
+ if (IS_VDEC_LAT_ARCH(dev->vdec_pdata->hw_arch))
+ destroy_workqueue(dev->core_workqueue);
err_res:
- mtk_vcodec_release_dec_pm(dev);
+ pm_runtime_disable(dev->pm.dev);
err_dec_pm:
mtk_vcodec_fw_release(dev->fw_handler);
return ret;
@@ -380,6 +459,10 @@ static const struct of_device_id mtk_vcodec_match[] = {
.compatible = "mediatek,mt8183-vcodec-dec",
.data = &mtk_vdec_8183_pdata,
},
+ {
+ .compatible = "mediatek,mt8192-vcodec-dec",
+ .data = &mtk_lat_sig_core_pdata,
+ },
{},
};
@@ -404,7 +487,7 @@ static int mtk_vcodec_dec_remove(struct platform_device *pdev)
video_unregister_device(dev->vfd_dec);
v4l2_device_unregister(&dev->v4l2_dev);
- mtk_vcodec_release_dec_pm(dev);
+ pm_runtime_disable(dev->pm.dev);
mtk_vcodec_fw_release(dev->fw_handler);
return 0;
}
diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c
new file mode 100644
index 000000000000..14bed2bd4283
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Yunfei Dong <yunfei.dong@mediatek.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_dec.h"
+#include "mtk_vcodec_dec_hw.h"
+#include "mtk_vcodec_dec_pm.h"
+#include "mtk_vcodec_intr.h"
+#include "mtk_vcodec_util.h"
+
+static const struct of_device_id mtk_vdec_hw_match[] = {
+ {
+ .compatible = "mediatek,mtk-vcodec-lat",
+ .data = (void *)MTK_VDEC_LAT0,
+ },
+ {
+ .compatible = "mediatek,mtk-vcodec-core",
+ .data = (void *)MTK_VDEC_CORE,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_vdec_hw_match);
+
+static int mtk_vdec_hw_prob_done(struct mtk_vcodec_dev *vdec_dev)
+{
+ struct platform_device *pdev = vdec_dev->plat_dev;
+ struct device_node *subdev_node;
+ enum mtk_vdec_hw_id hw_idx;
+ const struct of_device_id *of_id;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mtk_vdec_hw_match); i++) {
+ of_id = &mtk_vdec_hw_match[i];
+ subdev_node = of_find_compatible_node(NULL, NULL,
+ of_id->compatible);
+ if (!subdev_node)
+ continue;
+
+ of_node_put(subdev_node);
+
+ hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data;
+ if (!test_bit(hw_idx, vdec_dev->subdev_bitmap)) {
+ dev_err(&pdev->dev, "vdec %d is not ready", hw_idx);
+ return -EAGAIN;
+ }
+ }
+
+ return 0;
+}
+
+static irqreturn_t mtk_vdec_hw_irq_handler(int irq, void *priv)
+{
+ struct mtk_vdec_hw_dev *dev = priv;
+ struct mtk_vcodec_ctx *ctx;
+ u32 cg_status;
+ unsigned int dec_done_status;
+ void __iomem *vdec_misc_addr = dev->reg_base[VDEC_HW_MISC] +
+ VDEC_IRQ_CFG_REG;
+
+ ctx = mtk_vcodec_get_curr_ctx(dev->main_dev, dev->hw_idx);
+
+ /* check if HW active or not */
+ cg_status = readl(dev->reg_base[VDEC_HW_SYS]);
+ if (cg_status & VDEC_HW_ACTIVE) {
+ mtk_v4l2_err("vdec active is not 0x0 (0x%08x)",
+ cg_status);
+ return IRQ_HANDLED;
+ }
+
+ dec_done_status = readl(vdec_misc_addr);
+ if ((dec_done_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS) !=
+ MTK_VDEC_IRQ_STATUS_DEC_SUCCESS)
+ return IRQ_HANDLED;
+
+ /* clear interrupt */
+ writel(dec_done_status | VDEC_IRQ_CFG, vdec_misc_addr);
+ writel(dec_done_status & ~VDEC_IRQ_CLR, vdec_misc_addr);
+
+ wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, dev->hw_idx);
+
+ mtk_v4l2_debug(3, "wake up ctx %d, dec_done_status=%x",
+ ctx->id, dec_done_status);
+
+ return IRQ_HANDLED;
+}
+
+static int mtk_vdec_hw_init_irq(struct mtk_vdec_hw_dev *dev)
+{
+ struct platform_device *pdev = dev->plat_dev;
+ int ret;
+
+ dev->dec_irq = platform_get_irq(pdev, 0);
+ if (dev->dec_irq < 0)
+ return dev->dec_irq;
+
+ irq_set_status_flags(dev->dec_irq, IRQ_NOAUTOEN);
+ ret = devm_request_irq(&pdev->dev, dev->dec_irq,
+ mtk_vdec_hw_irq_handler, 0, pdev->name, dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to install dev->dec_irq %d (%d)",
+ dev->dec_irq, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mtk_vdec_hw_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_vdec_hw_dev *subdev_dev;
+ struct mtk_vcodec_dev *main_dev;
+ const struct of_device_id *of_id;
+ int hw_idx;
+ int ret;
+
+ if (!dev->parent) {
+ dev_err(dev, "no parent for hardware devices.\n");
+ return -ENODEV;
+ }
+
+ main_dev = dev_get_drvdata(dev->parent);
+ if (!main_dev) {
+ dev_err(dev, "failed to get parent driver data");
+ return -EINVAL;
+ }
+
+ subdev_dev = devm_kzalloc(dev, sizeof(*subdev_dev), GFP_KERNEL);
+ if (!subdev_dev)
+ return -ENOMEM;
+
+ subdev_dev->plat_dev = pdev;
+ ret = mtk_vcodec_init_dec_clk(pdev, &subdev_dev->pm);
+ if (ret)
+ return ret;
+ pm_runtime_enable(&pdev->dev);
+
+ of_id = of_match_device(mtk_vdec_hw_match, dev);
+ if (!of_id) {
+ dev_err(dev, "Can't get vdec subdev id.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ hw_idx = (enum mtk_vdec_hw_id)(uintptr_t)of_id->data;
+ if (hw_idx >= MTK_VDEC_HW_MAX) {
+ dev_err(dev, "Hardware index %d not correct.\n", hw_idx);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ main_dev->subdev_dev[hw_idx] = subdev_dev;
+ subdev_dev->hw_idx = hw_idx;
+ subdev_dev->main_dev = main_dev;
+ subdev_dev->reg_base[VDEC_HW_SYS] = main_dev->reg_base[VDEC_HW_SYS];
+ set_bit(subdev_dev->hw_idx, main_dev->subdev_bitmap);
+
+ ret = mtk_vdec_hw_init_irq(subdev_dev);
+ if (ret)
+ goto err;
+
+ subdev_dev->reg_base[VDEC_HW_MISC] =
+ devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC])) {
+ ret = PTR_ERR((__force void *)subdev_dev->reg_base[VDEC_HW_MISC]);
+ goto err;
+ }
+
+ if (!main_dev->subdev_prob_done)
+ main_dev->subdev_prob_done = mtk_vdec_hw_prob_done;
+
+ platform_set_drvdata(pdev, subdev_dev);
+ return 0;
+err:
+ pm_runtime_disable(subdev_dev->pm.dev);
+ return ret;
+}
+
+static struct platform_driver mtk_vdec_driver = {
+ .probe = mtk_vdec_hw_probe,
+ .driver = {
+ .name = "mtk-vdec-comp",
+ .of_match_table = mtk_vdec_hw_match,
+ },
+};
+module_platform_driver(mtk_vdec_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Mediatek video decoder hardware driver");
diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h
new file mode 100644
index 000000000000..a63e4b1b81c3
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_hw.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Yunfei Dong <yunfei.dong@mediatek.com>
+ */
+
+#ifndef _MTK_VCODEC_DEC_HW_H_
+#define _MTK_VCODEC_DEC_HW_H_
+
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#include "mtk_vcodec_drv.h"
+
+#define VDEC_HW_ACTIVE 0x10
+#define VDEC_IRQ_CFG 0x11
+#define VDEC_IRQ_CLR 0x10
+#define VDEC_IRQ_CFG_REG 0xa4
+
+/**
+ * enum mtk_vdec_hw_reg_idx - subdev hardware register base index
+ * @VDEC_HW_SYS : vdec soc register index
+ * @VDEC_HW_MISC: vdec misc register index
+ * @VDEC_HW_MAX : vdec supported max register index
+ */
+enum mtk_vdec_hw_reg_idx {
+ VDEC_HW_SYS,
+ VDEC_HW_MISC,
+ VDEC_HW_MAX
+};
+
+/**
+ * struct mtk_vdec_hw_dev - vdec hardware driver data
+ * @plat_dev: platform device
+ * @main_dev: main device
+ * @reg_base: mapped address of MTK Vcodec registers.
+ *
+ * @curr_ctx: the context that is waiting for codec hardware
+ *
+ * @dec_irq : decoder irq resource
+ * @pm : power management control
+ * @hw_idx : each hardware index
+ */
+struct mtk_vdec_hw_dev {
+ struct platform_device *plat_dev;
+ struct mtk_vcodec_dev *main_dev;
+ void __iomem *reg_base[VDEC_HW_MAX];
+
+ struct mtk_vcodec_ctx *curr_ctx;
+
+ int dec_irq;
+ struct mtk_vcodec_pm pm;
+ int hw_idx;
+};
+
+#endif /* _MTK_VCODEC_DEC_HW_H_ */
diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c
new file mode 100644
index 000000000000..7e0c2644bf7b
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Tiffany Lin <tiffany.lin@mediatek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+
+#include "mtk_vcodec_dec_hw.h"
+#include "mtk_vcodec_dec_pm.h"
+#include "mtk_vcodec_util.h"
+
+int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm)
+{
+ struct mtk_vcodec_clk *dec_clk;
+ struct mtk_vcodec_clk_info *clk_info;
+ int i = 0, ret;
+
+ dec_clk = &pm->vdec_clk;
+ pm->dev = &pdev->dev;
+
+ dec_clk->clk_num =
+ of_property_count_strings(pdev->dev.of_node, "clock-names");
+ if (dec_clk->clk_num > 0) {
+ dec_clk->clk_info = devm_kcalloc(&pdev->dev,
+ dec_clk->clk_num, sizeof(*clk_info),
+ GFP_KERNEL);
+ if (!dec_clk->clk_info)
+ return -ENOMEM;
+ } else {
+ mtk_v4l2_err("Failed to get vdec clock count");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < dec_clk->clk_num; i++) {
+ clk_info = &dec_clk->clk_info[i];
+ ret = of_property_read_string_index(pdev->dev.of_node,
+ "clock-names", i, &clk_info->clk_name);
+ if (ret) {
+ mtk_v4l2_err("Failed to get clock name id = %d", i);
+ return ret;
+ }
+ clk_info->vcodec_clk = devm_clk_get(&pdev->dev,
+ clk_info->clk_name);
+ if (IS_ERR(clk_info->vcodec_clk)) {
+ mtk_v4l2_err("devm_clk_get (%d)%s fail", i,
+ clk_info->clk_name);
+ return PTR_ERR(clk_info->vcodec_clk);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_init_dec_clk);
+
+int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx)
+{
+ struct mtk_vdec_hw_dev *subdev_dev;
+ struct mtk_vcodec_pm *pm;
+ int ret;
+
+ if (vdec_dev->vdec_pdata->is_subdev_supported) {
+ subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx);
+ if (!subdev_dev) {
+ mtk_v4l2_err("Failed to get hw dev\n");
+ return -EINVAL;
+ }
+ pm = &subdev_dev->pm;
+ } else {
+ pm = &vdec_dev->pm;
+ }
+
+ ret = pm_runtime_resume_and_get(pm->dev);
+ if (ret)
+ mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_on);
+
+void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx)
+{
+ struct mtk_vdec_hw_dev *subdev_dev;
+ struct mtk_vcodec_pm *pm;
+ int ret;
+
+ if (vdec_dev->vdec_pdata->is_subdev_supported) {
+ subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx);
+ if (!subdev_dev) {
+ mtk_v4l2_err("Failed to get hw dev\n");
+ return;
+ }
+ pm = &subdev_dev->pm;
+ } else {
+ pm = &vdec_dev->pm;
+ }
+
+ ret = pm_runtime_put_sync(pm->dev);
+ if (ret)
+ mtk_v4l2_err("pm_runtime_put_sync fail %d", ret);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_dec_pw_off);
+
+void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx)
+{
+ struct mtk_vdec_hw_dev *subdev_dev;
+ struct mtk_vcodec_pm *pm;
+ struct mtk_vcodec_clk *dec_clk;
+ int ret, i;
+
+ if (vdec_dev->vdec_pdata->is_subdev_supported) {
+ subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx);
+ if (!subdev_dev) {
+ mtk_v4l2_err("Failed to get hw dev\n");
+ return;
+ }
+ pm = &subdev_dev->pm;
+ enable_irq(subdev_dev->dec_irq);
+ } else {
+ pm = &vdec_dev->pm;
+ enable_irq(vdec_dev->dec_irq);
+ }
+
+ dec_clk = &pm->vdec_clk;
+ for (i = 0; i < dec_clk->clk_num; i++) {
+ ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk);
+ if (ret) {
+ mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i,
+ dec_clk->clk_info[i].clk_name, ret);
+ goto error;
+ }
+ }
+
+ return;
+error:
+ for (i -= 1; i >= 0; i--)
+ clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_on);
+
+void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx)
+{
+ struct mtk_vdec_hw_dev *subdev_dev;
+ struct mtk_vcodec_pm *pm;
+ struct mtk_vcodec_clk *dec_clk;
+ int i;
+
+ if (vdec_dev->vdec_pdata->is_subdev_supported) {
+ subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx);
+ if (!subdev_dev) {
+ mtk_v4l2_err("Failed to get hw dev\n");
+ return;
+ }
+ pm = &subdev_dev->pm;
+ disable_irq(subdev_dev->dec_irq);
+ } else {
+ pm = &vdec_dev->pm;
+ disable_irq(vdec_dev->dec_irq);
+ }
+
+ dec_clk = &pm->vdec_clk;
+ for (i = dec_clk->clk_num - 1; i >= 0; i--)
+ clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk);
+}
+EXPORT_SYMBOL_GPL(mtk_vcodec_dec_clock_off);
diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h
new file mode 100644
index 000000000000..3cc721bbfaf6
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_pm.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Tiffany Lin <tiffany.lin@mediatek.com>
+ */
+
+#ifndef _MTK_VCODEC_DEC_PM_H_
+#define _MTK_VCODEC_DEC_PM_H_
+
+#include "mtk_vcodec_drv.h"
+
+int mtk_vcodec_init_dec_clk(struct platform_device *pdev, struct mtk_vcodec_pm *pm);
+
+int mtk_vcodec_dec_pw_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx);
+void mtk_vcodec_dec_pw_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx);
+void mtk_vcodec_dec_clock_on(struct mtk_vcodec_dev *vdec_dev, int hw_idx);
+void mtk_vcodec_dec_clock_off(struct mtk_vcodec_dev *vdec_dev, int hw_idx);
+
+#endif /* _MTK_VCODEC_DEC_PM_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c
index bef49244e61b..04ca43c77e5f 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateful.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateful.c
@@ -625,4 +625,6 @@ const struct mtk_vcodec_dec_pdata mtk_vdec_8173_pdata = {
.num_framesizes = NUM_SUPPORTED_FRAMESIZE,
.worker = mtk_vdec_worker,
.flush_decoder = mtk_vdec_flush_decoder,
+ .is_subdev_supported = false,
+ .hw_arch = MTK_VDEC_PURE_SINGLE_CORE,
};
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c
index 3d9f47555884..23d997ac114d 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_stateless.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_dec_stateless.c
@@ -356,4 +356,25 @@ const struct mtk_vcodec_dec_pdata mtk_vdec_8183_pdata = {
.uses_stateless_api = true,
.worker = mtk_vdec_worker,
.flush_decoder = mtk_vdec_flush_decoder,
+ .is_subdev_supported = false,
+ .hw_arch = MTK_VDEC_PURE_SINGLE_CORE,
+};
+
+/* This platform data is used for one lat and one core architecture. */
+const struct mtk_vcodec_dec_pdata mtk_lat_sig_core_pdata = {
+ .chip = MTK_MT8192,
+ .init_vdec_params = mtk_init_vdec_params,
+ .ctrls_setup = mtk_vcodec_dec_ctrls_setup,
+ .vdec_vb2_ops = &mtk_vdec_request_vb2_ops,
+ .vdec_formats = mtk_video_formats,
+ .num_formats = NUM_FORMATS,
+ .default_out_fmt = &mtk_video_formats[DEFAULT_OUT_FMT_IDX],
+ .default_cap_fmt = &mtk_video_formats[DEFAULT_CAP_FMT_IDX],
+ .vdec_framesizes = mtk_vdec_framesizes,
+ .num_framesizes = NUM_SUPPORTED_FRAMESIZE,
+ .uses_stateless_api = true,
+ .worker = mtk_vdec_worker,
+ .flush_decoder = mtk_vdec_flush_decoder,
+ .is_subdev_supported = true,
+ .hw_arch = MTK_VDEC_LAT_SINGLE_CORE,
};
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h
index 581522177308..813901c4be5e 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_drv.h
@@ -15,7 +15,9 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-core.h>
+
#include "mtk_vcodec_util.h"
+#include "vdec_msg_queue.h"
#define MTK_VCODEC_DRV_NAME "mtk_vcodec_drv"
#define MTK_VCODEC_DEC_NAME "mtk-vcodec-dec"
@@ -25,6 +27,7 @@
#define MTK_VCODEC_MAX_PLANES 3
#define MTK_V4L2_BENCHMARK 0
#define WAIT_INTR_TIMEOUT_MS 1000
+#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE)
/*
* enum mtk_hw_reg_idx - MTK hw register base index
@@ -94,6 +97,27 @@ enum mtk_fmt_type {
};
/*
+ * enum mtk_vdec_hw_id - Hardware index used to separate
+ * different hardware
+ */
+enum mtk_vdec_hw_id {
+ MTK_VDEC_CORE,
+ MTK_VDEC_LAT0,
+ MTK_VDEC_LAT1,
+ MTK_VDEC_HW_MAX,
+};
+
+/*
+ * enum mtk_vdec_hw_count - Supported hardware count
+ */
+enum mtk_vdec_hw_count {
+ MTK_VDEC_NO_HW = 0,
+ MTK_VDEC_ONE_CORE,
+ MTK_VDEC_ONE_LAT_ONE_CORE,
+ MTK_VDEC_MAX_HW_COUNT,
+};
+
+/*
* struct mtk_video_fmt - Structure used to store information about pixelformats
*/
struct mtk_video_fmt {
@@ -190,12 +214,8 @@ struct mtk_vcodec_clk {
*/
struct mtk_vcodec_pm {
struct mtk_vcodec_clk vdec_clk;
- struct device *larbvdec;
-
struct mtk_vcodec_clk venc_clk;
- struct device *larbvenc;
struct device *dev;
- struct mtk_vcodec_dev *mtkdev;
};
/**
@@ -262,6 +282,9 @@ struct vdec_pic_info {
* @decoded_frame_cnt: number of decoded frames
* @lock: protect variables accessed by V4L2 threads and worker thread such as
* mtk_video_dec_buf.
+ * @hw_id: hardware index used to identify different hardware.
+ *
+ * @msg_queue: msg queue used to store lat buffer information.
*/
struct mtk_vcodec_ctx {
enum mtk_instance_type type;
@@ -283,9 +306,9 @@ struct mtk_vcodec_ctx {
struct vdec_pic_info picinfo;
int dpb_size;
- int int_cond;
- int int_type;
- wait_queue_head_t queue;
+ int int_cond[MTK_VDEC_HW_MAX];
+ int int_type[MTK_VDEC_HW_MAX];
+ wait_queue_head_t queue[MTK_VDEC_HW_MAX];
unsigned int irq_status;
struct v4l2_ctrl_handler ctrl_hdl;
@@ -304,7 +327,9 @@ struct mtk_vcodec_ctx {
int decoded_frame_cnt;
struct mutex lock;
+ int hw_id;
+ struct vdec_msg_queue msg_queue;
};
enum mtk_chip {
@@ -314,6 +339,14 @@ enum mtk_chip {
MTK_MT8195,
};
+/*
+ * enum mtk_vdec_hw_arch - Used to separate different hardware architecture
+ */
+enum mtk_vdec_hw_arch {
+ MTK_VDEC_PURE_SINGLE_CORE,
+ MTK_VDEC_LAT_SINGLE_CORE,
+};
+
/**
* struct mtk_vcodec_dec_pdata - compatible data for each IC
* @init_vdec_params: init vdec params
@@ -332,7 +365,9 @@ enum mtk_chip {
* @num_framesizes: count of video decoder frame sizes
*
* @chip: chip this decoder is compatible with
+ * @hw_arch: hardware arch is used to separate pure_sin_core and lat_sin_core
*
+ * @is_subdev_supported: whether support parent-node architecture(subdev)
* @uses_stateless_api: whether the decoder uses the stateless API with requests
*/
@@ -353,7 +388,9 @@ struct mtk_vcodec_dec_pdata {
const int num_framesizes;
enum mtk_chip chip;
+ enum mtk_vdec_hw_arch hw_arch;
+ bool is_subdev_supported;
bool uses_stateless_api;
};
@@ -424,6 +461,13 @@ struct mtk_vcodec_enc_pdata {
* @pm: power management control
* @dec_capability: used to identify decode capability, ex: 4k
* @enc_capability: used to identify encode capability
+ *
+ * @core_workqueue: queue used for core hardware decode
+ * @msg_queue_core_ctx: msg queue context used for core workqueue
+ *
+ * @subdev_dev: subdev hardware device
+ * @subdev_prob_done: check whether all used hw device is prob done
+ * @subdev_bitmap: used to record hardware is ready or not
*/
struct mtk_vcodec_dev {
struct v4l2_device v4l2_dev;
@@ -455,12 +499,20 @@ struct mtk_vcodec_dev {
int dec_irq;
int enc_irq;
- struct mutex dec_mutex;
+ /* decoder hardware mutex lock */
+ struct mutex dec_mutex[MTK_VDEC_HW_MAX];
struct mutex enc_mutex;
struct mtk_vcodec_pm pm;
unsigned int dec_capability;
unsigned int enc_capability;
+
+ struct workqueue_struct *core_workqueue;
+ struct vdec_msg_queue_ctx msg_queue_core_ctx;
+
+ void *subdev_dev[MTK_VDEC_HW_MAX];
+ int (*subdev_prob_done)(struct mtk_vcodec_dev *vdec_dev);
+ DECLARE_BITMAP(subdev_bitmap, MTK_VDEC_HW_MAX);
};
static inline struct mtk_vcodec_ctx *fh_to_ctx(struct v4l2_fh *fh)
@@ -473,4 +525,13 @@ static inline struct mtk_vcodec_ctx *ctrl_to_ctx(struct v4l2_ctrl *ctrl)
return container_of(ctrl->handler, struct mtk_vcodec_ctx, ctrl_hdl);
}
+/* Wake up context wait_queue */
+static inline void
+wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason, unsigned int hw_id)
+{
+ ctx->int_cond[hw_id] = 1;
+ ctx->int_type[hw_id] = reason;
+ wake_up_interruptible(&ctx->queue[hw_id]);
+}
+
#endif /* _MTK_VCODEC_DRV_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c
index ffb046eec610..c21367038c34 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.c
@@ -8,7 +8,6 @@
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
-#include <soc/mediatek/smi.h>
#include <linux/pm_runtime.h>
#include "mtk_vcodec_drv.h"
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h
index 513ee7993e34..513ee7993e34 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc.h
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c
index aeaecb8d416e..5172cfe0db4a 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_drv.c
@@ -11,10 +11,10 @@
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of.h>
+#include <linux/pm_runtime.h>
#include <media/v4l2-event.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-contig.h>
-#include <linux/pm_runtime.h>
#include "mtk_vcodec_drv.h"
#include "mtk_vcodec_enc.h"
@@ -62,14 +62,6 @@ static const struct mtk_video_fmt mtk_video_formats_capture_vp8[] = {
},
};
-/* Wake up context wait_queue */
-static void wake_up_ctx(struct mtk_vcodec_ctx *ctx, unsigned int reason)
-{
- ctx->int_cond = 1;
- ctx->int_type = reason;
- wake_up_interruptible(&ctx->queue);
-}
-
static void clean_irq_status(unsigned int irq_status, void __iomem *addr)
{
if (irq_status & MTK_VENC_IRQ_STATUS_PAUSE)
@@ -111,7 +103,7 @@ static irqreturn_t mtk_vcodec_enc_irq_handler(int irq, void *priv)
clean_irq_status(ctx->irq_status, addr);
- wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED);
+ wake_up_ctx(ctx, MTK_INST_IRQ_RECEIVED, 0);
return IRQ_HANDLED;
}
@@ -137,7 +129,7 @@ static int fops_vcodec_open(struct file *file)
v4l2_fh_add(&ctx->fh);
INIT_LIST_HEAD(&ctx->list);
ctx->dev = dev;
- init_waitqueue_head(&ctx->queue);
+ init_waitqueue_head(&ctx->queue[0]);
ctx->type = MTK_INST_ENCODER;
ret = mtk_vcodec_enc_ctrls_setup(ctx);
@@ -265,7 +257,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
return PTR_ERR(dev->fw_handler);
dev->venc_pdata = of_device_get_match_data(&pdev->dev);
- ret = mtk_vcodec_init_enc_pm(dev);
+ ret = mtk_vcodec_init_enc_clk(dev);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to get mtk vcodec clock source!");
goto err_enc_pm;
@@ -357,7 +349,7 @@ static int mtk_vcodec_probe(struct platform_device *pdev)
if (of_get_property(pdev->dev.of_node, "dma-ranges", NULL))
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
- ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, 1);
+ ret = video_register_device(vfd_enc, VFL_TYPE_VIDEO, -1);
if (ret) {
mtk_v4l2_err("Failed to register video device");
goto err_enc_reg;
@@ -377,7 +369,7 @@ err_enc_mem_init:
err_enc_alloc:
v4l2_device_unregister(&dev->v4l2_dev);
err_res:
- mtk_vcodec_release_enc_pm(dev);
+ pm_runtime_disable(dev->pm.dev);
err_enc_pm:
mtk_vcodec_fw_release(dev->fw_handler);
return ret;
@@ -466,7 +458,7 @@ static int mtk_vcodec_enc_remove(struct platform_device *pdev)
video_unregister_device(dev->vfd_enc);
v4l2_device_unregister(&dev->v4l2_dev);
- mtk_vcodec_release_enc_pm(dev);
+ pm_runtime_disable(dev->pm.dev);
mtk_vcodec_fw_release(dev->fw_handler);
return 0;
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c
index 1b2e4930ed27..7055954eb2af 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.c
@@ -8,58 +8,35 @@
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
-#include <soc/mediatek/smi.h>
#include "mtk_vcodec_enc_pm.h"
#include "mtk_vcodec_util.h"
-int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
+int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *mtkdev)
{
- struct device_node *node;
struct platform_device *pdev;
struct mtk_vcodec_pm *pm;
struct mtk_vcodec_clk *enc_clk;
struct mtk_vcodec_clk_info *clk_info;
- int ret = 0, i = 0;
- struct device *dev;
+ int ret, i;
pdev = mtkdev->plat_dev;
pm = &mtkdev->pm;
memset(pm, 0, sizeof(struct mtk_vcodec_pm));
- pm->mtkdev = mtkdev;
pm->dev = &pdev->dev;
- dev = &pdev->dev;
enc_clk = &pm->venc_clk;
- node = of_parse_phandle(dev->of_node, "mediatek,larb", 0);
- if (!node) {
- mtk_v4l2_err("no mediatek,larb found");
- return -ENODEV;
- }
- pdev = of_find_device_by_node(node);
- of_node_put(node);
- if (!pdev) {
- mtk_v4l2_err("no mediatek,larb device found");
- return -ENODEV;
- }
- pm->larbvenc = &pdev->dev;
- pdev = mtkdev->plat_dev;
- pm->dev = &pdev->dev;
-
enc_clk->clk_num = of_property_count_strings(pdev->dev.of_node,
"clock-names");
if (enc_clk->clk_num > 0) {
enc_clk->clk_info = devm_kcalloc(&pdev->dev,
enc_clk->clk_num, sizeof(*clk_info),
GFP_KERNEL);
- if (!enc_clk->clk_info) {
- ret = -ENOMEM;
- goto put_larbvenc;
- }
+ if (!enc_clk->clk_info)
+ return -ENOMEM;
} else {
mtk_v4l2_err("Failed to get venc clock count");
- ret = -EINVAL;
- goto put_larbvenc;
+ return -EINVAL;
}
for (i = 0; i < enc_clk->clk_num; i++) {
@@ -68,32 +45,20 @@ int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *mtkdev)
"clock-names", i, &clk_info->clk_name);
if (ret) {
mtk_v4l2_err("venc failed to get clk name %d", i);
- goto put_larbvenc;
+ return ret;
}
clk_info->vcodec_clk = devm_clk_get(&pdev->dev,
clk_info->clk_name);
if (IS_ERR(clk_info->vcodec_clk)) {
mtk_v4l2_err("venc devm_clk_get (%d)%s fail", i,
clk_info->clk_name);
- ret = PTR_ERR(clk_info->vcodec_clk);
- goto put_larbvenc;
+ return PTR_ERR(clk_info->vcodec_clk);
}
}
return 0;
-
-put_larbvenc:
- put_device(pm->larbvenc);
- return ret;
-}
-
-void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *mtkdev)
-{
- pm_runtime_disable(mtkdev->pm.dev);
- put_device(mtkdev->pm.larbvenc);
}
-
void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm)
{
struct mtk_vcodec_clk *enc_clk = &pm->venc_clk;
@@ -108,11 +73,6 @@ void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm)
}
}
- ret = mtk_smi_larb_get(pm->larbvenc);
- if (ret) {
- mtk_v4l2_err("mtk_smi_larb_get larb3 fail %d", ret);
- goto clkerr;
- }
return;
clkerr:
@@ -125,7 +85,6 @@ void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm)
struct mtk_vcodec_clk *enc_clk = &pm->venc_clk;
int i = 0;
- mtk_smi_larb_put(pm->larbvenc);
for (i = enc_clk->clk_num - 1; i >= 0; i--)
clk_disable_unprepare(enc_clk->clk_info[i].vcodec_clk);
}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h
index b7ecdfd74823..bc455cefc0cd 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_enc_pm.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_enc_pm.h
@@ -9,8 +9,7 @@
#include "mtk_vcodec_drv.h"
-int mtk_vcodec_init_enc_pm(struct mtk_vcodec_dev *dev);
-void mtk_vcodec_release_enc_pm(struct mtk_vcodec_dev *dev);
+int mtk_vcodec_init_enc_clk(struct mtk_vcodec_dev *dev);
void mtk_vcodec_enc_clock_on(struct mtk_vcodec_pm *pm);
void mtk_vcodec_enc_clock_off(struct mtk_vcodec_pm *pm);
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c
index 94b39ae5c2e1..94b39ae5c2e1 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.c
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h
index 539bb626772c..15ab6b8e3ae2 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw.h
@@ -6,7 +6,7 @@
#include <linux/remoteproc.h>
#include <linux/remoteproc/mtk_scp.h>
-#include "../mtk-vpu/mtk_vpu.h"
+#include "../vpu/mtk_vpu.h"
struct mtk_vcodec_dev;
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h
index b41e66185cec..b41e66185cec 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_priv.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_priv.h
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c
index d8e66b645bd8..d8e66b645bd8 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_scp.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_scp.c
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c
index cd27f637dbe7..cfc7ebed8fb7 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_fw_vpu.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_fw_vpu.c
@@ -102,6 +102,8 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_vpu_init(struct mtk_vcodec_dev *dev,
vpu_wdt_reg_handler(fw_pdev, mtk_vcodec_vpu_reset_handler, dev, rst_id);
fw = devm_kzalloc(&dev->plat_dev->dev, sizeof(*fw), GFP_KERNEL);
+ if (!fw)
+ return ERR_PTR(-ENOMEM);
fw->type = VPU;
fw->ops = &mtk_vcodec_vpu_msg;
fw->pdev = fw_pdev;
diff --git a/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c
new file mode 100644
index 000000000000..552b4c93d972
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+* Copyright (c) 2016 MediaTek Inc.
+* Author: Tiffany Lin <tiffany.lin@mediatek.com>
+*/
+
+#include <linux/errno.h>
+#include <linux/wait.h>
+
+#include "mtk_vcodec_drv.h"
+#include "mtk_vcodec_intr.h"
+#include "mtk_vcodec_util.h"
+
+int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx,
+ int command, unsigned int timeout_ms,
+ unsigned int hw_id)
+{
+ long timeout_jiff, ret;
+ int status = 0;
+
+ timeout_jiff = msecs_to_jiffies(timeout_ms);
+ ret = wait_event_interruptible_timeout(ctx->queue[hw_id],
+ ctx->int_cond[hw_id],
+ timeout_jiff);
+
+ if (!ret) {
+ status = -1; /* timeout */
+ mtk_v4l2_err("[%d] cmd=%d, type=%d, dec timeout=%ums (%d %d)",
+ ctx->id, command, ctx->type, timeout_ms,
+ ctx->int_cond[hw_id], ctx->int_type[hw_id]);
+ } else if (-ERESTARTSYS == ret) {
+ status = -1;
+ mtk_v4l2_err("[%d] cmd=%d, type=%d, dec inter fail (%d %d)",
+ ctx->id, command, ctx->type,
+ ctx->int_cond[hw_id], ctx->int_type[hw_id]);
+ }
+
+ ctx->int_cond[hw_id] = 0;
+ ctx->int_type[hw_id] = 0;
+
+ return status;
+}
+EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx);
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h
index 638cd1f3526a..9681f492813b 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_intr.h
@@ -12,7 +12,8 @@
struct mtk_vcodec_ctx;
/* timeout is ms */
-int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *data, int command,
- unsigned int timeout_ms);
+int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx,
+ int command, unsigned int timeout_ms,
+ unsigned int hw_id);
#endif /* _MTK_VCODEC_INTR_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c
index 5bac820a47fc..ace78c4b5b9e 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.c
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.c
@@ -6,7 +6,10 @@
*/
#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include "mtk_vcodec_dec_hw.h"
#include "mtk_vcodec_drv.h"
#include "mtk_vcodec_util.h"
@@ -71,25 +74,59 @@ void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data,
}
EXPORT_SYMBOL(mtk_vcodec_mem_free);
-void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *dev,
- struct mtk_vcodec_ctx *ctx)
+void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx)
{
- unsigned long flags;
+ if (hw_idx >= MTK_VDEC_HW_MAX || hw_idx < 0 || !dev->subdev_dev[hw_idx]) {
+ mtk_v4l2_err("hw idx is out of range:%d", hw_idx);
+ return NULL;
+ }
+
+ return dev->subdev_dev[hw_idx];
+}
+EXPORT_SYMBOL(mtk_vcodec_get_hw_dev);
- spin_lock_irqsave(&dev->irqlock, flags);
- dev->curr_ctx = ctx;
- spin_unlock_irqrestore(&dev->irqlock, flags);
+void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev,
+ struct mtk_vcodec_ctx *ctx, int hw_idx)
+{
+ unsigned long flags;
+ struct mtk_vdec_hw_dev *subdev_dev;
+
+ spin_lock_irqsave(&vdec_dev->irqlock, flags);
+ if (vdec_dev->vdec_pdata->is_subdev_supported) {
+ subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx);
+ if (!subdev_dev) {
+ mtk_v4l2_err("Failed to get hw dev");
+ spin_unlock_irqrestore(&vdec_dev->irqlock, flags);
+ return;
+ }
+ subdev_dev->curr_ctx = ctx;
+ } else {
+ vdec_dev->curr_ctx = ctx;
+ }
+ spin_unlock_irqrestore(&vdec_dev->irqlock, flags);
}
EXPORT_SYMBOL(mtk_vcodec_set_curr_ctx);
-struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *dev)
+struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev,
+ unsigned int hw_idx)
{
unsigned long flags;
struct mtk_vcodec_ctx *ctx;
-
- spin_lock_irqsave(&dev->irqlock, flags);
- ctx = dev->curr_ctx;
- spin_unlock_irqrestore(&dev->irqlock, flags);
+ struct mtk_vdec_hw_dev *subdev_dev;
+
+ spin_lock_irqsave(&vdec_dev->irqlock, flags);
+ if (vdec_dev->vdec_pdata->is_subdev_supported) {
+ subdev_dev = mtk_vcodec_get_hw_dev(vdec_dev, hw_idx);
+ if (!subdev_dev) {
+ mtk_v4l2_err("Failed to get hw dev");
+ spin_unlock_irqrestore(&vdec_dev->irqlock, flags);
+ return NULL;
+ }
+ ctx = subdev_dev->curr_ctx;
+ } else {
+ ctx = vdec_dev->curr_ctx;
+ }
+ spin_unlock_irqrestore(&vdec_dev->irqlock, flags);
return ctx;
}
EXPORT_SYMBOL(mtk_vcodec_get_curr_ctx);
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h
index 87c3d6d4bfa7..71956627a0e2 100644
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_util.h
+++ b/drivers/media/platform/mediatek/vcodec/mtk_vcodec_util.h
@@ -54,8 +54,10 @@ int mtk_vcodec_mem_alloc(struct mtk_vcodec_ctx *data,
struct mtk_vcodec_mem *mem);
void mtk_vcodec_mem_free(struct mtk_vcodec_ctx *data,
struct mtk_vcodec_mem *mem);
-void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *dev,
- struct mtk_vcodec_ctx *ctx);
-struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *dev);
+void mtk_vcodec_set_curr_ctx(struct mtk_vcodec_dev *vdec_dev,
+ struct mtk_vcodec_ctx *ctx, int hw_idx);
+struct mtk_vcodec_ctx *mtk_vcodec_get_curr_ctx(struct mtk_vcodec_dev *vdec_dev,
+ unsigned int hw_idx);
+void *mtk_vcodec_get_hw_dev(struct mtk_vcodec_dev *dev, int hw_idx);
#endif /* _MTK_VCODEC_UTIL_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c
index 40d6e6c5ac7a..481655bb6016 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_if.c
@@ -413,7 +413,7 @@ static int vdec_h264_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
/* wait decoder done interrupt */
err = mtk_vcodec_wait_for_done_ctx(inst->ctx,
MTK_INST_IRQ_RECEIVED,
- WAIT_INTR_TIMEOUT_MS);
+ WAIT_INTR_TIMEOUT_MS, 0);
if (err)
goto err_free_fb_out;
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c
index fada4d146703..43542de11e9c 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_h264_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_h264_req_if.c
@@ -727,7 +727,7 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
/* wait decoder done interrupt */
err = mtk_vcodec_wait_for_done_ctx(inst->ctx,
MTK_INST_IRQ_RECEIVED,
- WAIT_INTR_TIMEOUT_MS);
+ WAIT_INTR_TIMEOUT_MS, 0);
if (err)
goto err_free_fb_out;
vpu_dec_end(vpu);
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c
index e5393f841080..88c046731754 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp8_if.c
+++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp8_if.c
@@ -488,7 +488,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
/* wait decoder done interrupt */
mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED,
- WAIT_INTR_TIMEOUT_MS);
+ WAIT_INTR_TIMEOUT_MS, 0);
if (inst->vsi->load_data)
load_dec_table(inst);
diff --git a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c
index 71cdc3ddafcb..70b8383f7c8e 100644
--- a/drivers/media/platform/mtk-vcodec/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mediatek/vcodec/vdec/vdec_vp9_if.c
@@ -539,7 +539,7 @@ static bool vp9_wait_dec_end(struct vdec_vp9_inst *inst)
mtk_vcodec_wait_for_done_ctx(inst->ctx,
MTK_INST_IRQ_RECEIVED,
- WAIT_INTR_TIMEOUT_MS);
+ WAIT_INTR_TIMEOUT_MS, 0);
if (ctx->irq_status & MTK_VDEC_IRQ_STATUS_DEC_SUCCESS)
return true;
diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h b/drivers/media/platform/mediatek/vcodec/vdec_drv_base.h
index e913f963b7db..e913f963b7db 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_drv_base.h
+++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_base.h
diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c
index 42008243ceac..05a5b240e906 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.c
+++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.c
@@ -24,21 +24,24 @@ int vdec_if_init(struct mtk_vcodec_ctx *ctx, unsigned int fourcc)
break;
case V4L2_PIX_FMT_H264:
ctx->dec_if = &vdec_h264_if;
+ ctx->hw_id = MTK_VDEC_CORE;
break;
case V4L2_PIX_FMT_VP8:
ctx->dec_if = &vdec_vp8_if;
+ ctx->hw_id = MTK_VDEC_CORE;
break;
case V4L2_PIX_FMT_VP9:
ctx->dec_if = &vdec_vp9_if;
+ ctx->hw_id = MTK_VDEC_CORE;
break;
default:
return -EINVAL;
}
mtk_vdec_lock(ctx);
- mtk_vcodec_dec_clock_on(&ctx->dev->pm);
+ mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id);
ret = ctx->dec_if->init(ctx);
- mtk_vcodec_dec_clock_off(&ctx->dev->pm);
+ mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id);
mtk_vdec_unlock(ctx);
return ret;
@@ -69,13 +72,11 @@ int vdec_if_decode(struct mtk_vcodec_ctx *ctx, struct mtk_vcodec_mem *bs,
mtk_vdec_lock(ctx);
- mtk_vcodec_set_curr_ctx(ctx->dev, ctx);
- mtk_vcodec_dec_clock_on(&ctx->dev->pm);
- enable_irq(ctx->dev->dec_irq);
+ mtk_vcodec_set_curr_ctx(ctx->dev, ctx, ctx->hw_id);
+ mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id);
ret = ctx->dec_if->decode(ctx->drv_handle, bs, fb, res_chg);
- disable_irq(ctx->dev->dec_irq);
- mtk_vcodec_dec_clock_off(&ctx->dev->pm);
- mtk_vcodec_set_curr_ctx(ctx->dev, NULL);
+ mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id);
+ mtk_vcodec_set_curr_ctx(ctx->dev, NULL, ctx->hw_id);
mtk_vdec_unlock(ctx);
@@ -103,9 +104,9 @@ void vdec_if_deinit(struct mtk_vcodec_ctx *ctx)
return;
mtk_vdec_lock(ctx);
- mtk_vcodec_dec_clock_on(&ctx->dev->pm);
+ mtk_vcodec_dec_clock_on(ctx->dev, ctx->hw_id);
ctx->dec_if->deinit(ctx->drv_handle);
- mtk_vcodec_dec_clock_off(&ctx->dev->pm);
+ mtk_vcodec_dec_clock_off(ctx->dev, ctx->hw_id);
mtk_vdec_unlock(ctx);
ctx->drv_handle = NULL;
diff --git a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h
index d467e8af4a84..d467e8af4a84 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_drv_if.h
+++ b/drivers/media/platform/mediatek/vcodec/vdec_drv_if.h
diff --git a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h
index 5f45a537beb4..bf54d6d9a857 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_ipi_msg.h
+++ b/drivers/media/platform/mediatek/vcodec/vdec_ipi_msg.h
@@ -18,12 +18,16 @@ enum vdec_ipi_msgid {
AP_IPIMSG_DEC_END = 0xA002,
AP_IPIMSG_DEC_DEINIT = 0xA003,
AP_IPIMSG_DEC_RESET = 0xA004,
+ AP_IPIMSG_DEC_CORE = 0xA005,
+ AP_IPIMSG_DEC_CORE_END = 0xA006,
VPU_IPIMSG_DEC_INIT_ACK = 0xB000,
VPU_IPIMSG_DEC_START_ACK = 0xB001,
VPU_IPIMSG_DEC_END_ACK = 0xB002,
VPU_IPIMSG_DEC_DEINIT_ACK = 0xB003,
VPU_IPIMSG_DEC_RESET_ACK = 0xB004,
+ VPU_IPIMSG_DEC_CORE_ACK = 0xB005,
+ VPU_IPIMSG_DEC_CORE_END_ACK = 0xB006,
};
/**
@@ -31,6 +35,8 @@ enum vdec_ipi_msgid {
* @msg_id : vdec_ipi_msgid
* @vpu_inst_addr : VPU decoder instance address. Used if ABI version < 2.
* @inst_id : instance ID. Used if the ABI version >= 2.
+ * @codec_type : codec fourcc
+ * @reserved : reserved param
*/
struct vdec_ap_ipi_cmd {
uint32_t msg_id;
@@ -38,6 +44,8 @@ struct vdec_ap_ipi_cmd {
uint32_t vpu_inst_addr;
uint32_t inst_id;
};
+ u32 codec_type;
+ u32 reserved;
};
/**
@@ -55,12 +63,12 @@ struct vdec_vpu_ipi_ack {
/**
* struct vdec_ap_ipi_init - for AP_IPIMSG_DEC_INIT
* @msg_id : AP_IPIMSG_DEC_INIT
- * @reserved : Reserved field
+ * @codec_type : codec fourcc
* @ap_inst_addr : AP video decoder instance address
*/
struct vdec_ap_ipi_init {
uint32_t msg_id;
- uint32_t reserved;
+ u32 codec_type;
uint64_t ap_inst_addr;
};
@@ -73,7 +81,7 @@ struct vdec_ap_ipi_init {
* H264 decoder [0]:buf_sz [1]:nal_start
* VP8 decoder [0]:width/height
* VP9 decoder [0]:profile, [1][2] width/height
- * @reserved : Reserved field
+ * @codec_type : codec fourcc
*/
struct vdec_ap_ipi_dec_start {
uint32_t msg_id;
@@ -82,7 +90,7 @@ struct vdec_ap_ipi_dec_start {
uint32_t inst_id;
};
uint32_t data[3];
- uint32_t reserved;
+ u32 codec_type;
};
/**
diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c
new file mode 100644
index 000000000000..4b062a8128b4
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Yunfei Dong <yunfei.dong@mediatek.com>
+ */
+
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+
+#include "mtk_vcodec_dec_pm.h"
+#include "mtk_vcodec_drv.h"
+#include "vdec_msg_queue.h"
+
+#define VDEC_MSG_QUEUE_TIMEOUT_MS 1500
+
+/* the size used to store lat slice header information */
+#define VDEC_LAT_SLICE_HEADER_SZ (640 * SZ_1K)
+
+/* the size used to store avc error information */
+#define VDEC_ERR_MAP_SZ_AVC (17 * SZ_1K)
+
+/* core will read the trans buffer which decoded by lat to decode again.
+ * The trans buffer size of FHD and 4K bitstreams are different.
+ */
+static int vde_msg_queue_get_trans_size(int width, int height)
+{
+ if (width > 1920 || height > 1088)
+ return 30 * SZ_1M;
+ else
+ return 6 * SZ_1M;
+}
+
+void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index)
+{
+ init_waitqueue_head(&ctx->ready_to_use);
+ INIT_LIST_HEAD(&ctx->ready_queue);
+ spin_lock_init(&ctx->ready_lock);
+ ctx->ready_num = 0;
+ ctx->hardware_index = hardware_index;
+}
+
+static struct list_head *vdec_get_buf_list(int hardware_index, struct vdec_lat_buf *buf)
+{
+ switch (hardware_index) {
+ case MTK_VDEC_CORE:
+ return &buf->core_list;
+ case MTK_VDEC_LAT0:
+ return &buf->lat_list;
+ default:
+ return NULL;
+ }
+}
+
+int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *msg_ctx, struct vdec_lat_buf *buf)
+{
+ struct list_head *head;
+
+ head = vdec_get_buf_list(msg_ctx->hardware_index, buf);
+ if (!head) {
+ mtk_v4l2_err("fail to qbuf: %d", msg_ctx->hardware_index);
+ return -EINVAL;
+ }
+
+ spin_lock(&msg_ctx->ready_lock);
+ list_add_tail(head, &msg_ctx->ready_queue);
+ msg_ctx->ready_num++;
+
+ if (msg_ctx->hardware_index != MTK_VDEC_CORE)
+ wake_up_all(&msg_ctx->ready_to_use);
+ else
+ queue_work(buf->ctx->dev->core_workqueue,
+ &buf->ctx->msg_queue.core_work);
+
+ mtk_v4l2_debug(3, "enqueue buf type: %d addr: 0x%p num: %d",
+ msg_ctx->hardware_index, buf, msg_ctx->ready_num);
+ spin_unlock(&msg_ctx->ready_lock);
+
+ return 0;
+}
+
+static bool vdec_msg_queue_wait_event(struct vdec_msg_queue_ctx *msg_ctx)
+{
+ int ret;
+
+ ret = wait_event_timeout(msg_ctx->ready_to_use,
+ !list_empty(&msg_ctx->ready_queue),
+ msecs_to_jiffies(VDEC_MSG_QUEUE_TIMEOUT_MS));
+ if (!ret)
+ return false;
+
+ return true;
+}
+
+struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *msg_ctx)
+{
+ struct vdec_lat_buf *buf;
+ struct list_head *head;
+ int ret;
+
+ spin_lock(&msg_ctx->ready_lock);
+ if (list_empty(&msg_ctx->ready_queue)) {
+ mtk_v4l2_debug(3, "queue is NULL, type:%d num: %d",
+ msg_ctx->hardware_index, msg_ctx->ready_num);
+ spin_unlock(&msg_ctx->ready_lock);
+
+ if (msg_ctx->hardware_index == MTK_VDEC_CORE)
+ return NULL;
+
+ ret = vdec_msg_queue_wait_event(msg_ctx);
+ if (!ret)
+ return NULL;
+ spin_lock(&msg_ctx->ready_lock);
+ }
+
+ if (msg_ctx->hardware_index == MTK_VDEC_CORE)
+ buf = list_first_entry(&msg_ctx->ready_queue,
+ struct vdec_lat_buf, core_list);
+ else
+ buf = list_first_entry(&msg_ctx->ready_queue,
+ struct vdec_lat_buf, lat_list);
+
+ head = vdec_get_buf_list(msg_ctx->hardware_index, buf);
+ if (!head) {
+ spin_unlock(&msg_ctx->ready_lock);
+ mtk_v4l2_err("fail to dqbuf: %d", msg_ctx->hardware_index);
+ return NULL;
+ }
+ list_del(head);
+
+ msg_ctx->ready_num--;
+ mtk_v4l2_debug(3, "dqueue buf type:%d addr: 0x%p num: %d",
+ msg_ctx->hardware_index, buf, msg_ctx->ready_num);
+ spin_unlock(&msg_ctx->ready_lock);
+
+ return buf;
+}
+
+void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr)
+{
+ spin_lock(&msg_queue->lat_ctx.ready_lock);
+ msg_queue->wdma_rptr_addr = ube_rptr;
+ mtk_v4l2_debug(3, "update ube rprt (0x%llx)", ube_rptr);
+ spin_unlock(&msg_queue->lat_ctx.ready_lock);
+}
+
+void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr)
+{
+ spin_lock(&msg_queue->lat_ctx.ready_lock);
+ msg_queue->wdma_wptr_addr = ube_wptr;
+ mtk_v4l2_debug(3, "update ube wprt: (0x%llx 0x%llx) offset: 0x%llx",
+ msg_queue->wdma_rptr_addr, msg_queue->wdma_wptr_addr,
+ ube_wptr);
+ spin_unlock(&msg_queue->lat_ctx.ready_lock);
+}
+
+bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue)
+{
+ long timeout_jiff;
+ int ret;
+
+ timeout_jiff = msecs_to_jiffies(1000 * (NUM_BUFFER_COUNT + 2));
+ ret = wait_event_timeout(msg_queue->lat_ctx.ready_to_use,
+ msg_queue->lat_ctx.ready_num == NUM_BUFFER_COUNT,
+ timeout_jiff);
+ if (ret) {
+ mtk_v4l2_debug(3, "success to get lat buf: %d",
+ msg_queue->lat_ctx.ready_num);
+ return true;
+ }
+ mtk_v4l2_err("failed with lat buf isn't full: %d",
+ msg_queue->lat_ctx.ready_num);
+ return false;
+}
+
+void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue,
+ struct mtk_vcodec_ctx *ctx)
+{
+ struct vdec_lat_buf *lat_buf;
+ struct mtk_vcodec_mem *mem;
+ int i;
+
+ mem = &msg_queue->wdma_addr;
+ if (mem->va)
+ mtk_vcodec_mem_free(ctx, mem);
+ for (i = 0; i < NUM_BUFFER_COUNT; i++) {
+ lat_buf = &msg_queue->lat_buf[i];
+
+ mem = &lat_buf->wdma_err_addr;
+ if (mem->va)
+ mtk_vcodec_mem_free(ctx, mem);
+
+ mem = &lat_buf->slice_bc_addr;
+ if (mem->va)
+ mtk_vcodec_mem_free(ctx, mem);
+
+ kfree(lat_buf->private_data);
+ }
+}
+
+static void vdec_msg_queue_core_work(struct work_struct *work)
+{
+ struct vdec_msg_queue *msg_queue =
+ container_of(work, struct vdec_msg_queue, core_work);
+ struct mtk_vcodec_ctx *ctx =
+ container_of(msg_queue, struct mtk_vcodec_ctx, msg_queue);
+ struct mtk_vcodec_dev *dev = ctx->dev;
+ struct vdec_lat_buf *lat_buf;
+
+ lat_buf = vdec_msg_queue_dqbuf(&dev->msg_queue_core_ctx);
+ if (!lat_buf)
+ return;
+
+ ctx = lat_buf->ctx;
+ mtk_vcodec_set_curr_ctx(dev, ctx, MTK_VDEC_CORE);
+
+ lat_buf->core_decode(lat_buf);
+
+ mtk_vcodec_set_curr_ctx(dev, NULL, MTK_VDEC_CORE);
+ vdec_msg_queue_qbuf(&ctx->msg_queue.lat_ctx, lat_buf);
+
+ if (!list_empty(&ctx->msg_queue.lat_ctx.ready_queue)) {
+ mtk_v4l2_debug(3, "re-schedule to decode for core: %d",
+ dev->msg_queue_core_ctx.ready_num);
+ queue_work(dev->core_workqueue, &msg_queue->core_work);
+ }
+}
+
+int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue,
+ struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode,
+ int private_size)
+{
+ struct vdec_lat_buf *lat_buf;
+ int i, err;
+
+ /* already init msg queue */
+ if (msg_queue->wdma_addr.size)
+ return 0;
+
+ vdec_msg_queue_init_ctx(&msg_queue->lat_ctx, MTK_VDEC_LAT0);
+ INIT_WORK(&msg_queue->core_work, vdec_msg_queue_core_work);
+ msg_queue->wdma_addr.size =
+ vde_msg_queue_get_trans_size(ctx->picinfo.buf_w,
+ ctx->picinfo.buf_h);
+
+ err = mtk_vcodec_mem_alloc(ctx, &msg_queue->wdma_addr);
+ if (err) {
+ mtk_v4l2_err("failed to allocate wdma_addr buf");
+ return -ENOMEM;
+ }
+ msg_queue->wdma_rptr_addr = msg_queue->wdma_addr.dma_addr;
+ msg_queue->wdma_wptr_addr = msg_queue->wdma_addr.dma_addr;
+
+ for (i = 0; i < NUM_BUFFER_COUNT; i++) {
+ lat_buf = &msg_queue->lat_buf[i];
+
+ lat_buf->wdma_err_addr.size = VDEC_ERR_MAP_SZ_AVC;
+ err = mtk_vcodec_mem_alloc(ctx, &lat_buf->wdma_err_addr);
+ if (err) {
+ mtk_v4l2_err("failed to allocate wdma_err_addr buf[%d]", i);
+ goto mem_alloc_err;
+ }
+
+ lat_buf->slice_bc_addr.size = VDEC_LAT_SLICE_HEADER_SZ;
+ err = mtk_vcodec_mem_alloc(ctx, &lat_buf->slice_bc_addr);
+ if (err) {
+ mtk_v4l2_err("failed to allocate wdma_addr buf[%d]", i);
+ goto mem_alloc_err;
+ }
+
+ lat_buf->private_data = kzalloc(private_size, GFP_KERNEL);
+ if (!lat_buf->private_data) {
+ err = -ENOMEM;
+ goto mem_alloc_err;
+ }
+
+ lat_buf->ctx = ctx;
+ lat_buf->core_decode = core_decode;
+ err = vdec_msg_queue_qbuf(&msg_queue->lat_ctx, lat_buf);
+ if (err) {
+ mtk_v4l2_err("failed to qbuf buf[%d]", i);
+ goto mem_alloc_err;
+ }
+ }
+ return 0;
+
+mem_alloc_err:
+ vdec_msg_queue_deinit(msg_queue, ctx);
+ return err;
+}
diff --git a/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h
new file mode 100644
index 000000000000..b6ba66d3e026
--- /dev/null
+++ b/drivers/media/platform/mediatek/vcodec/vdec_msg_queue.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Author: Yunfei Dong <yunfei.dong@mediatek.com>
+ */
+
+#ifndef _VDEC_MSG_QUEUE_H_
+#define _VDEC_MSG_QUEUE_H_
+
+#include <linux/sched.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "mtk_vcodec_util.h"
+
+#define NUM_BUFFER_COUNT 3
+
+struct vdec_lat_buf;
+struct mtk_vcodec_ctx;
+struct mtk_vcodec_dev;
+typedef int (*core_decode_cb_t)(struct vdec_lat_buf *lat_buf);
+
+/**
+ * struct vdec_msg_queue_ctx - represents a queue for buffers ready to be processed
+ * @ready_to_use: ready used queue used to signalize when get a job queue
+ * @ready_queue: list of ready lat buffer queues
+ * @ready_lock: spin lock to protect the lat buffer usage
+ * @ready_num: number of buffers ready to be processed
+ * @hardware_index: hardware id that this queue is used for
+ */
+struct vdec_msg_queue_ctx {
+ wait_queue_head_t ready_to_use;
+ struct list_head ready_queue;
+ /* protect lat buffer */
+ spinlock_t ready_lock;
+ int ready_num;
+ int hardware_index;
+};
+
+/**
+ * struct vdec_lat_buf - lat buffer message used to store lat info for core decode
+ * @wdma_err_addr: wdma error address used for lat hardware
+ * @slice_bc_addr: slice bc address used for lat hardware
+ * @ts_info: need to set timestamp from output to capture
+ *
+ * @private_data: shared information used to lat and core hardware
+ * @ctx: mtk vcodec context information
+ * @core_decode: different codec use different decode callback function
+ * @lat_list: add lat buffer to lat head list
+ * @core_list: add lat buffer to core head list
+ */
+struct vdec_lat_buf {
+ struct mtk_vcodec_mem wdma_err_addr;
+ struct mtk_vcodec_mem slice_bc_addr;
+ struct vb2_v4l2_buffer ts_info;
+
+ void *private_data;
+ struct mtk_vcodec_ctx *ctx;
+ core_decode_cb_t core_decode;
+ struct list_head lat_list;
+ struct list_head core_list;
+};
+
+/**
+ * struct vdec_msg_queue - used to store lat buffer message
+ * @lat_buf: lat buffer used to store lat buffer information
+ * @wdma_addr: wdma address used for ube
+ * @wdma_rptr_addr: ube read point
+ * @wdma_wptr_addr: ube write point
+ * @core_work: core hardware work
+ * @lat_ctx: used to store lat buffer list
+ */
+struct vdec_msg_queue {
+ struct vdec_lat_buf lat_buf[NUM_BUFFER_COUNT];
+
+ struct mtk_vcodec_mem wdma_addr;
+ u64 wdma_rptr_addr;
+ u64 wdma_wptr_addr;
+
+ struct work_struct core_work;
+ struct vdec_msg_queue_ctx lat_ctx;
+};
+
+/**
+ * vdec_msg_queue_init - init lat buffer information.
+ * @msg_queue: used to store the lat buffer information
+ * @ctx: v4l2 ctx
+ * @core_decode: core decode callback for each codec
+ * @private_size: the private data size used to share with core
+ *
+ * Return: returns 0 if init successfully, or fail.
+ */
+int vdec_msg_queue_init(struct vdec_msg_queue *msg_queue,
+ struct mtk_vcodec_ctx *ctx, core_decode_cb_t core_decode,
+ int private_size);
+
+/**
+ * vdec_msg_queue_init_ctx - used to init msg queue context information.
+ * @ctx: message queue context
+ * @hardware_index: hardware index
+ */
+void vdec_msg_queue_init_ctx(struct vdec_msg_queue_ctx *ctx, int hardware_index);
+
+/**
+ * vdec_msg_queue_qbuf - enqueue lat buffer to queue list.
+ * @ctx: message queue context
+ * @buf: current lat buffer
+ *
+ * Return: returns 0 if qbuf successfully, or fail.
+ */
+int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *ctx, struct vdec_lat_buf *buf);
+
+/**
+ * vdec_msg_queue_dqbuf - dequeue lat buffer from queue list.
+ * @ctx: message queue context
+ *
+ * Return: returns not null if dq successfully, or fail.
+ */
+struct vdec_lat_buf *vdec_msg_queue_dqbuf(struct vdec_msg_queue_ctx *ctx);
+
+/**
+ * vdec_msg_queue_update_ube_rptr - used to updata the ube read point.
+ * @msg_queue: used to store the lat buffer information
+ * @ube_rptr: current ube read point
+ */
+void vdec_msg_queue_update_ube_rptr(struct vdec_msg_queue *msg_queue, uint64_t ube_rptr);
+
+/**
+ * vdec_msg_queue_update_ube_wptr - used to updata the ube write point.
+ * @msg_queue: used to store the lat buffer information
+ * @ube_wptr: current ube write point
+ */
+void vdec_msg_queue_update_ube_wptr(struct vdec_msg_queue *msg_queue, uint64_t ube_wptr);
+
+/**
+ * vdec_msg_queue_wait_lat_buf_full - used to check whether all lat buffer
+ * in lat list.
+ * @msg_queue: used to store the lat buffer information
+ *
+ * Return: returns true if successfully, or fail.
+ */
+bool vdec_msg_queue_wait_lat_buf_full(struct vdec_msg_queue *msg_queue);
+
+/**
+ * vdec_msg_queue_deinit - deinit lat buffer information.
+ * @msg_queue: used to store the lat buffer information
+ * @ctx: v4l2 ctx
+ */
+void vdec_msg_queue_deinit(struct vdec_msg_queue *msg_queue,
+ struct mtk_vcodec_ctx *ctx);
+
+#endif
diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c
index 5dffc459a33d..dd35d2c5f920 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.c
@@ -85,6 +85,8 @@ static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv)
case VPU_IPIMSG_DEC_END_ACK:
case VPU_IPIMSG_DEC_DEINIT_ACK:
case VPU_IPIMSG_DEC_RESET_ACK:
+ case VPU_IPIMSG_DEC_CORE_ACK:
+ case VPU_IPIMSG_DEC_CORE_END_ACK:
break;
default:
@@ -98,18 +100,29 @@ static void vpu_dec_ipi_handler(void *data, unsigned int len, void *priv)
static int vcodec_vpu_send_msg(struct vdec_vpu_inst *vpu, void *msg, int len)
{
- int err;
+ int err, id, msgid;
- mtk_vcodec_debug(vpu, "id=%X", *(uint32_t *)msg);
+ msgid = *(uint32_t *)msg;
+ mtk_vcodec_debug(vpu, "id=%X", msgid);
vpu->failure = 0;
vpu->signaled = 0;
- err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, vpu->id, msg,
+ if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) {
+ if (msgid == AP_IPIMSG_DEC_CORE ||
+ msgid == AP_IPIMSG_DEC_CORE_END)
+ id = vpu->core_id;
+ else
+ id = vpu->id;
+ } else {
+ id = vpu->id;
+ }
+
+ err = mtk_vcodec_fw_ipi_send(vpu->ctx->dev->fw_handler, id, msg,
len, 2000);
if (err) {
mtk_vcodec_err(vpu, "send fail vpu_id=%d msg_id=%X status=%d",
- vpu->id, *(uint32_t *)msg, err);
+ id, msgid, err);
return err;
}
@@ -129,6 +142,7 @@ static int vcodec_send_ap_ipi(struct vdec_vpu_inst *vpu, unsigned int msg_id)
msg.vpu_inst_addr = vpu->inst_addr;
else
msg.inst_id = vpu->inst_id;
+ msg.codec_type = vpu->codec_type;
err = vcodec_vpu_send_msg(vpu, &msg, sizeof(msg));
mtk_vcodec_debug(vpu, "- id=%X ret=%d", msg_id, err);
@@ -147,14 +161,25 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu)
err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler, vpu->id,
vpu->handler, "vdec", NULL);
- if (err != 0) {
+ if (err) {
mtk_vcodec_err(vpu, "vpu_ipi_register fail status=%d", err);
return err;
}
+ if (vpu->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_LAT_SINGLE_CORE) {
+ err = mtk_vcodec_fw_ipi_register(vpu->ctx->dev->fw_handler,
+ vpu->core_id, vpu->handler,
+ "vdec", NULL);
+ if (err) {
+ mtk_vcodec_err(vpu, "vpu_ipi_register core fail status=%d", err);
+ return err;
+ }
+ }
+
memset(&msg, 0, sizeof(msg));
msg.msg_id = AP_IPIMSG_DEC_INIT;
msg.ap_inst_addr = (unsigned long)vpu;
+ msg.codec_type = vpu->codec_type;
mtk_vcodec_debug(vpu, "vdec_inst=%p", vpu);
@@ -185,17 +210,28 @@ int vpu_dec_start(struct vdec_vpu_inst *vpu, uint32_t *data, unsigned int len)
for (i = 0; i < len; i++)
msg.data[i] = data[i];
+ msg.codec_type = vpu->codec_type;
err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg));
mtk_vcodec_debug(vpu, "- ret=%d", err);
return err;
}
+int vpu_dec_core(struct vdec_vpu_inst *vpu)
+{
+ return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE);
+}
+
int vpu_dec_end(struct vdec_vpu_inst *vpu)
{
return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_END);
}
+int vpu_dec_core_end(struct vdec_vpu_inst *vpu)
+{
+ return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_CORE_END);
+}
+
int vpu_dec_deinit(struct vdec_vpu_inst *vpu)
{
return vcodec_send_ap_ipi(vpu, AP_IPIMSG_DEC_DEINIT);
diff --git a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h
index c2ed5b6cab8b..4cb3c7f5a3ad 100644
--- a/drivers/media/platform/mtk-vcodec/vdec_vpu_if.h
+++ b/drivers/media/platform/mediatek/vcodec/vdec_vpu_if.h
@@ -14,6 +14,7 @@ struct mtk_vcodec_ctx;
/**
* struct vdec_vpu_inst - VPU instance for video codec
* @id : ipi msg id for each decoder
+ * @core_id : core id used to separate different hardware
* @vsi : driver structure allocated by VPU side and shared to AP side
* for control and info share
* @failure : VPU execution result status, 0: success, others: fail
@@ -26,9 +27,11 @@ struct mtk_vcodec_ctx;
* @dev : platform device of VPU
* @wq : wait queue to wait VPU message ack
* @handler : ipi handler for each decoder
+ * @codec_type : use codec type to separate different codecs
*/
struct vdec_vpu_inst {
int id;
+ int core_id;
void *vsi;
int32_t failure;
uint32_t inst_addr;
@@ -38,6 +41,7 @@ struct vdec_vpu_inst {
struct mtk_vcodec_ctx *ctx;
wait_queue_head_t wq;
mtk_vcodec_ipi_handler handler;
+ unsigned int codec_type;
};
/**
@@ -82,4 +86,22 @@ int vpu_dec_deinit(struct vdec_vpu_inst *vpu);
*/
int vpu_dec_reset(struct vdec_vpu_inst *vpu);
+/**
+ * vpu_dec_core - core start decoding, basically the function will be invoked once
+ * every frame.
+ *
+ * @vpu : instance for vdec_vpu_inst
+ */
+int vpu_dec_core(struct vdec_vpu_inst *vpu);
+
+/**
+ * vpu_dec_core_end - core end decoding, basically the function will be invoked once
+ * when core HW decoding done and receive interrupt successfully. The
+ * decoder in VPU will updata hardware information and deinit hardware
+ * and check if there is a new decoded frame available to display.
+ *
+ * @vpu : instance for vdec_vpu_inst
+ */
+int vpu_dec_core_end(struct vdec_vpu_inst *vpu);
+
#endif
diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c
index bf03888a824f..4d9b8798dffe 100644
--- a/drivers/media/platform/mtk-vcodec/venc/venc_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/venc/venc_h264_if.c
@@ -335,7 +335,7 @@ static unsigned int h264_enc_wait_venc_done(struct venc_h264_inst *inst)
struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx;
if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
- WAIT_INTR_TIMEOUT_MS)) {
+ WAIT_INTR_TIMEOUT_MS, 0)) {
irq_status = ctx->irq_status;
mtk_vcodec_debug(inst, "irq_status %x <-", irq_status);
}
diff --git a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c b/drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c
index 6b66957d5192..56ce58f761f1 100644
--- a/drivers/media/platform/mtk-vcodec/venc/venc_vp8_if.c
+++ b/drivers/media/platform/mediatek/vcodec/venc/venc_vp8_if.c
@@ -222,7 +222,7 @@ static unsigned int vp8_enc_wait_venc_done(struct venc_vp8_inst *inst)
struct mtk_vcodec_ctx *ctx = (struct mtk_vcodec_ctx *)inst->ctx;
if (!mtk_vcodec_wait_for_done_ctx(ctx, MTK_INST_IRQ_RECEIVED,
- WAIT_INTR_TIMEOUT_MS)) {
+ WAIT_INTR_TIMEOUT_MS, 0)) {
irq_status = ctx->irq_status;
mtk_vcodec_debug(inst, "isr return %x", irq_status);
}
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_base.h b/drivers/media/platform/mediatek/vcodec/venc_drv_base.h
index 3d718411dc73..3d718411dc73 100644
--- a/drivers/media/platform/mtk-vcodec/venc_drv_base.h
+++ b/drivers/media/platform/mediatek/vcodec/venc_drv_base.h
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.c b/drivers/media/platform/mediatek/vcodec/venc_drv_if.c
index ce0bce811615..ce0bce811615 100644
--- a/drivers/media/platform/mtk-vcodec/venc_drv_if.c
+++ b/drivers/media/platform/mediatek/vcodec/venc_drv_if.c
diff --git a/drivers/media/platform/mtk-vcodec/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/venc_drv_if.h
index 0b04a1020873..0b04a1020873 100644
--- a/drivers/media/platform/mtk-vcodec/venc_drv_if.h
+++ b/drivers/media/platform/mediatek/vcodec/venc_drv_if.h
diff --git a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h
index 587a2cf15b76..587a2cf15b76 100644
--- a/drivers/media/platform/mtk-vcodec/venc_ipi_msg.h
+++ b/drivers/media/platform/mediatek/vcodec/venc_ipi_msg.h
diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c
index e7899d8a3e4e..e7899d8a3e4e 100644
--- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.c
diff --git a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.h
index f83bc1b3f2bf..f83bc1b3f2bf 100644
--- a/drivers/media/platform/mtk-vcodec/venc_vpu_if.h
+++ b/drivers/media/platform/mediatek/vcodec/venc_vpu_if.h
diff --git a/drivers/media/platform/mediatek/vpu/Kconfig b/drivers/media/platform/mediatek/vpu/Kconfig
new file mode 100644
index 000000000000..2a8443a93ce0
--- /dev/null
+++ b/drivers/media/platform/mediatek/vpu/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_MEDIATEK_VPU
+ tristate "Mediatek Video Processor Unit"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ help
+ This driver provides downloading VPU firmware and
+ communicating with VPU. This driver for hw video
+ codec embedded in Mediatek's MT8173 SOCs. It is able
+ to handle video decoding/encoding in a range of formats.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mtk-vpu.
diff --git a/drivers/media/platform/mtk-vpu/Makefile b/drivers/media/platform/mediatek/vpu/Makefile
index ecd2d392b818..ecd2d392b818 100644
--- a/drivers/media/platform/mtk-vpu/Makefile
+++ b/drivers/media/platform/mediatek/vpu/Makefile
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.c b/drivers/media/platform/mediatek/vpu/mtk_vpu.c
index 7bd715fc844d..47b684b92f81 100644
--- a/drivers/media/platform/mtk-vpu/mtk_vpu.c
+++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.c
@@ -810,7 +810,6 @@ static int mtk_vpu_probe(struct platform_device *pdev)
{
struct mtk_vpu *vpu;
struct device *dev;
- struct resource *res;
int ret = 0;
dev_dbg(&pdev->dev, "initialization\n");
@@ -908,13 +907,10 @@ static int mtk_vpu_probe(struct platform_device *pdev)
init_waitqueue_head(&vpu->run.wq);
init_waitqueue_head(&vpu->ack_wq);
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "get IRQ resource failed.\n");
- ret = -ENXIO;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
goto free_p_mem;
- }
- vpu->reg.irq = platform_get_irq(pdev, 0);
+ vpu->reg.irq = ret;
ret = devm_request_irq(dev, vpu->reg.irq, vpu_irq_handler, 0,
pdev->name, vpu);
if (ret) {
diff --git a/drivers/media/platform/mtk-vpu/mtk_vpu.h b/drivers/media/platform/mediatek/vpu/mtk_vpu.h
index a56053ff135a..a56053ff135a 100644
--- a/drivers/media/platform/mtk-vpu/mtk_vpu.h
+++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.h
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
deleted file mode 100644
index 6038db96f71c..000000000000
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.c
+++ /dev/null
@@ -1,145 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Copyright (c) 2016 MediaTek Inc.
- * Author: Tiffany Lin <tiffany.lin@mediatek.com>
- */
-
-#include <linux/clk.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/pm_runtime.h>
-#include <soc/mediatek/smi.h>
-
-#include "mtk_vcodec_dec_pm.h"
-#include "mtk_vcodec_util.h"
-
-int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *mtkdev)
-{
- struct device_node *node;
- struct platform_device *pdev;
- struct mtk_vcodec_pm *pm;
- struct mtk_vcodec_clk *dec_clk;
- struct mtk_vcodec_clk_info *clk_info;
- int i = 0, ret = 0;
-
- pdev = mtkdev->plat_dev;
- pm = &mtkdev->pm;
- pm->mtkdev = mtkdev;
- dec_clk = &pm->vdec_clk;
- node = of_parse_phandle(pdev->dev.of_node, "mediatek,larb", 0);
- if (!node) {
- mtk_v4l2_err("of_parse_phandle mediatek,larb fail!");
- return -1;
- }
-
- pdev = of_find_device_by_node(node);
- of_node_put(node);
- if (WARN_ON(!pdev)) {
- return -1;
- }
- pm->larbvdec = &pdev->dev;
- pdev = mtkdev->plat_dev;
- pm->dev = &pdev->dev;
-
- dec_clk->clk_num =
- of_property_count_strings(pdev->dev.of_node, "clock-names");
- if (dec_clk->clk_num > 0) {
- dec_clk->clk_info = devm_kcalloc(&pdev->dev,
- dec_clk->clk_num, sizeof(*clk_info),
- GFP_KERNEL);
- if (!dec_clk->clk_info) {
- ret = -ENOMEM;
- goto put_device;
- }
- } else {
- mtk_v4l2_err("Failed to get vdec clock count");
- ret = -EINVAL;
- goto put_device;
- }
-
- for (i = 0; i < dec_clk->clk_num; i++) {
- clk_info = &dec_clk->clk_info[i];
- ret = of_property_read_string_index(pdev->dev.of_node,
- "clock-names", i, &clk_info->clk_name);
- if (ret) {
- mtk_v4l2_err("Failed to get clock name id = %d", i);
- goto put_device;
- }
- clk_info->vcodec_clk = devm_clk_get(&pdev->dev,
- clk_info->clk_name);
- if (IS_ERR(clk_info->vcodec_clk)) {
- mtk_v4l2_err("devm_clk_get (%d)%s fail", i,
- clk_info->clk_name);
- ret = PTR_ERR(clk_info->vcodec_clk);
- goto put_device;
- }
- }
-
- pm_runtime_enable(&pdev->dev);
- return 0;
-put_device:
- put_device(pm->larbvdec);
- return ret;
-}
-
-void mtk_vcodec_release_dec_pm(struct mtk_vcodec_dev *dev)
-{
- pm_runtime_disable(dev->pm.dev);
- put_device(dev->pm.larbvdec);
-}
-
-int mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm)
-{
- int ret;
-
- ret = pm_runtime_resume_and_get(pm->dev);
- if (ret)
- mtk_v4l2_err("pm_runtime_resume_and_get fail %d", ret);
-
- return ret;
-}
-
-void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm)
-{
- int ret;
-
- ret = pm_runtime_put_sync(pm->dev);
- if (ret)
- mtk_v4l2_err("pm_runtime_put_sync fail %d", ret);
-}
-
-void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm)
-{
- struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk;
- int ret, i = 0;
-
- for (i = 0; i < dec_clk->clk_num; i++) {
- ret = clk_prepare_enable(dec_clk->clk_info[i].vcodec_clk);
- if (ret) {
- mtk_v4l2_err("clk_prepare_enable %d %s fail %d", i,
- dec_clk->clk_info[i].clk_name, ret);
- goto error;
- }
- }
-
- ret = mtk_smi_larb_get(pm->larbvdec);
- if (ret) {
- mtk_v4l2_err("mtk_smi_larb_get larbvdec fail %d", ret);
- goto error;
- }
- return;
-
-error:
- for (i -= 1; i >= 0; i--)
- clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk);
-}
-
-void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm)
-{
- struct mtk_vcodec_clk *dec_clk = &pm->vdec_clk;
- int i = 0;
-
- mtk_smi_larb_put(pm->larbvdec);
- for (i = dec_clk->clk_num - 1; i >= 0; i--)
- clk_disable_unprepare(dec_clk->clk_info[i].vcodec_clk);
-}
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h b/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h
deleted file mode 100644
index 280aeaefdb65..000000000000
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_dec_pm.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (c) 2016 MediaTek Inc.
- * Author: Tiffany Lin <tiffany.lin@mediatek.com>
- */
-
-#ifndef _MTK_VCODEC_DEC_PM_H_
-#define _MTK_VCODEC_DEC_PM_H_
-
-#include "mtk_vcodec_drv.h"
-
-int mtk_vcodec_init_dec_pm(struct mtk_vcodec_dev *dev);
-void mtk_vcodec_release_dec_pm(struct mtk_vcodec_dev *dev);
-
-int mtk_vcodec_dec_pw_on(struct mtk_vcodec_pm *pm);
-void mtk_vcodec_dec_pw_off(struct mtk_vcodec_pm *pm);
-void mtk_vcodec_dec_clock_on(struct mtk_vcodec_pm *pm);
-void mtk_vcodec_dec_clock_off(struct mtk_vcodec_pm *pm);
-
-#endif /* _MTK_VCODEC_DEC_PM_H_ */
diff --git a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c b/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c
deleted file mode 100644
index 70580c2525ba..000000000000
--- a/drivers/media/platform/mtk-vcodec/mtk_vcodec_intr.c
+++ /dev/null
@@ -1,45 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
-* Copyright (c) 2016 MediaTek Inc.
-* Author: Tiffany Lin <tiffany.lin@mediatek.com>
-*/
-
-#include <linux/errno.h>
-#include <linux/wait.h>
-
-#include "mtk_vcodec_drv.h"
-#include "mtk_vcodec_intr.h"
-#include "mtk_vcodec_util.h"
-
-int mtk_vcodec_wait_for_done_ctx(struct mtk_vcodec_ctx *ctx, int command,
- unsigned int timeout_ms)
-{
- wait_queue_head_t *waitqueue;
- long timeout_jiff, ret;
- int status = 0;
-
- waitqueue = (wait_queue_head_t *)&ctx->queue;
- timeout_jiff = msecs_to_jiffies(timeout_ms);
-
- ret = wait_event_interruptible_timeout(*waitqueue,
- ctx->int_cond,
- timeout_jiff);
-
- if (!ret) {
- status = -1; /* timeout */
- mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout time=%ums out %d %d!",
- ctx->id, ctx->type, command, timeout_ms,
- ctx->int_cond, ctx->int_type);
- } else if (-ERESTARTSYS == ret) {
- mtk_v4l2_err("[%d] ctx->type=%d, cmd=%d, wait_event_interruptible_timeout interrupted by a signal %d %d",
- ctx->id, ctx->type, command, ctx->int_cond,
- ctx->int_type);
- status = -1;
- }
-
- ctx->int_cond = 0;
- ctx->int_type = 0;
-
- return status;
-}
-EXPORT_SYMBOL(mtk_vcodec_wait_for_done_ctx);
diff --git a/drivers/media/platform/nvidia/Kconfig b/drivers/media/platform/nvidia/Kconfig
new file mode 100644
index 000000000000..b211b46877f6
--- /dev/null
+++ b/drivers/media/platform/nvidia/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "NVidia media platform drivers"
+
+source "drivers/media/platform/nvidia/tegra-vde/Kconfig"
diff --git a/drivers/media/platform/nvidia/Makefile b/drivers/media/platform/nvidia/Makefile
new file mode 100644
index 000000000000..428415ff83de
--- /dev/null
+++ b/drivers/media/platform/nvidia/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += tegra-vde/
diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig
new file mode 100644
index 000000000000..f7454823bbbb
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig
@@ -0,0 +1,17 @@
+config VIDEO_TEGRA_VDE
+ tristate "NVIDIA Tegra Video Decoder Engine driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on ARCH_TEGRA || COMPILE_TEST
+ depends on VIDEO_DEV
+ select DMA_SHARED_BUFFER
+ select IOMMU_IOVA
+ select MEDIA_CONTROLLER
+ select MEDIA_CONTROLLER_REQUEST_API
+ select SRAM
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_DMA_SG
+ select V4L2_H264
+ select V4L2_MEM2MEM_DEV
+ help
+ Support for the NVIDIA Tegra video decoder unit.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/nvidia/tegra-vde/Makefile b/drivers/media/platform/nvidia/tegra-vde/Makefile
new file mode 100644
index 000000000000..4e96f3305567
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+tegra-vde-y := vde.o iommu.o dmabuf-cache.o h264.o v4l2.o
+obj-$(CONFIG_VIDEO_TEGRA_VDE) += tegra-vde.o
diff --git a/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c
new file mode 100644
index 000000000000..69c346148070
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c
@@ -0,0 +1,229 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NVIDIA Tegra Video decoder driver
+ *
+ * Copyright (C) 2016-2019 GRATE-DRIVER project
+ */
+
+#include <linux/dma-buf.h>
+#include <linux/iova.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+
+#include "vde.h"
+
+MODULE_IMPORT_NS(DMA_BUF);
+
+struct tegra_vde_cache_entry {
+ enum dma_data_direction dma_dir;
+ struct dma_buf_attachment *a;
+ struct delayed_work dwork;
+ struct tegra_vde *vde;
+ struct list_head list;
+ struct sg_table *sgt;
+ struct iova *iova;
+ unsigned int refcnt;
+};
+
+static void tegra_vde_release_entry(struct tegra_vde_cache_entry *entry)
+{
+ struct dma_buf *dmabuf = entry->a->dmabuf;
+
+ WARN_ON_ONCE(entry->refcnt);
+
+ if (entry->vde->domain)
+ tegra_vde_iommu_unmap(entry->vde, entry->iova);
+
+ dma_buf_unmap_attachment(entry->a, entry->sgt, entry->dma_dir);
+ dma_buf_detach(dmabuf, entry->a);
+ dma_buf_put(dmabuf);
+
+ list_del(&entry->list);
+ kfree(entry);
+}
+
+static void tegra_vde_delayed_unmap(struct work_struct *work)
+{
+ struct tegra_vde_cache_entry *entry;
+ struct tegra_vde *vde;
+
+ entry = container_of(work, struct tegra_vde_cache_entry,
+ dwork.work);
+ vde = entry->vde;
+
+ mutex_lock(&vde->map_lock);
+ tegra_vde_release_entry(entry);
+ mutex_unlock(&vde->map_lock);
+}
+
+int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde,
+ struct dma_buf *dmabuf,
+ enum dma_data_direction dma_dir,
+ struct dma_buf_attachment **ap,
+ dma_addr_t *addrp)
+{
+ struct dma_buf_attachment *attachment;
+ struct tegra_vde_cache_entry *entry;
+ struct device *dev = vde->dev;
+ struct sg_table *sgt;
+ struct iova *iova;
+ int err;
+
+ mutex_lock(&vde->map_lock);
+
+ list_for_each_entry(entry, &vde->map_list, list) {
+ if (entry->a->dmabuf != dmabuf)
+ continue;
+
+ if (!cancel_delayed_work(&entry->dwork))
+ continue;
+
+ if (entry->dma_dir != dma_dir)
+ entry->dma_dir = DMA_BIDIRECTIONAL;
+
+ dma_buf_put(dmabuf);
+
+ if (vde->domain)
+ *addrp = iova_dma_addr(&vde->iova, entry->iova);
+ else
+ *addrp = sg_dma_address(entry->sgt->sgl);
+
+ goto ref;
+ }
+
+ attachment = dma_buf_attach(dmabuf, dev);
+ if (IS_ERR(attachment)) {
+ dev_err(dev, "Failed to attach dmabuf\n");
+ err = PTR_ERR(attachment);
+ goto err_unlock;
+ }
+
+ sgt = dma_buf_map_attachment(attachment, dma_dir);
+ if (IS_ERR(sgt)) {
+ dev_err(dev, "Failed to get dmabufs sg_table\n");
+ err = PTR_ERR(sgt);
+ goto err_detach;
+ }
+
+ if (!vde->domain && sgt->nents > 1) {
+ dev_err(dev, "Sparse DMA region is unsupported, please enable IOMMU\n");
+ err = -EINVAL;
+ goto err_unmap;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ err = -ENOMEM;
+ goto err_unmap;
+ }
+
+ if (vde->domain) {
+ err = tegra_vde_iommu_map(vde, sgt, &iova, dmabuf->size);
+ if (err)
+ goto err_free;
+
+ *addrp = iova_dma_addr(&vde->iova, iova);
+ } else {
+ *addrp = sg_dma_address(sgt->sgl);
+ iova = NULL;
+ }
+
+ INIT_DELAYED_WORK(&entry->dwork, tegra_vde_delayed_unmap);
+ list_add(&entry->list, &vde->map_list);
+
+ entry->dma_dir = dma_dir;
+ entry->iova = iova;
+ entry->vde = vde;
+ entry->sgt = sgt;
+ entry->a = attachment;
+ref:
+ entry->refcnt++;
+
+ *ap = entry->a;
+
+ mutex_unlock(&vde->map_lock);
+
+ return 0;
+
+err_free:
+ kfree(entry);
+err_unmap:
+ dma_buf_unmap_attachment(attachment, sgt, dma_dir);
+err_detach:
+ dma_buf_detach(dmabuf, attachment);
+err_unlock:
+ mutex_unlock(&vde->map_lock);
+
+ return err;
+}
+
+void tegra_vde_dmabuf_cache_unmap(struct tegra_vde *vde,
+ struct dma_buf_attachment *a,
+ bool release)
+{
+ struct tegra_vde_cache_entry *entry;
+
+ mutex_lock(&vde->map_lock);
+
+ list_for_each_entry(entry, &vde->map_list, list) {
+ if (entry->a != a)
+ continue;
+
+ WARN_ON_ONCE(!entry->refcnt);
+
+ if (--entry->refcnt == 0) {
+ if (release)
+ tegra_vde_release_entry(entry);
+ else
+ schedule_delayed_work(&entry->dwork, 5 * HZ);
+ }
+ break;
+ }
+
+ mutex_unlock(&vde->map_lock);
+}
+
+void tegra_vde_dmabuf_cache_unmap_sync(struct tegra_vde *vde)
+{
+ struct tegra_vde_cache_entry *entry, *tmp;
+
+ mutex_lock(&vde->map_lock);
+
+ list_for_each_entry_safe(entry, tmp, &vde->map_list, list) {
+ if (entry->refcnt)
+ continue;
+
+ if (!cancel_delayed_work(&entry->dwork))
+ continue;
+
+ tegra_vde_release_entry(entry);
+ }
+
+ mutex_unlock(&vde->map_lock);
+}
+
+void tegra_vde_dmabuf_cache_unmap_all(struct tegra_vde *vde)
+{
+ struct tegra_vde_cache_entry *entry, *tmp;
+
+ mutex_lock(&vde->map_lock);
+
+ while (!list_empty(&vde->map_list)) {
+ list_for_each_entry_safe(entry, tmp, &vde->map_list, list) {
+ if (!cancel_delayed_work(&entry->dwork))
+ continue;
+
+ tegra_vde_release_entry(entry);
+ }
+
+ mutex_unlock(&vde->map_lock);
+ schedule();
+ mutex_lock(&vde->map_lock);
+ }
+
+ mutex_unlock(&vde->map_lock);
+}
diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c
new file mode 100644
index 000000000000..d8e5534e80c8
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/h264.c
@@ -0,0 +1,946 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NVIDIA Tegra Video decoder driver
+ *
+ * Copyright (C) 2016-2022 Dmitry Osipenko <digetx@gmail.com>
+ *
+ */
+
+#include <linux/iopoll.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <media/v4l2-h264.h>
+
+#include "trace.h"
+#include "vde.h"
+
+#define FLAG_B_FRAME 0x1
+#define FLAG_REFERENCE 0x2
+
+struct tegra_vde_h264_frame {
+ unsigned int frame_num;
+ unsigned int flags;
+};
+
+struct tegra_vde_h264_decoder_ctx {
+ unsigned int dpb_frames_nb;
+ unsigned int dpb_ref_frames_with_earlier_poc_nb;
+ unsigned int baseline_profile;
+ unsigned int level_idc;
+ unsigned int log2_max_pic_order_cnt_lsb;
+ unsigned int log2_max_frame_num;
+ unsigned int pic_order_cnt_type;
+ unsigned int direct_8x8_inference_flag;
+ unsigned int pic_width_in_mbs;
+ unsigned int pic_height_in_mbs;
+ unsigned int pic_init_qp;
+ unsigned int deblocking_filter_control_present_flag;
+ unsigned int constrained_intra_pred_flag;
+ unsigned int chroma_qp_index_offset;
+ unsigned int pic_order_present_flag;
+ unsigned int num_ref_idx_l0_active_minus1;
+ unsigned int num_ref_idx_l1_active_minus1;
+};
+
+struct h264_reflists {
+ u8 p[V4L2_H264_NUM_DPB_ENTRIES];
+ u8 b0[V4L2_H264_NUM_DPB_ENTRIES];
+ u8 b1[V4L2_H264_NUM_DPB_ENTRIES];
+};
+
+static int tegra_vde_wait_mbe(struct tegra_vde *vde)
+{
+ u32 tmp;
+
+ return readl_relaxed_poll_timeout(vde->mbe + 0x8C, tmp,
+ tmp >= 0x10, 1, 100);
+}
+
+static int tegra_vde_setup_mbe_frame_idx(struct tegra_vde *vde,
+ unsigned int refs_nb,
+ bool setup_refs)
+{
+ u32 value, frame_idx_enb_mask = 0;
+ unsigned int frame_idx;
+ unsigned int idx;
+ int err;
+
+ tegra_vde_writel(vde, 0xD0000000 | (0 << 23), vde->mbe, 0x80);
+ tegra_vde_writel(vde, 0xD0200000 | (0 << 23), vde->mbe, 0x80);
+
+ err = tegra_vde_wait_mbe(vde);
+ if (err)
+ return err;
+
+ if (!setup_refs)
+ return 0;
+
+ for (idx = 0, frame_idx = 1; idx < refs_nb; idx++, frame_idx++) {
+ tegra_vde_writel(vde, 0xD0000000 | (frame_idx << 23),
+ vde->mbe, 0x80);
+ tegra_vde_writel(vde, 0xD0200000 | (frame_idx << 23),
+ vde->mbe, 0x80);
+
+ frame_idx_enb_mask |= frame_idx << (6 * (idx % 4));
+
+ if (idx % 4 == 3 || idx == refs_nb - 1) {
+ value = 0xC0000000;
+ value |= (idx >> 2) << 24;
+ value |= frame_idx_enb_mask;
+
+ tegra_vde_writel(vde, value, vde->mbe, 0x80);
+
+ err = tegra_vde_wait_mbe(vde);
+ if (err)
+ return err;
+
+ frame_idx_enb_mask = 0;
+ }
+ }
+
+ return 0;
+}
+
+static void tegra_vde_mbe_set_0xa_reg(struct tegra_vde *vde, int reg, u32 val)
+{
+ tegra_vde_writel(vde, 0xA0000000 | (reg << 24) | (val & 0xFFFF),
+ vde->mbe, 0x80);
+ tegra_vde_writel(vde, 0xA0000000 | ((reg + 1) << 24) | (val >> 16),
+ vde->mbe, 0x80);
+}
+
+static int tegra_vde_wait_bsev(struct tegra_vde *vde, bool wait_dma)
+{
+ struct device *dev = vde->dev;
+ u32 value;
+ int err;
+
+ err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value,
+ !(value & BIT(2)), 1, 100);
+ if (err) {
+ dev_err(dev, "BSEV unknown bit timeout\n");
+ return err;
+ }
+
+ err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value,
+ (value & BSE_ICMDQUE_EMPTY), 1, 100);
+ if (err) {
+ dev_err(dev, "BSEV ICMDQUE flush timeout\n");
+ return err;
+ }
+
+ if (!wait_dma)
+ return 0;
+
+ err = readl_relaxed_poll_timeout(vde->bsev + INTR_STATUS, value,
+ !(value & BSE_DMA_BUSY), 1, 1000);
+ if (err) {
+ dev_err(dev, "BSEV DMA timeout\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int tegra_vde_push_to_bsev_icmdqueue(struct tegra_vde *vde,
+ u32 value, bool wait_dma)
+{
+ tegra_vde_writel(vde, value, vde->bsev, ICMDQUE_WR);
+
+ return tegra_vde_wait_bsev(vde, wait_dma);
+}
+
+static void tegra_vde_setup_frameid(struct tegra_vde *vde,
+ struct tegra_video_frame *frame,
+ unsigned int frameid,
+ u32 mbs_width, u32 mbs_height)
+{
+ u32 y_addr = frame ? frame->y_addr : 0x6CDEAD00;
+ u32 cb_addr = frame ? frame->cb_addr : 0x6CDEAD00;
+ u32 cr_addr = frame ? frame->cr_addr : 0x6CDEAD00;
+ u32 value1 = frame ? ((frame->luma_atoms_pitch << 16) | mbs_height) : 0;
+ u32 value2 = frame ? ((frame->chroma_atoms_pitch << 6) | 1) : 0;
+
+ tegra_vde_writel(vde, y_addr >> 8, vde->frameid, 0x000 + frameid * 4);
+ tegra_vde_writel(vde, cb_addr >> 8, vde->frameid, 0x100 + frameid * 4);
+ tegra_vde_writel(vde, cr_addr >> 8, vde->frameid, 0x180 + frameid * 4);
+ tegra_vde_writel(vde, value1, vde->frameid, 0x080 + frameid * 4);
+ tegra_vde_writel(vde, value2, vde->frameid, 0x280 + frameid * 4);
+}
+
+static void tegra_setup_frameidx(struct tegra_vde *vde,
+ struct tegra_video_frame *frames,
+ unsigned int frames_nb,
+ u32 mbs_width, u32 mbs_height)
+{
+ unsigned int idx;
+
+ for (idx = 0; idx < frames_nb; idx++)
+ tegra_vde_setup_frameid(vde, &frames[idx], idx,
+ mbs_width, mbs_height);
+
+ for (; idx < 17; idx++)
+ tegra_vde_setup_frameid(vde, NULL, idx, 0, 0);
+}
+
+static void tegra_vde_setup_iram_entry(struct tegra_vde *vde,
+ unsigned int table,
+ unsigned int row,
+ u32 value1, u32 value2)
+{
+ u32 *iram_tables = vde->iram;
+
+ trace_vde_setup_iram_entry(table, row, value1, value2);
+
+ iram_tables[0x20 * table + row * 2 + 0] = value1;
+ iram_tables[0x20 * table + row * 2 + 1] = value2;
+}
+
+static void tegra_vde_setup_iram_tables(struct tegra_vde *vde,
+ struct tegra_video_frame *dpb_frames,
+ unsigned int ref_frames_nb,
+ unsigned int with_earlier_poc_nb)
+{
+ struct tegra_video_frame *frame;
+ int with_later_poc_nb;
+ u32 value, aux_addr;
+ unsigned int i, k;
+
+ trace_vde_ref_l0(dpb_frames[0].frame_num);
+
+ for (i = 0; i < 16; i++) {
+ if (i < ref_frames_nb) {
+ frame = &dpb_frames[i + 1];
+
+ aux_addr = frame->aux_addr;
+
+ value = (i + 1) << 26;
+ value |= !(frame->flags & FLAG_B_FRAME) << 25;
+ value |= 1 << 24;
+ value |= frame->frame_num;
+ } else {
+ aux_addr = 0x6ADEAD00;
+ value = 0x3f;
+ }
+
+ tegra_vde_setup_iram_entry(vde, 0, i, value, aux_addr);
+ tegra_vde_setup_iram_entry(vde, 1, i, value, aux_addr);
+ tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr);
+ tegra_vde_setup_iram_entry(vde, 3, i, value, aux_addr);
+ }
+
+ if (!(dpb_frames[0].flags & FLAG_B_FRAME))
+ return;
+
+ if (with_earlier_poc_nb >= ref_frames_nb)
+ return;
+
+ with_later_poc_nb = ref_frames_nb - with_earlier_poc_nb;
+
+ trace_vde_ref_l1(with_later_poc_nb, with_earlier_poc_nb);
+
+ for (i = 0, k = with_earlier_poc_nb; i < with_later_poc_nb; i++, k++) {
+ frame = &dpb_frames[k + 1];
+
+ aux_addr = frame->aux_addr;
+
+ value = (k + 1) << 26;
+ value |= !(frame->flags & FLAG_B_FRAME) << 25;
+ value |= 1 << 24;
+ value |= frame->frame_num;
+
+ tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr);
+ }
+
+ for (k = 0; i < ref_frames_nb; i++, k++) {
+ frame = &dpb_frames[k + 1];
+
+ aux_addr = frame->aux_addr;
+
+ value = (k + 1) << 26;
+ value |= !(frame->flags & FLAG_B_FRAME) << 25;
+ value |= 1 << 24;
+ value |= frame->frame_num;
+
+ tegra_vde_setup_iram_entry(vde, 2, i, value, aux_addr);
+ }
+}
+
+static int tegra_vde_setup_hw_context(struct tegra_vde *vde,
+ struct tegra_vde_h264_decoder_ctx *ctx,
+ struct tegra_video_frame *dpb_frames,
+ dma_addr_t bitstream_data_addr,
+ size_t bitstream_data_size,
+ unsigned int macroblocks_nb)
+{
+ struct device *dev = vde->dev;
+ u32 value;
+ int err;
+
+ tegra_vde_set_bits(vde, 0x000A, vde->sxe, 0xF0);
+ tegra_vde_set_bits(vde, 0x000B, vde->bsev, CMDQUE_CONTROL);
+ tegra_vde_set_bits(vde, 0x8002, vde->mbe, 0x50);
+ tegra_vde_set_bits(vde, 0x000A, vde->mbe, 0xA0);
+ tegra_vde_set_bits(vde, 0x000A, vde->ppe, 0x14);
+ tegra_vde_set_bits(vde, 0x000A, vde->ppe, 0x28);
+ tegra_vde_set_bits(vde, 0x0A00, vde->mce, 0x08);
+ tegra_vde_set_bits(vde, 0x000A, vde->tfe, 0x00);
+ tegra_vde_set_bits(vde, 0x0005, vde->vdma, 0x04);
+
+ tegra_vde_writel(vde, 0x00000000, vde->vdma, 0x1C);
+ tegra_vde_writel(vde, 0x00000000, vde->vdma, 0x00);
+ tegra_vde_writel(vde, 0x00000007, vde->vdma, 0x04);
+ tegra_vde_writel(vde, 0x00000007, vde->frameid, 0x200);
+ tegra_vde_writel(vde, 0x00000005, vde->tfe, 0x04);
+ tegra_vde_writel(vde, 0x00000000, vde->mbe, 0x84);
+ tegra_vde_writel(vde, 0x00000010, vde->sxe, 0x08);
+ tegra_vde_writel(vde, 0x00000150, vde->sxe, 0x54);
+ tegra_vde_writel(vde, 0x0000054C, vde->sxe, 0x58);
+ tegra_vde_writel(vde, 0x00000E34, vde->sxe, 0x5C);
+ tegra_vde_writel(vde, 0x063C063C, vde->mce, 0x10);
+ tegra_vde_writel(vde, 0x0003FC00, vde->bsev, INTR_STATUS);
+ tegra_vde_writel(vde, 0x0000150D, vde->bsev, BSE_CONFIG);
+ tegra_vde_writel(vde, 0x00000100, vde->bsev, BSE_INT_ENB);
+ tegra_vde_writel(vde, 0x00000000, vde->bsev, 0x98);
+ tegra_vde_writel(vde, 0x00000060, vde->bsev, 0x9C);
+
+ memset(vde->iram + 128, 0, macroblocks_nb / 2);
+
+ tegra_setup_frameidx(vde, dpb_frames, ctx->dpb_frames_nb,
+ ctx->pic_width_in_mbs, ctx->pic_height_in_mbs);
+
+ tegra_vde_setup_iram_tables(vde, dpb_frames,
+ ctx->dpb_frames_nb - 1,
+ ctx->dpb_ref_frames_with_earlier_poc_nb);
+
+ /*
+ * The IRAM mapping is write-combine, ensure that CPU buffers have
+ * been flushed at this point.
+ */
+ wmb();
+
+ tegra_vde_writel(vde, 0x00000000, vde->bsev, 0x8C);
+ tegra_vde_writel(vde, bitstream_data_addr + bitstream_data_size,
+ vde->bsev, 0x54);
+
+ vde->bitstream_data_addr = bitstream_data_addr;
+
+ value = ctx->pic_width_in_mbs << 11 | ctx->pic_height_in_mbs << 3;
+
+ tegra_vde_writel(vde, value, vde->bsev, 0x88);
+
+ err = tegra_vde_wait_bsev(vde, false);
+ if (err)
+ return err;
+
+ err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x800003FC, false);
+ if (err)
+ return err;
+
+ value = 0x01500000;
+ value |= ((vde->iram_lists_addr + 512) >> 2) & 0xFFFF;
+
+ err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true);
+ if (err)
+ return err;
+
+ err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x840F054C, false);
+ if (err)
+ return err;
+
+ err = tegra_vde_push_to_bsev_icmdqueue(vde, 0x80000080, false);
+ if (err)
+ return err;
+
+ value = 0x0E340000 | ((vde->iram_lists_addr >> 2) & 0xFFFF);
+
+ err = tegra_vde_push_to_bsev_icmdqueue(vde, value, true);
+ if (err)
+ return err;
+
+ value = 0x00800005;
+ value |= ctx->pic_width_in_mbs << 11;
+ value |= ctx->pic_height_in_mbs << 3;
+
+ tegra_vde_writel(vde, value, vde->sxe, 0x10);
+
+ value = !ctx->baseline_profile << 17;
+ value |= ctx->level_idc << 13;
+ value |= ctx->log2_max_pic_order_cnt_lsb << 7;
+ value |= ctx->pic_order_cnt_type << 5;
+ value |= ctx->log2_max_frame_num;
+
+ tegra_vde_writel(vde, value, vde->sxe, 0x40);
+
+ value = ctx->pic_init_qp << 25;
+ value |= !!(ctx->deblocking_filter_control_present_flag) << 2;
+ value |= !!ctx->pic_order_present_flag;
+
+ tegra_vde_writel(vde, value, vde->sxe, 0x44);
+
+ value = ctx->chroma_qp_index_offset;
+ value |= ctx->num_ref_idx_l0_active_minus1 << 5;
+ value |= ctx->num_ref_idx_l1_active_minus1 << 10;
+ value |= !!ctx->constrained_intra_pred_flag << 15;
+
+ tegra_vde_writel(vde, value, vde->sxe, 0x48);
+
+ value = 0x0C000000;
+ value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 24;
+
+ tegra_vde_writel(vde, value, vde->sxe, 0x4C);
+
+ value = 0x03800000;
+ value |= bitstream_data_size & GENMASK(19, 15);
+
+ tegra_vde_writel(vde, value, vde->sxe, 0x68);
+
+ tegra_vde_writel(vde, bitstream_data_addr, vde->sxe, 0x6C);
+
+ if (vde->soc->supports_ref_pic_marking)
+ tegra_vde_writel(vde, vde->secure_bo->dma_addr, vde->sxe, 0x7c);
+
+ value = 0x10000005;
+ value |= ctx->pic_width_in_mbs << 11;
+ value |= ctx->pic_height_in_mbs << 3;
+
+ tegra_vde_writel(vde, value, vde->mbe, 0x80);
+
+ value = 0x26800000;
+ value |= ctx->level_idc << 4;
+ value |= !ctx->baseline_profile << 1;
+ value |= !!ctx->direct_8x8_inference_flag;
+
+ tegra_vde_writel(vde, value, vde->mbe, 0x80);
+
+ tegra_vde_writel(vde, 0xF4000001, vde->mbe, 0x80);
+ tegra_vde_writel(vde, 0x20000000, vde->mbe, 0x80);
+ tegra_vde_writel(vde, 0xF4000101, vde->mbe, 0x80);
+
+ value = 0x20000000;
+ value |= ctx->chroma_qp_index_offset << 8;
+
+ tegra_vde_writel(vde, value, vde->mbe, 0x80);
+
+ err = tegra_vde_setup_mbe_frame_idx(vde,
+ ctx->dpb_frames_nb - 1,
+ ctx->pic_order_cnt_type == 0);
+ if (err) {
+ dev_err(dev, "MBE frames setup failed %d\n", err);
+ return err;
+ }
+
+ tegra_vde_mbe_set_0xa_reg(vde, 0, 0x000009FC);
+ tegra_vde_mbe_set_0xa_reg(vde, 2, 0x61DEAD00);
+ tegra_vde_mbe_set_0xa_reg(vde, 4, 0x62DEAD00);
+ tegra_vde_mbe_set_0xa_reg(vde, 6, 0x63DEAD00);
+ tegra_vde_mbe_set_0xa_reg(vde, 8, dpb_frames[0].aux_addr);
+
+ value = 0xFC000000;
+ value |= !!(dpb_frames[0].flags & FLAG_B_FRAME) << 2;
+
+ if (!ctx->baseline_profile)
+ value |= !!(dpb_frames[0].flags & FLAG_REFERENCE) << 1;
+
+ tegra_vde_writel(vde, value, vde->mbe, 0x80);
+
+ err = tegra_vde_wait_mbe(vde);
+ if (err) {
+ dev_err(dev, "MBE programming failed %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void tegra_vde_decode_frame(struct tegra_vde *vde,
+ unsigned int macroblocks_nb)
+{
+ reinit_completion(&vde->decode_completion);
+
+ tegra_vde_writel(vde, 0x00000001, vde->bsev, 0x8C);
+ tegra_vde_writel(vde, 0x20000000 | (macroblocks_nb - 1),
+ vde->sxe, 0x00);
+}
+
+static int tegra_vde_validate_h264_ctx(struct device *dev,
+ struct tegra_vde_h264_decoder_ctx *ctx)
+{
+ if (ctx->dpb_frames_nb == 0 || ctx->dpb_frames_nb > 17) {
+ dev_err(dev, "Bad DPB size %u\n", ctx->dpb_frames_nb);
+ return -EINVAL;
+ }
+
+ if (ctx->level_idc > 15) {
+ dev_err(dev, "Bad level value %u\n", ctx->level_idc);
+ return -EINVAL;
+ }
+
+ if (ctx->pic_init_qp > 52) {
+ dev_err(dev, "Bad pic_init_qp value %u\n", ctx->pic_init_qp);
+ return -EINVAL;
+ }
+
+ if (ctx->log2_max_pic_order_cnt_lsb > 16) {
+ dev_err(dev, "Bad log2_max_pic_order_cnt_lsb value %u\n",
+ ctx->log2_max_pic_order_cnt_lsb);
+ return -EINVAL;
+ }
+
+ if (ctx->log2_max_frame_num > 16) {
+ dev_err(dev, "Bad log2_max_frame_num value %u\n",
+ ctx->log2_max_frame_num);
+ return -EINVAL;
+ }
+
+ if (ctx->chroma_qp_index_offset > 31) {
+ dev_err(dev, "Bad chroma_qp_index_offset value %u\n",
+ ctx->chroma_qp_index_offset);
+ return -EINVAL;
+ }
+
+ if (ctx->pic_order_cnt_type > 2) {
+ dev_err(dev, "Bad pic_order_cnt_type value %u\n",
+ ctx->pic_order_cnt_type);
+ return -EINVAL;
+ }
+
+ if (ctx->num_ref_idx_l0_active_minus1 > 15) {
+ dev_err(dev, "Bad num_ref_idx_l0_active_minus1 value %u\n",
+ ctx->num_ref_idx_l0_active_minus1);
+ return -EINVAL;
+ }
+
+ if (ctx->num_ref_idx_l1_active_minus1 > 15) {
+ dev_err(dev, "Bad num_ref_idx_l1_active_minus1 value %u\n",
+ ctx->num_ref_idx_l1_active_minus1);
+ return -EINVAL;
+ }
+
+ if (!ctx->pic_width_in_mbs || ctx->pic_width_in_mbs > 127) {
+ dev_err(dev, "Bad pic_width_in_mbs value %u\n",
+ ctx->pic_width_in_mbs);
+ return -EINVAL;
+ }
+
+ if (!ctx->pic_height_in_mbs || ctx->pic_height_in_mbs > 127) {
+ dev_err(dev, "Bad pic_height_in_mbs value %u\n",
+ ctx->pic_height_in_mbs);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra_vde_decode_begin(struct tegra_vde *vde,
+ struct tegra_vde_h264_decoder_ctx *ctx,
+ struct tegra_video_frame *dpb_frames,
+ dma_addr_t bitstream_data_addr,
+ size_t bitstream_data_size)
+{
+ struct device *dev = vde->dev;
+ unsigned int macroblocks_nb;
+ int err;
+
+ err = mutex_lock_interruptible(&vde->lock);
+ if (err)
+ return err;
+
+ err = pm_runtime_resume_and_get(dev);
+ if (err < 0)
+ goto unlock;
+
+ /*
+ * We rely on the VDE registers reset value, otherwise VDE
+ * causes bus lockup.
+ */
+ err = reset_control_assert(vde->rst_mc);
+ if (err) {
+ dev_err(dev, "DEC start: Failed to assert MC reset: %d\n",
+ err);
+ goto put_runtime_pm;
+ }
+
+ err = reset_control_reset(vde->rst);
+ if (err) {
+ dev_err(dev, "DEC start: Failed to reset HW: %d\n", err);
+ goto put_runtime_pm;
+ }
+
+ err = reset_control_deassert(vde->rst_mc);
+ if (err) {
+ dev_err(dev, "DEC start: Failed to deassert MC reset: %d\n",
+ err);
+ goto put_runtime_pm;
+ }
+
+ macroblocks_nb = ctx->pic_width_in_mbs * ctx->pic_height_in_mbs;
+
+ err = tegra_vde_setup_hw_context(vde, ctx, dpb_frames,
+ bitstream_data_addr,
+ bitstream_data_size,
+ macroblocks_nb);
+ if (err)
+ goto put_runtime_pm;
+
+ tegra_vde_decode_frame(vde, macroblocks_nb);
+
+ return 0;
+
+put_runtime_pm:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+unlock:
+ mutex_unlock(&vde->lock);
+
+ return err;
+}
+
+static void tegra_vde_decode_abort(struct tegra_vde *vde)
+{
+ struct device *dev = vde->dev;
+ int err;
+
+ /*
+ * At first reset memory client to avoid resetting VDE HW in the
+ * middle of DMA which could result into memory corruption or hang
+ * the whole system.
+ */
+ err = reset_control_assert(vde->rst_mc);
+ if (err)
+ dev_err(dev, "DEC end: Failed to assert MC reset: %d\n", err);
+
+ err = reset_control_assert(vde->rst);
+ if (err)
+ dev_err(dev, "DEC end: Failed to assert HW reset: %d\n", err);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ mutex_unlock(&vde->lock);
+}
+
+static int tegra_vde_decode_end(struct tegra_vde *vde)
+{
+ unsigned int read_bytes, macroblocks_nb;
+ struct device *dev = vde->dev;
+ dma_addr_t bsev_ptr;
+ long timeout;
+ int ret;
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &vde->decode_completion, msecs_to_jiffies(1000));
+ if (timeout == 0) {
+ bsev_ptr = tegra_vde_readl(vde, vde->bsev, 0x10);
+ macroblocks_nb = tegra_vde_readl(vde, vde->sxe, 0xC8) & 0x1FFF;
+ read_bytes = bsev_ptr ? bsev_ptr - vde->bitstream_data_addr : 0;
+
+ dev_err(dev, "Decoding failed: read 0x%X bytes, %u macroblocks parsed\n",
+ read_bytes, macroblocks_nb);
+
+ ret = -EIO;
+ } else if (timeout < 0) {
+ ret = timeout;
+ } else {
+ ret = 0;
+ }
+
+ tegra_vde_decode_abort(vde);
+
+ return ret;
+}
+
+static struct vb2_buffer *get_ref_buf(struct tegra_ctx *ctx,
+ struct vb2_v4l2_buffer *dst,
+ unsigned int dpb_idx)
+{
+ const struct v4l2_h264_dpb_entry *dpb = ctx->h264.decode_params->dpb;
+ struct vb2_queue *cap_q = &ctx->fh.m2m_ctx->cap_q_ctx.q;
+ int buf_idx = -1;
+
+ if (dpb[dpb_idx].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)
+ buf_idx = vb2_find_timestamp(cap_q,
+ dpb[dpb_idx].reference_ts, 0);
+
+ /*
+ * If a DPB entry is unused or invalid, address of current destination
+ * buffer is returned.
+ */
+ if (buf_idx < 0)
+ return &dst->vb2_buf;
+
+ return vb2_get_buffer(cap_q, buf_idx);
+}
+
+static int tegra_vde_validate_vb_size(struct tegra_ctx *ctx,
+ struct vb2_buffer *vb,
+ unsigned int plane_id,
+ size_t min_size)
+{
+ u64 offset = vb->planes[plane_id].data_offset;
+ struct device *dev = ctx->vde->dev;
+
+ if (offset + min_size > vb2_plane_size(vb, plane_id)) {
+ dev_err(dev, "Too small plane[%u] size %lu @0x%llX, should be at least %zu\n",
+ plane_id, vb2_plane_size(vb, plane_id), offset, min_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra_vde_h264_setup_frame(struct tegra_ctx *ctx,
+ struct tegra_vde_h264_decoder_ctx *h264,
+ struct v4l2_h264_reflist_builder *b,
+ struct vb2_buffer *vb,
+ unsigned int ref_id,
+ unsigned int id)
+{
+ struct v4l2_pix_format_mplane *pixfmt = &ctx->decoded_fmt.fmt.pix_mp;
+ struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb);
+ struct tegra_ctx_h264 *h = &ctx->h264;
+ struct tegra_vde *vde = ctx->vde;
+ struct device *dev = vde->dev;
+ unsigned int cstride, lstride;
+ unsigned int flags = 0;
+ size_t lsize, csize;
+ int err, frame_num;
+
+ lsize = h264->pic_width_in_mbs * 16 * h264->pic_height_in_mbs * 16;
+ csize = h264->pic_width_in_mbs * 8 * h264->pic_height_in_mbs * 8;
+ lstride = pixfmt->plane_fmt[0].bytesperline;
+ cstride = pixfmt->plane_fmt[1].bytesperline;
+
+ err = tegra_vde_validate_vb_size(ctx, vb, 0, lsize);
+ if (err)
+ return err;
+
+ err = tegra_vde_validate_vb_size(ctx, vb, 1, csize);
+ if (err)
+ return err;
+
+ err = tegra_vde_validate_vb_size(ctx, vb, 2, csize);
+ if (err)
+ return err;
+
+ if (!tb->aux || tb->aux->size < csize) {
+ dev_err(dev, "Too small aux size %zd, should be at least %zu\n",
+ tb->aux ? tb->aux->size : -1, csize);
+ return -EINVAL;
+ }
+
+ if (id == 0) {
+ frame_num = h->decode_params->frame_num;
+
+ if (h->decode_params->nal_ref_idc)
+ flags |= FLAG_REFERENCE;
+ } else {
+ frame_num = b->refs[ref_id].frame_num;
+ }
+
+ if (tb->b_frame)
+ flags |= FLAG_B_FRAME;
+
+ vde->frames[id].flags = flags;
+ vde->frames[id].y_addr = tb->dma_addr[0];
+ vde->frames[id].cb_addr = tb->dma_addr[1];
+ vde->frames[id].cr_addr = tb->dma_addr[2];
+ vde->frames[id].aux_addr = tb->aux->dma_addr;
+ vde->frames[id].frame_num = frame_num & 0x7fffff;
+ vde->frames[id].luma_atoms_pitch = lstride / VDE_ATOM;
+ vde->frames[id].chroma_atoms_pitch = cstride / VDE_ATOM;
+
+ return 0;
+}
+
+static int tegra_vde_h264_setup_frames(struct tegra_ctx *ctx,
+ struct tegra_vde_h264_decoder_ctx *h264)
+{
+ struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ struct vb2_v4l2_buffer *dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ const struct v4l2_h264_dpb_entry *dpb = ctx->h264.decode_params->dpb;
+ struct tegra_m2m_buffer *tb = vb_to_tegra_buf(&dst->vb2_buf);
+ struct tegra_ctx_h264 *h = &ctx->h264;
+ struct v4l2_h264_reflist_builder b;
+ struct h264_reflists reflists;
+ struct vb2_buffer *ref;
+ unsigned int i;
+ u8 *dpb_id;
+ int err;
+
+ /*
+ * Tegra hardware requires information about frame's type, assuming
+ * that frame consists of the same type slices. Userspace must tag
+ * frame's type appropriately.
+ *
+ * Decoding of a non-uniform frames isn't supported by hardware and
+ * require software preprocessing that we don't implement. Decoding
+ * is expected to fail in this case. Such video streams are rare in
+ * practice, so not a big deal.
+ *
+ * If userspace doesn't tell us frame's type, then we will try decode
+ * as-is.
+ */
+ v4l2_m2m_buf_copy_metadata(src, dst, true);
+
+ if (h->decode_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME)
+ tb->b_frame = true;
+ else
+ tb->b_frame = false;
+
+ err = tegra_vde_h264_setup_frame(ctx, h264, NULL, &dst->vb2_buf, 0,
+ h264->dpb_frames_nb++);
+ if (err)
+ return err;
+
+ if (!(h->decode_params->flags & (V4L2_H264_DECODE_PARAM_FLAG_PFRAME |
+ V4L2_H264_DECODE_PARAM_FLAG_BFRAME)))
+ return 0;
+
+ v4l2_h264_init_reflist_builder(&b, h->decode_params, h->sps, dpb);
+
+ if (h->decode_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BFRAME) {
+ v4l2_h264_build_b_ref_lists(&b, reflists.b0, reflists.b1);
+ dpb_id = reflists.b0;
+ } else {
+ v4l2_h264_build_p_ref_list(&b, reflists.p);
+ dpb_id = reflists.p;
+ }
+
+ for (i = 0; i < b.num_valid; i++) {
+ ref = get_ref_buf(ctx, dst, dpb_id[i]);
+
+ err = tegra_vde_h264_setup_frame(ctx, h264, &b, ref, dpb_id[i],
+ h264->dpb_frames_nb++);
+ if (err)
+ return err;
+
+ if (b.refs[dpb_id[i]].pic_order_count < b.cur_pic_order_count)
+ h264->dpb_ref_frames_with_earlier_poc_nb++;
+ }
+
+ return 0;
+}
+
+static unsigned int to_tegra_vde_h264_level_idc(unsigned int level_idc)
+{
+ switch (level_idc) {
+ case 11:
+ return 2;
+ case 12:
+ return 3;
+ case 13:
+ return 4;
+ case 20:
+ return 5;
+ case 21:
+ return 6;
+ case 22:
+ return 7;
+ case 30:
+ return 8;
+ case 31:
+ return 9;
+ case 32:
+ return 10;
+ case 40:
+ return 11;
+ case 41:
+ return 12;
+ case 42:
+ return 13;
+ case 50:
+ return 14;
+ default:
+ break;
+ }
+
+ return 15;
+}
+
+static int tegra_vde_h264_setup_context(struct tegra_ctx *ctx,
+ struct tegra_vde_h264_decoder_ctx *h264)
+{
+ struct tegra_ctx_h264 *h = &ctx->h264;
+ struct tegra_vde *vde = ctx->vde;
+ struct device *dev = vde->dev;
+ int err;
+
+ memset(h264, 0, sizeof(*h264));
+ memset(vde->frames, 0, sizeof(vde->frames));
+
+ tegra_vde_prepare_control_data(ctx, V4L2_CID_STATELESS_H264_DECODE_PARAMS);
+ tegra_vde_prepare_control_data(ctx, V4L2_CID_STATELESS_H264_SPS);
+ tegra_vde_prepare_control_data(ctx, V4L2_CID_STATELESS_H264_PPS);
+
+ /* CABAC unsupported by hardware, requires software preprocessing */
+ if (h->pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE)
+ return -EOPNOTSUPP;
+
+ if (h->sps->profile_idc == 66)
+ h264->baseline_profile = 1;
+
+ if (h->sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE)
+ h264->direct_8x8_inference_flag = 1;
+
+ if (h->pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED)
+ h264->constrained_intra_pred_flag = 1;
+
+ if (h->pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT)
+ h264->deblocking_filter_control_present_flag = 1;
+
+ if (h->pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT)
+ h264->pic_order_present_flag = 1;
+
+ h264->level_idc = to_tegra_vde_h264_level_idc(h->sps->level_idc);
+ h264->log2_max_pic_order_cnt_lsb = h->sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
+ h264->log2_max_frame_num = h->sps->log2_max_frame_num_minus4 + 4;
+ h264->pic_order_cnt_type = h->sps->pic_order_cnt_type;
+ h264->pic_width_in_mbs = h->sps->pic_width_in_mbs_minus1 + 1;
+ h264->pic_height_in_mbs = h->sps->pic_height_in_map_units_minus1 + 1;
+
+ h264->num_ref_idx_l0_active_minus1 = h->pps->num_ref_idx_l0_default_active_minus1;
+ h264->num_ref_idx_l1_active_minus1 = h->pps->num_ref_idx_l1_default_active_minus1;
+ h264->chroma_qp_index_offset = h->pps->chroma_qp_index_offset & 0x1f;
+ h264->pic_init_qp = h->pps->pic_init_qp_minus26 + 26;
+
+ err = tegra_vde_h264_setup_frames(ctx, h264);
+ if (err)
+ return err;
+
+ err = tegra_vde_validate_h264_ctx(dev, h264);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int tegra_vde_h264_decode_run(struct tegra_ctx *ctx)
+{
+ struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ struct tegra_m2m_buffer *bitstream = vb_to_tegra_buf(&src->vb2_buf);
+ size_t bitstream_size = vb2_get_plane_payload(&src->vb2_buf, 0);
+ struct tegra_vde_h264_decoder_ctx h264;
+ struct tegra_vde *vde = ctx->vde;
+ int err;
+
+ err = tegra_vde_h264_setup_context(ctx, &h264);
+ if (err)
+ return err;
+
+ err = tegra_vde_decode_begin(vde, &h264, vde->frames,
+ bitstream->dma_addr[0],
+ bitstream_size);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+int tegra_vde_h264_decode_wait(struct tegra_ctx *ctx)
+{
+ return tegra_vde_decode_end(ctx->vde);
+}
diff --git a/drivers/media/platform/nvidia/tegra-vde/iommu.c b/drivers/media/platform/nvidia/tegra-vde/iommu.c
new file mode 100644
index 000000000000..5521ed3e465f
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/iommu.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NVIDIA Tegra Video decoder driver
+ *
+ * Copyright (C) 2016-2019 GRATE-DRIVER project
+ */
+
+#include <linux/iommu.h>
+#include <linux/iova.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
+#include <asm/dma-iommu.h>
+#endif
+
+#include "vde.h"
+
+int tegra_vde_iommu_map(struct tegra_vde *vde,
+ struct sg_table *sgt,
+ struct iova **iovap,
+ size_t size)
+{
+ struct iova *iova;
+ unsigned long shift;
+ unsigned long end;
+ dma_addr_t addr;
+
+ end = vde->domain->geometry.aperture_end;
+ size = iova_align(&vde->iova, size);
+ shift = iova_shift(&vde->iova);
+
+ iova = alloc_iova(&vde->iova, size >> shift, end >> shift, true);
+ if (!iova)
+ return -ENOMEM;
+
+ addr = iova_dma_addr(&vde->iova, iova);
+
+ size = iommu_map_sgtable(vde->domain, addr, sgt,
+ IOMMU_READ | IOMMU_WRITE);
+ if (!size) {
+ __free_iova(&vde->iova, iova);
+ return -ENXIO;
+ }
+
+ *iovap = iova;
+
+ return 0;
+}
+
+void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova)
+{
+ unsigned long shift = iova_shift(&vde->iova);
+ unsigned long size = iova_size(iova) << shift;
+ dma_addr_t addr = iova_dma_addr(&vde->iova, iova);
+
+ iommu_unmap(vde->domain, addr, size);
+ __free_iova(&vde->iova, iova);
+}
+
+int tegra_vde_iommu_init(struct tegra_vde *vde)
+{
+ struct device *dev = vde->dev;
+ struct iova *iova;
+ unsigned long order;
+ unsigned long shift;
+ int err;
+
+ vde->group = iommu_group_get(dev);
+ if (!vde->group)
+ return 0;
+
+#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU)
+ if (dev->archdata.mapping) {
+ struct dma_iommu_mapping *mapping = to_dma_iommu_mapping(dev);
+
+ arm_iommu_detach_device(dev);
+ arm_iommu_release_mapping(mapping);
+ }
+#endif
+ vde->domain = iommu_domain_alloc(&platform_bus_type);
+ if (!vde->domain) {
+ err = -ENOMEM;
+ goto put_group;
+ }
+
+ err = iova_cache_get();
+ if (err)
+ goto free_domain;
+
+ order = __ffs(vde->domain->pgsize_bitmap);
+ init_iova_domain(&vde->iova, 1UL << order, 0);
+
+ err = iommu_attach_group(vde->domain, vde->group);
+ if (err)
+ goto put_iova;
+
+ /*
+ * We're using some static addresses that are not accessible by VDE
+ * to trap invalid memory accesses.
+ */
+ shift = iova_shift(&vde->iova);
+ iova = reserve_iova(&vde->iova, 0x60000000 >> shift,
+ 0x70000000 >> shift);
+ if (!iova) {
+ err = -ENOMEM;
+ goto detach_group;
+ }
+
+ vde->iova_resv_static_addresses = iova;
+
+ /*
+ * BSEV's end-address wraps around due to integer overflow during
+ * of hardware context preparation if IOVA is allocated at the end
+ * of address space and VDE can't handle that. Hence simply reserve
+ * the last page to avoid the problem.
+ */
+ iova = reserve_iova(&vde->iova, 0xffffffff >> shift,
+ (0xffffffff >> shift) + 1);
+ if (!iova) {
+ err = -ENOMEM;
+ goto unreserve_iova;
+ }
+
+ vde->iova_resv_last_page = iova;
+
+ return 0;
+
+unreserve_iova:
+ __free_iova(&vde->iova, vde->iova_resv_static_addresses);
+detach_group:
+ iommu_detach_group(vde->domain, vde->group);
+put_iova:
+ put_iova_domain(&vde->iova);
+ iova_cache_put();
+free_domain:
+ iommu_domain_free(vde->domain);
+put_group:
+ iommu_group_put(vde->group);
+
+ return err;
+}
+
+void tegra_vde_iommu_deinit(struct tegra_vde *vde)
+{
+ if (vde->domain) {
+ __free_iova(&vde->iova, vde->iova_resv_last_page);
+ __free_iova(&vde->iova, vde->iova_resv_static_addresses);
+ iommu_detach_group(vde->domain, vde->group);
+ put_iova_domain(&vde->iova);
+ iova_cache_put();
+ iommu_domain_free(vde->domain);
+ iommu_group_put(vde->group);
+
+ vde->domain = NULL;
+ }
+}
diff --git a/drivers/media/platform/nvidia/tegra-vde/trace.h b/drivers/media/platform/nvidia/tegra-vde/trace.h
new file mode 100644
index 000000000000..7853ab095ca4
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/trace.h
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM tegra_vde
+
+#if !defined(TEGRA_VDE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define TEGRA_VDE_TRACE_H
+
+#include <linux/tracepoint.h>
+
+#include "vde.h"
+
+DECLARE_EVENT_CLASS(register_access,
+ TP_PROTO(struct tegra_vde *vde, void __iomem *base,
+ u32 offset, u32 value),
+ TP_ARGS(vde, base, offset, value),
+ TP_STRUCT__entry(
+ __string(hw_name, tegra_vde_reg_base_name(vde, base))
+ __field(u32, offset)
+ __field(u32, value)
+ ),
+ TP_fast_assign(
+ __assign_str(hw_name, tegra_vde_reg_base_name(vde, base));
+ __entry->offset = offset;
+ __entry->value = value;
+ ),
+ TP_printk("%s:0x%03x 0x%08x", __get_str(hw_name), __entry->offset,
+ __entry->value)
+);
+
+DEFINE_EVENT(register_access, vde_writel,
+ TP_PROTO(struct tegra_vde *vde, void __iomem *base,
+ u32 offset, u32 value),
+ TP_ARGS(vde, base, offset, value));
+DEFINE_EVENT(register_access, vde_readl,
+ TP_PROTO(struct tegra_vde *vde, void __iomem *base,
+ u32 offset, u32 value),
+ TP_ARGS(vde, base, offset, value));
+
+TRACE_EVENT(vde_setup_iram_entry,
+ TP_PROTO(unsigned int table, unsigned int row, u32 value, u32 aux_addr),
+ TP_ARGS(table, row, value, aux_addr),
+ TP_STRUCT__entry(
+ __field(unsigned int, table)
+ __field(unsigned int, row)
+ __field(u32, value)
+ __field(u32, aux_addr)
+ ),
+ TP_fast_assign(
+ __entry->table = table;
+ __entry->row = row;
+ __entry->value = value;
+ __entry->aux_addr = aux_addr;
+ ),
+ TP_printk("[%u][%u] = { 0x%08x (flags = \"%s\", frame_num = %u); 0x%08x }",
+ __entry->table, __entry->row, __entry->value,
+ __print_flags(__entry->value, " ", { (1 << 25), "B" }),
+ __entry->value & 0x7FFFFF, __entry->aux_addr)
+);
+
+TRACE_EVENT(vde_ref_l0,
+ TP_PROTO(unsigned int frame_num),
+ TP_ARGS(frame_num),
+ TP_STRUCT__entry(
+ __field(unsigned int, frame_num)
+ ),
+ TP_fast_assign(
+ __entry->frame_num = frame_num;
+ ),
+ TP_printk("REF L0: DPB: Frame 0: frame_num = %u", __entry->frame_num)
+);
+
+TRACE_EVENT(vde_ref_l1,
+ TP_PROTO(unsigned int with_later_poc_nb,
+ unsigned int with_earlier_poc_nb),
+ TP_ARGS(with_later_poc_nb, with_earlier_poc_nb),
+ TP_STRUCT__entry(
+ __field(unsigned int, with_later_poc_nb)
+ __field(unsigned int, with_earlier_poc_nb)
+ ),
+ TP_fast_assign(
+ __entry->with_later_poc_nb = with_later_poc_nb;
+ __entry->with_earlier_poc_nb = with_earlier_poc_nb;
+ ),
+ TP_printk("REF L1: with_later_poc_nb %u, with_earlier_poc_nb %u",
+ __entry->with_later_poc_nb, __entry->with_earlier_poc_nb)
+);
+
+#endif /* TEGRA_VDE_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/media/platform/nvidia/tegra-vde
+#define TRACE_INCLUDE_FILE trace
+#include <trace/define_trace.h>
diff --git a/drivers/media/platform/nvidia/tegra-vde/v4l2.c b/drivers/media/platform/nvidia/tegra-vde/v4l2.c
new file mode 100644
index 000000000000..bd8c207d5b54
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/v4l2.c
@@ -0,0 +1,1018 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NVIDIA Tegra Video decoder driver
+ *
+ * Copyright (C) 2019-2022 Dmitry Osipenko <digetx@gmail.com>
+ *
+ * Based on Cedrus driver by Bootlin.
+ * Copyright (C) 2016 Florent Revest <florent.revest@free-electrons.com>
+ * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
+ *
+ * Based on Rockchip driver by Collabora.
+ * Copyright (C) 2019 Boris Brezillon <boris.brezillon@collabora.com>
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "vde.h"
+
+static const struct v4l2_ctrl_config ctrl_cfgs[] = {
+ { .id = V4L2_CID_STATELESS_H264_DECODE_PARAMS, },
+ { .id = V4L2_CID_STATELESS_H264_SPS, },
+ { .id = V4L2_CID_STATELESS_H264_PPS, },
+ {
+ .id = V4L2_CID_STATELESS_H264_DECODE_MODE,
+ .min = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .max = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ .def = V4L2_STATELESS_H264_DECODE_MODE_FRAME_BASED,
+ },
+ {
+ .id = V4L2_CID_STATELESS_H264_START_CODE,
+ .min = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .max = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ .def = V4L2_STATELESS_H264_START_CODE_ANNEX_B,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
+ .def = V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_5_1,
+ },
+};
+
+static inline struct tegra_ctx *fh_to_tegra_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct tegra_ctx, fh);
+}
+
+static void tegra_set_control_data(struct tegra_ctx *ctx, void *data, u32 id)
+{
+ switch (id) {
+ case V4L2_CID_STATELESS_H264_DECODE_PARAMS:
+ ctx->h264.decode_params = data;
+ break;
+ case V4L2_CID_STATELESS_H264_SPS:
+ ctx->h264.sps = data;
+ break;
+ case V4L2_CID_STATELESS_H264_PPS:
+ ctx->h264.pps = data;
+ break;
+ }
+}
+
+void tegra_vde_prepare_control_data(struct tegra_ctx *ctx, u32 id)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl_cfgs); i++) {
+ if (ctx->ctrls[i]->id == id) {
+ tegra_set_control_data(ctx, ctx->ctrls[i]->p_cur.p, id);
+ return;
+ }
+ }
+
+ tegra_set_control_data(ctx, NULL, id);
+}
+
+static int tegra_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbufs,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct tegra_ctx *ctx = vb2_get_drv_priv(vq);
+ struct v4l2_format *f;
+ unsigned int i;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ f = &ctx->coded_fmt;
+ else
+ f = &ctx->decoded_fmt;
+
+ if (*num_planes) {
+ if (*num_planes != f->fmt.pix_mp.num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+ if (sizes[i] < f->fmt.pix_mp.plane_fmt[i].sizeimage)
+ return -EINVAL;
+ }
+ } else {
+ *num_planes = f->fmt.pix_mp.num_planes;
+
+ for (i = 0; i < f->fmt.pix_mp.num_planes; i++)
+ sizes[i] = f->fmt.pix_mp.plane_fmt[i].sizeimage;
+ }
+
+ return 0;
+}
+
+static int tegra_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ vbuf->field = V4L2_FIELD_NONE;
+ return 0;
+}
+
+static void __tegra_buf_cleanup(struct vb2_buffer *vb, unsigned int i)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct tegra_ctx *ctx = vb2_get_drv_priv(vq);
+ struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb);
+
+ while (i--) {
+ if (tb->a[i]) {
+ tegra_vde_dmabuf_cache_unmap(ctx->vde, tb->a[i], true);
+ tb->a[i] = NULL;
+ }
+
+ if (tb->iova[i]) {
+ tegra_vde_iommu_unmap(ctx->vde, tb->iova[i]);
+ tb->iova[i] = NULL;
+ }
+ }
+
+ if (tb->aux) {
+ tegra_vde_free_bo(tb->aux);
+ tb->aux = NULL;
+ }
+}
+
+static int tegra_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct tegra_ctx *ctx = vb2_get_drv_priv(vq);
+ struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb);
+ struct tegra_vde *vde = ctx->vde;
+ enum dma_data_direction dma_dir;
+ struct sg_table *sgt;
+ unsigned int i;
+ int err;
+
+ if (V4L2_TYPE_IS_CAPTURE(vq->type) && vb->num_planes > 1) {
+ /*
+ * Tegra decoder writes auxiliary data for I/P frames.
+ * This data is needed for decoding of B frames.
+ */
+ err = tegra_vde_alloc_bo(vde, &tb->aux, DMA_FROM_DEVICE,
+ vb2_plane_size(vb, 1));
+ if (err)
+ return err;
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ dma_dir = DMA_TO_DEVICE;
+ else
+ dma_dir = DMA_FROM_DEVICE;
+
+ for (i = 0; i < vb->num_planes; i++) {
+ if (vq->memory == VB2_MEMORY_DMABUF) {
+ get_dma_buf(vb->planes[i].dbuf);
+
+ err = tegra_vde_dmabuf_cache_map(vde, vb->planes[i].dbuf,
+ dma_dir, &tb->a[i],
+ &tb->dma_base[i]);
+ if (err) {
+ dma_buf_put(vb->planes[i].dbuf);
+ goto cleanup;
+ }
+
+ continue;
+ }
+
+ if (vde->domain) {
+ sgt = vb2_dma_sg_plane_desc(vb, i);
+
+ err = tegra_vde_iommu_map(vde, sgt, &tb->iova[i],
+ vb2_plane_size(vb, i));
+ if (err)
+ goto cleanup;
+
+ tb->dma_base[i] = iova_dma_addr(&vde->iova, tb->iova[i]);
+ } else {
+ tb->dma_base[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+ }
+ }
+
+ return 0;
+
+cleanup:
+ __tegra_buf_cleanup(vb, i);
+
+ return err;
+}
+
+static void tegra_buf_cleanup(struct vb2_buffer *vb)
+{
+ __tegra_buf_cleanup(vb, vb->num_planes);
+}
+
+static int tegra_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct tegra_ctx *ctx = vb2_get_drv_priv(vq);
+ struct tegra_m2m_buffer *tb = vb_to_tegra_buf(vb);
+ size_t hw_align, hw_size, hw_payload, size, offset;
+ struct v4l2_pix_format_mplane *pixfmt;
+ unsigned int i;
+ void *vb_data;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ hw_align = BSEV_ALIGN;
+ pixfmt = &ctx->coded_fmt.fmt.pix_mp;
+ } else {
+ hw_align = FRAMEID_ALIGN;
+ pixfmt = &ctx->decoded_fmt.fmt.pix_mp;
+ }
+
+ for (i = 0; i < vb->num_planes; i++) {
+ offset = vb->planes[i].data_offset;
+
+ if (offset & (hw_align - 1))
+ return -EINVAL;
+
+ if (V4L2_TYPE_IS_CAPTURE(vq->type)) {
+ size = pixfmt->plane_fmt[i].sizeimage;
+ hw_payload = ALIGN(size, VDE_ATOM);
+ } else {
+ size = vb2_get_plane_payload(vb, i) - offset;
+ hw_payload = ALIGN(size + VDE_ATOM, SXE_BUFFER);
+ }
+
+ hw_size = offset + hw_payload;
+
+ if (vb2_plane_size(vb, i) < hw_size)
+ return -EINVAL;
+
+ vb2_set_plane_payload(vb, i, hw_payload);
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type)) {
+ vb_data = vb2_plane_vaddr(vb, i);
+
+ /*
+ * Hardware requires zero-padding of coded data.
+ * Otherwise it will fail to parse the trailing
+ * data and abort the decoding.
+ */
+ if (vb_data)
+ memset(vb_data + offset + size, 0,
+ hw_size - offset - size);
+ }
+
+ tb->dma_addr[i] = tb->dma_base[i] + offset;
+ }
+
+ switch (pixfmt->pixelformat) {
+ case V4L2_PIX_FMT_YVU420M:
+ swap(tb->dma_addr[1], tb->dma_addr[2]);
+ break;
+ }
+
+ return 0;
+}
+
+static void tegra_buf_queue(struct vb2_buffer *vb)
+{
+ struct tegra_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static void tegra_buf_request_complete(struct vb2_buffer *vb)
+{
+ struct tegra_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
+}
+
+static int tegra_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ return 0;
+}
+
+static void tegra_stop_streaming(struct vb2_queue *vq)
+{
+ struct tegra_ctx *ctx = vb2_get_drv_priv(vq);
+
+ while (true) {
+ struct vb2_v4l2_buffer *vbuf;
+
+ if (V4L2_TYPE_IS_OUTPUT(vq->type))
+ vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ else
+ vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+
+ if (!vbuf)
+ break;
+
+ v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req, &ctx->hdl);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops tegra_qops = {
+ .queue_setup = tegra_queue_setup,
+ .buf_init = tegra_buf_init,
+ .buf_cleanup = tegra_buf_cleanup,
+ .buf_prepare = tegra_buf_prepare,
+ .buf_queue = tegra_buf_queue,
+ .buf_out_validate = tegra_buf_out_validate,
+ .buf_request_complete = tegra_buf_request_complete,
+ .start_streaming = tegra_start_streaming,
+ .stop_streaming = tegra_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static int tegra_queue_init(void *priv,
+ struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct tegra_ctx *ctx = priv;
+ struct tegra_vde *vde = ctx->vde;
+ const struct vb2_mem_ops *mem_ops;
+ unsigned long dma_attrs;
+ int err;
+
+ /*
+ * TODO: Switch to use of vb2_dma_contig_memops uniformly once we
+ * will add IOMMU_DOMAIN support for video decoder to tegra-smmu
+ * driver. For now we need to stick with SG ops in order to be able
+ * to get SGT table easily. This is suboptimal since SG mappings are
+ * wasting CPU cache and we don't need that caching.
+ */
+ if (vde->domain)
+ mem_ops = &vb2_dma_sg_memops;
+ else
+ mem_ops = &vb2_dma_contig_memops;
+
+ dma_attrs = DMA_ATTR_WRITE_COMBINE;
+
+ src_vq->buf_struct_size = sizeof(struct tegra_m2m_buffer);
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ src_vq->supports_requests = true;
+ src_vq->requires_requests = true;
+ src_vq->lock = &vde->v4l2_lock;
+ src_vq->dma_attrs = dma_attrs;
+ src_vq->mem_ops = mem_ops;
+ src_vq->ops = &tegra_qops;
+ src_vq->drv_priv = ctx;
+ src_vq->dev = vde->dev;
+
+ err = vb2_queue_init(src_vq);
+ if (err) {
+ v4l2_err(&vde->v4l2_dev,
+ "failed to initialize src queue: %d\n", err);
+ return err;
+ }
+
+ /*
+ * We may need to zero the end of bitstream in kernel if userspace
+ * doesn't do that, hence kmap is needed for the coded data. It's not
+ * needed for framebuffers.
+ */
+ dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
+
+ dst_vq->buf_struct_size = sizeof(struct tegra_m2m_buffer);
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_DMABUF | VB2_MMAP;
+ dst_vq->lock = &vde->v4l2_lock;
+ dst_vq->dma_attrs = dma_attrs;
+ dst_vq->mem_ops = mem_ops;
+ dst_vq->ops = &tegra_qops;
+ dst_vq->drv_priv = ctx;
+ dst_vq->dev = vde->dev;
+
+ err = vb2_queue_init(dst_vq);
+ if (err) {
+ v4l2_err(&vde->v4l2_dev,
+ "failed to initialize dst queue: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void tegra_reset_fmt(struct tegra_ctx *ctx, struct v4l2_format *f,
+ u32 fourcc)
+{
+ memset(f, 0, sizeof(*f));
+ f->fmt.pix_mp.pixelformat = fourcc;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_REC709;
+ f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+}
+
+static void tegra_reset_coded_fmt(struct tegra_ctx *ctx)
+{
+ const struct tegra_vde_soc *soc = ctx->vde->soc;
+ struct v4l2_format *f = &ctx->coded_fmt;
+
+ ctx->coded_fmt_desc = &soc->coded_fmts[0];
+ tegra_reset_fmt(ctx, f, ctx->coded_fmt_desc->fourcc);
+
+ f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f->fmt.pix_mp.width = ctx->coded_fmt_desc->frmsize.min_width;
+ f->fmt.pix_mp.height = ctx->coded_fmt_desc->frmsize.min_height;
+}
+
+static void tegra_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
+ u32 pixelformat, u32 width, u32 height)
+{
+ const struct v4l2_format_info *info = v4l2_format_info(pixelformat);
+ struct v4l2_plane_pix_format *plane;
+ unsigned int i;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420M:
+ case V4L2_PIX_FMT_YVU420M:
+ pixfmt->width = width;
+ pixfmt->height = height;
+ pixfmt->pixelformat = pixelformat;
+ pixfmt->num_planes = info->mem_planes;
+
+ for (i = 0; i < pixfmt->num_planes; i++) {
+ unsigned int hdiv = (i == 0) ? 1 : 2;
+ unsigned int vdiv = (i == 0) ? 1 : 2;
+
+ /*
+ * VDE is connected to Graphics Memory using 128bit port,
+ * all memory accesses are made using 16B atoms.
+ *
+ * V4L requires Cb/Cr strides to be exactly half of the
+ * Y stride, hence we're aligning Y to 16B x 2.
+ */
+ plane = &pixfmt->plane_fmt[i];
+ plane->bytesperline = ALIGN(width, VDE_ATOM * 2) / hdiv;
+ plane->sizeimage = plane->bytesperline * height / vdiv;
+ }
+
+ break;
+ }
+}
+
+static void tegra_reset_decoded_fmt(struct tegra_ctx *ctx)
+{
+ struct v4l2_format *f = &ctx->decoded_fmt;
+
+ tegra_reset_fmt(ctx, f, ctx->coded_fmt_desc->decoded_fmts[0]);
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ tegra_fill_pixfmt_mp(&f->fmt.pix_mp,
+ ctx->coded_fmt_desc->decoded_fmts[0],
+ ctx->coded_fmt.fmt.pix_mp.width,
+ ctx->coded_fmt.fmt.pix_mp.height);
+}
+
+static void tegra_job_finish(struct tegra_ctx *ctx,
+ enum vb2_buffer_state result)
+{
+ v4l2_m2m_buf_done_and_job_finish(ctx->vde->m2m, ctx->fh.m2m_ctx,
+ result);
+}
+
+static void tegra_decode_complete(struct work_struct *work)
+{
+ struct tegra_ctx *ctx = container_of(work, struct tegra_ctx, work);
+ int err;
+
+ err = ctx->coded_fmt_desc->decode_wait(ctx);
+ if (err)
+ tegra_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ else
+ tegra_job_finish(ctx, VB2_BUF_STATE_DONE);
+}
+
+static int tegra_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->bus_info, "platform:tegra-vde", sizeof(cap->bus_info));
+ strscpy(cap->driver, "tegra-vde", sizeof(cap->driver));
+ strscpy(cap->card, "tegra-vde", sizeof(cap->card));
+
+ return 0;
+}
+
+static int tegra_enum_decoded_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+
+ if (WARN_ON(!ctx->coded_fmt_desc))
+ return -EINVAL;
+
+ if (f->index >= ctx->coded_fmt_desc->num_decoded_fmts)
+ return -EINVAL;
+
+ f->pixelformat = ctx->coded_fmt_desc->decoded_fmts[f->index];
+
+ return 0;
+}
+
+static int tegra_g_decoded_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+
+ *f = ctx->decoded_fmt;
+ return 0;
+}
+
+static int tegra_try_decoded_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+ const struct tegra_coded_fmt_desc *coded_desc;
+ unsigned int i;
+
+ /*
+ * The codec context should point to a coded format desc, if the format
+ * on the coded end has not been set yet, it should point to the
+ * default value.
+ */
+ coded_desc = ctx->coded_fmt_desc;
+ if (WARN_ON(!coded_desc))
+ return -EINVAL;
+
+ if (!coded_desc->num_decoded_fmts)
+ return -EINVAL;
+
+ for (i = 0; i < coded_desc->num_decoded_fmts; i++) {
+ if (coded_desc->decoded_fmts[i] == pix_mp->pixelformat)
+ break;
+ }
+
+ if (i == coded_desc->num_decoded_fmts)
+ pix_mp->pixelformat = coded_desc->decoded_fmts[0];
+
+ /* always apply the frmsize constraint of the coded end */
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &coded_desc->frmsize);
+
+ tegra_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
+ pix_mp->width, pix_mp->height);
+ pix_mp->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int tegra_s_decoded_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+ struct vb2_queue *vq;
+ int err;
+
+ /* change not allowed if queue is busy */
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ err = tegra_try_decoded_fmt(file, priv, f);
+ if (err)
+ return err;
+
+ ctx->decoded_fmt = *f;
+
+ return 0;
+}
+
+static int tegra_enum_coded_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+ const struct tegra_vde_soc *soc = ctx->vde->soc;
+
+ if (f->index >= soc->num_coded_fmts)
+ return -EINVAL;
+
+ f->pixelformat = soc->coded_fmts[f->index].fourcc;
+
+ return 0;
+}
+
+static int tegra_g_coded_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+
+ *f = ctx->coded_fmt;
+ return 0;
+}
+
+static const struct tegra_coded_fmt_desc *
+tegra_find_coded_fmt_desc(struct tegra_ctx *ctx, u32 fourcc)
+{
+ const struct tegra_vde_soc *soc = ctx->vde->soc;
+ unsigned int i;
+
+ for (i = 0; i < soc->num_coded_fmts; i++) {
+ if (soc->coded_fmts[i].fourcc == fourcc)
+ return &soc->coded_fmts[i];
+ }
+
+ return NULL;
+}
+
+static int tegra_try_coded_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+ const struct tegra_vde_soc *soc = ctx->vde->soc;
+ int size = pix_mp->plane_fmt[0].sizeimage;
+ const struct tegra_coded_fmt_desc *desc;
+
+ desc = tegra_find_coded_fmt_desc(ctx, pix_mp->pixelformat);
+ if (!desc) {
+ pix_mp->pixelformat = soc->coded_fmts[0].fourcc;
+ desc = &soc->coded_fmts[0];
+ }
+
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &desc->frmsize);
+
+ pix_mp->plane_fmt[0].sizeimage = max(ALIGN(size, SXE_BUFFER), SZ_2M);
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->num_planes = 1;
+
+ return 0;
+}
+
+static int tegra_s_coded_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+ struct v4l2_m2m_ctx *m2m_ctx = ctx->fh.m2m_ctx;
+ const struct tegra_coded_fmt_desc *desc;
+ struct vb2_queue *peer_vq, *vq;
+ struct v4l2_format *cap_fmt;
+ int err;
+
+ /*
+ * In order to support dynamic resolution change, the decoder admits
+ * a resolution change, as long as the pixelformat remains. Can't be
+ * done if streaming.
+ */
+ vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (vb2_is_streaming(vq) ||
+ (vb2_is_busy(vq) &&
+ f->fmt.pix_mp.pixelformat != ctx->coded_fmt.fmt.pix_mp.pixelformat))
+ return -EBUSY;
+
+ /*
+ * Since format change on the OUTPUT queue will reset the CAPTURE
+ * queue, we can't allow doing so when the CAPTURE queue has buffers
+ * allocated.
+ */
+ peer_vq = v4l2_m2m_get_vq(m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (vb2_is_busy(peer_vq))
+ return -EBUSY;
+
+ err = tegra_try_coded_fmt(file, priv, f);
+ if (err)
+ return err;
+
+ desc = tegra_find_coded_fmt_desc(ctx, f->fmt.pix_mp.pixelformat);
+ if (!desc)
+ return -EINVAL;
+
+ ctx->coded_fmt_desc = desc;
+ ctx->coded_fmt = *f;
+
+ /*
+ * Current decoded format might have become invalid with newly
+ * selected codec, so reset it to default just to be safe and
+ * keep internal driver state sane. User is mandated to set
+ * the decoded format again after we return, so we don't need
+ * anything smarter.
+ *
+ * Note that this will propagates any size changes to the decoded format.
+ */
+ tegra_reset_decoded_fmt(ctx);
+
+ /* propagate colorspace information to capture */
+ cap_fmt = &ctx->decoded_fmt;
+ cap_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ cap_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ cap_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ cap_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+
+ return 0;
+}
+
+static int tegra_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(priv);
+ const struct tegra_coded_fmt_desc *fmt;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fmt = tegra_find_coded_fmt_desc(ctx, fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise = fmt->frmsize;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops tegra_v4l2_ioctl_ops = {
+ .vidioc_querycap = tegra_querycap,
+ .vidioc_enum_framesizes = tegra_enum_framesizes,
+
+ .vidioc_try_fmt_vid_out_mplane = tegra_try_coded_fmt,
+ .vidioc_g_fmt_vid_out_mplane = tegra_g_coded_fmt,
+ .vidioc_s_fmt_vid_out_mplane = tegra_s_coded_fmt,
+ .vidioc_enum_fmt_vid_out = tegra_enum_coded_fmt,
+
+ .vidioc_try_fmt_vid_cap_mplane = tegra_try_decoded_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = tegra_g_decoded_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = tegra_s_decoded_fmt,
+ .vidioc_enum_fmt_vid_cap = tegra_enum_decoded_fmt,
+
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static int tegra_init_ctrls(struct tegra_ctx *ctx)
+{
+ unsigned int i;
+ int err;
+
+ err = v4l2_ctrl_handler_init(&ctx->hdl, ARRAY_SIZE(ctrl_cfgs));
+ if (err)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(ctrl_cfgs); i++) {
+ ctx->ctrls[i] = v4l2_ctrl_new_custom(&ctx->hdl, &ctrl_cfgs[i],
+ NULL);
+ if (ctx->hdl.error) {
+ err = ctx->hdl.error;
+ goto free_ctrls;
+ }
+ }
+
+ err = v4l2_ctrl_handler_setup(&ctx->hdl);
+ if (err)
+ goto free_ctrls;
+
+ ctx->fh.ctrl_handler = &ctx->hdl;
+
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(&ctx->hdl);
+
+ return err;
+}
+
+static int tegra_init_m2m(struct tegra_ctx *ctx)
+{
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(ctx->vde->m2m,
+ ctx, tegra_queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx))
+ return PTR_ERR(ctx->fh.m2m_ctx);
+
+ return 0;
+}
+
+static int tegra_open(struct file *file)
+{
+ struct tegra_vde *vde = video_drvdata(file);
+ struct tegra_ctx *ctx;
+ int err;
+
+ ctx = kzalloc(offsetof(struct tegra_ctx, ctrls[ARRAY_SIZE(ctrl_cfgs)]),
+ GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->vde = vde;
+ v4l2_fh_init(&ctx->fh, video_devdata(file));
+ INIT_WORK(&ctx->work, tegra_decode_complete);
+
+ err = tegra_init_ctrls(ctx);
+ if (err) {
+ v4l2_err(&vde->v4l2_dev, "failed to add controls: %d\n", err);
+ goto free_ctx;
+ }
+
+ err = tegra_init_m2m(ctx);
+ if (err) {
+ v4l2_err(&vde->v4l2_dev, "failed to initialize m2m: %d\n", err);
+ goto free_ctrls;
+ }
+
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
+ tegra_reset_coded_fmt(ctx);
+ tegra_try_coded_fmt(file, file->private_data, &ctx->coded_fmt);
+
+ tegra_reset_decoded_fmt(ctx);
+ tegra_try_decoded_fmt(file, file->private_data, &ctx->decoded_fmt);
+
+ return 0;
+
+free_ctrls:
+ v4l2_ctrl_handler_free(&ctx->hdl);
+free_ctx:
+ kfree(ctx);
+
+ return err;
+}
+
+static int tegra_release(struct file *file)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct tegra_ctx *ctx = fh_to_tegra_ctx(fh);
+ struct tegra_vde *vde = ctx->vde;
+
+ v4l2_fh_del(fh);
+ v4l2_m2m_ctx_release(fh->m2m_ctx);
+ v4l2_ctrl_handler_free(&ctx->hdl);
+ v4l2_fh_exit(fh);
+ kfree(ctx);
+
+ tegra_vde_dmabuf_cache_unmap_sync(vde);
+
+ return 0;
+}
+
+static const struct v4l2_file_operations tegra_v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = tegra_open,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+ .release = tegra_release,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static void tegra_device_run(void *priv)
+{
+ struct tegra_ctx *ctx = priv;
+ struct vb2_v4l2_buffer *src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ struct media_request *src_req = src->vb2_buf.req_obj.req;
+ int err;
+
+ v4l2_ctrl_request_setup(src_req, &ctx->hdl);
+
+ err = ctx->coded_fmt_desc->decode_run(ctx);
+
+ v4l2_ctrl_request_complete(src_req, &ctx->hdl);
+
+ if (err)
+ tegra_job_finish(ctx, VB2_BUF_STATE_ERROR);
+ else
+ queue_work(ctx->vde->wq, &ctx->work);
+}
+
+static const struct v4l2_m2m_ops tegra_v4l2_m2m_ops = {
+ .device_run = tegra_device_run,
+};
+
+static int tegra_request_validate(struct media_request *req)
+{
+ unsigned int count;
+
+ count = vb2_request_buffer_cnt(req);
+ if (!count)
+ return -ENOENT;
+ else if (count > 1)
+ return -EINVAL;
+
+ return vb2_request_validate(req);
+}
+
+static const struct media_device_ops tegra_media_device_ops = {
+ .req_validate = tegra_request_validate,
+ .req_queue = v4l2_m2m_request_queue,
+};
+
+int tegra_vde_v4l2_init(struct tegra_vde *vde)
+{
+ struct device *dev = vde->dev;
+ int err;
+
+ mutex_init(&vde->v4l2_lock);
+ media_device_init(&vde->mdev);
+ video_set_drvdata(&vde->vdev, vde);
+
+ vde->vdev.lock = &vde->v4l2_lock,
+ vde->vdev.fops = &tegra_v4l2_fops,
+ vde->vdev.vfl_dir = VFL_DIR_M2M,
+ vde->vdev.release = video_device_release_empty,
+ vde->vdev.v4l2_dev = &vde->v4l2_dev;
+ vde->vdev.ioctl_ops = &tegra_v4l2_ioctl_ops,
+ vde->vdev.device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING,
+
+ vde->v4l2_dev.mdev = &vde->mdev;
+ vde->mdev.ops = &tegra_media_device_ops;
+ vde->mdev.dev = dev;
+
+ strscpy(vde->mdev.model, "tegra-vde", sizeof(vde->mdev.model));
+ strscpy(vde->vdev.name, "tegra-vde", sizeof(vde->vdev.name));
+ strscpy(vde->mdev.bus_info, "platform:tegra-vde",
+ sizeof(vde->mdev.bus_info));
+
+ vde->wq = create_workqueue("tegra-vde");
+ if (!vde->wq)
+ return -ENOMEM;
+
+ err = media_device_register(&vde->mdev);
+ if (err) {
+ dev_err(dev, "failed to register media device: %d\n", err);
+ goto clean_up_media_device;
+ }
+
+ err = v4l2_device_register(dev, &vde->v4l2_dev);
+ if (err) {
+ dev_err(dev, "failed to register v4l2 device: %d\n", err);
+ goto unreg_media_device;
+ }
+
+ err = video_register_device(&vde->vdev, VFL_TYPE_VIDEO, -1);
+ if (err) {
+ dev_err(dev, "failed to register video device: %d\n", err);
+ goto unreg_v4l2;
+ }
+
+ vde->m2m = v4l2_m2m_init(&tegra_v4l2_m2m_ops);
+ err = PTR_ERR_OR_ZERO(vde->m2m);
+ if (err) {
+ dev_err(dev, "failed to initialize m2m device: %d\n", err);
+ goto unreg_video_device;
+ }
+
+ err = v4l2_m2m_register_media_controller(vde->m2m, &vde->vdev,
+ MEDIA_ENT_F_PROC_VIDEO_DECODER);
+ if (err) {
+ dev_err(dev, "failed to register media controller: %d\n", err);
+ goto release_m2m;
+ }
+
+ v4l2_info(&vde->v4l2_dev, "v4l2 device registered as /dev/video%d\n",
+ vde->vdev.num);
+
+ return 0;
+
+release_m2m:
+ v4l2_m2m_release(vde->m2m);
+unreg_video_device:
+ video_unregister_device(&vde->vdev);
+unreg_v4l2:
+ v4l2_device_unregister(&vde->v4l2_dev);
+unreg_media_device:
+ media_device_unregister(&vde->mdev);
+clean_up_media_device:
+ media_device_cleanup(&vde->mdev);
+
+ destroy_workqueue(vde->wq);
+
+ return err;
+}
+
+void tegra_vde_v4l2_deinit(struct tegra_vde *vde)
+{
+ v4l2_m2m_unregister_media_controller(vde->m2m);
+ v4l2_m2m_release(vde->m2m);
+
+ video_unregister_device(&vde->vdev);
+ v4l2_device_unregister(&vde->v4l2_dev);
+
+ media_device_unregister(&vde->mdev);
+ media_device_cleanup(&vde->mdev);
+
+ destroy_workqueue(vde->wq);
+}
diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.c b/drivers/media/platform/nvidia/tegra-vde/vde.c
new file mode 100644
index 000000000000..f3e863a94c5a
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/vde.c
@@ -0,0 +1,551 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * NVIDIA Tegra Video decoder driver
+ *
+ * Copyright (C) 2016-2017 Dmitry Osipenko <digetx@gmail.com>
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-buf.h>
+#include <linux/genalloc.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <soc/tegra/common.h>
+#include <soc/tegra/pmc.h>
+
+#include "vde.h"
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+void tegra_vde_writel(struct tegra_vde *vde, u32 value,
+ void __iomem *base, u32 offset)
+{
+ trace_vde_writel(vde, base, offset, value);
+
+ writel_relaxed(value, base + offset);
+}
+
+u32 tegra_vde_readl(struct tegra_vde *vde, void __iomem *base, u32 offset)
+{
+ u32 value = readl_relaxed(base + offset);
+
+ trace_vde_readl(vde, base, offset, value);
+
+ return value;
+}
+
+void tegra_vde_set_bits(struct tegra_vde *vde, u32 mask,
+ void __iomem *base, u32 offset)
+{
+ u32 value = tegra_vde_readl(vde, base, offset);
+
+ tegra_vde_writel(vde, value | mask, base, offset);
+}
+
+int tegra_vde_alloc_bo(struct tegra_vde *vde,
+ struct tegra_vde_bo **ret_bo,
+ enum dma_data_direction dma_dir,
+ size_t size)
+{
+ struct device *dev = vde->dev;
+ struct tegra_vde_bo *bo;
+ int err;
+
+ bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+ if (!bo)
+ return -ENOMEM;
+
+ bo->vde = vde;
+ bo->size = size;
+ bo->dma_dir = dma_dir;
+ bo->dma_attrs = DMA_ATTR_WRITE_COMBINE |
+ DMA_ATTR_NO_KERNEL_MAPPING;
+
+ if (!vde->domain)
+ bo->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS;
+
+ bo->dma_cookie = dma_alloc_attrs(dev, bo->size, &bo->dma_handle,
+ GFP_KERNEL, bo->dma_attrs);
+ if (!bo->dma_cookie) {
+ dev_err(dev, "Failed to allocate DMA buffer of size: %zu\n",
+ bo->size);
+ err = -ENOMEM;
+ goto free_bo;
+ }
+
+ err = dma_get_sgtable_attrs(dev, &bo->sgt, bo->dma_cookie,
+ bo->dma_handle, bo->size, bo->dma_attrs);
+ if (err) {
+ dev_err(dev, "Failed to get DMA buffer SG table: %d\n", err);
+ goto free_attrs;
+ }
+
+ err = dma_map_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs);
+ if (err) {
+ dev_err(dev, "Failed to map DMA buffer SG table: %d\n", err);
+ goto free_table;
+ }
+
+ if (vde->domain) {
+ err = tegra_vde_iommu_map(vde, &bo->sgt, &bo->iova, bo->size);
+ if (err) {
+ dev_err(dev, "Failed to map DMA buffer IOVA: %d\n", err);
+ goto unmap_sgtable;
+ }
+
+ bo->dma_addr = iova_dma_addr(&vde->iova, bo->iova);
+ } else {
+ bo->dma_addr = sg_dma_address(bo->sgt.sgl);
+ }
+
+ *ret_bo = bo;
+
+ return 0;
+
+unmap_sgtable:
+ dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs);
+free_table:
+ sg_free_table(&bo->sgt);
+free_attrs:
+ dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle,
+ bo->dma_attrs);
+free_bo:
+ kfree(bo);
+
+ return err;
+}
+
+void tegra_vde_free_bo(struct tegra_vde_bo *bo)
+{
+ struct tegra_vde *vde = bo->vde;
+ struct device *dev = vde->dev;
+
+ if (vde->domain)
+ tegra_vde_iommu_unmap(vde, bo->iova);
+
+ dma_unmap_sgtable(dev, &bo->sgt, bo->dma_dir, bo->dma_attrs);
+
+ sg_free_table(&bo->sgt);
+
+ dma_free_attrs(dev, bo->size, bo->dma_cookie, bo->dma_handle,
+ bo->dma_attrs);
+ kfree(bo);
+}
+
+static irqreturn_t tegra_vde_isr(int irq, void *data)
+{
+ struct tegra_vde *vde = data;
+
+ if (completion_done(&vde->decode_completion))
+ return IRQ_NONE;
+
+ tegra_vde_set_bits(vde, 0, vde->frameid, 0x208);
+ complete(&vde->decode_completion);
+
+ return IRQ_HANDLED;
+}
+
+static __maybe_unused int tegra_vde_runtime_suspend(struct device *dev)
+{
+ struct tegra_vde *vde = dev_get_drvdata(dev);
+ int err;
+
+ if (!dev->pm_domain) {
+ err = tegra_powergate_power_off(TEGRA_POWERGATE_VDEC);
+ if (err) {
+ dev_err(dev, "Failed to power down HW: %d\n", err);
+ return err;
+ }
+ }
+
+ clk_disable_unprepare(vde->clk);
+ reset_control_release(vde->rst);
+ reset_control_release(vde->rst_mc);
+
+ return 0;
+}
+
+static __maybe_unused int tegra_vde_runtime_resume(struct device *dev)
+{
+ struct tegra_vde *vde = dev_get_drvdata(dev);
+ int err;
+
+ err = reset_control_acquire(vde->rst_mc);
+ if (err) {
+ dev_err(dev, "Failed to acquire mc reset: %d\n", err);
+ return err;
+ }
+
+ err = reset_control_acquire(vde->rst);
+ if (err) {
+ dev_err(dev, "Failed to acquire reset: %d\n", err);
+ goto release_mc_reset;
+ }
+
+ if (!dev->pm_domain) {
+ err = tegra_powergate_sequence_power_up(TEGRA_POWERGATE_VDEC,
+ vde->clk, vde->rst);
+ if (err) {
+ dev_err(dev, "Failed to power up HW : %d\n", err);
+ goto release_reset;
+ }
+ } else {
+ /*
+ * tegra_powergate_sequence_power_up() leaves clocks enabled,
+ * while GENPD not.
+ */
+ err = clk_prepare_enable(vde->clk);
+ if (err) {
+ dev_err(dev, "Failed to enable clock: %d\n", err);
+ goto release_reset;
+ }
+ }
+
+ return 0;
+
+release_reset:
+ reset_control_release(vde->rst);
+release_mc_reset:
+ reset_control_release(vde->rst_mc);
+
+ return err;
+}
+
+static int tegra_vde_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra_vde *vde;
+ int irq, err;
+
+ vde = devm_kzalloc(dev, sizeof(*vde), GFP_KERNEL);
+ if (!vde)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vde);
+
+ vde->soc = of_device_get_match_data(&pdev->dev);
+ vde->dev = dev;
+
+ vde->sxe = devm_platform_ioremap_resource_byname(pdev, "sxe");
+ if (IS_ERR(vde->sxe))
+ return PTR_ERR(vde->sxe);
+
+ vde->bsev = devm_platform_ioremap_resource_byname(pdev, "bsev");
+ if (IS_ERR(vde->bsev))
+ return PTR_ERR(vde->bsev);
+
+ vde->mbe = devm_platform_ioremap_resource_byname(pdev, "mbe");
+ if (IS_ERR(vde->mbe))
+ return PTR_ERR(vde->mbe);
+
+ vde->ppe = devm_platform_ioremap_resource_byname(pdev, "ppe");
+ if (IS_ERR(vde->ppe))
+ return PTR_ERR(vde->ppe);
+
+ vde->mce = devm_platform_ioremap_resource_byname(pdev, "mce");
+ if (IS_ERR(vde->mce))
+ return PTR_ERR(vde->mce);
+
+ vde->tfe = devm_platform_ioremap_resource_byname(pdev, "tfe");
+ if (IS_ERR(vde->tfe))
+ return PTR_ERR(vde->tfe);
+
+ vde->ppb = devm_platform_ioremap_resource_byname(pdev, "ppb");
+ if (IS_ERR(vde->ppb))
+ return PTR_ERR(vde->ppb);
+
+ vde->vdma = devm_platform_ioremap_resource_byname(pdev, "vdma");
+ if (IS_ERR(vde->vdma))
+ return PTR_ERR(vde->vdma);
+
+ vde->frameid = devm_platform_ioremap_resource_byname(pdev, "frameid");
+ if (IS_ERR(vde->frameid))
+ return PTR_ERR(vde->frameid);
+
+ vde->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(vde->clk)) {
+ err = PTR_ERR(vde->clk);
+ dev_err(dev, "Could not get VDE clk %d\n", err);
+ return err;
+ }
+
+ vde->rst = devm_reset_control_get_exclusive_released(dev, NULL);
+ if (IS_ERR(vde->rst)) {
+ err = PTR_ERR(vde->rst);
+ dev_err(dev, "Could not get VDE reset %d\n", err);
+ return err;
+ }
+
+ vde->rst_mc = devm_reset_control_get_optional_exclusive_released(dev, "mc");
+ if (IS_ERR(vde->rst_mc)) {
+ err = PTR_ERR(vde->rst_mc);
+ dev_err(dev, "Could not get MC reset %d\n", err);
+ return err;
+ }
+
+ irq = platform_get_irq_byname(pdev, "sync-token");
+ if (irq < 0)
+ return irq;
+
+ err = devm_request_irq(dev, irq, tegra_vde_isr, 0,
+ dev_name(dev), vde);
+ if (err) {
+ dev_err(dev, "Could not request IRQ %d\n", err);
+ return err;
+ }
+
+ err = devm_tegra_core_dev_init_opp_table_common(dev);
+ if (err) {
+ dev_err(dev, "Could initialize OPP table %d\n", err);
+ return err;
+ }
+
+ vde->iram_pool = of_gen_pool_get(dev->of_node, "iram", 0);
+ if (!vde->iram_pool) {
+ dev_err(dev, "Could not get IRAM pool\n");
+ return -EPROBE_DEFER;
+ }
+
+ vde->iram = gen_pool_dma_alloc(vde->iram_pool,
+ gen_pool_size(vde->iram_pool),
+ &vde->iram_lists_addr);
+ if (!vde->iram) {
+ dev_err(dev, "Could not reserve IRAM\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&vde->map_list);
+ mutex_init(&vde->map_lock);
+ mutex_init(&vde->lock);
+ init_completion(&vde->decode_completion);
+
+ err = tegra_vde_iommu_init(vde);
+ if (err) {
+ dev_err(dev, "Failed to initialize IOMMU: %d\n", err);
+ goto err_gen_free;
+ }
+
+ pm_runtime_enable(dev);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 300);
+
+ /*
+ * VDE partition may be left ON after bootloader, hence let's
+ * power-cycle it in order to put hardware into a predictable lower
+ * power state.
+ */
+ err = pm_runtime_resume_and_get(dev);
+ if (err)
+ goto err_pm_runtime;
+
+ pm_runtime_put(dev);
+
+ err = tegra_vde_alloc_bo(vde, &vde->secure_bo, DMA_FROM_DEVICE, 4096);
+ if (err) {
+ dev_err(dev, "Failed to allocate secure BO: %d\n", err);
+ goto err_pm_runtime;
+ }
+
+ err = tegra_vde_v4l2_init(vde);
+ if (err) {
+ dev_err(dev, "Failed to initialize V4L2: %d\n", err);
+ goto err_free_secure_bo;
+ }
+
+ return 0;
+
+err_free_secure_bo:
+ tegra_vde_free_bo(vde->secure_bo);
+err_pm_runtime:
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_disable(dev);
+
+ tegra_vde_iommu_deinit(vde);
+
+err_gen_free:
+ gen_pool_free(vde->iram_pool, (unsigned long)vde->iram,
+ gen_pool_size(vde->iram_pool));
+
+ return err;
+}
+
+static int tegra_vde_remove(struct platform_device *pdev)
+{
+ struct tegra_vde *vde = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ tegra_vde_v4l2_deinit(vde);
+ tegra_vde_free_bo(vde->secure_bo);
+
+ /*
+ * As it increments RPM usage_count even on errors, we don't need to
+ * check the returned code here.
+ */
+ pm_runtime_get_sync(dev);
+
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_disable(dev);
+
+ /*
+ * Balance RPM state, the VDE power domain is left ON and hardware
+ * is clock-gated. It's safe to reboot machine now.
+ */
+ pm_runtime_put_noidle(dev);
+ clk_disable_unprepare(vde->clk);
+
+ tegra_vde_dmabuf_cache_unmap_all(vde);
+ tegra_vde_iommu_deinit(vde);
+
+ gen_pool_free(vde->iram_pool, (unsigned long)vde->iram,
+ gen_pool_size(vde->iram_pool));
+
+ return 0;
+}
+
+static void tegra_vde_shutdown(struct platform_device *pdev)
+{
+ /*
+ * On some devices bootloader isn't ready to a power-gated VDE on
+ * a warm-reboot, machine will hang in that case.
+ */
+ pm_runtime_get_sync(&pdev->dev);
+}
+
+static __maybe_unused int tegra_vde_pm_suspend(struct device *dev)
+{
+ struct tegra_vde *vde = dev_get_drvdata(dev);
+ int err;
+
+ mutex_lock(&vde->lock);
+
+ err = pm_runtime_force_suspend(dev);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static __maybe_unused int tegra_vde_pm_resume(struct device *dev)
+{
+ struct tegra_vde *vde = dev_get_drvdata(dev);
+ int err;
+
+ err = pm_runtime_force_resume(dev);
+ if (err < 0)
+ return err;
+
+ mutex_unlock(&vde->lock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra_vde_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_vde_runtime_suspend,
+ tegra_vde_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_vde_pm_suspend,
+ tegra_vde_pm_resume)
+};
+
+static const u32 tegra124_decoded_fmts[] = {
+ /* TBD: T124 supports only a non-standard Tegra tiled format */
+};
+
+static const struct tegra_coded_fmt_desc tegra124_coded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_H264_SLICE,
+ .frmsize = {
+ .min_width = 16,
+ .max_width = 1920,
+ .step_width = 16,
+ .min_height = 16,
+ .max_height = 2032,
+ .step_height = 16,
+ },
+ .num_decoded_fmts = ARRAY_SIZE(tegra124_decoded_fmts),
+ .decoded_fmts = tegra124_decoded_fmts,
+ .decode_run = tegra_vde_h264_decode_run,
+ .decode_wait = tegra_vde_h264_decode_wait,
+ },
+};
+
+static const u32 tegra20_decoded_fmts[] = {
+ V4L2_PIX_FMT_YUV420M,
+ V4L2_PIX_FMT_YVU420M,
+};
+
+static const struct tegra_coded_fmt_desc tegra20_coded_fmts[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_H264_SLICE,
+ .frmsize = {
+ .min_width = 16,
+ .max_width = 1920,
+ .step_width = 16,
+ .min_height = 16,
+ .max_height = 2032,
+ .step_height = 16,
+ },
+ .num_decoded_fmts = ARRAY_SIZE(tegra20_decoded_fmts),
+ .decoded_fmts = tegra20_decoded_fmts,
+ .decode_run = tegra_vde_h264_decode_run,
+ .decode_wait = tegra_vde_h264_decode_wait,
+ },
+};
+
+static const struct tegra_vde_soc tegra124_vde_soc = {
+ .supports_ref_pic_marking = true,
+ .coded_fmts = tegra124_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(tegra124_coded_fmts),
+};
+
+static const struct tegra_vde_soc tegra114_vde_soc = {
+ .supports_ref_pic_marking = true,
+ .coded_fmts = tegra20_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts),
+};
+
+static const struct tegra_vde_soc tegra30_vde_soc = {
+ .supports_ref_pic_marking = false,
+ .coded_fmts = tegra20_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts),
+};
+
+static const struct tegra_vde_soc tegra20_vde_soc = {
+ .supports_ref_pic_marking = false,
+ .coded_fmts = tegra20_coded_fmts,
+ .num_coded_fmts = ARRAY_SIZE(tegra20_coded_fmts),
+};
+
+static const struct of_device_id tegra_vde_of_match[] = {
+ { .compatible = "nvidia,tegra124-vde", .data = &tegra124_vde_soc },
+ { .compatible = "nvidia,tegra114-vde", .data = &tegra114_vde_soc },
+ { .compatible = "nvidia,tegra30-vde", .data = &tegra30_vde_soc },
+ { .compatible = "nvidia,tegra20-vde", .data = &tegra20_vde_soc },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_vde_of_match);
+
+static struct platform_driver tegra_vde_driver = {
+ .probe = tegra_vde_probe,
+ .remove = tegra_vde_remove,
+ .shutdown = tegra_vde_shutdown,
+ .driver = {
+ .name = "tegra-vde",
+ .of_match_table = tegra_vde_of_match,
+ .pm = &tegra_vde_pm_ops,
+ },
+};
+module_platform_driver(tegra_vde_driver);
+
+MODULE_DESCRIPTION("NVIDIA Tegra Video Decoder driver");
+MODULE_AUTHOR("Dmitry Osipenko <digetx@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.h b/drivers/media/platform/nvidia/tegra-vde/vde.h
new file mode 100644
index 000000000000..0fbb1f3d2c88
--- /dev/null
+++ b/drivers/media/platform/nvidia/tegra-vde/vde.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * NVIDIA Tegra Video decoder driver
+ *
+ * Copyright (C) 2016-2019 GRATE-DRIVER project
+ */
+
+#ifndef TEGRA_VDE_H
+#define TEGRA_VDE_H
+
+#include <linux/completion.h>
+#include <linux/dma-direction.h>
+#include <linux/iova.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include <media/media-device.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+
+#define ICMDQUE_WR 0x00
+#define CMDQUE_CONTROL 0x08
+#define INTR_STATUS 0x18
+#define BSE_INT_ENB 0x40
+#define BSE_CONFIG 0x44
+
+#define BSE_ICMDQUE_EMPTY BIT(3)
+#define BSE_DMA_BUSY BIT(23)
+
+#define BSEV_ALIGN SZ_1
+#define FRAMEID_ALIGN SZ_256
+#define SXE_BUFFER SZ_32K
+#define VDE_ATOM SZ_16
+
+struct clk;
+struct dma_buf;
+struct gen_pool;
+struct tegra_ctx;
+struct iommu_group;
+struct iommu_domain;
+struct reset_control;
+struct dma_buf_attachment;
+struct tegra_vde_h264_frame;
+struct tegra_vde_h264_decoder_ctx;
+
+struct tegra_video_frame {
+ struct dma_buf_attachment *y_dmabuf_attachment;
+ struct dma_buf_attachment *cb_dmabuf_attachment;
+ struct dma_buf_attachment *cr_dmabuf_attachment;
+ struct dma_buf_attachment *aux_dmabuf_attachment;
+ dma_addr_t y_addr;
+ dma_addr_t cb_addr;
+ dma_addr_t cr_addr;
+ dma_addr_t aux_addr;
+ u32 frame_num;
+ u32 flags;
+ u32 luma_atoms_pitch;
+ u32 chroma_atoms_pitch;
+};
+
+struct tegra_coded_fmt_desc {
+ u32 fourcc;
+ struct v4l2_frmsize_stepwise frmsize;
+ unsigned int num_decoded_fmts;
+ const u32 *decoded_fmts;
+ int (*decode_run)(struct tegra_ctx *ctx);
+ int (*decode_wait)(struct tegra_ctx *ctx);
+};
+
+struct tegra_vde_soc {
+ bool supports_ref_pic_marking;
+ const struct tegra_coded_fmt_desc *coded_fmts;
+ u32 num_coded_fmts;
+};
+
+struct tegra_vde_bo {
+ struct iova *iova;
+ struct sg_table sgt;
+ struct tegra_vde *vde;
+ enum dma_data_direction dma_dir;
+ unsigned long dma_attrs;
+ dma_addr_t dma_handle;
+ dma_addr_t dma_addr;
+ void *dma_cookie;
+ size_t size;
+};
+
+struct tegra_vde {
+ void __iomem *sxe;
+ void __iomem *bsev;
+ void __iomem *mbe;
+ void __iomem *ppe;
+ void __iomem *mce;
+ void __iomem *tfe;
+ void __iomem *ppb;
+ void __iomem *vdma;
+ void __iomem *frameid;
+ struct device *dev;
+ struct mutex lock;
+ struct mutex map_lock;
+ struct list_head map_list;
+ struct reset_control *rst;
+ struct reset_control *rst_mc;
+ struct gen_pool *iram_pool;
+ struct completion decode_completion;
+ struct clk *clk;
+ struct iommu_domain *domain;
+ struct iommu_group *group;
+ struct iova_domain iova;
+ struct iova *iova_resv_static_addresses;
+ struct iova *iova_resv_last_page;
+ const struct tegra_vde_soc *soc;
+ struct tegra_vde_bo *secure_bo;
+ dma_addr_t bitstream_data_addr;
+ dma_addr_t iram_lists_addr;
+ u32 *iram;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m;
+ struct media_device mdev;
+ struct video_device vdev;
+ struct mutex v4l2_lock;
+ struct workqueue_struct *wq;
+ struct tegra_video_frame frames[V4L2_H264_NUM_DPB_ENTRIES + 1];
+};
+
+int tegra_vde_alloc_bo(struct tegra_vde *vde,
+ struct tegra_vde_bo **ret_bo,
+ enum dma_data_direction dma_dir,
+ size_t size);
+void tegra_vde_free_bo(struct tegra_vde_bo *bo);
+
+struct tegra_ctx_h264 {
+ const struct v4l2_ctrl_h264_decode_params *decode_params;
+ const struct v4l2_ctrl_h264_sps *sps;
+ const struct v4l2_ctrl_h264_pps *pps;
+};
+
+struct tegra_ctx {
+ struct tegra_vde *vde;
+ struct tegra_ctx_h264 h264;
+ struct work_struct work;
+ struct v4l2_fh fh;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_format coded_fmt;
+ struct v4l2_format decoded_fmt;
+ const struct tegra_coded_fmt_desc *coded_fmt_desc;
+ struct v4l2_ctrl *ctrls[];
+};
+
+struct tegra_m2m_buffer {
+ struct v4l2_m2m_buffer m2m;
+ struct dma_buf_attachment *a[VB2_MAX_PLANES];
+ dma_addr_t dma_base[VB2_MAX_PLANES];
+ dma_addr_t dma_addr[VB2_MAX_PLANES];
+ struct iova *iova[VB2_MAX_PLANES];
+ struct tegra_vde_bo *aux;
+ bool b_frame;
+};
+
+static inline struct tegra_m2m_buffer *
+vb_to_tegra_buf(struct vb2_buffer *vb)
+{
+ struct v4l2_m2m_buffer *m2m = container_of(vb, struct v4l2_m2m_buffer,
+ vb.vb2_buf);
+
+ return container_of(m2m, struct tegra_m2m_buffer, m2m);
+}
+
+void tegra_vde_prepare_control_data(struct tegra_ctx *ctx, u32 id);
+
+void tegra_vde_writel(struct tegra_vde *vde, u32 value, void __iomem *base,
+ u32 offset);
+u32 tegra_vde_readl(struct tegra_vde *vde, void __iomem *base, u32 offset);
+void tegra_vde_set_bits(struct tegra_vde *vde, u32 mask, void __iomem *base,
+ u32 offset);
+
+int tegra_vde_h264_decode_run(struct tegra_ctx *ctx);
+int tegra_vde_h264_decode_wait(struct tegra_ctx *ctx);
+
+int tegra_vde_iommu_init(struct tegra_vde *vde);
+void tegra_vde_iommu_deinit(struct tegra_vde *vde);
+int tegra_vde_iommu_map(struct tegra_vde *vde,
+ struct sg_table *sgt,
+ struct iova **iovap,
+ size_t size);
+void tegra_vde_iommu_unmap(struct tegra_vde *vde, struct iova *iova);
+
+int tegra_vde_dmabuf_cache_map(struct tegra_vde *vde,
+ struct dma_buf *dmabuf,
+ enum dma_data_direction dma_dir,
+ struct dma_buf_attachment **ap,
+ dma_addr_t *addrp);
+void tegra_vde_dmabuf_cache_unmap(struct tegra_vde *vde,
+ struct dma_buf_attachment *a,
+ bool release);
+void tegra_vde_dmabuf_cache_unmap_sync(struct tegra_vde *vde);
+void tegra_vde_dmabuf_cache_unmap_all(struct tegra_vde *vde);
+
+static __maybe_unused char const *
+tegra_vde_reg_base_name(struct tegra_vde *vde, void __iomem *base)
+{
+ if (vde->sxe == base)
+ return "SXE";
+
+ if (vde->bsev == base)
+ return "BSEV";
+
+ if (vde->mbe == base)
+ return "MBE";
+
+ if (vde->ppe == base)
+ return "PPE";
+
+ if (vde->mce == base)
+ return "MCE";
+
+ if (vde->tfe == base)
+ return "TFE";
+
+ if (vde->ppb == base)
+ return "PPB";
+
+ if (vde->vdma == base)
+ return "VDMA";
+
+ if (vde->frameid == base)
+ return "FRAMEID";
+
+ return "???";
+}
+
+int tegra_vde_v4l2_init(struct tegra_vde *vde);
+void tegra_vde_v4l2_deinit(struct tegra_vde *vde);
+
+#endif /* TEGRA_VDE_H */
diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig
new file mode 100644
index 000000000000..28f2bafc14d2
--- /dev/null
+++ b/drivers/media/platform/nxp/Kconfig
@@ -0,0 +1,55 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+# V4L drivers
+
+comment "NXP media platform drivers"
+
+config VIDEO_IMX_MIPI_CSIS
+ tristate "NXP MIPI CSI-2 CSIS receiver found on i.MX7 and i.MX8 models"
+ depends on ARCH_MXC || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ default n
+ help
+ Video4Linux2 sub-device driver for the MIPI CSI-2 CSIS receiver
+ v3.3/v3.6.3 found on some i.MX7 and i.MX8 SoCs.
+
+config VIDEO_VIU
+ tristate "NXP VIU Video Driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && (PPC_MPC512x || COMPILE_TEST) && I2C
+ select VIDEOBUF_DMA_CONTIG
+ default y
+ help
+ Support for Freescale VIU video driver. This device captures
+ video data, or overlays video on DIU frame buffer.
+
+ Say Y here if you want to enable VIU device on MPC5121e Rev2+.
+ In doubt, say N.
+
+# mem2mem drivers
+
+config VIDEO_IMX_PXP
+ tristate "NXP i.MX Pixel Pipeline (PXP)"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV && (ARCH_MXC || COMPILE_TEST)
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ The i.MX Pixel Pipeline is a memory-to-memory engine for scaling,
+ color space conversion, and rotation.
+
+config VIDEO_MX2_EMMAPRP
+ tristate "NXP MX2 eMMa-PrP support"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on SOC_IMX27 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ MX2X chips have a PrP that can be used to process buffers from
+ memory to memory. Operations include resizing and format
+ conversion.
+
+source "drivers/media/platform/nxp/imx-jpeg/Kconfig"
diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile
new file mode 100644
index 000000000000..efc38c6578ce
--- /dev/null
+++ b/drivers/media/platform/nxp/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += imx-jpeg/
+
+obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o
+obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
+obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
+obj-$(CONFIG_VIDEO_VIU) += fsl-viu.o
diff --git a/drivers/media/platform/fsl-viu.c b/drivers/media/platform/nxp/fsl-viu.c
index a4bfa70b49b2..afc96f6db2a1 100644
--- a/drivers/media/platform/fsl-viu.c
+++ b/drivers/media/platform/nxp/fsl-viu.c
@@ -1407,7 +1407,7 @@ static int viu_of_probe(struct platform_device *op)
}
/* Prepare our private structure */
- viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_ATOMIC);
+ viu_dev = devm_kzalloc(&op->dev, sizeof(struct viu_dev), GFP_KERNEL);
if (!viu_dev) {
dev_err(&op->dev, "Can't allocate private structure\n");
ret = -ENOMEM;
diff --git a/drivers/media/platform/imx-jpeg/Kconfig b/drivers/media/platform/nxp/imx-jpeg/Kconfig
index 2fdd648cda80..5214dcd7fab5 100644
--- a/drivers/media/platform/imx-jpeg/Kconfig
+++ b/drivers/media/platform/nxp/imx-jpeg/Kconfig
@@ -1,8 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
config VIDEO_IMX8_JPEG
tristate "IMX8 JPEG Encoder/Decoder"
+ depends on V4L_MEM2MEM_DRIVERS
depends on ARCH_MXC || COMPILE_TEST
- depends on VIDEO_DEV && VIDEO_V4L2
+ depends on VIDEO_DEV
select VIDEOBUF2_DMA_CONTIG
select V4L2_MEM2MEM_DEV
select V4L2_JPEG_HELPER
diff --git a/drivers/media/platform/imx-jpeg/Makefile b/drivers/media/platform/nxp/imx-jpeg/Makefile
index bf19c82e61b4..bf19c82e61b4 100644
--- a/drivers/media/platform/imx-jpeg/Makefile
+++ b/drivers/media/platform/nxp/imx-jpeg/Makefile
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c
index 29c604b1b179..29c604b1b179 100644
--- a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.c
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
index ae70d3a0dc24..ae70d3a0dc24 100644
--- a/drivers/media/platform/imx-jpeg/mxc-jpeg-hw.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 4ca96cf9def7..d1ec1f4b506b 100644
--- a/drivers/media/platform/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -35,7 +35,7 @@
* it, enable dynamic debug for this module and:
* echo 1 > /sys/module/mxc_jpeg_encdec/parameters/jpeg_tracing
*
- * This is inspired by the drivers/media/platform/s5p-jpeg driver
+ * This is inspired by the drivers/media/platform/samsung/s5p-jpeg driver
*
* Copyright 2018-2019 NXP
*/
@@ -96,7 +96,7 @@ static const struct mxc_jpeg_fmt mxc_formats[] = {
},
{
.name = "YUV420", /* 1st plane = Y, 2nd plane = UV */
- .fourcc = V4L2_PIX_FMT_NV12,
+ .fourcc = V4L2_PIX_FMT_NV12M,
.subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
.nc = 3,
.depth = 12, /* 6 bytes (4Y + UV) for 4 pixels */
@@ -404,7 +404,7 @@ static enum mxc_jpeg_image_format mxc_jpeg_fourcc_to_imgfmt(u32 fourcc)
return MXC_JPEG_GRAY;
case V4L2_PIX_FMT_YUYV:
return MXC_JPEG_YUV422;
- case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12M:
return MXC_JPEG_YUV420;
case V4L2_PIX_FMT_YUV24:
return MXC_JPEG_YUV444;
@@ -673,7 +673,7 @@ static int mxc_jpeg_fixup_sof(struct mxc_jpeg_sof *sof,
_bswap16(&sof->width);
switch (fourcc) {
- case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12M:
sof->components_no = 3;
sof->comp[0].v = 0x2;
sof->comp[0].h = 0x2;
@@ -709,7 +709,7 @@ static int mxc_jpeg_fixup_sos(struct mxc_jpeg_sos *sos,
u8 *sof_u8 = (u8 *)sos;
switch (fourcc) {
- case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV12M:
sos->components_no = 3;
break;
case V4L2_PIX_FMT_YUYV:
@@ -947,8 +947,13 @@ static void mxc_jpeg_device_run(void *priv)
v4l2_m2m_buf_copy_metadata(src_buf, dst_buf, true);
jpeg_src_buf = vb2_to_mxc_buf(&src_buf->vb2_buf);
+ if (q_data_cap->fmt->colplanes != dst_buf->vb2_buf.num_planes) {
+ dev_err(dev, "Capture format %s has %d planes, but capture buffer has %d planes\n",
+ q_data_cap->fmt->name, q_data_cap->fmt->colplanes,
+ dst_buf->vb2_buf.num_planes);
+ jpeg_src_buf->jpeg_parse_error = true;
+ }
if (jpeg_src_buf->jpeg_parse_error) {
- jpeg->slot_data[ctx->slot].used = false;
v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
@@ -992,6 +997,20 @@ end:
spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
}
+static void mxc_jpeg_set_last_buffer_dequeued(struct mxc_jpeg_ctx *ctx)
+{
+ struct vb2_queue *q;
+
+ ctx->stopped = 1;
+ q = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx);
+ if (!list_empty(&q->done_list))
+ return;
+
+ q->last_buffer_dequeued = true;
+ wake_up(&q->done_wq);
+ ctx->stopped = 0;
+}
+
static int mxc_jpeg_decoder_cmd(struct file *file, void *priv,
struct v4l2_decoder_cmd *cmd)
{
@@ -1009,6 +1028,7 @@ static int mxc_jpeg_decoder_cmd(struct file *file, void *priv,
if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) {
/* No more src bufs, notify app EOS */
notify_eos(ctx);
+ mxc_jpeg_set_last_buffer_dequeued(ctx);
} else {
/* will send EOS later*/
ctx->stopping = 1;
@@ -1035,6 +1055,7 @@ static int mxc_jpeg_encoder_cmd(struct file *file, void *priv,
if (v4l2_m2m_num_src_bufs_ready(fh->m2m_ctx) == 0) {
/* No more src bufs, notify app EOS */
notify_eos(ctx);
+ mxc_jpeg_set_last_buffer_dequeued(ctx);
} else {
/* will send EOS later*/
ctx->stopping = 1;
@@ -1111,6 +1132,10 @@ static void mxc_jpeg_stop_streaming(struct vb2_queue *q)
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
}
pm_runtime_put_sync(&ctx->mxc_jpeg->pdev->dev);
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ ctx->stopping = 0;
+ ctx->stopped = 0;
+ }
}
static int mxc_jpeg_valid_comp_id(struct device *dev,
@@ -1183,7 +1208,7 @@ static void mxc_jpeg_bytesperline(struct mxc_jpeg_q_data *q,
/* bytesperline unused for compressed formats */
q->bytesperline[0] = 0;
q->bytesperline[1] = 0;
- } else if (q->fmt->fourcc == V4L2_PIX_FMT_NV12) {
+ } else if (q->fmt->fourcc == V4L2_PIX_FMT_NV12M) {
/* When the image format is planar the bytesperline value
* applies to the first plane and is divided by the same factor
* as the width field for the other planes
@@ -1215,7 +1240,7 @@ static void mxc_jpeg_sizeimage(struct mxc_jpeg_q_data *q)
} else {
q->sizeimage[0] = q->bytesperline[0] * q->h;
q->sizeimage[1] = 0;
- if (q->fmt->fourcc == V4L2_PIX_FMT_NV12)
+ if (q->fmt->fourcc == V4L2_PIX_FMT_NV12M)
q->sizeimage[1] = q->sizeimage[0] / 2;
}
}
@@ -1402,12 +1427,29 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb)
return 0;
}
+static void mxc_jpeg_buf_finish(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct mxc_jpeg_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_queue *q = vb->vb2_queue;
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->type))
+ return;
+ if (!ctx->stopped)
+ return;
+ if (list_empty(&q->done_list)) {
+ vbuf->flags |= V4L2_BUF_FLAG_LAST;
+ ctx->stopped = 0;
+ }
+}
+
static const struct vb2_ops mxc_jpeg_qops = {
.queue_setup = mxc_jpeg_queue_setup,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.buf_out_validate = mxc_jpeg_buf_out_validate,
.buf_prepare = mxc_jpeg_buf_prepare,
+ .buf_finish = mxc_jpeg_buf_finish,
.start_streaming = mxc_jpeg_start_streaming,
.stop_streaming = mxc_jpeg_stop_streaming,
.buf_queue = mxc_jpeg_buf_queue,
@@ -1843,14 +1885,14 @@ static int mxc_jpeg_dqbuf(struct file *file, void *priv,
int ret;
dev_dbg(dev, "DQBUF type=%d, index=%d", buf->type, buf->index);
- if (ctx->stopping == 1 && num_src_ready == 0) {
+ if (ctx->stopping == 1 && num_src_ready == 0) {
/* No more src bufs, notify app EOS */
notify_eos(ctx);
ctx->stopping = 0;
+ mxc_jpeg_set_last_buffer_dequeued(ctx);
}
ret = v4l2_m2m_dqbuf(file, fh->m2m_ctx, buf);
-
return ret;
}
@@ -2016,7 +2058,6 @@ static int mxc_jpeg_probe(struct platform_device *pdev)
for (slot = 0; slot < MXC_MAX_SLOTS; slot++) {
dec_irq = platform_get_irq(pdev, slot);
if (dec_irq < 0) {
- dev_err(&pdev->dev, "Failed to get irq %d\n", dec_irq);
ret = dec_irq;
goto err_irq;
}
diff --git a/drivers/media/platform/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index 9fb2a5aaa941..f53f004ba851 100644
--- a/drivers/media/platform/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -91,6 +91,7 @@ struct mxc_jpeg_ctx {
struct v4l2_fh fh;
enum mxc_jpeg_enc_state enc_state;
unsigned int stopping;
+ unsigned int stopped;
unsigned int slot;
};
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
new file mode 100644
index 000000000000..0a72734db55e
--- /dev/null
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -0,0 +1,1581 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Samsung CSIS MIPI CSI-2 receiver driver.
+ *
+ * The Samsung CSIS IP is a MIPI CSI-2 receiver found in various NXP i.MX7 and
+ * i.MX8 SoCs. The i.MX7 features version 3.3 of the IP, while i.MX8 features
+ * version 3.6.3.
+ *
+ * Copyright (C) 2019 Linaro Ltd
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011 - 2013 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/spinlock.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>
+
+#define CSIS_DRIVER_NAME "imx-mipi-csis"
+
+#define CSIS_PAD_SINK 0
+#define CSIS_PAD_SOURCE 1
+#define CSIS_PADS_NUM 2
+
+#define MIPI_CSIS_DEF_PIX_WIDTH 640
+#define MIPI_CSIS_DEF_PIX_HEIGHT 480
+
+/* Register map definition */
+
+/* CSIS common control */
+#define MIPI_CSIS_CMN_CTRL 0x04
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW BIT(16)
+#define MIPI_CSIS_CMN_CTRL_INTER_MODE BIT(10)
+#define MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL BIT(2)
+#define MIPI_CSIS_CMN_CTRL_RESET BIT(1)
+#define MIPI_CSIS_CMN_CTRL_ENABLE BIT(0)
+
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET 8
+#define MIPI_CSIS_CMN_CTRL_LANE_NR_MASK (3 << 8)
+
+/* CSIS clock control */
+#define MIPI_CSIS_CLK_CTRL 0x08
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH3(x) ((x) << 28)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH2(x) ((x) << 24)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH1(x) ((x) << 20)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(x) ((x) << 16)
+#define MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK (0xf << 4)
+#define MIPI_CSIS_CLK_CTRL_WCLK_SRC BIT(0)
+
+/* CSIS Interrupt mask */
+#define MIPI_CSIS_INT_MSK 0x10
+#define MIPI_CSIS_INT_MSK_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INT_MSK_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INT_MSK_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INT_MSK_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INT_MSK_FRAME_START BIT(24)
+#define MIPI_CSIS_INT_MSK_FRAME_END BIT(20)
+#define MIPI_CSIS_INT_MSK_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INT_MSK_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INT_MSK_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INT_MSK_ERR_OVER BIT(4)
+#define MIPI_CSIS_INT_MSK_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INT_MSK_ERR_ECC BIT(2)
+#define MIPI_CSIS_INT_MSK_ERR_CRC BIT(1)
+#define MIPI_CSIS_INT_MSK_ERR_UNKNOWN BIT(0)
+
+/* CSIS Interrupt source */
+#define MIPI_CSIS_INT_SRC 0x14
+#define MIPI_CSIS_INT_SRC_EVEN_BEFORE BIT(31)
+#define MIPI_CSIS_INT_SRC_EVEN_AFTER BIT(30)
+#define MIPI_CSIS_INT_SRC_EVEN BIT(30)
+#define MIPI_CSIS_INT_SRC_ODD_BEFORE BIT(29)
+#define MIPI_CSIS_INT_SRC_ODD_AFTER BIT(28)
+#define MIPI_CSIS_INT_SRC_ODD (0x3 << 28)
+#define MIPI_CSIS_INT_SRC_NON_IMAGE_DATA (0xf << 28)
+#define MIPI_CSIS_INT_SRC_FRAME_START BIT(24)
+#define MIPI_CSIS_INT_SRC_FRAME_END BIT(20)
+#define MIPI_CSIS_INT_SRC_ERR_SOT_HS BIT(16)
+#define MIPI_CSIS_INT_SRC_ERR_LOST_FS BIT(12)
+#define MIPI_CSIS_INT_SRC_ERR_LOST_FE BIT(8)
+#define MIPI_CSIS_INT_SRC_ERR_OVER BIT(4)
+#define MIPI_CSIS_INT_SRC_ERR_WRONG_CFG BIT(3)
+#define MIPI_CSIS_INT_SRC_ERR_ECC BIT(2)
+#define MIPI_CSIS_INT_SRC_ERR_CRC BIT(1)
+#define MIPI_CSIS_INT_SRC_ERR_UNKNOWN BIT(0)
+#define MIPI_CSIS_INT_SRC_ERRORS 0xfffff
+
+/* D-PHY status control */
+#define MIPI_CSIS_DPHY_STATUS 0x20
+#define MIPI_CSIS_DPHY_STATUS_ULPS_DAT BIT(8)
+#define MIPI_CSIS_DPHY_STATUS_STOPSTATE_DAT BIT(4)
+#define MIPI_CSIS_DPHY_STATUS_ULPS_CLK BIT(1)
+#define MIPI_CSIS_DPHY_STATUS_STOPSTATE_CLK BIT(0)
+
+/* D-PHY common control */
+#define MIPI_CSIS_DPHY_CMN_CTRL 0x24
+#define MIPI_CSIS_DPHY_CMN_CTRL_HSSETTLE(n) ((n) << 24)
+#define MIPI_CSIS_DPHY_CMN_CTRL_HSSETTLE_MASK GENMASK(31, 24)
+#define MIPI_CSIS_DPHY_CMN_CTRL_CLKSETTLE(n) ((n) << 22)
+#define MIPI_CSIS_DPHY_CMN_CTRL_CLKSETTLE_MASK GENMASK(23, 22)
+#define MIPI_CSIS_DPHY_CMN_CTRL_DPDN_SWAP_CLK BIT(6)
+#define MIPI_CSIS_DPHY_CMN_CTRL_DPDN_SWAP_DAT BIT(5)
+#define MIPI_CSIS_DPHY_CMN_CTRL_ENABLE_DAT BIT(1)
+#define MIPI_CSIS_DPHY_CMN_CTRL_ENABLE_CLK BIT(0)
+#define MIPI_CSIS_DPHY_CMN_CTRL_ENABLE (0x1f << 0)
+
+/* D-PHY Master and Slave Control register Low */
+#define MIPI_CSIS_DPHY_BCTRL_L 0x30
+#define MIPI_CSIS_DPHY_BCTRL_L_USER_DATA_PATTERN_LOW(n) (((n) & 3U) << 30)
+#define MIPI_CSIS_DPHY_BCTRL_L_BIAS_REF_VOLT_715MV (0 << 28)
+#define MIPI_CSIS_DPHY_BCTRL_L_BIAS_REF_VOLT_724MV (1 << 28)
+#define MIPI_CSIS_DPHY_BCTRL_L_BIAS_REF_VOLT_733MV (2 << 28)
+#define MIPI_CSIS_DPHY_BCTRL_L_BIAS_REF_VOLT_706MV (3 << 28)
+#define MIPI_CSIS_DPHY_BCTRL_L_BGR_CHOPPER_FREQ_3MHZ (0 << 27)
+#define MIPI_CSIS_DPHY_BCTRL_L_BGR_CHOPPER_FREQ_1_5MHZ (1 << 27)
+#define MIPI_CSIS_DPHY_BCTRL_L_VREG12_EXTPWR_EN_CTL BIT(26)
+#define MIPI_CSIS_DPHY_BCTRL_L_REG_12P_LVL_CTL_1_2V (0 << 24)
+#define MIPI_CSIS_DPHY_BCTRL_L_REG_12P_LVL_CTL_1_23V (1 << 24)
+#define MIPI_CSIS_DPHY_BCTRL_L_REG_12P_LVL_CTL_1_17V (2 << 24)
+#define MIPI_CSIS_DPHY_BCTRL_L_REG_12P_LVL_CTL_1_26V (3 << 24)
+#define MIPI_CSIS_DPHY_BCTRL_L_REG_1P2_LVL_SEL BIT(23)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_HYS_LVL_80MV (0 << 21)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_HYS_LVL_100MV (1 << 21)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_HYS_LVL_120MV (2 << 21)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_HYS_LVL_140MV (3 << 21)
+#define MIPI_CSIS_DPHY_BCTRL_L_VREF_SRC_SEL BIT(20)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_VREF_LVL_715MV (0 << 18)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_VREF_LVL_743MV (1 << 18)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_VREF_LVL_650MV (2 << 18)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_VREF_LVL_682MV (3 << 18)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_RX_PULSE_REJECT BIT(17)
+#define MIPI_CSIS_DPHY_BCTRL_L_MSTRCLK_LP_SLEW_RATE_DOWN_0 (0 << 15)
+#define MIPI_CSIS_DPHY_BCTRL_L_MSTRCLK_LP_SLEW_RATE_DOWN_15P (1 << 15)
+#define MIPI_CSIS_DPHY_BCTRL_L_MSTRCLK_LP_SLEW_RATE_DOWN_30P (3 << 15)
+#define MIPI_CSIS_DPHY_BCTRL_L_MSTRCLK_LP_SLEW_RATE_UP BIT(14)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_CD_HYS_60MV (0 << 13)
+#define MIPI_CSIS_DPHY_BCTRL_L_LP_CD_HYS_70MV (1 << 13)
+#define MIPI_CSIS_DPHY_BCTRL_L_BGR_CHOPPER_EN BIT(12)
+#define MIPI_CSIS_DPHY_BCTRL_L_ERRCONTENTION_LP_EN BIT(11)
+#define MIPI_CSIS_DPHY_BCTRL_L_TXTRIGGER_CLK_EN BIT(10)
+#define MIPI_CSIS_DPHY_BCTRL_L_B_DPHYCTRL(n) (((n) * 25 / 1000000) << 0)
+
+/* D-PHY Master and Slave Control register High */
+#define MIPI_CSIS_DPHY_BCTRL_H 0x34
+/* D-PHY Slave Control register Low */
+#define MIPI_CSIS_DPHY_SCTRL_L 0x38
+/* D-PHY Slave Control register High */
+#define MIPI_CSIS_DPHY_SCTRL_H 0x3c
+
+/* ISP Configuration register */
+#define MIPI_CSIS_ISP_CONFIG_CH(n) (0x40 + (n) * 0x10)
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP_MSK (0xff << 24)
+#define MIPI_CSIS_ISPCFG_MEM_FULL_GAP(x) ((x) << 24)
+#define MIPI_CSIS_ISPCFG_PIXEL_MODE_SINGLE (0 << 12)
+#define MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL (1 << 12)
+#define MIPI_CSIS_ISPCFG_PIXEL_MODE_QUAD (2 << 12) /* i.MX8M[MNP] only */
+#define MIPI_CSIS_ISPCFG_PIXEL_MASK (3 << 12)
+#define MIPI_CSIS_ISPCFG_ALIGN_32BIT BIT(11)
+#define MIPI_CSIS_ISPCFG_FMT(fmt) ((fmt) << 2)
+#define MIPI_CSIS_ISPCFG_FMT_MASK (0x3f << 2)
+
+/* ISP Image Resolution register */
+#define MIPI_CSIS_ISP_RESOL_CH(n) (0x44 + (n) * 0x10)
+#define CSIS_MAX_PIX_WIDTH 0xffff
+#define CSIS_MAX_PIX_HEIGHT 0xffff
+
+/* ISP SYNC register */
+#define MIPI_CSIS_ISP_SYNC_CH(n) (0x48 + (n) * 0x10)
+#define MIPI_CSIS_ISP_SYNC_HSYNC_LINTV_OFFSET 18
+#define MIPI_CSIS_ISP_SYNC_VSYNC_SINTV_OFFSET 12
+#define MIPI_CSIS_ISP_SYNC_VSYNC_EINTV_OFFSET 0
+
+/* ISP shadow registers */
+#define MIPI_CSIS_SDW_CONFIG_CH(n) (0x80 + (n) * 0x10)
+#define MIPI_CSIS_SDW_RESOL_CH(n) (0x84 + (n) * 0x10)
+#define MIPI_CSIS_SDW_SYNC_CH(n) (0x88 + (n) * 0x10)
+
+/* Debug control register */
+#define MIPI_CSIS_DBG_CTRL 0xc0
+#define MIPI_CSIS_DBG_INTR_MSK 0xc4
+#define MIPI_CSIS_DBG_INTR_MSK_DT_NOT_SUPPORT BIT(25)
+#define MIPI_CSIS_DBG_INTR_MSK_DT_IGNORE BIT(24)
+#define MIPI_CSIS_DBG_INTR_MSK_ERR_FRAME_SIZE BIT(20)
+#define MIPI_CSIS_DBG_INTR_MSK_TRUNCATED_FRAME BIT(16)
+#define MIPI_CSIS_DBG_INTR_MSK_EARLY_FE BIT(12)
+#define MIPI_CSIS_DBG_INTR_MSK_EARLY_FS BIT(8)
+#define MIPI_CSIS_DBG_INTR_MSK_CAM_VSYNC_FALL BIT(4)
+#define MIPI_CSIS_DBG_INTR_MSK_CAM_VSYNC_RISE BIT(0)
+#define MIPI_CSIS_DBG_INTR_SRC 0xc8
+#define MIPI_CSIS_DBG_INTR_SRC_DT_NOT_SUPPORT BIT(25)
+#define MIPI_CSIS_DBG_INTR_SRC_DT_IGNORE BIT(24)
+#define MIPI_CSIS_DBG_INTR_SRC_ERR_FRAME_SIZE BIT(20)
+#define MIPI_CSIS_DBG_INTR_SRC_TRUNCATED_FRAME BIT(16)
+#define MIPI_CSIS_DBG_INTR_SRC_EARLY_FE BIT(12)
+#define MIPI_CSIS_DBG_INTR_SRC_EARLY_FS BIT(8)
+#define MIPI_CSIS_DBG_INTR_SRC_CAM_VSYNC_FALL BIT(4)
+#define MIPI_CSIS_DBG_INTR_SRC_CAM_VSYNC_RISE BIT(0)
+
+#define MIPI_CSIS_FRAME_COUNTER_CH(n) (0x0100 + (n) * 4)
+
+/* Non-image packet data buffers */
+#define MIPI_CSIS_PKTDATA_ODD 0x2000
+#define MIPI_CSIS_PKTDATA_EVEN 0x3000
+#define MIPI_CSIS_PKTDATA_SIZE SZ_4K
+
+#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
+
+/* MIPI CSI-2 Data Types */
+#define MIPI_CSI2_DATA_TYPE_YUV420_8 0x18
+#define MIPI_CSI2_DATA_TYPE_YUV420_10 0x19
+#define MIPI_CSI2_DATA_TYPE_LE_YUV420_8 0x1a
+#define MIPI_CSI2_DATA_TYPE_CS_YUV420_8 0x1c
+#define MIPI_CSI2_DATA_TYPE_CS_YUV420_10 0x1d
+#define MIPI_CSI2_DATA_TYPE_YUV422_8 0x1e
+#define MIPI_CSI2_DATA_TYPE_YUV422_10 0x1f
+#define MIPI_CSI2_DATA_TYPE_RGB565 0x22
+#define MIPI_CSI2_DATA_TYPE_RGB666 0x23
+#define MIPI_CSI2_DATA_TYPE_RGB888 0x24
+#define MIPI_CSI2_DATA_TYPE_RAW6 0x28
+#define MIPI_CSI2_DATA_TYPE_RAW7 0x29
+#define MIPI_CSI2_DATA_TYPE_RAW8 0x2a
+#define MIPI_CSI2_DATA_TYPE_RAW10 0x2b
+#define MIPI_CSI2_DATA_TYPE_RAW12 0x2c
+#define MIPI_CSI2_DATA_TYPE_RAW14 0x2d
+#define MIPI_CSI2_DATA_TYPE_USER(x) (0x30 + (x))
+
+enum {
+ ST_POWERED = 1,
+ ST_STREAMING = 2,
+ ST_SUSPENDED = 4,
+};
+
+struct mipi_csis_event {
+ bool debug;
+ u32 mask;
+ const char * const name;
+ unsigned int counter;
+};
+
+static const struct mipi_csis_event mipi_csis_events[] = {
+ /* Errors */
+ { false, MIPI_CSIS_INT_SRC_ERR_SOT_HS, "SOT Error" },
+ { false, MIPI_CSIS_INT_SRC_ERR_LOST_FS, "Lost Frame Start Error" },
+ { false, MIPI_CSIS_INT_SRC_ERR_LOST_FE, "Lost Frame End Error" },
+ { false, MIPI_CSIS_INT_SRC_ERR_OVER, "FIFO Overflow Error" },
+ { false, MIPI_CSIS_INT_SRC_ERR_WRONG_CFG, "Wrong Configuration Error" },
+ { false, MIPI_CSIS_INT_SRC_ERR_ECC, "ECC Error" },
+ { false, MIPI_CSIS_INT_SRC_ERR_CRC, "CRC Error" },
+ { false, MIPI_CSIS_INT_SRC_ERR_UNKNOWN, "Unknown Error" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_DT_NOT_SUPPORT, "Data Type Not Supported" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_DT_IGNORE, "Data Type Ignored" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_ERR_FRAME_SIZE, "Frame Size Error" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_TRUNCATED_FRAME, "Truncated Frame" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_EARLY_FE, "Early Frame End" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_EARLY_FS, "Early Frame Start" },
+ /* Non-image data receive events */
+ { false, MIPI_CSIS_INT_SRC_EVEN_BEFORE, "Non-image data before even frame" },
+ { false, MIPI_CSIS_INT_SRC_EVEN_AFTER, "Non-image data after even frame" },
+ { false, MIPI_CSIS_INT_SRC_ODD_BEFORE, "Non-image data before odd frame" },
+ { false, MIPI_CSIS_INT_SRC_ODD_AFTER, "Non-image data after odd frame" },
+ /* Frame start/end */
+ { false, MIPI_CSIS_INT_SRC_FRAME_START, "Frame Start" },
+ { false, MIPI_CSIS_INT_SRC_FRAME_END, "Frame End" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_CAM_VSYNC_FALL, "VSYNC Falling Edge" },
+ { true, MIPI_CSIS_DBG_INTR_SRC_CAM_VSYNC_RISE, "VSYNC Rising Edge" },
+};
+
+#define MIPI_CSIS_NUM_EVENTS ARRAY_SIZE(mipi_csis_events)
+
+enum mipi_csis_clk {
+ MIPI_CSIS_CLK_PCLK,
+ MIPI_CSIS_CLK_WRAP,
+ MIPI_CSIS_CLK_PHY,
+ MIPI_CSIS_CLK_AXI,
+};
+
+static const char * const mipi_csis_clk_id[] = {
+ "pclk",
+ "wrap",
+ "phy",
+ "axi",
+};
+
+enum mipi_csis_version {
+ MIPI_CSIS_V3_3,
+ MIPI_CSIS_V3_6_3,
+};
+
+struct mipi_csis_info {
+ enum mipi_csis_version version;
+ unsigned int num_clocks;
+};
+
+struct csi_state {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk_bulk_data *clks;
+ struct reset_control *mrst;
+ struct regulator *mipi_phy_regulator;
+ const struct mipi_csis_info *info;
+
+ struct v4l2_subdev sd;
+ struct media_pad pads[CSIS_PADS_NUM];
+ struct v4l2_async_notifier notifier;
+ struct v4l2_subdev *src_sd;
+
+ struct v4l2_mbus_config_mipi_csi2 bus;
+ u32 clk_frequency;
+ u32 hs_settle;
+ u32 clk_settle;
+
+ struct mutex lock; /* Protect csis_fmt, format_mbus and state */
+ const struct csis_pix_format *csis_fmt;
+ struct v4l2_mbus_framefmt format_mbus[CSIS_PADS_NUM];
+ u32 state;
+
+ spinlock_t slock; /* Protect events */
+ struct mipi_csis_event events[MIPI_CSIS_NUM_EVENTS];
+ struct dentry *debugfs_root;
+ struct {
+ bool enable;
+ u32 hs_settle;
+ u32 clk_settle;
+ } debug;
+};
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+struct csis_pix_format {
+ u32 code;
+ u32 output;
+ u32 data_type;
+ u8 width;
+};
+
+static const struct csis_pix_format mipi_csis_formats[] = {
+ /* YUV formats. */
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .output = MEDIA_BUS_FMT_UYVY8_1X16,
+ .data_type = MIPI_CSI2_DATA_TYPE_YUV422_8,
+ .width = 16,
+ },
+ /* RGB formats. */
+ {
+ .code = MEDIA_BUS_FMT_RGB565_1X16,
+ .output = MEDIA_BUS_FMT_RGB565_1X16,
+ .data_type = MIPI_CSI2_DATA_TYPE_RGB565,
+ .width = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .output = MEDIA_BUS_FMT_RGB888_1X24,
+ .data_type = MIPI_CSI2_DATA_TYPE_RGB888,
+ .width = 24,
+ },
+ /* RAW (Bayer and greyscale) formats. */
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .output = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .output = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .output = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .output = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .output = MEDIA_BUS_FMT_Y8_1X8,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .width = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .output = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .output = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .output = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .output = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .output = MEDIA_BUS_FMT_Y10_1X10,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .width = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .output = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .output = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .output = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .output = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_Y12_1X12,
+ .output = MEDIA_BUS_FMT_Y12_1X12,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .width = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .output = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .output = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .output = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .width = 14,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .output = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .width = 14,
+ }
+};
+
+static const struct csis_pix_format *find_csis_format(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(mipi_csis_formats); i++)
+ if (code == mipi_csis_formats[i].code)
+ return &mipi_csis_formats[i];
+ return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware configuration
+ */
+
+static inline u32 mipi_csis_read(struct csi_state *state, u32 reg)
+{
+ return readl(state->regs + reg);
+}
+
+static inline void mipi_csis_write(struct csi_state *state, u32 reg, u32 val)
+{
+ writel(val, state->regs + reg);
+}
+
+static void mipi_csis_enable_interrupts(struct csi_state *state, bool on)
+{
+ mipi_csis_write(state, MIPI_CSIS_INT_MSK, on ? 0xffffffff : 0);
+ mipi_csis_write(state, MIPI_CSIS_DBG_INTR_MSK, on ? 0xffffffff : 0);
+}
+
+static void mipi_csis_sw_reset(struct csi_state *state)
+{
+ u32 val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL,
+ val | MIPI_CSIS_CMN_CTRL_RESET);
+ usleep_range(10, 20);
+}
+
+static void mipi_csis_system_enable(struct csi_state *state, int on)
+{
+ u32 val, mask;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ if (on)
+ val |= MIPI_CSIS_CMN_CTRL_ENABLE;
+ else
+ val &= ~MIPI_CSIS_CMN_CTRL_ENABLE;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_DPHY_CMN_CTRL);
+ val &= ~MIPI_CSIS_DPHY_CMN_CTRL_ENABLE;
+ if (on) {
+ mask = (1 << (state->bus.num_data_lanes + 1)) - 1;
+ val |= (mask & MIPI_CSIS_DPHY_CMN_CTRL_ENABLE);
+ }
+ mipi_csis_write(state, MIPI_CSIS_DPHY_CMN_CTRL, val);
+}
+
+/* Called with the state.lock mutex held */
+static void __mipi_csis_set_format(struct csi_state *state)
+{
+ struct v4l2_mbus_framefmt *mf = &state->format_mbus[CSIS_PAD_SINK];
+ u32 val;
+
+ /* Color format */
+ val = mipi_csis_read(state, MIPI_CSIS_ISP_CONFIG_CH(0));
+ val &= ~(MIPI_CSIS_ISPCFG_ALIGN_32BIT | MIPI_CSIS_ISPCFG_FMT_MASK
+ | MIPI_CSIS_ISPCFG_PIXEL_MASK);
+
+ /*
+ * YUV 4:2:2 can be transferred with 8 or 16 bits per clock sample
+ * (referred to in the documentation as single and dual pixel modes
+ * respectively, although the 8-bit mode transfers half a pixel per
+ * clock sample and the 16-bit mode one pixel). While both mode work
+ * when the CSIS is connected to a receiver that supports either option,
+ * single pixel mode requires clock rates twice as high. As all SoCs
+ * that integrate the CSIS can operate in 16-bit bit mode, and some do
+ * not support 8-bit mode (this is the case of the i.MX8MP), use dual
+ * pixel mode unconditionally.
+ *
+ * TODO: Verify which other formats require DUAL (or QUAD) modes.
+ */
+ if (state->csis_fmt->data_type == MIPI_CSI2_DATA_TYPE_YUV422_8)
+ val |= MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL;
+
+ val |= MIPI_CSIS_ISPCFG_FMT(state->csis_fmt->data_type);
+ mipi_csis_write(state, MIPI_CSIS_ISP_CONFIG_CH(0), val);
+
+ /* Pixel resolution */
+ val = mf->width | (mf->height << 16);
+ mipi_csis_write(state, MIPI_CSIS_ISP_RESOL_CH(0), val);
+}
+
+static int mipi_csis_calculate_params(struct csi_state *state)
+{
+ s64 link_freq;
+ u32 lane_rate;
+
+ /* Calculate the line rate from the pixel rate. */
+ link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler,
+ state->csis_fmt->width,
+ state->bus.num_data_lanes * 2);
+ if (link_freq < 0) {
+ dev_err(state->dev, "Unable to obtain link frequency: %d\n",
+ (int)link_freq);
+ return link_freq;
+ }
+
+ lane_rate = link_freq * 2;
+
+ if (lane_rate < 80000000 || lane_rate > 1500000000) {
+ dev_dbg(state->dev, "Out-of-bound lane rate %u\n", lane_rate);
+ return -EINVAL;
+ }
+
+ /*
+ * The HSSETTLE counter value is document in a table, but can also
+ * easily be calculated. Hardcode the CLKSETTLE value to 0 for now
+ * (which is documented as corresponding to CSI-2 v0.87 to v1.00) until
+ * we figure out how to compute it correctly.
+ */
+ state->hs_settle = (lane_rate - 5000000) / 45000000;
+ state->clk_settle = 0;
+
+ dev_dbg(state->dev, "lane rate %u, Tclk_settle %u, Ths_settle %u\n",
+ lane_rate, state->clk_settle, state->hs_settle);
+
+ if (state->debug.hs_settle < 0xff) {
+ dev_dbg(state->dev, "overriding Ths_settle with %u\n",
+ state->debug.hs_settle);
+ state->hs_settle = state->debug.hs_settle;
+ }
+
+ if (state->debug.clk_settle < 4) {
+ dev_dbg(state->dev, "overriding Tclk_settle with %u\n",
+ state->debug.clk_settle);
+ state->clk_settle = state->debug.clk_settle;
+ }
+
+ return 0;
+}
+
+static void mipi_csis_set_params(struct csi_state *state)
+{
+ int lanes = state->bus.num_data_lanes;
+ u32 val;
+
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ val &= ~MIPI_CSIS_CMN_CTRL_LANE_NR_MASK;
+ val |= (lanes - 1) << MIPI_CSIS_CMN_CTRL_LANE_NR_OFFSET;
+ if (state->info->version == MIPI_CSIS_V3_3)
+ val |= MIPI_CSIS_CMN_CTRL_INTER_MODE;
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL, val);
+
+ __mipi_csis_set_format(state);
+
+ mipi_csis_write(state, MIPI_CSIS_DPHY_CMN_CTRL,
+ MIPI_CSIS_DPHY_CMN_CTRL_HSSETTLE(state->hs_settle) |
+ MIPI_CSIS_DPHY_CMN_CTRL_CLKSETTLE(state->clk_settle));
+
+ val = (0 << MIPI_CSIS_ISP_SYNC_HSYNC_LINTV_OFFSET)
+ | (0 << MIPI_CSIS_ISP_SYNC_VSYNC_SINTV_OFFSET)
+ | (0 << MIPI_CSIS_ISP_SYNC_VSYNC_EINTV_OFFSET);
+ mipi_csis_write(state, MIPI_CSIS_ISP_SYNC_CH(0), val);
+
+ val = mipi_csis_read(state, MIPI_CSIS_CLK_CTRL);
+ val |= MIPI_CSIS_CLK_CTRL_WCLK_SRC;
+ val |= MIPI_CSIS_CLK_CTRL_CLKGATE_TRAIL_CH0(15);
+ val &= ~MIPI_CSIS_CLK_CTRL_CLKGATE_EN_MSK;
+ mipi_csis_write(state, MIPI_CSIS_CLK_CTRL, val);
+
+ mipi_csis_write(state, MIPI_CSIS_DPHY_BCTRL_L,
+ MIPI_CSIS_DPHY_BCTRL_L_BIAS_REF_VOLT_715MV |
+ MIPI_CSIS_DPHY_BCTRL_L_BGR_CHOPPER_FREQ_3MHZ |
+ MIPI_CSIS_DPHY_BCTRL_L_REG_12P_LVL_CTL_1_2V |
+ MIPI_CSIS_DPHY_BCTRL_L_LP_RX_HYS_LVL_80MV |
+ MIPI_CSIS_DPHY_BCTRL_L_LP_RX_VREF_LVL_715MV |
+ MIPI_CSIS_DPHY_BCTRL_L_LP_CD_HYS_60MV |
+ MIPI_CSIS_DPHY_BCTRL_L_B_DPHYCTRL(20000000));
+ mipi_csis_write(state, MIPI_CSIS_DPHY_BCTRL_H, 0);
+
+ /* Update the shadow register. */
+ val = mipi_csis_read(state, MIPI_CSIS_CMN_CTRL);
+ mipi_csis_write(state, MIPI_CSIS_CMN_CTRL,
+ val | MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW |
+ MIPI_CSIS_CMN_CTRL_UPDATE_SHADOW_CTRL);
+}
+
+static int mipi_csis_clk_enable(struct csi_state *state)
+{
+ return clk_bulk_prepare_enable(state->info->num_clocks, state->clks);
+}
+
+static void mipi_csis_clk_disable(struct csi_state *state)
+{
+ clk_bulk_disable_unprepare(state->info->num_clocks, state->clks);
+}
+
+static int mipi_csis_clk_get(struct csi_state *state)
+{
+ unsigned int i;
+ int ret;
+
+ state->clks = devm_kcalloc(state->dev, state->info->num_clocks,
+ sizeof(*state->clks), GFP_KERNEL);
+
+ if (!state->clks)
+ return -ENOMEM;
+
+ for (i = 0; i < state->info->num_clocks; i++)
+ state->clks[i].id = mipi_csis_clk_id[i];
+
+ ret = devm_clk_bulk_get(state->dev, state->info->num_clocks,
+ state->clks);
+ if (ret < 0)
+ return ret;
+
+ /* Set clock rate */
+ ret = clk_set_rate(state->clks[MIPI_CSIS_CLK_WRAP].clk,
+ state->clk_frequency);
+ if (ret < 0)
+ dev_err(state->dev, "set rate=%d failed: %d\n",
+ state->clk_frequency, ret);
+
+ return ret;
+}
+
+static void mipi_csis_start_stream(struct csi_state *state)
+{
+ mipi_csis_sw_reset(state);
+ mipi_csis_set_params(state);
+ mipi_csis_system_enable(state, true);
+ mipi_csis_enable_interrupts(state, true);
+}
+
+static void mipi_csis_stop_stream(struct csi_state *state)
+{
+ mipi_csis_enable_interrupts(state, false);
+ mipi_csis_system_enable(state, false);
+}
+
+static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
+{
+ struct csi_state *state = dev_id;
+ unsigned long flags;
+ unsigned int i;
+ u32 status;
+ u32 dbg_status;
+
+ status = mipi_csis_read(state, MIPI_CSIS_INT_SRC);
+ dbg_status = mipi_csis_read(state, MIPI_CSIS_DBG_INTR_SRC);
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ /* Update the event/error counters */
+ if ((status & MIPI_CSIS_INT_SRC_ERRORS) || state->debug.enable) {
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++) {
+ struct mipi_csis_event *event = &state->events[i];
+
+ if ((!event->debug && (status & event->mask)) ||
+ (event->debug && (dbg_status & event->mask)))
+ event->counter++;
+ }
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+
+ mipi_csis_write(state, MIPI_CSIS_INT_SRC, status);
+ mipi_csis_write(state, MIPI_CSIS_DBG_INTR_SRC, dbg_status);
+
+ return IRQ_HANDLED;
+}
+
+/* -----------------------------------------------------------------------------
+ * PHY regulator and reset
+ */
+
+static int mipi_csis_phy_enable(struct csi_state *state)
+{
+ if (state->info->version != MIPI_CSIS_V3_3)
+ return 0;
+
+ return regulator_enable(state->mipi_phy_regulator);
+}
+
+static int mipi_csis_phy_disable(struct csi_state *state)
+{
+ if (state->info->version != MIPI_CSIS_V3_3)
+ return 0;
+
+ return regulator_disable(state->mipi_phy_regulator);
+}
+
+static void mipi_csis_phy_reset(struct csi_state *state)
+{
+ if (state->info->version != MIPI_CSIS_V3_3)
+ return;
+
+ reset_control_assert(state->mrst);
+ msleep(20);
+ reset_control_deassert(state->mrst);
+}
+
+static int mipi_csis_phy_init(struct csi_state *state)
+{
+ if (state->info->version != MIPI_CSIS_V3_3)
+ return 0;
+
+ /* Get MIPI PHY reset and regulator. */
+ state->mrst = devm_reset_control_get_exclusive(state->dev, NULL);
+ if (IS_ERR(state->mrst))
+ return PTR_ERR(state->mrst);
+
+ state->mipi_phy_regulator = devm_regulator_get(state->dev, "phy");
+ if (IS_ERR(state->mipi_phy_regulator))
+ return PTR_ERR(state->mipi_phy_regulator);
+
+ return regulator_set_voltage(state->mipi_phy_regulator, 1000000,
+ 1000000);
+}
+
+/* -----------------------------------------------------------------------------
+ * Debug
+ */
+
+static void mipi_csis_clear_counters(struct csi_state *state)
+{
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&state->slock, flags);
+ for (i = 0; i < MIPI_CSIS_NUM_EVENTS; i++)
+ state->events[i].counter = 0;
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+static void mipi_csis_log_counters(struct csi_state *state, bool non_errors)
+{
+ unsigned int num_events = non_errors ? MIPI_CSIS_NUM_EVENTS
+ : MIPI_CSIS_NUM_EVENTS - 8;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&state->slock, flags);
+
+ for (i = 0; i < num_events; ++i) {
+ if (state->events[i].counter > 0 || state->debug.enable)
+ dev_info(state->dev, "%s events: %d\n",
+ state->events[i].name,
+ state->events[i].counter);
+ }
+ spin_unlock_irqrestore(&state->slock, flags);
+}
+
+static int mipi_csis_dump_regs(struct csi_state *state)
+{
+ static const struct {
+ u32 offset;
+ const char * const name;
+ } registers[] = {
+ { MIPI_CSIS_CMN_CTRL, "CMN_CTRL" },
+ { MIPI_CSIS_CLK_CTRL, "CLK_CTRL" },
+ { MIPI_CSIS_INT_MSK, "INT_MSK" },
+ { MIPI_CSIS_DPHY_STATUS, "DPHY_STATUS" },
+ { MIPI_CSIS_DPHY_CMN_CTRL, "DPHY_CMN_CTRL" },
+ { MIPI_CSIS_DPHY_SCTRL_L, "DPHY_SCTRL_L" },
+ { MIPI_CSIS_DPHY_SCTRL_H, "DPHY_SCTRL_H" },
+ { MIPI_CSIS_ISP_CONFIG_CH(0), "ISP_CONFIG_CH0" },
+ { MIPI_CSIS_ISP_RESOL_CH(0), "ISP_RESOL_CH0" },
+ { MIPI_CSIS_SDW_CONFIG_CH(0), "SDW_CONFIG_CH0" },
+ { MIPI_CSIS_SDW_RESOL_CH(0), "SDW_RESOL_CH0" },
+ { MIPI_CSIS_DBG_CTRL, "DBG_CTRL" },
+ { MIPI_CSIS_FRAME_COUNTER_CH(0), "FRAME_COUNTER_CH0" },
+ };
+
+ unsigned int i;
+ u32 cfg;
+
+ dev_info(state->dev, "--- REGISTERS ---\n");
+
+ for (i = 0; i < ARRAY_SIZE(registers); i++) {
+ cfg = mipi_csis_read(state, registers[i].offset);
+ dev_info(state->dev, "%14s: 0x%08x\n", registers[i].name, cfg);
+ }
+
+ return 0;
+}
+
+static int mipi_csis_dump_regs_show(struct seq_file *m, void *private)
+{
+ struct csi_state *state = m->private;
+
+ return mipi_csis_dump_regs(state);
+}
+DEFINE_SHOW_ATTRIBUTE(mipi_csis_dump_regs);
+
+static void mipi_csis_debugfs_init(struct csi_state *state)
+{
+ state->debug.hs_settle = UINT_MAX;
+ state->debug.clk_settle = UINT_MAX;
+
+ state->debugfs_root = debugfs_create_dir(dev_name(state->dev), NULL);
+
+ debugfs_create_bool("debug_enable", 0600, state->debugfs_root,
+ &state->debug.enable);
+ debugfs_create_file("dump_regs", 0600, state->debugfs_root, state,
+ &mipi_csis_dump_regs_fops);
+ debugfs_create_u32("tclk_settle", 0600, state->debugfs_root,
+ &state->debug.clk_settle);
+ debugfs_create_u32("ths_settle", 0600, state->debugfs_root,
+ &state->debug.hs_settle);
+}
+
+static void mipi_csis_debugfs_exit(struct csi_state *state)
+{
+ debugfs_remove_recursive(state->debugfs_root);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static struct csi_state *mipi_sd_to_csis_state(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_state, sd);
+}
+
+static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+ int ret;
+
+ if (enable) {
+ ret = mipi_csis_calculate_params(state);
+ if (ret < 0)
+ return ret;
+
+ mipi_csis_clear_counters(state);
+
+ ret = pm_runtime_resume_and_get(state->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = v4l2_subdev_call(state->src_sd, core, s_power, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ goto done;
+ }
+
+ mutex_lock(&state->lock);
+
+ if (enable) {
+ if (state->state & ST_SUSPENDED) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ mipi_csis_start_stream(state);
+ ret = v4l2_subdev_call(state->src_sd, video, s_stream, 1);
+ if (ret < 0)
+ goto unlock;
+
+ mipi_csis_log_counters(state, true);
+
+ state->state |= ST_STREAMING;
+ } else {
+ v4l2_subdev_call(state->src_sd, video, s_stream, 0);
+ ret = v4l2_subdev_call(state->src_sd, core, s_power, 0);
+ if (ret == -ENOIOCTLCMD)
+ ret = 0;
+ mipi_csis_stop_stream(state);
+ state->state &= ~ST_STREAMING;
+ if (state->debug.enable)
+ mipi_csis_log_counters(state, true);
+ }
+
+unlock:
+ mutex_unlock(&state->lock);
+
+done:
+ if (!enable || ret < 0)
+ pm_runtime_put(state->dev);
+
+ return ret;
+}
+
+static struct v4l2_mbus_framefmt *
+mipi_csis_get_format(struct csi_state *state,
+ struct v4l2_subdev_state *sd_state,
+ enum v4l2_subdev_format_whence which,
+ unsigned int pad)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_get_try_format(&state->sd, sd_state, pad);
+
+ return &state->format_mbus[pad];
+}
+
+static int mipi_csis_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+ struct v4l2_mbus_framefmt *fmt_sink;
+ struct v4l2_mbus_framefmt *fmt_source;
+ enum v4l2_subdev_format_whence which;
+
+ which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt_sink = mipi_csis_get_format(state, sd_state, which, CSIS_PAD_SINK);
+
+ fmt_sink->code = MEDIA_BUS_FMT_UYVY8_1X16;
+ fmt_sink->width = MIPI_CSIS_DEF_PIX_WIDTH;
+ fmt_sink->height = MIPI_CSIS_DEF_PIX_HEIGHT;
+ fmt_sink->field = V4L2_FIELD_NONE;
+
+ fmt_sink->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ fmt_sink->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt_sink->colorspace);
+ fmt_sink->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt_sink->colorspace);
+ fmt_sink->quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt_sink->colorspace,
+ fmt_sink->ycbcr_enc);
+
+ /*
+ * When called from mipi_csis_subdev_init() to initialize the active
+ * configuration, cfg is NULL, which indicates there's no source pad
+ * configuration to set.
+ */
+ if (!sd_state)
+ return 0;
+
+ fmt_source = mipi_csis_get_format(state, sd_state, which,
+ CSIS_PAD_SOURCE);
+ *fmt_source = *fmt_sink;
+
+ return 0;
+}
+
+static int mipi_csis_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = mipi_csis_get_format(state, sd_state, sdformat->which,
+ sdformat->pad);
+
+ mutex_lock(&state->lock);
+ sdformat->format = *fmt;
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+
+ /*
+ * The CSIS can't transcode in any way, the source format is identical
+ * to the sink format.
+ */
+ if (code->pad == CSIS_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ fmt = mipi_csis_get_format(state, sd_state, code->which,
+ code->pad);
+ code->code = fmt->code;
+ return 0;
+ }
+
+ if (code->pad != CSIS_PAD_SINK)
+ return -EINVAL;
+
+ if (code->index >= ARRAY_SIZE(mipi_csis_formats))
+ return -EINVAL;
+
+ code->code = mipi_csis_formats[code->index].code;
+
+ return 0;
+}
+
+static int mipi_csis_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+ struct csis_pix_format const *csis_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int align;
+
+ /*
+ * The CSIS can't transcode in any way, the source format can't be
+ * modified.
+ */
+ if (sdformat->pad == CSIS_PAD_SOURCE)
+ return mipi_csis_get_fmt(sd, sd_state, sdformat);
+
+ if (sdformat->pad != CSIS_PAD_SINK)
+ return -EINVAL;
+
+ /*
+ * Validate the media bus code and clamp and align the size.
+ *
+ * The total number of bits per line must be a multiple of 8. We thus
+ * need to align the width for formats that are not multiples of 8
+ * bits.
+ */
+ csis_fmt = find_csis_format(sdformat->format.code);
+ if (!csis_fmt)
+ csis_fmt = &mipi_csis_formats[0];
+
+ switch (csis_fmt->width % 8) {
+ case 0:
+ align = 0;
+ break;
+ case 4:
+ align = 1;
+ break;
+ case 2:
+ case 6:
+ align = 2;
+ break;
+ default:
+ /* 1, 3, 5, 7 */
+ align = 3;
+ break;
+ }
+
+ v4l_bound_align_image(&sdformat->format.width, 1,
+ CSIS_MAX_PIX_WIDTH, align,
+ &sdformat->format.height, 1,
+ CSIS_MAX_PIX_HEIGHT, 0, 0);
+
+ fmt = mipi_csis_get_format(state, sd_state, sdformat->which,
+ sdformat->pad);
+
+ mutex_lock(&state->lock);
+
+ fmt->code = csis_fmt->code;
+ fmt->width = sdformat->format.width;
+ fmt->height = sdformat->format.height;
+ fmt->colorspace = sdformat->format.colorspace;
+ fmt->quantization = sdformat->format.quantization;
+ fmt->xfer_func = sdformat->format.xfer_func;
+ fmt->ycbcr_enc = sdformat->format.ycbcr_enc;
+
+ sdformat->format = *fmt;
+
+ /* Propagate the format from sink to source. */
+ fmt = mipi_csis_get_format(state, sd_state, sdformat->which,
+ CSIS_PAD_SOURCE);
+ *fmt = sdformat->format;
+
+ /* The format on the source pad might change due to unpacking. */
+ fmt->code = csis_fmt->output;
+
+ /* Store the CSIS format descriptor for active formats. */
+ if (sdformat->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ state->csis_fmt = csis_fmt;
+
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static int mipi_csis_log_status(struct v4l2_subdev *sd)
+{
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+
+ mutex_lock(&state->lock);
+ mipi_csis_log_counters(state, true);
+ if (state->debug.enable && (state->state & ST_POWERED))
+ mipi_csis_dump_regs(state);
+ mutex_unlock(&state->lock);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops mipi_csis_core_ops = {
+ .log_status = mipi_csis_log_status,
+};
+
+static const struct v4l2_subdev_video_ops mipi_csis_video_ops = {
+ .s_stream = mipi_csis_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops mipi_csis_pad_ops = {
+ .init_cfg = mipi_csis_init_cfg,
+ .enum_mbus_code = mipi_csis_enum_mbus_code,
+ .get_fmt = mipi_csis_get_fmt,
+ .set_fmt = mipi_csis_set_fmt,
+};
+
+static const struct v4l2_subdev_ops mipi_csis_subdev_ops = {
+ .core = &mipi_csis_core_ops,
+ .video = &mipi_csis_video_ops,
+ .pad = &mipi_csis_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static int mipi_csis_link_setup(struct media_entity *entity,
+ const struct media_pad *local_pad,
+ const struct media_pad *remote_pad, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+ struct v4l2_subdev *remote_sd;
+
+ dev_dbg(state->dev, "link setup %s -> %s", remote_pad->entity->name,
+ local_pad->entity->name);
+
+ /* We only care about the link to the source. */
+ if (!(local_pad->flags & MEDIA_PAD_FL_SINK))
+ return 0;
+
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (state->src_sd)
+ return -EBUSY;
+
+ state->src_sd = remote_sd;
+ } else {
+ state->src_sd = NULL;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations mipi_csis_entity_ops = {
+ .link_setup = mipi_csis_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
+};
+
+/* -----------------------------------------------------------------------------
+ * Async subdev notifier
+ */
+
+static struct csi_state *
+mipi_notifier_to_csis_state(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_state, notifier);
+}
+
+static int mipi_csis_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_subdev *asd)
+{
+ struct csi_state *state = mipi_notifier_to_csis_state(notifier);
+ struct media_pad *sink = &state->sd.entity.pads[CSIS_PAD_SINK];
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, 0);
+}
+
+static const struct v4l2_async_notifier_operations mipi_csis_notify_ops = {
+ .bound = mipi_csis_notify_bound,
+};
+
+static int mipi_csis_async_register(struct csi_state *state)
+{
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct v4l2_async_subdev *asd;
+ struct fwnode_handle *ep;
+ unsigned int i;
+ int ret;
+
+ v4l2_async_nf_init(&state->notifier);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(state->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ goto err_parse;
+
+ for (i = 0; i < vep.bus.mipi_csi2.num_data_lanes; ++i) {
+ if (vep.bus.mipi_csi2.data_lanes[i] != i + 1) {
+ dev_err(state->dev,
+ "data lanes reordering is not supported");
+ ret = -EINVAL;
+ goto err_parse;
+ }
+ }
+
+ state->bus = vep.bus.mipi_csi2;
+
+ dev_dbg(state->dev, "data lanes: %d\n", state->bus.num_data_lanes);
+ dev_dbg(state->dev, "flags: 0x%08x\n", state->bus.flags);
+
+ asd = v4l2_async_nf_add_fwnode_remote(&state->notifier, ep,
+ struct v4l2_async_subdev);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ goto err_parse;
+ }
+
+ fwnode_handle_put(ep);
+
+ state->notifier.ops = &mipi_csis_notify_ops;
+
+ ret = v4l2_async_subdev_nf_register(&state->sd, &state->notifier);
+ if (ret)
+ return ret;
+
+ return v4l2_async_register_subdev(&state->sd);
+
+err_parse:
+ fwnode_handle_put(ep);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+static int mipi_csis_pm_suspend(struct device *dev, bool runtime)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+ if (state->state & ST_POWERED) {
+ mipi_csis_stop_stream(state);
+ ret = mipi_csis_phy_disable(state);
+ if (ret)
+ goto unlock;
+ mipi_csis_clk_disable(state);
+ state->state &= ~ST_POWERED;
+ if (!runtime)
+ state->state |= ST_SUSPENDED;
+ }
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int mipi_csis_pm_resume(struct device *dev, bool runtime)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+ int ret = 0;
+
+ mutex_lock(&state->lock);
+ if (!runtime && !(state->state & ST_SUSPENDED))
+ goto unlock;
+
+ if (!(state->state & ST_POWERED)) {
+ ret = mipi_csis_phy_enable(state);
+ if (ret)
+ goto unlock;
+
+ state->state |= ST_POWERED;
+ mipi_csis_clk_enable(state);
+ }
+ if (state->state & ST_STREAMING)
+ mipi_csis_start_stream(state);
+
+ state->state &= ~ST_SUSPENDED;
+
+unlock:
+ mutex_unlock(&state->lock);
+
+ return ret ? -EAGAIN : 0;
+}
+
+static int __maybe_unused mipi_csis_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, false);
+}
+
+static int __maybe_unused mipi_csis_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, false);
+}
+
+static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev)
+{
+ return mipi_csis_pm_suspend(dev, true);
+}
+
+static int __maybe_unused mipi_csis_runtime_resume(struct device *dev)
+{
+ return mipi_csis_pm_resume(dev, true);
+}
+
+static const struct dev_pm_ops mipi_csis_pm_ops = {
+ SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume,
+ NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(mipi_csis_suspend, mipi_csis_resume)
+};
+
+/* -----------------------------------------------------------------------------
+ * Probe/remove & platform driver
+ */
+
+static int mipi_csis_subdev_init(struct csi_state *state)
+{
+ struct v4l2_subdev *sd = &state->sd;
+
+ v4l2_subdev_init(sd, &mipi_csis_subdev_ops);
+ sd->owner = THIS_MODULE;
+ snprintf(sd->name, sizeof(sd->name), "csis-%s",
+ dev_name(state->dev));
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->ctrl_handler = NULL;
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &mipi_csis_entity_ops;
+
+ sd->dev = state->dev;
+
+ state->csis_fmt = &mipi_csis_formats[0];
+ mipi_csis_init_cfg(sd, NULL);
+
+ state->pads[CSIS_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
+ state->pads[CSIS_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE
+ | MEDIA_PAD_FL_MUST_CONNECT;
+ return media_entity_pads_init(&sd->entity, CSIS_PADS_NUM,
+ state->pads);
+}
+
+static int mipi_csis_parse_dt(struct csi_state *state)
+{
+ struct device_node *node = state->dev->of_node;
+
+ if (of_property_read_u32(node, "clock-frequency",
+ &state->clk_frequency))
+ state->clk_frequency = DEFAULT_SCLK_CSIS_FREQ;
+
+ return 0;
+}
+
+static int mipi_csis_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct csi_state *state;
+ int irq;
+ int ret;
+
+ state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+ if (!state)
+ return -ENOMEM;
+
+ mutex_init(&state->lock);
+ spin_lock_init(&state->slock);
+
+ state->dev = dev;
+ state->info = of_device_get_match_data(dev);
+
+ memcpy(state->events, mipi_csis_events, sizeof(state->events));
+
+ /* Parse DT properties. */
+ ret = mipi_csis_parse_dt(state);
+ if (ret < 0) {
+ dev_err(dev, "Failed to parse device tree: %d\n", ret);
+ return ret;
+ }
+
+ /* Acquire resources. */
+ state->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(state->regs))
+ return PTR_ERR(state->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = mipi_csis_phy_init(state);
+ if (ret < 0)
+ return ret;
+
+ ret = mipi_csis_clk_get(state);
+ if (ret < 0)
+ return ret;
+
+ /* Reset PHY and enable the clocks. */
+ mipi_csis_phy_reset(state);
+
+ ret = mipi_csis_clk_enable(state);
+ if (ret < 0) {
+ dev_err(state->dev, "failed to enable clocks: %d\n", ret);
+ return ret;
+ }
+
+ /* Now that the hardware is initialized, request the interrupt. */
+ ret = devm_request_irq(dev, irq, mipi_csis_irq_handler, 0,
+ dev_name(dev), state);
+ if (ret) {
+ dev_err(dev, "Interrupt request failed\n");
+ goto disable_clock;
+ }
+
+ /* Initialize and register the subdev. */
+ ret = mipi_csis_subdev_init(state);
+ if (ret < 0)
+ goto disable_clock;
+
+ platform_set_drvdata(pdev, &state->sd);
+
+ ret = mipi_csis_async_register(state);
+ if (ret < 0) {
+ dev_err(dev, "async register failed: %d\n", ret);
+ goto cleanup;
+ }
+
+ /* Initialize debugfs. */
+ mipi_csis_debugfs_init(state);
+
+ /* Enable runtime PM. */
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = mipi_csis_pm_resume(dev, true);
+ if (ret < 0)
+ goto unregister_all;
+ }
+
+ dev_info(dev, "lanes: %d, freq: %u\n",
+ state->bus.num_data_lanes, state->clk_frequency);
+
+ return 0;
+
+unregister_all:
+ mipi_csis_debugfs_exit(state);
+cleanup:
+ media_entity_cleanup(&state->sd.entity);
+ v4l2_async_nf_unregister(&state->notifier);
+ v4l2_async_nf_cleanup(&state->notifier);
+ v4l2_async_unregister_subdev(&state->sd);
+disable_clock:
+ mipi_csis_clk_disable(state);
+ mutex_destroy(&state->lock);
+
+ return ret;
+}
+
+static int mipi_csis_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csi_state *state = mipi_sd_to_csis_state(sd);
+
+ mipi_csis_debugfs_exit(state);
+ v4l2_async_nf_unregister(&state->notifier);
+ v4l2_async_nf_cleanup(&state->notifier);
+ v4l2_async_unregister_subdev(&state->sd);
+
+ pm_runtime_disable(&pdev->dev);
+ mipi_csis_pm_suspend(&pdev->dev, true);
+ mipi_csis_clk_disable(state);
+ media_entity_cleanup(&state->sd.entity);
+ mutex_destroy(&state->lock);
+ pm_runtime_set_suspended(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id mipi_csis_of_match[] = {
+ {
+ .compatible = "fsl,imx7-mipi-csi2",
+ .data = &(const struct mipi_csis_info){
+ .version = MIPI_CSIS_V3_3,
+ .num_clocks = 3,
+ },
+ }, {
+ .compatible = "fsl,imx8mm-mipi-csi2",
+ .data = &(const struct mipi_csis_info){
+ .version = MIPI_CSIS_V3_6_3,
+ .num_clocks = 4,
+ },
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mipi_csis_of_match);
+
+static struct platform_driver mipi_csis_driver = {
+ .probe = mipi_csis_probe,
+ .remove = mipi_csis_remove,
+ .driver = {
+ .of_match_table = mipi_csis_of_match,
+ .name = CSIS_DRIVER_NAME,
+ .pm = &mipi_csis_pm_ops,
+ },
+};
+
+module_platform_driver(mipi_csis_driver);
+
+MODULE_DESCRIPTION("i.MX7 & i.MX8 MIPI CSI-2 receiver driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:imx-mipi-csi2");
diff --git a/drivers/media/platform/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c
index 689ae5e6ac62..689ae5e6ac62 100644
--- a/drivers/media/platform/imx-pxp.c
+++ b/drivers/media/platform/nxp/imx-pxp.c
diff --git a/drivers/media/platform/imx-pxp.h b/drivers/media/platform/nxp/imx-pxp.h
index 44f95c749d2e..44f95c749d2e 100644
--- a/drivers/media/platform/imx-pxp.h
+++ b/drivers/media/platform/nxp/imx-pxp.h
diff --git a/drivers/media/platform/mx2_emmaprp.c b/drivers/media/platform/nxp/mx2_emmaprp.c
index 3ce84d0f078c..3ce84d0f078c 100644
--- a/drivers/media/platform/mx2_emmaprp.c
+++ b/drivers/media/platform/nxp/mx2_emmaprp.c
diff --git a/drivers/media/platform/qcom/Kconfig b/drivers/media/platform/qcom/Kconfig
new file mode 100644
index 000000000000..cc5799b9ea00
--- /dev/null
+++ b/drivers/media/platform/qcom/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Qualcomm media platform drivers"
+
+source "drivers/media/platform/qcom/camss/Kconfig"
+source "drivers/media/platform/qcom/venus/Kconfig"
diff --git a/drivers/media/platform/qcom/Makefile b/drivers/media/platform/qcom/Makefile
new file mode 100644
index 000000000000..4f055c396e04
--- /dev/null
+++ b/drivers/media/platform/qcom/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += camss/
+obj-y += venus/
diff --git a/drivers/media/platform/qcom/camss/Kconfig b/drivers/media/platform/qcom/camss/Kconfig
new file mode 100644
index 000000000000..4eda48cb1adf
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/Kconfig
@@ -0,0 +1,9 @@
+config VIDEO_QCOM_CAMSS
+ tristate "Qualcomm V4L2 Camera Subsystem driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
+ depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_SG
+ select V4L2_FWNODE
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 0752c46ea37b..4e2222358973 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -6,7 +6,7 @@ qcom-camss-objs += \
camss-csid.o \
camss-csid-4-1.o \
camss-csid-4-7.o \
- camss-csid-170.o \
+ camss-csid-gen2.o \
camss-csiphy-2ph-1-0.o \
camss-csiphy-3ph-1-0.o \
camss-csiphy.o \
@@ -15,6 +15,7 @@ qcom-camss-objs += \
camss-vfe-4-7.o \
camss-vfe-4-8.o \
camss-vfe-170.o \
+ camss-vfe-480.o \
camss-vfe-gen1.o \
camss-vfe.o \
camss-video.o \
diff --git a/drivers/media/platform/qcom/camss/camss-csid-170.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
index ac22ff29d2a9..2031bde13a93 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-170.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
@@ -21,7 +21,7 @@
* interface support. As a result of that it has an
* alternate register layout.
*/
-#define IS_LITE (csid->id == 2 ? 1 : 0)
+#define IS_LITE (csid->id >= 2 ? 1 : 0)
#define CSID_HW_VERSION 0x0
#define HW_VERSION_STEPPING 0
@@ -105,7 +105,8 @@
#define CSID_RDI_CTRL(rdi) ((IS_LITE ? 0x208 : 0x308)\
+ 0x100 * (rdi))
#define RDI_CTRL_HALT_CMD 0
-#define ALT_CMD_RESUME_AT_FRAME_BOUNDARY 1
+#define HALT_CMD_HALT_AT_FRAME_BOUNDARY 0
+#define HALT_CMD_RESUME_AT_FRAME_BOUNDARY 1
#define RDI_CTRL_HALT_MODE 2
#define CSID_RDI_FRM_DROP_PATTERN(rdi) ((IS_LITE ? 0x20C : 0x30C)\
@@ -262,6 +263,13 @@ static const struct csid_format csid_formats[] = {
1,
},
{
+ MEDIA_BUS_FMT_Y8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
MEDIA_BUS_FMT_Y10_1X10,
DATA_TYPE_RAW_10BIT,
DECODE_FORMAT_UNCOMPRESSED_10_BIT,
@@ -366,7 +374,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
val |= input_format->width & 0x1fff << TPG_DT_n_CFG_0_FRAME_WIDTH;
writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0));
- val = DATA_TYPE_RAW_10BIT << TPG_DT_n_CFG_1_DATA_TYPE;
+ val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE;
writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0));
val = tg->mode << TPG_DT_n_CFG_2_PAYLOAD_MODE;
@@ -382,8 +390,9 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
val = 1 << RDI_CFG0_BYTE_CNTR_EN;
val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN;
val |= 1 << RDI_CFG0_TIMESTAMP_EN;
+ /* note: for non-RDI path, this should be format->decode_format */
val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
- val |= DATA_TYPE_RAW_10BIT << RDI_CFG0_DATA_TYPE;
+ val |= format->data_type << RDI_CFG0_DATA_TYPE;
val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
val |= dt_id << RDI_CFG0_DT_ID;
writel_relaxed(val, csid->base + CSID_RDI_CFG0(0));
@@ -396,7 +405,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(0));
val = 0;
- writel_relaxed(0, csid->base + CSID_RDI_FRM_DROP_PATTERN(0));
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(0));
val = 1;
writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(0));
@@ -441,15 +450,12 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN;
val |= 1 << CSI2_RX_CFG1_MISR_EN;
- writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); // csi2_vc_mode_shift_val ?
-
- /* error irqs start at BIT(11) */
- writel_relaxed(~0u, csid->base + CSID_CSI2_RX_IRQ_MASK);
-
- /* RDI irq */
- writel_relaxed(~0u, csid->base + CSID_TOP_IRQ_MASK);
+ writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1);
- val = 1 << RDI_CTRL_HALT_CMD;
+ if (enable)
+ val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD;
+ else
+ val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD;
writel_relaxed(val, csid->base + CSID_RDI_CTRL(0));
}
@@ -588,7 +594,7 @@ static void csid_subdev_init(struct csid_device *csid)
csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2;
}
-const struct csid_hw_ops csid_ops_170 = {
+const struct csid_hw_ops csid_ops_gen2 = {
.configure_stream = csid_configure_stream,
.configure_testgen_pattern = csid_configure_testgen_pattern,
.hw_version = csid_hw_version,
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index a1637b78568b..f993f349b66b 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -25,6 +25,10 @@
#include "camss-csid-gen1.h"
#include "camss.h"
+/* offset of CSID registers in VFE region for VFE 480 */
+#define VFE_480_CSID_OFFSET 0x1200
+#define VFE_480_LITE_CSID_OFFSET 0x200
+
#define MSM_CSID_NAME "msm_csid"
const char * const csid_testgen_modes[] = {
@@ -152,15 +156,25 @@ static int csid_set_clock_rates(struct csid_device *csid)
static int csid_set_power(struct v4l2_subdev *sd, int on)
{
struct csid_device *csid = v4l2_get_subdevdata(sd);
- struct device *dev = csid->camss->dev;
- int ret;
+ struct camss *camss = csid->camss;
+ struct device *dev = camss->dev;
+ struct vfe_device *vfe = &camss->vfe[csid->id];
+ u32 version = camss->version;
+ int ret = 0;
if (on) {
+ if (version == CAMSS_8250 || version == CAMSS_845) {
+ ret = vfe_get(vfe);
+ if (ret < 0)
+ return ret;
+ }
+
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
- ret = regulator_enable(csid->vdda);
+ ret = regulator_bulk_enable(csid->num_supplies,
+ csid->supplies);
if (ret < 0) {
pm_runtime_put_sync(dev);
return ret;
@@ -168,14 +182,16 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
ret = csid_set_clock_rates(csid);
if (ret < 0) {
- regulator_disable(csid->vdda);
+ regulator_bulk_disable(csid->num_supplies,
+ csid->supplies);
pm_runtime_put_sync(dev);
return ret;
}
ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
if (ret < 0) {
- regulator_disable(csid->vdda);
+ regulator_bulk_disable(csid->num_supplies,
+ csid->supplies);
pm_runtime_put_sync(dev);
return ret;
}
@@ -186,7 +202,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
if (ret < 0) {
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
- regulator_disable(csid->vdda);
+ regulator_bulk_disable(csid->num_supplies,
+ csid->supplies);
pm_runtime_put_sync(dev);
return ret;
}
@@ -195,8 +212,11 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
} else {
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
- ret = regulator_disable(csid->vdda);
+ regulator_bulk_disable(csid->num_supplies,
+ csid->supplies);
pm_runtime_put_sync(dev);
+ if (version == CAMSS_8250 || version == CAMSS_845)
+ vfe_put(vfe);
}
return ret;
@@ -544,7 +564,6 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
{
struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
- struct resource *r;
int i, j;
int ret;
@@ -556,8 +575,9 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
} else if (camss->version == CAMSS_8x96 ||
camss->version == CAMSS_660) {
csid->ops = &csid_ops_4_7;
- } else if (camss->version == CAMSS_845) {
- csid->ops = &csid_ops_170;
+ } else if (camss->version == CAMSS_845 ||
+ camss->version == CAMSS_8250) {
+ csid->ops = &csid_ops_gen2;
} else {
return -EINVAL;
}
@@ -565,20 +585,28 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
/* Memory */
- csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
- if (IS_ERR(csid->base))
- return PTR_ERR(csid->base);
+ if (camss->version == CAMSS_8250) {
+ /* for titan 480, CSID registers are inside the VFE region,
+ * between the VFE "top" and "bus" registers. this requires
+ * VFE to be initialized before CSID
+ */
+ if (id >= 2) /* VFE/CSID lite */
+ csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET;
+ else
+ csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET;
+ } else {
+ csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
+ if (IS_ERR(csid->base))
+ return PTR_ERR(csid->base);
+ }
/* Interrupt */
- r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- res->interrupt[0]);
- if (!r) {
- dev_err(dev, "missing IRQ\n");
- return -EINVAL;
- }
+ ret = platform_get_irq_byname(pdev, res->interrupt[0]);
+ if (ret < 0)
+ return ret;
- csid->irq = r->start;
+ csid->irq = ret;
snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d",
dev_name(dev), MSM_CSID_NAME, csid->id);
ret = devm_request_irq(dev, csid->irq, csid->ops->isr,
@@ -630,13 +658,28 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
}
/* Regulator */
+ for (i = 0; i < ARRAY_SIZE(res->regulators); i++) {
+ if (res->regulators[i])
+ csid->num_supplies++;
+ }
- csid->vdda = devm_regulator_get(dev, res->regulator[0]);
- if (IS_ERR(csid->vdda)) {
- dev_err(dev, "could not get regulator\n");
- return PTR_ERR(csid->vdda);
+ if (csid->num_supplies) {
+ csid->supplies = devm_kmalloc_array(camss->dev,
+ csid->num_supplies,
+ sizeof(csid->supplies),
+ GFP_KERNEL);
+ if (!csid->supplies)
+ return -ENOMEM;
}
+ for (i = 0; i < csid->num_supplies; i++)
+ csid->supplies[i].supply = res->regulators[i];
+
+ ret = devm_regulator_bulk_get(camss->dev, csid->num_supplies,
+ csid->supplies);
+ if (ret)
+ return ret;
+
init_completion(&csid->reset_complete);
return 0;
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index 814ebc7c29d6..f06040e44c51 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -152,7 +152,8 @@ struct csid_device {
char irq_name[30];
struct camss_clock *clock;
int nclocks;
- struct regulator *vdda;
+ struct regulator_bulk_data *supplies;
+ int num_supplies;
struct completion reset_complete;
struct csid_testgen_config testgen;
struct csid_phy_config phy;
@@ -205,7 +206,7 @@ extern const char * const csid_testgen_modes[];
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_170;
+extern const struct csid_hw_ops csid_ops_gen2;
#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 30b454c369ab..cd4a8c369234 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
@@ -16,6 +16,7 @@
#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n))
#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n))
+#define CAMSS_CSI_PHY_LN_CLK 1
#define CAMSS_CSI_PHY_GLBL_RESET 0x140
#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144
#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164
@@ -26,6 +27,19 @@
#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec
#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4
+static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
+{
+ u8 lane_mask;
+ int i;
+
+ lane_mask = 1 << CAMSS_CSI_PHY_LN_CLK;
+
+ for (i = 0; i < lane_cfg->num_data; i++)
+ lane_mask |= 1 << lane_cfg->data[i].pos;
+
+ return lane_mask;
+}
+
static void csiphy_hw_version_read(struct csiphy_device *csiphy,
struct device *dev)
{
@@ -105,7 +119,7 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
for (i = 0; i <= c->num_data; i++) {
if (i == c->num_data)
- l = c->clk.pos;
+ l = CAMSS_CSI_PHY_LN_CLK;
else
l = c->data[i].pos;
@@ -129,7 +143,7 @@ static void csiphy_lanes_disable(struct csiphy_device *csiphy,
for (i = 0; i <= c->num_data; i++) {
if (i == c->num_data)
- l = c->clk.pos;
+ l = CAMSS_CSI_PHY_LN_CLK;
else
l = c->data[i].pos;
@@ -167,6 +181,7 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
}
const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = {
+ .get_lane_mask = csiphy_get_lane_mask,
.hw_version_read = csiphy_hw_version_read,
.reset = csiphy_reset,
.lanes_enable = csiphy_lanes_enable,
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 e318c822ab04..451a4c9b3d30 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
@@ -43,6 +43,7 @@
#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_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))
@@ -62,6 +63,7 @@ struct csiphy_reg_t {
u32 csiphy_param_type;
};
+/* GEN2 1.0 2PH */
static const struct
csiphy_reg_t lane_regs_sdm845[5][14] = {
{
@@ -146,6 +148,121 @@ csiphy_reg_t lane_regs_sdm845[5][14] = {
},
};
+/* 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},
+ },
+};
+
static void csiphy_hw_version_read(struct csiphy_device *csiphy,
struct device *dev)
{
@@ -163,7 +280,7 @@ static void csiphy_hw_version_read(struct csiphy_device *csiphy,
hw_version |= readl_relaxed(csiphy->base +
CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(15)) << 24;
- dev_err(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version);
+ dev_dbg(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version);
}
/*
@@ -298,13 +415,25 @@ static void csiphy_gen1_config_lanes(struct csiphy_device *csiphy,
static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy,
u8 settle_cnt)
{
- int i, l;
+ const struct csiphy_reg_t *r;
+ int i, l, array_size;
u32 val;
- for (l = 0; l < 5; l++) {
- for (i = 0; i < 14; i++) {
- const struct csiphy_reg_t *r = &lane_regs_sdm845[l][i];
+ switch (csiphy->camss->version) {
+ case CAMSS_845:
+ r = &lane_regs_sdm845[0][0];
+ array_size = ARRAY_SIZE(lane_regs_sdm845[0]);
+ break;
+ case CAMSS_8250:
+ r = &lane_regs_sm8250[0][0];
+ array_size = ARRAY_SIZE(lane_regs_sm8250[0]);
+ break;
+ default:
+ unreachable();
+ }
+ 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;
@@ -320,18 +449,33 @@ static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy,
}
}
+static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
+{
+ u8 lane_mask;
+ int i;
+
+ lane_mask = CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE;
+
+ for (i = 0; i < lane_cfg->num_data; i++)
+ lane_mask |= 1 << lane_cfg->data[i].pos;
+
+ return lane_mask;
+}
+
static void csiphy_lanes_enable(struct csiphy_device *csiphy,
struct csiphy_config *cfg,
s64 link_freq, u8 lane_mask)
{
struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
+ bool is_gen2 = (csiphy->camss->version == CAMSS_845 ||
+ csiphy->camss->version == CAMSS_8250);
u8 settle_cnt;
u8 val;
int i;
settle_cnt = csiphy_settle_cnt_calc(link_freq, csiphy->timer_clk_rate);
- val = BIT(c->clk.pos);
+ val = is_gen2 ? BIT(7) : CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE;
for (i = 0; i < c->num_data; i++)
val |= BIT(c->data[i].pos * 2);
@@ -346,44 +490,14 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
val = 0x00;
writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
- if (csiphy->camss->version == CAMSS_8x16 ||
- csiphy->camss->version == CAMSS_8x96)
- csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt);
- else if (csiphy->camss->version == CAMSS_845)
+ if (is_gen2)
csiphy_gen2_config_lanes(csiphy, settle_cnt);
+ else
+ csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt);
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(11));
-
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(12));
-
- val = 0xfb;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(13));
-
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(14));
-
- val = 0x7f;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(15));
-
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(16));
-
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(17));
-
- val = 0xef;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(18));
-
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(19));
-
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(20));
-
- val = 0xff;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(21));
+ /* IRQ_MASK registers - disable all interrupts */
+ for (i = 11; i < 22; i++)
+ writel_relaxed(0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i));
}
static void csiphy_lanes_disable(struct csiphy_device *csiphy,
@@ -397,6 +511,7 @@ static void csiphy_lanes_disable(struct csiphy_device *csiphy,
}
const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = {
+ .get_lane_mask = csiphy_get_lane_mask,
.hw_version_read = csiphy_hw_version_read,
.reset = csiphy_reset,
.lanes_enable = csiphy_lanes_enable,
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 24eec16197e7..75fcfc627400 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -94,6 +94,7 @@ static const struct csiphy_format csiphy_formats_sdm845[] = {
{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
+ { MEDIA_BUS_FMT_Y8_1X8, 8 },
{ MEDIA_BUS_FMT_Y10_1X10, 10 },
};
@@ -230,25 +231,6 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
}
/*
- * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter
- * @lane_cfg - CSI2 lane configuration
- *
- * Return lane mask
- */
-static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
-{
- u8 lane_mask;
- int i;
-
- lane_mask = 1 << lane_cfg->clk.pos;
-
- for (i = 0; i < lane_cfg->num_data; i++)
- lane_mask |= 1 << lane_cfg->data[i].pos;
-
- return lane_mask;
-}
-
-/*
* csiphy_stream_on - Enable streaming on CSIPHY module
* @csiphy: CSIPHY device
*
@@ -261,7 +243,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
{
struct csiphy_config *cfg = &csiphy->cfg;
s64 link_freq;
- u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg);
+ u8 lane_mask = csiphy->ops->get_lane_mask(&cfg->csi2->lane_cfg);
u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats,
csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
@@ -568,7 +550,6 @@ int msm_csiphy_subdev_init(struct camss *camss,
{
struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
- struct resource *r;
int i, j;
int ret;
@@ -585,7 +566,8 @@ int msm_csiphy_subdev_init(struct camss *camss,
csiphy->ops = &csiphy_ops_3ph_1_0;
csiphy->formats = csiphy_formats_8x96;
csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96);
- } else if (camss->version == CAMSS_845) {
+ } else if (camss->version == CAMSS_845 ||
+ camss->version == CAMSS_8250) {
csiphy->ops = &csiphy_ops_3ph_1_0;
csiphy->formats = csiphy_formats_sdm845;
csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845);
@@ -611,14 +593,11 @@ int msm_csiphy_subdev_init(struct camss *camss,
/* Interrupt */
- r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- res->interrupt[0]);
- if (!r) {
- dev_err(dev, "missing IRQ\n");
- return -EINVAL;
- }
+ ret = platform_get_irq_byname(pdev, res->interrupt[0]);
+ if (ret < 0)
+ return ret;
- csiphy->irq = r->start;
+ csiphy->irq = ret;
snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d",
dev_name(dev), MSM_CSIPHY_NAME, csiphy->id);
@@ -679,7 +658,10 @@ int msm_csiphy_subdev_init(struct camss *camss,
if (!strcmp(clock->name, "csiphy0_timer") ||
!strcmp(clock->name, "csiphy1_timer") ||
- !strcmp(clock->name, "csiphy2_timer"))
+ !strcmp(clock->name, "csiphy2_timer") ||
+ !strcmp(clock->name, "csiphy3_timer") ||
+ !strcmp(clock->name, "csiphy4_timer") ||
+ !strcmp(clock->name, "csiphy5_timer"))
csiphy->rate_set[i] = true;
if (camss->version == CAMSS_660 &&
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index d71b8bc6ec00..1c14947f92d3 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -45,6 +45,13 @@ struct csiphy_config {
struct csiphy_device;
struct csiphy_hw_ops {
+ /*
+ * csiphy_get_lane_mask - Calculate CSI2 lane mask configuration parameter
+ * @lane_cfg - CSI2 lane configuration
+ *
+ * Return lane mask
+ */
+ u8 (*get_lane_mask)(struct csiphy_lanes_cfg *lane_cfg);
void (*hw_version_read)(struct csiphy_device *csiphy,
struct device *dev);
void (*reset)(struct csiphy_device *csiphy);
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index ba5d65f6ef34..4ee11bb979cd 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -1100,7 +1100,6 @@ int msm_ispif_subdev_init(struct camss *camss,
struct device *dev = camss->dev;
struct ispif_device *ispif = camss->ispif;
struct platform_device *pdev = to_platform_device(dev);
- struct resource *r;
int i;
int ret;
@@ -1153,14 +1152,11 @@ int msm_ispif_subdev_init(struct camss *camss,
/* Interrupt */
- r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res->interrupt);
-
- if (!r) {
- dev_err(dev, "missing IRQ\n");
- return -EINVAL;
- }
+ ret = platform_get_irq_byname(pdev, res->interrupt);
+ if (ret < 0)
+ return ret;
- ispif->irq = r->start;
+ ispif->irq = ret;
snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s",
dev_name(dev), MSM_ISPIF_NAME);
if (camss->version == CAMSS_8x16)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c
index f524af712a84..600150cfc4f7 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-170.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c
@@ -395,17 +395,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
*/
static int vfe_halt(struct vfe_device *vfe)
{
- unsigned long time;
-
- reinit_completion(&vfe->halt_complete);
-
- time = wait_for_completion_timeout(&vfe->halt_complete,
- msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
- if (!time) {
- dev_err(vfe->camss->dev, "VFE halt timeout\n");
- return -EIO;
- }
-
+ /* rely on vfe_disable_output() to stop the VFE */
return 0;
}
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c
new file mode 100644
index 000000000000..129585110393
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c
@@ -0,0 +1,564 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-480.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v480 (SM8250)
+ *
+ * Copyright (C) 2020-2021 Linaro Ltd.
+ * Copyright (C) 2021 Jonathan Marek
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+
+/* VFE 2/3 are lite and have a different register layout */
+#define IS_LITE (vfe->id >= 2 ? 1 : 0)
+
+#define VFE_HW_VERSION (0x00)
+
+#define VFE_GLOBAL_RESET_CMD (IS_LITE ? 0x0c : 0x1c)
+#define GLOBAL_RESET_HW_AND_REG (IS_LITE ? BIT(1) : BIT(0))
+
+#define VFE_REG_UPDATE_CMD (IS_LITE ? 0x20 : 0x34)
+static inline int reg_update_rdi(struct vfe_device *vfe, int n)
+{
+ return IS_LITE ? BIT(n) : BIT(1 + (n));
+}
+
+#define REG_UPDATE_RDI reg_update_rdi
+#define VFE_IRQ_CMD (IS_LITE ? 0x24 : 0x38)
+#define IRQ_CMD_GLOBAL_CLEAR BIT(0)
+
+#define VFE_IRQ_MASK(n) ((IS_LITE ? 0x28 : 0x3c) + (n) * 4)
+#define IRQ_MASK_0_RESET_ACK (IS_LITE ? BIT(17) : BIT(0))
+#define IRQ_MASK_0_BUS_TOP_IRQ (IS_LITE ? BIT(4) : BIT(7))
+#define VFE_IRQ_CLEAR(n) ((IS_LITE ? 0x34 : 0x48) + (n) * 4)
+#define VFE_IRQ_STATUS(n) ((IS_LITE ? 0x40 : 0x54) + (n) * 4)
+
+#define BUS_REG_BASE (IS_LITE ? 0x1a00 : 0xaa00)
+
+#define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08)
+#define WM_CGC_OVERRIDE_ALL (0x3FFFFFF)
+
+#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0xdc)
+
+#define VFE_BUS_IRQ_MASK(n) (BUS_REG_BASE + 0x18 + (n) * 4)
+static inline int bus_irq_mask_0_rdi_rup(struct vfe_device *vfe, int n)
+{
+ return IS_LITE ? BIT(n) : BIT(3 + (n));
+}
+
+#define BUS_IRQ_MASK_0_RDI_RUP bus_irq_mask_0_rdi_rup
+static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n)
+{
+ return IS_LITE ? BIT(4 + (n)) : BIT(6 + (n));
+}
+
+#define BUS_IRQ_MASK_0_COMP_DONE bus_irq_mask_0_comp_done
+#define VFE_BUS_IRQ_CLEAR(n) (BUS_REG_BASE + 0x20 + (n) * 4)
+#define VFE_BUS_IRQ_STATUS(n) (BUS_REG_BASE + 0x28 + (n) * 4)
+#define VFE_BUS_IRQ_CLEAR_GLOBAL (BUS_REG_BASE + 0x30)
+
+#define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x200 + (n) * 0x100)
+#define WM_CFG_EN (0)
+#define WM_CFG_MODE (16)
+#define MODE_QCOM_PLAIN (0)
+#define MODE_MIPI_RAW (1)
+#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_1(n) (BUS_REG_BASE + 0x210 + (n) * 0x100)
+#define VFE_BUS_WM_IMAGE_CFG_2(n) (BUS_REG_BASE + 0x214 + (n) * 0x100)
+#define VFE_BUS_WM_PACKER_CFG(n) (BUS_REG_BASE + 0x218 + (n) * 0x100)
+#define VFE_BUS_WM_HEADER_ADDR(n) (BUS_REG_BASE + 0x220 + (n) * 0x100)
+#define VFE_BUS_WM_HEADER_INCR(n) (BUS_REG_BASE + 0x224 + (n) * 0x100)
+#define VFE_BUS_WM_HEADER_CFG(n) (BUS_REG_BASE + 0x228 + (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_SYSTEM_CACHE_CFG(n) (BUS_REG_BASE + 0x260 + (n) * 0x100)
+#define VFE_BUS_WM_BURST_LIMIT(n) (BUS_REG_BASE + 0x264 + (n) * 0x100)
+
+/* for titan 480, each bus client is hardcoded to a specific path
+ * and each bus client is part of a hardcoded "comp group"
+ */
+#define RDI_WM(n) ((IS_LITE ? 0 : 23) + (n))
+#define RDI_COMP_GROUP(n) ((IS_LITE ? 0 : 11) + (n))
+
+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));
+ writel_relaxed(GLOBAL_RESET_HW_AND_REG, vfe->base + VFE_GLOBAL_RESET_CMD);
+}
+
+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); /* map to actual WM used (from wm=RDI index) */
+
+ /* no clock gating at bus input */
+ writel_relaxed(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
+
+ writel_relaxed(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
+
+ writel_relaxed(pix->plane_fmt[0].bytesperline * pix->height,
+ vfe->base + VFE_BUS_WM_FRAME_INCR(wm));
+ writel_relaxed(0xf, vfe->base + VFE_BUS_WM_BURST_LIMIT(wm));
+ writel_relaxed(WM_IMAGE_CFG_0_DEFAULT_WIDTH,
+ vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm));
+ writel_relaxed(pix->plane_fmt[0].bytesperline,
+ vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm));
+ writel_relaxed(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm));
+
+ /* no dropped frames, one irq per frame */
+ writel_relaxed(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm));
+ writel_relaxed(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm));
+ writel_relaxed(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm));
+ writel_relaxed(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm));
+
+ writel_relaxed(1 << WM_CFG_EN | MODE_MIPI_RAW << 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); /* map to actual WM used (from wm=RDI index) */
+ writel_relaxed(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); /* map to actual WM used (from wm=RDI index) */
+ writel_relaxed(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ vfe->reg_update |= REG_UPDATE_RDI(vfe, line_id);
+ writel_relaxed(vfe->reg_update, vfe->base + VFE_REG_UPDATE_CMD);
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ vfe->reg_update &= ~REG_UPDATE_RDI(vfe, line_id);
+}
+
+static void vfe_enable_irq_common(struct vfe_device *vfe)
+{
+ /* enable only the IRQs used: rup and comp_done irqs for RDI0 */
+ writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ,
+ vfe->base + VFE_IRQ_MASK(0));
+ writel_relaxed(BUS_IRQ_MASK_0_RDI_RUP(vfe, 0) |
+ BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(0)),
+ 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
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ struct vfe_device *vfe = dev;
+ u32 status;
+
+ status = readl_relaxed(vfe->base + VFE_IRQ_STATUS(0));
+ writel_relaxed(status, vfe->base + VFE_IRQ_CLEAR(0));
+ writel_relaxed(IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD);
+
+ if (status & IRQ_MASK_0_RESET_ACK)
+ vfe_isr_reset_ack(vfe);
+
+ if (status & IRQ_MASK_0_BUS_TOP_IRQ) {
+ u32 status = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(0));
+
+ writel_relaxed(status, vfe->base + VFE_BUS_IRQ_CLEAR(0));
+ writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL);
+
+ if (status & BUS_IRQ_MASK_0_RDI_RUP(vfe, 0))
+ vfe_isr_reg_update(vfe, 0);
+
+ if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(0)))
+ vfe_isr_wm_done(vfe, 0);
+ }
+
+ 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 int vfe_get_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output;
+ unsigned long flags;
+ int wm_idx;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ output = &line->output;
+ if (output->state != VFE_OUTPUT_OFF) {
+ dev_err(vfe->camss->dev, "Output is running\n");
+ goto error;
+ }
+
+ output->wm_num = 1;
+
+ wm_idx = vfe_reserve_wm(vfe, line->id);
+ if (wm_idx < 0) {
+ dev_err(vfe->camss->dev, "Can not reserve wm\n");
+ goto error_get_wm;
+ }
+ output->wm_idx[0] = wm_idx;
+
+ output->drop_update_idx = 0;
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+
+error_get_wm:
+ vfe_release_wm(vfe, output->wm_idx[0]);
+ output->state = VFE_OUTPUT_OFF;
+error:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ 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_OFF) {
+ 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;
+}
+
+static int vfe_disable_output(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output = &line->output;
+ unsigned long flags;
+ unsigned int i;
+ bool done;
+ int timeout = 0;
+
+ do {
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ done = !output->gen2.active_num;
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ usleep_range(10000, 20000);
+
+ if (timeout++ == 100) {
+ dev_err(vfe->camss->dev, "VFE idle timeout - resetting\n");
+ vfe_reset(vfe);
+ output->gen2.active_num = 0;
+ return 0;
+ }
+ } while (!done);
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ for (i = 0; i < output->wm_num; i++)
+ vfe_wm_stop(vfe, output->wm_idx[i]);
+ 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++;
+
+ 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_disable - Disable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_disable(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+
+ vfe_disable_output(line);
+
+ vfe_put_output(line);
+
+ mutex_lock(&vfe->stream_lock);
+
+ vfe->stream_count--;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ return 0;
+}
+
+/*
+ * vfe_isr_reg_update - Process reg update interrupt
+ * @vfe: VFE Device
+ * @line_id: VFE line
+ */
+static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ struct vfe_output *output;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+ vfe_reg_update_clear(vfe, line_id);
+
+ output = &vfe->line[line_id].output;
+
+ if (output->wait_reg_update) {
+ output->wait_reg_update = 0;
+ complete(&output->reg_update);
+ }
+
+ 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;
+
+out_unlock:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+/*
+ * vfe_pm_domain_off - Disable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static void vfe_pm_domain_off(struct vfe_device *vfe)
+{
+ /* nop */
+}
+
+/*
+ * vfe_pm_domain_on - Enable power domains specific to this VFE.
+ * @vfe: VFE Device
+ */
+static int vfe_pm_domain_on(struct vfe_device *vfe)
+{
+ return 0;
+}
+
+/*
+ * 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 camss_video_ops vfe_video_ops_480 = {
+ .queue_buffer = vfe_queue_buffer,
+ .flush_buffers = vfe_flush_buffers,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->video_ops = vfe_video_ops_480;
+ vfe->line_num = 1;
+}
+
+const struct vfe_hw_ops vfe_ops_480 = {
+ .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,
+ .vfe_halt = vfe_halt,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 71f78b40e7f5..5b148e9f8134 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -118,6 +118,7 @@ static const struct vfe_format formats_rdi_845[] = {
{ MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
{ MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
{ MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
+ { MEDIA_BUS_FMT_Y8_1X8, 8 },
{ MEDIA_BUS_FMT_Y10_1X10, 10 },
{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
};
@@ -219,7 +220,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
}
else if (vfe->camss->version == CAMSS_8x96 ||
vfe->camss->version == CAMSS_660 ||
- vfe->camss->version == CAMSS_845)
+ vfe->camss->version == CAMSS_845 ||
+ vfe->camss->version == CAMSS_8250)
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_2X8:
{
@@ -573,7 +575,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
*
* Return 0 on success or a negative error code otherwise
*/
-static int vfe_get(struct vfe_device *vfe)
+int vfe_get(struct vfe_device *vfe)
{
int ret;
@@ -635,7 +637,7 @@ error_pm_domain:
* vfe_put - Power down VFE module
* @vfe: VFE Device
*/
-static void vfe_put(struct vfe_device *vfe)
+void vfe_put(struct vfe_device *vfe)
{
mutex_lock(&vfe->power_lock);
@@ -1279,7 +1281,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
{
struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
- struct resource *r;
int i, j;
int ret;
@@ -1293,10 +1294,12 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
case CAMSS_660:
vfe->ops = &vfe_ops_4_8;
break;
-
case CAMSS_845:
vfe->ops = &vfe_ops_170;
break;
+ case CAMSS_8250:
+ vfe->ops = &vfe_ops_480;
+ break;
default:
return -EINVAL;
}
@@ -1312,16 +1315,13 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
/* Interrupt */
- r = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
- res->interrupt[0]);
- if (!r) {
- dev_err(dev, "missing IRQ\n");
- return -EINVAL;
- }
+ ret = platform_get_irq_byname(pdev, res->interrupt[0]);
+ if (ret < 0)
+ return ret;
- vfe->irq = r->start;
+ vfe->irq = ret;
snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
- dev_name(dev), MSM_VFE_NAME, vfe->id);
+ dev_name(dev), MSM_VFE_NAME, id);
ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr,
IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
if (ret < 0) {
@@ -1407,7 +1407,8 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
l->formats = formats_rdi_8x96;
l->nformats = ARRAY_SIZE(formats_rdi_8x96);
}
- } else if (camss->version == CAMSS_845) {
+ } else if (camss->version == CAMSS_845 ||
+ camss->version == CAMSS_8250) {
l->formats = formats_rdi_845;
l->nformats = ARRAY_SIZE(formats_rdi_845);
} else {
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index f166d176cb77..0eba04eb9b77 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -201,5 +201,9 @@ extern const struct vfe_hw_ops vfe_ops_4_1;
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;
+
+int vfe_get(struct vfe_device *vfe);
+void vfe_put(struct vfe_device *vfe);
#endif /* QC_MSM_CAMSS_VFE_H */
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index f282275af626..5dc1ddbe6d65 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -176,6 +176,8 @@ static const struct camss_format_info formats_rdi_845[] = {
{ { 1, 1 } }, { { 1, 1 } }, { 14 } },
{ MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1,
{ { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_Y8_1X8, V4L2_PIX_FMT_GREY, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
{ MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
{ { 1, 1 } }, { { 1, 1 } }, { 10 } },
{ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1,
@@ -1009,7 +1011,8 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
video->formats = formats_rdi_8x96;
video->nformats = ARRAY_SIZE(formats_rdi_8x96);
}
- } else if (video->camss->version == CAMSS_845) {
+ } else if (video->camss->version == CAMSS_845 ||
+ video->camss->version == CAMSS_8250) {
video->formats = formats_rdi_845;
video->nformats = ARRAY_SIZE(formats_rdi_845);
} else {
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index be091c50a3c0..79ad82e233cb 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -8,6 +8,7 @@
* Copyright (C) 2015-2018 Linaro Ltd.
*/
#include <linux/clk.h>
+#include <linux/interconnect.h>
#include <linux/media-bus-format.h>
#include <linux/media.h>
#include <linux/module.h>
@@ -33,7 +34,7 @@
static const struct resources csiphy_res_8x16[] = {
/* CSIPHY0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" },
.clock_rate = { { 0 },
{ 0 },
@@ -45,7 +46,7 @@ static const struct resources csiphy_res_8x16[] = {
/* CSIPHY1 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" },
.clock_rate = { { 0 },
{ 0 },
@@ -59,7 +60,7 @@ static const struct resources csiphy_res_8x16[] = {
static const struct resources csid_res_8x16[] = {
/* CSID0 */
{
- .regulator = { "vdda" },
+ .regulators = { "vdda" },
.clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
.clock_rate = { { 0 },
@@ -76,7 +77,7 @@ static const struct resources csid_res_8x16[] = {
/* CSID1 */
{
- .regulator = { "vdda" },
+ .regulators = { "vdda" },
.clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
.clock_rate = { { 0 },
@@ -106,7 +107,7 @@ static const struct resources_ispif ispif_res_8x16 = {
static const struct resources vfe_res_8x16[] = {
/* VFE0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "vfe0", "csi_vfe0",
"vfe_ahb", "vfe_axi", "ahb" },
.clock_rate = { { 0 },
@@ -128,7 +129,7 @@ static const struct resources vfe_res_8x16[] = {
static const struct resources csiphy_res_8x96[] = {
/* CSIPHY0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" },
.clock_rate = { { 0 },
{ 0 },
@@ -140,7 +141,7 @@ static const struct resources csiphy_res_8x96[] = {
/* CSIPHY1 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" },
.clock_rate = { { 0 },
{ 0 },
@@ -152,7 +153,7 @@ static const struct resources csiphy_res_8x96[] = {
/* CSIPHY2 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer" },
.clock_rate = { { 0 },
{ 0 },
@@ -166,7 +167,7 @@ static const struct resources csiphy_res_8x96[] = {
static const struct resources csid_res_8x96[] = {
/* CSID0 */
{
- .regulator = { "vdda" },
+ .regulators = { "vdda" },
.clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
.clock_rate = { { 0 },
@@ -183,7 +184,7 @@ static const struct resources csid_res_8x96[] = {
/* CSID1 */
{
- .regulator = { "vdda" },
+ .regulators = { "vdda" },
.clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
.clock_rate = { { 0 },
@@ -200,7 +201,7 @@ static const struct resources csid_res_8x96[] = {
/* CSID2 */
{
- .regulator = { "vdda" },
+ .regulators = { "vdda" },
.clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
"csi2", "csi2_phy", "csi2_pix", "csi2_rdi" },
.clock_rate = { { 0 },
@@ -217,7 +218,7 @@ static const struct resources csid_res_8x96[] = {
/* CSID3 */
{
- .regulator = { "vdda" },
+ .regulators = { "vdda" },
.clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb",
"csi3", "csi3_phy", "csi3_pix", "csi3_rdi" },
.clock_rate = { { 0 },
@@ -248,7 +249,7 @@ static const struct resources_ispif ispif_res_8x96 = {
static const struct resources vfe_res_8x96[] = {
/* VFE0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ahb", "vfe0", "csi_vfe0", "vfe_ahb",
"vfe0_ahb", "vfe_axi", "vfe0_stream"},
.clock_rate = { { 0 },
@@ -266,7 +267,7 @@ static const struct resources vfe_res_8x96[] = {
/* VFE1 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ahb", "vfe1", "csi_vfe1", "vfe_ahb",
"vfe1_ahb", "vfe_axi", "vfe1_stream"},
.clock_rate = { { 0 },
@@ -286,7 +287,7 @@ static const struct resources vfe_res_8x96[] = {
static const struct resources csiphy_res_660[] = {
/* CSIPHY0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer",
"csi0_phy", "csiphy_ahb2crif" },
.clock_rate = { { 0 },
@@ -300,7 +301,7 @@ static const struct resources csiphy_res_660[] = {
/* CSIPHY1 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer",
"csi1_phy", "csiphy_ahb2crif" },
.clock_rate = { { 0 },
@@ -314,7 +315,7 @@ static const struct resources csiphy_res_660[] = {
/* CSIPHY2 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer",
"csi2_phy", "csiphy_ahb2crif" },
.clock_rate = { { 0 },
@@ -330,7 +331,7 @@ static const struct resources csiphy_res_660[] = {
static const struct resources csid_res_660[] = {
/* CSID0 */
{
- .regulator = { "vdda", "vdd_sec" },
+ .regulators = { "vdda", "vdd_sec" },
.clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi",
"cphy_csid0" },
@@ -350,7 +351,7 @@ static const struct resources csid_res_660[] = {
/* CSID1 */
{
- .regulator = { "vdda", "vdd_sec" },
+ .regulators = { "vdda", "vdd_sec" },
.clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi",
"cphy_csid1" },
@@ -370,7 +371,7 @@ static const struct resources csid_res_660[] = {
/* CSID2 */
{
- .regulator = { "vdda", "vdd_sec" },
+ .regulators = { "vdda", "vdd_sec" },
.clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
"csi2", "csi2_phy", "csi2_pix", "csi2_rdi",
"cphy_csid2" },
@@ -390,7 +391,7 @@ static const struct resources csid_res_660[] = {
/* CSID3 */
{
- .regulator = { "vdda", "vdd_sec" },
+ .regulators = { "vdda", "vdd_sec" },
.clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb",
"csi3", "csi3_phy", "csi3_pix", "csi3_rdi",
"cphy_csid3" },
@@ -424,7 +425,7 @@ static const struct resources_ispif ispif_res_660 = {
static const struct resources vfe_res_660[] = {
/* VFE0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "throttle_axi", "top_ahb", "ahb", "vfe0",
"csi_vfe0", "vfe_ahb", "vfe0_ahb", "vfe_axi",
"vfe0_stream"},
@@ -445,7 +446,7 @@ static const struct resources vfe_res_660[] = {
/* VFE1 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "throttle_axi", "top_ahb", "ahb", "vfe1",
"csi_vfe1", "vfe_ahb", "vfe1_ahb", "vfe_axi",
"vfe1_stream"},
@@ -468,7 +469,7 @@ static const struct resources vfe_res_660[] = {
static const struct resources csiphy_res_845[] = {
/* CSIPHY0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
"cpas_ahb", "cphy_rx_src", "csiphy0",
"csiphy0_timer_src", "csiphy0_timer" },
@@ -486,7 +487,7 @@ static const struct resources csiphy_res_845[] = {
/* CSIPHY1 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
"cpas_ahb", "cphy_rx_src", "csiphy1",
"csiphy1_timer_src", "csiphy1_timer" },
@@ -504,7 +505,7 @@ static const struct resources csiphy_res_845[] = {
/* CSIPHY2 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
"cpas_ahb", "cphy_rx_src", "csiphy2",
"csiphy2_timer_src", "csiphy2_timer" },
@@ -522,7 +523,7 @@ static const struct resources csiphy_res_845[] = {
/* CSIPHY3 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src",
"cpas_ahb", "cphy_rx_src", "csiphy3",
"csiphy3_timer_src", "csiphy3_timer" },
@@ -542,7 +543,7 @@ static const struct resources csiphy_res_845[] = {
static const struct resources csid_res_845[] = {
/* CSID0 */
{
- .regulator = { "vdda-csi0" },
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
"soc_ahb", "vfe0", "vfe0_src",
"vfe0_cphy_rx", "csi0",
@@ -562,7 +563,7 @@ static const struct resources csid_res_845[] = {
/* CSID1 */
{
- .regulator = { "vdda-csi1" },
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
"soc_ahb", "vfe1", "vfe1_src",
"vfe1_cphy_rx", "csi1",
@@ -582,7 +583,7 @@ static const struct resources csid_res_845[] = {
/* CSID2 */
{
- .regulator = { "vdda-csi2" },
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src",
"soc_ahb", "vfe_lite", "vfe_lite_src",
"vfe_lite_cphy_rx", "csi2",
@@ -604,7 +605,7 @@ static const struct resources csid_res_845[] = {
static const struct resources vfe_res_845[] = {
/* VFE0 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src",
"soc_ahb", "vfe0", "vfe0_axi",
"vfe0_src", "csi0",
@@ -624,7 +625,7 @@ static const struct resources vfe_res_845[] = {
/* VFE1 */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src",
"soc_ahb", "vfe1", "vfe1_axi",
"vfe1_src", "csi1",
@@ -644,7 +645,7 @@ static const struct resources vfe_res_845[] = {
/* VFE-lite */
{
- .regulator = { NULL },
+ .regulators = {},
.clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src",
"soc_ahb", "vfe_lite",
"vfe_lite_src", "csi2",
@@ -662,6 +663,208 @@ static const struct resources vfe_res_845[] = {
}
};
+static const struct resources csiphy_res_8250[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = {},
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" }
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = {},
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" }
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = {},
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" }
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = {},
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" }
+ },
+ /* CSIPHY4 */
+ {
+ .regulators = {},
+ .clock = { "csiphy4", "csiphy4_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy4" },
+ .interrupt = { "csiphy4" }
+ },
+ /* CSIPHY5 */
+ {
+ .regulators = {},
+ .clock = { "csiphy5", "csiphy5_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy5" },
+ .interrupt = { "csiphy5" }
+ }
+};
+
+static const struct resources csid_res_8250[] = {
+ /* CSID0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_areg", "vfe0_ahb" },
+ .clock_rate = { { 400000000 },
+ { 400000000 },
+ { 350000000, 475000000, 576000000, 720000000 },
+ { 100000000, 200000000, 300000000, 400000000 },
+ { 0 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" }
+ },
+ /* CSID1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_areg", "vfe1_ahb" },
+ .clock_rate = { { 400000000 },
+ { 400000000 },
+ { 350000000, 475000000, 576000000, 720000000 },
+ { 100000000, 200000000, 300000000, 400000000 },
+ { 0 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" }
+ },
+ /* CSID2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" },
+ .clock_rate = { { 400000000 },
+ { 400000000 },
+ { 400000000, 480000000 },
+ { 0 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" }
+ },
+ /* CSID3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" },
+ .clock_rate = { { 400000000 },
+ { 400000000 },
+ { 400000000, 480000000 },
+ { 0 } },
+ .reg = { "csid3" },
+ .interrupt = { "csid3" }
+ }
+};
+
+static const struct resources vfe_res_8250[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+ "camnoc_axi", "vfe0_ahb", "vfe0_areg", "vfe0",
+ "vfe0_axi", "cam_hf_axi" },
+ .clock_rate = { { 19200000, 300000000, 400000000, 480000000 },
+ { 19200000, 80000000 },
+ { 19200000 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 300000000, 400000000 },
+ { 350000000, 475000000, 576000000, 720000000 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" }
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+ "camnoc_axi", "vfe1_ahb", "vfe1_areg", "vfe1",
+ "vfe1_axi", "cam_hf_axi" },
+ .clock_rate = { { 19200000, 300000000, 400000000, 480000000 },
+ { 19200000, 80000000 },
+ { 19200000 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 300000000, 400000000 },
+ { 350000000, 475000000, 576000000, 720000000 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" }
+ },
+ /* VFE2 (lite) */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+ "camnoc_axi", "vfe_lite_ahb", "vfe_lite_axi",
+ "vfe_lite", "cam_hf_axi" },
+ .clock_rate = { { 19200000, 300000000, 400000000, 480000000 },
+ { 19200000, 80000000 },
+ { 19200000 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 400000000, 480000000 },
+ { 0 } },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" }
+ },
+ /* VFE3 (lite) */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi_src", "slow_ahb_src", "cpas_ahb",
+ "camnoc_axi", "vfe_lite_ahb", "vfe_lite_axi",
+ "vfe_lite", "cam_hf_axi" },
+ .clock_rate = { { 19200000, 300000000, 400000000, 480000000 },
+ { 19200000, 80000000 },
+ { 19200000 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 400000000, 480000000 },
+ { 0 } },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" }
+ },
+};
+
+static const struct resources_icc icc_res_sm8250[] = {
+ {
+ .name = "cam_ahb",
+ .icc_bw_tbl.avg = 38400,
+ .icc_bw_tbl.peak = 76800,
+ },
+ {
+ .name = "cam_hf_0_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "cam_sf_0_mnoc",
+ .icc_bw_tbl.avg = 0,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "cam_sf_icp_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
/*
* camss_add_clock_margin - Add margin to clock frequency rate
* @rate: Clock frequency rate
@@ -832,7 +1035,7 @@ static int camss_of_parse_endpoint_node(struct device *dev,
struct camss_async_subdev *csd)
{
struct csiphy_lanes_cfg *lncfg = &csd->interface.csi2.lane_cfg;
- struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2;
+ struct v4l2_mbus_config_mipi_csi2 *mipi_csi2;
struct v4l2_fwnode_endpoint vep = { { 0 } };
unsigned int i;
@@ -945,6 +1148,12 @@ static int camss_init_subdevices(struct camss *camss)
/* Titan VFEs don't have an ISPIF */
ispif_res = NULL;
vfe_res = vfe_res_845;
+ } else if (camss->version == CAMSS_8250) {
+ csiphy_res = csiphy_res_8250;
+ csid_res = csid_res_8250;
+ /* Titan VFEs don't have an ISPIF */
+ ispif_res = NULL;
+ vfe_res = vfe_res_8250;
} else {
return -EINVAL;
}
@@ -960,6 +1169,17 @@ static int camss_init_subdevices(struct camss *camss)
}
}
+ /* note: SM8250 requires VFE to be initialized before CSID */
+ for (i = 0; i < camss->vfe_num; i++) {
+ ret = msm_vfe_subdev_init(camss, &camss->vfe[i],
+ &vfe_res[i], i);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Fail to init vfe%d sub-device: %d\n", i, ret);
+ return ret;
+ }
+ }
+
for (i = 0; i < camss->csid_num; i++) {
ret = msm_csid_subdev_init(camss, &camss->csid[i],
&csid_res[i], i);
@@ -978,16 +1198,6 @@ static int camss_init_subdevices(struct camss *camss)
return ret;
}
- for (i = 0; i < camss->vfe_num; i++) {
- ret = msm_vfe_subdev_init(camss, &camss->vfe[i],
- &vfe_res[i], i);
- if (ret < 0) {
- dev_err(camss->dev,
- "Fail to init vfe%d sub-device: %d\n", i, ret);
- return ret;
- }
- }
-
return 0;
}
@@ -1250,7 +1460,8 @@ static int camss_configure_pd(struct camss *camss)
if (camss->version == CAMSS_8x96 ||
camss->version == CAMSS_660)
nbr_pm_domains = PM_DOMAIN_GEN1_COUNT;
- else if (camss->version == CAMSS_845)
+ else if (camss->version == CAMSS_845 ||
+ camss->version == CAMSS_8250)
nbr_pm_domains = PM_DOMAIN_GEN2_COUNT;
for (i = 0; i < nbr_pm_domains; i++) {
@@ -1283,6 +1494,29 @@ fail_pm:
return ret;
}
+static int camss_icc_get(struct camss *camss)
+{
+ const struct resources_icc *icc_res;
+ int nbr_icc_paths = 0;
+ int i;
+
+ if (camss->version == CAMSS_8250) {
+ icc_res = &icc_res_sm8250[0];
+ nbr_icc_paths = ICC_SM8250_COUNT;
+ }
+
+ for (i = 0; i < nbr_icc_paths; i++) {
+ camss->icc_path[i] = devm_of_icc_get(camss->dev,
+ icc_res[i].name);
+ if (IS_ERR(camss->icc_path[i]))
+ return PTR_ERR(camss->icc_path[i]);
+
+ camss->icc_bw_tbl[i] = icc_res[i].icc_bw_tbl;
+ }
+
+ return 0;
+}
+
/*
* camss_probe - Probe CAMSS platform device
* @pdev: Pointer to CAMSS platform device
@@ -1326,6 +1560,12 @@ static int camss_probe(struct platform_device *pdev)
camss->csiphy_num = 4;
camss->csid_num = 3;
camss->vfe_num = 3;
+ } else if (of_device_is_compatible(dev->of_node,
+ "qcom,sm8250-camss")) {
+ camss->version = CAMSS_8250;
+ camss->csiphy_num = 6;
+ camss->csid_num = 4;
+ camss->vfe_num = 4;
} else {
ret = -EINVAL;
goto err_free;
@@ -1369,6 +1609,10 @@ static int camss_probe(struct platform_device *pdev)
goto err_cleanup;
}
+ ret = camss_icc_get(camss);
+ if (ret < 0)
+ goto err_cleanup;
+
ret = camss_init_subdevices(camss);
if (ret < 0)
goto err_cleanup;
@@ -1457,7 +1701,8 @@ void camss_delete(struct camss *camss)
if (camss->version == CAMSS_8x96 ||
camss->version == CAMSS_660)
nbr_pm_domains = PM_DOMAIN_GEN1_COUNT;
- else if (camss->version == CAMSS_845)
+ else if (camss->version == CAMSS_845 ||
+ camss->version == CAMSS_8250)
nbr_pm_domains = PM_DOMAIN_GEN2_COUNT;
for (i = 0; i < nbr_pm_domains; i++) {
@@ -1493,6 +1738,7 @@ static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,msm8996-camss" },
{ .compatible = "qcom,sdm660-camss" },
{ .compatible = "qcom,sdm845-camss" },
+ { .compatible = "qcom,sm8250-camss" },
{ }
};
@@ -1500,11 +1746,41 @@ MODULE_DEVICE_TABLE(of, camss_dt_match);
static int __maybe_unused camss_runtime_suspend(struct device *dev)
{
+ struct camss *camss = dev_get_drvdata(dev);
+ int nbr_icc_paths = 0;
+ int i;
+ int ret;
+
+ if (camss->version == CAMSS_8250)
+ nbr_icc_paths = ICC_SM8250_COUNT;
+
+ for (i = 0; i < nbr_icc_paths; i++) {
+ ret = icc_set_bw(camss->icc_path[i], 0, 0);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
static int __maybe_unused camss_runtime_resume(struct device *dev)
{
+ struct camss *camss = dev_get_drvdata(dev);
+ int nbr_icc_paths = 0;
+ int i;
+ int ret;
+
+ if (camss->version == CAMSS_8250)
+ nbr_icc_paths = ICC_SM8250_COUNT;
+
+ for (i = 0; i < nbr_icc_paths; i++) {
+ ret = icc_set_bw(camss->icc_path[i],
+ camss->icc_bw_tbl[i].avg,
+ camss->icc_bw_tbl[i].peak);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index dc8b4154f92b..c9b3e0df5be8 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -42,7 +42,7 @@
#define CAMSS_RES_MAX 17
struct resources {
- char *regulator[CAMSS_RES_MAX];
+ char *regulators[CAMSS_RES_MAX];
char *clock[CAMSS_RES_MAX];
u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX];
char *reg[CAMSS_RES_MAX];
@@ -56,6 +56,16 @@ struct resources_ispif {
char *interrupt;
};
+struct icc_bw_tbl {
+ u32 avg;
+ u32 peak;
+};
+
+struct resources_icc {
+ char *name;
+ struct icc_bw_tbl icc_bw_tbl;
+};
+
enum pm_domain {
PM_DOMAIN_VFE0 = 0,
PM_DOMAIN_VFE1 = 1,
@@ -69,6 +79,12 @@ enum camss_version {
CAMSS_8x96,
CAMSS_660,
CAMSS_845,
+ CAMSS_8250,
+};
+
+enum icc_count {
+ ICC_DEFAULT_COUNT = 0,
+ ICC_SM8250_COUNT = 4,
};
struct camss {
@@ -87,6 +103,8 @@ struct camss {
atomic_t ref_count;
struct device *genpd[PM_DOMAIN_GEN2_COUNT];
struct device_link *genpd_link[PM_DOMAIN_GEN2_COUNT];
+ struct icc_path *icc_path[ICC_SM8250_COUNT];
+ struct icc_bw_tbl icc_bw_tbl[ICC_SM8250_COUNT];
};
struct camss_camera_interface {
diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig
new file mode 100644
index 000000000000..bfd50e8f3421
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/Kconfig
@@ -0,0 +1,14 @@
+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
+ select QCOM_MDT_LOADER if ARCH_QCOM
+ select QCOM_SCM
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a V4L2 driver for Qualcomm Venus video accelerator
+ hardware. It accelerates encoding and decoding operations
+ on various Qualcomm SoCs.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 7c3bac01cd49..c3023340d95c 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -127,6 +127,7 @@ struct venus_format {
* @done: a completion for sync HFI operations
* @error: an error returned during last HFI sync operations
* @sys_error: an error flag that signal system error event
+ * @sys_err_done: a waitqueue to wait for system error recovery end
* @core_ops: the core operations
* @pm_ops: a pointer to pm operations
* @pm_lock: a lock for PM operations
@@ -346,6 +347,7 @@ enum venus_inst_modes {
* @width: current capture width
* @height: current capture height
* @crop: current crop rectangle
+ * @fw_min_cnt: firmware minimum buffer count
* @out_width: current output width
* @out_height: current output height
* @colorspace: current color space
@@ -390,6 +392,8 @@ enum venus_inst_modes {
* @pic_struct: bitstream progressive vs interlaced
* @next_buf_last: a flag to mark next queued capture buffer as last
* @drain_active: Drain sequence is in progress
+ * @flags: bitmask flags describing current instance mode
+ * @dpb_ids: DPB buffer ID's
*/
struct venus_inst {
struct list_head list;
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 84c3a511ec31..0bca95d01650 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -189,7 +189,6 @@ int venus_helper_alloc_dpb_bufs(struct venus_inst *inst)
buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL,
buf->attrs);
if (!buf->va) {
- kfree(buf);
ret = -ENOMEM;
goto fail;
}
@@ -209,6 +208,7 @@ int venus_helper_alloc_dpb_bufs(struct venus_inst *inst)
return 0;
fail:
+ kfree(buf);
venus_helper_free_dpb_bufs(inst);
return ret;
}
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c
index 5aea07307e02..4ecd444050bb 100644
--- a/drivers/media/platform/qcom/venus/hfi_cmds.c
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.c
@@ -1054,6 +1054,8 @@ static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt,
pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info);
break;
}
+ case HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI:
+ return -ENOTSUPP;
/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index 84bafc3118cc..adea4c3b8c20 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -662,8 +662,8 @@ static int venc_set_properties(struct venus_inst *inst)
ptype = HFI_PROPERTY_PARAM_VENC_H264_TRANSFORM_8X8;
h264_transform.enable_type = 0;
- if (ctr->profile.h264 == HFI_H264_PROFILE_HIGH ||
- ctr->profile.h264 == HFI_H264_PROFILE_CONSTRAINED_HIGH)
+ if (ctr->profile.h264 == V4L2_MPEG_VIDEO_H264_PROFILE_HIGH ||
+ ctr->profile.h264 == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH)
h264_transform.enable_type = ctr->h264_8x8_transform;
ret = hfi_session_set_property(inst, ptype, &h264_transform);
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index 1ada42df314d..ea5805e71c14 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -320,8 +320,8 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
ctr->intra_refresh_period = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM:
- if (ctr->profile.h264 != HFI_H264_PROFILE_HIGH &&
- ctr->profile.h264 != HFI_H264_PROFILE_CONSTRAINED_HIGH)
+ if (ctr->profile.h264 != V4L2_MPEG_VIDEO_H264_PROFILE_HIGH &&
+ ctr->profile.h264 != V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH)
return -EINVAL;
/*
@@ -457,7 +457,7 @@ int venc_ctrl_init(struct venus_inst *inst)
V4L2_CID_MPEG_VIDEO_H264_I_FRAME_MIN_QP, 1, 51, 1, 1);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, 0, 1, 1, 0);
+ V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM, 0, 1, 1, 1);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_P_FRAME_MIN_QP, 1, 51, 1, 1);
diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig
new file mode 100644
index 000000000000..9fd90672ea2d
--- /dev/null
+++ b/drivers/media/platform/renesas/Kconfig
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Renesas media platform drivers"
+
+# V4L drivers
+
+config VIDEO_RENESAS_CEU
+ tristate "Renesas Capture Engine Unit (CEU) driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_SHMOBILE || ARCH_R7S72100 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ help
+ This is a v4l2 driver for the Renesas CEU Interface
+
+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
+ depends on VIDEO_DEV && I2C
+ depends on ARCH_SHMOBILE || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Support for the Video Output Unit (VOU) on SuperH SoCs.
+
+source "drivers/media/platform/renesas/rcar-vin/Kconfig"
+
+# Mem2mem drivers
+
+config VIDEO_RENESAS_FCP
+ tristate "Renesas Frame Compression Processor"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on OF
+ help
+ This is a driver for the Renesas Frame Compression Processor (FCP).
+ The FCP is a companion module of video processing modules in the
+ Renesas R-Car Gen3 and RZ/G2 SoCs. It handles memory access for
+ the codec, VSP and FDP modules.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rcar-fcp.
+
+config VIDEO_RENESAS_FDP1
+ tristate "Renesas Fine Display Processor"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a V4L2 driver for the Renesas Fine Display Processor
+ providing colour space conversion, and de-interlacing features.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rcar_fdp1.
+
+config VIDEO_RENESAS_JPU
+ tristate "Renesas JPEG Processing Unit"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a V4L2 driver for the Renesas JPEG Processing Unit.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rcar_jpu.
+
+config VIDEO_RENESAS_VSP1
+ tristate "Renesas VSP1 Video Processing Engine"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_RENESAS || COMPILE_TEST
+ depends on (!ARM64 && !VIDEO_RENESAS_FCP) || VIDEO_RENESAS_FCP
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ help
+ This is a V4L2 driver for the Renesas VSP1 video processing engine.
+
+ To compile this driver as a module, choose M here: the module
+ will be called vsp1.
+
+# SDR drivers
+
+config VIDEO_RCAR_DRIF
+ tristate "Renesas Digital Radio Interface (DRIF)"
+ depends on SDR_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select VIDEOBUF2_VMALLOC
+ select V4L2_ASYNC
+ help
+ Say Y if you want to enable R-Car Gen3 DRIF support. DRIF is Digital
+ Radio Interface that interfaces with an RF front end chip. It is a
+ receiver of digital data which uses DMA to transfer received data to
+ a configured location for an application to use.
+
+ To compile this driver as a module, choose M here; the module
+ will be called rcar_drif.
diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile
new file mode 100644
index 000000000000..3ec226ef5fd2
--- /dev/null
+++ b/drivers/media/platform/renesas/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the Renesas capture/playback device drivers.
+#
+
+obj-y += rcar-vin/
+obj-y += vsp1/
+
+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
+obj-$(CONFIG_VIDEO_RENESAS_JPU) += rcar_jpu.o
+obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
diff --git a/drivers/media/platform/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c
index eb59a3ba6d0f..eb59a3ba6d0f 100644
--- a/drivers/media/platform/rcar-fcp.c
+++ b/drivers/media/platform/renesas/rcar-fcp.c
diff --git a/drivers/media/platform/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp.c
index 2ffab30bc011..10b3474f93a4 100644
--- a/drivers/media/platform/rcar-isp.c
+++ b/drivers/media/platform/renesas/rcar-isp.c
@@ -17,6 +17,7 @@
#include <linux/pm_runtime.h>
#include <linux/reset.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-subdev.h>
#define ISPINPUTSEL0_REG 0x0008
@@ -51,12 +52,31 @@ struct rcar_isp_format {
};
static const struct rcar_isp_format rcar_isp_formats[] = {
- { .code = MEDIA_BUS_FMT_RGB888_1X24, .datatype = 0x24, .procmode = 0x15 },
- { .code = MEDIA_BUS_FMT_Y10_1X10, .datatype = 0x2b, .procmode = 0x10 },
- { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .procmode = 0x0c },
- { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .procmode = 0x0c },
- { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .procmode = 0x0c },
- { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .procmode = 0x0c },
+ {
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .datatype = MIPI_CSI2_DT_RGB888,
+ .procmode = 0x15
+ }, {
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x10,
+ }, {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .procmode = 0x0c,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .procmode = 0x0c,
+ }, {
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .procmode = 0x0c,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV10_2X10,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .procmode = 0x0c,
+ },
};
static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code)
diff --git a/drivers/media/platform/rcar-vin/Kconfig b/drivers/media/platform/renesas/rcar-vin/Kconfig
index 030312d862e7..de55fe63d84c 100644
--- a/drivers/media/platform/rcar-vin/Kconfig
+++ b/drivers/media/platform/renesas/rcar-vin/Kconfig
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
config VIDEO_RCAR_CSI2
tristate "R-Car MIPI CSI-2 Receiver"
- depends on VIDEO_V4L2 && OF
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
depends on ARCH_RENESAS || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
@@ -16,7 +17,8 @@ config VIDEO_RCAR_CSI2
config VIDEO_RCAR_VIN
tristate "R-Car Video Input (VIN) Driver"
- depends on VIDEO_V4L2 && OF
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
depends on ARCH_RENESAS || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/rcar-vin/Makefile b/drivers/media/platform/renesas/rcar-vin/Makefile
index 00d809f5d2c1..00d809f5d2c1 100644
--- a/drivers/media/platform/rcar-vin/Makefile
+++ b/drivers/media/platform/renesas/rcar-vin/Makefile
diff --git a/drivers/media/platform/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
index 0186ae235113..64cb05b3907c 100644
--- a/drivers/media/platform/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
@@ -743,27 +743,6 @@ static int rvin_parallel_init(struct rvin_dev *vin)
* CSI-2
*/
-static unsigned int rvin_csi2_get_mask(struct rvin_dev *vin,
- enum rvin_csi_id csi_id,
- unsigned char channel)
-{
- const struct rvin_group_route *route;
- unsigned int mask = 0;
-
- for (route = vin->info->routes; route->mask; route++) {
- if (route->vin == vin->id &&
- route->csi == csi_id &&
- route->channel == channel) {
- vin_dbg(vin,
- "Adding route: vin: %d csi: %d channel: %d\n",
- route->vin, route->csi, route->channel);
- mask |= route->mask;
- }
- }
-
- return mask;
-}
-
/*
* Link setup for the links between a VIN and a CSI-2 receiver is a bit
* complex. The reason for this is that the register controlling routing
@@ -793,12 +772,10 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags,
{
struct rvin_group *group = container_of(link->graph_obj.mdev,
struct rvin_group, mdev);
- unsigned int master_id, channel, mask_new, i;
- unsigned int mask = ~0;
struct media_entity *entity;
struct video_device *vdev;
- struct media_pad *csi_pad;
- struct rvin_dev *vin = NULL;
+ struct rvin_dev *vin;
+ unsigned int i;
int csi_id, ret;
ret = v4l2_pipeline_link_notify(link, flags, notification);
@@ -816,41 +793,16 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags,
* running streams.
*/
media_device_for_each_entity(entity, &group->mdev)
- if (entity->stream_count)
+ if (media_entity_is_streaming(entity))
return -EBUSY;
- mutex_lock(&group->lock);
-
/* Find the master VIN that controls the routes. */
vdev = media_entity_to_video_device(link->sink->entity);
vin = container_of(vdev, struct rvin_dev, vdev);
- master_id = rvin_group_id_to_master(vin->id);
- if (WARN_ON(!group->vin[master_id])) {
- ret = -ENODEV;
- goto out;
- }
-
- /* Build a mask for already enabled links. */
- for (i = master_id; i < master_id + 4; i++) {
- if (!group->vin[i])
- continue;
-
- /* Get remote CSI-2, if any. */
- csi_pad = media_entity_remote_pad(
- &group->vin[i]->vdev.entity.pads[0]);
- if (!csi_pad)
- continue;
-
- csi_id = rvin_group_entity_to_remote_id(group, csi_pad->entity);
- channel = rvin_group_csi_pad_to_channel(csi_pad->index);
-
- mask &= rvin_csi2_get_mask(group->vin[i], csi_id, channel);
- }
+ mutex_lock(&group->lock);
- /* Add the new link to the existing mask and check if it works. */
csi_id = rvin_group_entity_to_remote_id(group, link->source->entity);
-
if (csi_id == -ENODEV) {
struct v4l2_subdev *sd;
@@ -875,25 +827,58 @@ static int rvin_csi2_link_notify(struct media_link *link, u32 flags,
vin_err(vin, "Subdevice %s not registered to any VIN\n",
link->source->entity->name);
ret = -ENODEV;
- goto out;
- }
+ } else {
+ const struct rvin_group_route *route;
+ unsigned int chsel = UINT_MAX;
+ unsigned int master_id;
- channel = rvin_group_csi_pad_to_channel(link->source->index);
- mask_new = mask & rvin_csi2_get_mask(vin, csi_id, channel);
- vin_dbg(vin, "Try link change mask: 0x%x new: 0x%x\n", mask, mask_new);
+ master_id = rvin_group_id_to_master(vin->id);
- if (!mask_new) {
- ret = -EMLINK;
- goto out;
- }
+ if (WARN_ON(!group->vin[master_id])) {
+ ret = -ENODEV;
+ goto out;
+ }
- /* New valid CHSEL found, set the new value. */
- ret = rvin_set_channel_routing(group->vin[master_id], __ffs(mask_new));
- if (ret)
- goto out;
+ /* Make sure group is connected to same CSI-2 */
+ for (i = master_id; i < master_id + 4; i++) {
+ struct media_pad *csi_pad;
+
+ if (!group->vin[i])
+ continue;
+
+ /* Get remote CSI-2, if any. */
+ csi_pad = media_entity_remote_pad(
+ &group->vin[i]->vdev.entity.pads[0]);
+ if (!csi_pad)
+ continue;
+
+ if (csi_pad->entity != link->source->entity) {
+ vin_dbg(vin, "Already attached to %s\n",
+ csi_pad->entity->name);
+ ret = -EBUSY;
+ goto out;
+ }
+ }
+
+ for (route = vin->info->routes; route->chsel; route++) {
+ if (route->master == master_id && route->csi == csi_id) {
+ chsel = route->chsel;
+ break;
+ }
+ }
- vin->is_csi = true;
+ if (chsel == UINT_MAX) {
+ vin_err(vin, "No CHSEL value found\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = rvin_set_channel_routing(group->vin[master_id], chsel);
+ if (ret)
+ goto out;
+ vin->is_csi = true;
+ }
out:
mutex_unlock(&group->lock);
@@ -904,48 +889,60 @@ static const struct media_device_ops rvin_csi2_media_ops = {
.link_notify = rvin_csi2_link_notify,
};
+static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id,
+ const struct rvin_group_route *route)
+
+{
+ struct media_entity *source = &group->remotes[route->csi].subdev->entity;
+ struct media_entity *sink = &group->vin[id]->vdev.entity;
+ struct media_pad *sink_pad = &sink->pads[0];
+ unsigned int channel;
+ int ret;
+
+ for (channel = 0; channel < 4; channel++) {
+ unsigned int source_idx = rvin_group_csi_channel_to_pad(channel);
+ struct media_pad *source_pad = &source->pads[source_idx];
+
+ /* Skip if link already exists. */
+ if (media_entity_find_link(source_pad, sink_pad))
+ continue;
+
+ ret = media_create_pad_link(source, source_idx, sink, 0, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int rvin_csi2_setup_links(struct rvin_dev *vin)
{
const struct rvin_group_route *route;
+ unsigned int id;
int ret = -EINVAL;
/* Create all media device links between VINs and CSI-2's. */
mutex_lock(&vin->group->lock);
- for (route = vin->info->routes; route->mask; route++) {
- struct media_pad *source_pad, *sink_pad;
- struct media_entity *source, *sink;
- unsigned int source_idx;
-
- /* Check that VIN is part of the group. */
- if (!vin->group->vin[route->vin])
- continue;
-
+ for (route = vin->info->routes; route->chsel; route++) {
/* Check that VIN' master is part of the group. */
- if (!vin->group->vin[rvin_group_id_to_master(route->vin)])
+ if (!vin->group->vin[route->master])
continue;
/* Check that CSI-2 is part of the group. */
if (!vin->group->remotes[route->csi].subdev)
continue;
- source = &vin->group->remotes[route->csi].subdev->entity;
- source_idx = rvin_group_csi_channel_to_pad(route->channel);
- source_pad = &source->pads[source_idx];
-
- sink = &vin->group->vin[route->vin]->vdev.entity;
- sink_pad = &sink->pads[0];
-
- /* Skip if link already exists. */
- if (media_entity_find_link(source_pad, sink_pad))
- continue;
+ for (id = route->master; id < route->master + 4; id++) {
+ /* Check that VIN is part of the group. */
+ if (!vin->group->vin[id])
+ continue;
- ret = media_create_pad_link(source, source_idx, sink, 0, 0);
- if (ret) {
- vin_err(vin, "Error adding link from %s to %s\n",
- source->name, sink->name);
- break;
+ ret = rvin_csi2_create_link(vin->group, id, route);
+ if (ret)
+ goto out;
}
}
+out:
mutex_unlock(&vin->group->lock);
return ret;
@@ -1155,30 +1152,9 @@ static const struct rvin_info rcar_info_gen2 = {
};
static const struct rvin_group_route rcar_info_r8a774e1_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
+ { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 },
{ /* Sentinel */ }
};
@@ -1191,38 +1167,10 @@ static const struct rvin_info rcar_info_r8a774e1 = {
};
static const struct rvin_group_route rcar_info_r8a7795_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
- { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
- { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
+ { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 },
{ /* Sentinel */ }
};
@@ -1236,48 +1184,12 @@ static const struct rvin_info rcar_info_r8a7795 = {
};
static const struct rvin_group_route rcar_info_r8a7795es1_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI21, .channel = 0, .vin = 0, .mask = BIT(2) | BIT(5) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
- { .csi = RVIN_CSI21, .channel = 0, .vin = 1, .mask = BIT(1) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
- { .csi = RVIN_CSI21, .channel = 1, .vin = 1, .mask = BIT(5) },
- { .csi = RVIN_CSI21, .channel = 0, .vin = 2, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
- { .csi = RVIN_CSI21, .channel = 2, .vin = 2, .mask = BIT(5) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
- { .csi = RVIN_CSI21, .channel = 1, .vin = 3, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
- { .csi = RVIN_CSI21, .channel = 3, .vin = 3, .mask = BIT(5) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI21, .channel = 0, .vin = 4, .mask = BIT(2) | BIT(5) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
- { .csi = RVIN_CSI21, .channel = 0, .vin = 5, .mask = BIT(1) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
- { .csi = RVIN_CSI21, .channel = 1, .vin = 5, .mask = BIT(5) },
- { .csi = RVIN_CSI21, .channel = 0, .vin = 6, .mask = BIT(0) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
- { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
- { .csi = RVIN_CSI21, .channel = 2, .vin = 6, .mask = BIT(5) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
- { .csi = RVIN_CSI21, .channel = 1, .vin = 7, .mask = BIT(2) },
- { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
- { .csi = RVIN_CSI21, .channel = 3, .vin = 7, .mask = BIT(5) },
+ { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 0, .csi = RVIN_CSI21, .chsel = 0x05 },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
+ { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 4, .csi = RVIN_CSI21, .chsel = 0x05 },
+ { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 },
{ /* Sentinel */ }
};
@@ -1290,34 +1202,10 @@ static const struct rvin_info rcar_info_r8a7795es1 = {
};
static const struct rvin_group_route rcar_info_r8a7796_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
+ { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 },
{ /* Sentinel */ }
};
@@ -1331,38 +1219,10 @@ static const struct rvin_info rcar_info_r8a7796 = {
};
static const struct rvin_group_route rcar_info_r8a77965_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 0, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 1, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 1, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 2, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 2, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 2, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 3, .mask = BIT(1) | BIT(2) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 3, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 4, .mask = BIT(1) | BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 5, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 5, .mask = BIT(4) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 6, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 6, .mask = BIT(1) },
- { .csi = RVIN_CSI20, .channel = 0, .vin = 6, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 6, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 2, .vin = 6, .mask = BIT(4) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 7, .mask = BIT(0) },
- { .csi = RVIN_CSI20, .channel = 1, .vin = 7, .mask = BIT(1) | BIT(2) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 7, .mask = BIT(3) },
- { .csi = RVIN_CSI20, .channel = 3, .vin = 7, .mask = BIT(4) },
+ { .master = 0, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
+ { .master = 4, .csi = RVIN_CSI20, .chsel = 0x04 },
+ { .master = 4, .csi = RVIN_CSI40, .chsel = 0x03 },
{ /* Sentinel */ }
};
@@ -1376,13 +1236,7 @@ static const struct rvin_info rcar_info_r8a77965 = {
};
static const struct rvin_group_route rcar_info_r8a77970_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
{ /* Sentinel */ }
};
@@ -1395,22 +1249,8 @@ static const struct rvin_info rcar_info_r8a77970 = {
};
static const struct rvin_group_route rcar_info_r8a77980_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 0, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 0, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 1, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 1, .mask = BIT(1) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 2, .mask = BIT(1) },
- { .csi = RVIN_CSI40, .channel = 2, .vin = 2, .mask = BIT(3) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 3, .mask = BIT(0) },
- { .csi = RVIN_CSI40, .channel = 3, .vin = 3, .mask = BIT(3) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 4, .mask = BIT(2) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 5, .mask = BIT(2) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
- { .csi = RVIN_CSI41, .channel = 0, .vin = 6, .mask = BIT(1) },
- { .csi = RVIN_CSI41, .channel = 2, .vin = 6, .mask = BIT(3) },
- { .csi = RVIN_CSI41, .channel = 1, .vin = 7, .mask = BIT(0) },
- { .csi = RVIN_CSI41, .channel = 3, .vin = 7, .mask = BIT(3) },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
+ { .master = 4, .csi = RVIN_CSI41, .chsel = 0x03 },
{ /* Sentinel */ }
};
@@ -1424,10 +1264,7 @@ static const struct rvin_info rcar_info_r8a77980 = {
};
static const struct rvin_group_route rcar_info_r8a77990_routes[] = {
- { .csi = RVIN_CSI40, .channel = 0, .vin = 4, .mask = BIT(0) | BIT(3) },
- { .csi = RVIN_CSI40, .channel = 0, .vin = 5, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 4, .mask = BIT(2) },
- { .csi = RVIN_CSI40, .channel = 1, .vin = 5, .mask = BIT(1) | BIT(3) },
+ { .master = 0, .csi = RVIN_CSI40, .chsel = 0x03 },
{ /* Sentinel */ }
};
diff --git a/drivers/media/platform/rcar-vin/rcar-csi2.c b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c
index 8c939cb3073d..fea8f00a9152 100644
--- a/drivers/media/platform/rcar-vin/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-csi2.c
@@ -17,6 +17,7 @@
#include <linux/reset.h>
#include <linux/sys_soc.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -412,17 +413,51 @@ struct rcar_csi2_format {
};
static const struct rcar_csi2_format rcar_csi2_formats[] = {
- { .code = MEDIA_BUS_FMT_RGB888_1X24, .datatype = 0x24, .bpp = 24 },
- { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 },
- { .code = MEDIA_BUS_FMT_YUYV8_1X16, .datatype = 0x1e, .bpp = 16 },
- { .code = MEDIA_BUS_FMT_UYVY8_2X8, .datatype = 0x1e, .bpp = 16 },
- { .code = MEDIA_BUS_FMT_YUYV10_2X10, .datatype = 0x1e, .bpp = 20 },
- { .code = MEDIA_BUS_FMT_Y10_1X10, .datatype = 0x2b, .bpp = 10 },
- { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .datatype = 0x2a, .bpp = 8 },
- { .code = MEDIA_BUS_FMT_Y8_1X8, .datatype = 0x2a, .bpp = 8 },
+ {
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .datatype = MIPI_CSI2_DT_RGB888,
+ .bpp = 24,
+ }, {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .bpp = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .bpp = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_UYVY8_2X8,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .bpp = 16,
+ }, {
+ .code = MEDIA_BUS_FMT_YUYV10_2X10,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .bpp = 20,
+ }, {
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 8,
+ }, {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 8,
+ },
};
static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code)
@@ -468,6 +503,8 @@ struct rcar_csi2 {
struct v4l2_subdev *remote;
unsigned int remote_pad;
+ int channel_vc[4];
+
struct mutex lock; /* Protects mf and stream_count. */
struct v4l2_mbus_framefmt mf;
int stream_count;
@@ -603,7 +640,6 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
unsigned int *lanes)
{
struct v4l2_mbus_config mbus_config = { 0 };
- unsigned int num_lanes = UINT_MAX;
int ret;
*lanes = priv->lanes;
@@ -626,23 +662,14 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
return -EINVAL;
}
- if (mbus_config.flags & V4L2_MBUS_CSI2_1_LANE)
- num_lanes = 1;
- else if (mbus_config.flags & V4L2_MBUS_CSI2_2_LANE)
- num_lanes = 2;
- else if (mbus_config.flags & V4L2_MBUS_CSI2_3_LANE)
- num_lanes = 3;
- else if (mbus_config.flags & V4L2_MBUS_CSI2_4_LANE)
- num_lanes = 4;
-
- if (num_lanes > priv->lanes) {
+ if (mbus_config.bus.mipi_csi2.num_data_lanes > priv->lanes) {
dev_err(priv->dev,
"Unsupported mbus config: too many data lanes %u\n",
- num_lanes);
+ mbus_config.bus.mipi_csi2.num_data_lanes);
return -EINVAL;
}
- *lanes = num_lanes;
+ *lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
return 0;
}
@@ -675,8 +702,11 @@ static int rcsi2_start_receiver(struct rcar_csi2 *priv)
for (i = 0; i < priv->info->num_channels; i++) {
u32 vcdt_part;
- vcdt_part = VCDT_SEL_VC(i) | VCDT_VCDTN_EN | VCDT_SEL_DTN_ON |
- VCDT_SEL_DT(format->datatype);
+ if (priv->channel_vc[i] < 0)
+ continue;
+
+ vcdt_part = VCDT_SEL_VC(priv->channel_vc[i]) | VCDT_VCDTN_EN |
+ VCDT_SEL_DTN_ON | VCDT_SEL_DT(format->datatype);
/* Store in correct reg and offset. */
if (i < 2)
@@ -1258,7 +1288,52 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv,
* Platform Device Driver.
*/
+static int rcsi2_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+ struct video_device *vdev;
+ int channel, vc;
+ u32 id;
+
+ if (!is_media_entity_v4l2_video_device(remote->entity)) {
+ dev_err(priv->dev, "Remote is not a video device\n");
+ return -EINVAL;
+ }
+
+ vdev = media_entity_to_video_device(remote->entity);
+
+ if (of_property_read_u32(vdev->dev_parent->of_node, "renesas,id", &id)) {
+ dev_err(priv->dev, "No renesas,id, can't configure routing\n");
+ return -EINVAL;
+ }
+
+ channel = id % 4;
+
+ if (flags & MEDIA_LNK_FL_ENABLED) {
+ if (media_entity_remote_pad(local)) {
+ dev_dbg(priv->dev,
+ "Each VC can only be routed to one output channel\n");
+ return -EINVAL;
+ }
+
+ vc = local->index - 1;
+
+ dev_dbg(priv->dev, "Route VC%d to VIN%u on output channel %d\n",
+ vc, id, channel);
+ } else {
+ vc = -1;
+ }
+
+ priv->channel_vc[channel] = vc;
+
+ return 0;
+}
+
static const struct media_entity_operations rcar_csi2_entity_ops = {
+ .link_setup = rcsi2_link_setup,
.link_validate = v4l2_subdev_link_validate,
};
@@ -1414,7 +1489,7 @@ static const struct soc_device_attribute r8a7795[] = {
.soc_id = "r8a7795", .revision = "ES2.*",
.data = &rcar_csi2_info_r8a7795es2,
},
- { /* sentinel */ },
+ { /* sentinel */ }
};
static int rcsi2_probe(struct platform_device *pdev)
@@ -1477,6 +1552,9 @@ static int rcsi2_probe(struct platform_device *pdev)
if (ret)
goto error_async;
+ for (i = 0; i < ARRAY_SIZE(priv->channel_vc); i++)
+ priv->channel_vc[i] = -1;
+
pm_runtime_enable(&pdev->dev);
ret = v4l2_async_register_subdev(&priv->subdev);
diff --git a/drivers/media/platform/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index 8136bc75e7c4..2272f1c96aaf 100644
--- a/drivers/media/platform/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -1507,7 +1507,7 @@ int rvin_set_channel_routing(struct rvin_dev *vin, u8 chsel)
* register. IFMD_DES1 controls data expansion mode for CSI20/21,
* IFMD_DES0 controls data expansion mode for CSI40/41.
*/
- for (route = vin->info->routes; route->mask; route++) {
+ for (route = vin->info->routes; route->chsel; route++) {
if (route->csi == RVIN_CSI20 || route->csi == RVIN_CSI21)
ifmd |= VNCSI_IFMD_DES1;
else
diff --git a/drivers/media/platform/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
index 2e60b9fce03b..2e60b9fce03b 100644
--- a/drivers/media/platform/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
diff --git a/drivers/media/platform/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
index 6c06320174a2..1f94589d9ef1 100644
--- a/drivers/media/platform/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
@@ -118,7 +118,7 @@ struct rvin_parallel_entity {
struct v4l2_subdev *subdev;
enum v4l2_mbus_type mbus_type;
- struct v4l2_fwnode_bus_parallel bus;
+ struct v4l2_mbus_config_parallel bus;
unsigned int source_pad;
unsigned int sink_pad;
@@ -128,11 +128,9 @@ struct rvin_parallel_entity {
* struct rvin_group_route - describes a route from a channel of a
* CSI-2 receiver to a VIN
*
+ * @master: VIN group master ID.
* @csi: CSI-2 receiver ID.
- * @channel: Output channel of the CSI-2 receiver.
- * @vin: VIN ID.
- * @mask: Bitmask of the different CHSEL register values that
- * allow for a route from @csi + @chan to @vin.
+ * @chsel: CHSEL register values that connects VIN group to CSI-2.
*
* .. note::
* Each R-Car CSI-2 receiver has four output channels facing the VIN
@@ -140,19 +138,11 @@ struct rvin_parallel_entity {
* There is no correlation between channel number and CSI-2 VC. It's
* up to the CSI-2 receiver driver to configure which VC is output
* on which channel, the VIN devices only care about output channels.
- *
- * There are in some cases multiple CHSEL register settings which would
- * allow for the same route from @csi + @channel to @vin. For example
- * on R-Car H3 both the CHSEL values 0 and 3 allow for a route from
- * CSI40/VC0 to VIN0. All possible CHSEL values for a route need to be
- * recorded as a bitmask in @mask, in this example bit 0 and 3 should
- * be set.
*/
struct rvin_group_route {
+ unsigned int master;
enum rvin_csi_id csi;
- unsigned int channel;
- unsigned int vin;
- unsigned int mask;
+ unsigned int chsel;
};
/**
diff --git a/drivers/media/platform/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c
index 9a0982fa5c6b..9a0982fa5c6b 100644
--- a/drivers/media/platform/rcar_drif.c
+++ b/drivers/media/platform/renesas/rcar_drif.c
diff --git a/drivers/media/platform/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c
index 37ecf489d112..37ecf489d112 100644
--- a/drivers/media/platform/rcar_fdp1.c
+++ b/drivers/media/platform/renesas/rcar_fdp1.c
diff --git a/drivers/media/platform/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c
index 56bb464629ed..293beba131e2 100644
--- a/drivers/media/platform/rcar_jpu.c
+++ b/drivers/media/platform/renesas/rcar_jpu.c
@@ -4,7 +4,7 @@
* Copyright (C) 2014-2015 Cogent Embedded, Inc. <source@cogentembedded.com>
* Copyright (C) 2014-2015 Renesas Electronics Corporation
*
- * This is based on the drivers/media/platform/s5p-jpeg driver by
+ * This is based on the drivers/media/platform/samsung/s5p-jpeg driver by
* Andrzej Pietrasiewicz and Jacek Anaszewski.
* Some portions of code inspired by VSP1 driver by Laurent Pinchart.
*
diff --git a/drivers/media/platform/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c
index 2e8dbacc414e..2e8dbacc414e 100644
--- a/drivers/media/platform/renesas-ceu.c
+++ b/drivers/media/platform/renesas/renesas-ceu.c
diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c
index ca4310e26c49..ca4310e26c49 100644
--- a/drivers/media/platform/sh_vou.c
+++ b/drivers/media/platform/renesas/sh_vou.c
diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile
index 4bb4dcbef7b5..4bb4dcbef7b5 100644
--- a/drivers/media/platform/vsp1/Makefile
+++ b/drivers/media/platform/renesas/vsp1/Makefile
diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h
index 37cf33c7e6ca..37cf33c7e6ca 100644
--- a/drivers/media/platform/vsp1/vsp1.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1.h
diff --git a/drivers/media/platform/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
index 89385b4cabe5..89385b4cabe5 100644
--- a/drivers/media/platform/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
diff --git a/drivers/media/platform/vsp1/vsp1_brx.h b/drivers/media/platform/renesas/vsp1/vsp1_brx.h
index 6abbb8c3343c..6abbb8c3343c 100644
--- a/drivers/media/platform/vsp1/vsp1_brx.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.h
diff --git a/drivers/media/platform/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
index c5217fee24f1..c5217fee24f1 100644
--- a/drivers/media/platform/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
diff --git a/drivers/media/platform/vsp1/vsp1_clu.h b/drivers/media/platform/renesas/vsp1/vsp1_clu.h
index cef2f44481ba..cef2f44481ba 100644
--- a/drivers/media/platform/vsp1/vsp1_clu.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.h
diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index ad3fa1c9cc73..ad3fa1c9cc73 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/renesas/vsp1/vsp1_dl.h
index bebe16483ca5..bebe16483ca5 100644
--- a/drivers/media/platform/vsp1/vsp1_dl.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.h
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index 0c2507dc03d6..0c2507dc03d6 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h
index ab8b7e3161a2..ab8b7e3161a2 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h
diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index c9044785b903..502c7d9d6890 100644
--- a/drivers/media/platform/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -550,6 +550,16 @@ static int vsp1_device_init(struct vsp1_device *vsp1)
return 0;
}
+static void vsp1_mask_all_interrupts(struct vsp1_device *vsp1)
+{
+ unsigned int i;
+
+ for (i = 0; i < vsp1->info->lif_count; ++i)
+ vsp1_write(vsp1, VI6_DISP_IRQ_ENB(i), 0);
+ for (i = 0; i < vsp1->info->wpf_count; ++i)
+ vsp1_write(vsp1, VI6_WPF_IRQ_ENB(i), 0);
+}
+
/*
* vsp1_device_get - Acquire the VSP1 device
*
@@ -794,9 +804,9 @@ static int vsp1_probe(struct platform_device *pdev)
{
struct vsp1_device *vsp1;
struct device_node *fcp_node;
- struct resource *irq;
unsigned int i;
int ret;
+ int irq;
vsp1 = devm_kzalloc(&pdev->dev, sizeof(*vsp1), GFP_KERNEL);
if (vsp1 == NULL)
@@ -813,18 +823,9 @@ static int vsp1_probe(struct platform_device *pdev)
if (IS_ERR(vsp1->mmio))
return PTR_ERR(vsp1->mmio);
- irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!irq) {
- dev_err(&pdev->dev, "missing IRQ\n");
- return -EINVAL;
- }
-
- ret = devm_request_irq(&pdev->dev, irq->start, vsp1_irq_handler,
- IRQF_SHARED, dev_name(&pdev->dev), vsp1);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to request IRQ\n");
- return ret;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
/* FCP (optional). */
fcp_node = of_parse_phandle(pdev->dev.of_node, "renesas,fcp", 0);
@@ -855,7 +856,6 @@ static int vsp1_probe(struct platform_device *pdev)
goto done;
vsp1->version = vsp1_read(vsp1, VI6_IP_VERSION);
- vsp1_device_put(vsp1);
for (i = 0; i < ARRAY_SIZE(vsp1_device_infos); ++i) {
if ((vsp1->version & VI6_IP_VERSION_MODEL_MASK) ==
@@ -868,12 +868,31 @@ static int vsp1_probe(struct platform_device *pdev)
if (!vsp1->info) {
dev_err(&pdev->dev, "unsupported IP version 0x%08x\n",
vsp1->version);
+ vsp1_device_put(vsp1);
ret = -ENXIO;
goto done;
}
dev_dbg(&pdev->dev, "IP version 0x%08x\n", vsp1->version);
+ /*
+ * Previous use of the hardware (e.g. by the bootloader) could leave
+ * some interrupts enabled and pending.
+ *
+ * TODO: Investigate if this shouldn't be better handled by using the
+ * device reset provided by the CPG.
+ */
+ vsp1_mask_all_interrupts(vsp1);
+
+ vsp1_device_put(vsp1);
+
+ ret = devm_request_irq(&pdev->dev, irq, vsp1_irq_handler,
+ IRQF_SHARED, dev_name(&pdev->dev), vsp1);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto done;
+ }
+
/* Instantiate entities. */
ret = vsp1_create_entities(vsp1);
if (ret < 0) {
diff --git a/drivers/media/platform/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
index 823c15facd1b..823c15facd1b 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
diff --git a/drivers/media/platform/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
index f22724439cdc..f22724439cdc 100644
--- a/drivers/media/platform/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
diff --git a/drivers/media/platform/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
index bf3f981f93a1..bf3f981f93a1 100644
--- a/drivers/media/platform/vsp1/vsp1_hgo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
diff --git a/drivers/media/platform/vsp1/vsp1_hgo.h b/drivers/media/platform/renesas/vsp1/vsp1_hgo.h
index 6b0c8580e1bf..6b0c8580e1bf 100644
--- a/drivers/media/platform/vsp1/vsp1_hgo.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.h
diff --git a/drivers/media/platform/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
index aa1c718e0453..aa1c718e0453 100644
--- a/drivers/media/platform/vsp1/vsp1_hgt.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
diff --git a/drivers/media/platform/vsp1/vsp1_hgt.h b/drivers/media/platform/renesas/vsp1/vsp1_hgt.h
index 38ec237bdd2d..38ec237bdd2d 100644
--- a/drivers/media/platform/vsp1/vsp1_hgt.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.h
diff --git a/drivers/media/platform/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
index 5e5013d2cd2a..5e5013d2cd2a 100644
--- a/drivers/media/platform/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
diff --git a/drivers/media/platform/vsp1/vsp1_histo.h b/drivers/media/platform/renesas/vsp1/vsp1_histo.h
index 06f029846244..06f029846244 100644
--- a/drivers/media/platform/vsp1/vsp1_histo.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.h
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
index 361a870380c2..361a870380c2 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
diff --git a/drivers/media/platform/vsp1/vsp1_hsit.h b/drivers/media/platform/renesas/vsp1/vsp1_hsit.h
index a658b1aa49e7..a658b1aa49e7 100644
--- a/drivers/media/platform/vsp1/vsp1_hsit.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.h
diff --git a/drivers/media/platform/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c
index 6a6857ac9327..6a6857ac9327 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c
diff --git a/drivers/media/platform/vsp1/vsp1_lif.h b/drivers/media/platform/renesas/vsp1/vsp1_lif.h
index 71a4eda9c2b2..71a4eda9c2b2 100644
--- a/drivers/media/platform/vsp1/vsp1_lif.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.h
diff --git a/drivers/media/platform/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
index ac6802a325f5..ac6802a325f5 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
diff --git a/drivers/media/platform/vsp1/vsp1_lut.h b/drivers/media/platform/renesas/vsp1/vsp1_lut.h
index 8cb0df1b7e27..8cb0df1b7e27 100644
--- a/drivers/media/platform/vsp1/vsp1_lut.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.h
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
index f72ac01c21ea..f72ac01c21ea 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
index ae646c9ef337..ae646c9ef337 100644
--- a/drivers/media/platform/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
index fae7286eb01e..fae7286eb01e 100644
--- a/drivers/media/platform/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
index 85587c1b6a37..85587c1b6a37 100644
--- a/drivers/media/platform/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
index 22a82d218152..22a82d218152 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
index eac5c04c2239..eac5c04c2239 100644
--- a/drivers/media/platform/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
diff --git a/drivers/media/platform/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
index b614a2aea461..b614a2aea461 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
diff --git a/drivers/media/platform/vsp1/vsp1_sru.h b/drivers/media/platform/renesas/vsp1/vsp1_sru.h
index ddb00eadd1ea..ddb00eadd1ea 100644
--- a/drivers/media/platform/vsp1/vsp1_sru.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.h
diff --git a/drivers/media/platform/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
index 1c290cda005a..1c290cda005a 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
diff --git a/drivers/media/platform/vsp1/vsp1_uds.h b/drivers/media/platform/renesas/vsp1/vsp1_uds.h
index c34f95a666d2..c34f95a666d2 100644
--- a/drivers/media/platform/vsp1/vsp1_uds.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.h
diff --git a/drivers/media/platform/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
index 83d7f17df80e..83d7f17df80e 100644
--- a/drivers/media/platform/vsp1/vsp1_uif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
diff --git a/drivers/media/platform/vsp1/vsp1_uif.h b/drivers/media/platform/renesas/vsp1/vsp1_uif.h
index c71ab5f6a6f8..c71ab5f6a6f8 100644
--- a/drivers/media/platform/vsp1/vsp1_uif.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.h
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c
index 044eb5778820..044eb5778820 100644
--- a/drivers/media/platform/vsp1/vsp1_video.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c
diff --git a/drivers/media/platform/vsp1/vsp1_video.h b/drivers/media/platform/renesas/vsp1/vsp1_video.h
index f3cf5e2fdf5a..f3cf5e2fdf5a 100644
--- a/drivers/media/platform/vsp1/vsp1_video.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_video.h
diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
index 94e91d7bb56c..94e91d7bb56c 100644
--- a/drivers/media/platform/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig
new file mode 100644
index 000000000000..b41d3960c1b4
--- /dev/null
+++ b/drivers/media/platform/rockchip/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Rockchip media platform drivers"
+
+source "drivers/media/platform/rockchip/rga/Kconfig"
+source "drivers/media/platform/rockchip/rkisp1/Kconfig"
diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile
new file mode 100644
index 000000000000..4f782b876ac9
--- /dev/null
+++ b/drivers/media/platform/rockchip/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += rga/
+obj-y += rkisp1/
diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig
new file mode 100644
index 000000000000..727a0f6ea466
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/Kconfig
@@ -0,0 +1,14 @@
+config VIDEO_ROCKCHIP_RGA
+ tristate "Rockchip Raster 2d Graphic Acceleration Unit"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select VIDEOBUF2_DMA_SG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Rockchip SOC RGA 2d graphics accelerator.
+ Rockchip RGA is a separate 2D raster graphic acceleration unit.
+ It accelerates 2D graphics operations, such as point/line drawing,
+ image scaling, rotation, BitBLT, alpha blending and image blur/sharpness.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/rockchip/rkisp1/Kconfig b/drivers/media/platform/rockchip/rkisp1/Kconfig
new file mode 100644
index 000000000000..dabd7e42c193
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkisp1/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_ROCKCHIP_ISP1
+ tristate "Rockchip Image Signal Processing v1 Unit driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_FWNODE
+ select GENERIC_PHY_MIPI_DPHY
+ default n
+ help
+ Enable this to support the Image Signal Processing (ISP) module
+ present in RK3399 SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rockchip-isp1.
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
index 768987d5f2dd..fee2aaacb26b 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
@@ -249,7 +249,7 @@ static const struct rkisp1_capture_fmt_cfg rkisp1_sp_fmts[] = {
.fourcc = V4L2_PIX_FMT_GREY,
.uv_swap = 0,
.write_format = RKISP1_MI_CTRL_SP_WRITE_PLA,
- .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV400,
+ .output_format = RKISP1_MI_CTRL_SP_OUTPUT_YUV422,
.mbus = MEDIA_BUS_FMT_YUYV8_2X8,
},
/* rgb */
@@ -631,12 +631,26 @@ static void rkisp1_set_next_buf(struct rkisp1_capture *cap)
rkisp1_write(cap->rkisp1,
buff_addr[RKISP1_PLANE_Y],
cap->config->mi.y_base_ad_init);
- rkisp1_write(cap->rkisp1,
- buff_addr[RKISP1_PLANE_CB],
- cap->config->mi.cb_base_ad_init);
- rkisp1_write(cap->rkisp1,
- buff_addr[RKISP1_PLANE_CR],
- cap->config->mi.cr_base_ad_init);
+ /*
+ * In order to support grey format we capture
+ * YUV422 planar format from the camera and
+ * set the U and V planes to the dummy buffer
+ */
+ if (cap->pix.cfg->fourcc == V4L2_PIX_FMT_GREY) {
+ rkisp1_write(cap->rkisp1,
+ cap->buf.dummy.dma_addr,
+ cap->config->mi.cb_base_ad_init);
+ rkisp1_write(cap->rkisp1,
+ cap->buf.dummy.dma_addr,
+ cap->config->mi.cr_base_ad_init);
+ } else {
+ rkisp1_write(cap->rkisp1,
+ buff_addr[RKISP1_PLANE_CB],
+ cap->config->mi.cb_base_ad_init);
+ rkisp1_write(cap->rkisp1,
+ buff_addr[RKISP1_PLANE_CR],
+ cap->config->mi.cr_base_ad_init);
+ }
} else {
/*
* Use the dummy space allocated by dma_alloc_coherent to
diff --git a/drivers/media/platform/samsung/Kconfig b/drivers/media/platform/samsung/Kconfig
new file mode 100644
index 000000000000..0e34c5fc1dfc
--- /dev/null
+++ b/drivers/media/platform/samsung/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Samsung media platform drivers"
+
+source "drivers/media/platform/samsung/exynos-gsc/Kconfig"
+source "drivers/media/platform/samsung/exynos4-is/Kconfig"
+source "drivers/media/platform/samsung/s3c-camif/Kconfig"
+source "drivers/media/platform/samsung/s5p-g2d/Kconfig"
+source "drivers/media/platform/samsung/s5p-jpeg/Kconfig"
+source "drivers/media/platform/samsung/s5p-mfc/Kconfig"
diff --git a/drivers/media/platform/samsung/Makefile b/drivers/media/platform/samsung/Makefile
new file mode 100644
index 000000000000..21fea3330e4b
--- /dev/null
+++ b/drivers/media/platform/samsung/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += exynos-gsc/
+obj-y += exynos4-is/
+obj-y += s3c-camif/
+obj-y += s5p-g2d/
+obj-y += s5p-jpeg/
+obj-y += s5p-mfc/
diff --git a/drivers/media/platform/samsung/exynos-gsc/Kconfig b/drivers/media/platform/samsung/exynos-gsc/Kconfig
new file mode 100644
index 000000000000..7244d63c9646
--- /dev/null
+++ b/drivers/media/platform/samsung/exynos-gsc/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SAMSUNG_EXYNOS_GSC
+ tristate "Samsung Exynos G-Scaler driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_EXYNOS || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Samsung EXYNOS5 SoC G-Scaler.
diff --git a/drivers/media/platform/exynos-gsc/Makefile b/drivers/media/platform/samsung/exynos-gsc/Makefile
index bcefbad17a73..bcefbad17a73 100644
--- a/drivers/media/platform/exynos-gsc/Makefile
+++ b/drivers/media/platform/samsung/exynos-gsc/Makefile
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c
index cfd6ae70b8d8..e3559b047092 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c
@@ -1106,9 +1106,9 @@ MODULE_DEVICE_TABLE(of, exynos_gsc_match);
static int gsc_probe(struct platform_device *pdev)
{
struct gsc_dev *gsc;
- struct resource *res;
struct device *dev = &pdev->dev;
const struct gsc_driverdata *drv_data = of_device_get_match_data(dev);
+ int irq;
int ret;
int i;
@@ -1141,11 +1141,9 @@ static int gsc_probe(struct platform_device *pdev)
if (IS_ERR(gsc->regs))
return PTR_ERR(gsc->regs);
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "failed to get IRQ resource\n");
- return -ENXIO;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
for (i = 0; i < gsc->num_clocks; i++) {
gsc->clock[i] = devm_clk_get(dev, drv_data->clk_names[i]);
@@ -1167,8 +1165,8 @@ static int gsc_probe(struct platform_device *pdev)
}
}
- ret = devm_request_irq(dev, res->start, gsc_irq_handler,
- 0, pdev->name, gsc);
+ ret = devm_request_irq(dev, irq, gsc_irq_handler,
+ 0, pdev->name, gsc);
if (ret) {
dev_err(dev, "failed to install irq (%d)\n", ret);
goto err_clk;
diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h
index e894e85e84a4..e894e85e84a4 100644
--- a/drivers/media/platform/exynos-gsc/gsc-core.h
+++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.h
diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c
index f1cf847d1cc2..f1cf847d1cc2 100644
--- a/drivers/media/platform/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c
diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.c b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.c
index 995a1f0f875d..995a1f0f875d 100644
--- a/drivers/media/platform/exynos-gsc/gsc-regs.c
+++ b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.c
diff --git a/drivers/media/platform/exynos-gsc/gsc-regs.h b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.h
index d4f7ead6b322..d4f7ead6b322 100644
--- a/drivers/media/platform/exynos-gsc/gsc-regs.h
+++ b/drivers/media/platform/samsung/exynos-gsc/gsc-regs.h
diff --git a/drivers/media/platform/exynos4-is/Kconfig b/drivers/media/platform/samsung/exynos4-is/Kconfig
index 136d3b2a0fbb..da33faa7132e 100644
--- a/drivers/media/platform/exynos4-is/Kconfig
+++ b/drivers/media/platform/samsung/exynos4-is/Kconfig
@@ -2,7 +2,8 @@
config VIDEO_SAMSUNG_EXYNOS4_IS
tristate "Samsung S5P/EXYNOS4 SoC series Camera Subsystem driver"
- depends on VIDEO_V4L2 && OF && COMMON_CLK
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF && COMMON_CLK
depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/exynos4-is/Makefile b/drivers/media/platform/samsung/exynos4-is/Makefile
index a5ab01c73b95..a5ab01c73b95 100644
--- a/drivers/media/platform/exynos4-is/Makefile
+++ b/drivers/media/platform/samsung/exynos4-is/Makefile
diff --git a/drivers/media/platform/exynos4-is/common.c b/drivers/media/platform/samsung/exynos4-is/common.c
index 944b224eb621..023f624d29d5 100644
--- a/drivers/media/platform/exynos4-is/common.c
+++ b/drivers/media/platform/samsung/exynos4-is/common.c
@@ -10,7 +10,10 @@
#include <media/drv-intf/exynos-fimc.h>
#include "common.h"
-/* Called with the media graph mutex held or entity->stream_count > 0. */
+/*
+ * Called with the media graph mutex held or media_entity_is_streaming(entity)
+ * true.
+ */
struct v4l2_subdev *fimc_find_remote_sensor(struct media_entity *entity)
{
struct media_pad *pad = &entity->pads[0];
diff --git a/drivers/media/platform/exynos4-is/common.h b/drivers/media/platform/samsung/exynos4-is/common.h
index 0389b66e5144..0389b66e5144 100644
--- a/drivers/media/platform/exynos4-is/common.h
+++ b/drivers/media/platform/samsung/exynos4-is/common.h
diff --git a/drivers/media/platform/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
index 7ff4024003f4..7ff4024003f4 100644
--- a/drivers/media/platform/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
diff --git a/drivers/media/platform/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c
index bfdee771cef9..91cc8d58a663 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c
@@ -926,6 +926,7 @@ static int fimc_probe(struct platform_device *pdev)
struct fimc_dev *fimc;
struct resource *res;
int ret = 0;
+ int irq;
fimc = devm_kzalloc(dev, sizeof(*fimc), GFP_KERNEL);
if (!fimc)
@@ -965,11 +966,9 @@ static int fimc_probe(struct platform_device *pdev)
if (IS_ERR(fimc->regs))
return PTR_ERR(fimc->regs);
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(dev, "Failed to get IRQ resource\n");
- return -ENXIO;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
ret = fimc_clk_get(fimc);
if (ret)
@@ -986,7 +985,7 @@ static int fimc_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- ret = devm_request_irq(dev, res->start, fimc_irq_handler,
+ ret = devm_request_irq(dev, irq, fimc_irq_handler,
0, dev_name(dev), fimc);
if (ret < 0) {
dev_err(dev, "failed to install irq (%d)\n", ret);
diff --git a/drivers/media/platform/exynos4-is/fimc-core.h b/drivers/media/platform/samsung/exynos4-is/fimc-core.h
index 7a058f3e6298..7a058f3e6298 100644
--- a/drivers/media/platform/exynos4-is/fimc-core.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.h
diff --git a/drivers/media/platform/exynos4-is/fimc-is-command.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-command.h
index 87978609ad55..87978609ad55 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-command.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-command.h
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c
index 5d9f4c1cdc5e..5d9f4c1cdc5e 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-errno.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c
diff --git a/drivers/media/platform/exynos4-is/fimc-is-errno.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h
index da36b48b8f9f..da36b48b8f9f 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-errno.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
index 83a28ef8e099..83a28ef8e099 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-i2c.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
diff --git a/drivers/media/platform/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
index a23bd20be6c8..a23bd20be6c8 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-i2c.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c
index 9c816ae3b3e5..9c816ae3b3e5 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c
diff --git a/drivers/media/platform/exynos4-is/fimc-is-param.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h
index 206904674927..206904674927 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-param.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
index 366e6393817d..366e6393817d 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
diff --git a/drivers/media/platform/exynos4-is/fimc-is-regs.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.h
index 5d8b01bc84a2..5d8b01bc84a2 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-regs.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.h
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.c
index 0e5b9fede4ae..0e5b9fede4ae 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.c
diff --git a/drivers/media/platform/exynos4-is/fimc-is-sensor.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.h
index 9aefc63889de..9aefc63889de 100644
--- a/drivers/media/platform/exynos4-is/fimc-is-sensor.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-sensor.h
diff --git a/drivers/media/platform/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
index e55e411038f4..e55e411038f4 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
diff --git a/drivers/media/platform/exynos4-is/fimc-is.h b/drivers/media/platform/samsung/exynos4-is/fimc-is.h
index 06586e455b1d..06586e455b1d 100644
--- a/drivers/media/platform/exynos4-is/fimc-is.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.h
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c
index 83688a7982f7..83688a7982f7 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp-video.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c
diff --git a/drivers/media/platform/exynos4-is/fimc-isp-video.h b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.h
index edcb3a5e3cb9..edcb3a5e3cb9 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp-video.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.h
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c
index 855235bea46d..b85986e50f46 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c
@@ -226,7 +226,7 @@ static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd,
}
}
} else {
- if (sd->entity.stream_count == 0) {
+ if (!media_entity_is_streaming(&sd->entity)) {
if (fmt->pad == FIMC_ISP_SD_PAD_SINK) {
struct v4l2_subdev_format format = *fmt;
diff --git a/drivers/media/platform/exynos4-is/fimc-isp.h b/drivers/media/platform/samsung/exynos4-is/fimc-isp.h
index 12017cd924d9..12017cd924d9 100644
--- a/drivers/media/platform/exynos4-is/fimc-isp.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.h
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.c
index 57996b4104b4..57996b4104b4 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.c
diff --git a/drivers/media/platform/exynos4-is/fimc-lite-reg.h b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.h
index c5656e902750..c5656e902750 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite-reg.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite-reg.h
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
index aaa3af0493ce..2e8f476efc5c 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
@@ -1073,7 +1073,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
mutex_lock(&fimc->lock);
if ((atomic_read(&fimc->out_path) == FIMC_IO_ISP &&
- sd->entity.stream_count > 0) ||
+ media_entity_is_streaming(&sd->entity)) ||
(atomic_read(&fimc->out_path) == FIMC_IO_DMA &&
vb2_is_busy(&fimc->vb_queue))) {
mutex_unlock(&fimc->lock);
@@ -1197,8 +1197,8 @@ static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on)
* Find sensor subdev linked to FIMC-LITE directly or through
* MIPI-CSIS. This is required for configuration where FIMC-LITE
* is used as a subdev only and feeds data internally to FIMC-IS.
- * The pipeline links are protected through entity.stream_count
- * so there is no need to take the media graph mutex here.
+ * The pipeline links are protected through entity.pipe so there is no
+ * need to take the media graph mutex here.
*/
fimc->sensor = fimc_find_remote_sensor(&sd->entity);
@@ -1454,6 +1454,7 @@ static int fimc_lite_probe(struct platform_device *pdev)
struct fimc_lite *fimc;
struct resource *res;
int ret;
+ int irq;
if (!dev->of_node)
return -ENODEV;
@@ -1485,17 +1486,15 @@ static int fimc_lite_probe(struct platform_device *pdev)
if (IS_ERR(fimc->regs))
return PTR_ERR(fimc->regs);
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (res == NULL) {
- dev_err(dev, "Failed to get IRQ resource\n");
- return -ENXIO;
- }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
ret = fimc_lite_clk_get(fimc);
if (ret)
return ret;
- ret = devm_request_irq(dev, res->start, flite_irq_handler,
+ ret = devm_request_irq(dev, irq, flite_irq_handler,
0, dev_name(dev), fimc);
if (ret) {
dev_err(dev, "Failed to install irq (%d)\n", ret);
diff --git a/drivers/media/platform/exynos4-is/fimc-lite.h b/drivers/media/platform/samsung/exynos4-is/fimc-lite.h
index ddf29e0b5b1c..ddf29e0b5b1c 100644
--- a/drivers/media/platform/exynos4-is/fimc-lite.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.h
diff --git a/drivers/media/platform/exynos4-is/fimc-m2m.c b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c
index df8e2aa454d8..df8e2aa454d8 100644
--- a/drivers/media/platform/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.c b/drivers/media/platform/samsung/exynos4-is/fimc-reg.c
index 95165a2cc7d1..95165a2cc7d1 100644
--- a/drivers/media/platform/exynos4-is/fimc-reg.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-reg.c
diff --git a/drivers/media/platform/exynos4-is/fimc-reg.h b/drivers/media/platform/samsung/exynos4-is/fimc-reg.h
index b9b33aa1f12f..b9b33aa1f12f 100644
--- a/drivers/media/platform/exynos4-is/fimc-reg.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-reg.h
diff --git a/drivers/media/platform/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c
index 544b54e428c9..544b54e428c9 100644
--- a/drivers/media/platform/exynos4-is/media-dev.c
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c
diff --git a/drivers/media/platform/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h
index 62ad5d7e035a..62ad5d7e035a 100644
--- a/drivers/media/platform/exynos4-is/media-dev.h
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
index 27a214936cb0..27a214936cb0 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
diff --git a/drivers/media/platform/exynos4-is/mipi-csis.h b/drivers/media/platform/samsung/exynos4-is/mipi-csis.h
index 193f253c7907..193f253c7907 100644
--- a/drivers/media/platform/exynos4-is/mipi-csis.h
+++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.h
diff --git a/drivers/media/platform/samsung/s3c-camif/Kconfig b/drivers/media/platform/samsung/s3c-camif/Kconfig
new file mode 100644
index 000000000000..8cb8d1ac3edc
--- /dev/null
+++ b/drivers/media/platform/samsung/s3c-camif/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_S3C_CAMIF
+ tristate "Samsung S3C24XX/S3C64XX SoC Camera Interface driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && I2C && PM
+ depends on ARCH_S3C64XX || PLAT_S3C24XX || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ This is a v4l2 driver for s3c24xx and s3c64xx SoC series camera
+ host interface (CAMIF).
+
+ To compile this driver as a module, choose M here: the module
+ will be called s3c-camif.
diff --git a/drivers/media/platform/s3c-camif/Makefile b/drivers/media/platform/samsung/s3c-camif/Makefile
index 70ee042a3dae..70ee042a3dae 100644
--- a/drivers/media/platform/s3c-camif/Makefile
+++ b/drivers/media/platform/samsung/s3c-camif/Makefile
diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
index 140854ab4dd8..140854ab4dd8 100644
--- a/drivers/media/platform/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
diff --git a/drivers/media/platform/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c
index 6e8ef86566b7..6e8ef86566b7 100644
--- a/drivers/media/platform/s3c-camif/camif-core.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c
diff --git a/drivers/media/platform/s3c-camif/camif-core.h b/drivers/media/platform/samsung/s3c-camif/camif-core.h
index f3442e251bc9..f3442e251bc9 100644
--- a/drivers/media/platform/s3c-camif/camif-core.h
+++ b/drivers/media/platform/samsung/s3c-camif/camif-core.h
diff --git a/drivers/media/platform/s3c-camif/camif-regs.c b/drivers/media/platform/samsung/s3c-camif/camif-regs.c
index e80204f5720c..e80204f5720c 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-regs.c
diff --git a/drivers/media/platform/s3c-camif/camif-regs.h b/drivers/media/platform/samsung/s3c-camif/camif-regs.h
index 052948a7b669..052948a7b669 100644
--- a/drivers/media/platform/s3c-camif/camif-regs.h
+++ b/drivers/media/platform/samsung/s3c-camif/camif-regs.h
diff --git a/drivers/media/platform/samsung/s5p-g2d/Kconfig b/drivers/media/platform/samsung/s5p-g2d/Kconfig
new file mode 100644
index 000000000000..28ab88fc2d7c
--- /dev/null
+++ b/drivers/media/platform/samsung/s5p-g2d/Kconfig
@@ -0,0 +1,11 @@
+config VIDEO_SAMSUNG_S5P_G2D
+ tristate "Samsung S5P and EXYNOS4 G2D 2d graphics accelerator driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Samsung S5P and EXYNOS4 G2D
+ 2d graphics accelerator.
+
diff --git a/drivers/media/platform/s5p-g2d/Makefile b/drivers/media/platform/samsung/s5p-g2d/Makefile
index ad2c5bf66a5f..ad2c5bf66a5f 100644
--- a/drivers/media/platform/s5p-g2d/Makefile
+++ b/drivers/media/platform/samsung/s5p-g2d/Makefile
diff --git a/drivers/media/platform/s5p-g2d/g2d-hw.c b/drivers/media/platform/samsung/s5p-g2d/g2d-hw.c
index b69d3fb12502..b69d3fb12502 100644
--- a/drivers/media/platform/s5p-g2d/g2d-hw.c
+++ b/drivers/media/platform/samsung/s5p-g2d/g2d-hw.c
diff --git a/drivers/media/platform/s5p-g2d/g2d-regs.h b/drivers/media/platform/samsung/s5p-g2d/g2d-regs.h
index b2630c6133f1..b2630c6133f1 100644
--- a/drivers/media/platform/s5p-g2d/g2d-regs.h
+++ b/drivers/media/platform/samsung/s5p-g2d/g2d-regs.h
diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/samsung/s5p-g2d/g2d.c
index fa0bb31bd2b9..dd8864779a7c 100644
--- a/drivers/media/platform/s5p-g2d/g2d.c
+++ b/drivers/media/platform/samsung/s5p-g2d/g2d.c
@@ -623,7 +623,6 @@ static int g2d_probe(struct platform_device *pdev)
{
struct g2d_dev *dev;
struct video_device *vfd;
- struct resource *res;
const struct of_device_id *of_id;
int ret = 0;
@@ -664,14 +663,11 @@ static int g2d_probe(struct platform_device *pdev)
goto put_clk_gate;
}
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to find IRQ\n");
- ret = -ENXIO;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
goto unprep_clk_gate;
- }
- dev->irq = res->start;
+ dev->irq = ret;
ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr,
0, pdev->name, dev);
diff --git a/drivers/media/platform/s5p-g2d/g2d.h b/drivers/media/platform/samsung/s5p-g2d/g2d.h
index c2309c1370da..c2309c1370da 100644
--- a/drivers/media/platform/s5p-g2d/g2d.h
+++ b/drivers/media/platform/samsung/s5p-g2d/g2d.h
diff --git a/drivers/media/platform/samsung/s5p-jpeg/Kconfig b/drivers/media/platform/samsung/s5p-jpeg/Kconfig
new file mode 100644
index 000000000000..11f6e99dec39
--- /dev/null
+++ b/drivers/media/platform/samsung/s5p-jpeg/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_SAMSUNG_S5P_JPEG
+ tristate "Samsung S5P/Exynos3250/Exynos4 JPEG codec driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This is a v4l2 driver for Samsung S5P, EXYNOS3250
+ and EXYNOS4 JPEG codec
diff --git a/drivers/media/platform/s5p-jpeg/Makefile b/drivers/media/platform/samsung/s5p-jpeg/Makefile
index 8b0f92e27e70..8b0f92e27e70 100644
--- a/drivers/media/platform/s5p-jpeg/Makefile
+++ b/drivers/media/platform/samsung/s5p-jpeg/Makefile
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
index a8d9159d5ed8..5479bc8d474d 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.c
+/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
*
* Copyright (c) 2011-2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-core.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h
index 4a5fb1b15455..5570c79f122f 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* linux/drivers/media/platform/s5p-jpeg/jpeg-core.h
+/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c
index 637a5104d948..637a5104d948 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h
index 68160befce39..15af928fad76 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos3250.h
+/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h
*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* http://www.samsung.com
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c
index 0828cfa783fe..0828cfa783fe 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h
index 3e2887526960..3e2887526960 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-exynos4.h
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c
index 491e9248286c..01b47b3df1e7 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h
+/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h
index 98ddf7097562..f068d52c66b7 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-hw-s5p.h
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-s5p.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* linux/drivers/media/platform/s5p-jpeg/jpeg-hw.h
+/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
diff --git a/drivers/media/platform/s5p-jpeg/jpeg-regs.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h
index 86f376b50581..c2298b680022 100644
--- a/drivers/media/platform/s5p-jpeg/jpeg-regs.h
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* linux/drivers/media/platform/s5p-jpeg/jpeg-regs.h
+/* linux/drivers/media/platform/samsung/s5p-jpeg/jpeg-regs.h
*
* Register definition file for Samsung JPEG codec driver
*
diff --git a/drivers/media/platform/samsung/s5p-mfc/Kconfig b/drivers/media/platform/samsung/s5p-mfc/Kconfig
new file mode 100644
index 000000000000..7ee3b0c8d98b
--- /dev/null
+++ b/drivers/media/platform/samsung/s5p-mfc/Kconfig
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SAMSUNG_S5P_MFC
+ tristate "Samsung S5P MFC Video Codec"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ MFC 5.1 and 6.x driver for V4L2
diff --git a/drivers/media/platform/s5p-mfc/Makefile b/drivers/media/platform/samsung/s5p-mfc/Makefile
index 0b324af2ab00..0b324af2ab00 100644
--- a/drivers/media/platform/s5p-mfc/Makefile
+++ b/drivers/media/platform/samsung/s5p-mfc/Makefile
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v10.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v10.h
index fadd9139b489..fadd9139b489 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v10.h
+++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v10.h
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
index fa49fe580e1a..fa49fe580e1a 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v6.h
+++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h
index 4a7adfdaa359..4a7adfdaa359 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v7.h
+++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v7.h
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h
index 162e3c7e920f..162e3c7e920f 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc-v8.h
+++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v8.h
diff --git a/drivers/media/platform/s5p-mfc/regs-mfc.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc.h
index 9171e8181c18..9171e8181c18 100644
--- a/drivers/media/platform/s5p-mfc/regs-mfc.h
+++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc.h
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
index f6732f031e96..761341934925 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
@@ -1268,7 +1268,6 @@ static int s5p_mfc_probe(struct platform_device *pdev)
{
struct s5p_mfc_dev *dev;
struct video_device *vfd;
- struct resource *res;
int ret;
pr_debug("%s++\n", __func__);
@@ -1294,12 +1293,10 @@ static int s5p_mfc_probe(struct platform_device *pdev)
if (IS_ERR(dev->regs_base))
return PTR_ERR(dev->regs_base);
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to get irq resource\n");
- return -ENOENT;
- }
- dev->irq = res->start;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ return ret;
+ dev->irq = ret;
ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq,
0, pdev->name, dev);
if (ret) {
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c
index 0e88c28f4ad3..774c573dc075 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.c
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.c
*
* Copyright (C) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h
index ed4e32a12552..945d12fdceb7 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd.h
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd.h
*
* Copyright (C) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c
index 1ea4eda9c8e0..327e54e70611 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.c
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.c
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h
index 917854bffe9f..6eafa514aebc 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v5.h
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v5.h
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c
index 1f42130cc865..f8588e52dfc8 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.c
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.c
*
* Copyright (c) 2012 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h
index c19884ea2bfc..9dc44460cc38 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_cmd_v6.h
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_cmd_v6.h
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
index c3ef4f6a42d2..5304f42c8c72 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
@@ -732,6 +732,7 @@ struct s5p_mfc_fmt {
enum s5p_mfc_fmt_type type;
u32 num_planes;
u32 versions;
+ u32 flags;
};
/*
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c
index da138c314963..72d70984e99a 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.c
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.c
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h
index 7f32ef8a6b61..653ba5f3d048 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_ctrl.h
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_ctrl.h
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h
index 752bbe4fe48e..bba5dad6dbff 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * drivers/media/platform/s5p-mfc/s5p_mfc_debug.h
+ * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_debug.h
*
* Header file for Samsung MFC (Multi Function Codec - FIMV) driver
* This file contains debug macros
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c
index c1d3bda8385b..4b89df8bfd18 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
@@ -62,6 +62,8 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION |
+ V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM,
},
{
.fourcc = V4L2_PIX_FMT_H264_MVC,
@@ -69,6 +71,8 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V6PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION |
+ V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM,
},
{
.fourcc = V4L2_PIX_FMT_H263,
@@ -76,6 +80,7 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION,
},
{
.fourcc = V4L2_PIX_FMT_MPEG1,
@@ -83,6 +88,8 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION |
+ V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM,
},
{
.fourcc = V4L2_PIX_FMT_MPEG2,
@@ -90,6 +97,8 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION |
+ V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM,
},
{
.fourcc = V4L2_PIX_FMT_MPEG4,
@@ -97,6 +106,8 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION |
+ V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM,
},
{
.fourcc = V4L2_PIX_FMT_XVID,
@@ -104,6 +115,7 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION,
},
{
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
@@ -111,6 +123,7 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION,
},
{
.fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
@@ -118,6 +131,7 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V5PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION,
},
{
.fourcc = V4L2_PIX_FMT_VP8,
@@ -125,6 +139,7 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V6PLUS_BITS,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION,
},
{
.fourcc = V4L2_PIX_FMT_HEVC,
@@ -132,6 +147,8 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V10_BIT,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION |
+ V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM,
},
{
.fourcc = V4L2_PIX_FMT_VP9,
@@ -139,6 +156,7 @@ static struct s5p_mfc_fmt formats[] = {
.type = MFC_FMT_DEC,
.num_planes = 1,
.versions = MFC_V10_BIT,
+ .flags = V4L2_FMT_FLAG_DYN_RESOLUTION,
},
};
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h
index 0e9a0e3bbbe7..0c52ab46cff7 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_dec.h
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.h
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c
index 1fad99edb091..a8877d805b29 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h
index cacd1ca43e19..3f1b1a037a4f 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_enc.h
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.h
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.c
index 0a38f6d70ee9..0a38f6d70ee9 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.c
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.h
index d32860db17d2..d32860db17d2 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_intr.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_intr.h
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_iommu.h
index 1a32266b7ddc..1a32266b7ddc 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_iommu.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_iommu.h
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c
index bb65671eea91..673962301173 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * drivers/media/platform/s5p-mfc/s5p_mfc_opr.c
+ * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.c
*
* Samsung MFC (Multi Function Codec - FIMV) driver
* This file contains hw related functions.
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h
index 1c5d2d4c0543..b9831275f3ab 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * drivers/media/platform/s5p-mfc/s5p_mfc_opr.h
+ * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr.h
*
* Header file for Samsung MFC (Multi Function Codec - FIMV) driver
* Contains declarations of hw related functions.
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c
index 28a06dc343fd..28a06dc343fd 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.c
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.h
index b53d376ead60..b53d376ead60 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v5.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v5.h
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
index a1453053e31a..8227004f6746 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c
+ * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
*
* Samsung MFC (Multi Function Codec - FIMV) driver
* This file contains hw related functions.
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h
index 8ca514bf5e37..e4dd03c5454c 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.h
+ * drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.h
*
* Header file for Samsung MFC (Multi Function Codec - FIMV) driver
* Contains declarations of hw related functions.
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c
index 88b7d33c9197..72a901e99450 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_pm.c
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.c
*
* Copyright (c) 2010 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h
index 3d26443189a2..4159d2364e87 100644
--- a/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
- * linux/drivers/media/platform/s5p-mfc/s5p_mfc_pm.h
+ * linux/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_pm.h
*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com/
diff --git a/drivers/media/platform/st/Kconfig b/drivers/media/platform/st/Kconfig
new file mode 100644
index 000000000000..b29c258ea5fc
--- /dev/null
+++ b/drivers/media/platform/st/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "STMicroelectronics media platform drivers"
+
+source "drivers/media/platform/st/sti/Kconfig"
+source "drivers/media/platform/st/stm32/Kconfig"
diff --git a/drivers/media/platform/st/Makefile b/drivers/media/platform/st/Makefile
new file mode 100644
index 000000000000..a1f75b2a8225
--- /dev/null
+++ b/drivers/media/platform/st/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += sti/bdisp/
+obj-y += sti/c8sectpfe/
+obj-y += sti/delta/
+obj-y += sti/hva/
+obj-y += stm32/
diff --git a/drivers/media/platform/st/sti/Kconfig b/drivers/media/platform/st/sti/Kconfig
new file mode 100644
index 000000000000..60068e8b47b8
--- /dev/null
+++ b/drivers/media/platform/st/sti/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+source "drivers/media/platform/st/sti/bdisp/Kconfig"
+source "drivers/media/platform/st/sti/c8sectpfe/Kconfig"
+source "drivers/media/platform/st/sti/delta/Kconfig"
+source "drivers/media/platform/st/sti/hva/Kconfig"
diff --git a/drivers/media/platform/st/sti/Makefile b/drivers/media/platform/st/sti/Makefile
new file mode 100644
index 000000000000..f9ce8169b040
--- /dev/null
+++ b/drivers/media/platform/st/sti/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += bdisp/
+obj-y += c8sectpfe/
+obj-y += delta/
+obj-y += hva/
+obj-y += stm32/
diff --git a/drivers/media/platform/st/sti/bdisp/Kconfig b/drivers/media/platform/st/sti/bdisp/Kconfig
new file mode 100644
index 000000000000..496f8aedf0a4
--- /dev/null
+++ b/drivers/media/platform/st/sti/bdisp/Kconfig
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_STI_BDISP
+ tristate "STMicroelectronics BDISP 2D blitter driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_STI || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This v4l2 mem2mem driver is a 2D blitter for STMicroelectronics SoC.
diff --git a/drivers/media/platform/sti/bdisp/Makefile b/drivers/media/platform/st/sti/bdisp/Makefile
index 39ade0a34723..39ade0a34723 100644
--- a/drivers/media/platform/sti/bdisp/Makefile
+++ b/drivers/media/platform/st/sti/bdisp/Makefile
diff --git a/drivers/media/platform/sti/bdisp/bdisp-debug.c b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
index a27f638df11c..a27f638df11c 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
diff --git a/drivers/media/platform/sti/bdisp/bdisp-filter.h b/drivers/media/platform/st/sti/bdisp/bdisp-filter.h
index 9e1a95fd27ed..9e1a95fd27ed 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-filter.h
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-filter.h
diff --git a/drivers/media/platform/sti/bdisp/bdisp-hw.c b/drivers/media/platform/st/sti/bdisp/bdisp-hw.c
index a74e9fd65238..a74e9fd65238 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-hw.c
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-hw.c
diff --git a/drivers/media/platform/sti/bdisp/bdisp-reg.h b/drivers/media/platform/st/sti/bdisp/bdisp-reg.h
index b07ecc903707..b07ecc903707 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-reg.h
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-reg.h
diff --git a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
index 01ce7b711774..5aa79d9277c8 100644
--- a/drivers/media/platform/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
@@ -1284,7 +1284,6 @@ static int bdisp_remove(struct platform_device *pdev)
static int bdisp_probe(struct platform_device *pdev)
{
struct bdisp_dev *bdisp;
- struct resource *res;
struct device *dev = &pdev->dev;
int ret;
@@ -1335,14 +1334,11 @@ static int bdisp_probe(struct platform_device *pdev)
goto err_wq;
}
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "failed to get IRQ resource\n");
- ret = -EINVAL;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
goto err_clk;
- }
- ret = devm_request_threaded_irq(dev, res->start, bdisp_irq_handler,
+ ret = devm_request_threaded_irq(dev, ret, bdisp_irq_handler,
bdisp_irq_thread, IRQF_ONESHOT,
pdev->name, bdisp);
if (ret) {
diff --git a/drivers/media/platform/sti/bdisp/bdisp.h b/drivers/media/platform/st/sti/bdisp/bdisp.h
index 3fb009d24791..3fb009d24791 100644
--- a/drivers/media/platform/sti/bdisp/bdisp.h
+++ b/drivers/media/platform/st/sti/bdisp/bdisp.h
diff --git a/drivers/media/platform/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig
index 369509e03071..702b910509c9 100644
--- a/drivers/media/platform/sti/c8sectpfe/Kconfig
+++ b/drivers/media/platform/st/sti/c8sectpfe/Kconfig
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
config DVB_C8SECTPFE
tristate "STMicroelectronics C8SECTPFE DVB support"
+ depends on DVB_PLATFORM_DRIVERS
depends on PINCTRL && DVB_CORE && I2C
depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST
select FW_LOADER
diff --git a/drivers/media/platform/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile
index aedfc725cc19..aedfc725cc19 100644
--- a/drivers/media/platform/sti/c8sectpfe/Makefile
+++ b/drivers/media/platform/st/sti/c8sectpfe/Makefile
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c
index 5df67da25525..5df67da25525 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.c
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h
index 5ab7ca448cf9..5ab7ca448cf9 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-common.h
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-common.h
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
index 7bb1384e4bad..7bb1384e4bad 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h
index c9d6021904cd..c9d6021904cd 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-core.h
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c
index 301fa10f419b..301fa10f419b 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.c
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
index d2c35fb32d7e..d2c35fb32d7e 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.h
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c
index feb48cb546d7..feb48cb546d7 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.c
diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h
index 3d87a9ae8702..3d87a9ae8702 100644
--- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-dvb.h
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-dvb.h
diff --git a/drivers/media/platform/st/sti/delta/Kconfig b/drivers/media/platform/st/sti/delta/Kconfig
new file mode 100644
index 000000000000..efa936b1cc8a
--- /dev/null
+++ b/drivers/media/platform/st/sti/delta/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_STI_DELTA
+ tristate "STMicroelectronics DELTA multi-format video decoder V4L2 driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_STI || COMPILE_TEST
+ help
+ This V4L2 driver enables DELTA multi-format video decoder
+ of STMicroelectronics STiH4xx SoC series allowing hardware
+ decoding of various compressed video bitstream format in
+ raw uncompressed format.
+
+ Use this option to see the decoders available for such
+ hardware.
+
+ Please notice that the driver will only be built if
+ at least one of the DELTA decoder below is selected.
+
+config VIDEO_STI_DELTA_MJPEG
+ bool "STMicroelectronics DELTA MJPEG support"
+ default y
+ depends on VIDEO_STI_DELTA
+ help
+ Enables DELTA MJPEG hardware support.
+
+ To compile this driver as a module, choose M here:
+ the module will be called st-delta.
+
+config VIDEO_STI_DELTA_DRIVER
+ tristate
+ depends on VIDEO_STI_DELTA
+ depends on VIDEO_STI_DELTA_MJPEG
+ default VIDEO_STI_DELTA_MJPEG
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select RPMSG
diff --git a/drivers/media/platform/sti/delta/Makefile b/drivers/media/platform/st/sti/delta/Makefile
index 32412fa4c632..32412fa4c632 100644
--- a/drivers/media/platform/sti/delta/Makefile
+++ b/drivers/media/platform/st/sti/delta/Makefile
diff --git a/drivers/media/platform/sti/delta/delta-cfg.h b/drivers/media/platform/st/sti/delta/delta-cfg.h
index f47c6e6ff083..f47c6e6ff083 100644
--- a/drivers/media/platform/sti/delta/delta-cfg.h
+++ b/drivers/media/platform/st/sti/delta/delta-cfg.h
diff --git a/drivers/media/platform/sti/delta/delta-debug.c b/drivers/media/platform/st/sti/delta/delta-debug.c
index 4b2eb6b63aa2..4b2eb6b63aa2 100644
--- a/drivers/media/platform/sti/delta/delta-debug.c
+++ b/drivers/media/platform/st/sti/delta/delta-debug.c
diff --git a/drivers/media/platform/sti/delta/delta-debug.h b/drivers/media/platform/st/sti/delta/delta-debug.h
index fa90252623e1..fa90252623e1 100644
--- a/drivers/media/platform/sti/delta/delta-debug.h
+++ b/drivers/media/platform/st/sti/delta/delta-debug.h
diff --git a/drivers/media/platform/sti/delta/delta-ipc.c b/drivers/media/platform/st/sti/delta/delta-ipc.c
index 21d3e08e259a..21d3e08e259a 100644
--- a/drivers/media/platform/sti/delta/delta-ipc.c
+++ b/drivers/media/platform/st/sti/delta/delta-ipc.c
diff --git a/drivers/media/platform/sti/delta/delta-ipc.h b/drivers/media/platform/st/sti/delta/delta-ipc.h
index 9fba6b5d169a..9fba6b5d169a 100644
--- a/drivers/media/platform/sti/delta/delta-ipc.h
+++ b/drivers/media/platform/st/sti/delta/delta-ipc.h
diff --git a/drivers/media/platform/sti/delta/delta-mem.c b/drivers/media/platform/st/sti/delta/delta-mem.c
index aeccd50583da..aeccd50583da 100644
--- a/drivers/media/platform/sti/delta/delta-mem.c
+++ b/drivers/media/platform/st/sti/delta/delta-mem.c
diff --git a/drivers/media/platform/sti/delta/delta-mem.h b/drivers/media/platform/st/sti/delta/delta-mem.h
index ff7d02f00b28..ff7d02f00b28 100644
--- a/drivers/media/platform/sti/delta/delta-mem.h
+++ b/drivers/media/platform/st/sti/delta/delta-mem.h
diff --git a/drivers/media/platform/sti/delta/delta-mjpeg-dec.c b/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c
index 0533d4a083d2..0533d4a083d2 100644
--- a/drivers/media/platform/sti/delta/delta-mjpeg-dec.c
+++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-dec.c
diff --git a/drivers/media/platform/sti/delta/delta-mjpeg-fw.h b/drivers/media/platform/st/sti/delta/delta-mjpeg-fw.h
index 5a9404f4d055..5a9404f4d055 100644
--- a/drivers/media/platform/sti/delta/delta-mjpeg-fw.h
+++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-fw.h
diff --git a/drivers/media/platform/sti/delta/delta-mjpeg-hdr.c b/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c
index 90e5b2f72c82..90e5b2f72c82 100644
--- a/drivers/media/platform/sti/delta/delta-mjpeg-hdr.c
+++ b/drivers/media/platform/st/sti/delta/delta-mjpeg-hdr.c
diff --git a/drivers/media/platform/sti/delta/delta-mjpeg.h b/drivers/media/platform/st/sti/delta/delta-mjpeg.h
index 43f7a88b6e59..43f7a88b6e59 100644
--- a/drivers/media/platform/sti/delta/delta-mjpeg.h
+++ b/drivers/media/platform/st/sti/delta/delta-mjpeg.h
diff --git a/drivers/media/platform/sti/delta/delta-v4l2.c b/drivers/media/platform/st/sti/delta/delta-v4l2.c
index c887a31ebb54..c887a31ebb54 100644
--- a/drivers/media/platform/sti/delta/delta-v4l2.c
+++ b/drivers/media/platform/st/sti/delta/delta-v4l2.c
diff --git a/drivers/media/platform/sti/delta/delta.h b/drivers/media/platform/st/sti/delta/delta.h
index 914556030e70..914556030e70 100644
--- a/drivers/media/platform/sti/delta/delta.h
+++ b/drivers/media/platform/st/sti/delta/delta.h
diff --git a/drivers/media/platform/st/sti/hva/Kconfig b/drivers/media/platform/st/sti/hva/Kconfig
new file mode 100644
index 000000000000..46d6f82f648e
--- /dev/null
+++ b/drivers/media/platform/st/sti/hva/Kconfig
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_STI_HVA
+ tristate "STMicroelectronics HVA multi-format video encoder V4L2 driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_STI || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ This V4L2 driver enables HVA (Hardware Video Accelerator) multi-format
+ video encoder of STMicroelectronics SoC, allowing hardware encoding of
+ raw uncompressed formats in various compressed video bitstreams format.
+
+ To compile this driver as a module, choose M here:
+ the module will be called st-hva.
+
+config VIDEO_STI_HVA_DEBUGFS
+ bool "Export STMicroelectronics HVA internals in debugfs"
+ depends on VIDEO_STI_HVA
+ depends on DEBUG_FS
+ help
+ Select this to see information about the internal state and the last
+ operation of STMicroelectronics HVA multi-format video encoder in
+ debugfs.
+
+ Choose N unless you know you need this.
diff --git a/drivers/media/platform/sti/hva/Makefile b/drivers/media/platform/st/sti/hva/Makefile
index b5a5478bdd01..b5a5478bdd01 100644
--- a/drivers/media/platform/sti/hva/Makefile
+++ b/drivers/media/platform/st/sti/hva/Makefile
diff --git a/drivers/media/platform/sti/hva/hva-debugfs.c b/drivers/media/platform/st/sti/hva/hva-debugfs.c
index a86a07b6fbc7..a86a07b6fbc7 100644
--- a/drivers/media/platform/sti/hva/hva-debugfs.c
+++ b/drivers/media/platform/st/sti/hva/hva-debugfs.c
diff --git a/drivers/media/platform/sti/hva/hva-h264.c b/drivers/media/platform/st/sti/hva/hva-h264.c
index 98cb00d2d868..98cb00d2d868 100644
--- a/drivers/media/platform/sti/hva/hva-h264.c
+++ b/drivers/media/platform/st/sti/hva/hva-h264.c
diff --git a/drivers/media/platform/sti/hva/hva-hw.c b/drivers/media/platform/st/sti/hva/hva-hw.c
index fe4ea2e7f37e..fe4ea2e7f37e 100644
--- a/drivers/media/platform/sti/hva/hva-hw.c
+++ b/drivers/media/platform/st/sti/hva/hva-hw.c
diff --git a/drivers/media/platform/sti/hva/hva-hw.h b/drivers/media/platform/st/sti/hva/hva-hw.h
index b298990264d5..b298990264d5 100644
--- a/drivers/media/platform/sti/hva/hva-hw.h
+++ b/drivers/media/platform/st/sti/hva/hva-hw.h
diff --git a/drivers/media/platform/sti/hva/hva-mem.c b/drivers/media/platform/st/sti/hva/hva-mem.c
index 68047b60b66c..68047b60b66c 100644
--- a/drivers/media/platform/sti/hva/hva-mem.c
+++ b/drivers/media/platform/st/sti/hva/hva-mem.c
diff --git a/drivers/media/platform/sti/hva/hva-mem.h b/drivers/media/platform/st/sti/hva/hva-mem.h
index fec549dff2b3..fec549dff2b3 100644
--- a/drivers/media/platform/sti/hva/hva-mem.h
+++ b/drivers/media/platform/st/sti/hva/hva-mem.h
diff --git a/drivers/media/platform/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c
index bb34d6997d99..bb34d6997d99 100644
--- a/drivers/media/platform/sti/hva/hva-v4l2.c
+++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c
diff --git a/drivers/media/platform/sti/hva/hva.h b/drivers/media/platform/st/sti/hva/hva.h
index ba6b893416ec..ba6b893416ec 100644
--- a/drivers/media/platform/sti/hva/hva.h
+++ b/drivers/media/platform/st/sti/hva/hva.h
diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig
new file mode 100644
index 000000000000..b22dd4753496
--- /dev/null
+++ b/drivers/media/platform/st/stm32/Kconfig
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+# V4L drivers
+config VIDEO_STM32_DCMI
+ tristate "STM32 Digital Camera Memory Interface (DCMI) support"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_STM32 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This module makes the STM32 Digital Camera Memory Interface (DCMI)
+ available as a v4l2 device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called stm32-dcmi.
+
+# Mem2mem drivers
+config VIDEO_STM32_DMA2D
+ tristate "STM32 Chrom-Art Accelerator (DMA2D)"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_STM32 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Enables DMA2D hardware support on stm32.
+
+ The STM32 DMA2D is a memory-to-memory engine for pixel conversion
+ and specialized DMA dedicated to image manipulation.
diff --git a/drivers/media/platform/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile
index 896ef98a73ab..896ef98a73ab 100644
--- a/drivers/media/platform/stm32/Makefile
+++ b/drivers/media/platform/st/stm32/Makefile
diff --git a/drivers/media/platform/stm32/dma2d/dma2d-hw.c b/drivers/media/platform/st/stm32/dma2d/dma2d-hw.c
index ea4cc84d8a39..ea4cc84d8a39 100644
--- a/drivers/media/platform/stm32/dma2d/dma2d-hw.c
+++ b/drivers/media/platform/st/stm32/dma2d/dma2d-hw.c
diff --git a/drivers/media/platform/stm32/dma2d/dma2d-regs.h b/drivers/media/platform/st/stm32/dma2d/dma2d-regs.h
index 6444592d415b..6444592d415b 100644
--- a/drivers/media/platform/stm32/dma2d/dma2d-regs.h
+++ b/drivers/media/platform/st/stm32/dma2d/dma2d-regs.h
diff --git a/drivers/media/platform/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c
index 17af90d86898..9706aa41b5d2 100644
--- a/drivers/media/platform/stm32/dma2d/dma2d.c
+++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c
@@ -633,14 +633,11 @@ static int dma2d_probe(struct platform_device *pdev)
goto put_clk_gate;
}
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to find IRQ\n");
- ret = -ENXIO;
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
goto unprep_clk_gate;
- }
- dev->irq = res->start;
+ dev->irq = ret;
ret = devm_request_irq(&pdev->dev, dev->irq, dma2d_isr,
0, pdev->name, dev);
diff --git a/drivers/media/platform/stm32/dma2d/dma2d.h b/drivers/media/platform/st/stm32/dma2d/dma2d.h
index 3f03a7ca9ee3..3f03a7ca9ee3 100644
--- a/drivers/media/platform/stm32/dma2d/dma2d.h
+++ b/drivers/media/platform/st/stm32/dma2d/dma2d.h
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index e1b17c05229c..c4c65d852525 100644
--- a/drivers/media/platform/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -113,7 +113,7 @@ struct dcmi_framesize {
struct dcmi_buf {
struct vb2_v4l2_buffer vb;
bool prepared;
- dma_addr_t paddr;
+ struct sg_table sgt;
size_t size;
struct list_head list;
};
@@ -150,13 +150,14 @@ struct stm32_dcmi {
struct mutex lock;
struct vb2_queue queue;
- struct v4l2_fwnode_bus_parallel bus;
+ struct v4l2_mbus_config_parallel bus;
enum v4l2_mbus_type bus_type;
struct completion complete;
struct clk *mclk;
enum state state;
struct dma_chan *dma_chan;
dma_cookie_t dma_cookie;
+ u32 dma_max_burst;
u32 misr;
int errors_count;
int overrun_count;
@@ -326,13 +327,11 @@ static int dcmi_start_dma(struct stm32_dcmi *dcmi,
mutex_lock(&dcmi->dma_lock);
/* Prepare a DMA transaction */
- desc = dmaengine_prep_slave_single(dcmi->dma_chan, buf->paddr,
- buf->size,
- DMA_DEV_TO_MEM,
- DMA_PREP_INTERRUPT);
+ desc = dmaengine_prep_slave_sg(dcmi->dma_chan, buf->sgt.sgl, buf->sgt.nents,
+ DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
if (!desc) {
- dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_single failed for buffer phy=%pad size=%zu\n",
- __func__, &buf->paddr, buf->size);
+ dev_err(dcmi->dev, "%s: DMA dmaengine_prep_slave_sg failed\n", __func__);
mutex_unlock(&dcmi->dma_lock);
return -EINVAL;
}
@@ -524,6 +523,10 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmi_buf *buf = container_of(vbuf, struct dcmi_buf, vb);
unsigned long size;
+ unsigned int num_sgs = 1;
+ dma_addr_t dma_buf;
+ struct scatterlist *sg;
+ int i, ret;
size = dcmi->fmt.fmt.pix.sizeimage;
@@ -537,15 +540,33 @@ static int dcmi_buf_prepare(struct vb2_buffer *vb)
if (!buf->prepared) {
/* Get memory addresses */
- buf->paddr =
- vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
- buf->prepared = true;
+ if (buf->size > dcmi->dma_max_burst)
+ num_sgs = DIV_ROUND_UP(buf->size, dcmi->dma_max_burst);
- vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
+ ret = sg_alloc_table(&buf->sgt, num_sgs, GFP_ATOMIC);
+ if (ret) {
+ dev_err(dcmi->dev, "sg table alloc failed\n");
+ return ret;
+ }
+
+ dma_buf = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
dev_dbg(dcmi->dev, "buffer[%d] phy=%pad size=%zu\n",
- vb->index, &buf->paddr, buf->size);
+ vb->index, &dma_buf, buf->size);
+
+ for_each_sg(buf->sgt.sgl, sg, num_sgs, i) {
+ size_t bytes = min_t(size_t, size, dcmi->dma_max_burst);
+
+ sg_dma_address(sg) = dma_buf;
+ sg_dma_len(sg) = bytes;
+ dma_buf += bytes;
+ size -= bytes;
+ }
+
+ buf->prepared = true;
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
}
return 0;
@@ -1866,6 +1887,7 @@ static int dcmi_probe(struct platform_device *pdev)
struct stm32_dcmi *dcmi;
struct vb2_queue *q;
struct dma_chan *chan;
+ struct dma_slave_caps caps;
struct clk *mclk;
int irq;
int ret = 0;
@@ -1953,6 +1975,11 @@ static int dcmi_probe(struct platform_device *pdev)
return ret;
}
+ dcmi->dma_max_burst = UINT_MAX;
+ ret = dma_get_slave_caps(chan, &caps);
+ if (!ret && caps.max_sg_burst)
+ dcmi->dma_max_burst = caps.max_sg_burst * DMA_SLAVE_BUSWIDTH_4_BYTES;
+
spin_lock_init(&dcmi->irqlock);
mutex_init(&dcmi->lock);
mutex_init(&dcmi->dma_lock);
diff --git a/drivers/media/platform/sunxi/Kconfig b/drivers/media/platform/sunxi/Kconfig
index 7151cc249afa..46b7b9bf989c 100644
--- a/drivers/media/platform/sunxi/Kconfig
+++ b/drivers/media/platform/sunxi/Kconfig
@@ -1,4 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
+comment "Sunxi media platform drivers"
+
source "drivers/media/platform/sunxi/sun4i-csi/Kconfig"
source "drivers/media/platform/sunxi/sun6i-csi/Kconfig"
+source "drivers/media/platform/sunxi/sun8i-di/Kconfig"
+source "drivers/media/platform/sunxi/sun8i-rotate/Kconfig"
diff --git a/drivers/media/platform/sunxi/sun4i-csi/Kconfig b/drivers/media/platform/sunxi/sun4i-csi/Kconfig
index 903c6152f6e8..7960e6836f41 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/Kconfig
+++ b/drivers/media/platform/sunxi/sun4i-csi/Kconfig
@@ -2,7 +2,8 @@
config VIDEO_SUN4I_CSI
tristate "Allwinner A10 CMOS Sensor Interface Support"
- depends on VIDEO_V4L2 && COMMON_CLK && HAS_DMA
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && COMMON_CLK && HAS_DMA
depends on ARCH_SUNXI || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
index a5f61ee0ec4d..8eeed87bfb13 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.h
@@ -124,7 +124,7 @@ struct sun4i_csi {
dma_addr_t paddr;
} scratch;
- struct v4l2_fwnode_bus_parallel bus;
+ struct v4l2_mbus_config_parallel bus;
/* Main Device */
struct v4l2_device v4l;
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
index 2c39cd7f2862..0912a1b6d525 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
@@ -226,7 +226,7 @@ static void return_all_buffers(struct sun4i_csi *csi,
static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct sun4i_csi *csi = vb2_get_drv_priv(vq);
- struct v4l2_fwnode_bus_parallel *bus = &csi->bus;
+ struct v4l2_mbus_config_parallel *bus = &csi->bus;
const struct sun4i_csi_format *csi_fmt;
unsigned long href_pol, pclk_pol, vref_pol;
unsigned long flags;
diff --git a/drivers/media/platform/sunxi/sun6i-csi/Kconfig b/drivers/media/platform/sunxi/sun6i-csi/Kconfig
index 586e3fb3a80d..0345901617d4 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/Kconfig
+++ b/drivers/media/platform/sunxi/sun6i-csi/Kconfig
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_SUN6I_CSI
tristate "Allwinner V3s Camera Sensor Interface driver"
- depends on VIDEO_V4L2 && COMMON_CLK && HAS_DMA
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && COMMON_CLK && HAS_DMA
depends on ARCH_SUNXI || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
index 607a8d39fbe2..682c26536034 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_video.c
@@ -368,7 +368,11 @@ static int sun6i_video_try_fmt(struct sun6i_video *video,
if (pixfmt->field == V4L2_FIELD_ANY)
pixfmt->field = V4L2_FIELD_NONE;
- pixfmt->colorspace = V4L2_COLORSPACE_RAW;
+ if (pixfmt->pixelformat == V4L2_PIX_FMT_JPEG)
+ pixfmt->colorspace = V4L2_COLORSPACE_JPEG;
+ else
+ pixfmt->colorspace = V4L2_COLORSPACE_SRGB;
+
pixfmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
pixfmt->quantization = V4L2_QUANTIZATION_DEFAULT;
pixfmt->xfer_func = V4L2_XFER_FUNC_DEFAULT;
diff --git a/drivers/media/platform/sunxi/sun8i-di/Kconfig b/drivers/media/platform/sunxi/sun8i-di/Kconfig
new file mode 100644
index 000000000000..ff71e06ee2df
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-di/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_SUN8I_DEINTERLACE
+ tristate "Allwinner Deinterlace driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ depends on PM
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Support for the Allwinner deinterlace unit with scaling
+ capability found on some SoCs, like H3.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/Kconfig b/drivers/media/platform/sunxi/sun8i-rotate/Kconfig
new file mode 100644
index 000000000000..cfba29072d75
--- /dev/null
+++ b/drivers/media/platform/sunxi/sun8i-rotate/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_SUN8I_ROTATE
+ tristate "Allwinner DE2 rotation driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ depends on PM
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ Support for the Allwinner DE2 rotation unit.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig
new file mode 100644
index 000000000000..e1ab56c3be1f
--- /dev/null
+++ b/drivers/media/platform/ti/Kconfig
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Texas Instruments drivers"
+
+# TI VIDEO PORT Helper Modules
+# These will be selected by VPE and VIP
+config VIDEO_TI_VPDMA
+ tristate
+
+config VIDEO_TI_SC
+ tristate
+
+config VIDEO_TI_CSC
+ tristate
+
+# V4L drivers
+
+config VIDEO_TI_CAL
+ tristate "TI CAL (Camera Adaptation Layer) driver"
+ depends on VIDEO_DEV
+ depends on V4L_PLATFORM_DRIVERS
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ depends on SOC_DRA7XX || ARCH_K3 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ help
+ Support for the TI CAL (Camera Adaptation Layer) block
+ found on DRA72X SoC.
+ In TI Technical Reference Manual this module is referred as
+ Camera Interface Subsystem (CAMSS).
+
+config VIDEO_TI_CAL_MC
+ bool "Media Controller centric mode by default"
+ depends on VIDEO_TI_CAL
+ default n
+ help
+ Enables Media Controller centric mode by default.
+
+ If set, CAL driver will start in Media Controller mode by
+ default. Note that this behavior can be overridden via
+ module parameter 'mc_api'.
+
+# Mem2mem drivers
+
+config VIDEO_TI_VPE
+ tristate "TI VPE (Video Processing Engine) driver"
+ depends on V4L_MEM2MEM_DRIVERS
+ depends on VIDEO_DEV
+ depends on SOC_DRA7XX || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ select VIDEO_TI_VPDMA
+ select VIDEO_TI_SC
+ select VIDEO_TI_CSC
+ help
+ Support for the TI VPE(Video Processing Engine) block
+ found on DRA7XX SoC.
+
+config VIDEO_TI_VPE_DEBUG
+ bool "VPE debug messages"
+ depends on VIDEO_TI_VPE
+ help
+ Enable debug messages on VPE driver.
+
+source "drivers/media/platform/ti/am437x/Kconfig"
+source "drivers/media/platform/ti/davinci/Kconfig"
+source "drivers/media/platform/ti/omap/Kconfig"
+source "drivers/media/platform/ti/omap3isp/Kconfig"
diff --git a/drivers/media/platform/ti/Makefile b/drivers/media/platform/ti/Makefile
new file mode 100644
index 000000000000..98c5fe5c40d6
--- /dev/null
+++ b/drivers/media/platform/ti/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += am437x/
+obj-y += cal/
+obj-y += vpe/
+obj-y += davinci/
+obj-y += omap/
+obj-y += omap3isp/
diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/ti/am437x/Kconfig
index 9ef898f512de..2e24fff7e625 100644
--- a/drivers/media/platform/am437x/Kconfig
+++ b/drivers/media/platform/ti/am437x/Kconfig
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_AM437X_VPFE
tristate "TI AM437x VPFE video capture driver"
- depends on VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
depends on SOC_AM43XX || COMPILE_TEST
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
diff --git a/drivers/media/platform/am437x/Makefile b/drivers/media/platform/ti/am437x/Makefile
index 541043487268..541043487268 100644
--- a/drivers/media/platform/am437x/Makefile
+++ b/drivers/media/platform/ti/am437x/Makefile
diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c
index 2dfae9bc0bba..2dfae9bc0bba 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c
diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/ti/am437x/am437x-vpfe.h
index 05ee37db0273..05ee37db0273 100644
--- a/drivers/media/platform/am437x/am437x-vpfe.h
+++ b/drivers/media/platform/ti/am437x/am437x-vpfe.h
diff --git a/drivers/media/platform/am437x/am437x-vpfe_regs.h b/drivers/media/platform/ti/am437x/am437x-vpfe_regs.h
index 63ecdca3b908..63ecdca3b908 100644
--- a/drivers/media/platform/am437x/am437x-vpfe_regs.h
+++ b/drivers/media/platform/ti/am437x/am437x-vpfe_regs.h
diff --git a/drivers/media/platform/ti/cal/Makefile b/drivers/media/platform/ti/cal/Makefile
new file mode 100644
index 000000000000..45ac35585f0b
--- /dev/null
+++ b/drivers/media/platform/ti/cal/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o
+ti-cal-y := cal.o cal-camerarx.o cal-video.o
diff --git a/drivers/media/platform/ti-vpe/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c
index 4bf7a8c2e711..6b43a1525b45 100644
--- a/drivers/media/platform/ti-vpe/cal-camerarx.c
+++ b/drivers/media/platform/ti/cal/cal-camerarx.c
@@ -47,7 +47,7 @@ static inline void camerarx_write(struct cal_camerarx *phy, u32 offset, u32 val)
static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy)
{
- struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2;
+ 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;
u32 bpp;
@@ -76,7 +76,7 @@ static void cal_camerarx_lane_config(struct cal_camerarx *phy)
u32 val = cal_read(phy->cal, CAL_CSI2_COMPLEXIO_CFG(phy->instance));
u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK;
u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK;
- struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 =
+ struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 =
&phy->endpoint.bus.mipi_csi2;
int lane;
@@ -518,7 +518,7 @@ static int cal_camerarx_regmap_init(struct cal_dev *cal,
static int cal_camerarx_parse_dt(struct cal_camerarx *phy)
{
struct v4l2_fwnode_endpoint *endpoint = &phy->endpoint;
- char data_lanes[V4L2_FWNODE_CSI2_MAX_DATA_LANES * 2];
+ char data_lanes[V4L2_MBUS_CSI2_MAX_DATA_LANES * 2];
struct device_node *ep_node;
unsigned int i;
int ret;
diff --git a/drivers/media/platform/ti-vpe/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c
index 7799da1cc261..3e936a2ca36c 100644
--- a/drivers/media/platform/ti-vpe/cal-video.c
+++ b/drivers/media/platform/ti/cal/cal-video.c
@@ -823,6 +823,9 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
/* Enumerate sub device formats and enable all matching local formats */
ctx->active_fmt = devm_kcalloc(ctx->cal->dev, cal_num_formats,
sizeof(*ctx->active_fmt), GFP_KERNEL);
+ if (!ctx->active_fmt)
+ return -ENOMEM;
+
ctx->num_active_fmt = 0;
for (j = 0, i = 0; ; ++j) {
diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti/cal/cal.c
index 4a4a6c5983f7..4a4a6c5983f7 100644
--- a/drivers/media/platform/ti-vpe/cal.c
+++ b/drivers/media/platform/ti/cal/cal.c
diff --git a/drivers/media/platform/ti-vpe/cal.h b/drivers/media/platform/ti/cal/cal.h
index 527e22d022f3..527e22d022f3 100644
--- a/drivers/media/platform/ti-vpe/cal.h
+++ b/drivers/media/platform/ti/cal/cal.h
diff --git a/drivers/media/platform/ti-vpe/cal_regs.h b/drivers/media/platform/ti/cal/cal_regs.h
index 40e4f972fcb7..40e4f972fcb7 100644
--- a/drivers/media/platform/ti-vpe/cal_regs.h
+++ b/drivers/media/platform/ti/cal/cal_regs.h
diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/ti/davinci/Kconfig
index 9d2a9eeb3499..c61e697aeb12 100644
--- a/drivers/media/platform/davinci/Kconfig
+++ b/drivers/media/platform/ti/davinci/Kconfig
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config VIDEO_DAVINCI_VPIF_DISPLAY
tristate "TI DaVinci VPIF V4L2-Display driver"
- depends on VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF2_DMA_CONTIG
@@ -17,7 +18,8 @@ config VIDEO_DAVINCI_VPIF_DISPLAY
config VIDEO_DAVINCI_VPIF_CAPTURE
tristate "TI DaVinci VPIF video capture driver"
- depends on VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF2_DMA_CONTIG
@@ -32,7 +34,8 @@ config VIDEO_DAVINCI_VPIF_CAPTURE
config VIDEO_DM6446_CCDC
tristate "TI DM6446 CCDC video capture driver"
- depends on VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF_DMA_CONTIG
@@ -48,7 +51,8 @@ config VIDEO_DM6446_CCDC
config VIDEO_DM355_CCDC
tristate "TI DM355 CCDC video capture driver"
- depends on VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF_DMA_CONTIG
@@ -64,7 +68,8 @@ config VIDEO_DM355_CCDC
config VIDEO_DM365_ISIF
tristate "TI DM365 ISIF video capture driver"
- depends on VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF_DMA_CONTIG
@@ -78,7 +83,8 @@ config VIDEO_DM365_ISIF
config VIDEO_DAVINCI_VPBE_DISPLAY
tristate "TI DaVinci VPBE V4L2-Display driver"
- depends on VIDEO_V4L2
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
depends on ARCH_DAVINCI || COMPILE_TEST
depends on I2C
select VIDEOBUF2_DMA_CONTIG
diff --git a/drivers/media/platform/davinci/Makefile b/drivers/media/platform/ti/davinci/Makefile
index 05c45bf371aa..05c45bf371aa 100644
--- a/drivers/media/platform/davinci/Makefile
+++ b/drivers/media/platform/ti/davinci/Makefile
diff --git a/drivers/media/platform/davinci/ccdc_hw_device.h b/drivers/media/platform/ti/davinci/ccdc_hw_device.h
index a545052a95a9..a545052a95a9 100644
--- a/drivers/media/platform/davinci/ccdc_hw_device.h
+++ b/drivers/media/platform/ti/davinci/ccdc_hw_device.h
diff --git a/drivers/media/platform/davinci/dm355_ccdc.c b/drivers/media/platform/ti/davinci/dm355_ccdc.c
index e06d113dfe96..e06d113dfe96 100644
--- a/drivers/media/platform/davinci/dm355_ccdc.c
+++ b/drivers/media/platform/ti/davinci/dm355_ccdc.c
diff --git a/drivers/media/platform/davinci/dm355_ccdc_regs.h b/drivers/media/platform/ti/davinci/dm355_ccdc_regs.h
index eb381f075245..eb381f075245 100644
--- a/drivers/media/platform/davinci/dm355_ccdc_regs.h
+++ b/drivers/media/platform/ti/davinci/dm355_ccdc_regs.h
diff --git a/drivers/media/platform/davinci/dm644x_ccdc.c b/drivers/media/platform/ti/davinci/dm644x_ccdc.c
index c6378c4e0074..c6378c4e0074 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc.c
+++ b/drivers/media/platform/ti/davinci/dm644x_ccdc.c
diff --git a/drivers/media/platform/davinci/dm644x_ccdc_regs.h b/drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h
index c4894f6a254e..c4894f6a254e 100644
--- a/drivers/media/platform/davinci/dm644x_ccdc_regs.h
+++ b/drivers/media/platform/ti/davinci/dm644x_ccdc_regs.h
diff --git a/drivers/media/platform/davinci/isif.c b/drivers/media/platform/ti/davinci/isif.c
index c53cecd072b1..c53cecd072b1 100644
--- a/drivers/media/platform/davinci/isif.c
+++ b/drivers/media/platform/ti/davinci/isif.c
diff --git a/drivers/media/platform/davinci/isif_regs.h b/drivers/media/platform/ti/davinci/isif_regs.h
index d68d38841ae7..d68d38841ae7 100644
--- a/drivers/media/platform/davinci/isif_regs.h
+++ b/drivers/media/platform/ti/davinci/isif_regs.h
diff --git a/drivers/media/platform/davinci/vpbe.c b/drivers/media/platform/ti/davinci/vpbe.c
index 5f0aeb744e81..5f0aeb744e81 100644
--- a/drivers/media/platform/davinci/vpbe.c
+++ b/drivers/media/platform/ti/davinci/vpbe.c
diff --git a/drivers/media/platform/davinci/vpbe_display.c b/drivers/media/platform/ti/davinci/vpbe_display.c
index bf3c3e76b921..bf3c3e76b921 100644
--- a/drivers/media/platform/davinci/vpbe_display.c
+++ b/drivers/media/platform/ti/davinci/vpbe_display.c
diff --git a/drivers/media/platform/davinci/vpbe_osd.c b/drivers/media/platform/ti/davinci/vpbe_osd.c
index 32f7ef547c82..32f7ef547c82 100644
--- a/drivers/media/platform/davinci/vpbe_osd.c
+++ b/drivers/media/platform/ti/davinci/vpbe_osd.c
diff --git a/drivers/media/platform/davinci/vpbe_osd_regs.h b/drivers/media/platform/ti/davinci/vpbe_osd_regs.h
index cecd5991d4c5..cecd5991d4c5 100644
--- a/drivers/media/platform/davinci/vpbe_osd_regs.h
+++ b/drivers/media/platform/ti/davinci/vpbe_osd_regs.h
diff --git a/drivers/media/platform/davinci/vpbe_venc.c b/drivers/media/platform/ti/davinci/vpbe_venc.c
index 4c8e31de12b1..4c8e31de12b1 100644
--- a/drivers/media/platform/davinci/vpbe_venc.c
+++ b/drivers/media/platform/ti/davinci/vpbe_venc.c
diff --git a/drivers/media/platform/davinci/vpbe_venc_regs.h b/drivers/media/platform/ti/davinci/vpbe_venc_regs.h
index 29d8fc3af662..29d8fc3af662 100644
--- a/drivers/media/platform/davinci/vpbe_venc_regs.h
+++ b/drivers/media/platform/ti/davinci/vpbe_venc_regs.h
diff --git a/drivers/media/platform/davinci/vpfe_capture.c b/drivers/media/platform/ti/davinci/vpfe_capture.c
index 0a2226b321d7..0a2226b321d7 100644
--- a/drivers/media/platform/davinci/vpfe_capture.c
+++ b/drivers/media/platform/ti/davinci/vpfe_capture.c
diff --git a/drivers/media/platform/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c
index 5a89d885d0e3..97ef770266af 100644
--- a/drivers/media/platform/davinci/vpif.c
+++ b/drivers/media/platform/ti/davinci/vpif.c
@@ -20,8 +20,10 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
@@ -41,6 +43,11 @@ MODULE_ALIAS("platform:" VPIF_DRIVER_NAME);
#define VPIF_CH2_MAX_MODES 15
#define VPIF_CH3_MAX_MODES 2
+struct vpif_data {
+ struct platform_device *capture;
+ struct platform_device *display;
+};
+
DEFINE_SPINLOCK(vpif_lock);
EXPORT_SYMBOL_GPL(vpif_lock);
@@ -423,21 +430,35 @@ int vpif_channel_getfid(u8 channel_id)
}
EXPORT_SYMBOL(vpif_channel_getfid);
+static void vpif_pdev_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ kfree(pdev);
+}
+
static int vpif_probe(struct platform_device *pdev)
{
- static struct resource *res_irq;
+ static struct resource res_irq;
struct platform_device *pdev_capture, *pdev_display;
struct device_node *endpoint = NULL;
+ struct vpif_data *data;
+ int ret;
+ int irq;
vpif_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(vpif_base))
return PTR_ERR(vpif_base);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, data);
+
pm_runtime_enable(&pdev->dev);
pm_runtime_get(&pdev->dev);
- dev_info(&pdev->dev, "vpif probe success\n");
-
/*
* If VPIF Node has endpoints, assume "new" DT support,
* where capture and display drivers don't have DT nodes
@@ -453,49 +474,83 @@ static int vpif_probe(struct platform_device *pdev)
* For DT platforms, manually create platform_devices for
* capture/display drivers.
*/
- res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res_irq) {
- dev_warn(&pdev->dev, "Missing IRQ resource.\n");
- pm_runtime_put(&pdev->dev);
- return -EINVAL;
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = irq;
+ goto err_put_rpm;
}
+ res_irq = (struct resource)DEFINE_RES_IRQ_NAMED(irq, of_node_full_name(pdev->dev.of_node));
+ res_irq.flags |= irq_get_trigger_type(irq);
- pdev_capture = devm_kzalloc(&pdev->dev, sizeof(*pdev_capture),
- GFP_KERNEL);
- if (pdev_capture) {
- pdev_capture->name = "vpif_capture";
- pdev_capture->id = -1;
- pdev_capture->resource = res_irq;
- pdev_capture->num_resources = 1;
- pdev_capture->dev.dma_mask = pdev->dev.dma_mask;
- pdev_capture->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
- pdev_capture->dev.parent = &pdev->dev;
- platform_device_register(pdev_capture);
- } else {
- dev_warn(&pdev->dev, "Unable to allocate memory for pdev_capture.\n");
+ pdev_capture = kzalloc(sizeof(*pdev_capture), GFP_KERNEL);
+ if (!pdev_capture) {
+ ret = -ENOMEM;
+ goto err_put_rpm;
}
- pdev_display = devm_kzalloc(&pdev->dev, sizeof(*pdev_display),
- GFP_KERNEL);
- if (pdev_display) {
- pdev_display->name = "vpif_display";
- pdev_display->id = -1;
- pdev_display->resource = res_irq;
- pdev_display->num_resources = 1;
- pdev_display->dev.dma_mask = pdev->dev.dma_mask;
- pdev_display->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
- pdev_display->dev.parent = &pdev->dev;
- platform_device_register(pdev_display);
- } else {
- dev_warn(&pdev->dev, "Unable to allocate memory for pdev_display.\n");
+ pdev_capture->name = "vpif_capture";
+ pdev_capture->id = -1;
+ pdev_capture->resource = &res_irq;
+ pdev_capture->num_resources = 1;
+ pdev_capture->dev.dma_mask = pdev->dev.dma_mask;
+ pdev_capture->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
+ pdev_capture->dev.parent = &pdev->dev;
+ pdev_capture->dev.release = vpif_pdev_release;
+
+ ret = platform_device_register(pdev_capture);
+ if (ret)
+ goto err_put_pdev_capture;
+
+ pdev_display = kzalloc(sizeof(*pdev_display), GFP_KERNEL);
+ if (!pdev_display) {
+ ret = -ENOMEM;
+ goto err_put_pdev_capture;
}
+ pdev_display->name = "vpif_display";
+ pdev_display->id = -1;
+ pdev_display->resource = &res_irq;
+ pdev_display->num_resources = 1;
+ pdev_display->dev.dma_mask = pdev->dev.dma_mask;
+ pdev_display->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
+ pdev_display->dev.parent = &pdev->dev;
+ pdev_display->dev.release = vpif_pdev_release;
+
+ ret = platform_device_register(pdev_display);
+ if (ret)
+ goto err_put_pdev_display;
+
+ data->capture = pdev_capture;
+ data->display = pdev_display;
+
return 0;
+
+err_put_pdev_display:
+ platform_device_put(pdev_display);
+err_put_pdev_capture:
+ platform_device_put(pdev_capture);
+err_put_rpm:
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ kfree(data);
+
+ return ret;
}
static int vpif_remove(struct platform_device *pdev)
{
+ struct vpif_data *data = platform_get_drvdata(pdev);
+
+ if (data->capture)
+ platform_device_unregister(data->capture);
+ if (data->display)
+ platform_device_unregister(data->display);
+
+ pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+
+ kfree(data);
+
return 0;
}
diff --git a/drivers/media/platform/davinci/vpif.h b/drivers/media/platform/ti/davinci/vpif.h
index c6d1d890478a..c6d1d890478a 100644
--- a/drivers/media/platform/davinci/vpif.h
+++ b/drivers/media/platform/ti/davinci/vpif.h
diff --git a/drivers/media/platform/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index 8fe55374c5a3..bf76c5c83743 100644
--- a/drivers/media/platform/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -1607,7 +1607,6 @@ static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
struct i2c_adapter *i2c_adap;
- struct resource *res;
int subdev_count;
int res_idx = 0;
int i, err;
@@ -1632,17 +1631,23 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_free;
}
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
- err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
- IRQF_SHARED, VPIF_DRIVER_NAME,
- (void *)(&vpif_obj.dev[res_idx]->
- channel_id));
- if (err) {
- err = -EINVAL;
+ do {
+ int irq;
+
+ err = platform_get_irq_optional(pdev, res_idx);
+ if (err < 0 && err != -ENXIO)
goto vpif_unregister;
- }
- res_idx++;
- }
+ if (err > 0)
+ irq = err;
+ else
+ break;
+
+ err = devm_request_irq(&pdev->dev, irq, vpif_channel_isr,
+ IRQF_SHARED, VPIF_DRIVER_NAME,
+ (void *)(&vpif_obj.dev[res_idx]->channel_id));
+ if (err)
+ goto vpif_unregister;
+ } while (++res_idx);
vpif_obj.config = pdev->dev.platform_data;
diff --git a/drivers/media/platform/davinci/vpif_capture.h b/drivers/media/platform/ti/davinci/vpif_capture.h
index d5951f61df47..d5951f61df47 100644
--- a/drivers/media/platform/davinci/vpif_capture.h
+++ b/drivers/media/platform/ti/davinci/vpif_capture.h
diff --git a/drivers/media/platform/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c
index 59f6b782e104..fca148b66471 100644
--- a/drivers/media/platform/davinci/vpif_display.c
+++ b/drivers/media/platform/ti/davinci/vpif_display.c
@@ -1221,7 +1221,6 @@ static __init int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
struct i2c_adapter *i2c_adap;
- struct resource *res;
int subdev_count;
int res_idx = 0;
int i, err;
@@ -1245,18 +1244,25 @@ static __init int vpif_probe(struct platform_device *pdev)
goto vpif_free;
}
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, res_idx))) {
- err = devm_request_irq(&pdev->dev, res->start, vpif_channel_isr,
- IRQF_SHARED, VPIF_DRIVER_NAME,
- (void *)(&vpif_obj.dev[res_idx]->
- channel_id));
+ do {
+ int irq;
+
+ err = platform_get_irq_optional(pdev, res_idx);
+ if (err < 0 && err != -ENXIO)
+ goto vpif_unregister;
+ if (err > 0)
+ irq = err;
+ else
+ break;
+
+ err = devm_request_irq(&pdev->dev, irq, vpif_channel_isr,
+ IRQF_SHARED, VPIF_DRIVER_NAME,
+ (void *)(&vpif_obj.dev[res_idx]->channel_id));
if (err) {
- err = -EINVAL;
vpif_err("VPIF IRQ request failed\n");
goto vpif_unregister;
}
- res_idx++;
- }
+ } while (++res_idx);
vpif_obj.config = pdev->dev.platform_data;
subdev_count = vpif_obj.config->subdev_count;
diff --git a/drivers/media/platform/davinci/vpif_display.h b/drivers/media/platform/ti/davinci/vpif_display.h
index f98062e79167..f98062e79167 100644
--- a/drivers/media/platform/davinci/vpif_display.h
+++ b/drivers/media/platform/ti/davinci/vpif_display.h
diff --git a/drivers/media/platform/davinci/vpss.c b/drivers/media/platform/ti/davinci/vpss.c
index d15b991ab17c..d15b991ab17c 100644
--- a/drivers/media/platform/davinci/vpss.c
+++ b/drivers/media/platform/ti/davinci/vpss.c
diff --git a/drivers/media/platform/omap/Kconfig b/drivers/media/platform/ti/omap/Kconfig
index de16de46c0f4..a9dbe1097775 100644
--- a/drivers/media/platform/omap/Kconfig
+++ b/drivers/media/platform/ti/omap/Kconfig
@@ -6,10 +6,11 @@ config VIDEO_OMAP2_VOUT_VRFB
config VIDEO_OMAP2_VOUT
tristate "OMAP2/OMAP3 V4L2-Display driver"
+ depends on V4L_PLATFORM_DRIVERS
depends on MMU
depends on FB_OMAP2 || (COMPILE_TEST && FB_OMAP2=n)
depends on ARCH_OMAP2 || ARCH_OMAP3 || COMPILE_TEST
- depends on VIDEO_V4L2
+ depends on VIDEO_DEV
select VIDEOBUF2_DMA_CONTIG
select OMAP2_VRFB if ARCH_OMAP2 || ARCH_OMAP3
help
diff --git a/drivers/media/platform/omap/Makefile b/drivers/media/platform/ti/omap/Makefile
index b17a0ac10c00..b17a0ac10c00 100644
--- a/drivers/media/platform/omap/Makefile
+++ b/drivers/media/platform/ti/omap/Makefile
diff --git a/drivers/media/platform/omap/omap_vout.c b/drivers/media/platform/ti/omap/omap_vout.c
index 3e0d9af7ffec..3e0d9af7ffec 100644
--- a/drivers/media/platform/omap/omap_vout.c
+++ b/drivers/media/platform/ti/omap/omap_vout.c
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.c b/drivers/media/platform/ti/omap/omap_vout_vrfb.c
index 0cfa0169875f..0cfa0169875f 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.c
+++ b/drivers/media/platform/ti/omap/omap_vout_vrfb.c
diff --git a/drivers/media/platform/omap/omap_vout_vrfb.h b/drivers/media/platform/ti/omap/omap_vout_vrfb.h
index 40bc9e54ecc6..40bc9e54ecc6 100644
--- a/drivers/media/platform/omap/omap_vout_vrfb.h
+++ b/drivers/media/platform/ti/omap/omap_vout_vrfb.h
diff --git a/drivers/media/platform/omap/omap_voutdef.h b/drivers/media/platform/ti/omap/omap_voutdef.h
index b586193341d2..b586193341d2 100644
--- a/drivers/media/platform/omap/omap_voutdef.h
+++ b/drivers/media/platform/ti/omap/omap_voutdef.h
diff --git a/drivers/media/platform/omap/omap_voutlib.c b/drivers/media/platform/ti/omap/omap_voutlib.c
index 480a7e95533d..480a7e95533d 100644
--- a/drivers/media/platform/omap/omap_voutlib.c
+++ b/drivers/media/platform/ti/omap/omap_voutlib.c
diff --git a/drivers/media/platform/omap/omap_voutlib.h b/drivers/media/platform/ti/omap/omap_voutlib.h
index f9d1c0779f33..f9d1c0779f33 100644
--- a/drivers/media/platform/omap/omap_voutlib.h
+++ b/drivers/media/platform/ti/omap/omap_voutlib.h
diff --git a/drivers/media/platform/ti/omap3isp/Kconfig b/drivers/media/platform/ti/omap3isp/Kconfig
new file mode 100644
index 000000000000..f0a680938d5e
--- /dev/null
+++ b/drivers/media/platform/ti/omap3isp/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_OMAP3
+ tristate "OMAP 3 Camera support"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && I2C
+ depends on (ARCH_OMAP3 && OMAP_IOMMU) || COMPILE_TEST
+ depends on COMMON_CLK && OF
+ select ARM_DMA_USE_IOMMU if OMAP_IOMMU
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select MFD_SYSCON
+ select V4L2_FWNODE
+ help
+ Driver for an OMAP 3 camera controller.
+
+config VIDEO_OMAP3_DEBUG
+ bool "OMAP 3 Camera debug messages"
+ depends on VIDEO_OMAP3
+ help
+ Enable debug messages on OMAP 3 camera controller driver.
diff --git a/drivers/media/platform/omap3isp/Makefile b/drivers/media/platform/ti/omap3isp/Makefile
index 56e99b4f7d23..56e99b4f7d23 100644
--- a/drivers/media/platform/omap3isp/Makefile
+++ b/drivers/media/platform/ti/omap3isp/Makefile
diff --git a/drivers/media/platform/omap3isp/cfa_coef_table.h b/drivers/media/platform/ti/omap3isp/cfa_coef_table.h
index 786200c5e4fa..786200c5e4fa 100644
--- a/drivers/media/platform/omap3isp/cfa_coef_table.h
+++ b/drivers/media/platform/ti/omap3isp/cfa_coef_table.h
diff --git a/drivers/media/platform/omap3isp/gamma_table.h b/drivers/media/platform/ti/omap3isp/gamma_table.h
index 442c82c2eb22..442c82c2eb22 100644
--- a/drivers/media/platform/omap3isp/gamma_table.h
+++ b/drivers/media/platform/ti/omap3isp/gamma_table.h
diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c
index 4c937f3f323e..4c937f3f323e 100644
--- a/drivers/media/platform/omap3isp/isp.c
+++ b/drivers/media/platform/ti/omap3isp/isp.c
diff --git a/drivers/media/platform/omap3isp/isp.h b/drivers/media/platform/ti/omap3isp/isp.h
index a9d760fbf349..a9d760fbf349 100644
--- a/drivers/media/platform/omap3isp/isp.h
+++ b/drivers/media/platform/ti/omap3isp/isp.h
diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c
index 108b5e9f82cb..108b5e9f82cb 100644
--- a/drivers/media/platform/omap3isp/ispccdc.c
+++ b/drivers/media/platform/ti/omap3isp/ispccdc.c
diff --git a/drivers/media/platform/omap3isp/ispccdc.h b/drivers/media/platform/ti/omap3isp/ispccdc.h
index 7883365d7203..7883365d7203 100644
--- a/drivers/media/platform/omap3isp/ispccdc.h
+++ b/drivers/media/platform/ti/omap3isp/ispccdc.h
diff --git a/drivers/media/platform/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c
index acb58b6ddba1..acb58b6ddba1 100644
--- a/drivers/media/platform/omap3isp/ispccp2.c
+++ b/drivers/media/platform/ti/omap3isp/ispccp2.c
diff --git a/drivers/media/platform/omap3isp/ispccp2.h b/drivers/media/platform/ti/omap3isp/ispccp2.h
index 03e6af3de1d9..03e6af3de1d9 100644
--- a/drivers/media/platform/omap3isp/ispccp2.h
+++ b/drivers/media/platform/ti/omap3isp/ispccp2.h
diff --git a/drivers/media/platform/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c
index 6302e0c94034..6302e0c94034 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c
diff --git a/drivers/media/platform/omap3isp/ispcsi2.h b/drivers/media/platform/ti/omap3isp/ispcsi2.h
index 036b97f8470e..036b97f8470e 100644
--- a/drivers/media/platform/omap3isp/ispcsi2.h
+++ b/drivers/media/platform/ti/omap3isp/ispcsi2.h
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.c b/drivers/media/platform/ti/omap3isp/ispcsiphy.c
index 6dc7359c5131..6dc7359c5131 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.c
+++ b/drivers/media/platform/ti/omap3isp/ispcsiphy.c
diff --git a/drivers/media/platform/omap3isp/ispcsiphy.h b/drivers/media/platform/ti/omap3isp/ispcsiphy.h
index ed9b8d221e3f..ed9b8d221e3f 100644
--- a/drivers/media/platform/omap3isp/ispcsiphy.h
+++ b/drivers/media/platform/ti/omap3isp/ispcsiphy.h
diff --git a/drivers/media/platform/omap3isp/isph3a.h b/drivers/media/platform/ti/omap3isp/isph3a.h
index 5144f7689dda..5144f7689dda 100644
--- a/drivers/media/platform/omap3isp/isph3a.h
+++ b/drivers/media/platform/ti/omap3isp/isph3a.h
diff --git a/drivers/media/platform/omap3isp/isph3a_aewb.c b/drivers/media/platform/ti/omap3isp/isph3a_aewb.c
index e6c54c4bbfca..e6c54c4bbfca 100644
--- a/drivers/media/platform/omap3isp/isph3a_aewb.c
+++ b/drivers/media/platform/ti/omap3isp/isph3a_aewb.c
diff --git a/drivers/media/platform/omap3isp/isph3a_af.c b/drivers/media/platform/ti/omap3isp/isph3a_af.c
index de7b116d0122..de7b116d0122 100644
--- a/drivers/media/platform/omap3isp/isph3a_af.c
+++ b/drivers/media/platform/ti/omap3isp/isph3a_af.c
diff --git a/drivers/media/platform/omap3isp/isphist.c b/drivers/media/platform/ti/omap3isp/isphist.c
index 0ef78aace6da..0ef78aace6da 100644
--- a/drivers/media/platform/omap3isp/isphist.c
+++ b/drivers/media/platform/ti/omap3isp/isphist.c
diff --git a/drivers/media/platform/omap3isp/isphist.h b/drivers/media/platform/ti/omap3isp/isphist.h
index 93cd27a3b617..93cd27a3b617 100644
--- a/drivers/media/platform/omap3isp/isphist.h
+++ b/drivers/media/platform/ti/omap3isp/isphist.h
diff --git a/drivers/media/platform/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c
index 53aedec7990d..53aedec7990d 100644
--- a/drivers/media/platform/omap3isp/isppreview.c
+++ b/drivers/media/platform/ti/omap3isp/isppreview.c
diff --git a/drivers/media/platform/omap3isp/isppreview.h b/drivers/media/platform/ti/omap3isp/isppreview.h
index 5fff1ec3624f..5fff1ec3624f 100644
--- a/drivers/media/platform/omap3isp/isppreview.h
+++ b/drivers/media/platform/ti/omap3isp/isppreview.h
diff --git a/drivers/media/platform/omap3isp/ispreg.h b/drivers/media/platform/ti/omap3isp/ispreg.h
index 86b6ebb0438d..86b6ebb0438d 100644
--- a/drivers/media/platform/omap3isp/ispreg.h
+++ b/drivers/media/platform/ti/omap3isp/ispreg.h
diff --git a/drivers/media/platform/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c
index ed2fb0c7a57e..ed2fb0c7a57e 100644
--- a/drivers/media/platform/omap3isp/ispresizer.c
+++ b/drivers/media/platform/ti/omap3isp/ispresizer.c
diff --git a/drivers/media/platform/omap3isp/ispresizer.h b/drivers/media/platform/ti/omap3isp/ispresizer.h
index 28cc89940ead..28cc89940ead 100644
--- a/drivers/media/platform/omap3isp/ispresizer.h
+++ b/drivers/media/platform/ti/omap3isp/ispresizer.h
diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/ti/omap3isp/ispstat.c
index 5b9b57f4d9bf..68cf68dbcace 100644
--- a/drivers/media/platform/omap3isp/ispstat.c
+++ b/drivers/media/platform/ti/omap3isp/ispstat.c
@@ -512,7 +512,7 @@ int omap3isp_stat_request_statistics(struct ispstat *stat,
int omap3isp_stat_request_statistics_time32(struct ispstat *stat,
struct omap3isp_stat_data_time32 *data)
{
- struct omap3isp_stat_data data64;
+ struct omap3isp_stat_data data64 = { };
int ret;
ret = omap3isp_stat_request_statistics(stat, &data64);
@@ -521,7 +521,8 @@ int omap3isp_stat_request_statistics_time32(struct ispstat *stat,
data->ts.tv_sec = data64.ts.tv_sec;
data->ts.tv_usec = data64.ts.tv_usec;
- memcpy(&data->buf, &data64.buf, sizeof(*data) - sizeof(data->ts));
+ data->buf = (uintptr_t)data64.buf;
+ memcpy(&data->frame, &data64.frame, sizeof(data->frame));
return 0;
}
diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/ti/omap3isp/ispstat.h
index b548e617cf62..b548e617cf62 100644
--- a/drivers/media/platform/omap3isp/ispstat.h
+++ b/drivers/media/platform/ti/omap3isp/ispstat.h
diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c
index 8811d6dd4ee7..8811d6dd4ee7 100644
--- a/drivers/media/platform/omap3isp/ispvideo.c
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.c
diff --git a/drivers/media/platform/omap3isp/ispvideo.h b/drivers/media/platform/ti/omap3isp/ispvideo.h
index a0908670c0cf..a0908670c0cf 100644
--- a/drivers/media/platform/omap3isp/ispvideo.h
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.h
diff --git a/drivers/media/platform/omap3isp/luma_enhance_table.h b/drivers/media/platform/ti/omap3isp/luma_enhance_table.h
index d5fbf9241f48..d5fbf9241f48 100644
--- a/drivers/media/platform/omap3isp/luma_enhance_table.h
+++ b/drivers/media/platform/ti/omap3isp/luma_enhance_table.h
diff --git a/drivers/media/platform/omap3isp/noise_filter_table.h b/drivers/media/platform/ti/omap3isp/noise_filter_table.h
index da66bd0a3b9f..da66bd0a3b9f 100644
--- a/drivers/media/platform/omap3isp/noise_filter_table.h
+++ b/drivers/media/platform/ti/omap3isp/noise_filter_table.h
diff --git a/drivers/media/platform/omap3isp/omap3isp.h b/drivers/media/platform/ti/omap3isp/omap3isp.h
index 4a003c8263ed..4a003c8263ed 100644
--- a/drivers/media/platform/omap3isp/omap3isp.h
+++ b/drivers/media/platform/ti/omap3isp/omap3isp.h
diff --git a/drivers/media/platform/ti-vpe/Makefile b/drivers/media/platform/ti/vpe/Makefile
index ad624056e039..3fadfe084f87 100644
--- a/drivers/media/platform/ti-vpe/Makefile
+++ b/drivers/media/platform/ti/vpe/Makefile
@@ -10,7 +10,3 @@ ti-sc-y := sc.o
ti-csc-y := csc.o
ccflags-$(CONFIG_VIDEO_TI_VPE_DEBUG) += -DDEBUG
-
-obj-$(CONFIG_VIDEO_TI_CAL) += ti-cal.o
-
-ti-cal-y := cal.o cal-camerarx.o cal-video.o
diff --git a/drivers/media/platform/ti-vpe/csc.c b/drivers/media/platform/ti/vpe/csc.c
index ff15bc589f1b..ff15bc589f1b 100644
--- a/drivers/media/platform/ti-vpe/csc.c
+++ b/drivers/media/platform/ti/vpe/csc.c
diff --git a/drivers/media/platform/ti-vpe/csc.h b/drivers/media/platform/ti/vpe/csc.h
index af2e86bccf57..af2e86bccf57 100644
--- a/drivers/media/platform/ti-vpe/csc.h
+++ b/drivers/media/platform/ti/vpe/csc.h
diff --git a/drivers/media/platform/ti-vpe/sc.c b/drivers/media/platform/ti/vpe/sc.c
index 0202d278523f..0202d278523f 100644
--- a/drivers/media/platform/ti-vpe/sc.c
+++ b/drivers/media/platform/ti/vpe/sc.c
diff --git a/drivers/media/platform/ti-vpe/sc.h b/drivers/media/platform/ti/vpe/sc.h
index d55de44d5257..d55de44d5257 100644
--- a/drivers/media/platform/ti-vpe/sc.h
+++ b/drivers/media/platform/ti/vpe/sc.h
diff --git a/drivers/media/platform/ti-vpe/sc_coeff.h b/drivers/media/platform/ti/vpe/sc_coeff.h
index c525d1764099..c525d1764099 100644
--- a/drivers/media/platform/ti-vpe/sc_coeff.h
+++ b/drivers/media/platform/ti/vpe/sc_coeff.h
diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c
index f8998a8ad371..f8998a8ad371 100644
--- a/drivers/media/platform/ti-vpe/vpdma.c
+++ b/drivers/media/platform/ti/vpe/vpdma.c
diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h
index 393fcbb3cb40..393fcbb3cb40 100644
--- a/drivers/media/platform/ti-vpe/vpdma.h
+++ b/drivers/media/platform/ti/vpe/vpdma.h
diff --git a/drivers/media/platform/ti-vpe/vpdma_priv.h b/drivers/media/platform/ti/vpe/vpdma_priv.h
index 0bbee45338bd..0bbee45338bd 100644
--- a/drivers/media/platform/ti-vpe/vpdma_priv.h
+++ b/drivers/media/platform/ti/vpe/vpdma_priv.h
diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c
index 5b1c5d96a407..5b1c5d96a407 100644
--- a/drivers/media/platform/ti-vpe/vpe.c
+++ b/drivers/media/platform/ti/vpe/vpe.c
diff --git a/drivers/media/platform/ti-vpe/vpe_regs.h b/drivers/media/platform/ti/vpe/vpe_regs.h
index 1a1ad5ae1228..1a1ad5ae1228 100644
--- a/drivers/media/platform/ti-vpe/vpe_regs.h
+++ b/drivers/media/platform/ti/vpe/vpe_regs.h
diff --git a/drivers/media/platform/via/Kconfig b/drivers/media/platform/via/Kconfig
new file mode 100644
index 000000000000..8926eb0803b2
--- /dev/null
+++ b/drivers/media/platform/via/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "VIA media platform drivers"
+
+config VIDEO_VIA_CAMERA
+ tristate "VIAFB camera controller support"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on FB_VIA && VIDEO_DEV
+ select VIDEOBUF2_DMA_SG
+ select VIDEO_OV7670
+ help
+ Driver support for the integrated camera controller in VIA
+ Chrome9 chipsets. Currently only tested on OLPC xo-1.5 systems
+ with ov7670 sensors.
diff --git a/drivers/media/platform/via/Makefile b/drivers/media/platform/via/Makefile
new file mode 100644
index 000000000000..80f747f3fffc
--- /dev/null
+++ b/drivers/media/platform/via/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o
diff --git a/drivers/media/platform/via-camera.c b/drivers/media/platform/via/via-camera.c
index 95483c84c3f2..95483c84c3f2 100644
--- a/drivers/media/platform/via-camera.c
+++ b/drivers/media/platform/via/via-camera.c
diff --git a/drivers/media/platform/via-camera.h b/drivers/media/platform/via/via-camera.h
index 54f16318b1b3..54f16318b1b3 100644
--- a/drivers/media/platform/via-camera.h
+++ b/drivers/media/platform/via/via-camera.h
diff --git a/drivers/media/platform/xilinx/Kconfig b/drivers/media/platform/xilinx/Kconfig
index 44587dccacf1..93ef78bf62e6 100644
--- a/drivers/media/platform/xilinx/Kconfig
+++ b/drivers/media/platform/xilinx/Kconfig
@@ -1,8 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
+comment "Xilinx media platform drivers"
+
config VIDEO_XILINX
tristate "Xilinx Video IP (EXPERIMENTAL)"
- depends on VIDEO_V4L2 && OF && HAS_DMA
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF && HAS_DMA
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select VIDEOBUF2_DMA_CONTIG
@@ -10,9 +13,8 @@ config VIDEO_XILINX
help
Driver for Xilinx Video IP Pipelines
-if VIDEO_XILINX
-
config VIDEO_XILINX_CSI2RXSS
+ depends on VIDEO_XILINX
tristate "Xilinx CSI-2 Rx Subsystem"
help
Driver for Xilinx MIPI CSI-2 Rx Subsystem. This is a V4L sub-device
@@ -31,5 +33,3 @@ config VIDEO_XILINX_VTC
depends on VIDEO_XILINX
help
Driver for the Xilinx Video Timing Controller
-
-endif #VIDEO_XILINX
diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
index b1baf9d7b6ec..051c60cba1e0 100644
--- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c
+++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/v4l2-subdev.h>
#include <media/media-entity.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
@@ -115,23 +116,6 @@
#define XCSI_DEFAULT_WIDTH 1920
#define XCSI_DEFAULT_HEIGHT 1080
-/* MIPI CSI-2 Data Types from spec */
-#define XCSI_DT_YUV4228B 0x1e
-#define XCSI_DT_YUV42210B 0x1f
-#define XCSI_DT_RGB444 0x20
-#define XCSI_DT_RGB555 0x21
-#define XCSI_DT_RGB565 0x22
-#define XCSI_DT_RGB666 0x23
-#define XCSI_DT_RGB888 0x24
-#define XCSI_DT_RAW6 0x28
-#define XCSI_DT_RAW7 0x29
-#define XCSI_DT_RAW8 0x2a
-#define XCSI_DT_RAW10 0x2b
-#define XCSI_DT_RAW12 0x2c
-#define XCSI_DT_RAW14 0x2d
-#define XCSI_DT_RAW16 0x2e
-#define XCSI_DT_RAW20 0x2f
-
#define XCSI_VCX_START 4
#define XCSI_MAX_VC 4
#define XCSI_MAX_VCX 16
@@ -183,32 +167,32 @@ static const struct xcsi2rxss_event xcsi2rxss_events[] = {
* and media bus formats
*/
static const u32 xcsi2dt_mbus_lut[][2] = {
- { XCSI_DT_YUV4228B, MEDIA_BUS_FMT_UYVY8_1X16 },
- { XCSI_DT_YUV42210B, MEDIA_BUS_FMT_UYVY10_1X20 },
- { XCSI_DT_RGB444, 0 },
- { XCSI_DT_RGB555, 0 },
- { XCSI_DT_RGB565, 0 },
- { XCSI_DT_RGB666, 0 },
- { XCSI_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 },
- { XCSI_DT_RAW6, 0 },
- { XCSI_DT_RAW7, 0 },
- { XCSI_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 },
- { XCSI_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 },
- { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 },
- { XCSI_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 },
- { XCSI_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 },
- { XCSI_DT_RAW10, MEDIA_BUS_FMT_SBGGR10_1X10 },
- { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGBRG10_1X10 },
- { XCSI_DT_RAW10, MEDIA_BUS_FMT_SGRBG10_1X10 },
- { XCSI_DT_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 },
- { XCSI_DT_RAW12, MEDIA_BUS_FMT_SBGGR12_1X12 },
- { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGBRG12_1X12 },
- { XCSI_DT_RAW12, MEDIA_BUS_FMT_SGRBG12_1X12 },
- { XCSI_DT_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 },
- { XCSI_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 },
- { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 },
- { XCSI_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 },
- { XCSI_DT_RAW20, 0 },
+ { MIPI_CSI2_DT_YUV422_8B, MEDIA_BUS_FMT_UYVY8_1X16 },
+ { MIPI_CSI2_DT_YUV422_10B, MEDIA_BUS_FMT_UYVY10_1X20 },
+ { MIPI_CSI2_DT_RGB444, 0 },
+ { MIPI_CSI2_DT_RGB555, 0 },
+ { MIPI_CSI2_DT_RGB565, 0 },
+ { MIPI_CSI2_DT_RGB666, 0 },
+ { MIPI_CSI2_DT_RGB888, MEDIA_BUS_FMT_RBG888_1X24 },
+ { MIPI_CSI2_DT_RAW6, 0 },
+ { MIPI_CSI2_DT_RAW7, 0 },
+ { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SRGGB8_1X8 },
+ { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SBGGR8_1X8 },
+ { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SGBRG8_1X8 },
+ { MIPI_CSI2_DT_RAW8, MEDIA_BUS_FMT_SGRBG8_1X8 },
+ { MIPI_CSI2_DT_RAW10, MEDIA_BUS_FMT_SRGGB10_1X10 },
+ { MIPI_CSI2_DT_RAW10, 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_RAW12, MEDIA_BUS_FMT_SRGGB12_1X12 },
+ { MIPI_CSI2_DT_RAW12, 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_RAW16, MEDIA_BUS_FMT_SRGGB16_1X16 },
+ { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SBGGR16_1X16 },
+ { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SGBRG16_1X16 },
+ { MIPI_CSI2_DT_RAW16, MEDIA_BUS_FMT_SGRBG16_1X16 },
+ { MIPI_CSI2_DT_RAW20, 0 },
};
/**
@@ -791,7 +775,7 @@ static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
* other RAW, YUV422 8/10 or RGB888, set appropriate media bus format.
*/
dt = xcsi2rxss_get_dt(fmt->format.code);
- if (dt != xcsi2rxss->datatype && dt != XCSI_DT_RAW8) {
+ if (dt != xcsi2rxss->datatype && dt != MIPI_CSI2_DT_RAW8) {
dev_dbg(xcsi2rxss->dev, "Unsupported media bus format");
/* set the default format for the data type */
fmt->format.code = xcsi2rxss_get_nth_mbus(xcsi2rxss->datatype,
@@ -823,8 +807,8 @@ static int xcsi2rxss_enum_mbus_code(struct v4l2_subdev *sd,
/* RAW8 dt packets are available in all DT configurations */
if (code->index < 4) {
n = code->index;
- dt = XCSI_DT_RAW8;
- } else if (state->datatype != XCSI_DT_RAW8) {
+ dt = MIPI_CSI2_DT_RAW8;
+ } else if (state->datatype != MIPI_CSI2_DT_RAW8) {
n = code->index - 4;
dt = state->datatype;
} else {
@@ -895,22 +879,22 @@ static int xcsi2rxss_parse_of(struct xcsi2rxss_state *xcsi2rxss)
}
switch (xcsi2rxss->datatype) {
- case XCSI_DT_YUV4228B:
- case XCSI_DT_RGB444:
- case XCSI_DT_RGB555:
- case XCSI_DT_RGB565:
- case XCSI_DT_RGB666:
- case XCSI_DT_RGB888:
- case XCSI_DT_RAW6:
- case XCSI_DT_RAW7:
- case XCSI_DT_RAW8:
- case XCSI_DT_RAW10:
- case XCSI_DT_RAW12:
- case XCSI_DT_RAW14:
+ case MIPI_CSI2_DT_YUV422_8B:
+ case MIPI_CSI2_DT_RGB444:
+ case MIPI_CSI2_DT_RGB555:
+ case MIPI_CSI2_DT_RGB565:
+ case MIPI_CSI2_DT_RGB666:
+ case MIPI_CSI2_DT_RGB888:
+ case MIPI_CSI2_DT_RAW6:
+ case MIPI_CSI2_DT_RAW7:
+ case MIPI_CSI2_DT_RAW8:
+ case MIPI_CSI2_DT_RAW10:
+ case MIPI_CSI2_DT_RAW12:
+ case MIPI_CSI2_DT_RAW14:
break;
- case XCSI_DT_YUV42210B:
- case XCSI_DT_RAW16:
- case XCSI_DT_RAW20:
+ case MIPI_CSI2_DT_YUV422_10B:
+ case MIPI_CSI2_DT_RAW16:
+ case MIPI_CSI2_DT_RAW20:
if (!en_csi_v20) {
ret = -EINVAL;
dev_dbg(dev, "enable csi v2 for this pixel format");