summaryrefslogtreecommitdiff
path: root/drivers/media/platform
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform')
-rw-r--r--drivers/media/platform/Kconfig4
-rw-r--r--drivers/media/platform/Makefile4
-rw-r--r--drivers/media/platform/allegro-dvt/allegro-core.c37
-rw-r--r--drivers/media/platform/allegro-dvt/nal-hevc.h7
-rw-r--r--drivers/media/platform/amlogic/Kconfig1
-rw-r--r--drivers/media/platform/amlogic/Makefile2
-rw-r--r--drivers/media/platform/amlogic/c3/Kconfig5
-rw-r--r--drivers/media/platform/amlogic/c3/Makefile5
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Kconfig18
-rw-r--r--drivers/media/platform/amlogic/c3/isp/Makefile10
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c804
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-common.h340
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-core.c641
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c421
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-params.c1008
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h618
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c892
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c326
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c842
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig16
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/Makefile3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c828
-rw-r--r--drivers/media/platform/amlogic/meson-ge2d/ge2d.c4
-rw-r--r--drivers/media/platform/amphion/vdec.c4
-rw-r--r--drivers/media/platform/amphion/venc.c18
-rw-r--r--drivers/media/platform/amphion/vpu.h2
-rw-r--r--drivers/media/platform/amphion/vpu_core.c9
-rw-r--r--drivers/media/platform/amphion/vpu_defs.h1
-rw-r--r--drivers/media/platform/amphion/vpu_drv.c4
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c44
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c20
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.h1
-rw-r--r--drivers/media/platform/amphion/vpu_windsor.c2
-rw-r--r--drivers/media/platform/aspeed/aspeed-video.c4
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c20
-rw-r--r--drivers/media/platform/broadcom/Kconfig23
-rw-r--r--drivers/media/platform/broadcom/Makefile3
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam-regs.h246
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c2757
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c32
-rw-r--r--drivers/media/platform/cadence/cdns-csi2tx.c2
-rw-r--r--drivers/media/platform/chips-media/coda/coda-bit.c2
-rw-r--r--drivers/media/platform/chips-media/coda/coda-common.c5
-rw-r--r--drivers/media/platform/chips-media/coda/coda-jpeg.c2
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.c36
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.h5
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-hw.c425
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-regdefine.h5
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vdi.c27
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c390
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c324
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.c210
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu.h5
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.c43
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.h8
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h43
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5.h9
-rw-r--r--drivers/media/platform/imagination/Kconfig14
-rw-r--r--drivers/media/platform/imagination/Makefile3
-rw-r--r--drivers/media/platform/imagination/e5010-core-regs.h585
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc-hw.c267
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc-hw.h42
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.c1635
-rw-r--r--drivers/media/platform/imagination/e5010-jpeg-enc.h168
-rw-r--r--drivers/media/platform/imagination/e5010-mmu-regs.h311
-rw-r--r--drivers/media/platform/intel/pxa_camera.c19
-rw-r--r--drivers/media/platform/m2m-deinterlace.c8
-rw-r--r--drivers/media/platform/marvell/mcam-core.c20
-rw-r--r--drivers/media/platform/marvell/mcam-core.h3
-rw-r--r--drivers/media/platform/marvell/mmp-driver.c24
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c21
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h4
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c84
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h1
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h8
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c33
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h7
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_core.c2
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c4
-rw-r--r--drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c280
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h1
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h1
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c87
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h3
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c537
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h29
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c32
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h3
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c8
-rw-r--r--drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c5
-rw-r--r--drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c24
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c62
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c42
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c12
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h15
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c23
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c665
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c12
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c3
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h4
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c6
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h4
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c2
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c4
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h2
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c13
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c5
-rw-r--r--drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h2
-rw-r--r--drivers/media/platform/mediatek/vpu/mtk_vpu.c2
-rw-r--r--drivers/media/platform/microchip/microchip-csi2dc.c2
-rw-r--r--drivers/media/platform/microchip/microchip-isc-base.c21
-rw-r--r--drivers/media/platform/microchip/microchip-sama5d2-isc.c23
-rw-r--r--drivers/media/platform/microchip/microchip-sama7g5-isc.c23
-rw-r--r--drivers/media/platform/nuvoton/npcm-video.c33
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c2
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/h264.c15
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/iommu.c7
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/trace.h2
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/v4l2.c14
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.c2
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/vde.h1
-rw-r--r--drivers/media/platform/nxp/dw100/dw100.c14
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h1
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c170
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h5
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c80
-rw-r--r--drivers/media/platform/nxp/imx-pxp.c7
-rw-r--r--drivers/media/platform/nxp/imx-pxp.h9
-rw-r--r--drivers/media/platform/nxp/imx7-media-csi.c4
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c16
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c16
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c5
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c30
-rw-r--r--drivers/media/platform/nxp/mx2_emmaprp.c4
-rw-r--r--drivers/media/platform/qcom/Kconfig1
-rw-r--r--drivers/media/platform/qcom/Makefile1
-rw-r--r--drivers/media/platform/qcom/camss/Makefile7
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-1.c151
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-4-7.c202
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-680.c422
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-780.c337
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-780.h25
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.c467
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c650
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h87
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c6
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c864
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c142
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h41
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.c91
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.h62
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c5
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-17x.c (renamed from drivers/media/platform/qcom/camss/camss-vfe-170.c)120
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c23
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-7.c17
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-8.c17
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-480.c274
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-680.c244
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-780.c159
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-gen1.c17
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c790
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h82
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c301
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.h4
-rw-r--r--drivers/media/platform/qcom/camss/camss.c2459
-rw-r--r--drivers/media/platform/qcom/camss/camss.h40
-rw-r--r--drivers/media/platform/qcom/iris/Kconfig13
-rw-r--r--drivers/media/platform/qcom/iris/Makefile31
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c623
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h117
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.c96
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.h113
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c259
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.h22
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c118
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.c176
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h155
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1.h16
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c826
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h448
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c666
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2.h41
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c957
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h161
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c292
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h125
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c934
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.c318
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.h182
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h77
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h190
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c385
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h124
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8250.c149
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8650.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.c140
-rw-r--r--drivers/media/platform/qcom/iris/iris_power.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c372
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.c131
-rw-r--r--drivers/media/platform/qcom/iris/iris_resources.h18
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.c276
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.h144
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.c90
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.h53
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c335
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.h19
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c659
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.h25
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c453
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.h15
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu2.c39
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu3x.c275
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c270
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.h91
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c369
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.h31
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_register_defines.h17
-rw-r--r--drivers/media/platform/qcom/venus/Kconfig3
-rw-r--r--drivers/media/platform/qcom/venus/core.c200
-rw-r--r--drivers/media/platform/qcom/venus/core.h20
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c6
-rw-r--r--drivers/media/platform/qcom/venus/hfi.c23
-rw-r--r--drivers/media/platform/qcom/venus/hfi.h2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c8
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.h18
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h20
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c102
-rw-r--r--drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c20
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c29
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c84
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c54
-rw-r--r--drivers/media/platform/qcom/venus/vdec.h1
-rw-r--r--drivers/media/platform/qcom/venus/vdec_ctrls.c5
-rw-r--r--drivers/media/platform/qcom/venus/venc.c105
-rw-r--r--drivers/media/platform/qcom/venus/venc.h1
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c140
-rw-r--r--drivers/media/platform/raspberrypi/Kconfig6
-rw-r--r--drivers/media/platform/raspberrypi/Makefile4
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Kconfig13
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Makefile6
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be.c1797
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h519
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/Kconfig15
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/Makefile6
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h332
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h202
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.c2508
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.h43
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.c586
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.h89
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/dphy.c181
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/dphy.h27
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c605
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h53
-rw-r--r--drivers/media/platform/renesas/Kconfig18
-rw-r--r--drivers/media/platform/renesas/Makefile2
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c858
-rw-r--r--drivers/media/platform/renesas/rcar-fcp.c2
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Kconfig18
-rw-r--r--drivers/media/platform/renesas/rcar-isp/Makefile4
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c (renamed from drivers/media/platform/renesas/rcar-isp.c)225
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c32
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c232
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c67
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h47
-rw-r--r--drivers/media/platform/renesas/rcar_drif.c6
-rw-r--r--drivers/media/platform/renesas/rcar_fdp1.c4
-rw-r--r--drivers/media/platform/renesas/rcar_jpu.c6
-rw-r--r--drivers/media/platform/renesas/renesas-ceu.c4
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c153
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h111
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h73
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c207
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c98
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c590
-rw-r--r--drivers/media/platform/renesas/sh_vou.c4
-rw-r--r--drivers/media/platform/renesas/vsp1/Makefile2
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1.h4
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c40
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c4
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c7
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c57
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.h10
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c72
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c96
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h51
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.c28
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.c20
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c78
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c17
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.c121
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_iif.h29
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lif.c4
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c290
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.h54
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_regs.h8
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rpf.c96
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c77
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.h3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c46
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c60
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c15
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c274
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c96
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c16
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.c4
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.h2
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c10
-rw-r--r--drivers/media/platform/rockchip/rga/rga.h5
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c6
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.c14
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.h49
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c5
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c20
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c19
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c1043
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h30
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c14
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c53
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-core.c12
-rw-r--r--drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/common.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-capture.c8
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-core.c12
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c131
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.c9
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-param.h1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.c13
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-m2m.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.h5
-rw-r--r--drivers/media/platform/samsung/exynos4-is/mipi-csis.c12
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-capture.c14
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.c15
-rw-r--r--drivers/media/platform/samsung/s5p-g2d/g2d.c4
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c21
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c5
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h1
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c19
-rw-r--r--drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h4
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h1
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c29
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h1
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c2
-rw-r--r--drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c10
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-debug.c8
-rw-r--r--drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c28
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Kconfig1
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/Makefile7
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c24
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h2
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h5
-rw-r--r--drivers/media/platform/st/sti/delta/delta-debug.c8
-rw-r--r--drivers/media/platform/st/sti/delta/delta-v4l2.c6
-rw-r--r--drivers/media/platform/st/sti/hva/hva-hw.c3
-rw-r--r--drivers/media/platform/st/sti/hva/hva-v4l2.c4
-rw-r--r--drivers/media/platform/st/stm32/Kconfig14
-rw-r--r--drivers/media/platform/st/stm32/Makefile1
-rw-r--r--drivers/media/platform/st/stm32/dma2d/dma2d.c7
-rw-r--r--drivers/media/platform/st/stm32/stm32-csi.c1141
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c22
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/Makefile2
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c134
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c129
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h4
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c140
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c540
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c440
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c14
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c2
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c2
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c2
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c2
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig1
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c2
-rw-r--r--drivers/media/platform/sunxi/sun8i-di/sun8i-di.c4
-rw-r--r--drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c4
-rw-r--r--drivers/media/platform/synopsys/Kconfig3
-rw-r--r--drivers/media/platform/synopsys/Makefile2
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Kconfig36
-rw-r--r--drivers/media/platform/synopsys/hdmirx/Makefile4
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c2746
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h394
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c275
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h43
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.c18
-rw-r--r--drivers/media/platform/ti/cal/cal-camerarx.c271
-rw-r--r--drivers/media/platform/ti/cal/cal-video.c159
-rw-r--r--drivers/media/platform/ti/cal/cal.c59
-rw-r--r--drivers/media/platform/ti/cal/cal.h4
-rw-r--r--drivers/media/platform/ti/davinci/vpif.c6
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c22
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.c6
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c15
-rw-r--r--drivers/media/platform/ti/omap/omap_vout.c4
-rw-r--r--drivers/media/platform/ti/omap/omap_voutdef.h2
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.c58
-rw-r--r--drivers/media/platform/ti/omap3isp/isp.h2
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.c8
-rw-r--r--drivers/media/platform/ti/omap3isp/ispstat.c6
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c1
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.c1
-rw-r--r--drivers/media/platform/ti/vpe/vpe.c4
-rw-r--r--drivers/media/platform/verisilicon/Kconfig9
-rw-r--r--drivers/media/platform/verisilicon/Makefile14
-rw-r--r--drivers/media/platform/verisilicon/hantro.h9
-rw-r--r--drivers/media/platform/verisilicon/hantro_drv.c50
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c2
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2.c31
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c21
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_regs.h4
-rw-r--r--drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c8
-rw-r--r--drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c2
-rw-r--r--drivers/media/platform/verisilicon/hantro_h1_regs.h4
-rw-r--r--drivers/media/platform/verisilicon/hantro_hevc.c8
-rw-r--r--drivers/media/platform/verisilicon/hantro_hw.h38
-rw-r--r--drivers/media/platform/verisilicon/hantro_jpeg.c129
-rw-r--r--drivers/media/platform/verisilicon/hantro_postproc.c38
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.c49
-rw-r--r--drivers/media/platform/verisilicon/imx8m_vpu_hw.c10
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c2
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c2
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c19
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu981_regs.h10
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu_hw.c33
-rw-r--r--drivers/media/platform/via/via-camera.c4
-rw-r--r--drivers/media/platform/video-mux.c8
-rw-r--r--drivers/media/platform/xilinx/xilinx-csi2rxss.c2
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.c6
-rw-r--r--drivers/media/platform/xilinx/xilinx-dma.h2
-rw-r--r--drivers/media/platform/xilinx/xilinx-tpg.c18
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c11
-rw-r--r--drivers/media/platform/xilinx/xilinx-vtc.c2
453 files changed, 51178 insertions, 6981 deletions
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 91e54215de3a..9287faafdce5 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -67,8 +67,10 @@ 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/broadcom/Kconfig"
source "drivers/media/platform/cadence/Kconfig"
source "drivers/media/platform/chips-media/Kconfig"
+source "drivers/media/platform/imagination/Kconfig"
source "drivers/media/platform/intel/Kconfig"
source "drivers/media/platform/marvell/Kconfig"
source "drivers/media/platform/mediatek/Kconfig"
@@ -77,11 +79,13 @@ source "drivers/media/platform/nuvoton/Kconfig"
source "drivers/media/platform/nvidia/Kconfig"
source "drivers/media/platform/nxp/Kconfig"
source "drivers/media/platform/qcom/Kconfig"
+source "drivers/media/platform/raspberrypi/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/synopsys/Kconfig"
source "drivers/media/platform/ti/Kconfig"
source "drivers/media/platform/verisilicon/Kconfig"
source "drivers/media/platform/via/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 3296ec1ebe16..6fd7db0541c7 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -10,8 +10,10 @@ obj-y += amlogic/
obj-y += amphion/
obj-y += aspeed/
obj-y += atmel/
+obj-y += broadcom/
obj-y += cadence/
obj-y += chips-media/
+obj-y += imagination/
obj-y += intel/
obj-y += marvell/
obj-y += mediatek/
@@ -20,11 +22,13 @@ obj-y += nuvoton/
obj-y += nvidia/
obj-y += nxp/
obj-y += qcom/
+obj-y += raspberrypi/
obj-y += renesas/
obj-y += rockchip/
obj-y += samsung/
obj-y += st/
obj-y += sunxi/
+obj-y += synopsys/
obj-y += ti/
obj-y += verisilicon/
obj-y += via/
diff --git a/drivers/media/platform/allegro-dvt/allegro-core.c b/drivers/media/platform/allegro-dvt/allegro-core.c
index da61f9beb6b4..eb03df0d8652 100644
--- a/drivers/media/platform/allegro-dvt/allegro-core.c
+++ b/drivers/media/platform/allegro-dvt/allegro-core.c
@@ -179,7 +179,7 @@ struct allegro_dev {
struct list_head channels;
};
-static struct regmap_config allegro_regmap_config = {
+static const struct regmap_config allegro_regmap_config = {
.name = "regmap",
.reg_bits = 32,
.val_bits = 32,
@@ -188,7 +188,7 @@ static struct regmap_config allegro_regmap_config = {
.cache_type = REGCACHE_NONE,
};
-static struct regmap_config allegro_sram_config = {
+static const struct regmap_config allegro_sram_config = {
.name = "sram",
.reg_bits = 32,
.val_bits = 32,
@@ -1415,11 +1415,11 @@ static int allegro_mcu_send_encode_frame(struct allegro_dev *dev,
static int allegro_mcu_wait_for_init_timeout(struct allegro_dev *dev,
unsigned long timeout_ms)
{
- unsigned long tmo;
+ unsigned long time_left;
- tmo = wait_for_completion_timeout(&dev->init_complete,
- msecs_to_jiffies(timeout_ms));
- if (tmo == 0)
+ time_left = wait_for_completion_timeout(&dev->init_complete,
+ msecs_to_jiffies(timeout_ms));
+ if (time_left == 0)
return -ETIMEDOUT;
reinit_completion(&dev->init_complete);
@@ -1509,8 +1509,10 @@ static int allocate_buffers_internal(struct allegro_channel *channel,
INIT_LIST_HEAD(&buffer->head);
err = allegro_alloc_buffer(dev, buffer, size);
- if (err)
+ if (err) {
+ kfree(buffer);
goto err;
+ }
list_add(&buffer->head, list);
}
@@ -2481,14 +2483,14 @@ static void allegro_mcu_interrupt(struct allegro_dev *dev)
static void allegro_destroy_channel(struct allegro_channel *channel)
{
struct allegro_dev *dev = channel->dev;
- unsigned long timeout;
+ unsigned long time_left;
if (channel_exists(channel)) {
reinit_completion(&channel->completion);
allegro_mcu_send_destroy_channel(dev, channel);
- timeout = wait_for_completion_timeout(&channel->completion,
- msecs_to_jiffies(5000));
- if (timeout == 0)
+ time_left = wait_for_completion_timeout(&channel->completion,
+ msecs_to_jiffies(5000));
+ if (time_left == 0)
v4l2_warn(&dev->v4l2_dev,
"channel %d: timeout while destroying\n",
channel->mcu_channel_id);
@@ -2544,7 +2546,7 @@ static void allegro_destroy_channel(struct allegro_channel *channel)
static int allegro_create_channel(struct allegro_channel *channel)
{
struct allegro_dev *dev = channel->dev;
- unsigned long timeout;
+ unsigned long time_left;
if (channel_exists(channel)) {
v4l2_warn(&dev->v4l2_dev,
@@ -2595,9 +2597,9 @@ static int allegro_create_channel(struct allegro_channel *channel)
reinit_completion(&channel->completion);
allegro_mcu_send_create_channel(dev, channel);
- timeout = wait_for_completion_timeout(&channel->completion,
- msecs_to_jiffies(5000));
- if (timeout == 0)
+ time_left = wait_for_completion_timeout(&channel->completion,
+ msecs_to_jiffies(5000));
+ if (time_left == 0)
channel->error = -ETIMEDOUT;
if (channel->error)
goto err;
@@ -2895,8 +2897,6 @@ static const struct vb2_ops allegro_queue_ops = {
.buf_queue = allegro_buf_queue,
.start_streaming = allegro_start_streaming,
.stop_streaming = allegro_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int allegro_queue_init(void *priv,
@@ -3912,6 +3912,7 @@ static int allegro_probe(struct platform_device *pdev)
if (ret < 0) {
v4l2_err(&dev->v4l2_dev,
"failed to request firmware: %d\n", ret);
+ v4l2_device_unregister(&dev->v4l2_dev);
return ret;
}
@@ -4003,7 +4004,7 @@ static const struct dev_pm_ops allegro_pm_ops = {
static struct platform_driver allegro_driver = {
.probe = allegro_probe,
- .remove_new = allegro_remove,
+ .remove = allegro_remove,
.driver = {
.name = "allegro",
.of_match_table = allegro_dt_ids,
diff --git a/drivers/media/platform/allegro-dvt/nal-hevc.h b/drivers/media/platform/allegro-dvt/nal-hevc.h
index eb46f12aae80..361e2f55c254 100644
--- a/drivers/media/platform/allegro-dvt/nal-hevc.h
+++ b/drivers/media/platform/allegro-dvt/nal-hevc.h
@@ -96,10 +96,11 @@ struct nal_hevc_vps {
unsigned int extension_data_flag;
};
+#define N_HRD_PARAMS 1
struct nal_hevc_sub_layer_hrd_parameters {
- unsigned int bit_rate_value_minus1[1];
- unsigned int cpb_size_value_minus1[1];
- unsigned int cbr_flag[1];
+ unsigned int bit_rate_value_minus1[N_HRD_PARAMS];
+ unsigned int cpb_size_value_minus1[N_HRD_PARAMS];
+ unsigned int cbr_flag[N_HRD_PARAMS];
};
struct nal_hevc_hrd_parameters {
diff --git a/drivers/media/platform/amlogic/Kconfig b/drivers/media/platform/amlogic/Kconfig
index 5014957404e9..458acf3d5fa8 100644
--- a/drivers/media/platform/amlogic/Kconfig
+++ b/drivers/media/platform/amlogic/Kconfig
@@ -2,4 +2,5 @@
comment "Amlogic media platform drivers"
+source "drivers/media/platform/amlogic/c3/Kconfig"
source "drivers/media/platform/amlogic/meson-ge2d/Kconfig"
diff --git a/drivers/media/platform/amlogic/Makefile b/drivers/media/platform/amlogic/Makefile
index d3cdb8fa4ddb..c744afcd1b9e 100644
--- a/drivers/media/platform/amlogic/Makefile
+++ b/drivers/media/platform/amlogic/Makefile
@@ -1,2 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += c3/
obj-y += meson-ge2d/
diff --git a/drivers/media/platform/amlogic/c3/Kconfig b/drivers/media/platform/amlogic/c3/Kconfig
new file mode 100644
index 000000000000..d355d3a9358d
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/Kconfig
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/media/platform/amlogic/c3/isp/Kconfig"
+source "drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig"
+source "drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig"
diff --git a/drivers/media/platform/amlogic/c3/Makefile b/drivers/media/platform/amlogic/c3/Makefile
new file mode 100644
index 000000000000..14f305a493d2
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += isp/
+obj-y += mipi-adapter/
+obj-y += mipi-csi2/
diff --git a/drivers/media/platform/amlogic/c3/isp/Kconfig b/drivers/media/platform/amlogic/c3/isp/Kconfig
new file mode 100644
index 000000000000..02c62a50a5e8
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_C3_ISP
+ tristate "Amlogic C3 Image Signal Processor (ISP) driver"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on OF
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ help
+ Video4Linux2 driver for Amlogic C3 ISP pipeline.
+ The C3 ISP is used for processing raw images and
+ outputing results to memory.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amlogic/c3/isp/Makefile b/drivers/media/platform/amlogic/c3/isp/Makefile
new file mode 100644
index 000000000000..b1b064170b57
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+c3-isp-objs := c3-isp-dev.o \
+ c3-isp-params.o \
+ c3-isp-stats.o \
+ c3-isp-capture.o \
+ c3-isp-core.o \
+ c3-isp-resizer.o
+
+obj-$(CONFIG_VIDEO_C3_ISP) += c3-isp.o
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c
new file mode 100644
index 000000000000..11d85f5342f0
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-capture.c
@@ -0,0 +1,804 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/cleanup.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+#define C3_ISP_WRMIFX3_REG(addr, id) ((addr) + (id) * 0x100)
+
+static const struct c3_isp_cap_format_info cap_formats[] = {
+ /* YUV formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_Y_ONLY,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 2,
+ .vdiv = 2,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 2,
+ .vdiv = 2,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 1,
+ .vdiv = 2
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT,
+ .hdiv = 1,
+ .vdiv = 2,
+ },
+ /* RAW formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .format = ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW,
+ .planes = ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1,
+ .ch0_pix_bits = ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS,
+ .uv_swap = ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU,
+ .in_bits = ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT,
+ .hdiv = 1,
+ .vdiv = 1,
+ },
+};
+
+/* Hardware configuration */
+
+/* Set the address of wrmifx3(write memory interface) */
+static void c3_isp_cap_wrmifx3_buff(struct c3_isp_capture *cap)
+{
+ dma_addr_t y_dma_addr;
+ dma_addr_t uv_dma_addr;
+
+ if (cap->buff) {
+ y_dma_addr = cap->buff->dma_addr[C3_ISP_PLANE_Y];
+ uv_dma_addr = cap->buff->dma_addr[C3_ISP_PLANE_UV];
+ } else {
+ y_dma_addr = cap->dummy_buff.dma_addr;
+ uv_dma_addr = cap->dummy_buff.dma_addr;
+ }
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_BADDR, cap->id),
+ ISP_WRMIFX3_0_CH0_BASE_ADDR(y_dma_addr));
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_BADDR, cap->id),
+ ISP_WRMIFX3_0_CH1_BASE_ADDR(uv_dma_addr));
+}
+
+static void c3_isp_cap_wrmifx3_format(struct c3_isp_capture *cap)
+{
+ struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp;
+ const struct c3_isp_cap_format_info *info = cap->format.info;
+ u32 stride;
+ u32 chrom_h;
+ u32 chrom_v;
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_SIZE, cap->id),
+ ISP_WRMIFX3_0_FMT_SIZE_HSIZE(pix_mp->width) |
+ ISP_WRMIFX3_0_FMT_SIZE_VSIZE(pix_mp->height));
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_MASK, info->format);
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_MASK,
+ info->in_bits);
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_MASK, info->planes);
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_FMT_CTRL, cap->id),
+ ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_MASK,
+ info->uv_swap);
+
+ stride = DIV_ROUND_UP(pix_mp->plane_fmt[C3_ISP_PLANE_Y].bytesperline,
+ C3_ISP_DMA_SIZE_ALIGN_BYTES);
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_CTRL0, cap->id),
+ ISP_WRMIFX3_0_CH0_CTRL0_STRIDE_MASK,
+ ISP_WRMIFX3_0_CH0_CTRL0_STRIDE(stride));
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH0_CTRL1, cap->id),
+ ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_MODE_MASK,
+ info->ch0_pix_bits);
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_LUMA_H, cap->id),
+ ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND(pix_mp->width));
+
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_LUMA_V, cap->id),
+ ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND(pix_mp->height));
+
+ stride = DIV_ROUND_UP(pix_mp->plane_fmt[C3_ISP_PLANE_UV].bytesperline,
+ C3_ISP_DMA_SIZE_ALIGN_BYTES);
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_CTRL0, cap->id),
+ ISP_WRMIFX3_0_CH1_CTRL0_STRIDE_MASK,
+ ISP_WRMIFX3_0_CH1_CTRL0_STRIDE(stride));
+
+ c3_isp_update_bits(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_CH1_CTRL1, cap->id),
+ ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_MODE_MASK,
+ ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_16BITS);
+
+ chrom_h = DIV_ROUND_UP(pix_mp->width, info->hdiv);
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_CHROM_H, cap->id),
+ ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND(chrom_h));
+
+ chrom_v = DIV_ROUND_UP(pix_mp->height, info->vdiv);
+ c3_isp_write(cap->isp,
+ C3_ISP_WRMIFX3_REG(ISP_WRMIFX3_0_WIN_CHROM_V, cap->id),
+ ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND(chrom_v));
+}
+
+static int c3_isp_cap_dummy_buff_create(struct c3_isp_capture *cap)
+{
+ struct c3_isp_dummy_buffer *dummy_buff = &cap->dummy_buff;
+ struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp;
+
+ if (pix_mp->num_planes == 1)
+ dummy_buff->size = pix_mp->plane_fmt[C3_ISP_PLANE_Y].sizeimage;
+ else
+ dummy_buff->size =
+ max(pix_mp->plane_fmt[C3_ISP_PLANE_Y].sizeimage,
+ pix_mp->plane_fmt[C3_ISP_PLANE_UV].sizeimage);
+
+ /* The driver never access vaddr, no mapping is required */
+ dummy_buff->vaddr = dma_alloc_attrs(cap->isp->dev, dummy_buff->size,
+ &dummy_buff->dma_addr, GFP_KERNEL,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+ if (!dummy_buff->vaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void c3_isp_cap_dummy_buff_destroy(struct c3_isp_capture *cap)
+{
+ dma_free_attrs(cap->isp->dev, cap->dummy_buff.size,
+ cap->dummy_buff.vaddr, cap->dummy_buff.dma_addr,
+ DMA_ATTR_NO_KERNEL_MAPPING);
+}
+
+static void c3_isp_cap_cfg_buff(struct c3_isp_capture *cap)
+{
+ cap->buff = list_first_entry_or_null(&cap->pending,
+ struct c3_isp_cap_buffer, list);
+
+ c3_isp_cap_wrmifx3_buff(cap);
+
+ if (cap->buff)
+ list_del(&cap->buff->list);
+}
+
+static void c3_isp_cap_start(struct c3_isp_capture *cap)
+{
+ u32 mask;
+ u32 val;
+
+ scoped_guard(spinlock_irqsave, &cap->buff_lock)
+ c3_isp_cap_cfg_buff(cap);
+
+ c3_isp_cap_wrmifx3_format(cap);
+
+ if (cap->id == C3_ISP_CAP_DEV_0) {
+ mask = ISP_TOP_PATH_EN_WRMIF0_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF0_EN;
+ } else if (cap->id == C3_ISP_CAP_DEV_1) {
+ mask = ISP_TOP_PATH_EN_WRMIF1_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF1_EN;
+ } else {
+ mask = ISP_TOP_PATH_EN_WRMIF2_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF2_EN;
+ }
+
+ c3_isp_update_bits(cap->isp, ISP_TOP_PATH_EN, mask, val);
+}
+
+static void c3_isp_cap_stop(struct c3_isp_capture *cap)
+{
+ u32 mask;
+ u32 val;
+
+ if (cap->id == C3_ISP_CAP_DEV_0) {
+ mask = ISP_TOP_PATH_EN_WRMIF0_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF0_DIS;
+ } else if (cap->id == C3_ISP_CAP_DEV_1) {
+ mask = ISP_TOP_PATH_EN_WRMIF1_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF1_DIS;
+ } else {
+ mask = ISP_TOP_PATH_EN_WRMIF2_EN_MASK;
+ val = ISP_TOP_PATH_EN_WRMIF2_DIS;
+ }
+
+ c3_isp_update_bits(cap->isp, ISP_TOP_PATH_EN, mask, val);
+}
+
+static void c3_isp_cap_done(struct c3_isp_capture *cap)
+{
+ struct c3_isp_cap_buffer *buff = cap->buff;
+
+ guard(spinlock_irqsave)(&cap->buff_lock);
+
+ if (buff) {
+ buff->vb.sequence = cap->isp->frm_sequence;
+ buff->vb.vb2_buf.timestamp = ktime_get();
+ buff->vb.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ c3_isp_cap_cfg_buff(cap);
+}
+
+/* V4L2 video operations */
+
+static const struct c3_isp_cap_format_info *c3_cap_find_fmt(u32 fourcc)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(cap_formats); i++) {
+ if (cap_formats[i].fourcc == fourcc)
+ return &cap_formats[i];
+ }
+
+ return NULL;
+}
+
+static void c3_cap_try_fmt(struct v4l2_pix_format_mplane *pix_mp)
+{
+ const struct c3_isp_cap_format_info *fmt;
+ const struct v4l2_format_info *info;
+ struct v4l2_plane_pix_format *plane;
+
+ fmt = c3_cap_find_fmt(pix_mp->pixelformat);
+ if (!fmt)
+ fmt = &cap_formats[0];
+
+ pix_mp->width = clamp(pix_mp->width, C3_ISP_MIN_WIDTH,
+ C3_ISP_MAX_WIDTH);
+ pix_mp->height = clamp(pix_mp->height, C3_ISP_MIN_HEIGHT,
+ C3_ISP_MAX_HEIGHT);
+ pix_mp->pixelformat = fmt->fourcc;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ pix_mp->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ info = v4l2_format_info(fmt->fourcc);
+ pix_mp->num_planes = info->mem_planes;
+ memset(pix_mp->plane_fmt, 0, sizeof(pix_mp->plane_fmt));
+
+ for (unsigned int i = 0; i < info->comp_planes; i++) {
+ unsigned int hdiv = (i == 0) ? 1 : info->hdiv;
+ unsigned int vdiv = (i == 0) ? 1 : info->vdiv;
+
+ plane = &pix_mp->plane_fmt[i];
+
+ plane->bytesperline = DIV_ROUND_UP(pix_mp->width, hdiv) *
+ info->bpp[i] / info->bpp_div[i];
+ plane->bytesperline = ALIGN(plane->bytesperline,
+ C3_ISP_DMA_SIZE_ALIGN_BYTES);
+ plane->sizeimage = plane->bytesperline *
+ DIV_ROUND_UP(pix_mp->height, vdiv);
+ }
+}
+
+static void c3_isp_cap_return_buffers(struct c3_isp_capture *cap,
+ enum vb2_buffer_state state)
+{
+ struct c3_isp_cap_buffer *buff;
+
+ guard(spinlock_irqsave)(&cap->buff_lock);
+
+ if (cap->buff) {
+ vb2_buffer_done(&cap->buff->vb.vb2_buf, state);
+ cap->buff = NULL;
+ }
+
+ while (!list_empty(&cap->pending)) {
+ buff = list_first_entry(&cap->pending,
+ struct c3_isp_cap_buffer, list);
+ list_del(&buff->list);
+ vb2_buffer_done(&buff->vb.vb2_buf, state);
+ }
+}
+
+static int c3_isp_cap_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "AML C3 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static int c3_isp_cap_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ const struct c3_isp_cap_format_info *fmt;
+ unsigned int index = 0;
+ unsigned int i;
+
+ if (!f->mbus_code) {
+ if (f->index >= ARRAY_SIZE(cap_formats))
+ return -EINVAL;
+
+ fmt = &cap_formats[f->index];
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cap_formats); i++) {
+ fmt = &cap_formats[i];
+ if (f->mbus_code != fmt->mbus_code)
+ continue;
+
+ if (index++ == f->index) {
+ f->pixelformat = cap_formats[i].fourcc;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int c3_isp_cap_g_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_capture *cap = video_drvdata(file);
+
+ f->fmt.pix_mp = cap->format.pix_mp;
+
+ return 0;
+}
+
+static int c3_isp_cap_s_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_capture *cap = video_drvdata(file);
+
+ c3_cap_try_fmt(&f->fmt.pix_mp);
+
+ cap->format.pix_mp = f->fmt.pix_mp;
+ cap->format.info = c3_cap_find_fmt(f->fmt.pix_mp.pixelformat);
+
+ return 0;
+}
+
+static int c3_isp_cap_try_fmt_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ c3_cap_try_fmt(&f->fmt.pix_mp);
+
+ return 0;
+}
+
+static int c3_isp_cap_enum_frmsize(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ const struct c3_isp_cap_format_info *fmt;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fmt = c3_cap_find_fmt(fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = C3_ISP_MIN_WIDTH;
+ fsize->stepwise.min_height = C3_ISP_MIN_HEIGHT;
+ fsize->stepwise.max_width = C3_ISP_MAX_WIDTH;
+ fsize->stepwise.max_height = C3_ISP_MAX_HEIGHT;
+ fsize->stepwise.step_width = 2;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp_cap_v4l2_ioctl_ops = {
+ .vidioc_querycap = c3_isp_cap_querycap,
+ .vidioc_enum_fmt_vid_cap = c3_isp_cap_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = c3_isp_cap_g_fmt_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = c3_isp_cap_s_fmt_mplane,
+ .vidioc_try_fmt_vid_cap_mplane = c3_isp_cap_try_fmt_mplane,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_framesizes = c3_isp_cap_enum_frmsize,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations isp_cap_v4l2_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int c3_isp_cap_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(link->source->entity);
+ struct c3_isp_capture *cap = video_get_drvdata(vdev);
+ struct v4l2_subdev_format src_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = link->source->index,
+ };
+ int ret;
+
+ ret = v4l2_subdev_call_state_active(sd, pad, get_fmt, &src_fmt);
+ if (ret)
+ return ret;
+
+ if (src_fmt.format.width != cap->format.pix_mp.width ||
+ src_fmt.format.height != cap->format.pix_mp.height ||
+ src_fmt.format.code != cap->format.info->mbus_code) {
+ dev_err(cap->isp->dev,
+ "link %s: %u -> %s: %u not valid: 0x%04x/%ux%u not match 0x%04x/%ux%u\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index,
+ src_fmt.format.code, src_fmt.format.width,
+ src_fmt.format.height, cap->format.info->mbus_code,
+ cap->format.pix_mp.width, cap->format.pix_mp.height);
+
+ return -EPIPE;
+ }
+
+ return 0;
+}
+
+static const struct media_entity_operations isp_cap_entity_ops = {
+ .link_validate = c3_isp_cap_link_validate,
+};
+
+static int c3_isp_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(q);
+ const struct v4l2_pix_format_mplane *pix_mp = &cap->format.pix_mp;
+ unsigned int i;
+
+ if (*num_planes) {
+ if (*num_planes != pix_mp->num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < pix_mp->num_planes; i++)
+ if (sizes[i] < pix_mp->plane_fmt[i].sizeimage)
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *num_planes = pix_mp->num_planes;
+ for (i = 0; i < pix_mp->num_planes; i++)
+ sizes[i] = pix_mp->plane_fmt[i].sizeimage;
+
+ return 0;
+}
+
+static void c3_isp_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_cap_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_cap_buffer, vb);
+ struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue);
+
+ guard(spinlock_irqsave)(&cap->buff_lock);
+
+ list_add_tail(&buf->list, &cap->pending);
+}
+
+static int c3_isp_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size;
+
+ for (unsigned int i = 0; i < cap->format.pix_mp.num_planes; i++) {
+ size = cap->format.pix_mp.plane_fmt[i].sizeimage;
+ if (vb2_plane_size(vb, i) < size) {
+ dev_err(cap->isp->dev,
+ "User buffer too small (%ld < %lu)\n",
+ vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ return 0;
+}
+
+static int c3_isp_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_cap_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_cap_buffer, vb);
+
+ for (unsigned int i = 0; i < cap->format.pix_mp.num_planes; i++)
+ buf->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ return 0;
+}
+
+static int c3_isp_vb2_start_streaming(struct vb2_queue *q,
+ unsigned int count)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(q);
+ int ret;
+
+ ret = video_device_pipeline_start(&cap->vdev, &cap->isp->pipe);
+ if (ret) {
+ dev_err(cap->isp->dev,
+ "Failed to start cap%u pipeline: %d\n", cap->id, ret);
+ goto err_return_buffers;
+ }
+
+ ret = c3_isp_cap_dummy_buff_create(cap);
+ if (ret)
+ goto err_pipeline_stop;
+
+ ret = pm_runtime_resume_and_get(cap->isp->dev);
+ if (ret)
+ goto err_dummy_destroy;
+
+ c3_isp_cap_start(cap);
+
+ ret = v4l2_subdev_enable_streams(&cap->rsz->sd, C3_ISP_RSZ_PAD_SOURCE,
+ BIT(0));
+ if (ret)
+ goto err_pm_put;
+
+ return 0;
+
+err_pm_put:
+ pm_runtime_put(cap->isp->dev);
+err_dummy_destroy:
+ c3_isp_cap_dummy_buff_destroy(cap);
+err_pipeline_stop:
+ video_device_pipeline_stop(&cap->vdev);
+err_return_buffers:
+ c3_isp_cap_return_buffers(cap, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void c3_isp_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct c3_isp_capture *cap = vb2_get_drv_priv(q);
+
+ c3_isp_cap_stop(cap);
+
+ c3_isp_cap_return_buffers(cap, VB2_BUF_STATE_ERROR);
+
+ v4l2_subdev_disable_streams(&cap->rsz->sd, C3_ISP_RSZ_PAD_SOURCE,
+ BIT(0));
+
+ pm_runtime_put(cap->isp->dev);
+
+ c3_isp_cap_dummy_buff_destroy(cap);
+
+ video_device_pipeline_stop(&cap->vdev);
+}
+
+static const struct vb2_ops isp_video_vb2_ops = {
+ .queue_setup = c3_isp_vb2_queue_setup,
+ .buf_queue = c3_isp_vb2_buf_queue,
+ .buf_prepare = c3_isp_vb2_buf_prepare,
+ .buf_init = c3_isp_vb2_buf_init,
+ .start_streaming = c3_isp_vb2_start_streaming,
+ .stop_streaming = c3_isp_vb2_stop_streaming,
+};
+
+static int c3_isp_register_capture(struct c3_isp_capture *cap)
+{
+ struct video_device *vdev = &cap->vdev;
+ struct vb2_queue *vb2_q = &cap->vb2_q;
+ int ret;
+
+ snprintf(vdev->name, sizeof(vdev->name), "c3-isp-cap%u", cap->id);
+ vdev->fops = &isp_cap_v4l2_fops;
+ vdev->ioctl_ops = &isp_cap_v4l2_ioctl_ops;
+ vdev->v4l2_dev = &cap->isp->v4l2_dev;
+ vdev->entity.ops = &isp_cap_entity_ops;
+ vdev->lock = &cap->lock;
+ vdev->minor = -1;
+ vdev->queue = vb2_q;
+ vdev->release = video_device_release_empty;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ video_set_drvdata(vdev, cap);
+
+ vb2_q->drv_priv = cap;
+ vb2_q->mem_ops = &vb2_dma_contig_memops;
+ vb2_q->ops = &isp_video_vb2_ops;
+ vb2_q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ vb2_q->io_modes = VB2_DMABUF | VB2_MMAP;
+ vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2_q->buf_struct_size = sizeof(struct c3_isp_cap_buffer);
+ vb2_q->dev = cap->isp->dev;
+ vb2_q->lock = &cap->lock;
+
+ ret = vb2_queue_init(vb2_q);
+ if (ret)
+ goto err_destroy;
+
+ cap->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, &cap->pad);
+ if (ret)
+ goto err_queue_release;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(cap->isp->dev,
+ "Failed to register %s: %d\n", vdev->name, ret);
+ goto err_entity_cleanup;
+ }
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_queue_release:
+ vb2_queue_release(vb2_q);
+err_destroy:
+ mutex_destroy(&cap->lock);
+ return ret;
+}
+
+int c3_isp_captures_register(struct c3_isp_device *isp)
+{
+ int ret;
+ unsigned int i;
+ struct c3_isp_capture *cap;
+
+ for (i = C3_ISP_CAP_DEV_0; i < C3_ISP_NUM_CAP_DEVS; i++) {
+ cap = &isp->caps[i];
+ memset(cap, 0, sizeof(*cap));
+
+ cap->format.pix_mp.width = C3_ISP_DEFAULT_WIDTH;
+ cap->format.pix_mp.height = C3_ISP_DEFAULT_HEIGHT;
+ cap->format.pix_mp.field = V4L2_FIELD_NONE;
+ cap->format.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
+ cap->format.pix_mp.colorspace = V4L2_COLORSPACE_SRGB;
+ cap->format.info =
+ c3_cap_find_fmt(cap->format.pix_mp.pixelformat);
+
+ c3_cap_try_fmt(&cap->format.pix_mp);
+
+ cap->id = i;
+ cap->rsz = &isp->resizers[i];
+ cap->isp = isp;
+ INIT_LIST_HEAD(&cap->pending);
+ spin_lock_init(&cap->buff_lock);
+ mutex_init(&cap->lock);
+
+ ret = c3_isp_register_capture(cap);
+ if (ret) {
+ cap->isp = NULL;
+ mutex_destroy(&cap->lock);
+ c3_isp_captures_unregister(isp);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void c3_isp_captures_unregister(struct c3_isp_device *isp)
+{
+ unsigned int i;
+ struct c3_isp_capture *cap;
+
+ for (i = C3_ISP_CAP_DEV_0; i < C3_ISP_NUM_CAP_DEVS; i++) {
+ cap = &isp->caps[i];
+
+ if (!cap->isp)
+ continue;
+ vb2_queue_release(&cap->vb2_q);
+ media_entity_cleanup(&cap->vdev.entity);
+ video_unregister_device(&cap->vdev);
+ mutex_destroy(&cap->lock);
+ }
+}
+
+void c3_isp_captures_isr(struct c3_isp_device *isp)
+{
+ c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_0]);
+ c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_1]);
+ c3_isp_cap_done(&isp->caps[C3_ISP_CAP_DEV_2]);
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h b/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h
new file mode 100644
index 000000000000..cb470802e61e
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-common.h
@@ -0,0 +1,340 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef __C3_ISP_COMMON_H__
+#define __C3_ISP_COMMON_H__
+
+#include <linux/clk.h>
+
+#include <media/media-device.h>
+#include <media/videobuf2-core.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-v4l2.h>
+
+#define C3_ISP_DRIVER_NAME "c3-isp"
+#define C3_ISP_CLOCK_NUM_MAX 3
+
+#define C3_ISP_DEFAULT_WIDTH 1920
+#define C3_ISP_DEFAULT_HEIGHT 1080
+#define C3_ISP_MAX_WIDTH 2888
+#define C3_ISP_MAX_HEIGHT 2240
+#define C3_ISP_MIN_WIDTH 160
+#define C3_ISP_MIN_HEIGHT 120
+
+#define C3_ISP_DMA_SIZE_ALIGN_BYTES 16
+
+enum c3_isp_core_pads {
+ C3_ISP_CORE_PAD_SINK_VIDEO,
+ C3_ISP_CORE_PAD_SINK_PARAMS,
+ C3_ISP_CORE_PAD_SOURCE_STATS,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_0,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_1,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_2,
+ C3_ISP_CORE_PAD_MAX
+};
+
+enum c3_isp_resizer_ids {
+ C3_ISP_RSZ_0,
+ C3_ISP_RSZ_1,
+ C3_ISP_RSZ_2,
+ C3_ISP_NUM_RSZ
+};
+
+enum c3_isp_resizer_pads {
+ C3_ISP_RSZ_PAD_SINK,
+ C3_ISP_RSZ_PAD_SOURCE,
+ C3_ISP_RSZ_PAD_MAX
+};
+
+enum c3_isp_cap_devs {
+ C3_ISP_CAP_DEV_0,
+ C3_ISP_CAP_DEV_1,
+ C3_ISP_CAP_DEV_2,
+ C3_ISP_NUM_CAP_DEVS
+};
+
+enum c3_isp_planes {
+ C3_ISP_PLANE_Y,
+ C3_ISP_PLANE_UV,
+ C3_ISP_NUM_PLANES
+};
+
+/*
+ * struct c3_isp_cap_format_info - The image format of capture device
+ *
+ * @mbus_code: the mbus code
+ * @fourcc: the pixel format
+ * @format: defines the output format of hardware
+ * @planes: defines the mutil plane of hardware
+ * @ch0_pix_bits: defines the channel 0 pixel bits mode of hardware
+ * @uv_swap: defines the uv swap flag of hardware
+ * @in_bits: defines the input bits of hardware
+ * @hdiv: horizontal chroma subsampling factor of hardware
+ * @vdiv: vertical chroma subsampling factor of hardware
+ */
+struct c3_isp_cap_format_info {
+ u32 mbus_code;
+ u32 fourcc;
+ u32 format;
+ u32 planes;
+ u32 ch0_pix_bits;
+ u8 uv_swap;
+ u8 in_bits;
+ u8 hdiv;
+ u8 vdiv;
+};
+
+/*
+ * struct c3_isp_cap_buffer - A container of vb2 buffer used by the video
+ * devices: capture video devices
+ *
+ * @vb: vb2 buffer
+ * @dma_addr: buffer physical address
+ * @list: entry of the buffer in the queue
+ */
+struct c3_isp_cap_buffer {
+ struct vb2_v4l2_buffer vb;
+ dma_addr_t dma_addr[C3_ISP_NUM_PLANES];
+ struct list_head list;
+};
+
+/*
+ * struct c3_isp_stats_dma_buffer - A container of vb2 buffer used by the video
+ * devices: stats video devices
+ *
+ * @vb: vb2 buffer
+ * @dma_addr: buffer physical address
+ * @list: entry of the buffer in the queue
+ */
+struct c3_isp_stats_buffer {
+ struct vb2_v4l2_buffer vb;
+ dma_addr_t dma_addr;
+ struct list_head list;
+};
+
+/*
+ * struct c3_isp_params_buffer - A container of vb2 buffer used by the
+ * params video device
+ *
+ * @vb: vb2 buffer
+ * @cfg: scratch buffer used for caching the ISP configuration parameters
+ * @list: entry of the buffer in the queue
+ */
+struct c3_isp_params_buffer {
+ struct vb2_v4l2_buffer vb;
+ void *cfg;
+ struct list_head list;
+};
+
+/*
+ * struct c3_isp_dummy_buffer - A buffer to write the next frame to in case
+ * there are no vb2 buffers available.
+ *
+ * @vaddr: return value of call to dma_alloc_attrs
+ * @dma_addr: dma address of the buffer
+ * @size: size of the buffer
+ */
+struct c3_isp_dummy_buffer {
+ void *vaddr;
+ dma_addr_t dma_addr;
+ u32 size;
+};
+
+/*
+ * struct c3_isp_core - ISP core subdev
+ *
+ * @sd: ISP sub-device
+ * @pads: ISP sub-device pads
+ * @src_pad: source sub-device pad
+ * @isp: pointer to c3_isp_device
+ */
+struct c3_isp_core {
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_ISP_CORE_PAD_MAX];
+ struct media_pad *src_pad;
+ struct c3_isp_device *isp;
+};
+
+/*
+ * struct c3_isp_resizer - ISP resizer subdev
+ *
+ * @id: resizer id
+ * @sd: resizer sub-device
+ * @pads: resizer sub-device pads
+ * @src_sd: source sub-device
+ * @isp: pointer to c3_isp_device
+ * @src_pad: the pad of source sub-device
+ */
+struct c3_isp_resizer {
+ enum c3_isp_resizer_ids id;
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_ISP_RSZ_PAD_MAX];
+ struct v4l2_subdev *src_sd;
+ struct c3_isp_device *isp;
+ u32 src_pad;
+};
+
+/*
+ * struct c3_isp_stats - ISP statistics device
+ *
+ * @vb2_q: vb2 buffer queue
+ * @vdev: video node
+ * @vfmt: v4l2_format of the metadata format
+ * @pad: media pad
+ * @lock: protects vb2_q, vdev
+ * @isp: pointer to c3_isp_device
+ * @buff: in use buffer
+ * @buff_lock: protects stats buffer
+ * @pending: stats buffer list head
+ */
+struct c3_isp_stats {
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct v4l2_format vfmt;
+ struct media_pad pad;
+
+ struct mutex lock; /* Protects vb2_q, vdev */
+ struct c3_isp_device *isp;
+
+ struct c3_isp_stats_buffer *buff;
+ spinlock_t buff_lock; /* Protects stats buffer */
+ struct list_head pending;
+};
+
+/*
+ * struct c3_isp_params - ISP parameters device
+ *
+ * @vb2_q: vb2 buffer queue
+ * @vdev: video node
+ * @vfmt: v4l2_format of the metadata format
+ * @pad: media pad
+ * @lock: protects vb2_q, vdev
+ * @isp: pointer to c3_isp_device
+ * @buff: in use buffer
+ * @buff_lock: protects stats buffer
+ * @pending: stats buffer list head
+ */
+struct c3_isp_params {
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct v4l2_format vfmt;
+ struct media_pad pad;
+
+ struct mutex lock; /* Protects vb2_q, vdev */
+ struct c3_isp_device *isp;
+
+ struct c3_isp_params_buffer *buff;
+ spinlock_t buff_lock; /* Protects params buffer */
+ struct list_head pending;
+};
+
+/*
+ * struct c3_isp_capture - ISP capture device
+ *
+ * @id: capture device ID
+ * @vb2_q: vb2 buffer queue
+ * @vdev: video node
+ * @pad: media pad
+ * @lock: protects vb2_q, vdev
+ * @isp: pointer to c3_isp_device
+ * @rsz: pointer to c3_isp_resizer
+ * @buff: in use buffer
+ * @buff_lock: protects capture buffer
+ * @pending: capture buffer list head
+ * @format.info: a pointer to the c3_isp_capture_format of the pixel format
+ * @format.fmt: buffer format
+ */
+struct c3_isp_capture {
+ enum c3_isp_cap_devs id;
+ struct vb2_queue vb2_q;
+ struct video_device vdev;
+ struct media_pad pad;
+
+ struct mutex lock; /* Protects vb2_q, vdev */
+ struct c3_isp_device *isp;
+ struct c3_isp_resizer *rsz;
+
+ struct c3_isp_dummy_buffer dummy_buff;
+ struct c3_isp_cap_buffer *buff;
+ spinlock_t buff_lock; /* Protects stream buffer */
+ struct list_head pending;
+ struct {
+ const struct c3_isp_cap_format_info *info;
+ struct v4l2_pix_format_mplane pix_mp;
+ } format;
+};
+
+/**
+ * struct c3_isp_info - ISP information
+ *
+ * @clocks: array of ISP clock names
+ * @clock_num: actual clock number
+ */
+struct c3_isp_info {
+ char *clocks[C3_ISP_CLOCK_NUM_MAX];
+ u32 clock_num;
+};
+
+/**
+ * struct c3_isp_device - ISP platform device
+ *
+ * @dev: pointer to the struct device
+ * @base: base register address
+ * @clks: array of clocks
+ * @notifier: notifier to register on the v4l2-async API
+ * @v4l2_dev: v4l2_device variable
+ * @media_dev: media device variable
+ * @pipe: media pipeline
+ * @core: ISP core subdev
+ * @resizers: ISP resizer subdev
+ * @stats: ISP stats device
+ * @params: ISP params device
+ * @caps: array of ISP capture device
+ * @frm_sequence: used to record frame id
+ * @info: version-specific ISP information
+ */
+struct c3_isp_device {
+ struct device *dev;
+ void __iomem *base;
+ struct clk_bulk_data clks[C3_ISP_CLOCK_NUM_MAX];
+
+ struct v4l2_async_notifier notifier;
+ struct v4l2_device v4l2_dev;
+ struct media_device media_dev;
+ struct media_pipeline pipe;
+
+ struct c3_isp_core core;
+ struct c3_isp_resizer resizers[C3_ISP_NUM_RSZ];
+ struct c3_isp_stats stats;
+ struct c3_isp_params params;
+ struct c3_isp_capture caps[C3_ISP_NUM_CAP_DEVS];
+
+ u32 frm_sequence;
+ const struct c3_isp_info *info;
+};
+
+u32 c3_isp_read(struct c3_isp_device *isp, u32 reg);
+void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val);
+void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val);
+
+void c3_isp_core_queue_sof(struct c3_isp_device *isp);
+int c3_isp_core_register(struct c3_isp_device *isp);
+void c3_isp_core_unregister(struct c3_isp_device *isp);
+int c3_isp_resizers_register(struct c3_isp_device *isp);
+void c3_isp_resizers_unregister(struct c3_isp_device *isp);
+int c3_isp_captures_register(struct c3_isp_device *isp);
+void c3_isp_captures_unregister(struct c3_isp_device *isp);
+void c3_isp_captures_isr(struct c3_isp_device *isp);
+void c3_isp_stats_pre_cfg(struct c3_isp_device *isp);
+int c3_isp_stats_register(struct c3_isp_device *isp);
+void c3_isp_stats_unregister(struct c3_isp_device *isp);
+void c3_isp_stats_isr(struct c3_isp_device *isp);
+void c3_isp_params_pre_cfg(struct c3_isp_device *isp);
+int c3_isp_params_register(struct c3_isp_device *isp);
+void c3_isp_params_unregister(struct c3_isp_device *isp);
+void c3_isp_params_isr(struct c3_isp_device *isp);
+
+#endif
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c
new file mode 100644
index 000000000000..ff6413fff889
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/media/amlogic/c3-isp-config.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-event.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+#define C3_ISP_CORE_SUBDEV_NAME "c3-isp-core"
+
+#define C3_ISP_PHASE_OFFSET_0 0
+#define C3_ISP_PHASE_OFFSET_1 1
+#define C3_ISP_PHASE_OFFSET_NONE 0xff
+
+#define C3_ISP_CORE_DEF_SINK_PAD_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+#define C3_ISP_CORE_DEF_SRC_PAD_FMT MEDIA_BUS_FMT_YUV10_1X30
+
+/*
+ * struct c3_isp_core_format_info - ISP core format information
+ *
+ * @mbus_code: the mbus code
+ * @pads: bitmask detailing valid pads for this mbus_code
+ * @xofst: horizontal phase offset of hardware
+ * @yofst: vertical phase offset of hardware
+ * @is_raw: the raw format flag of mbus code
+ */
+struct c3_isp_core_format_info {
+ u32 mbus_code;
+ u32 pads;
+ u8 xofst;
+ u8 yofst;
+ bool is_raw;
+};
+
+static const struct c3_isp_core_format_info c3_isp_core_fmts[] = {
+ /* RAW formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_1,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_0,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .pads = BIT(C3_ISP_CORE_PAD_SINK_VIDEO),
+ .xofst = C3_ISP_PHASE_OFFSET_1,
+ .yofst = C3_ISP_PHASE_OFFSET_0,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1)
+ | BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = true,
+ },
+ /* YUV formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .pads = BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_0) |
+ BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_1) |
+ BIT(C3_ISP_CORE_PAD_SOURCE_VIDEO_2),
+ .xofst = C3_ISP_PHASE_OFFSET_NONE,
+ .yofst = C3_ISP_PHASE_OFFSET_NONE,
+ .is_raw = false,
+ },
+};
+
+static const struct c3_isp_core_format_info
+*core_find_format_by_code(u32 code, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) {
+ const struct c3_isp_core_format_info *info =
+ &c3_isp_core_fmts[i];
+
+ if (info->mbus_code == code && info->pads & BIT(pad))
+ return info;
+ }
+
+ return NULL;
+}
+
+static const struct c3_isp_core_format_info
+*core_find_format_by_index(u32 index, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_core_fmts); i++) {
+ const struct c3_isp_core_format_info *info =
+ &c3_isp_core_fmts[i];
+
+ if (!(info->pads & BIT(pad)))
+ continue;
+
+ if (!index)
+ return info;
+
+ index--;
+ }
+
+ return NULL;
+}
+
+static void c3_isp_core_enable(struct c3_isp_device *isp)
+{
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK,
+ ISP_TOP_IRQ_EN_FRM_END_EN);
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK,
+ ISP_TOP_IRQ_EN_FRM_RST_EN);
+
+ /* Enable image data to ISP core */
+ c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK,
+ ISP_TOP_PATH_SEL_CORE_MIPI_CORE);
+}
+
+static void c3_isp_core_disable(struct c3_isp_device *isp)
+{
+ /* Disable image data to ISP core */
+ c3_isp_update_bits(isp, ISP_TOP_PATH_SEL, ISP_TOP_PATH_SEL_CORE_MASK,
+ ISP_TOP_PATH_SEL_CORE_CORE_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_END_MASK,
+ ISP_TOP_IRQ_EN_FRM_END_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_IRQ_EN, ISP_TOP_IRQ_EN_FRM_RST_MASK,
+ ISP_TOP_IRQ_EN_FRM_RST_DIS);
+}
+
+/* Set the phase offset of blc, wb and lns */
+static void c3_isp_core_lswb_ofst(struct c3_isp_device *isp,
+ u8 xofst, u8 yofst)
+{
+ c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST,
+ ISP_LSWB_BLC_PHSOFST_HORIZ_OFST_MASK,
+ ISP_LSWB_BLC_PHSOFST_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_LSWB_BLC_PHSOFST,
+ ISP_LSWB_BLC_PHSOFST_VERT_OFST_MASK,
+ ISP_LSWB_BLC_PHSOFST_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST,
+ ISP_LSWB_WB_PHSOFST_HORIZ_OFST_MASK,
+ ISP_LSWB_WB_PHSOFST_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_PHSOFST,
+ ISP_LSWB_WB_PHSOFST_VERT_OFST_MASK,
+ ISP_LSWB_WB_PHSOFST_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST,
+ ISP_LSWB_LNS_PHSOFST_HORIZ_OFST_MASK,
+ ISP_LSWB_LNS_PHSOFST_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_LSWB_LNS_PHSOFST,
+ ISP_LSWB_LNS_PHSOFST_VERT_OFST_MASK,
+ ISP_LSWB_LNS_PHSOFST_VERT_OFST(yofst));
+}
+
+/* Set the phase offset of af, ae and awb */
+static void c3_isp_core_3a_ofst(struct c3_isp_device *isp,
+ u8 xofst, u8 yofst)
+{
+ c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_HORIZ_OFST_MASK,
+ ISP_AF_CTRL_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_AF_CTRL, ISP_AF_CTRL_VERT_OFST_MASK,
+ ISP_AF_CTRL_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_HORIZ_OFST_MASK,
+ ISP_AE_CTRL_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_AE_CTRL, ISP_AE_CTRL_VERT_OFST_MASK,
+ ISP_AE_CTRL_VERT_OFST(yofst));
+
+ c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_HORIZ_OFST_MASK,
+ ISP_AWB_CTRL_HORIZ_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_AWB_CTRL, ISP_AWB_CTRL_VERT_OFST_MASK,
+ ISP_AWB_CTRL_VERT_OFST(yofst));
+}
+
+/* Set the phase offset of demosaic */
+static void c3_isp_core_dms_ofst(struct c3_isp_device *isp,
+ u8 xofst, u8 yofst)
+{
+ c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0,
+ ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST_MASK,
+ ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST(xofst));
+ c3_isp_update_bits(isp, ISP_DMS_COMMON_PARAM0,
+ ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST_MASK,
+ ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST(yofst));
+}
+
+static void c3_isp_core_cfg_format(struct c3_isp_device *isp,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ const struct c3_isp_core_format_info *isp_fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO);
+ isp_fmt = core_find_format_by_code(fmt->code,
+ C3_ISP_CORE_PAD_SINK_VIDEO);
+
+ c3_isp_write(isp, ISP_TOP_INPUT_SIZE,
+ ISP_TOP_INPUT_SIZE_HORIZ_SIZE(fmt->width) |
+ ISP_TOP_INPUT_SIZE_VERT_SIZE(fmt->height));
+ c3_isp_write(isp, ISP_TOP_FRM_SIZE,
+ ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE(fmt->width) |
+ ISP_TOP_FRM_SIZE_CORE_VERT_SIZE(fmt->height));
+
+ c3_isp_update_bits(isp, ISP_TOP_HOLD_SIZE,
+ ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE_MASK,
+ ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE(fmt->width));
+
+ c3_isp_write(isp, ISP_AF_HV_SIZE,
+ ISP_AF_HV_SIZE_GLB_WIN_XSIZE(fmt->width) |
+ ISP_AF_HV_SIZE_GLB_WIN_YSIZE(fmt->height));
+ c3_isp_write(isp, ISP_AE_HV_SIZE,
+ ISP_AE_HV_SIZE_HORIZ_SIZE(fmt->width) |
+ ISP_AE_HV_SIZE_VERT_SIZE(fmt->height));
+ c3_isp_write(isp, ISP_AWB_HV_SIZE,
+ ISP_AWB_HV_SIZE_HORIZ_SIZE(fmt->width) |
+ ISP_AWB_HV_SIZE_VERT_SIZE(fmt->height));
+
+ c3_isp_core_lswb_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
+ c3_isp_core_3a_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
+ c3_isp_core_dms_ofst(isp, isp_fmt->xofst, isp_fmt->yofst);
+}
+
+static bool c3_isp_core_streams_ready(struct c3_isp_core *core)
+{
+ unsigned int n_links = 0;
+ struct media_link *link;
+
+ for_each_media_entity_data_link(&core->sd.entity, link) {
+ if ((link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 ||
+ link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 ||
+ link->source->index == C3_ISP_CORE_PAD_SOURCE_VIDEO_2) &&
+ link->flags == MEDIA_LNK_FL_ENABLED)
+ n_links++;
+ }
+
+ return n_links == core->isp->pipe.start_count;
+}
+
+static int c3_isp_core_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_core *core = v4l2_get_subdevdata(sd);
+ struct media_pad *sink_pad;
+ struct v4l2_subdev *src_sd;
+ int ret;
+
+ if (!c3_isp_core_streams_ready(core))
+ return 0;
+
+ core->isp->frm_sequence = 0;
+ c3_isp_core_cfg_format(core->isp, state);
+ c3_isp_core_enable(core->isp);
+
+ sink_pad = &core->pads[C3_ISP_CORE_PAD_SINK_VIDEO];
+ core->src_pad = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(core->src_pad)) {
+ dev_dbg(core->isp->dev,
+ "Failed to get source pad for ISP core\n");
+ return -EPIPE;
+ }
+
+ src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity);
+
+ ret = v4l2_subdev_enable_streams(src_sd, core->src_pad->index, BIT(0));
+ if (ret) {
+ c3_isp_core_disable(core->isp);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int c3_isp_core_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_core *core = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd;
+
+ if (core->isp->pipe.start_count != 1)
+ return 0;
+
+ if (core->src_pad) {
+ src_sd = media_entity_to_v4l2_subdev(core->src_pad->entity);
+ v4l2_subdev_disable_streams(src_sd, core->src_pad->index,
+ BIT(0));
+ }
+ core->src_pad = NULL;
+
+ c3_isp_core_disable(core->isp);
+
+ return 0;
+}
+
+static int c3_isp_core_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct c3_isp_core_format_info *info;
+
+ switch (code->pad) {
+ case C3_ISP_CORE_PAD_SINK_VIDEO:
+ case C3_ISP_CORE_PAD_SOURCE_VIDEO_0:
+ case C3_ISP_CORE_PAD_SOURCE_VIDEO_1:
+ case C3_ISP_CORE_PAD_SOURCE_VIDEO_2:
+ info = core_find_format_by_index(code->index, code->pad);
+ if (!info)
+ return -EINVAL;
+
+ code->code = info->mbus_code;
+
+ break;
+ case C3_ISP_CORE_PAD_SINK_PARAMS:
+ case C3_ISP_CORE_PAD_SOURCE_STATS:
+ if (code->index)
+ return -EINVAL;
+
+ code->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void c3_isp_core_set_sink_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ const struct c3_isp_core_format_info *isp_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+ isp_fmt = core_find_format_by_code(format->format.code, format->pad);
+ if (!isp_fmt)
+ sink_fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT;
+ else
+ sink_fmt->code = format->format.code;
+
+ sink_fmt->width = clamp_t(u32, format->format.width,
+ C3_ISP_MIN_WIDTH, C3_ISP_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, format->format.height,
+ C3_ISP_MIN_HEIGHT, C3_ISP_MAX_HEIGHT);
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0;
+ i < C3_ISP_CORE_PAD_MAX; i++) {
+ src_fmt = v4l2_subdev_state_get_format(state, i);
+
+ src_fmt->width = sink_fmt->width;
+ src_fmt->height = sink_fmt->height;
+ }
+
+ format->format = *sink_fmt;
+}
+
+static void c3_isp_core_set_source_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ const struct c3_isp_core_format_info *isp_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_mbus_framefmt *sink_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state,
+ C3_ISP_CORE_PAD_SINK_VIDEO);
+ src_fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+ isp_fmt = core_find_format_by_code(format->format.code, format->pad);
+ if (!isp_fmt)
+ src_fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT;
+ else
+ src_fmt->code = format->format.code;
+
+ src_fmt->width = sink_fmt->width;
+ src_fmt->height = sink_fmt->height;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ if (isp_fmt && isp_fmt->is_raw) {
+ src_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ } else {
+ src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ format->format = *src_fmt;
+}
+
+static int c3_isp_core_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ if (format->pad == C3_ISP_CORE_PAD_SINK_VIDEO)
+ c3_isp_core_set_sink_fmt(state, format);
+ else if (format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_0 ||
+ format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_1 ||
+ format->pad == C3_ISP_CORE_PAD_SOURCE_VIDEO_2)
+ c3_isp_core_set_source_fmt(state, format);
+ else
+ format->format =
+ *v4l2_subdev_state_get_format(state, format->pad);
+
+ return 0;
+}
+
+static int c3_isp_core_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ /* Video sink pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_VIDEO);
+ fmt->width = C3_ISP_DEFAULT_WIDTH;
+ fmt->height = C3_ISP_DEFAULT_HEIGHT;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_CORE_DEF_SINK_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ /* Video source pad */
+ for (unsigned int i = C3_ISP_CORE_PAD_SOURCE_VIDEO_0;
+ i < C3_ISP_CORE_PAD_MAX; i++) {
+ fmt = v4l2_subdev_state_get_format(state, i);
+ fmt->width = C3_ISP_DEFAULT_WIDTH;
+ fmt->height = C3_ISP_DEFAULT_HEIGHT;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_CORE_DEF_SRC_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ /* Parameters pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SINK_PARAMS);
+ fmt->width = 0;
+ fmt->height = 0;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ /* Statistics pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_CORE_PAD_SOURCE_STATS);
+ fmt->width = 0;
+ fmt->height = 0;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = MEDIA_BUS_FMT_METADATA_FIXED;
+
+ return 0;
+}
+
+static int c3_isp_core_subscribe_event(struct v4l2_subdev *sd,
+ struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* V4L2_EVENT_FRAME_SYNC doesn't need id, so should set 0 */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
+static const struct v4l2_subdev_pad_ops c3_isp_core_pad_ops = {
+ .enum_mbus_code = c3_isp_core_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_isp_core_set_fmt,
+ .enable_streams = c3_isp_core_enable_streams,
+ .disable_streams = c3_isp_core_disable_streams,
+};
+
+static const struct v4l2_subdev_core_ops c3_isp_core_core_ops = {
+ .subscribe_event = c3_isp_core_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_ops c3_isp_core_subdev_ops = {
+ .core = &c3_isp_core_core_ops,
+ .pad = &c3_isp_core_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_isp_core_internal_ops = {
+ .init_state = c3_isp_core_init_state,
+};
+
+static int c3_isp_core_link_validate(struct media_link *link)
+{
+ if (link->sink->index == C3_ISP_CORE_PAD_SINK_PARAMS)
+ return 0;
+
+ return v4l2_subdev_link_validate(link);
+}
+
+/* Media entity operations */
+static const struct media_entity_operations c3_isp_core_entity_ops = {
+ .link_validate = c3_isp_core_link_validate,
+};
+
+void c3_isp_core_queue_sof(struct c3_isp_device *isp)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+
+ event.u.frame_sync.frame_sequence = isp->frm_sequence;
+ v4l2_event_queue(isp->core.sd.devnode, &event);
+}
+
+int c3_isp_core_register(struct c3_isp_device *isp)
+{
+ struct c3_isp_core *core = &isp->core;
+ struct v4l2_subdev *sd = &core->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_isp_core_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ sd->internal_ops = &c3_isp_core_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "%s", C3_ISP_CORE_SUBDEV_NAME);
+
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->entity.ops = &c3_isp_core_entity_ops;
+
+ core->isp = isp;
+ sd->dev = isp->dev;
+ v4l2_set_subdevdata(sd, core);
+
+ core->pads[C3_ISP_CORE_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK;
+ core->pads[C3_ISP_CORE_PAD_SINK_PARAMS].flags = MEDIA_PAD_FL_SINK;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_STATS].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_0].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_1].flags = MEDIA_PAD_FL_SOURCE;
+ core->pads[C3_ISP_CORE_PAD_SOURCE_VIDEO_2].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_ISP_CORE_PAD_MAX,
+ core->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(&isp->v4l2_dev, sd);
+ if (ret)
+ goto err_subdev_cleanup;
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+void c3_isp_core_unregister(struct c3_isp_device *isp)
+{
+ struct c3_isp_core *core = &isp->core;
+ struct v4l2_subdev *sd = &core->sd;
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c
new file mode 100644
index 000000000000..c3b779f63088
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-dev.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+u32 c3_isp_read(struct c3_isp_device *isp, u32 reg)
+{
+ return readl(isp->base + reg);
+}
+
+void c3_isp_write(struct c3_isp_device *isp, u32 reg, u32 val)
+{
+ writel(val, isp->base + reg);
+}
+
+void c3_isp_update_bits(struct c3_isp_device *isp, u32 reg, u32 mask, u32 val)
+{
+ u32 orig, tmp;
+
+ orig = c3_isp_read(isp, reg);
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ if (tmp != orig)
+ c3_isp_write(isp, reg, tmp);
+}
+
+/* PM runtime suspend */
+static int c3_isp_runtime_suspend(struct device *dev)
+{
+ struct c3_isp_device *isp = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(isp->info->clock_num, isp->clks);
+
+ return 0;
+}
+
+/* PM runtime resume */
+static int c3_isp_runtime_resume(struct device *dev)
+{
+ struct c3_isp_device *isp = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(isp->info->clock_num, isp->clks);
+}
+
+static const struct dev_pm_ops c3_isp_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ RUNTIME_PM_OPS(c3_isp_runtime_suspend,
+ c3_isp_runtime_resume, NULL)
+};
+
+/* IRQ handling */
+static irqreturn_t c3_isp_irq_handler(int irq, void *dev)
+{
+ struct c3_isp_device *isp = dev;
+ u32 status;
+
+ /* Get irq status and clear irq status */
+ status = c3_isp_read(isp, ISP_TOP_RO_IRQ_STAT);
+ c3_isp_write(isp, ISP_TOP_IRQ_CLR, status);
+
+ if (status & ISP_TOP_RO_IRQ_STAT_FRM_END_MASK) {
+ c3_isp_stats_isr(isp);
+ c3_isp_params_isr(isp);
+ c3_isp_captures_isr(isp);
+ isp->frm_sequence++;
+ }
+
+ if (status & ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK)
+ c3_isp_core_queue_sof(isp);
+
+ return IRQ_HANDLED;
+}
+
+/* Subdev notifier register */
+static int c3_isp_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct c3_isp_device *isp =
+ container_of(notifier, struct c3_isp_device, notifier);
+ struct media_pad *sink =
+ &isp->core.sd.entity.pads[C3_ISP_CORE_PAD_SINK_VIDEO];
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static int c3_isp_notify_complete(struct v4l2_async_notifier *notifier)
+{
+ struct c3_isp_device *isp =
+ container_of(notifier, struct c3_isp_device, notifier);
+
+ return v4l2_device_register_subdev_nodes(&isp->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations c3_isp_notify_ops = {
+ .bound = c3_isp_notify_bound,
+ .complete = c3_isp_notify_complete,
+};
+
+static int c3_isp_async_nf_register(struct c3_isp_device *isp)
+{
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_nf_init(&isp->notifier, &isp->v4l2_dev);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(isp->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ asc = v4l2_async_nf_add_fwnode_remote(&isp->notifier, ep,
+ struct v4l2_async_connection);
+ fwnode_handle_put(ep);
+
+ if (IS_ERR(asc))
+ return PTR_ERR(asc);
+
+ isp->notifier.ops = &c3_isp_notify_ops;
+ ret = v4l2_async_nf_register(&isp->notifier);
+ if (ret)
+ v4l2_async_nf_cleanup(&isp->notifier);
+
+ return ret;
+}
+
+static void c3_isp_async_nf_unregister(struct c3_isp_device *isp)
+{
+ v4l2_async_nf_unregister(&isp->notifier);
+ v4l2_async_nf_cleanup(&isp->notifier);
+}
+
+static int c3_isp_media_register(struct c3_isp_device *isp)
+{
+ struct media_device *media_dev = &isp->media_dev;
+ struct v4l2_device *v4l2_dev = &isp->v4l2_dev;
+ int ret;
+
+ /* Initialize media device */
+ strscpy(media_dev->model, C3_ISP_DRIVER_NAME, sizeof(media_dev->model));
+ media_dev->dev = isp->dev;
+
+ media_device_init(media_dev);
+
+ /* Initialize v4l2 device */
+ v4l2_dev->mdev = media_dev;
+ strscpy(v4l2_dev->name, C3_ISP_DRIVER_NAME, sizeof(v4l2_dev->name));
+
+ ret = v4l2_device_register(isp->dev, v4l2_dev);
+ if (ret)
+ goto err_media_dev_cleanup;
+
+ ret = media_device_register(&isp->media_dev);
+ if (ret) {
+ dev_err(isp->dev, "Failed to register media device: %d\n", ret);
+ goto err_unreg_v4l2_dev;
+ }
+
+ return 0;
+
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&isp->v4l2_dev);
+err_media_dev_cleanup:
+ media_device_cleanup(media_dev);
+ return ret;
+}
+
+static void c3_isp_media_unregister(struct c3_isp_device *isp)
+{
+ media_device_unregister(&isp->media_dev);
+ v4l2_device_unregister(&isp->v4l2_dev);
+ media_device_cleanup(&isp->media_dev);
+}
+
+static void c3_isp_remove_links(struct c3_isp_device *isp)
+{
+ unsigned int i;
+
+ media_entity_remove_links(&isp->core.sd.entity);
+
+ for (i = 0; i < C3_ISP_NUM_RSZ; i++)
+ media_entity_remove_links(&isp->resizers[i].sd.entity);
+
+ for (i = 0; i < C3_ISP_NUM_CAP_DEVS; i++)
+ media_entity_remove_links(&isp->caps[i].vdev.entity);
+}
+
+static int c3_isp_create_links(struct c3_isp_device *isp)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < C3_ISP_NUM_RSZ; i++) {
+ ret = media_create_pad_link(&isp->resizers[i].sd.entity,
+ C3_ISP_RSZ_PAD_SOURCE,
+ &isp->caps[i].vdev.entity, 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err(isp->dev,
+ "Failed to link rsz %u and cap %u\n", i, i);
+ goto err_remove_links;
+ }
+
+ ret = media_create_pad_link(&isp->core.sd.entity,
+ C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i,
+ &isp->resizers[i].sd.entity,
+ C3_ISP_RSZ_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(isp->dev,
+ "Failed to link core and rsz %u\n", i);
+ goto err_remove_links;
+ }
+ }
+
+ ret = media_create_pad_link(&isp->core.sd.entity,
+ C3_ISP_CORE_PAD_SOURCE_STATS,
+ &isp->stats.vdev.entity,
+ 0, MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(isp->dev, "Failed to link core and stats\n");
+ goto err_remove_links;
+ }
+
+ ret = media_create_pad_link(&isp->params.vdev.entity, 0,
+ &isp->core.sd.entity,
+ C3_ISP_CORE_PAD_SINK_PARAMS,
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ dev_err(isp->dev, "Failed to link params and core\n");
+ goto err_remove_links;
+ }
+
+ return 0;
+
+err_remove_links:
+ c3_isp_remove_links(isp);
+ return ret;
+}
+
+static int c3_isp_videos_register(struct c3_isp_device *isp)
+{
+ int ret;
+
+ ret = c3_isp_captures_register(isp);
+ if (ret)
+ return ret;
+
+ ret = c3_isp_stats_register(isp);
+ if (ret)
+ goto err_captures_unregister;
+
+ ret = c3_isp_params_register(isp);
+ if (ret)
+ goto err_stats_unregister;
+
+ ret = c3_isp_create_links(isp);
+ if (ret)
+ goto err_params_unregister;
+
+ return 0;
+
+err_params_unregister:
+ c3_isp_params_unregister(isp);
+err_stats_unregister:
+ c3_isp_stats_unregister(isp);
+err_captures_unregister:
+ c3_isp_captures_unregister(isp);
+ return ret;
+}
+
+static void c3_isp_videos_unregister(struct c3_isp_device *isp)
+{
+ c3_isp_remove_links(isp);
+ c3_isp_params_unregister(isp);
+ c3_isp_stats_unregister(isp);
+ c3_isp_captures_unregister(isp);
+}
+
+static int c3_isp_get_clocks(struct c3_isp_device *isp)
+{
+ const struct c3_isp_info *info = isp->info;
+
+ for (unsigned int i = 0; i < info->clock_num; i++)
+ isp->clks[i].id = info->clocks[i];
+
+ return devm_clk_bulk_get(isp->dev, info->clock_num, isp->clks);
+}
+
+static int c3_isp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct c3_isp_device *isp;
+ int irq;
+ int ret;
+
+ isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
+ if (!isp)
+ return -ENOMEM;
+
+ isp->info = of_device_get_match_data(dev);
+ isp->dev = dev;
+
+ isp->base = devm_platform_ioremap_resource_byname(pdev, "isp");
+ if (IS_ERR(isp->base))
+ return dev_err_probe(dev, PTR_ERR(isp->base),
+ "Failed to ioremap resource\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = c3_isp_get_clocks(isp);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ platform_set_drvdata(pdev, isp);
+
+ pm_runtime_enable(dev);
+
+ ret = c3_isp_media_register(isp);
+ if (ret)
+ goto err_runtime_disable;
+
+ ret = c3_isp_core_register(isp);
+ if (ret)
+ goto err_v4l2_unregister;
+
+ ret = c3_isp_resizers_register(isp);
+ if (ret)
+ goto err_core_unregister;
+
+ ret = c3_isp_async_nf_register(isp);
+ if (ret)
+ goto err_resizers_unregister;
+
+ ret = devm_request_irq(dev, irq,
+ c3_isp_irq_handler, IRQF_SHARED,
+ dev_driver_string(dev), isp);
+ if (ret)
+ goto err_nf_unregister;
+
+ ret = c3_isp_videos_register(isp);
+ if (ret)
+ goto err_nf_unregister;
+
+ return 0;
+
+err_nf_unregister:
+ c3_isp_async_nf_unregister(isp);
+err_resizers_unregister:
+ c3_isp_resizers_unregister(isp);
+err_core_unregister:
+ c3_isp_core_unregister(isp);
+err_v4l2_unregister:
+ c3_isp_media_unregister(isp);
+err_runtime_disable:
+ pm_runtime_disable(dev);
+ return ret;
+};
+
+static void c3_isp_remove(struct platform_device *pdev)
+{
+ struct c3_isp_device *isp = platform_get_drvdata(pdev);
+
+ c3_isp_videos_unregister(isp);
+ c3_isp_async_nf_unregister(isp);
+ c3_isp_core_unregister(isp);
+ c3_isp_resizers_unregister(isp);
+ c3_isp_media_unregister(isp);
+ pm_runtime_disable(isp->dev);
+};
+
+static const struct c3_isp_info isp_info = {
+ .clocks = {"vapb", "isp0"},
+ .clock_num = 2
+};
+
+static const struct of_device_id c3_isp_of_match[] = {
+ { .compatible = "amlogic,c3-isp",
+ .data = &isp_info },
+ { },
+};
+MODULE_DEVICE_TABLE(of, c3_isp_of_match);
+
+static struct platform_driver c3_isp_driver = {
+ .probe = c3_isp_probe,
+ .remove = c3_isp_remove,
+ .driver = {
+ .name = "c3-isp",
+ .of_match_table = c3_isp_of_match,
+ .pm = pm_ptr(&c3_isp_pm_ops),
+ },
+};
+
+module_platform_driver(c3_isp_driver);
+
+MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic C3 ISP pipeline");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
new file mode 100644
index 000000000000..c80667dd7662
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
@@ -0,0 +1,1008 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/cleanup.h>
+#include <linux/media/amlogic/c3-isp-config.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+/*
+ * union c3_isp_params_block - Generalisation of a parameter block
+ *
+ * This union allows the driver to treat a block as a generic struct to this
+ * union and safely access the header and block-specific struct without having
+ * to resort to casting. The header member is accessed first, and the type field
+ * checked which allows the driver to determine which of the other members
+ * should be used.
+ *
+ * @header: The shared header struct embedded as the first member
+ * of all the possible other members. This member would be
+ * accessed first and the type field checked to determine
+ * which of the other members should be accessed.
+ * @awb_gains: For header.type == C3_ISP_PARAMS_BLOCK_AWB_GAINS
+ * @awb_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AWB_CONFIG
+ * @ae_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AE_CONFIG
+ * @af_cfg: For header.type == C3_ISP_PARAMS_BLOCK_AF_CONFIG
+ * @pst_gamma: For header.type == C3_ISP_PARAMS_BLOCK_PST_GAMMA
+ * @ccm: For header.type == C3_ISP_PARAMS_BLOCK_CCM
+ * @csc: For header.type == C3_ISP_PARAMS_BLOCK_CSC
+ * @blc: For header.type == C3_ISP_PARAMS_BLOCK_BLC
+ */
+union c3_isp_params_block {
+ struct c3_isp_params_block_header header;
+ struct c3_isp_params_awb_gains awb_gains;
+ struct c3_isp_params_awb_config awb_cfg;
+ struct c3_isp_params_ae_config ae_cfg;
+ struct c3_isp_params_af_config af_cfg;
+ struct c3_isp_params_pst_gamma pst_gamma;
+ struct c3_isp_params_ccm ccm;
+ struct c3_isp_params_csc csc;
+ struct c3_isp_params_blc blc;
+};
+
+typedef void (*c3_isp_block_handler)(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block);
+
+struct c3_isp_params_handler {
+ size_t size;
+ c3_isp_block_handler handler;
+};
+
+#define to_c3_isp_params_buffer(vbuf) \
+ container_of(vbuf, struct c3_isp_params_buffer, vb)
+
+/* Hardware configuration */
+
+static void c3_isp_params_cfg_awb_gains(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_awb_gains *awb_gains = &block->awb_gains;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_WB_EN_MASK,
+ ISP_TOP_BEO_CTRL_WB_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0,
+ ISP_LSWB_WB_GAIN0_GR_GAIN_MASK,
+ ISP_LSWB_WB_GAIN0_GR_GAIN(awb_gains->gr_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN0,
+ ISP_LSWB_WB_GAIN0_R_GAIN_MASK,
+ ISP_LSWB_WB_GAIN0_R_GAIN(awb_gains->r_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1,
+ ISP_LSWB_WB_GAIN1_B_GAIN_MASK,
+ ISP_LSWB_WB_GAIN1_B_GAIN(awb_gains->b_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN1,
+ ISP_LSWB_WB_GAIN1_GB_GAIN_MASK,
+ ISP_LSWB_WB_GAIN1_GB_GAIN(awb_gains->gb_gain));
+ c3_isp_update_bits(isp, ISP_LSWB_WB_GAIN2,
+ ISP_LSWB_WB_GAIN2_IR_GAIN_MASK,
+ ISP_LSWB_WB_GAIN2_IR_GAIN(awb_gains->gb_gain));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_WB_EN_MASK,
+ ISP_TOP_BEO_CTRL_WB_EN);
+}
+
+static void c3_isp_params_awb_wt(struct c3_isp_device *isp,
+ const struct c3_isp_params_awb_config *cfg)
+{
+ unsigned int zones_num;
+ unsigned int base;
+ unsigned int data;
+ unsigned int i;
+
+ /* Set the weight address to 0 position */
+ c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0);
+
+ zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+
+ /* Need to write 8 weights at once */
+ for (i = 0; i < zones_num / 8; i++) {
+ base = i * 8;
+ data = ISP_AWB_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) |
+ ISP_AWB_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) |
+ ISP_AWB_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) |
+ ISP_AWB_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) |
+ ISP_AWB_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) |
+ ISP_AWB_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) |
+ ISP_AWB_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) |
+ ISP_AWB_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]);
+ c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data);
+ }
+
+ if (zones_num % 8 == 0)
+ return;
+
+ data = 0;
+ base = i * 8;
+
+ for (i = 0; i < zones_num % 8; i++)
+ data |= ISP_AWB_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]);
+
+ c3_isp_write(isp, ISP_AWB_BLK_WT_DATA, data);
+}
+
+static void c3_isp_params_awb_cood(struct c3_isp_device *isp,
+ const struct c3_isp_params_awb_config *cfg)
+{
+ unsigned int max_point_num;
+
+ /* The number of points is one more than the number of edges */
+ max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
+
+ /* Set the index address to 0 position */
+ c3_isp_write(isp, ISP_AWB_IDX_ADDR, 0);
+
+ for (unsigned int i = 0; i < max_point_num; i++)
+ c3_isp_write(isp, ISP_AWB_IDX_DATA,
+ ISP_AWB_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
+ ISP_AWB_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
+}
+
+static void c3_isp_params_cfg_awb_config(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_awb_config *awb_cfg = &block->awb_cfg;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_POINT_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_POINT(awb_cfg->tap_point));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_CTRL2,
+ ISP_AWB_STAT_CTRL2_SATUR_CTRL_MASK,
+ ISP_AWB_STAT_CTRL2_SATUR_CTRL(awb_cfg->satur_vald));
+
+ c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM,
+ ISP_AWB_HV_BLKNUM_H_NUM_MASK,
+ ISP_AWB_HV_BLKNUM_H_NUM(awb_cfg->horiz_zones_num));
+ c3_isp_update_bits(isp, ISP_AWB_HV_BLKNUM,
+ ISP_AWB_HV_BLKNUM_V_NUM_MASK,
+ ISP_AWB_HV_BLKNUM_V_NUM(awb_cfg->vert_zones_num));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MIN_VALUE_MASK,
+ ISP_AWB_STAT_RG_MIN_VALUE(awb_cfg->rg_min));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG, ISP_AWB_STAT_RG_MAX_VALUE_MASK,
+ ISP_AWB_STAT_RG_MAX_VALUE(awb_cfg->rg_max));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MIN_VALUE_MASK,
+ ISP_AWB_STAT_BG_MIN_VALUE(awb_cfg->bg_min));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG, ISP_AWB_STAT_BG_MAX_VALUE_MASK,
+ ISP_AWB_STAT_BG_MAX_VALUE(awb_cfg->bg_max));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL,
+ ISP_AWB_STAT_RG_HL_LOW_VALUE_MASK,
+ ISP_AWB_STAT_RG_HL_LOW_VALUE(awb_cfg->rg_low));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_RG_HL,
+ ISP_AWB_STAT_RG_HL_HIGH_VALUE_MASK,
+ ISP_AWB_STAT_RG_HL_HIGH_VALUE(awb_cfg->rg_high));
+
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL,
+ ISP_AWB_STAT_BG_HL_LOW_VALUE_MASK,
+ ISP_AWB_STAT_BG_HL_LOW_VALUE(awb_cfg->bg_low));
+ c3_isp_update_bits(isp, ISP_AWB_STAT_BG_HL,
+ ISP_AWB_STAT_BG_HL_HIGH_VALUE_MASK,
+ ISP_AWB_STAT_BG_HL_HIGH_VALUE(awb_cfg->bg_high));
+
+ c3_isp_params_awb_wt(isp, awb_cfg);
+ c3_isp_params_awb_cood(isp, awb_cfg);
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN);
+}
+
+static void c3_isp_params_ae_wt(struct c3_isp_device *isp,
+ const struct c3_isp_params_ae_config *cfg)
+{
+ unsigned int zones_num;
+ unsigned int base;
+ unsigned int data;
+ unsigned int i;
+
+ /* Set the weight address to 0 position */
+ c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0);
+
+ zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+
+ /* Need to write 8 weights at once */
+ for (i = 0; i < zones_num / 8; i++) {
+ base = i * 8;
+ data = ISP_AE_BLK_WT_DATA_WT(0, cfg->zone_weight[base + 0]) |
+ ISP_AE_BLK_WT_DATA_WT(1, cfg->zone_weight[base + 1]) |
+ ISP_AE_BLK_WT_DATA_WT(2, cfg->zone_weight[base + 2]) |
+ ISP_AE_BLK_WT_DATA_WT(3, cfg->zone_weight[base + 3]) |
+ ISP_AE_BLK_WT_DATA_WT(4, cfg->zone_weight[base + 4]) |
+ ISP_AE_BLK_WT_DATA_WT(5, cfg->zone_weight[base + 5]) |
+ ISP_AE_BLK_WT_DATA_WT(6, cfg->zone_weight[base + 6]) |
+ ISP_AE_BLK_WT_DATA_WT(7, cfg->zone_weight[base + 7]);
+ c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data);
+ }
+
+ if (zones_num % 8 == 0)
+ return;
+
+ data = 0;
+ base = i * 8;
+
+ /* Write the last weights data */
+ for (i = 0; i < zones_num % 8; i++)
+ data |= ISP_AE_BLK_WT_DATA_WT(i, cfg->zone_weight[base + i]);
+
+ c3_isp_write(isp, ISP_AE_BLK_WT_DATA, data);
+}
+
+static void c3_isp_params_ae_cood(struct c3_isp_device *isp,
+ const struct c3_isp_params_ae_config *cfg)
+{
+ unsigned int max_point_num;
+
+ /* The number of points is one more than the number of edges */
+ max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
+
+ /* Set the index address to 0 position */
+ c3_isp_write(isp, ISP_AE_IDX_ADDR, 0);
+
+ for (unsigned int i = 0; i < max_point_num; i++)
+ c3_isp_write(isp, ISP_AE_IDX_DATA,
+ ISP_AE_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
+ ISP_AE_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
+}
+
+static void c3_isp_params_cfg_ae_config(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_ae_config *ae_cfg = &block->ae_cfg;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_POINT_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_POINT(ae_cfg->tap_point));
+
+ if (ae_cfg->tap_point == C3_ISP_AE_STATS_TAP_GE)
+ c3_isp_update_bits(isp, ISP_AE_CTRL,
+ ISP_AE_CTRL_INPUT_2LINE_MASK,
+ ISP_AE_CTRL_INPUT_2LINE_EN);
+ else
+ c3_isp_update_bits(isp, ISP_AE_CTRL,
+ ISP_AE_CTRL_INPUT_2LINE_MASK,
+ ISP_AE_CTRL_INPUT_2LINE_DIS);
+
+ c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM,
+ ISP_AE_HV_BLKNUM_H_NUM_MASK,
+ ISP_AE_HV_BLKNUM_H_NUM(ae_cfg->horiz_zones_num));
+ c3_isp_update_bits(isp, ISP_AE_HV_BLKNUM,
+ ISP_AE_HV_BLKNUM_V_NUM_MASK,
+ ISP_AE_HV_BLKNUM_V_NUM(ae_cfg->vert_zones_num));
+
+ c3_isp_params_ae_wt(isp, ae_cfg);
+ c3_isp_params_ae_cood(isp, ae_cfg);
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN);
+}
+
+static void c3_isp_params_af_cood(struct c3_isp_device *isp,
+ const struct c3_isp_params_af_config *cfg)
+{
+ unsigned int max_point_num;
+
+ /* The number of points is one more than the number of edges */
+ max_point_num = max(cfg->horiz_zones_num, cfg->vert_zones_num) + 1;
+
+ /* Set the index address to 0 position */
+ c3_isp_write(isp, ISP_AF_IDX_ADDR, 0);
+
+ for (unsigned int i = 0; i < max_point_num; i++)
+ c3_isp_write(isp, ISP_AF_IDX_DATA,
+ ISP_AF_IDX_DATA_HIDX_DATA(cfg->horiz_coord[i]) |
+ ISP_AF_IDX_DATA_VIDX_DATA(cfg->vert_coord[i]));
+}
+
+static void c3_isp_params_cfg_af_config(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_af_config *af_cfg = &block->af_cfg;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_POINT_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_POINT(af_cfg->tap_point));
+
+ c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM,
+ ISP_AF_HV_BLKNUM_H_NUM_MASK,
+ ISP_AF_HV_BLKNUM_H_NUM(af_cfg->horiz_zones_num));
+ c3_isp_update_bits(isp, ISP_AF_HV_BLKNUM,
+ ISP_AF_HV_BLKNUM_V_NUM_MASK,
+ ISP_AF_HV_BLKNUM_V_NUM(af_cfg->vert_zones_num));
+
+ c3_isp_params_af_cood(isp, af_cfg);
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN);
+}
+
+static void c3_isp_params_cfg_pst_gamma(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_pst_gamma *gm = &block->pst_gamma;
+ unsigned int base;
+ unsigned int i;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK,
+ ISP_TOP_BED_CTRL_PST_GAMMA_DIS);
+ return;
+ }
+
+ /* R, G and B channels use the same gamma lut */
+ for (unsigned int j = 0; j < 3; j++) {
+ /* Set the channel lut address */
+ c3_isp_write(isp, ISP_PST_GAMMA_LUT_ADDR,
+ ISP_PST_GAMMA_LUT_ADDR_IDX_ADDR(j));
+
+ /* Need to write 2 lut values at once */
+ for (i = 0; i < ARRAY_SIZE(gm->lut) / 2; i++) {
+ base = i * 2;
+ c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA,
+ ISP_PST_GM_LUT_DATA0(gm->lut[base]) |
+ ISP_PST_GM_LUT_DATA1(gm->lut[base + 1]));
+ }
+
+ /* Write the last one */
+ if (ARRAY_SIZE(gm->lut) % 2) {
+ base = i * 2;
+ c3_isp_write(isp, ISP_PST_GAMMA_LUT_DATA,
+ ISP_PST_GM_LUT_DATA0(gm->lut[base]));
+ }
+ }
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK,
+ ISP_TOP_BED_CTRL_PST_GAMMA_EN);
+}
+
+/* Configure 3 x 3 ccm matrix */
+static void c3_isp_params_cfg_ccm(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_ccm *ccm = &block->ccm;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CCM_EN_MASK,
+ ISP_TOP_BED_CTRL_CCM_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_CCM_MTX_00_01,
+ ISP_CCM_MTX_00_01_MTX_00_MASK,
+ ISP_CCM_MTX_00_01_MTX_00(ccm->matrix[0][0]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_00_01,
+ ISP_CCM_MTX_00_01_MTX_01_MASK,
+ ISP_CCM_MTX_00_01_MTX_01(ccm->matrix[0][1]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_02_03,
+ ISP_CCM_MTX_02_03_MTX_02_MASK,
+ ISP_CCM_MTX_02_03_MTX_02(ccm->matrix[0][2]));
+
+ c3_isp_update_bits(isp, ISP_CCM_MTX_10_11,
+ ISP_CCM_MTX_10_11_MTX_10_MASK,
+ ISP_CCM_MTX_10_11_MTX_10(ccm->matrix[1][0]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_10_11,
+ ISP_CCM_MTX_10_11_MTX_11_MASK,
+ ISP_CCM_MTX_10_11_MTX_11(ccm->matrix[1][1]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_12_13,
+ ISP_CCM_MTX_12_13_MTX_12_MASK,
+ ISP_CCM_MTX_12_13_MTX_12(ccm->matrix[1][2]));
+
+ c3_isp_update_bits(isp, ISP_CCM_MTX_20_21,
+ ISP_CCM_MTX_20_21_MTX_20_MASK,
+ ISP_CCM_MTX_20_21_MTX_20(ccm->matrix[2][0]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_20_21,
+ ISP_CCM_MTX_20_21_MTX_21_MASK,
+ ISP_CCM_MTX_20_21_MTX_21(ccm->matrix[2][1]));
+ c3_isp_update_bits(isp, ISP_CCM_MTX_22_23_RS,
+ ISP_CCM_MTX_22_23_RS_MTX_22_MASK,
+ ISP_CCM_MTX_22_23_RS_MTX_22(ccm->matrix[2][2]));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CCM_EN_MASK,
+ ISP_TOP_BED_CTRL_CCM_EN);
+}
+
+/* Configure color space conversion matrix parameters */
+static void c3_isp_params_cfg_csc(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_csc *csc = &block->csc;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CM0_EN_MASK,
+ ISP_TOP_BED_CTRL_CM0_DIS);
+ return;
+ }
+
+ c3_isp_update_bits(isp, ISP_CM0_COEF00_01,
+ ISP_CM0_COEF00_01_MTX_00_MASK,
+ ISP_CM0_COEF00_01_MTX_00(csc->matrix[0][0]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF00_01,
+ ISP_CM0_COEF00_01_MTX_01_MASK,
+ ISP_CM0_COEF00_01_MTX_01(csc->matrix[0][1]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF02_10,
+ ISP_CM0_COEF02_10_MTX_02_MASK,
+ ISP_CM0_COEF02_10_MTX_02(csc->matrix[0][2]));
+
+ c3_isp_update_bits(isp, ISP_CM0_COEF02_10,
+ ISP_CM0_COEF02_10_MTX_10_MASK,
+ ISP_CM0_COEF02_10_MTX_10(csc->matrix[1][0]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF11_12,
+ ISP_CM0_COEF11_12_MTX_11_MASK,
+ ISP_CM0_COEF11_12_MTX_11(csc->matrix[1][1]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF11_12,
+ ISP_CM0_COEF11_12_MTX_12_MASK,
+ ISP_CM0_COEF11_12_MTX_12(csc->matrix[1][2]));
+
+ c3_isp_update_bits(isp, ISP_CM0_COEF20_21,
+ ISP_CM0_COEF20_21_MTX_20_MASK,
+ ISP_CM0_COEF20_21_MTX_20(csc->matrix[2][0]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF20_21,
+ ISP_CM0_COEF20_21_MTX_21_MASK,
+ ISP_CM0_COEF20_21_MTX_21(csc->matrix[2][1]));
+ c3_isp_update_bits(isp, ISP_CM0_COEF22_OUP_OFST0,
+ ISP_CM0_COEF22_OUP_OFST0_MTX_22_MASK,
+ ISP_CM0_COEF22_OUP_OFST0_MTX_22(csc->matrix[2][2]));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_CM0_EN_MASK,
+ ISP_TOP_BED_CTRL_CM0_EN);
+}
+
+/* Set blc offset of each color channel */
+static void c3_isp_params_cfg_blc(struct c3_isp_device *isp,
+ const union c3_isp_params_block *block)
+{
+ const struct c3_isp_params_blc *blc = &block->blc;
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_DISABLE) {
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_BLC_EN_MASK,
+ ISP_TOP_BEO_CTRL_BLC_DIS);
+ return;
+ }
+
+ c3_isp_write(isp, ISP_LSWB_BLC_OFST0,
+ ISP_LSWB_BLC_OFST0_R_OFST(blc->r_ofst) |
+ ISP_LSWB_BLC_OFST0_GR_OFST(blc->gr_ofst));
+ c3_isp_write(isp, ISP_LSWB_BLC_OFST1,
+ ISP_LSWB_BLC_OFST1_GB_OFST(blc->gb_ofst) |
+ ISP_LSWB_BLC_OFST1_B_OFST(blc->b_ofst));
+
+ if (block->header.flags & C3_ISP_PARAMS_BLOCK_FL_ENABLE)
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_BLC_EN_MASK,
+ ISP_TOP_BEO_CTRL_BLC_EN);
+}
+
+static const struct c3_isp_params_handler c3_isp_params_handlers[] = {
+ [C3_ISP_PARAMS_BLOCK_AWB_GAINS] = {
+ .size = sizeof(struct c3_isp_params_awb_gains),
+ .handler = c3_isp_params_cfg_awb_gains,
+ },
+ [C3_ISP_PARAMS_BLOCK_AWB_CONFIG] = {
+ .size = sizeof(struct c3_isp_params_awb_config),
+ .handler = c3_isp_params_cfg_awb_config,
+ },
+ [C3_ISP_PARAMS_BLOCK_AE_CONFIG] = {
+ .size = sizeof(struct c3_isp_params_ae_config),
+ .handler = c3_isp_params_cfg_ae_config,
+ },
+ [C3_ISP_PARAMS_BLOCK_AF_CONFIG] = {
+ .size = sizeof(struct c3_isp_params_af_config),
+ .handler = c3_isp_params_cfg_af_config,
+ },
+ [C3_ISP_PARAMS_BLOCK_PST_GAMMA] = {
+ .size = sizeof(struct c3_isp_params_pst_gamma),
+ .handler = c3_isp_params_cfg_pst_gamma,
+ },
+ [C3_ISP_PARAMS_BLOCK_CCM] = {
+ .size = sizeof(struct c3_isp_params_ccm),
+ .handler = c3_isp_params_cfg_ccm,
+ },
+ [C3_ISP_PARAMS_BLOCK_CSC] = {
+ .size = sizeof(struct c3_isp_params_csc),
+ .handler = c3_isp_params_cfg_csc,
+ },
+ [C3_ISP_PARAMS_BLOCK_BLC] = {
+ .size = sizeof(struct c3_isp_params_blc),
+ .handler = c3_isp_params_cfg_blc,
+ },
+};
+
+static void c3_isp_params_cfg_blocks(struct c3_isp_params *params)
+{
+ struct c3_isp_params_cfg *config = params->buff->cfg;
+ size_t block_offset = 0;
+
+ if (WARN_ON(!config))
+ return;
+
+ /* Walk the list of parameter blocks and process them */
+ while (block_offset < config->data_size) {
+ const struct c3_isp_params_handler *block_handler;
+ const union c3_isp_params_block *block;
+
+ block = (const union c3_isp_params_block *)
+ &config->data[block_offset];
+
+ block_handler = &c3_isp_params_handlers[block->header.type];
+ block_handler->handler(params->isp, block);
+
+ block_offset += block->header.size;
+ }
+}
+
+void c3_isp_params_pre_cfg(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+
+ /* Disable some unused modules */
+ c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL0,
+ ISP_TOP_FEO_CTRL0_INPUT_FMT_EN_MASK,
+ ISP_TOP_FEO_CTRL0_INPUT_FMT_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0,
+ ISP_TOP_FEO_CTRL1_0_DPC_EN_MASK,
+ ISP_TOP_FEO_CTRL1_0_DPC_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FEO_CTRL1_0,
+ ISP_TOP_FEO_CTRL1_0_OG_EN_MASK,
+ ISP_TOP_FEO_CTRL1_0_OG_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_PDPC_EN_MASK,
+ ISP_TOP_FED_CTRL_PDPC_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
+ ISP_TOP_FED_CTRL_RAWCNR_EN_MASK,
+ ISP_TOP_FED_CTRL_RAWCNR_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SNR1_EN_MASK,
+ ISP_TOP_FED_CTRL_SNR1_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_TNR0_EN_MASK,
+ ISP_TOP_FED_CTRL_TNR0_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
+ ISP_TOP_FED_CTRL_CUBIC_CS_EN_MASK,
+ ISP_TOP_FED_CTRL_CUBIC_CS_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL, ISP_TOP_FED_CTRL_SQRT_EN_MASK,
+ ISP_TOP_FED_CTRL_SQRT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_FED_CTRL,
+ ISP_TOP_FED_CTRL_DGAIN_EN_MASK,
+ ISP_TOP_FED_CTRL_DGAIN_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL,
+ ISP_TOP_BEO_CTRL_INV_DGAIN_EN_MASK,
+ ISP_TOP_BEO_CTRL_INV_DGAIN_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BEO_CTRL, ISP_TOP_BEO_CTRL_EOTF_EN_MASK,
+ ISP_TOP_BEO_CTRL_EOTF_DIS);
+
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_YHS_STAT_EN_MASK,
+ ISP_TOP_BED_CTRL_YHS_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_GRPH_STAT_EN_MASK,
+ ISP_TOP_BED_CTRL_GRPH_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_FMETER_EN_MASK,
+ ISP_TOP_BED_CTRL_FMETER_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_BSC_EN_MASK,
+ ISP_TOP_BED_CTRL_BSC_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CNR2_EN_MASK,
+ ISP_TOP_BED_CTRL_CNR2_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_CM1_EN_MASK,
+ ISP_TOP_BED_CTRL_CM1_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_LUT3D_EN_MASK,
+ ISP_TOP_BED_CTRL_LUT3D_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL,
+ ISP_TOP_BED_CTRL_PST_TNR_LITE_EN_MASK,
+ ISP_TOP_BED_CTRL_PST_TNR_LITE_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_BED_CTRL, ISP_TOP_BED_CTRL_AMCM_EN_MASK,
+ ISP_TOP_BED_CTRL_AMCM_DIS);
+
+ /*
+ * Disable AE, AF and AWB stat module. Please configure the parameters
+ * in userspace algorithm if need to enable these switch.
+ */
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS);
+ c3_isp_update_bits(isp, ISP_TOP_3A_STAT_CRTL,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK,
+ ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS);
+
+ c3_isp_write(isp, ISP_LSWB_WB_LIMIT0,
+ ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MAX |
+ ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MAX);
+ c3_isp_write(isp, ISP_LSWB_WB_LIMIT1,
+ ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MAX |
+ ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MAX);
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ /* Only use the first buffer to initialize ISP */
+ params->buff =
+ list_first_entry_or_null(&params->pending,
+ struct c3_isp_params_buffer, list);
+ if (params->buff)
+ c3_isp_params_cfg_blocks(params);
+}
+
+/* V4L2 video operations */
+
+static int c3_isp_params_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "AML C3 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static int c3_isp_params_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_C3ISP_PARAMS;
+
+ return 0;
+}
+
+static int c3_isp_params_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_params *params = video_drvdata(file);
+
+ f->fmt.meta = params->vfmt.fmt.meta;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp_params_v4l2_ioctl_ops = {
+ .vidioc_querycap = c3_isp_params_querycap,
+ .vidioc_enum_fmt_meta_out = c3_isp_params_enum_fmt,
+ .vidioc_g_fmt_meta_out = c3_isp_params_g_fmt,
+ .vidioc_s_fmt_meta_out = c3_isp_params_g_fmt,
+ .vidioc_try_fmt_meta_out = c3_isp_params_g_fmt,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct v4l2_file_operations isp_params_v4l2_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int c3_isp_params_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+
+ if (sizes[0] < sizeof(struct c3_isp_params_cfg))
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *num_planes = 1;
+ sizes[0] = sizeof(struct c3_isp_params_cfg);
+
+ return 0;
+}
+
+static void c3_isp_params_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
+ struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ list_add_tail(&buf->list, &params->pending);
+}
+
+static int c3_isp_params_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(vbuf);
+ struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
+ struct c3_isp_params_cfg *cfg = buf->cfg;
+ struct c3_isp_params_cfg *usr_cfg = vb2_plane_vaddr(vb, 0);
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+ size_t header_size = offsetof(struct c3_isp_params_cfg, data);
+ size_t block_offset = 0;
+ size_t cfg_size;
+
+ /* Payload size can't be greater than the destination buffer size */
+ if (payload_size > params->vfmt.fmt.meta.buffersize) {
+ dev_dbg(params->isp->dev,
+ "Payload size is too large: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ /* Payload size can't be smaller than the header size */
+ if (payload_size < header_size) {
+ dev_dbg(params->isp->dev,
+ "Payload size is too small: %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ /*
+ * Use the internal scratch buffer to avoid userspace modifying
+ * the buffer content while the driver is processing it.
+ */
+ memcpy(cfg, usr_cfg, payload_size);
+
+ /* Only v0 is supported at the moment */
+ if (cfg->version != C3_ISP_PARAMS_BUFFER_V0) {
+ dev_dbg(params->isp->dev,
+ "Invalid params buffer version: %u\n", cfg->version);
+ return -EINVAL;
+ }
+
+ /* Validate the size reported in the parameter buffer header */
+ cfg_size = header_size + cfg->data_size;
+ if (cfg_size != payload_size) {
+ dev_dbg(params->isp->dev,
+ "Data size %zu and payload size %zu are different\n",
+ cfg_size, payload_size);
+ return -EINVAL;
+ }
+
+ /* Walk the list of parameter blocks and validate them */
+ cfg_size = cfg->data_size;
+ while (cfg_size >= sizeof(struct c3_isp_params_block_header)) {
+ const struct c3_isp_params_block_header *block;
+ const struct c3_isp_params_handler *handler;
+
+ block = (struct c3_isp_params_block_header *)
+ &cfg->data[block_offset];
+
+ if (block->type >= ARRAY_SIZE(c3_isp_params_handlers)) {
+ dev_dbg(params->isp->dev,
+ "Invalid params block type\n");
+ return -EINVAL;
+ }
+
+ if (block->size > cfg_size) {
+ dev_dbg(params->isp->dev,
+ "Block size is greater than cfg size\n");
+ return -EINVAL;
+ }
+
+ if ((block->flags & (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
+ C3_ISP_PARAMS_BLOCK_FL_DISABLE)) ==
+ (C3_ISP_PARAMS_BLOCK_FL_ENABLE |
+ C3_ISP_PARAMS_BLOCK_FL_DISABLE)) {
+ dev_dbg(params->isp->dev,
+ "Invalid parameters block flags\n");
+ return -EINVAL;
+ }
+
+ handler = &c3_isp_params_handlers[block->type];
+ if (block->size != handler->size) {
+ dev_dbg(params->isp->dev,
+ "Invalid params block size\n");
+ return -EINVAL;
+ }
+
+ block_offset += block->size;
+ cfg_size -= block->size;
+ }
+
+ if (cfg_size) {
+ dev_dbg(params->isp->dev,
+ "Unexpected data after the params buffer end\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_isp_params_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params *params = vb2_get_drv_priv(vb->vb2_queue);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
+
+ buf->cfg = kvmalloc(params->vfmt.fmt.meta.buffersize, GFP_KERNEL);
+ if (!buf->cfg)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void c3_isp_params_vb2_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_params_buffer *buf = to_c3_isp_params_buffer(v4l2_buf);
+
+ kvfree(buf->cfg);
+ buf->cfg = NULL;
+}
+
+static void c3_isp_params_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct c3_isp_params *params = vb2_get_drv_priv(q);
+ struct c3_isp_params_buffer *buff;
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ while (!list_empty(&params->pending)) {
+ buff = list_first_entry(&params->pending,
+ struct c3_isp_params_buffer, list);
+ list_del(&buff->list);
+ vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops isp_params_vb2_ops = {
+ .queue_setup = c3_isp_params_vb2_queue_setup,
+ .buf_queue = c3_isp_params_vb2_buf_queue,
+ .buf_prepare = c3_isp_params_vb2_buf_prepare,
+ .buf_init = c3_isp_params_vb2_buf_init,
+ .buf_cleanup = c3_isp_params_vb2_buf_cleanup,
+ .stop_streaming = c3_isp_params_vb2_stop_streaming,
+};
+
+int c3_isp_params_register(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+ struct video_device *vdev = &params->vdev;
+ struct vb2_queue *vb2_q = &params->vb2_q;
+ int ret;
+
+ memset(params, 0, sizeof(*params));
+ params->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_PARAMS;
+ params->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_params_cfg);
+ params->isp = isp;
+ INIT_LIST_HEAD(&params->pending);
+ spin_lock_init(&params->buff_lock);
+ mutex_init(&params->lock);
+
+ snprintf(vdev->name, sizeof(vdev->name), "c3-isp-params");
+ vdev->fops = &isp_params_v4l2_fops;
+ vdev->ioctl_ops = &isp_params_v4l2_ioctl_ops;
+ vdev->v4l2_dev = &isp->v4l2_dev;
+ vdev->lock = &params->lock;
+ vdev->minor = -1;
+ vdev->queue = vb2_q;
+ vdev->release = video_device_release_empty;
+ vdev->device_caps = V4L2_CAP_META_OUTPUT | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_TX;
+ video_set_drvdata(vdev, params);
+
+ vb2_q->drv_priv = params;
+ vb2_q->mem_ops = &vb2_vmalloc_memops;
+ vb2_q->ops = &isp_params_vb2_ops;
+ vb2_q->type = V4L2_BUF_TYPE_META_OUTPUT;
+ vb2_q->io_modes = VB2_DMABUF | VB2_MMAP;
+ vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2_q->buf_struct_size = sizeof(struct c3_isp_params_buffer);
+ vb2_q->dev = isp->dev;
+ vb2_q->lock = &params->lock;
+ vb2_q->min_queued_buffers = 1;
+
+ ret = vb2_queue_init(vb2_q);
+ if (ret)
+ goto err_detroy;
+
+ params->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&vdev->entity, 1, &params->pad);
+ if (ret)
+ goto err_queue_release;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ dev_err(isp->dev,
+ "Failed to register %s: %d\n", vdev->name, ret);
+ goto err_entity_cleanup;
+ }
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_queue_release:
+ vb2_queue_release(vb2_q);
+err_detroy:
+ mutex_destroy(&params->lock);
+ return ret;
+}
+
+void c3_isp_params_unregister(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+
+ vb2_queue_release(&params->vb2_q);
+ media_entity_cleanup(&params->vdev.entity);
+ video_unregister_device(&params->vdev);
+ mutex_destroy(&params->lock);
+}
+
+void c3_isp_params_isr(struct c3_isp_device *isp)
+{
+ struct c3_isp_params *params = &isp->params;
+
+ guard(spinlock_irqsave)(&params->buff_lock);
+
+ params->buff =
+ list_first_entry_or_null(&params->pending,
+ struct c3_isp_params_buffer, list);
+ if (!params->buff)
+ return;
+
+ list_del(&params->buff->list);
+
+ c3_isp_params_cfg_blocks(params);
+
+ params->buff->vb.sequence = params->isp->frm_sequence;
+ params->buff->vb.vb2_buf.timestamp = ktime_get();
+ params->buff->vb.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&params->buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h b/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h
new file mode 100644
index 000000000000..fa249985a771
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-regs.h
@@ -0,0 +1,618 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#ifndef __C3_ISP_REGS_H__
+#define __C3_ISP_REGS_H__
+
+#define ISP_TOP_INPUT_SIZE 0x0000
+#define ISP_TOP_INPUT_SIZE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_TOP_INPUT_SIZE_VERT_SIZE(x) ((x) << 0)
+#define ISP_TOP_INPUT_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_TOP_INPUT_SIZE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_TOP_FRM_SIZE 0x0004
+#define ISP_TOP_FRM_SIZE_CORE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_TOP_FRM_SIZE_CORE_VERT_SIZE(x) ((x) << 0)
+#define ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_TOP_FRM_SIZE_CORE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_TOP_HOLD_SIZE 0x0008
+#define ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_TOP_HOLD_SIZE_CORE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_TOP_PATH_EN 0x0010
+#define ISP_TOP_PATH_EN_DISP0_EN_MASK BIT(0)
+#define ISP_TOP_PATH_EN_DISP0_EN BIT(0)
+#define ISP_TOP_PATH_EN_DISP0_DIS (0 << 0)
+#define ISP_TOP_PATH_EN_DISP1_EN_MASK BIT(1)
+#define ISP_TOP_PATH_EN_DISP1_EN BIT(1)
+#define ISP_TOP_PATH_EN_DISP1_DIS (0 << 1)
+#define ISP_TOP_PATH_EN_DISP2_EN_MASK BIT(2)
+#define ISP_TOP_PATH_EN_DISP2_EN BIT(2)
+#define ISP_TOP_PATH_EN_DISP2_DIS (0 << 2)
+#define ISP_TOP_PATH_EN_WRMIF0_EN_MASK BIT(8)
+#define ISP_TOP_PATH_EN_WRMIF0_EN BIT(8)
+#define ISP_TOP_PATH_EN_WRMIF0_DIS (0 << 8)
+#define ISP_TOP_PATH_EN_WRMIF1_EN_MASK BIT(9)
+#define ISP_TOP_PATH_EN_WRMIF1_EN BIT(9)
+#define ISP_TOP_PATH_EN_WRMIF1_DIS (0 << 9)
+#define ISP_TOP_PATH_EN_WRMIF2_EN_MASK BIT(10)
+#define ISP_TOP_PATH_EN_WRMIF2_EN BIT(10)
+#define ISP_TOP_PATH_EN_WRMIF2_DIS (0 << 10)
+
+#define ISP_TOP_PATH_SEL 0x0014
+#define ISP_TOP_PATH_SEL_CORE_MASK GENMASK(18, 16)
+#define ISP_TOP_PATH_SEL_CORE_CORE_DIS (0 << 16)
+#define ISP_TOP_PATH_SEL_CORE_MIPI_CORE BIT(16)
+
+#define ISP_TOP_DISPIN_SEL 0x0018
+#define ISP_TOP_DISPIN_SEL_DISP0_MASK GENMASK(3, 0)
+#define ISP_TOP_DISPIN_SEL_DISP0_CORE_OUT (0 << 0)
+#define ISP_TOP_DISPIN_SEL_DISP0_MIPI_OUT (2 << 0)
+#define ISP_TOP_DISPIN_SEL_DISP1_MASK GENMASK(7, 4)
+#define ISP_TOP_DISPIN_SEL_DISP1_CORE_OUT (0 << 4)
+#define ISP_TOP_DISPIN_SEL_DISP1_MIPI_OUT (2 << 4)
+#define ISP_TOP_DISPIN_SEL_DISP2_MASK GENMASK(11, 8)
+#define ISP_TOP_DISPIN_SEL_DISP2_CORE_OUT (0 << 8)
+#define ISP_TOP_DISPIN_SEL_DISP2_MIPI_OUT (2 << 8)
+
+#define ISP_TOP_IRQ_EN 0x0080
+#define ISP_TOP_IRQ_EN_FRM_END_MASK BIT(0)
+#define ISP_TOP_IRQ_EN_FRM_END_EN BIT(0)
+#define ISP_TOP_IRQ_EN_FRM_END_DIS (0 << 0)
+#define ISP_TOP_IRQ_EN_FRM_RST_MASK BIT(1)
+#define ISP_TOP_IRQ_EN_FRM_RST_EN BIT(1)
+#define ISP_TOP_IRQ_EN_FRM_RST_DIS (0 << 1)
+#define ISP_TOP_IRQ_EN_3A_DMA_ERR_MASK BIT(5)
+#define ISP_TOP_IRQ_EN_3A_DMA_ERR_EN BIT(5)
+#define ISP_TOP_IRQ_EN_3A_DMA_ERR_DIS (0 << 5)
+
+#define ISP_TOP_IRQ_CLR 0x0084
+#define ISP_TOP_RO_IRQ_STAT 0x01c4
+#define ISP_TOP_RO_IRQ_STAT_FRM_END_MASK BIT(0)
+#define ISP_TOP_RO_IRQ_STAT_FRM_RST_MASK BIT(1)
+#define ISP_TOP_RO_IRQ_STAT_3A_DMA_ERR_MASK BIT(5)
+
+#define ISP_TOP_MODE_CTRL 0x0400
+#define ISP_TOP_FEO_CTRL0 0x040c
+#define ISP_TOP_FEO_CTRL0_INPUT_FMT_EN_MASK BIT(8)
+#define ISP_TOP_FEO_CTRL0_INPUT_FMT_DIS (0 << 8)
+#define ISP_TOP_FEO_CTRL0_INPUT_FMT_EN BIT(8)
+
+#define ISP_TOP_FEO_CTRL1_0 0x0410
+#define ISP_TOP_FEO_CTRL1_0_DPC_EN_MASK BIT(3)
+#define ISP_TOP_FEO_CTRL1_0_DPC_DIS (0 << 3)
+#define ISP_TOP_FEO_CTRL1_0_DPC_EN BIT(3)
+#define ISP_TOP_FEO_CTRL1_0_OG_EN_MASK BIT(5)
+#define ISP_TOP_FEO_CTRL1_0_OG_DIS (0 << 5)
+#define ISP_TOP_FEO_CTRL1_0_OG_EN BIT(5)
+
+#define ISP_TOP_FED_CTRL 0x0418
+#define ISP_TOP_FED_CTRL_PDPC_EN_MASK BIT(1)
+#define ISP_TOP_FED_CTRL_PDPC_DIS (0 << 1)
+#define ISP_TOP_FED_CTRL_PDPC_EN BIT(1)
+#define ISP_TOP_FED_CTRL_RAWCNR_EN_MASK GENMASK(6, 5)
+#define ISP_TOP_FED_CTRL_RAWCNR_DIS (0 << 5)
+#define ISP_TOP_FED_CTRL_RAWCNR_EN BIT(5)
+#define ISP_TOP_FED_CTRL_SNR1_EN_MASK BIT(9)
+#define ISP_TOP_FED_CTRL_SNR1_DIS (0 << 9)
+#define ISP_TOP_FED_CTRL_SNR1_EN BIT(9)
+#define ISP_TOP_FED_CTRL_TNR0_EN_MASK BIT(11)
+#define ISP_TOP_FED_CTRL_TNR0_DIS (0 << 11)
+#define ISP_TOP_FED_CTRL_TNR0_EN BIT(11)
+#define ISP_TOP_FED_CTRL_CUBIC_CS_EN_MASK BIT(12)
+#define ISP_TOP_FED_CTRL_CUBIC_CS_DIS (0 << 12)
+#define ISP_TOP_FED_CTRL_CUBIC_CS_EN BIT(12)
+#define ISP_TOP_FED_CTRL_SQRT_EN_MASK BIT(14)
+#define ISP_TOP_FED_CTRL_SQRT_DIS (0 << 14)
+#define ISP_TOP_FED_CTRL_SQRT_EN BIT(14)
+#define ISP_TOP_FED_CTRL_DGAIN_EN_MASK BIT(16)
+#define ISP_TOP_FED_CTRL_DGAIN_DIS (0 << 16)
+#define ISP_TOP_FED_CTRL_DGAIN_EN BIT(16)
+
+#define ISP_TOP_BEO_CTRL 0x041c
+#define ISP_TOP_BEO_CTRL_WB_EN_MASK BIT(6)
+#define ISP_TOP_BEO_CTRL_WB_DIS (0 << 6)
+#define ISP_TOP_BEO_CTRL_WB_EN BIT(6)
+#define ISP_TOP_BEO_CTRL_BLC_EN_MASK BIT(7)
+#define ISP_TOP_BEO_CTRL_BLC_DIS (0 << 7)
+#define ISP_TOP_BEO_CTRL_BLC_EN BIT(7)
+#define ISP_TOP_BEO_CTRL_INV_DGAIN_EN_MASK BIT(8)
+#define ISP_TOP_BEO_CTRL_INV_DGAIN_DIS (0 << 8)
+#define ISP_TOP_BEO_CTRL_INV_DGAIN_EN BIT(8)
+#define ISP_TOP_BEO_CTRL_EOTF_EN_MASK BIT(9)
+#define ISP_TOP_BEO_CTRL_EOTF_DIS (0 << 9)
+#define ISP_TOP_BEO_CTRL_EOTF_EN BIT(9)
+
+#define ISP_TOP_BED_CTRL 0x0420
+#define ISP_TOP_BED_CTRL_YHS_STAT_EN_MASK GENMASK(1, 0)
+#define ISP_TOP_BED_CTRL_YHS_STAT_DIS (0 << 0)
+#define ISP_TOP_BED_CTRL_YHS_STAT_EN BIT(0)
+#define ISP_TOP_BED_CTRL_GRPH_STAT_EN_MASK BIT(2)
+#define ISP_TOP_BED_CTRL_GRPH_STAT_DIS (0 << 2)
+#define ISP_TOP_BED_CTRL_GRPH_STAT_EN BIT(2)
+#define ISP_TOP_BED_CTRL_FMETER_EN_MASK BIT(3)
+#define ISP_TOP_BED_CTRL_FMETER_DIS (0 << 3)
+#define ISP_TOP_BED_CTRL_FMETER_EN BIT(3)
+#define ISP_TOP_BED_CTRL_BSC_EN_MASK BIT(10)
+#define ISP_TOP_BED_CTRL_BSC_DIS (0 << 10)
+#define ISP_TOP_BED_CTRL_BSC_EN BIT(10)
+#define ISP_TOP_BED_CTRL_CNR2_EN_MASK BIT(11)
+#define ISP_TOP_BED_CTRL_CNR2_DIS (0 << 11)
+#define ISP_TOP_BED_CTRL_CNR2_EN BIT(11)
+#define ISP_TOP_BED_CTRL_CM1_EN_MASK BIT(13)
+#define ISP_TOP_BED_CTRL_CM1_DIS (0 << 13)
+#define ISP_TOP_BED_CTRL_CM1_EN BIT(13)
+#define ISP_TOP_BED_CTRL_CM0_EN_MASK BIT(14)
+#define ISP_TOP_BED_CTRL_CM0_DIS (0 << 14)
+#define ISP_TOP_BED_CTRL_CM0_EN BIT(14)
+#define ISP_TOP_BED_CTRL_PST_GAMMA_EN_MASK BIT(16)
+#define ISP_TOP_BED_CTRL_PST_GAMMA_DIS (0 << 16)
+#define ISP_TOP_BED_CTRL_PST_GAMMA_EN BIT(16)
+#define ISP_TOP_BED_CTRL_LUT3D_EN_MASK BIT(17)
+#define ISP_TOP_BED_CTRL_LUT3D_DIS (0 << 17)
+#define ISP_TOP_BED_CTRL_LUT3D_EN BIT(17)
+#define ISP_TOP_BED_CTRL_CCM_EN_MASK BIT(18)
+#define ISP_TOP_BED_CTRL_CCM_DIS (0 << 18)
+#define ISP_TOP_BED_CTRL_CCM_EN BIT(18)
+#define ISP_TOP_BED_CTRL_PST_TNR_LITE_EN_MASK BIT(21)
+#define ISP_TOP_BED_CTRL_PST_TNR_LITE_DIS (0 << 21)
+#define ISP_TOP_BED_CTRL_PST_TNR_LITE_EN BIT(21)
+#define ISP_TOP_BED_CTRL_AMCM_EN_MASK BIT(25)
+#define ISP_TOP_BED_CTRL_AMCM_DIS (0 << 25)
+#define ISP_TOP_BED_CTRL_AMCM_EN BIT(25)
+
+#define ISP_TOP_3A_STAT_CRTL 0x0424
+#define ISP_TOP_3A_STAT_CRTL_AE_STAT_EN_MASK BIT(0)
+#define ISP_TOP_3A_STAT_CRTL_AE_STAT_DIS (0 << 0)
+#define ISP_TOP_3A_STAT_CRTL_AE_STAT_EN BIT(0)
+#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN_MASK BIT(1)
+#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_DIS (0 << 1)
+#define ISP_TOP_3A_STAT_CRTL_AWB_STAT_EN BIT(1)
+#define ISP_TOP_3A_STAT_CRTL_AF_STAT_EN_MASK BIT(2)
+#define ISP_TOP_3A_STAT_CRTL_AF_STAT_DIS (0 << 2)
+#define ISP_TOP_3A_STAT_CRTL_AF_STAT_EN BIT(2)
+#define ISP_TOP_3A_STAT_CRTL_AWB_POINT_MASK GENMASK(6, 4)
+#define ISP_TOP_3A_STAT_CRTL_AWB_POINT(x) ((x) << 4)
+#define ISP_TOP_3A_STAT_CRTL_AE_POINT_MASK GENMASK(9, 8)
+#define ISP_TOP_3A_STAT_CRTL_AE_POINT(x) ((x) << 8)
+#define ISP_TOP_3A_STAT_CRTL_AF_POINT_MASK GENMASK(13, 12)
+#define ISP_TOP_3A_STAT_CRTL_AF_POINT(x) ((x) << 12)
+
+#define ISP_LSWB_BLC_OFST0 0x4028
+#define ISP_LSWB_BLC_OFST0_R_OFST_MASK GENMASK(15, 0)
+#define ISP_LSWB_BLC_OFST0_R_OFST(x) ((x) << 0)
+#define ISP_LSWB_BLC_OFST0_GR_OFST_MASK GENMASK(31, 16)
+#define ISP_LSWB_BLC_OFST0_GR_OFST(x) ((x) << 16)
+
+#define ISP_LSWB_BLC_OFST1 0x402c
+#define ISP_LSWB_BLC_OFST1_GB_OFST_MASK GENMASK(15, 0)
+#define ISP_LSWB_BLC_OFST1_GB_OFST(x) ((x) << 0)
+#define ISP_LSWB_BLC_OFST1_B_OFST_MASK GENMASK(31, 16)
+#define ISP_LSWB_BLC_OFST1_B_OFST(x) ((x) << 16)
+
+#define ISP_LSWB_BLC_PHSOFST 0x4034
+#define ISP_LSWB_BLC_PHSOFST_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_LSWB_BLC_PHSOFST_VERT_OFST(x) ((x) << 0)
+#define ISP_LSWB_BLC_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_LSWB_BLC_PHSOFST_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_LSWB_WB_GAIN0 0x4038
+#define ISP_LSWB_WB_GAIN0_R_GAIN_MASK GENMASK(11, 0)
+#define ISP_LSWB_WB_GAIN0_R_GAIN(x) ((x) << 0)
+#define ISP_LSWB_WB_GAIN0_GR_GAIN_MASK GENMASK(27, 16)
+#define ISP_LSWB_WB_GAIN0_GR_GAIN(x) ((x) << 16)
+
+#define ISP_LSWB_WB_GAIN1 0x403c
+#define ISP_LSWB_WB_GAIN1_GB_GAIN_MASK GENMASK(11, 0)
+#define ISP_LSWB_WB_GAIN1_GB_GAIN(x) ((x) << 0)
+#define ISP_LSWB_WB_GAIN1_B_GAIN_MASK GENMASK(27, 16)
+#define ISP_LSWB_WB_GAIN1_B_GAIN(x) ((x) << 16)
+
+#define ISP_LSWB_WB_GAIN2 0x4040
+#define ISP_LSWB_WB_GAIN2_IR_GAIN_MASK GENMASK(11, 0)
+#define ISP_LSWB_WB_GAIN2_IR_GAIN(x) ((x) << 0)
+
+#define ISP_LSWB_WB_LIMIT0 0x4044
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MASK GENMASK(15, 0)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R(x) ((x) << 0)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_R_MAX (0x8fff << 0)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MASK GENMASK(31, 16)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR(x) ((x) << 16)
+#define ISP_LSWB_WB_LIMIT0_WB_LIMIT_GR_MAX (0x8fff << 16)
+
+#define ISP_LSWB_WB_LIMIT1 0x4048
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MASK GENMASK(15, 0)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB(x) ((x) << 0)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_GB_MAX (0x8fff << 0)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MASK GENMASK(31, 16)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B(x) ((x) << 16)
+#define ISP_LSWB_WB_LIMIT1_WB_LIMIT_B_MAX (0x8fff << 16)
+
+#define ISP_LSWB_WB_PHSOFST 0x4050
+#define ISP_LSWB_WB_PHSOFST_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_LSWB_WB_PHSOFST_VERT_OFST(x) ((x) << 0)
+#define ISP_LSWB_WB_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_LSWB_WB_PHSOFST_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_LSWB_LNS_PHSOFST 0x4054
+#define ISP_LSWB_LNS_PHSOFST_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_LSWB_LNS_PHSOFST_VERT_OFST(x) ((x) << 0)
+#define ISP_LSWB_LNS_PHSOFST_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_LSWB_LNS_PHSOFST_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_DMS_COMMON_PARAM0 0x5000
+#define ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST_MASK GENMASK(1, 0)
+#define ISP_DMS_COMMON_PARAM0_VERT_PHS_OFST(x) ((x) << 0)
+#define ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST_MASK GENMASK(3, 2)
+#define ISP_DMS_COMMON_PARAM0_HORIZ_PHS_OFST(x) ((x) << 2)
+
+#define ISP_CM0_COEF00_01 0x6048
+#define ISP_CM0_COEF00_01_MTX_00_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF00_01_MTX_00(x) ((x) << 0)
+#define ISP_CM0_COEF00_01_MTX_01_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF00_01_MTX_01(x) ((x) << 16)
+
+#define ISP_CM0_COEF02_10 0x604c
+#define ISP_CM0_COEF02_10_MTX_02_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF02_10_MTX_02(x) ((x) << 0)
+#define ISP_CM0_COEF02_10_MTX_10_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF02_10_MTX_10(x) ((x) << 16)
+
+#define ISP_CM0_COEF11_12 0x6050
+#define ISP_CM0_COEF11_12_MTX_11_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF11_12_MTX_11(x) ((x) << 0)
+#define ISP_CM0_COEF11_12_MTX_12_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF11_12_MTX_12(x) ((x) << 16)
+
+#define ISP_CM0_COEF20_21 0x6054
+#define ISP_CM0_COEF20_21_MTX_20_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF20_21_MTX_20(x) ((x) << 0)
+#define ISP_CM0_COEF20_21_MTX_21_MASK GENMASK(28, 16)
+#define ISP_CM0_COEF20_21_MTX_21(x) ((x) << 16)
+
+#define ISP_CM0_COEF22_OUP_OFST0 0x6058
+#define ISP_CM0_COEF22_OUP_OFST0_MTX_22_MASK GENMASK(12, 0)
+#define ISP_CM0_COEF22_OUP_OFST0_MTX_22(x) ((x) << 0)
+
+#define ISP_CCM_MTX_00_01 0x6098
+#define ISP_CCM_MTX_00_01_MTX_00_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_00_01_MTX_00(x) ((x) << 0)
+#define ISP_CCM_MTX_00_01_MTX_01_MASK GENMASK(28, 16)
+#define ISP_CCM_MTX_00_01_MTX_01(x) ((x) << 16)
+
+#define ISP_CCM_MTX_02_03 0x609c
+#define ISP_CCM_MTX_02_03_MTX_02_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_02_03_MTX_02(x) ((x) << 0)
+
+#define ISP_CCM_MTX_10_11 0x60A0
+#define ISP_CCM_MTX_10_11_MTX_10_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_10_11_MTX_10(x) ((x) << 0)
+#define ISP_CCM_MTX_10_11_MTX_11_MASK GENMASK(28, 16)
+#define ISP_CCM_MTX_10_11_MTX_11(x) ((x) << 16)
+
+#define ISP_CCM_MTX_12_13 0x60A4
+#define ISP_CCM_MTX_12_13_MTX_12_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_12_13_MTX_12(x) ((x) << 0)
+
+#define ISP_CCM_MTX_20_21 0x60A8
+#define ISP_CCM_MTX_20_21_MTX_20_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_20_21_MTX_20(x) ((x) << 0)
+#define ISP_CCM_MTX_20_21_MTX_21_MASK GENMASK(28, 16)
+#define ISP_CCM_MTX_20_21_MTX_21(x) ((x) << 16)
+
+#define ISP_CCM_MTX_22_23_RS 0x60Ac
+#define ISP_CCM_MTX_22_23_RS_MTX_22_MASK GENMASK(12, 0)
+#define ISP_CCM_MTX_22_23_RS_MTX_22(x) ((x) << 0)
+
+#define ISP_PST_GAMMA_LUT_ADDR 0x60cc
+#define ISP_PST_GAMMA_LUT_ADDR_IDX_ADDR(x) ((x) << 7)
+
+#define ISP_PST_GAMMA_LUT_DATA 0x60d0
+#define ISP_PST_GM_LUT_DATA0(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_PST_GM_LUT_DATA1(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define DISP0_TOP_TOP_CTRL 0x8000
+#define DISP0_TOP_TOP_CTRL_CROP2_EN_MASK BIT(5)
+#define DISP0_TOP_TOP_CTRL_CROP2_EN BIT(5)
+#define DISP0_TOP_TOP_CTRL_CROP2_DIS (0 << 5)
+
+#define DISP0_TOP_CRP2_START 0x8004
+#define DISP0_TOP_CRP2_START_V_START_MASK GENMASK(15, 0)
+#define DISP0_TOP_CRP2_START_V_START(x) ((x) << 0)
+#define DISP0_TOP_CRP2_START_H_START_MASK GENMASK(31, 16)
+#define DISP0_TOP_CRP2_START_H_START(x) ((x) << 16)
+
+#define DISP0_TOP_CRP2_SIZE 0x8008
+#define DISP0_TOP_CRP2_SIZE_V_SIZE_MASK GENMASK(15, 0)
+#define DISP0_TOP_CRP2_SIZE_V_SIZE(x) ((x) << 0)
+#define DISP0_TOP_CRP2_SIZE_H_SIZE_MASK GENMASK(31, 16)
+#define DISP0_TOP_CRP2_SIZE_H_SIZE(x) ((x) << 16)
+
+#define DISP0_TOP_OUT_SIZE 0x800c
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT_MASK GENMASK(12, 0)
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT(x) ((x) << 0)
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH_MASK GENMASK(28, 16)
+#define DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH(x) ((x) << 16)
+
+#define ISP_DISP0_TOP_IN_SIZE 0x804c
+#define ISP_DISP0_TOP_IN_SIZE_VSIZE_MASK GENMASK(12, 0)
+#define ISP_DISP0_TOP_IN_SIZE_VSIZE(x) ((x) << 0)
+#define ISP_DISP0_TOP_IN_SIZE_HSIZE_MASK GENMASK(28, 16)
+#define ISP_DISP0_TOP_IN_SIZE_HSIZE(x) ((x) << 16)
+
+#define DISP0_PPS_SCALE_EN 0x8200
+#define DISP0_PPS_SCALE_EN_VSC_TAP_NUM_MASK GENMASK(3, 0)
+#define DISP0_PPS_SCALE_EN_VSC_TAP_NUM(x) ((x) << 0)
+#define DISP0_PPS_SCALE_EN_HSC_TAP_NUM_MASK GENMASK(7, 4)
+#define DISP0_PPS_SCALE_EN_HSC_TAP_NUM(x) ((x) << 4)
+#define DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM_MASK GENMASK(11, 8)
+#define DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM(x) ((x) << 8)
+#define DISP0_PPS_SCALE_EN_PREHSC_FLT_NUM_MASK GENMASK(15, 12)
+#define DISP0_PPS_SCALE_EN_PREHSC_FLT_NUM(x) ((x) << 12)
+#define DISP0_PPS_SCALE_EN_PREVSC_RATE_MASK GENMASK(17, 16)
+#define DISP0_PPS_SCALE_EN_PREVSC_RATE(x) ((x) << 16)
+#define DISP0_PPS_SCALE_EN_PREHSC_RATE_MASK GENMASK(19, 18)
+#define DISP0_PPS_SCALE_EN_PREHSC_RATE(x) ((x) << 18)
+#define DISP0_PPS_SCALE_EN_HSC_EN_MASK BIT(20)
+#define DISP0_PPS_SCALE_EN_HSC_EN(x) ((x) << 20)
+#define DISP0_PPS_SCALE_EN_HSC_DIS (0 << 20)
+#define DISP0_PPS_SCALE_EN_VSC_EN_MASK BIT(21)
+#define DISP0_PPS_SCALE_EN_VSC_EN(x) ((x) << 21)
+#define DISP0_PPS_SCALE_EN_VSC_DIS (0 << 21)
+#define DISP0_PPS_SCALE_EN_PREVSC_EN_MASK BIT(22)
+#define DISP0_PPS_SCALE_EN_PREVSC_EN(x) ((x) << 22)
+#define DISP0_PPS_SCALE_EN_PREVSC_DIS (0 << 22)
+#define DISP0_PPS_SCALE_EN_PREHSC_EN_MASK BIT(23)
+#define DISP0_PPS_SCALE_EN_PREHSC_EN(x) ((x) << 23)
+#define DISP0_PPS_SCALE_EN_PREHSC_DIS (0 << 23)
+#define DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS_MASK GENMASK(27, 24)
+#define DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS(x) ((x) << 24)
+#define DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS_MASK GENMASK(31, 28)
+#define DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS(x) ((x) << 28)
+
+#define DISP0_PPS_VSC_START_PHASE_STEP 0x8224
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC_MASK GENMASK(23, 0)
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC(x) ((x) << 0)
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE_MASK GENMASK(27, 24)
+#define DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE(x) ((x) << 24)
+
+#define DISP0_PPS_HSC_START_PHASE_STEP 0x8230
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC_MASK GENMASK(23, 0)
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC(x) ((x) << 0)
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE_MASK GENMASK(27, 24)
+#define DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE(x) ((x) << 24)
+
+#define DISP0_PPS_444TO422 0x823c
+#define DISP0_PPS_444TO422_EN_MASK BIT(0)
+#define DISP0_PPS_444TO422_EN(x) ((x) << 0)
+
+#define ISP_SCALE0_COEF_IDX_LUMA 0x8240
+#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_MASK BIT(9)
+#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_EN BIT(9)
+#define ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_DIS (0 << 9)
+#define ISP_SCALE0_COEF_IDX_LUMA_CTYPE_MASK GENMASK(12, 10)
+#define ISP_SCALE0_COEF_IDX_LUMA_CTYPE(x) ((x) << 10)
+
+#define ISP_SCALE0_COEF_LUMA 0x8244
+#define ISP_SCALE0_COEF_LUMA_DATA1(x) (((x) & GENMASK(10, 0)) << 0)
+#define ISP_SCALE0_COEF_LUMA_DATA0(x) (((x) & GENMASK(10, 0)) << 16)
+
+#define ISP_SCALE0_COEF_IDX_CHRO 0x8248
+#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_MASK BIT(9)
+#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_EN BIT(9)
+#define ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_DIS (0 << 9)
+#define ISP_SCALE0_COEF_IDX_CHRO_CTYPE_MASK GENMASK(12, 10)
+#define ISP_SCALE0_COEF_IDX_CHRO_CTYPE(x) ((x) << 10)
+
+#define ISP_SCALE0_COEF_CHRO 0x824c
+#define ISP_SCALE0_COEF_CHRO_DATA1(x) (((x) & GENMASK(10, 0)) << 0)
+#define ISP_SCALE0_COEF_CHRO_DATA0(x) (((x) & GENMASK(10, 0)) << 16)
+
+#define ISP_AF_CTRL 0xa044
+#define ISP_AF_CTRL_VERT_OFST_MASK GENMASK(15, 14)
+#define ISP_AF_CTRL_VERT_OFST(x) ((x) << 14)
+#define ISP_AF_CTRL_HORIZ_OFST_MASK GENMASK(17, 16)
+#define ISP_AF_CTRL_HORIZ_OFST(x) ((x) << 16)
+
+#define ISP_AF_HV_SIZE 0xa04c
+#define ISP_AF_HV_SIZE_GLB_WIN_YSIZE_MASK GENMASK(15, 0)
+#define ISP_AF_HV_SIZE_GLB_WIN_YSIZE(x) ((x) << 0)
+#define ISP_AF_HV_SIZE_GLB_WIN_XSIZE_MASK GENMASK(31, 16)
+#define ISP_AF_HV_SIZE_GLB_WIN_XSIZE(x) ((x) << 16)
+
+#define ISP_AF_HV_BLKNUM 0xa050
+#define ISP_AF_HV_BLKNUM_V_NUM_MASK GENMASK(5, 0)
+#define ISP_AF_HV_BLKNUM_V_NUM(x) ((x) << 0)
+#define ISP_AF_HV_BLKNUM_H_NUM_MASK GENMASK(21, 16)
+#define ISP_AF_HV_BLKNUM_H_NUM(x) ((x) << 16)
+
+#define ISP_AF_EN_CTRL 0xa054
+#define ISP_AF_EN_CTRL_STAT_SEL_MASK BIT(21)
+#define ISP_AF_EN_CTRL_STAT_SEL_OLD (0 << 21)
+#define ISP_AF_EN_CTRL_STAT_SEL_NEW BIT(21)
+
+#define ISP_AF_IDX_ADDR 0xa1c0
+#define ISP_AF_IDX_DATA 0xa1c4
+#define ISP_AF_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_AF_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define ISP_AE_CTRL 0xa448
+#define ISP_AE_CTRL_INPUT_2LINE_MASK BIT(7)
+#define ISP_AE_CTRL_INPUT_2LINE_EN BIT(7)
+#define ISP_AE_CTRL_INPUT_2LINE_DIS (0 << 7)
+#define ISP_AE_CTRL_LUMA_MODE_MASK GENMASK(9, 8)
+#define ISP_AE_CTRL_LUMA_MODE_CUR (0 << 8)
+#define ISP_AE_CTRL_LUMA_MODE_MAX BIT(8)
+#define ISP_AE_CTRL_LUMA_MODE_FILTER (2 << 8)
+#define ISP_AE_CTRL_VERT_OFST_MASK GENMASK(25, 24)
+#define ISP_AE_CTRL_VERT_OFST(x) ((x) << 24)
+#define ISP_AE_CTRL_HORIZ_OFST_MASK GENMASK(27, 26)
+#define ISP_AE_CTRL_HORIZ_OFST(x) ((x) << 26)
+
+#define ISP_AE_HV_SIZE 0xa464
+#define ISP_AE_HV_SIZE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_AE_HV_SIZE_VERT_SIZE(x) ((x) << 0)
+#define ISP_AE_HV_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_AE_HV_SIZE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_AE_HV_BLKNUM 0xa468
+#define ISP_AE_HV_BLKNUM_V_NUM_MASK GENMASK(6, 0)
+#define ISP_AE_HV_BLKNUM_V_NUM(x) ((x) << 0)
+#define ISP_AE_HV_BLKNUM_H_NUM_MASK GENMASK(22, 16)
+#define ISP_AE_HV_BLKNUM_H_NUM(x) ((x) << 16)
+
+#define ISP_AE_IDX_ADDR 0xa600
+#define ISP_AE_IDX_DATA 0xa604
+#define ISP_AE_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_AE_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define ISP_AE_BLK_WT_ADDR 0xa608
+#define ISP_AE_BLK_WT_DATA 0xa60c
+#define ISP_AE_BLK_WT_DATA_WT(i, x) (((x) & GENMASK(3, 0)) << ((i) * 4))
+
+#define ISP_AWB_CTRL 0xa834
+#define ISP_AWB_CTRL_VERT_OFST_MASK GENMASK(1, 0)
+#define ISP_AWB_CTRL_VERT_OFST(x) ((x) << 0)
+#define ISP_AWB_CTRL_HORIZ_OFST_MASK GENMASK(3, 2)
+#define ISP_AWB_CTRL_HORIZ_OFST(x) ((x) << 2)
+
+#define ISP_AWB_HV_SIZE 0xa83c
+#define ISP_AWB_HV_SIZE_VERT_SIZE_MASK GENMASK(15, 0)
+#define ISP_AWB_HV_SIZE_VERT_SIZE(x) ((x) << 0)
+#define ISP_AWB_HV_SIZE_HORIZ_SIZE_MASK GENMASK(31, 16)
+#define ISP_AWB_HV_SIZE_HORIZ_SIZE(x) ((x) << 16)
+
+#define ISP_AWB_HV_BLKNUM 0xa840
+#define ISP_AWB_HV_BLKNUM_V_NUM_MASK GENMASK(5, 0)
+#define ISP_AWB_HV_BLKNUM_V_NUM(x) ((x) << 0)
+#define ISP_AWB_HV_BLKNUM_H_NUM_MASK GENMASK(21, 16)
+#define ISP_AWB_HV_BLKNUM_H_NUM(x) ((x) << 16)
+
+#define ISP_AWB_STAT_RG 0xa848
+#define ISP_AWB_STAT_RG_MIN_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_RG_MIN_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_RG_MAX_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_RG_MAX_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_BG 0xa84c
+#define ISP_AWB_STAT_BG_MIN_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_BG_MIN_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_BG_MAX_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_BG_MAX_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_RG_HL 0xa850
+#define ISP_AWB_STAT_RG_HL_LOW_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_RG_HL_LOW_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_RG_HL_HIGH_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_RG_HL_HIGH_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_BG_HL 0xa854
+#define ISP_AWB_STAT_BG_HL_LOW_VALUE_MASK GENMASK(11, 0)
+#define ISP_AWB_STAT_BG_HL_LOW_VALUE(x) ((x) << 0)
+#define ISP_AWB_STAT_BG_HL_HIGH_VALUE_MASK GENMASK(27, 16)
+#define ISP_AWB_STAT_BG_HL_HIGH_VALUE(x) ((x) << 16)
+
+#define ISP_AWB_STAT_CTRL2 0xa858
+#define ISP_AWB_STAT_CTRL2_SATUR_CTRL_MASK BIT(0)
+#define ISP_AWB_STAT_CTRL2_SATUR_CTRL(x) ((x) << 0)
+
+#define ISP_AWB_IDX_ADDR 0xaa00
+#define ISP_AWB_IDX_DATA 0xaa04
+#define ISP_AWB_IDX_DATA_VIDX_DATA(x) (((x) & GENMASK(15, 0)) << 0)
+#define ISP_AWB_IDX_DATA_HIDX_DATA(x) (((x) & GENMASK(15, 0)) << 16)
+
+#define ISP_AWB_BLK_WT_ADDR 0xaa08
+#define ISP_AWB_BLK_WT_DATA 0xaa0c
+#define ISP_AWB_BLK_WT_DATA_WT(i, x) (((x) & GENMASK(3, 0)) << ((i) * 4))
+
+#define ISP_WRMIFX3_0_CH0_CTRL0 0xc400
+#define ISP_WRMIFX3_0_CH0_CTRL0_STRIDE_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_CH0_CTRL0_STRIDE(x) ((x) << 16)
+
+#define ISP_WRMIFX3_0_CH0_CTRL1 0xc404
+#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_MODE_MASK GENMASK(30, 27)
+#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_8BITS BIT(27)
+#define ISP_WRMIFX3_0_CH0_CTRL1_PIX_BITS_16BITS (2 << 27)
+
+#define ISP_WRMIFX3_0_CH1_CTRL0 0xc408
+#define ISP_WRMIFX3_0_CH1_CTRL0_STRIDE_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_CH1_CTRL0_STRIDE(x) ((x) << 16)
+
+#define ISP_WRMIFX3_0_CH1_CTRL1 0xc40c
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_MODE_MASK GENMASK(30, 27)
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_8BITS BIT(27)
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_16BITS (2 << 27)
+#define ISP_WRMIFX3_0_CH1_CTRL1_PIX_BITS_32BITS (3 << 27)
+
+#define ISP_WRMIFX3_0_WIN_LUMA_H 0xc420
+#define ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_LUMA_H_LUMA_HEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_WIN_LUMA_V 0xc424
+#define ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_LUMA_V_LUMA_VEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_WIN_CHROM_H 0xc428
+#define ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_CHROM_H_CHROM_HEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_WIN_CHROM_V 0xc42c
+#define ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND_MASK GENMASK(28, 16)
+#define ISP_WRMIFX3_0_WIN_CHROM_V_CHROM_VEND(x) (((x) - 1) << 16)
+
+#define ISP_WRMIFX3_0_CH0_BADDR 0xc440
+#define ISP_WRMIFX3_0_CH0_BASE_ADDR(x) ((x) >> 4)
+
+#define ISP_WRMIFX3_0_CH1_BADDR 0xc444
+#define ISP_WRMIFX3_0_CH1_BASE_ADDR(x) ((x) >> 4)
+
+#define ISP_WRMIFX3_0_FMT_SIZE 0xc464
+#define ISP_WRMIFX3_0_FMT_SIZE_HSIZE_MASK GENMASK(15, 0)
+#define ISP_WRMIFX3_0_FMT_SIZE_HSIZE(x) ((x) << 0)
+#define ISP_WRMIFX3_0_FMT_SIZE_VSIZE_MASK GENMASK(31, 16)
+#define ISP_WRMIFX3_0_FMT_SIZE_VSIZE(x) ((x) << 16)
+
+#define ISP_WRMIFX3_0_FMT_CTRL 0xc468
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_MASK GENMASK(1, 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_8BIT (0 << 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_10BIT BIT(0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_12BIT (2 << 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_IBITS_16BIT (3 << 0)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_MASK BIT(2)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_VU (0 << 2)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_UV_SWAP_UV BIT(2)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_MASK GENMASK(5, 4)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X1 (0 << 4)
+#define ISP_WRMIFX3_0_FMT_CTRL_MTX_PLANE_X2 BIT(4)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_MASK GENMASK(18, 16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV422 BIT(16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_YUV420 (2 << 16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_Y_ONLY (3 << 16)
+#define ISP_WRMIFX3_0_FMT_CTRL_MODE_OUT_RAW (4 << 16)
+
+#define VIU_DMAWR_BADDR0 0xc840
+#define VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR_MASK GENMASK(27, 0)
+#define VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR(x) ((x) >> 4)
+
+#define VIU_DMAWR_BADDR1 0xc844
+#define VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR_MASK GENMASK(27, 0)
+#define VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR(x) ((x) >> 4)
+
+#define VIU_DMAWR_BADDR2 0xc848
+#define VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR_MASK GENMASK(27, 0)
+#define VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR(x) ((x) >> 4)
+
+#define VIU_DMAWR_SIZE0 0xc854
+#define VIU_DMAWR_SIZE0_AF_STATS_SIZE_MASK GENMASK(15, 0)
+#define VIU_DMAWR_SIZE0_AF_STATS_SIZE(x) ((x) << 0)
+#define VIU_DMAWR_SIZE0_AWB_STATS_SIZE_MASK GENMASK(31, 16)
+#define VIU_DMAWR_SIZE0_AWB_STATS_SIZE(x) ((x) << 16)
+
+#define VIU_DMAWR_SIZE1 0xc858
+#define VIU_DMAWR_SIZE1_AE_STATS_SIZE_MASK GENMASK(15, 0)
+#define VIU_DMAWR_SIZE1_AE_STATS_SIZE(x) ((x) << 0)
+
+#endif
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c
new file mode 100644
index 000000000000..453a889e0b27
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c
@@ -0,0 +1,892 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+#define C3_ISP_RSZ_DEF_PAD_FMT MEDIA_BUS_FMT_YUV10_1X30
+#define C3_ISP_DISP_REG(base, id) ((base) + (id) * 0x400)
+#define C3_ISP_PPS_LUT_H_NUM 33
+#define C3_ISP_PPS_LUT_CTYPE_0 0
+#define C3_ISP_PPS_LUT_CTYPE_2 2
+#define C3_ISP_SCL_EN 1
+#define C3_ISP_SCL_DIS 0
+
+/*
+ * struct c3_isp_rsz_format_info - ISP resizer format information
+ *
+ * @mbus_code: the mbus code
+ * @pads: bitmask detailing valid pads for this mbus_code
+ * @is_raw: the raw format flag of mbus code
+ */
+struct c3_isp_rsz_format_info {
+ u32 mbus_code;
+ u32 pads;
+ bool is_raw;
+};
+
+static const struct c3_isp_rsz_format_info c3_isp_rsz_fmts[] = {
+ /* RAW formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ }, {
+ .mbus_code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = true,
+ },
+ /* YUV formats */
+ {
+ .mbus_code = MEDIA_BUS_FMT_YUV10_1X30,
+ .pads = BIT(C3_ISP_RSZ_PAD_SINK)
+ | BIT(C3_ISP_RSZ_PAD_SOURCE),
+ .is_raw = false,
+ },
+};
+
+/*
+ * struct c3_isp_pps_io_size - ISP scaler input and output size
+ *
+ * @thsize: input horizontal size of after preprocessing
+ * @tvsize: input vertical size of after preprocessing
+ * @ohsize: output horizontal size
+ * @ovsize: output vertical size
+ * @ihsize: input horizontal size
+ * @max_hsize: maximum horizontal size
+ */
+struct c3_isp_pps_io_size {
+ u32 thsize;
+ u32 tvsize;
+ u32 ohsize;
+ u32 ovsize;
+ u32 ihsize;
+ u32 max_hsize;
+};
+
+/* The normal parameters of pps module */
+static const int c3_isp_pps_lut[C3_ISP_PPS_LUT_H_NUM][4] = {
+ { 0, 511, 0, 0}, { -5, 511, 5, 0}, {-10, 511, 11, 0},
+ {-14, 510, 17, -1}, {-18, 508, 23, -1}, {-22, 506, 29, -1},
+ {-25, 503, 36, -2}, {-28, 500, 43, -3}, {-32, 496, 51, -3},
+ {-34, 491, 59, -4}, {-37, 487, 67, -5}, {-39, 482, 75, -6},
+ {-41, 476, 84, -7}, {-42, 470, 92, -8}, {-44, 463, 102, -9},
+ {-45, 456, 111, -10}, {-45, 449, 120, -12}, {-47, 442, 130, -13},
+ {-47, 434, 140, -15}, {-47, 425, 151, -17}, {-47, 416, 161, -18},
+ {-47, 407, 172, -20}, {-47, 398, 182, -21}, {-47, 389, 193, -23},
+ {-46, 379, 204, -25}, {-45, 369, 215, -27}, {-44, 358, 226, -28},
+ {-43, 348, 237, -30}, {-43, 337, 249, -31}, {-41, 326, 260, -33},
+ {-40, 316, 271, -35}, {-39, 305, 282, -36}, {-37, 293, 293, -37}
+};
+
+static const struct c3_isp_rsz_format_info
+*rsz_find_format_by_code(u32 code, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_rsz_fmts); i++) {
+ const struct c3_isp_rsz_format_info *info = &c3_isp_rsz_fmts[i];
+
+ if (info->mbus_code == code && info->pads & BIT(pad))
+ return info;
+ }
+
+ return NULL;
+}
+
+static const struct c3_isp_rsz_format_info
+*rsz_find_format_by_index(u32 index, u32 pad)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_isp_rsz_fmts); i++) {
+ const struct c3_isp_rsz_format_info *info = &c3_isp_rsz_fmts[i];
+
+ if (!(info->pads & BIT(pad)))
+ continue;
+
+ if (!index)
+ return info;
+
+ index--;
+ }
+
+ return NULL;
+}
+
+static void c3_isp_rsz_pps_size(struct c3_isp_resizer *rsz,
+ struct c3_isp_pps_io_size *io_size)
+{
+ int thsize = io_size->thsize;
+ int tvsize = io_size->tvsize;
+ u32 ohsize = io_size->ohsize;
+ u32 ovsize = io_size->ovsize;
+ u32 ihsize = io_size->ihsize;
+ u32 max_hsize = io_size->max_hsize;
+ int h_int;
+ int v_int;
+ int h_fract;
+ int v_fract;
+ int yuv444to422_en;
+
+ /* Calculate the integer part of horizonal scaler step */
+ h_int = thsize / ohsize;
+
+ /* Calculate the vertical part of horizonal scaler step */
+ v_int = tvsize / ovsize;
+
+ /*
+ * Calculate the fraction part of horizonal scaler step.
+ * step_h_fraction = (source / dest) * 2^24,
+ * so step_h_fraction = ((source << 12) / dest) << 12.
+ */
+ h_fract = ((thsize << 12) / ohsize) << 12;
+
+ /*
+ * Calculate the fraction part of vertical scaler step
+ * step_v_fraction = (source / dest) * 2^24,
+ * so step_v_fraction = ((source << 12) / dest) << 12.
+ */
+ v_fract = ((tvsize << 12) / ovsize) << 12;
+
+ yuv444to422_en = ihsize > (max_hsize / 2) ? 1 : 0;
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_444TO422, rsz->id),
+ DISP0_PPS_444TO422_EN_MASK,
+ DISP0_PPS_444TO422_EN(yuv444to422_en));
+
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_VSC_START_PHASE_STEP, rsz->id),
+ DISP0_PPS_VSC_START_PHASE_STEP_VERT_FRAC(v_fract) |
+ DISP0_PPS_VSC_START_PHASE_STEP_VERT_INTE(v_int));
+
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_HSC_START_PHASE_STEP, rsz->id),
+ DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_FRAC(h_fract) |
+ DISP0_PPS_HSC_START_PHASE_STEP_HORIZ_INTE(h_int));
+}
+
+static void c3_isp_rsz_pps_lut(struct c3_isp_resizer *rsz, u32 ctype)
+{
+ unsigned int i;
+
+ /*
+ * Default value of this register is 0, so only need to set
+ * SCALE_LUMA_COEF_S11_MODE and SCALE_LUMA_CTYPE. This register needs
+ * to be written in one time.
+ */
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_IDX_LUMA, rsz->id),
+ ISP_SCALE0_COEF_IDX_LUMA_COEF_S11_MODE_EN |
+ ISP_SCALE0_COEF_IDX_LUMA_CTYPE(ctype));
+
+ for (i = 0; i < C3_ISP_PPS_LUT_H_NUM; i++) {
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_LUMA, rsz->id),
+ ISP_SCALE0_COEF_LUMA_DATA0(c3_isp_pps_lut[i][0]) |
+ ISP_SCALE0_COEF_LUMA_DATA1(c3_isp_pps_lut[i][1]));
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_LUMA, rsz->id),
+ ISP_SCALE0_COEF_LUMA_DATA0(c3_isp_pps_lut[i][2]) |
+ ISP_SCALE0_COEF_LUMA_DATA1(c3_isp_pps_lut[i][3]));
+ }
+
+ /*
+ * Default value of this register is 0, so only need to set
+ * SCALE_CHRO_COEF_S11_MODE and SCALE_CHRO_CTYPE. This register needs
+ * to be written in one time.
+ */
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_IDX_CHRO, rsz->id),
+ ISP_SCALE0_COEF_IDX_CHRO_COEF_S11_MODE_EN |
+ ISP_SCALE0_COEF_IDX_CHRO_CTYPE(ctype));
+
+ for (i = 0; i < C3_ISP_PPS_LUT_H_NUM; i++) {
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_CHRO, rsz->id),
+ ISP_SCALE0_COEF_CHRO_DATA0(c3_isp_pps_lut[i][0]) |
+ ISP_SCALE0_COEF_CHRO_DATA1(c3_isp_pps_lut[i][1]));
+ c3_isp_write(rsz->isp,
+ C3_ISP_DISP_REG(ISP_SCALE0_COEF_CHRO, rsz->id),
+ ISP_SCALE0_COEF_CHRO_DATA0(c3_isp_pps_lut[i][2]) |
+ ISP_SCALE0_COEF_CHRO_DATA1(c3_isp_pps_lut[i][3]));
+ }
+}
+
+static void c3_isp_rsz_pps_disable(struct c3_isp_resizer *rsz)
+{
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_HSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_HSC_DIS);
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_VSC_DIS);
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_DIS);
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREHSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREHSC_DIS);
+}
+
+static int c3_isp_rsz_pps_enable(struct c3_isp_resizer *rsz,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+ int max_hsize;
+ int hsc_en, vsc_en;
+ int preh_en, prev_en;
+ u32 prehsc_rate;
+ u32 prevsc_flt_num;
+ int pre_vscale_max_hsize;
+ u32 ihsize_after_pre_hsc;
+ u32 ihsize_after_pre_hsc_alt;
+ u32 vsc_tap_num_alt;
+ u32 ihsize;
+ u32 ivsize;
+ struct c3_isp_pps_io_size io_size;
+
+ crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK);
+ cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK);
+
+ ihsize = crop->width;
+ ivsize = crop->height;
+
+ hsc_en = (ihsize == cmps->width) ? C3_ISP_SCL_DIS : C3_ISP_SCL_EN;
+ vsc_en = (ivsize == cmps->height) ? C3_ISP_SCL_DIS : C3_ISP_SCL_EN;
+
+ /* Disable pps when there no need to use pps */
+ if (!hsc_en && !vsc_en) {
+ c3_isp_rsz_pps_disable(rsz);
+ return 0;
+ }
+
+ /* Pre-scale needs to be enable if the down scaling factor exceeds 4 */
+ preh_en = (ihsize > cmps->width * 4) ? C3_ISP_SCL_EN : C3_ISP_SCL_DIS;
+ prev_en = (ivsize > cmps->height * 4) ? C3_ISP_SCL_EN : C3_ISP_SCL_DIS;
+
+ if (rsz->id == C3_ISP_RSZ_2) {
+ max_hsize = C3_ISP_MAX_WIDTH;
+
+ /* Set vertical tap number */
+ prevsc_flt_num = 4;
+
+ /* Set the max hsize of pre-vertical scale */
+ pre_vscale_max_hsize = max_hsize / 2;
+ } else {
+ max_hsize = C3_ISP_DEFAULT_WIDTH;
+
+ /* Set vertical tap number and the max hsize of pre-vertical */
+ if (ihsize > (max_hsize / 2) &&
+ ihsize <= max_hsize && prev_en) {
+ prevsc_flt_num = 2;
+ pre_vscale_max_hsize = max_hsize;
+ } else {
+ prevsc_flt_num = 4;
+ pre_vscale_max_hsize = max_hsize / 2;
+ }
+ }
+
+ /*
+ * Set pre-horizonal scale rate and the hsize of after
+ * pre-horizonal scale.
+ */
+ if (preh_en) {
+ prehsc_rate = 1;
+ ihsize_after_pre_hsc = DIV_ROUND_UP(ihsize, 2);
+ } else {
+ prehsc_rate = 0;
+ ihsize_after_pre_hsc = ihsize;
+ }
+
+ /* Change pre-horizonal scale rate */
+ if (prev_en && ihsize_after_pre_hsc >= pre_vscale_max_hsize)
+ prehsc_rate += 1;
+
+ /* Set the actual hsize of after pre-horizonal scale */
+ if (preh_en)
+ ihsize_after_pre_hsc_alt =
+ DIV_ROUND_UP(ihsize, 1 << prehsc_rate);
+ else
+ ihsize_after_pre_hsc_alt = ihsize;
+
+ /* Set vertical scaler bank length */
+ if (ihsize_after_pre_hsc_alt <= (max_hsize / 2))
+ vsc_tap_num_alt = 4;
+ else if (ihsize_after_pre_hsc_alt <= max_hsize)
+ vsc_tap_num_alt = prev_en ? 2 : 4;
+ else
+ vsc_tap_num_alt = prev_en ? 4 : 2;
+
+ io_size.thsize = ihsize_after_pre_hsc_alt;
+ io_size.tvsize = prev_en ? DIV_ROUND_UP(ivsize, 2) : ivsize;
+ io_size.ohsize = cmps->width;
+ io_size.ovsize = cmps->height;
+ io_size.ihsize = ihsize;
+ io_size.max_hsize = max_hsize;
+
+ c3_isp_rsz_pps_size(rsz, &io_size);
+ c3_isp_rsz_pps_lut(rsz, C3_ISP_PPS_LUT_CTYPE_0);
+ c3_isp_rsz_pps_lut(rsz, C3_ISP_PPS_LUT_CTYPE_2);
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_TAP_NUM_MASK,
+ DISP0_PPS_SCALE_EN_VSC_TAP_NUM(vsc_tap_num_alt));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_FLT_NUM(prevsc_flt_num));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_RATE_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_RATE(prev_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREHSC_RATE_MASK,
+ DISP0_PPS_SCALE_EN_PREHSC_RATE(prehsc_rate));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_HSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_HSC_EN(hsc_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_VSC_EN(vsc_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREVSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREVSC_EN(prev_en));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_PREHSC_EN_MASK,
+ DISP0_PPS_SCALE_EN_PREHSC_EN(preh_en));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS_MASK,
+ DISP0_PPS_SCALE_EN_HSC_NOR_RS_BITS(9));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_PPS_SCALE_EN, rsz->id),
+ DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS_MASK,
+ DISP0_PPS_SCALE_EN_VSC_NOR_RS_BITS(9));
+
+ return 0;
+}
+
+static void c3_isp_rsz_start(struct c3_isp_resizer *rsz,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ const struct c3_isp_rsz_format_info *rsz_fmt;
+ struct v4l2_rect *sink_crop;
+ u32 mask;
+ u32 val;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK);
+ sink_crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE);
+ rsz_fmt = rsz_find_format_by_code(sink_fmt->code, C3_ISP_RSZ_PAD_SINK);
+
+ if (rsz->id == C3_ISP_RSZ_0) {
+ mask = ISP_TOP_DISPIN_SEL_DISP0_MASK;
+ val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP0_MIPI_OUT
+ : ISP_TOP_DISPIN_SEL_DISP0_CORE_OUT;
+ } else if (rsz->id == C3_ISP_RSZ_1) {
+ mask = ISP_TOP_DISPIN_SEL_DISP1_MASK;
+ val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP1_MIPI_OUT
+ : ISP_TOP_DISPIN_SEL_DISP1_CORE_OUT;
+ } else {
+ mask = ISP_TOP_DISPIN_SEL_DISP2_MASK;
+ val = rsz_fmt->is_raw ? ISP_TOP_DISPIN_SEL_DISP2_MIPI_OUT
+ : ISP_TOP_DISPIN_SEL_DISP2_CORE_OUT;
+ }
+
+ c3_isp_update_bits(rsz->isp, ISP_TOP_DISPIN_SEL, mask, val);
+
+ c3_isp_write(rsz->isp, C3_ISP_DISP_REG(ISP_DISP0_TOP_IN_SIZE, rsz->id),
+ ISP_DISP0_TOP_IN_SIZE_HSIZE(sink_fmt->width) |
+ ISP_DISP0_TOP_IN_SIZE_VSIZE(sink_fmt->height));
+
+ c3_isp_write(rsz->isp, C3_ISP_DISP_REG(DISP0_TOP_CRP2_START, rsz->id),
+ DISP0_TOP_CRP2_START_V_START(sink_crop->top) |
+ DISP0_TOP_CRP2_START_H_START(sink_crop->left));
+
+ c3_isp_write(rsz->isp, C3_ISP_DISP_REG(DISP0_TOP_CRP2_SIZE, rsz->id),
+ DISP0_TOP_CRP2_SIZE_V_SIZE(sink_crop->height) |
+ DISP0_TOP_CRP2_SIZE_H_SIZE(sink_crop->width));
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_TOP_CTRL, rsz->id),
+ DISP0_TOP_TOP_CTRL_CROP2_EN_MASK,
+ DISP0_TOP_TOP_CTRL_CROP2_EN);
+
+ if (!rsz_fmt->is_raw)
+ c3_isp_rsz_pps_enable(rsz, state);
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_OUT_SIZE, rsz->id),
+ DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT_MASK,
+ DISP0_TOP_OUT_SIZE_SCL_OUT_HEIGHT(src_fmt->height));
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_OUT_SIZE, rsz->id),
+ DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH_MASK,
+ DISP0_TOP_OUT_SIZE_SCL_OUT_WIDTH(src_fmt->width));
+
+ if (rsz->id == C3_ISP_RSZ_0) {
+ mask = ISP_TOP_PATH_EN_DISP0_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP0_EN;
+ } else if (rsz->id == C3_ISP_RSZ_1) {
+ mask = ISP_TOP_PATH_EN_DISP1_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP1_EN;
+ } else {
+ mask = ISP_TOP_PATH_EN_DISP2_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP2_EN;
+ }
+
+ c3_isp_update_bits(rsz->isp, ISP_TOP_PATH_EN, mask, val);
+}
+
+static void c3_isp_rsz_stop(struct c3_isp_resizer *rsz)
+{
+ u32 mask;
+ u32 val;
+
+ if (rsz->id == C3_ISP_RSZ_0) {
+ mask = ISP_TOP_PATH_EN_DISP0_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP0_DIS;
+ } else if (rsz->id == C3_ISP_RSZ_1) {
+ mask = ISP_TOP_PATH_EN_DISP1_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP1_DIS;
+ } else {
+ mask = ISP_TOP_PATH_EN_DISP2_EN_MASK;
+ val = ISP_TOP_PATH_EN_DISP2_DIS;
+ }
+
+ c3_isp_update_bits(rsz->isp, ISP_TOP_PATH_EN, mask, val);
+
+ c3_isp_update_bits(rsz->isp,
+ C3_ISP_DISP_REG(DISP0_TOP_TOP_CTRL, rsz->id),
+ DISP0_TOP_TOP_CTRL_CROP2_EN_MASK,
+ DISP0_TOP_TOP_CTRL_CROP2_DIS);
+
+ c3_isp_rsz_pps_disable(rsz);
+}
+
+static int c3_isp_rsz_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_resizer *rsz = v4l2_get_subdevdata(sd);
+
+ c3_isp_rsz_start(rsz, state);
+
+ c3_isp_params_pre_cfg(rsz->isp);
+ c3_isp_stats_pre_cfg(rsz->isp);
+
+ return v4l2_subdev_enable_streams(rsz->src_sd, rsz->src_pad, BIT(0));
+}
+
+static int c3_isp_rsz_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_isp_resizer *rsz = v4l2_get_subdevdata(sd);
+
+ c3_isp_rsz_stop(rsz);
+
+ return v4l2_subdev_disable_streams(rsz->src_sd, rsz->src_pad, BIT(0));
+}
+
+static int c3_isp_rsz_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct c3_isp_rsz_format_info *info;
+
+ info = rsz_find_format_by_index(code->index, code->pad);
+ if (!info)
+ return -EINVAL;
+
+ code->code = info->mbus_code;
+
+ return 0;
+}
+
+static void c3_isp_rsz_set_sink_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_rect *sink_crop;
+ struct v4l2_rect *sink_cmps;
+ const struct c3_isp_rsz_format_info *rsz_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, format->pad);
+ sink_crop = v4l2_subdev_state_get_crop(state, format->pad);
+ sink_cmps = v4l2_subdev_state_get_compose(state, format->pad);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE);
+
+ rsz_fmt = rsz_find_format_by_code(format->format.code, format->pad);
+ if (rsz_fmt)
+ sink_fmt->code = format->format.code;
+ else
+ sink_fmt->code = C3_ISP_RSZ_DEF_PAD_FMT;
+
+ sink_fmt->width = clamp_t(u32, format->format.width,
+ C3_ISP_MIN_WIDTH, C3_ISP_MAX_WIDTH);
+ sink_fmt->height = clamp_t(u32, format->format.height,
+ C3_ISP_MIN_HEIGHT, C3_ISP_MAX_HEIGHT);
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ if (rsz_fmt && rsz_fmt->is_raw) {
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ } else {
+ sink_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ sink_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ sink_crop->width = sink_fmt->width;
+ sink_crop->height = sink_fmt->height;
+ sink_crop->left = 0;
+ sink_crop->top = 0;
+
+ sink_cmps->width = sink_crop->width;
+ sink_cmps->height = sink_crop->height;
+ sink_cmps->left = 0;
+ sink_cmps->top = 0;
+
+ src_fmt->code = sink_fmt->code;
+ src_fmt->width = sink_cmps->width;
+ src_fmt->height = sink_cmps->height;
+
+ format->format = *sink_fmt;
+}
+
+static void c3_isp_rsz_set_source_fmt(struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_rect *sink_cmps;
+ const struct c3_isp_rsz_format_info *rsz_fmt;
+
+ src_fmt = v4l2_subdev_state_get_format(state, format->pad);
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK);
+ sink_cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK);
+
+ src_fmt->code = sink_fmt->code;
+ src_fmt->width = sink_cmps->width;
+ src_fmt->height = sink_cmps->height;
+ src_fmt->field = V4L2_FIELD_NONE;
+ src_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+
+ rsz_fmt = rsz_find_format_by_code(src_fmt->code, format->pad);
+ if (rsz_fmt->is_raw) {
+ src_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ src_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ } else {
+ src_fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ src_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ src_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ }
+
+ format->format = *src_fmt;
+}
+
+static int c3_isp_rsz_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ if (format->pad == C3_ISP_RSZ_PAD_SINK)
+ c3_isp_rsz_set_sink_fmt(state, format);
+ else
+ c3_isp_rsz_set_source_fmt(state, format);
+
+ return 0;
+}
+
+static int c3_isp_rsz_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+
+ if (sel->pad == C3_ISP_RSZ_PAD_SOURCE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ fmt = v4l2_subdev_state_get_format(state, sel->pad);
+ sel->r.width = fmt->width;
+ sel->r.height = fmt->height;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ break;
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ sel->r.width = crop->width;
+ sel->r.height = crop->height;
+ sel->r.left = 0;
+ sel->r.top = 0;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ sel->r = *crop;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ cmps = v4l2_subdev_state_get_compose(state, sel->pad);
+ sel->r = *cmps;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_isp_rsz_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+
+ if (sel->pad == C3_ISP_RSZ_PAD_SOURCE)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ fmt = v4l2_subdev_state_get_format(state, sel->pad);
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ cmps = v4l2_subdev_state_get_compose(state, sel->pad);
+ src_fmt = v4l2_subdev_state_get_format(state,
+ C3_ISP_RSZ_PAD_SOURCE);
+
+ sel->r.left = clamp_t(s32, sel->r.left, 0, fmt->width - 1);
+ sel->r.top = clamp_t(s32, sel->r.top, 0, fmt->height - 1);
+ sel->r.width = clamp(sel->r.width, C3_ISP_MIN_WIDTH,
+ fmt->width - sel->r.left);
+ sel->r.height = clamp(sel->r.height, C3_ISP_MIN_HEIGHT,
+ fmt->height - sel->r.top);
+
+ crop->width = ALIGN(sel->r.width, 2);
+ crop->height = ALIGN(sel->r.height, 2);
+ crop->left = sel->r.left;
+ crop->top = sel->r.top;
+
+ *cmps = *crop;
+
+ src_fmt->code = fmt->code;
+ src_fmt->width = cmps->width;
+ src_fmt->height = cmps->height;
+
+ sel->r = *crop;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ crop = v4l2_subdev_state_get_crop(state, sel->pad);
+ cmps = v4l2_subdev_state_get_compose(state, sel->pad);
+
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = clamp(sel->r.width, C3_ISP_MIN_WIDTH,
+ crop->width);
+ sel->r.height = clamp(sel->r.height, C3_ISP_MIN_HEIGHT,
+ crop->height);
+
+ cmps->width = ALIGN(sel->r.width, 2);
+ cmps->height = ALIGN(sel->r.height, 2);
+ cmps->left = sel->r.left;
+ cmps->top = sel->r.top;
+
+ sel->r = *cmps;
+
+ fmt = v4l2_subdev_state_get_format(state,
+ C3_ISP_RSZ_PAD_SOURCE);
+ fmt->width = cmps->width;
+ fmt->height = cmps->height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_isp_rsz_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_rect *crop;
+ struct v4l2_rect *cmps;
+
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SINK);
+ fmt->width = C3_ISP_DEFAULT_WIDTH;
+ fmt->height = C3_ISP_DEFAULT_HEIGHT;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_RSZ_DEF_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ crop = v4l2_subdev_state_get_crop(state, C3_ISP_RSZ_PAD_SINK);
+ crop->width = C3_ISP_DEFAULT_WIDTH;
+ crop->height = C3_ISP_DEFAULT_HEIGHT;
+ crop->left = 0;
+ crop->top = 0;
+
+ cmps = v4l2_subdev_state_get_compose(state, C3_ISP_RSZ_PAD_SINK);
+ cmps->width = C3_ISP_DEFAULT_WIDTH;
+ cmps->height = C3_ISP_DEFAULT_HEIGHT;
+ cmps->left = 0;
+ cmps->top = 0;
+
+ fmt = v4l2_subdev_state_get_format(state, C3_ISP_RSZ_PAD_SOURCE);
+ fmt->width = cmps->width;
+ fmt->height = cmps->height;
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->code = C3_ISP_RSZ_DEF_PAD_FMT;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+ fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops c3_isp_rsz_pad_ops = {
+ .enum_mbus_code = c3_isp_rsz_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_isp_rsz_set_fmt,
+ .get_selection = c3_isp_rsz_get_selection,
+ .set_selection = c3_isp_rsz_set_selection,
+ .enable_streams = c3_isp_rsz_enable_streams,
+ .disable_streams = c3_isp_rsz_disable_streams,
+};
+
+static const struct v4l2_subdev_ops c3_isp_rsz_subdev_ops = {
+ .pad = &c3_isp_rsz_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_isp_rsz_internal_ops = {
+ .init_state = c3_isp_rsz_init_state,
+};
+
+/* Media entity operations */
+static const struct media_entity_operations c3_isp_rsz_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+static int c3_isp_rsz_register(struct c3_isp_resizer *rsz)
+{
+ struct v4l2_subdev *sd = &rsz->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_isp_rsz_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &c3_isp_rsz_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "c3-isp-resizer%u", rsz->id);
+
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+ sd->entity.ops = &c3_isp_rsz_entity_ops;
+
+ sd->dev = rsz->isp->dev;
+ v4l2_set_subdevdata(sd, rsz);
+
+ rsz->pads[C3_ISP_RSZ_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ rsz->pads[C3_ISP_RSZ_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_ISP_RSZ_PAD_MAX,
+ rsz->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(&rsz->isp->v4l2_dev, sd);
+ if (ret)
+ goto err_subdev_cleanup;
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(sd);
+err_entity_cleanup:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+static void c3_isp_rsz_unregister(struct c3_isp_resizer *rsz)
+{
+ struct v4l2_subdev *sd = &rsz->sd;
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sd->entity);
+}
+
+int c3_isp_resizers_register(struct c3_isp_device *isp)
+{
+ int ret;
+
+ for (unsigned int i = C3_ISP_RSZ_0; i < C3_ISP_NUM_RSZ; i++) {
+ struct c3_isp_resizer *rsz = &isp->resizers[i];
+
+ rsz->id = i;
+ rsz->isp = isp;
+ rsz->src_sd = &isp->core.sd;
+ rsz->src_pad = C3_ISP_CORE_PAD_SOURCE_VIDEO_0 + i;
+
+ ret = c3_isp_rsz_register(rsz);
+ if (ret) {
+ rsz->isp = NULL;
+ c3_isp_resizers_unregister(isp);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void c3_isp_resizers_unregister(struct c3_isp_device *isp)
+{
+ for (unsigned int i = C3_ISP_RSZ_0; i < C3_ISP_NUM_RSZ; i++) {
+ struct c3_isp_resizer *rsz = &isp->resizers[i];
+
+ if (rsz->isp)
+ c3_isp_rsz_unregister(rsz);
+ }
+}
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c
new file mode 100644
index 000000000000..8a5d7e1a30c9
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-stats.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/cleanup.h>
+#include <linux/media/amlogic/c3-isp-config.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "c3-isp-common.h"
+#include "c3-isp-regs.h"
+
+/* Hardware configuration */
+
+static void c3_isp_stats_cfg_dmawr_addr(struct c3_isp_stats *stats)
+{
+ u32 awb_dma_size = sizeof(struct c3_isp_awb_stats);
+ u32 ae_dma_size = sizeof(struct c3_isp_ae_stats);
+ u32 awb_dma_addr = stats->buff->dma_addr;
+ u32 af_dma_addr;
+ u32 ae_dma_addr;
+
+ ae_dma_addr = awb_dma_addr + awb_dma_size;
+ af_dma_addr = ae_dma_addr + ae_dma_size;
+
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR0,
+ VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR_MASK,
+ VIU_DMAWR_BADDR0_AF_STATS_BASE_ADDR(af_dma_addr));
+
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR1,
+ VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR_MASK,
+ VIU_DMAWR_BADDR1_AWB_STATS_BASE_ADDR(awb_dma_addr));
+
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_BADDR2,
+ VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR_MASK,
+ VIU_DMAWR_BADDR2_AE_STATS_BASE_ADDR(ae_dma_addr));
+}
+
+static void c3_isp_stats_cfg_buff(struct c3_isp_stats *stats)
+{
+ stats->buff =
+ list_first_entry_or_null(&stats->pending,
+ struct c3_isp_stats_buffer, list);
+ if (stats->buff) {
+ c3_isp_stats_cfg_dmawr_addr(stats);
+ list_del(&stats->buff->list);
+ }
+}
+
+void c3_isp_stats_pre_cfg(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+ u32 dma_size;
+
+ c3_isp_update_bits(stats->isp, ISP_AF_EN_CTRL,
+ ISP_AF_EN_CTRL_STAT_SEL_MASK,
+ ISP_AF_EN_CTRL_STAT_SEL_NEW);
+ c3_isp_update_bits(stats->isp, ISP_AE_CTRL,
+ ISP_AE_CTRL_LUMA_MODE_MASK,
+ ISP_AE_CTRL_LUMA_MODE_FILTER);
+
+ /* The unit of dma_size is 16 bytes */
+ dma_size = sizeof(struct c3_isp_af_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES;
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE0,
+ VIU_DMAWR_SIZE0_AF_STATS_SIZE_MASK,
+ VIU_DMAWR_SIZE0_AF_STATS_SIZE(dma_size));
+
+ dma_size = sizeof(struct c3_isp_awb_stats) /
+ C3_ISP_DMA_SIZE_ALIGN_BYTES;
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE0,
+ VIU_DMAWR_SIZE0_AWB_STATS_SIZE_MASK,
+ VIU_DMAWR_SIZE0_AWB_STATS_SIZE(dma_size));
+
+ dma_size = sizeof(struct c3_isp_ae_stats) / C3_ISP_DMA_SIZE_ALIGN_BYTES;
+ c3_isp_update_bits(stats->isp, VIU_DMAWR_SIZE1,
+ VIU_DMAWR_SIZE1_AE_STATS_SIZE_MASK,
+ VIU_DMAWR_SIZE1_AE_STATS_SIZE(dma_size));
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ c3_isp_stats_cfg_buff(stats);
+}
+
+static int c3_isp_stats_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, C3_ISP_DRIVER_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "AML C3 ISP", sizeof(cap->card));
+
+ return 0;
+}
+
+static int c3_isp_stats_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct c3_isp_stats *stats = video_drvdata(file);
+
+ if (f->index > 0 || f->type != stats->vb2_q.type)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_C3ISP_STATS;
+
+ return 0;
+}
+
+static int c3_isp_stats_g_fmt(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct c3_isp_stats *stats = video_drvdata(file);
+
+ f->fmt.meta = stats->vfmt.fmt.meta;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp_stats_v4l2_ioctl_ops = {
+ .vidioc_querycap = c3_isp_stats_querycap,
+ .vidioc_enum_fmt_meta_cap = c3_isp_stats_enum_fmt,
+ .vidioc_g_fmt_meta_cap = c3_isp_stats_g_fmt,
+ .vidioc_s_fmt_meta_cap = c3_isp_stats_g_fmt,
+ .vidioc_try_fmt_meta_cap = c3_isp_stats_g_fmt,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct v4l2_file_operations isp_stats_v4l2_fops = {
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int c3_isp_stats_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ if (*num_planes) {
+ if (*num_planes != 1)
+ return -EINVAL;
+
+ if (sizes[0] < sizeof(struct c3_isp_stats_info))
+ return -EINVAL;
+
+ return 0;
+ }
+
+ *num_planes = 1;
+ sizes[0] = sizeof(struct c3_isp_stats_info);
+
+ return 0;
+}
+
+static void c3_isp_stats_vb2_buf_queue(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_stats_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_stats_buffer, vb);
+ struct c3_isp_stats *stats = vb2_get_drv_priv(vb->vb2_queue);
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ list_add_tail(&buf->list, &stats->pending);
+}
+
+static int c3_isp_stats_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct c3_isp_stats *stats = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned int size = stats->vfmt.fmt.meta.buffersize;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(stats->isp->dev,
+ "User buffer too small (%ld < %u)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+
+ return 0;
+}
+
+static int c3_isp_stats_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+ struct c3_isp_stats_buffer *buf =
+ container_of(v4l2_buf, struct c3_isp_stats_buffer, vb);
+
+ buf->dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+
+ return 0;
+}
+
+static void c3_isp_stats_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct c3_isp_stats *stats = vb2_get_drv_priv(q);
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ if (stats->buff) {
+ vb2_buffer_done(&stats->buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ stats->buff = NULL;
+ }
+
+ while (!list_empty(&stats->pending)) {
+ struct c3_isp_stats_buffer *buff;
+
+ buff = list_first_entry(&stats->pending,
+ struct c3_isp_stats_buffer, list);
+ list_del(&buff->list);
+ vb2_buffer_done(&buff->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static const struct vb2_ops isp_stats_vb2_ops = {
+ .queue_setup = c3_isp_stats_vb2_queue_setup,
+ .buf_queue = c3_isp_stats_vb2_buf_queue,
+ .buf_prepare = c3_isp_stats_vb2_buf_prepare,
+ .buf_init = c3_isp_stats_vb2_buf_init,
+ .stop_streaming = c3_isp_stats_vb2_stop_streaming,
+};
+
+int c3_isp_stats_register(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+ struct video_device *vdev = &stats->vdev;
+ struct vb2_queue *vb2_q = &stats->vb2_q;
+ int ret;
+
+ memset(stats, 0, sizeof(*stats));
+ stats->vfmt.fmt.meta.dataformat = V4L2_META_FMT_C3ISP_STATS;
+ stats->vfmt.fmt.meta.buffersize = sizeof(struct c3_isp_stats_info);
+ stats->isp = isp;
+ INIT_LIST_HEAD(&stats->pending);
+ spin_lock_init(&stats->buff_lock);
+
+ mutex_init(&stats->lock);
+
+ snprintf(vdev->name, sizeof(vdev->name), "c3-isp-stats");
+ vdev->fops = &isp_stats_v4l2_fops;
+ vdev->ioctl_ops = &isp_stats_v4l2_ioctl_ops;
+ vdev->v4l2_dev = &isp->v4l2_dev;
+ vdev->lock = &stats->lock;
+ vdev->minor = -1;
+ vdev->queue = vb2_q;
+ vdev->release = video_device_release_empty;
+ vdev->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+ video_set_drvdata(vdev, stats);
+
+ vb2_q->drv_priv = stats;
+ vb2_q->mem_ops = &vb2_dma_contig_memops;
+ vb2_q->ops = &isp_stats_vb2_ops;
+ vb2_q->type = V4L2_BUF_TYPE_META_CAPTURE;
+ vb2_q->io_modes = VB2_DMABUF | VB2_MMAP;
+ vb2_q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ vb2_q->buf_struct_size = sizeof(struct c3_isp_stats_buffer);
+ vb2_q->dev = isp->dev;
+ vb2_q->lock = &stats->lock;
+ vb2_q->min_queued_buffers = 2;
+
+ ret = vb2_queue_init(vb2_q);
+ if (ret)
+ goto err_destroy;
+
+ stats->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vdev->entity, 1, &stats->pad);
+ if (ret)
+ goto err_queue_release;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(isp->dev,
+ "Failed to register %s: %d\n", vdev->name, ret);
+ goto err_entity_cleanup;
+ }
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_queue_release:
+ vb2_queue_release(vb2_q);
+err_destroy:
+ mutex_destroy(&stats->lock);
+ return ret;
+}
+
+void c3_isp_stats_unregister(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+
+ vb2_queue_release(&stats->vb2_q);
+ media_entity_cleanup(&stats->vdev.entity);
+ video_unregister_device(&stats->vdev);
+ mutex_destroy(&stats->lock);
+}
+
+void c3_isp_stats_isr(struct c3_isp_device *isp)
+{
+ struct c3_isp_stats *stats = &isp->stats;
+
+ guard(spinlock_irqsave)(&stats->buff_lock);
+
+ if (stats->buff) {
+ stats->buff->vb.sequence = stats->isp->frm_sequence;
+ stats->buff->vb.vb2_buf.timestamp = ktime_get();
+ stats->buff->vb.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&stats->buff->vb.vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ c3_isp_stats_cfg_buff(stats);
+}
diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig b/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig
new file mode 100644
index 000000000000..bf19059b3543
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-adapter/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_C3_MIPI_ADAPTER
+ tristate "Amlogic C3 MIPI adapter"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on OF
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ Video4Linux2 driver for Amlogic C3 MIPI adapter.
+ C3 MIPI adapter mainly responsible for organizing
+ MIPI data and sending raw data to ISP pipeline.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile b/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile
new file mode 100644
index 000000000000..216fc310c5b4
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-adapter/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_VIDEO_C3_MIPI_ADAPTER) += c3-mipi-adap.o
diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c
new file mode 100644
index 000000000000..4bd98fb9c7e9
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c
@@ -0,0 +1,842 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/*
+ * Adapter Block Diagram
+ * ---------------------
+ *
+ * +--------------------------------------------+
+ * | Adapter |
+ * |--------------------------------------------|
+ * +------------+ | | | | | +-----+
+ * | MIPI CSI-2 |--->| Frontend -> DDR_RD0 -> PIXEL0 -> ALIGNMENT |--->| ISP |
+ * +------------+ | | | | | +-----+
+ * +--------------------------------------------+
+ *
+ */
+
+/* C3 adapter submodule definition */
+enum {
+ SUBMD_TOP,
+ SUBMD_FD,
+ SUBMD_RD,
+};
+
+#define ADAP_SUBMD_MASK GENMASK(17, 16)
+#define ADAP_SUBMD_SHIFT 16
+#define ADAP_SUBMD(x) (((x) & (ADAP_SUBMD_MASK)) >> (ADAP_SUBMD_SHIFT))
+#define ADAP_REG_ADDR_MASK GENMASK(15, 0)
+#define ADAP_REG_ADDR(x) ((x) & (ADAP_REG_ADDR_MASK))
+#define ADAP_REG_T(x) ((SUBMD_TOP << ADAP_SUBMD_SHIFT) | (x))
+#define ADAP_REG_F(x) ((SUBMD_FD << ADAP_SUBMD_SHIFT) | (x))
+#define ADAP_REG_R(x) ((SUBMD_RD << ADAP_SUBMD_SHIFT) | (x))
+
+#define MIPI_ADAP_CLOCK_NUM_MAX 3
+#define MIPI_ADAP_SUBDEV_NAME "c3-mipi-adapter"
+
+/* C3 MIPI adapter TOP register */
+#define MIPI_TOP_CTRL0 ADAP_REG_T(0x00)
+#define MIPI_TOP_CTRL0_RST_ADAPTER_MASK BIT(1)
+#define MIPI_TOP_CTRL0_RST_ADAPTER_APPLY BIT(1)
+#define MIPI_TOP_CTRL0_RST_ADAPTER_EXIT (0 << 1)
+
+#define MIPI_ADAPT_DE_CTRL0 ADAP_REG_T(0x40)
+#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK BIT(3)
+#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN BIT(3)
+#define MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_DIS (0 << 3)
+#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK BIT(7)
+#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN BIT(7)
+#define MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_DIS (0 << 7)
+
+/* C3 MIPI adapter FRONTEND register */
+#define CSI2_CLK_RESET ADAP_REG_F(0x00)
+#define CSI2_CLK_RESET_SW_RESET_MASK BIT(0)
+#define CSI2_CLK_RESET_SW_RESET_APPLY BIT(0)
+#define CSI2_CLK_RESET_SW_RESET_RELEASE (0 << 0)
+#define CSI2_CLK_RESET_CLK_ENABLE_MASK BIT(1)
+#define CSI2_CLK_RESET_CLK_ENABLE_EN BIT(1)
+#define CSI2_CLK_RESET_CLK_ENABLE_DIS (0 << 1)
+
+#define CSI2_GEN_CTRL0 ADAP_REG_F(0x04)
+#define CSI2_GEN_CTRL0_VC0_MASK BIT(0)
+#define CSI2_GEN_CTRL0_VC0_EN BIT(0)
+#define CSI2_GEN_CTRL0_VC0_DIS (0 << 0)
+#define CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK GENMASK(20, 16)
+#define CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW BIT(16)
+#define CSI2_GEN_CTRL0_ENABLE_PACKETS_YUV (2 << 16)
+
+#define CSI2_X_START_END_ISP ADAP_REG_F(0x0c)
+#define CSI2_X_START_END_ISP_X_START_MASK GENMASK(15, 0)
+#define CSI2_X_START_END_ISP_X_START(x) ((x) << 0)
+#define CSI2_X_START_END_ISP_X_END_MASK GENMASK(31, 16)
+#define CSI2_X_START_END_ISP_X_END(x) (((x) - 1) << 16)
+
+#define CSI2_Y_START_END_ISP ADAP_REG_F(0x10)
+#define CSI2_Y_START_END_ISP_Y_START_MASK GENMASK(15, 0)
+#define CSI2_Y_START_END_ISP_Y_START(x) ((x) << 0)
+#define CSI2_Y_START_END_ISP_Y_END_MASK GENMASK(31, 16)
+#define CSI2_Y_START_END_ISP_Y_END(x) (((x) - 1) << 16)
+
+#define CSI2_VC_MODE ADAP_REG_F(0x1c)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_MASK GENMASK(19, 16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_0 BIT(16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_1 (2 << 16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_2 (4 << 16)
+#define CSI2_VC_MODE_VS_ISP_SEL_VC_3 (8 << 16)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_MASK GENMASK(23, 20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_0 BIT(20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_1 (2 << 20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_2 (4 << 20)
+#define CSI2_VC_MODE_HS_ISP_SEL_VC_3 (8 << 20)
+
+/* C3 MIPI adapter READER register */
+#define MIPI_ADAPT_DDR_RD0_CNTL0 ADAP_REG_R(0x00)
+#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK BIT(0)
+#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN BIT(0)
+#define MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_DIS (0 << 0)
+
+#define MIPI_ADAPT_DDR_RD0_CNTL1 ADAP_REG_R(0x04)
+#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK GENMASK(31, 30)
+#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE (0 << 30)
+#define MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DDR_MODE BIT(30)
+
+#define MIPI_ADAPT_PIXEL0_CNTL0 ADAP_REG_R(0x80)
+#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK GENMASK(17, 16)
+#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DDR (0 << 16)
+#define MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT BIT(16)
+#define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK GENMASK(25, 20)
+#define MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(x) ((x) << 20)
+#define MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK BIT(31)
+#define MIPI_ADAPT_PIXEL0_CNTL0_START_EN BIT(31)
+
+#define MIPI_ADAPT_ALIG_CNTL0 ADAP_REG_R(0x100)
+#define MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK GENMASK(15, 0)
+#define MIPI_ADAPT_ALIG_CNTL0_H_NUM(x) ((x) << 0)
+#define MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK GENMASK(31, 16)
+#define MIPI_ADAPT_ALIG_CNTL0_V_NUM(x) ((x) << 16)
+
+#define MIPI_ADAPT_ALIG_CNTL1 ADAP_REG_R(0x104)
+#define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK GENMASK(31, 16)
+#define MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(x) ((x) << 16)
+
+#define MIPI_ADAPT_ALIG_CNTL2 ADAP_REG_R(0x108)
+#define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK GENMASK(31, 16)
+#define MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(x) ((x) << 16)
+
+#define MIPI_ADAPT_ALIG_CNTL6 ADAP_REG_R(0x118)
+#define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK BIT(0)
+#define MIPI_ADAPT_ALIG_CNTL6_PATH0_EN BIT(0)
+#define MIPI_ADAPT_ALIG_CNTL6_PATH0_DIS (0 << 0)
+#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK BIT(4)
+#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DDR (0 << 4)
+#define MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT BIT(4)
+#define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL6_DATA0_EN BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL6_DATA0_DIS (0 << 12)
+
+#define MIPI_ADAPT_ALIG_CNTL8 ADAP_REG_R(0x120)
+#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK BIT(5)
+#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN BIT(5)
+#define MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_DIS (0 << 5)
+#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_HOLD (0 << 12)
+#define MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD BIT(12)
+#define MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK BIT(31)
+#define MIPI_ADAPT_ALIG_CNTL8_START_EN BIT(31)
+
+#define MIPI_ADAP_MAX_WIDTH 2888
+#define MIPI_ADAP_MIN_WIDTH 160
+#define MIPI_ADAP_MAX_HEIGHT 2240
+#define MIPI_ADAP_MIN_HEIGHT 120
+#define MIPI_ADAP_DEFAULT_WIDTH 1920
+#define MIPI_ADAP_DEFAULT_HEIGHT 1080
+#define MIPI_ADAP_DEFAULT_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+
+/* C3 MIPI adapter pad list */
+enum {
+ C3_MIPI_ADAP_PAD_SINK,
+ C3_MIPI_ADAP_PAD_SRC,
+ C3_MIPI_ADAP_PAD_MAX
+};
+
+/*
+ * struct c3_adap_info - mipi adapter information
+ *
+ * @clocks: array of mipi adapter clock names
+ * @clock_num: actual clock number
+ */
+struct c3_adap_info {
+ char *clocks[MIPI_ADAP_CLOCK_NUM_MAX];
+ u32 clock_num;
+};
+
+/*
+ * struct c3_adap_device - mipi adapter platform device
+ *
+ * @dev: pointer to the struct device
+ * @top: mipi adapter top register address
+ * @fd: mipi adapter frontend register address
+ * @rd: mipi adapter reader register address
+ * @clks: array of MIPI adapter clocks
+ * @sd: mipi adapter sub-device
+ * @pads: mipi adapter sub-device pads
+ * @notifier: notifier to register on the v4l2-async API
+ * @src_sd: source sub-device pad
+ * @info: version-specific MIPI adapter information
+ */
+struct c3_adap_device {
+ struct device *dev;
+ void __iomem *top;
+ void __iomem *fd;
+ void __iomem *rd;
+ struct clk_bulk_data clks[MIPI_ADAP_CLOCK_NUM_MAX];
+
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_MIPI_ADAP_PAD_MAX];
+ struct v4l2_async_notifier notifier;
+ struct media_pad *src_pad;
+
+ const struct c3_adap_info *info;
+};
+
+/* Format helpers */
+
+struct c3_adap_pix_format {
+ u32 code;
+ u8 type;
+};
+
+static const struct c3_adap_pix_format c3_mipi_adap_formats[] = {
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 },
+};
+
+static const struct c3_adap_pix_format *c3_mipi_adap_find_format(u32 code)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(c3_mipi_adap_formats); i++)
+ if (code == c3_mipi_adap_formats[i].code)
+ return &c3_mipi_adap_formats[i];
+
+ return NULL;
+}
+
+/* Hardware configuration */
+
+static void c3_mipi_adap_update_bits(struct c3_adap_device *adap, u32 reg,
+ u32 mask, u32 val)
+{
+ void __iomem *addr;
+ u32 orig, tmp;
+
+ switch (ADAP_SUBMD(reg)) {
+ case SUBMD_TOP:
+ addr = adap->top + ADAP_REG_ADDR(reg);
+ break;
+ case SUBMD_FD:
+ addr = adap->fd + ADAP_REG_ADDR(reg);
+ break;
+ case SUBMD_RD:
+ addr = adap->rd + ADAP_REG_ADDR(reg);
+ break;
+ default:
+ dev_err(adap->dev,
+ "Invalid sub-module: %lu\n", ADAP_SUBMD(reg));
+ return;
+ }
+
+ orig = readl(addr);
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ if (tmp != orig)
+ writel(tmp, addr);
+}
+
+/* Configure adapter top sub module */
+static void c3_mipi_adap_cfg_top(struct c3_adap_device *adap)
+{
+ /* Reset adapter */
+ c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0,
+ MIPI_TOP_CTRL0_RST_ADAPTER_MASK,
+ MIPI_TOP_CTRL0_RST_ADAPTER_APPLY);
+ c3_mipi_adap_update_bits(adap, MIPI_TOP_CTRL0,
+ MIPI_TOP_CTRL0_RST_ADAPTER_MASK,
+ MIPI_TOP_CTRL0_RST_ADAPTER_EXIT);
+
+ /* Bypass decompress */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0,
+ MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_MASK,
+ MIPI_ADAPT_DE_CTRL0_RD_BUS_BYPASS_EN);
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DE_CTRL0,
+ MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_MASK,
+ MIPI_ADAPT_DE_CTRL0_WR_BUS_BYPASS_EN);
+}
+
+/* Configure adapter frontend sub module */
+static void c3_mipi_adap_cfg_frontend(struct c3_adap_device *adap,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ /* Reset frontend module */
+ c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET,
+ CSI2_CLK_RESET_SW_RESET_MASK,
+ CSI2_CLK_RESET_SW_RESET_APPLY);
+ c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET,
+ CSI2_CLK_RESET_SW_RESET_MASK,
+ CSI2_CLK_RESET_SW_RESET_RELEASE);
+ c3_mipi_adap_update_bits(adap, CSI2_CLK_RESET,
+ CSI2_CLK_RESET_CLK_ENABLE_MASK,
+ CSI2_CLK_RESET_CLK_ENABLE_EN);
+
+ c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP,
+ CSI2_X_START_END_ISP_X_START_MASK,
+ CSI2_X_START_END_ISP_X_START(0));
+ c3_mipi_adap_update_bits(adap, CSI2_X_START_END_ISP,
+ CSI2_X_START_END_ISP_X_END_MASK,
+ CSI2_X_START_END_ISP_X_END(fmt->width));
+
+ c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP,
+ CSI2_Y_START_END_ISP_Y_START_MASK,
+ CSI2_Y_START_END_ISP_Y_START(0));
+ c3_mipi_adap_update_bits(adap, CSI2_Y_START_END_ISP,
+ CSI2_Y_START_END_ISP_Y_END_MASK,
+ CSI2_Y_START_END_ISP_Y_END(fmt->height));
+
+ /* Select VS and HS signal for direct path */
+ c3_mipi_adap_update_bits(adap, CSI2_VC_MODE,
+ CSI2_VC_MODE_VS_ISP_SEL_VC_MASK,
+ CSI2_VC_MODE_VS_ISP_SEL_VC_0);
+ c3_mipi_adap_update_bits(adap, CSI2_VC_MODE,
+ CSI2_VC_MODE_HS_ISP_SEL_VC_MASK,
+ CSI2_VC_MODE_HS_ISP_SEL_VC_0);
+
+ /* Enable to receive RAW packet */
+ c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0,
+ CSI2_GEN_CTRL0_ENABLE_PACKETS_MASK,
+ CSI2_GEN_CTRL0_ENABLE_PACKETS_RAW);
+
+ /* Enable virtual channel 0 */
+ c3_mipi_adap_update_bits(adap, CSI2_GEN_CTRL0,
+ CSI2_GEN_CTRL0_VC0_MASK,
+ CSI2_GEN_CTRL0_VC0_EN);
+}
+
+static void c3_mipi_adap_cfg_rd0(struct c3_adap_device *adap)
+{
+ /* Select direct mode for DDR_RD0 mode */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL1,
+ MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_MASK,
+ MIPI_ADAPT_DDR_RD0_CNTL1_PORT_SEL_DIRECT_MODE);
+
+ /* Data can't bypass DDR_RD0 in direct mode, so enable DDR_RD0 here */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_DDR_RD0_CNTL0,
+ MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN_MASK,
+ MIPI_ADAPT_DDR_RD0_CNTL0_MODULE_EN);
+}
+
+static void c3_mipi_adap_cfg_pixel0(struct c3_adap_device *adap,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ const struct c3_adap_pix_format *pix;
+
+ pix = c3_mipi_adap_find_format(fmt->code);
+
+ /* Set work mode and data type for PIXEL0 module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0,
+ MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_MASK,
+ MIPI_ADAPT_PIXEL0_CNTL0_WORK_MODE_RAW_DIRECT);
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0,
+ MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE_MASK,
+ MIPI_ADAPT_PIXEL0_CNTL0_DATA_TYPE(pix->type));
+
+ /* Start PIXEL0 module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_PIXEL0_CNTL0,
+ MIPI_ADAPT_PIXEL0_CNTL0_START_EN_MASK,
+ MIPI_ADAPT_PIXEL0_CNTL0_START_EN);
+}
+
+static void c3_mipi_adap_cfg_alig(struct c3_adap_device *adap,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ /*
+ * ISP hardware requires the number of horizonal blanks greater than
+ * 64 cycles, so adding 64 here.
+ */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0,
+ MIPI_ADAPT_ALIG_CNTL0_H_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL0_H_NUM(fmt->width + 64));
+
+ /*
+ * ISP hardware requires the number of vertical blanks greater than
+ * 40 lines, so adding 40 here.
+ */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL0,
+ MIPI_ADAPT_ALIG_CNTL0_V_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL0_V_NUM(fmt->height + 40));
+
+ /* End pixel in a line */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL1,
+ MIPI_ADAPT_ALIG_CNTL1_HPE_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL1_HPE_NUM(fmt->width));
+
+ /* End line in a frame */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL2,
+ MIPI_ADAPT_ALIG_CNTL2_VPE_NUM_MASK,
+ MIPI_ADAPT_ALIG_CNTL2_VPE_NUM(fmt->height));
+
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6,
+ MIPI_ADAPT_ALIG_CNTL6_PATH0_EN_MASK,
+ MIPI_ADAPT_ALIG_CNTL6_PATH0_EN);
+
+ /* Select direct mode for ALIG module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6,
+ MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_MASK,
+ MIPI_ADAPT_ALIG_CNTL6_PIX0_DATA_MODE_DIRECT);
+
+ /* Enable to send raw data */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL6,
+ MIPI_ADAPT_ALIG_CNTL6_DATA0_EN_MASK,
+ MIPI_ADAPT_ALIG_CNTL6_DATA0_EN);
+
+ /* Set continue mode and disable hold counter */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8,
+ MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_MASK,
+ MIPI_ADAPT_ALIG_CNTL8_FRMAE_CONTINUE_EN);
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8,
+ MIPI_ADAPT_ALIG_CNTL8_EXCEED_DIS_MASK,
+ MIPI_ADAPT_ALIG_CNTL8_EXCEED_NOT_HOLD);
+
+ /* Start ALIG module */
+ c3_mipi_adap_update_bits(adap, MIPI_ADAPT_ALIG_CNTL8,
+ MIPI_ADAPT_ALIG_CNTL8_START_EN_MASK,
+ MIPI_ADAPT_ALIG_CNTL8_START_EN);
+}
+
+/* V4L2 subdev operations */
+
+static int c3_mipi_adap_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_adap_device *adap = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *fmt;
+ struct media_pad *sink_pad;
+ struct v4l2_subdev *src_sd;
+ int ret;
+
+ sink_pad = &adap->pads[C3_MIPI_ADAP_PAD_SINK];
+ adap->src_pad = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(adap->src_pad)) {
+ dev_dbg(adap->dev, "Failed to get source pad for MIPI adap\n");
+ return -EPIPE;
+ }
+
+ src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity);
+
+ pm_runtime_resume_and_get(adap->dev);
+
+ fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK);
+
+ c3_mipi_adap_cfg_top(adap);
+ c3_mipi_adap_cfg_frontend(adap, fmt);
+ c3_mipi_adap_cfg_rd0(adap);
+ c3_mipi_adap_cfg_pixel0(adap, fmt);
+ c3_mipi_adap_cfg_alig(adap, fmt);
+
+ ret = v4l2_subdev_enable_streams(src_sd, adap->src_pad->index, BIT(0));
+ if (ret) {
+ pm_runtime_put(adap->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_adap_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_adap_device *adap = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd;
+
+ if (adap->src_pad) {
+ src_sd = media_entity_to_v4l2_subdev(adap->src_pad->entity);
+ v4l2_subdev_disable_streams(src_sd, adap->src_pad->index,
+ BIT(0));
+ }
+ adap->src_pad = NULL;
+
+ pm_runtime_put(adap->dev);
+
+ return 0;
+}
+
+static int c3_mipi_adap_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ switch (code->pad) {
+ case C3_MIPI_ADAP_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(c3_mipi_adap_formats))
+ return -EINVAL;
+
+ code->code = c3_mipi_adap_formats[code->index].code;
+ break;
+ case C3_MIPI_ADAP_PAD_SRC:
+ if (code->index)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(state, code->pad);
+ code->code = fmt->code;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_adap_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ const struct c3_adap_pix_format *pix_format;
+
+ if (format->pad != C3_MIPI_ADAP_PAD_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ pix_format = c3_mipi_adap_find_format(format->format.code);
+ if (!pix_format)
+ pix_format = &c3_mipi_adap_formats[0];
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+ fmt->code = pix_format->code;
+ fmt->width = clamp_t(u32, format->format.width,
+ MIPI_ADAP_MIN_WIDTH, MIPI_ADAP_MAX_WIDTH);
+ fmt->height = clamp_t(u32, format->format.height,
+ MIPI_ADAP_MIN_HEIGHT, MIPI_ADAP_MAX_HEIGHT);
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ format->format = *fmt;
+
+ /* Synchronize the format to source pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC);
+ *fmt = format->format;
+
+ return 0;
+}
+
+static int c3_mipi_adap_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SINK);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_ADAP_PAD_SRC);
+
+ sink_fmt->width = MIPI_ADAP_DEFAULT_WIDTH;
+ sink_fmt->height = MIPI_ADAP_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = MIPI_ADAP_DEFAULT_FMT;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops c3_mipi_adap_pad_ops = {
+ .enum_mbus_code = c3_mipi_adap_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_mipi_adap_set_fmt,
+ .enable_streams = c3_mipi_adap_enable_streams,
+ .disable_streams = c3_mipi_adap_disable_streams,
+};
+
+static const struct v4l2_subdev_ops c3_mipi_adap_subdev_ops = {
+ .pad = &c3_mipi_adap_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_mipi_adap_internal_ops = {
+ .init_state = c3_mipi_adap_init_state,
+};
+
+/* Media entity operations */
+static const struct media_entity_operations c3_mipi_adap_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* PM runtime */
+
+static int c3_mipi_adap_runtime_suspend(struct device *dev)
+{
+ struct c3_adap_device *adap = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(adap->info->clock_num, adap->clks);
+
+ return 0;
+}
+
+static int c3_mipi_adap_runtime_resume(struct device *dev)
+{
+ struct c3_adap_device *adap = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(adap->info->clock_num, adap->clks);
+}
+
+static const struct dev_pm_ops c3_mipi_adap_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ RUNTIME_PM_OPS(c3_mipi_adap_runtime_suspend,
+ c3_mipi_adap_runtime_resume, NULL)
+};
+
+/* Probe/remove & platform driver */
+
+static int c3_mipi_adap_subdev_init(struct c3_adap_device *adap)
+{
+ struct v4l2_subdev *sd = &adap->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_mipi_adap_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &c3_mipi_adap_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "%s", MIPI_ADAP_SUBDEV_NAME);
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &c3_mipi_adap_entity_ops;
+
+ sd->dev = adap->dev;
+ v4l2_set_subdevdata(sd, adap);
+
+ adap->pads[C3_MIPI_ADAP_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ adap->pads[C3_MIPI_ADAP_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_MIPI_ADAP_PAD_MAX,
+ adap->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ media_entity_cleanup(&sd->entity);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void c3_mipi_adap_subdev_deinit(struct c3_adap_device *adap)
+{
+ v4l2_subdev_cleanup(&adap->sd);
+ media_entity_cleanup(&adap->sd.entity);
+}
+
+/* Subdev notifier register */
+static int c3_mipi_adap_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct c3_adap_device *adap = v4l2_get_subdevdata(notifier->sd);
+ struct media_pad *sink = &adap->sd.entity.pads[C3_MIPI_ADAP_PAD_SINK];
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static const struct v4l2_async_notifier_operations c3_mipi_adap_notify_ops = {
+ .bound = c3_mipi_adap_notify_bound,
+};
+
+static int c3_mipi_adap_async_register(struct c3_adap_device *adap)
+{
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_subdev_nf_init(&adap->notifier, &adap->sd);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(adap->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ asc = v4l2_async_nf_add_fwnode_remote(&adap->notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_put_handle;
+ }
+
+ adap->notifier.ops = &c3_mipi_adap_notify_ops;
+ ret = v4l2_async_nf_register(&adap->notifier);
+ if (ret)
+ goto err_cleanup_nf;
+
+ ret = v4l2_async_register_subdev(&adap->sd);
+ if (ret)
+ goto err_unregister_nf;
+
+ fwnode_handle_put(ep);
+
+ return 0;
+
+err_unregister_nf:
+ v4l2_async_nf_unregister(&adap->notifier);
+err_cleanup_nf:
+ v4l2_async_nf_cleanup(&adap->notifier);
+err_put_handle:
+ fwnode_handle_put(ep);
+ return ret;
+}
+
+static void c3_mipi_adap_async_unregister(struct c3_adap_device *adap)
+{
+ v4l2_async_unregister_subdev(&adap->sd);
+ v4l2_async_nf_unregister(&adap->notifier);
+ v4l2_async_nf_cleanup(&adap->notifier);
+}
+
+static int c3_mipi_adap_ioremap_resource(struct c3_adap_device *adap)
+{
+ struct device *dev = adap->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ adap->top = devm_platform_ioremap_resource_byname(pdev, "top");
+ if (IS_ERR(adap->top))
+ return PTR_ERR(adap->top);
+
+ adap->fd = devm_platform_ioremap_resource_byname(pdev, "fd");
+ if (IS_ERR(adap->fd))
+ return PTR_ERR(adap->fd);
+
+ adap->rd = devm_platform_ioremap_resource_byname(pdev, "rd");
+ if (IS_ERR(adap->rd))
+ return PTR_ERR(adap->rd);
+
+ return 0;
+}
+
+static int c3_mipi_adap_get_clocks(struct c3_adap_device *adap)
+{
+ const struct c3_adap_info *info = adap->info;
+
+ for (unsigned int i = 0; i < info->clock_num; i++)
+ adap->clks[i].id = info->clocks[i];
+
+ return devm_clk_bulk_get(adap->dev, info->clock_num, adap->clks);
+}
+
+static int c3_mipi_adap_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct c3_adap_device *adap;
+ int ret;
+
+ adap = devm_kzalloc(dev, sizeof(*adap), GFP_KERNEL);
+ if (!adap)
+ return -ENOMEM;
+
+ adap->info = of_device_get_match_data(dev);
+ adap->dev = dev;
+
+ ret = c3_mipi_adap_ioremap_resource(adap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to ioremap resource\n");
+
+ ret = c3_mipi_adap_get_clocks(adap);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ platform_set_drvdata(pdev, adap);
+
+ pm_runtime_enable(dev);
+
+ ret = c3_mipi_adap_subdev_init(adap);
+ if (ret)
+ goto err_disable_runtime_pm;
+
+ ret = c3_mipi_adap_async_register(adap);
+ if (ret)
+ goto err_deinit_subdev;
+
+ return 0;
+
+err_deinit_subdev:
+ c3_mipi_adap_subdev_deinit(adap);
+err_disable_runtime_pm:
+ pm_runtime_disable(dev);
+ return ret;
+};
+
+static void c3_mipi_adap_remove(struct platform_device *pdev)
+{
+ struct c3_adap_device *adap = platform_get_drvdata(pdev);
+
+ c3_mipi_adap_async_unregister(adap);
+ c3_mipi_adap_subdev_deinit(adap);
+
+ pm_runtime_disable(&pdev->dev);
+};
+
+static const struct c3_adap_info c3_mipi_adap_info = {
+ .clocks = {"vapb", "isp0"},
+ .clock_num = 2
+};
+
+static const struct of_device_id c3_mipi_adap_of_match[] = {
+ {
+ .compatible = "amlogic,c3-mipi-adapter",
+ .data = &c3_mipi_adap_info
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, c3_mipi_adap_of_match);
+
+static struct platform_driver c3_mipi_adap_driver = {
+ .probe = c3_mipi_adap_probe,
+ .remove = c3_mipi_adap_remove,
+ .driver = {
+ .name = "c3-mipi-adapter",
+ .of_match_table = c3_mipi_adap_of_match,
+ .pm = pm_ptr(&c3_mipi_adap_pm_ops),
+ },
+};
+
+module_platform_driver(c3_mipi_adap_driver);
+
+MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic C3 MIPI adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig b/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig
new file mode 100644
index 000000000000..0d7b2e203273
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-csi2/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_C3_MIPI_CSI2
+ tristate "Amlogic C3 MIPI CSI-2 receiver"
+ depends on ARCH_MESON || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on OF
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ Video4Linux2 driver for Amlogic C3 MIPI CSI-2 receiver.
+ C3 MIPI CSI-2 receiver is used to receive MIPI data from
+ image sensor.
+
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile b/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile
new file mode 100644
index 000000000000..cc08fc722bfd
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-csi2/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_VIDEO_C3_MIPI_CSI2) += c3-mipi-csi2.o
diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c
new file mode 100644
index 000000000000..1011ab3ebac7
--- /dev/null
+++ b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c
@@ -0,0 +1,828 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Copyright (C) 2024 Amlogic, Inc. All rights reserved
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/* C3 CSI-2 submodule definition */
+enum {
+ SUBMD_APHY,
+ SUBMD_DPHY,
+ SUBMD_HOST,
+};
+
+#define CSI2_SUBMD_MASK GENMASK(17, 16)
+#define CSI2_SUBMD_SHIFT 16
+#define CSI2_SUBMD(x) (((x) & (CSI2_SUBMD_MASK)) >> (CSI2_SUBMD_SHIFT))
+#define CSI2_REG_ADDR_MASK GENMASK(15, 0)
+#define CSI2_REG_ADDR(x) ((x) & (CSI2_REG_ADDR_MASK))
+#define CSI2_REG_A(x) ((SUBMD_APHY << CSI2_SUBMD_SHIFT) | (x))
+#define CSI2_REG_D(x) ((SUBMD_DPHY << CSI2_SUBMD_SHIFT) | (x))
+#define CSI2_REG_H(x) ((SUBMD_HOST << CSI2_SUBMD_SHIFT) | (x))
+
+#define MIPI_CSI2_CLOCK_NUM_MAX 3
+#define MIPI_CSI2_SUBDEV_NAME "c3-mipi-csi2"
+
+/* C3 CSI-2 APHY register */
+#define CSI_PHY_CNTL0 CSI2_REG_A(0x44)
+#define CSI_PHY_CNTL0_HS_LP_BIAS_EN BIT(10)
+#define CSI_PHY_CNTL0_HS_RX_TRIM_11 (11 << 11)
+#define CSI_PHY_CNTL0_LP_LOW_VTH_2 (2 << 16)
+#define CSI_PHY_CNTL0_LP_HIGH_VTH_4 (4 << 20)
+#define CSI_PHY_CNTL0_DATA_LANE0_HS_DIG_EN BIT(24)
+#define CSI_PHY_CNTL0_DATA_LANE1_HS_DIG_EN BIT(25)
+#define CSI_PHY_CNTL0_CLK0_LANE_HS_DIG_EN BIT(26)
+#define CSI_PHY_CNTL0_DATA_LANE2_HS_DIG_EN BIT(27)
+#define CSI_PHY_CNTL0_DATA_LANE3_HS_DIG_EN BIT(28)
+
+#define CSI_PHY_CNTL1 CSI2_REG_A(0x48)
+#define CSI_PHY_CNTL1_HS_EQ_CAP_SMALL (2 << 16)
+#define CSI_PHY_CNTL1_HS_EQ_CAP_BIG (3 << 16)
+#define CSI_PHY_CNTL1_HS_EQ_RES_MIN (3 << 18)
+#define CSI_PHY_CNTL1_HS_EQ_RES_MED (2 << 18)
+#define CSI_PHY_CNTL1_HS_EQ_RES_MAX BIT(18)
+#define CSI_PHY_CNTL1_CLK_CHN_EQ_MAX_GAIN BIT(20)
+#define CSI_PHY_CNTL1_DATA_CHN_EQ_MAX_GAIN BIT(21)
+#define CSI_PHY_CNTL1_COM_BG_EN BIT(24)
+#define CSI_PHY_CNTL1_HS_SYNC_EN BIT(25)
+
+/* C3 CSI-2 DPHY register */
+#define MIPI_PHY_CTRL CSI2_REG_D(0x00)
+#define MIPI_PHY_CTRL_DATA_LANE0_EN (0 << 0)
+#define MIPI_PHY_CTRL_DATA_LANE0_DIS BIT(0)
+#define MIPI_PHY_CTRL_DATA_LANE1_EN (0 << 1)
+#define MIPI_PHY_CTRL_DATA_LANE1_DIS BIT(1)
+#define MIPI_PHY_CTRL_DATA_LANE2_EN (0 << 2)
+#define MIPI_PHY_CTRL_DATA_LANE2_DIS BIT(2)
+#define MIPI_PHY_CTRL_DATA_LANE3_EN (0 << 3)
+#define MIPI_PHY_CTRL_DATA_LANE3_DIS BIT(3)
+#define MIPI_PHY_CTRL_CLOCK_LANE_EN (0 << 4)
+#define MIPI_PHY_CTRL_CLOCK_LANE_DIS BIT(4)
+
+#define MIPI_PHY_CLK_LANE_CTRL CSI2_REG_D(0x04)
+#define MIPI_PHY_CLK_LANE_CTRL_FORCE_ULPS_ENTER BIT(0)
+#define MIPI_PHY_CLK_LANE_CTRL_FORCE_ULPS_EXIT BIT(1)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS (0 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_2 BIT(3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_4 (2 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_8 (3 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_16 (4 << 3)
+#define MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_EN BIT(6)
+#define MIPI_PHY_CLK_LANE_CTRL_LPEN_DIS BIT(7)
+#define MIPI_PHY_CLK_LANE_CTRL_END_EN BIT(8)
+#define MIPI_PHY_CLK_LANE_CTRL_HS_RX_EN BIT(9)
+
+#define MIPI_PHY_DATA_LANE_CTRL1 CSI2_REG_D(0x0c)
+#define MIPI_PHY_DATA_LANE_CTRL1_INSERT_ERRESC BIT(0)
+#define MIPI_PHY_DATA_LANE_CTRL1_HS_SYNC_CHK_EN BIT(1)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_MASK GENMASK(6, 2)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_ALL_EN (0x1f << 2)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_MASK GENMASK(9, 7)
+#define MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_3 (3 << 7)
+
+#define MIPI_PHY_TCLK_MISS CSI2_REG_D(0x10)
+#define MIPI_PHY_TCLK_MISS_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_TCLK_MISS_CYCLES_9 (9 << 0)
+
+#define MIPI_PHY_TCLK_SETTLE CSI2_REG_D(0x14)
+#define MIPI_PHY_TCLK_SETTLE_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_TCLK_SETTLE_CYCLES_31 (31 << 0)
+
+#define MIPI_PHY_THS_EXIT CSI2_REG_D(0x18)
+#define MIPI_PHY_THS_EXIT_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_THS_EXIT_CYCLES_8 (8 << 0)
+
+#define MIPI_PHY_THS_SKIP CSI2_REG_D(0x1c)
+#define MIPI_PHY_THS_SKIP_CYCLES_MASK GENMASK(7, 0)
+#define MIPI_PHY_THS_SKIP_CYCLES_10 (10 << 0)
+
+#define MIPI_PHY_THS_SETTLE CSI2_REG_D(0x20)
+#define MIPI_PHY_THS_SETTLE_CYCLES_MASK GENMASK(7, 0)
+
+#define MIPI_PHY_TINIT CSI2_REG_D(0x24)
+#define MIPI_PHY_TINIT_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TINIT_CYCLES_20000 (20000 << 0)
+
+#define MIPI_PHY_TULPS_C CSI2_REG_D(0x28)
+#define MIPI_PHY_TULPS_C_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TULPS_C_CYCLES_4096 (4096 << 0)
+
+#define MIPI_PHY_TULPS_S CSI2_REG_D(0x2c)
+#define MIPI_PHY_TULPS_S_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TULPS_S_CYCLES_256 (256 << 0)
+
+#define MIPI_PHY_TMBIAS CSI2_REG_D(0x30)
+#define MIPI_PHY_TMBIAS_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TMBIAS_CYCLES_256 (256 << 0)
+
+#define MIPI_PHY_TLP_EN_W CSI2_REG_D(0x34)
+#define MIPI_PHY_TLP_EN_W_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TLP_EN_W_CYCLES_12 (12 << 0)
+
+#define MIPI_PHY_TLPOK CSI2_REG_D(0x38)
+#define MIPI_PHY_TLPOK_CYCLES_MASK GENMASK(31, 0)
+#define MIPI_PHY_TLPOK_CYCLES_256 (256 << 0)
+
+#define MIPI_PHY_TWD_INIT CSI2_REG_D(0x3c)
+#define MIPI_PHY_TWD_INIT_DOG_MASK GENMASK(31, 0)
+#define MIPI_PHY_TWD_INIT_DOG_0X400000 (0x400000 << 0)
+
+#define MIPI_PHY_TWD_HS CSI2_REG_D(0x40)
+#define MIPI_PHY_TWD_HS_DOG_MASK GENMASK(31, 0)
+#define MIPI_PHY_TWD_HS_DOG_0X400000 (0x400000 << 0)
+
+#define MIPI_PHY_MUX_CTRL0 CSI2_REG_D(0x284)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_MASK GENMASK(3, 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE0 (0 << 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE1 BIT(0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE2 (2 << 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE3 (3 << 0)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_MASK GENMASK(7, 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE0 (0 << 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE1 BIT(4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE2 (2 << 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE3 (3 << 4)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_MASK GENMASK(11, 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE0 (0 << 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE1 BIT(8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE2 (2 << 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE3 (3 << 8)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_MASK GENMASK(14, 12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE0 (0 << 12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE1 BIT(12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE2 (2 << 12)
+#define MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE3 (3 << 12)
+
+#define MIPI_PHY_MUX_CTRL1 CSI2_REG_D(0x288)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_MASK GENMASK(3, 0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN0 (0 << 0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN1 BIT(0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN2 (2 << 0)
+#define MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN3 (3 << 0)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_MASK GENMASK(7, 4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN0 (0 << 4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN1 BIT(4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN2 (2 << 4)
+#define MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN3 (3 << 4)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_MASK GENMASK(11, 8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN0 (0 << 8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN1 BIT(8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN2 (2 << 8)
+#define MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN3 (3 << 8)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_MASK GENMASK(14, 12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN0 (0 << 12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN1 BIT(12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN2 (2 << 12)
+#define MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN3 (3 << 12)
+
+/* C3 CSI-2 HOST register */
+#define CSI2_HOST_N_LANES CSI2_REG_H(0x04)
+#define CSI2_HOST_N_LANES_MASK GENMASK(1, 0)
+#define CSI2_HOST_N_LANES_1 (0 << 0)
+#define CSI2_HOST_N_LANES_2 BIT(0)
+#define CSI2_HOST_N_LANES_3 (2 << 0)
+#define CSI2_HOST_N_LANES_4 (3 << 0)
+
+#define CSI2_HOST_CSI2_RESETN CSI2_REG_H(0x10)
+#define CSI2_HOST_CSI2_RESETN_MASK BIT(0)
+#define CSI2_HOST_CSI2_RESETN_ACTIVE (0 << 0)
+#define CSI2_HOST_CSI2_RESETN_EXIT BIT(0)
+
+#define C3_MIPI_CSI2_MAX_WIDTH 2888
+#define C3_MIPI_CSI2_MIN_WIDTH 160
+#define C3_MIPI_CSI2_MAX_HEIGHT 2240
+#define C3_MIPI_CSI2_MIN_HEIGHT 120
+#define C3_MIPI_CSI2_DEFAULT_WIDTH 1920
+#define C3_MIPI_CSI2_DEFAULT_HEIGHT 1080
+#define C3_MIPI_CSI2_DEFAULT_FMT MEDIA_BUS_FMT_SRGGB10_1X10
+
+/* C3 CSI-2 pad list */
+enum {
+ C3_MIPI_CSI2_PAD_SINK,
+ C3_MIPI_CSI2_PAD_SRC,
+ C3_MIPI_CSI2_PAD_MAX
+};
+
+/*
+ * struct c3_csi_info - MIPI CSI2 information
+ *
+ * @clocks: array of MIPI CSI2 clock names
+ * @clock_num: actual clock number
+ */
+struct c3_csi_info {
+ char *clocks[MIPI_CSI2_CLOCK_NUM_MAX];
+ u32 clock_num;
+};
+
+/*
+ * struct c3_csi_device - MIPI CSI2 platform device
+ *
+ * @dev: pointer to the struct device
+ * @aphy: MIPI CSI2 aphy register address
+ * @dphy: MIPI CSI2 dphy register address
+ * @host: MIPI CSI2 host register address
+ * @clks: array of MIPI CSI2 clocks
+ * @sd: MIPI CSI2 sub-device
+ * @pads: MIPI CSI2 sub-device pads
+ * @notifier: notifier to register on the v4l2-async API
+ * @src_pad: source sub-device pad
+ * @bus: MIPI CSI2 bus information
+ * @info: version-specific MIPI CSI2 information
+ */
+struct c3_csi_device {
+ struct device *dev;
+ void __iomem *aphy;
+ void __iomem *dphy;
+ void __iomem *host;
+ struct clk_bulk_data clks[MIPI_CSI2_CLOCK_NUM_MAX];
+
+ struct v4l2_subdev sd;
+ struct media_pad pads[C3_MIPI_CSI2_PAD_MAX];
+ struct v4l2_async_notifier notifier;
+ struct media_pad *src_pad;
+ struct v4l2_mbus_config_mipi_csi2 bus;
+
+ const struct c3_csi_info *info;
+};
+
+static const u32 c3_mipi_csi_formats[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+};
+
+/* Hardware configuration */
+
+static void c3_mipi_csi_write(struct c3_csi_device *csi, u32 reg, u32 val)
+{
+ void __iomem *addr;
+
+ switch (CSI2_SUBMD(reg)) {
+ case SUBMD_APHY:
+ addr = csi->aphy + CSI2_REG_ADDR(reg);
+ break;
+ case SUBMD_DPHY:
+ addr = csi->dphy + CSI2_REG_ADDR(reg);
+ break;
+ case SUBMD_HOST:
+ addr = csi->host + CSI2_REG_ADDR(reg);
+ break;
+ default:
+ dev_err(csi->dev, "Invalid sub-module: %lu\n", CSI2_SUBMD(reg));
+ return;
+ }
+
+ writel(val, addr);
+}
+
+static void c3_mipi_csi_cfg_aphy(struct c3_csi_device *csi)
+{
+ c3_mipi_csi_write(csi, CSI_PHY_CNTL0,
+ CSI_PHY_CNTL0_HS_LP_BIAS_EN |
+ CSI_PHY_CNTL0_HS_RX_TRIM_11 |
+ CSI_PHY_CNTL0_LP_LOW_VTH_2 |
+ CSI_PHY_CNTL0_LP_HIGH_VTH_4 |
+ CSI_PHY_CNTL0_DATA_LANE0_HS_DIG_EN |
+ CSI_PHY_CNTL0_DATA_LANE1_HS_DIG_EN |
+ CSI_PHY_CNTL0_CLK0_LANE_HS_DIG_EN |
+ CSI_PHY_CNTL0_DATA_LANE2_HS_DIG_EN |
+ CSI_PHY_CNTL0_DATA_LANE3_HS_DIG_EN);
+
+ c3_mipi_csi_write(csi, CSI_PHY_CNTL1,
+ CSI_PHY_CNTL1_HS_EQ_CAP_SMALL |
+ CSI_PHY_CNTL1_HS_EQ_RES_MED |
+ CSI_PHY_CNTL1_CLK_CHN_EQ_MAX_GAIN |
+ CSI_PHY_CNTL1_DATA_CHN_EQ_MAX_GAIN |
+ CSI_PHY_CNTL1_COM_BG_EN |
+ CSI_PHY_CNTL1_HS_SYNC_EN);
+}
+
+static void c3_mipi_csi_cfg_dphy(struct c3_csi_device *csi, s64 rate)
+{
+ u32 val;
+ u32 settle;
+
+ /* Calculate the high speed settle */
+ val = DIV_ROUND_UP_ULL(1000000000, rate);
+ settle = (16 * val + 230) / 10;
+
+ c3_mipi_csi_write(csi, MIPI_PHY_CLK_LANE_CTRL,
+ MIPI_PHY_CLK_LANE_CTRL_HS_RX_EN |
+ MIPI_PHY_CLK_LANE_CTRL_END_EN |
+ MIPI_PHY_CLK_LANE_CTRL_LPEN_DIS |
+ MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_EN |
+ MIPI_PHY_CLK_LANE_CTRL_TCLK_ZERO_HS_8);
+
+ c3_mipi_csi_write(csi, MIPI_PHY_TCLK_MISS, MIPI_PHY_TCLK_MISS_CYCLES_9);
+ c3_mipi_csi_write(csi, MIPI_PHY_TCLK_SETTLE,
+ MIPI_PHY_TCLK_SETTLE_CYCLES_31);
+ c3_mipi_csi_write(csi, MIPI_PHY_THS_EXIT, MIPI_PHY_THS_EXIT_CYCLES_8);
+ c3_mipi_csi_write(csi, MIPI_PHY_THS_SKIP, MIPI_PHY_THS_SKIP_CYCLES_10);
+ c3_mipi_csi_write(csi, MIPI_PHY_THS_SETTLE, settle);
+ c3_mipi_csi_write(csi, MIPI_PHY_TINIT, MIPI_PHY_TINIT_CYCLES_20000);
+ c3_mipi_csi_write(csi, MIPI_PHY_TMBIAS, MIPI_PHY_TMBIAS_CYCLES_256);
+ c3_mipi_csi_write(csi, MIPI_PHY_TULPS_C, MIPI_PHY_TULPS_C_CYCLES_4096);
+ c3_mipi_csi_write(csi, MIPI_PHY_TULPS_S, MIPI_PHY_TULPS_S_CYCLES_256);
+ c3_mipi_csi_write(csi, MIPI_PHY_TLP_EN_W, MIPI_PHY_TLP_EN_W_CYCLES_12);
+ c3_mipi_csi_write(csi, MIPI_PHY_TLPOK, MIPI_PHY_TLPOK_CYCLES_256);
+ c3_mipi_csi_write(csi, MIPI_PHY_TWD_INIT,
+ MIPI_PHY_TWD_INIT_DOG_0X400000);
+ c3_mipi_csi_write(csi, MIPI_PHY_TWD_HS, MIPI_PHY_TWD_HS_DOG_0X400000);
+
+ c3_mipi_csi_write(csi, MIPI_PHY_DATA_LANE_CTRL1,
+ MIPI_PHY_DATA_LANE_CTRL1_INSERT_ERRESC |
+ MIPI_PHY_DATA_LANE_CTRL1_HS_SYNC_CHK_EN |
+ MIPI_PHY_DATA_LANE_CTRL1_PIPE_ALL_EN |
+ MIPI_PHY_DATA_LANE_CTRL1_PIPE_DELAY_3);
+
+ /* Set the order of lanes */
+ c3_mipi_csi_write(csi, MIPI_PHY_MUX_CTRL0,
+ MIPI_PHY_MUX_CTRL0_SFEN3_SRC_LANE3 |
+ MIPI_PHY_MUX_CTRL0_SFEN2_SRC_LANE2 |
+ MIPI_PHY_MUX_CTRL0_SFEN1_SRC_LANE1 |
+ MIPI_PHY_MUX_CTRL0_SFEN0_SRC_LANE0);
+
+ c3_mipi_csi_write(csi, MIPI_PHY_MUX_CTRL1,
+ MIPI_PHY_MUX_CTRL1_LANE3_SRC_SFEN3 |
+ MIPI_PHY_MUX_CTRL1_LANE2_SRC_SFEN2 |
+ MIPI_PHY_MUX_CTRL1_LANE1_SRC_SFEN1 |
+ MIPI_PHY_MUX_CTRL1_LANE0_SRC_SFEN0);
+
+ /* Enable digital data and clock lanes */
+ c3_mipi_csi_write(csi, MIPI_PHY_CTRL,
+ MIPI_PHY_CTRL_DATA_LANE0_EN |
+ MIPI_PHY_CTRL_DATA_LANE1_EN |
+ MIPI_PHY_CTRL_DATA_LANE2_EN |
+ MIPI_PHY_CTRL_DATA_LANE3_EN |
+ MIPI_PHY_CTRL_CLOCK_LANE_EN);
+}
+
+static void c3_mipi_csi_cfg_host(struct c3_csi_device *csi)
+{
+ /* Reset CSI-2 controller output */
+ c3_mipi_csi_write(csi, CSI2_HOST_CSI2_RESETN,
+ CSI2_HOST_CSI2_RESETN_ACTIVE);
+ c3_mipi_csi_write(csi, CSI2_HOST_CSI2_RESETN,
+ CSI2_HOST_CSI2_RESETN_EXIT);
+
+ /* Set data lane number */
+ c3_mipi_csi_write(csi, CSI2_HOST_N_LANES, csi->bus.num_data_lanes - 1);
+}
+
+static int c3_mipi_csi_start_stream(struct c3_csi_device *csi,
+ struct v4l2_subdev *src_sd)
+{
+ s64 link_freq;
+ s64 lane_rate;
+
+ link_freq = v4l2_get_link_freq(src_sd->ctrl_handler, 0, 0);
+ if (link_freq < 0) {
+ dev_err(csi->dev,
+ "Unable to obtain link frequency: %lld\n", link_freq);
+ return link_freq;
+ }
+
+ lane_rate = link_freq * 2;
+ if (lane_rate > 1500000000) {
+ dev_err(csi->dev, "Invalid lane rate: %lld\n", lane_rate);
+ return -EINVAL;
+ }
+
+ c3_mipi_csi_cfg_aphy(csi);
+ c3_mipi_csi_cfg_dphy(csi, lane_rate);
+ c3_mipi_csi_cfg_host(csi);
+
+ return 0;
+}
+
+static int c3_mipi_csi_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_csi_device *csi = v4l2_get_subdevdata(sd);
+ struct media_pad *sink_pad;
+ struct v4l2_subdev *src_sd;
+ int ret;
+
+ sink_pad = &csi->pads[C3_MIPI_CSI2_PAD_SINK];
+ csi->src_pad = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(csi->src_pad)) {
+ dev_dbg(csi->dev, "Failed to get source pad for MIPI CSI-2\n");
+ return -EPIPE;
+ }
+
+ src_sd = media_entity_to_v4l2_subdev(csi->src_pad->entity);
+
+ pm_runtime_resume_and_get(csi->dev);
+
+ c3_mipi_csi_start_stream(csi, src_sd);
+
+ ret = v4l2_subdev_enable_streams(src_sd, csi->src_pad->index, BIT(0));
+ if (ret) {
+ pm_runtime_put(csi->dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_csi_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct c3_csi_device *csi = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *src_sd;
+
+ if (csi->src_pad) {
+ src_sd = media_entity_to_v4l2_subdev(csi->src_pad->entity);
+ v4l2_subdev_disable_streams(src_sd, csi->src_pad->index,
+ BIT(0));
+ }
+ csi->src_pad = NULL;
+
+ pm_runtime_put(csi->dev);
+
+ return 0;
+}
+
+static int c3_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ switch (code->pad) {
+ case C3_MIPI_CSI2_PAD_SINK:
+ if (code->index >= ARRAY_SIZE(c3_mipi_csi_formats))
+ return -EINVAL;
+
+ code->code = c3_mipi_csi_formats[code->index];
+ break;
+ case C3_MIPI_CSI2_PAD_SRC:
+ if (code->index)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(state, code->pad);
+ code->code = fmt->code;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int c3_mipi_csi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int i;
+
+ if (format->pad != C3_MIPI_CSI2_PAD_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+
+ for (i = 0; i < ARRAY_SIZE(c3_mipi_csi_formats); i++) {
+ if (format->format.code == c3_mipi_csi_formats[i]) {
+ fmt->code = c3_mipi_csi_formats[i];
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(c3_mipi_csi_formats))
+ fmt->code = c3_mipi_csi_formats[0];
+
+ fmt->width = clamp_t(u32, format->format.width,
+ C3_MIPI_CSI2_MIN_WIDTH, C3_MIPI_CSI2_MAX_WIDTH);
+ fmt->height = clamp_t(u32, format->format.height,
+ C3_MIPI_CSI2_MIN_HEIGHT, C3_MIPI_CSI2_MAX_HEIGHT);
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ format->format = *fmt;
+
+ /* Synchronize the format to source pad */
+ fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SRC);
+ *fmt = format->format;
+
+ return 0;
+}
+
+static int c3_mipi_csi_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SINK);
+ src_fmt = v4l2_subdev_state_get_format(state, C3_MIPI_CSI2_PAD_SRC);
+
+ sink_fmt->width = C3_MIPI_CSI2_DEFAULT_WIDTH;
+ sink_fmt->height = C3_MIPI_CSI2_DEFAULT_HEIGHT;
+ sink_fmt->field = V4L2_FIELD_NONE;
+ sink_fmt->code = C3_MIPI_CSI2_DEFAULT_FMT;
+ sink_fmt->colorspace = V4L2_COLORSPACE_RAW;
+ sink_fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+ sink_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ sink_fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+
+ *src_fmt = *sink_fmt;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops c3_mipi_csi_pad_ops = {
+ .enum_mbus_code = c3_mipi_csi_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = c3_mipi_csi_set_fmt,
+ .enable_streams = c3_mipi_csi_enable_streams,
+ .disable_streams = c3_mipi_csi_disable_streams,
+};
+
+static const struct v4l2_subdev_ops c3_mipi_csi_subdev_ops = {
+ .pad = &c3_mipi_csi_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops c3_mipi_csi_internal_ops = {
+ .init_state = c3_mipi_csi_init_state,
+};
+
+/* Media entity operations */
+static const struct media_entity_operations c3_mipi_csi_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/* PM runtime */
+
+static int c3_mipi_csi_runtime_suspend(struct device *dev)
+{
+ struct c3_csi_device *csi = dev_get_drvdata(dev);
+
+ clk_bulk_disable_unprepare(csi->info->clock_num, csi->clks);
+
+ return 0;
+}
+
+static int c3_mipi_csi_runtime_resume(struct device *dev)
+{
+ struct c3_csi_device *csi = dev_get_drvdata(dev);
+
+ return clk_bulk_prepare_enable(csi->info->clock_num, csi->clks);
+}
+
+static const struct dev_pm_ops c3_mipi_csi_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ RUNTIME_PM_OPS(c3_mipi_csi_runtime_suspend,
+ c3_mipi_csi_runtime_resume, NULL)
+};
+
+/* Probe/remove & platform driver */
+
+static int c3_mipi_csi_subdev_init(struct c3_csi_device *csi)
+{
+ struct v4l2_subdev *sd = &csi->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &c3_mipi_csi_subdev_ops);
+ sd->owner = THIS_MODULE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->internal_ops = &c3_mipi_csi_internal_ops;
+ snprintf(sd->name, sizeof(sd->name), "%s", MIPI_CSI2_SUBDEV_NAME);
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &c3_mipi_csi_entity_ops;
+
+ sd->dev = csi->dev;
+ v4l2_set_subdevdata(sd, csi);
+
+ csi->pads[C3_MIPI_CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ csi->pads[C3_MIPI_CSI2_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&sd->entity, C3_MIPI_CSI2_PAD_MAX,
+ csi->pads);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ media_entity_cleanup(&sd->entity);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void c3_mipi_csi_subdev_deinit(struct c3_csi_device *csi)
+{
+ v4l2_subdev_cleanup(&csi->sd);
+ media_entity_cleanup(&csi->sd.entity);
+}
+
+/* Subdev notifier register */
+static int c3_mipi_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct c3_csi_device *csi = v4l2_get_subdevdata(notifier->sd);
+ struct media_pad *sink = &csi->sd.entity.pads[C3_MIPI_CSI2_PAD_SINK];
+
+ return v4l2_create_fwnode_links_to_pad(sd, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static const struct v4l2_async_notifier_operations c3_mipi_csi_notify_ops = {
+ .bound = c3_mipi_csi_notify_bound,
+};
+
+static int c3_mipi_csi_async_register(struct c3_csi_device *csi)
+{
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ struct v4l2_async_connection *asc;
+ struct fwnode_handle *ep;
+ int ret;
+
+ v4l2_async_subdev_nf_init(&csi->notifier, &csi->sd);
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csi->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ goto err_put_handle;
+
+ csi->bus = vep.bus.mipi_csi2;
+
+ asc = v4l2_async_nf_add_fwnode_remote(&csi->notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_put_handle;
+ }
+
+ csi->notifier.ops = &c3_mipi_csi_notify_ops;
+ ret = v4l2_async_nf_register(&csi->notifier);
+ if (ret)
+ goto err_cleanup_nf;
+
+ ret = v4l2_async_register_subdev(&csi->sd);
+ if (ret)
+ goto err_unregister_nf;
+
+ fwnode_handle_put(ep);
+
+ return 0;
+
+err_unregister_nf:
+ v4l2_async_nf_unregister(&csi->notifier);
+err_cleanup_nf:
+ v4l2_async_nf_cleanup(&csi->notifier);
+err_put_handle:
+ fwnode_handle_put(ep);
+ return ret;
+}
+
+static void c3_mipi_csi_async_unregister(struct c3_csi_device *csi)
+{
+ v4l2_async_unregister_subdev(&csi->sd);
+ v4l2_async_nf_unregister(&csi->notifier);
+ v4l2_async_nf_cleanup(&csi->notifier);
+}
+
+static int c3_mipi_csi_ioremap_resource(struct c3_csi_device *csi)
+{
+ struct device *dev = csi->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+
+ csi->aphy = devm_platform_ioremap_resource_byname(pdev, "aphy");
+ if (IS_ERR(csi->aphy))
+ return PTR_ERR(csi->aphy);
+
+ csi->dphy = devm_platform_ioremap_resource_byname(pdev, "dphy");
+ if (IS_ERR(csi->dphy))
+ return PTR_ERR(csi->dphy);
+
+ csi->host = devm_platform_ioremap_resource_byname(pdev, "host");
+ if (IS_ERR(csi->host))
+ return PTR_ERR(csi->host);
+
+ return 0;
+}
+
+static int c3_mipi_csi_get_clocks(struct c3_csi_device *csi)
+{
+ const struct c3_csi_info *info = csi->info;
+
+ for (unsigned int i = 0; i < info->clock_num; i++)
+ csi->clks[i].id = info->clocks[i];
+
+ return devm_clk_bulk_get(csi->dev, info->clock_num, csi->clks);
+}
+
+static int c3_mipi_csi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct c3_csi_device *csi;
+ int ret;
+
+ csi = devm_kzalloc(dev, sizeof(*csi), GFP_KERNEL);
+ if (!csi)
+ return -ENOMEM;
+
+ csi->info = of_device_get_match_data(dev);
+ csi->dev = dev;
+
+ ret = c3_mipi_csi_ioremap_resource(csi);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to ioremap resource\n");
+
+ ret = c3_mipi_csi_get_clocks(csi);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get clocks\n");
+
+ platform_set_drvdata(pdev, csi);
+
+ pm_runtime_enable(dev);
+
+ ret = c3_mipi_csi_subdev_init(csi);
+ if (ret)
+ goto err_disable_runtime_pm;
+
+ ret = c3_mipi_csi_async_register(csi);
+ if (ret)
+ goto err_deinit_subdev;
+
+ return 0;
+
+err_deinit_subdev:
+ c3_mipi_csi_subdev_deinit(csi);
+err_disable_runtime_pm:
+ pm_runtime_disable(dev);
+ return ret;
+};
+
+static void c3_mipi_csi_remove(struct platform_device *pdev)
+{
+ struct c3_csi_device *csi = platform_get_drvdata(pdev);
+
+ c3_mipi_csi_async_unregister(csi);
+ c3_mipi_csi_subdev_deinit(csi);
+
+ pm_runtime_disable(&pdev->dev);
+};
+
+static const struct c3_csi_info c3_mipi_csi_info = {
+ .clocks = {"vapb", "phy0"},
+ .clock_num = 2
+};
+
+static const struct of_device_id c3_mipi_csi_of_match[] = {
+ {
+ .compatible = "amlogic,c3-mipi-csi2",
+ .data = &c3_mipi_csi_info,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, c3_mipi_csi_of_match);
+
+static struct platform_driver c3_mipi_csi_driver = {
+ .probe = c3_mipi_csi_probe,
+ .remove = c3_mipi_csi_remove,
+ .driver = {
+ .name = "c3-mipi-csi2",
+ .of_match_table = c3_mipi_csi_of_match,
+ .pm = pm_ptr(&c3_mipi_csi_pm_ops),
+ },
+};
+
+module_platform_driver(c3_mipi_csi_driver);
+
+MODULE_AUTHOR("Keke Li <keke.li@amlogic.com>");
+MODULE_DESCRIPTION("Amlogic C3 MIPI CSI-2 receiver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
index 09409908ba5d..0c004bb8ba05 100644
--- a/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
+++ b/drivers/media/platform/amlogic/meson-ge2d/ge2d.c
@@ -391,8 +391,6 @@ static const struct vb2_ops ge2d_qops = {
.buf_queue = ge2d_buf_queue,
.start_streaming = ge2d_start_streaming,
.stop_streaming = ge2d_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int
@@ -1045,7 +1043,7 @@ MODULE_DEVICE_TABLE(of, meson_ge2d_match);
static struct platform_driver ge2d_drv = {
.probe = ge2d_probe,
- .remove_new = ge2d_remove,
+ .remove = ge2d_remove,
.driver = {
.name = "meson-ge2d",
.of_match_table = meson_ge2d_match,
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
index a57f9f4f3b87..85d518823159 100644
--- a/drivers/media/platform/amphion/vdec.c
+++ b/drivers/media/platform/amphion/vdec.c
@@ -195,7 +195,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
struct vdec_t *vdec = inst->priv;
int ret = 0;
- vpu_inst_lock(inst);
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE:
vdec->params.display_delay_enable = ctrl->val;
@@ -207,7 +206,6 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
ret = -EINVAL;
break;
}
- vpu_inst_unlock(inst);
return ret;
}
@@ -807,7 +805,7 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
cur_fmt = vpu_get_format(inst, inst->cap_format.type);
vbuf = &vpu_buf->m2m_buf.vb;
if (vbuf->vb2_buf.index != frame->id)
- dev_err(inst->dev, "[%d] buffer id(%d, %d) dismatch\n",
+ dev_err(inst->dev, "[%d] buffer id(%d, %d) mismatch\n",
inst->id, vbuf->vb2_buf.index, frame->id);
if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_READY && vdec->params.display_delay_enable)
diff --git a/drivers/media/platform/amphion/venc.c b/drivers/media/platform/amphion/venc.c
index 4eb57d793a9c..c5c1f1fbaa80 100644
--- a/drivers/media/platform/amphion/venc.c
+++ b/drivers/media/platform/amphion/venc.c
@@ -52,6 +52,7 @@ struct venc_t {
u32 ready_count;
u32 enable;
u32 stopped;
+ u32 memory_resource_configured;
u32 skipped_count;
u32 skipped_bytes;
@@ -518,7 +519,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *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;
@@ -579,7 +579,6 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
ret = -EINVAL;
break;
}
- vpu_inst_unlock(inst);
return ret;
}
@@ -680,6 +679,9 @@ static int venc_ctrl_init(struct vpu_inst *inst)
~(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME),
V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME);
+ v4l2_ctrl_new_std(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_AVERAGE_QP, 0, 51, 1, 0);
+
if (inst->ctrl_handler.error) {
ret = inst->ctrl_handler.error;
v4l2_ctrl_handler_free(&inst->ctrl_handler);
@@ -819,6 +821,7 @@ static int venc_get_one_encoded_frame(struct vpu_inst *inst,
vbuf->field = inst->cap_format.field;
vbuf->flags |= frame->info.pic_type;
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ vpu_set_buffer_average_qp(vbuf, frame->info.average_qp);
dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp);
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
venc->ready_count++;
@@ -941,10 +944,19 @@ static int venc_start_session(struct vpu_inst *inst, u32 type)
ret = vpu_iface_set_encode_params(inst, &venc->params, 0);
if (ret)
goto error;
+
+ venc->memory_resource_configured = false;
ret = vpu_session_configure_codec(inst);
if (ret)
goto error;
+ if (!venc->memory_resource_configured) {
+ vb2_queue_error(v4l2_m2m_get_src_vq(inst->fh.m2m_ctx));
+ vb2_queue_error(v4l2_m2m_get_dst_vq(inst->fh.m2m_ctx));
+ ret = -ENOMEM;
+ goto error;
+ }
+
inst->state = VPU_CODEC_STATE_CONFIGURED;
/*vpu_iface_config_memory_resource*/
@@ -983,6 +995,7 @@ static void venc_cleanup_mem_resource(struct vpu_inst *inst)
u32 i;
venc = inst->priv;
+ venc->memory_resource_configured = false;
for (i = 0; i < ARRAY_SIZE(venc->enc); i++)
vpu_free_dma(&venc->enc[i]);
@@ -1046,6 +1059,7 @@ static void venc_request_mem_resource(struct vpu_inst *inst,
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]);
+ venc->memory_resource_configured = true;
}
static void venc_cleanup_frames(struct venc_t *venc)
diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h
index 0246cf0ac3a8..1451549c9dd2 100644
--- a/drivers/media/platform/amphion/vpu.h
+++ b/drivers/media/platform/amphion/vpu.h
@@ -162,7 +162,6 @@ struct vpu_core {
struct delayed_work msg_delayed_work;
struct kfifo msg_fifo;
void *msg_buffer;
- unsigned int msg_buffer_size;
struct vpu_dev *vpu;
void *iface;
@@ -306,6 +305,7 @@ struct vpu_vb2_buffer {
dma_addr_t chroma_v;
unsigned int state;
u32 tag;
+ u32 average_qp;
};
void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val);
diff --git a/drivers/media/platform/amphion/vpu_core.c b/drivers/media/platform/amphion/vpu_core.c
index 3a2030d02e45..da00f5fc0e5d 100644
--- a/drivers/media/platform/amphion/vpu_core.c
+++ b/drivers/media/platform/amphion/vpu_core.c
@@ -250,6 +250,7 @@ static void vpu_core_get_vpu(struct vpu_core *core)
static int vpu_core_register(struct device *dev, struct vpu_core *core)
{
struct vpu_dev *vpu = dev_get_drvdata(dev);
+ unsigned int buffer_size;
int ret = 0;
dev_dbg(core->dev, "register core %s\n", vpu_core_type_desc(core->type));
@@ -263,14 +264,14 @@ static int vpu_core_register(struct device *dev, struct vpu_core *core)
}
INIT_WORK(&core->msg_work, vpu_msg_run_work);
INIT_DELAYED_WORK(&core->msg_delayed_work, vpu_msg_delayed_work);
- core->msg_buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE);
- core->msg_buffer = vzalloc(core->msg_buffer_size);
+ buffer_size = roundup_pow_of_two(VPU_MSG_BUFFER_SIZE);
+ core->msg_buffer = vzalloc(buffer_size);
if (!core->msg_buffer) {
dev_err(core->dev, "failed allocate buffer for fifo\n");
ret = -ENOMEM;
goto error;
}
- ret = kfifo_init(&core->msg_fifo, core->msg_buffer, core->msg_buffer_size);
+ ret = kfifo_init(&core->msg_fifo, core->msg_buffer, buffer_size);
if (ret) {
dev_err(core->dev, "failed init kfifo\n");
goto error;
@@ -864,7 +865,7 @@ MODULE_DEVICE_TABLE(of, vpu_core_dt_match);
static struct platform_driver amphion_vpu_core_driver = {
.probe = vpu_core_probe,
- .remove_new = vpu_core_remove,
+ .remove = vpu_core_remove,
.driver = {
.name = "amphion-vpu-core",
.of_match_table = vpu_core_dt_match,
diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h
index 7320852668d6..428d988cf2f7 100644
--- a/drivers/media/platform/amphion/vpu_defs.h
+++ b/drivers/media/platform/amphion/vpu_defs.h
@@ -114,6 +114,7 @@ struct vpu_enc_pic_info {
u32 wptr;
u32 crc;
s64 timestamp;
+ u32 average_qp;
};
struct vpu_dec_codec_info {
diff --git a/drivers/media/platform/amphion/vpu_drv.c b/drivers/media/platform/amphion/vpu_drv.c
index 2bf70aafd2ba..efbfd2652721 100644
--- a/drivers/media/platform/amphion/vpu_drv.c
+++ b/drivers/media/platform/amphion/vpu_drv.c
@@ -151,8 +151,8 @@ 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);
+ pm_runtime_set_suspended(dev);
return ret;
}
@@ -227,7 +227,7 @@ MODULE_DEVICE_TABLE(of, vpu_dt_match);
static struct platform_driver amphion_vpu_driver = {
.probe = vpu_probe,
- .remove_new = vpu_remove,
+ .remove = vpu_remove,
.driver = {
.name = "amphion-vpu",
.of_match_table = vpu_dt_match,
diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c
index d3425de7bccd..feca7d4220ed 100644
--- a/drivers/media/platform/amphion/vpu_malone.c
+++ b/drivers/media/platform/amphion/vpu_malone.c
@@ -3,6 +3,7 @@
* Copyright 2020-2021 NXP
*/
+#include <linux/bitfield.h>
#include <linux/init.h>
#include <linux/interconnect.h>
#include <linux/ioctl.h>
@@ -25,6 +26,10 @@
#include "vpu_imx8q.h"
#include "vpu_malone.h"
+static bool low_latency;
+module_param(low_latency, bool, 0644);
+MODULE_PARM_DESC(low_latency, "Set low latency frame flush mode: 0 (disable) or 1 (enable)");
+
#define CMD_SIZE 25600
#define MSG_SIZE 25600
#define CODEC_SIZE 0x1000
@@ -68,6 +73,12 @@
#define MALONE_DEC_FMT_RV_MASK BIT(21)
+#define MALONE_VERSION_MASK 0xFFFFF
+#define MALONE_VERSION(maj, min, inc) \
+ (FIELD_PREP(0xF0000, maj) | FIELD_PREP(0xFF00, min) | FIELD_PREP(0xFF, inc))
+#define CHECK_VERSION(iface, maj, min) \
+ (FIELD_GET(MALONE_VERSION_MASK, (iface)->fw_version) >= MALONE_VERSION(maj, min, 0))
+
enum vpu_malone_stream_input_mode {
INVALID_MODE = 0,
FRAME_LVL,
@@ -207,11 +218,6 @@ struct vpu_malone_dbglog_desc {
u32 reserved;
};
-struct vpu_malone_frame_buffer {
- u32 addr;
- u32 size;
-};
-
struct vpu_malone_udata {
u32 base;
u32 total_size;
@@ -337,6 +343,8 @@ struct vpu_dec_ctrl {
u32 buf_addr[VID_API_NUM_STREAMS];
};
+static const struct malone_padding_scode *get_padding_scode(u32 type, u32 fmt);
+
u32 vpu_malone_get_data_size(void)
{
return sizeof(struct vpu_dec_ctrl);
@@ -659,9 +667,15 @@ static int vpu_malone_set_params(struct vpu_shared_addr *shared,
hc->jpg[instance].jpg_mjpeg_interlaced = 0;
}
- hc->codec_param[instance].disp_imm = params->display_delay_enable ? 1 : 0;
- if (malone_format != MALONE_FMT_AVC)
+ if (params->display_delay_enable &&
+ get_padding_scode(SCODE_PADDING_BUFFLUSH, params->codec_format))
+ hc->codec_param[instance].disp_imm = 1;
+ else
+ hc->codec_param[instance].disp_imm = 0;
+
+ if (params->codec_format == V4L2_PIX_FMT_HEVC && !CHECK_VERSION(iface, 1, 9))
hc->codec_param[instance].disp_imm = 0;
+
hc->codec_param[instance].dbglog_enable = 0;
iface->dbglog_desc.level = 0;
@@ -1028,6 +1042,7 @@ static const struct malone_padding_scode padding_scodes[] = {
{SCODE_PADDING_EOS, V4L2_PIX_FMT_JPEG, {0x0, 0x0}},
{SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264, {0x15010000, 0x0}},
{SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_H264_MVC, {0x15010000, 0x0}},
+ {SCODE_PADDING_BUFFLUSH, V4L2_PIX_FMT_HEVC, {0x3e010000, 0x20}},
};
static const struct malone_padding_scode padding_scode_dft = {0x0, 0x0};
@@ -1062,8 +1077,11 @@ static int vpu_malone_add_padding_scode(struct vpu_buffer *stream_buffer,
int ret;
ps = get_padding_scode(scode_type, pixelformat);
- if (!ps)
+ if (!ps) {
+ if (scode_type == SCODE_PADDING_BUFFLUSH)
+ return 0;
return -EINVAL;
+ }
wptr = readl(&str_buf->wptr);
if (wptr < stream_buffer->phys || wptr > stream_buffer->phys + stream_buffer->length)
@@ -1567,7 +1585,15 @@ static int vpu_malone_input_frame_data(struct vpu_malone_str_buffer __iomem *str
vpu_malone_update_wptr(str_buf, wptr);
- if (disp_imm && !vpu_vb_is_codecconfig(vbuf)) {
+ /*
+ * Enable the low latency flush mode if display delay is set to 0
+ * or the low latency frame flush mode if it is set to 1.
+ * The low latency flush mode requires some padding data to be appended to each frame,
+ * but there must not be any padding data between the sequence header and the frame.
+ * This module is currently only supported for the H264 and HEVC formats,
+ * for other formats, vpu_malone_add_scode() will return 0.
+ */
+ if ((disp_imm || low_latency) && !vpu_vb_is_codecconfig(vbuf)) {
ret = vpu_malone_add_scode(inst->core->iface,
inst->id,
&inst->stream_buffer,
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
index c88738e8fff7..45707931bc4f 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.c
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -63,6 +63,13 @@ unsigned int vpu_get_buffer_state(struct vb2_v4l2_buffer *vbuf)
return vpu_buf->state;
}
+void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp)
+{
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+
+ vpu_buf->average_qp = qp;
+}
+
void vpu_v4l2_set_error(struct vpu_inst *inst)
{
vpu_inst_lock(inst);
@@ -539,6 +546,15 @@ static void vpu_vb2_buf_finish(struct vb2_buffer *vb)
struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_queue *q = vb->vb2_queue;
+ if (V4L2_TYPE_IS_CAPTURE(vb->type)) {
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+ V4L2_CID_MPEG_VIDEO_AVERAGE_QP);
+
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, vpu_buf->average_qp);
+ }
+
if (vbuf->flags & V4L2_BUF_FLAG_LAST)
vpu_notify_eos(inst);
@@ -630,8 +646,6 @@ static const struct vb2_ops vpu_vb2_ops = {
.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)
@@ -825,6 +839,7 @@ int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func)
vfd->fops = vdec_get_fops();
vfd->ioctl_ops = vdec_get_ioctl_ops();
}
+ video_set_drvdata(vfd, vpu);
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret) {
@@ -832,7 +847,6 @@ int vpu_add_func(struct vpu_dev *vpu, struct vpu_func *func)
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);
diff --git a/drivers/media/platform/amphion/vpu_v4l2.h b/drivers/media/platform/amphion/vpu_v4l2.h
index 60f43056a7a2..56f2939fa84d 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.h
+++ b/drivers/media/platform/amphion/vpu_v4l2.h
@@ -12,6 +12,7 @@ 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);
+void vpu_set_buffer_average_qp(struct vb2_v4l2_buffer *vbuf, u32 qp);
int vpu_v4l2_open(struct file *file, struct vpu_inst *inst);
int vpu_v4l2_close(struct file *file);
diff --git a/drivers/media/platform/amphion/vpu_windsor.c b/drivers/media/platform/amphion/vpu_windsor.c
index 5f1101d7cf9e..e7d37aa4b826 100644
--- a/drivers/media/platform/amphion/vpu_windsor.c
+++ b/drivers/media/platform/amphion/vpu_windsor.c
@@ -499,6 +499,7 @@ struct windsor_pic_info {
u32 proc_dacc_rng_wr_cnt;
s32 tv_s;
u32 tv_ns;
+ u32 average_qp;
};
u32 vpu_windsor_get_data_size(void)
@@ -734,6 +735,7 @@ static void vpu_windsor_unpack_pic_info(struct vpu_rpc_event *pkt, void *data)
info->wptr = get_ptr(windsor->str_buff_wptr);
info->crc = windsor->frame_crc;
info->timestamp = timespec64_to_ns(&ts);
+ info->average_qp = windsor->average_qp;
}
static void vpu_windsor_unpack_mem_req(struct vpu_rpc_event *pkt, void *data)
diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c
index fc6050e3be0d..54cae0da9aca 100644
--- a/drivers/media/platform/aspeed/aspeed-video.c
+++ b/drivers/media/platform/aspeed/aspeed-video.c
@@ -1891,8 +1891,6 @@ static void aspeed_video_buf_queue(struct vb2_buffer *vb)
static const struct vb2_ops aspeed_video_vb2_ops = {
.queue_setup = aspeed_video_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_prepare = aspeed_video_buf_prepare,
.start_streaming = aspeed_video_start_streaming,
.stop_streaming = aspeed_video_stop_streaming,
@@ -2226,7 +2224,7 @@ static struct platform_driver aspeed_video_driver = {
.of_match_table = aspeed_video_of_match,
},
.probe = aspeed_video_probe,
- .remove_new = aspeed_video_remove,
+ .remove = aspeed_video_remove,
};
module_platform_driver(aspeed_video_driver);
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index c1108df72dd5..a05a744cbb75 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -242,7 +242,7 @@ static irqreturn_t isi_interrupt(int irq, void *dev_id)
#define WAIT_ISI_DISABLE 0
static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
{
- unsigned long timeout;
+ unsigned long time_left;
/*
* The reset or disable will only succeed if we have a
* pixel clock from the camera.
@@ -257,9 +257,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset)
isi_writel(isi, ISI_CTRL, ISI_CTRL_DIS);
}
- timeout = wait_for_completion_timeout(&isi->complete,
- msecs_to_jiffies(500));
- if (timeout == 0)
+ time_left = wait_for_completion_timeout(&isi->complete,
+ msecs_to_jiffies(500));
+ if (time_left == 0)
return -ETIMEDOUT;
return 0;
@@ -526,8 +526,6 @@ static const struct vb2_ops isi_video_qops = {
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int isi_g_fmt_vid_cap(struct file *file, void *priv,
@@ -1074,16 +1072,12 @@ static int isi_formats_init(struct atmel_isi *isi)
return -ENXIO;
isi->num_user_formats = num_fmts;
- isi->user_formats = devm_kcalloc(isi->dev,
- num_fmts, sizeof(struct isi_format *),
- GFP_KERNEL);
+ isi->user_formats = devm_kmemdup_array(isi->dev, isi_fmts, num_fmts,
+ sizeof(*isi_fmts), GFP_KERNEL);
if (!isi->user_formats)
return -ENOMEM;
- memcpy(isi->user_formats, isi_fmts,
- num_fmts * sizeof(struct isi_format *));
isi->current_fmt = isi->user_formats[0];
-
return 0;
}
@@ -1367,7 +1361,7 @@ static struct platform_driver atmel_isi_driver = {
.pm = &atmel_isi_dev_pm_ops,
},
.probe = atmel_isi_probe,
- .remove_new = atmel_isi_remove,
+ .remove = atmel_isi_remove,
};
module_platform_driver(atmel_isi_driver);
diff --git a/drivers/media/platform/broadcom/Kconfig b/drivers/media/platform/broadcom/Kconfig
new file mode 100644
index 000000000000..32b76ebfcd9a
--- /dev/null
+++ b/drivers/media/platform/broadcom/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_BCM2835_UNICAM
+ tristate "Broadcom BCM283x/BCM271x Unicam video capture driver"
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on COMMON_CLK && PM
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Say Y here to enable support for the BCM283x/BCM271x CSI-2 receiver.
+ This is a V4L2 driver that controls the CSI-2 receiver directly,
+ independently from the VC4 firmware.
+
+ This driver is mutually exclusive with the use of bcm2835-camera. The
+ firmware will disable all access to the peripheral from within the
+ firmware if it finds a DT node using it, and bcm2835-camera will
+ therefore fail to probe.
+
+ To compile this driver as a module, choose M here. The module will be
+ called bcm2835-unicam.
diff --git a/drivers/media/platform/broadcom/Makefile b/drivers/media/platform/broadcom/Makefile
new file mode 100644
index 000000000000..03d2045aba2e
--- /dev/null
+++ b/drivers/media/platform/broadcom/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o
diff --git a/drivers/media/platform/broadcom/bcm2835-unicam-regs.h b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h
new file mode 100644
index 000000000000..fce6fa92707c
--- /dev/null
+++ b/drivers/media/platform/broadcom/bcm2835-unicam-regs.h
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * Copyright (C) 2017-2020 Raspberry Pi Trading.
+ * Dave Stevenson <dave.stevenson@raspberrypi.com>
+ */
+
+#ifndef VC4_REGS_UNICAM_H
+#define VC4_REGS_UNICAM_H
+
+#include <linux/bits.h>
+
+/*
+ * The following values are taken from files found within the code drop
+ * made by Broadcom for the BCM21553 Graphics Driver, predominantly in
+ * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h.
+ * They have been modified to be only the register offset.
+ */
+#define UNICAM_CTRL 0x000
+#define UNICAM_STA 0x004
+#define UNICAM_ANA 0x008
+#define UNICAM_PRI 0x00c
+#define UNICAM_CLK 0x010
+#define UNICAM_CLT 0x014
+#define UNICAM_DAT0 0x018
+#define UNICAM_DAT1 0x01c
+#define UNICAM_DAT2 0x020
+#define UNICAM_DAT3 0x024
+#define UNICAM_DLT 0x028
+#define UNICAM_CMP0 0x02c
+#define UNICAM_CMP1 0x030
+#define UNICAM_CAP0 0x034
+#define UNICAM_CAP1 0x038
+#define UNICAM_ICTL 0x100
+#define UNICAM_ISTA 0x104
+#define UNICAM_IDI0 0x108
+#define UNICAM_IPIPE 0x10c
+#define UNICAM_IBSA0 0x110
+#define UNICAM_IBEA0 0x114
+#define UNICAM_IBLS 0x118
+#define UNICAM_IBWP 0x11c
+#define UNICAM_IHWIN 0x120
+#define UNICAM_IHSTA 0x124
+#define UNICAM_IVWIN 0x128
+#define UNICAM_IVSTA 0x12c
+#define UNICAM_ICC 0x130
+#define UNICAM_ICS 0x134
+#define UNICAM_IDC 0x138
+#define UNICAM_IDPO 0x13c
+#define UNICAM_IDCA 0x140
+#define UNICAM_IDCD 0x144
+#define UNICAM_IDS 0x148
+#define UNICAM_DCS 0x200
+#define UNICAM_DBSA0 0x204
+#define UNICAM_DBEA0 0x208
+#define UNICAM_DBWP 0x20c
+#define UNICAM_DBCTL 0x300
+#define UNICAM_IBSA1 0x304
+#define UNICAM_IBEA1 0x308
+#define UNICAM_IDI1 0x30c
+#define UNICAM_DBSA1 0x310
+#define UNICAM_DBEA1 0x314
+#define UNICAM_MISC 0x400
+
+/*
+ * The following bitmasks are from the kernel released by Broadcom
+ * for Android - https://android.googlesource.com/kernel/bcm/
+ * The Rhea, Hawaii, and Java chips all contain the same VideoCore4
+ * Unicam block as BCM2835, as defined in eg
+ * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar.
+ * Values reworked to use the kernel BIT and GENMASK macros.
+ *
+ * Some of the bit mnenomics have been amended to match the datasheet.
+ */
+/* UNICAM_CTRL Register */
+#define UNICAM_CPE BIT(0)
+#define UNICAM_MEM BIT(1)
+#define UNICAM_CPR BIT(2)
+#define UNICAM_CPM_MASK GENMASK(3, 3)
+#define UNICAM_CPM_CSI2 0
+#define UNICAM_CPM_CCP2 1
+#define UNICAM_SOE BIT(4)
+#define UNICAM_DCM_MASK GENMASK(5, 5)
+#define UNICAM_DCM_STROBE 0
+#define UNICAM_DCM_DATA 1
+#define UNICAM_SLS BIT(6)
+#define UNICAM_PFT_MASK GENMASK(11, 8)
+#define UNICAM_OET_MASK GENMASK(20, 12)
+
+/* UNICAM_STA Register */
+#define UNICAM_SYN BIT(0)
+#define UNICAM_CS BIT(1)
+#define UNICAM_SBE BIT(2)
+#define UNICAM_PBE BIT(3)
+#define UNICAM_HOE BIT(4)
+#define UNICAM_PLE BIT(5)
+#define UNICAM_SSC BIT(6)
+#define UNICAM_CRCE BIT(7)
+#define UNICAM_OES BIT(8)
+#define UNICAM_IFO BIT(9)
+#define UNICAM_OFO BIT(10)
+#define UNICAM_BFO BIT(11)
+#define UNICAM_DL BIT(12)
+#define UNICAM_PS BIT(13)
+#define UNICAM_IS BIT(14)
+#define UNICAM_PI0 BIT(15)
+#define UNICAM_PI1 BIT(16)
+#define UNICAM_FSI_S BIT(17)
+#define UNICAM_FEI_S BIT(18)
+#define UNICAM_LCI_S BIT(19)
+#define UNICAM_BUF0_RDY BIT(20)
+#define UNICAM_BUF0_NO BIT(21)
+#define UNICAM_BUF1_RDY BIT(22)
+#define UNICAM_BUF1_NO BIT(23)
+#define UNICAM_DI BIT(24)
+
+#define UNICAM_STA_MASK_ALL \
+ (UNICAM_SBE | UNICAM_PBE | UNICAM_HOE | UNICAM_PLE | UNICAM_SSC | \
+ UNICAM_CRCE | UNICAM_IFO | UNICAM_OFO | UNICAM_DL | UNICAM_PS | \
+ UNICAM_PI0 | UNICAM_PI1)
+
+/* UNICAM_ANA Register */
+#define UNICAM_APD BIT(0)
+#define UNICAM_BPD BIT(1)
+#define UNICAM_AR BIT(2)
+#define UNICAM_DDL BIT(3)
+#define UNICAM_CTATADJ_MASK GENMASK(7, 4)
+#define UNICAM_PTATADJ_MASK GENMASK(11, 8)
+
+/* UNICAM_PRI Register */
+#define UNICAM_PE BIT(0)
+#define UNICAM_PT_MASK GENMASK(2, 1)
+#define UNICAM_NP_MASK GENMASK(7, 4)
+#define UNICAM_PP_MASK GENMASK(11, 8)
+#define UNICAM_BS_MASK GENMASK(15, 12)
+#define UNICAM_BL_MASK GENMASK(17, 16)
+
+/* UNICAM_CLK Register */
+#define UNICAM_CLE BIT(0)
+#define UNICAM_CLPD BIT(1)
+#define UNICAM_CLLPE BIT(2)
+#define UNICAM_CLHSE BIT(3)
+#define UNICAM_CLTRE BIT(4)
+#define UNICAM_CLAC_MASK GENMASK(8, 5)
+#define UNICAM_CLSTE BIT(29)
+
+/* UNICAM_CLT Register */
+#define UNICAM_CLT1_MASK GENMASK(7, 0)
+#define UNICAM_CLT2_MASK GENMASK(15, 8)
+
+/* UNICAM_DATn Registers */
+#define UNICAM_DLE BIT(0)
+#define UNICAM_DLPD BIT(1)
+#define UNICAM_DLLPE BIT(2)
+#define UNICAM_DLHSE BIT(3)
+#define UNICAM_DLTRE BIT(4)
+#define UNICAM_DLSM BIT(5)
+#define UNICAM_DLFO BIT(28)
+#define UNICAM_DLSTE BIT(29)
+
+#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE | UNICAM_DLFO)
+
+/* UNICAM_DLT Register */
+#define UNICAM_DLT1_MASK GENMASK(7, 0)
+#define UNICAM_DLT2_MASK GENMASK(15, 8)
+#define UNICAM_DLT3_MASK GENMASK(23, 16)
+
+/* UNICAM_ICTL Register */
+#define UNICAM_FSIE BIT(0)
+#define UNICAM_FEIE BIT(1)
+#define UNICAM_IBOB BIT(2)
+#define UNICAM_FCM BIT(3)
+#define UNICAM_TFC BIT(4)
+#define UNICAM_LIP_MASK GENMASK(6, 5)
+#define UNICAM_LCIE_MASK GENMASK(28, 16)
+
+/* UNICAM_IDI0/1 Register */
+#define UNICAM_ID0_MASK GENMASK(7, 0)
+#define UNICAM_ID1_MASK GENMASK(15, 8)
+#define UNICAM_ID2_MASK GENMASK(23, 16)
+#define UNICAM_ID3_MASK GENMASK(31, 24)
+
+/* UNICAM_ISTA Register */
+#define UNICAM_FSI BIT(0)
+#define UNICAM_FEI BIT(1)
+#define UNICAM_LCI BIT(2)
+
+#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI | UNICAM_FEI | UNICAM_LCI)
+
+/* UNICAM_IPIPE Register */
+#define UNICAM_PUM_MASK GENMASK(2, 0)
+/* Unpacking modes */
+#define UNICAM_PUM_NONE 0
+#define UNICAM_PUM_UNPACK6 1
+#define UNICAM_PUM_UNPACK7 2
+#define UNICAM_PUM_UNPACK8 3
+#define UNICAM_PUM_UNPACK10 4
+#define UNICAM_PUM_UNPACK12 5
+#define UNICAM_PUM_UNPACK14 6
+#define UNICAM_PUM_UNPACK16 7
+#define UNICAM_DDM_MASK GENMASK(6, 3)
+#define UNICAM_PPM_MASK GENMASK(9, 7)
+/* Packing modes */
+#define UNICAM_PPM_NONE 0
+#define UNICAM_PPM_PACK8 1
+#define UNICAM_PPM_PACK10 2
+#define UNICAM_PPM_PACK12 3
+#define UNICAM_PPM_PACK14 4
+#define UNICAM_PPM_PACK16 5
+#define UNICAM_DEM_MASK GENMASK(11, 10)
+#define UNICAM_DEBL_MASK GENMASK(14, 12)
+#define UNICAM_ICM_MASK GENMASK(16, 15)
+#define UNICAM_IDM_MASK GENMASK(17, 17)
+
+/* UNICAM_ICC Register */
+#define UNICAM_ICFL_MASK GENMASK(4, 0)
+#define UNICAM_ICFH_MASK GENMASK(9, 5)
+#define UNICAM_ICST_MASK GENMASK(12, 10)
+#define UNICAM_ICLT_MASK GENMASK(15, 13)
+#define UNICAM_ICLL_MASK GENMASK(31, 16)
+
+/* UNICAM_DCS Register */
+#define UNICAM_DIE BIT(0)
+#define UNICAM_DIM BIT(1)
+#define UNICAM_DBOB BIT(3)
+#define UNICAM_FDE BIT(4)
+#define UNICAM_LDP BIT(5)
+#define UNICAM_EDL_MASK GENMASK(15, 8)
+
+/* UNICAM_DBCTL Register */
+#define UNICAM_DBEN BIT(0)
+#define UNICAM_BUF0_IE BIT(1)
+#define UNICAM_BUF1_IE BIT(2)
+
+/* UNICAM_CMP[0,1] register */
+#define UNICAM_PCE BIT(31)
+#define UNICAM_GI BIT(9)
+#define UNICAM_CPH BIT(8)
+#define UNICAM_PCVC_MASK GENMASK(7, 6)
+#define UNICAM_PCDT_MASK GENMASK(5, 0)
+
+/* UNICAM_MISC register */
+#define UNICAM_FL0 BIT(6)
+#define UNICAM_FL1 BIT(9)
+
+#endif
diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c
new file mode 100644
index 000000000000..f10064107d54
--- /dev/null
+++ b/drivers/media/platform/broadcom/bcm2835-unicam.c
@@ -0,0 +1,2757 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * BCM283x / BCM271x Unicam Capture Driver
+ *
+ * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd.
+ * Copyright (C) 2024 - Ideas on Board
+ *
+ * Dave Stevenson <dave.stevenson@raspberrypi.com>
+ *
+ * Based on TI am437x driver by
+ * Benoit Parrot <bparrot@ti.com>
+ * Lad, Prabhakar <prabhakar.csengg@gmail.com>
+ *
+ * and TI CAL camera interface driver by
+ * Benoit Parrot <bparrot@ti.com>
+ *
+ *
+ * There are two camera drivers in the kernel for BCM283x - this one and
+ * bcm2835-camera (currently in staging).
+ *
+ * This driver directly controls the Unicam peripheral - there is no
+ * involvement with the VideoCore firmware. Unicam receives CSI-2 or CCP2 data
+ * and writes it into SDRAM. The only potential processing options are to
+ * repack Bayer data into an alternate format, and applying windowing. The
+ * repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P to
+ * V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, but
+ * not generically up to V4L2_PIX_FMT_Sxxxx16. Support for windowing may be
+ * added later.
+ *
+ * It should be possible to connect this driver to any sensor with a suitable
+ * output interface and V4L2 subdevice driver.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "bcm2835-unicam-regs.h"
+
+#define UNICAM_MODULE_NAME "unicam"
+
+/*
+ * Unicam must request a minimum of 250Mhz from the VPU clock.
+ * Otherwise the input FIFOs overrun and cause image corruption.
+ */
+#define UNICAM_MIN_VPU_CLOCK_RATE (250 * 1000 * 1000)
+
+/* Unicam has an internal DMA alignment constraint of 16 bytes for each line. */
+#define UNICAM_DMA_BPL_ALIGNMENT 16
+
+/*
+ * The image stride is stored in a 16 bit register, and needs to be aligned to
+ * the DMA constraint. As the ISP in the same SoC has a 32 bytes alignment
+ * constraint on its input, set the image stride alignment to 32 bytes here as
+ * well to avoid incompatible configurations.
+ */
+#define UNICAM_IMAGE_BPL_ALIGNMENT 32
+#define UNICAM_IMAGE_MAX_BPL ((1U << 16) - UNICAM_IMAGE_BPL_ALIGNMENT)
+
+/*
+ * Max width is therefore determined by the max stride divided by the number of
+ * bits per pixel. Take 32bpp as a worst case. No imposed limit on the height,
+ * so adopt a square image for want of anything better.
+ */
+#define UNICAM_IMAGE_MIN_WIDTH 16
+#define UNICAM_IMAGE_MIN_HEIGHT 16
+#define UNICAM_IMAGE_MAX_WIDTH (UNICAM_IMAGE_MAX_BPL / 4)
+#define UNICAM_IMAGE_MAX_HEIGHT UNICAM_IMAGE_MAX_WIDTH
+
+/*
+ * There's no intrinsic limits on the width and height for embedded data. Use
+ * the same maximum values as for the image, to avoid overflows in the image
+ * size computation.
+ */
+#define UNICAM_META_MIN_WIDTH 1
+#define UNICAM_META_MIN_HEIGHT 1
+#define UNICAM_META_MAX_WIDTH UNICAM_IMAGE_MAX_WIDTH
+#define UNICAM_META_MAX_HEIGHT UNICAM_IMAGE_MAX_HEIGHT
+
+/*
+ * Size of the dummy buffer. Can be any size really, but the DMA
+ * allocation works in units of page sizes.
+ */
+#define UNICAM_DUMMY_BUF_SIZE PAGE_SIZE
+
+enum unicam_pad {
+ UNICAM_SD_PAD_SINK,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ UNICAM_SD_PAD_SOURCE_METADATA,
+ UNICAM_SD_NUM_PADS
+};
+
+enum unicam_node_type {
+ UNICAM_IMAGE_NODE,
+ UNICAM_METADATA_NODE,
+ UNICAM_MAX_NODES
+};
+
+/*
+ * struct unicam_format_info - Unicam media bus format information
+ * @fourcc: V4L2 pixel format FCC identifier. 0 if n/a.
+ * @unpacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded
+ * out to 16bpp. 0 if n/a.
+ * @code: V4L2 media bus format code.
+ * @depth: Bits per pixel as delivered from the source.
+ * @csi_dt: CSI data type.
+ * @unpack: PUM value when unpacking to @unpacked_fourcc
+ */
+struct unicam_format_info {
+ u32 fourcc;
+ u32 unpacked_fourcc;
+ u32 code;
+ u8 depth;
+ u8 csi_dt;
+ u8 unpack;
+};
+
+struct unicam_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ dma_addr_t dma_addr;
+ unsigned int size;
+};
+
+static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct unicam_buffer, vb.vb2_buf);
+}
+
+struct unicam_node {
+ bool registered;
+ unsigned int id;
+
+ /* Pointer to the current v4l2_buffer */
+ struct unicam_buffer *cur_frm;
+ /* Pointer to the next v4l2_buffer */
+ struct unicam_buffer *next_frm;
+ /* Used to store current pixel format */
+ struct v4l2_format fmt;
+ /* Buffer queue used in video-buf */
+ struct vb2_queue buffer_queue;
+ /* Queue of filled frames */
+ struct list_head dma_queue;
+ /* IRQ lock for DMA queue */
+ spinlock_t dma_queue_lock;
+ /* Identifies video device for this channel */
+ struct video_device video_dev;
+ /* Pointer to the parent handle */
+ struct unicam_device *dev;
+ struct media_pad pad;
+ /*
+ * Dummy buffer intended to be used by unicam
+ * if we have no other queued buffers to swap to.
+ */
+ struct unicam_buffer dummy_buf;
+ void *dummy_buf_cpu_addr;
+};
+
+struct unicam_device {
+ struct kref kref;
+
+ /* peripheral base address */
+ void __iomem *base;
+ /* clock gating base address */
+ void __iomem *clk_gate_base;
+ /* lp clock handle */
+ struct clk *clock;
+ /* vpu clock handle */
+ struct clk *vpu_clock;
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ struct media_device mdev;
+
+ /* parent device */
+ struct device *dev;
+ /* subdevice async notifier */
+ struct v4l2_async_notifier notifier;
+ unsigned int sequence;
+ bool frame_started;
+
+ /* Sensor node */
+ struct {
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ } sensor;
+
+ /* Internal subdev */
+ struct {
+ struct v4l2_subdev sd;
+ struct media_pad pads[UNICAM_SD_NUM_PADS];
+ unsigned int enabled_streams;
+ } subdev;
+
+ enum v4l2_mbus_type bus_type;
+ /*
+ * Stores bus.mipi_csi2.flags for CSI2 sensors, or
+ * bus.mipi_csi1.strobe for CCP2.
+ */
+ unsigned int bus_flags;
+ unsigned int max_data_lanes;
+
+ struct {
+ struct media_pipeline pipe;
+ unsigned int num_data_lanes;
+ unsigned int nodes;
+ } pipe;
+
+ /* Lock used for the video devices of both nodes */
+ struct mutex lock;
+ struct unicam_node node[UNICAM_MAX_NODES];
+};
+
+static inline struct unicam_device *
+notifier_to_unicam_device(struct v4l2_async_notifier *notifier)
+{
+ return container_of(notifier, struct unicam_device, notifier);
+}
+
+static inline struct unicam_device *
+sd_to_unicam_device(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct unicam_device, subdev.sd);
+}
+
+static void unicam_release(struct kref *kref)
+{
+ struct unicam_device *unicam =
+ container_of(kref, struct unicam_device, kref);
+
+ if (unicam->mdev.dev)
+ media_device_cleanup(&unicam->mdev);
+
+ mutex_destroy(&unicam->lock);
+ kfree(unicam);
+}
+
+static struct unicam_device *unicam_get(struct unicam_device *unicam)
+{
+ kref_get(&unicam->kref);
+
+ return unicam;
+}
+
+static void unicam_put(struct unicam_device *unicam)
+{
+ kref_put(&unicam->kref, unicam_release);
+}
+
+/* -----------------------------------------------------------------------------
+ * Misc helper functions
+ */
+
+static inline bool unicam_sd_pad_is_source(u32 pad)
+{
+ /* Camera RX has 1 sink pad, and N source pads */
+ return pad != UNICAM_SD_PAD_SINK;
+}
+
+static inline bool is_metadata_node(struct unicam_node *node)
+{
+ return node->video_dev.device_caps & V4L2_CAP_META_CAPTURE;
+}
+
+static inline bool is_image_node(struct unicam_node *node)
+{
+ return node->video_dev.device_caps & V4L2_CAP_VIDEO_CAPTURE;
+}
+
+/* -----------------------------------------------------------------------------
+ * Format data table and helper functions
+ */
+
+static const struct v4l2_mbus_framefmt unicam_default_image_format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ .flags = 0,
+};
+
+static const struct v4l2_mbus_framefmt unicam_default_meta_format = {
+ .width = 640,
+ .height = 2,
+ .code = MEDIA_BUS_FMT_META_8,
+ .field = V4L2_FIELD_NONE,
+};
+
+static const struct unicam_format_info unicam_image_formats[] = {
+ /* YUV Formats */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ }, {
+ /* RGB Formats */
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RGB565,
+ }, {
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ }, {
+ /* Bayer Formats */
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SBGGR10,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGBRG10,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGRBG10,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SRGGB10,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SBGGR12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGBRG12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGRBG12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SRGGB12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SBGGR14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SBGGR14,
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGBRG14,
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SGRBG14,
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_SRGGB14,
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ }, {
+ /* 16 bit Bayer formats could be supported. */
+
+ /* Greyscale formats */
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y10P,
+ .unpacked_fourcc = V4L2_PIX_FMT_Y10,
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .unpack = UNICAM_PUM_UNPACK10,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y12P,
+ .unpacked_fourcc = V4L2_PIX_FMT_Y12,
+ .code = MEDIA_BUS_FMT_Y12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .unpack = UNICAM_PUM_UNPACK12,
+ }, {
+ .fourcc = V4L2_PIX_FMT_Y14P,
+ .unpacked_fourcc = V4L2_PIX_FMT_Y14,
+ .code = MEDIA_BUS_FMT_Y14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .unpack = UNICAM_PUM_UNPACK14,
+ },
+};
+
+static const struct unicam_format_info unicam_meta_formats[] = {
+ {
+ .fourcc = V4L2_META_FMT_GENERIC_8,
+ .code = MEDIA_BUS_FMT_META_8,
+ .depth = 8,
+ }, {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_10,
+ .code = MEDIA_BUS_FMT_META_10,
+ .depth = 10,
+ }, {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_12,
+ .code = MEDIA_BUS_FMT_META_12,
+ .depth = 12,
+ }, {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_14,
+ .code = MEDIA_BUS_FMT_META_14,
+ .depth = 14,
+ },
+};
+
+/* Format setup functions */
+static const struct unicam_format_info *
+unicam_find_format_by_code(u32 code, u32 pad)
+{
+ const struct unicam_format_info *formats;
+ unsigned int num_formats;
+ unsigned int i;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ formats = unicam_image_formats;
+ num_formats = ARRAY_SIZE(unicam_image_formats);
+ } else {
+ formats = unicam_meta_formats;
+ num_formats = ARRAY_SIZE(unicam_meta_formats);
+ }
+
+ for (i = 0; i < num_formats; i++) {
+ if (formats[i].code == code)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static const struct unicam_format_info *
+unicam_find_format_by_fourcc(u32 fourcc, u32 pad)
+{
+ const struct unicam_format_info *formats;
+ unsigned int num_formats;
+ unsigned int i;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ formats = unicam_image_formats;
+ num_formats = ARRAY_SIZE(unicam_image_formats);
+ } else {
+ formats = unicam_meta_formats;
+ num_formats = ARRAY_SIZE(unicam_meta_formats);
+ }
+
+ for (i = 0; i < num_formats; ++i) {
+ if (formats[i].fourcc == fourcc ||
+ formats[i].unpacked_fourcc == fourcc)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static void unicam_calc_image_size_bpl(struct unicam_device *unicam,
+ const struct unicam_format_info *fmtinfo,
+ struct v4l2_pix_format *pix)
+{
+ u32 min_bpl;
+
+ v4l_bound_align_image(&pix->width, UNICAM_IMAGE_MIN_WIDTH,
+ UNICAM_IMAGE_MAX_WIDTH, 2,
+ &pix->height, UNICAM_IMAGE_MIN_HEIGHT,
+ UNICAM_IMAGE_MAX_HEIGHT, 0, 0);
+
+ /* Unpacking always goes to 16bpp */
+ if (pix->pixelformat == fmtinfo->unpacked_fourcc)
+ min_bpl = pix->width * 2;
+ else
+ min_bpl = pix->width * fmtinfo->depth / 8;
+ min_bpl = ALIGN(min_bpl, UNICAM_IMAGE_BPL_ALIGNMENT);
+
+ pix->bytesperline = ALIGN(pix->bytesperline, UNICAM_IMAGE_BPL_ALIGNMENT);
+ pix->bytesperline = clamp_t(unsigned int, pix->bytesperline, min_bpl,
+ UNICAM_IMAGE_MAX_BPL);
+
+ pix->sizeimage = pix->height * pix->bytesperline;
+}
+
+static void unicam_calc_meta_size_bpl(struct unicam_device *unicam,
+ const struct unicam_format_info *fmtinfo,
+ struct v4l2_meta_format *meta)
+{
+ v4l_bound_align_image(&meta->width, UNICAM_META_MIN_WIDTH,
+ UNICAM_META_MAX_WIDTH, 0,
+ &meta->height, UNICAM_META_MIN_HEIGHT,
+ UNICAM_META_MAX_HEIGHT, 0, 0);
+
+ meta->bytesperline = ALIGN(meta->width * fmtinfo->depth / 8,
+ UNICAM_DMA_BPL_ALIGNMENT);
+ meta->buffersize = meta->height * meta->bytesperline;
+}
+
+/* -----------------------------------------------------------------------------
+ * Hardware handling
+ */
+
+static inline void unicam_clk_write(struct unicam_device *unicam, u32 val)
+{
+ /* Pass the CM_PASSWORD along with the value. */
+ writel(val | 0x5a000000, unicam->clk_gate_base);
+}
+
+static inline u32 unicam_reg_read(struct unicam_device *unicam, u32 offset)
+{
+ return readl(unicam->base + offset);
+}
+
+static inline void unicam_reg_write(struct unicam_device *unicam, u32 offset, u32 val)
+{
+ writel(val, unicam->base + offset);
+}
+
+static inline int unicam_get_field(u32 value, u32 mask)
+{
+ return (value & mask) >> __ffs(mask);
+}
+
+static inline void unicam_set_field(u32 *valp, u32 field, u32 mask)
+{
+ u32 val = *valp;
+
+ val &= ~mask;
+ val |= (field << __ffs(mask)) & mask;
+ *valp = val;
+}
+
+static inline void unicam_reg_write_field(struct unicam_device *unicam, u32 offset,
+ u32 field, u32 mask)
+{
+ u32 val = unicam_reg_read(unicam, offset);
+
+ unicam_set_field(&val, field, mask);
+ unicam_reg_write(unicam, offset, val);
+}
+
+static void unicam_wr_dma_addr(struct unicam_node *node,
+ struct unicam_buffer *buf)
+{
+ /*
+ * Due to a HW bug causing buffer overruns in circular buffer mode under
+ * certain (not yet fully known) conditions, the dummy buffer allocation
+ * is set to a a single page size, but the hardware gets programmed with
+ * a buffer size of 0.
+ */
+ dma_addr_t endaddr = buf->dma_addr +
+ (buf != &node->dummy_buf ? buf->size : 0);
+
+ if (node->id == UNICAM_IMAGE_NODE) {
+ unicam_reg_write(node->dev, UNICAM_IBSA0, buf->dma_addr);
+ unicam_reg_write(node->dev, UNICAM_IBEA0, endaddr);
+ } else {
+ unicam_reg_write(node->dev, UNICAM_DBSA0, buf->dma_addr);
+ unicam_reg_write(node->dev, UNICAM_DBEA0, endaddr);
+ }
+}
+
+static unsigned int unicam_get_lines_done(struct unicam_device *unicam)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ unsigned int stride = node->fmt.fmt.pix.bytesperline;
+ struct unicam_buffer *frm = node->cur_frm;
+ dma_addr_t cur_addr;
+
+ if (!frm)
+ return 0;
+
+ cur_addr = unicam_reg_read(unicam, UNICAM_IBWP);
+ return (unsigned int)(cur_addr - frm->dma_addr) / stride;
+}
+
+static void unicam_schedule_next_buffer(struct unicam_node *node)
+{
+ struct unicam_buffer *buf;
+
+ buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list);
+ node->next_frm = buf;
+ list_del(&buf->list);
+
+ unicam_wr_dma_addr(node, buf);
+}
+
+static void unicam_schedule_dummy_buffer(struct unicam_node *node)
+{
+ int node_id = is_image_node(node) ? UNICAM_IMAGE_NODE : UNICAM_METADATA_NODE;
+
+ dev_dbg(node->dev->dev, "Scheduling dummy buffer for node %d\n", node_id);
+
+ unicam_wr_dma_addr(node, &node->dummy_buf);
+
+ node->next_frm = NULL;
+}
+
+static void unicam_process_buffer_complete(struct unicam_node *node,
+ unsigned int sequence)
+{
+ node->cur_frm->vb.field = node->fmt.fmt.pix.field;
+ node->cur_frm->vb.sequence = sequence;
+
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void unicam_queue_event_sof(struct unicam_device *unicam)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ .u.frame_sync.frame_sequence = unicam->sequence,
+ };
+
+ v4l2_event_queue(&node->video_dev, &event);
+}
+
+static irqreturn_t unicam_isr(int irq, void *dev)
+{
+ struct unicam_device *unicam = dev;
+ unsigned int lines_done = unicam_get_lines_done(dev);
+ unsigned int sequence = unicam->sequence;
+ unsigned int i;
+ u32 ista, sta;
+ bool fe;
+ u64 ts;
+
+ sta = unicam_reg_read(unicam, UNICAM_STA);
+ /* Write value back to clear the interrupts */
+ unicam_reg_write(unicam, UNICAM_STA, sta);
+
+ ista = unicam_reg_read(unicam, UNICAM_ISTA);
+ /* Write value back to clear the interrupts */
+ unicam_reg_write(unicam, UNICAM_ISTA, ista);
+
+ dev_dbg(unicam->dev, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d\n",
+ ista, sta, sequence, lines_done);
+
+ if (!(sta & (UNICAM_IS | UNICAM_PI0)))
+ return IRQ_HANDLED;
+
+ /*
+ * Look for either the Frame End interrupt or the Packet Capture status
+ * to signal a frame end.
+ */
+ fe = ista & UNICAM_FEI || sta & UNICAM_PI0;
+
+ /*
+ * We must run the frame end handler first. If we have a valid next_frm
+ * and we get a simultaneout FE + FS interrupt, running the FS handler
+ * first would null out the next_frm ptr and we would have lost the
+ * buffer forever.
+ */
+ if (fe) {
+ bool inc_seq = unicam->frame_started;
+
+ /*
+ * Ensure we have swapped buffers already as we can't
+ * stop the peripheral. If no buffer is available, use a
+ * dummy buffer to dump out frames until we get a new buffer
+ * to use.
+ */
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (!vb2_start_streaming_called(&node->buffer_queue))
+ continue;
+
+ /*
+ * If cur_frm == next_frm, it means we have not had
+ * a chance to swap buffers, likely due to having
+ * multiple interrupts occurring simultaneously (like FE
+ * + FS + LS). In this case, we cannot signal the buffer
+ * as complete, as the HW will reuse that buffer.
+ */
+ if (node->cur_frm && node->cur_frm != node->next_frm) {
+ unicam_process_buffer_complete(node, sequence);
+ inc_seq = true;
+ }
+ node->cur_frm = node->next_frm;
+ }
+
+ /*
+ * Increment the sequence number conditionally on either a FS
+ * having already occurred, or in the FE + FS condition as
+ * caught in the FE handler above. This ensures the sequence
+ * number corresponds to the frames generated by the sensor, not
+ * the frames dequeued to userland.
+ */
+ if (inc_seq) {
+ unicam->sequence++;
+ unicam->frame_started = false;
+ }
+ }
+
+ if (ista & UNICAM_FSI) {
+ /*
+ * Timestamp is to be when the first data byte was captured,
+ * aka frame start.
+ */
+ ts = ktime_get_ns();
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (!vb2_start_streaming_called(&node->buffer_queue))
+ continue;
+
+ if (node->cur_frm)
+ node->cur_frm->vb.vb2_buf.timestamp = ts;
+ else
+ dev_dbg(unicam->v4l2_dev.dev,
+ "ISR: [%d] Dropping frame, buffer not available at FS\n",
+ i);
+ /*
+ * Set the next frame output to go to a dummy frame
+ * if we have not managed to obtain another frame
+ * from the queue.
+ */
+ unicam_schedule_dummy_buffer(node);
+ }
+
+ unicam_queue_event_sof(unicam);
+ unicam->frame_started = true;
+ }
+
+ /*
+ * Cannot swap buffer at frame end, there may be a race condition
+ * where the HW does not actually swap it if the new frame has
+ * already started.
+ */
+ if (ista & (UNICAM_FSI | UNICAM_LCI) && !fe) {
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (!vb2_start_streaming_called(&node->buffer_queue))
+ continue;
+
+ spin_lock(&node->dma_queue_lock);
+ if (!list_empty(&node->dma_queue) && !node->next_frm)
+ unicam_schedule_next_buffer(node);
+ spin_unlock(&node->dma_queue_lock);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void unicam_set_packing_config(struct unicam_device *unicam,
+ const struct unicam_format_info *fmtinfo)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ u32 pack, unpack;
+ u32 val;
+
+ if (node->fmt.fmt.pix.pixelformat == fmtinfo->fourcc) {
+ unpack = UNICAM_PUM_NONE;
+ pack = UNICAM_PPM_NONE;
+ } else {
+ unpack = fmtinfo->unpack;
+ /* Repacking is always to 16bpp */
+ pack = UNICAM_PPM_PACK16;
+ }
+
+ val = 0;
+ unicam_set_field(&val, unpack, UNICAM_PUM_MASK);
+ unicam_set_field(&val, pack, UNICAM_PPM_MASK);
+ unicam_reg_write(unicam, UNICAM_IPIPE, val);
+}
+
+static void unicam_cfg_image_id(struct unicam_device *unicam, u8 vc, u8 dt)
+{
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 mode */
+ unicam_reg_write(unicam, UNICAM_IDI0, (vc << 6) | dt);
+ } else {
+ /* CCP2 mode */
+ unicam_reg_write(unicam, UNICAM_IDI0, 0x80 | dt);
+ }
+}
+
+static void unicam_enable_ed(struct unicam_device *unicam)
+{
+ u32 val = unicam_reg_read(unicam, UNICAM_DCS);
+
+ unicam_set_field(&val, 2, UNICAM_EDL_MASK);
+ /* Do not wrap at the end of the embedded data buffer */
+ unicam_set_field(&val, 0, UNICAM_DBOB);
+
+ unicam_reg_write(unicam, UNICAM_DCS, val);
+}
+
+static int unicam_get_image_vc_dt(struct unicam_device *unicam,
+ struct v4l2_subdev_state *state,
+ u8 *vc, u8 *dt)
+{
+ struct v4l2_mbus_frame_desc fd;
+ u32 stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ 0, NULL, &stream);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_frame_desc,
+ unicam->sensor.pad->index, &fd);
+ if (ret)
+ return ret;
+
+ /* Only CSI-2 supports DTs. */
+ if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < fd.num_entries; ++i) {
+ const struct v4l2_mbus_frame_desc_entry *fde = &fd.entry[i];
+
+ if (fde->stream == stream) {
+ *vc = fde->bus.csi2.vc;
+ *dt = fde->bus.csi2.dt;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static void unicam_start_rx(struct unicam_device *unicam,
+ struct v4l2_subdev_state *state)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_IMAGE_NODE];
+ const struct unicam_format_info *fmtinfo;
+ const struct v4l2_mbus_framefmt *fmt;
+ unsigned int line_int_freq;
+ u8 vc, dt;
+ u32 val;
+ int ret;
+
+ fmt = v4l2_subdev_state_get_format(state, UNICAM_SD_PAD_SOURCE_IMAGE, 0);
+ fmtinfo = unicam_find_format_by_code(fmt->code,
+ UNICAM_SD_PAD_SOURCE_IMAGE);
+ if (WARN_ON(!fmtinfo))
+ return;
+
+ /*
+ * Enable lane clocks. The register is structured as follows:
+ *
+ * [9:8] - DAT3
+ * [7:6] - DAT2
+ * [5:4] - DAT1
+ * [3:2] - DAT0
+ * [1:0] - CLK
+ *
+ * Enabled lane must be set to b01, and disabled lanes to b00. The clock
+ * lane is always enabled.
+ */
+ val = 0x155 & GENMASK(unicam->pipe.num_data_lanes * 2 + 1, 0);
+ unicam_clk_write(unicam, val);
+
+ /* Basic init */
+ unicam_reg_write(unicam, UNICAM_CTRL, UNICAM_MEM);
+
+ /* Enable analogue control, and leave in reset. */
+ val = UNICAM_AR;
+ unicam_set_field(&val, 7, UNICAM_CTATADJ_MASK);
+ unicam_set_field(&val, 7, UNICAM_PTATADJ_MASK);
+ unicam_reg_write(unicam, UNICAM_ANA, val);
+ usleep_range(1000, 2000);
+
+ /* Come out of reset */
+ unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_AR);
+
+ /* Peripheral reset */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR);
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR);
+
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE);
+
+ /* Enable Rx control. */
+ val = unicam_reg_read(unicam, UNICAM_CTRL);
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ unicam_set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK);
+ unicam_set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK);
+ } else {
+ unicam_set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK);
+ unicam_set_field(&val, unicam->bus_flags, UNICAM_DCM_MASK);
+ }
+ /* Packet framer timeout */
+ unicam_set_field(&val, 0xf, UNICAM_PFT_MASK);
+ unicam_set_field(&val, 128, UNICAM_OET_MASK);
+ unicam_reg_write(unicam, UNICAM_CTRL, val);
+
+ unicam_reg_write(unicam, UNICAM_IHWIN, 0);
+ unicam_reg_write(unicam, UNICAM_IVWIN, 0);
+
+ /* AXI bus access QoS setup */
+ val = unicam_reg_read(unicam, UNICAM_PRI);
+ unicam_set_field(&val, 0, UNICAM_BL_MASK);
+ unicam_set_field(&val, 0, UNICAM_BS_MASK);
+ unicam_set_field(&val, 0xe, UNICAM_PP_MASK);
+ unicam_set_field(&val, 8, UNICAM_NP_MASK);
+ unicam_set_field(&val, 2, UNICAM_PT_MASK);
+ unicam_set_field(&val, 1, UNICAM_PE);
+ unicam_reg_write(unicam, UNICAM_PRI, val);
+
+ unicam_reg_write_field(unicam, UNICAM_ANA, 0, UNICAM_DDL);
+
+ val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_IBOB;
+ line_int_freq = max(fmt->height >> 2, 128);
+ unicam_set_field(&val, line_int_freq, UNICAM_LCIE_MASK);
+ unicam_reg_write(unicam, UNICAM_ICTL, val);
+ unicam_reg_write(unicam, UNICAM_STA, UNICAM_STA_MASK_ALL);
+ unicam_reg_write(unicam, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL);
+
+ /* tclk_term_en */
+ unicam_reg_write_field(unicam, UNICAM_CLT, 2, UNICAM_CLT1_MASK);
+ /* tclk_settle */
+ unicam_reg_write_field(unicam, UNICAM_CLT, 6, UNICAM_CLT2_MASK);
+ /* td_term_en */
+ unicam_reg_write_field(unicam, UNICAM_DLT, 2, UNICAM_DLT1_MASK);
+ /* ths_settle */
+ unicam_reg_write_field(unicam, UNICAM_DLT, 6, UNICAM_DLT2_MASK);
+ /* trx_enable */
+ unicam_reg_write_field(unicam, UNICAM_DLT, 0, UNICAM_DLT3_MASK);
+
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_SOE);
+
+ /* Packet compare setup - required to avoid missing frame ends */
+ val = 0;
+ unicam_set_field(&val, 1, UNICAM_PCE);
+ unicam_set_field(&val, 1, UNICAM_GI);
+ unicam_set_field(&val, 1, UNICAM_CPH);
+ unicam_set_field(&val, 0, UNICAM_PCVC_MASK);
+ unicam_set_field(&val, 1, UNICAM_PCDT_MASK);
+ unicam_reg_write(unicam, UNICAM_CMP0, val);
+
+ /* Enable clock lane and set up terminations */
+ val = 0;
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 */
+ unicam_set_field(&val, 1, UNICAM_CLE);
+ unicam_set_field(&val, 1, UNICAM_CLLPE);
+ if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) {
+ unicam_set_field(&val, 1, UNICAM_CLTRE);
+ unicam_set_field(&val, 1, UNICAM_CLHSE);
+ }
+ } else {
+ /* CCP2 */
+ unicam_set_field(&val, 1, UNICAM_CLE);
+ unicam_set_field(&val, 1, UNICAM_CLHSE);
+ unicam_set_field(&val, 1, UNICAM_CLTRE);
+ }
+ unicam_reg_write(unicam, UNICAM_CLK, val);
+
+ /*
+ * Enable required data lanes with appropriate terminations.
+ * The same value needs to be written to UNICAM_DATn registers for
+ * the active lanes, and 0 for inactive ones.
+ */
+ val = 0;
+ if (unicam->bus_type == V4L2_MBUS_CSI2_DPHY) {
+ /* CSI2 */
+ unicam_set_field(&val, 1, UNICAM_DLE);
+ unicam_set_field(&val, 1, UNICAM_DLLPE);
+ if (!(unicam->bus_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)) {
+ unicam_set_field(&val, 1, UNICAM_DLTRE);
+ unicam_set_field(&val, 1, UNICAM_DLHSE);
+ }
+ } else {
+ /* CCP2 */
+ unicam_set_field(&val, 1, UNICAM_DLE);
+ unicam_set_field(&val, 1, UNICAM_DLHSE);
+ unicam_set_field(&val, 1, UNICAM_DLTRE);
+ }
+ unicam_reg_write(unicam, UNICAM_DAT0, val);
+
+ if (unicam->pipe.num_data_lanes == 1)
+ val = 0;
+ unicam_reg_write(unicam, UNICAM_DAT1, val);
+
+ if (unicam->max_data_lanes > 2) {
+ /*
+ * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the
+ * instance supports more than 2 data lanes.
+ */
+ if (unicam->pipe.num_data_lanes == 2)
+ val = 0;
+ unicam_reg_write(unicam, UNICAM_DAT2, val);
+
+ if (unicam->pipe.num_data_lanes == 3)
+ val = 0;
+ unicam_reg_write(unicam, UNICAM_DAT3, val);
+ }
+
+ unicam_reg_write(unicam, UNICAM_IBLS,
+ node->fmt.fmt.pix.bytesperline);
+ unicam_wr_dma_addr(node, node->cur_frm);
+ unicam_set_packing_config(unicam, fmtinfo);
+
+ ret = unicam_get_image_vc_dt(unicam, state, &vc, &dt);
+ if (ret) {
+ /*
+ * If the source doesn't support frame descriptors, default to
+ * VC 0 and use the DT corresponding to the format.
+ */
+ vc = 0;
+ dt = fmtinfo->csi_dt;
+ }
+
+ unicam_cfg_image_id(unicam, vc, dt);
+
+ val = unicam_reg_read(unicam, UNICAM_MISC);
+ unicam_set_field(&val, 1, UNICAM_FL0);
+ unicam_set_field(&val, 1, UNICAM_FL1);
+ unicam_reg_write(unicam, UNICAM_MISC, val);
+
+ /* Enable peripheral */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPE);
+
+ /* Load image pointers */
+ unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_LIP_MASK);
+
+ /*
+ * Enable trigger only for the first frame to
+ * sync correctly to the FS from the source.
+ */
+ unicam_reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC);
+}
+
+static void unicam_start_metadata(struct unicam_device *unicam)
+{
+ struct unicam_node *node = &unicam->node[UNICAM_METADATA_NODE];
+
+ unicam_enable_ed(unicam);
+ unicam_wr_dma_addr(node, node->cur_frm);
+ unicam_reg_write_field(unicam, UNICAM_DCS, 1, UNICAM_LDP);
+}
+
+static void unicam_disable(struct unicam_device *unicam)
+{
+ /* Analogue lane control disable */
+ unicam_reg_write_field(unicam, UNICAM_ANA, 1, UNICAM_DDL);
+
+ /* Stop the output engine */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_SOE);
+
+ /* Disable the data lanes. */
+ unicam_reg_write(unicam, UNICAM_DAT0, 0);
+ unicam_reg_write(unicam, UNICAM_DAT1, 0);
+
+ if (unicam->max_data_lanes > 2) {
+ unicam_reg_write(unicam, UNICAM_DAT2, 0);
+ unicam_reg_write(unicam, UNICAM_DAT3, 0);
+ }
+
+ /* Peripheral reset */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 1, UNICAM_CPR);
+ usleep_range(50, 100);
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPR);
+
+ /* Disable peripheral */
+ unicam_reg_write_field(unicam, UNICAM_CTRL, 0, UNICAM_CPE);
+
+ /* Clear ED setup */
+ unicam_reg_write(unicam, UNICAM_DCS, 0);
+
+ /* Disable all lane clocks */
+ unicam_clk_write(unicam, 0);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static int __unicam_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct v4l2_subdev_route *route;
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing(sd, state, routing);
+ if (ret)
+ return ret;
+
+ for_each_active_route(&state->routing, route) {
+ const struct v4l2_mbus_framefmt *def_fmt;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (route->source_pad == UNICAM_SD_PAD_SOURCE_IMAGE)
+ def_fmt = &unicam_default_image_format;
+ else
+ def_fmt = &unicam_default_meta_format;
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+ *fmt = *def_fmt;
+ fmt = v4l2_subdev_state_get_format(state, route->source_pad,
+ route->source_stream);
+ *fmt = *def_fmt;
+ }
+
+ return 0;
+}
+
+static int unicam_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = UNICAM_SD_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = UNICAM_SD_PAD_SOURCE_IMAGE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ struct v4l2_subdev_krouting routing = {
+ .len_routes = ARRAY_SIZE(routes),
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ /* Initialize routing to single route to the fist source pad. */
+ return __unicam_subdev_set_routing(sd, state, &routing);
+}
+
+static int unicam_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ u32 pad, stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ code->pad, code->stream,
+ &pad, &stream);
+ if (ret)
+ return ret;
+
+ if (unicam_sd_pad_is_source(code->pad)) {
+ /* No transcoding, source and sink codes must match. */
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ if (!fmt)
+ return -EINVAL;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ code->code = fmt->code;
+ } else {
+ const struct unicam_format_info *formats;
+ unsigned int num_formats;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ formats = unicam_image_formats;
+ num_formats = ARRAY_SIZE(unicam_image_formats);
+ } else {
+ formats = unicam_meta_formats;
+ num_formats = ARRAY_SIZE(unicam_meta_formats);
+ }
+
+ if (code->index >= num_formats)
+ return -EINVAL;
+
+ code->code = formats[code->index].code;
+ }
+
+ return 0;
+}
+
+static int unicam_subdev_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ u32 pad, stream;
+ int ret;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, fse->pad,
+ fse->stream, &pad,
+ &stream);
+ if (ret)
+ return ret;
+
+ if (unicam_sd_pad_is_source(fse->pad)) {
+ /* No transcoding, source and sink formats must match. */
+ const struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ if (!fmt)
+ return -EINVAL;
+
+ if (fse->code != fmt->code)
+ return -EINVAL;
+
+ fse->min_width = fmt->width;
+ fse->max_width = fmt->width;
+ fse->min_height = fmt->height;
+ fse->max_height = fmt->height;
+ } else {
+ const struct unicam_format_info *fmtinfo;
+
+ fmtinfo = unicam_find_format_by_code(fse->code, pad);
+ if (!fmtinfo)
+ return -EINVAL;
+
+ if (pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ fse->min_width = UNICAM_IMAGE_MIN_WIDTH;
+ fse->max_width = UNICAM_IMAGE_MAX_WIDTH;
+ fse->min_height = UNICAM_IMAGE_MIN_HEIGHT;
+ fse->max_height = UNICAM_IMAGE_MAX_HEIGHT;
+ } else {
+ fse->min_width = UNICAM_META_MIN_WIDTH;
+ fse->max_width = UNICAM_META_MAX_WIDTH;
+ fse->min_height = UNICAM_META_MIN_HEIGHT;
+ fse->max_height = UNICAM_META_MAX_HEIGHT;
+ }
+ }
+
+ return 0;
+}
+
+static int unicam_subdev_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+ struct v4l2_mbus_framefmt *sink_format, *source_format;
+ const struct unicam_format_info *fmtinfo;
+ u32 source_pad, source_stream;
+ int ret;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ unicam->subdev.enabled_streams)
+ return -EBUSY;
+
+ /* No transcoding, source and sink formats must match. */
+ if (unicam_sd_pad_is_source(format->pad))
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ /*
+ * Allowed formats for the stream on the sink pad depend on what source
+ * pad the stream is routed to. Find the corresponding source pad and
+ * use it to validate the media bus code.
+ */
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ format->pad, format->stream,
+ &source_pad, &source_stream);
+ if (ret)
+ return ret;
+
+ fmtinfo = unicam_find_format_by_code(format->format.code, source_pad);
+ if (!fmtinfo) {
+ fmtinfo = source_pad == UNICAM_SD_PAD_SOURCE_IMAGE
+ ? &unicam_image_formats[0] : &unicam_meta_formats[0];
+ format->format.code = fmtinfo->code;
+ }
+
+ if (source_pad == UNICAM_SD_PAD_SOURCE_IMAGE) {
+ format->format.width = clamp_t(unsigned int,
+ format->format.width,
+ UNICAM_IMAGE_MIN_WIDTH,
+ UNICAM_IMAGE_MAX_WIDTH);
+ format->format.height = clamp_t(unsigned int,
+ format->format.height,
+ UNICAM_IMAGE_MIN_HEIGHT,
+ UNICAM_IMAGE_MAX_HEIGHT);
+ format->format.field = V4L2_FIELD_NONE;
+ } else {
+ format->format.width = clamp_t(unsigned int,
+ format->format.width,
+ UNICAM_META_MIN_WIDTH,
+ UNICAM_META_MAX_WIDTH);
+ format->format.height = clamp_t(unsigned int,
+ format->format.height,
+ UNICAM_META_MIN_HEIGHT,
+ UNICAM_META_MAX_HEIGHT);
+ format->format.field = V4L2_FIELD_NONE;
+
+ /* Colorspace don't apply to metadata. */
+ format->format.colorspace = 0;
+ format->format.ycbcr_enc = 0;
+ format->format.quantization = 0;
+ format->format.xfer_func = 0;
+ }
+
+ sink_format = v4l2_subdev_state_get_format(state, format->pad,
+ format->stream);
+ source_format = v4l2_subdev_state_get_format(state, source_pad,
+ source_stream);
+ *sink_format = format->format;
+ *source_format = format->format;
+
+ return 0;
+}
+
+static int unicam_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE && unicam->subdev.enabled_streams)
+ return -EBUSY;
+
+ return __unicam_subdev_set_routing(sd, state, routing);
+}
+
+static int unicam_sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+ u32 other_pad, other_stream;
+ int ret;
+
+ if (!unicam->subdev.enabled_streams) {
+ /* Configure and start Unicam. */
+ unicam->sequence = 0;
+
+ if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE))
+ unicam_start_metadata(unicam);
+
+ unicam->frame_started = false;
+ unicam_start_rx(unicam, state);
+ }
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ &other_pad, &other_stream);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_enable_streams(unicam->sensor.subdev,
+ unicam->sensor.pad->index,
+ BIT(other_stream));
+ if (ret) {
+ dev_err(unicam->dev, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ unicam->subdev.enabled_streams |= BIT(other_stream);
+
+ return 0;
+}
+
+static int unicam_sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct unicam_device *unicam = sd_to_unicam_device(sd);
+ u32 other_pad, other_stream;
+ int ret;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ &other_pad, &other_stream);
+ if (ret)
+ return ret;
+
+ v4l2_subdev_disable_streams(unicam->sensor.subdev,
+ unicam->sensor.pad->index,
+ BIT(other_stream));
+
+ unicam->subdev.enabled_streams &= ~BIT(other_stream);
+
+ if (!unicam->subdev.enabled_streams)
+ unicam_disable(unicam);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops unicam_subdev_pad_ops = {
+ .enum_mbus_code = unicam_subdev_enum_mbus_code,
+ .enum_frame_size = unicam_subdev_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = unicam_subdev_set_format,
+ .set_routing = unicam_subdev_set_routing,
+ .enable_streams = unicam_sd_enable_streams,
+ .disable_streams = unicam_sd_disable_streams,
+};
+
+static const struct v4l2_subdev_ops unicam_subdev_ops = {
+ .pad = &unicam_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops unicam_subdev_internal_ops = {
+ .init_state = unicam_subdev_init_state,
+};
+
+static const struct media_entity_operations unicam_subdev_media_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+static int unicam_subdev_init(struct unicam_device *unicam)
+{
+ struct v4l2_subdev *sd = &unicam->subdev.sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &unicam_subdev_ops);
+ sd->internal_ops = &unicam_subdev_internal_ops;
+ v4l2_set_subdevdata(sd, unicam);
+
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->entity.ops = &unicam_subdev_media_ops;
+ sd->dev = unicam->dev;
+ sd->owner = THIS_MODULE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
+
+ strscpy(sd->name, "unicam", sizeof(sd->name));
+
+ unicam->subdev.pads[UNICAM_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_IMAGE].flags = MEDIA_PAD_FL_SOURCE;
+ unicam->subdev.pads[UNICAM_SD_PAD_SOURCE_METADATA].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, ARRAY_SIZE(unicam->subdev.pads),
+ unicam->subdev.pads);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to initialize media entity: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to initialize subdev: %d\n", ret);
+ goto err_entity;
+ }
+
+ ret = v4l2_device_register_subdev(&unicam->v4l2_dev, sd);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to register subdev: %d\n", ret);
+ goto err_subdev;
+ }
+
+ return 0;
+
+err_subdev:
+ v4l2_subdev_cleanup(sd);
+err_entity:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+static void unicam_subdev_cleanup(struct unicam_device *unicam)
+{
+ v4l2_subdev_cleanup(&unicam->subdev.sd);
+ media_entity_cleanup(&unicam->subdev.sd.entity);
+}
+
+/* -----------------------------------------------------------------------------
+ * Videobuf2 queue operations
+ */
+
+static int unicam_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage
+ : node->fmt.fmt.meta.buffersize;
+
+ if (*nplanes) {
+ if (sizes[0] < size) {
+ dev_dbg(node->dev->dev, "sizes[0] %i < size %u\n",
+ sizes[0], size);
+ return -EINVAL;
+ }
+ size = sizes[0];
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int unicam_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct unicam_buffer *buf = to_unicam_buffer(vb);
+ u32 size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage
+ : node->fmt.fmt.meta.buffersize;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_dbg(node->dev->dev,
+ "data will not fit into plane (%lu < %u)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ buf->dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ buf->size = size;
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+
+ return 0;
+}
+
+static void unicam_return_buffers(struct unicam_node *node,
+ enum vb2_buffer_state state)
+{
+ struct unicam_buffer *buf, *tmp;
+
+ list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) {
+ list_del(&buf->list);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+
+ if (node->cur_frm)
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf,
+ state);
+ if (node->next_frm && node->cur_frm != node->next_frm)
+ vb2_buffer_done(&node->next_frm->vb.vb2_buf,
+ state);
+
+ node->cur_frm = NULL;
+ node->next_frm = NULL;
+}
+
+static int unicam_num_data_lanes(struct unicam_device *unicam)
+{
+ struct v4l2_mbus_config mbus_config = { 0 };
+ unsigned int num_data_lanes;
+ int ret;
+
+ if (unicam->bus_type != V4L2_MBUS_CSI2_DPHY)
+ return unicam->max_data_lanes;
+
+ ret = v4l2_subdev_call(unicam->sensor.subdev, pad, get_mbus_config,
+ unicam->sensor.pad->index, &mbus_config);
+ if (ret == -ENOIOCTLCMD)
+ return unicam->max_data_lanes;
+
+ if (ret < 0) {
+ dev_err(unicam->dev, "Failed to get mbus config: %d\n", ret);
+ return ret;
+ }
+
+ num_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
+
+ if (num_data_lanes != 1 && num_data_lanes != 2 && num_data_lanes != 4) {
+ dev_err(unicam->dev,
+ "Device %s has requested %u data lanes, invalid\n",
+ unicam->sensor.subdev->name, num_data_lanes);
+ return -EINVAL;
+ }
+
+ if (num_data_lanes > unicam->max_data_lanes) {
+ dev_err(unicam->dev,
+ "Device %s has requested %u data lanes, >%u configured in DT\n",
+ unicam->sensor.subdev->name, num_data_lanes,
+ unicam->max_data_lanes);
+ return -EINVAL;
+ }
+
+ return num_data_lanes;
+}
+
+static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ struct unicam_device *unicam = node->dev;
+ struct unicam_buffer *buf;
+ struct media_pipeline_pad_iter iter;
+ struct media_pad *pad;
+ unsigned long flags;
+ int ret;
+
+ dev_dbg(unicam->dev, "Starting stream on %s device\n",
+ is_metadata_node(node) ? "metadata" : "image");
+
+ /*
+ * Start the pipeline. This validates all links, and populates the
+ * pipeline structure.
+ */
+ ret = video_device_pipeline_start(&node->video_dev, &unicam->pipe.pipe);
+ if (ret < 0) {
+ dev_dbg(unicam->dev, "Failed to start media pipeline: %d\n", ret);
+ goto err_buffers;
+ }
+
+ /*
+ * Determine which video nodes are included in the pipeline, and get the
+ * number of data lanes.
+ */
+ if (unicam->pipe.pipe.start_count == 1) {
+ unicam->pipe.nodes = 0;
+
+ media_pipeline_for_each_pad(&unicam->pipe.pipe, &iter, pad) {
+ if (pad->entity != &unicam->subdev.sd.entity)
+ continue;
+
+ if (pad->index == UNICAM_SD_PAD_SOURCE_IMAGE)
+ unicam->pipe.nodes |= BIT(UNICAM_IMAGE_NODE);
+ else if (pad->index == UNICAM_SD_PAD_SOURCE_METADATA)
+ unicam->pipe.nodes |= BIT(UNICAM_METADATA_NODE);
+ }
+
+ if (!(unicam->pipe.nodes & BIT(UNICAM_IMAGE_NODE))) {
+ dev_dbg(unicam->dev,
+ "Pipeline does not include image node\n");
+ ret = -EPIPE;
+ goto err_pipeline;
+ }
+
+ ret = unicam_num_data_lanes(unicam);
+ if (ret < 0)
+ goto err_pipeline;
+
+ unicam->pipe.num_data_lanes = ret;
+
+ dev_dbg(unicam->dev, "Running with %u data lanes, nodes %u\n",
+ unicam->pipe.num_data_lanes, unicam->pipe.nodes);
+ }
+
+ /* Arm the node with the first buffer from the DMA queue. */
+ spin_lock_irqsave(&node->dma_queue_lock, flags);
+ buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list);
+ node->cur_frm = buf;
+ node->next_frm = buf;
+ list_del(&buf->list);
+ spin_unlock_irqrestore(&node->dma_queue_lock, flags);
+
+ /*
+ * Wait for all the video devices in the pipeline to have been started
+ * before starting the hardware. In the general case, this would
+ * prevent capturing multiple streams independently. However, the
+ * Unicam DMA engines are not generic, they have been designed to
+ * capture image data and embedded data from the same camera sensor.
+ * Not only does the main use case not benefit from independent
+ * capture, it requires proper synchronization of the streams at start
+ * time.
+ */
+ if (unicam->pipe.pipe.start_count < hweight32(unicam->pipe.nodes))
+ return 0;
+
+ ret = pm_runtime_resume_and_get(unicam->dev);
+ if (ret < 0) {
+ dev_err(unicam->dev, "PM runtime resume failed: %d\n", ret);
+ goto err_pipeline;
+ }
+
+ /* Enable the streams on the source. */
+ ret = v4l2_subdev_enable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ BIT(0));
+ if (ret < 0) {
+ dev_err(unicam->dev, "stream on failed in subdev\n");
+ goto err_pm_put;
+ }
+
+ if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE)) {
+ ret = v4l2_subdev_enable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_METADATA,
+ BIT(0));
+ if (ret < 0) {
+ dev_err(unicam->dev, "stream on failed in subdev\n");
+ goto err_disable_streams;
+ }
+ }
+
+ return 0;
+
+err_disable_streams:
+ v4l2_subdev_disable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_IMAGE, BIT(0));
+err_pm_put:
+ pm_runtime_put_sync(unicam->dev);
+err_pipeline:
+ video_device_pipeline_stop(&node->video_dev);
+err_buffers:
+ unicam_return_buffers(node, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void unicam_stop_streaming(struct vb2_queue *vq)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vq);
+ struct unicam_device *unicam = node->dev;
+
+ /* Stop the hardware when the first video device gets stopped. */
+ if (unicam->pipe.pipe.start_count == hweight32(unicam->pipe.nodes)) {
+ if (unicam->pipe.nodes & BIT(UNICAM_METADATA_NODE))
+ v4l2_subdev_disable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_METADATA,
+ BIT(0));
+
+ v4l2_subdev_disable_streams(&unicam->subdev.sd,
+ UNICAM_SD_PAD_SOURCE_IMAGE,
+ BIT(0));
+
+ pm_runtime_put(unicam->dev);
+ }
+
+ video_device_pipeline_stop(&node->video_dev);
+
+ /* Clear all queued buffers for the node */
+ unicam_return_buffers(node, VB2_BUF_STATE_ERROR);
+}
+
+static void unicam_buffer_queue(struct vb2_buffer *vb)
+{
+ struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct unicam_buffer *buf = to_unicam_buffer(vb);
+
+ spin_lock_irq(&node->dma_queue_lock);
+ list_add_tail(&buf->list, &node->dma_queue);
+ spin_unlock_irq(&node->dma_queue_lock);
+}
+
+static const struct vb2_ops unicam_video_qops = {
+ .queue_setup = unicam_queue_setup,
+ .buf_prepare = unicam_buffer_prepare,
+ .start_streaming = unicam_start_streaming,
+ .stop_streaming = unicam_stop_streaming,
+ .buf_queue = unicam_buffer_queue,
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 video device operations
+ */
+
+static int unicam_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card));
+
+ cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE;
+
+ return 0;
+}
+
+static int unicam_enum_fmt_vid(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ unsigned int index;
+ unsigned int i;
+
+ for (i = 0, index = 0; i < ARRAY_SIZE(unicam_image_formats); i++) {
+ if (f->mbus_code && unicam_image_formats[i].code != f->mbus_code)
+ continue;
+
+ if (index == f->index) {
+ f->pixelformat = unicam_image_formats[i].fourcc;
+ return 0;
+ }
+
+ index++;
+
+ if (!unicam_image_formats[i].unpacked_fourcc)
+ continue;
+
+ if (index == f->index) {
+ f->pixelformat = unicam_image_formats[i].unpacked_fourcc;
+ return 0;
+ }
+
+ index++;
+ }
+
+ return -EINVAL;
+}
+
+static int unicam_g_fmt_vid(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ *f = node->fmt;
+
+ return 0;
+}
+
+static void __unicam_try_fmt_vid(struct unicam_node *node,
+ struct v4l2_pix_format *pix)
+{
+ const struct unicam_format_info *fmtinfo;
+
+ /*
+ * Default to the first format if the requested pixel format code isn't
+ * supported.
+ */
+ fmtinfo = unicam_find_format_by_fourcc(pix->pixelformat,
+ UNICAM_SD_PAD_SOURCE_IMAGE);
+ if (!fmtinfo) {
+ fmtinfo = &unicam_image_formats[0];
+ pix->pixelformat = fmtinfo->fourcc;
+ }
+
+ unicam_calc_image_size_bpl(node->dev, fmtinfo, pix);
+
+ if (pix->field == V4L2_FIELD_ANY)
+ pix->field = V4L2_FIELD_NONE;
+}
+
+static int unicam_try_fmt_vid(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ __unicam_try_fmt_vid(node, &f->fmt.pix);
+ return 0;
+}
+
+static int unicam_s_fmt_vid(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ if (vb2_is_busy(&node->buffer_queue))
+ return -EBUSY;
+
+ __unicam_try_fmt_vid(node, &f->fmt.pix);
+ node->fmt = *f;
+
+ return 0;
+}
+
+static int unicam_enum_fmt_meta(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ unsigned int i, index;
+
+ for (i = 0, index = 0; i < ARRAY_SIZE(unicam_meta_formats); i++) {
+ if (f->mbus_code && unicam_meta_formats[i].code != f->mbus_code)
+ continue;
+
+ if (index == f->index) {
+ f->pixelformat = unicam_meta_formats[i].fourcc;
+ f->type = V4L2_BUF_TYPE_META_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_META_LINE_BASED;
+ return 0;
+ }
+
+ index++;
+ }
+
+ return -EINVAL;
+}
+
+static int unicam_g_fmt_meta(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ f->fmt.meta = node->fmt.fmt.meta;
+
+ return 0;
+}
+
+static const struct unicam_format_info *
+__unicam_try_fmt_meta(struct unicam_node *node, struct v4l2_meta_format *meta)
+{
+ const struct unicam_format_info *fmtinfo;
+
+ /*
+ * Default to the first format if the requested pixel format code isn't
+ * supported.
+ */
+ fmtinfo = unicam_find_format_by_fourcc(meta->dataformat,
+ UNICAM_SD_PAD_SOURCE_METADATA);
+ if (!fmtinfo) {
+ fmtinfo = &unicam_meta_formats[0];
+ meta->dataformat = fmtinfo->fourcc;
+ }
+
+ unicam_calc_meta_size_bpl(node->dev, fmtinfo, meta);
+
+ return fmtinfo;
+}
+
+static int unicam_try_fmt_meta(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ __unicam_try_fmt_meta(node, &f->fmt.meta);
+ return 0;
+}
+
+static int unicam_s_fmt_meta(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct unicam_node *node = video_drvdata(file);
+
+ if (vb2_is_busy(&node->buffer_queue))
+ return -EBUSY;
+
+ __unicam_try_fmt_meta(node, &f->fmt.meta);
+ node->fmt = *f;
+
+ return 0;
+}
+
+static int unicam_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct unicam_node *node = video_drvdata(file);
+ int ret = -EINVAL;
+
+ if (fsize->index > 0)
+ return ret;
+
+ if (is_image_node(node)) {
+ if (!unicam_find_format_by_fourcc(fsize->pixel_format,
+ UNICAM_SD_PAD_SOURCE_IMAGE))
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = UNICAM_IMAGE_MIN_WIDTH;
+ fsize->stepwise.max_width = UNICAM_IMAGE_MAX_WIDTH;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = UNICAM_IMAGE_MIN_HEIGHT;
+ fsize->stepwise.max_height = UNICAM_IMAGE_MAX_HEIGHT;
+ fsize->stepwise.step_height = 1;
+ } else {
+ if (!unicam_find_format_by_fourcc(fsize->pixel_format,
+ UNICAM_SD_PAD_SOURCE_METADATA))
+ return ret;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = UNICAM_META_MIN_WIDTH;
+ fsize->stepwise.max_width = UNICAM_META_MAX_WIDTH;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = UNICAM_META_MIN_HEIGHT;
+ fsize->stepwise.max_height = UNICAM_META_MAX_HEIGHT;
+ fsize->stepwise.step_height = 1;
+ }
+
+ return 0;
+}
+
+static int unicam_log_status(struct file *file, void *fh)
+{
+ struct unicam_node *node = video_drvdata(file);
+ struct unicam_device *unicam = node->dev;
+ u32 reg;
+
+ /* status for sub devices */
+ v4l2_device_call_all(&unicam->v4l2_dev, 0, core, log_status);
+
+ dev_info(unicam->dev, "-----Receiver status-----\n");
+ dev_info(unicam->dev, "V4L2 width/height: %ux%u\n",
+ node->fmt.fmt.pix.width, node->fmt.fmt.pix.height);
+ dev_info(unicam->dev, "V4L2 format: %08x\n",
+ node->fmt.fmt.pix.pixelformat);
+ reg = unicam_reg_read(unicam, UNICAM_IPIPE);
+ dev_info(unicam->dev, "Unpacking/packing: %u / %u\n",
+ unicam_get_field(reg, UNICAM_PUM_MASK),
+ unicam_get_field(reg, UNICAM_PPM_MASK));
+ dev_info(unicam->dev, "----Live data----\n");
+ dev_info(unicam->dev, "Programmed stride: %4u\n",
+ unicam_reg_read(unicam, UNICAM_IBLS));
+ dev_info(unicam->dev, "Detected resolution: %ux%u\n",
+ unicam_reg_read(unicam, UNICAM_IHSTA),
+ unicam_reg_read(unicam, UNICAM_IVSTA));
+ dev_info(unicam->dev, "Write pointer: %08x\n",
+ unicam_reg_read(unicam, UNICAM_IBWP));
+
+ return 0;
+}
+
+static int unicam_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops unicam_ioctl_ops = {
+ .vidioc_querycap = unicam_querycap,
+
+ .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid,
+ .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid,
+ .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid,
+ .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid,
+
+ .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta,
+ .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta,
+ .vidioc_try_fmt_meta_cap = unicam_try_fmt_meta,
+ .vidioc_s_fmt_meta_cap = unicam_s_fmt_meta,
+
+ .vidioc_enum_framesizes = unicam_enum_framesizes,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = unicam_log_status,
+ .vidioc_subscribe_event = unicam_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+/* unicam capture driver file operations */
+static const struct v4l2_file_operations unicam_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int unicam_video_link_validate(struct media_link *link)
+{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct v4l2_subdev *sd =
+ media_entity_to_v4l2_subdev(link->source->entity);
+ struct unicam_node *node = video_get_drvdata(vdev);
+ const u32 pad = is_image_node(node) ? UNICAM_SD_PAD_SOURCE_IMAGE
+ : UNICAM_SD_PAD_SOURCE_METADATA;
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ format = v4l2_subdev_state_get_format(state, pad, 0);
+ if (!format) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (is_image_node(node)) {
+ const struct v4l2_pix_format *fmt = &node->fmt.fmt.pix;
+ const struct unicam_format_info *fmtinfo;
+
+ fmtinfo = unicam_find_format_by_fourcc(fmt->pixelformat,
+ UNICAM_SD_PAD_SOURCE_IMAGE);
+ if (WARN_ON(!fmtinfo)) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ if (fmtinfo->code != format->code ||
+ fmt->height != format->height ||
+ fmt->width != format->width ||
+ fmt->field != format->field) {
+ dev_dbg(node->dev->dev,
+ "image: (%u x %u) 0x%08x %s != (%u x %u) 0x%08x %s\n",
+ fmt->width, fmt->height, fmtinfo->code,
+ v4l2_field_names[fmt->field],
+ format->width, format->height, format->code,
+ v4l2_field_names[format->field]);
+ ret = -EPIPE;
+ }
+ } else {
+ const struct v4l2_meta_format *fmt = &node->fmt.fmt.meta;
+
+ const struct unicam_format_info *fmtinfo;
+
+ fmtinfo = unicam_find_format_by_fourcc(fmt->dataformat,
+ UNICAM_SD_PAD_SOURCE_METADATA);
+ if (WARN_ON(!fmtinfo)) {
+ ret = -EPIPE;
+ goto out;
+ }
+
+ if (fmtinfo->code != format->code ||
+ fmt->height != format->height ||
+ fmt->width != format->width) {
+ dev_dbg(node->dev->dev,
+ "meta: (%u x %u) 0x%04x != (%u x %u) 0x%04x\n",
+ fmt->width, fmt->height, fmtinfo->code,
+ format->width, format->height, format->code);
+ ret = -EPIPE;
+ }
+ }
+
+out:
+ v4l2_subdev_unlock_state(state);
+ return ret;
+}
+
+static const struct media_entity_operations unicam_video_media_ops = {
+ .link_validate = unicam_video_link_validate,
+};
+
+static void unicam_node_release(struct video_device *vdev)
+{
+ struct unicam_node *node = video_get_drvdata(vdev);
+
+ unicam_put(node->dev);
+}
+
+static void unicam_set_default_format(struct unicam_node *node)
+{
+ if (is_image_node(node)) {
+ struct v4l2_pix_format *fmt = &node->fmt.fmt.pix;
+ const struct unicam_format_info *fmtinfo =
+ &unicam_image_formats[0];
+
+ node->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ v4l2_fill_pix_format(fmt, &unicam_default_image_format);
+ fmt->pixelformat = fmtinfo->fourcc;
+ unicam_calc_image_size_bpl(node->dev, fmtinfo, fmt);
+ } else {
+ struct v4l2_meta_format *fmt = &node->fmt.fmt.meta;
+ const struct unicam_format_info *fmtinfo =
+ &unicam_meta_formats[0];
+
+ node->fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
+
+ fmt->dataformat = fmtinfo->fourcc;
+ fmt->width = unicam_default_meta_format.width;
+ fmt->height = unicam_default_meta_format.height;
+ unicam_calc_meta_size_bpl(node->dev, fmtinfo, fmt);
+ }
+}
+
+static int unicam_register_node(struct unicam_device *unicam,
+ enum unicam_node_type type)
+{
+ const u32 pad_index = type == UNICAM_IMAGE_NODE
+ ? UNICAM_SD_PAD_SOURCE_IMAGE
+ : UNICAM_SD_PAD_SOURCE_METADATA;
+ struct unicam_node *node = &unicam->node[type];
+ struct video_device *vdev = &node->video_dev;
+ struct vb2_queue *q = &node->buffer_queue;
+ int ret;
+
+ node->dev = unicam_get(unicam);
+ node->id = type;
+
+ spin_lock_init(&node->dma_queue_lock);
+
+ INIT_LIST_HEAD(&node->dma_queue);
+
+ /* Initialize the videobuf2 queue. */
+ q->type = type == UNICAM_IMAGE_NODE ? V4L2_BUF_TYPE_VIDEO_CAPTURE
+ : V4L2_BUF_TYPE_META_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = node;
+ q->ops = &unicam_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct unicam_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &unicam->lock;
+ q->min_queued_buffers = 1;
+ q->dev = unicam->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ dev_err(unicam->dev, "vb2_queue_init() failed\n");
+ goto err_unicam_put;
+ }
+
+ /* Initialize the video device. */
+ vdev->release = unicam_node_release;
+ vdev->fops = &unicam_fops;
+ vdev->ioctl_ops = &unicam_ioctl_ops;
+ vdev->v4l2_dev = &unicam->v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->queue = q;
+ vdev->lock = &unicam->lock;
+ vdev->device_caps = type == UNICAM_IMAGE_NODE
+ ? V4L2_CAP_VIDEO_CAPTURE : V4L2_CAP_META_CAPTURE;
+ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+ vdev->entity.ops = &unicam_video_media_ops;
+
+ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME,
+ type == UNICAM_IMAGE_NODE ? "image" : "embedded");
+
+ video_set_drvdata(vdev, node);
+
+ if (type == UNICAM_IMAGE_NODE)
+ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
+
+ node->pad.flags = MEDIA_PAD_FL_SINK;
+
+ ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
+ if (ret)
+ goto err_unicam_put;
+
+ node->dummy_buf.size = UNICAM_DUMMY_BUF_SIZE;
+ node->dummy_buf_cpu_addr = dma_alloc_coherent(unicam->dev,
+ node->dummy_buf.size,
+ &node->dummy_buf.dma_addr,
+ GFP_KERNEL);
+ if (!node->dummy_buf_cpu_addr) {
+ dev_err(unicam->dev, "Unable to allocate dummy buffer.\n");
+ ret = -ENOMEM;
+ goto err_entity_cleanup;
+ }
+
+ unicam_set_default_format(node);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register video device %s\n",
+ vdev->name);
+ goto err_dma_free;
+ }
+
+ node->registered = true;
+
+ ret = media_create_pad_link(&unicam->subdev.sd.entity,
+ pad_index,
+ &node->video_dev.entity,
+ 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ /*
+ * No need for cleanup, the caller will unregister the
+ * video device, which will drop the reference on the
+ * device and trigger the cleanup.
+ */
+ dev_err(unicam->dev, "Unable to create pad link for %s\n",
+ unicam->sensor.subdev->name);
+ return ret;
+ }
+
+ return 0;
+
+err_dma_free:
+ dma_free_coherent(unicam->dev, node->dummy_buf.size,
+ node->dummy_buf_cpu_addr,
+ node->dummy_buf.dma_addr);
+err_entity_cleanup:
+ media_entity_cleanup(&vdev->entity);
+err_unicam_put:
+ unicam_put(unicam);
+ return ret;
+}
+
+static void unicam_unregister_nodes(struct unicam_device *unicam)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(unicam->node); i++) {
+ struct unicam_node *node = &unicam->node[i];
+
+ if (node->registered) {
+ vb2_video_unregister_device(&node->video_dev);
+ node->registered = false;
+ }
+
+ if (node->dummy_buf_cpu_addr)
+ dma_free_coherent(unicam->dev, node->dummy_buf.size,
+ node->dummy_buf_cpu_addr,
+ node->dummy_buf.dma_addr);
+ }
+}
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int unicam_runtime_resume(struct device *dev)
+{
+ struct unicam_device *unicam = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_set_min_rate(unicam->vpu_clock, UNICAM_MIN_VPU_CLOCK_RATE);
+ if (ret) {
+ dev_err(unicam->dev, "failed to set up VPU clock\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(unicam->vpu_clock);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to enable VPU clock: %d\n", ret);
+ goto err_vpu_clock;
+ }
+
+ ret = clk_set_rate(unicam->clock, 100 * 1000 * 1000);
+ if (ret) {
+ dev_err(unicam->dev, "failed to set up CSI clock\n");
+ goto err_vpu_prepare;
+ }
+
+ ret = clk_prepare_enable(unicam->clock);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to enable CSI clock: %d\n", ret);
+ goto err_vpu_prepare;
+ }
+
+ return 0;
+
+err_vpu_prepare:
+ clk_disable_unprepare(unicam->vpu_clock);
+err_vpu_clock:
+ if (clk_set_min_rate(unicam->vpu_clock, 0))
+ dev_err(unicam->dev, "failed to reset the VPU clock\n");
+
+ return ret;
+}
+
+static int unicam_runtime_suspend(struct device *dev)
+{
+ struct unicam_device *unicam = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(unicam->clock);
+
+ if (clk_set_min_rate(unicam->vpu_clock, 0))
+ dev_err(unicam->dev, "failed to reset the VPU clock\n");
+
+ clk_disable_unprepare(unicam->vpu_clock);
+
+ return 0;
+}
+
+static const struct dev_pm_ops unicam_pm_ops = {
+ RUNTIME_PM_OPS(unicam_runtime_suspend, unicam_runtime_resume, NULL)
+};
+
+/* -----------------------------------------------------------------------------
+ * V4L2 async notifier
+ */
+
+static int unicam_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asc)
+{
+ struct unicam_device *unicam = notifier_to_unicam_device(notifier);
+ struct media_pad *sink = &unicam->subdev.pads[UNICAM_SD_PAD_SINK];
+ struct media_pad *source;
+ int ret;
+
+ dev_dbg(unicam->dev, "Using sensor %s for capture\n",
+ subdev->name);
+
+ ret = v4l2_create_fwnode_links_to_pad(subdev, sink, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ return ret;
+
+ source = media_pad_remote_pad_unique(sink);
+ if (IS_ERR(source)) {
+ dev_err(unicam->dev, "No connected sensor pad\n");
+ return PTR_ERR(source);
+ }
+
+ unicam->sensor.subdev = subdev;
+ unicam->sensor.pad = source;
+
+ return 0;
+}
+
+static int unicam_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct unicam_device *unicam = notifier_to_unicam_device(notifier);
+ int ret;
+
+ ret = unicam_register_node(unicam, UNICAM_IMAGE_NODE);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register image video device.\n");
+ goto unregister;
+ }
+
+ ret = unicam_register_node(unicam, UNICAM_METADATA_NODE);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register metadata video device.\n");
+ goto unregister;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&unicam->v4l2_dev);
+ if (ret) {
+ dev_err(unicam->dev, "Unable to register subdev nodes.\n");
+ goto unregister;
+ }
+
+ return 0;
+
+unregister:
+ unicam_unregister_nodes(unicam);
+ unicam_put(unicam);
+
+ return ret;
+}
+
+static const struct v4l2_async_notifier_operations unicam_async_ops = {
+ .bound = unicam_async_bound,
+ .complete = unicam_async_complete,
+};
+
+static int unicam_async_nf_init(struct unicam_device *unicam)
+{
+ struct v4l2_fwnode_endpoint ep = { };
+ struct fwnode_handle *ep_handle;
+ struct v4l2_async_connection *asc;
+ int ret;
+
+ ret = of_property_read_u32(unicam->dev->of_node, "brcm,num-data-lanes",
+ &unicam->max_data_lanes);
+ if (ret < 0) {
+ dev_err(unicam->dev, "Missing %s DT property\n",
+ "brcm,num-data-lanes");
+ return -EINVAL;
+ }
+
+ /* Get and parse the local endpoint. */
+ ep_handle = fwnode_graph_get_endpoint_by_id(dev_fwnode(unicam->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep_handle) {
+ dev_err(unicam->dev, "No endpoint found\n");
+ return -ENODEV;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(ep_handle, &ep);
+ if (ret) {
+ dev_err(unicam->dev, "Failed to parse endpoint: %d\n", ret);
+ goto error;
+ }
+
+ unicam->bus_type = ep.bus_type;
+
+ switch (ep.bus_type) {
+ case V4L2_MBUS_CSI2_DPHY: {
+ unsigned int num_data_lanes = ep.bus.mipi_csi2.num_data_lanes;
+
+ if (num_data_lanes != 1 && num_data_lanes != 2 &&
+ num_data_lanes != 4) {
+ dev_err(unicam->dev, "%u data lanes not supported\n",
+ num_data_lanes);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (num_data_lanes > unicam->max_data_lanes) {
+ dev_err(unicam->dev,
+ "Endpoint uses %u data lanes when %u are supported\n",
+ num_data_lanes, unicam->max_data_lanes);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ unicam->max_data_lanes = num_data_lanes;
+ unicam->bus_flags = ep.bus.mipi_csi2.flags;
+ break;
+ }
+
+ case V4L2_MBUS_CCP2:
+ unicam->max_data_lanes = 1;
+ unicam->bus_flags = ep.bus.mipi_csi1.strobe;
+ break;
+
+ default:
+ /* Unsupported bus type */
+ dev_err(unicam->dev, "Unsupported bus type %u\n", ep.bus_type);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Initialize and register the async notifier. */
+ v4l2_async_nf_init(&unicam->notifier, &unicam->v4l2_dev);
+
+ asc = v4l2_async_nf_add_fwnode_remote(&unicam->notifier, ep_handle,
+ struct v4l2_async_connection);
+ fwnode_handle_put(ep_handle);
+ ep_handle = NULL;
+
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ dev_err(unicam->dev, "Failed to add entry to notifier: %d\n",
+ ret);
+ goto error;
+ }
+
+ unicam->notifier.ops = &unicam_async_ops;
+
+ ret = v4l2_async_nf_register(&unicam->notifier);
+ if (ret) {
+ dev_err(unicam->dev, "Error registering device notifier: %d\n",
+ ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ fwnode_handle_put(ep_handle);
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Probe & remove
+ */
+
+static int unicam_media_init(struct unicam_device *unicam)
+{
+ int ret;
+
+ unicam->mdev.dev = unicam->dev;
+ strscpy(unicam->mdev.model, UNICAM_MODULE_NAME,
+ sizeof(unicam->mdev.model));
+ unicam->mdev.hw_revision = 0;
+
+ media_device_init(&unicam->mdev);
+
+ unicam->v4l2_dev.mdev = &unicam->mdev;
+
+ ret = v4l2_device_register(unicam->dev, &unicam->v4l2_dev);
+ if (ret < 0) {
+ dev_err(unicam->dev, "Unable to register v4l2 device\n");
+ goto err_media_cleanup;
+ }
+
+ ret = media_device_register(&unicam->mdev);
+ if (ret < 0) {
+ dev_err(unicam->dev,
+ "Unable to register media-controller device\n");
+ goto err_v4l2_unregister;
+ }
+
+ return 0;
+
+err_v4l2_unregister:
+ v4l2_device_unregister(&unicam->v4l2_dev);
+err_media_cleanup:
+ media_device_cleanup(&unicam->mdev);
+ return ret;
+}
+
+static int unicam_probe(struct platform_device *pdev)
+{
+ struct unicam_device *unicam;
+ int ret;
+
+ unicam = kzalloc(sizeof(*unicam), GFP_KERNEL);
+ if (!unicam)
+ return -ENOMEM;
+
+ kref_init(&unicam->kref);
+ mutex_init(&unicam->lock);
+
+ unicam->dev = &pdev->dev;
+ platform_set_drvdata(pdev, unicam);
+
+ unicam->base = devm_platform_ioremap_resource_byname(pdev, "unicam");
+ if (IS_ERR(unicam->base)) {
+ ret = PTR_ERR(unicam->base);
+ goto err_unicam_put;
+ }
+
+ unicam->clk_gate_base = devm_platform_ioremap_resource_byname(pdev, "cmi");
+ if (IS_ERR(unicam->clk_gate_base)) {
+ ret = PTR_ERR(unicam->clk_gate_base);
+ goto err_unicam_put;
+ }
+
+ unicam->clock = devm_clk_get(&pdev->dev, "lp");
+ if (IS_ERR(unicam->clock)) {
+ dev_err(unicam->dev, "Failed to get lp clock\n");
+ ret = PTR_ERR(unicam->clock);
+ goto err_unicam_put;
+ }
+
+ unicam->vpu_clock = devm_clk_get(&pdev->dev, "vpu");
+ if (IS_ERR(unicam->vpu_clock)) {
+ dev_err(unicam->dev, "Failed to get vpu clock\n");
+ ret = PTR_ERR(unicam->vpu_clock);
+ goto err_unicam_put;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret < 0)
+ goto err_unicam_put;
+
+ ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0,
+ "unicam_capture0", unicam);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request interrupt\n");
+ goto err_unicam_put;
+ }
+
+ /* Enable the block power domain. */
+ pm_runtime_enable(&pdev->dev);
+
+ ret = unicam_media_init(unicam);
+ if (ret)
+ goto err_pm_runtime;
+
+ ret = unicam_subdev_init(unicam);
+ if (ret)
+ goto err_media_unregister;
+
+ ret = unicam_async_nf_init(unicam);
+ if (ret)
+ goto err_subdev_unregister;
+
+ return 0;
+
+err_subdev_unregister:
+ unicam_subdev_cleanup(unicam);
+err_media_unregister:
+ media_device_unregister(&unicam->mdev);
+err_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
+err_unicam_put:
+ unicam_put(unicam);
+
+ return ret;
+}
+
+static void unicam_remove(struct platform_device *pdev)
+{
+ struct unicam_device *unicam = platform_get_drvdata(pdev);
+
+ unicam_unregister_nodes(unicam);
+ v4l2_device_unregister(&unicam->v4l2_dev);
+ media_device_unregister(&unicam->mdev);
+ v4l2_async_nf_unregister(&unicam->notifier);
+
+ unicam_subdev_cleanup(unicam);
+
+ unicam_put(unicam);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static const struct of_device_id unicam_of_match[] = {
+ { .compatible = "brcm,bcm2835-unicam", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, unicam_of_match);
+
+static struct platform_driver unicam_driver = {
+ .probe = unicam_probe,
+ .remove = unicam_remove,
+ .driver = {
+ .name = UNICAM_MODULE_NAME,
+ .pm = pm_ptr(&unicam_pm_ops),
+ .of_match_table = unicam_of_match,
+ },
+};
+
+module_platform_driver(unicam_driver);
+
+MODULE_AUTHOR("Dave Stevenson <dave.stevenson@raspberrypi.com>");
+MODULE_DESCRIPTION("BCM2835 Unicam driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 2d7b0508cc9a..cebcae196eec 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -164,6 +164,8 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx)
static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
{
+ struct media_pad *src_pad =
+ &csi2rx->source_subdev->entity.pads[csi2rx->source_pad];
union phy_configure_opts opts = { };
struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
struct v4l2_subdev_format sd_fmt = {
@@ -181,7 +183,7 @@ static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code);
- link_freq = v4l2_get_link_freq(csi2rx->source_subdev->ctrl_handler,
+ link_freq = v4l2_get_link_freq(src_pad,
fmt->bpp, 2 * csi2rx->num_lanes);
if (link_freq < 0)
return link_freq;
@@ -239,10 +241,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
writel(reg, csi2rx->base + CSI2RX_STATIC_CFG_REG);
- ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
- if (ret)
- goto err_disable_pclk;
-
/* Enable DPHY clk and data lanes. */
if (csi2rx->dphy) {
reg = CSI2RX_DPHY_CL_EN | CSI2RX_DPHY_CL_RST;
@@ -252,6 +250,13 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
}
writel(reg, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+
+ ret = csi2rx_configure_ext_dphy(csi2rx);
+ if (ret) {
+ dev_err(csi2rx->dev,
+ "Failed to configure external DPHY: %d\n", ret);
+ goto err_disable_pclk;
+ }
}
/*
@@ -291,14 +296,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
reset_control_deassert(csi2rx->sys_rst);
- if (csi2rx->dphy) {
- ret = csi2rx_configure_ext_dphy(csi2rx);
- if (ret) {
- dev_err(csi2rx->dev,
- "Failed to configure external DPHY: %d\n", ret);
- goto err_disable_sysclk;
- }
- }
+ ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
+ if (ret)
+ goto err_disable_sysclk;
clk_disable_unprepare(csi2rx->p_clk);
@@ -312,6 +312,10 @@ err_disable_pixclk:
clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
}
+ if (csi2rx->dphy) {
+ writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
+ phy_power_off(csi2rx->dphy);
+ }
err_disable_pclk:
clk_disable_unprepare(csi2rx->p_clk);
@@ -749,7 +753,7 @@ MODULE_DEVICE_TABLE(of, csi2rx_of_table);
static struct platform_driver csi2rx_driver = {
.probe = csi2rx_probe,
- .remove_new = csi2rx_remove,
+ .remove = csi2rx_remove,
.driver = {
.name = "cdns-csi2rx",
diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c
index 3d98f91f1bee..e22b133f346d 100644
--- a/drivers/media/platform/cadence/cdns-csi2tx.c
+++ b/drivers/media/platform/cadence/cdns-csi2tx.c
@@ -644,7 +644,7 @@ static void csi2tx_remove(struct platform_device *pdev)
static struct platform_driver csi2tx_driver = {
.probe = csi2tx_probe,
- .remove_new = csi2tx_remove,
+ .remove = csi2tx_remove,
.driver = {
.name = "cdns-csi2tx",
diff --git a/drivers/media/platform/chips-media/coda/coda-bit.c b/drivers/media/platform/chips-media/coda/coda-bit.c
index ed47d5bd8d61..84ded154adfe 100644
--- a/drivers/media/platform/chips-media/coda/coda-bit.c
+++ b/drivers/media/platform/chips-media/coda/coda-bit.c
@@ -585,7 +585,7 @@ static int coda_alloc_context_buffers(struct coda_ctx *ctx,
if (!ctx->slicebuf.vaddr && q_data->fourcc == V4L2_PIX_FMT_H264) {
/* worst case slice size */
- size = (DIV_ROUND_UP(q_data->rect.width, 16) *
+ size = (unsigned long)(DIV_ROUND_UP(q_data->rect.width, 16) *
DIV_ROUND_UP(q_data->rect.height, 16)) * 3200 / 8 + 512;
ret = coda_alloc_context_buf(ctx, &ctx->slicebuf, size,
"slicebuf");
diff --git a/drivers/media/platform/chips-media/coda/coda-common.c b/drivers/media/platform/chips-media/coda/coda-common.c
index 7da0194ec850..e6e3f5ec24f6 100644
--- a/drivers/media/platform/chips-media/coda/coda-common.c
+++ b/drivers/media/platform/chips-media/coda/coda-common.c
@@ -2171,8 +2171,6 @@ static const struct vb2_ops coda_qops = {
.buf_queue = coda_buf_queue,
.start_streaming = coda_start_streaming,
.stop_streaming = coda_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int coda_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -3342,11 +3340,12 @@ static int coda_runtime_resume(struct device *dev)
static const struct dev_pm_ops coda_pm_ops = {
SET_RUNTIME_PM_OPS(NULL, coda_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver coda_driver = {
.probe = coda_probe,
- .remove_new = coda_remove,
+ .remove = coda_remove,
.driver = {
.name = CODA_NAME,
.of_match_table = coda_dt_ids,
diff --git a/drivers/media/platform/chips-media/coda/coda-jpeg.c b/drivers/media/platform/chips-media/coda/coda-jpeg.c
index ba8f41002917..5746892658b1 100644
--- a/drivers/media/platform/chips-media/coda/coda-jpeg.c
+++ b/drivers/media/platform/chips-media/coda/coda-jpeg.c
@@ -5,7 +5,7 @@
* Copyright (C) 2014 Philipp Zabel, Pengutronix
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/irqreturn.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c
index 8433ecab230c..2c9d8cbca6e4 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-helper.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
@@ -7,6 +7,8 @@
#include "wave5-helper.h"
+#define DEFAULT_BS_SIZE(width, height) ((width) * (height) / 8 * 3)
+
const char *state_to_str(enum vpu_instance_state state)
{
switch (state) {
@@ -29,7 +31,13 @@ void wave5_cleanup_instance(struct vpu_instance *inst)
{
int i;
- if (list_is_singular(&inst->list))
+ /*
+ * For Wave515 SRAM memory is allocated at
+ * wave5_vpu_dec_register_device() and freed at
+ * wave5_vpu_dec_unregister_device().
+ */
+ if (list_is_singular(&inst->list) &&
+ inst->dev->product_code != WAVE515_CODE)
wave5_vdi_free_sram(inst->dev);
for (i = 0; i < inst->fbc_buf_count; i++)
@@ -52,11 +60,11 @@ int wave5_vpu_release_device(struct file *filp,
char *name)
{
struct vpu_instance *inst = wave5_to_vpu_inst(filp->private_data);
+ int ret = 0;
v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
if (inst->state != VPU_INST_STATE_NONE) {
u32 fail_res;
- int ret;
ret = close_func(inst, &fail_res);
if (fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING) {
@@ -72,7 +80,7 @@ int wave5_vpu_release_device(struct file *filp,
wave5_cleanup_instance(inst);
- return 0;
+ return ret;
}
int wave5_vpu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq,
@@ -211,3 +219,25 @@ void wave5_return_bufs(struct vb2_queue *q, u32 state)
v4l2_m2m_buf_done(vbuf, state);
}
}
+
+void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp,
+ int pix_fmt_type,
+ unsigned int width,
+ unsigned int height,
+ const struct v4l2_frmsize_stepwise *frmsize)
+{
+ v4l2_apply_frmsize_constraints(&width, &height, frmsize);
+
+ if (pix_fmt_type == VPU_FMT_TYPE_CODEC) {
+ pix_mp->width = width;
+ pix_mp->height = height;
+ pix_mp->num_planes = 1;
+ pix_mp->plane_fmt[0].bytesperline = 0;
+ pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_BS_SIZE(width, height),
+ pix_mp->plane_fmt[0].sizeimage);
+ } else {
+ v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat, width, height);
+ }
+ pix_mp->flags = 0;
+ pix_mp->field = V4L2_FIELD_NONE;
+}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h
index 6cee1c14d3ce..9937fce553fc 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-helper.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h
@@ -28,4 +28,9 @@ const struct vpu_format *wave5_find_vpu_fmt_by_idx(unsigned int idx,
const struct vpu_format fmt_list[MAX_FMTS]);
enum wave_std wave5_to_vpu_std(unsigned int v4l2_pix_fmt, enum vpu_instance_type type);
void wave5_return_bufs(struct vb2_queue *q, u32 state);
+void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp,
+ int pix_fmt_type,
+ unsigned int width,
+ unsigned int height,
+ const struct v4l2_frmsize_stepwise *frmsize);
#endif
diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c
index 2d82791f575e..d94cf84c3ee5 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-hw.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c
@@ -18,22 +18,33 @@
#define QUEUE_REPORT_MASK 0xffff
/* Encoder support fields */
-#define FEATURE_HEVC10BIT_ENC BIT(3)
-#define FEATURE_AVC10BIT_ENC BIT(11)
-#define FEATURE_AVC_ENCODER BIT(1)
-#define FEATURE_HEVC_ENCODER BIT(0)
+#define W521_FEATURE_HEVC10BIT_ENC BIT(3)
+#define W521_FEATURE_AVC10BIT_ENC BIT(11)
+#define W521_FEATURE_AVC_ENCODER BIT(1)
+#define W521_FEATURE_HEVC_ENCODER BIT(0)
+
+#define ENC_AVC_INTRA_IDR_PARAM_MASK 0x7ff
+#define ENC_AVC_INTRA_PERIOD_SHIFT 6
+#define ENC_AVC_IDR_PERIOD_SHIFT 17
+#define ENC_AVC_FORCED_IDR_HEADER_SHIFT 28
+
+#define ENC_HEVC_INTRA_QP_SHIFT 3
+#define ENC_HEVC_FORCED_IDR_HEADER_SHIFT 9
+#define ENC_HEVC_INTRA_PERIOD_SHIFT 16
/* Decoder support fields */
-#define FEATURE_AVC_DECODER BIT(3)
-#define FEATURE_HEVC_DECODER BIT(2)
+#define W521_FEATURE_AVC_DECODER BIT(3)
+#define W521_FEATURE_HEVC_DECODER BIT(2)
+#define W515_FEATURE_HEVC10BIT_DEC BIT(1)
+#define W515_FEATURE_HEVC_DECODER BIT(0)
-#define FEATURE_BACKBONE BIT(16)
-#define FEATURE_VCORE_BACKBONE BIT(22)
-#define FEATURE_VCPU_BACKBONE BIT(28)
+#define W521_FEATURE_BACKBONE BIT(16)
+#define W521_FEATURE_VCORE_BACKBONE BIT(22)
+#define W521_FEATURE_VCPU_BACKBONE BIT(28)
#define REMAP_CTRL_MAX_SIZE_BITS ((W5_REMAP_MAX_SIZE >> 12) & 0x1ff)
#define REMAP_CTRL_REGISTER_VALUE(index) ( \
- (BIT(31) | (index << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS) \
+ (BIT(31) | ((index) << 12) | BIT(11) | REMAP_CTRL_MAX_SIZE_BITS)\
)
#define FASTIO_ADDRESS_MASK GENMASK(15, 0)
@@ -155,6 +166,8 @@ static int wave5_wait_bus_busy(struct vpu_device *vpu_dev, unsigned int addr)
{
u32 gdi_status_check_value = 0x3f;
+ if (vpu_dev->product_code == WAVE515_CODE)
+ gdi_status_check_value = 0x0738;
if (vpu_dev->product_code == WAVE521C_CODE ||
vpu_dev->product_code == WAVE521_CODE ||
vpu_dev->product_code == WAVE521E1_CODE)
@@ -186,6 +199,8 @@ unsigned int wave5_vpu_get_product_id(struct vpu_device *vpu_dev)
u32 val = vpu_read_reg(vpu_dev, W5_PRODUCT_NUMBER);
switch (val) {
+ case WAVE515_CODE:
+ return PRODUCT_ID_515;
case WAVE521C_CODE:
return PRODUCT_ID_521;
case WAVE521_CODE:
@@ -299,6 +314,27 @@ static int wave5_send_query(struct vpu_device *vpu_dev, struct vpu_instance *ins
return wave5_vpu_firmware_command_queue_error_check(vpu_dev, NULL);
}
+static void setup_wave5_interrupts(struct vpu_device *vpu_dev)
+{
+ u32 reg_val = 0;
+
+ if (vpu_dev->attr.support_encoders) {
+ /* Encoder interrupt */
+ reg_val |= BIT(INT_WAVE5_ENC_SET_PARAM);
+ reg_val |= BIT(INT_WAVE5_ENC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
+ }
+
+ if (vpu_dev->attr.support_decoders) {
+ /* Decoder interrupt */
+ reg_val |= BIT(INT_WAVE5_INIT_SEQ);
+ reg_val |= BIT(INT_WAVE5_DEC_PIC);
+ reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
+ }
+
+ return vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
+}
+
static int setup_wave5_properties(struct device *dev)
{
struct vpu_device *vpu_dev = dev_get_drvdata(dev);
@@ -328,17 +364,35 @@ static int setup_wave5_properties(struct device *dev)
hw_config_def1 = vpu_read_reg(vpu_dev, W5_RET_STD_DEF1);
hw_config_feature = vpu_read_reg(vpu_dev, W5_RET_CONF_FEATURE);
- p_attr->support_hevc10bit_enc = FIELD_GET(FEATURE_HEVC10BIT_ENC, hw_config_feature);
- p_attr->support_avc10bit_enc = FIELD_GET(FEATURE_AVC10BIT_ENC, hw_config_feature);
-
- p_attr->support_decoders = FIELD_GET(FEATURE_AVC_DECODER, hw_config_def1) << STD_AVC;
- p_attr->support_decoders |= FIELD_GET(FEATURE_HEVC_DECODER, hw_config_def1) << STD_HEVC;
- p_attr->support_encoders = FIELD_GET(FEATURE_AVC_ENCODER, hw_config_def1) << STD_AVC;
- p_attr->support_encoders |= FIELD_GET(FEATURE_HEVC_ENCODER, hw_config_def1) << STD_HEVC;
-
- p_attr->support_backbone = FIELD_GET(FEATURE_BACKBONE, hw_config_def0);
- p_attr->support_vcpu_backbone = FIELD_GET(FEATURE_VCPU_BACKBONE, hw_config_def0);
- p_attr->support_vcore_backbone = FIELD_GET(FEATURE_VCORE_BACKBONE, hw_config_def0);
+ if (vpu_dev->product_code == WAVE515_CODE) {
+ p_attr->support_hevc10bit_dec = FIELD_GET(W515_FEATURE_HEVC10BIT_DEC,
+ hw_config_feature);
+ p_attr->support_decoders = FIELD_GET(W515_FEATURE_HEVC_DECODER,
+ hw_config_def1) << STD_HEVC;
+ } else {
+ p_attr->support_hevc10bit_enc = FIELD_GET(W521_FEATURE_HEVC10BIT_ENC,
+ hw_config_feature);
+ p_attr->support_avc10bit_enc = FIELD_GET(W521_FEATURE_AVC10BIT_ENC,
+ hw_config_feature);
+
+ p_attr->support_decoders = FIELD_GET(W521_FEATURE_AVC_DECODER,
+ hw_config_def1) << STD_AVC;
+ p_attr->support_decoders |= FIELD_GET(W521_FEATURE_HEVC_DECODER,
+ hw_config_def1) << STD_HEVC;
+ p_attr->support_encoders = FIELD_GET(W521_FEATURE_AVC_ENCODER,
+ hw_config_def1) << STD_AVC;
+ p_attr->support_encoders |= FIELD_GET(W521_FEATURE_HEVC_ENCODER,
+ hw_config_def1) << STD_HEVC;
+
+ p_attr->support_backbone = FIELD_GET(W521_FEATURE_BACKBONE,
+ hw_config_def0);
+ p_attr->support_vcpu_backbone = FIELD_GET(W521_FEATURE_VCPU_BACKBONE,
+ hw_config_def0);
+ p_attr->support_vcore_backbone = FIELD_GET(W521_FEATURE_VCORE_BACKBONE,
+ hw_config_def0);
+ }
+
+ setup_wave5_interrupts(vpu_dev);
return 0;
}
@@ -380,12 +434,18 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size)
common_vb = &vpu_dev->common_mem;
code_base = common_vb->daddr;
+
+ if (vpu_dev->product_code == WAVE515_CODE)
+ code_size = WAVE515_MAX_CODE_BUF_SIZE;
+ else
+ code_size = WAVE521_MAX_CODE_BUF_SIZE;
+
/* ALIGN TO 4KB */
- code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
+ code_size &= ~0xfff;
if (code_size < size * 2)
return -EINVAL;
- temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET;
+ temp_base = code_base + code_size;
temp_size = WAVE5_TEMPBUF_SIZE;
ret = wave5_vdi_write_memory(vpu_dev, common_vb, 0, fw, size);
@@ -413,22 +473,15 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size)
/* These register must be reset explicitly */
vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
- wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
- wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
- vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
-
- /* Encoder interrupt */
- reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
- reg_val |= BIT(INT_WAVE5_ENC_PIC);
- reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
- /* Decoder interrupt */
- reg_val |= BIT(INT_WAVE5_INIT_SEQ);
- reg_val |= BIT(INT_WAVE5_DEC_PIC);
- reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
- vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
+
+ if (vpu_dev->product_code != WAVE515_CODE) {
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
+ vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
+ }
reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
- if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
+ if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) {
reg_val = ((WAVE5_PROC_AXI_ID << 28) |
(WAVE5_PRP_AXI_ID << 24) |
(WAVE5_FBD_Y_AXI_ID << 20) |
@@ -440,6 +493,24 @@ int wave5_vpu_init(struct device *dev, u8 *fw, size_t size)
wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
}
+ if (vpu_dev->product_code == WAVE515_CODE) {
+ dma_addr_t task_buf_base;
+
+ vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF, WAVE515_COMMAND_QUEUE_DEPTH);
+ vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE, WAVE515_ONE_TASKBUF_SIZE);
+
+ for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) {
+ task_buf_base = temp_base + temp_size +
+ (i * WAVE515_ONE_TASKBUF_SIZE);
+ vpu_write_reg(vpu_dev,
+ W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4),
+ task_buf_base);
+ }
+
+ vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
+ vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
+ }
+
vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
@@ -480,30 +551,41 @@ int wave5_vpu_build_up_dec_param(struct vpu_instance *inst,
return -EINVAL;
}
- p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE;
+ if (vpu_dev->product == PRODUCT_ID_515)
+ p_dec_info->vb_work.size = WAVE515DEC_WORKBUF_SIZE;
+ else
+ p_dec_info->vb_work.size = WAVE521DEC_WORKBUF_SIZE;
+
ret = wave5_vdi_allocate_dma_memory(inst->dev, &p_dec_info->vb_work);
if (ret)
return ret;
- vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1);
+ if (inst->dev->product_code != WAVE515_CODE)
+ vpu_write_reg(inst->dev, W5_CMD_DEC_VCORE_INFO, 1);
wave5_vdi_clear_memory(inst->dev, &p_dec_info->vb_work);
vpu_write_reg(inst->dev, W5_ADDR_WORK_BASE, p_dec_info->vb_work.daddr);
vpu_write_reg(inst->dev, W5_WORK_SIZE, p_dec_info->vb_work.size);
- vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
- vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
+ if (inst->dev->product_code != WAVE515_CODE) {
+ vpu_write_reg(inst->dev, W5_CMD_ADDR_SEC_AXI, vpu_dev->sram_buf.daddr);
+ vpu_write_reg(inst->dev, W5_CMD_SEC_AXI_SIZE, vpu_dev->sram_buf.size);
+ }
vpu_write_reg(inst->dev, W5_CMD_DEC_BS_START_ADDR, p_dec_info->stream_buf_start_addr);
vpu_write_reg(inst->dev, W5_CMD_DEC_BS_SIZE, p_dec_info->stream_buf_size);
/* NOTE: SDMA reads MSB first */
vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, BITSTREAM_ENDIANNESS_BIG_ENDIAN);
- /* This register must be reset explicitly */
- vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
- vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1));
+ if (inst->dev->product_code != WAVE515_CODE) {
+ /* This register must be reset explicitly */
+ vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
+ vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1,
+ WAVE521_COMMAND_QUEUE_DEPTH - 1);
+ }
+ vpu_write_reg(inst->dev, W5_CMD_ERR_CONCEAL, 0);
ret = send_firmware_command(inst, W5_CREATE_INSTANCE, true, NULL, NULL);
if (ret) {
wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_work);
@@ -553,7 +635,7 @@ static u32 get_bitstream_options(struct dec_info *info)
int wave5_vpu_dec_init_seq(struct vpu_instance *inst)
{
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
- u32 cmd_option = INIT_SEQ_NORMAL;
+ u32 bs_option, cmd_option = INIT_SEQ_NORMAL;
u32 reg_val, fail_res;
int ret;
@@ -563,7 +645,13 @@ int wave5_vpu_dec_init_seq(struct vpu_instance *inst)
vpu_write_reg(inst->dev, W5_BS_RD_PTR, p_dec_info->stream_rd_ptr);
vpu_write_reg(inst->dev, W5_BS_WR_PTR, p_dec_info->stream_wr_ptr);
- vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
+ bs_option = get_bitstream_options(p_dec_info);
+
+ /* Without RD_PTR_VALID_FLAG Wave515 ignores RD_PTR value */
+ if (inst->dev->product_code == WAVE515_CODE)
+ bs_option |= BSOPTION_RD_PTR_VALID_FLAG;
+
+ vpu_write_reg(inst->dev, W5_BS_OPTION, bs_option);
vpu_write_reg(inst->dev, W5_COMMAND_OPTION, cmd_option);
vpu_write_reg(inst->dev, W5_CMD_DEC_USER_MASK, p_dec_info->user_data_enable);
@@ -629,10 +717,12 @@ static void wave5_get_dec_seq_result(struct vpu_instance *inst, struct dec_initi
info->profile = FIELD_GET(SEQ_PARAM_PROFILE_MASK, reg_val);
}
- info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
- info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
- p_dec_info->vlc_buf_size = info->vlc_buf_size;
- p_dec_info->param_buf_size = info->param_buf_size;
+ if (inst->dev->product_code != WAVE515_CODE) {
+ info->vlc_buf_size = vpu_read_reg(inst->dev, W5_RET_VLC_BUF_SIZE);
+ info->param_buf_size = vpu_read_reg(inst->dev, W5_RET_PARAM_BUF_SIZE);
+ p_dec_info->vlc_buf_size = info->vlc_buf_size;
+ p_dec_info->param_buf_size = info->param_buf_size;
+ }
}
int wave5_vpu_dec_get_seq_info(struct vpu_instance *inst, struct dec_initial_info *info)
@@ -734,22 +824,27 @@ int wave5_vpu_dec_register_framebuffer(struct vpu_instance *inst, struct frame_b
pic_size = (init_info->pic_width << 16) | (init_info->pic_height);
- vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) +
- (p_dec_info->param_buf_size * COMMAND_QUEUE_DEPTH);
- vb_buf.daddr = 0;
+ if (inst->dev->product_code != WAVE515_CODE) {
+ vb_buf.size = (p_dec_info->vlc_buf_size * VLC_BUF_NUM) +
+ (p_dec_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH);
+ vb_buf.daddr = 0;
- if (vb_buf.size != p_dec_info->vb_task.size) {
- wave5_vdi_free_dma_memory(inst->dev, &p_dec_info->vb_task);
- ret = wave5_vdi_allocate_dma_memory(inst->dev, &vb_buf);
- if (ret)
- goto free_fbc_c_tbl_buffers;
+ if (vb_buf.size != p_dec_info->vb_task.size) {
+ wave5_vdi_free_dma_memory(inst->dev,
+ &p_dec_info->vb_task);
+ ret = wave5_vdi_allocate_dma_memory(inst->dev,
+ &vb_buf);
+ if (ret)
+ goto free_fbc_c_tbl_buffers;
- p_dec_info->vb_task = vb_buf;
- }
+ p_dec_info->vb_task = vb_buf;
+ }
- vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
- p_dec_info->vb_task.daddr);
- vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE, vb_buf.size);
+ vpu_write_reg(inst->dev, W5_CMD_SET_FB_ADDR_TASK_BUF,
+ p_dec_info->vb_task.daddr);
+ vpu_write_reg(inst->dev, W5_CMD_SET_FB_TASK_BUF_SIZE,
+ vb_buf.size);
+ }
} else {
pic_size = (init_info->pic_width << 16) | (init_info->pic_height);
@@ -830,6 +925,43 @@ free_mv_buffers:
return ret;
}
+static u32 wave5_vpu_dec_validate_sec_axi(struct vpu_instance *inst)
+{
+ u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth;
+ struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ u32 bit_size = 0, ip_size = 0, lf_size = 0, ret = 0;
+ u32 sram_size = inst->dev->sram_size;
+ u32 width = inst->src_fmt.width;
+
+ if (!sram_size)
+ return 0;
+
+ /*
+ * TODO: calculate bit_size, ip_size, lf_size from width and bitdepth
+ * for Wave521.
+ */
+ if (inst->dev->product_code == WAVE515_CODE) {
+ bit_size = DIV_ROUND_UP(width, 16) * 5 * 8;
+ ip_size = ALIGN(width, 16) * 2 * bitdepth / 8;
+ lf_size = ALIGN(width, 16) * 10 * bitdepth / 8;
+ }
+
+ if (p_dec_info->sec_axi_info.use_bit_enable && sram_size >= bit_size) {
+ ret |= BIT(0);
+ sram_size -= bit_size;
+ }
+
+ if (p_dec_info->sec_axi_info.use_ip_enable && sram_size >= ip_size) {
+ ret |= BIT(9);
+ sram_size -= ip_size;
+ }
+
+ if (p_dec_info->sec_axi_info.use_lf_row_enable && sram_size >= lf_size)
+ ret |= BIT(15);
+
+ return ret;
+}
+
int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res)
{
u32 reg_val;
@@ -842,9 +974,7 @@ int wave5_vpu_decode(struct vpu_instance *inst, u32 *fail_res)
vpu_write_reg(inst->dev, W5_BS_OPTION, get_bitstream_options(p_dec_info));
/* secondary AXI */
- reg_val = p_dec_info->sec_axi_info.use_bit_enable |
- (p_dec_info->sec_axi_info.use_ip_enable << 9) |
- (p_dec_info->sec_axi_info.use_lf_row_enable << 15);
+ reg_val = wave5_vpu_dec_validate_sec_axi(inst);
vpu_write_reg(inst->dev, W5_USE_SEC_AXI, reg_val);
/* set attributes of user buffer */
@@ -992,11 +1122,18 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size)
common_vb = &vpu_dev->common_mem;
code_base = common_vb->daddr;
+
+ if (vpu_dev->product_code == WAVE515_CODE)
+ code_size = WAVE515_MAX_CODE_BUF_SIZE;
+ else
+ code_size = WAVE521_MAX_CODE_BUF_SIZE;
+
/* ALIGN TO 4KB */
- code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
+ code_size &= ~0xfff;
if (code_size < size * 2)
return -EINVAL;
- temp_base = common_vb->daddr + WAVE5_TEMPBUF_OFFSET;
+
+ temp_base = code_base + code_size;
temp_size = WAVE5_TEMPBUF_SIZE;
old_code_base = vpu_read_reg(vpu_dev, W5_VPU_REMAP_PADDR);
@@ -1030,22 +1167,15 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size)
/* These register must be reset explicitly */
vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
- wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
- wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
- vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
- /* Encoder interrupt */
- reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
- reg_val |= BIT(INT_WAVE5_ENC_PIC);
- reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
- /* Decoder interrupt */
- reg_val |= BIT(INT_WAVE5_INIT_SEQ);
- reg_val |= BIT(INT_WAVE5_DEC_PIC);
- reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
- vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
+ if (vpu_dev->product_code != WAVE515_CODE) {
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
+ vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
+ }
reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
- if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
+ if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) {
reg_val = ((WAVE5_PROC_AXI_ID << 28) |
(WAVE5_PRP_AXI_ID << 24) |
(WAVE5_FBD_Y_AXI_ID << 20) |
@@ -1057,6 +1187,29 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size)
wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
}
+ if (vpu_dev->product_code == WAVE515_CODE) {
+ dma_addr_t task_buf_base;
+ u32 i;
+
+ vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF,
+ WAVE515_COMMAND_QUEUE_DEPTH);
+ vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE,
+ WAVE515_ONE_TASKBUF_SIZE);
+
+ for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) {
+ task_buf_base = temp_base + temp_size +
+ (i * WAVE515_ONE_TASKBUF_SIZE);
+ vpu_write_reg(vpu_dev,
+ W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4),
+ task_buf_base);
+ }
+
+ vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI,
+ vpu_dev->sram_buf.daddr);
+ vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE,
+ vpu_dev->sram_buf.size);
+ }
+
vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
vpu_write_reg(vpu_dev, W5_COMMAND, W5_INIT_VPU);
vpu_write_reg(vpu_dev, W5_VPU_REMAP_CORE_START, 1);
@@ -1075,13 +1228,13 @@ int wave5_vpu_re_init(struct device *dev, u8 *fw, size_t size)
return setup_wave5_properties(dev);
}
-static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code,
- size_t size)
+int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code,
+ size_t size)
{
u32 reg_val;
struct vpu_buf *common_vb;
- dma_addr_t code_base;
- u32 code_size, reason_code;
+ dma_addr_t code_base, temp_base;
+ u32 code_size, temp_size, reason_code;
struct vpu_device *vpu_dev = dev_get_drvdata(dev);
int ret;
@@ -1111,13 +1264,22 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin
common_vb = &vpu_dev->common_mem;
code_base = common_vb->daddr;
+
+ if (vpu_dev->product_code == WAVE515_CODE)
+ code_size = WAVE515_MAX_CODE_BUF_SIZE;
+ else
+ code_size = WAVE521_MAX_CODE_BUF_SIZE;
+
/* ALIGN TO 4KB */
- code_size = (WAVE5_MAX_CODE_BUF_SIZE & ~0xfff);
+ code_size &= ~0xfff;
if (code_size < size * 2) {
dev_err(dev, "size too small\n");
return -EINVAL;
}
+ temp_base = code_base + code_size;
+ temp_size = WAVE5_TEMPBUF_SIZE;
+
/* Power on without DEBUG mode */
vpu_write_reg(vpu_dev, W5_PO_CONF, 0);
@@ -1130,22 +1292,17 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin
/* These register must be reset explicitly */
vpu_write_reg(vpu_dev, W5_HW_OPTION, 0);
- wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
- wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
- vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
- /* Encoder interrupt */
- reg_val = BIT(INT_WAVE5_ENC_SET_PARAM);
- reg_val |= BIT(INT_WAVE5_ENC_PIC);
- reg_val |= BIT(INT_WAVE5_BSBUF_FULL);
- /* Decoder interrupt */
- reg_val |= BIT(INT_WAVE5_INIT_SEQ);
- reg_val |= BIT(INT_WAVE5_DEC_PIC);
- reg_val |= BIT(INT_WAVE5_BSBUF_EMPTY);
- vpu_write_reg(vpu_dev, W5_VPU_VINT_ENABLE, reg_val);
+ if (vpu_dev->product_code != WAVE515_CODE) {
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_PROC_EXT_ADDR, 0);
+ wave5_fio_writel(vpu_dev, W5_BACKBONE_AXI_PARAM, 0);
+ vpu_write_reg(vpu_dev, W5_SEC_AXI_PARAM, 0);
+ }
+
+ setup_wave5_interrupts(vpu_dev);
reg_val = vpu_read_reg(vpu_dev, W5_VPU_RET_VPU_CONFIG0);
- if (FIELD_GET(FEATURE_BACKBONE, reg_val)) {
+ if (FIELD_GET(W521_FEATURE_BACKBONE, reg_val)) {
reg_val = ((WAVE5_PROC_AXI_ID << 28) |
(WAVE5_PRP_AXI_ID << 24) |
(WAVE5_FBD_Y_AXI_ID << 20) |
@@ -1157,6 +1314,29 @@ static int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uin
wave5_fio_writel(vpu_dev, W5_BACKBONE_PROG_AXI_ID, reg_val);
}
+ if (vpu_dev->product_code == WAVE515_CODE) {
+ dma_addr_t task_buf_base;
+ u32 i;
+
+ vpu_write_reg(vpu_dev, W5_CMD_INIT_NUM_TASK_BUF,
+ WAVE515_COMMAND_QUEUE_DEPTH);
+ vpu_write_reg(vpu_dev, W5_CMD_INIT_TASK_BUF_SIZE,
+ WAVE515_ONE_TASKBUF_SIZE);
+
+ for (i = 0; i < WAVE515_COMMAND_QUEUE_DEPTH; i++) {
+ task_buf_base = temp_base + temp_size +
+ (i * WAVE515_ONE_TASKBUF_SIZE);
+ vpu_write_reg(vpu_dev,
+ W5_CMD_INIT_ADDR_TASK_BUF0 + (i * 4),
+ task_buf_base);
+ }
+
+ vpu_write_reg(vpu_dev, W515_CMD_ADDR_SEC_AXI,
+ vpu_dev->sram_buf.daddr);
+ vpu_write_reg(vpu_dev, W515_CMD_SEC_AXI_SIZE,
+ vpu_dev->sram_buf.size);
+ }
+
vpu_write_reg(vpu_dev, W5_VPU_BUSY_STATUS, 1);
vpu_write_reg(vpu_dev, W5_COMMAND, W5_WAKEUP_VPU);
/* Start VPU after settings */
@@ -1401,7 +1581,7 @@ int wave5_vpu_build_up_enc_param(struct device *dev, struct vpu_instance *inst,
reg_val = (open_param->line_buf_int_en << 6) | BITSTREAM_ENDIANNESS_BIG_ENDIAN;
vpu_write_reg(inst->dev, W5_CMD_BS_PARAM, reg_val);
vpu_write_reg(inst->dev, W5_CMD_EXT_ADDR, 0);
- vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, (COMMAND_QUEUE_DEPTH - 1));
+ vpu_write_reg(inst->dev, W5_CMD_NUM_CQ_DEPTH_M1, WAVE521_COMMAND_QUEUE_DEPTH - 1);
/* This register must be reset explicitly */
vpu_write_reg(inst->dev, W5_CMD_ENC_SRC_OPTIONS, 0);
@@ -1601,12 +1781,19 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst)
if (inst->std == W_AVC_ENC)
vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM, p_param->intra_qp |
- ((p_param->intra_period & 0x7ff) << 6) |
- ((p_param->avc_idr_period & 0x7ff) << 17));
+ ((p_param->intra_period & ENC_AVC_INTRA_IDR_PARAM_MASK)
+ << ENC_AVC_INTRA_PERIOD_SHIFT) |
+ ((p_param->avc_idr_period & ENC_AVC_INTRA_IDR_PARAM_MASK)
+ << ENC_AVC_IDR_PERIOD_SHIFT) |
+ (p_param->forced_idr_header_enable
+ << ENC_AVC_FORCED_IDR_HEADER_SHIFT));
else if (inst->std == W_HEVC_ENC)
vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_INTRA_PARAM,
- p_param->decoding_refresh_type | (p_param->intra_qp << 3) |
- (p_param->intra_period << 16));
+ p_param->decoding_refresh_type |
+ (p_param->intra_qp << ENC_HEVC_INTRA_QP_SHIFT) |
+ (p_param->forced_idr_header_enable
+ << ENC_HEVC_FORCED_IDR_HEADER_SHIFT) |
+ (p_param->intra_period << ENC_HEVC_INTRA_PERIOD_SHIFT));
reg_val = (p_param->rdo_skip << 2) |
(p_param->lambda_scaling_enable << 3) |
@@ -1855,7 +2042,7 @@ int wave5_vpu_enc_register_framebuffer(struct device *dev, struct vpu_instance *
p_enc_info->vb_sub_sam_buf = vb_sub_sam_buf;
vb_task.size = (p_enc_info->vlc_buf_size * VLC_BUF_NUM) +
- (p_enc_info->param_buf_size * COMMAND_QUEUE_DEPTH);
+ (p_enc_info->param_buf_size * WAVE521_COMMAND_QUEUE_DEPTH);
vb_task.daddr = 0;
if (p_enc_info->vb_task.size == 0) {
ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vb_task);
@@ -1943,6 +2130,31 @@ free_vb_fbc_y_tbl:
return ret;
}
+static u32 wave5_vpu_enc_validate_sec_axi(struct vpu_instance *inst)
+{
+ struct enc_info *p_enc_info = &inst->codec_info->enc_info;
+ u32 rdo_size = 0, lf_size = 0, ret = 0;
+ u32 sram_size = inst->dev->sram_size;
+
+ if (!sram_size)
+ return 0;
+
+ /*
+ * TODO: calculate rdo_size and lf_size from inst->src_fmt.width and
+ * inst->codec_info->enc_info.open_param.wave_param.internal_bit_depth
+ */
+
+ if (p_enc_info->sec_axi_info.use_enc_rdo_enable && sram_size >= rdo_size) {
+ ret |= BIT(11);
+ sram_size -= rdo_size;
+ }
+
+ if (p_enc_info->sec_axi_info.use_enc_lf_enable && sram_size >= lf_size)
+ ret |= BIT(15);
+
+ return ret;
+}
+
int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *fail_res)
{
u32 src_frame_format;
@@ -1964,8 +2176,7 @@ int wave5_vpu_encode(struct vpu_instance *inst, struct enc_param *option, u32 *f
vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_SRC_AXI_SEL, DEFAULT_SRC_AXI);
/* secondary AXI */
- reg_val = (p_enc_info->sec_axi_info.use_enc_rdo_enable << 11) |
- (p_enc_info->sec_axi_info.use_enc_lf_enable << 15);
+ reg_val = wave5_vpu_enc_validate_sec_axi(inst);
vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_USE_SEC_AXI, reg_val);
vpu_write_reg(inst->dev, W5_CMD_ENC_PIC_REPORT_PARAM, 0);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h
index a15c6b2c3d8b..557344754c4c 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-regdefine.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-regdefine.h
@@ -205,6 +205,9 @@ enum query_opt {
#define W5_ADDR_TEMP_BASE (W5_REG_BASE + 0x011C)
#define W5_TEMP_SIZE (W5_REG_BASE + 0x0120)
#define W5_HW_OPTION (W5_REG_BASE + 0x012C)
+#define W5_CMD_INIT_NUM_TASK_BUF (W5_REG_BASE + 0x0134)
+#define W5_CMD_INIT_ADDR_TASK_BUF0 (W5_REG_BASE + 0x0138)
+#define W5_CMD_INIT_TASK_BUF_SIZE (W5_REG_BASE + 0x0178)
#define W5_SEC_AXI_PARAM (W5_REG_BASE + 0x0180)
/************************************************************************/
@@ -216,7 +219,9 @@ enum query_opt {
#define W5_CMD_DEC_BS_SIZE (W5_REG_BASE + 0x0120)
#define W5_CMD_BS_PARAM (W5_REG_BASE + 0x0124)
#define W5_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0130)
+#define W515_CMD_ADDR_SEC_AXI (W5_REG_BASE + 0x0124)
#define W5_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0134)
+#define W515_CMD_SEC_AXI_SIZE (W5_REG_BASE + 0x0128)
#define W5_CMD_EXT_ADDR (W5_REG_BASE + 0x0138)
#define W5_CMD_NUM_CQ_DEPTH_M1 (W5_REG_BASE + 0x013C)
#define W5_CMD_ERR_CONCEAL (W5_REG_BASE + 0x0140)
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vdi.c b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
index 3809f70bc0b4..bb13267ced38 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vdi.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vdi.c
@@ -18,7 +18,11 @@ static int wave5_vdi_allocate_common_memory(struct device *dev)
if (!vpu_dev->common_mem.vaddr) {
int ret;
- vpu_dev->common_mem.size = SIZE_COMMON;
+ if (vpu_dev->product_code == WAVE515_CODE)
+ vpu_dev->common_mem.size = WAVE515_SIZE_COMMON;
+ else
+ vpu_dev->common_mem.size = WAVE521_SIZE_COMMON;
+
ret = wave5_vdi_allocate_dma_memory(vpu_dev, &vpu_dev->common_mem);
if (ret) {
dev_err(dev, "unable to allocate common buffer\n");
@@ -174,16 +178,19 @@ int wave5_vdi_allocate_array(struct vpu_device *vpu_dev, struct vpu_buf *array,
void wave5_vdi_allocate_sram(struct vpu_device *vpu_dev)
{
struct vpu_buf *vb = &vpu_dev->sram_buf;
+ dma_addr_t daddr;
+ void *vaddr;
+ size_t size;
- if (!vpu_dev->sram_pool || !vpu_dev->sram_size)
+ if (!vpu_dev->sram_pool || vb->vaddr)
return;
- if (!vb->vaddr) {
- vb->size = vpu_dev->sram_size;
- vb->vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, vb->size,
- &vb->daddr);
- if (!vb->vaddr)
- vb->size = 0;
+ size = min_t(size_t, vpu_dev->sram_size, gen_pool_avail(vpu_dev->sram_pool));
+ vaddr = gen_pool_dma_alloc(vpu_dev->sram_pool, size, &daddr);
+ if (vaddr) {
+ vb->vaddr = vaddr;
+ vb->daddr = daddr;
+ vb->size = size;
}
dev_dbg(vpu_dev->dev, "%s: sram daddr: %pad, size: %zu, vaddr: 0x%p\n",
@@ -197,9 +204,7 @@ void wave5_vdi_free_sram(struct vpu_device *vpu_dev)
if (!vb->size || !vb->vaddr)
return;
- if (vb->vaddr)
- gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr,
- vb->size);
+ gen_pool_free(vpu_dev->sram_pool, (unsigned long)vb->vaddr, vb->size);
memset(vb, 0, sizeof(*vb));
}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
index ef227af72348..fd71f0c43ac3 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -5,116 +5,98 @@
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
*/
+#include <linux/pm_runtime.h>
#include "wave5-helper.h"
#define VPU_DEC_DEV_NAME "C&M Wave5 VPU decoder"
#define VPU_DEC_DRV_NAME "wave5-dec"
-#define DEFAULT_SRC_SIZE(width, height) ({ \
- (width) * (height) / 8 * 3; \
-})
+static const struct v4l2_frmsize_stepwise dec_hevc_frmsize = {
+ .min_width = W5_MIN_DEC_PIC_8_WIDTH,
+ .max_width = W5_MAX_DEC_PIC_WIDTH,
+ .step_width = W5_DEC_CODEC_STEP_WIDTH,
+ .min_height = W5_MIN_DEC_PIC_8_HEIGHT,
+ .max_height = W5_MAX_DEC_PIC_HEIGHT,
+ .step_height = W5_DEC_CODEC_STEP_HEIGHT,
+};
+
+static const struct v4l2_frmsize_stepwise dec_h264_frmsize = {
+ .min_width = W5_MIN_DEC_PIC_32_WIDTH,
+ .max_width = W5_MAX_DEC_PIC_WIDTH,
+ .step_width = W5_DEC_CODEC_STEP_WIDTH,
+ .min_height = W5_MIN_DEC_PIC_32_HEIGHT,
+ .max_height = W5_MAX_DEC_PIC_HEIGHT,
+ .step_height = W5_DEC_CODEC_STEP_HEIGHT,
+};
+
+static const struct v4l2_frmsize_stepwise dec_raw_frmsize = {
+ .min_width = W5_MIN_DEC_PIC_8_WIDTH,
+ .max_width = W5_MAX_DEC_PIC_WIDTH,
+ .step_width = W5_DEC_RAW_STEP_WIDTH,
+ .min_height = W5_MIN_DEC_PIC_8_HEIGHT,
+ .max_height = W5_MAX_DEC_PIC_HEIGHT,
+ .step_height = W5_DEC_RAW_STEP_HEIGHT,
+};
static const struct vpu_format dec_fmt_list[FMT_TYPES][MAX_FMTS] = {
[VPU_FMT_TYPE_CODEC] = {
{
.v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_hevc_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_H264,
- .max_width = 8192,
- .min_width = 32,
- .max_height = 4320,
- .min_height = 32,
+ .v4l2_frmsize = &dec_h264_frmsize,
},
},
[VPU_FMT_TYPE_RAW] = {
{
.v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV16,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV61,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV16M,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV61M,
- .max_width = 8192,
- .min_width = 8,
- .max_height = 4320,
- .min_height = 8,
+ .v4l2_frmsize = &dec_raw_frmsize,
},
}
};
@@ -233,74 +215,6 @@ static void wave5_handle_src_buffer(struct vpu_instance *inst, dma_addr_t rd_ptr
inst->remaining_consumed_bytes = consumed_bytes;
}
-static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
- unsigned int height)
-{
- switch (pix_mp->pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- pix_mp->width = round_up(width, 32);
- pix_mp->height = round_up(height, 16);
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = width * height * 3 / 2;
- break;
- case V4L2_PIX_FMT_YUV422P:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- pix_mp->width = round_up(width, 32);
- pix_mp->height = round_up(height, 16);
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = width * height * 2;
- break;
- case V4L2_PIX_FMT_YUV420M:
- pix_mp->width = round_up(width, 32);
- pix_mp->height = round_up(height, 16);
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = width * height;
- pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
- pix_mp->plane_fmt[1].sizeimage = width * height / 4;
- pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
- pix_mp->plane_fmt[2].sizeimage = width * height / 4;
- break;
- case V4L2_PIX_FMT_NV12M:
- case V4L2_PIX_FMT_NV21M:
- pix_mp->width = round_up(width, 32);
- pix_mp->height = round_up(height, 16);
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = width * height;
- pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[1].sizeimage = width * height / 2;
- break;
- case V4L2_PIX_FMT_YUV422M:
- pix_mp->width = round_up(width, 32);
- pix_mp->height = round_up(height, 16);
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = width * height;
- pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
- pix_mp->plane_fmt[1].sizeimage = width * height / 2;
- pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
- pix_mp->plane_fmt[2].sizeimage = width * height / 2;
- break;
- case V4L2_PIX_FMT_NV16M:
- case V4L2_PIX_FMT_NV61M:
- pix_mp->width = round_up(width, 32);
- pix_mp->height = round_up(height, 16);
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = width * height;
- pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[1].sizeimage = width * height;
- break;
- default:
- pix_mp->width = width;
- pix_mp->height = height;
- pix_mp->plane_fmt[0].bytesperline = 0;
- pix_mp->plane_fmt[0].sizeimage = max(DEFAULT_SRC_SIZE(width, height),
- pix_mp->plane_fmt[0].sizeimage);
- break;
- }
-}
-
static int start_decode(struct vpu_instance *inst, u32 *fail_res)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
@@ -388,6 +302,8 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst)
}
if (p_dec_info->initial_info_obtained) {
+ const struct vpu_format *vpu_fmt;
+
inst->conf_win.left = initial_info->pic_crop_rect.left;
inst->conf_win.top = initial_info->pic_crop_rect.top;
inst->conf_win.width = initial_info->pic_width -
@@ -395,10 +311,27 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst)
inst->conf_win.height = initial_info->pic_height -
initial_info->pic_crop_rect.top - initial_info->pic_crop_rect.bottom;
- wave5_update_pix_fmt(&inst->src_fmt, initial_info->pic_width,
- initial_info->pic_height);
- wave5_update_pix_fmt(&inst->dst_fmt, initial_info->pic_width,
- initial_info->pic_height);
+ vpu_fmt = wave5_find_vpu_fmt(inst->src_fmt.pixelformat,
+ dec_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ wave5_update_pix_fmt(&inst->src_fmt,
+ VPU_FMT_TYPE_CODEC,
+ initial_info->pic_width,
+ initial_info->pic_height,
+ vpu_fmt->v4l2_frmsize);
+
+ vpu_fmt = wave5_find_vpu_fmt(inst->dst_fmt.pixelformat,
+ dec_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ wave5_update_pix_fmt(&inst->dst_fmt,
+ VPU_FMT_TYPE_RAW,
+ initial_info->pic_width,
+ initial_info->pic_height,
+ vpu_fmt->v4l2_frmsize);
}
v4l2_event_queue_fh(fh, &vpu_event_src_ch);
@@ -518,6 +451,8 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst)
if (q_status.report_queue_count == 0 &&
(q_status.instance_queue_count == 0 || dec_info.sequence_changed)) {
dev_dbg(inst->dev->dev, "%s: finishing job.\n", __func__);
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
}
@@ -545,12 +480,12 @@ static int wave5_vpu_dec_enum_framesizes(struct file *f, void *fh, struct v4l2_f
}
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
- fsize->stepwise.min_width = vpu_fmt->min_width;
- fsize->stepwise.max_width = vpu_fmt->max_width;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.min_height = vpu_fmt->min_height;
- fsize->stepwise.max_height = vpu_fmt->max_height;
- fsize->stepwise.step_height = 1;
+ fsize->stepwise.min_width = vpu_fmt->v4l2_frmsize->min_width;
+ fsize->stepwise.max_width = vpu_fmt->v4l2_frmsize->max_width;
+ fsize->stepwise.step_width = W5_DEC_CODEC_STEP_WIDTH;
+ fsize->stepwise.min_height = vpu_fmt->v4l2_frmsize->min_height;
+ fsize->stepwise.max_height = vpu_fmt->v4l2_frmsize->max_height;
+ fsize->stepwise.step_height = W5_DEC_CODEC_STEP_HEIGHT;
return 0;
}
@@ -573,6 +508,7 @@ static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo
{
struct vpu_instance *inst = wave5_to_vpu_inst(fh);
struct dec_info *p_dec_info = &inst->codec_info->dec_info;
+ const struct v4l2_frmsize_stepwise *frmsize;
const struct vpu_format *vpu_fmt;
int width, height;
@@ -586,14 +522,12 @@ static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo
width = inst->dst_fmt.width;
height = inst->dst_fmt.height;
f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
- f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
+ frmsize = &dec_raw_frmsize;
} else {
- const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
-
- width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
- height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
+ width = f->fmt.pix_mp.width;
+ height = f->fmt.pix_mp.height;
f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
- f->fmt.pix_mp.num_planes = info->mem_planes;
+ frmsize = vpu_fmt->v4l2_frmsize;
}
if (p_dec_info->initial_info_obtained) {
@@ -601,9 +535,8 @@ static int wave5_vpu_dec_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo
height = inst->dst_fmt.height;
}
- wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
- f->fmt.pix_mp.flags = 0;
- f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_RAW,
+ width, height, frmsize);
f->fmt.pix_mp.colorspace = inst->colorspace;
f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
f->fmt.pix_mp.quantization = inst->quantization;
@@ -715,7 +648,9 @@ static int wave5_vpu_dec_enum_fmt_out(struct file *file, void *fh, struct v4l2_f
static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
{
struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct v4l2_frmsize_stepwise *frmsize;
const struct vpu_format *vpu_fmt;
+ int width, height;
dev_dbg(inst->dev->dev,
"%s: fourcc: %u width: %u height: %u num_planes: %u colorspace: %u field: %u\n",
@@ -724,20 +659,19 @@ static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_fo
vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, dec_fmt_list[VPU_FMT_TYPE_CODEC]);
if (!vpu_fmt) {
+ width = inst->src_fmt.width;
+ height = inst->src_fmt.height;
f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
- f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
- wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
+ frmsize = &dec_hevc_frmsize;
} else {
- int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
- int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
-
+ width = f->fmt.pix_mp.width;
+ height = f->fmt.pix_mp.height;
f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
- f->fmt.pix_mp.num_planes = 1;
- wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
+ frmsize = vpu_fmt->v4l2_frmsize;
}
- f->fmt.pix_mp.flags = 0;
- f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_CODEC,
+ width, height, frmsize);
return 0;
}
@@ -745,6 +679,7 @@ static int wave5_vpu_dec_try_fmt_out(struct file *file, void *fh, struct v4l2_fo
static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
{
struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
int i, ret;
dev_dbg(inst->dev->dev,
@@ -779,7 +714,13 @@ static int wave5_vpu_dec_s_fmt_out(struct file *file, void *fh, struct v4l2_form
inst->quantization = f->fmt.pix_mp.quantization;
inst->xfer_func = f->fmt.pix_mp.xfer_func;
- wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+ vpu_fmt = wave5_find_vpu_fmt(inst->dst_fmt.pixelformat, dec_fmt_list[VPU_FMT_TYPE_RAW]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ wave5_update_pix_fmt(&inst->dst_fmt, VPU_FMT_TYPE_RAW,
+ f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ vpu_fmt->v4l2_frmsize);
return 0;
}
@@ -1002,6 +943,7 @@ static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buff
struct vpu_instance *inst = vb2_get_drv_priv(q);
struct v4l2_pix_format_mplane inst_format =
(q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? inst->src_fmt : inst->dst_fmt;
+ unsigned int i;
dev_dbg(inst->dev->dev, "%s: num_buffers: %u | num_planes: %u | type: %u\n", __func__,
*num_buffers, *num_planes, q->type);
@@ -1015,31 +957,9 @@ static int wave5_vpu_dec_queue_setup(struct vb2_queue *q, unsigned int *num_buff
if (*num_buffers < inst->fbc_buf_count)
*num_buffers = inst->fbc_buf_count;
- if (*num_planes == 1) {
- if (inst->output_format == FORMAT_422)
- sizes[0] = inst_format.width * inst_format.height * 2;
- else
- sizes[0] = inst_format.width * inst_format.height * 3 / 2;
- dev_dbg(inst->dev->dev, "%s: size[0]: %u\n", __func__, sizes[0]);
- } else if (*num_planes == 2) {
- sizes[0] = inst_format.width * inst_format.height;
- if (inst->output_format == FORMAT_422)
- sizes[1] = inst_format.width * inst_format.height;
- else
- sizes[1] = inst_format.width * inst_format.height / 2;
- dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u\n",
- __func__, sizes[0], sizes[1]);
- } else if (*num_planes == 3) {
- sizes[0] = inst_format.width * inst_format.height;
- if (inst->output_format == FORMAT_422) {
- sizes[1] = inst_format.width * inst_format.height / 2;
- sizes[2] = inst_format.width * inst_format.height / 2;
- } else {
- sizes[1] = inst_format.width * inst_format.height / 4;
- sizes[2] = inst_format.width * inst_format.height / 4;
- }
- dev_dbg(inst->dev->dev, "%s: size[0]: %u | size[1]: %u | size[2]: %u\n",
- __func__, sizes[0], sizes[1], sizes[2]);
+ for (i = 0; i < *num_planes; i++) {
+ sizes[i] = inst_format.plane_fmt[i].sizeimage;
+ dev_dbg(inst->dev->dev, "%s: size[%u]: %u\n", __func__, i, sizes[i]);
}
}
@@ -1055,6 +975,22 @@ static int wave5_prepare_fb(struct vpu_instance *inst)
int ret, i;
struct v4l2_m2m_buffer *buf, *n;
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ u32 bitdepth = inst->codec_info->dec_info.initial_info.luma_bitdepth;
+
+ switch (bitdepth) {
+ case 8:
+ break;
+ case 10:
+ if (inst->std == W_HEVC_DEC &&
+ inst->dev->attr.support_hevc10bit_dec)
+ break;
+
+ fallthrough;
+ default:
+ dev_err(inst->dev->dev, "no support for %d bit depth\n", bitdepth);
+
+ return -EINVAL;
+ }
linear_num = v4l2_m2m_num_dst_bufs_ready(m2m_ctx);
non_linear_num = inst->fbc_buf_count;
@@ -1063,7 +999,7 @@ static int wave5_prepare_fb(struct vpu_instance *inst)
struct frame_buffer *frame = &inst->frame_buf[i];
struct vpu_buf *vframe = &inst->frame_vbuf[i];
- fb_stride = inst->dst_fmt.width;
+ fb_stride = ALIGN(inst->dst_fmt.width * bitdepth / 8, 32);
fb_height = ALIGN(inst->dst_fmt.height, 32);
luma_size = fb_stride * fb_height;
@@ -1382,6 +1318,7 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
int ret = 0;
dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
+ pm_runtime_resume_and_get(inst->dev->dev);
v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
@@ -1416,7 +1353,8 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
if (ret)
goto return_buffers;
- if (inst->state == VPU_INST_STATE_INIT_SEQ) {
+ if (inst->state == VPU_INST_STATE_INIT_SEQ &&
+ inst->dev->product_code == WAVE521C_CODE) {
if (initial_info->luma_bitdepth != 8) {
dev_info(inst->dev->dev, "%s: no support for %d bit depth",
__func__, initial_info->luma_bitdepth);
@@ -1424,14 +1362,17 @@ static int wave5_vpu_dec_start_streaming(struct vb2_queue *q, unsigned int count
goto return_buffers;
}
}
- }
+ }
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
return ret;
free_bitstream_vbuf:
wave5_vdi_free_dma_memory(inst->dev, &inst->bitstream_vbuf);
return_buffers:
wave5_return_bufs(q, VB2_BUF_STATE_QUEUED);
+ pm_runtime_put_autosuspend(inst->dev->dev);
return ret;
}
@@ -1442,6 +1383,16 @@ static int streamoff_output(struct vb2_queue *q)
struct vb2_v4l2_buffer *buf;
int ret;
dma_addr_t new_rd_ptr;
+ struct dec_output_info dec_info;
+ unsigned int i;
+
+ for (i = 0; i < v4l2_m2m_num_dst_bufs_ready(m2m_ctx); i++) {
+ ret = wave5_vpu_dec_set_disp_flag(inst, i);
+ if (ret)
+ dev_dbg(inst->dev->dev,
+ "%s: Setting display flag of buf index: %u, fail: %d\n",
+ __func__, i, ret);
+ }
while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx))) {
dev_dbg(inst->dev->dev, "%s: (Multiplanar) buf type %4u | index %4u\n",
@@ -1449,6 +1400,11 @@ static int streamoff_output(struct vb2_queue *q)
v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR);
}
+ while (wave5_vpu_dec_get_output_info(inst, &dec_info) == 0) {
+ if (dec_info.index_frame_display >= 0)
+ wave5_vpu_dec_set_disp_flag(inst, dec_info.index_frame_display);
+ }
+
ret = wave5_vpu_flush_instance(inst);
if (ret)
return ret;
@@ -1517,6 +1473,7 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
bool check_cmd = TRUE;
dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
+ pm_runtime_resume_and_get(inst->dev->dev);
while (check_cmd) {
struct queue_status_info q_status;
@@ -1531,7 +1488,7 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
break;
if (wave5_vpu_dec_get_output_info(inst, &dec_output_info))
- dev_dbg(inst->dev->dev, "Getting decoding results from fw, fail\n");
+ dev_dbg(inst->dev->dev, "there is no output info\n");
}
v4l2_m2m_update_stop_streaming_state(m2m_ctx, q);
@@ -1540,12 +1497,13 @@ static void wave5_vpu_dec_stop_streaming(struct vb2_queue *q)
streamoff_output(q);
else
streamoff_capture(q);
+
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
}
static const struct vb2_ops wave5_vpu_dec_vb2_ops = {
.queue_setup = wave5_vpu_dec_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_queue = wave5_vpu_dec_buf_queue,
.start_streaming = wave5_vpu_dec_start_streaming,
.stop_streaming = wave5_vpu_dec_stop_streaming,
@@ -1554,20 +1512,15 @@ static const struct vb2_ops wave5_vpu_dec_vb2_ops = {
static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
struct v4l2_pix_format_mplane *dst_fmt)
{
- unsigned int dst_pix_fmt = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
- const struct v4l2_format_info *dst_fmt_info = v4l2_format_info(dst_pix_fmt);
-
src_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
- src_fmt->field = V4L2_FIELD_NONE;
- src_fmt->flags = 0;
- src_fmt->num_planes = 1;
- wave5_update_pix_fmt(src_fmt, 720, 480);
-
- dst_fmt->pixelformat = dst_pix_fmt;
- dst_fmt->field = V4L2_FIELD_NONE;
- dst_fmt->flags = 0;
- dst_fmt->num_planes = dst_fmt_info->mem_planes;
- wave5_update_pix_fmt(dst_fmt, 736, 480);
+ wave5_update_pix_fmt(src_fmt, VPU_FMT_TYPE_CODEC,
+ W5_DEF_DEC_PIC_WIDTH, W5_DEF_DEC_PIC_HEIGHT,
+ &dec_hevc_frmsize);
+
+ dst_fmt->pixelformat = dec_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
+ wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_RAW,
+ W5_DEF_DEC_PIC_WIDTH, W5_DEF_DEC_PIC_HEIGHT,
+ &dec_raw_frmsize);
}
static int wave5_vpu_dec_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
@@ -1626,7 +1579,7 @@ static void wave5_vpu_dec_device_run(void *priv)
int ret = 0;
dev_dbg(inst->dev->dev, "%s: Fill the ring buffer with new bitstream data", __func__);
-
+ pm_runtime_resume_and_get(inst->dev->dev);
ret = fill_ringbuffer(inst);
if (ret) {
dev_warn(inst->dev->dev, "Filling ring buffer failed\n");
@@ -1709,6 +1662,8 @@ static void wave5_vpu_dec_device_run(void *priv)
finish_job_and_return:
dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
@@ -1810,7 +1765,6 @@ static int wave5_vpu_open_dec(struct file *filp)
v4l2_fh_add(&inst->v4l2_fh);
INIT_LIST_HEAD(&inst->list);
- list_add_tail(&inst->list, &dev->instances);
inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_dec_dev;
inst->v4l2_fh.m2m_ctx =
@@ -1865,7 +1819,23 @@ static int wave5_vpu_open_dec(struct file *filp)
goto cleanup_inst;
}
- wave5_vdi_allocate_sram(inst->dev);
+ /*
+ * For Wave515 SRAM memory was already allocated
+ * at wave5_vpu_dec_register_device()
+ */
+ if (inst->dev->product_code != WAVE515_CODE)
+ wave5_vdi_allocate_sram(inst->dev);
+
+ ret = mutex_lock_interruptible(&dev->dev_lock);
+ if (ret)
+ goto cleanup_inst;
+
+ if (list_empty(&dev->instances))
+ pm_runtime_use_autosuspend(inst->dev->dev);
+
+ list_add_tail(&inst->list, &dev->instances);
+
+ mutex_unlock(&dev->dev_lock);
return 0;
@@ -1893,6 +1863,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev)
struct video_device *vdev_dec;
int ret;
+ /*
+ * Secondary AXI setup for Wave515 is done by INIT_VPU command,
+ * i.e. wave5_vpu_init(), that's why we allocate SRAM memory early.
+ */
+ if (dev->product_code == WAVE515_CODE)
+ wave5_vdi_allocate_sram(dev);
+
vdev_dec = devm_kzalloc(dev->v4l2_dev.dev, sizeof(*vdev_dec), GFP_KERNEL);
if (!vdev_dec)
return -ENOMEM;
@@ -1926,6 +1903,13 @@ int wave5_vpu_dec_register_device(struct vpu_device *dev)
void wave5_vpu_dec_unregister_device(struct vpu_device *dev)
{
+ /*
+ * Here is a freeing pair for Wave515 SRAM memory allocation
+ * happened at wave5_vpu_dec_register_device().
+ */
+ if (dev->product_code == WAVE515_CODE)
+ wave5_vdi_free_sram(dev);
+
video_unregister_device(dev->video_dev_dec);
if (dev->v4l2_m2m_dec_dev)
v4l2_m2m_release(dev->v4l2_m2m_dec_dev);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
index 8bbf9d10b467..1e5fc5f8b856 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
@@ -5,70 +5,90 @@
* Copyright (C) 2021-2023 CHIPS&MEDIA INC
*/
+#include <linux/pm_runtime.h>
#include "wave5-helper.h"
#define VPU_ENC_DEV_NAME "C&M Wave5 VPU encoder"
#define VPU_ENC_DRV_NAME "wave5-enc"
+static const struct v4l2_frmsize_stepwise enc_frmsize[FMT_TYPES] = {
+ [VPU_FMT_TYPE_CODEC] = {
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .step_width = W5_ENC_CODEC_STEP_WIDTH,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .step_height = W5_ENC_CODEC_STEP_HEIGHT,
+ },
+ [VPU_FMT_TYPE_RAW] = {
+ .min_width = W5_MIN_ENC_PIC_WIDTH,
+ .max_width = W5_MAX_ENC_PIC_WIDTH,
+ .step_width = W5_ENC_RAW_STEP_WIDTH,
+ .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .max_height = W5_MAX_ENC_PIC_HEIGHT,
+ .step_height = W5_ENC_RAW_STEP_HEIGHT,
+ },
+};
+
static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = {
[VPU_FMT_TYPE_CODEC] = {
{
.v4l2_pix_fmt = V4L2_PIX_FMT_HEVC,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_CODEC],
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_H264,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_CODEC],
},
},
[VPU_FMT_TYPE_RAW] = {
{
.v4l2_pix_fmt = V4L2_PIX_FMT_YUV420,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV12,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV21,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_YUV420M,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV12M,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
},
{
.v4l2_pix_fmt = V4L2_PIX_FMT_NV21M,
- .max_width = W5_MAX_ENC_PIC_WIDTH,
- .min_width = W5_MIN_ENC_PIC_WIDTH,
- .max_height = W5_MAX_ENC_PIC_HEIGHT,
- .min_height = W5_MIN_ENC_PIC_HEIGHT,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422P,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV16,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV61,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUV422M,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV16M,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_NV61M,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
},
}
};
@@ -105,46 +125,6 @@ invalid_state_switch:
return -EINVAL;
}
-static void wave5_update_pix_fmt(struct v4l2_pix_format_mplane *pix_mp, unsigned int width,
- unsigned int height)
-{
- switch (pix_mp->pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- pix_mp->width = width;
- pix_mp->height = height;
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height * 3 / 2;
- break;
- case V4L2_PIX_FMT_YUV420M:
- pix_mp->width = width;
- pix_mp->height = height;
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
- pix_mp->plane_fmt[1].bytesperline = round_up(width, 32) / 2;
- pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 4;
- pix_mp->plane_fmt[2].bytesperline = round_up(width, 32) / 2;
- pix_mp->plane_fmt[2].sizeimage = round_up(width, 32) * height / 4;
- break;
- case V4L2_PIX_FMT_NV12M:
- case V4L2_PIX_FMT_NV21M:
- pix_mp->width = width;
- pix_mp->height = height;
- pix_mp->plane_fmt[0].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[0].sizeimage = round_up(width, 32) * height;
- pix_mp->plane_fmt[1].bytesperline = round_up(width, 32);
- pix_mp->plane_fmt[1].sizeimage = round_up(width, 32) * height / 2;
- break;
- default:
- pix_mp->width = width;
- pix_mp->height = height;
- pix_mp->plane_fmt[0].bytesperline = 0;
- pix_mp->plane_fmt[0].sizeimage = width * height / 8 * 3;
- break;
- }
-}
-
static int start_encode(struct vpu_instance *inst, u32 *fail_res)
{
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
@@ -153,13 +133,26 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res)
struct vb2_v4l2_buffer *dst_buf;
struct frame_buffer frame_buf;
struct enc_param pic_param;
- u32 stride = ALIGN(inst->dst_fmt.width, 32);
- u32 luma_size = (stride * inst->dst_fmt.height);
- u32 chroma_size = ((stride / 2) * (inst->dst_fmt.height / 2));
+ const struct v4l2_format_info *info;
+ u32 stride = inst->src_fmt.plane_fmt[0].bytesperline;
+ u32 luma_size = 0;
+ u32 chroma_size = 0;
memset(&pic_param, 0, sizeof(struct enc_param));
memset(&frame_buf, 0, sizeof(struct frame_buffer));
+ info = v4l2_format_info(inst->src_fmt.pixelformat);
+ if (!info)
+ return -EINVAL;
+
+ if (info->mem_planes == 1) {
+ luma_size = stride * inst->dst_fmt.height;
+ chroma_size = luma_size / (info->hdiv * info->vdiv);
+ } else {
+ luma_size = inst->src_fmt.plane_fmt[0].sizeimage;
+ chroma_size = inst->src_fmt.plane_fmt[1].sizeimage;
+ }
+
dst_buf = v4l2_m2m_next_dst_buf(m2m_ctx);
if (!dst_buf) {
dev_dbg(inst->dev->dev, "%s: No destination buffer found\n", __func__);
@@ -359,13 +352,8 @@ static int wave5_vpu_enc_enum_framesizes(struct file *f, void *fh, struct v4l2_f
return -EINVAL;
}
- fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
- fsize->stepwise.min_width = vpu_fmt->min_width;
- fsize->stepwise.max_width = vpu_fmt->max_width;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.min_height = vpu_fmt->min_height;
- fsize->stepwise.max_height = vpu_fmt->max_height;
- fsize->stepwise.step_height = 1;
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise = enc_frmsize[VPU_FMT_TYPE_CODEC];
return 0;
}
@@ -390,7 +378,9 @@ static int wave5_vpu_enc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_f
static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
{
struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct v4l2_frmsize_stepwise *frmsize;
const struct vpu_format *vpu_fmt;
+ int width, height;
dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
__func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
@@ -398,20 +388,19 @@ static int wave5_vpu_enc_try_fmt_cap(struct file *file, void *fh, struct v4l2_fo
vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
if (!vpu_fmt) {
+ width = inst->dst_fmt.width;
+ height = inst->dst_fmt.height;
f->fmt.pix_mp.pixelformat = inst->dst_fmt.pixelformat;
- f->fmt.pix_mp.num_planes = inst->dst_fmt.num_planes;
- wave5_update_pix_fmt(&f->fmt.pix_mp, inst->dst_fmt.width, inst->dst_fmt.height);
+ frmsize = &enc_frmsize[VPU_FMT_TYPE_CODEC];
} else {
- int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
- int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
-
+ width = f->fmt.pix_mp.width;
+ height = f->fmt.pix_mp.height;
f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
- f->fmt.pix_mp.num_planes = 1;
- wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
+ frmsize = vpu_fmt->v4l2_frmsize;
}
- f->fmt.pix_mp.flags = 0;
- f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_CODEC,
+ width, height, frmsize);
f->fmt.pix_mp.colorspace = inst->colorspace;
f->fmt.pix_mp.ycbcr_enc = inst->ycbcr_enc;
f->fmt.pix_mp.quantization = inst->quantization;
@@ -498,7 +487,9 @@ static int wave5_vpu_enc_enum_fmt_out(struct file *file, void *fh, struct v4l2_f
static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
{
struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct v4l2_frmsize_stepwise *frmsize;
const struct vpu_format *vpu_fmt;
+ int width, height;
dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
__func__, f->fmt.pix_mp.pixelformat, f->fmt.pix_mp.width, f->fmt.pix_mp.height,
@@ -506,28 +497,27 @@ static int wave5_vpu_enc_try_fmt_out(struct file *file, void *fh, struct v4l2_fo
vpu_fmt = wave5_find_vpu_fmt(f->fmt.pix_mp.pixelformat, enc_fmt_list[VPU_FMT_TYPE_RAW]);
if (!vpu_fmt) {
+ width = inst->src_fmt.width;
+ height = inst->src_fmt.height;
f->fmt.pix_mp.pixelformat = inst->src_fmt.pixelformat;
- f->fmt.pix_mp.num_planes = inst->src_fmt.num_planes;
- wave5_update_pix_fmt(&f->fmt.pix_mp, inst->src_fmt.width, inst->src_fmt.height);
+ frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW];
} else {
- int width = clamp(f->fmt.pix_mp.width, vpu_fmt->min_width, vpu_fmt->max_width);
- int height = clamp(f->fmt.pix_mp.height, vpu_fmt->min_height, vpu_fmt->max_height);
- const struct v4l2_format_info *info = v4l2_format_info(vpu_fmt->v4l2_pix_fmt);
-
+ width = f->fmt.pix_mp.width;
+ height = f->fmt.pix_mp.height;
f->fmt.pix_mp.pixelformat = vpu_fmt->v4l2_pix_fmt;
- f->fmt.pix_mp.num_planes = info->mem_planes;
- wave5_update_pix_fmt(&f->fmt.pix_mp, width, height);
+ frmsize = vpu_fmt->v4l2_frmsize;
}
- f->fmt.pix_mp.flags = 0;
- f->fmt.pix_mp.field = V4L2_FIELD_NONE;
-
+ wave5_update_pix_fmt(&f->fmt.pix_mp, VPU_FMT_TYPE_RAW,
+ width, height, frmsize);
return 0;
}
static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_format *f)
{
struct vpu_instance *inst = wave5_to_vpu_inst(fh);
+ const struct vpu_format *vpu_fmt;
+ const struct v4l2_format_info *info;
int i, ret;
dev_dbg(inst->dev->dev, "%s: fourcc: %u width: %u height: %u num_planes: %u field: %u\n",
@@ -549,16 +539,20 @@ static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_form
inst->src_fmt.plane_fmt[i].sizeimage = f->fmt.pix_mp.plane_fmt[i].sizeimage;
}
- if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12 ||
- inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV12M) {
- inst->cbcr_interleave = true;
- inst->nv21 = false;
- } else if (inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21 ||
- inst->src_fmt.pixelformat == V4L2_PIX_FMT_NV21M) {
- inst->cbcr_interleave = true;
+ info = v4l2_format_info(inst->src_fmt.pixelformat);
+ if (!info)
+ return -EINVAL;
+
+ inst->cbcr_interleave = (info->comp_planes == 2) ? true : false;
+
+ switch (inst->src_fmt.pixelformat) {
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV21M:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_NV61M:
inst->nv21 = true;
- } else {
- inst->cbcr_interleave = false;
+ break;
+ default:
inst->nv21 = false;
}
@@ -567,7 +561,15 @@ static int wave5_vpu_enc_s_fmt_out(struct file *file, void *fh, struct v4l2_form
inst->quantization = f->fmt.pix_mp.quantization;
inst->xfer_func = f->fmt.pix_mp.xfer_func;
- wave5_update_pix_fmt(&inst->dst_fmt, f->fmt.pix_mp.width, f->fmt.pix_mp.height);
+ vpu_fmt = wave5_find_vpu_fmt(inst->dst_fmt.pixelformat, enc_fmt_list[VPU_FMT_TYPE_CODEC]);
+ if (!vpu_fmt)
+ return -EINVAL;
+
+ wave5_update_pix_fmt(&inst->dst_fmt, VPU_FMT_TYPE_CODEC,
+ f->fmt.pix_mp.width, f->fmt.pix_mp.height,
+ vpu_fmt->v4l2_frmsize);
+ inst->conf_win.width = inst->dst_fmt.width;
+ inst->conf_win.height = inst->dst_fmt.height;
return 0;
}
@@ -583,12 +585,17 @@ static int wave5_vpu_enc_g_selection(struct file *file, void *fh, struct v4l2_se
switch (s->target) {
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
- case V4L2_SEL_TGT_CROP:
s->r.left = 0;
s->r.top = 0;
s->r.width = inst->dst_fmt.width;
s->r.height = inst->dst_fmt.height;
break;
+ case V4L2_SEL_TGT_CROP:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = inst->conf_win.width;
+ s->r.height = inst->conf_win.height;
+ break;
default:
return -EINVAL;
}
@@ -611,8 +618,10 @@ static int wave5_vpu_enc_s_selection(struct file *file, void *fh, struct v4l2_se
s->r.left = 0;
s->r.top = 0;
- s->r.width = inst->src_fmt.width;
- s->r.height = inst->src_fmt.height;
+ s->r.width = min(s->r.width, inst->dst_fmt.width);
+ s->r.height = min(s->r.height, inst->dst_fmt.height);
+
+ inst->conf_win = s->r;
return 0;
}
@@ -1061,6 +1070,9 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
inst->enc_param.entropy_coding_mode = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR:
+ inst->enc_param.forced_idr_header_enable = ctrl->val;
+ break;
case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT:
break;
default:
@@ -1125,13 +1137,23 @@ static void wave5_vpu_enc_buf_queue(struct vb2_buffer *vb)
v4l2_m2m_buf_queue(m2m_ctx, vbuf);
}
-static void wave5_set_enc_openparam(struct enc_open_param *open_param,
- struct vpu_instance *inst)
+static int wave5_set_enc_openparam(struct enc_open_param *open_param,
+ struct vpu_instance *inst)
{
struct enc_wave_param input = inst->enc_param;
+ const struct v4l2_format_info *info;
u32 num_ctu_row = ALIGN(inst->dst_fmt.height, 64) / 64;
u32 num_mb_row = ALIGN(inst->dst_fmt.height, 16) / 16;
+ info = v4l2_format_info(inst->src_fmt.pixelformat);
+ if (!info)
+ return -EINVAL;
+
+ if (info->hdiv == 2 && info->vdiv == 1)
+ open_param->src_format = FORMAT_422;
+ else
+ open_param->src_format = FORMAT_420;
+
open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE;
open_param->wave_param.hvs_qp_scale = 2;
open_param->wave_param.hvs_max_delta_qp = 10;
@@ -1147,8 +1169,8 @@ static void wave5_set_enc_openparam(struct enc_open_param *open_param,
open_param->wave_param.lambda_scaling_enable = 1;
open_param->line_buf_int_en = true;
- open_param->pic_width = inst->dst_fmt.width;
- open_param->pic_height = inst->dst_fmt.height;
+ open_param->pic_width = inst->conf_win.width;
+ open_param->pic_height = inst->conf_win.height;
open_param->frame_rate_info = inst->frame_rate;
open_param->rc_enable = inst->rc_enable;
if (inst->rc_enable) {
@@ -1219,6 +1241,9 @@ static void wave5_set_enc_openparam(struct enc_open_param *open_param,
else
open_param->wave_param.intra_refresh_arg = num_ctu_row;
}
+ open_param->wave_param.forced_idr_header_enable = input.forced_idr_header_enable;
+
+ return 0;
}
static int initialize_sequence(struct vpu_instance *inst)
@@ -1247,7 +1272,7 @@ static int initialize_sequence(struct vpu_instance *inst)
__func__, initial_info.min_frame_buffer_count,
initial_info.min_src_frame_count);
inst->min_src_buf_count = initial_info.min_src_frame_count +
- COMMAND_QUEUE_DEPTH;
+ WAVE521_COMMAND_QUEUE_DEPTH;
ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
V4L2_CID_MIN_BUFFERS_FOR_OUTPUT);
@@ -1306,6 +1331,7 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count
struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
int ret = 0;
+ pm_runtime_resume_and_get(inst->dev->dev);
v4l2_m2m_update_start_streaming_state(m2m_ctx, q);
if (inst->state == VPU_INST_STATE_NONE && q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
@@ -1313,7 +1339,12 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count
memset(&open_param, 0, sizeof(struct enc_open_param));
- wave5_set_enc_openparam(&open_param, inst);
+ ret = wave5_set_enc_openparam(&open_param, inst);
+ if (ret) {
+ dev_dbg(inst->dev->dev, "%s: wave5_set_enc_openparam, fail: %d\n",
+ __func__, ret);
+ goto return_buffers;
+ }
ret = wave5_vpu_enc_open(inst, &open_param);
if (ret) {
@@ -1360,9 +1391,13 @@ static int wave5_vpu_enc_start_streaming(struct vb2_queue *q, unsigned int count
if (ret)
goto return_buffers;
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
return 0;
return_buffers:
wave5_return_bufs(q, VB2_BUF_STATE_QUEUED);
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
return ret;
}
@@ -1404,6 +1439,7 @@ static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q)
*/
dev_dbg(inst->dev->dev, "%s: type: %u\n", __func__, q->type);
+ pm_runtime_resume_and_get(inst->dev->dev);
if (wave5_vpu_both_queues_are_streaming(inst))
switch_state(inst, VPU_INST_STATE_STOP);
@@ -1428,12 +1464,13 @@ static void wave5_vpu_enc_stop_streaming(struct vb2_queue *q)
streamoff_output(inst, q);
else
streamoff_capture(inst, q);
+
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
}
static const struct vb2_ops wave5_vpu_enc_vb2_ops = {
.queue_setup = wave5_vpu_enc_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_queue = wave5_vpu_enc_buf_queue,
.start_streaming = wave5_vpu_enc_start_streaming,
.stop_streaming = wave5_vpu_enc_stop_streaming,
@@ -1442,20 +1479,15 @@ static const struct vb2_ops wave5_vpu_enc_vb2_ops = {
static void wave5_set_default_format(struct v4l2_pix_format_mplane *src_fmt,
struct v4l2_pix_format_mplane *dst_fmt)
{
- unsigned int src_pix_fmt = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
- const struct v4l2_format_info *src_fmt_info = v4l2_format_info(src_pix_fmt);
-
- src_fmt->pixelformat = src_pix_fmt;
- src_fmt->field = V4L2_FIELD_NONE;
- src_fmt->flags = 0;
- src_fmt->num_planes = src_fmt_info->mem_planes;
- wave5_update_pix_fmt(src_fmt, 416, 240);
+ src_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_RAW][0].v4l2_pix_fmt;
+ wave5_update_pix_fmt(src_fmt, VPU_FMT_TYPE_RAW,
+ W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT,
+ &enc_frmsize[VPU_FMT_TYPE_RAW]);
dst_fmt->pixelformat = enc_fmt_list[VPU_FMT_TYPE_CODEC][0].v4l2_pix_fmt;
- dst_fmt->field = V4L2_FIELD_NONE;
- dst_fmt->flags = 0;
- dst_fmt->num_planes = 1;
- wave5_update_pix_fmt(dst_fmt, 416, 240);
+ wave5_update_pix_fmt(dst_fmt, VPU_FMT_TYPE_CODEC,
+ W5_DEF_ENC_PIC_WIDTH, W5_DEF_ENC_PIC_HEIGHT,
+ &enc_frmsize[VPU_FMT_TYPE_CODEC]);
}
static int wave5_vpu_enc_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
@@ -1474,6 +1506,7 @@ static void wave5_vpu_enc_device_run(void *priv)
u32 fail_res = 0;
int ret = 0;
+ pm_runtime_resume_and_get(inst->dev->dev);
switch (inst->state) {
case VPU_INST_STATE_PIC_RUN:
ret = start_encode(inst, &fail_res);
@@ -1487,6 +1520,8 @@ static void wave5_vpu_enc_device_run(void *priv)
break;
}
dev_dbg(inst->dev->dev, "%s: leave with active job", __func__);
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
return;
default:
WARN(1, "Execution of a job in state %s is invalid.\n",
@@ -1494,6 +1529,8 @@ static void wave5_vpu_enc_device_run(void *priv)
break;
}
dev_dbg(inst->dev->dev, "%s: leave and finish job", __func__);
+ pm_runtime_mark_last_busy(inst->dev->dev);
+ pm_runtime_put_autosuspend(inst->dev->dev);
v4l2_m2m_job_finish(inst->v4l2_m2m_dev, m2m_ctx);
}
@@ -1554,7 +1591,6 @@ static int wave5_vpu_open_enc(struct file *filp)
v4l2_fh_add(&inst->v4l2_fh);
INIT_LIST_HEAD(&inst->list);
- list_add_tail(&inst->list, &dev->instances);
inst->v4l2_m2m_dev = inst->dev->v4l2_m2m_enc_dev;
inst->v4l2_fh.m2m_ctx =
@@ -1702,6 +1738,9 @@ static int wave5_vpu_open_enc(struct file *filp)
0, 1, 1, 0);
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
V4L2_CID_MIN_BUFFERS_FOR_OUTPUT, 1, 32, 1, 1);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR,
+ 0, 1, 1, 0);
if (v4l2_ctrl_hdl->error) {
ret = -ENODEV;
@@ -1712,6 +1751,8 @@ static int wave5_vpu_open_enc(struct file *filp)
v4l2_ctrl_handler_setup(v4l2_ctrl_hdl);
wave5_set_default_format(&inst->src_fmt, &inst->dst_fmt);
+ inst->conf_win.width = inst->dst_fmt.width;
+ inst->conf_win.height = inst->dst_fmt.height;
inst->colorspace = V4L2_COLORSPACE_REC709;
inst->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
inst->quantization = V4L2_QUANTIZATION_DEFAULT;
@@ -1729,6 +1770,17 @@ static int wave5_vpu_open_enc(struct file *filp)
wave5_vdi_allocate_sram(inst->dev);
+ ret = mutex_lock_interruptible(&dev->dev_lock);
+ if (ret)
+ goto cleanup_inst;
+
+ if (list_empty(&dev->instances))
+ pm_runtime_use_autosuspend(inst->dev->dev);
+
+ list_add_tail(&inst->list, &dev->instances);
+
+ mutex_unlock(&dev->dev_lock);
+
return 0;
cleanup_inst:
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.c b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
index 1b3df5b04249..e1715d3f43b0 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.c
@@ -10,6 +10,8 @@
#include <linux/clk.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include "wave5-vpu.h"
#include "wave5-regdefine.h"
#include "wave5-vpuconfig.h"
@@ -24,8 +26,12 @@
struct wave5_match_data {
int flags;
const char *fw_name;
+ u32 sram_size;
};
+static int vpu_poll_interval = 5;
+module_param(vpu_poll_interval, int, 0644);
+
int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
{
int ret;
@@ -40,7 +46,7 @@ int wave5_vpu_wait_interrupt(struct vpu_instance *inst, unsigned int timeout)
return 0;
}
-static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
+static void wave5_vpu_handle_irq(void *dev_id)
{
u32 seq_done;
u32 cmd_done;
@@ -48,42 +54,73 @@ static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
struct vpu_instance *inst;
struct vpu_device *dev = dev_id;
- if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS)) {
- irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
- wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason);
- wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
-
- list_for_each_entry(inst, &dev->instances, list) {
- seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
- cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
-
- if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
- irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) {
- if (seq_done & BIT(inst->id)) {
- seq_done &= ~BIT(inst->id);
- wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO,
- seq_done);
- complete(&inst->irq_done);
- }
+ irq_reason = wave5_vdi_read_register(dev, W5_VPU_VINT_REASON);
+ seq_done = wave5_vdi_read_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO);
+ cmd_done = wave5_vdi_read_register(dev, W5_RET_QUEUE_CMD_DONE_INST);
+ wave5_vdi_write_register(dev, W5_VPU_VINT_REASON_CLR, irq_reason);
+ wave5_vdi_write_register(dev, W5_VPU_VINT_CLEAR, 0x1);
+
+ list_for_each_entry(inst, &dev->instances, list) {
+
+ if (irq_reason & BIT(INT_WAVE5_INIT_SEQ) ||
+ irq_reason & BIT(INT_WAVE5_ENC_SET_PARAM)) {
+ if (dev->product_code == WAVE515_CODE &&
+ (cmd_done & BIT(inst->id))) {
+ cmd_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST,
+ cmd_done);
+ complete(&inst->irq_done);
+ } else if (seq_done & BIT(inst->id)) {
+ seq_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_SEQ_DONE_INSTANCE_INFO,
+ seq_done);
+ complete(&inst->irq_done);
}
+ }
- if (irq_reason & BIT(INT_WAVE5_DEC_PIC) ||
- irq_reason & BIT(INT_WAVE5_ENC_PIC)) {
- if (cmd_done & BIT(inst->id)) {
- cmd_done &= ~BIT(inst->id);
- wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST,
- cmd_done);
- inst->ops->finish_process(inst);
- }
+ if (irq_reason & BIT(INT_WAVE5_DEC_PIC) ||
+ irq_reason & BIT(INT_WAVE5_ENC_PIC)) {
+ if (cmd_done & BIT(inst->id)) {
+ cmd_done &= ~BIT(inst->id);
+ wave5_vdi_write_register(dev, W5_RET_QUEUE_CMD_DONE_INST,
+ cmd_done);
+ inst->ops->finish_process(inst);
}
-
- wave5_vpu_clear_interrupt(inst, irq_reason);
}
+
+ wave5_vpu_clear_interrupt(inst, irq_reason);
}
+}
+
+static irqreturn_t wave5_vpu_irq_thread(int irq, void *dev_id)
+{
+ struct vpu_device *dev = dev_id;
+
+ if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS))
+ wave5_vpu_handle_irq(dev);
return IRQ_HANDLED;
}
+static void wave5_vpu_irq_work_fn(struct kthread_work *work)
+{
+ struct vpu_device *dev = container_of(work, struct vpu_device, work);
+
+ if (wave5_vdi_read_register(dev, W5_VPU_VPU_INT_STS))
+ wave5_vpu_handle_irq(dev);
+}
+
+static enum hrtimer_restart wave5_vpu_timer_callback(struct hrtimer *timer)
+{
+ struct vpu_device *dev =
+ container_of(timer, struct vpu_device, hrtimer);
+
+ kthread_queue_work(dev->worker, &dev->work);
+ hrtimer_forward_now(timer, ns_to_ktime(vpu_poll_interval * NSEC_PER_MSEC));
+
+ return HRTIMER_RESTART;
+}
+
static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
u32 *revision)
{
@@ -117,6 +154,45 @@ static int wave5_vpu_load_firmware(struct device *dev, const char *fw_name,
return 0;
}
+static __maybe_unused int wave5_pm_suspend(struct device *dev)
+{
+ struct vpu_device *vpu = dev_get_drvdata(dev);
+
+ if (pm_runtime_suspended(dev))
+ return 0;
+
+ if (vpu->irq < 0)
+ hrtimer_cancel(&vpu->hrtimer);
+
+ wave5_vpu_sleep_wake(dev, true, NULL, 0);
+ clk_bulk_disable_unprepare(vpu->num_clks, vpu->clks);
+
+ return 0;
+}
+
+static __maybe_unused int wave5_pm_resume(struct device *dev)
+{
+ struct vpu_device *vpu = dev_get_drvdata(dev);
+ int ret = 0;
+
+ wave5_vpu_sleep_wake(dev, false, NULL, 0);
+ ret = clk_bulk_prepare_enable(vpu->num_clks, vpu->clks);
+ if (ret) {
+ dev_err(dev, "Enabling clocks, fail: %d\n", ret);
+ return ret;
+ }
+
+ if (vpu->irq < 0 && !hrtimer_active(&vpu->hrtimer))
+ hrtimer_start(&vpu->hrtimer, ns_to_ktime(vpu->vpu_poll_interval * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL_PINNED);
+
+ return ret;
+}
+
+static const struct dev_pm_ops wave5_pm_ops = {
+ SET_RUNTIME_PM_OPS(wave5_pm_suspend, wave5_pm_resume, NULL)
+};
+
static int wave5_vpu_probe(struct platform_device *pdev)
{
int ret;
@@ -151,6 +227,16 @@ static int wave5_vpu_probe(struct platform_device *pdev)
dev_set_drvdata(&pdev->dev, dev);
dev->dev = &pdev->dev;
+ dev->resets = devm_reset_control_array_get_optional_exclusive(&pdev->dev);
+ if (IS_ERR(dev->resets)) {
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->resets),
+ "Failed to get reset control\n");
+ }
+
+ ret = reset_control_deassert(dev->resets);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to deassert resets\n");
+
ret = devm_clk_bulk_get_all(&pdev->dev, &dev->clks);
/* continue without clock, assume externally managed */
@@ -163,20 +249,15 @@ static int wave5_vpu_probe(struct platform_device *pdev)
ret = clk_bulk_prepare_enable(dev->num_clks, dev->clks);
if (ret) {
dev_err(&pdev->dev, "Enabling clocks, fail: %d\n", ret);
- return ret;
- }
-
- ret = of_property_read_u32(pdev->dev.of_node, "sram-size",
- &dev->sram_size);
- if (ret) {
- dev_warn(&pdev->dev, "sram-size not found\n");
- dev->sram_size = 0;
+ goto err_reset_assert;
}
dev->sram_pool = of_gen_pool_get(pdev->dev.of_node, "sram", 0);
if (!dev->sram_pool)
dev_warn(&pdev->dev, "sram node not found\n");
+ dev->sram_size = match_data->sram_size;
+
dev->product_code = wave5_vdi_read_register(dev, VPU_PRODUCT_CODE_REGISTER);
ret = wave5_vdi_init(&pdev->dev);
if (ret < 0) {
@@ -185,6 +266,28 @@ static int wave5_vpu_probe(struct platform_device *pdev)
}
dev->product = wave5_vpu_get_product_id(dev);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (dev->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq resource, falling back to polling\n");
+ hrtimer_setup(&dev->hrtimer, &wave5_vpu_timer_callback, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL_PINNED);
+ dev->worker = kthread_run_worker(0, "vpu_irq_thread");
+ if (IS_ERR(dev->worker)) {
+ dev_err(&pdev->dev, "failed to create vpu irq worker\n");
+ ret = PTR_ERR(dev->worker);
+ goto err_vdi_release;
+ }
+ dev->vpu_poll_interval = vpu_poll_interval;
+ kthread_init_work(&dev->work, wave5_vpu_irq_work_fn);
+ } else {
+ ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL,
+ wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
+ goto err_enc_unreg;
+ }
+ }
+
INIT_LIST_HEAD(&dev->instances);
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) {
@@ -207,20 +310,6 @@ static int wave5_vpu_probe(struct platform_device *pdev)
}
}
- dev->irq = platform_get_irq(pdev, 0);
- if (dev->irq < 0) {
- dev_err(&pdev->dev, "failed to get irq resource\n");
- ret = -ENXIO;
- goto err_enc_unreg;
- }
-
- ret = devm_request_threaded_irq(&pdev->dev, dev->irq, NULL,
- wave5_vpu_irq_thread, IRQF_ONESHOT, "vpu_irq", dev);
- if (ret) {
- dev_err(&pdev->dev, "Register interrupt handler, fail: %d\n", ret);
- goto err_enc_unreg;
- }
-
ret = wave5_vpu_load_firmware(&pdev->dev, match_data->fw_name, &fw_revision);
if (ret) {
dev_err(&pdev->dev, "wave5_vpu_load_firmware, fail: %d\n", ret);
@@ -232,6 +321,12 @@ static int wave5_vpu_probe(struct platform_device *pdev)
(match_data->flags & WAVE5_IS_DEC) ? "'DECODE'" : "");
dev_info(&pdev->dev, "Product Code: 0x%x\n", dev->product_code);
dev_info(&pdev->dev, "Firmware Revision: %u\n", fw_revision);
+
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ wave5_vpu_sleep_wake(&pdev->dev, true, NULL, 0);
+
return 0;
err_enc_unreg:
@@ -246,6 +341,8 @@ err_vdi_release:
wave5_vdi_release(&pdev->dev);
err_clk_dis:
clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
+err_reset_assert:
+ reset_control_assert(dev->resets);
return ret;
}
@@ -254,8 +351,17 @@ static void wave5_vpu_remove(struct platform_device *pdev)
{
struct vpu_device *dev = dev_get_drvdata(&pdev->dev);
+ if (dev->irq < 0) {
+ kthread_destroy_worker(dev->worker);
+ hrtimer_cancel(&dev->hrtimer);
+ }
+
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
mutex_destroy(&dev->dev_lock);
mutex_destroy(&dev->hw_lock);
+ reset_control_assert(dev->resets);
clk_bulk_disable_unprepare(dev->num_clks, dev->clks);
wave5_vpu_enc_unregister_device(dev);
wave5_vpu_dec_unregister_device(dev);
@@ -267,6 +373,7 @@ static void wave5_vpu_remove(struct platform_device *pdev)
static const struct wave5_match_data ti_wave521c_data = {
.flags = WAVE5_IS_ENC | WAVE5_IS_DEC,
.fw_name = "cnm/wave521c_k3_codec_fw.bin",
+ .sram_size = (64 * 1024),
};
static const struct of_device_id wave5_dt_ids[] = {
@@ -279,9 +386,10 @@ static struct platform_driver wave5_vpu_driver = {
.driver = {
.name = VPU_PLATFORM_DEVICE_NAME,
.of_match_table = of_match_ptr(wave5_dt_ids),
+ .pm = &wave5_pm_ops,
},
.probe = wave5_vpu_probe,
- .remove_new = wave5_vpu_remove,
+ .remove = wave5_vpu_remove,
};
module_platform_driver(wave5_vpu_driver);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu.h b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
index 32b7fd3730b5..3847332551fc 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu.h
@@ -38,10 +38,7 @@ enum vpu_fmt_type {
struct vpu_format {
unsigned int v4l2_pix_fmt;
- unsigned int max_width;
- unsigned int min_width;
- unsigned int max_height;
- unsigned int min_height;
+ const struct v4l2_frmsize_stepwise *v4l2_frmsize;
};
static inline struct vpu_instance *wave5_to_vpu_inst(struct v4l2_fh *vfh)
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
index 1a3efb638dde..e5e879a13e8b 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
@@ -6,6 +6,8 @@
*/
#include <linux/bug.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
#include "wave5-vpuapi.h"
#include "wave5-regdefine.h"
#include "wave5.h"
@@ -73,6 +75,16 @@ int wave5_vpu_flush_instance(struct vpu_instance *inst)
inst->type == VPU_INST_TYPE_DEC ? "DECODER" : "ENCODER", inst->id);
mutex_unlock(&inst->dev->hw_lock);
return -ETIMEDOUT;
+ } else if (ret == -EBUSY) {
+ struct dec_output_info dec_info;
+
+ mutex_unlock(&inst->dev->hw_lock);
+ wave5_vpu_dec_get_output_info(inst, &dec_info);
+ ret = mutex_lock_interruptible(&inst->dev->hw_lock);
+ if (ret)
+ return ret;
+ if (dec_info.index_frame_display > 0)
+ wave5_vpu_dec_set_disp_flag(inst, dec_info.index_frame_display);
}
} while (ret != 0);
mutex_unlock(&inst->dev->hw_lock);
@@ -195,14 +207,20 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
int retry = 0;
struct vpu_device *vpu_dev = inst->dev;
int i;
+ int inst_count = 0;
+ struct vpu_instance *inst_elm;
*fail_res = 0;
if (!inst->codec_info)
return -EINVAL;
+ pm_runtime_resume_and_get(inst->dev->dev);
+
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
- if (ret)
+ if (ret) {
+ pm_runtime_put_sync(inst->dev->dev);
return ret;
+ }
do {
ret = wave5_vpu_dec_finish_seq(inst, fail_res);
@@ -232,9 +250,14 @@ int wave5_vpu_dec_close(struct vpu_instance *inst, u32 *fail_res)
wave5_vdi_free_dma_memory(vpu_dev, &p_dec_info->vb_task);
+ list_for_each_entry(inst_elm, &vpu_dev->instances, list)
+ inst_count++;
+ if (inst_count == 1)
+ pm_runtime_dont_use_autosuspend(vpu_dev->dev);
+
unlock_and_return:
mutex_unlock(&vpu_dev->hw_lock);
-
+ pm_runtime_put_sync(inst->dev->dev);
return ret;
}
@@ -697,25 +720,33 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
int ret;
int retry = 0;
struct vpu_device *vpu_dev = inst->dev;
+ int inst_count = 0;
+ struct vpu_instance *inst_elm;
*fail_res = 0;
if (!inst->codec_info)
return -EINVAL;
+ pm_runtime_resume_and_get(inst->dev->dev);
+
ret = mutex_lock_interruptible(&vpu_dev->hw_lock);
- if (ret)
+ if (ret) {
+ pm_runtime_resume_and_get(inst->dev->dev);
return ret;
+ }
do {
ret = wave5_vpu_enc_finish_seq(inst, fail_res);
if (ret < 0 && *fail_res != WAVE5_SYSERR_VPU_STILL_RUNNING) {
dev_warn(inst->dev->dev, "enc_finish_seq timed out\n");
+ pm_runtime_resume_and_get(inst->dev->dev);
mutex_unlock(&vpu_dev->hw_lock);
return ret;
}
if (*fail_res == WAVE5_SYSERR_VPU_STILL_RUNNING &&
retry++ >= MAX_FIRMWARE_CALL_RETRY) {
+ pm_runtime_resume_and_get(inst->dev->dev);
mutex_unlock(&vpu_dev->hw_lock);
return -ETIMEDOUT;
}
@@ -734,7 +765,13 @@ int wave5_vpu_enc_close(struct vpu_instance *inst, u32 *fail_res)
wave5_vdi_free_dma_memory(vpu_dev, &p_enc_info->vb_task);
+ list_for_each_entry(inst_elm, &vpu_dev->instances, list)
+ inst_count++;
+ if (inst_count == 1)
+ pm_runtime_dont_use_autosuspend(vpu_dev->dev);
+
mutex_unlock(&vpu_dev->hw_lock);
+ pm_runtime_put_sync(inst->dev->dev);
return 0;
}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
index 352f6e904e50..45615c15beca 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
@@ -18,6 +18,7 @@
#include "wave5-vdi.h"
enum product_id {
+ PRODUCT_ID_515,
PRODUCT_ID_521,
PRODUCT_ID_511,
PRODUCT_ID_517,
@@ -327,6 +328,7 @@ struct vpu_attr {
u32 support_backbone: 1;
u32 support_avc10bit_enc: 1;
u32 support_hevc10bit_enc: 1;
+ u32 support_hevc10bit_dec: 1;
u32 support_vcore_backbone: 1;
u32 support_vcpu_backbone: 1;
};
@@ -566,6 +568,7 @@ struct enc_wave_param {
u32 lambda_scaling_enable: 1; /* enable lambda scaling using custom GOP */
u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */
u32 mb_level_rc_enable: 1; /* enable MB-level rate control */
+ u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */
};
struct enc_open_param {
@@ -756,7 +759,12 @@ struct vpu_device {
u32 product_code;
struct ida inst_ida;
struct clk_bulk_data *clks;
+ struct hrtimer hrtimer;
+ struct kthread_work work;
+ struct kthread_worker *worker;
+ int vpu_poll_interval;
int num_clks;
+ struct reset_control *resets;
};
struct vpu_instance;
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
index d9751eedb0f9..1ea9f5f31499 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuconfig.h
@@ -8,6 +8,7 @@
#ifndef _VPU_CONFIG_H_
#define _VPU_CONFIG_H_
+#define WAVE515_CODE 0x5150
#define WAVE517_CODE 0x5170
#define WAVE537_CODE 0x5370
#define WAVE511_CODE 0x5110
@@ -21,19 +22,39 @@
((c) == WAVE517_CODE || (c) == WAVE537_CODE || \
(c) == WAVE511_CODE || (c) == WAVE521_CODE || \
(c) == WAVE521E1_CODE || (c) == WAVE521C_CODE || \
- (c) == WAVE521C_DUAL_CODE); \
+ (c) == WAVE521C_DUAL_CODE) || (c) == WAVE515_CODE; \
})
#define WAVE517_WORKBUF_SIZE (2 * 1024 * 1024)
#define WAVE521ENC_WORKBUF_SIZE (128 * 1024) //HEVC 128K, AVC 40K
#define WAVE521DEC_WORKBUF_SIZE (1784 * 1024)
+#define WAVE515DEC_WORKBUF_SIZE (2 * 1024 * 1024)
#define MAX_NUM_INSTANCE 32
-#define W5_MIN_ENC_PIC_WIDTH 256
-#define W5_MIN_ENC_PIC_HEIGHT 128
-#define W5_MAX_ENC_PIC_WIDTH 8192
-#define W5_MAX_ENC_PIC_HEIGHT 8192
+#define W5_DEF_DEC_PIC_WIDTH 720U
+#define W5_DEF_DEC_PIC_HEIGHT 480U
+#define W5_MIN_DEC_PIC_8_WIDTH 8U
+#define W5_MIN_DEC_PIC_8_HEIGHT 8U
+#define W5_MIN_DEC_PIC_32_WIDTH 32U
+#define W5_MIN_DEC_PIC_32_HEIGHT 32U
+#define W5_MAX_DEC_PIC_WIDTH 8192U
+#define W5_MAX_DEC_PIC_HEIGHT 4320U
+#define W5_DEC_CODEC_STEP_WIDTH 1U
+#define W5_DEC_CODEC_STEP_HEIGHT 1U
+#define W5_DEC_RAW_STEP_WIDTH 32U
+#define W5_DEC_RAW_STEP_HEIGHT 16U
+
+#define W5_DEF_ENC_PIC_WIDTH 416U
+#define W5_DEF_ENC_PIC_HEIGHT 240U
+#define W5_MIN_ENC_PIC_WIDTH 256U
+#define W5_MIN_ENC_PIC_HEIGHT 128U
+#define W5_MAX_ENC_PIC_WIDTH 8192U
+#define W5_MAX_ENC_PIC_HEIGHT 8192U
+#define W5_ENC_CODEC_STEP_WIDTH 8U
+#define W5_ENC_CODEC_STEP_HEIGHT 8U
+#define W5_ENC_RAW_STEP_WIDTH 32U
+#define W5_ENC_RAW_STEP_HEIGHT 16U
// application specific configuration
#define VPU_ENC_TIMEOUT 60000
@@ -49,17 +70,21 @@
/************************************************************************/
#define VLC_BUF_NUM (2)
-#define COMMAND_QUEUE_DEPTH (2)
+#define WAVE521_COMMAND_QUEUE_DEPTH (2)
+#define WAVE515_COMMAND_QUEUE_DEPTH (4)
#define W5_REMAP_INDEX0 0
#define W5_REMAP_INDEX1 1
#define W5_REMAP_MAX_SIZE (1024 * 1024)
-#define WAVE5_MAX_CODE_BUF_SIZE (2 * 1024 * 1024)
-#define WAVE5_TEMPBUF_OFFSET WAVE5_MAX_CODE_BUF_SIZE
+#define WAVE521_MAX_CODE_BUF_SIZE (2 * 1024 * 1024)
+#define WAVE515_MAX_CODE_BUF_SIZE (1024 * 1024)
#define WAVE5_TEMPBUF_SIZE (1024 * 1024)
-#define SIZE_COMMON (WAVE5_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE)
+#define WAVE521_SIZE_COMMON (WAVE521_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE)
+#define WAVE515_ONE_TASKBUF_SIZE (8 * 1024 * 1024)
+#define WAVE515_SIZE_COMMON (WAVE515_MAX_CODE_BUF_SIZE + WAVE5_TEMPBUF_SIZE + \
+ WAVE515_COMMAND_QUEUE_DEPTH * WAVE515_ONE_TASKBUF_SIZE)
//=====4. VPU REPORT MEMORY ======================//
diff --git a/drivers/media/platform/chips-media/wave5/wave5.h b/drivers/media/platform/chips-media/wave5/wave5.h
index 063028eccd3b..2caab356f3e1 100644
--- a/drivers/media/platform/chips-media/wave5/wave5.h
+++ b/drivers/media/platform/chips-media/wave5/wave5.h
@@ -22,6 +22,12 @@
*/
#define BSOPTION_ENABLE_EXPLICIT_END BIT(0)
#define BSOPTION_HIGHLIGHT_STREAM_END BIT(1)
+/*
+ * When RD_PTR_VALID_FLAG is 0 Wave515 ignores RD_PTR value and starts to
+ * decode from the access unit end position of the last decoded picture in
+ * bitstream buffer.
+ */
+#define BSOPTION_RD_PTR_VALID_FLAG BIT(31)
/*
* Currently the driver only supports hardware with little endian but for source
@@ -56,6 +62,9 @@ int wave5_vpu_get_version(struct vpu_device *vpu_dev, u32 *revision);
int wave5_vpu_init(struct device *dev, u8 *fw, size_t size);
+int wave5_vpu_sleep_wake(struct device *dev, bool i_sleep_wake, const uint16_t *code,
+ size_t size);
+
int wave5_vpu_reset(struct device *dev, enum sw_reset_mode reset_mode);
int wave5_vpu_build_up_dec_param(struct vpu_instance *inst, struct dec_open_param *param);
diff --git a/drivers/media/platform/imagination/Kconfig b/drivers/media/platform/imagination/Kconfig
new file mode 100644
index 000000000000..a302c955483d
--- /dev/null
+++ b/drivers/media/platform/imagination/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+config VIDEO_E5010_JPEG_ENC
+ tristate "Imagination E5010 JPEG Encoder Driver"
+ depends on VIDEO_DEV
+ depends on ARCH_K3 || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
+ select VIDEOBUF2_VMALLOC
+ select V4L2_MEM2MEM_DEV
+ select V4L2_JPEG_HELPER
+ help
+ This is a video4linux2 M2M driver for Imagination E5010 JPEG encoder,
+ which supports JPEG and MJPEG baseline encoding of YUV422 and YUV420
+ semiplanar video formats, with resolution ranging from 64x64 to 8K x 8K
+ pixels. The module will be named as e5010_jpeg_enc.
diff --git a/drivers/media/platform/imagination/Makefile b/drivers/media/platform/imagination/Makefile
new file mode 100644
index 000000000000..d45b85b88575
--- /dev/null
+++ b/drivers/media/platform/imagination/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+e5010_jpeg_enc-objs := e5010-jpeg-enc-hw.o e5010-jpeg-enc.o
+obj-$(CONFIG_VIDEO_E5010_JPEG_ENC) += e5010_jpeg_enc.o
diff --git a/drivers/media/platform/imagination/e5010-core-regs.h b/drivers/media/platform/imagination/e5010-core-regs.h
new file mode 100644
index 000000000000..aaec498fe83f
--- /dev/null
+++ b/drivers/media/platform/imagination/e5010-core-regs.h
@@ -0,0 +1,585 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Imagination E5010 JPEG Encoder driver.
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Author: David Huang <d-huang@ti.com>
+ * Author: Devarsh Thakkar <devarsht@ti.com>
+ */
+
+#ifndef _E5010_CORE_REGS_H
+#define _E5010_CORE_REGS_H
+
+#define JASPER_CORE_ID_OFFSET (0x0000)
+#define JASPER_CORE_ID_CR_GROUP_ID_MASK (0xFF000000)
+#define JASPER_CORE_ID_CR_GROUP_ID_SHIFT (24)
+#define JASPER_CORE_ID_CR_CORE_ID_MASK (0x00FF0000)
+#define JASPER_CORE_ID_CR_CORE_ID_SHIFT (16)
+#define JASPER_CORE_ID_CR_UNIQUE_NUM_MASK (0x0000FFF8)
+#define JASPER_CORE_ID_CR_UNIQUE_NUM_SHIFT (3)
+#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_MASK (0x00000007)
+#define JASPER_CORE_ID_CR_PELS_PER_CYCLE_SHIFT (0)
+
+#define JASPER_CORE_REV_OFFSET (0x0004)
+#define JASPER_CORE_REV_CR_JASPER_DESIGNER_MASK (0xFF000000)
+#define JASPER_CORE_REV_CR_JASPER_DESIGNER_SHIFT (24)
+#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_MASK (0x00FF0000)
+#define JASPER_CORE_REV_CR_JASPER_MAJOR_REV_SHIFT (16)
+#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_MASK (0x0000FF00)
+#define JASPER_CORE_REV_CR_JASPER_MINOR_REV_SHIFT (8)
+#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_MASK (0x000000FF)
+#define JASPER_CORE_REV_CR_JASPER_MAINT_REV_SHIFT (0)
+
+#define JASPER_INTERRUPT_MASK_OFFSET (0x0008)
+#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK (0x00000002)
+#define JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT (1)
+#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK (0x00000001)
+#define JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT (0)
+
+#define JASPER_INTERRUPT_STATUS_OFFSET (0x000C)
+#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK (0x00000002)
+#define JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_SHIFT (1)
+#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK (0x00000001)
+#define JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_SHIFT (0)
+
+#define JASPER_INTERRUPT_CLEAR_OFFSET (0x0010)
+#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK (0x00000002)
+#define JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT (1)
+#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK (0x00000001)
+#define JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT (0)
+
+#define JASPER_CLK_CONTROL_OFFSET (0x0014)
+#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK (0x00000002)
+#define JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT (1)
+#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK (0x00000001)
+#define JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT (0)
+
+#define JASPER_CLK_STATUS_OFFSET (0x0018)
+#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_MASK (0x00000001)
+#define JASPER_CLK_STATUS_CR_JASPER_CLKG_STATUS_SHIFT (0)
+
+#define JASPER_RESET_OFFSET (0x001C)
+#define JASPER_RESET_CR_SYS_RESET_MASK (0x00000002)
+#define JASPER_RESET_CR_SYS_RESET_SHIFT (1)
+#define JASPER_RESET_CR_CORE_RESET_MASK (0x00000001)
+#define JASPER_RESET_CR_CORE_RESET_SHIFT (0)
+
+#define JASPER_CORE_CTRL_OFFSET (0x0020)
+#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK (0x00000001)
+#define JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT (0)
+
+#define JASPER_STATUS_OFFSET (0x0024)
+#define JASPER_STATUS_CR_FLUSH_MODE_MASK (0x00000002)
+#define JASPER_STATUS_CR_FLUSH_MODE_SHIFT (1)
+#define JASPER_STATUS_CR_JASPER_BUSY_MASK (0x00000001)
+#define JASPER_STATUS_CR_JASPER_BUSY_SHIFT (0)
+
+#define JASPER_CRC_CLEAR_OFFSET (0x0028)
+#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_MASK (0x00000001)
+#define JASPER_CRC_CLEAR_CR_FRONT_END_CRC_CLEAR_SHIFT (0)
+#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_MASK (0x00000002)
+#define JASPER_CRC_CLEAR_CR_DCT_CRC_CLEAR_SHIFT (1)
+#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_MASK (0x00000004)
+#define JASPER_CRC_CLEAR_CR_ZZ_CRC_CLEAR_SHIFT (2)
+#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_MASK (0x00000008)
+#define JASPER_CRC_CLEAR_CR_QUANT_CRC_CLEAR_SHIFT (3)
+#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_MASK (0x00000010)
+#define JASPER_CRC_CLEAR_CR_ENTROPY_ENCODER_CRC_CLEAR_SHIFT (4)
+#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_MASK (0x00000020)
+#define JASPER_CRC_CLEAR_CR_PACKING_BUFFER_CRC_CLEAR_SHIFT (5)
+
+#define JASPER_INPUT_CTRL0_OFFSET (0x002C)
+#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK (0x01000000)
+#define JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT (24)
+#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK (0x00030000)
+#define JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT (16)
+#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK (0x00000004)
+#define JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT (2)
+
+#define JASPER_INPUT_CTRL1_OFFSET (0x0030)
+#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK (0x1FC00000)
+#define JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT (22)
+#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK (0x00001FC0)
+#define JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT (6)
+
+#define JASPER_MMU_CTRL_OFFSET (0x0034)
+#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_MASK (0x00000002)
+#define JASPER_MMU_CTRL_CR_JASPER_TILING_SCHEME_SHIFT (1)
+#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_MASK (0x00000001)
+#define JASPER_MMU_CTRL_CR_JASPER_TILING_ENABLE_SHIFT (0)
+
+#define JASPER_IMAGE_SIZE_OFFSET (0x0038)
+#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK (0x1FFF0000)
+#define JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT (16)
+#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK (0x00001FFF)
+#define JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT (0)
+
+#define INPUT_LUMA_BASE_OFFSET (0x003C)
+#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK (0xFFFFFFC0)
+#define INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_SHIFT (6)
+
+#define INPUT_CHROMA_BASE_OFFSET (0x0040)
+#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK (0xFFFFFFC0)
+#define INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_SHIFT (6)
+
+#define JASPER_OUTPUT_BASE_OFFSET (0x0044)
+#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK (0xFFFFFFFF)
+#define JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT (0)
+
+#define JASPER_OUTPUT_SIZE_OFFSET (0x0048)
+#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_MASK (0xFFFFFFFF)
+#define JASPER_OUTPUT_SIZE_CR_OUTPUT_SIZE_SHIFT (0)
+#define JASPER_OUTPUT_MAX_SIZE_OFFSET (0x004C)
+#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK (0xFFFFFFFF)
+#define JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET (0x0050)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_03_SHIFT (24)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_02_SHIFT (16)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_01_SHIFT (8)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE0_CR_LUMA_QUANTIZATION_TABLE_00_SHIFT (0)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_OFFSET (0x0054)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_07_SHIFT (24)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_06_SHIFT (16)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_05_SHIFT (8)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE1_CR_LUMA_QUANTIZATION_TABLE_04_SHIFT (0)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_OFFSET (0x0058)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_13_SHIFT (24)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_12_SHIFT (16)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_11_SHIFT (8)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE2_CR_LUMA_QUANTIZATION_TABLE_10_SHIFT (0)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_OFFSET (0x005C)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_17_SHIFT (24)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_16_SHIFT (16)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_15_SHIFT (8)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE3_CR_LUMA_QUANTIZATION_TABLE_14_SHIFT (0)
+#define JASPER_LUMA_QUANTIZATION_TABLE4_OFFSET (0x0060)
+#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_21_SHIFT (8)
+#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE4_CR_LUMA_QUANTIZATION_TABLE_20_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE5_OFFSET (0x0064)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_27_SHIFT (24)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_26_SHIFT (16)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_25_SHIFT (8)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE5_CR_LUMA_QUANTIZATION_TABLE_24_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE6_OFFSET (0x0068)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_33_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_32_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_31_SHIFT (8)
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE6_CR_LUMA_QUANTIZATION_TABLE_30_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE7_OFFSET (0x006C)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_37_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_36_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_35_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE7_CR_LUMA_QUANTIZATION_TABLE_34_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE8_OFFSET (0x0070)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_43_SHIFT (24)
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_42_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_41_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE8_CR_LUMA_QUANTIZATION_TABLE_40_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE9_OFFSET (0x0074)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_47_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_46_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_45_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE9_CR_LUMA_QUANTIZATION_TABLE_44_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE10_OFFSET (0x0078)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_53_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_52_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_51_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE10_CR_LUMA_QUANTIZATION_TABLE_50_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE11_OFFSET (0x007C)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_57_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_56_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_55_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE11_CR_LUMA_QUANTIZATION_TABLE_54_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE12_OFFSET (0x0080)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_63_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_62_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_61_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE12_CR_LUMA_QUANTIZATION_TABLE_60_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE13_OFFSET (0x0084)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_67_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_66_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_65_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE13_CR_LUMA_QUANTIZATION_TABLE_64_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE14_OFFSET (0x0088)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_73_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_72_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_71_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE14_CR_LUMA_QUANTIZATION_TABLE_70_SHIFT (0)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE15_OFFSET (0x008C)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_MASK (0xFF000000)
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_77_SHIFT (24)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000)
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_76_SHIFT (16)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00)
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_75_SHIFT (8)
+
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_MASK (0x000000FF)
+#define JASPER_LUMA_QUANTIZATION_TABLE15_CR_LUMA_QUANTIZATION_TABLE_74_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET (0x0090)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_03_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_02_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_01_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE0_CR_CHROMA_QUANTIZATION_TABLE_00_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_OFFSET (0x0094)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_07_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_06_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_05_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE1_CR_CHROMA_QUANTIZATION_TABLE_04_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_OFFSET (0x0098)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_13_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_12_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_11_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE2_CR_CHROMA_QUANTIZATION_TABLE_10_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_OFFSET (0x009C)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_17_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_16_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_15_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE3_CR_CHROMA_QUANTIZATION_TABLE_14_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_OFFSET (0x00A0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_23_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_22_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_21_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE4_CR_CHROMA_QUANTIZATION_TABLE_20_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_OFFSET (0x00A4)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_27_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_26_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_25_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE5_CR_CHROMA_QUANTIZATION_TABLE_24_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_OFFSET (0x00A8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_33_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_32_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_31_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE6_CR_CHROMA_QUANTIZATION_TABLE_30_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_OFFSET (0x00AC)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_37_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_36_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_35_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE7_CR_CHROMA_QUANTIZATION_TABLE_34_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_OFFSET (0x00B0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_43_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_42_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_41_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE8_CR_CHROMA_QUANTIZATION_TABLE_40_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_OFFSET (0x00B4)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_47_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_46_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_45_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE9_CR_CHROMA_QUANTIZATION_TABLE_44_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_OFFSET (0x00B8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_53_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_52_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_51_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE10_CR_CHROMA_QUANTIZATION_TABLE_50_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_OFFSET (0x00BC)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_57_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_56_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_55_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE11_CR_CHROMA_QUANTIZATION_TABLE_54_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_OFFSET (0x00C0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_63_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_62_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_61_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE12_CR_CHROMA_QUANTIZATION_TABLE_60_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_OFFSET (0x00C4)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_67_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_66_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_65_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE13_CR_CHROMA_QUANTIZATION_TABLE_64_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_OFFSET (0x00C8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_73_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_72_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_71_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE14_CR_CHROMA_QUANTIZATION_TABLE_70_SHIFT (0)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_OFFSET (0x00CC)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_MASK (0xFF000000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_77_SHIFT (24)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_MASK (0x00FF0000)
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_76_SHIFT (16)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_MASK (0x0000FF00)
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_75_SHIFT (8)
+
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_MASK (0x000000FF)
+#define JASPER_CHROMA_QUANTIZATION_TABLE15_CR_CHROMA_QUANTIZATION_TABLE_74_SHIFT (0)
+
+#define JASPER_CRC_CTRL_OFFSET (0x00D0)
+#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK (0x00000001)
+#define JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT (0)
+
+#define JASPER_FRONT_END_CRC_OFFSET (0x00D4)
+#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_MASK (0xFFFFFFFF)
+#define JASPER_FRONT_END_CRC_CR_JASPER_FRONT_END_CRC_OUT_SHIFT (0)
+
+#define JASPER_DCT_CRC_OFFSET (0x00D8)
+#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_MASK (0xFFFFFFFF)
+#define JASPER_DCT_CRC_CR_JASPER_DCT_CRC_OUT_SHIFT (0)
+
+#define JASPER_ZZ_CRC_OFFSET (0x00DC)
+#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_MASK (0xFFFFFFFF)
+#define JASPER_ZZ_CRC_CR_JASPER_ZZ_CRC_OUT_SHIFT (0)
+
+#define JASPER_QUANT_CRC_OFFSET (0x00E0)
+#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_MASK (0xFFFFFFFF)
+#define JASPER_QUANT_CRC_CR_JASPER_QUANT_CRC_OUT_SHIFT (0)
+
+#define JASPER_ENTROPY_ENCODER_CRC_OFFSET (0x00E4)
+#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_MASK (0xFFFFFFFF)
+#define JASPER_ENTROPY_ENCODER_CRC_CR_JASPER_ENTROPY_CRC_OUT_SHIFT (0)
+
+#define JASPER_PACKING_BUFFER_DATA_CRC_OFFSET (0x00E8)
+#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_MASK (0xFFFFFFFF)
+#define JASPER_PACKING_BUFFER_DATA_CRC_CR_JASPER_PACKING_DATA_CRC_OUT_SHIFT (0)
+
+#define JASPER_PACKING_BUFFER_ADDR_CRC_OFFSET (0x00EC)
+#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_MASK (0xFFFFFFFF)
+#define JASPER_PACKING_BUFFER_ADDR_CRC_CR_JASPER_PACKING_ADDR_OUT_CRC_SHIFT (0)
+
+#define JASPER_CORE_BYTE_SIZE (0x00F0)
+
+#endif
diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c
new file mode 100644
index 000000000000..56d5941020fa
--- /dev/null
+++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Imagination E5010 JPEG Encoder driver.
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Author: David Huang <d-huang@ti.com>
+ * Author: Devarsh Thakkar <devarsht@ti.com>
+ */
+
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/dev_printk.h>
+#include "e5010-jpeg-enc-hw.h"
+
+static void write_reg_field(void __iomem *base, unsigned int offset, u32 mask,
+ unsigned int shift, u32 value)
+{
+ u32 reg;
+
+ value <<= shift;
+ if (mask != 0xffffffff) {
+ reg = readl(base + offset);
+ value = (value & mask) | (reg & ~mask);
+ }
+ writel(value, (base + offset));
+}
+
+static int write_reg_field_not_busy(void __iomem *jasper_base, void __iomem *wr_base,
+ unsigned int offset, u32 mask, unsigned int shift,
+ u32 value)
+{
+ int ret;
+ u32 val;
+
+ ret = readl_poll_timeout_atomic(jasper_base + JASPER_STATUS_OFFSET, val,
+ (val & JASPER_STATUS_CR_JASPER_BUSY_MASK) == 0,
+ 2000, 50000);
+ if (ret)
+ return ret;
+
+ write_reg_field(wr_base, offset, mask, shift, value);
+
+ return 0;
+}
+
+void e5010_reset(struct device *dev, void __iomem *core_base, void __iomem *mmu_base)
+{
+ int ret = 0;
+ u32 val;
+
+ write_reg_field(core_base, JASPER_RESET_OFFSET,
+ JASPER_RESET_CR_CORE_RESET_MASK,
+ JASPER_RESET_CR_CORE_RESET_SHIFT, 1);
+
+ write_reg_field(mmu_base, MMU_MMU_CONTROL1_OFFSET,
+ MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK,
+ MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT, 1);
+
+ ret = readl_poll_timeout_atomic(mmu_base + MMU_MMU_CONTROL1_OFFSET, val,
+ (val & MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK) == 0,
+ 2000, 50000);
+ if (ret)
+ dev_warn(dev, "MMU soft reset timed out, forcing system soft reset\n");
+
+ write_reg_field(core_base, JASPER_RESET_OFFSET,
+ JASPER_RESET_CR_SYS_RESET_MASK,
+ JASPER_RESET_CR_SYS_RESET_SHIFT, 1);
+}
+
+void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable)
+{
+ /* Bypass MMU */
+ write_reg_field(mmu_base,
+ MMU_MMU_ADDRESS_CONTROL_OFFSET,
+ MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK,
+ MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT,
+ enable);
+}
+
+int e5010_hw_enable_output_address_error_irq(void __iomem *core_base, u32 enable)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_INTERRUPT_MASK_OFFSET,
+ JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_MASK,
+ JASPER_INTERRUPT_MASK_CR_OUTPUT_ADDRESS_ERROR_ENABLE_SHIFT,
+ enable);
+}
+
+bool e5010_hw_pic_done_irq(void __iomem *core_base)
+{
+ u32 reg;
+
+ reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET);
+ return reg & JASPER_INTERRUPT_STATUS_CR_PICTURE_DONE_IRQ_MASK;
+}
+
+bool e5010_hw_output_address_irq(void __iomem *core_base)
+{
+ u32 reg;
+
+ reg = readl(core_base + JASPER_INTERRUPT_STATUS_OFFSET);
+ return reg & JASPER_INTERRUPT_STATUS_CR_OUTPUT_ADDRESS_ERROR_IRQ_MASK;
+}
+
+int e5010_hw_enable_picture_done_irq(void __iomem *core_base, u32 enable)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_INTERRUPT_MASK_OFFSET,
+ JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_MASK,
+ JASPER_INTERRUPT_MASK_CR_PICTURE_DONE_ENABLE_SHIFT,
+ enable);
+}
+
+int e5010_hw_enable_auto_clock_gating(void __iomem *core_base, u32 enable)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_CLK_CONTROL_OFFSET,
+ JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_MASK,
+ JASPER_CLK_CONTROL_CR_JASPER_AUTO_CLKG_ENABLE_SHIFT,
+ enable);
+}
+
+int e5010_hw_enable_manual_clock_gating(void __iomem *core_base, u32 enable)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_CLK_CONTROL_OFFSET,
+ JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_MASK,
+ JASPER_CLK_CONTROL_CR_JASPER_MAN_CLKG_ENABLE_SHIFT, 0);
+}
+
+int e5010_hw_enable_crc_check(void __iomem *core_base, u32 enable)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_CRC_CTRL_OFFSET,
+ JASPER_CRC_CTRL_JASPER_CRC_ENABLE_MASK,
+ JASPER_CRC_CTRL_JASPER_CRC_ENABLE_SHIFT, enable);
+}
+
+int e5010_hw_set_input_source_to_memory(void __iomem *core_base, u32 set)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_INPUT_CTRL0_OFFSET,
+ JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_MASK,
+ JASPER_INPUT_CTRL0_CR_INPUT_SOURCE_SHIFT, set);
+}
+
+int e5010_hw_set_input_luma_addr(void __iomem *core_base, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ INPUT_LUMA_BASE_OFFSET,
+ INPUT_LUMA_BASE_CR_INPUT_LUMA_BASE_MASK, 0, val);
+}
+
+int e5010_hw_set_input_chroma_addr(void __iomem *core_base, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ INPUT_CHROMA_BASE_OFFSET,
+ INPUT_CHROMA_BASE_CR_INPUT_CHROMA_BASE_MASK, 0, val);
+}
+
+int e5010_hw_set_output_base_addr(void __iomem *core_base, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_OUTPUT_BASE_OFFSET,
+ JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_MASK,
+ JASPER_OUTPUT_BASE_CR_OUTPUT_BASE_SHIFT, val);
+}
+
+int e5010_hw_set_horizontal_size(void __iomem *core_base, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_IMAGE_SIZE_OFFSET,
+ JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_MASK,
+ JASPER_IMAGE_SIZE_CR_IMAGE_HORIZONTAL_SIZE_SHIFT,
+ val);
+}
+
+int e5010_hw_set_vertical_size(void __iomem *core_base, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_IMAGE_SIZE_OFFSET,
+ JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_MASK,
+ JASPER_IMAGE_SIZE_CR_IMAGE_VERTICAL_SIZE_SHIFT,
+ val);
+}
+
+int e5010_hw_set_luma_stride(void __iomem *core_base, u32 bytesperline)
+{
+ u32 val = bytesperline / 64;
+
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_INPUT_CTRL1_OFFSET,
+ JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_MASK,
+ JASPER_INPUT_CTRL1_CR_INPUT_LUMA_STRIDE_SHIFT,
+ val);
+}
+
+int e5010_hw_set_chroma_stride(void __iomem *core_base, u32 bytesperline)
+{
+ u32 val = bytesperline / 64;
+
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_INPUT_CTRL1_OFFSET,
+ JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_MASK,
+ JASPER_INPUT_CTRL1_CR_INPUT_CHROMA_STRIDE_SHIFT,
+ val);
+}
+
+int e5010_hw_set_input_subsampling(void __iomem *core_base, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_INPUT_CTRL0_OFFSET,
+ JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_MASK,
+ JASPER_INPUT_CTRL0_CR_INPUT_SUBSAMPLING_SHIFT,
+ val);
+}
+
+int e5010_hw_set_chroma_order(void __iomem *core_base, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base,
+ JASPER_INPUT_CTRL0_OFFSET,
+ JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_MASK,
+ JASPER_INPUT_CTRL0_CR_INPUT_CHROMA_ORDER_SHIFT,
+ val);
+}
+
+void e5010_hw_set_output_max_size(void __iomem *core_base, u32 val)
+{
+ write_reg_field(core_base, JASPER_OUTPUT_MAX_SIZE_OFFSET,
+ JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_MASK,
+ JASPER_OUTPUT_MAX_SIZE_CR_OUTPUT_MAX_SIZE_SHIFT,
+ val);
+}
+
+int e5010_hw_set_qpvalue(void __iomem *core_base, u32 offset, u32 val)
+{
+ return write_reg_field_not_busy(core_base, core_base, offset, 0xffffffff, 0, val);
+}
+
+void e5010_hw_clear_output_error(void __iomem *core_base, u32 clear)
+{
+ /* Make sure interrupts are clear */
+ write_reg_field(core_base, JASPER_INTERRUPT_CLEAR_OFFSET,
+ JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_MASK,
+ JASPER_INTERRUPT_CLEAR_CR_OUTPUT_ERROR_CLEAR_SHIFT, clear);
+}
+
+void e5010_hw_clear_picture_done(void __iomem *core_base, u32 clear)
+{
+ write_reg_field(core_base,
+ JASPER_INTERRUPT_CLEAR_OFFSET,
+ JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_MASK,
+ JASPER_INTERRUPT_CLEAR_CR_PICTURE_DONE_CLEAR_SHIFT, clear);
+}
+
+int e5010_hw_get_output_size(void __iomem *core_base)
+{
+ return readl(core_base + JASPER_OUTPUT_SIZE_OFFSET);
+}
+
+void e5010_hw_encode_start(void __iomem *core_base, u32 start)
+{
+ write_reg_field(core_base, JASPER_CORE_CTRL_OFFSET,
+ JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_MASK,
+ JASPER_CORE_CTRL_CR_JASPER_ENCODE_START_SHIFT, start);
+}
diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h
new file mode 100644
index 000000000000..781d353c3226
--- /dev/null
+++ b/drivers/media/platform/imagination/e5010-jpeg-enc-hw.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Imagination E5010 JPEG Encoder driver.
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Author: David Huang <d-huang@ti.com>
+ * Author: Devarsh Thakkar <devarsht@ti.com>
+ */
+
+#ifndef _E5010_JPEG_ENC_HW_H
+#define _E5010_JPEG_ENC_HW_H
+
+#include "e5010-core-regs.h"
+#include "e5010-mmu-regs.h"
+
+int e5010_hw_enable_output_address_error_irq(void __iomem *core_offset, u32 enable);
+int e5010_hw_enable_picture_done_irq(void __iomem *core_offset, u32 enable);
+int e5010_hw_enable_auto_clock_gating(void __iomem *core_offset, u32 enable);
+int e5010_hw_enable_manual_clock_gating(void __iomem *core_offset, u32 enable);
+int e5010_hw_enable_crc_check(void __iomem *core_offset, u32 enable);
+int e5010_hw_set_input_source_to_memory(void __iomem *core_offset, u32 set);
+int e5010_hw_set_input_luma_addr(void __iomem *core_offset, u32 val);
+int e5010_hw_set_input_chroma_addr(void __iomem *core_offset, u32 val);
+int e5010_hw_set_output_base_addr(void __iomem *core_offset, u32 val);
+int e5010_hw_get_output_size(void __iomem *core_offset);
+int e5010_hw_set_horizontal_size(void __iomem *core_offset, u32 val);
+int e5010_hw_set_vertical_size(void __iomem *core_offset, u32 val);
+int e5010_hw_set_luma_stride(void __iomem *core_offset, u32 bytesperline);
+int e5010_hw_set_chroma_stride(void __iomem *core_offset, u32 bytesperline);
+int e5010_hw_set_input_subsampling(void __iomem *core_offset, u32 val);
+int e5010_hw_set_chroma_order(void __iomem *core_offset, u32 val);
+int e5010_hw_set_qpvalue(void __iomem *core_offset, u32 offset, u32 value);
+void e5010_reset(struct device *dev, void __iomem *core_offset, void __iomem *mmu_offset);
+void e5010_hw_set_output_max_size(void __iomem *core_offset, u32 val);
+void e5010_hw_clear_picture_done(void __iomem *core_offset, u32 clear);
+void e5010_hw_encode_start(void __iomem *core_offset, u32 start);
+void e5010_hw_clear_output_error(void __iomem *core_offset, u32 clear);
+void e5010_hw_bypass_mmu(void __iomem *mmu_base, u32 enable);
+bool e5010_hw_pic_done_irq(void __iomem *core_base);
+bool e5010_hw_output_address_irq(void __iomem *core_base);
+#endif
diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.c b/drivers/media/platform/imagination/e5010-jpeg-enc.c
new file mode 100644
index 000000000000..ae868d9f73e1
--- /dev/null
+++ b/drivers/media/platform/imagination/e5010-jpeg-enc.c
@@ -0,0 +1,1635 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Imagination E5010 JPEG Encoder driver.
+ *
+ * TODO: Add MMU and memory tiling support
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Author: David Huang <d-huang@ti.com>
+ * Author: Devarsh Thakkar <devarsht@ti.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <media/jpeg.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-jpeg.h>
+#include <media/v4l2-rect.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+#include "e5010-jpeg-enc.h"
+#include "e5010-jpeg-enc-hw.h"
+
+/* forward declarations */
+static const struct of_device_id e5010_of_match[];
+
+static const struct v4l2_file_operations e5010_fops;
+
+static const struct v4l2_ioctl_ops e5010_ioctl_ops;
+
+static const struct vb2_ops e5010_video_ops;
+
+static const struct v4l2_m2m_ops e5010_m2m_ops;
+
+static struct e5010_fmt e5010_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ .chroma_order = CHROMA_ORDER_CB_CR,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .num_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ .chroma_order = CHROMA_ORDER_CB_CR,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ .chroma_order = CHROMA_ORDER_CR_CB,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .num_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_420,
+ .chroma_order = CHROMA_ORDER_CR_CB,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ .chroma_order = CHROMA_ORDER_CB_CR,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .num_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ .chroma_order = CHROMA_ORDER_CB_CR,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ .chroma_order = CHROMA_ORDER_CR_CB,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .num_planes = 2,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .subsampling = V4L2_JPEG_CHROMA_SUBSAMPLING_422,
+ .chroma_order = CHROMA_ORDER_CR_CB,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 64,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .subsampling = 0,
+ .chroma_order = 0,
+ .frmsize = { MIN_DIMENSION, MAX_DIMENSION, 16,
+ MIN_DIMENSION, MAX_DIMENSION, 8 },
+ },
+};
+
+static unsigned int debug;
+module_param(debug, uint, 0644);
+MODULE_PARM_DESC(debug, "debug level");
+
+#define dprintk(dev, lvl, fmt, arg...) \
+ v4l2_dbg(lvl, debug, &(dev)->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+static const struct v4l2_event e5010_eos_event = {
+ .type = V4L2_EVENT_EOS
+};
+
+static const char *type_name(enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return "Output";
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ return "Capture";
+ default:
+ return "Invalid";
+ }
+}
+
+static struct e5010_q_data *get_queue(struct e5010_context *ctx, enum v4l2_buf_type type)
+{
+ return (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? &ctx->out_queue : &ctx->cap_queue;
+}
+
+static void calculate_qp_tables(struct e5010_context *ctx)
+{
+ long long luminosity, contrast;
+ int quality, i;
+
+ quality = 50 - ctx->quality;
+
+ luminosity = LUMINOSITY * quality / 50;
+ contrast = CONTRAST * quality / 50;
+
+ if (quality > 0) {
+ luminosity *= INCREASE;
+ contrast *= INCREASE;
+ }
+
+ for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++) {
+ long long delta = v4l2_jpeg_ref_table_chroma_qt[i] * contrast + luminosity;
+ int val = (int)(v4l2_jpeg_ref_table_chroma_qt[i] + delta);
+
+ clamp(val, 1, 255);
+ ctx->chroma_qp[i] = quality == -50 ? 1 : val;
+
+ delta = v4l2_jpeg_ref_table_luma_qt[i] * contrast + luminosity;
+ val = (int)(v4l2_jpeg_ref_table_luma_qt[i] + delta);
+ clamp(val, 1, 255);
+ ctx->luma_qp[i] = quality == -50 ? 1 : val;
+ }
+
+ ctx->update_qp = true;
+}
+
+static int update_qp_tables(struct e5010_context *ctx)
+{
+ struct e5010_dev *e5010 = ctx->e5010;
+ int i, ret = 0;
+ u32 lvalue, cvalue;
+
+ lvalue = 0;
+ cvalue = 0;
+
+ for (i = 0; i < QP_TABLE_SIZE; i++) {
+ lvalue |= ctx->luma_qp[i] << (8 * (i % 4));
+ cvalue |= ctx->chroma_qp[i] << (8 * (i % 4));
+ if (i % 4 == 3) {
+ ret |= e5010_hw_set_qpvalue(e5010->core_base,
+ JASPER_LUMA_QUANTIZATION_TABLE0_OFFSET
+ + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4),
+ lvalue);
+ ret |= e5010_hw_set_qpvalue(e5010->core_base,
+ JASPER_CHROMA_QUANTIZATION_TABLE0_OFFSET
+ + QP_TABLE_FIELD_OFFSET * ((i - 3) / 4),
+ cvalue);
+ lvalue = 0;
+ cvalue = 0;
+ }
+ }
+
+ return ret;
+}
+
+static int e5010_set_input_subsampling(void __iomem *core_base, int subsampling)
+{
+ switch (subsampling) {
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_420:
+ return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_420);
+ case V4L2_JPEG_CHROMA_SUBSAMPLING_422:
+ return e5010_hw_set_input_subsampling(core_base, SUBSAMPLING_422);
+ default:
+ return -EINVAL;
+ };
+}
+
+static int e5010_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, E5010_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, E5010_MODULE_NAME, sizeof(cap->card));
+
+ return 0;
+}
+
+static struct e5010_fmt *find_format(struct v4l2_format *f)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) {
+ if (e5010_formats[i].fourcc == f->fmt.pix_mp.pixelformat &&
+ e5010_formats[i].type == f->type)
+ return &e5010_formats[i];
+ }
+
+ return NULL;
+}
+
+static int e5010_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
+{
+ int i, index = 0;
+ struct e5010_fmt *fmt = NULL;
+ struct e5010_context *ctx = file->private_data;
+
+ if (!V4L2_TYPE_IS_MULTIPLANAR(f->type)) {
+ v4l2_err(&ctx->e5010->v4l2_dev, "ENUMFMT with Invalid type: %d\n", f->type);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(e5010_formats); ++i) {
+ if (e5010_formats[i].type == f->type) {
+ if (index == f->index) {
+ fmt = &e5010_formats[i];
+ break;
+ }
+ index++;
+ }
+ }
+
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static int e5010_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct e5010_context *ctx = file->private_data;
+ struct e5010_q_data *queue;
+ int i;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt;
+
+ queue = get_queue(ctx, f->type);
+
+ pix_mp->flags = 0;
+ pix_mp->field = V4L2_FIELD_NONE;
+ pix_mp->pixelformat = queue->fmt->fourcc;
+ pix_mp->width = queue->width_adjusted;
+ pix_mp->height = queue->height_adjusted;
+ pix_mp->num_planes = queue->fmt->num_planes;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ if (!pix_mp->colorspace)
+ pix_mp->colorspace = V4L2_COLORSPACE_SRGB;
+
+ for (i = 0; i < queue->fmt->num_planes; i++) {
+ plane_fmt[i].sizeimage = queue->sizeimage[i];
+ plane_fmt[i].bytesperline = queue->bytesperline[i];
+ }
+
+ } else {
+ pix_mp->colorspace = V4L2_COLORSPACE_JPEG;
+ plane_fmt[0].bytesperline = 0;
+ plane_fmt[0].sizeimage = queue->sizeimage[0];
+ }
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ return 0;
+}
+
+static int e5010_jpeg_try_fmt(struct v4l2_format *f, struct e5010_context *ctx)
+{
+ struct e5010_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt;
+
+ fmt = find_format(f);
+ if (!fmt) {
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ pix_mp->pixelformat = V4L2_PIX_FMT_NV12;
+ else
+ pix_mp->pixelformat = V4L2_PIX_FMT_JPEG;
+ fmt = find_format(f);
+ if (!fmt)
+ return -EINVAL;
+ }
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ if (!pix_mp->colorspace)
+ pix_mp->colorspace = V4L2_COLORSPACE_JPEG;
+ if (!pix_mp->ycbcr_enc)
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ if (!pix_mp->quantization)
+ pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT;
+ if (!pix_mp->xfer_func)
+ pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &fmt->frmsize);
+
+ v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
+ pix_mp->width, pix_mp->height);
+
+ } else {
+ pix_mp->colorspace = V4L2_COLORSPACE_JPEG;
+ pix_mp->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix_mp->quantization = V4L2_QUANTIZATION_DEFAULT;
+ pix_mp->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &fmt->frmsize);
+ plane_fmt[0].sizeimage = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL;
+ plane_fmt[0].sizeimage += HEADER_SIZE;
+ plane_fmt[0].bytesperline = 0;
+ pix_mp->pixelformat = fmt->fourcc;
+ pix_mp->num_planes = fmt->num_planes;
+ }
+ pix_mp->flags = 0;
+ pix_mp->field = V4L2_FIELD_NONE;
+
+ dprintk(ctx->e5010, 2,
+ "ctx: 0x%p: format type %s:, wxh: %dx%d (plane0 : %d bytes, plane1 : %d bytes),fmt: %c%c%c%c\n",
+ ctx, type_name(f->type), pix_mp->width, pix_mp->height,
+ plane_fmt[0].sizeimage, plane_fmt[1].sizeimage,
+ (fmt->fourcc & 0xff),
+ (fmt->fourcc >> 8) & 0xff,
+ (fmt->fourcc >> 16) & 0xff,
+ (fmt->fourcc >> 24) & 0xff);
+
+ return 0;
+}
+
+static int e5010_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct e5010_context *ctx = file->private_data;
+
+ return e5010_jpeg_try_fmt(f, ctx);
+}
+
+static int e5010_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct e5010_context *ctx = file->private_data;
+ struct vb2_queue *vq;
+ int ret = 0, i = 0;
+ struct v4l2_pix_format_mplane *pix_mp = &f->fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt;
+ struct e5010_q_data *queue;
+ struct e5010_fmt *fmt;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->e5010->v4l2_dev, "queue busy\n");
+ return -EBUSY;
+ }
+
+ ret = e5010_jpeg_try_fmt(f, ctx);
+ if (ret)
+ return ret;
+
+ fmt = find_format(f);
+ queue = get_queue(ctx, f->type);
+
+ queue->fmt = fmt;
+ queue->width = pix_mp->width;
+ queue->height = pix_mp->height;
+
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ for (i = 0; i < fmt->num_planes; i++) {
+ queue->bytesperline[i] = plane_fmt[i].bytesperline;
+ queue->sizeimage[i] = plane_fmt[i].sizeimage;
+ }
+ queue->crop.left = 0;
+ queue->crop.top = 0;
+ queue->crop.width = queue->width;
+ queue->crop.height = queue->height;
+ } else {
+ queue->sizeimage[0] = plane_fmt[0].sizeimage;
+ queue->sizeimage[1] = 0;
+ queue->bytesperline[0] = 0;
+ queue->bytesperline[1] = 0;
+ }
+
+ return 0;
+}
+
+static int e5010_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize)
+{
+ struct v4l2_format f;
+ struct e5010_fmt *fmt;
+
+ if (fsize->index != 0)
+ return -EINVAL;
+
+ f.fmt.pix_mp.pixelformat = fsize->pixel_format;
+ if (f.fmt.pix_mp.pixelformat == V4L2_PIX_FMT_JPEG)
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ else
+ f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+ fmt = find_format(&f);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise = fmt->frmsize;
+ fsize->reserved[0] = 0;
+ fsize->reserved[1] = 0;
+
+ return 0;
+}
+
+static int e5010_g_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct e5010_context *ctx = file->private_data;
+ struct e5010_q_data *queue;
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ 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 = queue->width;
+ s->r.height = queue->height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ memcpy(&s->r, &queue->crop, sizeof(s->r));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int e5010_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
+{
+ struct e5010_context *ctx = file->private_data;
+ struct e5010_q_data *queue;
+ struct vb2_queue *vq;
+ struct v4l2_rect base_rect;
+
+ vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, s->type);
+ if (!vq)
+ return -EINVAL;
+
+ if (vb2_is_streaming(vq))
+ return -EBUSY;
+
+ if (s->target != V4L2_SEL_TGT_CROP ||
+ s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ return -EINVAL;
+
+ queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ base_rect.top = 0;
+ base_rect.left = 0;
+ base_rect.width = queue->width;
+ base_rect.height = queue->height;
+
+ switch (s->flags) {
+ case 0:
+ s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width);
+ s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height);
+ s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width);
+ s->r.top = round_down(s->r.top, 2);
+
+ if (s->r.left + s->r.width > queue->width)
+ s->r.width = round_down(s->r.width + s->r.left - queue->width,
+ queue->fmt->frmsize.step_width);
+ if (s->r.top + s->r.height > queue->height)
+ s->r.top = round_down(s->r.top + s->r.height - queue->height, 2);
+ break;
+ case V4L2_SEL_FLAG_GE:
+ s->r.width = round_up(s->r.width, queue->fmt->frmsize.step_width);
+ s->r.height = round_up(s->r.height, queue->fmt->frmsize.step_height);
+ s->r.left = round_up(s->r.left, queue->fmt->frmsize.step_width);
+ s->r.top = round_up(s->r.top, 2);
+ break;
+ case V4L2_SEL_FLAG_LE:
+ s->r.width = round_down(s->r.width, queue->fmt->frmsize.step_width);
+ s->r.height = round_down(s->r.height, queue->fmt->frmsize.step_height);
+ s->r.left = round_down(s->r.left, queue->fmt->frmsize.step_width);
+ s->r.top = round_down(s->r.top, 2);
+ break;
+ case V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE:
+ if (!IS_ALIGNED(s->r.width, queue->fmt->frmsize.step_width) ||
+ !IS_ALIGNED(s->r.height, queue->fmt->frmsize.step_height) ||
+ !IS_ALIGNED(s->r.left, queue->fmt->frmsize.step_width) ||
+ !IS_ALIGNED(s->r.top, 2))
+ return -ERANGE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!v4l2_rect_enclosed(&s->r, &base_rect))
+ return -ERANGE;
+
+ memcpy(&queue->crop, &s->r, sizeof(s->r));
+
+ if (!v4l2_rect_equal(&s->r, &base_rect))
+ queue->crop_set = true;
+
+ dprintk(ctx->e5010, 2, "ctx: 0x%p: crop rectangle: w: %d, h : %d, l : %d, t : %d\n",
+ ctx, queue->crop.width, queue->crop.height, queue->crop.left, queue->crop.top);
+
+ return 0;
+}
+
+static int e5010_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;
+ }
+
+ return 0;
+}
+
+static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ struct e5010_context *ctx = priv;
+ struct e5010_dev *e5010 = ctx->e5010;
+ int ret = 0;
+
+ /* src_vq */
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct e5010_buffer);
+ src_vq->ops = &e5010_video_ops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->lock = &e5010->mutex;
+ src_vq->dev = e5010->v4l2_dev.dev;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ /* dst_vq */
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct e5010_buffer);
+ dst_vq->ops = &e5010_video_ops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->lock = &e5010->mutex;
+ dst_vq->dev = e5010->v4l2_dev.dev;
+
+ ret = vb2_queue_init(dst_vq);
+ if (ret) {
+ vb2_queue_release(src_vq);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int e5010_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct e5010_context *ctx =
+ container_of(ctrl->handler, struct e5010_context, ctrl_handler);
+
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ ctx->quality = ctrl->val;
+ calculate_qp_tables(ctx);
+ dprintk(ctx->e5010, 2, "ctx: 0x%p compression quality set to : %d\n", ctx,
+ ctx->quality);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops e5010_ctrl_ops = {
+ .s_ctrl = e5010_s_ctrl,
+};
+
+static void e5010_encode_ctrls(struct e5010_context *ctx)
+{
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &e5010_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 75);
+}
+
+static int e5010_ctrls_setup(struct e5010_context *ctx)
+{
+ int err;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1);
+
+ e5010_encode_ctrls(ctx);
+
+ if (ctx->ctrl_handler.error) {
+ err = ctx->ctrl_handler.error;
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+
+ return err;
+ }
+
+ err = v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
+ if (err)
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+
+ return err;
+}
+
+static void e5010_jpeg_set_default_params(struct e5010_context *ctx)
+{
+ struct e5010_q_data *queue;
+ struct v4l2_format f;
+ struct e5010_fmt *fmt;
+ struct v4l2_pix_format_mplane *pix_mp = &f.fmt.pix_mp;
+ struct v4l2_plane_pix_format *plane_fmt = pix_mp->plane_fmt;
+ int i = 0;
+
+ f.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+ fmt = find_format(&f);
+ queue = &ctx->out_queue;
+ queue->fmt = fmt;
+ queue->width = DEFAULT_WIDTH;
+ queue->height = DEFAULT_HEIGHT;
+ pix_mp->width = queue->width;
+ pix_mp->height = queue->height;
+ queue->crop.left = 0;
+ queue->crop.top = 0;
+ queue->crop.width = queue->width;
+ queue->crop.height = queue->height;
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &fmt->frmsize);
+ v4l2_fill_pixfmt_mp(pix_mp, pix_mp->pixelformat,
+ pix_mp->width, pix_mp->height);
+ for (i = 0; i < fmt->num_planes; i++) {
+ queue->bytesperline[i] = plane_fmt[i].bytesperline;
+ queue->sizeimage[i] = plane_fmt[i].sizeimage;
+ }
+ queue->width_adjusted = pix_mp->width;
+ queue->height_adjusted = pix_mp->height;
+
+ f.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_JPEG;
+ fmt = find_format(&f);
+ queue = &ctx->cap_queue;
+ queue->fmt = fmt;
+ queue->width = DEFAULT_WIDTH;
+ queue->height = DEFAULT_HEIGHT;
+ pix_mp->width = queue->width;
+ pix_mp->height = queue->height;
+ v4l2_apply_frmsize_constraints(&pix_mp->width,
+ &pix_mp->height,
+ &fmt->frmsize);
+ queue->sizeimage[0] = pix_mp->width * pix_mp->height * JPEG_MAX_BYTES_PER_PIXEL;
+ queue->sizeimage[0] += HEADER_SIZE;
+ queue->sizeimage[1] = 0;
+ queue->bytesperline[0] = 0;
+ queue->bytesperline[1] = 0;
+ queue->width_adjusted = pix_mp->width;
+ queue->height_adjusted = pix_mp->height;
+}
+
+static int e5010_open(struct file *file)
+{
+ struct e5010_dev *e5010 = video_drvdata(file);
+ struct video_device *vdev = video_devdata(file);
+ struct e5010_context *ctx;
+ int ret = 0;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ if (mutex_lock_interruptible(&e5010->mutex)) {
+ ret = -ERESTARTSYS;
+ goto free;
+ }
+
+ v4l2_fh_init(&ctx->fh, vdev);
+ file->private_data = ctx;
+ v4l2_fh_add(&ctx->fh);
+
+ ctx->e5010 = e5010;
+ ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(e5010->m2m_dev, ctx, queue_init);
+ if (IS_ERR(ctx->fh.m2m_ctx)) {
+ v4l2_err(&e5010->v4l2_dev, "failed to init m2m ctx\n");
+ ret = PTR_ERR(ctx->fh.m2m_ctx);
+ goto exit;
+ }
+
+ ret = e5010_ctrls_setup(ctx);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to setup e5010 jpeg controls\n");
+ goto err_ctrls_setup;
+ }
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+
+ e5010_jpeg_set_default_params(ctx);
+
+ dprintk(e5010, 1, "Created instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx);
+
+ mutex_unlock(&e5010->mutex);
+ return 0;
+
+err_ctrls_setup:
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+exit:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ mutex_unlock(&e5010->mutex);
+free:
+ kfree(ctx);
+ return ret;
+}
+
+static int e5010_release(struct file *file)
+{
+ struct e5010_dev *e5010 = video_drvdata(file);
+ struct e5010_context *ctx = file->private_data;
+
+ dprintk(e5010, 1, "Releasing instance: 0x%p, m2m_ctx: 0x%p\n", ctx, ctx->fh.m2m_ctx);
+ mutex_lock(&e5010->mutex);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ mutex_unlock(&e5010->mutex);
+
+ return 0;
+}
+
+static void header_write(struct e5010_context *ctx, u8 *addr, unsigned int *offset,
+ unsigned int no_bytes, unsigned long bits)
+{
+ u8 *w_addr = addr + *offset;
+ int i;
+
+ if ((*offset + no_bytes) > HEADER_SIZE) {
+ v4l2_warn(&ctx->e5010->v4l2_dev, "%s: %s: %d: Problem writing header. %d > HEADER_SIZE %d\n",
+ __FILE__, __func__, __LINE__, *offset + no_bytes, HEADER_SIZE);
+ return;
+ }
+
+ for (i = no_bytes - 1; i >= 0; i--)
+ *(w_addr++) = ((u8 *)&bits)[i];
+
+ *offset += no_bytes;
+}
+
+static void encode_marker_segment(struct e5010_context *ctx, void *addr, unsigned int *offset)
+{
+ u8 *buffer = (u8 *)addr;
+ int i;
+
+ header_write(ctx, buffer, offset, 2, START_OF_IMAGE);
+ header_write(ctx, buffer, offset, 2, DQT_MARKER);
+ header_write(ctx, buffer, offset, 3, LQPQ << 4);
+ for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++)
+ header_write(ctx, buffer, offset, 1, ctx->luma_qp[v4l2_jpeg_zigzag_scan_index[i]]);
+
+ header_write(ctx, buffer, offset, 2, DQT_MARKER);
+ header_write(ctx, buffer, offset, 3, (LQPQ << 4) | 1);
+ for (i = 0; i < V4L2_JPEG_PIXELS_IN_BLOCK; i++)
+ header_write(ctx, buffer, offset, 1,
+ ctx->chroma_qp[v4l2_jpeg_zigzag_scan_index[i]]);
+
+ /* Huffman tables */
+ header_write(ctx, buffer, offset, 2, DHT_MARKER);
+ header_write(ctx, buffer, offset, 2, LH_DC);
+ header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_DC_HT);
+ for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++)
+ header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_dc_ht[i]);
+
+ header_write(ctx, buffer, offset, 2, DHT_MARKER);
+ header_write(ctx, buffer, offset, 2, LH_AC);
+ header_write(ctx, buffer, offset, 1, V4L2_JPEG_LUM_HT | V4L2_JPEG_AC_HT);
+ for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++)
+ header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_luma_ac_ht[i]);
+
+ header_write(ctx, buffer, offset, 2, DHT_MARKER);
+ header_write(ctx, buffer, offset, 2, LH_DC);
+ header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_DC_HT);
+ for (i = 0 ; i < V4L2_JPEG_REF_HT_DC_LEN; i++)
+ header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_dc_ht[i]);
+
+ header_write(ctx, buffer, offset, 2, DHT_MARKER);
+ header_write(ctx, buffer, offset, 2, LH_AC);
+ header_write(ctx, buffer, offset, 1, V4L2_JPEG_CHR_HT | V4L2_JPEG_AC_HT);
+ for (i = 0 ; i < V4L2_JPEG_REF_HT_AC_LEN; i++)
+ header_write(ctx, buffer, offset, 1, v4l2_jpeg_ref_table_chroma_ac_ht[i]);
+}
+
+static void encode_frame_header(struct e5010_context *ctx, void *addr, unsigned int *offset)
+{
+ u8 *buffer = (u8 *)addr;
+
+ header_write(ctx, buffer, offset, 2, SOF_BASELINE_DCT);
+ header_write(ctx, buffer, offset, 2, 8 + (3 * UC_NUM_COMP));
+ header_write(ctx, buffer, offset, 1, PRECISION);
+ header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.height);
+ header_write(ctx, buffer, offset, 2, ctx->out_queue.crop.width);
+ header_write(ctx, buffer, offset, 1, UC_NUM_COMP);
+
+ /* Luma details */
+ header_write(ctx, buffer, offset, 1, 1);
+ if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422)
+ header_write(ctx, buffer, offset, 1,
+ HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_422));
+ else
+ header_write(ctx, buffer, offset, 1,
+ HORZ_SAMPLING_FACTOR | (VERT_SAMPLING_FACTOR_420));
+ header_write(ctx, buffer, offset, 1, 0);
+ /* Chroma details */
+ header_write(ctx, buffer, offset, 1, 2);
+ header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1);
+ header_write(ctx, buffer, offset, 1, 1);
+ header_write(ctx, buffer, offset, 1, 3);
+ header_write(ctx, buffer, offset, 1, (HORZ_SAMPLING_FACTOR >> 1) | 1);
+ header_write(ctx, buffer, offset, 1, 1);
+}
+
+static void jpg_encode_sos_header(struct e5010_context *ctx, void *addr, unsigned int *offset)
+{
+ u8 *buffer = (u8 *)addr;
+ int i;
+
+ header_write(ctx, buffer, offset, 2, START_OF_SCAN);
+ header_write(ctx, buffer, offset, 2, 6 + (COMPONENTS_IN_SCAN << 1));
+ header_write(ctx, buffer, offset, 1, COMPONENTS_IN_SCAN);
+
+ for (i = 0; i < COMPONENTS_IN_SCAN; i++) {
+ header_write(ctx, buffer, offset, 1, i + 1);
+ if (i == 0)
+ header_write(ctx, buffer, offset, 1, 0);
+ else
+ header_write(ctx, buffer, offset, 1, 17);
+ }
+
+ header_write(ctx, buffer, offset, 1, 0);
+ header_write(ctx, buffer, offset, 1, 63);
+ header_write(ctx, buffer, offset, 1, 0);
+}
+
+static void write_header(struct e5010_context *ctx, void *addr)
+{
+ unsigned int offset = 0;
+
+ encode_marker_segment(ctx, addr, &offset);
+ encode_frame_header(ctx, addr, &offset);
+ jpg_encode_sos_header(ctx, addr, &offset);
+}
+
+static irqreturn_t e5010_irq(int irq, void *data)
+{
+ struct e5010_dev *e5010 = data;
+ struct e5010_context *ctx;
+ int output_size;
+ struct vb2_v4l2_buffer *src_buf, *dst_buf;
+ bool pic_done, out_addr_err;
+
+ spin_lock(&e5010->hw_lock);
+ pic_done = e5010_hw_pic_done_irq(e5010->core_base);
+ out_addr_err = e5010_hw_output_address_irq(e5010->core_base);
+
+ if (!pic_done && !out_addr_err) {
+ spin_unlock(&e5010->hw_lock);
+ return IRQ_NONE;
+ }
+
+ ctx = v4l2_m2m_get_curr_priv(e5010->m2m_dev);
+ if (WARN_ON(!ctx))
+ goto job_unlock;
+
+ dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ if (!dst_buf || !src_buf) {
+ v4l2_err(&e5010->v4l2_dev, "ctx: 0x%p No source or destination buffer\n", ctx);
+ goto job_unlock;
+ }
+
+ if (out_addr_err) {
+ e5010_hw_clear_output_error(e5010->core_base, 1);
+ v4l2_warn(&e5010->v4l2_dev,
+ "ctx: 0x%p Output bitstream size exceeded max size\n", ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, dst_buf->planes[0].length);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+ if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) {
+ dst_buf->flags |= V4L2_BUF_FLAG_LAST;
+ v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx);
+ v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event);
+ dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx);
+ }
+ }
+
+ if (pic_done) {
+ e5010_hw_clear_picture_done(e5010->core_base, 1);
+ dprintk(e5010, 3, "ctx: 0x%p Got output bitstream of size %d bytes\n",
+ ctx, readl(e5010->core_base + JASPER_OUTPUT_SIZE_OFFSET));
+
+ if (v4l2_m2m_is_last_draining_src_buf(ctx->fh.m2m_ctx, src_buf)) {
+ dst_buf->flags |= V4L2_BUF_FLAG_LAST;
+ v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx);
+ v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event);
+ dprintk(e5010, 2, "ctx: 0x%p Sending EOS\n", ctx);
+ }
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
+ output_size = e5010_hw_get_output_size(e5010->core_base);
+ vb2_set_plane_payload(&dst_buf->vb2_buf, 0, output_size + HEADER_SIZE);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
+ dprintk(e5010, 3,
+ "ctx: 0x%p frame done for dst_buf->sequence: %d src_buf->sequence: %d\n",
+ ctx, dst_buf->sequence, src_buf->sequence);
+ }
+
+ v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx);
+ dprintk(e5010, 3, "ctx: 0x%p Finish job\n", ctx);
+
+job_unlock:
+ spin_unlock(&e5010->hw_lock);
+ return IRQ_HANDLED;
+}
+
+static int e5010_init_device(struct e5010_dev *e5010)
+{
+ int ret = 0;
+
+ /*TODO: Set MMU in bypass mode until support for the same is added in driver*/
+ e5010_hw_bypass_mmu(e5010->mmu_base, 1);
+
+ if (e5010_hw_enable_auto_clock_gating(e5010->core_base, 1))
+ v4l2_warn(&e5010->v4l2_dev, "failed to enable auto clock gating\n");
+
+ if (e5010_hw_enable_manual_clock_gating(e5010->core_base, 0))
+ v4l2_warn(&e5010->v4l2_dev, "failed to disable manual clock gating\n");
+
+ if (e5010_hw_enable_crc_check(e5010->core_base, 0))
+ v4l2_warn(&e5010->v4l2_dev, "failed to disable CRC check\n");
+
+ if (e5010_hw_enable_output_address_error_irq(e5010->core_base, 1))
+ v4l2_err(&e5010->v4l2_dev, "failed to enable Output Address Error interrupts\n");
+
+ ret = e5010_hw_set_input_source_to_memory(e5010->core_base, 1);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set input source to memory\n");
+ return ret;
+ }
+
+ ret = e5010_hw_enable_picture_done_irq(e5010->core_base, 1);
+ if (ret)
+ v4l2_err(&e5010->v4l2_dev, "failed to enable Picture Done interrupts\n");
+
+ return ret;
+}
+
+static int e5010_probe(struct platform_device *pdev)
+{
+ struct e5010_dev *e5010;
+ int irq, ret = 0;
+ struct device *dev = &pdev->dev;
+
+ ret = dma_set_mask(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(dev, ret, "32-bit consistent DMA enable failed\n");
+
+ e5010 = devm_kzalloc(dev, sizeof(*e5010), GFP_KERNEL);
+ if (!e5010)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, e5010);
+
+ e5010->dev = dev;
+
+ mutex_init(&e5010->mutex);
+ spin_lock_init(&e5010->hw_lock);
+
+ e5010->vdev = video_device_alloc();
+ if (!e5010->vdev) {
+ dev_err(dev, "failed to allocate video device\n");
+ return -ENOMEM;
+ }
+
+ snprintf(e5010->vdev->name, sizeof(e5010->vdev->name), "%s", E5010_MODULE_NAME);
+ e5010->vdev->fops = &e5010_fops;
+ e5010->vdev->ioctl_ops = &e5010_ioctl_ops;
+ e5010->vdev->minor = -1;
+ e5010->vdev->release = video_device_release;
+ e5010->vdev->vfl_dir = VFL_DIR_M2M;
+ e5010->vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+ e5010->vdev->v4l2_dev = &e5010->v4l2_dev;
+ e5010->vdev->lock = &e5010->mutex;
+
+ ret = v4l2_device_register(dev, &e5010->v4l2_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register v4l2 device\n");
+ goto fail_after_video_device_alloc;
+ }
+
+
+ e5010->m2m_dev = v4l2_m2m_init(&e5010_m2m_ops);
+ if (IS_ERR(e5010->m2m_dev)) {
+ ret = PTR_ERR(e5010->m2m_dev);
+ e5010->m2m_dev = NULL;
+ dev_err_probe(dev, ret, "failed to init mem2mem device\n");
+ goto fail_after_v4l2_register;
+ }
+
+ video_set_drvdata(e5010->vdev, e5010);
+
+ e5010->core_base = devm_platform_ioremap_resource_byname(pdev, "core");
+ if (IS_ERR(e5010->core_base)) {
+ ret = PTR_ERR(e5010->core_base);
+ dev_err_probe(dev, ret, "Missing 'core' resources area\n");
+ goto fail_after_v4l2_register;
+ }
+
+ e5010->mmu_base = devm_platform_ioremap_resource_byname(pdev, "mmu");
+ if (IS_ERR(e5010->mmu_base)) {
+ ret = PTR_ERR(e5010->mmu_base);
+ dev_err_probe(dev, ret, "Missing 'mmu' resources area\n");
+ goto fail_after_v4l2_register;
+ }
+
+ e5010->last_context_run = NULL;
+
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(dev, irq, e5010_irq, 0,
+ E5010_MODULE_NAME, e5010);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register IRQ %d\n", irq);
+ goto fail_after_v4l2_register;
+ }
+
+ e5010->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(e5010->clk)) {
+ ret = PTR_ERR(e5010->clk);
+ dev_err_probe(dev, ret, "failed to get clock\n");
+ goto fail_after_v4l2_register;
+ }
+
+ pm_runtime_enable(dev);
+
+ ret = video_register_device(e5010->vdev, VFL_TYPE_VIDEO, 0);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to register video device\n");
+ goto fail_after_video_register_device;
+ }
+
+ v4l2_info(&e5010->v4l2_dev, "Device registered as /dev/video%d\n",
+ e5010->vdev->num);
+
+ return 0;
+
+fail_after_video_register_device:
+ v4l2_m2m_release(e5010->m2m_dev);
+fail_after_v4l2_register:
+ v4l2_device_unregister(&e5010->v4l2_dev);
+fail_after_video_device_alloc:
+ video_device_release(e5010->vdev);
+ return ret;
+}
+
+static void e5010_remove(struct platform_device *pdev)
+{
+ struct e5010_dev *e5010 = platform_get_drvdata(pdev);
+
+ pm_runtime_disable(e5010->dev);
+ video_unregister_device(e5010->vdev);
+ v4l2_m2m_release(e5010->m2m_dev);
+ v4l2_device_unregister(&e5010->v4l2_dev);
+}
+
+static void e5010_vb2_buffers_return(struct vb2_queue *q, enum vb2_buffer_state state)
+{
+ struct vb2_v4l2_buffer *vbuf;
+ struct e5010_context *ctx = vb2_get_drv_priv(q);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type)) {
+ while ((vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) {
+ dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n",
+ ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index);
+ v4l2_m2m_buf_done(vbuf, state);
+ }
+ } else {
+ while ((vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) {
+ dprintk(ctx->e5010, 2, "ctx: 0x%p, buf type %s | index %d\n",
+ ctx, type_name(vbuf->vb2_buf.type), vbuf->vb2_buf.index);
+ vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0);
+ v4l2_m2m_buf_done(vbuf, state);
+ }
+ }
+}
+
+static int e5010_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct e5010_context *ctx = vb2_get_drv_priv(vq);
+ struct e5010_q_data *queue;
+ int i;
+
+ queue = get_queue(ctx, vq->type);
+
+ if (*nplanes) {
+ if (*nplanes != queue->fmt->num_planes)
+ return -EINVAL;
+ for (i = 0; i < *nplanes; i++) {
+ if (sizes[i] < queue->sizeimage[i])
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ *nplanes = queue->fmt->num_planes;
+ for (i = 0; i < *nplanes; i++)
+ sizes[i] = queue->sizeimage[i];
+
+ dprintk(ctx->e5010, 2,
+ "ctx: 0x%p, type %s, buffer(s): %d, planes %d, plane1: bytes %d plane2: %d bytes\n",
+ ctx, type_name(vq->type), *nbuffers, *nplanes, sizes[0], sizes[1]);
+
+ return 0;
+}
+
+static void e5010_buf_finish(struct vb2_buffer *vb)
+{
+ struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ void *d_addr;
+
+ if (vb->state != VB2_BUF_STATE_DONE || V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+ return;
+
+ d_addr = vb2_plane_vaddr(vb, 0);
+ write_header(ctx, d_addr);
+}
+
+static int e5010_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue);
+
+ if (vbuf->field != V4L2_FIELD_NONE)
+ dprintk(ctx->e5010, 1, "ctx: 0x%p, field isn't supported\n", ctx);
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+static int e5010_buf_prepare(struct vb2_buffer *vb)
+{
+ struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct e5010_q_data *queue;
+ int i;
+
+ vbuf->field = V4L2_FIELD_NONE;
+
+ queue = get_queue(ctx, vb->vb2_queue->type);
+
+ for (i = 0; i < queue->fmt->num_planes; i++) {
+ if (vb2_plane_size(vb, i) < (unsigned long)queue->sizeimage[i]) {
+ v4l2_err(&ctx->e5010->v4l2_dev, "plane %d too small (%lu < %lu)", i,
+ vb2_plane_size(vb, i), (unsigned long)queue->sizeimage[i]);
+
+ return -EINVAL;
+ }
+ }
+
+ if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
+ vb2_set_plane_payload(vb, 0, 0);
+ vb2_set_plane_payload(vb, 1, 0);
+ }
+
+ return 0;
+}
+
+static void e5010_buf_queue(struct vb2_buffer *vb)
+{
+ struct e5010_context *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type) &&
+ vb2_is_streaming(vb->vb2_queue) &&
+ v4l2_m2m_dst_buf_is_last(ctx->fh.m2m_ctx)) {
+ struct e5010_q_data *queue = get_queue(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ vbuf->sequence = queue->sequence++;
+ v4l2_m2m_last_buffer_done(ctx->fh.m2m_ctx, vbuf);
+ v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event);
+ return;
+ }
+
+ v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
+}
+
+static int e5010_encoder_cmd(struct file *file, void *priv,
+ struct v4l2_encoder_cmd *cmd)
+{
+ struct e5010_context *ctx = file->private_data;
+ int ret;
+ struct vb2_queue *cap_vq;
+
+ cap_vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ ret = v4l2_m2m_ioctl_try_encoder_cmd(file, &ctx->fh, cmd);
+ if (ret < 0)
+ return ret;
+
+ if (!vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)) ||
+ !vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)))
+ return 0;
+
+ ret = v4l2_m2m_ioctl_encoder_cmd(file, &ctx->fh, cmd);
+ if (ret < 0)
+ return ret;
+
+ if (cmd->cmd == V4L2_ENC_CMD_STOP &&
+ v4l2_m2m_has_stopped(ctx->fh.m2m_ctx))
+ v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event);
+
+ if (cmd->cmd == V4L2_ENC_CMD_START &&
+ v4l2_m2m_has_stopped(ctx->fh.m2m_ctx))
+ vb2_clear_last_buffer_dequeued(cap_vq);
+
+ return 0;
+}
+
+static int e5010_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct e5010_context *ctx = vb2_get_drv_priv(q);
+ int ret;
+
+ struct e5010_q_data *queue = get_queue(ctx, q->type);
+
+ v4l2_m2m_update_start_streaming_state(ctx->fh.m2m_ctx, q);
+ queue->sequence = 0;
+
+ ret = pm_runtime_resume_and_get(ctx->e5010->dev);
+ if (ret < 0) {
+ v4l2_err(&ctx->e5010->v4l2_dev, "failed to power up jpeg\n");
+ goto fail;
+ }
+
+ ret = e5010_init_device(ctx->e5010);
+ if (ret) {
+ v4l2_err(&ctx->e5010->v4l2_dev, "failed to Enable e5010 device\n");
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ e5010_vb2_buffers_return(q, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void e5010_stop_streaming(struct vb2_queue *q)
+{
+ struct e5010_context *ctx = vb2_get_drv_priv(q);
+
+ e5010_vb2_buffers_return(q, VB2_BUF_STATE_ERROR);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, q);
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type) &&
+ v4l2_m2m_has_stopped(ctx->fh.m2m_ctx)) {
+ v4l2_event_queue_fh(&ctx->fh, &e5010_eos_event);
+ }
+
+ pm_runtime_put_sync(ctx->e5010->dev);
+}
+
+static void e5010_device_run(void *priv)
+{
+ struct e5010_context *ctx = priv;
+ struct e5010_dev *e5010 = ctx->e5010;
+ struct vb2_v4l2_buffer *s_vb, *d_vb;
+ u32 reg = 0;
+ int ret = 0, luma_crop_offset = 0, chroma_crop_offset = 0;
+ unsigned long flags;
+ int num_planes = ctx->out_queue.fmt->num_planes;
+
+ spin_lock_irqsave(&e5010->hw_lock, flags);
+ s_vb = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ WARN_ON(!s_vb);
+ d_vb = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
+ WARN_ON(!d_vb);
+ if (!s_vb || !d_vb)
+ goto no_ready_buf_err;
+
+ s_vb->sequence = ctx->out_queue.sequence++;
+ d_vb->sequence = ctx->cap_queue.sequence++;
+
+ v4l2_m2m_buf_copy_metadata(s_vb, d_vb, false);
+
+ if (ctx != e5010->last_context_run || ctx->update_qp) {
+ dprintk(e5010, 1, "ctx updated: 0x%p -> 0x%p, updating qp tables\n",
+ e5010->last_context_run, ctx);
+ ret = update_qp_tables(ctx);
+ }
+
+ if (ret) {
+ ctx->update_qp = true;
+ v4l2_err(&e5010->v4l2_dev, "failed to update QP tables\n");
+ goto device_busy_err;
+ } else {
+ e5010->last_context_run = ctx;
+ ctx->update_qp = false;
+ }
+
+ /* Set I/O Buffer addresses */
+ reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0);
+
+ if (ctx->out_queue.crop_set) {
+ luma_crop_offset = ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top +
+ ctx->out_queue.crop.left;
+
+ if (ctx->out_queue.fmt->subsampling == V4L2_JPEG_CHROMA_SUBSAMPLING_422) {
+ chroma_crop_offset =
+ ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top
+ + ctx->out_queue.crop.left;
+ } else {
+ chroma_crop_offset =
+ ctx->out_queue.bytesperline[0] * ctx->out_queue.crop.top / 2
+ + ctx->out_queue.crop.left;
+ }
+
+ dprintk(e5010, 1, "Luma crop offset : %x, chroma crop offset : %x\n",
+ luma_crop_offset, chroma_crop_offset);
+ }
+
+ ret = e5010_hw_set_input_luma_addr(e5010->core_base, reg + luma_crop_offset);
+ if (ret || !reg) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set input luma address\n");
+ goto device_busy_err;
+ }
+
+ if (num_planes == 1)
+ reg += (ctx->out_queue.bytesperline[0]) * (ctx->out_queue.height);
+ else
+ reg = (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 1);
+
+ dprintk(e5010, 3,
+ "ctx: 0x%p, luma_addr: 0x%x, chroma_addr: 0x%x, out_addr: 0x%x\n",
+ ctx, (u32)vb2_dma_contig_plane_dma_addr(&s_vb->vb2_buf, 0) + luma_crop_offset,
+ reg + chroma_crop_offset, (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0));
+
+ dprintk(e5010, 3,
+ "ctx: 0x%p, buf indices: src_index: %d, dst_index: %d\n",
+ ctx, s_vb->vb2_buf.index, d_vb->vb2_buf.index);
+
+ ret = e5010_hw_set_input_chroma_addr(e5010->core_base, reg + chroma_crop_offset);
+ if (ret || !reg) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set input chroma address\n");
+ goto device_busy_err;
+ }
+
+ reg = (u32)vb2_dma_contig_plane_dma_addr(&d_vb->vb2_buf, 0);
+ reg += HEADER_SIZE;
+ ret = e5010_hw_set_output_base_addr(e5010->core_base, reg);
+ if (ret || !reg) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set output base address\n");
+ goto device_busy_err;
+ }
+
+ /* Set input settings */
+ ret = e5010_hw_set_horizontal_size(e5010->core_base, ctx->out_queue.crop.width - 1);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set input width\n");
+ goto device_busy_err;
+ }
+
+ ret = e5010_hw_set_vertical_size(e5010->core_base, ctx->out_queue.crop.height - 1);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set input width\n");
+ goto device_busy_err;
+ }
+
+ ret = e5010_hw_set_luma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set luma stride\n");
+ goto device_busy_err;
+ }
+
+ ret = e5010_hw_set_chroma_stride(e5010->core_base, ctx->out_queue.bytesperline[0]);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set chroma stride\n");
+ goto device_busy_err;
+ }
+
+ ret = e5010_set_input_subsampling(e5010->core_base, ctx->out_queue.fmt->subsampling);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set input subsampling\n");
+ goto device_busy_err;
+ }
+
+ ret = e5010_hw_set_chroma_order(e5010->core_base, ctx->out_queue.fmt->chroma_order);
+ if (ret) {
+ v4l2_err(&e5010->v4l2_dev, "failed to set chroma order\n");
+ goto device_busy_err;
+ }
+
+ e5010_hw_set_output_max_size(e5010->core_base, d_vb->planes[0].length);
+ e5010_hw_encode_start(e5010->core_base, 1);
+
+ spin_unlock_irqrestore(&e5010->hw_lock, flags);
+
+ return;
+
+device_busy_err:
+ e5010_reset(e5010->dev, e5010->core_base, e5010->mmu_base);
+
+no_ready_buf_err:
+ if (s_vb) {
+ v4l2_m2m_src_buf_remove_by_buf(ctx->fh.m2m_ctx, s_vb);
+ v4l2_m2m_buf_done(s_vb, VB2_BUF_STATE_ERROR);
+ }
+
+ if (d_vb) {
+ v4l2_m2m_dst_buf_remove_by_buf(ctx->fh.m2m_ctx, d_vb);
+ /* Payload set to 1 since 0 payload can trigger EOS */
+ vb2_set_plane_payload(&d_vb->vb2_buf, 0, 1);
+ v4l2_m2m_buf_done(d_vb, VB2_BUF_STATE_ERROR);
+ }
+ v4l2_m2m_job_finish(e5010->m2m_dev, ctx->fh.m2m_ctx);
+ spin_unlock_irqrestore(&e5010->hw_lock, flags);
+}
+
+#ifdef CONFIG_PM
+static int e5010_runtime_resume(struct device *dev)
+{
+ struct e5010_dev *e5010 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(e5010->clk);
+ if (ret < 0) {
+ v4l2_err(&e5010->v4l2_dev, "failed to enable clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int e5010_runtime_suspend(struct device *dev)
+{
+ struct e5010_dev *e5010 = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(e5010->clk);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int e5010_suspend(struct device *dev)
+{
+ struct e5010_dev *e5010 = dev_get_drvdata(dev);
+
+ v4l2_m2m_suspend(e5010->m2m_dev);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int e5010_resume(struct device *dev)
+{
+ struct e5010_dev *e5010 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = e5010_init_device(e5010);
+ if (ret) {
+ dev_err(dev, "Failed to re-enable e5010 device\n");
+ return ret;
+ }
+
+ v4l2_m2m_resume(e5010->m2m_dev);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops e5010_pm_ops = {
+ SET_RUNTIME_PM_OPS(e5010_runtime_suspend,
+ e5010_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(e5010_suspend, e5010_resume)
+};
+
+static const struct v4l2_ioctl_ops e5010_ioctl_ops = {
+ .vidioc_querycap = e5010_querycap,
+
+ .vidioc_enum_fmt_vid_cap = e5010_enum_fmt,
+ .vidioc_g_fmt_vid_cap_mplane = e5010_g_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = e5010_try_fmt,
+ .vidioc_s_fmt_vid_cap_mplane = e5010_s_fmt,
+
+ .vidioc_enum_fmt_vid_out = e5010_enum_fmt,
+ .vidioc_g_fmt_vid_out_mplane = e5010_g_fmt,
+ .vidioc_try_fmt_vid_out_mplane = e5010_try_fmt,
+ .vidioc_s_fmt_vid_out_mplane = e5010_s_fmt,
+
+ .vidioc_g_selection = e5010_g_selection,
+ .vidioc_s_selection = e5010_s_selection,
+
+ .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_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+
+ .vidioc_subscribe_event = e5010_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_try_encoder_cmd = v4l2_m2m_ioctl_try_encoder_cmd,
+ .vidioc_encoder_cmd = e5010_encoder_cmd,
+
+ .vidioc_enum_framesizes = e5010_enum_framesizes,
+};
+
+static const struct vb2_ops e5010_video_ops = {
+ .queue_setup = e5010_queue_setup,
+ .buf_queue = e5010_buf_queue,
+ .buf_finish = e5010_buf_finish,
+ .buf_prepare = e5010_buf_prepare,
+ .buf_out_validate = e5010_buf_out_validate,
+ .start_streaming = e5010_start_streaming,
+ .stop_streaming = e5010_stop_streaming,
+};
+
+static const struct v4l2_file_operations e5010_fops = {
+ .owner = THIS_MODULE,
+ .open = e5010_open,
+ .release = e5010_release,
+ .poll = v4l2_m2m_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct v4l2_m2m_ops e5010_m2m_ops = {
+ .device_run = e5010_device_run,
+};
+
+static const struct of_device_id e5010_of_match[] = {
+ {.compatible = "img,e5010-jpeg-enc"}, { /* end */},
+};
+MODULE_DEVICE_TABLE(of, e5010_of_match);
+
+static struct platform_driver e5010_driver = {
+ .probe = e5010_probe,
+ .remove = e5010_remove,
+ .driver = {
+ .name = E5010_MODULE_NAME,
+ .of_match_table = e5010_of_match,
+ .pm = &e5010_pm_ops,
+ },
+};
+module_platform_driver(e5010_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Imagination E5010 JPEG encoder driver");
diff --git a/drivers/media/platform/imagination/e5010-jpeg-enc.h b/drivers/media/platform/imagination/e5010-jpeg-enc.h
new file mode 100644
index 000000000000..71f49ead6898
--- /dev/null
+++ b/drivers/media/platform/imagination/e5010-jpeg-enc.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Imagination E5010 JPEG Encoder driver.
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Author: David Huang <d-huang@ti.com>
+ * Author: Devarsh Thakkar <devarsht@ti.com>
+ */
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+
+#ifndef _E5010_JPEG_ENC_H
+#define _E5010_JPEG_ENC_H
+
+#define MAX_PLANES 2
+#define HEADER_SIZE 0x025D
+#define MIN_DIMENSION 64
+#define MAX_DIMENSION 8192
+#define DEFAULT_WIDTH 640
+#define DEFAULT_HEIGHT 480
+#define E5010_MODULE_NAME "e5010"
+#define JPEG_MAX_BYTES_PER_PIXEL 2
+
+/* JPEG marker definitions */
+#define START_OF_IMAGE 0xFFD8
+#define SOF_BASELINE_DCT 0xFFC0
+#define END_OF_IMAGE 0xFFD9
+#define START_OF_SCAN 0xFFDA
+
+/* Definitions for the huffman table specification in the Marker segment */
+#define DHT_MARKER 0xFFC4
+#define LH_DC 0x001F
+#define LH_AC 0x00B5
+
+/* Definitions for the quantization table specification in the Marker segment */
+#define DQT_MARKER 0xFFDB
+#define ACMAX 0x03FF
+#define DCMAX 0x07FF
+
+/* Length and precision of the quantization table parameters */
+#define LQPQ 0x00430
+#define QMAX 255
+
+/* Misc JPEG header definitions */
+#define UC_NUM_COMP 3
+#define PRECISION 8
+#define HORZ_SAMPLING_FACTOR (2 << 4)
+#define VERT_SAMPLING_FACTOR_422 1
+#define VERT_SAMPLING_FACTOR_420 2
+#define COMPONENTS_IN_SCAN 3
+#define PELS_IN_BLOCK 64
+
+/* Used for Qp table generation */
+#define LUMINOSITY 10
+#define CONTRAST 1
+#define INCREASE 2
+#define QP_TABLE_SIZE (8 * 8)
+#define QP_TABLE_FIELD_OFFSET 0x04
+
+/*
+ * vb2 queue structure
+ * contains queue data information
+ *
+ * @fmt: format info
+ * @width: frame width
+ * @height: frame height
+ * @bytesperline: bytes per line in memory
+ * @size_image: image size in memory
+ */
+struct e5010_q_data {
+ struct e5010_fmt *fmt;
+ u32 width;
+ u32 height;
+ u32 width_adjusted;
+ u32 height_adjusted;
+ u32 sizeimage[MAX_PLANES];
+ u32 bytesperline[MAX_PLANES];
+ u32 sequence;
+ struct v4l2_rect crop;
+ bool crop_set;
+};
+
+/*
+ * Driver device structure
+ * Holds all memory handles and global parameters
+ * Shared by all instances
+ */
+struct e5010_dev {
+ struct device *dev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct video_device *vdev;
+ void __iomem *core_base;
+ void __iomem *mmu_base;
+ struct clk *clk;
+ struct e5010_context *last_context_run;
+ /* Protect access to device data */
+ struct mutex mutex;
+ /* Protect access to hardware*/
+ spinlock_t hw_lock;
+};
+
+/*
+ * Driver context structure
+ * One of these exists for every m2m context
+ * Holds context specific data
+ */
+struct e5010_context {
+ struct v4l2_fh fh;
+ struct e5010_dev *e5010;
+ struct e5010_q_data out_queue;
+ struct e5010_q_data cap_queue;
+ int quality;
+ bool update_qp;
+ struct v4l2_ctrl_handler ctrl_handler;
+ u8 luma_qp[QP_TABLE_SIZE];
+ u8 chroma_qp[QP_TABLE_SIZE];
+};
+
+/*
+ * Buffer structure
+ * Contains info for all buffers
+ */
+struct e5010_buffer {
+ struct v4l2_m2m_buffer buffer;
+};
+
+enum {
+ CHROMA_ORDER_CB_CR = 0, //UV ordering
+ CHROMA_ORDER_CR_CB = 1, //VU ordering
+};
+
+enum {
+ SUBSAMPLING_420 = 1,
+ SUBSAMPLING_422 = 2,
+};
+
+/*
+ * e5010 format structure
+ * contains format information
+ */
+struct e5010_fmt {
+ u32 fourcc;
+ unsigned int num_planes;
+ unsigned int type;
+ u32 subsampling;
+ u32 chroma_order;
+ const struct v4l2_frmsize_stepwise frmsize;
+};
+
+/*
+ * struct e5010_ctrl - contains info for each supported v4l2 control
+ */
+struct e5010_ctrl {
+ unsigned int cid;
+ enum v4l2_ctrl_type type;
+ unsigned char name[32];
+ int minimum;
+ int maximum;
+ int step;
+ int default_value;
+ unsigned char compound;
+};
+
+#endif
diff --git a/drivers/media/platform/imagination/e5010-mmu-regs.h b/drivers/media/platform/imagination/e5010-mmu-regs.h
new file mode 100644
index 000000000000..bfba06956cf2
--- /dev/null
+++ b/drivers/media/platform/imagination/e5010-mmu-regs.h
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Imagination E5010 JPEG Encoder driver.
+ *
+ * Copyright (C) 2023 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * Author: David Huang <d-huang@ti.com>
+ * Author: Devarsh Thakkar <devarsht@ti.com>
+ */
+
+#ifndef _E5010_MMU_REGS_H
+#define _E5010_MMU_REGS_H
+
+#define MMU_MMU_DIR_BASE_ADDR_OFFSET (0x0020)
+#define MMU_MMU_DIR_BASE_ADDR_STRIDE (4)
+#define MMU_MMU_DIR_BASE_ADDR_NO_ENTRIES (4)
+
+#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_MASK (0xFFFFFFFF)
+#define MMU_MMU_DIR_BASE_ADDR_MMU_DIR_BASE_ADDR_SHIFT (0)
+
+#define MMU_MMU_TILE_CFG_OFFSET (0x0040)
+#define MMU_MMU_TILE_CFG_STRIDE (4)
+#define MMU_MMU_TILE_CFG_NO_ENTRIES (4)
+
+#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_MASK (0x00000010)
+#define MMU_MMU_TILE_CFG_TILE_128INTERLEAVE_SHIFT (4)
+
+#define MMU_MMU_TILE_CFG_TILE_ENABLE_MASK (0x00000008)
+#define MMU_MMU_TILE_CFG_TILE_ENABLE_SHIFT (3)
+
+#define MMU_MMU_TILE_CFG_TILE_STRIDE_MASK (0x00000007)
+#define MMU_MMU_TILE_CFG_TILE_STRIDE_SHIFT (0)
+
+#define MMU_MMU_TILE_MIN_ADDR_OFFSET (0x0050)
+#define MMU_MMU_TILE_MIN_ADDR_STRIDE (4)
+#define MMU_MMU_TILE_MIN_ADDR_NO_ENTRIES (4)
+
+#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_MASK (0xFFFFFFFF)
+#define MMU_MMU_TILE_MIN_ADDR_TILE_MIN_ADDR_SHIFT (0)
+
+#define MMU_MMU_TILE_MAX_ADDR_OFFSET (0x0060)
+#define MMU_MMU_TILE_MAX_ADDR_STRIDE (4)
+#define MMU_MMU_TILE_MAX_ADDR_NO_ENTRIES (4)
+
+#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_MASK (0xFFFFFFFF)
+#define MMU_MMU_TILE_MAX_ADDR_TILE_MAX_ADDR_SHIFT (0)
+
+#define MMU_MMU_CONTROL0_OFFSET (0x0000)
+
+#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_MASK (0x00000001)
+#define MMU_MMU_CONTROL0_MMU_TILING_SCHEME_SHIFT (0)
+
+#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_MASK (0x00000100)
+#define MMU_MMU_CONTROL0_MMU_CACHE_POLICY_SHIFT (8)
+
+#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_MASK (0x00000200)
+#define MMU_MMU_CONTROL0_FORCE_CACHE_POLICY_BYPASS_SHIFT (9)
+
+#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_MASK (0x00001000)
+#define MMU_MMU_CONTROL0_STALL_ON_PROTOCOL_FAULT_SHIFT (12)
+
+#define MMU_MMU_CONTROL1_OFFSET (0x0008)
+
+#define MMU_MMU_CONTROL1_MMU_FLUSH_MASK (0x00000008)
+#define MMU_MMU_CONTROL1_MMU_FLUSH_SHIFT (3)
+#define MMU_MMU_CONTROL1_MMU_FLUSH_NO_REPS (4)
+#define MMU_MMU_CONTROL1_MMU_FLUSH_SIZE (1)
+
+#define MMU_MMU_CONTROL1_MMU_INVALDC_MASK (0x00000800)
+#define MMU_MMU_CONTROL1_MMU_INVALDC_SHIFT (11)
+#define MMU_MMU_CONTROL1_MMU_INVALDC_NO_REPS (4)
+#define MMU_MMU_CONTROL1_MMU_INVALDC_SIZE (1)
+
+#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_MASK (0x00010000)
+#define MMU_MMU_CONTROL1_MMU_FAULT_CLEAR_SHIFT (16)
+
+#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_MASK (0x00100000)
+#define MMU_MMU_CONTROL1_PROTOCOL_FAULT_CLEAR_SHIFT (20)
+
+#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_MASK (0x01000000)
+#define MMU_MMU_CONTROL1_MMU_PAUSE_SET_SHIFT (24)
+
+#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_MASK (0x02000000)
+#define MMU_MMU_CONTROL1_MMU_PAUSE_CLEAR_SHIFT (25)
+
+#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_MASK (0x10000000)
+#define MMU_MMU_CONTROL1_MMU_SOFT_RESET_SHIFT (28)
+
+#define MMU_MMU_BANK_INDEX_OFFSET (0x0010)
+
+#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_MASK (0xC0000000)
+#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SHIFT (30)
+#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_NO_REPS (16)
+#define MMU_MMU_BANK_INDEX_MMU_BANK_INDEX_SIZE (2)
+
+#define MMU_REQUEST_PRIORITY_ENABLE_OFFSET (0x0018)
+
+#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_MASK (0x00008000)
+#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SHIFT (15)
+#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_NO_REPS (16)
+#define MMU_REQUEST_PRIORITY_ENABLE_CMD_PRIORITY_ENABLE_SIZE (1)
+
+#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_MASK (0x00010000)
+#define MMU_REQUEST_PRIORITY_ENABLE_CMD_MMU_PRIORITY_ENABLE_SHIFT (16)
+
+#define MMU_REQUEST_LIMITED_THROUGHPUT_OFFSET (0x001C)
+
+#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_MASK (0x000003FF)
+#define MMU_REQUEST_LIMITED_THROUGHPUT_LIMITED_WORDS_SHIFT (0)
+
+#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_MASK (0x0FFF0000)
+#define MMU_REQUEST_LIMITED_THROUGHPUT_REQUEST_GAP_SHIFT (16)
+
+#define MMU_MMU_ADDRESS_CONTROL_OFFSET (0x0070)
+#define MMU_MMU_ADDRESS_CONTROL_TRUSTED (IMG_TRUE)
+
+#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_MASK (0x00000001)
+#define MMU_MMU_ADDRESS_CONTROL_MMU_BYPASS_SHIFT (0)
+
+#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_MASK (0x00000010)
+#define MMU_MMU_ADDRESS_CONTROL_MMU_ENABLE_EXT_ADDRESSING_SHIFT (4)
+
+#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_MASK (0x00FF0000)
+#define MMU_MMU_ADDRESS_CONTROL_UPPER_ADDRESS_FIXED_SHIFT (16)
+
+#define MMU_MMU_CONFIG0_OFFSET (0x0080)
+
+#define MMU_MMU_CONFIG0_NUM_REQUESTORS_MASK (0x0000000F)
+#define MMU_MMU_CONFIG0_NUM_REQUESTORS_SHIFT (0)
+
+#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_MASK (0x000000F0)
+#define MMU_MMU_CONFIG0_EXTENDED_ADDR_RANGE_SHIFT (4)
+
+#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_MASK (0x00000700)
+#define MMU_MMU_CONFIG0_GROUP_OVERRIDE_SIZE_SHIFT (8)
+
+#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_MASK (0x00001000)
+#define MMU_MMU_CONFIG0_ADDR_COHERENCY_SUPPORTED_SHIFT (12)
+
+#define MMU_MMU_CONFIG0_MMU_SUPPORTED_MASK (0x00002000)
+#define MMU_MMU_CONFIG0_MMU_SUPPORTED_SHIFT (13)
+
+#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_MASK (0x001F0000)
+#define MMU_MMU_CONFIG0_TILE_ADDR_GRANULARITY_SHIFT (16)
+
+#define MMU_MMU_CONFIG0_NO_READ_REORDER_MASK (0x00200000)
+#define MMU_MMU_CONFIG0_NO_READ_REORDER_SHIFT (21)
+
+#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_MASK (0xFFC00000)
+#define MMU_MMU_CONFIG0_TAGS_SUPPORTED_SHIFT (22)
+
+#define MMU_MMU_CONFIG1_OFFSET (0x0084)
+
+#define MMU_MMU_CONFIG1_PAGE_SIZE_MASK (0x0000000F)
+#define MMU_MMU_CONFIG1_PAGE_SIZE_SHIFT (0)
+
+#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_MASK (0x0000FF00)
+#define MMU_MMU_CONFIG1_PAGE_CACHE_ENTRIES_SHIFT (8)
+
+#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_MASK (0x001F0000)
+#define MMU_MMU_CONFIG1_DIR_CACHE_ENTRIES_SHIFT (16)
+
+#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_MASK (0x01000000)
+#define MMU_MMU_CONFIG1_BANDWIDTH_COUNT_SUPPORTED_SHIFT (24)
+
+#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_MASK (0x02000000)
+#define MMU_MMU_CONFIG1_STALL_COUNT_SUPPORTED_SHIFT (25)
+
+#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_MASK (0x04000000)
+#define MMU_MMU_CONFIG1_LATENCY_COUNT_SUPPORTED_SHIFT (26)
+
+#define MMU_MMU_STATUS0_OFFSET (0x0088)
+
+#define MMU_MMU_STATUS0_MMU_PF_N_RW_MASK (0x00000001)
+#define MMU_MMU_STATUS0_MMU_PF_N_RW_SHIFT (0)
+
+#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_MASK (0xFFFFF000)
+#define MMU_MMU_STATUS0_MMU_FAULT_ADDR_SHIFT (12)
+
+#define MMU_MMU_STATUS1_OFFSET (0x008C)
+
+#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_MASK (0x0000FFFF)
+#define MMU_MMU_STATUS1_MMU_FAULT_REQ_STAT_SHIFT (0)
+
+#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_MASK (0x000F0000)
+#define MMU_MMU_STATUS1_MMU_FAULT_REQ_ID_SHIFT (16)
+
+#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_MASK (0x03000000)
+#define MMU_MMU_STATUS1_MMU_FAULT_INDEX_SHIFT (24)
+
+#define MMU_MMU_STATUS1_MMU_FAULT_RNW_MASK (0x10000000)
+#define MMU_MMU_STATUS1_MMU_FAULT_RNW_SHIFT (28)
+
+#define MMU_MMU_MEM_REQ_OFFSET (0x0090)
+
+#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_MASK (0x000003FF)
+#define MMU_MMU_MEM_REQ_TAG_OUTSTANDING_SHIFT (0)
+
+#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_MASK (0x00001000)
+#define MMU_MMU_MEM_REQ_EXT_WRRESP_FAULT_SHIFT (12)
+
+#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_MASK (0x00002000)
+#define MMU_MMU_MEM_REQ_EXT_RDRESP_FAULT_SHIFT (13)
+
+#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_MASK (0x00004000)
+#define MMU_MMU_MEM_REQ_EXT_READ_BURST_FAULT_SHIFT (14)
+
+#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_MASK (0x80000000)
+#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SHIFT (31)
+#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_NO_REPS (16)
+#define MMU_MMU_MEM_REQ_INT_PROTOCOL_FAULT_SIZE (1)
+
+#define MMU_MMU_FAULT_SELECT_OFFSET (0x00A0)
+
+#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_MASK (0x0000000F)
+#define MMU_MMU_FAULT_SELECT_MMU_FAULT_SELECT_SHIFT (0)
+
+#define MMU_PROTOCOL_FAULT_OFFSET (0x00A8)
+
+#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_MASK (0x00000001)
+#define MMU_PROTOCOL_FAULT_FAULT_PAGE_BREAK_SHIFT (0)
+
+#define MMU_PROTOCOL_FAULT_FAULT_WRITE_MASK (0x00000010)
+#define MMU_PROTOCOL_FAULT_FAULT_WRITE_SHIFT (4)
+
+#define MMU_PROTOCOL_FAULT_FAULT_READ_MASK (0x00000020)
+#define MMU_PROTOCOL_FAULT_FAULT_READ_SHIFT (5)
+
+#define MMU_TOTAL_READ_REQ_OFFSET (0x0100)
+
+#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_MASK (0xFFFFFFFF)
+#define MMU_TOTAL_READ_REQ_TOTAL_READ_REQ_SHIFT (0)
+
+#define MMU_TOTAL_WRITE_REQ_OFFSET (0x0104)
+
+#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_MASK (0xFFFFFFFF)
+#define MMU_TOTAL_WRITE_REQ_TOTAL_WRITE_REQ_SHIFT (0)
+
+#define MMU_READS_LESS_64_REQ_OFFSET (0x0108)
+
+#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_MASK (0xFFFFFFFF)
+#define MMU_READS_LESS_64_REQ_READS_LESS_64_REQ_SHIFT (0)
+
+#define MMU_WRITES_LESS_64_REQ_OFFSET (0x010C)
+
+#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_MASK (0xFFFFFFFF)
+#define MMU_WRITES_LESS_64_REQ_WRITES_LESS_64_REQ_SHIFT (0)
+
+#define MMU_EXT_CMD_STALL_OFFSET (0x0120)
+
+#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_MASK (0xFFFFFFFF)
+#define MMU_EXT_CMD_STALL_EXT_CMD_STALL_SHIFT (0)
+
+#define MMU_WRITE_REQ_STALL_OFFSET (0x0124)
+
+#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_MASK (0xFFFFFFFF)
+#define MMU_WRITE_REQ_STALL_WRITE_REQ_STALL_SHIFT (0)
+
+#define MMU_MMU_MISS_STALL_OFFSET (0x0128)
+
+#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_MASK (0xFFFFFFFF)
+#define MMU_MMU_MISS_STALL_MMU_MISS_STALL_SHIFT (0)
+
+#define MMU_ADDRESS_STALL_OFFSET (0x012C)
+
+#define MMU_ADDRESS_STALL_ADDRESS_STALL_MASK (0xFFFFFFFF)
+#define MMU_ADDRESS_STALL_ADDRESS_STALL_SHIFT (0)
+
+#define MMU_TAG_STALL_OFFSET (0x0130)
+
+#define MMU_TAG_STALL_TAG_STALL_MASK (0xFFFFFFFF)
+#define MMU_TAG_STALL_TAG_STALL_SHIFT (0)
+
+#define MMU_PEAK_READ_OUTSTANDING_OFFSET (0x0140)
+
+#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_MASK (0x000003FF)
+#define MMU_PEAK_READ_OUTSTANDING_PEAK_TAG_OUTSTANDING_SHIFT (0)
+
+#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_MASK (0xFFFF0000)
+#define MMU_PEAK_READ_OUTSTANDING_PEAK_READ_LATENCY_SHIFT (16)
+
+#define MMU_AVERAGE_READ_LATENCY_OFFSET (0x0144)
+
+#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_MASK (0xFFFFFFFF)
+#define MMU_AVERAGE_READ_LATENCY_AVERAGE_READ_LATENCY_SHIFT (0)
+
+#define MMU_STATISTICS_CONTROL_OFFSET (0x0160)
+
+#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_MASK (0x00000001)
+#define MMU_STATISTICS_CONTROL_BANDWIDTH_STATS_INIT_SHIFT (0)
+
+#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_MASK (0x00000002)
+#define MMU_STATISTICS_CONTROL_STALL_STATS_INIT_SHIFT (1)
+
+#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_MASK (0x00000004)
+#define MMU_STATISTICS_CONTROL_LATENCY_STATS_INIT_SHIFT (2)
+
+#define MMU_MMU_VERSION_OFFSET (0x01D0)
+
+#define MMU_MMU_VERSION_MMU_MAJOR_REV_MASK (0x00FF0000)
+#define MMU_MMU_VERSION_MMU_MAJOR_REV_SHIFT (16)
+
+#define MMU_MMU_VERSION_MMU_MINOR_REV_MASK (0x0000FF00)
+#define MMU_MMU_VERSION_MMU_MINOR_REV_SHIFT (8)
+
+#define MMU_MMU_VERSION_MMU_MAINT_REV_MASK (0x000000FF)
+#define MMU_MMU_VERSION_MMU_MAINT_REV_SHIFT (0)
+
+#define MMU_BYTE_SIZE (0x01D4)
+
+#endif
diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c
index d904952bf00e..bef1e7137f23 100644
--- a/drivers/media/platform/intel/pxa_camera.c
+++ b/drivers/media/platform/intel/pxa_camera.c
@@ -43,6 +43,7 @@
#include <linux/videodev2.h>
#include <linux/platform_data/media/camera-pxa.h>
+#include <linux/workqueue.h>
#define PXA_CAM_VERSION "0.0.6"
#define PXA_CAM_DRV_NAME "pxa27x-camera"
@@ -683,7 +684,7 @@ struct pxa_camera_dev {
unsigned int buf_sequence;
struct pxa_buffer *active;
- struct tasklet_struct task_eof;
+ struct work_struct eof_bh_work;
u32 save_cicr[5];
};
@@ -1146,9 +1147,9 @@ static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
clk_disable_unprepare(pcdev->clk);
}
-static void pxa_camera_eof(struct tasklet_struct *t)
+static void pxa_camera_eof_bh_work(struct work_struct *t)
{
- struct pxa_camera_dev *pcdev = from_tasklet(pcdev, t, task_eof);
+ struct pxa_camera_dev *pcdev = from_work(pcdev, t, eof_bh_work);
unsigned long cifr;
struct pxa_buffer *buf;
@@ -1185,7 +1186,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data)
if (status & CISR_EOF) {
cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM;
__raw_writel(cicr0, pcdev->base + CICR0);
- tasklet_schedule(&pcdev->task_eof);
+ queue_work(system_bh_wq, &pcdev->eof_bh_work);
}
return IRQ_HANDLED;
@@ -1503,8 +1504,6 @@ static const struct vb2_ops pxac_vb2_ops = {
.buf_cleanup = pxac_vb2_cleanup,
.start_streaming = pxac_vb2_start_streaming,
.stop_streaming = pxac_vb2_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int pxa_camera_init_videobuf2(struct pxa_camera_dev *pcdev)
@@ -2383,7 +2382,7 @@ static int pxa_camera_probe(struct platform_device *pdev)
}
}
- tasklet_setup(&pcdev->task_eof, pxa_camera_eof);
+ INIT_WORK(&pcdev->eof_bh_work, pxa_camera_eof_bh_work);
pxa_camera_activate(pcdev);
@@ -2409,7 +2408,7 @@ static int pxa_camera_probe(struct platform_device *pdev)
return 0;
exit_deactivate:
pxa_camera_deactivate(pcdev);
- tasklet_kill(&pcdev->task_eof);
+ cancel_work_sync(&pcdev->eof_bh_work);
exit_free_dma:
dma_release_channel(pcdev->dma_chans[2]);
exit_free_dma_u:
@@ -2428,7 +2427,7 @@ static void pxa_camera_remove(struct platform_device *pdev)
struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev);
pxa_camera_deactivate(pcdev);
- tasklet_kill(&pcdev->task_eof);
+ cancel_work_sync(&pcdev->eof_bh_work);
dma_release_channel(pcdev->dma_chans[0]);
dma_release_channel(pcdev->dma_chans[1]);
dma_release_channel(pcdev->dma_chans[2]);
@@ -2459,7 +2458,7 @@ static struct platform_driver pxa_camera_driver = {
.of_match_table = pxa_camera_of_match,
},
.probe = pxa_camera_probe,
- .remove_new = pxa_camera_remove,
+ .remove = pxa_camera_remove,
};
module_platform_driver(pxa_camera_driver);
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 96b35a5d6174..5188f3189096 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -724,10 +724,6 @@ static const struct v4l2_ioctl_ops deinterlace_ioctl_ops = {
/*
* Queue operations
*/
-struct vb2_dc_conf {
- struct device *dev;
-};
-
static int deinterlace_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
@@ -788,8 +784,6 @@ static const struct vb2_ops deinterlace_qops = {
.queue_setup = deinterlace_queue_setup,
.buf_prepare = deinterlace_buf_prepare,
.buf_queue = deinterlace_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq,
@@ -997,7 +991,7 @@ static void deinterlace_remove(struct platform_device *pdev)
static struct platform_driver deinterlace_pdrv = {
.probe = deinterlace_probe,
- .remove_new = deinterlace_remove,
+ .remove = deinterlace_remove,
.driver = {
.name = MEM2MEM_NAME,
},
diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c
index 66688b4aece5..b8360d37000a 100644
--- a/drivers/media/platform/marvell/mcam-core.c
+++ b/drivers/media/platform/marvell/mcam-core.c
@@ -439,9 +439,9 @@ static void mcam_ctlr_dma_vmalloc(struct mcam_camera *cam)
/*
* Copy data out to user space in the vmalloc case
*/
-static void mcam_frame_tasklet(struct tasklet_struct *t)
+static void mcam_frame_work(struct work_struct *t)
{
- struct mcam_camera *cam = from_tasklet(cam, t, s_tasklet);
+ struct mcam_camera *cam = from_work(cam, t, s_bh_work);
int i;
unsigned long flags;
struct mcam_vb_buffer *buf;
@@ -493,7 +493,7 @@ static int mcam_check_dma_buffers(struct mcam_camera *cam)
static void mcam_vmalloc_done(struct mcam_camera *cam, int frame)
{
- tasklet_schedule(&cam->s_tasklet);
+ queue_work(system_bh_wq, &cam->s_bh_work);
}
#else /* MCAM_MODE_VMALLOC */
@@ -935,7 +935,12 @@ static int mclk_enable(struct clk_hw *hw)
ret = pm_runtime_resume_and_get(cam->dev);
if (ret < 0)
return ret;
- clk_enable(cam->clk[0]);
+ ret = clk_enable(cam->clk[0]);
+ if (ret) {
+ pm_runtime_put(cam->dev);
+ return ret;
+ }
+
mcam_reg_write(cam, REG_CLKCTRL, (mclk_src << 29) | mclk_div);
mcam_ctlr_power_up(cam);
@@ -1203,8 +1208,6 @@ static const struct vb2_ops mcam_vb2_ops = {
.buf_queue = mcam_vb_buf_queue,
.start_streaming = mcam_vb_start_streaming,
.stop_streaming = mcam_vb_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
@@ -1267,8 +1270,6 @@ static const struct vb2_ops mcam_vb2_sg_ops = {
.buf_cleanup = mcam_vb_sg_buf_cleanup,
.start_streaming = mcam_vb_start_streaming,
.stop_streaming = mcam_vb_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
#endif /* MCAM_MODE_DMA_SG */
@@ -1305,7 +1306,7 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
break;
case B_vmalloc:
#ifdef MCAM_MODE_VMALLOC
- tasklet_setup(&cam->s_tasklet, mcam_frame_tasklet);
+ INIT_WORK(&cam->s_bh_work, mcam_frame_work);
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_vmalloc_memops;
cam->dma_setup = mcam_ctlr_dma_vmalloc;
@@ -1981,5 +1982,6 @@ int mccic_resume(struct mcam_camera *cam)
}
EXPORT_SYMBOL_GPL(mccic_resume);
+MODULE_DESCRIPTION("Marvell camera core driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
diff --git a/drivers/media/platform/marvell/mcam-core.h b/drivers/media/platform/marvell/mcam-core.h
index 51e66db45af6..989dc6859a53 100644
--- a/drivers/media/platform/marvell/mcam-core.h
+++ b/drivers/media/platform/marvell/mcam-core.h
@@ -9,6 +9,7 @@
#include <linux/list.h>
#include <linux/clk-provider.h>
+#include <linux/workqueue.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dev.h>
@@ -167,7 +168,7 @@ struct mcam_camera {
unsigned int dma_buf_size; /* allocated size */
void *dma_bufs[MAX_DMA_BUFS]; /* Internal buffer addresses */
dma_addr_t dma_handles[MAX_DMA_BUFS]; /* Buffer bus addresses */
- struct tasklet_struct s_tasklet;
+ struct work_struct s_bh_work;
#endif
unsigned int sequence; /* Frame sequence number */
unsigned int buf_seq[MAX_DMA_BUFS]; /* Sequence for individual bufs */
diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c
index 170907cc1885..d3da7ebb4a2b 100644
--- a/drivers/media/platform/marvell/mmp-driver.c
+++ b/drivers/media/platform/marvell/mmp-driver.c
@@ -30,6 +30,7 @@
MODULE_ALIAS("platform:mmp-camera");
MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+MODULE_DESCRIPTION("Support for the camera device found on Marvell MMP processors");
MODULE_LICENSE("GPL");
static char *mcam_clks[] = {"axi", "func", "phy"};
@@ -231,12 +232,22 @@ static int mmpcam_probe(struct platform_device *pdev)
mcam_init_clk(mcam);
/*
+ * Register with V4L.
+ */
+
+ ret = v4l2_device_register(mcam->dev, &mcam->v4l2_dev);
+ if (ret)
+ return ret;
+
+ /*
* Create a match of the sensor against its OF node.
*/
ep = fwnode_graph_get_next_endpoint(of_fwnode_handle(pdev->dev.of_node),
NULL);
- if (!ep)
- return -ENODEV;
+ if (!ep) {
+ ret = -ENODEV;
+ goto out_v4l2_device_unregister;
+ }
v4l2_async_nf_init(&mcam->notifier, &mcam->v4l2_dev);
@@ -245,7 +256,7 @@ static int mmpcam_probe(struct platform_device *pdev)
fwnode_handle_put(ep);
if (IS_ERR(asd)) {
ret = PTR_ERR(asd);
- goto out;
+ goto out_v4l2_device_unregister;
}
/*
@@ -253,7 +264,7 @@ static int mmpcam_probe(struct platform_device *pdev)
*/
ret = mccic_register(mcam);
if (ret)
- goto out;
+ goto out_v4l2_device_unregister;
/*
* Add OF clock provider.
@@ -282,6 +293,8 @@ static int mmpcam_probe(struct platform_device *pdev)
return 0;
out:
mccic_shutdown(mcam);
+out_v4l2_device_unregister:
+ v4l2_device_unregister(&mcam->v4l2_dev);
return ret;
}
@@ -292,6 +305,7 @@ static void mmpcam_remove(struct platform_device *pdev)
struct mcam_camera *mcam = &cam->mcam;
mccic_shutdown(mcam);
+ v4l2_device_unregister(&mcam->v4l2_dev);
pm_runtime_force_suspend(mcam->dev);
}
@@ -358,7 +372,7 @@ MODULE_DEVICE_TABLE(of, mmpcam_of_match);
static struct platform_driver mmpcam_driver = {
.probe = mmpcam_probe,
- .remove_new = mmpcam_remove,
+ .remove = mmpcam_remove,
.driver = {
.name = "mmp-camera",
.of_match_table = mmpcam_of_match,
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index ac48658e2de4..7eb12449b63a 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -884,8 +884,6 @@ static const struct vb2_ops mtk_jpeg_dec_qops = {
.queue_setup = mtk_jpeg_queue_setup,
.buf_prepare = mtk_jpeg_buf_prepare,
.buf_queue = mtk_jpeg_dec_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = mtk_jpeg_dec_stop_streaming,
};
@@ -893,8 +891,6 @@ static const struct vb2_ops mtk_jpeg_enc_qops = {
.queue_setup = mtk_jpeg_queue_setup,
.buf_prepare = mtk_jpeg_buf_prepare,
.buf_queue = mtk_jpeg_enc_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = mtk_jpeg_enc_stop_streaming,
};
@@ -1030,6 +1026,7 @@ static void mtk_jpeg_dec_device_run(void *priv)
spin_lock_irqsave(&jpeg->hw_lock, flags);
mtk_jpeg_dec_reset(jpeg->reg_base);
mtk_jpeg_dec_set_config(jpeg->reg_base,
+ jpeg->variant->support_34bit,
&jpeg_src_buf->dec_param,
jpeg_src_buf->bs_size,
&bs,
@@ -1293,6 +1290,11 @@ static int mtk_jpeg_single_core_init(struct platform_device *pdev,
return 0;
}
+static void mtk_jpeg_destroy_workqueue(void *data)
+{
+ destroy_workqueue(data);
+}
+
static int mtk_jpeg_probe(struct platform_device *pdev)
{
struct mtk_jpeg_dev *jpeg;
@@ -1337,6 +1339,11 @@ static int mtk_jpeg_probe(struct platform_device *pdev)
| WQ_FREEZABLE);
if (!jpeg->workqueue)
return -EINVAL;
+ ret = devm_add_action_or_reset(&pdev->dev,
+ mtk_jpeg_destroy_workqueue,
+ jpeg->workqueue);
+ if (ret)
+ return ret;
}
ret = v4l2_device_register(&pdev->dev, &jpeg->v4l2_dev);
@@ -1564,7 +1571,8 @@ static irqreturn_t mtk_jpeg_enc_done(struct mtk_jpeg_dev *jpeg)
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
- result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base);
+ result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base,
+ jpeg->variant->support_34bit);
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size);
buf_state = VB2_BUF_STATE_DONE;
@@ -1764,6 +1772,7 @@ retry_select:
ctx->total_frame_num++;
mtk_jpeg_dec_reset(comp_jpeg[hw_id]->reg_base);
mtk_jpeg_dec_set_config(comp_jpeg[hw_id]->reg_base,
+ jpeg->variant->support_34bit,
&jpeg_src_buf->dec_param,
jpeg_src_buf->bs_size,
&bs,
@@ -1950,7 +1959,7 @@ MODULE_DEVICE_TABLE(of, mtk_jpeg_match);
static struct platform_driver mtk_jpeg_driver = {
.probe = mtk_jpeg_probe,
- .remove_new = mtk_jpeg_remove,
+ .remove = mtk_jpeg_remove,
.driver = {
.name = MTK_JPEG_NAME,
.of_match_table = mtk_jpeg_match,
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
index 8877eb39e807..02ed0ed5b736 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.h
@@ -34,6 +34,8 @@
#define MTK_JPEG_MAX_EXIF_SIZE (64 * 1024)
+#define MTK_JPEG_ADDR_MASK GENMASK(1, 0)
+
/**
* enum mtk_jpeg_ctx_state - states of the context state machine
* @MTK_JPEG_INIT: current state is initialized
@@ -62,6 +64,7 @@ enum mtk_jpeg_ctx_state {
* @cap_q_default_fourcc: capture queue default fourcc
* @multi_core: mark jpeg hw is multi_core or not
* @jpeg_worker: jpeg dec or enc worker
+ * @support_34bit: flag to check support for 34-bit DMA address
*/
struct mtk_jpeg_variant {
struct clk_bulk_data *clks;
@@ -78,6 +81,7 @@ struct mtk_jpeg_variant {
u32 cap_q_default_fourcc;
bool multi_core;
void (*jpeg_worker)(struct work_struct *work);
+ bool support_34bit;
};
struct mtk_jpeg_src_buf {
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
index 4a6ee211e18f..e78e1d11093c 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.c
@@ -5,6 +5,8 @@
* Rick Chang <rick.chang@mediatek.com>
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -279,23 +281,43 @@ static void mtk_jpeg_dec_set_brz_factor(void __iomem *base, u8 yscale_w,
writel(val, base + JPGDEC_REG_BRZ_FACTOR);
}
-static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, u32 addr_y,
- u32 addr_u, u32 addr_v)
+static void mtk_jpeg_dec_set_dst_bank0(void __iomem *base, bool support_34bit,
+ dma_addr_t addr_y, dma_addr_t addr_u, dma_addr_t addr_v)
{
+ u32 val;
+
mtk_jpeg_verify_align(addr_y, 16, JPGDEC_REG_DEST_ADDR0_Y);
- writel(addr_y, base + JPGDEC_REG_DEST_ADDR0_Y);
+ writel(lower_32_bits(addr_y), base + JPGDEC_REG_DEST_ADDR0_Y);
mtk_jpeg_verify_align(addr_u, 16, JPGDEC_REG_DEST_ADDR0_U);
- writel(addr_u, base + JPGDEC_REG_DEST_ADDR0_U);
+ writel(lower_32_bits(addr_u), base + JPGDEC_REG_DEST_ADDR0_U);
mtk_jpeg_verify_align(addr_v, 16, JPGDEC_REG_DEST_ADDR0_V);
- writel(addr_v, base + JPGDEC_REG_DEST_ADDR0_V);
+ writel(lower_32_bits(addr_v), base + JPGDEC_REG_DEST_ADDR0_V);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_y));
+ writel(val, base + JPGDEC_REG_DEST_ADDR0_Y_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_u));
+ writel(val, base + JPGDEC_REG_DEST_ADDR0_U_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_v));
+ writel(val, base + JPGDEC_REG_DEST_ADDR0_V_EXT);
+ }
}
-static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, u32 addr_y,
- u32 addr_u, u32 addr_v)
+static void mtk_jpeg_dec_set_dst_bank1(void __iomem *base, bool support_34bit,
+ dma_addr_t addr_y, dma_addr_t addr_u, dma_addr_t addr_v)
{
- writel(addr_y, base + JPGDEC_REG_DEST_ADDR1_Y);
- writel(addr_u, base + JPGDEC_REG_DEST_ADDR1_U);
- writel(addr_v, base + JPGDEC_REG_DEST_ADDR1_V);
+ u32 val;
+
+ writel(lower_32_bits(addr_y), base + JPGDEC_REG_DEST_ADDR1_Y);
+ writel(lower_32_bits(addr_u), base + JPGDEC_REG_DEST_ADDR1_U);
+ writel(lower_32_bits(addr_v), base + JPGDEC_REG_DEST_ADDR1_V);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_y));
+ writel(val, base + JPGDEC_REG_DEST_ADDR1_Y_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_u));
+ writel(val, base + JPGDEC_REG_DEST_ADDR1_U_EXT);
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr_v));
+ writel(val, base + JPGDEC_REG_DEST_ADDR1_V_EXT);
+ }
}
static void mtk_jpeg_dec_set_mem_stride(void __iomem *base, u32 stride_y,
@@ -322,18 +344,30 @@ static void mtk_jpeg_dec_set_dec_mode(void __iomem *base, u32 mode)
writel(mode & 0x03, base + JPGDEC_REG_OPERATION_MODE);
}
-static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, u32 ptr)
+static void mtk_jpeg_dec_set_bs_write_ptr(void __iomem *base, bool support_34bit, dma_addr_t ptr)
{
+ u32 val;
+
mtk_jpeg_verify_align(ptr, 16, JPGDEC_REG_FILE_BRP);
- writel(ptr, base + JPGDEC_REG_FILE_BRP);
+ writel(lower_32_bits(ptr), base + JPGDEC_REG_FILE_BRP);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(ptr));
+ writel(val, base + JPGDEC_REG_FILE_BRP_EXT);
+ }
}
-static void mtk_jpeg_dec_set_bs_info(void __iomem *base, u32 addr, u32 size,
- u32 bitstream_size)
+static void mtk_jpeg_dec_set_bs_info(void __iomem *base, bool support_34bit,
+ dma_addr_t addr, u32 size, u32 bitstream_size)
{
+ u32 val;
+
mtk_jpeg_verify_align(addr, 16, JPGDEC_REG_FILE_ADDR);
mtk_jpeg_verify_align(size, 128, JPGDEC_REG_FILE_TOTAL_SIZE);
- writel(addr, base + JPGDEC_REG_FILE_ADDR);
+ writel(lower_32_bits(addr), base + JPGDEC_REG_FILE_ADDR);
+ if (support_34bit) {
+ val = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(addr));
+ writel(val, base + JPGDEC_REG_FILE_ADDR_EXT);
+ }
writel(size, base + JPGDEC_REG_FILE_TOTAL_SIZE);
writel(bitstream_size, base + JPGDEC_REG_BIT_STREAM_SIZE);
}
@@ -404,6 +438,7 @@ static void mtk_jpeg_dec_set_sampling_factor(void __iomem *base, u32 comp_num,
}
void mtk_jpeg_dec_set_config(void __iomem *base,
+ bool support_34bits,
struct mtk_jpeg_dec_param *cfg,
u32 bitstream_size,
struct mtk_jpeg_bs *bs,
@@ -413,8 +448,8 @@ void mtk_jpeg_dec_set_config(void __iomem *base,
mtk_jpeg_dec_set_dec_mode(base, 0);
mtk_jpeg_dec_set_comp0_du(base, cfg->unit_num);
mtk_jpeg_dec_set_total_mcu(base, cfg->total_mcu);
- mtk_jpeg_dec_set_bs_info(base, bs->str_addr, bs->size, bitstream_size);
- mtk_jpeg_dec_set_bs_write_ptr(base, bs->end_addr);
+ mtk_jpeg_dec_set_bs_info(base, support_34bits, bs->str_addr, bs->size, bitstream_size);
+ mtk_jpeg_dec_set_bs_write_ptr(base, support_34bits, bs->end_addr);
mtk_jpeg_dec_set_du_membership(base, cfg->membership, 1,
(cfg->comp_num == 1) ? 1 : 0);
mtk_jpeg_dec_set_comp_id(base, cfg->comp_id[0], cfg->comp_id[1],
@@ -432,9 +467,9 @@ void mtk_jpeg_dec_set_config(void __iomem *base,
cfg->mem_stride[1]);
mtk_jpeg_dec_set_img_stride(base, cfg->img_stride[0],
cfg->img_stride[1]);
- mtk_jpeg_dec_set_dst_bank0(base, fb->plane_addr[0],
+ mtk_jpeg_dec_set_dst_bank0(base, support_34bits, fb->plane_addr[0],
fb->plane_addr[1], fb->plane_addr[2]);
- mtk_jpeg_dec_set_dst_bank1(base, 0, 0, 0);
+ mtk_jpeg_dec_set_dst_bank1(base, support_34bits, 0, 0, 0);
mtk_jpeg_dec_set_dma_group(base, cfg->dma_mcu, cfg->dma_group,
cfg->dma_last_mcu);
mtk_jpeg_dec_set_pause_mcu_idx(base, cfg->total_mcu);
@@ -578,11 +613,6 @@ static int mtk_jpegdec_hw_init_irq(struct mtk_jpegdec_comp_dev *dev)
return 0;
}
-static void mtk_jpegdec_destroy_workqueue(void *data)
-{
- destroy_workqueue(data);
-}
-
static int mtk_jpegdec_hw_probe(struct platform_device *pdev)
{
struct mtk_jpegdec_clk *jpegdec_clk;
@@ -606,12 +636,6 @@ static int mtk_jpegdec_hw_probe(struct platform_device *pdev)
dev->plat_dev = pdev;
dev->dev = &pdev->dev;
- ret = devm_add_action_or_reset(&pdev->dev,
- mtk_jpegdec_destroy_workqueue,
- master_dev->workqueue);
- if (ret)
- return ret;
-
spin_lock_init(&dev->hw_lock);
dev->hw_state = MTK_JPEG_HW_IDLE;
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
index 8c31c6b12417..2948c9c300a4 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_hw.h
@@ -71,6 +71,7 @@ int mtk_jpeg_dec_fill_param(struct mtk_jpeg_dec_param *param);
u32 mtk_jpeg_dec_get_int_status(void __iomem *dec_reg_base);
u32 mtk_jpeg_dec_enum_result(u32 irq_result);
void mtk_jpeg_dec_set_config(void __iomem *base,
+ bool support_34bits,
struct mtk_jpeg_dec_param *cfg,
u32 bitstream_size,
struct mtk_jpeg_bs *bs,
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
index 27b7711ca341..e94f52de7c69 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_dec_reg.h
@@ -46,5 +46,13 @@
#define JPGDEC_REG_INTERRUPT_STATUS 0x0274
#define JPGDEC_REG_STATUS 0x0278
#define JPGDEC_REG_BIT_STREAM_SIZE 0x0344
+#define JPGDEC_REG_DEST_ADDR0_Y_EXT 0x0360
+#define JPGDEC_REG_DEST_ADDR0_U_EXT 0x0364
+#define JPGDEC_REG_DEST_ADDR0_V_EXT 0x0368
+#define JPGDEC_REG_DEST_ADDR1_Y_EXT 0x036c
+#define JPGDEC_REG_DEST_ADDR1_U_EXT 0x0370
+#define JPGDEC_REG_DEST_ADDR1_V_EXT 0x0374
+#define JPGDEC_REG_FILE_ADDR_EXT 0x0378
+#define JPGDEC_REG_FILE_BRP_EXT 0x037c
#endif /* _MTK_JPEG_REG_H */
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
index f8fa3b841ccf..9ab27aee302a 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.c
@@ -5,6 +5,8 @@
*
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -62,9 +64,9 @@ void mtk_jpeg_enc_reset(void __iomem *base)
}
EXPORT_SYMBOL_GPL(mtk_jpeg_enc_reset);
-u32 mtk_jpeg_enc_get_file_size(void __iomem *base)
+u32 mtk_jpeg_enc_get_file_size(void __iomem *base, bool support_34bit)
{
- return readl(base + JPEG_ENC_DMA_ADDR0) -
+ return (readl(base + JPEG_ENC_DMA_ADDR0) << ((support_34bit) ? 2 : 0)) -
readl(base + JPEG_ENC_DST_ADDR0);
}
EXPORT_SYMBOL_GPL(mtk_jpeg_enc_get_file_size);
@@ -84,14 +86,24 @@ void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base,
{
int i;
dma_addr_t dma_addr;
+ u32 addr_ext;
+ bool support_34bit = ctx->jpeg->variant->support_34bit;
for (i = 0; i < src_buf->num_planes; i++) {
dma_addr = vb2_dma_contig_plane_dma_addr(src_buf, i) +
src_buf->planes[i].data_offset;
- if (!i)
- writel(dma_addr, base + JPEG_ENC_SRC_LUMA_ADDR);
+ if (i == 0)
+ writel(lower_32_bits(dma_addr), base + JPEG_ENC_SRC_LUMA_ADDR);
else
- writel(dma_addr, base + JPEG_ENC_SRC_CHROMA_ADDR);
+ writel(lower_32_bits(dma_addr), base + JPEG_ENC_SRC_CHROMA_ADDR);
+
+ if (support_34bit) {
+ addr_ext = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(dma_addr));
+ if (i == 0)
+ writel(addr_ext, base + JPEG_ENC_SRC_LUMA_ADDR_EXT);
+ else
+ writel(addr_ext, base + JPEG_ENC_SRC_CHRO_ADDR_EXT);
+ }
}
}
EXPORT_SYMBOL_GPL(mtk_jpeg_set_enc_src);
@@ -103,6 +115,8 @@ void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base,
size_t size;
u32 dma_addr_offset;
u32 dma_addr_offsetmask;
+ u32 addr_ext;
+ bool support_34bit = ctx->jpeg->variant->support_34bit;
dma_addr = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
dma_addr_offset = ctx->enable_exif ? MTK_JPEG_MAX_EXIF_SIZE : 0;
@@ -113,6 +127,12 @@ void mtk_jpeg_set_enc_dst(struct mtk_jpeg_ctx *ctx, void __iomem *base,
writel(dma_addr_offsetmask & 0xf, base + JPEG_ENC_BYTE_OFFSET_MASK);
writel(dma_addr & ~0xf, base + JPEG_ENC_DST_ADDR0);
writel((dma_addr + size) & ~0xf, base + JPEG_ENC_STALL_ADDR0);
+
+ if (support_34bit) {
+ addr_ext = FIELD_PREP(MTK_JPEG_ADDR_MASK, upper_32_bits(dma_addr));
+ writel(addr_ext, base + JPEG_ENC_DEST_ADDR0_EXT);
+ writel(addr_ext + size, base + JPEG_ENC_STALL_ADDR0_EXT);
+ }
}
EXPORT_SYMBOL_GPL(mtk_jpeg_set_enc_dst);
@@ -278,7 +298,8 @@ static irqreturn_t mtk_jpegenc_hw_irq_handler(int irq, void *priv)
if (!(irq_status & JPEG_ENC_INT_STATUS_DONE))
dev_warn(jpeg->dev, "Jpg Enc occurs unknown Err.");
- result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base);
+ result_size = mtk_jpeg_enc_get_file_size(jpeg->reg_base,
+ ctx->jpeg->variant->support_34bit);
vb2_set_plane_payload(&dst_buf->vb2_buf, 0, result_size);
buf_state = VB2_BUF_STATE_DONE;
v4l2_m2m_buf_done(src_buf, buf_state);
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
index 61c60e4e58ea..31ec9030ae88 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_enc_hw.h
@@ -68,6 +68,11 @@
#define JPEG_ENC_DCM_CTRL 0x300
#define JPEG_ENC_CODEC_SEL 0x314
#define JPEG_ENC_ULTRA_THRES 0x318
+#define JPEG_ENC_SRC_LUMA_ADDR_EXT 0x584
+#define JPEG_ENC_SRC_CHRO_ADDR_EXT 0x588
+#define JPEG_ENC_Q_TBL_ADDR_EXT 0x58C
+#define JPEG_ENC_DEST_ADDR0_EXT 0x590
+#define JPEG_ENC_STALL_ADDR0_EXT 0x594
/**
* struct mtk_jpeg_enc_qlt - JPEG encoder quality data
@@ -80,7 +85,7 @@ struct mtk_jpeg_enc_qlt {
};
void mtk_jpeg_enc_reset(void __iomem *base);
-u32 mtk_jpeg_enc_get_file_size(void __iomem *base);
+u32 mtk_jpeg_enc_get_file_size(void __iomem *base, bool support_34bit);
void mtk_jpeg_enc_start(void __iomem *enc_reg_base);
void mtk_jpeg_set_enc_src(struct mtk_jpeg_ctx *ctx, void __iomem *base,
struct vb2_buffer *src_buf);
diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
index 917cdf38f230..80fdc6ff57e0 100644
--- a/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_core.c
@@ -298,7 +298,7 @@ static const struct dev_pm_ops mtk_mdp_pm_ops = {
static struct platform_driver mtk_mdp_driver = {
.probe = mtk_mdp_probe,
- .remove_new = mtk_mdp_remove,
+ .remove = mtk_mdp_remove,
.driver = {
.name = MTK_MDP_MODULE_NAME,
.pm = &mtk_mdp_pm_ops,
diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
index f14779e7596e..d0fd77dcf8e2 100644
--- a/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_m2m.c
@@ -342,7 +342,7 @@ static int mtk_mdp_try_crop(struct mtk_mdp_ctx *ctx, u32 type,
if (r->left & 1)
r->left -= 1;
- mtk_mdp_dbg(2, "[%d] crop l,t,w,h:%d,%d,%d,%d, max:%dx%d", ctx->id,
+ mtk_mdp_dbg(2, "[%d] crop (%d,%d)/%ux%u, max:%dx%d", ctx->id,
r->left, r->top, r->width,
r->height, max_w, max_h);
return 0;
@@ -584,8 +584,6 @@ static const struct vb2_ops mtk_mdp_m2m_qops = {
.buf_queue = mtk_mdp_m2m_buf_queue,
.stop_streaming = mtk_mdp_m2m_stop_streaming,
.start_streaming = mtk_mdp_m2m_start_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int mtk_mdp_m2m_querycap(struct file *file, void *fh,
diff --git a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c
index ecca52b45307..0b4c50bc1776 100644
--- a/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c
+++ b/drivers/media/platform/mediatek/mdp3/mdp_cfg_data.c
@@ -46,6 +46,53 @@ enum mt8183_mdp_comp_id {
MT8183_MDP_COMP_WROT1, /* 25 */
};
+enum mt8188_mdp_comp_id {
+ /* MT8188 Comp id */
+ /* ISP */
+ MT8188_MDP_COMP_WPEI = 0,
+ MT8188_MDP_COMP_WPEO, /* 1 */
+
+ /* MDP */
+ MT8188_MDP_COMP_CAMIN, /* 2 */
+ MT8188_MDP_COMP_RDMA0, /* 3 */
+ MT8188_MDP_COMP_RDMA2, /* 4 */
+ MT8188_MDP_COMP_RDMA3, /* 5 */
+ MT8188_MDP_COMP_FG0, /* 6 */
+ MT8188_MDP_COMP_FG2, /* 7 */
+ MT8188_MDP_COMP_FG3, /* 8 */
+ MT8188_MDP_COMP_TO_SVPP2MOUT, /* 9 */
+ MT8188_MDP_COMP_TO_SVPP3MOUT, /* 10 */
+ MT8188_MDP_COMP_TO_WARP0MOUT, /* 11 */
+ MT8188_MDP_COMP_VPP0_SOUT, /* 12 */
+ MT8188_MDP_COMP_VPP1_SOUT, /* 13 */
+ MT8188_MDP_COMP_PQ0_SOUT, /* 14 */
+ MT8188_MDP_COMP_HDR0, /* 15 */
+ MT8188_MDP_COMP_HDR2, /* 16 */
+ MT8188_MDP_COMP_HDR3, /* 17 */
+ MT8188_MDP_COMP_AAL0, /* 18 */
+ MT8188_MDP_COMP_AAL2, /* 19 */
+ MT8188_MDP_COMP_AAL3, /* 20 */
+ MT8188_MDP_COMP_RSZ0, /* 21 */
+ MT8188_MDP_COMP_RSZ2, /* 22 */
+ MT8188_MDP_COMP_RSZ3, /* 23 */
+ MT8188_MDP_COMP_TDSHP0, /* 24 */
+ MT8188_MDP_COMP_TDSHP2, /* 25 */
+ MT8188_MDP_COMP_TDSHP3, /* 26 */
+ MT8188_MDP_COMP_COLOR0, /* 27 */
+ MT8188_MDP_COMP_COLOR2, /* 28 */
+ MT8188_MDP_COMP_COLOR3, /* 29 */
+ MT8188_MDP_COMP_OVL0, /* 30 */
+ MT8188_MDP_COMP_PAD0, /* 31 */
+ MT8188_MDP_COMP_PAD2, /* 32 */
+ MT8188_MDP_COMP_PAD3, /* 33 */
+ MT8188_MDP_COMP_TCC0, /* 34 */
+ MT8188_MDP_COMP_WROT0, /* 35 */
+ MT8188_MDP_COMP_WROT2, /* 36 */
+ MT8188_MDP_COMP_WROT3, /* 37 */
+ MT8188_MDP_COMP_MERGE2, /* 38 */
+ MT8188_MDP_COMP_MERGE3, /* 39 */
+};
+
enum mt8195_mdp_comp_id {
/* MT8195 Comp id */
/* ISP */
@@ -123,6 +170,13 @@ static const struct of_device_id mt8183_mdp_probe_infra[MDP_INFRA_MAX] = {
[MDP_INFRA_SCP] = { .compatible = "mediatek,mt8183-scp" }
};
+static const struct of_device_id mt8188_mdp_probe_infra[MDP_INFRA_MAX] = {
+ [MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8188-vppsys0" },
+ [MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8188-vppsys1" },
+ [MDP_INFRA_MUTEX] = { .compatible = "mediatek,mt8188-vpp-mutex" },
+ [MDP_INFRA_MUTEX2] = { .compatible = "mediatek,mt8188-vpp-mutex" },
+};
+
static const struct of_device_id mt8195_mdp_probe_infra[MDP_INFRA_MAX] = {
[MDP_INFRA_MMSYS] = { .compatible = "mediatek,mt8195-vppsys0" },
[MDP_INFRA_MMSYS2] = { .compatible = "mediatek,mt8195-vppsys1" },
@@ -167,6 +221,40 @@ static const u32 mt8183_mutex_idx[MDP_MAX_COMP_COUNT] = {
[MDP_COMP_CCORR0] = MUTEX_MOD_IDX_MDP_CCORR0,
};
+static const u32 mt8188_mutex_idx[MDP_MAX_COMP_COUNT] = {
+ [MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0,
+ [MDP_COMP_RDMA2] = MUTEX_MOD_IDX_MDP_RDMA2,
+ [MDP_COMP_RDMA3] = MUTEX_MOD_IDX_MDP_RDMA3,
+ [MDP_COMP_FG0] = MUTEX_MOD_IDX_MDP_FG0,
+ [MDP_COMP_FG2] = MUTEX_MOD_IDX_MDP_FG2,
+ [MDP_COMP_FG3] = MUTEX_MOD_IDX_MDP_FG3,
+ [MDP_COMP_HDR0] = MUTEX_MOD_IDX_MDP_HDR0,
+ [MDP_COMP_HDR2] = MUTEX_MOD_IDX_MDP_HDR2,
+ [MDP_COMP_HDR3] = MUTEX_MOD_IDX_MDP_HDR3,
+ [MDP_COMP_AAL0] = MUTEX_MOD_IDX_MDP_AAL0,
+ [MDP_COMP_AAL2] = MUTEX_MOD_IDX_MDP_AAL2,
+ [MDP_COMP_AAL3] = MUTEX_MOD_IDX_MDP_AAL3,
+ [MDP_COMP_RSZ0] = MUTEX_MOD_IDX_MDP_RSZ0,
+ [MDP_COMP_RSZ2] = MUTEX_MOD_IDX_MDP_RSZ2,
+ [MDP_COMP_RSZ3] = MUTEX_MOD_IDX_MDP_RSZ3,
+ [MDP_COMP_MERGE2] = MUTEX_MOD_IDX_MDP_MERGE2,
+ [MDP_COMP_MERGE3] = MUTEX_MOD_IDX_MDP_MERGE3,
+ [MDP_COMP_TDSHP0] = MUTEX_MOD_IDX_MDP_TDSHP0,
+ [MDP_COMP_TDSHP2] = MUTEX_MOD_IDX_MDP_TDSHP2,
+ [MDP_COMP_TDSHP3] = MUTEX_MOD_IDX_MDP_TDSHP3,
+ [MDP_COMP_COLOR0] = MUTEX_MOD_IDX_MDP_COLOR0,
+ [MDP_COMP_COLOR2] = MUTEX_MOD_IDX_MDP_COLOR2,
+ [MDP_COMP_COLOR3] = MUTEX_MOD_IDX_MDP_COLOR3,
+ [MDP_COMP_OVL0] = MUTEX_MOD_IDX_MDP_OVL0,
+ [MDP_COMP_PAD0] = MUTEX_MOD_IDX_MDP_PAD0,
+ [MDP_COMP_PAD2] = MUTEX_MOD_IDX_MDP_PAD2,
+ [MDP_COMP_PAD3] = MUTEX_MOD_IDX_MDP_PAD3,
+ [MDP_COMP_TCC0] = MUTEX_MOD_IDX_MDP_TCC0,
+ [MDP_COMP_WROT0] = MUTEX_MOD_IDX_MDP_WROT0,
+ [MDP_COMP_WROT2] = MUTEX_MOD_IDX_MDP_WROT2,
+ [MDP_COMP_WROT3] = MUTEX_MOD_IDX_MDP_WROT3,
+};
+
static const u32 mt8195_mutex_idx[MDP_MAX_COMP_COUNT] = {
[MDP_COMP_RDMA0] = MUTEX_MOD_IDX_MDP_RDMA0,
[MDP_COMP_RDMA1] = MUTEX_MOD_IDX_MDP_RDMA1,
@@ -288,6 +376,171 @@ static const struct mdp_comp_data mt8183_mdp_comp_data[MDP_MAX_COMP_COUNT] = {
},
};
+static const struct mdp_comp_data mt8188_mdp_comp_data[MDP_MAX_COMP_COUNT] = {
+ [MDP_COMP_WPEI] = {
+ {MDP_COMP_TYPE_WPEI, 0, MT8188_MDP_COMP_WPEI, MDP_MM_SUBSYS_0},
+ {0, 0, 0}
+ },
+ [MDP_COMP_WPEO] = {
+ {MDP_COMP_TYPE_EXTO, 0, MT8188_MDP_COMP_WPEO, MDP_MM_SUBSYS_0},
+ {0, 0, 0}
+ },
+ [MDP_COMP_CAMIN] = {
+ {MDP_COMP_TYPE_DL_PATH, 0, MT8188_MDP_COMP_CAMIN, MDP_MM_SUBSYS_0},
+ {3, 3, 0}
+ },
+ [MDP_COMP_RDMA0] = {
+ {MDP_COMP_TYPE_RDMA, 0, MT8188_MDP_COMP_RDMA0, MDP_MM_SUBSYS_0},
+ {3, 0, 0}
+ },
+ [MDP_COMP_RDMA2] = {
+ {MDP_COMP_TYPE_RDMA, 1, MT8188_MDP_COMP_RDMA2, MDP_MM_SUBSYS_1},
+ {3, 0, 0}
+ },
+ [MDP_COMP_RDMA3] = {
+ {MDP_COMP_TYPE_RDMA, 2, MT8188_MDP_COMP_RDMA3, MDP_MM_SUBSYS_1},
+ {3, 0, 0}
+ },
+ [MDP_COMP_FG0] = {
+ {MDP_COMP_TYPE_FG, 0, MT8188_MDP_COMP_FG0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_FG2] = {
+ {MDP_COMP_TYPE_FG, 1, MT8188_MDP_COMP_FG2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_FG3] = {
+ {MDP_COMP_TYPE_FG, 2, MT8188_MDP_COMP_FG3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_HDR0] = {
+ {MDP_COMP_TYPE_HDR, 0, MT8188_MDP_COMP_HDR0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_HDR2] = {
+ {MDP_COMP_TYPE_HDR, 1, MT8188_MDP_COMP_HDR2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_HDR3] = {
+ {MDP_COMP_TYPE_HDR, 2, MT8188_MDP_COMP_HDR3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_AAL0] = {
+ {MDP_COMP_TYPE_AAL, 0, MT8188_MDP_COMP_AAL0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_AAL2] = {
+ {MDP_COMP_TYPE_AAL, 1, MT8188_MDP_COMP_AAL2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_AAL3] = {
+ {MDP_COMP_TYPE_AAL, 2, MT8188_MDP_COMP_AAL3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_RSZ0] = {
+ {MDP_COMP_TYPE_RSZ, 0, MT8188_MDP_COMP_RSZ0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_RSZ2] = {
+ {MDP_COMP_TYPE_RSZ, 1, MT8188_MDP_COMP_RSZ2, MDP_MM_SUBSYS_1},
+ {2, 0, 0},
+ {MDP_COMP_MERGE2, true, true}
+ },
+ [MDP_COMP_RSZ3] = {
+ {MDP_COMP_TYPE_RSZ, 2, MT8188_MDP_COMP_RSZ3, MDP_MM_SUBSYS_1},
+ {2, 0, 0},
+ {MDP_COMP_MERGE3, true, true}
+ },
+ [MDP_COMP_TDSHP0] = {
+ {MDP_COMP_TYPE_TDSHP, 0, MT8188_MDP_COMP_TDSHP0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_TDSHP2] = {
+ {MDP_COMP_TYPE_TDSHP, 1, MT8188_MDP_COMP_TDSHP2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_TDSHP3] = {
+ {MDP_COMP_TYPE_TDSHP, 2, MT8188_MDP_COMP_TDSHP3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_COLOR0] = {
+ {MDP_COMP_TYPE_COLOR, 0, MT8188_MDP_COMP_COLOR0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_COLOR2] = {
+ {MDP_COMP_TYPE_COLOR, 1, MT8188_MDP_COMP_COLOR2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_COLOR3] = {
+ {MDP_COMP_TYPE_COLOR, 2, MT8188_MDP_COMP_COLOR3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_OVL0] = {
+ {MDP_COMP_TYPE_OVL, 0, MT8188_MDP_COMP_OVL0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_PAD0] = {
+ {MDP_COMP_TYPE_PAD, 0, MT8188_MDP_COMP_PAD0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_PAD2] = {
+ {MDP_COMP_TYPE_PAD, 1, MT8188_MDP_COMP_PAD2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_PAD3] = {
+ {MDP_COMP_TYPE_PAD, 2, MT8188_MDP_COMP_PAD3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_TCC0] = {
+ {MDP_COMP_TYPE_TCC, 0, MT8188_MDP_COMP_TCC0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_WROT0] = {
+ {MDP_COMP_TYPE_WROT, 0, MT8188_MDP_COMP_WROT0, MDP_MM_SUBSYS_0},
+ {1, 0, 0}
+ },
+ [MDP_COMP_WROT2] = {
+ {MDP_COMP_TYPE_WROT, 1, MT8188_MDP_COMP_WROT2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_WROT3] = {
+ {MDP_COMP_TYPE_WROT, 2, MT8188_MDP_COMP_WROT3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_MERGE2] = {
+ {MDP_COMP_TYPE_MERGE, 0, MT8188_MDP_COMP_MERGE2, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_MERGE3] = {
+ {MDP_COMP_TYPE_MERGE, 1, MT8188_MDP_COMP_MERGE3, MDP_MM_SUBSYS_1},
+ {1, 0, 0}
+ },
+ [MDP_COMP_PQ0_SOUT] = {
+ {MDP_COMP_TYPE_DUMMY, 0, MT8188_MDP_COMP_PQ0_SOUT, MDP_MM_SUBSYS_0},
+ {0, 0, 0}
+ },
+ [MDP_COMP_TO_WARP0MOUT] = {
+ {MDP_COMP_TYPE_DUMMY, 1, MT8188_MDP_COMP_TO_WARP0MOUT, MDP_MM_SUBSYS_0},
+ {0, 0, 0}
+ },
+ [MDP_COMP_TO_SVPP2MOUT] = {
+ {MDP_COMP_TYPE_DUMMY, 2, MT8188_MDP_COMP_TO_SVPP2MOUT, MDP_MM_SUBSYS_1},
+ {0, 0, 0}
+ },
+ [MDP_COMP_TO_SVPP3MOUT] = {
+ {MDP_COMP_TYPE_DUMMY, 3, MT8188_MDP_COMP_TO_SVPP3MOUT, MDP_MM_SUBSYS_1},
+ {0, 0, 0}
+ },
+ [MDP_COMP_VPP0_SOUT] = {
+ {MDP_COMP_TYPE_PATH, 0, MT8188_MDP_COMP_VPP0_SOUT, MDP_MM_SUBSYS_1},
+ {2, 6, 0}
+ },
+ [MDP_COMP_VPP1_SOUT] = {
+ {MDP_COMP_TYPE_PATH, 1, MT8188_MDP_COMP_VPP1_SOUT, MDP_MM_SUBSYS_0},
+ {2, 8, 0}
+ },
+};
+
static const struct mdp_comp_data mt8195_mdp_comp_data[MDP_MAX_COMP_COUNT] = {
[MDP_COMP_WPEI] = {
{MDP_COMP_TYPE_WPEI, 0, MT8195_MDP_COMP_WPEI, MDP_MM_SUBSYS_0},
@@ -1046,6 +1299,15 @@ static const struct mdp_pipe_info mt8183_pipe_info[] = {
[MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 3}
};
+static const struct mdp_pipe_info mt8188_pipe_info[] = {
+ [MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0},
+ [MDP_PIPE_RDMA0] = {MDP_PIPE_RDMA0, MDP_MM_SUBSYS_0, 1},
+ [MDP_PIPE_RDMA2] = {MDP_PIPE_RDMA2, MDP_MM_SUBSYS_1, 0},
+ [MDP_PIPE_RDMA3] = {MDP_PIPE_RDMA3, MDP_MM_SUBSYS_1, 1},
+ [MDP_PIPE_VPP1_SOUT] = {MDP_PIPE_VPP1_SOUT, MDP_MM_SUBSYS_0, 2},
+ [MDP_PIPE_VPP0_SOUT] = {MDP_PIPE_VPP0_SOUT, MDP_MM_SUBSYS_1, 2},
+};
+
static const struct mdp_pipe_info mt8195_pipe_info[] = {
[MDP_PIPE_WPEI] = {MDP_PIPE_WPEI, MDP_MM_SUBSYS_0, 0},
[MDP_PIPE_WPEI2] = {MDP_PIPE_WPEI2, MDP_MM_SUBSYS_0, 1},
@@ -1082,6 +1344,24 @@ const struct mtk_mdp_driver_data mt8183_mdp_driver_data = {
.pp_used = MDP_PP_USED_1,
};
+const struct mtk_mdp_driver_data mt8188_mdp_driver_data = {
+ .mdp_plat_id = MT8188,
+ .mdp_con_res = 0x14001000,
+ .mdp_probe_infra = mt8188_mdp_probe_infra,
+ .mdp_sub_comp_dt_ids = mt8195_sub_comp_dt_ids,
+ .mdp_cfg = &mt8195_plat_cfg,
+ .mdp_mutex_table_idx = mt8188_mutex_idx,
+ .comp_data = mt8188_mdp_comp_data,
+ .comp_data_len = ARRAY_SIZE(mt8188_mdp_comp_data),
+ .format = mt8195_formats,
+ .format_len = ARRAY_SIZE(mt8195_formats),
+ .def_limit = &mt8195_mdp_def_limit,
+ .pipe_info = mt8188_pipe_info,
+ .pipe_info_len = ARRAY_SIZE(mt8188_pipe_info),
+ .pp_criteria = &mt8195_mdp_pp_criteria,
+ .pp_used = MDP_PP_USED_2,
+};
+
const struct mtk_mdp_driver_data mt8195_mdp_driver_data = {
.mdp_plat_id = MT8195,
.mdp_con_res = 0x14001000,
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h
index f83ac408306e..4764c5b5107b 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-img-ipi.h
@@ -116,6 +116,7 @@ struct img_frameparam {
/* Platform config indicator */
#define MT8183 8183
+#define MT8188 8195
#define MT8195 8195
#define CFG_CHECK(plat, p_id) ((plat) == (p_id))
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h
index 49cdf45f6e59..7f7625299ce7 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cfg.h
@@ -10,6 +10,7 @@
#include <linux/types.h>
extern const struct mtk_mdp_driver_data mt8183_mdp_driver_data;
+extern const struct mtk_mdp_driver_data mt8188_mdp_driver_data;
extern const struct mtk_mdp_driver_data mt8195_mdp_driver_data;
struct mdp_dev;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
index 1d64bac34b90..e5ccf673e152 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.c
@@ -114,19 +114,15 @@ static struct img_config *__get_config_offset(struct mdp_dev *mdp,
if (pp_idx >= mdp->mdp_data->pp_used)
goto err_param;
- if (CFG_CHECK(MT8183, p_id))
+ if (CFG_CHECK(MT8183, p_id)) {
cfg_c = CFG_OFST(MT8183, param->config, pp_idx);
- else if (CFG_CHECK(MT8195, p_id))
- cfg_c = CFG_OFST(MT8195, param->config, pp_idx);
- else
- goto err_param;
-
- if (CFG_CHECK(MT8183, p_id))
cfg_n = CFG_OFST(MT8183, param->config, pp_idx + 1);
- else if (CFG_CHECK(MT8195, p_id))
+ } else if (CFG_CHECK(MT8195, p_id)) {
+ cfg_c = CFG_OFST(MT8195, param->config, pp_idx);
cfg_n = CFG_OFST(MT8195, param->config, pp_idx + 1);
- else
+ } else {
goto err_param;
+ }
if ((long)cfg_n - (long)mdp->vpu.config > bound) {
dev_err(dev, "config offset %ld OOB %ld\n", (long)cfg_n, bound);
@@ -325,8 +321,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd,
/* Enable mux settings */
for (index = 0; index < ctrl->num_sets; index++) {
set = &ctrl->sets[index];
- cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg,
- set->value, 0xFFFFFFFF);
+ cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, set->value);
}
/* Config sub-frame information */
for (index = (num_comp - 1); index >= 0; index--) {
@@ -381,8 +376,7 @@ static int mdp_path_config_subfrm(struct mdp_cmdq_cmd *cmd,
/* Disable mux settings */
for (index = 0; index < ctrl->num_sets; index++) {
set = &ctrl->sets[index];
- cmdq_pkt_write_mask(&cmd->pkt, set->subsys_id, set->reg,
- 0, 0xFFFFFFFF);
+ cmdq_pkt_write(&cmd->pkt, set->subsys_id, set->reg, 0);
}
return 0;
@@ -471,43 +465,6 @@ static int mdp_path_config(struct mdp_dev *mdp, struct mdp_cmdq_cmd *cmd,
return 0;
}
-static int mdp_cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt,
- size_t size)
-{
- struct device *dev;
- dma_addr_t dma_addr;
-
- pkt->va_base = kzalloc(size, GFP_KERNEL);
- if (!pkt->va_base)
- return -ENOMEM;
-
- pkt->buf_size = size;
- pkt->cl = (void *)client;
-
- dev = client->chan->mbox->dev;
- dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, dma_addr)) {
- dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size);
- kfree(pkt->va_base);
- return -ENOMEM;
- }
-
- pkt->pa_base = dma_addr;
-
- return 0;
-}
-
-static void mdp_cmdq_pkt_destroy(struct cmdq_pkt *pkt)
-{
- struct cmdq_client *client = (struct cmdq_client *)pkt->cl;
-
- dma_unmap_single(client->chan->mbox->dev, pkt->pa_base, pkt->buf_size,
- DMA_TO_DEVICE);
- kfree(pkt->va_base);
- pkt->va_base = NULL;
-}
-
static void mdp_auto_release_work(struct work_struct *work)
{
struct mdp_cmdq_cmd *cmd;
@@ -524,7 +481,7 @@ static void mdp_auto_release_work(struct work_struct *work)
mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps,
cmd->num_comps);
- if (atomic_dec_and_test(&mdp->job_count)) {
+ if (refcount_dec_and_test(&mdp->job_count)) {
if (cmd->mdp_ctx)
mdp_m2m_job_finish(cmd->mdp_ctx);
@@ -538,7 +495,7 @@ static void mdp_auto_release_work(struct work_struct *work)
wake_up(&mdp->callback_wq);
}
- mdp_cmdq_pkt_destroy(&cmd->pkt);
+ cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt);
kfree(cmd->comps);
cmd->comps = NULL;
kfree(cmd);
@@ -575,10 +532,10 @@ static void mdp_handle_cmdq_callback(struct mbox_client *cl, void *mssg)
mdp_comp_clocks_off(&mdp->pdev->dev, cmd->comps,
cmd->num_comps);
- if (atomic_dec_and_test(&mdp->job_count))
+ if (refcount_dec_and_test(&mdp->job_count))
wake_up(&mdp->callback_wq);
- mdp_cmdq_pkt_destroy(&cmd->pkt);
+ cmdq_pkt_destroy(mdp->cmdq_clt[cmd->pp_idx], &cmd->pkt);
kfree(cmd->comps);
cmd->comps = NULL;
kfree(cmd);
@@ -607,20 +564,13 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp,
goto err_uninit;
}
- if (CFG_CHECK(MT8183, p_id))
- num_comp = CFG_GET(MT8183, config, num_components);
- else if (CFG_CHECK(MT8195, p_id))
- num_comp = CFG_GET(MT8195, config, num_components);
- else
- goto err_uninit;
-
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd) {
ret = -ENOMEM;
goto err_uninit;
}
- ret = mdp_cmdq_pkt_create(mdp->cmdq_clt[pp_idx], &cmd->pkt, SZ_16K);
+ ret = cmdq_pkt_create(mdp->cmdq_clt[pp_idx], &cmd->pkt, SZ_16K);
if (ret)
goto err_free_cmd;
@@ -632,6 +582,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp,
ret = -EINVAL;
goto err_destroy_pkt;
}
+
comps = kcalloc(num_comp, sizeof(*comps), GFP_KERNEL);
if (!comps) {
ret = -ENOMEM;
@@ -676,7 +627,8 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp,
dev_err(dev, "mdp_path_config error %d\n", pp_idx);
goto err_free_path;
}
- cmdq_pkt_finalize(&cmd->pkt);
+ cmdq_pkt_eoc(&cmd->pkt);
+ cmdq_pkt_jump_rel(&cmd->pkt, CMDQ_INST_SIZE, mdp->cmdq_shift_pa[pp_idx]);
for (i = 0; i < num_comp; i++) {
s32 inner_id = MDP_COMP_NONE;
@@ -699,6 +651,7 @@ static struct mdp_cmdq_cmd *mdp_cmdq_prepare(struct mdp_dev *mdp,
cmd->comps = comps;
cmd->num_comps = num_comp;
cmd->mdp_ctx = param->mdp_ctx;
+ cmd->pp_idx = pp_idx;
kfree(path);
return cmd;
@@ -710,7 +663,7 @@ err_free_path:
err_free_comps:
kfree(comps);
err_destroy_pkt:
- mdp_cmdq_pkt_destroy(&cmd->pkt);
+ cmdq_pkt_destroy(mdp->cmdq_clt[pp_idx], &cmd->pkt);
err_free_cmd:
kfree(cmd);
err_uninit:
@@ -724,9 +677,9 @@ int mdp_cmdq_send(struct mdp_dev *mdp, struct mdp_cmdq_param *param)
int i, ret;
u8 pp_used = __get_pp_num(param->param->type);
- atomic_set(&mdp->job_count, pp_used);
+ refcount_set(&mdp->job_count, pp_used);
if (atomic_read(&mdp->suspended)) {
- atomic_set(&mdp->job_count, 0);
+ refcount_set(&mdp->job_count, 0);
return -ECANCELED;
}
@@ -764,7 +717,7 @@ err_clock_off:
mdp_comp_clocks_off(&mdp->pdev->dev, cmd[i]->comps,
cmd[i]->num_comps);
err_cancel_job:
- atomic_set(&mdp->job_count, 0);
+ refcount_set(&mdp->job_count, 0);
return ret;
}
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h
index 53a30ad7e0b0..222611e03a06 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-cmdq.h
@@ -12,8 +12,6 @@
#include <linux/soc/mediatek/mtk-cmdq.h>
#include "mtk-img-ipi.h"
-struct platform_device *mdp_get_plat_device(struct platform_device *pdev);
-
struct mdp_cmdq_param {
struct img_config *config;
struct img_ipi_frameparam *param;
@@ -35,6 +33,7 @@ struct mdp_cmdq_cmd {
struct mdp_comp *comps;
void *mdp_ctx;
u8 num_comps;
+ u8 pp_idx;
};
struct mdp_dev;
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
index 8f62fb167156..683c066ed975 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.c
@@ -72,14 +72,14 @@ static int init_rdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
/* Disable RSZ1 */
if (ctx->comp->inner_id == rdma0 && prz1)
- MM_REG_WRITE(cmd, subsys_id, prz1->reg_base, PRZ_ENABLE,
- 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, prz1->reg_base,
+ PRZ_ENABLE, 0x0, BIT(0));
}
/* Reset RDMA */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, BIT(0), BIT(0));
- MM_REG_POLL(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, BIT(8), BIT(8));
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_RESET, BIT(0), BIT(0));
+ MM_REG_POLL_MASK(cmd, subsys_id, base, MDP_RDMA_MON_STA_1, BIT(8), BIT(8));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_RESET, 0x0, BIT(0));
return 0;
}
@@ -98,26 +98,25 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx,
if (mdp_cfg && mdp_cfg->rdma_support_10bit) {
if (block10bit)
- MM_REG_WRITE(cmd, subsys_id, base,
- MDP_RDMA_RESV_DUMMY_0, 0x7, 0x7);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base,
+ MDP_RDMA_RESV_DUMMY_0, 0x7, 0x7);
else
- MM_REG_WRITE(cmd, subsys_id, base,
- MDP_RDMA_RESV_DUMMY_0, 0x0, 0x7);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base,
+ MDP_RDMA_RESV_DUMMY_0, 0x0, 0x7);
}
/* Setup smi control */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON,
- (7 << 4) + //burst type to 8
- (1 << 16), //enable pre-ultra
- 0x00030071);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_GMCIF_CON,
+ (7 << 4) + //burst type to 8
+ (1 << 16), //enable pre-ultra
+ 0x00030071);
/* Setup source frame info */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.src_ctrl);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.src_ctrl);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_CON, reg,
- 0x03C8FE0F);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_SRC_CON, reg, 0x03C8FE0F);
if (mdp_cfg)
if (mdp_cfg->rdma_support_10bit && en_ufo) {
@@ -126,17 +125,15 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_y);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ufo_dec_y);
- MM_REG_WRITE(cmd, subsys_id,
- base, MDP_RDMA_UFO_DEC_LENGTH_BASE_Y,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base,
+ MDP_RDMA_UFO_DEC_LENGTH_BASE_Y, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.ufo_dec_c);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ufo_dec_c);
- MM_REG_WRITE(cmd, subsys_id,
- base, MDP_RDMA_UFO_DEC_LENGTH_BASE_C,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base,
+ MDP_RDMA_UFO_DEC_LENGTH_BASE_C, reg);
/* Set 10bit source frame pitch */
if (block10bit) {
@@ -144,9 +141,9 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd_in_pxl);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.mf_bkgd_in_pxl);
- MM_REG_WRITE(cmd, subsys_id,
- base, MDP_RDMA_MF_BKGD_SIZE_IN_PXL,
- reg, 0x001FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base,
+ MDP_RDMA_MF_BKGD_SIZE_IN_PXL,
+ reg, 0x001FFFFF);
}
}
@@ -157,128 +154,121 @@ static int config_rdma_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8195, ctx->param, rdma.control);
rdma_con_mask = 0x1130;
}
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_CON, reg,
- rdma_con_mask);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_CON, reg, rdma_con_mask);
/* Setup source buffer base */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.iova[0]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.iova[0]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_0, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.iova[1]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.iova[1]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_1, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.iova[2]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.iova[2]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_BASE_2, reg);
/* Setup source buffer end */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[0]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[0]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_0, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[1]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[1]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_1, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.iova_end[2]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.iova_end[2]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_END_2, reg);
/* Setup source frame pitch */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.mf_bkgd);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.mf_bkgd);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE,
- reg, 0x001FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_BKGD_SIZE_IN_BYTE,
+ reg, 0x001FFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.sf_bkgd);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.sf_bkgd);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE,
- reg, 0x001FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_SF_BKGD_SIZE_IN_BYTE,
+ reg, 0x001FFFFF);
/* Setup color transform */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.transform);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.transform);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0,
- reg, 0x0F110000);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_TRANSFORM_0,
+ reg, 0x0F110000);
if (!mdp_cfg || !mdp_cfg->rdma_esl_setting)
goto rdma_config_done;
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_0,
- reg, 0x0FFF00FF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_0,
+ reg, 0x0FFF00FF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_0,
- reg, 0x3FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_0,
+ reg, 0x3FFFFFFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_0,
- reg, 0x3FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_0,
+ reg, 0x3FFFFFFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con1);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_1,
- reg, 0x0F7F007F);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_1,
+ reg, 0x0F7F007F);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con1);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_1,
- reg, 0x3FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_1,
+ reg, 0x3FFFFFFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con1);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_1,
- reg, 0x3FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_1,
+ reg, 0x3FFFFFFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con2);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_2,
- reg, 0x0F3F003F);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_2,
+ reg, 0x0F3F003F);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_high_con2);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_2,
- reg, 0x3FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_HIGH_CON_2,
+ reg, 0x3FFFFFFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.ultra_th_low_con2);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_2,
- reg, 0x3FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_ULTRA_TH_LOW_CON_2,
+ reg, 0x3FFFFFFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.dmabuf_con3);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_3,
- reg, 0x0F3F003F);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_DMABUF_CON_3,
+ reg, 0x0F3F003F);
rdma_config_done:
return 0;
@@ -297,15 +287,14 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx,
u32 reg = 0;
/* Enable RDMA */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_EN, BIT(0), BIT(0));
/* Set Y pixel offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[0]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[0]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_0, reg);
/* Set 10bit UFO mode */
if (mdp_cfg) {
@@ -315,8 +304,7 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx,
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset_0_p);
MM_REG_WRITE(cmd, subsys_id, base,
- MDP_RDMA_SRC_OFFSET_0_P,
- reg, 0xFFFFFFFF);
+ MDP_RDMA_SRC_OFFSET_0_P, reg);
}
}
@@ -325,40 +313,38 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[1]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[1]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_1, reg);
/* Set V pixel offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].offset[2]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].offset[2]);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2,
- reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_SRC_OFFSET_2, reg);
/* Set source size */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].src);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].src);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, reg,
- 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_SRC_SIZE, reg,
+ 0x1FFF1FFF);
/* Set target size */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].clip);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].clip);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE,
- reg, 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_CLIP_SIZE,
+ reg, 0x1FFF1FFF);
/* Set crop offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rdma.subfrms[index].clip_ofst);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rdma.subfrms[index].clip_ofst);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1,
- reg, 0x003F001F);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_MF_OFFSET_1,
+ reg, 0x003F001F);
if (CFG_CHECK(MT8183, p_id)) {
csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left);
@@ -369,8 +355,8 @@ static int config_rdma_subfrm(struct mdp_comp_ctx *ctx,
}
if (mdp_cfg && mdp_cfg->rdma_upsample_repeat_only)
if ((csf_r - csf_l + 1) > 320)
- MM_REG_WRITE(cmd, subsys_id, base,
- MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base,
+ MDP_RDMA_RESV_DUMMY_0, BIT(2), BIT(2));
return 0;
}
@@ -393,7 +379,7 @@ static int wait_rdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]);
/* Disable RDMA */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_RDMA_EN, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_RDMA_EN, 0x0, BIT(0));
return 0;
}
@@ -411,10 +397,10 @@ static int init_rsz(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
u8 subsys_id = ctx->comp->subsys_id;
/* Reset RSZ */
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x10000, BIT(16));
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(16));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x10000, BIT(16));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(16));
/* Enable RSZ */
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, BIT(0), BIT(0));
if (CFG_CHECK(MT8195, p_id)) {
struct device *dev;
@@ -437,7 +423,7 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx,
u32 reg = 0;
if (mdp_cfg && mdp_cfg->rsz_etc_control)
- MM_REG_WRITE(cmd, subsys_id, base, RSZ_ETC_CONTROL, 0x0, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, RSZ_ETC_CONTROL, 0x0);
if (CFG_CHECK(MT8183, p_id))
bypass = CFG_COMP(MT8183, ctx->param, frame.bypass);
@@ -446,7 +432,7 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx,
if (bypass) {
/* Disable RSZ */
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_ENABLE, 0x0, BIT(0));
return 0;
}
@@ -454,29 +440,27 @@ static int config_rsz_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, rsz.control1);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.control1);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, reg,
- 0x03FFFDF3);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, reg, 0x03FFFDF3);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rsz.control2);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.control2);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, reg,
- 0x0FFFC290);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_2, reg, 0x0FFFC290);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rsz.coeff_step_x);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.coeff_step_x);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP,
- reg, 0x007FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_HORIZONTAL_COEFF_STEP, reg,
+ 0x007FFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rsz.coeff_step_y);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.coeff_step_y);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP,
- reg, 0x007FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_VERTICAL_COEFF_STEP, reg,
+ 0x007FFFFF);
return 0;
}
@@ -495,15 +479,13 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].control2);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].control2);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_2, reg,
- 0x00003800);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_2, reg, 0x00003800);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].src);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].src);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, PRZ_INPUT_IMAGE, reg);
if (CFG_CHECK(MT8183, p_id)) {
csf_l = CFG_COMP(MT8183, ctx->param, subfrms[index].in.left);
@@ -514,60 +496,56 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx,
}
if (mdp_cfg && mdp_cfg->rsz_disable_dcm_small_sample)
if ((csf_r - csf_l + 1) <= 16)
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1,
- BIT(27), BIT(27));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1,
+ BIT(27), BIT(27));
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.left);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.left);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET,
- reg, 0xFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_INTEGER_OFFSET,
+ reg, 0xFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.left_subpix);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.left_subpix);
- MM_REG_WRITE(cmd, subsys_id,
- base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET,
- reg, 0x1FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_HORIZONTAL_SUBPIXEL_OFFSET,
+ reg, 0x1FFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.top);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET,
- reg, 0xFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_INTEGER_OFFSET,
+ reg, 0xFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, subfrms[index].luma.top_subpix);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, subfrms[index].luma.top_subpix);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET,
- reg, 0x1FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_LUMA_VERTICAL_SUBPIXEL_OFFSET,
+ reg, 0x1FFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, subfrms[index].chroma.left);
- MM_REG_WRITE(cmd, subsys_id,
- base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET,
- reg, 0xFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_INTEGER_OFFSET,
+ reg, 0xFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, subfrms[index].chroma.left_subpix);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, subfrms[index].chroma.left_subpix);
- MM_REG_WRITE(cmd, subsys_id,
- base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET,
- reg, 0x1FFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CHROMA_HORIZONTAL_SUBPIXEL_OFFSET,
+ reg, 0x1FFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, rsz.subfrms[index].clip);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].clip);
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, PRZ_OUTPUT_IMAGE, reg);
if (CFG_CHECK(MT8195, p_id)) {
struct device *dev;
@@ -596,19 +574,19 @@ static int config_rsz_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, rsz.subfrms[index].merge_cfg);
MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base,
- MDP_MERGE_CFG_0, reg, 0xFFFFFFFF);
+ MDP_MERGE_CFG_0, reg);
MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base,
- MDP_MERGE_CFG_4, reg, 0xFFFFFFFF);
+ MDP_MERGE_CFG_4, reg);
MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base,
- MDP_MERGE_CFG_24, reg, 0xFFFFFFFF);
+ MDP_MERGE_CFG_24, reg);
MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base,
- MDP_MERGE_CFG_25, reg, 0xFFFFFFFF);
+ MDP_MERGE_CFG_25, reg);
/* Bypass mode */
MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base,
- MDP_MERGE_CFG_12, BIT(0), 0xFFFFFFFF);
+ MDP_MERGE_CFG_12, BIT(0));
MM_REG_WRITE(cmd, merge->subsys_id, merge->reg_base,
- MDP_MERGE_ENABLE, BIT(0), 0xFFFFFFFF);
+ MDP_MERGE_ENABLE, BIT(0));
}
rsz_subfrm_done:
@@ -634,8 +612,8 @@ static int advance_rsz_subfrm(struct mdp_comp_ctx *ctx,
}
if ((csf_r - csf_l + 1) <= 16)
- MM_REG_WRITE(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0,
- BIT(27));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, PRZ_CONTROL_1, 0x0,
+ BIT(27));
}
return 0;
@@ -655,15 +633,15 @@ static int init_wrot(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
u8 subsys_id = ctx->comp->subsys_id;
/* Reset WROT */
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, BIT(0), BIT(0));
- MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SOFT_RST, BIT(0), BIT(0));
+ MM_REG_POLL_MASK(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, BIT(0), BIT(0));
/* Reset setting */
if (CFG_CHECK(MT8195, p_id))
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, 0x0, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, 0x0);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_SOFT_RST, 0x0, BIT(0));
- MM_REG_POLL(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SOFT_RST, 0x0, BIT(0));
+ MM_REG_POLL_MASK(cmd, subsys_id, base, VIDO_SOFT_RST_STAT, 0x0, BIT(0));
return 0;
}
@@ -681,39 +659,36 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, wrot.iova[0]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.iova[0]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.iova[1]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.iova[1]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_C, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.iova[2]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.iova[2]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, VIDO_BASE_ADDR_V, reg);
if (mdp_cfg && mdp_cfg->wrot_support_10bit) {
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.scan_10bit);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_SCAN_10BIT,
- reg, 0x0000000F);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_SCAN_10BIT,
+ reg, 0x0000000F);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.pending_zero);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_PENDING_ZERO,
- reg, 0x04000000);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_PENDING_ZERO,
+ reg, 0x04000000);
}
if (CFG_CHECK(MT8195, p_id)) {
reg = CFG_COMP(MT8195, ctx->param, wrot.bit_number);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL_2,
- reg, 0x00000007);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CTRL_2,
+ reg, 0x00000007);
}
/* Write frame related registers */
@@ -721,14 +696,13 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, wrot.control);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.control);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_CTRL, reg,
- 0xF131510F);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CTRL, reg, 0xF131510F);
/* Write pre-ultra threshold */
if (CFG_CHECK(MT8195, p_id)) {
reg = CFG_COMP(MT8195, ctx->param, wrot.pre_ultra);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_DMA_PREULTRA, reg,
- 0x00FFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_DMA_PREULTRA, reg,
+ 0x00FFFFFF);
}
/* Write frame Y pitch */
@@ -736,37 +710,34 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, wrot.stride[0]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.stride[0]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE, reg,
- 0x0000FFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE, reg, 0x0000FFFF);
/* Write frame UV pitch */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.stride[1]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.stride[1]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_C, reg,
- 0xFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE_C, reg, 0xFFFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.stride[2]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.stride[2]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_STRIDE_V, reg,
- 0xFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_STRIDE_V, reg, 0xFFFF);
/* Write matrix control */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.mat_ctrl);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.mat_ctrl);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAT_CTRL, reg, 0xF3);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAT_CTRL, reg, 0xF3);
/* Set the fixed ALPHA as 0xFF */
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000,
- 0xFF000000);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_DITHER, 0xFF000000,
+ 0xFF000000);
/* Set VIDO_EOL_SEL */
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_RSV_1, BIT(31), BIT(31));
/* Set VIDO_FIFO_TEST */
if (CFG_CHECK(MT8183, p_id))
@@ -775,8 +746,8 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8195, ctx->param, wrot.fifo_test);
if (reg != 0)
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_FIFO_TEST,
- reg, 0xFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_FIFO_TEST, reg,
+ 0xFFF);
/* Filter enable */
if (mdp_cfg && mdp_cfg->wrot_filter_constraint) {
@@ -784,13 +755,13 @@ static int config_wrot_frame(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, wrot.filter);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.filter);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE,
- reg, 0x77);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, reg,
+ 0x77);
/* Turn off WROT DMA DCM */
if (CFG_CHECK(MT8195, p_id))
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN,
- (0x1 << 23) + (0x1 << 20), 0x900000);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN,
+ (0x1 << 23) + (0x1 << 20), 0x900000);
}
return 0;
@@ -808,57 +779,52 @@ static int config_wrot_subfrm(struct mdp_comp_ctx *ctx,
reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[0]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[0]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR,
- reg, 0x0FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR, reg, 0x0FFFFFFF);
/* Write U pixel offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[1]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[1]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_C,
- reg, 0x0FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR_C, reg, 0x0FFFFFFF);
/* Write V pixel offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].offset[2]);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].offset[2]);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_OFST_ADDR_V,
- reg, 0x0FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_OFST_ADDR_V, reg,
+ 0x0FFFFFFF);
/* Write source size */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].src);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].src);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_IN_SIZE, reg,
- 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_IN_SIZE, reg, 0x1FFF1FFF);
/* Write target size */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].clip);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_TAR_SIZE, reg,
- 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_TAR_SIZE, reg, 0x1FFF1FFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].clip_ofst);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].clip_ofst);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_CROP_OFST, reg,
- 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_CROP_OFST, reg, 0x1FFF1FFF);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wrot.subfrms[index].main_buf);
else if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, wrot.subfrms[index].main_buf);
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE,
- reg, 0x1FFF7F00);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, reg,
+ 0x1FFF7F00);
/* Enable WROT */
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, BIT(0), BIT(0));
return 0;
}
@@ -881,11 +847,11 @@ static int wait_wrot_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]);
if (mdp_cfg && mdp_cfg->wrot_filter_constraint)
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, 0x0,
- 0x77);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_MAIN_BUF_SIZE, 0x0,
+ 0x77);
/* Disable WROT */
- MM_REG_WRITE(cmd, subsys_id, base, VIDO_ROT_EN, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, VIDO_ROT_EN, 0x0, BIT(0));
return 0;
}
@@ -904,9 +870,9 @@ static int init_wdma(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
u8 subsys_id = ctx->comp->subsys_id;
/* Reset WDMA */
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, BIT(0), BIT(0));
- MM_REG_POLL(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, BIT(0), BIT(0));
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_RST, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_RST, BIT(0), BIT(0));
+ MM_REG_POLL_MASK(cmd, subsys_id, base, WDMA_FLOW_CTRL_DBG, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_RST, 0x0, BIT(0));
return 0;
}
@@ -918,40 +884,35 @@ static int config_wdma_frame(struct mdp_comp_ctx *ctx,
u8 subsys_id = ctx->comp->subsys_id;
u32 reg = 0;
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, WDMA_BUF_CON2, 0x10101050);
/* Setup frame information */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.wdma_cfg);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_CFG, reg,
- 0x0F01B8F0);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CFG, reg, 0x0F01B8F0);
/* Setup frame base address */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.iova[0]);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.iova[1]);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR, reg);
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.iova[2]);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, reg,
- 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR, reg);
/* Setup Y pitch */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.w_in_byte);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE,
- reg, 0x0000FFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_W_IN_BYTE, reg,
+ 0x0000FFFF);
/* Setup UV pitch */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.uv_stride);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_UV_PITCH,
- reg, 0x0000FFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_UV_PITCH, reg,
+ 0x0000FFFF);
/* Set the fixed ALPHA as 0xFF */
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF,
- 0x800000FF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_ALPHA, 0x800000FF,
+ 0x800000FF);
return 0;
}
@@ -966,36 +927,33 @@ static int config_wdma_subfrm(struct mdp_comp_ctx *ctx,
/* Write Y pixel offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[0]);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET,
- reg, 0x0FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_ADDR_OFFSET, reg,
+ 0x0FFFFFFF);
/* Write U pixel offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[1]);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET,
- reg, 0x0FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_U_ADDR_OFFSET, reg,
+ 0x0FFFFFFF);
/* Write V pixel offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].offset[2]);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET,
- reg, 0x0FFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_DST_V_ADDR_OFFSET, reg,
+ 0x0FFFFFFF);
/* Write source size */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].src);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_SRC_SIZE, reg,
- 0x3FFF3FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_SRC_SIZE, reg, 0x3FFF3FFF);
/* Write target size */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].clip);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_SIZE, reg,
- 0x3FFF3FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CLIP_SIZE, reg, 0x3FFF3FFF);
/* Write clip offset */
if (CFG_CHECK(MT8183, p_id))
reg = CFG_COMP(MT8183, ctx->param, wdma.subfrms[index].clip_ofst);
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_CLIP_COORD, reg,
- 0x3FFF3FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_CLIP_COORD, reg, 0x3FFF3FFF);
/* Enable WDMA */
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_EN, BIT(0), BIT(0));
return 0;
}
@@ -1007,7 +965,7 @@ static int wait_wdma_event(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
MM_REG_WAIT(cmd, ctx->comp->gce_event[MDP_GCE_EVENT_EOF]);
/* Disable WDMA */
- MM_REG_WRITE(cmd, subsys_id, base, WDMA_EN, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, WDMA_EN, 0x0, BIT(0));
return 0;
}
@@ -1033,19 +991,17 @@ static int reset_luma_hist(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
/* Reset histogram */
for (i = 0; i <= hist_num; i++)
- MM_REG_WRITE_MASK(cmd, subsys_id, base,
- (MDP_LUMA_HIST_INIT + (i << 2)),
- 0, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base,
+ (MDP_LUMA_HIST_INIT + (i << 2)), 0);
if (mdp_cfg->tdshp_constrain)
MM_REG_WRITE(cmd, subsys_id, base,
- MDP_DC_TWO_D_W1_RESULT_INIT, 0, 0xFFFFFFFF);
+ MDP_DC_TWO_D_W1_RESULT_INIT, 0);
if (mdp_cfg->tdshp_contour)
for (i = 0; i < hist_num; i++)
- MM_REG_WRITE_MASK(cmd, subsys_id, base,
- (MDP_CONTOUR_HIST_INIT + (i << 2)),
- 0, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base,
+ (MDP_CONTOUR_HIST_INIT + (i << 2)), 0);
return 0;
}
@@ -1055,9 +1011,9 @@ static int init_tdshp(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
phys_addr_t base = ctx->comp->reg_base;
u16 subsys_id = ctx->comp->subsys_id;
- MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CTRL, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CTRL, BIT(0), BIT(0));
/* Enable FIFO */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CFG, BIT(1), BIT(1));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CFG, BIT(1), BIT(1));
return reset_luma_hist(ctx, cmd);
}
@@ -1072,7 +1028,7 @@ static int config_tdshp_frame(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, tdshp.cfg);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_CFG, reg, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_CFG, reg, BIT(0));
return 0;
}
@@ -1086,26 +1042,24 @@ static int config_tdshp_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].src);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_INPUT_SIZE,
- reg, MDP_TDSHP_INPUT_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_INPUT_SIZE, reg);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].clip_ofst);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_OFFSET,
- reg, 0x00FF00FF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_OFFSET, reg,
+ 0x00FF00FF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].clip);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_SIZE,
- reg, MDP_TDSHP_OUTPUT_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_TDSHP_OUTPUT_SIZE, reg);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].hist_cfg_0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_00, reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_00, reg);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, tdshp.subfrms[index].hist_cfg_1);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_01, reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_HIST_CFG_01, reg);
return 0;
}
@@ -1122,21 +1076,19 @@ static int init_color(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
phys_addr_t base = ctx->comp->reg_base;
u16 subsys_id = ctx->comp->subsys_id;
- MM_REG_WRITE(cmd, subsys_id, base,
- MDP_COLOR_START, 0x1, BIT(1) | BIT(0));
- MM_REG_WRITE(cmd, subsys_id, base,
- MDP_COLOR_WIN_X_MAIN, 0xFFFF0000, 0xFFFFFFFF);
- MM_REG_WRITE(cmd, subsys_id, base,
- MDP_COLOR_WIN_Y_MAIN, 0xFFFF0000, 0xFFFFFFFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_START, 0x1,
+ BIT(1) | BIT(0));
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_WIN_X_MAIN, 0xFFFF0000);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_WIN_Y_MAIN, 0xFFFF0000);
/* Reset color matrix */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_CM1_EN, 0x0, BIT(0));
- MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_CM2_EN, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_CM1_EN, 0x0, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_CM2_EN, 0x0, BIT(0));
/* Enable interrupt */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTEN, 0x7, 0x7);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTEN, 0x7, 0x7);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_OUT_SEL, 0x333, 0x333);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_OUT_SEL, 0x333, 0x333);
return 0;
}
@@ -1151,8 +1103,7 @@ static int config_color_frame(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, color.start);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_START,
- reg, MDP_COLOR_START_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_START, reg);
return 0;
}
@@ -1166,13 +1117,13 @@ static int config_color_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, color.subfrms[index].in_hsize);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_WIDTH,
- reg, 0x00003FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_WIDTH,
+ reg, 0x00003FFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, color.subfrms[index].in_vsize);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_HEIGHT,
- reg, 0x00003FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_COLOR_INTERNAL_IP_HEIGHT,
+ reg, 0x00003FFF);
return 0;
}
@@ -1190,9 +1141,9 @@ static int init_ccorr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
u8 subsys_id = ctx->comp->subsys_id;
/* CCORR enable */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_EN, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_EN, BIT(0), BIT(0));
/* Relay mode */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_CFG, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_CFG, BIT(0), BIT(0));
return 0;
}
@@ -1214,8 +1165,8 @@ static int config_ccorr_subfrm(struct mdp_comp_ctx *ctx,
hsize = csf_r - csf_l + 1;
vsize = csf_b - csf_t + 1;
- MM_REG_WRITE(cmd, subsys_id, base, MDP_CCORR_SIZE,
- (hsize << 16) + (vsize << 0), 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_CCORR_SIZE,
+ (hsize << 16) + (vsize << 0), 0x1FFF1FFF);
return 0;
}
@@ -1231,7 +1182,7 @@ static int init_aal(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
u16 subsys_id = ctx->comp->subsys_id;
/* Always set MDP_AAL enable to 1 */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_EN, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_EN, BIT(0), BIT(0));
return 0;
}
@@ -1246,11 +1197,11 @@ static int config_aal_frame(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, aal.cfg_main);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_CFG_MAIN, reg, BIT(7));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_CFG_MAIN, reg, BIT(7));
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, aal.cfg);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_CFG, reg, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_CFG, reg, BIT(0));
return 0;
}
@@ -1264,18 +1215,16 @@ static int config_aal_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].src);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_SIZE,
- reg, MDP_AAL_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_SIZE, reg);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].clip_ofst);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_OFFSET,
- reg, 0x00FF00FF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_AAL_OUTPUT_OFFSET, reg,
+ 0x00FF00FF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, aal.subfrms[index].clip);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_SIZE,
- reg, MDP_AAL_OUTPUT_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_AAL_OUTPUT_SIZE, reg);
return 0;
}
@@ -1293,7 +1242,7 @@ static int init_hdr(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
u16 subsys_id = ctx->comp->subsys_id;
/* Always set MDP_HDR enable to 1 */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, BIT(0), BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, BIT(0), BIT(0));
return 0;
}
@@ -1308,11 +1257,11 @@ static int config_hdr_frame(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.top);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(29) | BIT(28));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(29) | BIT(28));
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.relay);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_RELAY, reg, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_RELAY, reg, BIT(0));
return 0;
}
@@ -1326,37 +1275,36 @@ static int config_hdr_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].win_size);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TILE_POS,
- reg, MDP_HDR_TILE_POS_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TILE_POS, reg);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].src);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_0, reg, 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_0, reg, 0x1FFF1FFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].clip_ofst0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_1, reg, 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_1, reg, 0x1FFF1FFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].clip_ofst1);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_SIZE_2, reg, 0x1FFF1FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_SIZE_2, reg, 0x1FFF1FFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_ctrl_0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_0, reg, 0x00003FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_0, reg, 0x00003FFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_ctrl_1);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_1, reg, 0x00003FFF);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_CTRL_1, reg, 0x00003FFF);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hdr_top);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(6) | BIT(5));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_TOP, reg, BIT(6) | BIT(5));
/* Enable histogram */
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, hdr.subfrms[index].hist_addr);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_HDR_HIST_ADDR, reg, BIT(9));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_HDR_HIST_ADDR, reg, BIT(9));
return 0;
}
@@ -1373,8 +1321,8 @@ static int init_fg(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
phys_addr_t base = ctx->comp->reg_base;
u16 subsys_id = ctx->comp->subsys_id;
- MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TRIGGER, BIT(2), BIT(2));
- MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TRIGGER, 0x0, BIT(2));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_TRIGGER, BIT(2), BIT(2));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_TRIGGER, 0x0, BIT(2));
return 0;
}
@@ -1389,11 +1337,11 @@ static int config_fg_frame(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, fg.ctrl_0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_FG_CTRL_0, reg, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_FG_CTRL_0, reg, BIT(0));
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, fg.ck_en);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_FG_CK_EN, reg, 0x7);
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_FG_FG_CK_EN, reg, 0x7);
return 0;
}
@@ -1407,11 +1355,11 @@ static int config_fg_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, fg.subfrms[index].info_0);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_0, reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_0, reg);
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, fg.subfrms[index].info_1);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_1, reg, 0xFFFFFFFF);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_FG_TILE_INFO_1, reg);
return 0;
}
@@ -1428,14 +1376,11 @@ static int init_ovl(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
phys_addr_t base = ctx->comp->reg_base;
u16 subsys_id = ctx->comp->subsys_id;
- MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_EN,
- BIT(0), MDP_OVL_EN_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_EN, BIT(0));
/* Set to relay mode */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON,
- BIT(9), MDP_OVL_SRC_CON_MASK);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_DP_CON,
- BIT(0), MDP_OVL_DP_CON_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, BIT(9));
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_DP_CON, BIT(0));
return 0;
}
@@ -1450,11 +1395,11 @@ static int config_ovl_frame(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, ovl.L0_con);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_CON, reg, BIT(29) | BIT(28));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_OVL_L0_CON, reg, BIT(29) | BIT(28));
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, ovl.src_con);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_SRC_CON, reg, BIT(0));
+ MM_REG_WRITE_MASK(cmd, subsys_id, base, MDP_OVL_SRC_CON, reg, BIT(0));
return 0;
}
@@ -1468,14 +1413,12 @@ static int config_ovl_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, ovl.subfrms[index].L0_src_size);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_SRC_SIZE,
- reg, MDP_OVL_L0_SRC_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_L0_SRC_SIZE, reg);
/* Setup output size */
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, ovl.subfrms[index].roi_size);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_ROI_SIZE,
- reg, MDP_OVL_ROI_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_OVL_ROI_SIZE, reg);
return 0;
}
@@ -1492,13 +1435,10 @@ static int init_pad(struct mdp_comp_ctx *ctx, struct mdp_cmdq_cmd *cmd)
phys_addr_t base = ctx->comp->reg_base;
u16 subsys_id = ctx->comp->subsys_id;
- MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_CON,
- BIT(1), MDP_PAD_CON_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_CON, BIT(1));
/* Reset */
- MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_W_SIZE,
- 0, MDP_PAD_W_SIZE_MASK);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_H_SIZE,
- 0, MDP_PAD_H_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_W_SIZE, 0);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_H_SIZE, 0);
return 0;
}
@@ -1512,8 +1452,7 @@ static int config_pad_subfrm(struct mdp_comp_ctx *ctx,
if (CFG_CHECK(MT8195, p_id))
reg = CFG_COMP(MT8195, ctx->param, pad.subfrms[index].pic_size);
- MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_PIC_SIZE,
- reg, MDP_PAD_PIC_SIZE_MASK);
+ MM_REG_WRITE(cmd, subsys_id, base, MDP_PAD_PIC_SIZE, reg);
return 0;
}
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h
index 3e5d2da1c807..681906c16419 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-comp.h
@@ -9,18 +9,18 @@
#include "mtk-mdp3-cmdq.h"
-#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask, ...) \
- cmdq_pkt_write_mask(&((cmd)->pkt), id, \
- (base) + (ofst), (val), (mask), ##__VA_ARGS__)
-
-#define MM_REG_WRITE(cmd, id, base, ofst, val, mask, ...) \
+#define MM_REG_WRITE_MASK(cmd, id, base, ofst, val, mask) \
do { \
typeof(mask) (m) = (mask); \
- MM_REG_WRITE_MASK(cmd, id, base, ofst, val, \
+ cmdq_pkt_write_mask(&((cmd)->pkt), id, (base) + (ofst), \
+ (val), \
(((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
- (0xffffffff) : (m), ##__VA_ARGS__); \
+ (0xffffffff) : (m)); \
} while (0)
+#define MM_REG_WRITE(cmd, id, base, ofst, val) \
+ cmdq_pkt_write(&((cmd)->pkt), id, (base) + (ofst), (val))
+
#define MM_REG_WAIT(cmd, evt) \
do { \
typeof(cmd) (c) = (cmd); \
@@ -49,20 +49,17 @@ do { \
cmdq_pkt_set_event(&((c)->pkt), (e)); \
} while (0)
-#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask, ...) \
+#define MM_REG_POLL_MASK(cmd, id, base, ofst, val, _mask) \
do { \
typeof(_mask) (_m) = (_mask); \
cmdq_pkt_poll_mask(&((cmd)->pkt), id, \
- (base) + (ofst), (val), (_m), ##__VA_ARGS__); \
+ (base) + (ofst), (val), \
+ (((_m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
+ (0xffffffff) : (_m)); \
} while (0)
-#define MM_REG_POLL(cmd, id, base, ofst, val, mask, ...) \
-do { \
- typeof(mask) (m) = (mask); \
- MM_REG_POLL_MASK((cmd), id, base, ofst, val, \
- (((m) & (ofst##_MASK)) == (ofst##_MASK)) ? \
- (0xffffffff) : (m), ##__VA_ARGS__); \
-} while (0)
+#define MM_REG_POLL(cmd, id, base, ofst, val) \
+ cmdq_pkt_poll(&((cmd)->pkt), id, (base) + (ofst), (val))
enum mtk_mdp_comp_id {
MDP_COMP_NONE = -1, /* Invalid engine */
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
index 5209f531ef8d..8de2c8e4d333 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.c
@@ -21,6 +21,9 @@ static const struct of_device_id mdp_of_ids[] = {
{ .compatible = "mediatek,mt8183-mdp3-rdma",
.data = &mt8183_mdp_driver_data,
},
+ { .compatible = "mediatek,mt8188-mdp3-rdma",
+ .data = &mt8188_mdp_driver_data,
+ },
{ .compatible = "mediatek,mt8195-mdp3-rdma",
.data = &mt8195_mdp_driver_data,
},
@@ -76,25 +79,6 @@ static struct platform_device *__get_pdev_by_id(struct platform_device *pdev,
return mdp_pdev;
}
-struct platform_device *mdp_get_plat_device(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct device_node *mdp_node;
- struct platform_device *mdp_pdev;
-
- mdp_node = of_parse_phandle(dev->of_node, MDP_PHANDLE_NAME, 0);
- if (!mdp_node) {
- dev_err(dev, "can't get node %s\n", MDP_PHANDLE_NAME);
- return NULL;
- }
-
- mdp_pdev = of_find_device_by_node(mdp_node);
- of_node_put(mdp_node);
-
- return mdp_pdev;
-}
-EXPORT_SYMBOL_GPL(mdp_get_plat_device);
-
int mdp_vpu_get_locked(struct mdp_dev *mdp)
{
int ret = 0;
@@ -309,6 +293,8 @@ static int mdp_probe(struct platform_device *pdev)
ret = PTR_ERR(mdp->cmdq_clt[i]);
goto err_mbox_destroy;
}
+
+ mdp->cmdq_shift_pa[i] = cmdq_get_shift_pa(mdp->cmdq_clt[i]->chan);
}
init_waitqueue_head(&mdp->callback_wq);
@@ -380,14 +366,14 @@ static int __maybe_unused mdp_suspend(struct device *dev)
atomic_set(&mdp->suspended, 1);
- if (atomic_read(&mdp->job_count)) {
+ if (refcount_read(&mdp->job_count)) {
ret = wait_event_timeout(mdp->callback_wq,
- !atomic_read(&mdp->job_count),
+ !refcount_read(&mdp->job_count),
2 * HZ);
if (ret == 0) {
dev_err(dev,
"%s:flushed cmdq task incomplete, count=%d\n",
- __func__, atomic_read(&mdp->job_count));
+ __func__, refcount_read(&mdp->job_count));
return -EBUSY;
}
}
@@ -410,7 +396,7 @@ static const struct dev_pm_ops mdp_pm_ops = {
static struct platform_driver mdp_driver = {
.probe = mdp_probe,
- .remove_new = mdp_remove,
+ .remove = mdp_remove,
.driver = {
.name = MDP_MODULE_NAME,
.pm = &mdp_pm_ops,
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h
index 8c09e984fd01..05cade1d098e 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-core.h
@@ -126,6 +126,7 @@ struct mdp_dev {
u32 id_count;
struct ida mdp_ida;
struct cmdq_client *cmdq_clt[MDP_PP_MAX];
+ u8 cmdq_shift_pa[MDP_PP_MAX];
wait_queue_head_t callback_wq;
struct v4l2_device v4l2_dev;
@@ -134,7 +135,7 @@ struct mdp_dev {
/* synchronization protect for m2m device operation */
struct mutex m2m_lock;
atomic_t suspended;
- atomic_t job_count;
+ refcount_t job_count;
};
struct mdp_pipe_info {
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
index 35a8b059bde5..59ce5cce0698 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-m2m.c
@@ -104,14 +104,14 @@ static void mdp_m2m_device_run(void *priv)
task.cb_data = NULL;
task.mdp_ctx = ctx;
- if (atomic_read(&ctx->mdp_dev->job_count)) {
+ if (refcount_read(&ctx->mdp_dev->job_count)) {
ret = wait_event_timeout(ctx->mdp_dev->callback_wq,
- !atomic_read(&ctx->mdp_dev->job_count),
+ !refcount_read(&ctx->mdp_dev->job_count),
2 * HZ);
if (ret == 0) {
dev_err(&ctx->mdp_dev->pdev->dev,
"%d jobs not yet done\n",
- atomic_read(&ctx->mdp_dev->job_count));
+ refcount_read(&ctx->mdp_dev->job_count));
goto worker_end;
}
}
@@ -266,8 +266,6 @@ static void mdp_m2m_buf_queue(struct vb2_buffer *vb)
static const struct vb2_ops mdp_m2m_qops = {
.queue_setup = mdp_m2m_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_prepare = mdp_m2m_buf_prepare,
.start_streaming = mdp_m2m_start_streaming,
.stop_streaming = mdp_m2m_stop_streaming,
diff --git a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
index 657356f87743..644b223b2877 100644
--- a/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
+++ b/drivers/media/platform/mediatek/mdp3/mtk-mdp3-regs.c
@@ -236,7 +236,7 @@ int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r,
u32 framew, frameh, walign, halign;
int ret;
- dev_dbg(dev, "%d target:%d, set:(%d,%d) %ux%u", ctx->id,
+ dev_dbg(dev, "%d target:%d, set:(%d,%d)/%ux%u", ctx->id,
s->target, s->r.left, s->r.top, s->r.width, s->r.height);
left = s->r.left;
@@ -275,7 +275,7 @@ int mdp_try_crop(struct mdp_m2m_ctx *ctx, struct v4l2_rect *r,
r->width = right - left;
r->height = bottom - top;
- dev_dbg(dev, "%d crop:(%d,%d) %ux%u", ctx->id,
+ dev_dbg(dev, "%d crop:(%d,%d)/%ux%u", ctx->id,
r->left, r->top, r->width, r->height);
return 0;
}
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
index 6bbe55de6ce9..1b0bc47355c0 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_fw_scp.c
@@ -79,6 +79,11 @@ struct mtk_vcodec_fw *mtk_vcodec_fw_scp_init(void *priv, enum mtk_vcodec_fw_use
}
fw = devm_kzalloc(&plat_dev->dev, sizeof(*fw), GFP_KERNEL);
+ if (!fw) {
+ scp_put(scp);
+ return ERR_PTR(-ENOMEM);
+ }
+
fw->type = SCP;
fw->ops = &mtk_vcodec_rproc_msg;
fw->scp = scp;
diff --git a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c
index 9ce34a3b5ee6..fc4e34c29192 100644
--- a/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c
+++ b/drivers/media/platform/mediatek/vcodec/common/mtk_vcodec_util.c
@@ -49,7 +49,6 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem)
{
enum mtk_instance_type inst_type = *((unsigned int *)priv);
struct platform_device *plat_dev;
- unsigned long size = mem->size;
int id;
if (inst_type == MTK_INST_ENCODER) {
@@ -64,15 +63,16 @@ int mtk_vcodec_mem_alloc(void *priv, struct mtk_vcodec_mem *mem)
id = dec_ctx->id;
}
- mem->va = dma_alloc_coherent(&plat_dev->dev, size, &mem->dma_addr, GFP_KERNEL);
+ mem->va = dma_alloc_attrs(&plat_dev->dev, mem->size, &mem->dma_addr,
+ GFP_KERNEL, DMA_ATTR_ALLOC_SINGLE_PAGES);
if (!mem->va) {
- mtk_v4l2_err(plat_dev, "%s dma_alloc size=%ld failed!",
- dev_name(&plat_dev->dev), size);
+ mtk_v4l2_err(plat_dev, "%s dma_alloc size=0x%zx failed!",
+ __func__, mem->size);
return -ENOMEM;
}
- mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%lx", id, mem->va,
- (unsigned long)mem->dma_addr, size);
+ mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%zx", id, mem->va,
+ (unsigned long)mem->dma_addr, mem->size);
return 0;
}
@@ -82,7 +82,6 @@ void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem)
{
enum mtk_instance_type inst_type = *((unsigned int *)priv);
struct platform_device *plat_dev;
- unsigned long size = mem->size;
int id;
if (inst_type == MTK_INST_ENCODER) {
@@ -98,15 +97,16 @@ void mtk_vcodec_mem_free(void *priv, struct mtk_vcodec_mem *mem)
}
if (!mem->va) {
- mtk_v4l2_err(plat_dev, "%s dma_free size=%ld failed!",
- dev_name(&plat_dev->dev), size);
+ mtk_v4l2_err(plat_dev, "%s: Tried to free a NULL VA", __func__);
+ if (mem->size)
+ mtk_v4l2_err(plat_dev, "Failed to free %zu bytes", mem->size);
return;
}
- mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%lx", id, mem->va,
- (unsigned long)mem->dma_addr, size);
+ mtk_v4l2_debug(plat_dev, 3, "[%d] - va = %p dma = 0x%lx size = 0x%zx", id, mem->va,
+ (unsigned long)mem->dma_addr, mem->size);
- dma_free_coherent(&plat_dev->dev, size, mem->va, mem->dma_addr);
+ dma_free_coherent(&plat_dev->dev, mem->size, mem->va, mem->dma_addr);
mem->va = NULL;
mem->dma_addr = 0;
mem->size = 0;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
index ba742f0e391d..98838217b97d 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec.c
@@ -80,21 +80,18 @@ static struct mtk_q_data *mtk_vdec_get_q_data(struct mtk_vcodec_dec_ctx *ctx,
return &ctx->q_data[MTK_Q_DATA_DST];
}
-static int vidioc_try_decoder_cmd(struct file *file, void *priv,
- struct v4l2_decoder_cmd *cmd)
+static int stateful_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd)
{
return v4l2_m2m_ioctl_try_decoder_cmd(file, priv, cmd);
}
-
-static int vidioc_decoder_cmd(struct file *file, void *priv,
- struct v4l2_decoder_cmd *cmd)
+static int stateful_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd)
{
struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv);
struct vb2_queue *src_vq, *dst_vq;
int ret;
- ret = vidioc_try_decoder_cmd(file, priv, cmd);
+ ret = stateful_try_decoder_cmd(file, priv, cmd);
if (ret)
return ret;
@@ -128,6 +125,57 @@ static int vidioc_decoder_cmd(struct file *file, void *priv,
return 0;
}
+static int stateless_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd)
+{
+ return v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd);
+}
+
+static int stateless_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd)
+{
+ struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv);
+ int ret;
+
+ ret = v4l2_m2m_ioctl_stateless_try_decoder_cmd(file, priv, cmd);
+ if (ret)
+ return ret;
+
+ mtk_v4l2_vdec_dbg(3, ctx, "decoder cmd=%u", cmd->cmd);
+ switch (cmd->cmd) {
+ case V4L2_DEC_CMD_FLUSH:
+ /*
+ * If the flag of the output buffer is equals V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF,
+ * this command will prevent dequeueing the capture buffer containing the last
+ * decoded frame. Or do nothing
+ */
+ break;
+ default:
+ mtk_v4l2_vdec_err(ctx, "invalid stateless decoder cmd=%u", cmd->cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vidioc_try_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd)
+{
+ struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv);
+
+ if (ctx->dev->vdec_pdata->uses_stateless_api)
+ return stateless_try_decoder_cmd(file, priv, cmd);
+
+ return stateful_try_decoder_cmd(file, priv, cmd);
+}
+
+static int vidioc_decoder_cmd(struct file *file, void *priv, struct v4l2_decoder_cmd *cmd)
+{
+ struct mtk_vcodec_dec_ctx *ctx = fh_to_dec_ctx(priv);
+
+ if (ctx->dev->vdec_pdata->uses_stateless_api)
+ return stateless_decoder_cmd(file, priv, cmd);
+
+ return stateful_decoder_cmd(file, priv, cmd);
+}
+
void mtk_vdec_unlock(struct mtk_vcodec_dec_ctx *ctx)
{
mutex_unlock(&ctx->dev->dec_mutex[ctx->hw_id]);
@@ -262,7 +310,7 @@ static int vidioc_try_fmt(struct mtk_vcodec_dec_ctx *ctx, struct v4l2_format *f,
int tmp_w, tmp_h;
/*
- * Find next closer width align 64, heign align 64, size align
+ * Find next closer width align 64, height align 64, size align
* 64 rectangle
* Note: This only get default value, the real HW needed value
* only available when ctx in MTK_STATE_HEADER state
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
index 2073781ccadb..9247d92d431d 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.c
@@ -591,7 +591,7 @@ static void mtk_vcodec_dec_remove(struct platform_device *pdev)
static struct platform_driver mtk_vcodec_dec_driver = {
.probe = mtk_vcodec_probe,
- .remove_new = mtk_vcodec_dec_remove,
+ .remove = mtk_vcodec_dec_remove,
.driver = {
.name = MTK_VCODEC_DEC_NAME,
.of_match_table = mtk_vcodec_match,
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
index 85b2c0d3d8bc..aececca7ecf8 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_drv.h
@@ -17,6 +17,7 @@
#define IS_VDEC_LAT_ARCH(hw_arch) ((hw_arch) >= MTK_VDEC_LAT_SINGLE_CORE)
#define IS_VDEC_INNER_RACING(capability) ((capability) & MTK_VCODEC_INNER_RACING)
+#define IS_VDEC_SUPPORT_EXT(capability) ((capability) & MTK_VDEC_IS_SUPPORT_EXT)
enum mtk_vcodec_dec_chip_name {
MTK_VDEC_INVAL = 0,
@@ -42,6 +43,7 @@ enum mtk_vdec_format_types {
MTK_VDEC_FORMAT_HEVC_FRAME = 0x1000,
MTK_VCODEC_INNER_RACING = 0x20000,
MTK_VDEC_IS_SUPPORT_10BIT = 0x40000,
+ MTK_VDEC_IS_SUPPORT_EXT = 0x80000,
};
/*
@@ -67,11 +69,11 @@ enum mtk_vdec_hw_arch {
* @pic_w: picture width
* @pic_h: picture height
* @buf_w: picture buffer width (64 aligned up from pic_w)
- * @buf_h: picture buffer heiht (64 aligned up from pic_h)
+ * @buf_h: picture buffer height (64 aligned up from pic_h)
* @fb_sz: bitstream size of each plane
* E.g. suppose picture size is 176x144,
* buffer size will be aligned to 176x160.
- * @cap_fourcc: fourcc number(may changed when resolution change)
+ * @cap_fourcc: fourcc number(may change on a resolution change)
* @reserved: align struct to 64-bit in order to adjust 32-bit and 64-bit os.
*/
struct vdec_pic_info {
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c
index 11ca2c2fbaad..aa9bdee7a96c 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateful.c
@@ -595,11 +595,9 @@ static void mtk_init_vdec_params(struct mtk_vcodec_dec_ctx *ctx)
}
}
-static struct vb2_ops mtk_vdec_frame_vb2_ops = {
+static const struct vb2_ops mtk_vdec_frame_vb2_ops = {
.queue_setup = vb2ops_vdec_queue_setup,
.buf_prepare = vb2ops_vdec_buf_prepare,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = vb2ops_vdec_start_streaming,
.buf_queue = vb2ops_vdec_stateful_buf_queue,
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
index b903e39fee89..d873159b9b30 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/mtk_vcodec_dec_stateless.c
@@ -152,8 +152,6 @@ static const struct mtk_stateless_control mtk_stateless_controls[] = {
.id = V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
.def = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
.max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
- .menu_skip_mask =
- BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
},
.codec_type = V4L2_PIX_FMT_HEVC_SLICE,
},
@@ -854,10 +852,8 @@ static int vb2ops_vdec_out_buf_validate(struct vb2_buffer *vb)
return 0;
}
-static struct vb2_ops mtk_vdec_request_vb2_ops = {
+static const struct vb2_ops mtk_vdec_request_vb2_ops = {
.queue_setup = vb2ops_vdec_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = vb2ops_vdec_start_streaming,
.stop_streaming = vb2ops_vdec_stop_streaming,
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index 2b6a5adbc419..bf21f2467a0f 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -326,7 +326,7 @@ struct vdec_av1_slice_quantization {
* @use_lr: whether to use loop restoration
* @use_chroma_lr: whether to use chroma loop restoration
* @frame_restoration_type: specifies the type of restoration used for each plane
- * @loop_restoration_size: pecifies the size of loop restoration units in units
+ * @loop_restoration_size: specifies the size of loop restoration units in units
* of samples in the current plane
*/
struct vdec_av1_slice_lr {
@@ -347,7 +347,7 @@ struct vdec_av1_slice_lr {
* and loop_filter_sharpness together determine when
* a block edge is filtered, and by how much the
* filtering can change the sample values
- * @loop_filter_delta_enabled: filetr level depends on the mode and reference
+ * @loop_filter_delta_enabled: filter level depends on the mode and reference
* frame used to predict a block
*/
struct vdec_av1_slice_loop_filter {
@@ -392,7 +392,7 @@ struct vdec_av1_slice_mfmv {
/**
* struct vdec_av1_slice_tile - AV1 Tile info
* @tile_cols: specifies the number of tiles across the frame
- * @tile_rows: pecifies the number of tiles down the frame
+ * @tile_rows: specifies the number of tiles down the frame
* @mi_col_starts: an array specifying the start column
* @mi_row_starts: an array specifying the start row
* @context_update_tile_id: specifies which tile to use for the CDF update
@@ -423,15 +423,15 @@ struct vdec_av1_slice_tile {
* or the tile sizes are coded
* @interpolation_filter: specifies the filter selection used for performing inter prediction
* @allow_warped_motion: motion_mode may be present or not
- * @is_motion_mode_switchable : euqlt to 0 specifies that only the SIMPLE motion mode will be used
+ * @is_motion_mode_switchable : equal to 0 specifies that only the SIMPLE motion mode will be used
* @reference_mode : frame reference mode selected
* @allow_high_precision_mv: specifies that motion vectors are specified to
* quarter pel precision or to eighth pel precision
- * @allow_intra_bc: ubducates that intra block copy may be used in this frame
+ * @allow_intra_bc: allows that intra block copy may be used in this frame
* @force_integer_mv: specifies motion vectors will always be integers or
* can contain fractional bits
* @allow_screen_content_tools: intra blocks may use palette encoding
- * @error_resilient_mode: error resislent mode is enable/disable
+ * @error_resilient_mode: error resilient mode is enable/disable
* @frame_type: specifies the AV1 frame type
* @primary_ref_frame: specifies which reference frame contains the CDF values
* and other state that should be loaded at the start of the frame
@@ -440,8 +440,8 @@ struct vdec_av1_slice_tile {
* @disable_cdf_update: specified whether the CDF update in the symbol
* decoding process should be disables
* @skip_mode: av1 skip mode parameters
- * @seg: av1 segmentaon parameters
- * @delta_q_lf: av1 delta loop fileter
+ * @seg: av1 segmentation parameters
+ * @delta_q_lf: av1 delta loop filter
* @quant: av1 Quantization params
* @lr: av1 Loop Restauration parameters
* @superres_denom: the denominator for the upscaling ratio
@@ -450,8 +450,8 @@ struct vdec_av1_slice_tile {
* @mfmv: av1 mfmv parameters
* @tile: av1 Tile info
* @frame_is_intra: intra frame
- * @loss_less_array: loss less array
- * @coded_loss_less: coded lsss less
+ * @loss_less_array: lossless array
+ * @coded_loss_less: coded lossless
* @mi_rows: size of mi unit in rows
* @mi_cols: size of mi unit in cols
*/
@@ -1023,18 +1023,26 @@ static void vdec_av1_slice_free_working_buffer(struct vdec_av1_slice_instance *i
int i;
for (i = 0; i < ARRAY_SIZE(instance->mv); i++)
- mtk_vcodec_mem_free(ctx, &instance->mv[i]);
+ if (instance->mv[i].va)
+ mtk_vcodec_mem_free(ctx, &instance->mv[i]);
for (i = 0; i < ARRAY_SIZE(instance->seg); i++)
- mtk_vcodec_mem_free(ctx, &instance->seg[i]);
+ if (instance->seg[i].va)
+ mtk_vcodec_mem_free(ctx, &instance->seg[i]);
for (i = 0; i < ARRAY_SIZE(instance->cdf); i++)
- mtk_vcodec_mem_free(ctx, &instance->cdf[i]);
+ if (instance->cdf[i].va)
+ mtk_vcodec_mem_free(ctx, &instance->cdf[i]);
+
- mtk_vcodec_mem_free(ctx, &instance->tile);
- mtk_vcodec_mem_free(ctx, &instance->cdf_temp);
- mtk_vcodec_mem_free(ctx, &instance->cdf_table);
- mtk_vcodec_mem_free(ctx, &instance->iq_table);
+ if (instance->tile.va)
+ mtk_vcodec_mem_free(ctx, &instance->tile);
+ if (instance->cdf_temp.va)
+ mtk_vcodec_mem_free(ctx, &instance->cdf_temp);
+ if (instance->cdf_table.va)
+ mtk_vcodec_mem_free(ctx, &instance->cdf_table);
+ if (instance->iq_table.va)
+ mtk_vcodec_mem_free(ctx, &instance->iq_table);
instance->level = AV1_RES_NONE;
}
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c
index bf7dffe60d07..795cb19b075d 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_if.c
@@ -94,7 +94,7 @@ struct vdec_h264_dec_info {
* AP-W/R : AP is writer/reader on this item
* VPU-W/R: VPU is write/reader on this item
* @hdr_buf : Header parsing buffer (AP-W, VPU-R)
- * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R)
+ * @pred_buf_dma : HW working prediction buffer dma address (AP-W, VPU-R)
* @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R)
* @list_free : free frame buffer ring list (AP-W/R, VPU-W)
* @list_disp : display frame buffer ring list (AP-R, VPU-W)
@@ -117,7 +117,7 @@ struct vdec_h264_vsi {
* struct vdec_h264_inst - h264 decoder instance
* @num_nalu : how many nalus be decoded
* @ctx : point to mtk_vcodec_dec_ctx
- * @pred_buf : HW working predication buffer
+ * @pred_buf : HW working prediction buffer
* @mv_buf : HW working motion vector buffer
* @vpu : VPU instance
* @vsi : VPU shared information
@@ -136,7 +136,7 @@ static unsigned int get_mv_buf_size(unsigned int width, unsigned int height)
return HW_MB_STORE_SZ * (width/MB_UNIT_LEN) * (height/MB_UNIT_LEN);
}
-static int allocate_predication_buf(struct vdec_h264_inst *inst)
+static int allocate_prediction_buf(struct vdec_h264_inst *inst)
{
int err = 0;
@@ -151,7 +151,7 @@ static int allocate_predication_buf(struct vdec_h264_inst *inst)
return 0;
}
-static void free_predication_buf(struct vdec_h264_inst *inst)
+static void free_prediction_buf(struct vdec_h264_inst *inst)
{
struct mtk_vcodec_mem *mem = NULL;
@@ -286,7 +286,7 @@ static int vdec_h264_init(struct mtk_vcodec_dec_ctx *ctx)
}
inst->vsi = (struct vdec_h264_vsi *)inst->vpu.vsi;
- err = allocate_predication_buf(inst);
+ err = allocate_prediction_buf(inst);
if (err)
goto error_deinit;
@@ -308,7 +308,7 @@ static void vdec_h264_deinit(void *h_vdec)
struct vdec_h264_inst *inst = (struct vdec_h264_inst *)h_vdec;
vpu_dec_deinit(&inst->vpu);
- free_predication_buf(inst);
+ free_prediction_buf(inst);
free_mv_buf(inst);
kfree(inst);
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h
index ac82be336055..31ffa13160a3 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_common.h
@@ -175,7 +175,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list,
int num_valid);
/**
- * mtk_vdec_h264_get_ctrl_ptr - get each CID contrl address.
+ * mtk_vdec_h264_get_ctrl_ptr - get each CID control address.
*
* @ctx: v4l2 ctx
* @id: CID control ID
@@ -185,7 +185,7 @@ void mtk_vdec_h264_get_ref_list(u8 *ref_list,
void *mtk_vdec_h264_get_ctrl_ptr(struct mtk_vcodec_dec_ctx *ctx, int id);
/**
- * mtk_vdec_h264_fill_dpb_info - get each CID contrl address.
+ * mtk_vdec_h264_fill_dpb_info - Fill the decoded picture buffer info
*
* @ctx: v4l2 ctx
* @decode_params: slice decode params
@@ -225,10 +225,13 @@ void mtk_vdec_h264_copy_slice_hd_params(struct mtk_h264_slice_hd_param *dst_para
const struct v4l2_ctrl_h264_decode_params *dec_param);
/**
- * mtk_vdec_h264_copy_scaling_matrix - get each CID contrl address.
+ * mtk_vdec_h264_copy_scaling_matrix - Copy scaling matrix from a control to the driver
*
- * @dst_matrix: scaling list params for hw decoder
- * @src_matrix: scaling list params from user driver
+ * @dst_matrix: scaling list params for the HW decoder
+ * @src_matrix: scaling list params from a V4L2 control
+ *
+ * This function is used to copy the scaling matrix from a
+ * v4l2 control into the slice parameters for a decode.
*/
void mtk_vdec_h264_copy_scaling_matrix(struct slice_api_h264_scaling_matrix *dst_matrix,
const struct v4l2_ctrl_h264_scaling_matrix *src_matrix);
@@ -246,7 +249,7 @@ mtk_vdec_h264_copy_decode_params(struct slice_api_h264_decode_param *dst_params,
const struct v4l2_h264_dpb_entry dpb[V4L2_H264_NUM_DPB_ENTRIES]);
/**
- * mtk_vdec_h264_update_dpb - updata dpb list.
+ * mtk_vdec_h264_update_dpb - update dpb list.
*
* @dec_param: v4l2 control decode params
* @dpb: dpb entry informaton
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
index 5600f1df653d..1e1b32faac77 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_if.c
@@ -27,7 +27,7 @@ struct mtk_h264_dec_slice_param {
/**
* struct vdec_h264_dec_info - decode information
* @dpb_sz : decoding picture buffer size
- * @resolution_changed : resoltion change happen
+ * @resolution_changed : flag to notify that a resolution change happened
* @realloc_mv_buf : flag to notify driver to re-allocate mv buffer
* @cap_num_planes : number planes of capture buffer
* @bs_dma : Input bit-stream buffer dma address
@@ -54,7 +54,7 @@ struct vdec_h264_dec_info {
* by VPU.
* AP-W/R : AP is writer/reader on this item
* VPU-W/R: VPU is write/reader on this item
- * @pred_buf_dma : HW working predication buffer dma address (AP-W, VPU-R)
+ * @pred_buf_dma : HW working prediction buffer dma address (AP-W, VPU-R)
* @mv_buf_dma : HW working motion vector buffer dma address (AP-W, VPU-R)
* @dec : decode information (AP-R, VPU-W)
* @pic : picture information (AP-R, VPU-W)
@@ -74,7 +74,7 @@ struct vdec_h264_vsi {
* struct vdec_h264_slice_inst - h264 decoder instance
* @num_nalu : how many nalus be decoded
* @ctx : point to mtk_vcodec_dec_ctx
- * @pred_buf : HW working predication buffer
+ * @pred_buf : HW working prediction buffer
* @mv_buf : HW working motion vector buffer
* @vpu : VPU instance
* @vsi_ctx : Local VSI data for this decoding context
@@ -154,7 +154,7 @@ static int get_vdec_decode_parameters(struct vdec_h264_slice_inst *inst)
return 0;
}
-static int allocate_predication_buf(struct vdec_h264_slice_inst *inst)
+static int allocate_prediction_buf(struct vdec_h264_slice_inst *inst)
{
int err;
@@ -169,7 +169,7 @@ static int allocate_predication_buf(struct vdec_h264_slice_inst *inst)
return 0;
}
-static void free_predication_buf(struct vdec_h264_slice_inst *inst)
+static void free_prediction_buf(struct vdec_h264_slice_inst *inst)
{
struct mtk_vcodec_mem *mem = &inst->pred_buf;
@@ -292,7 +292,7 @@ static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
inst->vsi_ctx.dec.resolution_changed = true;
inst->vsi_ctx.dec.realloc_mv_buf = true;
- err = allocate_predication_buf(inst);
+ err = allocate_prediction_buf(inst);
if (err)
goto error_deinit;
@@ -320,7 +320,7 @@ static void vdec_h264_slice_deinit(void *h_vdec)
struct vdec_h264_slice_inst *inst = h_vdec;
vpu_dec_deinit(&inst->vpu);
- free_predication_buf(inst);
+ free_prediction_buf(inst);
free_mv_buf(inst);
kfree(inst);
@@ -347,11 +347,16 @@ static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
return vpu_dec_reset(vpu);
fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx);
+ if (!fb) {
+ mtk_vdec_err(inst->ctx, "fb buffer is NULL");
+ return -ENOMEM;
+ }
+
src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer);
- y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0;
- c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0;
+ y_fb_dma = fb->base_y.dma_addr;
+ c_fb_dma = fb->base_c.dma_addr;
mtk_vdec_debug(inst->ctx, "+ [%d] FB y_dma=%llx c_dma=%llx va=%p",
inst->num_nalu, y_fb_dma, c_fb_dma, fb);
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
index 0e741e0dc8ba..5b25e1679b51 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_h264_req_multi_if.c
@@ -30,6 +30,7 @@ enum vdec_h264_core_dec_err_type {
/**
* struct vdec_h264_slice_lat_dec_param - parameters for decode current frame
+ * (shared data between host and firmware)
*
* @sps: h264 sps syntax parameters
* @pps: h264 pps syntax parameters
@@ -48,10 +49,10 @@ struct vdec_h264_slice_lat_dec_param {
};
/**
- * struct vdec_h264_slice_info - decode information
+ * struct vdec_h264_slice_info - decode information (shared data between host and firmware)
*
* @nal_info: nal info of current picture
- * @timeout: Decode timeout: 1 timeout, 0 no timeount
+ * @timeout: Decode timeout: 1 timeout, 0 no timeout
* @bs_buf_size: bitstream size
* @bs_buf_addr: bitstream buffer dma address
* @y_fb_dma: Y frame buffer dma address
@@ -72,23 +73,22 @@ struct vdec_h264_slice_info {
/**
* struct vdec_h264_slice_vsi - shared memory for decode information exchange
- * between SCP and Host.
+ * between SCP and Host (shared data between host and firmware).
*
- * @wdma_err_addr: wdma error dma address
- * @wdma_start_addr: wdma start dma address
- * @wdma_end_addr: wdma end dma address
- * @slice_bc_start_addr: slice bc start dma address
- * @slice_bc_end_addr: slice bc end dma address
- * @row_info_start_addr: row info start dma address
- * @row_info_end_addr: row info end dma address
- * @trans_start: trans start dma address
- * @trans_end: trans end dma address
- * @wdma_end_addr_offset: wdma end address offset
+ * @wdma_err_addr: wdma error dma address
+ * @wdma_start_addr: wdma start dma address
+ * @wdma_end_addr: wdma end dma address
+ * @slice_bc_start_addr: slice bc start dma address
+ * @slice_bc_end_addr: slice bc end dma address
+ * @row_info_start_addr: row info start dma address
+ * @row_info_end_addr: row info end dma address
+ * @trans_start: trans start dma address
+ * @trans_end: trans end dma address
+ * @wdma_end_addr_offset: wdma end address offset
*
- * @mv_buf_dma: HW working motion vector buffer
- * dma address (AP-W, VPU-R)
- * @dec: decode information (AP-R, VPU-W)
- * @h264_slice_params: decode parameters for hw used
+ * @mv_buf_dma: HW working motion vector buffer
+ * @dec: decode information (AP-R, VPU-W)
+ * @h264_slice_params: decode parameters for hw used
*/
struct vdec_h264_slice_vsi {
/* LAT dec addr */
@@ -112,12 +112,12 @@ struct vdec_h264_slice_vsi {
* struct vdec_h264_slice_share_info - shared information used to exchange
* message between lat and core
*
- * @sps: sequence header information from user space
- * @dec_params: decoder params from user space
- * @h264_slice_params: decoder params used for hardware
- * @trans_start: trans start dma address
- * @trans_end: trans end dma address
- * @nal_info: nal info of current picture
+ * @sps: sequence header information from user space
+ * @dec_params: decoder params from user space
+ * @h264_slice_params: decoder params used for hardware
+ * @trans_start: trans start dma address
+ * @trans_end: trans end dma address
+ * @nal_info: nal info of current picture
*/
struct vdec_h264_slice_share_info {
struct v4l2_ctrl_h264_sps sps;
@@ -128,27 +128,111 @@ struct vdec_h264_slice_share_info {
u16 nal_info;
};
+/*
+ * struct vdec_h264_slice_mem - memory address and size
+ * (shared data between host and firmware)
+ */
+struct vdec_h264_slice_mem {
+ union {
+ u64 buf;
+ u64 dma_addr;
+ };
+ union {
+ size_t size;
+ u64 dma_addr_end;
+ };
+};
+
+/**
+ * struct vdec_h264_slice_fb - frame buffer for decoding
+ * (shared data between host and firmware)
+ *
+ * @y: current luma buffer address info
+ * @c: current chroma buffer address info
+ */
+struct vdec_h264_slice_fb {
+ struct vdec_h264_slice_mem y;
+ struct vdec_h264_slice_mem c;
+};
+
+/**
+ * struct vdec_h264_slice_info_ext - extend decode information
+ * (shared data between host and firmware)
+ *
+ * @wdma_end_addr_offset: offset from buffer start
+ * @nal_info: nal info of current picture
+ * @timeout: toggles whether a decode operation is timeout
+ * @reserved: reserved
+ * @vdec_fb_va: vdec frame buffer struct virtual address
+ * @crc: displays the hardware status
+ */
+struct vdec_h264_slice_info_ext {
+ u64 wdma_end_addr_offset;
+ u16 nal_info;
+ u16 timeout;
+ u32 reserved;
+ u64 vdec_fb_va;
+ u32 crc[8];
+};
+
+/**
+ * struct vdec_h264_slice_vsi_ext - extend shared memory for decode information exchange
+ * between SCP and Host (shared data between host and firmware).
+ *
+ * @bs: input buffer info
+ * @fb: current y/c buffer
+ *
+ * @ube: buffer used to share date between lat and core
+ * @trans: transcoded buffer used for core decode
+ * @row_info: row info buffer
+ * @err_map: error map buffer
+ * @slice_bc: slice buffer
+ *
+ * @mv_buf_dma: store hardware motion vector data
+ * @dec: decode information (AP-R, VPU-W)
+ * @h264_slice_params: decode parameters used for the hw
+ */
+struct vdec_h264_slice_vsi_ext {
+ /* LAT dec addr */
+ struct vdec_h264_slice_mem bs;
+ struct vdec_h264_slice_fb fb;
+
+ struct vdec_h264_slice_mem ube;
+ struct vdec_h264_slice_mem trans;
+ struct vdec_h264_slice_mem row_info;
+ struct vdec_h264_slice_mem err_map;
+ struct vdec_h264_slice_mem slice_bc;
+
+ struct vdec_h264_slice_mem mv_buf_dma[H264_MAX_MV_NUM];
+ struct vdec_h264_slice_info_ext dec;
+ struct vdec_h264_slice_lat_dec_param h264_slice_params;
+};
+
/**
* struct vdec_h264_slice_inst - h264 decoder instance
*
- * @slice_dec_num: how many picture be decoded
+ * @slice_dec_num: Number of frames to be decoded
* @ctx: point to mtk_vcodec_dec_ctx
- * @pred_buf: HW working predication buffer
+ * @pred_buf: HW working prediction buffer
* @mv_buf: HW working motion vector buffer
* @vpu: VPU instance
* @vsi: vsi used for lat
* @vsi_core: vsi used for core
- *
- * @vsi_ctx: Local VSI data for this decoding context
+ * @vsi_ctx: vsi data for this decoding context
+ * @vsi_ext: extended vsi used for lat
+ * @vsi_core_ext: extended vsi used for core
+ * @vsi_ctx_ext: extended vsi data for this decoding context
* @h264_slice_param: the parameters that hardware use to decode
*
- * @resolution_changed:resolution changed
+ * @resolution_changed: resolution changed
* @realloc_mv_buf: reallocate mv buffer
* @cap_num_planes: number of capture queue plane
*
* @dpb: decoded picture buffer used to store reference
* buffer information
- *@is_field_bitstream: is field bitstream
+ * @is_field_bitstream: not support field bitstream, only support frame
+ *
+ * @decode: lat decoder pointer for different architectures
*/
struct vdec_h264_slice_inst {
unsigned int slice_dec_num;
@@ -156,10 +240,18 @@ struct vdec_h264_slice_inst {
struct mtk_vcodec_mem pred_buf;
struct mtk_vcodec_mem mv_buf[H264_MAX_MV_NUM];
struct vdec_vpu_inst vpu;
- struct vdec_h264_slice_vsi *vsi;
- struct vdec_h264_slice_vsi *vsi_core;
-
- struct vdec_h264_slice_vsi vsi_ctx;
+ union {
+ struct {
+ struct vdec_h264_slice_vsi *vsi;
+ struct vdec_h264_slice_vsi *vsi_core;
+ struct vdec_h264_slice_vsi vsi_ctx;
+ };
+ struct {
+ struct vdec_h264_slice_vsi_ext *vsi_ext;
+ struct vdec_h264_slice_vsi_ext *vsi_core_ext;
+ struct vdec_h264_slice_vsi_ext vsi_ctx_ext;
+ };
+ };
struct vdec_h264_slice_lat_dec_param h264_slice_param;
unsigned int resolution_changed;
@@ -168,12 +260,15 @@ struct vdec_h264_slice_inst {
struct v4l2_h264_dpb_entry dpb[16];
bool is_field_bitstream;
+
+ int (*decode)(void *h_vdec, struct mtk_vcodec_mem *bs,
+ struct vdec_fb *unused, bool *res_chg);
};
static int vdec_h264_slice_fill_decode_parameters(struct vdec_h264_slice_inst *inst,
- struct vdec_h264_slice_share_info *share_info)
+ struct vdec_h264_slice_share_info *share_info,
+ struct vdec_h264_slice_lat_dec_param *slice_param)
{
- struct vdec_h264_slice_lat_dec_param *slice_param = &inst->vsi->h264_slice_params;
const struct v4l2_ctrl_h264_decode_params *dec_params;
const struct v4l2_ctrl_h264_scaling_matrix *src_matrix;
const struct v4l2_ctrl_h264_sps *sps;
@@ -266,9 +361,6 @@ static int get_vdec_sig_decode_parameters(struct vdec_h264_slice_inst *inst)
mtk_vdec_h264_get_ref_list(b0_reflist, v4l2_b0_reflist, reflist_builder.num_valid);
mtk_vdec_h264_get_ref_list(b1_reflist, v4l2_b1_reflist, reflist_builder.num_valid);
- memcpy(&inst->vsi_ctx.h264_slice_params, slice_param,
- sizeof(inst->vsi_ctx.h264_slice_params));
-
return 0;
}
@@ -392,68 +484,148 @@ static void vdec_h264_slice_get_crop_info(struct vdec_h264_slice_inst *inst,
cr->left, cr->top, cr->width, cr->height);
}
-static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
+static void vdec_h264_slice_setup_lat_buffer_ext(struct vdec_h264_slice_inst *inst,
+ struct mtk_vcodec_mem *bs,
+ struct vdec_lat_buf *lat_buf)
{
- struct vdec_h264_slice_inst *inst;
- int err, vsi_size;
+ struct mtk_vcodec_mem *mem;
+ int i;
- inst = kzalloc(sizeof(*inst), GFP_KERNEL);
- if (!inst)
- return -ENOMEM;
+ inst->vsi_ext->bs.dma_addr = (u64)bs->dma_addr;
+ inst->vsi_ext->bs.size = bs->size;
- inst->ctx = ctx;
+ for (i = 0; i < H264_MAX_MV_NUM; i++) {
+ mem = &inst->mv_buf[i];
+ inst->vsi_ext->mv_buf_dma[i].dma_addr = mem->dma_addr;
+ inst->vsi_ext->mv_buf_dma[i].size = mem->size;
+ }
+ inst->vsi_ext->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr;
+ inst->vsi_ext->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size;
- inst->vpu.id = SCP_IPI_VDEC_LAT;
- inst->vpu.core_id = SCP_IPI_VDEC_CORE;
- inst->vpu.ctx = ctx;
- inst->vpu.codec_type = ctx->current_codec;
- inst->vpu.capture_type = ctx->capture_fourcc;
+ inst->vsi_ext->row_info.dma_addr = 0;
+ inst->vsi_ext->row_info.size = 0;
- err = vpu_dec_init(&inst->vpu);
- if (err) {
- mtk_vdec_err(ctx, "vdec_h264 init err=%d", err);
- goto error_free_inst;
+ inst->vsi_ext->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr;
+ inst->vsi_ext->err_map.size = lat_buf->wdma_err_addr.size;
+
+ inst->vsi_ext->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr;
+ inst->vsi_ext->slice_bc.size = lat_buf->slice_bc_addr.size;
+
+ inst->vsi_ext->trans.dma_addr_end = inst->ctx->msg_queue.wdma_rptr_addr;
+ inst->vsi_ext->trans.dma_addr = inst->ctx->msg_queue.wdma_wptr_addr;
+}
+
+static int vdec_h264_slice_setup_core_buffer_ext(struct vdec_h264_slice_inst *inst,
+ struct vdec_h264_slice_share_info *share_info,
+ struct vdec_lat_buf *lat_buf)
+{
+ struct mtk_vcodec_mem *mem;
+ struct mtk_vcodec_dec_ctx *ctx = inst->ctx;
+ struct vb2_v4l2_buffer *vb2_v4l2;
+ struct vdec_fb *fb;
+ u64 y_fb_dma, c_fb_dma = 0;
+ int i;
+
+ fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx);
+ if (!fb) {
+ mtk_vdec_err(ctx, "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
+ return -EBUSY;
}
- vsi_size = round_up(sizeof(struct vdec_h264_slice_vsi), VCODEC_DEC_ALIGNED_64);
- inst->vsi = inst->vpu.vsi;
- inst->vsi_core =
- (struct vdec_h264_slice_vsi *)(((char *)inst->vpu.vsi) + vsi_size);
- inst->resolution_changed = true;
- inst->realloc_mv_buf = true;
+ y_fb_dma = (u64)fb->base_y.dma_addr;
+ if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1)
+ c_fb_dma = y_fb_dma + ctx->picinfo.fb_sz[0];
+ else
+ c_fb_dma = (u64)fb->base_c.dma_addr;
- mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n",
- (int)sizeof(struct mtk_h264_sps_param),
- (int)sizeof(struct mtk_h264_pps_param),
- (int)sizeof(struct vdec_h264_slice_lat_dec_param),
- (int)sizeof(struct mtk_h264_dpb_info),
- vsi_size);
- mtk_vdec_debug(ctx, "lat H264 instance >> %p, codec_type = 0x%x",
- inst, inst->vpu.codec_type);
+ mtk_vdec_debug(ctx, "[h264-core] y/c addr = 0x%llx 0x%llx", y_fb_dma, c_fb_dma);
- ctx->drv_handle = inst;
- return 0;
+ inst->vsi_core_ext->fb.y.dma_addr = y_fb_dma;
+ inst->vsi_core_ext->fb.y.size = ctx->picinfo.fb_sz[0];
+ inst->vsi_core_ext->fb.c.dma_addr = c_fb_dma;
+ inst->vsi_core_ext->fb.c.size = ctx->picinfo.fb_sz[1];
-error_free_inst:
- kfree(inst);
- return err;
+ inst->vsi_core_ext->dec.vdec_fb_va = (unsigned long)fb;
+ inst->vsi_core_ext->dec.nal_info = share_info->nal_info;
+
+ inst->vsi_core_ext->ube.dma_addr = lat_buf->ctx->msg_queue.wdma_addr.dma_addr;
+ inst->vsi_core_ext->ube.size = lat_buf->ctx->msg_queue.wdma_addr.size;
+
+ inst->vsi_core_ext->err_map.dma_addr = lat_buf->wdma_err_addr.dma_addr;
+ inst->vsi_core_ext->err_map.size = lat_buf->wdma_err_addr.size;
+
+ inst->vsi_core_ext->slice_bc.dma_addr = lat_buf->slice_bc_addr.dma_addr;
+ inst->vsi_core_ext->slice_bc.size = lat_buf->slice_bc_addr.size;
+
+ inst->vsi_core_ext->row_info.dma_addr = 0;
+ inst->vsi_core_ext->row_info.size = 0;
+
+ inst->vsi_core_ext->trans.dma_addr = share_info->trans_start;
+ inst->vsi_core_ext->trans.dma_addr_end = share_info->trans_end;
+
+ for (i = 0; i < H264_MAX_MV_NUM; i++) {
+ mem = &inst->mv_buf[i];
+ inst->vsi_core_ext->mv_buf_dma[i].dma_addr = mem->dma_addr;
+ inst->vsi_core_ext->mv_buf_dma[i].size = mem->size;
+ }
+
+ vb2_v4l2 = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+ v4l2_m2m_buf_copy_metadata(&lat_buf->ts_info, vb2_v4l2, true);
+
+ return 0;
}
-static void vdec_h264_slice_deinit(void *h_vdec)
+static int vdec_h264_slice_core_decode_ext(struct vdec_lat_buf *lat_buf)
{
- struct vdec_h264_slice_inst *inst = h_vdec;
+ int err, timeout;
+ struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx;
+ struct vdec_h264_slice_inst *inst = ctx->drv_handle;
+ struct vdec_h264_slice_share_info *share_info = lat_buf->private_data;
+ struct vdec_vpu_inst *vpu = &inst->vpu;
- vpu_dec_deinit(&inst->vpu);
- vdec_h264_slice_free_mv_buf(inst);
- vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx);
+ memcpy(&inst->vsi_core_ext->h264_slice_params, &share_info->h264_slice_params,
+ sizeof(share_info->h264_slice_params));
- kfree(inst);
+ err = vdec_h264_slice_setup_core_buffer_ext(inst, share_info, lat_buf);
+ if (err)
+ goto vdec_dec_end;
+
+ vdec_h264_slice_fill_decode_reflist(inst, &inst->vsi_core_ext->h264_slice_params,
+ share_info);
+ err = vpu_dec_core(vpu);
+ if (err) {
+ mtk_vdec_err(ctx, "core decode err=%d", err);
+ goto vdec_dec_end;
+ }
+
+ /* wait decoder done interrupt */
+ timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED,
+ WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE);
+ if (timeout)
+ mtk_vdec_err(ctx, "core decode timeout: pic_%d", ctx->decoded_frame_cnt);
+ inst->vsi_core_ext->dec.timeout = !!timeout;
+
+ vpu_dec_core_end(vpu);
+
+ /* crc is hardware checksum, can be used to check whether the decoder result is right.*/
+ mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+ ctx->decoded_frame_cnt,
+ inst->vsi_core_ext->dec.crc[0], inst->vsi_core_ext->dec.crc[1],
+ inst->vsi_core_ext->dec.crc[2], inst->vsi_core_ext->dec.crc[3],
+ inst->vsi_core_ext->dec.crc[4], inst->vsi_core_ext->dec.crc[5],
+ inst->vsi_core_ext->dec.crc[6], inst->vsi_core_ext->dec.crc[7]);
+
+vdec_dec_end:
+ vdec_msg_queue_update_ube_rptr(&lat_buf->ctx->msg_queue, share_info->trans_end);
+ ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req);
+ mtk_vdec_debug(ctx, "core decode done err=%d", err);
+ ctx->decoded_frame_cnt++;
+ return 0;
}
static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
{
struct vdec_fb *fb;
- u64 vdec_fb_va;
u64 y_fb_dma, c_fb_dma;
int err, timeout, i;
struct mtk_vcodec_dec_ctx *ctx = lat_buf->ctx;
@@ -463,22 +635,19 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
struct mtk_vcodec_mem *mem;
struct vdec_vpu_inst *vpu = &inst->vpu;
- mtk_vdec_debug(ctx, "[h264-core] vdec_h264 core decode");
memcpy(&inst->vsi_core->h264_slice_params, &share_info->h264_slice_params,
sizeof(share_info->h264_slice_params));
fb = ctx->dev->vdec_pdata->get_cap_buffer(ctx);
if (!fb) {
err = -EBUSY;
- mtk_vdec_err(ctx, "fb buffer is NULL");
+ mtk_vdec_err(ctx, "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
goto vdec_dec_end;
}
- vdec_fb_va = (unsigned long)fb;
y_fb_dma = (u64)fb->base_y.dma_addr;
if (ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1)
- c_fb_dma =
- y_fb_dma + inst->ctx->picinfo.buf_w * inst->ctx->picinfo.buf_h;
+ c_fb_dma = y_fb_dma + ctx->picinfo.fb_sz[0];
else
c_fb_dma = (u64)fb->base_c.dma_addr;
@@ -486,7 +655,7 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
inst->vsi_core->dec.y_fb_dma = y_fb_dma;
inst->vsi_core->dec.c_fb_dma = c_fb_dma;
- inst->vsi_core->dec.vdec_fb_va = vdec_fb_va;
+ inst->vsi_core->dec.vdec_fb_va = (unsigned long)fb;
inst->vsi_core->dec.nal_info = share_info->nal_info;
inst->vsi_core->wdma_start_addr =
lat_buf->ctx->msg_queue.wdma_addr.dma_addr;
@@ -524,6 +693,8 @@ static int vdec_h264_slice_core_decode(struct vdec_lat_buf *lat_buf)
inst->vsi_core->dec.timeout = !!timeout;
vpu_dec_core_end(vpu);
+
+ /* crc is hardware checksum, can be used to check whether the decoder result is right.*/
mtk_vdec_debug(ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
ctx->decoded_frame_cnt,
inst->vsi_core->dec.crc[0], inst->vsi_core->dec.crc[1],
@@ -536,6 +707,7 @@ vdec_dec_end:
ctx->dev->vdec_pdata->cap_to_disp(ctx, !!err, lat_buf->src_buf_req);
mtk_vdec_debug(ctx, "core decode done err=%d", err);
ctx->decoded_frame_cnt++;
+
return 0;
}
@@ -562,6 +734,128 @@ static void vdec_h264_insert_startcode(struct mtk_vcodec_dec_dev *vcodec_dev, un
(*bs_size) += 4;
}
+static int vdec_h264_slice_lat_decode_ext(void *h_vdec, struct mtk_vcodec_mem *bs,
+ struct vdec_fb *fb, bool *res_chg)
+{
+ struct vdec_h264_slice_inst *inst = h_vdec;
+ struct vdec_vpu_inst *vpu = &inst->vpu;
+ struct mtk_video_dec_buf *src_buf_info;
+ int err, timeout = 0;
+ unsigned int data[2];
+ struct vdec_lat_buf *lat_buf;
+ struct vdec_h264_slice_share_info *share_info;
+
+ if (vdec_msg_queue_init(&inst->ctx->msg_queue, inst->ctx,
+ vdec_h264_slice_core_decode_ext,
+ sizeof(*share_info)))
+ return -ENOMEM;
+
+ /* bs NULL means flush decoder */
+ if (!bs) {
+ vdec_msg_queue_wait_lat_buf_full(&inst->ctx->msg_queue);
+ return vpu_dec_reset(vpu);
+ }
+
+ if (inst->is_field_bitstream)
+ return -EINVAL;
+
+ lat_buf = vdec_msg_queue_dqbuf(&inst->ctx->msg_queue.lat_ctx);
+ if (!lat_buf) {
+ mtk_vdec_debug(inst->ctx, "failed to get lat buffer");
+ return -EAGAIN;
+ }
+ share_info = lat_buf->private_data;
+ src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
+
+ lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req;
+ v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true);
+
+ err = vdec_h264_slice_fill_decode_parameters(inst, share_info,
+ &inst->vsi_ext->h264_slice_params);
+ if (err)
+ goto err_free_fb_out;
+
+ vdec_h264_insert_startcode(inst->ctx->dev, bs->va, &bs->size,
+ &share_info->h264_slice_params.pps);
+
+ *res_chg = inst->resolution_changed;
+ if (inst->resolution_changed) {
+ mtk_vdec_debug(inst->ctx, "- resolution changed -");
+ if (inst->realloc_mv_buf) {
+ err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo);
+ inst->realloc_mv_buf = false;
+ if (err)
+ goto err_free_fb_out;
+ }
+ inst->resolution_changed = false;
+ }
+
+ vdec_h264_slice_setup_lat_buffer_ext(inst, bs, lat_buf);
+ mtk_vdec_debug(inst->ctx, "lat:trans(0x%llx 0x%lx) err:0x%llx",
+ inst->vsi_ext->ube.dma_addr, (unsigned long)inst->vsi_ext->ube.size,
+ inst->vsi_ext->err_map.dma_addr);
+
+ mtk_vdec_debug(inst->ctx, "slice(0x%llx 0x%lx) rprt((0x%llx 0x%llx))",
+ inst->vsi_ext->slice_bc.dma_addr,
+ (unsigned long)inst->vsi_ext->slice_bc.size,
+ inst->vsi_ext->trans.dma_addr, inst->vsi_ext->trans.dma_addr_end);
+
+ err = vpu_dec_start(vpu, data, 2);
+ if (err) {
+ mtk_vdec_debug(inst->ctx, "lat decode err: %d", err);
+ goto err_free_fb_out;
+ }
+
+ share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr +
+ inst->vsi_ext->dec.wdma_end_addr_offset;
+
+ share_info->trans_start = inst->ctx->msg_queue.wdma_wptr_addr;
+ share_info->nal_info = inst->vsi_ext->dec.nal_info;
+
+ if (IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) {
+ memcpy(&share_info->h264_slice_params, &inst->vsi_ext->h264_slice_params,
+ sizeof(share_info->h264_slice_params));
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf);
+ }
+
+ /* wait decoder done interrupt */
+ timeout = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED,
+ WAIT_INTR_TIMEOUT_MS, MTK_VDEC_LAT0);
+ if (timeout)
+ mtk_vdec_err(inst->ctx, "lat decode timeout: pic_%d", inst->slice_dec_num);
+ inst->vsi_ext->dec.timeout = !!timeout;
+
+ err = vpu_dec_end(vpu);
+ if (err == SLICE_HEADER_FULL || err == TRANS_BUFFER_FULL) {
+ if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability))
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf);
+ inst->slice_dec_num++;
+ mtk_vdec_err(inst->ctx, "lat dec fail: pic_%d err:%d", inst->slice_dec_num, err);
+ return -EINVAL;
+ }
+
+ share_info->trans_end = inst->ctx->msg_queue.wdma_addr.dma_addr +
+ inst->vsi_ext->dec.wdma_end_addr_offset;
+
+ vdec_msg_queue_update_ube_wptr(&lat_buf->ctx->msg_queue, share_info->trans_end);
+
+ if (!IS_VDEC_INNER_RACING(inst->ctx->dev->dec_capability)) {
+ memcpy(&share_info->h264_slice_params, &inst->vsi_ext->h264_slice_params,
+ sizeof(share_info->h264_slice_params));
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.core_ctx, lat_buf);
+ }
+ mtk_vdec_debug(inst->ctx, "dec num: %d lat crc: 0x%x 0x%x 0x%x", inst->slice_dec_num,
+ inst->vsi_ext->dec.crc[0], inst->vsi_ext->dec.crc[1],
+ inst->vsi_ext->dec.crc[2]);
+
+ inst->slice_dec_num++;
+ return 0;
+err_free_fb_out:
+ vdec_msg_queue_qbuf(&inst->ctx->msg_queue.lat_ctx, lat_buf);
+ mtk_vdec_err(inst->ctx, "slice dec number: %d err: %d", inst->slice_dec_num, err);
+ return err;
+}
+
static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
struct vdec_fb *fb, bool *res_chg)
{
@@ -608,7 +902,8 @@ static int vdec_h264_slice_lat_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
lat_buf->src_buf_req = src_buf_info->m2m_buf.vb.vb2_buf.req_obj.req;
v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb, &lat_buf->ts_info, true);
- err = vdec_h264_slice_fill_decode_parameters(inst, share_info);
+ err = vdec_h264_slice_fill_decode_parameters(inst, share_info,
+ &inst->vsi->h264_slice_params);
if (err)
goto err_free_fb_out;
@@ -706,6 +1001,101 @@ err_free_fb_out:
return err;
}
+static int vdec_h264_slice_single_decode_ext(void *h_vdec, struct mtk_vcodec_mem *bs,
+ struct vdec_fb *unused, bool *res_chg)
+{
+ struct vdec_h264_slice_inst *inst = h_vdec;
+ struct vdec_vpu_inst *vpu = &inst->vpu;
+ struct mtk_video_dec_buf *src_buf_info, *dst_buf_info;
+ struct vdec_fb *fb;
+ unsigned int data[2], i;
+ u64 y_fb_dma, c_fb_dma;
+ struct mtk_vcodec_mem *mem;
+ int err;
+
+ /* bs NULL means flush decoder */
+ if (!bs)
+ return vpu_dec_reset(vpu);
+
+ fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx);
+ if (!fb) {
+ mtk_vdec_err(inst->ctx,
+ "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
+ return -ENOMEM;
+ }
+
+ src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
+ dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer);
+
+ y_fb_dma = fb->base_y.dma_addr;
+ c_fb_dma = fb->base_c.dma_addr;
+ mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx c_dma=%llx",
+ inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma);
+
+ inst->vsi_ctx_ext.bs.dma_addr = (u64)bs->dma_addr;
+ inst->vsi_ctx_ext.bs.size = bs->size;
+ inst->vsi_ctx_ext.fb.y.dma_addr = y_fb_dma;
+ inst->vsi_ctx_ext.fb.c.dma_addr = c_fb_dma;
+ inst->vsi_ctx_ext.dec.vdec_fb_va = (u64)(uintptr_t)fb;
+
+ v4l2_m2m_buf_copy_metadata(&src_buf_info->m2m_buf.vb,
+ &dst_buf_info->m2m_buf.vb, true);
+ err = get_vdec_sig_decode_parameters(inst);
+ if (err)
+ goto err_free_fb_out;
+
+ memcpy(&inst->vsi_ctx_ext.h264_slice_params, &inst->h264_slice_param,
+ sizeof(inst->vsi_ctx_ext.h264_slice_params));
+
+ *res_chg = inst->resolution_changed;
+ if (inst->resolution_changed) {
+ mtk_vdec_debug(inst->ctx, "- resolution changed -");
+ if (inst->realloc_mv_buf) {
+ err = vdec_h264_slice_alloc_mv_buf(inst, &inst->ctx->picinfo);
+ inst->realloc_mv_buf = false;
+ if (err)
+ goto err_free_fb_out;
+ }
+ inst->resolution_changed = false;
+
+ for (i = 0; i < H264_MAX_MV_NUM; i++) {
+ mem = &inst->mv_buf[i];
+ inst->vsi_ctx_ext.mv_buf_dma[i].dma_addr = mem->dma_addr;
+ }
+ }
+
+ memcpy(inst->vpu.vsi, &inst->vsi_ctx_ext, sizeof(inst->vsi_ctx_ext));
+ err = vpu_dec_start(vpu, data, 2);
+ if (err)
+ goto err_free_fb_out;
+
+ /* wait decoder done interrupt */
+ err = mtk_vcodec_wait_for_done_ctx(inst->ctx, MTK_INST_IRQ_RECEIVED,
+ WAIT_INTR_TIMEOUT_MS, MTK_VDEC_CORE);
+ if (err)
+ mtk_vdec_err(inst->ctx, "decode timeout: pic_%d", inst->ctx->decoded_frame_cnt);
+
+ inst->vsi_ext->dec.timeout = !!err;
+ err = vpu_dec_end(vpu);
+ if (err)
+ goto err_free_fb_out;
+
+ memcpy(&inst->vsi_ctx_ext, inst->vpu.vsi, sizeof(inst->vsi_ctx_ext));
+ mtk_vdec_debug(inst->ctx, "pic[%d] crc: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
+ inst->ctx->decoded_frame_cnt,
+ inst->vsi_ctx_ext.dec.crc[0], inst->vsi_ctx_ext.dec.crc[1],
+ inst->vsi_ctx_ext.dec.crc[2], inst->vsi_ctx_ext.dec.crc[3],
+ inst->vsi_ctx_ext.dec.crc[4], inst->vsi_ctx_ext.dec.crc[5],
+ inst->vsi_ctx_ext.dec.crc[6], inst->vsi_ctx_ext.dec.crc[7]);
+
+ inst->ctx->decoded_frame_cnt++;
+ return 0;
+
+err_free_fb_out:
+ mtk_vdec_err(inst->ctx, "dec frame number: %d err: %d", inst->ctx->decoded_frame_cnt, err);
+ return err;
+}
+
static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
struct vdec_fb *unused, bool *res_chg)
{
@@ -724,11 +1114,17 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs
return vpu_dec_reset(vpu);
fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx);
+ if (!fb) {
+ mtk_vdec_err(inst->ctx,
+ "Unable to get a CAPTURE buffer for CAPTURE queue is empty.");
+ return -ENOMEM;
+ }
+
src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer);
- y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0;
- c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0;
+ y_fb_dma = fb->base_y.dma_addr;
+ c_fb_dma = fb->base_c.dma_addr;
mtk_vdec_debug(inst->ctx, "[h264-dec] [%d] y_dma=%llx c_dma=%llx",
inst->ctx->decoded_frame_cnt, y_fb_dma, c_fb_dma);
@@ -744,6 +1140,9 @@ static int vdec_h264_slice_single_decode(void *h_vdec, struct mtk_vcodec_mem *bs
if (err)
goto err_free_fb_out;
+ memcpy(&inst->vsi_ctx.h264_slice_params, &inst->h264_slice_param,
+ sizeof(inst->vsi_ctx.h264_slice_params));
+
buf = (unsigned char *)bs->va;
nal_start_idx = mtk_vdec_h264_find_start_code(buf, bs->size);
if (nal_start_idx < 0) {
@@ -801,21 +1200,95 @@ err_free_fb_out:
return err;
}
+static int vdec_h264_slice_init(struct mtk_vcodec_dec_ctx *ctx)
+{
+ struct vdec_h264_slice_inst *inst;
+ int err, vsi_size;
+ unsigned char *temp;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ inst->ctx = ctx;
+
+ inst->vpu.id = SCP_IPI_VDEC_LAT;
+ inst->vpu.core_id = SCP_IPI_VDEC_CORE;
+ inst->vpu.ctx = ctx;
+ inst->vpu.codec_type = ctx->current_codec;
+ inst->vpu.capture_type = ctx->capture_fourcc;
+
+ err = vpu_dec_init(&inst->vpu);
+ if (err) {
+ mtk_vdec_err(ctx, "vdec_h264 init err=%d", err);
+ goto error_free_inst;
+ }
+
+ if (IS_VDEC_SUPPORT_EXT(ctx->dev->dec_capability)) {
+ vsi_size = sizeof(struct vdec_h264_slice_vsi_ext);
+
+ vsi_size = round_up(vsi_size, VCODEC_DEC_ALIGNED_64);
+ inst->vsi_ext = inst->vpu.vsi;
+ temp = (unsigned char *)inst->vsi_ext;
+ inst->vsi_core_ext = (struct vdec_h264_slice_vsi_ext *)(temp + vsi_size);
+
+ if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
+ inst->decode = vdec_h264_slice_single_decode_ext;
+ else
+ inst->decode = vdec_h264_slice_lat_decode_ext;
+ } else {
+ vsi_size = sizeof(struct vdec_h264_slice_vsi);
+
+ vsi_size = round_up(vsi_size, VCODEC_DEC_ALIGNED_64);
+ inst->vsi = inst->vpu.vsi;
+ temp = (unsigned char *)inst->vsi;
+ inst->vsi_core = (struct vdec_h264_slice_vsi *)(temp + vsi_size);
+
+ if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
+ inst->decode = vdec_h264_slice_single_decode;
+ else
+ inst->decode = vdec_h264_slice_lat_decode;
+ }
+ inst->resolution_changed = true;
+ inst->realloc_mv_buf = true;
+
+ mtk_vdec_debug(ctx, "lat struct size = %d,%d,%d,%d vsi: %d\n",
+ (int)sizeof(struct mtk_h264_sps_param),
+ (int)sizeof(struct mtk_h264_pps_param),
+ (int)sizeof(struct vdec_h264_slice_lat_dec_param),
+ (int)sizeof(struct mtk_h264_dpb_info),
+ vsi_size);
+ mtk_vdec_debug(ctx, "lat H264 instance >> %p, codec_type = 0x%x",
+ inst, inst->vpu.codec_type);
+
+ ctx->drv_handle = inst;
+ return 0;
+
+error_free_inst:
+ kfree(inst);
+ return err;
+}
+
+static void vdec_h264_slice_deinit(void *h_vdec)
+{
+ struct vdec_h264_slice_inst *inst = h_vdec;
+
+ vpu_dec_deinit(&inst->vpu);
+ vdec_h264_slice_free_mv_buf(inst);
+ vdec_msg_queue_deinit(&inst->ctx->msg_queue, inst->ctx);
+
+ kfree(inst);
+}
+
static int vdec_h264_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
struct vdec_fb *unused, bool *res_chg)
{
struct vdec_h264_slice_inst *inst = h_vdec;
- int ret;
if (!h_vdec)
return -EINVAL;
- if (inst->ctx->dev->vdec_pdata->hw_arch == MTK_VDEC_PURE_SINGLE_CORE)
- ret = vdec_h264_slice_single_decode(h_vdec, bs, unused, res_chg);
- else
- ret = vdec_h264_slice_lat_decode(h_vdec, bs, unused, res_chg);
-
- return ret;
+ return inst->decode(h_vdec, bs, unused, res_chg);
}
static int vdec_h264_slice_get_param(void *h_vdec, enum vdec_get_param_type type,
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
index 21836dd6ef85..2725db882e5b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_hevc_req_multi_if.c
@@ -254,7 +254,7 @@ struct vdec_hevc_slice_lat_dec_param {
* struct vdec_hevc_slice_info - decode information
*
* @wdma_end_addr_offset: wdma end address offset
- * @timeout: Decode timeout: 1 timeout, 0 no timeount
+ * @timeout: Decode timeout: 1 timeout, 0 no timeout
* @vdec_fb_va: VDEC frame buffer struct virtual address
* @crc: Used to check whether hardware's status is right
*/
@@ -342,7 +342,7 @@ struct vdec_hevc_slice_share_info {
/**
* struct vdec_hevc_slice_inst - hevc decoder instance
*
- * @slice_dec_num: how many picture be decoded
+ * @slice_dec_num: Number of frames to be decoded
* @ctx: point to mtk_vcodec_dec_ctx
* @mv_buf: HW working motion vector buffer
* @vpu: VPU instance
@@ -821,7 +821,7 @@ static int vdec_hevc_slice_setup_core_buffer(struct vdec_hevc_slice_inst *inst,
inst->vsi_core->fb.y.dma_addr = y_fb_dma;
inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[0];
inst->vsi_core->fb.c.dma_addr = c_fb_dma;
- inst->vsi_core->fb.y.size = ctx->picinfo.fb_sz[1];
+ inst->vsi_core->fb.c.size = ctx->picinfo.fb_sz[1];
inst->vsi_core->dec.vdec_fb_va = (unsigned long)fb;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c
index 987b3d71b662..5f848691cea4 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_if.c
@@ -56,7 +56,7 @@
* @cur_c_fb_dma : current plane C frame buffer dma address
* @bs_dma : bitstream dma address
* @bs_sz : bitstream size
- * @resolution_changed: resolution change flag 1 - changed, 0 - not change
+ * @resolution_changed: resolution change flag 1 - changed, 0 - not changed
* @show_frame : display this frame or not
* @wait_key_frame : wait key frame coming
*/
@@ -109,7 +109,7 @@ struct vdec_vp8_hw_reg_base {
/**
* struct vdec_vp8_vpu_inst - VPU instance for VP8 decode
* @wq_hd : Wait queue to wait VPU message ack
- * @signaled : 1 - Host has received ack message from VPU, 0 - not receive
+ * @signaled : 1 - Host has received ack message from VPU, 0 - not received
* @failure : VPU execution result status 0 - success, others - fail
* @inst_addr : VPU decoder instance address
*/
@@ -449,7 +449,7 @@ static int vdec_vp8_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
inst->frm_cnt, y_fb_dma, c_fb_dma, fb);
inst->cur_fb = fb;
- dec->bs_dma = (uint64_t)bs->dma_addr;
+ dec->bs_dma = bs->dma_addr;
dec->bs_sz = bs->size;
dec->cur_y_fb_dma = y_fb_dma;
dec->cur_c_fb_dma = c_fb_dma;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
index f677e499fefa..232ef3bd246a 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp8_req_if.c
@@ -35,7 +35,7 @@
* @cur_c_fb_dma: current plane C frame buffer dma address
* @bs_dma: bitstream dma address
* @bs_sz: bitstream size
- * @resolution_changed:resolution change flag 1 - changed, 0 - not change
+ * @resolution_changed:resolution change flag 1 - changed, 0 - not changed
* @frame_header_type: current frame header type
* @crc: used to check whether hardware's status is right
* @reserved: reserved, currently unused
@@ -334,14 +334,18 @@ static int vdec_vp8_slice_decode(void *h_vdec, struct mtk_vcodec_mem *bs,
src_buf_info = container_of(bs, struct mtk_video_dec_buf, bs_buffer);
fb = inst->ctx->dev->vdec_pdata->get_cap_buffer(inst->ctx);
- dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer);
+ if (!fb) {
+ mtk_vdec_err(inst->ctx, "fb buffer is NULL");
+ return -ENOMEM;
+ }
- y_fb_dma = fb ? (u64)fb->base_y.dma_addr : 0;
+ dst_buf_info = container_of(fb, struct mtk_video_dec_buf, frame_buffer);
+ y_fb_dma = fb->base_y.dma_addr;
if (inst->ctx->q_data[MTK_Q_DATA_DST].fmt->num_planes == 1)
c_fb_dma = y_fb_dma +
inst->ctx->picinfo.buf_w * inst->ctx->picinfo.buf_h;
else
- c_fb_dma = fb ? (u64)fb->base_c.dma_addr : 0;
+ c_fb_dma = fb->base_c.dma_addr;
inst->vsi->dec.bs_dma = (u64)bs->dma_addr;
inst->vsi->dec.bs_sz = bs->size;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
index 039082f600c8..eb3354192853 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_if.c
@@ -42,7 +42,7 @@ struct vp9_dram_buf {
/**
* struct vp9_fb_info - contains frame buffer info
- * @fb : frmae buffer
+ * @fb : frame buffer
* @reserved : reserved field used by vpu
*/
struct vp9_fb_info {
@@ -90,7 +90,7 @@ struct vp9_sf_ref_fb {
* AP-W/R : AP is writer/reader on this item
* VPU-W/R: VPU is write/reader on this item
* @sf_bs_buf : super frame backup buffer (AP-W, VPU-R)
- * @sf_ref_fb : record supoer frame reference buffer information
+ * @sf_ref_fb : record super frame reference buffer information
* (AP-R/W, VPU-R/W)
* @sf_next_ref_fb_idx : next available super frame (AP-W, VPU-R)
* @sf_frm_cnt : super frame count, filled by vpu (AP-R, VPU-W)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
index eea709d93820..47c302745c1d 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_vp9_req_lat_if.c
@@ -1188,7 +1188,8 @@ err:
return ret;
}
-static
+/* clang stack usage explodes if this is inlined */
+static noinline_for_stack
void vdec_vp9_slice_map_counts_eob_coef(unsigned int i, unsigned int j, unsigned int k,
struct vdec_vp9_slice_frame_counts *counts,
struct v4l2_vp9_frame_symbol_counts *counts_helper)
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h
index 1d9beb9e4a14..b0f576867f4b 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_msg_queue.h
@@ -158,14 +158,14 @@ int vdec_msg_queue_qbuf(struct vdec_msg_queue_ctx *ctx, struct vdec_lat_buf *buf
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.
+ * vdec_msg_queue_update_ube_rptr - used to update 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.
+ * vdec_msg_queue_update_ube_wptr - used to update the ube write point.
* @msg_queue: used to store the lat buffer information
* @ube_wptr: current ube write point
*/
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
index da6be556727b..145958206e38 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.c
@@ -233,6 +233,12 @@ int vpu_dec_init(struct vdec_vpu_inst *vpu)
mtk_vdec_debug(vpu->ctx, "vdec_inst=%p", vpu);
err = vcodec_vpu_send_msg(vpu, (void *)&msg, sizeof(msg));
+
+ if (IS_ERR_OR_NULL(vpu->vsi)) {
+ mtk_vdec_err(vpu->ctx, "invalid vdec vsi, status=%d", err);
+ return -EINVAL;
+ }
+
mtk_vdec_debug(vpu->ctx, "- ret=%d", err);
return err;
}
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h
index aa7d08afc2f4..57ed9b1f5eaa 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_vpu_if.h
@@ -81,7 +81,7 @@ int vpu_dec_deinit(struct vdec_vpu_inst *vpu);
/**
* vpu_dec_reset - reset decoder, use for flush decoder when end of stream or
- * seek. Remainig non displayed frame will be pushed to display.
+ * seek. Remaining non displayed frame will be pushed to display.
*
* @vpu: instance for vdec_vpu_inst
*/
@@ -98,7 +98,7 @@ 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
+ * decoder in VPU will update hardware information and deinit hardware
* and check if there is a new decoded frame available to display.
*
* @vpu : instance for vdec_vpu_inst
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
index 181884e798fd..a01dc25a7699 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc.c
@@ -311,7 +311,7 @@ static int vidioc_try_fmt_out(struct mtk_vcodec_enc_ctx *ctx, struct v4l2_format
pix_fmt_mp->height = clamp(pix_fmt_mp->height, MTK_VENC_MIN_H, max_height);
pix_fmt_mp->width = clamp(pix_fmt_mp->width, MTK_VENC_MIN_W, max_width);
- /* find next closer width align 16, heign align 32, size align
+ /* find next closer width align 16, height align 32, size align
* 64 rectangle
*/
tmp_w = pix_fmt_mp->width;
@@ -1009,8 +1009,6 @@ static const struct vb2_ops mtk_venc_vb2_ops = {
.buf_out_validate = vb2ops_venc_buf_out_validate,
.buf_prepare = vb2ops_venc_buf_prepare,
.buf_queue = vb2ops_venc_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = vb2ops_venc_start_streaming,
.stop_streaming = vb2ops_venc_stop_streaming,
};
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
index 3cb8a1622222..a1e4483abcdb 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_drv.c
@@ -473,7 +473,7 @@ static void mtk_vcodec_enc_remove(struct platform_device *pdev)
static struct platform_driver mtk_vcodec_enc_driver = {
.probe = mtk_vcodec_probe,
- .remove_new = mtk_vcodec_enc_remove,
+ .remove = mtk_vcodec_enc_remove,
.driver = {
.name = MTK_VCODEC_ENC_NAME,
.of_match_table = mtk_vcodec_enc_match,
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
index a22b7dfc656e..1a2b14a3e219 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.c
@@ -58,13 +58,15 @@ int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *mtkdev)
return 0;
}
-void mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm)
+int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm)
{
int ret;
ret = pm_runtime_resume_and_get(pm->dev);
if (ret)
dev_err(pm->dev, "pm_runtime_resume_and_get fail: %d", ret);
+
+ return ret;
}
void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm)
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h
index 157ea08ba9e3..2e28f25e36cc 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/mtk_vcodec_enc_pm.h
@@ -10,7 +10,7 @@
#include "mtk_vcodec_enc_drv.h"
int mtk_vcodec_init_enc_clk(struct mtk_vcodec_enc_dev *dev);
-void mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm);
+int mtk_vcodec_enc_pw_on(struct mtk_vcodec_pm *pm);
void mtk_vcodec_enc_pw_off(struct mtk_vcodec_pm *pm);
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/mediatek/vcodec/encoder/venc/venc_h264_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
index a68dac72c4e4..0f63657d8bad 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc/venc_h264_if.c
@@ -301,11 +301,12 @@ static void h264_enc_free_work_buf(struct venc_h264_inst *inst)
* other buffers need to be freed by AP.
*/
for (i = 0; i < VENC_H264_VPU_WORK_BUF_MAX; i++) {
- if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME)
+ if (i != VENC_H264_VPU_WORK_BUF_SKIP_FRAME && inst->work_bufs[i].va)
mtk_vcodec_mem_free(inst->ctx, &inst->work_bufs[i]);
}
- mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf);
+ if (inst->pps_buf.va)
+ mtk_vcodec_mem_free(inst->ctx, &inst->pps_buf);
}
static int h264_enc_alloc_work_buf(struct venc_h264_inst *inst, bool is_34bit)
@@ -514,7 +515,7 @@ static int h264_encode_frame(struct venc_h264_inst *inst,
struct venc_frame_info frame_info;
struct mtk_vcodec_enc_ctx *ctx = inst->ctx;
- mtk_venc_debug(ctx, "frm_cnt = %d\n ", inst->frm_cnt);
+ mtk_venc_debug(ctx, "frm_cnt = %d\n", inst->frm_cnt);
if (MTK_ENC_IOVA_IS_34BIT(ctx)) {
gop_size = inst->vsi_34->config.gop_size;
@@ -593,7 +594,11 @@ static int h264_enc_init(struct mtk_vcodec_enc_ctx *ctx)
inst->ctx = ctx;
inst->vpu_inst.ctx = ctx;
- inst->vpu_inst.id = is_ext ? SCP_IPI_VENC_H264 : IPI_VENC_H264;
+ if (is_ext)
+ inst->vpu_inst.id = SCP_IPI_VENC_H264;
+ else
+ inst->vpu_inst.id = IPI_VENC_H264;
+
inst->hw_base = mtk_vcodec_get_reg_addr(inst->ctx->dev->reg_base, VENC_SYS);
ret = vpu_enc_init(&inst->vpu_inst);
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
index c402a686f3cb..e83747b8d69a 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.c
@@ -64,7 +64,9 @@ int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx,
ctx->dev->curr_ctx = ctx;
spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
- mtk_vcodec_enc_pw_on(&ctx->dev->pm);
+ ret = mtk_vcodec_enc_pw_on(&ctx->dev->pm);
+ if (ret)
+ goto venc_if_encode_pw_on_err;
mtk_vcodec_enc_clock_on(&ctx->dev->pm);
ret = ctx->enc_if->encode(ctx->drv_handle, opt, frm_buf,
bs_buf, result);
@@ -75,6 +77,7 @@ int venc_if_encode(struct mtk_vcodec_enc_ctx *ctx,
ctx->dev->curr_ctx = NULL;
spin_unlock_irqrestore(&ctx->dev->irqlock, flags);
+venc_if_encode_pw_on_err:
mtk_venc_unlock(ctx);
return ret;
}
diff --git a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
index d00fb68b8235..889440a436b6 100644
--- a/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
+++ b/drivers/media/platform/mediatek/vcodec/encoder/venc_drv_if.h
@@ -156,7 +156,7 @@ int venc_if_set_param(struct mtk_vcodec_enc_ctx *ctx,
* @ctx: device context
* @opt: encode frame option
* @frm_buf: input frame buffer information
- * @bs_buf: output bitstream buffer infomraiton
+ * @bs_buf: output bitstream buffer information
* @result: encode result
* Return: 0 if encoding frame successfully, otherwise it is failed.
*/
diff --git a/drivers/media/platform/mediatek/vpu/mtk_vpu.c b/drivers/media/platform/mediatek/vpu/mtk_vpu.c
index 724ae7c2ab3b..8d8319f0cd22 100644
--- a/drivers/media/platform/mediatek/vpu/mtk_vpu.c
+++ b/drivers/media/platform/mediatek/vpu/mtk_vpu.c
@@ -1041,7 +1041,7 @@ static const struct dev_pm_ops mtk_vpu_pm = {
static struct platform_driver mtk_vpu_driver = {
.probe = mtk_vpu_probe,
- .remove_new = mtk_vpu_remove,
+ .remove = mtk_vpu_remove,
.driver = {
.name = "mtk_vpu",
.pm = &mtk_vpu_pm,
diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c
index fee73260bb1e..70303a0b6919 100644
--- a/drivers/media/platform/microchip/microchip-csi2dc.c
+++ b/drivers/media/platform/microchip/microchip-csi2dc.c
@@ -782,7 +782,7 @@ MODULE_DEVICE_TABLE(of, csi2dc_of_match);
static struct platform_driver csi2dc_driver = {
.probe = csi2dc_probe,
- .remove_new = csi2dc_remove,
+ .remove = csi2dc_remove,
.driver = {
.name = "microchip-csi2dc",
.pm = &csi2dc_dev_pm_ops,
diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c
index f3a5cbacadbe..a7cdc743fda7 100644
--- a/drivers/media/platform/microchip/microchip-isc-base.c
+++ b/drivers/media/platform/microchip/microchip-isc-base.c
@@ -465,8 +465,6 @@ static void isc_buffer_queue(struct vb2_buffer *vb)
static const struct vb2_ops isc_vb2_ops = {
.queue_setup = isc_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_prepare = isc_buffer_prepare,
.start_streaming = isc_start_streaming,
.stop_streaming = isc_stop_streaming,
@@ -902,8 +900,11 @@ static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
return 0;
}
-static int isc_validate(struct isc_device *isc)
+static int isc_link_validate(struct media_link *link)
{
+ struct video_device *vdev =
+ media_entity_to_video_device(link->sink->entity);
+ struct isc_device *isc = video_get_drvdata(vdev);
int ret;
int i;
struct isc_format *sd_fmt = NULL;
@@ -1906,20 +1907,6 @@ int microchip_isc_pipeline_init(struct isc_device *isc)
}
EXPORT_SYMBOL_GPL(microchip_isc_pipeline_init);
-static int isc_link_validate(struct media_link *link)
-{
- struct video_device *vdev =
- media_entity_to_video_device(link->sink->entity);
- struct isc_device *isc = video_get_drvdata(vdev);
- int ret;
-
- ret = v4l2_subdev_link_validate(link);
- if (ret)
- return ret;
-
- return isc_validate(isc);
-}
-
static const struct media_entity_operations isc_entity_operations = {
.link_validate = isc_link_validate,
};
diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
index 5ac149cf3647..66d3d7891991 100644
--- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
@@ -353,33 +353,29 @@ static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = {
static int isc_parse_dt(struct device *dev, struct isc_device *isc)
{
struct device_node *np = dev->of_node;
- struct device_node *epn = NULL;
+ struct device_node *epn;
struct isc_subdev_entity *subdev_entity;
unsigned int flags;
- int ret;
INIT_LIST_HEAD(&isc->subdev_entities);
- while (1) {
+ for_each_endpoint_of_node(np, epn) {
struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
-
- epn = of_graph_get_next_endpoint(np, epn);
- if (!epn)
- return 0;
+ int ret;
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
&v4l2_epn);
if (ret) {
- ret = -EINVAL;
+ of_node_put(epn);
dev_err(dev, "Could not parse the endpoint\n");
- break;
+ return -EINVAL;
}
subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
GFP_KERNEL);
if (!subdev_entity) {
- ret = -ENOMEM;
- break;
+ of_node_put(epn);
+ return -ENOMEM;
}
subdev_entity->epn = epn;
@@ -400,9 +396,8 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
list_add_tail(&subdev_entity->list, &isc->subdev_entities);
}
- of_node_put(epn);
- return ret;
+ return 0;
}
static int microchip_isc_probe(struct platform_device *pdev)
@@ -663,7 +658,7 @@ MODULE_DEVICE_TABLE(of, microchip_isc_of_match);
static struct platform_driver microchip_isc_driver = {
.probe = microchip_isc_probe,
- .remove_new = microchip_isc_remove,
+ .remove = microchip_isc_remove,
.driver = {
.name = "microchip-sama5d2-isc",
.pm = &microchip_isc_dev_pm_ops,
diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
index 73445f33d26b..b0302dfc3278 100644
--- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
@@ -336,36 +336,32 @@ static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
{
struct device_node *np = dev->of_node;
- struct device_node *epn = NULL;
+ struct device_node *epn;
struct isc_subdev_entity *subdev_entity;
unsigned int flags;
- int ret;
bool mipi_mode;
INIT_LIST_HEAD(&isc->subdev_entities);
mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
- while (1) {
+ for_each_endpoint_of_node(np, epn) {
struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
-
- epn = of_graph_get_next_endpoint(np, epn);
- if (!epn)
- return 0;
+ int ret;
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
&v4l2_epn);
if (ret) {
- ret = -EINVAL;
+ of_node_put(epn);
dev_err(dev, "Could not parse the endpoint\n");
- break;
+ return -EINVAL;
}
subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
GFP_KERNEL);
if (!subdev_entity) {
- ret = -ENOMEM;
- break;
+ of_node_put(epn);
+ return -ENOMEM;
}
subdev_entity->epn = epn;
@@ -389,9 +385,8 @@ static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
list_add_tail(&subdev_entity->list, &isc->subdev_entities);
}
- of_node_put(epn);
- return ret;
+ return 0;
}
static int microchip_xisc_probe(struct platform_device *pdev)
@@ -626,7 +621,7 @@ MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
static struct platform_driver microchip_xisc_driver = {
.probe = microchip_xisc_probe,
- .remove_new = microchip_xisc_remove,
+ .remove = microchip_xisc_remove,
.driver = {
.name = "microchip-sama7g5-xisc",
.pm = &microchip_xisc_dev_pm_ops,
diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c
index 60fbb9140035..44e904e61801 100644
--- a/drivers/media/platform/nuvoton/npcm-video.c
+++ b/drivers/media/platform/nuvoton/npcm-video.c
@@ -578,7 +578,7 @@ static unsigned int npcm_video_hres(struct npcm_video *video)
regmap_read(gfxi, HVCNTL, &hvcntl);
apb_hor_res = (((hvcnth & HVCNTH_MASK) << 8) + (hvcntl & HVCNTL_MASK) + 1);
- return apb_hor_res;
+ return (apb_hor_res > MAX_WIDTH) ? MAX_WIDTH : apb_hor_res;
}
static unsigned int npcm_video_vres(struct npcm_video *video)
@@ -591,7 +591,7 @@ static unsigned int npcm_video_vres(struct npcm_video *video)
apb_ver_res = (((vvcnth & VVCNTH_MASK) << 8) + (vvcntl & VVCNTL_MASK));
- return apb_ver_res;
+ return (apb_ver_res > MAX_HEIGHT) ? MAX_HEIGHT : apb_ver_res;
}
static int npcm_video_capres(struct npcm_video *video, unsigned int hor_res,
@@ -863,7 +863,6 @@ static void npcm_video_detect_resolution(struct npcm_video *video)
struct regmap *gfxi = video->gfx_regmap;
unsigned int dispst;
- video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
det->width = npcm_video_hres(video);
det->height = npcm_video_vres(video);
@@ -892,12 +891,16 @@ static void npcm_video_detect_resolution(struct npcm_video *video)
clear_bit(VIDEO_RES_CHANGING, &video->flags);
}
- if (det->width && det->height)
+ if (det->width && det->height) {
video->v4l2_input_status = 0;
-
- dev_dbg(video->dev, "Got resolution[%dx%d] -> [%dx%d], status %d\n",
- act->width, act->height, det->width, det->height,
- video->v4l2_input_status);
+ dev_dbg(video->dev, "Got resolution[%dx%d] -> [%dx%d], status %d\n",
+ act->width, act->height, det->width, det->height,
+ video->v4l2_input_status);
+ } else {
+ video->v4l2_input_status = V4L2_IN_ST_NO_SIGNAL;
+ dev_err(video->dev, "Got invalid resolution[%dx%d]\n", det->width,
+ det->height);
+ }
}
static int npcm_video_set_resolution(struct npcm_video *video,
@@ -1558,8 +1561,6 @@ static const struct regmap_config npcm_video_ece_regmap_cfg = {
static const struct vb2_ops npcm_video_vb2_ops = {
.queue_setup = npcm_video_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_prepare = npcm_video_buf_prepare,
.buf_finish = npcm_video_buf_finish,
.start_streaming = npcm_video_start_streaming,
@@ -1650,8 +1651,8 @@ rel_ctrl_handler:
static int npcm_video_ece_init(struct npcm_video *video)
{
+ struct device_node *ece_node __free(device_node) = NULL;
struct device *dev = video->dev;
- struct device_node *ece_node;
struct platform_device *ece_pdev;
void __iomem *regs;
@@ -1667,11 +1668,11 @@ static int npcm_video_ece_init(struct npcm_video *video)
dev_info(dev, "Support HEXTILE pixel format\n");
ece_pdev = of_find_device_by_node(ece_node);
- if (IS_ERR(ece_pdev)) {
+ if (!ece_pdev) {
dev_err(dev, "Failed to find ECE device\n");
- return PTR_ERR(ece_pdev);
+ return -ENODEV;
}
- of_node_put(ece_node);
+ struct device *ece_dev __free(put_device) = &ece_pdev->dev;
regs = devm_platform_ioremap_resource(ece_pdev, 0);
if (IS_ERR(regs)) {
@@ -1686,7 +1687,7 @@ static int npcm_video_ece_init(struct npcm_video *video)
return PTR_ERR(video->ece.regmap);
}
- video->ece.reset = devm_reset_control_get(&ece_pdev->dev, NULL);
+ video->ece.reset = devm_reset_control_get(ece_dev, NULL);
if (IS_ERR(video->ece.reset)) {
dev_err(dev, "Failed to get ECE reset control in DTS\n");
return PTR_ERR(video->ece.reset);
@@ -1814,7 +1815,7 @@ static struct platform_driver npcm_video_driver = {
.of_match_table = npcm_video_match,
},
.probe = npcm_video_probe,
- .remove_new = npcm_video_remove,
+ .remove = npcm_video_remove,
};
module_platform_driver(npcm_video_driver);
diff --git a/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c
index 1c5b94989aec..b34244ea14dd 100644
--- a/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c
+++ b/drivers/media/platform/nvidia/tegra-vde/dmabuf-cache.c
@@ -16,7 +16,7 @@
#include "vde.h"
-MODULE_IMPORT_NS(DMA_BUF);
+MODULE_IMPORT_NS("DMA_BUF");
struct tegra_vde_cache_entry {
enum dma_data_direction dma_dir;
diff --git a/drivers/media/platform/nvidia/tegra-vde/h264.c b/drivers/media/platform/nvidia/tegra-vde/h264.c
index 204e474d57f7..0e56a4331b0d 100644
--- a/drivers/media/platform/nvidia/tegra-vde/h264.c
+++ b/drivers/media/platform/nvidia/tegra-vde/h264.c
@@ -19,11 +19,6 @@
#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;
@@ -628,12 +623,14 @@ 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;
+ long time_left;
int ret;
- timeout = wait_for_completion_interruptible_timeout(
+ time_left = wait_for_completion_interruptible_timeout(
&vde->decode_completion, msecs_to_jiffies(1000));
- if (timeout == 0) {
+ if (time_left < 0) {
+ ret = time_left;
+ } else if (time_left == 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;
@@ -642,8 +639,6 @@ static int tegra_vde_decode_end(struct tegra_vde *vde)
read_bytes, macroblocks_nb);
ret = -EIO;
- } else if (timeout < 0) {
- ret = timeout;
} else {
ret = 0;
}
diff --git a/drivers/media/platform/nvidia/tegra-vde/iommu.c b/drivers/media/platform/nvidia/tegra-vde/iommu.c
index 5521ed3e465f..b1d9d841d944 100644
--- a/drivers/media/platform/nvidia/tegra-vde/iommu.c
+++ b/drivers/media/platform/nvidia/tegra-vde/iommu.c
@@ -78,9 +78,10 @@ int tegra_vde_iommu_init(struct tegra_vde *vde)
arm_iommu_release_mapping(mapping);
}
#endif
- vde->domain = iommu_domain_alloc(&platform_bus_type);
- if (!vde->domain) {
- err = -ENOMEM;
+ vde->domain = iommu_paging_domain_alloc(dev);
+ if (IS_ERR(vde->domain)) {
+ err = PTR_ERR(vde->domain);
+ vde->domain = NULL;
goto put_group;
}
diff --git a/drivers/media/platform/nvidia/tegra-vde/trace.h b/drivers/media/platform/nvidia/tegra-vde/trace.h
index 7853ab095ca4..e8a75a7bd05d 100644
--- a/drivers/media/platform/nvidia/tegra-vde/trace.h
+++ b/drivers/media/platform/nvidia/tegra-vde/trace.h
@@ -20,7 +20,7 @@ DECLARE_EVENT_CLASS(register_access,
__field(u32, value)
),
TP_fast_assign(
- __assign_str(hw_name, tegra_vde_reg_base_name(vde, base));
+ __assign_str(hw_name);
__entry->offset = offset;
__entry->value = value;
),
diff --git a/drivers/media/platform/nvidia/tegra-vde/v4l2.c b/drivers/media/platform/nvidia/tegra-vde/v4l2.c
index 0f48ce6f243e..e3726cab0c82 100644
--- a/drivers/media/platform/nvidia/tegra-vde/v4l2.c
+++ b/drivers/media/platform/nvidia/tegra-vde/v4l2.c
@@ -328,8 +328,6 @@ static const struct vb2_ops tegra_qops = {
.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,
@@ -929,13 +927,13 @@ int tegra_vde_v4l2_init(struct tegra_vde *vde)
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.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->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;
diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.c b/drivers/media/platform/nvidia/tegra-vde/vde.c
index 81a0d3b76b88..3232392c60e2 100644
--- a/drivers/media/platform/nvidia/tegra-vde/vde.c
+++ b/drivers/media/platform/nvidia/tegra-vde/vde.c
@@ -535,7 +535,7 @@ MODULE_DEVICE_TABLE(of, tegra_vde_of_match);
static struct platform_driver tegra_vde_driver = {
.probe = tegra_vde_probe,
- .remove_new = tegra_vde_remove,
+ .remove = tegra_vde_remove,
.shutdown = tegra_vde_shutdown,
.driver = {
.name = "tegra-vde",
diff --git a/drivers/media/platform/nvidia/tegra-vde/vde.h b/drivers/media/platform/nvidia/tegra-vde/vde.h
index 0fbb1f3d2c88..b2890484b7c3 100644
--- a/drivers/media/platform/nvidia/tegra-vde/vde.h
+++ b/drivers/media/platform/nvidia/tegra-vde/vde.h
@@ -47,7 +47,6 @@ 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 {
diff --git a/drivers/media/platform/nxp/dw100/dw100.c b/drivers/media/platform/nxp/dw100/dw100.c
index 0024d6175ad9..3d1db1121bf9 100644
--- a/drivers/media/platform/nxp/dw100/dw100.c
+++ b/drivers/media/platform/nxp/dw100/dw100.c
@@ -558,8 +558,6 @@ static const struct vb2_ops dw100_qops = {
.buf_queue = dw100_buf_queue,
.start_streaming = dw100_start_streaming,
.stop_streaming = dw100_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -963,9 +961,9 @@ static int dw100_s_selection(struct file *file, void *fh,
src_q_data = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
dev_dbg(&ctx->dw_dev->pdev->dev,
- ">>> Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n",
+ ">>> Buffer Type: %u Target: %u Rect: (%d,%d)/%ux%u\n",
sel->type, sel->target,
- sel->r.width, sel->r.height, sel->r.left, sel->r.top);
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
@@ -1027,9 +1025,9 @@ static int dw100_s_selection(struct file *file, void *fh,
}
dev_dbg(&ctx->dw_dev->pdev->dev,
- "<<< Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n",
+ "<<< Buffer Type: %u Target: %u Rect: (%d,%d)/%ux%u\n",
sel->type, sel->target,
- sel->r.width, sel->r.height, sel->r.left, sel->r.top);
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
return 0;
}
@@ -1311,7 +1309,7 @@ static void dw100_hw_set_destination(struct dw100_device *dw_dev,
}
dev_dbg(&dw_dev->pdev->dev,
- "Set HW source registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n",
+ "Set HW destination registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n",
width, height, stride, &fourcc, &addr_y);
/* Pixel Format */
@@ -1688,7 +1686,7 @@ MODULE_DEVICE_TABLE(of, dw100_dt_ids);
static struct platform_driver dw100_driver = {
.probe = dw100_probe,
- .remove_new = dw100_remove,
+ .remove = dw100_remove,
.driver = {
.name = DRV_NAME,
.pm = &dw100_pm,
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
index d579c804b047..adb93e977be9 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg-hw.h
@@ -89,6 +89,7 @@
/* SLOT_STATUS fields for slots 0..3 */
#define SLOT_STATUS_FRMDONE (0x1 << 3)
#define SLOT_STATUS_ENC_CONFIG_ERR (0x1 << 8)
+#define SLOT_STATUS_ONGOING (0x1 << 31)
/* SLOT_IRQ_EN fields TBD */
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index cc97790ed30f..5c17bc58181e 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -535,7 +535,9 @@ static const unsigned char jpeg_sos_maximal[] = {
};
static const unsigned char jpeg_image_red[] = {
- 0xFC, 0x5F, 0xA2, 0xBF, 0xCA, 0x73, 0xFE, 0xFE,
+ 0xF9, 0xFE, 0x8A, 0xFC, 0x34, 0xFD, 0xC4, 0x28,
+ 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A,
+ 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0,
0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00,
0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28,
@@ -543,9 +545,18 @@ static const unsigned char jpeg_image_red[] = {
0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0,
0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00,
0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
+ 0x8A, 0x00, 0x28, 0xA0, 0x0F, 0xFF, 0xD0, 0xF9,
+ 0xFE, 0x8A, 0xFC, 0x34, 0xFD, 0xC4, 0x28, 0xA0,
+ 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00,
+ 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28,
0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A,
- 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00
+ 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0,
+ 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00,
+ 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02,
+ 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A, 0x00, 0x28,
+ 0xA0, 0x02, 0x8A, 0x00, 0x28, 0xA0, 0x02, 0x8A,
+ 0x00, 0x28, 0xA0, 0x0F
};
static const unsigned char jpeg_eoi[] = {
@@ -752,6 +763,39 @@ static int mxc_get_free_slot(struct mxc_jpeg_slot_data *slot_data)
return -1;
}
+static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg)
+{
+ /* free descriptor for decoding/encoding phase */
+ dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data.desc,
+ jpeg->slot_data.desc_handle);
+ jpeg->slot_data.desc = NULL;
+ jpeg->slot_data.desc_handle = 0;
+
+ /* free descriptor for encoder configuration phase / decoder DHT */
+ dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
+ jpeg->slot_data.cfg_desc,
+ jpeg->slot_data.cfg_desc_handle);
+ jpeg->slot_data.cfg_desc_handle = 0;
+ jpeg->slot_data.cfg_desc = NULL;
+
+ /* free configuration stream */
+ dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM,
+ jpeg->slot_data.cfg_stream_vaddr,
+ jpeg->slot_data.cfg_stream_handle);
+ jpeg->slot_data.cfg_stream_vaddr = NULL;
+ jpeg->slot_data.cfg_stream_handle = 0;
+
+ dma_free_coherent(jpeg->dev, jpeg->slot_data.cfg_dec_size,
+ jpeg->slot_data.cfg_dec_vaddr,
+ jpeg->slot_data.cfg_dec_daddr);
+ jpeg->slot_data.cfg_dec_size = 0;
+ jpeg->slot_data.cfg_dec_vaddr = NULL;
+ jpeg->slot_data.cfg_dec_daddr = 0;
+
+ jpeg->slot_data.used = false;
+}
+
static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg)
{
struct mxc_jpeg_desc *desc;
@@ -788,36 +832,25 @@ static bool mxc_jpeg_alloc_slot_data(struct mxc_jpeg_dev *jpeg)
goto err;
jpeg->slot_data.cfg_stream_vaddr = cfg_stm;
+ jpeg->slot_data.cfg_dec_size = MXC_JPEG_PATTERN_WIDTH * MXC_JPEG_PATTERN_HEIGHT * 2;
+ jpeg->slot_data.cfg_dec_vaddr = dma_alloc_coherent(jpeg->dev,
+ jpeg->slot_data.cfg_dec_size,
+ &jpeg->slot_data.cfg_dec_daddr,
+ GFP_ATOMIC);
+ if (!jpeg->slot_data.cfg_dec_vaddr)
+ goto err;
+
skip_alloc:
jpeg->slot_data.used = true;
return true;
err:
dev_err(jpeg->dev, "Could not allocate descriptors for slot %d", jpeg->slot_data.slot);
+ mxc_jpeg_free_slot_data(jpeg);
return false;
}
-static void mxc_jpeg_free_slot_data(struct mxc_jpeg_dev *jpeg)
-{
- /* free descriptor for decoding/encoding phase */
- dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
- jpeg->slot_data.desc,
- jpeg->slot_data.desc_handle);
-
- /* free descriptor for encoder configuration phase / decoder DHT */
- dma_free_coherent(jpeg->dev, sizeof(struct mxc_jpeg_desc),
- jpeg->slot_data.cfg_desc,
- jpeg->slot_data.cfg_desc_handle);
-
- /* free configuration stream */
- dma_free_coherent(jpeg->dev, MXC_JPEG_MAX_CFG_STREAM,
- jpeg->slot_data.cfg_stream_vaddr,
- jpeg->slot_data.cfg_stream_handle);
-
- jpeg->slot_data.used = false;
-}
-
static void mxc_jpeg_check_and_set_last_buffer(struct mxc_jpeg_ctx *ctx,
struct vb2_v4l2_buffer *src_buf,
struct vb2_v4l2_buffer *dst_buf)
@@ -877,6 +910,34 @@ static u32 mxc_jpeg_get_plane_size(struct mxc_jpeg_q_data *q_data, u32 plane_no)
return size;
}
+static bool mxc_dec_is_ongoing(struct mxc_jpeg_ctx *ctx)
+{
+ struct mxc_jpeg_dev *jpeg = ctx->mxc_jpeg;
+ u32 curr_desc;
+ u32 slot_status;
+
+ curr_desc = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_CUR_DESCPT_PTR));
+ if (curr_desc == jpeg->slot_data.cfg_desc_handle)
+ return true;
+
+ slot_status = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_STATUS));
+ if (slot_status & SLOT_STATUS_ONGOING)
+ return true;
+
+ /*
+ * The curr_desc register is updated when next_descpt_ptr is loaded,
+ * the ongoing bit of slot_status is set when the 32 bytes descriptor is loaded.
+ * So there will be a short time interval in between, which may cause fake false.
+ * Consider read register is quite slow compared with IP read 32byte from memory,
+ * read twice slot_status can avoid this situation.
+ */
+ slot_status = readl(jpeg->base_reg + MXC_SLOT_OFFSET(ctx->slot, SLOT_STATUS));
+ if (slot_status & SLOT_STATUS_ONGOING)
+ return true;
+
+ return false;
+}
+
static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
{
struct mxc_jpeg_dev *jpeg = priv;
@@ -946,7 +1007,8 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
mxc_jpeg_enc_mode_go(dev, reg, mxc_jpeg_is_extended_sequential(q_data->fmt));
goto job_unlock;
}
- if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed) {
+ if (jpeg->mode == MXC_JPEG_DECODE && jpeg_src_buf->dht_needed &&
+ mxc_dec_is_ongoing(ctx)) {
jpeg_src_buf->dht_needed = false;
dev_dbg(dev, "Decoder DHT cfg finished. Start decoding...\n");
goto job_unlock;
@@ -1209,14 +1271,14 @@ static void mxc_jpeg_config_dec_desc(struct vb2_buffer *out_buf,
*/
*cfg_size = mxc_jpeg_setup_cfg_stream(cfg_stream_vaddr,
V4L2_PIX_FMT_YUYV,
- MXC_JPEG_MIN_WIDTH,
- MXC_JPEG_MIN_HEIGHT);
+ MXC_JPEG_PATTERN_WIDTH,
+ MXC_JPEG_PATTERN_HEIGHT);
cfg_desc->next_descpt_ptr = desc_handle | MXC_NXT_DESCPT_EN;
- cfg_desc->buf_base0 = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ cfg_desc->buf_base0 = jpeg->slot_data.cfg_dec_daddr;
cfg_desc->buf_base1 = 0;
- cfg_desc->imgsize = MXC_JPEG_MIN_WIDTH << 16;
- cfg_desc->imgsize |= MXC_JPEG_MIN_HEIGHT;
- cfg_desc->line_pitch = MXC_JPEG_MIN_WIDTH * 2;
+ cfg_desc->imgsize = MXC_JPEG_PATTERN_WIDTH << 16;
+ cfg_desc->imgsize |= MXC_JPEG_PATTERN_HEIGHT;
+ cfg_desc->line_pitch = MXC_JPEG_PATTERN_WIDTH * 2;
cfg_desc->stm_ctrl = STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV422);
cfg_desc->stm_ctrl |= STM_CTRL_BITBUF_PTR_CLR(1);
cfg_desc->stm_bufbase = cfg_stream_handle;
@@ -1634,6 +1696,9 @@ static int mxc_jpeg_start_streaming(struct vb2_queue *q, unsigned int count)
dev_dbg(ctx->mxc_jpeg->dev, "Start streaming ctx=%p", ctx);
q_data->sequence = 0;
+ if (V4L2_TYPE_IS_CAPTURE(q->type))
+ ctx->need_initial_source_change_evt = false;
+
ret = pm_runtime_resume_and_get(ctx->mxc_jpeg->dev);
if (ret < 0) {
dev_err(ctx->mxc_jpeg->dev, "Failed to power up jpeg\n");
@@ -1840,17 +1905,6 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb)
q_data_out = mxc_jpeg_get_q_data(ctx,
V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- if (q_data_out->w == 0 && q_data_out->h == 0) {
- dev_warn(dev, "Invalid user resolution 0x0");
- dev_warn(dev, "Keeping resolution from JPEG: %dx%d",
- header.frame.width, header.frame.height);
- } else if (header.frame.width != q_data_out->w ||
- header.frame.height != q_data_out->h) {
- dev_err(dev,
- "Resolution mismatch: %dx%d (JPEG) versus %dx%d(user)",
- header.frame.width, header.frame.height,
- q_data_out->w, q_data_out->h);
- }
q_data_out->w = header.frame.width;
q_data_out->h = header.frame.height;
if (header.frame.width > MXC_JPEG_MAX_WIDTH ||
@@ -1926,9 +1980,19 @@ static void mxc_jpeg_buf_queue(struct vb2_buffer *vb)
jpeg_src_buf = vb2_to_mxc_buf(vb);
jpeg_src_buf->jpeg_parse_error = false;
ret = mxc_jpeg_parse(ctx, vb);
- if (ret)
+ if (ret) {
jpeg_src_buf->jpeg_parse_error = true;
+ /*
+ * if the capture queue is not setup, the device_run() won't be scheduled,
+ * need to drop the error buffer, so that the decoding can continue
+ */
+ if (!vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) {
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ return;
+ }
+ }
+
end:
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
}
@@ -1973,8 +2037,6 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb)
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,
.start_streaming = mxc_jpeg_start_streaming,
@@ -2687,9 +2749,12 @@ static void mxc_jpeg_detach_pm_domains(struct mxc_jpeg_dev *jpeg)
int i;
for (i = 0; i < jpeg->num_domains; i++) {
- if (jpeg->pd_link[i] && !IS_ERR(jpeg->pd_link[i]))
+ if (!IS_ERR_OR_NULL(jpeg->pd_dev[i]) &&
+ !pm_runtime_suspended(jpeg->pd_dev[i]))
+ pm_runtime_force_suspend(jpeg->pd_dev[i]);
+ if (!IS_ERR_OR_NULL(jpeg->pd_link[i]))
device_link_del(jpeg->pd_link[i]);
- if (jpeg->pd_dev[i] && !IS_ERR(jpeg->pd_dev[i]))
+ if (!IS_ERR_OR_NULL(jpeg->pd_dev[i]))
dev_pm_domain_detach(jpeg->pd_dev[i], true);
jpeg->pd_dev[i] = NULL;
jpeg->pd_link[i] = NULL;
@@ -2850,6 +2915,7 @@ static int mxc_jpeg_probe(struct platform_device *pdev)
jpeg->dec_vdev->vfl_dir = VFL_DIR_M2M;
jpeg->dec_vdev->device_caps = V4L2_CAP_STREAMING |
V4L2_CAP_VIDEO_M2M_MPLANE;
+ video_set_drvdata(jpeg->dec_vdev, jpeg);
if (mode == MXC_JPEG_ENCODE) {
v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_DECODER_CMD);
v4l2_disable_ioctl(jpeg->dec_vdev, VIDIOC_TRY_DECODER_CMD);
@@ -2862,7 +2928,6 @@ static int mxc_jpeg_probe(struct platform_device *pdev)
dev_err(dev, "failed to register video device\n");
goto err_vdev_register;
}
- video_set_drvdata(jpeg->dec_vdev, jpeg);
if (mode == MXC_JPEG_ENCODE)
v4l2_info(&jpeg->v4l2_dev,
"encoder device registered as /dev/video%d (%d,%d)\n",
@@ -2896,7 +2961,6 @@ err_clk:
return ret;
}
-#ifdef CONFIG_PM
static int mxc_jpeg_runtime_resume(struct device *dev)
{
struct mxc_jpeg_dev *jpeg = dev_get_drvdata(dev);
@@ -2919,9 +2983,7 @@ static int mxc_jpeg_runtime_suspend(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int mxc_jpeg_suspend(struct device *dev)
{
struct mxc_jpeg_dev *jpeg = dev_get_drvdata(dev);
@@ -2942,12 +3004,10 @@ static int mxc_jpeg_resume(struct device *dev)
v4l2_m2m_resume(jpeg->m2m_dev);
return ret;
}
-#endif
static const struct dev_pm_ops mxc_jpeg_pm_ops = {
- SET_RUNTIME_PM_OPS(mxc_jpeg_runtime_suspend,
- mxc_jpeg_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(mxc_jpeg_suspend, mxc_jpeg_resume)
+ RUNTIME_PM_OPS(mxc_jpeg_runtime_suspend, mxc_jpeg_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(mxc_jpeg_suspend, mxc_jpeg_resume)
};
static void mxc_jpeg_remove(struct platform_device *pdev)
@@ -2967,11 +3027,11 @@ MODULE_DEVICE_TABLE(of, mxc_jpeg_match);
static struct platform_driver mxc_jpeg_driver = {
.probe = mxc_jpeg_probe,
- .remove_new = mxc_jpeg_remove,
+ .remove = mxc_jpeg_remove,
.driver = {
.name = "mxc-jpeg",
.of_match_table = mxc_jpeg_match,
- .pm = &mxc_jpeg_pm_ops,
+ .pm = pm_ptr(&mxc_jpeg_pm_ops),
},
};
module_platform_driver(mxc_jpeg_driver);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index 86e324b21aed..fdde45f7e163 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -28,6 +28,8 @@
#define MXC_JPEG_W_ALIGN 3
#define MXC_JPEG_MAX_SIZEIMAGE 0xFFFFFC00
#define MXC_JPEG_MAX_PLANES 2
+#define MXC_JPEG_PATTERN_WIDTH 128
+#define MXC_JPEG_PATTERN_HEIGHT 64
enum mxc_jpeg_enc_state {
MXC_JPEG_ENCODING = 0, /* jpeg encode phase */
@@ -117,6 +119,9 @@ struct mxc_jpeg_slot_data {
dma_addr_t desc_handle;
dma_addr_t cfg_desc_handle; // configuration descriptor dma address
dma_addr_t cfg_stream_handle; // configuration bitstream dma address
+ dma_addr_t cfg_dec_size;
+ void *cfg_dec_vaddr;
+ dma_addr_t cfg_dec_daddr;
};
struct mxc_jpeg_dev {
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index db8ff5f5c4d3..d060eadebc7a 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -30,6 +30,7 @@
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mc.h>
#include <media/v4l2-subdev.h>
@@ -319,7 +320,11 @@ struct mipi_csis_device {
struct v4l2_subdev sd;
struct media_pad pads[CSIS_PADS_NUM];
struct v4l2_async_notifier notifier;
- struct v4l2_subdev *src_sd;
+
+ struct {
+ struct v4l2_subdev *sd;
+ const struct media_pad *pad;
+ } source;
struct v4l2_mbus_config_mipi_csi2 bus;
u32 clk_frequency;
@@ -592,12 +597,13 @@ static void __mipi_csis_set_format(struct mipi_csis_device *csis,
static int mipi_csis_calculate_params(struct mipi_csis_device *csis,
const struct csis_pix_format *csis_fmt)
{
+ struct media_pad *src_pad =
+ &csis->source.sd->entity.pads[csis->source.pad->index];
s64 link_freq;
u32 lane_rate;
/* Calculate the line rate from the pixel rate. */
- link_freq = v4l2_get_link_freq(csis->src_sd->ctrl_handler,
- csis_fmt->width,
+ link_freq = v4l2_get_link_freq(src_pad, csis_fmt->width,
csis->bus.num_data_lanes * 2);
if (link_freq < 0) {
dev_err(csis->dev, "Unable to obtain link frequency: %d\n",
@@ -742,6 +748,18 @@ static void mipi_csis_stop_stream(struct mipi_csis_device *csis)
mipi_csis_system_enable(csis, false);
}
+static void mipi_csis_queue_event_sof(struct mipi_csis_device *csis)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+ u32 frame;
+
+ frame = mipi_csis_read(csis, MIPI_CSIS_FRAME_COUNTER_CH(0));
+ event.u.frame_sync.frame_sequence = frame;
+ v4l2_event_queue(csis->sd.devnode, &event);
+}
+
static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
{
struct mipi_csis_device *csis = dev_id;
@@ -765,6 +783,10 @@ static irqreturn_t mipi_csis_irq_handler(int irq, void *dev_id)
event->counter++;
}
}
+
+ if (status & MIPI_CSIS_INT_SRC_FRAME_START)
+ mipi_csis_queue_event_sof(csis);
+
spin_unlock_irqrestore(&csis->slock, flags);
mipi_csis_write(csis, MIPI_CSIS_INT_SRC, status);
@@ -840,18 +862,21 @@ static void mipi_csis_log_counters(struct mipi_csis_device *csis, bool non_error
{
unsigned int num_events = non_errors ? MIPI_CSIS_NUM_EVENTS
: MIPI_CSIS_NUM_EVENTS - 8;
+ unsigned int counters[MIPI_CSIS_NUM_EVENTS];
unsigned long flags;
unsigned int i;
spin_lock_irqsave(&csis->slock, flags);
+ for (i = 0; i < num_events; ++i)
+ counters[i] = csis->events[i].counter;
+ spin_unlock_irqrestore(&csis->slock, flags);
for (i = 0; i < num_events; ++i) {
- if (csis->events[i].counter > 0 || csis->debug.enable)
+ if (counters[i] > 0 || csis->debug.enable)
dev_info(csis->dev, "%s events: %d\n",
csis->events[i].name,
- csis->events[i].counter);
+ counters[i]);
}
- spin_unlock_irqrestore(&csis->slock, flags);
}
static int mipi_csis_dump_regs(struct mipi_csis_device *csis)
@@ -941,7 +966,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable)
int ret;
if (!enable) {
- v4l2_subdev_call(csis->src_sd, video, s_stream, 0);
+ v4l2_subdev_disable_streams(csis->source.sd,
+ csis->source.pad->index, BIT(0));
mipi_csis_stop_stream(csis);
if (csis->debug.enable)
@@ -969,7 +995,8 @@ static int mipi_csis_s_stream(struct v4l2_subdev *sd, int enable)
mipi_csis_start_stream(csis, format, csis_fmt);
- ret = v4l2_subdev_call(csis->src_sd, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(csis->source.sd,
+ csis->source.pad->index, BIT(0));
if (ret < 0)
goto err_stop;
@@ -1154,8 +1181,23 @@ static int mipi_csis_log_status(struct v4l2_subdev *sd)
return 0;
}
+static int mipi_csis_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type != V4L2_EVENT_FRAME_SYNC)
+ return -EINVAL;
+
+ /* V4L2_EVENT_FRAME_SYNC doesn't require an id, so zero should be set */
+ if (sub->id != 0)
+ return -EINVAL;
+
+ return v4l2_event_subscribe(fh, sub, 0, NULL);
+}
+
static const struct v4l2_subdev_core_ops mipi_csis_core_ops = {
.log_status = mipi_csis_log_status,
+ .subscribe_event = mipi_csis_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_video_ops mipi_csis_video_ops = {
@@ -1201,12 +1243,14 @@ static int mipi_csis_link_setup(struct media_entity *entity,
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
if (flags & MEDIA_LNK_FL_ENABLED) {
- if (csis->src_sd)
+ if (csis->source.sd)
return -EBUSY;
- csis->src_sd = remote_sd;
+ csis->source.sd = remote_sd;
+ csis->source.pad = remote_pad;
} else {
- csis->src_sd = NULL;
+ csis->source.sd = NULL;
+ csis->source.pad = NULL;
}
return 0;
@@ -1304,7 +1348,7 @@ err_parse:
* Suspend/resume
*/
-static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev)
+static int mipi_csis_runtime_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
@@ -1319,7 +1363,7 @@ static int __maybe_unused mipi_csis_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused mipi_csis_runtime_resume(struct device *dev)
+static int mipi_csis_runtime_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct mipi_csis_device *csis = sd_to_mipi_csis_device(sd);
@@ -1339,8 +1383,8 @@ static int __maybe_unused mipi_csis_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops mipi_csis_pm_ops = {
- SET_RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(mipi_csis_runtime_suspend, mipi_csis_runtime_resume,
+ NULL)
};
/* -----------------------------------------------------------------------------
@@ -1358,7 +1402,7 @@ static int mipi_csis_subdev_init(struct mipi_csis_device *csis)
snprintf(sd->name, sizeof(sd->name), "csis-%s",
dev_name(csis->dev));
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->ctrl_handler = NULL;
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
@@ -1527,11 +1571,11 @@ MODULE_DEVICE_TABLE(of, mipi_csis_of_match);
static struct platform_driver mipi_csis_driver = {
.probe = mipi_csis_probe,
- .remove_new = mipi_csis_remove,
+ .remove = mipi_csis_remove,
.driver = {
.of_match_table = mipi_csis_of_match,
.name = CSIS_DRIVER_NAME,
- .pm = &mipi_csis_pm_ops,
+ .pm = pm_ptr(&mipi_csis_pm_ops),
},
};
diff --git a/drivers/media/platform/nxp/imx-pxp.c b/drivers/media/platform/nxp/imx-pxp.c
index e62dc5c1a4ae..7f8ffbac582f 100644
--- a/drivers/media/platform/nxp/imx-pxp.c
+++ b/drivers/media/platform/nxp/imx-pxp.c
@@ -1606,8 +1606,6 @@ static const struct vb2_ops pxp_qops = {
.buf_queue = pxp_buf_queue,
.start_streaming = pxp_start_streaming,
.stop_streaming = pxp_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq,
@@ -1805,6 +1803,9 @@ static int pxp_probe(struct platform_device *pdev)
return PTR_ERR(mmio);
dev->regmap = devm_regmap_init_mmio(&pdev->dev, mmio,
&pxp_regmap_config);
+ if (IS_ERR(dev->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->regmap),
+ "Failed to init regmap\n");
irq = platform_get_irq(pdev, 0);
if (irq < 0)
@@ -1940,7 +1941,7 @@ MODULE_DEVICE_TABLE(of, pxp_dt_ids);
static struct platform_driver pxp_driver = {
.probe = pxp_probe,
- .remove_new = pxp_remove,
+ .remove = pxp_remove,
.driver = {
.name = MEM2MEM_NAME,
.of_match_table = pxp_dt_ids,
diff --git a/drivers/media/platform/nxp/imx-pxp.h b/drivers/media/platform/nxp/imx-pxp.h
index 44f95c749d2e..476f2042fa6f 100644
--- a/drivers/media/platform/nxp/imx-pxp.h
+++ b/drivers/media/platform/nxp/imx-pxp.h
@@ -594,12 +594,17 @@
(((v) << 18) & BM_PXP_CSC1_COEF0_C0)
#define BP_PXP_CSC1_COEF0_UV_OFFSET 9
#define BM_PXP_CSC1_COEF0_UV_OFFSET 0x0003FE00
+
+/*
+ * We use v * (1 << 9) instead of v << 9, to workaround a gcc5 bug.
+ * The compiler cannot understand that the expression is constant.
+ */
#define BF_PXP_CSC1_COEF0_UV_OFFSET(v) \
- (((v) << 9) & BM_PXP_CSC1_COEF0_UV_OFFSET)
+ (((v) * (1 << 9)) & BM_PXP_CSC1_COEF0_UV_OFFSET)
#define BP_PXP_CSC1_COEF0_Y_OFFSET 0
#define BM_PXP_CSC1_COEF0_Y_OFFSET 0x000001FF
#define BF_PXP_CSC1_COEF0_Y_OFFSET(v) \
- (((v) << 0) & BM_PXP_CSC1_COEF0_Y_OFFSET)
+ ((v) & BM_PXP_CSC1_COEF0_Y_OFFSET)
#define HW_PXP_CSC1_COEF1 (0x000001b0)
diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c
index 9566ff738818..34a92642bbfe 100644
--- a/drivers/media/platform/nxp/imx7-media-csi.c
+++ b/drivers/media/platform/nxp/imx7-media-csi.c
@@ -1507,8 +1507,6 @@ static const struct vb2_ops imx7_csi_video_qops = {
.buf_init = imx7_csi_video_buf_init,
.buf_prepare = imx7_csi_video_buf_prepare,
.buf_queue = imx7_csi_video_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = imx7_csi_video_start_streaming,
.stop_streaming = imx7_csi_video_stop_streaming,
};
@@ -2281,7 +2279,7 @@ MODULE_DEVICE_TABLE(of, imx7_csi_of_match);
static struct platform_driver imx7_csi_driver = {
.probe = imx7_csi_probe,
- .remove_new = imx7_csi_remove,
+ .remove = imx7_csi_remove,
.driver = {
.of_match_table = imx7_csi_of_match,
.name = "imx7-csi",
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
index c2013995049c..1e79b1211b60 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -307,6 +307,19 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = {
.has_36bit_dma = true,
};
+static const struct mxc_isi_plat_data mxc_imx8ulp_data = {
+ .model = MXC_ISI_IMX8ULP,
+ .num_ports = 1,
+ .num_channels = 1,
+ .reg_offset = 0x0,
+ .ier_reg = &mxc_imx8_isi_ier_v2,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .clks = mxc_imx8mn_clks,
+ .num_clks = ARRAY_SIZE(mxc_imx8mn_clks),
+ .buf_active_reverse = true,
+ .has_36bit_dma = false,
+};
+
static const struct mxc_isi_plat_data mxc_imx93_data = {
.model = MXC_ISI_IMX93,
.num_ports = 1,
@@ -528,6 +541,7 @@ static void mxc_isi_remove(struct platform_device *pdev)
static const struct of_device_id mxc_isi_of_match[] = {
{ .compatible = "fsl,imx8mn-isi", .data = &mxc_imx8mn_data },
{ .compatible = "fsl,imx8mp-isi", .data = &mxc_imx8mp_data },
+ { .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data },
{ .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data },
{ /* sentinel */ },
};
@@ -535,7 +549,7 @@ MODULE_DEVICE_TABLE(of, mxc_isi_of_match);
static struct platform_driver mxc_isi_driver = {
.probe = mxc_isi_probe,
- .remove_new = mxc_isi_remove,
+ .remove = mxc_isi_remove,
.driver = {
.of_match_table = mxc_isi_of_match,
.name = MXC_ISI_DRIVER_NAME,
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
index 2810ebe9b5f7..9c7fe9e5f941 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -158,6 +158,7 @@ struct mxc_gasket_ops {
enum model {
MXC_ISI_IMX8MN,
MXC_ISI_IMX8MP,
+ MXC_ISI_IMX8ULP,
MXC_ISI_IMX93,
};
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
index 9745d6219a16..22e49d3a1287 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
@@ -43,6 +43,7 @@ struct mxc_isi_m2m_ctx_queue_data {
struct v4l2_pix_format_mplane format;
const struct mxc_isi_format_info *info;
u32 sequence;
+ bool streaming;
};
struct mxc_isi_m2m_ctx {
@@ -269,8 +270,6 @@ static const struct vb2_ops mxc_isi_m2m_vb2_qops = {
.buf_init = mxc_isi_m2m_vb2_buffer_init,
.buf_prepare = mxc_isi_m2m_vb2_buffer_prepare,
.buf_queue = mxc_isi_m2m_vb2_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = mxc_isi_m2m_vb2_start_streaming,
.stop_streaming = mxc_isi_m2m_vb2_stop_streaming,
};
@@ -486,15 +485,18 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh,
enum v4l2_buf_type type)
{
struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
+ struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type);
const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format;
const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format;
const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info;
const struct mxc_isi_format_info *out_info = ctx->queues.out.info;
struct mxc_isi_m2m *m2m = ctx->m2m;
bool bypass;
-
int ret;
+ if (q->streaming)
+ return 0;
+
mutex_lock(&m2m->lock);
if (m2m->usage_count == INT_MAX) {
@@ -547,6 +549,8 @@ static int mxc_isi_m2m_streamon(struct file *file, void *fh,
goto unchain;
}
+ q->streaming = true;
+
return 0;
unchain:
@@ -569,10 +573,14 @@ static int mxc_isi_m2m_streamoff(struct file *file, void *fh,
enum v4l2_buf_type type)
{
struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
+ struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type);
struct mxc_isi_m2m *m2m = ctx->m2m;
v4l2_m2m_ioctl_streamoff(file, fh, type);
+ if (!q->streaming)
+ return 0;
+
mutex_lock(&m2m->lock);
/*
@@ -598,6 +606,8 @@ static int mxc_isi_m2m_streamoff(struct file *file, void *fh,
mutex_unlock(&m2m->lock);
+ q->streaming = false;
+
return 0;
}
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
index 4091f1c0e78b..8654150728a8 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
@@ -861,6 +861,7 @@ int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2
const struct mxc_isi_format_info *info,
const struct v4l2_pix_format_mplane *pix)
{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb2);
unsigned int i;
for (i = 0; i < info->mem_planes; i++) {
@@ -875,6 +876,8 @@ int mxc_isi_video_buffer_prepare(struct mxc_isi_dev *isi, struct vb2_buffer *vb2
vb2_set_plane_payload(vb2, i, size);
}
+ v4l2_buf->field = pix->field;
+
return 0;
}
@@ -987,8 +990,6 @@ static const struct vb2_ops mxc_isi_vb2_qops = {
.buf_init = mxc_isi_vb2_buffer_init,
.buf_prepare = mxc_isi_vb2_buffer_prepare,
.buf_queue = mxc_isi_vb2_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = mxc_isi_vb2_start_streaming,
.stop_streaming = mxc_isi_vb2_stop_streaming,
};
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index ba2e81f24965..a8bcf60e2f37 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -287,6 +287,7 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
struct v4l2_subdev_state *sd_state,
u32 *hs_settle)
{
+ struct media_pad *src_pad;
s64 link_freq;
u32 lane_rate;
unsigned long esc_clk_rate;
@@ -294,13 +295,19 @@ static int imx8mq_mipi_csi_calc_hs_settle(struct csi_state *state,
const struct v4l2_mbus_framefmt *fmt;
const struct csi2_pix_format *csi2_fmt;
+ src_pad = media_entity_remote_source_pad_unique(&sd_state->sd->entity);
+ if (IS_ERR(src_pad)) {
+ dev_err(state->dev, "can't get source pad of %s (%ld)\n",
+ sd_state->sd->name, PTR_ERR(src_pad));
+ return PTR_ERR(src_pad);
+ }
+
/* Calculate the line rate from the pixel rate. */
fmt = v4l2_subdev_state_get_format(sd_state, MIPI_CSI2_PAD_SINK);
csi2_fmt = find_csi2_format(fmt->code);
- link_freq = v4l2_get_link_freq(state->src_sd->ctrl_handler,
- csi2_fmt->width,
+ link_freq = v4l2_get_link_freq(src_pad, csi2_fmt->width,
state->bus.num_data_lanes * 2);
if (link_freq < 0) {
dev_err(state->dev, "Unable to obtain link frequency: %d\n",
@@ -693,7 +700,7 @@ unlock:
return ret ? -EAGAIN : 0;
}
-static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev)
+static int imx8mq_mipi_csi_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct csi_state *state = mipi_sd_to_csi2_state(sd);
@@ -705,7 +712,7 @@ static int __maybe_unused imx8mq_mipi_csi_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev)
+static int imx8mq_mipi_csi_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct csi_state *state = mipi_sd_to_csi2_state(sd);
@@ -716,7 +723,7 @@ static int __maybe_unused imx8mq_mipi_csi_resume(struct device *dev)
return imx8mq_mipi_csi_pm_resume(dev);
}
-static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev)
+static int imx8mq_mipi_csi_runtime_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct csi_state *state = mipi_sd_to_csi2_state(sd);
@@ -731,7 +738,7 @@ static int __maybe_unused imx8mq_mipi_csi_runtime_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev)
+static int imx8mq_mipi_csi_runtime_resume(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct csi_state *state = mipi_sd_to_csi2_state(sd);
@@ -747,10 +754,9 @@ static int __maybe_unused imx8mq_mipi_csi_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops imx8mq_mipi_csi_pm_ops = {
- SET_RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend,
- imx8mq_mipi_csi_runtime_resume,
- NULL)
- SET_SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume)
+ RUNTIME_PM_OPS(imx8mq_mipi_csi_runtime_suspend,
+ imx8mq_mipi_csi_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(imx8mq_mipi_csi_suspend, imx8mq_mipi_csi_resume)
};
/* -----------------------------------------------------------------------------
@@ -954,11 +960,11 @@ MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
static struct platform_driver imx8mq_mipi_csi_driver = {
.probe = imx8mq_mipi_csi_probe,
- .remove_new = imx8mq_mipi_csi_remove,
+ .remove = imx8mq_mipi_csi_remove,
.driver = {
.of_match_table = imx8mq_mipi_csi_of_match,
.name = MIPI_CSI2_DRIVER_NAME,
- .pm = &imx8mq_mipi_csi_pm_ops,
+ .pm = pm_ptr(&imx8mq_mipi_csi_pm_ops),
},
};
diff --git a/drivers/media/platform/nxp/mx2_emmaprp.c b/drivers/media/platform/nxp/mx2_emmaprp.c
index 023ed40c6b08..0c6cc120fd2a 100644
--- a/drivers/media/platform/nxp/mx2_emmaprp.c
+++ b/drivers/media/platform/nxp/mx2_emmaprp.c
@@ -677,8 +677,6 @@ static const struct vb2_ops emmaprp_qops = {
.queue_setup = emmaprp_queue_setup,
.buf_prepare = emmaprp_buf_prepare,
.buf_queue = emmaprp_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq,
@@ -902,7 +900,7 @@ static void emmaprp_remove(struct platform_device *pdev)
static struct platform_driver emmaprp_pdrv = {
.probe = emmaprp_probe,
- .remove_new = emmaprp_remove,
+ .remove = emmaprp_remove,
.driver = {
.name = MEM2MEM_NAME,
},
diff --git a/drivers/media/platform/qcom/Kconfig b/drivers/media/platform/qcom/Kconfig
index cc5799b9ea00..4f4d3a68e6e5 100644
--- a/drivers/media/platform/qcom/Kconfig
+++ b/drivers/media/platform/qcom/Kconfig
@@ -3,4 +3,5 @@
comment "Qualcomm media platform drivers"
source "drivers/media/platform/qcom/camss/Kconfig"
+source "drivers/media/platform/qcom/iris/Kconfig"
source "drivers/media/platform/qcom/venus/Kconfig"
diff --git a/drivers/media/platform/qcom/Makefile b/drivers/media/platform/qcom/Makefile
index 4f055c396e04..ea2221a202c0 100644
--- a/drivers/media/platform/qcom/Makefile
+++ b/drivers/media/platform/qcom/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += camss/
+obj-y += iris/
obj-y += venus/
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 4e2222358973..d26a9c24a430 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -6,7 +6,9 @@ qcom-camss-objs += \
camss-csid.o \
camss-csid-4-1.o \
camss-csid-4-7.o \
+ camss-csid-680.o \
camss-csid-gen2.o \
+ camss-csid-780.o \
camss-csiphy-2ph-1-0.o \
camss-csiphy-3ph-1-0.o \
camss-csiphy.o \
@@ -14,10 +16,13 @@ qcom-camss-objs += \
camss-vfe-4-1.o \
camss-vfe-4-7.o \
camss-vfe-4-8.o \
- camss-vfe-170.o \
+ camss-vfe-17x.o \
camss-vfe-480.o \
+ camss-vfe-680.o \
+ camss-vfe-780.o \
camss-vfe-gen1.o \
camss-vfe.o \
camss-video.o \
+ camss-format.o \
obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o
diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-1.c b/drivers/media/platform/qcom/camss/camss-csid-4-1.c
index dd49a40e6a70..6998e1c52895 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-4-1.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-4-1.c
@@ -17,7 +17,6 @@
#include "camss-csid-gen1.h"
#include "camss.h"
-#define CAMSS_CSID_HW_VERSION 0x0
#define CAMSS_CSID_CORE_CTRL_0 0x004
#define CAMSS_CSID_CORE_CTRL_1 0x008
#define CAMSS_CSID_RST_CMD 0x00c
@@ -45,128 +44,6 @@
#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n))
#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n))
-static const struct csid_format csid_formats[] = {
- {
- MEDIA_BUS_FMT_UYVY8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_VYUY8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YUYV8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YVYU8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_Y10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
-};
-
static void csid_configure_stream(struct csid_device *csid, u8 enable)
{
struct csid_testgen_config *tg = &csid->testgen;
@@ -174,7 +51,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
if (enable) {
struct v4l2_mbus_framefmt *input_format;
- const struct csid_format *format;
+ const struct csid_format_info *format;
u8 vc = 0; /* Virtual Channel 0 */
u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */
u8 dt_shift;
@@ -184,7 +61,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
u32 num_lines, num_bytes_per_line;
input_format = &csid->fmt[MSM_CSID_PAD_SRC];
- format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
input_format->code);
num_bytes_per_line = input_format->width * format->bpp * format->spp / 8;
num_lines = input_format->height;
@@ -211,7 +89,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
struct csid_phy_config *phy = &csid->phy;
input_format = &csid->fmt[MSM_CSID_PAD_SINK];
- format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
input_format->code);
val = phy->lane_cnt - 1;
@@ -259,15 +138,6 @@ static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
return 0;
}
-static u32 csid_hw_version(struct csid_device *csid)
-{
- u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
-
- dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static irqreturn_t csid_isr(int irq, void *dev)
{
struct csid_device *csid = dev;
@@ -300,19 +170,8 @@ static int csid_reset(struct csid_device *csid)
return 0;
}
-static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
- unsigned int match_format_idx, u32 match_code)
-{
- if (match_format_idx > 0)
- return 0;
-
- return sink_code;
-}
-
static void csid_subdev_init(struct csid_device *csid)
{
- csid->formats = csid_formats;
- csid->nformats = ARRAY_SIZE(csid_formats);
csid->testgen.modes = csid_testgen_modes;
csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1;
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-7.c b/drivers/media/platform/qcom/camss/camss-csid-4-7.c
index 6b26e036294e..66054d4872e6 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-4-7.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-4-7.c
@@ -16,7 +16,6 @@
#include "camss-csid-gen1.h"
#include "camss.h"
-#define CAMSS_CSID_HW_VERSION 0x0
#define CAMSS_CSID_CORE_CTRL_0 0x004
#define CAMSS_CSID_CORE_CTRL_1 0x008
#define CAMSS_CSID_RST_CMD 0x010
@@ -44,156 +43,6 @@
#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b8 + 0xc * (n))
#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0bc + 0xc * (n))
-static const struct csid_format csid_formats[] = {
- {
- MEDIA_BUS_FMT_UYVY8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_VYUY8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YUYV8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YVYU8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_Y10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
-};
-
static void csid_configure_stream(struct csid_device *csid, u8 enable)
{
struct csid_testgen_config *tg = &csid->testgen;
@@ -203,7 +52,7 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
if (enable) {
struct v4l2_mbus_framefmt *input_format;
- const struct csid_format *format;
+ const struct csid_format_info *format;
u8 vc = 0; /* Virtual Channel 0 */
u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */
u8 dt_shift;
@@ -213,7 +62,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
u32 num_bytes_per_line, num_lines;
input_format = &csid->fmt[MSM_CSID_PAD_SRC];
- format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
input_format->code);
num_bytes_per_line = input_format->width * format->bpp * format->spp / 8;
num_lines = input_format->height;
@@ -240,7 +90,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
struct csid_phy_config *phy = &csid->phy;
input_format = &csid->fmt[MSM_CSID_PAD_SINK];
- format = csid_get_fmt_entry(csid->formats, csid->nformats,
+ format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
input_format->code);
val = phy->lane_cnt - 1;
@@ -299,15 +150,6 @@ static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
return 0;
}
-static u32 csid_hw_version(struct csid_device *csid)
-{
- u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION);
-
- dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
/*
* isr - CSID module interrupt service routine
* @irq: Interrupt line
@@ -353,42 +195,8 @@ static int csid_reset(struct csid_device *csid)
return 0;
}
-static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
- unsigned int match_format_idx, u32 match_code)
-{
- switch (sink_code) {
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- case MEDIA_BUS_FMT_Y10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_Y10_1X10,
- MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- default:
- if (match_format_idx > 0)
- return 0;
-
- return sink_code;
- }
-}
-
static void csid_subdev_init(struct csid_device *csid)
{
- csid->formats = csid_formats;
- csid->nformats = ARRAY_SIZE(csid_formats);
csid->testgen.modes = csid_testgen_modes;
csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1;
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c
new file mode 100644
index 000000000000..3ad3a174bcfb
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-680.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
+ *
+ * Copyright (C) 2020-2025 Linaro Ltd.
+ */
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include "camss.h"
+#include "camss-csid.h"
+#include "camss-csid-gen2.h"
+
+#define CSID_TOP_IO_PATH_CFG0(csid) (0x4 * (csid))
+#define CSID_TOP_IO_PATH_CFG0_INTERNAL_CSID BIT(0)
+#define CSID_TOP_IO_PATH_CFG0_SFE_0 BIT(1)
+#define CSID_TOP_IO_PATH_CFG0_SFE_1 GENMASK(1, 0)
+#define CSID_TOP_IO_PATH_CFG0_SBI_0 BIT(4)
+#define CSID_TOP_IO_PATH_CFG0_SBI_1 GENMASK(3, 0)
+#define CSID_TOP_IO_PATH_CFG0_SBI_2 GENMASK(3, 1)
+#define CSID_TOP_IO_PATH_CFG0_OUTPUT_IFE_EN BIT(8)
+#define CSID_TOP_IO_PATH_CFG0_SFE_OFFLINE_EN BIT(12)
+
+#define CSID_RESET_CMD 0x10
+#define CSID_RESET_CMD_HW_RESET BIT(0)
+#define CSID_RESET_CMD_SW_RESET BIT(1)
+#define CSID_RESET_CMD_IRQ_CTRL BIT(2)
+
+#define CSID_IRQ_CMD 0x14
+#define CSID_IRQ_CMD_CLEAR BIT(0)
+#define CSID_IRQ_CMD_SET BIT(4)
+
+#define CSID_REG_UPDATE_CMD 0x18
+
+#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) (0xec + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_CCIF_VIOLATION BIT(29)
+#define CSID_CSI2_RDIN_SENSOR_SWITCH_OUT_OF_SYNC_FRAME_DROP BIT(28)
+#define CSID_CSI2_RDIN_ERROR_REC_WIDTH_VIOLATION BIT(27)
+#define CSID_CSI2_RDIN_ERROR_REC_HEIGHT_VIOLATION BIT(26)
+#define CSID_CSI2_RDIN_BATCH_END_MISSING_VIOLATION BIT(25)
+#define CSID_CSI2_RDIN_ILLEGAL_BATCH_ID_IRQ BIT(24)
+#define CSID_CSI2_RDIN_RUP_DONE BIT(23)
+#define CSID_CSI2_RDIN_CAMIF_EPOCH_1_IRQ BIT(22)
+#define CSID_CSI2_RDIN_CAMIF_EPOCH_0_IRQ BIT(21)
+#define CSID_CSI2_RDIN_ERROR_REC_OVERFLOW_IRQ BIT(19)
+#define CSID_CSI2_RDIN_ERROR_REC_FRAME_DROP BIT(18)
+#define CSID_CSI2_RDIN_VCDT_GRP_CHANG BIT(17)
+#define CSID_CSI2_RDIN_VCDT_GRP_0_SEL BIT(16)
+#define CSID_CSI2_RDIN_VCDT_GRP_1_SEL BIT(15)
+#define CSID_CSI2_RDIN_ERROR_LINE_COUNT BIT(14)
+#define CSID_CSI2_RDIN_ERROR_PIX_COUNT BIT(13)
+#define CSID_CSI2_RDIN_INFO_INPUT_SOF BIT(12)
+#define CSID_CSI2_RDIN_INFO_INPUT_SOL BIT(11)
+#define CSID_CSI2_RDIN_INFO_INPUT_EOL BIT(10)
+#define CSID_CSI2_RDIN_INFO_INPUT_EOF BIT(9)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_SOF BIT(8)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_SOL BIT(7)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_EOL BIT(6)
+#define CSID_CSI2_RDIN_INFO_FRAME_DROP_EOF BIT(5)
+#define CSID_CSI2_RDIN_INFO_CAMIF_SOF BIT(4)
+#define CSID_CSI2_RDIN_INFO_CAMIF_EOF BIT(3)
+#define CSID_CSI2_RDIN_INFO_FIFO_OVERFLOW BIT(2)
+#define CSID_CSI2_RDIN_RES1 BIT(1)
+#define CSID_CSI2_RDIN_RES0 BIT(0)
+
+#define CSID_CSI2_RDIN_IRQ_MASK(rdi) (0xf0 + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) (0xf4 + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_SET(rdi) (0xf8 + 0x10 * (rdi))
+
+#define CSID_TOP_IRQ_STATUS 0x7c
+#define CSID_TOP_IRQ_MASK 0x80
+#define CSID_TOP_IRQ_CLEAR 0x84
+#define CSID_TOP_IRQ_RESET BIT(0)
+#define CSID_TOP_IRQ_RX BIT(2)
+#define CSID_TOP_IRQ_LONG_PKT(rdi) (BIT(8) << (rdi))
+#define CSID_TOP_IRQ_BUF_DONE BIT(13)
+
+#define CSID_BUF_DONE_IRQ_STATUS 0x8c
+#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? 1 : 14)
+#define CSID_BUF_DONE_IRQ_MASK 0x90
+#define CSID_BUF_DONE_IRQ_CLEAR 0x94
+
+#define CSID_CSI2_RX_IRQ_STATUS 0x9c
+#define CSID_CSI2_RX_IRQ_MASK 0xa0
+#define CSID_CSI2_RX_IRQ_CLEAR 0xa4
+
+#define CSID_RESET_CFG 0xc
+#define CSID_RESET_CFG_MODE_IMMEDIATE BIT(0)
+#define CSID_RESET_CFG_LOCATION_COMPLETE BIT(4)
+
+#define CSID_CSI2_RDI_IRQ_STATUS(rdi) (0xec + 0x10 * (rdi))
+#define CSID_CSI2_RDI_IRQ_MASK(rdi) (0xf0 + 0x10 * (rdi))
+#define CSID_CSI2_RDI_IRQ_CLEAR(rdi) (0xf4 + 0x10 * (rdi))
+
+#define CSID_CSI2_RX_CFG0 0x200
+#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0
+#define CSI2_RX_CFG0_DL0_INPUT_SEL 4
+#define CSI2_RX_CFG0_DL1_INPUT_SEL 8
+#define CSI2_RX_CFG0_DL2_INPUT_SEL 12
+#define CSI2_RX_CFG0_DL3_INPUT_SEL 16
+#define CSI2_RX_CFG0_PHY_NUM_SEL 20
+#define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1
+#define CSI2_RX_CFG0_PHY_TYPE_SEL 24
+
+#define CSID_CSI2_RX_CFG1 0x204
+#define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN BIT(0)
+#define CSI2_RX_CFG1_DE_SCRAMBLE_EN BIT(1)
+#define CSI2_RX_CFG1_VC_MODE BIT(2)
+#define CSI2_RX_CFG1_COMPLETE_STREAM_EN BIT(4)
+#define CSI2_RX_CFG1_COMPLETE_STREAM_FRAME_TIMING BIT(5)
+#define CSI2_RX_CFG1_MISR_EN BIT(6)
+#define CSI2_RX_CFG1_CGC_MODE BIT(7)
+
+#define CSID_CSI2_RX_CAPTURE_CTRL 0x208
+#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_EN BIT(0)
+#define CSI2_RX_CAPTURE_CTRL_SHORT_PKT_EN BIT(1)
+#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_EN BIT(2)
+#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_DT GENMASK(9, 4)
+#define CSI2_RX_CAPTURE_CTRL_LONG_PKT_VC GENMASK(14, 10)
+#define CSI2_RX_CAPTURE_CTRL_SHORT_PKT_VC GENMASK(19, 15)
+#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_DT GENMASK(20, 25)
+#define CSI2_RX_CAPTURE_CTRL_CPHY_PKT_VC GENMASK(30, 26)
+
+#define CSID_CSI2_RX_TOTAL_PKTS_RCVD 0x240
+#define CSID_CSI2_RX_STATS_ECC 0x244
+#define CSID_CSI2_RX_CRC_ERRORS 0x248
+
+#define CSID_RDI_CFG0(rdi) (0x500 + 0x100 * (rdi))
+#define RDI_CFG0_DECODE_FORMAT 12
+#define RDI_CFG0_DATA_TYPE 16
+#define RDI_CFG0_VIRTUAL_CHANNEL 22
+#define RDI_CFG0_DT_ID 27
+#define RDI_CFG0_ENABLE BIT(31)
+
+#define CSID_RDI_CTRL(rdi) (0x504 + 0x100 * (rdi))
+#define CSID_RDI_CTRL_HALT_CMD_HALT_AT_FRAME_BOUNDARY 0
+#define CSID_RDI_CTRL_HALT_CMD_RESUME_AT_FRAME_BOUNDARY 1
+
+#define CSID_RDI_CFG1(rdi) (0x510 + 0x100 * (rdi))
+#define RDI_CFG1_TIMESTAMP_STB_FRAME BIT(0)
+#define RDI_CFG1_TIMESTAMP_STB_IRQ BIT(1)
+#define RDI_CFG1_BYTE_CNTR_EN BIT(2)
+#define RDI_CFG1_TIMESTAMP_EN BIT(4)
+#define RDI_CFG1_DROP_H_EN BIT(5)
+#define RDI_CFG1_DROP_V_EN BIT(6)
+#define RDI_CFG1_CROP_H_EN BIT(7)
+#define RDI_CFG1_CROP_V_EN BIT(8)
+#define RDI_CFG1_MISR_EN BIT(9)
+#define RDI_CFG1_PLAIN_ALIGN_MSB BIT(11)
+#define RDI_CFG1_EARLY_EOF_EN BIT(14)
+#define RDI_CFG1_PACKING_MIPI BIT(15)
+
+#define CSID_RDI_ERR_RECOVERY_CFG0(rdi) (0x514 + 0x100 * (rdi))
+#define CSID_RDI_EPOCH_IRQ_CFG(rdi) (0x52c + 0x100 * (rdi))
+#define CSID_RDI_FRM_DROP_PATTERN(rdi) (0x540 + 0x100 * (rdi))
+#define CSID_RDI_FRM_DROP_PERIOD(rdi) (0x544 + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) (0x548 + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) (0x54c + 0x100 * (rdi))
+#define CSID_RDI_PIX_DROP_PATTERN(rdi) (0x558 + 0x100 * (rdi))
+#define CSID_RDI_PIX_DROP_PERIOD(rdi) (0x55c + 0x100 * (rdi))
+#define CSID_RDI_LINE_DROP_PATTERN(rdi) (0x560 + 0x100 * (rdi))
+#define CSID_RDI_LINE_DROP_PERIOD(rdi) (0x564 + 0x100 * (rdi))
+
+static inline int reg_update_rdi(struct csid_device *csid, int n)
+{
+ return BIT(4 + n) + BIT(20 + n);
+}
+
+static void csid_reg_update(struct csid_device *csid, int port_id)
+{
+ csid->reg_update |= reg_update_rdi(csid, port_id);
+ writel(csid->reg_update, csid->base + CSID_REG_UPDATE_CMD);
+}
+
+static inline void csid_reg_update_clear(struct csid_device *csid,
+ int port_id)
+{
+ csid->reg_update &= ~reg_update_rdi(csid, port_id);
+ writel(csid->reg_update, csid->base + CSID_REG_UPDATE_CMD);
+}
+
+static void __csid_configure_rx(struct csid_device *csid,
+ struct csid_phy_config *phy, int vc)
+{
+ u32 val;
+
+ val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
+ val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
+ val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL;
+
+ writel(val, csid->base + CSID_CSI2_RX_CFG0);
+
+ val = CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN;
+ if (vc > 3)
+ val |= CSI2_RX_CFG1_VC_MODE;
+ writel(val, csid->base + CSID_CSI2_RX_CFG1);
+}
+
+static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
+{
+ u32 val;
+
+ if (enable)
+ val = CSID_RDI_CTRL_HALT_CMD_RESUME_AT_FRAME_BOUNDARY;
+ else
+ val = CSID_RDI_CTRL_HALT_CMD_HALT_AT_FRAME_BOUNDARY;
+
+ writel(val, csid->base + CSID_RDI_CTRL(rdi));
+}
+
+static void __csid_configure_top(struct csid_device *csid)
+{
+ u32 val;
+
+ val = CSID_TOP_IO_PATH_CFG0_OUTPUT_IFE_EN | CSID_TOP_IO_PATH_CFG0_INTERNAL_CSID;
+ writel(val, csid->camss->csid_wrapper_base +
+ CSID_TOP_IO_PATH_CFG0(csid->id));
+}
+
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+{
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
+ input_format->code);
+ u8 lane_cnt = csid->phy.lane_cnt;
+ u8 dt_id;
+ u32 val;
+
+ if (!lane_cnt)
+ lane_cnt = 4;
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+
+ /*
+ * DT_ID is a two bit bitfield that is concatenated with
+ * the four least significant bits of the five bit VC
+ * bitfield to generate an internal CID value.
+ *
+ * CSID_RDI_CFG0(vc)
+ * DT_ID : 28:27
+ * VC : 26:22
+ * DT : 21:16
+ *
+ * CID : VC 3:0 << 2 | DT_ID 1:0
+ */
+ dt_id = vc & 0x03;
+
+ /* note: for non-RDI path, this should be format->decode_format */
+ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
+ val |= format->data_type << RDI_CFG0_DATA_TYPE;
+ val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
+ val |= dt_id << RDI_CFG0_DT_ID;
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+
+ val = RDI_CFG1_TIMESTAMP_STB_FRAME;
+ val |= RDI_CFG1_BYTE_CNTR_EN;
+ val |= RDI_CFG1_TIMESTAMP_EN;
+ val |= RDI_CFG1_DROP_H_EN;
+ val |= RDI_CFG1_DROP_V_EN;
+ val |= RDI_CFG1_CROP_H_EN;
+ val |= RDI_CFG1_CROP_V_EN;
+ val |= RDI_CFG1_PACKING_MIPI;
+
+ writel(val, csid->base + CSID_RDI_CFG1(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+
+ val = 1;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_CTRL(vc));
+
+ val = readl(csid->base + CSID_RDI_CFG0(vc));
+ if (enable)
+ val |= RDI_CFG0_ENABLE;
+ else
+ val &= ~RDI_CFG0_ENABLE;
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+}
+
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
+{
+ int i;
+
+ __csid_configure_top(csid);
+
+ /* Loop through all enabled VCs and configure stream for each */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+ if (csid->phy.en_vc & BIT(i)) {
+ __csid_configure_rdi_stream(csid, enable, i);
+ __csid_configure_rx(csid, &csid->phy, i);
+ __csid_ctrl_rdi(csid, enable, i);
+ }
+ }
+}
+
+/*
+ * csid_reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_reset(struct csid_device *csid)
+{
+ unsigned long time;
+ u32 val;
+ int i;
+
+ reinit_completion(&csid->reset_complete);
+
+ writel(CSID_IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+
+ /* preserve registers */
+ val = CSID_RESET_CFG_MODE_IMMEDIATE | CSID_RESET_CFG_LOCATION_COMPLETE;
+ writel(val, csid->base + CSID_RESET_CFG);
+
+ val = CSID_RESET_CMD_HW_RESET | CSID_RESET_CMD_SW_RESET;
+ writel(val, csid->base + CSID_RESET_CMD);
+
+ time = wait_for_completion_timeout(&csid->reset_complete,
+ msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+ if (!time) {
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
+ return -EIO;
+ }
+
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+ /* Enable RUP done for the client port */
+ writel(CSID_CSI2_RDIN_RUP_DONE, csid->base + CSID_CSI2_RDIN_IRQ_MASK(i));
+ }
+
+ /* Clear RDI status */
+ writel(~0u, csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+
+ /* Enable BUF_DONE bit for all write-master client ports */
+ writel(~0u, csid->base + CSID_BUF_DONE_IRQ_MASK);
+
+ /* Unmask all TOP interrupts */
+ writel(~0u, csid->base + CSID_TOP_IRQ_MASK);
+
+ return 0;
+}
+
+static void csid_rup_complete(struct csid_device *csid, int rdi)
+{
+ csid_reg_update_clear(csid, rdi);
+}
+
+/*
+ * csid_isr - CSID module interrupt service routine
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+ struct csid_device *csid = dev;
+ u32 buf_done_val, val, val_top;
+ int i;
+
+ /* Latch and clear TOP status */
+ val_top = readl(csid->base + CSID_TOP_IRQ_STATUS);
+ writel(val_top, csid->base + CSID_TOP_IRQ_CLEAR);
+
+ /* Latch and clear CSID_CSI2 status */
+ val = readl(csid->base + CSID_CSI2_RX_IRQ_STATUS);
+ writel(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR);
+
+ /* Latch and clear top level BUF_DONE status */
+ buf_done_val = readl(csid->base + CSID_BUF_DONE_IRQ_STATUS);
+ writel(buf_done_val, csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+
+ /* Process state for each RDI channel */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
+ val = readl(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
+ if (val)
+ writel(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+
+ if (val & CSID_CSI2_RDIN_RUP_DONE)
+ csid_rup_complete(csid, i);
+
+ if (buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i))
+ camss_buf_done(csid->camss, csid->id, i);
+ }
+
+ /* Issue clear command */
+ writel(CSID_IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+
+ /* Reset complete */
+ if (val_top & CSID_TOP_IRQ_RESET)
+ complete(&csid->reset_complete);
+
+ return IRQ_HANDLED;
+}
+
+static void csid_subdev_reg_update(struct csid_device *csid, int port_id, bool is_clear)
+{
+ if (is_clear)
+ csid_reg_update_clear(csid, port_id);
+ else
+ csid_reg_update(csid, port_id);
+}
+
+static void csid_subdev_init(struct csid_device *csid) {}
+
+const struct csid_hw_ops csid_ops_680 = {
+ .configure_testgen_pattern = NULL,
+ .configure_stream = csid_configure_stream,
+ .hw_version = csid_hw_version,
+ .isr = csid_isr,
+ .reset = csid_reset,
+ .src_pad_code = csid_src_pad_code,
+ .subdev_init = csid_subdev_init,
+ .reg_update = csid_subdev_reg_update,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csid-780.c b/drivers/media/platform/qcom/camss/camss-csid-780.c
new file mode 100644
index 000000000000..4c720d177731
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-780.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
+ *
+ * Copyright (c) 2024 Qualcomm Technologies, Inc.
+ */
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+
+#include "camss.h"
+#include "camss-csid.h"
+#include "camss-csid-780.h"
+
+#define CSID_IO_PATH_CFG0(csid) (0x4 * (csid))
+#define OUTPUT_IFE_EN 0x100
+#define INTERNAL_CSID 1
+
+#define CSID_RST_CFG 0xC
+#define RST_MODE BIT(0)
+#define RST_LOCATION BIT(4)
+
+#define CSID_RST_CMD 0x10
+#define SELECT_HW_RST BIT(0)
+#define SELECT_IRQ_RST BIT(2)
+
+#define CSID_IRQ_CMD 0x14
+#define IRQ_CMD_CLEAR BIT(0)
+
+#define CSID_RUP_AUP_CMD 0x18
+#define CSID_RUP_AUP_RDI(rdi) ((BIT(4) | BIT(20)) << (rdi))
+
+#define CSID_TOP_IRQ_STATUS 0x7C
+#define TOP_IRQ_STATUS_RESET_DONE BIT(0)
+
+#define CSID_TOP_IRQ_MASK 0x80
+#define CSID_TOP_IRQ_CLEAR 0x84
+#define CSID_TOP_IRQ_SET 0x88
+
+#define CSID_CSI2_RX_IRQ_STATUS 0x9C
+#define CSID_CSI2_RX_IRQ_MASK 0xA0
+#define CSID_CSI2_RX_IRQ_CLEAR 0xA4
+#define CSID_CSI2_RX_IRQ_SET 0xA8
+
+#define CSID_BUF_DONE_IRQ_STATUS 0x8C
+#define BUF_DONE_IRQ_STATUS_RDI_OFFSET (csid_is_lite(csid) ? 1 : 14)
+#define CSID_BUF_DONE_IRQ_MASK 0x90
+#define CSID_BUF_DONE_IRQ_CLEAR 0x94
+#define CSID_BUF_DONE_IRQ_SET 0x98
+
+#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) (0xEC + 0x10 * (rdi))
+#define RUP_DONE_IRQ_STATUS BIT(23)
+
+#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) (0xF4 + 0x10 * (rdi))
+#define CSID_CSI2_RDIN_IRQ_SET(rdi) (0xF8 + 0x10 * (rdi))
+
+#define CSID_CSI2_RX_CFG0 0x200
+#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0
+#define CSI2_RX_CFG0_DL0_INPUT_SEL 4
+#define CSI2_RX_CFG0_PHY_NUM_SEL 20
+
+#define CSID_CSI2_RX_CFG1 0x204
+#define CSI2_RX_CFG1_ECC_CORRECTION_EN BIT(0)
+#define CSI2_RX_CFG1_VC_MODE BIT(2)
+
+#define CSID_RDI_CFG0(rdi) (0x500 + 0x100 * (rdi))
+#define RDI_CFG0_TIMESTAMP_EN BIT(6)
+#define RDI_CFG0_TIMESTAMP_STB_SEL BIT(8)
+#define RDI_CFG0_DECODE_FORMAT 12
+#define RDI_CFG0_DT 16
+#define RDI_CFG0_VC 22
+#define RDI_CFG0_DT_ID 27
+#define RDI_CFG0_EN BIT(31)
+
+#define CSID_RDI_CTRL(rdi) (0x504 + 0x100 * (rdi))
+#define RDI_CTRL_START_CMD BIT(0)
+
+#define CSID_RDI_CFG1(rdi) (0x510 + 0x100 * (rdi))
+#define RDI_CFG1_DROP_H_EN BIT(5)
+#define RDI_CFG1_DROP_V_EN BIT(6)
+#define RDI_CFG1_CROP_H_EN BIT(7)
+#define RDI_CFG1_CROP_V_EN BIT(8)
+#define RDI_CFG1_PIX_STORE BIT(10)
+#define RDI_CFG1_PACKING_FORMAT_MIPI BIT(15)
+
+#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) (0x548 + 0x100 * (rdi))
+#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) (0x54C + 0x100 * (rdi))
+
+#define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1
+
+static void __csid_configure_rx(struct csid_device *csid,
+ struct csid_phy_config *phy, int vc)
+{
+ int val;
+
+ val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
+ val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
+ val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL;
+
+ writel(val, csid->base + CSID_CSI2_RX_CFG0);
+
+ val = CSI2_RX_CFG1_ECC_CORRECTION_EN;
+ if (vc > 3)
+ val |= CSI2_RX_CFG1_VC_MODE;
+
+ writel(val, csid->base + CSID_CSI2_RX_CFG1);
+}
+
+static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
+{
+ int val = 0;
+
+ if (enable)
+ val = RDI_CTRL_START_CMD;
+
+ writel(val, csid->base + CSID_RDI_CTRL(rdi));
+}
+
+static void __csid_configure_wrapper(struct csid_device *csid)
+{
+ u32 val;
+
+ /* csid lite doesn't need to configure top register */
+ if (csid->res->is_lite)
+ return;
+
+ val = OUTPUT_IFE_EN | INTERNAL_CSID;
+ writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id));
+}
+
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+{
+ u32 val;
+ u8 lane_cnt = csid->phy.lane_cnt;
+ /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
+ input_format->code);
+
+ if (!lane_cnt)
+ lane_cnt = 4;
+
+ /*
+ * DT_ID is a two bit bitfield that is concatenated with
+ * the four least significant bits of the five bit VC
+ * bitfield to generate an internal CID value.
+ *
+ * CSID_RDI_CFG0(vc)
+ * DT_ID : 28:27
+ * VC : 26:22
+ * DT : 21:16
+ *
+ * CID : VC 3:0 << 2 | DT_ID 1:0
+ */
+ u8 dt_id = vc & 0x03;
+
+ val = RDI_CFG0_TIMESTAMP_EN;
+ val |= RDI_CFG0_TIMESTAMP_STB_SEL;
+ /* note: for non-RDI path, this should be format->decode_format */
+ val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
+ val |= vc << RDI_CFG0_VC;
+ val |= format->data_type << RDI_CFG0_DT;
+ val |= dt_id << RDI_CFG0_DT_ID;
+
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+
+ val = RDI_CFG1_PACKING_FORMAT_MIPI;
+ val |= RDI_CFG1_PIX_STORE;
+ val |= RDI_CFG1_DROP_H_EN;
+ val |= RDI_CFG1_DROP_V_EN;
+ val |= RDI_CFG1_CROP_H_EN;
+ val |= RDI_CFG1_CROP_V_EN;
+
+ writel(val, csid->base + CSID_RDI_CFG1(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+
+ val = 1;
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+
+ val = 0;
+ writel(val, csid->base + CSID_RDI_CTRL(vc));
+
+ val = readl(csid->base + CSID_RDI_CFG0(vc));
+
+ if (enable)
+ val |= RDI_CFG0_EN;
+ writel(val, csid->base + CSID_RDI_CFG0(vc));
+}
+
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
+{
+ u8 i;
+
+ __csid_configure_wrapper(csid);
+
+ /* Loop through all enabled VCs and configure stream for each */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
+ if (csid->phy.en_vc & BIT(i)) {
+ __csid_configure_rdi_stream(csid, enable, i);
+ __csid_configure_rx(csid, &csid->phy, i);
+ __csid_ctrl_rdi(csid, enable, i);
+ }
+}
+
+static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
+{
+ return 0;
+}
+
+static void csid_subdev_reg_update(struct csid_device *csid, int port_id, bool clear)
+{
+ if (clear) {
+ csid->reg_update &= ~CSID_RUP_AUP_RDI(port_id);
+ } else {
+ csid->reg_update |= CSID_RUP_AUP_RDI(port_id);
+ writel(csid->reg_update, csid->base + CSID_RUP_AUP_CMD);
+ }
+}
+
+/*
+ * csid_isr - CSID module interrupt service routine
+ * @irq: Interrupt line
+ * @dev: CSID device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csid_isr(int irq, void *dev)
+{
+ struct csid_device *csid = dev;
+ u32 val, buf_done_val;
+ u8 reset_done;
+ int i;
+
+ val = readl(csid->base + CSID_TOP_IRQ_STATUS);
+ writel(val, csid->base + CSID_TOP_IRQ_CLEAR);
+ reset_done = val & TOP_IRQ_STATUS_RESET_DONE;
+
+ val = readl(csid->base + CSID_CSI2_RX_IRQ_STATUS);
+ writel(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR);
+
+ buf_done_val = readl(csid->base + CSID_BUF_DONE_IRQ_STATUS);
+ writel(buf_done_val, csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+
+ /* Read and clear IRQ status for each enabled RDI channel */
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
+ if (csid->phy.en_vc & BIT(i)) {
+ val = readl(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
+ writel(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
+
+ if (val & RUP_DONE_IRQ_STATUS)
+ /* clear the reg update bit */
+ csid_subdev_reg_update(csid, i, true);
+
+ if (buf_done_val & BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i)) {
+ /*
+ * For Titan 780, bus done and RUP IRQ have been moved to
+ * CSID from VFE. Once CSID received bus done, need notify
+ * VFE of this event. Trigger VFE to handle bus done process.
+ */
+ camss_buf_done(csid->camss, csid->id, i);
+ }
+ }
+
+ val = IRQ_CMD_CLEAR;
+ writel(val, csid->base + CSID_IRQ_CMD);
+
+ if (reset_done)
+ complete(&csid->reset_complete);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csid_reset - Trigger reset on CSID module and wait to complete
+ * @csid: CSID device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int csid_reset(struct csid_device *csid)
+{
+ unsigned long time;
+ u32 val;
+ int i;
+
+ reinit_completion(&csid->reset_complete);
+
+ writel(1, csid->base + CSID_TOP_IRQ_CLEAR);
+ writel(1, csid->base + CSID_IRQ_CMD);
+ writel(1, csid->base + CSID_TOP_IRQ_MASK);
+
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
+ if (csid->phy.en_vc & BIT(i)) {
+ writel(BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i),
+ csid->base + CSID_BUF_DONE_IRQ_CLEAR);
+ writel(IRQ_CMD_CLEAR, csid->base + CSID_IRQ_CMD);
+ writel(BIT(BUF_DONE_IRQ_STATUS_RDI_OFFSET + i),
+ csid->base + CSID_BUF_DONE_IRQ_MASK);
+ }
+
+ /* preserve registers */
+ val = RST_LOCATION | RST_MODE;
+ writel(val, csid->base + CSID_RST_CFG);
+
+ val = SELECT_HW_RST | SELECT_IRQ_RST;
+ writel(val, csid->base + CSID_RST_CMD);
+
+ time = wait_for_completion_timeout(&csid->reset_complete,
+ msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
+ if (!time) {
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void csid_subdev_init(struct csid_device *csid)
+{
+ csid->testgen.nmodes = CSID_PAYLOAD_MODE_DISABLED;
+}
+
+const struct csid_hw_ops csid_ops_780 = {
+ .configure_stream = csid_configure_stream,
+ .configure_testgen_pattern = csid_configure_testgen_pattern,
+ .hw_version = csid_hw_version,
+ .isr = csid_isr,
+ .reset = csid_reset,
+ .src_pad_code = csid_src_pad_code,
+ .subdev_init = csid_subdev_init,
+ .reg_update = csid_subdev_reg_update,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csid-780.h b/drivers/media/platform/qcom/camss/camss-csid-780.h
new file mode 100644
index 000000000000..a990c66a60ff
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csid-780.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-csid-780.h
+ *
+ * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module Generation 3
+ *
+ * Copyright (c) 2024 Qualcomm Technologies, Inc.
+ */
+#ifndef __QC_MSM_CAMSS_CSID_780_H__
+#define __QC_MSM_CAMSS_CSID_780_H__
+
+#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1
+#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2
+#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3
+#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x4
+#define DECODE_FORMAT_UNCOMPRESSED_16_BIT 0x5
+#define DECODE_FORMAT_UNCOMPRESSED_20_BIT 0x6
+#define DECODE_FORMAT_UNCOMPRESSED_24_BIT 0x7
+#define DECODE_FORMAT_PAYLOAD_ONLY 0xf
+
+#define PLAIN_FORMAT_PLAIN8 0x0 /* supports DPCM, UNCOMPRESSED_6/8_BIT */
+#define PLAIN_FORMAT_PLAIN16 0x1 /* supports DPCM, UNCOMPRESSED_10/16_BIT */
+#define PLAIN_FORMAT_PLAIN32 0x2 /* supports UNCOMPRESSED_20_BIT */
+
+#endif /* __QC_MSM_CAMSS_CSID_780_H__ */
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
index b11de4797cca..2a1746dcc1c5 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
@@ -22,11 +22,6 @@
* alternate register layout.
*/
-#define CSID_HW_VERSION 0x0
-#define HW_VERSION_STEPPING 0
-#define HW_VERSION_REVISION 16
-#define HW_VERSION_GENERATION 28
-
#define CSID_RST_STROBES 0x10
#define RST_STROBES 0
@@ -176,306 +171,171 @@
#define TPG_COLOR_BOX_CFG_MODE 0
#define TPG_COLOR_BOX_PATTERN_SEL 2
-static const struct csid_format csid_formats[] = {
- {
- MEDIA_BUS_FMT_UYVY8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_VYUY8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YUYV8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_YVYU8_1X16,
- DATA_TYPE_YUV422_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 2,
- },
- {
- MEDIA_BUS_FMT_SBGGR8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB8_1X8,
- DATA_TYPE_RAW_8BIT,
- DECODE_FORMAT_UNCOMPRESSED_8_BIT,
- 8,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB10_1X10,
- DATA_TYPE_RAW_10BIT,
- DECODE_FORMAT_UNCOMPRESSED_10_BIT,
- 10,
- 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,
- 10,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB12_1X12,
- DATA_TYPE_RAW_12BIT,
- DECODE_FORMAT_UNCOMPRESSED_12_BIT,
- 12,
- 1,
- },
- {
- MEDIA_BUS_FMT_SBGGR14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGBRG14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SGRBG14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
- {
- MEDIA_BUS_FMT_SRGGB14_1X14,
- DATA_TYPE_RAW_14BIT,
- DECODE_FORMAT_UNCOMPRESSED_14_BIT,
- 14,
- 1,
- },
-};
-
-static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rx(struct csid_device *csid,
+ struct csid_phy_config *phy, int vc)
{
- struct csid_testgen_config *tg = &csid->testgen;
- u32 val;
- u32 phy_sel = 0;
u8 lane_cnt = csid->phy.lane_cnt;
- /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
- const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats,
- input_format->code);
+ int val;
if (!lane_cnt)
lane_cnt = 4;
- if (!tg->enabled)
- phy_sel = csid->phy.csiphy_id;
-
- if (enable) {
- /*
- * DT_ID is a two bit bitfield that is concatenated with
- * the four least significant bits of the five bit VC
- * bitfield to generate an internal CID value.
- *
- * CSID_RDI_CFG0(vc)
- * DT_ID : 28:27
- * VC : 26:22
- * DT : 21:16
- *
- * CID : VC 3:0 << 2 | DT_ID 1:0
- */
- u8 dt_id = vc & 0x03;
-
- if (tg->enabled) {
- /* configure one DT, infinite frames */
- val = vc << TPG_VC_CFG0_VC_NUM;
- val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE;
- val |= 0 << TPG_VC_CFG0_NUM_FRAMES;
- writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0);
-
- val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT;
- val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT;
- writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1);
-
- writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED);
-
- val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT;
- 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 = 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 - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE;
- val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD;
- val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT;
- writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0));
-
- writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG);
-
- writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG);
- }
+ val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
+ val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
+ val |= phy->csiphy_id << CSI2_RX_CFG0_PHY_NUM_SEL;
+ writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0);
- 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 |= 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(vc));
+ val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN;
+ if (vc > 3)
+ val |= 1 << CSI2_RX_CFG1_VC_MODE;
+ val |= 1 << CSI2_RX_CFG1_MISR_EN;
+ writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1);
+}
- /* CSID_TIMESTAMP_STB_POST_IRQ */
- val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL;
- writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc));
+static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
+{
+ int val;
- val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+ 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(rdi));
+}
- val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc));
+static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc)
+{
+ struct csid_testgen_config *tg = &csid->testgen;
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
+ input_format->code);
+ u8 lane_cnt = csid->phy.lane_cnt;
+ u32 val;
- val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+ if (!lane_cnt)
+ lane_cnt = 4;
- val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+ /* configure one DT, infinite frames */
+ val = vc << TPG_VC_CFG0_VC_NUM;
+ val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE;
+ val |= 0 << TPG_VC_CFG0_NUM_FRAMES;
+ writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0);
- val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc));
+ val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT;
+ val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT;
+ writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1);
- val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc));
+ writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED);
- val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc));
+ val = (input_format->height & 0x1fff) << TPG_DT_n_CFG_0_FRAME_HEIGHT;
+ 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 = 0;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc));
+ val = format->data_type << TPG_DT_n_CFG_1_DATA_TYPE;
+ writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0));
- val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc));
+ val = (tg->mode - 1) << TPG_DT_n_CFG_2_PAYLOAD_MODE;
+ val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD;
+ val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT;
+ writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0));
- val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc));
- val |= 1 << RDI_CFG0_ENABLE;
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
- }
+ writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG);
- if (tg->enabled) {
- val = enable << TPG_CTRL_TEST_EN;
- val |= 1 << TPG_CTRL_FS_PKT_EN;
- val |= 1 << TPG_CTRL_FE_PKT_EN;
- val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES;
- val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS;
- val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES;
- writel_relaxed(val, csid->base + CSID_TPG_CTRL);
- }
+ writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG);
- val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
- val |= csid->phy.lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
- val |= phy_sel << CSI2_RX_CFG0_PHY_NUM_SEL;
- writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0);
+ val = enable << TPG_CTRL_TEST_EN;
+ val |= 1 << TPG_CTRL_FS_PKT_EN;
+ val |= 1 << TPG_CTRL_FE_PKT_EN;
+ val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES;
+ val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS;
+ val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES;
+ writel_relaxed(val, csid->base + CSID_TPG_CTRL);
+}
- val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN;
- if (vc > 3)
- val |= 1 << CSI2_RX_CFG1_VC_MODE;
- val |= 1 << CSI2_RX_CFG1_MISR_EN;
- writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1);
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+{
+ /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
+ csid->res->formats->nformats,
+ input_format->code);
+ u32 val;
- 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;
+ /*
+ * DT_ID is a two bit bitfield that is concatenated with
+ * the four least significant bits of the five bit VC
+ * bitfield to generate an internal CID value.
+ *
+ * CSID_RDI_CFG0(vc)
+ * DT_ID : 28:27
+ * VC : 26:22
+ * DT : 21:16
+ *
+ * CID : VC 3:0 << 2 | DT_ID 1:0
+ */
+ u8 dt_id = vc & 0x03;
+
+ val = 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 |= 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(vc));
+
+ /* CSID_TIMESTAMP_STB_POST_IRQ */
+ val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL;
+ writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc));
+
+ val = 1;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc));
+
+ val = 0;
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc));
+
+ val = 0;
writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc));
+
+ val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc));
+ val |= enable << RDI_CFG0_ENABLE;
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
}
static void csid_configure_stream(struct csid_device *csid, u8 enable)
{
+ struct csid_testgen_config *tg = &csid->testgen;
u8 i;
/* Loop through all enabled VCs and configure stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
- if (csid->phy.en_vc & BIT(i))
- __csid_configure_stream(csid, enable, i);
+ if (csid->phy.en_vc & BIT(i)) {
+ if (tg->enabled)
+ __csid_configure_testgen(csid, enable, i);
+
+ __csid_configure_rdi_stream(csid, enable, i);
+ __csid_configure_rx(csid, &csid->phy, i);
+ __csid_ctrl_rdi(csid, enable, i);
+ }
}
static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
@@ -487,29 +347,6 @@ static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
}
/*
- * csid_hw_version - CSID hardware version query
- * @csid: CSID device
- *
- * Return HW version or error
- */
-static u32 csid_hw_version(struct csid_device *csid)
-{
- u32 hw_version;
- u32 hw_gen;
- u32 hw_rev;
- u32 hw_step;
-
- hw_version = readl_relaxed(csid->base + CSID_HW_VERSION);
- hw_gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
- hw_rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
- hw_step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
- dev_dbg(csid->camss->dev, "CSID HW Version = %u.%u.%u\n",
- hw_gen, hw_rev, hw_step);
-
- return hw_version;
-}
-
-/*
* csid_isr - CSID module interrupt service routine
* @irq: Interrupt line
* @dev: CSID device
@@ -578,42 +415,8 @@ static int csid_reset(struct csid_device *csid)
return 0;
}
-static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
- unsigned int match_format_idx, u32 match_code)
-{
- switch (sink_code) {
- case MEDIA_BUS_FMT_SBGGR10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- case MEDIA_BUS_FMT_Y10_1X10:
- {
- u32 src_code[] = {
- MEDIA_BUS_FMT_Y10_1X10,
- MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
- };
-
- return csid_find_code(src_code, ARRAY_SIZE(src_code),
- match_format_idx, match_code);
- }
- default:
- if (match_format_idx > 0)
- return 0;
-
- return sink_code;
- }
-}
-
static void csid_subdev_init(struct csid_device *csid)
{
- csid->formats = csid_formats;
- csid->nformats = ARRAY_SIZE(csid_formats);
csid->testgen.modes = csid_testgen_modes;
csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2;
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index eb27d69e89a1..5284b5857368 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -17,6 +17,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/media-entity.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-subdev.h>
@@ -29,6 +30,11 @@
#define VFE_480_CSID_OFFSET 0x1200
#define VFE_480_LITE_CSID_OFFSET 0x200
+#define CSID_HW_VERSION 0x0
+#define HW_VERSION_STEPPING 0
+#define HW_VERSION_REVISION 16
+#define HW_VERSION_GENERATION 28
+
#define MSM_CSID_NAME "msm_csid"
const char * const csid_testgen_modes[] = {
@@ -45,6 +51,450 @@ const char * const csid_testgen_modes[] = {
NULL
};
+static const struct csid_format_info formats_4_1[] = {
+ {
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+};
+
+static const struct csid_format_info formats_4_7[] = {
+ {
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+};
+
+static const struct csid_format_info formats_gen2[] = {
+ {
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_1X16,
+ MIPI_CSI2_DT_YUV422_8B,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ MIPI_CSI2_DT_RAW14,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+};
+
+const struct csid_formats csid_formats_4_1 = {
+ .nformats = ARRAY_SIZE(formats_4_1),
+ .formats = formats_4_1
+};
+
+const struct csid_formats csid_formats_4_7 = {
+ .nformats = ARRAY_SIZE(formats_4_7),
+ .formats = formats_4_7
+};
+
+const struct csid_formats csid_formats_gen2 = {
+ .nformats = ARRAY_SIZE(formats_gen2),
+ .formats = formats_gen2
+};
+
u32 csid_find_code(u32 *codes, unsigned int ncodes,
unsigned int match_format_idx, u32 match_code)
{
@@ -65,9 +515,9 @@ u32 csid_find_code(u32 *codes, unsigned int ncodes,
return codes[0];
}
-const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats,
- unsigned int nformats,
- u32 code)
+const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats,
+ unsigned int nformats,
+ u32 code)
{
unsigned int i;
@@ -87,12 +537,12 @@ const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats,
static int csid_set_clock_rates(struct csid_device *csid)
{
struct device *dev = csid->camss->dev;
- const struct csid_format *fmt;
+ const struct csid_format_info *fmt;
s64 link_freq;
int i, j;
int ret;
- fmt = csid_get_fmt_entry(csid->formats, csid->nformats,
+ fmt = csid_get_fmt_entry(csid->res->formats->formats, csid->res->formats->nformats,
csid->fmt[MSM_CSIPHY_PAD_SINK].code);
link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp,
csid->phy.lane_cnt);
@@ -147,6 +597,78 @@ static int csid_set_clock_rates(struct csid_device *csid)
}
/*
+ * csid_hw_version - CSID hardware version query
+ * @csid: CSID device
+ *
+ * Return HW version or error
+ */
+u32 csid_hw_version(struct csid_device *csid)
+{
+ u32 hw_version;
+ u32 hw_gen;
+ u32 hw_rev;
+ u32 hw_step;
+
+ hw_version = readl_relaxed(csid->base + CSID_HW_VERSION);
+ hw_gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
+ hw_rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
+ hw_step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
+ dev_dbg(csid->camss->dev, "CSID:%d HW Version = %u.%u.%u\n",
+ csid->id, hw_gen, hw_rev, hw_step);
+
+ return hw_version;
+}
+
+/*
+ * csid_src_pad_code - Pick an output/src format based on the input/sink format
+ * @csid: CSID device
+ * @sink_code: The sink format of the input
+ * @match_format_idx: Request preferred index, as defined by subdevice csid
+ * format. Set @match_code to 0 if used.
+ * @match_code: Request preferred code, set @match_format_idx to 0 if used
+ *
+ * Return 0 on failure or src format code otherwise
+ */
+u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code)
+{
+ if (csid->camss->res->version == CAMSS_8x16) {
+ if (match_format_idx > 0)
+ return 0;
+
+ return sink_code;
+ }
+
+ switch (sink_code) {
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ case MEDIA_BUS_FMT_Y10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ match_format_idx, match_code);
+ }
+ default:
+ if (match_format_idx > 0)
+ return 0;
+
+ return sink_code;
+ }
+}
+
+/*
* csid_set_power - Power on/off CSID module
* @sd: CSID V4L2 subdevice
* @on: Requested power state
@@ -158,7 +680,6 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
struct csid_device *csid = v4l2_get_subdevdata(sd);
struct camss *camss = csid->camss;
struct device *dev = camss->dev;
- struct vfe_device *vfe = &camss->vfe[csid->id];
int ret = 0;
if (on) {
@@ -167,7 +688,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
* switching on the CSID. Do so unconditionally, as there is no
* drawback in following the same powering order on older SoCs.
*/
- ret = vfe_get(vfe);
+ ret = csid->res->parent_dev_ops->get(camss, csid->id);
if (ret < 0)
return ret;
@@ -202,7 +723,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
enable_irq(csid->irq);
- ret = csid->ops->reset(csid);
+ ret = csid->res->hw_ops->reset(csid);
if (ret < 0) {
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
@@ -212,14 +733,14 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
return ret;
}
- csid->ops->hw_version(csid);
+ csid->res->hw_ops->hw_version(csid);
} else {
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
regulator_bulk_disable(csid->num_supplies,
csid->supplies);
pm_runtime_put_sync(dev);
- vfe_put(vfe);
+ csid->res->parent_dev_ops->put(camss, csid->id);
}
return ret;
@@ -240,11 +761,13 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
int ret;
if (enable) {
- ret = v4l2_ctrl_handler_setup(&csid->ctrls);
- if (ret < 0) {
- dev_err(csid->camss->dev,
- "could not sync v4l2 controls: %d\n", ret);
- return ret;
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED) {
+ ret = v4l2_ctrl_handler_setup(&csid->ctrls);
+ if (ret < 0) {
+ dev_err(csid->camss->dev,
+ "could not sync v4l2 controls: %d\n", ret);
+ return ret;
+ }
}
if (!csid->testgen.enabled &&
@@ -253,7 +776,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
}
if (csid->phy.need_vc_update) {
- csid->ops->configure_stream(csid, enable);
+ csid->res->hw_ops->configure_stream(csid, enable);
csid->phy.need_vc_update = false;
}
@@ -301,12 +824,12 @@ static void csid_try_format(struct csid_device *csid,
case MSM_CSID_PAD_SINK:
/* Set format on sink pad */
- for (i = 0; i < csid->nformats; i++)
- if (fmt->code == csid->formats[i].code)
+ for (i = 0; i < csid->res->formats->nformats; i++)
+ if (fmt->code == csid->res->formats->formats[i].code)
break;
/* If not found, use UYVY as default */
- if (i >= csid->nformats)
+ if (i >= csid->res->formats->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -318,24 +841,25 @@ static void csid_try_format(struct csid_device *csid,
break;
case MSM_CSID_PAD_SRC:
- if (csid->testgen_mode->cur.val == 0) {
+ if (csid->testgen.nmodes == CSID_PAYLOAD_MODE_DISABLED ||
+ csid->testgen_mode->cur.val == 0) {
/* Test generator is disabled, */
/* keep pad formats in sync */
u32 code = fmt->code;
*fmt = *__csid_get_format(csid, sd_state,
MSM_CSID_PAD_SINK, which);
- fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code);
+ fmt->code = csid->res->hw_ops->src_pad_code(csid, fmt->code, 0, code);
} else {
/* Test generator is enabled, set format on source */
/* pad to allow test generator usage */
- for (i = 0; i < csid->nformats; i++)
- if (csid->formats[i].code == fmt->code)
+ for (i = 0; i < csid->res->formats->nformats; i++)
+ if (csid->res->formats->formats[i].code == fmt->code)
break;
/* If not found, use UYVY as default */
- if (i >= csid->nformats)
+ if (i >= csid->res->formats->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -363,27 +887,28 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd,
struct csid_device *csid = v4l2_get_subdevdata(sd);
if (code->pad == MSM_CSID_PAD_SINK) {
- if (code->index >= csid->nformats)
+ if (code->index >= csid->res->formats->nformats)
return -EINVAL;
- code->code = csid->formats[code->index].code;
+ code->code = csid->res->formats->formats[code->index].code;
} else {
- if (csid->testgen_mode->cur.val == 0) {
+ if (csid->testgen.nmodes == CSID_PAYLOAD_MODE_DISABLED ||
+ csid->testgen_mode->cur.val == 0) {
struct v4l2_mbus_framefmt *sink_fmt;
sink_fmt = __csid_get_format(csid, sd_state,
MSM_CSID_PAD_SINK,
code->which);
- code->code = csid->ops->src_pad_code(csid, sink_fmt->code,
- code->index, 0);
+ code->code = csid->res->hw_ops->src_pad_code(csid, sink_fmt->code,
+ code->index, 0);
if (!code->code)
return -EINVAL;
} else {
- if (code->index >= csid->nformats)
+ if (code->index >= csid->res->formats->nformats)
return -EINVAL;
- code->code = csid->formats[code->index].code;
+ code->code = csid->res->formats->formats[code->index].code;
}
}
@@ -529,7 +1054,7 @@ static int csid_set_test_pattern(struct csid_device *csid, s32 value)
tg->enabled = !!value;
- return csid->ops->configure_testgen_pattern(csid, value);
+ return csid->res->hw_ops->configure_testgen_pattern(csid, value);
}
/*
@@ -575,9 +1100,14 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
csid->camss = camss;
csid->id = id;
- csid->ops = res->ops;
+ csid->res = &res->csid;
- csid->ops->subdev_init(csid);
+ if (dev_WARN_ONCE(dev, !csid->res->parent_dev_ops,
+ "Error: CSID depends on VFE/IFE device ops!\n")) {
+ return -EINVAL;
+ }
+
+ csid->res->hw_ops->subdev_init(csid);
/* Memory */
@@ -587,9 +1117,11 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
* VFE to be initialized before CSID
*/
if (id >= 2) /* VFE/CSID lite */
- csid->base = camss->vfe[id].base + VFE_480_LITE_CSID_OFFSET;
+ csid->base = csid->res->parent_dev_ops->get_base_address(camss, id)
+ + VFE_480_LITE_CSID_OFFSET;
else
- csid->base = camss->vfe[id].base + VFE_480_CSID_OFFSET;
+ csid->base = csid->res->parent_dev_ops->get_base_address(camss, id)
+ + VFE_480_CSID_OFFSET;
} else {
csid->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
if (IS_ERR(csid->base))
@@ -605,7 +1137,7 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
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,
+ ret = devm_request_irq(dev, csid->irq, csid->res->hw_ops->isr,
IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN,
csid->irq_name, csid);
if (ret < 0) {
@@ -740,7 +1272,8 @@ static int csid_link_setup(struct media_entity *entity,
/* If test generator is enabled */
/* do not allow a link from CSIPHY to CSID */
- if (csid->testgen_mode->cur.val != 0)
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED &&
+ csid->testgen_mode->cur.val != 0)
return -EBUSY;
sd = media_entity_to_v4l2_subdev(remote->entity);
@@ -833,24 +1366,27 @@ int msm_csid_register_entity(struct csid_device *csid,
MSM_CSID_NAME, csid->id);
v4l2_set_subdevdata(sd, csid);
- ret = v4l2_ctrl_handler_init(&csid->ctrls, 1);
- if (ret < 0) {
- dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
- return ret;
- }
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED) {
+ ret = v4l2_ctrl_handler_init(&csid->ctrls, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
+ return ret;
+ }
- csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls,
- &csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
- csid->testgen.nmodes, 0, 0,
- csid->testgen.modes);
+ csid->testgen_mode =
+ v4l2_ctrl_new_std_menu_items(&csid->ctrls,
+ &csid_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ csid->testgen.nmodes, 0, 0,
+ csid->testgen.modes);
- if (csid->ctrls.error) {
- dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
- ret = csid->ctrls.error;
- goto free_ctrl;
- }
+ if (csid->ctrls.error) {
+ dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error);
+ ret = csid->ctrls.error;
+ goto free_ctrl;
+ }
- csid->subdev.ctrl_handler = &csid->ctrls;
+ csid->subdev.ctrl_handler = &csid->ctrls;
+ }
ret = csid_init_formats(sd, NULL);
if (ret < 0) {
@@ -881,7 +1417,8 @@ int msm_csid_register_entity(struct csid_device *csid,
media_cleanup:
media_entity_cleanup(&sd->entity);
free_ctrl:
- v4l2_ctrl_handler_free(&csid->ctrls);
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED)
+ v4l2_ctrl_handler_free(&csid->ctrls);
return ret;
}
@@ -894,10 +1431,11 @@ void msm_csid_unregister_entity(struct csid_device *csid)
{
v4l2_device_unregister_subdev(&csid->subdev);
media_entity_cleanup(&csid->subdev.entity);
- v4l2_ctrl_handler_free(&csid->ctrls);
+ if (csid->testgen.nmodes != CSID_PAYLOAD_MODE_DISABLED)
+ v4l2_ctrl_handler_free(&csid->ctrls);
}
inline bool csid_is_lite(struct csid_device *csid)
{
- return csid->camss->res->csid_res[csid->id].is_lite;
+ return csid->camss->res->csid_res[csid->id].csid.is_lite;
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index fddccb69da13..9dc826d8c8f6 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -27,29 +27,6 @@
/* CSID hardware can demultiplex up to 4 outputs */
#define MSM_CSID_MAX_SRC_STREAMS 4
-#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12
-#define DATA_TYPE_YUV420_8BIT 0x18
-#define DATA_TYPE_YUV420_10BIT 0x19
-#define DATA_TYPE_YUV420_8BIT_LEGACY 0x1a
-#define DATA_TYPE_YUV420_8BIT_SHIFTED 0x1c /* Chroma Shifted Pixel Sampling */
-#define DATA_TYPE_YUV420_10BIT_SHIFTED 0x1d /* Chroma Shifted Pixel Sampling */
-#define DATA_TYPE_YUV422_8BIT 0x1e
-#define DATA_TYPE_YUV422_10BIT 0x1f
-#define DATA_TYPE_RGB444 0x20
-#define DATA_TYPE_RGB555 0x21
-#define DATA_TYPE_RGB565 0x22
-#define DATA_TYPE_RGB666 0x23
-#define DATA_TYPE_RGB888 0x24
-#define DATA_TYPE_RAW_24BIT 0x27
-#define DATA_TYPE_RAW_6BIT 0x28
-#define DATA_TYPE_RAW_7BIT 0x29
-#define DATA_TYPE_RAW_8BIT 0x2a
-#define DATA_TYPE_RAW_10BIT 0x2b
-#define DATA_TYPE_RAW_12BIT 0x2c
-#define DATA_TYPE_RAW_14BIT 0x2d
-#define DATA_TYPE_RAW_16BIT 0x2e
-#define DATA_TYPE_RAW_20BIT 0x2f
-
#define CSID_RESET_TIMEOUT_MS 500
enum csid_testgen_mode {
@@ -67,7 +44,7 @@ enum csid_testgen_mode {
CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2 = 9, /* excluding disabled */
};
-struct csid_format {
+struct csid_format_info {
u32 code;
u8 data_type;
u8 decode_format;
@@ -75,6 +52,11 @@ struct csid_format {
u8 spp; /* bus samples per pixel */
};
+struct csid_formats {
+ unsigned int nformats;
+ const struct csid_format_info *formats;
+};
+
struct csid_testgen_config {
enum csid_testgen_mode mode;
const char * const*modes;
@@ -147,6 +129,21 @@ struct csid_hw_ops {
* @csid: CSID device
*/
void (*subdev_init)(struct csid_device *csid);
+
+ /*
+ * reg_update - receive message from other sub device
+ * @csid: CSID device
+ * @port_id: Port id
+ * @is_clear: Indicate if it is clearing reg update or setting reg update
+ */
+ void (*reg_update)(struct csid_device *csid, int port_id, bool is_clear);
+};
+
+struct csid_subdev_resources {
+ bool is_lite;
+ const struct csid_hw_ops *hw_ops;
+ const struct parent_dev_ops *parent_dev_ops;
+ const struct csid_formats *formats;
};
struct csid_device {
@@ -157,6 +154,7 @@ struct csid_device {
void __iomem *base;
u32 irq;
char irq_name[30];
+ u32 reg_update;
struct camss_clock *clock;
int nclocks;
struct regulator_bulk_data *supplies;
@@ -167,9 +165,7 @@ struct csid_device {
struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM];
struct v4l2_ctrl_handler ctrls;
struct v4l2_ctrl *testgen_mode;
- const struct csid_format *formats;
- unsigned int nformats;
- const struct csid_hw_ops *ops;
+ const struct csid_subdev_resources *res;
};
struct camss_subdev_resources;
@@ -188,16 +184,16 @@ u32 csid_find_code(u32 *codes, unsigned int ncode,
unsigned int match_format_idx, u32 match_code);
/*
- * csid_get_fmt_entry - Find csid_format entry with matching format code
- * @formats: Array of format csid_format entries
+ * csid_get_fmt_entry - Find csid_format_info entry with matching format code
+ * @formats: Array of format csid_format_info entries
* @nformats: Length of @nformats array
* @code: Desired format code
*
* Return formats[0] on failure to find code
*/
-const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats,
- unsigned int nformats,
- u32 code);
+const struct csid_format_info *csid_get_fmt_entry(const struct csid_format_info *formats,
+ unsigned int nformats,
+ u32 code);
int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
const struct camss_subdev_resources *res, u8 id);
@@ -211,9 +207,15 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id);
extern const char * const csid_testgen_modes[];
+extern const struct csid_formats csid_formats_4_1;
+extern const struct csid_formats csid_formats_4_7;
+extern const struct csid_formats csid_formats_gen2;
+
extern const struct csid_hw_ops csid_ops_4_1;
extern const struct csid_hw_ops csid_ops_4_7;
+extern const struct csid_hw_ops csid_ops_680;
extern const struct csid_hw_ops csid_ops_gen2;
+extern const struct csid_hw_ops csid_ops_780;
/*
* csid_is_lite - Check if CSID is CSID lite.
@@ -223,4 +225,25 @@ extern const struct csid_hw_ops csid_ops_gen2;
*/
bool csid_is_lite(struct csid_device *csid);
+/*
+ * csid_hw_version - CSID hardware version query
+ * @csid: CSID device
+ *
+ * Return HW version or error
+ */
+u32 csid_hw_version(struct csid_device *csid);
+
+/*
+ * csid_src_pad_code - Pick an output/src format based on the input/sink format
+ * @csid: CSID device
+ * @sink_code: The sink format of the input
+ * @match_format_idx: Request preferred index, as defined by subdevice csid
+ * format. Set @match_code to 0 if used.
+ * @match_code: Request preferred code, set @match_format_idx to 0 if used
+ *
+ * Return 0 on failure or src format code otherwise
+ */
+u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int match_format_idx, u32 match_code);
+
#endif /* QC_MSM_CAMSS_CSID_H */
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
index cd4a8c369234..9d67e7fa6366 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
@@ -180,6 +180,11 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
return IRQ_HANDLED;
}
+static int csiphy_init(struct csiphy_device *csiphy)
+{
+ return 0;
+}
+
const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = {
.get_lane_mask = csiphy_get_lane_mask,
.hw_version_read = csiphy_hw_version_read,
@@ -187,4 +192,5 @@ const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = {
.lanes_enable = csiphy_lanes_enable,
.lanes_disable = csiphy_lanes_disable,
.isr = csiphy_isr,
+ .init = csiphy_init,
};
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
index f50e2235c37f..f732a76de93e 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -42,243 +42,522 @@
#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15(n) (0x03c + 0x100 * (n))
#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8
-#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(n) (0x800 + 0x4 * (n))
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(offset, n) ((offset) + 0x4 * (n))
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL5_CLK_ENABLE BIT(7)
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0)
#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
-#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n))
-
-#define CSIPHY_DEFAULT_PARAMS 0
-#define CSIPHY_LANE_ENABLE 1
-#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
-#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3
-#define CSIPHY_DNP_PARAMS 4
-#define CSIPHY_2PH_REGS 5
-#define CSIPHY_3PH_REGS 6
-
-struct csiphy_reg_t {
+#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(offset, n) ((offset) + 0xb0 + 0x4 * (n))
+
+#define CSIPHY_DEFAULT_PARAMS 0
+#define CSIPHY_LANE_ENABLE 1
+#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2
+#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3
+#define CSIPHY_DNP_PARAMS 4
+#define CSIPHY_2PH_REGS 5
+#define CSIPHY_3PH_REGS 6
+#define CSIPHY_SKEW_CAL 7
+
+struct csiphy_lane_regs {
s32 reg_addr;
s32 reg_data;
- s32 delay;
+ u32 delay_us;
u32 csiphy_param_type;
};
/* GEN2 1.0 2PH */
static const struct
-csiphy_reg_t lane_regs_sdm845[5][14] = {
- {
- {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
+csiphy_lane_regs lane_regs_sdm845[] = {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+/* GEN2 1.1 2PH */
+static const struct
+csiphy_lane_regs lane_regs_sc8280xp[] = {
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x000C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x90, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x0E, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
};
/* GEN2 1.2.1 2PH */
static const struct
-csiphy_reg_t lane_regs_sm8250[5][20] = {
- {
- {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0900, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0908, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0034, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0010, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x001C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0000, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0024, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C80, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C88, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0734, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0710, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x071C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0708, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x070c, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0724, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0A04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0234, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0210, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x021C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0208, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0200, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x020c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0224, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0B04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0434, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0410, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x041C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0400, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x040c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0424, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
- {
- {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0C04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0634, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0610, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x061C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0608, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
- {0x0600, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x060c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0624, 0x00, 0x00, CSIPHY_DNP_PARAMS},
- {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
- {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
- },
+csiphy_lane_regs lane_regs_sm8250[] = {
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0900, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0908, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0010, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0000, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0024, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C80, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C88, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x070c, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0724, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0A04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0210, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0200, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x020c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0224, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0B04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0410, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0400, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x040c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0424, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C00, 0x05, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C08, 0x10, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0610, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061C, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0600, 0x8D, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x060c, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0624, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+/* GEN2 2.1.2 2PH DPHY mode */
+static const struct
+csiphy_lane_regs lane_regs_sm8550[] = {
+ {0x0E90, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x00A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0090, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x04A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0490, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x08A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0890, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0CA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C90, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0E30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E0C, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E38, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0E10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0800, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0838, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x082C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0834, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x081C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x083C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0804, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0808, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C00, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C38, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0C10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x005C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0060, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x045C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0460, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x085C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0860, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0864, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0xD7, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C5C, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C60, 0xBD, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C64, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS},
+};
+
+/* 4nm 2PH v 2.1.2 2p5Gbps 4 lane DPHY mode */
+static const struct
+csiphy_lane_regs lane_regs_x1e80100[] = {
+ /* Power up lanes 2ph mode */
+ {0x1014, 0xD5, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x101C, 0x7A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x1018, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0094, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x00A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0090, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0098, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0094, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x005C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0060, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0064, 0x7F, 0x00, CSIPHY_SKEW_CAL},
+
+ {0x0E94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0EA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0E30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E28, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E00, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E0C, 0xFF, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E38, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0E08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0E10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+
+ {0x0494, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x04A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0490, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0498, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0494, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x045C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0460, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0464, 0x7F, 0x00, CSIPHY_SKEW_CAL},
+
+ {0x0894, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x08A0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0890, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0898, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0830, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0800, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0838, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x082C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0834, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x081C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0814, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x083C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0804, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0820, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0808, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0810, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0894, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x085C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0860, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0864, 0x7F, 0x00, CSIPHY_SKEW_CAL},
+
+ {0x0C94, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0CA0, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C90, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C98, 0x08, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0x07, 0x01, CSIPHY_DEFAULT_PARAMS},
+ {0x0C30, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C00, 0x8E, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C38, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C2C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C34, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C1C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C14, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C3C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C04, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C20, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C08, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0C10, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0C94, 0xD7, 0x00, CSIPHY_SKEW_CAL},
+ {0x0C5C, 0x00, 0x00, CSIPHY_SKEW_CAL},
+ {0x0C60, 0xBD, 0x00, CSIPHY_SKEW_CAL},
+ {0x0C64, 0x7F, 0x00, CSIPHY_SKEW_CAL},
};
static void csiphy_hw_version_read(struct csiphy_device *csiphy,
struct device *dev)
{
+ struct csiphy_device_regs *regs = csiphy->regs;
u32 hw_version;
- writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID,
- csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+ writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6));
hw_version = readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(12));
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 12));
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(13)) << 8;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 13)) << 8;
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(14)) << 16;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 14)) << 16;
hw_version |= readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(15)) << 24;
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, 15)) << 24;
dev_dbg(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version);
}
@@ -289,31 +568,39 @@ static void csiphy_hw_version_read(struct csiphy_device *csiphy,
*/
static void csiphy_reset(struct csiphy_device *csiphy)
{
- writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+ struct csiphy_device_regs *regs = csiphy->regs;
+
+ writel_relaxed(0x1, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0));
usleep_range(5000, 8000);
- writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+ writel_relaxed(0x0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0));
}
static irqreturn_t csiphy_isr(int irq, void *dev)
{
struct csiphy_device *csiphy = dev;
+ struct csiphy_device_regs *regs = csiphy->regs;
int i;
for (i = 0; i < 11; i++) {
int c = i + 22;
u8 val = readl_relaxed(csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(i));
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(regs->offset, i));
writel_relaxed(val, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(c));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, c));
}
- writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10));
- writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10));
+ writel_relaxed(0x1, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10));
+ writel_relaxed(0x0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 10));
- for (i = 22; i < 33; i++)
+ for (i = 22; i < 33; i++) {
writel_relaxed(0x0, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, i));
+ }
return IRQ_HANDLED;
}
@@ -415,38 +702,27 @@ static void csiphy_gen1_config_lanes(struct csiphy_device *csiphy,
static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy,
u8 settle_cnt)
{
- const struct csiphy_reg_t *r;
- int i, l, array_size;
+ const struct csiphy_lane_regs *r = csiphy->regs->lane_regs;
+ int i, array_size = csiphy->regs->lane_array_size;
u32 val;
- switch (csiphy->camss->res->version) {
- case CAMSS_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:
- WARN(1, "unknown cspi version\n");
- return;
- }
-
- for (l = 0; l < 5; l++) {
- for (i = 0; i < array_size; i++, r++) {
- switch (r->csiphy_param_type) {
- case CSIPHY_SETTLE_CNT_LOWER_BYTE:
- val = settle_cnt & 0xff;
- break;
- case CSIPHY_DNP_PARAMS:
- continue;
- default:
- val = r->reg_data;
- break;
- }
- writel_relaxed(val, csiphy->base + r->reg_addr);
+ for (i = 0; i < array_size; i++, r++) {
+ switch (r->csiphy_param_type) {
+ case CSIPHY_SETTLE_CNT_LOWER_BYTE:
+ val = settle_cnt & 0xff;
+ break;
+ case CSIPHY_SKEW_CAL:
+ /* TODO: support application of skew from dt flag */
+ continue;
+ case CSIPHY_DNP_PARAMS:
+ continue;
+ default:
+ val = r->reg_data;
+ break;
}
+ writel_relaxed(val, csiphy->base + r->reg_addr);
+ if (r->delay_us)
+ udelay(r->delay_us);
}
}
@@ -463,13 +739,30 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
return lane_mask;
}
+static bool csiphy_is_gen2(u32 version)
+{
+ bool ret = false;
+
+ switch (version) {
+ case CAMSS_7280:
+ case CAMSS_8250:
+ case CAMSS_8280XP:
+ case CAMSS_845:
+ case CAMSS_8550:
+ case CAMSS_X1E80100:
+ ret = true;
+ break;
+ }
+
+ return ret;
+}
+
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->res->version == CAMSS_845 ||
- csiphy->camss->res->version == CAMSS_8250);
+ struct csiphy_device_regs *regs = csiphy->regs;
u8 settle_cnt;
u8 val;
int i;
@@ -480,35 +773,87 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy,
for (i = 0; i < c->num_data; i++)
val |= BIT(c->data[i].pos * 2);
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5));
val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6));
val = 0x02;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(7));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 7));
val = 0x00;
- writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 0));
- if (is_gen2)
+ if (csiphy_is_gen2(csiphy->camss->res->version))
csiphy_gen2_config_lanes(csiphy, settle_cnt);
else
csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt);
/* IRQ_MASK registers - disable all interrupts */
- for (i = 11; i < 22; i++)
- writel_relaxed(0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i));
+ for (i = 11; i < 22; i++) {
+ writel_relaxed(0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, i));
+ }
}
static void csiphy_lanes_disable(struct csiphy_device *csiphy,
struct csiphy_config *cfg)
{
+ struct csiphy_device_regs *regs = csiphy->regs;
+
writel_relaxed(0, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 5));
writel_relaxed(0, csiphy->base +
- CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(regs->offset, 6));
+}
+
+static int csiphy_init(struct csiphy_device *csiphy)
+{
+ struct device *dev = csiphy->camss->dev;
+ struct csiphy_device_regs *regs;
+
+ regs = devm_kmalloc(dev, sizeof(*regs), GFP_KERNEL);
+ if (!regs)
+ return -ENOMEM;
+
+ csiphy->regs = regs;
+ regs->offset = 0x800;
+
+ switch (csiphy->camss->res->version) {
+ case CAMSS_845:
+ regs->lane_regs = &lane_regs_sdm845[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sdm845);
+ break;
+ case CAMSS_7280:
+ case CAMSS_8250:
+ regs->lane_regs = &lane_regs_sm8250[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8250);
+ break;
+ case CAMSS_8280XP:
+ regs->lane_regs = &lane_regs_sc8280xp[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sc8280xp);
+ break;
+ case CAMSS_X1E80100:
+ regs->lane_regs = &lane_regs_x1e80100[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_x1e80100);
+ regs->offset = 0x1000;
+ break;
+ case CAMSS_8550:
+ regs->lane_regs = &lane_regs_sm8550[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sm8550);
+ regs->offset = 0x1000;
+ break;
+ default:
+ WARN(1, "unknown csiphy version\n");
+ return -ENODEV;
+ }
+
+ return 0;
}
const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = {
@@ -518,4 +863,5 @@ const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = {
.lanes_enable = csiphy_lanes_enable,
.lanes_disable = csiphy_lanes_disable,
.isr = csiphy_isr,
+ .init = csiphy_init,
};
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 264c99efeae8..c622efcc92ff 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -24,12 +24,7 @@
#define MSM_CSIPHY_NAME "msm_csiphy"
-struct csiphy_format {
- u32 code;
- u8 bpp;
-};
-
-static const struct csiphy_format csiphy_formats_8x16[] = {
+static const struct csiphy_format_info formats_8x16[] = {
{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
@@ -49,7 +44,7 @@ static const struct csiphy_format csiphy_formats_8x16[] = {
{ MEDIA_BUS_FMT_Y10_1X10, 10 },
};
-static const struct csiphy_format csiphy_formats_8x96[] = {
+static const struct csiphy_format_info formats_8x96[] = {
{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
@@ -73,7 +68,7 @@ static const struct csiphy_format csiphy_formats_8x96[] = {
{ MEDIA_BUS_FMT_Y10_1X10, 10 },
};
-static const struct csiphy_format csiphy_formats_sdm845[] = {
+static const struct csiphy_format_info formats_sdm845[] = {
{ MEDIA_BUS_FMT_UYVY8_1X16, 8 },
{ MEDIA_BUS_FMT_VYUY8_1X16, 8 },
{ MEDIA_BUS_FMT_YUYV8_1X16, 8 },
@@ -98,6 +93,26 @@ static const struct csiphy_format csiphy_formats_sdm845[] = {
{ MEDIA_BUS_FMT_Y10_1X10, 10 },
};
+const struct csiphy_formats csiphy_formats_8x16 = {
+ .nformats = ARRAY_SIZE(formats_8x16),
+ .formats = formats_8x16
+};
+
+const struct csiphy_formats csiphy_formats_8x96 = {
+ .nformats = ARRAY_SIZE(formats_8x96),
+ .formats = formats_8x96
+};
+
+const struct csiphy_formats csiphy_formats_sc7280 = {
+ .nformats = ARRAY_SIZE(formats_sdm845),
+ .formats = formats_sdm845
+};
+
+const struct csiphy_formats csiphy_formats_sdm845 = {
+ .nformats = ARRAY_SIZE(formats_sdm845),
+ .formats = formats_sdm845
+};
+
/*
* csiphy_get_bpp - map media bus format to bits per pixel
* @formats: supported media bus formats array
@@ -106,7 +121,7 @@ static const struct csiphy_format csiphy_formats_sdm845[] = {
*
* Return number of bits per pixel
*/
-static u8 csiphy_get_bpp(const struct csiphy_format *formats,
+static u8 csiphy_get_bpp(const struct csiphy_format_info *formats,
unsigned int nformats, u32 code)
{
unsigned int i;
@@ -131,7 +146,7 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
int i, j;
int ret;
- u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats,
+ u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats,
csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
@@ -202,28 +217,41 @@ static int csiphy_set_power(struct v4l2_subdev *sd, int on)
if (ret < 0)
return ret;
+ ret = regulator_bulk_enable(csiphy->num_supplies,
+ csiphy->supplies);
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
+ return ret;
+ }
+
ret = csiphy_set_clock_rates(csiphy);
if (ret < 0) {
+ regulator_bulk_disable(csiphy->num_supplies,
+ csiphy->supplies);
pm_runtime_put_sync(dev);
return ret;
}
ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
if (ret < 0) {
+ regulator_bulk_disable(csiphy->num_supplies,
+ csiphy->supplies);
pm_runtime_put_sync(dev);
return ret;
}
enable_irq(csiphy->irq);
- csiphy->ops->reset(csiphy);
+ csiphy->res->hw_ops->reset(csiphy);
- csiphy->ops->hw_version_read(csiphy, dev);
+ csiphy->res->hw_ops->hw_version_read(csiphy, dev);
} else {
disable_irq(csiphy->irq);
camss_disable_clocks(csiphy->nclocks, csiphy->clock);
+ regulator_bulk_disable(csiphy->num_supplies, csiphy->supplies);
+
pm_runtime_put_sync(dev);
}
@@ -243,8 +271,8 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
{
struct csiphy_config *cfg = &csiphy->cfg;
s64 link_freq;
- u8 lane_mask = csiphy->ops->get_lane_mask(&cfg->csi2->lane_cfg);
- u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats,
+ u8 lane_mask = csiphy->res->hw_ops->get_lane_mask(&cfg->csi2->lane_cfg);
+ u8 bpp = csiphy_get_bpp(csiphy->res->formats->formats, csiphy->res->formats->nformats,
csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
u8 val;
@@ -272,7 +300,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
wmb();
}
- csiphy->ops->lanes_enable(csiphy, cfg, link_freq, lane_mask);
+ csiphy->res->hw_ops->lanes_enable(csiphy, cfg, link_freq, lane_mask);
return 0;
}
@@ -285,7 +313,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
*/
static void csiphy_stream_off(struct csiphy_device *csiphy)
{
- csiphy->ops->lanes_disable(csiphy, &csiphy->cfg);
+ csiphy->res->hw_ops->lanes_disable(csiphy, &csiphy->cfg);
}
@@ -350,12 +378,12 @@ static void csiphy_try_format(struct csiphy_device *csiphy,
case MSM_CSIPHY_PAD_SINK:
/* Set format on sink pad */
- for (i = 0; i < csiphy->nformats; i++)
- if (fmt->code == csiphy->formats[i].code)
+ for (i = 0; i < csiphy->res->formats->nformats; i++)
+ if (fmt->code == csiphy->res->formats->formats[i].code)
break;
/* If not found, use UYVY as default */
- if (i >= csiphy->nformats)
+ if (i >= csiphy->res->formats->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -392,10 +420,10 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *format;
if (code->pad == MSM_CSIPHY_PAD_SINK) {
- if (code->index >= csiphy->nformats)
+ if (code->index >= csiphy->res->formats->nformats)
return -EINVAL;
- code->code = csiphy->formats[code->index].code;
+ code->code = csiphy->res->formats->formats[code->index].code;
} else {
if (code->index > 0)
return -EINVAL;
@@ -558,30 +586,17 @@ int msm_csiphy_subdev_init(struct camss *camss,
{
struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
- int i, j, k;
+ int i, j;
int ret;
csiphy->camss = camss;
csiphy->id = id;
csiphy->cfg.combo_mode = 0;
- csiphy->ops = res->ops;
+ csiphy->res = &res->csiphy;
- switch (camss->res->version) {
- case CAMSS_8x16:
- csiphy->formats = csiphy_formats_8x16;
- csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16);
- break;
- case CAMSS_8x96:
- case CAMSS_660:
- csiphy->formats = csiphy_formats_8x96;
- csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96);
- break;
- case CAMSS_845:
- case CAMSS_8250:
- csiphy->formats = csiphy_formats_sdm845;
- csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845);
- break;
- }
+ ret = csiphy->res->hw_ops->init(csiphy);
+ if (ret)
+ return ret;
/* Memory */
@@ -590,6 +605,7 @@ int msm_csiphy_subdev_init(struct camss *camss,
return PTR_ERR(csiphy->base);
if (camss->res->version == CAMSS_8x16 ||
+ camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_8x96) {
csiphy->base_clk_mux =
devm_platform_ioremap_resource_byname(pdev, res->reg[1]);
@@ -609,7 +625,7 @@ int msm_csiphy_subdev_init(struct camss *camss,
snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d",
dev_name(dev), MSM_CSIPHY_NAME, csiphy->id);
- ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr,
+ ret = devm_request_irq(dev, csiphy->irq, csiphy->res->hw_ops->isr,
IRQF_TRIGGER_RISING | IRQF_NO_AUTOEN,
csiphy->irq_name, csiphy);
if (ret < 0) {
@@ -664,26 +680,44 @@ int msm_csiphy_subdev_init(struct camss *camss,
for (j = 0; j < clock->nfreqs; j++)
clock->freq[j] = res->clock_rate[i][j];
- for (k = 0; k < camss->res->csiphy_num; k++) {
+ csiphy->rate_set[i] = csiphy_match_clock_name(clock->name,
+ "csiphy%d_timer",
+ csiphy->id);
+ if (csiphy->rate_set[i])
+ continue;
+
+ if (camss->res->version == CAMSS_660) {
csiphy->rate_set[i] = csiphy_match_clock_name(clock->name,
- "csiphy%d_timer", k);
+ "csi%d_phy",
+ csiphy->id);
if (csiphy->rate_set[i])
- break;
+ continue;
+ }
- if (camss->res->version == CAMSS_660) {
- csiphy->rate_set[i] = csiphy_match_clock_name(clock->name,
- "csi%d_phy", k);
- if (csiphy->rate_set[i])
- break;
- }
+ csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, "csiphy%d", csiphy->id);
+ }
- csiphy->rate_set[i] = csiphy_match_clock_name(clock->name, "csiphy%d", k);
- if (csiphy->rate_set[i])
- break;
- }
+ /* CSIPHY supplies */
+ for (i = 0; i < ARRAY_SIZE(res->regulators); i++) {
+ if (res->regulators[i])
+ csiphy->num_supplies++;
}
- return 0;
+ if (csiphy->num_supplies) {
+ csiphy->supplies = devm_kmalloc_array(camss->dev,
+ csiphy->num_supplies,
+ sizeof(*csiphy->supplies),
+ GFP_KERNEL);
+ if (!csiphy->supplies)
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < csiphy->num_supplies; i++)
+ csiphy->supplies[i].supply = res->regulators[i];
+
+ ret = devm_regulator_bulk_get(camss->dev, csiphy->num_supplies,
+ csiphy->supplies);
+ return ret;
}
/*
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index c9b7fe82b1f0..ab91273303b9 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -26,6 +26,12 @@ struct csiphy_lane {
u8 pol;
};
+/**
+ * struct csiphy_lanes_cfg - CSIPHY lanes configuration
+ * @num_data: number of data lanes
+ * @data: data lanes configuration
+ * @clk: clock lane configuration (only for D-PHY)
+ */
struct csiphy_lanes_cfg {
int num_data;
struct csiphy_lane *data;
@@ -42,6 +48,16 @@ struct csiphy_config {
struct csiphy_csi2_cfg *csi2;
};
+struct csiphy_format_info {
+ u32 code;
+ u8 bpp;
+};
+
+struct csiphy_formats {
+ unsigned int nformats;
+ const struct csiphy_format_info *formats;
+};
+
struct csiphy_device;
struct csiphy_hw_ops {
@@ -61,6 +77,19 @@ struct csiphy_hw_ops {
void (*lanes_disable)(struct csiphy_device *csiphy,
struct csiphy_config *cfg);
irqreturn_t (*isr)(int irq, void *dev);
+ int (*init)(struct csiphy_device *csiphy);
+};
+
+struct csiphy_subdev_resources {
+ u8 id;
+ const struct csiphy_hw_ops *hw_ops;
+ const struct csiphy_formats *formats;
+};
+
+struct csiphy_device_regs {
+ const struct csiphy_lane_regs *lane_regs;
+ int lane_array_size;
+ u32 offset;
};
struct csiphy_device {
@@ -76,11 +105,12 @@ struct csiphy_device {
bool *rate_set;
int nclocks;
u32 timer_clk_rate;
+ struct regulator_bulk_data *supplies;
+ int num_supplies;
struct csiphy_config cfg;
struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM];
- const struct csiphy_hw_ops *ops;
- const struct csiphy_format *formats;
- unsigned int nformats;
+ const struct csiphy_subdev_resources *res;
+ struct csiphy_device_regs *regs;
};
struct camss_subdev_resources;
@@ -94,6 +124,11 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy,
void msm_csiphy_unregister_entity(struct csiphy_device *csiphy);
+extern const struct csiphy_formats csiphy_formats_8x16;
+extern const struct csiphy_formats csiphy_formats_8x96;
+extern const struct csiphy_formats csiphy_formats_sc7280;
+extern const struct csiphy_formats csiphy_formats_sdm845;
+
extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0;
extern const struct csiphy_hw_ops csiphy_ops_3ph_1_0;
diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c
new file mode 100644
index 000000000000..4a3d5549615c
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-format.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-format.c
+ *
+ * Qualcomm MSM Camera Subsystem - Format helpers
+ *
+ * Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+#include <linux/bug.h>
+#include <linux/errno.h>
+
+#include "camss-format.h"
+
+/*
+ * camss_format_get_bpp - Map media bus format to bits per pixel
+ * @formats: supported media bus formats array
+ * @nformats: size of @formats array
+ * @code: media bus format code
+ *
+ * Return number of bits per pixel
+ */
+u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < nformats; i++)
+ if (code == formats[i].code)
+ return formats[i].mbus_bpp;
+
+ WARN(1, "Unknown format\n");
+
+ return formats[0].mbus_bpp;
+}
+
+/*
+ * camss_format_find_code - Find a format code in an array
+ * @code: a pointer to media bus format codes array
+ * @n_code: size of @code array
+ * @index: index of code in the array
+ * @req_code: required code
+ *
+ * Return media bus format code
+ */
+u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code)
+{
+ unsigned int i;
+
+ if (!req_code && index >= n_code)
+ return 0;
+
+ for (i = 0; i < n_code; i++) {
+ if (req_code) {
+ if (req_code == code[i])
+ return req_code;
+ } else {
+ if (i == index)
+ return code[i];
+ }
+ }
+
+ return code[0];
+}
+
+/*
+ * camss_format_find_format - Find a format in an array
+ * @code: media bus format code
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @formats: a pointer to formats array
+ * @nformats: size of @formats array
+ *
+ * Return index of a format or a negative error code otherwise
+ */
+int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats,
+ unsigned int nformats)
+{
+ unsigned int i;
+
+ for (i = 0; i < nformats; i++) {
+ if (formats[i].code == code &&
+ formats[i].pixelformat == pixelformat)
+ return i;
+ }
+
+ for (i = 0; i < nformats; i++) {
+ if (formats[i].code == code)
+ return i;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h
new file mode 100644
index 000000000000..923a48c9c3fb
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-format.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-format.h
+ *
+ * Qualcomm MSM Camera Subsystem - Format helpers
+ *
+ * Copyright (c) 2023, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Technologies, Inc.
+ */
+#ifndef __CAMSS_FORMAT_H__
+#define __CAMSS_FORMAT_H__
+
+#include <linux/types.h>
+
+#define PER_PLANE_DATA(plane, h_fract_num, h_fract_den, v_fract_num, v_fract_den, _bpp) \
+ .hsub[(plane)].numerator = (h_fract_num), \
+ .hsub[(plane)].denominator = (h_fract_den), \
+ .vsub[(plane)].numerator = (v_fract_num), \
+ .vsub[(plane)].denominator = (v_fract_den), \
+ .bpp[(plane)] = (_bpp)
+
+/*
+ * struct fract - Represents a fraction
+ * @numerator: Store the numerator part of the fraction
+ * @denominator: Store the denominator part of the fraction
+ */
+struct fract {
+ u8 numerator;
+ u8 denominator;
+};
+
+/*
+ * struct camss_format_info - ISP media bus format information
+ * @code: V4L2 media bus format code
+ * @mbus_bpp: Media bus bits per pixel
+ * @pixelformat: V4L2 pixel format FCC identifier
+ * @planes: Number of planes
+ * @hsub: Horizontal subsampling (for each plane)
+ * @vsub: Vertical subsampling (for each plane)
+ * @bpp: Bits per pixel when stored in memory (for each plane)
+ */
+struct camss_format_info {
+ u32 code;
+ u32 mbus_bpp;
+ u32 pixelformat;
+ u8 planes;
+ struct fract hsub[3];
+ struct fract vsub[3];
+ unsigned int bpp[3];
+};
+
+struct camss_formats {
+ unsigned int nformats;
+ const struct camss_format_info *formats;
+};
+
+u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code);
+u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code);
+int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats,
+ unsigned int nformats);
+
+#endif /* __CAMSS_FORMAT_H__ */
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index a12dcc7ff438..2dc585c6123d 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -830,6 +830,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
ispif_select_cid(ispif, intf, cid, vfe, 1);
ispif_config_irq(ispif, intf, vfe, 1);
if (camss->res->version == CAMSS_8x96 ||
+ camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_660)
ispif_config_pack(ispif,
line->fmt[MSM_ISPIF_PAD_SINK].code,
@@ -848,6 +849,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
mutex_lock(&ispif->config_lock);
if (camss->res->version == CAMSS_8x96 ||
+ camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_660)
ispif_config_pack(ispif,
line->fmt[MSM_ISPIF_PAD_SINK].code,
@@ -1111,6 +1113,7 @@ int msm_ispif_subdev_init(struct camss *camss,
if (camss->res->version == CAMSS_8x16)
ispif->line_num = 2;
else if (camss->res->version == CAMSS_8x96 ||
+ camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_660)
ispif->line_num = 4;
else
@@ -1130,6 +1133,7 @@ int msm_ispif_subdev_init(struct camss *camss,
ispif->line[i].nformats =
ARRAY_SIZE(ispif_formats_8x16);
} else if (camss->res->version == CAMSS_8x96 ||
+ camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_660) {
ispif->line[i].formats = ispif_formats_8x96;
ispif->line[i].nformats =
@@ -1162,6 +1166,7 @@ int msm_ispif_subdev_init(struct camss *camss,
ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16,
IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
else if (camss->res->version == CAMSS_8x96 ||
+ camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_660)
ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x96,
IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
index 795ac3815339..e5ee7e717b3b 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-170.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-17x.c
@@ -14,8 +14,6 @@
#include "camss.h"
#include "camss-vfe.h"
-#define VFE_HW_VERSION (0x000)
-
#define VFE_GLOBAL_RESET_CMD (0x018)
#define GLOBAL_RESET_CMD_CORE BIT(0)
#define GLOBAL_RESET_CMD_CAMIF BIT(1)
@@ -176,20 +174,6 @@
#define VFE_BUS_WM_FRAME_INC(n) (0x2258 + (n) * 0x100)
#define VFE_BUS_WM_BURST_LIMIT(n) (0x225c + (n) * 0x100)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
-
- u32 gen = (hw_version >> 28) & 0xF;
- u32 rev = (hw_version >> 16) & 0xFFF;
- u32 step = hw_version & 0xFFFF;
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n",
- gen, rev, step);
-
- return hw_version;
-}
-
static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
{
u32 bits = readl_relaxed(vfe->base + reg);
@@ -353,7 +337,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
writel_relaxed(status0, vfe->base + VFE_IRQ_CLEAR_0);
writel_relaxed(status1, vfe->base + VFE_IRQ_CLEAR_1);
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
vfe_bus_status[i] = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(i));
writel_relaxed(vfe_bus_status[i], vfe->base + VFE_BUS_IRQ_CLEAR(i));
}
@@ -367,11 +351,11 @@ static irqreturn_t vfe_isr(int irq, void *dev)
if (status0 & STATUS_0_RESET_ACK)
vfe->isr_ops.reset_ack(vfe);
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++)
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++)
if (status0 & STATUS_0_RDI_REG_UPDATE(i))
vfe->isr_ops.reg_update(vfe, i);
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++)
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++)
if (status0 & STATUS_1_RDI_SOF(i))
vfe->isr_ops.sof(vfe, i);
@@ -438,62 +422,6 @@ error:
return -EINVAL;
}
-static int vfe_enable_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output = &line->output;
- const struct vfe_hw_ops *ops = vfe->ops;
- struct media_entity *sensor;
- unsigned long flags;
- unsigned int frame_skip = 0;
- unsigned int i;
-
- sensor = camss_find_sensor(&line->subdev.entity);
- if (sensor) {
- struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
-
- v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
- /* Max frame skip is 29 frames */
- if (frame_skip > VFE_FRAME_DROP_VAL - 1)
- frame_skip = VFE_FRAME_DROP_VAL - 1;
- }
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- ops->reg_update_clear(vfe, line->id);
-
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
- output->state);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- return -EINVAL;
- }
-
- WARN_ON(output->gen2.active_num);
-
- output->state = VFE_OUTPUT_ON;
-
- output->sequence = 0;
- output->wait_reg_update = 0;
- reinit_completion(&output->reg_update);
-
- vfe_wm_start(vfe, output->wm_idx[0], line);
-
- for (i = 0; i < 2; i++) {
- output->buf[i] = vfe_buf_get_pending(output);
- if (!output->buf[i])
- break;
- output->gen2.active_num++;
- vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line);
- }
-
- ops->reg_update(vfe, line->id);
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
/*
* vfe_enable - Enable streaming on VFE line
* @line: VFE line
@@ -518,7 +446,7 @@ static int vfe_enable(struct vfe_line *line)
if (ret < 0)
goto error_get_output;
- ret = vfe_enable_output(line);
+ ret = vfe_enable_output_v2(line);
if (ret < 0)
goto error_enable_output;
@@ -560,7 +488,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
unsigned long flags;
spin_lock_irqsave(&vfe->output_lock, flags);
- vfe->ops->reg_update_clear(vfe, line_id);
+ vfe->res->hw_ops->reg_update_clear(vfe, line_id);
output = &vfe->line[line_id].output;
@@ -627,40 +555,6 @@ out_unlock:
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
-/*
- * vfe_queue_buffer - Add empty buffer
- * @vid: Video device structure
- * @buf: Buffer to be enqueued
- *
- * Add an empty buffer - depending on the current number of buffers it will be
- * put in pending buffer queue or directly given to the hardware to be filled.
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_queue_buffer(struct camss_video *vid,
- struct camss_buffer *buf)
-{
- struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
-
- output = &line->output;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) {
- output->buf[output->gen2.active_num++] = buf;
- vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line);
- } else {
- vfe_buf_add_pending(output, buf);
- }
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
static const struct vfe_isr_ops vfe_isr_ops_170 = {
.reset_ack = vfe_isr_reset_ack,
.halt_ack = vfe_isr_halt_ack,
@@ -671,7 +565,7 @@ static const struct vfe_isr_ops vfe_isr_ops_170 = {
};
static const struct camss_video_ops vfe_video_ops_170 = {
- .queue_buffer = vfe_queue_buffer,
+ .queue_buffer = vfe_queue_buffer_v2,
.flush_buffers = vfe_flush_buffers,
};
@@ -695,5 +589,7 @@ const struct vfe_hw_ops vfe_ops_170 = {
.vfe_enable = vfe_enable,
.vfe_halt = vfe_halt,
.violation_read = vfe_violation_read,
+ .vfe_wm_start = vfe_wm_start,
.vfe_wm_stop = vfe_wm_stop,
+ .vfe_wm_update = vfe_wm_update,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
index ef6b34c915df..901677293d97 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
@@ -210,15 +210,6 @@
#define MSM_VFE_VFE0_UB_SIZE 1023
#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static u16 vfe_get_ub_size(u8 vfe_id)
{
if (vfe_id == 0)
@@ -892,7 +883,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
u32 value0, value1;
int i, j;
- vfe->ops->isr_read(vfe, &value0, &value1);
+ vfe->res->hw_ops->isr_read(vfe, &value0, &value1);
dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n",
value0, value1);
@@ -901,7 +892,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe->isr_ops.reset_ack(vfe);
if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION)
- vfe->ops->violation_read(vfe);
+ vfe->res->hw_ops->violation_read(vfe);
if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
vfe->isr_ops.halt_ack(vfe);
@@ -938,7 +929,10 @@ static irqreturn_t vfe_isr(int irq, void *dev)
*/
static void vfe_4_1_pm_domain_off(struct vfe_device *vfe)
{
- /* nop */
+ if (!vfe->res->has_pd)
+ return;
+
+ vfe_pm_domain_off(vfe);
}
/*
@@ -947,7 +941,10 @@ static void vfe_4_1_pm_domain_off(struct vfe_device *vfe)
*/
static int vfe_4_1_pm_domain_on(struct vfe_device *vfe)
{
- return 0;
+ if (!vfe->res->has_pd)
+ return 0;
+
+ return vfe_pm_domain_on(vfe);
}
static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = {
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
index 7655d22a9fda..76729607db02 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
@@ -18,8 +18,6 @@
#include "camss-vfe-gen1.h"
-#define VFE_0_HW_VERSION 0x000
-
#define VFE_0_GLOBAL_RESET_CMD 0x018
#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
@@ -254,15 +252,6 @@
#define MSM_VFE_VFE1_UB_SIZE 1535
#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static u16 vfe_get_ub_size(u8 vfe_id)
{
if (vfe_id == 0)
@@ -1050,7 +1039,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
u32 value0, value1;
int i, j;
- vfe->ops->isr_read(vfe, &value0, &value1);
+ vfe->res->hw_ops->isr_read(vfe, &value0, &value1);
dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n",
value0, value1);
@@ -1059,12 +1048,12 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe->isr_ops.reset_ack(vfe);
if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION)
- vfe->ops->violation_read(vfe);
+ vfe->res->hw_ops->violation_read(vfe);
if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
vfe->isr_ops.halt_ack(vfe);
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++)
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++)
if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
vfe->isr_ops.reg_update(vfe, i);
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
index f52fa30f3853..b2f7d855d8dd 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c
@@ -17,8 +17,6 @@
#include "camss-vfe.h"
#include "camss-vfe-gen1.h"
-#define VFE_0_HW_VERSION 0x000
-
#define VFE_0_GLOBAL_RESET_CMD 0x018
#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
@@ -247,15 +245,6 @@
#define MSM_VFE_VFE1_UB_SIZE 1535
#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = 0x%08x\n", hw_version);
-
- return hw_version;
-}
-
static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
{
u32 bits = readl_relaxed(vfe->base + reg);
@@ -980,7 +969,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
u32 value0, value1;
int i, j;
- vfe->ops->isr_read(vfe, &value0, &value1);
+ vfe->res->hw_ops->isr_read(vfe, &value0, &value1);
dev_dbg(vfe->camss->dev, "VFE: status0 = 0x%08x, status1 = 0x%08x\n",
value0, value1);
@@ -989,12 +978,12 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe->isr_ops.reset_ack(vfe);
if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION)
- vfe->ops->violation_read(vfe);
+ vfe->res->hw_ops->violation_read(vfe);
if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
vfe->isr_ops.halt_ack(vfe);
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++)
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++)
if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
vfe->isr_ops.reg_update(vfe, i);
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-480.c b/drivers/media/platform/qcom/camss/camss-vfe-480.c
index dc2735476c82..4feea590a47b 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-480.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-480.c
@@ -15,8 +15,6 @@
#include "camss.h"
#include "camss-vfe.h"
-#define VFE_HW_VERSION (0x00)
-
#define VFE_GLOBAL_RESET_CMD (vfe_is_lite(vfe) ? 0x0c : 0x1c)
#define GLOBAL_RESET_HW_AND_REG (vfe_is_lite(vfe) ? BIT(1) : BIT(0))
@@ -92,19 +90,6 @@ static inline int bus_irq_mask_0_comp_done(struct vfe_device *vfe, int n)
#define MAX_VFE_OUTPUT_LINES 4
-static u32 vfe_hw_version(struct vfe_device *vfe)
-{
- u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
-
- u32 gen = (hw_version >> 28) & 0xF;
- u32 rev = (hw_version >> 16) & 0xFFF;
- u32 step = hw_version & 0xFFFF;
-
- dev_dbg(vfe->camss->dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step);
-
- return hw_version;
-}
-
static void vfe_global_reset(struct vfe_device *vfe)
{
writel_relaxed(IRQ_MASK_0_RESET_ACK, vfe->base + VFE_IRQ_MASK(0));
@@ -167,18 +152,16 @@ static inline void vfe_reg_update_clear(struct vfe_device *vfe,
vfe->reg_update &= ~REG_UPDATE_RDI(vfe, line_id);
}
-static void vfe_enable_irq_common(struct vfe_device *vfe)
-{
- /* enable reset ack IRQ and top BUS status IRQ */
- writel_relaxed(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ,
- vfe->base + VFE_IRQ_MASK(0));
-}
-
-static void vfe_enable_lines_irq(struct vfe_device *vfe)
+static void vfe_enable_irq(struct vfe_device *vfe)
{
int i;
u32 bus_irq_mask = 0;
+ if (!vfe->stream_count)
+ /* enable reset ack IRQ and top BUS status IRQ */
+ writel(IRQ_MASK_0_RESET_ACK | IRQ_MASK_0_BUS_TOP_IRQ,
+ vfe->base + VFE_IRQ_MASK(0));
+
for (i = 0; i < MAX_VFE_OUTPUT_LINES; i++) {
/* Enable IRQ for newly added lines, but also keep already running lines's IRQ */
if (vfe->line[i].output.state == VFE_OUTPUT_RESERVED ||
@@ -188,11 +171,10 @@ static void vfe_enable_lines_irq(struct vfe_device *vfe)
}
}
- writel_relaxed(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0));
+ writel(bus_irq_mask, vfe->base + VFE_BUS_IRQ_MASK(0));
}
static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id);
-static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm);
/*
* vfe_isr - VFE module interrupt handler
@@ -226,7 +208,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
vfe_isr_reg_update(vfe, i);
if (status & BUS_IRQ_MASK_0_COMP_DONE(vfe, RDI_COMP_GROUP(i)))
- vfe_isr_wm_done(vfe, i);
+ vfe_buf_done(vfe, i);
}
}
@@ -245,132 +227,6 @@ static int vfe_halt(struct vfe_device *vfe)
return 0;
}
-static int vfe_get_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- output = &line->output;
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is running\n");
- goto error;
- }
-
- output->wm_num = 1;
-
- /* Correspondence between VFE line number and WM number.
- * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3
- * Note this 1:1 mapping will not work for PIX streams.
- */
- output->wm_idx[0] = line->id;
- vfe->wm_output_map[line->id] = line->id;
-
- output->drop_update_idx = 0;
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-
-error:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- output->state = VFE_OUTPUT_OFF;
-
- return -EINVAL;
-}
-
-static int vfe_enable_output(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output = &line->output;
- unsigned long flags;
- unsigned int i;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- vfe_reg_update_clear(vfe, line->id);
-
- if (output->state > VFE_OUTPUT_RESERVED) {
- dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
- output->state);
- spin_unlock_irqrestore(&vfe->output_lock, flags);
- return -EINVAL;
- }
-
- WARN_ON(output->gen2.active_num);
-
- output->state = VFE_OUTPUT_ON;
-
- output->sequence = 0;
- output->wait_reg_update = 0;
- reinit_completion(&output->reg_update);
-
- vfe_wm_start(vfe, output->wm_idx[0], line);
-
- for (i = 0; i < 2; i++) {
- output->buf[i] = vfe_buf_get_pending(output);
- if (!output->buf[i])
- break;
- output->gen2.active_num++;
- vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line);
- }
-
- vfe_reg_update(vfe, line->id);
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
-}
-
-/*
- * vfe_enable - Enable streaming on VFE line
- * @line: VFE line
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_enable(struct vfe_line *line)
-{
- struct vfe_device *vfe = to_vfe(line);
- int ret;
-
- mutex_lock(&vfe->stream_lock);
-
- if (!vfe->stream_count)
- vfe_enable_irq_common(vfe);
-
- vfe->stream_count++;
-
- vfe_enable_lines_irq(vfe);
-
- mutex_unlock(&vfe->stream_lock);
-
- ret = vfe_get_output(line);
- if (ret < 0)
- goto error_get_output;
-
- ret = vfe_enable_output(line);
- if (ret < 0)
- goto error_enable_output;
-
- vfe->was_streaming = 1;
-
- return 0;
-
-error_enable_output:
- vfe_put_output(line);
-
-error_get_output:
- mutex_lock(&vfe->stream_lock);
-
- vfe->stream_count--;
-
- mutex_unlock(&vfe->stream_lock);
-
- return ret;
-}
-
/*
* vfe_isr_reg_update - Process reg update interrupt
* @vfe: VFE Device
@@ -394,114 +250,48 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
spin_unlock_irqrestore(&vfe->output_lock, flags);
}
-/*
- * vfe_isr_wm_done - Process write master done interrupt
- * @vfe: VFE Device
- * @wm: Write master id
- */
-static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
-{
- struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
- struct camss_buffer *ready_buf;
- struct vfe_output *output;
- unsigned long flags;
- u32 index;
- u64 ts = ktime_get_ns();
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
- dev_err_ratelimited(vfe->camss->dev,
- "Received wm done for unmapped index\n");
- goto out_unlock;
- }
- output = &vfe->line[vfe->wm_output_map[wm]].output;
-
- ready_buf = output->buf[0];
- if (!ready_buf) {
- dev_err_ratelimited(vfe->camss->dev,
- "Missing ready buf %d!\n", output->state);
- goto out_unlock;
- }
-
- ready_buf->vb.vb2_buf.timestamp = ts;
- ready_buf->vb.sequence = output->sequence++;
-
- index = 0;
- output->buf[0] = output->buf[1];
- if (output->buf[0])
- index = 1;
-
- output->buf[index] = vfe_buf_get_pending(output);
-
- if (output->buf[index])
- vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line);
- else
- output->gen2.active_num--;
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
-
- return;
+static const struct camss_video_ops vfe_video_ops_480 = {
+ .queue_buffer = vfe_queue_buffer_v2,
+ .flush_buffers = vfe_flush_buffers,
+};
-out_unlock:
- spin_unlock_irqrestore(&vfe->output_lock, flags);
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->video_ops = vfe_video_ops_480;
}
-/*
- * vfe_queue_buffer - Add empty buffer
- * @vid: Video device structure
- * @buf: Buffer to be enqueued
- *
- * Add an empty buffer - depending on the current number of buffers it will be
- * put in pending buffer queue or directly given to the hardware to be filled.
- *
- * Return 0 on success or a negative error code otherwise
- */
-static int vfe_queue_buffer(struct camss_video *vid,
- struct camss_buffer *buf)
+static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
{
- struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
- struct vfe_device *vfe = to_vfe(line);
- struct vfe_output *output;
- unsigned long flags;
-
- output = &line->output;
-
- spin_lock_irqsave(&vfe->output_lock, flags);
-
- if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) {
- output->buf[output->gen2.active_num++] = buf;
- vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line);
- } else {
- vfe_buf_add_pending(output, buf);
- }
-
- spin_unlock_irqrestore(&vfe->output_lock, flags);
-
- return 0;
+ /* nop */
}
-static const struct camss_video_ops vfe_video_ops_480 = {
- .queue_buffer = vfe_queue_buffer,
- .flush_buffers = vfe_flush_buffers,
-};
+static void vfe_violation_read(struct vfe_device *vfe)
+{
+ /* nop */
+}
-static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+static void vfe_buf_done_480(struct vfe_device *vfe, int port_id)
{
- vfe->video_ops = vfe_video_ops_480;
+ /* nop */
}
const struct vfe_hw_ops vfe_ops_480 = {
+ .enable_irq = vfe_enable_irq,
.global_reset = vfe_global_reset,
.hw_version = vfe_hw_version,
.isr = vfe_isr,
+ .isr_read = vfe_isr_read,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
.pm_domain_off = vfe_pm_domain_off,
.pm_domain_on = vfe_pm_domain_on,
.subdev_init = vfe_subdev_init,
.vfe_disable = vfe_disable,
- .vfe_enable = vfe_enable,
+ .vfe_enable = vfe_enable_v2,
.vfe_halt = vfe_halt,
+ .violation_read = vfe_violation_read,
+ .vfe_wm_start = vfe_wm_start,
.vfe_wm_stop = vfe_wm_stop,
+ .vfe_buf_done = vfe_buf_done_480,
+ .vfe_wm_update = vfe_wm_update,
};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-680.c b/drivers/media/platform/qcom/camss/camss-vfe-680.c
new file mode 100644
index 000000000000..99036e7c1e76
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-680.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-680.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v680
+ *
+ * Copyright (C) 2025 Linaro Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+
+#define VFE_TOP_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x1c : 0x44) + (n) * 4)
+#define VFE_TOP_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x24 : 0x34) + (n) * 4)
+#define VFE_TOP_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x2c : 0x3c) + (n) * 4)
+#define VFE_IRQ1_SOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(2) : BIT(8)) << ((n) * 2))
+#define VFE_IRQ1_EOF(vfe, n) ((vfe_is_lite(vfe) ? BIT(3) : BIT(9)) << ((n) * 2))
+#define VFE_TOP_IRQ_CMD(vfe) (vfe_is_lite(vfe) ? 0x38 : 0x30)
+#define VFE_TOP_IRQ_CMD_GLOBAL_CLEAR BIT(0)
+#define VFE_TOP_DIAG_CONFIG (vfe_is_lite(vfe) ? 0x40 : 0x50)
+
+#define VFE_TOP_DEBUG_11(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xcc)
+#define VFE_TOP_DEBUG_12(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd0)
+#define VFE_TOP_DEBUG_13(vfe) (vfe_is_lite(vfe) ? 0x40 : 0xd4)
+
+#define VFE_BUS_IRQn_MASK(vfe, n) ((vfe_is_lite(vfe) ? 0x218 : 0xc18) + (n) * 4)
+#define VFE_BUS_IRQn_CLEAR(vfe, n) ((vfe_is_lite(vfe) ? 0x220 : 0xc20) + (n) * 4)
+#define VFE_BUS_IRQn_STATUS(vfe, n) ((vfe_is_lite(vfe) ? 0x228 : 0xc28) + (n) * 4)
+#define VFE_BUS_IRQ_GLOBAL_CLEAR(vfe) (vfe_is_lite(vfe) ? 0x230 : 0xc30)
+#define VFE_BUS_WR_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x264 : 0xc64)
+#define VFE_BUS_WR_OVERFLOW_STATUS(vfe) (vfe_is_lite(vfe) ? 0x268 : 0xc68)
+#define VFE_BUS_WR_IMAGE_VIOLATION_STATUS(vfe) (vfe_is_lite(vfe) ? 0x270 : 0xc70)
+
+#define VFE_BUS_WRITE_CLIENT_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x400 : 0xe00) + (c) * 0x100)
+#define VFE_BUS_WRITE_CLIENT_CFG_EN BIT(0)
+#define VFE_BUS_IMAGE_ADDR(vfe, c) ((vfe_is_lite(vfe) ? 0x404 : 0xe04) + (c) * 0x100)
+#define VFE_BUS_FRAME_INCR(vfe, c) ((vfe_is_lite(vfe) ? 0x408 : 0xe08) + (c) * 0x100)
+#define VFE_BUS_IMAGE_CFG0(vfe, c) ((vfe_is_lite(vfe) ? 0x40c : 0xe0c) + (c) * 0x100)
+#define VFE_BUS_IMAGE_CFG0_DATA(h, s) (((h) << 16) | ((s) >> 4))
+#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF)
+
+#define VFE_BUS_IMAGE_CFG1(vfe, c) ((vfe_is_lite(vfe) ? 0x410 : 0xe10) + (c) * 0x100)
+#define VFE_BUS_IMAGE_CFG2(vfe, c) ((vfe_is_lite(vfe) ? 0x414 : 0xe14) + (c) * 0x100)
+#define VFE_BUS_PACKER_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x418 : 0xe18) + (c) * 0x100)
+#define VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x430 : 0xe30) + (c) * 0x100)
+#define VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x434 : 0xe34) + (c) * 0x100)
+#define VFE_BUS_FRAMEDROP_PERIOD(vfe, c) ((vfe_is_lite(vfe) ? 0x438 : 0xe38) + (c) * 0x100)
+#define VFE_BUS_FRAMEDROP_PATTERN(vfe, c) ((vfe_is_lite(vfe) ? 0x43c : 0xe3c) + (c) * 0x100)
+#define VFE_BUS_MMU_PREFETCH_CFG(vfe, c) ((vfe_is_lite(vfe) ? 0x460 : 0xe60) + (c) * 0x100)
+#define VFE_BUS_MMU_PREFETCH_CFG_EN BIT(0)
+#define VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, c) ((vfe_is_lite(vfe) ? 0x464 : 0xe64) + (c) * 0x100)
+#define VFE_BUS_ADDR_STATUS0(vfe, c) ((vfe_is_lite(vfe) ? 0x470 : 0xe70) + (c) * 0x100)
+
+/*
+ * TODO: differentiate the port id based on requested type of RDI, BHIST etc
+ *
+ * IFE write master IDs
+ *
+ * VIDEO_FULL_Y 0
+ * VIDEO_FULL_C 1
+ * VIDEO_DS_4:1 2
+ * VIDEO_DS_16:1 3
+ * DISPLAY_FULL_Y 4
+ * DISPLAY_FULL_C 5
+ * DISPLAY_DS_4:1 6
+ * DISPLAY_DS_16:1 7
+ * FD_Y 8
+ * FD_C 9
+ * PIXEL_RAW 10
+ * STATS_BE0 11
+ * STATS_BHIST0 12
+ * STATS_TINTLESS_BG 13
+ * STATS_AWB_BG 14
+ * STATS_AWB_BFW 15
+ * STATS_BAF 16
+ * STATS_BHIST 17
+ * STATS_RS 18
+ * STATS_IHIST 19
+ * SPARSE_PD 20
+ * PDAF_V2.0_PD_DATA 21
+ * PDAF_V2.0_SAD 22
+ * LCR 23
+ * RDI0 24
+ * RDI1 25
+ * RDI2 26
+ * LTM_STATS 27
+ *
+ * IFE Lite write master IDs
+ *
+ * RDI0 0
+ * RDI1 1
+ * RDI2 2
+ * RDI3 3
+ * GAMMA 4
+ * BE 5
+ */
+
+/* TODO: assign an ENUM in resources and use the provided master
+ * id directly for RDI, STATS, AWB_BG, BHIST.
+ * This macro only works because RDI is all we support right now.
+ */
+#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0 : 24) + (n))
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ /* VFE680 has no global reset, simply report a completion */
+ complete(&vfe->reset_complete);
+}
+
+/*
+ * vfe_isr - VFE module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ return IRQ_HANDLED;
+}
+
+/*
+ * vfe_halt - Trigger halt on VFE module and wait to complete
+ * @vfe: VFE device
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+static int vfe_halt(struct vfe_device *vfe)
+{
+ /* rely on vfe_disable_output() to stop the VFE */
+ return 0;
+}
+
+static void vfe_disable_irq(struct vfe_device *vfe)
+{
+ writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 0));
+ writel(0u, vfe->base + VFE_TOP_IRQn_MASK(vfe, 1));
+ writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 0));
+ writel(0u, vfe->base + VFE_BUS_IRQn_MASK(vfe, 1));
+}
+
+static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr,
+ struct vfe_line *line)
+{
+ u8 wm = RDI_WM(rdi);
+
+ writel(addr, vfe->base + VFE_BUS_IMAGE_ADDR(vfe, wm));
+}
+
+static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line)
+{
+ struct v4l2_pix_format_mplane *pix =
+ &line->video_out.active_fmt.fmt.pix_mp;
+ u32 stride = pix->plane_fmt[0].bytesperline;
+ u32 cfg;
+ u8 wm;
+
+ cfg = VFE_BUS_IMAGE_CFG0_DATA(pix->height, stride);
+ wm = RDI_WM(rdi);
+
+ writel(cfg, vfe->base + VFE_BUS_IMAGE_CFG0(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_IMAGE_CFG1(vfe, wm));
+ writel(stride, vfe->base + VFE_BUS_IMAGE_CFG2(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_PACKER_CFG(vfe, wm));
+
+ /* Set total frame increment value */
+ writel(pix->plane_fmt[0].bytesperline * pix->height,
+ vfe->base + VFE_BUS_FRAME_INCR(vfe, wm));
+
+ /* MMU */
+ writel(VFE_BUS_MMU_PREFETCH_CFG_EN, vfe->base + VFE_BUS_MMU_PREFETCH_CFG(vfe, wm));
+ writel(~0u, vfe->base + VFE_BUS_MMU_PREFETCH_MAX_OFFSET(vfe, wm));
+
+ /* no dropped frames, one irq per frame */
+ writel(1, vfe->base + VFE_BUS_FRAMEDROP_PATTERN(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_FRAMEDROP_PERIOD(vfe, wm));
+ writel(1, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PATTERN(vfe, wm));
+ writel(0, vfe->base + VFE_BUS_IRQ_SUBSAMPLE_PERIOD(vfe, wm));
+
+ /* We don't process IRQs for VFE in RDI mode at the moment */
+ vfe_disable_irq(vfe);
+
+ /* Enable WM */
+ writel(VFE_BUS_WRITE_CLIENT_CFG_EN,
+ vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm));
+
+ dev_dbg(vfe->camss->dev, "RDI%d WM:%d width %d height %d stride %d\n",
+ rdi, wm, pix->width, pix->height, stride);
+}
+
+static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi)
+{
+ u8 wm = RDI_WM(rdi);
+
+ writel(0, vfe->base + VFE_BUS_WRITE_CLIENT_CFG(vfe, wm));
+}
+
+static const struct camss_video_ops vfe_video_ops_680 = {
+ .queue_buffer = vfe_queue_buffer_v2,
+ .flush_buffers = vfe_flush_buffers,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->video_ops = vfe_video_ops_680;
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, false);
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, true);
+}
+
+const struct vfe_hw_ops vfe_ops_680 = {
+ .global_reset = vfe_global_reset,
+ .hw_version = vfe_hw_version,
+ .isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_disable,
+ .vfe_enable = vfe_enable_v2,
+ .vfe_halt = vfe_halt,
+ .vfe_wm_start = vfe_wm_start,
+ .vfe_wm_stop = vfe_wm_stop,
+ .vfe_buf_done = vfe_buf_done,
+ .vfe_wm_update = vfe_wm_update,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-780.c b/drivers/media/platform/qcom/camss/camss-vfe-780.c
new file mode 100644
index 000000000000..b9812d70f91b
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-780.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v780 (SM8550)
+ *
+ * Copyright (c) 2024 Qualcomm Technologies, Inc.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+
+#include "camss.h"
+#include "camss-vfe.h"
+
+#define BUS_REG_BASE (vfe_is_lite(vfe) ? 0x200 : 0xC00)
+
+#define VFE_BUS_WM_CGC_OVERRIDE (BUS_REG_BASE + 0x08)
+#define WM_CGC_OVERRIDE_ALL (0x7FFFFFF)
+
+#define VFE_BUS_WM_TEST_BUS_CTRL (BUS_REG_BASE + 0xDC)
+
+#define VFE_BUS_WM_CFG(n) (BUS_REG_BASE + 0x200 + (n) * 0x100)
+#define WM_CFG_EN BIT(0)
+#define WM_VIR_FRM_EN BIT(1)
+#define WM_CFG_MODE BIT(16)
+#define VFE_BUS_WM_IMAGE_ADDR(n) (BUS_REG_BASE + 0x204 + (n) * 0x100)
+#define VFE_BUS_WM_FRAME_INCR(n) (BUS_REG_BASE + 0x208 + (n) * 0x100)
+#define VFE_BUS_WM_IMAGE_CFG_0(n) (BUS_REG_BASE + 0x20c + (n) * 0x100)
+#define WM_IMAGE_CFG_0_DEFAULT_WIDTH (0xFFFF)
+#define VFE_BUS_WM_IMAGE_CFG_2(n) (BUS_REG_BASE + 0x214 + (n) * 0x100)
+#define WM_IMAGE_CFG_2_DEFAULT_STRIDE (0xFFFF)
+#define VFE_BUS_WM_PACKER_CFG(n) (BUS_REG_BASE + 0x218 + (n) * 0x100)
+
+#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (BUS_REG_BASE + 0x230 + (n) * 0x100)
+#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (BUS_REG_BASE + 0x234 + (n) * 0x100)
+#define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (BUS_REG_BASE + 0x238 + (n) * 0x100)
+#define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (BUS_REG_BASE + 0x23c + (n) * 0x100)
+
+#define VFE_BUS_WM_MMU_PREFETCH_CFG(n) (BUS_REG_BASE + 0x260 + (n) * 0x100)
+#define VFE_BUS_WM_MMU_PREFETCH_MAX_OFFSET(n) (BUS_REG_BASE + 0x264 + (n) * 0x100)
+
+/*
+ * Bus client mapping:
+ *
+ * Full VFE:
+ * 23 = RDI0, 24 = RDI1, 25 = RDI2
+ *
+ * VFE LITE:
+ * 0 = RDI0, 1 = RDI1, 2 = RDI3, 4 = RDI4
+ */
+#define RDI_WM(n) ((vfe_is_lite(vfe) ? 0x0 : 0x17) + (n))
+
+static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
+{
+ struct v4l2_pix_format_mplane *pix =
+ &line->video_out.active_fmt.fmt.pix_mp;
+
+ wm = RDI_WM(wm);
+
+ /* no clock gating at bus input */
+ writel(WM_CGC_OVERRIDE_ALL, vfe->base + VFE_BUS_WM_CGC_OVERRIDE);
+
+ writel(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL);
+
+ writel(ALIGN(pix->plane_fmt[0].bytesperline, 16) * pix->height >> 8,
+ vfe->base + VFE_BUS_WM_FRAME_INCR(wm));
+ writel((WM_IMAGE_CFG_0_DEFAULT_WIDTH & 0xFFFF),
+ vfe->base + VFE_BUS_WM_IMAGE_CFG_0(wm));
+ writel(WM_IMAGE_CFG_2_DEFAULT_STRIDE,
+ vfe->base + VFE_BUS_WM_IMAGE_CFG_2(wm));
+ writel(0, vfe->base + VFE_BUS_WM_PACKER_CFG(wm));
+
+ /* no dropped frames, one irq per frame */
+ writel(0, vfe->base + VFE_BUS_WM_FRAMEDROP_PERIOD(wm));
+ writel(1, vfe->base + VFE_BUS_WM_FRAMEDROP_PATTERN(wm));
+ writel(0, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(wm));
+ writel(1, vfe->base + VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(wm));
+
+ writel(1, vfe->base + VFE_BUS_WM_MMU_PREFETCH_CFG(wm));
+ writel(0xFFFFFFFF, vfe->base + VFE_BUS_WM_MMU_PREFETCH_MAX_OFFSET(wm));
+
+ writel(WM_CFG_EN | WM_CFG_MODE, vfe->base + VFE_BUS_WM_CFG(wm));
+}
+
+static void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
+{
+ wm = RDI_WM(wm);
+ writel(0, vfe->base + VFE_BUS_WM_CFG(wm));
+}
+
+static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
+ struct vfe_line *line)
+{
+ wm = RDI_WM(wm);
+ writel((addr >> 8) & 0xFFFFFFFF, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm));
+
+ dev_dbg(vfe->camss->dev, "wm:%d, image buf addr:0x%x\n",
+ wm, addr);
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, false);
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ int port_id = line_id;
+
+ camss_reg_update(vfe->camss, vfe->id, port_id, true);
+}
+
+static const struct camss_video_ops vfe_video_ops_780 = {
+ .queue_buffer = vfe_queue_buffer_v2,
+ .flush_buffers = vfe_flush_buffers,
+};
+
+static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe)
+{
+ vfe->video_ops = vfe_video_ops_780;
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ vfe_isr_reset_ack(vfe);
+}
+
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ /* nop */
+ return IRQ_HANDLED;
+}
+
+static int vfe_halt(struct vfe_device *vfe)
+{
+ /* rely on vfe_disable_output() to stop the VFE */
+ return 0;
+}
+
+const struct vfe_hw_ops vfe_ops_780 = {
+ .global_reset = vfe_global_reset,
+ .hw_version = vfe_hw_version,
+ .isr = vfe_isr,
+ .pm_domain_off = vfe_pm_domain_off,
+ .pm_domain_on = vfe_pm_domain_on,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
+ .subdev_init = vfe_subdev_init,
+ .vfe_disable = vfe_disable,
+ .vfe_enable = vfe_enable_v2,
+ .vfe_halt = vfe_halt,
+ .vfe_wm_start = vfe_wm_start,
+ .vfe_wm_stop = vfe_wm_stop,
+ .vfe_buf_done = vfe_buf_done,
+ .vfe_wm_update = vfe_wm_update,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
index 239d3d4ac666..d84a375e3318 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c
@@ -37,7 +37,7 @@ static int vfe_disable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
- const struct vfe_hw_ops *ops = vfe->ops;
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
unsigned long flags;
unsigned long time;
unsigned int i;
@@ -162,15 +162,15 @@ static void vfe_output_frame_drop(struct vfe_device *vfe,
vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern);
}
- vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id);
+ vfe->res->hw_ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id);
}
static int vfe_enable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
- const struct vfe_hw_ops *ops = vfe->ops;
- struct media_entity *sensor;
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ struct media_pad *sensor_pad;
unsigned long flags;
unsigned int frame_skip = 0;
unsigned int i;
@@ -180,9 +180,10 @@ static int vfe_enable_output(struct vfe_line *line)
if (!ub_size)
return -EINVAL;
- sensor = camss_find_sensor(&line->subdev.entity);
- if (sensor) {
- struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor);
+ sensor_pad = camss_find_sensor_pad(&line->subdev.entity);
+ if (sensor_pad) {
+ struct v4l2_subdev *subdev =
+ media_entity_to_v4l2_subdev(sensor_pad->entity);
v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
/* Max frame skip is 29 frames */
@@ -545,7 +546,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
unsigned long flags;
spin_lock_irqsave(&vfe->output_lock, flags);
- vfe->ops->reg_update_clear(vfe, line_id);
+ vfe->res->hw_ops->reg_update_clear(vfe, line_id);
output = &line->output;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 2062be668f49..4bca6c3abaff 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -32,139 +32,256 @@
#define SCALER_RATIO_MAX 16
-struct vfe_format {
- u32 code;
- u8 bpp;
+#define VFE_HW_VERSION 0x0
+#define HW_VERSION_STEPPING 0
+#define HW_VERSION_REVISION 16
+#define HW_VERSION_GENERATION 28
+
+static const struct camss_format_info formats_rdi_8x16[] = {
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
};
-static const struct vfe_format formats_rdi_8x16[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, 8 },
- { MEDIA_BUS_FMT_VYUY8_1X16, 8 },
- { MEDIA_BUS_FMT_YUYV8_1X16, 8 },
- { MEDIA_BUS_FMT_YVYU8_1X16, 8 },
- { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
- { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
- { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
- { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
- { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
- { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
- { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
- { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
- { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
- { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
- { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
- { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
- { MEDIA_BUS_FMT_Y10_1X10, 10 },
+static const struct camss_format_info formats_rdi_8x96[] = {
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
};
-static const struct vfe_format formats_pix_8x16[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, 8 },
- { MEDIA_BUS_FMT_VYUY8_1X16, 8 },
- { MEDIA_BUS_FMT_YUYV8_1X16, 8 },
- { MEDIA_BUS_FMT_YVYU8_1X16, 8 },
+static const struct camss_format_info formats_rdi_845[] = {
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8, V4L2_PIX_FMT_SBGGR8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8, V4L2_PIX_FMT_SGBRG8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8, V4L2_PIX_FMT_SGRBG8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8, V4L2_PIX_FMT_SRGGB8, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10, V4L2_PIX_FMT_SBGGR10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10, V4L2_PIX_FMT_SGBRG10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10, V4L2_PIX_FMT_SGRBG10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10, V4L2_PIX_FMT_SRGGB10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_SBGGR10, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12, V4L2_PIX_FMT_SBGGR12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12, V4L2_PIX_FMT_SGBRG12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12, V4L2_PIX_FMT_SGRBG12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12, V4L2_PIX_FMT_SRGGB12P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 12) },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, 14, V4L2_PIX_FMT_SBGGR14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, 14, V4L2_PIX_FMT_SGBRG14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, 14, V4L2_PIX_FMT_SGRBG14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, 14, V4L2_PIX_FMT_SRGGB14P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 14) },
+ { MEDIA_BUS_FMT_Y8_1X8, 8, V4L2_PIX_FMT_GREY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 8) },
+ { MEDIA_BUS_FMT_Y10_1X10, 10, V4L2_PIX_FMT_Y10P, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 10) },
+ { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16, V4L2_PIX_FMT_Y10, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
};
-static const struct vfe_format formats_rdi_8x96[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, 8 },
- { MEDIA_BUS_FMT_VYUY8_1X16, 8 },
- { MEDIA_BUS_FMT_YUYV8_1X16, 8 },
- { MEDIA_BUS_FMT_YVYU8_1X16, 8 },
- { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
- { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
- { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
- { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
- { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
- { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
- { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
- { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
- { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
- { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
- { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
- { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
- { MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
- { MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
- { MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
- { MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
- { MEDIA_BUS_FMT_Y10_1X10, 10 },
- { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
+static const struct camss_format_info formats_pix_8x16[] = {
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
};
-static const struct vfe_format formats_pix_8x96[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, 8 },
- { MEDIA_BUS_FMT_VYUY8_1X16, 8 },
- { MEDIA_BUS_FMT_YUYV8_1X16, 8 },
- { MEDIA_BUS_FMT_YVYU8_1X16, 8 },
+static const struct camss_format_info formats_pix_8x96[] = {
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV12, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1_5X8, 8, V4L2_PIX_FMT_NV21, 1,
+ PER_PLANE_DATA(0, 1, 1, 2, 3, 8) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV16, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_NV61, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 2, 8) },
+ { MEDIA_BUS_FMT_UYVY8_1X16, 8, V4L2_PIX_FMT_UYVY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_VYUY8_1X16, 8, V4L2_PIX_FMT_VYUY, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YUYV8_1X16, 8, V4L2_PIX_FMT_YUYV, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
+ { MEDIA_BUS_FMT_YVYU8_1X16, 8, V4L2_PIX_FMT_YVYU, 1,
+ PER_PLANE_DATA(0, 1, 1, 1, 1, 16) },
};
-static const struct vfe_format formats_rdi_845[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, 8 },
- { MEDIA_BUS_FMT_VYUY8_1X16, 8 },
- { MEDIA_BUS_FMT_YUYV8_1X16, 8 },
- { MEDIA_BUS_FMT_YVYU8_1X16, 8 },
- { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
- { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
- { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
- { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
- { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
- { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
- { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
- { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
- { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
- { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
- { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
- { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
- { MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
- { 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 },
+const struct camss_formats vfe_formats_rdi_8x16 = {
+ .nformats = ARRAY_SIZE(formats_rdi_8x16),
+ .formats = formats_rdi_8x16
};
-/*
- * vfe_get_bpp - map media bus format to bits per pixel
- * @formats: supported media bus formats array
- * @nformats: size of @formats array
- * @code: media bus format code
- *
- * Return number of bits per pixel
- */
-static u8 vfe_get_bpp(const struct vfe_format *formats,
- unsigned int nformats, u32 code)
-{
- unsigned int i;
-
- for (i = 0; i < nformats; i++)
- if (code == formats[i].code)
- return formats[i].bpp;
-
- WARN(1, "Unknown format\n");
-
- return formats[0].bpp;
-}
+const struct camss_formats vfe_formats_pix_8x16 = {
+ .nformats = ARRAY_SIZE(formats_pix_8x16),
+ .formats = formats_pix_8x16
+};
-static u32 vfe_find_code(u32 *code, unsigned int n_code,
- unsigned int index, u32 req_code)
-{
- int i;
+const struct camss_formats vfe_formats_rdi_8x96 = {
+ .nformats = ARRAY_SIZE(formats_rdi_8x96),
+ .formats = formats_rdi_8x96
+};
- if (!req_code && (index >= n_code))
- return 0;
+const struct camss_formats vfe_formats_pix_8x96 = {
+ .nformats = ARRAY_SIZE(formats_pix_8x96),
+ .formats = formats_pix_8x96
+};
- for (i = 0; i < n_code; i++)
- if (req_code) {
- if (req_code == code[i])
- return req_code;
- } else {
- if (i == index)
- return code[i];
- }
+const struct camss_formats vfe_formats_rdi_845 = {
+ .nformats = ARRAY_SIZE(formats_rdi_845),
+ .formats = formats_rdi_845
+};
- return code[0];
-}
+/* TODO: Replace with pix formats */
+const struct camss_formats vfe_formats_pix_845 = {
+ .nformats = ARRAY_SIZE(formats_rdi_845),
+ .formats = formats_rdi_845
+};
static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
unsigned int index, u32 src_req_code)
@@ -173,6 +290,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
switch (vfe->camss->res->version) {
case CAMSS_8x16:
+ case CAMSS_8x53:
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
{
@@ -181,8 +299,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_YUYV8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
case MEDIA_BUS_FMT_YVYU8_1X16:
{
@@ -191,8 +309,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_YVYU8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
case MEDIA_BUS_FMT_UYVY8_1X16:
{
@@ -201,8 +319,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_UYVY8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
case MEDIA_BUS_FMT_VYUY8_1X16:
{
@@ -211,8 +329,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_VYUY8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
default:
if (index > 0)
@@ -221,10 +339,14 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
return sink_code;
}
break;
- case CAMSS_8x96:
case CAMSS_660:
- case CAMSS_845:
+ case CAMSS_7280:
+ case CAMSS_8x96:
case CAMSS_8250:
+ case CAMSS_8280XP:
+ case CAMSS_845:
+ case CAMSS_8550:
+ case CAMSS_X1E80100:
switch (sink_code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
{
@@ -236,8 +358,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_YUYV8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
case MEDIA_BUS_FMT_YVYU8_1X16:
{
@@ -249,8 +371,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_YVYU8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
case MEDIA_BUS_FMT_UYVY8_1X16:
{
@@ -262,8 +384,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_UYVY8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
case MEDIA_BUS_FMT_VYUY8_1X16:
{
@@ -275,8 +397,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
MEDIA_BUS_FMT_VYUY8_1_5X8,
};
- return vfe_find_code(src_code, ARRAY_SIZE(src_code),
- index, src_req_code);
+ return camss_format_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
}
default:
if (index > 0)
@@ -285,17 +407,285 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
return sink_code;
}
break;
+ default:
+ WARN(1, "Unsupported HW version: %x\n",
+ vfe->camss->res->version);
+ break;
+ }
+ return 0;
+}
+
+/*
+ * vfe_hw_version - Process write master done interrupt
+ * @vfe: VFE Device
+ *
+ * Return vfe hw version
+ */
+u32 vfe_hw_version(struct vfe_device *vfe)
+{
+ u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION);
+
+ u32 gen = (hw_version >> HW_VERSION_GENERATION) & 0xF;
+ u32 rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF;
+ u32 step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF;
+
+ dev_dbg(vfe->camss->dev, "VFE:%d HW Version = %u.%u.%u\n",
+ vfe->id, gen, rev, step);
+
+ return hw_version;
+}
+
+/*
+ * vfe_buf_done - Process write master done interrupt
+ * @vfe: VFE Device
+ * @wm: Write master id
+ */
+void vfe_buf_done(struct vfe_device *vfe, int wm)
+{
+ struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]];
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ struct camss_buffer *ready_buf;
+ struct vfe_output *output;
+ unsigned long flags;
+ u32 index;
+ u64 ts = ktime_get_ns();
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Received wm done for unmapped index\n");
+ goto out_unlock;
+ }
+ output = &vfe->line[vfe->wm_output_map[wm]].output;
+
+ ready_buf = output->buf[0];
+ if (!ready_buf) {
+ dev_err_ratelimited(vfe->camss->dev,
+ "Missing ready buf %d!\n", output->state);
+ goto out_unlock;
+ }
+
+ ready_buf->vb.vb2_buf.timestamp = ts;
+ ready_buf->vb.sequence = output->sequence++;
+
+ index = 0;
+ output->buf[0] = output->buf[1];
+ if (output->buf[0])
+ index = 1;
+
+ output->buf[index] = vfe_buf_get_pending(output);
+
+ if (output->buf[index]) {
+ ops->vfe_wm_update(vfe, output->wm_idx[0],
+ output->buf[index]->addr[0],
+ line);
+ ops->reg_update(vfe, line->id);
+ } else {
+ output->gen2.active_num--;
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+
+ return;
+
+out_unlock:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+}
+
+int vfe_enable_output_v2(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output = &line->output;
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ struct media_pad *sensor_pad;
+ unsigned long flags;
+ unsigned int frame_skip = 0;
+ unsigned int i;
+
+ sensor_pad = camss_find_sensor_pad(&line->subdev.entity);
+ if (sensor_pad) {
+ struct v4l2_subdev *subdev =
+ media_entity_to_v4l2_subdev(sensor_pad->entity);
+
+ v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip);
+ /* Max frame skip is 29 frames */
+ if (frame_skip > VFE_FRAME_DROP_VAL - 1)
+ frame_skip = VFE_FRAME_DROP_VAL - 1;
+ }
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ ops->reg_update_clear(vfe, line->id);
+
+ if (output->state > VFE_OUTPUT_RESERVED) {
+ dev_err(vfe->camss->dev,
+ "Output is not in reserved state %d\n",
+ output->state);
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ return -EINVAL;
+ }
+
+ WARN_ON(output->gen2.active_num);
+
+ output->state = VFE_OUTPUT_ON;
+
+ output->sequence = 0;
+ output->wait_reg_update = 0;
+ reinit_completion(&output->reg_update);
+
+ ops->vfe_wm_start(vfe, output->wm_idx[0], line);
+
+ for (i = 0; i < 2; i++) {
+ output->buf[i] = vfe_buf_get_pending(output);
+ if (!output->buf[i])
+ break;
+ output->gen2.active_num++;
+ ops->vfe_wm_update(vfe, output->wm_idx[0],
+ output->buf[i]->addr[0], line);
+ ops->reg_update(vfe, line->id);
+ }
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+}
+
+/*
+ * vfe_queue_buffer_v2 - Add empty buffer
+ * @vid: Video device structure
+ * @buf: Buffer to be enqueued
+ *
+ * Add an empty buffer - depending on the current number of buffers it will be
+ * put in pending buffer queue or directly given to the hardware to be filled.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_queue_buffer_v2(struct camss_video *vid,
+ struct camss_buffer *buf)
+{
+ struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
+ struct vfe_device *vfe = to_vfe(line);
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ struct vfe_output *output;
+ unsigned long flags;
+
+ output = &line->output;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ if (output->state == VFE_OUTPUT_ON &&
+ output->gen2.active_num < 2) {
+ output->buf[output->gen2.active_num++] = buf;
+ ops->vfe_wm_update(vfe, output->wm_idx[0],
+ buf->addr[0], line);
+ ops->reg_update(vfe, line->id);
+ } else {
+ vfe_buf_add_pending(output, buf);
}
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
return 0;
}
+/*
+ * vfe_enable_v2 - Enable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_enable_v2(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ const struct vfe_hw_ops *ops = vfe->res->hw_ops;
+ int ret;
+
+ mutex_lock(&vfe->stream_lock);
+
+ if (vfe->res->hw_ops->enable_irq)
+ ops->enable_irq(vfe);
+
+ vfe->stream_count++;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ ret = vfe_get_output_v2(line);
+ if (ret < 0)
+ goto error_get_output;
+
+ ret = vfe_enable_output_v2(line);
+ if (ret < 0)
+ goto error_enable_output;
+
+ vfe->was_streaming = 1;
+
+ return 0;
+
+error_enable_output:
+ vfe_put_output(line);
+
+error_get_output:
+ mutex_lock(&vfe->stream_lock);
+
+ vfe->stream_count--;
+
+ mutex_unlock(&vfe->stream_lock);
+
+ return ret;
+}
+
+/*
+ * vfe_get_output_v2 - Get vfe output port for corresponding VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_get_output_v2(struct vfe_line *line)
+{
+ struct vfe_device *vfe = to_vfe(line);
+ struct vfe_output *output;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vfe->output_lock, flags);
+
+ output = &line->output;
+ if (output->state > VFE_OUTPUT_RESERVED) {
+ dev_err(vfe->camss->dev, "Output is running\n");
+ goto error;
+ }
+
+ output->wm_num = 1;
+
+ /* Correspondence between VFE line number and WM number.
+ * line 0 -> RDI 0, line 1 -> RDI1, line 2 -> RDI2, line 3 -> PIX/RDI3
+ * Note this 1:1 mapping will not work for PIX streams.
+ */
+ output->wm_idx[0] = line->id;
+ vfe->wm_output_map[line->id] = line->id;
+
+ output->drop_update_idx = 0;
+
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+
+ return 0;
+
+error:
+ spin_unlock_irqrestore(&vfe->output_lock, flags);
+ output->state = VFE_OUTPUT_OFF;
+
+ return -EINVAL;
+}
+
int vfe_reset(struct vfe_device *vfe)
{
unsigned long time;
reinit_completion(&vfe->reset_complete);
- vfe->ops->global_reset(vfe);
+ vfe->res->hw_ops->global_reset(vfe);
time = wait_for_completion_timeout(&vfe->reset_complete,
msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
@@ -311,7 +701,7 @@ static void vfe_init_outputs(struct vfe_device *vfe)
{
int i;
- for (i = 0; i < vfe->line_num; i++) {
+ for (i = 0; i < vfe->res->line_num; i++) {
struct vfe_output *output = &vfe->line[i].output;
output->state = VFE_OUTPUT_OFF;
@@ -420,7 +810,7 @@ static int vfe_disable_output(struct vfe_line *line)
spin_lock_irqsave(&vfe->output_lock, flags);
for (i = 0; i < output->wm_num; i++)
- vfe->ops->vfe_wm_stop(vfe, output->wm_idx[i]);
+ vfe->res->hw_ops->vfe_wm_stop(vfe, output->wm_idx[i]);
output->gen2.active_num = 0;
spin_unlock_irqrestore(&vfe->output_lock, flags);
@@ -536,7 +926,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
int i, j;
int ret;
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
&pixel_clock[i]);
if (ret)
@@ -550,7 +940,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
u64 min_rate = 0;
long rate;
- for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
+ for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) {
u32 tmp;
u8 bpp;
@@ -559,9 +949,9 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
} else {
struct vfe_line *l = &vfe->line[j];
- bpp = vfe_get_bpp(l->formats,
- l->nformats,
- l->fmt[MSM_VFE_PAD_SINK].code);
+ bpp = camss_format_get_bpp(l->formats,
+ l->nformats,
+ l->fmt[MSM_VFE_PAD_SINK].code);
tmp = pixel_clock[j] * bpp / 64;
}
@@ -617,7 +1007,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
int i, j;
int ret;
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity,
&pixel_clock[i]);
if (ret)
@@ -631,7 +1021,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
u64 min_rate = 0;
unsigned long rate;
- for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) {
+ for (j = VFE_LINE_RDI0; j < vfe->res->line_num; j++) {
u32 tmp;
u8 bpp;
@@ -640,9 +1030,9 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
} else {
struct vfe_line *l = &vfe->line[j];
- bpp = vfe_get_bpp(l->formats,
- l->nformats,
- l->fmt[MSM_VFE_PAD_SINK].code);
+ bpp = camss_format_get_bpp(l->formats,
+ l->nformats,
+ l->fmt[MSM_VFE_PAD_SINK].code);
tmp = pixel_clock[j] * bpp / 64;
}
@@ -674,7 +1064,7 @@ int vfe_get(struct vfe_device *vfe)
mutex_lock(&vfe->power_lock);
if (vfe->power_count == 0) {
- ret = vfe->ops->pm_domain_on(vfe);
+ ret = vfe->res->hw_ops->pm_domain_on(vfe);
if (ret < 0)
goto error_pm_domain;
@@ -699,7 +1089,7 @@ int vfe_get(struct vfe_device *vfe)
vfe_init_outputs(vfe);
- vfe->ops->hw_version(vfe);
+ vfe->res->hw_ops->hw_version(vfe);
} else {
ret = vfe_check_clock_rates(vfe);
if (ret < 0)
@@ -717,7 +1107,7 @@ error_reset:
error_pm_runtime_get:
pm_runtime_put_sync(vfe->camss->dev);
error_domain_off:
- vfe->ops->pm_domain_off(vfe);
+ vfe->res->hw_ops->pm_domain_off(vfe);
error_pm_domain:
mutex_unlock(&vfe->power_lock);
@@ -739,11 +1129,11 @@ void vfe_put(struct vfe_device *vfe)
} else if (vfe->power_count == 1) {
if (vfe->was_streaming) {
vfe->was_streaming = 0;
- vfe->ops->vfe_halt(vfe);
+ vfe->res->hw_ops->vfe_halt(vfe);
}
camss_disable_clocks(vfe->nclocks, vfe->clock);
pm_runtime_put_sync(vfe->camss->dev);
- vfe->ops->pm_domain_off(vfe);
+ vfe->res->hw_ops->pm_domain_off(vfe);
}
vfe->power_count--;
@@ -833,12 +1223,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
if (enable) {
line->output.state = VFE_OUTPUT_RESERVED;
- ret = vfe->ops->vfe_enable(line);
+ ret = vfe->res->hw_ops->vfe_enable(line);
if (ret < 0)
dev_err(vfe->camss->dev,
"Failed to enable vfe outputs\n");
} else {
- ret = vfe->ops->vfe_disable(line);
+ ret = vfe->res->hw_ops->vfe_disable(line);
if (ret < 0)
dev_err(vfe->camss->dev,
"Failed to disable vfe outputs\n");
@@ -1375,23 +1765,24 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
int i, j;
int ret;
- vfe->ops = res->ops;
-
- if (!res->line_num)
+ if (!res->vfe.line_num)
return -EINVAL;
+ vfe->res = &res->vfe;
+ vfe->res->hw_ops->subdev_init(dev, vfe);
+
/* Power domain */
- if (res->pd_name) {
+ if (res->vfe.pd_name) {
vfe->genpd = dev_pm_domain_attach_by_name(camss->dev,
- res->pd_name);
+ res->vfe.pd_name);
if (IS_ERR(vfe->genpd)) {
ret = PTR_ERR(vfe->genpd);
return ret;
}
}
- if (!vfe->genpd && res->has_pd) {
+ if (!vfe->genpd && res->vfe.has_pd) {
/*
* Legacy magic index.
* Requires
@@ -1408,9 +1799,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
return PTR_ERR(vfe->genpd);
}
- vfe->line_num = res->line_num;
- vfe->ops->subdev_init(dev, vfe);
-
/* Memory */
vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
@@ -1428,7 +1816,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
vfe->irq = ret;
snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
dev_name(dev), MSM_VFE_NAME, id);
- ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr,
+ ret = devm_request_irq(dev, vfe->irq, vfe->res->hw_ops->isr,
IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
if (ret < 0) {
dev_err(dev, "request_irq failed: %d\n", ret);
@@ -1487,7 +1875,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
vfe->id = id;
vfe->reg_update = 0;
- for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) {
+ for (i = VFE_LINE_RDI0; i < vfe->res->line_num; i++) {
struct vfe_line *l = &vfe->line[i];
l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
@@ -1496,31 +1884,12 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
init_completion(&l->output.sof);
init_completion(&l->output.reg_update);
- switch (camss->res->version) {
- case CAMSS_8x16:
- if (i == VFE_LINE_PIX) {
- l->formats = formats_pix_8x16;
- l->nformats = ARRAY_SIZE(formats_pix_8x16);
- } else {
- l->formats = formats_rdi_8x16;
- l->nformats = ARRAY_SIZE(formats_rdi_8x16);
- }
- break;
- case CAMSS_8x96:
- case CAMSS_660:
- if (i == VFE_LINE_PIX) {
- l->formats = formats_pix_8x96;
- l->nformats = ARRAY_SIZE(formats_pix_8x96);
- } else {
- l->formats = formats_rdi_8x96;
- l->nformats = ARRAY_SIZE(formats_rdi_8x96);
- }
- break;
- case CAMSS_845:
- case CAMSS_8250:
- l->formats = formats_rdi_845;
- l->nformats = ARRAY_SIZE(formats_rdi_845);
- break;
+ if (i == VFE_LINE_PIX) {
+ l->nformats = res->vfe.formats_pix->nformats;
+ l->formats = res->vfe.formats_pix->formats;
+ } else {
+ l->nformats = res->vfe.formats_rdi->nformats;
+ l->formats = res->vfe.formats_rdi->formats;
}
}
@@ -1595,6 +1964,26 @@ static const struct media_entity_operations vfe_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
+static int vfe_bpl_align(struct vfe_device *vfe)
+{
+ int ret = 8;
+
+ switch (vfe->camss->res->version) {
+ case CAMSS_7280:
+ case CAMSS_8250:
+ case CAMSS_8280XP:
+ case CAMSS_845:
+ case CAMSS_8550:
+ case CAMSS_X1E80100:
+ ret = 16;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
/*
* msm_vfe_register_entities - Register subdev node for VFE module
* @vfe: VFE device
@@ -1617,7 +2006,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
int ret;
int i;
- for (i = 0; i < vfe->line_num; i++) {
+ for (i = 0; i < vfe->res->line_num; i++) {
char name[32];
sd = &vfe->line[i].subdev;
@@ -1661,20 +2050,19 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
}
video_out->ops = &vfe->video_ops;
- if (vfe->camss->res->version == CAMSS_845 ||
- vfe->camss->res->version == CAMSS_8250)
- video_out->bpl_alignment = 16;
- else
- video_out->bpl_alignment = 8;
+ video_out->bpl_alignment = vfe_bpl_align(vfe);
video_out->line_based = 0;
if (i == VFE_LINE_PIX) {
video_out->bpl_alignment = 16;
video_out->line_based = 1;
}
+
+ video_out->nformats = vfe->line[i].nformats;
+ video_out->formats = vfe->line[i].formats;
+
snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d",
MSM_VFE_NAME, vfe->id, "video", i);
- ret = msm_video_register(video_out, v4l2_dev, name,
- i == VFE_LINE_PIX ? 1 : 0);
+ ret = msm_video_register(video_out, v4l2_dev, name);
if (ret < 0) {
dev_err(dev, "Failed to register video node: %d\n",
ret);
@@ -1728,7 +2116,7 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe)
mutex_destroy(&vfe->power_lock);
mutex_destroy(&vfe->stream_lock);
- for (i = 0; i < vfe->line_num; i++) {
+ for (i = 0; i < vfe->res->line_num; i++) {
struct v4l2_subdev *sd = &vfe->line[i].subdev;
struct camss_video *video_out = &vfe->line[i].video_out;
@@ -1740,5 +2128,5 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe)
bool vfe_is_lite(struct vfe_device *vfe)
{
- return vfe->camss->res->vfe_res[vfe->id].is_lite;
+ return vfe->camss->res->vfe_res[vfe->id].vfe.is_lite;
}
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
index 0572c9b08e11..a23f666be753 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.h
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -92,14 +92,14 @@ struct vfe_line {
struct v4l2_rect crop;
struct camss_video video_out;
struct vfe_output output;
- const struct vfe_format *formats;
+ const struct camss_format_info *formats;
unsigned int nformats;
};
struct vfe_device;
struct vfe_hw_ops {
- void (*enable_irq_common)(struct vfe_device *vfe);
+ void (*enable_irq)(struct vfe_device *vfe);
void (*global_reset)(struct vfe_device *vfe);
u32 (*hw_version)(struct vfe_device *vfe);
irqreturn_t (*isr)(int irq, void *dev);
@@ -114,7 +114,12 @@ struct vfe_hw_ops {
int (*vfe_enable)(struct vfe_line *line);
int (*vfe_halt)(struct vfe_device *vfe);
void (*violation_read)(struct vfe_device *vfe);
+ void (*vfe_wm_start)(struct vfe_device *vfe, u8 wm,
+ struct vfe_line *line);
void (*vfe_wm_stop)(struct vfe_device *vfe, u8 wm);
+ void (*vfe_buf_done)(struct vfe_device *vfe, int port_id);
+ void (*vfe_wm_update)(struct vfe_device *vfe, u8 wm, u32 addr,
+ struct vfe_line *line);
};
struct vfe_isr_ops {
@@ -126,6 +131,16 @@ struct vfe_isr_ops {
void (*wm_done)(struct vfe_device *vfe, u8 wm);
};
+struct vfe_subdev_resources {
+ bool is_lite;
+ u8 line_num;
+ bool has_pd;
+ char *pd_name;
+ const struct vfe_hw_ops *hw_ops;
+ const struct camss_formats *formats_rdi;
+ const struct camss_formats *formats_pix;
+};
+
struct vfe_device {
struct camss *camss;
u8 id;
@@ -143,10 +158,9 @@ struct vfe_device {
spinlock_t output_lock;
enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
struct vfe_line line[VFE_LINE_NUM_MAX];
- u8 line_num;
u32 reg_update;
u8 was_streaming;
- const struct vfe_hw_ops *ops;
+ const struct vfe_subdev_resources *res;
const struct vfe_hw_ops_gen1 *ops_gen1;
struct vfe_isr_ops isr_ops;
struct camss_video_ops video_ops;
@@ -217,11 +231,20 @@ void vfe_pm_domain_off(struct vfe_device *vfe);
*/
int vfe_pm_domain_on(struct vfe_device *vfe);
+extern const struct camss_formats vfe_formats_rdi_8x16;
+extern const struct camss_formats vfe_formats_pix_8x16;
+extern const struct camss_formats vfe_formats_rdi_8x96;
+extern const struct camss_formats vfe_formats_pix_8x96;
+extern const struct camss_formats vfe_formats_rdi_845;
+extern const struct camss_formats vfe_formats_pix_845;
+
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;
+extern const struct vfe_hw_ops vfe_ops_680;
+extern const struct vfe_hw_ops vfe_ops_780;
int vfe_get(struct vfe_device *vfe);
void vfe_put(struct vfe_device *vfe);
@@ -236,4 +259,55 @@ void vfe_put(struct vfe_device *vfe);
*/
bool vfe_is_lite(struct vfe_device *vfe);
+/*
+ * vfe_hw_version - Process write master done interrupt
+ * @vfe: VFE Device
+ *
+ * Return vfe hw version
+ */
+u32 vfe_hw_version(struct vfe_device *vfe);
+/*
+ * vfe_enable - Enable streaming on VFE line
+ * @line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_enable_v2(struct vfe_line *line);
+
+/*
+ * vfe_buf_done - Process write master done interrupt
+ * @vfe: VFE Device
+ * @wm: Write master id
+ */
+void vfe_buf_done(struct vfe_device *vfe, int wm);
+
+/*
+ * vfe_get_output_v2 - Get vfe output line
+ * line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_get_output_v2(struct vfe_line *line);
+
+/*
+ * vfe_enable_output_v2 - Enable vfe output line
+ * line: VFE line
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_enable_output_v2(struct vfe_line *line);
+
+/*
+ * vfe_queue_buffer_v2 - Add empty buffer
+ * @vid: Video device structure
+ * @buf: Buffer to be enqueued
+ *
+ * Add an empty buffer - depending on the current number of buffers it will be
+ * put in pending buffer queue or directly given to the hardware to be filled.
+ *
+ * Return 0 on success or a negative error code otherwise
+ */
+int vfe_queue_buffer_v2(struct camss_video *vid,
+ struct camss_buffer *buf);
+
#endif /* QC_MSM_CAMSS_VFE_H */
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index a89da5ef4710..aa021fd5e123 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -24,269 +24,10 @@
#define CAMSS_FRAME_MAX_HEIGHT_RDI 8191
#define CAMSS_FRAME_MAX_HEIGHT_PIX 4096
-struct fract {
- u8 numerator;
- u8 denominator;
-};
-
-/*
- * struct camss_format_info - ISP media bus format information
- * @code: V4L2 media bus format code
- * @pixelformat: V4L2 pixel format FCC identifier
- * @planes: Number of planes
- * @hsub: Horizontal subsampling (for each plane)
- * @vsub: Vertical subsampling (for each plane)
- * @bpp: Bits per pixel when stored in memory (for each plane)
- */
-struct camss_format_info {
- u32 code;
- u32 pixelformat;
- u8 planes;
- struct fract hsub[3];
- struct fract vsub[3];
- unsigned int bpp[3];
-};
-
-static const struct camss_format_info formats_rdi_8x16[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
-};
-
-static const struct camss_format_info formats_rdi_8x96[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 14 } },
- { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 14 } },
- { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 14 } },
- { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 14 } },
- { 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,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
-};
-
-static const struct camss_format_info formats_rdi_845[] = {
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 8 } },
- { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 10 } },
- { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 12 } },
- { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 14 } },
- { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 14 } },
- { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1,
- { { 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,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
-};
-
-static const struct camss_format_info formats_pix_8x16[] = {
- { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
-};
-
-static const struct camss_format_info formats_pix_8x96[] = {
- { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
- { { 1, 1 } }, { { 2, 3 } }, { 8 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV16, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_NV61, 1,
- { { 1, 1 } }, { { 1, 2 } }, { 8 } },
- { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_PIX_FMT_UYVY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_VYUY8_1X16, V4L2_PIX_FMT_VYUY, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_PIX_FMT_YUYV, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
- { MEDIA_BUS_FMT_YVYU8_1X16, V4L2_PIX_FMT_YVYU, 1,
- { { 1, 1 } }, { { 1, 1 } }, { 16 } },
-};
-
/* -----------------------------------------------------------------------------
* Helper functions
*/
-static int video_find_format(u32 code, u32 pixelformat,
- const struct camss_format_info *formats,
- unsigned int nformats)
-{
- int i;
-
- for (i = 0; i < nformats; i++) {
- if (formats[i].code == code &&
- formats[i].pixelformat == pixelformat)
- return i;
- }
-
- for (i = 0; i < nformats; i++)
- if (formats[i].code == code)
- return i;
-
- WARN_ON(1);
-
- return -EINVAL;
-}
-
/*
* video_mbus_to_pix_mp - Convert v4l2_mbus_framefmt to v4l2_pix_format_mplane
* @mbus: v4l2_mbus_framefmt format (input)
@@ -359,9 +100,8 @@ static int video_get_subdev_format(struct camss_video *video,
if (ret)
return ret;
- ret = video_find_format(fmt.format.code,
- format->fmt.pix_mp.pixelformat,
- video->formats, video->nformats);
+ ret = camss_format_find_format(fmt.format.code, format->fmt.pix_mp.pixelformat,
+ video->formats, video->nformats);
if (ret < 0)
return ret;
@@ -557,12 +297,6 @@ static void video_stop_streaming(struct vb2_queue *q)
ret = v4l2_subdev_call(subdev, video, s_stream, 0);
- if (entity->use_count > 1) {
- /* Don't stop if other instances of the pipeline are still running */
- dev_dbg(video->camss->dev, "Video pipeline still used, don't stop streaming.\n");
- return;
- }
-
if (ret) {
dev_err(video->camss->dev, "Video pipeline stop failed: %d\n", ret);
return;
@@ -576,8 +310,6 @@ static void video_stop_streaming(struct vb2_queue *q)
static const struct vb2_ops msm_video_vb2_q_ops = {
.queue_setup = video_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_init = video_buf_init,
.buf_prepare = video_buf_prepare,
.buf_queue = video_buf_queue,
@@ -969,7 +701,7 @@ static int msm_video_init_format(struct camss_video *video)
*/
int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
- const char *name, int is_pix)
+ const char *name)
{
struct media_pad *pad = &video->pad;
struct video_device *vdev;
@@ -1006,33 +738,6 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
mutex_init(&video->lock);
- switch (video->camss->res->version) {
- case CAMSS_8x16:
- if (is_pix) {
- video->formats = formats_pix_8x16;
- video->nformats = ARRAY_SIZE(formats_pix_8x16);
- } else {
- video->formats = formats_rdi_8x16;
- video->nformats = ARRAY_SIZE(formats_rdi_8x16);
- }
- break;
- case CAMSS_8x96:
- case CAMSS_660:
- if (is_pix) {
- video->formats = formats_pix_8x96;
- video->nformats = ARRAY_SIZE(formats_pix_8x96);
- } else {
- video->formats = formats_rdi_8x96;
- video->nformats = ARRAY_SIZE(formats_rdi_8x96);
- }
- break;
- case CAMSS_845:
- case CAMSS_8250:
- video->formats = formats_rdi_845;
- video->nformats = ARRAY_SIZE(formats_rdi_845);
- break;
- }
-
ret = msm_video_init_format(video);
if (ret < 0) {
dev_err(v4l2_dev->dev, "Failed to init format: %d\n", ret);
diff --git a/drivers/media/platform/qcom/camss/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h
index bdbae8424140..d3e56e240a88 100644
--- a/drivers/media/platform/qcom/camss/camss-video.h
+++ b/drivers/media/platform/qcom/camss/camss-video.h
@@ -33,8 +33,6 @@ struct camss_video_ops {
enum vb2_buffer_state state);
};
-struct camss_format_info;
-
struct camss_video {
struct camss *camss;
struct vb2_queue vb2_q;
@@ -53,7 +51,7 @@ struct camss_video {
};
int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
- const char *name, int is_pix);
+ const char *name);
void msm_video_unregister(struct camss_video *video);
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 58f4be660290..06f42875702f 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -32,6 +32,8 @@
#define CAMSS_CLOCK_MARGIN_NUMERATOR 105
#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100
+static const struct parent_dev_ops vfe_parent_dev_ops;
+
static const struct camss_subdev_resources csiphy_res_8x16[] = {
/* CSIPHY0 */
{
@@ -43,7 +45,11 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = {
{ 100000000, 200000000 } },
.reg = { "csiphy0", "csiphy0_clk_mux" },
.interrupt = { "csiphy0" },
- .ops = &csiphy_ops_2ph_1_0
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_2ph_1_0,
+ .formats = &csiphy_formats_8x16
+ }
},
/* CSIPHY1 */
@@ -56,7 +62,11 @@ static const struct camss_subdev_resources csiphy_res_8x16[] = {
{ 100000000, 200000000 } },
.reg = { "csiphy1", "csiphy1_clk_mux" },
.interrupt = { "csiphy1" },
- .ops = &csiphy_ops_2ph_1_0
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_2ph_1_0,
+ .formats = &csiphy_formats_8x16
+ }
}
};
@@ -76,7 +86,11 @@ static const struct camss_subdev_resources csid_res_8x16[] = {
{ 0 } },
.reg = { "csid0" },
.interrupt = { "csid0" },
- .ops = &csid_ops_4_1,
+ .csid = {
+ .hw_ops = &csid_ops_4_1,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_1
+ }
},
/* CSID1 */
@@ -94,7 +108,11 @@ static const struct camss_subdev_resources csid_res_8x16[] = {
{ 0 } },
.reg = { "csid1" },
.interrupt = { "csid1" },
- .ops = &csid_ops_4_1,
+ .csid = {
+ .hw_ops = &csid_ops_4_1,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_1
+ }
},
};
@@ -105,8 +123,7 @@ static const struct camss_subdev_resources ispif_res_8x16 = {
"csi1", "csi1_pix", "csi1_rdi" },
.clock_for_reset = { "vfe0", "csi_vfe0" },
.reg = { "ispif", "csi_clk_mux" },
- .interrupt = { "ispif" }
-
+ .interrupt = { "ispif" },
};
static const struct camss_subdev_resources vfe_res_8x16[] = {
@@ -128,11 +145,169 @@ static const struct camss_subdev_resources vfe_res_8x16[] = {
{ 0 } },
.reg = { "vfe0" },
.interrupt = { "vfe0" },
- .line_num = 3,
- .ops = &vfe_ops_4_1
+ .vfe = {
+ .line_num = 3,
+ .hw_ops = &vfe_ops_4_1,
+ .formats_rdi = &vfe_formats_rdi_8x16,
+ .formats_pix = &vfe_formats_pix_8x16
+ }
}
};
+static const struct camss_subdev_resources csid_res_8x53[] = {
+ /* CSID0 */
+ {
+ .regulators = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
+ "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 310000000,
+ 400000000, 465000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
+ },
+
+ /* CSID1 */
+ {
+ .regulators = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
+ "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 310000000,
+ 400000000, 465000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
+ },
+
+ /* CSID2 */
+ {
+ .regulators = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
+ "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 310000000,
+ 400000000, 465000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
+ },
+};
+
+static const struct camss_subdev_resources ispif_res_8x53 = {
+ /* ISPIF */
+ .clock = { "top_ahb", "ahb", "ispif_ahb",
+ "csi0", "csi0_pix", "csi0_rdi",
+ "csi1", "csi1_pix", "csi1_rdi",
+ "csi2", "csi2_pix", "csi2_rdi" },
+ .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" },
+ .reg = { "ispif", "csi_clk_mux" },
+ .interrupt = { "ispif" },
+};
+
+static const struct camss_subdev_resources vfe_res_8x53[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "top_ahb", "ahb", "ispif_ahb",
+ "vfe0", "csi_vfe0", "vfe0_ahb", "vfe0_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 50000000, 100000000, 133330000,
+ 160000000, 200000000, 266670000,
+ 310000000, 400000000, 465000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "vfe0",
+ .hw_ops = &vfe_ops_4_1,
+ .formats_rdi = &vfe_formats_rdi_8x16,
+ .formats_pix = &vfe_formats_pix_8x16
+ }
+ },
+
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "top_ahb", "ahb", "ispif_ahb",
+ "vfe1", "csi_vfe1", "vfe1_ahb", "vfe1_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 50000000, 100000000, 133330000,
+ 160000000, 200000000, 266670000,
+ 310000000, 400000000, 465000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "vfe1",
+ .hw_ops = &vfe_ops_4_1,
+ .formats_rdi = &vfe_formats_rdi_8x16,
+ .formats_pix = &vfe_formats_pix_8x16
+ }
+ }
+};
+
+static const struct resources_icc icc_res_8x53[] = {
+ {
+ .name = "cam_ahb",
+ .icc_bw_tbl.avg = 38400,
+ .icc_bw_tbl.peak = 76800,
+ },
+ {
+ .name = "cam_vfe0_mem",
+ .icc_bw_tbl.avg = 939524,
+ .icc_bw_tbl.peak = 1342177,
+ },
+ {
+ .name = "cam_vfe1_mem",
+ .icc_bw_tbl.avg = 939524,
+ .icc_bw_tbl.peak = 1342177,
+ },
+};
+
static const struct camss_subdev_resources csiphy_res_8x96[] = {
/* CSIPHY0 */
{
@@ -144,7 +319,11 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = {
{ 100000000, 200000000, 266666667 } },
.reg = { "csiphy0", "csiphy0_clk_mux" },
.interrupt = { "csiphy0" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_8x96
+ }
},
/* CSIPHY1 */
@@ -157,7 +336,11 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = {
{ 100000000, 200000000, 266666667 } },
.reg = { "csiphy1", "csiphy1_clk_mux" },
.interrupt = { "csiphy1" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_8x96
+ }
},
/* CSIPHY2 */
@@ -170,7 +353,11 @@ static const struct camss_subdev_resources csiphy_res_8x96[] = {
{ 100000000, 200000000, 266666667 } },
.reg = { "csiphy2", "csiphy2_clk_mux" },
.interrupt = { "csiphy2" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_8x96
+ }
}
};
@@ -190,7 +377,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = {
{ 0 } },
.reg = { "csid0" },
.interrupt = { "csid0" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
},
/* CSID1 */
@@ -208,7 +399,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = {
{ 0 } },
.reg = { "csid1" },
.interrupt = { "csid1" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
},
/* CSID2 */
@@ -226,7 +421,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = {
{ 0 } },
.reg = { "csid2" },
.interrupt = { "csid2" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
},
/* CSID3 */
@@ -244,7 +443,11 @@ static const struct camss_subdev_resources csid_res_8x96[] = {
{ 0 } },
.reg = { "csid3" },
.interrupt = { "csid3" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
}
};
@@ -257,7 +460,7 @@ static const struct camss_subdev_resources ispif_res_8x96 = {
"csi3", "csi3_pix", "csi3_rdi" },
.clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" },
.reg = { "ispif", "csi_clk_mux" },
- .interrupt = { "ispif" }
+ .interrupt = { "ispif" },
};
static const struct camss_subdev_resources vfe_res_8x96[] = {
@@ -277,9 +480,13 @@ static const struct camss_subdev_resources vfe_res_8x96[] = {
{ 0 } },
.reg = { "vfe0" },
.interrupt = { "vfe0" },
- .line_num = 3,
- .has_pd = true,
- .ops = &vfe_ops_4_7
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .hw_ops = &vfe_ops_4_7,
+ .formats_rdi = &vfe_formats_rdi_8x96,
+ .formats_pix = &vfe_formats_pix_8x96
+ }
},
/* VFE1 */
@@ -298,9 +505,13 @@ static const struct camss_subdev_resources vfe_res_8x96[] = {
{ 0 } },
.reg = { "vfe1" },
.interrupt = { "vfe1" },
- .line_num = 3,
- .has_pd = true,
- .ops = &vfe_ops_4_7
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .hw_ops = &vfe_ops_4_7,
+ .formats_rdi = &vfe_formats_rdi_8x96,
+ .formats_pix = &vfe_formats_pix_8x96
+ }
}
};
@@ -317,7 +528,11 @@ static const struct camss_subdev_resources csiphy_res_660[] = {
{ 0 } },
.reg = { "csiphy0", "csiphy0_clk_mux" },
.interrupt = { "csiphy0" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_8x96
+ }
},
/* CSIPHY1 */
@@ -332,7 +547,11 @@ static const struct camss_subdev_resources csiphy_res_660[] = {
{ 0 } },
.reg = { "csiphy1", "csiphy1_clk_mux" },
.interrupt = { "csiphy1" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_8x96
+ }
},
/* CSIPHY2 */
@@ -347,7 +566,11 @@ static const struct camss_subdev_resources csiphy_res_660[] = {
{ 0 } },
.reg = { "csiphy2", "csiphy2_clk_mux" },
.interrupt = { "csiphy2" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_8x96
+ }
}
};
@@ -370,7 +593,11 @@ static const struct camss_subdev_resources csid_res_660[] = {
{ 0 } },
.reg = { "csid0" },
.interrupt = { "csid0" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
},
/* CSID1 */
@@ -391,7 +618,11 @@ static const struct camss_subdev_resources csid_res_660[] = {
{ 0 } },
.reg = { "csid1" },
.interrupt = { "csid1" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
},
/* CSID2 */
@@ -412,7 +643,11 @@ static const struct camss_subdev_resources csid_res_660[] = {
{ 0 } },
.reg = { "csid2" },
.interrupt = { "csid2" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
},
/* CSID3 */
@@ -433,7 +668,11 @@ static const struct camss_subdev_resources csid_res_660[] = {
{ 0 } },
.reg = { "csid3" },
.interrupt = { "csid3" },
- .ops = &csid_ops_4_7,
+ .csid = {
+ .hw_ops = &csid_ops_4_7,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_4_7
+ }
}
};
@@ -446,7 +685,7 @@ static const struct camss_subdev_resources ispif_res_660 = {
"csi3", "csi3_pix", "csi3_rdi" },
.clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" },
.reg = { "ispif", "csi_clk_mux" },
- .interrupt = { "ispif" }
+ .interrupt = { "ispif" },
};
static const struct camss_subdev_resources vfe_res_660[] = {
@@ -469,9 +708,13 @@ static const struct camss_subdev_resources vfe_res_660[] = {
{ 0 } },
.reg = { "vfe0" },
.interrupt = { "vfe0" },
- .line_num = 3,
- .has_pd = true,
- .ops = &vfe_ops_4_8
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .hw_ops = &vfe_ops_4_8,
+ .formats_rdi = &vfe_formats_rdi_8x96,
+ .formats_pix = &vfe_formats_pix_8x96
+ }
},
/* VFE1 */
@@ -493,9 +736,195 @@ static const struct camss_subdev_resources vfe_res_660[] = {
{ 0 } },
.reg = { "vfe1" },
.interrupt = { "vfe1" },
- .line_num = 3,
- .has_pd = true,
- .ops = &vfe_ops_4_8
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .hw_ops = &vfe_ops_4_8,
+ .formats_rdi = &vfe_formats_rdi_8x96,
+ .formats_pix = &vfe_formats_pix_8x96
+ }
+ }
+};
+
+static const struct camss_subdev_resources csiphy_res_670[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "soc_ahb", "cpas_ahb",
+ "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "soc_ahb", "cpas_ahb",
+ "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "soc_ahb", "cpas_ahb",
+ "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 19200000, 240000000, 269333333 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ }
+};
+
+static const struct camss_subdev_resources csid_res_670[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "soc_ahb", "vfe0",
+ "vfe0_cphy_rx", "csi0" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 384000000 },
+ { 19200000, 75000000, 384000000, 538666667 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "soc_ahb", "vfe1",
+ "vfe1_cphy_rx", "csi1" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 384000000 },
+ { 19200000, 75000000, 384000000, 538666667 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "soc_ahb", "vfe_lite",
+ "vfe_lite_cphy_rx", "csi2" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 384000000 },
+ { 19200000, 75000000, 384000000, 538666667 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ }
+};
+
+static const struct camss_subdev_resources vfe_res_670[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe0", "vfe0_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 4,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe1", "vfe1_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 },
+ { 0 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 4,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+
+ /* VFE-lite */
+ {
+ .regulators = {},
+ .clock = { "camnoc_axi", "cpas_ahb", "soc_ahb",
+ "vfe_lite" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 320000000, 404000000, 480000000, 600000000 } },
+ .reg = { "vfe_lite" },
+ .interrupt = { "vfe_lite" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
}
};
@@ -516,7 +945,11 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
{ 19200000, 240000000, 269333333 } },
.reg = { "csiphy0" },
.interrupt = { "csiphy0" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY1 */
@@ -535,7 +968,11 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
{ 19200000, 240000000, 269333333 } },
.reg = { "csiphy1" },
.interrupt = { "csiphy1" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY2 */
@@ -554,7 +991,11 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
{ 19200000, 240000000, 269333333 } },
.reg = { "csiphy2" },
.interrupt = { "csiphy2" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY3 */
@@ -573,7 +1014,11 @@ static const struct camss_subdev_resources csiphy_res_845[] = {
{ 19200000, 240000000, 269333333 } },
.reg = { "csiphy3" },
.interrupt = { "csiphy3" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
}
};
@@ -596,7 +1041,11 @@ static const struct camss_subdev_resources csid_res_845[] = {
{ 384000000 } },
.reg = { "csid0" },
.interrupt = { "csid0" },
- .ops = &csid_ops_gen2
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
},
/* CSID1 */
@@ -617,7 +1066,11 @@ static const struct camss_subdev_resources csid_res_845[] = {
{ 384000000 } },
.reg = { "csid1" },
.interrupt = { "csid1" },
- .ops = &csid_ops_gen2
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
},
/* CSID2 */
@@ -638,8 +1091,12 @@ static const struct camss_subdev_resources csid_res_845[] = {
{ 384000000 } },
.reg = { "csid2" },
.interrupt = { "csid2" },
- .is_lite = true,
- .ops = &csid_ops_gen2
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
}
};
@@ -662,9 +1119,14 @@ static const struct camss_subdev_resources vfe_res_845[] = {
{ 384000000 } },
.reg = { "vfe0" },
.interrupt = { "vfe0" },
- .line_num = 4,
- .has_pd = true,
- .ops = &vfe_ops_170
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife0",
+ .has_pd = true,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
},
/* VFE1 */
@@ -685,9 +1147,14 @@ static const struct camss_subdev_resources vfe_res_845[] = {
{ 384000000 } },
.reg = { "vfe1" },
.interrupt = { "vfe1" },
- .line_num = 4,
- .has_pd = true,
- .ops = &vfe_ops_170
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife1",
+ .has_pd = true,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
},
/* VFE-lite */
@@ -707,79 +1174,107 @@ static const struct camss_subdev_resources vfe_res_845[] = {
{ 384000000 } },
.reg = { "vfe_lite" },
.interrupt = { "vfe_lite" },
- .is_lite = true,
- .line_num = 4,
- .ops = &vfe_ops_170
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
}
};
static const struct camss_subdev_resources csiphy_res_8250[] = {
/* CSIPHY0 */
{
- .regulators = {},
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
.reg = { "csiphy0" },
.interrupt = { "csiphy0" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY1 */
{
- .regulators = {},
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
.reg = { "csiphy1" },
.interrupt = { "csiphy1" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY2 */
{
- .regulators = {},
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
.reg = { "csiphy2" },
.interrupt = { "csiphy2" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY3 */
{
- .regulators = {},
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "csiphy3", "csiphy3_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
.reg = { "csiphy3" },
.interrupt = { "csiphy3" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY4 */
{
- .regulators = {},
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
.reg = { "csiphy4" },
.interrupt = { "csiphy4" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 4,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
},
/* CSIPHY5 */
{
- .regulators = {},
+ .regulators = { "vdda-phy", "vdda-pll" },
.clock = { "csiphy5", "csiphy5_timer" },
.clock_rate = { { 400000000 },
{ 300000000 } },
.reg = { "csiphy5" },
.interrupt = { "csiphy5" },
- .ops = &csiphy_ops_3ph_1_0
+ .csiphy = {
+ .id = 5,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
}
};
static const struct camss_subdev_resources csid_res_8250[] = {
/* CSID0 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {},
.clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_areg", "vfe0_ahb" },
.clock_rate = { { 400000000 },
{ 400000000 },
@@ -788,11 +1283,15 @@ static const struct camss_subdev_resources csid_res_8250[] = {
{ 0 } },
.reg = { "csid0" },
.interrupt = { "csid0" },
- .ops = &csid_ops_gen2
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
},
/* CSID1 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {},
.clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_areg", "vfe1_ahb" },
.clock_rate = { { 400000000 },
{ 400000000 },
@@ -801,11 +1300,15 @@ static const struct camss_subdev_resources csid_res_8250[] = {
{ 0 } },
.reg = { "csid1" },
.interrupt = { "csid1" },
- .ops = &csid_ops_gen2
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
},
/* CSID2 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {},
.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" },
.clock_rate = { { 400000000 },
{ 400000000 },
@@ -813,12 +1316,16 @@ static const struct camss_subdev_resources csid_res_8250[] = {
{ 0 } },
.reg = { "csid2" },
.interrupt = { "csid2" },
- .is_lite = true,
- .ops = &csid_ops_gen2
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
},
/* CSID3 */
{
- .regulators = { "vdda-phy", "vdda-pll" },
+ .regulators = {},
.clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite", "vfe_lite_ahb" },
.clock_rate = { { 400000000 },
{ 400000000 },
@@ -826,8 +1333,12 @@ static const struct camss_subdev_resources csid_res_8250[] = {
{ 0 } },
.reg = { "csid3" },
.interrupt = { "csid3" },
- .is_lite = true,
- .ops = &csid_ops_gen2
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
}
};
@@ -849,10 +1360,14 @@ static const struct camss_subdev_resources vfe_res_8250[] = {
{ 0 } },
.reg = { "vfe0" },
.interrupt = { "vfe0" },
- .pd_name = "ife0",
- .line_num = 3,
- .has_pd = true,
- .ops = &vfe_ops_480
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_480,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
},
/* VFE1 */
{
@@ -871,10 +1386,14 @@ static const struct camss_subdev_resources vfe_res_8250[] = {
{ 0 } },
.reg = { "vfe1" },
.interrupt = { "vfe1" },
- .pd_name = "ife1",
- .line_num = 3,
- .has_pd = true,
- .ops = &vfe_ops_480
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_480,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
},
/* VFE2 (lite) */
{
@@ -892,9 +1411,13 @@ static const struct camss_subdev_resources vfe_res_8250[] = {
{ 0 } },
.reg = { "vfe_lite0" },
.interrupt = { "vfe_lite0" },
- .is_lite = true,
- .line_num = 4,
- .ops = &vfe_ops_480
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_480,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
},
/* VFE3 (lite) */
{
@@ -912,9 +1435,13 @@ static const struct camss_subdev_resources vfe_res_8250[] = {
{ 0 } },
.reg = { "vfe_lite1" },
.interrupt = { "vfe_lite1" },
- .is_lite = true,
- .line_num = 4,
- .ops = &vfe_ops_480
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_480,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
},
};
@@ -941,6 +1468,1314 @@ static const struct resources_icc icc_res_sm8250[] = {
},
};
+static const struct camss_subdev_resources csiphy_res_7280[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sc7280
+ }
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sc7280
+ }
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sc7280
+ }
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" },
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sc7280
+ }
+ },
+ /* CSIPHY4 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+
+ .clock = { "csiphy4", "csiphy4_timer" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy4" },
+ .interrupt = { "csiphy4" },
+ .csiphy = {
+ .id = 4,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sc7280
+ }
+ },
+};
+
+static const struct camss_subdev_resources csid_res_7280[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+
+ .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 0 },
+ { 380000000, 510000000, 637000000, 760000000 }
+ },
+
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .is_lite = false,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID1 */
+ {
+ .regulators = {},
+
+ .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 0 },
+ { 380000000, 510000000, 637000000, 760000000 }
+ },
+
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .is_lite = false,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID2 */
+ {
+ .regulators = {},
+
+ .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 0 },
+ { 380000000, 510000000, 637000000, 760000000 }
+ },
+
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .is_lite = false,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID3 */
+ {
+ .regulators = {},
+
+ .clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 0 },
+ { 320000000, 400000000, 480000000, 600000000 }
+ },
+
+ .reg = { "csid_lite0" },
+ .interrupt = { "csid_lite0" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID4 */
+ {
+ .regulators = {},
+
+ .clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" },
+ .clock_rate = { { 300000000, 400000000 },
+ { 0 },
+ { 320000000, 400000000, 480000000, 600000000 }
+ },
+
+ .reg = { "csid_lite1" },
+ .interrupt = { "csid_lite1" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+};
+
+static const struct camss_subdev_resources vfe_res_7280[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+
+ .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe0",
+ "vfe0_axi", "gcc_axi_hf", "gcc_axi_sf" },
+ .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
+ { 80000000 },
+ { 0 },
+ { 380000000, 510000000, 637000000, 760000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+
+ .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe1",
+ "vfe1_axi", "gcc_axi_hf", "gcc_axi_sf" },
+ .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
+ { 80000000 },
+ { 0 },
+ { 380000000, 510000000, 637000000, 760000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE2 */
+ {
+ .regulators = {},
+
+ .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb", "vfe2",
+ "vfe2_axi", "gcc_axi_hf", "gcc_axi_sf" },
+ .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
+ { 80000000 },
+ { 0 },
+ { 380000000, 510000000, 637000000, 760000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+
+ .reg = { "vfe2" },
+ .interrupt = { "vfe2" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .hw_ops = &vfe_ops_170,
+ .has_pd = true,
+ .pd_name = "ife2",
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE3 (lite) */
+ {
+ .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb",
+ "vfe_lite0", "gcc_axi_hf", "gcc_axi_sf" },
+ .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
+ { 80000000 },
+ { 0 },
+ { 320000000, 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 } },
+
+ .regulators = {},
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE4 (lite) */
+ {
+ .clock = { "camnoc_axi", "cpas_ahb", "icp_ahb",
+ "vfe_lite1", "gcc_axi_hf", "gcc_axi_sf" },
+ .clock_rate = { { 150000000, 240000000, 320000000, 400000000, 480000000 },
+ { 80000000 },
+ { 0 },
+ { 320000000, 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 } },
+
+ .regulators = {},
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+};
+
+static const struct resources_icc icc_res_sc7280[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 38400,
+ .icc_bw_tbl.peak = 76800,
+ },
+ {
+ .name = "hf_0",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
+static const struct camss_subdev_resources csiphy_res_sc8280xp[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = {},
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = {},
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = {},
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = {},
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" },
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+};
+
+static const struct camss_subdev_resources csid_res_sc8280xp[] = {
+ /* CSID0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0", "vfe0_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1", "vfe1_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2", "vfe2_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe3_csid", "vfe3_cphy_rx", "vfe3", "vfe3_axi" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid3" },
+ .interrupt = { "csid3" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID_LITE0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite0_csid", "vfe_lite0_cphy_rx", "vfe_lite0" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid0_lite" },
+ .interrupt = { "csid0_lite" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID_LITE1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite1_csid", "vfe_lite1_cphy_rx", "vfe_lite1" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid1_lite" },
+ .interrupt = { "csid1_lite" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID_LITE2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite2_csid", "vfe_lite2_cphy_rx", "vfe_lite2" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid2_lite" },
+ .interrupt = { "csid2_lite" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID_LITE3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "vfe_lite3_csid", "vfe_lite3_cphy_rx", "vfe_lite3" },
+ .clock_rate = { { 400000000, 480000000, 600000000 },
+ { 0 },
+ { 0 }, },
+ .reg = { "csid3_lite" },
+ .interrupt = { "csid3_lite" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ }
+};
+
+static const struct camss_subdev_resources vfe_res_sc8280xp[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe0", "vfe0_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe1", "vfe1_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe2", "vfe2_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe2" },
+ .interrupt = { "vfe2" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife2",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE3 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe3", "vfe3_axi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 400000000, 558000000, 637000000, 760000000 },
+ { 0 }, },
+ .reg = { "vfe3" },
+ .interrupt = { "vfe3" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife3",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE_LITE_0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite0" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000 }, },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE_LITE_1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite1" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000 }, },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE_LITE_2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite2" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000, }, },
+ .reg = { "vfe_lite2" },
+ .interrupt = { "vfe_lite2" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE_LITE_3 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb", "camnoc_axi", "vfe_lite3" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 19200000, 80000000},
+ { 19200000, 150000000, 266666667, 320000000, 400000000, 480000000 },
+ { 320000000, 400000000, 480000000, 600000000 }, },
+ .reg = { "vfe_lite3" },
+ .interrupt = { "vfe_lite3" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+};
+
+static const struct resources_icc icc_res_sc8280xp[] = {
+ {
+ .name = "cam_ahb",
+ .icc_bw_tbl.avg = 150000,
+ .icc_bw_tbl.peak = 300000,
+ },
+ {
+ .name = "cam_hf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "cam_sf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "cam_sf_icp_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
+static const struct camss_subdev_resources csiphy_res_8550[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" },
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY4 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy4", "csiphy4_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy4" },
+ .interrupt = { "csiphy4" },
+ .csiphy = {
+ .id = 4,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY5 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy5", "csiphy5_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy5" },
+ .interrupt = { "csiphy5" },
+ .csiphy = {
+ .id = 5,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY6 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy6", "csiphy6_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy6" },
+ .interrupt = { "csiphy6" },
+ .csiphy = {
+ .id = 6,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY7 */
+ {
+ .regulators = { "vdda-phy", "vdda-pll" },
+ .clock = { "csiphy7", "csiphy7_timer" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000 } },
+ .reg = { "csiphy7" },
+ .interrupt = { "csiphy7" },
+ .csiphy = {
+ .id = 7,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ }
+};
+
+static const struct resources_wrapper csid_wrapper_res_sm8550 = {
+ .reg = "csid_wrapper",
+};
+
+static const struct camss_subdev_resources csid_res_8550[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .is_lite = false,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .is_lite = false,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "csid", "csiphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .is_lite = false,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID3 */
+ {
+ .regulators = {},
+ .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid_lite0" },
+ .interrupt = { "csid_lite0" },
+ .csid = {
+ .is_lite = true,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID4 */
+ {
+ .regulators = {},
+ .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx" },
+ .clock_rate = { { 400000000, 480000000 },
+ { 400000000, 480000000 } },
+ .reg = { "csid_lite1" },
+ .interrupt = { "csid_lite1" },
+ .csid = {
+ .is_lite = true,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .hw_ops = &csid_ops_780,
+ .formats = &csid_formats_gen2
+ }
+ }
+};
+
+static const struct camss_subdev_resources vfe_res_8550[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe0_fast_ahb",
+ "vfe0", "cpas_vfe0", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe1_fast_ahb",
+ "vfe1", "cpas_vfe1", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe2_fast_ahb",
+ "vfe2", "cpas_vfe2", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 466000000, 594000000, 675000000, 785000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe2" },
+ .interrupt = { "vfe2" },
+ .vfe = {
+ .line_num = 3,
+ .is_lite = false,
+ .has_pd = true,
+ .pd_name = "ife2",
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE3 lite */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
+ "vfe_lite", "cpas_ife_lite", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 400000000, 480000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE4 lite */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "cpas_ahb", "cpas_fast_ahb_clk", "vfe_lite_ahb",
+ "vfe_lite", "cpas_ife_lite", "camnoc_axi" },
+ .clock_rate = { { 0 },
+ { 80000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 },
+ { 400000000, 480000000 },
+ { 300000000, 400000000 },
+ { 300000000, 400000000 } },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .vfe = {
+ .line_num = 4,
+ .is_lite = true,
+ .hw_ops = &vfe_ops_780,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+};
+
+static const struct resources_icc icc_res_sm8550[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "hf_0_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
+static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+ /* CSIPHY4 */
+ {
+ .regulators = { "vdd-csiphy-0p8-supply",
+ "vdd-csiphy-1p2-supply" },
+ .clock = { "csiphy4", "csiphy4_timer" },
+ .clock_rate = { { 300000000, 400000000, 480000000 },
+ { 266666667, 400000000 } },
+ .reg = { "csiphy4" },
+ .interrupt = { "csiphy4" },
+ .csiphy = {
+ .id = 4,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ },
+ },
+};
+
+static const struct camss_subdev_resources csid_res_x1e80100[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ },
+ },
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ },
+ },
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ },
+ },
+ /* CSID_LITE0 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+ .reg = { "csid_lite0" },
+ .interrupt = { "csid_lite0" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID_LITE1 */
+ {
+ .regulators = {},
+ .clock = { "gcc_axi_hf", "gcc_axi_sf", "cpas_ahb",
+ "cpas_fast_ahb", "csid", "csid_csiphy_rx" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 64000000, 80000000 },
+ { 80000000, 100000000, 200000000,
+ 300000000, 400000000 },
+ { 300000000, 400000000, 480000000 },
+ { 300000000, 400000000, 480000000 }, },
+
+ .reg = { "csid_lite1" },
+ .interrupt = { "csid_lite1" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_680,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+};
+
+static const struct camss_subdev_resources vfe_res_x1e80100[] = {
+ /* IFE0 */
+ {
+ .regulators = {},
+ .clock = {"camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "cpas_fast_ahb", "cpas_vfe0", "vfe0_fast_ahb",
+ "vfe0" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 345600000, 432000000, 594000000, 675000000,
+ 727000000 }, },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* IFE1 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "cpas_fast_ahb", "cpas_vfe1", "vfe1_fast_ahb",
+ "vfe1" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 345600000, 432000000, 594000000, 675000000,
+ 727000000 }, },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 4,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* IFE_LITE_0 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite",
+ "vfe_lite_csid" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 266666667, 400000000, 480000000 },
+ { 266666667, 400000000, 480000000 }, },
+ .reg = { "vfe_lite0" },
+ .interrupt = { "vfe_lite0" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+ /* IFE_LITE_1 */
+ {
+ .regulators = {},
+ .clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
+ "vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite",
+ "vfe_lite_csid" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 266666667, 400000000, 480000000 },
+ { 266666667, 400000000, 480000000 }, },
+ .reg = { "vfe_lite1" },
+ .interrupt = { "vfe_lite1" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_680,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ },
+ },
+};
+
+static const struct resources_icc icc_res_x1e80100[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 150000,
+ .icc_bw_tbl.peak = 300000,
+ },
+ {
+ .name = "hf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "sf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "sf_icp_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
+static const struct resources_wrapper csid_wrapper_res_x1e80100 = {
+ .reg = "csid_wrapper",
+};
+
/*
* camss_add_clock_margin - Add margin to clock frequency rate
* @rate: Clock frequency rate
@@ -999,12 +2834,12 @@ void camss_disable_clocks(int nclocks, struct camss_clock *clock)
}
/*
- * camss_find_sensor - Find a linked media entity which represents a sensor
+ * camss_find_sensor_pad - Find the media pad via which the sensor is linked
* @entity: Media entity to start searching from
*
- * Return a pointer to sensor media entity or NULL if not found
+ * Return a pointer to sensor media pad or NULL if not found
*/
-struct media_entity *camss_find_sensor(struct media_entity *entity)
+struct media_pad *camss_find_sensor_pad(struct media_entity *entity)
{
struct media_pad *pad;
@@ -1020,7 +2855,7 @@ struct media_entity *camss_find_sensor(struct media_entity *entity)
entity = pad->entity;
if (entity->function == MEDIA_ENT_F_CAM_SENSOR)
- return entity;
+ return pad;
}
}
@@ -1035,16 +2870,13 @@ struct media_entity *camss_find_sensor(struct media_entity *entity)
s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
unsigned int lanes)
{
- struct media_entity *sensor;
- struct v4l2_subdev *subdev;
+ struct media_pad *sensor_pad;
- sensor = camss_find_sensor(entity);
- if (!sensor)
+ sensor_pad = camss_find_sensor_pad(entity);
+ if (!sensor_pad)
return -ENODEV;
- subdev = media_entity_to_v4l2_subdev(sensor);
-
- return v4l2_get_link_freq(subdev->ctrl_handler, bpp, 2 * lanes);
+ return v4l2_get_link_freq(sensor_pad, bpp, 2 * lanes);
}
/*
@@ -1056,15 +2888,15 @@ s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
*/
int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock)
{
- struct media_entity *sensor;
+ struct media_pad *sensor_pad;
struct v4l2_subdev *subdev;
struct v4l2_ctrl *ctrl;
- sensor = camss_find_sensor(entity);
- if (!sensor)
+ sensor_pad = camss_find_sensor_pad(entity);
+ if (!sensor_pad)
return -ENODEV;
- subdev = media_entity_to_v4l2_subdev(sensor);
+ subdev = media_entity_to_v4l2_subdev(sensor_pad->entity);
ctrl = v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE);
@@ -1083,7 +2915,7 @@ int camss_pm_domain_on(struct camss *camss, int id)
if (id < camss->res->vfe_num) {
struct vfe_device *vfe = &camss->vfe[id];
- ret = vfe->ops->pm_domain_on(vfe);
+ ret = vfe->res->hw_ops->pm_domain_on(vfe);
}
return ret;
@@ -1094,10 +2926,52 @@ void camss_pm_domain_off(struct camss *camss, int id)
if (id < camss->res->vfe_num) {
struct vfe_device *vfe = &camss->vfe[id];
- vfe->ops->pm_domain_off(vfe);
+ vfe->res->hw_ops->pm_domain_off(vfe);
+ }
+}
+
+static int vfe_parent_dev_ops_get(struct camss *camss, int id)
+{
+ int ret = -EINVAL;
+
+ if (id < camss->res->vfe_num) {
+ struct vfe_device *vfe = &camss->vfe[id];
+
+ ret = vfe_get(vfe);
+ }
+
+ return ret;
+}
+
+static int vfe_parent_dev_ops_put(struct camss *camss, int id)
+{
+ if (id < camss->res->vfe_num) {
+ struct vfe_device *vfe = &camss->vfe[id];
+
+ vfe_put(vfe);
+ }
+
+ return 0;
+}
+
+static void __iomem
+*vfe_parent_dev_ops_get_base_address(struct camss *camss, int id)
+{
+ if (id < camss->res->vfe_num) {
+ struct vfe_device *vfe = &camss->vfe[id];
+
+ return vfe->base;
}
+
+ return NULL;
}
+static const struct parent_dev_ops vfe_parent_dev_ops = {
+ .get = vfe_parent_dev_ops_get,
+ .put = vfe_parent_dev_ops_put,
+ .get_base_address = vfe_parent_dev_ops_get_base_address
+};
+
/*
* camss_of_parse_endpoint_node - Parse port endpoint node
* @dev: Device
@@ -1114,8 +2988,20 @@ static int camss_of_parse_endpoint_node(struct device *dev,
struct v4l2_mbus_config_mipi_csi2 *mipi_csi2;
struct v4l2_fwnode_endpoint vep = { { 0 } };
unsigned int i;
+ int ret;
+
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
+ if (ret)
+ return ret;
- v4l2_fwnode_endpoint_parse(of_fwnode_handle(node), &vep);
+ /*
+ * Most SoCs support both D-PHY and C-PHY standards, but currently only
+ * D-PHY is supported in the driver.
+ */
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
+ dev_err(dev, "Unsupported bus type %d\n", vep.bus_type);
+ return -EINVAL;
+ }
csd->interface.csiphy_id = vep.base.port;
@@ -1196,13 +3082,15 @@ err_cleanup:
*/
static int camss_init_subdevices(struct camss *camss)
{
+ struct platform_device *pdev = to_platform_device(camss->dev);
const struct camss_resources *res = camss->res;
unsigned int i;
int ret;
for (i = 0; i < camss->res->csiphy_num; i++) {
ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i],
- &res->csiphy_res[i], i);
+ &res->csiphy_res[i],
+ res->csiphy_res[i].csiphy.id);
if (ret < 0) {
dev_err(camss->dev,
"Failed to init csiphy%d sub-device: %d\n",
@@ -1222,6 +3110,17 @@ static int camss_init_subdevices(struct camss *camss)
}
}
+ /* Get optional CSID wrapper regs shared between CSID devices */
+ if (res->csid_wrapper_res) {
+ char *reg = res->csid_wrapper_res->reg;
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource_byname(pdev, reg);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+ camss->csid_wrapper_base = base;
+ }
+
for (i = 0; i < camss->res->csid_num; i++) {
ret = msm_csid_subdev_init(camss, &camss->csid[i],
&res->csid_res[i], i);
@@ -1244,72 +3143,47 @@ static int camss_init_subdevices(struct camss *camss)
}
/*
- * camss_register_entities - Register subdev nodes and create links
+ * camss_link_entities - Register subdev nodes and create links
+ * camss_link_err - print error in case link creation fails
+ * @src_name: name for source of the link
+ * @sink_name: name for sink of the link
+ */
+inline void camss_link_err(struct camss *camss,
+ const char *src_name,
+ const char *sink_name,
+ int ret)
+{
+ dev_err(camss->dev,
+ "Failed to link %s->%s entities: %d\n",
+ src_name,
+ sink_name,
+ ret);
+}
+
+/*
+ * camss_link_entities - Register subdev nodes and create links
* @camss: CAMSS device
*
* Return 0 on success or a negative error code on failure
*/
-static int camss_register_entities(struct camss *camss)
+static int camss_link_entities(struct camss *camss)
{
int i, j, k;
int ret;
for (i = 0; i < camss->res->csiphy_num; i++) {
- ret = msm_csiphy_register_entity(&camss->csiphy[i],
- &camss->v4l2_dev);
- if (ret < 0) {
- dev_err(camss->dev,
- "Failed to register csiphy%d entity: %d\n",
- i, ret);
- goto err_reg_csiphy;
- }
- }
-
- for (i = 0; i < camss->res->csid_num; i++) {
- ret = msm_csid_register_entity(&camss->csid[i],
- &camss->v4l2_dev);
- if (ret < 0) {
- dev_err(camss->dev,
- "Failed to register csid%d entity: %d\n",
- i, ret);
- goto err_reg_csid;
- }
- }
-
- ret = msm_ispif_register_entities(camss->ispif,
- &camss->v4l2_dev);
- if (ret < 0) {
- dev_err(camss->dev, "Failed to register ispif entities: %d\n",
- ret);
- goto err_reg_ispif;
- }
-
- for (i = 0; i < camss->res->vfe_num; i++) {
- ret = msm_vfe_register_entities(&camss->vfe[i],
- &camss->v4l2_dev);
- if (ret < 0) {
- dev_err(camss->dev,
- "Failed to register vfe%d entities: %d\n",
- i, ret);
- goto err_reg_vfe;
- }
- }
-
- for (i = 0; i < camss->res->csiphy_num; i++) {
for (j = 0; j < camss->res->csid_num; j++) {
- ret = media_create_pad_link(
- &camss->csiphy[i].subdev.entity,
- MSM_CSIPHY_PAD_SRC,
- &camss->csid[j].subdev.entity,
- MSM_CSID_PAD_SINK,
- 0);
+ ret = media_create_pad_link(&camss->csiphy[i].subdev.entity,
+ MSM_CSIPHY_PAD_SRC,
+ &camss->csid[j].subdev.entity,
+ MSM_CSID_PAD_SINK,
+ 0);
if (ret < 0) {
- dev_err(camss->dev,
- "Failed to link %s->%s entities: %d\n",
- camss->csiphy[i].subdev.entity.name,
- camss->csid[j].subdev.entity.name,
- ret);
- goto err_link;
+ camss_link_err(camss,
+ camss->csiphy[i].subdev.entity.name,
+ camss->csid[j].subdev.entity.name,
+ ret);
+ return ret;
}
}
}
@@ -1317,26 +3191,24 @@ static int camss_register_entities(struct camss *camss)
if (camss->ispif) {
for (i = 0; i < camss->res->csid_num; i++) {
for (j = 0; j < camss->ispif->line_num; j++) {
- ret = media_create_pad_link(
- &camss->csid[i].subdev.entity,
- MSM_CSID_PAD_SRC,
- &camss->ispif->line[j].subdev.entity,
- MSM_ISPIF_PAD_SINK,
- 0);
+ ret = media_create_pad_link(&camss->csid[i].subdev.entity,
+ MSM_CSID_PAD_SRC,
+ &camss->ispif->line[j].subdev.entity,
+ MSM_ISPIF_PAD_SINK,
+ 0);
if (ret < 0) {
- dev_err(camss->dev,
- "Failed to link %s->%s entities: %d\n",
- camss->csid[i].subdev.entity.name,
- camss->ispif->line[j].subdev.entity.name,
- ret);
- goto err_link;
+ camss_link_err(camss,
+ camss->csid[i].subdev.entity.name,
+ camss->ispif->line[j].subdev.entity.name,
+ ret);
+ return ret;
}
}
}
for (i = 0; i < camss->ispif->line_num; i++)
for (k = 0; k < camss->res->vfe_num; k++)
- for (j = 0; j < camss->vfe[k].line_num; j++) {
+ for (j = 0; j < camss->vfe[k].res->line_num; j++) {
struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev;
struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev;
@@ -1346,18 +3218,16 @@ static int camss_register_entities(struct camss *camss)
MSM_VFE_PAD_SINK,
0);
if (ret < 0) {
- dev_err(camss->dev,
- "Failed to link %s->%s entities: %d\n",
- ispif->entity.name,
- vfe->entity.name,
- ret);
- goto err_link;
+ camss_link_err(camss, ispif->entity.name,
+ vfe->entity.name,
+ ret);
+ return ret;
}
}
} else {
for (i = 0; i < camss->res->csid_num; i++)
for (k = 0; k < camss->res->vfe_num; k++)
- for (j = 0; j < camss->vfe[k].line_num; j++) {
+ for (j = 0; j < camss->vfe[k].res->line_num; j++) {
struct v4l2_subdev *csid = &camss->csid[i].subdev;
struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev;
@@ -1367,20 +3237,92 @@ static int camss_register_entities(struct camss *camss)
MSM_VFE_PAD_SINK,
0);
if (ret < 0) {
- dev_err(camss->dev,
- "Failed to link %s->%s entities: %d\n",
- csid->entity.name,
- vfe->entity.name,
- ret);
- goto err_link;
+ camss_link_err(camss, csid->entity.name,
+ vfe->entity.name,
+ ret);
+ return ret;
}
}
}
return 0;
+}
+
+void camss_reg_update(struct camss *camss, int hw_id, int port_id, bool is_clear)
+{
+ struct csid_device *csid;
+
+ if (hw_id < camss->res->csid_num) {
+ csid = &camss->csid[hw_id];
+
+ csid->res->hw_ops->reg_update(csid, port_id, is_clear);
+ }
+}
+
+void camss_buf_done(struct camss *camss, int hw_id, int port_id)
+{
+ struct vfe_device *vfe;
+
+ if (hw_id < camss->res->vfe_num) {
+ vfe = &camss->vfe[hw_id];
+
+ vfe->res->hw_ops->vfe_buf_done(vfe, port_id);
+ }
+}
+
+/*
+ * camss_register_entities - Register subdev nodes and create links
+ * @camss: CAMSS device
+ *
+ * Return 0 on success or a negative error code on failure
+ */
+static int camss_register_entities(struct camss *camss)
+{
+ int i;
+ int ret;
+
+ for (i = 0; i < camss->res->csiphy_num; i++) {
+ ret = msm_csiphy_register_entity(&camss->csiphy[i],
+ &camss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to register csiphy%d entity: %d\n",
+ i, ret);
+ goto err_reg_csiphy;
+ }
+ }
+
+ for (i = 0; i < camss->res->csid_num; i++) {
+ ret = msm_csid_register_entity(&camss->csid[i],
+ &camss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to register csid%d entity: %d\n",
+ i, ret);
+ goto err_reg_csid;
+ }
+ }
+
+ ret = msm_ispif_register_entities(camss->ispif,
+ &camss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(camss->dev, "Failed to register ispif entities: %d\n", ret);
+ goto err_reg_ispif;
+ }
+
+ for (i = 0; i < camss->res->vfe_num; i++) {
+ ret = msm_vfe_register_entities(&camss->vfe[i],
+ &camss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to register vfe%d entities: %d\n",
+ i, ret);
+ goto err_reg_vfe;
+ }
+ }
+
+ return 0;
-err_link:
- i = camss->res->vfe_num;
err_reg_vfe:
for (i--; i >= 0; i--)
msm_vfe_unregister_entities(&camss->vfe[i]);
@@ -1468,9 +3410,9 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async)
input, MSM_CSIPHY_PAD_SINK,
MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
if (ret < 0) {
- dev_err(camss->dev,
- "Failed to link %s->%s entities: %d\n",
- sensor->name, input->name, ret);
+ camss_link_err(camss, sensor->name,
+ input->name,
+ ret);
return ret;
}
}
@@ -1518,7 +3460,7 @@ static int camss_configure_pd(struct camss *camss)
/* count the # of VFEs which have flagged power-domain */
for (vfepd_num = i = 0; i < camss->res->vfe_num; i++) {
- if (res->vfe_res[i].has_pd)
+ if (res->vfe_res[i].vfe.has_pd)
vfepd_num++;
}
@@ -1537,10 +3479,8 @@ static int camss_configure_pd(struct camss *camss)
if (camss->res->pd_name) {
camss->genpd = dev_pm_domain_attach_by_name(camss->dev,
camss->res->pd_name);
- if (IS_ERR(camss->genpd)) {
- ret = PTR_ERR(camss->genpd);
- goto fail_pm;
- }
+ if (IS_ERR(camss->genpd))
+ return PTR_ERR(camss->genpd);
}
if (!camss->genpd) {
@@ -1550,14 +3490,13 @@ static int camss_configure_pd(struct camss *camss)
*/
camss->genpd = dev_pm_domain_attach_by_id(camss->dev,
camss->genpd_num - 1);
+ if (IS_ERR(camss->genpd))
+ return PTR_ERR(camss->genpd);
}
- if (IS_ERR_OR_NULL(camss->genpd)) {
- if (!camss->genpd)
- ret = -ENODEV;
- else
- ret = PTR_ERR(camss->genpd);
- goto fail_pm;
- }
+
+ if (!camss->genpd)
+ return -ENODEV;
+
camss->genpd_link = device_link_add(camss->dev, camss->genpd,
DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE);
@@ -1646,6 +3585,7 @@ static int camss_probe(struct platform_device *pdev)
return -ENOMEM;
if (camss->res->version == CAMSS_8x16 ||
+ camss->res->version == CAMSS_8x53 ||
camss->res->version == CAMSS_8x96) {
camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL);
if (!camss->ispif)
@@ -1690,6 +3630,8 @@ static int camss_probe(struct platform_device *pdev)
v4l2_async_nf_init(&camss->notifier, &camss->v4l2_dev);
+ pm_runtime_enable(dev);
+
num_subdevs = camss_of_parse_ports(camss);
if (num_subdevs < 0) {
ret = num_subdevs;
@@ -1700,6 +3642,10 @@ static int camss_probe(struct platform_device *pdev)
if (ret < 0)
goto err_v4l2_device_unregister;
+ ret = camss->res->link_entities(camss);
+ if (ret < 0)
+ goto err_register_subdevs;
+
if (num_subdevs) {
camss->notifier.ops = &camss_subdev_notifier_ops;
@@ -1726,8 +3672,6 @@ static int camss_probe(struct platform_device *pdev)
}
}
- pm_runtime_enable(dev);
-
return 0;
err_register_subdevs:
@@ -1735,6 +3679,7 @@ err_register_subdevs:
err_v4l2_device_unregister:
v4l2_device_unregister(&camss->v4l2_dev);
v4l2_async_nf_cleanup(&camss->notifier);
+ pm_runtime_disable(dev);
err_genpd_cleanup:
camss_genpd_cleanup(camss);
@@ -1779,6 +3724,21 @@ static const struct camss_resources msm8916_resources = {
.csiphy_num = ARRAY_SIZE(csiphy_res_8x16),
.csid_num = ARRAY_SIZE(csid_res_8x16),
.vfe_num = ARRAY_SIZE(vfe_res_8x16),
+ .link_entities = camss_link_entities
+};
+
+static const struct camss_resources msm8953_resources = {
+ .version = CAMSS_8x53,
+ .icc_res = icc_res_8x53,
+ .icc_path_num = ARRAY_SIZE(icc_res_8x53),
+ .csiphy_res = csiphy_res_8x96,
+ .csid_res = csid_res_8x53,
+ .ispif_res = &ispif_res_8x53,
+ .vfe_res = vfe_res_8x53,
+ .csiphy_num = ARRAY_SIZE(csiphy_res_8x96),
+ .csid_num = ARRAY_SIZE(csid_res_8x53),
+ .vfe_num = ARRAY_SIZE(vfe_res_8x53),
+ .link_entities = camss_link_entities
};
static const struct camss_resources msm8996_resources = {
@@ -1790,6 +3750,7 @@ static const struct camss_resources msm8996_resources = {
.csiphy_num = ARRAY_SIZE(csiphy_res_8x96),
.csid_num = ARRAY_SIZE(csid_res_8x96),
.vfe_num = ARRAY_SIZE(vfe_res_8x96),
+ .link_entities = camss_link_entities
};
static const struct camss_resources sdm660_resources = {
@@ -1801,16 +3762,30 @@ static const struct camss_resources sdm660_resources = {
.csiphy_num = ARRAY_SIZE(csiphy_res_660),
.csid_num = ARRAY_SIZE(csid_res_660),
.vfe_num = ARRAY_SIZE(vfe_res_660),
+ .link_entities = camss_link_entities
+};
+
+static const struct camss_resources sdm670_resources = {
+ .version = CAMSS_845,
+ .csiphy_res = csiphy_res_670,
+ .csid_res = csid_res_670,
+ .vfe_res = vfe_res_670,
+ .csiphy_num = ARRAY_SIZE(csiphy_res_670),
+ .csid_num = ARRAY_SIZE(csid_res_670),
+ .vfe_num = ARRAY_SIZE(vfe_res_670),
+ .link_entities = camss_link_entities
};
static const struct camss_resources sdm845_resources = {
.version = CAMSS_845,
+ .pd_name = "top",
.csiphy_res = csiphy_res_845,
.csid_res = csid_res_845,
.vfe_res = vfe_res_845,
.csiphy_num = ARRAY_SIZE(csiphy_res_845),
.csid_num = ARRAY_SIZE(csid_res_845),
.vfe_num = ARRAY_SIZE(vfe_res_845),
+ .link_entities = camss_link_entities
};
static const struct camss_resources sm8250_resources = {
@@ -1824,14 +3799,80 @@ static const struct camss_resources sm8250_resources = {
.csiphy_num = ARRAY_SIZE(csiphy_res_8250),
.csid_num = ARRAY_SIZE(csid_res_8250),
.vfe_num = ARRAY_SIZE(vfe_res_8250),
+ .link_entities = camss_link_entities
+};
+
+static const struct camss_resources sc8280xp_resources = {
+ .version = CAMSS_8280XP,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_sc8280xp,
+ .csid_res = csid_res_sc8280xp,
+ .ispif_res = NULL,
+ .vfe_res = vfe_res_sc8280xp,
+ .icc_res = icc_res_sc8280xp,
+ .icc_path_num = ARRAY_SIZE(icc_res_sc8280xp),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_sc8280xp),
+ .csid_num = ARRAY_SIZE(csid_res_sc8280xp),
+ .vfe_num = ARRAY_SIZE(vfe_res_sc8280xp),
+ .link_entities = camss_link_entities
+};
+
+static const struct camss_resources sc7280_resources = {
+ .version = CAMSS_7280,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_7280,
+ .csid_res = csid_res_7280,
+ .vfe_res = vfe_res_7280,
+ .icc_res = icc_res_sc7280,
+ .icc_path_num = ARRAY_SIZE(icc_res_sc7280),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_7280),
+ .csid_num = ARRAY_SIZE(csid_res_7280),
+ .vfe_num = ARRAY_SIZE(vfe_res_7280),
+ .link_entities = camss_link_entities
+};
+
+static const struct camss_resources sm8550_resources = {
+ .version = CAMSS_8550,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_8550,
+ .csid_res = csid_res_8550,
+ .vfe_res = vfe_res_8550,
+ .csid_wrapper_res = &csid_wrapper_res_sm8550,
+ .icc_res = icc_res_sm8550,
+ .icc_path_num = ARRAY_SIZE(icc_res_sm8550),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_8550),
+ .csid_num = ARRAY_SIZE(csid_res_8550),
+ .vfe_num = ARRAY_SIZE(vfe_res_8550),
+ .link_entities = camss_link_entities
+};
+
+static const struct camss_resources x1e80100_resources = {
+ .version = CAMSS_X1E80100,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_x1e80100,
+ .csid_res = csid_res_x1e80100,
+ .vfe_res = vfe_res_x1e80100,
+ .csid_wrapper_res = &csid_wrapper_res_x1e80100,
+ .icc_res = icc_res_x1e80100,
+ .icc_path_num = ARRAY_SIZE(icc_res_x1e80100),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_x1e80100),
+ .csid_num = ARRAY_SIZE(csid_res_x1e80100),
+ .vfe_num = ARRAY_SIZE(vfe_res_x1e80100),
+ .link_entities = camss_link_entities
};
static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,msm8916-camss", .data = &msm8916_resources },
+ { .compatible = "qcom,msm8953-camss", .data = &msm8953_resources },
{ .compatible = "qcom,msm8996-camss", .data = &msm8996_resources },
+ { .compatible = "qcom,sc7280-camss", .data = &sc7280_resources },
+ { .compatible = "qcom,sc8280xp-camss", .data = &sc8280xp_resources },
{ .compatible = "qcom,sdm660-camss", .data = &sdm660_resources },
+ { .compatible = "qcom,sdm670-camss", .data = &sdm670_resources },
{ .compatible = "qcom,sdm845-camss", .data = &sdm845_resources },
{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
+ { .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
+ { .compatible = "qcom,x1e80100-camss", .data = &x1e80100_resources },
{ }
};
@@ -1878,7 +3919,7 @@ static const struct dev_pm_ops camss_pm_ops = {
static struct platform_driver qcom_camss_driver = {
.probe = camss_probe,
- .remove_new = camss_remove,
+ .remove = camss_remove,
.driver = {
.name = "qcom-camss",
.of_match_table = camss_dt_match,
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index a0c2dcc779f0..63c0afee154a 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -22,6 +22,7 @@
#include "camss-csiphy.h"
#include "camss-ispif.h"
#include "camss-vfe.h"
+#include "camss-format.h"
#define to_camss(ptr_module) \
container_of(ptr_module, struct camss, ptr_module)
@@ -48,11 +49,11 @@ struct camss_subdev_resources {
u32 clock_rate[CAMSS_RES_MAX][CAMSS_RES_MAX];
char *reg[CAMSS_RES_MAX];
char *interrupt[CAMSS_RES_MAX];
- char *pd_name;
- u8 line_num;
- bool has_pd;
- bool is_lite;
- const void *ops;
+ union {
+ struct csiphy_subdev_resources csiphy;
+ struct csid_subdev_resources csid;
+ struct vfe_subdev_resources vfe;
+ };
};
struct icc_bw_tbl {
@@ -65,6 +66,10 @@ struct resources_icc {
struct icc_bw_tbl icc_bw_tbl;
};
+struct resources_wrapper {
+ char *reg;
+};
+
enum pm_domain {
PM_DOMAIN_VFE0 = 0,
PM_DOMAIN_VFE1 = 1,
@@ -72,11 +77,16 @@ enum pm_domain {
};
enum camss_version {
+ CAMSS_660,
+ CAMSS_7280,
CAMSS_8x16,
+ CAMSS_8x53,
CAMSS_8x96,
- CAMSS_660,
- CAMSS_845,
CAMSS_8250,
+ CAMSS_8280XP,
+ CAMSS_845,
+ CAMSS_8550,
+ CAMSS_X1E80100,
};
enum icc_count {
@@ -91,11 +101,13 @@ struct camss_resources {
const struct camss_subdev_resources *csid_res;
const struct camss_subdev_resources *ispif_res;
const struct camss_subdev_resources *vfe_res;
+ const struct resources_wrapper *csid_wrapper_res;
const struct resources_icc *icc_res;
const unsigned int icc_path_num;
const unsigned int csiphy_num;
const unsigned int csid_num;
const unsigned int vfe_num;
+ int (*link_entities)(struct camss *camss);
};
struct camss {
@@ -107,6 +119,7 @@ struct camss {
struct csid_device *csid;
struct ispif_device *ispif;
struct vfe_device *vfe;
+ void __iomem *csid_wrapper_base;
atomic_t ref_count;
int genpd_num;
struct device *genpd;
@@ -132,16 +145,27 @@ struct camss_clock {
u32 nfreqs;
};
+struct parent_dev_ops {
+ int (*get)(struct camss *camss, int id);
+ int (*put)(struct camss *camss, int id);
+ void __iomem *(*get_base_address)(struct camss *camss, int id);
+};
+
void camss_add_clock_margin(u64 *rate);
int camss_enable_clocks(int nclocks, struct camss_clock *clock,
struct device *dev);
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
-struct media_entity *camss_find_sensor(struct media_entity *entity);
+struct media_pad *camss_find_sensor_pad(struct media_entity *entity);
s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp,
unsigned int lanes);
int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock);
int camss_pm_domain_on(struct camss *camss, int id);
void camss_pm_domain_off(struct camss *camss, int id);
+int camss_vfe_get(struct camss *camss, int id);
+void camss_vfe_put(struct camss *camss, int id);
void camss_delete(struct camss *camss);
+void camss_buf_done(struct camss *camss, int hw_id, int port_id);
+void camss_reg_update(struct camss *camss, int hw_id,
+ int port_id, bool is_clear);
#endif /* QC_MSM_CAMSS_H */
diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig
new file mode 100644
index 000000000000..3c803a05305a
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/Kconfig
@@ -0,0 +1,13 @@
+config VIDEO_QCOM_IRIS
+ tristate "Qualcomm iris V4L2 decoder driver"
+ depends on VIDEO_DEV
+ depends on ARCH_QCOM || COMPILE_TEST
+ select V4L2_MEM2MEM_DEV
+ select QCOM_MDT_LOADER if ARCH_QCOM
+ select QCOM_SCM
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ This is a V4L2 driver for Qualcomm iris video accelerator
+ hardware. It accelerates decoding operations on various
+ Qualcomm SoCs.
+ To compile this driver as a module choose m here.
diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile
new file mode 100644
index 000000000000..e86d00ee6f15
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -0,0 +1,31 @@
+qcom-iris-objs += \
+ iris_buffer.o \
+ iris_core.o \
+ iris_ctrls.o \
+ iris_firmware.o \
+ iris_hfi_common.o \
+ iris_hfi_gen1_command.o \
+ iris_hfi_gen1_response.o \
+ iris_hfi_gen2_command.o \
+ iris_hfi_gen2_packet.o \
+ iris_hfi_gen2_response.o \
+ iris_hfi_queue.o \
+ iris_platform_gen2.o \
+ iris_power.o \
+ iris_probe.o \
+ iris_resources.o \
+ iris_state.o \
+ iris_utils.o \
+ iris_vidc.o \
+ iris_vb2.o \
+ iris_vdec.o \
+ iris_vpu2.o \
+ iris_vpu3x.o \
+ iris_vpu_buffer.o \
+ iris_vpu_common.o \
+
+ifeq ($(CONFIG_VIDEO_QCOM_VENUS),)
+qcom-iris-objs += iris_platform_sm8250.o
+endif
+
+obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
new file mode 100644
index 000000000000..e5c5a564fcb8
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -0,0 +1,623 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_buffer.h"
+#include "iris_instance.h"
+#include "iris_power.h"
+#include "iris_vpu_buffer.h"
+
+#define PIXELS_4K 4096
+#define MAX_WIDTH 4096
+#define MAX_HEIGHT 2304
+#define Y_STRIDE_ALIGN 128
+#define UV_STRIDE_ALIGN 128
+#define Y_SCANLINE_ALIGN 32
+#define UV_SCANLINE_ALIGN 16
+#define UV_SCANLINE_ALIGN_QC08C 32
+#define META_STRIDE_ALIGNED 64
+#define META_SCANLINE_ALIGNED 16
+#define NUM_MBS_4K (DIV_ROUND_UP(MAX_WIDTH, 16) * DIV_ROUND_UP(MAX_HEIGHT, 16))
+
+/*
+ * NV12:
+ * YUV 4:2:0 image with a plane of 8 bit Y samples followed
+ * by an interleaved U/V plane containing 8 bit 2x2 subsampled
+ * colour difference samples.
+ *
+ * <-Y/UV_Stride (aligned to 128)->
+ * <------- Width ------->
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | y_scanlines (aligned to 32)
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * U V U V U V U V U V U V . . . . ^
+ * U V U V U V U V U V U V . . . . |
+ * U V U V U V U V U V U V . . . . |
+ * U V U V U V U V U V U V . . . . uv_scanlines (aligned to 16)
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . . . --> Buffer size aligned to 4K
+ *
+ * y_stride : Width aligned to 128
+ * uv_stride : Width aligned to 128
+ * y_scanlines: Height aligned to 32
+ * uv_scanlines: Height/2 aligned to 16
+ * Total size = align((y_stride * y_scanlines
+ * + uv_stride * uv_scanlines , 4096)
+ *
+ * Note: All the alignments are hardware requirements.
+ */
+static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst)
+{
+ u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines;
+ struct v4l2_format *f = inst->fmt_dst;
+
+ y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
+ uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
+ y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN);
+ uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN);
+ y_plane = y_stride * y_scanlines;
+ uv_plane = uv_stride * uv_scanlines;
+
+ return ALIGN(y_plane + uv_plane, PIXELS_4K);
+}
+
+/*
+ * QC08C:
+ * Compressed Macro-tile format for NV12.
+ * Contains 4 planes in the following order -
+ * (A) Y_Meta_Plane
+ * (B) Y_UBWC_Plane
+ * (C) UV_Meta_Plane
+ * (D) UV_UBWC_Plane
+ *
+ * Y_Meta_Plane consists of meta information to decode compressed
+ * tile data in Y_UBWC_Plane.
+ * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+ * UBWC decoder block will use the Y_Meta_Plane data together with
+ * Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples.
+ *
+ * UV_Meta_Plane consists of meta information to decode compressed
+ * tile data in UV_UBWC_Plane.
+ * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+ * UBWC decoder block will use UV_Meta_Plane data together with
+ * UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2
+ * subsampled color difference samples.
+ *
+ * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+ * and randomly accessible. There is no dependency between tiles.
+ *
+ * <----- y_meta_stride ----> (aligned to 64)
+ * <-------- Width ------>
+ * M M M M M M M M M M M M . . ^ ^
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . Height |
+ * M M M M M M M M M M M M . . | y_meta_scanlines (aligned to 16)
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . V |
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * . . . . . . . . . . . . . . V
+ * <--Compressed tile y_stride---> (aligned to 128)
+ * <------- Width ------->
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile y_scanlines (aligned to 32)
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * . . . . . . . . . . . . . . . . V
+ * <----- uv_meta_stride ----> (aligned to 64)
+ * M M M M M M M M M M M M . . ^
+ * M M M M M M M M M M M M . . |
+ * M M M M M M M M M M M M . . |
+ * M M M M M M M M M M M M . . uv_meta_scanlines (aligned to 16)
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * <--Compressed tile uv_stride---> (aligned to 128)
+ * U* V* U* V* U* V* U* V* . . . . ^
+ * U* V* U* V* U* V* U* V* . . . . |
+ * U* V* U* V* U* V* U* V* . . . . |
+ * U* V* U* V* U* V* U* V* . . . . uv_scanlines (aligned to 32)
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ *
+ * y_stride: width aligned to 128
+ * uv_stride: width aligned to 128
+ * y_scanlines: height aligned to 32
+ * uv_scanlines: height aligned to 32
+ * y_plane: buffer size aligned to 4096
+ * uv_plane: buffer size aligned to 4096
+ * y_meta_stride: width aligned to 64
+ * y_meta_scanlines: height aligned to 16
+ * y_meta_plane: buffer size aligned to 4096
+ * uv_meta_stride: width aligned to 64
+ * uv_meta_scanlines: height aligned to 16
+ * uv_meta_plane: buffer size aligned to 4096
+ *
+ * Total size = align( y_plane + uv_plane +
+ * y_meta_plane + uv_meta_plane, 4096)
+ *
+ * Note: All the alignments are hardware requirements.
+ */
+static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst)
+{
+ u32 y_plane, uv_plane, y_stride, uv_stride;
+ struct v4l2_format *f = inst->fmt_dst;
+ u32 uv_meta_stride, uv_meta_plane;
+ u32 y_meta_stride, y_meta_plane;
+
+ y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, META_STRIDE_ALIGNED >> 1),
+ META_STRIDE_ALIGNED);
+ y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height,
+ META_SCANLINE_ALIGNED >> 1),
+ META_SCANLINE_ALIGNED);
+ y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K);
+
+ y_stride = ALIGN(f->fmt.pix_mp.width, Y_STRIDE_ALIGN);
+ y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN), PIXELS_4K);
+
+ uv_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width / 2, META_STRIDE_ALIGNED >> 2),
+ META_STRIDE_ALIGNED);
+ uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height / 2,
+ META_SCANLINE_ALIGNED >> 1),
+ META_SCANLINE_ALIGNED);
+ uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K);
+
+ uv_stride = ALIGN(f->fmt.pix_mp.width, UV_STRIDE_ALIGN);
+ uv_plane = ALIGN(uv_stride * ALIGN(f->fmt.pix_mp.height / 2, UV_SCANLINE_ALIGN_QC08C),
+ PIXELS_4K);
+
+ return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K);
+}
+
+static u32 iris_bitstream_buffer_size(struct iris_inst *inst)
+{
+ struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
+ u32 base_res_mbs = NUM_MBS_4K;
+ u32 frame_size, num_mbs;
+ u32 div_factor = 2;
+
+ num_mbs = iris_get_mbpf(inst);
+ if (num_mbs > NUM_MBS_4K) {
+ div_factor = 4;
+ base_res_mbs = caps->max_mbpf;
+ }
+
+ /*
+ * frame_size = YUVsize / div_factor
+ * where YUVsize = resolution_in_MBs * MBs_in_pixel * 3 / 2
+ */
+ frame_size = base_res_mbs * (16 * 16) * 3 / 2 / div_factor;
+
+ return ALIGN(frame_size, PIXELS_4K);
+}
+
+int iris_get_buffer_size(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return iris_bitstream_buffer_size(inst);
+ case BUF_OUTPUT:
+ return iris_yuv_buffer_size_nv12(inst);
+ case BUF_DPB:
+ return iris_yuv_buffer_size_qc08c(inst);
+ default:
+ return 0;
+ }
+}
+
+static void iris_fill_internal_buf_info(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ struct iris_buffers *buffers = &inst->buffers[buffer_type];
+
+ buffers->size = iris_vpu_buf_size(inst, buffer_type);
+ buffers->min_count = iris_vpu_buf_count(inst, buffer_type);
+}
+
+void iris_get_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ for (i = 0; i < internal_buffer_count; i++)
+ iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ for (i = 0; i < internal_buffer_count; i++)
+ iris_fill_internal_buf_info(inst, internal_buf_type[i]);
+ }
+}
+
+static int iris_create_internal_buffer(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type, u32 index)
+{
+ struct iris_buffers *buffers = &inst->buffers[buffer_type];
+ struct iris_core *core = inst->core;
+ struct iris_buffer *buffer;
+
+ if (!buffers->size)
+ return 0;
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&buffer->list);
+ buffer->type = buffer_type;
+ buffer->index = index;
+ buffer->buffer_size = buffers->size;
+ buffer->dma_attrs = DMA_ATTR_WRITE_COMBINE | DMA_ATTR_NO_KERNEL_MAPPING;
+ list_add_tail(&buffer->list, &buffers->list);
+
+ buffer->kvaddr = dma_alloc_attrs(core->dev, buffer->buffer_size,
+ &buffer->device_addr, GFP_KERNEL, buffer->dma_attrs);
+ if (!buffer->kvaddr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ u32 internal_buffer_count, i, j;
+ struct iris_buffers *buffers;
+ const u32 *internal_buf_type;
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ }
+
+ for (i = 0; i < internal_buffer_count; i++) {
+ buffers = &inst->buffers[internal_buf_type[i]];
+ for (j = 0; j < buffers->min_count; j++) {
+ ret = iris_create_internal_buffer(inst, internal_buf_type[i], j);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->session_queue_buf(inst, buf);
+ if (ret)
+ return ret;
+
+ buf->attr &= ~BUF_ATTR_DEFERRED;
+ buf->attr |= BUF_ATTR_QUEUED;
+
+ return 0;
+}
+
+int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ struct iris_buffer *buffer, *next;
+ struct iris_buffers *buffers;
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ }
+
+ for (i = 0; i < internal_buffer_count; i++) {
+ buffers = &inst->buffers[internal_buf_type[i]];
+ list_for_each_entry_safe(buffer, next, &buffers->list, list) {
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ continue;
+ if (buffer->attr & BUF_ATTR_QUEUED)
+ continue;
+ ret = iris_queue_buffer(inst, buffer);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
+{
+ struct iris_core *core = inst->core;
+
+ list_del(&buffer->list);
+ dma_free_attrs(core->dev, buffer->buffer_size, buffer->kvaddr,
+ buffer->device_addr, buffer->dma_attrs);
+ kfree(buffer);
+
+ return 0;
+}
+
+int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ struct iris_buffer *buf, *next;
+ struct iris_buffers *buffers;
+ const u32 *internal_buf_type;
+ u32 i, len;
+ int ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ len = platform_data->dec_ip_int_buf_tbl_size;
+ } else {
+ internal_buf_type = platform_data->dec_op_int_buf_tbl;
+ len = platform_data->dec_op_int_buf_tbl_size;
+ }
+
+ for (i = 0; i < len; i++) {
+ buffers = &inst->buffers[internal_buf_type[i]];
+ list_for_each_entry_safe(buf, next, &buffers->list, list) {
+ ret = iris_destroy_internal_buffer(inst, buf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int iris_release_internal_buffers(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ struct iris_buffers *buffers = &inst->buffers[buffer_type];
+ struct iris_buffer *buffer, *next;
+ int ret;
+
+ list_for_each_entry_safe(buffer, next, &buffers->list, list) {
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ continue;
+ if (!(buffer->attr & BUF_ATTR_QUEUED))
+ continue;
+ ret = hfi_ops->session_release_buf(inst, buffer);
+ if (ret)
+ return ret;
+ buffer->attr |= BUF_ATTR_PENDING_RELEASE;
+ }
+
+ return 0;
+}
+
+static int iris_release_input_internal_buffers(struct iris_inst *inst)
+{
+ const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const u32 *internal_buf_type;
+ u32 internal_buffer_count, i;
+ int ret;
+
+ internal_buf_type = platform_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+
+ for (i = 0; i < internal_buffer_count; i++) {
+ ret = iris_release_internal_buffers(inst, internal_buf_type[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst)
+{
+ struct iris_buffers *buffers = &inst->buffers[BUF_PERSIST];
+ struct iris_buffer *buffer, *next;
+ int ret;
+ u32 i;
+
+ if (!list_empty(&buffers->list))
+ return 0;
+
+ iris_fill_internal_buf_info(inst, BUF_PERSIST);
+
+ for (i = 0; i < buffers->min_count; i++) {
+ ret = iris_create_internal_buffer(inst, BUF_PERSIST, i);
+ if (ret)
+ return ret;
+ }
+
+ list_for_each_entry_safe(buffer, next, &buffers->list, list) {
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ continue;
+ if (buffer->attr & BUF_ATTR_QUEUED)
+ continue;
+ ret = iris_queue_buffer(inst, buffer);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst)
+{
+ int ret;
+
+ iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ ret = iris_release_input_internal_buffers(inst);
+ if (ret)
+ return ret;
+
+ ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+ int ret;
+
+ iris_scale_power(inst);
+
+ if (buf_type == BUF_INPUT) {
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_DEFERRED))
+ continue;
+ ret = iris_queue_buffer(inst, buf);
+ if (ret)
+ return ret;
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_DEFERRED))
+ continue;
+ ret = iris_queue_buffer(inst, buf);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void iris_vb2_queue_error(struct iris_inst *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct vb2_queue *q;
+
+ q = v4l2_m2m_get_src_vq(m2m_ctx);
+ vb2_queue_error(q);
+ q = v4l2_m2m_get_dst_vq(m2m_ctx);
+ vb2_queue_error(q);
+}
+
+static struct vb2_v4l2_buffer *
+iris_helper_find_buf(struct iris_inst *inst, u32 type, u32 idx)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ return v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, idx);
+ else
+ return v4l2_m2m_dst_buf_remove_by_idx(m2m_ctx, idx);
+}
+
+static void iris_get_ts_metadata(struct iris_inst *inst, u64 timestamp_ns,
+ struct vb2_v4l2_buffer *vbuf)
+{
+ u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(inst->tss); ++i) {
+ if (inst->tss[i].ts_ns != timestamp_ns)
+ continue;
+
+ vbuf->flags &= ~mask;
+ vbuf->flags |= inst->tss[i].flags;
+ vbuf->timecode = inst->tss[i].tc;
+ return;
+ }
+
+ vbuf->flags &= ~mask;
+ vbuf->flags |= inst->tss[inst->metadata_idx].flags;
+ vbuf->timecode = inst->tss[inst->metadata_idx].tc;
+}
+
+int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct vb2_v4l2_buffer *vbuf;
+ struct vb2_buffer *vb2;
+ u32 type, state;
+
+ switch (buf->type) {
+ case BUF_INPUT:
+ type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ break;
+ case BUF_OUTPUT:
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ break;
+ default:
+ return 0; /* Internal DPB Buffers */
+ }
+
+ vbuf = iris_helper_find_buf(inst, type, buf->index);
+ if (!vbuf)
+ return -EINVAL;
+
+ vb2 = &vbuf->vb2_buf;
+
+ if (buf->flags & V4L2_BUF_FLAG_ERROR)
+ state = VB2_BUF_STATE_ERROR;
+ else
+ state = VB2_BUF_STATE_DONE;
+
+ vbuf->flags |= buf->flags;
+
+ if (V4L2_TYPE_IS_CAPTURE(type)) {
+ vb2_set_plane_payload(vb2, 0, buf->data_size);
+ vbuf->sequence = inst->sequence_cap++;
+ iris_get_ts_metadata(inst, buf->timestamp, vbuf);
+ } else {
+ vbuf->sequence = inst->sequence_out++;
+ }
+
+ if (vbuf->flags & V4L2_BUF_FLAG_LAST) {
+ if (!v4l2_m2m_has_stopped(m2m_ctx)) {
+ const struct v4l2_event ev = { .type = V4L2_EVENT_EOS };
+
+ v4l2_event_queue_fh(&inst->fh, &ev);
+ v4l2_m2m_mark_stopped(m2m_ctx);
+ }
+ }
+ vb2->timestamp = buf->timestamp;
+ v4l2_m2m_buf_done(vbuf, state);
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h
new file mode 100644
index 000000000000..c36b6347b077
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_buffer.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_BUFFER_H__
+#define __IRIS_BUFFER_H__
+
+#include <media/videobuf2-v4l2.h>
+
+struct iris_inst;
+
+#define to_iris_buffer(ptr) container_of(ptr, struct iris_buffer, vb2)
+
+/**
+ * enum iris_buffer_type
+ *
+ * @BUF_INPUT: input buffer to the iris hardware
+ * @BUF_OUTPUT: output buffer from the iris hardware
+ * @BUF_BIN: buffer to store intermediate bin data
+ * @BUF_ARP: buffer for auto register programming
+ * @BUF_COMV: buffer to store colocated motion vectors
+ * @BUF_NON_COMV: buffer to hold config data for HW
+ * @BUF_LINE: buffer to store decoding/encoding context data for HW
+ * @BUF_DPB: buffer to store display picture buffers for reference
+ * @BUF_PERSIST: buffer to store session context data
+ * @BUF_SCRATCH_1: buffer to store decoding/encoding context data for HW
+ * @BUF_TYPE_MAX: max buffer types
+ */
+enum iris_buffer_type {
+ BUF_INPUT = 1,
+ BUF_OUTPUT,
+ BUF_BIN,
+ BUF_ARP,
+ BUF_COMV,
+ BUF_NON_COMV,
+ BUF_LINE,
+ BUF_DPB,
+ BUF_PERSIST,
+ BUF_SCRATCH_1,
+ BUF_TYPE_MAX,
+};
+
+/*
+ * enum iris_buffer_attributes
+ *
+ * BUF_ATTR_DEFERRED: buffer queued by client but not submitted to firmware.
+ * BUF_ATTR_PENDING_RELEASE: buffers requested to be released from firmware.
+ * BUF_ATTR_QUEUED: buffers submitted to firmware.
+ * BUF_ATTR_DEQUEUED: buffers received from firmware.
+ * BUF_ATTR_BUFFER_DONE: buffers sent back to vb2.
+ */
+enum iris_buffer_attributes {
+ BUF_ATTR_DEFERRED = BIT(0),
+ BUF_ATTR_PENDING_RELEASE = BIT(1),
+ BUF_ATTR_QUEUED = BIT(2),
+ BUF_ATTR_DEQUEUED = BIT(3),
+ BUF_ATTR_BUFFER_DONE = BIT(4),
+};
+
+/**
+ * struct iris_buffer
+ *
+ * @vb2: v4l2 vb2 buffer
+ * @list: list head for the iris_buffers structure
+ * @inst: iris instance structure
+ * @type: enum for type of iris buffer
+ * @index: identifier for the iris buffer
+ * @fd: file descriptor of the buffer
+ * @buffer_size: accessible buffer size in bytes starting from addr_offset
+ * @data_offset: accessible buffer offset from base address
+ * @data_size: data size in bytes
+ * @device_addr: device address of the buffer
+ * @kvaddr: kernel virtual address of the buffer
+ * @dma_attrs: dma attributes
+ * @flags: buffer flags. It is represented as bit masks.
+ * @timestamp: timestamp of the buffer in nano seconds (ns)
+ * @attr: enum for iris buffer attributes
+ */
+struct iris_buffer {
+ struct vb2_v4l2_buffer vb2;
+ struct list_head list;
+ struct iris_inst *inst;
+ enum iris_buffer_type type;
+ u32 index;
+ int fd;
+ size_t buffer_size;
+ u32 data_offset;
+ size_t data_size;
+ dma_addr_t device_addr;
+ void *kvaddr;
+ unsigned long dma_attrs;
+ u32 flags; /* V4L2_BUF_FLAG_* */
+ u64 timestamp;
+ enum iris_buffer_attributes attr;
+};
+
+struct iris_buffers {
+ struct list_head list;
+ u32 min_count;
+ u32 size;
+};
+
+int iris_get_buffer_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+void iris_get_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_create_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buffer);
+int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_alloc_and_queue_persist_bufs(struct iris_inst *inst);
+int iris_alloc_and_queue_input_int_bufs(struct iris_inst *inst);
+int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf);
+int iris_queue_deferred_buffers(struct iris_inst *inst, enum iris_buffer_type buf_type);
+int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf);
+void iris_vb2_queue_error(struct iris_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c
new file mode 100644
index 000000000000..0fa0a3b549a2
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_core.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "iris_core.h"
+#include "iris_firmware.h"
+#include "iris_state.h"
+#include "iris_vpu_common.h"
+
+void iris_core_deinit(struct iris_core *core)
+{
+ pm_runtime_resume_and_get(core->dev);
+
+ mutex_lock(&core->lock);
+ iris_fw_unload(core);
+ iris_vpu_power_off(core);
+ iris_hfi_queues_deinit(core);
+ core->state = IRIS_CORE_DEINIT;
+ mutex_unlock(&core->lock);
+
+ pm_runtime_put_sync(core->dev);
+}
+
+static int iris_wait_for_system_response(struct iris_core *core)
+{
+ u32 hw_response_timeout_val = core->iris_platform_data->hw_response_timeout;
+ int ret;
+
+ if (core->state == IRIS_CORE_ERROR)
+ return -EIO;
+
+ ret = wait_for_completion_timeout(&core->core_init_done,
+ msecs_to_jiffies(hw_response_timeout_val));
+ if (!ret) {
+ core->state = IRIS_CORE_ERROR;
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int iris_core_init(struct iris_core *core)
+{
+ int ret;
+
+ mutex_lock(&core->lock);
+ if (core->state == IRIS_CORE_INIT) {
+ ret = 0;
+ goto exit;
+ } else if (core->state == IRIS_CORE_ERROR) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ core->state = IRIS_CORE_INIT;
+
+ ret = iris_hfi_queues_init(core);
+ if (ret)
+ goto error;
+
+ ret = iris_vpu_power_on(core);
+ if (ret)
+ goto error_queue_deinit;
+
+ ret = iris_fw_load(core);
+ if (ret)
+ goto error_power_off;
+
+ ret = iris_vpu_boot_firmware(core);
+ if (ret)
+ goto error_unload_fw;
+
+ ret = iris_hfi_core_init(core);
+ if (ret)
+ goto error_unload_fw;
+
+ mutex_unlock(&core->lock);
+
+ return iris_wait_for_system_response(core);
+
+error_unload_fw:
+ iris_fw_unload(core);
+error_power_off:
+ iris_vpu_power_off(core);
+error_queue_deinit:
+ iris_hfi_queues_deinit(core);
+error:
+ core->state = IRIS_CORE_DEINIT;
+exit:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h
new file mode 100644
index 000000000000..aeeac32a1f6d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_core.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_CORE_H__
+#define __IRIS_CORE_H__
+
+#include <linux/types.h>
+#include <linux/pm_domain.h>
+#include <media/v4l2-device.h>
+
+#include "iris_hfi_common.h"
+#include "iris_hfi_queue.h"
+#include "iris_platform_common.h"
+#include "iris_resources.h"
+#include "iris_state.h"
+
+struct icc_info {
+ const char *name;
+ u32 bw_min_kbps;
+ u32 bw_max_kbps;
+};
+
+#define IRIS_FW_VERSION_LENGTH 128
+#define IFACEQ_CORE_PKT_SIZE (1024 * 4)
+
+/**
+ * struct iris_core - holds core parameters valid for all instances
+ *
+ * @dev: reference to device structure
+ * @reg_base: IO memory base address
+ * @irq: iris irq
+ * @v4l2_dev: a holder for v4l2 device structure
+ * @vdev_dec: iris video device structure for decoder
+ * @iris_v4l2_file_ops: iris v4l2 file ops
+ * @iris_v4l2_ioctl_ops: iris v4l2 ioctl ops
+ * @iris_vb2_ops: iris vb2 ops
+ * @icc_tbl: table of iris interconnects
+ * @icc_count: count of iris interconnects
+ * @pmdomain_tbl: table of iris power domains
+ * @opp_pmdomain_tbl: table of opp power domains
+ * @clock_tbl: table of iris clocks
+ * @clk_count: count of iris clocks
+ * @resets: table of iris reset clocks
+ * @controller_resets: table of controller reset clocks
+ * @iris_platform_data: a structure for platform data
+ * @state: current state of core
+ * @iface_q_table_daddr: device address for interface queue table memory
+ * @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory
+ * @iface_q_table_vaddr: virtual address for interface queue table memory
+ * @sfr_vaddr: virtual address for SFR (Sub System Failure Reason) register memory
+ * @command_queue: shared interface queue to send commands to firmware
+ * @message_queue: shared interface queue to receive responses from firmware
+ * @debug_queue: shared interface queue to receive debug info from firmware
+ * @lock: a lock for this strucure
+ * @response_packet: a pointer to response packet from fw to driver
+ * @header_id: id of packet header
+ * @packet_id: id of packet
+ * @power: a structure for clock and bw information
+ * @hfi_ops: iris hfi command ops
+ * @hfi_response_ops: iris hfi response ops
+ * @core_init_done: structure of signal completion for system response
+ * @intr_status: interrupt status
+ * @sys_error_handler: a delayed work for handling system fatal error
+ * @instances: a list_head of all instances
+ * @inst_fw_caps: an array of supported instance capabilities
+ */
+
+struct iris_core {
+ struct device *dev;
+ void __iomem *reg_base;
+ int irq;
+ struct v4l2_device v4l2_dev;
+ struct video_device *vdev_dec;
+ const struct v4l2_file_operations *iris_v4l2_file_ops;
+ const struct v4l2_ioctl_ops *iris_v4l2_ioctl_ops;
+ const struct vb2_ops *iris_vb2_ops;
+ struct icc_bulk_data *icc_tbl;
+ u32 icc_count;
+ struct dev_pm_domain_list *pmdomain_tbl;
+ struct dev_pm_domain_list *opp_pmdomain_tbl;
+ struct clk_bulk_data *clock_tbl;
+ u32 clk_count;
+ struct reset_control_bulk_data *resets;
+ struct reset_control_bulk_data *controller_resets;
+ const struct iris_platform_data *iris_platform_data;
+ enum iris_core_state state;
+ dma_addr_t iface_q_table_daddr;
+ dma_addr_t sfr_daddr;
+ void *iface_q_table_vaddr;
+ void *sfr_vaddr;
+ struct iris_iface_q_info command_queue;
+ struct iris_iface_q_info message_queue;
+ struct iris_iface_q_info debug_queue;
+ struct mutex lock; /* lock for core related operations */
+ u8 *response_packet;
+ u32 header_id;
+ u32 packet_id;
+ struct iris_core_power power;
+ const struct iris_hfi_command_ops *hfi_ops;
+ const struct iris_hfi_response_ops *hfi_response_ops;
+ struct completion core_init_done;
+ u32 intr_status;
+ struct delayed_work sys_error_handler;
+ struct list_head instances;
+ struct platform_inst_fw_cap inst_fw_caps[INST_FW_CAP_MAX];
+};
+
+int iris_core_init(struct iris_core *core);
+void iris_core_deinit(struct iris_core *core);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
new file mode 100644
index 000000000000..b690578256d5
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_ctrls.h"
+#include "iris_instance.h"
+
+static inline bool iris_valid_cap_id(enum platform_inst_fw_cap_type cap_id)
+{
+ return cap_id >= 1 && cap_id < INST_FW_CAP_MAX;
+}
+
+static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id)
+{
+ switch (id) {
+ case V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER:
+ return DEBLOCK;
+ case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+ return PROFILE;
+ case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+ return LEVEL;
+ default:
+ return INST_FW_CAP_MAX;
+ }
+}
+
+static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
+{
+ if (!iris_valid_cap_id(cap_id))
+ return 0;
+
+ switch (cap_id) {
+ case DEBLOCK:
+ return V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER;
+ case PROFILE:
+ return V4L2_CID_MPEG_VIDEO_H264_PROFILE;
+ case LEVEL:
+ return V4L2_CID_MPEG_VIDEO_H264_LEVEL;
+ default:
+ return 0;
+ }
+}
+
+static int iris_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct iris_inst *inst = container_of(ctrl->handler, struct iris_inst, ctrl_handler);
+ enum platform_inst_fw_cap_type cap_id;
+ struct platform_inst_fw_cap *cap;
+ struct vb2_queue *q;
+
+ cap = &inst->fw_caps[0];
+ cap_id = iris_get_cap_id(ctrl->id);
+ if (!iris_valid_cap_id(cap_id))
+ return -EINVAL;
+
+ q = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ if (vb2_is_streaming(q) &&
+ (!(inst->fw_caps[cap_id].flags & CAP_FLAG_DYNAMIC_ALLOWED)))
+ return -EINVAL;
+
+ cap[cap_id].flags |= CAP_FLAG_CLIENT_SET;
+
+ inst->fw_caps[cap_id].value = ctrl->val;
+
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops iris_ctrl_ops = {
+ .s_ctrl = iris_vdec_op_s_ctrl,
+};
+
+int iris_ctrls_init(struct iris_inst *inst)
+{
+ struct platform_inst_fw_cap *cap = &inst->fw_caps[0];
+ u32 num_ctrls = 0, ctrl_idx = 0, idx = 0;
+ u32 v4l2_id;
+ int ret;
+
+ for (idx = 1; idx < INST_FW_CAP_MAX; idx++) {
+ if (iris_get_v4l2_id(cap[idx].cap_id))
+ num_ctrls++;
+ }
+ if (!num_ctrls)
+ return -EINVAL;
+
+ /* Adding 1 to num_ctrls to include V4L2_CID_MIN_BUFFERS_FOR_CAPTURE */
+
+ ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, num_ctrls + 1);
+ if (ret)
+ return ret;
+
+ for (idx = 1; idx < INST_FW_CAP_MAX; idx++) {
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_id = iris_get_v4l2_id(cap[idx].cap_id);
+ if (!v4l2_id)
+ continue;
+
+ if (ctrl_idx >= num_ctrls) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ if (cap[idx].flags & CAP_FLAG_MENU) {
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler,
+ &iris_ctrl_ops,
+ v4l2_id,
+ cap[idx].max,
+ ~(cap[idx].step_or_mask),
+ cap[idx].value);
+ } else {
+ ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
+ &iris_ctrl_ops,
+ v4l2_id,
+ cap[idx].min,
+ cap[idx].max,
+ cap[idx].step_or_mask,
+ cap[idx].value);
+ }
+ if (!ctrl) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ctrl_idx++;
+ }
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, NULL,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 4);
+
+ ret = inst->ctrl_handler.error;
+ if (ret)
+ goto error;
+
+ return 0;
+error:
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+
+ return ret;
+}
+
+void iris_session_init_caps(struct iris_core *core)
+{
+ struct platform_inst_fw_cap *caps;
+ u32 i, num_cap, cap_id;
+
+ caps = core->iris_platform_data->inst_fw_caps;
+ num_cap = core->iris_platform_data->inst_fw_caps_size;
+
+ for (i = 0; i < num_cap; i++) {
+ cap_id = caps[i].cap_id;
+ if (!iris_valid_cap_id(cap_id))
+ continue;
+
+ core->inst_fw_caps[cap_id].cap_id = caps[i].cap_id;
+ core->inst_fw_caps[cap_id].min = caps[i].min;
+ core->inst_fw_caps[cap_id].max = caps[i].max;
+ core->inst_fw_caps[cap_id].step_or_mask = caps[i].step_or_mask;
+ core->inst_fw_caps[cap_id].value = caps[i].value;
+ core->inst_fw_caps[cap_id].flags = caps[i].flags;
+ core->inst_fw_caps[cap_id].hfi_id = caps[i].hfi_id;
+ }
+}
+
+static u32 iris_get_port_info(struct iris_inst *inst,
+ enum platform_inst_fw_cap_type cap_id)
+{
+ if (inst->fw_caps[cap_id].flags & CAP_FLAG_INPUT_PORT)
+ return HFI_PORT_BITSTREAM;
+ else if (inst->fw_caps[cap_id].flags & CAP_FLAG_OUTPUT_PORT)
+ return HFI_PORT_RAW;
+
+ return HFI_PORT_NONE;
+}
+
+int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 hfi_value = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32_ENUM,
+ &hfi_value, sizeof(u32));
+}
+
+int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 hfi_value = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &hfi_value, sizeof(u32));
+}
+
+int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 height = inp_f->fmt.pix_mp.height;
+ u32 width = inp_f->fmt.pix_mp.width;
+ u32 work_mode = STAGE_2;
+
+ if (iris_res_is_less_than(width, height, 1280, 720))
+ work_mode = STAGE_1;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &work_mode, sizeof(u32));
+}
+
+int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ u32 work_route = inst->fw_caps[PIPE].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &work_route, sizeof(u32));
+}
+
+int iris_set_properties(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ struct platform_inst_fw_cap *cap;
+ int ret;
+ u32 i;
+
+ ret = hfi_ops->session_set_config_params(inst, plane);
+ if (ret)
+ return ret;
+
+ for (i = 1; i < INST_FW_CAP_MAX; i++) {
+ cap = &inst->fw_caps[i];
+ if (!iris_valid_cap_id(cap->cap_id))
+ continue;
+
+ if (cap->cap_id && cap->set)
+ cap->set(inst, i);
+ }
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h
new file mode 100644
index 000000000000..9b5741868933
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_CTRLS_H__
+#define __IRIS_CTRLS_H__
+
+#include "iris_platform_common.h"
+
+struct iris_core;
+struct iris_inst;
+
+int iris_ctrls_init(struct iris_inst *inst);
+void iris_session_init_caps(struct iris_core *core);
+int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_properties(struct iris_inst *inst, u32 plane);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
new file mode 100644
index 000000000000..f1b5cd56db32
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/firmware.h>
+#include <linux/firmware/qcom/qcom_scm.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/soc/qcom/mdt_loader.h>
+
+#include "iris_core.h"
+#include "iris_firmware.h"
+
+#define MAX_FIRMWARE_NAME_SIZE 128
+
+static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
+{
+ u32 pas_id = core->iris_platform_data->pas_id;
+ const struct firmware *firmware = NULL;
+ struct device *dev = core->dev;
+ struct reserved_mem *rmem;
+ struct device_node *node;
+ phys_addr_t mem_phys;
+ size_t res_size;
+ ssize_t fw_size;
+ void *mem_virt;
+ int ret;
+
+ if (strlen(fw_name) >= MAX_FIRMWARE_NAME_SIZE - 4)
+ return -EINVAL;
+
+ node = of_parse_phandle(dev->of_node, "memory-region", 0);
+ if (!node)
+ return -EINVAL;
+
+ rmem = of_reserved_mem_lookup(node);
+ of_node_put(node);
+ if (!rmem)
+ return -EINVAL;
+
+ mem_phys = rmem->base;
+ res_size = rmem->size;
+
+ ret = request_firmware(&firmware, fw_name, dev);
+ if (ret)
+ return ret;
+
+ fw_size = qcom_mdt_get_size(firmware);
+ if (fw_size < 0 || res_size < (size_t)fw_size) {
+ ret = -EINVAL;
+ goto err_release_fw;
+ }
+
+ mem_virt = memremap(mem_phys, res_size, MEMREMAP_WC);
+ if (!mem_virt) {
+ ret = -ENOMEM;
+ goto err_release_fw;
+ }
+
+ ret = qcom_mdt_load(dev, firmware, fw_name,
+ pas_id, mem_virt, mem_phys, res_size, NULL);
+ if (ret)
+ goto err_mem_unmap;
+
+ ret = qcom_scm_pas_auth_and_reset(pas_id);
+ if (ret)
+ goto err_mem_unmap;
+
+ return ret;
+
+err_mem_unmap:
+ memunmap(mem_virt);
+err_release_fw:
+ release_firmware(firmware);
+
+ return ret;
+}
+
+int iris_fw_load(struct iris_core *core)
+{
+ struct tz_cp_config *cp_config = core->iris_platform_data->tz_cp_config_data;
+ const char *fwpath = NULL;
+ int ret;
+
+ ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0,
+ &fwpath);
+ if (ret)
+ fwpath = core->iris_platform_data->fwname;
+
+ ret = iris_load_fw_to_memory(core, fwpath);
+ if (ret) {
+ dev_err(core->dev, "firmware download failed\n");
+ return -ENOMEM;
+ }
+
+ ret = qcom_scm_mem_protect_video_var(cp_config->cp_start,
+ cp_config->cp_size,
+ cp_config->cp_nonpixel_start,
+ cp_config->cp_nonpixel_size);
+ if (ret) {
+ dev_err(core->dev, "protect memory failed\n");
+ qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+ return ret;
+ }
+
+ return ret;
+}
+
+int iris_fw_unload(struct iris_core *core)
+{
+ return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+}
+
+int iris_set_hw_state(struct iris_core *core, bool resume)
+{
+ return qcom_scm_set_remote_state(resume, 0);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.h b/drivers/media/platform/qcom/iris/iris_firmware.h
new file mode 100644
index 000000000000..e833ecd34887
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_firmware.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_FIRMWARE_H__
+#define __IRIS_FIRMWARE_H__
+
+struct iris_core;
+
+int iris_fw_load(struct iris_core *core);
+int iris_fw_unload(struct iris_core *core);
+int iris_set_hw_state(struct iris_core *core, bool resume);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c
new file mode 100644
index 000000000000..92112eb16c11
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "iris_firmware.h"
+#include "iris_core.h"
+#include "iris_hfi_common.h"
+#include "iris_vpu_common.h"
+
+u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries)
+{
+ switch (hfi_primaries) {
+ case HFI_PRIMARIES_RESERVED:
+ return V4L2_COLORSPACE_DEFAULT;
+ case HFI_PRIMARIES_BT709:
+ return V4L2_COLORSPACE_REC709;
+ case HFI_PRIMARIES_BT470_SYSTEM_M:
+ return V4L2_COLORSPACE_470_SYSTEM_M;
+ case HFI_PRIMARIES_BT470_SYSTEM_BG:
+ return V4L2_COLORSPACE_470_SYSTEM_BG;
+ case HFI_PRIMARIES_BT601_525:
+ return V4L2_COLORSPACE_SMPTE170M;
+ case HFI_PRIMARIES_SMPTE_ST240M:
+ return V4L2_COLORSPACE_SMPTE240M;
+ case HFI_PRIMARIES_BT2020:
+ return V4L2_COLORSPACE_BT2020;
+ case V4L2_COLORSPACE_DCI_P3:
+ return HFI_PRIMARIES_SMPTE_RP431_2;
+ default:
+ return V4L2_COLORSPACE_DEFAULT;
+ }
+}
+
+u32 iris_hfi_get_v4l2_transfer_char(u32 hfi_characterstics)
+{
+ switch (hfi_characterstics) {
+ case HFI_TRANSFER_RESERVED:
+ return V4L2_XFER_FUNC_DEFAULT;
+ case HFI_TRANSFER_BT709:
+ return V4L2_XFER_FUNC_709;
+ case HFI_TRANSFER_SMPTE_ST240M:
+ return V4L2_XFER_FUNC_SMPTE240M;
+ case HFI_TRANSFER_SRGB_SYCC:
+ return V4L2_XFER_FUNC_SRGB;
+ case HFI_TRANSFER_SMPTE_ST2084_PQ:
+ return V4L2_XFER_FUNC_SMPTE2084;
+ default:
+ return V4L2_XFER_FUNC_DEFAULT;
+ }
+}
+
+u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients)
+{
+ switch (hfi_coefficients) {
+ case HFI_MATRIX_COEFF_RESERVED:
+ return V4L2_YCBCR_ENC_DEFAULT;
+ case HFI_MATRIX_COEFF_BT709:
+ return V4L2_YCBCR_ENC_709;
+ case HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625:
+ return V4L2_YCBCR_ENC_XV601;
+ case HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625:
+ return V4L2_YCBCR_ENC_601;
+ case HFI_MATRIX_COEFF_SMPTE_ST240:
+ return V4L2_YCBCR_ENC_SMPTE240M;
+ case HFI_MATRIX_COEFF_BT2020_NON_CONSTANT:
+ return V4L2_YCBCR_ENC_BT2020;
+ case HFI_MATRIX_COEFF_BT2020_CONSTANT:
+ return V4L2_YCBCR_ENC_BT2020_CONST_LUM;
+ default:
+ return V4L2_YCBCR_ENC_DEFAULT;
+ }
+}
+
+int iris_hfi_core_init(struct iris_core *core)
+{
+ const struct iris_hfi_command_ops *hfi_ops = core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->sys_init(core);
+ if (ret)
+ return ret;
+
+ ret = hfi_ops->sys_image_version(core);
+ if (ret)
+ return ret;
+
+ return hfi_ops->sys_interframe_powercollapse(core);
+}
+
+irqreturn_t iris_hfi_isr(int irq, void *data)
+{
+ disable_irq_nosync(irq);
+
+ return IRQ_WAKE_THREAD;
+}
+
+irqreturn_t iris_hfi_isr_handler(int irq, void *data)
+{
+ struct iris_core *core = data;
+
+ if (!core)
+ return IRQ_NONE;
+
+ mutex_lock(&core->lock);
+ pm_runtime_mark_last_busy(core->dev);
+ iris_vpu_clear_interrupt(core);
+ mutex_unlock(&core->lock);
+
+ core->hfi_response_ops->hfi_response_handler(core);
+
+ if (!iris_vpu_watchdog(core, core->intr_status))
+ enable_irq(irq);
+
+ return IRQ_HANDLED;
+}
+
+int iris_hfi_pm_suspend(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_vpu_prepare_pc(core);
+ if (ret) {
+ pm_runtime_mark_last_busy(core->dev);
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ ret = iris_set_hw_state(core, false);
+ if (ret)
+ goto error;
+
+ iris_vpu_power_off(core);
+
+ return 0;
+
+error:
+ dev_err(core->dev, "failed to suspend\n");
+
+ return ret;
+}
+
+int iris_hfi_pm_resume(struct iris_core *core)
+{
+ const struct iris_hfi_command_ops *ops = core->hfi_ops;
+ int ret;
+
+ ret = iris_vpu_power_on(core);
+ if (ret)
+ goto error;
+
+ ret = iris_set_hw_state(core, true);
+ if (ret)
+ goto err_power_off;
+
+ ret = iris_vpu_boot_firmware(core);
+ if (ret)
+ goto err_suspend_hw;
+
+ ret = ops->sys_interframe_powercollapse(core);
+ if (ret)
+ goto err_suspend_hw;
+
+ return 0;
+
+err_suspend_hw:
+ iris_set_hw_state(core, false);
+err_power_off:
+ iris_vpu_power_off(core);
+error:
+ dev_err(core->dev, "failed to resume\n");
+
+ return -EBUSY;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h
new file mode 100644
index 000000000000..b2c541367fc6
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_COMMON_H__
+#define __IRIS_HFI_COMMON_H__
+
+#include <linux/types.h>
+#include <media/v4l2-device.h>
+
+#include "iris_buffer.h"
+
+struct iris_inst;
+struct iris_core;
+
+enum hfi_packet_port_type {
+ HFI_PORT_NONE = 0x00000000,
+ HFI_PORT_BITSTREAM = 0x00000001,
+ HFI_PORT_RAW = 0x00000002,
+};
+
+enum hfi_packet_payload_info {
+ HFI_PAYLOAD_NONE = 0x00000000,
+ HFI_PAYLOAD_U32 = 0x00000001,
+ HFI_PAYLOAD_S32 = 0x00000002,
+ HFI_PAYLOAD_U64 = 0x00000003,
+ HFI_PAYLOAD_S64 = 0x00000004,
+ HFI_PAYLOAD_STRUCTURE = 0x00000005,
+ HFI_PAYLOAD_BLOB = 0x00000006,
+ HFI_PAYLOAD_STRING = 0x00000007,
+ HFI_PAYLOAD_Q16 = 0x00000008,
+ HFI_PAYLOAD_U32_ENUM = 0x00000009,
+ HFI_PAYLOAD_32_PACKED = 0x0000000a,
+ HFI_PAYLOAD_U32_ARRAY = 0x0000000b,
+ HFI_PAYLOAD_S32_ARRAY = 0x0000000c,
+ HFI_PAYLOAD_64_PACKED = 0x0000000d,
+};
+
+enum hfi_packet_host_flags {
+ HFI_HOST_FLAGS_NONE = 0x00000000,
+ HFI_HOST_FLAGS_INTR_REQUIRED = 0x00000001,
+ HFI_HOST_FLAGS_RESPONSE_REQUIRED = 0x00000002,
+ HFI_HOST_FLAGS_NON_DISCARDABLE = 0x00000004,
+ HFI_HOST_FLAGS_GET_PROPERTY = 0x00000008,
+};
+
+enum hfi_color_primaries {
+ HFI_PRIMARIES_RESERVED = 0,
+ HFI_PRIMARIES_BT709 = 1,
+ HFI_PRIMARIES_UNSPECIFIED = 2,
+ HFI_PRIMARIES_BT470_SYSTEM_M = 4,
+ HFI_PRIMARIES_BT470_SYSTEM_BG = 5,
+ HFI_PRIMARIES_BT601_525 = 6,
+ HFI_PRIMARIES_SMPTE_ST240M = 7,
+ HFI_PRIMARIES_GENERIC_FILM = 8,
+ HFI_PRIMARIES_BT2020 = 9,
+ HFI_PRIMARIES_SMPTE_ST428_1 = 10,
+ HFI_PRIMARIES_SMPTE_RP431_2 = 11,
+ HFI_PRIMARIES_SMPTE_EG431_1 = 12,
+ HFI_PRIMARIES_SMPTE_EBU_TECH = 22,
+};
+
+enum hfi_transfer_characteristics {
+ HFI_TRANSFER_RESERVED = 0,
+ HFI_TRANSFER_BT709 = 1,
+ HFI_TRANSFER_UNSPECIFIED = 2,
+ HFI_TRANSFER_BT470_SYSTEM_M = 4,
+ HFI_TRANSFER_BT470_SYSTEM_BG = 5,
+ HFI_TRANSFER_BT601_525_OR_625 = 6,
+ HFI_TRANSFER_SMPTE_ST240M = 7,
+ HFI_TRANSFER_LINEAR = 8,
+ HFI_TRANSFER_LOG_100_1 = 9,
+ HFI_TRANSFER_LOG_SQRT = 10,
+ HFI_TRANSFER_XVYCC = 11,
+ HFI_TRANSFER_BT1361_0 = 12,
+ HFI_TRANSFER_SRGB_SYCC = 13,
+ HFI_TRANSFER_BT2020_14 = 14,
+ HFI_TRANSFER_BT2020_15 = 15,
+ HFI_TRANSFER_SMPTE_ST2084_PQ = 16,
+ HFI_TRANSFER_SMPTE_ST428_1 = 17,
+ HFI_TRANSFER_BT2100_2_HLG = 18,
+};
+
+enum hfi_matrix_coefficients {
+ HFI_MATRIX_COEFF_SRGB_SMPTE_ST428_1 = 0,
+ HFI_MATRIX_COEFF_BT709 = 1,
+ HFI_MATRIX_COEFF_UNSPECIFIED = 2,
+ HFI_MATRIX_COEFF_RESERVED = 3,
+ HFI_MATRIX_COEFF_FCC_TITLE_47 = 4,
+ HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625 = 5,
+ HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625 = 6,
+ HFI_MATRIX_COEFF_SMPTE_ST240 = 7,
+ HFI_MATRIX_COEFF_YCGCO = 8,
+ HFI_MATRIX_COEFF_BT2020_NON_CONSTANT = 9,
+ HFI_MATRIX_COEFF_BT2020_CONSTANT = 10,
+ HFI_MATRIX_COEFF_SMPTE_ST2085 = 11,
+ HFI_MATRIX_COEFF_SMPTE_CHROM_DERV_NON_CONSTANT = 12,
+ HFI_MATRIX_COEFF_SMPTE_CHROM_DERV_CONSTANT = 13,
+ HFI_MATRIX_COEFF_BT2100 = 14,
+};
+
+struct iris_hfi_prop_type_handle {
+ u32 type;
+ int (*handle)(struct iris_inst *inst);
+};
+
+struct iris_hfi_command_ops {
+ int (*sys_init)(struct iris_core *core);
+ int (*sys_image_version)(struct iris_core *core);
+ int (*sys_interframe_powercollapse)(struct iris_core *core);
+ int (*sys_pc_prep)(struct iris_core *core);
+ int (*session_set_config_params)(struct iris_inst *inst, u32 plane);
+ int (*session_set_property)(struct iris_inst *inst,
+ u32 packet_type, u32 flag, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size);
+ int (*session_open)(struct iris_inst *inst);
+ int (*session_start)(struct iris_inst *inst, u32 plane);
+ int (*session_queue_buf)(struct iris_inst *inst, struct iris_buffer *buffer);
+ int (*session_release_buf)(struct iris_inst *inst, struct iris_buffer *buffer);
+ int (*session_pause)(struct iris_inst *inst, u32 plane);
+ int (*session_resume_drc)(struct iris_inst *inst, u32 plane);
+ int (*session_stop)(struct iris_inst *inst, u32 plane);
+ int (*session_drain)(struct iris_inst *inst, u32 plane);
+ int (*session_resume_drain)(struct iris_inst *inst, u32 plane);
+ int (*session_close)(struct iris_inst *inst);
+};
+
+struct iris_hfi_response_ops {
+ void (*hfi_response_handler)(struct iris_core *core);
+};
+
+struct hfi_subscription_params {
+ u32 bitstream_resolution;
+ u32 crop_offsets[2];
+ u32 bit_depth;
+ u32 coded_frames;
+ u32 fw_min_count;
+ u32 pic_order_cnt;
+ u32 color_info;
+ u32 profile;
+ u32 level;
+};
+
+u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries);
+u32 iris_hfi_get_v4l2_transfer_char(u32 hfi_characterstics);
+u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients);
+int iris_hfi_core_init(struct iris_core *core);
+int iris_hfi_pm_suspend(struct iris_core *core);
+int iris_hfi_pm_resume(struct iris_core *core);
+
+irqreturn_t iris_hfi_isr(int irq, void *data);
+irqreturn_t iris_hfi_isr_handler(int irq, void *data);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h
new file mode 100644
index 000000000000..19b8e9054a75
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN1_H__
+#define __IRIS_HFI_GEN1_H__
+
+struct iris_core;
+struct iris_inst;
+
+void iris_hfi_gen1_command_ops_init(struct iris_core *core);
+void iris_hfi_gen1_response_ops_init(struct iris_core *core);
+struct iris_inst *iris_hfi_gen1_get_instance(void);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
new file mode 100644
index 000000000000..64f887d9a17d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -0,0 +1,826 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_hfi_gen1.h"
+#include "iris_hfi_gen1_defines.h"
+#include "iris_instance.h"
+#include "iris_vpu_buffer.h"
+
+static u32 iris_hfi_gen1_buf_type_from_driver(enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return HFI_BUFFER_INPUT;
+ case BUF_OUTPUT:
+ return HFI_BUFFER_OUTPUT;
+ case BUF_PERSIST:
+ return HFI_BUFFER_INTERNAL_PERSIST_1;
+ case BUF_BIN:
+ return HFI_BUFFER_INTERNAL_SCRATCH;
+ case BUF_SCRATCH_1:
+ return HFI_BUFFER_INTERNAL_SCRATCH_1;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int iris_hfi_gen1_sys_init(struct iris_core *core)
+{
+ struct hfi_sys_init_pkt sys_init_pkt;
+
+ sys_init_pkt.hdr.size = sizeof(sys_init_pkt);
+ sys_init_pkt.hdr.pkt_type = HFI_CMD_SYS_INIT;
+ sys_init_pkt.arch_type = HFI_VIDEO_ARCH_OX;
+
+ return iris_hfi_queue_cmd_write_locked(core, &sys_init_pkt, sys_init_pkt.hdr.size);
+}
+
+static int iris_hfi_gen1_sys_image_version(struct iris_core *core)
+{
+ struct hfi_sys_get_property_pkt packet;
+
+ packet.hdr.size = sizeof(packet);
+ packet.hdr.pkt_type = HFI_CMD_SYS_GET_PROPERTY;
+ packet.num_properties = 1;
+ packet.data = HFI_PROPERTY_SYS_IMAGE_VERSION;
+
+ return iris_hfi_queue_cmd_write_locked(core, &packet, packet.hdr.size);
+}
+
+static int iris_hfi_gen1_sys_interframe_powercollapse(struct iris_core *core)
+{
+ struct hfi_sys_set_property_pkt *pkt;
+ struct hfi_enable *hfi;
+ u32 packet_size;
+ int ret;
+
+ packet_size = struct_size(pkt, data, 1) + sizeof(*hfi);
+ pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+
+ hfi = (struct hfi_enable *)&pkt->data[1];
+
+ pkt->hdr.size = packet_size;
+ pkt->hdr.pkt_type = HFI_CMD_SYS_SET_PROPERTY;
+ pkt->num_properties = 1;
+ pkt->data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL;
+ hfi->enable = true;
+
+ ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt->hdr.size);
+ kfree(pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_sys_pc_prep(struct iris_core *core)
+{
+ struct hfi_sys_pc_prep_pkt pkt;
+
+ pkt.hdr.size = sizeof(struct hfi_sys_pc_prep_pkt);
+ pkt.hdr.pkt_type = HFI_CMD_SYS_PC_PREP;
+
+ return iris_hfi_queue_cmd_write_locked(core, &pkt, pkt.hdr.size);
+}
+
+static int iris_hfi_gen1_session_open(struct iris_inst *inst)
+{
+ struct hfi_session_open_pkt packet;
+ int ret;
+
+ if (inst->state != IRIS_INST_DEINIT)
+ return -EALREADY;
+
+ packet.shdr.hdr.size = sizeof(struct hfi_session_open_pkt);
+ packet.shdr.hdr.pkt_type = HFI_CMD_SYS_SESSION_INIT;
+ packet.shdr.session_id = inst->session_id;
+ packet.session_domain = HFI_SESSION_TYPE_DEC;
+ packet.session_codec = HFI_VIDEO_CODEC_H264;
+
+ reinit_completion(&inst->completion);
+
+ ret = iris_hfi_queue_cmd_write(inst->core, &packet, packet.shdr.hdr.size);
+ if (ret)
+ return ret;
+
+ return iris_wait_for_session_response(inst, false);
+}
+
+static void iris_hfi_gen1_packet_session_cmd(struct iris_inst *inst,
+ struct hfi_session_pkt *packet,
+ u32 ptype)
+{
+ packet->shdr.hdr.size = sizeof(*packet);
+ packet->shdr.hdr.pkt_type = ptype;
+ packet->shdr.session_id = inst->session_id;
+}
+
+static int iris_hfi_gen1_session_close(struct iris_inst *inst)
+{
+ struct hfi_session_pkt packet;
+
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SYS_SESSION_END);
+
+ return iris_hfi_queue_cmd_write(inst->core, &packet, packet.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_session_start(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ struct hfi_session_pkt packet;
+ int ret;
+
+ if (!V4L2_TYPE_IS_OUTPUT(plane))
+ return 0;
+
+ if (inst->sub_state & IRIS_INST_SUB_LOAD_RESOURCES)
+ return 0;
+
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_LOAD_RESOURCES);
+
+ ret = iris_hfi_queue_cmd_write(core, &packet, packet.shdr.hdr.size);
+ if (ret)
+ return ret;
+
+ ret = iris_wait_for_session_response(inst, false);
+ if (ret)
+ return ret;
+
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_START);
+
+ ret = iris_hfi_queue_cmd_write(core, &packet, packet.shdr.hdr.size);
+ if (ret)
+ return ret;
+
+ ret = iris_wait_for_session_response(inst, false);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_LOAD_RESOURCES);
+}
+
+static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane)
+{
+ struct hfi_session_flush_pkt flush_pkt;
+ struct iris_core *core = inst->core;
+ struct hfi_session_pkt pkt;
+ u32 flush_type = 0;
+ int ret = 0;
+
+ if ((V4L2_TYPE_IS_OUTPUT(plane) &&
+ inst->state == IRIS_INST_INPUT_STREAMING) ||
+ (V4L2_TYPE_IS_CAPTURE(plane) &&
+ inst->state == IRIS_INST_OUTPUT_STREAMING) ||
+ inst->state == IRIS_INST_ERROR) {
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &pkt, HFI_CMD_SESSION_STOP);
+ ret = iris_hfi_queue_cmd_write(core, &pkt, pkt.shdr.hdr.size);
+ if (!ret)
+ ret = iris_wait_for_session_response(inst, false);
+
+ reinit_completion(&inst->completion);
+ iris_hfi_gen1_packet_session_cmd(inst, &pkt, HFI_CMD_SESSION_RELEASE_RESOURCES);
+ ret = iris_hfi_queue_cmd_write(core, &pkt, pkt.shdr.hdr.size);
+ if (!ret)
+ ret = iris_wait_for_session_response(inst, false);
+
+ iris_inst_change_sub_state(inst, IRIS_INST_SUB_LOAD_RESOURCES, 0);
+
+ iris_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ VB2_BUF_STATE_ERROR);
+ iris_helper_buffers_done(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ VB2_BUF_STATE_ERROR);
+ } else if (inst->state == IRIS_INST_STREAMING) {
+ if (V4L2_TYPE_IS_OUTPUT(plane))
+ flush_type = HFI_FLUSH_ALL;
+ else if (V4L2_TYPE_IS_CAPTURE(plane))
+ flush_type = HFI_FLUSH_OUTPUT;
+
+ reinit_completion(&inst->flush_completion);
+
+ flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
+ flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+ flush_pkt.shdr.session_id = inst->session_id;
+ flush_pkt.flush_type = flush_type;
+
+ ret = iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ if (!ret)
+ ret = iris_wait_for_session_response(inst, true);
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_continue(struct iris_inst *inst, u32 plane)
+{
+ struct hfi_session_pkt packet;
+
+ iris_hfi_gen1_packet_session_cmd(inst, &packet, HFI_CMD_SESSION_CONTINUE);
+
+ return iris_hfi_queue_cmd_write(inst->core, &packet, packet.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_queue_input_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_empty_buffer_compressed_pkt ip_pkt;
+
+ ip_pkt.shdr.hdr.size = sizeof(struct hfi_session_empty_buffer_compressed_pkt);
+ ip_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+ ip_pkt.shdr.session_id = inst->session_id;
+ ip_pkt.time_stamp_hi = upper_32_bits(buf->timestamp);
+ ip_pkt.time_stamp_lo = lower_32_bits(buf->timestamp);
+ ip_pkt.flags = buf->flags;
+ ip_pkt.mark_target = 0;
+ ip_pkt.mark_data = 0;
+ ip_pkt.offset = buf->data_offset;
+ ip_pkt.alloc_len = buf->buffer_size;
+ ip_pkt.filled_len = buf->data_size;
+ ip_pkt.input_tag = buf->index;
+ ip_pkt.packet_buffer = buf->device_addr;
+
+ return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_queue_output_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_fill_buffer_pkt op_pkt;
+
+ op_pkt.shdr.hdr.size = sizeof(struct hfi_session_fill_buffer_pkt);
+ op_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FILL_BUFFER;
+ op_pkt.shdr.session_id = inst->session_id;
+ op_pkt.output_tag = buf->index;
+ op_pkt.packet_buffer = buf->device_addr;
+ op_pkt.extradata_buffer = 0;
+ op_pkt.alloc_len = buf->buffer_size;
+ op_pkt.filled_len = buf->data_size;
+ op_pkt.offset = buf->data_offset;
+ op_pkt.data = 0;
+
+ if (buf->type == BUF_OUTPUT && iris_split_mode_enabled(inst))
+ op_pkt.stream_id = 1;
+ else
+ op_pkt.stream_id = 0;
+
+ return iris_hfi_queue_cmd_write(inst->core, &op_pkt, op_pkt.shdr.hdr.size);
+}
+
+static int iris_hfi_gen1_queue_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_set_buffers_pkt *int_pkt;
+ u32 buffer_type, i;
+ u32 packet_size;
+ int ret;
+
+ packet_size = struct_size(int_pkt, buffer_info, 1);
+ int_pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!int_pkt)
+ return -ENOMEM;
+
+ int_pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_BUFFERS;
+ int_pkt->shdr.session_id = inst->session_id;
+ int_pkt->buffer_size = buf->buffer_size;
+ int_pkt->min_buffer_size = buf->buffer_size;
+ int_pkt->num_buffers = 1;
+ int_pkt->extradata_size = 0;
+ int_pkt->shdr.hdr.size = packet_size;
+ for (i = 0; i < int_pkt->num_buffers; i++)
+ int_pkt->buffer_info[i] = buf->device_addr;
+ buffer_type = iris_hfi_gen1_buf_type_from_driver(buf->type);
+ if (buffer_type == -EINVAL) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ int_pkt->buffer_type = buffer_type;
+ ret = iris_hfi_queue_cmd_write(inst->core, int_pkt, int_pkt->shdr.hdr.size);
+
+exit:
+ kfree(int_pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ switch (buf->type) {
+ case BUF_INPUT:
+ return iris_hfi_gen1_queue_input_buffer(inst, buf);
+ case BUF_OUTPUT:
+ case BUF_DPB:
+ return iris_hfi_gen1_queue_output_buffer(inst, buf);
+ case BUF_PERSIST:
+ case BUF_BIN:
+ case BUF_SCRATCH_1:
+ return iris_hfi_gen1_queue_internal_buffer(inst, buf);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int iris_hfi_gen1_session_unset_buffers(struct iris_inst *inst, struct iris_buffer *buf)
+{
+ struct hfi_session_release_buffer_pkt *pkt;
+ u32 packet_size, buffer_type, i;
+ int ret;
+
+ buffer_type = iris_hfi_gen1_buf_type_from_driver(buf->type);
+ if (buffer_type == -EINVAL)
+ return -EINVAL;
+
+ if (buffer_type == HFI_BUFFER_INPUT)
+ return 0;
+
+ packet_size = sizeof(*pkt) + sizeof(struct hfi_buffer_info);
+ pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_RELEASE_BUFFERS;
+ pkt->shdr.session_id = inst->session_id;
+ pkt->buffer_size = buf->buffer_size;
+ pkt->num_buffers = 1;
+
+ if (buffer_type == HFI_BUFFER_OUTPUT ||
+ buffer_type == HFI_BUFFER_OUTPUT2) {
+ struct hfi_buffer_info *bi;
+
+ bi = (struct hfi_buffer_info *)pkt->buffer_info;
+ for (i = 0; i < pkt->num_buffers; i++) {
+ bi->buffer_addr = buf->device_addr;
+ bi->extradata_addr = 0;
+ }
+ pkt->shdr.hdr.size = packet_size;
+ } else {
+ for (i = 0; i < pkt->num_buffers; i++)
+ pkt->buffer_info[i] = buf->device_addr;
+ pkt->extradata_size = 0;
+ pkt->shdr.hdr.size =
+ sizeof(struct hfi_session_set_buffers_pkt) +
+ ((pkt->num_buffers) * sizeof(u32));
+ }
+
+ pkt->response_req = true;
+ pkt->buffer_type = buffer_type;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, pkt, pkt->shdr.hdr.size);
+ if (ret)
+ goto exit;
+
+ ret = iris_wait_for_session_response(inst, false);
+
+exit:
+ kfree(pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_drain(struct iris_inst *inst, u32 plane)
+{
+ struct hfi_session_empty_buffer_compressed_pkt ip_pkt = {0};
+
+ ip_pkt.shdr.hdr.size = sizeof(struct hfi_session_empty_buffer_compressed_pkt);
+ ip_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+ ip_pkt.shdr.session_id = inst->session_id;
+ ip_pkt.flags = HFI_BUFFERFLAG_EOS;
+
+ return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size);
+}
+
+static int
+iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *packet,
+ struct iris_inst *inst, u32 ptype, void *pdata)
+{
+ void *prop_data = &packet->data[1];
+
+ packet->shdr.hdr.size = sizeof(*packet);
+ packet->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY;
+ packet->shdr.session_id = inst->session_id;
+ packet->num_properties = 1;
+ packet->data[0] = ptype;
+
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_FRAME_SIZE: {
+ struct hfi_framesize *in = pdata, *fsize = prop_data;
+
+ fsize->buffer_type = in->buffer_type;
+ fsize->height = in->height;
+ fsize->width = in->width;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*fsize);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: {
+ struct hfi_videocores_usage_type *in = pdata, *cu = prop_data;
+
+ cu->video_core_enable_mask = in->video_core_enable_mask;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*cu);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: {
+ struct hfi_uncompressed_format_select *in = pdata;
+ struct hfi_uncompressed_format_select *hfi = prop_data;
+
+ hfi->buffer_type = in->buffer_type;
+ hfi->format = in->format;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*hfi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: {
+ struct hfi_uncompressed_plane_actual_constraints_info *info = prop_data;
+
+ info->buffer_type = HFI_BUFFER_OUTPUT2;
+ info->num_planes = 2;
+ info->plane_format[0].stride_multiples = 128;
+ info->plane_format[0].max_stride = 8192;
+ info->plane_format[0].min_plane_buffer_height_multiple = 32;
+ info->plane_format[0].buffer_alignment = 256;
+ if (info->num_planes > 1) {
+ info->plane_format[1].stride_multiples = 128;
+ info->plane_format[1].max_stride = 8192;
+ info->plane_format[1].min_plane_buffer_height_multiple = 16;
+ info->plane_format[1].buffer_alignment = 256;
+ }
+
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*info);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: {
+ struct hfi_buffer_count_actual *in = pdata;
+ struct hfi_buffer_count_actual *count = prop_data;
+
+ count->type = in->type;
+ count->count_actual = in->count_actual;
+ count->count_min_host = in->count_min_host;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*count);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: {
+ struct hfi_multi_stream *in = pdata;
+ struct hfi_multi_stream *multi = prop_data;
+
+ multi->buffer_type = in->buffer_type;
+ multi->enable = in->enable;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*multi);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: {
+ struct hfi_buffer_size_actual *in = pdata, *sz = prop_data;
+
+ sz->size = in->size;
+ sz->type = in->type;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*sz);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_WORK_ROUTE: {
+ struct hfi_video_work_route *wr = prop_data;
+ u32 *in = pdata;
+
+ wr->video_work_route = *in;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*wr);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_WORK_MODE: {
+ struct hfi_video_work_mode *wm = prop_data;
+ u32 *in = pdata;
+
+ wm->video_work_mode = *in;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: {
+ struct hfi_enable *en = prop_data;
+ u32 *in = pdata;
+
+ en->enable = *in;
+ packet->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hfi_gen1_set_property(struct iris_inst *inst, u32 packet_type,
+ void *payload, u32 payload_size)
+{
+ struct hfi_session_set_property_pkt *pkt;
+ u32 packet_size;
+ int ret;
+
+ packet_size = sizeof(*pkt) + sizeof(u32) + payload_size;
+ pkt = kzalloc(packet_size, GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+
+ ret = iris_hfi_gen1_packet_session_set_property(pkt, inst, packet_type, payload);
+ if (ret == -EOPNOTSUPP) {
+ ret = 0;
+ goto exit;
+ }
+ if (ret)
+ goto exit;
+
+ ret = iris_hfi_queue_cmd_write(inst->core, pkt, pkt->shdr.hdr.size);
+
+exit:
+ kfree(pkt);
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_set_property(struct iris_inst *inst, u32 packet_type,
+ u32 flag, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size)
+{
+ return hfi_gen1_set_property(inst, packet_type, payload, payload_size);
+}
+
+static int iris_hfi_gen1_set_resolution(struct iris_inst *inst)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE;
+ struct hfi_framesize fs;
+ int ret;
+
+ fs.buffer_type = HFI_BUFFER_INPUT;
+ fs.width = inst->fmt_src->fmt.pix_mp.width;
+ fs.height = inst->fmt_src->fmt.pix_mp.height;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs));
+ if (ret)
+ return ret;
+
+ fs.buffer_type = HFI_BUFFER_OUTPUT2;
+ fs.width = inst->fmt_dst->fmt.pix_mp.width;
+ fs.height = inst->fmt_dst->fmt.pix_mp.height;
+
+ return hfi_gen1_set_property(inst, ptype, &fs, sizeof(fs));
+}
+
+static int iris_hfi_gen1_decide_core(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE;
+ struct hfi_videocores_usage_type cu;
+
+ cu.video_core_enable_mask = HFI_CORE_ID_1;
+
+ return hfi_gen1_set_property(inst, ptype, &cu, sizeof(cu));
+}
+
+static int iris_hfi_gen1_set_raw_format(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
+ u32 pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ struct hfi_uncompressed_format_select fmt;
+ int ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ fmt.buffer_type = HFI_BUFFER_OUTPUT;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12_UBWC : 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
+ if (ret)
+ return ret;
+
+ fmt.buffer_type = HFI_BUFFER_OUTPUT2;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
+ } else {
+ fmt.buffer_type = HFI_BUFFER_OUTPUT;
+ fmt.format = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FORMAT_NV12 : 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &fmt, sizeof(fmt));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_set_format_constraints(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO;
+ struct hfi_uncompressed_plane_actual_constraints_info pconstraint;
+
+ pconstraint.buffer_type = HFI_BUFFER_OUTPUT2;
+ pconstraint.num_planes = 2;
+ pconstraint.plane_format[0].stride_multiples = 128;
+ pconstraint.plane_format[0].max_stride = 8192;
+ pconstraint.plane_format[0].min_plane_buffer_height_multiple = 32;
+ pconstraint.plane_format[0].buffer_alignment = 256;
+
+ pconstraint.plane_format[1].stride_multiples = 128;
+ pconstraint.plane_format[1].max_stride = 8192;
+ pconstraint.plane_format[1].min_plane_buffer_height_multiple = 16;
+ pconstraint.plane_format[1].buffer_alignment = 256;
+
+ return hfi_gen1_set_property(inst, ptype, &pconstraint, sizeof(pconstraint));
+}
+
+static int iris_hfi_gen1_set_num_bufs(struct iris_inst *inst)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL;
+ struct hfi_buffer_count_actual buf_count;
+ int ret;
+
+ buf_count.type = HFI_BUFFER_INPUT;
+ buf_count.count_actual = VIDEO_MAX_FRAME;
+ buf_count.count_min_host = VIDEO_MAX_FRAME;
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ if (ret)
+ return ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ buf_count.type = HFI_BUFFER_OUTPUT;
+ buf_count.count_actual = VIDEO_MAX_FRAME;
+ buf_count.count_min_host = VIDEO_MAX_FRAME;
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ if (ret)
+ return ret;
+
+ buf_count.type = HFI_BUFFER_OUTPUT2;
+ buf_count.count_actual = iris_vpu_buf_count(inst, BUF_DPB);
+ buf_count.count_min_host = iris_vpu_buf_count(inst, BUF_DPB);
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ } else {
+ buf_count.type = HFI_BUFFER_OUTPUT;
+ buf_count.count_actual = VIDEO_MAX_FRAME;
+ buf_count.count_min_host = VIDEO_MAX_FRAME;
+
+ ret = hfi_gen1_set_property(inst, ptype, &buf_count, sizeof(buf_count));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_set_multistream(struct iris_inst *inst)
+{
+ u32 ptype = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+ struct hfi_multi_stream multi = {0};
+ int ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ multi.buffer_type = HFI_BUFFER_OUTPUT;
+ multi.enable = 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ if (ret)
+ return ret;
+
+ multi.buffer_type = HFI_BUFFER_OUTPUT2;
+ multi.enable = 1;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ } else {
+ multi.buffer_type = HFI_BUFFER_OUTPUT;
+ multi.enable = 1;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ if (ret)
+ return ret;
+
+ multi.buffer_type = HFI_BUFFER_OUTPUT2;
+ multi.enable = 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &multi, sizeof(multi));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_set_bufsize(struct iris_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL;
+ struct hfi_buffer_size_actual bufsz;
+ int ret;
+
+ if (iris_split_mode_enabled(inst)) {
+ bufsz.type = HFI_BUFFER_OUTPUT;
+ bufsz.size = iris_vpu_buf_size(inst, BUF_DPB);
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ if (ret)
+ return ret;
+
+ bufsz.type = HFI_BUFFER_OUTPUT2;
+ bufsz.size = inst->buffers[BUF_OUTPUT].size;
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ } else {
+ bufsz.type = HFI_BUFFER_OUTPUT;
+ bufsz.size = inst->buffers[BUF_OUTPUT].size;
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ if (ret)
+ return ret;
+
+ bufsz.type = HFI_BUFFER_OUTPUT2;
+ bufsz.size = 0;
+
+ ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ u32 config_params_size, i, j;
+ const u32 *config_params;
+ int ret;
+
+ static const struct iris_hfi_prop_type_handle prop_type_handle_inp_arr[] = {
+ {HFI_PROPERTY_PARAM_FRAME_SIZE,
+ iris_hfi_gen1_set_resolution},
+ {HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE,
+ iris_hfi_gen1_decide_core},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ iris_hfi_gen1_set_raw_format},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+ iris_hfi_gen1_set_format_constraints},
+ {HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
+ iris_hfi_gen1_set_num_bufs},
+ {HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
+ iris_hfi_gen1_set_multistream},
+ {HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL,
+ iris_hfi_gen1_set_bufsize},
+ };
+
+ static const struct iris_hfi_prop_type_handle prop_type_handle_out_arr[] = {
+ {HFI_PROPERTY_PARAM_FRAME_SIZE,
+ iris_hfi_gen1_set_resolution},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ iris_hfi_gen1_set_raw_format},
+ {HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+ iris_hfi_gen1_set_format_constraints},
+ {HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
+ iris_hfi_gen1_set_num_bufs},
+ {HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
+ iris_hfi_gen1_set_multistream},
+ {HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL,
+ iris_hfi_gen1_set_bufsize},
+ };
+
+ config_params = core->iris_platform_data->input_config_params;
+ config_params_size = core->iris_platform_data->input_config_params_size;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ for (i = 0; i < config_params_size; i++) {
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_inp_arr); j++) {
+ if (prop_type_handle_inp_arr[j].type == config_params[i]) {
+ ret = prop_type_handle_inp_arr[j].handle(inst);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ }
+ } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+ for (i = 0; i < config_params_size; i++) {
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_out_arr); j++) {
+ if (prop_type_handle_out_arr[j].type == config_params[i]) {
+ ret = prop_type_handle_out_arr[j].handle(inst);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = {
+ .sys_init = iris_hfi_gen1_sys_init,
+ .sys_image_version = iris_hfi_gen1_sys_image_version,
+ .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse,
+ .sys_pc_prep = iris_hfi_gen1_sys_pc_prep,
+ .session_open = iris_hfi_gen1_session_open,
+ .session_set_config_params = iris_hfi_gen1_session_set_config_params,
+ .session_set_property = iris_hfi_gen1_session_set_property,
+ .session_start = iris_hfi_gen1_session_start,
+ .session_queue_buf = iris_hfi_gen1_session_queue_buffer,
+ .session_release_buf = iris_hfi_gen1_session_unset_buffers,
+ .session_resume_drc = iris_hfi_gen1_session_continue,
+ .session_stop = iris_hfi_gen1_session_stop,
+ .session_drain = iris_hfi_gen1_session_drain,
+ .session_close = iris_hfi_gen1_session_close,
+};
+
+void iris_hfi_gen1_command_ops_init(struct iris_core *core)
+{
+ core->hfi_ops = &iris_hfi_gen1_command_ops;
+}
+
+struct iris_inst *iris_hfi_gen1_get_instance(void)
+{
+ return kzalloc(sizeof(struct iris_inst), GFP_KERNEL);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
new file mode 100644
index 000000000000..9f246816a286
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -0,0 +1,448 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN1_DEFINES_H__
+#define __IRIS_HFI_GEN1_DEFINES_H__
+
+#include <linux/types.h>
+
+#define HFI_VIDEO_ARCH_OX 0x1
+
+#define HFI_SESSION_TYPE_DEC 2
+
+#define HFI_VIDEO_CODEC_H264 0x00000002
+
+#define HFI_ERR_NONE 0x0
+
+#define HFI_CMD_SYS_INIT 0x10001
+#define HFI_CMD_SYS_PC_PREP 0x10002
+#define HFI_CMD_SYS_SET_PROPERTY 0x10005
+#define HFI_CMD_SYS_GET_PROPERTY 0x10006
+#define HFI_CMD_SYS_SESSION_INIT 0x10007
+#define HFI_CMD_SYS_SESSION_END 0x10008
+
+#define HFI_CMD_SESSION_SET_PROPERTY 0x11001
+#define HFI_CMD_SESSION_SET_BUFFERS 0x11002
+
+#define HFI_CMD_SESSION_LOAD_RESOURCES 0x211001
+#define HFI_CMD_SESSION_START 0x211002
+#define HFI_CMD_SESSION_STOP 0x211003
+#define HFI_CMD_SESSION_EMPTY_BUFFER 0x211004
+#define HFI_CMD_SESSION_FILL_BUFFER 0x211005
+#define HFI_CMD_SESSION_FLUSH 0x211008
+#define HFI_CMD_SESSION_RELEASE_BUFFERS 0x21100b
+#define HFI_CMD_SESSION_RELEASE_RESOURCES 0x21100c
+#define HFI_CMD_SESSION_CONTINUE 0x21100d
+
+#define HFI_ERR_SESSION_UNSUPPORTED_SETTING 0x1008
+#define HFI_ERR_SESSION_UNSUPPORTED_STREAM 0x100d
+#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE 0x1010
+#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR 0x1012
+#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED 0x1013
+
+#define HFI_EVENT_SYS_ERROR 0x1
+#define HFI_EVENT_SESSION_ERROR 0x2
+
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES 0x1000001
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES 0x1000002
+#define HFI_EVENT_SESSION_SEQUENCE_CHANGED 0x1000003
+
+#define HFI_BUFFERFLAG_EOS 0x00000001
+#define HFI_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
+
+#define HFI_FLUSH_OUTPUT 0x1000002
+#define HFI_FLUSH_OUTPUT2 0x1000003
+#define HFI_FLUSH_ALL 0x1000004
+
+#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e
+
+#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL 0x201001
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO 0x201002
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE 0x201008
+#define HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL 0x20100c
+
+#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS 0x202001
+
+#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER 0x1200001
+#define HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS 0x120300e
+#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY 0x1204004
+
+#define HFI_BUFFER_INPUT 0x1
+#define HFI_BUFFER_OUTPUT 0x2
+#define HFI_BUFFER_OUTPUT2 0x3
+#define HFI_BUFFER_INTERNAL_PERSIST_1 0x5
+#define HFI_BUFFER_INTERNAL_SCRATCH 0x6
+#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x7
+
+#define HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL 0x5
+#define HFI_PROPERTY_SYS_IMAGE_VERSION 0x6
+
+#define HFI_PROPERTY_PARAM_FRAME_SIZE 0x1001
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT 0x1003
+#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT 0x1005
+#define HFI_PROPERTY_PARAM_WORK_MODE 0x1015
+#define HFI_PROPERTY_PARAM_WORK_ROUTE 0x1017
+#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE 0x2002
+
+#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001
+#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH 0x1003007
+#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT 0x1003009
+#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE 0x100300a
+#define HFI_CORE_ID_1 1
+#define HFI_COLOR_FORMAT_NV12 0x02
+#define HFI_COLOR_FORMAT_NV12_UBWC 0x8002
+
+#define HFI_MSG_SYS_INIT 0x20001
+#define HFI_MSG_SYS_SESSION_INIT 0x20006
+#define HFI_MSG_SYS_SESSION_END 0x20007
+#define HFI_MSG_SYS_COV 0x20009
+#define HFI_MSG_SYS_PROPERTY_INFO 0x2000a
+
+#define HFI_MSG_EVENT_NOTIFY 0x21001
+#define HFI_MSG_SESSION_LOAD_RESOURCES 0x221001
+#define HFI_MSG_SESSION_START 0x221002
+#define HFI_MSG_SESSION_STOP 0x221003
+#define HFI_MSG_SESSION_FLUSH 0x221006
+#define HFI_MSG_SESSION_EMPTY_BUFFER 0x221007
+#define HFI_MSG_SESSION_FILL_BUFFER 0x221008
+#define HFI_MSG_SESSION_RELEASE_RESOURCES 0x22100a
+#define HFI_MSG_SESSION_RELEASE_BUFFERS 0x22100c
+
+#define HFI_PICTURE_I 0x00000001
+#define HFI_PICTURE_P 0x00000002
+#define HFI_PICTURE_B 0x00000004
+#define HFI_PICTURE_IDR 0x00000008
+#define HFI_FRAME_NOTCODED 0x7f002000
+#define HFI_FRAME_YUV 0x7f004000
+#define HFI_UNUSED_PICT 0x10000000
+
+struct hfi_pkt_hdr {
+ u32 size;
+ u32 pkt_type;
+};
+
+struct hfi_session_hdr_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 session_id;
+};
+
+struct hfi_session_open_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 session_domain;
+ u32 session_codec;
+};
+
+struct hfi_session_pkt {
+ struct hfi_session_hdr_pkt shdr;
+};
+
+struct hfi_sys_init_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 arch_type;
+};
+
+struct hfi_sys_set_property_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_sys_get_property_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 data;
+};
+
+struct hfi_session_set_property_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_sys_pc_prep_pkt {
+ struct hfi_pkt_hdr hdr;
+};
+
+struct hfi_session_set_buffers_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 extradata_size;
+ u32 min_buffer_size;
+ u32 num_buffers;
+ u32 buffer_info[];
+};
+
+struct hfi_session_empty_buffer_compressed_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 input_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data;
+};
+
+struct hfi_session_fill_buffer_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 stream_id;
+ u32 offset;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 output_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data;
+};
+
+struct hfi_session_flush_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 flush_type;
+};
+
+struct hfi_session_release_buffer_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 buffer_type;
+ u32 buffer_size;
+ u32 extradata_size;
+ u32 response_req;
+ u32 num_buffers;
+ u32 buffer_info[];
+};
+
+struct hfi_buffer_info {
+ u32 buffer_addr;
+ u32 extradata_addr;
+};
+
+struct hfi_msg_event_notify_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 event_id;
+ u32 event_data1;
+ u32 event_data2;
+ u32 ext_event_data[];
+};
+
+struct hfi_msg_sys_init_done_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 error_type;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_msg_session_hdr_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 error_type;
+};
+
+struct hfi_msg_session_init_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 num_properties;
+ u32 data[];
+};
+
+struct hfi_msg_sys_property_info_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 num_properties;
+ u32 property;
+ u8 data[];
+};
+
+struct hfi_msg_session_flush_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 flush_type;
+};
+
+struct hfi_enable {
+ u32 enable;
+};
+
+struct hfi_profile_level {
+ u32 profile;
+ u32 level;
+};
+
+struct hfi_framesize {
+ u32 buffer_type;
+ u32 width;
+ u32 height;
+};
+
+struct hfi_videocores_usage_type {
+ u32 video_core_enable_mask;
+};
+
+struct hfi_video_work_mode {
+ u32 video_work_mode;
+};
+
+struct hfi_video_work_route {
+ u32 video_work_route;
+};
+
+struct hfi_bit_depth {
+ u32 buffer_type;
+ u32 bit_depth;
+};
+
+struct hfi_pic_struct {
+ u32 progressive_only;
+};
+
+struct hfi_colour_space {
+ u32 colour_space;
+};
+
+struct hfi_extradata_input_crop {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
+struct hfi_dpb_counts {
+ u32 max_dpb_count;
+ u32 max_ref_frames;
+ u32 max_dec_buffering;
+ u32 max_reorder_frames;
+ u32 fw_min_count;
+};
+
+struct hfi_uncompressed_format_select {
+ u32 buffer_type;
+ u32 format;
+};
+
+struct hfi_uncompressed_plane_constraints {
+ u32 stride_multiples;
+ u32 max_stride;
+ u32 min_plane_buffer_height_multiple;
+ u32 buffer_alignment;
+};
+
+struct hfi_uncompressed_plane_actual_constraints_info {
+ u32 buffer_type;
+ u32 num_planes;
+ struct hfi_uncompressed_plane_constraints plane_format[2];
+};
+
+struct hfi_buffer_count_actual {
+ u32 type;
+ u32 count_actual;
+ u32 count_min_host;
+};
+
+struct hfi_buffer_size_actual {
+ u32 type;
+ u32 size;
+};
+
+struct hfi_multi_stream {
+ u32 buffer_type;
+ u32 enable;
+};
+
+struct hfi_buffer_requirements {
+ u32 type;
+ u32 size;
+ u32 region_size;
+ u32 hold_count;
+ u32 count_min;
+ u32 count_actual;
+ u32 contiguous;
+ u32 alignment;
+};
+
+struct hfi_event_data {
+ u32 error;
+ u32 height;
+ u32 width;
+ u32 event_type;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 tag;
+ u32 profile;
+ u32 level;
+ u32 bit_depth;
+ u32 pic_struct;
+ u32 colour_space;
+ u32 entropy_mode;
+ u32 buf_count;
+ struct {
+ u32 left, top;
+ u32 width, height;
+ } input_crop;
+};
+
+struct hfi_msg_session_empty_buffer_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 offset;
+ u32 filled_len;
+ u32 input_tag;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[];
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane0_pkt {
+ struct hfi_session_hdr_pkt shdr;
+ u32 stream_id;
+ u32 view_id;
+ u32 error_type;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u32 flags;
+ u32 mark_target;
+ u32 mark_data;
+ u32 stats;
+ u32 alloc_len;
+ u32 filled_len;
+ u32 offset;
+ u32 frame_width;
+ u32 frame_height;
+ u32 start_x_coord;
+ u32 start_y_coord;
+ u32 input_tag;
+ u32 input_tag2;
+ u32 output_tag;
+ u32 picture_type;
+ u32 packet_buffer;
+ u32 extradata_buffer;
+ u32 data[];
+};
+
+struct hfi_msg_session_release_buffers_done_pkt {
+ struct hfi_msg_session_hdr_pkt shdr;
+ u32 num_buffers;
+ u32 buffer_info[];
+};
+
+struct hfi_msg_sys_debug_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 msg_type;
+ u32 msg_size;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u8 msg_data[];
+};
+
+struct hfi_msg_sys_coverage_pkt {
+ struct hfi_pkt_hdr hdr;
+ u32 msg_size;
+ u32 time_stamp_hi;
+ u32 time_stamp_lo;
+ u8 msg_data[];
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
new file mode 100644
index 000000000000..b72d503dd740
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
@@ -0,0 +1,666 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_hfi_gen1.h"
+#include "iris_hfi_gen1_defines.h"
+#include "iris_instance.h"
+#include "iris_vdec.h"
+#include "iris_vpu_buffer.h"
+
+static void iris_hfi_gen1_read_changed_params(struct iris_inst *inst,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp;
+ u32 num_properties_changed = pkt->event_data2;
+ u8 *data_ptr = (u8 *)&pkt->ext_event_data[0];
+ u32 primaries, matrix_coeff, transfer_char;
+ struct hfi_dpb_counts *iris_vpu_dpb_count;
+ struct hfi_profile_level *profile_level;
+ struct hfi_buffer_requirements *bufreq;
+ struct hfi_extradata_input_crop *crop;
+ struct hfi_colour_space *colour_info;
+ struct iris_core *core = inst->core;
+ u32 colour_description_present_flag;
+ u32 video_signal_type_present_flag;
+ struct hfi_event_data event = {0};
+ struct hfi_bit_depth *pixel_depth;
+ struct hfi_pic_struct *pic_struct;
+ struct hfi_framesize *frame_sz;
+ struct vb2_queue *dst_q;
+ struct v4l2_ctrl *ctrl;
+ u32 full_range, ptype;
+
+ do {
+ ptype = *((u32 *)data_ptr);
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_FRAME_SIZE:
+ data_ptr += sizeof(u32);
+ frame_sz = (struct hfi_framesize *)data_ptr;
+ event.width = frame_sz->width;
+ event.height = frame_sz->height;
+ data_ptr += sizeof(*frame_sz);
+ break;
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
+ data_ptr += sizeof(u32);
+ profile_level = (struct hfi_profile_level *)data_ptr;
+ event.profile = profile_level->profile;
+ event.level = profile_level->level;
+ data_ptr += sizeof(*profile_level);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
+ data_ptr += sizeof(u32);
+ pixel_depth = (struct hfi_bit_depth *)data_ptr;
+ event.bit_depth = pixel_depth->bit_depth;
+ data_ptr += sizeof(*pixel_depth);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
+ data_ptr += sizeof(u32);
+ pic_struct = (struct hfi_pic_struct *)data_ptr;
+ event.pic_struct = pic_struct->progressive_only;
+ data_ptr += sizeof(*pic_struct);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
+ data_ptr += sizeof(u32);
+ colour_info = (struct hfi_colour_space *)data_ptr;
+ event.colour_space = colour_info->colour_space;
+ data_ptr += sizeof(*colour_info);
+ break;
+ case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
+ data_ptr += sizeof(u32);
+ event.entropy_mode = *(u32 *)data_ptr;
+ data_ptr += sizeof(u32);
+ break;
+ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+ data_ptr += sizeof(u32);
+ bufreq = (struct hfi_buffer_requirements *)data_ptr;
+ event.buf_count = bufreq->count_min;
+ data_ptr += sizeof(*bufreq);
+ break;
+ case HFI_INDEX_EXTRADATA_INPUT_CROP:
+ data_ptr += sizeof(u32);
+ crop = (struct hfi_extradata_input_crop *)data_ptr;
+ event.input_crop.left = crop->left;
+ event.input_crop.top = crop->top;
+ event.input_crop.width = crop->width;
+ event.input_crop.height = crop->height;
+ data_ptr += sizeof(*crop);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS:
+ data_ptr += sizeof(u32);
+ iris_vpu_dpb_count = (struct hfi_dpb_counts *)data_ptr;
+ event.buf_count = iris_vpu_dpb_count->fw_min_count;
+ data_ptr += sizeof(*iris_vpu_dpb_count);
+ break;
+ default:
+ break;
+ }
+ num_properties_changed--;
+ } while (num_properties_changed > 0);
+
+ pixmp_ip->width = event.width;
+ pixmp_ip->height = event.height;
+
+ pixmp_op->width = ALIGN(event.width, 128);
+ pixmp_op->height = ALIGN(event.height, 32);
+ pixmp_op->plane_fmt[0].bytesperline = ALIGN(event.width, 128);
+ pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+
+ matrix_coeff = FIELD_GET(GENMASK(7, 0), event.colour_space);
+ transfer_char = FIELD_GET(GENMASK(15, 8), event.colour_space);
+ primaries = FIELD_GET(GENMASK(23, 16), event.colour_space);
+ colour_description_present_flag = FIELD_GET(GENMASK(24, 24), event.colour_space);
+ full_range = FIELD_GET(GENMASK(25, 25), event.colour_space);
+ video_signal_type_present_flag = FIELD_GET(GENMASK(29, 29), event.colour_space);
+
+ pixmp_op->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pixmp_op->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pixmp_op->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pixmp_op->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ if (video_signal_type_present_flag) {
+ pixmp_op->quantization =
+ full_range ?
+ V4L2_QUANTIZATION_FULL_RANGE :
+ V4L2_QUANTIZATION_LIM_RANGE;
+ if (colour_description_present_flag) {
+ pixmp_op->colorspace =
+ iris_hfi_get_v4l2_color_primaries(primaries);
+ pixmp_op->xfer_func =
+ iris_hfi_get_v4l2_transfer_char(transfer_char);
+ pixmp_op->ycbcr_enc =
+ iris_hfi_get_v4l2_matrix_coefficients(matrix_coeff);
+ }
+ }
+
+ pixmp_ip->colorspace = pixmp_op->colorspace;
+ pixmp_ip->xfer_func = pixmp_op->xfer_func;
+ pixmp_ip->ycbcr_enc = pixmp_op->ycbcr_enc;
+ pixmp_ip->quantization = pixmp_op->quantization;
+
+ if (event.input_crop.width > 0 && event.input_crop.height > 0) {
+ inst->crop.left = event.input_crop.left;
+ inst->crop.top = event.input_crop.top;
+ inst->crop.width = event.input_crop.width;
+ inst->crop.height = event.input_crop.height;
+ } else {
+ inst->crop.left = 0;
+ inst->crop.top = 0;
+ inst->crop.width = event.width;
+ inst->crop.height = event.height;
+ }
+
+ inst->fw_min_count = event.buf_count;
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage;
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, inst->buffers[BUF_OUTPUT].min_count);
+
+ dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ dst_q->min_reqbufs_allocation = inst->buffers[BUF_OUTPUT].min_count;
+
+ if (event.bit_depth || !event.pic_struct) {
+ dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n",
+ event.bit_depth, event.pic_struct);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+}
+
+static void iris_hfi_gen1_event_seq_changed(struct iris_inst *inst,
+ struct hfi_msg_event_notify_pkt *pkt)
+{
+ struct hfi_session_flush_pkt flush_pkt;
+ u32 num_properties_changed;
+ int ret;
+
+ ret = iris_inst_sub_state_change_drc(inst);
+ if (ret)
+ return;
+
+ switch (pkt->event_data1) {
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUF_RESOURCES:
+ case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUF_RESOURCES:
+ break;
+ default:
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return;
+ }
+
+ num_properties_changed = pkt->event_data2;
+ if (!num_properties_changed) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return;
+ }
+
+ iris_hfi_gen1_read_changed_params(inst, pkt);
+
+ if (inst->state != IRIS_INST_ERROR) {
+ reinit_completion(&inst->flush_completion);
+
+ flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
+ flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+ flush_pkt.shdr.session_id = inst->session_id;
+ flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
+ iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ }
+
+ iris_vdec_src_change(inst);
+ iris_inst_sub_state_change_drc_last(inst);
+}
+
+static void
+iris_hfi_gen1_sys_event_notify(struct iris_core *core, void *packet)
+{
+ struct hfi_msg_event_notify_pkt *pkt = packet;
+ struct iris_inst *instance;
+
+ if (pkt->event_id == HFI_EVENT_SYS_ERROR)
+ dev_err(core->dev, "sys error (type: %x, session id:%x, data1:%x, data2:%x)\n",
+ pkt->event_id, pkt->shdr.session_id, pkt->event_data1,
+ pkt->event_data2);
+
+ core->state = IRIS_CORE_ERROR;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list)
+ iris_inst_change_state(instance, IRIS_INST_ERROR);
+ mutex_unlock(&core->lock);
+
+ schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10));
+}
+
+static void
+iris_hfi_gen1_event_session_error(struct iris_inst *inst, struct hfi_msg_event_notify_pkt *pkt)
+{
+ switch (pkt->event_data1) {
+ /* non fatal session errors */
+ case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
+ case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
+ case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
+ case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED:
+ dev_dbg(inst->core->dev, "session error: event id:%x, session id:%x\n",
+ pkt->event_data1, pkt->shdr.session_id);
+ break;
+ /* fatal session errors */
+ default:
+ /*
+ * firmware fills event_data2 as an additional information about the
+ * hfi command for which session error has ouccured.
+ */
+ dev_err(inst->core->dev,
+ "session error for command: %x, event id:%x, session id:%x\n",
+ pkt->event_data2, pkt->event_data1,
+ pkt->shdr.session_id);
+ iris_vb2_queue_error(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ break;
+ }
+}
+
+static void iris_hfi_gen1_session_event_notify(struct iris_inst *inst, void *packet)
+{
+ struct hfi_msg_event_notify_pkt *pkt = packet;
+
+ switch (pkt->event_id) {
+ case HFI_EVENT_SESSION_ERROR:
+ iris_hfi_gen1_event_session_error(inst, pkt);
+ break;
+ case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
+ iris_hfi_gen1_event_seq_changed(inst, pkt);
+ break;
+ default:
+ break;
+ }
+}
+
+static void iris_hfi_gen1_sys_init_done(struct iris_core *core, void *packet)
+{
+ struct hfi_msg_sys_init_done_pkt *pkt = packet;
+
+ if (pkt->error_type != HFI_ERR_NONE) {
+ core->state = IRIS_CORE_ERROR;
+ return;
+ }
+
+ complete(&core->core_init_done);
+}
+
+static void
+iris_hfi_gen1_sys_get_prop_image_version(struct iris_core *core,
+ struct hfi_msg_sys_property_info_pkt *pkt)
+{
+ int req_bytes = pkt->hdr.size - sizeof(*pkt);
+ char fw_version[IRIS_FW_VERSION_LENGTH];
+ u8 *str_image_version;
+ u32 i;
+
+ if (req_bytes < IRIS_FW_VERSION_LENGTH - 1 || !pkt->data[0] || pkt->num_properties > 1) {
+ dev_err(core->dev, "bad packet\n");
+ return;
+ }
+
+ str_image_version = pkt->data;
+ if (!str_image_version) {
+ dev_err(core->dev, "firmware version not available\n");
+ return;
+ }
+
+ for (i = 0; i < IRIS_FW_VERSION_LENGTH - 1; i++) {
+ if (str_image_version[i] != '\0')
+ fw_version[i] = str_image_version[i];
+ else
+ fw_version[i] = ' ';
+ }
+ fw_version[i] = '\0';
+ dev_dbg(core->dev, "firmware version: %s\n", fw_version);
+}
+
+static void iris_hfi_gen1_sys_property_info(struct iris_core *core, void *packet)
+{
+ struct hfi_msg_sys_property_info_pkt *pkt = packet;
+
+ if (!pkt->num_properties) {
+ dev_dbg(core->dev, "no properties\n");
+ return;
+ }
+
+ switch (pkt->property) {
+ case HFI_PROPERTY_SYS_IMAGE_VERSION:
+ iris_hfi_gen1_sys_get_prop_image_version(core, pkt);
+ break;
+ default:
+ dev_dbg(core->dev, "unknown property data\n");
+ break;
+ }
+}
+
+static void iris_hfi_gen1_session_etb_done(struct iris_inst *inst, void *packet)
+{
+ struct hfi_msg_session_empty_buffer_done_pkt *pkt = packet;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct iris_buffer *buf = NULL;
+ bool found = false;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (buf->index == pkt->input_tag) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ goto error;
+
+ if (pkt->shdr.error_type == HFI_ERR_SESSION_UNSUPPORTED_STREAM) {
+ buf->flags = V4L2_BUF_FLAG_ERROR;
+ iris_vb2_queue_error(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ return;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ iris_vb2_buffer_done(inst, buf);
+ }
+
+ return;
+
+error:
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ dev_err(inst->core->dev, "error in etb done\n");
+}
+
+static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
+{
+ struct hfi_msg_session_fbd_uncompressed_plane0_pkt *pkt = packet;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct hfi_session_flush_pkt flush_pkt;
+ u32 timestamp_hi = pkt->time_stamp_hi;
+ u32 timestamp_lo = pkt->time_stamp_lo;
+ struct iris_core *core = inst->core;
+ u32 filled_len = pkt->filled_len;
+ u32 pic_type = pkt->picture_type;
+ u32 output_tag = pkt->output_tag;
+ struct iris_buffer *buf, *iter;
+ struct iris_buffers *buffers;
+ u32 hfi_flags = pkt->flags;
+ u32 offset = pkt->offset;
+ u64 timestamp_us = 0;
+ bool found = false;
+ u32 flags = 0;
+
+ if ((hfi_flags & HFI_BUFFERFLAG_EOS) && !filled_len) {
+ reinit_completion(&inst->flush_completion);
+
+ flush_pkt.shdr.hdr.size = sizeof(struct hfi_session_flush_pkt);
+ flush_pkt.shdr.hdr.pkt_type = HFI_CMD_SESSION_FLUSH;
+ flush_pkt.shdr.session_id = inst->session_id;
+ flush_pkt.flush_type = HFI_FLUSH_OUTPUT;
+ iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
+ iris_inst_sub_state_change_drain_last(inst);
+
+ return;
+ }
+
+ if (iris_split_mode_enabled(inst) && pkt->stream_id == 0) {
+ buffers = &inst->buffers[BUF_DPB];
+ if (!buffers)
+ goto error;
+
+ found = false;
+ list_for_each_entry(iter, &buffers->list, list) {
+ if (!(iter->attr & BUF_ATTR_QUEUED))
+ continue;
+
+ found = (iter->index == output_tag &&
+ iter->data_offset == offset);
+
+ if (found) {
+ buf = iter;
+ break;
+ }
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ continue;
+
+ found = (buf->index == output_tag &&
+ buf->data_offset == offset);
+
+ if (found)
+ break;
+ }
+ }
+ if (!found)
+ goto error;
+
+ buf->data_offset = offset;
+ buf->data_size = filled_len;
+
+ if (filled_len) {
+ timestamp_us = timestamp_hi;
+ timestamp_us = (timestamp_us << 32) | timestamp_lo;
+ } else {
+ flags |= V4L2_BUF_FLAG_LAST;
+ }
+ buf->timestamp = timestamp_us;
+
+ switch (pic_type) {
+ case HFI_PICTURE_IDR:
+ case HFI_PICTURE_I:
+ flags |= V4L2_BUF_FLAG_KEYFRAME;
+ break;
+ case HFI_PICTURE_P:
+ flags |= V4L2_BUF_FLAG_PFRAME;
+ break;
+ case HFI_PICTURE_B:
+ flags |= V4L2_BUF_FLAG_BFRAME;
+ break;
+ case HFI_FRAME_NOTCODED:
+ case HFI_UNUSED_PICT:
+ case HFI_FRAME_YUV:
+ default:
+ break;
+ }
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ buf->attr |= BUF_ATTR_DEQUEUED;
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+
+ buf->flags |= flags;
+
+ iris_vb2_buffer_done(inst, buf);
+
+ return;
+
+error:
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ dev_err(core->dev, "error in ftb done\n");
+}
+
+struct iris_hfi_gen1_response_pkt_info {
+ u32 pkt;
+ u32 pkt_sz;
+};
+
+static const struct iris_hfi_gen1_response_pkt_info pkt_infos[] = {
+ {
+ .pkt = HFI_MSG_EVENT_NOTIFY,
+ .pkt_sz = sizeof(struct hfi_msg_event_notify_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_INIT,
+ .pkt_sz = sizeof(struct hfi_msg_sys_init_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_PROPERTY_INFO,
+ .pkt_sz = sizeof(struct hfi_msg_sys_property_info_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_SESSION_INIT,
+ .pkt_sz = sizeof(struct hfi_msg_session_init_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SYS_SESSION_END,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_LOAD_RESOURCES,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_START,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_STOP,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_EMPTY_BUFFER,
+ .pkt_sz = sizeof(struct hfi_msg_session_empty_buffer_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_FILL_BUFFER,
+ .pkt_sz = sizeof(struct hfi_msg_session_fbd_uncompressed_plane0_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_FLUSH,
+ .pkt_sz = sizeof(struct hfi_msg_session_flush_done_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_RELEASE_RESOURCES,
+ .pkt_sz = sizeof(struct hfi_msg_session_hdr_pkt),
+ },
+ {
+ .pkt = HFI_MSG_SESSION_RELEASE_BUFFERS,
+ .pkt_sz = sizeof(struct hfi_msg_session_release_buffers_done_pkt),
+ },
+};
+
+static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response)
+{
+ struct hfi_pkt_hdr *hdr = (struct hfi_pkt_hdr *)response;
+ const struct iris_hfi_gen1_response_pkt_info *pkt_info;
+ struct device *dev = core->dev;
+ struct hfi_session_pkt *pkt;
+ struct completion *done;
+ struct iris_inst *inst;
+ bool found = false;
+ u32 i;
+
+ for (i = 0; i < ARRAY_SIZE(pkt_infos); i++) {
+ pkt_info = &pkt_infos[i];
+ if (pkt_info->pkt != hdr->pkt_type)
+ continue;
+ found = true;
+ break;
+ }
+
+ if (!found || hdr->size < pkt_info->pkt_sz) {
+ dev_err(dev, "bad packet size (%d should be %d, pkt type:%x, found %d)\n",
+ hdr->size, pkt_info->pkt_sz, hdr->pkt_type, found);
+
+ return;
+ }
+
+ switch (hdr->pkt_type) {
+ case HFI_MSG_SYS_INIT:
+ iris_hfi_gen1_sys_init_done(core, hdr);
+ break;
+ case HFI_MSG_SYS_PROPERTY_INFO:
+ iris_hfi_gen1_sys_property_info(core, hdr);
+ break;
+ case HFI_MSG_EVENT_NOTIFY:
+ pkt = (struct hfi_session_pkt *)hdr;
+ inst = iris_get_instance(core, pkt->shdr.session_id);
+ if (inst) {
+ mutex_lock(&inst->lock);
+ iris_hfi_gen1_session_event_notify(inst, hdr);
+ mutex_unlock(&inst->lock);
+ } else {
+ iris_hfi_gen1_sys_event_notify(core, hdr);
+ }
+
+ break;
+ default:
+ pkt = (struct hfi_session_pkt *)hdr;
+ inst = iris_get_instance(core, pkt->shdr.session_id);
+ if (!inst) {
+ dev_warn(dev, "no valid instance(pkt session_id:%x, pkt:%x)\n",
+ pkt->shdr.session_id,
+ pkt_info ? pkt_info->pkt : 0);
+ return;
+ }
+
+ mutex_lock(&inst->lock);
+ if (hdr->pkt_type == HFI_MSG_SESSION_EMPTY_BUFFER) {
+ iris_hfi_gen1_session_etb_done(inst, hdr);
+ } else if (hdr->pkt_type == HFI_MSG_SESSION_FILL_BUFFER) {
+ iris_hfi_gen1_session_ftb_done(inst, hdr);
+ } else {
+ struct hfi_msg_session_hdr_pkt *shdr;
+
+ shdr = (struct hfi_msg_session_hdr_pkt *)hdr;
+ if (shdr->error_type != HFI_ERR_NONE)
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+
+ done = pkt_info->pkt == HFI_MSG_SESSION_FLUSH ?
+ &inst->flush_completion : &inst->completion;
+ complete(done);
+ }
+ mutex_unlock(&inst->lock);
+
+ break;
+ }
+}
+
+static void iris_hfi_gen1_flush_debug_queue(struct iris_core *core, u8 *packet)
+{
+ struct hfi_msg_sys_coverage_pkt *pkt;
+
+ while (!iris_hfi_queue_dbg_read(core, packet)) {
+ pkt = (struct hfi_msg_sys_coverage_pkt *)packet;
+
+ if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) {
+ struct hfi_msg_sys_debug_pkt *pkt =
+ (struct hfi_msg_sys_debug_pkt *)packet;
+
+ dev_dbg(core->dev, "%s", pkt->msg_data);
+ }
+ }
+}
+
+static void iris_hfi_gen1_response_handler(struct iris_core *core)
+{
+ memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr));
+ while (!iris_hfi_queue_msg_read(core, core->response_packet)) {
+ iris_hfi_gen1_handle_response(core, core->response_packet);
+ memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr));
+ }
+
+ iris_hfi_gen1_flush_debug_queue(core, core->response_packet);
+}
+
+static const struct iris_hfi_response_ops iris_hfi_gen1_response_ops = {
+ .hfi_response_handler = iris_hfi_gen1_response_handler,
+};
+
+void iris_hfi_gen1_response_ops_init(struct iris_core *core)
+{
+ core->hfi_response_ops = &iris_hfi_gen1_response_ops;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
new file mode 100644
index 000000000000..b9d3749a10ef
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN2_H__
+#define __IRIS_HFI_GEN2_H__
+
+#include "iris_instance.h"
+
+struct iris_core;
+
+#define to_iris_inst_hfi_gen2(ptr) \
+ container_of(ptr, struct iris_inst_hfi_gen2, inst)
+
+/**
+ * struct iris_inst_hfi_gen2 - holds per video instance parameters for hfi_gen2
+ *
+ * @inst: pointer to iris_instance structure
+ * @packet: HFI packet
+ * @ipsc_properties_set: boolean to set ipsc properties to fw
+ * @opsc_properties_set: boolean to set opsc properties to fw
+ * @hfi_frame_info: structure of frame info
+ * @src_subcr_params: subscription params to fw on input port
+ * @dst_subcr_params: subscription params to fw on output port
+ */
+struct iris_inst_hfi_gen2 {
+ struct iris_inst inst;
+ struct iris_hfi_header *packet;
+ bool ipsc_properties_set;
+ bool opsc_properties_set;
+ struct iris_hfi_frame_info hfi_frame_info;
+ struct hfi_subscription_params src_subcr_params;
+ struct hfi_subscription_params dst_subcr_params;
+};
+
+void iris_hfi_gen2_command_ops_init(struct iris_core *core);
+void iris_hfi_gen2_response_ops_init(struct iris_core *core);
+struct iris_inst *iris_hfi_gen2_get_instance(void);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
new file mode 100644
index 000000000000..a908b41e2868
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -0,0 +1,957 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/bitfield.h>
+
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_packet.h"
+
+#define UNSPECIFIED_COLOR_FORMAT 5
+#define NUM_SYS_INIT_PACKETS 8
+
+#define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \
+ NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32)))
+
+#define SYS_IFPC_PKT_SIZE (sizeof(struct iris_hfi_header) + \
+ sizeof(struct iris_hfi_packet) + sizeof(u32))
+
+#define SYS_NO_PAYLOAD_PKT_SIZE (sizeof(struct iris_hfi_header) + \
+ sizeof(struct iris_hfi_packet))
+
+static int iris_hfi_gen2_sys_init(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_INIT_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_sys_init(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_sys_image_version(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_image_version(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_sys_interframe_powercollapse(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_IFPC_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_sys_interframe_powercollapse(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_sys_pc_prep(struct iris_core *core)
+{
+ struct iris_hfi_header *hdr;
+ int ret;
+
+ hdr = kzalloc(SYS_NO_PAYLOAD_PKT_SIZE, GFP_KERNEL);
+ if (!hdr)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_sys_pc_prep(core, hdr);
+ ret = iris_hfi_queue_cmd_write_locked(core, hdr, hdr->size);
+
+ kfree(hdr);
+
+ return ret;
+}
+
+static u32 iris_hfi_gen2_get_port(u32 plane)
+{
+ switch (plane) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ return HFI_PORT_BITSTREAM;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ return HFI_PORT_RAW;
+ default:
+ return HFI_PORT_NONE;
+ }
+}
+
+static u32 iris_hfi_gen2_get_port_from_buf_type(enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ case BUF_BIN:
+ case BUF_COMV:
+ case BUF_NON_COMV:
+ case BUF_LINE:
+ return HFI_PORT_BITSTREAM;
+ case BUF_OUTPUT:
+ case BUF_DPB:
+ return HFI_PORT_RAW;
+ case BUF_PERSIST:
+ default:
+ return HFI_PORT_NONE;
+ }
+}
+
+static int iris_hfi_gen2_session_set_property(struct iris_inst *inst, u32 packet_type, u32 flag,
+ u32 plane, u32 payload_type, void *payload,
+ u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ iris_hfi_gen2_packet_session_property(inst,
+ packet_type,
+ flag,
+ plane,
+ payload_type,
+ payload,
+ payload_size);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_set_bitstream_resolution(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 resolution = inst->fmt_src->fmt.pix_mp.width << 16 |
+ inst->fmt_src->fmt.pix_mp.height;
+
+ inst_hfi_gen2->src_subcr_params.bitstream_resolution = resolution;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &resolution,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst)
+{
+ u32 bottom_offset = (inst->fmt_src->fmt.pix_mp.height - inst->crop.height);
+ u32 right_offset = (inst->fmt_src->fmt.pix_mp.width - inst->crop.width);
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 left_offset = inst->crop.left;
+ u32 top_offset = inst->crop.top;
+ u32 payload[2];
+
+ payload[0] = FIELD_PREP(GENMASK(31, 16), left_offset) | top_offset;
+ payload[1] = FIELD_PREP(GENMASK(31, 16), right_offset) | bottom_offset;
+ inst_hfi_gen2->src_subcr_params.crop_offsets[0] = payload[0];
+ inst_hfi_gen2->src_subcr_params.crop_offsets[1] = payload[1];
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_64_PACKED,
+ &payload,
+ sizeof(u64));
+}
+
+static int iris_hfi_gen2_set_bit_dpeth(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 bitdepth = BIT_DEPTH_8;
+
+ inst_hfi_gen2->src_subcr_params.bit_depth = bitdepth;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &bitdepth,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_coded_frames(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 coded_frames = 0;
+
+ if (inst->fw_caps[CODED_FRAMES].value == CODED_FRAMES_PROGRESSIVE)
+ coded_frames = HFI_BITMASK_FRAME_MBS_ONLY_FLAG;
+ inst_hfi_gen2->src_subcr_params.coded_frames = coded_frames;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_CODED_FRAMES,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &coded_frames,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_min_output_count(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 min_output = inst->buffers[BUF_OUTPUT].min_count;
+
+ inst_hfi_gen2->src_subcr_params.fw_min_count = min_output;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &min_output,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_picture_order_count(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 poc = 0;
+
+ inst_hfi_gen2->src_subcr_params.pic_order_cnt = poc;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_PIC_ORDER_CNT_TYPE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &poc,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_colorspace(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ struct v4l2_pix_format_mplane *pixmp = &inst->fmt_src->fmt.pix_mp;
+ u32 video_signal_type_present_flag = 0, color_info;
+ u32 matrix_coeff = HFI_MATRIX_COEFF_RESERVED;
+ u32 video_format = UNSPECIFIED_COLOR_FORMAT;
+ u32 full_range = V4L2_QUANTIZATION_DEFAULT;
+ u32 transfer_char = HFI_TRANSFER_RESERVED;
+ u32 colour_description_present_flag = 0;
+ u32 primaries = HFI_PRIMARIES_RESERVED;
+
+ if (pixmp->colorspace != V4L2_COLORSPACE_DEFAULT ||
+ pixmp->ycbcr_enc != V4L2_YCBCR_ENC_DEFAULT ||
+ pixmp->xfer_func != V4L2_XFER_FUNC_DEFAULT) {
+ colour_description_present_flag = 1;
+ video_signal_type_present_flag = 1;
+ primaries = iris_hfi_gen2_get_color_primaries(pixmp->colorspace);
+ matrix_coeff = iris_hfi_gen2_get_matrix_coefficients(pixmp->ycbcr_enc);
+ transfer_char = iris_hfi_gen2_get_transfer_char(pixmp->xfer_func);
+ }
+
+ if (pixmp->quantization != V4L2_QUANTIZATION_DEFAULT) {
+ video_signal_type_present_flag = 1;
+ full_range = pixmp->quantization == V4L2_QUANTIZATION_FULL_RANGE ? 1 : 0;
+ }
+
+ color_info = iris_hfi_gen2_get_color_info(matrix_coeff, transfer_char, primaries,
+ colour_description_present_flag, full_range,
+ video_format, video_signal_type_present_flag);
+
+ inst_hfi_gen2->src_subcr_params.color_info = color_info;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_32_PACKED,
+ &color_info,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_profile(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 profile = inst->fw_caps[PROFILE].value;
+
+ inst_hfi_gen2->src_subcr_params.profile = profile;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_PROFILE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &profile,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_level(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ u32 level = inst->fw_caps[LEVEL].value;
+
+ inst_hfi_gen2->src_subcr_params.level = level;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_LEVEL,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &level,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst)
+{
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ u32 hfi_colorformat, pixelformat;
+
+ pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ? HFI_COLOR_FMT_NV12 : 0;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_COLOR_FORMAT,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32,
+ &hfi_colorformat,
+ sizeof(u32));
+}
+
+static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst)
+{
+ u32 port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ u32 pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ u32 scanline_y = inst->fmt_dst->fmt.pix_mp.height;
+ u32 stride_y = inst->fmt_dst->fmt.pix_mp.width;
+ u32 scanline_uv = scanline_y / 2;
+ u32 stride_uv = stride_y;
+ u32 payload[2];
+
+ if (pixelformat != V4L2_PIX_FMT_NV12)
+ return 0;
+
+ payload[0] = stride_y << 16 | scanline_y;
+ payload[1] = stride_uv << 16 | scanline_uv;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_LINEAR_STRIDE_SCANLINE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U64,
+ &payload,
+ sizeof(u64));
+}
+
+static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ u32 config_params_size, i, j;
+ const u32 *config_params;
+ int ret;
+
+ static const struct iris_hfi_prop_type_handle prop_type_handle_arr[] = {
+ {HFI_PROP_BITSTREAM_RESOLUTION, iris_hfi_gen2_set_bitstream_resolution },
+ {HFI_PROP_CROP_OFFSETS, iris_hfi_gen2_set_crop_offsets },
+ {HFI_PROP_CODED_FRAMES, iris_hfi_gen2_set_coded_frames },
+ {HFI_PROP_LUMA_CHROMA_BIT_DEPTH, iris_hfi_gen2_set_bit_dpeth },
+ {HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT, iris_hfi_gen2_set_min_output_count },
+ {HFI_PROP_PIC_ORDER_CNT_TYPE, iris_hfi_gen2_set_picture_order_count },
+ {HFI_PROP_SIGNAL_COLOR_INFO, iris_hfi_gen2_set_colorspace },
+ {HFI_PROP_PROFILE, iris_hfi_gen2_set_profile },
+ {HFI_PROP_LEVEL, iris_hfi_gen2_set_level },
+ {HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat },
+ {HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline },
+ };
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ config_params = core->iris_platform_data->input_config_params;
+ config_params_size = core->iris_platform_data->input_config_params_size;
+ } else {
+ config_params = core->iris_platform_data->output_config_params;
+ config_params_size = core->iris_platform_data->output_config_params_size;
+ }
+
+ if (!config_params || !config_params_size)
+ return -EINVAL;
+
+ for (i = 0; i < config_params_size; i++) {
+ for (j = 0; j < ARRAY_SIZE(prop_type_handle_arr); j++) {
+ if (prop_type_handle_arr[j].type == config_params[i]) {
+ ret = prop_type_handle_arr[j].handle(inst);
+ if (ret)
+ return ret;
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_session_set_codec(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 codec = HFI_CODEC_DECODE_AVC;
+
+ iris_hfi_gen2_packet_session_property(inst,
+ HFI_PROP_CODEC,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_NONE,
+ HFI_PAYLOAD_U32_ENUM,
+ &codec,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_set_default_header(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 default_header = false;
+
+ iris_hfi_gen2_packet_session_property(inst,
+ HFI_PROP_DEC_DEFAULT_HEADER,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_U32,
+ &default_header,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_open(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret;
+
+ if (inst->state != IRIS_INST_DEINIT)
+ return -EALREADY;
+
+ inst_hfi_gen2->ipsc_properties_set = false;
+ inst_hfi_gen2->opsc_properties_set = false;
+
+ inst_hfi_gen2->packet = kzalloc(4096, GFP_KERNEL);
+ if (!inst_hfi_gen2->packet)
+ return -ENOMEM;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_OPEN,
+ HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED,
+ HFI_PORT_NONE,
+ 0,
+ HFI_PAYLOAD_U32,
+ &inst->session_id,
+ sizeof(u32));
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+ if (ret)
+ goto fail_free_packet;
+
+ ret = iris_hfi_gen2_session_set_codec(inst);
+ if (ret)
+ goto fail_free_packet;
+
+ ret = iris_hfi_gen2_session_set_default_header(inst);
+ if (ret)
+ goto fail_free_packet;
+
+ return 0;
+
+fail_free_packet:
+ kfree(inst_hfi_gen2->packet);
+ inst_hfi_gen2->packet = NULL;
+
+ return ret;
+}
+
+static int iris_hfi_gen2_session_close(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret;
+
+ if (!inst_hfi_gen2->packet)
+ return -EINVAL;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_CLOSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ HFI_PORT_NONE,
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+
+ kfree(inst_hfi_gen2->packet);
+ inst_hfi_gen2->packet = NULL;
+
+ return ret;
+}
+
+static int iris_hfi_gen2_session_subscribe_mode(struct iris_inst *inst,
+ u32 cmd, u32 plane, u32 payload_type,
+ void *payload, u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ cmd,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ payload_type,
+ payload,
+ payload_size);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct hfi_subscription_params subsc_params;
+ u32 prop_type, payload_size, payload_type;
+ struct iris_core *core = inst->core;
+ const u32 *change_param;
+ u32 change_param_size;
+ u32 payload[32] = {0};
+ u32 hfi_port = 0, i;
+ int ret;
+
+ if ((V4L2_TYPE_IS_OUTPUT(plane) && inst_hfi_gen2->ipsc_properties_set) ||
+ (V4L2_TYPE_IS_CAPTURE(plane) && inst_hfi_gen2->opsc_properties_set)) {
+ dev_err(core->dev, "invalid plane\n");
+ return 0;
+ }
+
+ change_param = core->iris_platform_data->input_config_params;
+ change_param_size = core->iris_platform_data->input_config_params_size;
+
+ payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE;
+
+ for (i = 0; i < change_param_size; i++)
+ payload[i + 1] = change_param[i];
+
+ ret = iris_hfi_gen2_session_subscribe_mode(inst,
+ HFI_CMD_SUBSCRIBE_MODE,
+ plane,
+ HFI_PAYLOAD_U32_ARRAY,
+ &payload[0],
+ ((change_param_size + 1) * sizeof(u32)));
+ if (ret)
+ return ret;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ inst_hfi_gen2->ipsc_properties_set = true;
+ } else {
+ hfi_port = iris_hfi_gen2_get_port(V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ memcpy(&inst_hfi_gen2->dst_subcr_params,
+ &inst_hfi_gen2->src_subcr_params,
+ sizeof(inst_hfi_gen2->src_subcr_params));
+ subsc_params = inst_hfi_gen2->dst_subcr_params;
+ for (i = 0; i < change_param_size; i++) {
+ payload[0] = 0;
+ payload[1] = 0;
+ payload_size = 0;
+ payload_type = 0;
+ prop_type = change_param[i];
+ switch (prop_type) {
+ case HFI_PROP_BITSTREAM_RESOLUTION:
+ payload[0] = subsc_params.bitstream_resolution;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_CROP_OFFSETS:
+ payload[0] = subsc_params.crop_offsets[0];
+ payload[1] = subsc_params.crop_offsets[1];
+ payload_size = sizeof(u64);
+ payload_type = HFI_PAYLOAD_64_PACKED;
+ break;
+ case HFI_PROP_CODED_FRAMES:
+ payload[0] = subsc_params.coded_frames;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT:
+ payload[0] = subsc_params.fw_min_count;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_PIC_ORDER_CNT_TYPE:
+ payload[0] = subsc_params.pic_order_cnt;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_SIGNAL_COLOR_INFO:
+ payload[0] = subsc_params.color_info;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_PROFILE:
+ payload[0] = subsc_params.profile;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ case HFI_PROP_LEVEL:
+ payload[0] = subsc_params.level;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
+ default:
+ prop_type = 0;
+ ret = -EINVAL;
+ break;
+ }
+ if (prop_type) {
+ ret = iris_hfi_gen2_session_set_property(inst,
+ prop_type,
+ HFI_HOST_FLAGS_NONE,
+ hfi_port,
+ payload_type,
+ &payload,
+ payload_size);
+ if (ret)
+ return ret;
+ }
+ }
+ inst_hfi_gen2->opsc_properties_set = true;
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane)
+{
+ struct iris_core *core = inst->core;
+ u32 subscribe_prop_size, i;
+ const u32 *subcribe_prop;
+ u32 payload[32] = {0};
+
+ payload[0] = HFI_MODE_PROPERTY;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ subscribe_prop_size = core->iris_platform_data->dec_input_prop_size;
+ subcribe_prop = core->iris_platform_data->dec_input_prop;
+ } else {
+ subscribe_prop_size = core->iris_platform_data->dec_output_prop_size;
+ subcribe_prop = core->iris_platform_data->dec_output_prop;
+ }
+
+ for (i = 0; i < subscribe_prop_size; i++)
+ payload[i + 1] = subcribe_prop[i];
+
+ return iris_hfi_gen2_session_subscribe_mode(inst,
+ HFI_CMD_SUBSCRIBE_MODE,
+ plane,
+ HFI_PAYLOAD_U32_ARRAY,
+ &payload[0],
+ (subscribe_prop_size + 1) * sizeof(u32));
+}
+
+static int iris_hfi_gen2_session_start(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret = 0;
+
+ ret = iris_hfi_gen2_subscribe_change_param(inst, plane);
+ if (ret)
+ return ret;
+
+ ret = iris_hfi_gen2_subscribe_property(inst, plane);
+ if (ret)
+ return ret;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_START,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_stop(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ int ret = 0;
+
+ reinit_completion(&inst->completion);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_STOP,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ ret = iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+ if (ret)
+ return ret;
+
+ return iris_wait_for_session_response(inst, false);
+}
+
+static int iris_hfi_gen2_session_pause(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_PAUSE,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_resume_drc(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 payload = HFI_CMD_SETTINGS_CHANGE;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_RESUME,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_U32,
+ &payload,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_resume_drain(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 payload = HFI_CMD_DRAIN;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_RESUME,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_U32,
+ &payload,
+ sizeof(u32));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_drain(struct iris_inst *inst, u32 plane)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ if (!V4L2_TYPE_IS_OUTPUT(plane))
+ return 0;
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_DRAIN,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ iris_hfi_gen2_get_port(plane),
+ inst->session_id,
+ HFI_PAYLOAD_NONE,
+ NULL,
+ 0);
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static u32 iris_hfi_gen2_buf_type_from_driver(enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return HFI_BUFFER_BITSTREAM;
+ case BUF_OUTPUT:
+ return HFI_BUFFER_RAW;
+ case BUF_BIN:
+ return HFI_BUFFER_BIN;
+ case BUF_COMV:
+ return HFI_BUFFER_COMV;
+ case BUF_NON_COMV:
+ return HFI_BUFFER_NON_COMV;
+ case BUF_LINE:
+ return HFI_BUFFER_LINE;
+ case BUF_DPB:
+ return HFI_BUFFER_DPB;
+ case BUF_PERSIST:
+ return HFI_BUFFER_PERSIST;
+ default:
+ return 0;
+ }
+}
+
+static int iris_set_num_comv(struct iris_inst *inst)
+{
+ struct platform_inst_caps *caps;
+ struct iris_core *core = inst->core;
+ u32 num_comv;
+
+ caps = core->iris_platform_data->inst_caps;
+ num_comv = caps->num_comv;
+
+ return core->hfi_ops->session_set_property(inst,
+ HFI_PROP_COMV_BUFFER_COUNT,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_U32,
+ &num_comv, sizeof(u32));
+}
+
+static void iris_hfi_gen2_get_buffer(struct iris_buffer *buffer, struct iris_hfi_buffer *buf)
+{
+ memset(buf, 0, sizeof(*buf));
+ buf->type = iris_hfi_gen2_buf_type_from_driver(buffer->type);
+ buf->index = buffer->index;
+ buf->base_address = buffer->device_addr;
+ buf->addr_offset = 0;
+ buf->buffer_size = buffer->buffer_size;
+
+ if (buffer->type == BUF_INPUT)
+ buf->buffer_size = ALIGN(buffer->buffer_size, 256);
+ buf->data_offset = buffer->data_offset;
+ buf->data_size = buffer->data_size;
+ if (buffer->attr & BUF_ATTR_PENDING_RELEASE)
+ buf->flags |= HFI_BUF_HOST_FLAG_RELEASE;
+ buf->flags |= HFI_BUF_HOST_FLAGS_CB_NON_SECURE;
+ buf->timestamp = buffer->timestamp;
+}
+
+static int iris_hfi_gen2_session_queue_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_hfi_buffer hfi_buffer;
+ u32 port;
+ int ret;
+
+ iris_hfi_gen2_get_buffer(buffer, &hfi_buffer);
+ if (buffer->type == BUF_COMV) {
+ ret = iris_set_num_comv(inst);
+ if (ret)
+ return ret;
+ }
+
+ port = iris_hfi_gen2_get_port_from_buf_type(buffer->type);
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_BUFFER,
+ HFI_HOST_FLAGS_INTR_REQUIRED,
+ port,
+ inst->session_id,
+ HFI_PAYLOAD_STRUCTURE,
+ &hfi_buffer,
+ sizeof(hfi_buffer));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static int iris_hfi_gen2_session_release_buffer(struct iris_inst *inst, struct iris_buffer *buffer)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_hfi_buffer hfi_buffer;
+ u32 port;
+
+ iris_hfi_gen2_get_buffer(buffer, &hfi_buffer);
+ hfi_buffer.flags |= HFI_BUF_HOST_FLAG_RELEASE;
+ port = iris_hfi_gen2_get_port_from_buf_type(buffer->type);
+
+ iris_hfi_gen2_packet_session_command(inst,
+ HFI_CMD_BUFFER,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED),
+ port,
+ inst->session_id,
+ HFI_PAYLOAD_STRUCTURE,
+ &hfi_buffer,
+ sizeof(hfi_buffer));
+
+ return iris_hfi_queue_cmd_write(inst->core, inst_hfi_gen2->packet,
+ inst_hfi_gen2->packet->size);
+}
+
+static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = {
+ .sys_init = iris_hfi_gen2_sys_init,
+ .sys_image_version = iris_hfi_gen2_sys_image_version,
+ .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse,
+ .sys_pc_prep = iris_hfi_gen2_sys_pc_prep,
+ .session_open = iris_hfi_gen2_session_open,
+ .session_set_config_params = iris_hfi_gen2_session_set_config_params,
+ .session_set_property = iris_hfi_gen2_session_set_property,
+ .session_start = iris_hfi_gen2_session_start,
+ .session_queue_buf = iris_hfi_gen2_session_queue_buffer,
+ .session_release_buf = iris_hfi_gen2_session_release_buffer,
+ .session_pause = iris_hfi_gen2_session_pause,
+ .session_resume_drc = iris_hfi_gen2_session_resume_drc,
+ .session_stop = iris_hfi_gen2_session_stop,
+ .session_drain = iris_hfi_gen2_session_drain,
+ .session_resume_drain = iris_hfi_gen2_session_resume_drain,
+ .session_close = iris_hfi_gen2_session_close,
+};
+
+void iris_hfi_gen2_command_ops_init(struct iris_core *core)
+{
+ core->hfi_ops = &iris_hfi_gen2_command_ops;
+}
+
+struct iris_inst *iris_hfi_gen2_get_instance(void)
+{
+ return kzalloc(sizeof(struct iris_inst_hfi_gen2), GFP_KERNEL);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
new file mode 100644
index 000000000000..806f8bb7f505
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -0,0 +1,161 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN2_DEFINES_H__
+#define __IRIS_HFI_GEN2_DEFINES_H__
+
+#include <linux/types.h>
+
+#define HFI_VIDEO_ARCH_LX 0x1
+
+#define HFI_CMD_BEGIN 0x01000000
+#define HFI_CMD_INIT 0x01000001
+#define HFI_CMD_POWER_COLLAPSE 0x01000002
+#define HFI_CMD_OPEN 0x01000003
+#define HFI_CMD_CLOSE 0x01000004
+#define HFI_CMD_START 0x01000005
+#define HFI_CMD_STOP 0x01000006
+#define HFI_CMD_DRAIN 0x01000007
+#define HFI_CMD_RESUME 0x01000008
+#define HFI_CMD_BUFFER 0x01000009
+#define HFI_CMD_SUBSCRIBE_MODE 0x0100000B
+#define HFI_CMD_SETTINGS_CHANGE 0x0100000C
+#define HFI_CMD_PAUSE 0x01000011
+#define HFI_CMD_END 0x01FFFFFF
+
+#define HFI_BITMASK_BITSTREAM_WIDTH 0xffff0000
+#define HFI_BITMASK_BITSTREAM_HEIGHT 0x0000ffff
+#define HFI_BITMASK_FRAME_MBS_ONLY_FLAG 0x00000001
+
+#define HFI_PROP_BEGIN 0x03000000
+#define HFI_PROP_IMAGE_VERSION 0x03000001
+#define HFI_PROP_INTRA_FRAME_POWER_COLLAPSE 0x03000002
+#define HFI_PROP_UBWC_MAX_CHANNELS 0x03000003
+#define HFI_PROP_UBWC_MAL_LENGTH 0x03000004
+#define HFI_PROP_UBWC_HBB 0x03000005
+#define HFI_PROP_UBWC_BANK_SWZL_LEVEL1 0x03000006
+#define HFI_PROP_UBWC_BANK_SWZL_LEVEL2 0x03000007
+#define HFI_PROP_UBWC_BANK_SWZL_LEVEL3 0x03000008
+#define HFI_PROP_UBWC_BANK_SPREADING 0x03000009
+#define HFI_PROP_CODEC 0x03000100
+#define HFI_PROP_COLOR_FORMAT 0x03000101
+#define HFI_PROP_BITSTREAM_RESOLUTION 0x03000103
+#define HFI_PROP_LINEAR_STRIDE_SCANLINE 0x03000104
+#define HFI_PROP_CROP_OFFSETS 0x03000105
+#define HFI_PROP_PROFILE 0x03000107
+#define HFI_PROP_LEVEL 0x03000108
+#define HFI_PROP_STAGE 0x0300010a
+#define HFI_PROP_PIPE 0x0300010b
+#define HFI_PROP_LUMA_CHROMA_BIT_DEPTH 0x0300010f
+#define HFI_PROP_CODED_FRAMES 0x03000120
+#define HFI_PROP_CABAC_SESSION 0x03000121
+#define HFI_PROP_BUFFER_HOST_MAX_COUNT 0x03000123
+#define HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT 0x03000124
+#define HFI_PROP_PIC_ORDER_CNT_TYPE 0x03000128
+#define HFI_PROP_QUALITY_MODE 0x03000148
+#define HFI_PROP_SIGNAL_COLOR_INFO 0x03000155
+#define HFI_PROP_PICTURE_TYPE 0x03000162
+#define HFI_PROP_DEC_DEFAULT_HEADER 0x03000168
+#define HFI_PROP_DEC_START_FROM_RAP_FRAME 0x03000169
+#define HFI_PROP_NO_OUTPUT 0x0300016a
+#define HFI_PROP_COMV_BUFFER_COUNT 0x03000193
+#define HFI_PROP_END 0x03FFFFFF
+
+#define HFI_SESSION_ERROR_BEGIN 0x04000000
+#define HFI_ERROR_UNKNOWN_SESSION 0x04000001
+#define HFI_ERROR_MAX_SESSIONS 0x04000002
+#define HFI_ERROR_FATAL 0x04000003
+#define HFI_ERROR_INVALID_STATE 0x04000004
+#define HFI_ERROR_INSUFFICIENT_RESOURCES 0x04000005
+#define HFI_ERROR_BUFFER_NOT_SET 0x04000006
+#define HFI_ERROR_STREAM_UNSUPPORTED 0x04000008
+#define HFI_SESSION_ERROR_END 0x04FFFFFF
+
+#define HFI_SYSTEM_ERROR_BEGIN 0x05000000
+#define HFI_SYS_ERROR_WD_TIMEOUT 0x05000001
+#define HFI_SYSTEM_ERROR_END 0x05FFFFFF
+
+#define HFI_INFORMATION_BEGIN 0x06000000
+#define HFI_INFO_UNSUPPORTED 0x06000001
+#define HFI_INFO_DATA_CORRUPT 0x06000002
+#define HFI_INFO_BUFFER_OVERFLOW 0x06000004
+#define HFI_INFO_HFI_FLAG_DRAIN_LAST 0x06000006
+#define HFI_INFO_HFI_FLAG_PSC_LAST 0x06000007
+#define HFI_INFORMATION_END 0x06FFFFFF
+
+enum hfi_property_mode_type {
+ HFI_MODE_PORT_SETTINGS_CHANGE = 0x00000001,
+ HFI_MODE_PROPERTY = 0x00000002,
+};
+
+enum hfi_color_format {
+ HFI_COLOR_FMT_OPAQUE = 0,
+ HFI_COLOR_FMT_NV12 = 1,
+ HFI_COLOR_FMT_NV12_UBWC = 2,
+ HFI_COLOR_FMT_P010 = 3,
+ HFI_COLOR_FMT_TP10_UBWC = 4,
+ HFI_COLOR_FMT_RGBA8888 = 5,
+ HFI_COLOR_FMT_RGBA8888_UBWC = 6,
+ HFI_COLOR_FMT_NV21 = 7,
+};
+
+enum hfi_codec_type {
+ HFI_CODEC_DECODE_AVC = 1,
+ HFI_CODEC_ENCODE_AVC = 2,
+};
+
+enum hfi_picture_type {
+ HFI_PICTURE_IDR = 0x00000001,
+ HFI_PICTURE_P = 0x00000002,
+ HFI_PICTURE_B = 0x00000004,
+ HFI_PICTURE_I = 0x00000008,
+ HFI_PICTURE_CRA = 0x00000010,
+ HFI_PICTURE_BLA = 0x00000020,
+};
+
+enum hfi_buffer_type {
+ HFI_BUFFER_BITSTREAM = 0x00000001,
+ HFI_BUFFER_RAW = 0x00000002,
+ HFI_BUFFER_METADATA = 0x00000003,
+ HFI_BUFFER_SUBCACHE = 0x00000004,
+ HFI_BUFFER_PARTIAL_DATA = 0x00000005,
+ HFI_BUFFER_DPB = 0x00000006,
+ HFI_BUFFER_BIN = 0x00000007,
+ HFI_BUFFER_LINE = 0x00000008,
+ HFI_BUFFER_ARP = 0x00000009,
+ HFI_BUFFER_COMV = 0x0000000A,
+ HFI_BUFFER_NON_COMV = 0x0000000B,
+ HFI_BUFFER_PERSIST = 0x0000000C,
+ HFI_BUFFER_VPSS = 0x0000000D,
+};
+
+enum hfi_buffer_host_flags {
+ HFI_BUF_HOST_FLAG_RELEASE = 0x00000001,
+ HFI_BUF_HOST_FLAG_READONLY = 0x00000010,
+ HFI_BUF_HOST_FLAG_CODEC_CONFIG = 0x00000100,
+ HFI_BUF_HOST_FLAGS_CB_NON_SECURE = 0x00000200,
+};
+
+enum hfi_buffer_firmware_flags {
+ HFI_BUF_FW_FLAG_RELEASE_DONE = 0x00000001,
+ HFI_BUF_FW_FLAG_READONLY = 0x00000010,
+ HFI_BUF_FW_FLAG_LAST = 0x10000000,
+ HFI_BUF_FW_FLAG_PSC_LAST = 0x20000000,
+};
+
+enum hfi_packet_firmware_flags {
+ HFI_FW_FLAGS_SUCCESS = 0x00000001,
+ HFI_FW_FLAGS_INFORMATION = 0x00000002,
+ HFI_FW_FLAGS_SESSION_ERROR = 0x00000004,
+ HFI_FW_FLAGS_SYSTEM_ERROR = 0x00000008,
+};
+
+struct hfi_debug_header {
+ u32 size;
+ u32 debug_level;
+ u32 reserved[2];
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
new file mode 100644
index 000000000000..d77fa29f44fc
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
@@ -0,0 +1,292 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_hfi_common.h"
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_packet.h"
+
+u32 iris_hfi_gen2_get_color_primaries(u32 primaries)
+{
+ switch (primaries) {
+ case V4L2_COLORSPACE_DEFAULT:
+ return HFI_PRIMARIES_RESERVED;
+ case V4L2_COLORSPACE_REC709:
+ return HFI_PRIMARIES_BT709;
+ case V4L2_COLORSPACE_470_SYSTEM_M:
+ return HFI_PRIMARIES_BT470_SYSTEM_M;
+ case V4L2_COLORSPACE_470_SYSTEM_BG:
+ return HFI_PRIMARIES_BT470_SYSTEM_BG;
+ case V4L2_COLORSPACE_SMPTE170M:
+ return HFI_PRIMARIES_BT601_525;
+ case V4L2_COLORSPACE_SMPTE240M:
+ return HFI_PRIMARIES_SMPTE_ST240M;
+ case V4L2_COLORSPACE_BT2020:
+ return HFI_PRIMARIES_BT2020;
+ case V4L2_COLORSPACE_DCI_P3:
+ return HFI_PRIMARIES_SMPTE_RP431_2;
+ default:
+ return HFI_PRIMARIES_RESERVED;
+ }
+}
+
+u32 iris_hfi_gen2_get_transfer_char(u32 characterstics)
+{
+ switch (characterstics) {
+ case V4L2_XFER_FUNC_DEFAULT:
+ return HFI_TRANSFER_RESERVED;
+ case V4L2_XFER_FUNC_709:
+ return HFI_TRANSFER_BT709;
+ case V4L2_XFER_FUNC_SMPTE240M:
+ return HFI_TRANSFER_SMPTE_ST240M;
+ case V4L2_XFER_FUNC_SRGB:
+ return HFI_TRANSFER_SRGB_SYCC;
+ case V4L2_XFER_FUNC_SMPTE2084:
+ return HFI_TRANSFER_SMPTE_ST2084_PQ;
+ default:
+ return HFI_TRANSFER_RESERVED;
+ }
+}
+
+u32 iris_hfi_gen2_get_matrix_coefficients(u32 coefficients)
+{
+ switch (coefficients) {
+ case V4L2_YCBCR_ENC_DEFAULT:
+ return HFI_MATRIX_COEFF_RESERVED;
+ case V4L2_YCBCR_ENC_709:
+ return HFI_MATRIX_COEFF_BT709;
+ case V4L2_YCBCR_ENC_XV709:
+ return HFI_MATRIX_COEFF_BT709;
+ case V4L2_YCBCR_ENC_XV601:
+ return HFI_MATRIX_COEFF_BT470_SYS_BG_OR_BT601_625;
+ case V4L2_YCBCR_ENC_601:
+ return HFI_MATRIX_COEFF_BT601_525_BT1358_525_OR_625;
+ case V4L2_YCBCR_ENC_SMPTE240M:
+ return HFI_MATRIX_COEFF_SMPTE_ST240;
+ case V4L2_YCBCR_ENC_BT2020:
+ return HFI_MATRIX_COEFF_BT2020_NON_CONSTANT;
+ case V4L2_YCBCR_ENC_BT2020_CONST_LUM:
+ return HFI_MATRIX_COEFF_BT2020_CONSTANT;
+ default:
+ return HFI_MATRIX_COEFF_RESERVED;
+ }
+}
+
+u32 iris_hfi_gen2_get_color_info(u32 matrix_coeff, u32 transfer_char, u32 primaries,
+ u32 colour_description_present_flag, u32 full_range,
+ u32 video_format, u32 video_signal_type_present_flag)
+{
+ return (matrix_coeff & 0xFF) |
+ ((transfer_char << 8) & 0xFF00) |
+ ((primaries << 16) & 0xFF0000) |
+ ((colour_description_present_flag << 24) & 0x1000000) |
+ ((full_range << 25) & 0x2000000) |
+ ((video_format << 26) & 0x1C000000) |
+ ((video_signal_type_present_flag << 29) & 0x20000000);
+}
+
+static void iris_hfi_gen2_create_header(struct iris_hfi_header *hdr,
+ u32 session_id, u32 header_id)
+{
+ memset(hdr, 0, sizeof(*hdr));
+
+ hdr->size = sizeof(*hdr);
+ hdr->session_id = session_id;
+ hdr->header_id = header_id;
+ hdr->num_packets = 0;
+}
+
+static void iris_hfi_gen2_create_packet(struct iris_hfi_header *hdr, u32 pkt_type,
+ u32 pkt_flags, u32 payload_type, u32 port,
+ u32 packet_id, void *payload, u32 payload_size)
+{
+ struct iris_hfi_packet *pkt = (struct iris_hfi_packet *)((u8 *)hdr + hdr->size);
+ u32 pkt_size = sizeof(*pkt) + payload_size;
+
+ memset(pkt, 0, pkt_size);
+ pkt->size = pkt_size;
+ pkt->type = pkt_type;
+ pkt->flags = pkt_flags;
+ pkt->payload_info = payload_type;
+ pkt->port = port;
+ pkt->packet_id = packet_id;
+ if (payload_size)
+ memcpy(&pkt->payload[0], payload, payload_size);
+
+ hdr->num_packets++;
+ hdr->size += pkt->size;
+}
+
+void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ u32 payload = 0;
+
+ iris_hfi_gen2_create_header(hdr, 0, core->header_id++);
+
+ payload = HFI_VIDEO_ARCH_LX;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_CMD_INIT,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_NON_DISCARDABLE),
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->max_channels;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_MAX_CHANNELS,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->mal_length;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_MAL_LENGTH,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->highest_bank_bit;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_HBB,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_swzl_level;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SWZL_LEVEL1,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_swz2_level;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SWZL_LEVEL2,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_swz3_level;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SWZL_LEVEL3,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+
+ payload = core->iris_platform_data->ubwc_config->bank_spreading;
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_UBWC_BANK_SPREADING,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+}
+
+void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ iris_hfi_gen2_create_header(hdr, 0, core->header_id++);
+
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_IMAGE_VERSION,
+ (HFI_HOST_FLAGS_RESPONSE_REQUIRED |
+ HFI_HOST_FLAGS_INTR_REQUIRED |
+ HFI_HOST_FLAGS_GET_PROPERTY),
+ HFI_PAYLOAD_NONE,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ NULL, 0);
+}
+
+void iris_hfi_gen2_packet_session_command(struct iris_inst *inst, u32 pkt_type,
+ u32 flags, u32 port, u32 session_id,
+ u32 payload_type, void *payload,
+ u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_core *core = inst->core;
+
+ iris_hfi_gen2_create_header(inst_hfi_gen2->packet, session_id, core->header_id++);
+
+ iris_hfi_gen2_create_packet(inst_hfi_gen2->packet,
+ pkt_type,
+ flags,
+ payload_type,
+ port,
+ core->packet_id++,
+ payload,
+ payload_size);
+}
+
+void iris_hfi_gen2_packet_session_property(struct iris_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload, u32 payload_size)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_core *core = inst->core;
+
+ iris_hfi_gen2_create_header(inst_hfi_gen2->packet, inst->session_id, core->header_id++);
+
+ iris_hfi_gen2_create_packet(inst_hfi_gen2->packet,
+ pkt_type,
+ flags,
+ payload_type,
+ port,
+ core->packet_id++,
+ payload,
+ payload_size);
+}
+
+void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
+ struct iris_hfi_header *hdr)
+{
+ u32 payload = 1; /* HFI_TRUE */
+
+ iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++);
+
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_PROP_INTRA_FRAME_POWER_COLLAPSE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_U32,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ &payload,
+ sizeof(u32));
+}
+
+void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ iris_hfi_gen2_create_header(hdr, 0 /*session_id*/, core->header_id++);
+
+ iris_hfi_gen2_create_packet(hdr,
+ HFI_CMD_POWER_COLLAPSE,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PAYLOAD_NONE,
+ HFI_PORT_NONE,
+ core->packet_id++,
+ NULL, 0);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
new file mode 100644
index 000000000000..25b9582349ca
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.h
@@ -0,0 +1,125 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_GEN2_PACKET_H__
+#define __IRIS_HFI_GEN2_PACKET_H__
+
+#include "iris_hfi_gen2_defines.h"
+
+struct iris_core;
+
+/**
+ * struct iris_hfi_header
+ *
+ * @size: size of the total packet in bytes including hfi_header
+ * @session_id: For session level hfi_header session_id is non-zero.
+ * For system level hfi_header session_id is zero.
+ * @header_id: unique header id for each hfi_header
+ * @reserved: reserved for future use
+ * @num_packets: number of hfi_packet that are included with the hfi_header
+ */
+struct iris_hfi_header {
+ u32 size;
+ u32 session_id;
+ u32 header_id;
+ u32 reserved[4];
+ u32 num_packets;
+};
+
+/**
+ * struct iris_hfi_packet
+ *
+ * @size: size of the hfi_packet in bytes including payload
+ * @type: one of the below hfi_packet types:
+ * HFI_CMD_*,
+ * HFI_PROP_*,
+ * HFI_ERROR_*,
+ * HFI_INFO_*,
+ * HFI_SYS_ERROR_*
+ * @flags: hfi_packet flags. It is represented as bit masks.
+ * host packet flags are "enum hfi_packet_host_flags"
+ * firmware packet flags are "enum hfi_packet_firmware_flags"
+ * @payload_info: payload information indicated by "enum hfi_packet_payload_info"
+ * @port: hfi_packet port type indicated by "enum hfi_packet_port_type"
+ * This is bitmask and may be applicable to multiple ports.
+ * @packet_id: host hfi_packet contains unique packet id.
+ * firmware returns host packet id in response packet
+ * wherever applicable. If not applicable firmware sets it to zero.
+ * @reserved: reserved for future use.
+ * @payload: flexible array of payload having additional packet information.
+ */
+struct iris_hfi_packet {
+ u32 size;
+ u32 type;
+ u32 flags;
+ u32 payload_info;
+ u32 port;
+ u32 packet_id;
+ u32 reserved[2];
+ u32 payload[];
+};
+
+/**
+ * struct iris_hfi_buffer
+ *
+ * @type: buffer type indicated by "enum hfi_buffer_type"
+ * FW needs to return proper type for any buffer command.
+ * @index: index of the buffer
+ * @base_address: base address of the buffer.
+ * This buffer address is always 4KBytes aligned.
+ * @addr_offset: accessible buffer offset from base address
+ * Decoder bitstream buffer: 256 Bytes aligned
+ * Firmware can uniquely identify a buffer based on
+ * base_address & addr_offset.
+ * HW can read memory only from base_address+addr_offset.
+ * @buffer_size: accessible buffer size in bytes starting from addr_offset
+ * @data_offset: data starts from "base_address + addr_offset + data_offset"
+ * RAW buffer: data_offset is 0. Restriction: 4KBytes aligned
+ * decoder bitstream buffer: no restriction (can be any value)
+ * @data_size: data size in bytes
+ * @flags: buffer flags. It is represented as bit masks.
+ * host buffer flags are "enum hfi_buffer_host_flags"
+ * firmware buffer flags are "enum hfi_buffer_firmware_flags"
+ * @timestamp: timestamp of the buffer in nano seconds (ns)
+ * It is Presentation timestamp (PTS) for encoder & decoder.
+ * Decoder: it is pass through from bitstream to raw buffer.
+ * firmware does not need to return as part of input buffer done.
+ * For any internal buffers: there is no timestamp. Host sets as 0.
+ * @reserved: reserved for future use
+ */
+struct iris_hfi_buffer {
+ u32 type;
+ u32 index;
+ u64 base_address;
+ u32 addr_offset;
+ u32 buffer_size;
+ u32 data_offset;
+ u32 data_size;
+ u64 timestamp;
+ u32 flags;
+ u32 reserved[5];
+};
+
+u32 iris_hfi_gen2_get_color_primaries(u32 primaries);
+u32 iris_hfi_gen2_get_transfer_char(u32 characterstics);
+u32 iris_hfi_gen2_get_matrix_coefficients(u32 coefficients);
+u32 iris_hfi_gen2_get_color_info(u32 matrix_coeff, u32 transfer_char, u32 primaries,
+ u32 colour_description_present_flag, u32 full_range,
+ u32 video_format, u32 video_signal_type_present_flag);
+
+void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr);
+void iris_hfi_gen2_packet_image_version(struct iris_core *core, struct iris_hfi_header *hdr);
+void iris_hfi_gen2_packet_session_command(struct iris_inst *inst, u32 pkt_type,
+ u32 flags, u32 port, u32 session_id,
+ u32 payload_type, void *payload,
+ u32 payload_size);
+void iris_hfi_gen2_packet_session_property(struct iris_inst *inst,
+ u32 pkt_type, u32 flags, u32 port,
+ u32 payload_type, void *payload, u32 payload_size);
+void iris_hfi_gen2_packet_sys_interframe_powercollapse(struct iris_core *core,
+ struct iris_hfi_header *hdr);
+void iris_hfi_gen2_packet_sys_pc_prep(struct iris_core *core, struct iris_hfi_header *hdr);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
new file mode 100644
index 000000000000..b75a01641d5d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -0,0 +1,934 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_defines.h"
+#include "iris_hfi_gen2_packet.h"
+#include "iris_vdec.h"
+#include "iris_vpu_buffer.h"
+#include "iris_vpu_common.h"
+
+struct iris_hfi_gen2_core_hfi_range {
+ u32 begin;
+ u32 end;
+ int (*handle)(struct iris_core *core, struct iris_hfi_packet *pkt);
+};
+
+struct iris_hfi_gen2_inst_hfi_range {
+ u32 begin;
+ u32 end;
+ int (*handle)(struct iris_inst *inst, struct iris_hfi_packet *pkt);
+};
+
+struct iris_hfi_gen2_packet_handle {
+ enum hfi_buffer_type type;
+ int (*handle)(struct iris_inst *inst, struct iris_hfi_packet *pkt);
+};
+
+static u32 iris_hfi_gen2_buf_type_to_driver(enum hfi_buffer_type buf_type)
+{
+ switch (buf_type) {
+ case HFI_BUFFER_BITSTREAM:
+ return BUF_INPUT;
+ case HFI_BUFFER_RAW:
+ return BUF_OUTPUT;
+ case HFI_BUFFER_BIN:
+ return BUF_BIN;
+ case HFI_BUFFER_ARP:
+ return BUF_ARP;
+ case HFI_BUFFER_COMV:
+ return BUF_COMV;
+ case HFI_BUFFER_NON_COMV:
+ return BUF_NON_COMV;
+ case HFI_BUFFER_LINE:
+ return BUF_LINE;
+ case HFI_BUFFER_DPB:
+ return BUF_DPB;
+ case HFI_BUFFER_PERSIST:
+ return BUF_PERSIST;
+ default:
+ return 0;
+ }
+}
+
+static bool iris_hfi_gen2_is_valid_hfi_buffer_type(u32 buffer_type)
+{
+ switch (buffer_type) {
+ case HFI_BUFFER_BITSTREAM:
+ case HFI_BUFFER_RAW:
+ case HFI_BUFFER_BIN:
+ case HFI_BUFFER_ARP:
+ case HFI_BUFFER_COMV:
+ case HFI_BUFFER_NON_COMV:
+ case HFI_BUFFER_LINE:
+ case HFI_BUFFER_DPB:
+ case HFI_BUFFER_PERSIST:
+ case HFI_BUFFER_VPSS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool iris_hfi_gen2_is_valid_hfi_port(u32 port, u32 buffer_type)
+{
+ if (port == HFI_PORT_NONE && buffer_type != HFI_BUFFER_PERSIST)
+ return false;
+
+ if (port != HFI_PORT_BITSTREAM && port != HFI_PORT_RAW)
+ return false;
+
+ return true;
+}
+
+static int iris_hfi_gen2_get_driver_buffer_flags(struct iris_inst *inst, u32 hfi_flags)
+{
+ u32 keyframe = HFI_PICTURE_IDR | HFI_PICTURE_I | HFI_PICTURE_CRA | HFI_PICTURE_BLA;
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ u32 driver_flags = 0;
+
+ if (inst_hfi_gen2->hfi_frame_info.picture_type & keyframe)
+ driver_flags |= V4L2_BUF_FLAG_KEYFRAME;
+ else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_P)
+ driver_flags |= V4L2_BUF_FLAG_PFRAME;
+ else if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_B)
+ driver_flags |= V4L2_BUF_FLAG_BFRAME;
+
+ if (inst_hfi_gen2->hfi_frame_info.data_corrupt || inst_hfi_gen2->hfi_frame_info.overflow)
+ driver_flags |= V4L2_BUF_FLAG_ERROR;
+
+ if (hfi_flags & HFI_BUF_FW_FLAG_LAST ||
+ hfi_flags & HFI_BUF_FW_FLAG_PSC_LAST)
+ driver_flags |= V4L2_BUF_FLAG_LAST;
+
+ return driver_flags;
+}
+
+static bool iris_hfi_gen2_validate_packet_payload(struct iris_hfi_packet *pkt)
+{
+ u32 payload_size = 0;
+
+ switch (pkt->payload_info) {
+ case HFI_PAYLOAD_U32:
+ case HFI_PAYLOAD_S32:
+ case HFI_PAYLOAD_Q16:
+ case HFI_PAYLOAD_U32_ENUM:
+ case HFI_PAYLOAD_32_PACKED:
+ payload_size = 4;
+ break;
+ case HFI_PAYLOAD_U64:
+ case HFI_PAYLOAD_S64:
+ case HFI_PAYLOAD_64_PACKED:
+ payload_size = 8;
+ break;
+ case HFI_PAYLOAD_STRUCTURE:
+ if (pkt->type == HFI_CMD_BUFFER)
+ payload_size = sizeof(struct iris_hfi_buffer);
+ break;
+ default:
+ payload_size = 0;
+ break;
+ }
+
+ if (pkt->size < sizeof(struct iris_hfi_packet) + payload_size)
+ return false;
+
+ return true;
+}
+
+static int iris_hfi_gen2_validate_packet(u8 *response_pkt, u8 *core_resp_pkt)
+{
+ u8 *response_limit = core_resp_pkt + IFACEQ_CORE_PKT_SIZE;
+ u32 response_pkt_size = *(u32 *)response_pkt;
+
+ if (!response_pkt_size)
+ return -EINVAL;
+
+ if (response_pkt_size < sizeof(struct iris_hfi_packet))
+ return -EINVAL;
+
+ if (response_pkt + response_pkt_size > response_limit)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int iris_hfi_gen2_validate_hdr_packet(struct iris_core *core, struct iris_hfi_header *hdr)
+{
+ struct iris_hfi_packet *packet;
+ int ret;
+ u8 *pkt;
+ u32 i;
+
+ if (hdr->size < sizeof(*hdr) + sizeof(*packet))
+ return -EINVAL;
+
+ pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+
+ for (i = 0; i < hdr->num_packets; i++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ ret = iris_hfi_gen2_validate_packet(pkt, core->response_packet);
+ if (ret)
+ return ret;
+
+ pkt += packet->size;
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_session_info(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct iris_core *core = inst->core;
+ int ret = 0;
+ char *info;
+
+ switch (pkt->type) {
+ case HFI_INFO_UNSUPPORTED:
+ info = "unsupported";
+ break;
+ case HFI_INFO_DATA_CORRUPT:
+ info = "data corrupt";
+ inst_hfi_gen2->hfi_frame_info.data_corrupt = 1;
+ break;
+ case HFI_INFO_BUFFER_OVERFLOW:
+ info = "buffer overflow";
+ inst_hfi_gen2->hfi_frame_info.overflow = 1;
+ break;
+ case HFI_INFO_HFI_FLAG_DRAIN_LAST:
+ info = "drain last flag";
+ ret = iris_inst_sub_state_change_drain_last(inst);
+ break;
+ case HFI_INFO_HFI_FLAG_PSC_LAST:
+ info = "drc last flag";
+ ret = iris_inst_sub_state_change_drc_last(inst);
+ break;
+ default:
+ info = "unknown";
+ break;
+ }
+
+ dev_dbg(core->dev, "session info received %#x: %s\n",
+ pkt->type, info);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_error(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_core *core = inst->core;
+ char *error;
+
+ switch (pkt->type) {
+ case HFI_ERROR_MAX_SESSIONS:
+ error = "exceeded max sessions";
+ break;
+ case HFI_ERROR_UNKNOWN_SESSION:
+ error = "unknown session id";
+ break;
+ case HFI_ERROR_INVALID_STATE:
+ error = "invalid operation for current state";
+ break;
+ case HFI_ERROR_INSUFFICIENT_RESOURCES:
+ error = "insufficient resources";
+ break;
+ case HFI_ERROR_BUFFER_NOT_SET:
+ error = "internal buffers not set";
+ break;
+ case HFI_ERROR_FATAL:
+ error = "fatal error";
+ break;
+ case HFI_ERROR_STREAM_UNSUPPORTED:
+ error = "unsupported stream";
+ break;
+ default:
+ error = "unknown";
+ break;
+ }
+
+ dev_err(core->dev, "session error received %#x: %s\n", pkt->type, error);
+ iris_vb2_queue_error(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_system_error(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_inst *instance;
+
+ dev_err(core->dev, "received system error of type %#x\n", pkt->type);
+
+ core->state = IRIS_CORE_ERROR;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list)
+ iris_inst_change_state(instance, IRIS_INST_ERROR);
+ mutex_unlock(&core->lock);
+
+ schedule_delayed_work(&core->sys_error_handler, msecs_to_jiffies(10));
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_system_init(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+ core->state = IRIS_CORE_ERROR;
+ return 0;
+ }
+
+ complete(&core->core_init_done);
+
+ return 0;
+}
+
+static void iris_hfi_gen2_handle_session_close(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return;
+ }
+
+ complete(&inst->completion);
+}
+
+static int iris_hfi_gen2_handle_input_buffer(struct iris_inst *inst,
+ struct iris_hfi_buffer *buffer)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct iris_buffer *buf;
+ bool found = false;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (buf->index == buffer->index) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ return -EINVAL;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ buf->attr |= BUF_ATTR_DEQUEUED;
+
+ buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, buffer->flags);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_output_buffer(struct iris_inst *inst,
+ struct iris_hfi_buffer *hfi_buffer)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *m2m_buffer, *n;
+ struct iris_buffer *buf;
+ bool found = false;
+ int ret;
+
+ if (hfi_buffer->flags & HFI_BUF_FW_FLAG_LAST) {
+ ret = iris_inst_sub_state_change_drain_last(inst);
+ if (ret)
+ return ret;
+ }
+
+ if (hfi_buffer->flags & HFI_BUF_FW_FLAG_PSC_LAST) {
+ ret = iris_inst_sub_state_change_drc_last(inst);
+ if (ret)
+ return ret;
+ }
+
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, m2m_buffer, n) {
+ buf = to_iris_buffer(&m2m_buffer->vb);
+ if (buf->index == hfi_buffer->index &&
+ buf->device_addr == hfi_buffer->base_address &&
+ buf->data_offset == hfi_buffer->data_offset) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ return -EINVAL;
+
+ buf->data_offset = hfi_buffer->data_offset;
+ buf->data_size = hfi_buffer->data_size;
+ buf->timestamp = hfi_buffer->timestamp;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ buf->attr |= BUF_ATTR_DEQUEUED;
+
+ buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, hfi_buffer->flags);
+
+ return 0;
+}
+
+static void iris_hfi_gen2_handle_dequeue_buffers(struct iris_inst *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf = NULL;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEQUEUED) {
+ buf->attr &= ~BUF_ATTR_DEQUEUED;
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEQUEUED) {
+ buf->attr &= ~BUF_ATTR_DEQUEUED;
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+}
+
+static int iris_hfi_gen2_handle_release_internal_buffer(struct iris_inst *inst,
+ struct iris_hfi_buffer *buffer)
+{
+ u32 buf_type = iris_hfi_gen2_buf_type_to_driver(buffer->type);
+ struct iris_buffers *buffers = &inst->buffers[buf_type];
+ struct iris_buffer *buf, *iter;
+ bool found = false;
+ int ret = 0;
+
+ list_for_each_entry(iter, &buffers->list, list) {
+ if (iter->device_addr == buffer->base_address) {
+ found = true;
+ buf = iter;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ buf->attr &= ~BUF_ATTR_QUEUED;
+ if (buf->attr & BUF_ATTR_PENDING_RELEASE)
+ ret = iris_destroy_internal_buffer(inst, buf);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_stop(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret = 0;
+
+ if (pkt->port == HFI_PORT_RAW)
+ ret = iris_inst_sub_state_change_pause(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ else if (pkt->port == HFI_PORT_BITSTREAM)
+ ret = iris_inst_sub_state_change_pause(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ complete(&inst->completion);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_buffer(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_hfi_buffer *buffer;
+
+ if (pkt->payload_info == HFI_PAYLOAD_NONE)
+ return 0;
+
+ if (!iris_hfi_gen2_validate_packet_payload(pkt)) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return 0;
+ }
+
+ buffer = (struct iris_hfi_buffer *)((u8 *)pkt + sizeof(*pkt));
+ if (!iris_hfi_gen2_is_valid_hfi_buffer_type(buffer->type))
+ return 0;
+
+ if (!iris_hfi_gen2_is_valid_hfi_port(pkt->port, buffer->type))
+ return 0;
+
+ if (buffer->type == HFI_BUFFER_BITSTREAM)
+ return iris_hfi_gen2_handle_input_buffer(inst, buffer);
+ else if (buffer->type == HFI_BUFFER_RAW)
+ return iris_hfi_gen2_handle_output_buffer(inst, buffer);
+ else
+ return iris_hfi_gen2_handle_release_internal_buffer(inst, buffer);
+}
+
+static int iris_hfi_gen2_handle_session_drain(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret = 0;
+
+ if (!(pkt->flags & HFI_FW_FLAGS_SUCCESS)) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return 0;
+ }
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN)
+ ret = iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_INPUT_PAUSE);
+
+ return ret;
+}
+
+static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp;
+ u32 primaries, matrix_coeff, transfer_char;
+ struct hfi_subscription_params subsc_params;
+ u32 colour_description_present_flag;
+ u32 video_signal_type_present_flag;
+ struct iris_core *core = inst->core;
+ u32 full_range, width, height;
+ struct vb2_queue *dst_q;
+ struct v4l2_ctrl *ctrl;
+
+ subsc_params = inst_hfi_gen2->src_subcr_params;
+ width = (subsc_params.bitstream_resolution &
+ HFI_BITMASK_BITSTREAM_WIDTH) >> 16;
+ height = subsc_params.bitstream_resolution &
+ HFI_BITMASK_BITSTREAM_HEIGHT;
+
+ pixmp_ip->width = width;
+ pixmp_ip->height = height;
+
+ pixmp_op->width = ALIGN(width, 128);
+ pixmp_op->height = ALIGN(height, 32);
+ pixmp_op->plane_fmt[0].bytesperline = ALIGN(width, 128);
+ pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+
+ matrix_coeff = subsc_params.color_info & 0xFF;
+ transfer_char = (subsc_params.color_info & 0xFF00) >> 8;
+ primaries = (subsc_params.color_info & 0xFF0000) >> 16;
+ colour_description_present_flag =
+ (subsc_params.color_info & 0x1000000) >> 24;
+ full_range = (subsc_params.color_info & 0x2000000) >> 25;
+ video_signal_type_present_flag =
+ (subsc_params.color_info & 0x20000000) >> 29;
+
+ pixmp_op->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pixmp_op->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pixmp_op->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pixmp_op->quantization = V4L2_QUANTIZATION_DEFAULT;
+
+ if (video_signal_type_present_flag) {
+ pixmp_op->quantization =
+ full_range ?
+ V4L2_QUANTIZATION_FULL_RANGE :
+ V4L2_QUANTIZATION_LIM_RANGE;
+ if (colour_description_present_flag) {
+ pixmp_op->colorspace =
+ iris_hfi_get_v4l2_color_primaries(primaries);
+ pixmp_op->xfer_func =
+ iris_hfi_get_v4l2_transfer_char(transfer_char);
+ pixmp_op->ycbcr_enc =
+ iris_hfi_get_v4l2_matrix_coefficients(matrix_coeff);
+ }
+ }
+
+ pixmp_ip->colorspace = pixmp_op->colorspace;
+ pixmp_ip->xfer_func = pixmp_op->xfer_func;
+ pixmp_ip->ycbcr_enc = pixmp_op->ycbcr_enc;
+ pixmp_ip->quantization = pixmp_op->quantization;
+
+ inst->crop.top = subsc_params.crop_offsets[0] & 0xFFFF;
+ inst->crop.left = (subsc_params.crop_offsets[0] >> 16) & 0xFFFF;
+ inst->crop.height = pixmp_ip->height -
+ (subsc_params.crop_offsets[1] & 0xFFFF) - inst->crop.top;
+ inst->crop.width = pixmp_ip->width -
+ ((subsc_params.crop_offsets[1] >> 16) & 0xFFFF) - inst->crop.left;
+
+ inst->fw_caps[PROFILE].value = subsc_params.profile;
+ inst->fw_caps[LEVEL].value = subsc_params.level;
+ inst->fw_caps[POC].value = subsc_params.pic_order_cnt;
+
+ if (subsc_params.bit_depth != BIT_DEPTH_8 ||
+ !(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) {
+ dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n",
+ subsc_params.bit_depth, subsc_params.coded_frames);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+
+ inst->fw_min_count = subsc_params.fw_min_count;
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = pixmp_op->plane_fmt[0].sizeimage;
+ ctrl = v4l2_ctrl_find(&inst->ctrl_handler, V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, inst->buffers[BUF_OUTPUT].min_count);
+
+ dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ dst_q->min_reqbufs_allocation = inst->buffers[BUF_OUTPUT].min_count;
+}
+
+static int iris_hfi_gen2_handle_src_change(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret;
+
+ if (pkt->port != HFI_PORT_BITSTREAM)
+ return 0;
+
+ ret = iris_inst_sub_state_change_drc(inst);
+ if (ret)
+ return ret;
+
+ iris_hfi_gen2_read_input_subcr_params(inst);
+ iris_vdec_src_change(inst);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_session_command(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ int ret = 0;
+
+ switch (pkt->type) {
+ case HFI_CMD_CLOSE:
+ iris_hfi_gen2_handle_session_close(inst, pkt);
+ break;
+ case HFI_CMD_STOP:
+ iris_hfi_gen2_handle_session_stop(inst, pkt);
+ break;
+ case HFI_CMD_BUFFER:
+ ret = iris_hfi_gen2_handle_session_buffer(inst, pkt);
+ break;
+ case HFI_CMD_SETTINGS_CHANGE:
+ ret = iris_hfi_gen2_handle_src_change(inst, pkt);
+ break;
+ case HFI_CMD_DRAIN:
+ ret = iris_hfi_gen2_handle_session_drain(inst, pkt);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
+ struct iris_hfi_packet *pkt)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+
+ if (pkt->port != HFI_PORT_BITSTREAM)
+ return 0;
+
+ if (pkt->flags & HFI_FW_FLAGS_INFORMATION)
+ return 0;
+
+ switch (pkt->type) {
+ case HFI_PROP_BITSTREAM_RESOLUTION:
+ inst_hfi_gen2->src_subcr_params.bitstream_resolution = pkt->payload[0];
+ break;
+ case HFI_PROP_CROP_OFFSETS:
+ inst_hfi_gen2->src_subcr_params.crop_offsets[0] = pkt->payload[0];
+ inst_hfi_gen2->src_subcr_params.crop_offsets[1] = pkt->payload[1];
+ break;
+ case HFI_PROP_CODED_FRAMES:
+ inst_hfi_gen2->src_subcr_params.coded_frames = pkt->payload[0];
+ break;
+ case HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT:
+ inst_hfi_gen2->src_subcr_params.fw_min_count = pkt->payload[0];
+ break;
+ case HFI_PROP_PIC_ORDER_CNT_TYPE:
+ inst_hfi_gen2->src_subcr_params.pic_order_cnt = pkt->payload[0];
+ break;
+ case HFI_PROP_SIGNAL_COLOR_INFO:
+ inst_hfi_gen2->src_subcr_params.color_info = pkt->payload[0];
+ break;
+ case HFI_PROP_PROFILE:
+ inst_hfi_gen2->src_subcr_params.profile = pkt->payload[0];
+ break;
+ case HFI_PROP_LEVEL:
+ inst_hfi_gen2->src_subcr_params.level = pkt->payload[0];
+ break;
+ case HFI_PROP_PICTURE_TYPE:
+ inst_hfi_gen2->hfi_frame_info.picture_type = pkt->payload[0];
+ break;
+ case HFI_PROP_NO_OUTPUT:
+ inst_hfi_gen2->hfi_frame_info.no_output = 1;
+ break;
+ case HFI_PROP_QUALITY_MODE:
+ case HFI_PROP_STAGE:
+ case HFI_PROP_PIPE:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_image_version_property(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ u8 *str_image_version = (u8 *)pkt + sizeof(*pkt);
+ u32 req_bytes = pkt->size - sizeof(*pkt);
+ char fw_version[IRIS_FW_VERSION_LENGTH];
+ u32 i;
+
+ if (req_bytes < IRIS_FW_VERSION_LENGTH - 1)
+ return -EINVAL;
+
+ for (i = 0; i < IRIS_FW_VERSION_LENGTH - 1; i++) {
+ if (str_image_version[i] != '\0')
+ fw_version[i] = str_image_version[i];
+ else
+ fw_version[i] = ' ';
+ }
+ fw_version[i] = '\0';
+ dev_dbg(core->dev, "firmware version: %s\n", fw_version);
+
+ return 0;
+}
+
+static int iris_hfi_gen2_handle_system_property(struct iris_core *core,
+ struct iris_hfi_packet *pkt)
+{
+ switch (pkt->type) {
+ case HFI_PROP_IMAGE_VERSION:
+ return iris_hfi_gen2_handle_image_version_property(core, pkt);
+ default:
+ return 0;
+ }
+}
+
+static int iris_hfi_gen2_handle_system_response(struct iris_core *core,
+ struct iris_hfi_header *hdr)
+{
+ u8 *start_pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ struct iris_hfi_packet *packet;
+ u32 i, j;
+ u8 *pkt;
+ int ret;
+ static const struct iris_hfi_gen2_core_hfi_range range[] = {
+ {HFI_SYSTEM_ERROR_BEGIN, HFI_SYSTEM_ERROR_END, iris_hfi_gen2_handle_system_error },
+ {HFI_PROP_BEGIN, HFI_PROP_END, iris_hfi_gen2_handle_system_property },
+ {HFI_CMD_BEGIN, HFI_CMD_END, iris_hfi_gen2_handle_system_init },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(range); i++) {
+ pkt = start_pkt;
+ for (j = 0; j < hdr->num_packets; j++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ if (packet->flags & HFI_FW_FLAGS_SYSTEM_ERROR) {
+ ret = iris_hfi_gen2_handle_system_error(core, packet);
+ return ret;
+ }
+
+ if (packet->type > range[i].begin && packet->type < range[i].end) {
+ ret = range[i].handle(core, packet);
+ if (ret)
+ return ret;
+
+ if (packet->type > HFI_SYSTEM_ERROR_BEGIN &&
+ packet->type < HFI_SYSTEM_ERROR_END)
+ return 0;
+ }
+ pkt += packet->size;
+ }
+ }
+
+ return 0;
+}
+
+static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst)
+{
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ struct v4l2_pix_format_mplane *pixmp_ip = &inst->fmt_src->fmt.pix_mp;
+ struct v4l2_pix_format_mplane *pixmp_op = &inst->fmt_dst->fmt.pix_mp;
+ u32 bottom_offset = (pixmp_ip->height - inst->crop.height);
+ u32 right_offset = (pixmp_ip->width - inst->crop.width);
+ struct hfi_subscription_params *subsc_params;
+ u32 primaries, matrix_coeff, transfer_char;
+ u32 colour_description_present_flag = 0;
+ u32 video_signal_type_present_flag = 0;
+ u32 full_range, video_format = 0;
+ u32 left_offset = inst->crop.left;
+ u32 top_offset = inst->crop.top;
+
+ subsc_params = &inst_hfi_gen2->src_subcr_params;
+ subsc_params->bitstream_resolution =
+ pixmp_ip->width << 16 | pixmp_ip->height;
+ subsc_params->crop_offsets[0] =
+ left_offset << 16 | top_offset;
+ subsc_params->crop_offsets[1] =
+ right_offset << 16 | bottom_offset;
+ subsc_params->fw_min_count = inst->buffers[BUF_OUTPUT].min_count;
+
+ primaries = iris_hfi_gen2_get_color_primaries(pixmp_op->colorspace);
+ matrix_coeff = iris_hfi_gen2_get_matrix_coefficients(pixmp_op->ycbcr_enc);
+ transfer_char = iris_hfi_gen2_get_transfer_char(pixmp_op->xfer_func);
+ full_range = pixmp_op->quantization == V4L2_QUANTIZATION_FULL_RANGE ? 1 : 0;
+ subsc_params->color_info =
+ iris_hfi_gen2_get_color_info(matrix_coeff, transfer_char, primaries,
+ colour_description_present_flag,
+ full_range, video_format,
+ video_signal_type_present_flag);
+
+ subsc_params->profile = inst->fw_caps[PROFILE].value;
+ subsc_params->level = inst->fw_caps[LEVEL].value;
+ subsc_params->pic_order_cnt = inst->fw_caps[POC].value;
+ subsc_params->bit_depth = inst->fw_caps[BIT_DEPTH].value;
+ if (inst->fw_caps[CODED_FRAMES].value ==
+ CODED_FRAMES_PROGRESSIVE)
+ subsc_params->coded_frames = HFI_BITMASK_FRAME_MBS_ONLY_FLAG;
+ else
+ subsc_params->coded_frames = 0;
+}
+
+static int iris_hfi_gen2_handle_session_response(struct iris_core *core,
+ struct iris_hfi_header *hdr)
+{
+ u8 *pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ struct iris_inst_hfi_gen2 *inst_hfi_gen2;
+ struct iris_hfi_packet *packet;
+ struct iris_inst *inst;
+ bool dequeue = false;
+ int ret = 0;
+ u32 i, j;
+ static const struct iris_hfi_gen2_inst_hfi_range range[] = {
+ {HFI_SESSION_ERROR_BEGIN, HFI_SESSION_ERROR_END,
+ iris_hfi_gen2_handle_session_error},
+ {HFI_INFORMATION_BEGIN, HFI_INFORMATION_END,
+ iris_hfi_gen2_handle_session_info},
+ {HFI_PROP_BEGIN, HFI_PROP_END,
+ iris_hfi_gen2_handle_session_property},
+ {HFI_CMD_BEGIN, HFI_CMD_END,
+ iris_hfi_gen2_handle_session_command },
+ };
+
+ inst = iris_get_instance(core, hdr->session_id);
+ if (!inst)
+ return -EINVAL;
+
+ mutex_lock(&inst->lock);
+ inst_hfi_gen2 = to_iris_inst_hfi_gen2(inst);
+ memset(&inst_hfi_gen2->hfi_frame_info, 0, sizeof(struct iris_hfi_frame_info));
+
+ for (i = 0; i < hdr->num_packets; i++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ if (packet->type == HFI_CMD_SETTINGS_CHANGE) {
+ if (packet->port == HFI_PORT_BITSTREAM) {
+ iris_hfi_gen2_init_src_change_param(inst);
+ break;
+ }
+ }
+ pkt += packet->size;
+ }
+
+ pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ for (i = 0; i < ARRAY_SIZE(range); i++) {
+ pkt = (u8 *)((u8 *)hdr + sizeof(*hdr));
+ for (j = 0; j < hdr->num_packets; j++) {
+ packet = (struct iris_hfi_packet *)pkt;
+ if (packet->flags & HFI_FW_FLAGS_SESSION_ERROR)
+ iris_hfi_gen2_handle_session_error(inst, packet);
+
+ if (packet->type > range[i].begin && packet->type < range[i].end) {
+ dequeue |= (packet->type == HFI_CMD_BUFFER);
+ ret = range[i].handle(inst, packet);
+ if (ret)
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ }
+ pkt += packet->size;
+ }
+ }
+
+ if (dequeue)
+ iris_hfi_gen2_handle_dequeue_buffers(inst);
+
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_hfi_gen2_handle_response(struct iris_core *core, void *response)
+{
+ struct iris_hfi_header *hdr = (struct iris_hfi_header *)response;
+ int ret;
+
+ ret = iris_hfi_gen2_validate_hdr_packet(core, hdr);
+ if (ret)
+ return iris_hfi_gen2_handle_system_error(core, NULL);
+
+ if (!hdr->session_id)
+ return iris_hfi_gen2_handle_system_response(core, hdr);
+ else
+ return iris_hfi_gen2_handle_session_response(core, hdr);
+}
+
+static void iris_hfi_gen2_flush_debug_queue(struct iris_core *core, u8 *packet)
+{
+ struct hfi_debug_header *pkt;
+ u8 *log;
+
+ while (!iris_hfi_queue_dbg_read(core, packet)) {
+ pkt = (struct hfi_debug_header *)packet;
+
+ if (pkt->size < sizeof(*pkt))
+ continue;
+
+ if (pkt->size >= IFACEQ_CORE_PKT_SIZE)
+ continue;
+
+ packet[pkt->size] = '\0';
+ log = (u8 *)packet + sizeof(*pkt) + 1;
+ dev_dbg(core->dev, "%s", log);
+ }
+}
+
+static void iris_hfi_gen2_response_handler(struct iris_core *core)
+{
+ if (iris_vpu_watchdog(core, core->intr_status)) {
+ struct iris_hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT};
+
+ dev_err(core->dev, "cpu watchdog error received\n");
+ core->state = IRIS_CORE_ERROR;
+ iris_hfi_gen2_handle_system_error(core, &pkt);
+
+ return;
+ }
+
+ memset(core->response_packet, 0, sizeof(struct iris_hfi_header));
+ while (!iris_hfi_queue_msg_read(core, core->response_packet)) {
+ iris_hfi_gen2_handle_response(core, core->response_packet);
+ memset(core->response_packet, 0, sizeof(struct iris_hfi_header));
+ }
+
+ iris_hfi_gen2_flush_debug_queue(core, core->response_packet);
+}
+
+static const struct iris_hfi_response_ops iris_hfi_gen2_response_ops = {
+ .hfi_response_handler = iris_hfi_gen2_response_handler,
+};
+
+void iris_hfi_gen2_response_ops_init(struct iris_core *core)
+{
+ core->hfi_response_ops = &iris_hfi_gen2_response_ops;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
new file mode 100644
index 000000000000..fac7df0c4d1a
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+
+#include "iris_core.h"
+#include "iris_hfi_queue.h"
+#include "iris_vpu_common.h"
+
+static int iris_hfi_queue_write(struct iris_iface_q_info *qinfo, void *packet, u32 packet_size)
+{
+ struct iris_hfi_queue_header *queue = qinfo->qhdr;
+ u32 write_idx = queue->write_idx * sizeof(u32);
+ u32 read_idx = queue->read_idx * sizeof(u32);
+ u32 empty_space, new_write_idx, residue;
+ u32 *write_ptr;
+
+ if (write_idx < read_idx)
+ empty_space = read_idx - write_idx;
+ else
+ empty_space = IFACEQ_QUEUE_SIZE - (write_idx - read_idx);
+ if (empty_space < packet_size)
+ return -ENOSPC;
+
+ queue->tx_req = 0;
+
+ new_write_idx = write_idx + packet_size;
+ write_ptr = (u32 *)((u8 *)qinfo->kernel_vaddr + write_idx);
+
+ if (write_ptr < (u32 *)qinfo->kernel_vaddr ||
+ write_ptr > (u32 *)(qinfo->kernel_vaddr +
+ IFACEQ_QUEUE_SIZE))
+ return -EINVAL;
+
+ if (new_write_idx < IFACEQ_QUEUE_SIZE) {
+ memcpy(write_ptr, packet, packet_size);
+ } else {
+ residue = new_write_idx - IFACEQ_QUEUE_SIZE;
+ memcpy(write_ptr, packet, (packet_size - residue));
+ memcpy(qinfo->kernel_vaddr,
+ packet + (packet_size - residue), residue);
+ new_write_idx = residue;
+ }
+
+ /* Make sure packet is written before updating the write index */
+ mb();
+ queue->write_idx = new_write_idx / sizeof(u32);
+
+ /* Make sure write index is updated before an interrupt is raised */
+ mb();
+
+ return 0;
+}
+
+static int iris_hfi_queue_read(struct iris_iface_q_info *qinfo, void *packet)
+{
+ struct iris_hfi_queue_header *queue = qinfo->qhdr;
+ u32 write_idx = queue->write_idx * sizeof(u32);
+ u32 read_idx = queue->read_idx * sizeof(u32);
+ u32 packet_size, receive_request = 0;
+ u32 new_read_idx, residue;
+ u32 *read_ptr;
+ int ret = 0;
+
+ if (queue->queue_type == IFACEQ_MSGQ_ID)
+ receive_request = 1;
+
+ if (read_idx == write_idx) {
+ queue->rx_req = receive_request;
+ /* Ensure qhdr is updated in main memory */
+ mb();
+ return -ENODATA;
+ }
+
+ read_ptr = qinfo->kernel_vaddr + read_idx;
+ if (read_ptr < (u32 *)qinfo->kernel_vaddr ||
+ read_ptr > (u32 *)(qinfo->kernel_vaddr +
+ IFACEQ_QUEUE_SIZE - sizeof(*read_ptr)))
+ return -ENODATA;
+
+ packet_size = *read_ptr;
+ if (!packet_size)
+ return -EINVAL;
+
+ new_read_idx = read_idx + packet_size;
+ if (packet_size <= IFACEQ_CORE_PKT_SIZE) {
+ if (new_read_idx < IFACEQ_QUEUE_SIZE) {
+ memcpy(packet, read_ptr, packet_size);
+ } else {
+ residue = new_read_idx - IFACEQ_QUEUE_SIZE;
+ memcpy(packet, read_ptr, (packet_size - residue));
+ memcpy((packet + (packet_size - residue)),
+ qinfo->kernel_vaddr, residue);
+ new_read_idx = residue;
+ }
+ } else {
+ new_read_idx = write_idx;
+ ret = -EBADMSG;
+ }
+
+ queue->rx_req = receive_request;
+
+ queue->read_idx = new_read_idx / sizeof(u32);
+ /* Ensure qhdr is updated in main memory */
+ mb();
+
+ return ret;
+}
+
+int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_size)
+{
+ struct iris_iface_q_info *q_info = &core->command_queue;
+
+ if (core->state == IRIS_CORE_ERROR)
+ return -EINVAL;
+
+ if (!iris_hfi_queue_write(q_info, pkt, pkt_size)) {
+ iris_vpu_raise_interrupt(core);
+ } else {
+ dev_err(core->dev, "queue full\n");
+ return -ENODATA;
+ }
+
+ return 0;
+}
+
+int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt, u32 pkt_size)
+{
+ int ret;
+
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ goto exit;
+
+ mutex_lock(&core->lock);
+ ret = iris_hfi_queue_cmd_write_locked(core, pkt, pkt_size);
+ if (ret) {
+ mutex_unlock(&core->lock);
+ goto exit;
+ }
+ mutex_unlock(&core->lock);
+
+ pm_runtime_mark_last_busy(core->dev);
+ pm_runtime_put_autosuspend(core->dev);
+
+ return 0;
+
+exit:
+ pm_runtime_put_sync(core->dev);
+
+ return ret;
+}
+
+int iris_hfi_queue_msg_read(struct iris_core *core, void *pkt)
+{
+ struct iris_iface_q_info *q_info = &core->message_queue;
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (iris_hfi_queue_read(q_info, pkt)) {
+ ret = -ENODATA;
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+int iris_hfi_queue_dbg_read(struct iris_core *core, void *pkt)
+{
+ struct iris_iface_q_info *q_info = &core->debug_queue;
+ int ret = 0;
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT) {
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ if (iris_hfi_queue_read(q_info, pkt)) {
+ ret = -ENODATA;
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static void iris_hfi_queue_set_header(struct iris_core *core, u32 queue_id,
+ struct iris_iface_q_info *iface_q)
+{
+ iface_q->qhdr->status = 0x1;
+ iface_q->qhdr->start_addr = iface_q->device_addr;
+ iface_q->qhdr->header_type = IFACEQ_DFLT_QHDR;
+ iface_q->qhdr->queue_type = queue_id;
+ iface_q->qhdr->q_size = IFACEQ_QUEUE_SIZE / sizeof(u32);
+ iface_q->qhdr->pkt_size = 0; /* variable packet size */
+ iface_q->qhdr->rx_wm = 0x1;
+ iface_q->qhdr->tx_wm = 0x1;
+ iface_q->qhdr->rx_req = 0x1;
+ iface_q->qhdr->tx_req = 0x0;
+ iface_q->qhdr->rx_irq_status = 0x0;
+ iface_q->qhdr->tx_irq_status = 0x0;
+ iface_q->qhdr->read_idx = 0x0;
+ iface_q->qhdr->write_idx = 0x0;
+
+ /*
+ * Set receive request to zero on debug queue as there is no
+ * need of interrupt from video hardware for debug messages
+ */
+ if (queue_id == IFACEQ_DBGQ_ID)
+ iface_q->qhdr->rx_req = 0;
+}
+
+static void
+iris_hfi_queue_init(struct iris_core *core, u32 queue_id, struct iris_iface_q_info *iface_q)
+{
+ struct iris_hfi_queue_table_header *q_tbl_hdr = core->iface_q_table_vaddr;
+ u32 offset = sizeof(*q_tbl_hdr) + (queue_id * IFACEQ_QUEUE_SIZE);
+
+ iface_q->device_addr = core->iface_q_table_daddr + offset;
+ iface_q->kernel_vaddr =
+ (void *)((char *)core->iface_q_table_vaddr + offset);
+ iface_q->qhdr = &q_tbl_hdr->q_hdr[queue_id];
+
+ iris_hfi_queue_set_header(core, queue_id, iface_q);
+}
+
+static void iris_hfi_queue_deinit(struct iris_iface_q_info *iface_q)
+{
+ iface_q->qhdr = NULL;
+ iface_q->kernel_vaddr = NULL;
+ iface_q->device_addr = 0;
+}
+
+int iris_hfi_queues_init(struct iris_core *core)
+{
+ struct iris_hfi_queue_table_header *q_tbl_hdr;
+ u32 queue_size;
+
+ /* Iris hardware requires 4K queue alignment */
+ queue_size = ALIGN((sizeof(*q_tbl_hdr) + (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ)), SZ_4K);
+ core->iface_q_table_vaddr = dma_alloc_attrs(core->dev, queue_size,
+ &core->iface_q_table_daddr,
+ GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
+ if (!core->iface_q_table_vaddr) {
+ dev_err(core->dev, "queues alloc and map failed\n");
+ return -ENOMEM;
+ }
+
+ core->sfr_vaddr = dma_alloc_attrs(core->dev, SFR_SIZE,
+ &core->sfr_daddr,
+ GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
+ if (!core->sfr_vaddr) {
+ dev_err(core->dev, "sfr alloc and map failed\n");
+ dma_free_attrs(core->dev, sizeof(*q_tbl_hdr), core->iface_q_table_vaddr,
+ core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
+ return -ENOMEM;
+ }
+
+ iris_hfi_queue_init(core, IFACEQ_CMDQ_ID, &core->command_queue);
+ iris_hfi_queue_init(core, IFACEQ_MSGQ_ID, &core->message_queue);
+ iris_hfi_queue_init(core, IFACEQ_DBGQ_ID, &core->debug_queue);
+
+ q_tbl_hdr = (struct iris_hfi_queue_table_header *)core->iface_q_table_vaddr;
+ q_tbl_hdr->version = 0;
+ q_tbl_hdr->device_addr = (void *)core;
+ strscpy(q_tbl_hdr->name, "iris-hfi-queues", sizeof(q_tbl_hdr->name));
+ q_tbl_hdr->size = sizeof(*q_tbl_hdr);
+ q_tbl_hdr->qhdr0_offset = sizeof(*q_tbl_hdr) -
+ (IFACEQ_NUMQ * sizeof(struct iris_hfi_queue_header));
+ q_tbl_hdr->qhdr_size = sizeof(q_tbl_hdr->q_hdr[0]);
+ q_tbl_hdr->num_q = IFACEQ_NUMQ;
+ q_tbl_hdr->num_active_q = IFACEQ_NUMQ;
+
+ /* Write sfr size in first word to be used by firmware */
+ *((u32 *)core->sfr_vaddr) = SFR_SIZE;
+
+ return 0;
+}
+
+void iris_hfi_queues_deinit(struct iris_core *core)
+{
+ u32 queue_size;
+
+ if (!core->iface_q_table_vaddr)
+ return;
+
+ iris_hfi_queue_deinit(&core->debug_queue);
+ iris_hfi_queue_deinit(&core->message_queue);
+ iris_hfi_queue_deinit(&core->command_queue);
+
+ dma_free_attrs(core->dev, SFR_SIZE, core->sfr_vaddr,
+ core->sfr_daddr, DMA_ATTR_WRITE_COMBINE);
+
+ core->sfr_vaddr = NULL;
+ core->sfr_daddr = 0;
+
+ queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) +
+ (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K);
+
+ dma_free_attrs(core->dev, queue_size, core->iface_q_table_vaddr,
+ core->iface_q_table_daddr, DMA_ATTR_WRITE_COMBINE);
+
+ core->iface_q_table_vaddr = NULL;
+ core->iface_q_table_daddr = 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.h b/drivers/media/platform/qcom/iris/iris_hfi_queue.h
new file mode 100644
index 000000000000..2174fc5ce618
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.h
@@ -0,0 +1,182 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_HFI_QUEUE_H__
+#define __IRIS_HFI_QUEUE_H__
+
+struct iris_core;
+
+/*
+ * Max 64 Buffers ( 32 input buffers and 32 output buffers)
+ * can be queued by v4l2 framework at any given time.
+ */
+#define IFACEQ_MAX_BUF_COUNT 64
+/*
+ * Max session supported are 16.
+ * this value is used to calcualte the size of
+ * individual shared queue.
+ */
+#define IFACE_MAX_PARALLEL_SESSIONS 16
+#define IFACEQ_DFLT_QHDR 0x0101
+#define IFACEQ_MAX_PKT_SIZE 1024 /* Maximum size of a packet in the queue */
+
+/*
+ * SFR: Subsystem Failure Reason
+ * when hardware goes into bad state/failure, firmware fills this memory
+ * and driver will get to know the actual failure reason from this SFR buffer.
+ */
+#define SFR_SIZE SZ_4K /* Iris hardware requires 4K queue alignment */
+
+#define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \
+ IFACEQ_MAX_BUF_COUNT * IFACE_MAX_PARALLEL_SESSIONS)
+
+/*
+ * Memory layout of the shared queues:
+ *
+ * ||=================|| ^ ^ ^
+ * || || | | |
+ * || Queue Table || 288 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||-----------------|| V | |
+ * ||-----------------|| ^ | |
+ * || || | | |
+ * || Command Queue || 56 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||-----------------|| V 456 Bytes |
+ * ||-----------------|| ^ | |
+ * || || | | |
+ * || Message Queue || 56 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||-----------------|| V | Buffer size aligned to 4k
+ * ||-----------------|| ^ | Overall Queue Size = 2,404 KB
+ * || || | | |
+ * || Debug Queue || 56 Bytes | |
+ * || Header || | | |
+ * || || | | |
+ * ||=================|| V V |
+ * ||=================|| ^ |
+ * || || | |
+ * || Command || 800 KB |
+ * || Queue || | |
+ * || || | |
+ * ||=================|| V |
+ * ||=================|| ^ |
+ * || || | |
+ * || Message || 800 KB |
+ * || Queue || | |
+ * || || | |
+ * ||=================|| V |
+ * ||=================|| ^ |
+ * || || | |
+ * || Debug || 800 KB |
+ * || Queue || | |
+ * || || | |
+ * ||=================|| V |
+ * || || |
+ * ||=================|| V
+ */
+
+/*
+ * Shared queues are used for communication between driver and firmware.
+ * There are 3 types of queues:
+ * Command queue - driver to write any command to firmware.
+ * Message queue - firmware to send any response to driver.
+ * Debug queue - firmware to write debug message.
+ */
+
+/* Host-firmware shared queue ids */
+enum iris_iface_queue {
+ IFACEQ_CMDQ_ID,
+ IFACEQ_MSGQ_ID,
+ IFACEQ_DBGQ_ID,
+ IFACEQ_NUMQ, /* not an index */
+};
+
+/**
+ * struct iris_hfi_queue_header
+ *
+ * @status: Queue status, bits (7:0), 0x1 - active, 0x0 - inactive
+ * @start_addr: Queue start address in non cached memory
+ * @queue_type: Queue ID
+ * @header_type: Default queue header
+ * @q_size: Queue size
+ * Number of queue packets if pkt_size is non-zero
+ * Queue size in bytes if pkt_size is zero
+ * @pkt_size: Size of queue packet entries
+ * 0x0: variable queue packet size
+ * non zero: size of queue packet entry, fixed
+ * @pkt_drop_cnt: Number of packets dropped by sender
+ * @rx_wm: Receiver watermark, applicable in event driven mode
+ * @tx_wm: Sender watermark, applicable in event driven mode
+ * @rx_req: Receiver sets this bit if queue is empty
+ * @tx_req: Sender sets this bit if queue is full
+ * @rx_irq_status: Receiver sets this bit and triggers an interrupt to
+ * the sender after packets are dequeued. Sender clears this bit
+ * @tx_irq_status: Sender sets this bit and triggers an interrupt to
+ * the receiver after packets are queued. Receiver clears this bit
+ * @read_idx: Index till where receiver has consumed the packets from the queue.
+ * @write_idx: Index till where sender has written the packets into the queue.
+ */
+struct iris_hfi_queue_header {
+ u32 status;
+ u32 start_addr;
+ u16 queue_type;
+ u16 header_type;
+ u32 q_size;
+ u32 pkt_size;
+ u32 pkt_drop_cnt;
+ u32 rx_wm;
+ u32 tx_wm;
+ u32 rx_req;
+ u32 tx_req;
+ u32 rx_irq_status;
+ u32 tx_irq_status;
+ u32 read_idx;
+ u32 write_idx;
+};
+
+/**
+ * struct iris_hfi_queue_table_header
+ *
+ * @version: Queue table version number
+ * @size: Queue table size from version to last parametr in qhdr entry
+ * @qhdr0_offset: Offset to the start of first qhdr
+ * @qhdr_size: Queue header size in bytes
+ * @num_q: Total number of queues in Queue table
+ * @num_active_q: Total number of active queues
+ * @device_addr: Device address of the queue
+ * @name: Queue name in characters
+ * @q_hdr: Array of queue headers
+ */
+struct iris_hfi_queue_table_header {
+ u32 version;
+ u32 size;
+ u32 qhdr0_offset;
+ u32 qhdr_size;
+ u32 num_q;
+ u32 num_active_q;
+ void *device_addr;
+ char name[256]; /* NUL-terminated array of characters */
+ struct iris_hfi_queue_header q_hdr[IFACEQ_NUMQ];
+};
+
+struct iris_iface_q_info {
+ struct iris_hfi_queue_header *qhdr;
+ dma_addr_t device_addr;
+ void *kernel_vaddr;
+};
+
+int iris_hfi_queues_init(struct iris_core *core);
+void iris_hfi_queues_deinit(struct iris_core *core);
+
+int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_size);
+int iris_hfi_queue_cmd_write(struct iris_core *core, void *pkt, u32 pkt_size);
+int iris_hfi_queue_msg_read(struct iris_core *core, void *pkt);
+int iris_hfi_queue_dbg_read(struct iris_core *core, void *pkt);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
new file mode 100644
index 000000000000..caa3c6507006
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_INSTANCE_H__
+#define __IRIS_INSTANCE_H__
+
+#include <media/v4l2-ctrls.h>
+
+#include "iris_buffer.h"
+#include "iris_core.h"
+#include "iris_utils.h"
+
+/**
+ * struct iris_inst - holds per video instance parameters
+ *
+ * @list: used for attach an instance to the core
+ * @core: pointer to core structure
+ * @session_id: id of current video session
+ * @ctx_q_lock: lock to serialize queues related ioctls
+ * @lock: lock to seralise forward and reverse threads
+ * @fh: reference of v4l2 file handler
+ * @fmt_src: structure of v4l2_format for source
+ * @fmt_dst: structure of v4l2_format for destination
+ * @ctrl_handler: reference of v4l2 ctrl handler
+ * @crop: structure of crop info
+ * @completion: structure of signal completions
+ * @flush_completion: structure of signal completions for flush cmd
+ * @fw_caps: array of supported instance firmware capabilities
+ * @buffers: array of different iris buffers
+ * @fw_min_count: minimnum count of buffers needed by fw
+ * @state: instance state
+ * @sub_state: instance sub state
+ * @once_per_session_set: boolean to set once per session property
+ * @max_input_data_size: max size of input data
+ * @power: structure of power info
+ * @icc_data: structure of interconnect data
+ * @m2m_dev: a reference to m2m device structure
+ * @m2m_ctx: a reference to m2m context structure
+ * @sequence_cap: a sequence counter for capture queue
+ * @sequence_out: a sequence counter for output queue
+ * @tss: timestamp metadata
+ * @metadata_idx: index for metadata buffer
+ */
+
+struct iris_inst {
+ struct list_head list;
+ struct iris_core *core;
+ u32 session_id;
+ struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */
+ struct mutex lock; /* lock to serialize forward and reverse threads */
+ struct v4l2_fh fh;
+ struct v4l2_format *fmt_src;
+ struct v4l2_format *fmt_dst;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct iris_hfi_rect_desc crop;
+ struct completion completion;
+ struct completion flush_completion;
+ struct platform_inst_fw_cap fw_caps[INST_FW_CAP_MAX];
+ struct iris_buffers buffers[BUF_TYPE_MAX];
+ u32 fw_min_count;
+ enum iris_inst_state state;
+ enum iris_inst_sub_state sub_state;
+ bool once_per_session_set;
+ size_t max_input_data_size;
+ struct iris_inst_power power;
+ struct icc_vote_data icc_data;
+ struct v4l2_m2m_dev *m2m_dev;
+ struct v4l2_m2m_ctx *m2m_ctx;
+ u32 sequence_cap;
+ u32 sequence_out;
+ struct iris_ts_metadata tss[VIDEO_MAX_FRAME];
+ u32 metadata_idx;
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
new file mode 100644
index 000000000000..ac76d9e1ef9c
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -0,0 +1,190 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_PLATFORM_COMMON_H__
+#define __IRIS_PLATFORM_COMMON_H__
+
+#include <linux/bits.h>
+
+struct iris_core;
+struct iris_inst;
+
+#define IRIS_PAS_ID 9
+#define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */
+#define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */
+
+#define REGISTER_BIT_DEPTH(luma, chroma) ((luma) << 16 | (chroma))
+#define BIT_DEPTH_8 REGISTER_BIT_DEPTH(8, 8)
+#define CODED_FRAMES_PROGRESSIVE 0x0
+#define DEFAULT_MAX_HOST_BUF_COUNT 64
+#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
+#define DEFAULT_FPS 30
+
+enum stage_type {
+ STAGE_1 = 1,
+ STAGE_2 = 2,
+};
+
+enum pipe_type {
+ PIPE_1 = 1,
+ PIPE_2 = 2,
+ PIPE_4 = 4,
+};
+
+extern struct iris_platform_data qcs8300_data;
+extern struct iris_platform_data sm8250_data;
+extern struct iris_platform_data sm8550_data;
+extern struct iris_platform_data sm8650_data;
+
+enum platform_clk_type {
+ IRIS_AXI_CLK,
+ IRIS_CTRL_CLK,
+ IRIS_HW_CLK,
+};
+
+struct platform_clk_data {
+ enum platform_clk_type clk_type;
+ const char *clk_name;
+};
+
+struct tz_cp_config {
+ u32 cp_start;
+ u32 cp_size;
+ u32 cp_nonpixel_start;
+ u32 cp_nonpixel_size;
+};
+
+struct ubwc_config_data {
+ u32 max_channels;
+ u32 mal_length;
+ u32 highest_bank_bit;
+ u32 bank_swzl_level;
+ u32 bank_swz2_level;
+ u32 bank_swz3_level;
+ u32 bank_spreading;
+};
+
+struct platform_inst_caps {
+ u32 min_frame_width;
+ u32 max_frame_width;
+ u32 min_frame_height;
+ u32 max_frame_height;
+ u32 max_mbpf;
+ u32 mb_cycles_vsp;
+ u32 mb_cycles_vpp;
+ u32 mb_cycles_fw;
+ u32 mb_cycles_fw_vpp;
+ u32 num_comv;
+};
+
+enum platform_inst_fw_cap_type {
+ PROFILE = 1,
+ LEVEL,
+ INPUT_BUF_HOST_MAX_COUNT,
+ STAGE,
+ PIPE,
+ POC,
+ CODED_FRAMES,
+ BIT_DEPTH,
+ RAP_FRAME,
+ DEBLOCK,
+ INST_FW_CAP_MAX,
+};
+
+enum platform_inst_fw_cap_flags {
+ CAP_FLAG_DYNAMIC_ALLOWED = BIT(0),
+ CAP_FLAG_MENU = BIT(1),
+ CAP_FLAG_INPUT_PORT = BIT(2),
+ CAP_FLAG_OUTPUT_PORT = BIT(3),
+ CAP_FLAG_CLIENT_SET = BIT(4),
+ CAP_FLAG_BITMASK = BIT(5),
+ CAP_FLAG_VOLATILE = BIT(6),
+};
+
+struct platform_inst_fw_cap {
+ enum platform_inst_fw_cap_type cap_id;
+ s64 min;
+ s64 max;
+ s64 step_or_mask;
+ s64 value;
+ u32 hfi_id;
+ enum platform_inst_fw_cap_flags flags;
+ int (*set)(struct iris_inst *inst,
+ enum platform_inst_fw_cap_type cap_id);
+};
+
+struct bw_info {
+ u32 mbs_per_sec;
+ u32 bw_ddr;
+};
+
+struct iris_core_power {
+ u64 clk_freq;
+ u64 icc_bw;
+};
+
+struct iris_inst_power {
+ u64 min_freq;
+ u32 icc_bw;
+};
+
+struct icc_vote_data {
+ u32 height, width;
+ u32 fps;
+};
+
+enum platform_pm_domain_type {
+ IRIS_CTRL_POWER_DOMAIN,
+ IRIS_HW_POWER_DOMAIN,
+};
+
+struct iris_platform_data {
+ void (*init_hfi_command_ops)(struct iris_core *core);
+ void (*init_hfi_response_ops)(struct iris_core *core);
+ struct iris_inst *(*get_instance)(void);
+ const struct vpu_ops *vpu_ops;
+ void (*set_preset_registers)(struct iris_core *core);
+ const struct icc_info *icc_tbl;
+ unsigned int icc_tbl_size;
+ const struct bw_info *bw_tbl_dec;
+ unsigned int bw_tbl_dec_size;
+ const char * const *pmdomain_tbl;
+ unsigned int pmdomain_tbl_size;
+ const char * const *opp_pd_tbl;
+ unsigned int opp_pd_tbl_size;
+ const struct platform_clk_data *clk_tbl;
+ unsigned int clk_tbl_size;
+ const char * const *clk_rst_tbl;
+ unsigned int clk_rst_tbl_size;
+ const char * const *controller_rst_tbl;
+ unsigned int controller_rst_tbl_size;
+ u64 dma_mask;
+ const char *fwname;
+ u32 pas_id;
+ struct platform_inst_caps *inst_caps;
+ struct platform_inst_fw_cap *inst_fw_caps;
+ u32 inst_fw_caps_size;
+ struct tz_cp_config *tz_cp_config_data;
+ u32 core_arch;
+ u32 hw_response_timeout;
+ struct ubwc_config_data *ubwc_config;
+ u32 num_vpp_pipe;
+ u32 max_session_count;
+ u32 max_core_mbpf;
+ const u32 *input_config_params;
+ unsigned int input_config_params_size;
+ const u32 *output_config_params;
+ unsigned int output_config_params_size;
+ const u32 *dec_input_prop;
+ unsigned int dec_input_prop_size;
+ const u32 *dec_output_prop;
+ unsigned int dec_output_prop_size;
+ const u32 *dec_ip_int_buf_tbl;
+ unsigned int dec_ip_int_buf_tbl_size;
+ const u32 *dec_op_int_buf_tbl;
+ unsigned int dec_op_int_buf_tbl_size;
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
new file mode 100644
index 000000000000..1e69ba15db0f
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
@@ -0,0 +1,385 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_defines.h"
+#include "iris_platform_common.h"
+#include "iris_vpu_common.h"
+
+#include "iris_platform_qcs8300.h"
+#include "iris_platform_sm8650.h"
+
+#define VIDEO_ARCH_LX 1
+
+static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
+ {
+ .cap_id = PROFILE,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH),
+ .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = INPUT_BUF_HOST_MAX_COUNT,
+ .min = DEFAULT_MAX_HOST_BUF_COUNT,
+ .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
+ .step_or_mask = 1,
+ .value = DEFAULT_MAX_HOST_BUF_COUNT,
+ .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROP_STAGE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = PIPE,
+ .min = PIPE_1,
+ .max = PIPE_4,
+ .step_or_mask = 1,
+ .value = PIPE_4,
+ .hfi_id = HFI_PROP_PIPE,
+ .set = iris_set_pipe,
+ },
+ {
+ .cap_id = POC,
+ .min = 0,
+ .max = 2,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE,
+ },
+ {
+ .cap_id = CODED_FRAMES,
+ .min = CODED_FRAMES_PROGRESSIVE,
+ .max = CODED_FRAMES_PROGRESSIVE,
+ .step_or_mask = 0,
+ .value = CODED_FRAMES_PROGRESSIVE,
+ .hfi_id = HFI_PROP_CODED_FRAMES,
+ },
+ {
+ .cap_id = BIT_DEPTH,
+ .min = BIT_DEPTH_8,
+ .max = BIT_DEPTH_8,
+ .step_or_mask = 1,
+ .value = BIT_DEPTH_8,
+ .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ },
+ {
+ .cap_id = RAP_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+};
+
+static struct platform_inst_caps platform_inst_cap_sm8550 = {
+ .min_frame_width = 96,
+ .max_frame_width = 8192,
+ .min_frame_height = 96,
+ .max_frame_height = 8192,
+ .max_mbpf = (8192 * 4352) / 256,
+ .mb_cycles_vpp = 200,
+ .mb_cycles_fw = 489583,
+ .mb_cycles_fw_vpp = 66234,
+ .num_comv = 0,
+};
+
+static void iris_set_sm8550_preset_registers(struct iris_core *core)
+{
+ writel(0x0, core->reg_base + 0xB0088);
+}
+
+static const struct icc_info sm8550_icc_table[] = {
+ { "cpu-cfg", 1000, 1000 },
+ { "video-mem", 1000, 15000000 },
+};
+
+static const char * const sm8550_clk_reset_table[] = { "bus" };
+
+static const struct bw_info sm8550_bw_table_dec[] = {
+ { ((4096 * 2160) / 256) * 60, 1608000 },
+ { ((4096 * 2160) / 256) * 30, 826000 },
+ { ((1920 * 1080) / 256) * 60, 567000 },
+ { ((1920 * 1080) / 256) * 30, 294000 },
+};
+
+static const char * const sm8550_pmdomain_table[] = { "venus", "vcodec0" };
+
+static const char * const sm8550_opp_pd_table[] = { "mxc", "mmcx" };
+
+static const struct platform_clk_data sm8550_clk_table[] = {
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_HW_CLK, "vcodec0_core" },
+};
+
+static struct ubwc_config_data ubwc_config_sm8550 = {
+ .max_channels = 8,
+ .mal_length = 32,
+ .highest_bank_bit = 16,
+ .bank_swzl_level = 0,
+ .bank_swz2_level = 1,
+ .bank_swz3_level = 1,
+ .bank_spreading = 1,
+};
+
+static struct tz_cp_config tz_cp_config_sm8550 = {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+};
+
+static const u32 sm8550_vdec_input_config_params[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_CODED_FRAMES,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_PROP_PIC_ORDER_CNT_TYPE,
+ HFI_PROP_PROFILE,
+ HFI_PROP_LEVEL,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+};
+
+static const u32 sm8550_vdec_output_config_params[] = {
+ HFI_PROP_COLOR_FORMAT,
+ HFI_PROP_LINEAR_STRIDE_SCANLINE,
+};
+
+static const u32 sm8550_vdec_subscribe_input_properties[] = {
+ HFI_PROP_NO_OUTPUT,
+};
+
+static const u32 sm8550_vdec_subscribe_output_properties[] = {
+ HFI_PROP_PICTURE_TYPE,
+ HFI_PROP_CABAC_SESSION,
+};
+
+static const u32 sm8550_dec_ip_int_buf_tbl[] = {
+ BUF_BIN,
+ BUF_COMV,
+ BUF_NON_COMV,
+ BUF_LINE,
+};
+
+static const u32 sm8550_dec_op_int_buf_tbl[] = {
+ BUF_DPB,
+};
+
+struct iris_platform_data sm8550_data = {
+ .get_instance = iris_hfi_gen2_get_instance,
+ .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+ .vpu_ops = &iris_vpu3_ops,
+ .set_preset_registers = iris_set_sm8550_preset_registers,
+ .icc_tbl = sm8550_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
+ .clk_rst_tbl = sm8550_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
+ .bw_tbl_dec = sm8550_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
+ .pmdomain_tbl = sm8550_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
+ .opp_pd_tbl = sm8550_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu/vpu30_p4.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_sm8550,
+ .inst_fw_caps = inst_fw_cap_sm8550,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8550),
+ .tz_cp_config_data = &tz_cp_config_sm8550,
+ .core_arch = VIDEO_ARCH_LX,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .ubwc_config = &ubwc_config_sm8550,
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = ((8192 * 4352) / 256) * 2,
+ .input_config_params =
+ sm8550_vdec_input_config_params,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .output_config_params =
+ sm8550_vdec_output_config_params,
+ .output_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_output_config_params),
+ .dec_input_prop = sm8550_vdec_subscribe_input_properties,
+ .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties),
+ .dec_output_prop = sm8550_vdec_subscribe_output_properties,
+ .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties),
+
+ .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+};
+
+/*
+ * Shares most of SM8550 data except:
+ * - vpu_ops to iris_vpu33_ops
+ * - clk_rst_tbl to sm8650_clk_reset_table
+ * - controller_rst_tbl to sm8650_controller_reset_table
+ * - fwname to "qcom/vpu/vpu33_p4.mbn"
+ */
+struct iris_platform_data sm8650_data = {
+ .get_instance = iris_hfi_gen2_get_instance,
+ .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+ .vpu_ops = &iris_vpu33_ops,
+ .set_preset_registers = iris_set_sm8550_preset_registers,
+ .icc_tbl = sm8550_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
+ .clk_rst_tbl = sm8650_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table),
+ .controller_rst_tbl = sm8650_controller_reset_table,
+ .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table),
+ .bw_tbl_dec = sm8550_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
+ .pmdomain_tbl = sm8550_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
+ .opp_pd_tbl = sm8550_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu/vpu33_p4.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_sm8550,
+ .inst_fw_caps = inst_fw_cap_sm8550,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8550),
+ .tz_cp_config_data = &tz_cp_config_sm8550,
+ .core_arch = VIDEO_ARCH_LX,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .ubwc_config = &ubwc_config_sm8550,
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = ((8192 * 4352) / 256) * 2,
+ .input_config_params =
+ sm8550_vdec_input_config_params,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .output_config_params =
+ sm8550_vdec_output_config_params,
+ .output_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_output_config_params),
+ .dec_input_prop = sm8550_vdec_subscribe_input_properties,
+ .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties),
+ .dec_output_prop = sm8550_vdec_subscribe_output_properties,
+ .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties),
+
+ .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+};
+
+/*
+ * Shares most of SM8550 data except:
+ * - inst_caps to platform_inst_cap_qcs8300
+ * - inst_fw_caps to inst_fw_cap_qcs8300
+ */
+struct iris_platform_data qcs8300_data = {
+ .get_instance = iris_hfi_gen2_get_instance,
+ .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
+ .vpu_ops = &iris_vpu3_ops,
+ .set_preset_registers = iris_set_sm8550_preset_registers,
+ .icc_tbl = sm8550_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
+ .clk_rst_tbl = sm8550_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
+ .bw_tbl_dec = sm8550_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
+ .pmdomain_tbl = sm8550_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
+ .opp_pd_tbl = sm8550_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu/vpu30_p4_s6.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_qcs8300,
+ .inst_fw_caps = inst_fw_cap_qcs8300,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_qcs8300),
+ .tz_cp_config_data = &tz_cp_config_sm8550,
+ .core_arch = VIDEO_ARCH_LX,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .ubwc_config = &ubwc_config_sm8550,
+ .num_vpp_pipe = 2,
+ .max_session_count = 16,
+ .max_core_mbpf = ((4096 * 2176) / 256) * 4,
+ .input_config_params =
+ sm8550_vdec_input_config_params,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params),
+ .output_config_params =
+ sm8550_vdec_output_config_params,
+ .output_config_params_size =
+ ARRAY_SIZE(sm8550_vdec_output_config_params),
+ .dec_input_prop = sm8550_vdec_subscribe_input_properties,
+ .dec_input_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_input_properties),
+ .dec_output_prop = sm8550_vdec_subscribe_output_properties,
+ .dec_output_prop_size = ARRAY_SIZE(sm8550_vdec_subscribe_output_properties),
+
+ .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
new file mode 100644
index 000000000000..f82355d72fcf
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+static struct platform_inst_fw_cap inst_fw_cap_qcs8300[] = {
+ {
+ .cap_id = PROFILE,
+ .min = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+ .max = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_H264_PROFILE_HIGH),
+ .value = V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL,
+ .min = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1B) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_1_3) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_2_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_3_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_4_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_0) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_H264_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_H264_LEVEL_6_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = INPUT_BUF_HOST_MAX_COUNT,
+ .min = DEFAULT_MAX_HOST_BUF_COUNT,
+ .max = DEFAULT_MAX_HOST_BURST_BUF_COUNT,
+ .step_or_mask = 1,
+ .value = DEFAULT_MAX_HOST_BUF_COUNT,
+ .hfi_id = HFI_PROP_BUFFER_HOST_MAX_COUNT,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROP_STAGE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = PIPE,
+ .min = PIPE_1,
+ .max = PIPE_2,
+ .step_or_mask = 1,
+ .value = PIPE_2,
+ .hfi_id = HFI_PROP_PIPE,
+ .set = iris_set_pipe,
+ },
+ {
+ .cap_id = POC,
+ .min = 0,
+ .max = 2,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_PIC_ORDER_CNT_TYPE,
+ },
+ {
+ .cap_id = CODED_FRAMES,
+ .min = CODED_FRAMES_PROGRESSIVE,
+ .max = CODED_FRAMES_PROGRESSIVE,
+ .step_or_mask = 0,
+ .value = CODED_FRAMES_PROGRESSIVE,
+ .hfi_id = HFI_PROP_CODED_FRAMES,
+ },
+ {
+ .cap_id = BIT_DEPTH,
+ .min = BIT_DEPTH_8,
+ .max = BIT_DEPTH_8,
+ .step_or_mask = 1,
+ .value = BIT_DEPTH_8,
+ .hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ },
+ {
+ .cap_id = RAP_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 1,
+ .hfi_id = HFI_PROP_DEC_START_FROM_RAP_FRAME,
+ .flags = CAP_FLAG_INPUT_PORT,
+ .set = iris_set_u32,
+ },
+};
+
+static struct platform_inst_caps platform_inst_cap_qcs8300 = {
+ .min_frame_width = 96,
+ .max_frame_width = 4096,
+ .min_frame_height = 96,
+ .max_frame_height = 4096,
+ .max_mbpf = (4096 * 2176) / 256,
+ .mb_cycles_vpp = 200,
+ .mb_cycles_fw = 326389,
+ .mb_cycles_fw_vpp = 44156,
+ .num_comv = 0,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
new file mode 100644
index 000000000000..5c86fd7b7b6f
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_platform_common.h"
+#include "iris_resources.h"
+#include "iris_hfi_gen1.h"
+#include "iris_hfi_gen1_defines.h"
+#include "iris_vpu_common.h"
+
+static struct platform_inst_fw_cap inst_fw_cap_sm8250[] = {
+ {
+ .cap_id = PIPE,
+ .min = PIPE_1,
+ .max = PIPE_4,
+ .step_or_mask = 1,
+ .value = PIPE_4,
+ .hfi_id = HFI_PROPERTY_PARAM_WORK_ROUTE,
+ .set = iris_set_pipe,
+ },
+ {
+ .cap_id = STAGE,
+ .min = STAGE_1,
+ .max = STAGE_2,
+ .step_or_mask = 1,
+ .value = STAGE_2,
+ .hfi_id = HFI_PROPERTY_PARAM_WORK_MODE,
+ .set = iris_set_stage,
+ },
+ {
+ .cap_id = DEBLOCK,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER,
+ .set = iris_set_u32,
+ },
+};
+
+static struct platform_inst_caps platform_inst_cap_sm8250 = {
+ .min_frame_width = 128,
+ .max_frame_width = 8192,
+ .min_frame_height = 128,
+ .max_frame_height = 8192,
+ .max_mbpf = 138240,
+ .mb_cycles_vsp = 25,
+ .mb_cycles_vpp = 200,
+};
+
+static void iris_set_sm8250_preset_registers(struct iris_core *core)
+{
+ writel(0x0, core->reg_base + 0xB0088);
+}
+
+static const struct icc_info sm8250_icc_table[] = {
+ { "cpu-cfg", 1000, 1000 },
+ { "video-mem", 1000, 15000000 },
+};
+
+static const char * const sm8250_clk_reset_table[] = { "bus", "core" };
+
+static const struct bw_info sm8250_bw_table_dec[] = {
+ { ((4096 * 2160) / 256) * 60, 2403000 },
+ { ((4096 * 2160) / 256) * 30, 1224000 },
+ { ((1920 * 1080) / 256) * 60, 812000 },
+ { ((1920 * 1080) / 256) * 30, 416000 },
+};
+
+static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" };
+
+static const char * const sm8250_opp_pd_table[] = { "mx" };
+
+static const struct platform_clk_data sm8250_clk_table[] = {
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_HW_CLK, "vcodec0_core" },
+};
+
+static struct tz_cp_config tz_cp_config_sm8250 = {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+};
+
+static const u32 sm8250_vdec_input_config_param_default[] = {
+ HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE,
+ HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+ HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+ HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL,
+ HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM,
+ HFI_PROPERTY_PARAM_FRAME_SIZE,
+ HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL,
+ HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE,
+};
+
+static const u32 sm8250_dec_ip_int_buf_tbl[] = {
+ BUF_BIN,
+ BUF_SCRATCH_1,
+};
+
+static const u32 sm8250_dec_op_int_buf_tbl[] = {
+ BUF_DPB,
+};
+
+struct iris_platform_data sm8250_data = {
+ .get_instance = iris_hfi_gen1_get_instance,
+ .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init,
+ .init_hfi_response_ops = iris_hfi_gen1_response_ops_init,
+ .vpu_ops = &iris_vpu2_ops,
+ .set_preset_registers = iris_set_sm8250_preset_registers,
+ .icc_tbl = sm8250_icc_table,
+ .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table),
+ .clk_rst_tbl = sm8250_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8250_clk_reset_table),
+ .bw_tbl_dec = sm8250_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec),
+ .pmdomain_tbl = sm8250_pmdomain_table,
+ .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table),
+ .opp_pd_tbl = sm8250_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table),
+ .clk_tbl = sm8250_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table),
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/vpu-1.0/venus.mbn",
+ .pas_id = IRIS_PAS_ID,
+ .inst_caps = &platform_inst_cap_sm8250,
+ .inst_fw_caps = inst_fw_cap_sm8250,
+ .inst_fw_caps_size = ARRAY_SIZE(inst_fw_cap_sm8250),
+ .tz_cp_config_data = &tz_cp_config_sm8250,
+ .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = (8192 * 4352) / 256,
+ .input_config_params =
+ sm8250_vdec_input_config_param_default,
+ .input_config_params_size =
+ ARRAY_SIZE(sm8250_vdec_input_config_param_default),
+
+ .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl,
+ .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl),
+ .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl,
+ .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl),
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8650.h b/drivers/media/platform/qcom/iris/iris_platform_sm8650.h
new file mode 100644
index 000000000000..75e9d572e788
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8650.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_PLATFORM_SM8650_H__
+#define __IRIS_PLATFORM_SM8650_H__
+
+static const char * const sm8650_clk_reset_table[] = { "bus", "core" };
+
+static const char * const sm8650_controller_reset_table[] = { "xo" };
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_power.c b/drivers/media/platform/qcom/iris/iris_power.c
new file mode 100644
index 000000000000..dbca42df0910
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_power.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_buffer.h"
+#include "iris_instance.h"
+#include "iris_power.h"
+#include "iris_resources.h"
+#include "iris_vpu_common.h"
+
+static u32 iris_calc_bw(struct iris_inst *inst, struct icc_vote_data *data)
+{
+ const struct bw_info *bw_tbl = NULL;
+ struct iris_core *core = inst->core;
+ u32 num_rows, i, mbs, mbps;
+ u32 icc_bw = 0;
+
+ mbs = DIV_ROUND_UP(data->height, 16) * DIV_ROUND_UP(data->width, 16);
+ mbps = mbs * data->fps;
+ if (mbps == 0)
+ goto exit;
+
+ bw_tbl = core->iris_platform_data->bw_tbl_dec;
+ num_rows = core->iris_platform_data->bw_tbl_dec_size;
+
+ for (i = 0; i < num_rows; i++) {
+ if (i != 0 && mbps > bw_tbl[i].mbs_per_sec)
+ break;
+
+ icc_bw = bw_tbl[i].bw_ddr;
+ }
+
+exit:
+ return icc_bw;
+}
+
+static int iris_set_interconnects(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance;
+ u64 total_bw_ddr = 0;
+ int ret;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list) {
+ if (!instance->max_input_data_size)
+ continue;
+
+ total_bw_ddr += instance->power.icc_bw;
+ }
+
+ ret = iris_set_icc_bw(core, total_bw_ddr);
+
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int iris_vote_interconnects(struct iris_inst *inst)
+{
+ struct icc_vote_data *vote_data = &inst->icc_data;
+ struct v4l2_format *inp_f = inst->fmt_src;
+
+ vote_data->width = inp_f->fmt.pix_mp.width;
+ vote_data->height = inp_f->fmt.pix_mp.height;
+ vote_data->fps = DEFAULT_FPS;
+
+ inst->power.icc_bw = iris_calc_bw(inst, vote_data);
+
+ return iris_set_interconnects(inst);
+}
+
+static int iris_set_clocks(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance;
+ u64 freq = 0;
+ int ret;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list) {
+ if (!instance->max_input_data_size)
+ continue;
+
+ freq += instance->power.min_freq;
+ }
+
+ core->power.clk_freq = freq;
+ ret = dev_pm_opp_set_rate(core->dev, freq);
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int iris_scale_clocks(struct iris_inst *inst)
+{
+ const struct vpu_ops *vpu_ops = inst->core->iris_platform_data->vpu_ops;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+ size_t data_size = 0;
+
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ data_size = max(data_size, buf->data_size);
+ }
+
+ inst->max_input_data_size = data_size;
+ if (!inst->max_input_data_size)
+ return 0;
+
+ inst->power.min_freq = vpu_ops->calc_freq(inst, inst->max_input_data_size);
+
+ return iris_set_clocks(inst);
+}
+
+int iris_scale_power(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ int ret;
+
+ if (pm_runtime_suspended(core->dev)) {
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ return ret;
+
+ pm_runtime_put_autosuspend(core->dev);
+ }
+
+ ret = iris_scale_clocks(inst);
+ if (ret)
+ return ret;
+
+ return iris_vote_interconnects(inst);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_power.h b/drivers/media/platform/qcom/iris/iris_power.h
new file mode 100644
index 000000000000..55212660e72d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_power.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_POWER_H__
+#define __IRIS_POWER_H__
+
+struct iris_inst;
+
+int iris_scale_power(struct iris_inst *inst);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
new file mode 100644
index 000000000000..9a7ce142f700
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -0,0 +1,372 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/interconnect.h>
+#include <linux/module.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_vidc.h"
+
+static int iris_init_icc(struct iris_core *core)
+{
+ const struct icc_info *icc_tbl;
+ u32 i = 0;
+
+ icc_tbl = core->iris_platform_data->icc_tbl;
+
+ core->icc_count = core->iris_platform_data->icc_tbl_size;
+ core->icc_tbl = devm_kzalloc(core->dev,
+ sizeof(struct icc_bulk_data) * core->icc_count,
+ GFP_KERNEL);
+ if (!core->icc_tbl)
+ return -ENOMEM;
+
+ for (i = 0; i < core->icc_count; i++) {
+ core->icc_tbl[i].name = icc_tbl[i].name;
+ core->icc_tbl[i].avg_bw = icc_tbl[i].bw_min_kbps;
+ core->icc_tbl[i].peak_bw = 0;
+ }
+
+ return devm_of_icc_bulk_get(core->dev, core->icc_count, core->icc_tbl);
+}
+
+static int iris_init_power_domains(struct iris_core *core)
+{
+ const struct platform_clk_data *clk_tbl;
+ u32 clk_cnt, i;
+ int ret;
+
+ struct dev_pm_domain_attach_data iris_pd_data = {
+ .pd_names = core->iris_platform_data->pmdomain_tbl,
+ .num_pd_names = core->iris_platform_data->pmdomain_tbl_size,
+ .pd_flags = PD_FLAG_NO_DEV_LINK,
+ };
+
+ struct dev_pm_domain_attach_data iris_opp_pd_data = {
+ .pd_names = core->iris_platform_data->opp_pd_tbl,
+ .num_pd_names = core->iris_platform_data->opp_pd_tbl_size,
+ .pd_flags = PD_FLAG_DEV_LINK_ON,
+ };
+
+ ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl);
+ if (ret < 0)
+ return ret;
+
+ clk_tbl = core->iris_platform_data->clk_tbl;
+ clk_cnt = core->iris_platform_data->clk_tbl_size;
+
+ for (i = 0; i < clk_cnt; i++) {
+ if (clk_tbl[i].clk_type == IRIS_HW_CLK) {
+ ret = devm_pm_opp_set_clkname(core->dev, clk_tbl[i].clk_name);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return devm_pm_opp_of_add_table(core->dev);
+}
+
+static int iris_init_clocks(struct iris_core *core)
+{
+ int ret;
+
+ ret = devm_clk_bulk_get_all(core->dev, &core->clock_tbl);
+ if (ret < 0)
+ return ret;
+
+ core->clk_count = ret;
+
+ return 0;
+}
+
+static int iris_init_reset_table(struct iris_core *core,
+ struct reset_control_bulk_data **resets,
+ const char * const *rst_tbl, u32 rst_tbl_size)
+{
+ u32 i = 0;
+
+ *resets = devm_kzalloc(core->dev,
+ sizeof(struct reset_control_bulk_data) * rst_tbl_size,
+ GFP_KERNEL);
+ if (!*resets)
+ return -ENOMEM;
+
+ for (i = 0; i < rst_tbl_size; i++)
+ (*resets)[i].id = rst_tbl[i];
+
+ return devm_reset_control_bulk_get_exclusive(core->dev, rst_tbl_size, *resets);
+}
+
+static int iris_init_resets(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_init_reset_table(core, &core->resets,
+ core->iris_platform_data->clk_rst_tbl,
+ core->iris_platform_data->clk_rst_tbl_size);
+ if (ret)
+ return ret;
+
+ if (!core->iris_platform_data->controller_rst_tbl_size)
+ return 0;
+
+ return iris_init_reset_table(core, &core->controller_resets,
+ core->iris_platform_data->controller_rst_tbl,
+ core->iris_platform_data->controller_rst_tbl_size);
+}
+
+static int iris_init_resources(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_init_icc(core);
+ if (ret)
+ return ret;
+
+ ret = iris_init_power_domains(core);
+ if (ret)
+ return ret;
+
+ ret = iris_init_clocks(core);
+ if (ret)
+ return ret;
+
+ return iris_init_resets(core);
+}
+
+static int iris_register_video_device(struct iris_core *core)
+{
+ struct video_device *vdev;
+ int ret;
+
+ vdev = video_device_alloc();
+ if (!vdev)
+ return -ENOMEM;
+
+ strscpy(vdev->name, "qcom-iris-decoder", sizeof(vdev->name));
+ vdev->release = video_device_release;
+ vdev->fops = core->iris_v4l2_file_ops;
+ vdev->ioctl_ops = core->iris_v4l2_ioctl_ops;
+ vdev->vfl_dir = VFL_DIR_M2M;
+ vdev->v4l2_dev = &core->v4l2_dev;
+ vdev->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ goto err_vdev_release;
+
+ core->vdev_dec = vdev;
+ video_set_drvdata(vdev, core);
+
+ return 0;
+
+err_vdev_release:
+ video_device_release(vdev);
+
+ return ret;
+}
+
+static void iris_remove(struct platform_device *pdev)
+{
+ struct iris_core *core;
+
+ core = platform_get_drvdata(pdev);
+ if (!core)
+ return;
+
+ iris_core_deinit(core);
+
+ video_unregister_device(core->vdev_dec);
+
+ v4l2_device_unregister(&core->v4l2_dev);
+
+ mutex_destroy(&core->lock);
+}
+
+static void iris_sys_error_handler(struct work_struct *work)
+{
+ struct iris_core *core =
+ container_of(work, struct iris_core, sys_error_handler.work);
+
+ iris_core_deinit(core);
+ iris_core_init(core);
+}
+
+static int iris_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iris_core *core;
+ u64 dma_mask;
+ int ret;
+
+ core = devm_kzalloc(&pdev->dev, sizeof(*core), GFP_KERNEL);
+ if (!core)
+ return -ENOMEM;
+ core->dev = dev;
+
+ core->state = IRIS_CORE_DEINIT;
+ mutex_init(&core->lock);
+ init_completion(&core->core_init_done);
+
+ core->response_packet = devm_kzalloc(core->dev, IFACEQ_CORE_PKT_SIZE, GFP_KERNEL);
+ if (!core->response_packet)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&core->instances);
+ INIT_DELAYED_WORK(&core->sys_error_handler, iris_sys_error_handler);
+
+ core->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(core->reg_base))
+ return PTR_ERR(core->reg_base);
+
+ core->irq = platform_get_irq(pdev, 0);
+ if (core->irq < 0)
+ return core->irq;
+
+ core->iris_platform_data = of_device_get_match_data(core->dev);
+
+ ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr,
+ iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core);
+ if (ret)
+ return ret;
+
+ disable_irq_nosync(core->irq);
+
+ iris_init_ops(core);
+ core->iris_platform_data->init_hfi_command_ops(core);
+ core->iris_platform_data->init_hfi_response_ops(core);
+
+ ret = iris_init_resources(core);
+ if (ret)
+ return ret;
+
+ iris_session_init_caps(core);
+
+ ret = v4l2_device_register(dev, &core->v4l2_dev);
+ if (ret)
+ return ret;
+
+ ret = iris_register_video_device(core);
+ if (ret)
+ goto err_v4l2_unreg;
+
+ platform_set_drvdata(pdev, core);
+
+ dma_mask = core->iris_platform_data->dma_mask;
+
+ ret = dma_set_mask_and_coherent(dev, dma_mask);
+ if (ret)
+ goto err_vdev_unreg;
+
+ dma_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
+ dma_set_seg_boundary(&pdev->dev, DMA_BIT_MASK(32));
+
+ pm_runtime_set_autosuspend_delay(core->dev, AUTOSUSPEND_DELAY_VALUE);
+ pm_runtime_use_autosuspend(core->dev);
+ ret = devm_pm_runtime_enable(core->dev);
+ if (ret)
+ goto err_vdev_unreg;
+
+ return 0;
+
+err_vdev_unreg:
+ video_unregister_device(core->vdev_dec);
+err_v4l2_unreg:
+ v4l2_device_unregister(&core->v4l2_dev);
+
+ return ret;
+}
+
+static int __maybe_unused iris_pm_suspend(struct device *dev)
+{
+ struct iris_core *core;
+ int ret = 0;
+
+ core = dev_get_drvdata(dev);
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT)
+ goto exit;
+
+ ret = iris_hfi_pm_suspend(core);
+
+exit:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static int __maybe_unused iris_pm_resume(struct device *dev)
+{
+ struct iris_core *core;
+ int ret = 0;
+
+ core = dev_get_drvdata(dev);
+
+ mutex_lock(&core->lock);
+ if (core->state != IRIS_CORE_INIT)
+ goto exit;
+
+ ret = iris_hfi_pm_resume(core);
+ pm_runtime_mark_last_busy(core->dev);
+
+exit:
+ mutex_unlock(&core->lock);
+
+ return ret;
+}
+
+static const struct dev_pm_ops iris_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(iris_pm_suspend, iris_pm_resume, NULL)
+};
+
+static const struct of_device_id iris_dt_match[] = {
+ {
+ .compatible = "qcom,qcs8300-iris",
+ .data = &qcs8300_data,
+ },
+#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS))
+ {
+ .compatible = "qcom,sm8250-venus",
+ .data = &sm8250_data,
+ },
+#endif
+ {
+ .compatible = "qcom,sm8550-iris",
+ .data = &sm8550_data,
+ },
+ {
+ .compatible = "qcom,sm8650-iris",
+ .data = &sm8650_data,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, iris_dt_match);
+
+static struct platform_driver qcom_iris_driver = {
+ .probe = iris_probe,
+ .remove = iris_remove,
+ .driver = {
+ .name = "qcom-iris",
+ .of_match_table = iris_dt_match,
+ .pm = &iris_pm_ops,
+ },
+};
+
+module_platform_driver(qcom_iris_driver);
+MODULE_DESCRIPTION("Qualcomm iris video driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/qcom/iris/iris_resources.c b/drivers/media/platform/qcom/iris/iris_resources.c
new file mode 100644
index 000000000000..cf32f268b703
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_resources.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/interconnect.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_opp.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "iris_core.h"
+#include "iris_resources.h"
+
+#define BW_THRESHOLD 50000
+
+int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw)
+{
+ unsigned long bw_kbps = 0, bw_prev = 0;
+ const struct icc_info *icc_tbl;
+ int ret = 0, i;
+
+ icc_tbl = core->iris_platform_data->icc_tbl;
+
+ for (i = 0; i < core->icc_count; i++) {
+ if (!strcmp(core->icc_tbl[i].name, "video-mem")) {
+ bw_kbps = icc_bw;
+ bw_prev = core->power.icc_bw;
+
+ bw_kbps = clamp_t(typeof(bw_kbps), bw_kbps,
+ icc_tbl[i].bw_min_kbps, icc_tbl[i].bw_max_kbps);
+
+ if (abs(bw_kbps - bw_prev) < BW_THRESHOLD && bw_prev)
+ return ret;
+
+ core->icc_tbl[i].avg_bw = bw_kbps;
+
+ core->power.icc_bw = bw_kbps;
+ break;
+ }
+ }
+
+ return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
+}
+
+int iris_unset_icc_bw(struct iris_core *core)
+{
+ u32 i;
+
+ core->power.icc_bw = 0;
+
+ for (i = 0; i < core->icc_count; i++) {
+ core->icc_tbl[i].avg_bw = 0;
+ core->icc_tbl[i].peak_bw = 0;
+ }
+
+ return icc_bulk_set_bw(core->icc_count, core->icc_tbl);
+}
+
+int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev)
+{
+ int ret;
+
+ ret = dev_pm_opp_set_rate(core->dev, ULONG_MAX);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(pd_dev);
+ if (ret < 0)
+ return ret;
+
+ return ret;
+}
+
+int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev)
+{
+ int ret;
+
+ ret = dev_pm_opp_set_rate(core->dev, 0);
+ if (ret)
+ return ret;
+
+ pm_runtime_put_sync(pd_dev);
+
+ return 0;
+}
+
+static struct clk *iris_get_clk_by_type(struct iris_core *core, enum platform_clk_type clk_type)
+{
+ const struct platform_clk_data *clk_tbl;
+ u32 clk_cnt, i, j;
+
+ clk_tbl = core->iris_platform_data->clk_tbl;
+ clk_cnt = core->iris_platform_data->clk_tbl_size;
+
+ for (i = 0; i < clk_cnt; i++) {
+ if (clk_tbl[i].clk_type == clk_type) {
+ for (j = 0; core->clock_tbl && j < core->clk_count; j++) {
+ if (!strcmp(core->clock_tbl[j].id, clk_tbl[i].clk_name))
+ return core->clock_tbl[j].clk;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type)
+{
+ struct clk *clock;
+
+ clock = iris_get_clk_by_type(core, clk_type);
+ if (!clock)
+ return -EINVAL;
+
+ return clk_prepare_enable(clock);
+}
+
+int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type)
+{
+ struct clk *clock;
+
+ clock = iris_get_clk_by_type(core, clk_type);
+ if (!clock)
+ return -EINVAL;
+
+ clk_disable_unprepare(clock);
+
+ return 0;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_resources.h b/drivers/media/platform/qcom/iris/iris_resources.h
new file mode 100644
index 000000000000..f723dfe5bd81
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_resources.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_RESOURCES_H__
+#define __IRIS_RESOURCES_H__
+
+struct iris_core;
+
+int iris_enable_power_domains(struct iris_core *core, struct device *pd_dev);
+int iris_disable_power_domains(struct iris_core *core, struct device *pd_dev);
+int iris_unset_icc_bw(struct iris_core *core);
+int iris_set_icc_bw(struct iris_core *core, unsigned long icc_bw);
+int iris_disable_unprepare_clock(struct iris_core *core, enum platform_clk_type clk_type);
+int iris_prepare_enable_clock(struct iris_core *core, enum platform_clk_type clk_type);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c
new file mode 100644
index 000000000000..5976e926c83d
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_state.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_instance.h"
+
+static bool iris_allow_inst_state_change(struct iris_inst *inst,
+ enum iris_inst_state req_state)
+{
+ switch (inst->state) {
+ case IRIS_INST_INIT:
+ if (req_state == IRIS_INST_INPUT_STREAMING ||
+ req_state == IRIS_INST_OUTPUT_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_INPUT_STREAMING:
+ if (req_state == IRIS_INST_INIT ||
+ req_state == IRIS_INST_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_OUTPUT_STREAMING:
+ if (req_state == IRIS_INST_INIT ||
+ req_state == IRIS_INST_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_STREAMING:
+ if (req_state == IRIS_INST_INPUT_STREAMING ||
+ req_state == IRIS_INST_OUTPUT_STREAMING ||
+ req_state == IRIS_INST_DEINIT)
+ return true;
+ return false;
+ case IRIS_INST_DEINIT:
+ if (req_state == IRIS_INST_INIT)
+ return true;
+ return false;
+ default:
+ return false;
+ }
+}
+
+int iris_inst_change_state(struct iris_inst *inst,
+ enum iris_inst_state request_state)
+{
+ if (inst->state == IRIS_INST_ERROR)
+ return 0;
+
+ if (inst->state == request_state)
+ return 0;
+
+ if (request_state == IRIS_INST_ERROR)
+ goto change_state;
+
+ if (!iris_allow_inst_state_change(inst, request_state))
+ return -EINVAL;
+
+change_state:
+ inst->state = request_state;
+ dev_dbg(inst->core->dev, "state changed from %x to %x\n",
+ inst->state, request_state);
+
+ return 0;
+}
+
+int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane)
+{
+ enum iris_inst_state new_state = IRIS_INST_ERROR;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ if (inst->state == IRIS_INST_INIT)
+ new_state = IRIS_INST_INPUT_STREAMING;
+ else if (inst->state == IRIS_INST_OUTPUT_STREAMING)
+ new_state = IRIS_INST_STREAMING;
+ } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+ if (inst->state == IRIS_INST_INIT)
+ new_state = IRIS_INST_OUTPUT_STREAMING;
+ else if (inst->state == IRIS_INST_INPUT_STREAMING)
+ new_state = IRIS_INST_STREAMING;
+ }
+
+ return iris_inst_change_state(inst, new_state);
+}
+
+int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane)
+{
+ enum iris_inst_state new_state = IRIS_INST_ERROR;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ if (inst->state == IRIS_INST_INPUT_STREAMING)
+ new_state = IRIS_INST_INIT;
+ else if (inst->state == IRIS_INST_STREAMING)
+ new_state = IRIS_INST_OUTPUT_STREAMING;
+ } else if (V4L2_TYPE_IS_CAPTURE(plane)) {
+ if (inst->state == IRIS_INST_OUTPUT_STREAMING)
+ new_state = IRIS_INST_INIT;
+ else if (inst->state == IRIS_INST_STREAMING)
+ new_state = IRIS_INST_INPUT_STREAMING;
+ }
+
+ return iris_inst_change_state(inst, new_state);
+}
+
+static bool iris_inst_allow_sub_state(struct iris_inst *inst, enum iris_inst_sub_state sub_state)
+{
+ if (!sub_state)
+ return true;
+
+ switch (inst->state) {
+ case IRIS_INST_INIT:
+ if (sub_state & IRIS_INST_SUB_LOAD_RESOURCES)
+ return true;
+ return false;
+ case IRIS_INST_INPUT_STREAMING:
+ if (sub_state & (IRIS_INST_SUB_FIRST_IPSC | IRIS_INST_SUB_DRC |
+ IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_INPUT_PAUSE))
+ return true;
+ return false;
+ case IRIS_INST_OUTPUT_STREAMING:
+ if (sub_state & (IRIS_INST_SUB_DRC_LAST |
+ IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE))
+ return true;
+ return false;
+ case IRIS_INST_STREAMING:
+ if (sub_state & (IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE | IRIS_INST_SUB_OUTPUT_PAUSE))
+ return true;
+ return false;
+ case IRIS_INST_DEINIT:
+ if (sub_state & (IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRAIN |
+ IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_DRAIN_LAST |
+ IRIS_INST_SUB_INPUT_PAUSE | IRIS_INST_SUB_OUTPUT_PAUSE))
+ return true;
+ return false;
+ default:
+ return false;
+ }
+}
+
+int iris_inst_change_sub_state(struct iris_inst *inst,
+ enum iris_inst_sub_state clear_sub_state,
+ enum iris_inst_sub_state set_sub_state)
+{
+ enum iris_inst_sub_state prev_sub_state;
+
+ if (inst->state == IRIS_INST_ERROR)
+ return 0;
+
+ if (!clear_sub_state && !set_sub_state)
+ return 0;
+
+ if ((clear_sub_state & set_sub_state) ||
+ set_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE ||
+ clear_sub_state > IRIS_INST_MAX_SUB_STATE_VALUE)
+ return -EINVAL;
+
+ prev_sub_state = inst->sub_state;
+
+ if (!iris_inst_allow_sub_state(inst, set_sub_state))
+ return -EINVAL;
+
+ inst->sub_state |= set_sub_state;
+ inst->sub_state &= ~clear_sub_state;
+
+ if (inst->sub_state != prev_sub_state)
+ dev_dbg(inst->core->dev, "sub_state changed from %x to %x\n",
+ prev_sub_state, inst->sub_state);
+
+ return 0;
+}
+
+int iris_inst_sub_state_change_drc(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state set_sub_state = 0;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC)
+ return -EINVAL;
+
+ if (inst->state == IRIS_INST_INPUT_STREAMING ||
+ inst->state == IRIS_INST_INIT)
+ set_sub_state = IRIS_INST_SUB_FIRST_IPSC | IRIS_INST_SUB_INPUT_PAUSE;
+ else
+ set_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_INPUT_PAUSE;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_inst_sub_state_change_drain_last(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state set_sub_state;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)
+ return -EINVAL;
+
+ if (!(inst->sub_state & IRIS_INST_SUB_DRAIN))
+ return -EINVAL;
+
+ set_sub_state = IRIS_INST_SUB_DRAIN_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_inst_sub_state_change_drc_last(struct iris_inst *inst)
+{
+ enum iris_inst_sub_state set_sub_state;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC_LAST)
+ return -EINVAL;
+
+ if (!(inst->sub_state & IRIS_INST_SUB_DRC) ||
+ !(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE))
+ return -EINVAL;
+
+ if (inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)
+ return 0;
+
+ set_sub_state = IRIS_INST_SUB_DRC_LAST | IRIS_INST_SUB_OUTPUT_PAUSE;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane)
+{
+ enum iris_inst_sub_state set_sub_state;
+
+ if (V4L2_TYPE_IS_OUTPUT(plane)) {
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ !(inst->sub_state & IRIS_INST_SUB_DRC_LAST))
+ return -EINVAL;
+
+ if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ !(inst->sub_state & IRIS_INST_SUB_DRAIN_LAST))
+ return -EINVAL;
+
+ set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
+ } else {
+ set_sub_state = IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+static inline bool iris_drc_pending(struct iris_inst *inst)
+{
+ return inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST;
+}
+
+static inline bool iris_drain_pending(struct iris_inst *inst)
+{
+ return inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+}
+
+bool iris_allow_cmd(struct iris_inst *inst, u32 cmd)
+{
+ struct vb2_queue *src_q = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ struct vb2_queue *dst_q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+
+ if (cmd == V4L2_DEC_CMD_START) {
+ if (vb2_is_streaming(src_q) || vb2_is_streaming(dst_q))
+ if (iris_drc_pending(inst) || iris_drain_pending(inst))
+ return true;
+ } else if (cmd == V4L2_DEC_CMD_STOP) {
+ if (vb2_is_streaming(src_q))
+ if (inst->sub_state != IRIS_INST_SUB_DRAIN)
+ return true;
+ }
+
+ return false;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_state.h b/drivers/media/platform/qcom/iris/iris_state.h
new file mode 100644
index 000000000000..78c61aac5e7e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_state.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_STATE_H__
+#define __IRIS_STATE_H__
+
+struct iris_inst;
+
+/**
+ * enum iris_core_state
+ *
+ * @IRIS_CORE_DEINIT: default state.
+ * @IRIS_CORE_INIT: core state with core initialized. FW loaded and
+ * HW brought out of reset, shared queues established
+ * between host driver and firmware.
+ * @IRIS_CORE_ERROR: error state.
+ *
+ * -----------
+ * |
+ * V
+ * -----------
+ * +--->| DEINIT |<---+
+ * | ----------- |
+ * | | |
+ * | v |
+ * | ----------- |
+ * | / \ |
+ * | / \ |
+ * | / \ |
+ * | v v v
+ * ----------- -----------
+ * | INIT |--->| ERROR |
+ * ----------- -----------
+ */
+enum iris_core_state {
+ IRIS_CORE_DEINIT,
+ IRIS_CORE_INIT,
+ IRIS_CORE_ERROR,
+};
+
+/**
+ * enum iris_inst_state
+ *
+ * @IRIS_INST_INIT: video instance is opened.
+ * @IRIS_INST_INPUT_STREAMING: stream on is completed on output plane.
+ * @IRIS_INST_OUTPUT_STREAMING: stream on is completed on capture plane.
+ * @IRIS_INST_STREAMING: stream on is completed on both output and capture planes.
+ * @IRIS_INST_DEINIT: video instance is closed.
+ * @IRIS_INST_ERROR: error state.
+ * |
+ * V
+ * -------------
+ * +--------| INIT |----------+
+ * | ------------- |
+ * | ^ ^ |
+ * | / \ |
+ * | / \ |
+ * | v v |
+ * | ----------- ----------- |
+ * | | INPUT OUTPUT | |
+ * |---| STREAMING STREAMING |---|
+ * | ----------- ----------- |
+ * | ^ ^ |
+ * | \ / |
+ * | \ / |
+ * | v v |
+ * | ------------- |
+ * |--------| STREAMING |-----------|
+ * | ------------- |
+ * | | |
+ * | | |
+ * | v |
+ * | ----------- |
+ * +-------->| DEINIT |<----------+
+ * | ----------- |
+ * | | |
+ * | | |
+ * | v |
+ * | ---------- |
+ * +-------->| ERROR |<------------+
+ * ----------
+ */
+enum iris_inst_state {
+ IRIS_INST_DEINIT,
+ IRIS_INST_INIT,
+ IRIS_INST_INPUT_STREAMING,
+ IRIS_INST_OUTPUT_STREAMING,
+ IRIS_INST_STREAMING,
+ IRIS_INST_ERROR,
+};
+
+#define IRIS_INST_SUB_STATES 8
+#define IRIS_INST_MAX_SUB_STATE_VALUE ((1 << IRIS_INST_SUB_STATES) - 1)
+
+/**
+ * enum iris_inst_sub_state
+ *
+ * @IRIS_INST_SUB_FIRST_IPSC: indicates source change is received from firmware
+ * when output port is not yet streaming.
+ * @IRIS_INST_SUB_DRC: indicates source change is received from firmware
+ * when output port is streaming and source change event is
+ * sent to client.
+ * @IRIS_INST_SUB_DRC_LAST: indicates last buffer is received from firmware
+ * as part of source change.
+ * @IRIS_INST_SUB_DRAIN: indicates drain is in progress.
+ * @IRIS_INST_SUB_DRAIN_LAST: indicates last buffer is received from firmware
+ * as part of drain sequence.
+ * @IRIS_INST_SUB_INPUT_PAUSE: source change is received form firmware. This
+ * indicates that firmware is paused to process
+ * any further input frames.
+ * @IRIS_INST_SUB_OUTPUT_PAUSE: last buffer is received form firmware as part
+ * of drc sequence. This indicates that
+ * firmware is paused to process any further output frames.
+ * @IRIS_INST_SUB_LOAD_RESOURCES: indicates all the resources have been loaded by the
+ * firmware and it is ready for processing.
+ */
+enum iris_inst_sub_state {
+ IRIS_INST_SUB_FIRST_IPSC = BIT(0),
+ IRIS_INST_SUB_DRC = BIT(1),
+ IRIS_INST_SUB_DRC_LAST = BIT(2),
+ IRIS_INST_SUB_DRAIN = BIT(3),
+ IRIS_INST_SUB_DRAIN_LAST = BIT(4),
+ IRIS_INST_SUB_INPUT_PAUSE = BIT(5),
+ IRIS_INST_SUB_OUTPUT_PAUSE = BIT(6),
+ IRIS_INST_SUB_LOAD_RESOURCES = BIT(7),
+};
+
+int iris_inst_change_state(struct iris_inst *inst,
+ enum iris_inst_state request_state);
+int iris_inst_change_sub_state(struct iris_inst *inst,
+ enum iris_inst_sub_state clear_sub_state,
+ enum iris_inst_sub_state set_sub_state);
+
+int iris_inst_state_change_streamon(struct iris_inst *inst, u32 plane);
+int iris_inst_state_change_streamoff(struct iris_inst *inst, u32 plane);
+int iris_inst_sub_state_change_drc(struct iris_inst *inst);
+int iris_inst_sub_state_change_drain_last(struct iris_inst *inst);
+int iris_inst_sub_state_change_drc_last(struct iris_inst *inst);
+int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane);
+bool iris_allow_cmd(struct iris_inst *inst, u32 cmd);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c
new file mode 100644
index 000000000000..83c70d6a2d90
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_utils.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_instance.h"
+#include "iris_utils.h"
+
+bool iris_res_is_less_than(u32 width, u32 height,
+ u32 ref_width, u32 ref_height)
+{
+ u32 num_mbs = NUM_MBS_PER_FRAME(height, width);
+ u32 max_side = max(ref_width, ref_height);
+
+ if (num_mbs < NUM_MBS_PER_FRAME(ref_height, ref_width) &&
+ width < max_side &&
+ height < max_side)
+ return true;
+
+ return false;
+}
+
+int iris_get_mbpf(struct iris_inst *inst)
+{
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+ u32 width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+
+ return NUM_MBS_PER_FRAME(height, width);
+}
+
+bool iris_split_mode_enabled(struct iris_inst *inst)
+{
+ return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12;
+}
+
+void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
+ enum vb2_buffer_state state)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct vb2_v4l2_buffer *buf;
+
+ if (V4L2_TYPE_IS_OUTPUT(type)) {
+ while ((buf = v4l2_m2m_src_buf_remove(m2m_ctx)))
+ v4l2_m2m_buf_done(buf, state);
+ } else if (V4L2_TYPE_IS_CAPTURE(type)) {
+ while ((buf = v4l2_m2m_dst_buf_remove(m2m_ctx)))
+ v4l2_m2m_buf_done(buf, state);
+ }
+}
+
+int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush)
+{
+ struct iris_core *core = inst->core;
+ u32 hw_response_timeout_val;
+ struct completion *done;
+ int ret;
+
+ hw_response_timeout_val = core->iris_platform_data->hw_response_timeout;
+ done = is_flush ? &inst->flush_completion : &inst->completion;
+
+ mutex_unlock(&inst->lock);
+ ret = wait_for_completion_timeout(done, msecs_to_jiffies(hw_response_timeout_val));
+ mutex_lock(&inst->lock);
+ if (!ret) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id)
+{
+ struct iris_inst *inst;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(inst, &core->instances, list) {
+ if (inst->session_id == session_id) {
+ mutex_unlock(&core->lock);
+ return inst;
+ }
+ }
+
+ mutex_unlock(&core->lock);
+ return NULL;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h
new file mode 100644
index 000000000000..49869cf7a376
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_utils.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_UTILS_H__
+#define __IRIS_UTILS_H__
+
+struct iris_core;
+#include "iris_buffer.h"
+
+struct iris_hfi_rect_desc {
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
+struct iris_hfi_frame_info {
+ u32 picture_type;
+ u32 no_output;
+ u32 data_corrupt;
+ u32 overflow;
+};
+
+struct iris_ts_metadata {
+ u64 ts_ns;
+ u64 ts_us;
+ u32 flags;
+ struct v4l2_timecode tc;
+};
+
+#define NUM_MBS_PER_FRAME(height, width) \
+ (DIV_ROUND_UP(height, 16) * DIV_ROUND_UP(width, 16))
+
+static inline enum iris_buffer_type iris_v4l2_type_to_driver(u32 type)
+{
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ return BUF_INPUT;
+ else
+ return BUF_OUTPUT;
+}
+
+bool iris_res_is_less_than(u32 width, u32 height,
+ u32 ref_width, u32 ref_height);
+int iris_get_mbpf(struct iris_inst *inst);
+bool iris_split_mode_enabled(struct iris_inst *inst);
+struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id);
+void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
+ enum vb2_buffer_state state);
+int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
new file mode 100644
index 000000000000..cdf11feb590b
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/videobuf2-dma-contig.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_instance.h"
+#include "iris_vb2.h"
+#include "iris_vdec.h"
+#include "iris_power.h"
+
+static int iris_check_core_mbpf(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance;
+ u32 total_mbpf = 0;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry(instance, &core->instances, list)
+ total_mbpf += iris_get_mbpf(instance);
+ mutex_unlock(&core->lock);
+
+ if (total_mbpf > core->iris_platform_data->max_core_mbpf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int iris_check_inst_mbpf(struct iris_inst *inst)
+{
+ struct platform_inst_caps *caps;
+ u32 mbpf, max_mbpf;
+
+ caps = inst->core->iris_platform_data->inst_caps;
+ max_mbpf = caps->max_mbpf;
+ mbpf = iris_get_mbpf(inst);
+ if (mbpf > max_mbpf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int iris_check_resolution_supported(struct iris_inst *inst)
+{
+ u32 width, height, min_width, min_height, max_width, max_height;
+ struct platform_inst_caps *caps;
+
+ caps = inst->core->iris_platform_data->inst_caps;
+ width = inst->fmt_src->fmt.pix_mp.width;
+ height = inst->fmt_src->fmt.pix_mp.height;
+
+ min_width = caps->min_frame_width;
+ max_width = caps->max_frame_width;
+ min_height = caps->min_frame_height;
+ max_height = caps->max_frame_height;
+
+ if (!(min_width <= width && width <= max_width) ||
+ !(min_height <= height && height <= max_height))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int iris_check_session_supported(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *instance = NULL;
+ bool found = false;
+ int ret;
+
+ list_for_each_entry(instance, &core->instances, list) {
+ if (instance == inst)
+ found = true;
+ }
+
+ if (!found) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ ret = iris_check_core_mbpf(inst);
+ if (ret)
+ goto exit;
+
+ ret = iris_check_inst_mbpf(inst);
+ if (ret)
+ goto exit;
+
+ ret = iris_check_resolution_supported(inst);
+ if (ret)
+ goto exit;
+
+ return 0;
+exit:
+ dev_err(inst->core->dev, "current session not supported(%d)\n", ret);
+
+ return ret;
+}
+
+int iris_vb2_buf_init(struct vb2_buffer *vb2)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
+ struct iris_buffer *buf = to_iris_buffer(vbuf);
+
+ buf->device_addr = vb2_dma_contig_plane_dma_addr(vb2, 0);
+
+ return 0;
+}
+
+int iris_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[])
+{
+ struct iris_inst *inst;
+ struct iris_core *core;
+ struct v4l2_format *f;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(q);
+
+ mutex_lock(&inst->lock);
+ if (inst->state == IRIS_INST_ERROR) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ core = inst->core;
+ f = V4L2_TYPE_IS_OUTPUT(q->type) ? inst->fmt_src : inst->fmt_dst;
+
+ if (*num_planes) {
+ if (*num_planes != f->fmt.pix_mp.num_planes ||
+ sizes[0] < f->fmt.pix_mp.plane_fmt[0].sizeimage)
+ ret = -EINVAL;
+ goto unlock;
+ }
+
+ ret = iris_check_session_supported(inst);
+ if (ret)
+ goto unlock;
+
+ if (!inst->once_per_session_set) {
+ inst->once_per_session_set = true;
+
+ ret = core->hfi_ops->session_open(inst);
+ if (ret) {
+ ret = -EINVAL;
+ dev_err(core->dev, "session open failed\n");
+ goto unlock;
+ }
+
+ ret = iris_inst_change_state(inst, IRIS_INST_INIT);
+ if (ret)
+ goto unlock;
+ }
+
+ *num_planes = 1;
+ sizes[0] = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ enum iris_buffer_type buf_type;
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(q);
+
+ if (V4L2_TYPE_IS_CAPTURE(q->type) && inst->state == IRIS_INST_INIT)
+ return 0;
+
+ mutex_lock(&inst->lock);
+ if (inst->state == IRIS_INST_ERROR) {
+ ret = -EBUSY;
+ goto error;
+ }
+
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
+ !V4L2_TYPE_IS_CAPTURE(q->type)) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ iris_scale_power(inst);
+
+ ret = iris_check_session_supported(inst);
+ if (ret)
+ goto error;
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ ret = iris_vdec_streamon_input(inst);
+ else if (V4L2_TYPE_IS_CAPTURE(q->type))
+ ret = iris_vdec_streamon_output(inst);
+ if (ret)
+ goto error;
+
+ buf_type = iris_v4l2_type_to_driver(q->type);
+
+ ret = iris_queue_deferred_buffers(inst, buf_type);
+ if (ret)
+ goto error;
+
+ mutex_unlock(&inst->lock);
+
+ return ret;
+
+error:
+ iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_QUEUED);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+void iris_vb2_stop_streaming(struct vb2_queue *q)
+{
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(q);
+
+ if (V4L2_TYPE_IS_CAPTURE(q->type) && inst->state == IRIS_INST_INIT)
+ return;
+
+ mutex_lock(&inst->lock);
+
+ if (!V4L2_TYPE_IS_OUTPUT(q->type) &&
+ !V4L2_TYPE_IS_CAPTURE(q->type))
+ goto exit;
+
+ ret = iris_vdec_session_streamoff(inst, q->type);
+ if (ret)
+ goto exit;
+
+exit:
+ iris_helper_buffers_done(inst, q->type, VB2_BUF_STATE_ERROR);
+ if (ret)
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+
+ mutex_unlock(&inst->lock);
+}
+
+int iris_vb2_buf_prepare(struct vb2_buffer *vb)
+{
+ struct iris_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ }
+
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_OUTPUT))
+ return -EINVAL;
+ if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+ vb2_plane_size(vb, 0) < iris_get_buffer_size(inst, BUF_INPUT))
+ return -EINVAL;
+
+ return 0;
+}
+
+int iris_vb2_buf_out_validate(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *v4l2_buf = to_vb2_v4l2_buffer(vb);
+
+ v4l2_buf->field = V4L2_FIELD_NONE;
+
+ return 0;
+}
+
+void iris_vb2_buf_queue(struct vb2_buffer *vb2)
+{
+ static const struct v4l2_event eos = { .type = V4L2_EVENT_EOS };
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
+ struct v4l2_m2m_ctx *m2m_ctx;
+ struct iris_inst *inst;
+ int ret = 0;
+
+ inst = vb2_get_drv_priv(vb2->vb2_queue);
+
+ mutex_lock(&inst->lock);
+ if (inst->state == IRIS_INST_ERROR) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+
+ m2m_ctx = inst->m2m_ctx;
+
+ if (!vb2->planes[0].bytesused && V4L2_TYPE_IS_OUTPUT(vb2->type)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) {
+ if ((inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST) ||
+ (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST)) {
+ vbuf->flags |= V4L2_BUF_FLAG_LAST;
+ vbuf->sequence = inst->sequence_cap++;
+ vbuf->field = V4L2_FIELD_NONE;
+ vb2_set_plane_payload(vb2, 0, 0);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
+ if (!v4l2_m2m_has_stopped(m2m_ctx)) {
+ v4l2_event_queue_fh(&inst->fh, &eos);
+ v4l2_m2m_mark_stopped(m2m_ctx);
+ }
+ goto exit;
+ }
+ }
+
+ v4l2_m2m_buf_queue(m2m_ctx, vbuf);
+
+ ret = iris_vdec_qbuf(inst, vbuf);
+
+exit:
+ if (ret) {
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
+ }
+ mutex_unlock(&inst->lock);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.h b/drivers/media/platform/qcom/iris/iris_vb2.h
new file mode 100644
index 000000000000..a88565fdd3e4
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vb2.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VB2_H__
+#define __IRIS_VB2_H__
+
+int iris_vb2_buf_init(struct vb2_buffer *vb2);
+int iris_vb2_queue_setup(struct vb2_queue *q,
+ unsigned int *num_buffers, unsigned int *num_planes,
+ unsigned int sizes[], struct device *alloc_devs[]);
+int iris_vb2_start_streaming(struct vb2_queue *q, unsigned int count);
+void iris_vb2_stop_streaming(struct vb2_queue *q);
+int iris_vb2_buf_prepare(struct vb2_buffer *vb);
+int iris_vb2_buf_out_validate(struct vb2_buffer *vb);
+void iris_vb2_buf_queue(struct vb2_buffer *vb2);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
new file mode 100644
index 000000000000..4143acedfc57
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -0,0 +1,659 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <media/v4l2-event.h>
+#include <media/v4l2-mem2mem.h>
+
+#include "iris_buffer.h"
+#include "iris_ctrls.h"
+#include "iris_instance.h"
+#include "iris_power.h"
+#include "iris_vdec.h"
+#include "iris_vpu_buffer.h"
+
+#define DEFAULT_WIDTH 320
+#define DEFAULT_HEIGHT 240
+#define DEFAULT_CODEC_ALIGNMENT 16
+
+int iris_vdec_inst_init(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct v4l2_format *f;
+
+ inst->fmt_src = kzalloc(sizeof(*inst->fmt_src), GFP_KERNEL);
+ inst->fmt_dst = kzalloc(sizeof(*inst->fmt_dst), GFP_KERNEL);
+
+ inst->fw_min_count = MIN_BUFFERS;
+
+ f = inst->fmt_src;
+ f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ f->fmt.pix_mp.width = DEFAULT_WIDTH;
+ f->fmt.pix_mp.height = DEFAULT_HEIGHT;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
+ inst->buffers[BUF_INPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ f = inst->fmt_dst;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+ f->fmt.pix_mp.width = ALIGN(DEFAULT_WIDTH, 128);
+ f->fmt.pix_mp.height = ALIGN(DEFAULT_HEIGHT, 32);
+ f->fmt.pix_mp.num_planes = 1;
+ f->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(DEFAULT_WIDTH, 128);
+ f->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.colorspace = V4L2_COLORSPACE_DEFAULT;
+ f->fmt.pix_mp.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ f->fmt.pix_mp.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ memcpy(&inst->fw_caps[0], &core->inst_fw_caps[0],
+ INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap));
+
+ return iris_ctrls_init(inst);
+}
+
+void iris_vdec_inst_deinit(struct iris_inst *inst)
+{
+ kfree(inst->fmt_dst);
+ kfree(inst->fmt_src);
+}
+
+int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
+{
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ f->pixelformat = V4L2_PIX_FMT_H264;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ f->pixelformat = V4L2_PIX_FMT_NV12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_format *f_inst;
+ struct vb2_queue *src_q;
+
+ memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264) {
+ f_inst = inst->fmt_src;
+ f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
+ f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
+ f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
+ }
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) {
+ f_inst = inst->fmt_dst;
+ f->fmt.pix_mp.pixelformat = f_inst->fmt.pix_mp.pixelformat;
+ f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
+ f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
+ }
+
+ src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+ if (vb2_is_streaming(src_q)) {
+ f_inst = inst->fmt_src;
+ f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
+ f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (pixmp->field == V4L2_FIELD_ANY)
+ pixmp->field = V4L2_FIELD_NONE;
+
+ pixmp->num_planes = 1;
+
+ return 0;
+}
+
+int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
+{
+ struct v4l2_format *fmt, *output_fmt;
+ struct vb2_queue *q;
+ u32 codec_align;
+
+ q = v4l2_m2m_get_vq(inst->m2m_ctx, f->type);
+ if (!q)
+ return -EINVAL;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ iris_vdec_try_fmt(inst, f);
+
+ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264)
+ return -EINVAL;
+
+ fmt = inst->fmt_src;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+ codec_align = DEFAULT_CODEC_ALIGNMENT;
+ fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, codec_align);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, codec_align);
+ fmt->fmt.pix_mp.num_planes = 1;
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = 0;
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_INPUT);
+ inst->buffers[BUF_INPUT].min_count = iris_vpu_buf_count(inst, BUF_INPUT);
+ inst->buffers[BUF_INPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+
+ output_fmt = inst->fmt_dst;
+ output_fmt->fmt.pix_mp.colorspace = f->fmt.pix_mp.colorspace;
+ output_fmt->fmt.pix_mp.xfer_func = f->fmt.pix_mp.xfer_func;
+ output_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
+ output_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+
+ inst->crop.left = 0;
+ inst->crop.top = 0;
+ inst->crop.width = f->fmt.pix_mp.width;
+ inst->crop.height = f->fmt.pix_mp.height;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ fmt = inst->fmt_dst;
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (fmt->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12)
+ return -EINVAL;
+ fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
+ fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
+ fmt->fmt.pix_mp.num_planes = 1;
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(f->fmt.pix_mp.width, 128);
+ fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
+ inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ inst->crop.top = 0;
+ inst->crop.left = 0;
+ inst->crop.width = f->fmt.pix_mp.width;
+ inst->crop.height = f->fmt.pix_mp.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+ memcpy(f, fmt, sizeof(*fmt));
+
+ return 0;
+}
+
+int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub)
+{
+ int ret = 0;
+
+ switch (sub->type) {
+ case V4L2_EVENT_EOS:
+ ret = v4l2_event_subscribe(&inst->fh, sub, 0, NULL);
+ break;
+ case V4L2_EVENT_SOURCE_CHANGE:
+ ret = v4l2_src_change_event_subscribe(&inst->fh, sub);
+ break;
+ case V4L2_EVENT_CTRL:
+ ret = v4l2_ctrl_subscribe_event(&inst->fh, sub);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+void iris_vdec_src_change(struct iris_inst *inst)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_event event = {0};
+ struct vb2_queue *src_q;
+
+ src_q = v4l2_m2m_get_src_vq(m2m_ctx);
+ if (!vb2_is_streaming(src_q))
+ return;
+
+ event.type = V4L2_EVENT_SOURCE_CHANGE;
+ event.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION;
+ v4l2_event_queue_fh(&inst->fh, &event);
+}
+
+static int iris_vdec_get_num_queued_buffers(struct iris_inst *inst,
+ enum iris_buffer_type type)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+ u32 count = 0;
+
+ switch (type) {
+ case BUF_INPUT:
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ continue;
+ count++;
+ }
+ return count;
+ case BUF_OUTPUT:
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (!(buf->attr & BUF_ATTR_QUEUED))
+ continue;
+ count++;
+ }
+ return count;
+ default:
+ return count;
+ }
+}
+
+static void iris_vdec_flush_deferred_buffers(struct iris_inst *inst,
+ enum iris_buffer_type type)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+ struct v4l2_m2m_buffer *buffer, *n;
+ struct iris_buffer *buf;
+
+ if (type == BUF_INPUT) {
+ v4l2_m2m_for_each_src_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEFERRED) {
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ buf->data_size = 0;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+ } else {
+ v4l2_m2m_for_each_dst_buf_safe(m2m_ctx, buffer, n) {
+ buf = to_iris_buffer(&buffer->vb);
+ if (buf->attr & BUF_ATTR_DEFERRED) {
+ if (!(buf->attr & BUF_ATTR_BUFFER_DONE)) {
+ buf->attr |= BUF_ATTR_BUFFER_DONE;
+ buf->data_size = 0;
+ iris_vb2_buffer_done(inst, buf);
+ }
+ }
+ }
+ }
+}
+
+static void iris_vdec_kill_session(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+
+ if (!inst->session_id)
+ return;
+
+ hfi_ops->session_close(inst);
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+}
+
+int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ enum iris_buffer_type buffer_type;
+ u32 count;
+ int ret;
+
+ switch (plane) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+ buffer_type = BUF_INPUT;
+ break;
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ buffer_type = BUF_OUTPUT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = hfi_ops->session_stop(inst, plane);
+ if (ret)
+ goto error;
+
+ count = iris_vdec_get_num_queued_buffers(inst, buffer_type);
+ if (count) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = iris_inst_state_change_streamoff(inst, plane);
+ if (ret)
+ goto error;
+
+ iris_vdec_flush_deferred_buffers(inst, buffer_type);
+
+ return 0;
+
+error:
+ iris_vdec_kill_session(inst);
+ iris_vdec_flush_deferred_buffers(inst, buffer_type);
+
+ return ret;
+}
+
+static int iris_vdec_process_streamon_input(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ enum iris_inst_sub_state set_sub_state = 0;
+ int ret;
+
+ iris_scale_power(inst);
+
+ ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_inst_change_sub_state(inst, IRIS_INST_SUB_INPUT_PAUSE, 0);
+ if (ret)
+ return ret;
+ }
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC ||
+ inst->sub_state & IRIS_INST_SUB_DRAIN ||
+ inst->sub_state & IRIS_INST_SUB_FIRST_IPSC) {
+ if (!(inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE)) {
+ if (hfi_ops->session_pause) {
+ ret = hfi_ops->session_pause(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ }
+ set_sub_state = IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ }
+
+ ret = iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, 0, set_sub_state);
+}
+
+int iris_vdec_streamon_input(struct iris_inst *inst)
+{
+ int ret;
+
+ ret = iris_set_properties(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_alloc_and_queue_persist_bufs(inst);
+ if (ret)
+ return ret;
+
+ iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+ ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_vdec_process_streamon_input(inst);
+}
+
+static int iris_vdec_process_streamon_output(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ bool drain_active = false, drc_active = false;
+ enum iris_inst_sub_state clear_sub_state = 0;
+ int ret = 0;
+
+ iris_scale_power(inst);
+
+ drain_active = inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST;
+
+ drc_active = inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST;
+
+ if (drc_active)
+ clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+ else if (drain_active)
+ clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = iris_alloc_and_queue_input_int_bufs(inst);
+ if (ret)
+ return ret;
+ ret = iris_set_stage(inst, STAGE);
+ if (ret)
+ return ret;
+ ret = iris_set_pipe(inst, PIPE);
+ if (ret)
+ return ret;
+ }
+
+ if (inst->state == IRIS_INST_INPUT_STREAMING &&
+ inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ if (!drain_active)
+ ret = hfi_ops->session_resume_drc(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ else if (hfi_ops->session_resume_drain)
+ ret = hfi_ops->session_resume_drain(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+
+ if (inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)
+ clear_sub_state |= IRIS_INST_SUB_FIRST_IPSC;
+
+ ret = hfi_ops->session_start(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE)
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+
+ ret = iris_inst_state_change_streamon(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, clear_sub_state, 0);
+}
+
+int iris_vdec_streamon_output(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_create_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+
+ ret = iris_vdec_process_streamon_output(inst);
+ if (ret)
+ goto error;
+
+ ret = iris_queue_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ goto error;
+
+ return ret;
+
+error:
+ iris_vdec_session_streamoff(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+
+ return ret;
+}
+
+static int
+iris_vdec_vb2_buffer_to_driver(struct vb2_buffer *vb2, struct iris_buffer *buf)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2);
+
+ buf->type = iris_v4l2_type_to_driver(vb2->type);
+ buf->index = vb2->index;
+ buf->fd = vb2->planes[0].m.fd;
+ buf->buffer_size = vb2->planes[0].length;
+ buf->data_offset = vb2->planes[0].data_offset;
+ buf->data_size = vb2->planes[0].bytesused - vb2->planes[0].data_offset;
+ buf->flags = vbuf->flags;
+ buf->timestamp = vb2->timestamp;
+ buf->attr = 0;
+
+ return 0;
+}
+
+static void
+iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ u32 mask = V4L2_BUF_FLAG_TIMECODE | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ struct vb2_buffer *vb = &vbuf->vb2_buf;
+ u64 ts_us = vb->timestamp;
+
+ if (inst->metadata_idx >= ARRAY_SIZE(inst->tss))
+ inst->metadata_idx = 0;
+
+ do_div(ts_us, NSEC_PER_USEC);
+
+ inst->tss[inst->metadata_idx].flags = vbuf->flags & mask;
+ inst->tss[inst->metadata_idx].tc = vbuf->timecode;
+ inst->tss[inst->metadata_idx].ts_us = ts_us;
+ inst->tss[inst->metadata_idx].ts_ns = vb->timestamp;
+
+ inst->metadata_idx++;
+}
+
+int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
+{
+ struct iris_buffer *buf = to_iris_buffer(vbuf);
+ struct vb2_buffer *vb2 = &vbuf->vb2_buf;
+ struct vb2_queue *q;
+ int ret;
+
+ ret = iris_vdec_vb2_buffer_to_driver(vb2, buf);
+ if (ret)
+ return ret;
+
+ if (buf->type == BUF_INPUT)
+ iris_set_ts_metadata(inst, vbuf);
+
+ q = v4l2_m2m_get_vq(inst->m2m_ctx, vb2->type);
+ if (!vb2_is_streaming(q)) {
+ buf->attr |= BUF_ATTR_DEFERRED;
+ return 0;
+ }
+
+ iris_scale_power(inst);
+
+ return iris_queue_buffer(inst, buf);
+}
+
+int iris_vdec_start_cmd(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ enum iris_inst_sub_state clear_sub_state = 0;
+ struct vb2_queue *dst_vq;
+ int ret;
+
+ dst_vq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+
+ if (inst->sub_state & IRIS_INST_SUB_DRC &&
+ inst->sub_state & IRIS_INST_SUB_DRC_LAST) {
+ vb2_clear_last_buffer_dequeued(dst_vq);
+ clear_sub_state = IRIS_INST_SUB_DRC | IRIS_INST_SUB_DRC_LAST;
+
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ ret = hfi_ops->session_resume_drc(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ ret = hfi_ops->session_resume_drc(inst,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+ } else if (inst->sub_state & IRIS_INST_SUB_DRAIN &&
+ inst->sub_state & IRIS_INST_SUB_DRAIN_LAST) {
+ vb2_clear_last_buffer_dequeued(dst_vq);
+ clear_sub_state = IRIS_INST_SUB_DRAIN | IRIS_INST_SUB_DRAIN_LAST;
+ if (inst->sub_state & IRIS_INST_SUB_INPUT_PAUSE) {
+ if (hfi_ops->session_resume_drain) {
+ ret =
+ hfi_ops->session_resume_drain(inst,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+ }
+
+ clear_sub_state |= IRIS_INST_SUB_INPUT_PAUSE;
+ }
+ if (inst->sub_state & IRIS_INST_SUB_OUTPUT_PAUSE) {
+ if (hfi_ops->session_resume_drain) {
+ ret =
+ hfi_ops->session_resume_drain(inst,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (ret)
+ return ret;
+ }
+
+ clear_sub_state |= IRIS_INST_SUB_OUTPUT_PAUSE;
+ }
+ } else {
+ dev_err(inst->core->dev, "start called before receiving last_flag\n");
+ iris_inst_change_state(inst, IRIS_INST_ERROR);
+ return -EBUSY;
+ }
+
+ return iris_inst_change_sub_state(inst, clear_sub_state, 0);
+}
+
+int iris_vdec_stop_cmd(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ int ret;
+
+ ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (ret)
+ return ret;
+
+ return iris_inst_change_sub_state(inst, 0, IRIS_INST_SUB_DRAIN);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h
new file mode 100644
index 000000000000..b24932dc511a
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vdec.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VDEC_H__
+#define __IRIS_VDEC_H__
+
+struct iris_inst;
+
+int iris_vdec_inst_init(struct iris_inst *inst);
+void iris_vdec_inst_deinit(struct iris_inst *inst);
+int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f);
+int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f);
+int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f);
+int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub);
+void iris_vdec_src_change(struct iris_inst *inst);
+int iris_vdec_streamon_input(struct iris_inst *inst);
+int iris_vdec_streamon_output(struct iris_inst *inst);
+int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf);
+int iris_vdec_start_cmd(struct iris_inst *inst);
+int iris_vdec_stop_cmd(struct iris_inst *inst);
+int iris_vdec_session_streamoff(struct iris_inst *inst, u32 plane);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
new file mode 100644
index 000000000000..ca0f4e310f77
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -0,0 +1,453 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/pm_runtime.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "iris_vidc.h"
+#include "iris_instance.h"
+#include "iris_vdec.h"
+#include "iris_vb2.h"
+#include "iris_vpu_buffer.h"
+#include "iris_platform_common.h"
+
+#define IRIS_DRV_NAME "iris_driver"
+#define IRIS_BUS_NAME "platform:iris_icc"
+#define STEP_WIDTH 1
+#define STEP_HEIGHT 1
+
+static void iris_v4l2_fh_init(struct iris_inst *inst)
+{
+ v4l2_fh_init(&inst->fh, inst->core->vdev_dec);
+ inst->fh.ctrl_handler = &inst->ctrl_handler;
+ v4l2_fh_add(&inst->fh);
+}
+
+static void iris_v4l2_fh_deinit(struct iris_inst *inst)
+{
+ v4l2_fh_del(&inst->fh);
+ inst->fh.ctrl_handler = NULL;
+ v4l2_fh_exit(&inst->fh);
+}
+
+static void iris_add_session(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *iter;
+ u32 count = 0;
+
+ mutex_lock(&core->lock);
+
+ list_for_each_entry(iter, &core->instances, list)
+ count++;
+
+ if (count < core->iris_platform_data->max_session_count)
+ list_add_tail(&inst->list, &core->instances);
+
+ mutex_unlock(&core->lock);
+}
+
+static void iris_remove_session(struct iris_inst *inst)
+{
+ struct iris_core *core = inst->core;
+ struct iris_inst *iter, *temp;
+
+ mutex_lock(&core->lock);
+ list_for_each_entry_safe(iter, temp, &core->instances, list) {
+ if (iter->session_id == inst->session_id) {
+ list_del_init(&iter->list);
+ break;
+ }
+ }
+ mutex_unlock(&core->lock);
+}
+
+static inline struct iris_inst *iris_get_inst(struct file *filp, void *fh)
+{
+ return container_of(filp->private_data, struct iris_inst, fh);
+}
+
+static void iris_m2m_device_run(void *priv)
+{
+}
+
+static void iris_m2m_job_abort(void *priv)
+{
+ struct iris_inst *inst = priv;
+ struct v4l2_m2m_ctx *m2m_ctx = inst->m2m_ctx;
+
+ v4l2_m2m_job_finish(inst->m2m_dev, m2m_ctx);
+}
+
+static const struct v4l2_m2m_ops iris_m2m_ops = {
+ .device_run = iris_m2m_device_run,
+ .job_abort = iris_m2m_job_abort,
+};
+
+static int
+iris_m2m_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
+{
+ struct iris_inst *inst = priv;
+ int ret;
+
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ src_vq->ops = inst->core->iris_vb2_ops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+ src_vq->drv_priv = inst;
+ src_vq->buf_struct_size = sizeof(struct iris_buffer);
+ src_vq->min_reqbufs_allocation = MIN_BUFFERS;
+ src_vq->dev = inst->core->dev;
+ src_vq->lock = &inst->ctx_q_lock;
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
+ dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ dst_vq->ops = inst->core->iris_vb2_ops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+ dst_vq->drv_priv = inst;
+ dst_vq->buf_struct_size = sizeof(struct iris_buffer);
+ dst_vq->min_reqbufs_allocation = MIN_BUFFERS;
+ dst_vq->dev = inst->core->dev;
+ dst_vq->lock = &inst->ctx_q_lock;
+
+ return vb2_queue_init(dst_vq);
+}
+
+int iris_open(struct file *filp)
+{
+ struct iris_core *core = video_drvdata(filp);
+ struct iris_inst *inst;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(core->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = iris_core_init(core);
+ if (ret) {
+ dev_err(core->dev, "core init failed\n");
+ pm_runtime_put_sync(core->dev);
+ return ret;
+ }
+
+ pm_runtime_put_sync(core->dev);
+
+ inst = core->iris_platform_data->get_instance();
+ if (!inst)
+ return -ENOMEM;
+
+ inst->core = core;
+ inst->session_id = hash32_ptr(inst);
+ inst->state = IRIS_INST_DEINIT;
+
+ mutex_init(&inst->lock);
+ mutex_init(&inst->ctx_q_lock);
+
+ INIT_LIST_HEAD(&inst->buffers[BUF_BIN].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_ARP].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_COMV].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_NON_COMV].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_LINE].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_DPB].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_PERSIST].list);
+ INIT_LIST_HEAD(&inst->buffers[BUF_SCRATCH_1].list);
+ init_completion(&inst->completion);
+ init_completion(&inst->flush_completion);
+
+ iris_v4l2_fh_init(inst);
+
+ inst->m2m_dev = v4l2_m2m_init(&iris_m2m_ops);
+ if (IS_ERR_OR_NULL(inst->m2m_dev)) {
+ ret = -EINVAL;
+ goto fail_v4l2_fh_deinit;
+ }
+
+ inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, iris_m2m_queue_init);
+ if (IS_ERR_OR_NULL(inst->m2m_ctx)) {
+ ret = -EINVAL;
+ goto fail_m2m_release;
+ }
+
+ ret = iris_vdec_inst_init(inst);
+ if (ret)
+ goto fail_m2m_ctx_release;
+
+ iris_add_session(inst);
+
+ inst->fh.m2m_ctx = inst->m2m_ctx;
+ filp->private_data = &inst->fh;
+
+ return 0;
+
+fail_m2m_ctx_release:
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+fail_m2m_release:
+ v4l2_m2m_release(inst->m2m_dev);
+fail_v4l2_fh_deinit:
+ iris_v4l2_fh_deinit(inst);
+ mutex_destroy(&inst->ctx_q_lock);
+ mutex_destroy(&inst->lock);
+ kfree(inst);
+
+ return ret;
+}
+
+static void iris_session_close(struct iris_inst *inst)
+{
+ const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ bool wait_for_response = true;
+ int ret;
+
+ if (inst->state == IRIS_INST_DEINIT)
+ return;
+
+ reinit_completion(&inst->completion);
+
+ ret = hfi_ops->session_close(inst);
+ if (ret)
+ wait_for_response = false;
+
+ if (wait_for_response)
+ iris_wait_for_session_response(inst, false);
+}
+
+int iris_close(struct file *filp)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+ v4l2_m2m_release(inst->m2m_dev);
+ mutex_lock(&inst->lock);
+ iris_vdec_inst_deinit(inst);
+ iris_session_close(inst);
+ iris_inst_change_state(inst, IRIS_INST_DEINIT);
+ iris_v4l2_fh_deinit(inst);
+ iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ iris_remove_session(inst);
+ mutex_unlock(&inst->lock);
+ mutex_destroy(&inst->ctx_q_lock);
+ mutex_destroy(&inst->lock);
+ kfree(inst);
+ filp->private_data = NULL;
+
+ return 0;
+}
+
+static int iris_enum_fmt(struct file *filp, void *fh, struct v4l2_fmtdesc *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+
+ if (f->index)
+ return -EINVAL;
+
+ return iris_vdec_enum_fmt(inst, f);
+}
+
+static int iris_try_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret;
+
+ mutex_lock(&inst->lock);
+ ret = iris_vdec_try_fmt(inst, f);
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_s_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret;
+
+ mutex_lock(&inst->lock);
+ ret = iris_vdec_s_fmt(inst, f);
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_g_fmt_vid_mplane(struct file *filp, void *fh, struct v4l2_format *f)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret = 0;
+
+ mutex_lock(&inst->lock);
+ if (V4L2_TYPE_IS_OUTPUT(f->type))
+ *f = *inst->fmt_src;
+ else if (V4L2_TYPE_IS_CAPTURE(f->type))
+ *f = *inst->fmt_dst;
+ else
+ ret = -EINVAL;
+
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static int iris_enum_framesizes(struct file *filp, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ struct platform_inst_caps *caps;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ if (fsize->pixel_format != V4L2_PIX_FMT_H264 &&
+ fsize->pixel_format != V4L2_PIX_FMT_NV12)
+ return -EINVAL;
+
+ caps = inst->core->iris_platform_data->inst_caps;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = caps->min_frame_width;
+ fsize->stepwise.max_width = caps->max_frame_width;
+ fsize->stepwise.step_width = STEP_WIDTH;
+ fsize->stepwise.min_height = caps->min_frame_height;
+ fsize->stepwise.max_height = caps->max_frame_height;
+ fsize->stepwise.step_height = STEP_HEIGHT;
+
+ return 0;
+}
+
+static int iris_querycap(struct file *filp, void *fh, struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, IRIS_DRV_NAME, sizeof(cap->driver));
+ strscpy(cap->card, "Iris Decoder", sizeof(cap->card));
+
+ return 0;
+}
+
+static int iris_g_selection(struct file *filp, void *fh, struct v4l2_selection *s)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_PADDED:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.left = inst->crop.left;
+ s->r.top = inst->crop.top;
+ s->r.width = inst->crop.width;
+ s->r.height = inst->crop.height;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iris_subscribe_event(struct v4l2_fh *fh, const struct v4l2_event_subscription *sub)
+{
+ struct iris_inst *inst = container_of(fh, struct iris_inst, fh);
+
+ return iris_vdec_subscribe_event(inst, sub);
+}
+
+static int iris_dec_cmd(struct file *filp, void *fh,
+ struct v4l2_decoder_cmd *dec)
+{
+ struct iris_inst *inst = iris_get_inst(filp, NULL);
+ int ret = 0;
+
+ mutex_lock(&inst->lock);
+
+ ret = v4l2_m2m_ioctl_decoder_cmd(filp, fh, dec);
+ if (ret)
+ goto unlock;
+
+ if (inst->state == IRIS_INST_DEINIT)
+ goto unlock;
+
+ if (!iris_allow_cmd(inst, dec->cmd)) {
+ ret = -EBUSY;
+ goto unlock;
+ }
+
+ if (dec->cmd == V4L2_DEC_CMD_START)
+ ret = iris_vdec_start_cmd(inst);
+ else if (dec->cmd == V4L2_DEC_CMD_STOP)
+ ret = iris_vdec_stop_cmd(inst);
+ else
+ ret = -EINVAL;
+
+unlock:
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
+static struct v4l2_file_operations iris_v4l2_file_ops = {
+ .owner = THIS_MODULE,
+ .open = iris_open,
+ .release = iris_close,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = v4l2_m2m_fop_poll,
+ .mmap = v4l2_m2m_fop_mmap,
+};
+
+static const struct vb2_ops iris_vb2_ops = {
+ .buf_init = iris_vb2_buf_init,
+ .queue_setup = iris_vb2_queue_setup,
+ .start_streaming = iris_vb2_start_streaming,
+ .stop_streaming = iris_vb2_stop_streaming,
+ .buf_prepare = iris_vb2_buf_prepare,
+ .buf_out_validate = iris_vb2_buf_out_validate,
+ .buf_queue = iris_vb2_buf_queue,
+};
+
+static const struct v4l2_ioctl_ops iris_v4l2_ioctl_ops = {
+ .vidioc_enum_fmt_vid_cap = iris_enum_fmt,
+ .vidioc_enum_fmt_vid_out = iris_enum_fmt,
+ .vidioc_try_fmt_vid_cap_mplane = iris_try_fmt_vid_mplane,
+ .vidioc_try_fmt_vid_out_mplane = iris_try_fmt_vid_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = iris_s_fmt_vid_mplane,
+ .vidioc_s_fmt_vid_out_mplane = iris_s_fmt_vid_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = iris_g_fmt_vid_mplane,
+ .vidioc_g_fmt_vid_out_mplane = iris_g_fmt_vid_mplane,
+ .vidioc_enum_framesizes = iris_enum_framesizes,
+ .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs,
+ .vidioc_querybuf = v4l2_m2m_ioctl_querybuf,
+ .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
+ .vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
+ .vidioc_qbuf = v4l2_m2m_ioctl_qbuf,
+ .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
+ .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
+ .vidioc_querycap = iris_querycap,
+ .vidioc_g_selection = iris_g_selection,
+ .vidioc_subscribe_event = iris_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_streamon = v4l2_m2m_ioctl_streamon,
+ .vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
+ .vidioc_try_decoder_cmd = v4l2_m2m_ioctl_try_decoder_cmd,
+ .vidioc_decoder_cmd = iris_dec_cmd,
+};
+
+void iris_init_ops(struct iris_core *core)
+{
+ core->iris_v4l2_file_ops = &iris_v4l2_file_ops;
+ core->iris_vb2_ops = &iris_vb2_ops;
+ core->iris_v4l2_ioctl_ops = &iris_v4l2_ioctl_ops;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.h b/drivers/media/platform/qcom/iris/iris_vidc.h
new file mode 100644
index 000000000000..a26054ff55b5
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vidc.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VIDC_H__
+#define __IRIS_VIDC_H__
+
+struct iris_core;
+
+void iris_init_ops(struct iris_core *core);
+int iris_open(struct file *filp);
+int iris_close(struct file *filp);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c
new file mode 100644
index 000000000000..7cf1bfc352d3
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu2.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+
+static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size)
+{
+ struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 mbs_per_second, mbpf, height, width;
+ unsigned long vpp_freq, vsp_freq;
+ u32 fps = DEFAULT_FPS;
+
+ width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+ height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+
+ mbpf = NUM_MBS_PER_FRAME(height, width);
+ mbs_per_second = mbpf * fps;
+
+ vpp_freq = mbs_per_second * caps->mb_cycles_vpp;
+
+ /* 21 / 20 is overhead factor */
+ vpp_freq += vpp_freq / 20;
+ vsp_freq = mbs_per_second * caps->mb_cycles_vsp;
+
+ /* 10 / 7 is overhead factor */
+ vsp_freq += ((fps * data_size * 8) * 10) / 7;
+
+ return max(vpp_freq, vsp_freq);
+}
+
+const struct vpu_ops iris_vpu2_ops = {
+ .power_off_hw = iris_vpu_power_off_hw,
+ .power_off_controller = iris_vpu_power_off_controller,
+ .calc_freq = iris_vpu2_calc_freq,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu3x.c b/drivers/media/platform/qcom/iris/iris_vpu3x.c
new file mode 100644
index 000000000000..9b7c9a1495ee
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu3x.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/reset.h>
+
+#include "iris_instance.h"
+#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
+#define WRAPPER_TZ_BASE_OFFS 0x000C0000
+#define AON_BASE_OFFS 0x000E0000
+#define AON_MVP_NOC_RESET 0x0001F000
+
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
+#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
+#define REQ_POWER_DOWN_PREP BIT(0)
+#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
+#define WRAPPER_CORE_CLOCK_CONFIG (WRAPPER_BASE_OFFS + 0x88)
+#define CORE_CLK_RUN 0x0
+
+#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
+#define CTL_AXI_CLK_HALT BIT(0)
+#define CTL_CLK_HALT BIT(1)
+
+#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
+#define RESET_HIGH BIT(0)
+
+#define CPU_CS_AHB_BRIDGE_SYNC_RESET (CPU_CS_BASE_OFFS + 0x160)
+#define CORE_BRIDGE_SW_RESET BIT(0)
+#define CORE_BRIDGE_HW_RESET_DISABLE BIT(1)
+
+#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
+#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
+#define MSK_CORE_POWER_ON BIT(1)
+
+#define AON_WRAPPER_MVP_NOC_RESET_REQ (AON_MVP_NOC_RESET + 0x000)
+#define VIDEO_NOC_RESET_REQ (BIT(0) | BIT(1))
+
+#define AON_WRAPPER_MVP_NOC_RESET_ACK (AON_MVP_NOC_RESET + 0x004)
+
+#define VCODEC_SS_IDLE_STATUSN (VCODEC_BASE_OFFS + 0x70)
+
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
+
+#define AON_WRAPPER_MVP_NOC_CORE_SW_RESET (AON_BASE_OFFS + 0x18)
+#define SW_RESET BIT(0)
+#define AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL (AON_BASE_OFFS + 0x20)
+#define NOC_HALT BIT(0)
+#define AON_WRAPPER_SPARE (AON_BASE_OFFS + 0x28)
+
+static bool iris_vpu3x_hw_power_collapsed(struct iris_core *core)
+{
+ u32 value, pwr_status;
+
+ value = readl(core->reg_base + WRAPPER_CORE_POWER_STATUS);
+ pwr_status = value & BIT(1);
+
+ return pwr_status ? false : true;
+}
+
+static void iris_vpu3_power_off_hardware(struct iris_core *core)
+{
+ u32 reg_val = 0, value, i;
+ int ret;
+
+ if (iris_vpu3x_hw_power_collapsed(core))
+ goto disable_power;
+
+ dev_err(core->dev, "video hw is power on\n");
+
+ value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+ if (value)
+ writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
+ ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i,
+ reg_val, reg_val & 0x400000, 2000, 20000);
+ if (ret)
+ goto disable_power;
+ }
+
+ writel(VIDEO_NOC_RESET_REQ, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ reg_val, reg_val & 0x3, 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0x0, core->reg_base + AON_WRAPPER_MVP_NOC_RESET_REQ);
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_RESET_ACK,
+ reg_val, !(reg_val & 0x3), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE,
+ core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+
+disable_power:
+ iris_vpu_power_off_hw(core);
+}
+
+static void iris_vpu33_power_off_hardware(struct iris_core *core)
+{
+ u32 reg_val = 0, value, i;
+ int ret;
+
+ if (iris_vpu3x_hw_power_collapsed(core))
+ goto disable_power;
+
+ dev_err(core->dev, "video hw is power on\n");
+
+ value = readl(core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+ if (value)
+ writel(CORE_CLK_RUN, core->reg_base + WRAPPER_CORE_CLOCK_CONFIG);
+
+ for (i = 0; i < core->iris_platform_data->num_vpp_pipe; i++) {
+ ret = readl_poll_timeout(core->reg_base + VCODEC_SS_IDLE_STATUSN + 4 * i,
+ reg_val, reg_val & 0x400000, 2000, 20000);
+ if (ret)
+ goto disable_power;
+ }
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ reg_val, reg_val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ /* set MNoC to low power, set PD_NOC_QREQ (bit 0) */
+ writel(BIT(0), core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+
+ writel(CORE_BRIDGE_SW_RESET | CORE_BRIDGE_HW_RESET_DISABLE,
+ core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(CORE_BRIDGE_HW_RESET_DISABLE, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+ writel(0x0, core->reg_base + CPU_CS_AHB_BRIDGE_SYNC_RESET);
+
+disable_power:
+ iris_vpu_power_off_hw(core);
+}
+
+static int iris_vpu33_power_off_controller(struct iris_core *core)
+{
+ u32 xo_rst_tbl_size = core->iris_platform_data->controller_rst_tbl_size;
+ u32 clk_rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
+ u32 val = 0;
+ int ret;
+
+ writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
+
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
+ val, val == 0, 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT,
+ core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+ writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+
+ reset_control_bulk_reset(clk_rst_tbl_size, core->resets);
+
+ /* Disable MVP NoC clock */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+ val |= NOC_HALT;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+
+ /* enable MVP NoC reset */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+ val |= SW_RESET;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+
+ /* poll AON spare register bit0 to become zero with 50ms timeout */
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_SPARE,
+ val, (val & BIT(0)) == 0, 1000, 50000);
+ if (ret)
+ goto disable_power;
+
+ /* enable bit(1) to avoid cvp noc xo reset */
+ val = readl(core->reg_base + AON_WRAPPER_SPARE);
+ val |= BIT(1);
+ writel(val, core->reg_base + AON_WRAPPER_SPARE);
+
+ reset_control_bulk_assert(xo_rst_tbl_size, core->controller_resets);
+
+ /* De-assert MVP NoC reset */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+ val &= ~SW_RESET;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_SW_RESET);
+
+ usleep_range(80, 100);
+
+ reset_control_bulk_deassert(xo_rst_tbl_size, core->controller_resets);
+
+ /* reset AON spare register */
+ writel(0, core->reg_base + AON_WRAPPER_SPARE);
+
+ /* Enable MVP NoC clock */
+ val = readl(core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+ val &= ~NOC_HALT;
+ writel(val, core->reg_base + AON_WRAPPER_MVP_NOC_CORE_CLK_CONTROL);
+
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+
+disable_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+
+ return 0;
+}
+
+static u64 iris_vpu3x_calculate_frequency(struct iris_inst *inst, size_t data_size)
+{
+ struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
+ struct v4l2_format *inp_f = inst->fmt_src;
+ u32 height, width, mbs_per_second, mbpf;
+ u64 fw_cycles, fw_vpp_cycles;
+ u64 vsp_cycles, vpp_cycles;
+ u32 fps = DEFAULT_FPS;
+
+ width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
+ height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
+
+ mbpf = NUM_MBS_PER_FRAME(height, width);
+ mbs_per_second = mbpf * fps;
+
+ fw_cycles = fps * caps->mb_cycles_fw;
+ fw_vpp_cycles = fps * caps->mb_cycles_fw_vpp;
+
+ vpp_cycles = mult_frac(mbs_per_second, caps->mb_cycles_vpp, (u32)inst->fw_caps[PIPE].value);
+ /* 21 / 20 is minimum overhead factor */
+ vpp_cycles += max(div_u64(vpp_cycles, 20), fw_vpp_cycles);
+
+ /* 1.059 is multi-pipe overhead */
+ if (inst->fw_caps[PIPE].value > 1)
+ vpp_cycles += div_u64(vpp_cycles * 59, 1000);
+
+ vsp_cycles = fps * data_size * 8;
+ vsp_cycles = div_u64(vsp_cycles, 2);
+ /* VSP FW overhead 1.05 */
+ vsp_cycles = div_u64(vsp_cycles * 21, 20);
+
+ if (inst->fw_caps[STAGE].value == STAGE_1)
+ vsp_cycles = vsp_cycles * 3;
+
+ return max3(vpp_cycles, vsp_cycles, fw_cycles);
+}
+
+const struct vpu_ops iris_vpu3_ops = {
+ .power_off_hw = iris_vpu3_power_off_hardware,
+ .power_off_controller = iris_vpu_power_off_controller,
+ .calc_freq = iris_vpu3x_calculate_frequency,
+};
+
+const struct vpu_ops iris_vpu33_ops = {
+ .power_off_hw = iris_vpu33_power_off_hardware,
+ .power_off_controller = iris_vpu33_power_off_controller,
+ .calc_freq = iris_vpu3x_calculate_frequency,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
new file mode 100644
index 000000000000..dce25e410d80
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_instance.h"
+#include "iris_vpu_buffer.h"
+
+static u32 size_h264d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 size_yuv, size_bin_hdr, size_bin_res;
+
+ size_yuv = ((frame_width * frame_height) <= BIN_BUFFER_THRESHOLD) ?
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1) :
+ ((frame_width * frame_height * 3) >> 1);
+ size_bin_hdr = size_yuv * H264_CABAC_HDR_RATIO_HD_TOT;
+ size_bin_res = size_yuv * H264_CABAC_RES_RATIO_HD_TOT;
+ size_bin_hdr = ALIGN(size_bin_hdr / num_vpp_pipes,
+ DMA_ALIGNMENT) * num_vpp_pipes;
+ size_bin_res = ALIGN(size_bin_res / num_vpp_pipes,
+ DMA_ALIGNMENT) * num_vpp_pipes;
+
+ return size_bin_hdr + size_bin_res;
+}
+
+static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 n_aligned_h = ALIGN(frame_height, 16);
+ u32 n_aligned_w = ALIGN(frame_width, 16);
+
+ return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
+}
+
+static u32 hfi_buffer_comv_h264d(u32 frame_width, u32 frame_height, u32 _comv_bufcount)
+{
+ u32 frame_height_in_mbs = DIV_ROUND_UP(frame_height, 16);
+ u32 frame_width_in_mbs = DIV_ROUND_UP(frame_width, 16);
+ u32 col_zero_aligned_width = (frame_width_in_mbs << 2);
+ u32 col_mv_aligned_width = (frame_width_in_mbs << 7);
+ u32 col_zero_size, size_colloc;
+
+ col_mv_aligned_width = ALIGN(col_mv_aligned_width, 16);
+ col_zero_aligned_width = ALIGN(col_zero_aligned_width, 16);
+ col_zero_size = col_zero_aligned_width *
+ ((frame_height_in_mbs + 1) >> 1);
+ col_zero_size = ALIGN(col_zero_size, 64);
+ col_zero_size <<= 1;
+ col_zero_size = ALIGN(col_zero_size, 512);
+ size_colloc = col_mv_aligned_width * ((frame_height_in_mbs + 1) >> 1);
+ size_colloc = ALIGN(size_colloc, 64);
+ size_colloc <<= 1;
+ size_colloc = ALIGN(size_colloc, 512);
+ size_colloc += (col_zero_size + SIZE_H264D_BUFTAB_T * 2);
+
+ return (size_colloc * (_comv_bufcount)) + 512;
+}
+
+static u32 size_h264d_bse_cmd_buf(u32 frame_height)
+{
+ u32 height = ALIGN(frame_height, 32);
+
+ return min_t(u32, (DIV_ROUND_UP(height, 16) * 48), H264D_MAX_SLICE) *
+ SIZE_H264D_BSE_CMD_PER_BUF;
+}
+
+static u32 size_h264d_vpp_cmd_buf(u32 frame_height)
+{
+ u32 size, height = ALIGN(frame_height, 32);
+
+ size = min_t(u32, (DIV_ROUND_UP(height, 16) * 48), H264D_MAX_SLICE) *
+ SIZE_H264D_VPP_CMD_PER_BUF;
+
+ return size > VPP_CMD_MAX_SIZE ? VPP_CMD_MAX_SIZE : size;
+}
+
+static u32 hfi_buffer_persist_h264d(void)
+{
+ return ALIGN(SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264 +
+ H264_DISPLAY_BUF_SIZE * H264_NUM_FRM_INFO +
+ NUM_HW_PIC_BUF * SIZE_SEI_USERDATA,
+ DMA_ALIGNMENT);
+}
+
+static u32 hfi_buffer_non_comv_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 size_bse, size_vpp, size;
+
+ size_bse = size_h264d_bse_cmd_buf(frame_height);
+ size_vpp = size_h264d_vpp_cmd_buf(frame_height);
+ size = ALIGN(size_bse, DMA_ALIGNMENT) +
+ ALIGN(size_vpp, DMA_ALIGNMENT) +
+ ALIGN(SIZE_HW_PIC(SIZE_H264D_HW_PIC_T), DMA_ALIGNMENT);
+
+ return ALIGN(size, DMA_ALIGNMENT);
+}
+
+static u32 size_vpss_lb(u32 frame_width, u32 frame_height)
+{
+ u32 opb_lb_wr_llb_y_buffer_size, opb_lb_wr_llb_uv_buffer_size;
+ u32 opb_wr_top_line_chroma_buffer_size;
+ u32 opb_wr_top_line_luma_buffer_size;
+ u32 macrotiling_size = 32;
+
+ opb_wr_top_line_luma_buffer_size =
+ ALIGN(frame_width, macrotiling_size) / macrotiling_size * 256;
+ opb_wr_top_line_luma_buffer_size =
+ ALIGN(opb_wr_top_line_luma_buffer_size, DMA_ALIGNMENT) +
+ (MAX_TILE_COLUMNS - 1) * 256;
+ opb_wr_top_line_luma_buffer_size =
+ max_t(u32, opb_wr_top_line_luma_buffer_size, (32 * ALIGN(frame_height, 8)));
+ opb_wr_top_line_chroma_buffer_size = opb_wr_top_line_luma_buffer_size;
+ opb_lb_wr_llb_uv_buffer_size =
+ ALIGN((ALIGN(frame_height, 8) / (4 / 2)) * 64, 32);
+ opb_lb_wr_llb_y_buffer_size =
+ ALIGN((ALIGN(frame_height, 8) / (4 / 2)) * 64, 32);
+ return opb_wr_top_line_luma_buffer_size +
+ opb_wr_top_line_chroma_buffer_size +
+ opb_lb_wr_llb_uv_buffer_size +
+ opb_lb_wr_llb_y_buffer_size;
+}
+
+static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height,
+ bool is_opb, u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0;
+ u32 size;
+
+ size = ALIGN(size_h264d_lb_fe_top_data(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_fe_top_ctrl(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_fe_left_ctrl(frame_height), DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h264d_lb_se_top_ctrl(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_se_left_ctrl(frame_height), DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h264d_lb_pe_top_data(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_vsp_top(frame_width), DMA_ALIGNMENT) +
+ ALIGN(size_h264d_lb_recon_dma_metadata_wr(frame_height), DMA_ALIGNMENT) * 2 +
+ ALIGN(size_h264d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+ size = ALIGN(size, DMA_ALIGNMENT);
+ if (is_opb)
+ vpss_lb_size = size_vpss_lb(frame_width, frame_height);
+
+ return ALIGN((size + vpss_lb_size), DMA_ALIGNMENT);
+}
+
+static u32 iris_vpu_dec_bin_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_buffer_bin_h264d(width, height, num_vpp_pipes);
+}
+
+static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
+{
+ u32 num_comv = VIDEO_MAX_FRAME;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_buffer_comv_h264d(width, height, num_comv);
+}
+
+static u32 iris_vpu_dec_persist_size(struct iris_inst *inst)
+{
+ return hfi_buffer_persist_h264d();
+}
+
+static u32 iris_vpu_dec_dpb_size(struct iris_inst *inst)
+{
+ if (iris_split_mode_enabled(inst))
+ return iris_get_buffer_size(inst, BUF_DPB);
+ else
+ return 0;
+}
+
+static u32 iris_vpu_dec_non_comv_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ return hfi_buffer_non_comv_h264d(width, height, num_vpp_pipes);
+}
+
+static u32 iris_vpu_dec_line_size(struct iris_inst *inst)
+{
+ u32 num_vpp_pipes = inst->core->iris_platform_data->num_vpp_pipe;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+ bool is_opb = false;
+
+ if (iris_split_mode_enabled(inst))
+ is_opb = true;
+
+ return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes);
+}
+
+static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst)
+{
+ return iris_vpu_dec_comv_size(inst) +
+ iris_vpu_dec_non_comv_size(inst) +
+ iris_vpu_dec_line_size(inst);
+}
+
+struct iris_vpu_buf_type_handle {
+ enum iris_buffer_type type;
+ u32 (*handle)(struct iris_inst *inst);
+};
+
+int iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
+{
+ const struct iris_vpu_buf_type_handle *buf_type_handle_arr;
+ u32 size = 0, buf_type_handle_size, i;
+
+ static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = {
+ {BUF_BIN, iris_vpu_dec_bin_size },
+ {BUF_COMV, iris_vpu_dec_comv_size },
+ {BUF_NON_COMV, iris_vpu_dec_non_comv_size },
+ {BUF_LINE, iris_vpu_dec_line_size },
+ {BUF_PERSIST, iris_vpu_dec_persist_size },
+ {BUF_DPB, iris_vpu_dec_dpb_size },
+ {BUF_SCRATCH_1, iris_vpu_dec_scratch1_size },
+ };
+
+ buf_type_handle_size = ARRAY_SIZE(dec_internal_buf_type_handle);
+ buf_type_handle_arr = dec_internal_buf_type_handle;
+
+ for (i = 0; i < buf_type_handle_size; i++) {
+ if (buf_type_handle_arr[i].type == buffer_type) {
+ size = buf_type_handle_arr[i].handle(inst);
+ break;
+ }
+ }
+
+ return size;
+}
+
+static inline int iris_vpu_dpb_count(struct iris_inst *inst)
+{
+ if (iris_split_mode_enabled(inst)) {
+ return inst->fw_min_count ?
+ inst->fw_min_count : inst->buffers[BUF_OUTPUT].min_count;
+ }
+
+ return 0;
+}
+
+int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type)
+{
+ switch (buffer_type) {
+ case BUF_INPUT:
+ return MIN_BUFFERS;
+ case BUF_OUTPUT:
+ return inst->fw_min_count;
+ case BUF_BIN:
+ case BUF_COMV:
+ case BUF_NON_COMV:
+ case BUF_LINE:
+ case BUF_PERSIST:
+ case BUF_SCRATCH_1:
+ return 1; /* internal buffer count needed by firmware is 1 */
+ case BUF_DPB:
+ return iris_vpu_dpb_count(inst);
+ default:
+ return 0;
+ }
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
new file mode 100644
index 000000000000..62af6ea6ba1f
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VPU_BUFFER_H__
+#define __IRIS_VPU_BUFFER_H__
+
+struct iris_inst;
+
+#define MIN_BUFFERS 4
+
+#define DMA_ALIGNMENT 256
+
+#define NUM_HW_PIC_BUF 32
+#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf))
+
+#define MAX_TILE_COLUMNS 32
+#define BIN_BUFFER_THRESHOLD (1280 * 736)
+#define VPP_CMD_MAX_SIZE (BIT(20))
+#define H264D_MAX_SLICE 1800
+
+#define SIZE_H264D_BUFTAB_T 256
+#define SIZE_H264D_BSE_CMD_PER_BUF (32 * 4)
+#define SIZE_H264D_VPP_CMD_PER_BUF 512
+
+#define NUM_SLIST_BUF_H264 (256 + 32)
+#define SIZE_SLIST_BUF_H264 512
+#define H264_DISPLAY_BUF_SIZE 3328
+#define H264_NUM_FRM_INFO 66
+
+#define SIZE_SEI_USERDATA 4096
+
+#define H264_CABAC_HDR_RATIO_HD_TOT 1
+#define H264_CABAC_RES_RATIO_HD_TOT 3
+#define SIZE_H264D_HW_PIC_T (BIT(11))
+
+#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
+#define MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 16
+#define MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE 384
+#define MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE 640
+
+static inline u32 size_h264d_lb_fe_top_data(u32 frame_width)
+{
+ return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE * ALIGN(frame_width, 16) * 3;
+}
+
+static inline u32 size_h264d_lb_fe_top_ctrl(u32 frame_width)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
+}
+
+static inline u32 size_h264d_lb_fe_left_ctrl(u32 frame_height)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_height, 16);
+}
+
+static inline u32 size_h264d_lb_se_top_ctrl(u32 frame_width)
+{
+ return MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
+}
+
+static inline u32 size_h264d_lb_se_left_ctrl(u32 frame_height)
+{
+ return MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_height, 16);
+}
+
+static inline u32 size_h264d_lb_pe_top_data(u32 frame_width)
+{
+ return MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE * DIV_ROUND_UP(frame_width, 16);
+}
+
+static inline u32 size_h264d_lb_vsp_top(u32 frame_width)
+{
+ return (DIV_ROUND_UP(frame_width, 16) << 7);
+}
+
+static inline u32 size_h264d_lb_recon_dma_metadata_wr(u32 frame_height)
+{
+ return ALIGN(frame_height, 16) * 32;
+}
+
+static inline u32 size_h264d_qp(u32 frame_width, u32 frame_height)
+{
+ return DIV_ROUND_UP(frame_width, 64) * DIV_ROUND_UP(frame_height, 64) * 128;
+}
+
+int iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c
new file mode 100644
index 000000000000..268e45acaa7c
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/iopoll.h>
+#include <linux/pm_opp.h>
+#include <linux/reset.h>
+
+#include "iris_core.h"
+#include "iris_vpu_common.h"
+#include "iris_vpu_register_defines.h"
+
+#define WRAPPER_TZ_BASE_OFFS 0x000C0000
+#define AON_BASE_OFFS 0x000E0000
+
+#define CPU_IC_BASE_OFFS (CPU_BASE_OFFS)
+
+#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE_OFFS + 0x1C)
+#define CLEAR_XTENSA2HOST_INTR BIT(0)
+
+#define CTRL_INIT (CPU_CS_BASE_OFFS + 0x48)
+#define CTRL_STATUS (CPU_CS_BASE_OFFS + 0x4C)
+
+#define CTRL_INIT_IDLE_MSG_BMSK 0x40000000
+#define CTRL_ERROR_STATUS__M 0xfe
+#define CTRL_STATUS_PC_READY 0x100
+
+#define QTBL_INFO (CPU_CS_BASE_OFFS + 0x50)
+#define QTBL_ENABLE BIT(0)
+
+#define QTBL_ADDR (CPU_CS_BASE_OFFS + 0x54)
+#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE_OFFS + 0x58)
+#define SFR_ADDR (CPU_CS_BASE_OFFS + 0x5C)
+#define UC_REGION_ADDR (CPU_CS_BASE_OFFS + 0x64)
+#define UC_REGION_SIZE (CPU_CS_BASE_OFFS + 0x68)
+
+#define CPU_CS_H2XSOFTINTEN (CPU_CS_BASE_OFFS + 0x148)
+#define HOST2XTENSA_INTR_ENABLE BIT(0)
+
+#define CPU_CS_X2RPMH (CPU_CS_BASE_OFFS + 0x168)
+#define MSK_SIGNAL_FROM_TENSILICA BIT(0)
+#define MSK_CORE_POWER_ON BIT(1)
+
+#define CPU_IC_SOFTINT (CPU_IC_BASE_OFFS + 0x150)
+#define CPU_IC_SOFTINT_H2A_SHFT 0x0
+
+#define WRAPPER_INTR_STATUS (WRAPPER_BASE_OFFS + 0x0C)
+#define WRAPPER_INTR_STATUS_A2HWD_BMSK BIT(3)
+#define WRAPPER_INTR_STATUS_A2H_BMSK BIT(2)
+
+#define WRAPPER_INTR_MASK (WRAPPER_BASE_OFFS + 0x10)
+#define WRAPPER_INTR_MASK_A2HWD_BMSK BIT(3)
+#define WRAPPER_INTR_MASK_A2HCPU_BMSK BIT(2)
+
+#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x54)
+#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS (WRAPPER_BASE_OFFS + 0x58)
+#define WRAPPER_IRIS_CPU_NOC_LPI_CONTROL (WRAPPER_BASE_OFFS + 0x5C)
+#define WRAPPER_IRIS_CPU_NOC_LPI_STATUS (WRAPPER_BASE_OFFS + 0x60)
+
+#define WRAPPER_TZ_CPU_STATUS (WRAPPER_TZ_BASE_OFFS + 0x10)
+#define WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG (WRAPPER_TZ_BASE_OFFS + 0x14)
+#define CTL_AXI_CLK_HALT BIT(0)
+#define CTL_CLK_HALT BIT(1)
+
+#define WRAPPER_TZ_QNS4PDXFIFO_RESET (WRAPPER_TZ_BASE_OFFS + 0x18)
+#define RESET_HIGH BIT(0)
+
+#define AON_WRAPPER_MVP_NOC_LPI_CONTROL (AON_BASE_OFFS)
+#define REQ_POWER_DOWN_PREP BIT(0)
+
+#define AON_WRAPPER_MVP_NOC_LPI_STATUS (AON_BASE_OFFS + 0x4)
+
+static void iris_vpu_interrupt_init(struct iris_core *core)
+{
+ u32 mask_val;
+
+ mask_val = readl(core->reg_base + WRAPPER_INTR_MASK);
+ mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BMSK |
+ WRAPPER_INTR_MASK_A2HCPU_BMSK);
+ writel(mask_val, core->reg_base + WRAPPER_INTR_MASK);
+}
+
+static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core)
+{
+ u32 queue_size, value;
+
+ /* Iris hardware requires 4K queue alignment */
+ queue_size = ALIGN(sizeof(struct iris_hfi_queue_table_header) +
+ (IFACEQ_QUEUE_SIZE * IFACEQ_NUMQ), SZ_4K);
+
+ value = (u32)core->iface_q_table_daddr;
+ writel(value, core->reg_base + UC_REGION_ADDR);
+
+ /* Iris hardware requires 1M queue alignment */
+ value = ALIGN(SFR_SIZE + queue_size, SZ_1M);
+ writel(value, core->reg_base + UC_REGION_SIZE);
+
+ value = (u32)core->iface_q_table_daddr;
+ writel(value, core->reg_base + QTBL_ADDR);
+
+ writel(QTBL_ENABLE, core->reg_base + QTBL_INFO);
+
+ if (core->sfr_daddr) {
+ value = (u32)core->sfr_daddr + core->iris_platform_data->core_arch;
+ writel(value, core->reg_base + SFR_ADDR);
+ }
+}
+
+int iris_vpu_boot_firmware(struct iris_core *core)
+{
+ u32 ctrl_init = BIT(0), ctrl_status = 0, count = 0, max_tries = 1000;
+
+ iris_vpu_setup_ucregion_memory_map(core);
+
+ writel(ctrl_init, core->reg_base + CTRL_INIT);
+ writel(0x1, core->reg_base + CPU_CS_SCIACMDARG3);
+
+ while (!ctrl_status && count < max_tries) {
+ ctrl_status = readl(core->reg_base + CTRL_STATUS);
+ if ((ctrl_status & CTRL_ERROR_STATUS__M) == 0x4) {
+ dev_err(core->dev, "invalid setting for uc_region\n");
+ break;
+ }
+
+ usleep_range(50, 100);
+ count++;
+ }
+
+ if (count >= max_tries) {
+ dev_err(core->dev, "error booting up iris firmware\n");
+ return -ETIME;
+ }
+
+ writel(HOST2XTENSA_INTR_ENABLE, core->reg_base + CPU_CS_H2XSOFTINTEN);
+ writel(0x0, core->reg_base + CPU_CS_X2RPMH);
+
+ return 0;
+}
+
+void iris_vpu_raise_interrupt(struct iris_core *core)
+{
+ writel(1 << CPU_IC_SOFTINT_H2A_SHFT, core->reg_base + CPU_IC_SOFTINT);
+}
+
+void iris_vpu_clear_interrupt(struct iris_core *core)
+{
+ u32 intr_status, mask;
+
+ intr_status = readl(core->reg_base + WRAPPER_INTR_STATUS);
+ mask = (WRAPPER_INTR_STATUS_A2H_BMSK |
+ WRAPPER_INTR_STATUS_A2HWD_BMSK |
+ CTRL_INIT_IDLE_MSG_BMSK);
+
+ if (intr_status & mask)
+ core->intr_status |= intr_status;
+
+ writel(CLEAR_XTENSA2HOST_INTR, core->reg_base + CPU_CS_A2HSOFTINTCLR);
+}
+
+int iris_vpu_watchdog(struct iris_core *core, u32 intr_status)
+{
+ if (intr_status & WRAPPER_INTR_STATUS_A2HWD_BMSK) {
+ dev_err(core->dev, "received watchdog interrupt\n");
+ return -ETIME;
+ }
+
+ return 0;
+}
+
+int iris_vpu_prepare_pc(struct iris_core *core)
+{
+ u32 wfi_status, idle_status, pc_ready;
+ u32 ctrl_status, val = 0;
+ int ret;
+
+ ctrl_status = readl(core->reg_base + CTRL_STATUS);
+ pc_ready = ctrl_status & CTRL_STATUS_PC_READY;
+ idle_status = ctrl_status & BIT(30);
+ if (pc_ready)
+ return 0;
+
+ wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
+ wfi_status &= BIT(0);
+ if (!wfi_status || !idle_status)
+ goto skip_power_off;
+
+ ret = core->hfi_ops->sys_pc_prep(core);
+ if (ret)
+ goto skip_power_off;
+
+ ret = readl_poll_timeout(core->reg_base + CTRL_STATUS, val,
+ val & CTRL_STATUS_PC_READY, 250, 2500);
+ if (ret)
+ goto skip_power_off;
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_TZ_CPU_STATUS,
+ val, val & BIT(0), 250, 2500);
+ if (ret)
+ goto skip_power_off;
+
+ return 0;
+
+skip_power_off:
+ ctrl_status = readl(core->reg_base + CTRL_STATUS);
+ wfi_status = readl(core->reg_base + WRAPPER_TZ_CPU_STATUS);
+ wfi_status &= BIT(0);
+ dev_err(core->dev, "skip power collapse, wfi=%#x, idle=%#x, pcr=%#x, ctrl=%#x)\n",
+ wfi_status, idle_status, pc_ready, ctrl_status);
+
+ return -EAGAIN;
+}
+
+int iris_vpu_power_off_controller(struct iris_core *core)
+{
+ u32 val = 0;
+ int ret;
+
+ writel(MSK_SIGNAL_FROM_TENSILICA | MSK_CORE_POWER_ON, core->reg_base + CPU_CS_X2RPMH);
+
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + AON_WRAPPER_MVP_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(REQ_POWER_DOWN_PREP, core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_IRIS_CPU_NOC_LPI_STATUS,
+ val, val & BIT(0), 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(0x0, core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL);
+
+ ret = readl_poll_timeout(core->reg_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS,
+ val, val == 0, 200, 2000);
+ if (ret)
+ goto disable_power;
+
+ writel(CTL_AXI_CLK_HALT | CTL_CLK_HALT,
+ core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+ writel(RESET_HIGH, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_QNS4PDXFIFO_RESET);
+ writel(0x0, core->reg_base + WRAPPER_TZ_CTL_AXI_CLOCK_CONFIG);
+
+disable_power:
+ iris_disable_unprepare_clock(core, IRIS_CTRL_CLK);
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ return 0;
+}
+
+void iris_vpu_power_off_hw(struct iris_core *core)
+{
+ dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false);
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+}
+
+void iris_vpu_power_off(struct iris_core *core)
+{
+ dev_pm_opp_set_rate(core->dev, 0);
+ core->iris_platform_data->vpu_ops->power_off_hw(core);
+ core->iris_platform_data->vpu_ops->power_off_controller(core);
+ iris_unset_icc_bw(core);
+
+ if (!iris_vpu_watchdog(core, core->intr_status))
+ disable_irq_nosync(core->irq);
+}
+
+static int iris_vpu_power_on_controller(struct iris_core *core)
+{
+ u32 rst_tbl_size = core->iris_platform_data->clk_rst_tbl_size;
+ int ret;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = reset_control_bulk_reset(rst_tbl_size, core->resets);
+ if (ret)
+ goto err_disable_power;
+
+ ret = iris_prepare_enable_clock(core, IRIS_AXI_CLK);
+ if (ret)
+ goto err_disable_power;
+
+ ret = iris_prepare_enable_clock(core, IRIS_CTRL_CLK);
+ if (ret)
+ goto err_disable_clock;
+
+ return 0;
+
+err_disable_clock:
+ iris_disable_unprepare_clock(core, IRIS_AXI_CLK);
+err_disable_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_CTRL_POWER_DOMAIN]);
+
+ return ret;
+}
+
+static int iris_vpu_power_on_hw(struct iris_core *core)
+{
+ int ret;
+
+ ret = iris_enable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ if (ret)
+ return ret;
+
+ ret = iris_prepare_enable_clock(core, IRIS_HW_CLK);
+ if (ret)
+ goto err_disable_power;
+
+ ret = dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], true);
+ if (ret)
+ goto err_disable_clock;
+
+ return 0;
+
+err_disable_clock:
+ iris_disable_unprepare_clock(core, IRIS_HW_CLK);
+err_disable_power:
+ iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+
+ return ret;
+}
+
+int iris_vpu_power_on(struct iris_core *core)
+{
+ u32 freq;
+ int ret;
+
+ ret = iris_set_icc_bw(core, INT_MAX);
+ if (ret)
+ goto err;
+
+ ret = iris_vpu_power_on_controller(core);
+ if (ret)
+ goto err_unvote_icc;
+
+ ret = iris_vpu_power_on_hw(core);
+ if (ret)
+ goto err_power_off_ctrl;
+
+ freq = core->power.clk_freq ? core->power.clk_freq :
+ (u32)ULONG_MAX;
+
+ dev_pm_opp_set_rate(core->dev, freq);
+
+ core->iris_platform_data->set_preset_registers(core);
+
+ iris_vpu_interrupt_init(core);
+ core->intr_status = 0;
+ enable_irq(core->irq);
+
+ return 0;
+
+err_power_off_ctrl:
+ iris_vpu_power_off_controller(core);
+err_unvote_icc:
+ iris_unset_icc_bw(core);
+err:
+ dev_err(core->dev, "power on failed\n");
+
+ return ret;
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h
new file mode 100644
index 000000000000..93b7fa27be3b
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VPU_COMMON_H__
+#define __IRIS_VPU_COMMON_H__
+
+struct iris_core;
+
+extern const struct vpu_ops iris_vpu2_ops;
+extern const struct vpu_ops iris_vpu3_ops;
+extern const struct vpu_ops iris_vpu33_ops;
+
+struct vpu_ops {
+ void (*power_off_hw)(struct iris_core *core);
+ int (*power_off_controller)(struct iris_core *core);
+ u64 (*calc_freq)(struct iris_inst *inst, size_t data_size);
+};
+
+int iris_vpu_boot_firmware(struct iris_core *core);
+void iris_vpu_raise_interrupt(struct iris_core *core);
+void iris_vpu_clear_interrupt(struct iris_core *core);
+int iris_vpu_watchdog(struct iris_core *core, u32 intr_status);
+int iris_vpu_prepare_pc(struct iris_core *core);
+int iris_vpu_power_on(struct iris_core *core);
+int iris_vpu_power_off_controller(struct iris_core *core);
+void iris_vpu_power_off_hw(struct iris_core *core);
+void iris_vpu_power_off(struct iris_core *core);
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
new file mode 100644
index 000000000000..fe8a39e5e5a3
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_vpu_register_defines.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_VPU_REGISTER_DEFINES_H__
+#define __IRIS_VPU_REGISTER_DEFINES_H__
+
+#define VCODEC_BASE_OFFS 0x00000000
+#define CPU_BASE_OFFS 0x000A0000
+#define WRAPPER_BASE_OFFS 0x000B0000
+
+#define CPU_CS_BASE_OFFS (CPU_BASE_OFFS)
+
+#define WRAPPER_CORE_POWER_STATUS (WRAPPER_BASE_OFFS + 0x80)
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/Kconfig b/drivers/media/platform/qcom/venus/Kconfig
index bfd50e8f3421..ffb731ecd48c 100644
--- a/drivers/media/platform/qcom/venus/Kconfig
+++ b/drivers/media/platform/qcom/venus/Kconfig
@@ -2,7 +2,8 @@ config VIDEO_QCOM_VENUS
tristate "Qualcomm Venus V4L2 encoder/decoder driver"
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV && QCOM_SMEM
- depends on (ARCH_QCOM && IOMMU_DMA) || COMPILE_TEST
+ depends on (ARCH_QCOM && ARM64 && IOMMU_API) || COMPILE_TEST
+ select OF_DYNAMIC if ARCH_QCOM
select QCOM_MDT_LOADER if ARCH_QCOM
select QCOM_SCM
select VIDEOBUF2_DMA_CONTIG
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index ce206b709754..d305d74bb152 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -19,6 +19,7 @@
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-ctrls.h>
#include <media/v4l2-mem2mem.h>
#include <media/v4l2-ioctl.h>
@@ -68,6 +69,7 @@ static void venus_event_notify(struct venus_core *core, u32 event)
mutex_lock(&core->lock);
set_bit(0, &core->sys_error);
+ set_bit(0, &core->dump_core);
list_for_each_entry(inst, &core->instances, list)
inst->ops->event_notify(inst, EVT_SESSION_ERROR, NULL);
mutex_unlock(&core->lock);
@@ -110,7 +112,10 @@ static void venus_sys_error_handler(struct work_struct *work)
venus_shutdown(core);
- venus_coredump(core);
+ if (test_bit(0, &core->dump_core)) {
+ venus_coredump(core);
+ clear_bit(0, &core->dump_core);
+ }
pm_runtime_put_sync(core->dev);
@@ -281,6 +286,89 @@ static irqreturn_t venus_isr_thread(int irq, void *dev_id)
return ret;
}
+#if defined(CONFIG_OF_DYNAMIC)
+static int venus_add_video_core(struct venus_core *core, const char *node_name,
+ const char *compat)
+{
+ struct of_changeset *ocs = core->ocs;
+ struct device *dev = core->dev;
+ struct device_node *np, *enp;
+ int ret;
+
+ if (!node_name)
+ return 0;
+
+ enp = of_find_node_by_name(dev->of_node, node_name);
+ if (enp) {
+ of_node_put(enp);
+ return 0;
+ }
+
+ np = of_changeset_create_node(ocs, dev->of_node, node_name);
+ if (!np) {
+ dev_err(dev, "Unable to create new node\n");
+ return -ENODEV;
+ }
+
+ ret = of_changeset_add_prop_string(ocs, np, "compatible", compat);
+ if (ret)
+ dev_err(dev, "unable to add %s\n", compat);
+
+ of_node_put(np);
+
+ return ret;
+}
+
+static int venus_add_dynamic_nodes(struct venus_core *core)
+{
+ struct device *dev = core->dev;
+ int ret;
+
+ core->ocs = kmalloc(sizeof(*core->ocs), GFP_KERNEL);
+ if (!core->ocs)
+ return -ENOMEM;
+
+ of_changeset_init(core->ocs);
+
+ ret = venus_add_video_core(core, core->res->dec_nodename, "venus-decoder");
+ if (ret)
+ goto err;
+
+ ret = venus_add_video_core(core, core->res->enc_nodename, "venus-encoder");
+ if (ret)
+ goto err;
+
+ ret = of_changeset_apply(core->ocs);
+ if (ret) {
+ dev_err(dev, "applying changeset fail ret %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ of_changeset_destroy(core->ocs);
+ kfree(core->ocs);
+ core->ocs = NULL;
+ return ret;
+}
+
+static void venus_remove_dynamic_nodes(struct venus_core *core)
+{
+ if (core->ocs) {
+ of_changeset_revert(core->ocs);
+ of_changeset_destroy(core->ocs);
+ kfree(core->ocs);
+ }
+}
+#else
+static int venus_add_dynamic_nodes(struct venus_core *core)
+{
+ return 0;
+}
+
+static void venus_remove_dynamic_nodes(struct venus_core *core) {}
+#endif
+
static int venus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -350,7 +438,7 @@ static int venus_probe(struct platform_device *pdev)
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret)
- goto err_core_deinit;
+ goto err_hfi_destroy;
platform_set_drvdata(pdev, core);
@@ -360,9 +448,15 @@ static int venus_probe(struct platform_device *pdev)
if (ret < 0)
goto err_runtime_disable;
+ if (core->res->dec_nodename || core->res->enc_nodename) {
+ ret = venus_add_dynamic_nodes(core);
+ if (ret)
+ goto err_runtime_disable;
+ }
+
ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
if (ret)
- goto err_runtime_disable;
+ goto err_remove_dynamic_nodes;
ret = venus_firmware_init(core);
if (ret)
@@ -382,37 +476,39 @@ static int venus_probe(struct platform_device *pdev)
ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC);
if (ret)
- goto err_venus_shutdown;
+ goto err_core_deinit;
ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC);
if (ret)
- goto err_venus_shutdown;
+ goto err_core_deinit;
ret = pm_runtime_put_sync(dev);
if (ret) {
pm_runtime_get_noresume(dev);
- goto err_dev_unregister;
+ goto err_core_deinit;
}
venus_dbgfs_init(core);
return 0;
-err_dev_unregister:
- v4l2_device_unregister(&core->v4l2_dev);
+err_core_deinit:
+ hfi_core_deinit(core, false);
err_venus_shutdown:
venus_shutdown(core);
err_firmware_deinit:
venus_firmware_deinit(core);
err_of_depopulate:
of_platform_depopulate(dev);
+err_remove_dynamic_nodes:
+ venus_remove_dynamic_nodes(core);
err_runtime_disable:
pm_runtime_put_noidle(dev);
- pm_runtime_set_suspended(dev);
pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ v4l2_device_unregister(&core->v4l2_dev);
+err_hfi_destroy:
hfi_destroy(core);
-err_core_deinit:
- hfi_core_deinit(core, false);
err_core_put:
if (core->pm_ops->core_put)
core->pm_ops->core_put(core);
@@ -426,6 +522,7 @@ static void venus_remove(struct platform_device *pdev)
struct device *dev = core->dev;
int ret;
+ cancel_delayed_work_sync(&core->work);
ret = pm_runtime_get_sync(dev);
WARN_ON(ret < 0);
@@ -437,6 +534,8 @@ static void venus_remove(struct platform_device *pdev)
venus_firmware_deinit(core);
+ venus_remove_dynamic_nodes(core);
+
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -497,6 +596,26 @@ err_cpucfg_path:
return ret;
}
+void venus_close_common(struct venus_inst *inst)
+{
+ /*
+ * Make sure we don't have IRQ/IRQ-thread currently running
+ * or pending execution, which would race with the inst destruction.
+ */
+ synchronize_irq(inst->core->irq);
+
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+ v4l2_m2m_release(inst->m2m_dev);
+ hfi_session_destroy(inst);
+ v4l2_fh_del(&inst->fh);
+ v4l2_fh_exit(&inst->fh);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
+
+ mutex_destroy(&inst->lock);
+ mutex_destroy(&inst->ctx_q_lock);
+}
+EXPORT_SYMBOL_GPL(venus_close_common);
+
static __maybe_unused int venus_runtime_resume(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
@@ -552,6 +671,8 @@ static const struct venus_resources msm8916_res = {
.vmem_addr = 0,
.dma_mask = 0xddc00000 - 1,
.fwname = "qcom/venus-1.8/venus.mbn",
+ .dec_nodename = "video-decoder",
+ .enc_nodename = "video-encoder",
};
static const struct freq_tbl msm8996_freq_table[] = {
@@ -587,6 +708,44 @@ static const struct venus_resources msm8996_res = {
.fwname = "qcom/venus-4.2/venus.mbn",
};
+static const struct freq_tbl msm8998_freq_table[] = {
+ { 1944000, 465000000 }, /* 4k UHD @ 60 (decode only) */
+ { 972000, 465000000 }, /* 4k UHD @ 30 */
+ { 489600, 360000000 }, /* 1080p @ 60 */
+ { 244800, 186000000 }, /* 1080p @ 30 */
+ { 108000, 100000000 }, /* 720p @ 30 */
+};
+
+static const struct reg_val msm8998_reg_preset[] = {
+ { 0x80124, 0x00000003 },
+ { 0x80550, 0x01111111 },
+ { 0x80560, 0x01111111 },
+ { 0x80568, 0x01111111 },
+ { 0x80570, 0x01111111 },
+ { 0x80580, 0x01111111 },
+ { 0x80588, 0x01111111 },
+ { 0xe2010, 0x00000000 },
+};
+
+static const struct venus_resources msm8998_res = {
+ .freq_tbl = msm8998_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(msm8998_freq_table),
+ .reg_tbl = msm8998_reg_preset,
+ .reg_tbl_size = ARRAY_SIZE(msm8998_reg_preset),
+ .clks = { "core", "iface", "bus", "mbus" },
+ .clks_num = 4,
+ .vcodec0_clks = { "core" },
+ .vcodec1_clks = { "core" },
+ .vcodec_clks_num = 1,
+ .max_load = 2563200,
+ .hfi_version = HFI_VERSION_3XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xddc00000 - 1,
+ .fwname = "qcom/venus-4.4/venus.mbn",
+};
+
static const struct freq_tbl sdm660_freq_table[] = {
{ 979200, 518400000 },
{ 489600, 441600000 },
@@ -709,7 +868,7 @@ static const struct venus_resources sdm845_res_v2 = {
.vcodec_clks_num = 2,
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" },
.vcodec_pmdomains_num = 3,
- .opp_pmdomain = (const char *[]) { "cx", NULL },
+ .opp_pmdomain = (const char *[]) { "cx" },
.vcodec_num = 2,
.max_load = 3110400, /* 4096x2160@90 */
.hfi_version = HFI_VERSION_4XX,
@@ -723,6 +882,8 @@ static const struct venus_resources sdm845_res_v2 = {
.cp_nonpixel_start = 0x1000000,
.cp_nonpixel_size = 0x24800000,
.fwname = "qcom/venus-5.2/venus.mbn",
+ .dec_nodename = "video-core0",
+ .enc_nodename = "video-core1",
};
static const struct freq_tbl sc7180_freq_table[] = {
@@ -758,7 +919,7 @@ static const struct venus_resources sc7180_res = {
.vcodec_clks_num = 2,
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
- .opp_pmdomain = (const char *[]) { "cx", NULL },
+ .opp_pmdomain = (const char *[]) { "cx" },
.vcodec_num = 1,
.hfi_version = HFI_VERSION_4XX,
.vpu_version = VPU_VERSION_AR50,
@@ -771,6 +932,8 @@ static const struct venus_resources sc7180_res = {
.cp_nonpixel_start = 0x1000000,
.cp_nonpixel_size = 0x24800000,
.fwname = "qcom/venus-5.4/venus.mbn",
+ .dec_nodename = "video-decoder",
+ .enc_nodename = "video-encoder",
};
static const struct freq_tbl sm8250_freq_table[] = {
@@ -815,7 +978,7 @@ static const struct venus_resources sm8250_res = {
.vcodec_clks_num = 1,
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
- .opp_pmdomain = (const char *[]) { "mx", NULL },
+ .opp_pmdomain = (const char *[]) { "mx" },
.vcodec_num = 1,
.max_load = 7833600,
.hfi_version = HFI_VERSION_6XX,
@@ -826,6 +989,8 @@ static const struct venus_resources sm8250_res = {
.vmem_addr = 0,
.dma_mask = 0xe0000000 - 1,
.fwname = "qcom/vpu-1.0/venus.mbn",
+ .dec_nodename = "video-decoder",
+ .enc_nodename = "video-encoder",
};
static const struct freq_tbl sc7280_freq_table[] = {
@@ -874,7 +1039,7 @@ static const struct venus_resources sc7280_res = {
.vcodec_clks_num = 2,
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
- .opp_pmdomain = (const char *[]) { "cx", NULL },
+ .opp_pmdomain = (const char *[]) { "cx" },
.vcodec_num = 1,
.hfi_version = HFI_VERSION_6XX,
.vpu_version = VPU_VERSION_IRIS2_1,
@@ -888,11 +1053,14 @@ static const struct venus_resources sc7280_res = {
.cp_nonpixel_start = 0x1000000,
.cp_nonpixel_size = 0x24800000,
.fwname = "qcom/vpu-2.0/venus.mbn",
+ .dec_nodename = "video-decoder",
+ .enc_nodename = "video-encoder",
};
static const struct of_device_id venus_dt_match[] = {
{ .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
{ .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
+ { .compatible = "qcom,msm8998-venus", .data = &msm8998_res, },
{ .compatible = "qcom,sdm660-venus", .data = &sdm660_res, },
{ .compatible = "qcom,sdm845-venus", .data = &sdm845_res, },
{ .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, },
@@ -905,7 +1073,7 @@ MODULE_DEVICE_TABLE(of, venus_dt_match);
static struct platform_driver qcom_venus_driver = {
.probe = venus_probe,
- .remove_new = venus_remove,
+ .remove = venus_remove,
.driver = {
.name = "qcom-venus",
.of_match_table = venus_dt_match,
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 6a77de374454..b412e0c5515a 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -26,6 +26,7 @@
#define VIDC_CLKS_NUM_MAX 4
#define VIDC_VCODEC_CLKS_NUM_MAX 2
#define VIDC_RESETS_NUM_MAX 2
+#define VIDC_MAX_HIER_CODING_LAYER 6
extern int venus_fw_debug;
@@ -89,6 +90,8 @@ struct venus_resources {
u32 cp_nonpixel_start;
u32 cp_nonpixel_size;
const char *fwname;
+ const char *enc_nodename;
+ const char *dec_nodename;
};
enum venus_fmt {
@@ -132,9 +135,7 @@ struct venus_format {
* @vcodec1_clks: an array of vcodec1 struct clk pointers
* @video_path: an interconnect handle to video to/from memory path
* @cpucfg_path: an interconnect handle to cpu configuration path
- * @has_opp_table: does OPP table exist
* @pmdomains: a pointer to a list of pmdomains
- * @opp_dl_venus: an device-link for device OPP
* @opp_pmdomain: an OPP power-domain
* @resets: an array of reset signals
* @vdev_dec: a reference to video device structure for decoder instances
@@ -169,6 +170,9 @@ struct venus_format {
* @core1_usage_count: usage counter for core1
* @root: debugfs root directory
* @venus_ver: the venus firmware version
+ * @dump_core: a flag indicating that a core dump is required
+ * @ocs: OF changeset pointer
+ * @hwmode_dev: a flag indicating that HW_CTRL_TRIGGER is used in clock driver
*/
struct venus_core {
void __iomem *base;
@@ -185,10 +189,8 @@ struct venus_core {
struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
struct icc_path *video_path;
struct icc_path *cpucfg_path;
- bool has_opp_table;
struct dev_pm_domain_list *pmdomains;
- struct device_link *opp_dl_venus;
- struct device *opp_pmdomain;
+ struct dev_pm_domain_list *opp_pmdomain;
struct reset_control *resets[VIDC_RESETS_NUM_MAX];
struct video_device *vdev_dec;
struct video_device *vdev_enc;
@@ -232,6 +234,9 @@ struct venus_core {
u32 minor;
u32 rev;
} venus_ver;
+ unsigned long dump_core;
+ struct of_changeset *ocs;
+ bool hwmode_dev;
};
struct vdec_controls {
@@ -253,6 +258,7 @@ struct venc_controls {
u32 rc_enable;
u32 const_quality;
u32 frame_skip_mode;
+ u32 layer_bitrate;
u32 h264_i_period;
u32 h264_entropy_mode;
@@ -271,6 +277,8 @@ struct venc_controls {
s32 h264_loop_filter_alpha;
s32 h264_loop_filter_beta;
u32 h264_8x8_transform;
+ u32 h264_hier_layers;
+ u32 h264_hier_layer_bitrate[VIDC_MAX_HIER_CODING_LAYER];
u32 hevc_i_qp;
u32 hevc_p_qp;
@@ -562,4 +570,6 @@ is_fw_rev_or_older(struct venus_core *core, u32 vmajor, u32 vminor, u32 vrev)
(core)->venus_ver.minor == vminor &&
(core)->venus_ver.rev <= vrev);
}
+
+void venus_close_common(struct venus_inst *inst);
#endif
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index fe7da2b30482..66a18830e66d 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -316,10 +316,10 @@ int venus_firmware_init(struct venus_core *core)
core->fw.dev = &pdev->dev;
- iommu_dom = iommu_domain_alloc(&platform_bus_type);
- if (!iommu_dom) {
+ iommu_dom = iommu_paging_domain_alloc(core->fw.dev);
+ if (IS_ERR(iommu_dom)) {
dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
- ret = -ENOMEM;
+ ret = PTR_ERR(iommu_dom);
goto err_unregister;
}
diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c
index e00aedb41d16..675e6fd1e9fa 100644
--- a/drivers/media/platform/qcom/venus/hfi.c
+++ b/drivers/media/platform/qcom/venus/hfi.c
@@ -138,29 +138,6 @@ int hfi_core_trigger_ssr(struct venus_core *core, u32 type)
return core->ops->core_trigger_ssr(core, type);
}
-int hfi_core_ping(struct venus_core *core)
-{
- int ret;
-
- mutex_lock(&core->lock);
-
- ret = core->ops->core_ping(core, 0xbeef);
- if (ret)
- goto unlock;
-
- ret = wait_for_completion_timeout(&core->done, TIMEOUT);
- if (!ret) {
- ret = -ETIMEDOUT;
- goto unlock;
- }
- ret = 0;
- if (core->error != HFI_ERR_NONE)
- ret = -ENODEV;
-unlock:
- mutex_unlock(&core->lock);
- return ret;
-}
-
static int wait_session_msg(struct venus_inst *inst)
{
int ret;
diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h
index f25d412d6553..0338841d5992 100644
--- a/drivers/media/platform/qcom/venus/hfi.h
+++ b/drivers/media/platform/qcom/venus/hfi.h
@@ -108,7 +108,6 @@ struct hfi_inst_ops {
struct hfi_ops {
int (*core_init)(struct venus_core *core);
int (*core_deinit)(struct venus_core *core);
- int (*core_ping)(struct venus_core *core, u32 cookie);
int (*core_trigger_ssr)(struct venus_core *core, u32 trigger_type);
int (*session_init)(struct venus_inst *inst, u32 session_type,
@@ -152,7 +151,6 @@ int hfi_core_deinit(struct venus_core *core, bool blocking);
int hfi_core_suspend(struct venus_core *core);
int hfi_core_resume(struct venus_core *core, bool force);
int hfi_core_trigger_ssr(struct venus_core *core, u32 type);
-int hfi_core_ping(struct venus_core *core);
int hfi_session_create(struct venus_inst *inst, const struct hfi_inst_ops *ops);
void hfi_session_destroy(struct venus_inst *inst);
int hfi_session_init(struct venus_inst *inst, u32 pixfmt);
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c
index 3418d2dd9371..3ae063094e3e 100644
--- a/drivers/media/platform/qcom/venus/hfi_cmds.c
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.c
@@ -156,7 +156,7 @@ void pkt_sys_image_version(struct hfi_sys_get_property_pkt *pkt)
pkt->hdr.size = sizeof(*pkt);
pkt->hdr.pkt_type = HFI_CMD_SYS_GET_PROPERTY;
pkt->num_properties = 1;
- pkt->data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION;
+ pkt->data = HFI_PROPERTY_SYS_IMAGE_VERSION;
}
int pkt_session_init(struct hfi_session_init_pkt *pkt, void *cookie,
@@ -331,7 +331,7 @@ int pkt_session_ftb(struct hfi_session_fill_buffer_pkt *pkt, void *cookie,
pkt->alloc_len = out_frame->alloc_len;
pkt->filled_len = out_frame->filled_len;
pkt->offset = out_frame->offset;
- pkt->data[0] = out_frame->extradata_size;
+ pkt->data = out_frame->extradata_size;
return 0;
}
@@ -402,7 +402,7 @@ static int pkt_session_get_property_1x(struct hfi_session_get_property_pkt *pkt,
pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_GET_PROPERTY;
pkt->shdr.session_id = hash32_ptr(cookie);
pkt->num_properties = 1;
- pkt->data[0] = ptype;
+ pkt->data = ptype;
return 0;
}
@@ -1110,7 +1110,7 @@ pkt_session_get_property_3xx(struct hfi_session_get_property_pkt *pkt,
switch (ptype) {
case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
- pkt->data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY;
+ pkt->data = HFI_PROPERTY_CONFIG_VDEC_ENTROPY;
break;
default:
ret = pkt_session_get_property_1x(pkt, cookie, ptype);
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.h b/drivers/media/platform/qcom/venus/hfi_cmds.h
index 20acd412ee7b..a83125bc17aa 100644
--- a/drivers/media/platform/qcom/venus/hfi_cmds.h
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.h
@@ -74,7 +74,7 @@ struct hfi_sys_set_property_pkt {
struct hfi_sys_get_property_pkt {
struct hfi_pkt_hdr hdr;
u32 num_properties;
- u32 data[1];
+ u32 data;
};
struct hfi_sys_set_buffers_pkt {
@@ -82,7 +82,7 @@ struct hfi_sys_set_buffers_pkt {
u32 buffer_type;
u32 buffer_size;
u32 num_buffers;
- u32 buffer_addr[1];
+ u32 buffer_addr[];
};
struct hfi_sys_ping_pkt {
@@ -151,7 +151,7 @@ struct hfi_session_empty_buffer_compressed_pkt {
u32 input_tag;
u32 packet_buffer;
u32 extradata_buffer;
- u32 data[1];
+ u32 data;
};
struct hfi_session_empty_buffer_uncompressed_plane0_pkt {
@@ -168,7 +168,7 @@ struct hfi_session_empty_buffer_uncompressed_plane0_pkt {
u32 input_tag;
u32 packet_buffer;
u32 extradata_buffer;
- u32 data[1];
+ u32 data;
};
struct hfi_session_empty_buffer_uncompressed_plane1_pkt {
@@ -177,7 +177,7 @@ struct hfi_session_empty_buffer_uncompressed_plane1_pkt {
u32 filled_len;
u32 offset;
u32 packet_buffer2;
- u32 data[1];
+ u32 data;
};
struct hfi_session_empty_buffer_uncompressed_plane2_pkt {
@@ -186,7 +186,7 @@ struct hfi_session_empty_buffer_uncompressed_plane2_pkt {
u32 filled_len;
u32 offset;
u32 packet_buffer3;
- u32 data[1];
+ u32 data;
};
struct hfi_session_fill_buffer_pkt {
@@ -198,7 +198,7 @@ struct hfi_session_fill_buffer_pkt {
u32 output_tag;
u32 packet_buffer;
u32 extradata_buffer;
- u32 data[1];
+ u32 data;
};
struct hfi_session_flush_pkt {
@@ -217,7 +217,7 @@ struct hfi_session_resume_pkt {
struct hfi_session_get_property_pkt {
struct hfi_session_hdr_pkt shdr;
u32 num_properties;
- u32 data[1];
+ u32 data;
};
struct hfi_session_release_buffer_pkt {
@@ -227,7 +227,7 @@ struct hfi_session_release_buffer_pkt {
u32 extradata_size;
u32 response_req;
u32 num_buffers;
- u32 buffer_info[1];
+ u32 buffer_info[] __counted_by(num_buffers);
};
struct hfi_session_release_resources_pkt {
diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
index e4c05d62cfc7..f44059f19505 100644
--- a/drivers/media/platform/qcom/venus/hfi_helper.h
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -761,7 +761,7 @@ struct hfi_multi_stream_3x {
struct hfi_multi_view_format {
u32 views;
- u32 view_order[1];
+ u32 view_order[];
};
#define HFI_MULTI_SLICE_OFF 0x1
@@ -1005,13 +1005,13 @@ struct hfi_uncompressed_plane_constraints {
struct hfi_uncompressed_plane_info {
u32 format;
u32 num_planes;
- struct hfi_uncompressed_plane_constraints plane_constraints[1];
+ struct hfi_uncompressed_plane_constraints plane_constraints;
};
struct hfi_uncompressed_format_supported {
u32 buffer_type;
u32 format_entries;
- struct hfi_uncompressed_plane_info plane_info[1];
+ struct hfi_uncompressed_plane_info plane_info;
};
struct hfi_uncompressed_plane_actual {
@@ -1038,7 +1038,7 @@ struct hfi_codec_supported {
struct hfi_properties_supported {
u32 num_properties;
- u32 properties[1];
+ u32 properties[];
};
struct hfi_max_sessions_supported {
@@ -1085,12 +1085,12 @@ struct hfi_resource_ocmem_requirement {
struct hfi_resource_ocmem_requirement_info {
u32 num_entries;
- struct hfi_resource_ocmem_requirement requirements[1];
+ struct hfi_resource_ocmem_requirement requirements[];
};
struct hfi_property_sys_image_version_info_type {
u32 string_size;
- u8 str_image_version[1];
+ u8 str_image_version[];
};
struct hfi_codec_mask_supported {
@@ -1141,7 +1141,7 @@ struct hfi_extradata_header {
u32 port_index;
u32 type;
u32 data_size;
- u8 data[1];
+ u8 data[];
};
struct hfi_batch_info {
@@ -1236,7 +1236,7 @@ static inline void hfi_bufreq_set_count_min_host(struct hfi_buffer_requirements
struct hfi_data_payload {
u32 size;
- u8 data[1];
+ u8 data[];
};
struct hfi_enable_picture {
@@ -1264,12 +1264,12 @@ struct hfi_interlace_format_supported {
struct hfi_buffer_alloc_mode_supported {
u32 buffer_type;
u32 num_entries;
- u32 data[1];
+ u32 data[];
};
struct hfi_mb_error_map {
u32 error_map_size;
- u8 error_map[1];
+ u8 error_map[];
};
struct hfi_metadata_pass_through {
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index c43839539d4d..1b3db2caa99f 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -19,6 +19,8 @@ static void init_codecs(struct venus_core *core)
struct hfi_plat_caps *caps = core->caps, *cap;
unsigned long bit;
+ core->codecs_count = 0;
+
if (hweight_long(core->dec_codecs) + hweight_long(core->enc_codecs) > MAX_CODEC_NUM)
return;
@@ -62,7 +64,7 @@ fill_buf_mode(struct hfi_plat_caps *cap, const void *data, unsigned int num)
cap->cap_bufs_mode_dynamic = true;
}
-static void
+static int
parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_buffer_alloc_mode_supported *mode = data;
@@ -70,7 +72,7 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
u32 *type;
if (num_entries > MAX_ALLOC_MODE_ENTRIES)
- return;
+ return -EINVAL;
type = mode->data;
@@ -82,6 +84,8 @@ parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
type++;
}
+
+ return sizeof(*mode);
}
static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
@@ -96,7 +100,7 @@ static void fill_profile_level(struct hfi_plat_caps *cap, const void *data,
cap->num_pl += num;
}
-static void
+static int
parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_profile_level_supported *pl = data;
@@ -104,12 +108,14 @@ parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data)
struct hfi_profile_level pl_arr[HFI_MAX_PROFILE_COUNT] = {};
if (pl->profile_count > HFI_MAX_PROFILE_COUNT)
- return;
+ return -EINVAL;
memcpy(pl_arr, proflevel, pl->profile_count * sizeof(*proflevel));
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_profile_level, pl_arr, pl->profile_count);
+
+ return pl->profile_count * sizeof(*proflevel) + sizeof(u32);
}
static void
@@ -124,7 +130,7 @@ fill_caps(struct hfi_plat_caps *cap, const void *data, unsigned int num)
cap->num_caps += num;
}
-static void
+static int
parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_capabilities *caps = data;
@@ -133,12 +139,14 @@ parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
struct hfi_capability caps_arr[MAX_CAP_ENTRIES] = {};
if (num_caps > MAX_CAP_ENTRIES)
- return;
+ return -EINVAL;
memcpy(caps_arr, cap, num_caps * sizeof(*cap));
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_caps, caps_arr, num_caps);
+
+ return sizeof(*caps);
}
static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
@@ -153,16 +161,17 @@ static void fill_raw_fmts(struct hfi_plat_caps *cap, const void *fmts,
cap->num_fmts += num_fmts;
}
-static void
+static int
parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
{
struct hfi_uncompressed_format_supported *fmt = data;
- struct hfi_uncompressed_plane_info *pinfo = fmt->plane_info;
+ struct hfi_uncompressed_plane_info *pinfo = &fmt->plane_info;
struct hfi_uncompressed_plane_constraints *constr;
struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {};
u32 entries = fmt->format_entries;
unsigned int i = 0;
- u32 num_planes;
+ u32 num_planes = 0;
+ u32 size;
while (entries) {
num_planes = pinfo->num_planes;
@@ -172,7 +181,7 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
i++;
if (i >= MAX_FMT_ENTRIES)
- return;
+ return -EINVAL;
if (pinfo->num_planes > MAX_PLANES)
break;
@@ -184,9 +193,13 @@ parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
fill_raw_fmts, rawfmts, i);
+ size = fmt->format_entries * (sizeof(*constr) * num_planes + 2 * sizeof(u32))
+ + 2 * sizeof(u32);
+
+ return size;
}
-static void parse_codecs(struct venus_core *core, void *data)
+static int parse_codecs(struct venus_core *core, void *data)
{
struct hfi_codec_supported *codecs = data;
@@ -198,21 +211,27 @@ static void parse_codecs(struct venus_core *core, void *data)
core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK;
core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC;
}
+
+ return sizeof(*codecs);
}
-static void parse_max_sessions(struct venus_core *core, const void *data)
+static int parse_max_sessions(struct venus_core *core, const void *data)
{
const struct hfi_max_sessions_supported *sessions = data;
core->max_sessions_supported = sessions->max_sessions;
+
+ return sizeof(*sessions);
}
-static void parse_codecs_mask(u32 *codecs, u32 *domain, void *data)
+static int parse_codecs_mask(u32 *codecs, u32 *domain, void *data)
{
struct hfi_codec_mask_supported *mask = data;
*codecs = mask->codecs;
*domain = mask->video_domains;
+
+ return sizeof(*mask);
}
static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain)
@@ -281,8 +300,9 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst)
u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
u32 size)
{
- unsigned int words_count = size >> 2;
- u32 *word = buf, *data, codecs = 0, domain = 0;
+ u32 *words = buf, *payload, codecs = 0, domain = 0;
+ u32 *frame_size = buf + size;
+ u32 rem_bytes = size;
int ret;
ret = hfi_platform_parser(core, inst);
@@ -299,38 +319,66 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
memset(core->caps, 0, sizeof(core->caps));
}
- while (words_count) {
- data = word + 1;
+ while (words < frame_size) {
+ payload = words + 1;
- switch (*word) {
+ switch (*words) {
case HFI_PROPERTY_PARAM_CODEC_SUPPORTED:
- parse_codecs(core, data);
+ if (rem_bytes <= sizeof(struct hfi_codec_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_codecs(core, payload);
+ if (ret < 0)
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
init_codecs(core);
break;
case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED:
- parse_max_sessions(core, data);
+ if (rem_bytes <= sizeof(struct hfi_max_sessions_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_max_sessions(core, payload);
break;
case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED:
- parse_codecs_mask(&codecs, &domain, data);
+ if (rem_bytes <= sizeof(struct hfi_codec_mask_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_codecs_mask(&codecs, &domain, payload);
break;
case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
- parse_raw_formats(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_uncompressed_format_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_raw_formats(core, codecs, domain, payload);
break;
case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED:
- parse_caps(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_capabilities))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_caps(core, codecs, domain, payload);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED:
- parse_profile_level(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_profile_level_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_profile_level(core, codecs, domain, payload);
break;
case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED:
- parse_alloc_mode(core, codecs, domain, data);
+ if (rem_bytes <= sizeof(struct hfi_buffer_alloc_mode_supported))
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ ret = parse_alloc_mode(core, codecs, domain, payload);
break;
default:
+ ret = sizeof(u32);
break;
}
- word++;
- words_count--;
+ if (ret < 0)
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ words += ret / sizeof(u32);
+ rem_bytes -= ret;
}
if (!core->max_sessions_supported)
diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
index f5a655973c08..6289166786ec 100644
--- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
+++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c
@@ -1063,51 +1063,51 @@ struct enc_bufsize_ops {
u32 (*persist)(void);
};
-static struct dec_bufsize_ops dec_h264_ops = {
+static const struct dec_bufsize_ops dec_h264_ops = {
.scratch = h264d_scratch_size,
.scratch1 = h264d_scratch1_size,
.persist1 = h264d_persist1_size,
};
-static struct dec_bufsize_ops dec_h265_ops = {
+static const struct dec_bufsize_ops dec_h265_ops = {
.scratch = h265d_scratch_size,
.scratch1 = h265d_scratch1_size,
.persist1 = h265d_persist1_size,
};
-static struct dec_bufsize_ops dec_vp8_ops = {
+static const struct dec_bufsize_ops dec_vp8_ops = {
.scratch = vpxd_scratch_size,
.scratch1 = vp8d_scratch1_size,
.persist1 = vp8d_persist1_size,
};
-static struct dec_bufsize_ops dec_vp9_ops = {
+static const struct dec_bufsize_ops dec_vp9_ops = {
.scratch = vpxd_scratch_size,
.scratch1 = vp9d_scratch1_size,
.persist1 = vp9d_persist1_size,
};
-static struct dec_bufsize_ops dec_mpeg2_ops = {
+static const struct dec_bufsize_ops dec_mpeg2_ops = {
.scratch = mpeg2d_scratch_size,
.scratch1 = mpeg2d_scratch1_size,
.persist1 = mpeg2d_persist1_size,
};
-static struct enc_bufsize_ops enc_h264_ops = {
+static const struct enc_bufsize_ops enc_h264_ops = {
.scratch = h264e_scratch_size,
.scratch1 = h264e_scratch1_size,
.scratch2 = enc_scratch2_size,
.persist = enc_persist_size,
};
-static struct enc_bufsize_ops enc_h265_ops = {
+static const struct enc_bufsize_ops enc_h265_ops = {
.scratch = h265e_scratch_size,
.scratch1 = h265e_scratch1_size,
.scratch2 = enc_scratch2_size,
.persist = enc_persist_size,
};
-static struct enc_bufsize_ops enc_vp8_ops = {
+static const struct enc_bufsize_ops enc_vp8_ops = {
.scratch = vp8e_scratch_size,
.scratch1 = vp8e_scratch1_size,
.scratch2 = enc_scratch2_size,
@@ -1186,7 +1186,7 @@ static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype,
u32 codec = params->codec;
u32 width = params->width, height = params->height, out_min_count;
u32 out_width = params->out_width, out_height = params->out_height;
- struct dec_bufsize_ops *dec_ops;
+ const struct dec_bufsize_ops *dec_ops;
bool is_secondary_output = params->dec.is_secondary_output;
bool is_interlaced = params->dec.is_interlaced;
u32 max_mbs_per_frame = params->dec.max_mbs_per_frame;
@@ -1260,7 +1260,7 @@ static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype,
struct hfi_buffer_requirements *bufreq)
{
enum hfi_version version = params->version;
- struct enc_bufsize_ops *enc_ops;
+ const struct enc_bufsize_ops *enc_ops;
u32 width = params->width;
u32 height = params->height;
bool is_tenbit = params->enc.is_tenbit;
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index f9437b6412b9..b5f2ea879950 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -187,6 +187,9 @@ static int venus_write_queue(struct venus_hfi_device *hdev,
/* ensure rd/wr indices's are read from memory */
rmb();
+ if (qsize > IFACEQ_QUEUE_SIZE / 4)
+ return -EINVAL;
+
if (wr_idx >= rd_idx)
empty_space = qsize - (wr_idx - rd_idx);
else
@@ -255,6 +258,9 @@ static int venus_read_queue(struct venus_hfi_device *hdev,
wr_idx = qhdr->write_idx;
qsize = qhdr->q_size;
+ if (qsize > IFACEQ_QUEUE_SIZE / 4)
+ return -EINVAL;
+
/* make sure data is valid before using it */
rmb();
@@ -1035,18 +1041,26 @@ static void venus_sfr_print(struct venus_hfi_device *hdev)
{
struct device *dev = hdev->core->dev;
struct hfi_sfr *sfr = hdev->sfr.kva;
+ u32 size;
void *p;
if (!sfr)
return;
- p = memchr(sfr->data, '\0', sfr->buf_size);
+ size = sfr->buf_size;
+ if (!size)
+ return;
+
+ if (size > ALIGNED_SFR_SIZE)
+ size = ALIGNED_SFR_SIZE;
+
+ p = memchr(sfr->data, '\0', size);
/*
* SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates
* that Venus is in the process of crashing.
*/
if (!p)
- sfr->data[sfr->buf_size - 1] = '\0';
+ sfr->data[size - 1] = '\0';
dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data);
}
@@ -1178,16 +1192,6 @@ static int venus_core_deinit(struct venus_core *core)
return 0;
}
-static int venus_core_ping(struct venus_core *core, u32 cookie)
-{
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- struct hfi_sys_ping_pkt pkt;
-
- pkt_sys_ping(&pkt, cookie);
-
- return venus_iface_cmdq_write(hdev, &pkt, false);
-}
-
static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
@@ -1639,7 +1643,6 @@ static int venus_suspend(struct venus_core *core)
static const struct hfi_ops venus_hfi_ops = {
.core_init = venus_core_init,
.core_deinit = venus_core_deinit,
- .core_ping = venus_core_ping,
.core_trigger_ssr = venus_core_trigger_ssr,
.session_init = venus_session_init,
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index 502822059498..409aa9bd0b5d 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -412,10 +412,17 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable)
u32 val;
int ret;
- if (IS_V6(core)) {
- ctrl = core->wrapper_base + WRAPPER_CORE_POWER_CONTROL_V6;
- stat = core->wrapper_base + WRAPPER_CORE_POWER_STATUS_V6;
- } else if (coreid == VIDC_CORE_ID_1) {
+ ret = dev_pm_genpd_set_hwmode(core->pmdomains->pd_devs[coreid], !enable);
+ if (ret == -EOPNOTSUPP) {
+ core->hwmode_dev = false;
+ goto legacy;
+ }
+
+ core->hwmode_dev = true;
+ return ret;
+
+legacy:
+ if (coreid == VIDC_CORE_ID_1) {
ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL;
stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS;
} else {
@@ -451,9 +458,11 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask)
vcodec_clks_disable(core, core->vcodec0_clks);
- ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false);
- if (ret)
- return ret;
+ if (!core->hwmode_dev) {
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false);
+ if (ret)
+ return ret;
+ }
ret = pm_runtime_put_sync(core->pmdomains->pd_devs[1]);
if (ret < 0)
@@ -467,9 +476,11 @@ static int poweroff_coreid(struct venus_core *core, unsigned int coreid_mask)
vcodec_clks_disable(core, core->vcodec1_clks);
- ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false);
- if (ret)
- return ret;
+ if (!core->hwmode_dev) {
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false);
+ if (ret)
+ return ret;
+ }
ret = pm_runtime_put_sync(core->pmdomains->pd_devs[2]);
if (ret < 0)
@@ -804,7 +815,7 @@ static int vdec_power_v4(struct device *dev, int on)
else
vcodec_clks_disable(core, core->vcodec0_clks);
- vcodec_control_v4(core, VIDC_CORE_ID_1, false);
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_1, false);
return ret;
}
@@ -849,7 +860,7 @@ static int venc_power_v4(struct device *dev, int on)
else
vcodec_clks_disable(core, core->vcodec1_clks);
- vcodec_control_v4(core, VIDC_CORE_ID_2, false);
+ ret = vcodec_control_v4(core, VIDC_CORE_ID_2, false);
return ret;
}
@@ -857,7 +868,6 @@ static int venc_power_v4(struct device *dev, int on)
static int vcodec_domains_get(struct venus_core *core)
{
int ret;
- struct device **opp_virt_dev;
struct device *dev = core->dev;
const struct venus_resources *res = core->res;
struct dev_pm_domain_attach_data vcodec_data = {
@@ -865,49 +875,29 @@ static int vcodec_domains_get(struct venus_core *core)
.num_pd_names = res->vcodec_pmdomains_num,
.pd_flags = PD_FLAG_NO_DEV_LINK,
};
+ struct dev_pm_domain_attach_data opp_pd_data = {
+ .pd_names = res->opp_pmdomain,
+ .num_pd_names = 1,
+ .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP,
+ };
if (!res->vcodec_pmdomains_num)
goto skip_pmdomains;
- ret = dev_pm_domain_attach_list(dev, &vcodec_data, &core->pmdomains);
+ ret = devm_pm_domain_attach_list(dev, &vcodec_data, &core->pmdomains);
if (ret < 0)
return ret;
skip_pmdomains:
- if (!core->res->opp_pmdomain)
+ if (!res->opp_pmdomain)
return 0;
/* Attach the power domain for setting performance state */
- ret = devm_pm_opp_attach_genpd(dev, res->opp_pmdomain, &opp_virt_dev);
- if (ret)
- goto opp_attach_err;
-
- core->opp_pmdomain = *opp_virt_dev;
- core->opp_dl_venus = device_link_add(dev, core->opp_pmdomain,
- DL_FLAG_RPM_ACTIVE |
- DL_FLAG_PM_RUNTIME |
- DL_FLAG_STATELESS);
- if (!core->opp_dl_venus) {
- ret = -ENODEV;
- goto opp_attach_err;
- }
+ ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain);
+ if (ret < 0)
+ return ret;
return 0;
-
-opp_attach_err:
- dev_pm_domain_detach_list(core->pmdomains);
- return ret;
-}
-
-static void vcodec_domains_put(struct venus_core *core)
-{
- dev_pm_domain_detach_list(core->pmdomains);
-
- if (!core->has_opp_table)
- return;
-
- if (core->opp_dl_venus)
- device_link_del(core->opp_dl_venus);
}
static int core_resets_reset(struct venus_core *core)
@@ -996,9 +986,7 @@ static int core_get_v4(struct venus_core *core)
if (core->res->opp_pmdomain) {
ret = devm_pm_opp_of_add_table(dev);
- if (!ret) {
- core->has_opp_table = true;
- } else if (ret != -ENODEV) {
+ if (ret && ret != -ENODEV) {
dev_err(dev, "invalid OPP table in device tree\n");
return ret;
}
@@ -1009,10 +997,6 @@ static int core_get_v4(struct venus_core *core)
static void core_put_v4(struct venus_core *core)
{
- if (legacy_binding)
- return;
-
- vcodec_domains_put(core);
}
static int core_power_v4(struct venus_core *core, int on)
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 29130a9441e7..99ce5fd41577 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -154,14 +154,14 @@ find_format_by_index(struct venus_inst *inst, unsigned int index, u32 type)
return NULL;
for (i = 0; i < size; i++) {
- bool valid;
+ bool valid = false;
if (fmt[i].type != type)
continue;
if (V4L2_TYPE_IS_OUTPUT(type)) {
valid = venus_helper_check_codec(inst, fmt[i].pixfmt);
- } else if (V4L2_TYPE_IS_CAPTURE(type)) {
+ } else {
valid = venus_helper_check_format(inst, fmt[i].pixfmt);
if (fmt[i].pixfmt == V4L2_PIX_FMT_QC10C &&
@@ -1110,10 +1110,20 @@ static int vdec_start_output(struct venus_inst *inst)
if (inst->codec_state == VENUS_DEC_STATE_SEEK) {
ret = venus_helper_process_initial_out_bufs(inst);
- if (inst->next_buf_last)
+ if (ret)
+ return ret;
+
+ if (inst->next_buf_last) {
inst->codec_state = VENUS_DEC_STATE_DRC;
- else
+ } else {
inst->codec_state = VENUS_DEC_STATE_DECODING;
+
+ if (inst->streamon_cap) {
+ ret = venus_helper_queue_dpb_bufs(inst);
+ if (ret)
+ return ret;
+ }
+ }
goto done;
}
@@ -1255,7 +1265,7 @@ static int vdec_stop_output(struct venus_inst *inst)
break;
case VENUS_DEC_STATE_INIT:
case VENUS_DEC_STATE_CAPTURE_SETUP:
- ret = hfi_session_flush(inst, HFI_FLUSH_INPUT, true);
+ ret = hfi_session_flush(inst, HFI_FLUSH_ALL, true);
break;
default:
break;
@@ -1697,10 +1707,6 @@ static int vdec_open(struct file *file)
if (ret)
goto err_free;
- ret = hfi_session_create(inst, &vdec_hfi_ops);
- if (ret)
- goto err_ctrl_deinit;
-
vdec_inst_init(inst);
ida_init(&inst->dpb_ids);
@@ -1712,15 +1718,19 @@ static int vdec_open(struct file *file)
inst->m2m_dev = v4l2_m2m_init(&vdec_m2m_ops);
if (IS_ERR(inst->m2m_dev)) {
ret = PTR_ERR(inst->m2m_dev);
- goto err_session_destroy;
+ goto err_ctrl_deinit;
}
inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init);
if (IS_ERR(inst->m2m_ctx)) {
ret = PTR_ERR(inst->m2m_ctx);
- goto err_m2m_release;
+ goto err_m2m_dev_release;
}
+ ret = hfi_session_create(inst, &vdec_hfi_ops);
+ if (ret)
+ goto err_m2m_ctx_release;
+
v4l2_fh_init(&inst->fh, core->vdev_dec);
inst->fh.ctrl_handler = &inst->ctrl_handler;
@@ -1730,12 +1740,12 @@ static int vdec_open(struct file *file)
return 0;
-err_m2m_release:
+err_m2m_ctx_release:
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+err_m2m_dev_release:
v4l2_m2m_release(inst->m2m_dev);
-err_session_destroy:
- hfi_session_destroy(inst);
err_ctrl_deinit:
- vdec_ctrl_deinit(inst);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
err_free:
kfree(inst);
return ret;
@@ -1746,17 +1756,9 @@ static int vdec_close(struct file *file)
struct venus_inst *inst = to_inst(file);
vdec_pm_get(inst);
-
- v4l2_m2m_ctx_release(inst->m2m_ctx);
- v4l2_m2m_release(inst->m2m_dev);
- vdec_ctrl_deinit(inst);
+ cancel_work_sync(&inst->delayed_process_work);
+ venus_close_common(inst);
ida_destroy(&inst->dpb_ids);
- hfi_session_destroy(inst);
- mutex_destroy(&inst->lock);
- mutex_destroy(&inst->ctx_q_lock);
- v4l2_fh_del(&inst->fh);
- v4l2_fh_exit(&inst->fh);
-
vdec_pm_put(inst, false);
kfree(inst);
@@ -1874,7 +1876,7 @@ MODULE_DEVICE_TABLE(of, vdec_dt_match);
static struct platform_driver qcom_venus_dec_driver = {
.probe = vdec_probe,
- .remove_new = vdec_remove,
+ .remove = vdec_remove,
.driver = {
.name = "qcom-venus-decoder",
.of_match_table = vdec_dt_match,
diff --git a/drivers/media/platform/qcom/venus/vdec.h b/drivers/media/platform/qcom/venus/vdec.h
index 6b262d0bf561..0cf981108ff0 100644
--- a/drivers/media/platform/qcom/venus/vdec.h
+++ b/drivers/media/platform/qcom/venus/vdec.h
@@ -9,6 +9,5 @@
struct venus_inst;
int vdec_ctrl_init(struct venus_inst *inst);
-void vdec_ctrl_deinit(struct venus_inst *inst);
#endif
diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c
index 7e0f29bf7fae..36ed955b0419 100644
--- a/drivers/media/platform/qcom/venus/vdec_ctrls.c
+++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c
@@ -187,8 +187,3 @@ int vdec_ctrl_init(struct venus_inst *inst)
return 0;
}
-
-void vdec_ctrl_deinit(struct venus_inst *inst)
-{
- v4l2_ctrl_handler_free(&inst->ctrl_handler);
-}
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index 3ec2fb8d9fab..c7f8e37dba9b 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -734,6 +734,29 @@ static int venc_set_properties(struct venus_inst *inst)
if (ret)
return ret;
+ if (ctr->layer_bitrate) {
+ unsigned int i;
+
+ ptype = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER;
+ ret = hfi_session_set_property(inst, ptype, &ctr->h264_hier_layers);
+ if (ret)
+ return ret;
+
+ ptype = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER;
+ ret = hfi_session_set_property(inst, ptype, &ctr->layer_bitrate);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ctr->h264_hier_layers; ++i) {
+ ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
+ brate.bitrate = ctr->h264_hier_layer_bitrate[i];
+ brate.layer_id = i;
+
+ ret = hfi_session_set_property(inst, ptype, &brate);
+ if (ret)
+ return ret;
+ }
+ }
}
if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 ||
@@ -823,18 +846,33 @@ static int venc_set_properties(struct venus_inst *inst)
return ret;
}
- if (!ctr->bitrate)
- bitrate = 64000;
- else
- bitrate = ctr->bitrate;
+ if (!ctr->layer_bitrate) {
+ if (!ctr->bitrate)
+ bitrate = 64000;
+ else
+ bitrate = ctr->bitrate;
- ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
- brate.bitrate = bitrate;
- brate.layer_id = 0;
+ ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
+ brate.bitrate = bitrate;
+ brate.layer_id = 0;
- ret = hfi_session_set_property(inst, ptype, &brate);
- if (ret)
- return ret;
+ ret = hfi_session_set_property(inst, ptype, &brate);
+ if (ret)
+ return ret;
+
+ if (!ctr->bitrate_peak)
+ bitrate *= 2;
+ else
+ bitrate = ctr->bitrate_peak;
+
+ ptype = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE;
+ brate.bitrate = bitrate;
+ brate.layer_id = 0;
+
+ ret = hfi_session_set_property(inst, ptype, &brate);
+ if (ret)
+ return ret;
+ }
if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 ||
inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
@@ -849,19 +887,6 @@ static int venc_set_properties(struct venus_inst *inst)
return ret;
}
- if (!ctr->bitrate_peak)
- bitrate *= 2;
- else
- bitrate = ctr->bitrate_peak;
-
- ptype = HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE;
- brate.bitrate = bitrate;
- brate.layer_id = 0;
-
- ret = hfi_session_set_property(inst, ptype, &brate);
- if (ret)
- return ret;
-
ptype = HFI_PROPERTY_PARAM_VENC_SESSION_QP;
if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
quant.qp_i = ctr->hevc_i_qp;
@@ -1467,10 +1492,6 @@ static int venc_open(struct file *file)
if (ret)
goto err_free;
- ret = hfi_session_create(inst, &venc_hfi_ops);
- if (ret)
- goto err_ctrl_deinit;
-
venc_inst_init(inst);
/*
@@ -1480,15 +1501,19 @@ static int venc_open(struct file *file)
inst->m2m_dev = v4l2_m2m_init(&venc_m2m_ops);
if (IS_ERR(inst->m2m_dev)) {
ret = PTR_ERR(inst->m2m_dev);
- goto err_session_destroy;
+ goto err_ctrl_deinit;
}
inst->m2m_ctx = v4l2_m2m_ctx_init(inst->m2m_dev, inst, m2m_queue_init);
if (IS_ERR(inst->m2m_ctx)) {
ret = PTR_ERR(inst->m2m_ctx);
- goto err_m2m_release;
+ goto err_m2m_dev_release;
}
+ ret = hfi_session_create(inst, &venc_hfi_ops);
+ if (ret)
+ goto err_m2m_ctx_release;
+
v4l2_fh_init(&inst->fh, core->vdev_enc);
inst->fh.ctrl_handler = &inst->ctrl_handler;
@@ -1498,12 +1523,12 @@ static int venc_open(struct file *file)
return 0;
-err_m2m_release:
+err_m2m_ctx_release:
+ v4l2_m2m_ctx_release(inst->m2m_ctx);
+err_m2m_dev_release:
v4l2_m2m_release(inst->m2m_dev);
-err_session_destroy:
- hfi_session_destroy(inst);
err_ctrl_deinit:
- venc_ctrl_deinit(inst);
+ v4l2_ctrl_handler_free(&inst->ctrl_handler);
err_free:
kfree(inst);
return ret;
@@ -1514,18 +1539,8 @@ static int venc_close(struct file *file)
struct venus_inst *inst = to_inst(file);
venc_pm_get(inst);
-
- v4l2_m2m_ctx_release(inst->m2m_ctx);
- v4l2_m2m_release(inst->m2m_dev);
- venc_ctrl_deinit(inst);
- hfi_session_destroy(inst);
- mutex_destroy(&inst->lock);
- mutex_destroy(&inst->ctx_q_lock);
- v4l2_fh_del(&inst->fh);
- v4l2_fh_exit(&inst->fh);
-
+ venus_close_common(inst);
inst->enc_state = VENUS_ENC_STATE_DEINIT;
-
venc_pm_put(inst, false);
kfree(inst);
@@ -1643,7 +1658,7 @@ MODULE_DEVICE_TABLE(of, venc_dt_match);
static struct platform_driver qcom_venus_enc_driver = {
.probe = venc_probe,
- .remove_new = venc_remove,
+ .remove = venc_remove,
.driver = {
.name = "qcom-venus-encoder",
.of_match_table = venc_dt_match,
diff --git a/drivers/media/platform/qcom/venus/venc.h b/drivers/media/platform/qcom/venus/venc.h
index 4ea37fdcd9b8..719d0f73b14b 100644
--- a/drivers/media/platform/qcom/venus/venc.h
+++ b/drivers/media/platform/qcom/venus/venc.h
@@ -9,6 +9,5 @@
struct venus_inst;
int venc_ctrl_init(struct venus_inst *inst);
-void venc_ctrl_deinit(struct venus_inst *inst);
#endif
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index d9d2a293f3ef..4d36c44f9d44 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -67,12 +67,28 @@ static int venc_calc_bpframes(u32 gop_size, u32 conseq_b, u32 *bf, u32 *pf)
return 0;
}
+static int dynamic_bitrate_update(struct venus_inst *inst, u32 bitrate,
+ u32 layer_id)
+{
+ int ret = 0;
+
+ mutex_lock(&inst->lock);
+ if (inst->streamon_out && inst->streamon_cap) {
+ u32 ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
+ struct hfi_bitrate brate = { .bitrate = bitrate, .layer_id = layer_id };
+
+ ret = hfi_session_set_property(inst, ptype, &brate);
+ }
+ mutex_unlock(&inst->lock);
+
+ return ret;
+}
+
static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct venus_inst *inst = ctrl_to_inst(ctrl);
struct venc_controls *ctr = &inst->controls.enc;
struct hfi_enable en = { .enable = 1 };
- struct hfi_bitrate brate;
struct hfi_ltr_use ltr_use;
struct hfi_ltr_mark ltr_mark;
u32 bframes;
@@ -85,19 +101,9 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_MPEG_VIDEO_BITRATE:
ctr->bitrate = ctrl->val;
- mutex_lock(&inst->lock);
- if (inst->streamon_out && inst->streamon_cap) {
- ptype = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
- brate.bitrate = ctr->bitrate;
- brate.layer_id = 0;
-
- ret = hfi_session_set_property(inst, ptype, &brate);
- if (ret) {
- mutex_unlock(&inst->lock);
- return ret;
- }
- }
- mutex_unlock(&inst->lock);
+ ret = dynamic_bitrate_update(inst, ctr->bitrate, 0);
+ if (ret)
+ return ret;
break;
case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
ctr->bitrate_peak = ctrl->val;
@@ -340,6 +346,55 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
ctr->h264_8x8_transform = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE:
+ if (ctrl->val != V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P)
+ return -EINVAL;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING:
+ ctr->layer_bitrate = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER:
+ if (ctrl->val > VIDC_MAX_HIER_CODING_LAYER)
+ return -EINVAL;
+ ctr->h264_hier_layers = ctrl->val;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR:
+ ctr->h264_hier_layer_bitrate[0] = ctrl->val;
+ ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[0], 0);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR:
+ ctr->h264_hier_layer_bitrate[1] = ctrl->val;
+ ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[1], 1);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR:
+ ctr->h264_hier_layer_bitrate[2] = ctrl->val;
+ ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[2], 2);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR:
+ ctr->h264_hier_layer_bitrate[3] = ctrl->val;
+ ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[3], 3);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR:
+ ctr->h264_hier_layer_bitrate[4] = ctrl->val;
+ ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[4], 4);
+ if (ret)
+ return ret;
+ break;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR:
+ ctr->h264_hier_layer_bitrate[5] = ctrl->val;
+ ret = dynamic_bitrate_update(inst, ctr->h264_hier_layer_bitrate[5], 5);
+ if (ret)
+ return ret;
+ break;
+
default:
return -EINVAL;
}
@@ -607,11 +662,16 @@ int venc_ctrl_init(struct venus_inst *inst)
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_COLORIMETRY_HDR10_CLL_INFO,
- v4l2_ctrl_ptr_create(&p_hdr10_cll));
+ v4l2_ctrl_ptr_create(&p_hdr10_cll),
+ v4l2_ctrl_ptr_create(NULL),
+ v4l2_ctrl_ptr_create(NULL));
v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY,
- v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering));
+ v4l2_ctrl_ptr_create((void *)&p_hdr10_mastering),
+ v4l2_ctrl_ptr_create(NULL),
+ v4l2_ctrl_ptr_create(NULL));
+
v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE,
@@ -622,6 +682,49 @@ int venc_ctrl_init(struct venus_inst *inst)
V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD, 0,
((4096 * 2304) >> 8), 1, 0);
+ if (IS_V4(inst->core) || IS_V6(inst->core)) {
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE,
+ V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ 1, V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER, 0,
+ VIDC_MAX_HIER_CODING_LAYER, 1, 0);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR,
+ BITRATE_MIN, BITRATE_MAX, BITRATE_STEP, BITRATE_DEFAULT);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR,
+ BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP, BITRATE_DEFAULT);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR,
+ BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP, BITRATE_DEFAULT);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR,
+ BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP, BITRATE_DEFAULT);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR,
+ BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP, BITRATE_DEFAULT);
+
+ v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR,
+ BITRATE_MIN, BITRATE_MAX,
+ BITRATE_STEP, BITRATE_DEFAULT);
+ }
+
ret = inst->ctrl_handler.error;
if (ret)
goto err;
@@ -635,8 +738,3 @@ err:
v4l2_ctrl_handler_free(&inst->ctrl_handler);
return ret;
}
-
-void venc_ctrl_deinit(struct venus_inst *inst)
-{
- v4l2_ctrl_handler_free(&inst->ctrl_handler);
-}
diff --git a/drivers/media/platform/raspberrypi/Kconfig b/drivers/media/platform/raspberrypi/Kconfig
new file mode 100644
index 000000000000..bd5101ffefb5
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/Kconfig
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+comment "Raspberry Pi media platform drivers"
+
+source "drivers/media/platform/raspberrypi/pisp_be/Kconfig"
+source "drivers/media/platform/raspberrypi/rp1-cfe/Kconfig"
diff --git a/drivers/media/platform/raspberrypi/Makefile b/drivers/media/platform/raspberrypi/Makefile
new file mode 100644
index 000000000000..af7fde84eefe
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += pisp_be/
+obj-y += rp1-cfe/
diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
new file mode 100644
index 000000000000..46765a2e4c4d
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
@@ -0,0 +1,13 @@
+config VIDEO_RASPBERRYPI_PISP_BE
+ tristate "Raspberry Pi PiSP Backend (BE) ISP driver"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV
+ depends on ARCH_BCM2835 || COMPILE_TEST
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Say Y here to enable support for the PiSP Backend (BE) ISP driver.
+
+ To compile this driver as a module, choose M here. The module will be
+ called pisp-be.
diff --git a/drivers/media/platform/raspberrypi/pisp_be/Makefile b/drivers/media/platform/raspberrypi/pisp_be/Makefile
new file mode 100644
index 000000000000..a70bf5716824
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/pisp_be/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Raspberry Pi PiSP Backend driver
+#
+pisp-be-objs := pisp_be.o
+obj-$(CONFIG_VIDEO_RASPBERRYPI_PISP_BE) += pisp-be.o
diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
new file mode 100644
index 000000000000..7596ae1f7de6
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
@@ -0,0 +1,1797 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PiSP Back End driver.
+ * Copyright (c) 2021-2024 Raspberry Pi Limited.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include <uapi/linux/media/raspberrypi/pisp_be_config.h>
+
+#include "pisp_be_formats.h"
+
+/* Maximum number of config buffers possible */
+#define PISP_BE_NUM_CONFIG_BUFFERS VB2_MAX_FRAME
+
+#define PISPBE_NAME "pispbe"
+
+/* Some ISP-BE registers */
+#define PISP_BE_VERSION_REG 0x0
+#define PISP_BE_CONTROL_REG 0x4
+#define PISP_BE_CONTROL_COPY_CONFIG BIT(1)
+#define PISP_BE_CONTROL_QUEUE_JOB BIT(0)
+#define PISP_BE_CONTROL_NUM_TILES(n) ((n) << 16)
+#define PISP_BE_TILE_ADDR_LO_REG 0x8
+#define PISP_BE_TILE_ADDR_HI_REG 0xc
+#define PISP_BE_STATUS_REG 0x10
+#define PISP_BE_STATUS_QUEUED BIT(0)
+#define PISP_BE_BATCH_STATUS_REG 0x14
+#define PISP_BE_INTERRUPT_EN_REG 0x18
+#define PISP_BE_INTERRUPT_STATUS_REG 0x1c
+#define PISP_BE_AXI_REG 0x20
+#define PISP_BE_CONFIG_BASE_REG 0x40
+#define PISP_BE_IO_ADDR_LOW(n) (PISP_BE_CONFIG_BASE_REG + 8 * (n))
+#define PISP_BE_IO_ADDR_HIGH(n) (PISP_BE_IO_ADDR_LOW((n)) + 4)
+#define PISP_BE_GLOBAL_BAYER_ENABLE 0xb0
+#define PISP_BE_GLOBAL_RGB_ENABLE 0xb4
+#define N_HW_ADDRESSES 13
+#define N_HW_ENABLES 2
+
+#define PISP_BE_VERSION_2712 0x02252700
+#define PISP_BE_VERSION_MINOR_BITS 0xf
+
+/*
+ * This maps our nodes onto the inputs/outputs of the actual PiSP Back End.
+ * Be wary of the word "OUTPUT" which is used ambiguously here. In a V4L2
+ * context it means an input to the hardware (source image or metadata).
+ * Elsewhere it means an output from the hardware.
+ */
+enum pispbe_node_ids {
+ MAIN_INPUT_NODE,
+ TDN_INPUT_NODE,
+ STITCH_INPUT_NODE,
+ OUTPUT0_NODE,
+ OUTPUT1_NODE,
+ TDN_OUTPUT_NODE,
+ STITCH_OUTPUT_NODE,
+ CONFIG_NODE,
+ PISPBE_NUM_NODES
+};
+
+struct pispbe_node_description {
+ const char *ent_name;
+ enum v4l2_buf_type buf_type;
+ unsigned int caps;
+};
+
+static const struct pispbe_node_description node_desc[PISPBE_NUM_NODES] = {
+ /* MAIN_INPUT_NODE */
+ {
+ .ent_name = PISPBE_NAME "-input",
+ .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
+ },
+ /* TDN_INPUT_NODE */
+ {
+ .ent_name = PISPBE_NAME "-tdn_input",
+ .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
+ },
+ /* STITCH_INPUT_NODE */
+ {
+ .ent_name = PISPBE_NAME "-stitch_input",
+ .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
+ },
+ /* OUTPUT0_NODE */
+ {
+ .ent_name = PISPBE_NAME "-output0",
+ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+ },
+ /* OUTPUT1_NODE */
+ {
+ .ent_name = PISPBE_NAME "-output1",
+ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+ },
+ /* TDN_OUTPUT_NODE */
+ {
+ .ent_name = PISPBE_NAME "-tdn_output",
+ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+ },
+ /* STITCH_OUTPUT_NODE */
+ {
+ .ent_name = PISPBE_NAME "-stitch_output",
+ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
+ },
+ /* CONFIG_NODE */
+ {
+ .ent_name = PISPBE_NAME "-config",
+ .buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+ .caps = V4L2_CAP_META_OUTPUT,
+ }
+};
+
+#define NODE_DESC_IS_OUTPUT(desc) ( \
+ ((desc)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
+ ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \
+ ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+
+#define NODE_IS_META(node) ( \
+ ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT))
+#define NODE_IS_OUTPUT(node) ( \
+ ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
+ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \
+ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+#define NODE_IS_CAPTURE(node) ( \
+ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || \
+ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+#define NODE_IS_MPLANE(node) ( \
+ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || \
+ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+
+/*
+ * Structure to describe a single node /dev/video<N> which represents a single
+ * input or output queue to the PiSP Back End device.
+ */
+struct pispbe_node {
+ unsigned int id;
+ int vfl_dir;
+ enum v4l2_buf_type buf_type;
+ struct video_device vfd;
+ struct media_pad pad;
+ struct media_intf_devnode *intf_devnode;
+ struct media_link *intf_link;
+ struct pispbe_dev *pispbe;
+ /* Video device lock */
+ struct mutex node_lock;
+ /* vb2_queue lock */
+ struct mutex queue_lock;
+ /* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */
+ spinlock_t ready_lock;
+ struct list_head ready_queue;
+ struct vb2_queue queue;
+ struct v4l2_format format;
+ const struct pisp_be_format *pisp_format;
+};
+
+/* For logging only, use the entity name with "pispbe" and separator removed */
+#define NODE_NAME(node) \
+ (node_desc[(node)->id].ent_name + sizeof(PISPBE_NAME))
+
+/* Records details of the jobs currently running or queued on the h/w. */
+struct pispbe_job {
+ bool valid;
+ /*
+ * An array of buffer pointers - remember it's source buffers first,
+ * then captures, then metadata last.
+ */
+ struct pispbe_buffer *buf[PISPBE_NUM_NODES];
+};
+
+struct pispbe_hw_enables {
+ u32 bayer_enables;
+ u32 rgb_enables;
+};
+
+/* Records a job configuration and memory addresses. */
+struct pispbe_job_descriptor {
+ dma_addr_t hw_dma_addrs[N_HW_ADDRESSES];
+ struct pisp_be_tiles_config *config;
+ struct pispbe_hw_enables hw_enables;
+ dma_addr_t tiles;
+};
+
+/*
+ * Structure representing the entire PiSP Back End device, comprising several
+ * nodes which share platform resources and a mutex for the actual HW.
+ */
+struct pispbe_dev {
+ struct device *dev;
+ struct pispbe_dev *pispbe;
+ struct pisp_be_tiles_config *config;
+ void __iomem *be_reg_base;
+ struct clk *clk;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev sd;
+ struct media_device mdev;
+ struct media_pad pad[PISPBE_NUM_NODES]; /* output pads first */
+ struct pispbe_node node[PISPBE_NUM_NODES];
+ dma_addr_t config_dma_addr;
+ unsigned int sequence;
+ u32 streaming_map;
+ struct pispbe_job queued_job, running_job;
+ spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */
+ bool hw_busy; /* non-zero if a job is queued or is being started */
+ int irq;
+ u32 hw_version;
+ u8 done, started;
+};
+
+static u32 pispbe_rd(struct pispbe_dev *pispbe, unsigned int offset)
+{
+ return readl(pispbe->be_reg_base + offset);
+}
+
+static void pispbe_wr(struct pispbe_dev *pispbe, unsigned int offset, u32 val)
+{
+ writel(val, pispbe->be_reg_base + offset);
+}
+
+/*
+ * Queue a job to the h/w. If the h/w is idle it will begin immediately.
+ * Caller must ensure it is "safe to queue", i.e. we don't already have a
+ * queued, unstarted job.
+ */
+static void pispbe_queue_job(struct pispbe_dev *pispbe,
+ struct pispbe_job_descriptor *job)
+{
+ unsigned int begin, end;
+
+ if (pispbe_rd(pispbe, PISP_BE_STATUS_REG) & PISP_BE_STATUS_QUEUED)
+ dev_err(pispbe->dev, "ERROR: not safe to queue new job!\n");
+
+ /*
+ * Write configuration to hardware. DMA addresses and enable flags
+ * are passed separately, because the driver needs to sanitize them,
+ * and we don't want to modify (or be vulnerable to modifications of)
+ * the mmap'd buffer.
+ */
+ for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) {
+ pispbe_wr(pispbe, PISP_BE_IO_ADDR_LOW(u),
+ lower_32_bits(job->hw_dma_addrs[u]));
+ pispbe_wr(pispbe, PISP_BE_IO_ADDR_HIGH(u),
+ upper_32_bits(job->hw_dma_addrs[u]));
+ }
+ pispbe_wr(pispbe, PISP_BE_GLOBAL_BAYER_ENABLE,
+ job->hw_enables.bayer_enables);
+ pispbe_wr(pispbe, PISP_BE_GLOBAL_RGB_ENABLE,
+ job->hw_enables.rgb_enables);
+
+ /* Everything else is as supplied by the user. */
+ begin = offsetof(struct pisp_be_config, global.bayer_order) /
+ sizeof(u32);
+ end = sizeof(struct pisp_be_config) / sizeof(u32);
+ for (unsigned int u = begin; u < end; u++)
+ pispbe_wr(pispbe, PISP_BE_CONFIG_BASE_REG + sizeof(u32) * u,
+ ((u32 *)job->config)[u]);
+
+ /* Read back the addresses -- an error here could be fatal */
+ for (unsigned int u = 0; u < N_HW_ADDRESSES; ++u) {
+ unsigned int offset = PISP_BE_IO_ADDR_LOW(u);
+ u64 along = pispbe_rd(pispbe, offset);
+
+ along += ((u64)pispbe_rd(pispbe, offset + 4)) << 32;
+ if (along != (u64)(job->hw_dma_addrs[u])) {
+ dev_dbg(pispbe->dev,
+ "ISP BE config error: check if ISP RAMs enabled?\n");
+ return;
+ }
+ }
+
+ /*
+ * Write tile pointer to hardware. The IOMMU should prevent
+ * out-of-bounds offsets reaching non-ISP buffers.
+ */
+ pispbe_wr(pispbe, PISP_BE_TILE_ADDR_LO_REG, lower_32_bits(job->tiles));
+ pispbe_wr(pispbe, PISP_BE_TILE_ADDR_HI_REG, upper_32_bits(job->tiles));
+
+ /* Enqueue the job */
+ pispbe_wr(pispbe, PISP_BE_CONTROL_REG,
+ PISP_BE_CONTROL_COPY_CONFIG | PISP_BE_CONTROL_QUEUE_JOB |
+ PISP_BE_CONTROL_NUM_TILES(job->config->num_tiles));
+}
+
+struct pispbe_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head ready_list;
+ unsigned int config_index;
+};
+
+static int pispbe_get_planes_addr(dma_addr_t addr[3], struct pispbe_buffer *buf,
+ struct pispbe_node *node)
+{
+ unsigned int num_planes = node->format.fmt.pix_mp.num_planes;
+ unsigned int plane_factor = 0;
+ unsigned int size;
+ unsigned int p;
+
+ if (!buf || !node->pisp_format)
+ return 0;
+
+ /*
+ * Determine the base plane size. This will not be the same
+ * as node->format.fmt.pix_mp.plane_fmt[0].sizeimage for a single
+ * plane buffer in an mplane format.
+ */
+ size = node->format.fmt.pix_mp.plane_fmt[0].bytesperline *
+ node->format.fmt.pix_mp.height;
+
+ for (p = 0; p < num_planes && p < PISPBE_MAX_PLANES; p++) {
+ addr[p] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, p);
+ plane_factor += node->pisp_format->plane_factor[p];
+ }
+
+ for (; p < PISPBE_MAX_PLANES && node->pisp_format->plane_factor[p]; p++) {
+ /*
+ * Calculate the address offset of this plane as needed
+ * by the hardware. This is specifically for non-mplane
+ * buffer formats, where there are 3 image planes, e.g.
+ * for the V4L2_PIX_FMT_YUV420 format.
+ */
+ addr[p] = addr[0] + ((size * plane_factor) >> 3);
+ plane_factor += node->pisp_format->plane_factor[p];
+ }
+
+ return num_planes;
+}
+
+static dma_addr_t pispbe_get_addr(struct pispbe_buffer *buf)
+{
+ if (buf)
+ return vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+
+ return 0;
+}
+
+static void pispbe_xlate_addrs(struct pispbe_dev *pispbe,
+ struct pispbe_job_descriptor *job,
+ struct pispbe_buffer *buf[PISPBE_NUM_NODES])
+{
+ struct pispbe_hw_enables *hw_en = &job->hw_enables;
+ struct pisp_be_tiles_config *config = job->config;
+ dma_addr_t *addrs = job->hw_dma_addrs;
+ int ret;
+
+ /* Take a copy of the "enable" bitmaps so we can modify them. */
+ hw_en->bayer_enables = config->config.global.bayer_enables;
+ hw_en->rgb_enables = config->config.global.rgb_enables;
+
+ /*
+ * Main input first. There are 3 address pointers, corresponding to up
+ * to 3 planes.
+ */
+ ret = pispbe_get_planes_addr(addrs, buf[MAIN_INPUT_NODE],
+ &pispbe->node[MAIN_INPUT_NODE]);
+ if (ret <= 0) {
+ /*
+ * This shouldn't happen; pispbe_schedule_internal should insist
+ * on an input.
+ */
+ dev_warn(pispbe->dev, "ISP-BE missing input\n");
+ hw_en->bayer_enables = 0;
+ hw_en->rgb_enables = 0;
+ return;
+ }
+
+ /*
+ * Now TDN/Stitch inputs and outputs. These are single-plane and only
+ * used with Bayer input. Input enables must match the requirements
+ * of the processing stages, otherwise the hardware can lock up!
+ */
+ if (hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) {
+ addrs[3] = pispbe_get_addr(buf[TDN_INPUT_NODE]);
+ if (addrs[3] == 0 ||
+ !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN_INPUT) ||
+ !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_TDN) ||
+ (config->config.tdn.reset & 1)) {
+ hw_en->bayer_enables &=
+ ~(PISP_BE_BAYER_ENABLE_TDN_INPUT |
+ PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS);
+ if (!(config->config.tdn.reset & 1))
+ hw_en->bayer_enables &=
+ ~PISP_BE_BAYER_ENABLE_TDN;
+ }
+
+ addrs[4] = pispbe_get_addr(buf[STITCH_INPUT_NODE]);
+ if (addrs[4] == 0 ||
+ !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_INPUT) ||
+ !(hw_en->bayer_enables & PISP_BE_BAYER_ENABLE_STITCH)) {
+ hw_en->bayer_enables &=
+ ~(PISP_BE_BAYER_ENABLE_STITCH_INPUT |
+ PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS |
+ PISP_BE_BAYER_ENABLE_STITCH);
+ }
+
+ addrs[5] = pispbe_get_addr(buf[TDN_OUTPUT_NODE]);
+ if (addrs[5] == 0)
+ hw_en->bayer_enables &=
+ ~(PISP_BE_BAYER_ENABLE_TDN_COMPRESS |
+ PISP_BE_BAYER_ENABLE_TDN_OUTPUT);
+
+ addrs[6] = pispbe_get_addr(buf[STITCH_OUTPUT_NODE]);
+ if (addrs[6] == 0)
+ hw_en->bayer_enables &=
+ ~(PISP_BE_BAYER_ENABLE_STITCH_COMPRESS |
+ PISP_BE_BAYER_ENABLE_STITCH_OUTPUT);
+ } else {
+ /* No Bayer input? Disable entire Bayer pipe (else lockup) */
+ hw_en->bayer_enables = 0;
+ }
+
+ /* Main image output channels. */
+ for (unsigned int i = 0; i < PISP_BACK_END_NUM_OUTPUTS; i++) {
+ ret = pispbe_get_planes_addr(addrs + 7 + 3 * i,
+ buf[OUTPUT0_NODE + i],
+ &pispbe->node[OUTPUT0_NODE + i]);
+ if (ret <= 0)
+ hw_en->rgb_enables &= ~(PISP_BE_RGB_ENABLE_OUTPUT0 << i);
+ }
+}
+
+/*
+ * Prepare a job description to be submitted to the HW.
+ *
+ * To schedule a job, we need all streaming nodes (apart from Output0,
+ * Output1, Tdn and Stitch) to have a buffer ready, which must
+ * include at least a config buffer and a main input image.
+ *
+ * For Output0, Output1, Tdn and Stitch, a buffer only needs to be
+ * available if the blocks are enabled in the config.
+ *
+ * Needs to be called with hw_lock held.
+ *
+ * Returns 0 if a job has been successfully prepared, < 0 otherwise.
+ */
+static int pispbe_prepare_job(struct pispbe_dev *pispbe,
+ struct pispbe_job_descriptor *job)
+{
+ struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {};
+ unsigned int config_index;
+ struct pispbe_node *node;
+ unsigned long flags;
+
+ lockdep_assert_held(&pispbe->hw_lock);
+
+ memset(job, 0, sizeof(struct pispbe_job_descriptor));
+
+ if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) &
+ pispbe->streaming_map) !=
+ (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)))
+ return -ENODEV;
+
+ node = &pispbe->node[CONFIG_NODE];
+ spin_lock_irqsave(&node->ready_lock, flags);
+ buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue,
+ struct pispbe_buffer,
+ ready_list);
+ if (buf[CONFIG_NODE]) {
+ list_del(&buf[CONFIG_NODE]->ready_list);
+ pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE];
+ }
+ spin_unlock_irqrestore(&node->ready_lock, flags);
+
+ /* Exit early if no config buffer has been queued. */
+ if (!buf[CONFIG_NODE])
+ return -ENODEV;
+
+ config_index = buf[CONFIG_NODE]->vb.vb2_buf.index;
+ job->config = &pispbe->config[config_index];
+ job->tiles = pispbe->config_dma_addr +
+ config_index * sizeof(struct pisp_be_tiles_config) +
+ offsetof(struct pisp_be_tiles_config, tiles);
+
+ /* remember: srcimages, captures then metadata */
+ for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) {
+ unsigned int bayer_en =
+ job->config->config.global.bayer_enables;
+ unsigned int rgb_en =
+ job->config->config.global.rgb_enables;
+ bool ignore_buffers = false;
+
+ /* Config node is handled outside the loop above. */
+ if (i == CONFIG_NODE)
+ continue;
+
+ buf[i] = NULL;
+ if (!(pispbe->streaming_map & BIT(i)))
+ continue;
+
+ if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) &&
+ i == OUTPUT0_NODE) ||
+ (!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT1) &&
+ i == OUTPUT1_NODE) ||
+ (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_INPUT) &&
+ i == TDN_INPUT_NODE) ||
+ (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) &&
+ i == TDN_OUTPUT_NODE) ||
+ (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_INPUT) &&
+ i == STITCH_INPUT_NODE) ||
+ (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) &&
+ i == STITCH_OUTPUT_NODE)) {
+ /*
+ * Ignore Output0/Output1/Tdn/Stitch buffer check if the
+ * global enables aren't set for these blocks. If a
+ * buffer has been provided, we dequeue it back to the
+ * user with the other in-use buffers.
+ */
+ ignore_buffers = true;
+ }
+
+ node = &pispbe->node[i];
+
+ /* Pull a buffer from each V4L2 queue to form the queued job */
+ spin_lock_irqsave(&node->ready_lock, flags);
+ buf[i] = list_first_entry_or_null(&node->ready_queue,
+ struct pispbe_buffer,
+ ready_list);
+ if (buf[i]) {
+ list_del(&buf[i]->ready_list);
+ pispbe->queued_job.buf[i] = buf[i];
+ }
+ spin_unlock_irqrestore(&node->ready_lock, flags);
+
+ if (!buf[i] && !ignore_buffers)
+ goto err_return_buffers;
+ }
+
+ pispbe->queued_job.valid = true;
+
+ /* Convert buffers to DMA addresses for the hardware */
+ pispbe_xlate_addrs(pispbe, job, buf);
+
+ return 0;
+
+err_return_buffers:
+ for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) {
+ struct pispbe_node *n = &pispbe->node[i];
+
+ if (!buf[i])
+ continue;
+
+ /* Return the buffer to the ready_list queue */
+ spin_lock_irqsave(&n->ready_lock, flags);
+ list_add(&buf[i]->ready_list, &n->ready_queue);
+ spin_unlock_irqrestore(&n->ready_lock, flags);
+ }
+
+ memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job));
+
+ return -ENODEV;
+}
+
+static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy)
+{
+ struct pispbe_job_descriptor job;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&pispbe->hw_lock, flags);
+
+ if (clear_hw_busy)
+ pispbe->hw_busy = false;
+
+ if (pispbe->hw_busy)
+ goto unlock_and_return;
+
+ ret = pispbe_prepare_job(pispbe, &job);
+ if (ret)
+ goto unlock_and_return;
+
+ /*
+ * We can kick the job off without the hw_lock, as this can
+ * never run again until hw_busy is cleared, which will happen
+ * only when the following job has been queued and an interrupt
+ * is rised.
+ */
+ pispbe->hw_busy = true;
+ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ if (job.config->num_tiles <= 0 ||
+ job.config->num_tiles > PISP_BACK_END_NUM_TILES ||
+ !((job.hw_enables.bayer_enables | job.hw_enables.rgb_enables) &
+ PISP_BE_BAYER_ENABLE_INPUT)) {
+ /*
+ * Bad job. We can't let it proceed as it could lock up
+ * the hardware, or worse!
+ *
+ * For now, just force num_tiles to 0, which causes the
+ * H/W to do something bizarre but survivable. It
+ * increments (started,done) counters by more than 1,
+ * but we seem to survive...
+ */
+ dev_dbg(pispbe->dev, "Bad job: invalid number of tiles: %u\n",
+ job.config->num_tiles);
+ job.config->num_tiles = 0;
+ }
+
+ pispbe_queue_job(pispbe, &job);
+
+ return;
+
+unlock_and_return:
+ /* No job has been queued, just release the lock and return. */
+ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+}
+
+static void pispbe_isr_jobdone(struct pispbe_dev *pispbe,
+ struct pispbe_job *job)
+{
+ struct pispbe_buffer **buf = job->buf;
+ u64 ts = ktime_get_ns();
+
+ for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++) {
+ if (buf[i]) {
+ buf[i]->vb.vb2_buf.timestamp = ts;
+ buf[i]->vb.sequence = pispbe->sequence;
+ vb2_buffer_done(&buf[i]->vb.vb2_buf,
+ VB2_BUF_STATE_DONE);
+ }
+ }
+
+ pispbe->sequence++;
+}
+
+static irqreturn_t pispbe_isr(int irq, void *dev)
+{
+ struct pispbe_dev *pispbe = (struct pispbe_dev *)dev;
+ bool can_queue_another = false;
+ u8 started, done;
+ u32 u;
+
+ u = pispbe_rd(pispbe, PISP_BE_INTERRUPT_STATUS_REG);
+ if (u == 0)
+ return IRQ_NONE;
+
+ pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, u);
+ u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG);
+ done = (uint8_t)u;
+ started = (uint8_t)(u >> 8);
+
+ /*
+ * Be aware that done can go up by 2 and started by 1 when: a job that
+ * we previously saw "start" now finishes, and we then queued a new job
+ * which we see both start and finish "simultaneously".
+ */
+ if (pispbe->running_job.valid && pispbe->done != done) {
+ pispbe_isr_jobdone(pispbe, &pispbe->running_job);
+ memset(&pispbe->running_job, 0, sizeof(pispbe->running_job));
+ pispbe->done++;
+ }
+
+ if (pispbe->started != started) {
+ pispbe->started++;
+ can_queue_another = 1;
+
+ if (pispbe->done != done && pispbe->queued_job.valid) {
+ pispbe_isr_jobdone(pispbe, &pispbe->queued_job);
+ pispbe->done++;
+ } else {
+ pispbe->running_job = pispbe->queued_job;
+ }
+
+ memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job));
+ }
+
+ if (pispbe->done != done || pispbe->started != started) {
+ dev_dbg(pispbe->dev,
+ "Job counters not matching: done = %u, expected %u - started = %u, expected %u\n",
+ pispbe->done, done, pispbe->started, started);
+ pispbe->started = started;
+ pispbe->done = done;
+ }
+
+ /* check if there's more to do before going to sleep */
+ pispbe_schedule(pispbe, can_queue_another);
+
+ return IRQ_HANDLED;
+}
+
+static int pisp_be_validate_config(struct pispbe_dev *pispbe,
+ struct pisp_be_tiles_config *config)
+{
+ u32 bayer_enables = config->config.global.bayer_enables;
+ u32 rgb_enables = config->config.global.rgb_enables;
+ struct device *dev = pispbe->dev;
+ struct v4l2_format *fmt;
+ unsigned int bpl, size;
+
+ if (!(bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) ==
+ !(rgb_enables & PISP_BE_RGB_ENABLE_INPUT)) {
+ dev_dbg(dev, "%s: Not one input enabled\n", __func__);
+ return -EIO;
+ }
+
+ /* Ensure output config strides and buffer sizes match the V4L2 formats. */
+ fmt = &pispbe->node[TDN_OUTPUT_NODE].format;
+ if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) {
+ bpl = config->config.tdn_output_format.stride;
+ size = bpl * config->config.tdn_output_format.height;
+
+ if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) {
+ dev_dbg(dev, "%s: bpl mismatch on tdn_output\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) {
+ dev_dbg(dev, "%s: size mismatch on tdn_output\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ fmt = &pispbe->node[STITCH_OUTPUT_NODE].format;
+ if (bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) {
+ bpl = config->config.stitch_output_format.stride;
+ size = bpl * config->config.stitch_output_format.height;
+
+ if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) {
+ dev_dbg(dev, "%s: bpl mismatch on stitch_output\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) {
+ dev_dbg(dev, "%s: size mismatch on stitch_output\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+
+ for (unsigned int j = 0; j < PISP_BACK_END_NUM_OUTPUTS; j++) {
+ if (!(rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT(j)))
+ continue;
+
+ if (config->config.output_format[j].image.format &
+ PISP_IMAGE_FORMAT_WALLPAPER_ROLL)
+ continue; /* TODO: Size checks for wallpaper formats */
+
+ fmt = &pispbe->node[OUTPUT0_NODE + j].format;
+ for (unsigned int i = 0; i < fmt->fmt.pix_mp.num_planes; i++) {
+ bpl = !i ? config->config.output_format[j].image.stride
+ : config->config.output_format[j].image.stride2;
+ size = bpl * config->config.output_format[j].image.height;
+
+ if (config->config.output_format[j].image.format &
+ PISP_IMAGE_FORMAT_SAMPLING_420)
+ size >>= 1;
+
+ if (fmt->fmt.pix_mp.plane_fmt[i].bytesperline < bpl) {
+ dev_dbg(dev, "%s: bpl mismatch on output %d\n",
+ __func__, j);
+ return -EINVAL;
+ }
+
+ if (fmt->fmt.pix_mp.plane_fmt[i].sizeimage < size) {
+ dev_dbg(dev, "%s: size mismatch on output\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int pispbe_node_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct pispbe_node *node = vb2_get_drv_priv(q);
+ struct pispbe_dev *pispbe = node->pispbe;
+ unsigned int num_planes = NODE_IS_MPLANE(node) ?
+ node->format.fmt.pix_mp.num_planes : 1;
+
+ if (*nplanes) {
+ if (*nplanes != num_planes)
+ return -EINVAL;
+
+ for (unsigned int i = 0; i < *nplanes; i++) {
+ unsigned int size = NODE_IS_MPLANE(node) ?
+ node->format.fmt.pix_mp.plane_fmt[i].sizeimage :
+ node->format.fmt.meta.buffersize;
+
+ if (sizes[i] < size)
+ return -EINVAL;
+ }
+
+ return 0;
+ }
+
+ *nplanes = num_planes;
+ for (unsigned int i = 0; i < *nplanes; i++) {
+ unsigned int size = NODE_IS_MPLANE(node) ?
+ node->format.fmt.pix_mp.plane_fmt[i].sizeimage :
+ node->format.fmt.meta.buffersize;
+ sizes[i] = size;
+ }
+
+ dev_dbg(pispbe->dev,
+ "Image (or metadata) size %u, nbuffers %u for node %s\n",
+ sizes[0], *nbuffers, NODE_NAME(node));
+
+ return 0;
+}
+
+static int pispbe_node_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct pispbe_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct pispbe_dev *pispbe = node->pispbe;
+ unsigned int num_planes = NODE_IS_MPLANE(node) ?
+ node->format.fmt.pix_mp.num_planes : 1;
+
+ for (unsigned int i = 0; i < num_planes; i++) {
+ unsigned long size = NODE_IS_MPLANE(node) ?
+ node->format.fmt.pix_mp.plane_fmt[i].sizeimage :
+ node->format.fmt.meta.buffersize;
+
+ if (vb2_plane_size(vb, i) < size) {
+ dev_dbg(pispbe->dev,
+ "data will not fit into plane %d (%lu < %lu)\n",
+ i, vb2_plane_size(vb, i), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, i, size);
+ }
+
+ if (node->id == CONFIG_NODE) {
+ void *dst = &node->pispbe->config[vb->index];
+ void *src = vb2_plane_vaddr(vb, 0);
+
+ memcpy(dst, src, sizeof(struct pisp_be_tiles_config));
+
+ return pisp_be_validate_config(pispbe, dst);
+ }
+
+ return 0;
+}
+
+static void pispbe_node_buffer_queue(struct vb2_buffer *buf)
+{
+ struct vb2_v4l2_buffer *vbuf =
+ container_of(buf, struct vb2_v4l2_buffer, vb2_buf);
+ struct pispbe_buffer *buffer =
+ container_of(vbuf, struct pispbe_buffer, vb);
+ struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue);
+ struct pispbe_dev *pispbe = node->pispbe;
+ unsigned long flags;
+
+ dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
+ spin_lock_irqsave(&node->ready_lock, flags);
+ list_add_tail(&buffer->ready_list, &node->ready_queue);
+ spin_unlock_irqrestore(&node->ready_lock, flags);
+
+ /*
+ * Every time we add a buffer, check if there's now some work for the hw
+ * to do.
+ */
+ pispbe_schedule(pispbe, false);
+}
+
+static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct pispbe_node *node = vb2_get_drv_priv(q);
+ struct pispbe_dev *pispbe = node->pispbe;
+ struct pispbe_buffer *buf, *tmp;
+ unsigned long flags;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(pispbe->dev);
+ if (ret < 0)
+ goto err_return_buffers;
+
+ spin_lock_irqsave(&pispbe->hw_lock, flags);
+ node->pispbe->streaming_map |= BIT(node->id);
+ node->pispbe->sequence = 0;
+ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n",
+ __func__, NODE_NAME(node), count);
+ dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n",
+ node->pispbe->streaming_map);
+
+ /* Maybe we're ready to run. */
+ pispbe_schedule(pispbe, false);
+
+ return 0;
+
+err_return_buffers:
+ spin_lock_irqsave(&pispbe->hw_lock, flags);
+ list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) {
+ list_del(&buf->ready_list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ return ret;
+}
+
+static void pispbe_node_stop_streaming(struct vb2_queue *q)
+{
+ struct pispbe_node *node = vb2_get_drv_priv(q);
+ struct pispbe_dev *pispbe = node->pispbe;
+ struct pispbe_buffer *buf;
+ unsigned long flags;
+
+ /*
+ * Now this is a bit awkward. In a simple M2M device we could just wait
+ * for all queued jobs to complete, but here there's a risk that a
+ * partial set of buffers was queued and cannot be run. For now, just
+ * cancel all buffers stuck in the "ready queue", then wait for any
+ * running job.
+ *
+ * This may return buffers out of order.
+ */
+ dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
+ spin_lock_irqsave(&pispbe->hw_lock, flags);
+ do {
+ unsigned long flags1;
+
+ spin_lock_irqsave(&node->ready_lock, flags1);
+ buf = list_first_entry_or_null(&node->ready_queue,
+ struct pispbe_buffer,
+ ready_list);
+ if (buf) {
+ list_del(&buf->ready_list);
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+ spin_unlock_irqrestore(&node->ready_lock, flags1);
+ } while (buf);
+ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ vb2_wait_for_all_buffers(&node->queue);
+
+ spin_lock_irqsave(&pispbe->hw_lock, flags);
+ pispbe->streaming_map &= ~BIT(node->id);
+ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ pm_runtime_mark_last_busy(pispbe->dev);
+ pm_runtime_put_autosuspend(pispbe->dev);
+
+ dev_dbg(pispbe->dev, "Nodes streaming now 0x%x\n",
+ pispbe->streaming_map);
+}
+
+static const struct vb2_ops pispbe_node_queue_ops = {
+ .queue_setup = pispbe_node_queue_setup,
+ .buf_prepare = pispbe_node_buffer_prepare,
+ .buf_queue = pispbe_node_buffer_queue,
+ .start_streaming = pispbe_node_start_streaming,
+ .stop_streaming = pispbe_node_stop_streaming,
+};
+
+static const struct v4l2_file_operations pispbe_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap
+};
+
+static int pispbe_node_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ strscpy(cap->driver, PISPBE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, PISPBE_NAME, sizeof(cap->card));
+
+ dev_dbg(pispbe->dev, "Caps for node %s: %x and %x (dev %x)\n",
+ NODE_NAME(node), cap->capabilities, cap->device_caps,
+ node->vfd.device_caps);
+
+ return 0;
+}
+
+static int pispbe_node_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
+ dev_dbg(pispbe->dev,
+ "Cannot get capture fmt for output node %s\n",
+ NODE_NAME(node));
+ return -EINVAL;
+ }
+
+ *f = node->format;
+ dev_dbg(pispbe->dev, "Get capture format for node %s\n",
+ NODE_NAME(node));
+
+ return 0;
+}
+
+static int pispbe_node_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ if (NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
+ dev_dbg(pispbe->dev,
+ "Cannot get capture fmt for output node %s\n",
+ NODE_NAME(node));
+ return -EINVAL;
+ }
+
+ *f = node->format;
+ dev_dbg(pispbe->dev, "Get output format for node %s\n",
+ NODE_NAME(node));
+
+ return 0;
+}
+
+static int pispbe_node_g_fmt_meta_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) {
+ dev_dbg(pispbe->dev,
+ "Cannot get capture fmt for meta output node %s\n",
+ NODE_NAME(node));
+ return -EINVAL;
+ }
+
+ *f = node->format;
+ dev_dbg(pispbe->dev, "Get output format for meta node %s\n",
+ NODE_NAME(node));
+
+ return 0;
+}
+
+static const struct pisp_be_format *pispbe_find_fmt(unsigned int fourcc)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(supported_formats); i++) {
+ if (supported_formats[i].fourcc == fourcc)
+ return &supported_formats[i];
+ }
+
+ return NULL;
+}
+
+static void pispbe_set_plane_params(struct v4l2_format *f,
+ const struct pisp_be_format *fmt)
+{
+ unsigned int nplanes = f->fmt.pix_mp.num_planes;
+ unsigned int total_plane_factor = 0;
+
+ for (unsigned int i = 0; i < PISPBE_MAX_PLANES; i++)
+ total_plane_factor += fmt->plane_factor[i];
+
+ for (unsigned int i = 0; i < nplanes; i++) {
+ struct v4l2_plane_pix_format *p = &f->fmt.pix_mp.plane_fmt[i];
+ unsigned int bpl, plane_size;
+
+ bpl = (f->fmt.pix_mp.width * fmt->bit_depth) >> 3;
+ bpl = ALIGN(max(p->bytesperline, bpl), fmt->align);
+
+ plane_size = bpl * f->fmt.pix_mp.height *
+ (nplanes > 1 ? fmt->plane_factor[i] : total_plane_factor);
+ /*
+ * The shift is to divide out the plane_factor fixed point
+ * scaling of 8.
+ */
+ plane_size = max(p->sizeimage, plane_size >> 3);
+
+ p->bytesperline = bpl;
+ p->sizeimage = plane_size;
+ }
+}
+
+static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node)
+{
+ struct pispbe_dev *pispbe = node->pispbe;
+ u32 pixfmt = f->fmt.pix_mp.pixelformat;
+ const struct pisp_be_format *fmt;
+ bool is_rgb;
+
+ dev_dbg(pispbe->dev,
+ "%s: [%s] req %ux%u %p4cc, planes %d\n",
+ __func__, NODE_NAME(node), f->fmt.pix_mp.width,
+ f->fmt.pix_mp.height, &pixfmt,
+ f->fmt.pix_mp.num_planes);
+
+ fmt = pispbe_find_fmt(pixfmt);
+ if (!fmt) {
+ dev_dbg(pispbe->dev,
+ "%s: [%s] Format not found, defaulting to YUV420\n",
+ __func__, NODE_NAME(node));
+ fmt = pispbe_find_fmt(V4L2_PIX_FMT_YUV420);
+ }
+
+ f->fmt.pix_mp.pixelformat = fmt->fourcc;
+ f->fmt.pix_mp.num_planes = fmt->num_planes;
+ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
+ f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u),
+ PISP_BACK_END_MIN_TILE_WIDTH);
+ f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u),
+ PISP_BACK_END_MIN_TILE_HEIGHT);
+
+ /*
+ * Fill in the actual colour space when the requested one was
+ * not supported. This also catches the case when the "default"
+ * colour space was requested (as that's never in the mask).
+ */
+ if (!(V4L2_COLORSPACE_MASK(f->fmt.pix_mp.colorspace) &
+ fmt->colorspace_mask))
+ f->fmt.pix_mp.colorspace = fmt->colorspace_default;
+
+ /* In all cases, we only support the defaults for these: */
+ f->fmt.pix_mp.ycbcr_enc =
+ V4L2_MAP_YCBCR_ENC_DEFAULT(f->fmt.pix_mp.colorspace);
+ f->fmt.pix_mp.xfer_func =
+ V4L2_MAP_XFER_FUNC_DEFAULT(f->fmt.pix_mp.colorspace);
+
+ is_rgb = f->fmt.pix_mp.colorspace == V4L2_COLORSPACE_SRGB;
+ f->fmt.pix_mp.quantization =
+ V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix_mp.colorspace,
+ f->fmt.pix_mp.ycbcr_enc);
+
+ /* Set plane size and bytes/line for each plane. */
+ pispbe_set_plane_params(f, fmt);
+
+ for (unsigned int i = 0; i < f->fmt.pix_mp.num_planes; i++) {
+ dev_dbg(pispbe->dev,
+ "%s: [%s] calc plane %d, %ux%u, depth %u, bpl %u size %u\n",
+ __func__, NODE_NAME(node), i, f->fmt.pix_mp.width,
+ f->fmt.pix_mp.height, fmt->bit_depth,
+ f->fmt.pix_mp.plane_fmt[i].bytesperline,
+ f->fmt.pix_mp.plane_fmt[i].sizeimage);
+ }
+}
+
+static int pispbe_node_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
+ dev_dbg(pispbe->dev,
+ "Cannot set capture fmt for output node %s\n",
+ NODE_NAME(node));
+ return -EINVAL;
+ }
+
+ pispbe_try_format(f, node);
+
+ return 0;
+}
+
+static int pispbe_node_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ if (!NODE_IS_OUTPUT(node) || NODE_IS_META(node)) {
+ dev_dbg(pispbe->dev,
+ "Cannot set capture fmt for output node %s\n",
+ NODE_NAME(node));
+ return -EINVAL;
+ }
+
+ pispbe_try_format(f, node);
+
+ return 0;
+}
+
+static int pispbe_node_try_fmt_meta_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) {
+ dev_dbg(pispbe->dev,
+ "Cannot set capture fmt for meta output node %s\n",
+ NODE_NAME(node));
+ return -EINVAL;
+ }
+
+ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG;
+ f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config);
+
+ return 0;
+}
+
+static int pispbe_node_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+ int ret;
+
+ ret = pispbe_node_try_fmt_vid_cap(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ if (vb2_is_busy(&node->queue))
+ return -EBUSY;
+
+ node->format = *f;
+ node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat);
+
+ dev_dbg(pispbe->dev, "Set capture format for node %s to %p4cc\n",
+ NODE_NAME(node), &f->fmt.pix_mp.pixelformat);
+
+ return 0;
+}
+
+static int pispbe_node_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+ int ret;
+
+ ret = pispbe_node_try_fmt_vid_out(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ if (vb2_is_busy(&node->queue))
+ return -EBUSY;
+
+ node->format = *f;
+ node->pisp_format = pispbe_find_fmt(f->fmt.pix_mp.pixelformat);
+
+ dev_dbg(pispbe->dev, "Set output format for node %s to %p4cc\n",
+ NODE_NAME(node), &f->fmt.pix_mp.pixelformat);
+
+ return 0;
+}
+
+static int pispbe_node_s_fmt_meta_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+ int ret;
+
+ ret = pispbe_node_try_fmt_meta_out(file, priv, f);
+ if (ret < 0)
+ return ret;
+
+ if (vb2_is_busy(&node->queue))
+ return -EBUSY;
+
+ node->format = *f;
+ node->pisp_format = &meta_out_supported_formats[0];
+
+ dev_dbg(pispbe->dev, "Set output format for meta node %s to %p4cc\n",
+ NODE_NAME(node), &f->fmt.meta.dataformat);
+
+ return 0;
+}
+
+static int pispbe_node_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct pispbe_node *node = video_drvdata(file);
+
+ if (f->type != node->queue.type)
+ return -EINVAL;
+
+ if (NODE_IS_META(node)) {
+ if (f->index)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_META_FMT_RPI_BE_CFG;
+ f->flags = 0;
+ return 0;
+ }
+
+ if (f->index >= ARRAY_SIZE(supported_formats))
+ return -EINVAL;
+
+ f->pixelformat = supported_formats[f->index].fourcc;
+ f->flags = 0;
+
+ return 0;
+}
+
+static int pispbe_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct pispbe_node *node = video_drvdata(file);
+ struct pispbe_dev *pispbe = node->pispbe;
+
+ if (NODE_IS_META(node) || fsize->index)
+ return -EINVAL;
+
+ if (!pispbe_find_fmt(fsize->pixel_format)) {
+ dev_dbg(pispbe->dev, "Invalid pixel code: %x\n",
+ fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = 32;
+ fsize->stepwise.max_width = 65535;
+ fsize->stepwise.step_width = 2;
+
+ fsize->stepwise.min_height = 32;
+ fsize->stepwise.max_height = 65535;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops pispbe_node_ioctl_ops = {
+ .vidioc_querycap = pispbe_node_querycap,
+ .vidioc_g_fmt_vid_cap_mplane = pispbe_node_g_fmt_vid_cap,
+ .vidioc_g_fmt_vid_out_mplane = pispbe_node_g_fmt_vid_out,
+ .vidioc_g_fmt_meta_out = pispbe_node_g_fmt_meta_out,
+ .vidioc_try_fmt_vid_cap_mplane = pispbe_node_try_fmt_vid_cap,
+ .vidioc_try_fmt_vid_out_mplane = pispbe_node_try_fmt_vid_out,
+ .vidioc_try_fmt_meta_out = pispbe_node_try_fmt_meta_out,
+ .vidioc_s_fmt_vid_cap_mplane = pispbe_node_s_fmt_vid_cap,
+ .vidioc_s_fmt_vid_out_mplane = pispbe_node_s_fmt_vid_out,
+ .vidioc_s_fmt_meta_out = pispbe_node_s_fmt_meta_out,
+ .vidioc_enum_fmt_vid_cap = pispbe_node_enum_fmt,
+ .vidioc_enum_fmt_vid_out = pispbe_node_enum_fmt,
+ .vidioc_enum_fmt_meta_out = pispbe_node_enum_fmt,
+ .vidioc_enum_framesizes = pispbe_enum_framesizes,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct video_device pispbe_videodev = {
+ .name = PISPBE_NAME,
+ .vfl_dir = VFL_DIR_M2M, /* gets overwritten */
+ .fops = &pispbe_fops,
+ .ioctl_ops = &pispbe_node_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release_empty,
+};
+
+static void pispbe_node_def_fmt(struct pispbe_node *node)
+{
+ if (NODE_IS_META(node) && NODE_IS_OUTPUT(node)) {
+ /* Config node */
+ struct v4l2_format *f = &node->format;
+
+ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG;
+ f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config);
+ f->type = node->buf_type;
+ } else {
+ struct v4l2_format f = {
+ .fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420,
+ .fmt.pix_mp.width = 1920,
+ .fmt.pix_mp.height = 1080,
+ .type = node->buf_type,
+ };
+ pispbe_try_format(&f, node);
+ node->format = f;
+ }
+
+ node->pisp_format = pispbe_find_fmt(node->format.fmt.pix_mp.pixelformat);
+}
+
+/*
+ * Initialise a struct pispbe_node and register it as /dev/video<N>
+ * to represent one of the PiSP Back End's input or output streams.
+ */
+static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id)
+{
+ bool output = NODE_DESC_IS_OUTPUT(&node_desc[id]);
+ struct pispbe_node *node = &pispbe->node[id];
+ struct media_entity *entity = &node->vfd.entity;
+ struct video_device *vdev = &node->vfd;
+ struct vb2_queue *q = &node->queue;
+ int ret;
+
+ node->id = id;
+ node->pispbe = pispbe;
+ node->buf_type = node_desc[id].buf_type;
+
+ mutex_init(&node->node_lock);
+ mutex_init(&node->queue_lock);
+ INIT_LIST_HEAD(&node->ready_queue);
+ spin_lock_init(&node->ready_lock);
+
+ node->format.type = node->buf_type;
+ pispbe_node_def_fmt(node);
+
+ q->type = node->buf_type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->drv_priv = node;
+ q->ops = &pispbe_node_queue_ops;
+ q->buf_struct_size = sizeof(struct pispbe_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->dev = pispbe->dev;
+ /* get V4L2 to handle node->queue locking */
+ q->lock = &node->queue_lock;
+
+ ret = vb2_queue_init(q);
+ if (ret < 0) {
+ dev_err(pispbe->dev, "vb2_queue_init failed\n");
+ goto err_mutex_destroy;
+ }
+
+ *vdev = pispbe_videodev; /* default initialization */
+ strscpy(vdev->name, node_desc[id].ent_name, sizeof(vdev->name));
+ vdev->v4l2_dev = &pispbe->v4l2_dev;
+ vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
+ /* get V4L2 to serialise our ioctls */
+ vdev->lock = &node->node_lock;
+ vdev->queue = &node->queue;
+ vdev->device_caps = V4L2_CAP_STREAMING | node_desc[id].caps;
+
+ node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(entity, 1, &node->pad);
+ if (ret) {
+ dev_err(pispbe->dev,
+ "Failed to register media pads for %s device node\n",
+ NODE_NAME(node));
+ goto err_unregister_queue;
+ }
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(pispbe->dev,
+ "Failed to register video %s device node\n",
+ NODE_NAME(node));
+ goto err_unregister_queue;
+ }
+ video_set_drvdata(vdev, node);
+
+ if (output)
+ ret = media_create_pad_link(entity, 0, &pispbe->sd.entity,
+ id, MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ else
+ ret = media_create_pad_link(&pispbe->sd.entity, id, entity,
+ 0, MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ goto err_unregister_video_dev;
+
+ dev_dbg(pispbe->dev, "%s device node registered as /dev/video%d\n",
+ NODE_NAME(node), node->vfd.num);
+
+ return 0;
+
+err_unregister_video_dev:
+ video_unregister_device(&node->vfd);
+err_unregister_queue:
+ vb2_queue_release(&node->queue);
+err_mutex_destroy:
+ mutex_destroy(&node->node_lock);
+ mutex_destroy(&node->queue_lock);
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops pispbe_pad_ops = {
+ .link_validate = v4l2_subdev_link_validate_default,
+};
+
+static const struct v4l2_subdev_ops pispbe_sd_ops = {
+ .pad = &pispbe_pad_ops,
+};
+
+static int pispbe_init_subdev(struct pispbe_dev *pispbe)
+{
+ struct v4l2_subdev *sd = &pispbe->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &pispbe_sd_ops);
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->owner = THIS_MODULE;
+ sd->dev = pispbe->dev;
+ strscpy(sd->name, PISPBE_NAME, sizeof(sd->name));
+
+ for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++)
+ pispbe->pad[i].flags =
+ NODE_DESC_IS_OUTPUT(&node_desc[i]) ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, PISPBE_NUM_NODES,
+ pispbe->pad);
+ if (ret)
+ goto error;
+
+ ret = v4l2_device_register_subdev(&pispbe->v4l2_dev, sd);
+ if (ret)
+ goto error;
+
+ return 0;
+
+error:
+ media_entity_cleanup(&sd->entity);
+ return ret;
+}
+
+static int pispbe_init_devices(struct pispbe_dev *pispbe)
+{
+ struct v4l2_device *v4l2_dev;
+ struct media_device *mdev;
+ unsigned int num_regist;
+ int ret;
+
+ /* Register v4l2_device and media_device */
+ mdev = &pispbe->mdev;
+ mdev->hw_revision = pispbe->hw_version;
+ mdev->dev = pispbe->dev;
+ strscpy(mdev->model, PISPBE_NAME, sizeof(mdev->model));
+ media_device_init(mdev);
+
+ v4l2_dev = &pispbe->v4l2_dev;
+ v4l2_dev->mdev = &pispbe->mdev;
+ strscpy(v4l2_dev->name, PISPBE_NAME, sizeof(v4l2_dev->name));
+
+ ret = v4l2_device_register(pispbe->dev, v4l2_dev);
+ if (ret)
+ goto err_media_dev_cleanup;
+
+ /* Register the PISPBE subdevice. */
+ ret = pispbe_init_subdev(pispbe);
+ if (ret)
+ goto err_unregister_v4l2;
+
+ /* Create device video nodes */
+ for (num_regist = 0; num_regist < PISPBE_NUM_NODES; num_regist++) {
+ ret = pispbe_init_node(pispbe, num_regist);
+ if (ret)
+ goto err_unregister_nodes;
+ }
+
+ ret = media_device_register(mdev);
+ if (ret)
+ goto err_unregister_nodes;
+
+ pispbe->config =
+ dma_alloc_coherent(pispbe->dev,
+ sizeof(struct pisp_be_tiles_config) *
+ PISP_BE_NUM_CONFIG_BUFFERS,
+ &pispbe->config_dma_addr, GFP_KERNEL);
+ if (!pispbe->config) {
+ dev_err(pispbe->dev, "Unable to allocate cached config buffers.\n");
+ ret = -ENOMEM;
+ goto err_unregister_mdev;
+ }
+
+ return 0;
+
+err_unregister_mdev:
+ media_device_unregister(mdev);
+err_unregister_nodes:
+ while (num_regist-- > 0) {
+ video_unregister_device(&pispbe->node[num_regist].vfd);
+ vb2_queue_release(&pispbe->node[num_regist].queue);
+ }
+ v4l2_device_unregister_subdev(&pispbe->sd);
+ media_entity_cleanup(&pispbe->sd.entity);
+err_unregister_v4l2:
+ v4l2_device_unregister(v4l2_dev);
+err_media_dev_cleanup:
+ media_device_cleanup(mdev);
+ return ret;
+}
+
+static void pispbe_destroy_devices(struct pispbe_dev *pispbe)
+{
+ if (pispbe->config) {
+ dma_free_coherent(pispbe->dev,
+ sizeof(struct pisp_be_tiles_config) *
+ PISP_BE_NUM_CONFIG_BUFFERS,
+ pispbe->config,
+ pispbe->config_dma_addr);
+ }
+
+ dev_dbg(pispbe->dev, "Unregister from media controller\n");
+
+ v4l2_device_unregister_subdev(&pispbe->sd);
+ media_entity_cleanup(&pispbe->sd.entity);
+ media_device_unregister(&pispbe->mdev);
+
+ for (int i = PISPBE_NUM_NODES - 1; i >= 0; i--) {
+ video_unregister_device(&pispbe->node[i].vfd);
+ vb2_queue_release(&pispbe->node[i].queue);
+ mutex_destroy(&pispbe->node[i].node_lock);
+ mutex_destroy(&pispbe->node[i].queue_lock);
+ }
+
+ media_device_cleanup(&pispbe->mdev);
+ v4l2_device_unregister(&pispbe->v4l2_dev);
+}
+
+static int pispbe_runtime_suspend(struct device *dev)
+{
+ struct pispbe_dev *pispbe = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(pispbe->clk);
+
+ return 0;
+}
+
+static int pispbe_runtime_resume(struct device *dev)
+{
+ struct pispbe_dev *pispbe = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(pispbe->clk);
+ if (ret) {
+ dev_err(dev, "Unable to enable clock\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: Enabled clock, rate=%lu\n",
+ __func__, clk_get_rate(pispbe->clk));
+
+ return 0;
+}
+
+static int pispbe_hw_init(struct pispbe_dev *pispbe)
+{
+ u32 u;
+
+ /* Check the HW is present and has a known version */
+ u = pispbe_rd(pispbe, PISP_BE_VERSION_REG);
+ dev_dbg(pispbe->dev, "pispbe_probe: HW version: 0x%08x", u);
+ pispbe->hw_version = u;
+ if ((u & ~PISP_BE_VERSION_MINOR_BITS) != PISP_BE_VERSION_2712)
+ return -ENODEV;
+
+ /* Clear leftover interrupts */
+ pispbe_wr(pispbe, PISP_BE_INTERRUPT_STATUS_REG, 0xFFFFFFFFu);
+ u = pispbe_rd(pispbe, PISP_BE_BATCH_STATUS_REG);
+ dev_dbg(pispbe->dev, "pispbe_probe: BatchStatus: 0x%08x", u);
+
+ pispbe->done = (uint8_t)u;
+ pispbe->started = (uint8_t)(u >> 8);
+ u = pispbe_rd(pispbe, PISP_BE_STATUS_REG);
+ dev_dbg(pispbe->dev, "pispbe_probe: Status: 0x%08x", u);
+
+ if (u != 0 || pispbe->done != pispbe->started) {
+ dev_err(pispbe->dev, "pispbe_probe: HW is stuck or busy\n");
+ return -EBUSY;
+ }
+
+ /*
+ * AXI QOS=0, CACHE=4'b0010, PROT=3'b011
+ * Also set "chicken bits" 22:20 which enable sub-64-byte bursts
+ * and AXI AWID/BID variability (on versions which support this).
+ */
+ pispbe_wr(pispbe, PISP_BE_AXI_REG, 0x32703200u);
+
+ /* Enable both interrupt flags */
+ pispbe_wr(pispbe, PISP_BE_INTERRUPT_EN_REG, 0x00000003u);
+
+ return 0;
+}
+
+/* Probe the ISP-BE hardware block, as a single platform device. */
+static int pispbe_probe(struct platform_device *pdev)
+{
+ struct pispbe_dev *pispbe;
+ int ret;
+
+ pispbe = devm_kzalloc(&pdev->dev, sizeof(*pispbe), GFP_KERNEL);
+ if (!pispbe)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, pispbe);
+ pispbe->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pispbe);
+
+ pispbe->be_reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pispbe->be_reg_base)) {
+ dev_err(&pdev->dev, "Failed to get ISP-BE registers address\n");
+ return PTR_ERR(pispbe->be_reg_base);
+ }
+
+ pispbe->irq = platform_get_irq(pdev, 0);
+ if (pispbe->irq <= 0)
+ return -EINVAL;
+
+ ret = devm_request_irq(&pdev->dev, pispbe->irq, pispbe_isr, 0,
+ PISPBE_NAME, pispbe);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request interrupt\n");
+ return ret;
+ }
+
+ ret = dma_set_mask_and_coherent(pispbe->dev, DMA_BIT_MASK(36));
+ if (ret)
+ return ret;
+
+ pispbe->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pispbe->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pispbe->clk),
+ "Failed to get clock");
+
+ /* Hardware initialisation */
+ pm_runtime_set_autosuspend_delay(pispbe->dev, 200);
+ pm_runtime_use_autosuspend(pispbe->dev);
+ pm_runtime_enable(pispbe->dev);
+
+ ret = pispbe_runtime_resume(pispbe->dev);
+ if (ret)
+ goto pm_runtime_disable_err;
+
+ pispbe->hw_busy = false;
+ spin_lock_init(&pispbe->hw_lock);
+ ret = pispbe_hw_init(pispbe);
+ if (ret)
+ goto pm_runtime_suspend_err;
+
+ ret = pispbe_init_devices(pispbe);
+ if (ret)
+ goto disable_devs_err;
+
+ pm_runtime_mark_last_busy(pispbe->dev);
+ pm_runtime_put_autosuspend(pispbe->dev);
+
+ return 0;
+
+disable_devs_err:
+ pispbe_destroy_devices(pispbe);
+pm_runtime_suspend_err:
+ pispbe_runtime_suspend(pispbe->dev);
+pm_runtime_disable_err:
+ pm_runtime_dont_use_autosuspend(pispbe->dev);
+ pm_runtime_disable(pispbe->dev);
+
+ return ret;
+}
+
+static void pispbe_remove(struct platform_device *pdev)
+{
+ struct pispbe_dev *pispbe = platform_get_drvdata(pdev);
+
+ pispbe_destroy_devices(pispbe);
+
+ pispbe_runtime_suspend(pispbe->dev);
+ pm_runtime_dont_use_autosuspend(pispbe->dev);
+ pm_runtime_disable(pispbe->dev);
+}
+
+static const struct dev_pm_ops pispbe_pm_ops = {
+ SET_RUNTIME_PM_OPS(pispbe_runtime_suspend, pispbe_runtime_resume, NULL)
+};
+
+static const struct of_device_id pispbe_of_match[] = {
+ {
+ .compatible = "raspberrypi,pispbe",
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, pispbe_of_match);
+
+static struct platform_driver pispbe_pdrv = {
+ .probe = pispbe_probe,
+ .remove = pispbe_remove,
+ .driver = {
+ .name = PISPBE_NAME,
+ .of_match_table = pispbe_of_match,
+ .pm = &pispbe_pm_ops,
+ },
+};
+
+module_platform_driver(pispbe_pdrv);
+
+MODULE_DESCRIPTION("PiSP Back End driver");
+MODULE_AUTHOR("David Plowman <david.plowman@raspberrypi.com>");
+MODULE_AUTHOR("Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
new file mode 100644
index 000000000000..b5cb7b8c7531
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
@@ -0,0 +1,519 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PiSP Back End driver image format definitions.
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd
+ */
+
+#ifndef _PISP_BE_FORMATS_
+#define _PISP_BE_FORMATS_
+
+#include <linux/bits.h>
+#include <linux/videodev2.h>
+
+#define PISPBE_MAX_PLANES 3
+#define P3(x) ((x) * 8)
+
+struct pisp_be_format {
+ unsigned int fourcc;
+ unsigned int align;
+ unsigned int bit_depth;
+ /* 0P3 factor for plane sizing */
+ unsigned int plane_factor[PISPBE_MAX_PLANES];
+ unsigned int num_planes;
+ unsigned int colorspace_mask;
+ enum v4l2_colorspace colorspace_default;
+};
+
+#define V4L2_COLORSPACE_MASK(colorspace) BIT(colorspace)
+
+#define V4L2_COLORSPACE_MASK_JPEG \
+ V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_JPEG)
+#define V4L2_COLORSPACE_MASK_SMPTE170M \
+ V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SMPTE170M)
+#define V4L2_COLORSPACE_MASK_REC709 \
+ V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_REC709)
+#define V4L2_COLORSPACE_MASK_SRGB \
+ V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SRGB)
+#define V4L2_COLORSPACE_MASK_RAW \
+ V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_RAW)
+
+/*
+ * All three colour spaces SRGB, SMPTE170M and REC709 are fundamentally sRGB
+ * underneath (as near as makes no difference to us), just with different YCbCr
+ * encodings. Therefore the ISP can generate sRGB on its main output and any of
+ * the others on its low resolution output. Applications should, when using both
+ * outputs, program the colour spaces on them to be the same, matching whatever
+ * is requested for the low resolution output, even if the main output is
+ * producing an RGB format. In turn this requires us to allow all these colour
+ * spaces for every YUV/RGB output format.
+ */
+#define V4L2_COLORSPACE_MASK_ALL_SRGB (V4L2_COLORSPACE_MASK_JPEG | \
+ V4L2_COLORSPACE_MASK_SRGB | \
+ V4L2_COLORSPACE_MASK_SMPTE170M | \
+ V4L2_COLORSPACE_MASK_REC709)
+
+static const struct pisp_be_format supported_formats[] = {
+ /* Single plane YUV formats */
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ /* 128 alignment to ensure U/V planes are 64 byte aligned. */
+ .align = 128,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ /* 128 alignment to ensure U/V planes are 64 byte aligned. */
+ .align = 128,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .align = 32,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.5) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .align = 32,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.5) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .align = 64,
+ .bit_depth = 16,
+ .plane_factor = { P3(1) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .align = 64,
+ .bit_depth = 16,
+ .plane_factor = { P3(1) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .align = 64,
+ .bit_depth = 16,
+ .plane_factor = { P3(1) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .align = 64,
+ .bit_depth = 16,
+ .plane_factor = { P3(1) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ /* Multiplane YUV formats */
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420M,
+ .align = 64,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
+ .num_planes = 3,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .align = 32,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.5) },
+ .num_planes = 2,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .align = 32,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.5) },
+ .num_planes = 2,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU420M,
+ .align = 64,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
+ .num_planes = 3,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422M,
+ .align = 64,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.5), P3(0.5) },
+ .num_planes = 3,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU422M,
+ .align = 64,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(0.5), P3(0.5) },
+ .num_planes = 3,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV444M,
+ .align = 64,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(1), P3(1) },
+ .num_planes = 3,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU444M,
+ .align = 64,
+ .bit_depth = 8,
+ .plane_factor = { P3(1), P3(1), P3(1) },
+ .num_planes = 3,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
+ },
+ /* RGB formats */
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .align = 32,
+ .bit_depth = 24,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .align = 32,
+ .bit_depth = 24,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .align = 64,
+ .bit_depth = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGBX32,
+ .align = 64,
+ .bit_depth = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB48,
+ .align = 64,
+ .bit_depth = 48,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SRGB,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR48,
+ .align = 64,
+ .bit_depth = 48,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SRGB,
+ },
+ /* Bayer formats - 8-bit */
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ /* Bayer formats - 16-bit */
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ /* Bayer formats unpacked to 16bpp */
+ /* 10 bit */
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ /* 12 bit */
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ /* 14 bit */
+ .fourcc = V4L2_PIX_FMT_SRGGB14,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR14,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG14,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG14,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ /* Bayer formats - 16-bit PiSP Compressed */
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ /* Greyscale Formats */
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .bit_depth = 8,
+ .align = 32,
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y16,
+ .bit_depth = 16,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO,
+ .bit_depth = 8,
+ .align = 32,
+ .plane_factor = { P3(1.0) },
+ .num_planes = 1,
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
+};
+
+static const struct pisp_be_format meta_out_supported_formats[] = {
+ /* Configuration buffer format. */
+ {
+ .fourcc = V4L2_META_FMT_RPI_BE_CFG,
+ },
+};
+
+#endif /* _PISP_BE_FORMATS_ */
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig
new file mode 100644
index 000000000000..327b61f1134b
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/Kconfig
@@ -0,0 +1,15 @@
+# RP1 V4L2 camera support
+
+config VIDEO_RP1_CFE
+ tristate "Raspberry Pi RP1 Camera Front End (CFE) video capture driver"
+ depends on VIDEO_DEV
+ depends on PM
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_FWNODE
+ help
+ Say Y here to enable support for the Raspberry Pi RP1 Camera Front End.
+
+ To compile this driver as a module, choose M here. The module will be
+ called rp1-cfe.
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/Makefile b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile
new file mode 100644
index 000000000000..3f0d0fc8570e
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for RP1 Camera Front End driver
+#
+rp1-cfe-objs := cfe.o csi2.o pisp-fe.o dphy.o
+obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h
new file mode 100644
index 000000000000..7aecf7f83733
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h
@@ -0,0 +1,332 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RP1 Camera Front End formats definition
+ *
+ * Copyright (C) 2021-2024 - Raspberry Pi Ltd.
+ */
+#ifndef _CFE_FMTS_H_
+#define _CFE_FMTS_H_
+
+#include "cfe.h"
+#include <media/mipi-csi2.h>
+
+static const struct cfe_fmt formats[] = {
+ /* YUV Formats */
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_YUV422_8B,
+ },
+ {
+ /* RGB Formats */
+ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RGB565,
+ },
+ { .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RGB565,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RGB555,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
+ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RGB555,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
+ .code = MEDIA_BUS_FMT_BGR888_1X24,
+ .depth = 24,
+ .csi_dt = MIPI_CSI2_DT_RGB888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
+ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
+ .depth = 32,
+ .csi_dt = 0x0,
+ },
+
+ /* Bayer Formats */
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR8,
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10P,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10P,
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10P,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10P,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR12P,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG12P,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG12P,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB12P,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR14P,
+ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG14P,
+ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG14P,
+ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB14P,
+ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR16,
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+ },
+ /* PiSP Compressed Mode 1 */
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
+ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
+ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
+ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+ /* Greyscale format */
+ {
+ .fourcc = V4L2_PIX_FMT_GREY,
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_RAW8,
+ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y10P,
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_RAW10,
+ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y12P,
+ .code = MEDIA_BUS_FMT_Y12_1X12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y14P,
+ .code = MEDIA_BUS_FMT_Y14_1X14,
+ .depth = 14,
+ .csi_dt = MIPI_CSI2_DT_RAW14,
+ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y16,
+ .code = MEDIA_BUS_FMT_Y16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO,
+ .code = MEDIA_BUS_FMT_Y16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+
+ /* Embedded data formats */
+ {
+ .fourcc = V4L2_META_FMT_GENERIC_8,
+ .code = MEDIA_BUS_FMT_META_8,
+ .depth = 8,
+ .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B,
+ .flags = CFE_FORMAT_FLAG_META_CAP,
+ },
+ {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_10,
+ .code = MEDIA_BUS_FMT_META_10,
+ .depth = 10,
+ .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B,
+ .flags = CFE_FORMAT_FLAG_META_CAP,
+ },
+ {
+ .fourcc = V4L2_META_FMT_GENERIC_CSI2_12,
+ .code = MEDIA_BUS_FMT_META_12,
+ .depth = 12,
+ .csi_dt = MIPI_CSI2_DT_EMBEDDED_8B,
+ .flags = CFE_FORMAT_FLAG_META_CAP,
+ },
+
+ /* Frontend formats */
+ {
+ .fourcc = V4L2_META_FMT_RPI_FE_CFG,
+ .code = MEDIA_BUS_FMT_FIXED,
+ .flags = CFE_FORMAT_FLAG_META_OUT,
+ },
+ {
+ .fourcc = V4L2_META_FMT_RPI_FE_STATS,
+ .code = MEDIA_BUS_FMT_FIXED,
+ .flags = CFE_FORMAT_FLAG_META_CAP,
+ },
+};
+
+#endif /* _CFE_FMTS_H_ */
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h
new file mode 100644
index 000000000000..1a36259f51b7
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h
@@ -0,0 +1,202 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2024 Raspberry Pi Ltd.
+ * Copyright (c) 2024 Ideas on Board Oy
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cfe
+
+#if !defined(_CFE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _CFE_TRACE_H
+
+#include <linux/tracepoint.h>
+#include <media/videobuf2-v4l2.h>
+
+TRACE_EVENT(cfe_return_buffer,
+ TP_PROTO(u32 node_id, u32 buf_idx, u32 queue_id),
+ TP_ARGS(node_id, buf_idx, queue_id),
+ TP_STRUCT__entry(
+ __field(u32, node_id)
+ __field(u32, buf_idx)
+ __field(u32, queue_id)
+ ),
+ TP_fast_assign(
+ __entry->node_id = node_id;
+ __entry->buf_idx = buf_idx;
+ __entry->queue_id = queue_id;
+ ),
+ TP_printk("node=%u buf=%u, queue=%u", __entry->node_id,
+ __entry->buf_idx, __entry->queue_id)
+);
+
+DECLARE_EVENT_CLASS(cfe_buffer_template,
+ TP_PROTO(u32 node_id, struct vb2_buffer *buf),
+ TP_ARGS(node_id, buf),
+ TP_STRUCT__entry(
+ __field(u32, node_id)
+ __field(u32, buf_idx)
+ ),
+ TP_fast_assign(
+ __entry->node_id = node_id;
+ __entry->buf_idx = buf->index;
+ ),
+ TP_printk("node=%u buf=%u", __entry->node_id, __entry->buf_idx)
+);
+
+DEFINE_EVENT(cfe_buffer_template, cfe_buffer_prepare,
+ TP_PROTO(u32 node_id, struct vb2_buffer *buf),
+ TP_ARGS(node_id, buf));
+
+TRACE_EVENT(cfe_buffer_queue,
+ TP_PROTO(u32 node_id, struct vb2_buffer *buf, bool schedule_now),
+ TP_ARGS(node_id, buf, schedule_now),
+ TP_STRUCT__entry(
+ __field(u32, node_id)
+ __field(u32, buf_idx)
+ __field(bool, schedule_now)
+ ),
+ TP_fast_assign(
+ __entry->node_id = node_id;
+ __entry->buf_idx = buf->index;
+ __entry->schedule_now = schedule_now;
+ ),
+ TP_printk("node=%u buf=%u%s", __entry->node_id, __entry->buf_idx,
+ __entry->schedule_now ? " schedule immediately" : "")
+);
+
+DEFINE_EVENT(cfe_buffer_template, cfe_csi2_schedule,
+ TP_PROTO(u32 node_id, struct vb2_buffer *buf),
+ TP_ARGS(node_id, buf));
+
+DEFINE_EVENT(cfe_buffer_template, cfe_fe_schedule,
+ TP_PROTO(u32 node_id, struct vb2_buffer *buf),
+ TP_ARGS(node_id, buf));
+
+TRACE_EVENT(cfe_buffer_complete,
+ TP_PROTO(u32 node_id, struct vb2_v4l2_buffer *buf),
+ TP_ARGS(node_id, buf),
+ TP_STRUCT__entry(
+ __field(u32, node_id)
+ __field(u32, buf_idx)
+ __field(u32, seq)
+ __field(u64, ts)
+ ),
+ TP_fast_assign(
+ __entry->node_id = node_id;
+ __entry->buf_idx = buf->vb2_buf.index;
+ __entry->seq = buf->sequence;
+ __entry->ts = buf->vb2_buf.timestamp;
+ ),
+ TP_printk("node=%u buf=%u seq=%u ts=%llu", __entry->node_id,
+ __entry->buf_idx, __entry->seq, __entry->ts)
+);
+
+TRACE_EVENT(cfe_frame_start,
+ TP_PROTO(u32 node_id, u32 fs_count),
+ TP_ARGS(node_id, fs_count),
+ TP_STRUCT__entry(
+ __field(u32, node_id)
+ __field(u32, fs_count)
+ ),
+ TP_fast_assign(
+ __entry->node_id = node_id;
+ __entry->fs_count = fs_count;
+ ),
+ TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count)
+);
+
+TRACE_EVENT(cfe_frame_end,
+ TP_PROTO(u32 node_id, u32 fs_count),
+ TP_ARGS(node_id, fs_count),
+ TP_STRUCT__entry(
+ __field(u32, node_id)
+ __field(u32, fs_count)
+ ),
+ TP_fast_assign(
+ __entry->node_id = node_id;
+ __entry->fs_count = fs_count;
+ ),
+ TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count)
+);
+
+TRACE_EVENT(cfe_prepare_next_job,
+ TP_PROTO(bool fe_enabled),
+ TP_ARGS(fe_enabled),
+ TP_STRUCT__entry(
+ __field(bool, fe_enabled)
+ ),
+ TP_fast_assign(
+ __entry->fe_enabled = fe_enabled;
+ ),
+ TP_printk("fe_enabled=%u", __entry->fe_enabled)
+);
+
+/* These are copied from csi2.c */
+#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x))
+#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x))
+#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x))
+#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x))
+#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x))
+
+TRACE_EVENT(csi2_irq,
+ TP_PROTO(u32 channel, u32 status, u32 dbg),
+ TP_ARGS(channel, status, dbg),
+ TP_STRUCT__entry(
+ __field(u32, channel)
+ __field(u32, status)
+ __field(u32, dbg)
+ ),
+ TP_fast_assign(
+ __entry->channel = channel;
+ __entry->status = status;
+ __entry->dbg = dbg;
+ ),
+ TP_printk("ch=%u flags=[ %s%s%s%s%s] frame=%u line=%u\n",
+ __entry->channel,
+ (__entry->status & CSI2_STATUS_IRQ_FS(__entry->channel)) ?
+ "FS " : "",
+ (__entry->status & CSI2_STATUS_IRQ_FE(__entry->channel)) ?
+ "FE " : "",
+ (__entry->status & CSI2_STATUS_IRQ_FE_ACK(__entry->channel)) ?
+ "FE_ACK " : "",
+ (__entry->status & CSI2_STATUS_IRQ_LE(__entry->channel)) ?
+ "LE " : "",
+ (__entry->status & CSI2_STATUS_IRQ_LE_ACK(__entry->channel)) ?
+ "LE_ACK " : "",
+ __entry->dbg >> 16, __entry->dbg & 0xffff)
+);
+
+TRACE_EVENT(fe_irq,
+ TP_PROTO(u32 status, u32 output_status, u32 frame_status,
+ u32 error_status, u32 int_status),
+ TP_ARGS(status, output_status, frame_status, error_status, int_status),
+ TP_STRUCT__entry(
+ __field(u32, status)
+ __field(u32, output_status)
+ __field(u32, frame_status)
+ __field(u32, error_status)
+ __field(u32, int_status)
+ ),
+ TP_fast_assign(
+ __entry->status = status;
+ __entry->output_status = output_status;
+ __entry->frame_status = frame_status;
+ __entry->error_status = error_status;
+ __entry->int_status = int_status;
+ ),
+ TP_printk("status 0x%x out_status 0x%x frame_status 0x%x error_status 0x%x int_status 0x%x",
+ __entry->status,
+ __entry->output_status,
+ __entry->frame_status,
+ __entry->error_status,
+ __entry->int_status)
+);
+
+#endif /* _CFE_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE ../../drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace
+#include <trace/define_trace.h>
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
new file mode 100644
index 000000000000..fcadb2143c88
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
@@ -0,0 +1,2508 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RP1 Camera Front End Driver
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ * Copyright (c) 2023-2024 Ideas on Board Oy
+ */
+
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/lcm.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <linux/media/raspberrypi/pisp_fe_config.h>
+#include <linux/media/raspberrypi/pisp_fe_statistics.h>
+
+#include "cfe-fmts.h"
+#include "cfe.h"
+#include "csi2.h"
+#include "pisp-fe.h"
+
+#define CREATE_TRACE_POINTS
+#include "cfe-trace.h"
+
+#define CFE_MODULE_NAME "rp1-cfe"
+#define CFE_VERSION "1.0"
+
+#define cfe_dbg(cfe, fmt, arg...) dev_dbg(&(cfe)->pdev->dev, fmt, ##arg)
+#define cfe_info(cfe, fmt, arg...) dev_info(&(cfe)->pdev->dev, fmt, ##arg)
+#define cfe_err(cfe, fmt, arg...) dev_err(&(cfe)->pdev->dev, fmt, ##arg)
+
+/* MIPICFG registers */
+#define MIPICFG_CFG 0x004
+#define MIPICFG_INTR 0x028
+#define MIPICFG_INTE 0x02c
+#define MIPICFG_INTF 0x030
+#define MIPICFG_INTS 0x034
+
+#define MIPICFG_CFG_SEL_CSI BIT(0)
+
+#define MIPICFG_INT_CSI_DMA BIT(0)
+#define MIPICFG_INT_CSI_HOST BIT(2)
+#define MIPICFG_INT_PISP_FE BIT(4)
+
+#define BPL_ALIGNMENT 16
+#define MAX_BYTESPERLINE 0xffffff00
+#define MAX_BUFFER_SIZE 0xffffff00
+/*
+ * Max width is therefore determined by the max stride divided by the number of
+ * bits per pixel.
+ *
+ * However, to avoid overflow issues let's use a 16k maximum. This lets us
+ * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful
+ * review and adjustment of the code is needed so that it will deal with
+ * overflows correctly.
+ */
+#define MAX_WIDTH 16384
+#define MAX_HEIGHT MAX_WIDTH
+/* Define a nominal minimum image size */
+#define MIN_WIDTH 16
+#define MIN_HEIGHT 16
+
+#define MIN_META_WIDTH 4
+#define MIN_META_HEIGHT 1
+
+const struct v4l2_mbus_framefmt cfe_default_format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_NONE,
+};
+
+enum node_ids {
+ /* CSI2 HW output nodes first. */
+ CSI2_CH0,
+ CSI2_CH1,
+ CSI2_CH2,
+ CSI2_CH3,
+ /* FE only nodes from here on. */
+ FE_OUT0,
+ FE_OUT1,
+ FE_STATS,
+ FE_CONFIG,
+ NUM_NODES
+};
+
+struct node_description {
+ enum node_ids id;
+ const char *name;
+ unsigned int caps;
+ unsigned int pad_flags;
+ unsigned int link_pad;
+};
+
+/* Must match the ordering of enum ids */
+static const struct node_description node_desc[NUM_NODES] = {
+ [CSI2_CH0] = {
+ .name = "csi2-ch0",
+ .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_PAD_FIRST_SOURCE + 0
+ },
+ /*
+ * At the moment the main userspace component (libcamera) doesn't
+ * support metadata with video nodes that support both video and
+ * metadata. So for the time being this node is set to only support
+ * V4L2_CAP_META_CAPTURE.
+ */
+ [CSI2_CH1] = {
+ .name = "csi2-ch1",
+ .caps = V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_PAD_FIRST_SOURCE + 1
+ },
+ [CSI2_CH2] = {
+ .name = "csi2-ch2",
+ .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_PAD_FIRST_SOURCE + 2
+ },
+ [CSI2_CH3] = {
+ .name = "csi2-ch3",
+ .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_PAD_FIRST_SOURCE + 3
+ },
+ [FE_OUT0] = {
+ .name = "fe-image0",
+ .caps = V4L2_CAP_VIDEO_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_OUTPUT0_PAD
+ },
+ [FE_OUT1] = {
+ .name = "fe-image1",
+ .caps = V4L2_CAP_VIDEO_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_OUTPUT1_PAD
+ },
+ [FE_STATS] = {
+ .name = "fe-stats",
+ .caps = V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_STATS_PAD
+ },
+ [FE_CONFIG] = {
+ .name = "fe-config",
+ .caps = V4L2_CAP_META_OUTPUT,
+ .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_CONFIG_PAD
+ },
+};
+
+#define is_fe_node(node) (((node)->id) >= FE_OUT0)
+#define is_csi2_node(node) (!is_fe_node(node))
+
+#define node_supports_image_output(node) \
+ (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE)
+#define node_supports_meta_output(node) \
+ (node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE)
+#define node_supports_image_input(node) \
+ (node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT)
+#define node_supports_meta_input(node) \
+ (node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT)
+#define node_supports_image(node) \
+ (node_supports_image_output(node) || node_supports_image_input(node))
+#define node_supports_meta(node) \
+ (node_supports_meta_output(node) || node_supports_meta_input(node))
+
+#define is_image_output_node(node) \
+ ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+#define is_image_input_node(node) \
+ ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+#define is_image_node(node) \
+ (is_image_output_node(node) || is_image_input_node(node))
+#define is_meta_output_node(node) \
+ ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE)
+#define is_meta_input_node(node) \
+ ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT)
+#define is_meta_node(node) \
+ (is_meta_output_node(node) || is_meta_input_node(node))
+
+/* To track state across all nodes. */
+#define NODE_REGISTERED BIT(0)
+#define NODE_ENABLED BIT(1)
+#define NODE_STREAMING BIT(2)
+#define FS_INT BIT(3)
+#define FE_INT BIT(4)
+#define NUM_STATES 5
+
+struct cfe_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+};
+
+struct cfe_config_buffer {
+ struct cfe_buffer buf;
+ struct pisp_fe_config config;
+};
+
+static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct cfe_buffer, vb.vb2_buf);
+}
+
+static inline
+struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf)
+{
+ return container_of(buf, struct cfe_config_buffer, buf);
+}
+
+struct cfe_node {
+ /* Node id */
+ enum node_ids id;
+ /* Pointer pointing to current v4l2_buffer */
+ struct cfe_buffer *cur_frm;
+ /* Pointer pointing to next v4l2_buffer */
+ struct cfe_buffer *next_frm;
+ /* Used to store current pixel format */
+ struct v4l2_format vid_fmt;
+ /* Used to store current meta format */
+ struct v4l2_format meta_fmt;
+ /* Buffer queue used in video-buf */
+ struct vb2_queue buffer_queue;
+ /* Queue of filled frames */
+ struct list_head dma_queue;
+ /* lock used to access this structure */
+ struct mutex lock;
+ /* Identifies video device for this channel */
+ struct video_device video_dev;
+ /* Pointer to the parent handle */
+ struct cfe_device *cfe;
+ /* Media pad for this node */
+ struct media_pad pad;
+ /* Frame-start counter */
+ unsigned int fs_count;
+ /* Timestamp of the current buffer */
+ u64 ts;
+};
+
+struct cfe_device {
+ struct dentry *debugfs;
+ struct kref kref;
+
+ /* peripheral base address */
+ void __iomem *mipi_cfg_base;
+
+ struct clk *clk;
+
+ /* V4l2 device */
+ struct v4l2_device v4l2_dev;
+ struct media_device mdev;
+ struct media_pipeline pipe;
+
+ /* IRQ lock for node state and DMA queues */
+ spinlock_t state_lock;
+ bool job_ready;
+ bool job_queued;
+
+ /* parent device */
+ struct platform_device *pdev;
+ /* subdevice async Notifier */
+ struct v4l2_async_notifier notifier;
+
+ /* Source sub device */
+ struct v4l2_subdev *source_sd;
+ /* Source subdev's pad */
+ u32 source_pad;
+
+ struct cfe_node node[NUM_NODES];
+ DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES);
+
+ struct csi2_device csi2;
+ struct pisp_fe_device fe;
+
+ int fe_csi2_channel;
+
+ /* Mask of enabled streams */
+ u64 streams_mask;
+};
+
+static inline bool is_fe_enabled(struct cfe_device *cfe)
+{
+ return cfe->fe_csi2_channel != -1;
+}
+
+static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct cfe_device, v4l2_dev);
+}
+
+static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset)
+{
+ return readl(cfe->mipi_cfg_base + offset);
+}
+
+static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val)
+{
+ writel(val, cfe->mipi_cfg_base + offset);
+}
+
+static bool check_state(struct cfe_device *cfe, unsigned long state,
+ unsigned int node_id)
+{
+ unsigned long bit;
+
+ for_each_set_bit(bit, &state, sizeof(state)) {
+ if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags))
+ return false;
+ }
+
+ return true;
+}
+
+static void set_state(struct cfe_device *cfe, unsigned long state,
+ unsigned int node_id)
+{
+ unsigned long bit;
+
+ for_each_set_bit(bit, &state, sizeof(state))
+ set_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
+}
+
+static void clear_state(struct cfe_device *cfe, unsigned long state,
+ unsigned int node_id)
+{
+ unsigned long bit;
+
+ for_each_set_bit(bit, &state, sizeof(state))
+ clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
+}
+
+static bool test_any_node(struct cfe_device *cfe, unsigned long cond)
+{
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ if (check_state(cfe, cond, i))
+ return true;
+ }
+
+ return false;
+}
+
+static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond,
+ unsigned long cond)
+{
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ if (check_state(cfe, precond, i)) {
+ if (!check_state(cfe, cond, i))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int mipi_cfg_regs_show(struct seq_file *s, void *data)
+{
+ struct cfe_device *cfe = s->private;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
+ if (ret)
+ return ret;
+
+#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg))
+ DUMP(MIPICFG_CFG);
+ DUMP(MIPICFG_INTR);
+ DUMP(MIPICFG_INTE);
+ DUMP(MIPICFG_INTF);
+ DUMP(MIPICFG_INTS);
+#undef DUMP
+
+ pm_runtime_put(&cfe->pdev->dev);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs);
+
+/* Format setup functions */
+const struct cfe_fmt *find_format_by_code(u32 code)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].code == code)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].fourcc == pixelformat)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+static const struct cfe_fmt *find_format_by_code_and_fourcc(u32 code,
+ u32 fourcc)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].code == code && formats[i].fourcc == fourcc)
+ return &formats[i];
+ }
+
+ return NULL;
+}
+
+/*
+ * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap
+ * possible.
+ */
+u32 cfe_find_16bit_code(u32 code)
+{
+ const struct cfe_fmt *cfe_fmt;
+
+ cfe_fmt = find_format_by_code(code);
+
+ if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT])
+ return 0;
+
+ cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]);
+ if (!cfe_fmt)
+ return 0;
+
+ return cfe_fmt->code;
+}
+
+/*
+ * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap
+ * possible.
+ */
+u32 cfe_find_compressed_code(u32 code)
+{
+ const struct cfe_fmt *cfe_fmt;
+
+ cfe_fmt = find_format_by_code(code);
+
+ if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED])
+ return 0;
+
+ cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]);
+ if (!cfe_fmt)
+ return 0;
+
+ return cfe_fmt->code;
+}
+
+static void cfe_calc_vid_format_size_bpl(struct cfe_device *cfe,
+ const struct cfe_fmt *fmt,
+ struct v4l2_format *f)
+{
+ unsigned int min_bytesperline;
+
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
+ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, 0);
+
+ min_bytesperline =
+ ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT);
+
+ if (f->fmt.pix.bytesperline > min_bytesperline &&
+ f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
+ f->fmt.pix.bytesperline =
+ ALIGN(f->fmt.pix.bytesperline, BPL_ALIGNMENT);
+ else
+ f->fmt.pix.bytesperline = min_bytesperline;
+
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u img_size:%u\n", __func__,
+ &f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height,
+ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
+}
+
+static void cfe_calc_meta_format_size_bpl(struct cfe_device *cfe,
+ const struct cfe_fmt *fmt,
+ struct v4l2_format *f)
+{
+ v4l_bound_align_image(&f->fmt.meta.width, MIN_META_WIDTH, MAX_WIDTH, 2,
+ &f->fmt.meta.height, MIN_META_HEIGHT, MAX_HEIGHT,
+ 0, 0);
+
+ f->fmt.meta.bytesperline = (f->fmt.meta.width * fmt->depth) >> 3;
+ f->fmt.meta.buffersize = f->fmt.meta.height * f->fmt.pix.bytesperline;
+
+ cfe_dbg(cfe, "%s: %p4cc size: %ux%u bpl:%u buf_size:%u\n", __func__,
+ &f->fmt.meta.dataformat, f->fmt.meta.width, f->fmt.meta.height,
+ f->fmt.meta.bytesperline, f->fmt.meta.buffersize);
+}
+
+static void cfe_schedule_next_csi2_job(struct cfe_device *cfe)
+{
+ struct cfe_buffer *buf;
+ dma_addr_t addr;
+
+ for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
+ struct cfe_node *node = &cfe->node[i];
+ unsigned int stride, size;
+
+ if (!check_state(cfe, NODE_STREAMING, i))
+ continue;
+
+ buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
+ list);
+ node->next_frm = buf;
+ list_del(&buf->list);
+
+ trace_cfe_csi2_schedule(node->id, &buf->vb.vb2_buf);
+
+ if (is_meta_node(node)) {
+ size = node->meta_fmt.fmt.meta.buffersize;
+ /* We use CSI2_CH_CTRL_PACK_BYTES, so stride == 0 */
+ stride = 0;
+ } else {
+ size = node->vid_fmt.fmt.pix.sizeimage;
+ stride = node->vid_fmt.fmt.pix.bytesperline;
+ }
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size);
+ }
+}
+
+static void cfe_schedule_next_pisp_job(struct cfe_device *cfe)
+{
+ struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 };
+ struct cfe_config_buffer *config_buf;
+ struct cfe_buffer *buf;
+
+ for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) {
+ struct cfe_node *node = &cfe->node[i];
+
+ if (!check_state(cfe, NODE_STREAMING, i))
+ continue;
+
+ buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
+ list);
+
+ trace_cfe_fe_schedule(node->id, &buf->vb.vb2_buf);
+
+ node->next_frm = buf;
+ vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf;
+ list_del(&buf->list);
+ }
+
+ config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm);
+ pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config);
+}
+
+static bool cfe_check_job_ready(struct cfe_device *cfe)
+{
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ struct cfe_node *node = &cfe->node[i];
+
+ if (!check_state(cfe, NODE_ENABLED, i))
+ continue;
+
+ if (list_empty(&node->dma_queue))
+ return false;
+ }
+
+ return true;
+}
+
+static void cfe_prepare_next_job(struct cfe_device *cfe)
+{
+ trace_cfe_prepare_next_job(is_fe_enabled(cfe));
+
+ cfe->job_queued = true;
+ cfe_schedule_next_csi2_job(cfe);
+ if (is_fe_enabled(cfe))
+ cfe_schedule_next_pisp_job(cfe);
+
+ /* Flag if another job is ready after this. */
+ cfe->job_ready = cfe_check_job_ready(cfe);
+}
+
+static void cfe_process_buffer_complete(struct cfe_node *node,
+ enum vb2_buffer_state state)
+{
+ trace_cfe_buffer_complete(node->id, &node->cur_frm->vb);
+
+ node->cur_frm->vb.sequence = node->fs_count - 1;
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
+}
+
+static void cfe_queue_event_sof(struct cfe_node *node)
+{
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ .u.frame_sync.frame_sequence = node->fs_count - 1,
+ };
+
+ v4l2_event_queue(&node->video_dev, &event);
+}
+
+static void cfe_sof_isr(struct cfe_node *node)
+{
+ struct cfe_device *cfe = node->cfe;
+ bool matching_fs = true;
+
+ trace_cfe_frame_start(node->id, node->fs_count);
+
+ /*
+ * If the sensor is producing unexpected frame event ordering over a
+ * sustained period of time, guard against the possibility of coming
+ * here and orphaning the cur_frm if it's not been dequeued already.
+ * Unfortunately, there is not enough hardware state to tell if this
+ * may have occurred.
+ */
+ if (WARN(node->cur_frm, "%s: [%s] Orphaned frame at seq %u\n",
+ __func__, node_desc[node->id].name, node->fs_count))
+ cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR);
+
+ node->cur_frm = node->next_frm;
+ node->next_frm = NULL;
+ node->fs_count++;
+
+ node->ts = ktime_get_ns();
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ if (!check_state(cfe, NODE_STREAMING, i) || i == node->id)
+ continue;
+ /*
+ * This checks if any other node has seen a FS. If yes, use the
+ * same timestamp, eventually across all node buffers.
+ */
+ if (cfe->node[i].fs_count >= node->fs_count)
+ node->ts = cfe->node[i].ts;
+ /*
+ * This checks if all other node have seen a matching FS. If
+ * yes, we can flag another job to be queued.
+ */
+ if (matching_fs && cfe->node[i].fs_count != node->fs_count)
+ matching_fs = false;
+ }
+
+ if (matching_fs)
+ cfe->job_queued = false;
+
+ if (node->cur_frm)
+ node->cur_frm->vb.vb2_buf.timestamp = node->ts;
+
+ set_state(cfe, FS_INT, node->id);
+ clear_state(cfe, FE_INT, node->id);
+
+ if (is_image_output_node(node))
+ cfe_queue_event_sof(node);
+}
+
+static void cfe_eof_isr(struct cfe_node *node)
+{
+ struct cfe_device *cfe = node->cfe;
+
+ trace_cfe_frame_end(node->id, node->fs_count - 1);
+
+ if (node->cur_frm)
+ cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE);
+
+ node->cur_frm = NULL;
+ set_state(cfe, FE_INT, node->id);
+ clear_state(cfe, FS_INT, node->id);
+}
+
+static irqreturn_t cfe_isr(int irq, void *dev)
+{
+ struct cfe_device *cfe = dev;
+ bool sof[NUM_NODES] = { 0 }, eof[NUM_NODES] = { 0 };
+ u32 sts;
+
+ sts = cfg_reg_read(cfe, MIPICFG_INTS);
+
+ if (sts & MIPICFG_INT_CSI_DMA)
+ csi2_isr(&cfe->csi2, sof, eof);
+
+ if (sts & MIPICFG_INT_PISP_FE)
+ pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS,
+ eof + CSI2_NUM_CHANNELS);
+
+ spin_lock(&cfe->state_lock);
+
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ struct cfe_node *node = &cfe->node[i];
+
+ /*
+ * The check_state(NODE_STREAMING) is to ensure we do not loop
+ * over the CSI2_CHx nodes when the FE is active since they
+ * generate interrupts even though the node is not streaming.
+ */
+ if (!check_state(cfe, NODE_STREAMING, i) || !(sof[i] || eof[i]))
+ continue;
+
+ /*
+ * There are 3 cases where we could get FS + FE_ACK at
+ * the same time:
+ * 1) FE of the current frame, and FS of the next frame.
+ * 2) FS + FE of the same frame.
+ * 3) FE of the current frame, and FS + FE of the next
+ * frame. To handle this, see the sof handler below.
+ *
+ * (1) is handled implicitly by the ordering of the FE and FS
+ * handlers below.
+ */
+ if (eof[i]) {
+ /*
+ * The condition below tests for (2). Run the FS handler
+ * first before the FE handler, both for the current
+ * frame.
+ */
+ if (sof[i] && !check_state(cfe, FS_INT, i)) {
+ cfe_sof_isr(node);
+ sof[i] = false;
+ }
+
+ cfe_eof_isr(node);
+ }
+
+ if (sof[i]) {
+ /*
+ * The condition below tests for (3). In such cases, we
+ * come in here with FS flag set in the node state from
+ * the previous frame since it only gets cleared in
+ * cfe_eof_isr(). Handle the FE for the previous
+ * frame first before the FS handler for the current
+ * frame.
+ */
+ if (check_state(cfe, FS_INT, node->id) &&
+ !check_state(cfe, FE_INT, node->id)) {
+ cfe_dbg(cfe, "%s: [%s] Handling missing previous FE interrupt\n",
+ __func__, node_desc[node->id].name);
+ cfe_eof_isr(node);
+ }
+
+ cfe_sof_isr(node);
+ }
+
+ if (!cfe->job_queued && cfe->job_ready)
+ cfe_prepare_next_job(cfe);
+ }
+
+ spin_unlock(&cfe->state_lock);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Stream helpers
+ */
+
+static int cfe_get_vc_dt_fallback(struct cfe_device *cfe, u8 *vc, u8 *dt)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+ const struct cfe_fmt *cfe_fmt;
+
+ state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
+
+ fmt = v4l2_subdev_state_get_format(state, CSI2_PAD_SINK, 0);
+ if (!fmt)
+ return -EINVAL;
+
+ cfe_fmt = find_format_by_code(fmt->code);
+ if (!cfe_fmt)
+ return -EINVAL;
+
+ *vc = 0;
+ *dt = cfe_fmt->csi_dt;
+
+ return 0;
+}
+
+static int cfe_get_vc_dt(struct cfe_device *cfe, unsigned int channel, u8 *vc,
+ u8 *dt)
+{
+ struct v4l2_mbus_frame_desc remote_desc;
+ struct v4l2_subdev_state *state;
+ u32 sink_stream;
+ unsigned int i;
+ int ret;
+
+ state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ CSI2_PAD_FIRST_SOURCE + channel, 0, NULL, &sink_stream);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_call(cfe->source_sd, pad, get_frame_desc,
+ cfe->source_pad, &remote_desc);
+ if (ret == -ENOIOCTLCMD) {
+ cfe_dbg(cfe, "source does not support get_frame_desc, use fallback\n");
+ return cfe_get_vc_dt_fallback(cfe, vc, dt);
+ } else if (ret) {
+ cfe_err(cfe, "Failed to get frame descriptor\n");
+ return ret;
+ }
+
+ if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ cfe_err(cfe, "Frame descriptor does not describe CSI-2 link");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < remote_desc.num_entries; i++) {
+ if (remote_desc.entry[i].stream == sink_stream)
+ break;
+ }
+
+ if (i == remote_desc.num_entries) {
+ cfe_err(cfe, "Stream %u not found in remote frame desc\n",
+ sink_stream);
+ return -EINVAL;
+ }
+
+ *vc = remote_desc.entry[i].bus.csi2.vc;
+ *dt = remote_desc.entry[i].bus.csi2.dt;
+
+ return 0;
+}
+
+static int cfe_start_channel(struct cfe_node *node)
+{
+ struct cfe_device *cfe = node->cfe;
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *source_fmt;
+ const struct cfe_fmt *fmt;
+ unsigned long flags;
+ bool start_fe;
+ int ret;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ start_fe = is_fe_enabled(cfe) &&
+ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
+
+ state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
+
+ if (start_fe) {
+ unsigned int width, height;
+ u8 vc, dt;
+
+ cfe_dbg(cfe, "%s: %s using csi2 channel %d\n", __func__,
+ node_desc[FE_OUT0].name, cfe->fe_csi2_channel);
+
+ ret = cfe_get_vc_dt(cfe, cfe->fe_csi2_channel, &vc, &dt);
+ if (ret)
+ return ret;
+
+ source_fmt = v4l2_subdev_state_get_format(state,
+ node_desc[cfe->fe_csi2_channel].link_pad);
+ fmt = find_format_by_code(source_fmt->code);
+
+ width = source_fmt->width;
+ height = source_fmt->height;
+
+ /* Must have a valid CSI2 datatype. */
+ WARN_ON(!fmt->csi_dt);
+
+ /*
+ * Start the associated CSI2 Channel as well.
+ *
+ * Must write to the ADDR register to latch the ctrl values
+ * even if we are connected to the front end. Once running,
+ * this is handled by the CSI2 AUTO_ARM mode.
+ */
+ csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel,
+ CSI2_MODE_FE_STREAMING,
+ true, false, width, height, vc, dt);
+ csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1);
+ pisp_fe_start(&cfe->fe);
+ }
+
+ if (is_csi2_node(node)) {
+ unsigned int width = 0, height = 0;
+ u8 vc, dt;
+
+ ret = cfe_get_vc_dt(cfe, node->id, &vc, &dt);
+ if (ret) {
+ if (start_fe) {
+ csi2_stop_channel(&cfe->csi2,
+ cfe->fe_csi2_channel);
+ pisp_fe_stop(&cfe->fe);
+ }
+
+ return ret;
+ }
+
+ u32 mode = CSI2_MODE_NORMAL;
+
+ source_fmt = v4l2_subdev_state_get_format(state,
+ node_desc[node->id].link_pad);
+ fmt = find_format_by_code(source_fmt->code);
+
+ /* Must have a valid CSI2 datatype. */
+ WARN_ON(!fmt->csi_dt);
+
+ if (is_image_output_node(node)) {
+ u32 pixfmt;
+
+ width = source_fmt->width;
+ height = source_fmt->height;
+
+ pixfmt = node->vid_fmt.fmt.pix.pixelformat;
+
+ if (pixfmt == fmt->remap[CFE_REMAP_16BIT]) {
+ mode = CSI2_MODE_REMAP;
+ } else if (pixfmt == fmt->remap[CFE_REMAP_COMPRESSED]) {
+ mode = CSI2_MODE_COMPRESSED;
+ csi2_set_compression(&cfe->csi2, node->id,
+ CSI2_COMPRESSION_DELTA, 0,
+ 0);
+ }
+ }
+ /* Unconditionally start this CSI2 channel. */
+ csi2_start_channel(&cfe->csi2, node->id,
+ mode,
+ /* Auto arm */
+ false,
+ /* Pack bytes */
+ is_meta_node(node) ? true : false,
+ width, height, vc, dt);
+ }
+
+ spin_lock_irqsave(&cfe->state_lock, flags);
+ if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING))
+ cfe_prepare_next_job(cfe);
+ spin_unlock_irqrestore(&cfe->state_lock, flags);
+
+ return 0;
+}
+
+static void cfe_stop_channel(struct cfe_node *node, bool fe_stop)
+{
+ struct cfe_device *cfe = node->cfe;
+
+ cfe_dbg(cfe, "%s: [%s] fe_stop %u\n", __func__,
+ node_desc[node->id].name, fe_stop);
+
+ if (fe_stop) {
+ csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel);
+ pisp_fe_stop(&cfe->fe);
+ }
+
+ if (is_csi2_node(node))
+ csi2_stop_channel(&cfe->csi2, node->id);
+}
+
+static void cfe_return_buffers(struct cfe_node *node,
+ enum vb2_buffer_state state)
+{
+ struct cfe_device *cfe = node->cfe;
+ struct cfe_buffer *buf, *tmp;
+ unsigned long flags;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ spin_lock_irqsave(&cfe->state_lock, flags);
+ list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) {
+ list_del(&buf->list);
+ trace_cfe_return_buffer(node->id, buf->vb.vb2_buf.index, 2);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+
+ if (node->cur_frm) {
+ trace_cfe_return_buffer(node->id,
+ node->cur_frm->vb.vb2_buf.index, 0);
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
+ }
+ if (node->next_frm && node->cur_frm != node->next_frm) {
+ trace_cfe_return_buffer(node->id,
+ node->next_frm->vb.vb2_buf.index, 1);
+ vb2_buffer_done(&node->next_frm->vb.vb2_buf, state);
+ }
+
+ node->cur_frm = NULL;
+ node->next_frm = NULL;
+ spin_unlock_irqrestore(&cfe->state_lock, flags);
+}
+
+/*
+ * vb2 ops
+ */
+
+static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct cfe_node *node = vb2_get_drv_priv(vq);
+ struct cfe_device *cfe = node->cfe;
+ unsigned int size = is_image_node(node) ?
+ node->vid_fmt.fmt.pix.sizeimage :
+ node->meta_fmt.fmt.meta.buffersize;
+
+ cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
+ node->buffer_queue.type);
+
+ if (vq->max_num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->max_num_buffers;
+
+ if (*nplanes) {
+ if (sizes[0] < size) {
+ cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size);
+ return -EINVAL;
+ }
+ size = sizes[0];
+ }
+
+ *nplanes = 1;
+ sizes[0] = size;
+
+ return 0;
+}
+
+static int cfe_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct cfe_device *cfe = node->cfe;
+ struct cfe_buffer *buf = to_cfe_buffer(vb);
+ unsigned long size;
+
+ trace_cfe_buffer_prepare(node->id, vb);
+
+ size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage :
+ node->meta_fmt.fmt.meta.buffersize;
+ if (vb2_plane_size(vb, 0) < size) {
+ cfe_err(cfe, "data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
+
+ if (node->id == FE_CONFIG) {
+ struct cfe_config_buffer *b = to_cfe_config_buffer(buf);
+ void *addr = vb2_plane_vaddr(vb, 0);
+
+ memcpy(&b->config, addr, sizeof(struct pisp_fe_config));
+ return pisp_fe_validate_config(&cfe->fe, &b->config,
+ &cfe->node[FE_OUT0].vid_fmt,
+ &cfe->node[FE_OUT1].vid_fmt);
+ }
+
+ return 0;
+}
+
+static void cfe_buffer_queue(struct vb2_buffer *vb)
+{
+ struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
+ struct cfe_device *cfe = node->cfe;
+ struct cfe_buffer *buf = to_cfe_buffer(vb);
+ unsigned long flags;
+ bool schedule_now;
+
+ spin_lock_irqsave(&cfe->state_lock, flags);
+
+ list_add_tail(&buf->list, &node->dma_queue);
+
+ if (!cfe->job_ready)
+ cfe->job_ready = cfe_check_job_ready(cfe);
+
+ schedule_now = !cfe->job_queued && cfe->job_ready &&
+ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
+
+ trace_cfe_buffer_queue(node->id, vb, schedule_now);
+
+ if (schedule_now)
+ cfe_prepare_next_job(cfe);
+
+ spin_unlock_irqrestore(&cfe->state_lock, flags);
+}
+
+static s64 cfe_get_source_link_freq(struct cfe_device *cfe)
+{
+ struct media_pad *src_pad =
+ &cfe->source_sd->entity.pads[cfe->source_pad];
+ struct v4l2_subdev_state *state;
+ s64 link_freq;
+ u32 bpp;
+
+ state = v4l2_subdev_get_locked_active_state(&cfe->csi2.sd);
+
+ /*
+ * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back
+ * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available.
+ *
+ * With multistream input there is no single pixel rate, and thus we
+ * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which
+ * causes v4l2_get_link_freq() to return an error if it falls back to
+ * V4L2_CID_PIXEL_RATE.
+ */
+
+ if (state->routing.num_routes == 1) {
+ struct v4l2_subdev_route *route = &state->routing.routes[0];
+ struct v4l2_mbus_framefmt *source_fmt;
+ const struct cfe_fmt *fmt;
+
+ source_fmt = v4l2_subdev_state_get_format(state,
+ route->sink_pad,
+ route->sink_stream);
+
+ fmt = find_format_by_code(source_fmt->code);
+ if (!fmt)
+ return -EINVAL;
+
+ bpp = fmt->depth;
+ } else {
+ bpp = 0;
+ }
+
+ link_freq = v4l2_get_link_freq(src_pad, bpp,
+ 2 * cfe->csi2.dphy.active_lanes);
+ if (link_freq < 0)
+ cfe_err(cfe, "failed to get link freq for subdev '%s'\n",
+ cfe->source_sd->name);
+
+ return link_freq;
+}
+
+static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct v4l2_mbus_config mbus_config = { 0 };
+ struct cfe_node *node = vb2_get_drv_priv(vq);
+ struct cfe_device *cfe = node->cfe;
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *route;
+ s64 link_freq;
+ int ret;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ if (!check_state(cfe, NODE_ENABLED, node->id)) {
+ cfe_err(cfe, "%s node link is not enabled.\n",
+ node_desc[node->id].name);
+ ret = -EINVAL;
+ goto err_streaming;
+ }
+
+ ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
+ if (ret < 0) {
+ cfe_err(cfe, "pm_runtime_resume_and_get failed\n");
+ goto err_streaming;
+ }
+
+ /* When using the Frontend, we must enable the FE_CONFIG node. */
+ if (is_fe_enabled(cfe) &&
+ !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) {
+ cfe_err(cfe, "FE enabled, but FE_CONFIG node is not\n");
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ ret = media_pipeline_start(&node->pad, &cfe->pipe);
+ if (ret < 0) {
+ cfe_err(cfe, "Failed to start media pipeline: %d\n", ret);
+ goto err_pm_put;
+ }
+
+ state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
+
+ clear_state(cfe, FS_INT | FE_INT, node->id);
+ set_state(cfe, NODE_STREAMING, node->id);
+ node->fs_count = 0;
+
+ ret = cfe_start_channel(node);
+ if (ret)
+ goto err_unlock_state;
+
+ if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
+ cfe_dbg(cfe, "Streaming on hold, as all nodes are not set to streaming yet\n");
+ v4l2_subdev_unlock_state(state);
+ return 0;
+ }
+
+ cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI);
+ cfg_reg_write(cfe, MIPICFG_INTE,
+ MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE);
+
+ ret = v4l2_subdev_call(cfe->source_sd, pad, get_mbus_config, 0,
+ &mbus_config);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ cfe_err(cfe, "g_mbus_config failed\n");
+ goto err_clear_inte;
+ }
+
+ cfe->csi2.dphy.active_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
+ if (!cfe->csi2.dphy.active_lanes)
+ cfe->csi2.dphy.active_lanes = cfe->csi2.dphy.max_lanes;
+ if (cfe->csi2.dphy.active_lanes > cfe->csi2.dphy.max_lanes) {
+ cfe_err(cfe, "Device has requested %u data lanes, which is >%u configured in DT\n",
+ cfe->csi2.dphy.active_lanes, cfe->csi2.dphy.max_lanes);
+ ret = -EINVAL;
+ goto err_clear_inte;
+ }
+
+ link_freq = cfe_get_source_link_freq(cfe);
+ if (link_freq < 0)
+ goto err_clear_inte;
+
+ cfe->csi2.dphy.dphy_rate = div_s64(link_freq * 2, 1000000);
+ csi2_open_rx(&cfe->csi2);
+
+ cfe->streams_mask = 0;
+
+ for_each_active_route(&state->routing, route)
+ cfe->streams_mask |= BIT_ULL(route->sink_stream);
+
+ ret = v4l2_subdev_enable_streams(cfe->source_sd, cfe->source_pad,
+ cfe->streams_mask);
+ if (ret) {
+ cfe_err(cfe, "stream on failed in subdev\n");
+ goto err_disable_cfe;
+ }
+
+ cfe_dbg(cfe, "Streaming enabled\n");
+
+ v4l2_subdev_unlock_state(state);
+
+ return 0;
+
+err_disable_cfe:
+ csi2_close_rx(&cfe->csi2);
+err_clear_inte:
+ cfg_reg_write(cfe, MIPICFG_INTE, 0);
+
+ cfe_stop_channel(node,
+ is_fe_enabled(cfe) && test_all_nodes(cfe, NODE_ENABLED,
+ NODE_STREAMING));
+err_unlock_state:
+ v4l2_subdev_unlock_state(state);
+ media_pipeline_stop(&node->pad);
+err_pm_put:
+ pm_runtime_put(&cfe->pdev->dev);
+err_streaming:
+ cfe_return_buffers(node, VB2_BUF_STATE_QUEUED);
+ clear_state(cfe, NODE_STREAMING, node->id);
+
+ return ret;
+}
+
+static void cfe_stop_streaming(struct vb2_queue *vq)
+{
+ struct cfe_node *node = vb2_get_drv_priv(vq);
+ struct cfe_device *cfe = node->cfe;
+ unsigned long flags;
+ bool fe_stop;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ spin_lock_irqsave(&cfe->state_lock, flags);
+ fe_stop = is_fe_enabled(cfe) &&
+ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
+
+ cfe->job_ready = false;
+ clear_state(cfe, NODE_STREAMING, node->id);
+ spin_unlock_irqrestore(&cfe->state_lock, flags);
+
+ cfe_stop_channel(node, fe_stop);
+
+ if (!test_any_node(cfe, NODE_STREAMING)) {
+ struct v4l2_subdev_state *state;
+ int ret;
+
+ state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
+
+ ret = v4l2_subdev_disable_streams(cfe->source_sd,
+ cfe->source_pad,
+ cfe->streams_mask);
+ if (ret)
+ cfe_err(cfe, "stream disable failed in subdev\n");
+
+ v4l2_subdev_unlock_state(state);
+
+ csi2_close_rx(&cfe->csi2);
+
+ cfg_reg_write(cfe, MIPICFG_INTE, 0);
+
+ cfe_dbg(cfe, "%s: Streaming disabled\n", __func__);
+ }
+
+ media_pipeline_stop(&node->pad);
+
+ /* Clear all queued buffers for the node */
+ cfe_return_buffers(node, VB2_BUF_STATE_ERROR);
+
+ pm_runtime_put(&cfe->pdev->dev);
+}
+
+static const struct vb2_ops cfe_video_qops = {
+ .queue_setup = cfe_queue_setup,
+ .buf_prepare = cfe_buffer_prepare,
+ .buf_queue = cfe_buffer_queue,
+ .start_streaming = cfe_start_streaming,
+ .stop_streaming = cfe_stop_streaming,
+};
+
+/*
+ * v4l2 ioctl ops
+ */
+
+static int cfe_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver));
+ strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card));
+
+ cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE |
+ V4L2_CAP_META_OUTPUT;
+
+ return 0;
+}
+
+static int cfe_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+ unsigned int i, j;
+
+ if (!node_supports_image_output(node))
+ return -EINVAL;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) {
+ if (f->mbus_code && formats[i].code != f->mbus_code)
+ continue;
+
+ if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT ||
+ formats[i].flags & CFE_FORMAT_FLAG_META_CAP)
+ continue;
+
+ if (is_fe_node(node) &&
+ !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT))
+ continue;
+
+ if (j == f->index) {
+ f->pixelformat = formats[i].fourcc;
+ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ return 0;
+ }
+ j++;
+ }
+
+ return -EINVAL;
+}
+
+static int cfe_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+
+ if (!node_supports_image(node))
+ return -EINVAL;
+
+ *f = node->vid_fmt;
+
+ return 0;
+}
+
+static int cfe_validate_fmt_vid_cap(struct cfe_node *node,
+ struct v4l2_format *f)
+{
+ struct cfe_device *cfe = node->cfe;
+ const struct cfe_fmt *fmt;
+
+ cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 pix %p4cc\n", __func__,
+ node_desc[node->id].name, f->fmt.pix.width, f->fmt.pix.height,
+ &f->fmt.pix.pixelformat);
+
+ if (!node_supports_image_output(node))
+ return -EINVAL;
+
+ /*
+ * Default to a format that works for both CSI2 and FE.
+ */
+ fmt = find_format_by_pix(f->fmt.pix.pixelformat);
+ if (!fmt)
+ fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
+
+ f->fmt.pix.pixelformat = fmt->fourcc;
+
+ if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) {
+ f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT];
+ fmt = find_format_by_pix(f->fmt.pix.pixelformat);
+ }
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+
+ cfe_calc_vid_format_size_bpl(cfe, fmt, f);
+
+ return 0;
+}
+
+static int cfe_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+ struct vb2_queue *q = &node->buffer_queue;
+ int ret;
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ ret = cfe_validate_fmt_vid_cap(node, f);
+ if (ret)
+ return ret;
+
+ node->vid_fmt = *f;
+
+ cfe_dbg(cfe, "%s: Set %ux%u, V4L2 pix %p4cc\n", __func__,
+ node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height,
+ &node->vid_fmt.fmt.pix.pixelformat);
+
+ return 0;
+}
+
+static int cfe_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ return cfe_validate_fmt_vid_cap(node, f);
+}
+
+static int cfe_enum_fmt_meta(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ if (!node_supports_meta(node))
+ return -EINVAL;
+
+ switch (node->id) {
+ case CSI2_CH0...CSI2_CH3:
+ f->flags = V4L2_FMT_FLAG_META_LINE_BASED;
+
+ switch (f->index) {
+ case 0:
+ f->pixelformat = V4L2_META_FMT_GENERIC_8;
+ return 0;
+ case 1:
+ f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_10;
+ return 0;
+ case 2:
+ f->pixelformat = V4L2_META_FMT_GENERIC_CSI2_12;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ default:
+ break;
+ }
+
+ if (f->index != 0)
+ return -EINVAL;
+
+ switch (node->id) {
+ case FE_STATS:
+ f->pixelformat = V4L2_META_FMT_RPI_FE_STATS;
+ return 0;
+ case FE_CONFIG:
+ f->pixelformat = V4L2_META_FMT_RPI_FE_CFG;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cfe_validate_fmt_meta(struct cfe_node *node, struct v4l2_format *f)
+{
+ struct cfe_device *cfe = node->cfe;
+ const struct cfe_fmt *fmt;
+
+ switch (node->id) {
+ case CSI2_CH0...CSI2_CH3:
+ cfe_dbg(cfe, "%s: [%s] %ux%u, V4L2 meta %p4cc\n", __func__,
+ node_desc[node->id].name, f->fmt.meta.width,
+ f->fmt.meta.height, &f->fmt.meta.dataformat);
+ break;
+ case FE_STATS:
+ case FE_CONFIG:
+ cfe_dbg(cfe, "%s: [%s] %u bytes, V4L2 meta %p4cc\n", __func__,
+ node_desc[node->id].name, f->fmt.meta.buffersize,
+ &f->fmt.meta.dataformat);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!node_supports_meta(node))
+ return -EINVAL;
+
+ switch (node->id) {
+ case CSI2_CH0...CSI2_CH3:
+ fmt = find_format_by_pix(f->fmt.meta.dataformat);
+ if (!fmt || !(fmt->flags & CFE_FORMAT_FLAG_META_CAP))
+ fmt = find_format_by_pix(V4L2_META_FMT_GENERIC_CSI2_10);
+
+ f->fmt.meta.dataformat = fmt->fourcc;
+
+ cfe_calc_meta_format_size_bpl(cfe, fmt, f);
+
+ return 0;
+ case FE_STATS:
+ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS;
+ f->fmt.meta.buffersize = sizeof(struct pisp_statistics);
+ return 0;
+ case FE_CONFIG:
+ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG;
+ f->fmt.meta.buffersize = sizeof(struct pisp_fe_config);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ if (!node_supports_meta(node))
+ return -EINVAL;
+
+ *f = node->meta_fmt;
+
+ return 0;
+}
+
+static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+ struct vb2_queue *q = &node->buffer_queue;
+ int ret;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+ if (!node_supports_meta(node))
+ return -EINVAL;
+
+ ret = cfe_validate_fmt_meta(node, f);
+ if (ret)
+ return ret;
+
+ node->meta_fmt = *f;
+
+ cfe_dbg(cfe, "%s: Set %p4cc\n", __func__,
+ &node->meta_fmt.fmt.meta.dataformat);
+
+ return 0;
+}
+
+static int cfe_try_fmt_meta(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+
+ cfe_dbg(cfe, "%s: [%s]\n", __func__, node_desc[node->id].name);
+ return cfe_validate_fmt_meta(node, f);
+}
+
+static int cfe_enum_framesizes(struct file *file, void *priv,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct cfe_node *node = video_drvdata(file);
+ struct cfe_device *cfe = node->cfe;
+ const struct cfe_fmt *fmt;
+
+ cfe_dbg(cfe, "%s [%s]\n", __func__, node_desc[node->id].name);
+
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ /* check for valid format */
+ fmt = find_format_by_pix(fsize->pixel_format);
+ if (!fmt) {
+ cfe_dbg(cfe, "Invalid pixel code: %x\n", fsize->pixel_format);
+ return -EINVAL;
+ }
+
+ /* TODO: Do we have limits on the step_width? */
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = MIN_WIDTH;
+ fsize->stepwise.max_width = MAX_WIDTH;
+ fsize->stepwise.step_width = 2;
+ fsize->stepwise.min_height = MIN_HEIGHT;
+ fsize->stepwise.max_height = MAX_HEIGHT;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
+static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cfe_node *node = video_get_drvdata(vdev);
+ struct cfe_device *cfe = node->cfe;
+ int ret;
+
+ cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
+ p->type);
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ p->type != V4L2_BUF_TYPE_META_CAPTURE &&
+ p->type != V4L2_BUF_TYPE_META_OUTPUT)
+ return -EINVAL;
+
+ ret = vb2_queue_change_type(vdev->queue, p->type);
+ if (ret)
+ return ret;
+
+ return vb2_ioctl_reqbufs(file, priv, p);
+}
+
+static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *p)
+{
+ struct video_device *vdev = video_devdata(file);
+ struct cfe_node *node = video_get_drvdata(vdev);
+ struct cfe_device *cfe = node->cfe;
+ int ret;
+
+ cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
+ p->format.type);
+
+ if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ p->format.type != V4L2_BUF_TYPE_META_CAPTURE &&
+ p->format.type != V4L2_BUF_TYPE_META_OUTPUT)
+ return -EINVAL;
+
+ ret = vb2_queue_change_type(vdev->queue, p->format.type);
+ if (ret)
+ return ret;
+
+ return vb2_ioctl_create_bufs(file, priv, p);
+}
+
+static int cfe_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ struct cfe_node *node = video_get_drvdata(fh->vdev);
+
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ if (!node_supports_image_output(node))
+ break;
+
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+ if (!node_supports_image_output(node) &&
+ !node_supports_meta_output(node))
+ break;
+
+ return v4l2_event_subscribe(fh, sub, 4, NULL);
+ }
+
+ return v4l2_ctrl_subscribe_event(fh, sub);
+}
+
+static const struct v4l2_ioctl_ops cfe_ioctl_ops = {
+ .vidioc_querycap = cfe_querycap,
+ .vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cfe_g_fmt,
+ .vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap,
+
+ .vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta,
+ .vidioc_g_fmt_meta_cap = cfe_g_fmt_meta,
+ .vidioc_s_fmt_meta_cap = cfe_s_fmt_meta,
+ .vidioc_try_fmt_meta_cap = cfe_try_fmt_meta,
+
+ .vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta,
+ .vidioc_g_fmt_meta_out = cfe_g_fmt_meta,
+ .vidioc_s_fmt_meta_out = cfe_s_fmt_meta,
+ .vidioc_try_fmt_meta_out = cfe_try_fmt_meta,
+
+ .vidioc_enum_framesizes = cfe_enum_framesizes,
+
+ .vidioc_reqbufs = cfe_vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = cfe_vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_subscribe_event = cfe_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification,
+ void *arg)
+{
+ struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev);
+
+ switch (notification) {
+ case V4L2_DEVICE_NOTIFY_EVENT:
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ struct cfe_node *node = &cfe->node[i];
+
+ if (check_state(cfe, NODE_REGISTERED, i))
+ continue;
+
+ v4l2_event_queue(&node->video_dev, arg);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* cfe capture driver file operations */
+static const struct v4l2_file_operations cfe_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int cfe_video_link_validate(struct media_link *link)
+{
+ struct video_device *vd = container_of(link->sink->entity,
+ struct video_device, entity);
+ struct cfe_node *node = container_of(vd, struct cfe_node, video_dev);
+ struct cfe_device *cfe = node->cfe;
+ struct v4l2_mbus_framefmt *source_fmt;
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev *source_sd;
+ int ret = 0;
+
+ cfe_dbg(cfe, "%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__,
+ node_desc[node->id].name,
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index);
+
+ if (!media_entity_remote_source_pad_unique(link->sink->entity)) {
+ cfe_err(cfe, "video node %s pad not connected\n", vd->name);
+ return -ENOTCONN;
+ }
+
+ source_sd = media_entity_to_v4l2_subdev(link->source->entity);
+
+ state = v4l2_subdev_lock_and_get_active_state(source_sd);
+
+ source_fmt = v4l2_subdev_state_get_format(state, link->source->index);
+ if (!source_fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (is_image_output_node(node)) {
+ struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix;
+ const struct cfe_fmt *fmt;
+
+ if (source_fmt->width != pix_fmt->width ||
+ source_fmt->height != pix_fmt->height) {
+ cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n",
+ pix_fmt->width, pix_fmt->height,
+ source_fmt->width, source_fmt->height);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fmt = find_format_by_code_and_fourcc(source_fmt->code,
+ pix_fmt->pixelformat);
+ if (!fmt) {
+ cfe_err(cfe, "Format mismatch!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ } else if (is_csi2_node(node) && is_meta_output_node(node)) {
+ struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta;
+ const struct cfe_fmt *fmt;
+
+ if (source_fmt->width != meta_fmt->width ||
+ source_fmt->height != meta_fmt->height) {
+ cfe_err(cfe, "Wrong width or height %ux%u (remote pad set to %ux%u)\n",
+ meta_fmt->width, meta_fmt->height,
+ source_fmt->width, source_fmt->height);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fmt = find_format_by_code_and_fourcc(source_fmt->code,
+ meta_fmt->dataformat);
+ if (!fmt) {
+ cfe_err(cfe, "Format mismatch!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+out:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static const struct media_entity_operations cfe_media_entity_ops = {
+ .link_validate = cfe_video_link_validate,
+};
+
+static int cfe_video_link_notify(struct media_link *link, u32 flags,
+ unsigned int notification)
+{
+ struct media_device *mdev = link->graph_obj.mdev;
+ struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev);
+ struct media_entity *fe = &cfe->fe.sd.entity;
+ struct media_entity *csi2 = &cfe->csi2.sd.entity;
+ unsigned long lock_flags;
+
+ if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH)
+ return 0;
+
+ cfe_dbg(cfe, "%s: %s[%u] -> %s[%u] 0x%x", __func__,
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index, flags);
+
+ spin_lock_irqsave(&cfe->state_lock, lock_flags);
+
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ if (link->sink->entity != &cfe->node[i].video_dev.entity &&
+ link->source->entity != &cfe->node[i].video_dev.entity)
+ continue;
+
+ if (link->flags & MEDIA_LNK_FL_ENABLED)
+ set_state(cfe, NODE_ENABLED, i);
+ else
+ clear_state(cfe, NODE_ENABLED, i);
+
+ break;
+ }
+
+ spin_unlock_irqrestore(&cfe->state_lock, lock_flags);
+
+ if (link->source->entity != csi2)
+ return 0;
+ if (link->sink->entity != fe)
+ return 0;
+ if (link->sink->index != 0)
+ return 0;
+
+ cfe->fe_csi2_channel = -1;
+ if (link->flags & MEDIA_LNK_FL_ENABLED) {
+ if (link->source->index == node_desc[CSI2_CH0].link_pad)
+ cfe->fe_csi2_channel = CSI2_CH0;
+ else if (link->source->index == node_desc[CSI2_CH1].link_pad)
+ cfe->fe_csi2_channel = CSI2_CH1;
+ else if (link->source->index == node_desc[CSI2_CH2].link_pad)
+ cfe->fe_csi2_channel = CSI2_CH2;
+ else if (link->source->index == node_desc[CSI2_CH3].link_pad)
+ cfe->fe_csi2_channel = CSI2_CH3;
+ }
+
+ if (is_fe_enabled(cfe))
+ cfe_dbg(cfe, "%s: Found CSI2:%d -> FE:0 link\n", __func__,
+ cfe->fe_csi2_channel);
+ else
+ cfe_dbg(cfe, "%s: Unable to find CSI2:x -> FE:0 link\n",
+ __func__);
+
+ return 0;
+}
+
+static const struct media_device_ops cfe_media_device_ops = {
+ .link_notify = cfe_video_link_notify,
+};
+
+static void cfe_release(struct kref *kref)
+{
+ struct cfe_device *cfe = container_of(kref, struct cfe_device, kref);
+
+ media_device_cleanup(&cfe->mdev);
+
+ kfree(cfe);
+}
+
+static void cfe_put(struct cfe_device *cfe)
+{
+ kref_put(&cfe->kref, cfe_release);
+}
+
+static void cfe_get(struct cfe_device *cfe)
+{
+ kref_get(&cfe->kref);
+}
+
+static void cfe_node_release(struct video_device *vdev)
+{
+ struct cfe_node *node = video_get_drvdata(vdev);
+
+ cfe_put(node->cfe);
+}
+
+static int cfe_register_node(struct cfe_device *cfe, int id)
+{
+ struct video_device *vdev;
+ const struct cfe_fmt *fmt;
+ struct vb2_queue *q;
+ struct cfe_node *node = &cfe->node[id];
+ int ret;
+
+ node->cfe = cfe;
+ node->id = id;
+
+ if (node_supports_image(node)) {
+ if (node_supports_image_output(node))
+ node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ else
+ node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+
+ fmt = find_format_by_code(cfe_default_format.code);
+ if (!fmt) {
+ cfe_err(cfe, "Failed to find format code\n");
+ return -EINVAL;
+ }
+
+ node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc;
+ v4l2_fill_pix_format(&node->vid_fmt.fmt.pix,
+ &cfe_default_format);
+
+ ret = cfe_validate_fmt_vid_cap(node, &node->vid_fmt);
+ if (ret)
+ return ret;
+ }
+
+ if (node_supports_meta(node)) {
+ if (node_supports_meta_output(node))
+ node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
+ else
+ node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT;
+
+ ret = cfe_validate_fmt_meta(node, &node->meta_fmt);
+ if (ret)
+ return ret;
+ }
+
+ mutex_init(&node->lock);
+
+ q = &node->buffer_queue;
+ q->type = node_supports_image(node) ? node->vid_fmt.type :
+ node->meta_fmt.type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = node;
+ q->ops = &cfe_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer)
+ : sizeof(struct cfe_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &node->lock;
+ q->min_queued_buffers = 1;
+ q->dev = &cfe->pdev->dev;
+
+ ret = vb2_queue_init(q);
+ if (ret) {
+ cfe_err(cfe, "vb2_queue_init() failed\n");
+ return ret;
+ }
+
+ INIT_LIST_HEAD(&node->dma_queue);
+
+ vdev = &node->video_dev;
+ vdev->release = cfe_node_release;
+ vdev->fops = &cfe_fops;
+ vdev->ioctl_ops = &cfe_ioctl_ops;
+ vdev->entity.ops = &cfe_media_entity_ops;
+ vdev->v4l2_dev = &cfe->v4l2_dev;
+ vdev->vfl_dir = (node_supports_image_output(node) ||
+ node_supports_meta_output(node)) ?
+ VFL_DIR_RX :
+ VFL_DIR_TX;
+ vdev->queue = q;
+ vdev->lock = &node->lock;
+ vdev->device_caps = node_desc[id].caps;
+ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+
+ /* Define the device names */
+ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME,
+ node_desc[id].name);
+
+ video_set_drvdata(vdev, node);
+ node->pad.flags = node_desc[id].pad_flags;
+ media_entity_pads_init(&vdev->entity, 1, &node->pad);
+
+ if (!node_supports_image(node)) {
+ v4l2_disable_ioctl(&node->video_dev,
+ VIDIOC_ENUM_FRAMEINTERVALS);
+ v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES);
+ }
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ cfe_err(cfe, "Unable to register video device %s\n",
+ vdev->name);
+ return ret;
+ }
+
+ cfe_info(cfe, "Registered [%s] node id %d as /dev/video%u\n",
+ vdev->name, id, vdev->num);
+
+ /*
+ * Acquire a reference to cfe, which will be released when the video
+ * device will be unregistered and userspace will have closed all open
+ * file handles.
+ */
+ cfe_get(cfe);
+ set_state(cfe, NODE_REGISTERED, id);
+
+ return 0;
+}
+
+static void cfe_unregister_nodes(struct cfe_device *cfe)
+{
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ struct cfe_node *node = &cfe->node[i];
+
+ if (check_state(cfe, NODE_REGISTERED, i)) {
+ clear_state(cfe, NODE_REGISTERED, i);
+ video_unregister_device(&node->video_dev);
+ }
+ }
+}
+
+static int cfe_link_node_pads(struct cfe_device *cfe)
+{
+ struct media_pad *remote_pad;
+ int ret;
+
+ /* Source -> CSI2 */
+
+ ret = v4l2_create_fwnode_links_to_pad(cfe->source_sd,
+ &cfe->csi2.pad[CSI2_PAD_SINK],
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+
+ if (ret) {
+ cfe_err(cfe, "Failed to create links to the source: %d\n", ret);
+ return ret;
+ }
+
+ remote_pad = media_pad_remote_pad_unique(&cfe->csi2.pad[CSI2_PAD_SINK]);
+ if (IS_ERR(remote_pad)) {
+ ret = PTR_ERR(remote_pad);
+ cfe_err(cfe, "Failed to get unique remote source pad: %d\n",
+ ret);
+ return ret;
+ }
+
+ cfe->source_pad = remote_pad->index;
+
+ for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
+ struct cfe_node *node = &cfe->node[i];
+
+ if (!check_state(cfe, NODE_REGISTERED, i))
+ continue;
+
+ /* CSI2 channel # -> /dev/video# */
+ ret = media_create_pad_link(&cfe->csi2.sd.entity,
+ node_desc[i].link_pad,
+ &node->video_dev.entity, 0, 0);
+ if (ret)
+ return ret;
+
+ if (node_supports_image(node)) {
+ /* CSI2 channel # -> FE Input */
+ ret = media_create_pad_link(&cfe->csi2.sd.entity,
+ node_desc[i].link_pad,
+ &cfe->fe.sd.entity,
+ FE_STREAM_PAD, 0);
+ if (ret)
+ return ret;
+ }
+ }
+
+ for (unsigned int i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) {
+ struct cfe_node *node = &cfe->node[i];
+ struct media_entity *src, *dst;
+ unsigned int src_pad, dst_pad;
+
+ if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) {
+ /* FE -> /dev/video# */
+ src = &cfe->fe.sd.entity;
+ src_pad = node_desc[i].link_pad;
+ dst = &node->video_dev.entity;
+ dst_pad = 0;
+ } else {
+ /* /dev/video# -> FE */
+ dst = &cfe->fe.sd.entity;
+ dst_pad = node_desc[i].link_pad;
+ src = &node->video_dev.entity;
+ src_pad = 0;
+ }
+
+ ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cfe_probe_complete(struct cfe_device *cfe)
+{
+ int ret;
+
+ cfe->v4l2_dev.notify = cfe_notify;
+
+ for (unsigned int i = 0; i < NUM_NODES; i++) {
+ ret = cfe_register_node(cfe, i);
+ if (ret) {
+ cfe_err(cfe, "Unable to register video node %u.\n", i);
+ goto unregister;
+ }
+ }
+
+ ret = cfe_link_node_pads(cfe);
+ if (ret) {
+ cfe_err(cfe, "Unable to link node pads.\n");
+ goto unregister;
+ }
+
+ ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev);
+ if (ret) {
+ cfe_err(cfe, "Unable to register subdev nodes.\n");
+ goto unregister;
+ }
+
+ return 0;
+
+unregister:
+ cfe_unregister_nodes(cfe);
+ return ret;
+}
+
+static int cfe_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *subdev,
+ struct v4l2_async_connection *asd)
+{
+ struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
+
+ if (cfe->source_sd) {
+ cfe_err(cfe, "Rejecting subdev %s (Already set!!)",
+ subdev->name);
+ return 0;
+ }
+
+ cfe->source_sd = subdev;
+
+ cfe_dbg(cfe, "Using source %s for capture\n", subdev->name);
+
+ return 0;
+}
+
+static int cfe_async_complete(struct v4l2_async_notifier *notifier)
+{
+ struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
+
+ return cfe_probe_complete(cfe);
+}
+
+static const struct v4l2_async_notifier_operations cfe_async_ops = {
+ .bound = cfe_async_bound,
+ .complete = cfe_async_complete,
+};
+
+static int cfe_register_async_nf(struct cfe_device *cfe)
+{
+ struct platform_device *pdev = cfe->pdev;
+ struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
+ struct fwnode_handle *local_ep_fwnode;
+ struct v4l2_async_connection *asd;
+ int ret;
+
+ local_ep_fwnode = fwnode_graph_get_endpoint_by_id(pdev->dev.fwnode, 0,
+ 0, 0);
+ if (!local_ep_fwnode) {
+ cfe_err(cfe, "Failed to find local endpoint fwnode\n");
+ return -ENODEV;
+ }
+
+ /* Parse the local endpoint and validate its configuration. */
+ ret = v4l2_fwnode_endpoint_parse(local_ep_fwnode, &ep);
+ if (ret) {
+ cfe_err(cfe, "Failed to find remote endpoint fwnode\n");
+ goto err_put_local_fwnode;
+ }
+
+ for (unsigned int lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes;
+ lane++) {
+ if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) {
+ cfe_err(cfe, "Data lanes reordering not supported\n");
+ ret = -EINVAL;
+ goto err_put_local_fwnode;
+ }
+ }
+
+ cfe->csi2.dphy.max_lanes = ep.bus.mipi_csi2.num_data_lanes;
+ cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags;
+
+ /* Initialize and register the async notifier. */
+ v4l2_async_nf_init(&cfe->notifier, &cfe->v4l2_dev);
+ cfe->notifier.ops = &cfe_async_ops;
+
+ asd = v4l2_async_nf_add_fwnode_remote(&cfe->notifier, local_ep_fwnode,
+ struct v4l2_async_connection);
+ if (IS_ERR(asd)) {
+ ret = PTR_ERR(asd);
+ cfe_err(cfe, "Error adding subdevice: %d\n", ret);
+ goto err_put_local_fwnode;
+ }
+
+ ret = v4l2_async_nf_register(&cfe->notifier);
+ if (ret) {
+ cfe_err(cfe, "Error registering async notifier: %d\n", ret);
+ goto err_nf_cleanup;
+ }
+
+ fwnode_handle_put(local_ep_fwnode);
+
+ return 0;
+
+err_nf_cleanup:
+ v4l2_async_nf_cleanup(&cfe->notifier);
+err_put_local_fwnode:
+ fwnode_handle_put(local_ep_fwnode);
+
+ return ret;
+}
+
+static int cfe_probe(struct platform_device *pdev)
+{
+ struct cfe_device *cfe;
+ char debugfs_name[32];
+ int ret;
+
+ cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);
+ if (!cfe)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, cfe);
+
+ kref_init(&cfe->kref);
+ cfe->pdev = pdev;
+ cfe->fe_csi2_channel = -1;
+ spin_lock_init(&cfe->state_lock);
+
+ cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(cfe->csi2.base)) {
+ dev_err(&pdev->dev, "Failed to get dma io block\n");
+ ret = PTR_ERR(cfe->csi2.base);
+ goto err_cfe_put;
+ }
+
+ cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(cfe->csi2.dphy.base)) {
+ dev_err(&pdev->dev, "Failed to get host io block\n");
+ ret = PTR_ERR(cfe->csi2.dphy.base);
+ goto err_cfe_put;
+ }
+
+ cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2);
+ if (IS_ERR(cfe->mipi_cfg_base)) {
+ dev_err(&pdev->dev, "Failed to get mipi cfg io block\n");
+ ret = PTR_ERR(cfe->mipi_cfg_base);
+ goto err_cfe_put;
+ }
+
+ cfe->fe.base = devm_platform_ioremap_resource(pdev, 3);
+ if (IS_ERR(cfe->fe.base)) {
+ dev_err(&pdev->dev, "Failed to get pisp fe io block\n");
+ ret = PTR_ERR(cfe->fe.base);
+ goto err_cfe_put;
+ }
+
+ ret = platform_get_irq(pdev, 0);
+ if (ret <= 0) {
+ ret = -EINVAL;
+ goto err_cfe_put;
+ }
+
+ ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to request interrupt\n");
+ ret = -EINVAL;
+ goto err_cfe_put;
+ }
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (ret) {
+ dev_err(&pdev->dev, "DMA enable failed\n");
+ goto err_cfe_put;
+ }
+
+ ret = vb2_dma_contig_set_max_seg_size(&pdev->dev, UINT_MAX);
+ if (ret)
+ goto err_cfe_put;
+
+ /* TODO: Enable clock only when running. */
+ cfe->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(cfe->clk)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk),
+ "clock not found\n");
+ goto err_cfe_put;
+ }
+
+ cfe->mdev.dev = &pdev->dev;
+ cfe->mdev.ops = &cfe_media_device_ops;
+ strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model));
+ strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial));
+ snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s",
+ dev_name(&pdev->dev));
+
+ media_device_init(&cfe->mdev);
+
+ cfe->v4l2_dev.mdev = &cfe->mdev;
+
+ ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev);
+ if (ret) {
+ cfe_err(cfe, "Unable to register v4l2 device.\n");
+ goto err_cfe_put;
+ }
+
+ snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s",
+ dev_name(&pdev->dev));
+ cfe->debugfs = debugfs_create_dir(debugfs_name, NULL);
+ debugfs_create_file("regs", 0440, cfe->debugfs, cfe,
+ &mipi_cfg_regs_fops);
+
+ /* Enable the block power domain */
+ pm_runtime_enable(&pdev->dev);
+
+ ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
+ if (ret)
+ goto err_runtime_disable;
+
+ cfe->csi2.v4l2_dev = &cfe->v4l2_dev;
+ ret = csi2_init(&cfe->csi2, cfe->debugfs);
+ if (ret) {
+ cfe_err(cfe, "Failed to init csi2 (%d)\n", ret);
+ goto err_runtime_put;
+ }
+
+ cfe->fe.v4l2_dev = &cfe->v4l2_dev;
+ ret = pisp_fe_init(&cfe->fe, cfe->debugfs);
+ if (ret) {
+ cfe_err(cfe, "Failed to init pisp fe (%d)\n", ret);
+ goto err_csi2_uninit;
+ }
+
+ cfe->mdev.hw_revision = cfe->fe.hw_revision;
+ ret = media_device_register(&cfe->mdev);
+ if (ret < 0) {
+ cfe_err(cfe, "Unable to register media-controller device.\n");
+ goto err_pisp_fe_uninit;
+ }
+
+ ret = cfe_register_async_nf(cfe);
+ if (ret) {
+ cfe_err(cfe, "Failed to connect subdevs\n");
+ goto err_media_unregister;
+ }
+
+ pm_runtime_put(&cfe->pdev->dev);
+
+ return 0;
+
+err_media_unregister:
+ media_device_unregister(&cfe->mdev);
+err_pisp_fe_uninit:
+ pisp_fe_uninit(&cfe->fe);
+err_csi2_uninit:
+ csi2_uninit(&cfe->csi2);
+err_runtime_put:
+ pm_runtime_put(&cfe->pdev->dev);
+err_runtime_disable:
+ pm_runtime_disable(&pdev->dev);
+ debugfs_remove(cfe->debugfs);
+ v4l2_device_unregister(&cfe->v4l2_dev);
+err_cfe_put:
+ cfe_put(cfe);
+
+ return ret;
+}
+
+static void cfe_remove(struct platform_device *pdev)
+{
+ struct cfe_device *cfe = platform_get_drvdata(pdev);
+
+ debugfs_remove(cfe->debugfs);
+
+ v4l2_async_nf_unregister(&cfe->notifier);
+ v4l2_async_nf_cleanup(&cfe->notifier);
+
+ media_device_unregister(&cfe->mdev);
+ cfe_unregister_nodes(cfe);
+
+ pisp_fe_uninit(&cfe->fe);
+ csi2_uninit(&cfe->csi2);
+
+ pm_runtime_disable(&pdev->dev);
+
+ v4l2_device_unregister(&cfe->v4l2_dev);
+
+ cfe_put(cfe);
+}
+
+static int cfe_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct cfe_device *cfe = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(cfe->clk);
+
+ return 0;
+}
+
+static int cfe_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct cfe_device *cfe = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = clk_prepare_enable(cfe->clk);
+ if (ret) {
+ dev_err(dev, "Unable to enable clock\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops cfe_pm_ops = {
+ SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static const struct of_device_id cfe_of_match[] = {
+ { .compatible = "raspberrypi,rp1-cfe" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cfe_of_match);
+
+static struct platform_driver cfe_driver = {
+ .probe = cfe_probe,
+ .remove = cfe_remove,
+ .driver = {
+ .name = CFE_MODULE_NAME,
+ .of_match_table = cfe_of_match,
+ .pm = &cfe_pm_ops,
+ },
+};
+
+module_platform_driver(cfe_driver);
+
+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
+MODULE_DESCRIPTION("Raspberry Pi RP1 Camera Front End driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CFE_VERSION);
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h
new file mode 100644
index 000000000000..c63cc314be3c
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RP1 CFE Driver
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ * Copyright (c) 2023-2024 Ideas on Board Oy
+ */
+#ifndef _RP1_CFE_
+#define _RP1_CFE_
+
+#include <linux/media-bus-format.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+extern bool cfe_debug_verbose;
+
+enum cfe_remap_types {
+ CFE_REMAP_16BIT,
+ CFE_REMAP_COMPRESSED,
+ CFE_NUM_REMAP,
+};
+
+#define CFE_FORMAT_FLAG_META_OUT BIT(0)
+#define CFE_FORMAT_FLAG_META_CAP BIT(1)
+#define CFE_FORMAT_FLAG_FE_OUT BIT(2)
+
+struct cfe_fmt {
+ u32 fourcc;
+ u32 code;
+ u8 depth;
+ u8 csi_dt;
+ u32 remap[CFE_NUM_REMAP];
+ u32 flags;
+};
+
+extern const struct v4l2_mbus_framefmt cfe_default_format;
+
+const struct cfe_fmt *find_format_by_code(u32 code);
+const struct cfe_fmt *find_format_by_pix(u32 pixelformat);
+u32 cfe_find_16bit_code(u32 code);
+u32 cfe_find_compressed_code(u32 code);
+
+#endif
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
new file mode 100644
index 000000000000..35c2ab1e2cd4
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RP1 CSI-2 Driver
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ * Copyright (c) 2023-2024 Ideas on Board Oy
+ */
+
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <linux/seq_file.h>
+
+#include <media/videobuf2-dma-contig.h>
+
+#include "cfe.h"
+#include "csi2.h"
+
+#include "cfe-trace.h"
+
+static bool csi2_track_errors;
+module_param_named(track_csi2_errors, csi2_track_errors, bool, 0);
+MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors");
+
+#define csi2_dbg(csi2, fmt, arg...) dev_dbg((csi2)->v4l2_dev->dev, fmt, ##arg)
+#define csi2_err(csi2, fmt, arg...) dev_err((csi2)->v4l2_dev->dev, fmt, ##arg)
+
+/* CSI2-DMA registers */
+#define CSI2_STATUS 0x000
+#define CSI2_QOS 0x004
+#define CSI2_DISCARDS_OVERFLOW 0x008
+#define CSI2_DISCARDS_INACTIVE 0x00c
+#define CSI2_DISCARDS_UNMATCHED 0x010
+#define CSI2_DISCARDS_LEN_LIMIT 0x014
+
+#define CSI2_DISCARDS_AMOUNT_SHIFT 0
+#define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0)
+#define CSI2_DISCARDS_DT_SHIFT 24
+#define CSI2_DISCARDS_DT_MASK GENMASK(29, 24)
+#define CSI2_DISCARDS_VC_SHIFT 30
+#define CSI2_DISCARDS_VC_MASK GENMASK(31, 30)
+
+#define CSI2_LLEV_PANICS 0x018
+#define CSI2_ULEV_PANICS 0x01c
+#define CSI2_IRQ_MASK 0x020
+#define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0)
+#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1)
+#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2)
+#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3)
+#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4)
+#define CSI2_IRQ_MASK_IRQ_ALL \
+ (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \
+ CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \
+ CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \
+ CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE)
+
+#define CSI2_CTRL 0x024
+#define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28)
+#define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c)
+#define CSI2_CH_ADDR1(x) ((x) * 0x40 + 0x3c)
+#define CSI2_CH_STRIDE(x) ((x) * 0x40 + 0x30)
+#define CSI2_CH_LENGTH(x) ((x) * 0x40 + 0x34)
+#define CSI2_CH_DEBUG(x) ((x) * 0x40 + 0x38)
+#define CSI2_CH_FRAME_SIZE(x) ((x) * 0x40 + 0x40)
+#define CSI2_CH_COMP_CTRL(x) ((x) * 0x40 + 0x44)
+#define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48)
+
+/* CSI2_STATUS */
+#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x))
+#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x))
+#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x))
+#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x))
+#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x))
+#define CSI2_STATUS_IRQ_CH_MASK(x) \
+ (CSI2_STATUS_IRQ_FS(x) | CSI2_STATUS_IRQ_FE(x) | \
+ CSI2_STATUS_IRQ_FE_ACK(x) | CSI2_STATUS_IRQ_LE(x) | \
+ CSI2_STATUS_IRQ_LE_ACK(x))
+#define CSI2_STATUS_IRQ_OVERFLOW BIT(20)
+#define CSI2_STATUS_IRQ_DISCARD_OVERFLOW BIT(21)
+#define CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT BIT(22)
+#define CSI2_STATUS_IRQ_DISCARD_UNMATCHED BIT(23)
+#define CSI2_STATUS_IRQ_DISCARD_INACTIVE BIT(24)
+
+/* CSI2_CTRL */
+#define CSI2_CTRL_EOP_IS_EOL BIT(0)
+
+/* CSI2_CH_CTRL */
+#define CSI2_CH_CTRL_DMA_EN BIT(0)
+#define CSI2_CH_CTRL_FORCE BIT(3)
+#define CSI2_CH_CTRL_AUTO_ARM BIT(4)
+#define CSI2_CH_CTRL_IRQ_EN_FS BIT(13)
+#define CSI2_CH_CTRL_IRQ_EN_FE BIT(14)
+#define CSI2_CH_CTRL_IRQ_EN_FE_ACK BIT(15)
+#define CSI2_CH_CTRL_IRQ_EN_LE BIT(16)
+#define CSI2_CH_CTRL_IRQ_EN_LE_ACK BIT(17)
+#define CSI2_CH_CTRL_FLUSH_FE BIT(28)
+#define CSI2_CH_CTRL_PACK_LINE BIT(29)
+#define CSI2_CH_CTRL_PACK_BYTES BIT(30)
+#define CSI2_CH_CTRL_CH_MODE_MASK GENMASK(2, 1)
+#define CSI2_CH_CTRL_VC_MASK GENMASK(6, 5)
+#define CSI2_CH_CTRL_DT_MASK GENMASK(12, 7)
+#define CSI2_CH_CTRL_LC_MASK GENMASK(27, 18)
+
+/* CHx_COMPRESSION_CONTROL */
+#define CSI2_CH_COMP_CTRL_OFFSET_MASK GENMASK(15, 0)
+#define CSI2_CH_COMP_CTRL_SHIFT_MASK GENMASK(19, 16)
+#define CSI2_CH_COMP_CTRL_MODE_MASK GENMASK(25, 24)
+
+static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset)
+{
+ return readl(csi2->base + offset);
+}
+
+static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val)
+{
+ writel(val, csi2->base + offset);
+}
+
+static inline void set_field(u32 *valp, u32 field, u32 mask)
+{
+ u32 val = *valp;
+
+ val &= ~mask;
+ val |= (field << __ffs(mask)) & mask;
+ *valp = val;
+}
+
+static int csi2_regs_show(struct seq_file *s, void *data)
+{
+ struct csi2_device *csi2 = s->private;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev);
+ if (ret)
+ return ret;
+
+#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg))
+#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, \
+ csi2_reg_read(csi2, reg(idx)))
+
+ DUMP(CSI2_STATUS);
+ DUMP(CSI2_DISCARDS_OVERFLOW);
+ DUMP(CSI2_DISCARDS_INACTIVE);
+ DUMP(CSI2_DISCARDS_UNMATCHED);
+ DUMP(CSI2_DISCARDS_LEN_LIMIT);
+ DUMP(CSI2_LLEV_PANICS);
+ DUMP(CSI2_ULEV_PANICS);
+ DUMP(CSI2_IRQ_MASK);
+ DUMP(CSI2_CTRL);
+
+ for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) {
+ DUMP_CH(i, CSI2_CH_CTRL);
+ DUMP_CH(i, CSI2_CH_ADDR0);
+ DUMP_CH(i, CSI2_CH_ADDR1);
+ DUMP_CH(i, CSI2_CH_STRIDE);
+ DUMP_CH(i, CSI2_CH_LENGTH);
+ DUMP_CH(i, CSI2_CH_DEBUG);
+ DUMP_CH(i, CSI2_CH_FRAME_SIZE);
+ DUMP_CH(i, CSI2_CH_COMP_CTRL);
+ DUMP_CH(i, CSI2_CH_FE_FRAME_ID);
+ }
+
+#undef DUMP
+#undef DUMP_CH
+
+ pm_runtime_put(csi2->v4l2_dev->dev);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(csi2_regs);
+
+static int csi2_errors_show(struct seq_file *s, void *data)
+{
+ struct csi2_device *csi2 = s->private;
+ unsigned long flags;
+ u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
+ u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
+ u32 overflows;
+
+ spin_lock_irqsave(&csi2->errors_lock, flags);
+
+ memcpy(discards_table, csi2->discards_table, sizeof(discards_table));
+ memcpy(discards_dt_table, csi2->discards_dt_table,
+ sizeof(discards_dt_table));
+ overflows = csi2->overflows;
+
+ csi2->overflows = 0;
+ memset(csi2->discards_table, 0, sizeof(discards_table));
+ memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table));
+
+ spin_unlock_irqrestore(&csi2->errors_lock, flags);
+
+ seq_printf(s, "Overflows %u\n", overflows);
+ seq_puts(s, "Discards:\n");
+ seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n");
+
+ for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) {
+ seq_printf(s, "%u %10u %10u %10u %10u\n", vc,
+ discards_table[vc][DISCARDS_TABLE_OVERFLOW],
+ discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT],
+ discards_table[vc][DISCARDS_TABLE_UNMATCHED],
+ discards_table[vc][DISCARDS_TABLE_INACTIVE]);
+ }
+
+ seq_printf(s, "Last DT %10u %10u %10u %10u\n",
+ discards_dt_table[DISCARDS_TABLE_OVERFLOW],
+ discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT],
+ discards_dt_table[DISCARDS_TABLE_UNMATCHED],
+ discards_dt_table[DISCARDS_TABLE_INACTIVE]);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(csi2_errors);
+
+static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status)
+{
+ spin_lock(&csi2->errors_lock);
+
+ if (status & CSI2_STATUS_IRQ_OVERFLOW)
+ csi2->overflows++;
+
+ for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) {
+ static const u32 discard_bits[] = {
+ CSI2_STATUS_IRQ_DISCARD_OVERFLOW,
+ CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT,
+ CSI2_STATUS_IRQ_DISCARD_UNMATCHED,
+ CSI2_STATUS_IRQ_DISCARD_INACTIVE,
+ };
+ static const u8 discard_regs[] = {
+ CSI2_DISCARDS_OVERFLOW,
+ CSI2_DISCARDS_LEN_LIMIT,
+ CSI2_DISCARDS_UNMATCHED,
+ CSI2_DISCARDS_INACTIVE,
+ };
+ u32 amount;
+ u8 dt, vc;
+ u32 v;
+
+ if (!(status & discard_bits[i]))
+ continue;
+
+ v = csi2_reg_read(csi2, discard_regs[i]);
+ csi2_reg_write(csi2, discard_regs[i], 0);
+
+ amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >>
+ CSI2_DISCARDS_AMOUNT_SHIFT;
+ dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT;
+ vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT;
+
+ csi2->discards_table[vc][i] += amount;
+ csi2->discards_dt_table[i] = dt;
+ }
+
+ spin_unlock(&csi2->errors_lock);
+}
+
+void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof)
+{
+ u32 status;
+
+ status = csi2_reg_read(csi2, CSI2_STATUS);
+
+ /* Write value back to clear the interrupts */
+ csi2_reg_write(csi2, CSI2_STATUS, status);
+
+ for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
+ u32 dbg;
+
+ if ((status & CSI2_STATUS_IRQ_CH_MASK(i)) == 0)
+ continue;
+
+ dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i));
+
+ trace_csi2_irq(i, status, dbg);
+
+ sof[i] = !!(status & CSI2_STATUS_IRQ_FS(i));
+ eof[i] = !!(status & CSI2_STATUS_IRQ_FE_ACK(i));
+ }
+
+ if (csi2_track_errors)
+ csi2_isr_handle_errors(csi2, status);
+}
+
+void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
+ dma_addr_t dmaaddr, unsigned int stride, unsigned int size)
+{
+ u64 addr = dmaaddr;
+ /*
+ * ADDRESS0 must be written last as it triggers the double buffering
+ * mechanism for all buffer registers within the hardware.
+ */
+ addr >>= 4;
+ csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4);
+ csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4);
+ csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32);
+ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff);
+}
+
+void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
+ enum csi2_compression_mode mode, unsigned int shift,
+ unsigned int offset)
+{
+ u32 compression = 0;
+
+ set_field(&compression, CSI2_CH_COMP_CTRL_OFFSET_MASK, offset);
+ set_field(&compression, CSI2_CH_COMP_CTRL_SHIFT_MASK, shift);
+ set_field(&compression, CSI2_CH_COMP_CTRL_MODE_MASK, mode);
+ csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression);
+}
+
+void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
+ enum csi2_mode mode, bool auto_arm, bool pack_bytes,
+ unsigned int width, unsigned int height,
+ u8 vc, u8 dt)
+{
+ u32 ctrl;
+
+ csi2_dbg(csi2, "%s [%u]\n", __func__, channel);
+
+ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0);
+ csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0);
+ csi2_reg_write(csi2, CSI2_STATUS, CSI2_STATUS_IRQ_CH_MASK(channel));
+
+ /* Enable channel and FS/FE interrupts. */
+ ctrl = CSI2_CH_CTRL_DMA_EN | CSI2_CH_CTRL_IRQ_EN_FS |
+ CSI2_CH_CTRL_IRQ_EN_FE_ACK | CSI2_CH_CTRL_PACK_LINE;
+ /* PACK_BYTES ensures no striding for embedded data. */
+ if (pack_bytes)
+ ctrl |= CSI2_CH_CTRL_PACK_BYTES;
+
+ if (auto_arm)
+ ctrl |= CSI2_CH_CTRL_AUTO_ARM;
+
+ if (width && height) {
+ set_field(&ctrl, mode, CSI2_CH_CTRL_CH_MODE_MASK);
+ csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel),
+ (height << 16) | width);
+ } else {
+ set_field(&ctrl, 0x0, CSI2_CH_CTRL_CH_MODE_MASK);
+ csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
+ }
+
+ set_field(&ctrl, vc, CSI2_CH_CTRL_VC_MASK);
+ set_field(&ctrl, dt, CSI2_CH_CTRL_DT_MASK);
+ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl);
+ csi2->num_lines[channel] = height;
+}
+
+void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel)
+{
+ csi2_dbg(csi2, "%s [%u]\n", __func__, channel);
+
+ /* Channel disable. Use FORCE to allow stopping mid-frame. */
+ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), CSI2_CH_CTRL_FORCE);
+ /* Latch the above change by writing to the ADDR0 register. */
+ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
+ /* Write this again, the HW needs it! */
+ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
+}
+
+void csi2_open_rx(struct csi2_device *csi2)
+{
+ csi2_reg_write(csi2, CSI2_IRQ_MASK,
+ csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0);
+
+ dphy_start(&csi2->dphy);
+
+ csi2_reg_write(csi2, CSI2_CTRL, CSI2_CTRL_EOP_IS_EOL);
+}
+
+void csi2_close_rx(struct csi2_device *csi2)
+{
+ dphy_stop(&csi2->dphy);
+
+ csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
+}
+
+static int csi2_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[] = { {
+ .sink_pad = CSI2_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = CSI2_PAD_FIRST_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ } };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ int ret;
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
+ &cfe_default_format);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int csi2_pad_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ if (format->pad == CSI2_PAD_SINK) {
+ /* Store the sink format and propagate it to the source. */
+
+ const struct cfe_fmt *cfe_fmt;
+
+ cfe_fmt = find_format_by_code(format->format.code);
+ if (!cfe_fmt) {
+ cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB10_1X10);
+ format->format.code = cfe_fmt->code;
+ }
+
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ format->format.field = V4L2_FIELD_NONE;
+
+ *fmt = format->format;
+ } else {
+ /* Only allow changing the source pad mbus code. */
+
+ struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
+ u32 sink_code;
+ u32 code;
+
+ sink_fmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ format->pad,
+ format->stream);
+ if (!sink_fmt)
+ return -EINVAL;
+
+ source_fmt = v4l2_subdev_state_get_format(state, format->pad,
+ format->stream);
+ if (!source_fmt)
+ return -EINVAL;
+
+ sink_code = sink_fmt->code;
+ code = format->format.code;
+
+ /*
+ * Only allow changing the mbus code to:
+ * - The sink's mbus code
+ * - The 16-bit version of the sink's mbus code
+ * - The compressed version of the sink's mbus code
+ */
+ if (code == sink_code ||
+ code == cfe_find_16bit_code(sink_code) ||
+ code == cfe_find_compressed_code(sink_code))
+ source_fmt->code = code;
+
+ format->format.code = source_fmt->code;
+ }
+
+ return 0;
+}
+
+static int csi2_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+ if (ret)
+ return ret;
+
+ /* Only stream ID 0 allowed on source pads */
+ for (unsigned int i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (route->source_stream != 0)
+ return -EINVAL;
+ }
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing,
+ &cfe_default_format);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = csi2_pad_set_fmt,
+ .set_routing = csi2_set_routing,
+ .link_validate = v4l2_subdev_link_validate_default,
+};
+
+static const struct media_entity_operations csi2_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+static const struct v4l2_subdev_ops csi2_subdev_ops = {
+ .pad = &csi2_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
+ .init_state = csi2_init_state,
+};
+
+int csi2_init(struct csi2_device *csi2, struct dentry *debugfs)
+{
+ unsigned int ret;
+
+ spin_lock_init(&csi2->errors_lock);
+
+ csi2->dphy.dev = csi2->v4l2_dev->dev;
+ dphy_probe(&csi2->dphy);
+
+ debugfs_create_file("csi2_regs", 0440, debugfs, csi2, &csi2_regs_fops);
+
+ if (csi2_track_errors)
+ debugfs_create_file("csi2_errors", 0440, debugfs, csi2,
+ &csi2_errors_fops);
+
+ csi2->pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+ for (unsigned int i = CSI2_PAD_FIRST_SOURCE;
+ i < CSI2_PAD_FIRST_SOURCE + CSI2_PAD_NUM_SOURCES; i++)
+ csi2->pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad),
+ csi2->pad);
+ if (ret)
+ return ret;
+
+ /* Initialize subdev */
+ v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
+ csi2->sd.internal_ops = &csi2_internal_ops;
+ csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ csi2->sd.entity.ops = &csi2_entity_ops;
+ csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
+ csi2->sd.owner = THIS_MODULE;
+ snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2");
+
+ ret = v4l2_subdev_init_finalize(&csi2->sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd);
+ if (ret) {
+ csi2_err(csi2, "Failed register csi2 subdev (%d)\n", ret);
+ goto err_subdev_cleanup;
+ }
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(&csi2->sd);
+err_entity_cleanup:
+ media_entity_cleanup(&csi2->sd.entity);
+
+ return ret;
+}
+
+void csi2_uninit(struct csi2_device *csi2)
+{
+ v4l2_device_unregister_subdev(&csi2->sd);
+ v4l2_subdev_cleanup(&csi2->sd);
+ media_entity_cleanup(&csi2->sd.entity);
+}
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h
new file mode 100644
index 000000000000..a8ee5de565fb
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.h
@@ -0,0 +1,89 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * RP1 CSI-2 Driver
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ * Copyright (c) 2023-2024 Ideas on Board Oy
+ */
+
+#ifndef _RP1_CSI2_
+#define _RP1_CSI2_
+
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "dphy.h"
+
+#define CSI2_NUM_CHANNELS 4
+
+#define CSI2_PAD_SINK 0
+#define CSI2_PAD_FIRST_SOURCE 1
+#define CSI2_PAD_NUM_SOURCES 4
+#define CSI2_NUM_PADS 5
+
+#define DISCARDS_TABLE_NUM_VCS 4
+
+enum csi2_mode {
+ CSI2_MODE_NORMAL = 0,
+ CSI2_MODE_REMAP = 1,
+ CSI2_MODE_COMPRESSED = 2,
+ CSI2_MODE_FE_STREAMING = 3,
+};
+
+enum csi2_compression_mode {
+ CSI2_COMPRESSION_DELTA = 1,
+ CSI2_COMPRESSION_SIMPLE = 2,
+ CSI2_COMPRESSION_COMBINED = 3,
+};
+
+enum discards_table_index {
+ DISCARDS_TABLE_OVERFLOW = 0,
+ DISCARDS_TABLE_LENGTH_LIMIT,
+ DISCARDS_TABLE_UNMATCHED,
+ DISCARDS_TABLE_INACTIVE,
+ DISCARDS_TABLE_NUM_ENTRIES,
+};
+
+struct csi2_device {
+ /* Parent V4l2 device */
+ struct v4l2_device *v4l2_dev;
+
+ void __iomem *base;
+
+ struct dphy_data dphy;
+
+ enum v4l2_mbus_type bus_type;
+ unsigned int bus_flags;
+ unsigned int num_lines[CSI2_NUM_CHANNELS];
+
+ struct media_pad pad[CSI2_NUM_PADS];
+ struct v4l2_subdev sd;
+
+ /* lock for csi2 errors counters */
+ spinlock_t errors_lock;
+ u32 overflows;
+ u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
+ u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
+};
+
+void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof);
+void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
+ dma_addr_t dmaaddr, unsigned int stride,
+ unsigned int size);
+void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
+ enum csi2_compression_mode mode, unsigned int shift,
+ unsigned int offset);
+void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
+ enum csi2_mode mode, bool auto_arm,
+ bool pack_bytes, unsigned int width,
+ unsigned int height, u8 vc, u8 dt);
+void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel);
+void csi2_open_rx(struct csi2_device *csi2);
+void csi2_close_rx(struct csi2_device *csi2);
+int csi2_init(struct csi2_device *csi2, struct dentry *debugfs);
+void csi2_uninit(struct csi2_device *csi2);
+
+#endif
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c
new file mode 100644
index 000000000000..b443f0f56ddc
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * RP1 CSI-2 Driver
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ * Copyright (c) 2023-2024 Ideas on Board Oy
+ */
+
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+
+#include "dphy.h"
+
+#define dphy_dbg(dphy, fmt, arg...) dev_dbg((dphy)->dev, fmt, ##arg)
+#define dphy_err(dphy, fmt, arg...) dev_err((dphy)->dev, fmt, ##arg)
+
+/* DW dphy Host registers */
+#define DPHY_VERSION 0x000
+#define DPHY_N_LANES 0x004
+#define DPHY_RESETN 0x008
+#define DPHY_PHY_SHUTDOWNZ 0x040
+#define DPHY_PHY_RSTZ 0x044
+#define DPHY_PHY_RX 0x048
+#define DPHY_PHY_STOPSTATE 0x04c
+#define DPHY_PHY_TST_CTRL0 0x050
+#define DPHY_PHY_TST_CTRL1 0x054
+#define DPHY_PHY2_TST_CTRL0 0x058
+#define DPHY_PHY2_TST_CTRL1 0x05c
+
+/* DW dphy Host Transactions */
+#define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44
+#define DPHY_PLL_INPUT_DIV_OFFSET 0x17
+#define DPHY_PLL_LOOP_DIV_OFFSET 0x18
+#define DPHY_PLL_DIV_CTRL_OFFSET 0x19
+
+static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset)
+{
+ return readl(dphy->base + offset);
+}
+
+static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data)
+{
+ writel(data, dphy->base + offset);
+}
+
+static void set_tstclr(struct dphy_data *dphy, u32 val)
+{
+ u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0);
+
+ dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~1) | val);
+}
+
+static void set_tstclk(struct dphy_data *dphy, u32 val)
+{
+ u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0);
+
+ dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1));
+}
+
+static uint8_t get_tstdout(struct dphy_data *dphy)
+{
+ u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
+
+ return ((ctrl1 >> 8) & 0xff);
+}
+
+static void set_testen(struct dphy_data *dphy, u32 val)
+{
+ u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
+
+ dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1,
+ (ctrl1 & ~(1 << 16)) | (val << 16));
+}
+
+static void set_testdin(struct dphy_data *dphy, u32 val)
+{
+ u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
+
+ dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, (ctrl1 & ~0xff) | val);
+}
+
+static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code,
+ uint8_t test_data)
+{
+ /* See page 101 of the MIPI DPHY databook. */
+ set_tstclk(dphy, 1);
+ set_testen(dphy, 0);
+ set_testdin(dphy, test_code);
+ set_testen(dphy, 1);
+ set_tstclk(dphy, 0);
+ set_testen(dphy, 0);
+ set_testdin(dphy, test_data);
+ set_tstclk(dphy, 1);
+ return get_tstdout(dphy);
+}
+
+static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps)
+{
+ /* See Table 5-1 on page 65 of dphy databook */
+ static const u16 hsfreqrange_table[][2] = {
+ { 89, 0b000000 }, { 99, 0b010000 }, { 109, 0b100000 },
+ { 129, 0b000001 }, { 139, 0b010001 }, { 149, 0b100001 },
+ { 169, 0b000010 }, { 179, 0b010010 }, { 199, 0b100010 },
+ { 219, 0b000011 }, { 239, 0b010011 }, { 249, 0b100011 },
+ { 269, 0b000100 }, { 299, 0b010100 }, { 329, 0b000101 },
+ { 359, 0b010101 }, { 399, 0b100101 }, { 449, 0b000110 },
+ { 499, 0b010110 }, { 549, 0b000111 }, { 599, 0b010111 },
+ { 649, 0b001000 }, { 699, 0b011000 }, { 749, 0b001001 },
+ { 799, 0b011001 }, { 849, 0b101001 }, { 899, 0b111001 },
+ { 949, 0b001010 }, { 999, 0b011010 }, { 1049, 0b101010 },
+ { 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 },
+ { 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 },
+ { 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 },
+ };
+ unsigned int i;
+
+ if (mbps < 80 || mbps > 1500)
+ dphy_err(dphy, "DPHY: Datarate %u Mbps out of range\n", mbps);
+
+ for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) {
+ if (mbps <= hsfreqrange_table[i][0])
+ break;
+ }
+
+ dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET,
+ hsfreqrange_table[i][1] << 1);
+}
+
+static void dphy_init(struct dphy_data *dphy)
+{
+ dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 0);
+ dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 0);
+ set_tstclk(dphy, 1);
+ set_testen(dphy, 0);
+ set_tstclr(dphy, 1);
+ usleep_range(15, 20);
+ set_tstclr(dphy, 0);
+ usleep_range(15, 20);
+
+ dphy_set_hsfreqrange(dphy, dphy->dphy_rate);
+
+ usleep_range(5, 10);
+ dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 1);
+ usleep_range(5, 10);
+ dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 1);
+}
+
+void dphy_start(struct dphy_data *dphy)
+{
+ dphy_dbg(dphy, "%s: Link rate %u Mbps, %u data lanes\n", __func__,
+ dphy->dphy_rate, dphy->active_lanes);
+
+ dw_csi2_host_write(dphy, DPHY_N_LANES, (dphy->active_lanes - 1));
+ dphy_init(dphy);
+ dw_csi2_host_write(dphy, DPHY_RESETN, 0xffffffff);
+ usleep_range(10, 50);
+}
+
+void dphy_stop(struct dphy_data *dphy)
+{
+ dphy_dbg(dphy, "%s\n", __func__);
+
+ /* Set only one lane (lane 0) as active (ON) */
+ dw_csi2_host_write(dphy, DPHY_N_LANES, 0);
+ dw_csi2_host_write(dphy, DPHY_RESETN, 0);
+}
+
+void dphy_probe(struct dphy_data *dphy)
+{
+ u32 host_ver;
+ u8 host_ver_major, host_ver_minor;
+
+ host_ver = dw_csi2_host_read(dphy, DPHY_VERSION);
+ host_ver_major = (u8)((host_ver >> 24) - '0');
+ host_ver_minor = (u8)((host_ver >> 16) - '0');
+ host_ver_minor = host_ver_minor * 10;
+ host_ver_minor += (u8)((host_ver >> 8) - '0');
+
+ dphy_dbg(dphy, "DW dphy Host HW v%u.%u\n", host_ver_major,
+ host_ver_minor);
+}
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h
new file mode 100644
index 000000000000..84fa370957cc
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/dphy.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ * Copyright (c) 2023-2024 Ideas on Board Oy
+ */
+
+#ifndef _RP1_DPHY_
+#define _RP1_DPHY_
+
+#include <linux/io.h>
+#include <linux/types.h>
+
+struct dphy_data {
+ struct device *dev;
+
+ void __iomem *base;
+
+ u32 dphy_rate;
+ u32 max_lanes;
+ u32 active_lanes;
+};
+
+void dphy_probe(struct dphy_data *dphy);
+void dphy_start(struct dphy_data *dphy);
+void dphy_stop(struct dphy_data *dphy);
+
+#endif
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c
new file mode 100644
index 000000000000..05762b1be2bc
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c
@@ -0,0 +1,605 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PiSP Front End Driver
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <linux/seq_file.h>
+
+#include <media/videobuf2-dma-contig.h>
+
+#include "cfe.h"
+#include "pisp-fe.h"
+
+#include "cfe-trace.h"
+
+#define FE_VERSION 0x000
+#define FE_CONTROL 0x004
+#define FE_STATUS 0x008
+#define FE_FRAME_STATUS 0x00c
+#define FE_ERROR_STATUS 0x010
+#define FE_OUTPUT_STATUS 0x014
+#define FE_INT_EN 0x018
+#define FE_INT_STATUS 0x01c
+
+/* CONTROL */
+#define FE_CONTROL_QUEUE BIT(0)
+#define FE_CONTROL_ABORT BIT(1)
+#define FE_CONTROL_RESET BIT(2)
+#define FE_CONTROL_LATCH_REGS BIT(3)
+
+/* INT_EN / INT_STATUS */
+#define FE_INT_EOF BIT(0)
+#define FE_INT_SOF BIT(1)
+#define FE_INT_LINES0 BIT(8)
+#define FE_INT_LINES1 BIT(9)
+#define FE_INT_STATS BIT(16)
+#define FE_INT_QREADY BIT(24)
+
+/* STATUS */
+#define FE_STATUS_QUEUED BIT(0)
+#define FE_STATUS_WAITING BIT(1)
+#define FE_STATUS_ACTIVE BIT(2)
+
+#define PISP_FE_CONFIG_BASE_OFFSET 0x0040
+
+#define PISP_FE_ENABLE_STATS_CLUSTER \
+ (PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE | \
+ PISP_FE_ENABLE_BLC | PISP_FE_ENABLE_CDAF_STATS | \
+ PISP_FE_ENABLE_AWB_STATS | PISP_FE_ENABLE_RGBY | \
+ PISP_FE_ENABLE_LSC | PISP_FE_ENABLE_AGC_STATS)
+
+#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i) \
+ ((PISP_FE_ENABLE_CROP0 | PISP_FE_ENABLE_DOWNSCALE0 | \
+ PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i)))
+
+struct pisp_fe_config_param {
+ u32 dirty_flags;
+ u32 dirty_flags_extra;
+ size_t offset;
+ size_t size;
+};
+
+static const struct pisp_fe_config_param pisp_fe_config_map[] = {
+ /* *_dirty_flag_extra types */
+ { 0, PISP_FE_DIRTY_GLOBAL,
+ offsetof(struct pisp_fe_config, global),
+ sizeof(struct pisp_fe_global_config) },
+ { 0, PISP_FE_DIRTY_FLOATING,
+ offsetof(struct pisp_fe_config, floating_stats),
+ sizeof(struct pisp_fe_floating_stats_config) },
+ { 0, PISP_FE_DIRTY_OUTPUT_AXI,
+ offsetof(struct pisp_fe_config, output_axi),
+ sizeof(struct pisp_fe_output_axi_config) },
+ /* *_dirty_flag types */
+ { PISP_FE_ENABLE_INPUT, 0,
+ offsetof(struct pisp_fe_config, input),
+ sizeof(struct pisp_fe_input_config) },
+ { PISP_FE_ENABLE_DECOMPRESS, 0,
+ offsetof(struct pisp_fe_config, decompress),
+ sizeof(struct pisp_decompress_config) },
+ { PISP_FE_ENABLE_DECOMPAND, 0,
+ offsetof(struct pisp_fe_config, decompand),
+ sizeof(struct pisp_fe_decompand_config) },
+ { PISP_FE_ENABLE_BLA, 0,
+ offsetof(struct pisp_fe_config, bla),
+ sizeof(struct pisp_bla_config) },
+ { PISP_FE_ENABLE_DPC, 0,
+ offsetof(struct pisp_fe_config, dpc),
+ sizeof(struct pisp_fe_dpc_config) },
+ { PISP_FE_ENABLE_STATS_CROP, 0,
+ offsetof(struct pisp_fe_config, stats_crop),
+ sizeof(struct pisp_fe_crop_config) },
+ { PISP_FE_ENABLE_BLC, 0,
+ offsetof(struct pisp_fe_config, blc),
+ sizeof(struct pisp_bla_config) },
+ { PISP_FE_ENABLE_CDAF_STATS, 0,
+ offsetof(struct pisp_fe_config, cdaf_stats),
+ sizeof(struct pisp_fe_cdaf_stats_config) },
+ { PISP_FE_ENABLE_AWB_STATS, 0,
+ offsetof(struct pisp_fe_config, awb_stats),
+ sizeof(struct pisp_fe_awb_stats_config) },
+ { PISP_FE_ENABLE_RGBY, 0,
+ offsetof(struct pisp_fe_config, rgby),
+ sizeof(struct pisp_fe_rgby_config) },
+ { PISP_FE_ENABLE_LSC, 0,
+ offsetof(struct pisp_fe_config, lsc),
+ sizeof(struct pisp_fe_lsc_config) },
+ { PISP_FE_ENABLE_AGC_STATS, 0,
+ offsetof(struct pisp_fe_config, agc_stats),
+ sizeof(struct pisp_agc_statistics) },
+ { PISP_FE_ENABLE_CROP0, 0,
+ offsetof(struct pisp_fe_config, ch[0].crop),
+ sizeof(struct pisp_fe_crop_config) },
+ { PISP_FE_ENABLE_DOWNSCALE0, 0,
+ offsetof(struct pisp_fe_config, ch[0].downscale),
+ sizeof(struct pisp_fe_downscale_config) },
+ { PISP_FE_ENABLE_COMPRESS0, 0,
+ offsetof(struct pisp_fe_config, ch[0].compress),
+ sizeof(struct pisp_compress_config) },
+ { PISP_FE_ENABLE_OUTPUT0, 0,
+ offsetof(struct pisp_fe_config, ch[0].output),
+ sizeof(struct pisp_fe_output_config) },
+ { PISP_FE_ENABLE_CROP1, 0,
+ offsetof(struct pisp_fe_config, ch[1].crop),
+ sizeof(struct pisp_fe_crop_config) },
+ { PISP_FE_ENABLE_DOWNSCALE1, 0,
+ offsetof(struct pisp_fe_config, ch[1].downscale),
+ sizeof(struct pisp_fe_downscale_config) },
+ { PISP_FE_ENABLE_COMPRESS1, 0,
+ offsetof(struct pisp_fe_config, ch[1].compress),
+ sizeof(struct pisp_compress_config) },
+ { PISP_FE_ENABLE_OUTPUT1, 0,
+ offsetof(struct pisp_fe_config, ch[1].output),
+ sizeof(struct pisp_fe_output_config) },
+};
+
+#define pisp_fe_dbg(fe, fmt, arg...) dev_dbg((fe)->v4l2_dev->dev, fmt, ##arg)
+#define pisp_fe_info(fe, fmt, arg...) dev_info((fe)->v4l2_dev->dev, fmt, ##arg)
+#define pisp_fe_err(fe, fmt, arg...) dev_err((fe)->v4l2_dev->dev, fmt, ##arg)
+
+static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset)
+{
+ return readl(fe->base + offset);
+}
+
+static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset,
+ u32 val)
+{
+ writel(val, fe->base + offset);
+}
+
+static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe,
+ u32 offset, u32 val)
+{
+ writel_relaxed(val, fe->base + offset);
+}
+
+static int pisp_fe_regs_show(struct seq_file *s, void *data)
+{
+ struct pisp_fe_device *fe = s->private;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev);
+ if (ret)
+ return ret;
+
+ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
+
+#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg))
+ DUMP(FE_VERSION);
+ DUMP(FE_CONTROL);
+ DUMP(FE_STATUS);
+ DUMP(FE_FRAME_STATUS);
+ DUMP(FE_ERROR_STATUS);
+ DUMP(FE_OUTPUT_STATUS);
+ DUMP(FE_INT_EN);
+ DUMP(FE_INT_STATUS);
+#undef DUMP
+
+ pm_runtime_put(fe->v4l2_dev->dev);
+
+ return 0;
+}
+
+DEFINE_SHOW_ATTRIBUTE(pisp_fe_regs);
+
+static void pisp_fe_config_write(struct pisp_fe_device *fe,
+ struct pisp_fe_config *config,
+ unsigned int start_offset, unsigned int size)
+{
+ const unsigned int max_offset =
+ offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]);
+ unsigned int end_offset;
+ u32 *cfg = (u32 *)config;
+
+ start_offset = min(start_offset, max_offset);
+ end_offset = min(start_offset + size, max_offset);
+
+ cfg += start_offset >> 2;
+ for (unsigned int i = start_offset; i < end_offset; i += 4, cfg++)
+ pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i,
+ *cfg);
+}
+
+void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof)
+{
+ u32 status, int_status, out_status, frame_status, error_status;
+
+ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
+ status = pisp_fe_reg_read(fe, FE_STATUS);
+ out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS);
+ frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS);
+ error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS);
+
+ int_status = pisp_fe_reg_read(fe, FE_INT_STATUS);
+ pisp_fe_reg_write(fe, FE_INT_STATUS, int_status);
+
+ trace_fe_irq(status, out_status, frame_status, error_status,
+ int_status);
+
+ /* We do not report interrupts for the input/stream pad. */
+ for (unsigned int i = 0; i < FE_NUM_PADS - 1; i++) {
+ sof[i] = !!(int_status & FE_INT_SOF);
+ eof[i] = !!(int_status & FE_INT_EOF);
+ }
+}
+
+static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg,
+ unsigned int c, struct v4l2_format const *f)
+{
+ unsigned int wbytes;
+
+ wbytes = cfg->ch[c].output.format.width;
+ if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK)
+ wbytes *= 2;
+
+ /* Check output image dimensions are nonzero and not too big */
+ if (cfg->ch[c].output.format.width < 2 ||
+ cfg->ch[c].output.format.height < 2 ||
+ cfg->ch[c].output.format.height > f->fmt.pix.height ||
+ cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline ||
+ wbytes > f->fmt.pix.bytesperline)
+ return false;
+
+ /* Check for zero-sized crops, which could cause lockup */
+ if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) &&
+ ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) ||
+ cfg->ch[c].crop.offset_y >= cfg->input.format.height ||
+ cfg->ch[c].crop.width < 2 || cfg->ch[c].crop.height < 2)))
+ return false;
+
+ if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) &&
+ (cfg->ch[c].downscale.output_width < 2 ||
+ cfg->ch[c].downscale.output_height < 2))
+ return false;
+
+ return true;
+}
+
+static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg)
+{
+ /* Check for zero-sized crop, which could cause lockup */
+ return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) ||
+ (cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) &&
+ cfg->stats_crop.offset_y < cfg->input.format.height &&
+ cfg->stats_crop.width >= 2 && cfg->stats_crop.height >= 2));
+}
+
+int pisp_fe_validate_config(struct pisp_fe_device *fe,
+ struct pisp_fe_config *cfg,
+ struct v4l2_format const *f0,
+ struct v4l2_format const *f1)
+{
+ /*
+ * Check the input is enabled, streaming and has nonzero size;
+ * to avoid cases where the hardware might lock up or try to
+ * read inputs from memory (which this driver doesn't support).
+ */
+ if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) ||
+ cfg->input.streaming != 1 || cfg->input.format.width < 2 ||
+ cfg->input.format.height < 2) {
+ pisp_fe_err(fe, "%s: Input config not valid", __func__);
+ return -EINVAL;
+ }
+
+ for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
+ if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) {
+ if (cfg->global.enables &
+ PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) {
+ pisp_fe_err(fe, "%s: Output %u not valid",
+ __func__, i);
+ return -EINVAL;
+ }
+ continue;
+ }
+
+ if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0))
+ return -EINVAL;
+ }
+
+ if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) &&
+ !pisp_fe_validate_stats(cfg)) {
+ pisp_fe_err(fe, "%s: Stats config not valid", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
+ struct pisp_fe_config *cfg)
+{
+ u64 addr;
+ u32 status;
+
+ /*
+ * Check output buffers exist and outputs are correctly configured.
+ * If valid, set the buffer's DMA address; otherwise disable.
+ */
+ for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
+ struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i];
+
+ if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i)))
+ continue;
+
+ addr = vb2_dma_contig_plane_dma_addr(buf, 0);
+ cfg->output_buffer[i].addr_lo = addr & 0xffffffff;
+ cfg->output_buffer[i].addr_hi = addr >> 32;
+ }
+
+ if (vb2_bufs[FE_STATS_PAD]) {
+ addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0);
+ cfg->stats_buffer.addr_lo = addr & 0xffffffff;
+ cfg->stats_buffer.addr_hi = addr >> 32;
+ }
+
+ /* Set up ILINES interrupts 3/4 of the way down each output */
+ cfg->ch[0].output.ilines =
+ max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2);
+ cfg->ch[1].output.ilines =
+ max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2);
+
+ /*
+ * The hardware must have consumed the previous config by now.
+ * This read of status also serves as a memory barrier before the
+ * sequence of relaxed writes which follow.
+ */
+ status = pisp_fe_reg_read(fe, FE_STATUS);
+ if (WARN_ON(status & FE_STATUS_QUEUED))
+ return;
+
+ /*
+ * Unconditionally write buffers, global and input parameters.
+ * Write cropping and output parameters whenever they are enabled.
+ * Selectively write other parameters that have been marked as
+ * changed through the dirty flags.
+ */
+ pisp_fe_config_write(fe, cfg, 0,
+ offsetof(struct pisp_fe_config, decompress));
+ cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL;
+ cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT;
+ cfg->dirty_flags |= (cfg->global.enables &
+ (PISP_FE_ENABLE_STATS_CROP |
+ PISP_FE_ENABLE_OUTPUT_CLUSTER(0) |
+ PISP_FE_ENABLE_OUTPUT_CLUSTER(1)));
+ for (unsigned int i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) {
+ const struct pisp_fe_config_param *p = &pisp_fe_config_map[i];
+
+ if (cfg->dirty_flags & p->dirty_flags ||
+ cfg->dirty_flags_extra & p->dirty_flags_extra)
+ pisp_fe_config_write(fe, cfg, p->offset, p->size);
+ }
+
+ /* This final non-relaxed write serves as a memory barrier */
+ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE);
+}
+
+void pisp_fe_start(struct pisp_fe_device *fe)
+{
+ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET);
+ pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
+ pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF |
+ FE_INT_LINES0 | FE_INT_LINES1);
+ fe->inframe_count = 0;
+}
+
+void pisp_fe_stop(struct pisp_fe_device *fe)
+{
+ pisp_fe_reg_write(fe, FE_INT_EN, 0);
+ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT);
+ usleep_range(1000, 2000);
+ WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
+ pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
+}
+
+static int pisp_fe_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
+ *fmt = cfe_default_format;
+ fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_CONFIG_PAD);
+ fmt->code = MEDIA_BUS_FMT_FIXED;
+ fmt->width = sizeof(struct pisp_fe_config);
+ fmt->height = 1;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD);
+ *fmt = cfe_default_format;
+ fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD);
+ *fmt = cfe_default_format;
+ fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_STATS_PAD);
+ fmt->code = MEDIA_BUS_FMT_FIXED;
+ fmt->width = sizeof(struct pisp_statistics);
+ fmt->height = 1;
+
+ return 0;
+}
+
+static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+ const struct cfe_fmt *cfe_fmt;
+
+ /* TODO: format propagation to source pads */
+ /* TODO: format validation */
+
+ switch (format->pad) {
+ case FE_STREAM_PAD:
+ cfe_fmt = find_format_by_code(format->format.code);
+ if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
+ cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
+
+ format->format.code = cfe_fmt->code;
+ format->format.field = V4L2_FIELD_NONE;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
+ *fmt = format->format;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD);
+ *fmt = format->format;
+
+ fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD);
+ *fmt = format->format;
+
+ return 0;
+
+ case FE_OUTPUT0_PAD:
+ case FE_OUTPUT1_PAD: {
+ /*
+ * TODO: we should allow scaling and cropping by allowing the
+ * user to set the size here.
+ */
+ struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
+ u32 sink_code;
+ u32 code;
+
+ cfe_fmt = find_format_by_code(format->format.code);
+ if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
+ cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
+
+ format->format.code = cfe_fmt->code;
+
+ sink_fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
+ if (!sink_fmt)
+ return -EINVAL;
+
+ source_fmt = v4l2_subdev_state_get_format(state, format->pad);
+ if (!source_fmt)
+ return -EINVAL;
+
+ sink_code = sink_fmt->code;
+ code = format->format.code;
+
+ /*
+ * If the source code from the user does not match the code in
+ * the sink pad, check that the source code matches the
+ * compressed version of the sink code.
+ */
+
+ if (code != sink_code &&
+ code == cfe_find_compressed_code(sink_code))
+ source_fmt->code = code;
+
+ return 0;
+ }
+
+ case FE_CONFIG_PAD:
+ case FE_STATS_PAD:
+ default:
+ return v4l2_subdev_get_fmt(sd, state, format);
+ }
+}
+
+static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = pisp_fe_pad_set_fmt,
+ .link_validate = v4l2_subdev_link_validate_default,
+};
+
+static int pisp_fe_link_validate(struct media_link *link)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->sink->entity);
+ struct pisp_fe_device *fe = container_of(sd, struct pisp_fe_device, sd);
+
+ pisp_fe_dbg(fe, "%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name, link->sink->index);
+
+ if (link->sink->index == FE_STREAM_PAD)
+ return v4l2_subdev_link_validate(link);
+
+ if (link->sink->index == FE_CONFIG_PAD)
+ return 0;
+
+ return -EINVAL;
+}
+
+static const struct media_entity_operations pisp_fe_entity_ops = {
+ .link_validate = pisp_fe_link_validate,
+};
+
+static const struct v4l2_subdev_ops pisp_fe_subdev_ops = {
+ .pad = &pisp_fe_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = {
+ .init_state = pisp_fe_init_state,
+};
+
+int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs)
+{
+ int ret;
+
+ debugfs_create_file("fe_regs", 0440, debugfs, fe, &pisp_fe_regs_fops);
+
+ fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION);
+ pisp_fe_info(fe, "PiSP FE HW v%u.%u\n",
+ (fe->hw_revision >> 24) & 0xff,
+ (fe->hw_revision >> 20) & 0x0f);
+
+ fe->pad[FE_STREAM_PAD].flags =
+ MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK;
+ fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE;
+ fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE;
+ fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad),
+ fe->pad);
+ if (ret)
+ return ret;
+
+ /* Initialize subdev */
+ v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops);
+ fe->sd.internal_ops = &pisp_fe_internal_ops;
+ fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
+ fe->sd.entity.ops = &pisp_fe_entity_ops;
+ fe->sd.entity.name = "pisp-fe";
+ fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ fe->sd.owner = THIS_MODULE;
+ snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe");
+
+ ret = v4l2_subdev_init_finalize(&fe->sd);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd);
+ if (ret) {
+ pisp_fe_err(fe, "Failed register pisp fe subdev (%d)\n", ret);
+ goto err_subdev_cleanup;
+ }
+
+ /* Must be in IDLE state (STATUS == 0) here. */
+ WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(&fe->sd);
+err_entity_cleanup:
+ media_entity_cleanup(&fe->sd.entity);
+
+ return ret;
+}
+
+void pisp_fe_uninit(struct pisp_fe_device *fe)
+{
+ v4l2_device_unregister_subdev(&fe->sd);
+ v4l2_subdev_cleanup(&fe->sd);
+ media_entity_cleanup(&fe->sd.entity);
+}
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h
new file mode 100644
index 000000000000..54d506e19cf2
--- /dev/null
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * PiSP Front End Driver
+ *
+ * Copyright (c) 2021-2024 Raspberry Pi Ltd.
+ */
+#ifndef _PISP_FE_H_
+#define _PISP_FE_H_
+
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include <linux/media/raspberrypi/pisp_fe_config.h>
+
+enum pisp_fe_pads {
+ FE_STREAM_PAD,
+ FE_CONFIG_PAD,
+ FE_OUTPUT0_PAD,
+ FE_OUTPUT1_PAD,
+ FE_STATS_PAD,
+ FE_NUM_PADS
+};
+
+struct pisp_fe_device {
+ /* Parent V4l2 device */
+ struct v4l2_device *v4l2_dev;
+ void __iomem *base;
+ u32 hw_revision;
+
+ u16 inframe_count;
+ struct media_pad pad[FE_NUM_PADS];
+ struct v4l2_subdev sd;
+};
+
+void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof);
+int pisp_fe_validate_config(struct pisp_fe_device *fe,
+ struct pisp_fe_config *cfg,
+ struct v4l2_format const *f0,
+ struct v4l2_format const *f1);
+void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
+ struct pisp_fe_config *cfg);
+void pisp_fe_start(struct pisp_fe_device *fe);
+void pisp_fe_stop(struct pisp_fe_device *fe);
+int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs);
+void pisp_fe_uninit(struct pisp_fe_device *fe);
+
+#endif
diff --git a/drivers/media/platform/renesas/Kconfig b/drivers/media/platform/renesas/Kconfig
index c7fc718a30a5..27a54fa79083 100644
--- a/drivers/media/platform/renesas/Kconfig
+++ b/drivers/media/platform/renesas/Kconfig
@@ -30,23 +30,6 @@ config VIDEO_RCAR_CSI2
To compile this driver as a module, choose M here: the
module will be called rcar-csi2.
-config VIDEO_RCAR_ISP
- tristate "R-Car Image Signal Processor (ISP)"
- depends on V4L_PLATFORM_DRIVERS
- depends on VIDEO_DEV && OF
- depends on ARCH_RENESAS || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select RESET_CONTROLLER
- select V4L2_FWNODE
- help
- Support for Renesas R-Car Image Signal Processor (ISP).
- Enable this to support the Renesas R-Car Image Signal
- Processor (ISP).
-
- To compile this driver as a module, choose M here: the
- module will be called rcar-isp.
-
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on V4L_PLATFORM_DRIVERS
@@ -56,6 +39,7 @@ config VIDEO_SH_VOU
help
Support for the Video Output Unit (VOU) on SuperH SoCs.
+source "drivers/media/platform/renesas/rcar-isp/Kconfig"
source "drivers/media/platform/renesas/rcar-vin/Kconfig"
source "drivers/media/platform/renesas/rzg2l-cru/Kconfig"
diff --git a/drivers/media/platform/renesas/Makefile b/drivers/media/platform/renesas/Makefile
index 50774a20330c..1127259c09d6 100644
--- a/drivers/media/platform/renesas/Makefile
+++ b/drivers/media/platform/renesas/Makefile
@@ -3,13 +3,13 @@
# Makefile for the Renesas capture/playback device drivers.
#
+obj-y += rcar-isp/
obj-y += rcar-vin/
obj-y += rzg2l-cru/
obj-y += vsp1/
obj-$(CONFIG_VIDEO_RCAR_CSI2) += rcar-csi2.o
obj-$(CONFIG_VIDEO_RCAR_DRIF) += rcar_drif.o
-obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
obj-$(CONFIG_VIDEO_RENESAS_CEU) += renesas-ceu.o
obj-$(CONFIG_VIDEO_RENESAS_FCP) += rcar-fcp.o
obj-$(CONFIG_VIDEO_RENESAS_FDP1) += rcar_fdp1.o
diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index 582d5e35db0e..9979de4f6ef1 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-csi2.c
@@ -8,6 +8,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
@@ -15,6 +16,7 @@
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <linux/sys_soc.h>
+#include <linux/units.h>
#include <media/mipi-csi2.h>
#include <media/v4l2-ctrls.h>
@@ -135,13 +137,23 @@ struct rcar_csi2;
/* V4H BASE registers */
#define V4H_N_LANES_REG 0x0004
#define V4H_CSI2_RESETN_REG 0x0008
+
#define V4H_PHY_MODE_REG 0x001c
+#define V4H_PHY_MODE_DPHY 0
+#define V4H_PHY_MODE_CPHY 1
+
#define V4H_PHY_SHUTDOWNZ_REG 0x0040
#define V4H_DPHY_RSTZ_REG 0x0044
#define V4H_FLDC_REG 0x0804
#define V4H_FLDD_REG 0x0808
#define V4H_IDIC_REG 0x0810
+
#define V4H_PHY_EN_REG 0x2000
+#define V4H_PHY_EN_ENABLE_3 BIT(7)
+#define V4H_PHY_EN_ENABLE_2 BIT(6)
+#define V4H_PHY_EN_ENABLE_1 BIT(5)
+#define V4H_PHY_EN_ENABLE_0 BIT(4)
+#define V4H_PHY_EN_ENABLE_CLK BIT(0)
#define V4H_ST_PHYST_REG 0x2814
#define V4H_ST_PHYST_ST_PHY_READY BIT(31)
@@ -173,17 +185,19 @@ struct rcar_csi2;
#define V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(n) (0x23840 + ((n) * 2)) /* n = 0 - 11 */
#define V4H_CORE_DIG_RW_COMMON_REG(n) (0x23880 + ((n) * 2)) /* n = 0 - 15 */
#define V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(n) (0x239e0 + ((n) * 2)) /* n = 0 - 3 */
-#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400
#define V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG 0x2a60c
/* V4H C-PHY */
#define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */
#define V4H_CORE_DIG_RW_TRIO1_REG(n) (0x22500 + ((n) * 2)) /* n = 0 - 3 */
#define V4H_CORE_DIG_RW_TRIO2_REG(n) (0x22900 + ((n) * 2)) /* n = 0 - 3 */
+#define V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG 0x2a000
#define V4H_CORE_DIG_CLANE_0_RW_LP_0_REG 0x2a080
#define V4H_CORE_DIG_CLANE_0_RW_HS_RX_REG(n) (0x2a100 + ((n) * 2)) /* n = 0 - 6 */
+#define V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG 0x2a400
#define V4H_CORE_DIG_CLANE_1_RW_LP_0_REG 0x2a480
#define V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(n) (0x2a500 + ((n) * 2)) /* n = 0 - 6 */
+#define V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG 0x2a800
#define V4H_CORE_DIG_CLANE_2_RW_LP_0_REG 0x2a880
#define V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(n) (0x2a900 + ((n) * 2)) /* n = 0 - 6 */
@@ -237,17 +251,37 @@ static const struct rcsi2_cphy_setting cphy_setting_table_r8a779g0[] = {
{ /* sentinel */ },
};
+/* V4M registers */
+#define V4M_OVR1_REG 0x0848
+#define V4M_OVR1_FORCERXMODE_3 BIT(12)
+#define V4M_OVR1_FORCERXMODE_2 BIT(11)
+#define V4M_OVR1_FORCERXMODE_1 BIT(10)
+#define V4M_OVR1_FORCERXMODE_0 BIT(9)
+
+#define V4M_FRXM_REG 0x2004
+#define V4M_FRXM_FORCERXMODE_3 BIT(3)
+#define V4M_FRXM_FORCERXMODE_2 BIT(2)
+#define V4M_FRXM_FORCERXMODE_1 BIT(1)
+#define V4M_FRXM_FORCERXMODE_0 BIT(0)
+
+#define V4M_PHYPLL_REG 0x02050
+#define V4M_CSI0CLKFCPR_REG 0x02054
+#define V4M_PHTW_REG 0x02060
+#define V4M_PHTR_REG 0x02064
+#define V4M_PHTC_REG 0x02068
+
struct phtw_value {
- u16 data;
- u16 code;
+ u8 data;
+ u8 code;
};
-struct rcsi2_mbps_reg {
+struct rcsi2_mbps_info {
u16 mbps;
- u16 reg;
+ u8 reg;
+ u16 osc_freq; /* V4M */
};
-static const struct rcsi2_mbps_reg phtw_mbps_v3u[] = {
+static const struct rcsi2_mbps_info phtw_mbps_v3u[] = {
{ .mbps = 1500, .reg = 0xcc },
{ .mbps = 1550, .reg = 0x1d },
{ .mbps = 1600, .reg = 0x27 },
@@ -272,7 +306,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_v3u[] = {
{ /* sentinel */ },
};
-static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = {
+static const struct rcsi2_mbps_info phtw_mbps_h3_v3h_m3n[] = {
{ .mbps = 80, .reg = 0x86 },
{ .mbps = 90, .reg = 0x86 },
{ .mbps = 100, .reg = 0x87 },
@@ -292,7 +326,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_h3_v3h_m3n[] = {
{ /* sentinel */ },
};
-static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = {
+static const struct rcsi2_mbps_info phtw_mbps_v3m_e3[] = {
{ .mbps = 80, .reg = 0x00 },
{ .mbps = 90, .reg = 0x20 },
{ .mbps = 100, .reg = 0x40 },
@@ -336,7 +370,7 @@ static const struct rcsi2_mbps_reg phtw_mbps_v3m_e3[] = {
#define PHYPLL_REG 0x68
#define PHYPLL_HSFREQRANGE(n) ((n) << 16)
-static const struct rcsi2_mbps_reg hsfreqrange_v3u[] = {
+static const struct rcsi2_mbps_info hsfreqrange_v3u[] = {
{ .mbps = 80, .reg = 0x00 },
{ .mbps = 90, .reg = 0x10 },
{ .mbps = 100, .reg = 0x20 },
@@ -402,7 +436,7 @@ static const struct rcsi2_mbps_reg hsfreqrange_v3u[] = {
{ /* sentinel */ },
};
-static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = {
+static const struct rcsi2_mbps_info hsfreqrange_h3_v3h_m3n[] = {
{ .mbps = 80, .reg = 0x00 },
{ .mbps = 90, .reg = 0x10 },
{ .mbps = 100, .reg = 0x20 },
@@ -449,7 +483,7 @@ static const struct rcsi2_mbps_reg hsfreqrange_h3_v3h_m3n[] = {
{ /* sentinel */ },
};
-static const struct rcsi2_mbps_reg hsfreqrange_m3w[] = {
+static const struct rcsi2_mbps_info hsfreqrange_m3w[] = {
{ .mbps = 80, .reg = 0x00 },
{ .mbps = 90, .reg = 0x10 },
{ .mbps = 100, .reg = 0x20 },
@@ -496,6 +530,73 @@ static const struct rcsi2_mbps_reg hsfreqrange_m3w[] = {
{ /* sentinel */ },
};
+static const struct rcsi2_mbps_info hsfreqrange_v4m[] = {
+ { .mbps = 80, .reg = 0x00, .osc_freq = 0x01a9 },
+ { .mbps = 90, .reg = 0x10, .osc_freq = 0x01a9 },
+ { .mbps = 100, .reg = 0x20, .osc_freq = 0x01a9 },
+ { .mbps = 110, .reg = 0x30, .osc_freq = 0x01a9 },
+ { .mbps = 120, .reg = 0x01, .osc_freq = 0x01a9 },
+ { .mbps = 130, .reg = 0x11, .osc_freq = 0x01a9 },
+ { .mbps = 140, .reg = 0x21, .osc_freq = 0x01a9 },
+ { .mbps = 150, .reg = 0x31, .osc_freq = 0x01a9 },
+ { .mbps = 160, .reg = 0x02, .osc_freq = 0x01a9 },
+ { .mbps = 170, .reg = 0x12, .osc_freq = 0x01a9 },
+ { .mbps = 180, .reg = 0x22, .osc_freq = 0x01a9 },
+ { .mbps = 190, .reg = 0x32, .osc_freq = 0x01a9 },
+ { .mbps = 205, .reg = 0x03, .osc_freq = 0x01a9 },
+ { .mbps = 220, .reg = 0x13, .osc_freq = 0x01a9 },
+ { .mbps = 235, .reg = 0x23, .osc_freq = 0x01a9 },
+ { .mbps = 250, .reg = 0x33, .osc_freq = 0x01a9 },
+ { .mbps = 275, .reg = 0x04, .osc_freq = 0x01a9 },
+ { .mbps = 300, .reg = 0x14, .osc_freq = 0x01a9 },
+ { .mbps = 325, .reg = 0x25, .osc_freq = 0x01a9 },
+ { .mbps = 350, .reg = 0x35, .osc_freq = 0x01a9 },
+ { .mbps = 400, .reg = 0x05, .osc_freq = 0x01a9 },
+ { .mbps = 450, .reg = 0x16, .osc_freq = 0x01a9 },
+ { .mbps = 500, .reg = 0x26, .osc_freq = 0x01a9 },
+ { .mbps = 550, .reg = 0x37, .osc_freq = 0x01a9 },
+ { .mbps = 600, .reg = 0x07, .osc_freq = 0x01a9 },
+ { .mbps = 650, .reg = 0x18, .osc_freq = 0x01a9 },
+ { .mbps = 700, .reg = 0x28, .osc_freq = 0x01a9 },
+ { .mbps = 750, .reg = 0x39, .osc_freq = 0x01a9 },
+ { .mbps = 800, .reg = 0x09, .osc_freq = 0x01a9 },
+ { .mbps = 850, .reg = 0x19, .osc_freq = 0x01a9 },
+ { .mbps = 900, .reg = 0x29, .osc_freq = 0x01a9 },
+ { .mbps = 950, .reg = 0x3a, .osc_freq = 0x01a9 },
+ { .mbps = 1000, .reg = 0x0a, .osc_freq = 0x01a9 },
+ { .mbps = 1050, .reg = 0x1a, .osc_freq = 0x01a9 },
+ { .mbps = 1100, .reg = 0x2a, .osc_freq = 0x01a9 },
+ { .mbps = 1150, .reg = 0x3b, .osc_freq = 0x01a9 },
+ { .mbps = 1200, .reg = 0x0b, .osc_freq = 0x01a9 },
+ { .mbps = 1250, .reg = 0x1b, .osc_freq = 0x01a9 },
+ { .mbps = 1300, .reg = 0x2b, .osc_freq = 0x01a9 },
+ { .mbps = 1350, .reg = 0x3c, .osc_freq = 0x01a9 },
+ { .mbps = 1400, .reg = 0x0c, .osc_freq = 0x01a9 },
+ { .mbps = 1450, .reg = 0x1c, .osc_freq = 0x01a9 },
+ { .mbps = 1500, .reg = 0x2c, .osc_freq = 0x01a9 },
+ { .mbps = 1550, .reg = 0x3d, .osc_freq = 0x0108 },
+ { .mbps = 1600, .reg = 0x0d, .osc_freq = 0x0110 },
+ { .mbps = 1650, .reg = 0x1d, .osc_freq = 0x0119 },
+ { .mbps = 1700, .reg = 0x2e, .osc_freq = 0x0121 },
+ { .mbps = 1750, .reg = 0x3e, .osc_freq = 0x012a },
+ { .mbps = 1800, .reg = 0x0e, .osc_freq = 0x0132 },
+ { .mbps = 1850, .reg = 0x1e, .osc_freq = 0x013b },
+ { .mbps = 1900, .reg = 0x2f, .osc_freq = 0x0143 },
+ { .mbps = 1950, .reg = 0x3f, .osc_freq = 0x014c },
+ { .mbps = 2000, .reg = 0x0f, .osc_freq = 0x0154 },
+ { .mbps = 2050, .reg = 0x40, .osc_freq = 0x015d },
+ { .mbps = 2100, .reg = 0x41, .osc_freq = 0x0165 },
+ { .mbps = 2150, .reg = 0x42, .osc_freq = 0x016e },
+ { .mbps = 2200, .reg = 0x43, .osc_freq = 0x0176 },
+ { .mbps = 2250, .reg = 0x44, .osc_freq = 0x017f },
+ { .mbps = 2300, .reg = 0x45, .osc_freq = 0x0187 },
+ { .mbps = 2350, .reg = 0x46, .osc_freq = 0x0190 },
+ { .mbps = 2400, .reg = 0x47, .osc_freq = 0x0198 },
+ { .mbps = 2450, .reg = 0x48, .osc_freq = 0x01a1 },
+ { .mbps = 2500, .reg = 0x49, .osc_freq = 0x01a9 },
+ { /* sentinel */ },
+};
+
/* PHY ESC Error Monitor */
#define PHEERM_REG 0x74
@@ -538,6 +639,10 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = {
.datatype = MIPI_CSI2_DT_YUV422_8B,
.bpp = 20,
}, {
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 8,
+ }, {
.code = MEDIA_BUS_FMT_Y10_1X10,
.datatype = MIPI_CSI2_DT_RAW10,
.bpp = 10,
@@ -558,9 +663,37 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = {
.datatype = MIPI_CSI2_DT_RAW8,
.bpp = 8,
}, {
- .code = MEDIA_BUS_FMT_Y8_1X8,
- .datatype = MIPI_CSI2_DT_RAW8,
- .bpp = 8,
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .bpp = 10,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .bpp = 12,
},
};
@@ -575,6 +708,21 @@ static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code)
return NULL;
}
+struct rcsi2_cphy_line_order {
+ enum v4l2_mbus_csi2_cphy_line_orders_type order;
+ u16 cfg;
+ u16 ctrl29;
+};
+
+static const struct rcsi2_cphy_line_order rcsi2_cphy_line_orders[] = {
+ { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ABC, .cfg = 0x0, .ctrl29 = 0x0 },
+ { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_ACB, .cfg = 0xa, .ctrl29 = 0x1 },
+ { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BAC, .cfg = 0xc, .ctrl29 = 0x1 },
+ { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_BCA, .cfg = 0x5, .ctrl29 = 0x0 },
+ { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CAB, .cfg = 0x3, .ctrl29 = 0x0 },
+ { .order = V4L2_MBUS_CSI2_CPHY_LINE_ORDER_CBA, .cfg = 0x9, .ctrl29 = 0x1 }
+};
+
enum rcar_csi2_pads {
RCAR_CSI2_SINK,
RCAR_CSI2_SOURCE_VC0,
@@ -584,12 +732,19 @@ enum rcar_csi2_pads {
NR_OF_RCAR_CSI2_PAD,
};
+struct rcsi2_register_layout {
+ unsigned int phtw;
+ unsigned int phypll;
+};
+
struct rcar_csi2_info {
+ const struct rcsi2_register_layout *regs;
int (*init_phtw)(struct rcar_csi2 *priv, unsigned int mbps);
int (*phy_post_init)(struct rcar_csi2 *priv);
- int (*start_receiver)(struct rcar_csi2 *priv);
+ int (*start_receiver)(struct rcar_csi2 *priv,
+ struct v4l2_subdev_state *state);
void (*enter_standby)(struct rcar_csi2 *priv);
- const struct rcsi2_mbps_reg *hsfreqrange;
+ const struct rcsi2_mbps_info *hsfreqrange;
unsigned int csi0clkfreqrange;
unsigned int num_channels;
bool clear_ulps;
@@ -613,13 +768,12 @@ struct rcar_csi2 {
int channel_vc[4];
- struct mutex lock; /* Protects mf and stream_count. */
- struct v4l2_mbus_framefmt mf;
int stream_count;
bool cphy;
unsigned short lanes;
unsigned char lane_swap[4];
+ enum v4l2_mbus_csi2_cphy_line_orders_type line_orders[3];
};
static inline struct rcar_csi2 *sd_to_csi2(struct v4l2_subdev *sd)
@@ -632,6 +786,16 @@ static inline struct rcar_csi2 *notifier_to_csi2(struct v4l2_async_notifier *n)
return container_of(n, struct rcar_csi2, notifier);
}
+static unsigned int rcsi2_num_pads(const struct rcar_csi2 *priv)
+{
+ /* Used together with R-Car ISP: one sink and one source pad. */
+ if (priv->info->use_isp)
+ return 2;
+
+ /* Used together with R-Car VIN: one sink and four source pads. */
+ return 5;
+}
+
static u32 rcsi2_read(struct rcar_csi2 *priv, unsigned int reg)
{
return ioread32(priv->base + reg);
@@ -642,11 +806,88 @@ static void rcsi2_write(struct rcar_csi2 *priv, unsigned int reg, u32 data)
iowrite32(data, priv->base + reg);
}
+static u16 rcsi2_read16(struct rcar_csi2 *priv, unsigned int reg)
+{
+ return ioread16(priv->base + reg);
+}
+
static void rcsi2_write16(struct rcar_csi2 *priv, unsigned int reg, u16 data)
{
iowrite16(data, priv->base + reg);
}
+static void rcsi2_modify16(struct rcar_csi2 *priv, unsigned int reg, u16 data, u16 mask)
+{
+ u16 val;
+
+ val = rcsi2_read16(priv, reg) & ~mask;
+ rcsi2_write16(priv, reg, val | data);
+}
+
+static int rcsi2_phtw_write(struct rcar_csi2 *priv, u8 data, u8 code)
+{
+ unsigned int timeout;
+
+ rcsi2_write(priv, priv->info->regs->phtw,
+ PHTW_DWEN | PHTW_TESTDIN_DATA(data) |
+ PHTW_CWEN | PHTW_TESTDIN_CODE(code));
+
+ /* Wait for DWEN and CWEN to be cleared by hardware. */
+ for (timeout = 0; timeout <= 20; timeout++) {
+ if (!(rcsi2_read(priv, priv->info->regs->phtw) & (PHTW_DWEN | PHTW_CWEN)))
+ return 0;
+
+ usleep_range(1000, 2000);
+ }
+
+ dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n");
+
+ return -ETIMEDOUT;
+}
+
+static int rcsi2_phtw_write_array(struct rcar_csi2 *priv,
+ const struct phtw_value *values,
+ unsigned int size)
+{
+ int ret;
+
+ for (unsigned int i = 0; i < size; i++) {
+ ret = rcsi2_phtw_write(priv, values[i].data, values[i].code);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct rcsi2_mbps_info *
+rcsi2_mbps_to_info(struct rcar_csi2 *priv,
+ const struct rcsi2_mbps_info *infotable, unsigned int mbps)
+{
+ const struct rcsi2_mbps_info *info;
+ const struct rcsi2_mbps_info *prev = NULL;
+
+ if (mbps < infotable->mbps)
+ dev_warn(priv->dev, "%u Mbps less than min PHY speed %u Mbps",
+ mbps, infotable->mbps);
+
+ for (info = infotable; info->mbps != 0; info++) {
+ if (info->mbps >= mbps)
+ break;
+ prev = info;
+ }
+
+ if (!info->mbps) {
+ dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps);
+ return NULL;
+ }
+
+ if (prev && ((mbps - prev->mbps) <= (info->mbps - mbps)))
+ info = prev;
+
+ return info;
+}
+
static void rcsi2_enter_standby_gen3(struct rcar_csi2 *priv)
{
rcsi2_write(priv, PHYCNT_REG, 0);
@@ -699,29 +940,13 @@ static int rcsi2_wait_phy_start(struct rcar_csi2 *priv,
static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps)
{
- const struct rcsi2_mbps_reg *hsfreq;
- const struct rcsi2_mbps_reg *hsfreq_prev = NULL;
-
- if (mbps < priv->info->hsfreqrange->mbps)
- dev_warn(priv->dev, "%u Mbps less than min PHY speed %u Mbps",
- mbps, priv->info->hsfreqrange->mbps);
+ const struct rcsi2_mbps_info *info;
- for (hsfreq = priv->info->hsfreqrange; hsfreq->mbps != 0; hsfreq++) {
- if (hsfreq->mbps >= mbps)
- break;
- hsfreq_prev = hsfreq;
- }
-
- if (!hsfreq->mbps) {
- dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps);
+ info = rcsi2_mbps_to_info(priv, priv->info->hsfreqrange, mbps);
+ if (!info)
return -ERANGE;
- }
- if (hsfreq_prev &&
- ((mbps - hsfreq_prev->mbps) <= (hsfreq->mbps - mbps)))
- hsfreq = hsfreq_prev;
-
- rcsi2_write(priv, PHYPLL_REG, PHYPLL_HSFREQRANGE(hsfreq->reg));
+ rcsi2_write(priv, priv->info->regs->phypll, PHYPLL_HSFREQRANGE(info->reg));
return 0;
}
@@ -730,7 +955,7 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
unsigned int lanes)
{
struct v4l2_subdev *source;
- struct v4l2_ctrl *ctrl;
+ s64 freq;
u64 mbps;
if (!priv->remote)
@@ -738,21 +963,17 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
source = priv->remote;
- /* Read the pixel rate control from remote. */
- ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE);
- if (!ctrl) {
- dev_err(priv->dev, "no pixel rate control in subdev %s\n",
- source->name);
- return -EINVAL;
+ freq = v4l2_get_link_freq(source->ctrl_handler, bpp, 2 * lanes);
+ if (freq < 0) {
+ int ret = (int)freq;
+
+ dev_err(priv->dev, "failed to get link freq for %s: %d\n",
+ source->name, ret);
+
+ return ret;
}
- /*
- * Calculate the phypll in mbps.
- * link_freq = (pixel_rate * bits_per_sample) / (2 * nr_of_lanes)
- * bps = link_freq * 2
- */
- mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * bpp;
- do_div(mbps, lanes * 1000000);
+ mbps = div_u64(freq * 2, MEGA);
/* Adjust for C-PHY, divide by 2.8. */
if (priv->cphy)
@@ -808,20 +1029,25 @@ static int rcsi2_get_active_lanes(struct rcar_csi2 *priv,
return 0;
}
-static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv)
+static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
+ struct v4l2_subdev_state *state)
{
const struct rcar_csi2_format *format;
u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0;
+ const struct v4l2_mbus_framefmt *fmt;
unsigned int lanes;
unsigned int i;
int mbps, ret;
+ /* Use the format on the sink pad to compute the receiver config. */
+ fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
+
dev_dbg(priv->dev, "Input size (%ux%u%c)\n",
- priv->mf.width, priv->mf.height,
- priv->mf.field == V4L2_FIELD_NONE ? 'p' : 'i');
+ fmt->width, fmt->height,
+ fmt->field == V4L2_FIELD_NONE ? 'p' : 'i');
/* Code is validated in set_fmt. */
- format = rcsi2_code_to_fmt(priv->mf.code);
+ format = rcsi2_code_to_fmt(fmt->code);
if (!format)
return -EINVAL;
@@ -849,16 +1075,10 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv)
vcdt2 |= vcdt_part << ((i % 2) * 16);
}
- if (priv->mf.field == V4L2_FIELD_ALTERNATE) {
+ if (fmt->field == V4L2_FIELD_ALTERNATE)
fld = FLD_DET_SEL(1) | FLD_FLD_EN4 | FLD_FLD_EN3 | FLD_FLD_EN2
| FLD_FLD_EN;
- if (priv->mf.height == 240)
- fld |= FLD_FLD_NUM(0);
- else
- fld |= FLD_FLD_NUM(1);
- }
-
/*
* Get the number of active data lanes inspecting the remote mbus
* configuration.
@@ -947,6 +1167,26 @@ static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv)
return 0;
}
+static void rsci2_set_line_order(struct rcar_csi2 *priv,
+ enum v4l2_mbus_csi2_cphy_line_orders_type order,
+ unsigned int cfgreg, unsigned int ctrlreg)
+{
+ const struct rcsi2_cphy_line_order *info = NULL;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(rcsi2_cphy_line_orders); i++) {
+ if (rcsi2_cphy_line_orders[i].order == order) {
+ info = &rcsi2_cphy_line_orders[i];
+ break;
+ }
+ }
+
+ if (!info)
+ return;
+
+ rcsi2_modify16(priv, cfgreg, info->cfg, 0x000f);
+ rcsi2_modify16(priv, ctrlreg, info->ctrl29, 0x0100);
+}
+
static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match)
{
unsigned int timeout;
@@ -1024,12 +1264,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(1), conf->trio1);
rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO2_REG(1), conf->trio1);
- /*
- * Configure pin-swap.
- * TODO: This registers is not documented yet, the values should depend
- * on the 'clock-lanes' and 'data-lanes' devicetree properties.
- */
- rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG, 0xf5);
+ /* Configure data line order. */
+ rsci2_set_line_order(priv, priv->line_orders[0],
+ V4H_CORE_DIG_CLANE_0_RW_CFG_0_REG,
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9));
+ rsci2_set_line_order(priv, priv->line_orders[1],
+ V4H_CORE_DIG_CLANE_1_RW_CFG_0_REG,
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(9));
+ rsci2_set_line_order(priv, priv->line_orders[2],
+ V4H_CORE_DIG_CLANE_2_RW_CFG_0_REG,
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(9));
+
+ /* TODO: This registers is not documented. */
rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000);
/* Leave Shutdown mode */
@@ -1049,15 +1295,18 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
return 0;
}
-static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
+static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
+ struct v4l2_subdev_state *state)
{
const struct rcar_csi2_format *format;
+ const struct v4l2_mbus_framefmt *fmt;
unsigned int lanes;
int msps;
int ret;
- /* Calculate parameters */
- format = rcsi2_code_to_fmt(priv->mf.code);
+ /* Use the format on the sink pad to compute the receiver config. */
+ fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
+ format = rcsi2_code_to_fmt(fmt->code);
if (!format)
return -EINVAL;
@@ -1075,11 +1324,11 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0);
/* PHY static setting */
- rcsi2_write(priv, V4H_PHY_EN_REG, BIT(0));
+ rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK);
rcsi2_write(priv, V4H_FLDC_REG, 0);
rcsi2_write(priv, V4H_FLDD_REG, 0);
rcsi2_write(priv, V4H_IDIC_REG, 0);
- rcsi2_write(priv, V4H_PHY_MODE_REG, BIT(0));
+ rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_CPHY);
rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1);
/* Reset CSI2 */
@@ -1114,7 +1363,201 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv)
return 0;
}
-static int rcsi2_start(struct rcar_csi2 *priv)
+static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int data_rate)
+{
+ unsigned int timeout;
+ int ret;
+
+ static const struct phtw_value step1[] = {
+ { .data = 0x00, .code = 0x00 },
+ { .data = 0x00, .code = 0x1e },
+ };
+
+ /* Shutdown and reset PHY. */
+ rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0));
+ rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0));
+
+ /* Start internal calibration (POR). */
+ ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1));
+ if (ret)
+ return ret;
+
+ /* Wait for POR to complete. */
+ for (timeout = 10; timeout > 0; timeout--) {
+ if ((rcsi2_read(priv, V4M_PHTR_REG) & 0xf0000) == 0x70000)
+ break;
+ usleep_range(1000, 2000);
+ }
+
+ if (!timeout) {
+ dev_err(priv->dev, "D-PHY calibration failed\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int rcsi2_set_osc_freq(struct rcar_csi2 *priv, unsigned int mbps)
+{
+ const struct rcsi2_mbps_info *info;
+ struct phtw_value steps[] = {
+ { .data = 0x00, .code = 0x00 },
+ { .code = 0xe2 }, /* Data filled in below. */
+ { .code = 0xe3 }, /* Data filled in below. */
+ { .data = 0x01, .code = 0xe4 },
+ };
+
+ info = rcsi2_mbps_to_info(priv, priv->info->hsfreqrange, mbps);
+ if (!info)
+ return -ERANGE;
+
+ /* Fill in data for command. */
+ steps[1].data = (info->osc_freq & 0x00ff) >> 0;
+ steps[2].data = (info->osc_freq & 0x0f00) >> 8;
+
+ return rcsi2_phtw_write_array(priv, steps, ARRAY_SIZE(steps));
+}
+
+static int rcsi2_init_common_v4m(struct rcar_csi2 *priv, unsigned int mbps)
+{
+ int ret;
+
+ static const struct phtw_value step1[] = {
+ { .data = 0x00, .code = 0x00 },
+ { .data = 0x3c, .code = 0x08 },
+ };
+
+ static const struct phtw_value step2[] = {
+ { .data = 0x00, .code = 0x00 },
+ { .data = 0x80, .code = 0xe0 },
+ { .data = 0x31, .code = 0xe1 },
+ { .data = 0x06, .code = 0x00 },
+ { .data = 0x11, .code = 0x11 },
+ { .data = 0x08, .code = 0x00 },
+ { .data = 0x11, .code = 0x11 },
+ { .data = 0x0a, .code = 0x00 },
+ { .data = 0x11, .code = 0x11 },
+ { .data = 0x0c, .code = 0x00 },
+ { .data = 0x11, .code = 0x11 },
+ { .data = 0x01, .code = 0x00 },
+ { .data = 0x31, .code = 0xaa },
+ { .data = 0x05, .code = 0x00 },
+ { .data = 0x05, .code = 0x09 },
+ { .data = 0x07, .code = 0x00 },
+ { .data = 0x05, .code = 0x09 },
+ { .data = 0x09, .code = 0x00 },
+ { .data = 0x05, .code = 0x09 },
+ { .data = 0x0b, .code = 0x00 },
+ { .data = 0x05, .code = 0x09 },
+ };
+
+ static const struct phtw_value step3[] = {
+ { .data = 0x01, .code = 0x00 },
+ { .data = 0x06, .code = 0xab },
+ };
+
+ if (priv->info->hsfreqrange) {
+ ret = rcsi2_set_phypll(priv, mbps);
+ if (ret)
+ return ret;
+
+ ret = rcsi2_set_osc_freq(priv, mbps);
+ if (ret)
+ return ret;
+ }
+
+ if (mbps <= 1500) {
+ ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1));
+ if (ret)
+ return ret;
+ }
+
+ if (priv->info->csi0clkfreqrange)
+ rcsi2_write(priv, V4M_CSI0CLKFCPR_REG,
+ CSI0CLKFREQRANGE(priv->info->csi0clkfreqrange));
+
+ rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK |
+ V4H_PHY_EN_ENABLE_0 | V4H_PHY_EN_ENABLE_1 |
+ V4H_PHY_EN_ENABLE_2 | V4H_PHY_EN_ENABLE_3);
+
+ if (mbps > 1500) {
+ ret = rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2));
+ if (ret)
+ return ret;
+ }
+
+ return rcsi2_phtw_write_array(priv, step3, ARRAY_SIZE(step3));
+}
+
+static int rcsi2_start_receiver_v4m(struct rcar_csi2 *priv,
+ struct v4l2_subdev_state *state)
+{
+ const struct rcar_csi2_format *format;
+ const struct v4l2_mbus_framefmt *fmt;
+ unsigned int lanes;
+ int mbps;
+ int ret;
+
+ /* Calculate parameters */
+ fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
+ format = rcsi2_code_to_fmt(fmt->code);
+ if (!format)
+ return -EINVAL;
+
+ ret = rcsi2_get_active_lanes(priv, &lanes);
+ if (ret)
+ return ret;
+
+ mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+ if (mbps < 0)
+ return mbps;
+
+ /* Reset LINK and PHY */
+ rcsi2_write(priv, V4H_CSI2_RESETN_REG, 0);
+ rcsi2_write(priv, V4H_DPHY_RSTZ_REG, 0);
+ rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, 0);
+ rcsi2_write(priv, V4M_PHTC_REG, PHTC_TESTCLR);
+
+ /* PHY static setting */
+ rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK);
+ rcsi2_write(priv, V4H_FLDC_REG, 0);
+ rcsi2_write(priv, V4H_FLDD_REG, 0);
+ rcsi2_write(priv, V4H_IDIC_REG, 0);
+ rcsi2_write(priv, V4H_PHY_MODE_REG, V4H_PHY_MODE_DPHY);
+ rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1);
+
+ rcsi2_write(priv, V4M_FRXM_REG,
+ V4M_FRXM_FORCERXMODE_0 | V4M_FRXM_FORCERXMODE_1 |
+ V4M_FRXM_FORCERXMODE_2 | V4M_FRXM_FORCERXMODE_3);
+ rcsi2_write(priv, V4M_OVR1_REG,
+ V4M_OVR1_FORCERXMODE_0 | V4M_OVR1_FORCERXMODE_1 |
+ V4M_OVR1_FORCERXMODE_2 | V4M_OVR1_FORCERXMODE_3);
+
+ /* Reset CSI2 */
+ rcsi2_write(priv, V4M_PHTC_REG, 0);
+ rcsi2_write(priv, V4H_CSI2_RESETN_REG, BIT(0));
+
+ /* Common settings */
+ ret = rcsi2_init_common_v4m(priv, mbps);
+ if (ret)
+ return ret;
+
+ /* D-PHY settings */
+ ret = rcsi2_d_phy_setting_v4m(priv, mbps);
+ if (ret)
+ return ret;
+
+ rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_STOPSTATE_0 |
+ V4H_ST_PHYST_ST_STOPSTATE_1 |
+ V4H_ST_PHYST_ST_STOPSTATE_2 |
+ V4H_ST_PHYST_ST_STOPSTATE_3);
+
+ rcsi2_write(priv, V4M_FRXM_REG, 0);
+
+ return 0;
+}
+
+static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state)
{
int ret;
@@ -1122,13 +1565,14 @@ static int rcsi2_start(struct rcar_csi2 *priv)
if (ret < 0)
return ret;
- ret = priv->info->start_receiver(priv);
+ ret = priv->info->start_receiver(priv, state);
if (ret) {
rcsi2_enter_standby(priv);
return ret;
}
- ret = v4l2_subdev_call(priv->remote, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(priv->remote, priv->remote_pad,
+ BIT_ULL(0));
if (ret) {
rcsi2_enter_standby(priv);
return ret;
@@ -1140,92 +1584,115 @@ static int rcsi2_start(struct rcar_csi2 *priv)
static void rcsi2_stop(struct rcar_csi2 *priv)
{
rcsi2_enter_standby(priv);
- v4l2_subdev_call(priv->remote, video, s_stream, 0);
+ v4l2_subdev_disable_streams(priv->remote, priv->remote_pad, BIT_ULL(0));
}
-static int rcsi2_s_stream(struct v4l2_subdev *sd, int enable)
+static int rcsi2_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 source_pad,
+ u64 source_streams_mask)
{
struct rcar_csi2 *priv = sd_to_csi2(sd);
int ret = 0;
- mutex_lock(&priv->lock);
+ if (source_streams_mask != 1)
+ return -EINVAL;
- if (!priv->remote) {
- ret = -ENODEV;
- goto out;
- }
+ if (!priv->remote)
+ return -ENODEV;
- if (enable && priv->stream_count == 0) {
- ret = rcsi2_start(priv);
+ if (priv->stream_count == 0) {
+ ret = rcsi2_start(priv, state);
if (ret)
- goto out;
- } else if (!enable && priv->stream_count == 1) {
- rcsi2_stop(priv);
+ return ret;
}
- priv->stream_count += enable ? 1 : -1;
-out:
- mutex_unlock(&priv->lock);
+ priv->stream_count += 1;
return ret;
}
-static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
+static int rcsi2_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 source_pad, u64 source_streams_mask)
{
struct rcar_csi2 *priv = sd_to_csi2(sd);
- struct v4l2_mbus_framefmt *framefmt;
+ int ret = 0;
- mutex_lock(&priv->lock);
+ if (source_streams_mask != 1)
+ return -EINVAL;
- if (!rcsi2_code_to_fmt(format->format.code))
- format->format.code = rcar_csi2_formats[0].code;
+ if (!priv->remote)
+ return -ENODEV;
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- priv->mf = format->format;
- } else {
- framefmt = v4l2_subdev_state_get_format(sd_state, 0);
- *framefmt = format->format;
- }
+ if (priv->stream_count == 1)
+ rcsi2_stop(priv);
- mutex_unlock(&priv->lock);
+ priv->stream_count -= 1;
- return 0;
+ return ret;
}
-static int rcsi2_get_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
+static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
struct rcar_csi2 *priv = sd_to_csi2(sd);
+ unsigned int num_pads = rcsi2_num_pads(priv);
- mutex_lock(&priv->lock);
+ if (format->pad > RCAR_CSI2_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- format->format = priv->mf;
- else
- format->format = *v4l2_subdev_state_get_format(sd_state, 0);
+ if (!rcsi2_code_to_fmt(format->format.code))
+ format->format.code = rcar_csi2_formats[0].code;
+
+ *v4l2_subdev_state_get_format(state, format->pad) = format->format;
- mutex_unlock(&priv->lock);
+ /* Propagate the format to the source pads. */
+ for (unsigned int i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++)
+ *v4l2_subdev_state_get_format(state, i) = format->format;
return 0;
}
-static const struct v4l2_subdev_video_ops rcar_csi2_video_ops = {
- .s_stream = rcsi2_s_stream,
-};
-
static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = {
+ .enable_streams = rcsi2_enable_streams,
+ .disable_streams = rcsi2_disable_streams,
+
.set_fmt = rcsi2_set_pad_format,
- .get_fmt = rcsi2_get_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
};
static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
- .video = &rcar_csi2_video_ops,
.pad = &rcar_csi2_pad_ops,
};
+static int rcsi2_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+ unsigned int num_pads = rcsi2_num_pads(priv);
+
+ static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = {
+ .width = 1920,
+ .height = 1080,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+ };
+
+ for (unsigned int i = RCAR_CSI2_SINK; i < num_pads; i++)
+ *v4l2_subdev_state_get_format(state, i) = rcar_csi2_default_fmt;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops rcar_csi2_internal_ops = {
+ .init_state = rcsi2_init_state,
+};
+
static irqreturn_t rcsi2_irq(int irq, void *data)
{
struct rcar_csi2 *priv = data;
@@ -1251,14 +1718,17 @@ static irqreturn_t rcsi2_irq(int irq, void *data)
static irqreturn_t rcsi2_irq_thread(int irq, void *data)
{
+ struct v4l2_subdev_state *state;
struct rcar_csi2 *priv = data;
- mutex_lock(&priv->lock);
+ state = v4l2_subdev_lock_and_get_active_state(&priv->subdev);
+
rcsi2_stop(priv);
usleep_range(1000, 2000);
- if (rcsi2_start(priv))
+ if (rcsi2_start(priv, state))
dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n");
- mutex_unlock(&priv->lock);
+
+ v4l2_subdev_unlock_state(state);
return IRQ_HANDLED;
}
@@ -1366,6 +1836,9 @@ static int rcsi2_parse_v4l2(struct rcar_csi2 *priv,
}
}
+ for (i = 0; i < ARRAY_SIZE(priv->line_orders); i++)
+ priv->line_orders[i] = vep->bus.mipi_csi2.line_orders[i];
+
return 0;
}
@@ -1425,64 +1898,16 @@ static int rcsi2_parse_dt(struct rcar_csi2 *priv)
* NOTE: Magic values are from the datasheet and lack documentation.
*/
-static int rcsi2_phtw_write(struct rcar_csi2 *priv, u16 data, u16 code)
-{
- unsigned int timeout;
-
- rcsi2_write(priv, PHTW_REG,
- PHTW_DWEN | PHTW_TESTDIN_DATA(data) |
- PHTW_CWEN | PHTW_TESTDIN_CODE(code));
-
- /* Wait for DWEN and CWEN to be cleared by hardware. */
- for (timeout = 0; timeout <= 20; timeout++) {
- if (!(rcsi2_read(priv, PHTW_REG) & (PHTW_DWEN | PHTW_CWEN)))
- return 0;
-
- usleep_range(1000, 2000);
- }
-
- dev_err(priv->dev, "Timeout waiting for PHTW_DWEN and/or PHTW_CWEN\n");
-
- return -ETIMEDOUT;
-}
-
-static int rcsi2_phtw_write_array(struct rcar_csi2 *priv,
- const struct phtw_value *values)
-{
- const struct phtw_value *value;
- int ret;
-
- for (value = values; value->data || value->code; value++) {
- ret = rcsi2_phtw_write(priv, value->data, value->code);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
static int rcsi2_phtw_write_mbps(struct rcar_csi2 *priv, unsigned int mbps,
- const struct rcsi2_mbps_reg *values, u16 code)
+ const struct rcsi2_mbps_info *values, u8 code)
{
- const struct rcsi2_mbps_reg *value;
- const struct rcsi2_mbps_reg *prev_value = NULL;
-
- for (value = values; value->mbps; value++) {
- if (value->mbps >= mbps)
- break;
- prev_value = value;
- }
+ const struct rcsi2_mbps_info *info;
- if (prev_value &&
- ((mbps - prev_value->mbps) <= (value->mbps - mbps)))
- value = prev_value;
-
- if (!value->mbps) {
- dev_err(priv->dev, "Unsupported PHY speed (%u Mbps)", mbps);
+ info = rcsi2_mbps_to_info(priv, values, mbps);
+ if (!info)
return -ERANGE;
- }
- return rcsi2_phtw_write(priv, value->reg, code);
+ return rcsi2_phtw_write(priv, info->reg, code);
}
static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv,
@@ -1494,7 +1919,6 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv,
{ .data = 0x11, .code = 0xe4 },
{ .data = 0x01, .code = 0xe5 },
{ .data = 0x10, .code = 0x04 },
- { /* sentinel */ },
};
static const struct phtw_value step2[] = {
@@ -1503,12 +1927,11 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv,
{ .data = 0x4b, .code = 0xac },
{ .data = 0x03, .code = 0x00 },
{ .data = 0x80, .code = 0x07 },
- { /* sentinel */ },
};
int ret;
- ret = rcsi2_phtw_write_array(priv, step1);
+ ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1));
if (ret)
return ret;
@@ -1523,7 +1946,7 @@ static int __rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv,
return ret;
}
- return rcsi2_phtw_write_array(priv, step2);
+ return rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2));
}
static int rcsi2_init_phtw_h3_v3h_m3n(struct rcar_csi2 *priv, unsigned int mbps)
@@ -1549,10 +1972,9 @@ static int rcsi2_phy_post_init_v3m_e3(struct rcar_csi2 *priv)
{ .data = 0xee, .code = 0x54 },
{ .data = 0xee, .code = 0x84 },
{ .data = 0xee, .code = 0x94 },
- { /* sentinel */ },
};
- return rcsi2_phtw_write_array(priv, step1);
+ return rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1));
}
static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv,
@@ -1561,20 +1983,17 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv,
/* In case of 1500Mbps or less */
static const struct phtw_value step1[] = {
{ .data = 0xcc, .code = 0xe2 },
- { /* sentinel */ },
};
static const struct phtw_value step2[] = {
{ .data = 0x01, .code = 0xe3 },
{ .data = 0x11, .code = 0xe4 },
{ .data = 0x01, .code = 0xe5 },
- { /* sentinel */ },
};
/* In case of 1500Mbps or less */
static const struct phtw_value step3[] = {
{ .data = 0x38, .code = 0x08 },
- { /* sentinel */ },
};
static const struct phtw_value step4[] = {
@@ -1582,29 +2001,28 @@ static int rcsi2_init_phtw_v3u(struct rcar_csi2 *priv,
{ .data = 0x4b, .code = 0xac },
{ .data = 0x03, .code = 0x00 },
{ .data = 0x80, .code = 0x07 },
- { /* sentinel */ },
};
int ret;
if (mbps != 0 && mbps <= 1500)
- ret = rcsi2_phtw_write_array(priv, step1);
+ ret = rcsi2_phtw_write_array(priv, step1, ARRAY_SIZE(step1));
else
ret = rcsi2_phtw_write_mbps(priv, mbps, phtw_mbps_v3u, 0xe2);
if (ret)
return ret;
- ret = rcsi2_phtw_write_array(priv, step2);
+ ret = rcsi2_phtw_write_array(priv, step2, ARRAY_SIZE(step2));
if (ret)
return ret;
if (mbps != 0 && mbps <= 1500) {
- ret = rcsi2_phtw_write_array(priv, step3);
+ ret = rcsi2_phtw_write_array(priv, step3, ARRAY_SIZE(step3));
if (ret)
return ret;
}
- ret = rcsi2_phtw_write_array(priv, step4);
+ ret = rcsi2_phtw_write_array(priv, step4, ARRAY_SIZE(step4));
if (ret)
return ret;
@@ -1688,7 +2106,13 @@ static int rcsi2_probe_resources(struct rcar_csi2 *priv,
return PTR_ERR_OR_ZERO(priv->rstc);
}
+static const struct rcsi2_register_layout rcsi2_registers_gen3 = {
+ .phtw = PHTW_REG,
+ .phypll = PHYPLL_REG,
+};
+
static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = {
+ .regs = &rcsi2_registers_gen3,
.init_phtw = rcsi2_init_phtw_h3_v3h_m3n,
.start_receiver = rcsi2_start_receiver_gen3,
.enter_standby = rcsi2_enter_standby_gen3,
@@ -1700,6 +2124,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = {
+ .regs = &rcsi2_registers_gen3,
.init_phtw = rcsi2_init_phtw_h3es2,
.start_receiver = rcsi2_start_receiver_gen3,
.enter_standby = rcsi2_enter_standby_gen3,
@@ -1711,6 +2136,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7795es2 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = {
+ .regs = &rcsi2_registers_gen3,
.start_receiver = rcsi2_start_receiver_gen3,
.enter_standby = rcsi2_enter_standby_gen3,
.hsfreqrange = hsfreqrange_m3w,
@@ -1719,6 +2145,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a7796 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a77961 = {
+ .regs = &rcsi2_registers_gen3,
.start_receiver = rcsi2_start_receiver_gen3,
.enter_standby = rcsi2_enter_standby_gen3,
.hsfreqrange = hsfreqrange_m3w,
@@ -1727,6 +2154,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77961 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = {
+ .regs = &rcsi2_registers_gen3,
.init_phtw = rcsi2_init_phtw_h3_v3h_m3n,
.start_receiver = rcsi2_start_receiver_gen3,
.enter_standby = rcsi2_enter_standby_gen3,
@@ -1738,6 +2166,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77965 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = {
+ .regs = &rcsi2_registers_gen3,
.init_phtw = rcsi2_init_phtw_v3m_e3,
.phy_post_init = rcsi2_phy_post_init_v3m_e3,
.start_receiver = rcsi2_start_receiver_gen3,
@@ -1747,6 +2176,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77970 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = {
+ .regs = &rcsi2_registers_gen3,
.init_phtw = rcsi2_init_phtw_h3_v3h_m3n,
.start_receiver = rcsi2_start_receiver_gen3,
.enter_standby = rcsi2_enter_standby_gen3,
@@ -1757,6 +2187,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77980 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = {
+ .regs = &rcsi2_registers_gen3,
.init_phtw = rcsi2_init_phtw_v3m_e3,
.phy_post_init = rcsi2_phy_post_init_v3m_e3,
.start_receiver = rcsi2_start_receiver_gen3,
@@ -1766,6 +2197,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a77990 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a779a0 = {
+ .regs = &rcsi2_registers_gen3,
.init_phtw = rcsi2_init_phtw_v3u,
.start_receiver = rcsi2_start_receiver_gen3,
.enter_standby = rcsi2_enter_standby_gen3,
@@ -1777,11 +2209,26 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a779a0 = {
};
static const struct rcar_csi2_info rcar_csi2_info_r8a779g0 = {
+ .regs = &rcsi2_registers_gen3,
.start_receiver = rcsi2_start_receiver_v4h,
.use_isp = true,
.support_cphy = true,
};
+static const struct rcsi2_register_layout rcsi2_registers_v4m = {
+ .phtw = V4M_PHTW_REG,
+ .phypll = V4M_PHYPLL_REG,
+};
+
+static const struct rcar_csi2_info rcar_csi2_info_r8a779h0 = {
+ .regs = &rcsi2_registers_v4m,
+ .start_receiver = rcsi2_start_receiver_v4m,
+ .hsfreqrange = hsfreqrange_v4m,
+ .csi0clkfreqrange = 0x0c,
+ .use_isp = true,
+ .support_dphy = true,
+};
+
static const struct of_device_id rcar_csi2_of_table[] = {
{
.compatible = "renesas,r8a774a1-csi2",
@@ -1835,6 +2282,10 @@ static const struct of_device_id rcar_csi2_of_table[] = {
.compatible = "renesas,r8a779g0-csi2",
.data = &rcar_csi2_info_r8a779g0,
},
+ {
+ .compatible = "renesas,r8a779h0-csi2",
+ .data = &rcar_csi2_info_r8a779h0,
+ },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, rcar_csi2_of_table);
@@ -1870,23 +2321,23 @@ static int rcsi2_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
- mutex_init(&priv->lock);
priv->stream_count = 0;
ret = rcsi2_probe_resources(priv, pdev);
if (ret) {
dev_err(priv->dev, "Failed to get resources\n");
- goto error_mutex;
+ return ret;
}
platform_set_drvdata(pdev, priv);
ret = rcsi2_parse_dt(priv);
if (ret)
- goto error_mutex;
+ return ret;
priv->subdev.owner = THIS_MODULE;
priv->subdev.dev = &pdev->dev;
+ priv->subdev.internal_ops = &rcar_csi2_internal_ops;
v4l2_subdev_init(&priv->subdev, &rcar_csi2_subdev_ops);
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s",
@@ -1896,7 +2347,7 @@ static int rcsi2_probe(struct platform_device *pdev)
priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
priv->subdev.entity.ops = &rcar_csi2_entity_ops;
- num_pads = priv->info->use_isp ? 2 : NR_OF_RCAR_CSI2_PAD;
+ num_pads = rcsi2_num_pads(priv);
priv->pads[RCAR_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
for (i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++)
@@ -1912,19 +2363,25 @@ static int rcsi2_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
+ ret = v4l2_subdev_init_finalize(&priv->subdev);
+ if (ret)
+ goto error_pm_runtime;
+
ret = v4l2_async_register_subdev(&priv->subdev);
if (ret < 0)
- goto error_async;
+ goto error_subdev;
dev_info(priv->dev, "%d lanes found\n", priv->lanes);
return 0;
+error_subdev:
+ v4l2_subdev_cleanup(&priv->subdev);
+error_pm_runtime:
+ pm_runtime_disable(&pdev->dev);
error_async:
v4l2_async_nf_unregister(&priv->notifier);
v4l2_async_nf_cleanup(&priv->notifier);
-error_mutex:
- mutex_destroy(&priv->lock);
return ret;
}
@@ -1936,14 +2393,13 @@ static void rcsi2_remove(struct platform_device *pdev)
v4l2_async_nf_unregister(&priv->notifier);
v4l2_async_nf_cleanup(&priv->notifier);
v4l2_async_unregister_subdev(&priv->subdev);
+ v4l2_subdev_cleanup(&priv->subdev);
pm_runtime_disable(&pdev->dev);
-
- mutex_destroy(&priv->lock);
}
static struct platform_driver rcar_csi2_pdrv = {
- .remove_new = rcsi2_remove,
+ .remove = rcsi2_remove,
.probe = rcsi2_probe,
.driver = {
.name = "rcar-csi2",
diff --git a/drivers/media/platform/renesas/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c
index bcef7b87da7c..cee9bbce4e3a 100644
--- a/drivers/media/platform/renesas/rcar-fcp.c
+++ b/drivers/media/platform/renesas/rcar-fcp.c
@@ -164,7 +164,7 @@ MODULE_DEVICE_TABLE(of, rcar_fcp_of_match);
static struct platform_driver rcar_fcp_platform_driver = {
.probe = rcar_fcp_probe,
- .remove_new = rcar_fcp_remove,
+ .remove = rcar_fcp_remove,
.driver = {
.name = "rcar-fcp",
.of_match_table = rcar_fcp_of_match,
diff --git a/drivers/media/platform/renesas/rcar-isp/Kconfig b/drivers/media/platform/renesas/rcar-isp/Kconfig
new file mode 100644
index 000000000000..242f6a23851f
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_RCAR_ISP
+ tristate "R-Car Image Signal Processor (ISP)"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_RENESAS || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select RESET_CONTROLLER
+ select V4L2_FWNODE
+ help
+ Support for Renesas R-Car Image Signal Processor (ISP).
+ Enable this to support the Renesas R-Car Image Signal
+ Processor (ISP).
+
+ To compile this driver as a module, choose M here: the
+ module will be called rcar-isp.
diff --git a/drivers/media/platform/renesas/rcar-isp/Makefile b/drivers/media/platform/renesas/rcar-isp/Makefile
new file mode 100644
index 000000000000..b542118c831e
--- /dev/null
+++ b/drivers/media/platform/renesas/rcar-isp/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+rcar-isp-objs = csisp.o
+
+obj-$(CONFIG_VIDEO_RCAR_ISP) += rcar-isp.o
diff --git a/drivers/media/platform/renesas/rcar-isp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
index 4512ac338ca5..1eb29a0b774a 100644
--- a/drivers/media/platform/renesas/rcar-isp.c
+++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
@@ -76,6 +76,54 @@ static const struct rcar_isp_format rcar_isp_formats[] = {
.code = MEDIA_BUS_FMT_YUYV10_2X10,
.datatype = MIPI_CSI2_DT_YUV422_8B,
.procmode = 0x0c,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .procmode = 0x00,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .procmode = 0x01,
+ }, {
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
+ }, {
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
+ }, {
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
+ }, {
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .procmode = 0x02,
},
};
@@ -111,7 +159,7 @@ enum rcar_isp_pads {
struct rcar_isp {
struct device *dev;
- void __iomem *base;
+ void __iomem *csbase;
struct reset_control *rstc;
enum rcar_isp_input csi_input;
@@ -121,9 +169,8 @@ struct rcar_isp {
struct v4l2_async_notifier notifier;
struct v4l2_subdev *remote;
+ unsigned int remote_pad;
- struct mutex lock; /* Protects mf and stream_count. */
- struct v4l2_mbus_framefmt mf;
int stream_count;
};
@@ -137,14 +184,14 @@ static inline struct rcar_isp *notifier_to_isp(struct v4l2_async_notifier *n)
return container_of(n, struct rcar_isp, notifier);
}
-static void risp_write(struct rcar_isp *isp, u32 offset, u32 value)
+static void risp_write_cs(struct rcar_isp *isp, u32 offset, u32 value)
{
- iowrite32(value, isp->base + offset);
+ iowrite32(value, isp->csbase + offset);
}
-static u32 risp_read(struct rcar_isp *isp, u32 offset)
+static u32 risp_read_cs(struct rcar_isp *isp, u32 offset)
{
- return ioread32(isp->base + offset);
+ return ioread32(isp->csbase + offset);
}
static int risp_power_on(struct rcar_isp *isp)
@@ -170,14 +217,19 @@ static void risp_power_off(struct rcar_isp *isp)
pm_runtime_put(isp->dev);
}
-static int risp_start(struct rcar_isp *isp)
+static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state)
{
+ const struct v4l2_mbus_framefmt *fmt;
const struct rcar_isp_format *format;
unsigned int vc;
u32 sel_csi = 0;
int ret;
- format = risp_code_to_fmt(isp->mf.code);
+ fmt = v4l2_subdev_state_get_format(state, RCAR_ISP_SINK);
+ if (!fmt)
+ return -EINVAL;
+
+ format = risp_code_to_fmt(fmt->code);
if (!format) {
dev_err(isp->dev, "Unsupported bus format\n");
return -EINVAL;
@@ -193,33 +245,34 @@ static int risp_start(struct rcar_isp *isp)
if (isp->csi_input == RISP_CSI_INPUT1)
sel_csi = ISPINPUTSEL0_SEL_CSI0;
- risp_write(isp, ISPINPUTSEL0_REG,
- risp_read(isp, ISPINPUTSEL0_REG) | sel_csi);
+ risp_write_cs(isp, ISPINPUTSEL0_REG,
+ risp_read_cs(isp, ISPINPUTSEL0_REG) | sel_csi);
/* Configure Channel Selector. */
for (vc = 0; vc < 4; vc++) {
u8 ch = vc + 4;
u8 dt = format->datatype;
- risp_write(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
- risp_write(isp, ISPCS_DT_CODE03_CH_REG(ch),
- ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |
- ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |
- ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |
- ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
+ risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
+ risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch),
+ ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |
+ ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |
+ ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |
+ ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
}
/* Setup processing method. */
- risp_write(isp, ISPPROCMODE_DT_REG(format->datatype),
- ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));
+ risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype),
+ ISPPROCMODE_DT_PROC_MODE_VC3(format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VC2(format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VC1(format->procmode) |
+ ISPPROCMODE_DT_PROC_MODE_VC0(format->procmode));
/* Start ISP. */
- risp_write(isp, ISPSTART_REG, ISPSTART_START);
+ risp_write_cs(isp, ISPSTART_REG, ISPSTART_START);
- ret = v4l2_subdev_call(isp->remote, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad,
+ BIT_ULL(0));
if (ret)
risp_power_off(isp);
@@ -228,95 +281,87 @@ static int risp_start(struct rcar_isp *isp)
static void risp_stop(struct rcar_isp *isp)
{
- v4l2_subdev_call(isp->remote, video, s_stream, 0);
+ v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, BIT_ULL(0));
/* Stop ISP. */
- risp_write(isp, ISPSTART_REG, ISPSTART_STOP);
+ risp_write_cs(isp, ISPSTART_REG, ISPSTART_STOP);
risp_power_off(isp);
}
-static int risp_s_stream(struct v4l2_subdev *sd, int enable)
+static int risp_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 source_pad,
+ u64 source_streams_mask)
{
struct rcar_isp *isp = sd_to_isp(sd);
int ret = 0;
- mutex_lock(&isp->lock);
+ if (source_streams_mask != 1)
+ return -EINVAL;
- if (!isp->remote) {
- ret = -ENODEV;
- goto out;
- }
+ if (!isp->remote)
+ return -ENODEV;
- if (enable && isp->stream_count == 0) {
- ret = risp_start(isp);
+ if (isp->stream_count == 0) {
+ ret = risp_start(isp, state);
if (ret)
- goto out;
- } else if (!enable && isp->stream_count == 1) {
- risp_stop(isp);
+ return ret;
}
- isp->stream_count += enable ? 1 : -1;
-out:
- mutex_unlock(&isp->lock);
+ isp->stream_count += 1;
return ret;
}
-static const struct v4l2_subdev_video_ops risp_video_ops = {
- .s_stream = risp_s_stream,
-};
-
-static int risp_set_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
+static int risp_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 source_pad,
+ u64 source_streams_mask)
{
struct rcar_isp *isp = sd_to_isp(sd);
- struct v4l2_mbus_framefmt *framefmt;
- mutex_lock(&isp->lock);
+ if (source_streams_mask != 1)
+ return -EINVAL;
- if (!risp_code_to_fmt(format->format.code))
- format->format.code = rcar_isp_formats[0].code;
+ if (!isp->remote)
+ return -ENODEV;
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
- isp->mf = format->format;
- } else {
- framefmt = v4l2_subdev_state_get_format(sd_state, 0);
- *framefmt = format->format;
- }
+ if (isp->stream_count == 1)
+ risp_stop(isp);
- mutex_unlock(&isp->lock);
+ isp->stream_count -= 1;
return 0;
}
-static int risp_get_pad_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
+static int risp_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- struct rcar_isp *isp = sd_to_isp(sd);
+ struct v4l2_mbus_framefmt *framefmt;
- mutex_lock(&isp->lock);
+ if (format->pad > RCAR_ISP_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
- if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- format->format = isp->mf;
- else
- format->format = *v4l2_subdev_state_get_format(sd_state, 0);
+ if (!risp_code_to_fmt(format->format.code))
+ format->format.code = rcar_isp_formats[0].code;
- mutex_unlock(&isp->lock);
+ for (unsigned int i = 0; i < RCAR_ISP_NUM_PADS; i++) {
+ framefmt = v4l2_subdev_state_get_format(state, i);
+ *framefmt = format->format;
+ }
return 0;
}
static const struct v4l2_subdev_pad_ops risp_pad_ops = {
+ .enable_streams = risp_enable_streams,
+ .disable_streams = risp_disable_streams,
.set_fmt = risp_set_pad_format,
- .get_fmt = risp_get_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
.link_validate = v4l2_subdev_link_validate_default,
};
static const struct v4l2_subdev_ops rcar_isp_subdev_ops = {
- .video = &risp_video_ops,
.pad = &risp_pad_ops,
};
@@ -339,6 +384,7 @@ static int risp_notify_bound(struct v4l2_async_notifier *notifier,
}
isp->remote = subdev;
+ isp->remote_pad = pad;
dev_dbg(isp->dev, "Bound %s pad: %d\n", subdev->name, pad);
@@ -419,9 +465,20 @@ static const struct media_entity_operations risp_entity_ops = {
static int risp_probe_resources(struct rcar_isp *isp,
struct platform_device *pdev)
{
- isp->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
- if (IS_ERR(isp->base))
- return PTR_ERR(isp->base);
+ struct resource *res;
+
+ /*
+ * For backward compatibility allow cs base to be the only reg if no
+ * reg-names are set in DT.
+ */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs");
+ if (!res)
+ isp->csbase = devm_platform_ioremap_resource(pdev, 0);
+ else
+ isp->csbase = devm_ioremap_resource(&pdev->dev, res);
+
+ if (IS_ERR(isp->csbase))
+ return PTR_ERR(isp->csbase);
isp->rstc = devm_reset_control_get(&pdev->dev, NULL);
@@ -431,7 +488,9 @@ static int risp_probe_resources(struct rcar_isp *isp,
static const struct of_device_id risp_of_id_table[] = {
{ .compatible = "renesas,r8a779a0-isp" },
{ .compatible = "renesas,r8a779g0-isp" },
- { /* sentinel */ },
+ /* Keep above for compatibility with old DTB files. */
+ { .compatible = "renesas,rcar-gen4-isp" },
+ { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, risp_of_id_table);
@@ -447,12 +506,10 @@ static int risp_probe(struct platform_device *pdev)
isp->dev = &pdev->dev;
- mutex_init(&isp->lock);
-
ret = risp_probe_resources(isp, pdev);
if (ret) {
dev_err(isp->dev, "Failed to get resources\n");
- goto error_mutex;
+ return ret;
}
platform_set_drvdata(pdev, isp);
@@ -483,20 +540,25 @@ static int risp_probe(struct platform_device *pdev)
if (ret)
goto error_notifier;
+ ret = v4l2_subdev_init_finalize(&isp->subdev);
+ if (ret)
+ goto error_notifier;
+
ret = v4l2_async_register_subdev(&isp->subdev);
if (ret < 0)
- goto error_notifier;
+ goto error_subdev;
dev_info(isp->dev, "Using CSI-2 input: %u\n", isp->csi_input);
return 0;
+
+error_subdev:
+ v4l2_subdev_cleanup(&isp->subdev);
error_notifier:
v4l2_async_nf_unregister(&isp->notifier);
v4l2_async_nf_cleanup(&isp->notifier);
error_pm:
pm_runtime_disable(&pdev->dev);
-error_mutex:
- mutex_destroy(&isp->lock);
return ret;
}
@@ -509,10 +571,9 @@ static void risp_remove(struct platform_device *pdev)
v4l2_async_nf_cleanup(&isp->notifier);
v4l2_async_unregister_subdev(&isp->subdev);
+ v4l2_subdev_cleanup(&isp->subdev);
pm_runtime_disable(&pdev->dev);
-
- mutex_destroy(&isp->lock);
}
static struct platform_driver rcar_isp_driver = {
@@ -522,7 +583,7 @@ static struct platform_driver rcar_isp_driver = {
.of_match_table = risp_of_id_table,
},
.probe = risp_probe,
- .remove_new = risp_remove,
+ .remove = risp_remove,
};
module_platform_driver(rcar_isp_driver);
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
index 809c3a38cc4a..846ae7989b1d 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
@@ -1080,13 +1080,11 @@ static int __maybe_unused rvin_suspend(struct device *dev)
{
struct rvin_dev *vin = dev_get_drvdata(dev);
- if (vin->state != RUNNING)
+ if (!vin->running)
return 0;
rvin_stop_streaming(vin);
- vin->state = SUSPENDED;
-
return 0;
}
@@ -1094,7 +1092,7 @@ static int __maybe_unused rvin_resume(struct device *dev)
{
struct rvin_dev *vin = dev_get_drvdata(dev);
- if (vin->state != SUSPENDED)
+ if (!vin->running)
return 0;
/*
@@ -1274,20 +1272,12 @@ static const struct rvin_info rcar_info_r8a77995 = {
.scaler = rvin_scaler_gen3,
};
-static const struct rvin_info rcar_info_r8a779a0 = {
- .model = RCAR_GEN3,
- .use_mc = true,
- .use_isp = true,
- .nv12 = true,
- .max_width = 4096,
- .max_height = 4096,
-};
-
-static const struct rvin_info rcar_info_r8a779g0 = {
- .model = RCAR_GEN3,
+static const struct rvin_info rcar_info_gen4 = {
+ .model = RCAR_GEN4,
.use_mc = true,
.use_isp = true,
.nv12 = true,
+ .raw10 = true,
.max_width = 4096,
.max_height = 4096,
};
@@ -1354,12 +1344,18 @@ static const struct of_device_id rvin_of_id_table[] = {
.data = &rcar_info_r8a77995,
},
{
+ /* Keep to be compatible with old DTS files. */
.compatible = "renesas,vin-r8a779a0",
- .data = &rcar_info_r8a779a0,
+ .data = &rcar_info_gen4,
},
{
+ /* Keep to be compatible with old DTS files. */
.compatible = "renesas,vin-r8a779g0",
- .data = &rcar_info_r8a779g0,
+ .data = &rcar_info_gen4,
+ },
+ {
+ .compatible = "renesas,rcar-gen4-vin",
+ .data = &rcar_info_gen4,
},
{ /* Sentinel */ },
};
@@ -1446,7 +1442,7 @@ static struct platform_driver rcar_vin_driver = {
.of_match_table = rvin_of_id_table,
},
.probe = rcar_vin_probe,
- .remove_new = rcar_vin_remove,
+ .remove = rcar_vin_remove,
};
module_platform_driver(rcar_vin_driver);
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index e2c40abc6d3d..5c08ee2c9807 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -94,6 +94,7 @@
#define VNMC_INF_YUV16 (5 << 16)
#define VNMC_INF_RGB888 (6 << 16)
#define VNMC_INF_RGB666 (7 << 16)
+#define VNMC_EXINF_RAW8 (1 << 12) /* Gen4 specific */
#define VNMC_VUP (1 << 10)
#define VNMC_IM_ODD (0 << 3)
#define VNMC_IM_ODD_EVEN (1 << 3)
@@ -123,7 +124,9 @@
/* Video n Data Mode Register bits */
#define VNDMR_A8BIT(n) (((n) & 0xff) << 24)
#define VNDMR_A8BIT_MASK (0xff << 24)
+#define VNDMR_RMODE_RAW10 (2 << 19)
#define VNDMR_YMODE_Y8 (1 << 12)
+#define VNDMR_YC_THR (1 << 11)
#define VNDMR_EXRGB (1 << 8)
#define VNDMR_BPSM (1 << 4)
#define VNDMR_ABIT (1 << 2)
@@ -640,8 +643,6 @@ void rvin_scaler_gen3(struct rvin_dev *vin)
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
clip_size |= vin->compose.height / 2;
break;
default:
@@ -677,22 +678,6 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
fmt = rvin_format_from_pixel(vin, vin->format.pixelformat);
stride = vin->format.bytesperline / fmt->bpp;
-
- /* For RAW8 format bpp is 1, but the hardware process RAW8
- * format in 2 pixel unit hence configure VNIS_REG as stride / 2.
- */
- switch (vin->format.pixelformat) {
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- case V4L2_PIX_FMT_GREY:
- stride /= 2;
- break;
- default:
- break;
- }
-
rvin_write(vin, stride, VNIS_REG);
}
@@ -725,8 +710,6 @@ static int rvin_setup(struct rvin_dev *vin)
case V4L2_FIELD_INTERLACED_BT:
vnmc = VNMC_IM_FULL | VNMC_FOC;
break;
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
case V4L2_FIELD_NONE:
case V4L2_FIELD_ALTERNATE:
vnmc = VNMC_IM_ODD_EVEN;
@@ -742,12 +725,22 @@ static int rvin_setup(struct rvin_dev *vin)
*/
switch (vin->mbus_code) {
case MEDIA_BUS_FMT_YUYV8_1X16:
- /* BT.601/BT.1358 16bit YCbCr422 */
- vnmc |= VNMC_INF_YUV16;
+ if (vin->is_csi)
+ /* YCbCr422 8-bit */
+ vnmc |= VNMC_INF_YUV8_BT601;
+ else
+ /* BT.601/BT.1358 16bit YCbCr422 */
+ vnmc |= VNMC_INF_YUV16;
input_is_yuv = true;
break;
case MEDIA_BUS_FMT_UYVY8_1X16:
- vnmc |= VNMC_INF_YUV16 | VNMC_YCAL;
+ if (vin->is_csi)
+ /* YCbCr422 8-bit */
+ vnmc |= VNMC_INF_YUV8_BT601;
+ else
+ /* BT.601/BT.1358 16bit YCbCr422 */
+ vnmc |= VNMC_INF_YUV16;
+ vnmc |= VNMC_YCAL;
input_is_yuv = true;
break;
case MEDIA_BUS_FMT_UYVY8_2X8:
@@ -779,36 +772,21 @@ static int rvin_setup(struct rvin_dev *vin)
case MEDIA_BUS_FMT_SRGGB8_1X8:
case MEDIA_BUS_FMT_Y8_1X8:
vnmc |= VNMC_INF_RAW8;
+ if (vin->info->model == RCAR_GEN4)
+ vnmc |= VNMC_EXINF_RAW8;
+ break;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ vnmc |= VNMC_INF_RGB666;
break;
default:
break;
}
- /* Make sure input interface and input format is valid. */
- if (vin->info->model == RCAR_GEN3) {
- switch (vnmc & VNMC_INF_MASK) {
- case VNMC_INF_YUV8_BT656:
- case VNMC_INF_YUV10_BT656:
- case VNMC_INF_YUV16:
- case VNMC_INF_RGB666:
- if (vin->is_csi) {
- vin_err(vin, "Invalid setting in MIPI CSI2\n");
- return -EINVAL;
- }
- break;
- case VNMC_INF_RAW8:
- if (!vin->is_csi) {
- vin_err(vin, "Invalid setting in Digital Pins\n");
- return -EINVAL;
- }
- break;
- default:
- break;
- }
- }
-
/* Enable VSYNC Field Toggle mode after one VSYNC input */
- if (vin->info->model == RCAR_GEN3)
+ if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4)
dmr2 = VNDMR2_FTEV;
else
dmr2 = VNDMR2_FTEV | VNDMR2_VLV(1);
@@ -888,6 +866,12 @@ static int rvin_setup(struct rvin_dev *vin)
dmr = 0;
}
break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ dmr = VNDMR_RMODE_RAW10;
+ break;
default:
vin_err(vin, "Invalid pixelformat (0x%x)\n",
vin->format.pixelformat);
@@ -902,7 +886,7 @@ static int rvin_setup(struct rvin_dev *vin)
if (input_is_yuv == output_is_yuv)
vnmc |= VNMC_BPS;
- if (vin->info->model == RCAR_GEN3) {
+ if (vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) {
/* Select between CSI-2 and parallel input */
if (vin->is_csi)
vnmc &= ~VNMC_DPINE;
@@ -997,33 +981,13 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
struct rvin_buffer *buf;
struct vb2_v4l2_buffer *vbuf;
dma_addr_t phys_addr;
- int prev;
/* A already populated slot shall never be overwritten. */
if (WARN_ON(vin->buf_hw[slot].buffer))
return;
- prev = (slot == 0 ? HW_BUFFER_NUM : slot) - 1;
-
- if (vin->buf_hw[prev].type == HALF_TOP) {
- vbuf = vin->buf_hw[prev].buffer;
- vin->buf_hw[slot].buffer = vbuf;
- vin->buf_hw[slot].type = HALF_BOTTOM;
- switch (vin->format.pixelformat) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV16:
- phys_addr = vin->buf_hw[prev].phys +
- vin->format.sizeimage / 4;
- break;
- default:
- phys_addr = vin->buf_hw[prev].phys +
- vin->format.sizeimage / 2;
- break;
- }
- } else if ((vin->state != STOPPED && vin->state != RUNNING) ||
- list_empty(&vin->buf_list)) {
+ if (list_empty(&vin->buf_list)) {
vin->buf_hw[slot].buffer = NULL;
- vin->buf_hw[slot].type = FULL;
phys_addr = vin->scratch_phys;
} else {
/* Keep track of buffer we give to HW */
@@ -1032,16 +996,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
list_del_init(to_buf_list(vbuf));
vin->buf_hw[slot].buffer = vbuf;
- vin->buf_hw[slot].type =
- V4L2_FIELD_IS_SEQUENTIAL(vin->format.field) ?
- HALF_TOP : FULL;
-
/* Setup DMA */
phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
}
- vin_dbg(vin, "Filling HW slot: %d type: %d buffer: %p\n",
- slot, vin->buf_hw[slot].type, vin->buf_hw[slot].buffer);
+ vin_dbg(vin, "Filling HW slot: %d buffer: %p\n",
+ slot, vin->buf_hw[slot].buffer);
vin->buf_hw[slot].phys = phys_addr;
rvin_set_slot_addr(vin, slot, phys_addr);
@@ -1049,15 +1009,12 @@ static void rvin_fill_hw_slot(struct rvin_dev *vin, int slot)
static int rvin_capture_start(struct rvin_dev *vin)
{
- int slot, ret;
+ int ret;
- for (slot = 0; slot < HW_BUFFER_NUM; slot++) {
+ for (unsigned int slot = 0; slot < HW_BUFFER_NUM; slot++) {
vin->buf_hw[slot].buffer = NULL;
- vin->buf_hw[slot].type = FULL;
- }
-
- for (slot = 0; slot < HW_BUFFER_NUM; slot++)
rvin_fill_hw_slot(vin, slot);
+ }
ret = rvin_setup(vin);
if (ret)
@@ -1070,8 +1027,6 @@ static int rvin_capture_start(struct rvin_dev *vin)
/* Continuous Frame Capture Mode */
rvin_write(vin, VNFC_C_FRAME, VNFC_REG);
- vin->state = STARTING;
-
return 0;
}
@@ -1112,9 +1067,9 @@ static irqreturn_t rvin_irq(int irq, void *data)
if (!(int_status & VNINTS_FIS))
goto done;
- /* Nothing to do if capture status is 'STOPPED' */
- if (vin->state == STOPPED) {
- vin_dbg(vin, "IRQ while state stopped\n");
+ /* Nothing to do if not running. */
+ if (!vin->running) {
+ vin_dbg(vin, "IRQ while not running, ignoring\n");
goto done;
}
@@ -1126,28 +1081,17 @@ static irqreturn_t rvin_irq(int irq, void *data)
* To hand buffers back in a known order to userspace start
* to capture first from slot 0.
*/
- if (vin->state == STARTING) {
+ if (!vin->sequence) {
if (slot != 0) {
vin_dbg(vin, "Starting sync slot: %d\n", slot);
goto done;
}
vin_dbg(vin, "Capture start synced!\n");
- vin->state = RUNNING;
}
/* Capture frame */
if (vin->buf_hw[slot].buffer) {
- /*
- * Nothing to do but refill the hardware slot if
- * capture only filled first half of vb2 buffer.
- */
- if (vin->buf_hw[slot].type == HALF_TOP) {
- vin->buf_hw[slot].buffer = NULL;
- rvin_fill_hw_slot(vin, slot);
- goto done;
- }
-
vin->buf_hw[slot].buffer->field =
rvin_get_active_field(vin, vnms);
vin->buf_hw[slot].buffer->sequence = vin->sequence;
@@ -1270,6 +1214,22 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
if (vin->format.pixelformat != V4L2_PIX_FMT_GREY)
return -EPIPE;
break;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SBGGR10)
+ return -EPIPE;
+ break;
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SGBRG10)
+ return -EPIPE;
+ break;
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SGRBG10)
+ return -EPIPE;
+ break;
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ if (vin->format.pixelformat != V4L2_PIX_FMT_SRGGB10)
+ return -EPIPE;
+ break;
default:
return -EPIPE;
}
@@ -1282,8 +1242,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
/* Supported natively */
break;
case V4L2_FIELD_ALTERNATE:
@@ -1296,8 +1254,6 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
/* Use VIN hardware to combine the two fields */
fmt.format.height *= 2;
break;
@@ -1311,7 +1267,7 @@ static int rvin_mc_validate_format(struct rvin_dev *vin, struct v4l2_subdev *sd,
if (rvin_scaler_needed(vin)) {
/* Gen3 can't scale NV12 */
- if (vin->info->model == RCAR_GEN3 &&
+ if ((vin->info->model == RCAR_GEN3 || vin->info->model == RCAR_GEN4) &&
vin->format.pixelformat == V4L2_PIX_FMT_NV12)
return -EPIPE;
@@ -1357,7 +1313,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
if (!on) {
video_device_pipeline_stop(&vin->vdev);
- return v4l2_subdev_call(sd, video, s_stream, 0);
+ return v4l2_subdev_disable_streams(sd, pad->index, BIT_ULL(0));
}
ret = rvin_mc_validate_format(vin, sd, pad);
@@ -1368,7 +1324,7 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
if (ret)
return ret;
- ret = v4l2_subdev_call(sd, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(sd, pad->index, BIT_ULL(0));
if (ret == -ENOIOCTLCMD)
ret = 0;
if (ret)
@@ -1394,6 +1350,8 @@ int rvin_start_streaming(struct rvin_dev *vin)
if (ret)
rvin_set_stream(vin, 0);
+ vin->running = true;
+
spin_unlock_irqrestore(&vin->qlock, flags);
return ret;
@@ -1426,44 +1384,21 @@ err_scratch:
void rvin_stop_streaming(struct rvin_dev *vin)
{
- unsigned int i, retries;
unsigned long flags;
- bool buffersFreed;
spin_lock_irqsave(&vin->qlock, flags);
- if (vin->state == STOPPED) {
+ if (!vin->running) {
spin_unlock_irqrestore(&vin->qlock, flags);
return;
}
- vin->state = STOPPING;
-
- /* Wait until only scratch buffer is used, max 3 interrupts. */
- retries = 0;
- while (retries++ < RVIN_RETRIES) {
- buffersFreed = true;
- for (i = 0; i < HW_BUFFER_NUM; i++)
- if (vin->buf_hw[i].buffer)
- buffersFreed = false;
-
- if (buffersFreed)
- break;
-
- spin_unlock_irqrestore(&vin->qlock, flags);
- msleep(RVIN_TIMEOUT_MS);
- spin_lock_irqsave(&vin->qlock, flags);
- }
-
/* Wait for streaming to stop */
- retries = 0;
- while (retries++ < RVIN_RETRIES) {
-
+ for (unsigned int i = 0; i < RVIN_RETRIES; i++) {
rvin_capture_stop(vin);
/* Check if HW is stopped */
if (!rvin_capture_active(vin)) {
- vin->state = STOPPED;
break;
}
@@ -1472,32 +1407,25 @@ void rvin_stop_streaming(struct rvin_dev *vin)
spin_lock_irqsave(&vin->qlock, flags);
}
- if (!buffersFreed || vin->state != STOPPED) {
- /*
- * If this happens something have gone horribly wrong.
- * Set state to stopped to prevent the interrupt handler
- * to make things worse...
- */
- vin_err(vin, "Failed stop HW, something is seriously broken\n");
- vin->state = STOPPED;
- }
+ if (rvin_capture_active(vin))
+ vin_err(vin, "Hardware did not stop\n");
- spin_unlock_irqrestore(&vin->qlock, flags);
+ vin->running = false;
- /* If something went wrong, free buffers with an error. */
- if (!buffersFreed) {
- return_unused_buffers(vin, VB2_BUF_STATE_ERROR);
- for (i = 0; i < HW_BUFFER_NUM; i++) {
- if (vin->buf_hw[i].buffer)
- vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf,
- VB2_BUF_STATE_ERROR);
- }
- }
+ spin_unlock_irqrestore(&vin->qlock, flags);
rvin_set_stream(vin, 0);
/* disable interrupts */
rvin_disable_interrupts(vin);
+
+ /* Return unprocessed buffers from hardware. */
+ for (unsigned int i = 0; i < HW_BUFFER_NUM; i++) {
+ if (vin->buf_hw[i].buffer)
+ vb2_buffer_done(&vin->buf_hw[i].buffer->vb2_buf,
+ VB2_BUF_STATE_ERROR);
+ }
+
}
static void rvin_stop_streaming_vq(struct vb2_queue *vq)
@@ -1519,8 +1447,6 @@ static const struct vb2_ops rvin_qops = {
.buf_queue = rvin_buffer_queue,
.start_streaming = rvin_start_streaming_vq,
.stop_streaming = rvin_stop_streaming_vq,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
void rvin_dma_unregister(struct rvin_dev *vin)
@@ -1545,8 +1471,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq)
spin_lock_init(&vin->qlock);
- vin->state = STOPPED;
-
for (i = 0; i < HW_BUFFER_NUM; i++)
vin->buf_hw[i].buffer = NULL;
@@ -1649,7 +1573,7 @@ void rvin_set_alpha(struct rvin_dev *vin, unsigned int alpha)
vin->alpha = alpha;
- if (vin->state == STOPPED)
+ if (!vin->running)
goto out;
switch (vin->format.pixelformat) {
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
index 073f70c6ac68..db091af57c19 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
@@ -86,6 +86,22 @@ static const struct rvin_video_format rvin_formats[] = {
.fourcc = V4L2_PIX_FMT_GREY,
.bpp = 1,
},
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10,
+ .bpp = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG10,
+ .bpp = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG10,
+ .bpp = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB10,
+ .bpp = 2,
+ },
};
const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin,
@@ -106,6 +122,13 @@ const struct rvin_video_format *rvin_format_from_pixel(struct rvin_dev *vin,
if (!vin->info->nv12 || !(BIT(vin->id) & 0x3333))
return NULL;
break;
+ case V4L2_PIX_FMT_SBGGR10:
+ case V4L2_PIX_FMT_SGBRG10:
+ case V4L2_PIX_FMT_SGRBG10:
+ case V4L2_PIX_FMT_SRGGB10:
+ if (!vin->info->raw10)
+ return NULL;
+ break;
default:
break;
}
@@ -138,9 +161,6 @@ static u32 rvin_format_bytesperline(struct rvin_dev *vin,
break;
}
- if (V4L2_FIELD_IS_SEQUENTIAL(pix->field))
- align = 0x80;
-
return ALIGN(pix->width, align) * fmt->bpp;
}
@@ -171,8 +191,6 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
case V4L2_FIELD_ALTERNATE:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
break;
default:
pix->field = RVIN_DEFAULT_FIELD;
@@ -407,6 +425,26 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv,
return -EINVAL;
f->pixelformat = V4L2_PIX_FMT_SRGGB8;
return 0;
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SBGGR10;
+ return 0;
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SGBRG10;
+ return 0;
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SGRBG10;
+ return 0;
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ if (f->index)
+ return -EINVAL;
+ f->pixelformat = V4L2_PIX_FMT_SRGGB10;
+ return 0;
default:
return -EINVAL;
}
@@ -461,8 +499,6 @@ static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect)
case V4L2_FIELD_INTERLACED_TB:
case V4L2_FIELD_INTERLACED_BT:
case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
rect->height *= 2;
break;
}
@@ -548,8 +584,8 @@ static int rvin_s_selection(struct file *file, void *fh,
vin->crop = s->r = r;
- vin_dbg(vin, "Cropped %dx%d@%d:%d of %dx%d\n",
- r.width, r.height, r.left, r.top,
+ vin_dbg(vin, "Cropped (%d,%d)/%ux%u of %dx%d\n",
+ r.left, r.top, r.width, r.height,
max_rect.width, max_rect.height);
break;
case V4L2_SEL_TGT_COMPOSE:
@@ -573,8 +609,8 @@ static int rvin_s_selection(struct file *file, void *fh,
vin->compose = s->r = r;
- vin_dbg(vin, "Compose %dx%d@%d:%d in %dx%d\n",
- r.width, r.height, r.left, r.top,
+ vin_dbg(vin, "Compose (%d,%d)/%ux%u in %dx%d\n",
+ r.left, r.top, r.width, r.height,
vin->format.width, vin->format.height);
break;
default:
@@ -730,7 +766,8 @@ static int rvin_s_dv_timings(struct file *file, void *priv_fh,
struct v4l2_subdev *sd = vin_to_source(vin);
int ret;
- ret = v4l2_subdev_call(sd, video, s_dv_timings, timings);
+ ret = v4l2_subdev_call(sd, pad, s_dv_timings,
+ vin->parallel.sink_pad, timings);
if (ret)
return ret;
@@ -744,7 +781,8 @@ static int rvin_g_dv_timings(struct file *file, void *priv_fh,
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- return v4l2_subdev_call(sd, video, g_dv_timings, timings);
+ return v4l2_subdev_call(sd, pad, g_dv_timings,
+ vin->parallel.sink_pad, timings);
}
static int rvin_query_dv_timings(struct file *file, void *priv_fh,
@@ -753,7 +791,8 @@ static int rvin_query_dv_timings(struct file *file, void *priv_fh,
struct rvin_dev *vin = video_drvdata(file);
struct v4l2_subdev *sd = vin_to_source(vin);
- return v4l2_subdev_call(sd, video, query_dv_timings, timings);
+ return v4l2_subdev_call(sd, pad, query_dv_timings,
+ vin->parallel.sink_pad, timings);
}
static int rvin_dv_timings_cap(struct file *file, void *priv_fh,
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
index 792336dada44..83d1b2734c41 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
@@ -39,6 +39,7 @@ enum model_id {
RCAR_M1,
RCAR_GEN2,
RCAR_GEN3,
+ RCAR_GEN4,
};
enum rvin_csi_id {
@@ -59,40 +60,7 @@ enum rvin_isp_id {
#define RVIN_REMOTES_MAX \
(((unsigned int)RVIN_CSI_MAX) > ((unsigned int)RVIN_ISP_MAX) ? \
- RVIN_CSI_MAX : RVIN_ISP_MAX)
-
-/**
- * enum rvin_dma_state - DMA states
- * @STOPPED: No operation in progress
- * @STARTING: Capture starting up
- * @RUNNING: Operation in progress have buffers
- * @STOPPING: Stopping operation
- * @SUSPENDED: Capture is suspended
- */
-enum rvin_dma_state {
- STOPPED = 0,
- STARTING,
- RUNNING,
- STOPPING,
- SUSPENDED,
-};
-
-/**
- * enum rvin_buffer_type
- *
- * Describes how a buffer is given to the hardware. To be able
- * to capture SEQ_TB/BT it's needed to capture to the same vb2
- * buffer twice so the type of buffer needs to be kept.
- *
- * @FULL: One capture fills the whole vb2 buffer
- * @HALF_TOP: One capture fills the top half of the vb2 buffer
- * @HALF_BOTTOM: One capture fills the bottom half of the vb2 buffer
- */
-enum rvin_buffer_type {
- FULL,
- HALF_TOP,
- HALF_BOTTOM,
-};
+ (unsigned int)RVIN_CSI_MAX : (unsigned int)RVIN_ISP_MAX)
/**
* struct rvin_video_format - Data format stored in memory
@@ -151,7 +119,8 @@ struct rvin_group_route {
* @model: VIN model
* @use_mc: use media controller instead of controlling subdevice
* @use_isp: the VIN is connected to the ISP and not to the CSI-2
- * @nv12: support outputing NV12 pixel format
+ * @nv12: support outputting NV12 pixel format
+ * @raw10: support outputting RAW10 pixel format
* @max_width: max input width the VIN supports
* @max_height: max input height the VIN supports
* @routes: list of possible routes from the CSI-2 recivers to
@@ -163,6 +132,7 @@ struct rvin_info {
bool use_mc;
bool use_isp;
bool nv12;
+ bool raw10;
unsigned int max_width;
unsigned int max_height;
@@ -192,11 +162,11 @@ struct rvin_info {
* @scratch: cpu address for scratch buffer
* @scratch_phys: physical address of the scratch buffer
*
- * @qlock: protects @buf_hw, @buf_list, @sequence and @state
+ * @qlock: Protects @buf_hw, @buf_list, @sequence and @running
* @buf_hw: Keeps track of buffers given to HW slot
* @buf_list: list of queued buffers
* @sequence: V4L2 buffers sequence number
- * @state: keeps track of operation state
+ * @running: Keeps track of if the VIN is running
*
* @is_csi: flag to mark the VIN as using a CSI-2 subdevice
* @chsel: Cached value of the current CSI-2 channel selection
@@ -235,12 +205,11 @@ struct rvin_dev {
spinlock_t qlock;
struct {
struct vb2_v4l2_buffer *buffer;
- enum rvin_buffer_type type;
dma_addr_t phys;
} buf_hw[HW_BUFFER_NUM];
struct list_head buf_list;
unsigned int sequence;
- enum rvin_dma_state state;
+ bool running;
bool is_csi;
unsigned int chsel;
diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c
index f21d05054341..fc8b6bbef793 100644
--- a/drivers/media/platform/renesas/rcar_drif.c
+++ b/drivers/media/platform/renesas/rcar_drif.c
@@ -861,8 +861,6 @@ static const struct vb2_ops rcar_drif_vb2_ops = {
.buf_queue = rcar_drif_buf_queue,
.start_streaming = rcar_drif_start_streaming,
.stop_streaming = rcar_drif_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int rcar_drif_querycap(struct file *file, void *fh,
@@ -1071,7 +1069,6 @@ static int rcar_drif_sdr_register(struct rcar_drif_sdr *sdr)
sdr->vdev->release = video_device_release;
sdr->vdev->lock = &sdr->v4l2_mutex;
sdr->vdev->queue = &sdr->vb_queue;
- sdr->vdev->queue->lock = &sdr->vb_queue_mutex;
sdr->vdev->ctrl_handler = &sdr->ctrl_hdl;
sdr->vdev->v4l2_dev = &sdr->v4l2_dev;
sdr->vdev->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_TUNER |
@@ -1316,6 +1313,7 @@ static int rcar_drif_sdr_probe(struct rcar_drif_sdr *sdr)
sdr->vb_queue.ops = &rcar_drif_vb2_ops;
sdr->vb_queue.mem_ops = &vb2_vmalloc_memops;
sdr->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ sdr->vb_queue.lock = &sdr->vb_queue_mutex;
/* Init videobuf2 queue */
ret = vb2_queue_init(&sdr->vb_queue);
@@ -1475,7 +1473,7 @@ static struct platform_driver rcar_drif_driver = {
.pm = &rcar_drif_pm_ops,
},
.probe = rcar_drif_probe,
- .remove_new = rcar_drif_remove,
+ .remove = rcar_drif_remove,
};
module_platform_driver(rcar_drif_driver);
diff --git a/drivers/media/platform/renesas/rcar_fdp1.c b/drivers/media/platform/renesas/rcar_fdp1.c
index a2565b269f3b..5d453a7a8988 100644
--- a/drivers/media/platform/renesas/rcar_fdp1.c
+++ b/drivers/media/platform/renesas/rcar_fdp1.c
@@ -2032,8 +2032,6 @@ static const struct vb2_ops fdp1_qops = {
.buf_queue = fdp1_buf_queue,
.start_streaming = fdp1_start_streaming,
.stop_streaming = fdp1_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq,
@@ -2444,7 +2442,7 @@ MODULE_DEVICE_TABLE(of, fdp1_dt_ids);
static struct platform_driver fdp1_pdrv = {
.probe = fdp1_probe,
- .remove_new = fdp1_remove,
+ .remove = fdp1_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = fdp1_dt_ids,
diff --git a/drivers/media/platform/renesas/rcar_jpu.c b/drivers/media/platform/renesas/rcar_jpu.c
index fff349e45067..81038df71bb5 100644
--- a/drivers/media/platform/renesas/rcar_jpu.c
+++ b/drivers/media/platform/renesas/rcar_jpu.c
@@ -14,7 +14,7 @@
* 3) V4L2_CID_JPEG_ACTIVE_MARKER
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/interrupt.h>
@@ -1171,8 +1171,6 @@ static const struct vb2_ops jpu_qops = {
.buf_finish = jpu_buf_finish,
.start_streaming = jpu_start_streaming,
.stop_streaming = jpu_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int jpu_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -1736,7 +1734,7 @@ static const struct dev_pm_ops jpu_pm_ops = {
static struct platform_driver jpu_driver = {
.probe = jpu_probe,
- .remove_new = jpu_remove,
+ .remove = jpu_remove,
.driver = {
.of_match_table = jpu_dt_ids,
.name = DRV_NAME,
diff --git a/drivers/media/platform/renesas/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c
index 167760276796..8cceafe491b1 100644
--- a/drivers/media/platform/renesas/renesas-ceu.c
+++ b/drivers/media/platform/renesas/renesas-ceu.c
@@ -761,8 +761,6 @@ static const struct vb2_ops ceu_vb2_ops = {
.queue_setup = ceu_vb2_setup,
.buf_queue = ceu_vb2_queue,
.buf_prepare = ceu_vb2_prepare,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = ceu_start_streaming,
.stop_streaming = ceu_stop_streaming,
};
@@ -1723,7 +1721,7 @@ static struct platform_driver ceu_driver = {
.of_match_table = of_match_ptr(ceu_of_match),
},
.probe = ceu_probe,
- .remove_new = ceu_remove,
+ .remove = ceu_remove,
};
module_platform_driver(ceu_driver);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
index 280efd2a8185..5fa73ab2db53 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
@@ -22,6 +22,7 @@
#include <media/v4l2-mc.h>
#include "rzg2l-cru.h"
+#include "rzg2l-cru-regs.h"
static inline struct rzg2l_cru_dev *notifier_to_cru(struct v4l2_async_notifier *n)
{
@@ -72,7 +73,6 @@ static int rzg2l_cru_group_notify_complete(struct v4l2_async_notifier *notifier)
source->name, sink->name);
return ret;
}
- cru->csi.channel = 0;
cru->ip.remote = cru->csi.subdev;
/* Create media device link between CRU IP <-> CRU OUTPUT */
@@ -209,7 +209,7 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru)
const struct of_device_id *match;
int ret;
- cru->pad.flags = MEDIA_PAD_FL_SINK;
+ cru->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
ret = media_entity_pads_init(&cru->vdev.entity, 1, &cru->pad);
if (ret)
return ret;
@@ -241,10 +241,11 @@ static int rzg2l_cru_media_init(struct rzg2l_cru_dev *cru)
static int rzg2l_cru_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct rzg2l_cru_dev *cru;
- int ret;
+ int irq, ret;
- cru = devm_kzalloc(&pdev->dev, sizeof(*cru), GFP_KERNEL);
+ cru = devm_kzalloc(dev, sizeof(*cru), GFP_KERNEL);
if (!cru)
return -ENOMEM;
@@ -252,27 +253,32 @@ static int rzg2l_cru_probe(struct platform_device *pdev)
if (IS_ERR(cru->base))
return PTR_ERR(cru->base);
- cru->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn");
+ cru->presetn = devm_reset_control_get_shared(dev, "presetn");
if (IS_ERR(cru->presetn))
- return dev_err_probe(&pdev->dev, PTR_ERR(cru->presetn),
+ return dev_err_probe(dev, PTR_ERR(cru->presetn),
"Failed to get cpg presetn\n");
- cru->aresetn = devm_reset_control_get_exclusive(&pdev->dev, "aresetn");
+ cru->aresetn = devm_reset_control_get_exclusive(dev, "aresetn");
if (IS_ERR(cru->aresetn))
- return dev_err_probe(&pdev->dev, PTR_ERR(cru->aresetn),
+ return dev_err_probe(dev, PTR_ERR(cru->aresetn),
"Failed to get cpg aresetn\n");
- cru->vclk = devm_clk_get(&pdev->dev, "video");
+ cru->vclk = devm_clk_get(dev, "video");
if (IS_ERR(cru->vclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(cru->vclk),
+ return dev_err_probe(dev, PTR_ERR(cru->vclk),
"Failed to get video clock\n");
- cru->dev = &pdev->dev;
- cru->info = of_device_get_match_data(&pdev->dev);
+ cru->dev = dev;
+ cru->info = of_device_get_match_data(dev);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
- cru->image_conv_irq = platform_get_irq(pdev, 0);
- if (cru->image_conv_irq < 0)
- return cru->image_conv_irq;
+ ret = devm_request_irq(dev, irq, cru->info->irq_handler, 0,
+ KBUILD_MODNAME, cru);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to request irq\n");
platform_set_drvdata(pdev, cru);
@@ -281,8 +287,10 @@ static int rzg2l_cru_probe(struct platform_device *pdev)
return ret;
cru->num_buf = RZG2L_CRU_HW_BUFFER_DEFAULT;
- pm_suspend_ignore_children(&pdev->dev, true);
- pm_runtime_enable(&pdev->dev);
+ pm_suspend_ignore_children(dev, true);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ goto error_dma_unregister;
ret = rzg2l_cru_media_init(cru);
if (ret)
@@ -292,7 +300,6 @@ static int rzg2l_cru_probe(struct platform_device *pdev)
error_dma_unregister:
rzg2l_cru_dma_unregister(cru);
- pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -301,8 +308,6 @@ static void rzg2l_cru_remove(struct platform_device *pdev)
{
struct rzg2l_cru_dev *cru = platform_get_drvdata(pdev);
- pm_runtime_disable(&pdev->dev);
-
v4l2_async_nf_unregister(&cru->notifier);
v4l2_async_nf_cleanup(&cru->notifier);
@@ -313,8 +318,112 @@ static void rzg2l_cru_remove(struct platform_device *pdev)
rzg2l_cru_dma_unregister(cru);
}
+static const u16 rzg3e_cru_regs[] = {
+ [CRUnCTRL] = 0x0,
+ [CRUnIE] = 0x4,
+ [CRUnIE2] = 0x8,
+ [CRUnINTS] = 0xc,
+ [CRUnINTS2] = 0x10,
+ [CRUnRST] = 0x18,
+ [AMnMB1ADDRL] = 0x40,
+ [AMnMB1ADDRH] = 0x44,
+ [AMnMB2ADDRL] = 0x48,
+ [AMnMB2ADDRH] = 0x4c,
+ [AMnMB3ADDRL] = 0x50,
+ [AMnMB3ADDRH] = 0x54,
+ [AMnMB4ADDRL] = 0x58,
+ [AMnMB4ADDRH] = 0x5c,
+ [AMnMB5ADDRL] = 0x60,
+ [AMnMB5ADDRH] = 0x64,
+ [AMnMB6ADDRL] = 0x68,
+ [AMnMB6ADDRH] = 0x6c,
+ [AMnMB7ADDRL] = 0x70,
+ [AMnMB7ADDRH] = 0x74,
+ [AMnMB8ADDRL] = 0x78,
+ [AMnMB8ADDRH] = 0x7c,
+ [AMnMBVALID] = 0x88,
+ [AMnMADRSL] = 0x8c,
+ [AMnMADRSH] = 0x90,
+ [AMnAXIATTR] = 0xec,
+ [AMnFIFOPNTR] = 0xf8,
+ [AMnAXISTP] = 0x110,
+ [AMnAXISTPACK] = 0x114,
+ [AMnIS] = 0x128,
+ [ICnEN] = 0x1f0,
+ [ICnSVCNUM] = 0x1f8,
+ [ICnSVC] = 0x1fc,
+ [ICnIPMC_C0] = 0x200,
+ [ICnMS] = 0x2d8,
+ [ICnDMR] = 0x304,
+};
+
+static const struct rzg2l_cru_info rzg3e_cru_info = {
+ .max_width = 4095,
+ .max_height = 4095,
+ .image_conv = ICnIPMC_C0,
+ .has_stride = true,
+ .regs = rzg3e_cru_regs,
+ .irq_handler = rzg3e_cru_irq,
+ .enable_interrupts = rzg3e_cru_enable_interrupts,
+ .disable_interrupts = rzg3e_cru_disable_interrupts,
+ .fifo_empty = rz3e_fifo_empty,
+ .csi_setup = rzg3e_cru_csi2_setup,
+};
+
+static const u16 rzg2l_cru_regs[] = {
+ [CRUnCTRL] = 0x0,
+ [CRUnIE] = 0x4,
+ [CRUnINTS] = 0x8,
+ [CRUnRST] = 0xc,
+ [AMnMB1ADDRL] = 0x100,
+ [AMnMB1ADDRH] = 0x104,
+ [AMnMB2ADDRL] = 0x108,
+ [AMnMB2ADDRH] = 0x10c,
+ [AMnMB3ADDRL] = 0x110,
+ [AMnMB3ADDRH] = 0x114,
+ [AMnMB4ADDRL] = 0x118,
+ [AMnMB4ADDRH] = 0x11c,
+ [AMnMB5ADDRL] = 0x120,
+ [AMnMB5ADDRH] = 0x124,
+ [AMnMB6ADDRL] = 0x128,
+ [AMnMB6ADDRH] = 0x12c,
+ [AMnMB7ADDRL] = 0x130,
+ [AMnMB7ADDRH] = 0x134,
+ [AMnMB8ADDRL] = 0x138,
+ [AMnMB8ADDRH] = 0x13c,
+ [AMnMBVALID] = 0x148,
+ [AMnMBS] = 0x14c,
+ [AMnAXIATTR] = 0x158,
+ [AMnFIFOPNTR] = 0x168,
+ [AMnAXISTP] = 0x174,
+ [AMnAXISTPACK] = 0x178,
+ [ICnEN] = 0x200,
+ [ICnMC] = 0x208,
+ [ICnMS] = 0x254,
+ [ICnDMR] = 0x26c,
+};
+
+static const struct rzg2l_cru_info rzgl2_cru_info = {
+ .max_width = 2800,
+ .max_height = 4095,
+ .image_conv = ICnMC,
+ .regs = rzg2l_cru_regs,
+ .irq_handler = rzg2l_cru_irq,
+ .enable_interrupts = rzg2l_cru_enable_interrupts,
+ .disable_interrupts = rzg2l_cru_disable_interrupts,
+ .fifo_empty = rzg2l_fifo_empty,
+ .csi_setup = rzg2l_cru_csi2_setup,
+};
+
static const struct of_device_id rzg2l_cru_of_id_table[] = {
- { .compatible = "renesas,rzg2l-cru", },
+ {
+ .compatible = "renesas,r9a09g047-cru",
+ .data = &rzg3e_cru_info,
+ },
+ {
+ .compatible = "renesas,rzg2l-cru",
+ .data = &rzgl2_cru_info,
+ },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, rzg2l_cru_of_id_table);
@@ -325,7 +434,7 @@ static struct platform_driver rzg2l_cru_driver = {
.of_match_table = rzg2l_cru_of_id_table,
},
.probe = rzg2l_cru_probe,
- .remove_new = rzg2l_cru_remove,
+ .remove = rzg2l_cru_remove,
};
module_platform_driver(rzg2l_cru_driver);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
new file mode 100644
index 000000000000..a5a57369ef0e
--- /dev/null
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * rzg2l-cru-regs.h--RZ/G2L (and alike SoCs) CRU Registers Definitions
+ *
+ * Copyright (C) 2024 Renesas Electronics Corp.
+ */
+
+#ifndef __RZG2L_CRU_REGS_H__
+#define __RZG2L_CRU_REGS_H__
+
+/* HW CRU Registers Definition */
+
+#define CRUnCTRL_VINSEL(x) ((x) << 0)
+
+#define CRUnIE_EFE BIT(17)
+
+#define CRUnIE2_FSxE(x) BIT(((x) * 3))
+#define CRUnIE2_FExE(x) BIT(((x) * 3) + 1)
+
+#define CRUnINTS_SFS BIT(16)
+
+#define CRUnINTS2_FSxS(x) BIT(((x) * 3))
+
+#define CRUnRST_VRESETN BIT(0)
+
+/* Memory Bank Base Address (Lower) Register for CRU Image Data */
+#define AMnMBxADDRL(x) (AMnMB1ADDRL + (x) * 2)
+
+/* Memory Bank Base Address (Higher) Register for CRU Image Data */
+#define AMnMBxADDRH(x) (AMnMB1ADDRH + (x) * 2)
+
+#define AMnMBVALID_MBVALID(x) GENMASK(x, 0)
+
+#define AMnMBS_MBSTS 0x7
+
+#define AMnAXIATTR_AXILEN_MASK GENMASK(3, 0)
+#define AMnAXIATTR_AXILEN (0xf)
+
+#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0)
+#define AMnFIFOPNTR_FIFOWPNTR_B0 AMnFIFOPNTR_FIFOWPNTR
+#define AMnFIFOPNTR_FIFOWPNTR_B1 GENMASK(15, 8)
+#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16)
+#define AMnFIFOPNTR_FIFORPNTR_B0 AMnFIFOPNTR_FIFORPNTR_Y
+#define AMnFIFOPNTR_FIFORPNTR_B1 GENMASK(31, 24)
+
+#define AMnIS_IS_MASK GENMASK(14, 7)
+#define AMnIS_IS(x) ((x) << 7)
+
+#define AMnAXISTP_AXI_STOP BIT(0)
+
+#define AMnAXISTPACK_AXI_STOP_ACK BIT(0)
+
+#define ICnEN_ICEN BIT(0)
+
+#define ICnSVC_SVC0(x) (x)
+#define ICnSVC_SVC1(x) ((x) << 4)
+#define ICnSVC_SVC2(x) ((x) << 8)
+#define ICnSVC_SVC3(x) ((x) << 12)
+
+#define ICnMC_CSCTHR BIT(5)
+#define ICnMC_INF(x) ((x) << 16)
+#define ICnMC_VCSEL(x) ((x) << 22)
+#define ICnMC_INF_MASK GENMASK(21, 16)
+
+#define ICnMS_IA BIT(2)
+
+#define ICnDMR_YCMODE_UYVY (1 << 4)
+
+enum rzg2l_cru_common_regs {
+ CRUnCTRL, /* CRU Control */
+ CRUnIE, /* CRU Interrupt Enable */
+ CRUnIE2, /* CRU Interrupt Enable(2) */
+ CRUnINTS, /* CRU Interrupt Status */
+ CRUnINTS2, /* CRU Interrupt Status(2) */
+ CRUnRST, /* CRU Reset */
+ AMnMB1ADDRL, /* Bank 1 Address (Lower) for CRU Image Data */
+ AMnMB1ADDRH, /* Bank 1 Address (Higher) for CRU Image Data */
+ AMnMB2ADDRL, /* Bank 2 Address (Lower) for CRU Image Data */
+ AMnMB2ADDRH, /* Bank 2 Address (Higher) for CRU Image Data */
+ AMnMB3ADDRL, /* Bank 3 Address (Lower) for CRU Image Data */
+ AMnMB3ADDRH, /* Bank 3 Address (Higher) for CRU Image Data */
+ AMnMB4ADDRL, /* Bank 4 Address (Lower) for CRU Image Data */
+ AMnMB4ADDRH, /* Bank 4 Address (Higher) for CRU Image Data */
+ AMnMB5ADDRL, /* Bank 5 Address (Lower) for CRU Image Data */
+ AMnMB5ADDRH, /* Bank 5 Address (Higher) for CRU Image Data */
+ AMnMB6ADDRL, /* Bank 6 Address (Lower) for CRU Image Data */
+ AMnMB6ADDRH, /* Bank 6 Address (Higher) for CRU Image Data */
+ AMnMB7ADDRL, /* Bank 7 Address (Lower) for CRU Image Data */
+ AMnMB7ADDRH, /* Bank 7 Address (Higher) for CRU Image Data */
+ AMnMB8ADDRL, /* Bank 8 Address (Lower) for CRU Image Data */
+ AMnMB8ADDRH, /* Bank 8 Address (Higher) for CRU Image Data */
+ AMnMBVALID, /* Memory Bank Enable for CRU Image Data */
+ AMnMBS, /* Memory Bank Status for CRU Image Data */
+ AMnMADRSL, /* VD Memory Address Lower Status Register */
+ AMnMADRSH, /* VD Memory Address Higher Status Register */
+ AMnAXIATTR, /* AXI Master Transfer Setting Register for CRU Image Data */
+ AMnFIFOPNTR, /* AXI Master FIFO Pointer for CRU Image Data */
+ AMnAXISTP, /* AXI Master Transfer Stop for CRU Image Data */
+ AMnAXISTPACK, /* AXI Master Transfer Stop Status for CRU Image Data */
+ AMnIS, /* Image Stride Setting Register */
+ ICnEN, /* CRU Image Processing Enable */
+ ICnSVCNUM, /* CRU SVC Number Register */
+ ICnSVC, /* CRU VC Select Register */
+ ICnMC, /* CRU Image Processing Main Control */
+ ICnIPMC_C0, /* CRU Image Converter Main Control 0 */
+ ICnMS, /* CRU Module Status */
+ ICnDMR, /* CRU Data Output Mode */
+ RZG2L_CRU_MAX_REG,
+};
+
+#endif /* __RZG2L_CRU_REGS_H__ */
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
index a5a99b004322..c30f3b281284 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
@@ -8,6 +8,7 @@
#ifndef __RZG2L_CRU__
#define __RZG2L_CRU__
+#include <linux/irqreturn.h>
#include <linux/reset.h>
#include <media/v4l2-async.h>
@@ -26,9 +27,14 @@
#define RZG2L_CRU_CSI2_VCHANNEL 4
#define RZG2L_CRU_MIN_INPUT_WIDTH 320
-#define RZG2L_CRU_MAX_INPUT_WIDTH 2800
#define RZG2L_CRU_MIN_INPUT_HEIGHT 240
-#define RZG2L_CRU_MAX_INPUT_HEIGHT 4095
+
+enum rzg2l_csi2_pads {
+ RZG2L_CRU_IP_SINK = 0,
+ RZG2L_CRU_IP_SOURCE,
+};
+
+struct rzg2l_cru_dev;
/**
* enum rzg2l_cru_dma_state - DMA states
@@ -47,7 +53,6 @@ enum rzg2l_cru_dma_state {
struct rzg2l_cru_csi {
struct v4l2_async_connection *asd;
struct v4l2_subdev *subdev;
- u32 channel;
};
struct rzg2l_cru_ip {
@@ -58,6 +63,39 @@ struct rzg2l_cru_ip {
};
/**
+ * struct rzg2l_cru_ip_format - CRU IP format
+ * @code: Media bus code
+ * @datatype: MIPI CSI2 data type
+ * @format: 4CC format identifier (V4L2_PIX_FMT_*)
+ * @icndmr: ICnDMR register value
+ * @bpp: bytes per pixel
+ * @yuv: Flag to indicate whether the format is YUV-based.
+ */
+struct rzg2l_cru_ip_format {
+ u32 code;
+ u32 datatype;
+ u32 format;
+ u32 icndmr;
+ u8 bpp;
+ bool yuv;
+};
+
+struct rzg2l_cru_info {
+ unsigned int max_width;
+ unsigned int max_height;
+ u16 image_conv;
+ const u16 *regs;
+ bool has_stride;
+ irqreturn_t (*irq_handler)(int irq, void *data);
+ void (*enable_interrupts)(struct rzg2l_cru_dev *cru);
+ void (*disable_interrupts)(struct rzg2l_cru_dev *cru);
+ bool (*fifo_empty)(struct rzg2l_cru_dev *cru);
+ void (*csi_setup)(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc);
+};
+
+/**
* struct rzg2l_cru_dev - Renesas CRU device structure
* @dev: (OF) device
* @base: device I/O register space remapped to virtual memory
@@ -68,11 +106,11 @@ struct rzg2l_cru_ip {
*
* @vclk: CRU Main clock
*
- * @image_conv_irq: Holds image conversion interrupt number
- *
* @vdev: V4L2 video device associated with CRU
* @v4l2_dev: V4L2 device
* @num_buf: Holds the current number of buffers enabled
+ * @svc_channel: SVC0/1/2/3 to use for RZ/G3E
+ * @buf_addr: Memory addresses where current video data is written.
* @notifier: V4L2 asynchronous subdevs notifier
*
* @ip: Image processing subdev info
@@ -105,12 +143,13 @@ struct rzg2l_cru_dev {
struct clk *vclk;
- int image_conv_irq;
-
struct video_device vdev;
struct v4l2_device v4l2_dev;
u8 num_buf;
+ u8 svc_channel;
+ dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT];
+
struct v4l2_async_notifier notifier;
struct rzg2l_cru_ip ip;
@@ -141,6 +180,8 @@ void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru);
int rzg2l_cru_video_register(struct rzg2l_cru_dev *cru);
void rzg2l_cru_video_unregister(struct rzg2l_cru_dev *cru);
+irqreturn_t rzg2l_cru_irq(int irq, void *data);
+irqreturn_t rzg3e_cru_irq(int irq, void *data);
const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format);
@@ -148,4 +189,22 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru);
void rzg2l_cru_ip_subdev_unregister(struct rzg2l_cru_dev *cru);
struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru);
+const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code);
+const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format);
+const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index);
+
+void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru);
+void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru);
+void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru);
+void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru);
+
+bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru);
+bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru);
+void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc);
+void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc);
+
#endif
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
index e68fcdaea207..9243306e2aa9 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
@@ -85,6 +85,15 @@
CSIDPHYSKW0_UTIL_DL2_SKW_ADJ(1) | \
CSIDPHYSKW0_UTIL_DL3_SKW_ADJ(1))
+/* DPHY registers on RZ/V2H(P) SoC */
+#define CRUm_S_TIMCTL 0x41c
+#define CRUm_S_TIMCTL_S_HSSETTLECTL(x) ((x) << 8)
+
+#define CRUm_S_DPHYCTL_MSB 0x434
+#define CRUm_S_DPHYCTL_MSB_DESKEW BIT(1)
+
+#define CRUm_SWAPCTL 0x438
+
#define VSRSTS_RETRIES 20
#define RZG2L_CSI2_MIN_WIDTH 320
@@ -107,6 +116,7 @@ struct rzg2l_csi2 {
void __iomem *base;
struct reset_control *presetn;
struct reset_control *cmn_rstb;
+ const struct rzg2l_csi2_info *info;
struct clk *sysclk;
struct clk *vclk;
unsigned long vclk_rate;
@@ -123,6 +133,12 @@ struct rzg2l_csi2 {
bool dphy_enabled;
};
+struct rzg2l_csi2_info {
+ int (*dphy_enable)(struct rzg2l_csi2 *csi2);
+ int (*dphy_disable)(struct rzg2l_csi2 *csi2);
+ bool has_system_clk;
+};
+
struct rzg2l_csi2_timings {
u32 t_init;
u32 tclk_miss;
@@ -133,6 +149,30 @@ struct rzg2l_csi2_timings {
u32 max_hsfreq;
};
+struct rzv2h_csi2_s_hssettlectl {
+ unsigned int hsfreq;
+ u16 s_hssettlectl;
+};
+
+static const struct rzv2h_csi2_s_hssettlectl rzv2h_s_hssettlectl[] = {
+ { 90, 1 }, { 130, 2 }, { 180, 3 },
+ { 220, 4 }, { 270, 5 }, { 310, 6 },
+ { 360, 7 }, { 400, 8 }, { 450, 9 },
+ { 490, 10 }, { 540, 11 }, { 580, 12 },
+ { 630, 13 }, { 670, 14 }, { 720, 15 },
+ { 760, 16 }, { 810, 17 }, { 850, 18 },
+ { 900, 19 }, { 940, 20 }, { 990, 21 },
+ { 1030, 22 }, { 1080, 23 }, { 1120, 24 },
+ { 1170, 25 }, { 1220, 26 }, { 1260, 27 },
+ { 1310, 28 }, { 1350, 29 }, { 1400, 30 },
+ { 1440, 31 }, { 1490, 32 }, { 1530, 33 },
+ { 1580, 34 }, { 1620, 35 }, { 1670, 36 },
+ { 1710, 37 }, { 1760, 38 }, { 1800, 39 },
+ { 1850, 40 }, { 1890, 41 }, { 1940, 42 },
+ { 1980, 43 }, { 2030, 44 }, { 2070, 45 },
+ { 2100, 46 },
+};
+
static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = {
{
.max_hsfreq = 80,
@@ -183,12 +223,15 @@ static const struct rzg2l_csi2_timings rzg2l_csi2_global_timings[] = {
struct rzg2l_csi2_format {
u32 code;
- unsigned int datatype;
unsigned int bpp;
};
static const struct rzg2l_csi2_format rzg2l_csi2_formats[] = {
- { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_UYVY8_1X16, .bpp = 16 },
+ { .code = MEDIA_BUS_FMT_SBGGR8_1X8, .bpp = 8, },
+ { .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, },
+ { .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, },
+ { .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, },
};
static inline struct rzg2l_csi2 *sd_to_csi2(struct v4l2_subdev *sd)
@@ -352,14 +395,20 @@ static int rzg2l_csi2_dphy_enable(struct rzg2l_csi2 *csi2)
return ret;
}
+static const struct rzg2l_csi2_info rzg2l_csi2_info = {
+ .dphy_enable = rzg2l_csi2_dphy_enable,
+ .dphy_disable = rzg2l_csi2_dphy_disable,
+ .has_system_clk = true,
+};
+
static int rzg2l_csi2_dphy_setting(struct v4l2_subdev *sd, bool on)
{
struct rzg2l_csi2 *csi2 = sd_to_csi2(sd);
if (on)
- return rzg2l_csi2_dphy_enable(csi2);
+ return csi2->info->dphy_enable(csi2);
- return rzg2l_csi2_dphy_disable(csi2);
+ return csi2->info->dphy_disable(csi2);
}
static int rzg2l_csi2_mipi_link_enable(struct rzg2l_csi2 *csi2)
@@ -418,6 +467,64 @@ static int rzg2l_csi2_mipi_link_disable(struct rzg2l_csi2 *csi2)
return 0;
}
+static int rzv2h_csi2_dphy_disable(struct rzg2l_csi2 *csi2)
+{
+ int ret;
+
+ /* Reset the CRU (D-PHY) */
+ ret = reset_control_assert(csi2->cmn_rstb);
+ if (ret)
+ return ret;
+
+ csi2->dphy_enabled = false;
+
+ return 0;
+}
+
+static int rzv2h_csi2_dphy_enable(struct rzg2l_csi2 *csi2)
+{
+ unsigned int i;
+ u16 hssettle;
+ int mbps;
+
+ mbps = rzg2l_csi2_calc_mbps(csi2);
+ if (mbps < 0)
+ return mbps;
+
+ csi2->hsfreq = mbps;
+
+ for (i = 0; i < ARRAY_SIZE(rzv2h_s_hssettlectl); i++) {
+ if (csi2->hsfreq <= rzv2h_s_hssettlectl[i].hsfreq)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(rzv2h_s_hssettlectl))
+ return -EINVAL;
+
+ rzg2l_csi2_write(csi2, CRUm_SWAPCTL, 0);
+
+ hssettle = rzv2h_s_hssettlectl[i].s_hssettlectl;
+ rzg2l_csi2_write(csi2, CRUm_S_TIMCTL,
+ CRUm_S_TIMCTL_S_HSSETTLECTL(hssettle));
+
+ if (csi2->hsfreq > 1500)
+ rzg2l_csi2_set(csi2, CRUm_S_DPHYCTL_MSB,
+ CRUm_S_DPHYCTL_MSB_DESKEW);
+ else
+ rzg2l_csi2_clr(csi2, CRUm_S_DPHYCTL_MSB,
+ CRUm_S_DPHYCTL_MSB_DESKEW);
+
+ csi2->dphy_enabled = true;
+
+ return 0;
+}
+
+static const struct rzg2l_csi2_info rzv2h_csi2_info = {
+ .dphy_enable = rzv2h_csi2_dphy_enable,
+ .dphy_disable = rzv2h_csi2_dphy_disable,
+ .has_system_clk = false,
+};
+
static int rzg2l_csi2_mipi_link_setting(struct v4l2_subdev *sd, bool on)
{
struct rzg2l_csi2 *csi2 = sd_to_csi2(sd);
@@ -574,6 +681,9 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd,
if (fse->index != 0)
return -EINVAL;
+ if (!rzg2l_csi2_code_to_fmt(fse->code))
+ return -EINVAL;
+
fse->min_width = RZG2L_CSI2_MIN_WIDTH;
fse->min_height = RZG2L_CSI2_MIN_HEIGHT;
fse->max_width = RZG2L_CSI2_MAX_WIDTH;
@@ -582,6 +692,25 @@ static int rzg2l_csi2_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
+static int rzg2l_csi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct rzg2l_csi2 *csi2 = sd_to_csi2(sd);
+ struct media_pad *remote_pad;
+
+ if (!csi2->remote_source)
+ return -ENODEV;
+
+ remote_pad = media_pad_remote_pad_unique(&csi2->pads[RZG2L_CSI2_SINK]);
+ if (IS_ERR(remote_pad)) {
+ dev_err(csi2->dev, "can't get source pad of %s (%ld)\n",
+ csi2->remote_source->name, PTR_ERR(remote_pad));
+ return PTR_ERR(remote_pad);
+ }
+ return v4l2_subdev_call(csi2->remote_source, pad, get_frame_desc,
+ remote_pad->index, fd);
+}
+
static const struct v4l2_subdev_video_ops rzg2l_csi2_video_ops = {
.s_stream = rzg2l_csi2_s_stream,
.pre_streamon = rzg2l_csi2_pre_streamon,
@@ -593,6 +722,7 @@ static const struct v4l2_subdev_pad_ops rzg2l_csi2_pad_ops = {
.enum_frame_size = rzg2l_csi2_enum_frame_size,
.set_fmt = rzg2l_csi2_set_format,
.get_fmt = v4l2_subdev_get_fmt,
+ .get_frame_desc = rzg2l_csi2_get_frame_desc,
};
static const struct v4l2_subdev_ops rzg2l_csi2_subdev_ops = {
@@ -738,39 +868,46 @@ static const struct media_entity_operations rzg2l_csi2_entity_ops = {
static int rzg2l_csi2_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct rzg2l_csi2 *csi2;
int ret;
- csi2 = devm_kzalloc(&pdev->dev, sizeof(*csi2), GFP_KERNEL);
+ csi2 = devm_kzalloc(dev, sizeof(*csi2), GFP_KERNEL);
if (!csi2)
return -ENOMEM;
+ csi2->info = of_device_get_match_data(dev);
+ if (!csi2->info)
+ return dev_err_probe(dev, -EINVAL, "Failed to get OF match data\n");
+
csi2->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(csi2->base))
return PTR_ERR(csi2->base);
- csi2->cmn_rstb = devm_reset_control_get_exclusive(&pdev->dev, "cmn-rstb");
+ csi2->cmn_rstb = devm_reset_control_get_exclusive(dev, "cmn-rstb");
if (IS_ERR(csi2->cmn_rstb))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->cmn_rstb),
+ return dev_err_probe(dev, PTR_ERR(csi2->cmn_rstb),
"Failed to get cpg cmn-rstb\n");
- csi2->presetn = devm_reset_control_get_shared(&pdev->dev, "presetn");
+ csi2->presetn = devm_reset_control_get_shared(dev, "presetn");
if (IS_ERR(csi2->presetn))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->presetn),
+ return dev_err_probe(dev, PTR_ERR(csi2->presetn),
"Failed to get cpg presetn\n");
- csi2->sysclk = devm_clk_get(&pdev->dev, "system");
- if (IS_ERR(csi2->sysclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->sysclk),
- "Failed to get system clk\n");
+ if (csi2->info->has_system_clk) {
+ csi2->sysclk = devm_clk_get(dev, "system");
+ if (IS_ERR(csi2->sysclk))
+ return dev_err_probe(dev, PTR_ERR(csi2->sysclk),
+ "Failed to get system clk\n");
+ }
- csi2->vclk = devm_clk_get(&pdev->dev, "video");
+ csi2->vclk = devm_clk_get(dev, "video");
if (IS_ERR(csi2->vclk))
- return dev_err_probe(&pdev->dev, PTR_ERR(csi2->vclk),
+ return dev_err_probe(dev, PTR_ERR(csi2->vclk),
"Failed to get video clock\n");
csi2->vclk_rate = clk_get_rate(csi2->vclk);
- csi2->dev = &pdev->dev;
+ csi2->dev = dev;
platform_set_drvdata(pdev, csi2);
@@ -778,33 +915,38 @@ static int rzg2l_csi2_probe(struct platform_device *pdev)
if (ret)
return ret;
- pm_runtime_enable(&pdev->dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
ret = rzg2l_validate_csi2_lanes(csi2);
if (ret)
- goto error_pm;
+ return ret;
- csi2->subdev.dev = &pdev->dev;
+ csi2->subdev.dev = dev;
v4l2_subdev_init(&csi2->subdev, &rzg2l_csi2_subdev_ops);
csi2->subdev.internal_ops = &rzg2l_csi2_internal_ops;
- v4l2_set_subdevdata(&csi2->subdev, &pdev->dev);
+ v4l2_set_subdevdata(&csi2->subdev, dev);
snprintf(csi2->subdev.name, sizeof(csi2->subdev.name),
- "csi-%s", dev_name(&pdev->dev));
+ "csi-%s", dev_name(dev));
csi2->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
csi2->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
csi2->subdev.entity.ops = &rzg2l_csi2_entity_ops;
- csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK;
+ csi2->pads[RZG2L_CSI2_SINK].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
/*
* TODO: RZ/G2L CSI2 supports 4 virtual channels, as virtual
* channels should be implemented by streams API which is under
* development lets hardcode to VC0 for now.
*/
- csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
- ret = media_entity_pads_init(&csi2->subdev.entity, 2, csi2->pads);
+ csi2->pads[RZG2L_CSI2_SOURCE].flags = MEDIA_PAD_FL_SOURCE |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ ret = media_entity_pads_init(&csi2->subdev.entity, ARRAY_SIZE(csi2->pads),
+ csi2->pads);
if (ret)
- goto error_pm;
+ return ret;
ret = v4l2_subdev_init_finalize(&csi2->subdev);
if (ret < 0)
@@ -822,8 +964,6 @@ error_async:
v4l2_async_nf_unregister(&csi2->notifier);
v4l2_async_nf_cleanup(&csi2->notifier);
media_entity_cleanup(&csi2->subdev.entity);
-error_pm:
- pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -837,7 +977,6 @@ static void rzg2l_csi2_remove(struct platform_device *pdev)
v4l2_async_unregister_subdev(&csi2->subdev);
v4l2_subdev_cleanup(&csi2->subdev);
media_entity_cleanup(&csi2->subdev.entity);
- pm_runtime_disable(&pdev->dev);
}
static int rzg2l_csi2_pm_runtime_suspend(struct device *dev)
@@ -862,12 +1001,20 @@ static const struct dev_pm_ops rzg2l_csi2_pm_ops = {
};
static const struct of_device_id rzg2l_csi2_of_table[] = {
- { .compatible = "renesas,rzg2l-csi2", },
+ {
+ .compatible = "renesas,r9a09g057-csi2",
+ .data = &rzv2h_csi2_info,
+ },
+ {
+ .compatible = "renesas,rzg2l-csi2",
+ .data = &rzg2l_csi2_info,
+ },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, rzg2l_csi2_of_table);
static struct platform_driver rzg2l_csi2_pdrv = {
- .remove_new = rzg2l_csi2_remove,
+ .remove = rzg2l_csi2_remove,
.probe = rzg2l_csi2_probe,
.driver = {
.name = "rzg2l-csi2",
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
index ac8ebae4ed07..7836c7cd53dc 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
@@ -6,24 +6,55 @@
*/
#include <linux/delay.h>
-#include "rzg2l-cru.h"
+#include <media/mipi-csi2.h>
-struct rzg2l_cru_ip_format {
- u32 code;
- unsigned int datatype;
- unsigned int bpp;
-};
+#include "rzg2l-cru.h"
+#include "rzg2l-cru-regs.h"
static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = {
- { .code = MEDIA_BUS_FMT_UYVY8_1X16, .datatype = 0x1e, .bpp = 16 },
-};
-
-enum rzg2l_csi2_pads {
- RZG2L_CRU_IP_SINK = 0,
- RZG2L_CRU_IP_SOURCE,
+ {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .datatype = MIPI_CSI2_DT_YUV422_8B,
+ .format = V4L2_PIX_FMT_UYVY,
+ .bpp = 2,
+ .icndmr = ICnDMR_YCMODE_UYVY,
+ .yuv = true,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .format = V4L2_PIX_FMT_SBGGR8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 1,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .format = V4L2_PIX_FMT_SGBRG8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 1,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .format = V4L2_PIX_FMT_SGRBG8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 1,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .format = V4L2_PIX_FMT_SRGGB8,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .bpp = 1,
+ .icndmr = 0,
+ .yuv = false,
+ },
};
-static const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code)
+const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code)
{
unsigned int i;
@@ -34,6 +65,26 @@ static const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int c
return NULL;
}
+const struct rzg2l_cru_ip_format *rzg2l_cru_ip_format_to_fmt(u32 format)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
+ if (rzg2l_cru_ip_formats[i].format == format)
+ return &rzg2l_cru_ip_formats[i];
+ }
+
+ return NULL;
+}
+
+const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index)
+{
+ if (index >= ARRAY_SIZE(rzg2l_cru_ip_formats))
+ return NULL;
+
+ return &rzg2l_cru_ip_formats[index];
+}
+
struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru)
{
struct v4l2_subdev_state *state;
@@ -97,6 +148,8 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
+ struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd);
+ const struct rzg2l_cru_info *info = cru->info;
struct v4l2_mbus_framefmt *src_format;
struct v4l2_mbus_framefmt *sink_format;
@@ -119,9 +172,9 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
sink_format->ycbcr_enc = fmt->format.ycbcr_enc;
sink_format->quantization = fmt->format.quantization;
sink_format->width = clamp_t(u32, fmt->format.width,
- RZG2L_CRU_MIN_INPUT_WIDTH, RZG2L_CRU_MAX_INPUT_WIDTH);
+ RZG2L_CRU_MIN_INPUT_WIDTH, info->max_width);
sink_format->height = clamp_t(u32, fmt->format.height,
- RZG2L_CRU_MIN_INPUT_HEIGHT, RZG2L_CRU_MAX_INPUT_HEIGHT);
+ RZG2L_CRU_MIN_INPUT_HEIGHT, info->max_height);
fmt->format = *sink_format;
@@ -146,16 +199,19 @@ static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_size_enum *fse)
{
+ struct rzg2l_cru_dev *cru = v4l2_get_subdevdata(sd);
+ const struct rzg2l_cru_info *info = cru->info;
+
if (fse->index != 0)
return -EINVAL;
- if (fse->code != MEDIA_BUS_FMT_UYVY8_1X16)
+ if (!rzg2l_cru_ip_code_to_fmt(fse->code))
return -EINVAL;
fse->min_width = RZG2L_CRU_MIN_INPUT_WIDTH;
fse->min_height = RZG2L_CRU_MIN_INPUT_HEIGHT;
- fse->max_width = RZG2L_CRU_MAX_INPUT_WIDTH;
- fse->max_height = RZG2L_CRU_MAX_INPUT_HEIGHT;
+ fse->max_width = info->max_width;
+ fse->max_height = info->max_height;
return 0;
}
@@ -217,8 +273,10 @@ int rzg2l_cru_ip_subdev_register(struct rzg2l_cru_dev *cru)
ip->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
ip->subdev.entity.ops = &rzg2l_cru_ip_entity_ops;
- ip->pads[0].flags = MEDIA_PAD_FL_SINK;
- ip->pads[1].flags = MEDIA_PAD_FL_SOURCE;
+ ip->pads[RZG2L_CRU_IP_SINK].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+ ip->pads[RZG2L_CRU_IP_SOURCE].flags = MEDIA_PAD_FL_SOURCE |
+ MEDIA_PAD_FL_MUST_CONNECT;
ret = media_entity_pads_init(&ip->subdev.entity, 2, ip->pads);
if (ret)
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index b16b8af6e8f8..067c6af14e95 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -15,75 +15,12 @@
#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
#include "rzg2l-cru.h"
-
-/* HW CRU Registers Definition */
-
-/* CRU Control Register */
-#define CRUnCTRL 0x0
-#define CRUnCTRL_VINSEL(x) ((x) << 0)
-
-/* CRU Interrupt Enable Register */
-#define CRUnIE 0x4
-#define CRUnIE_EFE BIT(17)
-
-/* CRU Interrupt Status Register */
-#define CRUnINTS 0x8
-#define CRUnINTS_SFS BIT(16)
-
-/* CRU Reset Register */
-#define CRUnRST 0xc
-#define CRUnRST_VRESETN BIT(0)
-
-/* Memory Bank Base Address (Lower) Register for CRU Image Data */
-#define AMnMBxADDRL(x) (0x100 + ((x) * 8))
-
-/* Memory Bank Base Address (Higher) Register for CRU Image Data */
-#define AMnMBxADDRH(x) (0x104 + ((x) * 8))
-
-/* Memory Bank Enable Register for CRU Image Data */
-#define AMnMBVALID 0x148
-#define AMnMBVALID_MBVALID(x) GENMASK(x, 0)
-
-/* Memory Bank Status Register for CRU Image Data */
-#define AMnMBS 0x14c
-#define AMnMBS_MBSTS 0x7
-
-/* AXI Master FIFO Pointer Register for CRU Image Data */
-#define AMnFIFOPNTR 0x168
-#define AMnFIFOPNTR_FIFOWPNTR GENMASK(7, 0)
-#define AMnFIFOPNTR_FIFORPNTR_Y GENMASK(23, 16)
-
-/* AXI Master Transfer Stop Register for CRU Image Data */
-#define AMnAXISTP 0x174
-#define AMnAXISTP_AXI_STOP BIT(0)
-
-/* AXI Master Transfer Stop Status Register for CRU Image Data */
-#define AMnAXISTPACK 0x178
-#define AMnAXISTPACK_AXI_STOP_ACK BIT(0)
-
-/* CRU Image Processing Enable Register */
-#define ICnEN 0x200
-#define ICnEN_ICEN BIT(0)
-
-/* CRU Image Processing Main Control Register */
-#define ICnMC 0x208
-#define ICnMC_CSCTHR BIT(5)
-#define ICnMC_INF_YUV8_422 (0x1e << 16)
-#define ICnMC_INF_USER (0x30 << 16)
-#define ICnMC_VCSEL(x) ((x) << 22)
-#define ICnMC_INF_MASK GENMASK(21, 16)
-
-/* CRU Module Status Register */
-#define ICnMS 0x254
-#define ICnMS_IA BIT(2)
-
-/* CRU Data Output Mode Register */
-#define ICnDMR 0x26c
-#define ICnDMR_YCMODE_UYVY (1 << 4)
+#include "rzg2l-cru-regs.h"
#define RZG2L_TIMEOUT_MS 100
#define RZG2L_RETRIES 10
@@ -94,6 +31,9 @@
#define RZG2L_CRU_DEFAULT_FIELD V4L2_FIELD_NONE
#define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB
+#define RZG2L_CRU_STRIDE_MAX 32640
+#define RZG2L_CRU_STRIDE_ALIGN 128
+
struct rzg2l_cru_buffer {
struct vb2_v4l2_buffer vb;
struct list_head list;
@@ -105,16 +45,66 @@ struct rzg2l_cru_buffer {
/* -----------------------------------------------------------------------------
* DMA operations
*/
-static void rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value)
+static void __rzg2l_cru_write(struct rzg2l_cru_dev *cru, u32 offset, u32 value)
{
- iowrite32(value, cru->base + offset);
+ const u16 *regs = cru->info->regs;
+
+ /*
+ * CRUnCTRL is a first register on all CRU supported SoCs so validate
+ * rest of the registers have valid offset being set in cru->info->regs.
+ */
+ if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) ||
+ WARN_ON(offset != CRUnCTRL && regs[offset] == 0))
+ return;
+
+ iowrite32(value, cru->base + regs[offset]);
}
-static u32 rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset)
+static u32 __rzg2l_cru_read(struct rzg2l_cru_dev *cru, u32 offset)
{
- return ioread32(cru->base + offset);
+ const u16 *regs = cru->info->regs;
+
+ /*
+ * CRUnCTRL is a first register on all CRU supported SoCs so validate
+ * rest of the registers have valid offset being set in cru->info->regs.
+ */
+ if (WARN_ON(offset >= RZG2L_CRU_MAX_REG) ||
+ WARN_ON(offset != CRUnCTRL && regs[offset] == 0))
+ return 0;
+
+ return ioread32(cru->base + regs[offset]);
}
+static __always_inline void
+__rzg2l_cru_write_constant(struct rzg2l_cru_dev *cru, u32 offset, u32 value)
+{
+ const u16 *regs = cru->info->regs;
+
+ BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG);
+
+ iowrite32(value, cru->base + regs[offset]);
+}
+
+static __always_inline u32
+__rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset)
+{
+ const u16 *regs = cru->info->regs;
+
+ BUILD_BUG_ON(offset >= RZG2L_CRU_MAX_REG);
+
+ return ioread32(cru->base + regs[offset]);
+}
+
+#define rzg2l_cru_write(cru, offset, value) \
+ (__builtin_constant_p(offset) ? \
+ __rzg2l_cru_write_constant(cru, offset, value) : \
+ __rzg2l_cru_write(cru, offset, value))
+
+#define rzg2l_cru_read(cru, offset) \
+ (__builtin_constant_p(offset) ? \
+ __rzg2l_cru_read_constant(cru, offset) : \
+ __rzg2l_cru_read(cru, offset))
+
/* Need to hold qlock before calling */
static void return_unused_buffers(struct rzg2l_cru_dev *cru,
enum vb2_buffer_state state)
@@ -184,46 +174,6 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&cru->qlock, flags);
}
-static int rzg2l_cru_mc_validate_format(struct rzg2l_cru_dev *cru,
- struct v4l2_subdev *sd,
- struct media_pad *pad)
-{
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
-
- fmt.pad = pad->index;
- if (v4l2_subdev_call_state_active(sd, pad, get_fmt, &fmt))
- return -EPIPE;
-
- switch (fmt.format.code) {
- case MEDIA_BUS_FMT_UYVY8_1X16:
- break;
- default:
- return -EPIPE;
- }
-
- switch (fmt.format.field) {
- case V4L2_FIELD_TOP:
- case V4L2_FIELD_BOTTOM:
- case V4L2_FIELD_NONE:
- case V4L2_FIELD_INTERLACED_TB:
- case V4L2_FIELD_INTERLACED_BT:
- case V4L2_FIELD_INTERLACED:
- case V4L2_FIELD_SEQ_TB:
- case V4L2_FIELD_SEQ_BT:
- break;
- default:
- return -EPIPE;
- }
-
- if (fmt.format.width != cru->format.width ||
- fmt.format.height != cru->format.height)
- return -EPIPE;
-
- return 0;
-}
-
static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru,
int slot, dma_addr_t addr)
{
@@ -237,6 +187,8 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru,
/* Currently, we just use the buffer in 32 bits address */
rzg2l_cru_write(cru, AMnMBxADDRL(slot), addr);
rzg2l_cru_write(cru, AMnMBxADDRH(slot), 0);
+
+ cru->buf_addr[slot] = addr;
}
/*
@@ -277,7 +229,9 @@ static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot)
static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru)
{
+ const struct rzg2l_cru_info *info = cru->info;
unsigned int slot;
+ u32 amnaxiattr;
/*
* Set image data memory banks.
@@ -287,70 +241,118 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru)
for (slot = 0; slot < cru->num_buf; slot++)
rzg2l_cru_fill_hw_slot(cru, slot);
+
+ if (info->has_stride) {
+ u32 stride = cru->format.bytesperline;
+ u32 amnis;
+
+ stride /= RZG2L_CRU_STRIDE_ALIGN;
+ amnis = rzg2l_cru_read(cru, AMnIS) & ~AMnIS_IS_MASK;
+ rzg2l_cru_write(cru, AMnIS, amnis | AMnIS_IS(stride));
+ }
+
+ /* Set AXI burst max length to recommended setting */
+ amnaxiattr = rzg2l_cru_read(cru, AMnAXIATTR) & ~AMnAXIATTR_AXILEN_MASK;
+ amnaxiattr |= AMnAXIATTR_AXILEN;
+ rzg2l_cru_write(cru, AMnAXIATTR, amnaxiattr);
}
-static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru, bool *input_is_yuv,
- struct v4l2_mbus_framefmt *ip_sd_fmt)
+void rzg3e_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc)
{
- u32 icnmc;
+ const struct rzg2l_cru_info *info = cru->info;
+ u32 icnmc = ICnMC_INF(ip_fmt->datatype);
- switch (ip_sd_fmt->code) {
- case MEDIA_BUS_FMT_UYVY8_1X16:
- icnmc = ICnMC_INF_YUV8_422;
- *input_is_yuv = true;
- break;
- default:
- *input_is_yuv = false;
- icnmc = ICnMC_INF_USER;
- break;
- }
+ icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK;
+
+ /* Set virtual channel CSI2 */
+ icnmc |= ICnMC_VCSEL(csi_vc);
+
+ rzg2l_cru_write(cru, ICnSVCNUM, csi_vc);
+ rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) |
+ ICnSVC_SVC2(2) | ICnSVC_SVC3(3));
+ rzg2l_cru_write(cru, info->image_conv, icnmc);
+}
+
+void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
+ const struct rzg2l_cru_ip_format *ip_fmt,
+ u8 csi_vc)
+{
+ const struct rzg2l_cru_info *info = cru->info;
+ u32 icnmc = ICnMC_INF(ip_fmt->datatype);
- icnmc |= (rzg2l_cru_read(cru, ICnMC) & ~ICnMC_INF_MASK);
+ icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK;
/* Set virtual channel CSI2 */
- icnmc |= ICnMC_VCSEL(cru->csi.channel);
+ icnmc |= ICnMC_VCSEL(csi_vc);
- rzg2l_cru_write(cru, ICnMC, icnmc);
+ rzg2l_cru_write(cru, info->image_conv, icnmc);
}
static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru,
- struct v4l2_mbus_framefmt *ip_sd_fmt)
+ struct v4l2_mbus_framefmt *ip_sd_fmt,
+ u8 csi_vc)
{
- bool output_is_yuv = false;
- bool input_is_yuv = false;
- u32 icndmr;
+ const struct rzg2l_cru_info *info = cru->info;
+ const struct rzg2l_cru_ip_format *cru_video_fmt;
+ const struct rzg2l_cru_ip_format *cru_ip_fmt;
- rzg2l_cru_csi2_setup(cru, &input_is_yuv, ip_sd_fmt);
+ cru_ip_fmt = rzg2l_cru_ip_code_to_fmt(ip_sd_fmt->code);
+ info->csi_setup(cru, cru_ip_fmt, csi_vc);
/* Output format */
- switch (cru->format.pixelformat) {
- case V4L2_PIX_FMT_UYVY:
- icndmr = ICnDMR_YCMODE_UYVY;
- output_is_yuv = true;
- break;
- default:
+ cru_video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat);
+ if (!cru_video_fmt) {
dev_err(cru->dev, "Invalid pixelformat (0x%x)\n",
cru->format.pixelformat);
return -EINVAL;
}
/* If input and output use same colorspace, do bypass mode */
- if (output_is_yuv == input_is_yuv)
- rzg2l_cru_write(cru, ICnMC,
- rzg2l_cru_read(cru, ICnMC) | ICnMC_CSCTHR);
+ if (cru_ip_fmt->yuv == cru_video_fmt->yuv)
+ rzg2l_cru_write(cru, info->image_conv,
+ rzg2l_cru_read(cru, info->image_conv) | ICnMC_CSCTHR);
else
- rzg2l_cru_write(cru, ICnMC,
- rzg2l_cru_read(cru, ICnMC) & (~ICnMC_CSCTHR));
+ rzg2l_cru_write(cru, info->image_conv,
+ rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_CSCTHR);
/* Set output data format */
- rzg2l_cru_write(cru, ICnDMR, icndmr);
+ rzg2l_cru_write(cru, ICnDMR, cru_video_fmt->icndmr);
return 0;
}
-void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
+bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru)
+{
+ u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
+
+ if ((((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B1) >> 24) ==
+ ((amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B1) >> 8)) &&
+ (((amnfifopntr & AMnFIFOPNTR_FIFORPNTR_B0) >> 16) ==
+ (amnfifopntr & AMnFIFOPNTR_FIFOWPNTR_B0)))
+ return true;
+
+ return false;
+}
+
+bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru)
{
u32 amnfifopntr, amnfifopntr_w, amnfifopntr_r_y;
+
+ amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
+
+ amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR;
+ amnfifopntr_r_y =
+ (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16;
+ if (amnfifopntr_w == amnfifopntr_r_y)
+ return true;
+
+ return amnfifopntr_w == amnfifopntr_r_y;
+}
+
+void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
+{
unsigned int retries = 0;
unsigned long flags;
u32 icnms;
@@ -358,8 +360,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
spin_lock_irqsave(&cru->qlock, flags);
/* Disable and clear the interrupt */
- rzg2l_cru_write(cru, CRUnIE, 0);
- rzg2l_cru_write(cru, CRUnINTS, 0x001F0F0F);
+ cru->info->disable_interrupts(cru);
/* Stop the operation of image conversion */
rzg2l_cru_write(cru, ICnEN, 0);
@@ -379,12 +380,7 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
/* Wait until the FIFO becomes empty */
for (retries = 5; retries > 0; retries--) {
- amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
-
- amnfifopntr_w = amnfifopntr & AMnFIFOPNTR_FIFOWPNTR;
- amnfifopntr_r_y =
- (amnfifopntr & AMnFIFOPNTR_FIFORPNTR_Y) >> 16;
- if (amnfifopntr_w == amnfifopntr_r_y)
+ if (cru->info->fifo_empty(cru))
break;
usleep_range(10, 20);
@@ -422,12 +418,73 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
spin_unlock_irqrestore(&cru->qlock, flags);
}
+static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru)
+{
+ struct v4l2_mbus_frame_desc fd = { };
+ struct media_pad *remote_pad;
+ int ret;
+
+ remote_pad = media_pad_remote_pad_unique(&cru->ip.pads[RZG2L_CRU_IP_SINK]);
+ ret = v4l2_subdev_call(cru->ip.remote, pad, get_frame_desc, remote_pad->index, &fd);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_err(cru->dev, "get_frame_desc failed on IP remote subdev\n");
+ return ret;
+ }
+ /* If remote subdev does not implement .get_frame_desc default to VC0. */
+ if (ret == -ENOIOCTLCMD)
+ return 0;
+
+ if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ dev_err(cru->dev, "get_frame_desc returned invalid bus type %d\n", fd.type);
+ return -EINVAL;
+ }
+
+ if (!fd.num_entries) {
+ dev_err(cru->dev, "get_frame_desc returned zero entries\n");
+ return -EINVAL;
+ }
+
+ return fd.entry[0].bus.csi2.vc;
+}
+
+void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel));
+ rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel));
+}
+
+void rzg3e_cru_disable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE, 0);
+ rzg2l_cru_write(cru, CRUnIE2, 0);
+ rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS));
+ rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2));
+}
+
+void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE);
+}
+
+void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru)
+{
+ rzg2l_cru_write(cru, CRUnIE, 0);
+ rzg2l_cru_write(cru, CRUnINTS, 0x001f000f);
+}
+
int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
{
struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru);
unsigned long flags;
+ u8 csi_vc;
int ret;
+ ret = rzg2l_cru_get_virtual_channel(cru);
+ if (ret < 0)
+ return ret;
+ csi_vc = ret;
+ cru->svc_channel = csi_vc;
+
spin_lock_irqsave(&cru->qlock, flags);
/* Select a video input */
@@ -437,21 +494,20 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
rzg2l_cru_write(cru, CRUnRST, CRUnRST_VRESETN);
/* Disable and clear the interrupt before using */
- rzg2l_cru_write(cru, CRUnIE, 0);
- rzg2l_cru_write(cru, CRUnINTS, 0x001f000f);
+ cru->info->disable_interrupts(cru);
/* Initialize the AXI master */
rzg2l_cru_initialize_axi(cru);
/* Initialize image convert */
- ret = rzg2l_cru_initialize_image_conv(cru, fmt);
+ ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc);
if (ret) {
spin_unlock_irqrestore(&cru->qlock, flags);
return ret;
}
/* Enable interrupt */
- rzg2l_cru_write(cru, CRUnIE, CRUnIE_EFE);
+ cru->info->enable_interrupts(cru);
/* Enable image processing reception */
rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN);
@@ -492,10 +548,6 @@ static int rzg2l_cru_set_stream(struct rzg2l_cru_dev *cru, int on)
return stream_off_ret;
}
- ret = rzg2l_cru_mc_validate_format(cru, sd, pad);
- if (ret)
- return ret;
-
pipe = media_entity_pipeline(&sd->entity) ? : &cru->vdev.pipe;
ret = video_device_pipeline_start(&cru->vdev, pipe);
if (ret)
@@ -527,7 +579,7 @@ static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru)
rzg2l_cru_set_stream(cru, 0);
}
-static irqreturn_t rzg2l_cru_irq(int irq, void *data)
+irqreturn_t rzg2l_cru_irq(int irq, void *data)
{
struct rzg2l_cru_dev *cru = data;
unsigned int handled = 0;
@@ -610,6 +662,104 @@ done:
return IRQ_RETVAL(handled);
}
+static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru)
+{
+ u64 amnmadrs;
+ int slot;
+
+ /*
+ * When AMnMADRSL is read, AMnMADRSH of the higher-order
+ * address also latches the address.
+ *
+ * AMnMADRSH must be read after AMnMADRSL has been read.
+ */
+ amnmadrs = rzg2l_cru_read(cru, AMnMADRSL);
+ amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32;
+
+ /* Ensure amnmadrs is within this buffer range */
+ for (slot = 0; slot < cru->num_buf; slot++) {
+ if (amnmadrs >= cru->buf_addr[slot] &&
+ amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage)
+ return slot;
+ }
+
+ dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs);
+ return -EINVAL;
+}
+
+irqreturn_t rzg3e_cru_irq(int irq, void *data)
+{
+ struct rzg2l_cru_dev *cru = data;
+ u32 irq_status;
+ int slot;
+
+ scoped_guard(spinlock, &cru->qlock) {
+ irq_status = rzg2l_cru_read(cru, CRUnINTS2);
+ if (!irq_status)
+ return IRQ_NONE;
+
+ dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status);
+
+ rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2));
+
+ /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */
+ if (cru->state == RZG2L_CRU_DMA_STOPPED) {
+ dev_dbg(cru->dev, "IRQ while state stopped\n");
+ return IRQ_HANDLED;
+ }
+
+ if (cru->state == RZG2L_CRU_DMA_STOPPING) {
+ if (irq_status & CRUnINTS2_FSxS(0) ||
+ irq_status & CRUnINTS2_FSxS(1) ||
+ irq_status & CRUnINTS2_FSxS(2) ||
+ irq_status & CRUnINTS2_FSxS(3))
+ dev_dbg(cru->dev, "IRQ while state stopping\n");
+ return IRQ_HANDLED;
+ }
+
+ slot = rzg3e_cru_get_current_slot(cru);
+ if (slot < 0)
+ return IRQ_HANDLED;
+
+ dev_dbg(cru->dev, "Current written slot: %d\n", slot);
+ cru->buf_addr[slot] = 0;
+
+ /*
+ * To hand buffers back in a known order to userspace start
+ * to capture first from slot 0.
+ */
+ if (cru->state == RZG2L_CRU_DMA_STARTING) {
+ if (slot != 0) {
+ dev_dbg(cru->dev, "Starting sync slot: %d\n", slot);
+ return IRQ_HANDLED;
+ }
+ dev_dbg(cru->dev, "Capture start synced!\n");
+ cru->state = RZG2L_CRU_DMA_RUNNING;
+ }
+
+ /* Capture frame */
+ if (cru->queue_buf[slot]) {
+ struct vb2_v4l2_buffer *buf = cru->queue_buf[slot];
+
+ buf->field = cru->format.field;
+ buf->sequence = cru->sequence;
+ buf->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE);
+ cru->queue_buf[slot] = NULL;
+ } else {
+ /* Scratch buffer was used, dropping frame. */
+ dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence);
+ }
+
+ cru->sequence++;
+
+ /* Prepare for next frame */
+ rzg2l_cru_fill_hw_slot(cru, slot);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count)
{
struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vq);
@@ -637,21 +787,14 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count
goto assert_aresetn;
}
- ret = request_irq(cru->image_conv_irq, rzg2l_cru_irq,
- IRQF_SHARED, KBUILD_MODNAME, cru);
- if (ret) {
- dev_err(cru->dev, "failed to request irq\n");
- goto assert_presetn;
- }
-
- /* Allocate scratch buffer. */
+ /* Allocate scratch buffer */
cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage,
&cru->scratch_phys, GFP_KERNEL);
if (!cru->scratch) {
return_unused_buffers(cru, VB2_BUF_STATE_QUEUED);
dev_err(cru->dev, "Failed to allocate scratch buffer\n");
ret = -ENOMEM;
- goto free_image_conv_irq;
+ goto assert_presetn;
}
cru->sequence = 0;
@@ -670,9 +813,6 @@ out:
if (ret)
dma_free_coherent(cru->dev, cru->format.sizeimage, cru->scratch,
cru->scratch_phys);
-free_image_conv_irq:
- free_irq(cru->image_conv_irq, cru);
-
assert_presetn:
reset_control_assert(cru->presetn);
@@ -698,7 +838,6 @@ static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq)
dma_free_coherent(cru->dev, cru->format.sizeimage,
cru->scratch, cru->scratch_phys);
- free_irq(cru->image_conv_irq, cru);
return_unused_buffers(cru, VB2_BUF_STATE_ERROR);
reset_control_assert(cru->presetn);
@@ -712,8 +851,6 @@ static const struct vb2_ops rzg2l_cru_qops = {
.buf_queue = rzg2l_cru_buffer_queue,
.start_streaming = rzg2l_cru_start_streaming_vq,
.stop_streaming = rzg2l_cru_stop_streaming_vq,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
void rzg2l_cru_dma_unregister(struct rzg2l_cru_dev *cru)
@@ -775,46 +912,17 @@ error:
* V4L2 stuff
*/
-static const struct v4l2_format_info rzg2l_cru_formats[] = {
- {
- .format = V4L2_PIX_FMT_UYVY,
- .bpp[0] = 2,
- },
-};
-
-const struct v4l2_format_info *rzg2l_cru_format_from_pixel(u32 format)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(rzg2l_cru_formats); i++)
- if (rzg2l_cru_formats[i].format == format)
- return rzg2l_cru_formats + i;
-
- return NULL;
-}
-
-static u32 rzg2l_cru_format_bytesperline(struct v4l2_pix_format *pix)
-{
- const struct v4l2_format_info *fmt;
-
- fmt = rzg2l_cru_format_from_pixel(pix->pixelformat);
-
- if (WARN_ON(!fmt))
- return -EINVAL;
-
- return pix->width * fmt->bpp[0];
-}
-
-static u32 rzg2l_cru_format_sizeimage(struct v4l2_pix_format *pix)
-{
- return pix->bytesperline * pix->height;
-}
-
static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
struct v4l2_pix_format *pix)
{
- if (!rzg2l_cru_format_from_pixel(pix->pixelformat))
+ const struct rzg2l_cru_info *info = cru->info;
+ const struct rzg2l_cru_ip_format *fmt;
+
+ fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat);
+ if (!fmt) {
pix->pixelformat = RZG2L_CRU_DEFAULT_FORMAT;
+ fmt = rzg2l_cru_ip_format_to_fmt(pix->pixelformat);
+ }
switch (pix->field) {
case V4L2_FIELD_TOP:
@@ -830,11 +938,18 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
}
/* Limit to CRU capabilities */
- v4l_bound_align_image(&pix->width, 320, RZG2L_CRU_MAX_INPUT_WIDTH, 1,
- &pix->height, 240, RZG2L_CRU_MAX_INPUT_HEIGHT, 2, 0);
+ v4l_bound_align_image(&pix->width, 320, info->max_width, 1,
+ &pix->height, 240, info->max_height, 2, 0);
+
+ if (info->has_stride) {
+ u32 stride = clamp(pix->bytesperline, pix->width * fmt->bpp,
+ RZG2L_CRU_STRIDE_MAX);
+ pix->bytesperline = round_up(stride, RZG2L_CRU_STRIDE_ALIGN);
+ } else {
+ pix->bytesperline = pix->width * fmt->bpp;
+ }
- pix->bytesperline = rzg2l_cru_format_bytesperline(pix);
- pix->sizeimage = rzg2l_cru_format_sizeimage(pix);
+ pix->sizeimage = pix->bytesperline * pix->height;
dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n",
pix->width, pix->height, pix->bytesperline, pix->sizeimage);
@@ -905,10 +1020,13 @@ static int rzg2l_cru_g_fmt_vid_cap(struct file *file, void *priv,
static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- if (f->index >= ARRAY_SIZE(rzg2l_cru_formats))
+ const struct rzg2l_cru_ip_format *fmt;
+
+ fmt = rzg2l_cru_ip_index_to_fmt(f->index);
+ if (!fmt)
return -EINVAL;
- f->pixelformat = rzg2l_cru_formats[f->index].format;
+ f->pixelformat = fmt->format;
return 0;
}
@@ -984,6 +1102,43 @@ static const struct v4l2_file_operations rzg2l_cru_fops = {
.read = vb2_fop_read,
};
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static int rzg2l_cru_video_link_validate(struct media_link *link)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ };
+ const struct rzg2l_cru_ip_format *video_fmt;
+ struct v4l2_subdev *subdev;
+ struct rzg2l_cru_dev *cru;
+ int ret;
+
+ subdev = media_entity_to_v4l2_subdev(link->source->entity);
+ fmt.pad = link->source->index;
+ ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt);
+ if (ret < 0)
+ return ret == -ENOIOCTLCMD ? -EINVAL : ret;
+
+ cru = container_of(media_entity_to_video_device(link->sink->entity),
+ struct rzg2l_cru_dev, vdev);
+ video_fmt = rzg2l_cru_ip_format_to_fmt(cru->format.pixelformat);
+
+ if (fmt.format.width != cru->format.width ||
+ fmt.format.height != cru->format.height ||
+ fmt.format.field != cru->format.field ||
+ video_fmt->code != fmt.format.code)
+ return -EPIPE;
+
+ return 0;
+}
+
+static const struct media_entity_operations rzg2l_cru_video_media_ops = {
+ .link_validate = rzg2l_cru_video_link_validate,
+};
+
static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru)
{
struct video_device *vdev = &cru->vdev;
@@ -995,6 +1150,7 @@ static void rzg2l_cru_v4l2_init(struct rzg2l_cru_dev *cru)
vdev->lock = &cru->lock;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
vdev->device_caps |= V4L2_CAP_IO_MC;
+ vdev->entity.ops = &rzg2l_cru_video_media_ops;
vdev->fops = &rzg2l_cru_fops;
vdev->ioctl_ops = &rzg2l_cru_ioctl_ops;
diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c
index 1e74dd601c2b..4ad7ae188d5b 100644
--- a/drivers/media/platform/renesas/sh_vou.c
+++ b/drivers/media/platform/renesas/sh_vou.c
@@ -360,8 +360,6 @@ static const struct vb2_ops sh_vou_qops = {
.buf_queue = sh_vou_buf_queue,
.start_streaming = sh_vou_start_streaming,
.stop_streaming = sh_vou_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/* Video IOCTLs */
@@ -1359,7 +1357,7 @@ static void sh_vou_remove(struct platform_device *pdev)
}
static struct platform_driver sh_vou = {
- .remove_new = sh_vou_remove,
+ .remove = sh_vou_remove,
.driver = {
.name = "sh-vou",
},
diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile
index 4bb4dcbef7b5..de8c802e1d1a 100644
--- a/drivers/media/platform/renesas/vsp1/Makefile
+++ b/drivers/media/platform/renesas/vsp1/Makefile
@@ -5,6 +5,6 @@ vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o
vsp1-y += vsp1_clu.o vsp1_hsit.o vsp1_lut.o
vsp1-y += vsp1_brx.o vsp1_sru.o vsp1_uds.o
vsp1-y += vsp1_hgo.o vsp1_hgt.o vsp1_histo.o
-vsp1-y += vsp1_lif.o vsp1_uif.o
+vsp1-y += vsp1_iif.o vsp1_lif.o vsp1_uif.o
obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1.o
diff --git a/drivers/media/platform/renesas/vsp1/vsp1.h b/drivers/media/platform/renesas/vsp1/vsp1.h
index 2f6f0c6ae555..f97a1a31bfab 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1.h
@@ -32,6 +32,7 @@ struct vsp1_clu;
struct vsp1_hgo;
struct vsp1_hgt;
struct vsp1_hsit;
+struct vsp1_iif;
struct vsp1_lif;
struct vsp1_lut;
struct vsp1_rwpf;
@@ -56,6 +57,8 @@ struct vsp1_uif;
#define VSP1_HAS_BRS BIT(9)
#define VSP1_HAS_EXT_DL BIT(10)
#define VSP1_HAS_NON_ZERO_LBA BIT(11)
+#define VSP1_HAS_IIF BIT(12)
+#define VSP1_HAS_HSIT BIT(13)
struct vsp1_device_info {
u32 version;
@@ -91,6 +94,7 @@ struct vsp1_device {
struct vsp1_hgt *hgt;
struct vsp1_hsit *hsi;
struct vsp1_hsit *hst;
+ struct vsp1_iif *iif;
struct vsp1_lif *lif[VSP1_MAX_LIF];
struct vsp1_lut *lut;
struct vsp1_rwpf *rpf[VSP1_MAX_RPF];
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
index a8535c6e2c46..5fc2e5a3bb30 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
@@ -15,6 +15,7 @@
#include "vsp1.h"
#include "vsp1_brx.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_pipe.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
@@ -96,13 +97,6 @@ static int brx_enum_frame_size(struct v4l2_subdev *subdev,
return 0;
}
-static struct v4l2_rect *brx_get_compose(struct vsp1_brx *brx,
- struct v4l2_subdev_state *sd_state,
- unsigned int pad)
-{
- return v4l2_subdev_state_get_compose(sd_state, pad);
-}
-
static void brx_try_format(struct vsp1_brx *brx,
struct v4l2_subdev_state *sd_state,
unsigned int pad, struct v4l2_mbus_framefmt *fmt)
@@ -115,20 +109,26 @@ static void brx_try_format(struct vsp1_brx *brx,
if (fmt->code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+
+ vsp1_entity_adjust_color_space(fmt);
break;
default:
/* The BRx can't perform format conversion. */
- format = vsp1_entity_get_pad_format(&brx->entity, sd_state,
- BRX_PAD_SINK(0));
+ format = v4l2_subdev_state_get_format(sd_state,
+ BRX_PAD_SINK(0));
fmt->code = format->code;
+
+ fmt->colorspace = format->colorspace;
+ fmt->xfer_func = format->xfer_func;
+ fmt->ycbcr_enc = format->ycbcr_enc;
+ fmt->quantization = format->quantization;
break;
}
fmt->width = clamp(fmt->width, BRX_MIN_SIZE, BRX_MAX_SIZE);
fmt->height = clamp(fmt->height, BRX_MIN_SIZE, BRX_MAX_SIZE);
fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int brx_set_format(struct v4l2_subdev *subdev,
@@ -150,14 +150,14 @@ static int brx_set_format(struct v4l2_subdev *subdev,
brx_try_format(brx, state, fmt->pad, &fmt->format);
- format = vsp1_entity_get_pad_format(&brx->entity, state, fmt->pad);
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
*format = fmt->format;
/* Reset the compose rectangle. */
if (fmt->pad != brx->entity.source_pad) {
struct v4l2_rect *compose;
- compose = brx_get_compose(brx, state, fmt->pad);
+ compose = v4l2_subdev_state_get_compose(state, fmt->pad);
compose->left = 0;
compose->top = 0;
compose->width = format->width;
@@ -169,8 +169,7 @@ static int brx_set_format(struct v4l2_subdev *subdev,
unsigned int i;
for (i = 0; i <= brx->entity.source_pad; ++i) {
- format = vsp1_entity_get_pad_format(&brx->entity,
- state, i);
+ format = v4l2_subdev_state_get_format(state, i);
format->code = fmt->format.code;
}
}
@@ -205,7 +204,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev,
return -EINVAL;
mutex_lock(&brx->entity.lock);
- sel->r = *brx_get_compose(brx, state, sel->pad);
+ sel->r = *v4l2_subdev_state_get_compose(state, sel->pad);
mutex_unlock(&brx->entity.lock);
return 0;
@@ -242,8 +241,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev,
* The compose rectangle top left corner must be inside the output
* frame.
*/
- format = vsp1_entity_get_pad_format(&brx->entity, state,
- brx->entity.source_pad);
+ format = v4l2_subdev_state_get_format(state, brx->entity.source_pad);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
@@ -251,11 +249,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev,
* Scaling isn't supported, the compose rectangle size must be identical
* to the sink format size.
*/
- format = vsp1_entity_get_pad_format(&brx->entity, state, sel->pad);
+ format = v4l2_subdev_state_get_format(state, sel->pad);
sel->r.width = format->width;
sel->r.height = format->height;
- compose = brx_get_compose(brx, state, sel->pad);
+ compose = v4l2_subdev_state_get_compose(state, sel->pad);
*compose = sel->r;
done:
@@ -281,6 +279,7 @@ static const struct v4l2_subdev_ops brx_ops = {
*/
static void brx_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -290,8 +289,7 @@ static void brx_configure_stream(struct vsp1_entity *entity,
unsigned int flags;
unsigned int i;
- format = vsp1_entity_get_pad_format(&brx->entity, brx->entity.state,
- brx->entity.source_pad);
+ format = v4l2_subdev_state_get_format(state, brx->entity.source_pad);
/*
* The hardware is extremely flexible but we have no userspace API to
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
index 625776a9bda4..98645bd2a983 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
@@ -170,6 +170,7 @@ static const struct v4l2_subdev_ops clu_ops = {
*/
static void clu_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -181,8 +182,7 @@ static void clu_configure_stream(struct vsp1_entity *entity,
* The yuv_mode can't be changed during streaming. Cache it internally
* for future runtime configuration calls.
*/
- format = vsp1_entity_get_pad_format(&clu->entity, clu->entity.state,
- CLU_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, CLU_PAD_SINK);
clu->yuv_mode = format->code == MEDIA_BUS_FMT_AYUV8_1X32;
}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index ad3fa1c9cc73..bb8228b19824 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
@@ -1099,7 +1099,12 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
return NULL;
dlm->index = index;
- dlm->singleshot = vsp1->info->uapi;
+ /*
+ * uapi = single shot mode;
+ * DRM = continuous mode;
+ * VSPX = single shot mode;
+ */
+ dlm->singleshot = vsp1->info->uapi || vsp1->iif;
dlm->vsp1 = vsp1;
spin_lock_init(&dlm->lock);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index 9b087bd8df7d..fe55e8747b05 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
@@ -118,26 +118,26 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
struct vsp1_entity *uif,
unsigned int brx_input)
{
+ const struct vsp1_drm_input *input = &vsp1->drm->inputs[rpf->entity.index];
struct v4l2_subdev_selection sel = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
- const struct v4l2_rect *crop;
int ret;
/*
* Configure the format on the RPF sink pad and propagate it up to the
* BRx sink pad.
*/
- crop = &vsp1->drm->inputs[rpf->entity.index].crop;
-
format.pad = RWPF_PAD_SINK;
- format.format.width = crop->width + crop->left;
- format.format.height = crop->height + crop->top;
+ format.format.width = input->crop.width + input->crop.left;
+ format.format.height = input->crop.height + input->crop.top;
format.format.code = rpf->fmtinfo->mbus;
format.format.field = V4L2_FIELD_NONE;
+ format.format.ycbcr_enc = input->ycbcr_enc;
+ format.format.quantization = input->quantization;
ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
&format);
@@ -151,7 +151,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
sel.pad = RWPF_PAD_SINK;
sel.target = V4L2_SEL_TGT_CROP;
- sel.r = *crop;
+ sel.r = input->crop;
ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
&sel);
@@ -317,7 +317,10 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
list_add_tail(&released_brx->list_pipe,
&pipe->entities);
- /* Add the BRx to the pipeline. */
+ /*
+ * Add the BRx to the pipeline, inserting it just before the
+ * WPF.
+ */
dev_dbg(vsp1->dev, "%s: pipe %u: acquired %s\n",
__func__, pipe->lif->index, BRX_NAME(brx));
@@ -326,7 +329,8 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
pipe->brx->sink = &pipe->output->entity;
pipe->brx->sink_pad = 0;
- list_add_tail(&pipe->brx->list_pipe, &pipe->entities);
+ list_add_tail(&pipe->brx->list_pipe,
+ &pipe->output->entity.list_pipe);
}
/*
@@ -420,7 +424,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
if (!rpf->entity.pipe) {
rpf->entity.pipe = pipe;
- list_add_tail(&rpf->entity.list_pipe, &pipe->entities);
+ list_add(&rpf->entity.list_pipe, &pipe->entities);
}
brx->inputs[i].rpf = rpf;
@@ -546,6 +550,9 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
struct vsp1_dl_body *dlb;
unsigned int dl_flags = 0;
+ vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0],
+ drm_pipe->width, 0);
+
if (drm_pipe->force_brx_release)
dl_flags |= VSP1_DL_FRAME_END_INTERNAL;
if (pipe->output->writeback)
@@ -567,9 +574,11 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
}
vsp1_entity_route_setup(entity, pipe, dlb);
- vsp1_entity_configure_stream(entity, pipe, dl, dlb);
+ vsp1_entity_configure_stream(entity, entity->state, pipe,
+ dl, dlb);
vsp1_entity_configure_frame(entity, pipe, dl, dlb);
- vsp1_entity_configure_partition(entity, pipe, dl, dlb);
+ vsp1_entity_configure_partition(entity, pipe,
+ &pipe->part_table[0], dl, dlb);
}
vsp1_dl_list_commit(dl, dl_flags);
@@ -584,8 +593,8 @@ static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1,
fmtinfo = vsp1_get_format_info(vsp1, pixelformat);
if (!fmtinfo) {
- dev_dbg(vsp1->dev, "Unsupported pixel format %08x\n",
- pixelformat);
+ dev_dbg(vsp1->dev, "Unsupported pixel format %p4cc\n",
+ &pixelformat);
return -EINVAL;
}
@@ -733,6 +742,8 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
if (ret < 0)
goto unlock;
+ vsp1_pipeline_dump(pipe, "LIF setup");
+
/* Enable the VSP1. */
ret = vsp1_device_get(vsp1);
if (ret < 0)
@@ -815,12 +826,14 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[pipe_index];
+ struct vsp1_drm_input *input;
struct vsp1_rwpf *rpf;
int ret;
if (rpf_index >= vsp1->info->rpf_count)
return -EINVAL;
+ input = &vsp1->drm->inputs[rpf_index];
rpf = vsp1->rpf[rpf_index];
if (!cfg) {
@@ -838,11 +851,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
}
dev_dbg(vsp1->dev,
- "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%08x), pitch %u dma { %pad, %pad, %pad } zpos %u\n",
+ "%s: RPF%u: (%u,%u)/%ux%u -> (%u,%u)/%ux%u (%p4cc), pitch %u dma { %pad, %pad, %pad } zpos %u\n",
__func__, rpf_index,
cfg->src.left, cfg->src.top, cfg->src.width, cfg->src.height,
cfg->dst.left, cfg->dst.top, cfg->dst.width, cfg->dst.height,
- cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
+ &cfg->pixelformat, cfg->pitch, &cfg->mem[0], &cfg->mem[1],
&cfg->mem[2], cfg->zpos);
/*
@@ -862,9 +875,11 @@ int vsp1_du_atomic_update(struct device *dev, unsigned int pipe_index,
rpf->format.flags = cfg->premult ? V4L2_PIX_FMT_FLAG_PREMUL_ALPHA : 0;
- vsp1->drm->inputs[rpf_index].crop = cfg->src;
- vsp1->drm->inputs[rpf_index].compose = cfg->dst;
- vsp1->drm->inputs[rpf_index].zpos = cfg->zpos;
+ input->crop = cfg->src;
+ input->compose = cfg->dst;
+ input->zpos = cfg->zpos;
+ input->ycbcr_enc = cfg->color_encoding;
+ input->quantization = cfg->color_range;
drm_pipe->pipe.inputs[rpf_index] = rpf;
@@ -906,6 +921,9 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
}
vsp1_du_pipeline_setup_inputs(vsp1, pipe);
+
+ vsp1_pipeline_dump(pipe, "atomic update");
+
vsp1_du_pipeline_configure(pipe);
done:
@@ -959,6 +977,9 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
vsp1_pipeline_init(pipe);
+ pipe->partitions = 1;
+ pipe->part_table = &drm_pipe->partition;
+
pipe->frame_end = vsp1_du_pipeline_frame_end;
/*
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.h b/drivers/media/platform/renesas/vsp1/vsp1_drm.h
index ab8b7e3161a2..07a5d0adbd08 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.h
@@ -20,6 +20,7 @@
/**
* struct vsp1_drm_pipeline - State for the API exposed to the DRM driver
* @pipe: the VSP1 pipeline used for display
+ * @partition: the pre-calculated partition used by the pipeline
* @width: output display width
* @height: output display height
* @force_brx_release: when set, release the BRx during the next reconfiguration
@@ -31,6 +32,7 @@
*/
struct vsp1_drm_pipeline {
struct vsp1_pipeline pipe;
+ struct vsp1_partition partition;
unsigned int width;
unsigned int height;
@@ -50,17 +52,19 @@ struct vsp1_drm_pipeline {
* struct vsp1_drm - State for the API exposed to the DRM driver
* @pipe: the VSP1 DRM pipeline used for display
* @lock: protects the BRU and BRS allocation
- * @inputs: source crop rectangle, destination compose rectangle and z-order
- * position for every input (indexed by RPF index)
+ * @inputs: source crop rectangle, destination compose rectangle, z-order
+ * position and colorspace for every input (indexed by RPF index)
*/
struct vsp1_drm {
struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF];
struct mutex lock;
- struct {
+ struct vsp1_drm_input {
struct v4l2_rect crop;
struct v4l2_rect compose;
unsigned int zpos;
+ enum v4l2_ycbcr_encoding ycbcr_enc;
+ enum v4l2_quantization quantization;
} inputs[VSP1_MAX_RPF];
};
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index 1aac44d68731..8270a9d207cb 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -29,6 +29,7 @@
#include "vsp1_hgo.h"
#include "vsp1_hgt.h"
#include "vsp1_hsit.h"
+#include "vsp1_iif.h"
#include "vsp1_lif.h"
#include "vsp1_lut.h"
#include "vsp1_pipe.h"
@@ -302,22 +303,6 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
list_add_tail(&vsp1->clu->entity.list_dev, &vsp1->entities);
}
- vsp1->hsi = vsp1_hsit_create(vsp1, true);
- if (IS_ERR(vsp1->hsi)) {
- ret = PTR_ERR(vsp1->hsi);
- goto done;
- }
-
- list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
-
- vsp1->hst = vsp1_hsit_create(vsp1, false);
- if (IS_ERR(vsp1->hst)) {
- ret = PTR_ERR(vsp1->hst);
- goto done;
- }
-
- list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
-
if (vsp1_feature(vsp1, VSP1_HAS_HGO) && vsp1->info->uapi) {
vsp1->hgo = vsp1_hgo_create(vsp1);
if (IS_ERR(vsp1->hgo)) {
@@ -340,6 +325,34 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
&vsp1->entities);
}
+ if (vsp1_feature(vsp1, VSP1_HAS_IIF)) {
+ vsp1->iif = vsp1_iif_create(vsp1);
+ if (IS_ERR(vsp1->iif)) {
+ ret = PTR_ERR(vsp1->iif);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->iif->entity.list_dev, &vsp1->entities);
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ vsp1->hsi = vsp1_hsit_create(vsp1, true);
+ if (IS_ERR(vsp1->hsi)) {
+ ret = PTR_ERR(vsp1->hsi);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->hsi->entity.list_dev, &vsp1->entities);
+
+ vsp1->hst = vsp1_hsit_create(vsp1, false);
+ if (IS_ERR(vsp1->hst)) {
+ ret = PTR_ERR(vsp1->hst);
+ goto done;
+ }
+
+ list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities);
+ }
+
/*
* The LIFs are only supported when used in conjunction with the DU, in
* which case the userspace API is disabled. If the userspace API is
@@ -683,8 +696,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.model = "VSP1-S",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO
- | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU
- | VSP1_HAS_WPF_VFLIP,
+ | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT
+ | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 3,
.wpf_count = 4,
@@ -694,7 +707,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPR_H2,
.model = "VSP1-R",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
+ .features = VSP1_HAS_BRU | VSP1_HAS_HSIT | VSP1_HAS_SRU
+ | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 3,
.wpf_count = 4,
@@ -704,7 +718,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPD_GEN2,
.model = "VSP1-D",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_LUT,
+ .features = VSP1_HAS_BRU | VSP1_HAS_HGO | VSP1_HAS_HSIT
+ | VSP1_HAS_LUT,
.lif_count = 1,
.rpf_count = 4,
.uds_count = 1,
@@ -716,8 +731,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.model = "VSP1-S",
.gen = 2,
.features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HGO
- | VSP1_HAS_HGT | VSP1_HAS_LUT | VSP1_HAS_SRU
- | VSP1_HAS_WPF_VFLIP,
+ | VSP1_HAS_HGT | VSP1_HAS_HSIT | VSP1_HAS_LUT
+ | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 5,
.uds_count = 1,
.wpf_count = 4,
@@ -727,8 +742,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPS_V2H,
.model = "VSP1V-S",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT
- | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT
+ | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_VFLIP,
.rpf_count = 4,
.uds_count = 1,
.wpf_count = 4,
@@ -738,7 +753,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.version = VI6_IP_VERSION_MODEL_VSPD_V2H,
.model = "VSP1V-D",
.gen = 2,
- .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_LUT,
+ .features = VSP1_HAS_BRU | VSP1_HAS_CLU | VSP1_HAS_HSIT
+ | VSP1_HAS_LUT,
.lif_count = 1,
.rpf_count = 4,
.uds_count = 1,
@@ -750,8 +766,8 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.model = "VSP2-I",
.gen = 3,
.features = VSP1_HAS_CLU | VSP1_HAS_HGO | VSP1_HAS_HGT
- | VSP1_HAS_LUT | VSP1_HAS_SRU | VSP1_HAS_WPF_HFLIP
- | VSP1_HAS_WPF_VFLIP,
+ | VSP1_HAS_HSIT | VSP1_HAS_LUT | VSP1_HAS_SRU
+ | VSP1_HAS_WPF_HFLIP | VSP1_HAS_WPF_VFLIP,
.rpf_count = 1,
.uds_count = 1,
.wpf_count = 1,
@@ -1005,7 +1021,7 @@ MODULE_DEVICE_TABLE(of, vsp1_of_match);
static struct platform_driver vsp1_platform_driver = {
.probe = vsp1_probe,
- .remove_new = vsp1_remove,
+ .remove = vsp1_remove,
.driver = {
.name = "vsp1",
.pm = &vsp1_pm_ops,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
index 0a5a7f9cc870..a6680d531872 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
@@ -63,19 +63,25 @@ void vsp1_entity_route_setup(struct vsp1_entity *entity,
/*
* The ILV and BRS share the same data path route. The extra BRSSEL bit
* selects between the ILV and BRS.
+ *
+ * The BRU and IIF share the same data path route. The extra IIFSEL bit
+ * selects between the IIF and BRU.
*/
if (source->type == VSP1_ENTITY_BRS)
route |= VI6_DPR_ROUTE_BRSSEL;
+ else if (source->type == VSP1_ENTITY_IIF)
+ route |= VI6_DPR_ROUTE_IIFSEL;
vsp1_dl_body_write(dlb, source->route->reg, route);
}
void vsp1_entity_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
{
if (entity->ops->configure_stream)
- entity->ops->configure_stream(entity, pipe, dl, dlb);
+ entity->ops->configure_stream(entity, state, pipe, dl, dlb);
}
void vsp1_entity_configure_frame(struct vsp1_entity *entity,
@@ -89,11 +95,27 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity,
void vsp1_entity_configure_partition(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
+ const struct vsp1_partition *partition,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
{
if (entity->ops->configure_partition)
- entity->ops->configure_partition(entity, pipe, dl, dlb);
+ entity->ops->configure_partition(entity, pipe, partition,
+ dl, dlb);
+}
+
+void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format)
+{
+ u8 xfer_func = format->xfer_func;
+ u8 ycbcr_enc = format->ycbcr_enc;
+ u8 quantization = format->quantization;
+
+ vsp1_adjust_color_space(format->code, &format->colorspace, &xfer_func,
+ &ycbcr_enc, &quantization);
+
+ format->xfer_func = xfer_func;
+ format->ycbcr_enc = ycbcr_enc;
+ format->quantization = quantization;
}
/* -----------------------------------------------------------------------------
@@ -127,49 +149,6 @@ vsp1_entity_get_state(struct vsp1_entity *entity,
}
}
-/**
- * vsp1_entity_get_pad_format - Get a pad format from storage for an entity
- * @entity: the entity
- * @sd_state: the state storage
- * @pad: the pad number
- *
- * Return the format stored in the given configuration for an entity's pad. The
- * configuration can be an ACTIVE or TRY configuration.
- */
-struct v4l2_mbus_framefmt *
-vsp1_entity_get_pad_format(struct vsp1_entity *entity,
- struct v4l2_subdev_state *sd_state,
- unsigned int pad)
-{
- return v4l2_subdev_state_get_format(sd_state, pad);
-}
-
-/**
- * vsp1_entity_get_pad_selection - Get a pad selection from storage for entity
- * @entity: the entity
- * @sd_state: the state storage
- * @pad: the pad number
- * @target: the selection target
- *
- * Return the selection rectangle stored in the given configuration for an
- * entity's pad. The configuration can be an ACTIVE or TRY configuration. The
- * selection target can be COMPOSE or CROP.
- */
-struct v4l2_rect *
-vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
- struct v4l2_subdev_state *sd_state,
- unsigned int pad, unsigned int target)
-{
- switch (target) {
- case V4L2_SEL_TGT_COMPOSE:
- return v4l2_subdev_state_get_compose(sd_state, pad);
- case V4L2_SEL_TGT_CROP:
- return v4l2_subdev_state_get_crop(sd_state, pad);
- default:
- return NULL;
- }
-}
-
/*
* vsp1_subdev_get_pad_format - Subdev pad get_fmt handler
* @subdev: V4L2 subdevice
@@ -191,7 +170,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
return -EINVAL;
mutex_lock(&entity->lock);
- fmt->format = *vsp1_entity_get_pad_format(entity, state, fmt->pad);
+ fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad);
mutex_unlock(&entity->lock);
return 0;
@@ -238,7 +217,7 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
return -EINVAL;
mutex_lock(&entity->lock);
- format = vsp1_entity_get_pad_format(entity, state, 0);
+ format = v4l2_subdev_state_get_format(state, 0);
code->code = format->code;
mutex_unlock(&entity->lock);
}
@@ -276,7 +255,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
if (!state)
return -EINVAL;
- format = vsp1_entity_get_pad_format(entity, state, fse->pad);
+ format = v4l2_subdev_state_get_format(state, fse->pad);
mutex_lock(&entity->lock);
@@ -346,7 +325,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
goto done;
}
- format = vsp1_entity_get_pad_format(entity, state, fmt->pad);
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
if (fmt->pad == entity->source_pad) {
/* The output format can't be modified. */
@@ -369,24 +348,28 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
format->height = clamp_t(unsigned int, fmt->format.height,
min_height, max_height);
format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ format->colorspace = fmt->format.colorspace;
+ format->xfer_func = fmt->format.xfer_func;
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+
+ vsp1_entity_adjust_color_space(format);
fmt->format = *format;
/* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(entity, state, entity->source_pad);
+ format = v4l2_subdev_state_get_format(state, entity->source_pad);
*format = fmt->format;
/* Reset the crop and compose rectangles. */
- selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad,
- V4L2_SEL_TGT_CROP);
+ selection = v4l2_subdev_state_get_crop(state, fmt->pad);
selection->left = 0;
selection->top = 0;
selection->width = format->width;
selection->height = format->height;
- selection = vsp1_entity_get_pad_selection(entity, state, fmt->pad,
- V4L2_SEL_TGT_COMPOSE);
+ selection = v4l2_subdev_state_get_compose(state, fmt->pad);
selection->left = 0;
selection->top = 0;
selection->width = format->width;
@@ -570,6 +553,9 @@ struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad)
{ VI6_DPR_NODE_WPF(idx) }, VI6_DPR_NODE_WPF(idx) }
static const struct vsp1_route vsp1_routes[] = {
+ { VSP1_ENTITY_IIF, 0, VI6_DPR_BRU_ROUTE,
+ { VI6_DPR_NODE_BRU_IN(0), VI6_DPR_NODE_BRU_IN(1),
+ VI6_DPR_NODE_BRU_IN(3) }, VI6_DPR_NODE_WPF(0) },
{ VSP1_ENTITY_BRS, 0, VI6_DPR_ILV_BRS_ROUTE,
{ VI6_DPR_NODE_BRS_IN(0), VI6_DPR_NODE_BRS_IN(1) }, 0 },
{ VSP1_ENTITY_BRU, 0, VI6_DPR_BRU_ROUTE,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
index 735f32dde4b5..b7c72d0b7f8e 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
@@ -19,7 +19,6 @@ struct vsp1_dl_body;
struct vsp1_dl_list;
struct vsp1_pipeline;
struct vsp1_partition;
-struct vsp1_partition_window;
enum vsp1_entity_type {
VSP1_ENTITY_BRS,
@@ -29,6 +28,7 @@ enum vsp1_entity_type {
VSP1_ENTITY_HGT,
VSP1_ENTITY_HSI,
VSP1_ENTITY_HST,
+ VSP1_ENTITY_IIF,
VSP1_ENTITY_LIF,
VSP1_ENTITY_LUT,
VSP1_ENTITY_RPF,
@@ -78,19 +78,30 @@ struct vsp1_route {
* configuration.
*/
struct vsp1_entity_operations {
- void (*destroy)(struct vsp1_entity *);
- void (*configure_stream)(struct vsp1_entity *, struct vsp1_pipeline *,
- struct vsp1_dl_list *, struct vsp1_dl_body *);
- void (*configure_frame)(struct vsp1_entity *, struct vsp1_pipeline *,
- struct vsp1_dl_list *, struct vsp1_dl_body *);
- void (*configure_partition)(struct vsp1_entity *,
- struct vsp1_pipeline *,
- struct vsp1_dl_list *,
- struct vsp1_dl_body *);
- unsigned int (*max_width)(struct vsp1_entity *, struct vsp1_pipeline *);
- void (*partition)(struct vsp1_entity *, struct vsp1_pipeline *,
- struct vsp1_partition *, unsigned int,
- struct vsp1_partition_window *);
+ void (*destroy)(struct vsp1_entity *entity);
+ void (*configure_stream)(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb);
+ void (*configure_frame)(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb);
+ void (*configure_partition)(struct vsp1_entity *entity,
+ struct vsp1_pipeline *pipe,
+ const struct vsp1_partition *partition,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb);
+ unsigned int (*max_width)(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
+ struct vsp1_pipeline *pipe);
+ void (*partition)(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_partition *partition,
+ unsigned int index,
+ struct v4l2_rect *window);
};
struct vsp1_entity {
@@ -138,20 +149,13 @@ struct v4l2_subdev_state *
vsp1_entity_get_state(struct vsp1_entity *entity,
struct v4l2_subdev_state *sd_state,
enum v4l2_subdev_format_whence which);
-struct v4l2_mbus_framefmt *
-vsp1_entity_get_pad_format(struct vsp1_entity *entity,
- struct v4l2_subdev_state *sd_state,
- unsigned int pad);
-struct v4l2_rect *
-vsp1_entity_get_pad_selection(struct vsp1_entity *entity,
- struct v4l2_subdev_state *sd_state,
- unsigned int pad, unsigned int target);
void vsp1_entity_route_setup(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
struct vsp1_dl_body *dlb);
void vsp1_entity_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb);
@@ -163,9 +167,12 @@ void vsp1_entity_configure_frame(struct vsp1_entity *entity,
void vsp1_entity_configure_partition(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
+ const struct vsp1_partition *partition,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb);
+void vsp1_entity_adjust_color_space(struct v4l2_mbus_framefmt *format);
+
struct media_pad *vsp1_entity_remote_pad(struct media_pad *pad);
int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
index 40c571a987ef..2c8ce7175a4e 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
@@ -130,6 +130,7 @@ static const struct v4l2_ctrl_config hgo_num_bins_control = {
*/
static void hgo_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -140,11 +141,8 @@ static void hgo_configure_stream(struct vsp1_entity *entity,
unsigned int hratio;
unsigned int vratio;
- crop = vsp1_entity_get_pad_selection(entity, entity->state,
- HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
- compose = vsp1_entity_get_pad_selection(entity, entity->state,
- HISTO_PAD_SINK,
- V4L2_SEL_TGT_COMPOSE);
+ crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK);
+ compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK);
vsp1_hgo_write(hgo, dlb, VI6_HGO_REGRST, VI6_HGO_REGRST_RCLEA);
@@ -194,6 +192,16 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1)
if (hgo == NULL)
return ERR_PTR(-ENOMEM);
+ /* Initialize the video device and queue for statistics data. */
+ ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo",
+ &hgo_entity_ops, hgo_mbus_formats,
+ ARRAY_SIZE(hgo_mbus_formats),
+ HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO);
+ if (ret < 0) {
+ vsp1_entity_destroy(&hgo->histo.entity);
+ return ERR_PTR(ret);
+ }
+
/* Initialize the control handler. */
v4l2_ctrl_handler_init(&hgo->ctrls.handler,
vsp1->info->gen >= 3 ? 2 : 1);
@@ -209,15 +217,5 @@ struct vsp1_hgo *vsp1_hgo_create(struct vsp1_device *vsp1)
hgo->histo.entity.subdev.ctrl_handler = &hgo->ctrls.handler;
- /* Initialize the video device and queue for statistics data. */
- ret = vsp1_histogram_init(vsp1, &hgo->histo, VSP1_ENTITY_HGO, "hgo",
- &hgo_entity_ops, hgo_mbus_formats,
- ARRAY_SIZE(hgo_mbus_formats),
- HGO_DATA_SIZE, V4L2_META_FMT_VSP1_HGO);
- if (ret < 0) {
- vsp1_entity_destroy(&hgo->histo.entity);
- return ERR_PTR(ret);
- }
-
return hgo;
}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
index 8281b86874ab..858f330d44fa 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
@@ -126,6 +126,7 @@ static const struct v4l2_ctrl_config hgt_hue_areas = {
*/
static void hgt_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -139,11 +140,8 @@ static void hgt_configure_stream(struct vsp1_entity *entity,
u8 upper;
unsigned int i;
- crop = vsp1_entity_get_pad_selection(entity, entity->state,
- HISTO_PAD_SINK, V4L2_SEL_TGT_CROP);
- compose = vsp1_entity_get_pad_selection(entity, entity->state,
- HISTO_PAD_SINK,
- V4L2_SEL_TGT_COMPOSE);
+ crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK);
+ compose = v4l2_subdev_state_get_compose(state, HISTO_PAD_SINK);
vsp1_hgt_write(hgt, dlb, VI6_HGT_REGRST, VI6_HGT_REGRST_RCLEA);
@@ -193,12 +191,6 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1)
if (hgt == NULL)
return ERR_PTR(-ENOMEM);
- /* Initialize the control handler. */
- v4l2_ctrl_handler_init(&hgt->ctrls, 1);
- v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL);
-
- hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls;
-
/* Initialize the video device and queue for statistics data. */
ret = vsp1_histogram_init(vsp1, &hgt->histo, VSP1_ENTITY_HGT, "hgt",
&hgt_entity_ops, hgt_mbus_formats,
@@ -209,6 +201,12 @@ struct vsp1_hgt *vsp1_hgt_create(struct vsp1_device *vsp1)
return ERR_PTR(ret);
}
+ /* Initialize the control handler. */
+ v4l2_ctrl_handler_init(&hgt->ctrls, 1);
+ v4l2_ctrl_new_custom(&hgt->ctrls, &hgt_hue_areas, NULL);
+
+ hgt->histo.entity.subdev.ctrl_handler = &hgt->ctrls;
+
v4l2_ctrl_handler_setup(&hgt->ctrls);
return hgt;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
index 71155282ca11..c762202877ba 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
@@ -36,9 +36,8 @@ struct vsp1_histogram_buffer *
vsp1_histogram_buffer_get(struct vsp1_histogram *histo)
{
struct vsp1_histogram_buffer *buf = NULL;
- unsigned long flags;
- spin_lock_irqsave(&histo->irqlock, flags);
+ spin_lock(&histo->irqlock);
if (list_empty(&histo->irqqueue))
goto done;
@@ -49,7 +48,7 @@ vsp1_histogram_buffer_get(struct vsp1_histogram *histo)
histo->readout = true;
done:
- spin_unlock_irqrestore(&histo->irqlock, flags);
+ spin_unlock(&histo->irqlock);
return buf;
}
@@ -58,7 +57,6 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
size_t size)
{
struct vsp1_pipeline *pipe = histo->entity.pipe;
- unsigned long flags;
/*
* The pipeline pointer is guaranteed to be valid as this function is
@@ -70,10 +68,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
- spin_lock_irqsave(&histo->irqlock, flags);
+ spin_lock(&histo->irqlock);
histo->readout = false;
wake_up(&histo->wait_queue);
- spin_unlock_irqrestore(&histo->irqlock, flags);
+ spin_unlock(&histo->irqlock);
}
/* -----------------------------------------------------------------------------
@@ -124,11 +122,10 @@ static void histo_buffer_queue(struct vb2_buffer *vb)
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue);
struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf);
- unsigned long flags;
- spin_lock_irqsave(&histo->irqlock, flags);
+ spin_lock_irq(&histo->irqlock);
list_add_tail(&buf->queue, &histo->irqqueue);
- spin_unlock_irqrestore(&histo->irqlock, flags);
+ spin_unlock_irq(&histo->irqlock);
}
static int histo_start_streaming(struct vb2_queue *vq, unsigned int count)
@@ -140,9 +137,8 @@ static void histo_stop_streaming(struct vb2_queue *vq)
{
struct vsp1_histogram *histo = vb2_get_drv_priv(vq);
struct vsp1_histogram_buffer *buffer;
- unsigned long flags;
- spin_lock_irqsave(&histo->irqlock, flags);
+ spin_lock_irq(&histo->irqlock);
/* Remove all buffers from the IRQ queue. */
list_for_each_entry(buffer, &histo->irqqueue, queue)
@@ -152,15 +148,13 @@ static void histo_stop_streaming(struct vb2_queue *vq)
/* Wait for the buffer being read out (if any) to complete. */
wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock);
- spin_unlock_irqrestore(&histo->irqlock, flags);
+ spin_unlock_irq(&histo->irqlock);
}
static const struct vb2_ops histo_video_queue_qops = {
.queue_setup = histo_queue_setup,
.buf_prepare = histo_buffer_prepare,
.buf_queue = histo_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = histo_start_streaming,
.stop_streaming = histo_stop_streaming,
};
@@ -222,9 +216,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
- crop = vsp1_entity_get_pad_selection(&histo->entity, state,
- HISTO_PAD_SINK,
- V4L2_SEL_TGT_CROP);
+ crop = v4l2_subdev_state_get_crop(state, HISTO_PAD_SINK);
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = crop->width;
@@ -233,8 +225,7 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
- format = vsp1_entity_get_pad_format(&histo->entity, state,
- HISTO_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, HISTO_PAD_SINK);
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = format->width;
@@ -242,9 +233,11 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
break;
case V4L2_SEL_TGT_COMPOSE:
+ sel->r = *v4l2_subdev_state_get_compose(state, sel->pad);
+ break;
+
case V4L2_SEL_TGT_CROP:
- sel->r = *vsp1_entity_get_pad_selection(&histo->entity, state,
- sel->pad, sel->target);
+ sel->r = *v4l2_subdev_state_get_crop(state, sel->pad);
break;
default:
@@ -261,13 +254,10 @@ static int histo_set_crop(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
- struct vsp1_histogram *histo = subdev_to_histo(subdev);
struct v4l2_mbus_framefmt *format;
- struct v4l2_rect *selection;
/* The crop rectangle must be inside the input frame. */
- format = vsp1_entity_get_pad_format(&histo->entity, sd_state,
- HISTO_PAD_SINK);
+ format = v4l2_subdev_state_get_format(sd_state, HISTO_PAD_SINK);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
sel->r.width = clamp_t(unsigned int, sel->r.width, HISTO_MIN_SIZE,
@@ -276,14 +266,8 @@ static int histo_set_crop(struct v4l2_subdev *subdev,
format->height - sel->r.top);
/* Set the crop rectangle and reset the compose rectangle. */
- selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
- sel->pad, V4L2_SEL_TGT_CROP);
- *selection = sel->r;
-
- selection = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
- sel->pad,
- V4L2_SEL_TGT_COMPOSE);
- *selection = sel->r;
+ *v4l2_subdev_state_get_crop(sd_state, sel->pad) = sel->r;
+ *v4l2_subdev_state_get_compose(sd_state, sel->pad) = sel->r;
return 0;
}
@@ -292,7 +276,6 @@ static int histo_set_compose(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
- struct vsp1_histogram *histo = subdev_to_histo(subdev);
struct v4l2_rect *compose;
struct v4l2_rect *crop;
unsigned int ratio;
@@ -305,9 +288,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev,
sel->r.left = 0;
sel->r.top = 0;
- crop = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
- sel->pad,
- V4L2_SEL_TGT_CROP);
+ crop = v4l2_subdev_state_get_crop(sd_state, sel->pad);
/*
* Clamp the width and height to acceptable values first and then
@@ -332,9 +313,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev,
ratio = 1 << (crop->height * 2 / sel->r.height / 3);
sel->r.height = crop->height / ratio;
- compose = vsp1_entity_get_pad_selection(&histo->entity, sd_state,
- sel->pad,
- V4L2_SEL_TGT_COMPOSE);
+ compose = v4l2_subdev_state_get_compose(sd_state, sel->pad);
*compose = sel->r;
return 0;
@@ -371,31 +350,22 @@ done:
return ret;
}
-static int histo_get_format(struct v4l2_subdev *subdev,
+static int histo_set_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
+ struct vsp1_histogram *histo = subdev_to_histo(subdev);
+
if (fmt->pad == HISTO_PAD_SOURCE) {
fmt->format.code = MEDIA_BUS_FMT_FIXED;
fmt->format.width = 0;
fmt->format.height = 0;
fmt->format.field = V4L2_FIELD_NONE;
fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+
return 0;
}
- return vsp1_subdev_get_pad_format(subdev, sd_state, fmt);
-}
-
-static int histo_set_format(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct vsp1_histogram *histo = subdev_to_histo(subdev);
-
- if (fmt->pad != HISTO_PAD_SINK)
- return histo_get_format(subdev, sd_state, fmt);
-
return vsp1_subdev_set_pad_format(subdev, sd_state, fmt,
histo->formats, histo->num_formats,
HISTO_MIN_SIZE, HISTO_MIN_SIZE,
@@ -405,7 +375,7 @@ static int histo_set_format(struct v4l2_subdev *subdev,
static const struct v4l2_subdev_pad_ops histo_pad_ops = {
.enum_mbus_code = histo_enum_mbus_code,
.enum_frame_size = histo_enum_frame_size,
- .get_fmt = histo_get_format,
+ .get_fmt = vsp1_subdev_get_pad_format,
.set_fmt = histo_set_format,
.get_selection = histo_get_selection,
.set_selection = histo_set_selection,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
index bc1299c29ac9..1fcd1967d3b2 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
@@ -14,6 +14,7 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_hsit.h"
#define HSIT_MIN_SIZE 4U
@@ -78,7 +79,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
goto done;
}
- format = vsp1_entity_get_pad_format(&hsit->entity, state, fmt->pad);
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
if (fmt->pad == HSIT_PAD_SOURCE) {
/*
@@ -96,17 +97,24 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
format->height = clamp_t(unsigned int, fmt->format.height,
HSIT_MIN_SIZE, HSIT_MAX_SIZE);
format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ format->colorspace = fmt->format.colorspace;
+ format->xfer_func = fmt->format.xfer_func;
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+
+ vsp1_entity_adjust_color_space(format);
fmt->format = *format;
/* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&hsit->entity, state,
- HSIT_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, HSIT_PAD_SOURCE);
*format = fmt->format;
format->code = hsit->inverse ? MEDIA_BUS_FMT_ARGB8888_1X32
: MEDIA_BUS_FMT_AHSV8888_1X32;
+ vsp1_entity_adjust_color_space(format);
+
done:
mutex_unlock(&hsit->entity.lock);
return ret;
@@ -128,6 +136,7 @@ static const struct v4l2_subdev_ops hsit_ops = {
*/
static void hsit_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.c b/drivers/media/platform/renesas/vsp1/vsp1_iif.c
new file mode 100644
index 000000000000..5dd62bebbe8c
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vsp1_iif.c -- R-Car VSP1 IIF (ISP Interface)
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Corporation
+ */
+
+#include "vsp1.h"
+#include "vsp1_dl.h"
+#include "vsp1_iif.h"
+
+#define IIF_MIN_WIDTH 128U
+#define IIF_MIN_HEIGHT 32U
+#define IIF_MAX_WIDTH 5120U
+#define IIF_MAX_HEIGHT 4096U
+
+/* -----------------------------------------------------------------------------
+ * Device Access
+ */
+
+static inline void vsp1_iif_write(struct vsp1_dl_body *dlb, u32 reg, u32 data)
+{
+ vsp1_dl_body_write(dlb, reg, data);
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 Subdevice Operations
+ */
+
+static const unsigned int iif_codes[] = {
+ MEDIA_BUS_FMT_Y8_1X8,
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y12_1X12,
+ MEDIA_BUS_FMT_Y16_1X16,
+ MEDIA_BUS_FMT_METADATA_FIXED
+};
+
+static int iif_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ return vsp1_subdev_enum_mbus_code(subdev, sd_state, code, iif_codes,
+ ARRAY_SIZE(iif_codes));
+}
+
+static int iif_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ return vsp1_subdev_enum_frame_size(subdev, sd_state, fse,
+ IIF_MIN_WIDTH, IIF_MIN_HEIGHT,
+ IIF_MAX_WIDTH, IIF_MAX_HEIGHT);
+}
+
+static int iif_set_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ return vsp1_subdev_set_pad_format(subdev, sd_state, fmt, iif_codes,
+ ARRAY_SIZE(iif_codes),
+ IIF_MIN_WIDTH, IIF_MIN_HEIGHT,
+ IIF_MAX_WIDTH, IIF_MAX_HEIGHT);
+}
+
+static const struct v4l2_subdev_pad_ops iif_pad_ops = {
+ .enum_mbus_code = iif_enum_mbus_code,
+ .enum_frame_size = iif_enum_frame_size,
+ .get_fmt = vsp1_subdev_get_pad_format,
+ .set_fmt = iif_set_format,
+};
+
+static const struct v4l2_subdev_ops iif_ops = {
+ .pad = &iif_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * VSP1 Entity Operations
+ */
+
+static void iif_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
+ struct vsp1_pipeline *pipe,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ vsp1_iif_write(dlb, VI6_IIF_CTRL, VI6_IIF_CTRL_CTRL);
+}
+
+static const struct vsp1_entity_operations iif_entity_ops = {
+ .configure_stream = iif_configure_stream,
+};
+
+/* -----------------------------------------------------------------------------
+ * Initialization and Cleanup
+ */
+
+struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1)
+{
+ struct vsp1_iif *iif;
+ int ret;
+
+ iif = devm_kzalloc(vsp1->dev, sizeof(*iif), GFP_KERNEL);
+ if (!iif)
+ return ERR_PTR(-ENOMEM);
+
+ iif->entity.ops = &iif_entity_ops;
+ iif->entity.type = VSP1_ENTITY_IIF;
+
+ /*
+ * The IIF is never exposed to userspace, but media entity registration
+ * requires a function to be set. Use PROC_VIDEO_PIXEL_FORMATTER just to
+ * avoid triggering a WARN_ON(), the value won't be seen anywhere.
+ */
+ ret = vsp1_entity_init(vsp1, &iif->entity, "iif", 3, &iif_ops,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ return iif;
+}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_iif.h b/drivers/media/platform/renesas/vsp1/vsp1_iif.h
new file mode 100644
index 000000000000..46f327851c35
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_iif.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_iif.h -- R-Car VSP1 IIF (ISP Interface)
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Corporation
+ */
+#ifndef __VSP1_IIF_H__
+#define __VSP1_IIF_H__
+
+#include <media/v4l2-subdev.h>
+
+#include "vsp1_entity.h"
+
+#define VSPX_IIF_SINK_PAD_IMG 0
+#define VSPX_IIF_SINK_PAD_CONFIG 2
+
+struct vsp1_iif {
+ struct vsp1_entity entity;
+};
+
+static inline struct vsp1_iif *to_iif(struct v4l2_subdev *subdev)
+{
+ return container_of(subdev, struct vsp1_iif, entity.subdev);
+}
+
+struct vsp1_iif *vsp1_iif_create(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_IIF_H__ */
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lif.c b/drivers/media/platform/renesas/vsp1/vsp1_lif.c
index b1d21a54837b..b3d83d1c5306 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_lif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lif.c
@@ -83,6 +83,7 @@ static const struct v4l2_subdev_ops lif_ops = {
*/
static void lif_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -93,8 +94,7 @@ static void lif_configure_stream(struct vsp1_entity *entity,
unsigned int obth;
unsigned int lbth;
- format = vsp1_entity_get_pad_format(&lif->entity, lif->entity.state,
- LIF_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, LIF_PAD_SOURCE);
switch (entity->vsp1->version & VI6_IP_VERSION_MODEL_MASK) {
case VI6_IP_VERSION_MODEL_VSPD_GEN2:
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
index 451d24ab0b56..dd264e6532e0 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
@@ -146,6 +146,7 @@ static const struct v4l2_subdev_ops lut_ops = {
*/
static void lut_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
index 68d05243c3ee..3cbb768cf6ad 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
@@ -138,14 +138,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 32, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_1X32,
- VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 24, 0, 0 }, false, false, 1, 1, false },
- { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_1X32,
- VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 32, 0, 0 }, false, false, 1, 1, false },
{ V4L2_PIX_FMT_RGBX1010102, MEDIA_BUS_FMT_ARGB8888_1X32,
VI6_FMT_RGB10_RGB10A2_A2RGB10,
VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS,
@@ -162,10 +154,6 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
1, { 16, 0, 0 }, false, false, 2, 1, false },
- { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32,
- VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
- VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
- 1, { 16, 0, 0 }, false, true, 2, 1, false },
{ V4L2_PIX_FMT_YUYV, MEDIA_BUS_FMT_AYUV8_1X32,
VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
@@ -222,6 +210,24 @@ static const struct vsp1_format_info vsp1_video_formats[] = {
1, { 32, 0, 0 }, false, false, 2, 1, false },
};
+static const struct vsp1_format_info vsp1_video_gen2_formats[] = {
+ { V4L2_PIX_FMT_VYUY, MEDIA_BUS_FMT_AYUV8_1X32,
+ VI6_FMT_YUYV_422, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 16, 0, 0 }, false, true, 2, 1, false },
+};
+
+static const struct vsp1_format_info vsp1_video_hsit_formats[] = {
+ { V4L2_PIX_FMT_HSV24, MEDIA_BUS_FMT_AHSV8888_1X32,
+ VI6_FMT_RGB_888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 24, 0, 0 }, false, false, 1, 1, false },
+ { V4L2_PIX_FMT_HSV32, MEDIA_BUS_FMT_AHSV8888_1X32,
+ VI6_FMT_ARGB_8888, VI6_RPF_DSWAP_P_LLS | VI6_RPF_DSWAP_P_LWS |
+ VI6_RPF_DSWAP_P_WDS | VI6_RPF_DSWAP_P_BTS,
+ 1, { 32, 0, 0 }, false, false, 1, 1, false },
+};
+
/**
* vsp1_get_format_info - Retrieve format information for a 4CC
* @vsp1: the VSP1 device
@@ -235,26 +241,164 @@ const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
{
unsigned int i;
- /* Special case, the VYUY and HSV formats are supported on Gen2 only. */
- if (vsp1->info->gen != 2) {
- switch (fourcc) {
- case V4L2_PIX_FMT_VYUY:
- case V4L2_PIX_FMT_HSV24:
- case V4L2_PIX_FMT_HSV32:
- return NULL;
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
+ const struct vsp1_format_info *info = &vsp1_video_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+
+ if (vsp1->info->gen == 2) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_gen2_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_hsit_formats[i];
+
+ if (info->fourcc == fourcc)
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * vsp1_get_format_info_by_index - Enumerate format information
+ * @vsp1: the VSP1 device
+ * @index: the format index
+ * @code: media bus code to limit enumeration
+ *
+ * Return a pointer to the format information structure corresponding to the
+ * given index, or NULL if the index exceeds the supported formats list. If the
+ * @code parameter is not zero, only formats compatible with the media bus code
+ * will be enumerated.
+ */
+const struct vsp1_format_info *
+vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
+ u32 code)
+{
+ unsigned int i;
+
+ if (!code) {
+ if (index < ARRAY_SIZE(vsp1_video_formats))
+ return &vsp1_video_formats[index];
+
+ if (vsp1->info->gen == 2) {
+ index -= ARRAY_SIZE(vsp1_video_formats);
+ if (index < ARRAY_SIZE(vsp1_video_gen2_formats))
+ return &vsp1_video_gen2_formats[index];
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ index -= ARRAY_SIZE(vsp1_video_gen2_formats);
+ if (index < ARRAY_SIZE(vsp1_video_hsit_formats))
+ return &vsp1_video_hsit_formats[index];
}
+
+ return NULL;
}
for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
const struct vsp1_format_info *info = &vsp1_video_formats[i];
- if (info->fourcc == fourcc)
- return info;
+ if (info->mbus == code) {
+ if (!index)
+ return info;
+ index--;
+ }
+ }
+
+ if (vsp1->info->gen == 2) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_gen2_formats[i];
+
+ if (info->mbus == code) {
+ if (!index)
+ return info;
+ index--;
+ }
+ }
+ }
+
+ if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
+ for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) {
+ const struct vsp1_format_info *info =
+ &vsp1_video_hsit_formats[i];
+
+ if (info->mbus == code) {
+ if (!index)
+ return info;
+ index--;
+ }
+ }
}
return NULL;
}
+/**
+ * vsp1_adjust_color_space - Adjust color space fields in a format
+ * @code: the media bus code
+ * @colorspace: the colorspace
+ * @xfer_func: the transfer function
+ * @encoding: the encoding
+ * @quantization: the quantization
+ *
+ * This function adjusts all color space fields of a video device of subdev
+ * format structure, taking into account the requested format, requested color
+ * space and limitations of the VSP1. It should be used in the video device and
+ * subdev set format handlers.
+ *
+ * The colorspace and xfer_func fields are freely configurable, as they are out
+ * of scope for VSP processing. The encoding and quantization is hardcoded for
+ * non-YUV formats, and can be configured for YUV formats.
+ */
+void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func,
+ u8 *encoding, u8 *quantization)
+{
+ if (*colorspace == V4L2_COLORSPACE_DEFAULT ||
+ *colorspace >= V4L2_COLORSPACE_LAST)
+ *colorspace = code == MEDIA_BUS_FMT_AYUV8_1X32
+ ? V4L2_COLORSPACE_SMPTE170M
+ : V4L2_COLORSPACE_SRGB;
+
+ if (*xfer_func == V4L2_XFER_FUNC_DEFAULT ||
+ *xfer_func >= V4L2_XFER_FUNC_LAST)
+ *xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(*colorspace);
+
+ switch (code) {
+ case MEDIA_BUS_FMT_ARGB8888_1X32:
+ default:
+ *encoding = V4L2_YCBCR_ENC_601;
+ *quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+
+ case MEDIA_BUS_FMT_AHSV8888_1X32:
+ *encoding = V4L2_HSV_ENC_256;
+ *quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+
+ case MEDIA_BUS_FMT_AYUV8_1X32:
+ if (*encoding != V4L2_YCBCR_ENC_601 &&
+ *encoding != V4L2_YCBCR_ENC_709)
+ *encoding = V4L2_YCBCR_ENC_601;
+ if (*quantization != V4L2_QUANTIZATION_FULL_RANGE &&
+ *quantization != V4L2_QUANTIZATION_LIM_RANGE)
+ *quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ }
+}
+
/* -----------------------------------------------------------------------------
* Pipeline Management
*/
@@ -286,6 +430,7 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe)
pipe->brx = NULL;
pipe->hgo = NULL;
pipe->hgt = NULL;
+ pipe->iif = NULL;
pipe->lif = NULL;
pipe->uds = NULL;
}
@@ -301,6 +446,28 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe)
pipe->state = VSP1_PIPELINE_STOPPED;
}
+void __vsp1_pipeline_dump(struct _ddebug *dbg, struct vsp1_pipeline *pipe,
+ const char *msg)
+{
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+ struct vsp1_entity *entity;
+ bool first = true;
+
+ printk(KERN_DEBUG "%s: %s: pipe: ", dev_name(vsp1->dev), msg);
+
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ const char *name;
+
+ name = strchrnul(entity->subdev.name, ' ');
+ name = name ? name + 1 : entity->subdev.name;
+
+ pr_cont("%s%s", first ? "" : ", ", name);
+ first = false;
+ }
+
+ pr_cont("\n");
+}
+
/* Must be called with the pipe irqlock held. */
void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
{
@@ -444,6 +611,10 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
vsp1_uds_set_alpha(pipe->uds, dlb, alpha);
}
+/* -----------------------------------------------------------------------------
+ * VSP1 Partition Algorithm support
+ */
+
/*
* Propagate the partition calculations through the pipeline
*
@@ -452,17 +623,82 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
* source. Each entity must produce the partition required for the previous
* entity in the pipeline.
*/
-void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
- struct vsp1_partition *partition,
- unsigned int index,
- struct vsp1_partition_window *window)
+static void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
+ struct vsp1_partition *partition,
+ unsigned int index,
+ struct v4l2_rect *window)
{
struct vsp1_entity *entity;
list_for_each_entry_reverse(entity, &pipe->entities, list_pipe) {
if (entity->ops->partition)
- entity->ops->partition(entity, pipe, partition, index,
- window);
+ entity->ops->partition(entity, entity->state, pipe,
+ partition, index, window);
}
}
+/*
+ * vsp1_pipeline_calculate_partition - Calculate pipeline configuration for a
+ * partition
+ *
+ * @pipe: the pipeline
+ * @partition: partition that will hold the calculated values
+ * @div_size: pre-determined maximum partition division size
+ * @index: partition index
+ */
+void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe,
+ struct vsp1_partition *partition,
+ unsigned int div_size,
+ unsigned int index)
+{
+ const struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect window;
+ unsigned int modulus;
+
+ /*
+ * Partitions are computed on the size before rotation, use the format
+ * at the WPF sink.
+ */
+ format = v4l2_subdev_state_get_format(pipe->output->entity.state,
+ RWPF_PAD_SINK);
+
+ /* Initialise the partition with sane starting conditions. */
+ window.left = index * div_size;
+ window.width = div_size;
+ window.top = 0;
+ window.height = format->height;
+
+ modulus = format->width % div_size;
+
+ /*
+ * We need to prevent the last partition from being smaller than the
+ * *minimum* width of the hardware capabilities.
+ *
+ * If the modulus is less than half of the partition size,
+ * the penultimate partition is reduced to half, which is added
+ * to the final partition: |1234|1234|1234|12|341|
+ * to prevent this: |1234|1234|1234|1234|1|.
+ */
+ if (modulus) {
+ /*
+ * pipe->partitions is 1 based, whilst index is a 0 based index.
+ * Normalise this locally.
+ */
+ unsigned int partitions = pipe->partitions - 1;
+
+ if (modulus < div_size / 2) {
+ if (index == partitions - 1) {
+ /* Halve the penultimate partition. */
+ window.width = div_size / 2;
+ } else if (index == partitions) {
+ /* Increase the final partition. */
+ window.width = (div_size / 2) + modulus;
+ window.left -= div_size / 2;
+ }
+ } else if (index == partitions) {
+ window.width = modulus;
+ }
+ }
+
+ vsp1_pipeline_propagate_partition(pipe, partition, index, &window);
+}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
index 674b5748d929..7f623b8cbe5c 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.h
@@ -9,6 +9,7 @@
#ifndef __VSP1_PIPE_H__
#define __VSP1_PIPE_H__
+#include <linux/dynamic_debug.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@@ -54,17 +55,6 @@ enum vsp1_pipeline_state {
};
/*
- * struct vsp1_partition_window - Partition window coordinates
- * @left: horizontal coordinate of the partition start in pixels relative to the
- * left edge of the image
- * @width: partition width in pixels
- */
-struct vsp1_partition_window {
- unsigned int left;
- unsigned int width;
-};
-
-/*
* struct vsp1_partition - A description of a slice for the partition algorithm
* @rpf: The RPF partition window configuration
* @uds_sink: The UDS input partition window configuration
@@ -73,11 +63,11 @@ struct vsp1_partition_window {
* @wpf: The WPF partition window configuration
*/
struct vsp1_partition {
- struct vsp1_partition_window rpf;
- struct vsp1_partition_window uds_sink;
- struct vsp1_partition_window uds_source;
- struct vsp1_partition_window sru;
- struct vsp1_partition_window wpf;
+ struct v4l2_rect rpf[VSP1_MAX_RPF];
+ struct v4l2_rect uds_sink;
+ struct v4l2_rect uds_source;
+ struct v4l2_rect sru;
+ struct v4l2_rect wpf;
};
/*
@@ -106,7 +96,6 @@ struct vsp1_partition {
* @configured: when false the @stream_config shall be written to the hardware
* @interlaced: True when the pipeline is configured in interlaced mode
* @partitions: The number of partitions used to process one frame
- * @partition: The current partition for configuration to process
* @part_table: The pre-calculated partitions used by the pipeline
*/
struct vsp1_pipeline {
@@ -130,6 +119,7 @@ struct vsp1_pipeline {
struct vsp1_entity *brx;
struct vsp1_entity *hgo;
struct vsp1_entity *hgt;
+ struct vsp1_entity *iif;
struct vsp1_entity *lif;
struct vsp1_entity *uds;
struct vsp1_entity *uds_input;
@@ -146,7 +136,6 @@ struct vsp1_pipeline {
bool interlaced;
unsigned int partitions;
- struct vsp1_partition *partition;
struct vsp1_partition *part_table;
u32 underrun_count;
@@ -155,6 +144,24 @@ struct vsp1_pipeline {
void vsp1_pipeline_reset(struct vsp1_pipeline *pipe);
void vsp1_pipeline_init(struct vsp1_pipeline *pipe);
+void __vsp1_pipeline_dump(struct _ddebug *, struct vsp1_pipeline *pipe,
+ const char *msg);
+
+#if defined(CONFIG_DYNAMIC_DEBUG) || \
+ (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
+#define vsp1_pipeline_dump(pipe, msg) \
+ _dynamic_func_call("vsp1_pipeline_dump()", __vsp1_pipeline_dump, pipe, msg)
+#elif defined(DEBUG)
+#define vsp1_pipeline_dump(pipe, msg) \
+ __vsp1_pipeline_dump(NULL, pipe, msg)
+#else
+#define vsp1_pipeline_dump(pipe, msg) \
+({ \
+ if (0) \
+ __vsp1_pipeline_dump(NULL, pipe, msg); \
+})
+#endif
+
void vsp1_pipeline_run(struct vsp1_pipeline *pipe);
bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe);
int vsp1_pipeline_stop(struct vsp1_pipeline *pipe);
@@ -166,12 +173,17 @@ void vsp1_pipeline_propagate_alpha(struct vsp1_pipeline *pipe,
struct vsp1_dl_body *dlb,
unsigned int alpha);
-void vsp1_pipeline_propagate_partition(struct vsp1_pipeline *pipe,
+void vsp1_pipeline_calculate_partition(struct vsp1_pipeline *pipe,
struct vsp1_partition *partition,
- unsigned int index,
- struct vsp1_partition_window *window);
+ unsigned int div_size,
+ unsigned int index);
const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
u32 fourcc);
+const struct vsp1_format_info *
+vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
+ u32 code);
+void vsp1_adjust_color_space(u32 code, u32 *colorspace, u8 *xfer_func,
+ u8 *encoding, u8 *quantization);
#endif /* __VSP1_PIPE_H__ */
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
index 7eca82e0ba7e..86e47c2d991f 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
@@ -253,6 +253,13 @@
#define VI6_RPF_BRDITH_CTRL_CBRM BIT(0)
/* -----------------------------------------------------------------------------
+ * IIF Control Registers
+ */
+
+#define VI6_IIF_CTRL 0x0608
+#define VI6_IIF_CTRL_CTRL 0x13
+
+/* -----------------------------------------------------------------------------
* WPF Control Registers
*/
@@ -388,6 +395,7 @@
#define VI6_DPR_HST_ROUTE 0x2044
#define VI6_DPR_HSI_ROUTE 0x2048
#define VI6_DPR_BRU_ROUTE 0x204c
+#define VI6_DPR_ROUTE_IIFSEL BIT(28)
#define VI6_DPR_ILV_BRS_ROUTE 0x2050
#define VI6_DPR_ROUTE_BRSSEL BIT(28)
#define VI6_DPR_ROUTE_FXA_MASK (0xff << 16)
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
index c47579efc65f..811f2b7c5cc5 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rpf.c
@@ -48,6 +48,7 @@ static inline void vsp1_rpf_write(struct vsp1_rwpf *rpf,
*/
static void rpf_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -80,14 +81,10 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
vsp1_rpf_write(rpf, dlb, VI6_RPF_SRCM_PSTRIDE, pstride);
/* Format */
- sink_format = vsp1_entity_get_pad_format(&rpf->entity,
- rpf->entity.state,
- RWPF_PAD_SINK);
- source_format = vsp1_entity_get_pad_format(&rpf->entity,
- rpf->entity.state,
- RWPF_PAD_SOURCE);
-
- infmt = VI6_RPF_INFMT_CIPM
+ sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
+ source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
+
+ infmt = (pipe->iif ? 0 : VI6_RPF_INFMT_CIPM)
| (fmtinfo->hwfmt << VI6_RPF_INFMT_RDFMT_SHIFT);
if (fmtinfo->swap_yc)
@@ -95,12 +92,44 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
if (fmtinfo->swap_uv)
infmt |= VI6_RPF_INFMT_SPUVS;
- if (sink_format->code != source_format->code)
- infmt |= VI6_RPF_INFMT_CSC;
+ if (sink_format->code != source_format->code) {
+ u16 ycbcr_enc;
+ u16 quantization;
+ u32 rdtm;
+
+ if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
+ ycbcr_enc = sink_format->ycbcr_enc;
+ quantization = sink_format->quantization;
+ } else {
+ ycbcr_enc = source_format->ycbcr_enc;
+ quantization = source_format->quantization;
+ }
+
+ if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ rdtm = VI6_RPF_INFMT_RDTM_BT601;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ rdtm = VI6_RPF_INFMT_RDTM_BT601_EXT;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_709 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ rdtm = VI6_RPF_INFMT_RDTM_BT709;
+ else
+ rdtm = VI6_RPF_INFMT_RDTM_BT709_EXT;
+
+ infmt |= VI6_RPF_INFMT_CSC | rdtm;
+ }
vsp1_rpf_write(rpf, dlb, VI6_RPF_INFMT, infmt);
vsp1_rpf_write(rpf, dlb, VI6_RPF_DSWAP, fmtinfo->swap);
+ /* No further configuration for VSPX. */
+ if (pipe->iif) {
+ /* VSPX wants alpha_sel to be set to 0. */
+ vsp1_rpf_write(rpf, dlb, VI6_RPF_ALPH_SEL, 0);
+ return;
+ }
+
if (entity->vsp1->info->gen == 4) {
u32 ext_infmt0;
u32 ext_infmt1;
@@ -157,10 +186,8 @@ static void rpf_configure_stream(struct vsp1_entity *entity,
if (pipe->brx) {
const struct v4l2_rect *compose;
- compose = vsp1_entity_get_pad_selection(pipe->brx,
- pipe->brx->state,
- rpf->brx_input,
- V4L2_SEL_TGT_COMPOSE);
+ compose = v4l2_subdev_state_get_compose(pipe->brx->state,
+ rpf->brx_input);
left = compose->left;
top = compose->top;
}
@@ -284,6 +311,7 @@ static void rpf_configure_frame(struct vsp1_entity *entity,
static void rpf_configure_partition(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
+ const struct vsp1_partition *partition,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
{
@@ -292,7 +320,7 @@ static void rpf_configure_partition(struct vsp1_entity *entity,
struct vsp1_device *vsp1 = rpf->entity.vsp1;
const struct vsp1_format_info *fmtinfo = rpf->fmtinfo;
const struct v4l2_pix_format_mplane *format = &rpf->format;
- struct v4l2_rect crop;
+ struct v4l2_rect crop = partition->rpf[rpf->entity.index];
/*
* Source size and crop offsets.
@@ -302,22 +330,6 @@ static void rpf_configure_partition(struct vsp1_entity *entity,
* offsets are needed, as planes 2 and 3 always have identical
* strides.
*/
- crop = *vsp1_rwpf_get_crop(rpf, rpf->entity.state);
-
- /*
- * Partition Algorithm Control
- *
- * The partition algorithm can split this frame into multiple
- * slices. We must scale our partition window based on the pipe
- * configuration to match the destination partition window.
- * To achieve this, we adjust our crop to provide a 'sub-crop'
- * matching the expected partition window. Only 'left' and
- * 'width' need to be adjusted.
- */
- if (pipe->partitions > 1) {
- crop.width = pipe->partition->rpf.width;
- crop.left += pipe->partition->rpf.left;
- }
if (pipe->interlaced) {
crop.height = round_down(crop.height / 2, fmtinfo->vsub);
@@ -366,12 +378,30 @@ static void rpf_configure_partition(struct vsp1_entity *entity,
}
static void rpf_partition(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_partition *partition,
unsigned int partition_idx,
- struct vsp1_partition_window *window)
+ struct v4l2_rect *window)
{
- partition->rpf = *window;
+ struct vsp1_rwpf *rpf = to_rwpf(&entity->subdev);
+ struct v4l2_rect *rpf_rect = &partition->rpf[rpf->entity.index];
+
+ /*
+ * Partition Algorithm Control
+ *
+ * The partition algorithm can split this frame into multiple slices. We
+ * must adjust our partition window based on the pipe configuration to
+ * match the destination partition window. To achieve this, we adjust
+ * our crop to provide a 'sub-crop' matching the expected partition
+ * window.
+ */
+ *rpf_rect = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
+
+ if (pipe->partitions > 1) {
+ rpf_rect->width = window->width;
+ rpf_rect->left += window->left;
+ }
}
static const struct vsp1_entity_operations rpf_entity_ops = {
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
index 09fb6ffa14e2..9c8085d5d306 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
@@ -10,18 +10,13 @@
#include <media/v4l2-subdev.h>
#include "vsp1.h"
+#include "vsp1_entity.h"
#include "vsp1_rwpf.h"
#include "vsp1_video.h"
#define RWPF_MIN_WIDTH 1
#define RWPF_MIN_HEIGHT 1
-struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
- struct v4l2_subdev_state *sd_state)
-{
- return v4l2_subdev_state_get_crop(sd_state, RWPF_PAD_SINK);
-}
-
/* -----------------------------------------------------------------------------
* V4L2 Subdevice Operations
*/
@@ -41,6 +36,11 @@ static int vsp1_rwpf_enum_mbus_code(struct v4l2_subdev *subdev,
code->code = codes[code->index];
+ if (code->pad == RWPF_PAD_SOURCE &&
+ code->code == MEDIA_BUS_FMT_AYUV8_1X32)
+ code->flags = V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC
+ | V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION;
+
return 0;
}
@@ -79,15 +79,48 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
fmt->format.code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->format.code = MEDIA_BUS_FMT_AYUV8_1X32;
- format = vsp1_entity_get_pad_format(&rwpf->entity, state, fmt->pad);
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
if (fmt->pad == RWPF_PAD_SOURCE) {
+ const struct v4l2_mbus_framefmt *sink_format =
+ v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
+ u16 flags = fmt->format.flags & V4L2_MBUS_FRAMEFMT_SET_CSC;
+ bool csc;
+
/*
* The RWPF performs format conversion but can't scale, only the
- * format code can be changed on the source pad.
+ * format code, encoding and quantization can be changed on the
+ * source pad when converting between RGB and YUV.
+ */
+ if (sink_format->code != MEDIA_BUS_FMT_AHSV8888_1X32 &&
+ fmt->format.code != MEDIA_BUS_FMT_AHSV8888_1X32)
+ format->code = fmt->format.code;
+ else
+ format->code = sink_format->code;
+
+ /*
+ * Encoding and quantization can only be configured when YCbCr
+ * <-> RGB is enabled. The V4L2 API requires userspace to set
+ * the V4L2_MBUS_FRAMEFMT_SET_CSC flag. If either of these
+ * conditions is not met, use the encoding and quantization
+ * values from the sink pad.
*/
- format->code = fmt->format.code;
+ csc = (format->code == MEDIA_BUS_FMT_AYUV8_1X32) !=
+ (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32);
+
+ if (csc && (flags & V4L2_MBUS_FRAMEFMT_SET_CSC)) {
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+ } else {
+ format->ycbcr_enc = sink_format->ycbcr_enc;
+ format->quantization = sink_format->quantization;
+ }
+
+ vsp1_entity_adjust_color_space(format);
+
fmt->format = *format;
+ fmt->format.flags = flags;
+
goto done;
}
@@ -97,7 +130,13 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
format->height = clamp_t(unsigned int, fmt->format.height,
RWPF_MIN_HEIGHT, rwpf->max_height);
format->field = V4L2_FIELD_NONE;
- format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ format->colorspace = fmt->format.colorspace;
+ format->xfer_func = fmt->format.xfer_func;
+ format->ycbcr_enc = fmt->format.ycbcr_enc;
+ format->quantization = fmt->format.quantization;
+
+ vsp1_entity_adjust_color_space(format);
fmt->format = *format;
@@ -105,7 +144,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
struct v4l2_rect *crop;
/* Update the sink crop rectangle. */
- crop = vsp1_rwpf_get_crop(rwpf, state);
+ crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
crop->left = 0;
crop->top = 0;
crop->width = fmt->format.width;
@@ -113,8 +152,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
}
/* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&rwpf->entity, state,
- RWPF_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
*format = fmt->format;
if (rwpf->flip.rotate) {
@@ -153,12 +191,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
- sel->r = *vsp1_rwpf_get_crop(rwpf, state);
+ sel->r = *v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
break;
case V4L2_SEL_TGT_CROP_BOUNDS:
- format = vsp1_entity_get_pad_format(&rwpf->entity, state,
- RWPF_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = format->width;
@@ -204,8 +241,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
}
/* Make sure the crop rectangle is entirely contained in the image. */
- format = vsp1_entity_get_pad_format(&rwpf->entity, state,
- RWPF_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
/*
* Restrict the crop rectangle coordinates to multiples of 2 to avoid
@@ -225,12 +261,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
sel->r.height = min_t(unsigned int, sel->r.height,
format->height - sel->r.top);
- crop = vsp1_rwpf_get_crop(rwpf, state);
+ crop = v4l2_subdev_state_get_crop(state, RWPF_PAD_SINK);
*crop = sel->r;
/* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&rwpf->entity, state,
- RWPF_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
format->width = crop->width;
format->height = crop->height;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
index e0d212c70b2f..5ac9f0a6fafc 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.h
@@ -85,7 +85,4 @@ int vsp1_rwpf_init_ctrls(struct vsp1_rwpf *rwpf, unsigned int ncontrols);
extern const struct v4l2_subdev_ops vsp1_rwpf_subdev_ops;
-struct v4l2_rect *vsp1_rwpf_get_crop(struct vsp1_rwpf *rwpf,
- struct v4l2_subdev_state *sd_state);
-
#endif /* __VSP1_RWPF_H__ */
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
index 11e008aa9f20..bba2872afaf2 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
@@ -14,6 +14,7 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_pipe.h"
#include "vsp1_sru.h"
@@ -131,7 +132,7 @@ static int sru_enum_frame_size(struct v4l2_subdev *subdev,
if (!state)
return -EINVAL;
- format = vsp1_entity_get_pad_format(&sru->entity, state, SRU_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
mutex_lock(&sru->entity.lock);
@@ -178,16 +179,22 @@ static void sru_try_format(struct vsp1_sru *sru,
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+ vsp1_entity_adjust_color_space(fmt);
+
fmt->width = clamp(fmt->width, SRU_MIN_SIZE, SRU_MAX_SIZE);
fmt->height = clamp(fmt->height, SRU_MIN_SIZE, SRU_MAX_SIZE);
break;
case SRU_PAD_SOURCE:
/* The SRU can't perform format conversion. */
- format = vsp1_entity_get_pad_format(&sru->entity, sd_state,
- SRU_PAD_SINK);
+ format = v4l2_subdev_state_get_format(sd_state, SRU_PAD_SINK);
fmt->code = format->code;
+ fmt->colorspace = format->colorspace;
+ fmt->xfer_func = format->xfer_func;
+ fmt->ycbcr_enc = format->ycbcr_enc;
+ fmt->quantization = format->quantization;
+
/*
* We can upscale by 2 in both direction, but not independently.
* Compare the input and output rectangles areas (avoiding
@@ -212,7 +219,6 @@ static void sru_try_format(struct vsp1_sru *sru,
}
fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int sru_set_format(struct v4l2_subdev *subdev,
@@ -234,13 +240,12 @@ static int sru_set_format(struct v4l2_subdev *subdev,
sru_try_format(sru, state, fmt->pad, &fmt->format);
- format = vsp1_entity_get_pad_format(&sru->entity, state, fmt->pad);
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
*format = fmt->format;
if (fmt->pad == SRU_PAD_SINK) {
/* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&sru->entity, state,
- SRU_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
*format = fmt->format;
sru_try_format(sru, state, SRU_PAD_SOURCE, format);
@@ -267,6 +272,7 @@ static const struct v4l2_subdev_ops sru_ops = {
*/
static void sru_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -277,10 +283,8 @@ static void sru_configure_stream(struct vsp1_entity *entity,
struct v4l2_mbus_framefmt *output;
u32 ctrl0;
- input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
- SRU_PAD_SINK);
- output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
- SRU_PAD_SOURCE);
+ input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
+ output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
if (input->code == MEDIA_BUS_FMT_ARGB8888_1X32)
ctrl0 = VI6_SRU_CTRL0_PARAM2 | VI6_SRU_CTRL0_PARAM3
@@ -301,16 +305,14 @@ static void sru_configure_stream(struct vsp1_entity *entity,
}
static unsigned int sru_max_width(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe)
{
- struct vsp1_sru *sru = to_sru(&entity->subdev);
struct v4l2_mbus_framefmt *input;
struct v4l2_mbus_framefmt *output;
- input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
- SRU_PAD_SINK);
- output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
- SRU_PAD_SOURCE);
+ input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
+ output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
/*
* The maximum input width of the SRU is 288 input pixels, but 32
@@ -324,24 +326,24 @@ static unsigned int sru_max_width(struct vsp1_entity *entity,
}
static void sru_partition(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_partition *partition,
unsigned int partition_idx,
- struct vsp1_partition_window *window)
+ struct v4l2_rect *window)
{
- struct vsp1_sru *sru = to_sru(&entity->subdev);
struct v4l2_mbus_framefmt *input;
struct v4l2_mbus_framefmt *output;
- input = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
- SRU_PAD_SINK);
- output = vsp1_entity_get_pad_format(&sru->entity, sru->entity.state,
- SRU_PAD_SOURCE);
+ input = v4l2_subdev_state_get_format(state, SRU_PAD_SINK);
+ output = v4l2_subdev_state_get_format(state, SRU_PAD_SOURCE);
/* Adapt if SRUx2 is enabled. */
if (input->width != output->width) {
window->width /= 2;
window->left /= 2;
+ window->height /= 2;
+ window->top /= 2;
}
partition->sru = *window;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
index d89f1197b86c..2db473b6f83c 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
@@ -14,6 +14,7 @@
#include "vsp1.h"
#include "vsp1_dl.h"
+#include "vsp1_entity.h"
#include "vsp1_pipe.h"
#include "vsp1_uds.h"
@@ -136,7 +137,7 @@ static int uds_enum_frame_size(struct v4l2_subdev *subdev,
if (!state)
return -EINVAL;
- format = vsp1_entity_get_pad_format(&uds->entity, state, UDS_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
mutex_lock(&uds->entity.lock);
@@ -177,16 +178,22 @@ static void uds_try_format(struct vsp1_uds *uds,
fmt->code != MEDIA_BUS_FMT_AYUV8_1X32)
fmt->code = MEDIA_BUS_FMT_AYUV8_1X32;
+ vsp1_entity_adjust_color_space(fmt);
+
fmt->width = clamp(fmt->width, UDS_MIN_SIZE, UDS_MAX_SIZE);
fmt->height = clamp(fmt->height, UDS_MIN_SIZE, UDS_MAX_SIZE);
break;
case UDS_PAD_SOURCE:
/* The UDS scales but can't perform format conversion. */
- format = vsp1_entity_get_pad_format(&uds->entity, sd_state,
- UDS_PAD_SINK);
+ format = v4l2_subdev_state_get_format(sd_state, UDS_PAD_SINK);
fmt->code = format->code;
+ fmt->colorspace = format->colorspace;
+ fmt->xfer_func = format->xfer_func;
+ fmt->ycbcr_enc = format->ycbcr_enc;
+ fmt->quantization = format->quantization;
+
uds_output_limits(format->width, &minimum, &maximum);
fmt->width = clamp(fmt->width, minimum, maximum);
uds_output_limits(format->height, &minimum, &maximum);
@@ -195,7 +202,6 @@ static void uds_try_format(struct vsp1_uds *uds,
}
fmt->field = V4L2_FIELD_NONE;
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
}
static int uds_set_format(struct v4l2_subdev *subdev,
@@ -217,13 +223,12 @@ static int uds_set_format(struct v4l2_subdev *subdev,
uds_try_format(uds, state, fmt->pad, &fmt->format);
- format = vsp1_entity_get_pad_format(&uds->entity, state, fmt->pad);
+ format = v4l2_subdev_state_get_format(state, fmt->pad);
*format = fmt->format;
if (fmt->pad == UDS_PAD_SINK) {
/* Propagate the format to the source pad. */
- format = vsp1_entity_get_pad_format(&uds->entity, state,
- UDS_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
*format = fmt->format;
uds_try_format(uds, state, UDS_PAD_SOURCE, format);
@@ -254,6 +259,7 @@ static const struct v4l2_subdev_ops uds_ops = {
*/
static void uds_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -265,10 +271,8 @@ static void uds_configure_stream(struct vsp1_entity *entity,
unsigned int vscale;
bool multitap;
- input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
- UDS_PAD_SINK);
- output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
- UDS_PAD_SOURCE);
+ input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
+ output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
hscale = uds_compute_ratio(input->width, output->width);
vscale = uds_compute_ratio(input->height, output->height);
@@ -303,15 +307,11 @@ static void uds_configure_stream(struct vsp1_entity *entity,
static void uds_configure_partition(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
+ const struct vsp1_partition *partition,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
{
struct vsp1_uds *uds = to_uds(&entity->subdev);
- struct vsp1_partition *partition = pipe->partition;
- const struct v4l2_mbus_framefmt *output;
-
- output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
- UDS_PAD_SOURCE);
/* Input size clipping. */
vsp1_uds_write(uds, dlb, VI6_UDS_HSZCLIP, VI6_UDS_HSZCLIP_HCEN |
@@ -323,22 +323,20 @@ static void uds_configure_partition(struct vsp1_entity *entity,
vsp1_uds_write(uds, dlb, VI6_UDS_CLIP_SIZE,
(partition->uds_source.width
<< VI6_UDS_CLIP_SIZE_HSIZE_SHIFT) |
- (output->height
+ (partition->uds_source.height
<< VI6_UDS_CLIP_SIZE_VSIZE_SHIFT));
}
static unsigned int uds_max_width(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe)
{
- struct vsp1_uds *uds = to_uds(&entity->subdev);
const struct v4l2_mbus_framefmt *output;
const struct v4l2_mbus_framefmt *input;
unsigned int hscale;
- input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
- UDS_PAD_SINK);
- output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
- UDS_PAD_SOURCE);
+ input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
+ output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
hscale = output->width / input->width;
/*
@@ -364,28 +362,26 @@ static unsigned int uds_max_width(struct vsp1_entity *entity,
*/
static void uds_partition(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_partition *partition,
unsigned int partition_idx,
- struct vsp1_partition_window *window)
+ struct v4l2_rect *window)
{
- struct vsp1_uds *uds = to_uds(&entity->subdev);
const struct v4l2_mbus_framefmt *output;
const struct v4l2_mbus_framefmt *input;
- /* Initialise the partition state. */
- partition->uds_sink = *window;
- partition->uds_source = *window;
-
- input = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
- UDS_PAD_SINK);
- output = vsp1_entity_get_pad_format(&uds->entity, uds->entity.state,
- UDS_PAD_SOURCE);
+ input = v4l2_subdev_state_get_format(state, UDS_PAD_SINK);
+ output = v4l2_subdev_state_get_format(state, UDS_PAD_SOURCE);
partition->uds_sink.width = window->width * input->width
/ output->width;
partition->uds_sink.left = window->left * input->width
/ output->width;
+ partition->uds_sink.height = input->height;
+ partition->uds_sink.top = 0;
+
+ partition->uds_source = *window;
*window = partition->uds_sink;
}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
index f66936a28a2a..edaf28b544d2 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
@@ -104,8 +104,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev,
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
case V4L2_SEL_TGT_CROP_DEFAULT:
- format = vsp1_entity_get_pad_format(&uif->entity, state,
- UIF_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK);
sel->r.left = 0;
sel->r.top = 0;
sel->r.width = format->width;
@@ -113,8 +112,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev,
break;
case V4L2_SEL_TGT_CROP:
- sel->r = *vsp1_entity_get_pad_selection(&uif->entity, state,
- sel->pad, sel->target);
+ sel->r = *v4l2_subdev_state_get_crop(state, sel->pad);
break;
default:
@@ -150,7 +148,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev,
}
/* The crop rectangle must be inside the input frame. */
- format = vsp1_entity_get_pad_format(&uif->entity, state, UIF_PAD_SINK);
+ format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK);
sel->r.left = clamp_t(unsigned int, sel->r.left, 0, format->width - 1);
sel->r.top = clamp_t(unsigned int, sel->r.top, 0, format->height - 1);
@@ -160,8 +158,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev,
format->height - sel->r.top);
/* Store the crop rectangle. */
- selection = vsp1_entity_get_pad_selection(&uif->entity, state,
- sel->pad, V4L2_SEL_TGT_CROP);
+ selection = v4l2_subdev_state_get_crop(state, sel->pad);
*selection = sel->r;
done:
@@ -191,6 +188,7 @@ static const struct v4l2_subdev_ops uif_ops = {
*/
static void uif_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -203,8 +201,7 @@ static void uif_configure_stream(struct vsp1_entity *entity,
vsp1_uif_write(uif, dlb, VI6_UIF_DISCOM_DOCMPMR,
VI6_UIF_DISCOM_DOCMPMR_SEL(9));
- crop = vsp1_entity_get_pad_selection(entity, entity->state,
- UIF_PAD_SINK, V4L2_SEL_TGT_CROP);
+ crop = v4l2_subdev_state_get_crop(state, UIF_PAD_SINK);
left = crop->left;
width = crop->width;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c
index 5a9cb0e5640e..bc66fbdde3cc 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_video.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c
@@ -78,8 +78,14 @@ static int vsp1_video_verify_format(struct vsp1_video *video)
if (video->rwpf->fmtinfo->mbus != fmt.format.code ||
video->rwpf->format.height != fmt.format.height ||
- video->rwpf->format.width != fmt.format.width)
- return -EINVAL;
+ video->rwpf->format.width != fmt.format.width) {
+ dev_dbg(video->vsp1->dev,
+ "Format mismatch: 0x%04x/%ux%u != 0x%04x/%ux%u\n",
+ video->rwpf->fmtinfo->mbus, video->rwpf->format.width,
+ video->rwpf->format.height, fmt.format.code,
+ fmt.format.width, fmt.format.height);
+ return -EPIPE;
+ }
return 0;
}
@@ -121,12 +127,24 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
info = vsp1_get_format_info(video->vsp1, VSP1_VIDEO_DEF_FORMAT);
pix->pixelformat = info->fourcc;
- pix->colorspace = V4L2_COLORSPACE_SRGB;
pix->field = V4L2_FIELD_NONE;
- if (info->fourcc == V4L2_PIX_FMT_HSV24 ||
- info->fourcc == V4L2_PIX_FMT_HSV32)
- pix->hsv_enc = V4L2_HSV_ENC_256;
+ /*
+ * Adjust the colour space fields. On capture devices, userspace needs
+ * to set the V4L2_PIX_FMT_FLAG_SET_CSC to override the defaults. Reset
+ * all fields to *_DEFAULT if the flag isn't set, to then handle
+ * capture and output devices in the same way.
+ */
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ !(pix->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
+ pix->colorspace = V4L2_COLORSPACE_DEFAULT;
+ pix->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ pix->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ pix->quantization = V4L2_QUANTIZATION_DEFAULT;
+ }
+
+ vsp1_adjust_color_space(info->mbus, &pix->colorspace, &pix->xfer_func,
+ &pix->ycbcr_enc, &pix->quantization);
memset(pix->reserved, 0, sizeof(pix->reserved));
@@ -173,131 +191,6 @@ static int __vsp1_video_try_format(struct vsp1_video *video,
}
/* -----------------------------------------------------------------------------
- * VSP1 Partition Algorithm support
- */
-
-/**
- * vsp1_video_calculate_partition - Calculate the active partition output window
- *
- * @pipe: the pipeline
- * @partition: partition that will hold the calculated values
- * @div_size: pre-determined maximum partition division size
- * @index: partition index
- */
-static void vsp1_video_calculate_partition(struct vsp1_pipeline *pipe,
- struct vsp1_partition *partition,
- unsigned int div_size,
- unsigned int index)
-{
- const struct v4l2_mbus_framefmt *format;
- struct vsp1_partition_window window;
- unsigned int modulus;
-
- /*
- * Partitions are computed on the size before rotation, use the format
- * at the WPF sink.
- */
- format = vsp1_entity_get_pad_format(&pipe->output->entity,
- pipe->output->entity.state,
- RWPF_PAD_SINK);
-
- /* A single partition simply processes the output size in full. */
- if (pipe->partitions <= 1) {
- window.left = 0;
- window.width = format->width;
-
- vsp1_pipeline_propagate_partition(pipe, partition, index,
- &window);
- return;
- }
-
- /* Initialise the partition with sane starting conditions. */
- window.left = index * div_size;
- window.width = div_size;
-
- modulus = format->width % div_size;
-
- /*
- * We need to prevent the last partition from being smaller than the
- * *minimum* width of the hardware capabilities.
- *
- * If the modulus is less than half of the partition size,
- * the penultimate partition is reduced to half, which is added
- * to the final partition: |1234|1234|1234|12|341|
- * to prevent this: |1234|1234|1234|1234|1|.
- */
- if (modulus) {
- /*
- * pipe->partitions is 1 based, whilst index is a 0 based index.
- * Normalise this locally.
- */
- unsigned int partitions = pipe->partitions - 1;
-
- if (modulus < div_size / 2) {
- if (index == partitions - 1) {
- /* Halve the penultimate partition. */
- window.width = div_size / 2;
- } else if (index == partitions) {
- /* Increase the final partition. */
- window.width = (div_size / 2) + modulus;
- window.left -= div_size / 2;
- }
- } else if (index == partitions) {
- window.width = modulus;
- }
- }
-
- vsp1_pipeline_propagate_partition(pipe, partition, index, &window);
-}
-
-static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
-{
- struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
- const struct v4l2_mbus_framefmt *format;
- struct vsp1_entity *entity;
- unsigned int div_size;
- unsigned int i;
-
- /*
- * Partitions are computed on the size before rotation, use the format
- * at the WPF sink.
- */
- format = vsp1_entity_get_pad_format(&pipe->output->entity,
- pipe->output->entity.state,
- RWPF_PAD_SINK);
- div_size = format->width;
-
- /*
- * Only Gen3+ hardware requires image partitioning, Gen2 will operate
- * with a single partition that covers the whole output.
- */
- if (vsp1->info->gen >= 3) {
- list_for_each_entry(entity, &pipe->entities, list_pipe) {
- unsigned int entity_max;
-
- if (!entity->ops->max_width)
- continue;
-
- entity_max = entity->ops->max_width(entity, pipe);
- if (entity_max)
- div_size = min(div_size, entity_max);
- }
- }
-
- pipe->partitions = DIV_ROUND_UP(format->width, div_size);
- pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table),
- GFP_KERNEL);
- if (!pipe->part_table)
- return -ENOMEM;
-
- for (i = 0; i < pipe->partitions; ++i)
- vsp1_video_calculate_partition(pipe, &pipe->part_table[i],
- div_size, i);
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
* Pipeline Management
*/
@@ -365,13 +258,12 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
unsigned int partition)
{
+ struct vsp1_partition *part = &pipe->part_table[partition];
struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl);
struct vsp1_entity *entity;
- pipe->partition = &pipe->part_table[partition];
-
list_for_each_entry(entity, &pipe->entities, list_pipe)
- vsp1_entity_configure_partition(entity, pipe, dl, dlb);
+ vsp1_entity_configure_partition(entity, pipe, part, dl, dlb);
}
static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe)
@@ -646,11 +538,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe,
static int vsp1_video_pipeline_init(struct vsp1_pipeline *pipe,
struct vsp1_video *video)
{
+ int ret;
+
vsp1_pipeline_init(pipe);
pipe->frame_end = vsp1_video_pipeline_frame_end;
- return vsp1_video_pipeline_build(pipe, video);
+ ret = vsp1_video_pipeline_build(pipe, video);
+ if (ret)
+ return ret;
+
+ vsp1_pipeline_dump(pipe, "video");
+
+ return 0;
}
static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video)
@@ -784,6 +684,54 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
spin_unlock_irqrestore(&pipe->irqlock, flags);
}
+static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+ const struct v4l2_mbus_framefmt *format;
+ struct vsp1_entity *entity;
+ unsigned int div_size;
+ unsigned int i;
+
+ /*
+ * Partitions are computed on the size before rotation, use the format
+ * at the WPF sink.
+ */
+ format = v4l2_subdev_state_get_format(pipe->output->entity.state,
+ RWPF_PAD_SINK);
+ div_size = format->width;
+
+ /*
+ * Only Gen3+ hardware requires image partitioning, Gen2 will operate
+ * with a single partition that covers the whole output.
+ */
+ if (vsp1->info->gen >= 3) {
+ list_for_each_entry(entity, &pipe->entities, list_pipe) {
+ unsigned int entity_max;
+
+ if (!entity->ops->max_width)
+ continue;
+
+ entity_max = entity->ops->max_width(entity,
+ entity->state,
+ pipe);
+ if (entity_max)
+ div_size = min(div_size, entity_max);
+ }
+ }
+
+ pipe->partitions = DIV_ROUND_UP(format->width, div_size);
+ pipe->part_table = kcalloc(pipe->partitions, sizeof(*pipe->part_table),
+ GFP_KERNEL);
+ if (!pipe->part_table)
+ return -ENOMEM;
+
+ for (i = 0; i < pipe->partitions; ++i)
+ vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[i],
+ div_size, i);
+
+ return 0;
+}
+
static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
{
struct vsp1_entity *entity;
@@ -826,7 +774,7 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
list_for_each_entry(entity, &pipe->entities, list_pipe) {
vsp1_entity_route_setup(entity, pipe, pipe->stream_config);
- vsp1_entity_configure_stream(entity, pipe, NULL,
+ vsp1_entity_configure_stream(entity, entity->state, pipe, NULL,
pipe->stream_config);
}
@@ -937,8 +885,6 @@ static const struct vb2_ops vsp1_video_queue_qops = {
.queue_setup = vsp1_video_queue_setup,
.buf_prepare = vsp1_video_buffer_prepare,
.buf_queue = vsp1_video_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = vsp1_video_start_streaming,
.stop_streaming = vsp1_video_stop_streaming,
};
@@ -954,16 +900,36 @@ vsp1_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
struct vsp1_video *video = to_vsp1_video(vfh->vdev);
cap->capabilities = V4L2_CAP_DEVICE_CAPS | V4L2_CAP_STREAMING
- | V4L2_CAP_VIDEO_CAPTURE_MPLANE
+ | V4L2_CAP_IO_MC | V4L2_CAP_VIDEO_CAPTURE_MPLANE
| V4L2_CAP_VIDEO_OUTPUT_MPLANE;
-
strscpy(cap->driver, "vsp1", sizeof(cap->driver));
strscpy(cap->card, video->video.name, sizeof(cap->card));
return 0;
}
+static int vsp1_video_enum_format(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ struct v4l2_fh *vfh = file->private_data;
+ struct vsp1_video *video = to_vsp1_video(vfh->vdev);
+ const struct vsp1_format_info *info;
+
+ info = vsp1_get_format_info_by_index(video->vsp1, f->index, f->mbus_code);
+ if (!info)
+ return -EINVAL;
+
+ f->pixelformat = info->fourcc;
+
+ if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+ info->mbus == MEDIA_BUS_FMT_AYUV8_1X32)
+ f->flags = V4L2_FMT_FLAG_CSC_YCBCR_ENC
+ | V4L2_FMT_FLAG_CSC_QUANTIZATION;
+
+ return 0;
+}
+
static int
vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
{
@@ -1079,6 +1045,8 @@ err_pipe:
static const struct v4l2_ioctl_ops vsp1_video_ioctl_ops = {
.vidioc_querycap = vsp1_video_querycap,
+ .vidioc_enum_fmt_vid_cap = vsp1_video_enum_format,
+ .vidioc_enum_fmt_vid_out = vsp1_video_enum_format,
.vidioc_g_fmt_vid_cap_mplane = vsp1_video_get_format,
.vidioc_s_fmt_vid_cap_mplane = vsp1_video_set_format,
.vidioc_try_fmt_vid_cap_mplane = vsp1_video_try_format,
@@ -1146,6 +1114,27 @@ static const struct v4l2_file_operations vsp1_video_fops = {
};
/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static int vsp1_video_link_validate(struct media_link *link)
+{
+ /*
+ * Ideally, link validation should be implemented here instead of
+ * calling vsp1_video_verify_format() in vsp1_video_streamon()
+ * manually. That would however break userspace that start one video
+ * device before configures formats on other video devices in the
+ * pipeline. This operation is just a no-op to silence the warnings
+ * from v4l2_subdev_link_validate().
+ */
+ return 0;
+}
+
+static const struct media_entity_operations vsp1_video_media_ops = {
+ .link_validate = vsp1_video_link_validate,
+};
+
+/* -----------------------------------------------------------------------------
* Suspend and Resume
*/
@@ -1252,14 +1241,14 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
video->pad.flags = MEDIA_PAD_FL_SOURCE;
video->video.vfl_dir = VFL_DIR_TX;
video->video.device_caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE |
- V4L2_CAP_STREAMING;
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
} else {
direction = "output";
video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
video->pad.flags = MEDIA_PAD_FL_SINK;
video->video.vfl_dir = VFL_DIR_RX;
video->video.device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
- V4L2_CAP_STREAMING;
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
}
mutex_init(&video->lock);
@@ -1279,6 +1268,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1,
/* ... and the video node... */
video->video.v4l2_dev = &video->vsp1->v4l2_dev;
+ video->video.entity.ops = &vsp1_video_media_ops;
video->video.fops = &vsp1_video_fops;
snprintf(video->video.name, sizeof(video->video.name), "%s %s",
rwpf->entity.subdev.name, direction);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
index 9693aeab1cac..30662cfdf837 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
@@ -65,12 +65,10 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation)
goto done;
}
- sink_format = vsp1_entity_get_pad_format(&wpf->entity,
- wpf->entity.state,
- RWPF_PAD_SINK);
- source_format = vsp1_entity_get_pad_format(&wpf->entity,
- wpf->entity.state,
- RWPF_PAD_SOURCE);
+ sink_format = v4l2_subdev_state_get_format(wpf->entity.state,
+ RWPF_PAD_SINK);
+ source_format = v4l2_subdev_state_get_format(wpf->entity.state,
+ RWPF_PAD_SOURCE);
mutex_lock(&wpf->entity.lock);
@@ -135,6 +133,7 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf)
{
struct vsp1_device *vsp1 = wpf->entity.vsp1;
unsigned int num_flip_ctrls;
+ int ret;
spin_lock_init(&wpf->flip.lock);
@@ -158,7 +157,9 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf)
num_flip_ctrls = 0;
}
- vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
+ ret = vsp1_rwpf_init_ctrls(wpf, num_flip_ctrls);
+ if (ret < 0)
+ return ret;
if (num_flip_ctrls >= 1) {
wpf->flip.ctrls.vflip =
@@ -176,11 +177,8 @@ static int wpf_init_controls(struct vsp1_rwpf *wpf)
v4l2_ctrl_cluster(3, &wpf->flip.ctrls.vflip);
}
- if (wpf->ctrls.error) {
- dev_err(vsp1->dev, "wpf%u: failed to initialize controls\n",
- wpf->entity.index);
+ if (wpf->ctrls.error)
return wpf->ctrls.error;
- }
return 0;
}
@@ -231,6 +229,7 @@ static int wpf_configure_writeback_chain(struct vsp1_rwpf *wpf,
}
static void wpf_configure_stream(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
@@ -245,15 +244,14 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
u32 srcrpf = 0;
int ret;
- sink_format = vsp1_entity_get_pad_format(&wpf->entity,
- wpf->entity.state,
- RWPF_PAD_SINK);
- source_format = vsp1_entity_get_pad_format(&wpf->entity,
- wpf->entity.state,
- RWPF_PAD_SOURCE);
+ sink_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
+ source_format = v4l2_subdev_state_get_format(state, RWPF_PAD_SOURCE);
- /* Format */
- if (!pipe->lif || wpf->writeback) {
+ /*
+ * Format configuration. Skip for IIF (VSPX) or if the pipe doesn't
+ * write to memory.
+ */
+ if (!pipe->iif && (!pipe->lif || wpf->writeback)) {
const struct v4l2_pix_format_mplane *format = &wpf->format;
const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
@@ -284,8 +282,33 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
(256 << VI6_WPF_ROT_CTRL_LMEM_WD_SHIFT));
}
- if (sink_format->code != source_format->code)
- outfmt |= VI6_WPF_OUTFMT_CSC;
+ if (sink_format->code != source_format->code) {
+ u16 ycbcr_enc;
+ u16 quantization;
+ u32 wrtm;
+
+ if (sink_format->code == MEDIA_BUS_FMT_AYUV8_1X32) {
+ ycbcr_enc = sink_format->ycbcr_enc;
+ quantization = sink_format->quantization;
+ } else {
+ ycbcr_enc = source_format->ycbcr_enc;
+ quantization = source_format->quantization;
+ }
+
+ if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT601;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_601 &&
+ quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT601_EXT;
+ else if (ycbcr_enc == V4L2_YCBCR_ENC_709 &&
+ quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT709;
+ else
+ wrtm = VI6_WPF_OUTFMT_WRTM_BT709_EXT;
+
+ outfmt |= VI6_WPF_OUTFMT_CSC | wrtm;
+ }
wpf->outfmt = outfmt;
@@ -296,7 +319,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
* Sources. If the pipeline has a single input and BRx is not used,
* configure it as the master layer. Otherwise configure all
* inputs as sub-layers and select the virtual RPF as the master
- * layer.
+ * layer. For VSPX configure the enabled sources as masters.
*/
for (i = 0; i < vsp1->info->rpf_count; ++i) {
struct vsp1_rwpf *input = pipe->inputs[i];
@@ -304,7 +327,7 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
if (!input)
continue;
- srcrpf |= (!pipe->brx && pipe->num_inputs == 1)
+ srcrpf |= (pipe->iif || (!pipe->brx && pipe->num_inputs == 1))
? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index)
: VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index);
}
@@ -321,6 +344,9 @@ static void wpf_configure_stream(struct vsp1_entity *entity,
vsp1_dl_body_write(dlb, VI6_WPF_IRQ_ENB(index),
VI6_WPF_IRQ_ENB_DFEE);
+ if (pipe->iif)
+ return;
+
/*
* Configure writeback for display pipelines (the wpf writeback flag is
* never set for memory-to-memory pipelines). Start by adding a chained
@@ -367,13 +393,13 @@ static void wpf_configure_frame(struct vsp1_entity *entity,
static void wpf_configure_partition(struct vsp1_entity *entity,
struct vsp1_pipeline *pipe,
+ const struct vsp1_partition *partition,
struct vsp1_dl_list *dl,
struct vsp1_dl_body *dlb)
{
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
struct vsp1_device *vsp1 = wpf->entity.vsp1;
struct vsp1_rwpf_memory mem = wpf->mem;
- const struct v4l2_mbus_framefmt *sink_format;
const struct v4l2_pix_format_mplane *format = &wpf->format;
const struct vsp1_format_info *fmtinfo = wpf->fmtinfo;
unsigned int width;
@@ -383,21 +409,13 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
unsigned int flip;
unsigned int i;
- sink_format = vsp1_entity_get_pad_format(&wpf->entity,
- wpf->entity.state,
- RWPF_PAD_SINK);
- width = sink_format->width;
- height = sink_format->height;
- left = 0;
-
/*
- * Cropping. The partition algorithm can split the image into
- * multiple slices.
+ * Cropping. The partition algorithm can split the image into multiple
+ * slices.
*/
- if (pipe->partitions > 1) {
- width = pipe->partition->wpf.width;
- left = pipe->partition->wpf.left;
- }
+ width = partition->wpf.width;
+ left = partition->wpf.left;
+ height = partition->wpf.height;
vsp1_wpf_write(wpf, dlb, VI6_WPF_HSZCLIP, VI6_WPF_SZCLIP_EN |
(0 << VI6_WPF_SZCLIP_OFST_SHIFT) |
@@ -508,6 +526,7 @@ static void wpf_configure_partition(struct vsp1_entity *entity,
}
static unsigned int wpf_max_width(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe)
{
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
@@ -516,10 +535,11 @@ static unsigned int wpf_max_width(struct vsp1_entity *entity,
}
static void wpf_partition(struct vsp1_entity *entity,
+ struct v4l2_subdev_state *state,
struct vsp1_pipeline *pipe,
struct vsp1_partition *partition,
unsigned int partition_idx,
- struct vsp1_partition_window *window)
+ struct v4l2_rect *window)
{
partition->wpf = *window;
}
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index 662c81b6d0b5..730bdf98565a 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2017 Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) 2017 Rockchip Electronics Co., Ltd.
* Author: Jacob Chen <jacob-chen@iotwrt.com>
*/
@@ -119,6 +119,13 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
if (IS_ERR(f))
return PTR_ERR(f);
+ if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) {
+ if (vbuf->field == V4L2_FIELD_ANY)
+ vbuf->field = V4L2_FIELD_NONE;
+ if (vbuf->field != V4L2_FIELD_NONE)
+ return -EINVAL;
+ }
+
for (i = 0; i < vb->num_planes; i++) {
vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage);
@@ -195,6 +202,11 @@ static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count)
return ret;
}
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ ctx->osequence = 0;
+ else
+ ctx->csequence = 0;
+
return 0;
}
@@ -213,8 +225,6 @@ const struct vb2_ops rga_qops = {
.buf_prepare = rga_buf_prepare,
.buf_queue = rga_buf_queue,
.buf_cleanup = rga_buf_cleanup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = rga_buf_start_streaming,
.stop_streaming = rga_buf_stop_streaming,
};
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 11c3d7234757..43ed742a1649 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Jacob Chen <jacob-chen@iotwrt.com>
*/
@@ -376,7 +376,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
* Configure the dest framebuffer base address with pixel offset.
*/
offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h);
- dst_offset = rga_lookup_draw_pos(&offsets, mir_mode, rot_mode);
+ dst_offset = rga_lookup_draw_pos(&offsets, rot_mode, mir_mode);
dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
dst_offset->y_off;
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
index e8917e5630a4..cc6bd7f5b030 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.h
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Jacob Chen <jacob-chen@iotwrt.com>
*/
#ifndef __RGA_HW_H__
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 00fdfa9e10bc..3dccab5fa4a1 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Jacob Chen <jacob-chen@iotwrt.com>
*/
@@ -43,6 +43,8 @@ static void device_run(void *prv)
rga->curr = ctx;
src = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
+ src->sequence = ctx->osequence++;
+
dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst));
@@ -75,6 +77,8 @@ static irqreturn_t rga_isr(int irq, void *prv)
v4l2_m2m_buf_copy_metadata(src, dst, true);
+ dst->sequence = ctx->csequence++;
+
v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE);
v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE);
v4l2_m2m_job_finish(rga->m2m_dev, ctx->fh.m2m_ctx);
@@ -98,7 +102,7 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
src_vq->drv_priv = ctx;
src_vq->ops = &rga_qops;
src_vq->mem_ops = &vb2_dma_sg_memops;
- dst_vq->gfp_flags = __GFP_DMA32;
+ src_vq->gfp_flags = __GFP_DMA32;
src_vq->buf_struct_size = sizeof(struct rga_vb_buffer);
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
src_vq->lock = &ctx->rga->mutex;
@@ -968,7 +972,7 @@ MODULE_DEVICE_TABLE(of, rockchip_rga_match);
static struct platform_driver rga_pdrv = {
.probe = rga_probe,
- .remove_new = rga_remove,
+ .remove = rga_remove,
.driver = {
.name = RGA_NAME,
.pm = &rga_pm,
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 3502dff6055c..530e12de73c4 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Copyright (C) Rockchip Electronics Co., Ltd.
* Author: Jacob Chen <jacob-chen@iotwrt.com>
*/
#ifndef __RGA_H__
@@ -57,6 +57,9 @@ struct rga_ctx {
struct rga_frame out;
struct v4l2_ctrl_handler ctrl_handler;
+ int osequence;
+ int csequence;
+
/* Control values */
u32 op;
u32 hflip;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
index 2bddb4fa8a5c..6dcefd144d5a 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-capture.c
@@ -35,8 +35,6 @@
#define RKISP1_SP_DEV_NAME RKISP1_DRIVER_NAME "_selfpath"
#define RKISP1_MP_DEV_NAME RKISP1_DRIVER_NAME "_mainpath"
-#define RKISP1_MIN_BUFFERS_NEEDED 3
-
enum rkisp1_plane {
RKISP1_PLANE_Y = 0,
RKISP1_PLANE_CB = 1,
@@ -1203,8 +1201,6 @@ static const struct vb2_ops rkisp1_vb2_ops = {
.buf_init = rkisp1_vb2_buf_init,
.buf_queue = rkisp1_vb2_buf_queue,
.buf_prepare = rkisp1_vb2_buf_prepare,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = rkisp1_vb2_stop_streaming,
.start_streaming = rkisp1_vb2_start_streaming,
};
@@ -1563,7 +1559,7 @@ static int rkisp1_register_capture(struct rkisp1_capture *cap)
q->ops = &rkisp1_vb2_ops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct rkisp1_buffer);
- q->min_queued_buffers = RKISP1_MIN_BUFFERS_NEEDED;
+ q->min_queued_buffers = 1;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &node->vlock;
q->dev = cap->rkisp1->dev;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c
index f956b90a407a..60c97bb7b18b 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.c
@@ -178,3 +178,17 @@ void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
rkisp1_sd_adjust_crop_rect(crop, &crop_bounds);
}
+
+void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern,
+ const u32 input[4], u32 output[4])
+{
+ static const unsigned int swap[4][4] = {
+ [RKISP1_RAW_RGGB] = { 0, 1, 2, 3 },
+ [RKISP1_RAW_GRBG] = { 1, 0, 3, 2 },
+ [RKISP1_RAW_GBRG] = { 2, 3, 0, 1 },
+ [RKISP1_RAW_BGGR] = { 3, 2, 1, 0 },
+ };
+
+ for (unsigned int i = 0; i < 4; ++i)
+ output[i] = input[swap[pattern][i]];
+}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
index 26573f6ae575..ca952fd0829b 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
@@ -33,9 +33,10 @@ struct regmap;
#define RKISP1_ISP_SD_SRC BIT(0)
#define RKISP1_ISP_SD_SINK BIT(1)
-/* min and max values for the widths and heights of the entities */
-#define RKISP1_ISP_MAX_WIDTH 4032
-#define RKISP1_ISP_MAX_HEIGHT 3024
+/*
+ * Minimum values for the width and height of entities. The maximum values are
+ * model-specific and stored in the rkisp1_info structure.
+ */
#define RKISP1_ISP_MIN_WIDTH 32
#define RKISP1_ISP_MIN_HEIGHT 32
@@ -115,6 +116,8 @@ enum rkisp1_isp_pad {
* @RKISP1_FEATURE_SELF_PATH: The ISP has a self path
* @RKISP1_FEATURE_DUAL_CROP: The ISP has the dual crop block at the resizer input
* @RKISP1_FEATURE_DMA_34BIT: The ISP uses 34-bit DMA addresses
+ * @RKISP1_FEATURE_BLS: The ISP has a dedicated BLS block
+ * @RKISP1_FEATURE_COMPAND: The ISP has a companding block
*
* The ISP features are stored in a bitmask in &rkisp1_info.features and allow
* the driver to implement support for features present in some ISP versions
@@ -126,6 +129,8 @@ enum rkisp1_feature {
RKISP1_FEATURE_SELF_PATH = BIT(2),
RKISP1_FEATURE_DUAL_CROP = BIT(3),
RKISP1_FEATURE_DMA_34BIT = BIT(4),
+ RKISP1_FEATURE_BLS = BIT(5),
+ RKISP1_FEATURE_COMPAND = BIT(6),
};
#define rkisp1_has_feature(rkisp1, feature) \
@@ -140,6 +145,8 @@ enum rkisp1_feature {
* @isr_size: number of entries in the @isrs array
* @isp_ver: ISP version
* @features: bitmask of rkisp1_feature features implemented by the ISP
+ * @max_width: maximum input frame width
+ * @max_height: maximum input frame height
*
* This structure contains information about the ISP specific to a particular
* ISP model, version, or integration in a particular SoC.
@@ -151,6 +158,8 @@ struct rkisp1_info {
unsigned int isr_size;
enum rkisp1_cif_isp_version isp_ver;
unsigned int features;
+ unsigned int max_width;
+ unsigned int max_height;
};
/*
@@ -232,7 +241,7 @@ struct rkisp1_vdev_node {
/*
* struct rkisp1_buffer - A container for the vb2 buffers used by the video devices:
- * params, stats, mainpath, selfpath
+ * stats, mainpath, selfpath
*
* @vb: vb2 buffer
* @queue: entry of the buffer in the queue
@@ -245,6 +254,26 @@ struct rkisp1_buffer {
};
/*
+ * struct rkisp1_params_buffer - A container for the vb2 buffers used by the
+ * params video device
+ *
+ * @vb: vb2 buffer
+ * @queue: entry of the buffer in the queue
+ * @cfg: scratch buffer used for caching the ISP configuration parameters
+ */
+struct rkisp1_params_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ void *cfg;
+};
+
+static inline struct rkisp1_params_buffer *
+to_rkisp1_params_buffer(struct vb2_v4l2_buffer *vbuf)
+{
+ return container_of(vbuf, struct rkisp1_params_buffer, vb);
+}
+
+/*
* struct rkisp1_dummy_buffer - A buffer to write the next frame to in case
* there are no vb2 buffers available.
*
@@ -372,9 +401,11 @@ struct rkisp1_params_ops {
* @ops: pointer to the variant-specific operations
* @config_lock: locks the buffer list 'params'
* @params: queue of rkisp1_buffer
- * @vdev_fmt: v4l2_format of the metadata format
+ * @metafmt the currently enabled metadata format
* @quantization: the quantization configured on the isp's src pad
+ * @ycbcr_encoding the YCbCr encoding
* @raw_type: the bayer pattern on the isp video sink pad
+ * @enabled_blocks: bitmask of enabled ISP blocks
*/
struct rkisp1_params {
struct rkisp1_vdev_node vnode;
@@ -383,11 +414,14 @@ struct rkisp1_params {
spinlock_t config_lock; /* locks the buffers list 'params' */
struct list_head params;
- struct v4l2_format vdev_fmt;
+
+ const struct v4l2_meta_format *metafmt;
enum v4l2_quantization quantization;
enum v4l2_ycbcr_encoding ycbcr_encoding;
enum rkisp1_fmt_raw_pat_type raw_type;
+
+ u32 enabled_blocks;
};
/*
@@ -573,6 +607,9 @@ void rkisp1_sd_adjust_crop_rect(struct v4l2_rect *crop,
void rkisp1_sd_adjust_crop(struct v4l2_rect *crop,
const struct v4l2_mbus_framefmt *bounds);
+void rkisp1_bls_swap_regs(enum rkisp1_fmt_raw_pat_type pattern,
+ const u32 input[4], u32 output[4]);
+
/*
* rkisp1_mbus_info_get_by_code - get the isp info of the media bus code
*
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
index 4202642e0523..841e58c20f7f 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
@@ -307,6 +307,7 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
+ struct rkisp1_csi *csi = to_rkisp1_csi(sd);
const struct rkisp1_mbus_info *mbus_info;
struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
@@ -326,10 +327,10 @@ static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd,
sink_fmt->width = clamp_t(u32, fmt->format.width,
RKISP1_ISP_MIN_WIDTH,
- RKISP1_ISP_MAX_WIDTH);
+ csi->rkisp1->info->max_width);
sink_fmt->height = clamp_t(u32, fmt->format.height,
RKISP1_ISP_MIN_HEIGHT,
- RKISP1_ISP_MAX_HEIGHT);
+ csi->rkisp1->info->max_height);
fmt->format = *sink_fmt;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
index bb0202386c70..dc65a7924f8a 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
@@ -228,6 +228,9 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
break;
}
+ if (ret)
+ break;
+
/* Parse the endpoint and validate the bus type. */
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
if (ret) {
@@ -509,7 +512,10 @@ static const struct rkisp1_info px30_isp_info = {
.isp_ver = RKISP1_V12,
.features = RKISP1_FEATURE_MIPI_CSI2
| RKISP1_FEATURE_SELF_PATH
- | RKISP1_FEATURE_DUAL_CROP,
+ | RKISP1_FEATURE_DUAL_CROP
+ | RKISP1_FEATURE_BLS,
+ .max_width = 3264,
+ .max_height = 2448,
};
static const char * const rk3399_isp_clks[] = {
@@ -530,7 +536,10 @@ static const struct rkisp1_info rk3399_isp_info = {
.isp_ver = RKISP1_V10,
.features = RKISP1_FEATURE_MIPI_CSI2
| RKISP1_FEATURE_SELF_PATH
- | RKISP1_FEATURE_DUAL_CROP,
+ | RKISP1_FEATURE_DUAL_CROP
+ | RKISP1_FEATURE_BLS,
+ .max_width = 4416,
+ .max_height = 3312,
};
static const char * const imx8mp_isp_clks[] = {
@@ -550,7 +559,10 @@ static const struct rkisp1_info imx8mp_isp_info = {
.isr_size = ARRAY_SIZE(imx8mp_isp_isrs),
.isp_ver = RKISP1_V_IMX8MP,
.features = RKISP1_FEATURE_MAIN_STRIDE
- | RKISP1_FEATURE_DMA_34BIT,
+ | RKISP1_FEATURE_DMA_34BIT
+ | RKISP1_FEATURE_COMPAND,
+ .max_width = 4096,
+ .max_height = 3072,
};
static const struct of_device_id rkisp1_of_match[] = {
@@ -743,7 +755,7 @@ static struct platform_driver rkisp1_drv = {
.pm = &rkisp1_pm_ops,
},
.probe = rkisp1_probe,
- .remove_new = rkisp1_remove,
+ .remove = rkisp1_remove,
};
module_platform_driver(rkisp1_drv);
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
index e45a213baf49..8c29a1c9309a 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
@@ -173,7 +173,7 @@ static void rkisp1_gasket_disable(struct rkisp1_device *rkisp1)
* or at the frame end interrupt
*/
static void rkisp1_config_ism(struct rkisp1_isp *isp,
- struct v4l2_subdev_state *sd_state)
+ const struct v4l2_subdev_state *sd_state)
{
const struct v4l2_rect *src_crop =
v4l2_subdev_state_get_crop(sd_state,
@@ -201,7 +201,7 @@ static void rkisp1_config_ism(struct rkisp1_isp *isp,
* configure ISP blocks with input format, size......
*/
static int rkisp1_config_isp(struct rkisp1_isp *isp,
- struct v4l2_subdev_state *sd_state,
+ const struct v4l2_subdev_state *sd_state,
enum v4l2_mbus_type mbus_type, u32 mbus_flags)
{
struct rkisp1_device *rkisp1 = isp->rkisp1;
@@ -309,7 +309,7 @@ static int rkisp1_config_isp(struct rkisp1_isp *isp,
if (src_fmt->pixel_enc == V4L2_PIXEL_ENC_BAYER) {
rkisp1_params_disable(&rkisp1->params);
} else {
- struct v4l2_mbus_framefmt *src_frm;
+ const struct v4l2_mbus_framefmt *src_frm;
src_frm = v4l2_subdev_state_get_format(sd_state,
RKISP1_ISP_PAD_SOURCE_VIDEO);
@@ -429,7 +429,7 @@ static void rkisp1_config_clk(struct rkisp1_isp *isp)
}
static int rkisp1_isp_start(struct rkisp1_isp *isp,
- struct v4l2_subdev_state *sd_state,
+ const struct v4l2_subdev_state *sd_state,
struct media_pad *source)
{
struct rkisp1_device *rkisp1 = isp->rkisp1;
@@ -517,6 +517,7 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
+ struct rkisp1_isp *isp = to_rkisp1_isp(sd);
const struct rkisp1_mbus_info *mbus_info;
if (fse->pad == RKISP1_ISP_PAD_SINK_PARAMS ||
@@ -539,9 +540,9 @@ static int rkisp1_isp_enum_frame_size(struct v4l2_subdev *sd,
return -EINVAL;
fse->min_width = RKISP1_ISP_MIN_WIDTH;
- fse->max_width = RKISP1_ISP_MAX_WIDTH;
+ fse->max_width = isp->rkisp1->info->max_width;
fse->min_height = RKISP1_ISP_MIN_HEIGHT;
- fse->max_height = RKISP1_ISP_MAX_HEIGHT;
+ fse->max_height = isp->rkisp1->info->max_height;
return 0;
}
@@ -772,10 +773,10 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
sink_fmt->width = clamp_t(u32, format->width,
RKISP1_ISP_MIN_WIDTH,
- RKISP1_ISP_MAX_WIDTH);
+ isp->rkisp1->info->max_width);
sink_fmt->height = clamp_t(u32, format->height,
RKISP1_ISP_MIN_HEIGHT,
- RKISP1_ISP_MAX_HEIGHT);
+ isp->rkisp1->info->max_height);
/*
* Adjust the color space fields. Accept any color primaries and
@@ -880,7 +881,7 @@ static int rkisp1_isp_set_selection(struct v4l2_subdev *sd,
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
- dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__,
+ dev_dbg(isp->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__,
sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
if (sel->pad == RKISP1_ISP_PAD_SINK_VIDEO)
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index 173d1ea41874..b28f4140c8a3 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -5,6 +5,9 @@
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
*/
+#include <linux/math.h>
+#include <linux/string.h>
+
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
@@ -33,6 +36,59 @@
#define RKISP1_ISP_CC_COEFF(n) \
(RKISP1_CIF_ISP_CC_COEFF_0 + (n) * 4)
+#define RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS BIT(0)
+#define RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC BIT(1)
+
+union rkisp1_ext_params_config {
+ struct rkisp1_ext_params_block_header header;
+ struct rkisp1_ext_params_bls_config bls;
+ struct rkisp1_ext_params_dpcc_config dpcc;
+ struct rkisp1_ext_params_sdg_config sdg;
+ struct rkisp1_ext_params_lsc_config lsc;
+ struct rkisp1_ext_params_awb_gain_config awbg;
+ struct rkisp1_ext_params_flt_config flt;
+ struct rkisp1_ext_params_bdm_config bdm;
+ struct rkisp1_ext_params_ctk_config ctk;
+ struct rkisp1_ext_params_goc_config goc;
+ struct rkisp1_ext_params_dpf_config dpf;
+ struct rkisp1_ext_params_dpf_strength_config dpfs;
+ struct rkisp1_ext_params_cproc_config cproc;
+ struct rkisp1_ext_params_ie_config ie;
+ struct rkisp1_ext_params_awb_meas_config awbm;
+ struct rkisp1_ext_params_hst_config hst;
+ struct rkisp1_ext_params_aec_config aec;
+ struct rkisp1_ext_params_afc_config afc;
+ struct rkisp1_ext_params_compand_bls_config compand_bls;
+ struct rkisp1_ext_params_compand_curve_config compand_curve;
+};
+
+enum rkisp1_params_formats {
+ RKISP1_PARAMS_FIXED,
+ RKISP1_PARAMS_EXTENSIBLE,
+};
+
+static const struct v4l2_meta_format rkisp1_params_formats[] = {
+ [RKISP1_PARAMS_FIXED] = {
+ .dataformat = V4L2_META_FMT_RK_ISP1_PARAMS,
+ .buffersize = sizeof(struct rkisp1_params_cfg),
+ },
+ [RKISP1_PARAMS_EXTENSIBLE] = {
+ .dataformat = V4L2_META_FMT_RK_ISP1_EXT_PARAMS,
+ .buffersize = sizeof(struct rkisp1_ext_params_cfg),
+ },
+};
+
+static const struct v4l2_meta_format *
+rkisp1_params_get_format_info(u32 dataformat)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(rkisp1_params_formats); i++) {
+ if (rkisp1_params_formats[i].dataformat == dataformat)
+ return &rkisp1_params_formats[i];
+ }
+
+ return &rkisp1_params_formats[RKISP1_PARAMS_FIXED];
+}
+
static inline void
rkisp1_param_set_bits(struct rkisp1_params *params, u32 reg, u32 bit_mask)
{
@@ -112,54 +168,20 @@ static void rkisp1_bls_config(struct rkisp1_params *params,
new_control &= RKISP1_CIF_ISP_BLS_ENA;
/* fixed subtraction values */
if (!arg->enable_auto) {
- const struct rkisp1_cif_isp_bls_fixed_val *pval =
- &arg->fixed_val;
-
- switch (params->raw_type) {
- case RKISP1_RAW_BGGR:
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
- pval->r);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
- pval->gr);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
- pval->gb);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
- pval->b);
- break;
- case RKISP1_RAW_GBRG:
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
- pval->r);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
- pval->gr);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
- pval->gb);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
- pval->b);
- break;
- case RKISP1_RAW_GRBG:
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
- pval->r);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
- pval->gr);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
- pval->gb);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
- pval->b);
- break;
- case RKISP1_RAW_RGGB:
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_A_FIXED,
- pval->r);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_B_FIXED,
- pval->gr);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_C_FIXED,
- pval->gb);
- rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_D_FIXED,
- pval->b);
- break;
- default:
- break;
- }
-
+ static const u32 regs[] = {
+ RKISP1_CIF_ISP_BLS_A_FIXED,
+ RKISP1_CIF_ISP_BLS_B_FIXED,
+ RKISP1_CIF_ISP_BLS_C_FIXED,
+ RKISP1_CIF_ISP_BLS_D_FIXED,
+ };
+ u32 swapped[4];
+
+ rkisp1_bls_swap_regs(params->raw_type, regs, swapped);
+
+ rkisp1_write(params->rkisp1, swapped[0], arg->fixed_val.r);
+ rkisp1_write(params->rkisp1, swapped[1], arg->fixed_val.gr);
+ rkisp1_write(params->rkisp1, swapped[2], arg->fixed_val.gb);
+ rkisp1_write(params->rkisp1, swapped[3], arg->fixed_val.b);
} else {
if (arg->en_windows & BIT(1)) {
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_BLS_H2_START,
@@ -1239,6 +1261,93 @@ rkisp1_dpf_strength_config(struct rkisp1_params *params,
rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_DPF_STRENGTH_R, arg->r);
}
+static void rkisp1_compand_write_px_curve(struct rkisp1_params *params,
+ unsigned int addr, const u8 *curve)
+{
+ const unsigned int points_per_reg = 6;
+ const unsigned int num_regs =
+ DIV_ROUND_UP(RKISP1_CIF_ISP_COMPAND_NUM_POINTS,
+ points_per_reg);
+
+ /*
+ * The compand curve is specified as a piecewise linear function with
+ * 64 points. X coordinates are stored as a log2 of the displacement
+ * from the previous point, in 5 bits, with 6 values per register. The
+ * last register stores 4 values.
+ */
+ for (unsigned int reg = 0; reg < num_regs; ++reg) {
+ unsigned int num_points =
+ min(RKISP1_CIF_ISP_COMPAND_NUM_POINTS -
+ reg * points_per_reg, points_per_reg);
+ u32 val = 0;
+
+ for (unsigned int i = 0; i < num_points; i++)
+ val |= (*curve++ & 0x1f) << (i * 5);
+
+ rkisp1_write(params->rkisp1, addr, val);
+ addr += 4;
+ }
+}
+
+static void
+rkisp1_compand_write_curve_mem(struct rkisp1_params *params,
+ unsigned int reg_addr, unsigned int reg_data,
+ const u32 curve[RKISP1_CIF_ISP_COMPAND_NUM_POINTS])
+{
+ for (unsigned int i = 0; i < RKISP1_CIF_ISP_COMPAND_NUM_POINTS; i++) {
+ rkisp1_write(params->rkisp1, reg_addr, i);
+ rkisp1_write(params->rkisp1, reg_data, curve[i]);
+ }
+}
+
+static void
+rkisp1_compand_bls_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_compand_bls_config *arg)
+{
+ static const u32 regs[] = {
+ RKISP1_CIF_ISP_COMPAND_BLS_A_FIXED,
+ RKISP1_CIF_ISP_COMPAND_BLS_B_FIXED,
+ RKISP1_CIF_ISP_COMPAND_BLS_C_FIXED,
+ RKISP1_CIF_ISP_COMPAND_BLS_D_FIXED,
+ };
+ u32 swapped[4];
+
+ rkisp1_bls_swap_regs(params->raw_type, regs, swapped);
+
+ rkisp1_write(params->rkisp1, swapped[0], arg->r);
+ rkisp1_write(params->rkisp1, swapped[1], arg->gr);
+ rkisp1_write(params->rkisp1, swapped[2], arg->gb);
+ rkisp1_write(params->rkisp1, swapped[3], arg->b);
+}
+
+static void
+rkisp1_compand_expand_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_compand_curve_config *arg)
+{
+ rkisp1_compand_write_px_curve(params, RKISP1_CIF_ISP_COMPAND_EXPAND_PX_N(0),
+ arg->px);
+ rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_EXPAND_Y_ADDR,
+ RKISP1_CIF_ISP_COMPAND_EXPAND_Y_WRITE_DATA,
+ arg->y);
+ rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_EXPAND_X_ADDR,
+ RKISP1_CIF_ISP_COMPAND_EXPAND_X_WRITE_DATA,
+ arg->x);
+}
+
+static void
+rkisp1_compand_compress_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_compand_curve_config *arg)
+{
+ rkisp1_compand_write_px_curve(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_PX_N(0),
+ arg->px);
+ rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_ADDR,
+ RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_WRITE_DATA,
+ arg->y);
+ rkisp1_compand_write_curve_mem(params, RKISP1_CIF_ISP_COMPAND_COMPRESS_X_ADDR,
+ RKISP1_CIF_ISP_COMPAND_COMPRESS_X_WRITE_DATA,
+ arg->x);
+}
+
static void
rkisp1_isp_isr_other_config(struct rkisp1_params *params,
const struct rkisp1_params_cfg *new_params)
@@ -1249,6 +1358,12 @@ rkisp1_isp_isr_other_config(struct rkisp1_params *params,
module_cfg_update = new_params->module_cfg_update;
module_ens = new_params->module_ens;
+ if (!rkisp1_has_feature(params->rkisp1, BLS)) {
+ module_en_update &= ~RKISP1_CIF_ISP_MODULE_BLS;
+ module_cfg_update &= ~RKISP1_CIF_ISP_MODULE_BLS;
+ module_ens &= ~RKISP1_CIF_ISP_MODULE_BLS;
+ }
+
/* update dpc config */
if (module_cfg_update & RKISP1_CIF_ISP_MODULE_DPCC)
rkisp1_dpcc_config(params,
@@ -1501,21 +1616,551 @@ static void rkisp1_isp_isr_meas_config(struct rkisp1_params *params,
}
}
-static bool rkisp1_params_get_buffer(struct rkisp1_params *params,
- struct rkisp1_buffer **buf,
- struct rkisp1_params_cfg **cfg)
+/*------------------------------------------------------------------------------
+ * Extensible parameters format handling
+ */
+
+static void
+rkisp1_ext_params_bls(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_bls_config *bls = &block->bls;
+
+ if (bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_BLS_CTRL,
+ RKISP1_CIF_ISP_BLS_ENA);
+ return;
+ }
+
+ rkisp1_bls_config(params, &bls->config);
+
+ if ((bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(bls->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_BLS_CTRL,
+ RKISP1_CIF_ISP_BLS_ENA);
+}
+
+static void
+rkisp1_ext_params_dpcc(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
{
- if (list_empty(&params->params))
- return false;
+ const struct rkisp1_ext_params_dpcc_config *dpcc = &block->dpcc;
+
+ if (dpcc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPCC_MODE,
+ RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
+ return;
+ }
- *buf = list_first_entry(&params->params, struct rkisp1_buffer, queue);
- *cfg = vb2_plane_vaddr(&(*buf)->vb.vb2_buf, 0);
+ rkisp1_dpcc_config(params, &dpcc->config);
- return true;
+ if ((dpcc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(dpcc->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPCC_MODE,
+ RKISP1_CIF_ISP_DPCC_MODE_DPCC_ENABLE);
+}
+
+static void
+rkisp1_ext_params_sdg(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_sdg_config *sdg = &block->sdg;
+
+ if (sdg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+ return;
+ }
+
+ rkisp1_sdg_config(params, &sdg->config);
+
+ if ((sdg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(sdg->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_IN_ENA);
+}
+
+static void
+rkisp1_ext_params_lsc(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_lsc_config *lsc = &block->lsc;
+
+ if (lsc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+ return;
+ }
+
+ rkisp1_lsc_config(params, &lsc->config);
+
+ if ((lsc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(lsc->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_LSC_CTRL,
+ RKISP1_CIF_ISP_LSC_CTRL_ENA);
+}
+
+static void
+rkisp1_ext_params_awbg(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_awb_gain_config *awbg = &block->awbg;
+
+ if (awbg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+ return;
+ }
+
+ params->ops->awb_gain_config(params, &awbg->config);
+
+ if ((awbg->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(awbg->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_AWB_ENA);
+}
+
+static void
+rkisp1_ext_params_flt(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_flt_config *flt = &block->flt;
+
+ if (flt->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_FILT_MODE,
+ RKISP1_CIF_ISP_FLT_ENA);
+ return;
+ }
+
+ rkisp1_flt_config(params, &flt->config);
+
+ if ((flt->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(flt->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_FILT_MODE,
+ RKISP1_CIF_ISP_FLT_ENA);
+}
+
+static void
+rkisp1_ext_params_bdm(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_bdm_config *bdm = &block->bdm;
+
+ if (bdm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
+ return;
+ }
+
+ rkisp1_bdm_config(params, &bdm->config);
+
+ if ((bdm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(bdm->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DEMOSAIC,
+ RKISP1_CIF_ISP_DEMOSAIC_BYPASS);
+}
+
+static void
+rkisp1_ext_params_ctk(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_ctk_config *ctk = &block->ctk;
+
+ if (ctk->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_ctk_enable(params, false);
+ return;
+ }
+
+ rkisp1_ctk_config(params, &ctk->config);
+
+ if ((ctk->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(ctk->header.type)))
+ rkisp1_ctk_enable(params, true);
+}
+
+static void
+rkisp1_ext_params_goc(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_goc_config *goc = &block->goc;
+
+ if (goc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+ return;
+ }
+
+ params->ops->goc_config(params, &goc->config);
+
+ /*
+ * Unconditionally re-enable the GOC module which gets disabled by
+ * goc_config().
+ */
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
+ RKISP1_CIF_ISP_CTRL_ISP_GAMMA_OUT_ENA);
+}
+
+static void
+rkisp1_ext_params_dpf(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_dpf_config *dpf = &block->dpf;
+
+ if (dpf->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE,
+ RKISP1_CIF_ISP_DPF_MODE_EN);
+ return;
+ }
+
+ rkisp1_dpf_config(params, &dpf->config);
+
+ if ((dpf->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(dpf->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_DPF_MODE,
+ RKISP1_CIF_ISP_DPF_MODE_EN);
+}
+
+static void
+rkisp1_ext_params_dpfs(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_dpf_strength_config *dpfs = &block->dpfs;
+
+ rkisp1_dpf_strength_config(params, &dpfs->config);
+}
+
+static void
+rkisp1_ext_params_cproc(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_cproc_config *cproc = &block->cproc;
+
+ if (cproc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_CTR_ENABLE);
+ return;
+ }
+
+ rkisp1_cproc_config(params, &cproc->config);
+
+ if ((cproc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(cproc->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_C_PROC_CTRL,
+ RKISP1_CIF_C_PROC_CTR_ENABLE);
+}
+
+static void
+rkisp1_ext_params_ie(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_ie_config *ie = &block->ie;
+
+ if (ie->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_ie_enable(params, false);
+ return;
+ }
+
+ rkisp1_ie_config(params, &ie->config);
+
+ if ((ie->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(ie->header.type)))
+ rkisp1_ie_enable(params, true);
+}
+
+static void
+rkisp1_ext_params_awbm(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_awb_meas_config *awbm = &block->awbm;
+
+ if (awbm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ params->ops->awb_meas_enable(params, &awbm->config,
+ false);
+ return;
+ }
+
+ params->ops->awb_meas_config(params, &awbm->config);
+
+ if ((awbm->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(awbm->header.type)))
+ params->ops->awb_meas_enable(params, &awbm->config,
+ true);
+}
+
+static void
+rkisp1_ext_params_hstm(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_hst_config *hst = &block->hst;
+
+ if (hst->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ params->ops->hst_enable(params, &hst->config, false);
+ return;
+ }
+
+ params->ops->hst_config(params, &hst->config);
+
+ if ((hst->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(hst->header.type)))
+ params->ops->hst_enable(params, &hst->config, true);
+}
+
+static void
+rkisp1_ext_params_aecm(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_aec_config *aec = &block->aec;
+
+ if (aec->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
+ return;
+ }
+
+ params->ops->aec_config(params, &aec->config);
+
+ if ((aec->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(aec->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_EXP_CTRL,
+ RKISP1_CIF_ISP_EXP_ENA);
+}
+
+static void
+rkisp1_ext_params_afcm(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_afc_config *afc = &block->afc;
+
+ if (afc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+ return;
+ }
+
+ params->ops->afm_config(params, &afc->config);
+
+ if ((afc->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(afc->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_AFM_CTRL,
+ RKISP1_CIF_ISP_AFM_ENA);
+}
+
+static void rkisp1_ext_params_compand_bls(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_compand_bls_config *bls =
+ &block->compand_bls;
+
+ if (bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL,
+ RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE);
+ return;
+ }
+
+ rkisp1_compand_bls_config(params, &bls->config);
+
+ if ((bls->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(bls->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL,
+ RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE);
+}
+
+static void rkisp1_ext_params_compand_expand(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_compand_curve_config *curve =
+ &block->compand_curve;
+
+ if (curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL,
+ RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE);
+ return;
+ }
+
+ rkisp1_compand_expand_config(params, &curve->config);
+
+ if ((curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(curve->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL,
+ RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE);
+}
+
+static void rkisp1_ext_params_compand_compress(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_compand_curve_config *curve =
+ &block->compand_curve;
+
+ if (curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL,
+ RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE);
+ return;
+ }
+
+ rkisp1_compand_compress_config(params, &curve->config);
+
+ if ((curve->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(curve->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_COMPAND_CTRL,
+ RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE);
+}
+
+typedef void (*rkisp1_block_handler)(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *config);
+
+static const struct rkisp1_ext_params_handler {
+ size_t size;
+ rkisp1_block_handler handler;
+ unsigned int group;
+ unsigned int features;
+} rkisp1_ext_params_handlers[] = {
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS] = {
+ .size = sizeof(struct rkisp1_ext_params_bls_config),
+ .handler = rkisp1_ext_params_bls,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ .features = RKISP1_FEATURE_BLS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPCC] = {
+ .size = sizeof(struct rkisp1_ext_params_dpcc_config),
+ .handler = rkisp1_ext_params_dpcc,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_SDG] = {
+ .size = sizeof(struct rkisp1_ext_params_sdg_config),
+ .handler = rkisp1_ext_params_sdg,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_GAIN] = {
+ .size = sizeof(struct rkisp1_ext_params_awb_gain_config),
+ .handler = rkisp1_ext_params_awbg,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_FLT] = {
+ .size = sizeof(struct rkisp1_ext_params_flt_config),
+ .handler = rkisp1_ext_params_flt,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_BDM] = {
+ .size = sizeof(struct rkisp1_ext_params_bdm_config),
+ .handler = rkisp1_ext_params_bdm,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_CTK] = {
+ .size = sizeof(struct rkisp1_ext_params_ctk_config),
+ .handler = rkisp1_ext_params_ctk,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_GOC] = {
+ .size = sizeof(struct rkisp1_ext_params_goc_config),
+ .handler = rkisp1_ext_params_goc,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF] = {
+ .size = sizeof(struct rkisp1_ext_params_dpf_config),
+ .handler = rkisp1_ext_params_dpf,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_DPF_STRENGTH] = {
+ .size = sizeof(struct rkisp1_ext_params_dpf_strength_config),
+ .handler = rkisp1_ext_params_dpfs,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_CPROC] = {
+ .size = sizeof(struct rkisp1_ext_params_cproc_config),
+ .handler = rkisp1_ext_params_cproc,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_IE] = {
+ .size = sizeof(struct rkisp1_ext_params_ie_config),
+ .handler = rkisp1_ext_params_ie,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_LSC] = {
+ .size = sizeof(struct rkisp1_ext_params_lsc_config),
+ .handler = rkisp1_ext_params_lsc,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_AWB_MEAS] = {
+ .size = sizeof(struct rkisp1_ext_params_awb_meas_config),
+ .handler = rkisp1_ext_params_awbm,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_HST_MEAS] = {
+ .size = sizeof(struct rkisp1_ext_params_hst_config),
+ .handler = rkisp1_ext_params_hstm,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_AEC_MEAS] = {
+ .size = sizeof(struct rkisp1_ext_params_aec_config),
+ .handler = rkisp1_ext_params_aecm,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_AFC_MEAS] = {
+ .size = sizeof(struct rkisp1_ext_params_afc_config),
+ .handler = rkisp1_ext_params_afcm,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_BLS] = {
+ .size = sizeof(struct rkisp1_ext_params_compand_bls_config),
+ .handler = rkisp1_ext_params_compand_bls,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ .features = RKISP1_FEATURE_COMPAND,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND] = {
+ .size = sizeof(struct rkisp1_ext_params_compand_curve_config),
+ .handler = rkisp1_ext_params_compand_expand,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ .features = RKISP1_FEATURE_COMPAND,
+ },
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS] = {
+ .size = sizeof(struct rkisp1_ext_params_compand_curve_config),
+ .handler = rkisp1_ext_params_compand_compress,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ .features = RKISP1_FEATURE_COMPAND,
+ },
+};
+
+static void rkisp1_ext_params_config(struct rkisp1_params *params,
+ struct rkisp1_ext_params_cfg *cfg,
+ u32 block_group_mask)
+{
+ size_t block_offset = 0;
+
+ if (WARN_ON(!cfg))
+ return;
+
+ /* Walk the list of parameter blocks and process them. */
+ while (block_offset < cfg->data_size) {
+ const struct rkisp1_ext_params_handler *block_handler;
+ const union rkisp1_ext_params_config *block;
+
+ block = (const union rkisp1_ext_params_config *)
+ &cfg->data[block_offset];
+ block_offset += block->header.size;
+
+ /*
+ * Make sure the block is supported by the platform and in the
+ * list of groups to configure.
+ */
+ block_handler = &rkisp1_ext_params_handlers[block->header.type];
+ if (!(block_handler->group & block_group_mask))
+ continue;
+
+ if ((params->rkisp1->info->features & block_handler->features) !=
+ block_handler->features)
+ continue;
+
+ block_handler->handler(params, block);
+
+ if (block->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)
+ params->enabled_blocks &= ~BIT(block->header.type);
+ else if (block->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE)
+ params->enabled_blocks |= BIT(block->header.type);
+ }
}
static void rkisp1_params_complete_buffer(struct rkisp1_params *params,
- struct rkisp1_buffer *buf,
+ struct rkisp1_params_buffer *buf,
unsigned int frame_sequence)
{
list_del(&buf->queue);
@@ -1527,17 +2172,24 @@ static void rkisp1_params_complete_buffer(struct rkisp1_params *params,
void rkisp1_params_isr(struct rkisp1_device *rkisp1)
{
struct rkisp1_params *params = &rkisp1->params;
- struct rkisp1_params_cfg *new_params;
- struct rkisp1_buffer *cur_buf;
+ struct rkisp1_params_buffer *cur_buf;
spin_lock(&params->config_lock);
- if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
+ cur_buf = list_first_entry_or_null(&params->params,
+ struct rkisp1_params_buffer, queue);
+ if (!cur_buf)
goto unlock;
- rkisp1_isp_isr_other_config(params, new_params);
- rkisp1_isp_isr_lsc_config(params, new_params);
- rkisp1_isp_isr_meas_config(params, new_params);
+ if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) {
+ rkisp1_isp_isr_other_config(params, cur_buf->cfg);
+ rkisp1_isp_isr_lsc_config(params, cur_buf->cfg);
+ rkisp1_isp_isr_meas_config(params, cur_buf->cfg);
+ } else {
+ rkisp1_ext_params_config(params, cur_buf->cfg,
+ RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS |
+ RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC);
+ }
/* update shadow register immediately */
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
@@ -1603,8 +2255,7 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params,
enum v4l2_ycbcr_encoding ycbcr_encoding)
{
struct rkisp1_cif_isp_hst_config hst = rkisp1_hst_params_default_config;
- struct rkisp1_params_cfg *new_params;
- struct rkisp1_buffer *cur_buf;
+ struct rkisp1_params_buffer *cur_buf;
params->quantization = quantization;
params->ycbcr_encoding = ycbcr_encoding;
@@ -1633,11 +2284,18 @@ void rkisp1_params_pre_configure(struct rkisp1_params *params,
/* apply the first buffer if there is one already */
- if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
+ cur_buf = list_first_entry_or_null(&params->params,
+ struct rkisp1_params_buffer, queue);
+ if (!cur_buf)
goto unlock;
- rkisp1_isp_isr_other_config(params, new_params);
- rkisp1_isp_isr_meas_config(params, new_params);
+ if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS) {
+ rkisp1_isp_isr_other_config(params, cur_buf->cfg);
+ rkisp1_isp_isr_meas_config(params, cur_buf->cfg);
+ } else {
+ rkisp1_ext_params_config(params, cur_buf->cfg,
+ RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS);
+ }
/* update shadow register immediately */
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
@@ -1649,8 +2307,7 @@ unlock:
void rkisp1_params_post_configure(struct rkisp1_params *params)
{
- struct rkisp1_params_cfg *new_params;
- struct rkisp1_buffer *cur_buf;
+ struct rkisp1_params_buffer *cur_buf;
spin_lock_irq(&params->config_lock);
@@ -1662,11 +2319,16 @@ void rkisp1_params_post_configure(struct rkisp1_params *params)
* ordering doesn't affect other ISP versions negatively, do so
* unconditionally.
*/
-
- if (!rkisp1_params_get_buffer(params, &cur_buf, &new_params))
+ cur_buf = list_first_entry_or_null(&params->params,
+ struct rkisp1_params_buffer, queue);
+ if (!cur_buf)
goto unlock;
- rkisp1_isp_isr_lsc_config(params, new_params);
+ if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_PARAMS)
+ rkisp1_isp_isr_lsc_config(params, cur_buf->cfg);
+ else
+ rkisp1_ext_params_config(params, cur_buf->cfg,
+ RKISP1_EXT_PARAMS_BLOCK_GROUP_LSC);
/* update shadow register immediately */
rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CTRL,
@@ -1742,12 +2404,12 @@ static int rkisp1_params_enum_fmt_meta_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct video_device *video = video_devdata(file);
- struct rkisp1_params *params = video_get_drvdata(video);
- if (f->index > 0 || f->type != video->queue->type)
+ if (f->index >= ARRAY_SIZE(rkisp1_params_formats) ||
+ f->type != video->queue->type)
return -EINVAL;
- f->pixelformat = params->vdev_fmt.fmt.meta.dataformat;
+ f->pixelformat = rkisp1_params_formats[f->index].dataformat;
return 0;
}
@@ -1762,9 +2424,40 @@ static int rkisp1_params_g_fmt_meta_out(struct file *file, void *fh,
if (f->type != video->queue->type)
return -EINVAL;
- memset(meta, 0, sizeof(*meta));
- meta->dataformat = params->vdev_fmt.fmt.meta.dataformat;
- meta->buffersize = params->vdev_fmt.fmt.meta.buffersize;
+ *meta = *params->metafmt;
+
+ return 0;
+}
+
+static int rkisp1_params_try_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ *meta = *rkisp1_params_get_format_info(meta->dataformat);
+
+ return 0;
+}
+
+static int rkisp1_params_s_fmt_meta_out(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct video_device *video = video_devdata(file);
+ struct rkisp1_params *params = video_get_drvdata(video);
+ struct v4l2_meta_format *meta = &f->fmt.meta;
+
+ if (f->type != video->queue->type)
+ return -EINVAL;
+
+ if (vb2_is_busy(video->queue))
+ return -EBUSY;
+
+ params->metafmt = rkisp1_params_get_format_info(meta->dataformat);
+ *meta = *params->metafmt;
return 0;
}
@@ -1794,8 +2487,8 @@ static const struct v4l2_ioctl_ops rkisp1_params_ioctl = {
.vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_enum_fmt_meta_out = rkisp1_params_enum_fmt_meta_out,
.vidioc_g_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
- .vidioc_s_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
- .vidioc_try_fmt_meta_out = rkisp1_params_g_fmt_meta_out,
+ .vidioc_s_fmt_meta_out = rkisp1_params_s_fmt_meta_out,
+ .vidioc_try_fmt_meta_out = rkisp1_params_try_fmt_meta_out,
.vidioc_querycap = rkisp1_params_querycap,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
@@ -1807,22 +2500,46 @@ static int rkisp1_params_vb2_queue_setup(struct vb2_queue *vq,
unsigned int sizes[],
struct device *alloc_devs[])
{
+ struct rkisp1_params *params = vq->drv_priv;
+
*num_buffers = clamp_t(u32, *num_buffers,
RKISP1_ISP_PARAMS_REQ_BUFS_MIN,
RKISP1_ISP_PARAMS_REQ_BUFS_MAX);
*num_planes = 1;
- sizes[0] = sizeof(struct rkisp1_params_cfg);
+ sizes[0] = params->metafmt->buffersize;
return 0;
}
+static int rkisp1_params_vb2_buf_init(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf);
+ struct rkisp1_params *params = vb->vb2_queue->drv_priv;
+
+ params_buf->cfg = kvmalloc(params->metafmt->buffersize,
+ GFP_KERNEL);
+ if (!params_buf->cfg)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void rkisp1_params_vb2_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf);
+
+ kvfree(params_buf->cfg);
+ params_buf->cfg = NULL;
+}
+
static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct rkisp1_buffer *params_buf =
- container_of(vbuf, struct rkisp1_buffer, vb);
+ struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf);
struct vb2_queue *vq = vb->vb2_queue;
struct rkisp1_params *params = vq->drv_priv;
@@ -1831,12 +2548,133 @@ static void rkisp1_params_vb2_buf_queue(struct vb2_buffer *vb)
spin_unlock_irq(&params->config_lock);
}
+static int rkisp1_params_prepare_ext_params(struct rkisp1_params *params,
+ struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf);
+ size_t header_size = offsetof(struct rkisp1_ext_params_cfg, data);
+ struct rkisp1_ext_params_cfg *cfg = params_buf->cfg;
+ size_t payload_size = vb2_get_plane_payload(vb, 0);
+ struct rkisp1_ext_params_cfg *usr_cfg =
+ vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+ size_t block_offset = 0;
+ size_t cfg_size;
+
+ /*
+ * Validate the buffer payload size before copying the parameters. The
+ * payload has to be smaller than the destination buffer size and larger
+ * than the header size.
+ */
+ if (payload_size > params->metafmt->buffersize) {
+ dev_dbg(params->rkisp1->dev,
+ "Too large buffer payload size %zu\n", payload_size);
+ return -EINVAL;
+ }
+
+ if (payload_size < header_size) {
+ dev_dbg(params->rkisp1->dev,
+ "Buffer payload %zu smaller than header size %zu\n",
+ payload_size, header_size);
+ return -EINVAL;
+ }
+
+ /*
+ * Copy the parameters buffer to the internal scratch buffer to avoid
+ * userspace modifying the buffer content while the driver processes it.
+ */
+ memcpy(cfg, usr_cfg, payload_size);
+
+ /* Only v1 is supported at the moment. */
+ if (cfg->version != RKISP1_EXT_PARAM_BUFFER_V1) {
+ dev_dbg(params->rkisp1->dev,
+ "Unsupported extensible format version: %u\n",
+ cfg->version);
+ return -EINVAL;
+ }
+
+ /* Validate the size reported in the parameters buffer header. */
+ cfg_size = header_size + cfg->data_size;
+ if (cfg_size != payload_size) {
+ dev_dbg(params->rkisp1->dev,
+ "Data size %zu different than buffer payload size %zu\n",
+ cfg_size, payload_size);
+ return -EINVAL;
+ }
+
+ /* Walk the list of parameter blocks and validate them. */
+ cfg_size = cfg->data_size;
+ while (cfg_size >= sizeof(struct rkisp1_ext_params_block_header)) {
+ const struct rkisp1_ext_params_block_header *block;
+ const struct rkisp1_ext_params_handler *handler;
+
+ block = (const struct rkisp1_ext_params_block_header *)
+ &cfg->data[block_offset];
+
+ if (block->type >= ARRAY_SIZE(rkisp1_ext_params_handlers)) {
+ dev_dbg(params->rkisp1->dev,
+ "Invalid parameters block type\n");
+ return -EINVAL;
+ }
+
+ if (block->size > cfg_size) {
+ dev_dbg(params->rkisp1->dev,
+ "Premature end of parameters data\n");
+ return -EINVAL;
+ }
+
+ if ((block->flags & (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE |
+ RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) ==
+ (RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE |
+ RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE)) {
+ dev_dbg(params->rkisp1->dev,
+ "Invalid parameters block flags\n");
+ return -EINVAL;
+ }
+
+ handler = &rkisp1_ext_params_handlers[block->type];
+ if (block->size != handler->size) {
+ dev_dbg(params->rkisp1->dev,
+ "Invalid parameters block size\n");
+ return -EINVAL;
+ }
+
+ block_offset += block->size;
+ cfg_size -= block->size;
+ }
+
+ if (cfg_size) {
+ dev_dbg(params->rkisp1->dev,
+ "Unexpected data after the parameters buffer end\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb)
{
- if (vb2_plane_size(vb, 0) < sizeof(struct rkisp1_params_cfg))
+ struct rkisp1_params *params = vb->vb2_queue->drv_priv;
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct rkisp1_params_buffer *params_buf = to_rkisp1_params_buffer(vbuf);
+ struct rkisp1_params_cfg *cfg = vb2_plane_vaddr(&vbuf->vb2_buf, 0);
+ size_t payload = vb2_get_plane_payload(vb, 0);
+
+ if (params->metafmt->dataformat == V4L2_META_FMT_RK_ISP1_EXT_PARAMS)
+ return rkisp1_params_prepare_ext_params(params, vb);
+
+ /*
+ * For the fixed parameters format the payload size must be exactly the
+ * size of the parameters structure.
+ */
+ if (payload != sizeof(*cfg))
return -EINVAL;
- vb2_set_plane_payload(vb, 0, sizeof(struct rkisp1_params_cfg));
+ /*
+ * Copy the parameters buffer to the internal scratch buffer to avoid
+ * userspace modifying the buffer content while the driver processes it.
+ */
+ memcpy(params_buf->cfg, cfg, payload);
return 0;
}
@@ -1844,7 +2682,7 @@ static int rkisp1_params_vb2_buf_prepare(struct vb2_buffer *vb)
static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq)
{
struct rkisp1_params *params = vq->drv_priv;
- struct rkisp1_buffer *buf;
+ struct rkisp1_params_buffer *buf;
LIST_HEAD(tmp_list);
/*
@@ -1858,16 +2696,17 @@ static void rkisp1_params_vb2_stop_streaming(struct vb2_queue *vq)
list_for_each_entry(buf, &tmp_list, queue)
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+
+ params->enabled_blocks = 0;
}
static const struct vb2_ops rkisp1_params_vb2_ops = {
.queue_setup = rkisp1_params_vb2_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
+ .buf_init = rkisp1_params_vb2_buf_init,
+ .buf_cleanup = rkisp1_params_vb2_buf_cleanup,
.buf_queue = rkisp1_params_vb2_buf_queue,
.buf_prepare = rkisp1_params_vb2_buf_prepare,
.stop_streaming = rkisp1_params_vb2_stop_streaming,
-
};
static const struct v4l2_file_operations rkisp1_params_fops = {
@@ -1890,26 +2729,13 @@ static int rkisp1_params_init_vb2_queue(struct vb2_queue *q,
q->drv_priv = params;
q->ops = &rkisp1_params_vb2_ops;
q->mem_ops = &vb2_vmalloc_memops;
- q->buf_struct_size = sizeof(struct rkisp1_buffer);
+ q->buf_struct_size = sizeof(struct rkisp1_params_buffer);
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &node->vlock;
return vb2_queue_init(q);
}
-static void rkisp1_init_params(struct rkisp1_params *params)
-{
- params->vdev_fmt.fmt.meta.dataformat =
- V4L2_META_FMT_RK_ISP1_PARAMS;
- params->vdev_fmt.fmt.meta.buffersize =
- sizeof(struct rkisp1_params_cfg);
-
- if (params->rkisp1->info->isp_ver == RKISP1_V12)
- params->ops = &rkisp1_v12_params_ops;
- else
- params->ops = &rkisp1_v10_params_ops;
-}
-
int rkisp1_params_register(struct rkisp1_device *rkisp1)
{
struct rkisp1_params *params = &rkisp1->params;
@@ -1938,7 +2764,14 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1)
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
vdev->vfl_dir = VFL_DIR_TX;
rkisp1_params_init_vb2_queue(vdev->queue, params);
- rkisp1_init_params(params);
+
+ params->metafmt = &rkisp1_params_formats[RKISP1_PARAMS_FIXED];
+
+ if (params->rkisp1->info->isp_ver == RKISP1_V12)
+ params->ops = &rkisp1_v12_params_ops;
+ else
+ params->ops = &rkisp1_v10_params_ops;
+
video_set_drvdata(vdev, params);
node->pad.flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
index fccf4c17ee8d..139177db9c6d 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -327,13 +327,6 @@
#define RKISP1_CIF_IMG_EFF_CTRL_CFG_UPD BIT(4)
#define RKISP1_CIF_IMG_EFF_CTRL_YCBCR_FULL BIT(5)
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_BLACKWHITE_SHIFT 0
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_NEGATIVE_SHIFT 1
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SEPIA_SHIFT 2
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_COLOR_SEL_SHIFT 3
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_EMBOSS_SHIFT 4
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SKETCH_SHIFT 5
-#define RKISP1_CIF_IMG_EFF_CTRL_MODE_SHARPEN_SHIFT 6
#define RKISP1_CIF_IMG_EFF_CTRL_MODE_MASK 0xe
/* IMG_EFF_COLOR_SEL */
@@ -704,6 +697,12 @@
#define RKISP1_CIF_ISP_DPF_SPATIAL_COEFF_MAX 0x1f
#define RKISP1_CIF_ISP_DPF_NLL_COEFF_N_MAX 0x3ff
+/* COMPAND */
+#define RKISP1_CIF_ISP_COMPAND_CTRL_EXPAND_ENABLE BIT(0)
+#define RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE BIT(1)
+#define RKISP1_CIF_ISP_COMPAND_CTRL_SOFT_RESET_FLAG BIT(2)
+#define RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE BIT(3)
+
/* =================================================================== */
/* CIF Registers */
/* =================================================================== */
@@ -1394,6 +1393,23 @@
#define RKISP1_CIF_ISP_VSM_DELTA_H (RKISP1_CIF_ISP_VSM_BASE + 0x0000001c)
#define RKISP1_CIF_ISP_VSM_DELTA_V (RKISP1_CIF_ISP_VSM_BASE + 0x00000020)
+#define RKISP1_CIF_ISP_COMPAND_BASE 0x00003200
+#define RKISP1_CIF_ISP_COMPAND_CTRL (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000000)
+#define RKISP1_CIF_ISP_COMPAND_BLS_A_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000004)
+#define RKISP1_CIF_ISP_COMPAND_BLS_B_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000008)
+#define RKISP1_CIF_ISP_COMPAND_BLS_C_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000000c)
+#define RKISP1_CIF_ISP_COMPAND_BLS_D_FIXED (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000010)
+#define RKISP1_CIF_ISP_COMPAND_EXPAND_PX_N(n) (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000014 + (n) * 4)
+#define RKISP1_CIF_ISP_COMPAND_COMPRESS_PX_N(n) (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000040 + (n) * 4)
+#define RKISP1_CIF_ISP_COMPAND_EXPAND_Y_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000006c)
+#define RKISP1_CIF_ISP_COMPAND_EXPAND_Y_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000070)
+#define RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000074)
+#define RKISP1_CIF_ISP_COMPAND_COMPRESS_Y_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000078)
+#define RKISP1_CIF_ISP_COMPAND_EXPAND_X_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x0000007c)
+#define RKISP1_CIF_ISP_COMPAND_EXPAND_X_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000080)
+#define RKISP1_CIF_ISP_COMPAND_COMPRESS_X_ADDR (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000084)
+#define RKISP1_CIF_ISP_COMPAND_COMPRESS_X_WRITE_DATA (RKISP1_CIF_ISP_COMPAND_BASE + 0x00000088)
+
#define RKISP1_CIF_ISP_CSI0_BASE 0x00007000
#define RKISP1_CIF_ISP_CSI0_CTRL0 (RKISP1_CIF_ISP_CSI0_BASE + 0x00000000)
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
index 6f3931ca5b51..8e6b753d3081 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
@@ -135,11 +135,11 @@ static void rkisp1_dcrop_disable(struct rkisp1_resizer *rsz,
/* configure dual-crop unit */
static void rkisp1_dcrop_config(struct rkisp1_resizer *rsz,
- struct v4l2_subdev_state *sd_state)
+ const struct v4l2_subdev_state *sd_state)
{
struct rkisp1_device *rkisp1 = rsz->rkisp1;
- struct v4l2_mbus_framefmt *sink_fmt;
- struct v4l2_rect *sink_crop;
+ const struct v4l2_mbus_framefmt *sink_fmt;
+ const struct v4l2_rect *sink_crop;
u32 dc_ctrl;
sink_crop = v4l2_subdev_state_get_crop(sd_state, RKISP1_RSZ_PAD_SINK);
@@ -264,7 +264,7 @@ static void rkisp1_rsz_config_regs(struct rkisp1_resizer *rsz,
}
static void rkisp1_rsz_config(struct rkisp1_resizer *rsz,
- struct v4l2_subdev_state *sd_state,
+ const struct v4l2_subdev_state *sd_state,
enum rkisp1_shadow_regs_when when)
{
const struct rkisp1_rsz_yuv_mbus_info *sink_yuv_info, *src_yuv_info;
@@ -494,10 +494,10 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
sink_fmt->width = clamp_t(u32, format->width,
RKISP1_ISP_MIN_WIDTH,
- RKISP1_ISP_MAX_WIDTH);
+ rsz->rkisp1->info->max_width);
sink_fmt->height = clamp_t(u32, format->height,
RKISP1_ISP_MIN_HEIGHT,
- RKISP1_ISP_MAX_HEIGHT);
+ rsz->rkisp1->info->max_height);
/*
* Adjust the color space fields. Accept any color primaries and
@@ -600,7 +600,7 @@ static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd,
if (sel->target != V4L2_SEL_TGT_CROP || sel->pad == RKISP1_RSZ_PAD_SRC)
return -EINVAL;
- dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%dx%d\n", __func__,
+ dev_dbg(rsz->rkisp1->dev, "%s: pad: %d sel(%d,%d)/%ux%u\n", __func__,
sel->pad, sel->r.left, sel->r.top, sel->r.width, sel->r.height);
rkisp1_rsz_set_sink_crop(rsz, sd_state, &sel->r);
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c
index 2795eef91bdd..d5fdb8f82dc7 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-stats.c
@@ -150,8 +150,6 @@ static const struct vb2_ops rkisp1_stats_vb2_ops = {
.queue_setup = rkisp1_stats_vb2_queue_setup,
.buf_queue = rkisp1_stats_vb2_buf_queue,
.buf_prepare = rkisp1_stats_vb2_buf_prepare,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = rkisp1_stats_vb2_stop_streaming,
};
@@ -304,48 +302,25 @@ static void rkisp1_stats_get_hst_meas_v12(struct rkisp1_stats *stats,
static void rkisp1_stats_get_bls_meas(struct rkisp1_stats *stats,
struct rkisp1_stat_buffer *pbuf)
{
+ static const u32 regs[] = {
+ RKISP1_CIF_ISP_BLS_A_MEASURED,
+ RKISP1_CIF_ISP_BLS_B_MEASURED,
+ RKISP1_CIF_ISP_BLS_C_MEASURED,
+ RKISP1_CIF_ISP_BLS_D_MEASURED,
+ };
struct rkisp1_device *rkisp1 = stats->rkisp1;
const struct rkisp1_mbus_info *in_fmt = rkisp1->isp.sink_fmt;
struct rkisp1_cif_isp_bls_meas_val *bls_val;
+ u32 swapped[4];
+
+ rkisp1_bls_swap_regs(in_fmt->bayer_pat, regs, swapped);
bls_val = &pbuf->params.ae.bls_val;
- if (in_fmt->bayer_pat == RKISP1_RAW_BGGR) {
- bls_val->meas_b =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
- bls_val->meas_gb =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
- bls_val->meas_gr =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
- bls_val->meas_r =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
- } else if (in_fmt->bayer_pat == RKISP1_RAW_GBRG) {
- bls_val->meas_gb =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
- bls_val->meas_b =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
- bls_val->meas_r =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
- bls_val->meas_gr =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
- } else if (in_fmt->bayer_pat == RKISP1_RAW_GRBG) {
- bls_val->meas_gr =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
- bls_val->meas_r =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
- bls_val->meas_b =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
- bls_val->meas_gb =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
- } else if (in_fmt->bayer_pat == RKISP1_RAW_RGGB) {
- bls_val->meas_r =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_A_MEASURED);
- bls_val->meas_gr =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_B_MEASURED);
- bls_val->meas_gb =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_C_MEASURED);
- bls_val->meas_b =
- rkisp1_read(rkisp1, RKISP1_CIF_ISP_BLS_D_MEASURED);
- }
+
+ bls_val->meas_r = rkisp1_read(rkisp1, swapped[0]);
+ bls_val->meas_gr = rkisp1_read(rkisp1, swapped[1]);
+ bls_val->meas_gb = rkisp1_read(rkisp1, swapped[2]);
+ bls_val->meas_b = rkisp1_read(rkisp1, swapped[3]);
}
static const struct rkisp1_stats_ops rkisp1_v10_stats_ops = {
diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c
index 618ae55fe396..a06d7cace92f 100644
--- a/drivers/media/platform/samsung/exynos-gsc/gsc-core.c
+++ b/drivers/media/platform/samsung/exynos-gsc/gsc-core.c
@@ -1225,7 +1225,7 @@ static void gsc_remove(struct platform_device *pdev)
static int gsc_m2m_suspend(struct gsc_dev *gsc)
{
unsigned long flags;
- int timeout;
+ long time_left;
spin_lock_irqsave(&gsc->slock, flags);
if (!gsc_m2m_pending(gsc)) {
@@ -1236,12 +1236,12 @@ static int gsc_m2m_suspend(struct gsc_dev *gsc)
set_bit(ST_M2M_SUSPENDING, &gsc->state);
spin_unlock_irqrestore(&gsc->slock, flags);
- timeout = wait_event_timeout(gsc->irq_queue,
- test_bit(ST_M2M_SUSPENDED, &gsc->state),
- GSC_SHUTDOWN_TIMEOUT);
+ time_left = wait_event_timeout(gsc->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &gsc->state),
+ GSC_SHUTDOWN_TIMEOUT);
clear_bit(ST_M2M_SUSPENDING, &gsc->state);
- return timeout == 0 ? -EAGAIN : 0;
+ return time_left == 0 ? -EAGAIN : 0;
}
static void gsc_m2m_resume(struct gsc_dev *gsc)
@@ -1309,7 +1309,7 @@ static const struct dev_pm_ops gsc_pm_ops = {
static struct platform_driver gsc_driver = {
.probe = gsc_probe,
- .remove_new = gsc_remove,
+ .remove = gsc_remove,
.driver = {
.name = GSC_MODULE_NAME,
.pm = &gsc_pm_ops,
diff --git a/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c
index b7854ce5fb8e..4bda1c369c44 100644
--- a/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c
+++ b/drivers/media/platform/samsung/exynos-gsc/gsc-m2m.c
@@ -276,8 +276,6 @@ static const struct vb2_ops gsc_m2m_qops = {
.queue_setup = gsc_m2m_queue_setup,
.buf_prepare = gsc_m2m_buf_prepare,
.buf_queue = gsc_m2m_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = gsc_m2m_stop_streaming,
.start_streaming = gsc_m2m_start_streaming,
};
diff --git a/drivers/media/platform/samsung/exynos4-is/common.c b/drivers/media/platform/samsung/exynos4-is/common.c
index e41333535eac..77007f1a909b 100644
--- a/drivers/media/platform/samsung/exynos4-is/common.c
+++ b/drivers/media/platform/samsung/exynos4-is/common.c
@@ -44,4 +44,5 @@ void __fimc_vidioc_querycap(struct device *dev, struct v4l2_capability *cap)
}
EXPORT_SYMBOL(__fimc_vidioc_querycap);
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS4 SoC Camera Subsystem driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
index ffa4ea21387d..5b412afd7d60 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
@@ -455,8 +455,6 @@ static const struct vb2_ops fimc_capture_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
};
@@ -702,7 +700,7 @@ static void fimc_capture_try_selection(struct fimc_ctx *ctx,
r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height);
r->left = round_down(r->left, var->hor_offs_align);
- dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d",
+ dbg("target %#x: (%d,%d)/%ux%u, sink fmt: %dx%d",
target, r->left, r->top, r->width, r->height,
sink->f_width, sink->f_height);
}
@@ -1624,7 +1622,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
r->height = f->height;
}
- dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d",
+ dbg("target %#x: (%d,%d)/%ux%u, f_w: %d, f_h: %d",
sel->pad, r->left, r->top, r->width, r->height,
f->f_width, f->f_height);
@@ -1673,7 +1671,7 @@ static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
spin_unlock_irqrestore(&fimc->slock, flags);
}
- dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top,
+ dbg("target %#x: (%d,%d)/%ux%u", sel->target, r->left, r->top,
r->width, r->height);
mutex_unlock(&fimc->lock);
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-core.c b/drivers/media/platform/samsung/exynos4-is/fimc-core.c
index aae74b501a42..2c9edd0a559b 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-core.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-core.c
@@ -822,7 +822,7 @@ err:
static int fimc_m2m_suspend(struct fimc_dev *fimc)
{
unsigned long flags;
- int timeout;
+ long time_left;
spin_lock_irqsave(&fimc->slock, flags);
if (!fimc_m2m_pending(fimc)) {
@@ -833,12 +833,12 @@ static int fimc_m2m_suspend(struct fimc_dev *fimc)
set_bit(ST_M2M_SUSPENDING, &fimc->state);
spin_unlock_irqrestore(&fimc->slock, flags);
- timeout = wait_event_timeout(fimc->irq_queue,
- test_bit(ST_M2M_SUSPENDED, &fimc->state),
- FIMC_SHUTDOWN_TIMEOUT);
+ time_left = wait_event_timeout(fimc->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &fimc->state),
+ FIMC_SHUTDOWN_TIMEOUT);
clear_bit(ST_M2M_SUSPENDING, &fimc->state);
- return timeout == 0 ? -EAGAIN : 0;
+ return time_left == 0 ? -EAGAIN : 0;
}
static int fimc_m2m_resume(struct fimc_dev *fimc)
@@ -1157,7 +1157,7 @@ static const struct dev_pm_ops fimc_pm_ops = {
static struct platform_driver fimc_driver = {
.probe = fimc_probe,
- .remove_new = fimc_remove,
+ .remove = fimc_remove,
.driver = {
.of_match_table = fimc_of_match,
.name = FIMC_DRIVER_NAME,
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c
index 7a48fad1df16..ac67a04e5eeb 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.c
@@ -12,137 +12,6 @@
#include "fimc-is-errno.h"
-const char *fimc_is_param_strerr(unsigned int error)
-{
- switch (error) {
- case ERROR_COMMON_CMD:
- return "ERROR_COMMON_CMD: Invalid Command";
- case ERROR_COMMON_PARAMETER:
- return "ERROR_COMMON_PARAMETER: Invalid Parameter";
- case ERROR_COMMON_SETFILE_LOAD:
- return "ERROR_COMMON_SETFILE_LOAD: Illegal Setfile Loading";
- case ERROR_COMMON_SETFILE_ADJUST:
- return "ERROR_COMMON_SETFILE_ADJUST: Setfile isn't adjusted";
- case ERROR_COMMON_SETFILE_INDEX:
- return "ERROR_COMMON_SETFILE_INDEX: Invalid setfile index";
- case ERROR_COMMON_INPUT_PATH:
- return "ERROR_COMMON_INPUT_PATH: Input path can be changed in ready state";
- case ERROR_COMMON_INPUT_INIT:
- return "ERROR_COMMON_INPUT_INIT: IP can not start if input path is not set";
- case ERROR_COMMON_OUTPUT_PATH:
- return "ERROR_COMMON_OUTPUT_PATH: Output path can be changed in ready state (stop)";
- case ERROR_COMMON_OUTPUT_INIT:
- return "ERROR_COMMON_OUTPUT_INIT: IP can not start if output path is not set";
- case ERROR_CONTROL_BYPASS:
- return "ERROR_CONTROL_BYPASS";
- case ERROR_OTF_INPUT_FORMAT:
- return "ERROR_OTF_INPUT_FORMAT: Invalid format (DRC: YUV444, FD: YUV444, 422, 420)";
- case ERROR_OTF_INPUT_WIDTH:
- return "ERROR_OTF_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)";
- case ERROR_OTF_INPUT_HEIGHT:
- return "ERROR_OTF_INPUT_HEIGHT: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
- case ERROR_OTF_INPUT_BIT_WIDTH:
- return "ERROR_OTF_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
- case ERROR_DMA_INPUT_WIDTH:
- return "ERROR_DMA_INPUT_WIDTH: Invalid width (DRC: 128~8192, FD: 32~8190)";
- case ERROR_DMA_INPUT_HEIGHT:
- return "ERROR_DMA_INPUT_HEIGHT: Invalid height (DRC: 64~8192, FD: 16~8190)";
- case ERROR_DMA_INPUT_FORMAT:
- return "ERROR_DMA_INPUT_FORMAT: Invalid format (DRC: YUV444 or YUV422, FD: YUV444,422,420)";
- case ERROR_DMA_INPUT_BIT_WIDTH:
- return "ERROR_DMA_INPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
- case ERROR_DMA_INPUT_ORDER:
- return "ERROR_DMA_INPUT_ORDER: Invalid order(DRC: YYCbCr,YCbYCr,FD:NO,YYCbCr,YCbYCr,CbCr,CrCb)";
- case ERROR_DMA_INPUT_PLANE:
- return "ERROR_DMA_INPUT_PLANE: Invalid plane (DRC: 3, FD: 1, 2, 3)";
- case ERROR_OTF_OUTPUT_WIDTH:
- return "ERROR_OTF_OUTPUT_WIDTH: Invalid width (DRC: 128~8192)";
- case ERROR_OTF_OUTPUT_HEIGHT:
- return "ERROR_OTF_OUTPUT_HEIGHT: Invalid height (DRC: 64~8192)";
- case ERROR_OTF_OUTPUT_FORMAT:
- return "ERROR_OTF_OUTPUT_FORMAT: Invalid format (DRC: YUV444)";
- case ERROR_OTF_OUTPUT_BIT_WIDTH:
- return "ERROR_OTF_OUTPUT_BIT_WIDTH: Invalid bit-width (DRC: 8~12bits, FD: 8bit)";
- case ERROR_DMA_OUTPUT_WIDTH:
- return "ERROR_DMA_OUTPUT_WIDTH";
- case ERROR_DMA_OUTPUT_HEIGHT:
- return "ERROR_DMA_OUTPUT_HEIGHT";
- case ERROR_DMA_OUTPUT_FORMAT:
- return "ERROR_DMA_OUTPUT_FORMAT";
- case ERROR_DMA_OUTPUT_BIT_WIDTH:
- return "ERROR_DMA_OUTPUT_BIT_WIDTH";
- case ERROR_DMA_OUTPUT_PLANE:
- return "ERROR_DMA_OUTPUT_PLANE";
- case ERROR_DMA_OUTPUT_ORDER:
- return "ERROR_DMA_OUTPUT_ORDER";
-
- /* Sensor Error(100~199) */
- case ERROR_SENSOR_I2C_FAIL:
- return "ERROR_SENSOR_I2C_FAIL";
- case ERROR_SENSOR_INVALID_FRAMERATE:
- return "ERROR_SENSOR_INVALID_FRAMERATE";
- case ERROR_SENSOR_INVALID_EXPOSURETIME:
- return "ERROR_SENSOR_INVALID_EXPOSURETIME";
- case ERROR_SENSOR_INVALID_SIZE:
- return "ERROR_SENSOR_INVALID_SIZE";
- case ERROR_SENSOR_INVALID_SETTING:
- return "ERROR_SENSOR_INVALID_SETTING";
- case ERROR_SENSOR_ACTUATOR_INIT_FAIL:
- return "ERROR_SENSOR_ACTUATOR_INIT_FAIL";
- case ERROR_SENSOR_INVALID_AF_POS:
- return "ERROR_SENSOR_INVALID_AF_POS";
- case ERROR_SENSOR_UNSUPPORT_FUNC:
- return "ERROR_SENSOR_UNSUPPORT_FUNC";
- case ERROR_SENSOR_UNSUPPORT_PERI:
- return "ERROR_SENSOR_UNSUPPORT_PERI";
- case ERROR_SENSOR_UNSUPPORT_AF:
- return "ERROR_SENSOR_UNSUPPORT_AF";
-
- /* ISP Error (200~299) */
- case ERROR_ISP_AF_BUSY:
- return "ERROR_ISP_AF_BUSY";
- case ERROR_ISP_AF_INVALID_COMMAND:
- return "ERROR_ISP_AF_INVALID_COMMAND";
- case ERROR_ISP_AF_INVALID_MODE:
- return "ERROR_ISP_AF_INVALID_MODE";
-
- /* DRC Error (300~399) */
- /* FD Error (400~499) */
- case ERROR_FD_CONFIG_MAX_NUMBER_STATE:
- return "ERROR_FD_CONFIG_MAX_NUMBER_STATE";
- case ERROR_FD_CONFIG_MAX_NUMBER_INVALID:
- return "ERROR_FD_CONFIG_MAX_NUMBER_INVALID";
- case ERROR_FD_CONFIG_YAW_ANGLE_STATE:
- return "ERROR_FD_CONFIG_YAW_ANGLE_STATE";
- case ERROR_FD_CONFIG_YAW_ANGLE_INVALID:
- return "ERROR_FD_CONFIG_YAW_ANGLE_INVALID\n";
- case ERROR_FD_CONFIG_ROLL_ANGLE_STATE:
- return "ERROR_FD_CONFIG_ROLL_ANGLE_STATE";
- case ERROR_FD_CONFIG_ROLL_ANGLE_INVALID:
- return "ERROR_FD_CONFIG_ROLL_ANGLE_INVALID";
- case ERROR_FD_CONFIG_SMILE_MODE_INVALID:
- return "ERROR_FD_CONFIG_SMILE_MODE_INVALID";
- case ERROR_FD_CONFIG_BLINK_MODE_INVALID:
- return "ERROR_FD_CONFIG_BLINK_MODE_INVALID";
- case ERROR_FD_CONFIG_EYES_DETECT_INVALID:
- return "ERROR_FD_CONFIG_EYES_DETECT_INVALID";
- case ERROR_FD_CONFIG_MOUTH_DETECT_INVALID:
- return "ERROR_FD_CONFIG_MOUTH_DETECT_INVALID";
- case ERROR_FD_CONFIG_ORIENTATION_STATE:
- return "ERROR_FD_CONFIG_ORIENTATION_STATE";
- case ERROR_FD_CONFIG_ORIENTATION_INVALID:
- return "ERROR_FD_CONFIG_ORIENTATION_INVALID";
- case ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID:
- return "ERROR_FD_CONFIG_ORIENTATION_VALUE_INVALID";
- case ERROR_FD_RESULT:
- return "ERROR_FD_RESULT";
- case ERROR_FD_MODE:
- return "ERROR_FD_MODE";
- default:
- return "Unknown";
- }
-}
-
const char *fimc_is_strerr(unsigned int error)
{
error &= ~IS_ERROR_TIME_OUT_FLAG;
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h
index 809e117331c0..fa8204ffec7b 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-errno.h
@@ -240,6 +240,5 @@ enum fimc_is_error {
};
const char *fimc_is_strerr(unsigned int error);
-const char *fimc_is_param_strerr(unsigned int error);
#endif /* FIMC_IS_ERR_H_ */
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
index 44363c4241d5..b243cbb1d010 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
@@ -137,7 +137,7 @@ static const struct of_device_id fimc_is_i2c_of_match[] = {
static struct platform_driver fimc_is_i2c_driver = {
.probe = fimc_is_i2c_probe,
- .remove_new = fimc_is_i2c_remove,
+ .remove = fimc_is_i2c_remove,
.driver = {
.of_match_table = fimc_is_i2c_of_match,
.name = "fimc-isp-i2c",
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c
index 9c816ae3b3e5..443362da8cc8 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.c
@@ -204,15 +204,6 @@ int __is_hw_update_params(struct fimc_is *is)
return ret;
}
-void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf)
-{
- struct isp_param *isp;
-
- isp = &is->config[is->config_index].isp;
- mf->width = isp->otf_input.width;
- mf->height = isp->otf_input.height;
-}
-
void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf)
{
unsigned int index = is->config_index;
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h
index 206904674927..10ad02f36fed 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-param.h
@@ -994,7 +994,6 @@ void fimc_is_set_initial_params(struct fimc_is *is);
unsigned int __get_pending_param_count(struct fimc_is *is);
int __is_hw_update_params(struct fimc_is *is);
-void __is_get_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf);
void __is_set_frame_size(struct fimc_is *is, struct v4l2_mbus_framefmt *mf);
void __is_set_sensor(struct fimc_is *is, int fps);
void __is_set_isp_aa_ae(struct fimc_is *is);
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
index 366e6393817d..5f9c44e825a5 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-regs.c
@@ -164,6 +164,7 @@ int fimc_is_hw_change_mode(struct fimc_is *is)
if (WARN_ON(is->config_index >= ARRAY_SIZE(cmd)))
return -EINVAL;
+ fimc_is_hw_wait_intmsr0_intmsd0(is);
mcuctl_write(cmd[is->config_index], is, MCUCTL_REG_ISSR(0));
mcuctl_write(is->sensor_index, is, MCUCTL_REG_ISSR(1));
mcuctl_write(is->setfile.sub_index, is, MCUCTL_REG_ISSR(2));
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
index 39aab667910d..2e8fe9e49735 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
@@ -963,7 +963,7 @@ static const struct dev_pm_ops fimc_is_pm_ops = {
static struct platform_driver fimc_is_driver = {
.probe = fimc_is_probe,
- .remove_new = fimc_is_remove,
+ .remove = fimc_is_remove,
.driver = {
.of_match_table = fimc_is_of_match,
.name = FIMC_IS_DRV_NAME,
@@ -999,4 +999,5 @@ module_exit(fimc_is_module_exit);
MODULE_ALIAS("platform:" FIMC_IS_DRV_NAME);
MODULE_AUTHOR("Younghwan Joo <yhwan.joo@samsung.com>");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c
index 06c4352562b3..ad219ac1b951 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp-video.c
@@ -255,8 +255,6 @@ static const struct vb2_ops isp_video_capture_qops = {
.queue_setup = isp_video_capture_queue_setup,
.buf_prepare = isp_video_capture_buffer_prepare,
.buf_queue = isp_video_capture_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = isp_video_capture_start_streaming,
.stop_streaming = isp_video_capture_stop_streaming,
};
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
index d1d860fa3454..0ce293b0718b 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
@@ -441,8 +441,6 @@ static const struct vb2_ops fimc_lite_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
};
@@ -613,7 +611,7 @@ static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r)
r->left = round_down(r->left, fimc->dd->win_hor_offs_align);
r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height);
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d\n",
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%ux%u, sink fmt: %dx%d\n",
r->left, r->top, r->width, r->height,
frame->f_width, frame->f_height);
}
@@ -633,7 +631,7 @@ static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r)
r->left = round_down(r->left, fimc->dd->out_hor_offs_align);
r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height);
- v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d\n",
+ v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%ux%u, source fmt: %dx%d\n",
r->left, r->top, r->width, r->height,
frame->f_width, frame->f_height);
}
@@ -1142,7 +1140,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&fimc->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %d, f_h: %d\n",
__func__, f->rect.left, f->rect.top, f->rect.width,
f->rect.height, f->f_width, f->f_height);
@@ -1176,7 +1174,7 @@ static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&fimc->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d\n",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %d, f_h: %d\n",
__func__, f->rect.left, f->rect.top, f->rect.width,
f->rect.height, f->f_width, f->f_height);
@@ -1654,7 +1652,7 @@ MODULE_DEVICE_TABLE(of, flite_of_match);
static struct platform_driver fimc_lite_driver = {
.probe = fimc_lite_probe,
- .remove_new = fimc_lite_remove,
+ .remove = fimc_lite_remove,
.driver = {
.of_match_table = flite_of_match,
.name = FIMC_LITE_DRV_NAME,
@@ -1662,5 +1660,6 @@ static struct platform_driver fimc_lite_driver = {
}
};
module_platform_driver(fimc_lite_driver);
+MODULE_DESCRIPTION("Samsung EXYNOS FIMC-LITE (camera host interface) driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME);
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c
index 199997eec1cc..951433c8e92a 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-m2m.c
@@ -216,8 +216,6 @@ static const struct vb2_ops fimc_qops = {
.queue_setup = fimc_queue_setup,
.buf_prepare = fimc_buf_prepare,
.buf_queue = fimc_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = stop_streaming,
.start_streaming = start_streaming,
};
diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c
index 5f10bb4eb4f7..b5ee3c547789 100644
--- a/drivers/media/platform/samsung/exynos4-is/media-dev.c
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c
@@ -1564,7 +1564,7 @@ MODULE_DEVICE_TABLE(of, fimc_md_of_match);
static struct platform_driver fimc_md_driver = {
.probe = fimc_md_probe,
- .remove_new = fimc_md_remove,
+ .remove = fimc_md_remove,
.driver = {
.of_match_table = of_match_ptr(fimc_md_of_match),
.name = "s5p-fimc-md",
diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.h b/drivers/media/platform/samsung/exynos4-is/media-dev.h
index 786264cf79dc..ea496670d4b5 100644
--- a/drivers/media/platform/samsung/exynos4-is/media-dev.h
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.h
@@ -178,8 +178,9 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on);
#ifdef CONFIG_OF
static inline bool fimc_md_is_isp_available(struct device_node *node)
{
- node = of_get_child_by_name(node, FIMC_IS_OF_NODE_NAME);
- return node ? of_device_is_available(node) : false;
+ struct device_node *child __free(device_node) =
+ of_get_available_child_by_name(node, FIMC_IS_OF_NODE_NAME);
+ return child;
}
#else
#define fimc_md_is_isp_available(node) (false)
diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
index 4b9b20ba3504..452880b5350c 100644
--- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
@@ -940,13 +940,19 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
state->supplies);
goto unlock;
}
- clk_enable(state->clock[CSIS_CLK_GATE]);
+ ret = clk_enable(state->clock[CSIS_CLK_GATE]);
+ if (ret) {
+ phy_power_off(state->phy);
+ regulator_bulk_disable(CSIS_NUM_SUPPLIES,
+ state->supplies);
+ goto unlock;
+ }
}
if (state->flags & ST_STREAMING)
s5pcsis_start_stream(state);
state->flags &= ~ST_SUSPENDED;
- unlock:
+unlock:
mutex_unlock(&state->lock);
return ret ? -EAGAIN : 0;
}
@@ -1020,7 +1026,7 @@ MODULE_DEVICE_TABLE(of, s5pcsis_of_match);
static struct platform_driver s5pcsis_driver = {
.probe = s5pcsis_probe,
- .remove_new = s5pcsis_remove,
+ .remove = s5pcsis_remove,
.driver = {
.of_match_table = s5pcsis_of_match,
.name = CSIS_DRIVER_NAME,
diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
index be58260ea67e..3e566b65f417 100644
--- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
@@ -525,8 +525,6 @@ static const struct vb2_ops s3c_camif_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
};
@@ -1032,9 +1030,9 @@ static int s3c_camif_s_selection(struct file *file, void *priv,
vp->state |= ST_VP_CONFIG;
spin_unlock_irqrestore(&camif->slock, flags);
- pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%dx%d\n",
- sel->type, sel->target, sel->flags,
- sel->r.left, sel->r.top, sel->r.width, sel->r.height);
+ pr_debug("type: %#x, target: %#x, flags: %#x, (%d,%d)/%ux%u\n",
+ sel->type, sel->target, sel->flags,
+ sel->r.left, sel->r.top, sel->r.width, sel->r.height);
return 0;
}
@@ -1374,7 +1372,7 @@ static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd,
mutex_unlock(&camif->lock);
- v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d) %dx%d, size: %ux%u\n",
+ v4l2_dbg(1, debug, sd, "%s: crop: (%d,%d)/%ux%u, size: %ux%u\n",
__func__, crop->left, crop->top, crop->width,
crop->height, mf->width, mf->height);
@@ -1426,7 +1424,7 @@ static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r)
}
}
- v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%dx%d, fmt: %ux%u\n",
+ v4l2_dbg(1, debug, &camif->v4l2_dev, "crop: (%d,%d)/%ux%u, fmt: %ux%u\n",
r->left, r->top, r->width, r->height, mf->width, mf->height);
}
@@ -1466,7 +1464,7 @@ static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd,
}
mutex_unlock(&camif->lock);
- v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %u, f_h: %u\n",
+ v4l2_dbg(1, debug, sd, "%s: (%d,%d)/%ux%u, f_w: %u, f_h: %u\n",
__func__, crop->left, crop->top, crop->width, crop->height,
camif->mbus_fmt.width, camif->mbus_fmt.height);
diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c
index e4529f666e20..221e3c447f36 100644
--- a/drivers/media/platform/samsung/s3c-camif/camif-core.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c
@@ -527,10 +527,19 @@ static void s3c_camif_remove(struct platform_device *pdev)
static int s3c_camif_runtime_resume(struct device *dev)
{
struct camif_dev *camif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(camif->clock[CLK_GATE]);
+ if (ret)
+ return ret;
- clk_enable(camif->clock[CLK_GATE]);
/* null op on s3c244x */
- clk_enable(camif->clock[CLK_CAM]);
+ ret = clk_enable(camif->clock[CLK_CAM]);
+ if (ret) {
+ clk_disable(camif->clock[CLK_GATE]);
+ return ret;
+ }
+
return 0;
}
@@ -622,7 +631,7 @@ static const struct dev_pm_ops s3c_camif_pm_ops = {
static struct platform_driver s3c_camif_driver = {
.probe = s3c_camif_probe,
- .remove_new = s3c_camif_remove,
+ .remove = s3c_camif_remove,
.id_table = s3c_camif_driver_ids,
.driver = {
.name = S3C_CAMIF_DRIVER_NAME,
diff --git a/drivers/media/platform/samsung/s5p-g2d/g2d.c b/drivers/media/platform/samsung/s5p-g2d/g2d.c
index 89aeba47ed07..ffed16a34493 100644
--- a/drivers/media/platform/samsung/s5p-g2d/g2d.c
+++ b/drivers/media/platform/samsung/s5p-g2d/g2d.c
@@ -133,8 +133,6 @@ static const struct vb2_ops g2d_qops = {
.queue_setup = g2d_queue_setup,
.buf_prepare = g2d_buf_prepare,
.buf_queue = g2d_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq,
@@ -777,7 +775,7 @@ MODULE_DEVICE_TABLE(of, exynos_g2d_match);
static struct platform_driver g2d_pdrv = {
.probe = g2d_probe,
- .remove_new = g2d_remove,
+ .remove = g2d_remove,
.driver = {
.name = G2D_NAME,
.of_match_table = exynos_g2d_match,
diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
index d2c4a0178b3c..ac4cf269456a 100644
--- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-core.c
@@ -775,11 +775,14 @@ static void exynos4_jpeg_parse_decode_h_tbl(struct s5p_jpeg_ctx *ctx)
(unsigned long)vb2_plane_vaddr(&vb->vb2_buf, 0) + ctx->out_q.sos + 2;
jpeg_buffer.curr = 0;
- word = 0;
-
if (get_word_be(&jpeg_buffer, &word))
return;
- jpeg_buffer.size = (long)word - 2;
+
+ if (word < 2)
+ jpeg_buffer.size = 0;
+ else
+ jpeg_buffer.size = (long)word - 2;
+
jpeg_buffer.data += 2;
jpeg_buffer.curr = 0;
@@ -1058,6 +1061,7 @@ static int get_word_be(struct s5p_jpeg_buffer *buf, unsigned int *word)
if (byte == -1)
return -1;
*word = (unsigned int)byte | temp;
+
return 0;
}
@@ -1145,7 +1149,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
if (get_word_be(&jpeg_buffer, &word))
break;
length = (long)word - 2;
- if (!length)
+ if (length <= 0)
return false;
sof = jpeg_buffer.curr; /* after 0xffc0 */
sof_len = length;
@@ -1176,7 +1180,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
if (get_word_be(&jpeg_buffer, &word))
break;
length = (long)word - 2;
- if (!length)
+ if (length <= 0)
return false;
if (n_dqt >= S5P_JPEG_MAX_MARKER)
return false;
@@ -1189,7 +1193,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
if (get_word_be(&jpeg_buffer, &word))
break;
length = (long)word - 2;
- if (!length)
+ if (length <= 0)
return false;
if (n_dht >= S5P_JPEG_MAX_MARKER)
return false;
@@ -1214,6 +1218,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
if (get_word_be(&jpeg_buffer, &word))
break;
length = (long)word - 2;
+ /* No need to check underflows as skip() does it */
skip(&jpeg_buffer, length);
break;
}
@@ -2590,8 +2595,6 @@ static const struct vb2_ops s5p_jpeg_qops = {
.queue_setup = s5p_jpeg_queue_setup,
.buf_prepare = s5p_jpeg_buf_prepare,
.buf_queue = s5p_jpeg_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = s5p_jpeg_start_streaming,
.stop_streaming = s5p_jpeg_stop_streaming,
};
@@ -3160,7 +3163,7 @@ static void *jpeg_get_drv_data(struct device *dev)
static struct platform_driver s5p_jpeg_driver = {
.probe = s5p_jpeg_probe,
- .remove_new = s5p_jpeg_remove,
+ .remove = s5p_jpeg_remove,
.driver = {
.of_match_table = samsung_jpeg_match,
.name = S5P_JPEG_M2M_NAME,
diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c
index 637a5104d948..6657d294c10a 100644
--- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.c
@@ -427,11 +427,6 @@ void exynos3250_jpeg_clear_int_status(void __iomem *regs,
writel(value, regs + EXYNOS3250_JPGINTST);
}
-unsigned int exynos3250_jpeg_operating(void __iomem *regs)
-{
- return readl(regs + S5P_JPGOPR) & EXYNOS3250_JPGOPR_MASK;
-}
-
unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs)
{
return readl(regs + EXYNOS3250_JPGCNT) & EXYNOS3250_JPGCNT_MASK;
diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h
index 15af928fad76..709c61ae322c 100644
--- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos3250.h
@@ -45,7 +45,6 @@ void exynos3250_jpeg_rstart(void __iomem *regs);
unsigned int exynos3250_jpeg_get_int_status(void __iomem *regs);
void exynos3250_jpeg_clear_int_status(void __iomem *regs,
unsigned int value);
-unsigned int exynos3250_jpeg_operating(void __iomem *regs);
unsigned int exynos3250_jpeg_compressed_size(void __iomem *regs);
void exynos3250_jpeg_dec_stream_size(void __iomem *regs, unsigned int size);
void exynos3250_jpeg_dec_scaling_ratio(void __iomem *regs, unsigned int sratio);
diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c
index 0828cfa783fe..479288fc8c77 100644
--- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.c
@@ -185,11 +185,6 @@ unsigned int exynos4_jpeg_get_int_status(void __iomem *base)
return readl(base + EXYNOS4_INT_STATUS_REG);
}
-unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base)
-{
- return readl(base + EXYNOS4_FIFO_STATUS_REG);
-}
-
void exynos4_jpeg_set_huf_table_enable(void __iomem *base, int value)
{
unsigned int reg;
@@ -300,22 +295,8 @@ void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size)
writel(size, base + EXYNOS4_BITSTREAM_SIZE_REG);
}
-void exynos4_jpeg_get_frame_size(void __iomem *base,
- unsigned int *width, unsigned int *height)
-{
- *width = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) &
- EXYNOS4_DECODED_SIZE_MASK);
- *height = (readl(base + EXYNOS4_DECODE_XY_SIZE_REG) >> 16) &
- EXYNOS4_DECODED_SIZE_MASK;
-}
-
unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base)
{
return readl(base + EXYNOS4_DECODE_IMG_FMT_REG) &
EXYNOS4_JPEG_DECODED_IMG_FMT_MASK;
}
-
-void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size)
-{
- writel(size, base + EXYNOS4_INT_TIMER_COUNT_REG);
-}
diff --git a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h
index 3e2887526960..b941cc89e4ba 100644
--- a/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h
+++ b/drivers/media/platform/samsung/s5p-jpeg/jpeg-hw-exynos4.h
@@ -35,10 +35,6 @@ void exynos4_jpeg_select_dec_h_tbl(void __iomem *base, char c, char x);
void exynos4_jpeg_set_encode_hoff_cnt(void __iomem *base, unsigned int fmt);
void exynos4_jpeg_set_dec_bitstream_size(void __iomem *base, unsigned int size);
unsigned int exynos4_jpeg_get_stream_size(void __iomem *base);
-void exynos4_jpeg_get_frame_size(void __iomem *base,
- unsigned int *width, unsigned int *height);
unsigned int exynos4_jpeg_get_frame_fmt(void __iomem *base);
-unsigned int exynos4_jpeg_get_fifo_status(void __iomem *base);
-void exynos4_jpeg_set_timer_count(void __iomem *base, unsigned int size);
#endif /* JPEG_HW_EXYNOS4_H_ */
diff --git a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
index fa49fe580e1a..075a58b50b8c 100644
--- a/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
+++ b/drivers/media/platform/samsung/s5p-mfc/regs-mfc-v6.h
@@ -45,6 +45,7 @@
#define S5P_FIMV_H2R_CMD_WAKEUP_V6 8
#define S5P_FIMV_CH_LAST_FRAME_V6 9
#define S5P_FIMV_H2R_CMD_FLUSH_V6 10
+#define S5P_FIMV_H2R_CMD_NAL_ABORT_V6 11
/* RMVME: REALLOC used? */
#define S5P_FIMV_CH_FRAME_START_REALLOC_V6 5
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
index 50451984d59f..9f89bd2620c7 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
@@ -739,6 +739,20 @@ static irqreturn_t s5p_mfc_irq(int irq, void *priv)
ctx->state = MFCINST_RUNNING;
goto irq_cleanup_hw;
+ case S5P_MFC_R2H_CMD_ENC_BUFFER_FUL_RET:
+ ctx->state = MFCINST_NAL_ABORT;
+ s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
+ set_work_bit(ctx);
+ WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0);
+ s5p_mfc_hw_call(dev->mfc_ops, try_run, dev);
+ break;
+
+ case S5P_MFC_R2H_CMD_NAL_ABORT_RET:
+ ctx->state = MFCINST_ERROR;
+ s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst);
+ s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src);
+ goto irq_cleanup_hw;
+
default:
mfc_debug(2, "Unknown int reason\n");
s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev);
@@ -774,8 +788,10 @@ static int s5p_mfc_open(struct file *file)
int ret = 0;
mfc_debug_enter();
- if (mutex_lock_interruptible(&dev->mfc_mutex))
- return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&dev->mfc_mutex)) {
+ ret = -ERESTARTSYS;
+ goto err_enter;
+ }
dev->num_inst++; /* It is guarded by mfc_mutex in vfd */
/* Allocate memory for context */
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -933,7 +949,7 @@ err_pwr_enable:
if (dev->num_inst == 1) {
if (s5p_mfc_power_off(dev) < 0)
mfc_err("power off failed\n");
- del_timer_sync(&dev->watchdog_timer);
+ timer_delete_sync(&dev->watchdog_timer);
}
err_ctrls_setup:
s5p_mfc_dec_ctrls_delete(ctx);
@@ -946,6 +962,7 @@ err_no_ctx:
err_alloc:
dev->num_inst--;
mutex_unlock(&dev->mfc_mutex);
+err_enter:
mfc_debug_leave();
return ret;
}
@@ -982,7 +999,7 @@ static int s5p_mfc_release(struct file *file)
if (dev->num_inst == 0) {
mfc_debug(2, "Last instance\n");
s5p_mfc_deinit_hw(dev);
- del_timer_sync(&dev->watchdog_timer);
+ timer_delete_sync(&dev->watchdog_timer);
s5p_mfc_clock_off(dev);
if (s5p_mfc_power_off(dev) < 0)
mfc_err("Power off failed\n");
@@ -1458,7 +1475,7 @@ static void s5p_mfc_remove(struct platform_device *pdev)
}
mutex_unlock(&dev->mfc_mutex);
- del_timer_sync(&dev->watchdog_timer);
+ timer_delete_sync(&dev->watchdog_timer);
flush_work(&dev->watchdog_work);
video_unregister_device(dev->vfd_enc);
@@ -1721,7 +1738,7 @@ MODULE_DEVICE_TABLE(of, exynos_mfc_match);
static struct platform_driver s5p_mfc_driver = {
.probe = s5p_mfc_probe,
- .remove_new = s5p_mfc_remove,
+ .remove = s5p_mfc_remove,
.driver = {
.name = S5P_MFC_NAME,
.pm = &s5p_mfc_pm_ops,
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
index 3cc2a4f5c40a..86c316c1ff8f 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_common.h
@@ -141,6 +141,7 @@ enum s5p_mfc_inst_state {
MFCINST_RES_CHANGE_INIT,
MFCINST_RES_CHANGE_FLUSH,
MFCINST_RES_CHANGE_END,
+ MFCINST_NAL_ABORT,
};
/*
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c
index 91e102d4ec4e..3efbc3367906 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_dec.c
@@ -1161,8 +1161,6 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
static const struct vb2_ops s5p_mfc_dec_qops = {
.queue_setup = s5p_mfc_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_init = s5p_mfc_buf_init,
.start_streaming = s5p_mfc_start_streaming,
.stop_streaming = s5p_mfc_stop_streaming,
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c
index 81cbb36fb382..6c603dcd5664 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_enc.c
@@ -2652,8 +2652,6 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb)
static const struct vb2_ops s5p_mfc_enc_qops = {
.queue_setup = s5p_mfc_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_init = s5p_mfc_buf_init,
.buf_prepare = s5p_mfc_buf_prepare,
.start_streaming = s5p_mfc_start_streaming,
diff --git a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
index 73f7af674c01..98f8292b3173 100644
--- a/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
+++ b/drivers/media/platform/samsung/s5p-mfc/s5p_mfc_opr_v6.c
@@ -549,8 +549,9 @@ static void s5p_mfc_enc_calc_src_size_v6(struct s5p_mfc_ctx *ctx)
case V4L2_PIX_FMT_NV21M:
ctx->stride[0] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6);
ctx->stride[1] = ALIGN(ctx->img_width, S5P_FIMV_NV12M_HALIGN_V6);
- ctx->luma_size = ctx->stride[0] * ALIGN(ctx->img_height, 16);
- ctx->chroma_size = ctx->stride[0] * ALIGN(ctx->img_height / 2, 16);
+ ctx->luma_size = ALIGN(ctx->stride[0] * ALIGN(ctx->img_height, 16), 256);
+ ctx->chroma_size = ALIGN(ctx->stride[0] * ALIGN(ctx->img_height / 2, 16),
+ 256);
break;
case V4L2_PIX_FMT_YUV420M:
case V4L2_PIX_FMT_YVU420M:
@@ -2228,6 +2229,11 @@ static void s5p_mfc_try_run_v6(struct s5p_mfc_dev *dev)
case MFCINST_HEAD_PRODUCED:
ret = s5p_mfc_run_init_enc_buffers(ctx);
break;
+ case MFCINST_NAL_ABORT:
+ mfc_write(dev, ctx->inst_no, S5P_FIMV_INSTANCE_ID_V6);
+ s5p_mfc_hw_call(dev->mfc_cmds, cmd_host2risc,
+ dev, S5P_FIMV_H2R_CMD_NAL_ABORT_V6, NULL);
+ break;
default:
ret = -EAGAIN;
}
diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
index a27f638df11c..f9348aeacc11 100644
--- a/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-debug.c
@@ -455,11 +455,11 @@ static int last_request_show(struct seq_file *s, void *data)
seq_printf(s, "Format: %s\t\t\t%s\n",
bdisp_fmt_to_str(src), bdisp_fmt_to_str(dst));
- seq_printf(s, "Crop area: %dx%d @ %d,%d ==>\t%dx%d @ %d,%d\n",
- src.crop.width, src.crop.height,
+ seq_printf(s, "Crop area: (%d,%d)/%ux%u ==>\t(%d,%d)/%ux%u\n",
src.crop.left, src.crop.top,
- dst.crop.width, dst.crop.height,
- dst.crop.left, dst.crop.top);
+ src.crop.width, src.crop.height,
+ dst.crop.left, dst.crop.top,
+ dst.crop.width, dst.crop.height);
seq_printf(s, "Buff size: %dx%d\t\t%dx%d\n\n",
src.width, src.height, dst.width, dst.height);
diff --git a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
index 1328b4eb6b9f..1eb934490c0b 100644
--- a/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
+++ b/drivers/media/platform/st/sti/bdisp/bdisp-v4l2.c
@@ -531,8 +531,6 @@ static const struct vb2_ops bdisp_qops = {
.queue_setup = bdisp_queue_setup,
.buf_prepare = bdisp_buf_prepare,
.buf_queue = bdisp_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = bdisp_stop_streaming,
.start_streaming = bdisp_start_streaming,
};
@@ -955,8 +953,8 @@ static int bdisp_s_selection(struct file *file, void *fh,
if ((out.left < 0) || (out.left >= frame->width) ||
(out.top < 0) || (out.top >= frame->height)) {
dev_err(ctx->bdisp_dev->dev,
- "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n",
- out.width, out.height, out.left, out.top,
+ "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n",
+ out.left, out.top, out.width, out.height,
frame->width, frame->height);
return -EINVAL;
}
@@ -968,8 +966,8 @@ static int bdisp_s_selection(struct file *file, void *fh,
if (((out.left + out.width) > frame->width) ||
((out.top + out.height) > frame->height)) {
dev_err(ctx->bdisp_dev->dev,
- "Invalid crop: %dx%d@(%d,%d) vs frame: %dx%d\n",
- out.width, out.height, out.left, out.top,
+ "Invalid crop: (%d,%d)/%ux%u vs frame: %dx%d\n",
+ out.left, out.top, out.width, out.height,
frame->width, frame->height);
return -EINVAL;
}
@@ -984,9 +982,9 @@ static int bdisp_s_selection(struct file *file, void *fh,
if ((out.left != in->left) || (out.top != in->top) ||
(out.width != in->width) || (out.height != in->height)) {
dev_dbg(ctx->bdisp_dev->dev,
- "%s crop updated: %dx%d@(%d,%d) -> %dx%d@(%d,%d)\n",
- __func__, in->width, in->height, in->left, in->top,
- out.width, out.height, out.left, out.top);
+ "%s crop updated: (%d,%d)/%ux%u -> (%d,%d)/%ux%u\n",
+ __func__, in->left, in->top, in->width, in->height,
+ out.left, out.top, out.width, out.height);
*in = out;
}
@@ -1160,7 +1158,7 @@ static void bdisp_irq_timeout(struct work_struct *ptr)
static int bdisp_m2m_suspend(struct bdisp_dev *bdisp)
{
unsigned long flags;
- int timeout;
+ long time_left;
spin_lock_irqsave(&bdisp->slock, flags);
if (!test_bit(ST_M2M_RUNNING, &bdisp->state)) {
@@ -1171,13 +1169,13 @@ static int bdisp_m2m_suspend(struct bdisp_dev *bdisp)
set_bit(ST_M2M_SUSPENDING, &bdisp->state);
spin_unlock_irqrestore(&bdisp->slock, flags);
- timeout = wait_event_timeout(bdisp->irq_queue,
- test_bit(ST_M2M_SUSPENDED, &bdisp->state),
- BDISP_WORK_TIMEOUT);
+ time_left = wait_event_timeout(bdisp->irq_queue,
+ test_bit(ST_M2M_SUSPENDED, &bdisp->state),
+ BDISP_WORK_TIMEOUT);
clear_bit(ST_M2M_SUSPENDING, &bdisp->state);
- if (!timeout) {
+ if (!time_left) {
dev_err(bdisp->dev, "%s IRQ timeout\n", __func__);
return -EAGAIN;
}
@@ -1411,7 +1409,7 @@ MODULE_DEVICE_TABLE(of, bdisp_match_types);
static struct platform_driver bdisp_driver = {
.probe = bdisp_probe,
- .remove_new = bdisp_remove,
+ .remove = bdisp_remove,
.driver = {
.name = BDISP_NAME,
.of_match_table = bdisp_match_types,
diff --git a/drivers/media/platform/st/sti/c8sectpfe/Kconfig b/drivers/media/platform/st/sti/c8sectpfe/Kconfig
index 702b910509c9..01c33d9c9ec3 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/Kconfig
+++ b/drivers/media/platform/st/sti/c8sectpfe/Kconfig
@@ -5,7 +5,6 @@ config DVB_C8SECTPFE
depends on PINCTRL && DVB_CORE && I2C
depends on ARCH_STI || ARCH_MULTIPLATFORM || COMPILE_TEST
select FW_LOADER
- select DEBUG_FS
select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT
select DVB_STV090x if MEDIA_SUBDRV_AUTOSELECT
select DVB_STB6100 if MEDIA_SUBDRV_AUTOSELECT
diff --git a/drivers/media/platform/st/sti/c8sectpfe/Makefile b/drivers/media/platform/st/sti/c8sectpfe/Makefile
index aedfc725cc19..99425137ee0a 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/Makefile
+++ b/drivers/media/platform/st/sti/c8sectpfe/Makefile
@@ -1,6 +1,9 @@
# SPDX-License-Identifier: GPL-2.0
-c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o \
- c8sectpfe-debugfs.o
+c8sectpfe-y += c8sectpfe-core.o c8sectpfe-common.o c8sectpfe-dvb.o
+
+ifneq ($(CONFIG_DEBUG_FS),)
+c8sectpfe-y += c8sectpfe-debugfs.o
+endif
obj-$(CONFIG_DVB_C8SECTPFE) += c8sectpfe.o
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
index e4cf27b5a072..87a817dda4a9 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
@@ -24,7 +24,6 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinctrl.h>
@@ -73,16 +72,16 @@ static void c8sectpfe_timer_interrupt(struct timer_list *t)
/* is this descriptor initialised and TP enabled */
if (channel->irec && readl(channel->irec + DMA_PRDS_TPENABLE))
- tasklet_schedule(&channel->tsklet);
+ queue_work(system_bh_wq, &channel->bh_work);
}
fei->timer.expires = jiffies + msecs_to_jiffies(POLL_MSECS);
add_timer(&fei->timer);
}
-static void channel_swdemux_tsklet(struct tasklet_struct *t)
+static void channel_swdemux_bh_work(struct work_struct *t)
{
- struct channel_info *channel = from_tasklet(channel, t, tsklet);
+ struct channel_info *channel = from_work(channel, t, bh_work);
struct c8sectpfei *fei;
unsigned long wp, rp;
int pos, num_packets, n, size;
@@ -211,7 +210,7 @@ static int c8sectpfe_start_feed(struct dvb_demux_feed *dvbdmxfeed)
dev_dbg(fei->dev, "Starting channel=%p\n", channel);
- tasklet_setup(&channel->tsklet, channel_swdemux_tsklet);
+ INIT_WORK(&channel->bh_work, channel_swdemux_bh_work);
/* Reset the internal inputblock sram pointers */
writel(channel->fifo,
@@ -304,7 +303,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
/* disable this channels descriptor */
writel(0, channel->irec + DMA_PRDS_TPENABLE);
- tasklet_disable(&channel->tsklet);
+ disable_work_sync(&channel->bh_work);
/* now request memdma channel goes idle */
idlereq = (1 << channel->tsin_id) | IDLEREQ;
@@ -352,7 +351,7 @@ static int c8sectpfe_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
dev_dbg(fei->dev, "%s:%d global_feed_count=%d\n"
, __func__, __LINE__, fei->global_feed_count);
- del_timer(&fei->timer);
+ timer_delete(&fei->timer);
}
mutex_unlock(&fei->lock);
@@ -631,8 +630,8 @@ static int configure_memdma_and_inputblock(struct c8sectpfei *fei,
writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSWP_TP(0));
writel(tsin->back_buffer_busaddr, tsin->irec + DMA_PRDS_BUSRP_TP(0));
- /* initialize tasklet */
- tasklet_setup(&tsin->tsklet, channel_swdemux_tsklet);
+ /* initialize bh work */
+ INIT_WORK(&tsin->bh_work, channel_swdemux_bh_work);
return 0;
@@ -798,13 +797,12 @@ static int c8sectpfe_probe(struct platform_device *pdev)
}
tsin->i2c_adapter =
of_find_i2c_adapter_by_node(i2c_bus);
+ of_node_put(i2c_bus);
if (!tsin->i2c_adapter) {
dev_err(&pdev->dev, "No i2c adapter found\n");
- of_node_put(i2c_bus);
ret = -ENODEV;
goto err_node_put;
}
- of_node_put(i2c_bus);
/* Acquire reset GPIO and activate it */
tsin->rst_gpio = devm_fwnode_gpiod_get(dev,
@@ -1097,7 +1095,6 @@ static int load_slim_core_fw(const struct firmware *fw, struct c8sectpfei *fei)
}
}
- release_firmware(fw);
return err;
}
@@ -1121,6 +1118,7 @@ static int load_c8sectpfe_fw(struct c8sectpfei *fei)
}
err = load_slim_core_fw(fw, fei);
+ release_firmware(fw);
if (err) {
dev_err(fei->dev, "load_slim_core_fw failed err=(%d)\n", err);
return err;
@@ -1159,7 +1157,7 @@ static struct platform_driver c8sectpfe_driver = {
.of_match_table = c8sectpfe_match,
},
.probe = c8sectpfe_probe,
- .remove_new = c8sectpfe_remove,
+ .remove = c8sectpfe_remove,
};
module_platform_driver(c8sectpfe_driver);
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h
index bf377cc82225..c1b124c6ef12 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.h
@@ -51,7 +51,7 @@ struct channel_info {
unsigned long fifo;
struct completion idle_completion;
- struct tasklet_struct tsklet;
+ struct work_struct bh_work;
struct c8sectpfei *fei;
void __iomem *irec;
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
index d2c35fb32d7e..3fe177b59b16 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-debugfs.h
@@ -12,7 +12,12 @@
#include "c8sectpfe-core.h"
+#if defined(CONFIG_DEBUG_FS)
void c8sectpfe_debugfs_init(struct c8sectpfei *);
void c8sectpfe_debugfs_exit(struct c8sectpfei *);
+#else
+static inline void c8sectpfe_debugfs_init(struct c8sectpfei *fei) {};
+static inline void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) {};
+#endif
#endif /* __C8SECTPFE_DEBUG_H */
diff --git a/drivers/media/platform/st/sti/delta/delta-debug.c b/drivers/media/platform/st/sti/delta/delta-debug.c
index 4b2eb6b63aa2..6acf46913cda 100644
--- a/drivers/media/platform/st/sti/delta/delta-debug.c
+++ b/drivers/media/platform/st/sti/delta/delta-debug.c
@@ -16,14 +16,14 @@ char *delta_streaminfo_str(struct delta_streaminfo *s, char *str,
return NULL;
snprintf(str, len,
- "%4.4s %dx%d %s %s dpb=%d %s %s %s%dx%d@(%d,%d) %s%d/%d",
+ "%4.4s %dx%d %s %s dpb=%d %s %s %s(%d,%d)/%ux%u %s%d/%d",
(char *)&s->streamformat, s->width, s->height,
s->profile, s->level, s->dpb,
(s->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced",
s->other,
s->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "",
- s->crop.width, s->crop.height,
s->crop.left, s->crop.top,
+ s->crop.width, s->crop.height,
s->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "",
s->pixelaspect.numerator,
s->pixelaspect.denominator);
@@ -38,13 +38,13 @@ char *delta_frameinfo_str(struct delta_frameinfo *f, char *str,
return NULL;
snprintf(str, len,
- "%4.4s %dx%d aligned %dx%d %s %s%dx%d@(%d,%d) %s%d/%d",
+ "%4.4s %dx%d aligned %dx%d %s %s(%d,%d)/%ux%u %s%d/%d",
(char *)&f->pixelformat, f->width, f->height,
f->aligned_width, f->aligned_height,
(f->field == V4L2_FIELD_NONE) ? "progressive" : "interlaced",
f->flags & DELTA_STREAMINFO_FLAG_CROP ? "crop=" : "",
- f->crop.width, f->crop.height,
f->crop.left, f->crop.top,
+ f->crop.width, f->crop.height,
f->flags & DELTA_STREAMINFO_FLAG_PIXELASPECT ? "par=" : "",
f->pixelaspect.numerator,
f->pixelaspect.denominator);
diff --git a/drivers/media/platform/st/sti/delta/delta-v4l2.c b/drivers/media/platform/st/sti/delta/delta-v4l2.c
index da402d1e9171..196e6a40335d 100644
--- a/drivers/media/platform/st/sti/delta/delta-v4l2.c
+++ b/drivers/media/platform/st/sti/delta/delta-v4l2.c
@@ -1559,8 +1559,6 @@ static const struct vb2_ops delta_vb2_au_ops = {
.queue_setup = delta_vb2_au_queue_setup,
.buf_prepare = delta_vb2_au_prepare,
.buf_queue = delta_vb2_au_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = delta_vb2_au_start_streaming,
.stop_streaming = delta_vb2_au_stop_streaming,
};
@@ -1570,8 +1568,6 @@ static const struct vb2_ops delta_vb2_frame_ops = {
.buf_prepare = delta_vb2_frame_prepare,
.buf_finish = delta_vb2_frame_finish,
.buf_queue = delta_vb2_frame_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.stop_streaming = delta_vb2_frame_stop_streaming,
};
@@ -1954,7 +1950,7 @@ MODULE_DEVICE_TABLE(of, delta_match_types);
static struct platform_driver delta_driver = {
.probe = delta_probe,
- .remove_new = delta_remove,
+ .remove = delta_remove,
.driver = {
.name = DELTA_NAME,
.of_match_table = delta_match_types,
diff --git a/drivers/media/platform/st/sti/hva/hva-hw.c b/drivers/media/platform/st/sti/hva/hva-hw.c
index fe4ea2e7f37e..fcb18fb52fdd 100644
--- a/drivers/media/platform/st/sti/hva/hva-hw.c
+++ b/drivers/media/platform/st/sti/hva/hva-hw.c
@@ -406,8 +406,7 @@ err_pm:
err_disable:
pm_runtime_disable(dev);
err_clk:
- if (hva->clk)
- clk_unprepare(hva->clk);
+ clk_unprepare(hva->clk);
return ret;
}
diff --git a/drivers/media/platform/st/sti/hva/hva-v4l2.c b/drivers/media/platform/st/sti/hva/hva-v4l2.c
index 161a5c0fbc4e..5366c0f92549 100644
--- a/drivers/media/platform/st/sti/hva/hva-v4l2.c
+++ b/drivers/media/platform/st/sti/hva/hva-v4l2.c
@@ -1114,8 +1114,6 @@ static const struct vb2_ops hva_qops = {
.buf_queue = hva_buf_queue,
.start_streaming = hva_start_streaming,
.stop_streaming = hva_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/*
@@ -1456,7 +1454,7 @@ MODULE_DEVICE_TABLE(of, hva_match_types);
static struct platform_driver hva_driver = {
.probe = hva_probe,
- .remove_new = hva_remove,
+ .remove = hva_remove,
.driver = {
.name = HVA_NAME,
.of_match_table = hva_match_types,
diff --git a/drivers/media/platform/st/stm32/Kconfig b/drivers/media/platform/st/stm32/Kconfig
index 9df9a2a17728..f12e67bcc9bc 100644
--- a/drivers/media/platform/st/stm32/Kconfig
+++ b/drivers/media/platform/st/stm32/Kconfig
@@ -1,6 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-only
# V4L drivers
+config VIDEO_STM32_CSI
+ tristate "STM32 Camera Serial Interface (CSI) support"
+ depends on V4L_PLATFORM_DRIVERS
+ depends on VIDEO_DEV && OF
+ depends on ARCH_STM32 || COMPILE_TEST
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ help
+ This module makes the STM32 Camera Serial Interface (CSI)
+ available as a v4l2 device.
+
+ To compile this driver as a module, choose M here: the module
+ will be called stm32-csi.
+
config VIDEO_STM32_DCMI
tristate "STM32 Digital Camera Memory Interface (DCMI) support"
depends on V4L_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/st/stm32/Makefile b/drivers/media/platform/st/stm32/Makefile
index 7ed8297b9b19..9ae57897f030 100644
--- a/drivers/media/platform/st/stm32/Makefile
+++ b/drivers/media/platform/st/stm32/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_VIDEO_STM32_CSI) += stm32-csi.o
obj-$(CONFIG_VIDEO_STM32_DCMI) += stm32-dcmi.o
obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp/
stm32-dma2d-objs := dma2d/dma2d.o dma2d/dma2d-hw.o
diff --git a/drivers/media/platform/st/stm32/dma2d/dma2d.c b/drivers/media/platform/st/stm32/dma2d/dma2d.c
index 92f1edee58f8..48fa781aab06 100644
--- a/drivers/media/platform/st/stm32/dma2d/dma2d.c
+++ b/drivers/media/platform/st/stm32/dma2d/dma2d.c
@@ -186,8 +186,6 @@ static const struct vb2_ops dma2d_qops = {
.buf_queue = dma2d_buf_queue,
.start_streaming = dma2d_start_streaming,
.stop_streaming = dma2d_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int queue_init(void *priv, struct vb2_queue *src_vq,
@@ -492,7 +490,8 @@ static void device_run(void *prv)
dst->sequence = frm_cap->sequence++;
v4l2_m2m_buf_copy_metadata(src, dst, true);
- clk_enable(dev->gate);
+ if (clk_enable(dev->gate))
+ goto end;
dma2d_config_fg(dev, frm_out,
vb2_dma_contig_plane_dma_addr(&src->vb2_buf, 0));
@@ -717,7 +716,7 @@ MODULE_DEVICE_TABLE(of, stm32_dma2d_match);
static struct platform_driver dma2d_pdrv = {
.probe = dma2d_probe,
- .remove_new = dma2d_remove,
+ .remove = dma2d_remove,
.driver = {
.name = DMA2D_NAME,
.of_match_table = stm32_dma2d_match,
diff --git a/drivers/media/platform/st/stm32/stm32-csi.c b/drivers/media/platform/st/stm32/stm32-csi.c
new file mode 100644
index 000000000000..b69048144cc1
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-csi.c
@@ -0,0 +1,1141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Camera Serial Interface
+ *
+ * Copyright (C) STMicroelectronics SA 2024
+ * Author: Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#define STM32_CSI_CR 0x0000
+#define STM32_CSI_CR_CSIEN BIT(0)
+#define STM32_CSI_CR_VCXSTART(x) BIT(2 + ((x) * 4))
+#define STM32_CSI_CR_VCXSTOP(x) BIT(3 + ((x) * 4))
+#define STM32_CSI_PCR 0x0004
+#define STM32_CSI_PCR_DL1EN BIT(3)
+#define STM32_CSI_PCR_DL0EN BIT(2)
+#define STM32_CSI_PCR_CLEN BIT(1)
+#define STM32_CSI_PCR_PWRDOWN BIT(0)
+#define STM32_CSI_VCXCFGR1(x) ((((x) + 1) * 0x0010) + 0x0)
+#define STM32_CSI_VCXCFGR1_ALLDT BIT(0)
+#define STM32_CSI_VCXCFGR1_DT0EN BIT(1)
+#define STM32_CSI_VCXCFGR1_DT1EN BIT(2)
+#define STM32_CSI_VCXCFGR1_CDTFT_SHIFT 8
+#define STM32_CSI_VCXCFGR1_DT0_SHIFT 16
+#define STM32_CSI_VCXCFGR1_DT0FT_SHIFT 24
+#define STM32_CSI_VCXCFGR2(x) ((((x) + 1) * 0x0010) + 0x4)
+#define STM32_CSI_VCXCFGR2_DT1_SHIFT 0
+#define STM32_CSI_VCXCFGR2_DT1FT_SHIFT 8
+#define STM32_CSI_INPUT_BPP8 2
+#define STM32_CSI_INPUT_BPP10 3
+#define STM32_CSI_INPUT_BPP12 4
+#define STM32_CSI_INPUT_BPP14 5
+#define STM32_CSI_LMCFGR 0x0070
+#define STM32_CSI_LMCFGR_LANENB_SHIFT 8
+#define STM32_CSI_LMCFGR_DLMAP_SHIFT 16
+#define STM32_CSI_IER0 0x0080
+#define STM32_CSI_IER1 0x0084
+#define STM32_CSI_SR0 0x0090
+#define STM32_CSI_SR0_SYNCERRF BIT(30)
+#define STM32_CSI_SR0_SPKTERRF BIT(28)
+#define STM32_CSI_SR0_IDERRF BIT(27)
+#define STM32_CSI_SR0_CECCERRF BIT(26)
+#define STM32_CSI_SR0_ECCERRF BIT(25)
+#define STM32_CSI_SR0_CRCERRF BIT(24)
+#define STM32_CSI_SR0_CCFIFOFF BIT(21)
+#define STM32_CSI_SR0_VCXSTATEF(x) BIT(17 + (x))
+#define STM32_CSI_SR1 0x0094
+#define STM32_CSI_SR1_ECTRLDL1F BIT(12)
+#define STM32_CSI_SR1_ESYNCESCDL1F BIT(11)
+#define STM32_CSI_SR1_EESCDL1F BIT(10)
+#define STM32_CSI_SR1_ESOTSYNCDL1F BIT(9)
+#define STM32_CSI_SR1_ESOTDL1F BIT(8)
+#define STM32_CSI_SR1_ECTRLDL0F BIT(4)
+#define STM32_CSI_SR1_ESYNCESCDL0F BIT(3)
+#define STM32_CSI_SR1_EESCDL0F BIT(2)
+#define STM32_CSI_SR1_ESOTSYNCDL0F BIT(1)
+#define STM32_CSI_SR1_ESOTDL0F BIT(0)
+#define STM32_CSI_FCR0 0x0100
+#define STM32_CSI_FCR1 0x0104
+#define STM32_CSI_SPDFR 0x0110
+#define STM32_CSI_DT_MASK 0x3f
+#define STM32_CSI_VC_MASK 0x03
+#define STM32_CSI_ERR1 0x0114
+#define STM32_CSI_ERR1_IDVCERR_SHIFT 22
+#define STM32_CSI_ERR1_IDDTERR_SHIFT 16
+#define STM32_CSI_ERR1_CECCVCERR_SHIFT 14
+#define STM32_CSI_ERR1_CECCDTERR_SHIFT 8
+#define STM32_CSI_ERR1_CRCVCERR_SHIFT 6
+#define STM32_CSI_ERR1_CRCDTERR_SHIFT 0
+#define STM32_CSI_ERR2 0x0118
+#define STM32_CSI_ERR2_SYNCVCERR_SHIFT 18
+#define STM32_CSI_ERR2_SPKTVCERR_SHIFT 6
+#define STM32_CSI_ERR2_SPKTDTERR_SHIFT 0
+#define STM32_CSI_PRCR 0x1000
+#define STM32_CSI_PRCR_PEN BIT(1)
+#define STM32_CSI_PMCR 0x1004
+#define STM32_CSI_PFCR 0x1008
+#define STM32_CSI_PFCR_CCFR_MASK GENMASK(5, 0)
+#define STM32_CSI_PFCR_CCFR_SHIFT 0
+#define STM32_CSI_PFCR_HSFR_MASK GENMASK(14, 8)
+#define STM32_CSI_PFCR_HSFR_SHIFT 8
+#define STM32_CSI_PFCR_DLD BIT(16)
+#define STM32_CSI_PTCR0 0x1010
+#define STM32_CSI_PTCR0_TCKEN BIT(0)
+#define STM32_CSI_PTCR1 0x1014
+#define STM32_CSI_PTCR1_TWM BIT(16)
+#define STM32_CSI_PTCR1_TDI_MASK GENMASK(7, 0)
+#define STM32_CSI_PTCR1_TDI_SHIFT 0
+#define STM32_CSI_PTSR 0x1018
+
+#define STM32_CSI_LANES_MAX 2
+
+#define STM32_CSI_SR0_ERRORS (STM32_CSI_SR0_SYNCERRF | STM32_CSI_SR0_SPKTERRF |\
+ STM32_CSI_SR0_IDERRF | STM32_CSI_SR0_CECCERRF |\
+ STM32_CSI_SR0_ECCERRF | STM32_CSI_SR0_CRCERRF |\
+ STM32_CSI_SR0_CCFIFOFF)
+#define STM32_CSI_SR1_DL0_ERRORS (STM32_CSI_SR1_ECTRLDL0F | STM32_CSI_SR1_ESYNCESCDL0F |\
+ STM32_CSI_SR1_EESCDL0F | STM32_CSI_SR1_ESOTSYNCDL0F |\
+ STM32_CSI_SR1_ESOTDL0F)
+#define STM32_CSI_SR1_DL1_ERRORS (STM32_CSI_SR1_ECTRLDL1F | STM32_CSI_SR1_ESYNCESCDL1F |\
+ STM32_CSI_SR1_EESCDL1F | STM32_CSI_SR1_ESOTSYNCDL1F |\
+ STM32_CSI_SR1_ESOTDL1F)
+#define STM32_CSI_SR1_ERRORS (STM32_CSI_SR1_DL0_ERRORS | STM32_CSI_SR1_DL1_ERRORS)
+
+enum stm32_csi_pads {
+ STM32_CSI_PAD_SINK,
+ STM32_CSI_PAD_SOURCE,
+ STM32_CSI_PAD_MAX,
+};
+
+struct stm32_csi_event {
+ u32 mask;
+ const char * const name;
+};
+
+static const struct stm32_csi_event stm32_csi_events_sr0[] = {
+ {STM32_CSI_SR0_SYNCERRF, "Synchronization error"},
+ {STM32_CSI_SR0_SPKTERRF, "Short packet error"},
+ {STM32_CSI_SR0_IDERRF, "Data type ID error"},
+ {STM32_CSI_SR0_CECCERRF, "Corrected ECC error"},
+ {STM32_CSI_SR0_ECCERRF, "ECC error"},
+ {STM32_CSI_SR0_CRCERRF, "CRC error"},
+ {STM32_CSI_SR0_CCFIFOFF, "Clk changer FIFO full error"},
+};
+
+#define STM32_CSI_NUM_SR0_EVENTS ARRAY_SIZE(stm32_csi_events_sr0)
+
+static const struct stm32_csi_event stm32_csi_events_sr1[] = {
+ {STM32_CSI_SR1_ECTRLDL1F, "L1: D-PHY control error"},
+ {STM32_CSI_SR1_ESYNCESCDL1F,
+ "L1: D-PHY low power data transmission synchro error"},
+ {STM32_CSI_SR1_EESCDL1F, "L1: D-PHY escape entry error"},
+ {STM32_CSI_SR1_ESOTSYNCDL1F,
+ "L1: Start of transmission synchro error"},
+ {STM32_CSI_SR1_ESOTDL1F, "L1: Start of transmission error"},
+ {STM32_CSI_SR1_ECTRLDL0F, "L0: D-PHY control error"},
+ {STM32_CSI_SR1_ESYNCESCDL0F,
+ "L0: D-PHY low power data transmission synchro error"},
+ {STM32_CSI_SR1_EESCDL0F, "L0: D-PHY escape entry error"},
+ {STM32_CSI_SR1_ESOTSYNCDL0F,
+ "L0: Start of transmission synchro error"},
+ {STM32_CSI_SR1_ESOTDL0F, "L0: Start of transmission error"},
+};
+
+#define STM32_CSI_NUM_SR1_EVENTS ARRAY_SIZE(stm32_csi_events_sr1)
+
+enum stm32_csi_clk {
+ STM32_CSI_CLK_PCLK,
+ STM32_CSI_CLK_TXESC,
+ STM32_CSI_CLK_CSI2PHY,
+ STM32_CSI_CLK_NB,
+};
+
+static const char * const stm32_csi_clks_id[] = {
+ "pclk",
+ "txesc",
+ "csi2phy",
+};
+
+struct stm32_csi_dev {
+ struct device *dev;
+
+ void __iomem *base;
+
+ struct clk_bulk_data clks[STM32_CSI_CLK_NB];
+ struct regulator_bulk_data supplies[2];
+
+ u8 lanes[STM32_CSI_LANES_MAX];
+ u8 num_lanes;
+
+ /*
+ * spinlock slock is used to protect to srX_counters tables being
+ * accessed from log_status and interrupt context
+ */
+ spinlock_t slock;
+
+ u32 sr0_counters[STM32_CSI_NUM_SR0_EVENTS];
+ u32 sr1_counters[STM32_CSI_NUM_SR1_EVENTS];
+
+ struct v4l2_subdev sd;
+ struct v4l2_async_notifier notifier;
+ struct media_pad pads[STM32_CSI_PAD_MAX];
+
+ /* Remote source */
+ struct v4l2_subdev *s_subdev;
+ u32 s_subdev_pad_nb;
+};
+
+struct stm32_csi_fmts {
+ u32 code;
+ u32 datatype;
+ u32 input_fmt;
+ u8 bpp;
+};
+
+#define FMT_MBUS_DT_DTFMT_BPP(mbus, dt, input, byteperpixel) \
+ { \
+ .code = MEDIA_BUS_FMT_##mbus, \
+ .datatype = MIPI_CSI2_DT_##dt, \
+ .input_fmt = STM32_CSI_INPUT_##input, \
+ .bpp = byteperpixel, \
+ }
+static const struct stm32_csi_fmts stm32_csi_formats[] = {
+ /* YUV 422 8 bit */
+ FMT_MBUS_DT_DTFMT_BPP(UYVY8_1X16, YUV422_8B, BPP8, 8),
+ FMT_MBUS_DT_DTFMT_BPP(YUYV8_1X16, YUV422_8B, BPP8, 8),
+ FMT_MBUS_DT_DTFMT_BPP(YVYU8_1X16, YUV422_8B, BPP8, 8),
+ FMT_MBUS_DT_DTFMT_BPP(VYUY8_1X16, YUV422_8B, BPP8, 8),
+
+ /* Raw Bayer */
+ /* 8 bit */
+ FMT_MBUS_DT_DTFMT_BPP(SBGGR8_1X8, RAW8, BPP8, 8),
+ FMT_MBUS_DT_DTFMT_BPP(SGBRG8_1X8, RAW8, BPP8, 8),
+ FMT_MBUS_DT_DTFMT_BPP(SGRBG8_1X8, RAW8, BPP8, 8),
+ FMT_MBUS_DT_DTFMT_BPP(SRGGB8_1X8, RAW8, BPP8, 8),
+ /* 10 bit */
+ FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10),
+ FMT_MBUS_DT_DTFMT_BPP(SGBRG10_1X10, RAW10, BPP10, 10),
+ FMT_MBUS_DT_DTFMT_BPP(SGRBG10_1X10, RAW10, BPP10, 10),
+ FMT_MBUS_DT_DTFMT_BPP(SRGGB10_1X10, RAW10, BPP10, 10),
+ /* 12 bit */
+ FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12),
+ FMT_MBUS_DT_DTFMT_BPP(SGBRG12_1X12, RAW12, BPP12, 12),
+ FMT_MBUS_DT_DTFMT_BPP(SGRBG12_1X12, RAW12, BPP12, 12),
+ FMT_MBUS_DT_DTFMT_BPP(SRGGB12_1X12, RAW12, BPP12, 12),
+ /* 14 bit */
+ FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14),
+ FMT_MBUS_DT_DTFMT_BPP(SGBRG14_1X14, RAW14, BPP14, 14),
+ FMT_MBUS_DT_DTFMT_BPP(SGRBG14_1X14, RAW14, BPP14, 14),
+ FMT_MBUS_DT_DTFMT_BPP(SRGGB14_1X14, RAW14, BPP14, 14),
+
+ /* RGB 565 */
+ FMT_MBUS_DT_DTFMT_BPP(RGB565_1X16, RGB565, BPP8, 8),
+
+ /* JPEG (datatype isn't used) */
+ FMT_MBUS_DT_DTFMT_BPP(JPEG_1X8, NULL, BPP8, 8),
+};
+
+struct stm32_csi_mbps_phy_reg {
+ unsigned int mbps;
+ unsigned int hsfreqrange;
+ unsigned int osc_freq_target;
+};
+
+/*
+ * Table describing configuration of the PHY depending on the
+ * intended Bit Rate. From table 5-8 Frequency Ranges and Defaults
+ * of the Synopsis DWC MIPI PHY databook
+ */
+static const struct stm32_csi_mbps_phy_reg snps_stm32mp25[] = {
+ { .mbps = 80, .hsfreqrange = 0x00, .osc_freq_target = 460 },
+ { .mbps = 90, .hsfreqrange = 0x10, .osc_freq_target = 460 },
+ { .mbps = 100, .hsfreqrange = 0x20, .osc_freq_target = 460 },
+ { .mbps = 110, .hsfreqrange = 0x30, .osc_freq_target = 460 },
+ { .mbps = 120, .hsfreqrange = 0x01, .osc_freq_target = 460 },
+ { .mbps = 130, .hsfreqrange = 0x11, .osc_freq_target = 460 },
+ { .mbps = 140, .hsfreqrange = 0x21, .osc_freq_target = 460 },
+ { .mbps = 150, .hsfreqrange = 0x31, .osc_freq_target = 460 },
+ { .mbps = 160, .hsfreqrange = 0x02, .osc_freq_target = 460 },
+ { .mbps = 170, .hsfreqrange = 0x12, .osc_freq_target = 460 },
+ { .mbps = 180, .hsfreqrange = 0x22, .osc_freq_target = 460 },
+ { .mbps = 190, .hsfreqrange = 0x32, .osc_freq_target = 460 },
+ { .mbps = 205, .hsfreqrange = 0x03, .osc_freq_target = 460 },
+ { .mbps = 220, .hsfreqrange = 0x13, .osc_freq_target = 460 },
+ { .mbps = 235, .hsfreqrange = 0x23, .osc_freq_target = 460 },
+ { .mbps = 250, .hsfreqrange = 0x33, .osc_freq_target = 460 },
+ { .mbps = 275, .hsfreqrange = 0x04, .osc_freq_target = 460 },
+ { .mbps = 300, .hsfreqrange = 0x14, .osc_freq_target = 460 },
+ { .mbps = 325, .hsfreqrange = 0x25, .osc_freq_target = 460 },
+ { .mbps = 350, .hsfreqrange = 0x35, .osc_freq_target = 460 },
+ { .mbps = 400, .hsfreqrange = 0x05, .osc_freq_target = 460 },
+ { .mbps = 450, .hsfreqrange = 0x16, .osc_freq_target = 460 },
+ { .mbps = 500, .hsfreqrange = 0x26, .osc_freq_target = 460 },
+ { .mbps = 550, .hsfreqrange = 0x37, .osc_freq_target = 460 },
+ { .mbps = 600, .hsfreqrange = 0x07, .osc_freq_target = 460 },
+ { .mbps = 650, .hsfreqrange = 0x18, .osc_freq_target = 460 },
+ { .mbps = 700, .hsfreqrange = 0x28, .osc_freq_target = 460 },
+ { .mbps = 750, .hsfreqrange = 0x39, .osc_freq_target = 460 },
+ { .mbps = 800, .hsfreqrange = 0x09, .osc_freq_target = 460 },
+ { .mbps = 850, .hsfreqrange = 0x19, .osc_freq_target = 460 },
+ { .mbps = 900, .hsfreqrange = 0x29, .osc_freq_target = 460 },
+ { .mbps = 950, .hsfreqrange = 0x3a, .osc_freq_target = 460 },
+ { .mbps = 1000, .hsfreqrange = 0x0a, .osc_freq_target = 460 },
+ { .mbps = 1050, .hsfreqrange = 0x1a, .osc_freq_target = 460 },
+ { .mbps = 1100, .hsfreqrange = 0x2a, .osc_freq_target = 460 },
+ { .mbps = 1150, .hsfreqrange = 0x3b, .osc_freq_target = 460 },
+ { .mbps = 1200, .hsfreqrange = 0x0b, .osc_freq_target = 460 },
+ { .mbps = 1250, .hsfreqrange = 0x1b, .osc_freq_target = 460 },
+ { .mbps = 1300, .hsfreqrange = 0x2b, .osc_freq_target = 460 },
+ { .mbps = 1350, .hsfreqrange = 0x3c, .osc_freq_target = 460 },
+ { .mbps = 1400, .hsfreqrange = 0x0c, .osc_freq_target = 460 },
+ { .mbps = 1450, .hsfreqrange = 0x1c, .osc_freq_target = 460 },
+ { .mbps = 1500, .hsfreqrange = 0x2c, .osc_freq_target = 460 },
+ { .mbps = 1550, .hsfreqrange = 0x3d, .osc_freq_target = 285 },
+ { .mbps = 1600, .hsfreqrange = 0x0d, .osc_freq_target = 295 },
+ { .mbps = 1650, .hsfreqrange = 0x1d, .osc_freq_target = 304 },
+ { .mbps = 1700, .hsfreqrange = 0x2e, .osc_freq_target = 313 },
+ { .mbps = 1750, .hsfreqrange = 0x3e, .osc_freq_target = 322 },
+ { .mbps = 1800, .hsfreqrange = 0x0e, .osc_freq_target = 331 },
+ { .mbps = 1850, .hsfreqrange = 0x1e, .osc_freq_target = 341 },
+ { .mbps = 1900, .hsfreqrange = 0x2f, .osc_freq_target = 350 },
+ { .mbps = 1950, .hsfreqrange = 0x3f, .osc_freq_target = 359 },
+ { .mbps = 2000, .hsfreqrange = 0x0f, .osc_freq_target = 368 },
+ { .mbps = 2050, .hsfreqrange = 0x40, .osc_freq_target = 377 },
+ { .mbps = 2100, .hsfreqrange = 0x41, .osc_freq_target = 387 },
+ { .mbps = 2150, .hsfreqrange = 0x42, .osc_freq_target = 396 },
+ { .mbps = 2200, .hsfreqrange = 0x43, .osc_freq_target = 405 },
+ { .mbps = 2250, .hsfreqrange = 0x44, .osc_freq_target = 414 },
+ { .mbps = 2300, .hsfreqrange = 0x45, .osc_freq_target = 423 },
+ { .mbps = 2350, .hsfreqrange = 0x46, .osc_freq_target = 432 },
+ { .mbps = 2400, .hsfreqrange = 0x47, .osc_freq_target = 442 },
+ { .mbps = 2450, .hsfreqrange = 0x48, .osc_freq_target = 451 },
+ { .mbps = 2500, .hsfreqrange = 0x49, .osc_freq_target = 460 },
+};
+
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_RGB565_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_REC709,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+};
+
+static const struct stm32_csi_fmts *stm32_csi_code_to_fmt(unsigned int code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(stm32_csi_formats); i++)
+ if (stm32_csi_formats[i].code == code)
+ return &stm32_csi_formats[i];
+
+ return NULL;
+}
+
+static inline struct stm32_csi_dev *to_csidev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct stm32_csi_dev, sd);
+}
+
+static int stm32_csi_setup_lane_merger(struct stm32_csi_dev *csidev)
+{
+ u32 lmcfgr = 0;
+ unsigned int i;
+
+ for (i = 0; i < csidev->num_lanes; i++) {
+ if (!csidev->lanes[i] || csidev->lanes[i] > STM32_CSI_LANES_MAX) {
+ dev_err(csidev->dev, "Invalid lane id (%d)\n", csidev->lanes[i]);
+ return -EINVAL;
+ }
+ lmcfgr |= (csidev->lanes[i] << ((i * 4) + STM32_CSI_LMCFGR_DLMAP_SHIFT));
+ }
+
+ lmcfgr |= (csidev->num_lanes << STM32_CSI_LMCFGR_LANENB_SHIFT);
+
+ writel_relaxed(lmcfgr, csidev->base + STM32_CSI_LMCFGR);
+
+ return 0;
+}
+
+static void stm32_csi_phy_reg_write(struct stm32_csi_dev *csidev,
+ u32 addr, u32 val)
+{
+ /* Based on sequence described at section 5.2.3.2 of DesignWave document */
+ /* For writing the 4-bit testcode MSBs */
+ /* Set testen to high */
+ writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1);
+
+ /* Set testclk to high */
+ writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0);
+
+ /* Place 0x00 in testdin */
+ writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1);
+
+ /*
+ * Set testclk to low (with the falling edge on testclk, the testdin
+ * signal content is latched internally)
+ */
+ writel_relaxed(0, csidev->base + STM32_CSI_PTCR0);
+
+ /* Set testen to low */
+ writel_relaxed(0, csidev->base + STM32_CSI_PTCR1);
+
+ /* Place the 8-bit word corresponding to the testcode MSBs in testdin */
+ writel_relaxed(((addr >> 8) & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT,
+ csidev->base + STM32_CSI_PTCR1);
+
+ /* Set testclk to high */
+ writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0);
+
+ /* For writing the 8-bit testcode LSBs */
+ /* Set testclk to low */
+ writel_relaxed(0, csidev->base + STM32_CSI_PTCR0);
+
+ /* Set testen to high */
+ writel_relaxed(STM32_CSI_PTCR1_TWM, csidev->base + STM32_CSI_PTCR1);
+
+ /* Set testclk to high */
+ writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0);
+
+ /* Place the 8-bit word test data in testdin */
+ writel_relaxed((addr & STM32_CSI_PTCR1_TDI_MASK) <<
+ STM32_CSI_PTCR1_TDI_SHIFT | STM32_CSI_PTCR1_TWM,
+ csidev->base + STM32_CSI_PTCR1);
+
+ /*
+ * Set testclk to low (with the falling edge on testclk, the testdin
+ * signal content is latched internally)
+ */
+ writel_relaxed(0, csidev->base + STM32_CSI_PTCR0);
+
+ /* Set testen to low */
+ writel_relaxed(0, csidev->base + STM32_CSI_PTCR1);
+
+ /* For writing the data */
+ /* Place the 8-bit word corresponding to the page offset in testdin */
+ writel_relaxed((val & STM32_CSI_PTCR1_TDI_MASK) << STM32_CSI_PTCR1_TDI_SHIFT,
+ csidev->base + STM32_CSI_PTCR1);
+
+ /* Set testclk to high (test data is programmed internally */
+ writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0);
+
+ /* Finish by setting testclk to low */
+ writel_relaxed(0, csidev->base + STM32_CSI_PTCR0);
+}
+
+static int stm32_csi_start(struct stm32_csi_dev *csidev,
+ struct v4l2_subdev_state *state)
+{
+ struct media_pad *src_pad =
+ &csidev->s_subdev->entity.pads[csidev->s_subdev_pad_nb];
+ const struct stm32_csi_mbps_phy_reg *phy_regs = NULL;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ const struct stm32_csi_fmts *fmt;
+ unsigned long phy_clk_frate;
+ u32 lanes_ie, lanes_en;
+ unsigned int mbps;
+ unsigned int i;
+ s64 link_freq;
+ int ret;
+ u32 ccfr;
+
+ dev_dbg(csidev->dev, "Starting the CSI2\n");
+
+ /* Get the bpp value on pad0 (input of CSI) */
+ sink_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK);
+ fmt = stm32_csi_code_to_fmt(sink_fmt->code);
+
+ /* Get the remote sensor link frequency */
+ if (!csidev->s_subdev)
+ return -EIO;
+
+ link_freq = v4l2_get_link_freq(src_pad,
+ fmt->bpp, 2 * csidev->num_lanes);
+ if (link_freq < 0)
+ return link_freq;
+
+ /* MBPS is expressed in Mbps, hence link_freq / 100000 * 2 */
+ mbps = div_s64(link_freq, 500000);
+ dev_dbg(csidev->dev, "Computed Mbps: %u\n", mbps);
+
+ for (i = 0; i < ARRAY_SIZE(snps_stm32mp25); i++) {
+ if (snps_stm32mp25[i].mbps >= mbps) {
+ phy_regs = &snps_stm32mp25[i];
+ break;
+ }
+ }
+
+ if (!phy_regs) {
+ dev_err(csidev->dev, "Unsupported PHY speed (%u Mbps)", mbps);
+ return -ERANGE;
+ }
+
+ dev_dbg(csidev->dev, "PHY settings: (%u Mbps, %u HS FRange, %u OSC Freq)\n",
+ phy_regs->mbps, phy_regs->hsfreqrange,
+ phy_regs->osc_freq_target);
+
+ /* Prepare lanes related configuration bits */
+ lanes_ie = STM32_CSI_SR1_DL0_ERRORS;
+ lanes_en = STM32_CSI_PCR_DL0EN;
+ if (csidev->num_lanes == 2) {
+ lanes_ie |= STM32_CSI_SR1_DL1_ERRORS;
+ lanes_en |= STM32_CSI_PCR_DL1EN;
+ }
+
+ ret = pm_runtime_get_sync(csidev->dev);
+ if (ret < 0)
+ goto error_put;
+
+ /* Retrieve CSI2PHY clock rate to compute CCFR value */
+ phy_clk_frate = clk_get_rate(csidev->clks[STM32_CSI_CLK_CSI2PHY].clk);
+ if (!phy_clk_frate) {
+ dev_err(csidev->dev, "CSI2PHY clock rate invalid (0)\n");
+ ret = -EINVAL;
+ goto error_put;
+ }
+
+ ret = stm32_csi_setup_lane_merger(csidev);
+ if (ret)
+ goto error_put;
+
+ /* Enable the CSI */
+ writel_relaxed(STM32_CSI_CR_CSIEN, csidev->base + STM32_CSI_CR);
+
+ /* Enable some global CSI related interrupts - bits are same as SR0 */
+ writel_relaxed(STM32_CSI_SR0_ERRORS, csidev->base + STM32_CSI_IER0);
+
+ /* Enable lanes related error interrupts */
+ writel_relaxed(lanes_ie, csidev->base + STM32_CSI_IER1);
+
+ /* Initialization of the D-PHY */
+ /* Stop the D-PHY */
+ writel_relaxed(0, csidev->base + STM32_CSI_PRCR);
+
+ /* Keep the D-PHY in power down state */
+ writel_relaxed(0, csidev->base + STM32_CSI_PCR);
+
+ /* Enable testclr clock during 15ns */
+ writel_relaxed(STM32_CSI_PTCR0_TCKEN, csidev->base + STM32_CSI_PTCR0);
+ udelay(1);
+ writel_relaxed(0, csidev->base + STM32_CSI_PTCR0);
+
+ /* Set hsfreqrange */
+ phy_clk_frate /= 1000000;
+ ccfr = (phy_clk_frate - 17) * 4;
+ writel_relaxed((ccfr << STM32_CSI_PFCR_CCFR_SHIFT) |
+ (phy_regs->hsfreqrange << STM32_CSI_PFCR_HSFR_SHIFT),
+ csidev->base + STM32_CSI_PFCR);
+
+ /* set reg @08 deskew_polarity_rw 1'b1 */
+ stm32_csi_phy_reg_write(csidev, 0x08, 0x38);
+
+ /* set reg @0xE4 counter_for_des_en_config_if_rx 0x10 + DLL prog EN */
+ /* This is because 13<= cfgclkfreqrange[5:0]<=38 */
+ stm32_csi_phy_reg_write(csidev, 0xe4, 0x11);
+
+ /* set reg @0xe2 & reg @0xe3 value DLL target oscilation freq */
+ /* Based on the table page 77, osc_freq_target */
+ stm32_csi_phy_reg_write(csidev, 0xe2, phy_regs->osc_freq_target & 0xFF);
+ stm32_csi_phy_reg_write(csidev, 0xe3, (phy_regs->osc_freq_target >> 8) & 0x0F);
+
+ writel_relaxed(STM32_CSI_PFCR_DLD | readl_relaxed(csidev->base + STM32_CSI_PFCR),
+ csidev->base + STM32_CSI_PFCR);
+
+ /* Enable Lanes */
+ writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN, csidev->base + STM32_CSI_PCR);
+ writel_relaxed(lanes_en | STM32_CSI_PCR_CLEN | STM32_CSI_PCR_PWRDOWN,
+ csidev->base + STM32_CSI_PCR);
+
+ writel_relaxed(STM32_CSI_PRCR_PEN, csidev->base + STM32_CSI_PRCR);
+
+ /* Remove the force */
+ writel_relaxed(0, csidev->base + STM32_CSI_PMCR);
+
+ return ret;
+
+error_put:
+ pm_runtime_put(csidev->dev);
+ return ret;
+}
+
+static void stm32_csi_stop(struct stm32_csi_dev *csidev)
+{
+ dev_dbg(csidev->dev, "Stopping the CSI2\n");
+
+ /* Disable the D-PHY */
+ writel_relaxed(0, csidev->base + STM32_CSI_PCR);
+
+ /* Disable ITs */
+ writel_relaxed(0, csidev->base + STM32_CSI_IER0);
+ writel_relaxed(0, csidev->base + STM32_CSI_IER1);
+
+ /* Disable the CSI */
+ writel_relaxed(0, csidev->base + STM32_CSI_CR);
+
+ pm_runtime_put(csidev->dev);
+}
+
+static int stm32_csi_start_vc(struct stm32_csi_dev *csidev,
+ struct v4l2_subdev_state *state, u32 vc)
+{
+ struct v4l2_mbus_framefmt *mbus_fmt;
+ const struct stm32_csi_fmts *fmt;
+ u32 status;
+ u32 cfgr1;
+ int ret;
+
+ mbus_fmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE);
+ fmt = stm32_csi_code_to_fmt(mbus_fmt->code);
+
+ /* If the mbus code is JPEG, don't enable filtering */
+ if (mbus_fmt->code == MEDIA_BUS_FMT_JPEG_1X8) {
+ cfgr1 = STM32_CSI_VCXCFGR1_ALLDT;
+ cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_CDTFT_SHIFT;
+ dev_dbg(csidev->dev, "VC%d: enable AllDT mode\n", vc);
+ } else {
+ cfgr1 = fmt->datatype << STM32_CSI_VCXCFGR1_DT0_SHIFT;
+ cfgr1 |= fmt->input_fmt << STM32_CSI_VCXCFGR1_DT0FT_SHIFT;
+ cfgr1 |= STM32_CSI_VCXCFGR1_DT0EN;
+ dev_dbg(csidev->dev, "VC%d: enable DT0(0x%x)/DT0FT(0x%x)\n",
+ vc, fmt->datatype, fmt->input_fmt);
+ }
+ writel_relaxed(cfgr1, csidev->base + STM32_CSI_VCXCFGR1(vc));
+
+ /* Enable processing of the virtual-channel and wait for its status */
+ writel_relaxed(STM32_CSI_CR_VCXSTART(vc) | STM32_CSI_CR_CSIEN,
+ csidev->base + STM32_CSI_CR);
+
+ ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0,
+ status,
+ status & STM32_CSI_SR0_VCXSTATEF(vc),
+ 1000, 1000000);
+ if (ret) {
+ dev_err(csidev->dev, "failed to start VC(%d)\n", vc);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_csi_stop_vc(struct stm32_csi_dev *csidev, u32 vc)
+{
+ u32 status;
+ int ret;
+
+ /* Stop the Virtual Channel */
+ writel_relaxed(STM32_CSI_CR_VCXSTOP(vc) | STM32_CSI_CR_CSIEN,
+ csidev->base + STM32_CSI_CR);
+
+ ret = readl_relaxed_poll_timeout(csidev->base + STM32_CSI_SR0,
+ status,
+ !(status & STM32_CSI_SR0_VCXSTATEF(vc)),
+ 1000, 1000000);
+ if (ret) {
+ dev_err(csidev->dev, "failed to stop VC(%d)\n", vc);
+ return ret;
+ }
+
+ /* Disable all DTs */
+ writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR1(vc));
+ writel_relaxed(0, csidev->base + STM32_CSI_VCXCFGR2(vc));
+
+ return 0;
+}
+
+static int stm32_csi_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct stm32_csi_dev *csidev = to_csidev(sd);
+ int ret;
+
+ ret = v4l2_subdev_disable_streams(csidev->s_subdev,
+ csidev->s_subdev_pad_nb, BIT_ULL(0));
+ if (ret)
+ return ret;
+
+ /* Stop the VC0 */
+ ret = stm32_csi_stop_vc(csidev, 0);
+ if (ret)
+ dev_err(csidev->dev, "Failed to stop VC0\n");
+
+ stm32_csi_stop(csidev);
+
+ return 0;
+}
+
+static int stm32_csi_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct stm32_csi_dev *csidev = to_csidev(sd);
+ int ret;
+
+ ret = stm32_csi_start(csidev, state);
+ if (ret)
+ return ret;
+
+ /* Configure & start the VC0 */
+ ret = stm32_csi_start_vc(csidev, state, 0);
+ if (ret) {
+ dev_err(csidev->dev, "Failed to start VC0\n");
+ goto failed_start_vc;
+ }
+
+ ret = v4l2_subdev_enable_streams(csidev->s_subdev,
+ csidev->s_subdev_pad_nb, BIT_ULL(0));
+ if (ret)
+ goto failed_enable_streams;
+
+ return 0;
+
+failed_enable_streams:
+ stm32_csi_stop_vc(csidev, 0);
+failed_start_vc:
+ stm32_csi_stop(csidev);
+ return ret;
+}
+
+static int stm32_csi_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < sd->entity.num_pads; i++)
+ *v4l2_subdev_state_get_format(state, i) = fmt_default;
+
+ return 0;
+}
+
+static int stm32_csi_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index >= ARRAY_SIZE(stm32_csi_formats))
+ return -EINVAL;
+
+ code->code = stm32_csi_formats[code->index].code;
+ return 0;
+}
+
+static int stm32_csi_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct stm32_csi_dev *csidev = to_csidev(sd);
+ struct v4l2_mbus_framefmt *framefmt;
+ const struct stm32_csi_fmts *fmt;
+
+ fmt = stm32_csi_code_to_fmt(format->format.code);
+ if (!fmt) {
+ dev_dbg(csidev->dev, "Unsupported code %d, use default\n",
+ format->format.code);
+ format->format.code = fmt_default.code;
+ }
+
+ framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SINK);
+
+ if (format->pad == STM32_CSI_PAD_SOURCE)
+ format->format = *framefmt;
+ else
+ *framefmt = format->format;
+
+ framefmt = v4l2_subdev_state_get_format(state, STM32_CSI_PAD_SOURCE);
+ *framefmt = format->format;
+
+ return 0;
+}
+
+static int stm32_csi_log_status(struct v4l2_subdev *sd)
+{
+ struct stm32_csi_dev *csidev = to_csidev(sd);
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&csidev->slock, flags);
+
+ for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++) {
+ if (csidev->sr0_counters[i])
+ dev_info(csidev->dev, "%s events: %d\n",
+ stm32_csi_events_sr0[i].name,
+ csidev->sr0_counters[i]);
+ }
+
+ for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++) {
+ if (csidev->sr1_counters[i])
+ dev_info(csidev->dev, "%s events: %d\n",
+ stm32_csi_events_sr1[i].name,
+ csidev->sr1_counters[i]);
+ }
+
+ spin_unlock_irqrestore(&csidev->slock, flags);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops stm32_csi_core_ops = {
+ .log_status = stm32_csi_log_status,
+};
+
+static const struct v4l2_subdev_video_ops stm32_csi_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops stm32_csi_pad_ops = {
+ .enum_mbus_code = stm32_csi_enum_mbus_code,
+ .set_fmt = stm32_csi_set_pad_format,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .enable_streams = stm32_csi_enable_streams,
+ .disable_streams = stm32_csi_disable_streams,
+};
+
+static const struct v4l2_subdev_ops stm32_csi_subdev_ops = {
+ .core = &stm32_csi_core_ops,
+ .pad = &stm32_csi_pad_ops,
+ .video = &stm32_csi_video_ops,
+};
+
+static const struct v4l2_subdev_internal_ops stm32_csi_subdev_internal_ops = {
+ .init_state = stm32_csi_init_state,
+};
+
+static int stm32_csi_async_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *s_subdev,
+ struct v4l2_async_connection *asd)
+{
+ struct v4l2_subdev *sd = notifier->sd;
+ struct stm32_csi_dev *csidev = to_csidev(sd);
+ int remote_pad;
+
+ remote_pad = media_entity_get_fwnode_pad(&s_subdev->entity,
+ s_subdev->fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (remote_pad < 0) {
+ dev_err(csidev->dev, "Couldn't find output pad for subdev %s\n",
+ s_subdev->name);
+ return remote_pad;
+ }
+
+ csidev->s_subdev = s_subdev;
+ csidev->s_subdev_pad_nb = remote_pad;
+
+ return media_create_pad_link(&csidev->s_subdev->entity,
+ remote_pad, &csidev->sd.entity,
+ STM32_CSI_PAD_SINK,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+static const struct v4l2_async_notifier_operations stm32_csi_notifier_ops = {
+ .bound = stm32_csi_async_bound,
+};
+
+static irqreturn_t stm32_csi_irq_thread(int irq, void *arg)
+{
+ struct stm32_csi_dev *csidev = arg;
+ unsigned long flags;
+ u32 sr0, sr1;
+ int i;
+
+ sr0 = readl_relaxed(csidev->base + STM32_CSI_SR0);
+ sr1 = readl_relaxed(csidev->base + STM32_CSI_SR1);
+
+ /* Clear interrupt */
+ writel_relaxed(sr0 & STM32_CSI_SR0_ERRORS,
+ csidev->base + STM32_CSI_FCR0);
+ writel_relaxed(sr1 & STM32_CSI_SR1_ERRORS,
+ csidev->base + STM32_CSI_FCR1);
+
+ spin_lock_irqsave(&csidev->slock, flags);
+
+ for (i = 0; i < STM32_CSI_NUM_SR0_EVENTS; i++)
+ if (sr0 & stm32_csi_events_sr0[i].mask)
+ csidev->sr0_counters[i]++;
+
+ for (i = 0; i < STM32_CSI_NUM_SR1_EVENTS; i++)
+ if (sr1 & stm32_csi_events_sr1[i].mask)
+ csidev->sr1_counters[i]++;
+
+ spin_unlock_irqrestore(&csidev->slock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int stm32_csi_get_resources(struct stm32_csi_dev *csidev,
+ struct platform_device *pdev)
+{
+ unsigned int i;
+ int irq, ret;
+
+ csidev->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
+ if (IS_ERR(csidev->base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(csidev->base),
+ "Failed to ioremap resource\n");
+
+ for (i = 0; i < STM32_CSI_CLK_NB; i++)
+ csidev->clks[i].id = stm32_csi_clks_id[i];
+
+ ret = devm_clk_bulk_get(&pdev->dev, STM32_CSI_CLK_NB,
+ csidev->clks);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret, "Couldn't get clks\n");
+
+ csidev->supplies[0].supply = "vdd";
+ csidev->supplies[1].supply = "vdda18";
+ ret = devm_regulator_bulk_get(&pdev->dev, ARRAY_SIZE(csidev->supplies),
+ csidev->supplies);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to request regulator vdd\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ stm32_csi_irq_thread, IRQF_ONESHOT,
+ dev_name(&pdev->dev), csidev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Unable to request irq");
+
+ return 0;
+}
+
+static int stm32_csi_parse_dt(struct stm32_csi_dev *csidev)
+{
+ struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
+ struct v4l2_async_connection *asd;
+ struct fwnode_handle *ep;
+ int ret;
+
+ /* Get bus characteristics from devicetree */
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(csidev->dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep) {
+ dev_err(csidev->dev, "Could not find the endpoint\n");
+ return -ENODEV;
+ }
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &v4l2_ep);
+ if (ret) {
+ dev_err(csidev->dev, "Could not parse v4l2 endpoint\n");
+ goto out;
+ }
+
+ csidev->num_lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
+ if (csidev->num_lanes > STM32_CSI_LANES_MAX) {
+ dev_err(csidev->dev, "Unsupported number of data-lanes: %d\n",
+ csidev->num_lanes);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ memcpy(csidev->lanes, v4l2_ep.bus.mipi_csi2.data_lanes,
+ sizeof(csidev->lanes));
+
+ v4l2_async_subdev_nf_init(&csidev->notifier, &csidev->sd);
+
+ asd = v4l2_async_nf_add_fwnode_remote(&csidev->notifier, ep,
+ struct v4l2_async_connection);
+
+
+ if (IS_ERR(asd)) {
+ dev_err(csidev->dev, "Failed to add fwnode remote subdev\n");
+ ret = PTR_ERR(asd);
+ goto out;
+ }
+
+ csidev->notifier.ops = &stm32_csi_notifier_ops;
+
+ ret = v4l2_async_nf_register(&csidev->notifier);
+ if (ret) {
+ dev_err(csidev->dev, "Failed to register notifier\n");
+ v4l2_async_nf_cleanup(&csidev->notifier);
+ goto out;
+ }
+
+out:
+ fwnode_handle_put(ep);
+ return ret;
+}
+
+static int stm32_csi_probe(struct platform_device *pdev)
+{
+ struct stm32_csi_dev *csidev;
+ struct reset_control *rstc;
+ int ret;
+
+ csidev = devm_kzalloc(&pdev->dev, sizeof(*csidev), GFP_KERNEL);
+ if (!csidev)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, csidev);
+ csidev->dev = &pdev->dev;
+
+ spin_lock_init(&csidev->slock);
+
+ ret = stm32_csi_get_resources(csidev, pdev);
+ if (ret)
+ return ret;
+
+ ret = stm32_csi_parse_dt(csidev);
+ if (ret)
+ return ret;
+
+ csidev->sd.owner = THIS_MODULE;
+ csidev->sd.dev = &pdev->dev;
+ csidev->sd.internal_ops = &stm32_csi_subdev_internal_ops;
+ v4l2_subdev_init(&csidev->sd, &stm32_csi_subdev_ops);
+ v4l2_set_subdevdata(&csidev->sd, &pdev->dev);
+ snprintf(csidev->sd.name, sizeof(csidev->sd.name), "%s",
+ dev_name(&pdev->dev));
+
+ /* Create our media pads */
+ csidev->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ csidev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ csidev->pads[STM32_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ csidev->pads[STM32_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&csidev->sd.entity, STM32_CSI_PAD_MAX,
+ csidev->pads);
+ if (ret)
+ goto err_cleanup;
+
+ ret = v4l2_subdev_init_finalize(&csidev->sd);
+ if (ret < 0)
+ goto err_cleanup;
+
+ /* Reset device */
+ rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(rstc)) {
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(rstc),
+ "Couldn't get reset control\n");
+ goto err_cleanup;
+ }
+
+ ret = reset_control_assert(rstc);
+ if (ret) {
+ ret = dev_err_probe(&pdev->dev, ret,
+ "Failed to assert the reset line\n");
+ goto err_cleanup;
+ }
+
+ usleep_range(3000, 5000);
+
+ ret = reset_control_deassert(rstc);
+ if (ret) {
+ ret = dev_err_probe(&pdev->dev, ret,
+ "Failed to deassert the reset line\n");
+ goto err_cleanup;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+
+ ret = v4l2_async_register_subdev(&csidev->sd);
+ if (ret < 0)
+ goto err_cleanup;
+
+ dev_info(&pdev->dev,
+ "Probed CSI with %u lanes\n", csidev->num_lanes);
+
+ return 0;
+
+err_cleanup:
+ v4l2_async_nf_cleanup(&csidev->notifier);
+ return ret;
+}
+
+static void stm32_csi_remove(struct platform_device *pdev)
+{
+ struct stm32_csi_dev *csidev = platform_get_drvdata(pdev);
+
+ v4l2_async_unregister_subdev(&csidev->sd);
+
+ pm_runtime_disable(&pdev->dev);
+}
+
+static int stm32_csi_runtime_suspend(struct device *dev)
+{
+ struct stm32_csi_dev *csidev = dev_get_drvdata(dev);
+ int ret;
+
+ clk_bulk_disable_unprepare(STM32_CSI_CLK_NB, csidev->clks);
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies),
+ csidev->supplies);
+ if (ret < 0)
+ dev_err(dev, "cannot disable regulators %d\n", ret);
+
+ return 0;
+}
+
+static int stm32_csi_runtime_resume(struct device *dev)
+{
+ struct stm32_csi_dev *csidev = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(csidev->supplies),
+ csidev->supplies);
+ if (ret)
+ goto error_out;
+
+ ret = clk_bulk_prepare_enable(STM32_CSI_CLK_NB, csidev->clks);
+ if (ret)
+ goto error_disable_supplies;
+
+ return 0;
+
+error_disable_supplies:
+ ret = regulator_bulk_disable(ARRAY_SIZE(csidev->supplies), csidev->supplies);
+ if (ret < 0)
+ dev_err(dev, "cannot disable regulators %d\n", ret);
+error_out:
+ dev_err(csidev->dev, "Failed to resume: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id stm32_csi_of_table[] = {
+ { .compatible = "st,stm32mp25-csi", },
+ { /* end node */ },
+};
+MODULE_DEVICE_TABLE(of, stm32_csi_of_table);
+
+static const struct dev_pm_ops stm32_csi_pm_ops = {
+ RUNTIME_PM_OPS(stm32_csi_runtime_suspend,
+ stm32_csi_runtime_resume, NULL)
+};
+
+static struct platform_driver stm32_csi_driver = {
+ .driver = {
+ .name = "stm32-csi",
+ .of_match_table = stm32_csi_of_table,
+ .pm = pm_ptr(&stm32_csi_pm_ops),
+ },
+ .probe = stm32_csi_probe,
+ .remove = stm32_csi_remove,
+};
+
+module_platform_driver(stm32_csi_driver);
+
+MODULE_AUTHOR("Alain Volmat <alain.volmat@foss.st.com>");
+MODULE_DESCRIPTION("STM32 CSI controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index ff3331af9406..d94c61b8569d 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -388,9 +388,9 @@ static void dcmi_set_crop(struct stm32_dcmi *dcmi)
((dcmi->crop.left << 1));
reg_write(dcmi->regs, DCMI_CWSTRT, start);
- dev_dbg(dcmi->dev, "Cropping to %ux%u@%u:%u\n",
- dcmi->crop.width, dcmi->crop.height,
- dcmi->crop.left, dcmi->crop.top);
+ dev_dbg(dcmi->dev, "Cropping to (%d,%d)/%ux%u\n",
+ dcmi->crop.left, dcmi->crop.top,
+ dcmi->crop.width, dcmi->crop.height);
/* Enable crop */
reg_set(dcmi->regs, DCMI_CR, CR_CROP);
@@ -898,8 +898,6 @@ static const struct vb2_ops dcmi_video_qops = {
.buf_queue = dcmi_buf_queue,
.start_streaming = dcmi_start_streaming,
.stop_streaming = dcmi_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int dcmi_g_fmt_vid_cap(struct file *file, void *priv,
@@ -1294,8 +1292,8 @@ static int dcmi_s_selection(struct file *file, void *priv,
/* Crop if request is different than sensor resolution */
dcmi->do_crop = true;
dcmi->crop = r;
- dev_dbg(dcmi->dev, "s_selection: crop %ux%u@(%u,%u) from %ux%u\n",
- r.width, r.height, r.left, r.top,
+ dev_dbg(dcmi->dev, "s_selection: crop (%d,%d)/%ux%u from %ux%u\n",
+ r.left, r.top, r.width, r.height,
pix.width, pix.height);
} else {
/* Disable crop */
@@ -1684,18 +1682,14 @@ static int dcmi_formats_init(struct stm32_dcmi *dcmi)
return -ENXIO;
dcmi->num_of_sd_formats = num_fmts;
- dcmi->sd_formats = devm_kcalloc(dcmi->dev,
- num_fmts, sizeof(struct dcmi_format *),
- GFP_KERNEL);
+ dcmi->sd_formats = devm_kmemdup_array(dcmi->dev, sd_fmts, num_fmts,
+ sizeof(*sd_fmts), GFP_KERNEL);
if (!dcmi->sd_formats) {
dev_err(dcmi->dev, "Could not allocate memory\n");
return -ENOMEM;
}
- memcpy(dcmi->sd_formats, sd_fmts,
- num_fmts * sizeof(struct dcmi_format *));
dcmi->sd_format = dcmi->sd_formats[0];
-
return 0;
}
@@ -2149,7 +2143,7 @@ static const struct dev_pm_ops dcmi_pm_ops = {
static struct platform_driver stm32_dcmi_driver = {
.probe = dcmi_probe,
- .remove_new = dcmi_remove,
+ .remove = dcmi_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(stm32_dcmi_of_match),
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
index 8920d9388a21..159105fb40b8 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-parallel.o dcmipp-byteproc.o dcmipp-bytecap.o
+stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o
obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index 9f768f011fa2..1c1b6b48918e 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -56,15 +56,32 @@ struct dcmipp_bytecap_pix_map {
static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = {
PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565),
+ PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565),
PIXMAP_MBUS_PFMT(YUYV8_2X8, YUYV),
+ PIXMAP_MBUS_PFMT(YUYV8_1X16, YUYV),
PIXMAP_MBUS_PFMT(YVYU8_2X8, YVYU),
+ PIXMAP_MBUS_PFMT(YVYU8_1X16, YVYU),
PIXMAP_MBUS_PFMT(UYVY8_2X8, UYVY),
+ PIXMAP_MBUS_PFMT(UYVY8_1X16, UYVY),
PIXMAP_MBUS_PFMT(VYUY8_2X8, VYUY),
+ PIXMAP_MBUS_PFMT(VYUY8_1X16, VYUY),
PIXMAP_MBUS_PFMT(Y8_1X8, GREY),
PIXMAP_MBUS_PFMT(SBGGR8_1X8, SBGGR8),
PIXMAP_MBUS_PFMT(SGBRG8_1X8, SGBRG8),
PIXMAP_MBUS_PFMT(SGRBG8_1X8, SGRBG8),
PIXMAP_MBUS_PFMT(SRGGB8_1X8, SRGGB8),
+ PIXMAP_MBUS_PFMT(SBGGR10_1X10, SBGGR10),
+ PIXMAP_MBUS_PFMT(SGBRG10_1X10, SGBRG10),
+ PIXMAP_MBUS_PFMT(SGRBG10_1X10, SGRBG10),
+ PIXMAP_MBUS_PFMT(SRGGB10_1X10, SRGGB10),
+ PIXMAP_MBUS_PFMT(SBGGR12_1X12, SBGGR12),
+ PIXMAP_MBUS_PFMT(SGBRG12_1X12, SGBRG12),
+ PIXMAP_MBUS_PFMT(SGRBG12_1X12, SGRBG12),
+ PIXMAP_MBUS_PFMT(SRGGB12_1X12, SRGGB12),
+ PIXMAP_MBUS_PFMT(SBGGR14_1X14, SBGGR14),
+ PIXMAP_MBUS_PFMT(SGBRG14_1X14, SGBRG14),
+ PIXMAP_MBUS_PFMT(SGRBG14_1X14, SGRBG14),
+ PIXMAP_MBUS_PFMT(SRGGB14_1X14, SRGGB14),
PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG),
};
@@ -112,6 +129,7 @@ struct dcmipp_bytecap_device {
u32 sequence;
struct media_pipeline pipe;
struct v4l2_subdev *s_subdev;
+ u32 s_subdev_pad_nb;
enum dcmipp_state state;
@@ -250,34 +268,34 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
{
const struct dcmipp_bytecap_pix_map *vpix;
unsigned int index = f->index;
- unsigned int i;
+ unsigned int i, prev_pixelformat = 0;
- if (f->mbus_code) {
- /*
- * If a media bus code is specified, only enumerate formats
- * compatible with it.
- */
- for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
- vpix = &dcmipp_bytecap_pix_map_list[i];
- if (vpix->code != f->mbus_code)
- continue;
+ /*
+ * List up all formats (or only ones matching f->mbus_code), taking
+ * care of removing duplicated entries (due to support of both
+ * parallel & csi 16 bits formats
+ */
+ for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
+ vpix = &dcmipp_bytecap_pix_map_list[i];
+ /* Skip formats not matching requested mbus code */
+ if (f->mbus_code && vpix->code != f->mbus_code)
+ continue;
- if (index == 0)
- break;
+ /* Skip duplicated pixelformat */
+ if (vpix->pixelformat == prev_pixelformat)
+ continue;
- index--;
- }
+ prev_pixelformat = vpix->pixelformat;
- if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list))
- return -EINVAL;
- } else {
- /* Otherwise, enumerate all formats. */
- if (f->index >= ARRAY_SIZE(dcmipp_bytecap_pix_map_list))
- return -EINVAL;
+ if (index == 0)
+ break;
- vpix = &dcmipp_bytecap_pix_map_list[f->index];
+ index--;
}
+ if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list))
+ return -EINVAL;
+
f->pixelformat = vpix->pixelformat;
return 0;
@@ -337,33 +355,6 @@ static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = {
.vidioc_streamoff = vb2_ioctl_streamoff,
};
-static int dcmipp_pipeline_s_stream(struct dcmipp_bytecap_device *vcap,
- int state)
-{
- struct media_pad *pad;
- int ret;
-
- /*
- * Get source subdev - since link is IMMUTABLE, pointer is cached
- * within the dcmipp_bytecap_device structure
- */
- if (!vcap->s_subdev) {
- pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
- if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
- return -EINVAL;
- vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity);
- }
-
- ret = v4l2_subdev_call(vcap->s_subdev, video, s_stream, state);
- if (ret < 0) {
- dev_err(vcap->dev, "failed to %s streaming (%d)\n",
- state ? "start" : "stop", ret);
- return ret;
- }
-
- return 0;
-}
-
static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap,
struct dcmipp_buf *buf)
{
@@ -395,11 +386,24 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq);
struct media_entity *entity = &vcap->vdev.entity;
struct dcmipp_buf *buf;
+ struct media_pad *pad;
int ret;
vcap->sequence = 0;
memset(&vcap->count, 0, sizeof(vcap->count));
+ /*
+ * Get source subdev - since link is IMMUTABLE, pointer is cached
+ * within the dcmipp_bytecap_device structure
+ */
+ if (!vcap->s_subdev) {
+ pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ return -EINVAL;
+ vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity);
+ vcap->s_subdev_pad_nb = pad->index;
+ }
+
ret = pm_runtime_resume_and_get(vcap->dev);
if (ret < 0) {
dev_err(vcap->dev, "%s: Failed to start streaming, cannot get sync (%d)\n",
@@ -414,7 +418,8 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
goto err_pm_put;
}
- ret = dcmipp_pipeline_s_stream(vcap, 1);
+ ret = v4l2_subdev_enable_streams(vcap->s_subdev,
+ vcap->s_subdev_pad_nb, BIT_ULL(0));
if (ret)
goto err_media_pipeline_stop;
@@ -482,7 +487,10 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
int ret;
u32 status;
- dcmipp_pipeline_s_stream(vcap, 0);
+ ret = v4l2_subdev_disable_streams(vcap->s_subdev,
+ vcap->s_subdev_pad_nb, BIT_ULL(0));
+ if (ret)
+ dev_warn(vcap->dev, "Failed to disable stream\n");
/* Stop the media pipeline */
media_pipeline_stop(vcap->vdev.entity.pads);
@@ -625,12 +633,6 @@ static const struct vb2_ops dcmipp_bytecap_qops = {
.buf_prepare = dcmipp_bytecap_buf_prepare,
.buf_queue = dcmipp_bytecap_buf_queue,
.queue_setup = dcmipp_bytecap_queue_setup,
- /*
- * Since q->lock is set we can use the standard
- * vb2_ops_wait_prepare/finish helper functions.
- */
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static void dcmipp_bytecap_release(struct video_device *vdev)
@@ -816,8 +818,7 @@ static int dcmipp_bytecap_link_validate(struct media_link *link)
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.pad = link->source->index,
};
- const struct dcmipp_bytecap_pix_map *vpix;
- int ret;
+ int ret, i;
ret = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &source_fmt);
if (ret < 0)
@@ -831,10 +832,17 @@ static int dcmipp_bytecap_link_validate(struct media_link *link)
return -EINVAL;
}
- vpix = dcmipp_bytecap_pix_map_by_pixelformat(vcap->format.pixelformat);
- if (source_fmt.format.code != vpix->code) {
- dev_err(vcap->dev, "Wrong mbus_code 0x%x, (0x%x expected)\n",
- vpix->code, source_fmt.format.code);
+ for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
+ if (dcmipp_bytecap_pix_map_list[i].pixelformat ==
+ vcap->format.pixelformat &&
+ dcmipp_bytecap_pix_map_list[i].code ==
+ source_fmt.format.code)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) {
+ dev_err(vcap->dev, "mbus code 0x%x do not match capture device format (0x%x)\n",
+ vcap->format.pixelformat, source_fmt.format.code);
return -EINVAL;
}
@@ -893,7 +901,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
q->dev = dev;
/* DCMIPP requires 16 bytes aligned buffers */
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32) & ~0x0f);
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "Failed to set DMA mask\n");
goto err_mutex_destroy;
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
index 5a361ad6b023..db76a02a1848 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
@@ -48,15 +48,32 @@ struct dcmipp_byteproc_pix_map {
}
static const struct dcmipp_byteproc_pix_map dcmipp_byteproc_pix_map_list[] = {
PIXMAP_MBUS_BPP(RGB565_2X8_LE, 2),
+ PIXMAP_MBUS_BPP(RGB565_1X16, 2),
PIXMAP_MBUS_BPP(YUYV8_2X8, 2),
+ PIXMAP_MBUS_BPP(YUYV8_1X16, 2),
PIXMAP_MBUS_BPP(YVYU8_2X8, 2),
+ PIXMAP_MBUS_BPP(YVYU8_1X16, 2),
PIXMAP_MBUS_BPP(UYVY8_2X8, 2),
+ PIXMAP_MBUS_BPP(UYVY8_1X16, 2),
PIXMAP_MBUS_BPP(VYUY8_2X8, 2),
+ PIXMAP_MBUS_BPP(VYUY8_1X16, 2),
PIXMAP_MBUS_BPP(Y8_1X8, 1),
PIXMAP_MBUS_BPP(SBGGR8_1X8, 1),
PIXMAP_MBUS_BPP(SGBRG8_1X8, 1),
PIXMAP_MBUS_BPP(SGRBG8_1X8, 1),
PIXMAP_MBUS_BPP(SRGGB8_1X8, 1),
+ PIXMAP_MBUS_BPP(SBGGR10_1X10, 2),
+ PIXMAP_MBUS_BPP(SGBRG10_1X10, 2),
+ PIXMAP_MBUS_BPP(SGRBG10_1X10, 2),
+ PIXMAP_MBUS_BPP(SRGGB10_1X10, 2),
+ PIXMAP_MBUS_BPP(SBGGR12_1X12, 2),
+ PIXMAP_MBUS_BPP(SGBRG12_1X12, 2),
+ PIXMAP_MBUS_BPP(SGRBG12_1X12, 2),
+ PIXMAP_MBUS_BPP(SRGGB12_1X12, 2),
+ PIXMAP_MBUS_BPP(SBGGR14_1X14, 2),
+ PIXMAP_MBUS_BPP(SGBRG14_1X14, 2),
+ PIXMAP_MBUS_BPP(SGRBG14_1X14, 2),
+ PIXMAP_MBUS_BPP(SRGGB14_1X14, 2),
PIXMAP_MBUS_BPP(JPEG_1X8, 1),
};
@@ -78,7 +95,6 @@ struct dcmipp_byteproc_device {
struct v4l2_subdev sd;
struct device *dev;
void __iomem *regs;
- bool streaming;
};
static const struct v4l2_mbus_framefmt fmt_default = {
@@ -239,11 +255,10 @@ static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
- struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
struct v4l2_rect *crop, *compose;
- if (byteproc->streaming)
+ if (v4l2_subdev_is_streaming(sd))
return -EBUSY;
mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
@@ -358,8 +373,8 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,
mf->width = s->r.width;
mf->height = s->r.height;
- dev_dbg(byteproc->dev, "s_selection: crop %ux%u@(%u,%u)\n",
- crop->width, crop->height, crop->left, crop->top);
+ dev_dbg(byteproc->dev, "s_selection: crop (%d,%d)/%ux%u\n",
+ crop->left, crop->top, crop->width, crop->height);
break;
case V4L2_SEL_TGT_COMPOSE:
mf = v4l2_subdev_state_get_format(sd_state, 0);
@@ -371,9 +386,9 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,
mf->width = s->r.width;
mf->height = s->r.height;
- dev_dbg(byteproc->dev, "s_selection: compose %ux%u@(%u,%u)\n",
- compose->width, compose->height,
- compose->left, compose->top);
+ dev_dbg(byteproc->dev, "s_selection: compose (%d,%d)/%ux%u\n",
+ compose->left, compose->top,
+ compose->width, compose->height);
break;
default:
return -EINVAL;
@@ -382,30 +397,19 @@ static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,
return 0;
}
-static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = {
- .enum_mbus_code = dcmipp_byteproc_enum_mbus_code,
- .enum_frame_size = dcmipp_byteproc_enum_frame_size,
- .get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = dcmipp_byteproc_set_fmt,
- .get_selection = dcmipp_byteproc_get_selection,
- .set_selection = dcmipp_byteproc_set_selection,
-};
-
static int dcmipp_byteproc_configure_scale_crop
- (struct dcmipp_byteproc_device *byteproc)
+ (struct dcmipp_byteproc_device *byteproc,
+ struct v4l2_subdev_state *state)
{
const struct dcmipp_byteproc_pix_map *vpix;
- struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *sink_fmt;
u32 hprediv, vprediv;
struct v4l2_rect *compose, *crop;
u32 val = 0;
- state = v4l2_subdev_lock_and_get_active_state(&byteproc->sd);
sink_fmt = v4l2_subdev_state_get_format(state, 0);
compose = v4l2_subdev_state_get_compose(state, 0);
crop = v4l2_subdev_state_get_crop(state, 1);
- v4l2_subdev_unlock_state(state);
/* find output format bpp */
vpix = dcmipp_byteproc_pix_map_by_code(sink_fmt->code);
@@ -460,48 +464,73 @@ static int dcmipp_byteproc_configure_scale_crop
return 0;
}
-static int dcmipp_byteproc_s_stream(struct v4l2_subdev *sd, int enable)
+static int dcmipp_byteproc_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
{
struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);
struct v4l2_subdev *s_subdev;
- struct media_pad *pad;
- int ret = 0;
+ struct media_pad *s_pad;
+ int ret;
/* Get source subdev */
- pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
- if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
return -EINVAL;
- s_subdev = media_entity_to_v4l2_subdev(pad->entity);
-
- if (enable) {
- ret = dcmipp_byteproc_configure_scale_crop(byteproc);
- if (ret)
- return ret;
-
- ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);
- if (ret < 0) {
- dev_err(byteproc->dev,
- "failed to start source subdev streaming (%d)\n",
- ret);
- return ret;
- }
- } else {
- ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);
- if (ret < 0) {
- dev_err(byteproc->dev,
- "failed to stop source subdev streaming (%d)\n",
- ret);
- return ret;
- }
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ ret = dcmipp_byteproc_configure_scale_crop(byteproc, state);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(byteproc->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
}
- byteproc->streaming = enable;
+ return 0;
+}
+
+static int dcmipp_byteproc_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_byteproc_device *byteproc = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(byteproc->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
return 0;
}
+static const struct v4l2_subdev_pad_ops dcmipp_byteproc_pad_ops = {
+ .enum_mbus_code = dcmipp_byteproc_enum_mbus_code,
+ .enum_frame_size = dcmipp_byteproc_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = dcmipp_byteproc_set_fmt,
+ .get_selection = dcmipp_byteproc_get_selection,
+ .set_selection = dcmipp_byteproc_set_selection,
+ .enable_streams = dcmipp_byteproc_enable_streams,
+ .disable_streams = dcmipp_byteproc_disable_streams,
+};
+
static const struct v4l2_subdev_video_ops dcmipp_byteproc_video_ops = {
- .s_stream = dcmipp_byteproc_s_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_ops dcmipp_byteproc_ops = {
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index 7a7cf43baf24..fe5f97233f5e 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -199,11 +199,11 @@ static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg,
}
/* DCMIPP subdev init / release entry points */
-struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev,
+struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
const char *entity_name,
struct v4l2_device *v4l2_dev,
void __iomem *regs);
-void dcmipp_par_ent_release(struct dcmipp_ent_device *ved);
+void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved);
struct dcmipp_ent_device *
dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
struct v4l2_device *v4l2_dev, void __iomem *regs);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index bce821eb71ce..1b7bae3266c8 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -40,6 +40,7 @@ struct dcmipp_device {
/* Hardware resources */
void __iomem *regs;
+ struct clk *mclk;
struct clk *kclk;
/* The pipeline configuration */
@@ -87,6 +88,9 @@ struct dcmipp_pipeline_config {
size_t num_ents;
const struct dcmipp_ent_link *links;
size_t num_links;
+ u32 hw_revision;
+ bool has_csi2;
+ bool needs_mclk;
};
/* --------------------------------------------------------------------------
@@ -95,9 +99,9 @@ struct dcmipp_pipeline_config {
static const struct dcmipp_ent_config stm32mp13_ent_config[] = {
{
- .name = "dcmipp_parallel",
- .init = dcmipp_par_ent_init,
- .release = dcmipp_par_ent_release,
+ .name = "dcmipp_input",
+ .init = dcmipp_inp_ent_init,
+ .release = dcmipp_inp_ent_release,
},
{
.name = "dcmipp_dump_postproc",
@@ -111,22 +115,60 @@ static const struct dcmipp_ent_config stm32mp13_ent_config[] = {
},
};
-#define ID_PARALLEL 0
+#define ID_INPUT 0
#define ID_DUMP_BYTEPROC 1
#define ID_DUMP_CAPTURE 2
static const struct dcmipp_ent_link stm32mp13_ent_links[] = {
- DCMIPP_ENT_LINK(ID_PARALLEL, 1, ID_DUMP_BYTEPROC, 0,
+ DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0,
MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0,
MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
};
+#define DCMIPP_STM32MP13_VERR 0x10
static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = {
.ents = stm32mp13_ent_config,
.num_ents = ARRAY_SIZE(stm32mp13_ent_config),
.links = stm32mp13_ent_links,
- .num_links = ARRAY_SIZE(stm32mp13_ent_links)
+ .num_links = ARRAY_SIZE(stm32mp13_ent_links),
+ .hw_revision = DCMIPP_STM32MP13_VERR
+};
+
+static const struct dcmipp_ent_config stm32mp25_ent_config[] = {
+ {
+ .name = "dcmipp_input",
+ .init = dcmipp_inp_ent_init,
+ .release = dcmipp_inp_ent_release,
+ },
+ {
+ .name = "dcmipp_dump_postproc",
+ .init = dcmipp_byteproc_ent_init,
+ .release = dcmipp_byteproc_ent_release,
+ },
+ {
+ .name = "dcmipp_dump_capture",
+ .init = dcmipp_bytecap_ent_init,
+ .release = dcmipp_bytecap_ent_release,
+ },
+};
+
+static const struct dcmipp_ent_link stm32mp25_ent_links[] = {
+ DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+};
+
+#define DCMIPP_STM32MP25_VERR 0x30
+static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = {
+ .ents = stm32mp25_ent_config,
+ .num_ents = ARRAY_SIZE(stm32mp25_ent_config),
+ .links = stm32mp25_ent_links,
+ .num_links = ARRAY_SIZE(stm32mp25_ent_links),
+ .hw_revision = DCMIPP_STM32MP25_VERR,
+ .has_csi2 = true,
+ .needs_mclk = true
};
#define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\
@@ -202,13 +244,14 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp)
return 0;
err_init_entity:
- while (i > 0)
- dcmipp->pipe_cfg->ents[i - 1].release(dcmipp->entity[i - 1]);
+ while (i-- > 0)
+ dcmipp->pipe_cfg->ents[i].release(dcmipp->entity[i]);
return ret;
}
static const struct of_device_id dcmipp_of_match[] = {
{ .compatible = "st,stm32mp13-dcmipp", .data = &stm32mp13_pipe_cfg },
+ { .compatible = "st,stm32mp25-dcmipp", .data = &stm32mp25_pipe_cfg },
{ /* end node */ },
};
MODULE_DEVICE_TABLE(of, dcmipp_of_match);
@@ -257,11 +300,14 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier,
struct v4l2_async_connection *asd)
{
struct dcmipp_device *dcmipp = notifier_to_dcmipp(notifier);
- unsigned int ret;
- int src_pad;
+ int ret = -EINVAL;
+ int src_pad, i;
struct dcmipp_ent_device *sink;
- struct v4l2_fwnode_endpoint vep = { .bus_type = V4L2_MBUS_PARALLEL };
+ struct v4l2_fwnode_endpoint vep = { 0 };
struct fwnode_handle *ep;
+ enum v4l2_mbus_type supported_types[] = {
+ V4L2_MBUS_PARALLEL, V4L2_MBUS_BT656, V4L2_MBUS_CSI2_DPHY
+ };
dev_dbg(dcmipp->dev, "Subdev \"%s\" bound\n", subdev->name);
@@ -281,21 +327,28 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier,
return -ENODEV;
}
- /* Check for parallel bus-type first, then bt656 */
- ret = v4l2_fwnode_endpoint_parse(ep, &vep);
- if (ret) {
- vep.bus_type = V4L2_MBUS_BT656;
+ /* Check for supported MBUS type */
+ for (i = 0; i < ARRAY_SIZE(supported_types); i++) {
+ /* Only MP25 supports CSI input */
+ if (supported_types[i] == V4L2_MBUS_CSI2_DPHY &&
+ !dcmipp->pipe_cfg->has_csi2)
+ continue;
+
+ vep.bus_type = supported_types[i];
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
- if (ret) {
- dev_err(dcmipp->dev, "Could not parse the endpoint\n");
- fwnode_handle_put(ep);
- return ret;
- }
+ if (!ret)
+ break;
}
fwnode_handle_put(ep);
- if (vep.bus.parallel.bus_width == 0) {
+ if (ret) {
+ dev_err(dcmipp->dev, "Could not parse the endpoint\n");
+ return ret;
+ }
+
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY &&
+ vep.bus.parallel.bus_width == 0) {
dev_err(dcmipp->dev, "Invalid parallel interface bus-width\n");
return -ENODEV;
}
@@ -308,11 +361,13 @@ static int dcmipp_graph_notify_bound(struct v4l2_async_notifier *notifier,
return -ENODEV;
}
- /* Parallel input device detected, connect it to parallel subdev */
- sink = dcmipp->entity[ID_PARALLEL];
- sink->bus.flags = vep.bus.parallel.flags;
- sink->bus.bus_width = vep.bus.parallel.bus_width;
- sink->bus.data_shift = vep.bus.parallel.data_shift;
+ /* Connect input device to the dcmipp_input subdev */
+ sink = dcmipp->entity[ID_INPUT];
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY) {
+ sink->bus.flags = vep.bus.parallel.flags;
+ sink->bus.bus_width = vep.bus.parallel.bus_width;
+ sink->bus.data_shift = vep.bus.parallel.data_shift;
+ }
sink->bus_type = vep.bus_type;
ret = media_create_pad_link(&subdev->entity, src_pad, sink->ent, 0,
MEDIA_LNK_FL_IMMUTABLE |
@@ -411,7 +466,7 @@ static int dcmipp_graph_init(struct dcmipp_device *dcmipp)
static int dcmipp_probe(struct platform_device *pdev)
{
struct dcmipp_device *dcmipp;
- struct clk *kclk;
+ struct clk *kclk, *mclk;
const struct dcmipp_pipeline_config *pipe_cfg;
struct reset_control *rstc;
int irq;
@@ -439,11 +494,8 @@ static int dcmipp_probe(struct platform_device *pdev)
"Could not get reset control\n");
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- if (irq != -EPROBE_DEFER)
- dev_err(&pdev->dev, "Could not get irq\n");
- return irq ? irq : -ENXIO;
- }
+ if (irq < 0)
+ return irq;
dcmipp->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
if (IS_ERR(dcmipp->regs)) {
@@ -474,12 +526,20 @@ static int dcmipp_probe(struct platform_device *pdev)
return ret;
}
- kclk = devm_clk_get(&pdev->dev, NULL);
+ kclk = devm_clk_get(&pdev->dev, "kclk");
if (IS_ERR(kclk))
return dev_err_probe(&pdev->dev, PTR_ERR(kclk),
"Unable to get kclk\n");
dcmipp->kclk = kclk;
+ if (dcmipp->pipe_cfg->needs_mclk) {
+ mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(mclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(mclk),
+ "Unable to get mclk\n");
+ dcmipp->mclk = mclk;
+ }
+
dcmipp->entity = devm_kcalloc(&pdev->dev, dcmipp->pipe_cfg->num_ents,
sizeof(*dcmipp->entity), GFP_KERNEL);
if (!dcmipp->entity)
@@ -499,6 +559,7 @@ static int dcmipp_probe(struct platform_device *pdev)
/* Initialize media device */
strscpy(dcmipp->mdev.model, DCMIPP_MDEV_MODEL_NAME,
sizeof(dcmipp->mdev.model));
+ dcmipp->mdev.hw_revision = pipe_cfg->hw_revision;
dcmipp->mdev.dev = &pdev->dev;
media_device_init(&dcmipp->mdev);
@@ -541,6 +602,7 @@ static int dcmipp_runtime_suspend(struct device *dev)
struct dcmipp_device *dcmipp = dev_get_drvdata(dev);
clk_disable_unprepare(dcmipp->kclk);
+ clk_disable_unprepare(dcmipp->mclk);
return 0;
}
@@ -550,9 +612,17 @@ static int dcmipp_runtime_resume(struct device *dev)
struct dcmipp_device *dcmipp = dev_get_drvdata(dev);
int ret;
+ ret = clk_prepare_enable(dcmipp->mclk);
+ if (ret) {
+ dev_err(dev, "%s: Failed to prepare_enable mclk\n", __func__);
+ return ret;
+ }
+
ret = clk_prepare_enable(dcmipp->kclk);
- if (ret)
+ if (ret) {
+ clk_disable_unprepare(dcmipp->mclk);
dev_err(dev, "%s: Failed to prepare_enable kclk\n", __func__);
+ }
return ret;
}
@@ -586,7 +656,7 @@ static const struct dev_pm_ops dcmipp_pm_ops = {
static struct platform_driver dcmipp_pdrv = {
.probe = dcmipp_probe,
- .remove_new = dcmipp_remove,
+ .remove = dcmipp_remove,
.driver = {
.name = DCMIPP_PDEV_NAME,
.of_match_table = dcmipp_of_match,
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
new file mode 100644
index 000000000000..7e5311b67d7e
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2023
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/v4l2-mediabus.h>
+#include <media/mipi-csi2.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
+
+#include "dcmipp-common.h"
+
+#define DCMIPP_PRCR 0x104
+#define DCMIPP_PRCR_FORMAT_SHIFT 16
+#define DCMIPP_PRCR_FORMAT_YUV422 0x1e
+#define DCMIPP_PRCR_FORMAT_RGB565 0x22
+#define DCMIPP_PRCR_FORMAT_RAW8 0x2a
+#define DCMIPP_PRCR_FORMAT_RAW10 0x2b
+#define DCMIPP_PRCR_FORMAT_RAW12 0x2c
+#define DCMIPP_PRCR_FORMAT_RAW14 0x2d
+#define DCMIPP_PRCR_FORMAT_G8 0x4a
+#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a
+#define DCMIPP_PRCR_ESS BIT(4)
+#define DCMIPP_PRCR_PCKPOL BIT(5)
+#define DCMIPP_PRCR_HSPOL BIT(6)
+#define DCMIPP_PRCR_VSPOL BIT(7)
+#define DCMIPP_PRCR_ENABLE BIT(14)
+#define DCMIPP_PRCR_SWAPCYCLES BIT(25)
+
+#define DCMIPP_PRESCR 0x108
+#define DCMIPP_PRESUR 0x10c
+
+#define DCMIPP_CMCR 0x204
+#define DCMIPP_CMCR_INSEL BIT(0)
+
+#define DCMIPP_P0FSCR 0x404
+#define DCMIPP_P0FSCR_DTMODE_MASK GENMASK(17, 16)
+#define DCMIPP_P0FSCR_DTMODE_SHIFT 16
+#define DCMIPP_P0FSCR_DTMODE_DTIDA 0x00
+#define DCMIPP_P0FSCR_DTMODE_ALLDT 0x03
+#define DCMIPP_P0FSCR_DTIDA_MASK GENMASK(5, 0)
+#define DCMIPP_P0FSCR_DTIDA_SHIFT 0
+
+#define IS_SINK(pad) (!(pad))
+#define IS_SRC(pad) ((pad))
+
+struct dcmipp_inp_pix_map {
+ unsigned int code_sink;
+ unsigned int code_src;
+ /* Parallel related information */
+ u8 prcr_format;
+ u8 prcr_swapcycles;
+ /* CSI related information */
+ unsigned int dt;
+};
+
+#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap, data_type) \
+ { \
+ .code_sink = MEDIA_BUS_FMT_##sink, \
+ .code_src = MEDIA_BUS_FMT_##src, \
+ .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \
+ .prcr_swapcycles = swap, \
+ .dt = data_type, \
+ }
+static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = {
+ /* RGB565 */
+ PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1, MIPI_CSI2_DT_RGB565),
+ PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0, MIPI_CSI2_DT_RGB565),
+ PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_1X16, RGB565_1X16, RGB565, 0, MIPI_CSI2_DT_RGB565),
+ /* YUV422 */
+ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_1X16, UYVY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_1X16, YVYU8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
+ /* GREY */
+ PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8),
+ /* Raw Bayer */
+ PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0, MIPI_CSI2_DT_RAW8),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR10_1X10, SBGGR10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG10_1X10, SGBRG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG10_1X10, SGRBG10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB10_1X10, SRGGB10_1X10, RAW10, 0, MIPI_CSI2_DT_RAW10),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR12_1X12, SBGGR12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG12_1X12, SGBRG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG12_1X12, SGRBG12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB12_1X12, SRGGB12_1X12, RAW12, 0, MIPI_CSI2_DT_RAW12),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR14_1X14, SBGGR14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG14_1X14, SGBRG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG14_1X14, SGRBG14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14),
+ PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB14_1X14, SRGGB14_1X14, RAW14, 0, MIPI_CSI2_DT_RAW14),
+ /* JPEG */
+ PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0, 0),
+};
+
+/*
+ * Search through the pix_map table, skipping two consecutive entry with the
+ * same code
+ */
+static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_index
+ (unsigned int index,
+ unsigned int pad)
+{
+ unsigned int i = 0;
+ u32 prev_code = 0, cur_code;
+
+ while (i < ARRAY_SIZE(dcmipp_inp_pix_map_list)) {
+ if (IS_SRC(pad))
+ cur_code = dcmipp_inp_pix_map_list[i].code_src;
+ else
+ cur_code = dcmipp_inp_pix_map_list[i].code_sink;
+
+ if (cur_code == prev_code) {
+ i++;
+ continue;
+ }
+ prev_code = cur_code;
+
+ if (index == 0)
+ break;
+ i++;
+ index--;
+ }
+
+ if (i >= ARRAY_SIZE(dcmipp_inp_pix_map_list))
+ return NULL;
+
+ return &dcmipp_inp_pix_map_list[i];
+}
+
+static inline const struct dcmipp_inp_pix_map *dcmipp_inp_pix_map_by_code
+ (u32 code_sink, u32 code_src)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dcmipp_inp_pix_map_list); i++) {
+ if ((dcmipp_inp_pix_map_list[i].code_sink == code_sink &&
+ dcmipp_inp_pix_map_list[i].code_src == code_src) ||
+ (dcmipp_inp_pix_map_list[i].code_sink == code_src &&
+ dcmipp_inp_pix_map_list[i].code_src == code_sink) ||
+ (dcmipp_inp_pix_map_list[i].code_sink == code_sink &&
+ code_src == 0) ||
+ (code_sink == 0 &&
+ dcmipp_inp_pix_map_list[i].code_src == code_src))
+ return &dcmipp_inp_pix_map_list[i];
+ }
+ return NULL;
+}
+
+struct dcmipp_inp_device {
+ struct dcmipp_ent_device ved;
+ struct v4l2_subdev sd;
+ struct device *dev;
+ void __iomem *regs;
+};
+
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = DCMIPP_COLORSPACE_DEFAULT,
+ .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,
+ .quantization = DCMIPP_QUANTIZATION_DEFAULT,
+ .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
+};
+
+static int dcmipp_inp_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ unsigned int i;
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = v4l2_subdev_state_get_format(sd_state, i);
+ *mf = fmt_default;
+ }
+
+ return 0;
+}
+
+static int dcmipp_inp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ const struct dcmipp_inp_pix_map *vpix =
+ dcmipp_inp_pix_map_by_index(code->index, code->pad);
+
+ if (!vpix)
+ return -EINVAL;
+
+ code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink;
+
+ return 0;
+}
+
+static int dcmipp_inp_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct dcmipp_inp_pix_map *vpix;
+
+ if (fse->index)
+ return -EINVAL;
+
+ /* Only accept code in the pix map table */
+ vpix = dcmipp_inp_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0,
+ IS_SRC(fse->pad) ? fse->code : 0);
+ if (!vpix)
+ return -EINVAL;
+
+ fse->min_width = DCMIPP_FRAME_MIN_WIDTH;
+ fse->max_width = DCMIPP_FRAME_MAX_WIDTH;
+ fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;
+ fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;
+
+ return 0;
+}
+
+static void dcmipp_inp_adjust_fmt(struct dcmipp_inp_device *inp,
+ struct v4l2_mbus_framefmt *fmt, __u32 pad)
+{
+ const struct dcmipp_inp_pix_map *vpix;
+
+ /* Only accept code in the pix map table */
+ vpix = dcmipp_inp_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0,
+ IS_SRC(pad) ? fmt->code : 0);
+ if (!vpix)
+ fmt->code = fmt_default.code;
+
+ /* Exclude JPEG if BT656 bus is selected */
+ if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 &&
+ inp->ved.bus_type == V4L2_MBUS_BT656)
+ fmt->code = fmt_default.code;
+
+ fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,
+ DCMIPP_FRAME_MAX_WIDTH) & ~1;
+ fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,
+ DCMIPP_FRAME_MAX_HEIGHT) & ~1;
+
+ if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
+ fmt->field = fmt_default.field;
+
+ dcmipp_colorimetry_clamp(fmt);
+}
+
+static int dcmipp_inp_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct dcmipp_inp_device *inp = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *mf;
+
+ if (v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
+
+ /* Set the new format */
+ dcmipp_inp_adjust_fmt(inp, &fmt->format, fmt->pad);
+
+ dev_dbg(inp->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n",
+ inp->sd.name,
+ /* old */
+ mf->width, mf->height, mf->code,
+ mf->colorspace, mf->quantization,
+ mf->xfer_func, mf->ycbcr_enc,
+ /* new */
+ fmt->format.width, fmt->format.height, fmt->format.code,
+ fmt->format.colorspace, fmt->format.quantization,
+ fmt->format.xfer_func, fmt->format.ycbcr_enc);
+
+ *mf = fmt->format;
+
+ /* When setting the sink format, report that format on the src pad */
+ if (IS_SINK(fmt->pad)) {
+ mf = v4l2_subdev_state_get_format(sd_state, 1);
+ *mf = fmt->format;
+ dcmipp_inp_adjust_fmt(inp, mf, 1);
+ }
+
+ return 0;
+}
+
+static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp,
+ struct v4l2_subdev_state *state)
+{
+ u32 val = 0;
+ const struct dcmipp_inp_pix_map *vpix;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ /* Set vertical synchronization polarity */
+ if (inp->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
+ val |= DCMIPP_PRCR_VSPOL;
+
+ /* Set horizontal synchronization polarity */
+ if (inp->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
+ val |= DCMIPP_PRCR_HSPOL;
+
+ /* Set pixel clock polarity */
+ if (inp->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ val |= DCMIPP_PRCR_PCKPOL;
+
+ /*
+ * BT656 embedded synchronisation bus mode.
+ *
+ * Default SAV/EAV mode is supported here with default codes
+ * SAV=0xff000080 & EAV=0xff00009d.
+ * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d.
+ */
+ if (inp->ved.bus_type == V4L2_MBUS_BT656) {
+ val |= DCMIPP_PRCR_ESS;
+
+ /* Unmask all codes */
+ reg_write(inp, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */
+
+ /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */
+ reg_write(inp, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */
+ }
+
+ /* Set format */
+ sink_fmt = v4l2_subdev_state_get_format(state, 0);
+ src_fmt = v4l2_subdev_state_get_format(state, 1);
+
+ vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code);
+ if (!vpix) {
+ dev_err(inp->dev, "Invalid sink/src format configuration\n");
+ return -EINVAL;
+ }
+
+ val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT;
+
+ /* swap cycles */
+ if (vpix->prcr_swapcycles)
+ val |= DCMIPP_PRCR_SWAPCYCLES;
+
+ reg_write(inp, DCMIPP_PRCR, val);
+
+ /* Select the DCMIPP parallel interface */
+ reg_write(inp, DCMIPP_CMCR, 0);
+
+ /* Enable parallel interface */
+ reg_set(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE);
+
+ return 0;
+}
+
+static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp,
+ struct v4l2_subdev_state *state)
+{
+ const struct dcmipp_inp_pix_map *vpix;
+ struct v4l2_mbus_framefmt *sink_fmt;
+ struct v4l2_mbus_framefmt *src_fmt;
+
+ /* Get format information */
+ sink_fmt = v4l2_subdev_state_get_format(state, 0);
+ src_fmt = v4l2_subdev_state_get_format(state, 1);
+
+ vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code);
+ if (!vpix) {
+ dev_err(inp->dev, "Invalid sink/src format configuration\n");
+ return -EINVAL;
+ }
+
+ /* Apply configuration on each input pipe */
+ reg_clear(inp, DCMIPP_P0FSCR,
+ DCMIPP_P0FSCR_DTMODE_MASK | DCMIPP_P0FSCR_DTIDA_MASK);
+
+ /* In case of JPEG we don't know the DT so we allow all data */
+ /*
+ * TODO - check instead dt == 0 for the time being to allow other
+ * unknown data-type
+ */
+ if (!vpix->dt)
+ reg_set(inp, DCMIPP_P0FSCR,
+ DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_P0FSCR_DTMODE_SHIFT);
+ else
+ reg_set(inp, DCMIPP_P0FSCR,
+ vpix->dt << DCMIPP_P0FSCR_DTIDA_SHIFT |
+ DCMIPP_P0FSCR_DTMODE_DTIDA);
+
+ /* Select the DCMIPP CSI interface */
+ reg_write(inp, DCMIPP_CMCR, DCMIPP_CMCR_INSEL);
+
+ return 0;
+}
+
+static int dcmipp_inp_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_inp_device *inp =
+ container_of(sd, struct dcmipp_inp_device, sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret = 0;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ if (inp->ved.bus_type == V4L2_MBUS_PARALLEL ||
+ inp->ved.bus_type == V4L2_MBUS_BT656)
+ ret = dcmipp_inp_configure_parallel(inp, state);
+ else if (inp->ved.bus_type == V4L2_MBUS_CSI2_DPHY)
+ ret = dcmipp_inp_configure_csi(inp, state);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(inp->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dcmipp_inp_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_inp_device *inp =
+ container_of(sd, struct dcmipp_inp_device, sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(inp->dev,
+ "failed to stop source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ if (inp->ved.bus_type == V4L2_MBUS_PARALLEL ||
+ inp->ved.bus_type == V4L2_MBUS_BT656) {
+ /* Disable parallel interface */
+ reg_clear(inp, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops dcmipp_inp_pad_ops = {
+ .enum_mbus_code = dcmipp_inp_enum_mbus_code,
+ .enum_frame_size = dcmipp_inp_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = dcmipp_inp_set_fmt,
+ .enable_streams = dcmipp_inp_enable_streams,
+ .disable_streams = dcmipp_inp_disable_streams,
+};
+
+static const struct v4l2_subdev_video_ops dcmipp_inp_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_ops dcmipp_inp_ops = {
+ .pad = &dcmipp_inp_pad_ops,
+ .video = &dcmipp_inp_video_ops,
+};
+
+static void dcmipp_inp_release(struct v4l2_subdev *sd)
+{
+ struct dcmipp_inp_device *inp =
+ container_of(sd, struct dcmipp_inp_device, sd);
+
+ kfree(inp);
+}
+
+static const struct v4l2_subdev_internal_ops dcmipp_inp_int_ops = {
+ .init_state = dcmipp_inp_init_state,
+ .release = dcmipp_inp_release,
+};
+
+void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved)
+{
+ struct dcmipp_inp_device *inp =
+ container_of(ved, struct dcmipp_inp_device, ved);
+
+ dcmipp_ent_sd_unregister(ved, &inp->sd);
+}
+
+struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
+ const char *entity_name,
+ struct v4l2_device *v4l2_dev,
+ void __iomem *regs)
+{
+ struct dcmipp_inp_device *inp;
+ const unsigned long pads_flag[] = {
+ MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ };
+ int ret;
+
+ /* Allocate the inp struct */
+ inp = kzalloc(sizeof(*inp), GFP_KERNEL);
+ if (!inp)
+ return ERR_PTR(-ENOMEM);
+
+ inp->regs = regs;
+
+ /* Initialize ved and sd */
+ ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, v4l2_dev,
+ entity_name, MEDIA_ENT_F_VID_IF_BRIDGE,
+ ARRAY_SIZE(pads_flag), pads_flag,
+ &dcmipp_inp_int_ops, &dcmipp_inp_ops,
+ NULL, NULL);
+ if (ret) {
+ kfree(inp);
+ return ERR_PTR(ret);
+ }
+
+ inp->dev = dev;
+
+ return &inp->ved;
+}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c
deleted file mode 100644
index 62c5c3331cfe..000000000000
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-parallel.c
+++ /dev/null
@@ -1,440 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Driver for STM32 Digital Camera Memory Interface Pixel Processor
- *
- * Copyright (C) STMicroelectronics SA 2023
- * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
- * Alain Volmat <alain.volmat@foss.st.com>
- * for STMicroelectronics.
- */
-
-#include <linux/v4l2-mediabus.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-subdev.h>
-
-#include "dcmipp-common.h"
-
-#define DCMIPP_PRCR 0x104
-#define DCMIPP_PRCR_FORMAT_SHIFT 16
-#define DCMIPP_PRCR_FORMAT_YUV422 0x1e
-#define DCMIPP_PRCR_FORMAT_RGB565 0x22
-#define DCMIPP_PRCR_FORMAT_RAW8 0x2a
-#define DCMIPP_PRCR_FORMAT_G8 0x4a
-#define DCMIPP_PRCR_FORMAT_BYTE_STREAM 0x5a
-#define DCMIPP_PRCR_ESS BIT(4)
-#define DCMIPP_PRCR_PCKPOL BIT(5)
-#define DCMIPP_PRCR_HSPOL BIT(6)
-#define DCMIPP_PRCR_VSPOL BIT(7)
-#define DCMIPP_PRCR_ENABLE BIT(14)
-#define DCMIPP_PRCR_SWAPCYCLES BIT(25)
-
-#define DCMIPP_PRESCR 0x108
-#define DCMIPP_PRESUR 0x10c
-
-#define IS_SINK(pad) (!(pad))
-#define IS_SRC(pad) ((pad))
-
-struct dcmipp_par_pix_map {
- unsigned int code_sink;
- unsigned int code_src;
- u8 prcr_format;
- u8 prcr_swapcycles;
-};
-
-#define PIXMAP_SINK_SRC_PRCR_SWAP(sink, src, prcr, swap) \
- { \
- .code_sink = MEDIA_BUS_FMT_##sink, \
- .code_src = MEDIA_BUS_FMT_##src, \
- .prcr_format = DCMIPP_PRCR_FORMAT_##prcr, \
- .prcr_swapcycles = swap, \
- }
-static const struct dcmipp_par_pix_map dcmipp_par_pix_map_list[] = {
- /* RGB565 */
- PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_LE, RGB565_2X8_LE, RGB565, 1),
- PIXMAP_SINK_SRC_PRCR_SWAP(RGB565_2X8_BE, RGB565_2X8_LE, RGB565, 0),
- /* YUV422 */
- PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1),
- PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0),
- PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1),
- PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0),
- PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1),
- PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1),
- /* GREY */
- PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0),
- /* Raw Bayer */
- PIXMAP_SINK_SRC_PRCR_SWAP(SBGGR8_1X8, SBGGR8_1X8, RAW8, 0),
- PIXMAP_SINK_SRC_PRCR_SWAP(SGBRG8_1X8, SGBRG8_1X8, RAW8, 0),
- PIXMAP_SINK_SRC_PRCR_SWAP(SGRBG8_1X8, SGRBG8_1X8, RAW8, 0),
- PIXMAP_SINK_SRC_PRCR_SWAP(SRGGB8_1X8, SRGGB8_1X8, RAW8, 0),
- /* JPEG */
- PIXMAP_SINK_SRC_PRCR_SWAP(JPEG_1X8, JPEG_1X8, BYTE_STREAM, 0),
-};
-
-/*
- * Search through the pix_map table, skipping two consecutive entry with the
- * same code
- */
-static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_index
- (unsigned int index,
- unsigned int pad)
-{
- unsigned int i = 0;
- u32 prev_code = 0, cur_code;
-
- while (i < ARRAY_SIZE(dcmipp_par_pix_map_list)) {
- if (IS_SRC(pad))
- cur_code = dcmipp_par_pix_map_list[i].code_src;
- else
- cur_code = dcmipp_par_pix_map_list[i].code_sink;
-
- if (cur_code == prev_code) {
- i++;
- continue;
- }
- prev_code = cur_code;
-
- if (index == 0)
- break;
- i++;
- index--;
- }
-
- if (i >= ARRAY_SIZE(dcmipp_par_pix_map_list))
- return NULL;
-
- return &dcmipp_par_pix_map_list[i];
-}
-
-static inline const struct dcmipp_par_pix_map *dcmipp_par_pix_map_by_code
- (u32 code_sink, u32 code_src)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(dcmipp_par_pix_map_list); i++) {
- if ((dcmipp_par_pix_map_list[i].code_sink == code_sink &&
- dcmipp_par_pix_map_list[i].code_src == code_src) ||
- (dcmipp_par_pix_map_list[i].code_sink == code_src &&
- dcmipp_par_pix_map_list[i].code_src == code_sink) ||
- (dcmipp_par_pix_map_list[i].code_sink == code_sink &&
- code_src == 0) ||
- (code_sink == 0 &&
- dcmipp_par_pix_map_list[i].code_src == code_src))
- return &dcmipp_par_pix_map_list[i];
- }
- return NULL;
-}
-
-struct dcmipp_par_device {
- struct dcmipp_ent_device ved;
- struct v4l2_subdev sd;
- struct device *dev;
- void __iomem *regs;
- bool streaming;
-};
-
-static const struct v4l2_mbus_framefmt fmt_default = {
- .width = DCMIPP_FMT_WIDTH_DEFAULT,
- .height = DCMIPP_FMT_HEIGHT_DEFAULT,
- .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .field = V4L2_FIELD_NONE,
- .colorspace = DCMIPP_COLORSPACE_DEFAULT,
- .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,
- .quantization = DCMIPP_QUANTIZATION_DEFAULT,
- .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
-};
-
-static int dcmipp_par_init_state(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state)
-{
- unsigned int i;
-
- for (i = 0; i < sd->entity.num_pads; i++) {
- struct v4l2_mbus_framefmt *mf;
-
- mf = v4l2_subdev_state_get_format(sd_state, i);
- *mf = fmt_default;
- }
-
- return 0;
-}
-
-static int dcmipp_par_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_mbus_code_enum *code)
-{
- const struct dcmipp_par_pix_map *vpix =
- dcmipp_par_pix_map_by_index(code->index, code->pad);
-
- if (!vpix)
- return -EINVAL;
-
- code->code = IS_SRC(code->pad) ? vpix->code_src : vpix->code_sink;
-
- return 0;
-}
-
-static int dcmipp_par_enum_frame_size(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_size_enum *fse)
-{
- const struct dcmipp_par_pix_map *vpix;
-
- if (fse->index)
- return -EINVAL;
-
- /* Only accept code in the pix map table */
- vpix = dcmipp_par_pix_map_by_code(IS_SINK(fse->pad) ? fse->code : 0,
- IS_SRC(fse->pad) ? fse->code : 0);
- if (!vpix)
- return -EINVAL;
-
- fse->min_width = DCMIPP_FRAME_MIN_WIDTH;
- fse->max_width = DCMIPP_FRAME_MAX_WIDTH;
- fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;
- fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;
-
- return 0;
-}
-
-static void dcmipp_par_adjust_fmt(struct dcmipp_par_device *par,
- struct v4l2_mbus_framefmt *fmt, __u32 pad)
-{
- const struct dcmipp_par_pix_map *vpix;
-
- /* Only accept code in the pix map table */
- vpix = dcmipp_par_pix_map_by_code(IS_SINK(pad) ? fmt->code : 0,
- IS_SRC(pad) ? fmt->code : 0);
- if (!vpix)
- fmt->code = fmt_default.code;
-
- /* Exclude JPEG if BT656 bus is selected */
- if (vpix && vpix->code_sink == MEDIA_BUS_FMT_JPEG_1X8 &&
- par->ved.bus_type == V4L2_MBUS_BT656)
- fmt->code = fmt_default.code;
-
- fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,
- DCMIPP_FRAME_MAX_WIDTH) & ~1;
- fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,
- DCMIPP_FRAME_MAX_HEIGHT) & ~1;
-
- if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
- fmt->field = fmt_default.field;
-
- dcmipp_colorimetry_clamp(fmt);
-}
-
-static int dcmipp_par_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct dcmipp_par_device *par = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *mf;
-
- if (par->streaming)
- return -EBUSY;
-
- mf = v4l2_subdev_state_get_format(sd_state, fmt->pad);
-
- /* Set the new format */
- dcmipp_par_adjust_fmt(par, &fmt->format, fmt->pad);
-
- dev_dbg(par->dev, "%s: format update: old:%dx%d (0x%x, %d, %d, %d, %d) new:%dx%d (0x%x, %d, %d, %d, %d)\n",
- par->sd.name,
- /* old */
- mf->width, mf->height, mf->code,
- mf->colorspace, mf->quantization,
- mf->xfer_func, mf->ycbcr_enc,
- /* new */
- fmt->format.width, fmt->format.height, fmt->format.code,
- fmt->format.colorspace, fmt->format.quantization,
- fmt->format.xfer_func, fmt->format.ycbcr_enc);
-
- *mf = fmt->format;
-
- /* When setting the sink format, report that format on the src pad */
- if (IS_SINK(fmt->pad)) {
- mf = v4l2_subdev_state_get_format(sd_state, 1);
- *mf = fmt->format;
- dcmipp_par_adjust_fmt(par, mf, 1);
- }
-
- return 0;
-}
-
-static const struct v4l2_subdev_pad_ops dcmipp_par_pad_ops = {
- .enum_mbus_code = dcmipp_par_enum_mbus_code,
- .enum_frame_size = dcmipp_par_enum_frame_size,
- .get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = dcmipp_par_set_fmt,
-};
-
-static int dcmipp_par_configure(struct dcmipp_par_device *par)
-{
- u32 val = 0;
- const struct dcmipp_par_pix_map *vpix;
- struct v4l2_subdev_state *state;
- struct v4l2_mbus_framefmt *sink_fmt;
- struct v4l2_mbus_framefmt *src_fmt;
-
- /* Set vertical synchronization polarity */
- if (par->ved.bus.flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH)
- val |= DCMIPP_PRCR_VSPOL;
-
- /* Set horizontal synchronization polarity */
- if (par->ved.bus.flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH)
- val |= DCMIPP_PRCR_HSPOL;
-
- /* Set pixel clock polarity */
- if (par->ved.bus.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
- val |= DCMIPP_PRCR_PCKPOL;
-
- /*
- * BT656 embedded synchronisation bus mode.
- *
- * Default SAV/EAV mode is supported here with default codes
- * SAV=0xff000080 & EAV=0xff00009d.
- * With DCMIPP this means LSC=SAV=0x80 & LEC=EAV=0x9d.
- */
- if (par->ved.bus_type == V4L2_MBUS_BT656) {
- val |= DCMIPP_PRCR_ESS;
-
- /* Unmask all codes */
- reg_write(par, DCMIPP_PRESUR, 0xffffffff);/* FEC:LEC:LSC:FSC */
-
- /* Trig on LSC=0x80 & LEC=0x9d codes, ignore FSC and FEC */
- reg_write(par, DCMIPP_PRESCR, 0xff9d80ff);/* FEC:LEC:LSC:FSC */
- }
-
- /* Set format */
- state = v4l2_subdev_lock_and_get_active_state(&par->sd);
- sink_fmt = v4l2_subdev_state_get_format(state, 0);
- src_fmt = v4l2_subdev_state_get_format(state, 1);
- v4l2_subdev_unlock_state(state);
-
- vpix = dcmipp_par_pix_map_by_code(sink_fmt->code, src_fmt->code);
- if (!vpix) {
- dev_err(par->dev, "Invalid sink/src format configuration\n");
- return -EINVAL;
- }
-
- val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT;
-
- /* swap cycles */
- if (vpix->prcr_swapcycles)
- val |= DCMIPP_PRCR_SWAPCYCLES;
-
- reg_write(par, DCMIPP_PRCR, val);
-
- return 0;
-}
-
-static int dcmipp_par_s_stream(struct v4l2_subdev *sd, int enable)
-{
- struct dcmipp_par_device *par =
- container_of(sd, struct dcmipp_par_device, sd);
- struct v4l2_subdev *s_subdev;
- struct media_pad *pad;
- int ret = 0;
-
- /* Get source subdev */
- pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
- if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
- return -EINVAL;
- s_subdev = media_entity_to_v4l2_subdev(pad->entity);
-
- if (enable) {
- ret = dcmipp_par_configure(par);
- if (ret)
- return ret;
-
- /* Enable parallel interface */
- reg_set(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE);
-
- ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);
- if (ret < 0) {
- dev_err(par->dev,
- "failed to start source subdev streaming (%d)\n",
- ret);
- return ret;
- }
- } else {
- ret = v4l2_subdev_call(s_subdev, video, s_stream, enable);
- if (ret < 0) {
- dev_err(par->dev,
- "failed to stop source subdev streaming (%d)\n",
- ret);
- return ret;
- }
-
- /* Disable parallel interface */
- reg_clear(par, DCMIPP_PRCR, DCMIPP_PRCR_ENABLE);
- }
-
- par->streaming = enable;
-
- return ret;
-}
-
-static const struct v4l2_subdev_video_ops dcmipp_par_video_ops = {
- .s_stream = dcmipp_par_s_stream,
-};
-
-static const struct v4l2_subdev_ops dcmipp_par_ops = {
- .pad = &dcmipp_par_pad_ops,
- .video = &dcmipp_par_video_ops,
-};
-
-static void dcmipp_par_release(struct v4l2_subdev *sd)
-{
- struct dcmipp_par_device *par =
- container_of(sd, struct dcmipp_par_device, sd);
-
- kfree(par);
-}
-
-static const struct v4l2_subdev_internal_ops dcmipp_par_int_ops = {
- .init_state = dcmipp_par_init_state,
- .release = dcmipp_par_release,
-};
-
-void dcmipp_par_ent_release(struct dcmipp_ent_device *ved)
-{
- struct dcmipp_par_device *par =
- container_of(ved, struct dcmipp_par_device, ved);
-
- dcmipp_ent_sd_unregister(ved, &par->sd);
-}
-
-struct dcmipp_ent_device *dcmipp_par_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs)
-{
- struct dcmipp_par_device *par;
- const unsigned long pads_flag[] = {
- MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
- };
- int ret;
-
- /* Allocate the par struct */
- par = kzalloc(sizeof(*par), GFP_KERNEL);
- if (!par)
- return ERR_PTR(-ENOMEM);
-
- par->regs = regs;
-
- /* Initialize ved and sd */
- ret = dcmipp_ent_sd_register(&par->ved, &par->sd, v4l2_dev,
- entity_name, MEDIA_ENT_F_VID_IF_BRIDGE,
- ARRAY_SIZE(pads_flag), pads_flag,
- &dcmipp_par_int_ops, &dcmipp_par_ops,
- NULL, NULL);
- if (ret) {
- kfree(par);
- return ERR_PTR(ret);
- }
-
- par->dev = dev;
-
- return &par->ved;
-}
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
index 097a3a08ef7d..e53a07b770b7 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_csi.c
@@ -35,7 +35,18 @@ struct sun4i_csi_traits {
bool has_isp;
};
+static int sun4i_csi_video_link_validate(struct media_link *link)
+{
+ dev_warn_once(link->graph_obj.mdev->dev,
+ "Driver bug: link validation not implemented\n");
+ return 0;
+}
+
static const struct media_entity_operations sun4i_csi_video_entity_ops = {
+ .link_validate = sun4i_csi_video_link_validate,
+};
+
+static const struct media_entity_operations sun4i_csi_subdev_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
@@ -214,6 +225,7 @@ static int sun4i_csi_probe(struct platform_device *pdev)
subdev->internal_ops = &sun4i_csi_subdev_internal_ops;
subdev->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ subdev->entity.ops = &sun4i_csi_subdev_entity_ops;
subdev->owner = THIS_MODULE;
snprintf(subdev->name, sizeof(subdev->name), "sun4i-csi-0");
v4l2_set_subdevdata(subdev, csi);
@@ -328,7 +340,7 @@ static const struct dev_pm_ops sun4i_csi_pm_ops = {
static struct platform_driver sun4i_csi_driver = {
.probe = sun4i_csi_probe,
- .remove_new = sun4i_csi_remove,
+ .remove = sun4i_csi_remove,
.driver = {
.name = "sun4i-csi",
.of_match_table = sun4i_csi_of_match,
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
index d1371e130113..e911c7f7acc5 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
@@ -371,8 +371,6 @@ static const struct vb2_ops sun4i_csi_qops = {
.buf_queue = sun4i_csi_buffer_queue,
.start_streaming = sun4i_csi_start_streaming,
.stop_streaming = sun4i_csi_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static irqreturn_t sun4i_csi_irq(int irq, void *data)
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
index c6ba385c0c86..af2a32c226a5 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi.c
@@ -423,7 +423,7 @@ MODULE_DEVICE_TABLE(of, sun6i_csi_of_match);
static struct platform_driver sun6i_csi_platform_driver = {
.probe = sun6i_csi_probe,
- .remove_new = sun6i_csi_remove,
+ .remove = sun6i_csi_remove,
.driver = {
.name = SUN6I_CSI_NAME,
.of_match_table = sun6i_csi_of_match,
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index 14c0dc827c52..76356bc7f10e 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -657,8 +657,6 @@ static const struct vb2_ops sun6i_csi_capture_queue_ops = {
.buf_queue = sun6i_csi_capture_buffer_queue,
.start_streaming = sun6i_csi_capture_start_streaming,
.stop_streaming = sun6i_csi_capture_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/* V4L2 Device */
diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
index f9d4dc45b490..b06cb73015cd 100644
--- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
+++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
@@ -757,7 +757,7 @@ MODULE_DEVICE_TABLE(of, sun6i_mipi_csi2_of_match);
static struct platform_driver sun6i_mipi_csi2_platform_driver = {
.probe = sun6i_mipi_csi2_probe,
- .remove_new = sun6i_mipi_csi2_remove,
+ .remove = sun6i_mipi_csi2_remove,
.driver = {
.name = SUN6I_MIPI_CSI2_NAME,
.of_match_table = sun6i_mipi_csi2_of_match,
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
index 47a8c0fb7eb9..99c401e653bc 100644
--- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/Kconfig
@@ -8,6 +8,7 @@ config VIDEO_SUN8I_A83T_MIPI_CSI2
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
select REGMAP_MMIO
+ select GENERIC_PHY
select GENERIC_PHY_MIPI_DPHY
help
Support for the Allwinner A83T MIPI CSI-2 controller and D-PHY.
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
index 4a5698eb12b7..dbc51daa4fe3 100644
--- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
@@ -824,7 +824,7 @@ MODULE_DEVICE_TABLE(of, sun8i_a83t_mipi_csi2_of_match);
static struct platform_driver sun8i_a83t_mipi_csi2_platform_driver = {
.probe = sun8i_a83t_mipi_csi2_probe,
- .remove_new = sun8i_a83t_mipi_csi2_remove,
+ .remove = sun8i_a83t_mipi_csi2_remove,
.driver = {
.name = SUN8I_A83T_MIPI_CSI2_NAME,
.of_match_table = sun8i_a83t_mipi_csi2_of_match,
diff --git a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
index a1c35a2b68ed..3e7f2df70408 100644
--- a/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
+++ b/drivers/media/platform/sunxi/sun8i-di/sun8i-di.c
@@ -659,8 +659,6 @@ static const struct vb2_ops deinterlace_qops = {
.buf_queue = deinterlace_buf_queue,
.start_streaming = deinterlace_start_streaming,
.stop_streaming = deinterlace_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int deinterlace_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -1001,7 +999,7 @@ static const struct dev_pm_ops deinterlace_pm_ops = {
static struct platform_driver deinterlace_driver = {
.probe = deinterlace_probe,
- .remove_new = deinterlace_remove,
+ .remove = deinterlace_remove,
.driver = {
.name = DEINTERLACE_NAME,
.of_match_table = deinterlace_dt_match,
diff --git a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
index a12323ca89fa..abd10b218aa1 100644
--- a/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
+++ b/drivers/media/platform/sunxi/sun8i-rotate/sun8i_rotate.c
@@ -522,8 +522,6 @@ static const struct vb2_ops rotate_qops = {
.buf_queue = rotate_buf_queue,
.start_streaming = rotate_start_streaming,
.stop_streaming = rotate_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int rotate_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -905,7 +903,7 @@ static const struct dev_pm_ops rotate_pm_ops = {
static struct platform_driver rotate_driver = {
.probe = rotate_probe,
- .remove_new = rotate_remove,
+ .remove = rotate_remove,
.driver = {
.name = ROTATE_NAME,
.of_match_table = rotate_dt_match,
diff --git a/drivers/media/platform/synopsys/Kconfig b/drivers/media/platform/synopsys/Kconfig
new file mode 100644
index 000000000000..4fd521f78425
--- /dev/null
+++ b/drivers/media/platform/synopsys/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+source "drivers/media/platform/synopsys/hdmirx/Kconfig"
diff --git a/drivers/media/platform/synopsys/Makefile b/drivers/media/platform/synopsys/Makefile
new file mode 100644
index 000000000000..3b12c574dd67
--- /dev/null
+++ b/drivers/media/platform/synopsys/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += hdmirx/
diff --git a/drivers/media/platform/synopsys/hdmirx/Kconfig b/drivers/media/platform/synopsys/hdmirx/Kconfig
new file mode 100644
index 000000000000..4321f985f632
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/Kconfig
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config VIDEO_SYNOPSYS_HDMIRX
+ tristate "Synopsys DesignWare HDMI Receiver driver"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select VIDEOBUF2_DMA_CONTIG
+ select CEC_CORE
+ select HDMI
+ help
+ Support for Synopsys HDMI HDMI RX Controller.
+ This driver supports HDMI 2.0 version.
+
+ To compile this driver as a module, choose M here. The module
+ will be called synopsys_hdmirx.
+
+config VIDEO_SYNOPSYS_HDMIRX_LOAD_DEFAULT_EDID
+ bool "Load default EDID"
+ depends on VIDEO_SYNOPSYS_HDMIRX
+ help
+ Preload default EDID (Extended Display Identification Data)
+ branded by Linux Foundation that exposes display modes up
+ to 4k@30Hz, which have best compatibility with HDMI transmitters.
+
+ Enabling this option is recommended for a non-production use-cases.
+ It will make driver usable out-of-the-box.
+
+ For a higher display modes you will need to load customized EDID
+ from userspace using v4l2-ctl tool or by other means.
+
+ Without enabling this option driver will be practically
+ non-functional until EDID will be loaded from userspace.
+ Which is a wanted behavior when using this driver in a
+ commercial product that should utilize own branded EDID.
diff --git a/drivers/media/platform/synopsys/hdmirx/Makefile b/drivers/media/platform/synopsys/hdmirx/Makefile
new file mode 100644
index 000000000000..2fa2d9e25300
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+synopsys-hdmirx-objs := snps_hdmirx.o snps_hdmirx_cec.o
+
+obj-$(CONFIG_VIDEO_SYNOPSYS_HDMIRX) += synopsys-hdmirx.o
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
new file mode 100644
index 000000000000..7af6765532e3
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -0,0 +1,2746 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Collabora, Ltd.
+ * Author: Shreeya Patel <shreeya.patel@collabora.com>
+ * Author: Dmitry Osipenko <dmitry.osipenko@collabora.com>
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ * Author: Dingxian Wen <shawn.wen@rock-chips.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/workqueue.h>
+
+#include <media/cec.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "snps_hdmirx.h"
+#include "snps_hdmirx_cec.h"
+
+#define EDID_NUM_BLOCKS_MAX 4
+#define EDID_BLOCK_SIZE 128
+#define HDMIRX_PLANE_Y 0
+#define HDMIRX_PLANE_CBCR 1
+#define FILTER_FRAME_CNT 6
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-3)");
+
+enum hdmirx_pix_fmt {
+ HDMIRX_RGB888 = 0,
+ HDMIRX_YUV422 = 1,
+ HDMIRX_YUV444 = 2,
+ HDMIRX_YUV420 = 3,
+};
+
+enum ddr_store_fmt {
+ STORE_RGB888 = 0,
+ STORE_RGBA_ARGB,
+ STORE_YUV420_8BIT,
+ STORE_YUV420_10BIT,
+ STORE_YUV422_8BIT,
+ STORE_YUV422_10BIT,
+ STORE_YUV444_8BIT,
+ STORE_YUV420_16BIT = 8,
+ STORE_YUV422_16BIT = 9,
+};
+
+enum hdmirx_reg_attr {
+ HDMIRX_ATTR_RW = 0,
+ HDMIRX_ATTR_RO = 1,
+ HDMIRX_ATTR_WO = 2,
+ HDMIRX_ATTR_RE = 3,
+};
+
+enum {
+ HDMIRX_RST_A,
+ HDMIRX_RST_P,
+ HDMIRX_RST_REF,
+ HDMIRX_RST_BIU,
+ HDMIRX_NUM_RST,
+};
+
+static const char *const pix_fmt_str[] = {
+ "RGB888",
+ "YUV422",
+ "YUV444",
+ "YUV420",
+};
+
+struct hdmirx_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head queue;
+ u32 buff_addr[VIDEO_MAX_PLANES];
+};
+
+struct hdmirx_stream {
+ struct snps_hdmirx_dev *hdmirx_dev;
+ struct video_device vdev;
+ struct vb2_queue buf_queue;
+ struct list_head buf_head;
+ struct hdmirx_buffer *curr_buf;
+ struct hdmirx_buffer *next_buf;
+ struct v4l2_pix_format_mplane pixm;
+ const struct v4l2_format_info *out_finfo;
+ struct mutex vlock; /* to lock resources associated with video buffer and video device */
+ spinlock_t vbq_lock; /* to lock video buffer queue */
+ bool stopping;
+ wait_queue_head_t wq_stopped;
+ u32 sequence;
+ u32 line_flag_int_cnt;
+ u32 irq_stat;
+};
+
+struct snps_hdmirx_dev {
+ struct device *dev;
+ struct hdmirx_stream stream;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *detect_tx_5v_ctrl;
+ struct v4l2_ctrl *rgb_range;
+ struct v4l2_ctrl *content_type;
+ struct v4l2_dv_timings timings;
+ struct gpio_desc *detect_5v_gpio;
+ struct delayed_work delayed_work_hotplug;
+ struct delayed_work delayed_work_res_change;
+ struct hdmirx_cec *cec;
+ struct mutex stream_lock; /* to lock video stream capture */
+ struct mutex work_lock; /* to lock the critical section of hotplug event */
+ struct reset_control_bulk_data resets[HDMIRX_NUM_RST];
+ struct clk_bulk_data *clks;
+ struct regmap *grf;
+ struct regmap *vo1_grf;
+ struct completion cr_write_done;
+ struct completion timer_base_lock;
+ struct completion avi_pkt_rcv;
+ struct dentry *debugfs_dir;
+ struct v4l2_debugfs_if *infoframes;
+ enum hdmirx_pix_fmt pix_fmt;
+ void __iomem *regs;
+ int hdmi_irq;
+ int dma_irq;
+ int det_irq;
+ bool hpd_trigger_level_high;
+ bool tmds_clk_ratio;
+ bool plugged;
+ int num_clks;
+ u32 edid_blocks_written;
+ u32 cur_fmt_fourcc;
+ u32 color_depth;
+ spinlock_t rst_lock; /* to lock register access */
+ u8 edid[EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE];
+};
+
+static const struct v4l2_dv_timings cea640x480 = V4L2_DV_BT_CEA_640X480P59_94;
+
+static const struct v4l2_dv_timings_cap hdmirx_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ .reserved = { 0 },
+ V4L2_INIT_BT_TIMINGS(640, 4096, /* min/max width */
+ 480, 2160, /* min/max height */
+ 20000000, 600000000, /* min/max pixelclock */
+ /* standards */
+ V4L2_DV_BT_STD_CEA861,
+ /* capabilities */
+ V4L2_DV_BT_CAP_PROGRESSIVE |
+ V4L2_DV_BT_CAP_INTERLACED)
+};
+
+static void hdmirx_writel(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val)
+{
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ writel(val, hdmirx_dev->regs + reg);
+}
+
+static u32 hdmirx_readl(struct snps_hdmirx_dev *hdmirx_dev, int reg)
+{
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ return readl(hdmirx_dev->regs + reg);
+}
+
+static void hdmirx_reset_dma(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ reset_control_reset(hdmirx_dev->resets[0].rstc);
+}
+
+static void hdmirx_update_bits(struct snps_hdmirx_dev *hdmirx_dev, int reg,
+ u32 mask, u32 data)
+{
+ u32 val;
+
+ guard(spinlock_irqsave)(&hdmirx_dev->rst_lock);
+
+ val = readl(hdmirx_dev->regs + reg) & ~mask;
+ val |= (data & mask);
+ writel(val, hdmirx_dev->regs + reg);
+}
+
+static int hdmirx_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static bool tx_5v_power_present(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ const unsigned int detection_threshold = 7;
+ int val, i, cnt = 0;
+ bool ret;
+
+ for (i = 0; i < 10; i++) {
+ usleep_range(1000, 1100);
+ val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
+ if (val > 0)
+ cnt++;
+ if (cnt >= detection_threshold)
+ break;
+ }
+
+ ret = (cnt >= detection_threshold) ? true : false;
+ v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: %d\n", __func__, ret);
+
+ return ret;
+}
+
+static bool signal_not_lock(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ u32 mu_status, dma_st10, cmu_st;
+
+ mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
+ dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
+ cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
+
+ if ((mu_status & TMDSVALID_STABLE_ST) &&
+ (dma_st10 & HDMIRX_LOCK) &&
+ (cmu_st & TMDSQPCLK_LOCKED_ST))
+ return false;
+
+ return true;
+}
+
+static void hdmirx_get_timings(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_bt_timings *bt)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 hact, vact, htotal, vtotal, fps;
+ u32 hfp, hs, hbp, vfp, vs, vbp;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS2);
+ hact = (val >> 16) & 0xffff;
+ vact = val & 0xffff;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS3);
+ htotal = (val >> 16) & 0xffff;
+ vtotal = val & 0xffff;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS4);
+ hs = (val >> 16) & 0xffff;
+ vs = val & 0xffff;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS5);
+ hbp = (val >> 16) & 0xffff;
+ vbp = val & 0xffff;
+
+ if (hdmirx_dev->pix_fmt == HDMIRX_YUV420) {
+ htotal *= 2;
+ hbp *= 2;
+ hs *= 2;
+ }
+
+ hfp = htotal - hact - hs - hbp;
+ vfp = vtotal - vact - vs - vbp;
+
+ fps = div_u64(bt->pixelclock + (htotal * vtotal) / 2, htotal * vtotal);
+ bt->width = hact;
+ bt->height = vact;
+ bt->hfrontporch = hfp;
+ bt->hsync = hs;
+ bt->hbackporch = hbp;
+ bt->vfrontporch = vfp;
+ bt->vsync = vs;
+ bt->vbackporch = vbp;
+
+ v4l2_dbg(1, debug, v4l2_dev, "get timings from dma\n");
+ v4l2_dbg(1, debug, v4l2_dev,
+ "act:%ux%u%s, total:%ux%u, fps:%u, pixclk:%llu\n",
+ bt->width, bt->height, bt->interlaced ? "i" : "p",
+ htotal, vtotal, fps, bt->pixelclock);
+
+ v4l2_dbg(2, debug, v4l2_dev,
+ "hfp:%u, hact:%u, hs:%u, hbp:%u, vfp:%u, vact:%u, vs:%u, vbp:%u\n",
+ bt->hfrontporch, hact, bt->hsync, bt->hbackporch,
+ bt->vfrontporch, vact, bt->vsync, bt->vbackporch);
+
+ if (bt->interlaced == V4L2_DV_INTERLACED) {
+ bt->height *= 2;
+ bt->il_vfrontporch = bt->vfrontporch;
+ bt->il_vsync = bt->vsync + 1;
+ bt->il_vbackporch = bt->vbackporch;
+ }
+}
+
+static bool hdmirx_check_timing_valid(struct v4l2_bt_timings *bt)
+{
+ /*
+ * Sanity-check timing values. Some of the values will be outside
+ * of a valid range till hardware becomes ready to perform capture.
+ */
+ if (bt->width < 100 || bt->width > 5000 ||
+ bt->height < 100 || bt->height > 5000)
+ return false;
+
+ if (!bt->hsync || bt->hsync > 200 ||
+ !bt->vsync || bt->vsync > 100)
+ return false;
+
+ /*
+ * According to the CEA-861, 1280x720p25 Hblank timing is up to 2680,
+ * and all standard video format timings are less than 3000.
+ */
+ if (!bt->hbackporch || bt->hbackporch > 3000 ||
+ !bt->vbackporch || bt->vbackporch > 3000)
+ return false;
+
+ if (!bt->hfrontporch || bt->hfrontporch > 3000 ||
+ !bt->vfrontporch || bt->vfrontporch > 3000)
+ return false;
+
+ return true;
+}
+
+static void hdmirx_toggle_polarity(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ u32 val = hdmirx_readl(hdmirx_dev, DMA_CONFIG6);
+
+ if (!(val & (VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN))) {
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN);
+ hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
+ VPROC_VSYNC_POL_OVR_VALUE |
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_VALUE |
+ VPROC_HSYNC_POL_OVR_EN,
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_EN);
+ return;
+ }
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6,
+ VSYNC_TOGGLE_EN | HSYNC_TOGGLE_EN, 0);
+
+ hdmirx_update_bits(hdmirx_dev, VIDEO_CONFIG2,
+ VPROC_VSYNC_POL_OVR_VALUE |
+ VPROC_VSYNC_POL_OVR_EN |
+ VPROC_HSYNC_POL_OVR_VALUE |
+ VPROC_HSYNC_POL_OVR_EN, 0);
+}
+
+/*
+ * When querying DV timings during preview, if the DMA's timing is stable,
+ * we retrieve the timings directly from the DMA. However, if the current
+ * resolution is negative, obtaining the timing from CTRL may require a
+ * change in the sync polarity, potentially leading to DMA errors.
+ */
+static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_dv_timings *timings)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_bt_timings *bt = &timings->bt;
+ u32 val, tmdsqpclk_freq, pix_clk;
+ unsigned int num_retries = 0;
+ u32 field_type, deframer_st;
+ u64 tmp_data, tmds_clk;
+ bool is_dvi_mode;
+ int ret;
+
+ mutex_lock(&hdmirx_dev->work_lock);
+retry:
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+ timings->type = V4L2_DV_BT_656_1120;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ field_type = (val & HDMIRX_TYPE_MASK) >> 7;
+
+ if (field_type & BIT(0))
+ bt->interlaced = V4L2_DV_INTERLACED;
+ else
+ bt->interlaced = V4L2_DV_PROGRESSIVE;
+
+ deframer_st = hdmirx_readl(hdmirx_dev, DEFRAMER_STATUS);
+ is_dvi_mode = !(deframer_st & OPMODE_STS_MASK);
+
+ tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
+ tmds_clk = tmdsqpclk_freq * 4 * 1000;
+ tmp_data = tmds_clk * 24;
+ do_div(tmp_data, hdmirx_dev->color_depth);
+ pix_clk = tmp_data;
+ bt->pixelclock = pix_clk;
+
+ if (hdmirx_dev->pix_fmt == HDMIRX_YUV420)
+ bt->pixelclock *= 2;
+
+ hdmirx_get_timings(hdmirx_dev, bt);
+
+ v4l2_dbg(2, debug, v4l2_dev, "tmds_clk:%llu, pix_clk:%d\n", tmds_clk, pix_clk);
+ v4l2_dbg(1, debug, v4l2_dev, "interlace:%d, fmt:%d, color:%d, mode:%s\n",
+ bt->interlaced, hdmirx_dev->pix_fmt,
+ hdmirx_dev->color_depth,
+ is_dvi_mode ? "dvi" : "hdmi");
+ v4l2_dbg(2, debug, v4l2_dev, "deframer_st:%#x\n", deframer_st);
+
+ /*
+ * Timing will be invalid until it's latched by HW or if signal's
+ * polarity doesn't match.
+ */
+ if (!hdmirx_check_timing_valid(bt)) {
+ if (num_retries++ < 20) {
+ if (num_retries == 10)
+ hdmirx_toggle_polarity(hdmirx_dev);
+
+ usleep_range(10 * 1000, 10 * 1100);
+ goto retry;
+ }
+
+ ret = -ERANGE;
+ } else {
+ ret = 0;
+ }
+
+ mutex_unlock(&hdmirx_dev->work_lock);
+
+ return ret;
+}
+
+static bool port_no_link(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ return !tx_5v_power_present(hdmirx_dev);
+}
+
+static int hdmirx_query_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ if (port_no_link(hdmirx_dev)) {
+ v4l2_err(v4l2_dev, "%s: port has no link\n", __func__);
+ return -ENOLINK;
+ }
+
+ if (signal_not_lock(hdmirx_dev)) {
+ v4l2_err(v4l2_dev, "%s: signal is not locked\n", __func__);
+ return -ENOLCK;
+ }
+
+ ret = hdmirx_get_detected_timings(hdmirx_dev, timings);
+ if (ret)
+ return ret;
+
+ if (debug)
+ v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
+ "query_dv_timings: ", timings, false);
+
+ if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: timings out of range\n", __func__);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: %sable, hpd_trigger_level_high:%d\n",
+ __func__, en ? "en" : "dis", hdmirx_dev->hpd_trigger_level_high);
+
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, HPDLOW, en ? 0 : HPDLOW);
+ hdmirx_writel(hdmirx_dev, CORE_CONFIG,
+ hdmirx_dev->hpd_trigger_level_high ? en : !en);
+
+ /* 100ms delay as per HDMI spec */
+ if (!en)
+ msleep(100);
+}
+
+static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev,
+ u8 *edid, unsigned int num_blocks)
+{
+ static u8 data[EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE];
+ unsigned int edid_len = num_blocks * EDID_BLOCK_SIZE;
+ unsigned int i;
+
+ cec_s_phys_addr_from_edid(hdmirx_dev->cec->adap,
+ (const struct edid *)edid);
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK |
+ EDID_WRITE_EN_MASK |
+ EDID_SLAVE_ADDR_MASK,
+ EDID_READ_EN(0) |
+ EDID_WRITE_EN(1) |
+ EDID_SLAVE_ADDR(0x50));
+ for (i = 0; i < edid_len; i++)
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG10, edid[i]);
+
+ /* read out for debug */
+ if (debug >= 2) {
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK |
+ EDID_WRITE_EN_MASK,
+ EDID_READ_EN(1) |
+ EDID_WRITE_EN(0));
+
+ for (i = 0; i < edid_len; i++)
+ data[i] = hdmirx_readl(hdmirx_dev, DMA_STATUS14);
+
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1, data,
+ edid_len, false);
+ }
+
+ /*
+ * Must set EDID_READ_EN & EDID_WRITE_EN bit to 0,
+ * when the read/write edid operation is completed. Otherwise, it
+ * will affect the reading and writing of other registers.
+ */
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG11,
+ EDID_READ_EN_MASK | EDID_WRITE_EN_MASK,
+ EDID_READ_EN(0) | EDID_WRITE_EN(0));
+}
+
+static void hdmirx_write_edid(struct snps_hdmirx_dev *hdmirx_dev,
+ struct v4l2_edid *edid)
+{
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+ memset(hdmirx_dev->edid, 0, sizeof(hdmirx_dev->edid));
+
+ hdmirx_write_edid_data(hdmirx_dev, edid->edid, edid->blocks);
+
+ hdmirx_dev->edid_blocks_written = edid->blocks;
+ memcpy(hdmirx_dev->edid, edid->edid, edid->blocks * EDID_BLOCK_SIZE);
+}
+
+/*
+ * Before clearing interrupt, we need to read the interrupt status.
+ */
+static inline void hdmirx_clear_interrupt(struct snps_hdmirx_dev *hdmirx_dev,
+ u32 reg, u32 val)
+{
+ /* (interrupt status register) = (interrupt clear register) - 0x8 */
+ hdmirx_readl(hdmirx_dev, reg - 0x8);
+ hdmirx_writel(hdmirx_dev, reg, val);
+}
+
+static void hdmirx_interrupts_setup(struct snps_hdmirx_dev *hdmirx_dev, bool en)
+{
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: %sable\n",
+ __func__, en ? "en" : "dis");
+
+ disable_irq(hdmirx_dev->hdmi_irq);
+
+ /* Note: In DVI mode, it needs to be written twice to take effect. */
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+
+ if (en) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG,
+ TMDSQPCLK_OFF_CHG | TMDSQPCLK_LOCKED_CHG);
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ TMDSVALID_STABLE_CHG, TMDSVALID_STABLE_CHG);
+ hdmirx_update_bits(hdmirx_dev, AVPUNIT_0_INT_MASK_N,
+ CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ,
+ CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ);
+ } else {
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
+ }
+
+ enable_irq(hdmirx_dev->hdmi_irq);
+}
+
+static void hdmirx_plugout(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ if (!hdmirx_dev->plugged)
+ return;
+
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0);
+ hdmirx_interrupts_setup(hdmirx_dev, false);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ hdmirx_reset_dma(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE | PHY_RESET |
+ PHY_PDDQ, HDMI_DISABLE);
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x0);
+ cancel_delayed_work(&hdmirx_dev->delayed_work_res_change);
+
+ /* will be NULL on driver removal */
+ if (hdmirx_dev->rgb_range)
+ v4l2_ctrl_s_ctrl(hdmirx_dev->rgb_range, V4L2_DV_RGB_RANGE_AUTO);
+
+ if (hdmirx_dev->content_type)
+ v4l2_ctrl_s_ctrl(hdmirx_dev->content_type,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+
+ hdmirx_dev->plugged = false;
+}
+
+static int hdmirx_set_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ u16 phys_addr;
+ int err;
+
+ if (edid->pad)
+ return -EINVAL;
+
+ if (edid->start_block)
+ return -EINVAL;
+
+ if (edid->blocks > EDID_NUM_BLOCKS_MAX) {
+ edid->blocks = EDID_NUM_BLOCKS_MAX;
+ return -E2BIG;
+ }
+
+ if (edid->blocks) {
+ phys_addr = cec_get_edid_phys_addr(edid->edid,
+ edid->blocks * EDID_BLOCK_SIZE,
+ NULL);
+
+ err = v4l2_phys_addr_validate(phys_addr, &phys_addr, NULL);
+ if (err)
+ return err;
+ }
+
+ /*
+ * Touching HW registers in parallel with plugin/out handlers
+ * will bring hardware into a bad state.
+ */
+ mutex_lock(&hdmirx_dev->work_lock);
+
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+
+ if (edid->blocks) {
+ hdmirx_write_edid(hdmirx_dev, edid);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+ } else {
+ cec_phys_addr_invalidate(hdmirx_dev->cec->adap);
+ hdmirx_dev->edid_blocks_written = 0;
+ }
+
+ mutex_unlock(&hdmirx_dev->work_lock);
+
+ return 0;
+}
+
+static int hdmirx_get_edid(struct file *file, void *fh, struct v4l2_edid *edid)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->pad)
+ return -EINVAL;
+
+ if (!edid->start_block && !edid->blocks) {
+ edid->blocks = hdmirx_dev->edid_blocks_written;
+ return 0;
+ }
+
+ if (!hdmirx_dev->edid_blocks_written)
+ return -ENODATA;
+
+ if (edid->start_block >= hdmirx_dev->edid_blocks_written || !edid->blocks)
+ return -EINVAL;
+
+ if (edid->start_block + edid->blocks > hdmirx_dev->edid_blocks_written)
+ edid->blocks = hdmirx_dev->edid_blocks_written - edid->start_block;
+
+ memcpy(edid->edid, hdmirx_dev->edid, edid->blocks * EDID_BLOCK_SIZE);
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: read EDID:\n", __func__);
+ if (debug > 0)
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_NONE, 16, 1,
+ edid->edid, edid->blocks * EDID_BLOCK_SIZE, false);
+
+ return 0;
+}
+
+static int hdmirx_g_parm(struct file *file, void *priv,
+ struct v4l2_streamparm *parm)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+
+ parm->parm.capture.timeperframe = v4l2_calc_timeperframe(&hdmirx_dev->timings);
+
+ return 0;
+}
+
+static int hdmirx_dv_timings_cap(struct file *file, void *fh,
+ struct v4l2_dv_timings_cap *cap)
+{
+ *cap = hdmirx_timings_cap;
+ return 0;
+}
+
+static int hdmirx_enum_dv_timings(struct file *file, void *_fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ return v4l2_enum_dv_timings_cap(timings, &hdmirx_timings_cap, NULL, NULL);
+}
+
+static void hdmirx_scdc_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, I2C_SLAVE_CONFIG1,
+ I2C_SDA_OUT_HOLD_VALUE_QST_MASK |
+ I2C_SDA_IN_HOLD_VALUE_QST_MASK,
+ I2C_SDA_OUT_HOLD_VALUE_QST(0x80) |
+ I2C_SDA_IN_HOLD_VALUE_QST(0x15));
+ hdmirx_update_bits(hdmirx_dev, SCDC_REGBANK_CONFIG0,
+ SCDC_SINKVERSION_QST_MASK,
+ SCDC_SINKVERSION_QST(1));
+}
+
+static int wait_reg_bit_status(struct snps_hdmirx_dev *hdmirx_dev, u32 reg,
+ u32 bit_mask, u32 expect_val, bool is_grf,
+ u32 ms)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 i, val;
+
+ for (i = 0; i < ms; i++) {
+ if (is_grf)
+ regmap_read(hdmirx_dev->grf, reg, &val);
+ else
+ val = hdmirx_readl(hdmirx_dev, reg);
+
+ if ((val & bit_mask) == expect_val) {
+ v4l2_dbg(2, debug, v4l2_dev,
+ "%s: i:%d, time: %dms\n", __func__, i, ms);
+ break;
+ }
+ usleep_range(1000, 1010);
+ }
+
+ if (i == ms)
+ return -1;
+
+ return 0;
+}
+
+static int hdmirx_phy_register_write(struct snps_hdmirx_dev *hdmirx_dev,
+ u32 phy_reg, u32 val)
+{
+ struct device *dev = hdmirx_dev->dev;
+
+ reinit_completion(&hdmirx_dev->cr_write_done);
+ /* clear irq status */
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ /* en irq */
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_WRITE_DONE, PHYCREG_CR_WRITE_DONE);
+ /* write phy reg addr */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG1, phy_reg);
+ /* write phy reg val */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG2, val);
+ /* config write enable */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONTROL, PHYCREG_CR_PARA_WRITE_P);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->cr_write_done,
+ msecs_to_jiffies(20))) {
+ dev_err(dev, "%s wait cr write done failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void hdmirx_tmds_clk_ratio_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS1);
+ v4l2_dbg(3, debug, v4l2_dev, "%s: scdc_regbank_st:%#x\n", __func__, val);
+ hdmirx_dev->tmds_clk_ratio = (val & SCDC_TMDSBITCLKRATIO) > 0;
+
+ if (hdmirx_dev->tmds_clk_ratio) {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX greater than 3.4Gbps\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
+ TMDS_CLOCK_RATIO, TMDS_CLOCK_RATIO);
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: HDMITX less than 3.4Gbps\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG,
+ TMDS_CLOCK_RATIO, 0);
+ }
+}
+
+static void hdmirx_phy_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct device *dev = hdmirx_dev->dev;
+
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, SCDC_INT_MASK_N, SCDCTMDSCCFG_CHG,
+ SCDCTMDSCCFG_CHG);
+ /* cr_para_clk 24M */
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, REFFREQ_SEL_MASK, REFFREQ_SEL(0));
+ /* rx data width 40bit valid */
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, RXDATA_WIDTH, RXDATA_WIDTH);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, PHY_RESET);
+ usleep_range(100, 110);
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET, 0);
+ usleep_range(100, 110);
+ /* select cr para interface */
+ hdmirx_writel(hdmirx_dev, PHYCREG_CONFIG0, 0x3);
+
+ if (wait_reg_bit_status(hdmirx_dev, SYS_GRF_SOC_STATUS1,
+ HDMIRXPHY_SRAM_INIT_DONE,
+ HDMIRXPHY_SRAM_INIT_DONE, true, 10))
+ dev_err(dev, "%s: phy SRAM init failed\n", __func__);
+
+ regmap_write(hdmirx_dev->grf, SYS_GRF_SOC_CON1,
+ (HDMIRXPHY_SRAM_EXT_LD_DONE << 16) |
+ HDMIRXPHY_SRAM_EXT_LD_DONE);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 3);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 2);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 1);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+ hdmirx_phy_register_write(hdmirx_dev, SUP_DIG_ANA_CREGS_SUP_ANA_NC, 0);
+
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG,
+ CDR_SETTING_BOUNDARY_3_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG,
+ CDR_SETTING_BOUNDARY_4_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG,
+ CDR_SETTING_BOUNDARY_5_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG,
+ CDR_SETTING_BOUNDARY_6_DEFAULT);
+ hdmirx_phy_register_write(hdmirx_dev,
+ HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG,
+ CDR_SETTING_BOUNDARY_7_DEFAULT);
+
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_PDDQ, 0);
+ if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, PDDQ_ACK, 0, false, 10))
+ dev_err(dev, "%s: wait pddq ack failed\n", __func__);
+
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, HDMI_DISABLE, 0);
+ if (wait_reg_bit_status(hdmirx_dev, PHY_STATUS, HDMI_DISABLE_ACK, 0,
+ false, 50))
+ dev_err(dev, "%s: wait hdmi disable ack failed\n", __func__);
+
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+}
+
+static void hdmirx_controller_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ const unsigned long iref_clk_freq_hz = 428571429;
+ struct device *dev = hdmirx_dev->dev;
+
+ reinit_completion(&hdmirx_dev->timer_base_lock);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ /* en irq */
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TIMER_BASE_LOCKED_IRQ, TIMER_BASE_LOCKED_IRQ);
+ /* write irefclk freq */
+ hdmirx_writel(hdmirx_dev, GLOBAL_TIMER_REF_BASE, iref_clk_freq_hz);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->timer_base_lock,
+ msecs_to_jiffies(20)))
+ dev_err(dev, "%s wait timer base lock failed\n", __func__);
+
+ hdmirx_update_bits(hdmirx_dev, CMU_CONFIG0,
+ TMDSQPCLK_STABLE_FREQ_MARGIN_MASK |
+ AUDCLK_STABLE_FREQ_MARGIN_MASK,
+ TMDSQPCLK_STABLE_FREQ_MARGIN(2) |
+ AUDCLK_STABLE_FREQ_MARGIN(1));
+ hdmirx_update_bits(hdmirx_dev, DESCRAND_EN_CONTROL,
+ SCRAMB_EN_SEL_QST_MASK, SCRAMB_EN_SEL_QST(1));
+ hdmirx_update_bits(hdmirx_dev, CED_CONFIG,
+ CED_VIDDATACHECKEN_QST |
+ CED_DATAISCHECKEN_QST |
+ CED_GBCHECKEN_QST |
+ CED_CTRLCHECKEN_QST |
+ CED_CHLOCKMAXER_QST_MASK,
+ CED_VIDDATACHECKEN_QST |
+ CED_GBCHECKEN_QST |
+ CED_CTRLCHECKEN_QST |
+ CED_CHLOCKMAXER_QST(0x10));
+ hdmirx_update_bits(hdmirx_dev, DEFRAMER_CONFIG0,
+ VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST_MASK,
+ VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST(0x3));
+}
+
+static void hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val, color_depth_reg;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
+
+ switch (color_depth_reg) {
+ case 0x4:
+ hdmirx_dev->color_depth = 24;
+ break;
+ case 0x5:
+ hdmirx_dev->color_depth = 30;
+ break;
+ case 0x6:
+ hdmirx_dev->color_depth = 36;
+ break;
+ case 0x7:
+ hdmirx_dev->color_depth = 48;
+ break;
+ default:
+ hdmirx_dev->color_depth = 24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
+ __func__, hdmirx_dev->color_depth, color_depth_reg);
+}
+
+static void hdmirx_get_pix_fmt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ hdmirx_dev->pix_fmt = val & HDMIRX_FORMAT_MASK;
+
+ switch (hdmirx_dev->pix_fmt) {
+ case HDMIRX_RGB888:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ break;
+ case HDMIRX_YUV422:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV16;
+ break;
+ case HDMIRX_YUV444:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV24;
+ break;
+ case HDMIRX_YUV420:
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_NV12;
+ break;
+ default:
+ v4l2_err(v4l2_dev,
+ "%s: err pix_fmt: %d, set RGB888 as default\n",
+ __func__, hdmirx_dev->pix_fmt);
+ hdmirx_dev->pix_fmt = HDMIRX_RGB888;
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s\n", __func__,
+ pix_fmt_str[hdmirx_dev->pix_fmt]);
+}
+
+static void hdmirx_read_avi_infoframe(struct snps_hdmirx_dev *hdmirx_dev,
+ u8 *aviif)
+{
+ unsigned int i, b, itr = 0;
+ u32 val;
+
+ aviif[itr++] = HDMI_INFOFRAME_TYPE_AVI;
+ val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PH2_1);
+ aviif[itr++] = val & 0xff;
+ aviif[itr++] = (val >> 8) & 0xff;
+
+ for (i = 0; i < 7; i++) {
+ val = hdmirx_readl(hdmirx_dev, PKTDEC_AVIIF_PB3_0 + 4 * i);
+
+ for (b = 0; b < 4; b++)
+ aviif[itr++] = (val >> (8 * b)) & 0xff;
+ }
+}
+
+static void hdmirx_get_avi_infoframe(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ union hdmi_infoframe frame = {};
+ u8 aviif[3 + 7 * 4];
+ int err;
+
+ hdmirx_read_avi_infoframe(hdmirx_dev, aviif);
+
+ err = hdmi_infoframe_unpack(&frame, aviif, sizeof(aviif));
+ if (err) {
+ v4l2_err(v4l2_dev, "failed to unpack AVI infoframe\n");
+ return;
+ }
+
+ v4l2_ctrl_s_ctrl(hdmirx_dev->rgb_range, frame.avi.quantization_range);
+
+ if (frame.avi.itc)
+ v4l2_ctrl_s_ctrl(hdmirx_dev->content_type,
+ frame.avi.content_type);
+ else
+ v4l2_ctrl_s_ctrl(hdmirx_dev->content_type,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+}
+
+static ssize_t
+hdmirx_debugfs_if_read(u32 type, void *priv, struct file *filp,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = priv;
+ u8 aviif[V4L2_DEBUGFS_IF_MAX_LEN] = {};
+ int len;
+
+ if (type != V4L2_DEBUGFS_IF_AVI)
+ return 0;
+
+ hdmirx_read_avi_infoframe(hdmirx_dev, aviif);
+
+ len = aviif[2] + 4;
+ if (len > V4L2_DEBUGFS_IF_MAX_LEN)
+ len = -ENOENT;
+ else
+ len = simple_read_from_buffer(ubuf, count, ppos, aviif, len);
+
+ return len < 0 ? 0 : len;
+}
+
+static void hdmirx_format_change(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ static const struct v4l2_event ev_src_chg = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+
+ hdmirx_get_pix_fmt(hdmirx_dev);
+ hdmirx_get_colordepth(hdmirx_dev);
+ hdmirx_get_avi_infoframe(hdmirx_dev);
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: queue res_chg_event\n", __func__);
+ v4l2_event_queue(&stream->vdev, &ev_src_chg);
+}
+
+static void hdmirx_set_ddr_store_fmt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ enum ddr_store_fmt store_fmt;
+ u32 dma_cfg1;
+
+ switch (hdmirx_dev->pix_fmt) {
+ case HDMIRX_RGB888:
+ store_fmt = STORE_RGB888;
+ break;
+ case HDMIRX_YUV444:
+ store_fmt = STORE_YUV444_8BIT;
+ break;
+ case HDMIRX_YUV422:
+ store_fmt = STORE_YUV422_8BIT;
+ break;
+ case HDMIRX_YUV420:
+ store_fmt = STORE_YUV420_8BIT;
+ break;
+ default:
+ store_fmt = STORE_RGB888;
+ break;
+ }
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
+ DDR_STORE_FORMAT_MASK, DDR_STORE_FORMAT(store_fmt));
+ dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
+ __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
+}
+
+static void hdmirx_dma_config(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_set_ddr_store_fmt(hdmirx_dev);
+
+ /* Note: uv_swap, rb can not swap, doc err */
+ if (hdmirx_dev->cur_fmt_fourcc != V4L2_PIX_FMT_NV16)
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, RB_SWAP_EN);
+ else
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, RB_SWAP_EN, 0);
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
+ LOCK_FRAME_NUM_MASK,
+ LOCK_FRAME_NUM(2));
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG1,
+ UV_WID_MASK | Y_WID_MASK | ABANDON_EN,
+ UV_WID(1) | Y_WID(2) | ABANDON_EN);
+}
+
+static void hdmirx_submodule_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ /* Note: if not config HDCP2_CONFIG, there will be some errors; */
+ hdmirx_update_bits(hdmirx_dev, HDCP2_CONFIG,
+ HDCP2_SWITCH_OVR_VALUE |
+ HDCP2_SWITCH_OVR_EN,
+ HDCP2_SWITCH_OVR_EN);
+ hdmirx_scdc_init(hdmirx_dev);
+ hdmirx_controller_init(hdmirx_dev);
+}
+
+static int hdmirx_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ if (input->index > 0)
+ return -EINVAL;
+
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ input->std = 0;
+ strscpy(input->name, "HDMI IN", sizeof(input->name));
+ input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+
+ return 0;
+}
+
+static int hdmirx_get_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int hdmirx_set_input(struct file *file, void *priv, unsigned int i)
+{
+ if (i)
+ return -EINVAL;
+ return 0;
+}
+
+static void hdmirx_set_fmt(struct hdmirx_stream *stream,
+ struct v4l2_pix_format_mplane *pixm, bool try)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_bt_timings *bt = &hdmirx_dev->timings.bt;
+ const struct v4l2_format_info *finfo;
+ unsigned int imagesize = 0;
+ unsigned int i;
+
+ memset(&pixm->plane_fmt[0], 0, sizeof(struct v4l2_plane_pix_format));
+ finfo = v4l2_format_info(pixm->pixelformat);
+ if (!finfo) {
+ finfo = v4l2_format_info(V4L2_PIX_FMT_BGR24);
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: set_fmt:%#x not supported, use def_fmt:%x\n",
+ __func__, pixm->pixelformat, finfo->format);
+ }
+
+ if (!bt->width || !bt->height)
+ v4l2_dbg(1, debug, v4l2_dev, "%s: invalid resolution:%#xx%#x\n",
+ __func__, bt->width, bt->height);
+
+ pixm->pixelformat = finfo->format;
+ pixm->width = bt->width;
+ pixm->height = bt->height;
+ pixm->num_planes = finfo->mem_planes;
+ pixm->quantization = V4L2_QUANTIZATION_DEFAULT;
+ pixm->colorspace = V4L2_COLORSPACE_SRGB;
+ pixm->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+
+ if (bt->interlaced == V4L2_DV_INTERLACED)
+ pixm->field = V4L2_FIELD_INTERLACED_TB;
+ else
+ pixm->field = V4L2_FIELD_NONE;
+
+ memset(pixm->reserved, 0, sizeof(pixm->reserved));
+
+ v4l2_fill_pixfmt_mp(pixm, finfo->format, pixm->width, pixm->height);
+
+ for (i = 0; i < finfo->comp_planes; i++) {
+ struct v4l2_plane_pix_format *plane_fmt;
+ int width, height, bpl, size, bpp = 0;
+ const unsigned int hw_align = 64;
+
+ if (!i) {
+ width = pixm->width;
+ height = pixm->height;
+ } else {
+ width = pixm->width / finfo->hdiv;
+ height = pixm->height / finfo->vdiv;
+ }
+
+ switch (finfo->format) {
+ case V4L2_PIX_FMT_NV24:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_BGR24:
+ bpp = finfo->bpp[i];
+ break;
+ default:
+ v4l2_dbg(1, debug, v4l2_dev,
+ "fourcc: %#x is not supported\n",
+ finfo->format);
+ break;
+ }
+
+ bpl = ALIGN(width * bpp, hw_align);
+ size = bpl * height;
+ imagesize += size;
+
+ if (finfo->mem_planes > i) {
+ /* Set bpl and size for each mplane */
+ plane_fmt = pixm->plane_fmt + i;
+ plane_fmt->bytesperline = bpl;
+ plane_fmt->sizeimage = size;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev,
+ "C-Plane %u size: %d, Total imagesize: %d\n",
+ i, size, imagesize);
+ }
+
+ /* Convert to non-MPLANE format as we want to unify non-MPLANE and MPLANE */
+ if (finfo->mem_planes == 1)
+ pixm->plane_fmt[0].sizeimage = imagesize;
+
+ if (!try) {
+ stream->out_finfo = finfo;
+ stream->pixm = *pixm;
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: req(%d, %d), out(%d, %d), fmt:%#x\n", __func__,
+ pixm->width, pixm->height, stream->pixm.width,
+ stream->pixm.height, finfo->format);
+ }
+}
+
+static int hdmirx_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ if (f->index >= 1)
+ return -EINVAL;
+
+ f->pixelformat = hdmirx_dev->cur_fmt_fourcc;
+
+ return 0;
+}
+
+static int hdmirx_s_fmt_vid_cap_mplane(struct file *file,
+ void *priv, struct v4l2_format *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (vb2_is_busy(&stream->buf_queue)) {
+ v4l2_err(v4l2_dev, "%s: queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ hdmirx_set_fmt(stream, &f->fmt.pix_mp, false);
+
+ return 0;
+}
+
+static int hdmirx_g_fmt_vid_cap_mplane(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_pix_format_mplane pixm = {};
+
+ pixm.pixelformat = hdmirx_dev->cur_fmt_fourcc;
+ hdmirx_set_fmt(stream, &pixm, true);
+ f->fmt.pix_mp = pixm;
+
+ return 0;
+}
+
+static int hdmirx_g_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 dma_cfg1;
+
+ *timings = hdmirx_dev->timings;
+ dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
+ __func__, pix_fmt_str[hdmirx_dev->pix_fmt], dma_cfg1);
+
+ return 0;
+}
+
+static int hdmirx_s_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (!timings)
+ return -EINVAL;
+
+ if (debug)
+ v4l2_print_dv_timings(hdmirx_dev->v4l2_dev.name,
+ "s_dv_timings: ", timings, false);
+
+ if (!v4l2_valid_dv_timings(timings, &hdmirx_timings_cap, NULL, NULL)) {
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: timings out of range\n", __func__);
+ return -ERANGE;
+ }
+
+ /* Check if the timings are part of the CEA-861 timings. */
+ v4l2_find_dv_timings_cap(timings, &hdmirx_timings_cap, 0, NULL, NULL);
+
+ if (v4l2_match_dv_timings(&hdmirx_dev->timings, timings, 0, false)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: no change\n", __func__);
+ return 0;
+ }
+
+ /*
+ * Changing the timings implies a format change, which is not allowed
+ * while buffers for use with streaming have already been allocated.
+ */
+ if (vb2_is_busy(&stream->buf_queue))
+ return -EBUSY;
+
+ hdmirx_dev->timings = *timings;
+ /* Update the internal format */
+ hdmirx_set_fmt(stream, &stream->pixm, false);
+
+ return 0;
+}
+
+static int hdmirx_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct device *dev = stream->hdmirx_dev->dev;
+
+ strscpy(cap->driver, dev->driver->name, sizeof(cap->driver));
+ strscpy(cap->card, dev->driver->name, sizeof(cap->card));
+
+ return 0;
+}
+
+static int hdmirx_queue_setup(struct vb2_queue *queue,
+ unsigned int *num_buffers,
+ unsigned int *num_planes,
+ unsigned int sizes[],
+ struct device *alloc_ctxs[])
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ const struct v4l2_pix_format_mplane *pixm = NULL;
+ const struct v4l2_format_info *out_finfo;
+ u32 i;
+
+ pixm = &stream->pixm;
+ out_finfo = stream->out_finfo;
+
+ if (!out_finfo) {
+ v4l2_err(v4l2_dev, "%s: out_fmt not set\n", __func__);
+ return -EINVAL;
+ }
+
+ if (*num_planes) {
+ if (*num_planes != pixm->num_planes)
+ return -EINVAL;
+
+ for (i = 0; i < *num_planes; i++)
+ if (sizes[i] < pixm->plane_fmt[i].sizeimage)
+ return -EINVAL;
+ return 0;
+ }
+
+ *num_planes = out_finfo->mem_planes;
+
+ for (i = 0; i < out_finfo->mem_planes; i++)
+ sizes[i] = pixm->plane_fmt[i].sizeimage;
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: count %d, size %d\n",
+ v4l2_type_names[queue->type], *num_buffers, sizes[0]);
+
+ return 0;
+}
+
+/*
+ * The vb2_buffer are stored in hdmirx_buffer, in order to unify
+ * mplane buffer and none-mplane buffer.
+ */
+static void hdmirx_buf_queue(struct vb2_buffer *vb)
+{
+ const struct v4l2_pix_format_mplane *pixm;
+ const struct v4l2_format_info *out_finfo;
+ struct hdmirx_buffer *hdmirx_buf;
+ struct vb2_v4l2_buffer *vbuf;
+ struct hdmirx_stream *stream;
+ struct vb2_queue *queue;
+ unsigned long flags;
+ unsigned int i;
+
+ vbuf = to_vb2_v4l2_buffer(vb);
+ hdmirx_buf = container_of(vbuf, struct hdmirx_buffer, vb);
+ queue = vb->vb2_queue;
+ stream = vb2_get_drv_priv(queue);
+ pixm = &stream->pixm;
+ out_finfo = stream->out_finfo;
+
+ memset(hdmirx_buf->buff_addr, 0, sizeof(hdmirx_buf->buff_addr));
+
+ /*
+ * If mplanes > 1, every c-plane has its own m-plane,
+ * otherwise, multiple c-planes are in the same m-plane
+ */
+ for (i = 0; i < out_finfo->mem_planes; i++)
+ hdmirx_buf->buff_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
+
+ if (out_finfo->mem_planes == 1) {
+ if (out_finfo->comp_planes == 1) {
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_CBCR] =
+ hdmirx_buf->buff_addr[HDMIRX_PLANE_Y];
+ } else {
+ for (i = 0; i < out_finfo->comp_planes - 1; i++)
+ hdmirx_buf->buff_addr[i + 1] =
+ hdmirx_buf->buff_addr[i] +
+ pixm->plane_fmt[i].bytesperline *
+ pixm->height;
+ }
+ }
+
+ spin_lock_irqsave(&stream->vbq_lock, flags);
+ list_add_tail(&hdmirx_buf->queue, &stream->buf_head);
+ spin_unlock_irqrestore(&stream->vbq_lock, flags);
+}
+
+static void return_all_buffers(struct hdmirx_stream *stream,
+ enum vb2_buffer_state state)
+{
+ struct hdmirx_buffer *buf, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&stream->vbq_lock, flags);
+ if (stream->curr_buf)
+ list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
+ if (stream->next_buf && stream->next_buf != stream->curr_buf)
+ list_add_tail(&stream->next_buf->queue, &stream->buf_head);
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+
+ list_for_each_entry_safe(buf, tmp, &stream->buf_head, queue) {
+ list_del(&buf->queue);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, flags);
+}
+
+static void hdmirx_stop_streaming(struct vb2_queue *queue)
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ v4l2_dbg(1, debug, v4l2_dev, "stream start stopping\n");
+ mutex_lock(&hdmirx_dev->stream_lock);
+ WRITE_ONCE(stream->stopping, true);
+
+ /* wait last irq to return the buffer */
+ ret = wait_event_timeout(stream->wq_stopped, !stream->stopping,
+ msecs_to_jiffies(500));
+ if (!ret)
+ v4l2_dbg(1, debug, v4l2_dev, "%s: timeout waiting last irq\n",
+ __func__);
+
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ return_all_buffers(stream, VB2_BUF_STATE_ERROR);
+ mutex_unlock(&hdmirx_dev->stream_lock);
+ v4l2_dbg(1, debug, v4l2_dev, "stream stopping finished\n");
+}
+
+static int hdmirx_start_streaming(struct vb2_queue *queue, unsigned int count)
+{
+ struct hdmirx_stream *stream = vb2_get_drv_priv(queue);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ unsigned long lock_flags = 0;
+ int line_flag;
+
+ mutex_lock(&hdmirx_dev->stream_lock);
+ stream->sequence = 0;
+ stream->line_flag_int_cnt = 0;
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+ stream->irq_stat = 0;
+
+ WRITE_ONCE(stream->stopping, false);
+
+ spin_lock_irqsave(&stream->vbq_lock, lock_flags);
+ if (!stream->curr_buf) {
+ if (!list_empty(&stream->buf_head)) {
+ stream->curr_buf = list_first_entry(&stream->buf_head,
+ struct hdmirx_buffer,
+ queue);
+ list_del(&stream->curr_buf->queue);
+ } else {
+ stream->curr_buf = NULL;
+ }
+ }
+ spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
+
+ if (!stream->curr_buf) {
+ mutex_unlock(&hdmirx_dev->stream_lock);
+ return -ENOMEM;
+ }
+
+ v4l2_dbg(2, debug, v4l2_dev,
+ "%s: start_stream cur_buf y_addr:%#x, uv_addr:%#x\n",
+ __func__, stream->curr_buf->buff_addr[HDMIRX_PLANE_Y],
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_Y]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
+ stream->curr_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+
+ if (bt->height) {
+ if (bt->interlaced == V4L2_DV_INTERLACED)
+ line_flag = bt->height / 4;
+ else
+ line_flag = bt->height / 2;
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG7,
+ LINE_FLAG_NUM_MASK,
+ LINE_FLAG_NUM(line_flag));
+ } else {
+ v4l2_err(v4l2_dev, "invalid BT timing height=%d\n", bt->height);
+ }
+
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, CED_DYN_CONTROL, 0x1);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, HDMIRX_DMA_EN);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: enable dma", __func__);
+ mutex_unlock(&hdmirx_dev->stream_lock);
+
+ return 0;
+}
+
+/* vb2 queue */
+static const struct vb2_ops hdmirx_vb2_ops = {
+ .queue_setup = hdmirx_queue_setup,
+ .buf_queue = hdmirx_buf_queue,
+ .stop_streaming = hdmirx_stop_streaming,
+ .start_streaming = hdmirx_start_streaming,
+};
+
+static int hdmirx_init_vb2_queue(struct vb2_queue *q,
+ struct hdmirx_stream *stream,
+ enum v4l2_buf_type buf_type)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
+ q->type = buf_type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = stream;
+ q->ops = &hdmirx_vb2_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct hdmirx_buffer);
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &stream->vlock;
+ q->dev = hdmirx_dev->dev;
+ q->min_queued_buffers = 1;
+
+ return vb2_queue_init(q);
+}
+
+/* video device */
+static const struct v4l2_ioctl_ops hdmirx_v4l2_ioctl_ops = {
+ .vidioc_querycap = hdmirx_querycap,
+ .vidioc_try_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
+ .vidioc_s_fmt_vid_cap_mplane = hdmirx_s_fmt_vid_cap_mplane,
+ .vidioc_g_fmt_vid_cap_mplane = hdmirx_g_fmt_vid_cap_mplane,
+ .vidioc_enum_fmt_vid_cap = hdmirx_enum_fmt_vid_cap_mplane,
+
+ .vidioc_s_dv_timings = hdmirx_s_dv_timings,
+ .vidioc_g_dv_timings = hdmirx_g_dv_timings,
+ .vidioc_enum_dv_timings = hdmirx_enum_dv_timings,
+ .vidioc_query_dv_timings = hdmirx_query_dv_timings,
+ .vidioc_dv_timings_cap = hdmirx_dv_timings_cap,
+ .vidioc_enum_input = hdmirx_enum_input,
+ .vidioc_g_input = hdmirx_get_input,
+ .vidioc_s_input = hdmirx_set_input,
+ .vidioc_g_edid = hdmirx_get_edid,
+ .vidioc_s_edid = hdmirx_set_edid,
+ .vidioc_g_parm = hdmirx_g_parm,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = hdmirx_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static const struct v4l2_file_operations hdmirx_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .poll = vb2_fop_poll,
+ .mmap = vb2_fop_mmap,
+};
+
+static int hdmirx_register_stream_vdev(struct hdmirx_stream *stream)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct video_device *vdev = &stream->vdev;
+ int ret;
+
+ strscpy(vdev->name, "stream_hdmirx", sizeof(vdev->name));
+ INIT_LIST_HEAD(&stream->buf_head);
+ spin_lock_init(&stream->vbq_lock);
+ mutex_init(&stream->vlock);
+ init_waitqueue_head(&stream->wq_stopped);
+ stream->curr_buf = NULL;
+ stream->next_buf = NULL;
+
+ vdev->ioctl_ops = &hdmirx_v4l2_ioctl_ops;
+ vdev->release = video_device_release_empty;
+ vdev->fops = &hdmirx_fops;
+ vdev->minor = -1;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->lock = &stream->vlock;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+ V4L2_CAP_STREAMING;
+ vdev->vfl_dir = VFL_DIR_RX;
+
+ video_set_drvdata(vdev, stream);
+
+ hdmirx_init_vb2_queue(&stream->buf_queue, stream,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ vdev->queue = &stream->buf_queue;
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret < 0) {
+ v4l2_err(v4l2_dev, "video_register_device failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void process_signal_change(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG6, HDMIRX_DMA_EN, 0);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ hdmirx_reset_dma(hdmirx_dev);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_res_change,
+ msecs_to_jiffies(50));
+}
+
+static void avpunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (status & (CED_DYN_CNT_CH2_IRQ |
+ CED_DYN_CNT_CH1_IRQ |
+ CED_DYN_CNT_CH0_IRQ)) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: avp0_st:%#x\n",
+ __func__, status);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_FORCE, 0x0);
+}
+
+static void avpunit_1_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ if (status & DEFRAMER_VSYNC_THR_REACHED_IRQ) {
+ v4l2_dbg(2, debug, v4l2_dev,
+ "Vertical Sync threshold reached interrupt %#x", status);
+ hdmirx_update_bits(hdmirx_dev, AVPUNIT_1_INT_MASK_N,
+ DEFRAMER_VSYNC_THR_REACHED_MASK_N, 0);
+ *handled = true;
+ }
+}
+
+static void mainunit_0_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "mu0_st:%#x\n", status);
+ if (status & TIMER_BASE_LOCKED_IRQ) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_0_INT_MASK_N,
+ TIMER_BASE_LOCKED_IRQ, 0);
+ complete(&hdmirx_dev->timer_base_lock);
+ *handled = true;
+ }
+
+ if (status & TMDSQPCLK_OFF_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_OFF_CHG\n", __func__);
+ *handled = true;
+ }
+
+ if (status & TMDSQPCLK_LOCKED_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSQPCLK_LOCKED_CHG\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_FORCE, 0x0);
+}
+
+static void mainunit_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "mu2_st:%#x\n", status);
+ if (status & PHYCREG_CR_WRITE_DONE) {
+ hdmirx_update_bits(hdmirx_dev, MAINUNIT_2_INT_MASK_N,
+ PHYCREG_CR_WRITE_DONE, 0);
+ complete(&hdmirx_dev->cr_write_done);
+ *handled = true;
+ }
+
+ if (status & TMDSVALID_STABLE_CHG) {
+ process_signal_change(hdmirx_dev);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: TMDSVALID_STABLE_CHG\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_FORCE, 0x0);
+}
+
+static void pkt_2_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: pk2_st:%#x\n", __func__, status);
+ if (status & PKTDEC_AVIIF_RCV_IRQ) {
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, 0);
+ complete(&hdmirx_dev->avi_pkt_rcv);
+ v4l2_dbg(2, debug, v4l2_dev, "%s: AVIIF_RCV_IRQ\n", __func__);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+}
+
+static void scdc_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ int status, bool *handled)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: scdc_st:%#x\n", __func__, status);
+ if (status & SCDCTMDSCCFG_CHG) {
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+ *handled = true;
+ }
+
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+}
+
+static irqreturn_t hdmirx_hdmi_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 mu0_st, mu2_st, pk2_st, scdc_st, avp1_st, avp0_st;
+ u32 mu0_mask, mu2_mask, pk2_mask, scdc_mask, avp1_msk, avp0_msk;
+ bool handled = false;
+
+ mu0_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_MASK_N);
+ mu2_mask = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_MASK_N);
+ pk2_mask = hdmirx_readl(hdmirx_dev, PKT_2_INT_MASK_N);
+ scdc_mask = hdmirx_readl(hdmirx_dev, SCDC_INT_MASK_N);
+ mu0_st = hdmirx_readl(hdmirx_dev, MAINUNIT_0_INT_STATUS);
+ mu2_st = hdmirx_readl(hdmirx_dev, MAINUNIT_2_INT_STATUS);
+ pk2_st = hdmirx_readl(hdmirx_dev, PKT_2_INT_STATUS);
+ scdc_st = hdmirx_readl(hdmirx_dev, SCDC_INT_STATUS);
+ avp0_st = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_STATUS);
+ avp1_st = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_STATUS);
+ avp0_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_0_INT_MASK_N);
+ avp1_msk = hdmirx_readl(hdmirx_dev, AVPUNIT_1_INT_MASK_N);
+ mu0_st &= mu0_mask;
+ mu2_st &= mu2_mask;
+ pk2_st &= pk2_mask;
+ avp1_st &= avp1_msk;
+ avp0_st &= avp0_msk;
+ scdc_st &= scdc_mask;
+
+ if (avp0_st)
+ avpunit_0_int_handler(hdmirx_dev, avp0_st, &handled);
+ if (avp1_st)
+ avpunit_1_int_handler(hdmirx_dev, avp1_st, &handled);
+ if (mu0_st)
+ mainunit_0_int_handler(hdmirx_dev, mu0_st, &handled);
+ if (mu2_st)
+ mainunit_2_int_handler(hdmirx_dev, mu2_st, &handled);
+ if (pk2_st)
+ pkt_2_int_handler(hdmirx_dev, pk2_st, &handled);
+ if (scdc_st)
+ scdc_int_handler(hdmirx_dev, scdc_st, &handled);
+
+ if (!handled) {
+ v4l2_dbg(2, debug, v4l2_dev, "%s: hdmi irq not handled", __func__);
+ v4l2_dbg(2, debug, v4l2_dev,
+ "avp0:%#x, avp1:%#x, mu0:%#x, mu2:%#x, pk2:%#x, scdc:%#x\n",
+ avp0_st, avp1_st, mu0_st, mu2_st, pk2_st, scdc_st);
+ }
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: en_fiq", __func__);
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void hdmirx_vb_done(struct hdmirx_stream *stream,
+ struct vb2_v4l2_buffer *vb_done)
+{
+ const struct v4l2_format_info *finfo = stream->out_finfo;
+ u32 i;
+
+ /* Dequeue a filled buffer */
+ for (i = 0; i < finfo->mem_planes; i++) {
+ vb2_set_plane_payload(&vb_done->vb2_buf, i,
+ stream->pixm.plane_fmt[i].sizeimage);
+ }
+
+ vb_done->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&vb_done->vb2_buf, VB2_BUF_STATE_DONE);
+}
+
+static void dma_idle_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ bool *handled)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ struct vb2_v4l2_buffer *vb_done = NULL;
+
+ if (!(stream->irq_stat) && !(stream->irq_stat & LINE_FLAG_INT_EN))
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: last time have no line_flag_irq\n", __func__);
+
+ /* skip first frames that are expected to come out zeroed from DMA */
+ if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
+ goto DMA_IDLE_OUT;
+
+ if (bt->interlaced != V4L2_DV_INTERLACED ||
+ !(stream->line_flag_int_cnt % 2)) {
+ if (stream->next_buf) {
+ if (stream->curr_buf)
+ vb_done = &stream->curr_buf->vb;
+
+ if (vb_done) {
+ vb_done->vb2_buf.timestamp = ktime_get_ns();
+ vb_done->sequence = stream->sequence;
+
+ if (bt->interlaced)
+ vb_done->field = V4L2_FIELD_INTERLACED_TB;
+ else
+ vb_done->field = V4L2_FIELD_NONE;
+
+ hdmirx_vb_done(stream, vb_done);
+ }
+
+ stream->curr_buf = NULL;
+ if (stream->next_buf) {
+ stream->curr_buf = stream->next_buf;
+ stream->next_buf = NULL;
+ }
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: next_buf NULL, skip vb_done\n", __func__);
+ }
+
+ stream->sequence++;
+ if (stream->sequence == 30)
+ v4l2_dbg(1, debug, v4l2_dev, "rcv frames\n");
+ }
+
+DMA_IDLE_OUT:
+ *handled = true;
+}
+
+static void line_flag_int_handler(struct snps_hdmirx_dev *hdmirx_dev,
+ bool *handled)
+{
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ struct v4l2_dv_timings timings = hdmirx_dev->timings;
+ struct v4l2_bt_timings *bt = &timings.bt;
+ u32 dma_cfg6;
+
+ stream->line_flag_int_cnt++;
+ if (!(stream->irq_stat) && !(stream->irq_stat & HDMIRX_DMA_IDLE_INT))
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: last have no dma_idle_irq\n", __func__);
+ dma_cfg6 = hdmirx_readl(hdmirx_dev, DMA_CONFIG6);
+ if (!(dma_cfg6 & HDMIRX_DMA_EN)) {
+ v4l2_dbg(2, debug, v4l2_dev, "%s: dma not on\n", __func__);
+ goto LINE_FLAG_OUT;
+ }
+
+ if (stream->line_flag_int_cnt <= FILTER_FRAME_CNT)
+ goto LINE_FLAG_OUT;
+
+ if (bt->interlaced != V4L2_DV_INTERLACED ||
+ !(stream->line_flag_int_cnt % 2)) {
+ if (!stream->next_buf) {
+ spin_lock(&stream->vbq_lock);
+ if (!list_empty(&stream->buf_head)) {
+ stream->next_buf = list_first_entry(&stream->buf_head,
+ struct hdmirx_buffer,
+ queue);
+ list_del(&stream->next_buf->queue);
+ } else {
+ stream->next_buf = NULL;
+ }
+ spin_unlock(&stream->vbq_lock);
+
+ if (stream->next_buf) {
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG2,
+ stream->next_buf->buff_addr[HDMIRX_PLANE_Y]);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG3,
+ stream->next_buf->buff_addr[HDMIRX_PLANE_CBCR]);
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: no buffer is available\n", __func__);
+ }
+ }
+ } else {
+ v4l2_dbg(3, debug, v4l2_dev, "%s: interlace:%d, line_flag_int_cnt:%d\n",
+ __func__, bt->interlaced, stream->line_flag_int_cnt);
+ }
+
+LINE_FLAG_OUT:
+ *handled = true;
+}
+
+static irqreturn_t hdmirx_dma_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ struct hdmirx_stream *stream = &hdmirx_dev->stream;
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 dma_stat1, dma_stat13;
+ bool handled = false;
+
+ dma_stat1 = hdmirx_readl(hdmirx_dev, DMA_STATUS1);
+ dma_stat13 = hdmirx_readl(hdmirx_dev, DMA_STATUS13);
+ v4l2_dbg(3, debug, v4l2_dev, "dma_irq st1:%#x, st13:%d\n",
+ dma_stat1, dma_stat13);
+
+ if (READ_ONCE(stream->stopping)) {
+ v4l2_dbg(1, debug, v4l2_dev, "%s: stop stream\n", __func__);
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, DMA_CONFIG4,
+ LINE_FLAG_INT_EN |
+ HDMIRX_DMA_IDLE_INT |
+ HDMIRX_LOCK_DISABLE_INT |
+ LAST_FRAME_AXI_UNFINISH_INT_EN |
+ FIFO_OVERFLOW_INT_EN |
+ FIFO_UNDERFLOW_INT_EN |
+ HDMIRX_AXI_ERROR_INT_EN, 0);
+ WRITE_ONCE(stream->stopping, false);
+ wake_up(&stream->wq_stopped);
+ return IRQ_HANDLED;
+ }
+
+ if (dma_stat1 & HDMIRX_DMA_IDLE_INT)
+ dma_idle_int_handler(hdmirx_dev, &handled);
+
+ if (dma_stat1 & LINE_FLAG_INT_EN)
+ line_flag_int_handler(hdmirx_dev, &handled);
+
+ if (!handled)
+ v4l2_dbg(3, debug, v4l2_dev,
+ "%s: dma irq not handled, dma_stat1:%#x\n",
+ __func__, dma_stat1);
+
+ stream->irq_stat = dma_stat1;
+ hdmirx_writel(hdmirx_dev, DMA_CONFIG5, 0xffffffff);
+
+ return IRQ_HANDLED;
+}
+
+static int hdmirx_wait_signal_lock(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 mu_status, scdc_status, dma_st10, cmu_st;
+ u32 i;
+
+ for (i = 0; i < 300; i++) {
+ mu_status = hdmirx_readl(hdmirx_dev, MAINUNIT_STATUS);
+ scdc_status = hdmirx_readl(hdmirx_dev, SCDC_REGBANK_STATUS3);
+ dma_st10 = hdmirx_readl(hdmirx_dev, DMA_STATUS10);
+ cmu_st = hdmirx_readl(hdmirx_dev, CMU_STATUS);
+
+ if ((mu_status & TMDSVALID_STABLE_ST) &&
+ (dma_st10 & HDMIRX_LOCK) &&
+ (cmu_st & TMDSQPCLK_LOCKED_ST))
+ break;
+
+ if (!tx_5v_power_present(hdmirx_dev)) {
+ v4l2_dbg(1, debug, v4l2_dev,
+ "%s: HDMI pull out, return\n", __func__);
+ return -1;
+ }
+
+ hdmirx_tmds_clk_ratio_config(hdmirx_dev);
+ }
+
+ if (i == 300) {
+ v4l2_err(v4l2_dev, "%s: signal not lock, tmds_clk_ratio:%d\n",
+ __func__, hdmirx_dev->tmds_clk_ratio);
+ v4l2_err(v4l2_dev, "%s: mu_st:%#x, scdc_st:%#x, dma_st10:%#x\n",
+ __func__, mu_status, scdc_status, dma_st10);
+ return -1;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: signal lock ok, i:%d\n", __func__, i);
+ hdmirx_writel(hdmirx_dev, GLOBAL_SWRESET_REQUEST, DATAPATH_SWRESETREQ);
+
+ reinit_completion(&hdmirx_dev->avi_pkt_rcv);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, PKTDEC_AVIIF_RCV_IRQ);
+
+ if (!wait_for_completion_timeout(&hdmirx_dev->avi_pkt_rcv,
+ msecs_to_jiffies(300))) {
+ v4l2_err(v4l2_dev, "%s wait avi_pkt_rcv failed\n", __func__);
+ hdmirx_update_bits(hdmirx_dev, PKT_2_INT_MASK_N,
+ PKTDEC_AVIIF_RCV_IRQ, 0);
+ }
+
+ msleep(50);
+ hdmirx_format_change(hdmirx_dev);
+
+ return 0;
+}
+
+static void hdmirx_plugin(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ if (hdmirx_dev->plugged)
+ return;
+
+ hdmirx_submodule_init(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
+ POWERPROVIDED);
+ hdmirx_phy_config(hdmirx_dev);
+ hdmirx_interrupts_setup(hdmirx_dev, true);
+
+ hdmirx_dev->plugged = true;
+}
+
+static void hdmirx_delayed_work_hotplug(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ bool plugin;
+
+ hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
+ delayed_work_hotplug.work);
+
+ mutex_lock(&hdmirx_dev->work_lock);
+ plugin = tx_5v_power_present(hdmirx_dev);
+ v4l2_ctrl_s_ctrl(hdmirx_dev->detect_tx_5v_ctrl, plugin);
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
+ __func__, plugin);
+
+ hdmirx_plugout(hdmirx_dev);
+
+ if (plugin)
+ hdmirx_plugin(hdmirx_dev);
+
+ mutex_unlock(&hdmirx_dev->work_lock);
+}
+
+static void hdmirx_delayed_work_res_change(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ bool plugin;
+
+ hdmirx_dev = container_of(work, struct snps_hdmirx_dev,
+ delayed_work_res_change.work);
+
+ mutex_lock(&hdmirx_dev->work_lock);
+ plugin = tx_5v_power_present(hdmirx_dev);
+ v4l2_dbg(1, debug, &hdmirx_dev->v4l2_dev, "%s: plugin:%d\n",
+ __func__, plugin);
+ if (plugin) {
+ hdmirx_interrupts_setup(hdmirx_dev, false);
+ hdmirx_submodule_init(hdmirx_dev);
+ hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
+ POWERPROVIDED);
+ hdmirx_phy_config(hdmirx_dev);
+
+ if (hdmirx_wait_signal_lock(hdmirx_dev)) {
+ hdmirx_plugout(hdmirx_dev);
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(200));
+ } else {
+ hdmirx_dma_config(hdmirx_dev);
+ hdmirx_interrupts_setup(hdmirx_dev, true);
+ }
+ }
+ mutex_unlock(&hdmirx_dev->work_lock);
+}
+
+static irqreturn_t hdmirx_5v_det_irq_handler(int irq, void *dev_id)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_id;
+ u32 val;
+
+ val = gpiod_get_value(hdmirx_dev->detect_5v_gpio);
+ v4l2_dbg(3, debug, &hdmirx_dev->v4l2_dev, "%s: 5v:%d\n", __func__, val);
+
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(10));
+
+ return IRQ_HANDLED;
+}
+
+static const struct hdmirx_cec_ops hdmirx_cec_ops = {
+ .write = hdmirx_writel,
+ .read = hdmirx_readl,
+};
+
+static void devm_hdmirx_of_reserved_mem_device_release(void *dev)
+{
+ of_reserved_mem_device_release(dev);
+}
+
+static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ int ret;
+
+ hdmirx_dev->num_clks = devm_clk_bulk_get_all(dev, &hdmirx_dev->clks);
+ if (hdmirx_dev->num_clks < 1)
+ return -ENODEV;
+
+ hdmirx_dev->resets[HDMIRX_RST_A].id = "axi";
+ hdmirx_dev->resets[HDMIRX_RST_P].id = "apb";
+ hdmirx_dev->resets[HDMIRX_RST_REF].id = "ref";
+ hdmirx_dev->resets[HDMIRX_RST_BIU].id = "biu";
+
+ ret = devm_reset_control_bulk_get_exclusive(dev, HDMIRX_NUM_RST,
+ hdmirx_dev->resets);
+ if (ret < 0) {
+ dev_err(dev, "failed to get reset controls\n");
+ return ret;
+ }
+
+ hdmirx_dev->detect_5v_gpio =
+ devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
+
+ if (IS_ERR(hdmirx_dev->detect_5v_gpio)) {
+ dev_err(dev, "failed to get hdmirx hot plug detection gpio\n");
+ return PTR_ERR(hdmirx_dev->detect_5v_gpio);
+ }
+
+ hdmirx_dev->grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(hdmirx_dev->grf)) {
+ dev_err(dev, "failed to get rockchip,grf\n");
+ return PTR_ERR(hdmirx_dev->grf);
+ }
+
+ hdmirx_dev->vo1_grf = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,vo1-grf");
+ if (IS_ERR(hdmirx_dev->vo1_grf)) {
+ dev_err(dev, "failed to get rockchip,vo1-grf\n");
+ return PTR_ERR(hdmirx_dev->vo1_grf);
+ }
+
+ if (!device_property_read_bool(dev, "hpd-is-active-low"))
+ hdmirx_dev->hpd_trigger_level_high = true;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_warn(dev, "no reserved memory for HDMIRX, use default CMA\n");
+ } else {
+ ret = devm_add_action_or_reset(dev,
+ devm_hdmirx_of_reserved_mem_device_release,
+ dev);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void hdmirx_disable_all_interrupts(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_writel(hdmirx_dev, MAINUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, AVPUNIT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_0_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_1_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, PKT_2_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, SCDC_INT_MASK_N, 0);
+ hdmirx_writel(hdmirx_dev, CEC_INT_MASK_N, 0);
+
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, MAINUNIT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, AVPUNIT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_0_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, PKT_2_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, SCDC_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, HDCP_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, HDCP_1_INT_CLEAR, 0xffffffff);
+ hdmirx_clear_interrupt(hdmirx_dev, CEC_INT_CLEAR, 0xffffffff);
+}
+
+static void hdmirx_init(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_update_bits(hdmirx_dev, PHY_CONFIG, PHY_RESET | PHY_PDDQ, 0);
+
+ regmap_write(hdmirx_dev->vo1_grf, VO1_GRF_VO1_CON2,
+ (HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) |
+ ((HDMIRX_SDAIN_MSK | HDMIRX_SCLIN_MSK) << 16));
+ /*
+ * Some interrupts are enabled by default, so we disable
+ * all interrupts and clear interrupts status first.
+ */
+ hdmirx_disable_all_interrupts(hdmirx_dev);
+}
+
+/* hdmi-4k-300mhz EDID produced by v4l2-ctl tool */
+static u8 __maybe_unused edid_default[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x1a, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78,
+ 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26,
+ 0x0f, 0x50, 0x54, 0x2f, 0xcf, 0x00, 0x31, 0x59,
+ 0x45, 0x59, 0x81, 0x80, 0x81, 0x40, 0x90, 0x40,
+ 0x95, 0x00, 0xa9, 0x40, 0xb3, 0x00, 0x04, 0x74,
+ 0x00, 0x30, 0xf2, 0x70, 0x5a, 0x80, 0xb0, 0x58,
+ 0x8a, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 0x55, 0x18,
+ 0x87, 0x1e, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x68,
+ 0x64, 0x6d, 0x69, 0x2d, 0x34, 0x6b, 0x2d, 0x33,
+ 0x30, 0x30, 0x0a, 0x20, 0x00, 0x00, 0x00, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc5,
+
+ 0x02, 0x03, 0x40, 0xf1, 0x4f, 0x5f, 0x5e, 0x5d,
+ 0x10, 0x1f, 0x04, 0x13, 0x22, 0x21, 0x20, 0x05,
+ 0x14, 0x02, 0x11, 0x01, 0x23, 0x09, 0x07, 0x07,
+ 0x83, 0x01, 0x00, 0x00, 0x6d, 0x03, 0x0c, 0x00,
+ 0x10, 0x00, 0x00, 0x3c, 0x21, 0x00, 0x60, 0x01,
+ 0x02, 0x03, 0x67, 0xd8, 0x5d, 0xc4, 0x01, 0x00,
+ 0x00, 0x00, 0xe2, 0x00, 0xca, 0xe3, 0x05, 0x00,
+ 0x00, 0xe3, 0x06, 0x01, 0x00, 0xe2, 0x0d, 0x5f,
+ 0xa3, 0x66, 0x00, 0xa0, 0xf0, 0x70, 0x1f, 0x80,
+ 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c, 0x32, 0x00,
+ 0x00, 0x1e, 0x1a, 0x36, 0x80, 0xa0, 0x70, 0x38,
+ 0x1f, 0x40, 0x30, 0x20, 0x35, 0x00, 0xc0, 0x1c,
+ 0x32, 0x00, 0x00, 0x1a, 0x1a, 0x1d, 0x00, 0x80,
+ 0x51, 0xd0, 0x1c, 0x20, 0x40, 0x80, 0x35, 0x00,
+ 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa1,
+};
+
+static void hdmirx_load_default_edid(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_edid def_edid = {};
+
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+
+ if (!IS_ENABLED(CONFIG_VIDEO_SYNOPSYS_HDMIRX_LOAD_DEFAULT_EDID))
+ return;
+
+ /* disable hpd and write edid */
+ def_edid.blocks = sizeof(edid_default) / EDID_BLOCK_SIZE;
+ def_edid.edid = edid_default;
+
+ hdmirx_write_edid(hdmirx_dev, &def_edid);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+}
+
+static int hdmirx_disable(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+
+ hdmirx_plugout(hdmirx_dev);
+ hdmirx_hpd_ctrl(hdmirx_dev, false);
+
+ clk_bulk_disable_unprepare(hdmirx_dev->num_clks, hdmirx_dev->clks);
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: suspend\n", __func__);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int hdmirx_enable(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ int ret;
+
+ v4l2_dbg(2, debug, v4l2_dev, "%s: resume\n", __func__);
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_bulk_prepare_enable(hdmirx_dev->num_clks, hdmirx_dev->clks);
+ if (ret) {
+ dev_err(dev, "failed to enable hdmirx bulk clks: %d\n", ret);
+ return ret;
+ }
+
+ reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+ usleep_range(150, 160);
+ reset_control_bulk_deassert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+ usleep_range(150, 160);
+
+ return 0;
+}
+
+static void hdmirx_disable_irq(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ disable_irq(hdmirx_dev->det_irq);
+ disable_irq(hdmirx_dev->dma_irq);
+ disable_irq(hdmirx_dev->hdmi_irq);
+
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_hotplug);
+ cancel_delayed_work_sync(&hdmirx_dev->delayed_work_res_change);
+}
+
+static void hdmirx_enable_irq(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ enable_irq(hdmirx_dev->hdmi_irq);
+ enable_irq(hdmirx_dev->dma_irq);
+ enable_irq(hdmirx_dev->det_irq);
+
+ queue_delayed_work(system_unbound_wq,
+ &hdmirx_dev->delayed_work_hotplug,
+ msecs_to_jiffies(110));
+}
+
+static __maybe_unused int hdmirx_suspend(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ hdmirx_disable_irq(dev);
+
+ /* TODO store CEC HW state */
+ disable_irq(hdmirx_dev->cec->irq);
+
+ return hdmirx_disable(dev);
+}
+
+static __maybe_unused int hdmirx_resume(struct device *dev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ int ret = hdmirx_enable(dev);
+
+ if (ret)
+ return ret;
+
+ if (hdmirx_dev->edid_blocks_written) {
+ hdmirx_write_edid_data(hdmirx_dev, hdmirx_dev->edid,
+ hdmirx_dev->edid_blocks_written);
+ hdmirx_hpd_ctrl(hdmirx_dev, true);
+ }
+
+ /* TODO restore CEC HW state */
+ enable_irq(hdmirx_dev->cec->irq);
+
+ hdmirx_enable_irq(dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops snps_hdmirx_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(hdmirx_suspend, hdmirx_resume)
+};
+
+static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
+ struct platform_device *pdev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ int ret, irq;
+
+ irq = platform_get_irq_byname(pdev, "hdmi");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get hdmi irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->hdmi_irq = irq;
+ ret = devm_request_irq(dev, irq, hdmirx_hdmi_irq_handler, 0,
+ "rk_hdmirx-hdmi", hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request hdmi irq\n");
+ return ret;
+ }
+
+ irq = platform_get_irq_byname(pdev, "dma");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get dma irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->dma_irq = irq;
+ ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
+ IRQF_ONESHOT, "rk_hdmirx-dma",
+ hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request dma irq\n");
+ return ret;
+ }
+
+ irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get hdmirx-5v irq\n");
+ return irq;
+ }
+
+ irq_set_status_flags(irq, IRQ_NOAUTOEN);
+
+ hdmirx_dev->det_irq = irq;
+ ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "rk_hdmirx-5v", hdmirx_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
+ struct platform_device *pdev)
+{
+ struct device *dev = hdmirx_dev->dev;
+ struct hdmirx_cec_data cec_data;
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, "cec");
+ if (irq < 0) {
+ dev_err_probe(dev, irq, "failed to get cec irq\n");
+ return irq;
+ }
+
+ cec_data.hdmirx = hdmirx_dev;
+ cec_data.dev = hdmirx_dev->dev;
+ cec_data.ops = &hdmirx_cec_ops;
+ cec_data.irq = irq;
+
+ hdmirx_dev->cec = snps_hdmirx_cec_register(&cec_data);
+ if (IS_ERR(hdmirx_dev->cec))
+ return dev_err_probe(dev, PTR_ERR(hdmirx_dev->cec),
+ "failed to register cec\n");
+
+ return 0;
+}
+
+static int hdmirx_probe(struct platform_device *pdev)
+{
+ struct snps_hdmirx_dev *hdmirx_dev;
+ struct device *dev = &pdev->dev;
+ struct v4l2_ctrl_handler *hdl;
+ struct hdmirx_stream *stream;
+ struct v4l2_device *v4l2_dev;
+ int ret;
+
+ hdmirx_dev = devm_kzalloc(dev, sizeof(*hdmirx_dev), GFP_KERNEL);
+ if (!hdmirx_dev)
+ return -ENOMEM;
+
+ /*
+ * RK3588 HDMIRX SoC integration doesn't use IOMMU and can
+ * address only first 32bit of the physical address space.
+ */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
+
+ hdmirx_dev->dev = dev;
+ dev_set_drvdata(dev, hdmirx_dev);
+
+ ret = hdmirx_parse_dt(hdmirx_dev);
+ if (ret)
+ return ret;
+
+ ret = hdmirx_setup_irq(hdmirx_dev, pdev);
+ if (ret)
+ return ret;
+
+ hdmirx_dev->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(hdmirx_dev->regs))
+ return dev_err_probe(dev, PTR_ERR(hdmirx_dev->regs),
+ "failed to remap regs resource\n");
+
+ mutex_init(&hdmirx_dev->stream_lock);
+ mutex_init(&hdmirx_dev->work_lock);
+ spin_lock_init(&hdmirx_dev->rst_lock);
+
+ init_completion(&hdmirx_dev->cr_write_done);
+ init_completion(&hdmirx_dev->timer_base_lock);
+ init_completion(&hdmirx_dev->avi_pkt_rcv);
+
+ INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_hotplug,
+ hdmirx_delayed_work_hotplug);
+ INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
+ hdmirx_delayed_work_res_change);
+
+ hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
+ hdmirx_dev->timings = cea640x480;
+
+ hdmirx_enable(dev);
+ hdmirx_init(hdmirx_dev);
+
+ v4l2_dev = &hdmirx_dev->v4l2_dev;
+ strscpy(v4l2_dev->name, dev_name(dev), sizeof(v4l2_dev->name));
+
+ hdl = &hdmirx_dev->hdl;
+ v4l2_ctrl_handler_init(hdl, 3);
+
+ hdmirx_dev->detect_tx_5v_ctrl = v4l2_ctrl_new_std(hdl, NULL,
+ V4L2_CID_DV_RX_POWER_PRESENT,
+ 0, 1, 0, 0);
+
+ hdmirx_dev->rgb_range = v4l2_ctrl_new_std_menu(hdl, NULL,
+ V4L2_CID_DV_RX_RGB_RANGE,
+ V4L2_DV_RGB_RANGE_FULL, 0,
+ V4L2_DV_RGB_RANGE_AUTO);
+
+ hdmirx_dev->rgb_range->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ hdmirx_dev->content_type =
+ v4l2_ctrl_new_std_menu(hdl, NULL, V4L2_CID_DV_RX_IT_CONTENT_TYPE,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0,
+ V4L2_DV_IT_CONTENT_TYPE_NO_ITC);
+
+ if (hdl->error) {
+ ret = hdl->error;
+ dev_err_probe(dev, ret, "v4l2 ctrl handler init failed\n");
+ goto err_pm;
+ }
+ hdmirx_dev->v4l2_dev.ctrl_handler = hdl;
+
+ ret = v4l2_device_register(dev, &hdmirx_dev->v4l2_dev);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "v4l2 device registration failed\n");
+ goto err_hdl;
+ }
+
+ stream = &hdmirx_dev->stream;
+ stream->hdmirx_dev = hdmirx_dev;
+ ret = hdmirx_register_stream_vdev(stream);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "video device registration failed\n");
+ goto err_unreg_v4l2_dev;
+ }
+
+ ret = hdmirx_register_cec(hdmirx_dev, pdev);
+ if (ret)
+ goto err_unreg_video_dev;
+
+ hdmirx_load_default_edid(hdmirx_dev);
+
+ hdmirx_enable_irq(dev);
+
+ hdmirx_dev->debugfs_dir = debugfs_create_dir(hdmirx_dev->v4l2_dev.name,
+ v4l2_debugfs_root());
+
+ hdmirx_dev->infoframes = v4l2_debugfs_if_alloc(hdmirx_dev->debugfs_dir,
+ V4L2_DEBUGFS_IF_AVI, hdmirx_dev,
+ hdmirx_debugfs_if_read);
+
+ return 0;
+
+err_unreg_video_dev:
+ vb2_video_unregister_device(&hdmirx_dev->stream.vdev);
+err_unreg_v4l2_dev:
+ v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
+err_hdl:
+ v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
+err_pm:
+ hdmirx_disable(dev);
+
+ return ret;
+}
+
+static void hdmirx_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ v4l2_debugfs_if_free(hdmirx_dev->infoframes);
+ debugfs_remove_recursive(hdmirx_dev->debugfs_dir);
+
+ snps_hdmirx_cec_unregister(hdmirx_dev->cec);
+
+ hdmirx_disable_irq(dev);
+
+ vb2_video_unregister_device(&hdmirx_dev->stream.vdev);
+ v4l2_ctrl_handler_free(&hdmirx_dev->hdl);
+ v4l2_device_unregister(&hdmirx_dev->v4l2_dev);
+
+ /* touched by hdmirx_disable()->hdmirx_plugout() */
+ hdmirx_dev->rgb_range = NULL;
+ hdmirx_dev->content_type = NULL;
+
+ hdmirx_disable(dev);
+
+ reset_control_bulk_assert(HDMIRX_NUM_RST, hdmirx_dev->resets);
+}
+
+static const struct of_device_id hdmirx_id[] = {
+ { .compatible = "rockchip,rk3588-hdmirx-ctrler" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hdmirx_id);
+
+static struct platform_driver hdmirx_driver = {
+ .probe = hdmirx_probe,
+ .remove = hdmirx_remove,
+ .driver = {
+ .name = "snps_hdmirx",
+ .of_match_table = hdmirx_id,
+ .pm = &snps_hdmirx_pm_ops,
+ }
+};
+module_platform_driver(hdmirx_driver);
+
+MODULE_DESCRIPTION("Synopsys HDMI Receiver Driver");
+MODULE_AUTHOR("Dingxian Wen <shawn.wen@rock-chips.com>");
+MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
+MODULE_AUTHOR("Dmitry Osipenko <dmitry.osipenko@collabora.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
new file mode 100644
index 000000000000..220ab99ca611
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Dingxian Wen <shawn.wen@rock-chips.com>
+ */
+
+#ifndef DW_HDMIRX_H
+#define DW_HDMIRX_H
+
+#include <linux/bitops.h>
+
+#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
+#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16))
+
+/* SYS_GRF */
+#define SYS_GRF_SOC_CON1 0x0304
+#define HDMIRXPHY_SRAM_EXT_LD_DONE BIT(1)
+#define HDMIRXPHY_SRAM_BYPASS BIT(0)
+#define SYS_GRF_SOC_STATUS1 0x0384
+#define HDMIRXPHY_SRAM_INIT_DONE BIT(10)
+#define SYS_GRF_CHIP_ID 0x0600
+
+/* VO1_GRF */
+#define VO1_GRF_VO1_CON2 0x0008
+#define HDMIRX_SDAIN_MSK BIT(2)
+#define HDMIRX_SCLIN_MSK BIT(1)
+
+/* HDMIRX PHY */
+#define SUP_DIG_ANA_CREGS_SUP_ANA_NC 0x004f
+
+#define LANE0_DIG_ASIC_RX_OVRD_OUT_0 0x100f
+#define LANE1_DIG_ASIC_RX_OVRD_OUT_0 0x110f
+#define LANE2_DIG_ASIC_RX_OVRD_OUT_0 0x120f
+#define LANE3_DIG_ASIC_RX_OVRD_OUT_0 0x130f
+#define ASIC_ACK_OVRD_EN BIT(1)
+#define ASIC_ACK BIT(0)
+
+#define LANE0_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x104a
+#define LANE1_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x114a
+#define LANE2_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x124a
+#define LANE3_DIG_RX_VCOCAL_RX_VCO_CAL_CTRL_2 0x134a
+#define FREQ_TUNE_START_VAL_MASK GENMASK(9, 0)
+#define FREQ_TUNE_START_VAL(x) UPDATE(x, 9, 0)
+
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_FSM_CONFIG 0x20c4
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_ADAPT_REF_FOM 0x20c7
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_3_REG 0x20e9
+#define CDR_SETTING_BOUNDARY_3_DEFAULT 0x52da
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_4_REG 0x20ea
+#define CDR_SETTING_BOUNDARY_4_DEFAULT 0x43cd
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_5_REG 0x20eb
+#define CDR_SETTING_BOUNDARY_5_DEFAULT 0x35b3
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_6_REG 0x20fb
+#define CDR_SETTING_BOUNDARY_6_DEFAULT 0x2799
+#define HDMIPCS_DIG_CTRL_PATH_MAIN_FSM_RATE_CALC_HDMI14_CDR_SETTING_7_REG 0x20fc
+#define CDR_SETTING_BOUNDARY_7_DEFAULT 0x1b65
+
+#define RAWLANE0_DIG_PCS_XF_RX_OVRD_OUT 0x300e
+#define RAWLANE1_DIG_PCS_XF_RX_OVRD_OUT 0x310e
+#define RAWLANE2_DIG_PCS_XF_RX_OVRD_OUT 0x320e
+#define RAWLANE3_DIG_PCS_XF_RX_OVRD_OUT 0x330e
+#define PCS_ACK_WRITE_SELECT BIT(14)
+#define PCS_EN_CTL BIT(1)
+#define PCS_ACK BIT(0)
+
+#define RAWLANE0_DIG_AON_FAST_FLAGS 0x305c
+#define RAWLANE1_DIG_AON_FAST_FLAGS 0x315c
+#define RAWLANE2_DIG_AON_FAST_FLAGS 0x325c
+#define RAWLANE3_DIG_AON_FAST_FLAGS 0x335c
+
+/* HDMIRX Ctrler */
+#define GLOBAL_SWRESET_REQUEST 0x0020
+#define DATAPATH_SWRESETREQ BIT(12)
+#define GLOBAL_SWENABLE 0x0024
+#define PHYCTRL_ENABLE BIT(21)
+#define CEC_ENABLE BIT(16)
+#define TMDS_ENABLE BIT(13)
+#define DATAPATH_ENABLE BIT(12)
+#define PKTFIFO_ENABLE BIT(11)
+#define AVPUNIT_ENABLE BIT(8)
+#define MAIN_ENABLE BIT(0)
+#define GLOBAL_TIMER_REF_BASE 0x0028
+#define CORE_CONFIG 0x0050
+#define CMU_CONFIG0 0x0060
+#define TMDSQPCLK_STABLE_FREQ_MARGIN_MASK GENMASK(30, 16)
+#define TMDSQPCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 30, 16)
+#define AUDCLK_STABLE_FREQ_MARGIN_MASK GENMASK(11, 9)
+#define AUDCLK_STABLE_FREQ_MARGIN(x) UPDATE(x, 11, 9)
+#define CMU_STATUS 0x007c
+#define TMDSQPCLK_LOCKED_ST BIT(4)
+#define CMU_TMDSQPCLK_FREQ 0x0084
+#define PHY_CONFIG 0x00c0
+#define LDO_AFE_PROG_MASK GENMASK(24, 23)
+#define LDO_AFE_PROG(x) UPDATE(x, 24, 23)
+#define LDO_PWRDN BIT(21)
+#define TMDS_CLOCK_RATIO BIT(16)
+#define RXDATA_WIDTH BIT(15)
+#define REFFREQ_SEL_MASK GENMASK(11, 9)
+#define REFFREQ_SEL(x) UPDATE(x, 11, 9)
+#define HDMI_DISABLE BIT(8)
+#define PHY_PDDQ BIT(1)
+#define PHY_RESET BIT(0)
+#define PHY_STATUS 0x00c8
+#define HDMI_DISABLE_ACK BIT(1)
+#define PDDQ_ACK BIT(0)
+#define PHYCREG_CONFIG0 0x00e0
+#define PHYCREG_CR_PARA_SELECTION_MODE_MASK GENMASK(1, 0)
+#define PHYCREG_CR_PARA_SELECTION_MODE(x) UPDATE(x, 1, 0)
+#define PHYCREG_CONFIG1 0x00e4
+#define PHYCREG_CONFIG2 0x00e8
+#define PHYCREG_CONFIG3 0x00ec
+#define PHYCREG_CONTROL 0x00f0
+#define PHYCREG_CR_PARA_WRITE_P BIT(1)
+#define PHYCREG_CR_PARA_READ_P BIT(0)
+#define PHYCREG_STATUS 0x00f4
+
+#define MAINUNIT_STATUS 0x0150
+#define TMDSVALID_STABLE_ST BIT(1)
+#define DESCRAND_EN_CONTROL 0x0210
+#define SCRAMB_EN_SEL_QST_MASK GENMASK(1, 0)
+#define SCRAMB_EN_SEL_QST(x) UPDATE(x, 1, 0)
+#define DESCRAND_SYNC_CONTROL 0x0214
+#define RECOVER_UNSYNC_STREAM_QST BIT(0)
+#define DESCRAND_SYNC_SEQ_CONFIG 0x022c
+#define DESCRAND_SYNC_SEQ_ERR_CNT_EN BIT(0)
+#define DESCRAND_SYNC_SEQ_STATUS 0x0234
+#define DEFRAMER_CONFIG0 0x0270
+#define VS_CNT_THR_QST_MASK GENMASK(27, 20)
+#define VS_CNT_THR_QST(x) UPDATE(x, 27, 20)
+#define HS_POL_QST_MASK GENMASK(19, 18)
+#define HS_POL_QST(x) UPDATE(x, 19, 18)
+#define VS_POL_QST_MASK GENMASK(17, 16)
+#define VS_POL_QST(x) UPDATE(x, 17, 16)
+#define VS_REMAPFILTER_EN_QST BIT(8)
+#define VS_FILTER_ORDER_QST_MASK GENMASK(1, 0)
+#define VS_FILTER_ORDER_QST(x) UPDATE(x, 1, 0)
+#define DEFRAMER_VSYNC_CNT_CLEAR 0x0278
+#define VSYNC_CNT_CLR_P BIT(0)
+#define DEFRAMER_STATUS 0x027c
+#define OPMODE_STS_MASK GENMASK(6, 4)
+#define I2C_SLAVE_CONFIG1 0x0164
+#define I2C_SDA_OUT_HOLD_VALUE_QST_MASK GENMASK(15, 8)
+#define I2C_SDA_OUT_HOLD_VALUE_QST(x) UPDATE(x, 15, 8)
+#define I2C_SDA_IN_HOLD_VALUE_QST_MASK GENMASK(7, 0)
+#define I2C_SDA_IN_HOLD_VALUE_QST(x) UPDATE(x, 7, 0)
+#define OPMODE_STS_MASK GENMASK(6, 4)
+#define REPEATER_QST BIT(28)
+#define FASTREAUTH_QST BIT(27)
+#define FEATURES_1DOT1_QST BIT(26)
+#define FASTI2C_QST BIT(25)
+#define EESS_CTL_THR_QST_MASK GENMASK(19, 16)
+#define EESS_CTL_THR_QST(x) UPDATE(x, 19, 16)
+#define OESS_CTL3_THR_QST_MASK GENMASK(11, 8)
+#define OESS_CTL3_THR_QST(x) UPDATE(x, 11, 8)
+#define EESS_OESS_SEL_QST_MASK GENMASK(5, 4)
+#define EESS_OESS_SEL_QST(x) UPDATE(x, 5, 4)
+#define KEY_DECRYPT_EN_QST BIT(0)
+#define KEY_DECRYPT_SEED_QST_MASK GENMASK(15, 0)
+#define KEY_DECRYPT_SEED_QST(x) UPDATE(x, 15, 0)
+#define HDCP_INT_CLEAR 0x50d8
+#define HDCP_1_INT_CLEAR 0x50e8
+#define HDCP2_CONFIG 0x02f0
+#define HDCP2_SWITCH_OVR_VALUE BIT(2)
+#define HDCP2_SWITCH_OVR_EN BIT(1)
+
+#define VIDEO_CONFIG2 0x042c
+#define VPROC_VSYNC_POL_OVR_VALUE BIT(19)
+#define VPROC_VSYNC_POL_OVR_EN BIT(18)
+#define VPROC_HSYNC_POL_OVR_VALUE BIT(17)
+#define VPROC_HSYNC_POL_OVR_EN BIT(16)
+#define VPROC_FMT_OVR_VALUE_MASK GENMASK(6, 4)
+#define VPROC_FMT_OVR_VALUE(x) UPDATE(x, 6, 4)
+#define VPROC_FMT_OVR_EN BIT(0)
+
+#define AFIFO_FILL_RESTART BIT(0)
+#define AFIFO_INIT_P BIT(0)
+#define AFIFO_THR_LOW_QST_MASK GENMASK(25, 16)
+#define AFIFO_THR_LOW_QST(x) UPDATE(x, 25, 16)
+#define AFIFO_THR_HIGH_QST_MASK GENMASK(9, 0)
+#define AFIFO_THR_HIGH_QST(x) UPDATE(x, 9, 0)
+#define AFIFO_THR_MUTE_LOW_QST_MASK GENMASK(25, 16)
+#define AFIFO_THR_MUTE_LOW_QST(x) UPDATE(x, 25, 16)
+#define AFIFO_THR_MUTE_HIGH_QST_MASK GENMASK(9, 0)
+#define AFIFO_THR_MUTE_HIGH_QST(x) UPDATE(x, 9, 0)
+
+#define AFIFO_UNDERFLOW_ST BIT(25)
+#define AFIFO_OVERFLOW_ST BIT(24)
+
+#define SPEAKER_ALLOC_OVR_EN BIT(16)
+#define I2S_BPCUV_EN BIT(4)
+#define SPDIF_EN BIT(2)
+#define I2S_EN BIT(1)
+#define AFIFO_THR_PASS_DEMUTEMASK_N BIT(24)
+#define AVMUTE_DEMUTEMASK_N BIT(16)
+#define AFIFO_THR_MUTE_LOW_MUTEMASK_N BIT(9)
+#define AFIFO_THR_MUTE_HIGH_MUTEMASK_N BIT(8)
+#define AVMUTE_MUTEMASK_N BIT(0)
+#define SCDC_CONFIG 0x0580
+#define HPDLOW BIT(1)
+#define POWERPROVIDED BIT(0)
+#define SCDC_REGBANK_STATUS1 0x058c
+#define SCDC_TMDSBITCLKRATIO BIT(1)
+#define SCDC_REGBANK_STATUS3 0x0594
+#define SCDC_REGBANK_CONFIG0 0x05c0
+#define SCDC_SINKVERSION_QST_MASK GENMASK(7, 0)
+#define SCDC_SINKVERSION_QST(x) UPDATE(x, 7, 0)
+#define AGEN_LAYOUT BIT(4)
+#define AGEN_SPEAKER_ALLOC GENMASK(15, 8)
+
+#define CED_CONFIG 0x0760
+#define CED_VIDDATACHECKEN_QST BIT(27)
+#define CED_DATAISCHECKEN_QST BIT(26)
+#define CED_GBCHECKEN_QST BIT(25)
+#define CED_CTRLCHECKEN_QST BIT(24)
+#define CED_CHLOCKMAXER_QST_MASK GENMASK(14, 0)
+#define CED_CHLOCKMAXER_QST(x) UPDATE(x, 14, 0)
+#define CED_DYN_CONFIG 0x0768
+#define CED_DYN_CONTROL 0x076c
+#define PKTEX_BCH_ERRFILT_CONFIG 0x07c4
+#define PKTEX_CHKSUM_ERRFILT_CONFIG 0x07c8
+
+#define PKTDEC_ACR_PH2_1 0x1100
+#define PKTDEC_ACR_PB3_0 0x1104
+#define PKTDEC_ACR_PB7_4 0x1108
+#define PKTDEC_AVIIF_PH2_1 0x1200
+#define PKTDEC_AVIIF_PB3_0 0x1204
+#define PKTDEC_AVIIF_PB7_4 0x1208
+#define VIC_VAL_MASK GENMASK(6, 0)
+#define PKTDEC_AVIIF_PB11_8 0x120c
+#define PKTDEC_AVIIF_PB15_12 0x1210
+#define PKTDEC_AVIIF_PB19_16 0x1214
+#define PKTDEC_AVIIF_PB23_20 0x1218
+#define PKTDEC_AVIIF_PB27_24 0x121c
+
+#define PKTFIFO_CONFIG 0x1500
+#define PKTFIFO_STORE_FILT_CONFIG 0x1504
+#define PKTFIFO_THR_CONFIG0 0x1508
+#define PKTFIFO_THR_CONFIG1 0x150c
+#define PKTFIFO_CONTROL 0x1510
+
+#define VMON_STATUS1 0x1580
+#define VMON_STATUS2 0x1584
+#define VMON_STATUS3 0x1588
+#define VMON_STATUS4 0x158c
+#define VMON_STATUS5 0x1590
+#define VMON_STATUS6 0x1594
+#define VMON_STATUS7 0x1598
+#define VMON_ILACE_DETECT BIT(4)
+
+#define CEC_TX_CONTROL 0x2000
+#define CEC_STATUS 0x2004
+#define CEC_CONFIG 0x2008
+#define RX_AUTO_DRIVE_ACKNOWLEDGE BIT(9)
+#define CEC_ADDR 0x200c
+#define CEC_TX_COUNT 0x2020
+#define CEC_TX_DATA3_0 0x2024
+#define CEC_RX_COUNT_STATUS 0x2040
+#define CEC_RX_DATA3_0 0x2044
+#define CEC_LOCK_CONTROL 0x2054
+#define CEC_RXQUAL_BITTIME_CONFIG 0x2060
+#define CEC_RX_BITTIME_CONFIG 0x2064
+#define CEC_TX_BITTIME_CONFIG 0x2068
+
+#define DMA_CONFIG1 0x4400
+#define UV_WID_MASK GENMASK(31, 28)
+#define UV_WID(x) UPDATE(x, 31, 28)
+#define Y_WID_MASK GENMASK(27, 24)
+#define Y_WID(x) UPDATE(x, 27, 24)
+#define DDR_STORE_FORMAT_MASK GENMASK(15, 12)
+#define DDR_STORE_FORMAT(x) UPDATE(x, 15, 12)
+#define ABANDON_EN BIT(0)
+#define DMA_CONFIG2 0x4404
+#define DMA_CONFIG3 0x4408
+#define DMA_CONFIG4 0x440c // dma irq en
+#define DMA_CONFIG5 0x4410 // dma irq clear status
+#define LINE_FLAG_INT_EN BIT(8)
+#define HDMIRX_DMA_IDLE_INT BIT(7)
+#define HDMIRX_LOCK_DISABLE_INT BIT(6)
+#define LAST_FRAME_AXI_UNFINISH_INT_EN BIT(5)
+#define FIFO_OVERFLOW_INT_EN BIT(2)
+#define FIFO_UNDERFLOW_INT_EN BIT(1)
+#define HDMIRX_AXI_ERROR_INT_EN BIT(0)
+#define DMA_CONFIG6 0x4414
+#define RB_SWAP_EN BIT(9)
+#define HSYNC_TOGGLE_EN BIT(5)
+#define VSYNC_TOGGLE_EN BIT(4)
+#define HDMIRX_DMA_EN BIT(1)
+#define DMA_CONFIG7 0x4418
+#define LINE_FLAG_NUM_MASK GENMASK(31, 16)
+#define LINE_FLAG_NUM(x) UPDATE(x, 31, 16)
+#define LOCK_FRAME_NUM_MASK GENMASK(11, 0)
+#define LOCK_FRAME_NUM(x) UPDATE(x, 11, 0)
+#define DMA_CONFIG8 0x441c
+#define REG_MIRROR_EN BIT(0)
+#define DMA_CONFIG9 0x4420
+#define DMA_CONFIG10 0x4424
+#define DMA_CONFIG11 0x4428
+#define EDID_READ_EN_MASK BIT(8)
+#define EDID_READ_EN(x) UPDATE(x, 8, 8)
+#define EDID_WRITE_EN_MASK BIT(7)
+#define EDID_WRITE_EN(x) UPDATE(x, 7, 7)
+#define EDID_SLAVE_ADDR_MASK GENMASK(6, 0)
+#define EDID_SLAVE_ADDR(x) UPDATE(x, 6, 0)
+#define DMA_STATUS1 0x4430 // dma irq status
+#define DMA_STATUS2 0x4434
+#define DMA_STATUS3 0x4438
+#define DMA_STATUS4 0x443c
+#define DMA_STATUS5 0x4440
+#define DMA_STATUS6 0x4444
+#define DMA_STATUS7 0x4448
+#define DMA_STATUS8 0x444c
+#define DMA_STATUS9 0x4450
+#define DMA_STATUS10 0x4454
+#define HDMIRX_LOCK BIT(3)
+#define DMA_STATUS11 0x4458
+#define HDMIRX_TYPE_MASK GENMASK(8, 7)
+#define HDMIRX_COLOR_DEPTH_MASK GENMASK(6, 3)
+#define HDMIRX_FORMAT_MASK GENMASK(2, 0)
+#define DMA_STATUS12 0x445c
+#define DMA_STATUS13 0x4460
+#define DMA_STATUS14 0x4464
+
+#define MAINUNIT_INTVEC_INDEX 0x5000
+#define MAINUNIT_0_INT_STATUS 0x5010
+#define CECRX_NOTIFY_ERR BIT(12)
+#define CECRX_EOM BIT(11)
+#define CECTX_DRIVE_ERR BIT(10)
+#define CECRX_BUSY BIT(9)
+#define CECTX_BUSY BIT(8)
+#define CECTX_FRAME_DISCARDED BIT(5)
+#define CECTX_NRETRANSMIT_FAIL BIT(4)
+#define CECTX_LINE_ERR BIT(3)
+#define CECTX_ARBLOST BIT(2)
+#define CECTX_NACK BIT(1)
+#define CECTX_DONE BIT(0)
+#define MAINUNIT_0_INT_MASK_N 0x5014
+#define MAINUNIT_0_INT_CLEAR 0x5018
+#define MAINUNIT_0_INT_FORCE 0x501c
+#define TIMER_BASE_LOCKED_IRQ BIT(26)
+#define TMDSQPCLK_OFF_CHG BIT(5)
+#define TMDSQPCLK_LOCKED_CHG BIT(4)
+#define MAINUNIT_1_INT_STATUS 0x5020
+#define MAINUNIT_1_INT_MASK_N 0x5024
+#define MAINUNIT_1_INT_CLEAR 0x5028
+#define MAINUNIT_1_INT_FORCE 0x502c
+#define MAINUNIT_2_INT_STATUS 0x5030
+#define MAINUNIT_2_INT_MASK_N 0x5034
+#define MAINUNIT_2_INT_CLEAR 0x5038
+#define MAINUNIT_2_INT_FORCE 0x503c
+#define PHYCREG_CR_READ_DONE BIT(11)
+#define PHYCREG_CR_WRITE_DONE BIT(10)
+#define TMDSVALID_STABLE_CHG BIT(1)
+
+#define AVPUNIT_0_INT_STATUS 0x5040
+#define AVPUNIT_0_INT_MASK_N 0x5044
+#define AVPUNIT_0_INT_CLEAR 0x5048
+#define AVPUNIT_0_INT_FORCE 0x504c
+#define CED_DYN_CNT_CH2_IRQ BIT(22)
+#define CED_DYN_CNT_CH1_IRQ BIT(21)
+#define CED_DYN_CNT_CH0_IRQ BIT(20)
+#define AVPUNIT_1_INT_STATUS 0x5050
+#define DEFRAMER_VSYNC_THR_REACHED_IRQ BIT(1)
+#define AVPUNIT_1_INT_MASK_N 0x5054
+#define DEFRAMER_VSYNC_THR_REACHED_MASK_N BIT(1)
+#define DEFRAMER_VSYNC_MASK_N BIT(0)
+#define AVPUNIT_1_INT_CLEAR 0x5058
+#define DEFRAMER_VSYNC_THR_REACHED_CLEAR BIT(1)
+#define PKT_0_INT_STATUS 0x5080
+#define PKTDEC_ACR_CHG_IRQ BIT(3)
+#define PKT_0_INT_MASK_N 0x5084
+#define PKTDEC_ACR_CHG_MASK_N BIT(3)
+#define PKT_0_INT_CLEAR 0x5088
+#define PKT_1_INT_STATUS 0x5090
+#define PKT_1_INT_MASK_N 0x5094
+#define PKT_1_INT_CLEAR 0x5098
+#define PKT_2_INT_STATUS 0x50a0
+#define PKTDEC_ACR_RCV_IRQ BIT(3)
+#define PKT_2_INT_MASK_N 0x50a4
+#define PKTDEC_AVIIF_RCV_IRQ BIT(11)
+#define PKTDEC_ACR_RCV_MASK_N BIT(3)
+#define PKT_2_INT_CLEAR 0x50a8
+#define PKTDEC_AVIIF_RCV_CLEAR BIT(11)
+#define PKTDEC_ACR_RCV_CLEAR BIT(3)
+#define SCDC_INT_STATUS 0x50c0
+#define SCDC_INT_MASK_N 0x50c4
+#define SCDC_INT_CLEAR 0x50c8
+#define SCDCTMDSCCFG_CHG BIT(2)
+
+#define CEC_INT_STATUS 0x5100
+#define CEC_INT_MASK_N 0x5104
+#define CEC_INT_CLEAR 0x5108
+
+#endif
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
new file mode 100644
index 000000000000..8e470c0376d6
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chips.com>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <media/cec.h>
+
+#include "snps_hdmirx.h"
+#include "snps_hdmirx_cec.h"
+
+static void hdmirx_cec_write(struct hdmirx_cec *cec, int reg, u32 val)
+{
+ cec->ops->write(cec->hdmirx, reg, val);
+}
+
+static u32 hdmirx_cec_read(struct hdmirx_cec *cec, int reg)
+{
+ return cec->ops->read(cec->hdmirx, reg);
+}
+
+static void hdmirx_cec_update_bits(struct hdmirx_cec *cec, int reg, u32 mask,
+ u32 data)
+{
+ u32 val = hdmirx_cec_read(cec, reg) & ~mask;
+
+ val |= (data & mask);
+ hdmirx_cec_write(cec, reg, val);
+}
+
+static int hdmirx_cec_log_addr(struct cec_adapter *adap, u8 logical_addr)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (logical_addr == CEC_LOG_ADDR_INVALID)
+ cec->addresses = 0;
+ else
+ cec->addresses |= BIT(logical_addr) | BIT(15);
+
+ hdmirx_cec_write(cec, CEC_ADDR, cec->addresses);
+
+ return 0;
+}
+
+/* signal_free_time is handled by the Synopsys Designware
+ * HDMIRX Controller hardware.
+ */
+static int hdmirx_cec_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+ u32 data[4] = {0};
+ int i, data_len, msg_len;
+
+ msg_len = msg->len;
+
+ hdmirx_cec_write(cec, CEC_TX_COUNT, msg_len - 1);
+ for (i = 0; i < msg_len; i++)
+ data[i / 4] |= msg->msg[i] << (i % 4) * 8;
+
+ data_len = DIV_ROUND_UP(msg_len, 4);
+
+ for (i = 0; i < data_len; i++)
+ hdmirx_cec_write(cec, CEC_TX_DATA3_0 + i * 4, data[i]);
+
+ hdmirx_cec_write(cec, CEC_TX_CONTROL, 0x1);
+
+ return 0;
+}
+
+static irqreturn_t hdmirx_cec_hardirq(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+ u32 stat = hdmirx_cec_read(cec, CEC_INT_STATUS);
+ irqreturn_t ret = IRQ_HANDLED;
+ u32 val;
+
+ if (!stat)
+ return IRQ_NONE;
+
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, stat);
+
+ if (stat & CECTX_LINE_ERR) {
+ cec->tx_status = CEC_TX_STATUS_ERROR;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_DONE) {
+ cec->tx_status = CEC_TX_STATUS_OK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_NACK) {
+ cec->tx_status = CEC_TX_STATUS_NACK;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ } else if (stat & CECTX_ARBLOST) {
+ cec->tx_status = CEC_TX_STATUS_ARB_LOST;
+ cec->tx_done = true;
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ if (stat & CECRX_EOM) {
+ unsigned int len, i;
+
+ val = hdmirx_cec_read(cec, CEC_RX_COUNT_STATUS);
+ /* rxbuffer locked status */
+ if ((val & 0x80))
+ return ret;
+
+ len = (val & 0xf) + 1;
+ if (len > sizeof(cec->rx_msg.msg))
+ len = sizeof(cec->rx_msg.msg);
+
+ for (i = 0; i < len; i++) {
+ if (!(i % 4))
+ val = hdmirx_cec_read(cec, CEC_RX_DATA3_0 + i / 4 * 4);
+ cec->rx_msg.msg[i] = (val >> ((i % 4) * 8)) & 0xff;
+ }
+
+ cec->rx_msg.len = len;
+ smp_wmb(); /* receive RX msg */
+ cec->rx_done = true;
+ hdmirx_cec_write(cec, CEC_LOCK_CONTROL, 0x1);
+
+ ret = IRQ_WAKE_THREAD;
+ }
+
+ return ret;
+}
+
+static irqreturn_t hdmirx_cec_thread(int irq, void *data)
+{
+ struct cec_adapter *adap = data;
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (cec->tx_done) {
+ cec->tx_done = false;
+ cec_transmit_attempt_done(adap, cec->tx_status);
+ }
+ if (cec->rx_done) {
+ cec->rx_done = false;
+ smp_rmb(); /* RX msg has been received */
+ cec_received_msg(adap, &cec->rx_msg);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int hdmirx_cec_enable(struct cec_adapter *adap, bool enable)
+{
+ struct hdmirx_cec *cec = cec_get_drvdata(adap);
+
+ if (!enable) {
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, 0);
+ if (cec->ops->disable)
+ cec->ops->disable(cec->hdmirx);
+ } else {
+ unsigned int irqs;
+
+ hdmirx_cec_log_addr(cec->adap, CEC_LOG_ADDR_INVALID);
+ if (cec->ops->enable)
+ cec->ops->enable(cec->hdmirx);
+ hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
+
+ irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
+ }
+
+ return 0;
+}
+
+static const struct cec_adap_ops hdmirx_cec_ops = {
+ .adap_enable = hdmirx_cec_enable,
+ .adap_log_addr = hdmirx_cec_log_addr,
+ .adap_transmit = hdmirx_cec_transmit,
+};
+
+static void hdmirx_cec_del(void *data)
+{
+ struct hdmirx_cec *cec = data;
+
+ cec_delete_adapter(cec->adap);
+}
+
+struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data)
+{
+ struct hdmirx_cec *cec;
+ unsigned int irqs;
+ int ret;
+
+ /*
+ * Our device is just a convenience - we want to link to the real
+ * hardware device here, so that userspace can see the association
+ * between the HDMI hardware and its associated CEC chardev.
+ */
+ cec = devm_kzalloc(data->dev, sizeof(*cec), GFP_KERNEL);
+ if (!cec)
+ return ERR_PTR(-ENOMEM);
+
+ cec->dev = data->dev;
+ cec->irq = data->irq;
+ cec->ops = data->ops;
+ cec->hdmirx = data->hdmirx;
+
+ hdmirx_cec_update_bits(cec, GLOBAL_SWENABLE, CEC_ENABLE, CEC_ENABLE);
+ hdmirx_cec_update_bits(cec, CEC_CONFIG, RX_AUTO_DRIVE_ACKNOWLEDGE,
+ RX_AUTO_DRIVE_ACKNOWLEDGE);
+
+ hdmirx_cec_write(cec, CEC_TX_COUNT, 0);
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, 0);
+ hdmirx_cec_write(cec, CEC_INT_CLEAR, ~0);
+
+ cec->adap = cec_allocate_adapter(&hdmirx_cec_ops, cec, "snps-hdmirx",
+ CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL,
+ CEC_MAX_LOG_ADDRS);
+ if (IS_ERR(cec->adap)) {
+ dev_err(cec->dev, "cec adapter allocation failed\n");
+ return ERR_CAST(cec->adap);
+ }
+
+ /* override the module pointer */
+ cec->adap->owner = THIS_MODULE;
+
+ ret = devm_add_action(cec->dev, hdmirx_cec_del, cec);
+ if (ret) {
+ cec_delete_adapter(cec->adap);
+ return ERR_PTR(ret);
+ }
+
+ irq_set_status_flags(cec->irq, IRQ_NOAUTOEN);
+
+ ret = devm_request_threaded_irq(cec->dev, cec->irq,
+ hdmirx_cec_hardirq,
+ hdmirx_cec_thread, IRQF_ONESHOT,
+ "rk_hdmirx_cec", cec->adap);
+ if (ret) {
+ dev_err(cec->dev, "cec irq request failed\n");
+ return ERR_PTR(ret);
+ }
+
+ ret = cec_register_adapter(cec->adap, cec->dev);
+ if (ret < 0) {
+ dev_err(cec->dev, "cec adapter registration failed\n");
+ return ERR_PTR(ret);
+ }
+
+ irqs = CECTX_LINE_ERR | CECTX_NACK | CECRX_EOM | CECTX_DONE;
+ hdmirx_cec_write(cec, CEC_INT_MASK_N, irqs);
+
+ /*
+ * CEC documentation says we must not call cec_delete_adapter
+ * after a successful call to cec_register_adapter().
+ */
+ devm_remove_action(cec->dev, hdmirx_cec_del, cec);
+
+ enable_irq(cec->irq);
+
+ return cec;
+}
+
+void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec)
+{
+ disable_irq(cec->irq);
+
+ cec_unregister_adapter(cec->adap);
+}
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
new file mode 100644
index 000000000000..1b10da5b8fd4
--- /dev/null
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx_cec.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chips.com>
+ */
+
+#ifndef DW_HDMI_RX_CEC_H
+#define DW_HDMI_RX_CEC_H
+
+struct snps_hdmirx_dev;
+
+struct hdmirx_cec_ops {
+ void (*write)(struct snps_hdmirx_dev *hdmirx_dev, int reg, u32 val);
+ u32 (*read)(struct snps_hdmirx_dev *hdmirx_dev, int reg);
+ void (*enable)(struct snps_hdmirx_dev *hdmirx);
+ void (*disable)(struct snps_hdmirx_dev *hdmirx);
+};
+
+struct hdmirx_cec_data {
+ struct snps_hdmirx_dev *hdmirx;
+ const struct hdmirx_cec_ops *ops;
+ struct device *dev;
+ int irq;
+};
+
+struct hdmirx_cec {
+ struct snps_hdmirx_dev *hdmirx;
+ struct device *dev;
+ const struct hdmirx_cec_ops *ops;
+ u32 addresses;
+ struct cec_adapter *adap;
+ struct cec_msg rx_msg;
+ unsigned int tx_status;
+ bool tx_done;
+ bool rx_done;
+ int irq;
+};
+
+struct hdmirx_cec *snps_hdmirx_cec_register(struct hdmirx_cec_data *data);
+void snps_hdmirx_cec_unregister(struct hdmirx_cec *cec);
+
+#endif /* DW_HDMI_RX_CEC_H */
diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c
index 77e12457d149..1ca559df7e59 100644
--- a/drivers/media/platform/ti/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c
@@ -2030,7 +2030,7 @@ vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s)
vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline *
vpfe->fmt.fmt.pix.height;
- vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n",
+ vpfe_dbg(1, vpfe, "cropped (%d,%d)/%ux%u of %dx%d\n",
r.left, r.top, r.width, r.height, cr.width, cr.height);
return 0;
@@ -2079,8 +2079,6 @@ static long vpfe_ioctl_default(struct file *file, void *priv,
}
static const struct vb2_ops vpfe_video_qops = {
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.queue_setup = vpfe_queue_setup,
.buf_prepare = vpfe_buffer_prepare,
.buf_queue = vpfe_buffer_queue,
@@ -2287,7 +2285,7 @@ static const struct v4l2_async_notifier_operations vpfe_async_ops = {
static struct vpfe_config *
vpfe_get_pdata(struct vpfe_device *vpfe)
{
- struct device_node *endpoint = NULL;
+ struct device_node *endpoint;
struct device *dev = vpfe->pdev;
struct vpfe_subdev_info *sdinfo;
struct vpfe_config *pdata;
@@ -2306,14 +2304,11 @@ vpfe_get_pdata(struct vpfe_device *vpfe)
if (!pdata)
return NULL;
- for (i = 0; ; i++) {
+ i = 0;
+ for_each_endpoint_of_node(dev->of_node, endpoint) {
struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
struct device_node *rem;
- endpoint = of_graph_get_next_endpoint(dev->of_node, endpoint);
- if (!endpoint)
- break;
-
sdinfo = &pdata->sub_devs[i];
sdinfo->grp_id = 0;
@@ -2371,9 +2366,10 @@ vpfe_get_pdata(struct vpfe_device *vpfe)
of_node_put(rem);
if (IS_ERR(pdata->asd[i]))
goto cleanup;
+
+ i++;
}
- of_node_put(endpoint);
return pdata;
cleanup:
@@ -2619,7 +2615,7 @@ MODULE_DEVICE_TABLE(of, vpfe_of_match);
static struct platform_driver vpfe_driver = {
.probe = vpfe_probe,
- .remove_new = vpfe_remove,
+ .remove = vpfe_remove,
.driver = {
.name = VPFE_MODULE_NAME,
.pm = &vpfe_pm_ops,
diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c
index 4afc2ad00330..00a71dac0ff4 100644
--- a/drivers/media/platform/ti/cal/cal-camerarx.c
+++ b/drivers/media/platform/ti/cal/cal-camerarx.c
@@ -49,23 +49,41 @@ static s64 cal_camerarx_get_ext_link_freq(struct cal_camerarx *phy)
{
struct v4l2_mbus_config_mipi_csi2 *mipi_csi2 = &phy->endpoint.bus.mipi_csi2;
u32 num_lanes = mipi_csi2->num_data_lanes;
- const struct cal_format_info *fmtinfo;
struct v4l2_subdev_state *state;
- struct v4l2_mbus_framefmt *fmt;
u32 bpp;
s64 freq;
+ /*
+ * v4l2_get_link_freq() uses V4L2_CID_LINK_FREQ first, and falls back
+ * to V4L2_CID_PIXEL_RATE if V4L2_CID_LINK_FREQ is not available.
+ *
+ * With multistream input there is no single pixel rate, and thus we
+ * cannot use V4L2_CID_PIXEL_RATE, so we pass 0 as the bpp which
+ * causes v4l2_get_link_freq() to return an error if it falls back to
+ * V4L2_CID_PIXEL_RATE.
+ */
+
state = v4l2_subdev_get_locked_active_state(&phy->subdev);
- fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK);
+ if (state->routing.num_routes > 1) {
+ bpp = 0;
+ } else {
+ struct v4l2_subdev_route *route = &state->routing.routes[0];
+ const struct cal_format_info *fmtinfo;
+ struct v4l2_mbus_framefmt *fmt;
- fmtinfo = cal_format_by_code(fmt->code);
- if (!fmtinfo)
- return -EINVAL;
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
- bpp = fmtinfo->bpp;
+ fmtinfo = cal_format_by_code(fmt->code);
+ if (!fmtinfo)
+ return -EINVAL;
- freq = v4l2_get_link_freq(phy->source->ctrl_handler, bpp, 2 * num_lanes);
+ bpp = fmtinfo->bpp;
+ }
+
+ freq = v4l2_get_link_freq(&phy->source->entity.pads[phy->source_pad],
+ bpp, 2 * num_lanes);
if (freq < 0) {
phy_err(phy, "failed to get link freq for subdev '%s'\n",
phy->source->name);
@@ -284,15 +302,32 @@ static void cal_camerarx_ppi_disable(struct cal_camerarx *phy)
0, CAL_CSI2_PPI_CTRL_IF_EN_MASK);
}
-static int cal_camerarx_start(struct cal_camerarx *phy)
+static int cal_camerarx_start(struct cal_camerarx *phy, u32 sink_stream)
{
+ struct media_pad *remote_pad;
s64 link_freq;
u32 sscounter;
u32 val;
int ret;
+ remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);
+
+ /*
+ * We need to enable the PHY hardware when enabling the first stream,
+ * but for the following streams we just propagate the enable_streams
+ * to the source.
+ */
+
if (phy->enable_count > 0) {
+ ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index,
+ BIT(sink_stream));
+ if (ret) {
+ phy_err(phy, "enable streams failed in source: %d\n", ret);
+ return ret;
+ }
+
phy->enable_count++;
+
return 0;
}
@@ -394,7 +429,8 @@ static int cal_camerarx_start(struct cal_camerarx *phy)
* Start the source to enable the CSI-2 HS clock. We can now wait for
* CSI-2 PHY reset to complete.
*/
- ret = v4l2_subdev_call(phy->source, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(phy->source, remote_pad->index,
+ BIT(sink_stream));
if (ret) {
v4l2_subdev_call(phy->source, core, s_power, 0);
cal_camerarx_disable_irqs(phy);
@@ -425,12 +461,22 @@ static int cal_camerarx_start(struct cal_camerarx *phy)
return 0;
}
-static void cal_camerarx_stop(struct cal_camerarx *phy)
+static void cal_camerarx_stop(struct cal_camerarx *phy, u32 sink_stream)
{
+ struct media_pad *remote_pad;
int ret;
- if (--phy->enable_count > 0)
+ remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);
+
+ if (--phy->enable_count > 0) {
+ ret = v4l2_subdev_disable_streams(phy->source,
+ remote_pad->index,
+ BIT(sink_stream));
+ if (ret)
+ phy_err(phy, "stream off failed in subdev\n");
+
return;
+ }
cal_camerarx_ppi_disable(phy);
@@ -450,7 +496,9 @@ static void cal_camerarx_stop(struct cal_camerarx *phy)
/* Disable the phy */
cal_camerarx_disable(phy);
- if (v4l2_subdev_call(phy->source, video, s_stream, 0))
+ ret = v4l2_subdev_disable_streams(phy->source, remote_pad->index,
+ BIT(sink_stream));
+ if (ret)
phy_err(phy, "stream off failed in subdev\n");
ret = v4l2_subdev_call(phy->source, core, s_power, 0);
@@ -599,22 +647,50 @@ static inline struct cal_camerarx *to_cal_camerarx(struct v4l2_subdev *sd)
return container_of(sd, struct cal_camerarx, subdev);
}
-static int cal_camerarx_sd_s_stream(struct v4l2_subdev *sd, int enable)
+struct cal_camerarx *
+cal_camerarx_get_phy_from_entity(struct media_entity *entity)
+{
+ struct v4l2_subdev *sd;
+
+ sd = media_entity_to_v4l2_subdev(entity);
+ if (!sd)
+ return NULL;
+
+ return to_cal_camerarx(sd);
+}
+
+static int cal_camerarx_sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
{
struct cal_camerarx *phy = to_cal_camerarx(sd);
- struct v4l2_subdev_state *state;
- int ret = 0;
+ u32 sink_stream;
+ int ret;
- state = v4l2_subdev_lock_and_get_active_state(sd);
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ NULL, &sink_stream);
+ if (ret)
+ return ret;
- if (enable)
- ret = cal_camerarx_start(phy);
- else
- cal_camerarx_stop(phy);
+ return cal_camerarx_start(phy, sink_stream);
+}
- v4l2_subdev_unlock_state(state);
+static int cal_camerarx_sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct cal_camerarx *phy = to_cal_camerarx(sd);
+ u32 sink_stream;
+ int ret;
- return ret;
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, 0,
+ NULL, &sink_stream);
+ if (ret)
+ return ret;
+
+ cal_camerarx_stop(phy, sink_stream);
+
+ return 0;
}
static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
@@ -628,8 +704,12 @@ static int cal_camerarx_sd_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index > 0)
return -EINVAL;
- fmt = v4l2_subdev_state_get_format(state,
- CAL_CAMERARX_PAD_SINK);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ code->pad,
+ code->stream);
+ if (!fmt)
+ return -EINVAL;
+
code->code = fmt->code;
} else {
if (code->index >= cal_num_formats)
@@ -654,8 +734,12 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd,
if (cal_rx_pad_is_source(fse->pad)) {
struct v4l2_mbus_framefmt *fmt;
- fmt = v4l2_subdev_state_get_format(state,
- CAL_CAMERARX_PAD_SINK);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ fse->pad,
+ fse->stream);
+ if (!fmt)
+ return -EINVAL;
+
if (fse->code != fmt->code)
return -EINVAL;
@@ -711,36 +795,77 @@ static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd,
/* Store the format and propagate it to the source pad. */
- fmt = v4l2_subdev_state_get_format(state, CAL_CAMERARX_PAD_SINK);
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
*fmt = format->format;
- fmt = v4l2_subdev_state_get_format(state,
- CAL_CAMERARX_PAD_FIRST_SOURCE);
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
*fmt = format->format;
return 0;
}
+static int cal_camerarx_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ static const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int cal_camerarx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ return cal_camerarx_set_routing(sd, state, routing);
+}
+
static int cal_camerarx_sd_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
- struct v4l2_subdev_format format = {
- .which = state ? V4L2_SUBDEV_FORMAT_TRY
- : V4L2_SUBDEV_FORMAT_ACTIVE,
- .pad = CAL_CAMERARX_PAD_SINK,
- .format = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .ycbcr_enc = V4L2_YCBCR_ENC_601,
- .quantization = V4L2_QUANTIZATION_LIM_RANGE,
- .xfer_func = V4L2_XFER_FUNC_SRGB,
- },
+ struct v4l2_subdev_route routes[] = { {
+ .sink_pad = 0,
+ .sink_stream = 0,
+ .source_pad = 1,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ } };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = 1,
+ .routes = routes,
};
- return cal_camerarx_sd_set_fmt(sd, state, &format);
+ /* Initialize routing to single route to the fist source pad */
+ return cal_camerarx_set_routing(sd, state, &routing);
}
static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
@@ -749,48 +874,71 @@ static int cal_camerarx_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
struct cal_camerarx *phy = to_cal_camerarx(sd);
struct v4l2_mbus_frame_desc remote_desc;
const struct media_pad *remote_pad;
+ struct v4l2_subdev_state *state;
+ u32 sink_stream;
+ unsigned int i;
int ret;
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ pad, 0,
+ NULL, &sink_stream);
+ if (ret)
+ goto out_unlock;
+
remote_pad = media_pad_remote_pad_first(&phy->pads[CAL_CAMERARX_PAD_SINK]);
- if (!remote_pad)
- return -EPIPE;
+ if (!remote_pad) {
+ ret = -EPIPE;
+ goto out_unlock;
+ }
ret = v4l2_subdev_call(phy->source, pad, get_frame_desc,
remote_pad->index, &remote_desc);
if (ret)
- return ret;
+ goto out_unlock;
if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
cal_err(phy->cal,
"Frame descriptor does not describe CSI-2 link");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out_unlock;
}
- if (remote_desc.num_entries > 1)
- cal_err(phy->cal,
- "Multiple streams not supported in remote frame descriptor, using the first one\n");
+ for (i = 0; i < remote_desc.num_entries; i++) {
+ if (remote_desc.entry[i].stream == sink_stream)
+ break;
+ }
+
+ if (i == remote_desc.num_entries) {
+ cal_err(phy->cal, "Stream %u not found in remote frame desc\n",
+ sink_stream);
+ ret = -EINVAL;
+ goto out_unlock;
+ }
fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
fd->num_entries = 1;
- fd->entry[0] = remote_desc.entry[0];
+ fd->entry[0] = remote_desc.entry[i];
- return 0;
-}
+out_unlock:
+ v4l2_subdev_unlock_state(state);
-static const struct v4l2_subdev_video_ops cal_camerarx_video_ops = {
- .s_stream = cal_camerarx_sd_s_stream,
-};
+ return ret;
+}
static const struct v4l2_subdev_pad_ops cal_camerarx_pad_ops = {
+ .enable_streams = cal_camerarx_sd_enable_streams,
+ .disable_streams = cal_camerarx_sd_disable_streams,
.enum_mbus_code = cal_camerarx_sd_enum_mbus_code,
.enum_frame_size = cal_camerarx_sd_enum_frame_size,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = cal_camerarx_sd_set_fmt,
+ .set_routing = cal_camerarx_sd_set_routing,
.get_frame_desc = cal_camerarx_get_frame_desc,
};
static const struct v4l2_subdev_ops cal_camerarx_subdev_ops = {
- .video = &cal_camerarx_video_ops,
.pad = &cal_camerarx_pad_ops,
};
@@ -798,8 +946,9 @@ static const struct v4l2_subdev_internal_ops cal_camerarx_internal_ops = {
.init_state = cal_camerarx_sd_init_state,
};
-static struct media_entity_operations cal_camerarx_media_ops = {
+static const struct media_entity_operations cal_camerarx_media_ops = {
.link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
};
/* ------------------------------------------------------------------
@@ -851,7 +1000,7 @@ struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
v4l2_subdev_init(sd, &cal_camerarx_subdev_ops);
sd->internal_ops = &cal_camerarx_internal_ops;
sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
- sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
snprintf(sd->name, sizeof(sd->name), "CAMERARX%u", instance);
sd->dev = cal->dev;
diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c
index e1ba5dfc217e..d40e24ab1127 100644
--- a/drivers/media/platform/ti/cal/cal-video.c
+++ b/drivers/media/platform/ti/cal/cal-video.c
@@ -25,20 +25,6 @@
#include "cal.h"
-/* Print Four-character-code (FOURCC) */
-static char *fourcc_to_str(u32 fmt)
-{
- static char code[5];
-
- code[0] = (unsigned char)(fmt & 0xff);
- code[1] = (unsigned char)((fmt >> 8) & 0xff);
- code[2] = (unsigned char)((fmt >> 16) & 0xff);
- code[3] = (unsigned char)((fmt >> 24) & 0xff);
- code[4] = '\0';
-
- return code;
-}
-
/* ------------------------------------------------------------------
* V4L2 Common IOCTLs
* ------------------------------------------------------------------
@@ -122,9 +108,10 @@ static int __subdev_get_format(struct cal_ctx *ctx,
.pad = 0,
};
struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ struct v4l2_subdev *sd = ctx->phy->source;
int ret;
- ret = v4l2_subdev_call(ctx->phy->source, pad, get_fmt, NULL, &sd_fmt);
+ ret = v4l2_subdev_call_state_active(sd, pad, get_fmt, &sd_fmt);
if (ret)
return ret;
@@ -144,11 +131,12 @@ static int __subdev_set_format(struct cal_ctx *ctx,
.pad = 0,
};
struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format;
+ struct v4l2_subdev *sd = ctx->phy->source;
int ret;
*mbus_fmt = *fmt;
- ret = v4l2_subdev_call(ctx->phy->source, pad, set_fmt, NULL, &sd_fmt);
+ ret = v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt);
if (ret)
return ret;
@@ -180,8 +168,8 @@ static void cal_calc_format_size(struct cal_ctx *ctx,
f->fmt.pix.sizeimage = f->fmt.pix.height *
f->fmt.pix.bytesperline;
- ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n",
- __func__, fourcc_to_str(f->fmt.pix.pixelformat),
+ ctx_dbg(3, ctx, "%s: fourcc: %p4cc size: %dx%d bpl:%d img_size:%d\n",
+ __func__, &f->fmt.pix.pixelformat,
f->fmt.pix.width, f->fmt.pix.height,
f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
}
@@ -190,6 +178,7 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = ctx->phy->source;
const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_size_enum fse = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
@@ -215,8 +204,8 @@ static int cal_legacy_try_fmt_vid_cap(struct file *file, void *priv,
for (fse.index = 0; ; fse.index++) {
int ret;
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size,
- NULL, &fse);
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_size,
+ &fse);
if (ret)
break;
@@ -252,6 +241,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = &ctx->phy->subdev;
struct vb2_queue *q = &ctx->vb_vidq;
struct v4l2_subdev_format sd_fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
@@ -291,7 +281,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv,
ctx->v_fmt.fmt.pix.field = sd_fmt.format.field;
cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
- v4l2_subdev_call(&ctx->phy->subdev, pad, set_fmt, NULL, &sd_fmt);
+ v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt);
ctx->fmtinfo = fmtinfo;
*f = ctx->v_fmt;
@@ -303,6 +293,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = ctx->phy->source;
const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_size_enum fse = {
.index = fsize->index,
@@ -321,8 +312,7 @@ static int cal_legacy_enum_framesizes(struct file *file, void *fh,
fse.code = fmtinfo->code;
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_size, NULL,
- &fse);
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_size, &fse);
if (ret)
return ret;
@@ -364,6 +354,7 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv,
struct v4l2_frmivalenum *fival)
{
struct cal_ctx *ctx = video_drvdata(file);
+ struct v4l2_subdev *sd = ctx->phy->source;
const struct cal_format_info *fmtinfo;
struct v4l2_subdev_frame_interval_enum fie = {
.index = fival->index,
@@ -378,8 +369,8 @@ static int cal_legacy_enum_frameintervals(struct file *file, void *priv,
return -EINVAL;
fie.code = fmtinfo->code;
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_frame_interval,
- NULL, &fie);
+
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_frame_interval, &fie);
if (ret)
return ret;
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
@@ -446,6 +437,9 @@ static int cal_mc_enum_fmt_vid_cap(struct file *file, void *priv,
idx = 0;
for (i = 0; i < cal_num_formats; ++i) {
+ if (cal_formats[i].meta)
+ continue;
+
if (f->mbus_code && cal_formats[i].code != f->mbus_code)
continue;
@@ -473,7 +467,7 @@ static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f,
* supported.
*/
fmtinfo = cal_format_by_fourcc(f->fmt.pix.pixelformat);
- if (!fmtinfo)
+ if (!fmtinfo || fmtinfo->meta)
fmtinfo = &cal_formats[0];
/*
@@ -509,8 +503,8 @@ static void cal_mc_try_fmt(struct cal_ctx *ctx, struct v4l2_format *f,
if (info)
*info = fmtinfo;
- ctx_dbg(3, ctx, "%s: %s %ux%u (bytesperline %u sizeimage %u)\n",
- __func__, fourcc_to_str(format->pixelformat),
+ ctx_dbg(3, ctx, "%s: %p4cc %ux%u (bytesperline %u sizeimage %u)\n",
+ __func__, &format->pixelformat,
format->width, format->height,
format->bytesperline, format->sizeimage);
}
@@ -689,16 +683,16 @@ static int cal_video_check_format(struct cal_ctx *ctx)
{
const struct v4l2_mbus_framefmt *format;
struct v4l2_subdev_state *state;
- struct media_pad *remote_pad;
+ struct media_pad *phy_source_pad;
int ret = 0;
- remote_pad = media_pad_remote_pad_first(&ctx->pad);
- if (!remote_pad)
+ phy_source_pad = media_pad_remote_pad_first(&ctx->pad);
+ if (!phy_source_pad)
return -ENODEV;
state = v4l2_subdev_lock_and_get_active_state(&ctx->phy->subdev);
- format = v4l2_subdev_state_get_format(state, remote_pad->index);
+ format = v4l2_subdev_state_get_format(state, phy_source_pad->index, 0);
if (!format) {
ret = -EINVAL;
goto out;
@@ -721,16 +715,28 @@ out:
static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct media_pad *phy_source_pad;
struct cal_buffer *buf;
dma_addr_t addr;
int ret;
+ phy_source_pad = media_pad_remote_pad_first(&ctx->pad);
+ if (!phy_source_pad) {
+ ctx_err(ctx, "Context not connected\n");
+ ret = -ENODEV;
+ goto error_release_buffers;
+ }
+
ret = video_device_pipeline_alloc_start(&ctx->vdev);
if (ret < 0) {
ctx_err(ctx, "Failed to start media pipeline: %d\n", ret);
goto error_release_buffers;
}
+ /* Find the PHY connected to this video device */
+ if (cal_mc_api)
+ ctx->phy = cal_camerarx_get_phy_from_entity(phy_source_pad->entity);
+
/*
* Verify that the currently configured format matches the output of
* the connected CAMERARX.
@@ -758,12 +764,13 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
ret = pm_runtime_resume_and_get(ctx->cal->dev);
if (ret < 0)
- goto error_pipeline;
+ goto error_unprepare;
cal_ctx_set_dma_addr(ctx, addr);
cal_ctx_start(ctx);
- ret = v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 1);
+ ret = v4l2_subdev_enable_streams(&ctx->phy->subdev,
+ phy_source_pad->index, BIT(0));
if (ret)
goto error_stop;
@@ -775,8 +782,8 @@ static int cal_start_streaming(struct vb2_queue *vq, unsigned int count)
error_stop:
cal_ctx_stop(ctx);
pm_runtime_put_sync(ctx->cal->dev);
+error_unprepare:
cal_ctx_unprepare(ctx);
-
error_pipeline:
video_device_pipeline_stop(&ctx->vdev);
error_release_buffers:
@@ -788,10 +795,14 @@ error_release_buffers:
static void cal_stop_streaming(struct vb2_queue *vq)
{
struct cal_ctx *ctx = vb2_get_drv_priv(vq);
+ struct media_pad *phy_source_pad;
cal_ctx_stop(ctx);
- v4l2_subdev_call(&ctx->phy->subdev, video, s_stream, 0);
+ phy_source_pad = media_pad_remote_pad_first(&ctx->pad);
+
+ v4l2_subdev_disable_streams(&ctx->phy->subdev, phy_source_pad->index,
+ BIT(0));
pm_runtime_put_sync(ctx->cal->dev);
@@ -800,6 +811,9 @@ static void cal_stop_streaming(struct vb2_queue *vq)
cal_release_buffers(ctx, VB2_BUF_STATE_ERROR);
video_device_pipeline_stop(&ctx->vdev);
+
+ if (cal_mc_api)
+ ctx->phy = NULL;
}
static const struct vb2_ops cal_video_qops = {
@@ -808,8 +822,6 @@ static const struct vb2_ops cal_video_qops = {
.buf_queue = cal_buffer_queue,
.start_streaming = cal_start_streaming,
.stop_streaming = cal_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/* ------------------------------------------------------------------
@@ -828,6 +840,7 @@ static const struct v4l2_file_operations cal_fops = {
static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
{
+ struct v4l2_subdev *sd = ctx->phy->source;
struct v4l2_mbus_framefmt mbus_fmt;
const struct cal_format_info *fmtinfo;
unsigned int i, j, k;
@@ -847,20 +860,20 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
- ret = v4l2_subdev_call(ctx->phy->source, pad, enum_mbus_code,
- NULL, &mbus_code);
+ ret = v4l2_subdev_call_state_active(sd, pad, enum_mbus_code,
+ &mbus_code);
if (ret == -EINVAL)
break;
if (ret) {
ctx_err(ctx, "Error enumerating mbus codes in subdev %s: %d\n",
- ctx->phy->source->name, ret);
+ sd->name, ret);
return ret;
}
ctx_dbg(2, ctx,
"subdev %s: code: %04x idx: %u\n",
- ctx->phy->source->name, mbus_code.code, j);
+ sd->name, mbus_code.code, j);
for (k = 0; k < cal_num_formats; k++) {
fmtinfo = &cal_formats[k];
@@ -868,8 +881,8 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
if (mbus_code.code == fmtinfo->code) {
ctx->active_fmt[i] = fmtinfo;
ctx_dbg(2, ctx,
- "matched fourcc: %s: code: %04x idx: %u\n",
- fourcc_to_str(fmtinfo->fourcc),
+ "matched fourcc: %p4cc: code: %04x idx: %u\n",
+ &fmtinfo->fourcc,
fmtinfo->code, i);
ctx->num_active_fmt = ++i;
}
@@ -878,7 +891,7 @@ static int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx)
if (i == 0) {
ctx_err(ctx, "No suitable format reported by subdev %s\n",
- ctx->phy->source->name);
+ sd->name);
return -EINVAL;
}
@@ -964,16 +977,52 @@ int cal_ctx_v4l2_register(struct cal_ctx *ctx)
return ret;
}
- ret = media_create_pad_link(&ctx->phy->subdev.entity,
- CAL_CAMERARX_PAD_FIRST_SOURCE,
- &vfd->entity, 0,
- MEDIA_LNK_FL_IMMUTABLE |
- MEDIA_LNK_FL_ENABLED);
- if (ret) {
- ctx_err(ctx, "Failed to create media link for context %u\n",
- ctx->dma_ctx);
- video_unregister_device(vfd);
- return ret;
+ if (cal_mc_api) {
+ u16 phy_idx;
+ u16 pad_idx;
+
+ /* Create links from all video nodes to all PHYs */
+
+ for (phy_idx = 0; phy_idx < ctx->cal->data->num_csi2_phy;
+ ++phy_idx) {
+ struct media_entity *phy_entity =
+ &ctx->cal->phy[phy_idx]->subdev.entity;
+
+ for (pad_idx = 1; pad_idx < CAL_CAMERARX_NUM_PADS;
+ ++pad_idx) {
+ /*
+ * Enable only links from video0 to PHY0 pad 1,
+ * and video1 to PHY1 pad 1.
+ */
+ bool enable = (ctx->dma_ctx == 0 &&
+ phy_idx == 0 && pad_idx == 1) ||
+ (ctx->dma_ctx == 1 &&
+ phy_idx == 1 && pad_idx == 1);
+
+ ret = media_create_pad_link(phy_entity, pad_idx,
+ &vfd->entity, 0,
+ enable ? MEDIA_LNK_FL_ENABLED : 0);
+ if (ret) {
+ ctx_err(ctx,
+ "Failed to create media link for context %u\n",
+ ctx->dma_ctx);
+ video_unregister_device(vfd);
+ return ret;
+ }
+ }
+ }
+ } else {
+ ret = media_create_pad_link(&ctx->phy->subdev.entity,
+ CAL_CAMERARX_PAD_FIRST_SOURCE,
+ &vfd->entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ ctx_err(ctx,
+ "Failed to create media link for context %u\n",
+ ctx->dma_ctx);
+ video_unregister_device(vfd);
+ return ret;
+ }
}
ctx_info(ctx, "V4L2 device registered as %s\n",
diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c
index 528909ae4bd6..b644ed890412 100644
--- a/drivers/media/platform/ti/cal/cal.c
+++ b/drivers/media/platform/ti/cal/cal.c
@@ -481,8 +481,9 @@ int cal_ctx_prepare(struct cal_ctx *ctx)
ctx->vc = 0;
ctx->datatype = CAL_CSI2_CTX_DT_ANY;
} else if (!ret) {
- ctx_dbg(2, ctx, "Framedesc: len %u, vc %u, dt %#x\n",
- entry.length, entry.bus.csi2.vc, entry.bus.csi2.dt);
+ ctx_dbg(2, ctx, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
+ entry.stream, entry.length, entry.bus.csi2.vc,
+ entry.bus.csi2.dt);
ctx->vc = entry.bus.csi2.vc;
ctx->datatype = entry.bus.csi2.dt;
@@ -490,7 +491,7 @@ int cal_ctx_prepare(struct cal_ctx *ctx)
return ret;
}
- ctx->use_pix_proc = !ctx->fmtinfo->meta;
+ ctx->use_pix_proc = ctx->vb_vidq.type == V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ctx->use_pix_proc) {
ret = cal_reserve_pix_proc(ctx->cal);
@@ -549,7 +550,7 @@ void cal_ctx_start(struct cal_ctx *ctx)
void cal_ctx_stop(struct cal_ctx *ctx)
{
struct cal_camerarx *phy = ctx->phy;
- long timeout;
+ long time_left;
WARN_ON(phy->vc_enable_count[ctx->vc] == 0);
@@ -565,9 +566,9 @@ void cal_ctx_stop(struct cal_ctx *ctx)
ctx->dma.state = CAL_DMA_STOP_REQUESTED;
spin_unlock_irq(&ctx->dma.lock);
- timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx),
- msecs_to_jiffies(500));
- if (!timeout) {
+ time_left = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx),
+ msecs_to_jiffies(500));
+ if (!time_left) {
ctx_err(ctx, "failed to disable dma cleanly\n");
cal_ctx_wr_dma_disable(ctx);
}
@@ -798,7 +799,6 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
return 0;
}
- phy->source = subdev;
phy_dbg(1, phy, "Using source %s for capture\n", subdev->name);
pad = media_entity_get_fwnode_pad(&subdev->entity,
@@ -820,6 +820,9 @@ static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier,
return ret;
}
+ phy->source = subdev;
+ phy->source_pad = pad;
+
return 0;
}
@@ -1014,7 +1017,6 @@ static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst)
return NULL;
ctx->cal = cal;
- ctx->phy = cal->phy[inst];
ctx->dma_ctx = inst;
ctx->csi2_ctx = inst;
ctx->cport = inst;
@@ -1226,18 +1228,37 @@ static int cal_probe(struct platform_device *pdev)
}
/* Create contexts. */
- for (i = 0; i < cal->data->num_csi2_phy; ++i) {
- if (!cal->phy[i]->source_node)
- continue;
+ if (!cal_mc_api) {
+ for (i = 0; i < cal->data->num_csi2_phy; ++i) {
+ struct cal_ctx *ctx;
+
+ if (!cal->phy[i]->source_node)
+ continue;
- cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i);
- if (!cal->ctx[cal->num_contexts]) {
- cal_err(cal, "Failed to create context %u\n", cal->num_contexts);
- ret = -ENODEV;
- goto error_context;
+ ctx = cal_ctx_create(cal, i);
+ if (!ctx) {
+ cal_err(cal, "Failed to create context %u\n", cal->num_contexts);
+ ret = -ENODEV;
+ goto error_context;
+ }
+
+ ctx->phy = cal->phy[i];
+
+ cal->ctx[cal->num_contexts++] = ctx;
}
+ } else {
+ for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) {
+ struct cal_ctx *ctx;
+
+ ctx = cal_ctx_create(cal, i);
+ if (!ctx) {
+ cal_err(cal, "Failed to create context %u\n", i);
+ ret = -ENODEV;
+ goto error_context;
+ }
- cal->num_contexts++;
+ cal->ctx[cal->num_contexts++] = ctx;
+ }
}
/* Register the media device. */
@@ -1332,7 +1353,7 @@ static const struct dev_pm_ops cal_pm_ops = {
static struct platform_driver cal_pdrv = {
.probe = cal_probe,
- .remove_new = cal_remove,
+ .remove = cal_remove,
.driver = {
.name = CAL_MODULE_NAME,
.pm = &cal_pm_ops,
diff --git a/drivers/media/platform/ti/cal/cal.h b/drivers/media/platform/ti/cal/cal.h
index 0856297adc0b..33b1885aafe1 100644
--- a/drivers/media/platform/ti/cal/cal.h
+++ b/drivers/media/platform/ti/cal/cal.h
@@ -45,7 +45,7 @@
#define CAL_CAMERARX_PAD_SINK 0
#define CAL_CAMERARX_PAD_FIRST_SOURCE 1
-#define CAL_CAMERARX_NUM_SOURCE_PADS 1
+#define CAL_CAMERARX_NUM_SOURCE_PADS 8
#define CAL_CAMERARX_NUM_PADS (1 + CAL_CAMERARX_NUM_SOURCE_PADS)
static inline bool cal_rx_pad_is_sink(u32 pad)
@@ -174,6 +174,7 @@ struct cal_camerarx {
struct device_node *source_ep_node;
struct device_node *source_node;
struct v4l2_subdev *source;
+ unsigned int source_pad;
struct v4l2_subdev subdev;
struct media_pad pads[CAL_CAMERARX_NUM_PADS];
@@ -319,6 +320,7 @@ const struct cal_format_info *cal_format_by_code(u32 code);
void cal_quickdump_regs(struct cal_dev *cal);
+struct cal_camerarx *cal_camerarx_get_phy_from_entity(struct media_entity *entity);
void cal_camerarx_disable(struct cal_camerarx *phy);
void cal_camerarx_i913_errata(struct cal_camerarx *phy);
struct cal_camerarx *cal_camerarx_create(struct cal_dev *cal,
diff --git a/drivers/media/platform/ti/davinci/vpif.c b/drivers/media/platform/ti/davinci/vpif.c
index f4e1fa76bf37..969d623fc842 100644
--- a/drivers/media/platform/ti/davinci/vpif.c
+++ b/drivers/media/platform/ti/davinci/vpif.c
@@ -504,7 +504,7 @@ static int vpif_probe(struct platform_device *pdev)
pdev_display = kzalloc(sizeof(*pdev_display), GFP_KERNEL);
if (!pdev_display) {
ret = -ENOMEM;
- goto err_put_pdev_capture;
+ goto err_del_pdev_capture;
}
pdev_display->name = "vpif_display";
@@ -527,6 +527,8 @@ static int vpif_probe(struct platform_device *pdev)
err_put_pdev_display:
platform_device_put(pdev_display);
+err_del_pdev_capture:
+ platform_device_del(pdev_capture);
err_put_pdev_capture:
platform_device_put(pdev_capture);
err_put_rpm:
@@ -589,7 +591,7 @@ static struct platform_driver vpif_driver = {
.name = VPIF_DRIVER_NAME,
.pm = vpif_pm_ops,
},
- .remove_new = vpif_remove,
+ .remove = vpif_remove,
.probe = vpif_probe,
};
diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index c31a5566fc5a..d053972888d1 100644
--- a/drivers/media/platform/ti/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -310,8 +310,6 @@ static const struct vb2_ops video_qops = {
.start_streaming = vpif_start_streaming,
.stop_streaming = vpif_stop_streaming,
.buf_queue = vpif_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/**
@@ -1132,7 +1130,7 @@ vpif_query_dv_timings(struct file *file, void *priv,
if (input.capabilities != V4L2_IN_CAP_DV_TIMINGS)
return -ENODATA;
- ret = v4l2_subdev_call(ch->sd, video, query_dv_timings, timings);
+ ret = v4l2_subdev_call(ch->sd, pad, query_dv_timings, 0, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
return -ENODATA;
@@ -1177,7 +1175,7 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
return -EBUSY;
/* Configure subdevice timings, if any */
- ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings);
+ ret = v4l2_subdev_call(ch->sd, pad, s_dv_timings, 0, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
ret = 0;
if (ret < 0) {
@@ -1487,7 +1485,7 @@ static struct vpif_capture_config *
vpif_capture_get_pdata(struct platform_device *pdev,
struct v4l2_device *v4l2_dev)
{
- struct device_node *endpoint = NULL;
+ struct device_node *endpoint;
struct device_node *rem = NULL;
struct vpif_capture_config *pdata;
struct vpif_subdev_info *sdinfo;
@@ -1517,16 +1515,12 @@ vpif_capture_get_pdata(struct platform_device *pdev,
if (!pdata->subdev_info)
return NULL;
- for (i = 0; i < VPIF_CAPTURE_NUM_CHANNELS; i++) {
+ i = 0;
+ for_each_endpoint_of_node(pdev->dev.of_node, endpoint) {
struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 };
unsigned int flags;
int err;
- endpoint = of_graph_get_next_endpoint(pdev->dev.of_node,
- endpoint);
- if (!endpoint)
- break;
-
rem = of_graph_get_remote_port_parent(endpoint);
if (!rem) {
dev_dbg(&pdev->dev, "Remote device at %pOF not found\n",
@@ -1577,6 +1571,10 @@ vpif_capture_get_pdata(struct platform_device *pdev,
goto err_cleanup;
of_node_put(rem);
+
+ i++;
+ if (i >= VPIF_CAPTURE_NUM_CHANNELS)
+ break;
}
done:
@@ -1815,7 +1813,7 @@ static __refdata struct platform_driver vpif_driver = {
.pm = &vpif_pm_ops,
},
.probe = vpif_probe,
- .remove_new = vpif_remove,
+ .remove = vpif_remove,
};
module_platform_driver(vpif_driver);
diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c
index 02ede1fe12cb..70c89549f4b6 100644
--- a/drivers/media/platform/ti/davinci/vpif_display.c
+++ b/drivers/media/platform/ti/davinci/vpif_display.c
@@ -293,8 +293,6 @@ static void vpif_stop_streaming(struct vb2_queue *vq)
static const struct vb2_ops video_qops = {
.queue_setup = vpif_buffer_queue_setup,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.buf_prepare = vpif_buffer_prepare,
.start_streaming = vpif_start_streaming,
.stop_streaming = vpif_stop_streaming,
@@ -934,7 +932,7 @@ static int vpif_s_dv_timings(struct file *file, void *priv,
}
/* Configure subdevice timings, if any */
- ret = v4l2_subdev_call(ch->sd, video, s_dv_timings, timings);
+ ret = v4l2_subdev_call(ch->sd, pad, s_dv_timings, 0, timings);
if (ret == -ENOIOCTLCMD || ret == -ENODEV)
ret = 0;
if (ret < 0) {
@@ -1398,7 +1396,7 @@ static __refdata struct platform_driver vpif_driver = {
.pm = &vpif_pm_ops,
},
.probe = vpif_probe,
- .remove_new = vpif_remove,
+ .remove = vpif_remove,
};
module_platform_driver(vpif_driver);
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 6da83d0cffaa..6412a00be8ea 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -786,15 +786,14 @@ static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
dev_warn(csi->dev,
"Failed to drain DMA. Next frame might be bogus\n");
+ spin_lock_irqsave(&dma->lock, flags);
ret = ti_csi2rx_start_dma(csi, buf);
if (ret) {
- dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
- spin_lock_irqsave(&dma->lock, flags);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
dma->state = TI_CSI2RX_DMA_IDLE;
spin_unlock_irqrestore(&dma->lock, flags);
+ dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
} else {
- spin_lock_irqsave(&dma->lock, flags);
list_add_tail(&buf->list, &dma->submitted);
spin_unlock_irqrestore(&dma->lock, flags);
}
@@ -879,8 +878,6 @@ static const struct vb2_ops csi_vb2_qops = {
.buf_queue = ti_csi2rx_buffer_queue,
.start_streaming = ti_csi2rx_start_streaming,
.stop_streaming = ti_csi2rx_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi)
@@ -1015,9 +1012,9 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
pix_fmt->height = 480;
pix_fmt->field = V4L2_FIELD_NONE;
pix_fmt->colorspace = V4L2_COLORSPACE_SRGB;
- pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601,
- pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE,
- pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB,
+ pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
ti_csi2rx_fill_fmt(fmt, &csi->v_fmt);
@@ -1164,7 +1161,7 @@ MODULE_DEVICE_TABLE(of, ti_csi2rx_of_match);
static struct platform_driver ti_csi2rx_pdrv = {
.probe = ti_csi2rx_probe,
- .remove_new = ti_csi2rx_remove,
+ .remove = ti_csi2rx_remove,
.driver = {
.name = TI_CSI2RX_MODULE_NAME,
.of_match_table = ti_csi2rx_of_match,
diff --git a/drivers/media/platform/ti/omap/omap_vout.c b/drivers/media/platform/ti/omap/omap_vout.c
index 1c56b6a87ced..a87d5030ac35 100644
--- a/drivers/media/platform/ti/omap/omap_vout.c
+++ b/drivers/media/platform/ti/omap/omap_vout.c
@@ -1300,8 +1300,6 @@ static const struct vb2_ops omap_vout_vb2_ops = {
.buf_prepare = omap_vout_vb2_prepare,
.start_streaming = omap_vout_vb2_start_streaming,
.stop_streaming = omap_vout_vb2_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/* Init functions used during driver initialization */
@@ -1721,7 +1719,7 @@ static struct platform_driver omap_vout_driver = {
.driver = {
.name = VOUT_NAME,
},
- .remove_new = omap_vout_remove,
+ .remove = omap_vout_remove,
};
static int __init omap_vout_init(void)
diff --git a/drivers/media/platform/ti/omap/omap_voutdef.h b/drivers/media/platform/ti/omap/omap_voutdef.h
index b586193341d2..159e5e670d91 100644
--- a/drivers/media/platform/ti/omap/omap_voutdef.h
+++ b/drivers/media/platform/ti/omap/omap_voutdef.h
@@ -48,7 +48,7 @@
#define VRFB_TX_TIMEOUT 1000
#define VRFB_NUM_BUFS 4
-/* Max buffer size tobe allocated during init */
+/* Max buffer size to be allocated during init */
#define OMAP_VOUT_MAX_BUF_SIZE (VID_MAX_WIDTH*VID_MAX_HEIGHT*4)
enum dma_channel_state {
diff --git a/drivers/media/platform/ti/omap3isp/isp.c b/drivers/media/platform/ti/omap3isp/isp.c
index 1cda23244c7b..f51cf6119e97 100644
--- a/drivers/media/platform/ti/omap3isp/isp.c
+++ b/drivers/media/platform/ti/omap3isp/isp.c
@@ -1475,43 +1475,6 @@ void omap3isp_put(struct isp_device *isp)
* Platform device driver
*/
-/*
- * omap3isp_print_status - Prints the values of the ISP Control Module registers
- * @isp: OMAP3 ISP device
- */
-#define ISP_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###ISP " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_MAIN, ISP_##name))
-#define SBL_PRINT_REGISTER(isp, name)\
- dev_dbg(isp->dev, "###SBL " #name "=0x%08x\n", \
- isp_reg_readl(isp, OMAP3_ISP_IOMEM_SBL, ISPSBL_##name))
-
-void omap3isp_print_status(struct isp_device *isp)
-{
- dev_dbg(isp->dev, "-------------ISP Register dump--------------\n");
-
- ISP_PRINT_REGISTER(isp, SYSCONFIG);
- ISP_PRINT_REGISTER(isp, SYSSTATUS);
- ISP_PRINT_REGISTER(isp, IRQ0ENABLE);
- ISP_PRINT_REGISTER(isp, IRQ0STATUS);
- ISP_PRINT_REGISTER(isp, TCTRL_GRESET_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_REPLAY);
- ISP_PRINT_REGISTER(isp, CTRL);
- ISP_PRINT_REGISTER(isp, TCTRL_CTRL);
- ISP_PRINT_REGISTER(isp, TCTRL_FRAME);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_STRB_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_SHUT_DELAY);
- ISP_PRINT_REGISTER(isp, TCTRL_PSTRB_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_STRB_LENGTH);
- ISP_PRINT_REGISTER(isp, TCTRL_SHUT_LENGTH);
-
- SBL_PRINT_REGISTER(isp, PCR);
- SBL_PRINT_REGISTER(isp, SDR_REQ_EXP);
-
- dev_dbg(isp->dev, "--------------------------------------------\n");
-}
-
#ifdef CONFIG_PM
/*
@@ -1961,11 +1924,18 @@ static int isp_attach_iommu(struct isp_device *isp)
struct dma_iommu_mapping *mapping;
int ret;
+ /* We always want to replace any default mapping from the arch code */
+ mapping = to_dma_iommu_mapping(isp->dev);
+ if (mapping) {
+ arm_iommu_detach_device(isp->dev);
+ arm_iommu_release_mapping(mapping);
+ }
+
/*
* Create the ARM mapping, used by the ARM DMA mapping core to allocate
* VAs. This will allocate a corresponding IOMMU domain.
*/
- mapping = arm_iommu_create_mapping(&platform_bus_type, SZ_1G, SZ_2G);
+ mapping = arm_iommu_create_mapping(isp->dev, SZ_1G, SZ_2G);
if (IS_ERR(mapping)) {
dev_err(isp->dev, "failed to create ARM IOMMU mapping\n");
return PTR_ERR(mapping);
@@ -2272,18 +2242,14 @@ static int isp_probe(struct platform_device *pdev)
if (ret)
goto error_release_isp;
- isp->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
- "syscon");
+ isp->syscon = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node,
+ "syscon", 1,
+ &isp->syscon_offset);
if (IS_ERR(isp->syscon)) {
ret = PTR_ERR(isp->syscon);
goto error_release_isp;
}
- ret = of_property_read_u32_index(pdev->dev.of_node,
- "syscon", 1, &isp->syscon_offset);
- if (ret)
- goto error_release_isp;
-
isp->autoidle = autoidle;
mutex_init(&isp->isp_mutex);
@@ -2472,7 +2438,7 @@ MODULE_DEVICE_TABLE(of, omap3isp_of_table);
static struct platform_driver omap3isp_driver = {
.probe = isp_probe,
- .remove_new = isp_remove,
+ .remove = isp_remove,
.id_table = omap3isp_id_table,
.driver = {
.name = "omap3isp",
diff --git a/drivers/media/platform/ti/omap3isp/isp.h b/drivers/media/platform/ti/omap3isp/isp.h
index b4793631ad97..60acf3401ac9 100644
--- a/drivers/media/platform/ti/omap3isp/isp.h
+++ b/drivers/media/platform/ti/omap3isp/isp.h
@@ -260,8 +260,6 @@ void omap3isp_configure_bridge(struct isp_device *isp,
struct isp_device *omap3isp_get(struct isp_device *isp);
void omap3isp_put(struct isp_device *isp);
-void omap3isp_print_status(struct isp_device *isp);
-
void omap3isp_sbl_enable(struct isp_device *isp, enum isp_sbl_resource res);
void omap3isp_sbl_disable(struct isp_device *isp, enum isp_sbl_resource res);
diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c
index dd375c4e180d..7d0c723dcd11 100644
--- a/drivers/media/platform/ti/omap3isp/ispccdc.c
+++ b/drivers/media/platform/ti/omap3isp/ispccdc.c
@@ -446,8 +446,8 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
if (ret < 0)
goto done;
- dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl,
- req->table.sgt.nents, DMA_TO_DEVICE);
+ dma_sync_sgtable_for_cpu(isp->dev, &req->table.sgt,
+ DMA_TO_DEVICE);
if (copy_from_user(req->table.addr, config->lsc,
req->config.size)) {
@@ -455,8 +455,8 @@ static int ccdc_lsc_config(struct isp_ccdc_device *ccdc,
goto done;
}
- dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl,
- req->table.sgt.nents, DMA_TO_DEVICE);
+ dma_sync_sgtable_for_device(isp->dev, &req->table.sgt,
+ DMA_TO_DEVICE);
}
spin_lock_irqsave(&ccdc->lsc.req_lock, flags);
diff --git a/drivers/media/platform/ti/omap3isp/ispstat.c b/drivers/media/platform/ti/omap3isp/ispstat.c
index 359a846205b0..d3da68408ecb 100644
--- a/drivers/media/platform/ti/omap3isp/ispstat.c
+++ b/drivers/media/platform/ti/omap3isp/ispstat.c
@@ -161,8 +161,7 @@ static void isp_stat_buf_sync_for_device(struct ispstat *stat,
if (ISP_STAT_USES_DMAENGINE(stat))
return;
- dma_sync_sg_for_device(stat->isp->dev, buf->sgt.sgl,
- buf->sgt.nents, DMA_FROM_DEVICE);
+ dma_sync_sgtable_for_device(stat->isp->dev, &buf->sgt, DMA_FROM_DEVICE);
}
static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
@@ -171,8 +170,7 @@ static void isp_stat_buf_sync_for_cpu(struct ispstat *stat,
if (ISP_STAT_USES_DMAENGINE(stat))
return;
- dma_sync_sg_for_cpu(stat->isp->dev, buf->sgt.sgl,
- buf->sgt.nents, DMA_FROM_DEVICE);
+ dma_sync_sgtable_for_cpu(stat->isp->dev, &buf->sgt, DMA_FROM_DEVICE);
}
static void isp_stat_buf_clear(struct ispstat *stat)
diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c
index daca689dc082..78e30298c7ad 100644
--- a/drivers/media/platform/ti/omap3isp/ispvideo.c
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.c
@@ -1320,6 +1320,7 @@ static int isp_video_open(struct file *file)
queue->buf_struct_size = sizeof(struct isp_buffer);
queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
queue->dev = video->isp->dev;
+ queue->lock = &video->queue_lock;
ret = vb2_queue_init(&handle->queue);
if (ret < 0) {
diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c
index f8998a8ad371..da90d7f03f82 100644
--- a/drivers/media/platform/ti/vpe/vpdma.c
+++ b/drivers/media/platform/ti/vpe/vpdma.c
@@ -1173,4 +1173,5 @@ EXPORT_SYMBOL(vpdma_create);
MODULE_AUTHOR("Texas Instruments Inc.");
MODULE_FIRMWARE(VPDMA_FIRMWARE);
+MODULE_DESCRIPTION("TI VPDMA helper library");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c
index 6848cbc82f52..636d76ecebcd 100644
--- a/drivers/media/platform/ti/vpe/vpe.c
+++ b/drivers/media/platform/ti/vpe/vpe.c
@@ -2210,8 +2210,6 @@ static const struct vb2_ops vpe_qops = {
.queue_setup = vpe_queue_setup,
.buf_prepare = vpe_buf_prepare,
.buf_queue = vpe_buf_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = vpe_start_streaming,
.stop_streaming = vpe_stop_streaming,
};
@@ -2649,7 +2647,7 @@ MODULE_DEVICE_TABLE(of, vpe_of_match);
static struct platform_driver vpe_pdrv = {
.probe = vpe_probe,
- .remove_new = vpe_remove,
+ .remove = vpe_remove,
.driver = {
.name = VPE_MODULE_NAME,
.of_match_table = of_match_ptr(vpe_of_match),
diff --git a/drivers/media/platform/verisilicon/Kconfig b/drivers/media/platform/verisilicon/Kconfig
index 9a34d14c6e40..3272a24db71d 100644
--- a/drivers/media/platform/verisilicon/Kconfig
+++ b/drivers/media/platform/verisilicon/Kconfig
@@ -12,6 +12,7 @@ config VIDEO_HANTRO
select VIDEOBUF2_VMALLOC
select V4L2_MEM2MEM_DEV
select V4L2_H264
+ select V4L2_JPEG_HELPER
select V4L2_VP9
help
Support for the Hantro IP based Video Processing Units present on
@@ -20,6 +21,14 @@ config VIDEO_HANTRO
To compile this driver as a module, choose M here: the module
will be called hantro-vpu.
+config VIDEO_HANTRO_HEVC_RFC
+ bool "Use reference frame compression for HEVC"
+ depends on VIDEO_HANTRO
+ default n
+ help
+ Enable the reference frame compression feature for the HEVC codec.
+ It will use more memory but save bandwidth on memory bus.
+
config VIDEO_HANTRO_IMX8M
bool "Hantro VPU i.MX8M support"
depends on VIDEO_HANTRO
diff --git a/drivers/media/platform/verisilicon/Makefile b/drivers/media/platform/verisilicon/Makefile
index eb38a1833b02..f6f019d04ff0 100644
--- a/drivers/media/platform/verisilicon/Makefile
+++ b/drivers/media/platform/verisilicon/Makefile
@@ -14,13 +14,6 @@ hantro-vpu-y += \
hantro_g2.o \
hantro_g2_hevc_dec.o \
hantro_g2_vp9_dec.o \
- rockchip_vpu2_hw_jpeg_enc.o \
- rockchip_vpu2_hw_h264_dec.o \
- rockchip_vpu2_hw_mpeg2_dec.o \
- rockchip_vpu2_hw_vp8_dec.o \
- rockchip_vpu981_hw_av1_dec.o \
- rockchip_av1_filmgrain.o \
- rockchip_av1_entropymode.o \
hantro_jpeg.o \
hantro_h264.o \
hantro_hevc.o \
@@ -35,6 +28,13 @@ hantro-vpu-$(CONFIG_VIDEO_HANTRO_SAMA5D4) += \
sama5d4_vdec_hw.o
hantro-vpu-$(CONFIG_VIDEO_HANTRO_ROCKCHIP) += \
+ rockchip_vpu2_hw_jpeg_enc.o \
+ rockchip_vpu2_hw_h264_dec.o \
+ rockchip_vpu2_hw_mpeg2_dec.o \
+ rockchip_vpu2_hw_vp8_dec.o \
+ rockchip_vpu981_hw_av1_dec.o \
+ rockchip_av1_filmgrain.o \
+ rockchip_av1_entropymode.o \
rockchip_vpu_hw.o
hantro-vpu-$(CONFIG_VIDEO_HANTRO_SUNXI) += \
diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h
index 811260dc3c77..edc217eed293 100644
--- a/drivers/media/platform/verisilicon/hantro.h
+++ b/drivers/media/platform/verisilicon/hantro.h
@@ -227,6 +227,7 @@ struct hantro_dev {
* @src_fmt: V4L2 pixel format of active source format.
* @vpu_dst_fmt: Descriptor of active destination format.
* @dst_fmt: V4L2 pixel format of active destination format.
+ * @ref_fmt: V4L2 pixel format of the reference frames format.
*
* @ctrl_handler: Control handler used to register controls.
* @jpeg_quality: User-specified JPEG compression quality.
@@ -255,6 +256,7 @@ struct hantro_ctx {
struct v4l2_pix_format_mplane src_fmt;
const struct hantro_fmt *vpu_dst_fmt;
struct v4l2_pix_format_mplane dst_fmt;
+ struct v4l2_pix_format_mplane ref_fmt;
struct v4l2_ctrl_handler ctrl_handler;
int jpeg_quality;
@@ -332,12 +334,19 @@ struct hantro_vp9_decoded_buffer_info {
u32 bit_depth : 4;
};
+struct hantro_av1_decoded_buffer_info {
+ /* Info needed when the decoded frame serves as a reference frame. */
+ size_t chroma_offset;
+ size_t mv_offset;
+};
+
struct hantro_decoded_buffer {
/* Must be the first field in this struct. */
struct v4l2_m2m_buffer base;
union {
struct hantro_vp9_decoded_buffer_info vp9;
+ struct hantro_av1_decoded_buffer_info av1;
};
};
diff --git a/drivers/media/platform/verisilicon/hantro_drv.c b/drivers/media/platform/verisilicon/hantro_drv.c
index 34b123dafd89..8542238e0fb1 100644
--- a/drivers/media/platform/verisilicon/hantro_drv.c
+++ b/drivers/media/platform/verisilicon/hantro_drv.c
@@ -722,6 +722,7 @@ static const struct of_device_id of_hantro_match[] = {
{ .compatible = "rockchip,rk3399-vpu", .data = &rk3399_vpu_variant, },
{ .compatible = "rockchip,rk3568-vepu", .data = &rk3568_vepu_variant, },
{ .compatible = "rockchip,rk3568-vpu", .data = &rk3568_vpu_variant, },
+ { .compatible = "rockchip,rk3588-vepu121", .data = &rk3568_vepu_variant, },
{ .compatible = "rockchip,rk3588-av1-vpu", .data = &rk3588_vpu981_variant, },
#endif
#ifdef CONFIG_VIDEO_HANTRO_IMX8M
@@ -992,6 +993,49 @@ static const struct media_device_ops hantro_m2m_media_ops = {
.req_queue = v4l2_m2m_request_queue,
};
+/*
+ * Some SoCs, like RK3588 have multiple identical Hantro cores, but the
+ * kernel is currently missing support for multi-core handling. Exposing
+ * separate devices for each core to userspace is bad, since that does
+ * not allow scheduling tasks properly (and creates ABI). With this workaround
+ * the driver will only probe for the first core and early exit for the other
+ * cores. Once the driver gains multi-core support, the same technique
+ * for detecting the main core can be used to cluster all cores together.
+ */
+static int hantro_disable_multicore(struct hantro_dev *vpu)
+{
+ struct device_node *node = NULL;
+ const char *compatible;
+ bool is_main_core;
+ int ret;
+
+ /* Intentionally ignores the fallback strings */
+ ret = of_property_read_string(vpu->dev->of_node, "compatible", &compatible);
+ if (ret)
+ return ret;
+
+ /* The first compatible and available node found is considered the main core */
+ do {
+ node = of_find_compatible_node(node, NULL, compatible);
+ if (of_device_is_available(node))
+ break;
+ } while (node);
+
+ if (!node)
+ return -EINVAL;
+
+ is_main_core = (vpu->dev->of_node == node);
+
+ of_node_put(node);
+
+ if (!is_main_core) {
+ dev_info(vpu->dev, "missing multi-core support, ignoring this instance\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int hantro_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -1011,6 +1055,10 @@ static int hantro_probe(struct platform_device *pdev)
match = of_match_node(of_hantro_match, pdev->dev.of_node);
vpu->variant = match->data;
+ ret = hantro_disable_multicore(vpu);
+ if (ret)
+ return ret;
+
/*
* Support for nxp,imx8mq-vpu is kept for backwards compatibility
* but it's deprecated. Please update your DTS file to use
@@ -1229,7 +1277,7 @@ static const struct dev_pm_ops hantro_pm_ops = {
static struct platform_driver hantro_driver = {
.probe = hantro_probe,
- .remove_new = hantro_remove,
+ .remove = hantro_remove,
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_hantro_match,
diff --git a/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c
index 9aea331e1a3c..e0d6bd0a6e44 100644
--- a/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g1_mpeg2_dec.c
@@ -5,7 +5,7 @@
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/bitfield.h>
#include <media/v4l2-mem2mem.h>
#include "hantro.h"
diff --git a/drivers/media/platform/verisilicon/hantro_g2.c b/drivers/media/platform/verisilicon/hantro_g2.c
index b880a6849d58..aae0b562fabb 100644
--- a/drivers/media/platform/verisilicon/hantro_g2.c
+++ b/drivers/media/platform/verisilicon/hantro_g2.c
@@ -47,7 +47,7 @@ irqreturn_t hantro_g2_irq(int irq, void *dev_id)
size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx)
{
- return ctx->dst_fmt.width * ctx->dst_fmt.height * ctx->bit_depth / 8;
+ return ctx->ref_fmt.plane_fmt[0].bytesperline * ctx->ref_fmt.height;
}
size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx)
@@ -56,3 +56,32 @@ size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx)
return ALIGN((cr_offset * 3) / 2, G2_ALIGN);
}
+
+static size_t hantro_g2_mv_size(struct hantro_ctx *ctx)
+{
+ const struct hantro_hevc_dec_ctrls *ctrls = &ctx->hevc_dec.ctrls;
+ const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
+ unsigned int pic_width_in_ctbs, pic_height_in_ctbs;
+ unsigned int max_log2_ctb_size;
+
+ max_log2_ctb_size = sps->log2_min_luma_coding_block_size_minus3 + 3 +
+ sps->log2_diff_max_min_luma_coding_block_size;
+ pic_width_in_ctbs = (sps->pic_width_in_luma_samples +
+ (1 << max_log2_ctb_size) - 1) >> max_log2_ctb_size;
+ pic_height_in_ctbs = (sps->pic_height_in_luma_samples + (1 << max_log2_ctb_size) - 1)
+ >> max_log2_ctb_size;
+
+ return pic_width_in_ctbs * pic_height_in_ctbs * (1 << (2 * (max_log2_ctb_size - 4))) * 16;
+}
+
+size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx)
+{
+ return hantro_g2_motion_vectors_offset(ctx) +
+ hantro_g2_mv_size(ctx);
+}
+
+size_t hantro_g2_chroma_compress_offset(struct hantro_ctx *ctx)
+{
+ return hantro_g2_luma_compress_offset(ctx) +
+ hantro_hevc_luma_compressed_size(ctx->dst_fmt.width, ctx->dst_fmt.height);
+}
diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
index d3f8c33eb16c..0e212198dd65 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
@@ -367,11 +367,14 @@ static int set_ref(struct hantro_ctx *ctx)
const struct v4l2_ctrl_hevc_decode_params *decode_params = ctrls->decode_params;
const struct v4l2_hevc_dpb_entry *dpb = decode_params->dpb;
dma_addr_t luma_addr, chroma_addr, mv_addr = 0;
+ dma_addr_t compress_luma_addr, compress_chroma_addr = 0;
struct hantro_dev *vpu = ctx->dev;
struct vb2_v4l2_buffer *vb2_dst;
struct hantro_decoded_buffer *dst;
size_t cr_offset = hantro_g2_chroma_offset(ctx);
size_t mv_offset = hantro_g2_motion_vectors_offset(ctx);
+ size_t compress_luma_offset = hantro_g2_luma_compress_offset(ctx);
+ size_t compress_chroma_offset = hantro_g2_chroma_compress_offset(ctx);
u32 max_ref_frames;
u16 dpb_longterm_e;
static const struct hantro_reg cur_poc[] = {
@@ -445,6 +448,8 @@ static int set_ref(struct hantro_ctx *ctx)
chroma_addr = luma_addr + cr_offset;
mv_addr = luma_addr + mv_offset;
+ compress_luma_addr = luma_addr + compress_luma_offset;
+ compress_chroma_addr = luma_addr + compress_chroma_offset;
if (dpb[i].flags & V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE)
dpb_longterm_e |= BIT(V4L2_HEVC_DPB_ENTRIES_NUM_MAX - 1 - i);
@@ -452,6 +457,8 @@ static int set_ref(struct hantro_ctx *ctx)
hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr);
hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr);
hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr);
+ hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), compress_luma_addr);
+ hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i), compress_chroma_addr);
}
vb2_dst = hantro_get_dst_buf(ctx);
@@ -465,19 +472,27 @@ static int set_ref(struct hantro_ctx *ctx)
chroma_addr = luma_addr + cr_offset;
mv_addr = luma_addr + mv_offset;
+ compress_luma_addr = luma_addr + compress_luma_offset;
+ compress_chroma_addr = luma_addr + compress_chroma_offset;
hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), luma_addr);
hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), chroma_addr);
- hantro_write_addr(vpu, G2_REF_MV_ADDR(i++), mv_addr);
+ hantro_write_addr(vpu, G2_REF_MV_ADDR(i), mv_addr);
+ hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), compress_luma_addr);
+ hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i++), compress_chroma_addr);
hantro_write_addr(vpu, G2_OUT_LUMA_ADDR, luma_addr);
hantro_write_addr(vpu, G2_OUT_CHROMA_ADDR, chroma_addr);
hantro_write_addr(vpu, G2_OUT_MV_ADDR, mv_addr);
+ hantro_write_addr(vpu, G2_OUT_COMP_LUMA_ADDR, compress_luma_addr);
+ hantro_write_addr(vpu, G2_OUT_COMP_CHROMA_ADDR, compress_chroma_addr);
for (; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX; i++) {
hantro_write_addr(vpu, G2_REF_LUMA_ADDR(i), 0);
hantro_write_addr(vpu, G2_REF_CHROMA_ADDR(i), 0);
hantro_write_addr(vpu, G2_REF_MV_ADDR(i), 0);
+ hantro_write_addr(vpu, G2_REF_COMP_LUMA_ADDR(i), 0);
+ hantro_write_addr(vpu, G2_REF_COMP_CHROMA_ADDR(i), 0);
}
hantro_reg_write(vpu, &g2_refer_lterm_e, dpb_longterm_e);
@@ -503,6 +518,7 @@ static void set_buffers(struct hantro_ctx *ctx)
hantro_reg_write(vpu, &g2_stream_len, src_len);
hantro_reg_write(vpu, &g2_strm_buffer_len, src_buf_len);
hantro_reg_write(vpu, &g2_strm_start_offset, 0);
+ hantro_reg_write(vpu, &g2_start_bit, 0);
hantro_reg_write(vpu, &g2_write_mvs_e, 1);
hantro_write_addr(vpu, G2_TILE_SIZES_ADDR, ctx->hevc_dec.tile_sizes.dma);
@@ -594,8 +610,7 @@ int hantro_g2_hevc_dec_run(struct hantro_ctx *ctx)
/* Don't disable output */
hantro_reg_write(vpu, &g2_out_dis, 0);
- /* Don't compress buffers */
- hantro_reg_write(vpu, &g2_ref_compress_bypass, 1);
+ hantro_reg_write(vpu, &g2_ref_compress_bypass, !ctx->hevc_dec.use_compression);
/* Bus width and max burst */
hantro_reg_write(vpu, &g2_buswidth, BUS_WIDTH_128);
diff --git a/drivers/media/platform/verisilicon/hantro_g2_regs.h b/drivers/media/platform/verisilicon/hantro_g2_regs.h
index 82606783591a..b943b1816db7 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_regs.h
+++ b/drivers/media/platform/verisilicon/hantro_g2_regs.h
@@ -318,6 +318,10 @@
#define G2_TILE_BSD_ADDR (G2_SWREG(183))
#define G2_DS_DST (G2_SWREG(186))
#define G2_DS_DST_CHR (G2_SWREG(188))
+#define G2_OUT_COMP_LUMA_ADDR (G2_SWREG(190))
+#define G2_REF_COMP_LUMA_ADDR(i) (G2_SWREG(192) + ((i) * 0x8))
+#define G2_OUT_COMP_CHROMA_ADDR (G2_SWREG(224))
+#define G2_REF_COMP_CHROMA_ADDR(i) (G2_SWREG(226) + ((i) * 0x8))
#define g2_strm_buffer_len G2_DEC_REG(258, 0, 0xffffffff)
#define g2_strm_start_offset G2_DEC_REG(259, 0, 0xffffffff)
diff --git a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
index 342e543dee4c..82a478ac645e 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g2_vp9_dec.c
@@ -776,15 +776,15 @@ config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_para
struct vb2_v4l2_buffer *vb2_src)
{
dma_addr_t stream_base, tmp_addr;
- unsigned int headres_size;
+ unsigned int headers_size;
u32 src_len, start_bit, src_buf_len;
- headres_size = dec_params->uncompressed_header_size
+ headers_size = dec_params->uncompressed_header_size
+ dec_params->compressed_header_size;
stream_base = vb2_dma_contig_plane_dma_addr(&vb2_src->vb2_buf, 0);
- tmp_addr = stream_base + headres_size;
+ tmp_addr = stream_base + headers_size;
if (ctx->dev->variant->legacy_regs)
hantro_write_addr(ctx->dev, G2_STREAM_ADDR, (tmp_addr & ~0xf));
else
@@ -794,7 +794,7 @@ config_source(struct hantro_ctx *ctx, const struct v4l2_ctrl_vp9_frame *dec_para
hantro_reg_write(ctx->dev, &g2_start_bit, start_bit);
src_len = vb2_get_plane_payload(&vb2_src->vb2_buf, 0);
- src_len += start_bit / 8 - headres_size;
+ src_len += start_bit / 8 - headers_size;
hantro_reg_write(ctx->dev, &g2_stream_len, src_len);
if (!ctx->dev->variant->legacy_regs) {
diff --git a/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c b/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c
index 12d69503d6ba..86cc1a07026f 100644
--- a/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c
+++ b/drivers/media/platform/verisilicon/hantro_h1_jpeg_enc.c
@@ -5,7 +5,7 @@
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <media/v4l2-mem2mem.h>
#include "hantro_jpeg.h"
#include "hantro.h"
diff --git a/drivers/media/platform/verisilicon/hantro_h1_regs.h b/drivers/media/platform/verisilicon/hantro_h1_regs.h
index 30e7e7b920b5..8650cc489392 100644
--- a/drivers/media/platform/verisilicon/hantro_h1_regs.h
+++ b/drivers/media/platform/verisilicon/hantro_h1_regs.h
@@ -62,7 +62,7 @@
#define H1_REG_ENC_CTRL1_INTRA_PRED_MODE(x) ((x) << 16)
#define H1_REG_ENC_CTRL1_FRAME_NUM(x) ((x))
#define H1_REG_ENC_CTRL2 0x048
-#define H1_REG_ENC_CTRL2_DEBLOCKING_FILETER_MODE(x) ((x) << 30)
+#define H1_REG_ENC_CTRL2_DEBLOCKING_FILTER_MODE(x) ((x) << 30)
#define H1_REG_ENC_CTRL2_H264_SLICE_SIZE(x) ((x) << 23)
#define H1_REG_ENC_CTRL2_DISABLE_QUARTER_PIXMV BIT(22)
#define H1_REG_ENC_CTRL2_TRANS8X8_MODE_EN BIT(21)
@@ -89,7 +89,7 @@
#define H1_REG_STR_BUF_LIMIT 0x060
#define H1_REG_MAD_CTRL 0x064
#define H1_REG_MAD_CTRL_QP_ADJUST(x) ((x) << 28)
-#define H1_REG_MAD_CTRL_MAD_THREDHOLD(x) ((x) << 22)
+#define H1_REG_MAD_CTRL_MAD_THRESHOLD(x) ((x) << 22)
#define H1_REG_MAD_CTRL_QP_SUM_DIV2(x) ((x))
#define H1_REG_ADDR_VP8_PROB_CNT 0x068
#define H1_REG_QP_VAL 0x06c
diff --git a/drivers/media/platform/verisilicon/hantro_hevc.c b/drivers/media/platform/verisilicon/hantro_hevc.c
index 2c14330bc562..83cd12b0ddd6 100644
--- a/drivers/media/platform/verisilicon/hantro_hevc.c
+++ b/drivers/media/platform/verisilicon/hantro_hevc.c
@@ -25,6 +25,11 @@
#define MAX_TILE_COLS 20
#define MAX_TILE_ROWS 22
+static bool hevc_use_compression = IS_ENABLED(CONFIG_VIDEO_HANTRO_HEVC_RFC);
+module_param_named(hevc_use_compression, hevc_use_compression, bool, 0644);
+MODULE_PARM_DESC(hevc_use_compression,
+ "Use reference frame compression for HEVC");
+
void hantro_hevc_ref_init(struct hantro_ctx *ctx)
{
struct hantro_hevc_dec_hw_ctx *hevc_dec = &ctx->hevc_dec;
@@ -275,5 +280,8 @@ int hantro_hevc_dec_init(struct hantro_ctx *ctx)
hantro_hevc_ref_init(ctx);
+ hevc_dec->use_compression =
+ hevc_use_compression & hantro_needs_postproc(ctx, ctx->vpu_dst_fmt);
+
return 0;
}
diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h
index 7737320cc8cc..c9b6556f8b2b 100644
--- a/drivers/media/platform/verisilicon/hantro_hw.h
+++ b/drivers/media/platform/verisilicon/hantro_hw.h
@@ -42,6 +42,13 @@
#define MAX_POSTPROC_BUFFERS 64
+#define CBS_SIZE 16 /* compression table size in bytes */
+#define CBS_LUMA 8 /* luminance CBS is composed of 1 8x8 coded block */
+#define CBS_CHROMA_W (8 * 2) /* chrominance CBS is composed of two 8x4 coded
+ * blocks, with Cb CB first then Cr CB following
+ */
+#define CBS_CHROMA_H 4
+
struct hantro_dev;
struct hantro_ctx;
struct hantro_buf;
@@ -144,6 +151,7 @@ struct hantro_hevc_dec_ctrls {
* @ref_bufs_used: Bitfield of used reference buffers
* @ctrls: V4L2 controls attached to a run
* @num_tile_cols_allocated: number of allocated tiles
+ * @use_compression: use reference buffer compression
*/
struct hantro_hevc_dec_hw_ctx {
struct hantro_aux_buf tile_sizes;
@@ -156,6 +164,7 @@ struct hantro_hevc_dec_hw_ctx {
u32 ref_bufs_used;
struct hantro_hevc_dec_ctrls ctrls;
unsigned int num_tile_cols_allocated;
+ bool use_compression;
};
/**
@@ -510,6 +519,33 @@ hantro_hevc_mv_size(unsigned int width, unsigned int height)
return width * height / 16;
}
+static inline size_t
+hantro_hevc_luma_compressed_size(unsigned int width, unsigned int height)
+{
+ u32 pic_width_in_cbsy =
+ round_up((width + CBS_LUMA - 1) / CBS_LUMA, CBS_SIZE);
+ u32 pic_height_in_cbsy = (height + CBS_LUMA - 1) / CBS_LUMA;
+
+ return round_up(pic_width_in_cbsy * pic_height_in_cbsy, CBS_SIZE);
+}
+
+static inline size_t
+hantro_hevc_chroma_compressed_size(unsigned int width, unsigned int height)
+{
+ u32 pic_width_in_cbsc =
+ round_up((width + CBS_CHROMA_W - 1) / CBS_CHROMA_W, CBS_SIZE);
+ u32 pic_height_in_cbsc = (height / 2 + CBS_CHROMA_H - 1) / CBS_CHROMA_H;
+
+ return round_up(pic_width_in_cbsc * pic_height_in_cbsc, CBS_SIZE);
+}
+
+static inline size_t
+hantro_hevc_compressed_size(unsigned int width, unsigned int height)
+{
+ return hantro_hevc_luma_compressed_size(width, height) +
+ hantro_hevc_chroma_compressed_size(width, height);
+}
+
static inline unsigned short hantro_av1_num_sbs(unsigned short dimension)
{
return DIV_ROUND_UP(dimension, 64);
@@ -525,6 +561,8 @@ hantro_av1_mv_size(unsigned int width, unsigned int height)
size_t hantro_g2_chroma_offset(struct hantro_ctx *ctx);
size_t hantro_g2_motion_vectors_offset(struct hantro_ctx *ctx);
+size_t hantro_g2_luma_compress_offset(struct hantro_ctx *ctx);
+size_t hantro_g2_chroma_compress_offset(struct hantro_ctx *ctx);
int hantro_g1_mpeg2_dec_run(struct hantro_ctx *ctx);
int rockchip_vpu2_mpeg2_dec_run(struct hantro_ctx *ctx);
diff --git a/drivers/media/platform/verisilicon/hantro_jpeg.c b/drivers/media/platform/verisilicon/hantro_jpeg.c
index d07b1b449b61..13a60638e43f 100644
--- a/drivers/media/platform/verisilicon/hantro_jpeg.c
+++ b/drivers/media/platform/verisilicon/hantro_jpeg.c
@@ -11,6 +11,7 @@
#include <linux/build_bug.h>
#include <linux/kernel.h>
#include <linux/string.h>
+#include <media/v4l2-jpeg.h>
#include "hantro_jpeg.h"
#include "hantro.h"
@@ -24,42 +25,6 @@
#define HUFF_CHROMA_DC_OFF 394
#define HUFF_CHROMA_AC_OFF 427
-/* Default tables from JPEG ITU-T.81
- * (ISO/IEC 10918-1) Annex K, tables K.1 and K.2
- */
-static const unsigned char luma_q_table[] = {
- 0x10, 0x0b, 0x0a, 0x10, 0x18, 0x28, 0x33, 0x3d,
- 0x0c, 0x0c, 0x0e, 0x13, 0x1a, 0x3a, 0x3c, 0x37,
- 0x0e, 0x0d, 0x10, 0x18, 0x28, 0x39, 0x45, 0x38,
- 0x0e, 0x11, 0x16, 0x1d, 0x33, 0x57, 0x50, 0x3e,
- 0x12, 0x16, 0x25, 0x38, 0x44, 0x6d, 0x67, 0x4d,
- 0x18, 0x23, 0x37, 0x40, 0x51, 0x68, 0x71, 0x5c,
- 0x31, 0x40, 0x4e, 0x57, 0x67, 0x79, 0x78, 0x65,
- 0x48, 0x5c, 0x5f, 0x62, 0x70, 0x64, 0x67, 0x63
-};
-
-static const unsigned char chroma_q_table[] = {
- 0x11, 0x12, 0x18, 0x2f, 0x63, 0x63, 0x63, 0x63,
- 0x12, 0x15, 0x1a, 0x42, 0x63, 0x63, 0x63, 0x63,
- 0x18, 0x1a, 0x38, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x2f, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
- 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
-};
-
-static const unsigned char zigzag[] = {
- 0, 1, 8, 16, 9, 2, 3, 10,
- 17, 24, 32, 25, 18, 11, 4, 5,
- 12, 19, 26, 33, 40, 48, 41, 34,
- 27, 20, 13, 6, 7, 14, 21, 28,
- 35, 42, 49, 56, 57, 50, 43, 36,
- 29, 22, 15, 23, 30, 37, 44, 51,
- 58, 59, 52, 45, 38, 31, 39, 46,
- 53, 60, 61, 54, 47, 55, 62, 63
-};
-
static const u32 hw_reorder[] = {
0, 8, 16, 24, 1, 9, 17, 25,
32, 40, 48, 56, 33, 41, 49, 57,
@@ -71,73 +36,6 @@ static const u32 hw_reorder[] = {
38, 46, 54, 62, 39, 47, 55, 63
};
-/* Huffman tables are shared with CODA */
-static const unsigned char luma_dc_table[] = {
- 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b,
-};
-
-static const unsigned char chroma_dc_table[] = {
- 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
- 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b,
-};
-
-static const unsigned char luma_ac_table[] = {
- 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
- 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
- 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
- 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
- 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
- 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
- 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
- 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
- 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
- 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
- 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
- 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
- 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
- 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
- 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
- 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
- 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
- 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
- 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
- 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
- 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
- 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
- 0xf9, 0xfa,
-};
-
-static const unsigned char chroma_ac_table[] = {
- 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04,
- 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77,
- 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
- 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
- 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
- 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
- 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
- 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
- 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
- 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
- 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
- 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
- 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
- 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
- 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
- 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
- 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
- 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
- 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
- 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
- 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
- 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
- 0xf9, 0xfa,
-};
-
/* For simplicity, we keep a pre-formatted JPEG header,
* and we'll use fixed offsets to change the width, height
* quantization tables, etc.
@@ -292,11 +190,11 @@ jpeg_scale_quant_table(unsigned char *file_q_tab,
{
int i;
- BUILD_BUG_ON(ARRAY_SIZE(zigzag) != JPEG_QUANT_SIZE);
+ BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_zigzag_scan_index) != JPEG_QUANT_SIZE);
BUILD_BUG_ON(ARRAY_SIZE(hw_reorder) != JPEG_QUANT_SIZE);
for (i = 0; i < JPEG_QUANT_SIZE; i++) {
- file_q_tab[i] = jpeg_scale_qp(tab[zigzag[i]], scale);
+ file_q_tab[i] = jpeg_scale_qp(tab[v4l2_jpeg_zigzag_scan_index[i]], scale);
reordered_q_tab[i] = jpeg_scale_qp(tab[hw_reorder[i]], scale);
}
}
@@ -304,7 +202,6 @@ jpeg_scale_quant_table(unsigned char *file_q_tab,
static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx)
{
int scale;
-
/*
* Non-linear scaling factor:
* [5,50] -> [1000..100], [51,100] -> [98..0]
@@ -314,15 +211,17 @@ static void jpeg_set_quality(struct hantro_jpeg_ctx *ctx)
else
scale = 200 - 2 * ctx->quality;
- BUILD_BUG_ON(ARRAY_SIZE(luma_q_table) != JPEG_QUANT_SIZE);
- BUILD_BUG_ON(ARRAY_SIZE(chroma_q_table) != JPEG_QUANT_SIZE);
+ BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_luma_qt) != JPEG_QUANT_SIZE);
+ BUILD_BUG_ON(ARRAY_SIZE(v4l2_jpeg_ref_table_chroma_qt) != JPEG_QUANT_SIZE);
BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_luma_qtable) != JPEG_QUANT_SIZE);
BUILD_BUG_ON(ARRAY_SIZE(ctx->hw_chroma_qtable) != JPEG_QUANT_SIZE);
jpeg_scale_quant_table(ctx->buffer + LUMA_QUANT_OFF,
- ctx->hw_luma_qtable, luma_q_table, scale);
+ ctx->hw_luma_qtable,
+ (const unsigned char *)v4l2_jpeg_ref_table_luma_qt, scale);
jpeg_scale_quant_table(ctx->buffer + CHROMA_QUANT_OFF,
- ctx->hw_chroma_qtable, chroma_q_table, scale);
+ ctx->hw_chroma_qtable,
+ (const unsigned char *)v4l2_jpeg_ref_table_chroma_qt, scale);
}
void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx)
@@ -337,12 +236,10 @@ void hantro_jpeg_header_assemble(struct hantro_jpeg_ctx *ctx)
buf[WIDTH_OFF + 0] = ctx->width >> 8;
buf[WIDTH_OFF + 1] = ctx->width;
- memcpy(buf + HUFF_LUMA_DC_OFF, luma_dc_table, sizeof(luma_dc_table));
- memcpy(buf + HUFF_LUMA_AC_OFF, luma_ac_table, sizeof(luma_ac_table));
- memcpy(buf + HUFF_CHROMA_DC_OFF, chroma_dc_table,
- sizeof(chroma_dc_table));
- memcpy(buf + HUFF_CHROMA_AC_OFF, chroma_ac_table,
- sizeof(chroma_ac_table));
+ memcpy(buf + HUFF_LUMA_DC_OFF, v4l2_jpeg_ref_table_luma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN);
+ memcpy(buf + HUFF_LUMA_AC_OFF, v4l2_jpeg_ref_table_luma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN);
+ memcpy(buf + HUFF_CHROMA_DC_OFF, v4l2_jpeg_ref_table_chroma_dc_ht, V4L2_JPEG_REF_HT_DC_LEN);
+ memcpy(buf + HUFF_CHROMA_AC_OFF, v4l2_jpeg_ref_table_chroma_ac_ht, V4L2_JPEG_REF_HT_AC_LEN);
jpeg_set_quality(ctx);
}
diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c
index 41e93176300b..9f559a13d409 100644
--- a/drivers/media/platform/verisilicon/hantro_postproc.c
+++ b/drivers/media/platform/verisilicon/hantro_postproc.c
@@ -194,31 +194,25 @@ void hantro_postproc_free(struct hantro_ctx *ctx)
static unsigned int hantro_postproc_buffer_size(struct hantro_ctx *ctx)
{
- struct v4l2_pix_format_mplane pix_mp;
- const struct hantro_fmt *fmt;
unsigned int buf_size;
- /* this should always pick native format */
- fmt = hantro_get_default_fmt(ctx, false, ctx->bit_depth, HANTRO_AUTO_POSTPROC);
- if (!fmt)
- return 0;
-
- v4l2_fill_pixfmt_mp(&pix_mp, fmt->fourcc, ctx->src_fmt.width,
- ctx->src_fmt.height);
-
- buf_size = pix_mp.plane_fmt[0].sizeimage;
+ buf_size = ctx->ref_fmt.plane_fmt[0].sizeimage;
if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_H264_SLICE)
- buf_size += hantro_h264_mv_size(pix_mp.width,
- pix_mp.height);
+ buf_size += hantro_h264_mv_size(ctx->ref_fmt.width,
+ ctx->ref_fmt.height);
else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_VP9_FRAME)
- buf_size += hantro_vp9_mv_size(pix_mp.width,
- pix_mp.height);
- else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE)
- buf_size += hantro_hevc_mv_size(pix_mp.width,
- pix_mp.height);
+ buf_size += hantro_vp9_mv_size(ctx->ref_fmt.width,
+ ctx->ref_fmt.height);
+ else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_HEVC_SLICE) {
+ buf_size += hantro_hevc_mv_size(ctx->ref_fmt.width,
+ ctx->ref_fmt.height);
+ if (ctx->hevc_dec.use_compression)
+ buf_size += hantro_hevc_compressed_size(ctx->ref_fmt.width,
+ ctx->ref_fmt.height);
+ }
else if (ctx->vpu_src_fmt->fourcc == V4L2_PIX_FMT_AV1_FRAME)
- buf_size += hantro_av1_mv_size(pix_mp.width,
- pix_mp.height);
+ buf_size += hantro_av1_mv_size(ctx->ref_fmt.width,
+ ctx->ref_fmt.height);
return buf_size;
}
@@ -256,8 +250,10 @@ int hantro_postproc_init(struct hantro_ctx *ctx)
for (i = 0; i < num_buffers; i++) {
ret = hantro_postproc_alloc(ctx, i);
- if (ret)
+ if (ret) {
+ hantro_postproc_free(ctx);
return ret;
+ }
}
return 0;
diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c
index 941fa23c211a..7c3515cf7d64 100644
--- a/drivers/media/platform/verisilicon/hantro_v4l2.c
+++ b/drivers/media/platform/verisilicon/hantro_v4l2.c
@@ -77,6 +77,7 @@ int hantro_get_format_depth(u32 fourcc)
switch (fourcc) {
case V4L2_PIX_FMT_P010:
case V4L2_PIX_FMT_P010_4L4:
+ case V4L2_PIX_FMT_NV15:
case V4L2_PIX_FMT_NV15_4L4:
return 10;
default:
@@ -126,6 +127,24 @@ hantro_find_format(const struct hantro_ctx *ctx, u32 fourcc)
return NULL;
}
+static int
+hantro_set_reference_frames_format(struct hantro_ctx *ctx)
+{
+ const struct hantro_fmt *fmt;
+ int dst_bit_depth = hantro_get_format_depth(ctx->vpu_dst_fmt->fourcc);
+
+ fmt = hantro_get_default_fmt(ctx, false, dst_bit_depth, HANTRO_AUTO_POSTPROC);
+ if (!fmt)
+ return -EINVAL;
+
+ ctx->ref_fmt.width = ctx->src_fmt.width;
+ ctx->ref_fmt.height = ctx->src_fmt.height;
+
+ v4l2_apply_frmsize_constraints(&ctx->ref_fmt.width, &ctx->ref_fmt.height, &fmt->frmsize);
+ return v4l2_fill_pixfmt_mp(&ctx->ref_fmt, fmt->fourcc,
+ ctx->ref_fmt.width, ctx->ref_fmt.height);
+}
+
const struct hantro_fmt *
hantro_get_default_fmt(const struct hantro_ctx *ctx, bool bitstream,
int bit_depth, bool need_postproc)
@@ -201,7 +220,15 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
struct hantro_ctx *ctx = fh_to_ctx(priv);
const struct hantro_fmt *fmt, *formats;
unsigned int num_fmts, i, j = 0;
- bool skip_mode_none;
+ bool skip_mode_none, enum_all_formats;
+ u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL;
+
+ /*
+ * If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all
+ * hardware supported pixel formats
+ */
+ enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL);
+ f->index = index;
/*
* When dealing with an encoder:
@@ -222,9 +249,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
if (skip_mode_none == mode_none)
continue;
- if (!hantro_check_depth_match(fmt, ctx->bit_depth))
+ if (!hantro_check_depth_match(fmt, ctx->bit_depth) && !enum_all_formats)
continue;
- if (j == f->index) {
+ if (j == index) {
f->pixelformat = fmt->fourcc;
return 0;
}
@@ -242,9 +269,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
for (i = 0; i < num_fmts; i++) {
fmt = &formats[i];
- if (!hantro_check_depth_match(fmt, ctx->bit_depth))
+ if (!hantro_check_depth_match(fmt, ctx->bit_depth) && !enum_all_formats)
continue;
- if (j == f->index) {
+ if (j == index) {
f->pixelformat = fmt->fourcc;
return 0;
}
@@ -303,11 +330,7 @@ static int hantro_try_fmt(const struct hantro_ctx *ctx,
coded = capture == ctx->is_encoder;
- vpu_debug(4, "trying format %c%c%c%c\n",
- (pix_mp->pixelformat & 0x7f),
- (pix_mp->pixelformat >> 8) & 0x7f,
- (pix_mp->pixelformat >> 16) & 0x7f,
- (pix_mp->pixelformat >> 24) & 0x7f);
+ vpu_debug(4, "trying format %p4cc\n", &pix_mp->pixelformat);
fmt = hantro_find_format(ctx, pix_mp->pixelformat);
if (!fmt) {
@@ -591,6 +614,9 @@ static int hantro_set_fmt_cap(struct hantro_ctx *ctx,
ctx->vpu_dst_fmt = hantro_find_format(ctx, pix_mp->pixelformat);
ctx->dst_fmt = *pix_mp;
+ ret = hantro_set_reference_frames_format(ctx);
+ if (ret)
+ return ret;
/*
* Current raw format might have become invalid with newly
@@ -756,6 +782,7 @@ const struct v4l2_ioctl_ops hantro_ioctl_ops = {
.vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf,
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
+ .vidioc_remove_bufs = v4l2_m2m_ioctl_remove_bufs,
.vidioc_expbuf = v4l2_m2m_ioctl_expbuf,
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
@@ -999,6 +1026,4 @@ const struct vb2_ops hantro_queue_ops = {
.buf_request_complete = hantro_buf_request_complete,
.start_streaming = hantro_start_streaming,
.stop_streaming = hantro_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
diff --git a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
index f850d8bddef6..35799da534ed 100644
--- a/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/imx8m_vpu_hw.c
@@ -187,23 +187,23 @@ static const struct hantro_fmt imx8m_vpu_g2_dec_fmts[] = {
.frmsize = {
.min_width = FMT_MIN_WIDTH,
.max_width = FMT_UHD_WIDTH,
- .step_width = TILE_MB_DIM,
+ .step_width = 8,
.min_height = FMT_MIN_HEIGHT,
.max_height = FMT_UHD_HEIGHT,
- .step_height = TILE_MB_DIM,
+ .step_height = 32,
},
},
{
- .fourcc = V4L2_PIX_FMT_P010_4L4,
+ .fourcc = V4L2_PIX_FMT_NV15_4L4,
.codec_mode = HANTRO_MODE_NONE,
.match_depth = true,
.frmsize = {
.min_width = FMT_MIN_WIDTH,
.max_width = FMT_UHD_WIDTH,
- .step_width = TILE_MB_DIM,
+ .step_width = 8,
.min_height = FMT_MIN_HEIGHT,
.max_height = FMT_UHD_HEIGHT,
- .step_height = TILE_MB_DIM,
+ .step_height = 32,
},
},
{
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c
index 8395c4d48dd0..61621b1be8a2 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_jpeg_enc.c
@@ -22,7 +22,7 @@
* zigzag, nor linear.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <media/v4l2-mem2mem.h>
#include "hantro_jpeg.h"
#include "hantro.h"
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c
index b66737fab46b..50a3a3eeaa00 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu2_hw_mpeg2_dec.c
@@ -5,7 +5,7 @@
* Copyright (C) 2018 Rockchip Electronics Co., Ltd.
*/
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/bitfield.h>
#include <media/v4l2-mem2mem.h>
#include "hantro.h"
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
index cc4483857489..e4703bb6be7c 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
@@ -161,8 +161,7 @@ static int rockchip_vpu981_av1_dec_frame_ref(struct hantro_ctx *ctx,
av1_dec->frame_refs[i].timestamp = timestamp;
av1_dec->frame_refs[i].frame_type = frame->frame_type;
av1_dec->frame_refs[i].order_hint = frame->order_hint;
- if (!av1_dec->frame_refs[i].vb2_ref)
- av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx);
+ av1_dec->frame_refs[i].vb2_ref = hantro_get_dst_buf(ctx);
for (j = 0; j < V4L2_AV1_TOTAL_REFS_PER_FRAME; j++)
av1_dec->frame_refs[i].order_hints[j] = frame->order_hints[j];
@@ -257,7 +256,8 @@ static int rockchip_vpu981_av1_dec_tiles_reallocate(struct hantro_ctx *ctx)
struct hantro_dev *vpu = ctx->dev;
struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
- unsigned int num_tile_cols = 1 << ctrls->tile_group_entry->tile_col;
+ const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
+ unsigned int num_tile_cols = tile_info->tile_cols;
unsigned int height = ALIGN(ctrls->frame->frame_height_minus_1 + 1, 64);
unsigned int height_in_sb = height / 64;
unsigned int stripe_num = ((height + 8) + 63) / 64;
@@ -686,8 +686,6 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx,
struct hantro_dev *vpu = ctx->dev;
struct hantro_decoded_buffer *dst;
dma_addr_t luma_addr, chroma_addr, mv_addr = 0;
- size_t cr_offset = rockchip_vpu981_av1_dec_luma_size(ctx);
- size_t mv_offset = rockchip_vpu981_av1_dec_chroma_size(ctx);
int cur_width = frame->frame_width_minus_1 + 1;
int cur_height = frame->frame_height_minus_1 + 1;
int scale_width =
@@ -744,8 +742,8 @@ rockchip_vpu981_av1_dec_set_ref(struct hantro_ctx *ctx, int ref, int idx,
dst = vb2_to_hantro_decoded_buf(&av1_dec->frame_refs[idx].vb2_ref->vb2_buf);
luma_addr = hantro_get_dec_buf_addr(ctx, &dst->base.vb.vb2_buf);
- chroma_addr = luma_addr + cr_offset;
- mv_addr = luma_addr + mv_offset;
+ chroma_addr = luma_addr + dst->av1.chroma_offset;
+ mv_addr = luma_addr + dst->av1.mv_offset;
hantro_write_addr(vpu, AV1_REFERENCE_Y(ref), luma_addr);
hantro_write_addr(vpu, AV1_REFERENCE_CB(ref), chroma_addr);
@@ -2089,6 +2087,9 @@ rockchip_vpu981_av1_dec_set_output_buffer(struct hantro_ctx *ctx)
chroma_addr = luma_addr + cr_offset;
mv_addr = luma_addr + mv_offset;
+ dst->av1.chroma_offset = cr_offset;
+ dst->av1.mv_offset = mv_offset;
+
hantro_write_addr(vpu, AV1_TILE_OUT_LU, luma_addr);
hantro_write_addr(vpu, AV1_TILE_OUT_CH, chroma_addr);
hantro_write_addr(vpu, AV1_TILE_OUT_MV, mv_addr);
@@ -2201,6 +2202,10 @@ static void rockchip_vpu981_postproc_enable(struct hantro_ctx *ctx)
case V4L2_PIX_FMT_NV12:
hantro_reg_write(vpu, &av1_pp_out_format, 3);
break;
+ case V4L2_PIX_FMT_NV15:
+ /* this mapping is RK specific */
+ hantro_reg_write(vpu, &av1_pp_out_format, 10);
+ break;
default:
hantro_reg_write(vpu, &av1_pp_out_format, 0);
}
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h b/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h
index 850ff0f84424..e4008da64f19 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_regs.h
@@ -327,7 +327,7 @@
#define av1_apf_threshold AV1_DEC_REG(55, 0, 0xffff)
#define av1_apf_single_pu_mode AV1_DEC_REG(55, 30, 0x1)
-#define av1_apf_disable AV1_DEC_REG(55, 30, 0x1)
+#define av1_apf_disable AV1_DEC_REG(55, 31, 0x1)
#define av1_dec_max_burst AV1_DEC_REG(58, 0, 0xff)
#define av1_dec_buswidth AV1_DEC_REG(58, 8, 0x7)
@@ -337,10 +337,10 @@
#define av1_dec_mc_polltime AV1_DEC_REG(58, 17, 0x3ff)
#define av1_dec_mc_pollmode AV1_DEC_REG(58, 27, 0x3)
-#define av1_filt_ref_adj_3 AV1_DEC_REG(59, 0, 0x3f)
-#define av1_filt_ref_adj_2 AV1_DEC_REG(59, 7, 0x3f)
-#define av1_filt_ref_adj_1 AV1_DEC_REG(59, 14, 0x3f)
-#define av1_filt_ref_adj_0 AV1_DEC_REG(59, 21, 0x3f)
+#define av1_filt_ref_adj_3 AV1_DEC_REG(59, 0, 0x7f)
+#define av1_filt_ref_adj_2 AV1_DEC_REG(59, 7, 0x7f)
+#define av1_filt_ref_adj_1 AV1_DEC_REG(59, 14, 0x7f)
+#define av1_filt_ref_adj_0 AV1_DEC_REG(59, 21, 0x7f)
#define av1_ref0_sign_bias AV1_DEC_REG(59, 28, 0x1)
#define av1_ref1_sign_bias AV1_DEC_REG(59, 29, 0x1)
#define av1_ref2_sign_bias AV1_DEC_REG(59, 30, 0x1)
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index f97527670783..acd29fa41d2d 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -82,14 +82,27 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = {
{
.fourcc = V4L2_PIX_FMT_NV12,
.codec_mode = HANTRO_MODE_NONE,
+ .postprocessed = true,
+ .frmsize = {
+ .min_width = ROCKCHIP_VPU981_MIN_SIZE,
+ .max_width = FMT_4K_WIDTH,
+ .step_width = MB_DIM,
+ .min_height = ROCKCHIP_VPU981_MIN_SIZE,
+ .max_height = FMT_4K_HEIGHT,
+ .step_height = MB_DIM,
+ },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV15,
+ .codec_mode = HANTRO_MODE_NONE,
.match_depth = true,
.postprocessed = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -100,10 +113,10 @@ static const struct hantro_fmt rockchip_vpu981_postproc_fmts[] = {
.postprocessed = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -319,10 +332,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = {
.match_depth = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -332,10 +345,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = {
.match_depth = true,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
@@ -345,10 +358,10 @@ static const struct hantro_fmt rockchip_vpu981_dec_fmts[] = {
.max_depth = 2,
.frmsize = {
.min_width = ROCKCHIP_VPU981_MIN_SIZE,
- .max_width = FMT_UHD_WIDTH,
+ .max_width = FMT_4K_WIDTH,
.step_width = MB_DIM,
.min_height = ROCKCHIP_VPU981_MIN_SIZE,
- .max_height = FMT_UHD_HEIGHT,
+ .max_height = FMT_4K_HEIGHT,
.step_height = MB_DIM,
},
},
diff --git a/drivers/media/platform/via/via-camera.c b/drivers/media/platform/via/via-camera.c
index 4cb8f29e2f14..5702eff664d4 100644
--- a/drivers/media/platform/via/via-camera.c
+++ b/drivers/media/platform/via/via-camera.c
@@ -666,8 +666,6 @@ static const struct vb2_ops viacam_vb2_ops = {
.buf_prepare = viacam_vb2_prepare,
.start_streaming = viacam_vb2_start_streaming,
.stop_streaming = viacam_vb2_stop_streaming,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
};
/* --------------------------------------------------------------------------*/
@@ -1307,7 +1305,7 @@ static struct platform_driver viacam_driver = {
.name = "viafb-camera",
},
.probe = viacam_probe,
- .remove_new = viacam_remove,
+ .remove = viacam_remove,
};
module_platform_driver(viacam_driver);
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
index 31e9e92e723e..cba34893258a 100644
--- a/drivers/media/platform/video-mux.c
+++ b/drivers/media/platform/video-mux.c
@@ -52,6 +52,7 @@ static int video_mux_link_setup(struct media_entity *entity,
const struct media_pad *remote, u32 flags)
{
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
+ struct v4l2_subdev_state *sd_state;
struct video_mux *vmux = v4l2_subdev_to_video_mux(sd);
u16 source_pad = entity->num_pads - 1;
int ret = 0;
@@ -67,10 +68,10 @@ static int video_mux_link_setup(struct media_entity *entity,
remote->entity->name, remote->index, local->entity->name,
local->index, flags & MEDIA_LNK_FL_ENABLED);
+ sd_state = v4l2_subdev_lock_and_get_active_state(sd);
mutex_lock(&vmux->lock);
if (flags & MEDIA_LNK_FL_ENABLED) {
- struct v4l2_subdev_state *sd_state;
struct v4l2_mbus_framefmt *source_mbusformat;
if (vmux->active == local->index)
@@ -88,12 +89,10 @@ static int video_mux_link_setup(struct media_entity *entity,
vmux->active = local->index;
/* Propagate the active format to the source */
- sd_state = v4l2_subdev_lock_and_get_active_state(sd);
source_mbusformat = v4l2_subdev_state_get_format(sd_state,
source_pad);
*source_mbusformat = *v4l2_subdev_state_get_format(sd_state,
vmux->active);
- v4l2_subdev_unlock_state(sd_state);
} else {
if (vmux->active != local->index)
goto out;
@@ -105,6 +104,7 @@ static int video_mux_link_setup(struct media_entity *entity,
out:
mutex_unlock(&vmux->lock);
+ v4l2_subdev_unlock_state(sd_state);
return ret;
}
@@ -486,7 +486,7 @@ MODULE_DEVICE_TABLE(of, video_mux_dt_ids);
static struct platform_driver video_mux_driver = {
.probe = video_mux_probe,
- .remove_new = video_mux_remove,
+ .remove = video_mux_remove,
.driver = {
.of_match_table = video_mux_dt_ids,
.name = "video-mux",
diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
index f953d5474ae0..146131b8f37e 100644
--- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c
+++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
@@ -1028,7 +1028,7 @@ static struct platform_driver xcsi2rxss_driver = {
.of_match_table = xcsi2rxss_of_id_table,
},
.probe = xcsi2rxss_probe,
- .remove_new = xcsi2rxss_remove,
+ .remove = xcsi2rxss_remove,
};
module_platform_driver(xcsi2rxss_driver);
diff --git a/drivers/media/platform/xilinx/xilinx-dma.c b/drivers/media/platform/xilinx/xilinx-dma.c
index a96de5d388a1..18bfa6001909 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.c
+++ b/drivers/media/platform/xilinx/xilinx-dma.c
@@ -348,8 +348,8 @@ static void xvip_dma_buffer_queue(struct vb2_buffer *vb)
}
dma->xt.frame_size = 1;
- dma->sgl[0].size = dma->format.width * dma->fmtinfo->bpp;
- dma->sgl[0].icg = dma->format.bytesperline - dma->sgl[0].size;
+ dma->sgl.size = dma->format.width * dma->fmtinfo->bpp;
+ dma->sgl.icg = dma->format.bytesperline - dma->sgl.size;
dma->xt.numf = dma->format.height;
desc = dmaengine_prep_interleaved_dma(dma->dma, &dma->xt, flags);
@@ -458,8 +458,6 @@ static const struct vb2_ops xvip_dma_queue_qops = {
.queue_setup = xvip_dma_queue_setup,
.buf_prepare = xvip_dma_buffer_prepare,
.buf_queue = xvip_dma_buffer_queue,
- .wait_prepare = vb2_ops_wait_prepare,
- .wait_finish = vb2_ops_wait_finish,
.start_streaming = xvip_dma_start_streaming,
.stop_streaming = xvip_dma_stop_streaming,
};
diff --git a/drivers/media/platform/xilinx/xilinx-dma.h b/drivers/media/platform/xilinx/xilinx-dma.h
index 9c6d4c18d1a9..18f77e1a7b39 100644
--- a/drivers/media/platform/xilinx/xilinx-dma.h
+++ b/drivers/media/platform/xilinx/xilinx-dma.h
@@ -97,7 +97,7 @@ struct xvip_dma {
struct dma_chan *dma;
unsigned int align;
struct dma_interleaved_template xt;
- struct data_chunk sgl[1];
+ struct data_chunk sgl;
};
#define to_xvip_dma(vdev) container_of(vdev, struct xvip_dma, video)
diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c
index e05e528ffc6f..7deec6e37edc 100644
--- a/drivers/media/platform/xilinx/xilinx-tpg.c
+++ b/drivers/media/platform/xilinx/xilinx-tpg.c
@@ -13,6 +13,7 @@
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/xilinx-v4l2-controls.h>
@@ -711,26 +712,16 @@ static int xtpg_parse_of(struct xtpg_device *xtpg)
{
struct device *dev = xtpg->xvip.dev;
struct device_node *node = xtpg->xvip.dev->of_node;
- struct device_node *ports;
- struct device_node *port;
unsigned int nports = 0;
bool has_endpoint = false;
- ports = of_get_child_by_name(node, "ports");
- if (ports == NULL)
- ports = node;
-
- for_each_child_of_node(ports, port) {
+ for_each_of_graph_port(node, port) {
const struct xvip_video_format *format;
struct device_node *endpoint;
- if (!of_node_name_eq(port, "port"))
- continue;
-
format = xvip_of_get_format(port);
if (IS_ERR(format)) {
dev_err(dev, "invalid format in DT");
- of_node_put(port);
return PTR_ERR(format);
}
@@ -739,12 +730,11 @@ static int xtpg_parse_of(struct xtpg_device *xtpg)
xtpg->vip_format = format;
} else if (xtpg->vip_format != format) {
dev_err(dev, "in/out format mismatch in DT");
- of_node_put(port);
return -EINVAL;
}
if (nports == 0) {
- endpoint = of_get_next_child(port, NULL);
+ endpoint = of_graph_get_next_port_endpoint(port, NULL);
if (endpoint)
has_endpoint = true;
of_node_put(endpoint);
@@ -920,7 +910,7 @@ static struct platform_driver xtpg_driver = {
.of_match_table = xtpg_of_id_table,
},
.probe = xtpg_probe,
- .remove_new = xtpg_remove,
+ .remove = xtpg_remove,
};
module_platform_driver(xtpg_driver);
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index 996684a73038..024b439feec9 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -199,18 +199,13 @@ static int xvip_graph_build_dma(struct xvip_composite_device *xdev)
struct media_pad *sink_pad;
struct xvip_graph_entity *ent;
struct v4l2_fwnode_link link;
- struct device_node *ep = NULL;
+ struct device_node *ep;
struct xvip_dma *dma;
int ret = 0;
dev_dbg(xdev->dev, "creating links for DMA engines\n");
- while (1) {
- /* Get the next endpoint and parse its link. */
- ep = of_graph_get_next_endpoint(node, ep);
- if (ep == NULL)
- break;
-
+ for_each_endpoint_of_node(node, ep) {
dev_dbg(xdev->dev, "processing endpoint %pOF\n", ep);
ret = v4l2_fwnode_parse_link(of_fwnode_handle(ep), &link);
@@ -623,7 +618,7 @@ static struct platform_driver xvip_composite_driver = {
.of_match_table = xvip_composite_of_id_table,
},
.probe = xvip_composite_probe,
- .remove_new = xvip_composite_remove,
+ .remove = xvip_composite_remove,
};
module_platform_driver(xvip_composite_driver);
diff --git a/drivers/media/platform/xilinx/xilinx-vtc.c b/drivers/media/platform/xilinx/xilinx-vtc.c
index dda70719f004..92fec7bb47da 100644
--- a/drivers/media/platform/xilinx/xilinx-vtc.c
+++ b/drivers/media/platform/xilinx/xilinx-vtc.c
@@ -365,7 +365,7 @@ static struct platform_driver xvtc_driver = {
.of_match_table = xvtc_of_id_table,
},
.probe = xvtc_probe,
- .remove_new = xvtc_remove,
+ .remove = xvtc_remove,
};
module_platform_driver(xvtc_driver);