aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-07-31 13:16:09 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2025-07-31 13:16:09 -0700
commit0cdee263bc5e7b20f657ea09f9272f50c568f35b (patch)
tree1841bfda91eeb316666fc401c8dca7267ae806e9 /drivers
parentMerge tag 'libnvdimm-for-6.17' of git://git.kernel.org/pub/scm/linux/kernel/g... (diff)
parentmedia: rkvdec: Unstage the driver (diff)
downloadlinux-0cdee263bc5e7b20f657ea09f9272f50c568f35b.tar.gz
linux-0cdee263bc5e7b20f657ea09f9272f50c568f35b.zip
Merge tag 'media/v6.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - v4l2 core: - sub-device framework routing improvements - NV12M tiled variants added to v4l2_format_info - some fixes at control handler freeing logic - fixed H264 SEPARATE_COLOUR_PLANE check - new staging driver: Intel IPU7 PCI - Rockchip video decoder driver got promoted from staging - iris: added HEVC/VP9 encoder/decoder support - vsp1: driver has gained Renesas VSPX support - uvc: - switched to vb2 ioctl helpers - added MSXU 1.5 metadata support - atomisp: GC0310 sensor driver cleanups in preparation for moving it out of staging - Lots of cleanup, fixes and improvements * tag 'media/v6.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (310 commits) media: rkvdec: Unstage the driver media: rkvdec: Remove TODO file media: dt-bindings: rockchip: Add RK3576 Video Decoder bindings media: dt-bindings: rockchip: Document RK3588 Video Decoder bindings media: amphion: Support dmabuf and v4l2 buffer without binding media: verisilicon: postproc: 4K support media: v4l2: Add support for NV12M tiled variants to v4l2_format_info() media: uvcvideo: Use a count variable for meta_formats instead of 0 terminating media: uvcvideo: Auto-set UVC_QUIRK_MSXU_META media: uvcvideo: Introduce V4L2_META_FMT_UVC_MSXU_1_5 media: uvcvideo: Introduce dev->meta_formats media: Documentation: Add note about UVCH length field media: uvcvideo: Do not mark valid metadata as invalid media: uvcvideo: uvc_v4l2_unlocked_ioctl: Invert PM logic media: core: export v4l2_translate_cmd media: uvcvideo: Turn on the camera if V4L2_EVENT_SUB_FL_SEND_INITIAL media: uvcvideo: Remove stream->is_streaming field media: uvcvideo: Split uvc_stop_streaming() media: uvcvideo: Handle locks in uvc_queue_return_buffers media: uvcvideo: Use vb2 ioctl and fop helpers ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/cec/core/cec-pin-error-inj.c59
-rw-r--r--drivers/media/cec/core/cec-pin-priv.h8
-rw-r--r--drivers/media/cec/core/cec-pin.c31
-rw-r--r--drivers/media/cec/platform/cec-gpio/cec-gpio.c58
-rw-r--r--drivers/media/cec/usb/rainshadow/rainshadow-cec.c3
-rw-r--r--drivers/media/common/b2c2/flexcop-i2c.c2
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c6
-rw-r--r--drivers/media/dvb-frontends/dib7000p.c8
-rw-r--r--drivers/media/i2c/Kconfig41
-rw-r--r--drivers/media/i2c/adv7180.c16
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c17
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c10
-rw-r--r--drivers/media/i2c/adv7604.c4
-rw-r--r--drivers/media/i2c/ds90ub913.c14
-rw-r--r--drivers/media/i2c/ds90ub953.c19
-rw-r--r--drivers/media/i2c/ds90ub960.c8
-rw-r--r--drivers/media/i2c/dw9714.c62
-rw-r--r--drivers/media/i2c/hi556.c73
-rw-r--r--drivers/media/i2c/imx214.c263
-rw-r--r--drivers/media/i2c/imx290.c1
-rw-r--r--drivers/media/i2c/imx415.c2
-rw-r--r--drivers/media/i2c/lt6911uxe.c2
-rw-r--r--drivers/media/i2c/max9286.c8
-rw-r--r--drivers/media/i2c/max96714.c7
-rw-r--r--drivers/media/i2c/max96717.c11
-rw-r--r--drivers/media/i2c/mt9m114.c171
-rw-r--r--drivers/media/i2c/ov2659.c3
-rw-r--r--drivers/media/i2c/ov2740.c18
-rw-r--r--drivers/media/i2c/ov5670.c9
-rw-r--r--drivers/media/i2c/ov5693.c7
-rw-r--r--drivers/media/i2c/ov7251.c7
-rw-r--r--drivers/media/i2c/ov8865.c3
-rw-r--r--drivers/media/i2c/saa7115.c12
-rw-r--r--drivers/media/i2c/tc358743.c138
-rw-r--r--drivers/media/i2c/tda1997x.c4
-rw-r--r--drivers/media/i2c/vd55g1.c32
-rw-r--r--drivers/media/pci/cx18/cx18-av-vbi.c12
-rw-r--r--drivers/media/pci/cx18/cx18-driver.h12
-rw-r--r--drivers/media/pci/cx18/cx18-fileops.c2
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c2
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c13
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c82
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.h2
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c12
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.h2
-rw-r--r--drivers/media/pci/intel/ivsc/mei_ace.c4
-rw-r--r--drivers/media/pci/intel/ivsc/mei_csi.c4
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.c9
-rw-r--r--drivers/media/pci/saa7164/saa7164-buffer.c20
-rw-r--r--drivers/media/pci/saa7164/saa7164-cmd.c28
-rw-r--r--drivers/media/pci/saa7164/saa7164.h2
-rw-r--r--drivers/media/pci/solo6x10/solo6x10-gpio.c22
-rw-r--r--drivers/media/platform/amphion/vdec.c294
-rw-r--r--drivers/media/platform/amphion/vpu.h7
-rw-r--r--drivers/media/platform/amphion/vpu_color.c73
-rw-r--r--drivers/media/platform/amphion/vpu_dbg.c15
-rw-r--r--drivers/media/platform/amphion/vpu_defs.h12
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.c123
-rw-r--r--drivers/media/platform/amphion/vpu_helpers.h12
-rw-r--r--drivers/media/platform/amphion/vpu_malone.c5
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.c4
-rw-r--r--drivers/media/platform/amphion/vpu_mbox.h1
-rw-r--r--drivers/media/platform/amphion/vpu_v4l2.c11
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c131
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c47
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h1
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c68
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c135
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h6
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c18
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c169
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c3
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c5
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h1
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c39
-rw-r--r--drivers/media/platform/qcom/camss/camss.c107
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c35
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.h3
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c35
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c48
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h5
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c37
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c143
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h5
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c56
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_queue.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h6
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h28
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_gen2.c198
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h126
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8250.c15
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_state.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c18
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c116
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.h11
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c36
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c397
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.h46
-rw-r--r--drivers/media/platform/qcom/venus/core.c18
-rw-r--r--drivers/media/platform/qcom/venus/core.h2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c83
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c5
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c58
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c8
-rw-r--r--drivers/media/platform/qcom/venus/venc.c8
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Kconfig1
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be.c207
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.c4
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c336
-rw-r--r--drivers/media/platform/renesas/rcar-fcp.c36
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c694
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c77
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c492
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-vin.h16
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c6
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h14
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c45
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c108
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c42
-rw-r--r--drivers/media/platform/renesas/vsp1/Makefile1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1.h1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c25
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drv.c22
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_regs.h1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.c633
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.h16
-rw-r--r--drivers/media/platform/rockchip/Kconfig1
-rw-r--r--drivers/media/platform/rockchip/Makefile1
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-common.h2
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c150
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h99
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Kconfig (renamed from drivers/staging/media/rkvdec/Kconfig)0
-rw-r--r--drivers/media/platform/rockchip/rkvdec/Makefile (renamed from drivers/staging/media/rkvdec/Makefile)0
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c (renamed from drivers/staging/media/rkvdec/rkvdec-h264.c)0
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h (renamed from drivers/staging/media/rkvdec/rkvdec-regs.h)0
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c (renamed from drivers/staging/media/rkvdec/rkvdec-vp9.c)0
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.c (renamed from drivers/staging/media/rkvdec/rkvdec.c)43
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec.h (renamed from drivers/staging/media/rkvdec/rkvdec.h)1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-is.c2
-rw-r--r--drivers/media/platform/samsung/exynos4-is/media-dev.c27
-rw-r--r--drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c34
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c2
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.c32
-rw-r--r--drivers/media/platform/ti/vpe/vpdma.h3
-rw-r--r--drivers/media/platform/verisilicon/hantro.h2
-rw-r--r--drivers/media/platform/verisilicon/hantro_g1_regs.h2
-rw-r--r--drivers/media/platform/verisilicon/hantro_h264.c6
-rw-r--r--drivers/media/platform/verisilicon/hantro_postproc.c6
-rw-r--r--drivers/media/platform/verisilicon/rockchip_vpu_hw.c9
-rw-r--r--drivers/media/platform/xilinx/xilinx-vipp.c7
-rw-r--r--drivers/media/rc/ir-spi.c40
-rw-r--r--drivers/media/test-drivers/vivid/vivid-ctrls.c3
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vbi-gen.c8
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c4
-rw-r--r--drivers/media/usb/gspca/vicam.c10
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c30
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c4
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c131
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c74
-rw-r--r--drivers/media/usb/uvc/uvc_metadata.c122
-rw-r--r--drivers/media/usb/uvc/uvc_queue.c199
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c366
-rw-r--r--drivers/media/usb/uvc/uvc_video.c21
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h46
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c40
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c20
-rw-r--r--drivers/media/v4l2-core/v4l2-i2c.c3
-rw-r--r--drivers/media/v4l2-core/v4l2-ioctl.c10
-rw-r--r--drivers/media/v4l2-core/v4l2-jpeg.c80
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c31
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470_board_data.c128
-rw-r--r--drivers/staging/media/Kconfig4
-rw-r--r--drivers/staging/media/Makefile2
-rw-r--r--drivers/staging/media/atomisp/Kconfig1
-rw-r--r--drivers/staging/media/atomisp/Makefile1
-rw-r--r--drivers/staging/media/atomisp/TODO2
-rw-r--r--drivers/staging/media/atomisp/i2c/Kconfig1
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc0310.c611
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c2
-rw-r--r--drivers/staging/media/atomisp/i2c/gc2235.h16
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2722.h16
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_css20.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2.h17
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c233
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_drvfs.c155
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_drvfs.h15
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_fops.c5
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_ioctl.c129
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.h3
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_v4l2.c5
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h5
-rw-r--r--drivers/staging/media/atomisp/pci/hmm/hmm.c91
-rw-r--r--drivers/staging/media/atomisp/pci/hmm/hmm_bo.c5
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_pipe.h2
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h4
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h6
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c4
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h22
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c6
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h2
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c3
-rw-r--r--drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h6
-rw-r--r--drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h157
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c1
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c29
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c11
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c2
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h1
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c2
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css.c27
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_defs.h12
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_internal.h8
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_mipi.c11
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_mipi.h2
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_dvs.h22
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_params.c12
-rw-r--r--drivers/staging/media/imx/imx-media-csc-scaler.c2
-rw-r--r--drivers/staging/media/ipu7/Kconfig19
-rw-r--r--drivers/staging/media/ipu7/Makefile23
-rw-r--r--drivers/staging/media/ipu7/TODO28
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h163
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h175
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h19
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h19
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h412
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h465
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h24
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h49
-rw-r--r--drivers/staging/media/ipu7/ipu7-boot.c430
-rw-r--r--drivers/staging/media/ipu7/ipu7-boot.h25
-rw-r--r--drivers/staging/media/ipu7/ipu7-bus.c158
-rw-r--r--drivers/staging/media/ipu7/ipu7-bus.h69
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress-regs.h461
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress.c1192
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress.h77
-rw-r--r--drivers/staging/media/ipu7/ipu7-cpd.c276
-rw-r--r--drivers/staging/media/ipu7/ipu7-cpd.h16
-rw-r--r--drivers/staging/media/ipu7/ipu7-dma.c477
-rw-r--r--drivers/staging/media/ipu7/ipu7-dma.h46
-rw-r--r--drivers/staging/media/ipu7/ipu7-fw-isys.c301
-rw-r--r--drivers/staging/media/ipu7/ipu7-fw-isys.h39
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi-phy.c1034
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi-phy.h16
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h1197
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2.c543
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2.h64
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-queue.c829
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-queue.h72
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-subdev.c348
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-subdev.h53
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-video.c1112
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-video.h117
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys.c1166
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys.h140
-rw-r--r--drivers/staging/media/ipu7/ipu7-mmu.c853
-rw-r--r--drivers/staging/media/ipu7/ipu7-mmu.h414
-rw-r--r--drivers/staging/media/ipu7/ipu7-platform-regs.h82
-rw-r--r--drivers/staging/media/ipu7/ipu7-syscom.c78
-rw-r--r--drivers/staging/media/ipu7/ipu7-syscom.h35
-rw-r--r--drivers/staging/media/ipu7/ipu7.c2783
-rw-r--r--drivers/staging/media/ipu7/ipu7.h242
-rw-r--r--drivers/staging/media/rkvdec/TODO11
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_hw.c19
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_video.c18
271 files changed, 22176 insertions, 4478 deletions
diff --git a/drivers/media/cec/core/cec-pin-error-inj.c b/drivers/media/cec/core/cec-pin-error-inj.c
index 6e61a04b8168..d9e613c7ce3f 100644
--- a/drivers/media/cec/core/cec-pin-error-inj.c
+++ b/drivers/media/cec/core/cec-pin-error-inj.c
@@ -91,16 +91,22 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
if (!strcmp(token, "clear")) {
memset(pin->error_inj, 0, sizeof(pin->error_inj));
pin->rx_toggle = pin->tx_toggle = false;
+ pin->rx_no_low_drive = false;
pin->tx_ignore_nack_until_eom = false;
pin->tx_custom_pulse = false;
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_falling_edge = false;
+ pin->tx_glitch_rising_edge = false;
return true;
}
if (!strcmp(token, "rx-clear")) {
for (i = 0; i <= CEC_ERROR_INJ_OP_ANY; i++)
pin->error_inj[i] &= ~CEC_ERROR_INJ_RX_MASK;
pin->rx_toggle = false;
+ pin->rx_no_low_drive = false;
return true;
}
if (!strcmp(token, "tx-clear")) {
@@ -111,6 +117,14 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
pin->tx_custom_pulse = false;
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_falling_edge = false;
+ pin->tx_glitch_rising_edge = false;
+ return true;
+ }
+ if (!strcmp(token, "rx-no-low-drive")) {
+ pin->rx_no_low_drive = true;
return true;
}
if (!strcmp(token, "tx-ignore-nack-until-eom")) {
@@ -122,6 +136,14 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
cec_pin_start_timer(pin);
return true;
}
+ if (!strcmp(token, "tx-glitch-falling-edge")) {
+ pin->tx_glitch_falling_edge = true;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-rising-edge")) {
+ pin->tx_glitch_rising_edge = true;
+ return true;
+ }
if (!p)
return false;
@@ -139,7 +161,23 @@ bool cec_pin_error_inj_parse_line(struct cec_adapter *adap, char *line)
if (kstrtou32(p, 0, &usecs) || usecs > 10000000)
return false;
- pin->tx_custom_high_usecs = usecs;
+ pin->tx_glitch_high_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-low-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 100)
+ return false;
+ pin->tx_glitch_low_usecs = usecs;
+ return true;
+ }
+ if (!strcmp(token, "tx-glitch-high-usecs")) {
+ u32 usecs;
+
+ if (kstrtou32(p, 0, &usecs) || usecs > 100)
+ return false;
+ pin->tx_glitch_high_usecs = usecs;
return true;
}
@@ -273,6 +311,9 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
seq_puts(sf, "# <op> rx-clear clear all rx error injections for <op>\n");
seq_puts(sf, "# <op> tx-clear clear all tx error injections for <op>\n");
seq_puts(sf, "#\n");
+ seq_puts(sf, "# RX error injection settings:\n");
+ seq_puts(sf, "# rx-no-low-drive do not generate low-drive pulses\n");
+ seq_puts(sf, "#\n");
seq_puts(sf, "# RX error injection:\n");
seq_puts(sf, "# <op>[,<mode>] rx-nack NACK the message instead of sending an ACK\n");
seq_puts(sf, "# <op>[,<mode>] rx-low-drive <bit> force a low-drive condition at this bit position\n");
@@ -285,6 +326,10 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
seq_puts(sf, "# tx-custom-low-usecs <usecs> define the 'low' time for the custom pulse\n");
seq_puts(sf, "# tx-custom-high-usecs <usecs> define the 'high' time for the custom pulse\n");
seq_puts(sf, "# tx-custom-pulse transmit the custom pulse once the bus is idle\n");
+ seq_puts(sf, "# tx-glitch-low-usecs <usecs> define the 'low' time for the glitch pulse\n");
+ seq_puts(sf, "# tx-glitch-high-usecs <usecs> define the 'high' time for the glitch pulse\n");
+ seq_puts(sf, "# tx-glitch-falling-edge send the glitch pulse after every falling edge\n");
+ seq_puts(sf, "# tx-glitch-rising-edge send the glitch pulse after every rising edge\n");
seq_puts(sf, "#\n");
seq_puts(sf, "# TX error injection:\n");
seq_puts(sf, "# <op>[,<mode>] tx-no-eom don't set the EOM bit\n");
@@ -332,8 +377,14 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
}
}
+ if (pin->rx_no_low_drive)
+ seq_puts(sf, "rx-no-low-drive\n");
if (pin->tx_ignore_nack_until_eom)
seq_puts(sf, "tx-ignore-nack-until-eom\n");
+ if (pin->tx_glitch_falling_edge)
+ seq_puts(sf, "tx-glitch-falling-edge\n");
+ if (pin->tx_glitch_rising_edge)
+ seq_puts(sf, "tx-glitch-rising-edge\n");
if (pin->tx_custom_pulse)
seq_puts(sf, "tx-custom-pulse\n");
if (pin->tx_custom_low_usecs != CEC_TIM_CUSTOM_DEFAULT)
@@ -342,5 +393,11 @@ int cec_pin_error_inj_show(struct cec_adapter *adap, struct seq_file *sf)
if (pin->tx_custom_high_usecs != CEC_TIM_CUSTOM_DEFAULT)
seq_printf(sf, "tx-custom-high-usecs %u\n",
pin->tx_custom_high_usecs);
+ if (pin->tx_glitch_low_usecs != CEC_TIM_GLITCH_DEFAULT)
+ seq_printf(sf, "tx-glitch-low-usecs %u\n",
+ pin->tx_glitch_low_usecs);
+ if (pin->tx_glitch_high_usecs != CEC_TIM_GLITCH_DEFAULT)
+ seq_printf(sf, "tx-glitch-high-usecs %u\n",
+ pin->tx_glitch_high_usecs);
return 0;
}
diff --git a/drivers/media/cec/core/cec-pin-priv.h b/drivers/media/cec/core/cec-pin-priv.h
index 156a9f81be94..e7801be9adb9 100644
--- a/drivers/media/cec/core/cec-pin-priv.h
+++ b/drivers/media/cec/core/cec-pin-priv.h
@@ -164,6 +164,9 @@ enum cec_pin_state {
/* The default for the low/high time of the custom pulse */
#define CEC_TIM_CUSTOM_DEFAULT 1000
+/* The default for the low/high time of the glitch pulse */
+#define CEC_TIM_GLITCH_DEFAULT 1
+
#define CEC_NUM_PIN_EVENTS 128
#define CEC_PIN_EVENT_FL_IS_HIGH (1 << 0)
#define CEC_PIN_EVENT_FL_DROPPED (1 << 1)
@@ -225,12 +228,17 @@ struct cec_pin {
u32 timer_max_overrun;
u32 timer_sum_overrun;
+ bool rx_no_low_drive;
u32 tx_custom_low_usecs;
u32 tx_custom_high_usecs;
+ u32 tx_glitch_low_usecs;
+ u32 tx_glitch_high_usecs;
bool tx_ignore_nack_until_eom;
bool tx_custom_pulse;
bool tx_generated_poll;
bool tx_post_eom;
+ bool tx_glitch_falling_edge;
+ bool tx_glitch_rising_edge;
u8 tx_extra_bytes;
u32 tx_low_drive_cnt;
#ifdef CONFIG_CEC_PIN_ERROR_INJ
diff --git a/drivers/media/cec/core/cec-pin.c b/drivers/media/cec/core/cec-pin.c
index 59ac12113f3a..4d7155281daa 100644
--- a/drivers/media/cec/core/cec-pin.c
+++ b/drivers/media/cec/core/cec-pin.c
@@ -142,15 +142,42 @@ static bool cec_pin_read(struct cec_pin *pin)
return v;
}
+static void cec_pin_insert_glitch(struct cec_pin *pin, bool rising_edge)
+{
+ /*
+ * Insert a short glitch after the falling or rising edge to
+ * simulate reflections on the CEC line. This can be used to
+ * test deglitch filters, which should be present in CEC devices
+ * to deal with noise on the line.
+ */
+ if (!pin->tx_glitch_high_usecs || !pin->tx_glitch_low_usecs)
+ return;
+ if (rising_edge) {
+ udelay(pin->tx_glitch_high_usecs);
+ call_void_pin_op(pin, low);
+ udelay(pin->tx_glitch_low_usecs);
+ call_void_pin_op(pin, high);
+ } else {
+ udelay(pin->tx_glitch_low_usecs);
+ call_void_pin_op(pin, high);
+ udelay(pin->tx_glitch_high_usecs);
+ call_void_pin_op(pin, low);
+ }
+}
+
static void cec_pin_low(struct cec_pin *pin)
{
call_void_pin_op(pin, low);
+ if (pin->tx_glitch_falling_edge && pin->adap->cec_pin_is_high)
+ cec_pin_insert_glitch(pin, false);
cec_pin_update(pin, false, false);
}
static bool cec_pin_high(struct cec_pin *pin)
{
call_void_pin_op(pin, high);
+ if (pin->tx_glitch_rising_edge && !pin->adap->cec_pin_is_high)
+ cec_pin_insert_glitch(pin, true);
return cec_pin_read(pin);
}
@@ -770,7 +797,7 @@ static void cec_pin_rx_states(struct cec_pin *pin, ktime_t ts)
* Go to low drive state when the total bit time is
* too short.
*/
- if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN) {
+ if (delta < CEC_TIM_DATA_BIT_TOTAL_MIN && !pin->rx_no_low_drive) {
if (!pin->rx_data_bit_too_short_cnt++) {
pin->rx_data_bit_too_short_ts = ktime_to_ns(pin->ts);
pin->rx_data_bit_too_short_delta = delta;
@@ -1350,6 +1377,8 @@ struct cec_adapter *cec_pin_allocate_adapter(const struct cec_pin_ops *pin_ops,
init_waitqueue_head(&pin->kthread_waitq);
pin->tx_custom_low_usecs = CEC_TIM_CUSTOM_DEFAULT;
pin->tx_custom_high_usecs = CEC_TIM_CUSTOM_DEFAULT;
+ pin->tx_glitch_low_usecs = CEC_TIM_GLITCH_DEFAULT;
+ pin->tx_glitch_high_usecs = CEC_TIM_GLITCH_DEFAULT;
adap = cec_allocate_adapter(&cec_pin_adap_ops, priv, name,
caps | CEC_CAP_MONITOR_ALL | CEC_CAP_MONITOR_PIN,
diff --git a/drivers/media/cec/platform/cec-gpio/cec-gpio.c b/drivers/media/cec/platform/cec-gpio/cec-gpio.c
index 50cdc557c943..3c27789d8657 100644
--- a/drivers/media/cec/platform/cec-gpio/cec-gpio.c
+++ b/drivers/media/cec/platform/cec-gpio/cec-gpio.c
@@ -61,49 +61,51 @@ static void cec_gpio_low(struct cec_adapter *adap)
gpiod_set_value(cec->cec_gpio, 0);
}
-static irqreturn_t cec_hpd_gpio_irq_handler_thread(int irq, void *priv)
+static irqreturn_t cec_gpio_5v_irq_handler_thread(int irq, void *priv)
{
struct cec_gpio *cec = priv;
+ int val = gpiod_get_value_cansleep(cec->v5_gpio);
+ bool is_high = val > 0;
- cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts);
+ if (val < 0 || is_high == cec->v5_is_high)
+ return IRQ_HANDLED;
+
+ cec->v5_is_high = is_high;
+ cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts);
return IRQ_HANDLED;
}
-static irqreturn_t cec_5v_gpio_irq_handler(int irq, void *priv)
+static irqreturn_t cec_gpio_5v_irq_handler(int irq, void *priv)
{
struct cec_gpio *cec = priv;
- int val = gpiod_get_value(cec->v5_gpio);
- bool is_high = val > 0;
- if (val < 0 || is_high == cec->v5_is_high)
- return IRQ_HANDLED;
cec->v5_ts = ktime_get();
- cec->v5_is_high = is_high;
return IRQ_WAKE_THREAD;
}
-static irqreturn_t cec_5v_gpio_irq_handler_thread(int irq, void *priv)
+static irqreturn_t cec_gpio_hpd_irq_handler_thread(int irq, void *priv)
{
struct cec_gpio *cec = priv;
+ int val = gpiod_get_value_cansleep(cec->hpd_gpio);
+ bool is_high = val > 0;
- cec_queue_pin_5v_event(cec->adap, cec->v5_is_high, cec->v5_ts);
+ if (val < 0 || is_high == cec->hpd_is_high)
+ return IRQ_HANDLED;
+
+ cec->hpd_is_high = is_high;
+ cec_queue_pin_hpd_event(cec->adap, cec->hpd_is_high, cec->hpd_ts);
return IRQ_HANDLED;
}
-static irqreturn_t cec_hpd_gpio_irq_handler(int irq, void *priv)
+static irqreturn_t cec_gpio_hpd_irq_handler(int irq, void *priv)
{
struct cec_gpio *cec = priv;
- int val = gpiod_get_value(cec->hpd_gpio);
- bool is_high = val > 0;
- if (val < 0 || is_high == cec->hpd_is_high)
- return IRQ_HANDLED;
cec->hpd_ts = ktime_get();
- cec->hpd_is_high = is_high;
return IRQ_WAKE_THREAD;
}
-static irqreturn_t cec_gpio_irq_handler(int irq, void *priv)
+static irqreturn_t cec_gpio_cec_irq_handler(int irq, void *priv)
{
struct cec_gpio *cec = priv;
int val = gpiod_get_value(cec->cec_gpio);
@@ -113,7 +115,7 @@ static irqreturn_t cec_gpio_irq_handler(int irq, void *priv)
return IRQ_HANDLED;
}
-static bool cec_gpio_enable_irq(struct cec_adapter *adap)
+static bool cec_gpio_cec_enable_irq(struct cec_adapter *adap)
{
struct cec_gpio *cec = cec_get_drvdata(adap);
@@ -121,7 +123,7 @@ static bool cec_gpio_enable_irq(struct cec_adapter *adap)
return true;
}
-static void cec_gpio_disable_irq(struct cec_adapter *adap)
+static void cec_gpio_cec_disable_irq(struct cec_adapter *adap)
{
struct cec_gpio *cec = cec_get_drvdata(adap);
@@ -148,7 +150,7 @@ static int cec_gpio_read_hpd(struct cec_adapter *adap)
if (!cec->hpd_gpio)
return -ENOTTY;
- return gpiod_get_value(cec->hpd_gpio);
+ return gpiod_get_value_cansleep(cec->hpd_gpio);
}
static int cec_gpio_read_5v(struct cec_adapter *adap)
@@ -157,15 +159,15 @@ static int cec_gpio_read_5v(struct cec_adapter *adap)
if (!cec->v5_gpio)
return -ENOTTY;
- return gpiod_get_value(cec->v5_gpio);
+ return gpiod_get_value_cansleep(cec->v5_gpio);
}
static const struct cec_pin_ops cec_gpio_pin_ops = {
.read = cec_gpio_read,
.low = cec_gpio_low,
.high = cec_gpio_high,
- .enable_irq = cec_gpio_enable_irq,
- .disable_irq = cec_gpio_disable_irq,
+ .enable_irq = cec_gpio_cec_enable_irq,
+ .disable_irq = cec_gpio_cec_disable_irq,
.status = cec_gpio_status,
.read_hpd = cec_gpio_read_hpd,
.read_5v = cec_gpio_read_5v,
@@ -209,7 +211,7 @@ static int cec_gpio_probe(struct platform_device *pdev)
if (IS_ERR(cec->adap))
return PTR_ERR(cec->adap);
- ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_irq_handler,
+ ret = devm_request_irq(dev, cec->cec_irq, cec_gpio_cec_irq_handler,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN,
cec->adap->name, cec);
if (ret)
@@ -218,8 +220,8 @@ static int cec_gpio_probe(struct platform_device *pdev)
if (cec->hpd_gpio) {
cec->hpd_irq = gpiod_to_irq(cec->hpd_gpio);
ret = devm_request_threaded_irq(dev, cec->hpd_irq,
- cec_hpd_gpio_irq_handler,
- cec_hpd_gpio_irq_handler_thread,
+ cec_gpio_hpd_irq_handler,
+ cec_gpio_hpd_irq_handler_thread,
IRQF_ONESHOT |
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"hpd-gpio", cec);
@@ -230,8 +232,8 @@ static int cec_gpio_probe(struct platform_device *pdev)
if (cec->v5_gpio) {
cec->v5_irq = gpiod_to_irq(cec->v5_gpio);
ret = devm_request_threaded_irq(dev, cec->v5_irq,
- cec_5v_gpio_irq_handler,
- cec_5v_gpio_irq_handler_thread,
+ cec_gpio_5v_irq_handler,
+ cec_gpio_5v_irq_handler_thread,
IRQF_ONESHOT |
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"v5-gpio", cec);
diff --git a/drivers/media/cec/usb/rainshadow/rainshadow-cec.c b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
index ee870ea1a886..6f8d6797c614 100644
--- a/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
+++ b/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
@@ -171,11 +171,12 @@ static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data,
{
struct rain *rain = serio_get_drvdata(serio);
+ spin_lock(&rain->buf_lock);
if (rain->buf_len == DATA_SIZE) {
+ spin_unlock(&rain->buf_lock);
dev_warn_once(rain->dev, "buffer overflow\n");
return IRQ_HANDLED;
}
- spin_lock(&rain->buf_lock);
rain->buf_len++;
rain->buf[rain->buf_wr_idx] = data;
rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
diff --git a/drivers/media/common/b2c2/flexcop-i2c.c b/drivers/media/common/b2c2/flexcop-i2c.c
index 1f1eaa807811..21edf870d927 100644
--- a/drivers/media/common/b2c2/flexcop-i2c.c
+++ b/drivers/media/common/b2c2/flexcop-i2c.c
@@ -209,7 +209,7 @@ static u32 flexcop_i2c_func(struct i2c_adapter *adapter)
return I2C_FUNC_I2C;
}
-static struct i2c_algorithm flexcop_algo = {
+static const struct i2c_algorithm flexcop_algo = {
.master_xfer = flexcop_master_xfer,
.functionality = flexcop_i2c_func,
};
diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
index c3d8ced6c3ba..a31a8a6a4946 100644
--- a/drivers/media/dvb-frontends/cxd2820r_core.c
+++ b/drivers/media/dvb-frontends/cxd2820r_core.c
@@ -433,7 +433,7 @@ static int cxd2820r_gpio_direction_output(struct gpio_chip *chip, unsigned nr,
return cxd2820r_gpio(&priv->fe, gpio);
}
-static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
+static int cxd2820r_gpio_set(struct gpio_chip *chip, unsigned int nr, int val)
{
struct cxd2820r_priv *priv = gpiochip_get_data(chip);
struct i2c_client *client = priv->client[0];
@@ -446,7 +446,7 @@ static void cxd2820r_gpio_set(struct gpio_chip *chip, unsigned nr, int val)
(void) cxd2820r_gpio(&priv->fe, gpio);
- return;
+ return 0;
}
static int cxd2820r_gpio_get(struct gpio_chip *chip, unsigned nr)
@@ -651,7 +651,7 @@ static int cxd2820r_probe(struct i2c_client *client)
priv->gpio_chip.parent = &client->dev;
priv->gpio_chip.owner = THIS_MODULE;
priv->gpio_chip.direction_output = cxd2820r_gpio_direction_output;
- priv->gpio_chip.set = cxd2820r_gpio_set;
+ priv->gpio_chip.set_rv = cxd2820r_gpio_set;
priv->gpio_chip.get = cxd2820r_gpio_get;
priv->gpio_chip.base = -1; /* Dynamic allocation */
priv->gpio_chip.ngpio = GPIO_COUNT;
diff --git a/drivers/media/dvb-frontends/dib7000p.c b/drivers/media/dvb-frontends/dib7000p.c
index b40daf242046..7d3a994b7cc4 100644
--- a/drivers/media/dvb-frontends/dib7000p.c
+++ b/drivers/media/dvb-frontends/dib7000p.c
@@ -2193,6 +2193,8 @@ static int w7090p_tuner_write_serpar(struct i2c_adapter *i2c_adap, struct i2c_ms
struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
u8 n_overflow = 1;
u16 i = 1000;
+ if (msg[0].len < 3)
+ return -EOPNOTSUPP;
u16 serpar_num = msg[0].buf[0];
while (n_overflow == 1 && i) {
@@ -2212,6 +2214,8 @@ static int w7090p_tuner_read_serpar(struct i2c_adapter *i2c_adap, struct i2c_msg
struct dib7000p_state *state = i2c_get_adapdata(i2c_adap);
u8 n_overflow = 1, n_empty = 1;
u16 i = 1000;
+ if (msg[0].len < 1 || msg[1].len < 2)
+ return -EOPNOTSUPP;
u16 serpar_num = msg[0].buf[0];
u16 read_word;
@@ -2256,8 +2260,12 @@ static int dib7090p_rw_on_apb(struct i2c_adapter *i2c_adap,
u16 word;
if (num == 1) { /* write */
+ if (msg[0].len < 3)
+ return -EOPNOTSUPP;
dib7000p_write_word(state, apb_address, ((msg[0].buf[1] << 8) | (msg[0].buf[2])));
} else {
+ if (msg[1].len < 2)
+ return -EOPNOTSUPP;
word = dib7000p_read_word(state, apb_address);
msg[1].buf[0] = (word >> 8) & 0xff;
msg[1].buf[1] = (word) & 0xff;
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index e68202954a8f..6237fe804a5c 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -141,6 +141,7 @@ config VIDEO_IMX214
depends on GPIOLIB
select REGMAP_I2C
select V4L2_CCI_I2C
+ select VIDEO_CCS_PLL
help
This is a Video4Linux2 sensor driver for the Sony
IMX214 camera.
@@ -765,24 +766,25 @@ config VIDEO_THP7312
endmenu
-menu "Lens drivers"
- visible if MEDIA_CAMERA_SUPPORT
+menuconfig VIDEO_CAMERA_LENS
+ bool "Lens drivers"
+ depends on MEDIA_CAMERA_SUPPORT && I2C
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ default y
+
+if VIDEO_CAMERA_LENS
config VIDEO_AD5820
tristate "AD5820 lens voice coil support"
- depends on GPIOLIB && I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select V4L2_ASYNC
+ depends on GPIOLIB
help
This is a driver for the AD5820 camera lens voice coil.
It is used for example in Nokia N900 (RX-51).
config VIDEO_AK7375
tristate "AK7375 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
help
This is a driver for the AK7375 camera lens voice coil.
AK7375 is a 12 bit DAC with 120mA output current sink
@@ -791,10 +793,7 @@ config VIDEO_AK7375
config VIDEO_DW9714
tristate "DW9714 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
+ depends on GPIOLIB
help
This is a driver for the DW9714 camera lens voice coil.
DW9714 is a 10 bit DAC with 120mA output current sink
@@ -803,10 +802,6 @@ config VIDEO_DW9714
config VIDEO_DW9719
tristate "DW9719 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
select V4L2_CCI_I2C
help
This is a driver for the DW9719 camera lens voice coil.
@@ -815,10 +810,6 @@ config VIDEO_DW9719
config VIDEO_DW9768
tristate "DW9768 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_FWNODE
help
This is a driver for the DW9768 camera lens voice coil.
DW9768 is a 10 bit DAC with 100mA output current sink
@@ -827,17 +818,13 @@ config VIDEO_DW9768
config VIDEO_DW9807_VCM
tristate "DW9807 lens voice coil support"
- depends on I2C && VIDEO_DEV
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select V4L2_ASYNC
help
This is a driver for the DW9807 camera lens voice coil.
DW9807 is a 10 bit DAC with 100mA output current sink
capability. This is designed for linear control of
voice coil motors, controlled via I2C serial interface.
-endmenu
+endif
menu "Flash devices"
visible if MEDIA_CAMERA_SUPPORT
@@ -1684,7 +1671,7 @@ config VIDEO_MAX96714
config VIDEO_MAX96717
tristate "Maxim MAX96717 GMSL2 Serializer support"
- depends on OF && I2C && VIDEO_DEV && COMMON_CLK
+ depends on I2C && VIDEO_DEV && COMMON_CLK
select I2C_MUX
select MEDIA_CONTROLLER
select GPIOLIB
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 6e50b14f888f..5d90b8ab9b6d 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -868,21 +868,6 @@ static int adv7180_get_skip_frames(struct v4l2_subdev *sd, u32 *frames)
return 0;
}
-static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect)
-{
- struct adv7180_state *state = to_state(sd);
-
- if (state->curr_norm & V4L2_STD_525_60) {
- aspect->numerator = 11;
- aspect->denominator = 10;
- } else {
- aspect->numerator = 54;
- aspect->denominator = 59;
- }
-
- return 0;
-}
-
static int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm)
{
*norm = V4L2_STD_ALL;
@@ -929,7 +914,6 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = {
.querystd = adv7180_querystd,
.g_input_status = adv7180_g_input_status,
.s_routing = adv7180_s_routing,
- .g_pixelaspect = adv7180_g_pixelaspect,
.g_tvnorms = adv7180_g_tvnorms,
.s_stream = adv7180_s_stream,
};
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index 5edb3295dc58..678199196b84 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -161,22 +161,6 @@ int adv748x_afe_s_input(struct adv748x_afe *afe, unsigned int input)
return sdp_write(state, ADV748X_SDP_INSEL, input);
}
-static int adv748x_afe_g_pixelaspect(struct v4l2_subdev *sd,
- struct v4l2_fract *aspect)
-{
- struct adv748x_afe *afe = adv748x_sd_to_afe(sd);
-
- if (afe->curr_norm & V4L2_STD_525_60) {
- aspect->numerator = 11;
- aspect->denominator = 10;
- } else {
- aspect->numerator = 54;
- aspect->denominator = 59;
- }
-
- return 0;
-}
-
/* -----------------------------------------------------------------------------
* v4l2_subdev_video_ops
*/
@@ -307,7 +291,6 @@ static const struct v4l2_subdev_video_ops adv748x_afe_video_ops = {
.g_tvnorms = adv748x_afe_g_tvnorms,
.g_input_status = adv748x_afe_g_input_status,
.s_stream = adv748x_afe_s_stream,
- .g_pixelaspect = adv748x_afe_g_pixelaspect,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index a4db9bae5f79..b154dea29ba2 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -382,19 +382,9 @@ done:
return ret;
}
-static int adv748x_hdmi_g_pixelaspect(struct v4l2_subdev *sd,
- struct v4l2_fract *aspect)
-{
- aspect->numerator = 1;
- aspect->denominator = 1;
-
- return 0;
-}
-
static const struct v4l2_subdev_video_ops adv748x_video_ops_hdmi = {
.g_input_status = adv748x_hdmi_g_input_status,
.s_stream = adv748x_hdmi_s_stream,
- .g_pixelaspect = adv748x_hdmi_g_pixelaspect,
};
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index e271782b7b70..afed38596362 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -2448,8 +2448,8 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
}
cec_s_phys_addr(state->cec_adap, parent_pa, false);
- /* enable hotplug after 100 ms */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
+ /* enable hotplug after 143 ms */
+ schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7);
return 0;
}
diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c
index 6d3f8617ef13..bc74499b0a96 100644
--- a/drivers/media/i2c/ds90ub913.c
+++ b/drivers/media/i2c/ds90ub913.c
@@ -203,9 +203,9 @@ static int ub913_gpio_direction_out(struct gpio_chip *gc, unsigned int offset,
0));
}
-static void ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+static int ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
- ub913_gpio_direction_out(gc, offset, value);
+ return ub913_gpio_direction_out(gc, offset, value);
}
static int ub913_gpio_of_xlate(struct gpio_chip *gc,
@@ -235,7 +235,7 @@ static int ub913_gpiochip_probe(struct ub913_data *priv)
gc->ngpio = UB913_NUM_GPIOS;
gc->get_direction = ub913_gpio_get_direction;
gc->direction_output = ub913_gpio_direction_out;
- gc->set = ub913_gpio_set;
+ gc->set_rv = ub913_gpio_set;
gc->of_xlate = ub913_gpio_of_xlate;
gc->of_gpio_n_cells = 2;
@@ -337,14 +337,6 @@ static int _ub913_set_routing(struct v4l2_subdev *sd,
unsigned int i;
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
-
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -EINVAL;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
if (ret)
diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index 59bd92388845..a865bfc89500 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -317,14 +317,13 @@ static int ub953_gpio_get(struct gpio_chip *gc, unsigned int offset)
return !!(v & UB953_REG_GPIO_PIN_STS_GPIO_STS(offset));
}
-static void ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+static int ub953_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
struct ub953_data *priv = gpiochip_get_data(gc);
- regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA,
- UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset),
- value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) :
- 0);
+ return regmap_update_bits(priv->regmap, UB953_REG_LOCAL_GPIO_DATA,
+ UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset),
+ value ? UB953_REG_LOCAL_GPIO_DATA_GPIO_OUT_SRC(offset) : 0);
}
static int ub953_gpio_of_xlate(struct gpio_chip *gc,
@@ -362,7 +361,7 @@ static int ub953_gpiochip_probe(struct ub953_data *priv)
gc->direction_input = ub953_gpio_direction_in;
gc->direction_output = ub953_gpio_direction_out;
gc->get = ub953_gpio_get;
- gc->set = ub953_gpio_set;
+ gc->set_rv = ub953_gpio_set;
gc->of_xlate = ub953_gpio_of_xlate;
gc->of_gpio_n_cells = 2;
@@ -400,14 +399,6 @@ static int _ub953_set_routing(struct v4l2_subdev *sd,
};
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
-
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -EINVAL;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
if (ret)
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index 082fc62b0f5b..3156f6d6c6de 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -3861,14 +3861,6 @@ static int _ub960_set_routing(struct v4l2_subdev *sd,
};
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
-
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -E2BIG;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
V4L2_SUBDEV_ROUTING_NO_SINK_STREAM_MIX);
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 2ddd7daa79e2..1e7ad355a388 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -2,6 +2,7 @@
// Copyright (c) 2015--2017 Intel Corporation.
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@@ -38,6 +39,7 @@ struct dw9714_device {
struct v4l2_subdev sd;
u16 current_val;
struct regulator *vcc;
+ struct gpio_desc *powerdown_gpio;
};
static inline struct dw9714_device *to_dw9714_vcm(struct v4l2_ctrl *ctrl)
@@ -137,6 +139,28 @@ static int dw9714_init_controls(struct dw9714_device *dev_vcm)
return hdl->error;
}
+static int dw9714_power_up(struct dw9714_device *dw9714_dev)
+{
+ int ret;
+
+ ret = regulator_enable(dw9714_dev->vcc);
+ if (ret)
+ return ret;
+
+ gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 0);
+
+ usleep_range(1000, 2000);
+
+ return 0;
+}
+
+static int dw9714_power_down(struct dw9714_device *dw9714_dev)
+{
+ gpiod_set_value_cansleep(dw9714_dev->powerdown_gpio, 1);
+
+ return regulator_disable(dw9714_dev->vcc);
+}
+
static int dw9714_probe(struct i2c_client *client)
{
struct dw9714_device *dw9714_dev;
@@ -144,20 +168,25 @@ static int dw9714_probe(struct i2c_client *client)
dw9714_dev = devm_kzalloc(&client->dev, sizeof(*dw9714_dev),
GFP_KERNEL);
- if (dw9714_dev == NULL)
+ if (!dw9714_dev)
return -ENOMEM;
dw9714_dev->vcc = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(dw9714_dev->vcc))
return PTR_ERR(dw9714_dev->vcc);
- rval = regulator_enable(dw9714_dev->vcc);
- if (rval < 0) {
- dev_err(&client->dev, "failed to enable vcc: %d\n", rval);
- return rval;
- }
+ dw9714_dev->powerdown_gpio = devm_gpiod_get_optional(&client->dev,
+ "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(dw9714_dev->powerdown_gpio))
+ return dev_err_probe(&client->dev,
+ PTR_ERR(dw9714_dev->powerdown_gpio),
+ "could not get powerdown gpio\n");
- usleep_range(1000, 2000);
+ rval = dw9714_power_up(dw9714_dev);
+ if (rval)
+ return dev_err_probe(&client->dev, rval,
+ "failed to power up: %d\n", rval);
v4l2_i2c_subdev_init(&dw9714_dev->sd, client, &dw9714_ops);
dw9714_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
@@ -185,7 +214,7 @@ static int dw9714_probe(struct i2c_client *client)
return 0;
err_cleanup:
- regulator_disable(dw9714_dev->vcc);
+ dw9714_power_down(dw9714_dev);
v4l2_ctrl_handler_free(&dw9714_dev->ctrls_vcm);
media_entity_cleanup(&dw9714_dev->sd.entity);
@@ -200,10 +229,10 @@ static void dw9714_remove(struct i2c_client *client)
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev)) {
- ret = regulator_disable(dw9714_dev->vcc);
+ ret = dw9714_power_down(dw9714_dev);
if (ret) {
dev_err(&client->dev,
- "Failed to disable vcc: %d\n", ret);
+ "Failed to power down: %d\n", ret);
}
}
pm_runtime_set_suspended(&client->dev);
@@ -234,9 +263,9 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev)
usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10);
}
- ret = regulator_disable(dw9714_dev->vcc);
+ ret = dw9714_power_down(dw9714_dev);
if (ret)
- dev_err(dev, "Failed to disable vcc: %d\n", ret);
+ dev_err(dev, "Failed to power down: %d\n", ret);
return ret;
}
@@ -247,7 +276,7 @@ static int __maybe_unused dw9714_vcm_suspend(struct device *dev)
* The lens position is gradually moved in units of DW9714_CTRL_STEPS,
* to make the movements smoothly.
*/
-static int __maybe_unused dw9714_vcm_resume(struct device *dev)
+static int __maybe_unused dw9714_vcm_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct v4l2_subdev *sd = i2c_get_clientdata(client);
@@ -257,12 +286,11 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev)
if (pm_runtime_suspended(&client->dev))
return 0;
- ret = regulator_enable(dw9714_dev->vcc);
+ ret = dw9714_power_up(dw9714_dev);
if (ret) {
- dev_err(dev, "Failed to enable vcc: %d\n", ret);
+ dev_err(dev, "Failed to power up: %d\n", ret);
return ret;
}
- usleep_range(1000, 2000);
for (val = dw9714_dev->current_val % DW9714_CTRL_STEPS;
val < dw9714_dev->current_val + DW9714_CTRL_STEPS - 1;
@@ -271,7 +299,7 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev)
DW9714_VAL(val, DW9714_DEFAULT_S));
if (ret)
dev_err_ratelimited(dev, "%s I2C failure: %d",
- __func__, ret);
+ __func__, ret);
usleep_range(DW9714_CTRL_DELAY_US, DW9714_CTRL_DELAY_US + 10);
}
diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
index aed258211b8a..076c19fcf99c 100644
--- a/drivers/media/i2c/hi556.c
+++ b/drivers/media/i2c/hi556.c
@@ -624,6 +624,12 @@ static const struct hi556_mode supported_modes[] = {
},
};
+static const char * const hi556_supply_names[] = {
+ "dovdd", /* Digital I/O power */
+ "avdd", /* Analog power */
+ "dvdd", /* Digital core power */
+};
+
struct hi556 {
struct v4l2_subdev sd;
struct media_pad pad;
@@ -639,7 +645,7 @@ struct hi556 {
/* GPIOs, clocks, etc. */
struct gpio_desc *reset_gpio;
struct clk *clk;
- struct regulator *avdd;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(hi556_supply_names)];
/* Current mode */
const struct hi556_mode *cur_mode;
@@ -756,21 +762,23 @@ static int hi556_test_pattern(struct hi556 *hi556, u32 pattern)
int ret;
u32 val;
- if (pattern) {
- ret = hi556_read_reg(hi556, HI556_REG_ISP,
- HI556_REG_VALUE_08BIT, &val);
- if (ret)
- return ret;
+ ret = hi556_read_reg(hi556, HI556_REG_ISP,
+ HI556_REG_VALUE_08BIT, &val);
+ if (ret)
+ return ret;
- ret = hi556_write_reg(hi556, HI556_REG_ISP,
- HI556_REG_VALUE_08BIT,
- val | HI556_REG_ISP_TPG_EN);
- if (ret)
- return ret;
- }
+ val = pattern ? (val | HI556_REG_ISP_TPG_EN) :
+ (val & ~HI556_REG_ISP_TPG_EN);
+
+ ret = hi556_write_reg(hi556, HI556_REG_ISP,
+ HI556_REG_VALUE_08BIT, val);
+ if (ret)
+ return ret;
+
+ val = pattern ? BIT(pattern - 1) : 0;
return hi556_write_reg(hi556, HI556_REG_TEST_PATTERN,
- HI556_REG_VALUE_08BIT, pattern);
+ HI556_REG_VALUE_08BIT, val);
}
static int hi556_set_ctrl(struct v4l2_ctrl *ctrl)
@@ -1289,17 +1297,10 @@ static int hi556_suspend(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct hi556 *hi556 = to_hi556(sd);
- int ret;
gpiod_set_value_cansleep(hi556->reset_gpio, 1);
-
- ret = regulator_disable(hi556->avdd);
- if (ret) {
- dev_err(dev, "failed to disable avdd: %d\n", ret);
- gpiod_set_value_cansleep(hi556->reset_gpio, 0);
- return ret;
- }
-
+ regulator_bulk_disable(ARRAY_SIZE(hi556_supply_names),
+ hi556->supplies);
clk_disable_unprepare(hi556->clk);
return 0;
}
@@ -1314,14 +1315,20 @@ static int hi556_resume(struct device *dev)
if (ret)
return ret;
- ret = regulator_enable(hi556->avdd);
+ ret = regulator_bulk_enable(ARRAY_SIZE(hi556_supply_names),
+ hi556->supplies);
if (ret) {
- dev_err(dev, "failed to enable avdd: %d\n", ret);
+ dev_err(dev, "failed to enable regulators: %d", ret);
clk_disable_unprepare(hi556->clk);
return ret;
}
- gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ if (hi556->reset_gpio) {
+ /* Assert reset for at least 2ms on back to back off-on */
+ usleep_range(2000, 2200);
+ gpiod_set_value_cansleep(hi556->reset_gpio, 0);
+ }
+
usleep_range(5000, 5500);
return 0;
}
@@ -1330,7 +1337,7 @@ static int hi556_probe(struct i2c_client *client)
{
struct hi556 *hi556;
bool full_power;
- int ret;
+ int i, ret;
ret = hi556_check_hwcfg(&client->dev);
if (ret)
@@ -1353,11 +1360,15 @@ static int hi556_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, PTR_ERR(hi556->clk),
"failed to get clock\n");
- /* The regulator core will provide a "dummy" regulator if necessary */
- hi556->avdd = devm_regulator_get(&client->dev, "avdd");
- if (IS_ERR(hi556->avdd))
- return dev_err_probe(&client->dev, PTR_ERR(hi556->avdd),
- "failed to get avdd regulator\n");
+ for (i = 0; i < ARRAY_SIZE(hi556_supply_names); i++)
+ hi556->supplies[i].supply = hi556_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&client->dev,
+ ARRAY_SIZE(hi556_supply_names),
+ hi556->supplies);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to get regulators\n");
full_power = acpi_dev_state_d0(&client->dev);
if (full_power) {
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index dd7bc45523d8..a0cef9e61b41 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -20,6 +20,8 @@
#include <media/v4l2-fwnode.h>
#include <media/v4l2-subdev.h>
+#include "ccs-pll.h"
+
/* Chip ID */
#define IMX214_REG_CHIP_ID CCI_REG16(0x0016)
#define IMX214_CHIP_ID 0x0214
@@ -30,11 +32,9 @@
#define IMX214_REG_FAST_STANDBY_CTRL CCI_REG8(0x0106)
-#define IMX214_DEFAULT_CLK_FREQ 24000000
#define IMX214_DEFAULT_LINK_FREQ 600000000
/* Keep wrong link frequency for backward compatibility */
#define IMX214_DEFAULT_LINK_FREQ_LEGACY 480000000
-#define IMX214_DEFAULT_PIXEL_RATE ((IMX214_DEFAULT_LINK_FREQ * 8LL) / 10)
#define IMX214_FPS 30
/* V-TIMING internal */
@@ -84,6 +84,7 @@
#define IMX214_CSI_DATA_FORMAT_RAW10 0x0A0A
#define IMX214_CSI_DATA_FORMAT_COMP6 0x0A06
#define IMX214_CSI_DATA_FORMAT_COMP8 0x0A08
+#define IMX214_BITS_PER_PIXEL_MASK 0xFF
#define IMX214_REG_CSI_LANE_MODE CCI_REG8(0x0114)
#define IMX214_CSI_2_LANE_MODE 1
@@ -249,6 +250,10 @@ struct imx214 {
struct clk *xclk;
struct regmap *regmap;
+ struct ccs_pll pll;
+
+ struct v4l2_fwnode_endpoint bus_cfg;
+
struct v4l2_subdev sd;
struct media_pad pad;
@@ -299,16 +304,6 @@ static const struct cci_reg_sequence mode_4096x2304[] = {
{ IMX214_REG_DIG_CROP_WIDTH, 4096 },
{ IMX214_REG_DIG_CROP_HEIGHT, 2304 },
- { IMX214_REG_VTPXCK_DIV, 5 },
- { IMX214_REG_VTSYCK_DIV, 2 },
- { IMX214_REG_PREPLLCK_VT_DIV, 3 },
- { IMX214_REG_PLL_VT_MPY, 150 },
- { IMX214_REG_OPPXCK_DIV, 10 },
- { IMX214_REG_OPSYCK_DIV, 1 },
- { IMX214_REG_PLL_MULT_DRIV, IMX214_PLL_SINGLE },
-
- { IMX214_REG_REQ_LINK_BIT_RATE, IMX214_LINK_BIT_RATE_MBPS(4800) },
-
{ CCI_REG8(0x3A03), 0x09 },
{ CCI_REG8(0x3A04), 0x50 },
{ CCI_REG8(0x3A05), 0x01 },
@@ -362,16 +357,6 @@ static const struct cci_reg_sequence mode_1920x1080[] = {
{ IMX214_REG_DIG_CROP_WIDTH, 1920 },
{ IMX214_REG_DIG_CROP_HEIGHT, 1080 },
- { IMX214_REG_VTPXCK_DIV, 5 },
- { IMX214_REG_VTSYCK_DIV, 2 },
- { IMX214_REG_PREPLLCK_VT_DIV, 3 },
- { IMX214_REG_PLL_VT_MPY, 150 },
- { IMX214_REG_OPPXCK_DIV, 10 },
- { IMX214_REG_OPSYCK_DIV, 1 },
- { IMX214_REG_PLL_MULT_DRIV, IMX214_PLL_SINGLE },
-
- { IMX214_REG_REQ_LINK_BIT_RATE, IMX214_LINK_BIT_RATE_MBPS(4800) },
-
{ CCI_REG8(0x3A03), 0x04 },
{ CCI_REG8(0x3A04), 0xF8 },
{ CCI_REG8(0x3A05), 0x02 },
@@ -405,9 +390,6 @@ static const struct cci_reg_sequence mode_table_common[] = {
/* ATR setting */
{ IMX214_REG_ATR_FAST_MOVE, 2 },
- /* external clock setting */
- { IMX214_REG_EXCK_FREQ, IMX214_EXCK_FREQ(IMX214_DEFAULT_CLK_FREQ / 1000000) },
-
/* global setting */
/* basic config */
{ IMX214_REG_MASK_CORR_FRAMES, IMX214_CORR_FRAMES_MASK },
@@ -777,6 +759,30 @@ static int imx214_entity_init_state(struct v4l2_subdev *subdev,
return 0;
}
+static int imx214_configure_pll(struct imx214 *imx214)
+{
+ int ret = 0;
+
+ cci_write(imx214->regmap, IMX214_REG_VTPXCK_DIV,
+ imx214->pll.vt_bk.pix_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_VTSYCK_DIV,
+ imx214->pll.vt_bk.sys_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_PREPLLCK_VT_DIV,
+ imx214->pll.vt_fr.pre_pll_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_PLL_VT_MPY,
+ imx214->pll.vt_fr.pll_multiplier, &ret);
+ cci_write(imx214->regmap, IMX214_REG_OPPXCK_DIV,
+ imx214->pll.op_bk.pix_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_OPSYCK_DIV,
+ imx214->pll.op_bk.sys_clk_div, &ret);
+ cci_write(imx214->regmap, IMX214_REG_PLL_MULT_DRIV,
+ IMX214_PLL_SINGLE, &ret);
+ cci_write(imx214->regmap, IMX214_REG_EXCK_FREQ,
+ IMX214_EXCK_FREQ(imx214->pll.ext_clk_freq_hz / 1000000), &ret);
+
+ return ret;
+}
+
static int imx214_update_digital_gain(struct imx214 *imx214, u32 val)
{
int ret = 0;
@@ -877,9 +883,6 @@ static const struct v4l2_ctrl_ops imx214_ctrl_ops = {
static int imx214_ctrls_init(struct imx214 *imx214)
{
- static const s64 link_freq[] = {
- IMX214_DEFAULT_LINK_FREQ
- };
static const struct v4l2_area unit_size = {
.width = 1120,
.height = 1120,
@@ -900,15 +903,14 @@ static int imx214_ctrls_init(struct imx214 *imx214)
if (ret)
return ret;
- imx214->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, NULL,
- V4L2_CID_PIXEL_RATE, 0,
- IMX214_DEFAULT_PIXEL_RATE, 1,
- IMX214_DEFAULT_PIXEL_RATE);
+ imx214->pixel_rate =
+ v4l2_ctrl_new_std(ctrl_hdlr, NULL, V4L2_CID_PIXEL_RATE, 1,
+ INT_MAX, 1, 1);
imx214->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, NULL,
V4L2_CID_LINK_FREQ,
- ARRAY_SIZE(link_freq) - 1,
- 0, link_freq);
+ imx214->bus_cfg.nr_of_link_frequencies - 1,
+ 0, imx214->bus_cfg.link_frequencies);
if (imx214->link_freq)
imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
@@ -1011,6 +1013,7 @@ static int imx214_start_streaming(struct imx214 *imx214)
const struct v4l2_mbus_framefmt *fmt;
struct v4l2_subdev_state *state;
const struct imx214_mode *mode;
+ int bit_rate_mbps;
int ret;
ret = cci_multi_reg_write(imx214->regmap, mode_table_common,
@@ -1020,6 +1023,21 @@ static int imx214_start_streaming(struct imx214 *imx214)
return ret;
}
+ ret = imx214_configure_pll(imx214);
+ if (ret) {
+ dev_err(imx214->dev, "failed to configure PLL: %d\n", ret);
+ return ret;
+ }
+
+ bit_rate_mbps = (imx214->pll.pixel_rate_csi / 1000000)
+ * imx214->pll.bits_per_pixel;
+ ret = cci_write(imx214->regmap, IMX214_REG_REQ_LINK_BIT_RATE,
+ IMX214_LINK_BIT_RATE_MBPS(bit_rate_mbps), NULL);
+ if (ret) {
+ dev_err(imx214->dev, "failed to configure link bit rate\n");
+ return ret;
+ }
+
ret = cci_write(imx214->regmap, IMX214_REG_CSI_LANE_MODE,
IMX214_CSI_4_LANE_MODE, NULL);
if (ret) {
@@ -1097,6 +1115,109 @@ err_rpm_put:
return ret;
}
+static int imx214_pll_calculate(struct imx214 *imx214, struct ccs_pll *pll,
+ unsigned int link_freq)
+{
+ struct ccs_pll_limits limits = {
+ .min_ext_clk_freq_hz = 6000000,
+ .max_ext_clk_freq_hz = 27000000,
+
+ .vt_fr = {
+ .min_pre_pll_clk_div = 1,
+ .max_pre_pll_clk_div = 15,
+ /* Value is educated guess as we don't have a spec */
+ .min_pll_ip_clk_freq_hz = 6000000,
+ /* Value is educated guess as we don't have a spec */
+ .max_pll_ip_clk_freq_hz = 12000000,
+ .min_pll_multiplier = 12,
+ .max_pll_multiplier = 1200,
+ .min_pll_op_clk_freq_hz = 338000000,
+ .max_pll_op_clk_freq_hz = 1200000000,
+ },
+ .vt_bk = {
+ .min_sys_clk_div = 2,
+ .max_sys_clk_div = 4,
+ .min_pix_clk_div = 5,
+ .max_pix_clk_div = 10,
+ .min_pix_clk_freq_hz = 30000000,
+ .max_pix_clk_freq_hz = 120000000,
+ },
+ .op_bk = {
+ .min_sys_clk_div = 1,
+ .max_sys_clk_div = 2,
+ .min_pix_clk_div = 6,
+ .max_pix_clk_div = 10,
+ .min_pix_clk_freq_hz = 30000000,
+ .max_pix_clk_freq_hz = 120000000,
+ },
+
+ .min_line_length_pck_bin = IMX214_PPL_DEFAULT,
+ .min_line_length_pck = IMX214_PPL_DEFAULT,
+ };
+ unsigned int num_lanes = imx214->bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+ /*
+ * There are no documented constraints on the sys clock frequency, for
+ * either branch. Recover them based on the PLL output clock frequency
+ * and sys_clk_div limits on one hand, and the pix clock frequency and
+ * the pix_clk_div limits on the other hand.
+ */
+ limits.vt_bk.min_sys_clk_freq_hz =
+ max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.vt_bk.max_sys_clk_div,
+ limits.vt_bk.min_pix_clk_freq_hz * limits.vt_bk.min_pix_clk_div);
+ limits.vt_bk.max_sys_clk_freq_hz =
+ min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.vt_bk.min_sys_clk_div,
+ limits.vt_bk.max_pix_clk_freq_hz * limits.vt_bk.max_pix_clk_div);
+
+ limits.op_bk.min_sys_clk_freq_hz =
+ max(limits.vt_fr.min_pll_op_clk_freq_hz / limits.op_bk.max_sys_clk_div,
+ limits.op_bk.min_pix_clk_freq_hz * limits.op_bk.min_pix_clk_div);
+ limits.op_bk.max_sys_clk_freq_hz =
+ min(limits.vt_fr.max_pll_op_clk_freq_hz / limits.op_bk.min_sys_clk_div,
+ limits.op_bk.max_pix_clk_freq_hz * limits.op_bk.max_pix_clk_div);
+
+ memset(pll, 0, sizeof(*pll));
+
+ pll->bus_type = CCS_PLL_BUS_TYPE_CSI2_DPHY;
+ pll->op_lanes = num_lanes;
+ pll->vt_lanes = num_lanes;
+ pll->csi2.lanes = num_lanes;
+
+ pll->binning_horizontal = 1;
+ pll->binning_vertical = 1;
+ pll->scale_m = 1;
+ pll->scale_n = 1;
+ pll->bits_per_pixel =
+ IMX214_CSI_DATA_FORMAT_RAW10 & IMX214_BITS_PER_PIXEL_MASK;
+ pll->flags = CCS_PLL_FLAG_LANE_SPEED_MODEL;
+ pll->link_freq = link_freq;
+ pll->ext_clk_freq_hz = clk_get_rate(imx214->xclk);
+
+ return ccs_pll_calculate(imx214->dev, &limits, pll);
+}
+
+static int imx214_pll_update(struct imx214 *imx214)
+{
+ u64 link_freq;
+ int ret;
+
+ link_freq = imx214->bus_cfg.link_frequencies[imx214->link_freq->val];
+ ret = imx214_pll_calculate(imx214, &imx214->pll, link_freq);
+ if (ret) {
+ dev_err(imx214->dev, "PLL calculations failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_ctrl_s_ctrl_int64(imx214->pixel_rate,
+ imx214->pll.pixel_rate_pixel_array);
+ if (ret) {
+ dev_err(imx214->dev, "failed to set pixel rate\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int imx214_get_frame_interval(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_interval *fival)
@@ -1203,12 +1324,10 @@ static int imx214_identify_module(struct imx214 *imx214)
return 0;
}
-static int imx214_parse_fwnode(struct device *dev)
+static int imx214_parse_fwnode(struct device *dev, struct imx214 *imx214)
{
+ struct v4l2_fwnode_endpoint *bus_cfg = &imx214->bus_cfg;
struct fwnode_handle *endpoint;
- struct v4l2_fwnode_endpoint bus_cfg = {
- .bus_type = V4L2_MBUS_CSI2_DPHY,
- };
unsigned int i;
int ret;
@@ -1216,42 +1335,52 @@ static int imx214_parse_fwnode(struct device *dev)
if (!endpoint)
return dev_err_probe(dev, -EINVAL, "endpoint node not found\n");
- ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ bus_cfg->bus_type = V4L2_MBUS_CSI2_DPHY;
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, bus_cfg);
+ fwnode_handle_put(endpoint);
if (ret) {
dev_err_probe(dev, ret, "parsing endpoint node failed\n");
- goto done;
+ goto error;
}
/* Check the number of MIPI CSI2 data lanes */
- if (bus_cfg.bus.mipi_csi2.num_data_lanes != 4) {
+ if (bus_cfg->bus.mipi_csi2.num_data_lanes != 4) {
ret = dev_err_probe(dev, -EINVAL,
"only 4 data lanes are currently supported\n");
- goto done;
+ goto error;
}
- if (bus_cfg.nr_of_link_frequencies != 1)
+ if (bus_cfg->nr_of_link_frequencies != 1)
dev_warn(dev, "Only one link-frequency supported, please review your DT. Continuing anyway\n");
- for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) {
- if (bus_cfg.link_frequencies[i] == IMX214_DEFAULT_LINK_FREQ)
+ for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) {
+ u64 freq = bus_cfg->link_frequencies[i];
+ struct ccs_pll pll;
+
+ if (!imx214_pll_calculate(imx214, &pll, freq))
break;
- if (bus_cfg.link_frequencies[i] ==
- IMX214_DEFAULT_LINK_FREQ_LEGACY) {
+ if (freq == IMX214_DEFAULT_LINK_FREQ_LEGACY) {
dev_warn(dev,
"link-frequencies %d not supported, please review your DT. Continuing anyway\n",
IMX214_DEFAULT_LINK_FREQ);
+ freq = IMX214_DEFAULT_LINK_FREQ;
+ if (imx214_pll_calculate(imx214, &pll, freq))
+ continue;
+ bus_cfg->link_frequencies[i] = freq;
break;
}
}
- if (i == bus_cfg.nr_of_link_frequencies)
+ if (i == bus_cfg->nr_of_link_frequencies)
ret = dev_err_probe(dev, -EINVAL,
- "link-frequencies %d not supported, please review your DT\n",
- IMX214_DEFAULT_LINK_FREQ);
+ "link-frequencies %lld not supported, please review your DT\n",
+ bus_cfg->nr_of_link_frequencies ?
+ bus_cfg->link_frequencies[0] : 0);
-done:
- v4l2_fwnode_endpoint_free(&bus_cfg);
- fwnode_handle_put(endpoint);
+ return 0;
+
+error:
+ v4l2_fwnode_endpoint_free(&imx214->bus_cfg);
return ret;
}
@@ -1261,10 +1390,6 @@ static int imx214_probe(struct i2c_client *client)
struct imx214 *imx214;
int ret;
- ret = imx214_parse_fwnode(dev);
- if (ret)
- return ret;
-
imx214 = devm_kzalloc(dev, sizeof(*imx214), GFP_KERNEL);
if (!imx214)
return -ENOMEM;
@@ -1276,11 +1401,6 @@ static int imx214_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(imx214->xclk),
"failed to get xclk\n");
- ret = clk_set_rate(imx214->xclk, IMX214_DEFAULT_CLK_FREQ);
- if (ret)
- return dev_err_probe(dev, ret,
- "failed to set xclk frequency\n");
-
ret = imx214_get_regulators(dev, imx214);
if (ret < 0)
return dev_err_probe(dev, ret, "failed to get regulators\n");
@@ -1295,6 +1415,10 @@ static int imx214_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(imx214->regmap),
"failed to initialize CCI\n");
+ ret = imx214_parse_fwnode(dev, imx214);
+ if (ret)
+ return ret;
+
v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops);
imx214->sd.internal_ops = &imx214_internal_ops;
@@ -1302,7 +1426,9 @@ static int imx214_probe(struct i2c_client *client)
* Enable power initially, to avoid warnings
* from clk_disable on power_off
*/
- imx214_power_on(imx214->dev);
+ ret = imx214_power_on(imx214->dev);
+ if (ret < 0)
+ goto error_fwnode;
ret = imx214_identify_module(imx214);
if (ret)
@@ -1333,6 +1459,12 @@ static int imx214_probe(struct i2c_client *client)
pm_runtime_set_active(imx214->dev);
pm_runtime_enable(imx214->dev);
+ ret = imx214_pll_update(imx214);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "failed to update PLL\n");
+ goto error_subdev_cleanup;
+ }
+
ret = v4l2_async_register_subdev_sensor(&imx214->sd);
if (ret < 0) {
dev_err_probe(dev, ret,
@@ -1358,6 +1490,9 @@ free_ctrl:
error_power_off:
imx214_power_off(imx214->dev);
+error_fwnode:
+ v4l2_fwnode_endpoint_free(&imx214->bus_cfg);
+
return ret;
}
@@ -1370,6 +1505,8 @@ static void imx214_remove(struct i2c_client *client)
v4l2_subdev_cleanup(sd);
media_entity_cleanup(&imx214->sd.entity);
v4l2_ctrl_handler_free(&imx214->ctrls);
+ v4l2_fwnode_endpoint_free(&imx214->bus_cfg);
+
pm_runtime_disable(&client->dev);
if (!pm_runtime_status_suspended(&client->dev)) {
imx214_power_off(imx214->dev);
diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index fbf7eba3d71d..4f3f386c5353 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1294,7 +1294,6 @@ static int imx290_subdev_init(struct imx290 *imx290)
* will already be prevented even before the delay.
*/
v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops);
- imx290->sd.dev = imx290->dev;
pm_runtime_mark_last_busy(imx290->dev);
pm_runtime_put_autosuspend(imx290->dev);
diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c
index 9f37779bd611..278e743646ea 100644
--- a/drivers/media/i2c/imx415.c
+++ b/drivers/media/i2c/imx415.c
@@ -1251,7 +1251,7 @@ static int imx415_parse_hw_config(struct imx415 *sensor)
return dev_err_probe(sensor->dev, PTR_ERR(sensor->reset),
"failed to get reset GPIO\n");
- sensor->clk = devm_clk_get(sensor->dev, "inck");
+ sensor->clk = devm_clk_get(sensor->dev, NULL);
if (IS_ERR(sensor->clk))
return dev_err_probe(sensor->dev, PTR_ERR(sensor->clk),
"failed to get clock\n");
diff --git a/drivers/media/i2c/lt6911uxe.c b/drivers/media/i2c/lt6911uxe.c
index 24857d683fcf..bdefdd157e69 100644
--- a/drivers/media/i2c/lt6911uxe.c
+++ b/drivers/media/i2c/lt6911uxe.c
@@ -600,7 +600,7 @@ static int lt6911uxe_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&lt6911uxe->sd, client, &lt6911uxe_subdev_ops);
- lt6911uxe->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN);
+ lt6911uxe->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(lt6911uxe->reset_gpio))
return dev_err_probe(dev, PTR_ERR(lt6911uxe->reset_gpio),
"failed to get reset gpio\n");
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 9fc4e130a273..1d0b5f56f989 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -1193,12 +1193,12 @@ static int max9286_gpio_set(struct max9286_priv *priv, unsigned int offset,
MAX9286_0X0F_RESERVED | priv->gpio_state);
}
-static void max9286_gpiochip_set(struct gpio_chip *chip,
- unsigned int offset, int value)
+static int max9286_gpiochip_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
{
struct max9286_priv *priv = gpiochip_get_data(chip);
- max9286_gpio_set(priv, offset, value);
+ return max9286_gpio_set(priv, offset, value);
}
static int max9286_gpiochip_get(struct gpio_chip *chip, unsigned int offset)
@@ -1220,7 +1220,7 @@ static int max9286_register_gpio(struct max9286_priv *priv)
gpio->owner = THIS_MODULE;
gpio->ngpio = 2;
gpio->base = -1;
- gpio->set = max9286_gpiochip_set;
+ gpio->set_rv = max9286_gpiochip_set;
gpio->get = max9286_gpiochip_get;
gpio->can_sleep = true;
diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c
index 3cc1b1ae47d1..e3e625e6f11a 100644
--- a/drivers/media/i2c/max96714.c
+++ b/drivers/media/i2c/max96714.c
@@ -370,13 +370,6 @@ static int _max96714_set_routing(struct v4l2_subdev *sd,
};
int ret;
- /*
- * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
- * frame desc is made dynamically allocated.
- */
- if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
- return -EINVAL;
-
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
if (ret)
diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
index 3746729366ac..015e42fbe246 100644
--- a/drivers/media/i2c/max96717.c
+++ b/drivers/media/i2c/max96717.c
@@ -297,13 +297,13 @@ static int max96717_gpiochip_get(struct gpio_chip *gpiochip,
return !!(val & MAX96717_GPIO_OUT);
}
-static void max96717_gpiochip_set(struct gpio_chip *gpiochip,
- unsigned int offset, int value)
+static int max96717_gpiochip_set(struct gpio_chip *gpiochip,
+ unsigned int offset, int value)
{
struct max96717_priv *priv = gpiochip_get_data(gpiochip);
- cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset),
- MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL);
+ return cci_update_bits(priv->regmap, MAX96717_GPIO_REG_A(offset),
+ MAX96717_GPIO_OUT, MAX96717_GPIO_OUT, NULL);
}
static int max96717_gpio_get_direction(struct gpio_chip *gpiochip,
@@ -355,9 +355,8 @@ static int max96717_gpiochip_probe(struct max96717_priv *priv)
gc->get_direction = max96717_gpio_get_direction;
gc->direction_input = max96717_gpio_direction_in;
gc->direction_output = max96717_gpio_direction_out;
- gc->set = max96717_gpiochip_set;
+ gc->set_rv = max96717_gpiochip_set;
gc->get = max96717_gpiochip_get;
- gc->of_gpio_n_cells = 2;
/* Disable GPIO forwarding */
for (i = 0; i < gc->ngpio; i++)
diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c
index 5f0b0ad8f885..3f540ca40f3c 100644
--- a/drivers/media/i2c/mt9m114.c
+++ b/drivers/media/i2c/mt9m114.c
@@ -261,6 +261,7 @@
#define MT9M114_CAM_PGA_PGA_CONTROL CCI_REG16(0xc95e)
#define MT9M114_CAM_SYSCTL_PLL_ENABLE CCI_REG8(0xc97e)
#define MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE BIT(0)
+#define MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE 0x00
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N CCI_REG16(0xc980)
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(m, n) (((n) << 8) | (m))
#define MT9M114_CAM_SYSCTL_PLL_DIVIDER_P CCI_REG16(0xc982)
@@ -377,6 +378,7 @@ struct mt9m114 {
struct gpio_desc *reset;
struct regulator_bulk_data supplies[3];
struct v4l2_fwnode_endpoint bus_cfg;
+ bool bypass_pll;
struct {
unsigned int m;
@@ -743,14 +745,21 @@ static int mt9m114_initialize(struct mt9m114 *sensor)
}
/* Configure the PLL. */
- cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE,
- MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret);
- cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N,
- MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m,
- sensor->pll.n),
- &ret);
- cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P,
- MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p), &ret);
+ if (sensor->bypass_pll) {
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE,
+ MT9M114_CAM_SYSCTL_PLL_DISABLE_VALUE, &ret);
+ } else {
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_ENABLE,
+ MT9M114_CAM_SYSCTL_PLL_ENABLE_VALUE, &ret);
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_M_N,
+ MT9M114_CAM_SYSCTL_PLL_DIVIDER_VALUE(sensor->pll.m,
+ sensor->pll.n),
+ &ret);
+ cci_write(sensor->regmap, MT9M114_CAM_SYSCTL_PLL_DIVIDER_P,
+ MT9M114_CAM_SYSCTL_PLL_DIVIDER_P_VALUE(sensor->pll.p),
+ &ret);
+ }
+
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_PIXCLK,
sensor->pixrate, &ret);
@@ -781,41 +790,25 @@ static int mt9m114_initialize(struct mt9m114 *sensor)
return 0;
}
-static int mt9m114_configure(struct mt9m114 *sensor,
- struct v4l2_subdev_state *pa_state,
- struct v4l2_subdev_state *ifp_state)
+static int mt9m114_configure_pa(struct mt9m114 *sensor,
+ struct v4l2_subdev_state *state)
{
- const struct v4l2_mbus_framefmt *pa_format;
- const struct v4l2_rect *pa_crop;
- const struct mt9m114_format_info *ifp_info;
- const struct v4l2_mbus_framefmt *ifp_format;
- const struct v4l2_rect *ifp_crop;
- const struct v4l2_rect *ifp_compose;
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
unsigned int hratio, vratio;
- u64 output_format;
u64 read_mode;
- int ret = 0;
-
- pa_format = v4l2_subdev_state_get_format(pa_state, 0);
- pa_crop = v4l2_subdev_state_get_crop(pa_state, 0);
+ int ret;
- ifp_format = v4l2_subdev_state_get_format(ifp_state, 1);
- ifp_info = mt9m114_format_info(sensor, 1, ifp_format->code);
- ifp_crop = v4l2_subdev_state_get_crop(ifp_state, 0);
- ifp_compose = v4l2_subdev_state_get_compose(ifp_state, 0);
+ format = v4l2_subdev_state_get_format(state, 0);
+ crop = v4l2_subdev_state_get_crop(state, 0);
ret = cci_read(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE,
&read_mode, NULL);
if (ret < 0)
return ret;
- ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT,
- &output_format, NULL);
- if (ret < 0)
- return ret;
-
- hratio = pa_crop->width / pa_format->width;
- vratio = pa_crop->height / pa_format->height;
+ hratio = crop->width / format->width;
+ vratio = crop->height / format->height;
/*
* Pixel array crop and binning. The CAM_SENSOR_CFG_CPIPE_LAST_ROW
@@ -824,15 +817,15 @@ static int mt9m114_configure(struct mt9m114 *sensor,
* example sensor modes.
*/
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_START,
- pa_crop->left, &ret);
+ crop->left, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_START,
- pa_crop->top, &ret);
+ crop->top, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_X_ADDR_END,
- pa_crop->width + pa_crop->left - 1, &ret);
+ crop->width + crop->left - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_Y_ADDR_END,
- pa_crop->height + pa_crop->top - 1, &ret);
+ crop->height + crop->top - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CFG_CPIPE_LAST_ROW,
- (pa_crop->height - 4) / vratio - 1, &ret);
+ (crop->height - 4) / vratio - 1, &ret);
read_mode &= ~(MT9M114_CAM_SENSOR_CONTROL_X_READ_OUT_MASK |
MT9M114_CAM_SENSOR_CONTROL_Y_READ_OUT_MASK);
@@ -845,6 +838,29 @@ static int mt9m114_configure(struct mt9m114 *sensor,
cci_write(sensor->regmap, MT9M114_CAM_SENSOR_CONTROL_READ_MODE,
read_mode, &ret);
+ return ret;
+}
+
+static int mt9m114_configure_ifp(struct mt9m114 *sensor,
+ struct v4l2_subdev_state *state)
+{
+ const struct mt9m114_format_info *info;
+ const struct v4l2_mbus_framefmt *format;
+ const struct v4l2_rect *crop;
+ const struct v4l2_rect *compose;
+ u64 output_format;
+ int ret = 0;
+
+ format = v4l2_subdev_state_get_format(state, 1);
+ info = mt9m114_format_info(sensor, 1, format->code);
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ compose = v4l2_subdev_state_get_compose(state, 0);
+
+ ret = cci_read(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT,
+ &output_format, NULL);
+ if (ret < 0)
+ return ret;
+
/*
* Color pipeline (IFP) cropping and scaling. Subtract 4 from the left
* and top coordinates to compensate for the lines and columns removed
@@ -852,18 +868,18 @@ static int mt9m114_configure(struct mt9m114 *sensor,
* not in the hardware.
*/
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_XOFFSET,
- ifp_crop->left - 4, &ret);
+ crop->left - 4, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_YOFFSET,
- ifp_crop->top - 4, &ret);
+ crop->top - 4, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_WIDTH,
- ifp_crop->width, &ret);
+ crop->width, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_WINDOW_HEIGHT,
- ifp_crop->height, &ret);
+ crop->height, &ret);
cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_WIDTH,
- ifp_compose->width, &ret);
+ compose->width, &ret);
cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_HEIGHT,
- ifp_compose->height, &ret);
+ compose->height, &ret);
/* AWB and AE windows, use the full frame. */
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XSTART,
@@ -871,18 +887,18 @@ static int mt9m114_configure(struct mt9m114 *sensor,
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YSTART,
0, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_XEND,
- ifp_compose->width - 1, &ret);
+ compose->width - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AWB_CLIP_WINDOW_YEND,
- ifp_compose->height - 1, &ret);
+ compose->height - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XSTART,
0, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YSTART,
0, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_XEND,
- ifp_compose->width / 5 - 1, &ret);
+ compose->width / 5 - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_STAT_AE_INITIAL_WINDOW_YEND,
- ifp_compose->height / 5 - 1, &ret);
+ compose->height / 5 - 1, &ret);
cci_write(sensor->regmap, MT9M114_CAM_CROP_CROPMODE,
MT9M114_CAM_CROP_MODE_AWB_AUTO_CROP_EN |
@@ -894,7 +910,7 @@ static int mt9m114_configure(struct mt9m114 *sensor,
MT9M114_CAM_OUTPUT_FORMAT_FORMAT_MASK |
MT9M114_CAM_OUTPUT_FORMAT_SWAP_BYTES |
MT9M114_CAM_OUTPUT_FORMAT_SWAP_RED_BLUE);
- output_format |= ifp_info->output_format;
+ output_format |= info->output_format;
cci_write(sensor->regmap, MT9M114_CAM_OUTPUT_FORMAT,
output_format, &ret);
@@ -925,7 +941,11 @@ static int mt9m114_start_streaming(struct mt9m114 *sensor,
if (ret)
return ret;
- ret = mt9m114_configure(sensor, pa_state, ifp_state);
+ ret = mt9m114_configure_ifp(sensor, ifp_state);
+ if (ret)
+ goto error;
+
+ ret = mt9m114_configure_pa(sensor, pa_state);
if (ret)
goto error;
@@ -1599,13 +1619,9 @@ static int mt9m114_ifp_get_frame_interval(struct v4l2_subdev *sd,
if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
return -EINVAL;
- mutex_lock(sensor->ifp.hdl.lock);
-
ival->numerator = 1;
ival->denominator = sensor->ifp.frame_rate;
- mutex_unlock(sensor->ifp.hdl.lock);
-
return 0;
}
@@ -1624,8 +1640,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd,
if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
return -EINVAL;
- mutex_lock(sensor->ifp.hdl.lock);
-
if (ival->numerator != 0 && ival->denominator != 0)
sensor->ifp.frame_rate = min_t(unsigned int,
ival->denominator / ival->numerator,
@@ -1639,8 +1653,6 @@ static int mt9m114_ifp_set_frame_interval(struct v4l2_subdev *sd,
if (sensor->streaming)
ret = mt9m114_set_frame_rate(sensor);
- mutex_unlock(sensor->ifp.hdl.lock);
-
return ret;
}
@@ -2235,9 +2247,22 @@ static const struct dev_pm_ops mt9m114_pm_ops = {
* Probe & Remove
*/
+static int mt9m114_verify_link_frequency(struct mt9m114 *sensor,
+ unsigned int pixrate)
+{
+ unsigned int link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY
+ ? pixrate * 8 : pixrate * 2;
+
+ if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
+ sensor->bus_cfg.link_frequencies[0] != link_freq)
+ return -EINVAL;
+
+ return 0;
+}
+
static int mt9m114_clk_init(struct mt9m114 *sensor)
{
- unsigned int link_freq;
+ unsigned int pixrate;
/* Hardcode the PLL multiplier and dividers to default settings. */
sensor->pll.m = 32;
@@ -2249,19 +2274,29 @@ static int mt9m114_clk_init(struct mt9m114 *sensor)
* for 16-bit per pixel, transmitted in DDR over a single lane. For
* parallel mode, the sensor ouputs one pixel in two PIXCLK cycles.
*/
- sensor->pixrate = clk_get_rate(sensor->clk) * sensor->pll.m
- / ((sensor->pll.n + 1) * (sensor->pll.p + 1));
- link_freq = sensor->bus_cfg.bus_type == V4L2_MBUS_CSI2_DPHY
- ? sensor->pixrate * 8 : sensor->pixrate * 2;
+ /*
+ * Check if EXTCLK fits the configured link frequency. Bypass the PLL
+ * in this case.
+ */
+ pixrate = clk_get_rate(sensor->clk) / 2;
+ if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) {
+ sensor->pixrate = pixrate;
+ sensor->bypass_pll = true;
+ return 0;
+ }
- if (sensor->bus_cfg.nr_of_link_frequencies != 1 ||
- sensor->bus_cfg.link_frequencies[0] != link_freq) {
- dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n");
- return -EINVAL;
+ /* Check if the PLL configuration fits the configured link frequency. */
+ pixrate = clk_get_rate(sensor->clk) * sensor->pll.m
+ / ((sensor->pll.n + 1) * (sensor->pll.p + 1));
+ if (mt9m114_verify_link_frequency(sensor, pixrate) == 0) {
+ sensor->pixrate = pixrate;
+ sensor->bypass_pll = false;
+ return 0;
}
- return 0;
+ dev_err(&sensor->client->dev, "Unsupported DT link-frequencies\n");
+ return -EINVAL;
}
static int mt9m114_identify(struct mt9m114 *sensor)
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index 06b7896c3eaf..586b31ba076b 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -1469,14 +1469,15 @@ static int ov2659_probe(struct i2c_client *client)
V4L2_CID_TEST_PATTERN,
ARRAY_SIZE(ov2659_test_pattern_menu) - 1,
0, 0, ov2659_test_pattern_menu);
- ov2659->sd.ctrl_handler = &ov2659->ctrls;
if (ov2659->ctrls.error) {
dev_err(&client->dev, "%s: control initialization error %d\n",
__func__, ov2659->ctrls.error);
+ v4l2_ctrl_handler_free(&ov2659->ctrls);
return ov2659->ctrls.error;
}
+ ov2659->sd.ctrl_handler = &ov2659->ctrls;
sd = &ov2659->sd;
client->flags |= I2C_CLIENT_SCCB;
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 6cf461e3373c..4e959534e6e7 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -766,11 +766,9 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd);
struct v4l2_ctrl_handler *ctrl_hdlr;
- const struct ov2740_mode *cur_mode;
s64 exposure_max, h_blank, pixel_rate;
u32 vblank_min, vblank_max, vblank_default;
struct v4l2_fwnode_device_properties props;
- int size;
int ret;
ctrl_hdlr = &ov2740->ctrl_handler;
@@ -778,12 +776,10 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
if (ret)
return ret;
- cur_mode = ov2740->cur_mode;
- size = ARRAY_SIZE(link_freq_menu_items);
-
ov2740->link_freq =
v4l2_ctrl_new_int_menu(ctrl_hdlr, &ov2740_ctrl_ops,
- V4L2_CID_LINK_FREQ, size - 1,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1,
ov2740->supported_modes->link_freq_index,
link_freq_menu_items);
if (ov2740->link_freq)
@@ -794,14 +790,14 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
V4L2_CID_PIXEL_RATE, 0,
pixel_rate, 1, pixel_rate);
- vblank_min = cur_mode->vts_min - cur_mode->height;
- vblank_max = cur_mode->vts_max - cur_mode->height;
- vblank_default = cur_mode->vts_def - cur_mode->height;
+ vblank_min = ov2740->cur_mode->vts_min - ov2740->cur_mode->height;
+ vblank_max = ov2740->cur_mode->vts_max - ov2740->cur_mode->height;
+ vblank_default = ov2740->cur_mode->vts_def - ov2740->cur_mode->height;
ov2740->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_VBLANK, vblank_min,
vblank_max, 1, vblank_default);
- h_blank = cur_mode->hts - cur_mode->width;
+ h_blank = ov2740->cur_mode->hts - ov2740->cur_mode->width;
ov2740->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_HBLANK, h_blank, h_blank, 1,
h_blank);
@@ -814,7 +810,7 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops, V4L2_CID_DIGITAL_GAIN,
OV2740_DGTL_GAIN_MIN, OV2740_DGTL_GAIN_MAX,
OV2740_DGTL_GAIN_STEP, OV2740_DGTL_GAIN_DEFAULT);
- exposure_max = cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN;
+ exposure_max = ov2740->cur_mode->vts_def - OV2740_EXPOSURE_MAX_MARGIN;
ov2740->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_EXPOSURE,
OV2740_EXPOSURE_MIN, exposure_max,
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
index c54bbc207189..b9efb2d2276a 100644
--- a/drivers/media/i2c/ov5670.c
+++ b/drivers/media/i2c/ov5670.c
@@ -2688,10 +2688,15 @@ static int ov5670_probe(struct i2c_client *client)
if (ret)
return dev_err_probe(&client->dev, ret, "GPIO probe failed\n");
- /* Graph Endpoint */
+ /*
+ * Graph Endpoint. If it's missing we defer rather than fail, as this
+ * sensor is known to co-exist on systems with the IPU3 and so it might
+ * be created by the ipu-bridge.
+ */
handle = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
if (!handle)
- return dev_err_probe(&client->dev, -ENXIO, "Endpoint for node get failed\n");
+ return dev_err_probe(&client->dev, -EPROBE_DEFER,
+ "Endpoint for node get failed\n");
ov5670->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY;
ov5670->endpoint.bus.mipi_csi2.num_data_lanes = 2;
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 46b9ce111676..485efd15257e 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -1222,9 +1222,14 @@ static int ov5693_check_hwcfg(struct ov5693_device *ov5693)
unsigned int i;
int ret;
+ /*
+ * Sometimes the fwnode graph is initialized by the bridge driver
+ * Bridge drivers doing this may also add GPIO mappings, wait for this.
+ */
endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!endpoint)
- return -EPROBE_DEFER; /* Could be provided by cio2-bridge */
+ return dev_err_probe(ov5693->dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
fwnode_handle_put(endpoint);
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
index 3226888d77e9..31a42d81e970 100644
--- a/drivers/media/i2c/ov7251.c
+++ b/drivers/media/i2c/ov7251.c
@@ -1486,9 +1486,14 @@ static int ov7251_check_hwcfg(struct ov7251 *ov7251)
unsigned int i, j;
int ret;
+ /*
+ * Sometimes the fwnode graph is initialized by the bridge driver
+ * Bridge drivers doing this may also add GPIO mappings, wait for this.
+ */
endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!endpoint)
- return -EPROBE_DEFER; /* could be provided by cio2-bridge */
+ return dev_err_probe(ov7251->dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
fwnode_handle_put(endpoint);
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 95ffe7536aa6..a2138f7988aa 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -2991,7 +2991,8 @@ static int ov8865_probe(struct i2c_client *client)
handle = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL);
if (!handle)
- return -EPROBE_DEFER;
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
sensor->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY;
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index a1c71187e773..b8b8f206ec3a 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -25,6 +25,7 @@
#include "saa711x_regs.h"
+#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -664,15 +665,6 @@ static const unsigned char saa7115_init_misc[] = {
0x00, 0x00
};
-static int saa711x_odd_parity(u8 c)
-{
- c ^= (c >> 4);
- c ^= (c >> 2);
- c ^= (c >> 1);
-
- return c & 1;
-}
-
static int saa711x_decode_vps(u8 *dst, u8 *p)
{
static const u8 biphase_tbl[] = {
@@ -1227,7 +1219,7 @@ static int saa711x_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vb
vbi->type = V4L2_SLICED_TELETEXT_B;
break;
case 4:
- if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
+ if (!parity8(p[0]) || !parity8(p[1]))
return 0;
vbi->type = V4L2_SLICED_CAPTION_525;
break;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index 3d6703b75bfa..1cc7636e446d 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -114,7 +114,7 @@ static inline struct tc358743_state *to_state(struct v4l2_subdev *sd)
/* --------------- I2C --------------- */
-static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
+static int i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
{
struct tc358743_state *state = to_state(sd);
struct i2c_client *client = state->i2c_client;
@@ -140,6 +140,7 @@ static void i2c_rd(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
v4l2_err(sd, "%s: reading register 0x%x from 0x%x failed: %d\n",
__func__, reg, client->addr, err);
}
+ return err != ARRAY_SIZE(msgs);
}
static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
@@ -196,15 +197,24 @@ static void i2c_wr(struct v4l2_subdev *sd, u16 reg, u8 *values, u32 n)
}
}
-static noinline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n)
+static noinline u32 i2c_rdreg_err(struct v4l2_subdev *sd, u16 reg, u32 n,
+ int *err)
{
+ int error;
__le32 val = 0;
- i2c_rd(sd, reg, (u8 __force *)&val, n);
+ error = i2c_rd(sd, reg, (u8 __force *)&val, n);
+ if (err)
+ *err = error;
return le32_to_cpu(val);
}
+static inline u32 i2c_rdreg(struct v4l2_subdev *sd, u16 reg, u32 n)
+{
+ return i2c_rdreg_err(sd, reg, n, NULL);
+}
+
static noinline void i2c_wrreg(struct v4l2_subdev *sd, u16 reg, u32 val, u32 n)
{
__le32 raw = cpu_to_le32(val);
@@ -233,6 +243,13 @@ static u16 i2c_rd16(struct v4l2_subdev *sd, u16 reg)
return i2c_rdreg(sd, reg, 2);
}
+static int i2c_rd16_err(struct v4l2_subdev *sd, u16 reg, u16 *value)
+{
+ int err;
+ *value = i2c_rdreg_err(sd, reg, 2, &err);
+ return err;
+}
+
static void i2c_wr16(struct v4l2_subdev *sd, u16 reg, u16 val)
{
i2c_wrreg(sd, reg, val, 2);
@@ -420,9 +437,9 @@ static void tc358743_enable_edid(struct v4l2_subdev *sd)
v4l2_dbg(2, debug, sd, "%s:\n", __func__);
- /* Enable hotplug after 100 ms. DDC access to EDID is also enabled when
+ /* Enable hotplug after 143 ms. DDC access to EDID is also enabled when
* hotplug is enabled. See register DDC_CTL */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 10);
+ schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7);
tc358743_enable_interrupts(sd, true);
tc358743_s_ctrl_detect_tx_5v(sd);
@@ -1691,12 +1708,23 @@ static int tc358743_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
+static u32 tc358743_g_colorspace(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ return V4L2_COLORSPACE_SRGB;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ return V4L2_COLORSPACE_SMPTE170M;
+ default:
+ return 0;
+ }
+}
+
static int tc358743_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
struct tc358743_state *state = to_state(sd);
- u8 vi_rep = i2c_rd8(sd, VI_REP);
if (format->pad != 0)
return -EINVAL;
@@ -1706,23 +1734,7 @@ static int tc358743_get_fmt(struct v4l2_subdev *sd,
format->format.height = state->timings.bt.height;
format->format.field = V4L2_FIELD_NONE;
- switch (vi_rep & MASK_VOUT_COLOR_SEL) {
- case MASK_VOUT_COLOR_RGB_FULL:
- case MASK_VOUT_COLOR_RGB_LIMITED:
- format->format.colorspace = V4L2_COLORSPACE_SRGB;
- break;
- case MASK_VOUT_COLOR_601_YCBCR_LIMITED:
- case MASK_VOUT_COLOR_601_YCBCR_FULL:
- format->format.colorspace = V4L2_COLORSPACE_SMPTE170M;
- break;
- case MASK_VOUT_COLOR_709_YCBCR_FULL:
- case MASK_VOUT_COLOR_709_YCBCR_LIMITED:
- format->format.colorspace = V4L2_COLORSPACE_REC709;
- break;
- default:
- format->format.colorspace = 0;
- break;
- }
+ format->format.colorspace = tc358743_g_colorspace(format->format.code);
return 0;
}
@@ -1736,19 +1748,14 @@ static int tc358743_set_fmt(struct v4l2_subdev *sd,
u32 code = format->format.code; /* is overwritten by get_fmt */
int ret = tc358743_get_fmt(sd, sd_state, format);
- format->format.code = code;
+ if (code == MEDIA_BUS_FMT_RGB888_1X24 ||
+ code == MEDIA_BUS_FMT_UYVY8_1X16)
+ format->format.code = code;
+ format->format.colorspace = tc358743_g_colorspace(format->format.code);
if (ret)
return ret;
- switch (code) {
- case MEDIA_BUS_FMT_RGB888_1X24:
- case MEDIA_BUS_FMT_UYVY8_1X16:
- break;
- default:
- return -EINVAL;
- }
-
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
return 0;
@@ -1972,8 +1979,19 @@ static int tc358743_probe_of(struct tc358743_state *state)
state->pdata.refclk_hz = clk_get_rate(refclk);
state->pdata.ddc5v_delay = DDC5V_DELAY_100_MS;
state->pdata.enable_hdcp = false;
- /* A FIFO level of 16 should be enough for 2-lane 720p60 at 594 MHz. */
- state->pdata.fifo_level = 16;
+ /*
+ * Ideally the FIFO trigger level should be set based on the input and
+ * output data rates, but the calculations required are buried in
+ * Toshiba's register settings spreadsheet.
+ * A value of 16 works with a 594Mbps data rate for 720p60 (using 2
+ * lanes) and 1080p60 (using 4 lanes), but fails when the data rate
+ * is increased, or a lower pixel clock is used that result in CSI
+ * reading out faster than the data is arriving.
+ *
+ * A value of 374 works with both those modes at 594Mbps, and with most
+ * modes on 972Mbps.
+ */
+ state->pdata.fifo_level = 374;
/*
* The PLL input clock is obtained by dividing refclk by pll_prd.
* It must be between 6 MHz and 40 MHz, lower frequency is better.
@@ -1993,6 +2011,7 @@ static int tc358743_probe_of(struct tc358743_state *state)
/*
* The CSI bps per lane must be between 62.5 Mbps and 1 Gbps.
* The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60.
+ * 972 Mbps allows 1080P50 UYVY over 2-lane.
*/
bps_pr_lane = 2 * endpoint.link_frequencies[0];
if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) {
@@ -2006,23 +2025,42 @@ static int tc358743_probe_of(struct tc358743_state *state)
state->pdata.refclk_hz * state->pdata.pll_prd;
/*
- * FIXME: These timings are from REF_02 for 594 Mbps per lane (297 MHz
- * link frequency). In principle it should be possible to calculate
+ * FIXME: These timings are from REF_02 for 594 or 972 Mbps per lane
+ * (297 MHz or 486 MHz link frequency).
+ * In principle it should be possible to calculate
* them based on link frequency and resolution.
*/
- if (bps_pr_lane != 594000000U)
+ switch (bps_pr_lane) {
+ default:
dev_warn(dev, "untested bps per lane: %u bps\n", bps_pr_lane);
- state->pdata.lineinitcnt = 0xe80;
- state->pdata.lptxtimecnt = 0x003;
- /* tclk-preparecnt: 3, tclk-zerocnt: 20 */
- state->pdata.tclk_headercnt = 0x1403;
- state->pdata.tclk_trailcnt = 0x00;
- /* ths-preparecnt: 3, ths-zerocnt: 1 */
- state->pdata.ths_headercnt = 0x0103;
- state->pdata.twakeup = 0x4882;
- state->pdata.tclk_postcnt = 0x008;
- state->pdata.ths_trailcnt = 0x2;
- state->pdata.hstxvregcnt = 0;
+ fallthrough;
+ case 594000000U:
+ state->pdata.lineinitcnt = 0xe80;
+ state->pdata.lptxtimecnt = 0x003;
+ /* tclk-preparecnt: 3, tclk-zerocnt: 20 */
+ state->pdata.tclk_headercnt = 0x1403;
+ state->pdata.tclk_trailcnt = 0x00;
+ /* ths-preparecnt: 3, ths-zerocnt: 1 */
+ state->pdata.ths_headercnt = 0x0103;
+ state->pdata.twakeup = 0x4882;
+ state->pdata.tclk_postcnt = 0x008;
+ state->pdata.ths_trailcnt = 0x2;
+ state->pdata.hstxvregcnt = 0;
+ break;
+ case 972000000U:
+ state->pdata.lineinitcnt = 0x1b58;
+ state->pdata.lptxtimecnt = 0x007;
+ /* tclk-preparecnt: 6, tclk-zerocnt: 40 */
+ state->pdata.tclk_headercnt = 0x2806;
+ state->pdata.tclk_trailcnt = 0x00;
+ /* ths-preparecnt: 6, ths-zerocnt: 8 */
+ state->pdata.ths_headercnt = 0x0806;
+ state->pdata.twakeup = 0x4268;
+ state->pdata.tclk_postcnt = 0x008;
+ state->pdata.ths_trailcnt = 0x5;
+ state->pdata.hstxvregcnt = 0;
+ break;
+ }
state->reset_gpio = devm_gpiod_get_optional(dev, "reset",
GPIOD_OUT_LOW);
@@ -2061,6 +2099,7 @@ static int tc358743_probe(struct i2c_client *client)
struct tc358743_platform_data *pdata = client->dev.platform_data;
struct v4l2_subdev *sd;
u16 irq_mask = MASK_HDMI_MSK | MASK_CSI_MSK;
+ u16 chipid;
int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@@ -2092,7 +2131,8 @@ static int tc358743_probe(struct i2c_client *client)
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
/* i2c access */
- if ((i2c_rd16(sd, CHIPID) & MASK_CHIPID) != 0) {
+ if (i2c_rd16_err(sd, CHIPID, &chipid) ||
+ (chipid & MASK_CHIPID) != 0) {
v4l2_info(sd, "not a TC358743 on address 0x%x\n",
client->addr << 1);
return -ENODEV;
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 959590afc80f..1087d2bddaf2 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -589,8 +589,8 @@ static void tda1997x_enable_edid(struct v4l2_subdev *sd)
v4l2_dbg(1, debug, sd, "%s\n", __func__);
- /* Enable hotplug after 100ms */
- schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 10);
+ /* Enable hotplug after 143ms */
+ schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 7);
}
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c
index 25e2fc88a036..c0754fd03b1d 100644
--- a/drivers/media/i2c/vd55g1.c
+++ b/drivers/media/i2c/vd55g1.c
@@ -111,9 +111,9 @@
#define VD55G1_WIDTH 804
#define VD55G1_HEIGHT 704
-#define VD55G1_DEFAULT_MODE 0
+#define VD55G1_MODE_DEF 0
#define VD55G1_NB_GPIOS 4
-#define VD55G1_MEDIA_BUS_FMT_DEF MEDIA_BUS_FMT_Y8_1X8
+#define VD55G1_MBUS_CODE_DEF 0
#define VD55G1_DGAIN_DEF 256
#define VD55G1_AGAIN_DEF 19
#define VD55G1_EXPO_MAX_TERM 64
@@ -129,8 +129,8 @@
#define VD55G1_FWPATCH_REVISION_MINOR 9
#define VD55G1_XCLK_FREQ_MIN (6 * HZ_PER_MHZ)
#define VD55G1_XCLK_FREQ_MAX (27 * HZ_PER_MHZ)
-#define VD55G1_MIPI_RATE_MIN (250 * HZ_PER_MHZ)
-#define VD55G1_MIPI_RATE_MAX (1200 * HZ_PER_MHZ)
+#define VD55G1_MIPI_RATE_MIN (250 * MEGA)
+#define VD55G1_MIPI_RATE_MAX (1200 * MEGA)
static const u8 patch_array[] = {
0x44, 0x03, 0x09, 0x02, 0xe6, 0x01, 0x42, 0x00, 0xea, 0x01, 0x42, 0x00,
@@ -883,10 +883,9 @@ static int vd55g1_apply_cold_start(struct vd55g1 *sensor,
return ret;
}
-static void vd55g1_update_img_pad_format(struct vd55g1 *sensor,
- const struct vd55g1_mode *mode,
- u32 code,
- struct v4l2_mbus_framefmt *fmt)
+static void vd55g1_update_pad_fmt(struct vd55g1 *sensor,
+ const struct vd55g1_mode *mode, u32 code,
+ struct v4l2_mbus_framefmt *fmt)
{
fmt->code = code;
fmt->width = mode->width;
@@ -1038,8 +1037,6 @@ static int vd55g1_enable_streams(struct v4l2_subdev *sd,
if (ret < 0)
return ret;
- vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret);
-
/* Configure output */
vd55g1_write(sensor, VD55G1_REG_MIPI_DATA_RATE,
sensor->mipi_rate, &ret);
@@ -1084,7 +1081,7 @@ static int vd55g1_enable_streams(struct v4l2_subdev *sd,
err_rpm_put:
pm_runtime_put(sensor->dev);
- return 0;
+ return -EINVAL;
}
static int vd55g1_disable_streams(struct v4l2_subdev *sd,
@@ -1231,8 +1228,8 @@ static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd,
width, height, sd_fmt->format.width,
sd_fmt->format.height);
- vd55g1_update_img_pad_format(sensor, new_mode, sd_fmt->format.code,
- &sd_fmt->format);
+ vd55g1_update_pad_fmt(sensor, new_mode, sd_fmt->format.code,
+ &sd_fmt->format);
/*
* Use binning to maximize the crop rectangle size, and centre it in the
@@ -1262,7 +1259,6 @@ static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd,
static int vd55g1_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
- unsigned int def_mode = VD55G1_DEFAULT_MODE;
struct vd55g1 *sensor = to_vd55g1(sd);
struct v4l2_subdev_format fmt = { 0 };
struct v4l2_subdev_route routes[] = {
@@ -1279,8 +1275,9 @@ static int vd55g1_init_state(struct v4l2_subdev *sd,
if (ret)
return ret;
- vd55g1_update_img_pad_format(sensor, &vd55g1_supported_modes[def_mode],
- VD55G1_MEDIA_BUS_FMT_DEF, &fmt.format);
+ vd55g1_update_pad_fmt(sensor, &vd55g1_supported_modes[VD55G1_MODE_DEF],
+ vd55g1_mbus_codes[VD55G1_MBUS_CODE_DEF].code,
+ &fmt.format);
return vd55g1_set_pad_fmt(sd, sd_state, &fmt);
}
@@ -1613,6 +1610,9 @@ static int vd55g1_power_on(struct device *dev)
goto disable_clock;
}
+ /* Setup clock now to advance through system FSM states */
+ vd55g1_write(sensor, VD55G1_REG_EXT_CLOCK, sensor->xclk_freq, &ret);
+
ret = vd55g1_patch(sensor);
if (ret) {
dev_err(dev, "Sensor patch failed %d\n", ret);
diff --git a/drivers/media/pci/cx18/cx18-av-vbi.c b/drivers/media/pci/cx18/cx18-av-vbi.c
index 65281d40c681..1a113aad9cd4 100644
--- a/drivers/media/pci/cx18/cx18-av-vbi.c
+++ b/drivers/media/pci/cx18/cx18-av-vbi.c
@@ -8,6 +8,7 @@
*/
+#include <linux/bitops.h>
#include "cx18-driver.h"
/*
@@ -56,15 +57,6 @@ struct vbi_anc_data {
/* u8 fill[]; Variable number of fill bytes */
};
-static int odd_parity(u8 c)
-{
- c ^= (c >> 4);
- c ^= (c >> 2);
- c ^= (c >> 1);
-
- return c & 1;
-}
-
static int decode_vps(u8 *dst, u8 *p)
{
static const u8 biphase_tbl[] = {
@@ -278,7 +270,7 @@ int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
break;
case 6:
sdid = V4L2_SLICED_CAPTION_525;
- err = !odd_parity(p[0]) || !odd_parity(p[1]);
+ err = !parity8(p[0]) || !parity8(p[1]);
break;
case 9:
sdid = V4L2_SLICED_VPS;
diff --git a/drivers/media/pci/cx18/cx18-driver.h b/drivers/media/pci/cx18/cx18-driver.h
index af05bde75816..485ca9747c4c 100644
--- a/drivers/media/pci/cx18/cx18-driver.h
+++ b/drivers/media/pci/cx18/cx18-driver.h
@@ -271,18 +271,6 @@ struct cx18_options {
#define CX18_SLICED_TYPE_WSS_625 (5)
#define CX18_SLICED_TYPE_VPS (7)
-/**
- * list_entry_is_past_end - check if a previous loop cursor is off list end
- * @pos: the type * previously used as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_head within the struct.
- *
- * Check if the entry's list_head is the head of the list, thus it's not a
- * real entry but was the loop cursor that walked past the end
- */
-#define list_entry_is_past_end(pos, head, member) \
- (&pos->member == (head))
-
struct cx18_vb2_buffer {
/* Common video buffer sub-system struct */
struct vb2_v4l2_buffer vb;
diff --git a/drivers/media/pci/cx18/cx18-fileops.c b/drivers/media/pci/cx18/cx18-fileops.c
index 315577d71d95..cefa91b37f89 100644
--- a/drivers/media/pci/cx18/cx18-fileops.c
+++ b/drivers/media/pci/cx18/cx18-fileops.c
@@ -371,7 +371,7 @@ static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
mdl->curr_buf = list_first_entry(&mdl->buf_list,
struct cx18_buffer, list);
- if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ if (list_entry_is_head(mdl->curr_buf, &mdl->buf_list, list)) {
/*
* For some reason we've exhausted the buffers, but the MDL
* object still said some data was unread.
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 1817b9ed042c..9a1512b1ccaa 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -764,7 +764,7 @@ static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl,
mdl->curr_buf = list_first_entry(&mdl->buf_list,
struct cx18_buffer, list);
- if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
+ if (list_entry_is_head(mdl->curr_buf, &mdl->buf_list, list)) {
/*
* For some reason we've exhausted the buffers, but the MDL
* object still said some data was unread.
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index 83e682e1a4b7..4e579352ab2c 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -55,11 +55,15 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
/* Himax HM2172 */
IPU_SENSOR_CONFIG("HIMX2172", 1, 384000000),
/* GalaxyCore GC0310 */
- IPU_SENSOR_CONFIG("INT0310", 0),
+ IPU_SENSOR_CONFIG("INT0310", 1, 55692000),
/* Omnivision OV5693 */
IPU_SENSOR_CONFIG("INT33BE", 1, 419200000),
+ /* Onsemi MT9M114 */
+ IPU_SENSOR_CONFIG("INT33F0", 1, 384000000),
/* Omnivision OV2740 */
IPU_SENSOR_CONFIG("INT3474", 1, 180000000),
+ /* Omnivision OV5670 */
+ IPU_SENSOR_CONFIG("INT3479", 1, 422400000),
/* Omnivision OV8865 */
IPU_SENSOR_CONFIG("INT347A", 1, 360000000),
/* Omnivision OV7251 */
@@ -78,7 +82,7 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
/* Omnivision OV08A10 */
IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000),
/* Omnivision OV08x40 */
- IPU_SENSOR_CONFIG("OVTI08F4", 1, 400000000),
+ IPU_SENSOR_CONFIG("OVTI08F4", 3, 400000000, 749000000, 800000000),
/* Omnivision OV13B10 */
IPU_SENSOR_CONFIG("OVTI13B1", 1, 560000000),
IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
@@ -86,6 +90,8 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
+ /* Toshiba T4KA3 */
+ IPU_SENSOR_CONFIG("XMCC0003", 1, 321468000),
};
static const struct ipu_property_names prop_names = {
@@ -809,7 +815,8 @@ int ipu_bridge_init(struct device *dev,
return 0;
if (!ipu_bridge_ivsc_is_ready())
- return -EPROBE_DEFER;
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "waiting for IVSC to become ready\n");
bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
if (!bridge)
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index 16fde96c9fb2..a87f105beb5e 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -358,6 +358,8 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
static const int FBPT_WIDTH = DIV_ROUND_UP(CIO2_MAX_LOPS,
CIO2_FBPT_SUBENTRY_UNIT);
const u32 num_buffers1 = CIO2_MAX_BUFFERS - 1;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *format;
const struct ipu3_cio2_fmt *fmt;
void __iomem *const base = cio2->base;
u8 lanes, csi2bus = q->csi2.port;
@@ -365,7 +367,13 @@ static int cio2_hw_init(struct cio2_device *cio2, struct cio2_queue *q)
struct cio2_csi2_timing timing = { 0 };
int i, r;
- fmt = cio2_find_format(NULL, &q->subdev_fmt.code);
+ state = v4l2_subdev_lock_and_get_active_state(&q->subdev);
+ format = v4l2_subdev_state_get_format(state, CIO2_PAD_SINK);
+
+ fmt = cio2_find_format(NULL, &format->code);
+
+ v4l2_subdev_unlock_state(state);
+
if (!fmt)
return -EINVAL;
@@ -1194,9 +1202,9 @@ static int cio2_subdev_subscribe_event(struct v4l2_subdev *sd,
return v4l2_event_subscribe(fh, sub, 0, NULL);
}
-static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+static int cio2_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
{
- struct v4l2_mbus_framefmt *format;
const struct v4l2_mbus_framefmt fmt_default = {
.width = 1936,
.height = 1096,
@@ -1207,42 +1215,23 @@ static int cio2_subdev_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
.quantization = V4L2_QUANTIZATION_DEFAULT,
.xfer_func = V4L2_XFER_FUNC_DEFAULT,
};
+ struct v4l2_mbus_framefmt *format;
- /* Initialize try_fmt */
- format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SINK);
+ /* Initialize the format on the sink and source pads. */
+ format = v4l2_subdev_state_get_format(state, CIO2_PAD_SINK);
*format = fmt_default;
/* same as sink */
- format = v4l2_subdev_state_get_format(fh->state, CIO2_PAD_SOURCE);
+ format = v4l2_subdev_state_get_format(state, CIO2_PAD_SOURCE);
*format = fmt_default;
return 0;
}
-static int cio2_subdev_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *fmt)
-{
- struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
-
- mutex_lock(&q->subdev_lock);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
- fmt->format = *v4l2_subdev_state_get_format(sd_state,
- fmt->pad);
- else
- fmt->format = q->subdev_fmt;
-
- mutex_unlock(&q->subdev_lock);
-
- return 0;
-}
-
static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
- struct cio2_queue *q = container_of(sd, struct cio2_queue, subdev);
struct v4l2_mbus_framefmt *mbus;
u32 mbus_code = fmt->format.code;
unsigned int i;
@@ -1252,12 +1241,7 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
* source always propagates from sink
*/
if (fmt->pad == CIO2_PAD_SOURCE)
- return cio2_subdev_get_fmt(sd, sd_state, fmt);
-
- if (fmt->which == V4L2_SUBDEV_FORMAT_TRY)
- mbus = v4l2_subdev_state_get_format(sd_state, fmt->pad);
- else
- mbus = &q->subdev_fmt;
+ return v4l2_subdev_get_fmt(sd, sd_state, fmt);
fmt->format.code = formats[0].mbus_code;
@@ -1272,9 +1256,12 @@ static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
fmt->format.height = min(fmt->format.height, CIO2_IMAGE_MAX_HEIGHT);
fmt->format.field = V4L2_FIELD_NONE;
- mutex_lock(&q->subdev_lock);
+ mbus = v4l2_subdev_state_get_format(sd_state, CIO2_PAD_SINK);
+ *mbus = fmt->format;
+
+ /* Propagate the format to the source pad. */
+ mbus = v4l2_subdev_state_get_format(sd_state, CIO2_PAD_SOURCE);
*mbus = fmt->format;
- mutex_unlock(&q->subdev_lock);
return 0;
}
@@ -1345,12 +1332,12 @@ static const struct v4l2_subdev_core_ops cio2_subdev_core_ops = {
};
static const struct v4l2_subdev_internal_ops cio2_subdev_internal_ops = {
- .open = cio2_subdev_open,
+ .init_state = cio2_subdev_init_state,
};
static const struct v4l2_subdev_pad_ops cio2_subdev_pad_ops = {
.link_validate = v4l2_subdev_link_validate_default,
- .get_fmt = cio2_subdev_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = cio2_subdev_set_fmt,
.enum_mbus_code = cio2_subdev_enum_mbus_code,
};
@@ -1502,28 +1489,18 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
{
static const u32 default_width = 1936;
static const u32 default_height = 1096;
- const struct ipu3_cio2_fmt dflt_fmt = formats[0];
struct device *dev = &cio2->pci_dev->dev;
struct video_device *vdev = &q->vdev;
struct vb2_queue *vbq = &q->vbq;
struct v4l2_subdev *subdev = &q->subdev;
- struct v4l2_mbus_framefmt *fmt;
int r;
/* Initialize miscellaneous variables */
mutex_init(&q->lock);
- mutex_init(&q->subdev_lock);
-
- /* Initialize formats to default values */
- fmt = &q->subdev_fmt;
- fmt->width = default_width;
- fmt->height = default_height;
- fmt->code = dflt_fmt.mbus_code;
- fmt->field = V4L2_FIELD_NONE;
q->format.width = default_width;
q->format.height = default_height;
- q->format.pixelformat = dflt_fmt.fourcc;
+ q->format.pixelformat = formats[0].fourcc;
q->format.colorspace = V4L2_COLORSPACE_RAW;
q->format.field = V4L2_FIELD_NONE;
q->format.num_planes = 1;
@@ -1567,9 +1544,16 @@ static int cio2_queue_init(struct cio2_device *cio2, struct cio2_queue *q)
CIO2_ENTITY_NAME " %td", q - cio2->queue);
subdev->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
v4l2_set_subdevdata(subdev, cio2);
+
+ r = v4l2_subdev_init_finalize(subdev);
+ if (r) {
+ dev_err(dev, "failed to initialize subdev (%d)\n", r);
+ goto fail_subdev;
+ }
+
r = v4l2_device_register_subdev(&cio2->v4l2_dev, subdev);
if (r) {
- dev_err(dev, "failed initialize subdev (%d)\n", r);
+ dev_err(dev, "failed to register subdev (%d)\n", r);
goto fail_subdev;
}
@@ -1626,7 +1610,6 @@ fail_vdev_media_entity:
fail_subdev_media_entity:
cio2_fbpt_exit(q, dev);
fail_fbpt:
- mutex_destroy(&q->subdev_lock);
mutex_destroy(&q->lock);
return r;
@@ -1639,7 +1622,6 @@ static void cio2_queue_exit(struct cio2_device *cio2, struct cio2_queue *q)
v4l2_device_unregister_subdev(&q->subdev);
media_entity_cleanup(&q->subdev.entity);
cio2_fbpt_exit(q, &cio2->pci_dev->dev);
- mutex_destroy(&q->subdev_lock);
mutex_destroy(&q->lock);
}
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.h b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
index d7cb7dae665b..19258190936a 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.h
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.h
@@ -351,9 +351,7 @@ struct cio2_queue {
/* Subdev, /dev/v4l-subdevX */
struct v4l2_subdev subdev;
- struct mutex subdev_lock; /* Serialise acces to subdev_fmt field */
struct media_pad subdev_pads[CIO2_PADS];
- struct v4l2_mbus_framefmt subdev_fmt;
atomic_t frame_sequence;
/* Video device, /dev/videoX */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
index da8581a37e22..6030bd23b4b9 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
@@ -354,9 +354,9 @@ static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd,
remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
- sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC,
- CSI2_PAD_SINK,
- &streams_mask);
+ sink_streams =
+ v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
+ &streams_mask);
ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
if (ret)
@@ -384,9 +384,9 @@ static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd,
struct media_pad *remote_pad;
u64 sink_streams;
- sink_streams = v4l2_subdev_state_xlate_streams(state, CSI2_PAD_SRC,
- CSI2_PAD_SINK,
- &streams_mask);
+ sink_streams =
+ v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
+ &streams_mask);
remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h
index f488e782c26e..0e2c8b71edfc 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h
@@ -40,7 +40,7 @@ struct ipu6_bus_device;
#define IPU6_ISYS_NUM_RECV_QUEUE 1
#define IPU6_ISYS_MIN_WIDTH 2U
-#define IPU6_ISYS_MIN_HEIGHT 2U
+#define IPU6_ISYS_MIN_HEIGHT 1U
#define IPU6_ISYS_MAX_WIDTH 4672U
#define IPU6_ISYS_MAX_HEIGHT 3416U
diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c
index 3622271c71c8..98310b8511b1 100644
--- a/drivers/media/pci/intel/ivsc/mei_ace.c
+++ b/drivers/media/pci/intel/ivsc/mei_ace.c
@@ -529,6 +529,8 @@ static void mei_ace_remove(struct mei_cl_device *cldev)
ace_set_camera_owner(ace, ACE_CAMERA_IVSC);
+ mei_cldev_disable(cldev);
+
mutex_destroy(&ace->lock);
}
@@ -574,7 +576,7 @@ static struct mei_cl_driver mei_ace_driver = {
module_mei_cl_driver(mei_ace_driver);
-MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_DESCRIPTION("Device driver for IVSC ACE");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c
index 92d871a378ba..c2917e156345 100644
--- a/drivers/media/pci/intel/ivsc/mei_csi.c
+++ b/drivers/media/pci/intel/ivsc/mei_csi.c
@@ -760,6 +760,8 @@ static void mei_csi_remove(struct mei_cl_device *cldev)
pm_runtime_disable(&cldev->dev);
+ mei_cldev_disable(cldev);
+
mutex_destroy(&csi->lock);
}
@@ -783,7 +785,7 @@ static struct mei_cl_driver mei_csi_driver = {
module_mei_cl_driver(mei_csi_driver);
MODULE_IMPORT_NS("INTEL_IPU_BRIDGE");
-MODULE_AUTHOR("Wentong Wu <wentong.wu@intel.com>");
+MODULE_AUTHOR("Wentong Wu");
MODULE_AUTHOR("Zhifeng Wang <zhifeng.wang@intel.com>");
MODULE_DESCRIPTION("Device driver for IVSC CSI");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c
index 14c5725bd4d8..c179c425e167 100644
--- a/drivers/media/pci/mgb4/mgb4_vout.c
+++ b/drivers/media/pci/mgb4/mgb4_vout.c
@@ -492,7 +492,14 @@ static int vidioc_s_dv_timings(struct file *file, void *fh,
static int vidioc_enum_dv_timings(struct file *file, void *fh,
struct v4l2_enum_dv_timings *timings)
{
- return v4l2_enum_dv_timings_cap(timings, &video_timings_cap, NULL, NULL);
+ struct mgb4_vout_dev *voutdev = video_drvdata(file);
+
+ if (timings->index != 0)
+ return -EINVAL;
+
+ get_timings(voutdev, &timings->timings);
+
+ return 0;
}
static int vidioc_dv_timings_cap(struct file *file, void *fh,
diff --git a/drivers/media/pci/saa7164/saa7164-buffer.c b/drivers/media/pci/saa7164/saa7164-buffer.c
index 89c5b79a5b24..7820e4f47fd5 100644
--- a/drivers/media/pci/saa7164/saa7164-buffer.c
+++ b/drivers/media/pci/saa7164/saa7164-buffer.c
@@ -52,26 +52,6 @@
* | etc
*/
-void saa7164_buffer_display(struct saa7164_buffer *buf)
-{
- struct saa7164_dev *dev = buf->port->dev;
- int i;
-
- dprintk(DBGLVL_BUF, "%s() buffer @ 0x%p nr=%d\n",
- __func__, buf, buf->idx);
- dprintk(DBGLVL_BUF, " pci_cpu @ 0x%p dma @ 0x%08llx len = 0x%x\n",
- buf->cpu, (long long)buf->dma, buf->pci_size);
- dprintk(DBGLVL_BUF, " pt_cpu @ 0x%p pt_dma @ 0x%08llx len = 0x%x\n",
- buf->pt_cpu, (long long)buf->pt_dma, buf->pt_size);
-
- /* Format the Page Table Entries to point into the data buffer */
- for (i = 0 ; i < SAA7164_PT_ENTRIES; i++) {
-
- dprintk(DBGLVL_BUF, " pt[%02d] = 0x%p -> 0x%llx\n",
- i, buf->pt_cpu, (u64)*(buf->pt_cpu));
-
- }
-}
/* Allocate a new buffer structure and associated PCI space in bytes.
* len must be a multiple of sizeof(u64)
*/
diff --git a/drivers/media/pci/saa7164/saa7164-cmd.c b/drivers/media/pci/saa7164/saa7164-cmd.c
index 42bd8e76005b..a95662776ee8 100644
--- a/drivers/media/pci/saa7164/saa7164-cmd.c
+++ b/drivers/media/pci/saa7164/saa7164-cmd.c
@@ -295,34 +295,6 @@ static int saa7164_cmd_wait(struct saa7164_dev *dev, u8 seqno)
return ret;
}
-void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno)
-{
- int i;
- dprintk(DBGLVL_CMD, "%s()\n", __func__);
-
- mutex_lock(&dev->lock);
- for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
- if (dev->cmds[i].inuse == 1) {
- dprintk(DBGLVL_CMD,
- "seqno %d inuse, sig = %d, t/out = %d\n",
- dev->cmds[i].seqno,
- dev->cmds[i].signalled,
- dev->cmds[i].timeout);
- }
- }
-
- for (i = 0; i < SAA_CMD_MAX_MSG_UNITS; i++) {
- if ((dev->cmds[i].inuse == 1) && ((i == 0) ||
- (dev->cmds[i].signalled) || (dev->cmds[i].timeout))) {
- dprintk(DBGLVL_CMD, "%s(seqno=%d) calling wake_up\n",
- __func__, i);
- dev->cmds[i].signalled = 1;
- wake_up(&dev->cmds[i].wait);
- }
- }
- mutex_unlock(&dev->lock);
-}
-
int saa7164_cmd_send(struct saa7164_dev *dev, u8 id, enum tmComResCmd command,
u16 controlselector, u16 size, void *buf)
{
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
index eede47b686a3..e1bac1fe19d3 100644
--- a/drivers/media/pci/saa7164/saa7164.h
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -508,7 +508,6 @@ int saa7164_bus_get(struct saa7164_dev *dev, struct tmComResInfo* msg,
int saa7164_cmd_send(struct saa7164_dev *dev,
u8 id, enum tmComResCmd command, u16 controlselector,
u16 size, void *buf);
-void saa7164_cmd_signal(struct saa7164_dev *dev, u8 seqno);
int saa7164_irq_dequeue(struct saa7164_dev *dev);
/* ----------------------------------------------------------- */
@@ -570,7 +569,6 @@ extern int saa7164_dvb_unregister(struct saa7164_port *port);
extern struct saa7164_buffer *saa7164_buffer_alloc(
struct saa7164_port *port, u32 len);
extern int saa7164_buffer_dealloc(struct saa7164_buffer *buf);
-extern void saa7164_buffer_display(struct saa7164_buffer *buf);
extern int saa7164_buffer_activate(struct saa7164_buffer *buf, int i);
extern int saa7164_buffer_cfg_port(struct saa7164_port *port);
extern struct saa7164_user_buffer *saa7164_buffer_alloc_user(
diff --git a/drivers/media/pci/solo6x10/solo6x10-gpio.c b/drivers/media/pci/solo6x10/solo6x10-gpio.c
index 084c30760e45..b16a8453a62a 100644
--- a/drivers/media/pci/solo6x10/solo6x10-gpio.c
+++ b/drivers/media/pci/solo6x10/solo6x10-gpio.c
@@ -116,18 +116,6 @@ static int solo_gpiochip_get_direction(struct gpio_chip *chip,
return -1;
}
-static int solo_gpiochip_direction_input(struct gpio_chip *chip,
- unsigned int offset)
-{
- return -1;
-}
-
-static int solo_gpiochip_direction_output(struct gpio_chip *chip,
- unsigned int offset, int value)
-{
- return -1;
-}
-
static int solo_gpiochip_get(struct gpio_chip *chip,
unsigned int offset)
{
@@ -139,8 +127,8 @@ static int solo_gpiochip_get(struct gpio_chip *chip,
return 1 & (ret >> (offset + 8));
}
-static void solo_gpiochip_set(struct gpio_chip *chip,
- unsigned int offset, int value)
+static int solo_gpiochip_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
{
struct solo_dev *solo_dev = gpiochip_get_data(chip);
@@ -148,6 +136,8 @@ static void solo_gpiochip_set(struct gpio_chip *chip,
solo_gpio_set(solo_dev, 1 << (offset + 8));
else
solo_gpio_clear(solo_dev, 1 << (offset + 8));
+
+ return 0;
}
#endif
@@ -167,10 +157,8 @@ int solo_gpio_init(struct solo_dev *solo_dev)
solo_dev->gpio_dev.can_sleep = 0;
solo_dev->gpio_dev.get_direction = solo_gpiochip_get_direction;
- solo_dev->gpio_dev.direction_input = solo_gpiochip_direction_input;
- solo_dev->gpio_dev.direction_output = solo_gpiochip_direction_output;
solo_dev->gpio_dev.get = solo_gpiochip_get;
- solo_dev->gpio_dev.set = solo_gpiochip_set;
+ solo_dev->gpio_dev.set_rv = solo_gpiochip_set;
ret = gpiochip_add_data(&solo_dev->gpio_dev, solo_dev);
diff --git a/drivers/media/platform/amphion/vdec.c b/drivers/media/platform/amphion/vdec.c
index 85d518823159..32eef2fd1f2a 100644
--- a/drivers/media/platform/amphion/vdec.c
+++ b/drivers/media/platform/amphion/vdec.c
@@ -26,6 +26,7 @@
#include "vpu_cmds.h"
#include "vpu_rpc.h"
+#define VDEC_SLOT_CNT_DFT 32
#define VDEC_MIN_BUFFER_CAP 8
#define VDEC_MIN_BUFFER_OUT 8
@@ -41,6 +42,14 @@ struct vdec_fs_info {
u32 tag;
};
+struct vdec_frame_store_t {
+ struct vpu_vb2_buffer *curr;
+ struct vpu_vb2_buffer *pend;
+ dma_addr_t addr;
+ unsigned int state;
+ u32 tag;
+};
+
struct vdec_t {
u32 seq_hdr_found;
struct vpu_buffer udata;
@@ -48,7 +57,8 @@ struct vdec_t {
struct vpu_dec_codec_info codec_info;
enum vpu_codec_state state;
- struct vpu_vb2_buffer *slots[VB2_MAX_FRAME];
+ struct vdec_frame_store_t *slots;
+ u32 slot_count;
u32 req_frame_count;
struct vdec_fs_info mbi;
struct vdec_fs_info dcp;
@@ -232,6 +242,35 @@ static int vdec_ctrl_init(struct vpu_inst *inst)
V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE,
0, 1, 1, 0);
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
+ ~((1 << V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED) |
+ (1 << V4L2_MPEG_VIDEO_H264_PROFILE_HIGH)),
+ V4L2_MPEG_VIDEO_H264_PROFILE_MAIN);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ V4L2_MPEG_VIDEO_H264_LEVEL_6_2,
+ 0,
+ V4L2_MPEG_VIDEO_H264_LEVEL_4_0);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
+ ~((1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ (1 << V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10)),
+ V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN);
+
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, NULL,
+ V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
+ 0,
+ V4L2_MPEG_VIDEO_HEVC_LEVEL_4);
+
ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
V4L2_CID_MIN_BUFFERS_FOR_CAPTURE, 1, 32, 1, 2);
if (ctrl)
@@ -258,6 +297,63 @@ static int vdec_ctrl_init(struct vpu_inst *inst)
return 0;
}
+static void vdec_attach_frame_store(struct vpu_inst *inst, struct vb2_buffer *vb)
+{
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+ struct vdec_t *vdec = inst->priv;
+ struct vdec_frame_store_t *new_slots = NULL;
+ dma_addr_t addr;
+ int i;
+
+ addr = vpu_get_vb_phy_addr(vb, 0);
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (addr == vdec->slots[i].addr) {
+ if (vdec->slots[i].curr && vdec->slots[i].curr != vpu_buf) {
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_CHANGED);
+ vdec->slots[i].pend = vpu_buf;
+ } else {
+ vpu_set_buffer_state(vbuf, vdec->slots[i].state);
+ }
+ vpu_buf->fs_id = i;
+ return;
+ }
+ }
+
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (!vdec->slots[i].addr) {
+ vdec->slots[i].addr = addr;
+ vpu_buf->fs_id = i;
+ return;
+ }
+ }
+
+ new_slots = krealloc_array(vdec->slots, vdec->slot_count * 2,
+ sizeof(*vdec->slots),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!new_slots) {
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_ERROR);
+ return;
+ }
+
+ vdec->slots = new_slots;
+ vdec->slot_count *= 2;
+
+ vdec->slots[i].addr = addr;
+ vpu_buf->fs_id = i;
+}
+
+static void vdec_reset_frame_store(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
+
+ if (!vdec->slots || !vdec->slot_count)
+ return;
+
+ vpu_trace(inst->dev, "inst[%d] reset slots\n", inst->id);
+ memset(vdec->slots, 0, sizeof(*vdec->slots) * vdec->slot_count);
+}
+
static void vdec_handle_resolution_change(struct vpu_inst *inst)
{
struct vdec_t *vdec = inst->priv;
@@ -714,11 +810,11 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg)
struct vb2_v4l2_buffer *src_buf;
int ret = 0;
- if (!info || info->id >= ARRAY_SIZE(vdec->slots))
+ if (!info || info->id >= vdec->slot_count)
return -EINVAL;
vpu_inst_lock(inst);
- vpu_buf = vdec->slots[info->id];
+ vpu_buf = vdec->slots[info->id].curr;
if (!vpu_buf) {
dev_err(inst->dev, "[%d] decoded invalid frame[%d]\n", inst->id, info->id);
ret = -EINVAL;
@@ -739,11 +835,13 @@ static int vdec_frame_decoded(struct vpu_inst *inst, void *arg)
if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED)
dev_info(inst->dev, "[%d] buf[%d] has been decoded\n", inst->id, info->id);
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_DECODED);
+ vdec->slots[info->id].state = VPU_BUF_STATE_DECODED;
vdec->decoded_frame_count++;
if (vdec->params.display_delay_enable) {
struct vpu_format *cur_fmt;
cur_fmt = vpu_get_format(inst, inst->cap_format.type);
+ vdec->slots[info->id].state = VPU_BUF_STATE_READY;
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_READY);
for (int i = 0; i < vbuf->vb2_buf.num_planes; i++)
vb2_set_plane_payload(&vbuf->vb2_buf,
@@ -766,11 +864,11 @@ static struct vpu_vb2_buffer *vdec_find_buffer(struct vpu_inst *inst, u32 luma)
struct vdec_t *vdec = inst->priv;
int i;
- for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) {
- if (!vdec->slots[i])
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (!vdec->slots[i].curr)
continue;
- if (luma == vdec->slots[i]->luma)
- return vdec->slots[i];
+ if (luma == vdec->slots[i].addr)
+ return vdec->slots[i].curr;
}
return NULL;
@@ -804,11 +902,11 @@ 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) mismatch\n",
- inst->id, vbuf->vb2_buf.index, frame->id);
+ if (vpu_buf->fs_id != frame->id)
+ dev_err(inst->dev, "[%d] buffer id(%d(%d), %d) mismatch\n",
+ inst->id, vpu_buf->fs_id, vbuf->vb2_buf.index, frame->id);
- if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_READY && vdec->params.display_delay_enable)
+ if (vdec->params.display_delay_enable)
return;
if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_DECODED)
@@ -821,10 +919,11 @@ static void vdec_buf_done(struct vpu_inst *inst, struct vpu_frame_info *frame)
vbuf->sequence = vdec->sequence;
dev_dbg(inst->dev, "[%d][OUTPUT TS]%32lld\n", inst->id, vbuf->vb2_buf.timestamp);
- v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
vpu_inst_lock(inst);
+ vdec->slots[vpu_buf->fs_id].state = VPU_BUF_STATE_READY;
vdec->display_frame_count++;
vpu_inst_unlock(inst);
+ v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_DONE);
dev_dbg(inst->dev, "[%d] decoded : %d, display : %d, sequence : %d\n",
inst->id, vdec->decoded_frame_count, vdec->display_frame_count, vdec->sequence);
}
@@ -1052,18 +1151,30 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb
if (!vbuf)
return -EINVAL;
- if (vdec->slots[vbuf->vb2_buf.index]) {
- dev_err(inst->dev, "[%d] repeat alloc fs %d\n",
- inst->id, vbuf->vb2_buf.index);
+ vpu_buf = to_vpu_vb2_buffer(vbuf);
+ if (vpu_buf->fs_id < 0 || vpu_buf->fs_id >= vdec->slot_count) {
+ dev_err(inst->dev, "invalid fs %d for v4l2 buffer %d\n",
+ vpu_buf->fs_id, vbuf->vb2_buf.index);
return -EINVAL;
}
+ if (vdec->slots[vpu_buf->fs_id].curr) {
+ if (vdec->slots[vpu_buf->fs_id].curr != vpu_buf) {
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_CHANGED);
+ vdec->slots[vpu_buf->fs_id].pend = vpu_buf;
+ } else {
+ vpu_set_buffer_state(vbuf, vdec->slots[vpu_buf->fs_id].state);
+ }
+ dev_err(inst->dev, "[%d] repeat alloc fs %d (v4l2 index %d)\n",
+ inst->id, vpu_buf->fs_id, vbuf->vb2_buf.index);
+ return -EAGAIN;
+ }
+
dev_dbg(inst->dev, "[%d] state = %s, alloc fs %d, tag = 0x%x\n",
inst->id, vpu_codec_state_name(inst->state), vbuf->vb2_buf.index, vdec->seq_tag);
- vpu_buf = to_vpu_vb2_buffer(vbuf);
memset(&info, 0, sizeof(info));
- info.id = vbuf->vb2_buf.index;
+ info.id = vpu_buf->fs_id;
info.type = MEM_RES_FRAME;
info.tag = vdec->seq_tag;
info.luma_addr = vpu_get_vb_phy_addr(&vbuf->vb2_buf, 0);
@@ -1078,12 +1189,13 @@ static int vdec_response_frame(struct vpu_inst *inst, struct vb2_v4l2_buffer *vb
if (ret)
return ret;
- vpu_buf->tag = info.tag;
vpu_buf->luma = info.luma_addr;
vpu_buf->chroma_u = info.chroma_addr;
vpu_buf->chroma_v = 0;
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_INUSE);
- vdec->slots[info.id] = vpu_buf;
+ vdec->slots[info.id].tag = info.tag;
+ vdec->slots[info.id].curr = vpu_buf;
+ vdec->slots[info.id].state = VPU_BUF_STATE_INUSE;
vdec->req_frame_count--;
return 0;
@@ -1144,25 +1256,76 @@ static void vdec_recycle_buffer(struct vpu_inst *inst, struct vb2_v4l2_buffer *v
v4l2_m2m_buf_queue(inst->fh.m2m_ctx, vbuf);
}
-static void vdec_clear_slots(struct vpu_inst *inst)
+static void vdec_release_curr_frame_store(struct vpu_inst *inst, u32 id)
{
struct vdec_t *vdec = inst->priv;
struct vpu_vb2_buffer *vpu_buf;
struct vb2_v4l2_buffer *vbuf;
+
+ if (id >= vdec->slot_count)
+ return;
+ if (!vdec->slots[id].curr)
+ return;
+
+ vpu_buf = vdec->slots[id].curr;
+ vbuf = &vpu_buf->m2m_buf.vb;
+
+ vdec_response_fs_release(inst, id, vdec->slots[id].tag);
+ if (vpu_buf->fs_id == id) {
+ if (vpu_buf->state != VPU_BUF_STATE_READY)
+ vdec_recycle_buffer(inst, vbuf);
+ vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ }
+
+ vdec->slots[id].curr = NULL;
+ vdec->slots[id].state = VPU_BUF_STATE_IDLE;
+
+ if (vdec->slots[id].pend) {
+ vpu_set_buffer_state(&vdec->slots[id].pend->m2m_buf.vb, VPU_BUF_STATE_IDLE);
+ vdec->slots[id].pend = NULL;
+ }
+}
+
+static void vdec_clear_slots(struct vpu_inst *inst)
+{
+ struct vdec_t *vdec = inst->priv;
int i;
- for (i = 0; i < ARRAY_SIZE(vdec->slots); i++) {
- if (!vdec->slots[i])
+ for (i = 0; i < vdec->slot_count; i++) {
+ if (!vdec->slots[i].curr)
continue;
- vpu_buf = vdec->slots[i];
- vbuf = &vpu_buf->m2m_buf.vb;
-
vpu_trace(inst->dev, "clear slot %d\n", i);
- vdec_response_fs_release(inst, i, vpu_buf->tag);
- vdec_recycle_buffer(inst, vbuf);
- vdec->slots[i]->state = VPU_BUF_STATE_IDLE;
- vdec->slots[i] = NULL;
+ vdec_release_curr_frame_store(inst, i);
+ }
+}
+
+static void vdec_update_v4l2_ctrl(struct vpu_inst *inst, u32 id, u32 val)
+{
+ struct v4l2_ctrl *ctrl = v4l2_ctrl_find(&inst->ctrl_handler, id);
+
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, val);
+}
+
+static void vdec_update_v4l2_profile_level(struct vpu_inst *inst, struct vpu_dec_codec_info *hdr)
+{
+ switch (inst->out_format.pixfmt) {
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_H264_MVC:
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+ vpu_get_h264_v4l2_profile(hdr));
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+ vpu_get_h264_v4l2_level(hdr));
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ vpu_get_hevc_v4l2_profile(hdr));
+ vdec_update_v4l2_ctrl(inst, V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ vpu_get_hevc_v4l2_level(hdr));
+ break;
+ default:
+ return;
}
}
@@ -1189,6 +1352,7 @@ static void vdec_event_seq_hdr(struct vpu_inst *inst, struct vpu_dec_codec_info
vdec_init_crop(inst);
vdec_init_mbi(inst);
vdec_init_dcp(inst);
+ vdec_update_v4l2_profile_level(inst, hdr);
if (!vdec->seq_hdr_found) {
vdec->seq_tag = vdec->codec_info.tag;
if (vdec->is_source_changed) {
@@ -1263,39 +1427,29 @@ static void vdec_event_req_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
static void vdec_evnet_rel_fs(struct vpu_inst *inst, struct vpu_fs_info *fs)
{
struct vdec_t *vdec = inst->priv;
- struct vpu_vb2_buffer *vpu_buf;
- struct vb2_v4l2_buffer *vbuf;
- if (!fs || fs->id >= ARRAY_SIZE(vdec->slots))
+ if (!fs || fs->id >= vdec->slot_count)
return;
if (fs->type != MEM_RES_FRAME)
return;
- if (fs->id >= vpu_get_num_buffers(inst, inst->cap_format.type)) {
+ if (fs->id >= vdec->slot_count) {
dev_err(inst->dev, "[%d] invalid fs(%d) to release\n", inst->id, fs->id);
return;
}
vpu_inst_lock(inst);
- vpu_buf = vdec->slots[fs->id];
- vdec->slots[fs->id] = NULL;
-
- if (!vpu_buf) {
+ if (!vdec->slots[fs->id].curr) {
dev_dbg(inst->dev, "[%d] fs[%d] has bee released\n", inst->id, fs->id);
goto exit;
}
- vbuf = &vpu_buf->m2m_buf.vb;
- if (vpu_get_buffer_state(vbuf) == VPU_BUF_STATE_DECODED) {
+ if (vdec->slots[fs->id].state == VPU_BUF_STATE_DECODED) {
dev_dbg(inst->dev, "[%d] frame skip\n", inst->id);
vdec->sequence++;
}
- vdec_response_fs_release(inst, fs->id, vpu_buf->tag);
- if (vpu_get_buffer_state(vbuf) != VPU_BUF_STATE_READY)
- vdec_recycle_buffer(inst, vbuf);
-
- vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+ vdec_release_curr_frame_store(inst, fs->id);
vpu_process_capture_buffer(inst);
exit:
@@ -1485,6 +1639,11 @@ static void vdec_cleanup(struct vpu_inst *inst)
return;
vdec = inst->priv;
+ if (vdec) {
+ kfree(vdec->slots);
+ vdec->slots = NULL;
+ vdec->slot_count = 0;
+ }
vfree(vdec);
inst->priv = NULL;
vfree(inst);
@@ -1606,11 +1765,43 @@ static int vdec_stop_session(struct vpu_inst *inst, u32 type)
return 0;
}
-static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
+static int vdec_get_slot_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
{
struct vdec_t *vdec = inst->priv;
+ struct vpu_vb2_buffer *vpu_buf;
int num = -1;
+ vpu_inst_lock(inst);
+ if (i >= vdec->slot_count || !vdec->slots[i].addr)
+ goto exit;
+
+ vpu_buf = vdec->slots[i].curr;
+
+ num = scnprintf(str, size, "slot[%2d] :", i);
+ if (vpu_buf) {
+ num += scnprintf(str + num, size - num, " %2d",
+ vpu_buf->m2m_buf.vb.vb2_buf.index);
+ num += scnprintf(str + num, size - num, "; state = %d", vdec->slots[i].state);
+ } else {
+ num += scnprintf(str + num, size - num, " -1");
+ }
+
+ if (vdec->slots[i].pend)
+ num += scnprintf(str + num, size - num, "; %d",
+ vdec->slots[i].pend->m2m_buf.vb.vb2_buf.index);
+
+ num += scnprintf(str + num, size - num, "\n");
+exit:
+ vpu_inst_unlock(inst);
+
+ return num;
+}
+
+static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i)
+{
+ struct vdec_t *vdec = inst->priv;
+ int num;
+
switch (i) {
case 0:
num = scnprintf(str, size,
@@ -1664,6 +1855,7 @@ static int vdec_get_debug_info(struct vpu_inst *inst, char *str, u32 size, u32 i
vdec->codec_info.vui_present);
break;
default:
+ num = vdec_get_slot_debug_info(inst, str, size, i - 10);
break;
}
@@ -1687,6 +1879,8 @@ static struct vpu_inst_ops vdec_inst_ops = {
.get_debug_info = vdec_get_debug_info,
.wait_prepare = vpu_inst_unlock,
.wait_finish = vpu_inst_lock,
+ .attach_frame_store = vdec_attach_frame_store,
+ .reset_frame_store = vdec_reset_frame_store,
};
static void vdec_init(struct file *file)
@@ -1727,6 +1921,16 @@ static int vdec_open(struct file *file)
return -ENOMEM;
}
+ vdec->slots = kmalloc_array(VDEC_SLOT_CNT_DFT,
+ sizeof(*vdec->slots),
+ GFP_KERNEL | __GFP_ZERO);
+ if (!vdec->slots) {
+ vfree(vdec);
+ vfree(inst);
+ return -ENOMEM;
+ }
+ vdec->slot_count = VDEC_SLOT_CNT_DFT;
+
inst->ops = &vdec_inst_ops;
inst->formats = vdec_formats;
inst->type = VPU_CORE_TYPE_DEC;
diff --git a/drivers/media/platform/amphion/vpu.h b/drivers/media/platform/amphion/vpu.h
index 1451549c9dd2..cac0f1a64fea 100644
--- a/drivers/media/platform/amphion/vpu.h
+++ b/drivers/media/platform/amphion/vpu.h
@@ -222,6 +222,8 @@ struct vpu_inst_ops {
int (*get_debug_info)(struct vpu_inst *inst, char *str, u32 size, u32 i);
void (*wait_prepare)(struct vpu_inst *inst);
void (*wait_finish)(struct vpu_inst *inst);
+ void (*attach_frame_store)(struct vpu_inst *inst, struct vb2_buffer *vb);
+ void (*reset_frame_store)(struct vpu_inst *inst);
};
struct vpu_inst {
@@ -295,7 +297,8 @@ enum {
VPU_BUF_STATE_DECODED,
VPU_BUF_STATE_READY,
VPU_BUF_STATE_SKIP,
- VPU_BUF_STATE_ERROR
+ VPU_BUF_STATE_ERROR,
+ VPU_BUF_STATE_CHANGED
};
struct vpu_vb2_buffer {
@@ -304,8 +307,8 @@ struct vpu_vb2_buffer {
dma_addr_t chroma_u;
dma_addr_t chroma_v;
unsigned int state;
- u32 tag;
u32 average_qp;
+ s32 fs_id;
};
void vpu_writel(struct vpu_dev *vpu, u32 reg, u32 val);
diff --git a/drivers/media/platform/amphion/vpu_color.c b/drivers/media/platform/amphion/vpu_color.c
index 4ae435cbc5cd..7c0ab8289a7b 100644
--- a/drivers/media/platform/amphion/vpu_color.c
+++ b/drivers/media/platform/amphion/vpu_color.c
@@ -108,76 +108,3 @@ u32 vpu_color_cvrt_full_range_i2v(u32 full_range)
return V4L2_QUANTIZATION_LIM_RANGE;
}
-
-int vpu_color_check_primaries(u32 primaries)
-{
- return vpu_color_cvrt_primaries_v2i(primaries) ? 0 : -EINVAL;
-}
-
-int vpu_color_check_transfers(u32 transfers)
-{
- return vpu_color_cvrt_transfers_v2i(transfers) ? 0 : -EINVAL;
-}
-
-int vpu_color_check_matrix(u32 matrix)
-{
- return vpu_color_cvrt_matrix_v2i(matrix) ? 0 : -EINVAL;
-}
-
-int vpu_color_check_full_range(u32 full_range)
-{
- int ret = -EINVAL;
-
- switch (full_range) {
- case V4L2_QUANTIZATION_FULL_RANGE:
- case V4L2_QUANTIZATION_LIM_RANGE:
- ret = 0;
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range)
-{
- u32 transfers;
- u32 matrix;
- u32 full_range;
-
- switch (primaries) {
- case V4L2_COLORSPACE_REC709:
- transfers = V4L2_XFER_FUNC_709;
- matrix = V4L2_YCBCR_ENC_709;
- break;
- case V4L2_COLORSPACE_470_SYSTEM_M:
- case V4L2_COLORSPACE_470_SYSTEM_BG:
- case V4L2_COLORSPACE_SMPTE170M:
- transfers = V4L2_XFER_FUNC_709;
- matrix = V4L2_YCBCR_ENC_601;
- break;
- case V4L2_COLORSPACE_SMPTE240M:
- transfers = V4L2_XFER_FUNC_SMPTE240M;
- matrix = V4L2_YCBCR_ENC_SMPTE240M;
- break;
- case V4L2_COLORSPACE_BT2020:
- transfers = V4L2_XFER_FUNC_709;
- matrix = V4L2_YCBCR_ENC_BT2020;
- break;
- default:
- transfers = V4L2_XFER_FUNC_DEFAULT;
- matrix = V4L2_YCBCR_ENC_DEFAULT;
- break;
- }
- full_range = V4L2_QUANTIZATION_LIM_RANGE;
-
- if (ptransfers)
- *ptransfers = transfers;
- if (pmatrix)
- *pmatrix = matrix;
- if (pfull_range)
- *pfull_range = full_range;
-
- return 0;
-}
diff --git a/drivers/media/platform/amphion/vpu_dbg.c b/drivers/media/platform/amphion/vpu_dbg.c
index 940e5bda5fa3..497ae4e8a229 100644
--- a/drivers/media/platform/amphion/vpu_dbg.c
+++ b/drivers/media/platform/amphion/vpu_dbg.c
@@ -48,6 +48,7 @@ static char *vpu_stat_name[] = {
[VPU_BUF_STATE_READY] = "ready",
[VPU_BUF_STATE_SKIP] = "skip",
[VPU_BUF_STATE_ERROR] = "error",
+ [VPU_BUF_STATE_CHANGED] = "changed",
};
static inline const char *to_vpu_stat_name(int state)
@@ -164,6 +165,7 @@ static int vpu_dbg_instance(struct seq_file *s, void *data)
for (i = 0; i < vb2_get_num_buffers(vq); i++) {
struct vb2_buffer *vb;
struct vb2_v4l2_buffer *vbuf;
+ struct vpu_vb2_buffer *vpu_buf;
vb = vb2_get_buffer(vq, i);
if (!vb)
@@ -173,13 +175,24 @@ static int vpu_dbg_instance(struct seq_file *s, void *data)
continue;
vbuf = to_vb2_v4l2_buffer(vb);
+ vpu_buf = to_vpu_vb2_buffer(vbuf);
num = scnprintf(str, sizeof(str),
- "capture[%2d] state = %10s, %8s\n",
+ "capture[%2d] state = %10s, %8s",
i, vb2_stat_name[vb->state],
to_vpu_stat_name(vpu_get_buffer_state(vbuf)));
if (seq_write(s, str, num))
return 0;
+
+ if (vpu_buf->fs_id >= 0) {
+ num = scnprintf(str, sizeof(str), "; fs %d", vpu_buf->fs_id);
+ if (seq_write(s, str, num))
+ return 0;
+ }
+
+ num = scnprintf(str, sizeof(str), "\n");
+ if (seq_write(s, str, num))
+ return 0;
}
num = scnprintf(str, sizeof(str), "sequence = %d\n", inst->sequence);
diff --git a/drivers/media/platform/amphion/vpu_defs.h b/drivers/media/platform/amphion/vpu_defs.h
index 428d988cf2f7..f56245ae2205 100644
--- a/drivers/media/platform/amphion/vpu_defs.h
+++ b/drivers/media/platform/amphion/vpu_defs.h
@@ -134,6 +134,7 @@ struct vpu_dec_codec_info {
u32 decoded_height;
struct v4l2_fract frame_rate;
u32 dsp_asp_ratio;
+ u32 profile_idc;
u32 level_idc;
u32 bit_depth_luma;
u32 bit_depth_chroma;
@@ -147,6 +148,17 @@ struct vpu_dec_codec_info {
u32 mbi_size;
u32 dcp_size;
u32 stride;
+ union {
+ struct {
+ u32 constraint_set5_flag : 1;
+ u32 constraint_set4_flag : 1;
+ u32 constraint_set3_flag : 1;
+ u32 constraint_set2_flag : 1;
+ u32 constraint_set1_flag : 1;
+ u32 constraint_set0_flag : 1;
+ };
+ u32 constraint_set_flags;
+ };
};
struct vpu_dec_pic_info {
diff --git a/drivers/media/platform/amphion/vpu_helpers.c b/drivers/media/platform/amphion/vpu_helpers.c
index d12310af9ebc..886d5632388e 100644
--- a/drivers/media/platform/amphion/vpu_helpers.c
+++ b/drivers/media/platform/amphion/vpu_helpers.c
@@ -509,3 +509,126 @@ const char *vpu_codec_state_name(enum vpu_codec_state state)
}
return "<unknown>";
}
+
+struct codec_id_mapping {
+ u32 id;
+ u32 v4l2_id;
+};
+
+static struct codec_id_mapping h264_profiles[] = {
+ {66, V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE},
+ {77, V4L2_MPEG_VIDEO_H264_PROFILE_MAIN},
+ {88, V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED},
+ {100, V4L2_MPEG_VIDEO_H264_PROFILE_HIGH}
+};
+
+static struct codec_id_mapping h264_levels[] = {
+ {10, V4L2_MPEG_VIDEO_H264_LEVEL_1_0},
+ {9, V4L2_MPEG_VIDEO_H264_LEVEL_1B},
+ {11, V4L2_MPEG_VIDEO_H264_LEVEL_1_1},
+ {12, V4L2_MPEG_VIDEO_H264_LEVEL_1_2},
+ {13, V4L2_MPEG_VIDEO_H264_LEVEL_1_3},
+ {20, V4L2_MPEG_VIDEO_H264_LEVEL_2_0},
+ {21, V4L2_MPEG_VIDEO_H264_LEVEL_2_1},
+ {22, V4L2_MPEG_VIDEO_H264_LEVEL_2_2},
+ {30, V4L2_MPEG_VIDEO_H264_LEVEL_3_0},
+ {31, V4L2_MPEG_VIDEO_H264_LEVEL_3_1},
+ {32, V4L2_MPEG_VIDEO_H264_LEVEL_3_2},
+ {40, V4L2_MPEG_VIDEO_H264_LEVEL_4_0},
+ {41, V4L2_MPEG_VIDEO_H264_LEVEL_4_1},
+ {42, V4L2_MPEG_VIDEO_H264_LEVEL_4_2},
+ {50, V4L2_MPEG_VIDEO_H264_LEVEL_5_0},
+ {51, V4L2_MPEG_VIDEO_H264_LEVEL_5_1},
+ {52, V4L2_MPEG_VIDEO_H264_LEVEL_5_2},
+ {60, V4L2_MPEG_VIDEO_H264_LEVEL_6_0},
+ {61, V4L2_MPEG_VIDEO_H264_LEVEL_6_1},
+ {62, V4L2_MPEG_VIDEO_H264_LEVEL_6_2}
+};
+
+static struct codec_id_mapping hevc_profiles[] = {
+ {1, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN},
+ {2, V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10}
+};
+
+static struct codec_id_mapping hevc_levels[] = {
+ {30, V4L2_MPEG_VIDEO_HEVC_LEVEL_1},
+ {60, V4L2_MPEG_VIDEO_HEVC_LEVEL_2},
+ {63, V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1},
+ {90, V4L2_MPEG_VIDEO_HEVC_LEVEL_3},
+ {93, V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1},
+ {120, V4L2_MPEG_VIDEO_HEVC_LEVEL_4},
+ {123, V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1},
+ {150, V4L2_MPEG_VIDEO_HEVC_LEVEL_5},
+ {153, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1},
+ {156, V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2},
+ {180, V4L2_MPEG_VIDEO_HEVC_LEVEL_6},
+ {183, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1},
+ {186, V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2}
+};
+
+static u32 vpu_find_v4l2_id(u32 id, struct codec_id_mapping *array, u32 array_sz)
+{
+ u32 i;
+
+ if (!array || !array_sz)
+ return 0;
+
+ for (i = 0; i < array_sz; i++) {
+ if (id == array[i].id)
+ return array[i].v4l2_id;
+ }
+
+ return 0;
+}
+
+u32 vpu_get_h264_v4l2_profile(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ /*
+ * In H.264 Document section A.2.1.1 Constrained Baseline profile
+ * Conformance of a bitstream to the Constrained Baseline profile is indicated by
+ * profile_idc being equal to 66 with constraint_set1_flag being equal to 1.
+ */
+ if (hdr->profile_idc == 66 && hdr->constraint_set1_flag)
+ return V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE;
+
+ return vpu_find_v4l2_id(hdr->profile_idc, h264_profiles, ARRAY_SIZE(h264_profiles));
+}
+
+u32 vpu_get_h264_v4l2_level(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ /*
+ * In H.264 Document section 7.4.2.1.1 Sequence parameter set data semantics
+ * If profile_idc is equal to 66, 77, or 88 and level_idc is equal to 11,
+ * constraint_set3_flag equal to 1 indicates that the coded video sequence
+ * obeys all constraints specified in Annex A for level 1b
+ * and constraint_set3_flag equal to 0 indicates that the coded video sequence
+ * obeys all constraints specified in Annex A for level 1.1.
+ */
+ if (hdr->level_idc == 11 && hdr->constraint_set3_flag &&
+ (hdr->profile_idc == 66 || hdr->profile_idc == 77 || hdr->profile_idc == 88))
+ return V4L2_MPEG_VIDEO_H264_LEVEL_1B;
+
+ return vpu_find_v4l2_id(hdr->level_idc, h264_levels, ARRAY_SIZE(h264_levels));
+}
+
+u32 vpu_get_hevc_v4l2_profile(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ return vpu_find_v4l2_id(hdr->profile_idc, hevc_profiles, ARRAY_SIZE(hevc_profiles));
+}
+
+u32 vpu_get_hevc_v4l2_level(struct vpu_dec_codec_info *hdr)
+{
+ if (!hdr)
+ return 0;
+
+ return vpu_find_v4l2_id(hdr->level_idc, hevc_levels, ARRAY_SIZE(hevc_levels));
+}
diff --git a/drivers/media/platform/amphion/vpu_helpers.h b/drivers/media/platform/amphion/vpu_helpers.h
index 0eaddb07190d..76fba0d6090c 100644
--- a/drivers/media/platform/amphion/vpu_helpers.h
+++ b/drivers/media/platform/amphion/vpu_helpers.h
@@ -6,6 +6,8 @@
#ifndef _AMPHION_VPU_HELPERS_H
#define _AMPHION_VPU_HELPERS_H
+#include "vpu_defs.h"
+
struct vpu_pair {
u32 src;
u32 dst;
@@ -54,10 +56,6 @@ static inline u8 vpu_helper_read_byte(struct vpu_buffer *stream_buffer, u32 pos)
return pdata[pos % stream_buffer->length];
}
-int vpu_color_check_primaries(u32 primaries);
-int vpu_color_check_transfers(u32 transfers);
-int vpu_color_check_matrix(u32 matrix);
-int vpu_color_check_full_range(u32 full_range);
u32 vpu_color_cvrt_primaries_v2i(u32 primaries);
u32 vpu_color_cvrt_primaries_i2v(u32 primaries);
u32 vpu_color_cvrt_transfers_v2i(u32 transfers);
@@ -66,8 +64,12 @@ u32 vpu_color_cvrt_matrix_v2i(u32 matrix);
u32 vpu_color_cvrt_matrix_i2v(u32 matrix);
u32 vpu_color_cvrt_full_range_v2i(u32 full_range);
u32 vpu_color_cvrt_full_range_i2v(u32 full_range);
-int vpu_color_get_default(u32 primaries, u32 *ptransfers, u32 *pmatrix, u32 *pfull_range);
int vpu_find_dst_by_src(struct vpu_pair *pairs, u32 cnt, u32 src);
int vpu_find_src_by_dst(struct vpu_pair *pairs, u32 cnt, u32 dst);
+
+u32 vpu_get_h264_v4l2_profile(struct vpu_dec_codec_info *hdr);
+u32 vpu_get_h264_v4l2_level(struct vpu_dec_codec_info *hdr);
+u32 vpu_get_hevc_v4l2_profile(struct vpu_dec_codec_info *hdr);
+u32 vpu_get_hevc_v4l2_level(struct vpu_dec_codec_info *hdr);
#endif
diff --git a/drivers/media/platform/amphion/vpu_malone.c b/drivers/media/platform/amphion/vpu_malone.c
index feca7d4220ed..ba688566dffd 100644
--- a/drivers/media/platform/amphion/vpu_malone.c
+++ b/drivers/media/platform/amphion/vpu_malone.c
@@ -908,7 +908,8 @@ static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt,
info->frame_rate.numerator = 1000;
info->frame_rate.denominator = pkt->data[8];
info->dsp_asp_ratio = pkt->data[9];
- info->level_idc = pkt->data[10];
+ info->profile_idc = (pkt->data[10] >> 8) & 0xff;
+ info->level_idc = pkt->data[10] & 0xff;
info->bit_depth_luma = pkt->data[13];
info->bit_depth_chroma = pkt->data[14];
info->chroma_fmt = pkt->data[15];
@@ -925,6 +926,8 @@ static void vpu_malone_unpack_seq_hdr(struct vpu_rpc_event *pkt,
info->pixfmt = V4L2_PIX_FMT_NV12M_10BE_8L128;
else
info->pixfmt = V4L2_PIX_FMT_NV12M_8L128;
+ if (pkt->hdr.num > 28)
+ info->constraint_set_flags = pkt->data[28];
if (info->frame_rate.numerator && info->frame_rate.denominator) {
unsigned long n, d;
diff --git a/drivers/media/platform/amphion/vpu_mbox.c b/drivers/media/platform/amphion/vpu_mbox.c
index c2963b8deb48..b2ac8de6a2d9 100644
--- a/drivers/media/platform/amphion/vpu_mbox.c
+++ b/drivers/media/platform/amphion/vpu_mbox.c
@@ -109,7 +109,3 @@ void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data)
mbox_send_message(core->tx_data.ch, &data);
mbox_send_message(core->tx_type.ch, &type);
}
-
-void vpu_mbox_enable_rx(struct vpu_dev *dev)
-{
-}
diff --git a/drivers/media/platform/amphion/vpu_mbox.h b/drivers/media/platform/amphion/vpu_mbox.h
index 79cfd874e92b..8b7aea4f606c 100644
--- a/drivers/media/platform/amphion/vpu_mbox.h
+++ b/drivers/media/platform/amphion/vpu_mbox.h
@@ -11,6 +11,5 @@ int vpu_mbox_request(struct vpu_core *core);
void vpu_mbox_free(struct vpu_core *core);
void vpu_mbox_send_msg(struct vpu_core *core, u32 type, u32 data);
void vpu_mbox_send_type(struct vpu_core *core, u32 type);
-void vpu_mbox_enable_rx(struct vpu_dev *dev);
#endif
diff --git a/drivers/media/platform/amphion/vpu_v4l2.c b/drivers/media/platform/amphion/vpu_v4l2.c
index 45707931bc4f..74668fa362e2 100644
--- a/drivers/media/platform/amphion/vpu_v4l2.c
+++ b/drivers/media/platform/amphion/vpu_v4l2.c
@@ -501,14 +501,25 @@ static int vpu_vb2_queue_setup(struct vb2_queue *vq,
call_void_vop(inst, release);
}
+ if (V4L2_TYPE_IS_CAPTURE(vq->type))
+ call_void_vop(inst, reset_frame_store);
+
return 0;
}
static int vpu_vb2_buf_init(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct vpu_vb2_buffer *vpu_buf = to_vpu_vb2_buffer(vbuf);
+ struct vpu_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ vpu_buf->fs_id = -1;
vpu_set_buffer_state(vbuf, VPU_BUF_STATE_IDLE);
+
+ if (!inst->ops->attach_frame_store || V4L2_TYPE_IS_OUTPUT(vb->type))
+ return 0;
+
+ call_void_vop(inst, attach_frame_store, vb);
return 0;
}
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index cebcae196eec..7f1ce95cdc3f 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -57,6 +57,25 @@
#define CSI2RX_LANES_MAX 4
#define CSI2RX_STREAMS_MAX 4
+#define CSI2RX_ERROR_IRQS_REG 0x28
+#define CSI2RX_ERROR_IRQS_MASK_REG 0x2C
+
+#define CSI2RX_STREAM3_FIFO_OVERFLOW_IRQ BIT(19)
+#define CSI2RX_STREAM2_FIFO_OVERFLOW_IRQ BIT(18)
+#define CSI2RX_STREAM1_FIFO_OVERFLOW_IRQ BIT(17)
+#define CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ BIT(16)
+#define CSI2RX_FRONT_TRUNC_HDR_IRQ BIT(12)
+#define CSI2RX_PROT_TRUNCATED_PACKET_IRQ BIT(11)
+#define CSI2RX_FRONT_LP_NO_PAYLOAD_IRQ BIT(10)
+#define CSI2RX_SP_INVALID_RCVD_IRQ BIT(9)
+#define CSI2RX_DATA_ID_IRQ BIT(7)
+#define CSI2RX_HEADER_CORRECTED_ECC_IRQ BIT(6)
+#define CSI2RX_HEADER_ECC_IRQ BIT(5)
+#define CSI2RX_PAYLOAD_CRC_IRQ BIT(4)
+
+#define CSI2RX_ECC_ERRORS GENMASK(7, 4)
+#define CSI2RX_PACKET_ERRORS GENMASK(12, 9)
+
enum csi2rx_pads {
CSI2RX_PAD_SINK,
CSI2RX_PAD_SOURCE_STREAM0,
@@ -71,9 +90,32 @@ struct csi2rx_fmt {
u8 bpp;
};
+struct csi2rx_event {
+ u32 mask;
+ const char *name;
+};
+
+static const struct csi2rx_event csi2rx_events[] = {
+ { CSI2RX_STREAM3_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 3 FIFO detected" },
+ { CSI2RX_STREAM2_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 2 FIFO detected" },
+ { CSI2RX_STREAM1_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 1 FIFO detected" },
+ { CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ, "Overflow of the Stream 0 FIFO detected" },
+ { CSI2RX_FRONT_TRUNC_HDR_IRQ, "A truncated header [short or long] has been received" },
+ { CSI2RX_PROT_TRUNCATED_PACKET_IRQ, "A truncated long packet has been received" },
+ { CSI2RX_FRONT_LP_NO_PAYLOAD_IRQ, "A truncated long packet has been received. No payload" },
+ { CSI2RX_SP_INVALID_RCVD_IRQ, "A reserved or invalid short packet has been received" },
+ { CSI2RX_DATA_ID_IRQ, "Data ID error in the header packet" },
+ { CSI2RX_HEADER_CORRECTED_ECC_IRQ, "ECC error detected and corrected" },
+ { CSI2RX_HEADER_ECC_IRQ, "Unrecoverable ECC error" },
+ { CSI2RX_PAYLOAD_CRC_IRQ, "CRC error" },
+};
+
+#define CSI2RX_NUM_EVENTS ARRAY_SIZE(csi2rx_events)
+
struct csi2rx_priv {
struct device *dev;
unsigned int count;
+ int error_irq;
/*
* Used to prevent race conditions between multiple,
@@ -95,6 +137,7 @@ struct csi2rx_priv {
u8 max_lanes;
u8 max_streams;
bool has_internal_dphy;
+ u32 events[CSI2RX_NUM_EVENTS];
struct v4l2_subdev subdev;
struct v4l2_async_notifier notifier;
@@ -124,6 +167,54 @@ static const struct csi2rx_fmt formats[] = {
{ .code = MEDIA_BUS_FMT_BGR888_1X24, .bpp = 24, },
};
+static void csi2rx_configure_error_irq_mask(void __iomem *base,
+ struct csi2rx_priv *csi2rx)
+{
+ u32 error_irq_mask = 0;
+
+ error_irq_mask |= CSI2RX_ECC_ERRORS;
+ error_irq_mask |= CSI2RX_PACKET_ERRORS;
+
+ /*
+ * Iterate through all source pads and check if they are linked
+ * to an active remote pad. If an active remote pad is found,
+ * calculate the corresponding bit position and set it in
+ * mask, enabling the stream overflow error in the mask.
+ */
+ for (int i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) {
+ struct media_pad *remote_pad;
+
+ remote_pad = media_pad_remote_pad_first(&csi2rx->pads[i]);
+ if (remote_pad) {
+ int pad = i - CSI2RX_PAD_SOURCE_STREAM0;
+ u32 bit_mask = CSI2RX_STREAM0_FIFO_OVERFLOW_IRQ << pad;
+
+ error_irq_mask |= bit_mask;
+ }
+ }
+
+ writel(error_irq_mask, base + CSI2RX_ERROR_IRQS_MASK_REG);
+}
+
+static irqreturn_t csi2rx_irq_handler(int irq, void *dev_id)
+{
+ struct csi2rx_priv *csi2rx = dev_id;
+ int i;
+ u32 error_status, error_mask;
+
+ error_status = readl(csi2rx->base + CSI2RX_ERROR_IRQS_REG);
+ error_mask = readl(csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG);
+
+ for (i = 0; i < CSI2RX_NUM_EVENTS; i++)
+ if ((error_status & csi2rx_events[i].mask) &&
+ (error_mask & csi2rx_events[i].mask))
+ csi2rx->events[i]++;
+
+ writel(error_status, csi2rx->base + CSI2RX_ERROR_IRQS_REG);
+
+ return IRQ_HANDLED;
+}
+
static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code)
{
unsigned int i;
@@ -220,6 +311,9 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
reset_control_deassert(csi2rx->p_rst);
csi2rx_reset(csi2rx);
+ if (csi2rx->error_irq >= 0)
+ csi2rx_configure_error_irq_mask(csi2rx->base, csi2rx);
+
reg = csi2rx->num_lanes << 8;
for (i = 0; i < csi2rx->num_lanes; i++) {
reg |= CSI2RX_STATIC_CFG_DLANE_MAP(i, csi2rx->lanes[i]);
@@ -332,6 +426,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
reset_control_assert(csi2rx->sys_rst);
clk_disable_unprepare(csi2rx->sys_clk);
+ writel(0, csi2rx->base + CSI2RX_ERROR_IRQS_MASK_REG);
+
for (i = 0; i < csi2rx->max_streams; i++) {
writel(CSI2RX_STREAM_CTRL_STOP,
csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
@@ -363,6 +459,21 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
}
}
+static int csi2rx_log_status(struct v4l2_subdev *sd)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(sd);
+ unsigned int i;
+
+ for (i = 0; i < CSI2RX_NUM_EVENTS; i++) {
+ if (csi2rx->events[i])
+ dev_info(csi2rx->dev, "%s events: %d\n",
+ csi2rx_events[i].name,
+ csi2rx->events[i]);
+ }
+
+ return 0;
+}
+
static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
{
struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
@@ -468,7 +579,12 @@ static const struct v4l2_subdev_video_ops csi2rx_video_ops = {
.s_stream = csi2rx_s_stream,
};
+static const struct v4l2_subdev_core_ops csi2rx_core_ops = {
+ .log_status = csi2rx_log_status,
+};
+
static const struct v4l2_subdev_ops csi2rx_subdev_ops = {
+ .core = &csi2rx_core_ops,
.video = &csi2rx_video_ops,
.pad = &csi2rx_pad_ops,
};
@@ -705,6 +821,21 @@ static int csi2rx_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup;
+ csi2rx->error_irq = platform_get_irq_byname_optional(pdev, "error_irq");
+
+ if (csi2rx->error_irq < 0) {
+ dev_dbg(csi2rx->dev, "Optional interrupt not defined, proceeding without it\n");
+ } else {
+ ret = devm_request_irq(csi2rx->dev, csi2rx->error_irq,
+ csi2rx_irq_handler, 0,
+ dev_name(&pdev->dev), csi2rx);
+ if (ret) {
+ dev_err(csi2rx->dev,
+ "Unable to request interrupt: %d\n", ret);
+ goto err_cleanup;
+ }
+ }
+
ret = v4l2_subdev_init_finalize(&csi2rx->subdev);
if (ret)
goto err_cleanup;
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 5c17bc58181e..8681dd193033 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -598,6 +598,27 @@ static void _bswap16(u16 *a)
*a = ((*a & 0x00FF) << 8) | ((*a & 0xFF00) >> 8);
}
+static dma_addr_t mxc_jpeg_get_plane_dma_addr(struct vb2_buffer *buf, unsigned int plane_no)
+{
+ if (plane_no >= buf->num_planes)
+ return 0;
+ return vb2_dma_contig_plane_dma_addr(buf, plane_no) + buf->planes[plane_no].data_offset;
+}
+
+static void *mxc_jpeg_get_plane_vaddr(struct vb2_buffer *buf, unsigned int plane_no)
+{
+ if (plane_no >= buf->num_planes)
+ return NULL;
+ return vb2_plane_vaddr(buf, plane_no) + buf->planes[plane_no].data_offset;
+}
+
+static unsigned long mxc_jpeg_get_plane_payload(struct vb2_buffer *buf, unsigned int plane_no)
+{
+ if (plane_no >= buf->num_planes)
+ return 0;
+ return vb2_get_plane_payload(buf, plane_no) - buf->planes[plane_no].data_offset;
+}
+
static void print_mxc_buf(struct mxc_jpeg_dev *jpeg, struct vb2_buffer *buf,
unsigned long len)
{
@@ -610,11 +631,11 @@ static void print_mxc_buf(struct mxc_jpeg_dev *jpeg, struct vb2_buffer *buf,
return;
for (plane_no = 0; plane_no < buf->num_planes; plane_no++) {
- payload = vb2_get_plane_payload(buf, plane_no);
+ payload = mxc_jpeg_get_plane_payload(buf, plane_no);
if (len == 0)
len = payload;
- dma_addr = vb2_dma_contig_plane_dma_addr(buf, plane_no);
- vaddr = vb2_plane_vaddr(buf, plane_no);
+ dma_addr = mxc_jpeg_get_plane_dma_addr(buf, plane_no);
+ vaddr = mxc_jpeg_get_plane_vaddr(buf, plane_no);
v4l2_dbg(3, debug, &jpeg->v4l2_dev,
"plane %d (vaddr=%p dma_addr=%x payload=%ld):",
plane_no, vaddr, dma_addr, payload);
@@ -712,16 +733,15 @@ static void mxc_jpeg_addrs(struct mxc_jpeg_desc *desc,
struct mxc_jpeg_q_data *q_data;
q_data = mxc_jpeg_get_q_data(ctx, raw_buf->type);
- desc->buf_base0 = vb2_dma_contig_plane_dma_addr(raw_buf, 0);
+ desc->buf_base0 = mxc_jpeg_get_plane_dma_addr(raw_buf, 0);
desc->buf_base1 = 0;
if (img_fmt == STM_CTRL_IMAGE_FORMAT(MXC_JPEG_YUV420)) {
if (raw_buf->num_planes == 2)
- desc->buf_base1 = vb2_dma_contig_plane_dma_addr(raw_buf, 1);
+ desc->buf_base1 = mxc_jpeg_get_plane_dma_addr(raw_buf, 1);
else
desc->buf_base1 = desc->buf_base0 + q_data->sizeimage[0];
}
- desc->stm_bufbase = vb2_dma_contig_plane_dma_addr(jpeg_buf, 0) +
- offset;
+ desc->stm_bufbase = mxc_jpeg_get_plane_dma_addr(jpeg_buf, 0) + offset;
}
static bool mxc_jpeg_is_extended_sequential(const struct mxc_jpeg_fmt *fmt)
@@ -1029,8 +1049,8 @@ static irqreturn_t mxc_jpeg_dec_irq(int irq, void *priv)
vb2_set_plane_payload(&dst_buf->vb2_buf, 1, payload);
}
dev_dbg(dev, "Decoding finished, payload size: %ld + %ld\n",
- vb2_get_plane_payload(&dst_buf->vb2_buf, 0),
- vb2_get_plane_payload(&dst_buf->vb2_buf, 1));
+ mxc_jpeg_get_plane_payload(&dst_buf->vb2_buf, 0),
+ mxc_jpeg_get_plane_payload(&dst_buf->vb2_buf, 1));
}
/* short preview of the results */
@@ -1889,8 +1909,8 @@ static int mxc_jpeg_parse(struct mxc_jpeg_ctx *ctx, struct vb2_buffer *vb)
struct mxc_jpeg_sof *psof = NULL;
struct mxc_jpeg_sos *psos = NULL;
struct mxc_jpeg_src_buf *jpeg_src_buf = vb2_to_mxc_buf(vb);
- u8 *src_addr = (u8 *)vb2_plane_vaddr(vb, 0);
- u32 size = vb2_get_plane_payload(vb, 0);
+ u8 *src_addr = (u8 *)mxc_jpeg_get_plane_vaddr(vb, 0);
+ u32 size = mxc_jpeg_get_plane_payload(vb, 0);
int ret;
memset(&header, 0, sizeof(header));
@@ -2027,6 +2047,11 @@ static int mxc_jpeg_buf_prepare(struct vb2_buffer *vb)
i, vb2_plane_size(vb, i), sizeimage);
return -EINVAL;
}
+ if (!IS_ALIGNED(mxc_jpeg_get_plane_dma_addr(vb, i), MXC_JPEG_ADDR_ALIGNMENT)) {
+ dev_err(dev, "planes[%d] address is not %d aligned\n",
+ i, MXC_JPEG_ADDR_ALIGNMENT);
+ return -EINVAL;
+ }
}
if (V4L2_TYPE_IS_CAPTURE(vb->vb2_queue->type)) {
vb2_set_plane_payload(vb, 0, 0);
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
index fdde45f7e163..44e46face6d1 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.h
@@ -30,6 +30,7 @@
#define MXC_JPEG_MAX_PLANES 2
#define MXC_JPEG_PATTERN_WIDTH 128
#define MXC_JPEG_PATTERN_HEIGHT 64
+#define MXC_JPEG_ADDR_ALIGNMENT 16
enum mxc_jpeg_enc_state {
MXC_JPEG_ENCODING = 0, /* jpeg encode phase */
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index d060eadebc7a..2beb5f43c2c0 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -28,6 +28,7 @@
#include <linux/reset.h>
#include <linux/spinlock.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
@@ -229,25 +230,6 @@
#define DEFAULT_SCLK_CSIS_FREQ 166000000UL
-/* MIPI CSI-2 Data Types */
-#define MIPI_CSI2_DATA_TYPE_YUV420_8 0x18
-#define MIPI_CSI2_DATA_TYPE_YUV420_10 0x19
-#define MIPI_CSI2_DATA_TYPE_LE_YUV420_8 0x1a
-#define MIPI_CSI2_DATA_TYPE_CS_YUV420_8 0x1c
-#define MIPI_CSI2_DATA_TYPE_CS_YUV420_10 0x1d
-#define MIPI_CSI2_DATA_TYPE_YUV422_8 0x1e
-#define MIPI_CSI2_DATA_TYPE_YUV422_10 0x1f
-#define MIPI_CSI2_DATA_TYPE_RGB565 0x22
-#define MIPI_CSI2_DATA_TYPE_RGB666 0x23
-#define MIPI_CSI2_DATA_TYPE_RGB888 0x24
-#define MIPI_CSI2_DATA_TYPE_RAW6 0x28
-#define MIPI_CSI2_DATA_TYPE_RAW7 0x29
-#define MIPI_CSI2_DATA_TYPE_RAW8 0x2a
-#define MIPI_CSI2_DATA_TYPE_RAW10 0x2b
-#define MIPI_CSI2_DATA_TYPE_RAW12 0x2c
-#define MIPI_CSI2_DATA_TYPE_RAW14 0x2d
-#define MIPI_CSI2_DATA_TYPE_USER(x) (0x30 + (x))
-
struct mipi_csis_event {
bool debug;
u32 mask;
@@ -357,116 +339,116 @@ static const struct csis_pix_format mipi_csis_formats[] = {
{
.code = MEDIA_BUS_FMT_UYVY8_1X16,
.output = MEDIA_BUS_FMT_UYVY8_1X16,
- .data_type = MIPI_CSI2_DATA_TYPE_YUV422_8,
+ .data_type = MIPI_CSI2_DT_YUV422_8B,
.width = 16,
},
/* RGB formats. */
{
.code = MEDIA_BUS_FMT_RGB565_1X16,
.output = MEDIA_BUS_FMT_RGB565_1X16,
- .data_type = MIPI_CSI2_DATA_TYPE_RGB565,
+ .data_type = MIPI_CSI2_DT_RGB565,
.width = 16,
}, {
.code = MEDIA_BUS_FMT_BGR888_1X24,
.output = MEDIA_BUS_FMT_RGB888_1X24,
- .data_type = MIPI_CSI2_DATA_TYPE_RGB888,
+ .data_type = MIPI_CSI2_DT_RGB888,
.width = 24,
},
/* RAW (Bayer and greyscale) formats. */
{
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
.output = MEDIA_BUS_FMT_SBGGR8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
.output = MEDIA_BUS_FMT_SGBRG8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
.output = MEDIA_BUS_FMT_SGRBG8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
.output = MEDIA_BUS_FMT_SRGGB8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_Y8_1X8,
.output = MEDIA_BUS_FMT_Y8_1X8,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}, {
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
.output = MEDIA_BUS_FMT_SBGGR10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SGBRG10_1X10,
.output = MEDIA_BUS_FMT_SGBRG10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SGRBG10_1X10,
.output = MEDIA_BUS_FMT_SGRBG10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SRGGB10_1X10,
.output = MEDIA_BUS_FMT_SRGGB10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_Y10_1X10,
.output = MEDIA_BUS_FMT_Y10_1X10,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW10,
+ .data_type = MIPI_CSI2_DT_RAW10,
.width = 10,
}, {
.code = MEDIA_BUS_FMT_SBGGR12_1X12,
.output = MEDIA_BUS_FMT_SBGGR12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SGBRG12_1X12,
.output = MEDIA_BUS_FMT_SGBRG12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SGRBG12_1X12,
.output = MEDIA_BUS_FMT_SGRBG12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SRGGB12_1X12,
.output = MEDIA_BUS_FMT_SRGGB12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_Y12_1X12,
.output = MEDIA_BUS_FMT_Y12_1X12,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW12,
+ .data_type = MIPI_CSI2_DT_RAW12,
.width = 12,
}, {
.code = MEDIA_BUS_FMT_SBGGR14_1X14,
.output = MEDIA_BUS_FMT_SBGGR14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
}, {
.code = MEDIA_BUS_FMT_SGBRG14_1X14,
.output = MEDIA_BUS_FMT_SGBRG14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
}, {
.code = MEDIA_BUS_FMT_SGRBG14_1X14,
.output = MEDIA_BUS_FMT_SGRBG14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
}, {
.code = MEDIA_BUS_FMT_SRGGB14_1X14,
.output = MEDIA_BUS_FMT_SRGGB14_1X14,
- .data_type = MIPI_CSI2_DATA_TYPE_RAW14,
+ .data_type = MIPI_CSI2_DT_RAW14,
.width = 14,
},
/* JPEG */
@@ -494,7 +476,7 @@ static const struct csis_pix_format mipi_csis_formats[] = {
* SoC that can support quad pixel mode, this will have to be
* revisited.
*/
- .data_type = MIPI_CSI2_DATA_TYPE_RAW8,
+ .data_type = MIPI_CSI2_DT_RAW8,
.width = 8,
}
};
@@ -583,7 +565,7 @@ static void __mipi_csis_set_format(struct mipi_csis_device *csis,
*
* TODO: Verify which other formats require DUAL (or QUAD) modes.
*/
- if (csis_fmt->data_type == MIPI_CSI2_DATA_TYPE_YUV422_8)
+ if (csis_fmt->data_type == MIPI_CSI2_DT_YUV422_8B)
val |= MIPI_CSIS_ISPCFG_PIXEL_MODE_DUAL;
val |= MIPI_CSIS_ISPCFG_FMT(csis_fmt->data_type);
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 1e79b1211b60..981648a03113 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -3,6 +3,7 @@
* Copyright 2019-2020 NXP
*/
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/errno.h>
@@ -245,26 +246,41 @@ static void mxc_isi_v4l2_cleanup(struct mxc_isi_dev *isi)
/* Panic will assert when the buffers are 50% full */
-/* For i.MX8QXP C0 and i.MX8MN ISI IER version */
+/* For i.MX8MN ISI IER version */
static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v1 = {
- .oflw_y_buf_en = { .offset = 19, .mask = 0x80000 },
- .oflw_u_buf_en = { .offset = 21, .mask = 0x200000 },
- .oflw_v_buf_en = { .offset = 23, .mask = 0x800000 },
+ .oflw_y_buf_en = { .mask = BIT(19) },
+ .oflw_u_buf_en = { .mask = BIT(21) },
+ .oflw_v_buf_en = { .mask = BIT(23) },
- .panic_y_buf_en = {.offset = 20, .mask = 0x100000 },
- .panic_u_buf_en = {.offset = 22, .mask = 0x400000 },
- .panic_v_buf_en = {.offset = 24, .mask = 0x1000000 },
+ .panic_y_buf_en = { .mask = BIT(20) },
+ .panic_u_buf_en = { .mask = BIT(22) },
+ .panic_v_buf_en = { .mask = BIT(24) },
};
-/* For i.MX8MP ISI IER version */
+/* For i.MX8QXP C0 and i.MX8MP ISI IER version */
static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_v2 = {
- .oflw_y_buf_en = { .offset = 18, .mask = 0x40000 },
- .oflw_u_buf_en = { .offset = 20, .mask = 0x100000 },
- .oflw_v_buf_en = { .offset = 22, .mask = 0x400000 },
+ .oflw_y_buf_en = { .mask = BIT(18) },
+ .oflw_u_buf_en = { .mask = BIT(20) },
+ .oflw_v_buf_en = { .mask = BIT(22) },
- .panic_y_buf_en = {.offset = 19, .mask = 0x80000 },
- .panic_u_buf_en = {.offset = 21, .mask = 0x200000 },
- .panic_v_buf_en = {.offset = 23, .mask = 0x800000 },
+ .panic_y_buf_en = { .mask = BIT(19) },
+ .panic_u_buf_en = { .mask = BIT(21) },
+ .panic_v_buf_en = { .mask = BIT(23) },
+};
+
+/* For i.MX8QM ISI IER version */
+static const struct mxc_isi_ier_reg mxc_imx8_isi_ier_qm = {
+ .oflw_y_buf_en = { .mask = BIT(16) },
+ .oflw_u_buf_en = { .mask = BIT(19) },
+ .oflw_v_buf_en = { .mask = BIT(22) },
+
+ .excs_oflw_y_buf_en = { .mask = BIT(17) },
+ .excs_oflw_u_buf_en = { .mask = BIT(20) },
+ .excs_oflw_v_buf_en = { .mask = BIT(23) },
+
+ .panic_y_buf_en = { .mask = BIT(18) },
+ .panic_u_buf_en = { .mask = BIT(21) },
+ .panic_v_buf_en = { .mask = BIT(24) },
};
/* Panic will assert when the buffers are 50% full */
@@ -274,11 +290,6 @@ static const struct mxc_isi_set_thd mxc_imx8_isi_thd_v1 = {
.panic_set_thd_v = { .mask = 0xf0000, .offset = 16, .threshold = 0x7 },
};
-static const struct clk_bulk_data mxc_imx8mn_clks[] = {
- { .id = "axi" },
- { .id = "apb" },
-};
-
static const struct mxc_isi_plat_data mxc_imx8mn_data = {
.model = MXC_ISI_IMX8MN,
.num_ports = 1,
@@ -286,8 +297,6 @@ static const struct mxc_isi_plat_data mxc_imx8mn_data = {
.reg_offset = 0,
.ier_reg = &mxc_imx8_isi_ier_v1,
.set_thd = &mxc_imx8_isi_thd_v1,
- .clks = mxc_imx8mn_clks,
- .num_clks = ARRAY_SIZE(mxc_imx8mn_clks),
.buf_active_reverse = false,
.gasket_ops = &mxc_imx8_gasket_ops,
.has_36bit_dma = false,
@@ -300,8 +309,6 @@ static const struct mxc_isi_plat_data mxc_imx8mp_data = {
.reg_offset = 0x2000,
.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,
.gasket_ops = &mxc_imx8_gasket_ops,
.has_36bit_dma = true,
@@ -314,8 +321,6 @@ static const struct mxc_isi_plat_data mxc_imx8ulp_data = {
.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,
};
@@ -327,13 +332,33 @@ static const struct mxc_isi_plat_data mxc_imx93_data = {
.reg_offset = 0,
.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,
.gasket_ops = &mxc_imx93_gasket_ops,
.has_36bit_dma = false,
};
+static const struct mxc_isi_plat_data mxc_imx8qm_data = {
+ .model = MXC_ISI_IMX8QM,
+ .num_ports = 5,
+ .num_channels = 8,
+ .reg_offset = 0x10000,
+ .ier_reg = &mxc_imx8_isi_ier_qm,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .buf_active_reverse = true,
+ .has_36bit_dma = false,
+};
+
+static const struct mxc_isi_plat_data mxc_imx8qxp_data = {
+ .model = MXC_ISI_IMX8QXP,
+ .num_ports = 5,
+ .num_channels = 6,
+ .reg_offset = 0x10000,
+ .ier_reg = &mxc_imx8_isi_ier_v2,
+ .set_thd = &mxc_imx8_isi_thd_v1,
+ .buf_active_reverse = true,
+ .has_36bit_dma = false,
+};
+
/* -----------------------------------------------------------------------------
* Power management
*/
@@ -385,7 +410,7 @@ static int mxc_isi_runtime_suspend(struct device *dev)
{
struct mxc_isi_dev *isi = dev_get_drvdata(dev);
- clk_bulk_disable_unprepare(isi->pdata->num_clks, isi->clks);
+ clk_bulk_disable_unprepare(isi->num_clks, isi->clks);
return 0;
}
@@ -395,7 +420,7 @@ static int mxc_isi_runtime_resume(struct device *dev)
struct mxc_isi_dev *isi = dev_get_drvdata(dev);
int ret;
- ret = clk_bulk_prepare_enable(isi->pdata->num_clks, isi->clks);
+ ret = clk_bulk_prepare_enable(isi->num_clks, isi->clks);
if (ret) {
dev_err(dev, "Failed to enable clocks (%d)\n", ret);
return ret;
@@ -413,27 +438,6 @@ static const struct dev_pm_ops mxc_isi_pm_ops = {
* Probe, remove & driver
*/
-static int mxc_isi_clk_get(struct mxc_isi_dev *isi)
-{
- unsigned int size = isi->pdata->num_clks
- * sizeof(*isi->clks);
- int ret;
-
- isi->clks = devm_kmemdup(isi->dev, isi->pdata->clks, size, GFP_KERNEL);
- if (!isi->clks)
- return -ENOMEM;
-
- ret = devm_clk_bulk_get(isi->dev, isi->pdata->num_clks,
- isi->clks);
- if (ret < 0) {
- dev_err(isi->dev, "Failed to acquire clocks: %d\n",
- ret);
- return ret;
- }
-
- return 0;
-}
-
static int mxc_isi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -456,34 +460,25 @@ static int mxc_isi_probe(struct platform_device *pdev)
if (!isi->pipes)
return -ENOMEM;
- ret = mxc_isi_clk_get(isi);
- if (ret < 0) {
- dev_err(dev, "Failed to get clocks\n");
- return ret;
- }
+ isi->num_clks = devm_clk_bulk_get_all(dev, &isi->clks);
+ if (isi->num_clks < 0)
+ return dev_err_probe(dev, isi->num_clks, "Failed to get clocks\n");
isi->regs = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(isi->regs)) {
- dev_err(dev, "Failed to get ISI register map\n");
- return PTR_ERR(isi->regs);
- }
+ if (IS_ERR(isi->regs))
+ return dev_err_probe(dev, PTR_ERR(isi->regs),
+ "Failed to get ISI register map\n");
if (isi->pdata->gasket_ops) {
isi->gasket = syscon_regmap_lookup_by_phandle(dev->of_node,
"fsl,blk-ctrl");
- if (IS_ERR(isi->gasket)) {
- ret = PTR_ERR(isi->gasket);
- dev_err(dev, "failed to get gasket: %d\n", ret);
- return ret;
- }
+ if (IS_ERR(isi->gasket))
+ return dev_err_probe(dev, PTR_ERR(isi->gasket),
+ "failed to get gasket\n");
}
dma_size = isi->pdata->has_36bit_dma ? 36 : 32;
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
- if (ret) {
- dev_err(dev, "failed to set DMA mask\n");
- return ret;
- }
+ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_size));
pm_runtime_enable(dev);
@@ -541,6 +536,8 @@ 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,imx8qm-isi", .data = &mxc_imx8qm_data },
+ { .compatible = "fsl,imx8qxp-isi", .data = &mxc_imx8qxp_data },
{ .compatible = "fsl,imx8ulp-isi", .data = &mxc_imx8ulp_data },
{ .compatible = "fsl,imx93-isi", .data = &mxc_imx93_data },
{ /* sentinel */ },
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 9c7fe9e5f941..206995bedca4 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -114,7 +114,6 @@ struct mxc_isi_buffer {
};
struct mxc_isi_reg {
- u32 offset;
u32 mask;
};
@@ -158,6 +157,8 @@ struct mxc_gasket_ops {
enum model {
MXC_ISI_IMX8MN,
MXC_ISI_IMX8MP,
+ MXC_ISI_IMX8QM,
+ MXC_ISI_IMX8QXP,
MXC_ISI_IMX8ULP,
MXC_ISI_IMX93,
};
@@ -170,8 +171,6 @@ struct mxc_isi_plat_data {
const struct mxc_isi_ier_reg *ier_reg;
const struct mxc_isi_set_thd *set_thd;
const struct mxc_gasket_ops *gasket_ops;
- const struct clk_bulk_data *clks;
- unsigned int num_clks;
bool buf_active_reverse;
bool has_36bit_dma;
};
@@ -283,6 +282,7 @@ struct mxc_isi_dev {
void __iomem *regs;
struct clk_bulk_data *clks;
+ int num_clks;
struct regmap *gasket;
struct mxc_isi_crossbar crossbar;
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
index 93a55c97cd17..ede6cc74c023 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -188,11 +188,12 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd,
* Create a 1:1 mapping between pixel link inputs and outputs to
* pipelines by default.
*/
- routes = kcalloc(xbar->num_sources, sizeof(*routes), GFP_KERNEL);
+ routing.num_routes = min(xbar->num_sinks - 1, xbar->num_sources);
+ routes = kcalloc(routing.num_routes, sizeof(*routes), GFP_KERNEL);
if (!routes)
return -ENOMEM;
- for (i = 0; i < xbar->num_sources; ++i) {
+ for (i = 0; i < routing.num_routes; ++i) {
struct v4l2_subdev_route *route = &routes[i];
route->sink_pad = i;
@@ -200,7 +201,6 @@ static int mxc_isi_crossbar_init_state(struct v4l2_subdev *sd,
route->flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE;
}
- routing.num_routes = xbar->num_sources;
routing.routes = routes;
ret = __mxc_isi_crossbar_set_routing(sd, state, &routing);
@@ -352,9 +352,8 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
sink_streams);
if (ret) {
dev_err(xbar->isi->dev,
- "failed to %s streams 0x%llx on '%s':%u: %d\n",
- "enable", sink_streams, remote_sd->name,
- remote_pad, ret);
+ "failed to enable streams 0x%llx on '%s':%u: %d\n",
+ sink_streams, remote_sd->name, remote_pad, ret);
mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
return ret;
}
@@ -392,9 +391,8 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
sink_streams);
if (ret)
dev_err(xbar->isi->dev,
- "failed to %s streams 0x%llx on '%s':%u: %d\n",
- "disable", sink_streams, remote_sd->name,
- remote_pad, ret);
+ "failed to disable streams 0x%llx on '%s':%u: %d\n",
+ sink_streams, remote_sd->name, remote_pad, ret);
mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
}
@@ -453,7 +451,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
* the memory input.
*/
xbar->num_sinks = isi->pdata->num_ports + 1;
- xbar->num_sources = isi->pdata->num_ports;
+ xbar->num_sources = isi->pdata->num_channels;
num_pads = xbar->num_sinks + xbar->num_sources;
xbar->pads = kcalloc(num_pads, sizeof(*xbar->pads), GFP_KERNEL);
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index a8bcf60e2f37..3a4645f59a44 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -5,6 +5,7 @@
* Copyright (C) 2021 Purism SPC
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
@@ -62,6 +63,8 @@
#define CSI2RX_CFG_VID_P_FIFO_SEND_LEVEL 0x188
#define CSI2RX_CFG_DISABLE_PAYLOAD_1 0x130
+struct csi_state;
+
enum {
ST_POWERED = 1,
ST_STREAMING = 2,
@@ -83,11 +86,11 @@ static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
#define CSI2_NUM_CLKS ARRAY_SIZE(imx8mq_mipi_csi_clk_id)
-#define GPR_CSI2_1_RX_ENABLE BIT(13)
-#define GPR_CSI2_1_VID_INTFC_ENB BIT(12)
-#define GPR_CSI2_1_HSEL BIT(10)
-#define GPR_CSI2_1_CONT_CLK_MODE BIT(8)
-#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2)
+struct imx8mq_plat_data {
+ int (*enable)(struct csi_state *state, u32 hs_settle);
+ void (*disable)(struct csi_state *state);
+ bool use_reg_csr;
+};
/*
* The send level configures the number of entries that must accumulate in
@@ -106,6 +109,7 @@ static const char * const imx8mq_mipi_csi_clk_id[CSI2_NUM_CLKS] = {
struct csi_state {
struct device *dev;
+ const struct imx8mq_plat_data *pdata;
void __iomem *regs;
struct clk_bulk_data clks[CSI2_NUM_CLKS];
struct reset_control *rst;
@@ -137,6 +141,123 @@ struct csi2_pix_format {
u8 width;
};
+/* -----------------------------------------------------------------------------
+ * i.MX8MQ GPR
+ */
+
+#define GPR_CSI2_1_RX_ENABLE BIT(13)
+#define GPR_CSI2_1_VID_INTFC_ENB BIT(12)
+#define GPR_CSI2_1_HSEL BIT(10)
+#define GPR_CSI2_1_CONT_CLK_MODE BIT(8)
+#define GPR_CSI2_1_S_PRG_RXHS_SETTLE(x) (((x) & 0x3f) << 2)
+
+static int imx8mq_gpr_enable(struct csi_state *state, u32 hs_settle)
+{
+ regmap_update_bits(state->phy_gpr,
+ state->phy_gpr_reg,
+ 0x3fff,
+ GPR_CSI2_1_RX_ENABLE |
+ GPR_CSI2_1_VID_INTFC_ENB |
+ GPR_CSI2_1_HSEL |
+ GPR_CSI2_1_CONT_CLK_MODE |
+ GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle));
+
+ return 0;
+}
+
+static const struct imx8mq_plat_data imx8mq_data = {
+ .enable = imx8mq_gpr_enable,
+};
+
+/* -----------------------------------------------------------------------------
+ * i.MX8QXP
+ */
+
+#define CSI2SS_PL_CLK_INTERVAL_US 100
+#define CSI2SS_PL_CLK_TIMEOUT_US 100000
+
+#define CSI2SS_PLM_CTRL 0x0
+#define CSI2SS_PLM_CTRL_ENABLE_PL BIT(0)
+#define CSI2SS_PLM_CTRL_VSYNC_OVERRIDE BIT(9)
+#define CSI2SS_PLM_CTRL_HSYNC_OVERRIDE BIT(10)
+#define CSI2SS_PLM_CTRL_VALID_OVERRIDE BIT(11)
+#define CSI2SS_PLM_CTRL_POLARITY_HIGH BIT(12)
+#define CSI2SS_PLM_CTRL_PL_CLK_RUN BIT(31)
+
+#define CSI2SS_PHY_CTRL 0x4
+#define CSI2SS_PHY_CTRL_RX_ENABLE BIT(0)
+#define CSI2SS_PHY_CTRL_AUTO_PD_EN BIT(1)
+#define CSI2SS_PHY_CTRL_DDRCLK_EN BIT(2)
+#define CSI2SS_PHY_CTRL_CONT_CLK_MODE BIT(3)
+#define CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK GENMASK(9, 4)
+#define CSI2SS_PHY_CTRL_RTERM_SEL BIT(21)
+#define CSI2SS_PHY_CTRL_PD BIT(22)
+
+#define CSI2SS_DATA_TYPE_DISABLE_BF 0x38
+#define CSI2SS_DATA_TYPE_DISABLE_BF_MASK GENMASK(23, 0)
+
+#define CSI2SS_CTRL_CLK_RESET 0x44
+#define CSI2SS_CTRL_CLK_RESET_EN BIT(0)
+
+static int imx8qxp_gpr_enable(struct csi_state *state, u32 hs_settle)
+{
+ int ret;
+ u32 val;
+
+ /* Clear format */
+ regmap_clear_bits(state->phy_gpr, CSI2SS_DATA_TYPE_DISABLE_BF,
+ CSI2SS_DATA_TYPE_DISABLE_BF_MASK);
+
+ regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);
+
+ regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL,
+ FIELD_PREP(CSI2SS_PHY_CTRL_RX_HS_SETTLE_MASK, hs_settle) |
+ CSI2SS_PHY_CTRL_RX_ENABLE | CSI2SS_PHY_CTRL_DDRCLK_EN |
+ CSI2SS_PHY_CTRL_CONT_CLK_MODE | CSI2SS_PHY_CTRL_PD |
+ CSI2SS_PHY_CTRL_RTERM_SEL | CSI2SS_PHY_CTRL_AUTO_PD_EN);
+
+ ret = regmap_read_poll_timeout(state->phy_gpr, CSI2SS_PLM_CTRL,
+ val, !(val & CSI2SS_PLM_CTRL_PL_CLK_RUN),
+ CSI2SS_PL_CLK_INTERVAL_US,
+ CSI2SS_PL_CLK_TIMEOUT_US);
+
+ if (ret) {
+ dev_err(state->dev, "Timeout waiting for Pixel-Link clock\n");
+ return ret;
+ }
+
+ /* Enable Pixel link Master */
+ regmap_set_bits(state->phy_gpr, CSI2SS_PLM_CTRL,
+ CSI2SS_PLM_CTRL_ENABLE_PL | CSI2SS_PLM_CTRL_VALID_OVERRIDE);
+
+ /* PHY Enable */
+ regmap_clear_bits(state->phy_gpr, CSI2SS_PHY_CTRL,
+ CSI2SS_PHY_CTRL_PD | CSI2SS_PLM_CTRL_POLARITY_HIGH);
+
+ /* Release Reset */
+ regmap_set_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET, CSI2SS_CTRL_CLK_RESET_EN);
+
+ return ret;
+}
+
+static void imx8qxp_gpr_disable(struct csi_state *state)
+{
+ /* Disable Pixel Link */
+ regmap_write(state->phy_gpr, CSI2SS_PLM_CTRL, 0x0);
+
+ /* Disable PHY */
+ regmap_write(state->phy_gpr, CSI2SS_PHY_CTRL, 0x0);
+
+ regmap_clear_bits(state->phy_gpr, CSI2SS_CTRL_CLK_RESET,
+ CSI2SS_CTRL_CLK_RESET_EN);
+};
+
+static const struct imx8mq_plat_data imx8qxp_data = {
+ .enable = imx8qxp_gpr_enable,
+ .disable = imx8qxp_gpr_disable,
+ .use_reg_csr = true,
+};
+
static const struct csi2_pix_format imx8mq_mipi_csi_formats[] = {
/* RAW (Bayer and greyscale) formats. */
{
@@ -371,14 +492,9 @@ static int imx8mq_mipi_csi_start_stream(struct csi_state *state,
if (ret)
return ret;
- regmap_update_bits(state->phy_gpr,
- state->phy_gpr_reg,
- 0x3fff,
- GPR_CSI2_1_RX_ENABLE |
- GPR_CSI2_1_VID_INTFC_ENB |
- GPR_CSI2_1_HSEL |
- GPR_CSI2_1_CONT_CLK_MODE |
- GPR_CSI2_1_S_PRG_RXHS_SETTLE(hs_settle));
+ ret = state->pdata->enable(state, hs_settle);
+ if (ret)
+ return ret;
return 0;
}
@@ -386,6 +502,9 @@ static int imx8mq_mipi_csi_start_stream(struct csi_state *state,
static void imx8mq_mipi_csi_stop_stream(struct csi_state *state)
{
imx8mq_mipi_csi_write(state, CSI2RX_CFG_DISABLE_DATA_LANES, 0xf);
+
+ if (state->pdata->disable)
+ state->pdata->disable(state);
}
/* -----------------------------------------------------------------------------
@@ -837,6 +956,25 @@ static int imx8mq_mipi_csi_parse_dt(struct csi_state *state)
return PTR_ERR(state->rst);
}
+ if (state->pdata->use_reg_csr) {
+ const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ };
+ void __iomem *base;
+
+ base = devm_platform_ioremap_resource(to_platform_device(dev), 1);
+ if (IS_ERR(base))
+ return dev_err_probe(dev, PTR_ERR(base), "Missing CSR register\n");
+
+ state->phy_gpr = devm_regmap_init_mmio(dev, base, &regmap_config);
+ if (IS_ERR(state->phy_gpr))
+ return dev_err_probe(dev, PTR_ERR(state->phy_gpr),
+ "Failed to init CSI MMIO regmap\n");
+ return 0;
+ }
+
ret = of_property_read_u32_array(np, "fsl,mipi-phy-gpr", out_val,
ARRAY_SIZE(out_val));
if (ret) {
@@ -876,6 +1014,8 @@ static int imx8mq_mipi_csi_probe(struct platform_device *pdev)
state->dev = dev;
+ state->pdata = of_device_get_match_data(dev);
+
ret = imx8mq_mipi_csi_parse_dt(state);
if (ret < 0) {
dev_err(dev, "Failed to parse device tree: %d\n", ret);
@@ -953,7 +1093,8 @@ static void imx8mq_mipi_csi_remove(struct platform_device *pdev)
}
static const struct of_device_id imx8mq_mipi_csi_of_match[] = {
- { .compatible = "fsl,imx8mq-mipi-csi2", },
+ { .compatible = "fsl,imx8mq-mipi-csi2", .data = &imx8mq_data },
+ { .compatible = "fsl,imx8qxp-mipi-csi2", .data = &imx8qxp_data },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, imx8mq_mipi_csi_of_match);
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 f732a76de93e..88c0ba495c32 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
@@ -849,8 +849,7 @@ static int csiphy_init(struct csiphy_device *csiphy)
regs->offset = 0x1000;
break;
default:
- WARN(1, "unknown csiphy version\n");
- return -ENODEV;
+ break;
}
return 0;
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index c622efcc92ff..2de97f58f9ae 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -103,11 +103,6 @@ const struct csiphy_formats csiphy_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
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index ab91273303b9..895f80003c44 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -126,7 +126,6 @@ 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;
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index aa021fd5e123..8d05802d1735 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -225,6 +225,21 @@ static int video_check_format(struct camss_video *video)
return 0;
}
+static int video_prepare_streaming(struct vb2_queue *q)
+{
+ struct camss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+ int ret;
+
+ ret = v4l2_pipeline_pm_get(&vdev->entity);
+ if (ret < 0) {
+ dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
static int video_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct camss_video *video = vb2_get_drv_priv(q);
@@ -308,13 +323,23 @@ static void video_stop_streaming(struct vb2_queue *q)
video->ops->flush_buffers(video, VB2_BUF_STATE_ERROR);
}
+static void video_unprepare_streaming(struct vb2_queue *q)
+{
+ struct camss_video *video = vb2_get_drv_priv(q);
+ struct video_device *vdev = &video->vdev;
+
+ v4l2_pipeline_pm_put(&vdev->entity);
+}
+
static const struct vb2_ops msm_video_vb2_q_ops = {
.queue_setup = video_queue_setup,
.buf_init = video_buf_init,
.buf_prepare = video_buf_prepare,
.buf_queue = video_buf_queue,
+ .prepare_streaming = video_prepare_streaming,
.start_streaming = video_start_streaming,
.stop_streaming = video_stop_streaming,
+ .unprepare_streaming = video_unprepare_streaming,
};
/* -----------------------------------------------------------------------------
@@ -599,20 +624,10 @@ static int video_open(struct file *file)
file->private_data = vfh;
- ret = v4l2_pipeline_pm_get(&vdev->entity);
- if (ret < 0) {
- dev_err(video->camss->dev, "Failed to power up pipeline: %d\n",
- ret);
- goto error_pm_use;
- }
-
mutex_unlock(&video->lock);
return 0;
-error_pm_use:
- v4l2_fh_release(file);
-
error_alloc:
mutex_unlock(&video->lock);
@@ -621,12 +636,8 @@ error_alloc:
static int video_release(struct file *file)
{
- struct video_device *vdev = video_devdata(file);
-
vb2_fop_release(file);
- v4l2_pipeline_pm_put(&vdev->entity);
-
file->private_data = NULL;
return 0;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 06f42875702f..e08e70b93824 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -1481,7 +1481,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 0,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY1 */
@@ -1496,7 +1496,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 1,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY2 */
@@ -1511,7 +1511,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 2,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY3 */
@@ -1526,7 +1526,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 3,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
/* CSIPHY4 */
@@ -1541,7 +1541,7 @@ static const struct camss_subdev_resources csiphy_res_7280[] = {
.csiphy = {
.id = 4,
.hw_ops = &csiphy_ops_3ph_1_0,
- .formats = &csiphy_formats_sc7280
+ .formats = &csiphy_formats_sdm845,
}
},
};
@@ -2486,8 +2486,8 @@ static const struct resources_icc icc_res_sm8550[] = {
static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
/* CSIPHY0 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy0", "csiphy0_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -2501,8 +2501,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY1 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy1", "csiphy1_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -2516,8 +2516,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY2 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy2", "csiphy2_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -2531,8 +2531,8 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
/* CSIPHY4 */
{
- .regulators = { "vdd-csiphy-0p8-supply",
- "vdd-csiphy-1p2-supply" },
+ .regulators = { "vdd-csiphy-0p8",
+ "vdd-csiphy-1p2" },
.clock = { "csiphy4", "csiphy4_timer" },
.clock_rate = { { 300000000, 400000000, 480000000 },
{ 266666667, 400000000 } },
@@ -3386,43 +3386,39 @@ static int camss_subdev_notifier_complete(struct v4l2_async_notifier *async)
struct camss *camss = container_of(async, struct camss, notifier);
struct v4l2_device *v4l2_dev = &camss->v4l2_dev;
struct v4l2_subdev *sd;
- int ret;
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (sd->host_priv) {
- struct media_entity *sensor = &sd->entity;
- struct csiphy_device *csiphy =
- (struct csiphy_device *) sd->host_priv;
- struct media_entity *input = &csiphy->subdev.entity;
- unsigned int i;
-
- for (i = 0; i < sensor->num_pads; i++) {
- if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
- break;
- }
- if (i == sensor->num_pads) {
- dev_err(camss->dev,
- "No source pad in external entity\n");
- return -EINVAL;
- }
+ struct csiphy_device *csiphy = sd->host_priv;
+ struct media_entity *input, *sensor;
+ unsigned int i;
+ int ret;
- ret = media_create_pad_link(sensor, i,
- input, MSM_CSIPHY_PAD_SINK,
- MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
- if (ret < 0) {
- camss_link_err(camss, sensor->name,
- input->name,
- ret);
- return ret;
- }
+ if (!csiphy)
+ continue;
+
+ input = &csiphy->subdev.entity;
+ sensor = &sd->entity;
+
+ for (i = 0; i < sensor->num_pads; i++) {
+ if (sensor->pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ break;
+ }
+ if (i == sensor->num_pads) {
+ dev_err(camss->dev,
+ "No source pad in external entity\n");
+ return -EINVAL;
}
- }
- ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
- if (ret < 0)
- return ret;
+ ret = media_create_pad_link(sensor, i, input,
+ MSM_CSIPHY_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ if (ret < 0) {
+ camss_link_err(camss, sensor->name, input->name, ret);
+ return ret;
+ }
+ }
- return media_device_register(&camss->media_dev);
+ return v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
}
static const struct v4l2_async_notifier_operations camss_subdev_notifier_ops = {
@@ -3625,7 +3621,7 @@ static int camss_probe(struct platform_device *pdev)
ret = v4l2_device_register(camss->dev, &camss->v4l2_dev);
if (ret < 0) {
dev_err(dev, "Failed to register V4L2 device: %d\n", ret);
- goto err_genpd_cleanup;
+ goto err_media_device_cleanup;
}
v4l2_async_nf_init(&camss->notifier, &camss->v4l2_dev);
@@ -3646,6 +3642,12 @@ static int camss_probe(struct platform_device *pdev)
if (ret < 0)
goto err_register_subdevs;
+ ret = media_device_register(&camss->media_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register media device: %d\n", ret);
+ goto err_register_subdevs;
+ }
+
if (num_subdevs) {
camss->notifier.ops = &camss_subdev_notifier_ops;
@@ -3654,32 +3656,29 @@ static int camss_probe(struct platform_device *pdev)
dev_err(dev,
"Failed to register async subdev nodes: %d\n",
ret);
- goto err_register_subdevs;
+ goto err_media_device_unregister;
}
} else {
ret = v4l2_device_register_subdev_nodes(&camss->v4l2_dev);
if (ret < 0) {
dev_err(dev, "Failed to register subdev nodes: %d\n",
ret);
- goto err_register_subdevs;
- }
-
- ret = media_device_register(&camss->media_dev);
- if (ret < 0) {
- dev_err(dev, "Failed to register media device: %d\n",
- ret);
- goto err_register_subdevs;
+ goto err_media_device_unregister;
}
}
return 0;
+err_media_device_unregister:
+ media_device_unregister(&camss->media_dev);
err_register_subdevs:
camss_unregister_entities(camss);
err_v4l2_device_unregister:
v4l2_device_unregister(&camss->v4l2_dev);
v4l2_async_nf_cleanup(&camss->notifier);
pm_runtime_disable(dev);
+err_media_device_cleanup:
+ media_device_cleanup(&camss->media_dev);
err_genpd_cleanup:
camss_genpd_cleanup(camss);
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
index e5c5a564fcb8..6425e4919e3b 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -205,6 +205,9 @@ static u32 iris_bitstream_buffer_size(struct iris_inst *inst)
if (num_mbs > NUM_MBS_4K) {
div_factor = 4;
base_res_mbs = caps->max_mbpf;
+ } else {
+ if (inst->codec == V4L2_PIX_FMT_VP9)
+ div_factor = 1;
}
/*
@@ -376,7 +379,7 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf
return 0;
}
-int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
+static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool force)
{
const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
struct iris_buffer *buf, *next;
@@ -396,6 +399,14 @@ int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
for (i = 0; i < len; i++) {
buffers = &inst->buffers[internal_buf_type[i]];
list_for_each_entry_safe(buf, next, &buffers->list, list) {
+ /*
+ * during stream on, skip destroying internal(DPB) buffer
+ * if firmware did not return it.
+ * during close, destroy all buffers irrespectively.
+ */
+ if (!force && buf->attr & BUF_ATTR_QUEUED)
+ continue;
+
ret = iris_destroy_internal_buffer(inst, buf);
if (ret)
return ret;
@@ -405,6 +416,16 @@ int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane)
return 0;
}
+int iris_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ return iris_destroy_internal_buffers(inst, plane, true);
+}
+
+int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane)
+{
+ return iris_destroy_internal_buffers(inst, plane, false);
+}
+
static int iris_release_internal_buffers(struct iris_inst *inst,
enum iris_buffer_type buffer_type)
{
@@ -593,10 +614,13 @@ int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
vb2 = &vbuf->vb2_buf;
- if (buf->flags & V4L2_BUF_FLAG_ERROR)
+ if (buf->flags & V4L2_BUF_FLAG_ERROR) {
state = VB2_BUF_STATE_ERROR;
- else
- state = VB2_BUF_STATE_DONE;
+ vb2_set_plane_payload(vb2, 0, 0);
+ vb2->timestamp = 0;
+ v4l2_m2m_buf_done(vbuf, state);
+ return 0;
+ }
vbuf->flags |= buf->flags;
@@ -615,7 +639,10 @@ int iris_vb2_buffer_done(struct iris_inst *inst, struct iris_buffer *buf)
v4l2_event_queue_fh(&inst->fh, &ev);
v4l2_m2m_mark_stopped(m2m_ctx);
}
+ inst->last_buffer_dequeued = true;
}
+
+ state = VB2_BUF_STATE_DONE;
vb2->timestamp = buf->timestamp;
v4l2_m2m_buf_done(vbuf, state);
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.h b/drivers/media/platform/qcom/iris/iris_buffer.h
index c36b6347b077..00825ad2dc3a 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.h
+++ b/drivers/media/platform/qcom/iris/iris_buffer.h
@@ -106,7 +106,8 @@ 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_destroy_all_internal_buffers(struct iris_inst *inst, u32 plane);
+int iris_destroy_dequeued_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);
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index b690578256d5..9136b723c0f2 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -17,12 +17,20 @@ static inline bool iris_valid_cap_id(enum platform_inst_fw_cap_type cap_id)
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;
+ return PROFILE_H264;
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ return PROFILE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_VP9_PROFILE:
+ return PROFILE_VP9;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
- return LEVEL;
+ return LEVEL_H264;
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ return LEVEL_HEVC;
+ case V4L2_CID_MPEG_VIDEO_VP9_LEVEL:
+ return LEVEL_VP9;
+ case V4L2_CID_MPEG_VIDEO_HEVC_TIER:
+ return TIER;
default:
return INST_FW_CAP_MAX;
}
@@ -34,12 +42,20 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
return 0;
switch (cap_id) {
- case DEBLOCK:
- return V4L2_CID_MPEG_VIDEO_DECODER_MPEG4_DEBLOCK_FILTER;
- case PROFILE:
+ case PROFILE_H264:
return V4L2_CID_MPEG_VIDEO_H264_PROFILE;
- case LEVEL:
+ case PROFILE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_PROFILE;
+ case PROFILE_VP9:
+ return V4L2_CID_MPEG_VIDEO_VP9_PROFILE;
+ case LEVEL_H264:
return V4L2_CID_MPEG_VIDEO_H264_LEVEL;
+ case LEVEL_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_LEVEL;
+ case LEVEL_VP9:
+ return V4L2_CID_MPEG_VIDEO_VP9_LEVEL;
+ case TIER:
+ return V4L2_CID_MPEG_VIDEO_HEVC_TIER;
default:
return 0;
}
@@ -84,8 +100,6 @@ int iris_ctrls_init(struct iris_inst *inst)
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 */
@@ -163,6 +177,7 @@ void iris_session_init_caps(struct iris_core *core)
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;
+ core->inst_fw_caps[cap_id].set = caps[i].set;
}
}
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h
index b2c541367fc6..9e6aadb83783 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -140,6 +140,7 @@ struct hfi_subscription_params {
u32 color_info;
u32 profile;
u32 level;
+ u32 tier;
};
u32 iris_hfi_get_v4l2_color_primaries(u32 hfi_primaries);
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
index 64f887d9a17d..5fc30d54af4d 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -88,16 +88,29 @@ static int iris_hfi_gen1_sys_pc_prep(struct iris_core *core)
static int iris_hfi_gen1_session_open(struct iris_inst *inst)
{
struct hfi_session_open_pkt packet;
+ u32 codec = 0;
int ret;
if (inst->state != IRIS_INST_DEINIT)
return -EALREADY;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ codec = HFI_VIDEO_CODEC_H264;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ codec = HFI_VIDEO_CODEC_HEVC;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ codec = HFI_VIDEO_CODEC_VP9;
+ break;
+ }
+
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;
+ packet.session_codec = codec;
reinit_completion(&inst->completion);
@@ -208,8 +221,10 @@ static int iris_hfi_gen1_session_stop(struct iris_inst *inst, u32 plane)
flush_pkt.flush_type = flush_type;
ret = iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size);
- if (!ret)
+ if (!ret) {
+ inst->flush_responses_pending++;
ret = iris_wait_for_session_response(inst, true);
+ }
}
return ret;
@@ -386,6 +401,8 @@ static int iris_hfi_gen1_session_drain(struct iris_inst *inst, u32 plane)
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;
+ if (inst->codec == V4L2_PIX_FMT_VP9)
+ ip_pkt.packet_buffer = 0xdeadb000;
return iris_hfi_queue_cmd_write(inst->core, &ip_pkt, ip_pkt.shdr.hdr.size);
}
@@ -490,14 +507,6 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
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;
}
@@ -546,14 +555,15 @@ static int iris_hfi_gen1_set_resolution(struct iris_inst *inst)
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;
+ if (!iris_drc_pending(inst)) {
+ 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;
@@ -768,8 +778,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p
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;
+ config_params = core->iris_platform_data->input_config_params_default;
+ config_params_size = core->iris_platform_data->input_config_params_default_size;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
for (i = 0; i < config_params_size; i++) {
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
index 9f246816a286..d4d119ca98b0 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -13,6 +13,8 @@
#define HFI_SESSION_TYPE_DEC 2
#define HFI_VIDEO_CODEC_H264 0x00000002
+#define HFI_VIDEO_CODEC_HEVC 0x00002000
+#define HFI_VIDEO_CODEC_VP9 0x00004000
#define HFI_ERR_NONE 0x0
@@ -65,7 +67,6 @@
#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
@@ -117,6 +118,8 @@
#define HFI_FRAME_NOTCODED 0x7f002000
#define HFI_FRAME_YUV 0x7f004000
#define HFI_UNUSED_PICT 0x10000000
+#define HFI_BUFFERFLAG_DATACORRUPT 0x00000008
+#define HFI_BUFFERFLAG_DROP_FRAME 0x20000000
struct hfi_pkt_hdr {
u32 size;
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
index b72d503dd740..8d1ce8a19a45 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
@@ -200,14 +200,14 @@ static void iris_hfi_gen1_event_seq_changed(struct iris_inst *inst,
iris_hfi_gen1_read_changed_params(inst, pkt);
- if (inst->state != IRIS_INST_ERROR) {
- reinit_completion(&inst->flush_completion);
+ if (inst->state != IRIS_INST_ERROR && !(inst->sub_state & IRIS_INST_SUB_FIRST_IPSC)) {
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);
+ if (!iris_hfi_queue_cmd_write(inst->core, &flush_pkt, flush_pkt.shdr.hdr.size))
+ inst->flush_responses_pending++;
}
iris_vdec_src_change(inst);
@@ -348,6 +348,10 @@ static void iris_hfi_gen1_session_etb_done(struct iris_inst *inst, void *packet)
struct iris_buffer *buf = NULL;
bool found = false;
+ /* EOS buffer sent via drain won't be in v4l2 buffer list */
+ if (pkt->packet_buffer == 0xdeadb000)
+ return;
+
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) {
@@ -408,7 +412,9 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
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);
+ if (!iris_hfi_queue_cmd_write(core, &flush_pkt, flush_pkt.shdr.hdr.size))
+ inst->flush_responses_pending++;
+
iris_inst_sub_state_change_drain_last(inst);
return;
@@ -455,7 +461,12 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
timestamp_us = timestamp_hi;
timestamp_us = (timestamp_us << 32) | timestamp_lo;
} else {
- flags |= V4L2_BUF_FLAG_LAST;
+ if (pkt->stream_id == 1 && !inst->last_buffer_dequeued) {
+ if (iris_drc_pending(inst)) {
+ flags |= V4L2_BUF_FLAG_LAST;
+ inst->last_buffer_dequeued = true;
+ }
+ }
}
buf->timestamp = timestamp_us;
@@ -481,6 +492,12 @@ static void iris_hfi_gen1_session_ftb_done(struct iris_inst *inst, void *packet)
buf->attr |= BUF_ATTR_DEQUEUED;
buf->attr |= BUF_ATTR_BUFFER_DONE;
+ if (hfi_flags & HFI_BUFFERFLAG_DATACORRUPT)
+ flags |= V4L2_BUF_FLAG_ERROR;
+
+ if (hfi_flags & HFI_BUFFERFLAG_DROP_FRAME)
+ flags |= V4L2_BUF_FLAG_ERROR;
+
buf->flags |= flags;
iris_vb2_buffer_done(inst, buf);
@@ -558,7 +575,6 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *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;
@@ -619,9 +635,12 @@ static void iris_hfi_gen1_handle_response(struct iris_core *core, void *response
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);
+ if (pkt_info->pkt == HFI_MSG_SESSION_FLUSH) {
+ if (!(--inst->flush_responses_pending))
+ complete(&inst->flush_completion);
+ } else {
+ complete(&inst->completion);
+ }
}
mutex_unlock(&inst->lock);
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
index a908b41e2868..7ca5ae13d62b 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -178,7 +178,7 @@ static int iris_hfi_gen2_set_crop_offsets(struct iris_inst *inst)
sizeof(u64));
}
-static int iris_hfi_gen2_set_bit_dpeth(struct iris_inst *inst)
+static int iris_hfi_gen2_set_bit_depth(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);
@@ -295,7 +295,19 @@ 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;
+ u32 profile = 0;
+
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ profile = inst->fw_caps[PROFILE_HEVC].value;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ profile = inst->fw_caps[PROFILE_VP9].value;
+ break;
+ case V4L2_PIX_FMT_H264:
+ profile = inst->fw_caps[PROFILE_H264].value;
+ break;
+ }
inst_hfi_gen2->src_subcr_params.profile = profile;
@@ -312,7 +324,19 @@ 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;
+ u32 level = 0;
+
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ level = inst->fw_caps[LEVEL_HEVC].value;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ level = inst->fw_caps[LEVEL_VP9].value;
+ break;
+ case V4L2_PIX_FMT_H264:
+ level = inst->fw_caps[LEVEL_H264].value;
+ break;
+ }
inst_hfi_gen2->src_subcr_params.level = level;
@@ -367,18 +391,35 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst)
sizeof(u64));
}
+static int iris_hfi_gen2_set_tier(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 tier = inst->fw_caps[TIER].value;
+
+ inst_hfi_gen2->src_subcr_params.tier = tier;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_TIER,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ENUM,
+ &tier,
+ sizeof(u32));
+}
+
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;
+ u32 config_params_size = 0, i, j;
+ const u32 *config_params = NULL;
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_LUMA_CHROMA_BIT_DEPTH, iris_hfi_gen2_set_bit_depth },
{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 },
@@ -386,11 +427,27 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
{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 },
+ {HFI_PROP_TIER, iris_hfi_gen2_set_tier },
};
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;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ config_params = core->iris_platform_data->input_config_params_default;
+ config_params_size =
+ core->iris_platform_data->input_config_params_default_size;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ config_params = core->iris_platform_data->input_config_params_hevc;
+ config_params_size =
+ core->iris_platform_data->input_config_params_hevc_size;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ config_params = core->iris_platform_data->input_config_params_vp9;
+ config_params_size =
+ core->iris_platform_data->input_config_params_vp9_size;
+ break;
+ }
} else {
config_params = core->iris_platform_data->output_config_params;
config_params_size = core->iris_platform_data->output_config_params_size;
@@ -416,7 +473,19 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
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;
+ u32 codec = 0;
+
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ codec = HFI_CODEC_DECODE_AVC;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ codec = HFI_CODEC_DECODE_HEVC;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ codec = HFI_CODEC_DECODE_VP9;
+ break;
+ }
iris_hfi_gen2_packet_session_property(inst,
HFI_PROP_CODEC,
@@ -548,8 +617,8 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
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;
+ const u32 *change_param = NULL;
+ u32 change_param_size = 0;
u32 payload[32] = {0};
u32 hfi_port = 0, i;
int ret;
@@ -560,8 +629,23 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
return 0;
}
- change_param = core->iris_platform_data->input_config_params;
- change_param_size = core->iris_platform_data->input_config_params_size;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ change_param = core->iris_platform_data->input_config_params_default;
+ change_param_size =
+ core->iris_platform_data->input_config_params_default_size;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ change_param = core->iris_platform_data->input_config_params_hevc;
+ change_param_size =
+ core->iris_platform_data->input_config_params_hevc_size;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ change_param = core->iris_platform_data->input_config_params_vp9;
+ change_param_size =
+ core->iris_platform_data->input_config_params_vp9_size;
+ break;
+ }
payload[0] = HFI_MODE_PORT_SETTINGS_CHANGE;
@@ -608,6 +692,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
payload_size = sizeof(u32);
payload_type = HFI_PAYLOAD_U32;
break;
+ case HFI_PROP_LUMA_CHROMA_BIT_DEPTH:
+ payload[0] = subsc_params.bit_depth;
+ 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);
@@ -633,6 +722,11 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
payload_size = sizeof(u32);
payload_type = HFI_PAYLOAD_U32;
break;
+ case HFI_PROP_TIER:
+ payload[0] = subsc_params.tier;
+ payload_size = sizeof(u32);
+ payload_type = HFI_PAYLOAD_U32;
+ break;
default:
prop_type = 0;
ret = -EINVAL;
@@ -659,8 +753,8 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
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 subscribe_prop_size = 0, i;
+ const u32 *subcribe_prop = NULL;
u32 payload[32] = {0};
payload[0] = HFI_MODE_PROPERTY;
@@ -669,8 +763,23 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 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;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_H264:
+ subcribe_prop = core->iris_platform_data->dec_output_prop_avc;
+ subscribe_prop_size =
+ core->iris_platform_data->dec_output_prop_avc_size;
+ break;
+ case V4L2_PIX_FMT_HEVC:
+ subcribe_prop = core->iris_platform_data->dec_output_prop_hevc;
+ subscribe_prop_size =
+ core->iris_platform_data->dec_output_prop_hevc_size;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ subcribe_prop = core->iris_platform_data->dec_output_prop_vp9;
+ subscribe_prop_size =
+ core->iris_platform_data->dec_output_prop_vp9_size;
+ break;
+ }
}
for (i = 0; i < subscribe_prop_size; i++)
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
index 806f8bb7f505..5f13dc11bea5 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -46,6 +46,7 @@
#define HFI_PROP_CROP_OFFSETS 0x03000105
#define HFI_PROP_PROFILE 0x03000107
#define HFI_PROP_LEVEL 0x03000108
+#define HFI_PROP_TIER 0x03000109
#define HFI_PROP_STAGE 0x0300010a
#define HFI_PROP_PIPE 0x0300010b
#define HFI_PROP_LUMA_CHROMA_BIT_DEPTH 0x0300010f
@@ -104,6 +105,9 @@ enum hfi_color_format {
enum hfi_codec_type {
HFI_CODEC_DECODE_AVC = 1,
HFI_CODEC_ENCODE_AVC = 2,
+ HFI_CODEC_DECODE_HEVC = 3,
+ HFI_CODEC_ENCODE_HEVC = 4,
+ HFI_CODEC_DECODE_VP9 = 5,
};
enum hfi_picture_type {
@@ -113,6 +117,7 @@ enum hfi_picture_type {
HFI_PICTURE_I = 0x00000008,
HFI_PICTURE_CRA = 0x00000010,
HFI_PICTURE_BLA = 0x00000020,
+ HFI_PICTURE_NOSHOW = 0x00000040,
};
enum hfi_buffer_type {
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
index b75a01641d5d..a8c30fc5c0d0 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -91,7 +91,9 @@ static int iris_hfi_gen2_get_driver_buffer_flags(struct iris_inst *inst, u32 hfi
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)
+ if (inst_hfi_gen2->hfi_frame_info.picture_type & HFI_PICTURE_NOSHOW)
+ driver_flags |= V4L2_BUF_FLAG_ERROR;
+ else 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;
@@ -265,7 +267,8 @@ static int iris_hfi_gen2_handle_system_error(struct iris_core *core,
{
struct iris_inst *instance;
- dev_err(core->dev, "received system error of type %#x\n", pkt->type);
+ if (pkt)
+ dev_err(core->dev, "received system error of type %#x\n", pkt->type);
core->state = IRIS_CORE_ERROR;
@@ -377,6 +380,11 @@ static int iris_hfi_gen2_handle_output_buffer(struct iris_inst *inst,
buf->flags = iris_hfi_gen2_get_driver_buffer_flags(inst, hfi_buffer->flags);
+ if (!buf->data_size && inst->state == IRIS_INST_STREAMING &&
+ !(hfi_buffer->flags & HFI_BUF_FW_FLAG_LAST)) {
+ buf->flags |= V4L2_BUF_FLAG_ERROR;
+ }
+
return 0;
}
@@ -563,9 +571,23 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
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;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ inst->fw_caps[PROFILE_HEVC].value = subsc_params.profile;
+ inst->fw_caps[LEVEL_HEVC].value = subsc_params.level;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ inst->fw_caps[PROFILE_VP9].value = subsc_params.profile;
+ inst->fw_caps[LEVEL_VP9].value = subsc_params.level;
+ break;
+ case V4L2_PIX_FMT_H264:
+ inst->fw_caps[PROFILE_H264].value = subsc_params.profile;
+ inst->fw_caps[LEVEL_H264].value = subsc_params.level;
+ break;
+ }
+
inst->fw_caps[POC].value = subsc_params.pic_order_cnt;
+ inst->fw_caps[TIER].value = subsc_params.tier;
if (subsc_params.bit_depth != BIT_DEPTH_8 ||
!(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) {
@@ -636,9 +658,6 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
{
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;
@@ -650,6 +669,9 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
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_LUMA_CHROMA_BIT_DEPTH:
+ inst_hfi_gen2->src_subcr_params.bit_depth = pkt->payload[0];
+ break;
case HFI_PROP_CODED_FRAMES:
inst_hfi_gen2->src_subcr_params.coded_frames = pkt->payload[0];
break;
@@ -668,6 +690,9 @@ static int iris_hfi_gen2_handle_session_property(struct iris_inst *inst,
case HFI_PROP_LEVEL:
inst_hfi_gen2->src_subcr_params.level = pkt->payload[0];
break;
+ case HFI_PROP_TIER:
+ inst_hfi_gen2->src_subcr_params.tier = pkt->payload[0];
+ break;
case HFI_PROP_PICTURE_TYPE:
inst_hfi_gen2->hfi_frame_info.picture_type = pkt->payload[0];
break;
@@ -791,8 +816,21 @@ static void iris_hfi_gen2_init_src_change_param(struct iris_inst *inst)
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;
+ switch (inst->codec) {
+ case V4L2_PIX_FMT_HEVC:
+ subsc_params->profile = inst->fw_caps[PROFILE_HEVC].value;
+ subsc_params->level = inst->fw_caps[LEVEL_HEVC].value;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ subsc_params->profile = inst->fw_caps[PROFILE_VP9].value;
+ subsc_params->level = inst->fw_caps[LEVEL_VP9].value;
+ break;
+ case V4L2_PIX_FMT_H264:
+ subsc_params->profile = inst->fw_caps[PROFILE_H264].value;
+ subsc_params->level = inst->fw_caps[LEVEL_H264].value;
+ break;
+ }
+
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 ==
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_queue.c b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
index fac7df0c4d1a..221dcd09e1e1 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_queue.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_queue.c
@@ -113,7 +113,7 @@ int iris_hfi_queue_cmd_write_locked(struct iris_core *core, void *pkt, u32 pkt_s
{
struct iris_iface_q_info *q_info = &core->command_queue;
- if (core->state == IRIS_CORE_ERROR)
+ if (core->state == IRIS_CORE_ERROR || core->state == IRIS_CORE_DEINIT)
return -EINVAL;
if (!iris_hfi_queue_write(q_info, pkt, pkt_size)) {
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
index caa3c6507006..0e1f5799b72d 100644
--- a/drivers/media/platform/qcom/iris/iris_instance.h
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -27,6 +27,7 @@
* @crop: structure of crop info
* @completion: structure of signal completions
* @flush_completion: structure of signal completions for flush cmd
+ * @flush_responses_pending: counter to track number of pending flush responses
* @fw_caps: array of supported instance firmware capabilities
* @buffers: array of different iris buffers
* @fw_min_count: minimnum count of buffers needed by fw
@@ -42,6 +43,8 @@
* @sequence_out: a sequence counter for output queue
* @tss: timestamp metadata
* @metadata_idx: index for metadata buffer
+ * @codec: codec type
+ * @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver
*/
struct iris_inst {
@@ -57,6 +60,7 @@ struct iris_inst {
struct iris_hfi_rect_desc crop;
struct completion completion;
struct completion flush_completion;
+ u32 flush_responses_pending;
struct platform_inst_fw_cap fw_caps[INST_FW_CAP_MAX];
struct iris_buffers buffers[BUF_TYPE_MAX];
u32 fw_min_count;
@@ -72,6 +76,8 @@ struct iris_inst {
u32 sequence_out;
struct iris_ts_metadata tss[VIDEO_MAX_FRAME];
u32 metadata_idx;
+ u32 codec;
+ bool last_buffer_dequeued;
};
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index ac76d9e1ef9c..adafdce8a856 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -21,6 +21,7 @@ struct iris_inst;
#define DEFAULT_MAX_HOST_BUF_COUNT 64
#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
#define DEFAULT_FPS 30
+#define NUM_MBS_8K ((8192 * 4352) / 256)
enum stage_type {
STAGE_1 = 1,
@@ -80,8 +81,12 @@ struct platform_inst_caps {
};
enum platform_inst_fw_cap_type {
- PROFILE = 1,
- LEVEL,
+ PROFILE_H264 = 1,
+ PROFILE_HEVC,
+ PROFILE_VP9,
+ LEVEL_H264,
+ LEVEL_HEVC,
+ LEVEL_VP9,
INPUT_BUF_HOST_MAX_COUNT,
STAGE,
PIPE,
@@ -89,7 +94,7 @@ enum platform_inst_fw_cap_type {
CODED_FRAMES,
BIT_DEPTH,
RAP_FRAME,
- DEBLOCK,
+ TIER,
INST_FW_CAP_MAX,
};
@@ -172,15 +177,24 @@ struct iris_platform_data {
struct ubwc_config_data *ubwc_config;
u32 num_vpp_pipe;
u32 max_session_count;
+ /* max number of macroblocks per frame supported */
u32 max_core_mbpf;
- const u32 *input_config_params;
- unsigned int input_config_params_size;
+ const u32 *input_config_params_default;
+ unsigned int input_config_params_default_size;
+ const u32 *input_config_params_hevc;
+ unsigned int input_config_params_hevc_size;
+ const u32 *input_config_params_vp9;
+ unsigned int input_config_params_vp9_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_output_prop_avc;
+ unsigned int dec_output_prop_avc_size;
+ const u32 *dec_output_prop_hevc;
+ unsigned int dec_output_prop_hevc_size;
+ const u32 *dec_output_prop_vp9;
+ unsigned int dec_output_prop_vp9_size;
const u32 *dec_ip_int_buf_tbl;
unsigned int dec_ip_int_buf_tbl_size;
const u32 *dec_op_int_buf_tbl;
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
index 1e69ba15db0f..d3026b2bcb70 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_gen2.c
@@ -17,7 +17,7 @@
static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
{
- .cap_id = PROFILE,
+ .cap_id = PROFILE_H264,
.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) |
@@ -31,7 +31,29 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
.set = iris_set_u32_enum,
},
{
- .cap_id = LEVEL,
+ .cap_id = PROFILE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
+ .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = PROFILE_VP9,
+ .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2),
+ .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL_H264,
.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) |
@@ -60,6 +82,60 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8550[] = {
.set = iris_set_u32_enum,
},
{
+ .cap_id = LEVEL_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL_VP9,
+ .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0),
+ .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = TIER,
+ .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH),
+ .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
+ .hfi_id = HFI_PROP_TIER,
+ .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,
@@ -181,9 +257,10 @@ static struct tz_cp_config tz_cp_config_sm8550 = {
.cp_nonpixel_size = 0x24800000,
};
-static const u32 sm8550_vdec_input_config_params[] = {
+static const u32 sm8550_vdec_input_config_params_default[] = {
HFI_PROP_BITSTREAM_RESOLUTION,
HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
HFI_PROP_CODED_FRAMES,
HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
HFI_PROP_PIC_ORDER_CNT_TYPE,
@@ -192,6 +269,26 @@ static const u32 sm8550_vdec_input_config_params[] = {
HFI_PROP_SIGNAL_COLOR_INFO,
};
+static const u32 sm8550_vdec_input_config_param_hevc[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_PROP_PROFILE,
+ HFI_PROP_LEVEL,
+ HFI_PROP_TIER,
+ HFI_PROP_SIGNAL_COLOR_INFO,
+};
+
+static const u32 sm8550_vdec_input_config_param_vp9[] = {
+ HFI_PROP_BITSTREAM_RESOLUTION,
+ HFI_PROP_CROP_OFFSETS,
+ HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
+ HFI_PROP_BUFFER_FW_MIN_OUTPUT_COUNT,
+ HFI_PROP_PROFILE,
+ HFI_PROP_LEVEL,
+};
+
static const u32 sm8550_vdec_output_config_params[] = {
HFI_PROP_COLOR_FORMAT,
HFI_PROP_LINEAR_STRIDE_SCANLINE,
@@ -201,11 +298,19 @@ static const u32 sm8550_vdec_subscribe_input_properties[] = {
HFI_PROP_NO_OUTPUT,
};
-static const u32 sm8550_vdec_subscribe_output_properties[] = {
+static const u32 sm8550_vdec_subscribe_output_properties_avc[] = {
HFI_PROP_PICTURE_TYPE,
HFI_PROP_CABAC_SESSION,
};
+static const u32 sm8550_vdec_subscribe_output_properties_hevc[] = {
+ HFI_PROP_PICTURE_TYPE,
+};
+
+static const u32 sm8550_vdec_subscribe_output_properties_vp9[] = {
+ HFI_PROP_PICTURE_TYPE,
+};
+
static const u32 sm8550_dec_ip_int_buf_tbl[] = {
BUF_BIN,
BUF_COMV,
@@ -248,19 +353,34 @@ struct iris_platform_data sm8550_data = {
.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),
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .input_config_params_default =
+ sm8550_vdec_input_config_params_default,
+ .input_config_params_default_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params_default),
+ .input_config_params_hevc =
+ sm8550_vdec_input_config_param_hevc,
+ .input_config_params_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
+ .input_config_params_vp9 =
+ sm8550_vdec_input_config_param_vp9,
+ .input_config_params_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
.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_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc,
+ .dec_output_prop_avc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc),
+ .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc,
+ .dec_output_prop_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc),
+ .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
+ .dec_output_prop_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
.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),
@@ -308,19 +428,34 @@ struct iris_platform_data sm8650_data = {
.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),
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .input_config_params_default =
+ sm8550_vdec_input_config_params_default,
+ .input_config_params_default_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params_default),
+ .input_config_params_hevc =
+ sm8550_vdec_input_config_param_hevc,
+ .input_config_params_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
+ .input_config_params_vp9 =
+ sm8550_vdec_input_config_param_vp9,
+ .input_config_params_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
.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_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc,
+ .dec_output_prop_avc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc),
+ .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc,
+ .dec_output_prop_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc),
+ .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
+ .dec_output_prop_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
.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),
@@ -365,18 +500,33 @@ struct iris_platform_data qcs8300_data = {
.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),
+ .input_config_params_default =
+ sm8550_vdec_input_config_params_default,
+ .input_config_params_default_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_params_default),
+ .input_config_params_hevc =
+ sm8550_vdec_input_config_param_hevc,
+ .input_config_params_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
+ .input_config_params_vp9 =
+ sm8550_vdec_input_config_param_vp9,
+ .input_config_params_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
.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_output_prop_avc = sm8550_vdec_subscribe_output_properties_avc,
+ .dec_output_prop_avc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_avc),
+ .dec_output_prop_hevc = sm8550_vdec_subscribe_output_properties_hevc,
+ .dec_output_prop_hevc_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_hevc),
+ .dec_output_prop_vp9 = sm8550_vdec_subscribe_output_properties_vp9,
+ .dec_output_prop_vp9_size =
+ ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_vp9),
.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),
diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
index f82355d72fcf..a8d66ed388a3 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
@@ -5,49 +5,125 @@
static struct platform_inst_fw_cap inst_fw_cap_qcs8300[] = {
{
- .cap_id = PROFILE,
+ .cap_id = PROFILE_H264,
.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),
+ 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,
+ .cap_id = PROFILE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
+ .value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = PROFILE_VP9,
+ .min = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .max = V4L2_MPEG_VIDEO_VP9_PROFILE_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_PROFILE_2),
+ .value = V4L2_MPEG_VIDEO_VP9_PROFILE_0,
+ .hfi_id = HFI_PROP_PROFILE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL_H264,
.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),
+ 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 = LEVEL_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_LEVEL_1,
+ .max = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2),
+ .value = V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = LEVEL_VP9,
+ .min = V4L2_MPEG_VIDEO_VP9_LEVEL_1_0,
+ .max = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_1_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_2_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_3_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_4_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_0) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_1) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_5_2) |
+ BIT(V4L2_MPEG_VIDEO_VP9_LEVEL_6_0),
+ .value = V4L2_MPEG_VIDEO_VP9_LEVEL_6_0,
+ .hfi_id = HFI_PROP_LEVEL,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_u32_enum,
+ },
+ {
+ .cap_id = TIER,
+ .min = V4L2_MPEG_VIDEO_HEVC_TIER_MAIN,
+ .max = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_TIER_MAIN) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_TIER_HIGH),
+ .value = V4L2_MPEG_VIDEO_HEVC_TIER_HIGH,
+ .hfi_id = HFI_PROP_TIER,
+ .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,
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
index 5c86fd7b7b6f..8d0816a67ae0 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.c
@@ -30,15 +30,6 @@ static struct platform_inst_fw_cap inst_fw_cap_sm8250[] = {
.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 = {
@@ -136,10 +127,10 @@ struct iris_platform_data sm8250_data = {
.hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
.num_vpp_pipe = 4,
.max_session_count = 16,
- .max_core_mbpf = (8192 * 4352) / 256,
- .input_config_params =
+ .max_core_mbpf = NUM_MBS_8K,
+ .input_config_params_default =
sm8250_vdec_input_config_param_default,
- .input_config_params_size =
+ .input_config_params_default_size =
ARRAY_SIZE(sm8250_vdec_input_config_param_default),
.dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl,
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index 9a7ce142f700..4e6e92357968 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -53,7 +53,7 @@ static int iris_init_power_domains(struct iris_core *core)
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,
+ .pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP,
};
ret = devm_pm_domain_attach_list(core->dev, &iris_pd_data, &core->pmdomain_tbl);
diff --git a/drivers/media/platform/qcom/iris/iris_state.c b/drivers/media/platform/qcom/iris/iris_state.c
index 5976e926c83d..104e1687ad39 100644
--- a/drivers/media/platform/qcom/iris/iris_state.c
+++ b/drivers/media/platform/qcom/iris/iris_state.c
@@ -245,7 +245,7 @@ int iris_inst_sub_state_change_pause(struct iris_inst *inst, u32 plane)
return iris_inst_change_sub_state(inst, 0, set_sub_state);
}
-static inline bool iris_drc_pending(struct iris_inst *inst)
+bool iris_drc_pending(struct iris_inst *inst)
{
return inst->sub_state & IRIS_INST_SUB_DRC &&
inst->sub_state & IRIS_INST_SUB_DRC_LAST;
diff --git a/drivers/media/platform/qcom/iris/iris_state.h b/drivers/media/platform/qcom/iris/iris_state.h
index 78c61aac5e7e..e718386dbe04 100644
--- a/drivers/media/platform/qcom/iris/iris_state.h
+++ b/drivers/media/platform/qcom/iris/iris_state.h
@@ -140,5 +140,6 @@ 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);
+bool iris_drc_pending(struct iris_inst *inst);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
index cdf11feb590b..8b17c7c39487 100644
--- a/drivers/media/platform/qcom/iris/iris_vb2.c
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -259,13 +259,14 @@ int iris_vb2_buf_prepare(struct vb2_buffer *vb)
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;
-
+ if (!(inst->sub_state & IRIS_INST_SUB_DRC)) {
+ 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;
}
@@ -304,7 +305,7 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2)
goto exit;
}
- if (V4L2_TYPE_IS_CAPTURE(vb2->vb2_queue->type)) {
+ if (!inst->last_buffer_dequeued && 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 &&
@@ -318,6 +319,7 @@ void iris_vb2_buf_queue(struct vb2_buffer *vb2)
v4l2_event_queue_fh(&inst->fh, &eos);
v4l2_m2m_mark_stopped(m2m_ctx);
}
+ inst->last_buffer_dequeued = true;
goto exit;
}
}
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
index 4143acedfc57..d670b51c5839 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.c
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -32,6 +32,7 @@ int iris_vdec_inst_init(struct iris_inst *inst)
f->fmt.pix_mp.width = DEFAULT_WIDTH;
f->fmt.pix_mp.height = DEFAULT_HEIGHT;
f->fmt.pix_mp.pixelformat = V4L2_PIX_FMT_H264;
+ inst->codec = f->fmt.pix_mp.pixelformat;
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);
@@ -67,14 +68,67 @@ void iris_vdec_inst_deinit(struct iris_inst *inst)
kfree(inst->fmt_src);
}
+static const struct iris_fmt iris_vdec_formats[] = {
+ [IRIS_FMT_H264] = {
+ .pixfmt = V4L2_PIX_FMT_H264,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_HEVC] = {
+ .pixfmt = V4L2_PIX_FMT_HEVC,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+ [IRIS_FMT_VP9] = {
+ .pixfmt = V4L2_PIX_FMT_VP9,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ },
+};
+
+static const struct iris_fmt *
+find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
+{
+ unsigned int size = ARRAY_SIZE(iris_vdec_formats);
+ const struct iris_fmt *fmt = iris_vdec_formats;
+ unsigned int i;
+
+ for (i = 0; i < size; i++) {
+ if (fmt[i].pixfmt == pixfmt)
+ break;
+ }
+
+ if (i == size || fmt[i].type != type)
+ return NULL;
+
+ return &fmt[i];
+}
+
+static const struct iris_fmt *
+find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
+{
+ const struct iris_fmt *fmt = iris_vdec_formats;
+ unsigned int size = ARRAY_SIZE(iris_vdec_formats);
+
+ if (index >= size || fmt[index].type != type)
+ return NULL;
+
+ return &fmt[index];
+}
+
int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
{
+ const struct iris_fmt *fmt;
+
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- f->pixelformat = V4L2_PIX_FMT_H264;
+ fmt = find_format_by_index(inst, f->index, f->type);
+ if (!fmt)
+ return -EINVAL;
+
+ f->pixelformat = fmt->pixfmt;
f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+ if (f->index)
+ return -EINVAL;
f->pixelformat = V4L2_PIX_FMT_NV12;
break;
default:
@@ -88,13 +142,15 @@ 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;
+ const struct iris_fmt *fmt;
struct v4l2_format *f_inst;
struct vb2_queue *src_q;
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
+ fmt = find_format(inst, pixmp->pixelformat, f->type);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264) {
+ if (!fmt) {
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;
@@ -102,7 +158,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_NV12) {
+ if (!fmt) {
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;
@@ -145,13 +201,14 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (f->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_H264)
+ if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
return -EINVAL;
fmt = inst->fmt_src;
fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
-
- codec_align = DEFAULT_CODEC_ALIGNMENT;
+ fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
+ inst->codec = fmt->fmt.pix_mp.pixelformat;
+ codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
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;
@@ -171,6 +228,11 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
output_fmt->fmt.pix_mp.ycbcr_enc = f->fmt.pix_mp.ycbcr_enc;
output_fmt->fmt.pix_mp.quantization = f->fmt.pix_mp.quantization;
+ /* Update capture format based on new ip w/h */
+ output_fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
+ output_fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
+ inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT);
+
inst->crop.left = 0;
inst->crop.top = 0;
inst->crop.width = f->fmt.pix_mp.width;
@@ -239,35 +301,6 @@ void iris_vdec_src_change(struct iris_inst *inst)
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)
@@ -316,7 +349,6 @@ 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) {
@@ -334,12 +366,6 @@ int iris_vdec_session_streamoff(struct iris_inst *inst, u32 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;
@@ -408,7 +434,7 @@ int iris_vdec_streamon_input(struct iris_inst *inst)
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
if (ret)
return ret;
@@ -482,6 +508,8 @@ static int iris_vdec_process_streamon_output(struct iris_inst *inst)
if (ret)
return ret;
+ inst->last_buffer_dequeued = false;
+
return iris_inst_change_sub_state(inst, clear_sub_state, 0);
}
@@ -496,7 +524,7 @@ int iris_vdec_streamon_output(struct iris_inst *inst)
iris_get_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
- ret = iris_destroy_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ ret = iris_destroy_dequeued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
if (ret)
return ret;
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.h b/drivers/media/platform/qcom/iris/iris_vdec.h
index b24932dc511a..cd7aab66dc7c 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.h
+++ b/drivers/media/platform/qcom/iris/iris_vdec.h
@@ -8,6 +8,17 @@
struct iris_inst;
+enum iris_fmt_type {
+ IRIS_FMT_H264,
+ IRIS_FMT_HEVC,
+ IRIS_FMT_VP9,
+};
+
+struct iris_fmt {
+ u32 pixfmt;
+ u32 type;
+};
+
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);
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index ca0f4e310f77..c417e8c31f80 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -221,6 +221,33 @@ static void iris_session_close(struct iris_inst *inst)
iris_wait_for_session_response(inst, false);
}
+static void iris_check_num_queued_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 internal_buffer_count, i;
+ u32 count = 0;
+
+ 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(buf, next, &buffers->list, list)
+ count++;
+ if (count)
+ dev_err(inst->core->dev, "%d buffer of type %d not released",
+ count, internal_buf_type[i]);
+ }
+}
+
int iris_close(struct file *filp)
{
struct iris_inst *inst = iris_get_inst(filp, NULL);
@@ -233,8 +260,10 @@ int iris_close(struct file *filp)
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_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ iris_destroy_all_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ iris_check_num_queued_internal_buffers(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
iris_remove_session(inst);
mutex_unlock(&inst->lock);
mutex_destroy(&inst->ctx_q_lock);
@@ -249,9 +278,6 @@ 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);
}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
index dce25e410d80..f92fd39fe310 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -31,6 +31,42 @@ static u32 hfi_buffer_bin_h264d(u32 frame_width, u32 frame_height, u32 num_vpp_p
return size_h264d_hw_bin_buffer(n_aligned_w, n_aligned_h, num_vpp_pipes);
}
+static u32 size_h265d_hw_bin_buffer(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 product = frame_width * frame_height;
+ u32 size_yuv, size_bin_hdr, size_bin_res;
+
+ size_yuv = (product <= BIN_BUFFER_THRESHOLD) ?
+ ((BIN_BUFFER_THRESHOLD * 3) >> 1) : ((product * 3) >> 1);
+ size_bin_hdr = size_yuv * H265_CABAC_HDR_RATIO_HD_TOT;
+ size_bin_res = size_yuv * H265_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_vp9d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 _size_yuv = ALIGN(frame_width, 16) * ALIGN(frame_height, 16) * 3 / 2;
+ u32 _size = ALIGN(((max_t(u32, _size_yuv, ((BIN_BUFFER_THRESHOLD * 3) >> 1)) *
+ VPX_DECODER_FRAME_BIN_HDR_BUDGET / VPX_DECODER_FRAME_BIN_DENOMINATOR *
+ VPX_DECODER_FRAME_CONCURENCY_LVL) / num_vpp_pipes), DMA_ALIGNMENT) +
+ ALIGN(((max_t(u32, _size_yuv, ((BIN_BUFFER_THRESHOLD * 3) >> 1)) *
+ VPX_DECODER_FRAME_BIN_RES_BUDGET / VPX_DECODER_FRAME_BIN_DENOMINATOR *
+ VPX_DECODER_FRAME_CONCURENCY_LVL) / num_vpp_pipes), DMA_ALIGNMENT);
+
+ return _size * num_vpp_pipes;
+}
+
+static u32 hfi_buffer_bin_h265d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 n_aligned_w = ALIGN(frame_width, 16);
+ u32 n_aligned_h = ALIGN(frame_height, 16);
+
+ return size_h265d_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);
@@ -55,6 +91,17 @@ static u32 hfi_buffer_comv_h264d(u32 frame_width, u32 frame_height, u32 _comv_bu
return (size_colloc * (_comv_bufcount)) + 512;
}
+static u32 hfi_buffer_comv_h265d(u32 frame_width, u32 frame_height, u32 _comv_bufcount)
+{
+ u32 frame_height_in_mbs = (frame_height + 15) >> 4;
+ u32 frame_width_in_mbs = (frame_width + 15) >> 4;
+ u32 _size;
+
+ _size = ALIGN(((frame_width_in_mbs * frame_height_in_mbs) << 8), 512);
+
+ return (_size * (_comv_bufcount)) + 512;
+}
+
static u32 size_h264d_bse_cmd_buf(u32 frame_height)
{
u32 height = ALIGN(frame_height, 32);
@@ -63,6 +110,44 @@ static u32 size_h264d_bse_cmd_buf(u32 frame_height)
SIZE_H264D_BSE_CMD_PER_BUF;
}
+static u32 size_h265d_bse_cmd_buf(u32 frame_width, u32 frame_height)
+{
+ u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) *
+ NUM_HW_PIC_BUF, DMA_ALIGNMENT);
+ _size = min_t(u32, _size, H265D_MAX_SLICE + 1);
+ _size = 2 * _size * SIZE_H265D_BSE_CMD_PER_BUF;
+
+ return _size;
+}
+
+static u32 hfi_buffer_persist_h265d(u32 rpu_enabled)
+{
+ return ALIGN((SIZE_SLIST_BUF_H265 * NUM_SLIST_BUF_H265 +
+ H265_NUM_FRM_INFO * H265_DISPLAY_BUF_SIZE +
+ H265_NUM_TILE * sizeof(u32) +
+ NUM_HW_PIC_BUF * SIZE_SEI_USERDATA +
+ rpu_enabled * NUM_HW_PIC_BUF * SIZE_DOLBY_RPU_METADATA),
+ DMA_ALIGNMENT);
+}
+
+static inline
+u32 hfi_iris3_vp9d_comv_size(void)
+{
+ return (((8192 + 63) >> 6) * ((4320 + 63) >> 6) * 8 * 8 * 2 * 8);
+}
+
+static u32 hfi_buffer_persist_vp9d(void)
+{
+ return ALIGN(VP9_NUM_PROBABILITY_TABLE_BUF * VP9_PROB_TABLE_SIZE, DMA_ALIGNMENT) +
+ ALIGN(hfi_iris3_vp9d_comv_size(), DMA_ALIGNMENT) +
+ ALIGN(MAX_SUPERFRAME_HEADER_LEN, DMA_ALIGNMENT) +
+ ALIGN(VP9_UDC_HEADER_BUF_SIZE, DMA_ALIGNMENT) +
+ ALIGN(VP9_NUM_FRAME_INFO_BUF * CCE_TILE_OFFSET_SIZE, DMA_ALIGNMENT) +
+ ALIGN(VP9_NUM_FRAME_INFO_BUF * VP9_FRAME_INFO_BUF_SIZE, DMA_ALIGNMENT) +
+ HDR10_HIST_EXTRADATA_SIZE;
+}
+
static u32 size_h264d_vpp_cmd_buf(u32 frame_height)
{
u32 size, height = ALIGN(frame_height, 32);
@@ -83,17 +168,45 @@ static u32 hfi_buffer_persist_h264d(void)
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) +
+ u32 size_bse = size_h264d_bse_cmd_buf(frame_height);
+ u32 size_vpp = size_h264d_vpp_cmd_buf(frame_height);
+ u32 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_h265d_vpp_cmd_buf(u32 frame_width, u32 frame_height)
+{
+ u32 _size = ALIGN(((ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS)) *
+ NUM_HW_PIC_BUF, DMA_ALIGNMENT);
+ _size = min_t(u32, _size, H265D_MAX_SLICE + 1);
+ _size = ALIGN(_size, 4);
+ _size = 2 * _size * SIZE_H265D_VPP_CMD_PER_BUF;
+ if (_size > VPP_CMD_MAX_SIZE)
+ _size = VPP_CMD_MAX_SIZE;
+
+ return _size;
+}
+
+static u32 hfi_buffer_non_comv_h265d(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ u32 _size_bse = size_h265d_bse_cmd_buf(frame_width, frame_height);
+ u32 _size_vpp = size_h265d_vpp_cmd_buf(frame_width, frame_height);
+ u32 _size = ALIGN(_size_bse, DMA_ALIGNMENT) +
+ ALIGN(_size_vpp, DMA_ALIGNMENT) +
+ ALIGN(NUM_HW_PIC_BUF * 20 * 22 * 4, DMA_ALIGNMENT) +
+ ALIGN(2 * sizeof(u16) *
+ (ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS) *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS), DMA_ALIGNMENT) +
+ ALIGN(SIZE_HW_PIC(SIZE_H265D_HW_PIC_T), DMA_ALIGNMENT) +
+ HDR10_HIST_EXTRADATA_SIZE;
+
+ 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;
@@ -119,6 +232,203 @@ static u32 size_vpss_lb(u32 frame_width, u32 frame_height)
opb_lb_wr_llb_y_buffer_size;
}
+static inline
+u32 size_h265d_lb_fe_top_data(u32 frame_width, u32 frame_height)
+{
+ return MAX_FE_NBR_DATA_LUMA_LINE_BUFFER_SIZE *
+ (ALIGN(frame_width, 64) + 8) * 2;
+}
+
+static inline
+u32 size_h265d_lb_fe_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE *
+ (ALIGN(frame_width, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS);
+}
+
+static inline
+u32 size_h265d_lb_fe_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE *
+ (ALIGN(frame_height, LCU_MAX_SIZE_PELS) / LCU_MIN_SIZE_PELS);
+}
+
+static inline
+u32 size_h265d_lb_se_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return (LCU_MAX_SIZE_PELS / 8 * (128 / 8)) * ((frame_width + 15) >> 4);
+}
+
+static inline
+u32 size_h265d_lb_se_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return max_t(u32, ((frame_height + 16 - 1) / 8) *
+ MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
+ max_t(u32, ((frame_height + 32 - 1) / 8) *
+ MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
+ ((frame_height + 64 - 1) / 8) *
+ MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE));
+}
+
+static inline
+u32 size_h265d_lb_pe_top_data(u32 frame_width, u32 frame_height)
+{
+ return MAX_PE_NBR_DATA_LCU64_LINE_BUFFER_SIZE *
+ (ALIGN(frame_width, LCU_MIN_SIZE_PELS) / LCU_MIN_SIZE_PELS);
+}
+
+static inline
+u32 size_h265d_lb_vsp_top(u32 frame_width, u32 frame_height)
+{
+ return ((frame_width + 63) >> 6) * 128;
+}
+
+static inline
+u32 size_h265d_lb_vsp_left(u32 frame_width, u32 frame_height)
+{
+ return ((frame_height + 63) >> 6) * 128;
+}
+
+static inline
+u32 size_h265d_lb_recon_dma_metadata_wr(u32 frame_width, u32 frame_height)
+{
+ return size_h264d_lb_recon_dma_metadata_wr(frame_height);
+}
+
+static inline
+u32 size_h265d_qp(u32 frame_width, u32 frame_height)
+{
+ return size_h264d_qp(frame_width, frame_height);
+}
+
+static inline
+u32 hfi_buffer_line_h265d(u32 frame_width, u32 frame_height, bool is_opb, u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0, _size;
+
+ _size = ALIGN(size_h265d_lb_fe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_fe_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_fe_left_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h265d_lb_se_left_ctrl(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h265d_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_pe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_h265d_lb_vsp_left(frame_width, frame_height),
+ DMA_ALIGNMENT) * num_vpp_pipes +
+ ALIGN(size_h265d_lb_recon_dma_metadata_wr(frame_width, frame_height),
+ DMA_ALIGNMENT) * 4 +
+ ALIGN(size_h265d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+ if (is_opb)
+ vpss_lb_size = size_vpss_lb(frame_width, frame_height);
+
+ return ALIGN((_size + vpss_lb_size), DMA_ALIGNMENT);
+}
+
+static inline
+u32 size_vpxd_lb_fe_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return max_t(u32, ((frame_height + 15) >> 4) *
+ MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
+ max_t(u32, ((frame_height + 31) >> 5) *
+ MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
+ ((frame_height + 63) >> 6) *
+ MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE));
+}
+
+static inline
+u32 size_vpxd_lb_fe_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return ((ALIGN(frame_width, 64) + 8) * 10 * 2);
+}
+
+static inline
+u32 size_vpxd_lb_se_top_ctrl(u32 frame_width, u32 frame_height)
+{
+ return ((frame_width + 15) >> 4) * MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE;
+}
+
+static inline
+u32 size_vpxd_lb_se_left_ctrl(u32 frame_width, u32 frame_height)
+{
+ return max_t(u32, ((frame_height + 15) >> 4) *
+ MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE,
+ max_t(u32, ((frame_height + 31) >> 5) *
+ MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE,
+ ((frame_height + 63) >> 6) *
+ MAX_SE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE));
+}
+
+static inline
+u32 size_vpxd_lb_recon_dma_metadata_wr(u32 frame_width, u32 frame_height)
+{
+ return ALIGN((ALIGN(frame_height, 8) / (4 / 2)) * 64,
+ BUFFER_ALIGNMENT_32_BYTES);
+}
+
+static inline __maybe_unused
+u32 size_mp2d_lb_fe_top_data(u32 frame_width, u32 frame_height)
+{
+ return ((ALIGN(frame_width, 16) + 8) * 10 * 2);
+}
+
+static inline
+u32 size_vp9d_lb_fe_top_data(u32 frame_width, u32 frame_height)
+{
+ return (ALIGN(ALIGN(frame_width, 8), 64) + 8) * 10 * 2;
+}
+
+static inline
+u32 size_vp9d_lb_pe_top_data(u32 frame_width, u32 frame_height)
+{
+ return ((ALIGN(ALIGN(frame_width, 8), 64) >> 6) * 176);
+}
+
+static inline
+u32 size_vp9d_lb_vsp_top(u32 frame_width, u32 frame_height)
+{
+ return (((ALIGN(ALIGN(frame_width, 8), 64) >> 6) * 64 * 8) + 256);
+}
+
+static inline
+u32 size_vp9d_qp(u32 frame_width, u32 frame_height)
+{
+ return size_h264d_qp(frame_width, frame_height);
+}
+
+static inline
+u32 hfi_iris3_vp9d_lb_size(u32 frame_width, u32 frame_height, u32 num_vpp_pipes)
+{
+ return ALIGN(size_vpxd_lb_fe_left_ctrl(frame_width, frame_height), DMA_ALIGNMENT) *
+ num_vpp_pipes +
+ ALIGN(size_vpxd_lb_se_left_ctrl(frame_width, frame_height), DMA_ALIGNMENT) *
+ num_vpp_pipes +
+ ALIGN(size_vp9d_lb_vsp_top(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vpxd_lb_fe_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ 2 * ALIGN(size_vpxd_lb_recon_dma_metadata_wr(frame_width, frame_height),
+ DMA_ALIGNMENT) +
+ ALIGN(size_vpxd_lb_se_top_ctrl(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vp9d_lb_pe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vp9d_lb_fe_top_data(frame_width, frame_height), DMA_ALIGNMENT) +
+ ALIGN(size_vp9d_qp(frame_width, frame_height), DMA_ALIGNMENT);
+}
+
+static inline
+u32 hfi_buffer_line_vp9d(u32 frame_width, u32 frame_height, u32 _yuv_bufcount_min, bool is_opb,
+ u32 num_vpp_pipes)
+{
+ u32 vpss_lb_size = 0;
+ u32 _lb_size;
+
+ _lb_size = hfi_iris3_vp9d_lb_size(frame_width, frame_height, num_vpp_pipes);
+
+ if (is_opb)
+ vpss_lb_size = size_vpss_lb(frame_width, frame_height);
+
+ return _lb_size + vpss_lb_size + 4096;
+}
+
static u32 hfi_buffer_line_h264d(u32 frame_width, u32 frame_height,
bool is_opb, u32 num_vpp_pipes)
{
@@ -148,7 +458,14 @@ static u32 iris_vpu_dec_bin_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
- return hfi_buffer_bin_h264d(width, height, num_vpp_pipes);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_bin_h264d(width, height, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_bin_h265d(width, height, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_buffer_bin_vp9d(width, height, num_vpp_pipes);
+
+ return 0;
}
static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
@@ -158,12 +475,24 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
- return hfi_buffer_comv_h264d(width, height, num_comv);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_comv_h264d(width, height, num_comv);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_comv_h265d(width, height, num_comv);
+
+ return 0;
}
static u32 iris_vpu_dec_persist_size(struct iris_inst *inst)
{
- return hfi_buffer_persist_h264d();
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_persist_h264d();
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_persist_h265d(0);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_buffer_persist_vp9d();
+
+ return 0;
}
static u32 iris_vpu_dec_dpb_size(struct iris_inst *inst)
@@ -181,7 +510,12 @@ static u32 iris_vpu_dec_non_comv_size(struct iris_inst *inst)
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);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_non_comv_h264d(width, height, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_non_comv_h265d(width, height, num_vpp_pipes);
+
+ return 0;
}
static u32 iris_vpu_dec_line_size(struct iris_inst *inst)
@@ -191,11 +525,20 @@ static u32 iris_vpu_dec_line_size(struct iris_inst *inst)
u32 height = f->fmt.pix_mp.height;
u32 width = f->fmt.pix_mp.width;
bool is_opb = false;
+ u32 out_min_count = inst->buffers[BUF_OUTPUT].min_count;
if (iris_split_mode_enabled(inst))
is_opb = true;
- return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes);
+ if (inst->codec == V4L2_PIX_FMT_H264)
+ return hfi_buffer_line_h264d(width, height, is_opb, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_HEVC)
+ return hfi_buffer_line_h265d(width, height, is_opb, num_vpp_pipes);
+ else if (inst->codec == V4L2_PIX_FMT_VP9)
+ return hfi_buffer_line_vp9d(width, height, out_min_count, is_opb,
+ num_vpp_pipes);
+
+ return 0;
}
static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst)
@@ -205,6 +548,24 @@ static u32 iris_vpu_dec_scratch1_size(struct iris_inst *inst)
iris_vpu_dec_line_size(inst);
}
+static int output_min_count(struct iris_inst *inst)
+{
+ int output_min_count = 4;
+
+ /* fw_min_count > 0 indicates reconfig event has already arrived */
+ if (inst->fw_min_count) {
+ if (iris_split_mode_enabled(inst) && inst->codec == V4L2_PIX_FMT_VP9)
+ return min_t(u32, 4, inst->fw_min_count);
+ else
+ return inst->fw_min_count;
+ }
+
+ if (inst->codec == V4L2_PIX_FMT_VP9)
+ output_min_count = 9;
+
+ return output_min_count;
+}
+
struct iris_vpu_buf_type_handle {
enum iris_buffer_type type;
u32 (*handle)(struct iris_inst *inst);
@@ -238,6 +599,19 @@ int iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
return size;
}
+static u32 internal_buffer_count(struct iris_inst *inst,
+ enum iris_buffer_type buffer_type)
+{
+ if (buffer_type == BUF_BIN || buffer_type == BUF_LINE ||
+ buffer_type == BUF_PERSIST) {
+ return 1;
+ } else if (buffer_type == BUF_COMV || buffer_type == BUF_NON_COMV) {
+ if (inst->codec == V4L2_PIX_FMT_H264 || inst->codec == V4L2_PIX_FMT_HEVC)
+ return 1;
+ }
+ return 0;
+}
+
static inline int iris_vpu_dpb_count(struct iris_inst *inst)
{
if (iris_split_mode_enabled(inst)) {
@@ -254,12 +628,13 @@ int iris_vpu_buf_count(struct iris_inst *inst, enum iris_buffer_type buffer_type
case BUF_INPUT:
return MIN_BUFFERS;
case BUF_OUTPUT:
- return inst->fw_min_count;
+ return output_min_count(inst);
case BUF_BIN:
case BUF_COMV:
case BUF_NON_COMV:
case BUF_LINE:
case BUF_PERSIST:
+ return internal_buffer_count(inst, buffer_type);
case BUF_SCRATCH_1:
return 1; /* internal buffer count needed by firmware is 1 */
case BUF_DPB:
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
index 62af6ea6ba1f..ee95fd20b794 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.h
@@ -13,6 +13,10 @@ struct iris_inst;
#define DMA_ALIGNMENT 256
#define NUM_HW_PIC_BUF 32
+#define LCU_MAX_SIZE_PELS 64
+#define LCU_MIN_SIZE_PELS 16
+#define HDR10_HIST_EXTRADATA_SIZE (4 * 1024)
+
#define SIZE_HW_PIC(size_per_buf) (NUM_HW_PIC_BUF * (size_per_buf))
#define MAX_TILE_COLUMNS 32
@@ -28,11 +32,47 @@ struct iris_inst;
#define SIZE_SLIST_BUF_H264 512
#define H264_DISPLAY_BUF_SIZE 3328
#define H264_NUM_FRM_INFO 66
-
-#define SIZE_SEI_USERDATA 4096
-
+#define H265_NUM_TILE_COL 32
+#define H265_NUM_TILE_ROW 128
+#define H265_NUM_TILE (H265_NUM_TILE_ROW * H265_NUM_TILE_COL + 1)
+#define SIZE_H265D_BSE_CMD_PER_BUF (16 * sizeof(u32))
+
+#define NUM_SLIST_BUF_H265 (80 + 20)
+#define SIZE_SLIST_BUF_H265 (BIT(10))
+#define H265_DISPLAY_BUF_SIZE (3072)
+#define H265_NUM_FRM_INFO (48)
+
+#define VP9_NUM_FRAME_INFO_BUF 32
+#define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4)
+#define VP9_PROB_TABLE_SIZE (3840)
+#define VP9_FRAME_INFO_BUF_SIZE (6144)
+#define BUFFER_ALIGNMENT_32_BYTES 32
+#define CCE_TILE_OFFSET_SIZE ALIGN(32 * 4 * 4, BUFFER_ALIGNMENT_32_BYTES)
+#define MAX_SUPERFRAME_HEADER_LEN (34)
+#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
+#define MAX_FE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE 64
+#define MAX_FE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE 64
+#define MAX_SE_NBR_CTRL_LCU16_LINE_BUFFER_SIZE (128 / 8)
+#define MAX_SE_NBR_CTRL_LCU32_LINE_BUFFER_SIZE (128 / 8)
+#define VP9_UDC_HEADER_BUF_SIZE (3 * 128)
+
+#define SIZE_SEI_USERDATA 4096
+#define SIZE_DOLBY_RPU_METADATA (41 * 1024)
#define H264_CABAC_HDR_RATIO_HD_TOT 1
#define H264_CABAC_RES_RATIO_HD_TOT 3
+#define H265D_MAX_SLICE 1200
+#define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T
+#define H265_CABAC_HDR_RATIO_HD_TOT 2
+#define H265_CABAC_RES_RATIO_HD_TOT 2
+#define SIZE_H265D_VPP_CMD_PER_BUF (256)
+
+#define VPX_DECODER_FRAME_CONCURENCY_LVL (2)
+#define VPX_DECODER_FRAME_BIN_HDR_BUDGET 1
+#define VPX_DECODER_FRAME_BIN_RES_BUDGET 3
+#define VPX_DECODER_FRAME_BIN_DENOMINATOR 2
+
+#define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO (3 / 2)
+
#define SIZE_H264D_HW_PIC_T (BIT(11))
#define MAX_FE_NBR_CTRL_LCU64_LINE_BUFFER_SIZE 64
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index d305d74bb152..4c049c694d9c 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -424,13 +424,13 @@ static int venus_probe(struct platform_device *pdev)
INIT_DELAYED_WORK(&core->work, venus_sys_error_handler);
init_waitqueue_head(&core->sys_err_done);
- ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
- "venus", core);
+ ret = hfi_create(core, &venus_core_ops);
if (ret)
goto err_core_put;
- ret = hfi_create(core, &venus_core_ops);
+ ret = devm_request_threaded_irq(dev, core->irq, hfi_isr, venus_isr_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "venus", core);
if (ret)
goto err_core_put;
@@ -709,11 +709,11 @@ static const struct venus_resources msm8996_res = {
};
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 */
+ { 1728000, 533000000 }, /* 4k UHD @ 60 (decode only) */
+ { 1036800, 444000000 }, /* 2k @ 120 */
+ { 829440, 355200000 }, /* 4k @ 44 */
+ { 489600, 269330000 },/* 4k @ 30 */
+ { 108000, 200000000 }, /* 1080p @ 60 */
};
static const struct reg_val msm8998_reg_preset[] = {
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index b412e0c5515a..5b1ba1c69adb 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -28,6 +28,8 @@
#define VIDC_RESETS_NUM_MAX 2
#define VIDC_MAX_HIER_CODING_LAYER 6
+#define VENUS_MAX_FPS 240
+
extern int venus_fw_debug;
struct freq_tbl {
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
index 0a041b4db9ef..cf0d97cbc463 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.c
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -33,8 +33,9 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
struct hfi_buffer_requirements *bufreq;
struct hfi_extradata_input_crop *crop;
struct hfi_dpb_counts *dpb_count;
+ u32 ptype, rem_bytes;
+ u32 size_read = 0;
u8 *data_ptr;
- u32 ptype;
inst->error = HFI_ERR_NONE;
@@ -44,86 +45,118 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
break;
default:
inst->error = HFI_ERR_SESSION_INVALID_PARAMETER;
- goto done;
+ inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
+ return;
}
event.event_type = pkt->event_data1;
num_properties_changed = pkt->event_data2;
- if (!num_properties_changed) {
- inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
- goto done;
- }
+ if (!num_properties_changed)
+ goto error;
data_ptr = (u8 *)&pkt->ext_event_data[0];
+ rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt);
+
do {
+ if (rem_bytes < sizeof(u32))
+ goto error;
ptype = *((u32 *)data_ptr);
+
+ data_ptr += sizeof(u32);
+ rem_bytes -= sizeof(u32);
+
switch (ptype) {
case HFI_PROPERTY_PARAM_FRAME_SIZE:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_framesize))
+ goto error;
+
frame_sz = (struct hfi_framesize *)data_ptr;
event.width = frame_sz->width;
event.height = frame_sz->height;
- data_ptr += sizeof(*frame_sz);
+ size_read = sizeof(struct hfi_framesize);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_profile_level))
+ goto error;
+
profile_level = (struct hfi_profile_level *)data_ptr;
event.profile = profile_level->profile;
event.level = profile_level->level;
- data_ptr += sizeof(*profile_level);
+ size_read = sizeof(struct hfi_profile_level);
break;
case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_bit_depth))
+ goto error;
+
pixel_depth = (struct hfi_bit_depth *)data_ptr;
event.bit_depth = pixel_depth->bit_depth;
- data_ptr += sizeof(*pixel_depth);
+ size_read = sizeof(struct hfi_bit_depth);
break;
case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_pic_struct))
+ goto error;
+
pic_struct = (struct hfi_pic_struct *)data_ptr;
event.pic_struct = pic_struct->progressive_only;
- data_ptr += sizeof(*pic_struct);
+ size_read = sizeof(struct hfi_pic_struct);
break;
case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_colour_space))
+ goto error;
+
colour_info = (struct hfi_colour_space *)data_ptr;
event.colour_space = colour_info->colour_space;
- data_ptr += sizeof(*colour_info);
+ size_read = sizeof(struct hfi_colour_space);
break;
case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(u32))
+ goto error;
+
event.entropy_mode = *(u32 *)data_ptr;
- data_ptr += sizeof(u32);
+ size_read = sizeof(u32);
break;
case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_buffer_requirements))
+ goto error;
+
bufreq = (struct hfi_buffer_requirements *)data_ptr;
event.buf_count = hfi_bufreq_get_count_min(bufreq, ver);
- data_ptr += sizeof(*bufreq);
+ size_read = sizeof(struct hfi_buffer_requirements);
break;
case HFI_INDEX_EXTRADATA_INPUT_CROP:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_extradata_input_crop))
+ goto error;
+
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);
+ size_read = sizeof(struct hfi_extradata_input_crop);
break;
case HFI_PROPERTY_PARAM_VDEC_DPB_COUNTS:
- data_ptr += sizeof(u32);
+ if (rem_bytes < sizeof(struct hfi_dpb_counts))
+ goto error;
+
dpb_count = (struct hfi_dpb_counts *)data_ptr;
event.buf_count = dpb_count->fw_min_cnt;
- data_ptr += sizeof(*dpb_count);
+ size_read = sizeof(struct hfi_dpb_counts);
break;
default:
+ size_read = 0;
break;
}
+ data_ptr += size_read;
+ rem_bytes -= size_read;
num_properties_changed--;
} while (num_properties_changed > 0);
-done:
+ inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
+ return;
+
+error:
+ inst->error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
inst->ops->event_notify(inst, EVT_SYS_EVENT_CHANGE, &event);
}
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index b5f2ea879950..cec7f5964d3d 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -239,6 +239,7 @@ static int venus_write_queue(struct venus_hfi_device *hdev,
static int venus_read_queue(struct venus_hfi_device *hdev,
struct iface_queue *queue, void *pkt, u32 *tx_req)
{
+ struct hfi_pkt_hdr *pkt_hdr = NULL;
struct hfi_queue_header *qhdr;
u32 dwords, new_rd_idx;
u32 rd_idx, wr_idx, type, qsize;
@@ -304,6 +305,9 @@ static int venus_read_queue(struct venus_hfi_device *hdev,
memcpy(pkt, rd_ptr, len);
memcpy(pkt + len, queue->qmem.kva, new_rd_idx << 2);
}
+ pkt_hdr = (struct hfi_pkt_hdr *)(pkt);
+ if ((pkt_hdr->size >> 2) != dwords)
+ return -EINVAL;
} else {
/* bad packet received, dropping */
new_rd_idx = qhdr->write_idx;
@@ -1678,6 +1682,7 @@ void venus_hfi_destroy(struct venus_core *core)
venus_interface_queues_release(hdev);
mutex_destroy(&hdev->lock);
kfree(hdev);
+ disable_irq(core->irq);
core->ops = NULL;
}
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index 409aa9bd0b5d..8dd5a9b0d060 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -41,16 +41,14 @@ static int core_clks_get(struct venus_core *core)
static int core_clks_enable(struct venus_core *core)
{
const struct venus_resources *res = core->res;
- const struct freq_tbl *freq_tbl = core->res->freq_tbl;
- unsigned int freq_tbl_size = core->res->freq_tbl_size;
- unsigned long freq;
+ struct device *dev = core->dev;
+ unsigned long freq = 0;
+ struct dev_pm_opp *opp;
unsigned int i;
int ret;
- if (!freq_tbl)
- return -EINVAL;
-
- freq = freq_tbl[freq_tbl_size - 1].freq;
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ dev_pm_opp_put(opp);
for (i = 0; i < res->clks_num; i++) {
if (IS_V6(core)) {
@@ -636,7 +634,9 @@ static int decide_core(struct venus_inst *inst)
u32 min_coreid, min_load, cur_inst_load;
u32 min_lp_coreid, min_lp_load, cur_inst_lp_load;
struct hfi_videocores_usage_type cu;
- unsigned long max_freq;
+ unsigned long max_freq = ULONG_MAX;
+ struct device *dev = core->dev;
+ struct dev_pm_opp *opp;
int ret = 0;
if (legacy_binding) {
@@ -659,7 +659,8 @@ static int decide_core(struct venus_inst *inst)
cur_inst_lp_load *= inst->clk_data.low_power_freq;
/*TODO : divide this inst->load by work_route */
- max_freq = core->res->freq_tbl[0].freq;
+ opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
+ dev_pm_opp_put(opp);
min_loaded_core(inst, &min_coreid, &min_load, false);
min_loaded_core(inst, &min_lp_coreid, &min_lp_load, true);
@@ -949,7 +950,10 @@ static int core_resets_get(struct venus_core *core)
static int core_get_v4(struct venus_core *core)
{
struct device *dev = core->dev;
+ const struct freq_tbl *freq_tbl = core->res->freq_tbl;
+ unsigned int num_rows = core->res->freq_tbl_size;
const struct venus_resources *res = core->res;
+ unsigned int i;
int ret;
ret = core_clks_get(core);
@@ -986,9 +990,17 @@ static int core_get_v4(struct venus_core *core)
if (core->res->opp_pmdomain) {
ret = devm_pm_opp_of_add_table(dev);
- if (ret && ret != -ENODEV) {
- dev_err(dev, "invalid OPP table in device tree\n");
- return ret;
+ if (ret) {
+ if (ret == -ENODEV) {
+ for (i = 0; i < num_rows; i++) {
+ ret = dev_pm_opp_add(dev, freq_tbl[i].freq, 0);
+ if (ret)
+ return ret;
+ }
+ } else {
+ dev_err(dev, "invalid OPP table in device tree\n");
+ return ret;
+ }
}
}
@@ -1078,11 +1090,11 @@ static unsigned long calculate_inst_freq(struct venus_inst *inst,
static int load_scale_v4(struct venus_inst *inst)
{
struct venus_core *core = inst->core;
- const struct freq_tbl *table = core->res->freq_tbl;
- unsigned int num_rows = core->res->freq_tbl_size;
struct device *dev = core->dev;
unsigned long freq = 0, freq_core1 = 0, freq_core2 = 0;
+ unsigned long max_freq = ULONG_MAX;
unsigned long filled_len = 0;
+ struct dev_pm_opp *opp;
int i, ret = 0;
for (i = 0; i < inst->num_input_bufs; i++)
@@ -1108,20 +1120,18 @@ static int load_scale_v4(struct venus_inst *inst)
freq = max(freq_core1, freq_core2);
- if (freq > table[0].freq) {
- dev_dbg(dev, VDBGL "requested clock rate: %lu scaling clock rate : %lu\n",
- freq, table[0].freq);
+ opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
+ dev_pm_opp_put(opp);
- freq = table[0].freq;
+ if (freq > max_freq) {
+ dev_dbg(dev, VDBGL "requested clock rate: %lu scaling clock rate : %lu\n",
+ freq, max_freq);
+ freq = max_freq;
goto set_freq;
}
- for (i = num_rows - 1 ; i >= 0; i--) {
- if (freq <= table[i].freq) {
- freq = table[i].freq;
- break;
- }
- }
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ dev_pm_opp_put(opp);
set_freq:
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 99ce5fd41577..29b0d6a5303d 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -481,11 +481,9 @@ static int vdec_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
do_div(us_per_frame, timeperframe->denominator);
- if (!us_per_frame)
- return -EINVAL;
-
- fps = (u64)USEC_PER_SEC;
- do_div(fps, us_per_frame);
+ us_per_frame = clamp(us_per_frame, 1, USEC_PER_SEC);
+ fps = USEC_PER_SEC / (u32)us_per_frame;
+ fps = min(VENUS_MAX_FPS, fps);
inst->fps = fps;
inst->timeperframe = *timeperframe;
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index c7f8e37dba9b..c0a0ccdded80 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -411,11 +411,9 @@ static int venc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
us_per_frame = timeperframe->numerator * (u64)USEC_PER_SEC;
do_div(us_per_frame, timeperframe->denominator);
- if (!us_per_frame)
- return -EINVAL;
-
- fps = (u64)USEC_PER_SEC;
- do_div(fps, us_per_frame);
+ us_per_frame = clamp(us_per_frame, 1, USEC_PER_SEC);
+ fps = USEC_PER_SEC / (u32)us_per_frame;
+ fps = min(VENUS_MAX_FPS, fps);
inst->timeperframe = *timeperframe;
inst->fps = fps;
diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
index 46765a2e4c4d..a9e51fd94aad 100644
--- a/drivers/media/platform/raspberrypi/pisp_be/Kconfig
+++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_RASPBERRYPI_PISP_BE
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on PM
select VIDEO_V4L2_SUBDEV_API
select MEDIA_CONTROLLER
select VIDEOBUF2_DMA_CONTIG
diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
index 7596ae1f7de6..b30891718d8d 100644
--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
@@ -9,9 +9,11 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/lockdep.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
@@ -161,8 +163,6 @@ struct pispbe_node {
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;
@@ -190,6 +190,8 @@ struct pispbe_hw_enables {
/* Records a job configuration and memory addresses. */
struct pispbe_job_descriptor {
+ struct list_head queue;
+ struct pispbe_buffer *buffers[PISPBE_NUM_NODES];
dma_addr_t hw_dma_addrs[N_HW_ADDRESSES];
struct pisp_be_tiles_config *config;
struct pispbe_hw_enables hw_enables;
@@ -215,8 +217,10 @@ struct pispbe_dev {
unsigned int sequence;
u32 streaming_map;
struct pispbe_job queued_job, running_job;
- spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */
+ /* protects "hw_busy" flag, streaming_map and job_queue */
+ spinlock_t hw_lock;
bool hw_busy; /* non-zero if a job is queued or is being started */
+ struct list_head job_queue;
int irq;
u32 hw_version;
u8 done, started;
@@ -368,10 +372,7 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe,
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.
- */
+ /* Shouldn't happen, we have validated an input is available. */
dev_warn(pispbe->dev, "ISP-BE missing input\n");
hw_en->bayer_enables = 0;
hw_en->rgb_enables = 0;
@@ -443,42 +444,48 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe,
* 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.
+ * If all the buffers required to form a job are available, append the
+ * job descriptor to the job queue to be later queued to the HW.
*
* 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)
+static int pispbe_prepare_job(struct pispbe_dev *pispbe)
{
+ struct pispbe_job_descriptor __free(kfree) *job = NULL;
struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {};
+ unsigned int streaming_map;
unsigned int config_index;
struct pispbe_node *node;
- unsigned long flags;
- lockdep_assert_held(&pispbe->hw_lock);
+ lockdep_assert_irqs_enabled();
- memset(job, 0, sizeof(struct pispbe_job_descriptor));
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ static const u32 mask = BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE);
- if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) &
- pispbe->streaming_map) !=
- (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)))
- return -ENODEV;
+ if ((pispbe->streaming_map & mask) != mask)
+ return -ENODEV;
+
+ /*
+ * Take a copy of streaming_map: nodes activated after this
+ * point are ignored when preparing this job.
+ */
+ streaming_map = pispbe->streaming_map;
+ }
+
+ job = kzalloc(sizeof(*job), GFP_KERNEL);
+ if (!job)
+ return -ENOMEM;
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;
+ list_del(&buf[CONFIG_NODE]->ready_list);
+ job->buffers[CONFIG_NODE] = buf[CONFIG_NODE];
+
config_index = buf[CONFIG_NODE]->vb.vb2_buf.index;
job->config = &pispbe->config[config_index];
job->tiles = pispbe->config_dma_addr +
@@ -498,7 +505,7 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe,
continue;
buf[i] = NULL;
- if (!(pispbe->streaming_map & BIT(i)))
+ if (!(streaming_map & BIT(i)))
continue;
if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) &&
@@ -525,25 +532,28 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe,
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];
+ job->buffers[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);
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ list_add_tail(&job->queue, &pispbe->job_queue);
+ }
+
+ /* Set job to NULL to avoid automatic release due to __free(). */
+ job = NULL;
+
return 0;
err_return_buffers:
@@ -554,33 +564,37 @@ err_return_buffers:
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;
+ struct pispbe_job_descriptor *job;
- spin_lock_irqsave(&pispbe->hw_lock, flags);
+ scoped_guard(spinlock_irqsave, &pispbe->hw_lock) {
+ if (clear_hw_busy)
+ pispbe->hw_busy = false;
- if (clear_hw_busy)
- pispbe->hw_busy = false;
+ if (pispbe->hw_busy)
+ return;
- if (pispbe->hw_busy)
- goto unlock_and_return;
+ job = list_first_entry_or_null(&pispbe->job_queue,
+ struct pispbe_job_descriptor,
+ queue);
+ if (!job)
+ return;
- ret = pispbe_prepare_job(pispbe, &job);
- if (ret)
- goto unlock_and_return;
+ list_del(&job->queue);
+
+ for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++)
+ pispbe->queued_job.buf[i] = job->buffers[i];
+ pispbe->queued_job.valid = true;
+
+ pispbe->hw_busy = true;
+ }
/*
* We can kick the job off without the hw_lock, as this can
@@ -588,34 +602,8 @@ static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy)
* 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);
+ pispbe_queue_job(pispbe, job);
+ kfree(job);
}
static void pispbe_isr_jobdone(struct pispbe_dev *pispbe,
@@ -706,6 +694,13 @@ static int pisp_be_validate_config(struct pispbe_dev *pispbe,
return -EIO;
}
+ if (config->num_tiles == 0 ||
+ config->num_tiles > PISP_BACK_END_NUM_TILES) {
+ dev_dbg(dev, "%s: Invalid number of tiles: %d\n", __func__,
+ config->num_tiles);
+ return -EINVAL;
+ }
+
/* 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) {
@@ -860,18 +855,16 @@ static void pispbe_node_buffer_queue(struct vb2_buffer *buf)
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);
+ if (!pispbe_prepare_job(pispbe))
+ pispbe_schedule(pispbe, false);
}
static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -879,17 +872,16 @@ 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);
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ node->pispbe->streaming_map |= BIT(node->id);
+ node->pispbe->sequence = 0;
+ }
dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n",
__func__, NODE_NAME(node), count);
@@ -897,17 +889,16 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
node->pispbe->streaming_map);
/* Maybe we're ready to run. */
- pispbe_schedule(pispbe, false);
+ if (!pispbe_prepare_job(pispbe))
+ 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;
}
@@ -916,8 +907,9 @@ 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_job_descriptor *job, *temp;
struct pispbe_buffer *buf;
- unsigned long flags;
+ LIST_HEAD(tmp_list);
/*
* Now this is a bit awkward. In a simple M2M device we could just wait
@@ -929,11 +921,7 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
* 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);
@@ -941,15 +929,26 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
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);
+ spin_lock_irq(&pispbe->hw_lock);
pispbe->streaming_map &= ~BIT(node->id);
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ if (pispbe->streaming_map == 0) {
+ /*
+ * If all nodes have stopped streaming release all jobs
+ * without holding the lock.
+ */
+ list_splice_init(&pispbe->job_queue, &tmp_list);
+ }
+ spin_unlock_irq(&pispbe->hw_lock);
+
+ list_for_each_entry_safe(job, temp, &tmp_list, queue) {
+ list_del(&job->queue);
+ kfree(job);
+ }
pm_runtime_mark_last_busy(pispbe->dev);
pm_runtime_put_autosuspend(pispbe->dev);
@@ -1114,10 +1113,12 @@ static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node)
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);
+ f->fmt.pix_mp.width = clamp(f->fmt.pix_mp.width,
+ PISP_BACK_END_MIN_TILE_WIDTH,
+ PISP_BACK_END_MAX_TILE_WIDTH);
+ f->fmt.pix_mp.height = clamp(f->fmt.pix_mp.height,
+ PISP_BACK_END_MIN_TILE_HEIGHT,
+ PISP_BACK_END_MAX_TILE_HEIGHT);
/*
* Fill in the actual colour space when the requested one was
@@ -1407,7 +1408,6 @@ static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id)
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);
@@ -1691,6 +1691,8 @@ static int pispbe_probe(struct platform_device *pdev)
if (!pispbe)
return -ENOMEM;
+ INIT_LIST_HEAD(&pispbe->job_queue);
+
dev_set_drvdata(&pdev->dev, pispbe);
pispbe->dev = &pdev->dev;
platform_set_drvdata(pdev, pispbe);
@@ -1726,7 +1728,7 @@ static int pispbe_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(pispbe->dev);
pm_runtime_enable(pispbe->dev);
- ret = pispbe_runtime_resume(pispbe->dev);
+ ret = pm_runtime_resume_and_get(pispbe->dev);
if (ret)
goto pm_runtime_disable_err;
@@ -1748,7 +1750,7 @@ static int pispbe_probe(struct platform_device *pdev)
disable_devs_err:
pispbe_destroy_devices(pispbe);
pm_runtime_suspend_err:
- pispbe_runtime_suspend(pispbe->dev);
+ pm_runtime_put(pispbe->dev);
pm_runtime_disable_err:
pm_runtime_dont_use_autosuspend(pispbe->dev);
pm_runtime_disable(pispbe->dev);
@@ -1762,7 +1764,6 @@ static void pispbe_remove(struct platform_device *pdev)
pispbe_destroy_devices(pispbe);
- pispbe_runtime_suspend(pispbe->dev);
pm_runtime_dont_use_autosuspend(pispbe->dev);
pm_runtime_disable(pispbe->dev);
}
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
index fcadb2143c88..62dca76b468d 100644
--- a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
@@ -1024,9 +1024,6 @@ static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
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);
@@ -1998,6 +1995,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id)
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &node->lock;
q->min_queued_buffers = 1;
+ q->min_reqbufs_allocation = 3;
q->dev = &cfe->pdev->dev;
ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index 9979de4f6ef1..d1b31ab8b8c4 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-csi2.c
@@ -172,20 +172,27 @@ struct rcar_csi2;
#define V4H_PPI_RW_LPDCOCAL_TWAIT_CONFIG_REG 0x21c0a
#define V4H_PPI_RW_LPDCOCAL_VT_CONFIG_REG 0x21c0c
#define V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG 0x21c10
+#define V4H_PPI_RW_DDLCAL_CFG_n_REG(n) (0x21c40 + ((n) * 2)) /* n = 0 - 7 */
#define V4H_PPI_RW_COMMON_CFG_REG 0x21c6c
#define V4H_PPI_RW_TERMCAL_CFG_0_REG 0x21c80
#define V4H_PPI_RW_OFFSETCAL_CFG_0_REG 0x21ca0
/* V4H CORE registers */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(n) (0x22040 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(n) (0x22440 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(n) (0x22840 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(n) (0x22c40 + ((n) * 2)) /* n = 0 - 15 */
-#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(n) (0x23040 + ((n) * 2)) /* n = 0 - 15 */
+
+#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, n) (0x22040 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_3_REG(l, n) (0x22060 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_4_REG(l, n) (0x22080 + ((l) * 0x400) + ((n) * 2))
+
#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_HS_TX_6_REG 0x2a60c
+#define V4H_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM_REG 0x23fe0
+
+#define V4H_CORE_DIG_DLANE_l_RW_CFG_n_REG(l, n) (0x26000 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, n) (0x26080 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, n) (0x26100 + ((l) * 0x400) + ((n) * 2))
+#define V4H_CORE_DIG_DLANE_CLK_RW_LP_n_REG(n) V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(4, (n))
+#define V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(n) V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(4, (n))
/* V4H C-PHY */
#define V4H_CORE_DIG_RW_TRIO0_REG(n) (0x22100 + ((n) * 2)) /* n = 0 - 3 */
@@ -197,6 +204,7 @@ struct rcar_csi2;
#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_1_RW_HS_TX_6_REG 0x2a60c
#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 */
@@ -954,6 +962,7 @@ static int rcsi2_set_phypll(struct rcar_csi2 *priv, unsigned int mbps)
static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
unsigned int lanes)
{
+ struct media_pad *remote_pad;
struct v4l2_subdev *source;
s64 freq;
u64 mbps;
@@ -962,8 +971,9 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
return -ENODEV;
source = priv->remote;
+ remote_pad = &source->entity.pads[priv->remote_pad];
- freq = v4l2_get_link_freq(source->ctrl_handler, bpp, 2 * lanes);
+ freq = v4l2_get_link_freq(remote_pad, bpp, 2 * lanes);
if (freq < 0) {
int ret = (int)freq;
@@ -975,10 +985,6 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv, unsigned int bpp,
mbps = div_u64(freq * 2, MEGA);
- /* Adjust for C-PHY, divide by 2.8. */
- if (priv->cphy)
- mbps = div_u64(mbps * 5, 14);
-
return mbps;
}
@@ -1203,9 +1209,14 @@ static int rcsi2_wait_phy_start_v4h(struct rcar_csi2 *priv, u32 match)
return -ETIMEDOUT;
}
-static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
+static const struct rcsi2_cphy_setting *
+rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int mbps)
{
const struct rcsi2_cphy_setting *conf;
+ int msps;
+
+ /* Adjust for C-PHY symbols, divide by 2.8. */
+ msps = div_u64(mbps * 5, 14);
for (conf = cphy_setting_table_r8a779g0; conf->msps != 0; conf++) {
if (conf->msps > msps)
@@ -1214,7 +1225,7 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
if (!conf->msps) {
dev_err(priv->dev, "Unsupported PHY speed for msps setting (%u Msps)", msps);
- return -ERANGE;
+ return NULL;
}
/* C-PHY specific */
@@ -1246,11 +1257,11 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_RX_REG(2), conf->rx2);
rcsi2_write16(priv, V4H_CORE_DIG_CLANE_2_RW_HS_RX_REG(2), conf->rx2);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(2), 0x0001);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_REG(2), 0);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_REG(2), 0x0001);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_REG(2), 0x0001);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_REG(2), 0);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 2), 0);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(3, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(4, 2), 0);
rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO0_REG(0), conf->trio0);
rcsi2_write16(priv, V4H_CORE_DIG_RW_TRIO1_REG(0), conf->trio0);
@@ -1267,30 +1278,198 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
/* 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));
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 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));
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 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));
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 9));
/* TODO: This registers is not documented. */
rcsi2_write16(priv, V4H_CORE_DIG_CLANE_1_RW_HS_TX_6_REG, 0x5000);
- /* Leave Shutdown mode */
- rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0));
- rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0));
+ return conf;
+}
- /* Wait for calibration */
- if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) {
- dev_err(priv->dev, "PHY calibration failed\n");
- return -ETIMEDOUT;
+struct rcsi2_d_phy_setting_v4h_lut_value {
+ unsigned int mbps;
+ unsigned char cfg_1;
+ unsigned char cfg_5_94;
+ unsigned char cfg_5_30;
+ unsigned char lane_ctrl_2_8;
+ unsigned char rw_hs_rx_3_83;
+ unsigned char rw_hs_rx_3_20;
+ unsigned char rw_hs_rx_6;
+ unsigned char rw_hs_rx_1;
+};
+
+static const struct rcsi2_d_phy_setting_v4h_lut_value *
+rcsi2_d_phy_setting_v4h_lut_lookup(int mbps)
+{
+ static const struct rcsi2_d_phy_setting_v4h_lut_value values[] = {
+ { 4500, 0x3f, 0x07, 0x00, 0x01, 0x02, 0x01, 0x0d, 0x10 },
+ { 4000, 0x47, 0x08, 0x01, 0x01, 0x05, 0x01, 0x0f, 0x0d },
+ { 3600, 0x4f, 0x09, 0x01, 0x01, 0x06, 0x01, 0x10, 0x0b },
+ { 3230, 0x57, 0x0a, 0x01, 0x01, 0x06, 0x01, 0x12, 0x09 },
+ { 3000, 0x47, 0x08, 0x00, 0x00, 0x03, 0x01, 0x0f, 0x0c },
+ { 2700, 0x4f, 0x09, 0x01, 0x00, 0x06, 0x01, 0x10, 0x0b },
+ { 2455, 0x57, 0x0a, 0x01, 0x00, 0x06, 0x01, 0x12, 0x09 },
+ { 2250, 0x5f, 0x0b, 0x01, 0x00, 0x08, 0x01, 0x13, 0x08 },
+ { 2077, 0x67, 0x0c, 0x01, 0x00, 0x06, 0x02, 0x15, 0x0d },
+ { 1929, 0x6f, 0x0d, 0x02, 0x00, 0x06, 0x02, 0x17, 0x0d },
+ { 1800, 0x77, 0x0e, 0x02, 0x00, 0x06, 0x02, 0x18, 0x0d },
+ { 1688, 0x7f, 0x0f, 0x02, 0x00, 0x08, 0x02, 0x1a, 0x0d },
+ { 1588, 0x87, 0x10, 0x02, 0x00, 0x08, 0x02, 0x1b, 0x0d },
+ { 1500, 0x8f, 0x11, 0x03, 0x00, 0x08, 0x02, 0x1d, 0x0c },
+ };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(values); i++)
+ if (mbps >= values[i].mbps)
+ return &values[i];
+
+ return NULL;
+}
+
+static int rcsi2_d_phy_setting_v4h(struct rcar_csi2 *priv, int mbps)
+{
+ const struct rcsi2_d_phy_setting_v4h_lut_value *lut =
+ rcsi2_d_phy_setting_v4h_lut_lookup(mbps);
+ u16 val;
+
+ rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(7), 0x0000);
+ rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(7), mbps > 1500 ? 0x0028 : 0x0068);
+ rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(8), 0x0050);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(0), 0x0063);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(7), 0x1132);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(1), 0x1340);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(2), 0x4b13);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(4), 0x000a);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(6), 0x800a);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(7), 0x1109);
+
+ if (mbps > 1500) {
+ val = DIV_ROUND_UP(5 * mbps, 64);
+ rcsi2_write16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(3), val);
+ }
+
+ if (lut) {
+ rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(1),
+ lut->cfg_1, 0x00ff);
+ rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(5),
+ lut->cfg_5_94 << 4, 0x03f0);
+ rcsi2_modify16(priv, V4H_PPI_RW_DDLCAL_CFG_n_REG(5),
+ lut->cfg_5_30 << 0, 0x000f);
+
+ for (unsigned int l = 0; l < 5; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 8),
+ lut->lane_ctrl_2_8 << 12, 0x1000);
+ }
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, 0), 0x463c);
+
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(0, 2), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(1, 2), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 2), 0x0001);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(3, 2), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(4, 2), 0x0000);
+
+ rcsi2_write16(priv, V4H_CORE_DIG_RW_COMMON_REG(6), 0x0009);
+
+ val = mbps > 1500 ? 0x0800 : 0x0802;
+ for (unsigned int l = 0; l < 5; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 12), val);
+
+ val = mbps > 1500 ? 0x0000 : 0x0002;
+ for (unsigned int l = 0; l < 5; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 13), val);
+
+ if (mbps >= 80) {
+ /* 2560: 6, 1280: 5, 640: 4, 320: 3, 160: 2, 80: 1 */
+ val = ilog2(mbps / 80) + 1;
+ rcsi2_modify16(priv,
+ V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(2, 9),
+ val << 5, 0xe0);
+ }
+
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(0), 0x091c);
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_HS_RX_n_REG(7), 0x3b06);
+
+ val = DIV_ROUND_UP(1200, mbps) + 12;
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 0), val << 8, 0xf0);
+
+ val = mbps > 1500 ? 0x0004 : 0x0008;
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_CFG_n_REG(l, 1), val);
+
+ val = mbps > 2500 ? 0x669a : mbps > 1500 ? 0xe69a : 0xe69b;
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 2), val);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_LP_n_REG(l, 0), 0x163c);
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_CLK_RW_LP_n_REG(0), 0x163c);
+
+ if (lut) {
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 1),
+ lut->rw_hs_rx_1, 0xff);
+ }
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 3), 0x9209);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 4), 0x0096);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 5), 0x0100);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 6), 0x2d02);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_write16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 7), 0x1b06);
+
+ if (lut) {
+ /*
+ * Documentation LUT have two values but document writing both
+ * values in a single write.
+ */
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 3),
+ lut->rw_hs_rx_3_83 << 3 | lut->rw_hs_rx_3_20, 0x1ff);
+
+ for (unsigned int l = 0; l < 4; l++)
+ rcsi2_modify16(priv, V4H_CORE_DIG_DLANE_l_RW_HS_RX_n_REG(l, 6),
+ lut->rw_hs_rx_6 << 8, 0xff00);
}
- /* C-PHY setting - analog programing*/
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(9), conf->lane29);
- rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_REG(7), conf->lane27);
+ static const u16 deskew_fine[] = {
+ 0x0404, 0x040c, 0x0414, 0x041c, 0x0423, 0x0429, 0x0430, 0x043a,
+ 0x0445, 0x044a, 0x0450, 0x045a, 0x0465, 0x0469, 0x0472, 0x047a,
+ 0x0485, 0x0489, 0x0490, 0x049a, 0x04a4, 0x04ac, 0x04b4, 0x04bc,
+ 0x04c4, 0x04cc, 0x04d4, 0x04dc, 0x04e4, 0x04ec, 0x04f4, 0x04fc,
+ 0x0504, 0x050c, 0x0514, 0x051c, 0x0523, 0x0529, 0x0530, 0x053a,
+ 0x0545, 0x054a, 0x0550, 0x055a, 0x0565, 0x0569, 0x0572, 0x057a,
+ 0x0585, 0x0589, 0x0590, 0x059a, 0x05a4, 0x05ac, 0x05b4, 0x05bc,
+ 0x05c4, 0x05cc, 0x05d4, 0x05dc, 0x05e4, 0x05ec, 0x05f4, 0x05fc,
+ 0x0604, 0x060c, 0x0614, 0x061c, 0x0623, 0x0629, 0x0632, 0x063a,
+ 0x0645, 0x064a, 0x0650, 0x065a, 0x0665, 0x0669, 0x0672, 0x067a,
+ 0x0685, 0x0689, 0x0690, 0x069a, 0x06a4, 0x06ac, 0x06b4, 0x06bc,
+ 0x06c4, 0x06cc, 0x06d4, 0x06dc, 0x06e4, 0x06ec, 0x06f4, 0x06fc,
+ 0x0704, 0x070c, 0x0714, 0x071c, 0x0723, 0x072a, 0x0730, 0x073a,
+ 0x0745, 0x074a, 0x0750, 0x075a, 0x0765, 0x0769, 0x0772, 0x077a,
+ 0x0785, 0x0789, 0x0790, 0x079a, 0x07a4, 0x07ac, 0x07b4, 0x07bc,
+ 0x07c4, 0x07cc, 0x07d4, 0x07dc, 0x07e4, 0x07ec, 0x07f4, 0x07fc,
+ };
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(deskew_fine); i++) {
+ rcsi2_write16(priv, V4H_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM_REG,
+ deskew_fine[i]);
+ }
return 0;
}
@@ -1298,10 +1477,11 @@ static int rcsi2_c_phy_setting_v4h(struct rcar_csi2 *priv, int msps)
static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
struct v4l2_subdev_state *state)
{
+ const struct rcsi2_cphy_setting *cphy = NULL;
const struct rcar_csi2_format *format;
const struct v4l2_mbus_framefmt *fmt;
unsigned int lanes;
- int msps;
+ int mbps;
int ret;
/* Use the format on the sink pad to compute the receiver config. */
@@ -1314,28 +1494,40 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
if (ret)
return ret;
- msps = rcsi2_calc_mbps(priv, format->bpp, lanes);
- if (msps < 0)
- return msps;
+ mbps = rcsi2_calc_mbps(priv, format->bpp, lanes);
+ if (mbps < 0)
+ return mbps;
- /* Reset LINK and PHY*/
+ /* T0: 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);
- /* PHY static setting */
- rcsi2_write(priv, V4H_PHY_EN_REG, V4H_PHY_EN_ENABLE_CLK);
+ /* T1: PHY static setting */
+ 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);
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_CPHY);
+ rcsi2_write(priv, V4H_PHY_MODE_REG,
+ priv->cphy ? V4H_PHY_MODE_CPHY : V4H_PHY_MODE_DPHY);
rcsi2_write(priv, V4H_N_LANES_REG, lanes - 1);
- /* Reset CSI2 */
+ 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);
+
+ /* T2: Reset CSI2 */
rcsi2_write(priv, V4H_CSI2_RESETN_REG, BIT(0));
/* Registers static setting through APB */
/* Common setting */
+ rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(10), 0x0030);
+ rcsi2_write16(priv, V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(2), 0x1444);
rcsi2_write16(priv, V4H_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_REG(0), 0x1bfd);
rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_STARTUP_1_1_REG, 0x0233);
rcsi2_write16(priv, V4H_PPI_STARTUP_RW_COMMON_DPHY_REG(6), 0x0027);
@@ -1350,20 +1542,71 @@ static int rcsi2_start_receiver_v4h(struct rcar_csi2 *priv,
rcsi2_write16(priv, V4H_PPI_RW_LPDCOCAL_COARSE_CFG_REG, 0x0105);
rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x1000);
rcsi2_write16(priv, V4H_PPI_RW_COMMON_CFG_REG, 0x0003);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(0), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(1), 0x0400);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(3), 0x41f6);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(0), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(3), 0x43f6);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x3000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(7), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(6), 0x7000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(7), 0x0000);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_REG(5), 0x4000);
+
+ /* T3: PHY settings */
+ if (priv->cphy) {
+ cphy = rcsi2_c_phy_setting_v4h(priv, mbps);
+ if (!cphy)
+ return -ERANGE;
+ } else {
+ ret = rcsi2_d_phy_setting_v4h(priv, mbps);
+ if (ret)
+ return ret;
+ }
- /* C-PHY settings */
- ret = rcsi2_c_phy_setting_v4h(priv, msps);
- if (ret)
- return ret;
+ /* T4: Leave Shutdown mode */
+ rcsi2_write(priv, V4H_DPHY_RSTZ_REG, BIT(0));
+ rcsi2_write(priv, V4H_PHY_SHUTDOWNZ_REG, BIT(0));
+
+ /* T5: Wait for calibration */
+ if (rcsi2_wait_phy_start_v4h(priv, V4H_ST_PHYST_ST_PHY_READY)) {
+ dev_err(priv->dev, "PHY calibration failed\n");
+ return -ETIMEDOUT;
+ }
+
+ /* T6: Analog programming */
+ if (priv->cphy) {
+ for (unsigned int l = 0; l < 3; l++) {
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 9),
+ cphy->lane29);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 7),
+ cphy->lane27);
+ }
+ } else {
+ u16 val_2_9 = mbps > 2500 ? 0x14 : mbps > 1500 ? 0x04 : 0x00;
+ u16 val_2_15 = mbps > 1500 ? 0x03 : 0x00;
+
+ for (unsigned int l = 0; l < 5; l++) {
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 9),
+ val_2_9);
+ rcsi2_write16(priv, V4H_CORE_DIG_IOCTRL_RW_AFE_LANEl_CTRL_2_REG(l, 15),
+ val_2_15);
+ }
+ }
+ /* T7: Wait for stop state */
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_2 |
+ V4H_ST_PHYST_ST_STOPSTATE_3);
+
+ /* T8: De-assert FRXM */
+ rcsi2_write(priv, V4M_FRXM_REG, 0);
return 0;
}
-static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int data_rate)
+static int rcsi2_d_phy_setting_v4m(struct rcar_csi2 *priv, int mbps)
{
unsigned int timeout;
int ret;
@@ -2213,6 +2456,7 @@ static const struct rcar_csi2_info rcar_csi2_info_r8a779g0 = {
.start_receiver = rcsi2_start_receiver_v4h,
.use_isp = true,
.support_cphy = true,
+ .support_dphy = true,
};
static const struct rcsi2_register_layout rcsi2_registers_v4m = {
diff --git a/drivers/media/platform/renesas/rcar-fcp.c b/drivers/media/platform/renesas/rcar-fcp.c
index cee9bbce4e3a..f90c86bbce6e 100644
--- a/drivers/media/platform/renesas/rcar-fcp.c
+++ b/drivers/media/platform/renesas/rcar-fcp.c
@@ -9,6 +9,8 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@@ -19,14 +21,25 @@
#include <media/rcar-fcp.h>
+#define RCAR_FCP_REG_RST 0x0010
+#define RCAR_FCP_REG_RST_SOFTRST BIT(0)
+#define RCAR_FCP_REG_STA 0x0018
+#define RCAR_FCP_REG_STA_ACT BIT(0)
+
struct rcar_fcp_device {
struct list_head list;
struct device *dev;
+ void __iomem *base;
};
static LIST_HEAD(fcp_devices);
static DEFINE_MUTEX(fcp_lock);
+static inline void rcar_fcp_write(struct rcar_fcp_device *fcp, u32 reg, u32 val)
+{
+ iowrite32(val, fcp->base + reg);
+}
+
/* -----------------------------------------------------------------------------
* Public API
*/
@@ -117,6 +130,25 @@ void rcar_fcp_disable(struct rcar_fcp_device *fcp)
}
EXPORT_SYMBOL_GPL(rcar_fcp_disable);
+int rcar_fcp_soft_reset(struct rcar_fcp_device *fcp)
+{
+ u32 value;
+ int ret;
+
+ if (!fcp)
+ return 0;
+
+ rcar_fcp_write(fcp, RCAR_FCP_REG_RST, RCAR_FCP_REG_RST_SOFTRST);
+ ret = readl_poll_timeout(fcp->base + RCAR_FCP_REG_STA,
+ value, !(value & RCAR_FCP_REG_STA_ACT),
+ 1, 100);
+ if (ret)
+ dev_err(fcp->dev, "Failed to soft-reset\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rcar_fcp_soft_reset);
+
/* -----------------------------------------------------------------------------
* Platform Driver
*/
@@ -131,6 +163,10 @@ static int rcar_fcp_probe(struct platform_device *pdev)
fcp->dev = &pdev->dev;
+ fcp->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(fcp->base))
+ return PTR_ERR(fcp->base);
+
dma_set_max_seg_size(fcp->dev, UINT_MAX);
pm_runtime_enable(&pdev->dev);
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
index 846ae7989b1d..f73729f59671 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
@@ -2,14 +2,14 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
+#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_graph.h>
@@ -55,6 +55,7 @@
* be only one group for all instances.
*/
+static DEFINE_IDA(rvin_ida);
static DEFINE_MUTEX(rvin_group_lock);
static struct rvin_group *rvin_group_data;
@@ -65,7 +66,7 @@ static void rvin_group_cleanup(struct rvin_group *group)
}
static int rvin_group_init(struct rvin_group *group, struct rvin_dev *vin,
- int (*link_setup)(struct rvin_dev *),
+ int (*link_setup)(struct rvin_group *),
const struct media_device_ops *ops)
{
struct media_device *mdev = &group->mdev;
@@ -115,27 +116,12 @@ static void rvin_group_release(struct kref *kref)
}
static int rvin_group_get(struct rvin_dev *vin,
- int (*link_setup)(struct rvin_dev *),
+ int (*link_setup)(struct rvin_group *),
const struct media_device_ops *ops)
{
struct rvin_group *group;
- u32 id;
int ret;
- /* Make sure VIN id is present and sane */
- ret = of_property_read_u32(vin->dev->of_node, "renesas,id", &id);
- if (ret) {
- vin_err(vin, "%pOF: No renesas,id property found\n",
- vin->dev->of_node);
- return -EINVAL;
- }
-
- if (id >= RCAR_VIN_NUM) {
- vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
- vin->dev->of_node, id);
- return -EINVAL;
- }
-
/* Join or create a VIN group */
mutex_lock(&rvin_group_lock);
if (rvin_group_data) {
@@ -156,6 +142,7 @@ static int rvin_group_get(struct rvin_dev *vin,
}
kref_init(&group->refcount);
+ group->info = vin->info;
rvin_group_data = group;
}
@@ -164,16 +151,15 @@ static int rvin_group_get(struct rvin_dev *vin,
/* Add VIN to group */
mutex_lock(&group->lock);
- if (group->vin[id]) {
- vin_err(vin, "Duplicate renesas,id property value %u\n", id);
+ if (group->vin[vin->id]) {
+ vin_err(vin, "Duplicate renesas,id property value %u\n", vin->id);
mutex_unlock(&group->lock);
kref_put(&group->refcount, rvin_group_release);
return -EINVAL;
}
- group->vin[id] = vin;
+ group->vin[vin->id] = vin;
- vin->id = id;
vin->group = group;
vin->v4l2_dev.mdev = &group->mdev;
@@ -213,7 +199,7 @@ static int rvin_group_entity_to_remote_id(struct rvin_group *group,
sd = media_entity_to_v4l2_subdev(entity);
- for (i = 0; i < RVIN_REMOTES_MAX; i++)
+ for (i = 0; i < ARRAY_SIZE(group->remotes); i++)
if (group->remotes[i].subdev == sd)
return i;
@@ -246,7 +232,7 @@ static int rvin_group_notify_complete(struct v4l2_async_notifier *notifier)
}
}
- return vin->group->link_setup(vin);
+ return vin->group->link_setup(vin->group);
}
static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
@@ -254,20 +240,32 @@ static void rvin_group_notify_unbind(struct v4l2_async_notifier *notifier,
struct v4l2_async_connection *asc)
{
struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- unsigned int i;
+ struct rvin_group *group = vin->group;
- for (i = 0; i < RCAR_VIN_NUM; i++)
- if (vin->group->vin[i])
- rvin_v4l2_unregister(vin->group->vin[i]);
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ if (group->vin[i])
+ rvin_v4l2_unregister(group->vin[i]);
+ }
mutex_lock(&vin->group->lock);
- for (i = 0; i < RVIN_CSI_MAX; i++) {
- if (vin->group->remotes[i].asc != asc)
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ if (!group->vin[i] || group->vin[i]->parallel.asc != asc)
continue;
- vin->group->remotes[i].subdev = NULL;
+
+ group->vin[i]->parallel.subdev = NULL;
+
+ vin_dbg(group->vin[i], "Unbind parallel subdev %s\n",
+ subdev->name);
+ }
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) {
+ if (group->remotes[i].asc != asc)
+ continue;
+
+ group->remotes[i].subdev = NULL;
+
vin_dbg(vin, "Unbind %s from slot %u\n", subdev->name, i);
- break;
}
mutex_unlock(&vin->group->lock);
@@ -280,21 +278,38 @@ static int rvin_group_notify_bound(struct v4l2_async_notifier *notifier,
struct v4l2_async_connection *asc)
{
struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- unsigned int i;
+ struct rvin_group *group = vin->group;
- mutex_lock(&vin->group->lock);
+ guard(mutex)(&group->lock);
- for (i = 0; i < RVIN_CSI_MAX; i++) {
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ struct rvin_dev *pvin = group->vin[i];
+
+ if (!pvin || pvin->parallel.asc != asc)
+ continue;
+
+ pvin->parallel.source_pad = 0;
+ for (unsigned int pad = 0; pad < subdev->entity.num_pads; pad++)
+ if (subdev->entity.pads[pad].flags & MEDIA_PAD_FL_SOURCE)
+ pvin->parallel.source_pad = pad;
+
+ pvin->parallel.subdev = subdev;
+ vin_dbg(pvin, "Bound subdev %s\n", subdev->name);
+
+ return 0;
+ }
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) {
if (vin->group->remotes[i].asc != asc)
continue;
+
vin->group->remotes[i].subdev = subdev;
vin_dbg(vin, "Bound %s to slot %u\n", subdev->name, i);
- break;
- }
- mutex_unlock(&vin->group->lock);
+ return 0;
+ }
- return 0;
+ return -ENODEV;
}
static const struct v4l2_async_notifier_operations rvin_group_notify_ops = {
@@ -343,12 +358,49 @@ out:
return ret;
}
-static void rvin_group_notifier_cleanup(struct rvin_dev *vin)
+static int rvin_parallel_parse_of(struct rvin_dev *vin)
{
- if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) {
- v4l2_async_nf_unregister(&vin->group->notifier);
- v4l2_async_nf_cleanup(&vin->group->notifier);
+ struct fwnode_handle *fwnode __free(fwnode_handle) = NULL;
+ struct fwnode_handle *ep __free(fwnode_handle) = NULL;
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_UNKNOWN,
+ };
+ struct v4l2_async_connection *asc;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0);
+ if (!ep)
+ return 0;
+
+ if (v4l2_fwnode_endpoint_parse(ep, &vep)) {
+ vin_err(vin, "Failed to parse %pOF\n", to_of_node(ep));
+ return -EINVAL;
}
+
+ switch (vep.bus_type) {
+ case V4L2_MBUS_PARALLEL:
+ case V4L2_MBUS_BT656:
+ vin_dbg(vin, "Found %s media bus\n",
+ vep.bus_type == V4L2_MBUS_PARALLEL ?
+ "PARALLEL" : "BT656");
+ vin->parallel.mbus_type = vep.bus_type;
+ vin->parallel.bus = vep.bus.parallel;
+ break;
+ default:
+ vin_err(vin, "Unknown media bus type\n");
+ return -EINVAL;
+ }
+
+ fwnode = fwnode_graph_get_remote_endpoint(ep);
+ asc = v4l2_async_nf_add_fwnode(&vin->group->notifier, fwnode,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc))
+ return PTR_ERR(asc);
+
+ vin->parallel.asc = asc;
+
+ vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode));
+
+ return 0;
}
static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port,
@@ -385,6 +437,12 @@ static int rvin_group_notifier_init(struct rvin_dev *vin, unsigned int port,
if (!(vin_mask & BIT(i)))
continue;
+ /* Parse local subdevice. */
+ ret = rvin_parallel_parse_of(vin->group->vin[i]);
+ if (ret)
+ return ret;
+
+ /* Parse shared subdevices. */
for (id = 0; id < max_id; id++) {
if (vin->group->remotes[id].asc)
continue;
@@ -437,11 +495,11 @@ static void rvin_free_controls(struct rvin_dev *vin)
vin->vdev.ctrl_handler = NULL;
}
-static int rvin_create_controls(struct rvin_dev *vin, struct v4l2_subdev *subdev)
+static int rvin_create_controls(struct rvin_dev *vin)
{
int ret;
- ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 16);
+ ret = v4l2_ctrl_handler_init(&vin->ctrl_handler, 1);
if (ret < 0)
return ret;
@@ -455,287 +513,12 @@ static int rvin_create_controls(struct rvin_dev *vin, struct v4l2_subdev *subdev
return ret;
}
- /* For the non-MC mode add controls from the subdevice. */
- if (subdev) {
- ret = v4l2_ctrl_add_handler(&vin->ctrl_handler,
- subdev->ctrl_handler, NULL, true);
- if (ret < 0) {
- rvin_free_controls(vin);
- return ret;
- }
- }
-
vin->vdev.ctrl_handler = &vin->ctrl_handler;
return 0;
}
/* -----------------------------------------------------------------------------
- * Async notifier
- */
-
-static int rvin_find_pad(struct v4l2_subdev *sd, int direction)
-{
- unsigned int pad;
-
- if (sd->entity.num_pads <= 1)
- return 0;
-
- for (pad = 0; pad < sd->entity.num_pads; pad++)
- if (sd->entity.pads[pad].flags & direction)
- return pad;
-
- return -EINVAL;
-}
-
-/* -----------------------------------------------------------------------------
- * Parallel async notifier
- */
-
-/* The vin lock should be held when calling the subdevice attach and detach */
-static int rvin_parallel_subdevice_attach(struct rvin_dev *vin,
- struct v4l2_subdev *subdev)
-{
- struct v4l2_subdev_mbus_code_enum code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- int ret;
-
- /* Find source and sink pad of remote subdevice */
- ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SOURCE);
- if (ret < 0)
- return ret;
- vin->parallel.source_pad = ret;
-
- ret = rvin_find_pad(subdev, MEDIA_PAD_FL_SINK);
- vin->parallel.sink_pad = ret < 0 ? 0 : ret;
-
- if (vin->info->use_mc) {
- vin->parallel.subdev = subdev;
- return 0;
- }
-
- /* Find compatible subdevices mbus format */
- vin->mbus_code = 0;
- code.index = 0;
- code.pad = vin->parallel.source_pad;
- while (!vin->mbus_code &&
- !v4l2_subdev_call(subdev, pad, enum_mbus_code, NULL, &code)) {
- code.index++;
- switch (code.code) {
- case MEDIA_BUS_FMT_YUYV8_1X16:
- case MEDIA_BUS_FMT_UYVY8_1X16:
- case MEDIA_BUS_FMT_UYVY8_2X8:
- case MEDIA_BUS_FMT_UYVY10_2X10:
- case MEDIA_BUS_FMT_RGB888_1X24:
- vin->mbus_code = code.code;
- vin_dbg(vin, "Found media bus format for %s: %d\n",
- subdev->name, vin->mbus_code);
- break;
- default:
- break;
- }
- }
-
- if (!vin->mbus_code) {
- vin_err(vin, "Unsupported media bus format for %s\n",
- subdev->name);
- return -EINVAL;
- }
-
- /* Read tvnorms */
- ret = v4l2_subdev_call(subdev, video, g_tvnorms, &vin->vdev.tvnorms);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
-
- /* Read standard */
- vin->std = V4L2_STD_UNKNOWN;
- ret = v4l2_subdev_call(subdev, video, g_std, &vin->std);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- return ret;
-
- /* Add the controls */
- ret = rvin_create_controls(vin, subdev);
- if (ret < 0)
- return ret;
-
- vin->parallel.subdev = subdev;
-
- return 0;
-}
-
-static void rvin_parallel_subdevice_detach(struct rvin_dev *vin)
-{
- rvin_v4l2_unregister(vin);
- vin->parallel.subdev = NULL;
-
- if (!vin->info->use_mc)
- rvin_free_controls(vin);
-}
-
-static int rvin_parallel_notify_complete(struct v4l2_async_notifier *notifier)
-{
- struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- struct media_entity *source;
- struct media_entity *sink;
- int ret;
-
- ret = v4l2_device_register_subdev_nodes(&vin->v4l2_dev);
- if (ret < 0) {
- vin_err(vin, "Failed to register subdev nodes\n");
- return ret;
- }
-
- if (!video_is_registered(&vin->vdev)) {
- ret = rvin_v4l2_register(vin);
- if (ret < 0)
- return ret;
- }
-
- if (!vin->info->use_mc)
- return 0;
-
- /* If we're running with media-controller, link the subdevs. */
- source = &vin->parallel.subdev->entity;
- sink = &vin->vdev.entity;
-
- ret = media_create_pad_link(source, vin->parallel.source_pad,
- sink, vin->parallel.sink_pad, 0);
- if (ret)
- vin_err(vin, "Error adding link from %s to %s: %d\n",
- source->name, sink->name, ret);
-
- return ret;
-}
-
-static void rvin_parallel_notify_unbind(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_connection *asc)
-{
- struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
-
- vin_dbg(vin, "unbind parallel subdev %s\n", subdev->name);
-
- mutex_lock(&vin->lock);
- rvin_parallel_subdevice_detach(vin);
- mutex_unlock(&vin->lock);
-}
-
-static int rvin_parallel_notify_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_connection *asc)
-{
- struct rvin_dev *vin = v4l2_dev_to_vin(notifier->v4l2_dev);
- int ret;
-
- mutex_lock(&vin->lock);
- ret = rvin_parallel_subdevice_attach(vin, subdev);
- mutex_unlock(&vin->lock);
- if (ret)
- return ret;
-
- v4l2_set_subdev_hostdata(subdev, vin);
-
- vin_dbg(vin, "bound subdev %s source pad: %u sink pad: %u\n",
- subdev->name, vin->parallel.source_pad,
- vin->parallel.sink_pad);
-
- return 0;
-}
-
-static const struct v4l2_async_notifier_operations rvin_parallel_notify_ops = {
- .bound = rvin_parallel_notify_bound,
- .unbind = rvin_parallel_notify_unbind,
- .complete = rvin_parallel_notify_complete,
-};
-
-static int rvin_parallel_parse_of(struct rvin_dev *vin)
-{
- struct fwnode_handle *ep, *fwnode;
- struct v4l2_fwnode_endpoint vep = {
- .bus_type = V4L2_MBUS_UNKNOWN,
- };
- struct v4l2_async_connection *asc;
- int ret;
-
- ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(vin->dev), 0, 0, 0);
- if (!ep)
- return 0;
-
- fwnode = fwnode_graph_get_remote_endpoint(ep);
- ret = v4l2_fwnode_endpoint_parse(ep, &vep);
- fwnode_handle_put(ep);
- if (ret) {
- vin_err(vin, "Failed to parse %pOF\n", to_of_node(fwnode));
- ret = -EINVAL;
- goto out;
- }
-
- switch (vep.bus_type) {
- case V4L2_MBUS_PARALLEL:
- case V4L2_MBUS_BT656:
- vin_dbg(vin, "Found %s media bus\n",
- vep.bus_type == V4L2_MBUS_PARALLEL ?
- "PARALLEL" : "BT656");
- vin->parallel.mbus_type = vep.bus_type;
- vin->parallel.bus = vep.bus.parallel;
- break;
- default:
- vin_err(vin, "Unknown media bus type\n");
- ret = -EINVAL;
- goto out;
- }
-
- asc = v4l2_async_nf_add_fwnode(&vin->notifier, fwnode,
- struct v4l2_async_connection);
- if (IS_ERR(asc)) {
- ret = PTR_ERR(asc);
- goto out;
- }
-
- vin->parallel.asc = asc;
-
- vin_dbg(vin, "Add parallel OF device %pOF\n", to_of_node(fwnode));
-out:
- fwnode_handle_put(fwnode);
-
- return ret;
-}
-
-static void rvin_parallel_cleanup(struct rvin_dev *vin)
-{
- v4l2_async_nf_unregister(&vin->notifier);
- v4l2_async_nf_cleanup(&vin->notifier);
-}
-
-static int rvin_parallel_init(struct rvin_dev *vin)
-{
- int ret;
-
- v4l2_async_nf_init(&vin->notifier, &vin->v4l2_dev);
-
- ret = rvin_parallel_parse_of(vin);
- if (ret)
- return ret;
-
- if (!vin->parallel.asc)
- return -ENODEV;
-
- vin_dbg(vin, "Found parallel subdevice %pOF\n",
- to_of_node(vin->parallel.asc->match.fwnode));
-
- vin->notifier.ops = &rvin_parallel_notify_ops;
- ret = v4l2_async_nf_register(&vin->notifier);
- if (ret < 0) {
- vin_err(vin, "Notifier registration failed\n");
- v4l2_async_nf_cleanup(&vin->notifier);
- return ret;
- }
-
- return 0;
-}
-
-/* -----------------------------------------------------------------------------
* CSI-2
*/
@@ -909,80 +692,91 @@ static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id,
return 0;
}
-static int rvin_csi2_setup_links(struct rvin_dev *vin)
+static int rvin_parallel_setup_links(struct rvin_group *group)
+{
+ u32 flags = MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE;
+
+ guard(mutex)(&group->lock);
+
+ /* If the group also has links don't enable the link. */
+ for (unsigned int i = 0; i < ARRAY_SIZE(group->remotes); i++) {
+ if (group->remotes[i].subdev) {
+ flags = 0;
+ break;
+ }
+ }
+
+ /* Create links. */
+ for (unsigned int i = 0; i < RCAR_VIN_NUM; i++) {
+ struct rvin_dev *vin = group->vin[i];
+ struct media_entity *source;
+ struct media_entity *sink;
+ int ret;
+
+ /* Nothing to do if there is no VIN or parallel subdev. */
+ if (!vin || !vin->parallel.subdev)
+ continue;
+
+ source = &vin->parallel.subdev->entity;
+ sink = &vin->vdev.entity;
+
+ ret = media_create_pad_link(source, vin->parallel.source_pad,
+ sink, 0, flags);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rvin_csi2_setup_links(struct rvin_group *group)
{
const struct rvin_group_route *route;
unsigned int id;
- int ret = -EINVAL;
+ int ret;
+
+ ret = rvin_parallel_setup_links(group);
+ if (ret)
+ return ret;
/* Create all media device links between VINs and CSI-2's. */
- mutex_lock(&vin->group->lock);
- for (route = vin->info->routes; route->chsel; route++) {
+ mutex_lock(&group->lock);
+ for (route = group->info->routes; route->chsel; route++) {
/* Check that VIN' master is part of the group. */
- if (!vin->group->vin[route->master])
+ if (!group->vin[route->master])
continue;
/* Check that CSI-2 is part of the group. */
- if (!vin->group->remotes[route->csi].subdev)
+ if (!group->remotes[route->csi].subdev)
continue;
for (id = route->master; id < route->master + 4; id++) {
/* Check that VIN is part of the group. */
- if (!vin->group->vin[id])
+ if (!group->vin[id])
continue;
- ret = rvin_csi2_create_link(vin->group, id, route);
+ ret = rvin_csi2_create_link(group, id, route);
if (ret)
goto out;
}
}
out:
- mutex_unlock(&vin->group->lock);
+ mutex_unlock(&group->lock);
return ret;
}
-static void rvin_csi2_cleanup(struct rvin_dev *vin)
-{
- rvin_parallel_cleanup(vin);
- rvin_group_notifier_cleanup(vin);
- rvin_group_put(vin);
- rvin_free_controls(vin);
-}
-
static int rvin_csi2_init(struct rvin_dev *vin)
{
int ret;
- vin->pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
- if (ret)
- return ret;
-
- ret = rvin_create_controls(vin, NULL);
- if (ret < 0)
- return ret;
-
ret = rvin_group_get(vin, rvin_csi2_setup_links, &rvin_csi2_media_ops);
if (ret)
- goto err_controls;
-
- /* It's OK to not have a parallel subdevice. */
- ret = rvin_parallel_init(vin);
- if (ret && ret != -ENODEV)
- goto err_group;
+ return ret;
ret = rvin_group_notifier_init(vin, 1, RVIN_CSI_MAX);
if (ret)
- goto err_parallel;
-
- return 0;
-err_parallel:
- rvin_parallel_cleanup(vin);
-err_group:
- rvin_group_put(vin);
-err_controls:
- rvin_free_controls(vin);
+ rvin_group_put(vin);
return ret;
}
@@ -991,30 +785,31 @@ err_controls:
* ISP
*/
-static int rvin_isp_setup_links(struct rvin_dev *vin)
+static int rvin_isp_setup_links(struct rvin_group *group)
{
unsigned int i;
int ret = -EINVAL;
/* Create all media device links between VINs and ISP's. */
- mutex_lock(&vin->group->lock);
+ mutex_lock(&group->lock);
for (i = 0; i < RCAR_VIN_NUM; i++) {
struct media_pad *source_pad, *sink_pad;
struct media_entity *source, *sink;
unsigned int source_slot = i / 8;
unsigned int source_idx = i % 8 + 1;
+ struct rvin_dev *vin = group->vin[i];
- if (!vin->group->vin[i])
+ if (!vin)
continue;
/* Check that ISP is part of the group. */
- if (!vin->group->remotes[source_slot].subdev)
+ if (!group->remotes[source_slot].subdev)
continue;
- source = &vin->group->remotes[source_slot].subdev->entity;
+ source = &group->remotes[source_slot].subdev->entity;
source_pad = &source->pads[source_idx];
- sink = &vin->group->vin[i]->vdev.entity;
+ sink = &vin->vdev.entity;
sink_pad = &sink->pads[0];
/* Skip if link already exists. */
@@ -1030,44 +825,22 @@ static int rvin_isp_setup_links(struct rvin_dev *vin)
break;
}
}
- mutex_unlock(&vin->group->lock);
+ mutex_unlock(&group->lock);
return ret;
}
-static void rvin_isp_cleanup(struct rvin_dev *vin)
-{
- rvin_group_notifier_cleanup(vin);
- rvin_group_put(vin);
- rvin_free_controls(vin);
-}
-
static int rvin_isp_init(struct rvin_dev *vin)
{
int ret;
- vin->pad.flags = MEDIA_PAD_FL_SINK;
- ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
- if (ret)
- return ret;
-
- ret = rvin_create_controls(vin, NULL);
- if (ret < 0)
- return ret;
-
ret = rvin_group_get(vin, rvin_isp_setup_links, NULL);
if (ret)
- goto err_controls;
+ return ret;
ret = rvin_group_notifier_init(vin, 2, RVIN_ISP_MAX);
if (ret)
- goto err_group;
-
- return 0;
-err_group:
- rvin_group_put(vin);
-err_controls:
- rvin_free_controls(vin);
+ rvin_group_put(vin);
return ret;
}
@@ -1102,7 +875,7 @@ static int __maybe_unused rvin_resume(struct device *dev)
* as we don't know if and in which order the master VINs will
* be resumed.
*/
- if (vin->info->use_mc) {
+ if (vin->info->model == RCAR_GEN3) {
unsigned int master_id = rvin_group_id_to_master(vin->id);
struct rvin_dev *master = vin->group->vin[master_id];
int ret;
@@ -1124,7 +897,6 @@ static int __maybe_unused rvin_resume(struct device *dev)
static const struct rvin_info rcar_info_h1 = {
.model = RCAR_H1,
- .use_mc = false,
.max_width = 2048,
.max_height = 2048,
.scaler = rvin_scaler_gen2,
@@ -1132,7 +904,6 @@ static const struct rvin_info rcar_info_h1 = {
static const struct rvin_info rcar_info_m1 = {
.model = RCAR_M1,
- .use_mc = false,
.max_width = 2048,
.max_height = 2048,
.scaler = rvin_scaler_gen2,
@@ -1140,7 +911,6 @@ static const struct rvin_info rcar_info_m1 = {
static const struct rvin_info rcar_info_gen2 = {
.model = RCAR_GEN2,
- .use_mc = false,
.max_width = 2048,
.max_height = 2048,
.scaler = rvin_scaler_gen2,
@@ -1155,7 +925,6 @@ static const struct rvin_group_route rcar_info_r8a774e1_routes[] = {
static const struct rvin_info rcar_info_r8a774e1 = {
.model = RCAR_GEN3,
- .use_mc = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a774e1_routes,
@@ -1171,7 +940,6 @@ static const struct rvin_group_route rcar_info_r8a7795_routes[] = {
static const struct rvin_info rcar_info_r8a7795 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1189,7 +957,6 @@ static const struct rvin_group_route rcar_info_r8a7796_routes[] = {
static const struct rvin_info rcar_info_r8a7796 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1207,7 +974,6 @@ static const struct rvin_group_route rcar_info_r8a77965_routes[] = {
static const struct rvin_info rcar_info_r8a77965 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1222,7 +988,6 @@ static const struct rvin_group_route rcar_info_r8a77970_routes[] = {
static const struct rvin_info rcar_info_r8a77970 = {
.model = RCAR_GEN3,
- .use_mc = true,
.max_width = 4096,
.max_height = 4096,
.routes = rcar_info_r8a77970_routes,
@@ -1236,7 +1001,6 @@ static const struct rvin_group_route rcar_info_r8a77980_routes[] = {
static const struct rvin_info rcar_info_r8a77980 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1250,7 +1014,6 @@ static const struct rvin_group_route rcar_info_r8a77990_routes[] = {
static const struct rvin_info rcar_info_r8a77990 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1264,7 +1027,6 @@ static const struct rvin_group_route rcar_info_r8a77995_routes[] = {
static const struct rvin_info rcar_info_r8a77995 = {
.model = RCAR_GEN3,
- .use_mc = true,
.nv12 = true,
.max_width = 4096,
.max_height = 4096,
@@ -1274,7 +1036,6 @@ static const struct rvin_info rcar_info_r8a77995 = {
static const struct rvin_info rcar_info_gen4 = {
.model = RCAR_GEN4,
- .use_mc = true,
.use_isp = true,
.nv12 = true,
.raw10 = true,
@@ -1361,6 +1122,56 @@ static const struct of_device_id rvin_of_id_table[] = {
};
MODULE_DEVICE_TABLE(of, rvin_of_id_table);
+static int rvin_id_get(struct rvin_dev *vin)
+{
+ u32 oid;
+ int id;
+
+ switch (vin->info->model) {
+ case RCAR_GEN3:
+ case RCAR_GEN4:
+ if (of_property_read_u32(vin->dev->of_node, "renesas,id", &oid)) {
+ vin_err(vin, "%pOF: No renesas,id property found\n",
+ vin->dev->of_node);
+ return -EINVAL;
+ }
+
+ if (oid < 0 || oid >= RCAR_VIN_NUM) {
+ vin_err(vin, "%pOF: Invalid renesas,id '%u'\n",
+ vin->dev->of_node, oid);
+ return -EINVAL;
+ }
+
+ vin->id = oid;
+ break;
+ default:
+ id = ida_alloc_range(&rvin_ida, 0, RCAR_VIN_NUM - 1,
+ GFP_KERNEL);
+ if (id < 0) {
+ vin_err(vin, "%pOF: Failed to allocate VIN group ID\n",
+ vin->dev->of_node);
+ return -EINVAL;
+ }
+
+ vin->id = id;
+ break;
+ }
+
+ return 0;
+}
+
+static void rvin_id_put(struct rvin_dev *vin)
+{
+ switch (vin->info->model) {
+ case RCAR_GEN3:
+ case RCAR_GEN4:
+ break;
+ default:
+ ida_free(&rvin_ida, vin->id);
+ break;
+ }
+}
+
static int rcar_vin_probe(struct platform_device *pdev)
{
struct rvin_dev *vin;
@@ -1388,30 +1199,59 @@ static int rcar_vin_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, vin);
- if (vin->info->use_isp) {
- ret = rvin_isp_init(vin);
- } else if (vin->info->use_mc) {
- ret = rvin_csi2_init(vin);
+ if (rvin_id_get(vin)) {
+ ret = -EINVAL;
+ goto err_dma;
+ }
- if (vin->info->scaler &&
- rvin_group_id_to_master(vin->id) == vin->id)
- vin->scaler = vin->info->scaler;
- } else {
- ret = rvin_parallel_init(vin);
+ vin->pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&vin->vdev.entity, 1, &vin->pad);
+ if (ret)
+ goto err_id;
+
+ ret = rvin_create_controls(vin);
+ if (ret < 0)
+ goto err_id;
+
+ switch (vin->info->model) {
+ case RCAR_GEN3:
+ case RCAR_GEN4:
+ if (vin->info->use_isp) {
+ ret = rvin_isp_init(vin);
+ } else {
+ ret = rvin_csi2_init(vin);
+
+ if (vin->info->scaler &&
+ rvin_group_id_to_master(vin->id) == vin->id)
+ vin->scaler = vin->info->scaler;
+ }
+ break;
+ default:
+ ret = rvin_group_get(vin, rvin_parallel_setup_links, NULL);
+ if (!ret)
+ ret = rvin_group_notifier_init(vin, 0, 0);
if (vin->info->scaler)
vin->scaler = vin->info->scaler;
+ break;
}
- if (ret) {
- rvin_dma_unregister(vin);
- return ret;
- }
+ if (ret)
+ goto err_ctrl;
pm_suspend_ignore_children(&pdev->dev, true);
pm_runtime_enable(&pdev->dev);
return 0;
+
+err_ctrl:
+ rvin_free_controls(vin);
+err_id:
+ rvin_id_put(vin);
+err_dma:
+ rvin_dma_unregister(vin);
+
+ return ret;
}
static void rcar_vin_remove(struct platform_device *pdev)
@@ -1422,12 +1262,16 @@ static void rcar_vin_remove(struct platform_device *pdev)
rvin_v4l2_unregister(vin);
- if (vin->info->use_isp)
- rvin_isp_cleanup(vin);
- else if (vin->info->use_mc)
- rvin_csi2_cleanup(vin);
- else
- rvin_parallel_cleanup(vin);
+ if (&vin->v4l2_dev == vin->group->notifier.v4l2_dev) {
+ v4l2_async_nf_unregister(&vin->group->notifier);
+ v4l2_async_nf_cleanup(&vin->group->notifier);
+ }
+
+ rvin_group_put(vin);
+
+ rvin_free_controls(vin);
+
+ rvin_id_put(vin);
rvin_dma_unregister(vin);
}
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index 5c08ee2c9807..b619d1436a41 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -2,18 +2,18 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
+#include <media/v4l2-event.h>
#include <media/videobuf2-dma-contig.h>
#include "rcar-vin.h"
@@ -115,11 +115,16 @@
#define VNFC_S_FRAME (1 << 0)
/* Video n Interrupt Enable Register bits */
-#define VNIE_FIE (1 << 4)
-#define VNIE_EFE (1 << 1)
+#define VNIE_VFE BIT(17)
+#define VNIE_VRE BIT(16)
+#define VNIE_FIE BIT(4)
+#define VNIE_EFE BIT(1)
/* Video n Interrupt Status Register bits */
-#define VNINTS_FIS (1 << 4)
+#define VNINTS_VFS BIT(17)
+#define VNINTS_VRS BIT(16)
+#define VNINTS_FIS BIT(4)
+#define VNINTS_EFS BIT(1)
/* Video n Data Mode Register bits */
#define VNDMR_A8BIT(n) (((n) & 0xff) << 24)
@@ -555,17 +560,12 @@ static void rvin_set_coeff(struct rvin_dev *vin, unsigned short xs)
void rvin_scaler_gen2(struct rvin_dev *vin)
{
- unsigned int crop_height;
u32 xs, ys;
/* Set scaling coefficient */
- crop_height = vin->crop.height;
- if (V4L2_FIELD_HAS_BOTH(vin->format.field))
- crop_height *= 2;
-
ys = 0;
- if (crop_height != vin->compose.height)
- ys = (4096 * crop_height) / vin->compose.height;
+ if (vin->crop.height != vin->compose.height)
+ ys = (4096 * vin->crop.height) / vin->compose.height;
rvin_write(vin, ys, VNYS_REG);
xs = 0;
@@ -700,9 +700,6 @@ static int rvin_setup(struct rvin_dev *vin)
case V4L2_FIELD_INTERLACED:
/* Default to TB */
vnmc = VNMC_IM_FULL;
- /* Use BT if video standard can be read and is 60 Hz format */
- if (!vin->info->use_mc && vin->std & V4L2_STD_525_60)
- vnmc = VNMC_IM_FULL | VNMC_FOC;
break;
case V4L2_FIELD_INTERLACED_TB:
vnmc = VNMC_IM_FULL;
@@ -897,6 +894,8 @@ static int rvin_setup(struct rvin_dev *vin)
/* Progressive or interlaced mode */
interrupts = progressive ? VNIE_FIE : VNIE_EFE;
+ /* Enable VSYNC Rising Edge Detection. */
+ interrupts |= VNIE_VRE;
/* Ack interrupts */
rvin_write(vin, interrupts, VNINTS_REG);
@@ -912,21 +911,6 @@ static int rvin_setup(struct rvin_dev *vin)
return 0;
}
-static void rvin_disable_interrupts(struct rvin_dev *vin)
-{
- rvin_write(vin, 0, VNIE_REG);
-}
-
-static u32 rvin_get_interrupt_status(struct rvin_dev *vin)
-{
- return rvin_read(vin, VNINTS_REG);
-}
-
-static void rvin_ack_interrupt(struct rvin_dev *vin)
-{
- rvin_write(vin, rvin_read(vin, VNINTS_REG), VNINTS_REG);
-}
-
static bool rvin_capture_active(struct rvin_dev *vin)
{
return rvin_read(vin, VNMS_REG) & VNMS_CA;
@@ -1049,22 +1033,35 @@ static void rvin_capture_stop(struct rvin_dev *vin)
static irqreturn_t rvin_irq(int irq, void *data)
{
struct rvin_dev *vin = data;
- u32 int_status, vnms;
+ u32 capture, status, vnms;
int slot;
unsigned int handled = 0;
unsigned long flags;
spin_lock_irqsave(&vin->qlock, flags);
- int_status = rvin_get_interrupt_status(vin);
- if (!int_status)
+ status = rvin_read(vin, VNINTS_REG);
+ if (!status)
goto done;
- rvin_ack_interrupt(vin);
+ rvin_write(vin, status, VNINTS_REG);
handled = 1;
+ /* Signal Start of Frame. */
+ if (status & VNINTS_VRS) {
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ .u.frame_sync.frame_sequence = vin->sequence,
+ };
+
+ v4l2_event_queue(&vin->vdev, &event);
+ }
+
/* Nothing to do if nothing was captured. */
- if (!(int_status & VNINTS_FIS))
+ capture = vin->format.field == V4L2_FIELD_NONE ||
+ vin->format.field == V4L2_FIELD_ALTERNATE ?
+ VNINTS_FIS : VNINTS_EFS;
+ if (!(status & capture))
goto done;
/* Nothing to do if not running. */
@@ -1297,14 +1294,6 @@ static int rvin_set_stream(struct rvin_dev *vin, int on)
struct media_pad *pad;
int ret;
- /* No media controller used, simply pass operation to subdevice. */
- if (!vin->info->use_mc) {
- ret = v4l2_subdev_call(vin->parallel.subdev, video, s_stream,
- on);
-
- return ret == -ENOIOCTLCMD ? 0 : ret;
- }
-
pad = media_pad_remote_pad_first(&vin->pad);
if (!pad)
return -EPIPE;
@@ -1417,7 +1406,7 @@ void rvin_stop_streaming(struct rvin_dev *vin)
rvin_set_stream(vin, 0);
/* disable interrupts */
- rvin_disable_interrupts(vin);
+ rvin_write(vin, 0, VNIE_REG);
/* Return unprocessed buffers from hardware. */
for (unsigned int i = 0; i < HW_BUFFER_NUM; i++) {
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
index db091af57c19..62eddf3a35fc 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-v4l2.c
@@ -2,12 +2,11 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
#include <linux/pm_runtime.h>
@@ -230,101 +229,6 @@ static void rvin_format_align(struct rvin_dev *vin, struct v4l2_pix_format *pix)
* V4L2
*/
-static int rvin_reset_format(struct rvin_dev *vin)
-{
- struct v4l2_subdev_format fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .pad = vin->parallel.source_pad,
- };
- int ret;
-
- ret = v4l2_subdev_call(vin_to_source(vin), pad, get_fmt, NULL, &fmt);
- if (ret)
- return ret;
-
- v4l2_fill_pix_format(&vin->format, &fmt.format);
-
- vin->crop.top = 0;
- vin->crop.left = 0;
- vin->crop.width = vin->format.width;
- vin->crop.height = vin->format.height;
-
- /* Make use of the hardware interlacer by default. */
- if (vin->format.field == V4L2_FIELD_ALTERNATE) {
- vin->format.field = V4L2_FIELD_INTERLACED;
- vin->format.height *= 2;
- }
-
- rvin_format_align(vin, &vin->format);
-
- vin->compose.top = 0;
- vin->compose.left = 0;
- vin->compose.width = vin->format.width;
- vin->compose.height = vin->format.height;
-
- return 0;
-}
-
-static int rvin_try_format(struct rvin_dev *vin, u32 which,
- struct v4l2_pix_format *pix,
- struct v4l2_rect *src_rect)
-{
- struct v4l2_subdev *sd = vin_to_source(vin);
- struct v4l2_subdev_state *sd_state;
- static struct lock_class_key key;
- struct v4l2_subdev_format format = {
- .which = which,
- .pad = vin->parallel.source_pad,
- };
- enum v4l2_field field;
- u32 width, height;
- int ret;
-
- /*
- * FIXME: Drop this call, drivers are not supposed to use
- * __v4l2_subdev_state_alloc().
- */
- sd_state = __v4l2_subdev_state_alloc(sd, "rvin:state->lock", &key);
- if (IS_ERR(sd_state))
- return PTR_ERR(sd_state);
-
- if (!rvin_format_from_pixel(vin, pix->pixelformat))
- pix->pixelformat = RVIN_DEFAULT_FORMAT;
-
- v4l2_fill_mbus_format(&format.format, pix, vin->mbus_code);
-
- /* Allow the video device to override field and to scale */
- field = pix->field;
- width = pix->width;
- height = pix->height;
-
- ret = v4l2_subdev_call(sd, pad, set_fmt, sd_state, &format);
- if (ret < 0 && ret != -ENOIOCTLCMD)
- goto done;
- ret = 0;
-
- v4l2_fill_pix_format(pix, &format.format);
-
- if (src_rect) {
- src_rect->top = 0;
- src_rect->left = 0;
- src_rect->width = pix->width;
- src_rect->height = pix->height;
- }
-
- if (field != V4L2_FIELD_ANY)
- pix->field = field;
-
- pix->width = width;
- pix->height = height;
-
- rvin_format_align(vin, pix);
-done:
- __v4l2_subdev_state_free(sd_state);
-
- return ret;
-}
-
static int rvin_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
@@ -333,42 +237,6 @@ static int rvin_querycap(struct file *file, void *priv,
return 0;
}
-static int rvin_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct rvin_dev *vin = video_drvdata(file);
-
- return rvin_try_format(vin, V4L2_SUBDEV_FORMAT_TRY, &f->fmt.pix, NULL);
-}
-
-static int rvin_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_rect fmt_rect, src_rect;
- int ret;
-
- if (vb2_is_busy(&vin->queue))
- return -EBUSY;
-
- ret = rvin_try_format(vin, V4L2_SUBDEV_FORMAT_ACTIVE, &f->fmt.pix,
- &src_rect);
- if (ret)
- return ret;
-
- vin->format = f->fmt.pix;
-
- fmt_rect.top = 0;
- fmt_rect.left = 0;
- fmt_rect.width = vin->format.width;
- fmt_rect.height = vin->format.height;
-
- v4l2_rect_map_inside(&vin->crop, &src_rect);
- v4l2_rect_map_inside(&vin->compose, &fmt_rect);
-
- return 0;
-}
-
static int rvin_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
@@ -465,6 +333,7 @@ static int rvin_enum_fmt_vid_cap(struct file *file, void *priv,
static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect)
{
+ struct media_pad *pad = media_pad_remote_pad_first(&vin->pad);
struct v4l2_subdev_format fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
@@ -472,18 +341,11 @@ static int rvin_remote_rectangle(struct rvin_dev *vin, struct v4l2_rect *rect)
unsigned int index;
int ret;
- if (vin->info->use_mc) {
- struct media_pad *pad = media_pad_remote_pad_first(&vin->pad);
-
- if (!pad)
- return -EINVAL;
+ if (!pad)
+ return -EINVAL;
- sd = media_entity_to_v4l2_subdev(pad->entity);
- index = pad->index;
- } else {
- sd = vin_to_source(vin);
- index = vin->parallel.source_pad;
- }
+ sd = media_entity_to_v4l2_subdev(pad->entity);
+ index = pad->index;
fmt.pad = index;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &fmt);
@@ -623,284 +485,18 @@ static int rvin_s_selection(struct file *file, void *fh,
return 0;
}
-static int rvin_g_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_g_parm_cap(&vin->vdev, sd, parm);
-}
-
-static int rvin_s_parm(struct file *file, void *priv,
- struct v4l2_streamparm *parm)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_s_parm_cap(&vin->vdev, sd, parm);
-}
-
-static int rvin_g_pixelaspect(struct file *file, void *priv,
- int type, struct v4l2_fract *f)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- return v4l2_subdev_call(sd, video, g_pixelaspect, f);
-}
-
-static int rvin_enum_input(struct file *file, void *priv,
- struct v4l2_input *i)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (i->index != 0)
- return -EINVAL;
-
- ret = v4l2_subdev_call(sd, video, g_input_status, &i->status);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
-
- i->type = V4L2_INPUT_TYPE_CAMERA;
-
- if (v4l2_subdev_has_op(sd, pad, dv_timings_cap)) {
- i->capabilities = V4L2_IN_CAP_DV_TIMINGS;
- i->std = 0;
- } else {
- i->capabilities = V4L2_IN_CAP_STD;
- i->std = vin->vdev.tvnorms;
- }
-
- strscpy(i->name, "Camera", sizeof(i->name));
-
- return 0;
-}
-
-static int rvin_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int rvin_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
- return 0;
-}
-
-static int rvin_querystd(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- return v4l2_subdev_call(sd, video, querystd, a);
-}
-
-static int rvin_s_std(struct file *file, void *priv, v4l2_std_id a)
-{
- struct rvin_dev *vin = video_drvdata(file);
- int ret;
-
- ret = v4l2_subdev_call(vin_to_source(vin), video, s_std, a);
- if (ret < 0)
- return ret;
-
- vin->std = a;
-
- /* Changing the standard will change the width/height */
- return rvin_reset_format(vin);
-}
-
-static int rvin_g_std(struct file *file, void *priv, v4l2_std_id *a)
-{
- struct rvin_dev *vin = video_drvdata(file);
-
- if (v4l2_subdev_has_op(vin_to_source(vin), pad, dv_timings_cap))
- return -ENOIOCTLCMD;
-
- *a = vin->std;
-
- return 0;
-}
-
static int rvin_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);
case V4L2_EVENT_SOURCE_CHANGE:
return v4l2_event_subscribe(fh, sub, 4, NULL);
}
return v4l2_ctrl_subscribe_event(fh, sub);
}
-static int rvin_enum_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_enum_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (timings->pad)
- return -EINVAL;
-
- timings->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, enum_dv_timings, timings);
-
- timings->pad = 0;
-
- return ret;
-}
-
-static int rvin_s_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- ret = v4l2_subdev_call(sd, pad, s_dv_timings,
- vin->parallel.sink_pad, timings);
- if (ret)
- return ret;
-
- /* Changing the timings will change the width/height */
- return rvin_reset_format(vin);
-}
-
-static int rvin_g_dv_timings(struct file *file, void *priv_fh,
- struct v4l2_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- 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,
- struct v4l2_dv_timings *timings)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
-
- 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,
- struct v4l2_dv_timings_cap *cap)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (cap->pad)
- return -EINVAL;
-
- cap->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
-
- cap->pad = 0;
-
- return ret;
-}
-
-static int rvin_g_edid(struct file *file, void *fh, struct v4l2_edid *edid)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (edid->pad)
- return -EINVAL;
-
- edid->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, get_edid, edid);
-
- edid->pad = 0;
-
- return ret;
-}
-
-static int rvin_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
-{
- struct rvin_dev *vin = video_drvdata(file);
- struct v4l2_subdev *sd = vin_to_source(vin);
- int ret;
-
- if (edid->pad)
- return -EINVAL;
-
- edid->pad = vin->parallel.sink_pad;
-
- ret = v4l2_subdev_call(sd, pad, set_edid, edid);
-
- edid->pad = 0;
-
- return ret;
-}
-
-static const struct v4l2_ioctl_ops rvin_ioctl_ops = {
- .vidioc_querycap = rvin_querycap,
- .vidioc_try_fmt_vid_cap = rvin_try_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = rvin_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = rvin_s_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = rvin_enum_fmt_vid_cap,
-
- .vidioc_g_selection = rvin_g_selection,
- .vidioc_s_selection = rvin_s_selection,
-
- .vidioc_g_parm = rvin_g_parm,
- .vidioc_s_parm = rvin_s_parm,
-
- .vidioc_g_pixelaspect = rvin_g_pixelaspect,
-
- .vidioc_enum_input = rvin_enum_input,
- .vidioc_g_input = rvin_g_input,
- .vidioc_s_input = rvin_s_input,
-
- .vidioc_dv_timings_cap = rvin_dv_timings_cap,
- .vidioc_enum_dv_timings = rvin_enum_dv_timings,
- .vidioc_g_dv_timings = rvin_g_dv_timings,
- .vidioc_s_dv_timings = rvin_s_dv_timings,
- .vidioc_query_dv_timings = rvin_query_dv_timings,
-
- .vidioc_g_edid = rvin_g_edid,
- .vidioc_s_edid = rvin_s_edid,
-
- .vidioc_querystd = rvin_querystd,
- .vidioc_g_std = rvin_g_std,
- .vidioc_s_std = rvin_s_std,
-
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .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 = rvin_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-/* -----------------------------------------------------------------------------
- * V4L2 Media Controller
- */
-
static void rvin_mc_try_format(struct rvin_dev *vin,
struct v4l2_pix_format *pix)
{
@@ -979,19 +575,6 @@ static const struct v4l2_ioctl_ops rvin_mc_ioctl_ops = {
* File Operations
*/
-static int rvin_power_parallel(struct rvin_dev *vin, bool on)
-{
- struct v4l2_subdev *sd = vin_to_source(vin);
- int power = on ? 1 : 0;
- int ret;
-
- ret = v4l2_subdev_call(sd, core, s_power, power);
- if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV)
- return ret;
-
- return 0;
-}
-
static int rvin_open(struct file *file)
{
struct rvin_dev *vin = video_drvdata(file);
@@ -1011,11 +594,7 @@ static int rvin_open(struct file *file)
if (ret)
goto err_unlock;
- if (vin->info->use_mc)
- ret = v4l2_pipeline_pm_get(&vin->vdev.entity);
- else if (v4l2_fh_is_singular_file(file))
- ret = rvin_power_parallel(vin, true);
-
+ ret = v4l2_pipeline_pm_get(&vin->vdev.entity);
if (ret < 0)
goto err_open;
@@ -1027,10 +606,7 @@ static int rvin_open(struct file *file)
return 0;
err_power:
- if (vin->info->use_mc)
- v4l2_pipeline_pm_put(&vin->vdev.entity);
- else if (v4l2_fh_is_singular_file(file))
- rvin_power_parallel(vin, false);
+ v4l2_pipeline_pm_put(&vin->vdev.entity);
err_open:
v4l2_fh_release(file);
err_unlock:
@@ -1044,23 +620,14 @@ err_pm:
static int rvin_release(struct file *file)
{
struct rvin_dev *vin = video_drvdata(file);
- bool fh_singular;
int ret;
mutex_lock(&vin->lock);
- /* Save the singular status before we call the clean-up helper */
- fh_singular = v4l2_fh_is_singular_file(file);
-
/* the release helper will cleanup any on-going streaming */
ret = _vb2_fop_release(file, NULL);
- if (vin->info->use_mc) {
- v4l2_pipeline_pm_put(&vin->vdev.entity);
- } else {
- if (fh_singular)
- rvin_power_parallel(vin, false);
- }
+ v4l2_pipeline_pm_put(&vin->vdev.entity);
mutex_unlock(&vin->lock);
@@ -1091,18 +658,6 @@ void rvin_v4l2_unregister(struct rvin_dev *vin)
video_unregister_device(&vin->vdev);
}
-static void rvin_notify_video_device(struct rvin_dev *vin,
- unsigned int notification, void *arg)
-{
- switch (notification) {
- case V4L2_DEVICE_NOTIFY_EVENT:
- v4l2_event_queue(&vin->vdev, arg);
- break;
- default:
- break;
- }
-}
-
static void rvin_notify(struct v4l2_subdev *sd,
unsigned int notification, void *arg)
{
@@ -1113,12 +668,6 @@ static void rvin_notify(struct v4l2_subdev *sd,
container_of(sd->v4l2_dev, struct rvin_dev, v4l2_dev);
unsigned int i;
- /* If no media controller, no need to route the event. */
- if (!vin->info->use_mc) {
- rvin_notify_video_device(vin, notification, arg);
- return;
- }
-
group = vin->group;
for (i = 0; i < RCAR_VIN_NUM; i++) {
@@ -1134,7 +683,13 @@ static void rvin_notify(struct v4l2_subdev *sd,
if (remote != sd)
continue;
- rvin_notify_video_device(vin, notification, arg);
+ switch (notification) {
+ case V4L2_DEVICE_NOTIFY_EVENT:
+ v4l2_event_queue(&vin->vdev, arg);
+ break;
+ default:
+ break;
+ }
}
}
@@ -1153,7 +708,8 @@ int rvin_v4l2_register(struct rvin_dev *vin)
vdev->lock = &vin->lock;
vdev->fops = &rvin_fops;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
+ V4L2_CAP_READWRITE | V4L2_CAP_IO_MC;
+ vdev->ioctl_ops = &rvin_mc_ioctl_ops;
/* Set a default format */
vin->format.pixelformat = RVIN_DEFAULT_FORMAT;
@@ -1162,14 +718,6 @@ int rvin_v4l2_register(struct rvin_dev *vin)
vin->format.field = RVIN_DEFAULT_FIELD;
vin->format.colorspace = RVIN_DEFAULT_COLORSPACE;
- if (vin->info->use_mc) {
- vdev->device_caps |= V4L2_CAP_IO_MC;
- vdev->ioctl_ops = &rvin_mc_ioctl_ops;
- } else {
- vdev->ioctl_ops = &rvin_ioctl_ops;
- rvin_reset_format(vin);
- }
-
rvin_format_align(vin, &vin->format);
ret = video_register_device(&vin->vdev, VFL_TYPE_VIDEO, -1);
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
index 83d1b2734c41..74bef5b8adad 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-vin.h
@@ -2,12 +2,11 @@
/*
* Driver for Renesas R-Car VIN
*
+ * Copyright (C) 2025 Niklas Söderlund <niklas.soderlund@ragnatech.se>
* Copyright (C) 2016 Renesas Electronics Corp.
* Copyright (C) 2011-2013 Renesas Solutions Corp.
* Copyright (C) 2013 Cogent Embedded, Inc., <source@cogentembedded.com>
* Copyright (C) 2008 Magnus Damm
- *
- * Based on the soc-camera rcar_vin driver
*/
#ifndef __RCAR_VIN__
@@ -79,8 +78,6 @@ struct rvin_video_format {
* @mbus_type: media bus type
* @bus: media bus parallel configuration
* @source_pad: source pad of remote subdevice
- * @sink_pad: sink pad of remote subdevice
- *
*/
struct rvin_parallel_entity {
struct v4l2_async_connection *asc;
@@ -90,7 +87,6 @@ struct rvin_parallel_entity {
struct v4l2_mbus_config_parallel bus;
unsigned int source_pad;
- unsigned int sink_pad;
};
/**
@@ -117,7 +113,6 @@ struct rvin_group_route {
/**
* struct rvin_info - Information about the particular VIN implementation
* @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 outputting NV12 pixel format
* @raw10: support outputting RAW10 pixel format
@@ -129,7 +124,6 @@ struct rvin_group_route {
*/
struct rvin_info {
enum model_id model;
- bool use_mc;
bool use_isp;
bool nv12;
bool raw10;
@@ -149,7 +143,6 @@ struct rvin_info {
* @vdev: V4L2 video device associated with VIN
* @v4l2_dev: V4L2 device
* @ctrl_handler: V4L2 control handler
- * @notifier: V4L2 asynchronous subdevs notifier
*
* @parallel: parallel input subdevice descriptor
*
@@ -177,7 +170,6 @@ struct rvin_info {
* @crop: active cropping
* @compose: active composing
* @scaler: Optional scaler
- * @std: active video standard of the video source
*
* @alpha: Alpha component to fill in for supported pixel formats
*/
@@ -189,7 +181,6 @@ struct rvin_dev {
struct video_device vdev;
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler ctrl_handler;
- struct v4l2_async_notifier notifier;
struct rvin_parallel_entity parallel;
@@ -220,7 +211,6 @@ struct rvin_dev {
struct v4l2_rect crop;
struct v4l2_rect compose;
void (*scaler)(struct rvin_dev *vin);
- v4l2_std_id std;
unsigned int alpha;
};
@@ -242,6 +232,7 @@ struct rvin_dev {
* @lock: protects the count, notifier, vin and csi members
* @count: number of enabled VIN instances found in DT
* @notifier: group notifier for CSI-2 async connections
+ * @info: Platform dependent information about the VIN instances
* @vin: VIN instances which are part of the group
* @link_setup: Callback to create all links for the media graph
* @remotes: array of pairs of async connection and subdev pointers
@@ -255,9 +246,10 @@ struct rvin_group {
struct mutex lock;
unsigned int count;
struct v4l2_async_notifier notifier;
+ const struct rvin_info *info;
struct rvin_dev *vin[RCAR_VIN_NUM];
- int (*link_setup)(struct rvin_dev *vin);
+ int (*link_setup)(struct rvin_group *group);
struct {
struct v4l2_async_connection *asc;
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
index 5fa73ab2db53..806acc8f9728 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
@@ -366,7 +366,7 @@ static const struct rzg2l_cru_info rzg3e_cru_info = {
.irq_handler = rzg3e_cru_irq,
.enable_interrupts = rzg3e_cru_enable_interrupts,
.disable_interrupts = rzg3e_cru_disable_interrupts,
- .fifo_empty = rz3e_fifo_empty,
+ .fifo_empty = rzg3e_fifo_empty,
.csi_setup = rzg3e_cru_csi2_setup,
};
@@ -403,7 +403,7 @@ static const u16 rzg2l_cru_regs[] = {
[ICnDMR] = 0x26c,
};
-static const struct rzg2l_cru_info rzgl2_cru_info = {
+static const struct rzg2l_cru_info rzg2l_cru_info = {
.max_width = 2800,
.max_height = 4095,
.image_conv = ICnMC,
@@ -422,7 +422,7 @@ static const struct of_device_id rzg2l_cru_of_id_table[] = {
},
{
.compatible = "renesas,rzg2l-cru",
- .data = &rzgl2_cru_info,
+ .data = &rzg2l_cru_info,
},
{ /* sentinel */ }
};
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
index c30f3b281284..be95b41c37df 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
@@ -64,19 +64,21 @@ struct rzg2l_cru_ip {
/**
* struct rzg2l_cru_ip_format - CRU IP format
- * @code: Media bus code
+ * @codes: Array of up to four media bus codes
* @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;
+ /*
+ * RAW output formats might be produced by RAW media codes with any one
+ * of the 4 common bayer patterns.
+ */
+ u32 codes[4];
u32 datatype;
u32 format;
u32 icndmr;
- u8 bpp;
bool yuv;
};
@@ -192,6 +194,8 @@ 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);
+bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt,
+ unsigned int code);
void rzg2l_cru_enable_interrupts(struct rzg2l_cru_dev *cru);
void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru);
@@ -199,7 +203,7 @@ 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);
+bool rzg3e_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);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
index 9243306e2aa9..1520211e7418 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
@@ -232,6 +232,18 @@ static const struct rzg2l_csi2_format rzg2l_csi2_formats[] = {
{ .code = MEDIA_BUS_FMT_SGBRG8_1X8, .bpp = 8, },
{ .code = MEDIA_BUS_FMT_SGRBG8_1X8, .bpp = 8, },
{ .code = MEDIA_BUS_FMT_SRGGB8_1X8, .bpp = 8, },
+ { .code = MEDIA_BUS_FMT_SBGGR10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, },
+ { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, },
+ { .code = MEDIA_BUS_FMT_SBGGR14_1X14, .bpp = 14, },
+ { .code = MEDIA_BUS_FMT_SGBRG14_1X14, .bpp = 14, },
+ { .code = MEDIA_BUS_FMT_SGRBG14_1X14, .bpp = 14, },
+ { .code = MEDIA_BUS_FMT_SRGGB14_1X14, .bpp = 14, },
};
static inline struct rzg2l_csi2 *sd_to_csi2(struct v4l2_subdev *sd)
@@ -282,15 +294,18 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2)
const struct rzg2l_csi2_format *format;
const struct v4l2_mbus_framefmt *fmt;
struct v4l2_subdev_state *state;
- struct v4l2_ctrl *ctrl;
+ struct media_pad *remote_pad;
u64 mbps;
+ s64 ret;
- /* Read the pixel rate control from remote. */
- ctrl = v4l2_ctrl_find(source->ctrl_handler, V4L2_CID_PIXEL_RATE);
- if (!ctrl) {
- dev_err(csi2->dev, "no pixel rate control in subdev %s\n",
- source->name);
- return -EINVAL;
+ 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);
}
state = v4l2_subdev_lock_and_get_active_state(&csi2->subdev);
@@ -298,12 +313,16 @@ static int rzg2l_csi2_calc_mbps(struct rzg2l_csi2 *csi2)
format = rzg2l_csi2_code_to_fmt(fmt->code);
v4l2_subdev_unlock_state(state);
- /*
- * Calculate hsfreq in Mbps
- * hsfreq = (pixel_rate * bits_per_sample) / number_of_lanes
- */
- mbps = v4l2_ctrl_g_ctrl_int64(ctrl) * format->bpp;
- do_div(mbps, csi2->lanes * 1000000);
+ /* Read the link frequency from remote subdevice. */
+ ret = v4l2_get_link_freq(remote_pad, format->bpp, csi2->lanes * 2);
+ if (ret < 0) {
+ dev_err(csi2->dev, "can't retrieve link freq from subdev %s\n",
+ source->name);
+ return -EINVAL;
+ }
+
+ mbps = ret * 2;
+ do_div(mbps, 1000000);
return mbps;
}
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
index 7836c7cd53dc..5f2c87858bfe 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
@@ -13,42 +13,83 @@
static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = {
{
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .codes = {
+ 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,
+ .codes = {
+ 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,
+ .codes = {
+ 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,
+ .codes = {
+ 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,
+ .codes = {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ },
.format = V4L2_PIX_FMT_SRGGB8,
.datatype = MIPI_CSI2_DT_RAW8,
- .bpp = 1,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10
+ },
+ .format = V4L2_PIX_FMT_RAW_CRU10,
+ .datatype = MIPI_CSI2_DT_RAW10,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12
+ },
+ .format = V4L2_PIX_FMT_RAW_CRU12,
+ .datatype = MIPI_CSI2_DT_RAW12,
+ .icndmr = 0,
+ .yuv = false,
+ },
+ {
+ .codes = {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MEDIA_BUS_FMT_SRGGB14_1X14
+ },
+ .format = V4L2_PIX_FMT_RAW_CRU14,
+ .datatype = MIPI_CSI2_DT_RAW14,
.icndmr = 0,
.yuv = false,
},
@@ -56,11 +97,14 @@ static const struct rzg2l_cru_ip_format rzg2l_cru_ip_formats[] = {
const struct rzg2l_cru_ip_format *rzg2l_cru_ip_code_to_fmt(unsigned int code)
{
- unsigned int i;
+ unsigned int i, j;
- for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++)
- if (rzg2l_cru_ip_formats[i].code == code)
- return &rzg2l_cru_ip_formats[i];
+ for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
+ for (j = 0; j < ARRAY_SIZE(rzg2l_cru_ip_formats[i].codes); j++) {
+ if (rzg2l_cru_ip_formats[i].codes[j] == code)
+ return &rzg2l_cru_ip_formats[i];
+ }
+ }
return NULL;
}
@@ -85,6 +129,17 @@ const struct rzg2l_cru_ip_format *rzg2l_cru_ip_index_to_fmt(u32 index)
return &rzg2l_cru_ip_formats[index];
}
+bool rzg2l_cru_ip_fmt_supports_mbus_code(const struct rzg2l_cru_ip_format *fmt,
+ unsigned int code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fmt->codes); i++)
+ if (fmt->codes[i] == code)
+ return true;
+
+ return false;
+}
struct v4l2_mbus_framefmt *rzg2l_cru_ip_get_src_fmt(struct rzg2l_cru_dev *cru)
{
struct v4l2_subdev_state *state;
@@ -162,7 +217,7 @@ static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
sink_format = v4l2_subdev_state_get_format(state, fmt->pad);
if (!rzg2l_cru_ip_code_to_fmt(fmt->format.code))
- sink_format->code = rzg2l_cru_ip_formats[0].code;
+ sink_format->code = rzg2l_cru_ip_formats[0].codes[0];
else
sink_format->code = fmt->format.code;
@@ -188,11 +243,26 @@ static int rzg2l_cru_ip_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code)
{
- if (code->index >= ARRAY_SIZE(rzg2l_cru_ip_formats))
- return -EINVAL;
+ unsigned int index = code->index;
+ unsigned int i, j;
- code->code = rzg2l_cru_ip_formats[code->index].code;
- return 0;
+ for (i = 0; i < ARRAY_SIZE(rzg2l_cru_ip_formats); i++) {
+ const struct rzg2l_cru_ip_format *fmt = &rzg2l_cru_ip_formats[i];
+
+ for (j = 0; j < ARRAY_SIZE(fmt->codes); j++) {
+ if (!fmt->codes[j])
+ continue;
+
+ if (!index) {
+ code->code = fmt->codes[j];
+ return 0;
+ }
+
+ index--;
+ }
+ }
+
+ return -EINVAL;
}
static int rzg2l_cru_ip_enum_frame_size(struct v4l2_subdev *sd,
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index 067c6af14e95..a8817a7066b2 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -323,7 +323,7 @@ static int rzg2l_cru_initialize_image_conv(struct rzg2l_cru_dev *cru,
return 0;
}
-bool rz3e_fifo_empty(struct rzg2l_cru_dev *cru)
+bool rzg3e_fifo_empty(struct rzg2l_cru_dev *cru)
{
u32 amnfifopntr = rzg2l_cru_read(cru, AMnFIFOPNTR);
@@ -345,8 +345,6 @@ bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru)
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;
}
@@ -941,15 +939,7 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
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->sizeimage = pix->bytesperline * pix->height;
+ v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height);
dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n",
pix->width, pix->height, pix->bytesperline, pix->sizeimage);
@@ -1031,6 +1021,31 @@ static int rzg2l_cru_enum_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
+static int rzg2l_cru_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct rzg2l_cru_dev *cru = video_drvdata(file);
+ const struct rzg2l_cru_info *info = cru->info;
+ const struct rzg2l_cru_ip_format *fmt;
+
+ if (fsize->index)
+ return -EINVAL;
+
+ fmt = rzg2l_cru_ip_format_to_fmt(fsize->pixel_format);
+ if (!fmt)
+ return -EINVAL;
+
+ fsize->type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = RZG2L_CRU_MIN_INPUT_WIDTH;
+ fsize->stepwise.max_width = info->max_width;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.min_height = RZG2L_CRU_MIN_INPUT_HEIGHT;
+ fsize->stepwise.max_height = info->max_height;
+ fsize->stepwise.step_height = 1;
+
+ return 0;
+}
+
static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = {
.vidioc_querycap = rzg2l_cru_querycap,
.vidioc_try_fmt_vid_cap = rzg2l_cru_try_fmt_vid_cap,
@@ -1047,6 +1062,7 @@ static const struct v4l2_ioctl_ops rzg2l_cru_ioctl_ops = {
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_streamon = vb2_ioctl_streamon,
.vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_enum_framesizes = rzg2l_cru_enum_framesizes,
};
/* -----------------------------------------------------------------------------
@@ -1129,7 +1145,7 @@ static int rzg2l_cru_video_link_validate(struct media_link *link)
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)
+ !rzg2l_cru_ip_fmt_supports_mbus_code(video_fmt, fmt.format.code))
return -EPIPE;
return 0;
diff --git a/drivers/media/platform/renesas/vsp1/Makefile b/drivers/media/platform/renesas/vsp1/Makefile
index de8c802e1d1a..2057c8f7be47 100644
--- a/drivers/media/platform/renesas/vsp1/Makefile
+++ b/drivers/media/platform/renesas/vsp1/Makefile
@@ -6,5 +6,6 @@ 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_iif.o vsp1_lif.o vsp1_uif.o
+vsp1-y += vsp1_vspx.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 f97a1a31bfab..94de2e85792e 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1.h
@@ -111,6 +111,7 @@ struct vsp1_device {
struct media_entity_operations media_ops;
struct vsp1_drm *drm;
+ struct vsp1_vspx *vspx;
};
int vsp1_device_get(struct vsp1_device *vsp1);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index bb8228b19824..d732b4ed1180 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/gfp.h>
+#include <linux/lockdep.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
@@ -176,6 +177,7 @@ struct vsp1_dl_cmd_pool {
* @bodies: list of extra display list bodies
* @pre_cmd: pre command to be issued through extended dl header
* @post_cmd: post command to be issued through extended dl header
+ * @allocated: flag to detect double list release
* @has_chain: if true, indicates that there's a partition chain
* @chain: entry in the display list partition chain
* @flags: display list flags, a combination of VSP1_DL_FRAME_END_*
@@ -194,6 +196,8 @@ struct vsp1_dl_list {
struct vsp1_dl_ext_cmd *pre_cmd;
struct vsp1_dl_ext_cmd *post_cmd;
+ bool allocated;
+
bool has_chain;
struct list_head chain;
@@ -212,6 +216,7 @@ struct vsp1_dl_list {
* @pending: list waiting to be queued to the hardware
* @pool: body pool for the display list bodies
* @cmdpool: commands pool for extended display list
+ * @list_count: number of allocated display lists
*/
struct vsp1_dl_manager {
unsigned int index;
@@ -226,6 +231,8 @@ struct vsp1_dl_manager {
struct vsp1_dl_body_pool *pool;
struct vsp1_dl_cmd_pool *cmdpool;
+
+ size_t list_count;
};
/* -----------------------------------------------------------------------------
@@ -606,6 +613,8 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
struct vsp1_dl_list *dl = NULL;
unsigned long flags;
+ lockdep_assert_not_held(&dlm->lock);
+
spin_lock_irqsave(&dlm->lock, flags);
if (!list_empty(&dlm->free)) {
@@ -617,6 +626,7 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
* display list can assert list_empty() if it is not in a chain.
*/
INIT_LIST_HEAD(&dl->chain);
+ dl->allocated = true;
}
spin_unlock_irqrestore(&dlm->lock, flags);
@@ -632,6 +642,8 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
if (!dl)
return;
+ lockdep_assert_held(&dl->dlm->lock);
+
/*
* Release any linked display-lists which were chained for a single
* hardware operation.
@@ -657,6 +669,13 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
*/
dl->body0->num_entries = 0;
+ /*
+ * Return the display list to the 'free' pool. If the list had already
+ * been returned be loud about it.
+ */
+ WARN_ON_ONCE(!dl->allocated);
+ dl->allocated = false;
+
list_add_tail(&dl->list, &dl->dlm->free);
}
@@ -1067,6 +1086,7 @@ void vsp1_dlm_setup(struct vsp1_device *vsp1)
void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
{
unsigned long flags;
+ size_t list_count;
spin_lock_irqsave(&dlm->lock, flags);
@@ -1074,8 +1094,11 @@ void vsp1_dlm_reset(struct vsp1_dl_manager *dlm)
__vsp1_dl_list_put(dlm->queued);
__vsp1_dl_list_put(dlm->pending);
+ list_count = list_count_nodes(&dlm->free);
spin_unlock_irqrestore(&dlm->lock, flags);
+ WARN_ON_ONCE(list_count != dlm->list_count);
+
dlm->active = NULL;
dlm->queued = NULL;
dlm->pending = NULL;
@@ -1145,6 +1168,8 @@ struct vsp1_dl_manager *vsp1_dlm_create(struct vsp1_device *vsp1,
list_add_tail(&dl->list, &dlm->free);
}
+ dlm->list_count = prealloc;
+
if (vsp1_feature(vsp1, VSP1_HAS_EXT_DL)) {
dlm->cmdpool = vsp1_dl_cmd_pool_create(vsp1,
VSP1_EXTCMD_AUTOFLD, prealloc);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index fe55e8747b05..15d266439564 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
@@ -9,6 +9,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/export.h>
#include <linux/slab.h>
#include <media/media-entity.h>
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drv.c b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
index 8270a9d207cb..b8d06e88c475 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drv.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drv.c
@@ -33,11 +33,13 @@
#include "vsp1_lif.h"
#include "vsp1_lut.h"
#include "vsp1_pipe.h"
+#include "vsp1_regs.h"
#include "vsp1_rwpf.h"
#include "vsp1_sru.h"
#include "vsp1_uds.h"
#include "vsp1_uif.h"
#include "vsp1_video.h"
+#include "vsp1_vspx.h"
/* -----------------------------------------------------------------------------
* Interrupt Handling
@@ -490,7 +492,10 @@ static int vsp1_create_entities(struct vsp1_device *vsp1)
ret = media_device_register(mdev);
} else {
- ret = vsp1_drm_init(vsp1);
+ if (vsp1->info->version == VI6_IP_VERSION_MODEL_VSPX_GEN4)
+ ret = vsp1_vspx_init(vsp1);
+ else
+ ret = vsp1_drm_init(vsp1);
}
done:
@@ -502,7 +507,9 @@ done:
int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
{
+ u32 version = vsp1->version & VI6_IP_VERSION_MODEL_MASK;
unsigned int timeout;
+ int ret = 0;
u32 status;
status = vsp1_read(vsp1, VI6_STATUS);
@@ -523,7 +530,11 @@ int vsp1_reset_wpf(struct vsp1_device *vsp1, unsigned int index)
return -ETIMEDOUT;
}
- return 0;
+ if (version == VI6_IP_VERSION_MODEL_VSPD_GEN3 ||
+ version == VI6_IP_VERSION_MODEL_VSPD_GEN4)
+ ret = rcar_fcp_soft_reset(vsp1->fcp);
+
+ return ret;
}
static int vsp1_device_init(struct vsp1_device *vsp1)
@@ -851,6 +862,13 @@ static const struct vsp1_device_info vsp1_device_infos[] = {
.uif_count = 2,
.wpf_count = 1,
.num_bru_inputs = 5,
+ }, {
+ .version = VI6_IP_VERSION_MODEL_VSPX_GEN4,
+ .model = "VSP2-X",
+ .gen = 4,
+ .features = VSP1_HAS_IIF,
+ .rpf_count = 2,
+ .wpf_count = 1,
},
};
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
index 3cbb768cf6ad..5d769cc42fe1 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
@@ -9,6 +9,7 @@
#include <linux/delay.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/sched.h>
#include <linux/wait.h>
@@ -473,6 +474,8 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
+ lockdep_assert_held(&pipe->irqlock);
+
if (pipe->state == VSP1_PIPELINE_STOPPED) {
vsp1_write(vsp1, VI6_CMD(pipe->output->entity.index),
VI6_CMD_STRCMD);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_regs.h b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
index 86e47c2d991f..10cfbcd1b6e0 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_regs.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_regs.h
@@ -799,6 +799,7 @@
#define VI6_IP_VERSION_MODEL_VSPDL_GEN3 (0x19 << 8)
#define VI6_IP_VERSION_MODEL_VSPBS_GEN3 (0x1a << 8)
#define VI6_IP_VERSION_MODEL_VSPD_GEN4 (0x1c << 8)
+#define VI6_IP_VERSION_MODEL_VSPX_GEN4 (0x1d << 8)
/* RZ/G2L SoCs have no version register, So use 0x80 as the model version */
#define VI6_IP_VERSION_MODEL_VSPD_RZG2L (0x80 << 8)
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.c b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
new file mode 100644
index 000000000000..a754b92232bd
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
@@ -0,0 +1,633 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * vsp1_vspx.c -- R-Car Gen 4 VSPX
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+
+#include "vsp1_vspx.h"
+
+#include <linux/cleanup.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+#include <media/vsp1.h>
+
+#include "vsp1_dl.h"
+#include "vsp1_iif.h"
+#include "vsp1_pipe.h"
+#include "vsp1_rwpf.h"
+
+/*
+ * struct vsp1_vspx_pipeline - VSPX pipeline
+ * @pipe: the VSP1 pipeline
+ * @partition: the pre-calculated partition used by the pipeline
+ * @mutex: protects the streaming start/stop sequences
+ * @lock: protect access to the enabled flag
+ * @enabled: the enable flag
+ * @vspx_frame_end: frame end callback
+ * @frame_end_data: data for the frame end callback
+ */
+struct vsp1_vspx_pipeline {
+ struct vsp1_pipeline pipe;
+ struct vsp1_partition partition;
+
+ /*
+ * Protects the streaming start/stop sequences.
+ *
+ * The start/stop sequences cannot be locked with the 'lock' spinlock
+ * as they acquire mutexes when handling the pm runtime and the vsp1
+ * pipe start/stop operations. Provide a dedicated mutex for this
+ * reason.
+ */
+ struct mutex mutex;
+
+ /*
+ * Protects the enable flag.
+ *
+ * The enabled flag is contended between the start/stop streaming
+ * routines and the job_run one, which cannot take a mutex as it is
+ * called from the ISP irq context.
+ */
+ spinlock_t lock;
+ bool enabled;
+
+ void (*vspx_frame_end)(void *frame_end_data);
+ void *frame_end_data;
+};
+
+static inline struct vsp1_vspx_pipeline *
+to_vsp1_vspx_pipeline(struct vsp1_pipeline *pipe)
+{
+ return container_of(pipe, struct vsp1_vspx_pipeline, pipe);
+}
+
+/*
+ * struct vsp1_vspx - VSPX device
+ * @vsp1: the VSP1 device
+ * @pipe: the VSPX pipeline
+ */
+struct vsp1_vspx {
+ struct vsp1_device *vsp1;
+ struct vsp1_vspx_pipeline pipe;
+};
+
+/* Apply the given width, height and fourcc to the RWPF's subdevice */
+static int vsp1_vspx_rwpf_set_subdev_fmt(struct vsp1_device *vsp1,
+ struct vsp1_rwpf *rwpf,
+ u32 isp_fourcc,
+ unsigned int width,
+ unsigned int height)
+{
+ struct vsp1_entity *ent = &rwpf->entity;
+ struct v4l2_subdev_format format = {};
+ u32 vspx_fourcc;
+
+ switch (isp_fourcc) {
+ case V4L2_PIX_FMT_GREY:
+ /* 8 bit RAW Bayer image. */
+ vspx_fourcc = V4L2_PIX_FMT_RGB332;
+ break;
+ case V4L2_PIX_FMT_Y10:
+ case V4L2_PIX_FMT_Y12:
+ case V4L2_PIX_FMT_Y16:
+ /* 10, 12 and 16 bit RAW Bayer image. */
+ vspx_fourcc = V4L2_PIX_FMT_RGB565;
+ break;
+ case V4L2_META_FMT_GENERIC_8:
+ /* ConfigDMA parameters buffer. */
+ vspx_fourcc = V4L2_PIX_FMT_XBGR32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rwpf->fmtinfo = vsp1_get_format_info(vsp1, vspx_fourcc);
+
+ format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ format.pad = RWPF_PAD_SINK;
+ format.format.width = width;
+ format.format.height = height;
+ format.format.field = V4L2_FIELD_NONE;
+ format.format.code = rwpf->fmtinfo->mbus;
+
+ return v4l2_subdev_call(&ent->subdev, pad, set_fmt, NULL, &format);
+}
+
+/* Configure the RPF->IIF->WPF pipeline for ConfigDMA or RAW image transfer. */
+static int vsp1_vspx_pipeline_configure(struct vsp1_device *vsp1,
+ dma_addr_t addr, u32 isp_fourcc,
+ unsigned int width, unsigned int height,
+ unsigned int stride,
+ unsigned int iif_sink_pad,
+ struct vsp1_dl_list *dl,
+ struct vsp1_dl_body *dlb)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ struct vsp1_rwpf *rpf0 = pipe->inputs[0];
+ int ret;
+
+ ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, rpf0, isp_fourcc, width,
+ height);
+ if (ret)
+ return ret;
+
+ ret = vsp1_vspx_rwpf_set_subdev_fmt(vsp1, pipe->output, isp_fourcc,
+ width, height);
+ if (ret)
+ return ret;
+
+ vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0], width, 0);
+ rpf0->format.plane_fmt[0].bytesperline = stride;
+ rpf0->format.num_planes = 1;
+ rpf0->mem.addr[0] = addr;
+
+ /*
+ * Connect RPF0 to the IIF sink pad corresponding to the config or image
+ * path.
+ */
+ rpf0->entity.sink_pad = iif_sink_pad;
+
+ vsp1_entity_route_setup(&rpf0->entity, pipe, dlb);
+ vsp1_entity_configure_stream(&rpf0->entity, rpf0->entity.state, pipe,
+ dl, dlb);
+ vsp1_entity_configure_partition(&rpf0->entity, pipe,
+ &pipe->part_table[0], dl, dlb);
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Interrupt handling
+ */
+
+static void vsp1_vspx_pipeline_frame_end(struct vsp1_pipeline *pipe,
+ unsigned int completion)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe = to_vsp1_vspx_pipeline(pipe);
+
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ /*
+ * Operating the vsp1_pipe in singleshot mode requires to
+ * manually set the pipeline state to stopped when a transfer
+ * is completed.
+ */
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ }
+
+ if (vspx_pipe->vspx_frame_end)
+ vspx_pipe->vspx_frame_end(vspx_pipe->frame_end_data);
+}
+
+/* -----------------------------------------------------------------------------
+ * ISP Driver API (include/media/vsp1.h)
+ */
+
+/**
+ * vsp1_isp_init() - Initialize the VSPX
+ * @dev: The VSP1 struct device
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_init(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ if (!vsp1)
+ return -EPROBE_DEFER;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_init);
+
+/**
+ * vsp1_isp_get_bus_master - Get VSPX bus master
+ * @dev: The VSP1 struct device
+ *
+ * The VSPX accesses memory through an FCPX instance. When allocating memory
+ * buffers that will have to be accessed by the VSPX the 'struct device' of
+ * the FCPX should be used. Use this function to get a reference to it.
+ *
+ * Return: a pointer to the bus master's device
+ */
+struct device *vsp1_isp_get_bus_master(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ if (!vsp1)
+ return ERR_PTR(-ENODEV);
+
+ return vsp1->bus_master;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_get_bus_master);
+
+/**
+ * vsp1_isp_alloc_buffer - Allocate a buffer in the VSPX address space
+ * @dev: The VSP1 struct device
+ * @size: The size of the buffer to be allocated by the VSPX
+ * @buffer_desc: The buffer descriptor. Will be filled with the buffer
+ * CPU-mapped address, the bus address and the size of the
+ * allocated buffer
+ *
+ * Allocate a buffer that will be later accessed by the VSPX. Buffers allocated
+ * using vsp1_isp_alloc_buffer() shall be released with a call to
+ * vsp1_isp_free_buffer(). This function is used by the ISP driver to allocate
+ * memory for the ConfigDMA parameters buffer.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_alloc_buffer(struct device *dev, size_t size,
+ struct vsp1_isp_buffer_desc *buffer_desc)
+{
+ struct device *bus_master = vsp1_isp_get_bus_master(dev);
+
+ if (IS_ERR_OR_NULL(bus_master))
+ return -ENODEV;
+
+ buffer_desc->cpu_addr = dma_alloc_coherent(bus_master, size,
+ &buffer_desc->dma_addr,
+ GFP_KERNEL);
+ if (!buffer_desc->cpu_addr)
+ return -ENOMEM;
+
+ buffer_desc->size = size;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_alloc_buffer);
+
+/**
+ * vsp1_isp_free_buffer - Release a buffer allocated by vsp1_isp_alloc_buffer()
+ * @dev: The VSP1 struct device
+ * @buffer_desc: The descriptor of the buffer to release as returned by
+ * vsp1_isp_alloc_buffer()
+ *
+ * Release memory in the VSPX address space allocated by
+ * vsp1_isp_alloc_buffer().
+ */
+void vsp1_isp_free_buffer(struct device *dev,
+ struct vsp1_isp_buffer_desc *buffer_desc)
+{
+ struct device *bus_master = vsp1_isp_get_bus_master(dev);
+
+ if (IS_ERR_OR_NULL(bus_master))
+ return;
+
+ dma_free_coherent(bus_master, buffer_desc->size, buffer_desc->cpu_addr,
+ buffer_desc->dma_addr);
+}
+
+/**
+ * vsp1_isp_start_streaming - Start processing VSPX jobs
+ * @dev: The VSP1 struct device
+ * @frame_end: The frame end callback description
+ *
+ * Start the VSPX and prepare for accepting buffer transfer job requests.
+ * The caller is responsible for tracking the started state of the VSPX.
+ * Attempting to start an already started VSPX instance is an error.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_start_streaming(struct device *dev,
+ struct vsp1_vspx_frame_end *frame_end)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ u32 value;
+ int ret;
+
+ if (!frame_end)
+ return -EINVAL;
+
+ guard(mutex)(&vspx_pipe->mutex);
+
+ scoped_guard(spinlock_irq, &vspx_pipe->lock) {
+ if (vspx_pipe->enabled)
+ return -EBUSY;
+ }
+
+ vspx_pipe->vspx_frame_end = frame_end->vspx_frame_end;
+ vspx_pipe->frame_end_data = frame_end->frame_end_data;
+
+ /* Enable the VSP1 and prepare for streaming. */
+ vsp1_pipeline_dump(pipe, "VSPX job");
+
+ ret = vsp1_device_get(vsp1);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Make sure VSPX is not active. This should never happen in normal
+ * usage
+ */
+ value = vsp1_read(vsp1, VI6_CMD(0));
+ if (value & VI6_CMD_STRCMD) {
+ dev_err(vsp1->dev,
+ "%s: Starting of WPF0 already reserved\n", __func__);
+ ret = -EBUSY;
+ goto error_put;
+ }
+
+ value = vsp1_read(vsp1, VI6_STATUS);
+ if (value & VI6_STATUS_SYS_ACT(0)) {
+ dev_err(vsp1->dev,
+ "%s: WPF0 has not entered idle state\n", __func__);
+ ret = -EBUSY;
+ goto error_put;
+ }
+
+ scoped_guard(spinlock_irq, &vspx_pipe->lock) {
+ vspx_pipe->enabled = true;
+ }
+
+ return 0;
+
+error_put:
+ vsp1_device_put(vsp1);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_start_streaming);
+
+/**
+ * vsp1_isp_stop_streaming - Stop the VSPX
+ * @dev: The VSP1 struct device
+ *
+ * Stop the VSPX operation by stopping the vsp1 pipeline and waiting for the
+ * last frame in transfer, if any, to complete.
+ *
+ * The caller is responsible for tracking the stopped state of the VSPX.
+ * Attempting to stop an already stopped VSPX instance is a nop.
+ */
+void vsp1_isp_stop_streaming(struct device *dev)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+
+ guard(mutex)(&vspx_pipe->mutex);
+
+ scoped_guard(spinlock_irq, &vspx_pipe->lock) {
+ if (!vspx_pipe->enabled)
+ return;
+
+ vspx_pipe->enabled = false;
+ }
+
+ WARN_ON_ONCE(vsp1_pipeline_stop(pipe));
+
+ vspx_pipe->vspx_frame_end = NULL;
+ vsp1_dlm_reset(pipe->output->dlm);
+ vsp1_device_put(vsp1);
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_stop_streaming);
+
+/**
+ * vsp1_isp_job_prepare - Prepare a new buffer transfer job
+ * @dev: The VSP1 struct device
+ * @job: The job description
+ *
+ * Prepare a new buffer transfer job by populating a display list that will be
+ * later executed by a call to vsp1_isp_job_run(). All pending jobs must be
+ * released after stopping the streaming operations with a call to
+ * vsp1_isp_job_release().
+ *
+ * In order for the VSPX to accept new jobs to prepare the VSPX must have been
+ * started.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_job_prepare(struct device *dev, struct vsp1_isp_job_desc *job)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ const struct v4l2_pix_format_mplane *pix_mp;
+ struct vsp1_dl_list *second_dl = NULL;
+ struct vsp1_dl_body *dlb;
+ struct vsp1_dl_list *dl;
+ int ret;
+
+ /*
+ * Transfer the buffers described in the job: an optional ConfigDMA
+ * parameters buffer and a RAW image.
+ */
+
+ job->dl = vsp1_dl_list_get(pipe->output->dlm);
+ if (!job->dl)
+ return -ENOMEM;
+
+ dl = job->dl;
+ dlb = vsp1_dl_list_get_body0(dl);
+
+ /* Configure IIF routing and enable IIF function. */
+ vsp1_entity_route_setup(pipe->iif, pipe, dlb);
+ vsp1_entity_configure_stream(pipe->iif, pipe->iif->state, pipe,
+ dl, dlb);
+
+ /* Configure WPF0 to enable RPF0 as source. */
+ vsp1_entity_route_setup(&pipe->output->entity, pipe, dlb);
+ vsp1_entity_configure_stream(&pipe->output->entity,
+ pipe->output->entity.state, pipe,
+ dl, dlb);
+
+ if (job->config.pairs) {
+ /*
+ * Writing less than 17 pairs corrupts the output images ( < 16
+ * pairs) or freezes the VSPX operations (= 16 pairs). Only
+ * allow more than 16 pairs to be written.
+ */
+ if (job->config.pairs <= 16) {
+ ret = -EINVAL;
+ goto error_put_dl;
+ }
+
+ /*
+ * Configure RPF0 for ConfigDMA data. Transfer the number of
+ * configuration pairs plus 2 words for the header.
+ */
+ ret = vsp1_vspx_pipeline_configure(vsp1, job->config.mem,
+ V4L2_META_FMT_GENERIC_8,
+ job->config.pairs * 2 + 2, 1,
+ job->config.pairs * 2 + 2,
+ VSPX_IIF_SINK_PAD_CONFIG,
+ dl, dlb);
+ if (ret)
+ goto error_put_dl;
+
+ second_dl = vsp1_dl_list_get(pipe->output->dlm);
+ if (!second_dl) {
+ ret = -ENOMEM;
+ goto error_put_dl;
+ }
+
+ dl = second_dl;
+ dlb = vsp1_dl_list_get_body0(dl);
+ }
+
+ /* Configure RPF0 for RAW image transfer. */
+ pix_mp = &job->img.fmt;
+ ret = vsp1_vspx_pipeline_configure(vsp1, job->img.mem,
+ pix_mp->pixelformat,
+ pix_mp->width, pix_mp->height,
+ pix_mp->plane_fmt[0].bytesperline,
+ VSPX_IIF_SINK_PAD_IMG, dl, dlb);
+ if (ret)
+ goto error_put_dl;
+
+ if (second_dl)
+ vsp1_dl_list_add_chain(job->dl, second_dl);
+
+ return 0;
+
+error_put_dl:
+ if (second_dl)
+ vsp1_dl_list_put(second_dl);
+ vsp1_dl_list_put(job->dl);
+ job->dl = NULL;
+ return ret;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_job_prepare);
+
+/**
+ * vsp1_isp_job_run - Run a buffer transfer job
+ * @dev: The VSP1 struct device
+ * @job: The job to be run
+ *
+ * Run the display list contained in the job description provided by the caller.
+ * The job must have been prepared with a call to vsp1_isp_job_prepare() and
+ * the job's display list shall be valid.
+ *
+ * Jobs can be run only on VSPX instances which have been started. Requests
+ * to run a job after the VSPX has been stopped return -EINVAL and the job
+ * resources shall be released by the caller with vsp1_isp_job_release().
+ * When a job is run successfully all the resources acquired by
+ * vsp1_isp_job_prepare() are released by this function and no further action
+ * is required to the caller.
+ *
+ * Return: %0 on success or a negative error code on failure
+ */
+int vsp1_isp_job_run(struct device *dev, struct vsp1_isp_job_desc *job)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+ struct vsp1_pipeline *pipe = &vspx_pipe->pipe;
+ u32 value;
+
+ /* Make sure VSPX is not busy processing a frame. */
+ value = vsp1_read(vsp1, VI6_CMD(0));
+ if (value) {
+ dev_err(vsp1->dev,
+ "%s: Starting of WPF0 already reserved\n", __func__);
+ return -EBUSY;
+ }
+
+ scoped_guard(spinlock_irqsave, &vspx_pipe->lock) {
+ /*
+ * If a new job is scheduled when the VSPX is stopped, do not
+ * run it.
+ */
+ if (!vspx_pipe->enabled)
+ return -EINVAL;
+
+ vsp1_dl_list_commit(job->dl, 0);
+
+ /*
+ * The display list is now under control of the display list
+ * manager and will be released automatically when the job
+ * completes.
+ */
+ job->dl = NULL;
+ }
+
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ vsp1_pipeline_run(pipe);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_job_run);
+
+/**
+ * vsp1_isp_job_release - Release a non processed transfer job
+ * @dev: The VSP1 struct device
+ * @job: The job to release
+ *
+ * Release a job prepared by a call to vsp1_isp_job_prepare() and not yet
+ * run. All pending jobs shall be released after streaming has been stopped.
+ */
+void vsp1_isp_job_release(struct device *dev,
+ struct vsp1_isp_job_desc *job)
+{
+ vsp1_dl_list_put(job->dl);
+}
+EXPORT_SYMBOL_GPL(vsp1_isp_job_release);
+
+/* -----------------------------------------------------------------------------
+ * Initialization and cleanup
+ */
+
+int vsp1_vspx_init(struct vsp1_device *vsp1)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe;
+ struct vsp1_pipeline *pipe;
+
+ vsp1->vspx = devm_kzalloc(vsp1->dev, sizeof(*vsp1->vspx), GFP_KERNEL);
+ if (!vsp1->vspx)
+ return -ENOMEM;
+
+ vsp1->vspx->vsp1 = vsp1;
+
+ vspx_pipe = &vsp1->vspx->pipe;
+ vspx_pipe->enabled = false;
+
+ pipe = &vspx_pipe->pipe;
+
+ vsp1_pipeline_init(pipe);
+
+ pipe->partitions = 1;
+ pipe->part_table = &vspx_pipe->partition;
+ pipe->interlaced = false;
+ pipe->frame_end = vsp1_vspx_pipeline_frame_end;
+
+ mutex_init(&vspx_pipe->mutex);
+ spin_lock_init(&vspx_pipe->lock);
+
+ /*
+ * Initialize RPF0 as input for VSPX and use it unconditionally for
+ * now.
+ */
+ pipe->inputs[0] = vsp1->rpf[0];
+ pipe->inputs[0]->entity.pipe = pipe;
+ pipe->inputs[0]->entity.sink = &vsp1->iif->entity;
+ list_add_tail(&pipe->inputs[0]->entity.list_pipe, &pipe->entities);
+
+ pipe->iif = &vsp1->iif->entity;
+ pipe->iif->pipe = pipe;
+ pipe->iif->sink = &vsp1->wpf[0]->entity;
+ pipe->iif->sink_pad = RWPF_PAD_SINK;
+ list_add_tail(&pipe->iif->list_pipe, &pipe->entities);
+
+ pipe->output = vsp1->wpf[0];
+ pipe->output->entity.pipe = pipe;
+ list_add_tail(&pipe->output->entity.list_pipe, &pipe->entities);
+
+ return 0;
+}
+
+void vsp1_vspx_cleanup(struct vsp1_device *vsp1)
+{
+ struct vsp1_vspx_pipeline *vspx_pipe = &vsp1->vspx->pipe;
+
+ mutex_destroy(&vspx_pipe->mutex);
+}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.h b/drivers/media/platform/renesas/vsp1/vsp1_vspx.h
new file mode 100644
index 000000000000..f871bf9e7dec
--- /dev/null
+++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * vsp1_vspx.h -- R-Car Gen 4 VSPX
+ *
+ * Copyright (C) 2025 Ideas On Board Oy
+ * Copyright (C) 2025 Renesas Electronics Corporation
+ */
+#ifndef __VSP1_VSPX_H__
+#define __VSP1_VSPX_H__
+
+#include "vsp1.h"
+
+int vsp1_vspx_init(struct vsp1_device *vsp1);
+void vsp1_vspx_cleanup(struct vsp1_device *vsp1);
+
+#endif /* __VSP1_VSPX_H__ */
diff --git a/drivers/media/platform/rockchip/Kconfig b/drivers/media/platform/rockchip/Kconfig
index b41d3960c1b4..9bbeec4996aa 100644
--- a/drivers/media/platform/rockchip/Kconfig
+++ b/drivers/media/platform/rockchip/Kconfig
@@ -4,3 +4,4 @@ comment "Rockchip media platform drivers"
source "drivers/media/platform/rockchip/rga/Kconfig"
source "drivers/media/platform/rockchip/rkisp1/Kconfig"
+source "drivers/media/platform/rockchip/rkvdec/Kconfig"
diff --git a/drivers/media/platform/rockchip/Makefile b/drivers/media/platform/rockchip/Makefile
index 4f782b876ac9..286dc5c53f7e 100644
--- a/drivers/media/platform/rockchip/Makefile
+++ b/drivers/media/platform/rockchip/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += rga/
obj-y += rkisp1/
+obj-y += rkvdec/
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
index ca952fd0829b..5f187f9efc7b 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-common.h
@@ -415,6 +415,8 @@ struct rkisp1_params {
spinlock_t config_lock; /* locks the buffers list 'params' */
struct list_head params;
+ struct v4l2_ctrl_handler ctrls;
+
const struct v4l2_meta_format *metafmt;
enum v4l2_quantization quantization;
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index b28f4140c8a3..f1585f8fa0f4 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -5,6 +5,7 @@
* Copyright (C) 2017 Rockchip Electronics Co., Ltd.
*/
+#include <linux/bitfield.h>
#include <linux/math.h>
#include <linux/string.h>
@@ -60,6 +61,7 @@ union rkisp1_ext_params_config {
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;
+ struct rkisp1_ext_params_wdr_config wdr;
};
enum rkisp1_params_formats {
@@ -1348,6 +1350,73 @@ rkisp1_compand_compress_config(struct rkisp1_params *params,
arg->x);
}
+static void rkisp1_wdr_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_wdr_config *arg)
+{
+ unsigned int i;
+ u32 value;
+
+ value = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_WDR_CTRL)
+ & ~(RKISP1_CIF_ISP_WDR_USE_IREF |
+ RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT |
+ RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE |
+ RKISP1_CIF_ISP_WDR_USE_Y9_8 |
+ RKISP1_CIF_ISP_WDR_USE_RGB7_8 |
+ RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT |
+ RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK);
+
+ /* Colorspace and chrominance mapping */
+ if (arg->use_rgb_colorspace)
+ value |= RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT;
+
+ if (!arg->use_rgb_colorspace && arg->bypass_chroma_mapping)
+ value |= RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE;
+
+ /* Illumination reference */
+ if (arg->use_iref) {
+ value |= RKISP1_CIF_ISP_WDR_USE_IREF;
+
+ if (arg->iref_config.use_y9_8)
+ value |= RKISP1_CIF_ISP_WDR_USE_Y9_8;
+
+ if (arg->iref_config.use_rgb7_8)
+ value |= RKISP1_CIF_ISP_WDR_USE_RGB7_8;
+
+ if (arg->iref_config.disable_transient)
+ value |= RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT;
+
+ value |= FIELD_PREP(RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK,
+ min(arg->iref_config.rgb_factor,
+ RKISP1_CIF_ISP_WDR_RGB_FACTOR_MAX));
+ }
+
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_CTRL, value);
+
+ /* RGB and Luminance offsets */
+ value = FIELD_PREP(RKISP1_CIF_ISP_WDR_RGB_OFFSET_MASK,
+ arg->rgb_offset)
+ | FIELD_PREP(RKISP1_CIF_ISP_WDR_LUM_OFFSET_MASK,
+ arg->luma_offset);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_OFFSET, value);
+
+ /* DeltaMin */
+ value = FIELD_PREP(RKISP1_CIF_ISP_WDR_DMIN_THRESH_MASK,
+ arg->dmin_thresh)
+ | FIELD_PREP(RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK,
+ min(arg->dmin_strength,
+ RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX));
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_DELTAMIN, value);
+
+ /* Tone curve */
+ for (i = 0; i < RKISP1_CIF_ISP_WDR_CURVE_NUM_DY_REGS; i++)
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_TONECURVE(i),
+ arg->tone_curve.dY[i]);
+ for (i = 0; i < RKISP1_CIF_ISP_WDR_CURVE_NUM_COEFF; i++)
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_WDR_TONECURVE_YM(i),
+ arg->tone_curve.ym[i] &
+ RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK);
+}
+
static void
rkisp1_isp_isr_other_config(struct rkisp1_params *params,
const struct rkisp1_params_cfg *new_params)
@@ -2005,6 +2074,25 @@ static void rkisp1_ext_params_compand_compress(struct rkisp1_params *params,
RKISP1_CIF_ISP_COMPAND_CTRL_COMPRESS_ENABLE);
}
+static void rkisp1_ext_params_wdr(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_wdr_config *wdr = &block->wdr;
+
+ if (wdr->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_WDR_CTRL,
+ RKISP1_CIF_ISP_WDR_CTRL_ENABLE);
+ return;
+ }
+
+ rkisp1_wdr_config(params, &wdr->config);
+
+ if ((wdr->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(wdr->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_WDR_CTRL,
+ RKISP1_CIF_ISP_WDR_CTRL_ENABLE);
+}
+
typedef void (*rkisp1_block_handler)(struct rkisp1_params *params,
const union rkisp1_ext_params_config *config);
@@ -2118,6 +2206,11 @@ static const struct rkisp1_ext_params_handler {
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
.features = RKISP1_FEATURE_COMPAND,
},
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR] = {
+ .size = sizeof(struct rkisp1_ext_params_wdr_config),
+ .handler = rkisp1_ext_params_wdr,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
};
static void rkisp1_ext_params_config(struct rkisp1_params *params,
@@ -2736,6 +2829,44 @@ static int rkisp1_params_init_vb2_queue(struct vb2_queue *q,
return vb2_queue_init(q);
}
+static int rkisp1_params_ctrl_init(struct rkisp1_params *params)
+{
+ struct v4l2_ctrl_config ctrl_config = {
+ .id = RKISP1_CID_SUPPORTED_PARAMS_BLOCKS,
+ .name = "Supported Params Blocks",
+ .type = V4L2_CTRL_TYPE_BITMASK,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+ };
+ int ret;
+
+ v4l2_ctrl_handler_init(&params->ctrls, 1);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(rkisp1_ext_params_handlers); i++) {
+ const struct rkisp1_ext_params_handler *block_handler;
+
+ block_handler = &rkisp1_ext_params_handlers[i];
+ ctrl_config.max |= BIT(i);
+
+ if ((params->rkisp1->info->features & block_handler->features) !=
+ block_handler->features)
+ continue;
+
+ ctrl_config.def |= BIT(i);
+ }
+
+ v4l2_ctrl_new_custom(&params->ctrls, &ctrl_config, NULL);
+
+ params->vnode.vdev.ctrl_handler = &params->ctrls;
+
+ if (params->ctrls.error) {
+ ret = params->ctrls.error;
+ v4l2_ctrl_handler_free(&params->ctrls);
+ return ret;
+ }
+
+ return 0;
+}
+
int rkisp1_params_register(struct rkisp1_device *rkisp1)
{
struct rkisp1_params *params = &rkisp1->params;
@@ -2763,7 +2894,9 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1)
vdev->queue = &node->buf_queue;
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_META_OUTPUT;
vdev->vfl_dir = VFL_DIR_TX;
- rkisp1_params_init_vb2_queue(vdev->queue, params);
+ ret = rkisp1_params_init_vb2_queue(vdev->queue, params);
+ if (ret)
+ goto err_media;
params->metafmt = &rkisp1_params_formats[RKISP1_PARAMS_FIXED];
@@ -2777,18 +2910,26 @@ int rkisp1_params_register(struct rkisp1_device *rkisp1)
node->pad.flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&vdev->entity, 1, &node->pad);
if (ret)
- goto error;
+ goto err_media;
+
+ ret = rkisp1_params_ctrl_init(params);
+ if (ret) {
+ dev_err(rkisp1->dev, "Control initialization error %d\n", ret);
+ goto err_media;
+ }
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret) {
dev_err(rkisp1->dev,
"failed to register %s, ret=%d\n", vdev->name, ret);
- goto error;
+ goto err_ctrl;
}
return 0;
-error:
+err_ctrl:
+ v4l2_ctrl_handler_free(&params->ctrls);
+err_media:
media_entity_cleanup(&vdev->entity);
mutex_destroy(&node->vlock);
return ret;
@@ -2804,6 +2945,7 @@ void rkisp1_params_unregister(struct rkisp1_device *rkisp1)
return;
vb2_video_unregister_device(vdev);
+ v4l2_ctrl_handler_free(&params->ctrls);
media_entity_cleanup(&vdev->entity);
mutex_destroy(&node->vlock);
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
index 139177db9c6d..fbeb186cde0d 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -703,6 +703,27 @@
#define RKISP1_CIF_ISP_COMPAND_CTRL_SOFT_RESET_FLAG BIT(2)
#define RKISP1_CIF_ISP_COMPAND_CTRL_BLS_ENABLE BIT(3)
+/* WDR */
+/* ISP_WDR_CTRL */
+#define RKISP1_CIF_ISP_WDR_CTRL_ENABLE BIT(0)
+#define RKISP1_CIF_ISP_WDR_COLOR_SPACE_SELECT BIT(1)
+#define RKISP1_CIF_ISP_WDR_CR_MAPPING_DISABLE BIT(2)
+#define RKISP1_CIF_ISP_WDR_USE_IREF BIT(3)
+#define RKISP1_CIF_ISP_WDR_USE_Y9_8 BIT(4)
+#define RKISP1_CIF_ISP_WDR_USE_RGB7_8 BIT(5)
+#define RKISP1_CIF_ISP_WDR_DISABLE_TRANSIENT BIT(6)
+#define RKISP1_CIF_ISP_WDR_RGB_FACTOR_MASK GENMASK(11, 8)
+#define RKISP1_CIF_ISP_WDR_RGB_FACTOR_MAX 8U
+/* ISP_WDR_TONE_CURVE_YM */
+#define RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK GENMASK(12, 0)
+/* ISP_WDR_OFFSET */
+#define RKISP1_CIF_ISP_WDR_RGB_OFFSET_MASK GENMASK(11, 0)
+#define RKISP1_CIF_ISP_WDR_LUM_OFFSET_MASK GENMASK(27, 16)
+/* ISP_WDR_DELTAMIN */
+#define RKISP1_CIF_ISP_WDR_DMIN_THRESH_MASK GENMASK(11, 0)
+#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK GENMASK(20, 16)
+#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX 16U
+
/* =================================================================== */
/* CIF Registers */
/* =================================================================== */
@@ -1295,82 +1316,12 @@
#define RKISP1_CIF_ISP_WDR_BASE 0x00002a00
#define RKISP1_CIF_ISP_WDR_CTRL (RKISP1_CIF_ISP_WDR_BASE + 0x00000000)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000004)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_2 (RKISP1_CIF_ISP_WDR_BASE + 0x00000008)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_3 (RKISP1_CIF_ISP_WDR_BASE + 0x0000000c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000010)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0 (RKISP1_CIF_ISP_WDR_BASE + 0x00000014)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1 (RKISP1_CIF_ISP_WDR_BASE + 0x00000018)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2 (RKISP1_CIF_ISP_WDR_BASE + 0x0000001c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3 (RKISP1_CIF_ISP_WDR_BASE + 0x00000020)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4 (RKISP1_CIF_ISP_WDR_BASE + 0x00000024)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5 (RKISP1_CIF_ISP_WDR_BASE + 0x00000028)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6 (RKISP1_CIF_ISP_WDR_BASE + 0x0000002c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7 (RKISP1_CIF_ISP_WDR_BASE + 0x00000030)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8 (RKISP1_CIF_ISP_WDR_BASE + 0x00000034)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9 (RKISP1_CIF_ISP_WDR_BASE + 0x00000038)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10 (RKISP1_CIF_ISP_WDR_BASE + 0x0000003c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11 (RKISP1_CIF_ISP_WDR_BASE + 0x00000040)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12 (RKISP1_CIF_ISP_WDR_BASE + 0x00000044)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13 (RKISP1_CIF_ISP_WDR_BASE + 0x00000048)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14 (RKISP1_CIF_ISP_WDR_BASE + 0x0000004c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15 (RKISP1_CIF_ISP_WDR_BASE + 0x00000050)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16 (RKISP1_CIF_ISP_WDR_BASE + 0x00000054)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17 (RKISP1_CIF_ISP_WDR_BASE + 0x00000058)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18 (RKISP1_CIF_ISP_WDR_BASE + 0x0000005c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19 (RKISP1_CIF_ISP_WDR_BASE + 0x00000060)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20 (RKISP1_CIF_ISP_WDR_BASE + 0x00000064)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21 (RKISP1_CIF_ISP_WDR_BASE + 0x00000068)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22 (RKISP1_CIF_ISP_WDR_BASE + 0x0000006c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23 (RKISP1_CIF_ISP_WDR_BASE + 0x00000070)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24 (RKISP1_CIF_ISP_WDR_BASE + 0x00000074)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25 (RKISP1_CIF_ISP_WDR_BASE + 0x00000078)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26 (RKISP1_CIF_ISP_WDR_BASE + 0x0000007c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27 (RKISP1_CIF_ISP_WDR_BASE + 0x00000080)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28 (RKISP1_CIF_ISP_WDR_BASE + 0x00000084)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29 (RKISP1_CIF_ISP_WDR_BASE + 0x00000088)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30 (RKISP1_CIF_ISP_WDR_BASE + 0x0000008c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31 (RKISP1_CIF_ISP_WDR_BASE + 0x00000090)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32 (RKISP1_CIF_ISP_WDR_BASE + 0x00000094)
+#define RKISP1_CIF_ISP_WDR_TONECURVE(n) (RKISP1_CIF_ISP_WDR_BASE + 0x00000004 + (n) * 4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM(n) (RKISP1_CIF_ISP_WDR_BASE + 0x00000014 + (n) * 4)
#define RKISP1_CIF_ISP_WDR_OFFSET (RKISP1_CIF_ISP_WDR_BASE + 0x00000098)
#define RKISP1_CIF_ISP_WDR_DELTAMIN (RKISP1_CIF_ISP_WDR_BASE + 0x0000009c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000a8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000ac)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_0_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_1_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_2_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000b8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_3_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000bc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_4_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_5_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_6_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000c8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_7_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000cc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_8_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_9_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_10_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000d8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_11_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000dc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_12_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_13_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_14_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000e8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_15_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000ec)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_16_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f0)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_17_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f4)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_18_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000f8)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_19_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x000000fc)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_20_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000100)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_21_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000104)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_22_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000108)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_23_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000010c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_24_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000110)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_25_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000114)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_26_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000118)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_27_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000011c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_28_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000120)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_29_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000124)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_30_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000128)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_31_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x0000012c)
-#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_32_SHD (RKISP1_CIF_ISP_WDR_BASE + 0x00000130)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_SHD(n) (RKISP1_CIF_ISP_WDR_BASE + 0x000000a0 + (n) * 4)
+#define RKISP1_CIF_ISP_WDR_TONECURVE_YM_SHD(n) (RKISP1_CIF_ISP_WDR_BASE + 0x000000b0 + (n) * 4)
#define RKISP1_CIF_ISP_HIST_BASE_V12 0x00002c00
#define RKISP1_CIF_ISP_HIST_CTRL_V12 (RKISP1_CIF_ISP_HIST_BASE_V12 + 0x00000000)
diff --git a/drivers/staging/media/rkvdec/Kconfig b/drivers/media/platform/rockchip/rkvdec/Kconfig
index 5f3bdd848a2c..5f3bdd848a2c 100644
--- a/drivers/staging/media/rkvdec/Kconfig
+++ b/drivers/media/platform/rockchip/rkvdec/Kconfig
diff --git a/drivers/staging/media/rkvdec/Makefile b/drivers/media/platform/rockchip/rkvdec/Makefile
index cb86b429cfaa..cb86b429cfaa 100644
--- a/drivers/staging/media/rkvdec/Makefile
+++ b/drivers/media/platform/rockchip/rkvdec/Makefile
diff --git a/drivers/staging/media/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
index d14b4d173448..d14b4d173448 100644
--- a/drivers/staging/media/rkvdec/rkvdec-h264.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
diff --git a/drivers/staging/media/rkvdec/rkvdec-regs.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
index 15b9bee92016..15b9bee92016 100644
--- a/drivers/staging/media/rkvdec/rkvdec-regs.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-regs.h
diff --git a/drivers/staging/media/rkvdec/rkvdec-vp9.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
index 0e7e16f20eeb..0e7e16f20eeb 100644
--- a/drivers/staging/media/rkvdec/rkvdec-vp9.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vp9.c
diff --git a/drivers/staging/media/rkvdec/rkvdec.c b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
index 3367902f22de..d707088ec0dc 100644
--- a/drivers/staging/media/rkvdec/rkvdec.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.c
@@ -11,6 +11,7 @@
#include <linux/clk.h>
#include <linux/interrupt.h>
+#include <linux/iommu.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -1055,24 +1056,42 @@ static void rkvdec_v4l2_cleanup(struct rkvdec_dev *rkvdec)
v4l2_device_unregister(&rkvdec->v4l2_dev);
}
+static void rkvdec_iommu_restore(struct rkvdec_dev *rkvdec)
+{
+ if (rkvdec->empty_domain) {
+ /*
+ * To rewrite mapping into the attached IOMMU core, attach a new empty domain that
+ * will program an empty table, then detach it to restore the default domain and
+ * all cached mappings.
+ * This is safely done in this interrupt handler to make sure no memory get mapped
+ * through the IOMMU while the empty domain is attached.
+ */
+ iommu_attach_device(rkvdec->empty_domain, rkvdec->dev);
+ iommu_detach_device(rkvdec->empty_domain, rkvdec->dev);
+ }
+}
+
static irqreturn_t rkvdec_irq_handler(int irq, void *priv)
{
struct rkvdec_dev *rkvdec = priv;
+ struct rkvdec_ctx *ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
enum vb2_buffer_state state;
u32 status;
status = readl(rkvdec->regs + RKVDEC_REG_INTERRUPT);
- state = (status & RKVDEC_RDY_STA) ?
- VB2_BUF_STATE_DONE : VB2_BUF_STATE_ERROR;
-
writel(0, rkvdec->regs + RKVDEC_REG_INTERRUPT);
- if (cancel_delayed_work(&rkvdec->watchdog_work)) {
- struct rkvdec_ctx *ctx;
- ctx = v4l2_m2m_get_curr_priv(rkvdec->m2m_dev);
- rkvdec_job_finish(ctx, state);
+ if (status & RKVDEC_RDY_STA) {
+ state = VB2_BUF_STATE_DONE;
+ } else {
+ state = VB2_BUF_STATE_ERROR;
+ if (status & RKVDEC_SOFTRESET_RDY)
+ rkvdec_iommu_restore(rkvdec);
}
+ if (cancel_delayed_work(&rkvdec->watchdog_work))
+ rkvdec_job_finish(ctx, state);
+
return IRQ_HANDLED;
}
@@ -1140,6 +1159,13 @@ static int rkvdec_probe(struct platform_device *pdev)
return ret;
}
+ if (iommu_get_domain_for_dev(&pdev->dev)) {
+ rkvdec->empty_domain = iommu_paging_domain_alloc(rkvdec->dev);
+
+ if (!rkvdec->empty_domain)
+ dev_warn(rkvdec->dev, "cannot alloc new empty domain\n");
+ }
+
vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32));
irq = platform_get_irq(pdev, 0);
@@ -1179,6 +1205,9 @@ static void rkvdec_remove(struct platform_device *pdev)
rkvdec_v4l2_cleanup(rkvdec);
pm_runtime_disable(&pdev->dev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
+
+ if (rkvdec->empty_domain)
+ iommu_domain_free(rkvdec->empty_domain);
}
#ifdef CONFIG_PM
diff --git a/drivers/staging/media/rkvdec/rkvdec.h b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
index 9a9f4fced7a1..f6e8bf38add3 100644
--- a/drivers/staging/media/rkvdec/rkvdec.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec.h
@@ -110,6 +110,7 @@ struct rkvdec_dev {
void __iomem *regs;
struct mutex vdev_lock; /* serializes ioctls */
struct delayed_work watchdog_work;
+ struct iommu_domain *empty_domain;
};
struct rkvdec_ctx {
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 b243cbb1d010..b5b37b6f8fa8 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.c
@@ -131,7 +131,7 @@ static const struct dev_pm_ops fimc_is_i2c_pm_ops = {
};
static const struct of_device_id fimc_is_i2c_of_match[] = {
- { .compatible = FIMC_IS_I2C_COMPATIBLE },
+ { .compatible = "samsung,exynos4212-i2c-isp" },
{ },
};
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
index a23bd20be6c8..69d597e5c297 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is-i2c.h
@@ -6,7 +6,5 @@
* Sylwester Nawrocki <s.nawrocki@samsung.com>
*/
-#define FIMC_IS_I2C_COMPATIBLE "samsung,exynos4212-i2c-isp"
-
int fimc_is_register_i2c_driver(void);
void fimc_is_unregister_i2c_driver(void);
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-is.c b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
index 2e8fe9e49735..ed7b7ca16f71 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-is.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-is.c
@@ -207,7 +207,7 @@ static int fimc_is_register_subdevs(struct fimc_is *is)
if (ret < 0)
return ret;
- for_each_compatible_node(i2c_bus, NULL, FIMC_IS_I2C_COMPATIBLE) {
+ for_each_compatible_node(i2c_bus, NULL, "samsung,exynos4212-i2c-isp") {
for_each_available_child_of_node(i2c_bus, child) {
ret = fimc_is_parse_sensor_config(is, index, child);
diff --git a/drivers/media/platform/samsung/exynos4-is/media-dev.c b/drivers/media/platform/samsung/exynos4-is/media-dev.c
index b5ee3c547789..c781853586fd 100644
--- a/drivers/media/platform/samsung/exynos4-is/media-dev.c
+++ b/drivers/media/platform/samsung/exynos4-is/media-dev.c
@@ -482,15 +482,12 @@ static int fimc_md_parse_one_endpoint(struct fimc_md *fmd,
static int fimc_md_parse_port_node(struct fimc_md *fmd,
struct device_node *port)
{
- struct device_node *ep;
int ret;
- for_each_child_of_node(port, ep) {
+ for_each_child_of_node_scoped(port, ep) {
ret = fimc_md_parse_one_endpoint(fmd, ep);
- if (ret < 0) {
- of_node_put(ep);
+ if (ret < 0)
return ret;
- }
}
return 0;
@@ -501,7 +498,6 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
{
struct device_node *parent = fmd->pdev->dev.of_node;
struct device_node *ports = NULL;
- struct device_node *node;
int ret;
/*
@@ -518,7 +514,7 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
fmd->num_sensors = 0;
/* Attach sensors linked to MIPI CSI-2 receivers */
- for_each_available_child_of_node(parent, node) {
+ for_each_available_child_of_node_scoped(parent, node) {
struct device_node *port;
if (!of_node_name_eq(node, "csis"))
@@ -530,10 +526,8 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
ret = fimc_md_parse_port_node(fmd, port);
of_node_put(port);
- if (ret < 0) {
- of_node_put(node);
+ if (ret < 0)
goto cleanup;
- }
}
/* Attach sensors listed in the parallel-ports node */
@@ -541,12 +535,10 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd)
if (!ports)
goto rpm_put;
- for_each_child_of_node(ports, node) {
+ for_each_child_of_node_scoped(ports, node) {
ret = fimc_md_parse_port_node(fmd, node);
- if (ret < 0) {
- of_node_put(node);
+ if (ret < 0)
goto cleanup;
- }
}
of_node_put(ports);
@@ -736,10 +728,9 @@ dev_unlock:
static int fimc_md_register_platform_entities(struct fimc_md *fmd,
struct device_node *parent)
{
- struct device_node *node;
int ret = 0;
- for_each_available_child_of_node(parent, node) {
+ for_each_available_child_of_node_scoped(parent, node) {
struct platform_device *pdev;
int plat_entity = -1;
@@ -762,10 +753,8 @@ static int fimc_md_register_platform_entities(struct fimc_md *fmd,
ret = fimc_md_register_platform_entity(fmd, pdev,
plat_entity);
put_device(&pdev->dev);
- if (ret < 0) {
- of_node_put(node);
+ if (ret < 0)
break;
- }
}
return ret;
diff --git a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
index 602c37cbe177..89bd15a4d26a 100644
--- a/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
+++ b/drivers/media/platform/st/sti/c8sectpfe/c8sectpfe-core.c
@@ -658,7 +658,7 @@ static irqreturn_t c8sectpfe_error_irq_handler(int irq, void *priv)
static int c8sectpfe_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *child, *np = dev->of_node;
+ struct device_node *np = dev->of_node;
struct c8sectpfei *fei;
struct resource *res;
int ret, index = 0;
@@ -742,17 +742,15 @@ static int c8sectpfe_probe(struct platform_device *pdev)
return PTR_ERR(fei->pinctrl);
}
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
struct device_node *i2c_bus;
fei->channel_data[index] = devm_kzalloc(dev,
sizeof(struct channel_info),
GFP_KERNEL);
- if (!fei->channel_data[index]) {
- ret = -ENOMEM;
- goto err_node_put;
- }
+ if (!fei->channel_data[index])
+ return -ENOMEM;
tsin = fei->channel_data[index];
@@ -761,7 +759,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
ret = of_property_read_u32(child, "tsin-num", &tsin->tsin_id);
if (ret) {
dev_err(&pdev->dev, "No tsin_num found\n");
- goto err_node_put;
+ return ret;
}
/* sanity check value */
@@ -769,8 +767,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"tsin-num %d specified greater than number\n\tof input block hw in SoC! (%d)",
tsin->tsin_id, fei->hw_stats.num_ib);
- ret = -EINVAL;
- goto err_node_put;
+ return -EINVAL;
}
tsin->invert_ts_clk = of_property_read_bool(child,
@@ -786,22 +783,20 @@ static int c8sectpfe_probe(struct platform_device *pdev)
&tsin->dvb_card);
if (ret) {
dev_err(&pdev->dev, "No dvb-card found\n");
- goto err_node_put;
+ return ret;
}
i2c_bus = of_parse_phandle(child, "i2c-bus", 0);
if (!i2c_bus) {
dev_err(&pdev->dev, "No i2c-bus found\n");
- ret = -ENODEV;
- goto err_node_put;
+ return -ENODEV;
}
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");
- ret = -ENODEV;
- goto err_node_put;
+ return -ENODEV;
}
/* Acquire reset GPIO and activate it */
@@ -813,7 +808,7 @@ static int c8sectpfe_probe(struct platform_device *pdev)
if (ret && ret != -EBUSY) {
dev_err(dev, "Can't request tsin%d reset gpio\n",
fei->channel_data[index]->tsin_id);
- goto err_node_put;
+ return ret;
}
if (!ret) {
@@ -855,10 +850,6 @@ static int c8sectpfe_probe(struct platform_device *pdev)
c8sectpfe_debugfs_init(fei);
return 0;
-
-err_node_put:
- of_node_put(child);
- return ret;
}
static void c8sectpfe_remove(struct platform_device *pdev)
@@ -897,16 +888,15 @@ static void c8sectpfe_remove(struct platform_device *pdev)
static int configure_channels(struct c8sectpfei *fei)
{
int index = 0, ret;
- struct device_node *child, *np = fei->dev->of_node;
+ struct device_node *np = fei->dev->of_node;
/* iterate round each tsin and configure memdma descriptor and IB hw */
- for_each_child_of_node(np, child) {
+ for_each_child_of_node_scoped(np, child) {
ret = configure_memdma_and_inputblock(fei,
fei->channel_data[index]);
if (ret) {
dev_err(fei->dev,
"configure_memdma_and_inputblock failed\n");
- of_node_put(child);
goto err_unmap;
}
index++;
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 6412a00be8ea..b628d6e081db 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -619,6 +619,7 @@ static void ti_csi2rx_dma_callback(void *param)
if (ti_csi2rx_start_dma(csi, buf)) {
dev_err(csi->dev, "Failed to queue the next buffer for DMA\n");
+ list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
} else {
list_move_tail(&buf->list, &dma->submitted);
@@ -895,6 +896,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi)
q->dev = dmaengine_get_dma_device(csi->dma.chan);
q->lock = &csi->mutex;
q->min_queued_buffers = 1;
+ q->allow_cache_hints = 1;
ret = vb2_queue_init(q);
if (ret)
diff --git a/drivers/media/platform/ti/vpe/vpdma.c b/drivers/media/platform/ti/vpe/vpdma.c
index da90d7f03f82..bb8a8bd7980c 100644
--- a/drivers/media/platform/ti/vpe/vpdma.c
+++ b/drivers/media/platform/ti/vpe/vpdma.c
@@ -552,38 +552,6 @@ EXPORT_SYMBOL(vpdma_submit_descs);
static void dump_dtd(struct vpdma_dtd *dtd);
-void vpdma_update_dma_addr(struct vpdma_data *vpdma,
- struct vpdma_desc_list *list, dma_addr_t dma_addr,
- void *write_dtd, int drop, int idx)
-{
- struct vpdma_dtd *dtd = list->buf.addr;
- dma_addr_t write_desc_addr;
- int offset;
-
- dtd += idx;
- vpdma_unmap_desc_buf(vpdma, &list->buf);
-
- dtd->start_addr = dma_addr;
-
- /* Calculate write address from the offset of write_dtd from start
- * of the list->buf
- */
- offset = (void *)write_dtd - list->buf.addr;
- write_desc_addr = list->buf.dma_addr + offset;
-
- if (drop)
- dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
- 1, 1, 0);
- else
- dtd->desc_write_addr = dtd_desc_write_addr(write_desc_addr,
- 1, 0, 0);
-
- vpdma_map_desc_buf(vpdma, &list->buf);
-
- dump_dtd(dtd);
-}
-EXPORT_SYMBOL(vpdma_update_dma_addr);
-
void vpdma_set_max_size(struct vpdma_data *vpdma, int reg_addr,
u32 width, u32 height)
{
diff --git a/drivers/media/platform/ti/vpe/vpdma.h b/drivers/media/platform/ti/vpe/vpdma.h
index 393fcbb3cb40..e4d7941c6207 100644
--- a/drivers/media/platform/ti/vpe/vpdma.h
+++ b/drivers/media/platform/ti/vpe/vpdma.h
@@ -222,9 +222,6 @@ void vpdma_free_desc_list(struct vpdma_desc_list *list);
int vpdma_submit_descs(struct vpdma_data *vpdma, struct vpdma_desc_list *list,
int list_num);
bool vpdma_list_busy(struct vpdma_data *vpdma, int list_num);
-void vpdma_update_dma_addr(struct vpdma_data *vpdma,
- struct vpdma_desc_list *list, dma_addr_t dma_addr,
- void *write_dtd, int drop, int idx);
/* VPDMA hardware list funcs */
int vpdma_hwlist_alloc(struct vpdma_data *vpdma, void *priv);
diff --git a/drivers/media/platform/verisilicon/hantro.h b/drivers/media/platform/verisilicon/hantro.h
index edc217eed293..81328c63b796 100644
--- a/drivers/media/platform/verisilicon/hantro.h
+++ b/drivers/media/platform/verisilicon/hantro.h
@@ -323,6 +323,8 @@ struct hantro_postproc_regs {
struct hantro_reg output_fmt;
struct hantro_reg orig_width;
struct hantro_reg display_width;
+ struct hantro_reg input_width_ext;
+ struct hantro_reg input_height_ext;
};
struct hantro_vp9_decoded_buffer_info {
diff --git a/drivers/media/platform/verisilicon/hantro_g1_regs.h b/drivers/media/platform/verisilicon/hantro_g1_regs.h
index c623b3b0be18..7874d76c6898 100644
--- a/drivers/media/platform/verisilicon/hantro_g1_regs.h
+++ b/drivers/media/platform/verisilicon/hantro_g1_regs.h
@@ -350,7 +350,7 @@
#define G1_REG_PP_CONTROL_OUT_WIDTH(v) (((v) << 4) & GENMASK(14, 4))
#define G1_REG_PP_MASK1_ORIG_WIDTH G1_SWREG(88)
#define G1_REG_PP_ORIG_WIDTH(v) (((v) << 23) & GENMASK(31, 23))
-#define G1_REG_PP_DISPLAY_WIDTH G1_SWREG(92)
+#define G1_REG_PP_DISPLAY_WIDTH_IN_EXT G1_SWREG(92)
#define G1_REG_PP_FUSE G1_SWREG(99)
#endif /* HANTRO_G1_REGS_H_ */
diff --git a/drivers/media/platform/verisilicon/hantro_h264.c b/drivers/media/platform/verisilicon/hantro_h264.c
index 4e9a0ecf5c13..2414782f1eb6 100644
--- a/drivers/media/platform/verisilicon/hantro_h264.c
+++ b/drivers/media/platform/verisilicon/hantro_h264.c
@@ -325,12 +325,12 @@ static void update_dpb(struct hantro_ctx *ctx)
continue;
*cdpb = *ndpb;
- set_bit(j, used);
+ __set_bit(j, used);
break;
}
if (j == ARRAY_SIZE(ctx->h264_dec.dpb))
- set_bit(i, new);
+ __set_bit(i, new);
}
/* For entries that could not be matched, use remaining free slots. */
@@ -349,7 +349,7 @@ static void update_dpb(struct hantro_ctx *ctx)
cdpb = &ctx->h264_dec.dpb[j];
*cdpb = *ndpb;
- set_bit(j, used);
+ __set_bit(j, used);
}
}
diff --git a/drivers/media/platform/verisilicon/hantro_postproc.c b/drivers/media/platform/verisilicon/hantro_postproc.c
index 9f559a13d409..e94d1ba5ef10 100644
--- a/drivers/media/platform/verisilicon/hantro_postproc.c
+++ b/drivers/media/platform/verisilicon/hantro_postproc.c
@@ -49,7 +49,9 @@ static const struct hantro_postproc_regs hantro_g1_postproc_regs = {
.input_fmt = {G1_REG_PP_CONTROL, 29, 0x7},
.output_fmt = {G1_REG_PP_CONTROL, 26, 0x7},
.orig_width = {G1_REG_PP_MASK1_ORIG_WIDTH, 23, 0x1ff},
- .display_width = {G1_REG_PP_DISPLAY_WIDTH, 0, 0xfff},
+ .display_width = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 0, 0xfff},
+ .input_width_ext = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 26, 0x7},
+ .input_height_ext = {G1_REG_PP_DISPLAY_WIDTH_IN_EXT, 29, 0x7},
};
bool hantro_needs_postproc(const struct hantro_ctx *ctx,
@@ -103,6 +105,8 @@ static void hantro_postproc_g1_enable(struct hantro_ctx *ctx)
HANTRO_PP_REG_WRITE(vpu, output_height, ctx->dst_fmt.height);
HANTRO_PP_REG_WRITE(vpu, orig_width, MB_WIDTH(ctx->dst_fmt.width));
HANTRO_PP_REG_WRITE(vpu, display_width, ctx->dst_fmt.width);
+ HANTRO_PP_REG_WRITE(vpu, input_width_ext, MB_WIDTH(ctx->dst_fmt.width) >> 9);
+ HANTRO_PP_REG_WRITE(vpu, input_height_ext, MB_HEIGHT(ctx->dst_fmt.height >> 8));
}
static int down_scale_factor(struct hantro_ctx *ctx)
diff --git a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
index acd29fa41d2d..02673be9878e 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu_hw.c
@@ -17,7 +17,6 @@
#define RK3066_ACLK_MAX_FREQ (300 * 1000 * 1000)
#define RK3288_ACLK_MAX_FREQ (400 * 1000 * 1000)
-#define RK3588_ACLK_MAX_FREQ (300 * 1000 * 1000)
#define ROCKCHIP_VPU981_MIN_SIZE 64
@@ -454,13 +453,6 @@ static int rk3066_vpu_hw_init(struct hantro_dev *vpu)
return 0;
}
-static int rk3588_vpu981_hw_init(struct hantro_dev *vpu)
-{
- /* Bump ACLKs to max. possible freq. to improve performance. */
- clk_set_rate(vpu->clocks[0].clk, RK3588_ACLK_MAX_FREQ);
- return 0;
-}
-
static int rockchip_vpu_hw_init(struct hantro_dev *vpu)
{
/* Bump ACLK to max. possible freq. to improve performance. */
@@ -821,7 +813,6 @@ const struct hantro_variant rk3588_vpu981_variant = {
.codec_ops = rk3588_vpu981_codec_ops,
.irqs = rk3588_vpu981_irqs,
.num_irqs = ARRAY_SIZE(rk3588_vpu981_irqs),
- .init = rk3588_vpu981_hw_init,
.clk_names = rk3588_vpu981_vpu_clk_names,
.num_clocks = ARRAY_SIZE(rk3588_vpu981_vpu_clk_names)
};
diff --git a/drivers/media/platform/xilinx/xilinx-vipp.c b/drivers/media/platform/xilinx/xilinx-vipp.c
index 024b439feec9..30675f681410 100644
--- a/drivers/media/platform/xilinx/xilinx-vipp.c
+++ b/drivers/media/platform/xilinx/xilinx-vipp.c
@@ -450,7 +450,6 @@ static int xvip_graph_dma_init_one(struct xvip_composite_device *xdev,
static int xvip_graph_dma_init(struct xvip_composite_device *xdev)
{
struct device_node *ports;
- struct device_node *port;
int ret = 0;
ports = of_get_child_by_name(xdev->dev->of_node, "ports");
@@ -459,12 +458,10 @@ static int xvip_graph_dma_init(struct xvip_composite_device *xdev)
return -EINVAL;
}
- for_each_child_of_node(ports, port) {
+ for_each_child_of_node_scoped(ports, port) {
ret = xvip_graph_dma_init_one(xdev, port);
- if (ret) {
- of_node_put(port);
+ if (ret)
break;
- }
}
of_node_put(ports);
diff --git a/drivers/media/rc/ir-spi.c b/drivers/media/rc/ir-spi.c
index 8fc8e496e6aa..392441e0c116 100644
--- a/drivers/media/rc/ir-spi.c
+++ b/drivers/media/rc/ir-spi.c
@@ -21,13 +21,12 @@
#define IR_SPI_DRIVER_NAME "ir-spi"
#define IR_SPI_DEFAULT_FREQUENCY 38000
-#define IR_SPI_MAX_BUFSIZE 4096
+#define IR_SPI_BITS_PER_PULSE 16
struct ir_spi_data {
u32 freq;
bool negated;
- u16 tx_buf[IR_SPI_MAX_BUFSIZE];
u16 pulse;
u16 space;
@@ -43,17 +42,23 @@ static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int coun
unsigned int len = 0;
struct ir_spi_data *idata = dev->priv;
struct spi_transfer xfer;
+ u16 *tx_buf;
/* convert the pulse/space signal to raw binary signal */
for (i = 0; i < count; i++) {
- unsigned int periods;
- int j;
- u16 val;
+ buffer[i] = DIV_ROUND_CLOSEST_ULL((u64)buffer[i] * idata->freq,
+ 1000000);
+ len += buffer[i];
+ }
- periods = DIV_ROUND_CLOSEST(buffer[i] * idata->freq, 1000000);
+ tx_buf = kmalloc_array(len, sizeof(*tx_buf), GFP_KERNEL);
+ if (!tx_buf)
+ return -ENOMEM;
- if (len + periods >= IR_SPI_MAX_BUFSIZE)
- return -EINVAL;
+ len = 0;
+ for (i = 0; i < count; i++) {
+ int j;
+ u16 val;
/*
* The first value in buffer is a pulse, so that 0, 2, 4, ...
@@ -61,19 +66,19 @@ static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int coun
* contain a space duration.
*/
val = (i % 2) ? idata->space : idata->pulse;
- for (j = 0; j < periods; j++)
- idata->tx_buf[len++] = val;
+ for (j = 0; j < buffer[i]; j++)
+ tx_buf[len++] = val;
}
memset(&xfer, 0, sizeof(xfer));
- xfer.speed_hz = idata->freq * 16;
- xfer.len = len * sizeof(*idata->tx_buf);
- xfer.tx_buf = idata->tx_buf;
+ xfer.speed_hz = idata->freq * IR_SPI_BITS_PER_PULSE;
+ xfer.len = len * sizeof(*tx_buf);
+ xfer.tx_buf = tx_buf;
ret = regulator_enable(idata->regulator);
if (ret)
- return ret;
+ goto err_free_tx_buf;
ret = spi_sync_transfer(idata->spi, &xfer, 1);
if (ret)
@@ -81,6 +86,10 @@ static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int coun
regulator_disable(idata->regulator);
+err_free_tx_buf:
+
+ kfree(tx_buf);
+
return ret ? ret : count;
}
@@ -91,6 +100,9 @@ static int ir_spi_set_tx_carrier(struct rc_dev *dev, u32 carrier)
if (!carrier)
return -EINVAL;
+ if (carrier > idata->spi->max_speed_hz / IR_SPI_BITS_PER_PULSE)
+ return -EINVAL;
+
idata->freq = carrier;
return 0;
diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c
index e340df0b6261..f94c15ff84f7 100644
--- a/drivers/media/test-drivers/vivid/vivid-ctrls.c
+++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c
@@ -244,7 +244,8 @@ static const struct v4l2_ctrl_config vivid_ctrl_u8_pixel_array = {
.min = 0x00,
.max = 0xff,
.step = 1,
- .dims = { 640 / PIXEL_ARRAY_DIV, 360 / PIXEL_ARRAY_DIV },
+ .dims = { DIV_ROUND_UP(360, PIXEL_ARRAY_DIV),
+ DIV_ROUND_UP(640, PIXEL_ARRAY_DIV) },
};
static const struct v4l2_ctrl_config vivid_ctrl_s32_array = {
diff --git a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c
index 70a4024d461e..e0f4151bda18 100644
--- a/drivers/media/test-drivers/vivid/vivid-vbi-gen.c
+++ b/drivers/media/test-drivers/vivid/vivid-vbi-gen.c
@@ -5,6 +5,7 @@
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*/
+#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
@@ -165,12 +166,7 @@ static const u8 vivid_cc_sequence2[30] = {
static u8 calc_parity(u8 val)
{
- unsigned i;
- unsigned tot = 0;
-
- for (i = 0; i < 7; i++)
- tot += (val & (1 << i)) ? 1 : 0;
- return val | ((tot & 1) ? 0 : 0x80);
+ return val | (parity8(val) ? 0 : 0x80);
}
static void vivid_vbi_gen_set_time_of_day(u8 *packet)
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index 84e9155b5815..2e4c1ed37cd2 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -454,8 +454,8 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
if (keep_controls)
return;
- dims[0] = roundup(dev->src_rect.width, PIXEL_ARRAY_DIV);
- dims[1] = roundup(dev->src_rect.height, PIXEL_ARRAY_DIV);
+ dims[0] = DIV_ROUND_UP(dev->src_rect.height, PIXEL_ARRAY_DIV);
+ dims[1] = DIV_ROUND_UP(dev->src_rect.width, PIXEL_ARRAY_DIV);
v4l2_ctrl_modify_dimensions(dev->pixel_array, dims);
}
diff --git a/drivers/media/usb/gspca/vicam.c b/drivers/media/usb/gspca/vicam.c
index d98343fd33fe..91e177aa8136 100644
--- a/drivers/media/usb/gspca/vicam.c
+++ b/drivers/media/usb/gspca/vicam.c
@@ -227,6 +227,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
const struct ihex_binrec *rec;
const struct firmware *fw;
u8 *firmware_buf;
+ int len;
ret = request_ihex_firmware(&fw, VICAM_FIRMWARE,
&gspca_dev->dev->dev);
@@ -241,9 +242,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
goto exit;
}
for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
- memcpy(firmware_buf, rec->data, be16_to_cpu(rec->len));
+ len = be16_to_cpu(rec->len);
+ if (len > PAGE_SIZE) {
+ ret = -EINVAL;
+ break;
+ }
+ memcpy(firmware_buf, rec->data, len);
ret = vicam_control_msg(gspca_dev, 0xff, 0, 0, firmware_buf,
- be16_to_cpu(rec->len));
+ len);
if (ret < 0)
break;
}
diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c
index 070559b01b01..9eacc85e3f11 100644
--- a/drivers/media/usb/hdpvr/hdpvr-i2c.c
+++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c
@@ -124,32 +124,12 @@ static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs,
else
retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf,
msgs[0].len);
- } else if (num == 2) {
- if (msgs[0].addr != msgs[1].addr) {
- v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer with conflicting target addresses\n");
- retval = -EINVAL;
- goto out;
- }
-
- if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) {
- v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with r0=%d, r1=%d\n",
- msgs[0].flags & I2C_M_RD,
- msgs[1].flags & I2C_M_RD);
- retval = -EINVAL;
- goto out;
- }
-
- /*
- * Write followed by atomic read is the only complex xfer that
- * we actually support here.
- */
+ } else {
+ /* do write-then-read */
retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len,
msgs[1].buf, msgs[1].len);
- } else {
- v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num);
}
-out:
mutex_unlock(&dev->i2c_mutex);
return retval ? retval : num;
@@ -165,10 +145,16 @@ static const struct i2c_algorithm hdpvr_algo = {
.functionality = hdpvr_functionality,
};
+/* prevent invalid 0-length usb_control_msg and support only write-then-read */
+static const struct i2c_adapter_quirks hdpvr_quirks = {
+ .flags = I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_COMB_WRITE_THEN_READ,
+};
+
static const struct i2c_adapter hdpvr_i2c_adapter_template = {
.name = "Hauppauge HD PVR I2C",
.owner = THIS_MODULE,
.algo = &hdpvr_algo,
+ .quirks = &hdpvr_quirks,
};
static int hdpvr_activate_ir(struct hdpvr_device *dev)
diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c
index be22a9697197..de0328100a60 100644
--- a/drivers/media/usb/usbtv/usbtv-video.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -73,6 +73,10 @@ static int usbtv_configure_for_norm(struct usbtv *usbtv, v4l2_std_id norm)
}
if (params) {
+ if (vb2_is_busy(&usbtv->vb2q) &&
+ (usbtv->width != params->cap_width ||
+ usbtv->height != params->cap_height))
+ return -EBUSY;
usbtv->width = params->cap_width;
usbtv->height = params->cap_height;
usbtv->n_chunks = usbtv->width * usbtv->height
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index 44b6513c5264..efe609d70877 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1483,14 +1483,28 @@ static u32 uvc_get_ctrl_bitmap(struct uvc_control *ctrl,
return ~0;
}
+/*
+ * Maximum retry count to avoid spurious errors with controls. Increasing this
+ * value does no seem to produce better results in the tested hardware.
+ */
+#define MAX_QUERY_RETRIES 2
+
static int __uvc_queryctrl_boundaries(struct uvc_video_chain *chain,
struct uvc_control *ctrl,
struct uvc_control_mapping *mapping,
struct v4l2_query_ext_ctrl *v4l2_ctrl)
{
if (!ctrl->cached) {
- int ret = uvc_ctrl_populate_cache(chain, ctrl);
- if (ret < 0)
+ unsigned int retries;
+ int ret;
+
+ for (retries = 0; retries < MAX_QUERY_RETRIES; retries++) {
+ ret = uvc_ctrl_populate_cache(chain, ctrl);
+ if (ret != -EIO)
+ break;
+ }
+
+ if (ret)
return ret;
}
@@ -1567,6 +1581,7 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
{
struct uvc_control_mapping *master_map = NULL;
struct uvc_control *master_ctrl = NULL;
+ int ret;
memset(v4l2_ctrl, 0, sizeof(*v4l2_ctrl));
v4l2_ctrl->id = mapping->id;
@@ -1587,18 +1602,31 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
__uvc_find_control(ctrl->entity, mapping->master_id,
&master_map, &master_ctrl, 0, 0);
if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) {
+ unsigned int retries;
s32 val;
int ret;
if (WARN_ON(uvc_ctrl_mapping_is_compound(master_map)))
return -EIO;
- ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
- if (ret < 0)
- return ret;
+ for (retries = 0; retries < MAX_QUERY_RETRIES; retries++) {
+ ret = __uvc_ctrl_get(chain, master_ctrl, master_map,
+ &val);
+ if (!ret)
+ break;
+ if (ret < 0 && ret != -EIO)
+ return ret;
+ }
- if (val != mapping->master_manual)
- v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ if (ret == -EIO) {
+ dev_warn_ratelimited(&chain->dev->udev->dev,
+ "UVC non compliance: Error %d querying master control %x (%s)\n",
+ ret, master_map->id,
+ uvc_map_get_name(master_map));
+ } else {
+ if (val != mapping->master_manual)
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ }
}
v4l2_ctrl->elem_size = uvc_mapping_v4l2_size(mapping);
@@ -1613,7 +1641,18 @@ static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
return 0;
}
- return __uvc_queryctrl_boundaries(chain, ctrl, mapping, v4l2_ctrl);
+ ret = __uvc_queryctrl_boundaries(chain, ctrl, mapping, v4l2_ctrl);
+ if (ret && !mapping->disabled) {
+ dev_warn(&chain->dev->udev->dev,
+ "UVC non compliance: permanently disabling control %x (%s), due to error %d\n",
+ mapping->id, uvc_map_get_name(mapping), ret);
+ mapping->disabled = true;
+ }
+
+ if (mapping->disabled)
+ v4l2_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+
+ return 0;
}
int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain,
@@ -1812,48 +1851,48 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
}
-static int uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
- struct uvc_fh *new_handle)
+static int uvc_ctrl_set_handle(struct uvc_control *ctrl, struct uvc_fh *handle)
{
- lockdep_assert_held(&handle->chain->ctrl_mutex);
-
- if (new_handle) {
- int ret;
+ int ret;
- if (ctrl->handle)
- dev_warn_ratelimited(&handle->stream->dev->udev->dev,
- "UVC non compliance: Setting an async control with a pending operation.");
+ lockdep_assert_held(&handle->chain->ctrl_mutex);
- if (new_handle == ctrl->handle)
- return 0;
+ if (ctrl->handle) {
+ dev_warn_ratelimited(&handle->stream->dev->udev->dev,
+ "UVC non compliance: Setting an async control with a pending operation.");
- if (ctrl->handle) {
- WARN_ON(!ctrl->handle->pending_async_ctrls);
- if (ctrl->handle->pending_async_ctrls)
- ctrl->handle->pending_async_ctrls--;
- ctrl->handle = new_handle;
- handle->pending_async_ctrls++;
+ if (ctrl->handle == handle)
return 0;
- }
-
- ret = uvc_pm_get(handle->chain->dev);
- if (ret)
- return ret;
- ctrl->handle = new_handle;
- handle->pending_async_ctrls++;
+ WARN_ON(!ctrl->handle->pending_async_ctrls);
+ if (ctrl->handle->pending_async_ctrls)
+ ctrl->handle->pending_async_ctrls--;
+ ctrl->handle = handle;
+ ctrl->handle->pending_async_ctrls++;
return 0;
}
- /* Cannot clear the handle for a control not owned by us.*/
- if (WARN_ON(ctrl->handle != handle))
+ ret = uvc_pm_get(handle->chain->dev);
+ if (ret)
+ return ret;
+
+ ctrl->handle = handle;
+ ctrl->handle->pending_async_ctrls++;
+ return 0;
+}
+
+static int uvc_ctrl_clear_handle(struct uvc_control *ctrl)
+{
+ lockdep_assert_held(&ctrl->handle->chain->ctrl_mutex);
+
+ if (WARN_ON(!ctrl->handle->pending_async_ctrls)) {
+ ctrl->handle = NULL;
return -EINVAL;
+ }
+ ctrl->handle->pending_async_ctrls--;
+ uvc_pm_put(ctrl->handle->chain->dev);
ctrl->handle = NULL;
- if (WARN_ON(!handle->pending_async_ctrls))
- return -EINVAL;
- handle->pending_async_ctrls--;
- uvc_pm_put(handle->chain->dev);
return 0;
}
@@ -1871,7 +1910,7 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
handle = ctrl->handle;
if (handle)
- uvc_ctrl_set_handle(handle, ctrl, NULL);
+ uvc_ctrl_clear_handle(ctrl);
list_for_each_entry(mapping, &ctrl->info.mappings, list) {
s32 value;
@@ -2033,18 +2072,24 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
goto done;
}
- list_add_tail(&sev->node, &mapping->ev_subs);
if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) {
struct v4l2_event ev;
u32 changes = V4L2_EVENT_CTRL_CH_FLAGS;
s32 val = 0;
+ ret = uvc_pm_get(handle->chain->dev);
+ if (ret)
+ goto done;
+
if (uvc_ctrl_mapping_is_compound(mapping) ||
__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0)
changes |= V4L2_EVENT_CTRL_CH_VALUE;
uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val,
changes);
+
+ uvc_pm_put(handle->chain->dev);
+
/*
* Mark the queue as active, allowing this initial event to be
* accepted.
@@ -2053,6 +2098,8 @@ static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems)
v4l2_event_queue_fh(sev->fh, &ev);
}
+ list_add_tail(&sev->node, &mapping->ev_subs);
+
done:
mutex_unlock(&handle->chain->ctrl_mutex);
return ret;
@@ -2161,7 +2208,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
if (!rollback && handle && !ret &&
ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
- ret = uvc_ctrl_set_handle(handle, ctrl, handle);
+ ret = uvc_ctrl_set_handle(ctrl, handle);
if (ret < 0 && !rollback) {
if (err_ctrl)
@@ -3271,7 +3318,7 @@ void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
for (unsigned int i = 0; i < entity->ncontrols; ++i) {
if (entity->controls[i].handle != handle)
continue;
- uvc_ctrl_set_handle(handle, &entity->controls[i], NULL);
+ uvc_ctrl_clear_handle(&entity->controls[i]);
}
}
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index da24a655ab68..775bede0d93d 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -344,6 +344,9 @@ static int uvc_parse_format(struct uvc_device *dev,
u8 ftype;
int ret;
+ if (buflen < 4)
+ return -EINVAL;
+
format->type = buffer[2];
format->index = buffer[3];
format->frames = frames;
@@ -1866,7 +1869,7 @@ static int uvc_scan_device(struct uvc_device *dev)
if (list_empty(&dev->chains)) {
dev_info(&dev->udev->dev, "No valid video chain found.\n");
- return -1;
+ return -ENODEV;
}
/* Add GPIO entity to the first chain. */
@@ -1958,31 +1961,7 @@ static void uvc_unregister_video(struct uvc_device *dev)
if (!video_is_registered(&stream->vdev))
continue;
- /*
- * For stream->vdev we follow the same logic as:
- * vb2_video_unregister_device().
- */
-
- /* 1. Take a reference to vdev */
- get_device(&stream->vdev.dev);
-
- /* 2. Ensure that no new ioctls can be called. */
- video_unregister_device(&stream->vdev);
-
- /* 3. Wait for old ioctls to finish. */
- mutex_lock(&stream->mutex);
-
- /* 4. Stop streaming. */
- uvc_queue_release(&stream->queue);
-
- mutex_unlock(&stream->mutex);
-
- put_device(&stream->vdev.dev);
-
- /*
- * For stream->meta.vdev we can directly call:
- * vb2_video_unregister_device().
- */
+ vb2_video_unregister_device(&stream->vdev);
vb2_video_unregister_device(&stream->meta.vdev);
/*
@@ -2030,6 +2009,8 @@ int uvc_register_video_device(struct uvc_device *dev,
vdev->ioctl_ops = ioctl_ops;
vdev->release = uvc_release;
vdev->prio = &stream->chain->prio;
+ vdev->queue = &queue->queue;
+ vdev->lock = &queue->mutex;
if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
vdev->vfl_dir = VFL_DIR_TX;
else
@@ -2239,7 +2220,6 @@ static int uvc_probe(struct usb_interface *intf,
/* Parse the Video Class control descriptor. */
ret = uvc_parse_control(dev);
if (ret < 0) {
- ret = -ENODEV;
uvc_dbg(dev, PROBE, "Unable to parse UVC descriptors\n");
goto error;
}
@@ -2275,22 +2255,19 @@ static int uvc_probe(struct usb_interface *intf,
goto error;
/* Scan the device for video chains. */
- if (uvc_scan_device(dev) < 0) {
- ret = -ENODEV;
+ ret = uvc_scan_device(dev);
+ if (ret < 0)
goto error;
- }
/* Initialize controls. */
- if (uvc_ctrl_init_device(dev) < 0) {
- ret = -ENODEV;
+ ret = uvc_ctrl_init_device(dev);
+ if (ret < 0)
goto error;
- }
/* Register video device nodes. */
- if (uvc_register_chains(dev) < 0) {
- ret = -ENODEV;
+ ret = uvc_register_chains(dev);
+ if (ret < 0)
goto error;
- }
#ifdef CONFIG_MEDIA_CONTROLLER
/* Register the media device node */
@@ -2316,6 +2293,13 @@ static int uvc_probe(struct usb_interface *intf,
goto error;
}
+ ret = uvc_meta_init(dev);
+ if (ret < 0) {
+ dev_err(&dev->udev->dev,
+ "Error initializing the metadata formats (%d)\n", ret);
+ goto error;
+ }
+
if (dev->quirks & UVC_QUIRK_NO_RESET_RESUME)
udev->quirks &= ~USB_QUIRK_RESET_RESUME;
@@ -2398,9 +2382,12 @@ static int __uvc_resume(struct usb_interface *intf, int reset)
list_for_each_entry(stream, &dev->streams, list) {
if (stream->intf == intf) {
ret = uvc_video_resume(stream, reset);
- if (ret < 0)
- uvc_queue_streamoff(&stream->queue,
- stream->queue.queue.type);
+ if (ret < 0) {
+ mutex_lock(&stream->queue.mutex);
+ vb2_streamoff(&stream->queue.queue,
+ stream->queue.queue.type);
+ mutex_unlock(&stream->queue.mutex);
+ }
return ret;
}
}
@@ -2514,6 +2501,15 @@ static const struct uvc_device_info uvc_quirk_force_y8 = {
* Sort these by vendor/product ID.
*/
static const struct usb_device_id uvc_ids[] = {
+ /* HP Webcam HD 2300 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x03f0,
+ .idProduct = 0xe207,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&uvc_quirk_stream_no_fid },
/* Quanta ACER HD User Facing */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/usb/uvc/uvc_metadata.c b/drivers/media/usb/uvc/uvc_metadata.c
index 82de7781f5b6..229e08ff323e 100644
--- a/drivers/media/usb/uvc/uvc_metadata.c
+++ b/drivers/media/usb/uvc/uvc_metadata.c
@@ -10,6 +10,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
+#include <linux/usb/uvc.h>
#include <linux/videodev2.h>
#include <media/v4l2-ioctl.h>
@@ -63,15 +64,20 @@ static int uvc_meta_v4l2_try_format(struct file *file, void *fh,
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct uvc_device *dev = stream->dev;
struct v4l2_meta_format *fmt = &format->fmt.meta;
- u32 fmeta = fmt->dataformat;
+ u32 fmeta = V4L2_META_FMT_UVC;
if (format->type != vfh->vdev->queue->type)
return -EINVAL;
+ for (unsigned int i = 0; i < dev->nmeta_formats; i++)
+ if (dev->meta_formats[i] == fmt->dataformat) {
+ fmeta = fmt->dataformat;
+ break;
+ }
+
memset(fmt, 0, sizeof(*fmt));
- fmt->dataformat = fmeta == dev->info->meta_format
- ? fmeta : V4L2_META_FMT_UVC;
+ fmt->dataformat = fmeta;
fmt->buffersize = UVC_METADATA_BUF_SIZE;
return 0;
@@ -96,7 +102,7 @@ static int uvc_meta_v4l2_set_format(struct file *file, void *fh,
*/
mutex_lock(&stream->mutex);
- if (uvc_queue_allocated(&stream->queue))
+ if (vb2_is_busy(&stream->meta.queue.queue))
ret = -EBUSY;
else
stream->meta.format = fmt->dataformat;
@@ -112,17 +118,19 @@ static int uvc_meta_v4l2_enum_formats(struct file *file, void *fh,
struct v4l2_fh *vfh = file->private_data;
struct uvc_streaming *stream = video_get_drvdata(vfh->vdev);
struct uvc_device *dev = stream->dev;
- u32 index = fdesc->index;
+ u32 i = fdesc->index;
+
+ if (fdesc->type != vfh->vdev->queue->type)
+ return -EINVAL;
- if (fdesc->type != vfh->vdev->queue->type ||
- index > 1U || (index && !dev->info->meta_format))
+ if (i >= dev->nmeta_formats)
return -EINVAL;
memset(fdesc, 0, sizeof(*fdesc));
fdesc->type = vfh->vdev->queue->type;
- fdesc->index = index;
- fdesc->pixelformat = index ? dev->info->meta_format : V4L2_META_FMT_UVC;
+ fdesc->index = i;
+ fdesc->pixelformat = dev->meta_formats[i];
return 0;
}
@@ -156,6 +164,71 @@ static const struct v4l2_file_operations uvc_meta_fops = {
.mmap = vb2_fop_mmap,
};
+static struct uvc_entity *uvc_meta_find_msxu(struct uvc_device *dev)
+{
+ static const u8 uvc_msxu_guid[16] = UVC_GUID_MSXU_1_5;
+ struct uvc_entity *entity;
+
+ list_for_each_entry(entity, &dev->entities, list) {
+ if (!memcmp(entity->guid, uvc_msxu_guid, sizeof(entity->guid)))
+ return entity;
+ }
+
+ return NULL;
+}
+
+#define MSXU_CONTROL_METADATA 0x9
+static int uvc_meta_detect_msxu(struct uvc_device *dev)
+{
+ u32 *data __free(kfree) = NULL;
+ struct uvc_entity *entity;
+ int ret;
+
+ entity = uvc_meta_find_msxu(dev);
+ if (!entity)
+ return 0;
+
+ /*
+ * USB requires buffers aligned in a special way, simplest way is to
+ * make sure that query_ctrl will work is to kmalloc() them.
+ */
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* Check if the metadata is already enabled. */
+ ret = uvc_query_ctrl(dev, UVC_GET_CUR, entity->id, dev->intfnum,
+ MSXU_CONTROL_METADATA, data, sizeof(*data));
+ if (ret)
+ return 0;
+
+ if (*data) {
+ dev->quirks |= UVC_QUIRK_MSXU_META;
+ return 0;
+ }
+
+ /*
+ * We have seen devices that require 1 to enable the metadata, others
+ * requiring a value != 1 and others requiring a value >1. Luckily for
+ * us, the value from GET_MAX seems to work all the time.
+ */
+ ret = uvc_query_ctrl(dev, UVC_GET_MAX, entity->id, dev->intfnum,
+ MSXU_CONTROL_METADATA, data, sizeof(*data));
+ if (ret || !*data)
+ return 0;
+
+ /*
+ * If we can set MSXU_CONTROL_METADATA, the device will report
+ * metadata.
+ */
+ ret = uvc_query_ctrl(dev, UVC_SET_CUR, entity->id, dev->intfnum,
+ MSXU_CONTROL_METADATA, data, sizeof(*data));
+ if (!ret)
+ dev->quirks |= UVC_QUIRK_MSXU_META;
+
+ return 0;
+}
+
int uvc_meta_register(struct uvc_streaming *stream)
{
struct uvc_device *dev = stream->dev;
@@ -164,13 +237,32 @@ int uvc_meta_register(struct uvc_streaming *stream)
stream->meta.format = V4L2_META_FMT_UVC;
- /*
- * The video interface queue uses manual locking and thus does not set
- * the queue pointer. Set it manually here.
- */
- vdev->queue = &queue->queue;
-
return uvc_register_video_device(dev, stream, vdev, queue,
V4L2_BUF_TYPE_META_CAPTURE,
&uvc_meta_fops, &uvc_meta_ioctl_ops);
}
+
+int uvc_meta_init(struct uvc_device *dev)
+{
+ unsigned int i = 0;
+ int ret;
+
+ ret = uvc_meta_detect_msxu(dev);
+ if (ret)
+ return ret;
+
+ dev->meta_formats[i++] = V4L2_META_FMT_UVC;
+
+ if (dev->info->meta_format &&
+ !WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC))
+ dev->meta_formats[i++] = dev->info->meta_format;
+
+ if (dev->quirks & UVC_QUIRK_MSXU_META &&
+ !WARN_ON(dev->info->meta_format == V4L2_META_FMT_UVC_MSXU_1_5))
+ dev->meta_formats[i++] = V4L2_META_FMT_UVC_MSXU_1_5;
+
+ /* IMPORTANT: for new meta-formats update UVC_MAX_META_DATA_FORMATS. */
+ dev->nmeta_formats = i;
+
+ return 0;
+}
diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c
index 2ee142621042..790184c9843d 100644
--- a/drivers/media/usb/uvc/uvc_queue.c
+++ b/drivers/media/usb/uvc/uvc_queue.c
@@ -42,13 +42,15 @@ static inline struct uvc_buffer *uvc_vbuf_to_buffer(struct vb2_v4l2_buffer *buf)
*
* This function must be called with the queue spinlock held.
*/
-static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
- enum uvc_buffer_state state)
+static void __uvc_queue_return_buffers(struct uvc_video_queue *queue,
+ enum uvc_buffer_state state)
{
enum vb2_buffer_state vb2_state = state == UVC_BUF_STATE_ERROR
? VB2_BUF_STATE_ERROR
: VB2_BUF_STATE_QUEUED;
+ lockdep_assert_held(&queue->irqlock);
+
while (!list_empty(&queue->irqqueue)) {
struct uvc_buffer *buf = list_first_entry(&queue->irqqueue,
struct uvc_buffer,
@@ -59,6 +61,14 @@ static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
}
}
+static void uvc_queue_return_buffers(struct uvc_video_queue *queue,
+ enum uvc_buffer_state state)
+{
+ spin_lock_irq(&queue->irqlock);
+ __uvc_queue_return_buffers(queue, state);
+ spin_unlock_irq(&queue->irqlock);
+}
+
/* -----------------------------------------------------------------------------
* videobuf2 queue operations
*/
@@ -157,7 +167,7 @@ static void uvc_buffer_finish(struct vb2_buffer *vb)
uvc_video_clock_update(stream, vbuf, buf);
}
-static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
+static int uvc_start_streaming_video(struct vb2_queue *vq, unsigned int count)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
struct uvc_streaming *stream = uvc_queue_to_stream(queue);
@@ -165,31 +175,44 @@ static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count)
lockdep_assert_irqs_enabled();
+ ret = uvc_pm_get(stream->dev);
+ if (ret)
+ return ret;
+
queue->buf_used = 0;
ret = uvc_video_start_streaming(stream);
if (ret == 0)
return 0;
- spin_lock_irq(&queue->irqlock);
+ uvc_pm_put(stream->dev);
+
uvc_queue_return_buffers(queue, UVC_BUF_STATE_QUEUED);
- spin_unlock_irq(&queue->irqlock);
return ret;
}
-static void uvc_stop_streaming(struct vb2_queue *vq)
+static void uvc_stop_streaming_video(struct vb2_queue *vq)
{
struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+ struct uvc_streaming *stream = uvc_queue_to_stream(queue);
lockdep_assert_irqs_enabled();
- if (vq->type != V4L2_BUF_TYPE_META_CAPTURE)
- uvc_video_stop_streaming(uvc_queue_to_stream(queue));
+ uvc_video_stop_streaming(uvc_queue_to_stream(queue));
+
+ uvc_pm_put(stream->dev);
+
+ uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
+}
+
+static void uvc_stop_streaming_meta(struct vb2_queue *vq)
+{
+ struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
+
+ lockdep_assert_irqs_enabled();
- spin_lock_irq(&queue->irqlock);
uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
- spin_unlock_irq(&queue->irqlock);
}
static const struct vb2_ops uvc_queue_qops = {
@@ -197,15 +220,20 @@ static const struct vb2_ops uvc_queue_qops = {
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
.buf_finish = uvc_buffer_finish,
- .start_streaming = uvc_start_streaming,
- .stop_streaming = uvc_stop_streaming,
+ .start_streaming = uvc_start_streaming_video,
+ .stop_streaming = uvc_stop_streaming_video,
};
static const struct vb2_ops uvc_meta_queue_qops = {
.queue_setup = uvc_queue_setup,
.buf_prepare = uvc_buffer_prepare,
.buf_queue = uvc_buffer_queue,
- .stop_streaming = uvc_stop_streaming,
+ /*
+ * .start_streaming is not provided here. Metadata relies on video
+ * streaming being active. If video isn't streaming, then no metadata
+ * will arrive either.
+ */
+ .stop_streaming = uvc_stop_streaming_meta,
};
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
@@ -242,154 +270,11 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type)
return 0;
}
-void uvc_queue_release(struct uvc_video_queue *queue)
-{
- mutex_lock(&queue->mutex);
- vb2_queue_release(&queue->queue);
- mutex_unlock(&queue->mutex);
-}
-
-/* -----------------------------------------------------------------------------
- * V4L2 queue operations
- */
-
-int uvc_request_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_reqbufs(&queue->queue, rb);
- mutex_unlock(&queue->mutex);
-
- return ret ? ret : rb->count;
-}
-
-int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_querybuf(&queue->queue, buf);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_create_buffers(struct uvc_video_queue *queue,
- struct v4l2_create_buffers *cb)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_create_bufs(&queue->queue, cb);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct media_device *mdev, struct v4l2_buffer *buf)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_qbuf(&queue->queue, mdev, buf);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_export_buffer(struct uvc_video_queue *queue,
- struct v4l2_exportbuffer *exp)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_expbuf(&queue->queue, exp);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
- int nonblocking)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_streamon(&queue->queue, type);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type)
-{
- int ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_streamoff(&queue->queue, type);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
-int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
-{
- return vb2_mmap(&queue->queue, vma);
-}
-
-#ifndef CONFIG_MMU
-unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff)
-{
- return vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
-}
-#endif
-
-__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait)
-{
- __poll_t ret;
-
- mutex_lock(&queue->mutex);
- ret = vb2_poll(&queue->queue, file, wait);
- mutex_unlock(&queue->mutex);
-
- return ret;
-}
-
/* -----------------------------------------------------------------------------
*
*/
/*
- * Check if buffers have been allocated.
- */
-int uvc_queue_allocated(struct uvc_video_queue *queue)
-{
- int allocated;
-
- mutex_lock(&queue->mutex);
- allocated = vb2_is_busy(&queue->queue);
- mutex_unlock(&queue->mutex);
-
- return allocated;
-}
-
-/*
* Cancel the video buffers queue.
*
* Cancelling the queue marks all buffers on the irq queue as erroneous,
@@ -406,7 +291,7 @@ void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
unsigned long flags;
spin_lock_irqsave(&queue->irqlock, flags);
- uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
+ __uvc_queue_return_buffers(queue, UVC_BUF_STATE_ERROR);
/*
* This must be protected by the irqlock spinlock to avoid race
* conditions between uvc_buffer_queue and the disconnection event that
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 668a4e9d772c..160f9cf6e6db 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -47,8 +47,6 @@ void uvc_pm_put(struct uvc_device *dev)
usb_autopm_put_interface(dev->intf);
}
-static int uvc_acquire_privileges(struct uvc_fh *handle);
-
static int uvc_control_add_xu_mapping(struct uvc_video_chain *chain,
struct uvc_control_mapping *map,
const struct uvc_xu_control_mapping *xmap)
@@ -436,10 +434,6 @@ static int uvc_ioctl_s_fmt(struct file *file, void *fh,
const struct uvc_frame *frame;
int ret;
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
if (fmt->type != stream->type)
return -EINVAL;
@@ -448,8 +442,7 @@ static int uvc_ioctl_s_fmt(struct file *file, void *fh,
return ret;
mutex_lock(&stream->mutex);
-
- if (uvc_queue_allocated(&stream->queue)) {
+ if (vb2_is_busy(&stream->queue.queue)) {
ret = -EBUSY;
goto done;
}
@@ -513,10 +506,6 @@ static int uvc_ioctl_s_parm(struct file *file, void *fh,
unsigned int i;
int ret;
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
if (parm->type != stream->type)
return -EINVAL;
@@ -594,63 +583,6 @@ static int uvc_ioctl_s_parm(struct file *file, void *fh,
}
/* ------------------------------------------------------------------------
- * Privilege management
- */
-
-/*
- * Privilege management is the multiple-open implementation basis. The current
- * implementation is completely transparent for the end-user and doesn't
- * require explicit use of the VIDIOC_G_PRIORITY and VIDIOC_S_PRIORITY ioctls.
- * Those ioctls enable finer control on the device (by making possible for a
- * user to request exclusive access to a device), but are not mature yet.
- * Switching to the V4L2 priority mechanism might be considered in the future
- * if this situation changes.
- *
- * Each open instance of a UVC device can either be in a privileged or
- * unprivileged state. Only a single instance can be in a privileged state at
- * a given time. Trying to perform an operation that requires privileges will
- * automatically acquire the required privileges if possible, or return -EBUSY
- * otherwise. Privileges are dismissed when closing the instance or when
- * freeing the video buffers using VIDIOC_REQBUFS.
- *
- * Operations that require privileges are:
- *
- * - VIDIOC_S_INPUT
- * - VIDIOC_S_PARM
- * - VIDIOC_S_FMT
- * - VIDIOC_CREATE_BUFS
- * - VIDIOC_REQBUFS
- */
-static int uvc_acquire_privileges(struct uvc_fh *handle)
-{
- /* Always succeed if the handle is already privileged. */
- if (handle->state == UVC_HANDLE_ACTIVE)
- return 0;
-
- /* Check if the device already has a privileged handle. */
- if (atomic_inc_return(&handle->stream->active) != 1) {
- atomic_dec(&handle->stream->active);
- return -EBUSY;
- }
-
- handle->state = UVC_HANDLE_ACTIVE;
- return 0;
-}
-
-static void uvc_dismiss_privileges(struct uvc_fh *handle)
-{
- if (handle->state == UVC_HANDLE_ACTIVE)
- atomic_dec(&handle->stream->active);
-
- handle->state = UVC_HANDLE_PASSIVE;
-}
-
-static int uvc_has_privileges(struct uvc_fh *handle)
-{
- return handle->state == UVC_HANDLE_ACTIVE;
-}
-
-/* ------------------------------------------------------------------------
* V4L2 file operations
*/
@@ -671,7 +603,6 @@ static int uvc_v4l2_open(struct file *file)
v4l2_fh_add(&handle->vfh);
handle->chain = stream->chain;
handle->stream = stream;
- handle->state = UVC_HANDLE_PASSIVE;
file->private_data = handle;
return 0;
@@ -686,19 +617,8 @@ static int uvc_v4l2_release(struct file *file)
uvc_ctrl_cleanup_fh(handle);
- /* Only free resources if this is a privileged handle. */
- if (uvc_has_privileges(handle))
- uvc_queue_release(&stream->queue);
-
- if (handle->is_streaming)
- uvc_pm_put(stream->dev);
-
/* Release the file handle. */
- uvc_dismiss_privileges(handle);
- v4l2_fh_del(&handle->vfh);
- v4l2_fh_exit(&handle->vfh);
- kfree(handle);
- file->private_data = NULL;
+ vb2_fop_release(file);
return 0;
}
@@ -753,140 +673,6 @@ static int uvc_ioctl_try_fmt(struct file *file, void *fh,
return uvc_v4l2_try_format(stream, fmt, &probe, NULL, NULL);
}
-static int uvc_ioctl_reqbufs(struct file *file, void *fh,
- struct v4l2_requestbuffers *rb)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- mutex_lock(&stream->mutex);
- ret = uvc_request_buffers(&stream->queue, rb);
- mutex_unlock(&stream->mutex);
- if (ret < 0)
- return ret;
-
- if (ret == 0)
- uvc_dismiss_privileges(handle);
-
- return 0;
-}
-
-static int uvc_ioctl_querybuf(struct file *file, void *fh,
- struct v4l2_buffer *buf)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_query_buffer(&stream->queue, buf);
-}
-
-static int uvc_ioctl_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_queue_buffer(&stream->queue,
- stream->vdev.v4l2_dev->mdev, buf);
-}
-
-static int uvc_ioctl_expbuf(struct file *file, void *fh,
- struct v4l2_exportbuffer *exp)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_export_buffer(&stream->queue, exp);
-}
-
-static int uvc_ioctl_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- return uvc_dequeue_buffer(&stream->queue, buf,
- file->f_flags & O_NONBLOCK);
-}
-
-static int uvc_ioctl_create_bufs(struct file *file, void *fh,
- struct v4l2_create_buffers *cb)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
-
- return uvc_create_buffers(&stream->queue, cb);
-}
-
-static int uvc_ioctl_streamon(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
- int ret;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- guard(mutex)(&stream->mutex);
-
- if (handle->is_streaming)
- return 0;
-
- ret = uvc_queue_streamon(&stream->queue, type);
- if (ret)
- return ret;
-
- ret = uvc_pm_get(stream->dev);
- if (ret) {
- uvc_queue_streamoff(&stream->queue, type);
- return ret;
- }
- handle->is_streaming = true;
-
- return 0;
-}
-
-static int uvc_ioctl_streamoff(struct file *file, void *fh,
- enum v4l2_buf_type type)
-{
- struct uvc_fh *handle = fh;
- struct uvc_streaming *stream = handle->stream;
-
- if (!uvc_has_privileges(handle))
- return -EBUSY;
-
- guard(mutex)(&stream->mutex);
-
- uvc_queue_streamoff(&stream->queue, type);
- if (handle->is_streaming) {
- handle->is_streaming = false;
- uvc_pm_put(stream->dev);
- }
-
- return 0;
-}
-
static int uvc_ioctl_enum_input(struct file *file, void *fh,
struct v4l2_input *input)
{
@@ -961,13 +747,13 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
{
struct uvc_fh *handle = fh;
+ struct uvc_streaming *stream = handle->stream;
struct uvc_video_chain *chain = handle->chain;
u8 *buf;
int ret;
- ret = uvc_acquire_privileges(handle);
- if (ret < 0)
- return ret;
+ if (vb2_is_busy(&stream->queue.queue))
+ return -EBUSY;
if (chain->selector == NULL ||
(chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
@@ -1381,11 +1167,9 @@ static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
-DEFINE_FREE(uvc_pm_put, struct uvc_device *, if (_T) uvc_pm_put(_T))
static long uvc_v4l2_compat_ioctl32(struct file *file,
unsigned int cmd, unsigned long arg)
{
- struct uvc_device *uvc_device __free(uvc_pm_put) = NULL;
struct uvc_fh *handle = file->private_data;
union {
struct uvc_xu_control_mapping xmap;
@@ -1398,38 +1182,38 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
if (ret)
return ret;
- uvc_device = handle->stream->dev;
-
switch (cmd) {
case UVCIOC_CTRL_MAP32:
ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
if (ret)
- return ret;
+ break;
ret = uvc_ioctl_xu_ctrl_map(handle->chain, &karg.xmap);
if (ret)
- return ret;
+ break;
ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
if (ret)
- return ret;
-
+ break;
break;
case UVCIOC_CTRL_QUERY32:
ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
if (ret)
- return ret;
+ break;
ret = uvc_xu_ctrl_query(handle->chain, &karg.xqry);
if (ret)
- return ret;
+ break;
ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
if (ret)
- return ret;
+ break;
break;
default:
- return -ENOIOCTLCMD;
+ ret = -ENOIOCTLCMD;
+ break;
}
+ uvc_pm_put(handle->stream->dev);
+
return ret;
}
#endif
@@ -1438,81 +1222,37 @@ static long uvc_v4l2_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct uvc_fh *handle = file->private_data;
+ unsigned int converted_cmd = v4l2_translate_cmd(cmd);
int ret;
- /* The following IOCTLs do not need to turn on the camera. */
- switch (cmd) {
- case VIDIOC_CREATE_BUFS:
- case VIDIOC_DQBUF:
- case VIDIOC_ENUM_FMT:
- case VIDIOC_ENUM_FRAMEINTERVALS:
- case VIDIOC_ENUM_FRAMESIZES:
- case VIDIOC_ENUMINPUT:
- case VIDIOC_EXPBUF:
- case VIDIOC_G_FMT:
- case VIDIOC_G_PARM:
- case VIDIOC_G_SELECTION:
- case VIDIOC_QBUF:
- case VIDIOC_QUERYCAP:
- case VIDIOC_REQBUFS:
- case VIDIOC_SUBSCRIBE_EVENT:
- case VIDIOC_UNSUBSCRIBE_EVENT:
- return video_ioctl2(file, cmd, arg);
- }
-
- ret = uvc_pm_get(handle->stream->dev);
- if (ret)
+ /* The following IOCTLs need to turn on the camera. */
+ switch (converted_cmd) {
+ case UVCIOC_CTRL_MAP:
+ case UVCIOC_CTRL_QUERY:
+ case VIDIOC_G_CTRL:
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_G_INPUT:
+ case VIDIOC_QUERYCTRL:
+ case VIDIOC_QUERYMENU:
+ case VIDIOC_QUERY_EXT_CTRL:
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_EXT_CTRLS:
+ case VIDIOC_S_FMT:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_PARM:
+ case VIDIOC_TRY_EXT_CTRLS:
+ case VIDIOC_TRY_FMT:
+ ret = uvc_pm_get(handle->stream->dev);
+ if (ret)
+ return ret;
+ ret = video_ioctl2(file, cmd, arg);
+ uvc_pm_put(handle->stream->dev);
return ret;
+ }
- ret = video_ioctl2(file, cmd, arg);
-
- uvc_pm_put(handle->stream->dev);
- return ret;
-}
-
-static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s: not implemented\n", __func__);
- return -EINVAL;
-}
-
-static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_mmap(&stream->queue, vma);
-}
-
-static __poll_t uvc_v4l2_poll(struct file *file, poll_table *wait)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_poll(&stream->queue, file, wait);
-}
-
-#ifndef CONFIG_MMU
-static unsigned long uvc_v4l2_get_unmapped_area(struct file *file,
- unsigned long addr, unsigned long len, unsigned long pgoff,
- unsigned long flags)
-{
- struct uvc_fh *handle = file->private_data;
- struct uvc_streaming *stream = handle->stream;
-
- uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
-
- return uvc_queue_get_unmapped_area(&stream->queue, pgoff);
+ /* The other IOCTLs can run with the camera off. */
+ return video_ioctl2(file, cmd, arg);
}
-#endif
const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_g_fmt_vid_cap = uvc_ioctl_g_fmt,
@@ -1526,14 +1266,15 @@ const struct v4l2_ioctl_ops uvc_ioctl_ops = {
.vidioc_enum_fmt_vid_out = uvc_ioctl_enum_fmt,
.vidioc_try_fmt_vid_cap = uvc_ioctl_try_fmt,
.vidioc_try_fmt_vid_out = uvc_ioctl_try_fmt,
- .vidioc_reqbufs = uvc_ioctl_reqbufs,
- .vidioc_querybuf = uvc_ioctl_querybuf,
- .vidioc_qbuf = uvc_ioctl_qbuf,
- .vidioc_expbuf = uvc_ioctl_expbuf,
- .vidioc_dqbuf = uvc_ioctl_dqbuf,
- .vidioc_create_bufs = uvc_ioctl_create_bufs,
- .vidioc_streamon = uvc_ioctl_streamon,
- .vidioc_streamoff = uvc_ioctl_streamoff,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
.vidioc_enum_input = uvc_ioctl_enum_input,
.vidioc_g_input = uvc_ioctl_g_input,
.vidioc_s_input = uvc_ioctl_s_input,
@@ -1558,11 +1299,10 @@ const struct v4l2_file_operations uvc_fops = {
#ifdef CONFIG_COMPAT
.compat_ioctl32 = uvc_v4l2_compat_ioctl32,
#endif
- .read = uvc_v4l2_read,
- .mmap = uvc_v4l2_mmap,
- .poll = uvc_v4l2_poll,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
#ifndef CONFIG_MMU
- .get_unmapped_area = uvc_v4l2_get_unmapped_area,
+ .get_unmapped_area = vb2_fop_get_unmapped_area,
#endif
};
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index a75af314e46b..a5013a7fbca4 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -262,6 +262,15 @@ static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
ctrl->dwMaxPayloadTransferSize = bandwidth;
}
+
+ if (stream->intf->num_altsetting > 1 &&
+ ctrl->dwMaxPayloadTransferSize > stream->maxpsize) {
+ dev_warn_ratelimited(&stream->intf->dev,
+ "UVC non compliance: the max payload transmission size (%u) exceeds the size of the ep max packet (%u). Using the max size.\n",
+ ctrl->dwMaxPayloadTransferSize,
+ stream->maxpsize);
+ ctrl->dwMaxPayloadTransferSize = stream->maxpsize;
+ }
}
static size_t uvc_video_ctrl_size(struct uvc_streaming *stream)
@@ -1419,12 +1428,6 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
if (!meta_buf || length == 2)
return;
- if (meta_buf->length - meta_buf->bytesused <
- length + sizeof(meta->ns) + sizeof(meta->sof)) {
- meta_buf->error = 1;
- return;
- }
-
has_pts = mem[1] & UVC_STREAM_PTS;
has_scr = mem[1] & UVC_STREAM_SCR;
@@ -1445,6 +1448,12 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
!memcmp(scr, stream->clock.last_scr, 6)))
return;
+ if (meta_buf->length - meta_buf->bytesused <
+ length + sizeof(meta->ns) + sizeof(meta->sof)) {
+ meta_buf->error = 1;
+ return;
+ }
+
meta = (struct uvc_meta_buf *)((u8 *)meta_buf->mem + meta_buf->bytesused);
local_irq_save(flags);
time = uvc_video_get_time();
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index b9f8eb62ba1d..757254fc4fe9 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -77,6 +77,7 @@
#define UVC_QUIRK_DISABLE_AUTOSUSPEND 0x00008000
#define UVC_QUIRK_INVALID_DEVICE_SOF 0x00010000
#define UVC_QUIRK_MJPEG_NO_EOF 0x00020000
+#define UVC_QUIRK_MSXU_META 0x00040000
/* Format flags */
#define UVC_FMT_FLAG_COMPRESSED 0x00000001
@@ -134,6 +135,8 @@ struct uvc_control_mapping {
s32 master_manual;
u32 slave_ids[2];
+ bool disabled;
+
const struct uvc_control_mapping *(*filter_mapping)
(struct uvc_video_chain *chain,
struct uvc_control *ctrl);
@@ -326,7 +329,10 @@ struct uvc_buffer {
struct uvc_video_queue {
struct vb2_queue queue;
- struct mutex mutex; /* Protects queue */
+ struct mutex mutex; /*
+ * Serializes vb2_queue and
+ * fops
+ */
unsigned int flags;
unsigned int buf_used;
@@ -570,6 +576,8 @@ struct uvc_status {
};
} __packed;
+#define UVC_MAX_META_DATA_FORMATS 3
+
struct uvc_device {
struct usb_device *udev;
struct usb_interface *intf;
@@ -580,6 +588,9 @@ struct uvc_device {
const struct uvc_device_info *info;
+ u32 meta_formats[UVC_MAX_META_DATA_FORMATS];
+ unsigned int nmeta_formats;
+
atomic_t nmappings;
/* Video control interface */
@@ -619,18 +630,11 @@ struct uvc_device {
struct uvc_entity *gpio_unit;
};
-enum uvc_handle_state {
- UVC_HANDLE_PASSIVE = 0,
- UVC_HANDLE_ACTIVE = 1,
-};
-
struct uvc_fh {
struct v4l2_fh vfh;
struct uvc_video_chain *chain;
struct uvc_streaming *stream;
- enum uvc_handle_state state;
unsigned int pending_async_ctrls;
- bool is_streaming;
};
/* ------------------------------------------------------------------------
@@ -687,36 +691,11 @@ struct uvc_entity *uvc_entity_by_id(struct uvc_device *dev, int id);
/* Video buffers queue management. */
int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type);
-void uvc_queue_release(struct uvc_video_queue *queue);
-int uvc_request_buffers(struct uvc_video_queue *queue,
- struct v4l2_requestbuffers *rb);
-int uvc_query_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf);
-int uvc_create_buffers(struct uvc_video_queue *queue,
- struct v4l2_create_buffers *v4l2_cb);
-int uvc_queue_buffer(struct uvc_video_queue *queue,
- struct media_device *mdev,
- struct v4l2_buffer *v4l2_buf);
-int uvc_export_buffer(struct uvc_video_queue *queue,
- struct v4l2_exportbuffer *exp);
-int uvc_dequeue_buffer(struct uvc_video_queue *queue,
- struct v4l2_buffer *v4l2_buf, int nonblocking);
-int uvc_queue_streamon(struct uvc_video_queue *queue, enum v4l2_buf_type type);
-int uvc_queue_streamoff(struct uvc_video_queue *queue, enum v4l2_buf_type type);
void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect);
struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
struct uvc_buffer *buf);
struct uvc_buffer *uvc_queue_get_current_buffer(struct uvc_video_queue *queue);
void uvc_queue_buffer_release(struct uvc_buffer *buf);
-int uvc_queue_mmap(struct uvc_video_queue *queue,
- struct vm_area_struct *vma);
-__poll_t uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
- poll_table *wait);
-#ifndef CONFIG_MMU
-unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
- unsigned long pgoff);
-#endif
-int uvc_queue_allocated(struct uvc_video_queue *queue);
static inline int uvc_queue_streaming(struct uvc_video_queue *queue)
{
return vb2_is_streaming(&queue->queue);
@@ -749,6 +728,7 @@ int uvc_query_ctrl(struct uvc_device *dev, u8 query, u8 unit,
void uvc_video_clock_update(struct uvc_streaming *stream,
struct vb2_v4l2_buffer *vbuf,
struct uvc_buffer *buf);
+int uvc_meta_init(struct uvc_device *dev);
int uvc_meta_register(struct uvc_streaming *stream);
int uvc_register_video_device(struct uvc_device *dev,
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index bd160a8c9efe..6e585bc76367 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -323,6 +323,12 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_NV61M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_P012M, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 2, 4, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
+ /* Tiled YUV formats, non contiguous variant */
+ { .format = V4L2_PIX_FMT_NV12MT, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 64, 32, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
+ { .format = V4L2_PIX_FMT_NV12MT_16X16, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2,
+ .block_w = { 16, 8, 0, 0 }, .block_h = { 16, 8, 0, 0 }},
+
/* Bayer RGB formats */
{ .format = V4L2_PIX_FMT_SBGGR8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGBRG8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
@@ -332,6 +338,10 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_SGBRG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGRBG10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SRGGB10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB10P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 5, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SBGGR10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGBRG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGRBG10ALAW8, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
@@ -344,6 +354,28 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_SGBRG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SGRBG12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_SRGGB12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB12P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 2, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB14P, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 7, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SBGGR16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGBRG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SGRBG16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_SRGGB16, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
+ /* Renesas Camera Data Receiver Unit formats, bayer order agnostic */
+ { .format = V4L2_PIX_FMT_RAW_CRU10, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 6, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU12, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 5, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU14, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 4, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RAW_CRU20, .pixel_enc = V4L2_PIXEL_ENC_BAYER, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 3, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
};
unsigned int i;
@@ -505,10 +537,10 @@ s64 __v4l2_get_link_freq_ctrl(struct v4l2_ctrl_handler *handler,
freq = div_u64(v4l2_ctrl_g_ctrl_int64(ctrl) * mul, div);
- pr_warn("%s: Link frequency estimated using pixel rate: result might be inaccurate\n",
- __func__);
- pr_warn("%s: Consider implementing support for V4L2_CID_LINK_FREQ in the transmitter driver\n",
- __func__);
+ pr_warn_once("%s: Link frequency estimated using pixel rate: result might be inaccurate\n",
+ __func__);
+ pr_warn_once("%s: Consider implementing support for V4L2_CID_LINK_FREQ in the transmitter driver\n",
+ __func__);
}
return freq > 0 ? freq : -EINVAL;
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 90d25329661e..98b960775e87 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -968,12 +968,12 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS;
-
- if (p_h264_sps->chroma_format_idc < 3)
- p_h264_sps->flags &=
- ~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
}
+ if (p_h264_sps->chroma_format_idc < 3)
+ p_h264_sps->flags &=
+ ~V4L2_H264_SPS_FLAG_SEPARATE_COLOUR_PLANE;
+
if (p_h264_sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY)
p_h264_sps->flags &=
~V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD;
@@ -1631,14 +1631,17 @@ int v4l2_ctrl_handler_init_class(struct v4l2_ctrl_handler *hdl,
EXPORT_SYMBOL(v4l2_ctrl_handler_init_class);
/* Free all controls and control refs */
-void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
+int v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
{
struct v4l2_ctrl_ref *ref, *next_ref;
struct v4l2_ctrl *ctrl, *next_ctrl;
struct v4l2_subscribed_event *sev, *next_sev;
- if (hdl == NULL || hdl->buckets == NULL)
- return;
+ if (!hdl)
+ return 0;
+
+ if (!hdl->buckets)
+ return hdl->error;
v4l2_ctrl_handler_free_request(hdl);
@@ -1661,9 +1664,10 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
kvfree(hdl->buckets);
hdl->buckets = NULL;
hdl->cached = NULL;
- hdl->error = 0;
mutex_unlock(hdl->lock);
mutex_destroy(&hdl->_lock);
+
+ return hdl->error;
}
EXPORT_SYMBOL(v4l2_ctrl_handler_free);
diff --git a/drivers/media/v4l2-core/v4l2-i2c.c b/drivers/media/v4l2-core/v4l2-i2c.c
index 586c46544255..ffc64e10fcae 100644
--- a/drivers/media/v4l2-core/v4l2-i2c.c
+++ b/drivers/media/v4l2-core/v4l2-i2c.c
@@ -5,6 +5,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/property.h>
#include <media/v4l2-common.h>
#include <media/v4l2-device.h>
@@ -24,7 +25,7 @@ void v4l2_i2c_subdev_unregister(struct v4l2_subdev *sd)
* registered by us, and would not be
* re-created by just probing the V4L2 driver.
*/
- if (client && !client->dev.of_node && !client->dev.fwnode)
+ if (client && !dev_fwnode(&client->dev))
i2c_unregister_device(client);
}
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 650dc1956f73..46da373066f4 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1413,6 +1413,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG10DPCM8: descr = "8-bit Bayer GBGB/RGRG (DPCM)"; break;
case V4L2_PIX_FMT_SGRBG10DPCM8: descr = "8-bit Bayer GRGR/BGBG (DPCM)"; break;
case V4L2_PIX_FMT_SRGGB10DPCM8: descr = "8-bit Bayer RGRG/GBGB (DPCM)"; break;
+ case V4L2_PIX_FMT_RAW_CRU10: descr = "10-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR12: descr = "12-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG12: descr = "12-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG12: descr = "12-bit Bayer GRGR/BGBG"; break;
@@ -1421,6 +1422,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG12P: descr = "12-bit Bayer GBGB/RGRG Packed"; break;
case V4L2_PIX_FMT_SGRBG12P: descr = "12-bit Bayer GRGR/BGBG Packed"; break;
case V4L2_PIX_FMT_SRGGB12P: descr = "12-bit Bayer RGRG/GBGB Packed"; break;
+ case V4L2_PIX_FMT_RAW_CRU12: descr = "12-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR14: descr = "14-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG14: descr = "14-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG14: descr = "14-bit Bayer GRGR/BGBG"; break;
@@ -1429,10 +1431,12 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_SGBRG14P: descr = "14-bit Bayer GBGB/RGRG Packed"; break;
case V4L2_PIX_FMT_SGRBG14P: descr = "14-bit Bayer GRGR/BGBG Packed"; break;
case V4L2_PIX_FMT_SRGGB14P: descr = "14-bit Bayer RGRG/GBGB Packed"; break;
+ case V4L2_PIX_FMT_RAW_CRU14: descr = "14-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SBGGR16: descr = "16-bit Bayer BGBG/GRGR"; break;
case V4L2_PIX_FMT_SGBRG16: descr = "16-bit Bayer GBGB/RGRG"; break;
case V4L2_PIX_FMT_SGRBG16: descr = "16-bit Bayer GRGR/BGBG"; break;
case V4L2_PIX_FMT_SRGGB16: descr = "16-bit Bayer RGRG/GBGB"; break;
+ case V4L2_PIX_FMT_RAW_CRU20: descr = "14-bit Raw CRU Packed"; break;
case V4L2_PIX_FMT_SN9C20X_I420: descr = "GSPCA SN9C20X I420"; break;
case V4L2_PIX_FMT_SPCA501: descr = "GSPCA SPCA501"; break;
case V4L2_PIX_FMT_SPCA505: descr = "GSPCA SPCA505"; break;
@@ -1459,6 +1463,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
case V4L2_META_FMT_VSP1_HGO: descr = "R-Car VSP1 1-D Histogram"; break;
case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break;
case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break;
+ case V4L2_META_FMT_UVC_MSXU_1_5: descr = "UVC MSXU Metadata"; break;
case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break;
case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break;
case V4L2_META_FMT_RK_ISP1_PARAMS: descr = "Rockchip ISP1 3A Parameters"; break;
@@ -3245,7 +3250,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
return ret;
}
-static unsigned int video_translate_cmd(unsigned int cmd)
+unsigned int v4l2_translate_cmd(unsigned int cmd)
{
#if !defined(CONFIG_64BIT) && defined(CONFIG_COMPAT_32BIT_TIME)
switch (cmd) {
@@ -3266,6 +3271,7 @@ static unsigned int video_translate_cmd(unsigned int cmd)
return cmd;
}
+EXPORT_SYMBOL_GPL(v4l2_translate_cmd);
static int video_get_user(void __user *arg, void *parg,
unsigned int real_cmd, unsigned int cmd,
@@ -3426,7 +3432,7 @@ video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
size_t array_size = 0;
void __user *user_ptr = NULL;
void **kernel_ptr = NULL;
- unsigned int cmd = video_translate_cmd(orig_cmd);
+ unsigned int cmd = v4l2_translate_cmd(orig_cmd);
const size_t ioc_size = _IOC_SIZE(cmd);
/* Copy arguments into temp kernel buffer */
diff --git a/drivers/media/v4l2-core/v4l2-jpeg.c b/drivers/media/v4l2-core/v4l2-jpeg.c
index 6e2647323522..36a0f1a1b0d9 100644
--- a/drivers/media/v4l2-core/v4l2-jpeg.c
+++ b/drivers/media/v4l2-core/v4l2-jpeg.c
@@ -711,83 +711,3 @@ int v4l2_jpeg_parse_header(void *buf, size_t len, struct v4l2_jpeg_header *out)
return marker;
}
EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_header);
-
-/**
- * v4l2_jpeg_parse_frame_header - parse frame header
- * @buf: address of the frame header, after the SOF0 marker
- * @len: length of the frame header
- * @frame_header: returns the parsed frame header
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_frame_header(void *buf, size_t len,
- struct v4l2_jpeg_frame_header *frame_header)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_frame_header(&stream, SOF0, frame_header);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_frame_header);
-
-/**
- * v4l2_jpeg_parse_scan_header - parse scan header
- * @buf: address of the scan header, after the SOS marker
- * @len: length of the scan header
- * @scan_header: returns the parsed scan header
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_scan_header(void *buf, size_t len,
- struct v4l2_jpeg_scan_header *scan_header)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_scan_header(&stream, scan_header);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_scan_header);
-
-/**
- * v4l2_jpeg_parse_quantization_tables - parse quantization tables segment
- * @buf: address of the quantization table segment, after the DQT marker
- * @len: length of the quantization table segment
- * @precision: sample precision (P) in bits per component
- * @q_tables: returns four references into the buffer for the
- * four possible quantization table destinations
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_quantization_tables(void *buf, size_t len, u8 precision,
- struct v4l2_jpeg_reference *q_tables)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_quantization_tables(&stream, precision, q_tables);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_quantization_tables);
-
-/**
- * v4l2_jpeg_parse_huffman_tables - parse huffman tables segment
- * @buf: address of the Huffman table segment, after the DHT marker
- * @len: length of the Huffman table segment
- * @huffman_tables: returns four references into the buffer for the
- * four possible Huffman table destinations, in
- * the order DC0, DC1, AC0, AC1
- *
- * Returns 0 or negative error if parsing failed.
- */
-int v4l2_jpeg_parse_huffman_tables(void *buf, size_t len,
- struct v4l2_jpeg_reference *huffman_tables)
-{
- struct jpeg_stream stream;
-
- stream.curr = buf;
- stream.end = stream.curr + len;
- return jpeg_parse_huffman_tables(&stream, huffman_tables);
-}
-EXPORT_SYMBOL_GPL(v4l2_jpeg_parse_huffman_tables);
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index a3074f469b15..4fd25fea3b58 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -1004,6 +1004,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
struct v4l2_subdev_route *routes =
(struct v4l2_subdev_route *)(uintptr_t)routing->routes;
struct v4l2_subdev_krouting krouting = {};
+ unsigned int num_active_routes = 0;
unsigned int i;
if (!v4l2_subdev_enable_streams_api)
@@ -1041,9 +1042,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (!(pads[route->source_pad].flags &
MEDIA_PAD_FL_SOURCE))
return -EINVAL;
+
+ if (route->flags & V4L2_SUBDEV_ROUTE_FL_ACTIVE)
+ num_active_routes++;
}
/*
+ * Drivers that implement routing need to report a frame
+ * descriptor accordingly, with up to one entry per route. Until
+ * the frame descriptors entries get allocated dynamically,
+ * limit the number of active routes to
+ * V4L2_FRAME_DESC_ENTRY_MAX.
+ */
+ if (num_active_routes > V4L2_FRAME_DESC_ENTRY_MAX)
+ return -E2BIG;
+
+ /*
* If the driver doesn't support setting routing, just return
* the routing table.
*/
@@ -2219,6 +2233,9 @@ static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd,
*found_streams = BIT_ULL(0);
*enabled_streams =
(sd->enabled_pads & BIT_ULL(pad)) ? BIT_ULL(0) : 0;
+ dev_dbg(sd->dev,
+ "collect_streams: sub-device \"%s\" does not support streams\n",
+ sd->entity.name);
return;
}
@@ -2236,6 +2253,10 @@ static void v4l2_subdev_collect_streams(struct v4l2_subdev *sd,
if (cfg->enabled)
*enabled_streams |= BIT_ULL(cfg->stream);
}
+
+ dev_dbg(sd->dev,
+ "collect_streams: \"%s\":%u: found %#llx enabled %#llx\n",
+ sd->entity.name, pad, *found_streams, *enabled_streams);
}
static void v4l2_subdev_set_streams_enabled(struct v4l2_subdev *sd,
@@ -2271,6 +2292,9 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
bool use_s_stream;
int ret;
+ dev_dbg(dev, "enable streams \"%s\":%u/%#llx\n", sd->entity.name, pad,
+ streams_mask);
+
/* A few basic sanity checks first. */
if (pad >= sd->entity.num_pads)
return -EINVAL;
@@ -2318,8 +2342,6 @@ int v4l2_subdev_enable_streams(struct v4l2_subdev *sd, u32 pad,
goto done;
}
- dev_dbg(dev, "enable streams %u:%#llx\n", pad, streams_mask);
-
already_streaming = v4l2_subdev_is_streaming(sd);
if (!use_s_stream) {
@@ -2371,6 +2393,9 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
bool use_s_stream;
int ret;
+ dev_dbg(dev, "disable streams \"%s\":%u/%#llx\n", sd->entity.name, pad,
+ streams_mask);
+
/* A few basic sanity checks first. */
if (pad >= sd->entity.num_pads)
return -EINVAL;
@@ -2418,8 +2443,6 @@ int v4l2_subdev_disable_streams(struct v4l2_subdev *sd, u32 pad,
goto done;
}
- dev_dbg(dev, "disable streams %u:%#llx\n", pad, streams_mask);
-
if (!use_s_stream) {
/* Call the .disable_streams() operation. */
ret = v4l2_subdev_call(sd, pad, disable_streams, state, pad,
diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c
index 322237e056f3..71357a036292 100644
--- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c
+++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c
@@ -129,6 +129,109 @@ static const struct tps68470_regulator_platform_data surface_go_tps68470_pdata =
},
};
+/* Settings for Dell 7212 Tablet */
+
+static struct regulator_consumer_supply int3479_vsio_consumer_supplies[] = {
+ REGULATOR_SUPPLY("avdd", "i2c-INT3479:00"),
+};
+
+static struct regulator_consumer_supply int3479_aux1_consumer_supplies[] = {
+ REGULATOR_SUPPLY("dvdd", "i2c-INT3479:00"),
+};
+
+static struct regulator_consumer_supply int3479_aux2_consumer_supplies[] = {
+ REGULATOR_SUPPLY("dovdd", "i2c-INT3479:00"),
+};
+
+static const struct regulator_init_data dell_7212_tps68470_core_reg_init_data = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 0,
+ .consumer_supplies = NULL,
+};
+
+static const struct regulator_init_data dell_7212_tps68470_ana_reg_init_data = {
+ .constraints = {
+ .min_uV = 2815200,
+ .max_uV = 2815200,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 0,
+ .consumer_supplies = NULL,
+};
+
+static const struct regulator_init_data dell_7212_tps68470_vcm_reg_init_data = {
+ .constraints = {
+ .min_uV = 2815200,
+ .max_uV = 2815200,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 0,
+ .consumer_supplies = NULL,
+};
+
+static const struct regulator_init_data dell_7212_tps68470_vio_reg_init_data = {
+ .constraints = {
+ .min_uV = 1800600,
+ .max_uV = 1800600,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 0,
+ .consumer_supplies = NULL,
+};
+
+static const struct regulator_init_data dell_7212_tps68470_vsio_reg_init_data = {
+ .constraints = {
+ .min_uV = 1800600,
+ .max_uV = 1800600,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(int3479_vsio_consumer_supplies),
+ .consumer_supplies = int3479_vsio_consumer_supplies,
+};
+
+static const struct regulator_init_data dell_7212_tps68470_aux1_reg_init_data = {
+ .constraints = {
+ .min_uV = 1213200,
+ .max_uV = 1213200,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(int3479_aux1_consumer_supplies),
+ .consumer_supplies = int3479_aux1_consumer_supplies,
+};
+
+static const struct regulator_init_data dell_7212_tps68470_aux2_reg_init_data = {
+ .constraints = {
+ .min_uV = 1800600,
+ .max_uV = 1800600,
+ .apply_uV = 1,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(int3479_aux2_consumer_supplies),
+ .consumer_supplies = int3479_aux2_consumer_supplies,
+};
+
+static const struct tps68470_regulator_platform_data dell_7212_tps68470_pdata = {
+ .reg_init_data = {
+ [TPS68470_CORE] = &dell_7212_tps68470_core_reg_init_data,
+ [TPS68470_ANA] = &dell_7212_tps68470_ana_reg_init_data,
+ [TPS68470_VCM] = &dell_7212_tps68470_vcm_reg_init_data,
+ [TPS68470_VIO] = &dell_7212_tps68470_vio_reg_init_data,
+ [TPS68470_VSIO] = &dell_7212_tps68470_vsio_reg_init_data,
+ [TPS68470_AUX1] = &dell_7212_tps68470_aux1_reg_init_data,
+ [TPS68470_AUX2] = &dell_7212_tps68470_aux2_reg_init_data,
+ },
+};
+
static struct gpiod_lookup_table surface_go_int347a_gpios = {
.dev_id = "i2c-INT347A:00",
.table = {
@@ -146,6 +249,15 @@ static struct gpiod_lookup_table surface_go_int347e_gpios = {
}
};
+static struct gpiod_lookup_table dell_7212_int3479_gpios = {
+ .dev_id = "i2c-INT3479:00",
+ .table = {
+ GPIO_LOOKUP("tps68470-gpio", 3, "reset", GPIO_ACTIVE_LOW),
+ GPIO_LOOKUP("tps68470-gpio", 4, "powerdown", GPIO_ACTIVE_LOW),
+ { }
+ }
+};
+
static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = {
.dev_name = "i2c-INT3472:05",
.tps68470_regulator_pdata = &surface_go_tps68470_pdata,
@@ -166,6 +278,15 @@ static const struct int3472_tps68470_board_data surface_go3_tps68470_board_data
},
};
+static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data = {
+ .dev_name = "i2c-INT3472:05",
+ .tps68470_regulator_pdata = &dell_7212_tps68470_pdata,
+ .n_gpiod_lookups = 1,
+ .tps68470_gpio_lookup_tables = {
+ &dell_7212_int3479_gpios,
+ },
+};
+
static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
{
.matches = {
@@ -188,6 +309,13 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
},
.driver_data = (void *)&surface_go3_tps68470_board_data,
},
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7212 Rugged Extreme Tablet"),
+ },
+ .driver_data = (void *)&dell_7212_tps68470_board_data,
+ },
{ }
};
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index b44214854399..ab250c89cd4d 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -28,12 +28,12 @@ source "drivers/staging/media/imx/Kconfig"
source "drivers/staging/media/ipu3/Kconfig"
+source "drivers/staging/media/ipu7/Kconfig"
+
source "drivers/staging/media/max96712/Kconfig"
source "drivers/staging/media/meson/vdec/Kconfig"
-source "drivers/staging/media/rkvdec/Kconfig"
-
source "drivers/staging/media/starfive/Kconfig"
source "drivers/staging/media/sunxi/Kconfig"
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index ad4e9619a9e0..4a073938b2b2 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -4,9 +4,9 @@ obj-$(CONFIG_INTEL_ATOMISP) += atomisp/
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
obj-$(CONFIG_VIDEO_MAX96712) += max96712/
obj-$(CONFIG_VIDEO_MESON_VDEC) += meson/vdec/
-obj-$(CONFIG_VIDEO_ROCKCHIP_VDEC) += rkvdec/
obj-$(CONFIG_VIDEO_STARFIVE_CAMSS) += starfive/
obj-$(CONFIG_VIDEO_SUNXI) += sunxi/
obj-$(CONFIG_VIDEO_TEGRA) += tegra-video/
obj-$(CONFIG_VIDEO_IPU3_IMGU) += ipu3/
+obj-$(CONFIG_VIDEO_INTEL_IPU7) += ipu7/
obj-$(CONFIG_DVB_AV7110) += av7110/
diff --git a/drivers/staging/media/atomisp/Kconfig b/drivers/staging/media/atomisp/Kconfig
index 1265fc58a232..fbe507ae6ec6 100644
--- a/drivers/staging/media/atomisp/Kconfig
+++ b/drivers/staging/media/atomisp/Kconfig
@@ -12,6 +12,7 @@ menuconfig INTEL_ATOMISP
config VIDEO_ATOMISP
tristate "Intel Atom Image Signal Processor Driver"
depends on VIDEO_DEV && INTEL_ATOMISP
+ depends on INTEL_SKL_INT3472
depends on IPU_BRIDGE
depends on MEDIA_PCI_SUPPORT
depends on PMIC_OPREGION
diff --git a/drivers/staging/media/atomisp/Makefile b/drivers/staging/media/atomisp/Makefile
index 43116c74781d..1d0fbe22036b 100644
--- a/drivers/staging/media/atomisp/Makefile
+++ b/drivers/staging/media/atomisp/Makefile
@@ -17,7 +17,6 @@ atomisp-objs += \
pci/atomisp_compat_css20.o \
pci/atomisp_csi2.o \
pci/atomisp_csi2_bridge.o \
- pci/atomisp_drvfs.o \
pci/atomisp_fops.o \
pci/atomisp_ioctl.o \
pci/atomisp_subdev.o \
diff --git a/drivers/staging/media/atomisp/TODO b/drivers/staging/media/atomisp/TODO
index 27cbbde93b1e..82be275b4a0a 100644
--- a/drivers/staging/media/atomisp/TODO
+++ b/drivers/staging/media/atomisp/TODO
@@ -7,8 +7,6 @@ TODO
* Remove/disable custom v4l2-ctrls
-* Remove custom sysfs files created by atomisp_drvfs.c
-
* Remove unnecessary/unwanted module parameters
* Remove abuse of priv field in various v4l2 userspace API structs
diff --git a/drivers/staging/media/atomisp/i2c/Kconfig b/drivers/staging/media/atomisp/i2c/Kconfig
index 4f46182da58b..ef2094c22347 100644
--- a/drivers/staging/media/atomisp/i2c/Kconfig
+++ b/drivers/staging/media/atomisp/i2c/Kconfig
@@ -31,6 +31,7 @@ config VIDEO_ATOMISP_GC0310
tristate "GC0310 sensor support"
depends on ACPI
depends on I2C && VIDEO_DEV
+ select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor-level driver for the Galaxycore
GC0310 0.3MP sensor.
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
index d35394f1ddbb..7af4d66f42a0 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc0310.c
@@ -3,7 +3,7 @@
* Support for GalaxyCore GC0310 VGA camera sensor.
*
* Copyright (c) 2013 Intel Corporation. All Rights Reserved.
- * Copyright (c) 2023 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (c) 2023-2025 Hans de Goede <hansg@kernel.org>
*/
#include <linux/delay.h>
@@ -13,77 +13,92 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/string.h>
#include <linux/types.h>
+#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
#define GC0310_NATIVE_WIDTH 656
#define GC0310_NATIVE_HEIGHT 496
+/*
+ * The actual PLL output rate is unknown, the datasheet
+ * says that the formula for the frame-time in pixels is:
+ * rowtime = win-width + hblank + sh-delay + 4
+ * frametime = rowtime * (win-height + vblank)
+ * Filling this in and multiplying by 30 fps gives:
+ * pixelrate = (660 + 178 + 42 + 4) * (498 + 27) * 30 = 13923000
+ */
+#define GC0310_PIXELRATE 13923000
+/* single lane, bus-format is 8 bpp, CSI-2 is double data rate */
+#define GC0310_LINK_FREQ (GC0310_PIXELRATE * 8 / 2)
+#define GC0310_MCLK_FREQ 19200000
#define GC0310_FPS 30
#define GC0310_SKIP_FRAMES 3
-#define GC0310_FOCAL_LENGTH_NUM 278 /* 2.78mm */
-
#define GC0310_ID 0xa310
-#define GC0310_RESET_RELATED 0xFE
+#define GC0310_RESET_RELATED_REG CCI_REG8(0xfe)
#define GC0310_REGISTER_PAGE_0 0x0
#define GC0310_REGISTER_PAGE_3 0x3
/*
* GC0310 System control registers
*/
-#define GC0310_SW_STREAM 0x10
-
-#define GC0310_SC_CMMN_CHIP_ID_H 0xf0
-#define GC0310_SC_CMMN_CHIP_ID_L 0xf1
-
-#define GC0310_AEC_PK_EXPO_H 0x03
-#define GC0310_AEC_PK_EXPO_L 0x04
-#define GC0310_AGC_ADJ 0x48
-#define GC0310_DGC_ADJ 0x71
-#define GC0310_GROUP_ACCESS 0x3208
-
-#define GC0310_H_CROP_START_H 0x09
-#define GC0310_H_CROP_START_L 0x0A
-#define GC0310_V_CROP_START_H 0x0B
-#define GC0310_V_CROP_START_L 0x0C
-#define GC0310_H_OUTSIZE_H 0x0F
-#define GC0310_H_OUTSIZE_L 0x10
-#define GC0310_V_OUTSIZE_H 0x0D
-#define GC0310_V_OUTSIZE_L 0x0E
-#define GC0310_H_BLANKING_H 0x05
-#define GC0310_H_BLANKING_L 0x06
-#define GC0310_V_BLANKING_H 0x07
-#define GC0310_V_BLANKING_L 0x08
-#define GC0310_SH_DELAY 0x11
+#define GC0310_SW_STREAM_REG CCI_REG8(0x10)
#define GC0310_START_STREAMING 0x94 /* 8-bit enable */
#define GC0310_STOP_STREAMING 0x0 /* 8-bit disable */
+#define GC0310_SC_CMMN_CHIP_ID_REG CCI_REG16(0xf0)
+
+#define GC0310_AEC_PK_EXPO_REG CCI_REG16(0x03)
+#define GC0310_AGC_ADJ_REG CCI_REG8(0x48)
+#define GC0310_DGC_ADJ_REG CCI_REG8(0x71)
+
+#define GC0310_H_CROP_START_REG CCI_REG16(0x09)
+#define GC0310_V_CROP_START_REG CCI_REG16(0x0b)
+#define GC0310_H_OUTSIZE_REG CCI_REG16(0x0f)
+#define GC0310_V_OUTSIZE_REG CCI_REG16(0x0d)
+
+#define GC0310_H_BLANKING_REG CCI_REG16(0x05)
+/* Hblank-register + sh-delay + H-crop + 4 (from hw) */
+#define GC0310_H_BLANK_DEFAULT (178 + 42 + 4 + 4)
+
+#define GC0310_V_BLANKING_REG CCI_REG16(0x07)
+/* Vblank needs an offset compensate for the small V-crop done */
+#define GC0310_V_BLANK_OFFSET 2
+/* Vsync start time + 1 row vsync + vsync end time + offset */
+#define GC0310_V_BLANK_MIN (9 + 1 + 4 + GC0310_V_BLANK_OFFSET)
+#define GC0310_V_BLANK_DEFAULT (27 + GC0310_V_BLANK_OFFSET)
+#define GC0310_V_BLANK_MAX (4095 - GC0310_NATIVE_HEIGHT)
+
+#define GC0310_SH_DELAY_REG CCI_REG8(0x11)
+#define GC0310_VS_START_TIME_REG CCI_REG8(0x12)
+#define GC0310_VS_END_TIME_REG CCI_REG8(0x13)
+
#define to_gc0310_sensor(x) container_of(x, struct gc0310_device, sd)
struct gc0310_device {
struct v4l2_subdev sd;
struct media_pad pad;
- /* Protect against concurrent changes to controls */
- struct mutex input_lock;
- bool is_streaming;
+ struct regmap *regmap;
struct gpio_desc *reset;
struct gpio_desc *powerdown;
- struct gc0310_mode {
- struct v4l2_mbus_framefmt fmt;
- } mode;
-
struct gc0310_ctrls {
struct v4l2_ctrl_handler handler;
struct v4l2_ctrl *exposure;
struct v4l2_ctrl *gain;
+ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *pixel_rate;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *hblank;
} ctrls;
};
@@ -92,7 +107,7 @@ struct gc0310_reg {
u8 val;
};
-static const struct gc0310_reg gc0310_reset_register[] = {
+static const struct reg_sequence gc0310_reset_register[] = {
/* System registers */
{ 0xfe, 0xf0 },
{ 0xfe, 0xf0 },
@@ -142,8 +157,7 @@ static const struct gc0310_reg gc0310_reset_register[] = {
{ 0x04, 0xc0 }, /* 0xe8 //58 */
{ 0x05, 0x00 },
{ 0x06, 0xb2 }, /* 0x0a //HB */
- { 0x07, 0x00 },
- { 0x08, 0x0c }, /* 0x89 //VB */
+ /* Vblank (reg 0x07 + 0x08) gets set by the vblank ctrl */
{ 0x09, 0x00 }, /* row start */
{ 0x0a, 0x00 },
{ 0x0b, 0x00 }, /* col start */
@@ -235,7 +249,7 @@ static const struct gc0310_reg gc0310_reset_register[] = {
{ 0xfe, 0x00 },
};
-static const struct gc0310_reg gc0310_VGA_30fps[] = {
+static const struct reg_sequence gc0310_VGA_30fps[] = {
{ 0xfe, 0x00 },
{ 0x0d, 0x01 }, /* height */
{ 0x0e, 0xf2 }, /* 0xf7 //height */
@@ -259,41 +273,14 @@ static const struct gc0310_reg gc0310_VGA_30fps[] = {
{ 0xfe, 0x00 },
};
-/*
- * gc0310_write_reg_array - Initializes a list of GC0310 registers
- * @client: i2c driver client structure
- * @reglist: list of registers to be written
- * @count: number of register, value pairs in the list
- */
-static int gc0310_write_reg_array(struct i2c_client *client,
- const struct gc0310_reg *reglist, int count)
-{
- int i, err;
-
- for (i = 0; i < count; i++) {
- err = i2c_smbus_write_byte_data(client, reglist[i].reg, reglist[i].val);
- if (err) {
- dev_err(&client->dev, "write error: wrote 0x%x to offset 0x%x error %d",
- reglist[i].val, reglist[i].reg, err);
- return err;
- }
- }
-
- return 0;
-}
-
-static int gc0310_exposure_set(struct gc0310_device *dev, u32 exp)
-{
- struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
-
- return i2c_smbus_write_word_swapped(client, GC0310_AEC_PK_EXPO_H, exp);
-}
+static const s64 link_freq_menu_items[] = {
+ GC0310_LINK_FREQ,
+};
-static int gc0310_gain_set(struct gc0310_device *dev, u32 gain)
+static int gc0310_gain_set(struct gc0310_device *sensor, u32 gain)
{
- struct i2c_client *client = v4l2_get_subdevdata(&dev->sd);
u8 again, dgain;
- int ret;
+ int ret = 0;
/* Taken from original driver, this never sets dgain lower then 32? */
@@ -308,36 +295,55 @@ static int gc0310_gain_set(struct gc0310_device *dev, u32 gain)
dgain = gain / 2;
}
- ret = i2c_smbus_write_byte_data(client, GC0310_AGC_ADJ, again);
- if (ret)
- return ret;
+ cci_write(sensor->regmap, GC0310_AGC_ADJ_REG, again, &ret);
+ cci_write(sensor->regmap, GC0310_DGC_ADJ_REG, dgain, &ret);
+ return ret;
+}
+
+static int gc0310_exposure_update_range(struct gc0310_device *sensor)
+{
+ int exp_max = GC0310_NATIVE_HEIGHT + sensor->ctrls.vblank->val;
- return i2c_smbus_write_byte_data(client, GC0310_DGC_ADJ, dgain);
+ return __v4l2_ctrl_modify_range(sensor->ctrls.exposure, 0, exp_max,
+ 1, exp_max);
}
static int gc0310_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct gc0310_device *dev =
+ struct gc0310_device *sensor =
container_of(ctrl->handler, struct gc0310_device, ctrls.handler);
int ret;
+ /* Update exposure range on vblank changes */
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ ret = gc0310_exposure_update_range(sensor);
+ if (ret)
+ return ret;
+ }
+
/* Only apply changes to the controls if the device is powered up */
- if (!pm_runtime_get_if_in_use(dev->sd.dev))
+ if (!pm_runtime_get_if_in_use(sensor->sd.dev))
return 0;
switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
- ret = gc0310_exposure_set(dev, ctrl->val);
+ ret = cci_write(sensor->regmap, GC0310_AEC_PK_EXPO_REG,
+ ctrl->val, NULL);
break;
- case V4L2_CID_GAIN:
- ret = gc0310_gain_set(dev, ctrl->val);
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = gc0310_gain_set(sensor, ctrl->val);
+ break;
+ case V4L2_CID_VBLANK:
+ ret = cci_write(sensor->regmap, GC0310_V_BLANKING_REG,
+ ctrl->val - GC0310_V_BLANK_OFFSET,
+ NULL);
break;
default:
ret = -EINVAL;
break;
}
- pm_runtime_put(dev->sd.dev);
+ pm_runtime_put(sensor->sd.dev);
return ret;
}
@@ -345,17 +351,6 @@ static const struct v4l2_ctrl_ops ctrl_ops = {
.s_ctrl = gc0310_s_ctrl,
};
-static struct v4l2_mbus_framefmt *
-gc0310_get_pad_format(struct gc0310_device *dev,
- struct v4l2_subdev_state *state,
- unsigned int pad, enum v4l2_subdev_format_whence which)
-{
- if (which == V4L2_SUBDEV_FORMAT_TRY)
- return v4l2_subdev_state_get_format(state, pad);
-
- return &dev->mode.fmt;
-}
-
/* The GC0310 currently only supports 1 fixed fmt */
static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt)
{
@@ -366,54 +361,72 @@ static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt)
fmt->code = MEDIA_BUS_FMT_SGRBG8_1X8;
}
-static int gc0310_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
+static int gc0310_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
{
- struct gc0310_device *dev = to_gc0310_sensor(sd);
- struct v4l2_mbus_framefmt *fmt;
+ /* Only the single fixed 656x496 mode is supported, without croping */
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = GC0310_NATIVE_WIDTH;
+ sel->r.height = GC0310_NATIVE_HEIGHT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
- fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which);
- gc0310_fill_format(fmt);
+static int gc0310_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc0310_device *sensor = to_gc0310_sensor(sd);
- format->format = *fmt;
+ gpiod_set_value_cansleep(sensor->powerdown, 1);
+ gpiod_set_value_cansleep(sensor->reset, 1);
return 0;
}
-static int gc0310_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_format *format)
+static int gc0310_power_on(struct device *dev)
{
- struct gc0310_device *dev = to_gc0310_sensor(sd);
- struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct gc0310_device *sensor = to_gc0310_sensor(sd);
+
+ fsleep(10 * USEC_PER_MSEC);
+ gpiod_set_value_cansleep(sensor->reset, 0);
+ fsleep(10 * USEC_PER_MSEC);
+ gpiod_set_value_cansleep(sensor->powerdown, 0);
+ fsleep(10 * USEC_PER_MSEC);
- fmt = gc0310_get_pad_format(dev, sd_state, format->pad, format->which);
- format->format = *fmt;
return 0;
}
-static int gc0310_detect(struct i2c_client *client)
+static int gc0310_detect(struct gc0310_device *sensor)
{
- struct i2c_adapter *adapter = client->adapter;
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ u64 val;
int ret;
- if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
- ret = pm_runtime_get_sync(&client->dev);
- if (ret >= 0)
- ret = i2c_smbus_read_word_swapped(client, GC0310_SC_CMMN_CHIP_ID_H);
- pm_runtime_put(&client->dev);
+ ret = cci_read(sensor->regmap, GC0310_SC_CMMN_CHIP_ID_REG, &val, NULL);
if (ret < 0) {
dev_err(&client->dev, "read sensor_id failed: %d\n", ret);
return -ENODEV;
}
- dev_dbg(&client->dev, "sensor ID = 0x%x\n", ret);
+ dev_dbg(&client->dev, "sensor ID = 0x%llx\n", val);
- if (ret != GC0310_ID) {
- dev_err(&client->dev, "sensor ID error, read id = 0x%x, target id = 0x%x\n",
- ret, GC0310_ID);
+ if (val != GC0310_ID) {
+ dev_err(&client->dev, "sensor ID error, read id = 0x%llx, target id = 0x%x\n",
+ val, GC0310_ID);
return -ENODEV;
}
@@ -422,85 +435,67 @@ static int gc0310_detect(struct i2c_client *client)
return 0;
}
-static int gc0310_s_stream(struct v4l2_subdev *sd, int enable)
+static int gc0310_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
{
- struct gc0310_device *dev = to_gc0310_sensor(sd);
+ struct gc0310_device *sensor = to_gc0310_sensor(sd);
struct i2c_client *client = v4l2_get_subdevdata(sd);
- int ret = 0;
-
- dev_dbg(&client->dev, "%s S enable=%d\n", __func__, enable);
- mutex_lock(&dev->input_lock);
-
- if (enable) {
- ret = pm_runtime_get_sync(&client->dev);
- if (ret < 0)
- goto error_power_down;
-
- msleep(100);
-
- ret = gc0310_write_reg_array(client, gc0310_reset_register,
- ARRAY_SIZE(gc0310_reset_register));
- if (ret)
- goto error_power_down;
-
- ret = gc0310_write_reg_array(client, gc0310_VGA_30fps,
- ARRAY_SIZE(gc0310_VGA_30fps));
- if (ret)
- goto error_power_down;
-
- /* restore value of all ctrls */
- ret = __v4l2_ctrl_handler_setup(&dev->ctrls.handler);
- if (ret)
- goto error_power_down;
-
- /* enable per frame MIPI and sensor ctrl reset */
- ret = i2c_smbus_write_byte_data(client, 0xFE, 0x30);
- if (ret)
- goto error_power_down;
- }
+ int ret;
- ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_3);
+ ret = pm_runtime_resume_and_get(&client->dev);
if (ret)
- goto error_power_down;
+ return ret;
- ret = i2c_smbus_write_byte_data(client, GC0310_SW_STREAM,
- enable ? GC0310_START_STREAMING : GC0310_STOP_STREAMING);
+ ret = regmap_multi_reg_write(sensor->regmap,
+ gc0310_reset_register,
+ ARRAY_SIZE(gc0310_reset_register));
if (ret)
goto error_power_down;
- ret = i2c_smbus_write_byte_data(client, GC0310_RESET_RELATED, GC0310_REGISTER_PAGE_0);
+ ret = regmap_multi_reg_write(sensor->regmap,
+ gc0310_VGA_30fps,
+ ARRAY_SIZE(gc0310_VGA_30fps));
if (ret)
goto error_power_down;
- if (!enable)
- pm_runtime_put(&client->dev);
+ /* restore value of all ctrls */
+ ret = __v4l2_ctrl_handler_setup(&sensor->ctrls.handler);
- dev->is_streaming = enable;
- mutex_unlock(&dev->input_lock);
- return 0;
+ /* enable per frame MIPI and sensor ctrl reset */
+ cci_write(sensor->regmap, GC0310_RESET_RELATED_REG, 0x30, &ret);
+
+ cci_write(sensor->regmap, GC0310_RESET_RELATED_REG,
+ GC0310_REGISTER_PAGE_3, &ret);
+ cci_write(sensor->regmap, GC0310_SW_STREAM_REG,
+ GC0310_START_STREAMING, &ret);
+ cci_write(sensor->regmap, GC0310_RESET_RELATED_REG,
+ GC0310_REGISTER_PAGE_0, &ret);
error_power_down:
- pm_runtime_put(&client->dev);
- dev->is_streaming = false;
- mutex_unlock(&dev->input_lock);
+ if (ret)
+ pm_runtime_put(&client->dev);
+
return ret;
}
-static int gc0310_get_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state,
- struct v4l2_subdev_frame_interval *interval)
+static int gc0310_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
{
- /*
- * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
- * subdev active state API.
- */
- if (interval->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
+ struct gc0310_device *sensor = to_gc0310_sensor(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = 0;
- interval->interval.numerator = 1;
- interval->interval.denominator = GC0310_FPS;
+ cci_write(sensor->regmap, GC0310_RESET_RELATED_REG,
+ GC0310_REGISTER_PAGE_3, &ret);
+ cci_write(sensor->regmap, GC0310_SW_STREAM_REG,
+ GC0310_STOP_STREAMING, &ret);
+ cci_write(sensor->regmap, GC0310_RESET_RELATED_REG,
+ GC0310_REGISTER_PAGE_0, &ret);
- return 0;
+ pm_runtime_put(&client->dev);
+ return ret;
}
static int gc0310_enum_mbus_code(struct v4l2_subdev *sd,
@@ -531,164 +526,239 @@ static int gc0310_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static int gc0310_g_skip_frames(struct v4l2_subdev *sd, u32 *frames)
-{
- *frames = GC0310_SKIP_FRAMES;
- return 0;
-}
-
-static const struct v4l2_subdev_sensor_ops gc0310_sensor_ops = {
- .g_skip_frames = gc0310_g_skip_frames,
-};
-
static const struct v4l2_subdev_video_ops gc0310_video_ops = {
- .s_stream = gc0310_s_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops gc0310_pad_ops = {
.enum_mbus_code = gc0310_enum_mbus_code,
.enum_frame_size = gc0310_enum_frame_size,
- .get_fmt = gc0310_get_fmt,
- .set_fmt = gc0310_set_fmt,
- .get_frame_interval = gc0310_get_frame_interval,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = v4l2_subdev_get_fmt, /* Only 1 fixed mode supported */
+ .get_selection = gc0310_get_selection,
+ .set_selection = gc0310_get_selection,
+ .enable_streams = gc0310_enable_streams,
+ .disable_streams = gc0310_disable_streams,
};
static const struct v4l2_subdev_ops gc0310_ops = {
.video = &gc0310_video_ops,
.pad = &gc0310_pad_ops,
- .sensor = &gc0310_sensor_ops,
};
-static int gc0310_init_controls(struct gc0310_device *dev)
+static int gc0310_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
{
- struct v4l2_ctrl_handler *hdl = &dev->ctrls.handler;
+ gc0310_fill_format(v4l2_subdev_state_get_format(sd_state, 0));
+ return 0;
+}
- v4l2_ctrl_handler_init(hdl, 2);
+static const struct v4l2_subdev_internal_ops gc0310_internal_ops = {
+ .init_state = gc0310_init_state,
+};
+
+static int gc0310_init_controls(struct gc0310_device *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);
+ struct v4l2_ctrl_handler *hdl = &sensor->ctrls.handler;
+ struct v4l2_fwnode_device_properties props;
+ int exp_max, ret;
+
+ v4l2_ctrl_handler_init(hdl, 8);
/* Use the same lock for controls as for everything else */
- hdl->lock = &dev->input_lock;
- dev->sd.ctrl_handler = hdl;
+ sensor->sd.ctrl_handler = hdl;
- dev->ctrls.exposure =
- v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0, 4095, 1, 1023);
+ exp_max = GC0310_NATIVE_HEIGHT + GC0310_V_BLANK_DEFAULT;
+ sensor->ctrls.exposure =
+ v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_EXPOSURE, 0,
+ exp_max, 1, exp_max);
/* 32 steps at base gain 1 + 64 half steps at base gain 2 */
- dev->ctrls.gain =
- v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_GAIN, 0, 95, 1, 31);
+ sensor->ctrls.gain =
+ v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 0, 95, 1, 31);
+
+ sensor->ctrls.link_freq =
+ v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
+ 0, 0, link_freq_menu_items);
+ sensor->ctrls.pixel_rate =
+ v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_PIXEL_RATE, 0,
+ GC0310_PIXELRATE, 1, GC0310_PIXELRATE);
+
+ sensor->ctrls.vblank =
+ v4l2_ctrl_new_std(hdl, &ctrl_ops, V4L2_CID_VBLANK,
+ GC0310_V_BLANK_MIN,
+ GC0310_V_BLANK_MAX, 1,
+ GC0310_V_BLANK_DEFAULT);
+
+ sensor->ctrls.hblank =
+ v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_HBLANK,
+ GC0310_H_BLANK_DEFAULT,
+ GC0310_H_BLANK_DEFAULT, 1,
+ GC0310_H_BLANK_DEFAULT);
+
+ ret = v4l2_fwnode_device_parse(&client->dev, &props);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_new_fwnode_properties(hdl, &ctrl_ops, &props);
- return hdl->error;
+ if (hdl->error)
+ return hdl->error;
+
+ sensor->ctrls.pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ sensor->ctrls.link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ sensor->ctrls.hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ return 0;
}
static void gc0310_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
- struct gc0310_device *dev = to_gc0310_sensor(sd);
-
- dev_dbg(&client->dev, "gc0310_remove...\n");
+ struct gc0310_device *sensor = to_gc0310_sensor(sd);
v4l2_async_unregister_subdev(sd);
- media_entity_cleanup(&dev->sd.entity);
- v4l2_ctrl_handler_free(&dev->ctrls.handler);
- mutex_destroy(&dev->input_lock);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&sensor->sd.entity);
+ v4l2_ctrl_handler_free(&sensor->ctrls.handler);
pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ gc0310_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
}
-static int gc0310_probe(struct i2c_client *client)
+static int gc0310_check_hwcfg(struct device *dev)
{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
struct fwnode_handle *ep_fwnode;
- struct gc0310_device *dev;
+ unsigned long link_freq_bitmap;
+ u32 mclk;
int ret;
/*
* Sometimes the fwnode graph is initialized by the bridge driver.
* Bridge drivers doing this may also add GPIO mappings, wait for this.
*/
- ep_fwnode = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
+ ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
if (!ep_fwnode)
- return dev_err_probe(&client->dev, -EPROBE_DEFER, "waiting for fwnode graph endpoint\n");
+ return dev_err_probe(dev, -EPROBE_DEFER,
+ "waiting for fwnode graph endpoint\n");
+
+ ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency",
+ &mclk);
+ if (ret) {
+ fwnode_handle_put(ep_fwnode);
+ return dev_err_probe(dev, ret,
+ "reading clock-frequency property\n");
+ }
+
+ if (mclk != GC0310_MCLK_FREQ) {
+ fwnode_handle_put(ep_fwnode);
+ return dev_err_probe(dev, -EINVAL,
+ "external clock %u is not supported\n",
+ mclk);
+ }
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep_fwnode, &bus_cfg);
fwnode_handle_put(ep_fwnode);
+ if (ret)
+ return dev_err_probe(dev, ret, "parsing endpoint failed\n");
+
+ ret = v4l2_link_freq_to_bitmap(dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ link_freq_menu_items,
+ ARRAY_SIZE(link_freq_menu_items),
+ &link_freq_bitmap);
+
+ if (ret == 0 && bus_cfg.bus.mipi_csi2.num_data_lanes != 1)
+ ret = dev_err_probe(dev, -EINVAL,
+ "number of CSI2 data lanes %u is not supported\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
+
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+ return ret;
+}
+
+static int gc0310_probe(struct i2c_client *client)
+{
+ struct gc0310_device *sensor;
+ int ret;
- dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
- if (!dev)
+ ret = gc0310_check_hwcfg(&client->dev);
+ if (ret)
+ return ret;
+
+ sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
+ if (!sensor)
return -ENOMEM;
- dev->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(dev->reset)) {
- return dev_err_probe(&client->dev, PTR_ERR(dev->reset),
+ sensor->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->reset)) {
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->reset),
"getting reset GPIO\n");
}
- dev->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH);
- if (IS_ERR(dev->powerdown)) {
- return dev_err_probe(&client->dev, PTR_ERR(dev->powerdown),
+ sensor->powerdown = devm_gpiod_get(&client->dev, "powerdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(sensor->powerdown)) {
+ return dev_err_probe(&client->dev, PTR_ERR(sensor->powerdown),
"getting powerdown GPIO\n");
}
- mutex_init(&dev->input_lock);
- v4l2_i2c_subdev_init(&dev->sd, client, &gc0310_ops);
- gc0310_fill_format(&dev->mode.fmt);
+ v4l2_i2c_subdev_init(&sensor->sd, client, &gc0310_ops);
- pm_runtime_set_suspended(&client->dev);
- pm_runtime_enable(&client->dev);
- pm_runtime_set_autosuspend_delay(&client->dev, 1000);
- pm_runtime_use_autosuspend(&client->dev);
+ sensor->regmap = devm_cci_regmap_init_i2c(client, 8);
+ if (IS_ERR(sensor->regmap))
+ return PTR_ERR(sensor->regmap);
- ret = gc0310_detect(client);
- if (ret) {
- gc0310_remove(client);
- return ret;
- }
+ gc0310_power_on(&client->dev);
- dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- dev->pad.flags = MEDIA_PAD_FL_SOURCE;
- dev->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
-
- ret = gc0310_init_controls(dev);
- if (ret) {
- gc0310_remove(client);
- return ret;
- }
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_get_noresume(&client->dev);
+ pm_runtime_enable(&client->dev);
- ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
- if (ret) {
- gc0310_remove(client);
- return ret;
- }
+ ret = gc0310_detect(sensor);
+ if (ret)
+ goto err_power_down;
- ret = v4l2_async_register_subdev_sensor(&dev->sd);
- if (ret) {
- gc0310_remove(client);
- return ret;
- }
+ sensor->sd.internal_ops = &gc0310_internal_ops;
+ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
- return 0;
-}
+ ret = gc0310_init_controls(sensor);
+ if (ret)
+ goto err_power_down;
-static int gc0310_suspend(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd);
+ ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
+ if (ret)
+ goto err_power_down;
- gpiod_set_value_cansleep(gc0310_dev->powerdown, 1);
- gpiod_set_value_cansleep(gc0310_dev->reset, 1);
- return 0;
-}
+ sensor->sd.state_lock = sensor->ctrls.handler.lock;
+ ret = v4l2_subdev_init_finalize(&sensor->sd);
+ if (ret)
+ goto err_power_down;
-static int gc0310_resume(struct device *dev)
-{
- struct v4l2_subdev *sd = dev_get_drvdata(dev);
- struct gc0310_device *gc0310_dev = to_gc0310_sensor(sd);
+ ret = v4l2_async_register_subdev_sensor(&sensor->sd);
+ if (ret)
+ goto err_power_down;
- usleep_range(10000, 15000);
- gpiod_set_value_cansleep(gc0310_dev->reset, 0);
- usleep_range(10000, 15000);
- gpiod_set_value_cansleep(gc0310_dev->powerdown, 0);
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_put_autosuspend(&client->dev);
return 0;
+
+err_power_down:
+ pm_runtime_put_noidle(&client->dev);
+ gc0310_remove(client);
+ return ret;
}
-static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops, gc0310_suspend, gc0310_resume, NULL);
+static DEFINE_RUNTIME_DEV_PM_OPS(gc0310_pm_ops,
+ gc0310_power_off, gc0310_power_on, NULL);
static const struct acpi_device_id gc0310_acpi_match[] = {
{"INT0310"},
@@ -708,5 +778,6 @@ static struct i2c_driver gc0310_driver = {
module_i2c_driver(gc0310_driver);
MODULE_AUTHOR("Lai, Angie <angie.lai@intel.com>");
+MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
MODULE_DESCRIPTION("A low-level driver for GalaxyCore GC0310 sensors");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
index 857d7175942c..6fc39ab95e46 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
@@ -779,8 +779,6 @@ static void gc2235_remove(struct i2c_client *client)
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct gc2235_device *dev = to_gc2235_sensor(sd);
- dev_dbg(&client->dev, "gc2235_remove...\n");
-
dev->platform_data->csi_cfg(sd, 0);
v4l2_device_unregister_subdev(sd);
diff --git a/drivers/staging/media/atomisp/i2c/gc2235.h b/drivers/staging/media/atomisp/i2c/gc2235.h
index 6c743a17f198..7dd9a676fb98 100644
--- a/drivers/staging/media/atomisp/i2c/gc2235.h
+++ b/drivers/staging/media/atomisp/i2c/gc2235.h
@@ -179,21 +179,21 @@ struct gc2235_write_ctrl {
struct gc2235_write_buffer buffer;
};
-static struct gc2235_reg const gc2235_stream_on[] = {
+static const struct gc2235_reg gc2235_stream_on[] = {
{ GC2235_8BIT, 0xfe, 0x03}, /* switch to P3 */
{ GC2235_8BIT, 0x10, 0x91}, /* start mipi */
{ GC2235_8BIT, 0xfe, 0x00}, /* switch to P0 */
{ GC2235_TOK_TERM, 0, 0 }
};
-static struct gc2235_reg const gc2235_stream_off[] = {
+static const struct gc2235_reg gc2235_stream_off[] = {
{ GC2235_8BIT, 0xfe, 0x03}, /* switch to P3 */
{ GC2235_8BIT, 0x10, 0x01}, /* stop mipi */
{ GC2235_8BIT, 0xfe, 0x00}, /* switch to P0 */
{ GC2235_TOK_TERM, 0, 0 }
};
-static struct gc2235_reg const gc2235_init_settings[] = {
+static const struct gc2235_reg gc2235_init_settings[] = {
/* System */
{ GC2235_8BIT, 0xfe, 0x80 },
{ GC2235_8BIT, 0xfe, 0x80 },
@@ -268,7 +268,7 @@ static struct gc2235_reg const gc2235_init_settings[] = {
* Register settings for various resolution
*/
#if ENABLE_NON_PREVIEW
-static struct gc2235_reg const gc2235_1296_736_30fps[] = {
+static const struct gc2235_reg gc2235_1296_736_30fps[] = {
{ GC2235_8BIT, 0x8b, 0xa0 },
{ GC2235_8BIT, 0x8c, 0x02 },
@@ -321,7 +321,7 @@ static struct gc2235_reg const gc2235_1296_736_30fps[] = {
{ GC2235_TOK_TERM, 0, 0 }
};
-static struct gc2235_reg const gc2235_960_640_30fps[] = {
+static const struct gc2235_reg gc2235_960_640_30fps[] = {
{ GC2235_8BIT, 0x8b, 0xa0 },
{ GC2235_8BIT, 0x8c, 0x02 },
@@ -373,7 +373,7 @@ static struct gc2235_reg const gc2235_960_640_30fps[] = {
};
#endif
-static struct gc2235_reg const gc2235_1600_900_30fps[] = {
+static const struct gc2235_reg gc2235_1600_900_30fps[] = {
{ GC2235_8BIT, 0x8b, 0xa0 },
{ GC2235_8BIT, 0x8c, 0x02 },
@@ -418,7 +418,7 @@ static struct gc2235_reg const gc2235_1600_900_30fps[] = {
{ GC2235_TOK_TERM, 0, 0 }
};
-static struct gc2235_reg const gc2235_1616_1082_30fps[] = {
+static const struct gc2235_reg gc2235_1616_1082_30fps[] = {
{ GC2235_8BIT, 0x8b, 0xa0 },
{ GC2235_8BIT, 0x8c, 0x02 },
@@ -463,7 +463,7 @@ static struct gc2235_reg const gc2235_1616_1082_30fps[] = {
{ GC2235_TOK_TERM, 0, 0 }
};
-static struct gc2235_reg const gc2235_1616_1216_30fps[] = {
+static const struct gc2235_reg gc2235_1616_1216_30fps[] = {
{ GC2235_8BIT, 0x8b, 0xa0 },
{ GC2235_8BIT, 0x8c, 0x02 },
diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h
index bc36133f3722..00317d105305 100644
--- a/drivers/staging/media/atomisp/i2c/ov2722.h
+++ b/drivers/staging/media/atomisp/i2c/ov2722.h
@@ -236,7 +236,7 @@ struct ov2722_write_ctrl {
* Register settings for various resolution
*/
#if 0
-static struct ov2722_reg const ov2722_QVGA_30fps[] = {
+static const struct ov2722_reg ov2722_QVGA_30fps[] = {
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x0c},
{OV2722_8BIT, 0x373a, 0x1c},
@@ -346,7 +346,7 @@ static struct ov2722_reg const ov2722_QVGA_30fps[] = {
};
-static struct ov2722_reg const ov2722_480P_30fps[] = {
+static const struct ov2722_reg ov2722_480P_30fps[] = {
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x18},
{OV2722_8BIT, 0x373a, 0x3c},
@@ -455,7 +455,7 @@ static struct ov2722_reg const ov2722_480P_30fps[] = {
{OV2722_TOK_TERM, 0, 0},
};
-static struct ov2722_reg const ov2722_VGA_30fps[] = {
+static const struct ov2722_reg ov2722_VGA_30fps[] = {
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x18},
{OV2722_8BIT, 0x373a, 0x3c},
@@ -565,7 +565,7 @@ static struct ov2722_reg const ov2722_VGA_30fps[] = {
};
#endif
-static struct ov2722_reg const ov2722_1632_1092_30fps[] = {
+static const struct ov2722_reg ov2722_1632_1092_30fps[] = {
{OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for
a whole frame complete.(vblank) */
{OV2722_8BIT, 0x3718, 0x10},
@@ -667,7 +667,7 @@ static struct ov2722_reg const ov2722_1632_1092_30fps[] = {
{OV2722_TOK_TERM, 0, 0}
};
-static struct ov2722_reg const ov2722_1452_1092_30fps[] = {
+static const struct ov2722_reg ov2722_1452_1092_30fps[] = {
{OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for
a whole frame complete.(vblank) */
{OV2722_8BIT, 0x3718, 0x10},
@@ -769,7 +769,7 @@ static struct ov2722_reg const ov2722_1452_1092_30fps[] = {
};
#if 0
-static struct ov2722_reg const ov2722_1M3_30fps[] = {
+static const struct ov2722_reg ov2722_1M3_30fps[] = {
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x24},
{OV2722_8BIT, 0x373a, 0x60},
@@ -877,7 +877,7 @@ static struct ov2722_reg const ov2722_1M3_30fps[] = {
};
#endif
-static struct ov2722_reg const ov2722_1080p_30fps[] = {
+static const struct ov2722_reg ov2722_1080p_30fps[] = {
{OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole
frame complete.(vblank) */
{OV2722_8BIT, 0x3718, 0x10},
@@ -983,7 +983,7 @@ static struct ov2722_reg const ov2722_1080p_30fps[] = {
};
#if 0 /* Currently unused */
-static struct ov2722_reg const ov2722_720p_30fps[] = {
+static const struct ov2722_reg ov2722_720p_30fps[] = {
{OV2722_8BIT, 0x3021, 0x03},
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x24},
diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
index bc97fa2c374c..be5f37f4a6fd 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
@@ -387,7 +387,7 @@ static int __destroy_stream(struct atomisp_sub_device *asd,
}
if (stream_env->stream_state == CSS_STREAM_STARTED) {
- timeout = jiffies + msecs_to_jiffies(40);
+ timeout = jiffies + msecs_to_jiffies(200);
while (1) {
if (ia_css_stream_has_stopped(stream_env->stream))
break;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.h b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
index bb998c82a438..ec762f8fb922 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.h
@@ -19,28 +19,11 @@
#define CSI2_PAD_SOURCE 1
#define CSI2_PADS_NUM 2
-#define CSI2_MAX_ACPI_GPIOS 2u
-
-struct acpi_device;
struct v4l2_device;
struct atomisp_device;
struct atomisp_sub_device;
-struct atomisp_csi2_acpi_gpio_map {
- struct acpi_gpio_params params[CSI2_MAX_ACPI_GPIOS];
- struct acpi_gpio_mapping mapping[CSI2_MAX_ACPI_GPIOS + 1];
-};
-
-struct atomisp_csi2_acpi_gpio_parsing_data {
- struct acpi_device *adev;
- struct atomisp_csi2_acpi_gpio_map *map;
- u32 settings[CSI2_MAX_ACPI_GPIOS];
- unsigned int settings_count;
- unsigned int res_count;
- unsigned int map_count;
-};
-
struct atomisp_mipi_csi2_device {
struct v4l2_subdev subdev;
struct media_pad pads[CSI2_PADS_NUM];
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index 6abda358a72f..2a90f86e515f 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -13,6 +13,7 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dmi.h>
+#include <linux/platform_data/x86/int3472.h>
#include <linux/property.h>
#include <media/ipu-bridge.h>
@@ -25,39 +26,6 @@
#define PMC_CLK_RATE_19_2MHZ 19200000
/*
- * 79234640-9e10-4fea-a5c1-b5aa8b19756f
- * This _DSM GUID returns information about the GPIO lines mapped to a sensor.
- * Function number 1 returns a count of the GPIO lines that are mapped.
- * Subsequent functions return 32 bit ints encoding information about the GPIO.
- */
-static const guid_t intel_sensor_gpio_info_guid =
- GUID_INIT(0x79234640, 0x9e10, 0x4fea,
- 0xa5, 0xc1, 0xb5, 0xaa, 0x8b, 0x19, 0x75, 0x6f);
-
-#define INTEL_GPIO_DSM_TYPE_SHIFT 0
-#define INTEL_GPIO_DSM_TYPE_MASK GENMASK(7, 0)
-#define INTEL_GPIO_DSM_PIN_SHIFT 8
-#define INTEL_GPIO_DSM_PIN_MASK GENMASK(15, 8)
-#define INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT 24
-#define INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK GENMASK(31, 24)
-
-#define INTEL_GPIO_DSM_TYPE(x) \
- (((x) & INTEL_GPIO_DSM_TYPE_MASK) >> INTEL_GPIO_DSM_TYPE_SHIFT)
-#define INTEL_GPIO_DSM_PIN(x) \
- (((x) & INTEL_GPIO_DSM_PIN_MASK) >> INTEL_GPIO_DSM_PIN_SHIFT)
-#define INTEL_GPIO_DSM_SENSOR_ON_VAL(x) \
- (((x) & INTEL_GPIO_DSM_SENSOR_ON_VAL_MASK) >> INTEL_GPIO_DSM_SENSOR_ON_VAL_SHIFT)
-
-/*
- * 822ace8f-2814-4174-a56b-5f029fe079ee
- * This _DSM GUID returns a string from the sensor device, which acts as a
- * module identifier.
- */
-static const guid_t intel_sensor_module_guid =
- GUID_INIT(0x822ace8f, 0x2814, 0x4174,
- 0xa5, 0x6b, 0x5f, 0x02, 0x9f, 0xe0, 0x79, 0xee);
-
-/*
* dc2f6c4f-045b-4f1d-97b9-882a6860a4be
* This _DSM GUID returns a package with n*2 strings, with each set of 2 strings
* forming a key, value pair for settings like e.g. "CsiLanes" = "1".
@@ -323,196 +291,45 @@ static int atomisp_csi2_get_port(struct acpi_device *adev, int clock_num)
return gmin_cfg_get_int(adev, "CsiPort", port);
}
-/* Note this always returns 1 to continue looping so that res_count is accurate */
-static int atomisp_csi2_handle_acpi_gpio_res(struct acpi_resource *ares, void *_data)
-{
- struct atomisp_csi2_acpi_gpio_parsing_data *data = _data;
- struct acpi_resource_gpio *agpio;
- const char *name;
- bool active_low;
- unsigned int i;
- u32 settings = 0;
- u16 pin;
-
- if (!acpi_gpio_get_io_resource(ares, &agpio))
- return 1; /* Not a GPIO, continue the loop */
-
- data->res_count++;
-
- pin = agpio->pin_table[0];
- for (i = 0; i < data->settings_count; i++) {
- if (INTEL_GPIO_DSM_PIN(data->settings[i]) == pin) {
- settings = data->settings[i];
- break;
- }
- }
-
- if (i == data->settings_count) {
- acpi_handle_warn(data->adev->handle,
- "%s: Could not find DSM GPIO settings for pin %u\n",
- dev_name(&data->adev->dev), pin);
- return 1;
- }
-
- switch (INTEL_GPIO_DSM_TYPE(settings)) {
- case 0:
- name = "reset-gpios";
- break;
- case 1:
- name = "powerdown-gpios";
- break;
- default:
- acpi_handle_warn(data->adev->handle, "%s: Unknown GPIO type 0x%02lx for pin %u\n",
- dev_name(&data->adev->dev),
- INTEL_GPIO_DSM_TYPE(settings), pin);
- return 1;
- }
-
- /*
- * Both reset and power-down need to be logical false when the sensor
- * is on (sensor should not be in reset and not be powered-down). So
- * when the sensor-on-value (which is the physical pin value) is high,
- * then the signal is active-low.
- */
- active_low = INTEL_GPIO_DSM_SENSOR_ON_VAL(settings);
-
- i = data->map_count;
- if (i == CSI2_MAX_ACPI_GPIOS)
- return 1;
-
- /* res_count is already incremented */
- data->map->params[i].crs_entry_index = data->res_count - 1;
- data->map->params[i].active_low = active_low;
- data->map->mapping[i].name = name;
- data->map->mapping[i].data = &data->map->params[i];
- data->map->mapping[i].size = 1;
- data->map_count++;
-
- acpi_handle_info(data->adev->handle, "%s: %s crs %d %s pin %u active-%s\n",
- dev_name(&data->adev->dev), name,
- data->res_count - 1, agpio->resource_source.string_ptr,
- pin, active_low ? "low" : "high");
-
- return 1;
-}
-
/*
- * Helper function to create an ACPI GPIO lookup table for sensor reset and
- * powerdown signals on Intel Bay Trail (BYT) and Cherry Trail (CHT) devices,
- * including setting the correct polarity for the GPIO.
- *
- * This uses the "79234640-9e10-4fea-a5c1-b5aa8b19756f" DSM method directly
- * on the sensor device's ACPI node. This is different from later Intel
- * hardware which has a separate INT3472 acpi_device with this info.
- *
- * This function must be called before creating the sw-noded describing
- * the fwnode graph endpoint. And sensor drivers used on these devices
- * must return -EPROBE_DEFER when there is no endpoint description yet.
- * Together this guarantees that the GPIO lookups are in place before
- * the sensor driver tries to get GPIOs with gpiod_get().
+ * Alloc and fill an int3472_discrete_device struct so that we can re-use
+ * the INT3472 sensor GPIO mapping code.
*
- * Note this code uses the same DSM GUID as the int3472_gpio_guid in
- * the INT3472 discrete.c code and there is some overlap, but there are
- * enough differences that it is difficult to share the code.
+ * This gets called from ipu_bridge_init() which runs only once per boot,
+ * the created faux int3472 device is leaked on purpose to keep the created
+ * GPIO mappings around permanently.
*/
static int atomisp_csi2_add_gpio_mappings(struct acpi_device *adev)
{
- struct atomisp_csi2_acpi_gpio_parsing_data data = { };
- LIST_HEAD(resource_list);
- union acpi_object *obj;
- unsigned int i, j;
+ struct int3472_discrete_device *int3472;
int ret;
- obj = acpi_evaluate_dsm_typed(adev->handle, &intel_sensor_module_guid,
- 0x00, 1, NULL, ACPI_TYPE_STRING);
- if (obj) {
- acpi_handle_info(adev->handle, "%s: Sensor module id: '%s'\n",
- dev_name(&adev->dev), obj->string.pointer);
- ACPI_FREE(obj);
- }
+ /* Max num GPIOs we've seen plus a terminator */
+ int3472 = kzalloc(struct_size(int3472, gpios.table, INT3472_MAX_SENSOR_GPIOS + 1),
+ GFP_KERNEL);
+ if (!int3472)
+ return -ENOMEM;
/*
- * First get the GPIO-settings count and then get count GPIO-settings
- * values. Note the order of these may differ from the order in which
- * the GPIOs are listed on the ACPI resources! So we first store them all
- * and then enumerate the ACPI resources and match them up by pin number.
+ * On atomisp the _DSM to get the GPIO type must be made on the sensor
+ * adev, rather than on a separate INT3472 adev.
*/
- obj = acpi_evaluate_dsm_typed(adev->handle,
- &intel_sensor_gpio_info_guid, 0x00, 1,
- NULL, ACPI_TYPE_INTEGER);
- if (!obj) {
- acpi_handle_err(adev->handle, "%s: No _DSM entry for GPIO pin count\n",
- dev_name(&adev->dev));
- return -EIO;
- }
-
- data.settings_count = obj->integer.value;
- ACPI_FREE(obj);
+ int3472->adev = adev;
+ int3472->dev = &adev->dev;
+ int3472->sensor = adev;
- if (data.settings_count > CSI2_MAX_ACPI_GPIOS) {
- acpi_handle_err(adev->handle, "%s: Too many GPIOs %u > %u\n",
- dev_name(&adev->dev), data.settings_count,
- CSI2_MAX_ACPI_GPIOS);
- return -EOVERFLOW;
+ int3472->sensor_name = kasprintf(GFP_KERNEL, I2C_DEV_NAME_FORMAT, acpi_dev_name(adev));
+ if (!int3472->sensor_name) {
+ kfree(int3472);
+ return -ENOMEM;
}
- for (i = 0; i < data.settings_count; i++) {
- /*
- * i + 2 because the index of this _DSM function is 1-based
- * and the first function is just a count.
- */
- obj = acpi_evaluate_dsm_typed(adev->handle,
- &intel_sensor_gpio_info_guid,
- 0x00, i + 2,
- NULL, ACPI_TYPE_INTEGER);
- if (!obj) {
- acpi_handle_err(adev->handle, "%s: No _DSM entry for pin %u\n",
- dev_name(&adev->dev), i);
- return -EIO;
- }
-
- data.settings[i] = obj->integer.value;
- ACPI_FREE(obj);
+ ret = int3472_discrete_parse_crs(int3472);
+ if (ret) {
+ int3472_discrete_cleanup(int3472);
+ kfree(int3472);
}
- /* Since we match up by pin-number the pin-numbers must be unique */
- for (i = 0; i < data.settings_count; i++) {
- for (j = i + 1; j < data.settings_count; j++) {
- if (INTEL_GPIO_DSM_PIN(data.settings[i]) !=
- INTEL_GPIO_DSM_PIN(data.settings[j]))
- continue;
-
- acpi_handle_err(adev->handle, "%s: Duplicate pin number %lu\n",
- dev_name(&adev->dev),
- INTEL_GPIO_DSM_PIN(data.settings[i]));
- return -EIO;
- }
- }
-
- data.map = kzalloc(sizeof(*data.map), GFP_KERNEL);
- if (!data.map)
- return -ENOMEM;
-
- /* Now parse the ACPI resources and build the lookup table */
- data.adev = adev;
- ret = acpi_dev_get_resources(adev, &resource_list,
- atomisp_csi2_handle_acpi_gpio_res, &data);
- if (ret < 0)
- return ret;
-
- acpi_dev_free_resource_list(&resource_list);
-
- if (data.map_count != data.settings_count ||
- data.res_count != data.settings_count)
- acpi_handle_warn(adev->handle, "%s: ACPI GPIO resources vs DSM GPIO-info count mismatch (dsm: %d res: %d map %d\n",
- dev_name(&adev->dev), data.settings_count,
- data.res_count, data.map_count);
-
- ret = acpi_dev_add_driver_gpios(adev, data.map->mapping);
- if (ret)
- acpi_handle_err(adev->handle, "%s: Error adding driver GPIOs: %d\n",
- dev_name(&adev->dev), ret);
-
return ret;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_drvfs.c b/drivers/staging/media/atomisp/pci/atomisp_drvfs.c
deleted file mode 100644
index 31c82c3c0d33..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp_drvfs.c
+++ /dev/null
@@ -1,155 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Support for atomisp driver sysfs interface
- *
- * Copyright (c) 2014 Intel Corporation. All Rights Reserved.
- */
-
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/kernel.h>
-
-#include "atomisp_compat.h"
-#include "atomisp_internal.h"
-#include "atomisp_ioctl.h"
-#include "atomisp_drvfs.h"
-#include "hmm/hmm.h"
-#include "ia_css_debug.h"
-
-#define OPTION_BIN_LIST BIT(0)
-#define OPTION_BIN_RUN BIT(1)
-#define OPTION_VALID (OPTION_BIN_LIST | OPTION_BIN_RUN)
-
-/*
- * dbgopt: iunit debug option:
- * bit 0: binary list
- * bit 1: running binary
- * bit 2: memory statistic
- */
-static unsigned int dbgopt = OPTION_BIN_LIST;
-
-static inline int iunit_dump_dbgopt(struct atomisp_device *isp,
- unsigned int opt)
-{
- int ret = 0;
-
- if (opt & OPTION_VALID) {
- if (opt & OPTION_BIN_LIST) {
- ret = atomisp_css_dump_blob_infor(isp);
- if (ret) {
- dev_err(isp->dev, "%s dump blob infor err[ret:%d]\n",
- __func__, ret);
- goto opt_err;
- }
- }
-
- if (opt & OPTION_BIN_RUN) {
- if (isp->asd.streaming) {
- atomisp_css_dump_sp_raw_copy_linecount(true);
- atomisp_css_debug_dump_isp_binary();
- } else {
- ret = -EPERM;
- dev_err(isp->dev, "%s dump running bin err[ret:%d]\n",
- __func__, ret);
- goto opt_err;
- }
- }
- } else {
- ret = -EINVAL;
- dev_err(isp->dev, "%s dump nothing[ret=%d]\n", __func__, ret);
- }
-
-opt_err:
- return ret;
-}
-
-static ssize_t dbglvl_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- unsigned int dbglvl = ia_css_debug_get_dtrace_level();
-
- return sysfs_emit(buf, "dtrace level:%u\n", dbglvl);
-}
-
-static ssize_t dbglvl_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
-{
- unsigned int dbglvl;
- int ret;
-
- ret = kstrtouint(buf, 10, &dbglvl);
- if (ret)
- return ret;
-
- if (dbglvl < 1 || dbglvl > 9)
- return -ERANGE;
-
- ia_css_debug_set_dtrace_level(dbglvl);
- return size;
-}
-static DEVICE_ATTR_RW(dbglvl);
-
-static ssize_t dbgfun_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- unsigned int dbgfun = atomisp_get_css_dbgfunc();
-
- return sysfs_emit(buf, "dbgfun opt:%u\n", dbgfun);
-}
-
-static ssize_t dbgfun_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct atomisp_device *isp = dev_get_drvdata(dev);
- unsigned int opt;
- int ret;
-
- ret = kstrtouint(buf, 10, &opt);
- if (ret)
- return ret;
-
- return atomisp_set_css_dbgfunc(isp, opt);
-}
-static DEVICE_ATTR_RW(dbgfun);
-
-static ssize_t dbgopt_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return sysfs_emit(buf, "option:0x%x\n", dbgopt);
-}
-
-static ssize_t dbgopt_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
-{
- struct atomisp_device *isp = dev_get_drvdata(dev);
- unsigned int opt;
- int ret;
-
- ret = kstrtouint(buf, 10, &opt);
- if (ret)
- return ret;
-
- dbgopt = opt;
- ret = iunit_dump_dbgopt(isp, dbgopt);
- if (ret)
- return ret;
-
- return size;
-}
-static DEVICE_ATTR_RW(dbgopt);
-
-static struct attribute *dbg_attrs[] = {
- &dev_attr_dbglvl.attr,
- &dev_attr_dbgfun.attr,
- &dev_attr_dbgopt.attr,
- NULL
-};
-
-static const struct attribute_group dbg_attr_group = {
- .attrs = dbg_attrs,
-};
-
-const struct attribute_group *dbg_attr_groups[] = {
- &dbg_attr_group,
- NULL
-};
diff --git a/drivers/staging/media/atomisp/pci/atomisp_drvfs.h b/drivers/staging/media/atomisp/pci/atomisp_drvfs.h
deleted file mode 100644
index 46ad59b8df28..000000000000
--- a/drivers/staging/media/atomisp/pci/atomisp_drvfs.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Support for atomisp driver sysfs interface.
- *
- * Copyright (c) 2014 Intel Corporation. All Rights Reserved.
- */
-
-#ifndef __ATOMISP_DRVFS_H__
-#define __ATOMISP_DRVFS_H__
-
-#include <linux/sysfs.h>
-
-extern const struct attribute_group *dbg_attr_groups[];
-
-#endif /* __ATOMISP_DRVFS_H__ */
diff --git a/drivers/staging/media/atomisp/pci/atomisp_fops.c b/drivers/staging/media/atomisp/pci/atomisp_fops.c
index 57da7ddb1503..c7aef066f209 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_fops.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_fops.c
@@ -234,9 +234,6 @@ static int atomisp_q_video_buffers_to_css(struct atomisp_sub_device *asd,
if (WARN_ON(css_pipe_id >= IA_CSS_PIPE_ID_NUM))
return -EINVAL;
- if (pipe->stopping)
- return -EINVAL;
-
space = ATOMISP_CSS_Q_DEPTH - atomisp_buffers_in_css(pipe);
while (space--) {
struct ia_css_frame *frame;
@@ -367,7 +364,7 @@ static void atomisp_buf_queue(struct vb2_buffer *vb)
mutex_lock(&asd->isp->mutex);
ret = atomisp_pipe_check(pipe, false);
- if (ret || pipe->stopping) {
+ if (ret) {
spin_lock_irqsave(&pipe->irq_lock, irqflags);
atomisp_buffer_done(frame, VB2_BUF_STATE_ERROR);
spin_unlock_irqrestore(&pipe->irq_lock, irqflags);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
index 97d99bed1560..bb8b2f2213b0 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_ioctl.c
@@ -856,6 +856,62 @@ static void atomisp_dma_burst_len_cfg(struct atomisp_sub_device *asd)
atomisp_css2_hw_store_32(DMA_BURST_SIZE_REG, 0x00);
}
+static void atomisp_stop_stream(struct atomisp_video_pipe *pipe,
+ bool stop_sensor, enum vb2_buffer_state state)
+{
+ struct atomisp_sub_device *asd = pipe->asd;
+ struct atomisp_device *isp = asd->isp;
+ struct pci_dev *pdev = to_pci_dev(isp->dev);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&isp->lock, flags);
+ asd->streaming = false;
+ spin_unlock_irqrestore(&isp->lock, flags);
+
+ atomisp_clear_css_buffer_counters(asd);
+ atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false);
+
+ atomisp_css_stop(asd, false);
+
+ atomisp_subdev_cleanup_pending_events(asd);
+
+ if (stop_sensor) {
+ ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source,
+ video, s_stream, 0);
+ if (ret)
+ dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret);
+ }
+
+ /* Disable the CSI interface on ANN B0/K0 */
+ if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 <<
+ ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_B0)) {
+ pci_write_config_word(pdev, MRFLD_PCI_CSI_CONTROL,
+ isp->saved_regs.csi_control & ~MRFLD_PCI_CSI_CONTROL_CSI_READY);
+ }
+
+ if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, false))
+ dev_warn(isp->dev, "DFS failed.\n");
+
+ /*
+ * ISP work around, need to reset ISP to allow next stream on to work.
+ * Streams have already been destroyed by atomisp_css_stop().
+ * Disable PUNIT/ISP acknowledge/handshake - SRSE=3 and then reset.
+ */
+ pci_write_config_dword(pdev, PCI_I_CONTROL,
+ isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK);
+ atomisp_reset(isp);
+
+ atomisp_flush_video_pipe(pipe, state, false);
+
+ /* Streams were destroyed by atomisp_css_stop(), recreate them. */
+ ret = atomisp_create_pipes_stream(&isp->asd);
+ if (ret)
+ dev_warn(isp->dev, "Recreating streams failed: %d\n", ret);
+
+ media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]);
+}
+
int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct atomisp_video_pipe *pipe = vq_to_pipe(vq);
@@ -910,6 +966,7 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count)
ret = atomisp_css_start(asd);
if (ret) {
atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_QUEUED, true);
+ media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]);
goto out_unlock;
}
@@ -949,11 +1006,7 @@ int atomisp_start_streaming(struct vb2_queue *vq, unsigned int count)
video, s_stream, 1);
if (ret) {
dev_err(isp->dev, "Starting sensor stream failed: %d\n", ret);
- spin_lock_irqsave(&isp->lock, irqflags);
- asd->streaming = false;
- spin_unlock_irqrestore(&isp->lock, irqflags);
- ret = -EINVAL;
- goto out_unlock;
+ atomisp_stop_stream(pipe, false, VB2_BUF_STATE_QUEUED);
}
out_unlock:
@@ -964,74 +1017,12 @@ out_unlock:
void atomisp_stop_streaming(struct vb2_queue *vq)
{
struct atomisp_video_pipe *pipe = vq_to_pipe(vq);
- struct atomisp_sub_device *asd = pipe->asd;
- struct atomisp_device *isp = asd->isp;
- struct pci_dev *pdev = to_pci_dev(isp->dev);
- unsigned long flags;
- int ret;
+ struct atomisp_device *isp = pipe->asd->isp;
dev_dbg(isp->dev, "Stop stream\n");
mutex_lock(&isp->mutex);
- /*
- * There is no guarantee that the buffers queued to / owned by the ISP
- * will properly be returned to the queue when stopping. Set a flag to
- * avoid new buffers getting queued and then wait for all the current
- * buffers to finish.
- */
- pipe->stopping = true;
- mutex_unlock(&isp->mutex);
- /* wait max 1 second */
- ret = wait_event_timeout(pipe->vb_queue.done_wq,
- atomisp_buffers_in_css(pipe) == 0, HZ);
- mutex_lock(&isp->mutex);
- pipe->stopping = false;
- if (ret == 0)
- dev_warn(isp->dev, "Warning timeout waiting for CSS to return buffers\n");
-
- spin_lock_irqsave(&isp->lock, flags);
- asd->streaming = false;
- spin_unlock_irqrestore(&isp->lock, flags);
-
- atomisp_clear_css_buffer_counters(asd);
- atomisp_css_irq_enable(isp, IA_CSS_IRQ_INFO_CSS_RECEIVER_SOF, false);
-
- atomisp_css_stop(asd, false);
-
- atomisp_flush_video_pipe(pipe, VB2_BUF_STATE_ERROR, true);
-
- atomisp_subdev_cleanup_pending_events(asd);
-
- ret = v4l2_subdev_call(isp->inputs[asd->input_curr].csi_remote_source,
- video, s_stream, 0);
- if (ret)
- dev_warn(isp->dev, "Stopping sensor stream failed: %d\n", ret);
-
- /* Disable the CSI interface on ANN B0/K0 */
- if (isp->media_dev.hw_revision >= ((ATOMISP_HW_REVISION_ISP2401 <<
- ATOMISP_HW_REVISION_SHIFT) | ATOMISP_HW_STEPPING_B0)) {
- pci_write_config_word(pdev, MRFLD_PCI_CSI_CONTROL,
- isp->saved_regs.csi_control & ~MRFLD_PCI_CSI_CONTROL_CSI_READY);
- }
-
- if (atomisp_freq_scaling(isp, ATOMISP_DFS_MODE_LOW, false))
- dev_warn(isp->dev, "DFS failed.\n");
-
- /*
- * ISP work around, need to reset ISP to allow next stream on to work.
- * Streams have already been destroyed by atomisp_css_stop().
- * Disable PUNIT/ISP acknowledge/handshake - SRSE=3 and then reset.
- */
- pci_write_config_dword(pdev, PCI_I_CONTROL,
- isp->saved_regs.i_control | MRFLD_PCI_I_CONTROL_SRSE_RESET_MASK);
- atomisp_reset(isp);
-
- /* Streams were destroyed by atomisp_css_stop(), recreate them. */
- ret = atomisp_create_pipes_stream(&isp->asd);
- if (ret)
- dev_warn(isp->dev, "Recreating streams failed: %d\n", ret);
-
- media_pipeline_stop(&asd->video_out.vdev.entity.pads[0]);
+ atomisp_stop_stream(pipe, true, VB2_BUF_STATE_ERROR);
mutex_unlock(&isp->mutex);
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
index bd1a198cda30..e1d0168cb91d 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
@@ -57,9 +57,6 @@ struct atomisp_video_pipe {
/* Filled through atomisp_get_css_frame_info() on queue setup */
struct ia_css_frame_info frame_info;
- /* Set from streamoff to disallow queuing further buffers in CSS */
- bool stopping;
-
/*
* irq_lock is used to protect video buffer state change operations and
* also to make activeq and capq operations atomic.
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 0bd0bfded4af..900a67552d6a 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -29,7 +29,6 @@
#include "atomisp_internal.h"
#include "atomisp-regs.h"
#include "atomisp_dfs_tables.h"
-#include "atomisp_drvfs.h"
#include "hmm/hmm.h"
#include "atomisp_trace_event.h"
@@ -1497,9 +1496,6 @@ static const struct pci_device_id atomisp_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, atomisp_pci_tbl);
static struct pci_driver atomisp_pci_driver = {
- .driver = {
- .dev_groups = dbg_attr_groups,
- },
.name = "atomisp-isp2",
.id_table = atomisp_pci_tbl,
.probe = atomisp_pci_probe,
@@ -1513,3 +1509,4 @@ MODULE_AUTHOR("Xiaolin Zhang <xiaolin.zhang@intel.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Intel ATOM Platform ISP Driver");
MODULE_IMPORT_NS("INTEL_IPU_BRIDGE");
+MODULE_IMPORT_NS("INTEL_INT3472_DISCRETE");
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h
index 6d45d0d8d060..2cb5c986790a 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h
@@ -10,14 +10,9 @@
/* Override the definition of max/min from Linux kernel */
#include <linux/minmax.h>
-/* force a value to a lower even value */
-#define EVEN_FLOOR(x) ((x) & ~1)
-
#define CEIL_DIV(a, b) (((b) != 0) ? ((a) + (b) - 1) / (b) : 0)
#define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b))
-#define CEIL_MUL2(a, b) (((a) + (b) - 1) & ~((b) - 1))
#define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b))
-#define CEIL_SHIFT_MUL(a, b) (CEIL_SHIFT(a, b) << (b))
/*
* For SP and ISP, SDK provides the definition of OP_std_modadd.
diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm.c b/drivers/staging/media/atomisp/pci/hmm/hmm.c
index 84102c3aaf97..f998b57f90c4 100644
--- a/drivers/staging/media/atomisp/pci/hmm/hmm.c
+++ b/drivers/staging/media/atomisp/pci/hmm/hmm.c
@@ -28,88 +28,6 @@ struct hmm_bo_device bo_device;
static ia_css_ptr dummy_ptr = mmgr_EXCEPTION;
static bool hmm_initialized;
-/*
- * p: private
- * v: vmalloc
- */
-static const char hmm_bo_type_string[] = "pv";
-
-static ssize_t bo_show(struct device *dev, struct device_attribute *attr,
- char *buf, struct list_head *bo_list, bool active)
-{
- ssize_t ret = 0;
- struct hmm_buffer_object *bo;
- unsigned long flags;
- int i;
- long total[HMM_BO_LAST] = { 0 };
- long count[HMM_BO_LAST] = { 0 };
- int index1 = 0;
- int index2 = 0;
-
- ret = scnprintf(buf, PAGE_SIZE, "type pgnr\n");
- if (ret <= 0)
- return 0;
-
- index1 += ret;
-
- spin_lock_irqsave(&bo_device.list_lock, flags);
- list_for_each_entry(bo, bo_list, list) {
- if ((active && (bo->status & HMM_BO_ALLOCED)) ||
- (!active && !(bo->status & HMM_BO_ALLOCED))) {
- ret = scnprintf(buf + index1, PAGE_SIZE - index1,
- "%c %d\n",
- hmm_bo_type_string[bo->type], bo->pgnr);
-
- total[bo->type] += bo->pgnr;
- count[bo->type]++;
- if (ret > 0)
- index1 += ret;
- }
- }
- spin_unlock_irqrestore(&bo_device.list_lock, flags);
-
- for (i = 0; i < HMM_BO_LAST; i++) {
- if (count[i]) {
- ret = scnprintf(buf + index1 + index2,
- PAGE_SIZE - index1 - index2,
- "%ld %c buffer objects: %ld KB\n",
- count[i], hmm_bo_type_string[i],
- total[i] * 4);
- if (ret > 0)
- index2 += ret;
- }
- }
-
- /* Add trailing zero, not included by scnprintf */
- return index1 + index2 + 1;
-}
-
-static ssize_t active_bo_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return bo_show(dev, attr, buf, &bo_device.entire_bo_list, true);
-}
-
-static ssize_t free_bo_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- return bo_show(dev, attr, buf, &bo_device.entire_bo_list, false);
-}
-
-
-static DEVICE_ATTR_RO(active_bo);
-static DEVICE_ATTR_RO(free_bo);
-
-static struct attribute *sysfs_attrs_ctrl[] = {
- &dev_attr_active_bo.attr,
- &dev_attr_free_bo.attr,
- NULL
-};
-
-static struct attribute_group atomisp_attribute_group[] = {
- {.attrs = sysfs_attrs_ctrl },
-};
-
int hmm_init(void)
{
int ret;
@@ -130,14 +48,6 @@ int hmm_init(void)
*/
dummy_ptr = hmm_alloc(1);
- if (!ret) {
- ret = sysfs_create_group(&atomisp_dev->kobj,
- atomisp_attribute_group);
- if (ret)
- dev_err(atomisp_dev,
- "%s Failed to create sysfs\n", __func__);
- }
-
return ret;
}
@@ -145,7 +55,6 @@ void hmm_cleanup(void)
{
if (dummy_ptr == mmgr_EXCEPTION)
return;
- sysfs_remove_group(&atomisp_dev->kobj, atomisp_attribute_group);
/* free dummy memory first */
hmm_free(dummy_ptr);
diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
index 224ca8d42721..5d0cd5260d3a 100644
--- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
+++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
@@ -37,8 +37,6 @@ static int __bo_init(struct hmm_bo_device *bdev, struct hmm_buffer_object *bo,
unsigned int pgnr)
{
check_bodev_null_return(bdev, -EINVAL);
- var_equal_return(hmm_bo_device_inited(bdev), 0, -EINVAL,
- "hmm_bo_device not inited yet.\n");
/* prevent zero size buffer object */
if (pgnr == 0) {
dev_err(atomisp_dev, "0 size buffer is not allowed.\n");
@@ -341,7 +339,6 @@ int hmm_bo_device_init(struct hmm_bo_device *bdev,
spin_lock_init(&bdev->list_lock);
mutex_init(&bdev->rbtree_mutex);
- bdev->flag = HMM_BO_DEVICE_INITED;
INIT_LIST_HEAD(&bdev->entire_bo_list);
bdev->allocated_rbtree = RB_ROOT;
@@ -376,6 +373,8 @@ int hmm_bo_device_init(struct hmm_bo_device *bdev,
__bo_insert_to_free_rbtree(&bdev->free_rbtree, bo);
+ bdev->flag = HMM_BO_DEVICE_INITED;
+
return 0;
}
diff --git a/drivers/staging/media/atomisp/pci/ia_css_pipe.h b/drivers/staging/media/atomisp/pci/ia_css_pipe.h
index c97d2ae356fd..77072694eb42 100644
--- a/drivers/staging/media/atomisp/pci/ia_css_pipe.h
+++ b/drivers/staging/media/atomisp/pci/ia_css_pipe.h
@@ -102,8 +102,6 @@ struct ia_css_yuvpp_settings {
struct osys_object;
struct ia_css_pipe {
- /* TODO: Remove stop_requested and use stop_requested in the pipeline */
- bool stop_requested;
struct ia_css_pipe_config config;
struct ia_css_pipe_extra_config extra_config;
struct ia_css_pipe_info info;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h
index 6e573ceaa9ea..bc2a78dff004 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr_types.h
@@ -11,9 +11,11 @@
* CSS-API header file for Advanced Noise Reduction kernel v1
*/
+#include <linux/math.h>
+
/* Application specific DMA settings */
#define ANR_BPP 10
-#define ANR_ELEMENT_BITS ((CEIL_DIV(ANR_BPP, 8)) * 8)
+#define ANR_ELEMENT_BITS round_up(ANR_BPP, 8)
/* Advanced Noise Reduction configuration.
* This is also known as Low-Light.
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h
index 099f32b8de1a..b1bbc283e367 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/dpc2/ia_css_dpc2_param.h
@@ -7,6 +7,8 @@
#ifndef __IA_CSS_DPC2_PARAM_H
#define __IA_CSS_DPC2_PARAM_H
+#include <linux/math.h>
+
#include "type_support.h"
#include "vmem.h" /* for VMEM_ARRAY*/
@@ -19,12 +21,12 @@
/* 3 lines state per color plane input_line_state */
#define DPC2_STATE_INPUT_BUFFER_HEIGHT (3 * NUM_PLANES)
/* Each plane has width equal to half frame line */
-#define DPC2_STATE_INPUT_BUFFER_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define DPC2_STATE_INPUT_BUFFER_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 1 line state per color plane for local deviation state*/
#define DPC2_STATE_LOCAL_DEVIATION_BUFFER_HEIGHT (1 * NUM_PLANES)
/* Each plane has width equal to half frame line */
-#define DPC2_STATE_LOCAL_DEVIATION_BUFFER_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define DPC2_STATE_LOCAL_DEVIATION_BUFFER_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* MINMAX state buffer stores 1 full input line (GR-R color line) */
#define DPC2_STATE_SECOND_MINMAX_BUFFER_HEIGHT 1
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c
index 30c84639d7e8..e9d6dd0bbfe2 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c
@@ -141,10 +141,10 @@ convert_coords_to_ispparams(
/* similar to topleft_y calculation, but round up if ymax
* has any fraction bits */
- bottom_y = CEIL_DIV(ymax, 1 << DVS_COORD_FRAC_BITS);
+ bottom_y = DIV_ROUND_UP(ymax, BIT(DVS_COORD_FRAC_BITS));
s.in_block_height = bottom_y - topleft_y + dvs_interp_envelope;
- bottom_x = CEIL_DIV(xmax, 1 << DVS_COORD_FRAC_BITS);
+ bottom_x = DIV_ROUND_UP(xmax, BIT(DVS_COORD_FRAC_BITS));
s.in_block_width = bottom_x - topleft_x + dvs_interp_envelope;
topleft_x_frac = topleft_x << (DVS_COORD_FRAC_BITS);
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h
index df87770446dd..a24eef965f16 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8_param.h
@@ -7,6 +7,8 @@
#ifndef __IA_CSS_EED1_8_PARAM_H
#define __IA_CSS_EED1_8_PARAM_H
+#include <linux/math.h>
+
#include "type_support.h"
#include "vmem.h" /* needed for VMEM_ARRAY */
@@ -35,35 +37,35 @@
#define EED1_8_STATE_INPUT_BUFFER_HEIGHT (5 * NUM_PLANES)
/* Each plane has width equal to half frame line */
-#define EED1_8_STATE_INPUT_BUFFER_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_INPUT_BUFFER_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 1 line state per color plane LD_H state */
#define EED1_8_STATE_LD_H_HEIGHT (1 * NUM_PLANES)
-#define EED1_8_STATE_LD_H_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_LD_H_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 1 line state per color plane LD_V state */
#define EED1_8_STATE_LD_V_HEIGHT (1 * NUM_PLANES)
-#define EED1_8_STATE_LD_V_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_LD_V_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 1 line (single plane) state for D_Hr state */
#define EED1_8_STATE_D_HR_HEIGHT 1
-#define EED1_8_STATE_D_HR_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_D_HR_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 1 line (single plane) state for D_Hb state */
#define EED1_8_STATE_D_HB_HEIGHT 1
-#define EED1_8_STATE_D_HB_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_D_HB_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 2 lines (single plane) state for D_Vr state */
#define EED1_8_STATE_D_VR_HEIGHT 2
-#define EED1_8_STATE_D_VR_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_D_VR_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 2 line (single plane) state for D_Vb state */
#define EED1_8_STATE_D_VB_HEIGHT 2
-#define EED1_8_STATE_D_VB_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_D_VB_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 2 lines state for R and B (= 2 planes) rb_zipped_state */
#define EED1_8_STATE_RB_ZIPPED_HEIGHT (2 * 2)
-#define EED1_8_STATE_RB_ZIPPED_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_RB_ZIPPED_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
#if EED1_8_FC_ENABLE_MEDIAN
/* 1 full input line (GR-R color line) for Yc state */
@@ -72,11 +74,11 @@
/* 1 line state per color plane Cg_state */
#define EED1_8_STATE_CG_HEIGHT (1 * NUM_PLANES)
-#define EED1_8_STATE_CG_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_CG_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 1 line state per color plane Co_state */
#define EED1_8_STATE_CO_HEIGHT (1 * NUM_PLANES)
-#define EED1_8_STATE_CO_WIDTH CEIL_DIV(MAX_FRAME_SIMDWIDTH, 2)
+#define EED1_8_STATE_CO_WIDTH DIV_ROUND_UP(MAX_FRAME_SIMDWIDTH, 2)
/* 1 full input line (GR-R color line) for AbsK state */
#define EED1_8_STATE_ABSK_HEIGHT 1
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c
index b34feba5340b..e701b7e41ef4 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/fpn/fpn_1.0/ia_css_fpn.host.c
@@ -4,6 +4,8 @@
* Copyright (c) 2015, Intel Corporation.
*/
+#include <linux/math.h>
+
#include <assert_support.h>
#include <ia_css_frame_public.h>
#include <ia_css_frame.h>
@@ -71,9 +73,9 @@ int ia_css_fpn_configure(const struct ia_css_binary *binary,
&my_info
};
- my_info.res.width = CEIL_DIV(info->res.width, 2); /* Packed by 2x */
+ my_info.res.width = DIV_ROUND_UP(info->res.width, 2); /* Packed by 2x */
my_info.res.height = info->res.height;
- my_info.padded_width = CEIL_DIV(info->padded_width, 2); /* Packed by 2x */
+ my_info.padded_width = DIV_ROUND_UP(info->padded_width, 2); /* Packed by 2x */
my_info.format = info->format;
my_info.raw_bit_depth = FPN_BITS_PER_PIXEL;
my_info.raw_bayer_order = info->raw_bayer_order;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h b/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h
index 61e9c04d2515..b49761ad39ca 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/sc/sc_1.0/ia_css_sc_param.h
@@ -22,7 +22,7 @@ struct sh_css_isp_sc_params {
* vec_slice is used for 2 adjacent vectors of shading gains.
* The number of shift times by vec_slice is 8.
* Max grid cell bqs to support the shading table centerting: N = 32
- * CEIL_DIV(N-1, ISP_SLICE_NELEMS) = CEIL_DIV(31, 4) = 8
+ * DIV_ROUND_UP(N-1, ISP_SLICE_NELEMS) = DIV_ROUND_UP(31, 4) = 8
*/
#define SH_CSS_SC_INTERPED_GAIN_HOR_SLICE_TIMES 8
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c
index 127f12ba2214..3c675063c4a7 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c
@@ -91,8 +91,7 @@ configure_kernel(
unsigned int vf_log_ds = 0;
/* First compute value */
- if (vf_info)
- {
+ if (vf_info) {
err = sh_css_vf_downscale_log2(out_info, vf_info, &vf_log_ds);
if (err)
return err;
diff --git a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h
index c7ade6ce6c68..6a0257359e69 100644
--- a/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h
+++ b/drivers/staging/media/atomisp/pci/isp/modes/interface/input_buf.isp.h
@@ -8,9 +8,10 @@ Copyright (c) 2010 - 2015, Intel Corporation.
#ifndef _INPUT_BUF_ISP_H_
#define _INPUT_BUF_ISP_H_
+#include <linux/math.h>
+
/* Temporary include, since IA_CSS_BINARY_MODE_COPY is still needed */
#include "sh_css_defs.h"
-#include "isp_const.h" /* MAX_VECTORS_PER_INPUT_LINE */
#define INPUT_BUF_HEIGHT 2 /* double buffer */
#define INPUT_BUF_LINES 2
@@ -22,7 +23,8 @@ Copyright (c) 2010 - 2015, Intel Corporation.
/* In continuous mode, the input buffer must be a fixed size for all binaries
* and at a fixed address since it will be used by the SP. */
#define EXTRA_INPUT_VECTORS 2 /* For left padding */
-#define MAX_VECTORS_PER_INPUT_LINE_CONT (CEIL_DIV(SH_CSS_MAX_SENSOR_WIDTH, ISP_NWAY) + EXTRA_INPUT_VECTORS)
+#define MAX_VECTORS_PER_INPUT_LINE_CONT \
+ (DIV_ROUND_UP(SH_CSS_MAX_SENSOR_WIDTH, ISP_NWAY) + EXTRA_INPUT_VECTORS)
/* The input buffer should be on a fixed address in vmem, for continuous capture */
#define INPUT_BUF_ADDR 0x0
diff --git a/drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h b/drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h
deleted file mode 100644
index b767b0d35bb4..000000000000
--- a/drivers/staging/media/atomisp/pci/isp/modes/interface/isp_const.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/**
-Support for Intel Camera Imaging ISP subsystem.
-Copyright (c) 2010 - 2015, Intel Corporation.
-
-*/
-
-#ifndef _COMMON_ISP_CONST_H_
-#define _COMMON_ISP_CONST_H_
-
-/*#include "isp.h"*/ /* ISP_VEC_NELEMS */
-
-/* Binary independent constants */
-
-#ifndef NO_HOIST
-# define NO_HOIST HIVE_ATTRIBUTE((no_hoist))
-#endif
-
-#define NO_HOIST_CSE HIVE_ATTRIBUTE((no_hoist, no_cse))
-
-#define UNION struct /* Union constructors not allowed in C++ */
-
-#define XMEM_WIDTH_BITS HIVE_ISP_DDR_WORD_BITS
-#define XMEM_SHORTS_PER_WORD (HIVE_ISP_DDR_WORD_BITS / 16)
-#define XMEM_INTS_PER_WORD (HIVE_ISP_DDR_WORD_BITS / 32)
-#define XMEM_POW2_BYTES_PER_WORD HIVE_ISP_DDR_WORD_BYTES
-
-#define BITS8_ELEMENTS_PER_XMEM_ADDR CEIL_DIV(XMEM_WIDTH_BITS, 8)
-#define BITS16_ELEMENTS_PER_XMEM_ADDR CEIL_DIV(XMEM_WIDTH_BITS, 16)
-
-#define ISP_NWAY_LOG2 6
-
-/* *****************************
- * ISP input/output buffer sizes
- * ****************************/
-/* input image */
-#define INPUT_BUF_DMA_HEIGHT 2
-#define INPUT_BUF_HEIGHT 2 /* double buffer */
-#define OUTPUT_BUF_DMA_HEIGHT 2
-#define OUTPUT_BUF_HEIGHT 2 /* double buffer */
-#define OUTPUT_NUM_TRANSFERS 4
-
-/* GDC accelerator: Up/Down Scaling */
-/* These should be moved to the gdc_defs.h in the device */
-#define UDS_SCALING_N HRT_GDC_N
-/* AB: This should cover the zooming up to 16MP */
-#define UDS_MAX_OXDIM 5000
-/* We support maximally 2 planes with different parameters
- - luma and chroma (YUV420) */
-#define UDS_MAX_PLANES 2
-#define UDS_BLI_BLOCK_HEIGHT 2
-#define UDS_BCI_BLOCK_HEIGHT 4
-#define UDS_BLI_INTERP_ENVELOPE 1
-#define UDS_BCI_INTERP_ENVELOPE 3
-#define UDS_MAX_ZOOM_FAC 64
-/* Make it always one FPGA vector.
- Four FPGA vectors are required and
- four of them fit in one ASIC vector.*/
-#define UDS_MAX_CHUNKS 16
-
-#define ISP_LEFT_PADDING _ISP_LEFT_CROP_EXTRA(ISP_LEFT_CROPPING)
-#define ISP_LEFT_PADDING_VECS CEIL_DIV(ISP_LEFT_PADDING, ISP_VEC_NELEMS)
-/* in case of continuous the croppong of the current binary doesn't matter for the buffer calculation, but the cropping of the sp copy should be used */
-#define ISP_LEFT_PADDING_CONT _ISP_LEFT_CROP_EXTRA(SH_CSS_MAX_LEFT_CROPPING)
-#define ISP_LEFT_PADDING_VECS_CONT CEIL_DIV(ISP_LEFT_PADDING_CONT, ISP_VEC_NELEMS)
-
-#define CEIL_ROUND_DIV_STRIPE(width, stripe, padding) \
- CEIL_MUL(padding + CEIL_DIV(width - padding, stripe), ((ENABLE_RAW_BINNING || ENABLE_FIXED_BAYER_DS) ? 4 : 2))
-
-/* output (Y,U,V) image, 4:2:0 */
-#define MAX_VECTORS_PER_LINE \
- CEIL_ROUND_DIV_STRIPE(CEIL_DIV(ISP_MAX_INTERNAL_WIDTH, ISP_VEC_NELEMS), \
- ISP_NUM_STRIPES, \
- ISP_LEFT_PADDING_VECS)
-
-/*
- * ITERATOR_VECTOR_INCREMENT' explanation:
- * when striping an even number of iterations, one of the stripes is
- * one iteration wider than the other to account for overlap
- * so the calc for the output buffer vmem size is:
- * ((width[vectors]/num_of_stripes) + 2[vectors])
- */
-#define MAX_VECTORS_PER_OUTPUT_LINE \
- CEIL_DIV(CEIL_DIV(ISP_MAX_OUTPUT_WIDTH, ISP_NUM_STRIPES) + ISP_LEFT_PADDING, ISP_VEC_NELEMS)
-
-/* Must be even due to interlaced bayer input */
-#define MAX_VECTORS_PER_INPUT_LINE CEIL_MUL((CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS) + ISP_LEFT_PADDING_VECS), 2)
-#define MAX_VECTORS_PER_INPUT_STRIPE CEIL_ROUND_DIV_STRIPE(MAX_VECTORS_PER_INPUT_LINE, \
- ISP_NUM_STRIPES, \
- ISP_LEFT_PADDING_VECS)
-
-/* Add 2 for left croppping */
-#define MAX_SP_RAW_COPY_VECTORS_PER_INPUT_LINE (CEIL_DIV(ISP_MAX_INPUT_WIDTH, ISP_VEC_NELEMS) + 2)
-
-#define MAX_VECTORS_PER_BUF_LINE \
- (MAX_VECTORS_PER_LINE + DUMMY_BUF_VECTORS)
-#define MAX_VECTORS_PER_BUF_INPUT_LINE \
- (MAX_VECTORS_PER_INPUT_STRIPE + DUMMY_BUF_VECTORS)
-#define MAX_OUTPUT_Y_FRAME_WIDTH \
- (MAX_VECTORS_PER_LINE * ISP_VEC_NELEMS)
-#define MAX_OUTPUT_Y_FRAME_SIMDWIDTH \
- MAX_VECTORS_PER_LINE
-#define MAX_OUTPUT_C_FRAME_WIDTH \
- (MAX_OUTPUT_Y_FRAME_WIDTH / 2)
-#define MAX_OUTPUT_C_FRAME_SIMDWIDTH \
- CEIL_DIV(MAX_OUTPUT_C_FRAME_WIDTH, ISP_VEC_NELEMS)
-
-/* should be even */
-#define NO_CHUNKING (OUTPUT_NUM_CHUNKS == 1)
-
-#define MAX_VECTORS_PER_CHUNK \
- (NO_CHUNKING ? MAX_VECTORS_PER_LINE \
- : 2 * CEIL_DIV(MAX_VECTORS_PER_LINE, \
- 2 * OUTPUT_NUM_CHUNKS))
-
-#define MAX_C_VECTORS_PER_CHUNK \
- (MAX_VECTORS_PER_CHUNK / 2)
-
-/* should be even */
-#define MAX_VECTORS_PER_OUTPUT_CHUNK \
- (NO_CHUNKING ? MAX_VECTORS_PER_OUTPUT_LINE \
- : 2 * CEIL_DIV(MAX_VECTORS_PER_OUTPUT_LINE, \
- 2 * OUTPUT_NUM_CHUNKS))
-
-#define MAX_C_VECTORS_PER_OUTPUT_CHUNK \
- (MAX_VECTORS_PER_OUTPUT_CHUNK / 2)
-
-/* should be even */
-#define MAX_VECTORS_PER_INPUT_CHUNK \
- (INPUT_NUM_CHUNKS == 1 ? MAX_VECTORS_PER_INPUT_STRIPE \
- : 2 * CEIL_DIV(MAX_VECTORS_PER_INPUT_STRIPE, \
- 2 * OUTPUT_NUM_CHUNKS))
-
-#define DEFAULT_C_SUBSAMPLING 2
-
-/****** DMA buffer properties */
-
-#define RAW_BUF_LINES ((ENABLE_RAW_BINNING || ENABLE_FIXED_BAYER_DS) ? 4 : 2)
-
-/* [isp vmem] table size[vectors] per line per color (GR,R,B,GB),
- multiples of NWAY */
-#define ISP2400_SCTBL_VECTORS_PER_LINE_PER_COLOR \
- CEIL_DIV(SH_CSS_MAX_SCTBL_WIDTH_PER_COLOR, ISP_VEC_NELEMS)
-#define ISP2401_SCTBL_VECTORS_PER_LINE_PER_COLOR \
- CEIL_DIV(SH_CSS_MAX_SCTBL_WIDTH_PER_COLOR, ISP_VEC_NELEMS)
-/* [isp vmem] table size[vectors] per line for 4colors (GR,R,B,GB),
- multiples of NWAY */
-#define SCTBL_VECTORS_PER_LINE \
- (SCTBL_VECTORS_PER_LINE_PER_COLOR * IA_CSS_SC_NUM_COLORS)
-
-/*************/
-
-/* Format for fixed primaries */
-
-#define ISP_FIXED_PRIMARY_FORMAT IA_CSS_FRAME_FORMAT_NV12
-
-#endif /* _COMMON_ISP_CONST_H_ */
diff --git a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
index 84220359c957..b411ca2f415e 100644
--- a/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
+++ b/drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c
@@ -51,7 +51,6 @@
#include "sp.h"
#include "isp.h"
#include "type_support.h"
-#include "math_support.h" /* CEIL_DIV */
#include "input_system.h" /* input_formatter_reg_load */
#include "ia_css_tagger_common.h"
diff --git a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c
index 4f610f57e6c1..2cb96f9a6030 100644
--- a/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c
+++ b/drivers/staging/media/atomisp/pci/runtime/frame/src/frame.c
@@ -4,15 +4,16 @@
* Copyright (c) 2015, Intel Corporation.
*/
-#include "hmm.h"
+#include <linux/bitops.h>
+#include <linux/math.h>
-#include "ia_css_frame.h"
-#include <math_support.h>
#include "assert_support.h"
+#include "atomisp_internal.h"
+#include "hmm.h"
#include "ia_css_debug.h"
+#include "ia_css_frame.h"
#include "isp.h"
#include "sh_css_internal.h"
-#include "atomisp_internal.h"
#define NV12_TILEY_TILE_WIDTH 128
#define NV12_TILEY_TILE_HEIGHT 32
@@ -459,15 +460,16 @@ static void frame_init_single_plane(struct ia_css_frame *frame,
unsigned int stride;
stride = subpixels_per_line * bytes_per_pixel;
- /* Frame height needs to be even number - needed by hw ISYS2401
- In case of odd number, round up to even.
- Images won't be impacted by this round up,
- only needed by jpeg/embedded data.
- As long as buffer allocation and release are using data_bytes,
- there won't be memory leak. */
- frame->data_bytes = stride * CEIL_MUL2(height, 2);
+ /*
+ * Frame height needs to be even number - needed by hw ISYS2401.
+ * In case of odd number, round up to even.
+ * Images won't be impacted by this round up,
+ * only needed by jpeg/embedded data.
+ * As long as buffer allocation and release are using data_bytes,
+ * there won't be memory leak.
+ */
+ frame->data_bytes = stride * round_up(height, 2);
frame_init_plane(plane, subpixels_per_line, stride, height, 0);
- return;
}
static void frame_init_raw_single_plane(
@@ -486,7 +488,6 @@ static void frame_init_raw_single_plane(
HIVE_ISP_DDR_WORD_BITS / bits_per_pixel);
frame->data_bytes = stride * height;
frame_init_plane(plane, subpixels_per_line, stride, height, 0);
- return;
}
static void frame_init_nv_planes(struct ia_css_frame *frame,
@@ -690,7 +691,7 @@ ia_css_elems_bytes_from_info(const struct ia_css_frame_info *info)
if (info->format == IA_CSS_FRAME_FORMAT_RAW
|| (info->format == IA_CSS_FRAME_FORMAT_RAW_PACKED)) {
if (info->raw_bit_depth)
- return CEIL_DIV(info->raw_bit_depth, 8);
+ return BITS_TO_BYTES(info->raw_bit_depth);
else
return 2; /* bytes per pixel */
}
diff --git a/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c b/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c
index d57ffb335fc0..50b0b31d734a 100644
--- a/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c
+++ b/drivers/staging/media/atomisp/pci/runtime/ifmtr/src/ifmtr.c
@@ -4,9 +4,9 @@
* Copyright (c) 2010 - 2015, Intel Corporation.
*/
-#include "system_global.h"
-#include <linux/kernel.h>
+#include <linux/math.h>
+#include "system_global.h"
#include "ia_css_ifmtr.h"
#include <math_support.h>
@@ -149,10 +149,9 @@ int ia_css_ifmtr_configure(struct ia_css_stream_config *config,
left_padding = 2 * ISP_VEC_NELEMS - config->left_padding;
if (left_padding) {
- num_vectors = CEIL_DIV(cropped_width + left_padding,
- ISP_VEC_NELEMS);
+ num_vectors = DIV_ROUND_UP(cropped_width + left_padding, ISP_VEC_NELEMS);
} else {
- num_vectors = CEIL_DIV(cropped_width, ISP_VEC_NELEMS);
+ num_vectors = DIV_ROUND_UP(cropped_width, ISP_VEC_NELEMS);
num_vectors *= buffer_height;
/* todo: in case of left padding,
num_vectors is vectors per line,
@@ -305,7 +304,7 @@ int ia_css_ifmtr_configure(struct ia_css_stream_config *config,
if ((!binary) || config->continuous)
/* !binary -> sp raw copy pipe */
buffer_height *= 2;
- vectors_per_line = CEIL_DIV(cropped_width, ISP_VEC_NELEMS);
+ vectors_per_line = DIV_ROUND_UP(cropped_width, ISP_VEC_NELEMS);
vectors_per_line = CEIL_MUL(vectors_per_line, deinterleaving);
break;
case ATOMISP_INPUT_FORMAT_RAW_14:
diff --git a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c
index b5395aea89fc..e6c11d5f77b4 100644
--- a/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c
+++ b/drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c
@@ -808,7 +808,7 @@ static bool calculate_isys2401_dma_port_cfg(
cfg->elements = HIVE_ISP_DDR_WORD_BITS / bits_per_pixel;
cfg->cropping = 0;
- cfg->width = CEIL_DIV(cfg->stride, HIVE_ISP_DDR_WORD_BYTES);
+ cfg->width = DIV_ROUND_UP(cfg->stride, HIVE_ISP_DDR_WORD_BYTES);
return true;
}
diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h
index 316eaa2070ea..8b7cbf31a1a2 100644
--- a/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h
+++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/interface/ia_css_pipeline.h
@@ -34,7 +34,6 @@ struct ia_css_pipeline_stage {
struct ia_css_pipeline {
enum ia_css_pipe_id pipe_id;
u8 pipe_num;
- bool stop_requested;
struct ia_css_pipeline_stage *stages;
struct ia_css_pipeline_stage *current_stage;
unsigned int num_stages;
diff --git a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
index aabebe61ec77..cb8d652227a7 100644
--- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
+++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
@@ -198,7 +198,6 @@ int ia_css_pipeline_request_stop(struct ia_css_pipeline *pipeline)
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
"ia_css_pipeline_request_stop() enter: pipeline=%p\n",
pipeline);
- pipeline->stop_requested = true;
/* Send stop event to the sp*/
/* This needs improvement, stop on all the pipes available
@@ -663,7 +662,6 @@ static void pipeline_init_defaults(
pipeline->pipe_id = pipe_id;
pipeline->stages = NULL;
- pipeline->stop_requested = false;
pipeline->current_stage = NULL;
memcpy(&pipeline->in_frame, &ia_css_default_frame,
diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c
index 5a8e8e67aa13..73bd87f43a8c 100644
--- a/drivers/staging/media/atomisp/pci/sh_css.c
+++ b/drivers/staging/media/atomisp/pci/sh_css.c
@@ -2002,10 +2002,6 @@ ia_css_uninit(void)
sh_css_params_free_default_gdc_lut();
- /* TODO: JB: implement decent check and handling of freeing mipi frames */
- if (!mipi_is_free())
- dev_warn(atomisp_dev, "mipi frames are not freed.\n");
-
/* cleanup generic data */
sh_css_params_uninit();
ia_css_refcount_uninit();
@@ -3743,23 +3739,6 @@ ia_css_pipe_dequeue_buffer(struct ia_css_pipe *pipe,
case IA_CSS_BUFFER_TYPE_INPUT_FRAME:
case IA_CSS_BUFFER_TYPE_OUTPUT_FRAME:
case IA_CSS_BUFFER_TYPE_SEC_OUTPUT_FRAME:
- if (pipe && pipe->stop_requested) {
- if (!IS_ISP2401) {
- /*
- * free mipi frames only for old input
- * system for 2401 it is done in
- * ia_css_stream_destroy call
- */
- return_err = free_mipi_frames(pipe);
- if (return_err) {
- IA_CSS_LOG("free_mipi_frames() failed");
- IA_CSS_LEAVE_ERR(return_err);
- return return_err;
- }
- }
- pipe->stop_requested = false;
- }
- fallthrough;
case IA_CSS_BUFFER_TYPE_VF_OUTPUT_FRAME:
case IA_CSS_BUFFER_TYPE_SEC_VF_OUTPUT_FRAME:
frame = (struct ia_css_frame *)HOST_ADDRESS(ddr_buffer.kernel_ptr);
@@ -4095,8 +4074,6 @@ sh_css_pipe_start(struct ia_css_stream *stream)
return err;
}
- pipe->stop_requested = false;
-
switch (pipe_id) {
case IA_CSS_PIPE_ID_PREVIEW:
err = preview_start(pipe);
@@ -4120,19 +4097,15 @@ sh_css_pipe_start(struct ia_css_stream *stream)
for (i = 1; i < stream->num_pipes && 0 == err ; i++) {
switch (stream->pipes[i]->mode) {
case IA_CSS_PIPE_ID_PREVIEW:
- stream->pipes[i]->stop_requested = false;
err = preview_start(stream->pipes[i]);
break;
case IA_CSS_PIPE_ID_VIDEO:
- stream->pipes[i]->stop_requested = false;
err = video_start(stream->pipes[i]);
break;
case IA_CSS_PIPE_ID_CAPTURE:
- stream->pipes[i]->stop_requested = false;
err = capture_start(stream->pipes[i]);
break;
case IA_CSS_PIPE_ID_YUVPP:
- stream->pipes[i]->stop_requested = false;
err = yuvpp_start(stream->pipes[i]);
break;
default:
diff --git a/drivers/staging/media/atomisp/pci/sh_css_defs.h b/drivers/staging/media/atomisp/pci/sh_css_defs.h
index 51491dfe05cc..7bfeeb8cf053 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_defs.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_defs.h
@@ -7,12 +7,12 @@
#ifndef _SH_CSS_DEFS_H_
#define _SH_CSS_DEFS_H_
+#include <linux/math.h>
+
#include "isp.h"
/*#include "vamem.h"*/ /* Cannot include for VAMEM properties this file is visible on ISP -> pipeline generator */
-#include "math_support.h" /* max(), min, etc etc */
-
/* ID's for refcount */
#define IA_CSS_REFCOUNT_PARAM_SET_POOL 0xCAFE0001
#define IA_CSS_REFCOUNT_PARAM_BUFFER 0xCAFE0002
@@ -182,7 +182,7 @@ RGB[0,8191],coef[-8192,8191] -> RGB[0,8191]
The ISP firmware needs these rules to be applied at pre-processor time,
that's why these are macros, not functions. */
#define _ISP_BQS(num) ((num) / 2)
-#define _ISP_VECS(width) CEIL_DIV(width, ISP_VEC_NELEMS)
+#define _ISP_VECS(width) DIV_ROUND_UP(width, ISP_VEC_NELEMS)
#define ISP_BQ_GRID_WIDTH(elements_per_line, deci_factor_log2) \
CEIL_SHIFT(elements_per_line / 2, deci_factor_log2)
@@ -194,9 +194,9 @@ RGB[0,8191],coef[-8192,8191] -> RGB[0,8191]
/* The morphing table is similar to the shading table in the sense that we
have 1 more value than we have cells in the grid. */
#define _ISP_MORPH_TABLE_WIDTH(int_width) \
- (CEIL_DIV(int_width, SH_CSS_MORPH_TABLE_GRID) + 1)
+ (DIV_ROUND_UP(int_width, SH_CSS_MORPH_TABLE_GRID) + 1)
#define _ISP_MORPH_TABLE_HEIGHT(int_height) \
- (CEIL_DIV(int_height, SH_CSS_MORPH_TABLE_GRID) + 1)
+ (DIV_ROUND_UP(int_height, SH_CSS_MORPH_TABLE_GRID) + 1)
#define _ISP_MORPH_TABLE_ALIGNED_WIDTH(width) \
CEIL_MUL(_ISP_MORPH_TABLE_WIDTH(width), \
SH_CSS_MORPH_TABLE_ELEMS_PER_DDR_WORD)
@@ -298,7 +298,7 @@ RGB[0,8191],coef[-8192,8191] -> RGB[0,8191]
c_subsampling, \
num_chunks, \
pipelining) \
- CEIL_MUL2(CEIL_MUL2(MAX(__ISP_PADDED_OUTPUT_WIDTH(out_width, \
+ round_up(round_up(MAX(__ISP_PADDED_OUTPUT_WIDTH(out_width, \
dvs_env_width, \
left_crop), \
__ISP_MIN_INTERNAL_WIDTH(num_chunks, \
diff --git a/drivers/staging/media/atomisp/pci/sh_css_internal.h b/drivers/staging/media/atomisp/pci/sh_css_internal.h
index 7b3483585748..9155a83fcc03 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_internal.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_internal.h
@@ -8,6 +8,7 @@
#define _SH_CSS_INTERNAL_H_
#include <linux/build_bug.h>
+#include <linux/math.h>
#include <linux/stdarg.h>
#include <system_global.h>
@@ -95,7 +96,6 @@
* the SIZE_OF_XXX macro of the corresponding struct. If they are not
* equal, functionality will break.
*/
-#define CALC_ALIGNMENT_MEMBER(x, y) (CEIL_MUL(x, y) - x)
#define SIZE_OF_HRT_VADDRESS sizeof(hive_uint32)
/* Number of SP's */
@@ -704,13 +704,11 @@ struct sh_css_hmm_buffer {
/* Do not use sizeof(uint64_t) since that does not exist of SP */
#define SIZE_OF_SH_CSS_HMM_BUFFER_STRUCT \
- (SIZE_OF_PAYLOAD_UNION + \
- CALC_ALIGNMENT_MEMBER(SIZE_OF_PAYLOAD_UNION, 8) + \
+ (round_up(SIZE_OF_PAYLOAD_UNION, 8) + \
8 + \
8 + \
SIZE_OF_IA_CSS_TIME_MEAS_STRUCT + \
- SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT + \
- CALC_ALIGNMENT_MEMBER(SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT, 8))
+ round_up(SIZE_OF_IA_CSS_CLOCK_TICK_STRUCT, 8))
static_assert(sizeof(struct sh_css_hmm_buffer) == SIZE_OF_SH_CSS_HMM_BUFFER_STRUCT);
diff --git a/drivers/staging/media/atomisp/pci/sh_css_mipi.c b/drivers/staging/media/atomisp/pci/sh_css_mipi.c
index 42f14ed853e1..971b685cdb58 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_mipi.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_mipi.c
@@ -185,17 +185,6 @@ mipi_init(void)
ref_count_mipi_allocation[i] = 0;
}
-bool mipi_is_free(void)
-{
- unsigned int i;
-
- for (i = 0; i < N_CSI_PORTS; i++)
- if (ref_count_mipi_allocation[i])
- return false;
-
- return true;
-}
-
/*
* @brief Calculate the required MIPI buffer sizes.
* Based on the stream configuration, calculate the
diff --git a/drivers/staging/media/atomisp/pci/sh_css_mipi.h b/drivers/staging/media/atomisp/pci/sh_css_mipi.h
index 6f7389f44baa..b3887ee3c75a 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_mipi.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_mipi.h
@@ -14,8 +14,6 @@
void
mipi_init(void);
-bool mipi_is_free(void);
-
int
allocate_mipi_frames(struct ia_css_pipe *pipe, struct ia_css_stream_info *info);
diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h
index b7057887adea..b31a0d4e8acb 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h
+++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.h
@@ -7,6 +7,8 @@
#ifndef _SH_CSS_PARAMS_DVS_H_
#define _SH_CSS_PARAMS_DVS_H_
+#include <linux/math.h>
+
#include <math_support.h>
#include <ia_css_types.h>
#include "gdc_global.h" /* gdc_warp_param_mem_t */
@@ -20,22 +22,20 @@
/* ISP2400 */
/* horizontal 64x64 blocks round up to DVS_BLOCKDIM_X, make even */
-#define DVS_NUM_BLOCKS_X(X) (CEIL_MUL(CEIL_DIV((X), DVS_BLOCKDIM_X), 2))
+#define DVS_NUM_BLOCKS_X(X) round_up(DIV_ROUND_UP((X), DVS_BLOCKDIM_X), 2)
+#define DVS_NUM_BLOCKS_X_CHROMA(X) DIV_ROUND_UP((X), DVS_BLOCKDIM_X)
/* ISP2400 */
/* vertical 64x64 blocks round up to DVS_BLOCKDIM_Y */
-#define DVS_NUM_BLOCKS_Y(X) (CEIL_DIV((X), DVS_BLOCKDIM_Y_LUMA))
-#define DVS_NUM_BLOCKS_X_CHROMA(X) (CEIL_DIV((X), DVS_BLOCKDIM_X))
-#define DVS_NUM_BLOCKS_Y_CHROMA(X) (CEIL_DIV((X), DVS_BLOCKDIM_Y_CHROMA))
+#define DVS_NUM_BLOCKS_Y(X) DIV_ROUND_UP((X), DVS_BLOCKDIM_Y_LUMA)
+#define DVS_NUM_BLOCKS_Y_CHROMA(X) DIV_ROUND_UP((X), DVS_BLOCKDIM_Y_CHROMA)
-#define DVS_TABLE_IN_BLOCKDIM_X_LUMA(X) (DVS_NUM_BLOCKS_X(X) + 1) /* N blocks have N + 1 set of coords */
-#define DVS_TABLE_IN_BLOCKDIM_X_CHROMA(X) (DVS_NUM_BLOCKS_X_CHROMA(X) + 1)
+/* N blocks have N + 1 set of coords */
+#define DVS_TABLE_IN_BLOCKDIM_X_LUMA(X) (DVS_NUM_BLOCKS_X(X) + 1)
+#define DVS_TABLE_IN_BLOCKDIM_X_CHROMA(X) (DVS_NUM_BLOCKS_X_CHROMA(X) + 1)
#define DVS_TABLE_IN_BLOCKDIM_Y_LUMA(X) (DVS_NUM_BLOCKS_Y(X) + 1)
#define DVS_TABLE_IN_BLOCKDIM_Y_CHROMA(X) (DVS_NUM_BLOCKS_Y_CHROMA(X) + 1)
-#define DVS_ENVELOPE_X(X) (((X) == 0) ? (DVS_ENV_MIN_X) : (X))
-#define DVS_ENVELOPE_Y(X) (((X) == 0) ? (DVS_ENV_MIN_Y) : (X))
-
#define DVS_COORD_FRAC_BITS (10)
/* ISP2400 */
@@ -43,8 +43,8 @@
#define XMEM_ALIGN_LOG2 (5)
-#define DVS_6AXIS_COORDS_ELEMS CEIL_MUL(sizeof(gdc_warp_param_mem_t) \
- , HIVE_ISP_DDR_WORD_BYTES)
+#define DVS_6AXIS_COORDS_ELEMS \
+ round_up(sizeof(gdc_warp_param_mem_t), HIVE_ISP_DDR_WORD_BYTES)
/* currently we only support two output with the same resolution, output 0 is th default one. */
#define DVS_6AXIS_BYTES(binary) \
diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c
index 0d4a936ad80f..11d62313c908 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_params.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_params.c
@@ -4,6 +4,8 @@
* Copyright (c) 2015, Intel Corporation.
*/
+#include <linux/math.h>
+
#include "gdc_device.h" /* gdc_lut_store(), ... */
#include "isp.h" /* ISP_VEC_ELEMBITS */
#include "vamem.h"
@@ -21,8 +23,6 @@
#include "platform_support.h"
#include "assert_support.h"
-#include "misc_support.h" /* NOT_USED */
-#include "math_support.h" /* max(), min() EVEN_FLOOR()*/
#include "ia_css_stream.h"
#include "sh_css_params_internal.h"
@@ -4042,10 +4042,10 @@ sh_css_update_uds_and_crop_info(
}
/* Must enforce that the crop position is even */
- crop_x = EVEN_FLOOR(crop_x);
- crop_y = EVEN_FLOOR(crop_y);
- uds_xc = EVEN_FLOOR(uds_xc);
- uds_yc = EVEN_FLOOR(uds_yc);
+ crop_x = round_down(crop_x, 2);
+ crop_y = round_down(crop_y, 2);
+ uds_xc = round_down(uds_xc, 2);
+ uds_yc = round_down(uds_yc, 2);
uds->xc = (uint16_t)uds_xc;
uds->yc = (uint16_t)uds_yc;
diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c
index e5e08c6f79f2..19fd31cb9bb0 100644
--- a/drivers/staging/media/imx/imx-media-csc-scaler.c
+++ b/drivers/staging/media/imx/imx-media-csc-scaler.c
@@ -912,7 +912,7 @@ imx_media_csc_scaler_device_init(struct imx_media_dev *md)
return &priv->vdev;
err_m2m:
- video_set_drvdata(vfd, NULL);
+ video_device_release(vfd);
err_vfd:
kfree(priv);
return ERR_PTR(ret);
diff --git a/drivers/staging/media/ipu7/Kconfig b/drivers/staging/media/ipu7/Kconfig
new file mode 100644
index 000000000000..7d831ba7501d
--- /dev/null
+++ b/drivers/staging/media/ipu7/Kconfig
@@ -0,0 +1,19 @@
+config VIDEO_INTEL_IPU7
+ tristate "Intel IPU7 driver"
+ depends on ACPI || COMPILE_TEST
+ depends on VIDEO_DEV
+ depends on X86 && HAS_DMA
+ depends on IPU_BRIDGE || !IPU_BRIDGE
+ depends on PCI
+ select AUXILIARY_BUS
+ select IOMMU_IOVA
+ select VIDEO_V4L2_SUBDEV_API
+ select MEDIA_CONTROLLER
+ select VIDEOBUF2_DMA_SG
+ select V4L2_FWNODE
+ help
+ This is the 7th Gen Intel Image Processing Unit, found in Intel SoCs
+ and used for capturing images and video from camera sensors.
+
+ To compile this driver, say Y here! It contains 2 modules -
+ intel_ipu7 and intel_ipu7_isys.
diff --git a/drivers/staging/media/ipu7/Makefile b/drivers/staging/media/ipu7/Makefile
new file mode 100644
index 000000000000..6d2aec219e65
--- /dev/null
+++ b/drivers/staging/media/ipu7/Makefile
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 - 2025 Intel Corporation.
+
+intel-ipu7-objs += ipu7.o \
+ ipu7-bus.o \
+ ipu7-dma.o \
+ ipu7-mmu.o \
+ ipu7-buttress.o \
+ ipu7-cpd.o \
+ ipu7-syscom.o \
+ ipu7-boot.o
+
+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7.o
+
+intel-ipu7-isys-objs += ipu7-isys.o \
+ ipu7-isys-csi2.o \
+ ipu7-isys-csi-phy.o \
+ ipu7-fw-isys.o \
+ ipu7-isys-video.o \
+ ipu7-isys-queue.o \
+ ipu7-isys-subdev.o
+
+obj-$(CONFIG_VIDEO_INTEL_IPU7) += intel-ipu7-isys.o
diff --git a/drivers/staging/media/ipu7/TODO b/drivers/staging/media/ipu7/TODO
new file mode 100644
index 000000000000..7fbc37059adf
--- /dev/null
+++ b/drivers/staging/media/ipu7/TODO
@@ -0,0 +1,28 @@
+This is a list of things that need to be done to get this driver out of the
+staging directory.
+
+- ABI headers cleanup
+ Cleanup the firmware ABI headers
+
+- Add metadata capture support
+ The IPU7 hardware should support metadata capture, but it is not
+ fully verified with IPU7 firmware ABI so far, need to add the metadata
+ capture support.
+
+- Refine CSI2 PHY code
+ Refine the ipu7-isys-csi2-phy.c, move the hardware specific variant
+ into structure, clarify and explain the PHY registers to make it more
+ readable.
+
+- Work with the common IPU module
+ Sakari commented much of the driver code is the same than the IPU6 driver.
+ IPU7 driver is expected to work with the common IPU module in future.
+
+- Register definition cleanup
+ Cleanup the register definitions - remove some unnecessary definitions
+ remove 'U' suffix for hexadecimal and decimal values and add IPU7 prefix
+ for IPU7 specific registers.
+ Some ISYS IO sub-blocks register definitions are offset values from
+ specific sub-block base, but it is not clear and well suited for driver
+ to use, need to update the register definitions to make it more clear
+ and readable.
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h
new file mode 100644
index 000000000000..a1519c4fe661
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h
@@ -0,0 +1,163 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_BOOT_ABI_H
+#define IPU7_FW_BOOT_ABI_H
+
+#include "ipu7_fw_common_abi.h"
+#include "ipu7_fw_syscom_abi.h"
+
+#define IA_GOFO_FWLOG_SEVERITY_CRIT (0U)
+#define IA_GOFO_FWLOG_SEVERITY_ERROR (1U)
+#define IA_GOFO_FWLOG_SEVERITY_WARNING (2U)
+#define IA_GOFO_FWLOG_SEVERITY_INFO (3U)
+#define IA_GOFO_FWLOG_SEVERITY_DEBUG (4U)
+#define IA_GOFO_FWLOG_SEVERITY_VERBOSE (5U)
+#define IA_GOFO_FWLOG_MAX_LOGGER_SOURCES (64U)
+
+#define LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK BIT(0)
+#define LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK BIT(1)
+#define LOGGER_CONFIG_CHANNEL_ENABLE_ALL_BITMASK \
+ (LOGGER_CONFIG_CHANNEL_ENABLE_HWPRINTF_BITMASK | \
+ LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK)
+
+struct ia_gofo_logger_config {
+ u8 use_source_severity;
+ u8 source_severity[IA_GOFO_FWLOG_MAX_LOGGER_SOURCES];
+ u8 use_channels_enable_bitmask;
+ u8 channels_enable_bitmask;
+ u8 padding[1];
+ ia_gofo_addr_t hw_printf_buffer_base_addr;
+ u32 hw_printf_buffer_size_bytes;
+};
+
+#pragma pack(push, 1)
+
+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP \
+ ((u32)IA_GOFO_FW_BOOT_ID_MAX)
+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET (0U)
+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PS_OFFSET \
+ ((IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_IS_OFFSET) + \
+ (u32)(IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP))
+#define IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_PRIMARY_OFFSET (0U)
+#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000U / 4U)
+#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \
+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 2U)
+#define IA_GOFO_HKR_HIF_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET \
+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP)
+#define IA_GOFO_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \
+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U)
+#define IA_GOFO_HKR_IPU_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX \
+ (IA_GOFO_BUTTRESS_FW_BOOT_PARAMS_MAX_REG_IDX_PER_APP * 4U)
+
+#define IA_GOFO_BOOT_RESERVED_SIZE (58U)
+#define IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE (IA_GOFO_BOOT_RESERVED_SIZE)
+#define IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS \
+ (sizeof(ia_gofo_addr_t) + sizeof(ia_gofo_addr_t) + sizeof(u32))
+
+enum ia_gofo_buttress_reg_id {
+ IA_GOFO_FW_BOOT_CONFIG_ID = 0,
+ IA_GOFO_FW_BOOT_STATE_ID = 1,
+ IA_GOFO_FW_BOOT_RESERVED1_ID = IA_GOFO_FW_BOOT_STATE_ID,
+ IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2,
+ IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3,
+ IA_GOFO_FW_BOOT_RESERVED0_ID = IA_GOFO_FW_BOOT_UNTRUSTED_ADDR_MIN_ID,
+ IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID = 4,
+ IA_GOFO_FW_BOOT_ID_MAX
+};
+
+enum ia_gofo_boot_uc_tile_frequency_units {
+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ = 0,
+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_HZ = 1,
+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_N
+};
+
+#define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \
+ (0xdead0000U == ((boot_state) & 0xffff0000U))
+
+struct ia_gofo_boot_config {
+ u32 length;
+ struct ia_gofo_version_s config_version;
+ struct ia_gofo_msg_version_list client_version_support;
+ ia_gofo_addr_t pkg_dir;
+ ia_gofo_addr_t subsys_config;
+ u32 uc_tile_frequency;
+ u16 checksum;
+ u8 uc_tile_frequency_units;
+ u8 padding[1];
+ u32 reserved[IA_GOFO_BOOT_RESERVED_SIZE];
+ struct syscom_config_s syscom_context_config;
+};
+
+struct ia_gofo_secondary_boot_config {
+ u32 length;
+ struct ia_gofo_version_s config_version;
+ struct ia_gofo_msg_version_list client_version_support;
+ u8 reserved1[IA_GOFO_BOOT_SECONDARY_RESERVED_FIELDS];
+ u16 checksum;
+ u8 padding[2];
+ u32 reserved2[IA_GOFO_BOOT_SECONDARY_RESERVED_SIZE];
+ struct syscom_config_s syscom_context_config;
+};
+
+#pragma pack(pop)
+
+#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401U
+#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801U
+#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802U
+#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803U
+#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804U
+#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805U
+#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806U
+#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000U
+#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010U
+
+enum ia_gofo_boot_state {
+ IA_GOFO_FW_BOOT_STATE_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000U,
+ IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000U,
+ IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000U,
+ IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010U,
+ IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020U,
+ IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030U,
+ IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100U,
+ IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200U,
+ IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300U,
+ IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_WDT_TIMEOUT = IA_GOFO_WDT_TIMEOUT_ERR,
+ IA_GOFO_FW_BOOT_STATE_WRONG_DATA_SECTION_UNPACKING = 0xdead0501U,
+ IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601U,
+ IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_FATAL_DME = IA_GOFO_MEM_FATAL_DME_ERR,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_LOCAL =
+ IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DIRTY =
+ IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_DTAG =
+ IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MEM_UNCORRECTABLE_CACHE =
+ IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR,
+ IA_GOFO_FW_BOOT_STATE_CRIT_DOUBLE_EXCEPTION =
+ IA_GOFO_DOUBLE_EXCEPTION_ERR,
+ IA_GOFO_FW_BOOT_STATE_CRIT_BIST_DMEM_FAULT_DETECTION_ERR =
+ IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR,
+ IA_GOFO_FW_BOOT_STATE_CRIT_DATA_INTEGRITY_FAILURE = 0xdead1010U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_SYSCOM_CONTEXT_INTEGRITY_FAILURE =
+ 0xdead1012U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014U,
+ IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015U,
+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U,
+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200U,
+ IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300U,
+ IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400U,
+ IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500U
+};
+
+#endif
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h
new file mode 100644
index 000000000000..7bb6fac585a3
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h
@@ -0,0 +1,175 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_COMMOM_ABI_H
+#define IPU7_FW_COMMOM_ABI_H
+
+#include <linux/types.h>
+
+#pragma pack(push, 1)
+typedef u32 ia_gofo_addr_t;
+
+#define IA_GOFO_ADDR_NULL (0U)
+
+struct ia_gofo_version_s {
+ u8 patch;
+ u8 subminor;
+ u8 minor;
+ u8 major;
+};
+
+#define IA_GOFO_MSG_VERSION_INIT(major_val, minor_val, subminor_val, patch_val)\
+ {.major = (major_val), .minor = (minor_val), .subminor = \
+ (subminor_val), .patch = (patch_val)}
+
+#define IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES (3U)
+#define IA_GOFO_MSG_RESERVED_SIZE (3U)
+
+struct ia_gofo_msg_version_list {
+ u8 num_versions;
+ u8 reserved[IA_GOFO_MSG_RESERVED_SIZE];
+ struct ia_gofo_version_s versions[IA_GOFO_MSG_VERSION_LIST_MAX_ENTRIES];
+};
+
+#pragma pack(pop)
+
+#define TLV_TYPE_PADDING (0U)
+
+#pragma pack(push, 1)
+
+#define IA_GOFO_ABI_BITS_PER_BYTE (8U)
+
+struct ia_gofo_tlv_header {
+ u16 tlv_type;
+ u16 tlv_len32;
+};
+
+struct ia_gofo_tlv_list {
+ u16 num_elems;
+ u16 head_offset;
+};
+
+#define TLV_ITEM_ALIGNMENT ((u32)sizeof(u32))
+#define TLV_MSG_ALIGNMENT ((u32)sizeof(u64))
+#define TLV_LIST_ALIGNMENT TLV_ITEM_ALIGNMENT
+#pragma pack(pop)
+
+#define IA_GOFO_MODULO(dividend, divisor) ((dividend) % (divisor))
+
+#define IA_GOFO_MSG_ERR_MAX_DETAILS (4U)
+#define IA_GOFO_MSG_ERR_OK (0U)
+#define IA_GOFO_MSG_ERR_UNSPECIFED (0xffffffffU)
+#define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U)
+#define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code)
+
+#pragma pack(push, 1)
+struct ia_gofo_msg_err {
+ u32 err_group;
+ u32 err_code;
+ u32 err_detail[IA_GOFO_MSG_ERR_MAX_DETAILS];
+};
+
+#pragma pack(pop)
+
+#define IA_GOFO_MSG_ERR_GROUP_APP_EXT_START (16U)
+#define IA_GOFO_MSG_ERR_GROUP_MAX (31U)
+#define IA_GOFO_MSG_ERR_GROUP_INTERNAL_START (IA_GOFO_MSG_ERR_GROUP_MAX + 1U)
+#define IA_GOFO_MSG_ERR_GROUP_RESERVED IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED
+#define IA_GOFO_MSG_ERR_GROUP_GENERAL 1
+
+enum ia_gofo_msg_err_general {
+ IA_GOFO_MSG_ERR_GENERAL_OK = IA_GOFO_MSG_ERR_OK,
+ IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_SMALL = 1,
+ IA_GOFO_MSG_ERR_GENERAL_MSG_TOO_LARGE = 2,
+ IA_GOFO_MSG_ERR_GENERAL_DEVICE_STATE = 3,
+ IA_GOFO_MSG_ERR_GENERAL_ALIGNMENT = 4,
+ IA_GOFO_MSG_ERR_GENERAL_INDIRECT_REF_PTR_INVALID = 5,
+ IA_GOFO_MSG_ERR_GENERAL_INVALID_MSG_TYPE = 6,
+ IA_GOFO_MSG_ERR_GENERAL_SYSCOM_FAIL = 7,
+ IA_GOFO_MSG_ERR_GENERAL_N
+};
+
+#pragma pack(push, 1)
+#define IA_GOFO_MSG_TYPE_RESERVED 0
+#define IA_GOFO_MSG_TYPE_INDIRECT 1
+#define IA_GOFO_MSG_TYPE_LOG 2
+#define IA_GOFO_MSG_TYPE_GENERAL_ERR 3
+
+struct ia_gofo_msg_header {
+ struct ia_gofo_tlv_header tlv_header;
+ struct ia_gofo_tlv_list msg_options;
+ u64 user_token;
+};
+
+struct ia_gofo_msg_header_ack {
+ struct ia_gofo_msg_header header;
+ struct ia_gofo_msg_err err;
+
+};
+
+struct ia_gofo_msg_general_err {
+ struct ia_gofo_msg_header_ack header;
+};
+
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+enum ia_gofo_msg_link_streaming_mode {
+ IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF = 0,
+ IA_GOFO_MSG_LINK_STREAMING_MODE_DOFF = 1,
+ IA_GOFO_MSG_LINK_STREAMING_MODE_BCLM = 2,
+ IA_GOFO_MSG_LINK_STREAMING_MODE_BCSM_FIX = 3,
+ IA_GOFO_MSG_LINK_STREAMING_MODE_N
+};
+
+enum ia_gofo_soc_pbk_instance_id {
+ IA_GOFO_SOC_PBK_ID0 = 0,
+ IA_GOFO_SOC_PBK_ID1 = 1,
+ IA_GOFO_SOC_PBK_ID_N
+};
+
+#define IA_GOFO_MSG_LINK_PBK_MAX_SLOTS (2U)
+
+struct ia_gofo_msg_indirect {
+ struct ia_gofo_msg_header header;
+ struct ia_gofo_tlv_header ref_header;
+ ia_gofo_addr_t ref_msg_ptr;
+};
+
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+#define IA_GOFO_MSG_LOG_MAX_PARAMS (4U)
+#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MIN (0U)
+
+#define IA_GOFO_MSG_LOG_DOC_FMT_ID_MAX (4095U)
+#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffffU)
+
+struct ia_gofo_msg_log_info {
+ u16 log_counter;
+ u8 msg_parameter_types;
+ /* [0:0] is_out_of_order, [1:3] logger_channel, [4:7] reserved */
+ u8 logger_opts;
+ u32 fmt_id;
+ u32 params[IA_GOFO_MSG_LOG_MAX_PARAMS];
+};
+
+struct ia_gofo_msg_log_info_ts {
+ u64 msg_ts;
+ struct ia_gofo_msg_log_info log_info;
+};
+
+struct ia_gofo_msg_log {
+ struct ia_gofo_msg_header header;
+ struct ia_gofo_msg_log_info_ts log_info_ts;
+};
+
+#pragma pack(pop)
+
+#define IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID (0U)
+#define IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID (1U)
+#define IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID (2U)
+
+#endif
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h
new file mode 100644
index 000000000000..c3f62aaedd86
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_config_abi.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_CONFIG_ABI_H
+#define IPU7_FW_CONFIG_ABI_H
+
+#include <linux/types.h>
+
+#define IPU_CONFIG_ABI_WDT_TIMER_DISABLED 0U
+#define IPU_CONFIG_ABI_CMD_TIMER_DISABLED 0U
+
+struct ipu7_wdt_abi {
+ u32 wdt_timer1_us;
+ u32 wdt_timer2_us;
+};
+
+#endif
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h
new file mode 100644
index 000000000000..f161a605c500
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_insys_config_abi.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_INSYS_CONFIG_ABI_H
+#define IPU7_FW_INSYS_CONFIG_ABI_H
+
+#include "ipu7_fw_boot_abi.h"
+#include "ipu7_fw_config_abi.h"
+#include "ipu7_fw_isys_abi.h"
+
+struct ipu7_insys_config {
+ u32 timeout_val_ms;
+ struct ia_gofo_logger_config logger_config;
+ struct ipu7_wdt_abi wdt_config;
+};
+
+#endif
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h
new file mode 100644
index 000000000000..c42d0b7a2627
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_isys_abi.h
@@ -0,0 +1,412 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_ISYS_ABI_H
+#define IPU7_FW_ISYS_ABI_H
+
+#include "ipu7_fw_common_abi.h"
+#include "ipu7_fw_isys_abi.h"
+
+#define IPU_INSYS_MAX_OUTPUT_QUEUES (3U)
+#define IPU_INSYS_STREAM_ID_MAX (16U)
+
+#define IPU_INSYS_MAX_INPUT_QUEUES (IPU_INSYS_STREAM_ID_MAX + 1U)
+#define IPU_INSYS_OUTPUT_FIRST_QUEUE (0U)
+#define IPU_INSYS_OUTPUT_LAST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES - 1U)
+#define IPU_INSYS_OUTPUT_MSG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE)
+#define IPU_INSYS_OUTPUT_LOG_QUEUE (IPU_INSYS_OUTPUT_FIRST_QUEUE + 1U)
+#define IPU_INSYS_OUTPUT_RESERVED_QUEUE (IPU_INSYS_OUTPUT_LAST_QUEUE)
+#define IPU_INSYS_INPUT_FIRST_QUEUE (IPU_INSYS_MAX_OUTPUT_QUEUES)
+#define IPU_INSYS_INPUT_LAST_QUEUE \
+ (IPU_INSYS_INPUT_FIRST_QUEUE + IPU_INSYS_MAX_INPUT_QUEUES - 1U)
+#define IPU_INSYS_INPUT_DEV_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE)
+#define IPU_INSYS_INPUT_MSG_QUEUE (IPU_INSYS_INPUT_FIRST_QUEUE + 1U)
+#define IPU_INSYS_INPUT_MSG_MAX_QUEUE (IPU_INSYS_MAX_INPUT_QUEUES - 1U)
+
+#define MAX_OPINS_FOR_SINGLE_IPINS (3U)
+#define DEV_SEND_QUEUE_SIZE (IPU_INSYS_STREAM_ID_MAX)
+
+#define PIN_PLANES_MAX (4U)
+
+#define INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_INPUT \
+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES
+
+typedef u64 ipu7_insys_return_token;
+
+enum ipu7_insys_resp_type {
+ IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0,
+ IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1,
+ IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2,
+ IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3,
+ IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4,
+ IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5,
+ IPU_INSYS_RESP_TYPE_PIN_DATA_READY = 6,
+ IPU_INSYS_RESP_TYPE_FRAME_SOF = 7,
+ IPU_INSYS_RESP_TYPE_FRAME_EOF = 8,
+ IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9,
+ IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10,
+ IPU_INSYS_RESP_TYPE_PWM_IRQ = 11,
+ N_IPU_INSYS_RESP_TYPE
+};
+
+enum ipu7_insys_send_type {
+ IPU_INSYS_SEND_TYPE_STREAM_OPEN = 0,
+ IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1,
+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE = 2,
+ IPU_INSYS_SEND_TYPE_STREAM_ABORT = 3,
+ IPU_INSYS_SEND_TYPE_STREAM_FLUSH = 4,
+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE = 5,
+ N_IPU_INSYS_SEND_TYPE
+};
+
+enum ipu7_insys_mipi_vc {
+ IPU_INSYS_MIPI_VC_0 = 0,
+ IPU_INSYS_MIPI_VC_1 = 1,
+ IPU_INSYS_MIPI_VC_2 = 2,
+ IPU_INSYS_MIPI_VC_3 = 3,
+ IPU_INSYS_MIPI_VC_4 = 4,
+ IPU_INSYS_MIPI_VC_5 = 5,
+ IPU_INSYS_MIPI_VC_6 = 6,
+ IPU_INSYS_MIPI_VC_7 = 7,
+ IPU_INSYS_MIPI_VC_8 = 8,
+ IPU_INSYS_MIPI_VC_9 = 9,
+ IPU_INSYS_MIPI_VC_10 = 10,
+ IPU_INSYS_MIPI_VC_11 = 11,
+ IPU_INSYS_MIPI_VC_12 = 12,
+ IPU_INSYS_MIPI_VC_13 = 13,
+ IPU_INSYS_MIPI_VC_14 = 14,
+ IPU_INSYS_MIPI_VC_15 = 15,
+ N_IPU_INSYS_MIPI_VC
+};
+
+enum ipu7_insys_mipi_port {
+ IPU_INSYS_MIPI_PORT_0 = 0,
+ IPU_INSYS_MIPI_PORT_1 = 1,
+ IPU_INSYS_MIPI_PORT_2 = 2,
+ IPU_INSYS_MIPI_PORT_3 = 3,
+ IPU_INSYS_MIPI_PORT_4 = 4,
+ IPU_INSYS_MIPI_PORT_5 = 5,
+ NA_IPU_INSYS_MIPI_PORT
+};
+
+enum ipu7_insys_frame_format_type {
+ IPU_INSYS_FRAME_FORMAT_NV11 = 0,
+ IPU_INSYS_FRAME_FORMAT_NV12 = 1,
+ IPU_INSYS_FRAME_FORMAT_NV12_16 = 2,
+ IPU_INSYS_FRAME_FORMAT_NV12_TILEY = 3,
+ IPU_INSYS_FRAME_FORMAT_NV16 = 4,
+ IPU_INSYS_FRAME_FORMAT_NV21 = 5,
+ IPU_INSYS_FRAME_FORMAT_NV61 = 6,
+ IPU_INSYS_FRAME_FORMAT_YV12 = 7,
+ IPU_INSYS_FRAME_FORMAT_YV16 = 8,
+ IPU_INSYS_FRAME_FORMAT_YUV420 = 9,
+ IPU_INSYS_FRAME_FORMAT_YUV420_10 = 10,
+ IPU_INSYS_FRAME_FORMAT_YUV420_12 = 11,
+ IPU_INSYS_FRAME_FORMAT_YUV420_14 = 12,
+ IPU_INSYS_FRAME_FORMAT_YUV420_16 = 13,
+ IPU_INSYS_FRAME_FORMAT_YUV422 = 14,
+ IPU_INSYS_FRAME_FORMAT_YUV422_16 = 15,
+ IPU_INSYS_FRAME_FORMAT_UYVY = 16,
+ IPU_INSYS_FRAME_FORMAT_YUYV = 17,
+ IPU_INSYS_FRAME_FORMAT_YUV444 = 18,
+ IPU_INSYS_FRAME_FORMAT_YUV_LINE = 19,
+ IPU_INSYS_FRAME_FORMAT_RAW8 = 20,
+ IPU_INSYS_FRAME_FORMAT_RAW10 = 21,
+ IPU_INSYS_FRAME_FORMAT_RAW12 = 22,
+ IPU_INSYS_FRAME_FORMAT_RAW14 = 23,
+ IPU_INSYS_FRAME_FORMAT_RAW16 = 24,
+ IPU_INSYS_FRAME_FORMAT_RGB565 = 25,
+ IPU_INSYS_FRAME_FORMAT_PLANAR_RGB888 = 26,
+ IPU_INSYS_FRAME_FORMAT_RGBA888 = 27,
+ IPU_INSYS_FRAME_FORMAT_QPLANE6 = 28,
+ IPU_INSYS_FRAME_FORMAT_BINARY_8 = 29,
+ IPU_INSYS_FRAME_FORMAT_Y_8 = 30,
+ IPU_INSYS_FRAME_FORMAT_ARGB888 = 31,
+ IPU_INSYS_FRAME_FORMAT_BGRA888 = 32,
+ IPU_INSYS_FRAME_FORMAT_ABGR888 = 33,
+ N_IPU_INSYS_FRAME_FORMAT
+};
+
+#define IPU_INSYS_FRAME_FORMAT_RAW (IPU_INSYS_FRAME_FORMAT_RAW16)
+#define N_IPU_INSYS_MIPI_DATA_TYPE 0x40
+
+enum ipu7_insys_mipi_dt_rename_mode {
+ IPU_INSYS_MIPI_DT_NO_RENAME = 0,
+ IPU_INSYS_MIPI_DT_RENAMED_MODE = 1,
+ N_IPU_INSYS_MIPI_DT_MODE
+};
+
+#define IPU_INSYS_SEND_MSG_ENABLED 1U
+#define IPU_INSYS_SEND_MSG_DISABLED 0U
+
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0)
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1)
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2)
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3)
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4)
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5)
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6)
+#define IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7)
+#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_RESP ( \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF | \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF | \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED | \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED)
+#define IPU_INSYS_STREAM_SYNC_MSG_ENABLE_MSG_SEND_IRQ ( \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF | \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF | \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED | \
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED)
+
+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0)
+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1)
+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2)
+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3)
+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4)
+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5)
+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6)
+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7)
+#define IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8)
+#define IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9)
+#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \
+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \
+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \
+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \
+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \
+ IPU_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK)
+#define IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \
+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \
+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \
+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \
+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \
+ IPU_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK)
+
+#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0)
+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1)
+#define IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2)
+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3)
+#define IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4)
+#define IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5)
+#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \
+ IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \
+ IPU_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \
+ IPU_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY)
+#define IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \
+ IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \
+ IPU_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \
+ IPU_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY)
+
+enum ipu7_insys_output_link_dest {
+ IPU_INSYS_OUTPUT_LINK_DEST_MEM = 0,
+ IPU_INSYS_OUTPUT_LINK_DEST_PSYS = 1,
+ IPU_INSYS_OUTPUT_LINK_DEST_IPU_EXTERNAL = 2
+};
+
+enum ipu7_insys_dpcm_type {
+ IPU_INSYS_DPCM_TYPE_DISABLED = 0,
+ IPU_INSYS_DPCM_TYPE_10_8_10 = 1,
+ IPU_INSYS_DPCM_TYPE_12_8_12 = 2,
+ IPU_INSYS_DPCM_TYPE_12_10_12 = 3,
+ N_IPU_INSYS_DPCM_TYPE
+};
+
+enum ipu7_insys_dpcm_predictor {
+ IPU_INSYS_DPCM_PREDICTOR_1 = 0,
+ IPU_INSYS_DPCM_PREDICTOR_2 = 1,
+ N_IPU_INSYS_DPCM_PREDICTOR
+};
+
+enum ipu7_insys_send_queue_token_flag {
+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE = 0,
+ IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_FLUSH_FORCE = 1
+};
+
+#pragma pack(push, 1)
+struct ipu7_insys_resolution {
+ u32 width;
+ u32 height;
+};
+
+struct ipu7_insys_capture_output_pin_payload {
+ u64 user_token;
+ ia_gofo_addr_t addr;
+ u8 pad[4];
+};
+
+struct ipu7_insys_output_link {
+ u32 buffer_lines;
+ u16 foreign_key;
+ u16 granularity_pointer_update;
+ u8 msg_link_streaming_mode;
+ u8 pbk_id;
+ u8 pbk_slot_id;
+ u8 dest;
+ u8 use_sw_managed;
+ u8 is_snoop;
+ u8 pad[2];
+};
+
+struct ipu7_insys_output_cropping {
+ u16 line_top;
+ u16 line_bottom;
+};
+
+struct ipu7_insys_output_dpcm {
+ u8 enable;
+ u8 type;
+ u8 predictor;
+ u8 pad;
+};
+
+struct ipu7_insys_output_pin {
+ struct ipu7_insys_output_link link;
+ struct ipu7_insys_output_cropping crop;
+ struct ipu7_insys_output_dpcm dpcm;
+ u32 stride;
+ u16 ft;
+ u8 send_irq;
+ u8 input_pin_id;
+ u8 early_ack_en;
+ u8 pad[3];
+};
+
+struct ipu7_insys_input_pin {
+ struct ipu7_insys_resolution input_res;
+ u16 sync_msg_map;
+ u8 dt;
+ u8 disable_mipi_unpacking;
+ u8 dt_rename_mode;
+ u8 mapped_dt;
+ u8 pad[2];
+};
+
+struct ipu7_insys_stream_cfg {
+ struct ipu7_insys_input_pin input_pins[4];
+ struct ipu7_insys_output_pin output_pins[4];
+ u16 stream_msg_map;
+ u8 port_id;
+ u8 vc;
+ u8 nof_input_pins;
+ u8 nof_output_pins;
+ u8 pad[2];
+};
+
+struct ipu7_insys_buffset {
+ struct ipu7_insys_capture_output_pin_payload output_pins[4];
+ u8 capture_msg_map;
+ u8 frame_id;
+ u8 skip_frame;
+ u8 pad[5];
+};
+
+struct ipu7_insys_resp {
+ u64 buf_id;
+ struct ipu7_insys_capture_output_pin_payload pin;
+ struct ia_gofo_msg_err error_info;
+ u32 timestamp[2];
+ u8 type;
+ u8 msg_link_streaming_mode;
+ u8 stream_id;
+ u8 pin_id;
+ u8 frame_id;
+ u8 skip_frame;
+ u8 pad[2];
+};
+
+struct ipu7_insys_resp_queue_token {
+ struct ipu7_insys_resp resp_info;
+};
+
+struct ipu7_insys_send_queue_token {
+ u64 buf_handle;
+ ia_gofo_addr_t addr;
+ u16 stream_id;
+ u8 send_type;
+ u8 flag;
+};
+
+#pragma pack(pop)
+
+enum insys_msg_err_stream {
+ INSYS_MSG_ERR_STREAM_OK = IA_GOFO_MSG_ERR_OK,
+ INSYS_MSG_ERR_STREAM_STREAM_ID = 1,
+ INSYS_MSG_ERR_STREAM_MAX_OPINS = 2,
+ INSYS_MSG_ERR_STREAM_MAX_IPINS = 3,
+ INSYS_MSG_ERR_STREAM_STREAM_MESSAGES_MAP = 4,
+ INSYS_MSG_ERR_STREAM_SYNC_MESSAGES_MAP = 5,
+ INSYS_MSG_ERR_STREAM_SENSOR_TYPE = 6,
+ INSYS_MSG_ERR_STREAM_FOREIGN_KEY = 7,
+ INSYS_MSG_ERR_STREAM_STREAMING_MODE = 8,
+ INSYS_MSG_ERR_STREAM_DPCM_EN = 9,
+ INSYS_MSG_ERR_STREAM_DPCM_TYPE = 10,
+ INSYS_MSG_ERR_STREAM_DPCM_PREDICTOR = 11,
+ INSYS_MSG_ERR_STREAM_GRANULARITY_POINTER_UPDATE = 12,
+ INSYS_MSG_ERR_STREAM_MPF_LUT_ENTRY_RESOURCES_BUSY = 13,
+ INSYS_MSG_ERR_STREAM_MPF_DEV_ID = 14,
+ INSYS_MSG_ERR_STREAM_BUFFER_LINES = 15,
+ INSYS_MSG_ERR_STREAM_IPIN_ID = 16,
+ INSYS_MSG_ERR_STREAM_DATA_TYPE = 17,
+ INSYS_MSG_ERR_STREAM_STREAMING_PROTOCOL_STATE = 18,
+ INSYS_MSG_ERR_STREAM_SYSCOM_FLUSH = 19,
+ INSYS_MSG_ERR_STREAM_MIPI_VC = 20,
+ INSYS_MSG_ERR_STREAM_STREAM_SRC = 21,
+ INSYS_MSG_ERR_STREAM_PBK_ID = 22,
+ INSYS_MSG_ERR_STREAM_CMD_QUEUE_DEALLOCATE = 23,
+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES = 24,
+ INSYS_MSG_ERR_STREAM_IPIN_CONFIGURATION = 25,
+ INSYS_MSG_ERR_STREAM_INVALID_STATE = 26,
+ INSYS_MSG_ERR_STREAM_SW_MANAGED = 27,
+ INSYS_MSG_ERR_STREAM_PBK_SLOT_ID = 28,
+ INSYS_MSG_ERR_STREAM_FLUSH_TIMEOUT = 29,
+ INSYS_MSG_ERR_STREAM_IPIN_WIDTH = 30,
+ INSYS_MSG_ERR_STREAM_IPIN_HEIGHT = 31,
+ INSYS_MSG_ERR_STREAM_OUTPUT_PIN_EARLY_ACK_EN = 32,
+ INSYS_MSG_ERR_STREAM_INCONSISTENT_PARAMS = 33,
+ INSYS_MSG_ERR_STREAM_PLANE_COUNT = 34,
+ INSYS_MSG_ERR_STREAM_FRAME_FORMAT_TYPE = 35,
+ INSYS_MSG_ERR_STREAM_INSUFFICIENT_RESOURCES_OUTPUT = 36,
+ INSYS_MSG_ERR_STREAM_WIDTH_OUTPUT_SIZE = 37,
+ INSYS_MSG_ERR_STREAM_CLOSED = 38,
+ INSYS_MSG_ERR_STREAM_N
+};
+
+enum insys_msg_err_capture {
+ INSYS_MSG_ERR_CAPTURE_OK = IA_GOFO_MSG_ERR_OK,
+ INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1,
+ INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2,
+ INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3,
+ INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4,
+ INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5,
+ INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6,
+ INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7,
+ INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8,
+ INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9,
+ INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10,
+ INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11,
+ INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12,
+ INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21,
+ INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22,
+ INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23,
+ INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24,
+ INSYS_MSG_ERR_CAPTURE_N
+};
+
+enum insys_msg_err_groups {
+ INSYS_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED,
+ INSYS_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL,
+ INSYS_MSG_ERR_GROUP_STREAM = 2,
+ INSYS_MSG_ERR_GROUP_CAPTURE = 3,
+ INSYS_MSG_ERR_GROUP_N,
+};
+
+#endif
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h
new file mode 100644
index 000000000000..8a78dd0936df
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h
@@ -0,0 +1,465 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_MSG_ABI_H
+#define IPU7_FW_MSG_ABI_H
+
+#include "ipu7_fw_common_abi.h"
+
+#pragma pack(push, 1)
+enum ipu7_msg_type {
+ IPU_MSG_TYPE_RESERVED = IA_GOFO_MSG_TYPE_RESERVED,
+ IPU_MSG_TYPE_INDIRECT = IA_GOFO_MSG_TYPE_INDIRECT,
+ IPU_MSG_TYPE_DEV_LOG = IA_GOFO_MSG_TYPE_LOG,
+ IPU_MSG_TYPE_GENERAL_ERR = IA_GOFO_MSG_TYPE_GENERAL_ERR,
+ IPU_MSG_TYPE_DEV_OPEN = 4,
+ IPU_MSG_TYPE_DEV_OPEN_ACK = 5,
+ IPU_MSG_TYPE_GRAPH_OPEN = 6,
+ IPU_MSG_TYPE_GRAPH_OPEN_ACK = 7,
+ IPU_MSG_TYPE_TASK_REQ = 8,
+ IPU_MSG_TYPE_TASK_DONE = 9,
+ IPU_MSG_TYPE_GRAPH_CLOSE = 10,
+ IPU_MSG_TYPE_GRAPH_CLOSE_ACK = 11,
+ IPU_MSG_TYPE_DEV_CLOSE = 12,
+ IPU_MSG_TYPE_DEV_CLOSE_ACK = 13,
+ IPU_MSG_TYPE_TERM_EVENT = 14,
+ IPU_MSG_TYPE_N,
+};
+
+#define IPU_MSG_MAX_NODE_TERMS (64U)
+#define IPU_MSG_MAX_FRAGS (7U)
+
+enum ipu7_msg_node_type {
+ IPU_MSG_NODE_TYPE_PAD = 0,
+ IPU_MSG_NODE_TYPE_BASE,
+ IPU_MSG_NODE_TYPE_N
+};
+
+#define IPU_MSG_NODE_MAX_DEVICES (128U)
+#define DEB_NUM_UINT32 (IPU_MSG_NODE_MAX_DEVICES / (sizeof(u32) * 8U))
+
+typedef u32 ipu7_msg_teb_t[2];
+typedef u32 ipu7_msg_deb_t[DEB_NUM_UINT32];
+
+#define IPU_MSG_NODE_MAX_ROUTE_ENABLES (128U)
+#define RBM_NUM_UINT32 (IPU_MSG_NODE_MAX_ROUTE_ENABLES / (sizeof(u32) * 8U))
+
+typedef u32 ipu7_msg_rbm_t[RBM_NUM_UINT32];
+
+enum ipu7_msg_node_profile_type {
+ IPU_MSG_NODE_PROFILE_TYPE_PAD = 0,
+ IPU_MSG_NODE_PROFILE_TYPE_BASE,
+ IPU_MSG_NODE_PROFILE_TYPE_CB,
+ IPU_MSG_NODE_PROFILE_TYPE_N
+};
+
+struct ipu7_msg_node_profile {
+ struct ia_gofo_tlv_header tlv_header;
+ ipu7_msg_teb_t teb;
+};
+
+struct ipu7_msg_cb_profile {
+ struct ipu7_msg_node_profile profile_base;
+ ipu7_msg_deb_t deb;
+ ipu7_msg_rbm_t rbm;
+ ipu7_msg_rbm_t reb;
+};
+
+#define IPU_MSG_NODE_MAX_PROFILES (2U)
+#define IPU_MSG_NODE_DEF_PROFILE_IDX (0U)
+#define IPU_MSG_NODE_RSRC_ID_EXT_IP (0xffU)
+
+#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffffU)
+#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffffU)
+#define IPU_MSG_NODE_RSRC_ID_IS (0xfeU)
+
+struct ipu7_msg_node {
+ struct ia_gofo_tlv_header tlv_header;
+ u8 node_rsrc_id;
+ u8 node_ctx_id;
+ u8 num_frags;
+ u8 reserved[1];
+ struct ia_gofo_tlv_list profiles_list;
+ struct ia_gofo_tlv_list terms_list;
+ struct ia_gofo_tlv_list node_options;
+};
+
+enum ipu7_msg_node_option_types {
+ IPU_MSG_NODE_OPTION_TYPES_PADDING = 0,
+ IPU_MSG_NODE_OPTION_TYPES_N
+};
+
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+
+enum ipu7_msg_link_type {
+ IPU_MSG_LINK_TYPE_PAD = 0,
+ IPU_MSG_LINK_TYPE_GENERIC = 1,
+ IPU_MSG_LINK_TYPE_N
+};
+
+enum ipu7_msg_link_option_types {
+ IPU_MSG_LINK_OPTION_TYPES_PADDING = 0,
+ IPU_MSG_LINK_OPTION_TYPES_CMPRS = 1,
+ IPU_MSG_LINK_OPTION_TYPES_N
+};
+
+enum ipu7_msg_link_cmprs_option_bit_depth {
+ IPU_MSG_LINK_CMPRS_OPTION_8BPP = 0,
+ IPU_MSG_LINK_CMPRS_OPTION_10BPP = 1,
+ IPU_MSG_LINK_CMPRS_OPTION_12BPP = 2,
+};
+
+#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM (128U)
+#define IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE (5U)
+#define IPU_MSG_LINK_CMPRS_SPACE_SAVING_NUM_MAX \
+ (IPU_MSG_LINK_CMPRS_SPACE_SAVING_DENOM - 1U)
+
+struct ipu7_msg_link_cmprs_plane_desc {
+ u8 plane_enable;
+ u8 cmprs_enable;
+ u8 encoder_plane_id;
+ u8 decoder_plane_id;
+ u8 cmprs_is_lossy;
+ u8 cmprs_is_footprint;
+ u8 bit_depth;
+ u8 space_saving_numerator;
+ u32 pixels_offset;
+ u32 ts_offset;
+ u32 tile_row_to_tile_row_stride;
+ u32 rows_of_tiles;
+ u32 lossy_cfg[IPU_MSG_LINK_CMPRS_LOSSY_CFG_PAYLOAD_SIZE];
+};
+
+#define IPU_MSG_LINK_CMPRS_MAX_PLANES (2U)
+#define IPU_MSG_LINK_CMPRS_NO_ALIGN_INTERVAL (0U)
+#define IPU_MSG_LINK_CMPRS_MIN_ALIGN_INTERVAL (16U)
+#define IPU_MSG_LINK_CMPRS_MAX_ALIGN_INTERVAL (1024U)
+struct ipu7_msg_link_cmprs_option {
+ struct ia_gofo_tlv_header header;
+ u32 cmprs_buf_size;
+ u16 align_interval;
+ u8 reserved[2];
+ struct ipu7_msg_link_cmprs_plane_desc plane_descs[2];
+};
+
+struct ipu7_msg_link_ep {
+ u8 node_ctx_id;
+ u8 term_id;
+};
+
+struct ipu7_msg_link_ep_pair {
+ struct ipu7_msg_link_ep ep_src;
+ struct ipu7_msg_link_ep ep_dst;
+};
+
+#define IPU_MSG_LINK_FOREIGN_KEY_NONE (65535U)
+#define IPU_MSG_LINK_FOREIGN_KEY_MAX (64U)
+#define IPU_MSG_LINK_PBK_ID_DONT_CARE (255U)
+#define IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U)
+#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xffU)
+
+struct ipu7_msg_link {
+ struct ia_gofo_tlv_header tlv_header;
+ struct ipu7_msg_link_ep_pair endpoints;
+ u16 foreign_key;
+ u8 streaming_mode;
+ u8 pbk_id;
+ u8 pbk_slot_id;
+ u8 delayed_link;
+ u8 reserved[2];
+ struct ia_gofo_tlv_list link_options;
+};
+
+#pragma pack(pop)
+
+enum ipu7_msg_dev_state {
+ IPU_MSG_DEV_STATE_CLOSED = 0,
+ IPU_MSG_DEV_STATE_OPEN_WAIT = 1,
+ IPU_MSG_DEV_STATE_OPEN = 2,
+ IPU_MSG_DEV_STATE_CLOSE_WAIT = 3,
+ IPU_MSG_DEV_STATE_N
+};
+
+enum ipu7_msg_graph_state {
+ IPU_MSG_GRAPH_STATE_CLOSED = 0,
+ IPU_MSG_GRAPH_STATE_OPEN_WAIT = 1,
+ IPU_MSG_GRAPH_STATE_OPEN = 2,
+ IPU_MSG_GRAPH_STATE_CLOSE_WAIT = 3,
+ IPU_MSG_GRAPH_STATE_N
+};
+
+enum ipu7_msg_task_state {
+ IPU_MSG_TASK_STATE_DONE = 0,
+ IPU_MSG_TASK_STATE_WAIT_DONE = 1,
+ IPU_MSG_TASK_STATE_N
+};
+
+enum ipu7_msg_err_groups {
+ IPU_MSG_ERR_GROUP_RESERVED = IA_GOFO_MSG_ERR_GROUP_RESERVED,
+ IPU_MSG_ERR_GROUP_GENERAL = IA_GOFO_MSG_ERR_GROUP_GENERAL,
+ IPU_MSG_ERR_GROUP_DEVICE = 2,
+ IPU_MSG_ERR_GROUP_GRAPH = 3,
+ IPU_MSG_ERR_GROUP_TASK = 4,
+ IPU_MSG_ERR_GROUP_N,
+};
+
+#pragma pack(push, 1)
+struct ipu7_msg_task {
+ struct ia_gofo_msg_header header;
+ u8 graph_id;
+ u8 profile_idx;
+ u8 node_ctx_id;
+ u8 frame_id;
+ u8 frag_id;
+ u8 req_done_msg;
+ u8 req_done_irq;
+ u8 reserved[1];
+ ipu7_msg_teb_t payload_reuse_bm;
+ ia_gofo_addr_t term_buffers[IPU_MSG_MAX_NODE_TERMS];
+};
+
+struct ipu7_msg_task_done {
+ struct ia_gofo_msg_header_ack header;
+ u8 graph_id;
+ u8 frame_id;
+ u8 node_ctx_id;
+ u8 profile_idx;
+ u8 frag_id;
+ u8 reserved[3];
+};
+
+enum ipu7_msg_err_task {
+ IPU_MSG_ERR_TASK_OK = IA_GOFO_MSG_ERR_OK,
+ IPU_MSG_ERR_TASK_GRAPH_ID = 1,
+ IPU_MSG_ERR_TASK_NODE_CTX_ID = 2,
+ IPU_MSG_ERR_TASK_PROFILE_IDX = 3,
+ IPU_MSG_ERR_TASK_CTX_MEMORY_TASK = 4,
+ IPU_MSG_ERR_TASK_TERM_PAYLOAD_PTR = 5,
+ IPU_MSG_ERR_TASK_FRAME_ID = 6,
+ IPU_MSG_ERR_TASK_FRAG_ID = 7,
+ IPU_MSG_ERR_TASK_EXEC_EXT = 8,
+ IPU_MSG_ERR_TASK_EXEC_SBX = 9,
+ IPU_MSG_ERR_TASK_EXEC_INT = 10,
+ IPU_MSG_ERR_TASK_EXEC_UNKNOWN = 11,
+ IPU_MSG_ERR_TASK_PRE_EXEC = 12,
+ IPU_MSG_ERR_TASK_N
+};
+
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+enum ipu7_msg_term_type {
+ IPU_MSG_TERM_TYPE_PAD = 0,
+ IPU_MSG_TERM_TYPE_BASE,
+ IPU_MSG_TERM_TYPE_N,
+};
+
+#define IPU_MSG_TERM_EVENT_TYPE_NONE 0U
+#define IPU_MSG_TERM_EVENT_TYPE_PROGRESS 1U
+#define IPU_MSG_TERM_EVENT_TYPE_N (IPU_MSG_TERM_EVENT_TYPE_PROGRESS + 1U)
+
+struct ipu7_msg_term {
+ struct ia_gofo_tlv_header tlv_header;
+ u8 term_id;
+ u8 event_req_bm;
+ u8 reserved[2];
+ u32 payload_size;
+ struct ia_gofo_tlv_list term_options;
+};
+
+enum ipu7_msg_term_option_types {
+ IPU_MSG_TERM_OPTION_TYPES_PADDING = 0,
+ IPU_MSG_TERM_OPTION_TYPES_N
+};
+
+struct ipu7_msg_term_event {
+ struct ia_gofo_msg_header header;
+ u8 graph_id;
+ u8 frame_id;
+ u8 node_ctx_id;
+ u8 profile_idx;
+ u8 frag_id;
+ u8 term_id;
+ u8 event_type;
+ u8 reserved[1];
+ u64 event_ts;
+};
+
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+#define IPU_MSG_DEVICE_SEND_MSG_ENABLED 1U
+#define IPU_MSG_DEVICE_SEND_MSG_DISABLED 0U
+
+#define IPU_MSG_DEVICE_OPEN_SEND_RESP BIT(0)
+#define IPU_MSG_DEVICE_OPEN_SEND_IRQ BIT(1)
+
+#define IPU_MSG_DEVICE_CLOSE_SEND_RESP BIT(0)
+#define IPU_MSG_DEVICE_CLOSE_SEND_IRQ BIT(1)
+
+struct ipu7_msg_dev_open {
+ struct ia_gofo_msg_header header;
+ u32 max_graphs;
+ u8 dev_msg_map;
+ u8 enable_power_gating;
+ u8 reserved[2];
+};
+
+struct ipu7_msg_dev_open_ack {
+ struct ia_gofo_msg_header_ack header;
+};
+
+struct ipu7_msg_dev_close {
+ struct ia_gofo_msg_header header;
+ u8 dev_msg_map;
+ u8 reserved[7];
+};
+
+struct ipu7_msg_dev_close_ack {
+ struct ia_gofo_msg_header_ack header;
+};
+
+enum ipu7_msg_err_device {
+ IPU_MSG_ERR_DEVICE_OK = IA_GOFO_MSG_ERR_OK,
+ IPU_MSG_ERR_DEVICE_MAX_GRAPHS = 1,
+ IPU_MSG_ERR_DEVICE_MSG_MAP = 2,
+ IPU_MSG_ERR_DEVICE_N
+};
+
+#pragma pack(pop)
+
+#pragma pack(push, 1)
+#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU)
+#define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U
+#define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U
+
+#define IPU_MSG_GRAPH_OPEN_SEND_RESP BIT(0)
+#define IPU_MSG_GRAPH_OPEN_SEND_IRQ BIT(1)
+
+#define IPU_MSG_GRAPH_CLOSE_SEND_RESP BIT(0)
+#define IPU_MSG_GRAPH_CLOSE_SEND_IRQ BIT(1)
+
+struct ipu7_msg_graph_open {
+ struct ia_gofo_msg_header header;
+ struct ia_gofo_tlv_list nodes;
+ struct ia_gofo_tlv_list links;
+ u8 graph_id;
+ u8 graph_msg_map;
+ u8 reserved[6];
+};
+
+enum ipu7_msg_graph_ack_option_types {
+ IPU_MSG_GRAPH_ACK_OPTION_TYPES_PADDING = 0,
+ IPU_MSG_GRAPH_ACK_TASK_Q_INFO,
+ IPU_MSG_GRAPH_ACK_OPTION_TYPES_N
+};
+
+struct ipu7_msg_graph_open_ack_task_q_info {
+ struct ia_gofo_tlv_header header;
+ u8 node_ctx_id;
+ u8 q_id;
+ u8 reserved[2];
+};
+
+struct ipu7_msg_graph_open_ack {
+ struct ia_gofo_msg_header_ack header;
+ u8 graph_id;
+ u8 reserved[7];
+};
+
+struct ipu7_msg_graph_close {
+ struct ia_gofo_msg_header header;
+ u8 graph_id;
+ u8 graph_msg_map;
+ u8 reserved[6];
+};
+
+struct ipu7_msg_graph_close_ack {
+ struct ia_gofo_msg_header_ack header;
+ u8 graph_id;
+ u8 reserved[7];
+};
+
+enum ipu7_msg_err_graph {
+ IPU_MSG_ERR_GRAPH_OK = IA_GOFO_MSG_ERR_OK,
+ IPU_MSG_ERR_GRAPH_GRAPH_STATE = 1,
+ IPU_MSG_ERR_GRAPH_MAX_GRAPHS = 2,
+ IPU_MSG_ERR_GRAPH_GRAPH_ID = 3,
+ IPU_MSG_ERR_GRAPH_NODE_CTX_ID = 4,
+ IPU_MSG_ERR_GRAPH_NODE_RSRC_ID = 5,
+ IPU_MSG_ERR_GRAPH_PROFILE_IDX = 6,
+ IPU_MSG_ERR_GRAPH_TERM_ID = 7,
+ IPU_MSG_ERR_GRAPH_TERM_PAYLOAD_SIZE = 8,
+ IPU_MSG_ERR_GRAPH_LINK_NODE_CTX_ID = 9,
+ IPU_MSG_ERR_GRAPH_LINK_TERM_ID = 10,
+ IPU_MSG_ERR_GRAPH_PROFILE_TYPE = 11,
+ IPU_MSG_ERR_GRAPH_NUM_FRAGS = 12,
+ IPU_MSG_ERR_GRAPH_QUEUE_ID_USAGE = 13,
+ IPU_MSG_ERR_GRAPH_QUEUE_OPEN = 14,
+ IPU_MSG_ERR_GRAPH_QUEUE_CLOSE = 15,
+ IPU_MSG_ERR_GRAPH_QUEUE_ID_TASK_REQ_MISMATCH = 16,
+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_FGRAPH = 17,
+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE = 18,
+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_NODE_PROFILE = 19,
+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_TERM = 20,
+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_LINK = 21,
+ IPU_MSG_ERR_GRAPH_CTX_MSG_MAP = 22,
+ IPU_MSG_ERR_GRAPH_CTX_FOREIGN_KEY = 23,
+ IPU_MSG_ERR_GRAPH_CTX_STREAMING_MODE = 24,
+ IPU_MSG_ERR_GRAPH_CTX_PBK_RSRC = 25,
+ IPU_MSG_ERR_GRAPH_UNSUPPORTED_EVENT_TYPE = 26,
+ IPU_MSG_ERR_GRAPH_TOO_MANY_EVENTS = 27,
+ IPU_MSG_ERR_GRAPH_CTX_MEMORY_CMPRS = 28,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_ALIGN_INTERVAL = 29,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_PLANE_ID = 30,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_UNSUPPORTED_MODE = 31,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_BIT_DEPTH = 32,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_STRIDE_ALIGNMENT = 33,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_SUB_BUFFER_ALIGNMENT = 34,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_ORDER = 35,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_LAYOUT_OVERLAP = 36,
+ IPU_MSG_ERR_GRAPH_CTX_CMPRS_BUFFER_TOO_SMALL = 37,
+ IPU_MSG_ERR_GRAPH_CTX_DELAYED_LINK = 38,
+ IPU_MSG_ERR_GRAPH_N
+};
+
+#pragma pack(pop)
+
+#define FWPS_MSG_ABI_MAX_INPUT_QUEUES (60U)
+#define FWPS_MSG_ABI_MAX_OUTPUT_QUEUES (2U)
+#define FWPS_MSG_ABI_MAX_QUEUES \
+ (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES + FWPS_MSG_ABI_MAX_INPUT_QUEUES)
+
+#define FWPS_MSG_ABI_OUT_ACK_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_ACK_QUEUE_ID)
+#define FWPS_MSG_ABI_OUT_LOG_QUEUE_ID (IA_GOFO_MSG_ABI_OUT_LOG_QUEUE_ID)
+#if (FWPS_MSG_ABI_OUT_LOG_QUEUE_ID >= FWPS_MSG_ABI_MAX_OUTPUT_QUEUES)
+#error "Maximum output queues configuration is too small to fit ACK and LOG \
+queues"
+#endif
+#define FWPS_MSG_ABI_IN_DEV_QUEUE_ID (IA_GOFO_MSG_ABI_IN_DEV_QUEUE_ID)
+#define FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID (3U)
+#define FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID \
+ (FWPS_MSG_ABI_IN_RESERVED_QUEUE_ID + 1U)
+
+#if (FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID >= FWPS_MSG_ABI_MAX_INPUT_QUEUES)
+#error "Maximum queues configuration is too small to fit minimum number of \
+useful queues"
+#endif
+
+#define FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID (FWPS_MSG_ABI_MAX_QUEUES - 1U)
+#define FWPS_MSG_ABI_IN_MAX_TASK_QUEUES \
+ (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID - \
+ FWPS_MSG_ABI_IN_FIRST_TASK_QUEUE_ID + 1U)
+#define FWPS_MSG_ABI_OUT_FIRST_QUEUE_ID (FWPS_MSG_ABI_OUT_ACK_QUEUE_ID)
+#define FWPS_MSG_ABI_OUT_LAST_QUEUE_ID (FWPS_MSG_ABI_MAX_OUTPUT_QUEUES - 1U)
+#define FWPS_MSG_ABI_IN_FIRST_QUEUE_ID (FWPS_MSG_ABI_IN_DEV_QUEUE_ID)
+#define FWPS_MSG_ABI_IN_LAST_QUEUE_ID (FWPS_MSG_ABI_IN_LAST_TASK_QUEUE_ID)
+
+#define FWPS_MSG_HOST2FW_MAX_SIZE (2U * 1024U)
+#define FWPS_MSG_FW2HOST_MAX_SIZE (256U)
+
+#endif
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h
new file mode 100644
index 000000000000..0af04c8c6a88
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_psys_config_abi.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_PSYS_CONFIG_ABI_H_INCLUDED__
+#define IPU7_PSYS_CONFIG_ABI_H_INCLUDED__
+
+#include <linux/types.h>
+
+#include "ipu7_fw_boot_abi.h"
+#include "ipu7_fw_config_abi.h"
+
+struct ipu7_psys_config {
+ u32 use_debug_manifest;
+ u32 timeout_val_ms;
+ u32 compression_support_enabled;
+ struct ia_gofo_logger_config logger_config;
+ struct ipu7_wdt_abi wdt_config;
+ u8 ipu_psys_debug_bitmask;
+ u8 padding[3];
+};
+
+#endif
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h
new file mode 100644
index 000000000000..bfa5258d5b97
--- /dev/null
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_syscom_abi.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_SYSCOM_ABI_H
+#define IPU7_FW_SYSCOM_ABI_H
+
+#include <linux/types.h>
+
+#include "ipu7_fw_common_abi.h"
+
+#pragma pack(push, 1)
+#define SYSCOM_QUEUE_MIN_CAPACITY 2U
+
+struct syscom_queue_params_config {
+ ia_gofo_addr_t token_array_mem;
+ u16 token_size_in_bytes;
+ u16 max_capacity;
+};
+
+struct syscom_config_s {
+ u16 max_output_queues;
+ u16 max_input_queues;
+};
+
+#pragma pack(pop)
+
+static inline struct syscom_queue_params_config *
+syscom_config_get_queue_configs(struct syscom_config_s *config)
+{
+ return (struct syscom_queue_params_config *)(&config[1]);
+}
+
+static inline const struct syscom_queue_params_config *
+syscom_config_get_queue_configs_const(const struct syscom_config_s *config)
+{
+ return (const struct syscom_queue_params_config *)(&config[1]);
+}
+
+#pragma pack(push, 1)
+struct syscom_queue_indices_s {
+ u32 read_index;
+ u32 write_index;
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-boot.c b/drivers/staging/media/ipu7/ipu7-boot.c
new file mode 100644
index 000000000000..d7901ff78b38
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-boot.c
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 - 2025 Intel Corporation
+ */
+
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/iopoll.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "abi/ipu7_fw_boot_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-boot.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress-regs.h"
+#include "ipu7-dma.h"
+#include "ipu7-platform-regs.h"
+#include "ipu7-syscom.h"
+
+#define IPU_FW_START_STOP_TIMEOUT 2000
+#define IPU_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC)
+#define BOOT_STATE_IS_CRITICAL(s) IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(s)
+#define BOOT_STATE_IS_READY(s) ((s) == IA_GOFO_FW_BOOT_STATE_READY)
+#define BOOT_STATE_IS_INACTIVE(s) ((s) == IA_GOFO_FW_BOOT_STATE_INACTIVE)
+
+struct ipu7_boot_context {
+ u32 base;
+ u32 dmem_address;
+ u32 status_ctrl_reg;
+ u32 fw_start_address_reg;
+ u32 fw_code_base_reg;
+};
+
+static const struct ipu7_boot_context contexts[IPU_SUBSYS_NUM] = {
+ {
+ /* ISYS */
+ .dmem_address = IPU_ISYS_DMEM_OFFSET,
+ .status_ctrl_reg = BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS,
+ .fw_start_address_reg = BUTTRESS_REG_DRV_IS_UCX_START_ADDR,
+ .fw_code_base_reg = IS_UC_CTRL_BASE
+ },
+ {
+ /* PSYS */
+ .dmem_address = IPU_PSYS_DMEM_OFFSET,
+ .status_ctrl_reg = BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS,
+ .fw_start_address_reg = BUTTRESS_REG_DRV_PS_UCX_START_ADDR,
+ .fw_code_base_reg = PS_UC_CTRL_BASE
+ }
+};
+
+static u32 get_fw_boot_reg_addr(const struct ipu7_bus_device *adev,
+ enum ia_gofo_buttress_reg_id reg)
+{
+ u32 base = (adev->subsys == IPU_IS) ? 0U : (u32)IA_GOFO_FW_BOOT_ID_MAX;
+
+ return BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg);
+}
+
+static void write_fw_boot_param(const struct ipu7_bus_device *adev,
+ enum ia_gofo_buttress_reg_id reg,
+ u32 val)
+{
+ void __iomem *base = adev->isp->base;
+
+ dev_dbg(&adev->auxdev.dev,
+ "write boot param reg: %d addr: %x val: 0x%x\n",
+ reg, get_fw_boot_reg_addr(adev, reg), val);
+ writel(val, base + get_fw_boot_reg_addr(adev, reg));
+}
+
+static u32 read_fw_boot_param(const struct ipu7_bus_device *adev,
+ enum ia_gofo_buttress_reg_id reg)
+{
+ void __iomem *base = adev->isp->base;
+
+ return readl(base + get_fw_boot_reg_addr(adev, reg));
+}
+
+static int ipu7_boot_cell_reset(const struct ipu7_bus_device *adev)
+{
+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+ const struct device *dev = &adev->auxdev.dev;
+ u32 ucx_ctrl_status = ctx->status_ctrl_reg;
+ u32 timeout = IPU_BOOT_CELL_RESET_TIMEOUT;
+ void __iomem *base = adev->isp->base;
+ u32 val, val2;
+ int ret;
+
+ dev_dbg(dev, "cell enter reset...\n");
+ val = readl(base + ucx_ctrl_status);
+ dev_dbg(dev, "cell_ctrl_reg addr = 0x%x, val = 0x%x\n",
+ ucx_ctrl_status, val);
+
+ dev_dbg(dev, "force cell reset...\n");
+ val |= UCX_CTL_RESET;
+ val &= ~UCX_CTL_RUN;
+
+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+ ucx_ctrl_status, val);
+ writel(val, base + ucx_ctrl_status);
+
+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
+ (val2 & 0x3U) == (val & 0x3U), 100, timeout);
+ if (ret) {
+ dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2);
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(dev, "cell exit reset...\n");
+ val = readl(base + ucx_ctrl_status);
+ WARN((!(val & UCX_CTL_RESET) || val & UCX_CTL_RUN),
+ "cell status 0x%x", val);
+
+ val &= ~(UCX_CTL_RESET | UCX_CTL_RUN);
+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+ ucx_ctrl_status, val);
+ writel(val, base + ucx_ctrl_status);
+
+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
+ (val2 & 0x3U) == (val & 0x3U), 100, timeout);
+ if (ret) {
+ dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void ipu7_boot_cell_start(const struct ipu7_bus_device *adev)
+{
+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+ void __iomem *base = adev->isp->base;
+ const struct device *dev = &adev->auxdev.dev;
+ u32 val;
+
+ dev_dbg(dev, "starting cell...\n");
+ val = readl(base + ctx->status_ctrl_reg);
+ WARN_ON(val & (UCX_CTL_RESET | UCX_CTL_RUN));
+
+ val &= ~UCX_CTL_RESET;
+ val |= UCX_CTL_RUN;
+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+ ctx->status_ctrl_reg, val);
+ writel(val, base + ctx->status_ctrl_reg);
+}
+
+static void ipu7_boot_cell_stop(const struct ipu7_bus_device *adev)
+{
+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+ void __iomem *base = adev->isp->base;
+ const struct device *dev = &adev->auxdev.dev;
+ u32 val;
+
+ dev_dbg(dev, "stopping cell...\n");
+
+ val = readl(base + ctx->status_ctrl_reg);
+ val &= ~UCX_CTL_RUN;
+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+ ctx->status_ctrl_reg, val);
+ writel(val, base + ctx->status_ctrl_reg);
+
+ /* Wait for uC transactions complete */
+ usleep_range(10, 20);
+
+ val = readl(base + ctx->status_ctrl_reg);
+ val |= UCX_CTL_RESET;
+ dev_dbg(dev, "write status_ctrl_reg(0x%x) to 0x%x\n",
+ ctx->status_ctrl_reg, val);
+ writel(val, base + ctx->status_ctrl_reg);
+}
+
+static int ipu7_boot_cell_init(const struct ipu7_bus_device *adev)
+{
+ const struct ipu7_boot_context *ctx = &contexts[adev->subsys];
+ void __iomem *base = adev->isp->base;
+
+ dev_dbg(&adev->auxdev.dev, "write fw_start_address_reg(0x%x) to 0x%x\n",
+ ctx->fw_start_address_reg, adev->fw_entry);
+ writel(adev->fw_entry, base + ctx->fw_start_address_reg);
+
+ return ipu7_boot_cell_reset(adev);
+}
+
+static void init_boot_config(struct ia_gofo_boot_config *boot_config,
+ u32 length, u8 major)
+{
+ /* syscom version, new syscom2 version */
+ boot_config->length = length;
+ boot_config->config_version.major = 1U;
+ boot_config->config_version.minor = 0U;
+ boot_config->config_version.subminor = 0U;
+ boot_config->config_version.patch = 0U;
+
+ /* msg version for task interface */
+ boot_config->client_version_support.num_versions = 1U;
+ boot_config->client_version_support.versions[0].major = major;
+ boot_config->client_version_support.versions[0].minor = 0U;
+ boot_config->client_version_support.versions[0].subminor = 0U;
+ boot_config->client_version_support.versions[0].patch = 0U;
+}
+
+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev,
+ struct syscom_queue_config *qconfigs,
+ int num_queues, u32 uc_freq,
+ dma_addr_t subsys_config, u8 major)
+{
+ u32 total_queue_size_aligned = 0;
+ struct ipu7_syscom_context *syscom = adev->syscom;
+ struct ia_gofo_boot_config *boot_config;
+ struct syscom_queue_params_config *cfgs;
+ struct device *dev = &adev->auxdev.dev;
+ struct syscom_config_s *syscfg;
+ dma_addr_t queue_mem_dma_ptr;
+ void *queue_mem_ptr;
+ unsigned int i;
+
+ dev_dbg(dev, "boot config queues_nr: %d freq: %u sys_conf: 0x%pad\n",
+ num_queues, uc_freq, &subsys_config);
+ /* Allocate boot config. */
+ adev->boot_config_size =
+ sizeof(*cfgs) * num_queues + sizeof(*boot_config);
+ adev->boot_config = ipu7_dma_alloc(adev, adev->boot_config_size,
+ &adev->boot_config_dma_addr,
+ GFP_KERNEL, 0);
+ if (!adev->boot_config) {
+ dev_err(dev, "Failed to allocate boot config.\n");
+ return -ENOMEM;
+ }
+
+ boot_config = adev->boot_config;
+ memset(boot_config, 0, sizeof(struct ia_gofo_boot_config));
+ init_boot_config(boot_config, adev->boot_config_size, major);
+ boot_config->subsys_config = subsys_config;
+
+ boot_config->uc_tile_frequency = uc_freq;
+ boot_config->uc_tile_frequency_units =
+ IA_GOFO_FW_BOOT_UC_FREQUENCY_UNITS_MHZ;
+ boot_config->syscom_context_config.max_output_queues =
+ syscom->num_output_queues;
+ boot_config->syscom_context_config.max_input_queues =
+ syscom->num_input_queues;
+
+ ipu7_dma_sync_single(adev, adev->boot_config_dma_addr,
+ adev->boot_config_size);
+
+ for (i = 0; i < num_queues; i++) {
+ u32 queue_size = qconfigs[i].max_capacity *
+ qconfigs[i].token_size_in_bytes;
+
+ queue_size = ALIGN(queue_size, 64U);
+ total_queue_size_aligned += queue_size;
+ qconfigs[i].queue_size = queue_size;
+ }
+
+ /* Allocate queue memory */
+ syscom->queue_mem = ipu7_dma_alloc(adev, total_queue_size_aligned,
+ &syscom->queue_mem_dma_addr,
+ GFP_KERNEL, 0);
+ if (!syscom->queue_mem) {
+ dev_err(dev, "Failed to allocate queue memory.\n");
+ return -ENOMEM;
+ }
+ syscom->queue_mem_size = total_queue_size_aligned;
+
+ syscfg = &boot_config->syscom_context_config;
+ cfgs = ipu7_syscom_get_queue_config(syscfg);
+ queue_mem_ptr = syscom->queue_mem;
+ queue_mem_dma_ptr = syscom->queue_mem_dma_addr;
+ for (i = 0; i < num_queues; i++) {
+ cfgs[i].token_array_mem = queue_mem_dma_ptr;
+ cfgs[i].max_capacity = qconfigs[i].max_capacity;
+ cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes;
+ qconfigs[i].token_array_mem = queue_mem_ptr;
+ queue_mem_dma_ptr += qconfigs[i].queue_size;
+ queue_mem_ptr += qconfigs[i].queue_size;
+ }
+
+ ipu7_dma_sync_single(adev, syscom->queue_mem_dma_addr,
+ total_queue_size_aligned);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_init_boot_config, "INTEL_IPU7");
+
+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev)
+{
+ struct ipu7_syscom_context *syscom = adev->syscom;
+
+ if (syscom->queue_mem) {
+ ipu7_dma_free(adev, syscom->queue_mem_size,
+ syscom->queue_mem,
+ syscom->queue_mem_dma_addr, 0);
+ syscom->queue_mem = NULL;
+ syscom->queue_mem_dma_addr = 0;
+ }
+
+ if (adev->boot_config) {
+ ipu7_dma_free(adev, adev->boot_config_size,
+ adev->boot_config,
+ adev->boot_config_dma_addr, 0);
+ adev->boot_config = NULL;
+ adev->boot_config_dma_addr = 0;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_release_boot_config, "INTEL_IPU7");
+
+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev)
+{
+ const struct device *dev = &adev->auxdev.dev;
+ u32 timeout = IPU_FW_START_STOP_TIMEOUT;
+ void __iomem *base = adev->isp->base;
+ u32 boot_state, last_boot_state;
+ u32 indices_addr, msg_ver, id;
+ int ret;
+
+ ret = ipu7_boot_cell_init(adev);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "start booting fw...\n");
+ /* store "uninit" state to syscom/boot state reg */
+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID,
+ IA_GOFO_FW_BOOT_STATE_UNINIT);
+ /*
+ * Set registers to zero
+ * (not strictly required, but recommended for diagnostics)
+ */
+ write_fw_boot_param(adev,
+ IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0);
+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID, 0);
+ /* store firmware configuration address */
+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_CONFIG_ID,
+ adev->boot_config_dma_addr);
+
+ /* Kick uC, then wait for boot complete */
+ ipu7_boot_cell_start(adev);
+
+ last_boot_state = IA_GOFO_FW_BOOT_STATE_UNINIT;
+ while (timeout--) {
+ boot_state = read_fw_boot_param(adev,
+ IA_GOFO_FW_BOOT_STATE_ID);
+ if (boot_state != last_boot_state) {
+ dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n",
+ last_boot_state, boot_state);
+ last_boot_state = boot_state;
+ }
+ if (BOOT_STATE_IS_CRITICAL(boot_state) ||
+ BOOT_STATE_IS_READY(boot_state))
+ break;
+ usleep_range(1000, 1200);
+ }
+
+ if (BOOT_STATE_IS_CRITICAL(boot_state)) {
+ ipu7_dump_fw_error_log(adev);
+ dev_err(dev, "critical boot state error 0x%x\n", boot_state);
+ return -EINVAL;
+ } else if (!BOOT_STATE_IS_READY(boot_state)) {
+ dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state);
+ return -ETIMEDOUT;
+ }
+ dev_dbg(dev, "fw boot done.\n");
+
+ /* Get FW syscom queue indices addr */
+ id = IA_GOFO_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID;
+ indices_addr = read_fw_boot_param(adev, id);
+ adev->syscom->queue_indices = base + indices_addr;
+ dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr);
+
+ /* Get message version. */
+ msg_ver = read_fw_boot_param(adev,
+ IA_GOFO_FW_BOOT_MESSAGING_VERSION_ID);
+ dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_start_fw, "INTEL_IPU7");
+
+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev)
+{
+ const struct device *dev = &adev->auxdev.dev;
+ u32 timeout = IPU_FW_START_STOP_TIMEOUT;
+ u32 boot_state;
+
+ boot_state = read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID);
+ if (BOOT_STATE_IS_CRITICAL(boot_state) ||
+ !BOOT_STATE_IS_READY(boot_state)) {
+ dev_err(dev, "fw not ready for shutdown, state 0x%x\n",
+ boot_state);
+ return -EBUSY;
+ }
+
+ /* Issue shutdown to start shutdown process */
+ dev_dbg(dev, "stopping fw...\n");
+ write_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID,
+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD);
+ while (timeout--) {
+ boot_state = read_fw_boot_param(adev,
+ IA_GOFO_FW_BOOT_STATE_ID);
+ if (BOOT_STATE_IS_CRITICAL(boot_state) ||
+ BOOT_STATE_IS_INACTIVE(boot_state))
+ break;
+ usleep_range(1000, 1200);
+ }
+
+ if (BOOT_STATE_IS_CRITICAL(boot_state)) {
+ ipu7_dump_fw_error_log(adev);
+ dev_err(dev, "critical boot state error 0x%x\n", boot_state);
+ return -EINVAL;
+ } else if (!BOOT_STATE_IS_INACTIVE(boot_state)) {
+ dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state);
+ return -ETIMEDOUT;
+ }
+
+ ipu7_boot_cell_stop(adev);
+ dev_dbg(dev, "stop fw done.\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_stop_fw, "INTEL_IPU7");
+
+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev)
+{
+ return read_fw_boot_param(adev, IA_GOFO_FW_BOOT_STATE_ID);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_boot_get_boot_state, "INTEL_IPU7");
diff --git a/drivers/staging/media/ipu7/ipu7-boot.h b/drivers/staging/media/ipu7/ipu7-boot.h
new file mode 100644
index 000000000000..5600be849931
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-boot.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2022 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_BOOT_H
+#define IPU7_BOOT_H
+
+#include <linux/types.h>
+
+struct ipu7_bus_device;
+struct syscom_queue_config;
+
+#define FW_QUEUE_CONFIG_SIZE(num_queues) \
+ (sizeof(struct syscom_queue_config) * (num_queues))
+
+int ipu7_boot_init_boot_config(struct ipu7_bus_device *adev,
+ struct syscom_queue_config *qconfigs,
+ int num_queues, u32 uc_freq,
+ dma_addr_t subsys_config, u8 major);
+void ipu7_boot_release_boot_config(struct ipu7_bus_device *adev);
+int ipu7_boot_start_fw(const struct ipu7_bus_device *adev);
+int ipu7_boot_stop_fw(const struct ipu7_bus_device *adev);
+u32 ipu7_boot_get_boot_state(const struct ipu7_bus_device *adev);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-bus.c b/drivers/staging/media/ipu7/ipu7-bus.c
new file mode 100644
index 000000000000..7da44fde002a
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-bus.c
@@ -0,0 +1,158 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-boot.h"
+#include "ipu7-dma.h"
+
+static int bus_pm_runtime_suspend(struct device *dev)
+{
+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+ int ret;
+
+ ret = pm_generic_runtime_suspend(dev);
+ if (ret)
+ return ret;
+
+ ret = ipu_buttress_powerdown(dev, adev->ctrl);
+ if (!ret)
+ return 0;
+
+ dev_err(dev, "power down failed!\n");
+
+ /* Powering down failed, attempt to resume device now */
+ ret = pm_generic_runtime_resume(dev);
+ if (!ret)
+ return -EBUSY;
+
+ return -EIO;
+}
+
+static int bus_pm_runtime_resume(struct device *dev)
+{
+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+ int ret;
+
+ ret = ipu_buttress_powerup(dev, adev->ctrl);
+ if (ret)
+ return ret;
+
+ ret = pm_generic_runtime_resume(dev);
+ if (ret)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ ipu_buttress_powerdown(dev, adev->ctrl);
+
+ return -EBUSY;
+}
+
+static struct dev_pm_domain ipu7_bus_pm_domain = {
+ .ops = {
+ .runtime_suspend = bus_pm_runtime_suspend,
+ .runtime_resume = bus_pm_runtime_resume,
+ },
+};
+
+static DEFINE_MUTEX(ipu7_bus_mutex);
+static void ipu7_bus_release(struct device *dev)
+{
+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+
+ kfree(adev->pdata);
+ kfree(adev);
+}
+
+struct ipu7_bus_device *
+ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
+ void *pdata, const struct ipu_buttress_ctrl *ctrl,
+ const char *name)
+{
+ struct auxiliary_device *auxdev;
+ struct ipu7_bus_device *adev;
+ struct ipu7_device *isp = pci_get_drvdata(pdev);
+ int ret;
+
+ adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ if (!adev)
+ return ERR_PTR(-ENOMEM);
+
+ adev->isp = isp;
+ adev->ctrl = ctrl;
+ adev->pdata = pdata;
+ auxdev = &adev->auxdev;
+ auxdev->name = name;
+ auxdev->id = (pci_domain_nr(pdev->bus) << 16) |
+ PCI_DEVID(pdev->bus->number, pdev->devfn);
+
+ auxdev->dev.parent = parent;
+ auxdev->dev.release = ipu7_bus_release;
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret < 0) {
+ dev_err(&isp->pdev->dev, "auxiliary device init failed (%d)\n",
+ ret);
+ kfree(adev);
+ return ERR_PTR(ret);
+ }
+
+ dev_pm_domain_set(&auxdev->dev, &ipu7_bus_pm_domain);
+
+ pm_runtime_forbid(&adev->auxdev.dev);
+ pm_runtime_enable(&adev->auxdev.dev);
+
+ return adev;
+}
+
+int ipu7_bus_add_device(struct ipu7_bus_device *adev)
+{
+ struct auxiliary_device *auxdev = &adev->auxdev;
+ int ret;
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret) {
+ auxiliary_device_uninit(auxdev);
+ return ret;
+ }
+
+ mutex_lock(&ipu7_bus_mutex);
+ list_add(&adev->list, &adev->isp->devices);
+ mutex_unlock(&ipu7_bus_mutex);
+
+ pm_runtime_allow(&auxdev->dev);
+
+ return 0;
+}
+
+void ipu7_bus_del_devices(struct pci_dev *pdev)
+{
+ struct ipu7_device *isp = pci_get_drvdata(pdev);
+ struct ipu7_bus_device *adev, *save;
+
+ mutex_lock(&ipu7_bus_mutex);
+
+ list_for_each_entry_safe(adev, save, &isp->devices, list) {
+ pm_runtime_disable(&adev->auxdev.dev);
+ list_del(&adev->list);
+ auxiliary_device_delete(&adev->auxdev);
+ auxiliary_device_uninit(&adev->auxdev);
+ }
+
+ mutex_unlock(&ipu7_bus_mutex);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-bus.h b/drivers/staging/media/ipu7/ipu7-bus.h
new file mode 100644
index 000000000000..45157df16e90
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-bus.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_BUS_H
+#define IPU7_BUS_H
+
+#include <linux/auxiliary_bus.h>
+#include <linux/container_of.h>
+#include <linux/device.h>
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/scatterlist.h>
+#include <linux/types.h>
+
+#include "abi/ipu7_fw_boot_abi.h"
+
+#include "ipu7-syscom.h"
+
+struct pci_dev;
+struct ipu_buttress_ctrl;
+struct ipu7_mmu;
+struct ipu7_device;
+
+enum ipu7_subsys {
+ IPU_IS = 0,
+ IPU_PS = 1,
+ IPU_SUBSYS_NUM = 2,
+};
+
+struct ipu7_bus_device {
+ struct auxiliary_device auxdev;
+ const struct auxiliary_driver *auxdrv;
+ const struct ipu7_auxdrv_data *auxdrv_data;
+ struct list_head list;
+ enum ipu7_subsys subsys;
+ void *pdata;
+ struct ipu7_mmu *mmu;
+ struct ipu7_device *isp;
+ const struct ipu_buttress_ctrl *ctrl;
+ u64 dma_mask;
+ struct sg_table fw_sgt;
+ u32 fw_entry;
+ struct ipu7_syscom_context *syscom;
+ struct ia_gofo_boot_config *boot_config;
+ dma_addr_t boot_config_dma_addr;
+ u32 boot_config_size;
+};
+
+struct ipu7_auxdrv_data {
+ irqreturn_t (*isr)(struct ipu7_bus_device *adev);
+ irqreturn_t (*isr_threaded)(struct ipu7_bus_device *adev);
+ bool wake_isr_thread;
+};
+
+#define to_ipu7_bus_device(_dev) \
+ container_of(to_auxiliary_dev(_dev), struct ipu7_bus_device, auxdev)
+#define auxdev_to_adev(_auxdev) \
+ container_of(_auxdev, struct ipu7_bus_device, auxdev)
+#define ipu7_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev)
+
+struct ipu7_bus_device *
+ipu7_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
+ void *pdata, const struct ipu_buttress_ctrl *ctrl,
+ const char *name);
+int ipu7_bus_add_device(struct ipu7_bus_device *adev);
+void ipu7_bus_del_devices(struct pci_dev *pdev);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-buttress-regs.h b/drivers/staging/media/ipu7/ipu7-buttress-regs.h
new file mode 100644
index 000000000000..3eafd6a3813d
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-buttress-regs.h
@@ -0,0 +1,461 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_BUTTRESS_REGS_H
+#define IPU7_BUTTRESS_REGS_H
+
+#define BUTTRESS_REG_IRQ_STATUS 0x2000
+#define BUTTRESS_REG_IRQ_STATUS_UNMASKED 0x2004
+#define BUTTRESS_REG_IRQ_ENABLE 0x2008
+#define BUTTRESS_REG_IRQ_CLEAR 0x200c
+#define BUTTRESS_REG_IRQ_MASK 0x2010
+#define BUTTRESS_REG_TSC_CMD 0x2014
+#define BUTTRESS_REG_TSC_CTL 0x2018
+#define BUTTRESS_REG_TSC_LO 0x201c
+#define BUTTRESS_REG_TSC_HI 0x2020
+
+/* valid for PTL */
+#define BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030
+#define BUTTRESS_REG_PB_TIMESTAMP_HI 0x2034
+#define BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038
+
+#define BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100
+#define BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104
+#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ 0x2108
+#define BUTTRESS_REG_PS_DOMAINS_STATUS 0x2110
+#define BUTTRESS_REG_PWR_STATUS 0x2114
+#define BUTTRESS_REG_PS_WORKPOINT_REQ_SHADOW 0x2120
+#define BUTTRESS_REG_IS_WORKPOINT_REQ_SHADOW 0x2124
+#define BUTTRESS_REG_PS_WORKPOINT_DOMAIN_REQ_SHADOW 0x2128
+#define BUTTRESS_REG_ISPS_WORKPOINT_DOWNLOAD 0x212c
+#define BUTTRESS_REG_PG_FLOW_OVERRIDE 0x2180
+#define BUTTRESS_REG_GLOBAL_OVERRIDE_UNGATE_CTL 0x2184
+#define BUTTRESS_REG_PWR_FSM_CTL 0x2188
+#define BUTTRESS_REG_IDLE_WDT 0x218c
+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_EN 0x2190
+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_ADDR 0x2194
+#define BUTTRESS_REG_PS_PWR_DOMAIN_EVENTQ_DATA 0x2198
+#define BUTTRESS_REG_POWER_EN_DELAY 0x219c
+#define IPU7_BUTTRESS_REG_LTR_CONTROL 0x21a0
+#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4
+#define IPU7_BUTTRESS_REG_INT_FRM_PUNIT 0x21a8
+#define IPU8_BUTTRESS_REG_LTR_CONTROL 0x21a4
+#define IPU8_BUTTRESS_REG_NDE_CONTROL 0x21a8
+#define IPU8_BUTTRESS_REG_INT_FRM_PUNIT 0x21ac
+#define BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0
+#define BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4
+#define BUTTRESS_REG_DVFS_FSM_STATUS 0x21b8
+#define BUTTRESS_REG_PS_PLL_ENABLE 0x21bc
+#define BUTTRESS_REG_D2D_CTL 0x21d4
+#define BUTTRESS_REG_IB_CLK_CTL 0x21d8
+#define BUTTRESS_REG_IB_CRO_CLK_CTL 0x21dc
+#define BUTTRESS_REG_FUNC_FUSES 0x21e0
+#define BUTTRESS_REG_ISOCH_CTL 0x21e4
+#define BUTTRESS_REG_WORKPOINT_CTL 0x21f0
+#define BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS 0x2200
+#define BUTTRESS_REG_DRV_IS_UCX_START_ADDR 0x2204
+#define BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS 0x2208
+#define BUTTRESS_REG_DRV_PS_UCX_START_ADDR 0x220c
+#define BUTTRESS_REG_DRV_UCX_RESET_CFG 0x2210
+
+/* configured by CSE */
+#define BUTTRESS_REG_CSE_IS_UCX_CONTROL_STATUS 0x2300
+#define BUTTRESS_REG_CSE_IS_UCX_START_ADDR 0x2304
+#define BUTTRESS_REG_CSE_PS_UCX_CONTROL_STATUS 0x2308
+#define BUTTRESS_REG_CSE_PS_UCX_START_ADDR 0x230c
+
+#define BUTTRESS_REG_CAMERA_MASK 0x2310
+#define BUTTRESS_REG_FW_CTL 0x2314
+#define BUTTRESS_REG_SECURITY_CTL 0x2318
+#define BUTTRESS_REG_FUNCTIONAL_FW_SETUP 0x231c
+#define BUTTRESS_REG_FW_BASE 0x2320
+#define BUTTRESS_REG_FW_BASE_LIMIT 0x2324
+#define BUTTRESS_REG_FW_SCRATCH_BASE 0x2328
+#define BUTTRESS_REG_FW_SCRATCH_LIMIT 0x232c
+#define BUTTRESS_REG_CSE_ACTION 0x2330
+
+/* configured by SW */
+#define BUTTRESS_REG_FW_RESET_CTL 0x2334
+#define BUTTRESS_REG_FW_SOURCE_SIZE 0x2338
+#define BUTTRESS_REG_FW_SOURCE_BASE 0x233c
+
+#define BUTTRESS_REG_IPU_SEC_CP_LSB 0x2400
+#define BUTTRESS_REG_IPU_SEC_CP_MSB 0x2404
+#define BUTTRESS_REG_IPU_SEC_WAC_LSB 0x2408
+#define BUTTRESS_REG_IPU_SEC_WAC_MSB 0x240c
+#define BUTTRESS_REG_IPU_SEC_RAC_LSB 0x2410
+#define BUTTRESS_REG_IPU_SEC_RAC_MSB 0x2414
+#define BUTTRESS_REG_IPU_DRV_CP_LSB 0x2418
+#define BUTTRESS_REG_IPU_DRV_CP_MSB 0x241c
+#define BUTTRESS_REG_IPU_DRV_WAC_LSB 0x2420
+#define BUTTRESS_REG_IPU_DRV_WAC_MSB 0x2424
+#define BUTTRESS_REG_IPU_DRV_RAC_LSB 0x2428
+#define BUTTRESS_REG_IPU_DRV_RAC_MSB 0x242c
+#define BUTTRESS_REG_IPU_FW_CP_LSB 0x2430
+#define BUTTRESS_REG_IPU_FW_CP_MSB 0x2434
+#define BUTTRESS_REG_IPU_FW_WAC_LSB 0x2438
+#define BUTTRESS_REG_IPU_FW_WAC_MSB 0x243c
+#define BUTTRESS_REG_IPU_FW_RAC_LSB 0x2440
+#define BUTTRESS_REG_IPU_FW_RAC_MSB 0x2444
+#define BUTTRESS_REG_IPU_BIOS_SEC_CP_LSB 0x2448
+#define BUTTRESS_REG_IPU_BIOS_SEC_CP_MSB 0x244c
+#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_LSB 0x2450
+#define BUTTRESS_REG_IPU_BIOS_SEC_WAC_MSB 0x2454
+#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_LSB 0x2458
+#define BUTTRESS_REG_IPU_BIOS_SEC_RAC_MSB 0x245c
+#define BUTTRESS_REG_IPU_DFD_CP_LSB 0x2460
+#define BUTTRESS_REG_IPU_DFD_CP_MSB 0x2464
+#define BUTTRESS_REG_IPU_DFD_WAC_LSB 0x2468
+#define BUTTRESS_REG_IPU_DFD_WAC_MSB 0x246c
+#define BUTTRESS_REG_IPU_DFD_RAC_LSB 0x2470
+#define BUTTRESS_REG_IPU_DFD_RAC_MSB 0x2474
+#define BUTTRESS_REG_CSE2IUDB0 0x2500
+#define BUTTRESS_REG_CSE2IUDATA0 0x2504
+#define BUTTRESS_REG_CSE2IUCSR 0x2508
+#define BUTTRESS_REG_IU2CSEDB0 0x250c
+#define BUTTRESS_REG_IU2CSEDATA0 0x2510
+#define BUTTRESS_REG_IU2CSECSR 0x2514
+#define BUTTRESS_REG_CSE2IUDB0_CR_SHADOW 0x2520
+#define BUTTRESS_REG_CSE2IUDATA0_CR_SHADOW 0x2524
+#define BUTTRESS_REG_CSE2IUCSR_CR_SHADOW 0x2528
+#define BUTTRESS_REG_IU2CSEDB0_CR_SHADOW 0x252c
+#define BUTTRESS_REG_DVFS_FSM_SURVIVABILITY 0x2900
+#define BUTTRESS_REG_FLOWS_FSM_SURVIVABILITY 0x2904
+#define BUTTRESS_REG_FABRICS_FSM_SURVIVABILITY 0x2908
+#define BUTTRESS_REG_PS_SUB1_PM_FSM_SURVIVABILITY 0x290c
+#define BUTTRESS_REG_PS_SUB0_PM_FSM_SURVIVABILITY 0x2910
+#define BUTTRESS_REG_PS_PM_FSM_SURVIVABILITY 0x2914
+#define BUTTRESS_REG_IS_PM_FSM_SURVIVABILITY 0x2918
+#define BUTTRESS_REG_FLR_RST_FSM_SURVIVABILITY 0x291c
+#define BUTTRESS_REG_FW_RST_FSM_SURVIVABILITY 0x2920
+#define BUTTRESS_REG_RESETPREP_FSM_SURVIVABILITY 0x2924
+#define BUTTRESS_REG_POWER_FSM_DOMAIN_STATUS 0x3000
+#define BUTTRESS_REG_IDLEREQ_STATUS1 0x3004
+#define BUTTRESS_REG_POWER_FSM_STATUS_IS_PS 0x3008
+#define BUTTRESS_REG_POWER_ACK_B_STATUS 0x300c
+#define BUTTRESS_REG_DOMAIN_RETENTION_CTL 0x3010
+#define BUTTRESS_REG_CG_CTRL_BITS 0x3014
+#define BUTTRESS_REG_IS_IFC_STATUS0 0x3018
+#define BUTTRESS_REG_IS_IFC_STATUS1 0x301c
+#define BUTTRESS_REG_PS_IFC_STATUS0 0x3020
+#define BUTTRESS_REG_PS_IFC_STATUS1 0x3024
+#define BUTTRESS_REG_BTRS_IFC_STATUS0 0x3028
+#define BUTTRESS_REG_BTRS_IFC_STATUS1 0x302c
+#define BUTTRESS_REG_IPU_SKU 0x3030
+#define BUTTRESS_REG_PS_IDLEACK 0x3034
+#define BUTTRESS_REG_IS_IDLEACK 0x3038
+#define BUTTRESS_REG_SPARE_REGS_0 0x303c
+#define BUTTRESS_REG_SPARE_REGS_1 0x3040
+#define BUTTRESS_REG_SPARE_REGS_2 0x3044
+#define BUTTRESS_REG_SPARE_REGS_3 0x3048
+#define BUTTRESS_REG_IUNIT_ACV 0x304c
+#define BUTTRESS_REG_CHICKEN_BITS 0x3050
+#define BUTTRESS_REG_SBENDPOINT_CFG 0x3054
+#define BUTTRESS_REG_ECC_ERR_LOG 0x3058
+#define BUTTRESS_REG_POWER_FSM_STATUS 0x3070
+#define BUTTRESS_REG_RESET_FSM_STATUS 0x3074
+#define BUTTRESS_REG_IDLE_STATUS 0x3078
+#define BUTTRESS_REG_IDLEACK_STATUS 0x307c
+#define BUTTRESS_REG_IPU_DEBUG 0x3080
+
+#define BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000
+#define BUTTRESS_REG_FW_BOOT_PARAMS1 0x4004
+#define BUTTRESS_REG_FW_BOOT_PARAMS2 0x4008
+#define BUTTRESS_REG_FW_BOOT_PARAMS3 0x400c
+#define BUTTRESS_REG_FW_BOOT_PARAMS4 0x4010
+#define BUTTRESS_REG_FW_BOOT_PARAMS5 0x4014
+#define BUTTRESS_REG_FW_BOOT_PARAMS6 0x4018
+#define BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c
+#define BUTTRESS_REG_FW_BOOT_PARAMS8 0x4020
+#define BUTTRESS_REG_FW_BOOT_PARAMS9 0x4024
+#define BUTTRESS_REG_FW_BOOT_PARAMS10 0x4028
+#define BUTTRESS_REG_FW_BOOT_PARAMS11 0x402c
+#define BUTTRESS_REG_FW_BOOT_PARAMS12 0x4030
+#define BUTTRESS_REG_FW_BOOT_PARAMS13 0x4034
+#define BUTTRESS_REG_FW_BOOT_PARAMS14 0x4038
+#define BUTTRESS_REG_FW_BOOT_PARAMS15 0x403c
+
+#define BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \
+ (BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U))
+#define BUTTRESS_REG_FW_GP(i) (0x4040 + 0x4 * (i))
+#define BUTTRESS_REG_FPGA_SUPPORT(i) (0x40c0 + 0x4 * (i))
+
+#define BUTTRESS_REG_FW_GP8 0x4060
+#define BUTTRESS_REG_FW_GP24 0x40a0
+
+#define BUTTRESS_REG_GPIO_0_PADCFG_ADDR_CR 0x4100
+#define BUTTRESS_REG_GPIO_1_PADCFG_ADDR_CR 0x4104
+#define BUTTRESS_REG_GPIO_2_PADCFG_ADDR_CR 0x4108
+#define BUTTRESS_REG_GPIO_3_PADCFG_ADDR_CR 0x410c
+#define BUTTRESS_REG_GPIO_4_PADCFG_ADDR_CR 0x4110
+#define BUTTRESS_REG_GPIO_5_PADCFG_ADDR_CR 0x4114
+#define BUTTRESS_REG_GPIO_6_PADCFG_ADDR_CR 0x4118
+#define BUTTRESS_REG_GPIO_7_PADCFG_ADDR_CR 0x411c
+#define BUTTRESS_REG_GPIO_ENABLE 0x4140
+#define BUTTRESS_REG_GPIO_VALUE_CR 0x4144
+
+#define BUTTRESS_REG_IS_MEM_CORRECTABLE_ERROR_STATUS 0x5000
+#define BUTTRESS_REG_IS_MEM_FATAL_ERROR_STATUS 0x5004
+#define BUTTRESS_REG_IS_MEM_NON_FATAL_ERROR_STATUS 0x5008
+#define BUTTRESS_REG_IS_MEM_CHECK_PASSED 0x500c
+#define BUTTRESS_REG_IS_MEM_ERROR_INJECT 0x5010
+#define BUTTRESS_REG_IS_MEM_ERROR_CLEAR 0x5014
+#define BUTTRESS_REG_PS_MEM_CORRECTABLE_ERROR_STATUS 0x5040
+#define BUTTRESS_REG_PS_MEM_FATAL_ERROR_STATUS 0x5044
+#define BUTTRESS_REG_PS_MEM_NON_FATAL_ERROR_STATUS 0x5048
+#define BUTTRESS_REG_PS_MEM_CHECK_PASSED 0x504c
+#define BUTTRESS_REG_PS_MEM_ERROR_INJECT 0x5050
+#define BUTTRESS_REG_PS_MEM_ERROR_CLEAR 0x5054
+
+#define BUTTRESS_REG_IS_AB_REGION_MIN_ADDRESS(i) (0x6000 + 0x8 * (i))
+#define BUTTRESS_REG_IS_AB_REGION_MAX_ADDRESS(i) (0x6004 + 0x8 * (i))
+#define BUTTRESS_REG_IS_AB_VIOLATION_LOG0 0x6080
+#define BUTTRESS_REG_IS_AB_VIOLATION_LOG1 0x6084
+#define BUTTRESS_REG_PS_AB_REGION_MIN_ADDRESS(i) (0x6100 + 0x8 * (i))
+#define BUTTRESS_REG_PS_AB_REGION_MAX_ADDRESS0 (0x6104 + 0x8 * (i))
+#define BUTTRESS_REG_PS_AB_VIOLATION_LOG0 0x6180
+#define BUTTRESS_REG_PS_AB_VIOLATION_LOG1 0x6184
+#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG0 0x6200
+#define BUTTRESS_REG_PS_DEBUG_AB_VIOLATION_LOG1 0x6204
+#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG0 0x6208
+#define BUTTRESS_REG_IS_DEBUG_AB_VIOLATION_LOG1 0x620c
+#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG0 0x6210
+#define BUTTRESS_REG_IB_DVP_AB_VIOLATION_LOG1 0x6214
+#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG0 0x6218
+#define BUTTRESS_REG_IB_ATB2DTF_AB_VIOLATION_LOG1 0x621c
+#define BUTTRESS_REG_AB_ENABLE 0x6220
+#define BUTTRESS_REG_AB_DEFAULT_ACCESS 0x6230
+
+/* Indicates CSE has received an IPU driver IPC transaction */
+#define BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0)
+/* Indicates an IPC transaction from CSE has arrived */
+#define BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1)
+/* Indicates a CSR update from CSE has arrived */
+#define BUTTRESS_IRQ_CSE_CSR_SET BIT(2)
+/* Indicates an interrupt set by Punit (not in use at this time) */
+#define BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ BIT(3)
+/* Indicates an SAI violation was detected on access to IB registers */
+#define BUTTRESS_IRQ_SAI_VIOLATION BIT(4)
+/* Indicates a transaction to IS was not able to pass the access blocker */
+#define BUTTRESS_IRQ_IS_AB_VIOLATION BIT(5)
+/* Indicates a transaction to PS was not able to pass the access blocker */
+#define BUTTRESS_IRQ_PS_AB_VIOLATION BIT(6)
+/* Indicates an error response was detected by the IB config NoC */
+#define BUTTRESS_IRQ_IB_CFG_NOC_ERR_IRQ BIT(7)
+/* Indicates an error response was detected by the IB data NoC */
+#define BUTTRESS_IRQ_IB_DATA_NOC_ERR_IRQ BIT(8)
+/* Transaction to DVP regs was not able to pass the access blocker */
+#define BUTTRESS_IRQ_IB_DVP_AB_VIOLATION BIT(9)
+/* Transaction to ATB2DTF regs was not able to pass the access blocker */
+#define BUTTRESS_IRQ_ATB2DTF_AB_VIOLATION BIT(10)
+/* Transaction to IS debug regs was not able to pass the access blocker */
+#define BUTTRESS_IRQ_IS_DEBUG_AB_VIOLATION BIT(11)
+/* Transaction to PS debug regs was not able to pass the access blocker */
+#define BUTTRESS_IRQ_PS_DEBUG_AB_VIOLATION BIT(12)
+/* Indicates timeout occurred waiting for a response from a target */
+#define BUTTRESS_IRQ_IB_CFG_NOC_TIMEOUT_IRQ BIT(13)
+/* Set when any correctable ECC error input wire to buttress is set */
+#define BUTTRESS_IRQ_ECC_CORRECTABLE BIT(14)
+/* Any noncorrectable-nonfatal ECC error input wire to buttress is set */
+#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_NONFATAL BIT(15)
+/* Set when any noncorrectable-fatal ECC error input wire to buttress is set */
+#define BUTTRESS_IRQ_ECC_NONCORRECTABLE_FATAL BIT(16)
+/* Set when timeout occurred waiting for a response from a target */
+#define BUTTRESS_IRQ_IS_CFG_NOC_TIMEOUT_IRQ BIT(17)
+#define BUTTRESS_IRQ_PS_CFG_NOC_TIMEOUT_IRQ BIT(18)
+#define BUTTRESS_IRQ_LB_CFG_NOC_TIMEOUT_IRQ BIT(19)
+/* IS FW double exception event */
+#define BUTTRESS_IRQ_IS_UC_PFATAL_ERROR BIT(26)
+/* PS FW double exception event */
+#define BUTTRESS_IRQ_PS_UC_PFATAL_ERROR BIT(27)
+/* IS FW watchdog event */
+#define BUTTRESS_IRQ_IS_WATCHDOG BIT(28)
+/* PS FW watchdog event */
+#define BUTTRESS_IRQ_PS_WATCHDOG BIT(29)
+/* IS IRC irq out */
+#define BUTTRESS_IRQ_IS_IRQ BIT(30)
+/* PS IRC irq out */
+#define BUTTRESS_IRQ_PS_IRQ BIT(31)
+
+/* buttress irq */
+#define BUTTRESS_PWR_STATUS_HH_STATE_IDLE 0U
+#define BUTTRESS_PWR_STATUS_HH_STATE_IN_PRGS 1U
+#define BUTTRESS_PWR_STATUS_HH_STATE_DONE 2U
+#define BUTTRESS_PWR_STATUS_HH_STATE_ERR 3U
+
+#define BUTTRESS_TSC_CMD_START_TSC_SYNC BIT(0)
+#define BUTTRESS_PWR_STATUS_HH_STATUS_SHIFT 11
+#define BUTTRESS_PWR_STATUS_HH_STATUS_MASK (0x3U << 11)
+#define BUTTRESS_TSW_WA_SOFT_RESET BIT(8)
+/* new for PTL */
+#define BUTTRESS_SEL_PB_TIMESTAMP BIT(9)
+#define BUTTRESS_IRQS (BUTTRESS_IRQ_IS_IRQ | \
+ BUTTRESS_IRQ_PS_IRQ | \
+ BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \
+ BUTTRESS_IRQ_CSE_CSR_SET | \
+ BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE | \
+ BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ)
+
+/* Iunit to CSE regs */
+#define BUTTRESS_IU2CSEDB0_BUSY BIT(31)
+#define BUTTRESS_IU2CSEDB0_SHORT_FORMAT_SHIFT 27
+#define BUTTRESS_IU2CSEDB0_CLIENT_ID_SHIFT 10
+#define BUTTRESS_IU2CSEDB0_IPC_CLIENT_ID_VAL 2
+
+#define BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD 1
+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN 2
+#define BUTTRESS_IU2CSEDATA0_IPC_AUTH_REPLACE 3
+#define BUTTRESS_IU2CSEDATA0_IPC_UPDATE_SECURE_TOUCH 16
+
+#define BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE BIT(0)
+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE BIT(1)
+#define BUTTRESS_CSE2IUDATA0_IPC_AUTH_REPLACE_DONE BIT(2)
+#define BUTTRESS_CSE2IUDATA0_IPC_UPDATE_SECURE_TOUCH_DONE BIT(4)
+
+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1 BIT(0)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2 BIT(1)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE BIT(2)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ BIT(3)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID BIT(4)
+#define BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ BIT(5)
+
+/* 0x20 == NACK, 0xf == unknown command */
+#define BUTTRESS_CSE2IUDATA0_IPC_NACK 0xf20
+#define BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK 0xffff
+
+/* IS/PS freq control */
+#define BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xffU
+#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xffU
+
+#define IPU7_IS_FREQ_MAX 450
+#define IPU7_IS_FREQ_MIN 50
+#define IPU7_PS_FREQ_MAX 750
+#define BUTTRESS_PS_FREQ_RATIO_STEP 25U
+/* valid for IPU8 */
+#define BUTTRESS_IS_FREQ_RATIO_STEP 25U
+
+/* IS: 400mhz, PS: 500mhz */
+#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b
+#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14
+/* IS: 400mhz, PS: 400mhz */
+#define IPU8_IS_FREQ_CTL_DEFAULT_RATIO 0x10
+#define IPU8_PS_FREQ_CTL_DEFAULT_RATIO 0x10
+
+#define IPU_FREQ_CTL_CDYN 0x80
+#define IPU_FREQ_CTL_RATIO_SHIFT 0x0
+#define IPU_FREQ_CTL_CDYN_SHIFT 0x8
+
+/* buttree power status */
+#define IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0
+#define IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK \
+ (0x3U << IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT)
+
+#define IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4
+#define IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK \
+ (0x3U << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT)
+
+#define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0
+#define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1
+#define IPU_BUTTRESS_PWR_STATE_DN_PROCESS 0x2
+#define IPU_BUTTRESS_PWR_STATE_UP_DONE 0x3
+
+#define BUTTRESS_PWR_STATE_IS_PWR_SHIFT 3
+#define BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3 << 3)
+
+#define BUTTRESS_PWR_STATE_PS_PWR_SHIFT 6
+#define BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3 << 6)
+
+#define PS_FSM_CG BIT(3)
+
+#define BUTTRESS_OVERRIDE_IS_CLK BIT(1)
+#define BUTTRESS_OVERRIDE_PS_CLK BIT(2)
+/* ps_pll only valid for ipu8 */
+#define BUTTRESS_OWN_ACK_PS_PLL BIT(8)
+#define BUTTRESS_OWN_ACK_IS_CLK BIT(9)
+#define BUTTRESS_OWN_ACK_PS_CLK BIT(10)
+
+/* FW reset ctrl */
+#define BUTTRESS_FW_RESET_CTL_START BIT(0)
+#define BUTTRESS_FW_RESET_CTL_DONE BIT(1)
+
+/* security */
+#define BUTTRESS_SECURITY_CTL_FW_SECURE_MODE BIT(16)
+#define BUTTRESS_SECURITY_CTL_FW_SETUP_MASK GENMASK(4, 0)
+
+#define BUTTRESS_SECURITY_CTL_FW_SETUP_DONE BIT(0)
+#define BUTTRESS_SECURITY_CTL_AUTH_DONE BIT(1)
+#define BUTTRESS_SECURITY_CTL_AUTH_FAILED BIT(3)
+
+/* D2D */
+#define BUTTRESS_D2D_PWR_EN BIT(0)
+#define BUTTRESS_D2D_PWR_ACK BIT(4)
+
+/* NDE */
+#define NDE_VAL_MASK GENMASK(9, 0)
+#define NDE_SCALE_MASK GENMASK(12, 10)
+#define NDE_VALID_MASK BIT(13)
+#define NDE_RESVEC_MASK GENMASK(19, 16)
+#define NDE_IN_VBLANK_DIS_MASK BIT(31)
+
+#define BUTTRESS_NDE_VAL_ACTIVE 48
+#define BUTTRESS_NDE_SCALE_ACTIVE 2
+#define BUTTRESS_NDE_VALID_ACTIVE 1
+
+#define BUTTRESS_NDE_VAL_DEFAULT 1023
+#define BUTTRESS_NDE_SCALE_DEFAULT 2
+#define BUTTRESS_NDE_VALID_DEFAULT 0
+
+/* IS and PS UCX control */
+#define UCX_CTL_RESET BIT(0)
+#define UCX_CTL_RUN BIT(1)
+#define UCX_CTL_WAKEUP BIT(2)
+#define UCX_CTL_SPARE GENMASK(7, 3)
+#define UCX_STS_PWR GENMASK(17, 16)
+#define UCX_STS_SLEEPING BIT(18)
+
+/* offset from PHY base */
+#define PHY_CSI_CFG 0xc0
+#define PHY_CSI_RCOMP_CONTROL 0xc8
+#define PHY_CSI_BSCAN_EXCLUDE 0xd8
+
+#define PHY_CPHY_DLL_OVRD(x) (0x100 + 0x100 * (x))
+#define PHY_DPHY_DLL_OVRD(x) (0x14c + 0x100 * (x))
+#define PHY_CPHY_RX_CONTROL1(x) (0x110 + 0x100 * (x))
+#define PHY_CPHY_RX_CONTROL2(x) (0x114 + 0x100 * (x))
+#define PHY_DPHY_CFG(x) (0x148 + 0x100 * (x))
+#define PHY_BB_AFE_CONFIG(x) (0x174 + 0x100 * (x))
+
+/* PB registers */
+#define INTERRUPT_STATUS 0x0
+#define BTRS_LOCAL_INTERRUPT_MASK 0x4
+#define GLOBAL_INTERRUPT_MASK 0x8
+#define HM_ATS 0xc
+#define ATS_ERROR_LOG1 0x10
+#define ATS_ERROR_LOG2 0x14
+#define ATS_ERROR_CLEAR 0x18
+#define CFI_0_ERROR_LOG 0x1c
+#define CFI_0_ERROR_CLEAR 0x20
+#define HASH_CONFIG 0x2c
+#define TLBID_HASH_ENABLE_31_0 0x30
+#define TLBID_HASH_ENABLE_63_32 0x34
+#define TLBID_HASH_ENABLE_95_64 0x38
+#define TLBID_HASH_ENABLE_127_96 0x3c
+#define CFI_1_ERROR_LOGGING 0x40
+#define CFI_1_ERROR_CLEAR 0x44
+#define IMR_ERROR_LOGGING_LOW 0x48
+#define IMR_ERROR_LOGGING_HIGH 0x4c
+#define IMR_ERROR_CLEAR 0x50
+#define PORT_ARBITRATION_WEIGHTS 0x54
+#define IMR_ERROR_LOGGING_CFI_1_LOW 0x58
+#define IMR_ERROR_LOGGING_CFI_1_HIGH 0x5c
+#define IMR_ERROR_CLEAR_CFI_1 0x60
+#define BAR2_MISC_CONFIG 0x64
+#define RSP_ID_CONFIG_AXI2CFI_0 0x68
+#define RSP_ID_CONFIG_AXI2CFI_1 0x6c
+#define PB_DRIVER_PCODE_MAILBOX_STATUS 0x70
+#define PB_DRIVER_PCODE_MAILBOX_INTERFACE 0x74
+#define PORT_ARBITRATION_WEIGHTS_ATS 0x78
+
+#endif /* IPU7_BUTTRESS_REGS_H */
diff --git a/drivers/staging/media/ipu7/ipu7-buttress.c b/drivers/staging/media/ipu7/ipu7-buttress.c
new file mode 100644
index 000000000000..e5707f5e300b
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-buttress.c
@@ -0,0 +1,1192 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <asm/cpu_device_id.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/scatterlist.h>
+#include <linux/types.h>
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress.h"
+#include "ipu7-buttress-regs.h"
+
+#define BOOTLOADER_STATUS_OFFSET BUTTRESS_REG_FW_BOOT_PARAMS7
+
+#define BOOTLOADER_MAGIC_KEY 0xb00710adU
+
+#define ENTRY BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE1
+#define EXIT BUTTRESS_IU2CSECSR_IPC_PEER_COMP_ACTIONS_RST_PHASE2
+#define QUERY BUTTRESS_IU2CSECSR_IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE
+
+#define BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX 10U
+
+#define BUTTRESS_POWER_TIMEOUT_US (200 * USEC_PER_MSEC)
+
+#define BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US (5 * USEC_PER_SEC)
+#define BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US (10 * USEC_PER_SEC)
+#define BUTTRESS_CSE_FWRESET_TIMEOUT_US (100 * USEC_PER_MSEC)
+
+#define BUTTRESS_IPC_TX_TIMEOUT_MS MSEC_PER_SEC
+#define BUTTRESS_IPC_RX_TIMEOUT_MS MSEC_PER_SEC
+#define BUTTRESS_IPC_VALIDITY_TIMEOUT_US (1 * USEC_PER_SEC)
+#define BUTTRESS_TSC_SYNC_TIMEOUT_US (5 * USEC_PER_MSEC)
+
+#define BUTTRESS_IPC_RESET_RETRY 2000U
+#define BUTTRESS_CSE_IPC_RESET_RETRY 4U
+#define BUTTRESS_IPC_CMD_SEND_RETRY 1U
+
+struct ipu7_ipc_buttress_msg {
+ u32 cmd;
+ u32 expected_resp;
+ bool require_resp;
+ u8 cmd_size;
+};
+
+static const u32 ipu7_adev_irq_mask[2] = {
+ BUTTRESS_IRQ_IS_IRQ,
+ BUTTRESS_IRQ_PS_IRQ
+};
+
+int ipu_buttress_ipc_reset(struct ipu7_device *isp,
+ struct ipu_buttress_ipc *ipc)
+{
+ unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
+ struct ipu_buttress *b = &isp->buttress;
+ struct device *dev = &isp->pdev->dev;
+ u32 val = 0, csr_in_clr;
+
+ if (!isp->secure_mode) {
+ dev_dbg(dev, "Skip IPC reset for non-secure mode\n");
+ return 0;
+ }
+
+ mutex_lock(&b->ipc_mutex);
+
+ /* Clear-by-1 CSR (all bits), corresponding internal states. */
+ val = readl(isp->base + ipc->csr_in);
+ writel(val, isp->base + ipc->csr_in);
+
+ /* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
+ writel(ENTRY, isp->base + ipc->csr_out);
+ /*
+ * Clear-by-1 all CSR bits EXCEPT following
+ * bits:
+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ * C. Possibly custom bits, depending on
+ * their role.
+ */
+ csr_in_clr = BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ |
+ BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID |
+ BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ | QUERY;
+
+ do {
+ usleep_range(400, 500);
+ val = readl(isp->base + ipc->csr_in);
+ switch (val) {
+ case ENTRY | EXIT:
+ case ENTRY | EXIT | QUERY:
+ /*
+ * 1) Clear-by-1 CSR bits
+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2).
+ * 2) Set peer CSR bit
+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
+ */
+ writel(ENTRY | EXIT, isp->base + ipc->csr_in);
+ writel(QUERY, isp->base + ipc->csr_out);
+ break;
+ case ENTRY:
+ case ENTRY | QUERY:
+ /*
+ * 1) Clear-by-1 CSR bits
+ * (IPC_PEER_COMP_ACTIONS_RST_PHASE1,
+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE).
+ * 2) Set peer CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1.
+ */
+ writel(ENTRY | QUERY, isp->base + ipc->csr_in);
+ writel(ENTRY, isp->base + ipc->csr_out);
+ break;
+ case EXIT:
+ case EXIT | QUERY:
+ /*
+ * Clear-by-1 CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ * 1) Clear incoming doorbell.
+ * 2) Clear-by-1 all CSR bits EXCEPT following
+ * bits:
+ * A. IPC_PEER_COMP_ACTIONS_RST_PHASE1.
+ * B. IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ * C. Possibly custom bits, depending on
+ * their role.
+ * 3) Set peer CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE2.
+ */
+ writel(EXIT, isp->base + ipc->csr_in);
+ writel(0, isp->base + ipc->db0_in);
+ writel(csr_in_clr, isp->base + ipc->csr_in);
+ writel(EXIT, isp->base + ipc->csr_out);
+
+ /*
+ * Read csr_in again to make sure if RST_PHASE2 is done.
+ * If csr_in is QUERY, it should be handled again.
+ */
+ usleep_range(200, 300);
+ val = readl(isp->base + ipc->csr_in);
+ if (val & QUERY) {
+ dev_dbg(dev,
+ "RST_PHASE2 retry csr_in = %x\n", val);
+ break;
+ }
+ mutex_unlock(&b->ipc_mutex);
+ return 0;
+ case QUERY:
+ /*
+ * 1) Clear-by-1 CSR bit
+ * IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
+ * 2) Set peer CSR bit
+ * IPC_PEER_COMP_ACTIONS_RST_PHASE1
+ */
+ writel(QUERY, isp->base + ipc->csr_in);
+ writel(ENTRY, isp->base + ipc->csr_out);
+ break;
+ default:
+ dev_dbg_ratelimited(dev, "Unexpected CSR 0x%x\n", val);
+ break;
+ }
+ } while (retries--);
+
+ mutex_unlock(&b->ipc_mutex);
+ dev_err(dev, "Timed out while waiting for CSE\n");
+
+ return -ETIMEDOUT;
+}
+
+static void ipu_buttress_ipc_validity_close(struct ipu7_device *isp,
+ struct ipu_buttress_ipc *ipc)
+{
+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
+ isp->base + ipc->csr_out);
+}
+
+static int
+ipu_buttress_ipc_validity_open(struct ipu7_device *isp,
+ struct ipu_buttress_ipc *ipc)
+{
+ unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
+ void __iomem *addr;
+ int ret;
+ u32 val;
+
+ writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
+ isp->base + ipc->csr_out);
+
+ addr = isp->base + ipc->csr_in;
+ ret = readl_poll_timeout(addr, val, val & mask, 200,
+ BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
+ ipu_buttress_ipc_validity_close(isp, ipc);
+ }
+
+ return ret;
+}
+
+static void ipu_buttress_ipc_recv(struct ipu7_device *isp,
+ struct ipu_buttress_ipc *ipc, u32 *ipc_msg)
+{
+ if (ipc_msg)
+ *ipc_msg = readl(isp->base + ipc->data0_in);
+ writel(0, isp->base + ipc->db0_in);
+}
+
+static int ipu_buttress_ipc_send_msg(struct ipu7_device *isp,
+ struct ipu7_ipc_buttress_msg *msg)
+{
+ unsigned long tx_timeout_jiffies, rx_timeout_jiffies;
+ unsigned int retry = BUTTRESS_IPC_CMD_SEND_RETRY;
+ struct ipu_buttress *b = &isp->buttress;
+ struct ipu_buttress_ipc *ipc = &b->cse;
+ struct device *dev = &isp->pdev->dev;
+ int tout;
+ u32 val;
+ int ret;
+
+ mutex_lock(&b->ipc_mutex);
+
+ ret = ipu_buttress_ipc_validity_open(isp, ipc);
+ if (ret) {
+ dev_err(dev, "IPC validity open failed\n");
+ goto out;
+ }
+
+ tx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_TX_TIMEOUT_MS);
+ rx_timeout_jiffies = msecs_to_jiffies(BUTTRESS_IPC_RX_TIMEOUT_MS);
+
+try:
+ reinit_completion(&ipc->send_complete);
+ if (msg->require_resp)
+ reinit_completion(&ipc->recv_complete);
+
+ dev_dbg(dev, "IPC command: 0x%x\n", msg->cmd);
+ writel(msg->cmd, isp->base + ipc->data0_out);
+ val = BUTTRESS_IU2CSEDB0_BUSY | msg->cmd_size;
+ writel(val, isp->base + ipc->db0_out);
+
+ tout = wait_for_completion_timeout(&ipc->send_complete,
+ tx_timeout_jiffies);
+ if (!tout) {
+ dev_err(dev, "send IPC response timeout\n");
+ if (!retry--) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /* Try again if CSE is not responding on first try */
+ writel(0, isp->base + ipc->db0_out);
+ goto try;
+ }
+
+ if (!msg->require_resp) {
+ ret = -EIO;
+ goto out;
+ }
+
+ tout = wait_for_completion_timeout(&ipc->recv_complete,
+ rx_timeout_jiffies);
+ if (!tout) {
+ dev_err(dev, "recv IPC response timeout\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ if (ipc->nack_mask &&
+ (ipc->recv_data & ipc->nack_mask) == ipc->nack) {
+ dev_err(dev, "IPC NACK for cmd 0x%x\n", msg->cmd);
+ ret = -EIO;
+ goto out;
+ }
+
+ if (ipc->recv_data != msg->expected_resp) {
+ dev_err(dev,
+ "expected resp: 0x%x, IPC response: 0x%x\n",
+ msg->expected_resp, ipc->recv_data);
+ ret = -EIO;
+ goto out;
+ }
+
+ dev_dbg(dev, "IPC commands done\n");
+
+out:
+ ipu_buttress_ipc_validity_close(isp, ipc);
+ mutex_unlock(&b->ipc_mutex);
+
+ return ret;
+}
+
+static int ipu_buttress_ipc_send(struct ipu7_device *isp,
+ u32 ipc_msg, u32 size, bool require_resp,
+ u32 expected_resp)
+{
+ struct ipu7_ipc_buttress_msg msg = {
+ .cmd = ipc_msg,
+ .cmd_size = size,
+ .require_resp = require_resp,
+ .expected_resp = expected_resp,
+ };
+
+ return ipu_buttress_ipc_send_msg(isp, &msg);
+}
+
+static irqreturn_t ipu_buttress_call_isr(struct ipu7_bus_device *adev)
+{
+ irqreturn_t ret = IRQ_WAKE_THREAD;
+
+ if (!adev || !adev->auxdrv || !adev->auxdrv_data)
+ return IRQ_NONE;
+
+ if (adev->auxdrv_data->isr)
+ ret = adev->auxdrv_data->isr(adev);
+
+ if (ret == IRQ_WAKE_THREAD && !adev->auxdrv_data->isr_threaded)
+ ret = IRQ_NONE;
+
+ return ret;
+}
+
+irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr)
+{
+ struct ipu7_device *isp = isp_ptr;
+ struct ipu7_bus_device *adev[] = { isp->isys, isp->psys };
+ struct ipu_buttress *b = &isp->buttress;
+ struct device *dev = &isp->pdev->dev;
+ irqreturn_t ret = IRQ_NONE;
+ u32 pb_irq, pb_local_irq;
+ u32 disable_irqs = 0;
+ u32 irq_status;
+ unsigned int i;
+
+ pm_runtime_get_noresume(dev);
+
+ pb_irq = readl(isp->pb_base + INTERRUPT_STATUS);
+ writel(pb_irq, isp->pb_base + INTERRUPT_STATUS);
+
+ /* check btrs ATS, CFI and IMR errors, BIT(0) is unused for IPU */
+ pb_local_irq = readl(isp->pb_base + BTRS_LOCAL_INTERRUPT_MASK);
+ if (pb_local_irq & ~BIT(0)) {
+ dev_warn(dev, "PB interrupt status 0x%x local 0x%x\n", pb_irq,
+ pb_local_irq);
+ dev_warn(dev, "Details: %x %x %x %x %x %x %x %x\n",
+ readl(isp->pb_base + ATS_ERROR_LOG1),
+ readl(isp->pb_base + ATS_ERROR_LOG2),
+ readl(isp->pb_base + CFI_0_ERROR_LOG),
+ readl(isp->pb_base + CFI_1_ERROR_LOGGING),
+ readl(isp->pb_base + IMR_ERROR_LOGGING_LOW),
+ readl(isp->pb_base + IMR_ERROR_LOGGING_HIGH),
+ readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_LOW),
+ readl(isp->pb_base + IMR_ERROR_LOGGING_CFI_1_HIGH));
+ }
+
+ irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS);
+ if (!irq_status) {
+ pm_runtime_put_noidle(dev);
+ return IRQ_NONE;
+ }
+
+ do {
+ writel(irq_status, isp->base + BUTTRESS_REG_IRQ_CLEAR);
+
+ for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask); i++) {
+ irqreturn_t r = ipu_buttress_call_isr(adev[i]);
+
+ if (!(irq_status & ipu7_adev_irq_mask[i]))
+ continue;
+
+ if (r == IRQ_WAKE_THREAD) {
+ ret = IRQ_WAKE_THREAD;
+ disable_irqs |= ipu7_adev_irq_mask[i];
+ } else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
+ ret = IRQ_HANDLED;
+ }
+ }
+
+ if (irq_status & (BUTTRESS_IRQS | BUTTRESS_IRQ_SAI_VIOLATION) &&
+ ret == IRQ_NONE)
+ ret = IRQ_HANDLED;
+
+ if (irq_status & BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING) {
+ dev_dbg(dev, "BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING\n");
+ ipu_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data);
+ complete(&b->cse.recv_complete);
+ }
+
+ if (irq_status & BUTTRESS_IRQ_CSE_CSR_SET)
+ dev_dbg(dev, "BUTTRESS_IRQ_CSE_CSR_SET\n");
+
+ if (irq_status & BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE) {
+ dev_dbg(dev, "BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE\n");
+ complete(&b->cse.send_complete);
+ }
+
+ if (irq_status & BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ)
+ dev_dbg(dev, "BUTTRESS_IRQ_PUNIT_2_IUNIT_IRQ\n");
+
+ if (irq_status & BUTTRESS_IRQ_SAI_VIOLATION &&
+ ipu_buttress_get_secure_mode(isp))
+ dev_err(dev, "BUTTRESS_IRQ_SAI_VIOLATION\n");
+
+ irq_status = readl(isp->base + BUTTRESS_REG_IRQ_STATUS);
+ } while (irq_status);
+
+ if (disable_irqs)
+ writel(BUTTRESS_IRQS & ~disable_irqs,
+ isp->base + BUTTRESS_REG_IRQ_ENABLE);
+
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr)
+{
+ struct ipu7_device *isp = isp_ptr;
+ struct ipu7_bus_device *adev[] = { isp->isys, isp->psys };
+ const struct ipu7_auxdrv_data *drv_data = NULL;
+ irqreturn_t ret = IRQ_NONE;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ipu7_adev_irq_mask) && adev[i]; i++) {
+ drv_data = adev[i]->auxdrv_data;
+ if (!drv_data)
+ continue;
+
+ if (drv_data->wake_isr_thread &&
+ drv_data->isr_threaded(adev[i]) == IRQ_HANDLED)
+ ret = IRQ_HANDLED;
+ }
+
+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE);
+
+ return ret;
+}
+
+static int isys_d2d_power(struct device *dev, bool on)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+ int ret = 0;
+ u32 target = on ? BUTTRESS_D2D_PWR_ACK : 0U;
+ u32 val;
+
+ dev_dbg(dev, "power %s isys d2d.\n", on ? "UP" : "DOWN");
+ val = readl(isp->base + BUTTRESS_REG_D2D_CTL);
+ if ((val & BUTTRESS_D2D_PWR_ACK) == target) {
+ dev_info(dev, "d2d already in %s state.\n",
+ on ? "UP" : "DOWN");
+ return 0;
+ }
+
+ val = on ? val | BUTTRESS_D2D_PWR_EN : val & (~BUTTRESS_D2D_PWR_EN);
+ writel(val, isp->base + BUTTRESS_REG_D2D_CTL);
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_D2D_CTL,
+ val, (val & BUTTRESS_D2D_PWR_ACK) == target,
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret)
+ dev_err(dev, "power %s d2d timeout. status: 0x%x\n",
+ on ? "UP" : "DOWN", val);
+
+ return ret;
+}
+
+static void isys_nde_control(struct device *dev, bool on)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+ u32 val, value, scale, valid, resvec;
+ u32 nde_reg;
+
+ if (on) {
+ value = BUTTRESS_NDE_VAL_ACTIVE;
+ scale = BUTTRESS_NDE_SCALE_ACTIVE;
+ valid = BUTTRESS_NDE_VALID_ACTIVE;
+ } else {
+ value = BUTTRESS_NDE_VAL_DEFAULT;
+ scale = BUTTRESS_NDE_SCALE_DEFAULT;
+ valid = BUTTRESS_NDE_VALID_DEFAULT;
+ }
+
+ /* only set the fabrics resource ownership for ipu8 */
+ nde_reg = is_ipu8(isp->hw_ver) ? IPU8_BUTTRESS_REG_NDE_CONTROL :
+ IPU7_BUTTRESS_REG_NDE_CONTROL;
+ resvec = is_ipu8(isp->hw_ver) ? 0x2 : 0xe;
+ val = FIELD_PREP(NDE_VAL_MASK, value) |
+ FIELD_PREP(NDE_SCALE_MASK, scale) |
+ FIELD_PREP(NDE_VALID_MASK, valid) |
+ FIELD_PREP(NDE_RESVEC_MASK, resvec);
+
+ writel(val, isp->base + nde_reg);
+}
+
+static int ipu7_buttress_powerup(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+ u32 val, exp_sts;
+ int ret = 0;
+
+ if (!ctrl)
+ return 0;
+
+ mutex_lock(&isp->buttress.power_mutex);
+
+ exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
+ if (ctrl->subsys_id == IPU_IS) {
+ ret = isys_d2d_power(dev, true);
+ if (ret)
+ goto out_power;
+ isys_nde_control(dev, true);
+ }
+
+ /* request clock resource ownership */
+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG);
+ val |= ctrl->ovrd_clk;
+ writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG);
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS,
+ val, (val & ctrl->own_clk_ack),
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret)
+ dev_warn(dev, "request clk ownership timeout. status 0x%x\n",
+ val);
+
+ val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift;
+
+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val,
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS");
+ writel(val, isp->base + ctrl->freq_ctl);
+
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS,
+ val, ((val & ctrl->pwr_sts_mask) == exp_sts),
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "%s power up timeout with status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+ goto out_power;
+ }
+
+ dev_dbg(dev, "%s power up successfully. status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+
+ /* release clock resource ownership */
+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG);
+ val &= ~ctrl->ovrd_clk;
+ writel(val, isp->base + BUTTRESS_REG_SLEEP_LEVEL_CFG);
+
+out_power:
+ mutex_unlock(&isp->buttress.power_mutex);
+
+ return ret;
+}
+
+static int ipu7_buttress_powerdown(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+ u32 val, exp_sts;
+ int ret = 0;
+
+ if (!ctrl)
+ return 0;
+
+ mutex_lock(&isp->buttress.power_mutex);
+
+ exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
+ val = 0x8U << ctrl->ratio_shift;
+
+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val,
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS");
+ writel(val, isp->base + ctrl->freq_ctl);
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS,
+ val, ((val & ctrl->pwr_sts_mask) == exp_sts),
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "%s power down timeout with status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+ goto out_power;
+ }
+
+ dev_dbg(dev, "%s power down successfully. status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+out_power:
+ if (ctrl->subsys_id == IPU_IS && !ret) {
+ isys_d2d_power(dev, false);
+ isys_nde_control(dev, false);
+ }
+
+ mutex_unlock(&isp->buttress.power_mutex);
+
+ return ret;
+}
+
+static int ipu8_buttress_powerup(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+ u32 sleep_level_reg = BUTTRESS_REG_SLEEP_LEVEL_STS;
+ u32 val, exp_sts;
+ int ret = 0;
+
+ if (!ctrl)
+ return 0;
+
+ mutex_lock(&isp->buttress.power_mutex);
+ exp_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
+ if (ctrl->subsys_id == IPU_IS) {
+ ret = isys_d2d_power(dev, true);
+ if (ret)
+ goto out_power;
+ isys_nde_control(dev, true);
+ }
+
+ /* request ps_pll when psys freq > 400Mhz */
+ if (ctrl->subsys_id == IPU_PS && ctrl->ratio > 0x10) {
+ writel(1, isp->base + BUTTRESS_REG_PS_PLL_ENABLE);
+ ret = readl_poll_timeout(isp->base + sleep_level_reg,
+ val, (val & ctrl->own_clk_ack),
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret)
+ dev_warn(dev, "ps_pll req ack timeout. status 0x%x\n",
+ val);
+ }
+
+ val = ctrl->ratio << ctrl->ratio_shift | ctrl->cdyn << ctrl->cdyn_shift;
+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val,
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS");
+ writel(val, isp->base + ctrl->freq_ctl);
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS,
+ val, ((val & ctrl->pwr_sts_mask) == exp_sts),
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "%s power up timeout with status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+ goto out_power;
+ }
+
+ dev_dbg(dev, "%s power up successfully. status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+out_power:
+ mutex_unlock(&isp->buttress.power_mutex);
+
+ return ret;
+}
+
+static int ipu8_buttress_powerdown(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+ u32 val, exp_sts;
+ int ret = 0;
+
+ if (!ctrl)
+ return 0;
+
+ mutex_lock(&isp->buttress.power_mutex);
+ exp_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
+
+ if (ctrl->subsys_id == IPU_PS)
+ val = 0x10U << ctrl->ratio_shift;
+ else
+ val = 0x8U << ctrl->ratio_shift;
+
+ dev_dbg(dev, "set 0x%x to %s_WORKPOINT_REQ.\n", val,
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS");
+ writel(val, isp->base + ctrl->freq_ctl);
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS,
+ val, ((val & ctrl->pwr_sts_mask) == exp_sts),
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "%s power down timeout with status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+ goto out_power;
+ }
+
+ dev_dbg(dev, "%s power down successfully. status: 0x%x\n",
+ ctrl->subsys_id == IPU_IS ? "IS" : "PS", val);
+out_power:
+ if (ctrl->subsys_id == IPU_IS && !ret) {
+ isys_d2d_power(dev, false);
+ isys_nde_control(dev, false);
+ }
+
+ if (ctrl->subsys_id == IPU_PS) {
+ val = readl(isp->base + BUTTRESS_REG_SLEEP_LEVEL_STS);
+ if (val & ctrl->own_clk_ack)
+ writel(0, isp->base + BUTTRESS_REG_PS_PLL_ENABLE);
+ }
+ mutex_unlock(&isp->buttress.power_mutex);
+
+ return ret;
+}
+
+int ipu_buttress_powerup(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+
+ if (is_ipu8(isp->hw_ver))
+ return ipu8_buttress_powerup(dev, ctrl);
+
+ return ipu7_buttress_powerup(dev, ctrl);
+}
+
+int ipu_buttress_powerdown(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl)
+{
+ struct ipu7_device *isp = to_ipu7_bus_device(dev)->isp;
+
+ if (is_ipu8(isp->hw_ver))
+ return ipu8_buttress_powerdown(dev, ctrl);
+
+ return ipu7_buttress_powerdown(dev, ctrl);
+}
+
+bool ipu_buttress_get_secure_mode(struct ipu7_device *isp)
+{
+ u32 val;
+
+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
+
+ return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
+}
+
+bool ipu_buttress_auth_done(struct ipu7_device *isp)
+{
+ u32 val;
+
+ if (!isp->secure_mode)
+ return true;
+
+ val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
+ val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
+
+ return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_auth_done, "INTEL_IPU7");
+
+int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq)
+{
+ u32 reg_val;
+ int ret;
+
+ ret = pm_runtime_get_sync(&isp->isys->auxdev.dev);
+ if (ret < 0) {
+ pm_runtime_put(&isp->isys->auxdev.dev);
+ dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret);
+ return ret;
+ }
+
+ reg_val = readl(isp->base + BUTTRESS_REG_IS_WORKPOINT_REQ);
+
+ pm_runtime_put(&isp->isys->auxdev.dev);
+
+ if (is_ipu8(isp->hw_ver))
+ *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 25;
+ else
+ *freq = (reg_val & BUTTRESS_IS_FREQ_CTL_RATIO_MASK) * 50 / 3;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_isys_freq, "INTEL_IPU7");
+
+int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq)
+{
+ u32 reg_val;
+ int ret;
+
+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev);
+ if (ret < 0) {
+ pm_runtime_put(&isp->psys->auxdev.dev);
+ dev_err(&isp->pdev->dev, "Runtime PM failed (%d)\n", ret);
+ return ret;
+ }
+
+ reg_val = readl(isp->base + BUTTRESS_REG_PS_WORKPOINT_REQ);
+
+ pm_runtime_put(&isp->psys->auxdev.dev);
+
+ reg_val &= BUTTRESS_PS_FREQ_CTL_RATIO_MASK;
+ *freq = BUTTRESS_PS_FREQ_RATIO_STEP * reg_val;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_get_psys_freq, "INTEL_IPU7");
+
+int ipu_buttress_reset_authentication(struct ipu7_device *isp)
+{
+ struct device *dev = &isp->pdev->dev;
+ int ret;
+ u32 val;
+
+ if (!isp->secure_mode) {
+ dev_dbg(dev, "Skip auth for non-secure mode\n");
+ return 0;
+ }
+
+ writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
+ BUTTRESS_REG_FW_RESET_CTL);
+
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
+ val & BUTTRESS_FW_RESET_CTL_DONE, 500,
+ BUTTRESS_CSE_FWRESET_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "Time out while resetting authentication state\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "FW reset for authentication done\n");
+ writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
+ /* leave some time for HW restore */
+ usleep_range(800, 1000);
+
+ return 0;
+}
+
+int ipu_buttress_authenticate(struct ipu7_device *isp)
+{
+ struct ipu_buttress *b = &isp->buttress;
+ struct device *dev = &isp->pdev->dev;
+ u32 data, mask, done, fail;
+ int ret;
+
+ if (!isp->secure_mode) {
+ dev_dbg(dev, "Skip auth for non-secure mode\n");
+ return 0;
+ }
+
+ mutex_lock(&b->auth_mutex);
+
+ if (ipu_buttress_auth_done(isp)) {
+ ret = 0;
+ goto out_unlock;
+ }
+
+ /*
+ * BUTTRESS_REG_FW_SOURCE_BASE needs to be set with FW CPD
+ * package address for secure mode.
+ */
+
+ writel(isp->cpd_fw->size, isp->base + BUTTRESS_REG_FW_SOURCE_SIZE);
+ writel(sg_dma_address(isp->psys->fw_sgt.sgl),
+ isp->base + BUTTRESS_REG_FW_SOURCE_BASE);
+
+ /*
+ * Write boot_load into IU2CSEDATA0
+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
+ */
+ dev_info(dev, "Sending BOOT_LOAD to CSE\n");
+ ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_BOOT_LOAD,
+ 1, true,
+ BUTTRESS_CSE2IUDATA0_IPC_BOOT_LOAD_DONE);
+ if (ret) {
+ dev_err(dev, "CSE boot_load failed\n");
+ goto out_unlock;
+ }
+
+ mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
+ done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
+ fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
+ ((data & mask) == done ||
+ (data & mask) == fail), 500,
+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "CSE boot_load timeout\n");
+ goto out_unlock;
+ }
+
+ if ((data & mask) == fail) {
+ dev_err(dev, "CSE auth failed\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ ret = readl_poll_timeout(isp->base + BOOTLOADER_STATUS_OFFSET,
+ data, data == BOOTLOADER_MAGIC_KEY, 500,
+ BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "Unexpected magic number 0x%x\n", data);
+ goto out_unlock;
+ }
+
+ /*
+ * Write authenticate_run into IU2CSEDATA0
+ * Write sizeof(boot_load) | 0x2 << CLIENT_ID to
+ * IU2CSEDB.IU2CSECMD and set IU2CSEDB.IU2CSEBUSY as
+ */
+ dev_info(dev, "Sending AUTHENTICATE_RUN to CSE\n");
+ ret = ipu_buttress_ipc_send(isp, BUTTRESS_IU2CSEDATA0_IPC_AUTH_RUN,
+ 1, true,
+ BUTTRESS_CSE2IUDATA0_IPC_AUTH_RUN_DONE);
+ if (ret) {
+ dev_err(dev, "CSE authenticate_run failed\n");
+ goto out_unlock;
+ }
+
+ done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
+ ((data & mask) == done ||
+ (data & mask) == fail), 500,
+ BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "CSE authenticate timeout\n");
+ goto out_unlock;
+ }
+
+ if ((data & mask) == fail) {
+ dev_err(dev, "CSE boot_load failed\n");
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+
+ dev_info(dev, "CSE authenticate_run done\n");
+
+out_unlock:
+ mutex_unlock(&b->auth_mutex);
+
+ return ret;
+}
+
+static int ipu_buttress_send_tsc_request(struct ipu7_device *isp)
+{
+ u32 val, mask, done;
+ int ret;
+
+ mask = BUTTRESS_PWR_STATUS_HH_STATUS_MASK;
+
+ writel(BUTTRESS_TSC_CMD_START_TSC_SYNC,
+ isp->base + BUTTRESS_REG_TSC_CMD);
+
+ val = readl(isp->base + BUTTRESS_REG_PWR_STATUS);
+ val = FIELD_GET(mask, val);
+ if (val == BUTTRESS_PWR_STATUS_HH_STATE_ERR) {
+ dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
+ return -EINVAL;
+ }
+
+ done = BUTTRESS_PWR_STATUS_HH_STATE_DONE;
+ ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATUS, val,
+ FIELD_GET(mask, val) == done, 500,
+ BUTTRESS_TSC_SYNC_TIMEOUT_US);
+ if (ret)
+ dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
+
+ return ret;
+}
+
+int ipu_buttress_start_tsc_sync(struct ipu7_device *isp)
+{
+ void __iomem *base = isp->base;
+ unsigned int i;
+ u32 val;
+
+ if (is_ipu8(isp->hw_ver)) {
+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
+ val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID);
+ if (val == 1)
+ return 0;
+ usleep_range(40, 50);
+ }
+
+ dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val);
+ return -ETIMEDOUT;
+ }
+
+ if (is_ipu7p5(isp->hw_ver)) {
+ val = readl(base + BUTTRESS_REG_TSC_CTL);
+ val |= BUTTRESS_SEL_PB_TIMESTAMP;
+ writel(val, base + BUTTRESS_REG_TSC_CTL);
+
+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
+ val = readl(base + BUTTRESS_REG_PB_TIMESTAMP_VALID);
+ if (val == 1)
+ return 0;
+ usleep_range(40, 50);
+ }
+
+ dev_err(&isp->pdev->dev, "PB HH sync failed (valid %u)\n", val);
+
+ return -ETIMEDOUT;
+ }
+
+ for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
+ int ret;
+
+ ret = ipu_buttress_send_tsc_request(isp);
+ if (ret != -ETIMEDOUT)
+ return ret;
+
+ val = readl(base + BUTTRESS_REG_TSC_CTL);
+ val = val | BUTTRESS_TSW_WA_SOFT_RESET;
+ writel(val, base + BUTTRESS_REG_TSC_CTL);
+ val = val & (~BUTTRESS_TSW_WA_SOFT_RESET);
+ writel(val, base + BUTTRESS_REG_TSC_CTL);
+ }
+
+ dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
+
+ return -ETIMEDOUT;
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_start_tsc_sync, "INTEL_IPU7");
+
+void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val)
+{
+ unsigned long flags;
+ u32 tsc_hi, tsc_lo;
+
+ local_irq_save(flags);
+ if (is_ipu7(isp->hw_ver)) {
+ tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO);
+ tsc_hi = readl(isp->base + BUTTRESS_REG_TSC_HI);
+ } else {
+ tsc_lo = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_LO);
+ tsc_hi = readl(isp->base + BUTTRESS_REG_PB_TIMESTAMP_HI);
+ }
+ *val = (u64)tsc_hi << 32 | tsc_lo;
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_read, "INTEL_IPU7");
+
+u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp)
+{
+ u64 ns = ticks * 10000;
+
+ /*
+ * converting TSC tick count to ns is calculated by:
+ * Example (TSC clock frequency is 19.2MHz):
+ * ns = ticks * 1000 000 000 / 19.2Mhz
+ * = ticks * 1000 000 000 / 19200000Hz
+ * = ticks * 10000 / 192 ns
+ */
+ return div_u64(ns, isp->buttress.ref_clk);
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_tsc_ticks_to_ns, "INTEL_IPU7");
+
+/* trigger uc control to wakeup fw */
+void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp)
+{
+ u32 val;
+
+ val = readl(isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS);
+ val |= UCX_CTL_WAKEUP;
+ writel(val, isp->base + BUTTRESS_REG_DRV_IS_UCX_CONTROL_STATUS);
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_is_uc, "INTEL_IPU7");
+
+void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp)
+{
+ u32 val;
+
+ val = readl(isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS);
+ val |= UCX_CTL_WAKEUP;
+ writel(val, isp->base + BUTTRESS_REG_DRV_PS_UCX_CONTROL_STATUS);
+}
+EXPORT_SYMBOL_NS_GPL(ipu_buttress_wakeup_ps_uc, "INTEL_IPU7");
+
+static const struct x86_cpu_id ipu_misc_cfg_exclusion[] = {
+ X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0),
+ {},
+};
+
+static void ipu_buttress_setup(struct ipu7_device *isp)
+{
+ struct device *dev = &isp->pdev->dev;
+ u32 val;
+
+ /* program PB BAR */
+#define WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19)
+ writel(0, isp->pb_base + GLOBAL_INTERRUPT_MASK);
+ val = readl(isp->pb_base + BAR2_MISC_CONFIG);
+ if (is_ipu7(isp->hw_ver) || x86_match_cpu(ipu_misc_cfg_exclusion))
+ val |= 0x100U;
+ else
+ val |= FIELD_PREP(WRXREQOP_OVRD_VAL_MASK, 0xf) |
+ BIT(18) | 0x100U;
+
+ writel(val, isp->pb_base + BAR2_MISC_CONFIG);
+ val = readl(isp->pb_base + BAR2_MISC_CONFIG);
+
+ if (is_ipu8(isp->hw_ver)) {
+ writel(BIT(13), isp->pb_base + TLBID_HASH_ENABLE_63_32);
+ writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64);
+ dev_dbg(dev, "IPU8 TLBID_HASH %x %x\n",
+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32),
+ readl(isp->pb_base + TLBID_HASH_ENABLE_95_64));
+ } else if (is_ipu7p5(isp->hw_ver)) {
+ writel(BIT(14), isp->pb_base + TLBID_HASH_ENABLE_63_32);
+ writel(BIT(9), isp->pb_base + TLBID_HASH_ENABLE_95_64);
+ dev_dbg(dev, "IPU7P5 TLBID_HASH %x %x\n",
+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32),
+ readl(isp->pb_base + TLBID_HASH_ENABLE_95_64));
+ } else {
+ writel(BIT(22), isp->pb_base + TLBID_HASH_ENABLE_63_32);
+ writel(BIT(1), isp->pb_base + TLBID_HASH_ENABLE_127_96);
+ dev_dbg(dev, "TLBID_HASH %x %x\n",
+ readl(isp->pb_base + TLBID_HASH_ENABLE_63_32),
+ readl(isp->pb_base + TLBID_HASH_ENABLE_127_96));
+ }
+
+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_CLEAR);
+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_MASK);
+ writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_IRQ_ENABLE);
+ /* LNL SW workaround for PS PD hang when PS sub-domain during PD */
+ writel(PS_FSM_CG, isp->base + BUTTRESS_REG_CG_CTRL_BITS);
+}
+
+void ipu_buttress_restore(struct ipu7_device *isp)
+{
+ struct ipu_buttress *b = &isp->buttress;
+
+ ipu_buttress_setup(isp);
+
+ writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_IDLE_WDT);
+}
+
+int ipu_buttress_init(struct ipu7_device *isp)
+{
+ int ret, ipc_reset_retry = BUTTRESS_CSE_IPC_RESET_RETRY;
+ struct ipu_buttress *b = &isp->buttress;
+ struct device *dev = &isp->pdev->dev;
+ u32 val;
+
+ mutex_init(&b->power_mutex);
+ mutex_init(&b->auth_mutex);
+ mutex_init(&b->cons_mutex);
+ mutex_init(&b->ipc_mutex);
+ init_completion(&b->cse.send_complete);
+ init_completion(&b->cse.recv_complete);
+
+ b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK;
+ b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK;
+ b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR;
+ b->cse.csr_out = BUTTRESS_REG_IU2CSECSR;
+ b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0;
+ b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0;
+ b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0;
+ b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0;
+
+ isp->secure_mode = ipu_buttress_get_secure_mode(isp);
+ val = readl(isp->base + BUTTRESS_REG_IPU_SKU);
+ dev_info(dev, "IPU%u SKU %u in %s mode mask 0x%x\n", val & 0xfU,
+ (val >> 4) & 0x7U, isp->secure_mode ? "secure" : "non-secure",
+ readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
+ b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_IDLE_WDT);
+ b->ref_clk = 384;
+
+ ipu_buttress_setup(isp);
+
+ /* Retry couple of times in case of CSE initialization is delayed */
+ do {
+ ret = ipu_buttress_ipc_reset(isp, &b->cse);
+ if (ret) {
+ dev_warn(dev, "IPC reset protocol failed, retrying\n");
+ } else {
+ dev_dbg(dev, "IPC reset done\n");
+ return 0;
+ }
+ } while (ipc_reset_retry--);
+
+ dev_err(dev, "IPC reset protocol failed\n");
+
+ mutex_destroy(&b->power_mutex);
+ mutex_destroy(&b->auth_mutex);
+ mutex_destroy(&b->cons_mutex);
+ mutex_destroy(&b->ipc_mutex);
+
+ return ret;
+}
+
+void ipu_buttress_exit(struct ipu7_device *isp)
+{
+ struct ipu_buttress *b = &isp->buttress;
+
+ writel(0, isp->base + BUTTRESS_REG_IRQ_ENABLE);
+ mutex_destroy(&b->power_mutex);
+ mutex_destroy(&b->auth_mutex);
+ mutex_destroy(&b->cons_mutex);
+ mutex_destroy(&b->ipc_mutex);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-buttress.h b/drivers/staging/media/ipu7/ipu7-buttress.h
new file mode 100644
index 000000000000..8da7dd612575
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-buttress.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_BUTTRESS_H
+#define IPU7_BUTTRESS_H
+
+#include <linux/completion.h>
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+struct device;
+struct ipu7_device;
+
+struct ipu_buttress_ctrl {
+ u32 subsys_id;
+ u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off;
+ u32 ratio;
+ u32 ratio_shift;
+ u32 cdyn;
+ u32 cdyn_shift;
+ u32 ovrd_clk;
+ u32 own_clk_ack;
+};
+
+struct ipu_buttress_ipc {
+ struct completion send_complete;
+ struct completion recv_complete;
+ u32 nack;
+ u32 nack_mask;
+ u32 recv_data;
+ u32 csr_out;
+ u32 csr_in;
+ u32 db0_in;
+ u32 db0_out;
+ u32 data0_out;
+ u32 data0_in;
+};
+
+struct ipu_buttress {
+ struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex;
+ struct ipu_buttress_ipc cse;
+ u32 psys_min_freq;
+ u32 wdt_cached_value;
+ u8 psys_force_ratio;
+ bool force_suspend;
+ u32 ref_clk;
+};
+
+int ipu_buttress_ipc_reset(struct ipu7_device *isp,
+ struct ipu_buttress_ipc *ipc);
+int ipu_buttress_powerup(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl);
+int ipu_buttress_powerdown(struct device *dev,
+ const struct ipu_buttress_ctrl *ctrl);
+bool ipu_buttress_get_secure_mode(struct ipu7_device *isp);
+int ipu_buttress_authenticate(struct ipu7_device *isp);
+int ipu_buttress_reset_authentication(struct ipu7_device *isp);
+bool ipu_buttress_auth_done(struct ipu7_device *isp);
+int ipu_buttress_get_isys_freq(struct ipu7_device *isp, u32 *freq);
+int ipu_buttress_get_psys_freq(struct ipu7_device *isp, u32 *freq);
+int ipu_buttress_start_tsc_sync(struct ipu7_device *isp);
+void ipu_buttress_tsc_read(struct ipu7_device *isp, u64 *val);
+u64 ipu_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu7_device *isp);
+
+irqreturn_t ipu_buttress_isr(int irq, void *isp_ptr);
+irqreturn_t ipu_buttress_isr_threaded(int irq, void *isp_ptr);
+int ipu_buttress_init(struct ipu7_device *isp);
+void ipu_buttress_exit(struct ipu7_device *isp);
+void ipu_buttress_csi_port_config(struct ipu7_device *isp,
+ u32 legacy, u32 combo);
+void ipu_buttress_restore(struct ipu7_device *isp);
+void ipu_buttress_wakeup_is_uc(const struct ipu7_device *isp);
+void ipu_buttress_wakeup_ps_uc(const struct ipu7_device *isp);
+#endif /* IPU7_BUTTRESS_H */
diff --git a/drivers/staging/media/ipu7/ipu7-cpd.c b/drivers/staging/media/ipu7/ipu7-cpd.c
new file mode 100644
index 000000000000..4f49fb57eae4
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-cpd.c
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015 - 2025 Intel Corporation
+ */
+
+#include <linux/device.h>
+#include <linux/export.h>
+#include <linux/gfp_types.h>
+#include <linux/pci.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "ipu7.h"
+#include "ipu7-cpd.h"
+
+/* $CPD */
+#define CPD_HDR_MARK 0x44504324
+
+/* Maximum size is 4K DWORDs or 16KB */
+#define MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32))
+
+#define CPD_MANIFEST_IDX 0
+#define CPD_BINARY_START_IDX 1U
+#define CPD_METADATA_START_IDX 2U
+#define CPD_BINARY_NUM 2U /* ISYS + PSYS */
+/*
+ * Entries include:
+ * 1 manifest entry.
+ * 1 metadata entry for each sub system(ISYS and PSYS).
+ * 1 binary entry for each sub system(ISYS and PSYS).
+ */
+#define CPD_ENTRY_NUM (CPD_BINARY_NUM * 2U + 1U)
+
+#define CPD_METADATA_ATTR 0xa
+#define CPD_METADATA_IPL 0x1c
+#define ONLINE_METADATA_SIZE 128U
+#define ONLINE_METADATA_LINES 6U
+
+struct ipu7_cpd_hdr {
+ u32 hdr_mark;
+ u32 ent_cnt;
+ u8 hdr_ver;
+ u8 ent_ver;
+ u8 hdr_len;
+ u8 rsvd;
+ u8 partition_name[4];
+ u32 crc32;
+} __packed;
+
+struct ipu7_cpd_ent {
+ u8 name[12];
+ u32 offset;
+ u32 len;
+ u8 rsvd[4];
+} __packed;
+
+struct ipu7_cpd_metadata_hdr {
+ u32 type;
+ u32 len;
+} __packed;
+
+struct ipu7_cpd_metadata_attr {
+ struct ipu7_cpd_metadata_hdr hdr;
+ u8 compression_type;
+ u8 encryption_type;
+ u8 rsvd[2];
+ u32 uncompressed_size;
+ u32 compressed_size;
+ u32 module_id;
+ u8 hash[48];
+} __packed;
+
+struct ipu7_cpd_metadata_ipl {
+ struct ipu7_cpd_metadata_hdr hdr;
+ u32 param[4];
+ u8 rsvd[8];
+} __packed;
+
+struct ipu7_cpd_metadata {
+ struct ipu7_cpd_metadata_attr attr;
+ struct ipu7_cpd_metadata_ipl ipl;
+} __packed;
+
+static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx)
+{
+ const struct ipu7_cpd_hdr *cpd_hdr = cpd;
+
+ return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx;
+}
+
+#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0)
+
+static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx)
+{
+ struct ipu7_cpd_ent *cpd_ent =
+ ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2);
+
+ return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset);
+}
+
+static int ipu7_cpd_validate_cpd(struct ipu7_device *isp,
+ const void *cpd, unsigned long data_size)
+{
+ const struct ipu7_cpd_hdr *cpd_hdr = cpd;
+ struct device *dev = &isp->pdev->dev;
+ struct ipu7_cpd_ent *ent;
+ unsigned int i;
+ u8 len;
+
+ len = cpd_hdr->hdr_len;
+
+ /* Ensure cpd hdr is within moduledata */
+ if (data_size < len) {
+ dev_err(dev, "Invalid CPD moduledata size\n");
+ return -EINVAL;
+ }
+
+ /* Check for CPD file marker */
+ if (cpd_hdr->hdr_mark != CPD_HDR_MARK) {
+ dev_err(dev, "Invalid CPD header marker\n");
+ return -EINVAL;
+ }
+
+ /* Sanity check for CPD entry header */
+ if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) {
+ dev_err(dev, "Invalid CPD entry number %d\n",
+ cpd_hdr->ent_cnt);
+ return -EINVAL;
+ }
+ if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
+ dev_err(dev, "Invalid CPD entry headers\n");
+ return -EINVAL;
+ }
+
+ /* Ensure that all entries are within moduledata */
+ ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len);
+ for (i = 0; i < cpd_hdr->ent_cnt; i++) {
+ if (data_size < ent->offset ||
+ data_size - ent->offset < ent->len) {
+ dev_err(dev, "Invalid CPD entry %d\n", i);
+ return -EINVAL;
+ }
+ ent++;
+ }
+
+ return 0;
+}
+
+static int ipu7_cpd_validate_metadata(struct ipu7_device *isp,
+ const void *cpd, int idx)
+{
+ const struct ipu7_cpd_ent *cpd_ent =
+ ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2);
+ const struct ipu7_cpd_metadata *metadata =
+ ipu7_cpd_get_metadata(cpd, idx);
+ struct device *dev = &isp->pdev->dev;
+
+ /* Sanity check for metadata size */
+ if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) {
+ dev_err(dev, "Invalid metadata size\n");
+ return -EINVAL;
+ }
+
+ /* Validate type and length of metadata sections */
+ if (metadata->attr.hdr.type != CPD_METADATA_ATTR) {
+ dev_err(dev, "Invalid metadata attr type (%d)\n",
+ metadata->attr.hdr.type);
+ return -EINVAL;
+ }
+ if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) {
+ dev_err(dev, "Invalid metadata attr size (%d)\n",
+ metadata->attr.hdr.len);
+ return -EINVAL;
+ }
+ if (metadata->ipl.hdr.type != CPD_METADATA_IPL) {
+ dev_err(dev, "Invalid metadata ipl type (%d)\n",
+ metadata->ipl.hdr.type);
+ return -EINVAL;
+ }
+ if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) {
+ dev_err(dev, "Invalid metadata ipl size (%d)\n",
+ metadata->ipl.hdr.len);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file,
+ unsigned long cpd_file_size)
+{
+ struct device *dev = &isp->pdev->dev;
+ struct ipu7_cpd_ent *ent;
+ unsigned int i;
+ int ret;
+ char *buf;
+
+ ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size);
+ if (ret) {
+ dev_err(dev, "Invalid CPD in file\n");
+ return -EINVAL;
+ }
+
+ /* Sanity check for manifest size */
+ ent = ipu7_cpd_get_manifest(cpd_file);
+ if (ent->len > MAX_MANIFEST_SIZE) {
+ dev_err(dev, "Invalid manifest size\n");
+ return -EINVAL;
+ }
+
+ /* Validate metadata */
+ for (i = 0; i < CPD_BINARY_NUM; i++) {
+ ret = ipu7_cpd_validate_metadata(isp, cpd_file, i);
+ if (ret) {
+ dev_err(dev, "Invalid metadata%d\n", i);
+ return ret;
+ }
+ }
+
+ /* Get fw binary version. */
+ buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ for (i = 0; i < CPD_BINARY_NUM; i++) {
+ char *lines[ONLINE_METADATA_LINES];
+ char *info = buf;
+ unsigned int l;
+
+ ent = ipu7_cpd_get_entry(cpd_file,
+ CPD_BINARY_START_IDX + i * 2U);
+ memcpy(info, (u8 *)cpd_file + ent->offset + ent->len -
+ ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE);
+ for (l = 0; l < ONLINE_METADATA_LINES; l++) {
+ lines[l] = strsep((char **)&info, "\n");
+ if (!lines[l])
+ break;
+ }
+ if (l < ONLINE_METADATA_LINES) {
+ dev_err(dev, "Failed to parse fw binary%d info.\n", i);
+ continue;
+ }
+ dev_info(dev, "FW binary%d info:\n", i);
+ dev_info(dev, "Name: %s\n", lines[1]);
+ dev_info(dev, "Version: %s\n", lines[2]);
+ dev_info(dev, "Timestamp: %s\n", lines[3]);
+ dev_info(dev, "Commit: %s\n", lines[4]);
+ }
+ kfree(buf);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7");
+
+int ipu7_cpd_copy_binary(const void *cpd, const char *name,
+ void *code_region, u32 *entry)
+{
+ unsigned int i;
+
+ for (i = 0; i < CPD_BINARY_NUM; i++) {
+ const struct ipu7_cpd_ent *binary =
+ ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U);
+ const struct ipu7_cpd_metadata *metadata =
+ ipu7_cpd_get_metadata(cpd, i);
+
+ if (!strncmp(binary->name, name, sizeof(binary->name))) {
+ memcpy(code_region + metadata->ipl.param[0],
+ cpd + binary->offset, binary->len);
+ *entry = metadata->ipl.param[2];
+ return 0;
+ }
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7");
diff --git a/drivers/staging/media/ipu7/ipu7-cpd.h b/drivers/staging/media/ipu7/ipu7-cpd.h
new file mode 100644
index 000000000000..b4178848c6b9
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-cpd.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2015 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_CPD_H
+#define IPU7_CPD_H
+
+struct ipu7_device;
+
+int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp,
+ const void *cpd_file,
+ unsigned long cpd_file_size);
+int ipu7_cpd_copy_binary(const void *cpd, const char *name,
+ void *code_region, u32 *entry);
+#endif /* IPU7_CPD_H */
diff --git a/drivers/staging/media/ipu7/ipu7-dma.c b/drivers/staging/media/ipu7/ipu7-dma.c
new file mode 100644
index 000000000000..a118b41b2f34
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-dma.c
@@ -0,0 +1,477 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/cacheflush.h>
+#include <linux/dma-mapping.h>
+#include <linux/iova.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-dma.h"
+#include "ipu7-mmu.h"
+
+struct vm_info {
+ struct list_head list;
+ struct page **pages;
+ dma_addr_t ipu7_iova;
+ void *vaddr;
+ unsigned long size;
+};
+
+static struct vm_info *get_vm_info(struct ipu7_mmu *mmu, dma_addr_t iova)
+{
+ struct vm_info *info, *save;
+
+ list_for_each_entry_safe(info, save, &mmu->vma_list, list) {
+ if (iova >= info->ipu7_iova &&
+ iova < (info->ipu7_iova + info->size))
+ return info;
+ }
+
+ return NULL;
+}
+
+static void __clear_buffer(struct page *page, size_t size, unsigned long attrs)
+{
+ void *ptr;
+
+ if (!page)
+ return;
+ /*
+ * Ensure that the allocated pages are zeroed, and that any data
+ * lurking in the kernel direct-mapped region is invalidated.
+ */
+ ptr = page_address(page);
+ memset(ptr, 0, size);
+ if ((attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
+ clflush_cache_range(ptr, size);
+}
+
+static struct page **__alloc_buffer(size_t size, gfp_t gfp, unsigned long attrs)
+{
+ unsigned int count = PHYS_PFN(size);
+ unsigned int array_size = count * sizeof(struct page *);
+ struct page **pages;
+ int i = 0;
+
+ pages = kvzalloc(array_size, GFP_KERNEL);
+ if (!pages)
+ return NULL;
+
+ gfp |= __GFP_NOWARN;
+
+ while (count) {
+ int j, order = __fls(count);
+
+ pages[i] = alloc_pages(gfp, order);
+ while (!pages[i] && order)
+ pages[i] = alloc_pages(gfp, --order);
+ if (!pages[i])
+ goto error;
+
+ if (order) {
+ split_page(pages[i], order);
+ j = 1U << order;
+ while (j--)
+ pages[i + j] = pages[i] + j;
+ }
+
+ __clear_buffer(pages[i], PAGE_SIZE << order, attrs);
+ i += 1U << order;
+ count -= 1U << order;
+ }
+
+ return pages;
+error:
+ while (i--)
+ if (pages[i])
+ __free_pages(pages[i], 0);
+ kvfree(pages);
+ return NULL;
+}
+
+static void __free_buffer(struct page **pages, size_t size, unsigned long attrs)
+{
+ unsigned int count = PHYS_PFN(size);
+ unsigned int i;
+
+ for (i = 0; i < count && pages[i]; i++) {
+ __clear_buffer(pages[i], PAGE_SIZE, attrs);
+ __free_pages(pages[i], 0);
+ }
+
+ kvfree(pages);
+}
+
+void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle,
+ size_t size)
+{
+ void *vaddr;
+ u32 offset;
+ struct vm_info *info;
+ struct ipu7_mmu *mmu = sys->mmu;
+
+ info = get_vm_info(mmu, dma_handle);
+ if (WARN_ON(!info))
+ return;
+
+ offset = dma_handle - info->ipu7_iova;
+ if (WARN_ON(size > (info->size - offset)))
+ return;
+
+ vaddr = info->vaddr + offset;
+ clflush_cache_range(vaddr, size);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_single, "INTEL_IPU7");
+
+void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist,
+ unsigned int nents)
+{
+ struct scatterlist *sg;
+ int i;
+
+ for_each_sg(sglist, sg, nents, i)
+ clflush_cache_range(sg_virt(sg), sg->length);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sg, "INTEL_IPU7");
+
+void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt)
+{
+ ipu7_dma_sync_sg(sys, sgt->sgl, sgt->orig_nents);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_sync_sgtable, "INTEL_IPU7");
+
+void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp,
+ unsigned long attrs)
+{
+ struct device *dev = &sys->auxdev.dev;
+ struct pci_dev *pdev = sys->isp->pdev;
+ dma_addr_t pci_dma_addr, ipu7_iova;
+ struct ipu7_mmu *mmu = sys->mmu;
+ struct vm_info *info;
+ unsigned long count;
+ struct page **pages;
+ struct iova *iova;
+ unsigned int i;
+ int ret;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return NULL;
+
+ size = PAGE_ALIGN(size);
+ count = PHYS_PFN(size);
+
+ iova = alloc_iova(&mmu->dmap->iovad, count,
+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
+ if (!iova)
+ goto out_kfree;
+
+ pages = __alloc_buffer(size, gfp, attrs);
+ if (!pages)
+ goto out_free_iova;
+
+ dev_dbg(dev, "dma_alloc: size %zu iova low pfn %lu, high pfn %lu\n",
+ size, iova->pfn_lo, iova->pfn_hi);
+ for (i = 0; iova->pfn_lo + i <= iova->pfn_hi; i++) {
+ pci_dma_addr = dma_map_page_attrs(&pdev->dev, pages[i], 0,
+ PAGE_SIZE, DMA_BIDIRECTIONAL,
+ attrs);
+ dev_dbg(dev, "dma_alloc: mapped pci_dma_addr %pad\n",
+ &pci_dma_addr);
+ if (dma_mapping_error(&pdev->dev, pci_dma_addr)) {
+ dev_err(dev, "pci_dma_mapping for page[%d] failed", i);
+ goto out_unmap;
+ }
+
+ ret = ipu7_mmu_map(mmu->dmap->mmu_info,
+ PFN_PHYS(iova->pfn_lo + i), pci_dma_addr,
+ PAGE_SIZE);
+ if (ret) {
+ dev_err(dev, "ipu7_mmu_map for pci_dma[%d] %pad failed",
+ i, &pci_dma_addr);
+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr,
+ PAGE_SIZE, DMA_BIDIRECTIONAL,
+ attrs);
+ goto out_unmap;
+ }
+ }
+
+ info->vaddr = vmap(pages, count, VM_USERMAP, PAGE_KERNEL);
+ if (!info->vaddr)
+ goto out_unmap;
+
+ *dma_handle = PFN_PHYS(iova->pfn_lo);
+
+ info->pages = pages;
+ info->ipu7_iova = *dma_handle;
+ info->size = size;
+ list_add(&info->list, &mmu->vma_list);
+
+ return info->vaddr;
+
+out_unmap:
+ while (i--) {
+ ipu7_iova = PFN_PHYS(iova->pfn_lo + i);
+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info,
+ ipu7_iova);
+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
+ DMA_BIDIRECTIONAL, attrs);
+
+ ipu7_mmu_unmap(mmu->dmap->mmu_info, ipu7_iova, PAGE_SIZE);
+ }
+
+ __free_buffer(pages, size, attrs);
+
+out_free_iova:
+ __free_iova(&mmu->dmap->iovad, iova);
+out_kfree:
+ kfree(info);
+
+ return NULL;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_alloc, "INTEL_IPU7");
+
+void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr,
+ dma_addr_t dma_handle, unsigned long attrs)
+{
+ struct ipu7_mmu *mmu = sys->mmu;
+ struct pci_dev *pdev = sys->isp->pdev;
+ struct iova *iova = find_iova(&mmu->dmap->iovad, PHYS_PFN(dma_handle));
+ dma_addr_t pci_dma_addr, ipu7_iova;
+ struct vm_info *info;
+ struct page **pages;
+ unsigned int i;
+
+ if (WARN_ON(!iova))
+ return;
+
+ info = get_vm_info(mmu, dma_handle);
+ if (WARN_ON(!info))
+ return;
+
+ if (WARN_ON(!info->vaddr))
+ return;
+
+ if (WARN_ON(!info->pages))
+ return;
+
+ list_del(&info->list);
+
+ size = PAGE_ALIGN(size);
+
+ pages = info->pages;
+
+ vunmap(vaddr);
+
+ for (i = 0; i < PHYS_PFN(size); i++) {
+ ipu7_iova = PFN_PHYS(iova->pfn_lo + i);
+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info,
+ ipu7_iova);
+ dma_unmap_page_attrs(&pdev->dev, pci_dma_addr, PAGE_SIZE,
+ DMA_BIDIRECTIONAL, attrs);
+ }
+
+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+
+ __free_buffer(pages, size, attrs);
+
+ mmu->tlb_invalidate(mmu);
+
+ __free_iova(&mmu->dmap->iovad, iova);
+
+ kfree(info);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_free, "INTEL_IPU7");
+
+int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma,
+ void *addr, dma_addr_t iova, size_t size,
+ unsigned long attrs)
+{
+ struct ipu7_mmu *mmu = sys->mmu;
+ size_t count = PFN_UP(size);
+ struct vm_info *info;
+ size_t i;
+ int ret;
+
+ info = get_vm_info(mmu, iova);
+ if (!info)
+ return -EFAULT;
+
+ if (!info->vaddr)
+ return -EFAULT;
+
+ if (vma->vm_start & ~PAGE_MASK)
+ return -EINVAL;
+
+ if (size > info->size)
+ return -EFAULT;
+
+ for (i = 0; i < count; i++) {
+ ret = vm_insert_page(vma, vma->vm_start + PFN_PHYS(i),
+ info->pages[i]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist,
+ int nents, enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ struct device *dev = &sys->auxdev.dev;
+ struct ipu7_mmu *mmu = sys->mmu;
+ struct iova *iova = find_iova(&mmu->dmap->iovad,
+ PHYS_PFN(sg_dma_address(sglist)));
+ struct scatterlist *sg;
+ dma_addr_t pci_dma_addr;
+ unsigned int i;
+
+ if (!nents)
+ return;
+
+ if (WARN_ON(!iova))
+ return;
+
+ /*
+ * Before IPU7 mmu unmap, return the pci dma address back to sg
+ * assume the nents is less than orig_nents as the least granule
+ * is 1 SZ_4K page
+ */
+ dev_dbg(dev, "trying to unmap concatenated %u ents\n", nents);
+ for_each_sg(sglist, sg, nents, i) {
+ dev_dbg(dev, "unmap sg[%d] %pad size %u\n", i,
+ &sg_dma_address(sg), sg_dma_len(sg));
+ pci_dma_addr = ipu7_mmu_iova_to_phys(mmu->dmap->mmu_info,
+ sg_dma_address(sg));
+ dev_dbg(dev, "return pci_dma_addr %pad back to sg[%d]\n",
+ &pci_dma_addr, i);
+ sg_dma_address(sg) = pci_dma_addr;
+ }
+
+ dev_dbg(dev, "ipu7_mmu_unmap low pfn %lu high pfn %lu\n",
+ iova->pfn_lo, iova->pfn_hi);
+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+
+ mmu->tlb_invalidate(mmu);
+ __free_iova(&mmu->dmap->iovad, iova);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sg, "INTEL_IPU7");
+
+int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist,
+ int nents, enum dma_data_direction dir,
+ unsigned long attrs)
+{
+ struct device *dev = &sys->auxdev.dev;
+ struct ipu7_mmu *mmu = sys->mmu;
+ struct scatterlist *sg;
+ struct iova *iova;
+ size_t npages = 0;
+ unsigned long iova_addr;
+ int i;
+
+ for_each_sg(sglist, sg, nents, i) {
+ if (sg->offset) {
+ dev_err(dev, "Unsupported non-zero sg[%d].offset %x\n",
+ i, sg->offset);
+ return -EFAULT;
+ }
+ }
+
+ for_each_sg(sglist, sg, nents, i)
+ npages += PFN_UP(sg_dma_len(sg));
+
+ dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n",
+ nents, npages);
+
+ if (attrs & DMA_ATTR_RESERVE_REGION) {
+ /*
+ * Reserve iova with size aligned to IPU_FW_CODE_REGION_SIZE.
+ * Only apply for non-secure mode.
+ */
+ unsigned long lo, hi;
+
+ lo = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_START);
+ hi = iova_pfn(&mmu->dmap->iovad, IPU_FW_CODE_REGION_END) - 1U;
+ iova = reserve_iova(&mmu->dmap->iovad, lo, hi);
+ if (!iova) {
+ dev_err(dev, "Reserve iova[%lx:%lx] failed.\n", lo, hi);
+ return -ENOMEM;
+ }
+ dev_dbg(dev, "iova[%lx:%lx] reserved for FW code.\n", lo, hi);
+ } else {
+ iova = alloc_iova(&mmu->dmap->iovad, npages,
+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end),
+ 0);
+ if (!iova)
+ return 0;
+ }
+
+ dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo,
+ iova->pfn_hi);
+
+ iova_addr = iova->pfn_lo;
+ for_each_sg(sglist, sg, nents, i) {
+ phys_addr_t iova_pa;
+ int ret;
+
+ iova_pa = PFN_PHYS(iova_addr);
+ dev_dbg(dev, "mapping entry %d: iova %pap phy %pap size %d\n",
+ i, &iova_pa, &sg_dma_address(sg), sg_dma_len(sg));
+
+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
+ sg_dma_address(sg),
+ PAGE_ALIGN(sg_dma_len(sg)));
+ if (ret)
+ goto out_fail;
+
+ sg_dma_address(sg) = PFN_PHYS(iova_addr);
+
+ iova_addr += PFN_UP(sg_dma_len(sg));
+ }
+
+ dev_dbg(dev, "dmamap %d ents %zu pages mapped\n", nents, npages);
+
+ return nents;
+
+out_fail:
+ ipu7_dma_unmap_sg(sys, sglist, i, dir, attrs);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sg, "INTEL_IPU7");
+
+int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt,
+ enum dma_data_direction dir, unsigned long attrs)
+{
+ int nents;
+
+ nents = ipu7_dma_map_sg(sys, sgt->sgl, sgt->nents, dir, attrs);
+ if (nents < 0)
+ return nents;
+
+ sgt->nents = nents;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_map_sgtable, "INTEL_IPU7");
+
+void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt,
+ enum dma_data_direction dir, unsigned long attrs)
+{
+ ipu7_dma_unmap_sg(sys, sgt->sgl, sgt->nents, dir, attrs);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dma_unmap_sgtable, "INTEL_IPU7");
diff --git a/drivers/staging/media/ipu7/ipu7-dma.h b/drivers/staging/media/ipu7/ipu7-dma.h
new file mode 100644
index 000000000000..fe789af5e664
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-dma.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2013--2025 Intel Corporation */
+
+#ifndef IPU7_DMA_H
+#define IPU7_DMA_H
+
+#include <linux/dma-map-ops.h>
+#include <linux/dma-mapping.h>
+#include <linux/iova.h>
+#include <linux/scatterlist.h>
+#include <linux/types.h>
+
+#include "ipu7-bus.h"
+
+#define DMA_ATTR_RESERVE_REGION BIT(31)
+struct ipu7_mmu_info;
+
+struct ipu7_dma_mapping {
+ struct ipu7_mmu_info *mmu_info;
+ struct iova_domain iovad;
+};
+
+void ipu7_dma_sync_single(struct ipu7_bus_device *sys, dma_addr_t dma_handle,
+ size_t size);
+void ipu7_dma_sync_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist,
+ unsigned int nents);
+void ipu7_dma_sync_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt);
+void *ipu7_dma_alloc(struct ipu7_bus_device *sys, size_t size,
+ dma_addr_t *dma_handle, gfp_t gfp,
+ unsigned long attrs);
+void ipu7_dma_free(struct ipu7_bus_device *sys, size_t size, void *vaddr,
+ dma_addr_t dma_handle, unsigned long attrs);
+int ipu7_dma_mmap(struct ipu7_bus_device *sys, struct vm_area_struct *vma,
+ void *addr, dma_addr_t iova, size_t size,
+ unsigned long attrs);
+int ipu7_dma_map_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist,
+ int nents, enum dma_data_direction dir,
+ unsigned long attrs);
+void ipu7_dma_unmap_sg(struct ipu7_bus_device *sys, struct scatterlist *sglist,
+ int nents, enum dma_data_direction dir,
+ unsigned long attrs);
+int ipu7_dma_map_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt,
+ enum dma_data_direction dir, unsigned long attrs);
+void ipu7_dma_unmap_sgtable(struct ipu7_bus_device *sys, struct sg_table *sgt,
+ enum dma_data_direction dir, unsigned long attrs);
+#endif /* IPU7_DMA_H */
diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.c b/drivers/staging/media/ipu7/ipu7-fw-isys.c
new file mode 100644
index 000000000000..e4b9c364572b
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/cacheflush.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include "abi/ipu7_fw_insys_config_abi.h"
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-boot.h"
+#include "ipu7-bus.h"
+#include "ipu7-dma.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-isys.h"
+#include "ipu7-platform-regs.h"
+#include "ipu7-syscom.h"
+
+static const char * const send_msg_types[N_IPU_INSYS_SEND_TYPE] = {
+ "STREAM_OPEN",
+ "STREAM_START_AND_CAPTURE",
+ "STREAM_CAPTURE",
+ "STREAM_ABORT",
+ "STREAM_FLUSH",
+ "STREAM_CLOSE"
+};
+
+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys,
+ const unsigned int stream_handle,
+ void *cpu_mapped_buf,
+ dma_addr_t dma_mapped_buf,
+ size_t size, u16 send_type)
+{
+ struct ipu7_syscom_context *ctx = isys->adev->syscom;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct ipu7_insys_send_queue_token *token;
+
+ if (send_type >= N_IPU_INSYS_SEND_TYPE)
+ return -EINVAL;
+
+ dev_dbg(dev, "send_token: %s\n", send_msg_types[send_type]);
+
+ /*
+ * Time to flush cache in case we have some payload. Not all messages
+ * have that
+ */
+ if (cpu_mapped_buf)
+ clflush_cache_range(cpu_mapped_buf, size);
+
+ token = ipu7_syscom_get_token(ctx, stream_handle +
+ IPU_INSYS_INPUT_MSG_QUEUE);
+ if (!token)
+ return -EBUSY;
+
+ token->addr = dma_mapped_buf;
+ token->buf_handle = (unsigned long)cpu_mapped_buf;
+ token->send_type = send_type;
+ token->stream_id = stream_handle;
+ token->flag = IPU_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE;
+
+ ipu7_syscom_put_token(ctx, stream_handle + IPU_INSYS_INPUT_MSG_QUEUE);
+ /* now wakeup FW */
+ ipu_buttress_wakeup_is_uc(isys->adev->isp);
+
+ return 0;
+}
+
+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys,
+ const unsigned int stream_handle, u16 send_type)
+{
+ return ipu7_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
+ send_type);
+}
+
+int ipu7_fw_isys_init(struct ipu7_isys *isys)
+{
+ struct syscom_queue_config *queue_configs;
+ struct ipu7_bus_device *adev = isys->adev;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu7_insys_config *isys_config;
+ struct ipu7_syscom_context *syscom;
+ dma_addr_t isys_config_dma_addr;
+ unsigned int i, num_queues;
+ u32 freq;
+ u8 major;
+ int ret;
+
+ /* Allocate and init syscom context. */
+ syscom = devm_kzalloc(dev, sizeof(struct ipu7_syscom_context),
+ GFP_KERNEL);
+ if (!syscom)
+ return -ENOMEM;
+
+ adev->syscom = syscom;
+ syscom->num_input_queues = IPU_INSYS_MAX_INPUT_QUEUES;
+ syscom->num_output_queues = IPU_INSYS_MAX_OUTPUT_QUEUES;
+ num_queues = syscom->num_input_queues + syscom->num_output_queues;
+ queue_configs = devm_kzalloc(dev, FW_QUEUE_CONFIG_SIZE(num_queues),
+ GFP_KERNEL);
+ if (!queue_configs) {
+ ipu7_fw_isys_release(isys);
+ return -ENOMEM;
+ }
+ syscom->queue_configs = queue_configs;
+ queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].max_capacity =
+ IPU_ISYS_SIZE_RECV_QUEUE;
+ queue_configs[IPU_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes =
+ sizeof(struct ipu7_insys_resp);
+ queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].max_capacity =
+ IPU_ISYS_SIZE_LOG_QUEUE;
+ queue_configs[IPU_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes =
+ sizeof(struct ipu7_insys_resp);
+ queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0;
+ queue_configs[IPU_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0;
+
+ queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].max_capacity =
+ IPU_ISYS_MAX_STREAMS;
+ queue_configs[IPU_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes =
+ sizeof(struct ipu7_insys_send_queue_token);
+
+ for (i = IPU_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) {
+ queue_configs[i].max_capacity = IPU_ISYS_SIZE_SEND_QUEUE;
+ queue_configs[i].token_size_in_bytes =
+ sizeof(struct ipu7_insys_send_queue_token);
+ }
+
+ /* Allocate ISYS subsys config. */
+ isys_config = ipu7_dma_alloc(adev, sizeof(struct ipu7_insys_config),
+ &isys_config_dma_addr, GFP_KERNEL, 0);
+ if (!isys_config) {
+ dev_err(dev, "Failed to allocate isys subsys config.\n");
+ ipu7_fw_isys_release(isys);
+ return -ENOMEM;
+ }
+ isys->subsys_config = isys_config;
+ isys->subsys_config_dma_addr = isys_config_dma_addr;
+ memset(isys_config, 0, sizeof(struct ipu7_insys_config));
+ isys_config->logger_config.use_source_severity = 0;
+ isys_config->logger_config.use_channels_enable_bitmask = 1;
+ isys_config->logger_config.channels_enable_bitmask =
+ LOGGER_CONFIG_CHANNEL_ENABLE_SYSCOM_BITMASK;
+ isys_config->logger_config.hw_printf_buffer_base_addr = 0U;
+ isys_config->logger_config.hw_printf_buffer_size_bytes = 0U;
+ isys_config->wdt_config.wdt_timer1_us = 0;
+ isys_config->wdt_config.wdt_timer2_us = 0;
+ ret = ipu_buttress_get_isys_freq(adev->isp, &freq);
+ if (ret) {
+ dev_err(dev, "Failed to get ISYS frequency.\n");
+ ipu7_fw_isys_release(isys);
+ return ret;
+ }
+
+ ipu7_dma_sync_single(adev, isys_config_dma_addr,
+ sizeof(struct ipu7_insys_config));
+
+ major = is_ipu8(adev->isp->hw_ver) ? 2U : 1U;
+ ret = ipu7_boot_init_boot_config(adev, queue_configs, num_queues,
+ freq, isys_config_dma_addr, major);
+ if (ret)
+ ipu7_fw_isys_release(isys);
+
+ return ret;
+}
+
+void ipu7_fw_isys_release(struct ipu7_isys *isys)
+{
+ struct ipu7_bus_device *adev = isys->adev;
+
+ ipu7_boot_release_boot_config(adev);
+ if (isys->subsys_config) {
+ ipu7_dma_free(adev,
+ sizeof(struct ipu7_insys_config),
+ isys->subsys_config,
+ isys->subsys_config_dma_addr, 0);
+ isys->subsys_config = NULL;
+ isys->subsys_config_dma_addr = 0;
+ }
+}
+
+int ipu7_fw_isys_open(struct ipu7_isys *isys)
+{
+ return ipu7_boot_start_fw(isys->adev);
+}
+
+int ipu7_fw_isys_close(struct ipu7_isys *isys)
+{
+ return ipu7_boot_stop_fw(isys->adev);
+}
+
+struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys)
+{
+ return (struct ipu7_insys_resp *)
+ ipu7_syscom_get_token(isys->adev->syscom,
+ IPU_INSYS_OUTPUT_MSG_QUEUE);
+}
+
+void ipu7_fw_isys_put_resp(struct ipu7_isys *isys)
+{
+ ipu7_syscom_put_token(isys->adev->syscom, IPU_INSYS_OUTPUT_MSG_QUEUE);
+}
+
+void ipu7_fw_isys_dump_stream_cfg(struct device *dev,
+ struct ipu7_insys_stream_cfg *cfg)
+{
+ unsigned int i;
+
+ dev_dbg(dev, "---------------------------\n");
+ dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n");
+
+ dev_dbg(dev, ".port id %d\n", cfg->port_id);
+ dev_dbg(dev, ".vc %d\n", cfg->vc);
+ dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins);
+ dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins);
+ dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map);
+
+ for (i = 0; i < cfg->nof_input_pins; i++) {
+ dev_dbg(dev, ".input_pin[%d]:\n", i);
+ dev_dbg(dev, "\t.dt = 0x%0x\n",
+ cfg->input_pins[i].dt);
+ dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n",
+ cfg->input_pins[i].disable_mipi_unpacking);
+ dev_dbg(dev, "\t.dt_rename_mode = %d\n",
+ cfg->input_pins[i].dt_rename_mode);
+ dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",
+ cfg->input_pins[i].mapped_dt);
+ dev_dbg(dev, "\t.input_res = %d x %d\n",
+ cfg->input_pins[i].input_res.width,
+ cfg->input_pins[i].input_res.height);
+ dev_dbg(dev, "\t.sync_msg_map = 0x%x\n",
+ cfg->input_pins[i].sync_msg_map);
+ }
+
+ for (i = 0; i < cfg->nof_output_pins; i++) {
+ dev_dbg(dev, ".output_pin[%d]:\n", i);
+ dev_dbg(dev, "\t.input_pin_id = %d\n",
+ cfg->output_pins[i].input_pin_id);
+ dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);
+ dev_dbg(dev, "\t.send_irq = %d\n",
+ cfg->output_pins[i].send_irq);
+ dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);
+
+ dev_dbg(dev, "\t.link.buffer_lines = %d\n",
+ cfg->output_pins[i].link.buffer_lines);
+ dev_dbg(dev, "\t.link.foreign_key = %d\n",
+ cfg->output_pins[i].link.foreign_key);
+ dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n",
+ cfg->output_pins[i].link.granularity_pointer_update);
+ dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n",
+ cfg->output_pins[i].link.msg_link_streaming_mode);
+ dev_dbg(dev, "\t.link.pbk_id = %d\n",
+ cfg->output_pins[i].link.pbk_id);
+ dev_dbg(dev, "\t.link.pbk_slot_id = %d\n",
+ cfg->output_pins[i].link.pbk_slot_id);
+ dev_dbg(dev, "\t.link.dest = %d\n",
+ cfg->output_pins[i].link.dest);
+ dev_dbg(dev, "\t.link.use_sw_managed = %d\n",
+ cfg->output_pins[i].link.use_sw_managed);
+ dev_dbg(dev, "\t.link.is_snoop = %d\n",
+ cfg->output_pins[i].link.is_snoop);
+
+ dev_dbg(dev, "\t.crop.line_top = %d\n",
+ cfg->output_pins[i].crop.line_top);
+ dev_dbg(dev, "\t.crop.line_bottom = %d\n",
+ cfg->output_pins[i].crop.line_bottom);
+
+ dev_dbg(dev, "\t.dpcm_enable = %d\n",
+ cfg->output_pins[i].dpcm.enable);
+ dev_dbg(dev, "\t.dpcm.type = %d\n",
+ cfg->output_pins[i].dpcm.type);
+ dev_dbg(dev, "\t.dpcm.predictor = %d\n",
+ cfg->output_pins[i].dpcm.predictor);
+ }
+ dev_dbg(dev, "---------------------------\n");
+}
+
+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev,
+ struct ipu7_insys_buffset *buf,
+ unsigned int outputs)
+{
+ unsigned int i;
+
+ dev_dbg(dev, "--------------------------\n");
+ dev_dbg(dev, "IPU_ISYS_BUFF_SET\n");
+ dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map);
+ dev_dbg(dev, ".frame_id = %d\n", buf->frame_id);
+ dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame);
+
+ for (i = 0; i < outputs; i++) {
+ dev_dbg(dev, ".output_pin[%d]:\n", i);
+ dev_dbg(dev, "\t.user_token = %llx\n",
+ buf->output_pins[i].user_token);
+ dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr);
+ }
+ dev_dbg(dev, "---------------------------\n");
+}
diff --git a/drivers/staging/media/ipu7/ipu7-fw-isys.h b/drivers/staging/media/ipu7/ipu7-fw-isys.h
new file mode 100644
index 000000000000..b556feda6b08
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-fw-isys.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_FW_ISYS_H
+#define IPU7_FW_ISYS_H
+
+#include <linux/types.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+struct device;
+struct ipu7_insys_buffset;
+struct ipu7_insys_stream_cfg;
+struct ipu7_isys;
+
+/* From here on type defines not coming from the ISYSAPI interface */
+
+int ipu7_fw_isys_init(struct ipu7_isys *isys);
+void ipu7_fw_isys_release(struct ipu7_isys *isys);
+int ipu7_fw_isys_open(struct ipu7_isys *isys);
+int ipu7_fw_isys_close(struct ipu7_isys *isys);
+
+void ipu7_fw_isys_dump_stream_cfg(struct device *dev,
+ struct ipu7_insys_stream_cfg *cfg);
+void ipu7_fw_isys_dump_frame_buff_set(struct device *dev,
+ struct ipu7_insys_buffset *buf,
+ unsigned int outputs);
+int ipu7_fw_isys_simple_cmd(struct ipu7_isys *isys,
+ const unsigned int stream_handle, u16 send_type);
+int ipu7_fw_isys_complex_cmd(struct ipu7_isys *isys,
+ const unsigned int stream_handle,
+ void *cpu_mapped_buf,
+ dma_addr_t dma_mapped_buf,
+ size_t size, u16 send_type);
+struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu7_isys *isys);
+void ipu7_fw_isys_put_resp(struct ipu7_isys *isys);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c
new file mode 100644
index 000000000000..b8c5db7ae300
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.c
@@ -0,0 +1,1034 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-device.h>
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-platform-regs.h"
+#include "ipu7-isys-csi-phy.h"
+
+#define PORT_A 0U
+#define PORT_B 1U
+#define PORT_C 2U
+#define PORT_D 3U
+
+#define N_DATA_IDS 8U
+static DECLARE_BITMAP(data_ids, N_DATA_IDS);
+
+struct ddlcal_counter_ref_s {
+ u16 min_mbps;
+ u16 max_mbps;
+
+ u16 ddlcal_counter_ref;
+};
+
+struct ddlcal_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 oa_lanex_hsrx_cdphy_sel_fast;
+ u16 ddlcal_max_phase;
+ u16 phase_bound;
+ u16 ddlcal_dll_fbk;
+ u16 ddlcal_ddl_coarse_bank;
+ u16 fjump_deskew;
+ u16 min_eye_opening_deskew;
+};
+
+struct i_thssettle_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 i_thssettle;
+};
+
+ /* lane2 for 4l3t, lane1 for 2l2t */
+struct oa_lane_clk_div_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 oa_lane_hsrx_hs_clk_div;
+};
+
+struct cdr_fbk_cap_prog_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 val;
+};
+
+static const struct ddlcal_counter_ref_s table0[] = {
+ { 1500, 1999, 118 },
+ { 2000, 2499, 157 },
+ { 2500, 3499, 196 },
+ { 3500, 4499, 274 },
+ { 4500, 4500, 352 },
+ { }
+};
+
+static const struct ddlcal_params table1[] = {
+ { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 },
+ { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 },
+ { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 },
+ { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 },
+ { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 },
+ { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 },
+ { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 },
+ { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 },
+ { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 },
+ { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 },
+ { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 },
+ { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 },
+ { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 },
+ { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 },
+ { }
+};
+
+static const struct i_thssettle_params table2[] = {
+ { 80, 124, 24 },
+ { 125, 249, 20 },
+ { 250, 499, 16 },
+ { 500, 749, 14 },
+ { 750, 1499, 13 },
+ { 1500, 4500, 12 },
+ { }
+};
+
+static const struct oa_lane_clk_div_params table6[] = {
+ { 80, 159, 0x1 },
+ { 160, 319, 0x2 },
+ { 320, 639, 0x3 },
+ { 640, 1279, 0x4 },
+ { 1280, 2560, 0x5 },
+ { 2561, 4500, 0x6 },
+ { }
+};
+
+static const struct cdr_fbk_cap_prog_params table7[] = {
+ { 80, 919, 0 },
+ { 920, 1029, 1 },
+ { 1030, 1169, 2 },
+ { 1170, 1349, 3 },
+ { 1350, 1589, 4 },
+ { 1590, 1949, 5 },
+ { 1950, 2499, 6 },
+ { }
+};
+
+static void dwc_phy_write(struct ipu7_isys *isys, u32 id, u32 addr, u16 data)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id);
+
+ dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%zx = data 0x%04x",
+ base + addr - isys_base, data);
+ writew(data, base + addr);
+}
+
+static u16 dwc_phy_read(struct ipu7_isys *isys, u32 id, u32 addr)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IS_IO_CDPHY_BASE(id);
+ u16 data;
+
+ data = readw(base + addr);
+ dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%zx = data 0x%04x",
+ base + addr - isys_base, data);
+
+ return data;
+}
+
+static void dwc_csi_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id);
+ struct device *dev = &isys->adev->auxdev.dev;
+
+ dev_dbg(dev, "csi write: reg 0x%zx = data 0x%08x",
+ base + addr - isys_base, data);
+ writel(data, base + addr);
+ dev_dbg(dev, "csi read: reg 0x%zx = data 0x%08x",
+ base + addr - isys_base, readl(base + addr));
+}
+
+static void gpreg_write(struct ipu7_isys *isys, u32 id, u32 addr, u32 data)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ u32 gpreg = isys->pdata->ipdata->csi2.gpreg;
+ void __iomem *base = isys_base + gpreg + 0x1000 * id;
+ struct device *dev = &isys->adev->auxdev.dev;
+
+ dev_dbg(dev, "gpreg write: reg 0x%zx = data 0x%08x",
+ base + addr - isys_base, data);
+ writel(data, base + addr);
+ dev_dbg(dev, "gpreg read: reg 0x%zx = data 0x%08x",
+ base + addr - isys_base, readl(base + addr));
+}
+
+static u32 dwc_csi_read(struct ipu7_isys *isys, u32 id, u32 addr)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IS_IO_CSI2_HOST_BASE(id);
+ u32 data;
+
+ data = readl(base + addr);
+ dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%zx = data 0x%x",
+ base + addr - isys_base, data);
+
+ return data;
+}
+
+static void dwc_phy_write_mask(struct ipu7_isys *isys, u32 id, u32 addr,
+ u16 val, u8 lo, u8 hi)
+{
+ u32 temp, mask;
+
+ WARN_ON(lo > hi);
+ WARN_ON(hi > 15);
+
+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi));
+ temp = dwc_phy_read(isys, id, addr);
+ temp &= ~mask;
+ temp |= (val << lo) & mask;
+ dwc_phy_write(isys, id, addr, temp);
+}
+
+static void dwc_csi_write_mask(struct ipu7_isys *isys, u32 id, u32 addr,
+ u32 val, u8 hi, u8 lo)
+{
+ u32 temp, mask;
+
+ WARN_ON(lo > hi);
+
+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi));
+ temp = dwc_csi_read(isys, id, addr);
+ temp &= ~mask;
+ temp |= (val << lo) & mask;
+ dwc_csi_write(isys, id, addr, temp);
+}
+
+static void ipu7_isys_csi_ctrl_cfg(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_isys *isys = csi2->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ u32 id, lanes, phy_mode;
+ u32 val;
+
+ id = csi2->port;
+ lanes = csi2->nlanes;
+ phy_mode = csi2->phy_mode;
+ dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u",
+ id, lanes, phy_mode);
+
+ val = dwc_csi_read(isys, id, VERSION);
+ dev_dbg(dev, "csi-%d controller version = 0x%x", id, val);
+
+ /* num of active data lanes */
+ dwc_csi_write(isys, id, N_LANES, lanes - 1);
+ dwc_csi_write(isys, id, CDPHY_MODE, phy_mode);
+ dwc_csi_write(isys, id, VC_EXTENSION, 0);
+
+ /* only mask PHY_FATAL and PKT_FATAL interrupts */
+ dwc_csi_write(isys, id, INT_MSK_PHY_FATAL, 0xff);
+ dwc_csi_write(isys, id, INT_MSK_PKT_FATAL, 0x3);
+ dwc_csi_write(isys, id, INT_MSK_PHY, 0x0);
+ dwc_csi_write(isys, id, INT_MSK_LINE, 0x0);
+ dwc_csi_write(isys, id, INT_MSK_BNDRY_FRAME_FATAL, 0x0);
+ dwc_csi_write(isys, id, INT_MSK_SEQ_FRAME_FATAL, 0x0);
+ dwc_csi_write(isys, id, INT_MSK_CRC_FRAME_FATAL, 0x0);
+ dwc_csi_write(isys, id, INT_MSK_PLD_CRC_FATAL, 0x0);
+ dwc_csi_write(isys, id, INT_MSK_DATA_ID, 0x0);
+ dwc_csi_write(isys, id, INT_MSK_ECC_CORRECTED, 0x0);
+}
+
+static void ipu7_isys_csi_phy_reset(struct ipu7_isys *isys, u32 id)
+{
+ dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 0);
+ dwc_csi_write(isys, id, DPHY_RSTZ, 0);
+ dwc_csi_write(isys, id, CSI2_RESETN, 0);
+ gpreg_write(isys, id, PHY_RESET, 0);
+ gpreg_write(isys, id, PHY_SHUTDOWN, 0);
+}
+
+/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */
+static int __dids_config(struct ipu7_isys_csi2 *csi2, u32 id, u8 vc, u8 dt)
+{
+ struct ipu7_isys *isys = csi2->isys;
+ u32 reg, n;
+ u8 lo, hi;
+ int ret;
+
+ dev_dbg(&isys->adev->auxdev.dev, "config CSI-%u with vc:%u dt:0x%02x\n",
+ id, vc, dt);
+
+ dwc_csi_write(isys, id, VC_EXTENSION, 0x0);
+ n = find_first_zero_bit(data_ids, N_DATA_IDS);
+ if (n == N_DATA_IDS)
+ return -ENOSPC;
+
+ ret = test_and_set_bit(n, data_ids);
+ if (ret)
+ return -EBUSY;
+
+ reg = n < 4 ? DATA_IDS_VC_1 : DATA_IDS_VC_2;
+ lo = (n % 4) * 8;
+ hi = lo + 4;
+ dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo);
+
+ reg = n < 4 ? DATA_IDS_1 : DATA_IDS_2;
+ lo = (n % 4) * 8;
+ hi = lo + 5;
+ dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo);
+
+ return 0;
+}
+
+static int ipu7_isys_csi_ctrl_dids_config(struct ipu7_isys_csi2 *csi2, u32 id)
+{
+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct v4l2_mbus_frame_desc desc;
+ struct v4l2_subdev *ext_sd;
+ struct media_pad *pad;
+ unsigned int i;
+ int ret;
+
+ pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+ if (IS_ERR(pad)) {
+ dev_warn(dev, "can't get remote source pad of %s (%ld)\n",
+ csi2->asd.sd.name, PTR_ERR(pad));
+ return PTR_ERR(pad);
+ }
+
+ ext_sd = media_entity_to_v4l2_subdev(pad->entity);
+ if (WARN(!ext_sd, "Failed to get subdev for entity %s\n",
+ pad->entity->name))
+ return -ENODEV;
+
+ ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc);
+ if (ret)
+ return ret;
+
+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ dev_warn(dev, "Unsupported frame descriptor type\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < desc.num_entries; i++) {
+ desc_entry = &desc.entry[i];
+ if (desc_entry->bus.csi2.vc < IPU7_NR_OF_CSI2_VC) {
+ ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc,
+ desc_entry->bus.csi2.dt);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#define CDPHY_TIMEOUT 5000000U
+static int ipu7_isys_phy_ready(struct ipu7_isys *isys, u32 id)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg;
+ void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id;
+ struct device *dev = &isys->adev->auxdev.dev;
+ unsigned int i;
+ u32 phy_ready;
+ u32 reg, rext;
+ int ret;
+
+ dev_dbg(dev, "waiting phy ready...\n");
+ ret = readl_poll_timeout(gpreg + PHY_READY, phy_ready,
+ phy_ready & BIT(0) && phy_ready != ~0U,
+ 100, CDPHY_TIMEOUT);
+ dev_dbg(dev, "phy %u ready = 0x%08x\n", id, readl(gpreg + PHY_READY));
+ dev_dbg(dev, "csi %u PHY_RX = 0x%08x\n", id,
+ dwc_csi_read(isys, id, PHY_RX));
+ dev_dbg(dev, "csi %u PHY_STOPSTATE = 0x%08x\n", id,
+ dwc_csi_read(isys, id, PHY_STOPSTATE));
+ dev_dbg(dev, "csi %u PHY_CAL = 0x%08x\n", id,
+ dwc_csi_read(isys, id, PHY_CAL));
+ for (i = 0; i < 4U; i++) {
+ reg = CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U);
+ dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n",
+ id, i, dwc_phy_read(isys, id, reg));
+ }
+ dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id, PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5));
+ dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id, PPI_R_TERMCAL_DEBUG_0));
+ dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_RB));
+ dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id, PPI_R_HSDCOCAL_DEBUG_RB));
+ dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id,
+ dwc_phy_read(isys, id, PPI_R_LPDCOCAL_DEBUG_VT));
+
+ if (!ret) {
+ if (id) {
+ dev_dbg(dev, "ignore phy %u rext\n", id);
+ return 0;
+ }
+
+ rext = dwc_phy_read(isys, id,
+ CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) & 0xfU;
+ dev_dbg(dev, "phy %u rext value = %u\n", id, rext);
+ isys->phy_rext_cal = (rext ? rext : 5);
+
+ return 0;
+ }
+
+ dev_err(dev, "wait phy ready timeout!\n");
+
+ return ret;
+}
+
+static int lookup_table1(u64 mbps)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(table1); i++) {
+ if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps)
+ return i;
+ }
+
+ return -ENXIO;
+}
+
+static const u16 deskew_fine_mem[] = {
+ 0x0404, 0x040c, 0x0414, 0x041c,
+ 0x0423, 0x0429, 0x0430, 0x043a,
+ 0x0445, 0x044a, 0x0450, 0x045a,
+ 0x0465, 0x0469, 0x0472, 0x047a,
+ 0x0485, 0x0489, 0x0490, 0x049a,
+ 0x04a4, 0x04ac, 0x04b4, 0x04bc,
+ 0x04c4, 0x04cc, 0x04d4, 0x04dc,
+ 0x04e4, 0x04ec, 0x04f4, 0x04fc,
+ 0x0504, 0x050c, 0x0514, 0x051c,
+ 0x0523, 0x0529, 0x0530, 0x053a,
+ 0x0545, 0x054a, 0x0550, 0x055a,
+ 0x0565, 0x0569, 0x0572, 0x057a,
+ 0x0585, 0x0589, 0x0590, 0x059a,
+ 0x05a4, 0x05ac, 0x05b4, 0x05bc,
+ 0x05c4, 0x05cc, 0x05d4, 0x05dc,
+ 0x05e4, 0x05ec, 0x05f4, 0x05fc,
+ 0x0604, 0x060c, 0x0614, 0x061c,
+ 0x0623, 0x0629, 0x0632, 0x063a,
+ 0x0645, 0x064a, 0x0650, 0x065a,
+ 0x0665, 0x0669, 0x0672, 0x067a,
+ 0x0685, 0x0689, 0x0690, 0x069a,
+ 0x06a4, 0x06ac, 0x06b4, 0x06bc,
+ 0x06c4, 0x06cc, 0x06d4, 0x06dc,
+ 0x06e4, 0x06ec, 0x06f4, 0x06fc,
+ 0x0704, 0x070c, 0x0714, 0x071c,
+ 0x0723, 0x072a, 0x0730, 0x073a,
+ 0x0745, 0x074a, 0x0750, 0x075a,
+ 0x0765, 0x0769, 0x0772, 0x077a,
+ 0x0785, 0x0789, 0x0790, 0x079a,
+ 0x07a4, 0x07ac, 0x07b4, 0x07bc,
+ 0x07c4, 0x07cc, 0x07d4, 0x07dc,
+ 0x07e4, 0x07ec, 0x07f4, 0x07fc,
+};
+
+static void ipu7_isys_dphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
+ bool aggregation, u64 mbps)
+{
+ u16 hsrxval0 = 0;
+ u16 hsrxval1 = 0;
+ u16 hsrxval2 = 0;
+ int index;
+ u16 reg;
+ u16 val;
+ u32 i;
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, 0, 0, 9);
+ if (mbps > 1500)
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7,
+ 40, 0, 7);
+ else
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7,
+ 104, 0, 7);
+
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 80, 0, 7);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_0, 191, 0, 9);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 34, 7, 12);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1, 38, 8, 15);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 4, 12, 15);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 2, 10, 11);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 8, 8);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 38, 0, 7);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_2, 1, 9, 9);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_4, 10, 0, 9);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_6, 20, 0, 9);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_7, 19, 0, 6);
+
+ for (i = 0; i < ARRAY_SIZE(table0); i++) {
+ if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) {
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_3,
+ table0[i].ddlcal_counter_ref,
+ 0, 9);
+ break;
+ }
+ }
+
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_1,
+ table1[index].phase_bound, 0, 7);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5,
+ table1[index].ddlcal_dll_fbk, 4, 9);
+ dwc_phy_write_mask(isys, id, PPI_RW_DDLCAL_CFG_5,
+ table1[index].ddlcal_ddl_coarse_bank, 0, 3);
+
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8;
+ val = table1[index].oa_lanex_hsrx_cdphy_sel_fast;
+ for (i = 0; i < lanes + 1; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+ 12, 12);
+ }
+
+ reg = CORE_DIG_DLANE_0_RW_LP_0;
+ for (i = 0; i < lanes; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2,
+ 0, 0, 0);
+ if (!is_ipu7(isys->adev->isp->hw_ver) ||
+ id == PORT_B || id == PORT_C) {
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2,
+ 1, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2,
+ 0, 0, 0);
+ } else {
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2,
+ 0, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2,
+ 1, 0, 0);
+ }
+
+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2,
+ 0, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2,
+ 0, 0, 0);
+ }
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 0, 2);
+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_6, 1, 3, 5);
+
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12;
+ val = (mbps > 1500) ? 0 : 1;
+ for (i = 0; i < lanes + 1; i++) {
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3);
+ }
+
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13;
+ val = (mbps > 1500) ? 0 : 1;
+ for (i = 0; i < lanes + 1; i++) {
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3);
+ }
+
+ if (!is_ipu7(isys->adev->isp->hw_ver) || id == PORT_B || id == PORT_C)
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9;
+ else
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9;
+
+ for (i = 0; i < ARRAY_SIZE(table6); i++) {
+ if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) {
+ dwc_phy_write_mask(isys, id, reg,
+ table6[i].oa_lane_hsrx_hs_clk_div,
+ 5, 7);
+ break;
+ }
+ }
+
+ if (aggregation) {
+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_0, 1,
+ 1, 1);
+
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15;
+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4);
+
+ val = (id == PORT_A) ? 3 : 0;
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15;
+ dwc_phy_write_mask(isys, id, reg, val, 3, 4);
+
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15;
+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4);
+ }
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_0, 28, 0, 7);
+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_HS_RX_7, 6, 0, 7);
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_0;
+ for (i = 0; i < ARRAY_SIZE(table2); i++) {
+ if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) {
+ u8 j;
+
+ for (j = 0; j < lanes; j++)
+ dwc_phy_write_mask(isys, id, reg + (j * 0x400),
+ table2[i].i_thssettle,
+ 8, 15);
+ break;
+ }
+ }
+
+ /* deskew */
+ for (i = 0; i < lanes; i++) {
+ reg = CORE_DIG_DLANE_0_RW_CFG_1;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+ ((mbps > 1500) ? 0x1 : 0x2), 2, 3);
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_2;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+ ((mbps > 2500) ? 0 : 1), 15, 15);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12);
+
+ reg = CORE_DIG_DLANE_0_RW_LP_0;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15);
+
+ reg = CORE_DIG_DLANE_0_RW_LP_2;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0);
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_1;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7);
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_3;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2);
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ val = table1[index].fjump_deskew;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+ 3, 8);
+ }
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_4;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15);
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_5;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15);
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_6;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7);
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ val = table1[index].min_eye_opening_deskew;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+ 8, 15);
+ }
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_7;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15);
+
+ reg = CORE_DIG_DLANE_0_RW_HS_RX_9;
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ val = table1[index].ddlcal_max_phase;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+ val, 0, 7);
+ }
+ }
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_0, 1, 12, 15);
+ dwc_phy_write_mask(isys, id, CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0);
+
+ for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++)
+ dwc_phy_write_mask(isys, id, CORE_DIG_COMMON_RW_DESKEW_FINE_MEM,
+ deskew_fine_mem[i], 0, 15);
+
+ if (mbps > 1500) {
+ hsrxval0 = 4;
+ hsrxval2 = 3;
+ }
+
+ if (mbps > 2500)
+ hsrxval1 = 2;
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9,
+ hsrxval0, 0, 2);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9,
+ hsrxval0, 0, 2);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9,
+ hsrxval0, 0, 2);
+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9,
+ hsrxval0, 0, 2);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9,
+ hsrxval0, 0, 2);
+ }
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9,
+ hsrxval1, 3, 4);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9,
+ hsrxval1, 3, 4);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9,
+ hsrxval1, 3, 4);
+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9,
+ hsrxval1, 3, 4);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9,
+ hsrxval1, 3, 4);
+ }
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15,
+ hsrxval2, 0, 2);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15,
+ hsrxval2, 0, 2);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15,
+ hsrxval2, 0, 2);
+ if (lanes == 4 && is_ipu7(isys->adev->isp->hw_ver)) {
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15,
+ hsrxval2, 0, 2);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15,
+ hsrxval2, 0, 2);
+ }
+
+ /* force and override rext */
+ if (isys->phy_rext_cal && id) {
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8,
+ isys->phy_rext_cal, 0, 3);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
+ 1, 11, 11);
+ }
+}
+
+static void ipu7_isys_cphy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
+ bool aggregation, u64 mbps)
+{
+ u8 trios = 2;
+ u16 coarse_target;
+ u16 deass_thresh;
+ u16 delay_thresh;
+ u16 reset_thresh;
+ u16 cap_prog = 6U;
+ u16 reg;
+ u16 val;
+ u32 i;
+ u64 r64;
+ u32 r;
+
+ if (is_ipu7p5(isys->adev->isp->hw_ver))
+ val = 0x15;
+ else
+ val = 0x155;
+
+ if (is_ipu7(isys->adev->isp->hw_ver))
+ trios = 3;
+
+ dwc_phy_write_mask(isys, id, CORE_DIG_RW_COMMON_7, val, 0, 9);
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_7, 104, 0, 7);
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_8, 16, 0, 7);
+
+ reg = CORE_DIG_CLANE_0_RW_LP_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);
+
+ val = (mbps > 900U) ? 1U : 0U;
+ for (i = 0; i < trios; i++) {
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_1;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15);
+
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_5;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15);
+
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_6;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15);
+ }
+
+ /*
+ * Below 900Msps, always use the same value.
+ * The formula is suitable for data rate 80-3500Msps.
+ * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5
+ */
+ if (mbps >= 80U)
+ coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1;
+ else
+ coarse_target = 56;
+
+ for (i = 0; i < trios; i++) {
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400;
+ dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15);
+ }
+
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0);
+
+ if (!is_ipu7p5(isys->adev->isp->hw_ver) && lanes == 4) {
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2,
+ 1, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2,
+ 0, 0, 0);
+ }
+
+ for (i = 0; i < trios; i++) {
+ reg = CORE_DIG_RW_TRIO0_0 + i * 0x400;
+ dwc_phy_write_mask(isys, id, reg, 1, 6, 8);
+ dwc_phy_write_mask(isys, id, reg, 1, 3, 5);
+ dwc_phy_write_mask(isys, id, reg, 2, 0, 2);
+ }
+
+ deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1;
+ if (r64 != 0)
+ deass_thresh++;
+
+ reg = CORE_DIG_RW_TRIO0_2;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+ deass_thresh, 0, 7);
+
+ delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u;
+
+ if (delay_thresh < 1)
+ delay_thresh = 1;
+
+ reg = CORE_DIG_RW_TRIO0_1;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+ delay_thresh, 0, 15);
+
+ reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r);
+ if (!r)
+ reset_thresh--;
+
+ if (reset_thresh < 1)
+ reset_thresh = 1;
+
+ reg = CORE_DIG_RW_TRIO0_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+ reset_thresh, 9, 11);
+
+ reg = CORE_DIG_CLANE_0_RW_LP_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 1, 12, 15);
+
+ reg = CORE_DIG_CLANE_0_RW_LP_2;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0);
+
+ reg = CORE_DIG_CLANE_0_RW_HS_RX_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6);
+
+ for (i = 0; i < ARRAY_SIZE(table7); i++) {
+ if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) {
+ cap_prog = table7[i].val;
+ break;
+ }
+ }
+
+ for (i = 0; i < (lanes + 1); i++) {
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i;
+ dwc_phy_write_mask(isys, id, reg, 4U, 0, 2);
+ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4);
+
+ reg = CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i;
+ dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12);
+ }
+}
+
+static int ipu7_isys_phy_config(struct ipu7_isys *isys, u8 id, u8 lanes,
+ bool aggregation)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ u32 phy_mode;
+ s64 link_freq;
+ u64 mbps;
+
+ if (aggregation)
+ link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[0]);
+ else
+ link_freq = ipu7_isys_csi2_get_link_freq(&isys->csi2[id]);
+
+ if (link_freq < 0) {
+ dev_err(dev, "get link freq failed (%lld)\n", link_freq);
+ return link_freq;
+ }
+
+ mbps = div_u64(link_freq, 500000);
+ dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n",
+ id, lanes, aggregation, mbps);
+
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_10, 48, 0, 7);
+ dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2,
+ 1, 12, 13);
+ dwc_phy_write_mask(isys, id, CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0,
+ 63, 2, 7);
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_STARTUP_1_1,
+ 563, 0, 11);
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 5, 0, 7);
+ /* bypass the RCAL state (bit6) */
+ if (aggregation && id != PORT_A)
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_2, 0x45,
+ 0, 7);
+
+ dwc_phy_write_mask(isys, id, PPI_STARTUP_RW_COMMON_DPHY_6, 39, 0, 7);
+ dwc_phy_write_mask(isys, id, PPI_CALIBCTRL_RW_COMMON_BG_0, 500, 0, 8);
+ dwc_phy_write_mask(isys, id, PPI_RW_TERMCAL_CFG_0, 38, 0, 6);
+ dwc_phy_write_mask(isys, id, PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF, 800, 0, 10);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_NREF_RANGE, 27, 0, 4);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 47, 0, 8);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_TWAIT_CONFIG, 127, 9, 15);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1);
+ dwc_phy_write_mask(isys, id, PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1);
+ dwc_phy_write_mask(isys, id, PPI_RW_COMMON_CFG, 3, 0, 1);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0,
+ 0, 10, 10);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1,
+ 1, 10, 10);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1,
+ 0, 15, 15);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3,
+ 3, 8, 9);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0,
+ 0, 15, 15);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6,
+ 7, 12, 14);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
+ 0, 8, 10);
+ dwc_phy_write_mask(isys, id, CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5,
+ 0, 8, 8);
+
+ if (aggregation)
+ phy_mode = isys->csi2[0].phy_mode;
+ else
+ phy_mode = isys->csi2[id].phy_mode;
+
+ if (phy_mode == PHY_MODE_DPHY) {
+ ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps);
+ } else if (phy_mode == PHY_MODE_CPHY) {
+ ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps);
+ } else {
+ dev_err(dev, "unsupported phy mode %d!\n",
+ isys->csi2[id].phy_mode);
+ }
+
+ return 0;
+}
+
+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_isys *isys = csi2->isys;
+ u32 lanes = csi2->nlanes;
+ bool aggregation = false;
+ u32 id = csi2->port;
+ int ret;
+
+ /* lanes remapping for aggregation (port AB) mode */
+ if (!is_ipu7(isys->adev->isp->hw_ver) && lanes > 2 && id == PORT_A) {
+ aggregation = true;
+ lanes = 2;
+ }
+
+ ipu7_isys_csi_phy_reset(isys, id);
+ gpreg_write(isys, id, PHY_CLK_LANE_CONTROL, 0x1);
+ gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0x2);
+ gpreg_write(isys, id, PHY_LANE_CONTROL_EN, (1U << lanes) - 1U);
+ gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0xf);
+ gpreg_write(isys, id, PHY_MODE, csi2->phy_mode);
+
+ /* config PORT_B if aggregation mode */
+ if (aggregation) {
+ ipu7_isys_csi_phy_reset(isys, PORT_B);
+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_CONTROL, 0x0);
+ gpreg_write(isys, PORT_B, PHY_LANE_CONTROL_EN, 0x3);
+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0x2);
+ gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0xf);
+ gpreg_write(isys, PORT_B, PHY_MODE, csi2->phy_mode);
+ }
+
+ ipu7_isys_csi_ctrl_cfg(csi2);
+ ipu7_isys_csi_ctrl_dids_config(csi2, id);
+
+ ret = ipu7_isys_phy_config(isys, id, lanes, aggregation);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, id, PHY_RESET, 1);
+ gpreg_write(isys, id, PHY_SHUTDOWN, 1);
+ dwc_csi_write(isys, id, DPHY_RSTZ, 1);
+ dwc_csi_write(isys, id, PHY_SHUTDOWNZ, 1);
+ dwc_csi_write(isys, id, CSI2_RESETN, 1);
+
+ ret = ipu7_isys_phy_ready(isys, id);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, id, PHY_LANE_FORCE_CONTROL, 0);
+ gpreg_write(isys, id, PHY_CLK_LANE_FORCE_CONTROL, 0);
+
+ /* config PORT_B if aggregation mode */
+ if (aggregation) {
+ ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, PORT_B, PHY_RESET, 1);
+ gpreg_write(isys, PORT_B, PHY_SHUTDOWN, 1);
+ dwc_csi_write(isys, PORT_B, DPHY_RSTZ, 1);
+ dwc_csi_write(isys, PORT_B, PHY_SHUTDOWNZ, 1);
+ dwc_csi_write(isys, PORT_B, CSI2_RESETN, 1);
+ ret = ipu7_isys_phy_ready(isys, PORT_B);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, PORT_B, PHY_LANE_FORCE_CONTROL, 0);
+ gpreg_write(isys, PORT_B, PHY_CLK_LANE_FORCE_CONTROL, 0);
+ }
+
+ return 0;
+}
+
+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_isys *isys = csi2->isys;
+
+ ipu7_isys_csi_phy_reset(isys, csi2->port);
+ if (!is_ipu7(isys->adev->isp->hw_ver) &&
+ csi2->nlanes > 2U && csi2->port == PORT_A)
+ ipu7_isys_csi_phy_reset(isys, PORT_B);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h
new file mode 100644
index 000000000000..dfdcb61540c4
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-csi-phy.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_CSI_PHY_H
+#define IPU7_ISYS_CSI_PHY_H
+
+struct ipu7_isys;
+
+#define PHY_MODE_DPHY 0U
+#define PHY_MODE_CPHY 1U
+
+int ipu7_isys_csi_phy_powerup(struct ipu7_isys_csi2 *csi2);
+void ipu7_isys_csi_phy_powerdown(struct ipu7_isys_csi2 *csi2);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h b/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h
new file mode 100644
index 000000000000..aad52c44a005
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-csi2-regs.h
@@ -0,0 +1,1197 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_CSI2_REG_H
+#define IPU7_ISYS_CSI2_REG_H
+
+/* IS main regs base */
+#define IS_MAIN_BASE 0x240000
+#define IS_MAIN_S2B_BASE (IS_MAIN_BASE + 0x22000)
+#define IS_MAIN_B2O_BASE (IS_MAIN_BASE + 0x26000)
+#define IS_MAIN_ISD_M0_BASE (IS_MAIN_BASE + 0x2b000)
+#define IS_MAIN_ISD_M1_BASE (IS_MAIN_BASE + 0x2b100)
+#define IS_MAIN_ISD_INT_BASE (IS_MAIN_BASE + 0x2b200)
+#define IS_MAIN_GDA_BASE (IS_MAIN_BASE + 0x32000)
+#define IS_MAIN_GPREGS_MAIN_BASE (IS_MAIN_BASE + 0x32500)
+#define IS_MAIN_IRQ_CTRL_BASE (IS_MAIN_BASE + 0x32700)
+#define IS_MAIN_PWM_CTRL_BASE (IS_MAIN_BASE + 0x32b00)
+
+#define S2B_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_S2B_BASE + 0x1c)
+#define S2B_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_S2B_BASE + 0x20)
+#define S2B_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_S2B_BASE + 0x24)
+#define S2B_IID_IRQ_CTL_STATUS(iid) (IS_MAIN_S2B_BASE + 0x94 + \
+ 0x100 * (iid))
+
+#define B2O_IRQ_COMMON_0_CTL_STATUS (IS_MAIN_B2O_BASE + 0x30)
+#define B2O_IRQ_COMMON_0_CTL_CLEAR (IS_MAIN_B2O_BASE + 0x34)
+#define B2O_IRQ_COMMON_0_CTL_ENABLE (IS_MAIN_B2O_BASE + 0x38)
+#define B2O_IID_IRQ_CTL_STATUS(oid) (IS_MAIN_B2O_BASE + 0x3dc + \
+ 0x200 * (oid))
+
+#define ISD_M0_IRQ_CTL_STATUS (IS_MAIN_ISD_M0_BASE + 0x1c)
+#define ISD_M0_IRQ_CTL_CLEAR (IS_MAIN_ISD_M0_BASE + 0x20)
+#define ISD_M0_IRQ_CTL_ENABLE (IS_MAIN_ISD_M0_BASE + 0x24)
+
+#define ISD_M1_IRQ_CTL_STATUS (IS_MAIN_ISD_M1_BASE + 0x1c)
+#define ISD_M1_IRQ_CTL_CLEAR (IS_MAIN_ISD_M1_BASE + 0x20)
+#define ISD_M1_IRQ_CTL_ENABLE (IS_MAIN_ISD_M1_BASE + 0x24)
+
+#define ISD_INT_IRQ_CTL_STATUS (IS_MAIN_ISD_INT_BASE + 0x1c)
+#define ISD_INT_IRQ_CTL_CLEAR (IS_MAIN_ISD_INT_BASE + 0x20)
+#define ISD_INT_IRQ_CTL_ENABLE (IS_MAIN_ISD_INT_BASE + 0x24)
+
+#define GDA_IRQ_CTL_STATUS (IS_MAIN_GDA_BASE + 0x1c)
+#define GDA_IRQ_CTL_CLEAR (IS_MAIN_GDA_BASE + 0x20)
+#define GDA_IRQ_CTL_ENABLE (IS_MAIN_GDA_BASE + 0x24)
+
+#define IS_MAIN_IRQ_CTL_EDGE IS_MAIN_IRQ_CTRL_BASE
+#define IS_MAIN_IRQ_CTL_MASK (IS_MAIN_IRQ_CTRL_BASE + 0x4)
+#define IS_MAIN_IRQ_CTL_STATUS (IS_MAIN_IRQ_CTRL_BASE + 0x8)
+#define IS_MAIN_IRQ_CTL_CLEAR (IS_MAIN_IRQ_CTRL_BASE + 0xc)
+#define IS_MAIN_IRQ_CTL_ENABLE (IS_MAIN_IRQ_CTRL_BASE + 0x10)
+#define IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IS_MAIN_IRQ_CTRL_BASE + 0x14)
+
+/* IS IO regs base */
+#define IS_PHY_NUM 4U
+#define IS_IO_BASE 0x280000
+
+/* dwc csi cdphy registers */
+#define IS_IO_CDPHY_BASE(i) (IS_IO_BASE + 0x10000 * (i))
+#define PPI_STARTUP_RW_COMMON_DPHY_0 0x1800
+#define PPI_STARTUP_RW_COMMON_DPHY_1 0x1802
+#define PPI_STARTUP_RW_COMMON_DPHY_2 0x1804
+#define PPI_STARTUP_RW_COMMON_DPHY_3 0x1806
+#define PPI_STARTUP_RW_COMMON_DPHY_4 0x1808
+#define PPI_STARTUP_RW_COMMON_DPHY_5 0x180a
+#define PPI_STARTUP_RW_COMMON_DPHY_6 0x180c
+#define PPI_STARTUP_RW_COMMON_DPHY_7 0x180e
+#define PPI_STARTUP_RW_COMMON_DPHY_8 0x1810
+#define PPI_STARTUP_RW_COMMON_DPHY_9 0x1812
+#define PPI_STARTUP_RW_COMMON_DPHY_A 0x1814
+#define PPI_STARTUP_RW_COMMON_DPHY_10 0x1820
+#define PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822
+#define PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824
+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848
+#define PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a
+#define PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c
+#define PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e
+#define PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850
+#define PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852
+#define PPI_CALIBCTRL_R_ADC_DEBUG 0x1854
+#define PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00
+#define PPI_RW_LPDCOCAL_TIMEBASE 0x1c02
+#define PPI_RW_LPDCOCAL_NREF 0x1c04
+#define PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06
+#define PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08
+#define PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a
+#define PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c
+#define PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e
+#define PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16
+#define PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18
+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a
+#define PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c
+#define PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e
+#define PPI_RW_LB_TIMEBASE_CONFIG 0x1c20
+#define PPI_RW_LB_STARTCMU_CONFIG 0x1c22
+#define PPI_R_LBPULSE_COUNTER_RB 0x1c24
+#define PPI_R_LB_START_CMU_RB 0x1c26
+#define PPI_RW_LB_DPHY_BURST_START 0x1c28
+#define PPI_RW_LB_CPHY_BURST_START 0x1c2a
+#define PPI_RW_DDLCAL_CFG_0 0x1c40
+#define PPI_RW_DDLCAL_CFG_1 0x1c42
+#define PPI_RW_DDLCAL_CFG_2 0x1c44
+#define PPI_RW_DDLCAL_CFG_3 0x1c46
+#define PPI_RW_DDLCAL_CFG_4 0x1c48
+#define PPI_RW_DDLCAL_CFG_5 0x1c4a
+#define PPI_RW_DDLCAL_CFG_6 0x1c4c
+#define PPI_RW_DDLCAL_CFG_7 0x1c4e
+#define PPI_R_DDLCAL_DEBUG_0 0x1c50
+#define PPI_R_DDLCAL_DEBUG_1 0x1c52
+#define PPI_RW_PARITY_TEST 0x1c60
+#define PPI_RW_STARTUP_OVR_0 0x1c62
+#define PPI_RW_STARTUP_STATE_OVR_1 0x1c64
+#define PPI_RW_DTB_SELECTOR 0x1c66
+#define PPI_RW_DPHY_CLK_SPARE 0x1c6a
+#define PPI_RW_COMMON_CFG 0x1c6c
+#define PPI_RW_TERMCAL_CFG_0 0x1c80
+#define PPI_R_TERMCAL_DEBUG_0 0x1c82
+#define PPI_RW_TERMCAL_CTRL_0 0x1c84
+#define PPI_RW_OFFSETCAL_CFG_0 0x1ca0
+#define PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2
+#define PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4
+#define PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6
+#define PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8
+#define PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa
+#define PPI_RW_HSDCOCAL_CFG_O 0x1d00
+#define PPI_RW_HSDCOCAL_CFG_1 0x1d02
+#define PPI_RW_HSDCOCAL_CFG_2 0x1d04
+#define PPI_RW_HSDCOCAL_CFG_3 0x1d06
+#define PPI_RW_HSDCOCAL_CFG_4 0x1d08
+#define PPI_RW_HSDCOCAL_CFG_5 0x1d0a
+#define PPI_RW_HSDCOCAL_CFG_6 0x1d0c
+#define PPI_RW_HSDCOCAL_CFG_7 0x1d0e
+#define PPI_RW_HSDCOCAL_CFG_8 0x1d10
+#define PPI_R_HSDCOCAL_DEBUG_RB 0x1d12
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086
+#define CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8
+#define CORE_DIG_RW_TRIO0_0 0x2100
+#define CORE_DIG_RW_TRIO0_1 0x2102
+#define CORE_DIG_RW_TRIO0_2 0x2104
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486
+#define CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8
+#define CORE_DIG_RW_TRIO1_0 0x2500
+#define CORE_DIG_RW_TRIO1_1 0x2502
+#define CORE_DIG_RW_TRIO1_2 0x2504
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886
+#define CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4
+#define CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6
+#define CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8
+#define CORE_DIG_RW_TRIO2_0 0x2900
+#define CORE_DIG_RW_TRIO2_1 0x2902
+#define CORE_DIG_RW_TRIO2_2 0x2904
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86
+#define CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c
+#define CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086
+#define CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400
+#define CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402
+#define CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812
+#define CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826
+#define CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854
+#define CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e
+#define CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860
+#define CORE_DIG_RW_COMMON_0 0x3880
+#define CORE_DIG_RW_COMMON_1 0x3882
+#define CORE_DIG_RW_COMMON_2 0x3884
+#define CORE_DIG_RW_COMMON_3 0x3886
+#define CORE_DIG_RW_COMMON_4 0x3888
+#define CORE_DIG_RW_COMMON_5 0x388a
+#define CORE_DIG_RW_COMMON_6 0x388c
+#define CORE_DIG_RW_COMMON_7 0x388e
+#define CORE_DIG_RW_COMMON_8 0x3890
+#define CORE_DIG_RW_COMMON_9 0x3892
+#define CORE_DIG_RW_COMMON_10 0x3894
+#define CORE_DIG_RW_COMMON_11 0x3896
+#define CORE_DIG_RW_COMMON_12 0x3898
+#define CORE_DIG_RW_COMMON_13 0x389a
+#define CORE_DIG_RW_COMMON_14 0x389c
+#define CORE_DIG_RW_COMMON_15 0x389e
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4
+#define CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6
+#define CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0
+#define CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2
+#define PPI_RW_DPHY_LANE0_LBERT_0 0x4000
+#define PPI_RW_DPHY_LANE0_LBERT_1 0x4002
+#define PPI_R_DPHY_LANE0_LBERT_0 0x4004
+#define PPI_R_DPHY_LANE0_LBERT_1 0x4006
+#define PPI_RW_DPHY_LANE0_SPARE 0x4008
+#define PPI_RW_DPHY_LANE1_LBERT_0 0x4400
+#define PPI_RW_DPHY_LANE1_LBERT_1 0x4402
+#define PPI_R_DPHY_LANE1_LBERT_0 0x4404
+#define PPI_R_DPHY_LANE1_LBERT_1 0x4406
+#define PPI_RW_DPHY_LANE1_SPARE 0x4408
+#define PPI_RW_DPHY_LANE2_LBERT_0 0x4800
+#define PPI_RW_DPHY_LANE2_LBERT_1 0x4802
+#define PPI_R_DPHY_LANE2_LBERT_0 0x4804
+#define PPI_R_DPHY_LANE2_LBERT_1 0x4806
+#define PPI_RW_DPHY_LANE2_SPARE 0x4808
+#define PPI_RW_DPHY_LANE3_LBERT_0 0x4c00
+#define PPI_RW_DPHY_LANE3_LBERT_1 0x4c02
+#define PPI_R_DPHY_LANE3_LBERT_0 0x4c04
+#define PPI_R_DPHY_LANE3_LBERT_1 0x4c06
+#define PPI_RW_DPHY_LANE3_SPARE 0x4c08
+#define CORE_DIG_DLANE_0_RW_CFG_0 0x6000
+#define CORE_DIG_DLANE_0_RW_CFG_1 0x6002
+#define CORE_DIG_DLANE_0_RW_CFG_2 0x6004
+#define CORE_DIG_DLANE_0_RW_LP_0 0x6080
+#define CORE_DIG_DLANE_0_RW_LP_1 0x6082
+#define CORE_DIG_DLANE_0_RW_LP_2 0x6084
+#define CORE_DIG_DLANE_0_R_LP_0 0x60a0
+#define CORE_DIG_DLANE_0_R_LP_1 0x60a2
+#define CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0
+#define CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100
+#define CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102
+#define CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104
+#define CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106
+#define CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108
+#define CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a
+#define CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c
+#define CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e
+#define CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110
+#define CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112
+#define CORE_DIG_DLANE_0_R_HS_RX_0 0x6120
+#define CORE_DIG_DLANE_0_R_HS_RX_1 0x6122
+#define CORE_DIG_DLANE_0_R_HS_RX_2 0x6124
+#define CORE_DIG_DLANE_0_R_HS_RX_3 0x6126
+#define CORE_DIG_DLANE_0_R_HS_RX_4 0x6128
+#define CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200
+#define CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202
+#define CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204
+#define CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206
+#define CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208
+#define CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a
+#define CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c
+#define CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e
+#define CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210
+#define CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212
+#define CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214
+#define CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216
+#define CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218
+#define CORE_DIG_DLANE_1_RW_CFG_0 0x6400
+#define CORE_DIG_DLANE_1_RW_CFG_1 0x6402
+#define CORE_DIG_DLANE_1_RW_CFG_2 0x6404
+#define CORE_DIG_DLANE_1_RW_LP_0 0x6480
+#define CORE_DIG_DLANE_1_RW_LP_1 0x6482
+#define CORE_DIG_DLANE_1_RW_LP_2 0x6484
+#define CORE_DIG_DLANE_1_R_LP_0 0x64a0
+#define CORE_DIG_DLANE_1_R_LP_1 0x64a2
+#define CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0
+#define CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500
+#define CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502
+#define CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504
+#define CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506
+#define CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508
+#define CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a
+#define CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c
+#define CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e
+#define CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510
+#define CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512
+#define CORE_DIG_DLANE_1_R_HS_RX_0 0x6520
+#define CORE_DIG_DLANE_1_R_HS_RX_1 0x6522
+#define CORE_DIG_DLANE_1_R_HS_RX_2 0x6524
+#define CORE_DIG_DLANE_1_R_HS_RX_3 0x6526
+#define CORE_DIG_DLANE_1_R_HS_RX_4 0x6528
+#define CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600
+#define CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602
+#define CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604
+#define CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606
+#define CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608
+#define CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a
+#define CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c
+#define CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e
+#define CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610
+#define CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612
+#define CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614
+#define CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616
+#define CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618
+#define CORE_DIG_DLANE_2_RW_CFG_0 0x6800
+#define CORE_DIG_DLANE_2_RW_CFG_1 0x6802
+#define CORE_DIG_DLANE_2_RW_CFG_2 0x6804
+#define CORE_DIG_DLANE_2_RW_LP_0 0x6880
+#define CORE_DIG_DLANE_2_RW_LP_1 0x6882
+#define CORE_DIG_DLANE_2_RW_LP_2 0x6884
+#define CORE_DIG_DLANE_2_R_LP_0 0x68a0
+#define CORE_DIG_DLANE_2_R_LP_1 0x68a2
+#define CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0
+#define CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900
+#define CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902
+#define CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904
+#define CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906
+#define CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908
+#define CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a
+#define CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c
+#define CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e
+#define CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910
+#define CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912
+#define CORE_DIG_DLANE_2_R_HS_RX_0 0x6920
+#define CORE_DIG_DLANE_2_R_HS_RX_1 0x6922
+#define CORE_DIG_DLANE_2_R_HS_RX_2 0x6924
+#define CORE_DIG_DLANE_2_R_HS_RX_3 0x6926
+#define CORE_DIG_DLANE_2_R_HS_RX_4 0x6928
+#define CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00
+#define CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02
+#define CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04
+#define CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06
+#define CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08
+#define CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a
+#define CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c
+#define CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e
+#define CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10
+#define CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12
+#define CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14
+#define CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16
+#define CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18
+#define CORE_DIG_DLANE_3_RW_CFG_0 0x6c00
+#define CORE_DIG_DLANE_3_RW_CFG_1 0x6c02
+#define CORE_DIG_DLANE_3_RW_CFG_2 0x6c04
+#define CORE_DIG_DLANE_3_RW_LP_0 0x6c80
+#define CORE_DIG_DLANE_3_RW_LP_1 0x6c82
+#define CORE_DIG_DLANE_3_RW_LP_2 0x6c84
+#define CORE_DIG_DLANE_3_R_LP_0 0x6ca0
+#define CORE_DIG_DLANE_3_R_LP_1 0x6ca2
+#define CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0
+#define CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00
+#define CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02
+#define CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04
+#define CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06
+#define CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08
+#define CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a
+#define CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c
+#define CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e
+#define CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10
+#define CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12
+#define CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20
+#define CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22
+#define CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24
+#define CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26
+#define CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28
+#define CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00
+#define CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02
+#define CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04
+#define CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06
+#define CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08
+#define CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a
+#define CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c
+#define CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e
+#define CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10
+#define CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12
+#define CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14
+#define CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16
+#define CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18
+#define CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000
+#define CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002
+#define CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004
+#define CORE_DIG_DLANE_CLK_RW_LP_0 0x7080
+#define CORE_DIG_DLANE_CLK_RW_LP_1 0x7082
+#define CORE_DIG_DLANE_CLK_RW_LP_2 0x7084
+#define CORE_DIG_DLANE_CLK_R_LP_0 0x70a0
+#define CORE_DIG_DLANE_CLK_R_LP_1 0x70a2
+#define CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110
+#define CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112
+#define CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120
+#define CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122
+#define CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124
+#define CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126
+#define CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216
+#define CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218
+#define PPI_RW_CPHY_TRIO0_LBERT_0 0x8000
+#define PPI_RW_CPHY_TRIO0_LBERT_1 0x8002
+#define PPI_R_CPHY_TRIO0_LBERT_0 0x8004
+#define PPI_R_CPHY_TRIO0_LBERT_1 0x8006
+#define PPI_RW_CPHY_TRIO0_SPARE 0x8008
+#define PPI_RW_CPHY_TRIO1_LBERT_0 0x8400
+#define PPI_RW_CPHY_TRIO1_LBERT_1 0x8402
+#define PPI_R_CPHY_TRIO1_LBERT_0 0x8404
+#define PPI_R_CPHY_TRIO1_LBERT_1 0x8406
+#define PPI_RW_CPHY_TRIO1_SPARE 0x8408
+#define PPI_RW_CPHY_TRIO2_LBERT_0 0x8800
+#define PPI_RW_CPHY_TRIO2_LBERT_1 0x8802
+#define PPI_R_CPHY_TRIO2_LBERT_0 0x8804
+#define PPI_R_CPHY_TRIO2_LBERT_1 0x8806
+#define PPI_RW_CPHY_TRIO2_SPARE 0x8808
+#define CORE_DIG_CLANE_0_RW_CFG_0 0xa000
+#define CORE_DIG_CLANE_0_RW_CFG_2 0xa004
+#define CORE_DIG_CLANE_0_RW_LP_0 0xa080
+#define CORE_DIG_CLANE_0_RW_LP_1 0xa082
+#define CORE_DIG_CLANE_0_RW_LP_2 0xa084
+#define CORE_DIG_CLANE_0_R_LP_0 0xa0a0
+#define CORE_DIG_CLANE_0_R_LP_1 0xa0a2
+#define CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100
+#define CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102
+#define CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104
+#define CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106
+#define CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108
+#define CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a
+#define CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c
+#define CORE_DIG_CLANE_0_R_RX_0 0xa120
+#define CORE_DIG_CLANE_0_R_RX_1 0xa122
+#define CORE_DIG_CLANE_0_R_TX_0 0xa124
+#define CORE_DIG_CLANE_0_R_RX_2 0xa126
+#define CORE_DIG_CLANE_0_R_RX_3 0xa128
+#define CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200
+#define CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202
+#define CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204
+#define CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206
+#define CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208
+#define CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a
+#define CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c
+#define CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e
+#define CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210
+#define CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212
+#define CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214
+#define CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216
+#define CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218
+#define CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a
+#define CORE_DIG_CLANE_1_RW_CFG_0 0xa400
+#define CORE_DIG_CLANE_1_RW_CFG_2 0xa404
+#define CORE_DIG_CLANE_1_RW_LP_0 0xa480
+#define CORE_DIG_CLANE_1_RW_LP_1 0xa482
+#define CORE_DIG_CLANE_1_RW_LP_2 0xa484
+#define CORE_DIG_CLANE_1_R_LP_0 0xa4a0
+#define CORE_DIG_CLANE_1_R_LP_1 0xa4a2
+#define CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500
+#define CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502
+#define CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504
+#define CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506
+#define CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508
+#define CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a
+#define CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c
+#define CORE_DIG_CLANE_1_R_RX_0 0xa520
+#define CORE_DIG_CLANE_1_R_RX_1 0xa522
+#define CORE_DIG_CLANE_1_R_TX_0 0xa524
+#define CORE_DIG_CLANE_1_R_RX_2 0xa526
+#define CORE_DIG_CLANE_1_R_RX_3 0xa528
+#define CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600
+#define CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602
+#define CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604
+#define CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606
+#define CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608
+#define CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a
+#define CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c
+#define CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e
+#define CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610
+#define CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612
+#define CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614
+#define CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616
+#define CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618
+#define CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a
+#define CORE_DIG_CLANE_2_RW_CFG_0 0xa800
+#define CORE_DIG_CLANE_2_RW_CFG_2 0xa804
+#define CORE_DIG_CLANE_2_RW_LP_0 0xa880
+#define CORE_DIG_CLANE_2_RW_LP_1 0xa882
+#define CORE_DIG_CLANE_2_RW_LP_2 0xa884
+#define CORE_DIG_CLANE_2_R_LP_0 0xa8a0
+#define CORE_DIG_CLANE_2_R_LP_1 0xa8a2
+#define CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900
+#define CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902
+#define CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904
+#define CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906
+#define CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908
+#define CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a
+#define CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c
+#define CORE_DIG_CLANE_2_R_RX_0 0xa920
+#define CORE_DIG_CLANE_2_R_RX_1 0xa922
+#define CORE_DIG_CLANE_2_R_TX_0 0xa924
+#define CORE_DIG_CLANE_2_R_RX_2 0xa926
+#define CORE_DIG_CLANE_2_R_RX_3 0xa928
+#define CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00
+#define CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02
+#define CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04
+#define CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06
+#define CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08
+#define CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a
+#define CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c
+#define CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e
+#define CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10
+#define CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12
+#define CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14
+#define CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16
+#define CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18
+#define CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a
+
+/* dwc csi host controller registers */
+#define IS_IO_CSI2_HOST_BASE(i) (IS_IO_BASE + 0x40000 + \
+ 0x2000 * (i))
+#define VERSION 0x0
+#define N_LANES 0x4
+#define CSI2_RESETN 0x8
+#define INT_ST_MAIN 0xc
+#define DATA_IDS_1 0x10
+#define DATA_IDS_2 0x14
+#define CDPHY_MODE 0x1c
+#define DATA_IDS_VC_1 0x30
+#define DATA_IDS_VC_2 0x34
+#define PHY_SHUTDOWNZ 0x40
+#define DPHY_RSTZ 0x44
+#define PHY_RX 0x48
+#define PHY_STOPSTATE 0x4c
+#define PHY_TEST_CTRL0 0x50
+#define PHY_TEST_CTRL1 0x54
+#define PPI_PG_PATTERN_VRES 0x60
+#define PPI_PG_PATTERN_HRES 0x64
+#define PPI_PG_CONFIG 0x68
+#define PPI_PG_ENABLE 0x6c
+#define PPI_PG_STATUS 0x70
+#define VC_EXTENSION 0xc8
+#define PHY_CAL 0xcc
+#define INT_ST_PHY_FATAL 0xe0
+#define INT_MSK_PHY_FATAL 0xe4
+#define INT_FORCE_PHY_FATAL 0xe8
+#define INT_ST_PKT_FATAL 0xf0
+#define INT_MSK_PKT_FATAL 0xf4
+#define INT_FORCE_PKT_FATAL 0xf8
+#define INT_ST_PHY 0x110
+#define INT_MSK_PHY 0x114
+#define INT_FORCE_PHY 0x118
+#define INT_ST_LINE 0x130
+#define INT_MSK_LINE 0x134
+#define INT_FORCE_LINE 0x138
+#define INT_ST_BNDRY_FRAME_FATAL 0x280
+#define INT_MSK_BNDRY_FRAME_FATAL 0x284
+#define INT_FORCE_BNDRY_FRAME_FATAL 0x288
+#define INT_ST_SEQ_FRAME_FATAL 0x290
+#define INT_MSK_SEQ_FRAME_FATAL 0x294
+#define INT_FORCE_SEQ_FRAME_FATAL 0x298
+#define INT_ST_CRC_FRAME_FATAL 0x2a0
+#define INT_MSK_CRC_FRAME_FATAL 0x2a4
+#define INT_FORCE_CRC_FRAME_FATAL 0x2a8
+#define INT_ST_PLD_CRC_FATAL 0x2b0
+#define INT_MSK_PLD_CRC_FATAL 0x2b4
+#define INT_FORCE_PLD_CRC_FATAL 0x2b8
+#define INT_ST_DATA_ID 0x2c0
+#define INT_MSK_DATA_ID 0x2c4
+#define INT_FORCE_DATA_ID 0x2c8
+#define INT_ST_ECC_CORRECTED 0x2d0
+#define INT_MSK_ECC_CORRECTED 0x2d4
+#define INT_FORCE_ECC_CORRECTED 0x2d8
+#define SCRAMBLING 0x300
+#define SCRAMBLING_SEED1 0x304
+#define SCRAMBLING_SEED2 0x308
+#define SCRAMBLING_SEED3 0x30c
+#define SCRAMBLING_SEED4 0x310
+#define SCRAMBLING 0x300
+
+#define IS_IO_CSI2_ADPL_PORT_BASE(i) (IS_IO_BASE + 0x40800 + \
+ 0x2000 * (i))
+#define CSI2_ADPL_INPUT_MODE 0x0
+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4
+#define CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8
+#define CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc
+#define CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4
+#define CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8
+#define CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac
+#define CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc
+#define CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0
+#define CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4
+#define CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8
+#define CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc
+#define CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0
+
+/* software control the legacy csi irq */
+#define IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40c00 + \
+ 0x2000 * (i))
+#define IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IS_IO_BASE + 0x40d00 + \
+ 0x2000 * (i))
+#define IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IS_IO_BASE + 0x49000)
+#define IS_IO_CSI2_IRQ_CTRL_BASE (IS_IO_BASE + 0x4e100)
+
+#define IRQ_CTL_EDGE 0x0
+#define IRQ_CTL_MASK 0x4
+#define IRQ_CTL_STATUS 0x8
+#define IRQ_CTL_CLEAR 0xc
+#define IRQ_CTL_ENABLE 0x10
+/* FE irq for PTL */
+#define IRQ1_CTL_MASK 0x14
+#define IRQ1_CTL_STATUS 0x18
+#define IRQ1_CTL_CLEAR 0x1c
+#define IRQ1_CTL_ENABLE 0x20
+
+/* software to set the clock gate to use the port or mgc */
+#define IS_IO_GPREGS_BASE (IS_IO_BASE + 0x49200)
+#define SRST_PORT_ARB 0x0
+#define SRST_MGC 0x4
+#define SRST_WIDTH_CONV 0x8
+#define SRST_CSI_IRQ 0xc
+#define SRST_CSI_LEGACY_IRQ 0x10
+#define CLK_EN_TXCLKESC 0x14
+#define CLK_DIV_FACTOR_IS_CLK 0x18
+#define CLK_DIV_FACTOR_APB_CLK 0x1c
+#define CSI_PORT_CLK_GATE 0x20
+#define CSI_PORTAB_AGGREGATION 0x24
+#define MGC_CLK_GATE 0x2c
+#define CG_CTRL_BITS 0x3c
+#define SPARE_RW 0xf8
+#define SPARE_RO 0xfc
+
+#define IS_IO_CSI2_MPF_PORT_BASE(i) (IS_IO_BASE + 0x53000 + \
+ 0x1000 * (i))
+#define MPF_16_IRQ_CNTRL_STATUS 0x238
+#define MPF_16_IRQ_CNTRL_CLEAR 0x23c
+#define MPF_16_IRQ_CNTRL_ENABLE 0x240
+
+/* software config the phy */
+#define IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x53400)
+#define IPU8_IS_IO_CSI2_GPREGS_BASE (IS_IO_BASE + 0x40e00)
+#define CSI_ADAPT_LAYER_SRST 0x0
+#define MPF_SRST_RST 0x4
+#define CSI_ERR_IRQ_CTRL_SRST 0x8
+#define CSI_SYNC_RC_SRST 0xc
+#define CSI_CG_CTRL_BITS 0x10
+#define SOC_CSI2HOST_SELECT 0x14
+#define PHY_RESET 0x18
+#define PHY_SHUTDOWN 0x1c
+#define PHY_MODE 0x20
+#define PHY_READY 0x24
+#define PHY_CLK_LANE_FORCE_CONTROL 0x28
+#define PHY_CLK_LANE_CONTROL 0x2c
+#define PHY_CLK_LANE_STATUS 0x30
+#define PHY_LANE_RX_ESC_REQ 0x34
+#define PHY_LANE_RX_ESC_DATA 0x38
+#define PHY_LANE_TURNDISABLE 0x3c
+#define PHY_LANE_DIRECTION 0x40
+#define PHY_LANE_FORCE_CONTROL 0x44
+#define PHY_LANE_CONTROL_EN 0x48
+#define PHY_LANE_CONTROL_DATAWIDTH 0x4c
+#define PHY_LANE_STATUS 0x50
+#define PHY_LANE_ERR 0x54
+#define PHY_LANE_RXALP 0x58
+#define PHY_LANE_RXALP_NIBBLE 0x5c
+#define PHY_PARITY_ERROR 0x60
+#define PHY_DEBUG_REGS_CLK_GATE_EN 0x64
+#define SPARE_RW 0xf8
+#define SPARE_RO 0xfc
+
+/* software not touch */
+#define PORT_ARB_BASE (IS_IO_BASE + 0x4e000)
+#define PORT_ARB_IRQ_CTL_STATUS 0x4
+#define PORT_ARB_IRQ_CTL_CLEAR 0x8
+#define PORT_ARB_IRQ_CTL_ENABLE 0xc
+
+#define MGC_PPC 4U
+#define MGC_DTYPE_RAW(i) (((i) - 8) / 2)
+#define IS_IO_MGC_BASE (IS_IO_BASE + 0x48000)
+#define MGC_KICK 0x0
+#define MGC_ASYNC_STOP 0x4
+#define MGC_PORT_OFFSET 0x100
+#define MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4)
+#define MGC_MG_PORT(i) (IS_IO_MGC_BASE + \
+ (i) * MGC_PORT_OFFSET)
+/* per mgc instance */
+#define MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28
+#define MGC_MG_MODE 0x2c
+#define MGC_MG_INIT_COUNTER 0x30
+#define MGC_MG_MIPI_VC 0x34
+#define MGC_MG_MIPI_DTYPES 0x38
+#define MGC_MG_MULTI_DTYPES_MODE 0x3c
+#define MGC_MG_NOF_FRAMES 0x40
+#define MGC_MG_FRAME_DIM 0x44
+#define MGC_MG_HBLANK 0x48
+#define MGC_MG_VBLANK 0x4c
+#define MGC_MG_TPG_MODE 0x50
+#define MGC_MG_TPG_R0 0x54
+#define MGC_MG_TPG_G0 0x58
+#define MGC_MG_TPG_B0 0x5c
+#define MGC_MG_TPG_R1 0x60
+#define MGC_MG_TPG_G1 0x64
+#define MGC_MG_TPG_B1 0x68
+#define MGC_MG_TPG_FACTORS 0x6c
+#define MGC_MG_TPG_MASKS 0x70
+#define MGC_MG_TPG_XY_MASK 0x74
+#define MGC_MG_TPG_TILE_DIM 0x78
+#define MGC_MG_PRBS_LFSR_INIT_0 0x7c
+#define MGC_MG_PRBS_LFSR_INIT_1 0x80
+#define MGC_MG_SYNC_STOP_POINT 0x84
+#define MGC_MG_SYNC_STOP_POINT_LOC 0x88
+#define MGC_MG_ERR_INJECT 0x8c
+#define MGC_MG_ERR_LOCATION 0x90
+#define MGC_MG_DTO_SPEED_CTRL_EN 0x94
+#define MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98
+#define MGC_MG_HOR_LOC_STTS 0x9c
+#define MGC_MG_VER_LOC_STTS 0xa0
+#define MGC_MG_FRAME_NUM_STTS 0xa4
+#define MGC_MG_BUSY_STTS 0xa8
+#define MGC_MG_STOPPED_STTS 0xac
+/* tile width and height in pixels for Chess board and Color palette */
+#define MGC_TPG_TILE_WIDTH 64U
+#define MGC_TPG_TILE_HEIGHT 64U
+
+#define IPU_CSI_PORT_A_ADDR_OFFSET 0x0
+#define IPU_CSI_PORT_B_ADDR_OFFSET 0x0
+#define IPU_CSI_PORT_C_ADDR_OFFSET 0x0
+#define IPU_CSI_PORT_D_ADDR_OFFSET 0x0
+
+/*
+ * 0 - CSI RX Port 0 interrupt;
+ * 1 - MPF Port 0 interrupt;
+ * 2 - CSI RX Port 1 interrupt;
+ * 3 - MPF Port 1 interrupt;
+ * 4 - CSI RX Port 2 interrupt;
+ * 5 - MPF Port 2 interrupt;
+ * 6 - CSI RX Port 3 interrupt;
+ * 7 - MPF Port 3 interrupt;
+ * 8 - Port ARB FIFO 0 overflow;
+ * 9 - Port ARB FIFO 1 overflow;
+ * 10 - Port ARB FIFO 2 overflow;
+ * 11 - Port ARB FIFO 3 overflow;
+ * 12 - isys_cfgnoc_err_probe_intl;
+ * 13-15 - reserved
+ */
+#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff
+
+/* Adapter layer irq */
+#define IPU7_CSI_ADPL_IRQ_MASK 0xffff
+
+/* sw irq from legacy irq control
+ * legacy irq status
+ * IPU7
+ * 0 - CSI Port 0 error interrupt
+ * 1 - CSI Port 0 sync interrupt
+ * 2 - CSI Port 1 error interrupt
+ * 3 - CSI Port 1 sync interrupt
+ * 4 - CSI Port 2 error interrupt
+ * 5 - CSI Port 2 sync interrupt
+ * 6 - CSI Port 3 error interrupt
+ * 7 - CSI Port 3 sync interrupt
+ * IPU7P5
+ * 0 - CSI Port 0 error interrupt
+ * 1 - CSI Port 0 fs interrupt
+ * 2 - CSI Port 0 fe interrupt
+ * 3 - CSI Port 1 error interrupt
+ * 4 - CSI Port 1 fs interrupt
+ * 5 - CSI Port 1 fe interrupt
+ * 6 - CSI Port 2 error interrupt
+ * 7 - CSI Port 2 fs interrupt
+ * 8 - CSI Port 2 fe interrupt
+ */
+#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff
+
+/* legacy error status per port
+ * 0 - Error handler FIFO full;
+ * 1 - Reserved Short Packet encoding detected;
+ * 2 - Reserved Long Packet encoding detected;
+ * 3 - Received packet is too short (fewer data words than specified in header);
+ * 4 - Received packet is too long (more data words than specified in header);
+ * 5 - Short packet discarded due to errors;
+ * 6 - Long packet discarded due to errors;
+ * 7 - CSI Combo Rx interrupt;
+ * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0;
+ */
+#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff
+
+/*
+ * 0 - VC0 frame start received
+ * 1 - VC0 frame end received
+ * 2 - VC1 frame start received
+ * 3 - VC1 frame end received
+ * 4 - VC2 frame start received
+ * 5 - VC2 frame end received
+ * 6 - VC3 frame start received
+ * 7 - VC3 frame end received
+ * 8 - VC4 frame start received
+ * 9 - VC4 frame end received
+ * 10 - VC5 frame start received
+ * 11 - VC5 frame end received
+ * 12 - VC6 frame start received
+ * 13 - VC6 frame end received
+ * 14 - VC7 frame start received
+ * 15 - VC7 frame end received
+ * 16 - VC8 frame start received
+ * 17 - VC8 frame end received
+ * 18 - VC9 frame start received
+ * 19 - VC9 frame end received
+ * 20 - VC10 frame start received
+ * 21 - VC10 frame end received
+ * 22 - VC11 frame start received
+ * 23 - VC11 frame end received
+ * 24 - VC12 frame start received
+ * 25 - VC12 frame end received
+ * 26 - VC13 frame start received
+ * 27 - VC13 frame end received
+ * 28 - VC14 frame start received
+ * 29 - VC14 frame end received
+ * 30 - VC15 frame start received
+ * 31 - VC15 frame end received
+ */
+
+#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0
+#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0
+
+#define CSI_RX_NUM_ERRORS_IN_IRQ 12U
+#define CSI_RX_NUM_SYNC_IN_IRQ 32U
+
+enum CSI_FE_MODE_TYPE {
+ CSI_FE_DPHY_MODE = 0,
+ CSI_FE_CPHY_MODE = 1,
+};
+
+enum CSI_FE_INPUT_MODE {
+ CSI_SENSOR_INPUT = 0,
+ CSI_MIPIGEN_INPUT = 1,
+};
+
+enum MGC_CSI_ADPL_TYPE {
+ MGC_MAPPED_2_LANES = 0,
+ MGC_MAPPED_4_LANES = 1,
+};
+
+enum CSI2HOST_SELECTION {
+ CSI2HOST_SEL_SOC = 0,
+ CSI2HOST_SEL_CSI2HOST = 1,
+};
+
+#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port))
+#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port))
+
+/* ---------------------------------------------------------------- */
+#define CSI_REG_BASE 0x220000
+#define CSI_REG_BASE_PORT(id) ((id) * 0x1000)
+
+/* CSI Port General Purpose Registers */
+#define CSI_REG_PORT_GPREG_SRST 0x0
+#define CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4
+#define CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8
+
+#define CSI_RX_NUM_IRQ 32U
+
+#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555
+#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa
+#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff
+#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff
+
+#endif /* IPU7_ISYS_CSI2_REG_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.c b/drivers/staging/media/ipu7/ipu7-isys-csi2.c
new file mode 100644
index 000000000000..9c16ae9a0e5b
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-csi2.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/atomic.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/minmax.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-subdev.h>
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-isys-csi-phy.h"
+
+static const u32 csi2_supported_codes[] = {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_RGB565_1X16,
+ MEDIA_BUS_FMT_RGB888_1X24,
+ MEDIA_BUS_FMT_UYVY8_1X16,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+ MEDIA_BUS_FMT_YUYV10_1X20,
+ 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,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ 0,
+};
+
+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2)
+{
+ struct media_pad *src_pad;
+
+ src_pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+ if (IS_ERR(src_pad)) {
+ dev_err(&csi2->isys->adev->auxdev.dev,
+ "can't get source pad of %s (%ld)\n",
+ csi2->asd.sd.name, PTR_ERR(src_pad));
+ return PTR_ERR(src_pad);
+ }
+
+ return v4l2_get_link_freq(src_pad, 0, 0);
+}
+
+static int csi2_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+
+ dev_dbg(dev, "csi2 subscribe event(type %u id %u)\n",
+ sub->type, sub->id);
+
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+ return v4l2_event_subscribe(fh, sub, 10, NULL);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_subdev_core_ops csi2_sd_core_ops = {
+ .subscribe_event = csi2_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static void csi2_irq_enable(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_device *isp = csi2->isys->adev->isp;
+ unsigned int offset, mask;
+
+ /* enable CSI2 legacy error irq */
+ offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+ writel(mask, csi2->base + offset + IRQ_CTL_MASK);
+ writel(mask, csi2->base + offset + IRQ_CTL_ENABLE);
+
+ /* enable CSI2 legacy sync irq */
+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+ writel(mask, csi2->base + offset + IRQ_CTL_MASK);
+ writel(mask, csi2->base + offset + IRQ_CTL_ENABLE);
+
+ mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK;
+ if (!is_ipu7(isp->hw_ver)) {
+ writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR);
+ writel(mask, csi2->base + offset + IRQ1_CTL_MASK);
+ writel(mask, csi2->base + offset + IRQ1_CTL_ENABLE);
+ }
+}
+
+static void csi2_irq_disable(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_device *isp = csi2->isys->adev->isp;
+ unsigned int offset, mask;
+
+ /* disable CSI2 legacy error irq */
+ offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+ writel(0, csi2->base + offset + IRQ_CTL_MASK);
+ writel(0, csi2->base + offset + IRQ_CTL_ENABLE);
+
+ /* disable CSI2 legacy sync irq */
+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
+ writel(mask, csi2->base + offset + IRQ_CTL_CLEAR);
+ writel(0, csi2->base + offset + IRQ_CTL_MASK);
+ writel(0, csi2->base + offset + IRQ_CTL_ENABLE);
+
+ if (!is_ipu7(isp->hw_ver)) {
+ writel(mask, csi2->base + offset + IRQ1_CTL_CLEAR);
+ writel(0, csi2->base + offset + IRQ1_CTL_MASK);
+ writel(0, csi2->base + offset + IRQ1_CTL_ENABLE);
+ }
+}
+
+static void ipu7_isys_csi2_disable_stream(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_isys *isys = csi2->isys;
+ void __iomem *isys_base = isys->pdata->base;
+
+ ipu7_isys_csi_phy_powerdown(csi2);
+
+ writel(0x4, isys_base + IS_IO_GPREGS_BASE + CLK_DIV_FACTOR_APB_CLK);
+ csi2_irq_disable(csi2);
+}
+
+static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_isys *isys = csi2->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *isys_base = isys->pdata->base;
+ unsigned int port, nlanes, offset;
+ int ret;
+
+ port = csi2->port;
+ nlanes = csi2->nlanes;
+
+ offset = IS_IO_GPREGS_BASE;
+ writel(0x2, isys_base + offset + CLK_DIV_FACTOR_APB_CLK);
+ dev_dbg(dev, "port %u CLK_GATE = 0x%04x DIV_FACTOR_APB_CLK=0x%04x\n",
+ port, readl(isys_base + offset + CSI_PORT_CLK_GATE),
+ readl(isys_base + offset + CLK_DIV_FACTOR_APB_CLK));
+ if (port == 0U && nlanes == 4U && !is_ipu7(isys->adev->isp->hw_ver)) {
+ dev_dbg(dev, "CSI port %u in aggregation mode\n", port);
+ writel(0x1, isys_base + offset + CSI_PORTAB_AGGREGATION);
+ }
+
+ /* input is coming from CSI receiver (sensor) */
+ offset = IS_IO_CSI2_ADPL_PORT_BASE(port);
+ writel(CSI_SENSOR_INPUT, isys_base + offset + CSI2_ADPL_INPUT_MODE);
+ writel(1, isys_base + offset + CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN);
+
+ ret = ipu7_isys_csi_phy_powerup(csi2);
+ if (ret) {
+ dev_err(dev, "CSI-%d PHY power up failed %d\n", port, ret);
+ return ret;
+ }
+
+ csi2_irq_enable(csi2);
+
+ return 0;
+}
+
+static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+ struct device *dev = &asd->isys->adev->auxdev.dev;
+ struct v4l2_mbus_framefmt *sink_ffmt;
+ struct v4l2_mbus_framefmt *src_ffmt;
+ struct v4l2_rect *crop;
+
+ if (sel->pad == IPU7_CSI2_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP)
+ return -EINVAL;
+
+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ sel->pad,
+ sel->stream);
+ if (!sink_ffmt)
+ return -EINVAL;
+
+ src_ffmt = v4l2_subdev_state_get_format(state, sel->pad, sel->stream);
+ if (!src_ffmt)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ if (!crop)
+ return -EINVAL;
+
+ /* Only vertical cropping is supported */
+ sel->r.left = 0;
+ sel->r.width = sink_ffmt->width;
+ /* Non-bayer formats can't be single line cropped */
+ if (!ipu7_isys_is_bayer_format(sink_ffmt->code))
+ sel->r.top &= ~1U;
+ sel->r.height = clamp(sel->r.height & ~1U, IPU_ISYS_MIN_HEIGHT,
+ sink_ffmt->height - sel->r.top);
+ *crop = sel->r;
+
+ /* update source pad format */
+ src_ffmt->width = sel->r.width;
+ src_ffmt->height = sel->r.height;
+ if (ipu7_isys_is_bayer_format(sink_ffmt->code))
+ src_ffmt->code = ipu7_isys_convert_bayer_order(sink_ffmt->code,
+ sel->r.left,
+ sel->r.top);
+ dev_dbg(dev, "set crop for %s sel: %d,%d,%d,%d code: 0x%x\n",
+ sd->name, sel->r.left, sel->r.top, sel->r.width, sel->r.height,
+ src_ffmt->code);
+
+ return 0;
+}
+
+static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ struct v4l2_mbus_framefmt *sink_ffmt;
+ struct v4l2_rect *crop;
+ int ret = 0;
+
+ if (sd->entity.pads[sel->pad].flags & MEDIA_PAD_FL_SINK)
+ return -EINVAL;
+
+ sink_ffmt = v4l2_subdev_state_get_opposite_stream_format(state,
+ sel->pad,
+ sel->stream);
+ if (!sink_ffmt)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, sel->pad, sel->stream);
+ if (!crop)
+ return -EINVAL;
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = sink_ffmt->width;
+ sel->r.height = sink_ffmt->height;
+ break;
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *crop;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/*
+ * Maximum stream ID is 63 for now, as we use u64 bitmask to represent a set
+ * of streams.
+ */
+#define CSI2_SUBDEV_MAX_STREAM_ID 63
+
+static int ipu7_isys_csi2_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
+ struct v4l2_subdev *r_sd;
+ struct media_pad *rp;
+ u32 sink_pad, sink_stream;
+ int ret, i;
+
+ if (!csi2->stream_count) {
+ dev_dbg(&csi2->isys->adev->auxdev.dev,
+ "stream on CSI2-%u with %u lanes\n", csi2->port,
+ csi2->nlanes);
+ ret = ipu7_isys_csi2_enable_stream(csi2);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) {
+ if (streams_mask & BIT_ULL(i))
+ break;
+ }
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i,
+ &sink_pad, &sink_stream);
+ if (ret)
+ return ret;
+
+ rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]);
+ r_sd = media_entity_to_v4l2_subdev(rp->entity);
+
+ ret = v4l2_subdev_enable_streams(r_sd, rp->index,
+ BIT_ULL(sink_stream));
+ if (!ret) {
+ csi2->stream_count++;
+ return 0;
+ }
+
+ if (!csi2->stream_count)
+ ipu7_isys_csi2_disable_stream(csi2);
+
+ return ret;
+}
+
+static int ipu7_isys_csi2_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+ struct ipu7_isys_csi2 *csi2 = to_ipu7_isys_csi2(asd);
+ struct v4l2_subdev *r_sd;
+ struct media_pad *rp;
+ u32 sink_pad, sink_stream;
+ int ret, i;
+
+ for (i = 0; i <= CSI2_SUBDEV_MAX_STREAM_ID; i++) {
+ if (streams_mask & BIT_ULL(i))
+ break;
+ }
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing, pad, i,
+ &sink_pad, &sink_stream);
+ if (ret)
+ return ret;
+
+ rp = media_pad_remote_pad_first(&sd->entity.pads[IPU7_CSI2_PAD_SINK]);
+ r_sd = media_entity_to_v4l2_subdev(rp->entity);
+
+ v4l2_subdev_disable_streams(r_sd, rp->index, BIT_ULL(sink_stream));
+
+ if (--csi2->stream_count)
+ return 0;
+
+ dev_dbg(&csi2->isys->adev->auxdev.dev,
+ "stream off CSI2-%u with %u lanes\n", csi2->port, csi2->nlanes);
+
+ ipu7_isys_csi2_disable_stream(csi2);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops csi2_sd_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ipu7_isys_subdev_set_fmt,
+ .get_selection = ipu7_isys_csi2_get_sel,
+ .set_selection = ipu7_isys_csi2_set_sel,
+ .enum_mbus_code = ipu7_isys_subdev_enum_mbus_code,
+ .enable_streams = ipu7_isys_csi2_enable_streams,
+ .disable_streams = ipu7_isys_csi2_disable_streams,
+ .set_routing = ipu7_isys_subdev_set_routing,
+};
+
+static const struct v4l2_subdev_ops csi2_sd_ops = {
+ .core = &csi2_sd_core_ops,
+ .pad = &csi2_sd_pad_ops,
+};
+
+static const struct media_entity_operations csi2_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2)
+{
+ if (!csi2->isys)
+ return;
+
+ v4l2_device_unregister_subdev(&csi2->asd.sd);
+ v4l2_subdev_cleanup(&csi2->asd.sd);
+ ipu7_isys_subdev_cleanup(&csi2->asd);
+ csi2->isys = NULL;
+}
+
+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2,
+ struct ipu7_isys *isys,
+ void __iomem *base, unsigned int index)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ int ret;
+
+ csi2->isys = isys;
+ csi2->base = base;
+ csi2->port = index;
+
+ if (!is_ipu7(isys->adev->isp->hw_ver))
+ csi2->legacy_irq_mask = 0x7U << (index * 3U);
+ else
+ csi2->legacy_irq_mask = 0x3U << (index * 2U);
+
+ dev_dbg(dev, "csi-%d legacy irq mask = 0x%x\n", index,
+ csi2->legacy_irq_mask);
+
+ csi2->asd.sd.entity.ops = &csi2_entity_ops;
+ csi2->asd.isys = isys;
+
+ ret = ipu7_isys_subdev_init(&csi2->asd, &csi2_sd_ops, 0,
+ IPU7_NR_OF_CSI2_SINK_PADS,
+ IPU7_NR_OF_CSI2_SRC_PADS);
+ if (ret)
+ return ret;
+
+ csi2->asd.source = (int)index;
+ csi2->asd.supported_codes = csi2_supported_codes;
+ snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
+ IPU_ISYS_ENTITY_PREFIX " CSI2 %u", index);
+ v4l2_set_subdevdata(&csi2->asd.sd, &csi2->asd);
+
+ ret = v4l2_subdev_init_finalize(&csi2->asd.sd);
+ if (ret) {
+ dev_err(dev, "failed to init v4l2 subdev (%d)\n", ret);
+ goto isys_subdev_cleanup;
+ }
+
+ ret = v4l2_device_register_subdev(&isys->v4l2_dev, &csi2->asd.sd);
+ if (ret) {
+ dev_err(dev, "failed to register v4l2 subdev (%d)\n", ret);
+ goto v4l2_subdev_cleanup;
+ }
+
+ return 0;
+
+v4l2_subdev_cleanup:
+ v4l2_subdev_cleanup(&csi2->asd.sd);
+isys_subdev_cleanup:
+ ipu7_isys_subdev_cleanup(&csi2->asd);
+
+ return ret;
+}
+
+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream)
+{
+ struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct video_device *vdev = csi2->asd.sd.devnode;
+ struct v4l2_event ev = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+ };
+
+ ev.id = stream->vc;
+ ev.u.frame_sync.frame_sequence = atomic_fetch_inc(&stream->sequence);
+ v4l2_event_queue(vdev, &ev);
+
+ dev_dbg(dev, "sof_event::csi2-%i sequence: %i, vc: %d\n",
+ csi2->port, ev.u.frame_sync.frame_sequence, stream->vc);
+}
+
+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream)
+{
+ struct ipu7_isys_csi2 *csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ u32 frame_sequence = atomic_read(&stream->sequence);
+
+ dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
+ csi2->port, frame_sequence);
+}
+
+int ipu7_isys_csi2_get_remote_desc(u32 source_stream,
+ struct ipu7_isys_csi2 *csi2,
+ struct media_entity *source_entity,
+ struct v4l2_mbus_frame_desc_entry *entry,
+ int *nr_queues)
+{
+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct v4l2_mbus_frame_desc desc;
+ struct v4l2_subdev *source;
+ struct media_pad *pad;
+ unsigned int i;
+ int ret;
+
+ source = media_entity_to_v4l2_subdev(source_entity);
+ if (!source)
+ return -EPIPE;
+
+ pad = media_pad_remote_pad_first(&csi2->asd.pad[IPU7_CSI2_PAD_SINK]);
+ if (!pad)
+ return -EPIPE;
+
+ ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
+ if (ret)
+ return ret;
+
+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ dev_err(dev, "Unsupported frame descriptor type\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < desc.num_entries; i++) {
+ if (source_stream == desc.entry[i].stream) {
+ desc_entry = &desc.entry[i];
+ break;
+ }
+ }
+
+ if (!desc_entry) {
+ dev_err(dev, "Failed to find stream %u from remote subdev\n",
+ source_stream);
+ return -EINVAL;
+ }
+
+ if (desc_entry->bus.csi2.vc >= IPU7_NR_OF_CSI2_VC) {
+ dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
+ return -EINVAL;
+ }
+
+ *entry = *desc_entry;
+
+ for (i = 0; i < desc.num_entries; i++) {
+ if (desc_entry->bus.csi2.vc == desc.entry[i].bus.csi2.vc)
+ (*nr_queues)++;
+ }
+
+ return 0;
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.h b/drivers/staging/media/ipu7/ipu7-isys-csi2.h
new file mode 100644
index 000000000000..6c23b80f92a2
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-csi2.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_CSI2_H
+#define IPU7_ISYS_CSI2_H
+
+#include <linux/container_of.h>
+#include <linux/types.h>
+
+#include "ipu7-isys-subdev.h"
+#include "ipu7-isys-video.h"
+
+struct ipu7_isys;
+struct ipu7_isys_csi2_pdata;
+struct ipu7_isys_stream;
+
+#define IPU7_NR_OF_CSI2_VC 16U
+#define INVALID_VC_ID -1
+#define IPU7_NR_OF_CSI2_SINK_PADS 1U
+#define IPU7_CSI2_PAD_SINK 0U
+#define IPU7_NR_OF_CSI2_SRC_PADS 8U
+#define IPU7_CSI2_PAD_SRC 1U
+#define IPU7_NR_OF_CSI2_PADS (IPU7_NR_OF_CSI2_SINK_PADS + \
+ IPU7_NR_OF_CSI2_SRC_PADS)
+
+/*
+ * struct ipu7_isys_csi2
+ *
+ * @nlanes: number of lanes in the receiver
+ */
+struct ipu7_isys_csi2 {
+ struct ipu7_isys_subdev asd;
+ struct ipu7_isys_csi2_pdata *pdata;
+ struct ipu7_isys *isys;
+ struct ipu7_isys_video av[IPU7_NR_OF_CSI2_SRC_PADS];
+
+ void __iomem *base;
+ u32 receiver_errors;
+ u32 legacy_irq_mask;
+ unsigned int nlanes;
+ unsigned int port;
+ unsigned int phy_mode;
+ unsigned int stream_count;
+};
+
+#define ipu7_isys_subdev_to_csi2(__sd) \
+ container_of(__sd, struct ipu7_isys_csi2, asd)
+
+#define to_ipu7_isys_csi2(__asd) container_of(__asd, struct ipu7_isys_csi2, asd)
+
+s64 ipu7_isys_csi2_get_link_freq(struct ipu7_isys_csi2 *csi2);
+int ipu7_isys_csi2_init(struct ipu7_isys_csi2 *csi2, struct ipu7_isys *isys,
+ void __iomem *base, unsigned int index);
+void ipu7_isys_csi2_cleanup(struct ipu7_isys_csi2 *csi2);
+void ipu7_isys_csi2_sof_event_by_stream(struct ipu7_isys_stream *stream);
+void ipu7_isys_csi2_eof_event_by_stream(struct ipu7_isys_stream *stream);
+int ipu7_isys_csi2_get_remote_desc(u32 source_stream,
+ struct ipu7_isys_csi2 *csi2,
+ struct media_entity *source_entity,
+ struct v4l2_mbus_frame_desc_entry *entry,
+ int *nr_queues);
+#endif /* IPU7_ISYS_CSI2_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.c b/drivers/staging/media/ipu7/ipu7-isys-queue.c
new file mode 100644
index 000000000000..7046c29141f8
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.c
@@ -0,0 +1,829 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7-bus.h"
+#include "ipu7-dma.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-isys-video.h"
+#include "ipu7-platform-regs.h"
+
+#define IPU_MAX_FRAME_COUNTER (U8_MAX + 1)
+
+static int ipu7_isys_buf_init(struct vb2_buffer *vb)
+{
+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue);
+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0);
+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+ struct ipu7_isys_video_buffer *ivb =
+ vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+ int ret;
+
+ ret = ipu7_dma_map_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0);
+ if (ret)
+ return ret;
+
+ ivb->dma_addr = sg_dma_address(sg->sgl);
+
+ return 0;
+}
+
+static void ipu7_isys_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct ipu7_isys *isys = vb2_get_drv_priv(vb->vb2_queue);
+ struct sg_table *sg = vb2_dma_sg_plane_desc(vb, 0);
+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+ struct ipu7_isys_video_buffer *ivb =
+ vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+
+ ivb->dma_addr = 0;
+ ipu7_dma_unmap_sgtable(isys->adev, sg, DMA_TO_DEVICE, 0);
+}
+
+static int ipu7_isys_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *num_planes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q);
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ u32 size = av->pix_fmt.sizeimage;
+
+ /* num_planes == 0: we're being called through VIDIOC_REQBUFS */
+ if (!*num_planes) {
+ sizes[0] = size;
+ } else if (sizes[0] < size) {
+ dev_dbg(dev, "%s: queue setup: size %u < %u\n",
+ av->vdev.name, sizes[0], size);
+ return -EINVAL;
+ }
+
+ *num_planes = 1;
+
+ return 0;
+}
+
+static int ipu7_isys_buf_prepare(struct vb2_buffer *vb)
+{
+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ u32 bytesperline = av->pix_fmt.bytesperline;
+ u32 height = av->pix_fmt.height;
+
+ dev_dbg(dev, "buffer: %s: configured size %u, buffer size %lu\n",
+ av->vdev.name, av->pix_fmt.sizeimage, vb2_plane_size(vb, 0));
+
+ if (av->pix_fmt.sizeimage > vb2_plane_size(vb, 0))
+ return -EINVAL;
+
+ dev_dbg(dev, "buffer: %s: bytesperline %u, height %u\n",
+ av->vdev.name, bytesperline, height);
+ vb2_set_plane_payload(vb, 0, bytesperline * height);
+
+ return 0;
+}
+
+/*
+ * Queue a buffer list back to incoming or active queues. The buffers
+ * are removed from the buffer list.
+ */
+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl,
+ unsigned long op_flags,
+ enum vb2_buffer_state state)
+{
+ struct ipu7_isys_buffer *ib, *ib_safe;
+ unsigned long flags;
+ bool first = true;
+
+ if (!bl)
+ return;
+
+ WARN_ON_ONCE(!bl->nbufs);
+ WARN_ON_ONCE(op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE &&
+ op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING);
+
+ list_for_each_entry_safe(ib, ib_safe, &bl->head, head) {
+ struct ipu7_isys_video *av;
+
+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+ struct ipu7_isys_queue *aq =
+ vb2_queue_to_isys_queue(vb->vb2_queue);
+
+ av = ipu7_isys_queue_to_video(aq);
+ spin_lock_irqsave(&aq->lock, flags);
+ list_del(&ib->head);
+ if (op_flags & IPU_ISYS_BUFFER_LIST_FL_ACTIVE)
+ list_add(&ib->head, &aq->active);
+ else if (op_flags & IPU_ISYS_BUFFER_LIST_FL_INCOMING)
+ list_add_tail(&ib->head, &aq->incoming);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ if (op_flags & IPU_ISYS_BUFFER_LIST_FL_SET_STATE)
+ vb2_buffer_done(vb, state);
+
+ if (first) {
+ dev_dbg(&av->isys->adev->auxdev.dev,
+ "queue buf list %p flags %lx, s %d, %d bufs\n",
+ bl, op_flags, state, bl->nbufs);
+ first = false;
+ }
+
+ bl->nbufs--;
+ }
+
+ WARN_ON(bl->nbufs);
+}
+
+/*
+ * flush_firmware_streamon_fail() - Flush in cases where requests may
+ * have been queued to firmware and the *firmware streamon fails for a
+ * reason or another.
+ */
+static void flush_firmware_streamon_fail(struct ipu7_isys_stream *stream)
+{
+ struct ipu7_isys_queue *aq;
+ unsigned long flags;
+
+ lockdep_assert_held(&stream->mutex);
+
+ list_for_each_entry(aq, &stream->queues, node) {
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu7_isys_buffer *ib, *ib_safe;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ list_for_each_entry_safe(ib, ib_safe, &aq->active, head) {
+ struct vb2_buffer *vb =
+ ipu7_isys_buffer_to_vb2_buffer(ib);
+
+ list_del(&ib->head);
+ if (av->streaming) {
+ dev_dbg(dev,
+ "%s: queue buffer %u back to incoming\n",
+ av->vdev.name, vb->index);
+ /* Queue already streaming, return to driver. */
+ list_add(&ib->head, &aq->incoming);
+ continue;
+ }
+ /* Queue not yet streaming, return to user. */
+ dev_dbg(dev, "%s: return %u back to videobuf2\n",
+ av->vdev.name, vb->index);
+ vb2_buffer_done(ipu7_isys_buffer_to_vb2_buffer(ib),
+ VB2_BUF_STATE_QUEUED);
+ }
+ spin_unlock_irqrestore(&aq->lock, flags);
+ }
+}
+
+/*
+ * Attempt obtaining a buffer list from the incoming queues, a list of buffers
+ * that contains one entry from each video buffer queue. If a buffer can't be
+ * obtained from every queue, the buffers are returned back to the queue.
+ */
+static int buffer_list_get(struct ipu7_isys_stream *stream,
+ struct ipu7_isys_buffer_list *bl)
+{
+ unsigned long buf_flag = IPU_ISYS_BUFFER_LIST_FL_INCOMING;
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu7_isys_queue *aq;
+ unsigned long flags;
+
+ bl->nbufs = 0;
+ INIT_LIST_HEAD(&bl->head);
+
+ list_for_each_entry(aq, &stream->queues, node) {
+ struct ipu7_isys_buffer *ib;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ if (list_empty(&aq->incoming)) {
+ spin_unlock_irqrestore(&aq->lock, flags);
+ if (!list_empty(&bl->head))
+ ipu7_isys_buffer_list_queue(bl, buf_flag, 0);
+ return -ENODATA;
+ }
+
+ ib = list_last_entry(&aq->incoming,
+ struct ipu7_isys_buffer, head);
+
+ dev_dbg(dev, "buffer: %s: buffer %u\n",
+ ipu7_isys_queue_to_video(aq)->vdev.name,
+ ipu7_isys_buffer_to_vb2_buffer(ib)->index);
+ list_del(&ib->head);
+ list_add(&ib->head, &bl->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ bl->nbufs++;
+ }
+
+ dev_dbg(dev, "get buffer list %p, %u buffers\n", bl, bl->nbufs);
+
+ return 0;
+}
+
+static void ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
+ struct ipu7_insys_buffset *set)
+{
+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+ struct ipu7_isys_video_buffer *ivb =
+ vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+
+ set->output_pins[aq->fw_output].addr = ivb->dma_addr;
+ set->output_pins[aq->fw_output].user_token = (uintptr_t)set;
+}
+
+/*
+ * Convert a buffer list to a isys fw ABI framebuffer set. The
+ * buffer list is not modified.
+ */
+#define IPU_ISYS_FRAME_NUM_THRESHOLD (30)
+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set,
+ struct ipu7_isys_stream *stream,
+ struct ipu7_isys_buffer_list *bl)
+{
+ struct ipu7_isys_buffer *ib;
+ u32 buf_id;
+
+ WARN_ON(!bl->nbufs);
+
+ set->skip_frame = 0;
+ set->capture_msg_map = IPU_INSYS_FRAME_ENABLE_MSG_SEND_RESP |
+ IPU_INSYS_FRAME_ENABLE_MSG_SEND_IRQ;
+
+ buf_id = atomic_fetch_inc(&stream->buf_id);
+ set->frame_id = buf_id % IPU_MAX_FRAME_COUNTER;
+
+ list_for_each_entry(ib, &bl->head, head) {
+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+
+ ipu7_isys_buf_to_fw_frame_buf_pin(vb, set);
+ }
+}
+
+/* Start streaming for real. The buffer list must be available. */
+static int ipu7_isys_stream_start(struct ipu7_isys_video *av,
+ struct ipu7_isys_buffer_list *bl, bool error)
+{
+ struct ipu7_isys_stream *stream = av->stream;
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu7_isys_buffer_list __bl;
+ int ret;
+
+ mutex_lock(&stream->isys->stream_mutex);
+
+ ret = ipu7_isys_video_set_streaming(av, 1, bl);
+ mutex_unlock(&stream->isys->stream_mutex);
+ if (ret)
+ goto out_requeue;
+
+ stream->streaming = 1;
+
+ bl = &__bl;
+
+ do {
+ struct ipu7_insys_buffset *buf = NULL;
+ struct isys_fw_msgs *msg;
+ enum ipu7_insys_send_type send_type =
+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE;
+
+ ret = buffer_list_get(stream, bl);
+ if (ret < 0)
+ break;
+
+ msg = ipu7_get_fw_msg_buf(stream);
+ if (!msg)
+ return -ENOMEM;
+
+ buf = &msg->fw_msg.frame;
+
+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl);
+
+ ipu7_fw_isys_dump_frame_buff_set(dev, buf,
+ stream->nr_output_pins);
+
+ ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE,
+ 0);
+
+ ret = ipu7_fw_isys_complex_cmd(stream->isys,
+ stream->stream_handle, buf,
+ msg->dma_addr, sizeof(*buf),
+ send_type);
+ } while (!WARN_ON(ret));
+
+ return 0;
+
+out_requeue:
+ if (bl && bl->nbufs)
+ ipu7_isys_buffer_list_queue(bl,
+ IPU_ISYS_BUFFER_LIST_FL_INCOMING |
+ (error ?
+ IPU_ISYS_BUFFER_LIST_FL_SET_STATE :
+ 0), error ? VB2_BUF_STATE_ERROR :
+ VB2_BUF_STATE_QUEUED);
+ flush_firmware_streamon_fail(stream);
+
+ return ret;
+}
+
+static void buf_queue(struct vb2_buffer *vb)
+{
+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+ struct ipu7_isys_video_buffer *ivb =
+ vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+ struct media_pipeline *media_pipe =
+ media_entity_pipeline(&av->vdev.entity);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu7_isys_stream *stream = av->stream;
+ struct ipu7_isys_buffer *ib = &ivb->ib;
+ struct ipu7_insys_buffset *buf = NULL;
+ struct ipu7_isys_buffer_list bl;
+ struct isys_fw_msgs *msg;
+ unsigned long flags;
+ dma_addr_t dma;
+ int ret;
+
+ dev_dbg(dev, "queue buffer %u for %s\n", vb->index, av->vdev.name);
+
+ dma = ivb->dma_addr;
+ dev_dbg(dev, "iova: iova %pad\n", &dma);
+
+ spin_lock_irqsave(&aq->lock, flags);
+ list_add(&ib->head, &aq->incoming);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ if (!media_pipe || !vb->vb2_queue->start_streaming_called) {
+ dev_dbg(dev, "media pipeline is not ready for %s\n",
+ av->vdev.name);
+ return;
+ }
+
+ mutex_lock(&stream->mutex);
+
+ if (stream->nr_streaming != stream->nr_queues) {
+ dev_dbg(dev, "not streaming yet, adding to incoming\n");
+ goto out;
+ }
+
+ /*
+ * We just put one buffer to the incoming list of this queue
+ * (above). Let's see whether all queues in the pipeline would
+ * have a buffer.
+ */
+ ret = buffer_list_get(stream, &bl);
+ if (ret < 0) {
+ dev_dbg(dev, "No buffers available\n");
+ goto out;
+ }
+
+ msg = ipu7_get_fw_msg_buf(stream);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ buf = &msg->fw_msg.frame;
+
+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, &bl);
+
+ ipu7_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins);
+
+ if (!stream->streaming) {
+ ret = ipu7_isys_stream_start(av, &bl, true);
+ if (ret)
+ dev_err(dev, "stream start failed.\n");
+ goto out;
+ }
+
+ /*
+ * We must queue the buffers in the buffer list to the
+ * appropriate video buffer queues BEFORE passing them to the
+ * firmware since we could get a buffer event back before we
+ * have queued them ourselves to the active queue.
+ */
+ ipu7_isys_buffer_list_queue(&bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
+
+ ret = ipu7_fw_isys_complex_cmd(stream->isys, stream->stream_handle,
+ buf, msg->dma_addr, sizeof(*buf),
+ IPU_INSYS_SEND_TYPE_STREAM_CAPTURE);
+ if (ret < 0)
+ dev_err(dev, "send stream capture failed\n");
+
+out:
+ mutex_unlock(&stream->mutex);
+}
+
+static int ipu7_isys_link_fmt_validate(struct ipu7_isys_queue *aq)
+{
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct media_pad *remote_pad =
+ media_pad_remote_pad_first(av->vdev.entity.pads);
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_subdev *sd;
+ u32 r_stream, code;
+ int ret;
+
+ if (!remote_pad)
+ return -ENOTCONN;
+
+ sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+ r_stream = ipu7_isys_get_src_stream_by_src_pad(sd, remote_pad->index);
+
+ ret = ipu7_isys_get_stream_pad_fmt(sd, remote_pad->index, r_stream,
+ &format);
+ if (ret) {
+ dev_dbg(dev, "failed to get %s: pad %d, stream:%d format\n",
+ sd->entity.name, remote_pad->index, r_stream);
+ return ret;
+ }
+
+ if (format.width != av->pix_fmt.width ||
+ format.height != av->pix_fmt.height) {
+ dev_dbg(dev, "wrong width or height %ux%u (%ux%u expected)\n",
+ av->pix_fmt.width, av->pix_fmt.height, format.width,
+ format.height);
+ return -EINVAL;
+ }
+
+ code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code;
+ if (format.code != code) {
+ dev_dbg(dev, "wrong mbus code 0x%8.8x (0x%8.8x expected)\n",
+ code, format.code);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void return_buffers(struct ipu7_isys_queue *aq,
+ enum vb2_buffer_state state)
+{
+ struct ipu7_isys_buffer *ib;
+ struct vb2_buffer *vb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&aq->lock, flags);
+ /*
+ * Something went wrong (FW crash / HW hang / not all buffers
+ * returned from isys) if there are still buffers queued in active
+ * queue. We have to clean up places a bit.
+ */
+ while (!list_empty(&aq->active)) {
+ ib = list_last_entry(&aq->active, struct ipu7_isys_buffer,
+ head);
+ vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+
+ list_del(&ib->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ vb2_buffer_done(vb, state);
+
+ spin_lock_irqsave(&aq->lock, flags);
+ }
+
+ while (!list_empty(&aq->incoming)) {
+ ib = list_last_entry(&aq->incoming, struct ipu7_isys_buffer,
+ head);
+ vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+ list_del(&ib->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ vb2_buffer_done(vb, state);
+
+ spin_lock_irqsave(&aq->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&aq->lock, flags);
+}
+
+static void ipu7_isys_stream_cleanup(struct ipu7_isys_video *av)
+{
+ video_device_pipeline_stop(&av->vdev);
+ ipu7_isys_put_stream(av->stream);
+ av->stream = NULL;
+}
+
+static int start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q);
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ const struct ipu7_isys_pixelformat *pfmt =
+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+ struct ipu7_isys_buffer_list __bl, *bl = NULL;
+ struct ipu7_isys_stream *stream;
+ struct media_entity *source_entity = NULL;
+ int nr_queues, ret;
+
+ dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
+ av->vdev.name, av->pix_fmt.width, av->pix_fmt.height,
+ pfmt->css_pixelformat);
+
+ ret = ipu7_isys_setup_video(av, &source_entity, &nr_queues);
+ if (ret < 0) {
+ dev_dbg(dev, "failed to setup video\n");
+ goto out_return_buffers;
+ }
+
+ ret = ipu7_isys_link_fmt_validate(aq);
+ if (ret) {
+ dev_dbg(dev,
+ "%s: link format validation failed (%d)\n",
+ av->vdev.name, ret);
+ goto out_pipeline_stop;
+ }
+
+ stream = av->stream;
+ mutex_lock(&stream->mutex);
+ if (!stream->nr_streaming) {
+ ret = ipu7_isys_video_prepare_stream(av, source_entity,
+ nr_queues);
+ if (ret) {
+ mutex_unlock(&stream->mutex);
+ goto out_pipeline_stop;
+ }
+ }
+
+ stream->nr_streaming++;
+ dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming,
+ stream->nr_queues);
+
+ list_add(&aq->node, &stream->queues);
+
+ if (stream->nr_streaming != stream->nr_queues)
+ goto out;
+
+ bl = &__bl;
+ ret = buffer_list_get(stream, bl);
+ if (ret < 0) {
+ dev_warn(dev, "no buffer available, DRIVER BUG?\n");
+ goto out;
+ }
+
+ ret = ipu7_isys_fw_open(av->isys);
+ if (ret)
+ goto out_stream_start;
+
+ ipu7_isys_setup_hw(av->isys);
+
+ ret = ipu7_isys_stream_start(av, bl, false);
+ if (ret)
+ goto out_isys_fw_close;
+
+out:
+ mutex_unlock(&stream->mutex);
+
+ return 0;
+
+out_isys_fw_close:
+ ipu7_isys_fw_close(av->isys);
+
+out_stream_start:
+ list_del(&aq->node);
+ stream->nr_streaming--;
+ mutex_unlock(&stream->mutex);
+
+out_pipeline_stop:
+ ipu7_isys_stream_cleanup(av);
+
+out_return_buffers:
+ return_buffers(aq, VB2_BUF_STATE_QUEUED);
+
+ return ret;
+}
+
+static void stop_streaming(struct vb2_queue *q)
+{
+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(q);
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct ipu7_isys_stream *stream = av->stream;
+
+ mutex_lock(&stream->mutex);
+ mutex_lock(&av->isys->stream_mutex);
+ if (stream->nr_streaming == stream->nr_queues && stream->streaming)
+ ipu7_isys_video_set_streaming(av, 0, NULL);
+ mutex_unlock(&av->isys->stream_mutex);
+
+ stream->nr_streaming--;
+ list_del(&aq->node);
+ stream->streaming = 0;
+
+ mutex_unlock(&stream->mutex);
+
+ ipu7_isys_stream_cleanup(av);
+
+ return_buffers(aq, VB2_BUF_STATE_ERROR);
+
+ ipu7_isys_fw_close(av->isys);
+}
+
+static unsigned int
+get_sof_sequence_by_timestamp(struct ipu7_isys_stream *stream, u64 time)
+{
+ struct ipu7_isys *isys = stream->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ unsigned int i;
+
+ /*
+ * The timestamp is invalid as no TSC in some FPGA platform,
+ * so get the sequence from pipeline directly in this case.
+ */
+ if (time == 0)
+ return atomic_read(&stream->sequence) - 1;
+
+ for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++)
+ if (time == stream->seq[i].timestamp) {
+ dev_dbg(dev, "SOF: using seq nr %u for ts %llu\n",
+ stream->seq[i].sequence, time);
+ return stream->seq[i].sequence;
+ }
+
+ dev_dbg(dev, "SOF: looking for %llu\n", time);
+ for (i = 0; i < IPU_ISYS_MAX_PARALLEL_SOF; i++)
+ dev_dbg(dev, "SOF: sequence %u, timestamp value %llu\n",
+ stream->seq[i].sequence, stream->seq[i].timestamp);
+ dev_dbg(dev, "SOF sequence number not found\n");
+
+ return atomic_read(&stream->sequence) - 1;
+}
+
+static u64 get_sof_ns_delta(struct ipu7_isys_video *av, u64 time)
+{
+ struct ipu7_bus_device *adev = av->isys->adev;
+ struct ipu7_device *isp = adev->isp;
+ u64 delta, tsc_now;
+
+ ipu_buttress_tsc_read(isp, &tsc_now);
+ if (!tsc_now)
+ return 0;
+
+ delta = tsc_now - time;
+
+ return ipu_buttress_tsc_ticks_to_ns(delta, isp);
+}
+
+static void ipu7_isys_buf_calc_sequence_time(struct ipu7_isys_buffer *ib,
+ u64 time)
+{
+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+ struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+ struct ipu7_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu7_isys_stream *stream = av->stream;
+ u64 ns;
+ u32 sequence;
+
+ ns = ktime_get_ns() - get_sof_ns_delta(av, time);
+ sequence = get_sof_sequence_by_timestamp(stream, time);
+
+ vbuf->vb2_buf.timestamp = ns;
+ vbuf->sequence = sequence;
+
+ dev_dbg(dev, "buf: %s: buffer done, CPU-timestamp:%lld, sequence:%d\n",
+ av->vdev.name, ktime_get_ns(), sequence);
+ dev_dbg(dev, "index:%d, vbuf timestamp:%lld\n", vb->index,
+ vbuf->vb2_buf.timestamp);
+}
+
+static void ipu7_isys_queue_buf_done(struct ipu7_isys_buffer *ib)
+{
+ struct vb2_buffer *vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+
+ if (atomic_read(&ib->str2mmio_flag)) {
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ /*
+ * Operation on buffer is ended with error and will be reported
+ * to the userspace when it is de-queued
+ */
+ atomic_set(&ib->str2mmio_flag, 0);
+ } else {
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ }
+}
+
+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream,
+ struct ipu7_insys_resp *info)
+{
+ struct ipu7_isys_queue *aq = stream->output_pins[info->pin_id].aq;
+ u64 time = ((u64)info->timestamp[1] << 32 | info->timestamp[0]);
+ struct ipu7_isys *isys = stream->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct ipu7_isys_buffer *ib;
+ struct vb2_buffer *vb;
+ unsigned long flags;
+ bool first = true;
+ struct vb2_v4l2_buffer *buf;
+
+ dev_dbg(dev, "buffer: %s: received buffer %8.8x %d\n",
+ ipu7_isys_queue_to_video(aq)->vdev.name, info->pin.addr,
+ info->frame_id);
+
+ spin_lock_irqsave(&aq->lock, flags);
+ if (list_empty(&aq->active)) {
+ spin_unlock_irqrestore(&aq->lock, flags);
+ dev_err(dev, "active queue empty\n");
+ return;
+ }
+
+ list_for_each_entry_reverse(ib, &aq->active, head) {
+ struct ipu7_isys_video_buffer *ivb;
+ struct vb2_v4l2_buffer *vvb;
+ dma_addr_t addr;
+
+ vb = ipu7_isys_buffer_to_vb2_buffer(ib);
+ vvb = to_vb2_v4l2_buffer(vb);
+ ivb = vb2_buffer_to_ipu7_isys_video_buffer(vvb);
+ addr = ivb->dma_addr;
+
+ if (info->pin.addr != addr) {
+ if (first)
+ dev_err(dev, "Unexpected buffer address %pad\n",
+ &addr);
+
+ first = false;
+ continue;
+ }
+
+ dev_dbg(dev, "buffer: found buffer %pad\n", &addr);
+
+ buf = to_vb2_v4l2_buffer(vb);
+ buf->field = V4L2_FIELD_NONE;
+
+ list_del(&ib->head);
+ spin_unlock_irqrestore(&aq->lock, flags);
+
+ ipu7_isys_buf_calc_sequence_time(ib, time);
+
+ ipu7_isys_queue_buf_done(ib);
+
+ return;
+ }
+
+ dev_err(dev, "Failed to find a matching video buffer\n");
+
+ spin_unlock_irqrestore(&aq->lock, flags);
+}
+
+static const struct vb2_ops ipu7_isys_queue_ops = {
+ .queue_setup = ipu7_isys_queue_setup,
+ .buf_init = ipu7_isys_buf_init,
+ .buf_prepare = ipu7_isys_buf_prepare,
+ .buf_cleanup = ipu7_isys_buf_cleanup,
+ .start_streaming = start_streaming,
+ .stop_streaming = stop_streaming,
+ .buf_queue = buf_queue,
+};
+
+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq)
+{
+ struct ipu7_isys *isys = ipu7_isys_queue_to_video(aq)->isys;
+ struct ipu7_isys_video *av = ipu7_isys_queue_to_video(aq);
+ struct ipu7_bus_device *adev = isys->adev;
+ int ret;
+
+ if (!aq->vbq.io_modes)
+ aq->vbq.io_modes = VB2_MMAP | VB2_DMABUF;
+
+ aq->vbq.drv_priv = isys;
+ aq->vbq.ops = &ipu7_isys_queue_ops;
+ aq->vbq.lock = &av->mutex;
+ aq->vbq.mem_ops = &vb2_dma_sg_memops;
+ aq->vbq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ aq->vbq.min_queued_buffers = 1;
+ aq->vbq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+
+ ret = vb2_queue_init(&aq->vbq);
+ if (ret)
+ return ret;
+
+ aq->dev = &adev->auxdev.dev;
+ aq->vbq.dev = &adev->isp->pdev->dev;
+ spin_lock_init(&aq->lock);
+ INIT_LIST_HEAD(&aq->active);
+ INIT_LIST_HEAD(&aq->incoming);
+
+ return 0;
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-queue.h b/drivers/staging/media/ipu7/ipu7-isys-queue.h
new file mode 100644
index 000000000000..0cb08a38f756
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-queue.h
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_QUEUE_H
+#define IPU7_ISYS_QUEUE_H
+
+#include <linux/atomic.h>
+#include <linux/container_of.h>
+#include <linux/list.h>
+#include <linux/spinlock_types.h>
+
+#include <media/videobuf2-v4l2.h>
+
+struct device;
+struct ipu7_isys_stream;
+struct ipu7_insys_resp;
+struct ipu7_insys_buffset;
+
+struct ipu7_isys_queue {
+ struct vb2_queue vbq;
+ struct list_head node;
+ struct device *dev;
+ spinlock_t lock;
+ struct list_head active;
+ struct list_head incoming;
+ unsigned int fw_output;
+};
+
+struct ipu7_isys_buffer {
+ struct list_head head;
+ atomic_t str2mmio_flag;
+};
+
+struct ipu7_isys_video_buffer {
+ struct vb2_v4l2_buffer vb_v4l2;
+ struct ipu7_isys_buffer ib;
+ dma_addr_t dma_addr;
+};
+
+#define IPU_ISYS_BUFFER_LIST_FL_INCOMING BIT(0)
+#define IPU_ISYS_BUFFER_LIST_FL_ACTIVE BIT(1)
+#define IPU_ISYS_BUFFER_LIST_FL_SET_STATE BIT(2)
+
+struct ipu7_isys_buffer_list {
+ struct list_head head;
+ unsigned int nbufs;
+};
+
+#define vb2_queue_to_isys_queue(__vb2) \
+ container_of(__vb2, struct ipu7_isys_queue, vbq)
+
+#define ipu7_isys_to_isys_video_buffer(__ib) \
+ container_of(__ib, struct ipu7_isys_video_buffer, ib)
+
+#define vb2_buffer_to_ipu7_isys_video_buffer(__vvb) \
+ container_of(__vvb, struct ipu7_isys_video_buffer, vb_v4l2)
+
+#define ipu7_isys_buffer_to_vb2_buffer(__ib) \
+ (&ipu7_isys_to_isys_video_buffer(__ib)->vb_v4l2.vb2_buf)
+
+void ipu7_isys_buffer_list_queue(struct ipu7_isys_buffer_list *bl,
+ unsigned long op_flags,
+ enum vb2_buffer_state state);
+void ipu7_isys_buffer_to_fw_frame_buff(struct ipu7_insys_buffset *set,
+ struct ipu7_isys_stream *stream,
+ struct ipu7_isys_buffer_list *bl);
+void ipu7_isys_queue_buf_ready(struct ipu7_isys_stream *stream,
+ struct ipu7_insys_resp *info);
+int ipu7_isys_queue_init(struct ipu7_isys_queue *aq);
+#endif /* IPU7_ISYS_QUEUE_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.c b/drivers/staging/media/ipu7/ipu7-isys-subdev.c
new file mode 100644
index 000000000000..98b6ef6a2f21
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-subdev.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/minmax.h>
+#include <linux/types.h>
+
+#include <media/media-entity.h>
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+#include <uapi/linux/media-bus-format.h>
+
+#include "ipu7-bus.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-subdev.h"
+
+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code)
+{
+ switch (code) {
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ return MIPI_CSI2_DT_RGB565;
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ return MIPI_CSI2_DT_RGB888;
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ return MIPI_CSI2_DT_YUV422_10B;
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ return MIPI_CSI2_DT_YUV422_8B;
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ return MIPI_CSI2_DT_RAW12;
+ case MEDIA_BUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ return MIPI_CSI2_DT_RAW10;
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ return MIPI_CSI2_DT_RAW8;
+ default:
+ WARN_ON(1);
+ return 0xff;
+ }
+}
+
+bool ipu7_isys_is_bayer_format(u32 code)
+{
+ switch (ipu7_isys_mbus_code_to_mipi(code)) {
+ case MIPI_CSI2_DT_RAW8:
+ case MIPI_CSI2_DT_RAW10:
+ case MIPI_CSI2_DT_RAW12:
+ case MIPI_CSI2_DT_RAW14:
+ case MIPI_CSI2_DT_RAW16:
+ case MIPI_CSI2_DT_RAW20:
+ case MIPI_CSI2_DT_RAW24:
+ case MIPI_CSI2_DT_RAW28:
+ return true;
+ default:
+ return false;
+ }
+}
+
+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y)
+{
+ static const u32 code_map[] = {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(code_map); i++)
+ if (code_map[i] == code)
+ break;
+
+ if (WARN_ON(i == ARRAY_SIZE(code_map)))
+ return code;
+
+ return code_map[i ^ ((((u32)y & 1U) << 1U) | ((u32)x & 1U))];
+}
+
+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+ u32 code = asd->supported_codes[0];
+ struct v4l2_mbus_framefmt *fmt;
+ u32 other_pad, other_stream;
+ struct v4l2_rect *crop;
+ unsigned int i;
+ int ret;
+
+ /* No transcoding, source and sink formats must match. */
+ if ((sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SOURCE) &&
+ sd->entity.num_pads > 1)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ format->format.width = clamp(format->format.width, IPU_ISYS_MIN_WIDTH,
+ IPU_ISYS_MAX_WIDTH);
+ format->format.height = clamp(format->format.height,
+ IPU_ISYS_MIN_HEIGHT,
+ IPU_ISYS_MAX_HEIGHT);
+
+ for (i = 0; asd->supported_codes[i]; i++) {
+ if (asd->supported_codes[i] == format->format.code) {
+ code = asd->supported_codes[i];
+ break;
+ }
+ }
+ format->format.code = code;
+ format->format.field = V4L2_FIELD_NONE;
+
+ /* Store the format and propagate it to the source pad. */
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ if (!(sd->entity.pads[format->pad].flags & MEDIA_PAD_FL_SINK))
+ return 0;
+
+ /* propagate format to following source pad */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ ret = v4l2_subdev_routing_find_opposite_end(&state->routing,
+ format->pad,
+ format->stream,
+ &other_pad,
+ &other_stream);
+ if (ret)
+ return -EINVAL;
+
+ crop = v4l2_subdev_state_get_crop(state, other_pad, other_stream);
+ /* reset crop */
+ crop->left = 0;
+ crop->top = 0;
+ crop->width = fmt->width;
+ crop->height = fmt->height;
+
+ return 0;
+}
+
+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct ipu7_isys_subdev *asd = to_ipu7_isys_subdev(sd);
+ const u32 *supported_codes = asd->supported_codes;
+ u32 index;
+
+ for (index = 0; supported_codes[index]; index++) {
+ if (index == code->index) {
+ code->code = supported_codes[index];
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ static const struct v4l2_mbus_framefmt fmt = {
+ .width = 4096,
+ .height = 3072,
+ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
+ .field = V4L2_FIELD_NONE,
+ };
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &fmt);
+}
+
+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ struct v4l2_mbus_framefmt *format)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (!sd || !format)
+ return -EINVAL;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ fmt = v4l2_subdev_state_get_format(state, pad, stream);
+ if (fmt)
+ *format = *fmt;
+ v4l2_subdev_unlock_state(state);
+
+ return fmt ? 0 : -EINVAL;
+}
+
+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *routes;
+ u32 source_stream = 0;
+ unsigned int i;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ if (!state)
+ return 0;
+
+ routes = state->routing.routes;
+ for (i = 0; i < state->routing.num_routes; i++) {
+ if (routes[i].source_pad == pad) {
+ source_stream = routes[i].source_stream;
+ break;
+ }
+ }
+
+ v4l2_subdev_unlock_state(state);
+
+ return source_stream;
+}
+
+static int ipu7_isys_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route route = {
+ .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 = &route,
+ };
+
+ return subdev_set_routing(sd, state, &routing);
+}
+
+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ return subdev_set_routing(sd, state, routing);
+}
+
+static const struct v4l2_subdev_internal_ops ipu7_isys_subdev_internal_ops = {
+ .init_state = ipu7_isys_subdev_init_state,
+};
+
+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd,
+ const struct v4l2_subdev_ops *ops,
+ unsigned int nr_ctrls,
+ unsigned int num_sink_pads,
+ unsigned int num_source_pads)
+{
+ unsigned int num_pads = num_sink_pads + num_source_pads;
+ unsigned int i;
+ int ret;
+
+ v4l2_subdev_init(&asd->sd, ops);
+
+ asd->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS |
+ V4L2_SUBDEV_FL_STREAMS;
+ asd->sd.owner = THIS_MODULE;
+ asd->sd.dev = &asd->isys->adev->auxdev.dev;
+ asd->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ asd->sd.internal_ops = &ipu7_isys_subdev_internal_ops;
+
+ asd->pad = devm_kcalloc(&asd->isys->adev->auxdev.dev, num_pads,
+ sizeof(*asd->pad), GFP_KERNEL);
+ if (!asd->pad)
+ return -ENOMEM;
+
+ for (i = 0; i < num_sink_pads; i++)
+ asd->pad[i].flags = MEDIA_PAD_FL_SINK |
+ MEDIA_PAD_FL_MUST_CONNECT;
+
+ for (i = num_sink_pads; i < num_pads; i++)
+ asd->pad[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&asd->sd.entity, num_pads, asd->pad);
+ if (ret) {
+ pr_err("isys subdev init failed %d.\n", ret);
+ return ret;
+ }
+
+ if (asd->ctrl_init) {
+ ret = v4l2_ctrl_handler_init(&asd->ctrl_handler, nr_ctrls);
+ if (ret)
+ goto out_media_entity_cleanup;
+
+ asd->ctrl_init(&asd->sd);
+ if (asd->ctrl_handler.error) {
+ ret = asd->ctrl_handler.error;
+ goto out_v4l2_ctrl_handler_free;
+ }
+
+ asd->sd.ctrl_handler = &asd->ctrl_handler;
+ }
+
+ asd->source = -1;
+
+ return 0;
+
+out_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&asd->ctrl_handler);
+
+out_media_entity_cleanup:
+ media_entity_cleanup(&asd->sd.entity);
+
+ return ret;
+}
+
+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd)
+{
+ media_entity_cleanup(&asd->sd.entity);
+ v4l2_ctrl_handler_free(&asd->ctrl_handler);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.h b/drivers/staging/media/ipu7/ipu7-isys-subdev.h
new file mode 100644
index 000000000000..1057ec39ae39
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-subdev.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_SUBDEV_H
+#define IPU7_ISYS_SUBDEV_H
+
+#include <linux/container_of.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+struct ipu7_isys;
+
+struct ipu7_isys_subdev {
+ struct v4l2_subdev sd;
+ struct ipu7_isys *isys;
+ u32 const *supported_codes;
+ struct media_pad *pad;
+ struct v4l2_ctrl_handler ctrl_handler;
+ void (*ctrl_init)(struct v4l2_subdev *sd);
+ int source; /* SSI stream source; -1 if unset */
+};
+
+#define to_ipu7_isys_subdev(__sd) \
+ container_of(__sd, struct ipu7_isys_subdev, sd)
+unsigned int ipu7_isys_mbus_code_to_mipi(u32 code);
+bool ipu7_isys_is_bayer_format(u32 code);
+u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y);
+
+int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format);
+int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum
+ *code);
+u32 ipu7_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
+int ipu7_isys_get_stream_pad_fmt(struct v4l2_subdev *sd, u32 pad, u32 stream,
+ struct v4l2_mbus_framefmt *format);
+int ipu7_isys_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing);
+int ipu7_isys_subdev_init(struct ipu7_isys_subdev *asd,
+ const struct v4l2_subdev_ops *ops,
+ unsigned int nr_ctrls,
+ unsigned int num_sink_pads,
+ unsigned int num_source_pads);
+void ipu7_isys_subdev_cleanup(struct ipu7_isys_subdev *asd);
+#endif /* IPU7_ISYS_SUBDEV_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.c b/drivers/staging/media/ipu7/ipu7-isys-video.c
new file mode 100644
index 000000000000..8756da3a8fb0
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-video.c
@@ -0,0 +1,1112 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/align.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress-regs.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-video.h"
+#include "ipu7-platform-regs.h"
+
+const struct ipu7_isys_pixelformat ipu7_isys_pfmts[] = {
+ {V4L2_PIX_FMT_SBGGR12, 16, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SGBRG12, 16, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SGRBG12, 16, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SRGGB12, 16, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SBGGR10, 16, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SGBRG10, 16, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SGRBG10, 16, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SRGGB10, 16, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW16},
+ {V4L2_PIX_FMT_SBGGR8, 8, 8, MEDIA_BUS_FMT_SBGGR8_1X8,
+ IPU_INSYS_FRAME_FORMAT_RAW8},
+ {V4L2_PIX_FMT_SGBRG8, 8, 8, MEDIA_BUS_FMT_SGBRG8_1X8,
+ IPU_INSYS_FRAME_FORMAT_RAW8},
+ {V4L2_PIX_FMT_SGRBG8, 8, 8, MEDIA_BUS_FMT_SGRBG8_1X8,
+ IPU_INSYS_FRAME_FORMAT_RAW8},
+ {V4L2_PIX_FMT_SRGGB8, 8, 8, MEDIA_BUS_FMT_SRGGB8_1X8,
+ IPU_INSYS_FRAME_FORMAT_RAW8},
+ {V4L2_PIX_FMT_SBGGR12P, 12, 12, MEDIA_BUS_FMT_SBGGR12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW12},
+ {V4L2_PIX_FMT_SGBRG12P, 12, 12, MEDIA_BUS_FMT_SGBRG12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW12},
+ {V4L2_PIX_FMT_SGRBG12P, 12, 12, MEDIA_BUS_FMT_SGRBG12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW12},
+ {V4L2_PIX_FMT_SRGGB12P, 12, 12, MEDIA_BUS_FMT_SRGGB12_1X12,
+ IPU_INSYS_FRAME_FORMAT_RAW12},
+ {V4L2_PIX_FMT_SBGGR10P, 10, 10, MEDIA_BUS_FMT_SBGGR10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW10},
+ {V4L2_PIX_FMT_SGBRG10P, 10, 10, MEDIA_BUS_FMT_SGBRG10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW10},
+ {V4L2_PIX_FMT_SGRBG10P, 10, 10, MEDIA_BUS_FMT_SGRBG10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW10},
+ {V4L2_PIX_FMT_SRGGB10P, 10, 10, MEDIA_BUS_FMT_SRGGB10_1X10,
+ IPU_INSYS_FRAME_FORMAT_RAW10},
+ {V4L2_PIX_FMT_UYVY, 16, 16, MEDIA_BUS_FMT_UYVY8_1X16,
+ IPU_INSYS_FRAME_FORMAT_UYVY},
+ {V4L2_PIX_FMT_YUYV, 16, 16, MEDIA_BUS_FMT_YUYV8_1X16,
+ IPU_INSYS_FRAME_FORMAT_YUYV},
+ {V4L2_PIX_FMT_RGB565, 16, 16, MEDIA_BUS_FMT_RGB565_1X16,
+ IPU_INSYS_FRAME_FORMAT_RGB565},
+ {V4L2_PIX_FMT_BGR24, 24, 24, MEDIA_BUS_FMT_RGB888_1X24,
+ IPU_INSYS_FRAME_FORMAT_RGBA888},
+};
+
+static int video_open(struct file *file)
+{
+ return v4l2_fh_open(file);
+}
+
+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) {
+ const struct ipu7_isys_pixelformat *pfmt = &ipu7_isys_pfmts[i];
+
+ if (pfmt->pixelformat == pixelformat)
+ return pfmt;
+ }
+
+ return &ipu7_isys_pfmts[0];
+}
+
+static int ipu7_isys_vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct ipu7_isys_video *av = video_drvdata(file);
+
+ strscpy(cap->driver, IPU_ISYS_NAME, sizeof(cap->driver));
+ strscpy(cap->card, av->isys->media_dev.model, sizeof(cap->card));
+
+ return 0;
+}
+
+static int ipu7_isys_vidioc_enum_fmt(struct file *file, void *fh,
+ struct v4l2_fmtdesc *f)
+{
+ unsigned int i, num_found;
+
+ for (i = 0, num_found = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) {
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ continue;
+
+ if (f->mbus_code && f->mbus_code != ipu7_isys_pfmts[i].code)
+ continue;
+
+ if (num_found < f->index) {
+ num_found++;
+ continue;
+ }
+
+ f->flags = 0;
+ f->pixelformat = ipu7_isys_pfmts[i].pixelformat;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ipu7_isys_vidioc_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ unsigned int i;
+
+ if (fsize->index > 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(ipu7_isys_pfmts); i++) {
+ if (fsize->pixel_format != ipu7_isys_pfmts[i].pixelformat)
+ continue;
+
+ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+ fsize->stepwise.min_width = IPU_ISYS_MIN_WIDTH;
+ fsize->stepwise.max_width = IPU_ISYS_MAX_WIDTH;
+ fsize->stepwise.min_height = IPU_ISYS_MIN_HEIGHT;
+ fsize->stepwise.max_height = IPU_ISYS_MAX_HEIGHT;
+ fsize->stepwise.step_width = 2;
+ fsize->stepwise.step_height = 2;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int ipu7_isys_vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu7_isys_video *av = video_drvdata(file);
+
+ f->fmt.pix = av->pix_fmt;
+
+ return 0;
+}
+
+static void ipu7_isys_try_fmt_cap(struct ipu7_isys_video *av, u32 type,
+ u32 *format, u32 *width, u32 *height,
+ u32 *bytesperline, u32 *sizeimage)
+{
+ const struct ipu7_isys_pixelformat *pfmt =
+ ipu7_isys_get_isys_format(*format);
+
+ *format = pfmt->pixelformat;
+ *width = clamp(*width, IPU_ISYS_MIN_WIDTH, IPU_ISYS_MAX_WIDTH);
+ *height = clamp(*height, IPU_ISYS_MIN_HEIGHT, IPU_ISYS_MAX_HEIGHT);
+
+ if (pfmt->bpp != pfmt->bpp_packed)
+ *bytesperline = *width * DIV_ROUND_UP(pfmt->bpp, BITS_PER_BYTE);
+ else
+ *bytesperline = DIV_ROUND_UP(*width * pfmt->bpp, BITS_PER_BYTE);
+
+ *bytesperline = ALIGN(*bytesperline, 64U);
+
+ /*
+ * (height + 1) * bytesperline due to a hardware issue: the DMA unit
+ * is a power of two, and a line should be transferred as few units
+ * as possible. The result is that up to line length more data than
+ * the image size may be transferred to memory after the image.
+ * Another limitation is the GDA allocation unit size. For low
+ * resolution it gives a bigger number. Use larger one to avoid
+ * memory corruption.
+ */
+ *sizeimage = *bytesperline * *height +
+ max(*bytesperline,
+ av->isys->pdata->ipdata->isys_dma_overshoot);
+}
+
+static void __ipu_isys_vidioc_try_fmt_vid_cap(struct ipu7_isys_video *av,
+ struct v4l2_format *f)
+{
+ ipu7_isys_try_fmt_cap(av, f->type, &f->fmt.pix.pixelformat,
+ &f->fmt.pix.width, &f->fmt.pix.height,
+ &f->fmt.pix.bytesperline, &f->fmt.pix.sizeimage);
+
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_RAW;
+ f->fmt.pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ f->fmt.pix.quantization = V4L2_QUANTIZATION_DEFAULT;
+ f->fmt.pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+static int ipu7_isys_vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu7_isys_video *av = video_drvdata(file);
+
+ if (vb2_is_busy(&av->aq.vbq))
+ return -EBUSY;
+
+ __ipu_isys_vidioc_try_fmt_vid_cap(av, f);
+
+ return 0;
+}
+
+static int ipu7_isys_vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+{
+ struct ipu7_isys_video *av = video_drvdata(file);
+
+ ipu7_isys_vidioc_try_fmt_vid_cap(file, fh, f);
+ av->pix_fmt = f->fmt.pix;
+
+ return 0;
+}
+
+static int ipu7_isys_vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct ipu7_isys_video *av = video_drvdata(file);
+ int ret;
+
+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->type);
+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->type);
+
+ ret = vb2_queue_change_type(&av->aq.vbq, p->type);
+ if (ret)
+ return ret;
+
+ return vb2_ioctl_reqbufs(file, priv, p);
+}
+
+static int ipu7_isys_vidioc_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *p)
+{
+ struct ipu7_isys_video *av = video_drvdata(file);
+ int ret;
+
+ av->aq.vbq.is_multiplanar = V4L2_TYPE_IS_MULTIPLANAR(p->format.type);
+ av->aq.vbq.is_output = V4L2_TYPE_IS_OUTPUT(p->format.type);
+
+ ret = vb2_queue_change_type(&av->aq.vbq, p->format.type);
+ if (ret)
+ return ret;
+
+ return vb2_ioctl_create_bufs(file, priv, p);
+}
+
+static int link_validate(struct media_link *link)
+{
+ struct ipu7_isys_video *av =
+ container_of(link->sink, struct ipu7_isys_video, pad);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct v4l2_subdev_state *s_state;
+ struct v4l2_mbus_framefmt *s_fmt;
+ struct v4l2_subdev *s_sd;
+ struct media_pad *s_pad;
+ u32 s_stream, code;
+ int ret = -EPIPE;
+
+ if (!link->source->entity)
+ return ret;
+
+ s_sd = media_entity_to_v4l2_subdev(link->source->entity);
+ s_state = v4l2_subdev_get_unlocked_active_state(s_sd);
+ if (!s_state)
+ return ret;
+
+ dev_dbg(dev, "validating link \"%s\":%u -> \"%s\"\n",
+ link->source->entity->name, link->source->index,
+ link->sink->entity->name);
+
+ s_pad = media_pad_remote_pad_first(&av->pad);
+ s_stream = ipu7_isys_get_src_stream_by_src_pad(s_sd, s_pad->index);
+
+ v4l2_subdev_lock_state(s_state);
+
+ s_fmt = v4l2_subdev_state_get_format(s_state, s_pad->index, s_stream);
+ if (!s_fmt) {
+ dev_err(dev, "failed to get source pad format\n");
+ goto unlock;
+ }
+
+ code = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat)->code;
+
+ if (s_fmt->width != av->pix_fmt.width ||
+ s_fmt->height != av->pix_fmt.height || s_fmt->code != code) {
+ dev_dbg(dev, "format mismatch %dx%d,%x != %dx%d,%x\n",
+ s_fmt->width, s_fmt->height, s_fmt->code,
+ av->pix_fmt.width, av->pix_fmt.height, code);
+ goto unlock;
+ }
+
+ v4l2_subdev_unlock_state(s_state);
+
+ return 0;
+unlock:
+ v4l2_subdev_unlock_state(s_state);
+
+ return ret;
+}
+
+static void get_stream_opened(struct ipu7_isys_video *av)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&av->isys->streams_lock, flags);
+ av->isys->stream_opened++;
+ spin_unlock_irqrestore(&av->isys->streams_lock, flags);
+}
+
+static void put_stream_opened(struct ipu7_isys_video *av)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&av->isys->streams_lock, flags);
+ av->isys->stream_opened--;
+ spin_unlock_irqrestore(&av->isys->streams_lock, flags);
+}
+
+static int ipu7_isys_fw_pin_cfg(struct ipu7_isys_video *av,
+ struct ipu7_insys_stream_cfg *cfg)
+{
+ struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ struct ipu7_isys_stream *stream = av->stream;
+ const struct ipu7_isys_pixelformat *pfmt =
+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+ struct ipu7_insys_output_pin *output_pin;
+ struct ipu7_insys_input_pin *input_pin;
+ int input_pins = cfg->nof_input_pins++;
+ struct ipu7_isys_queue *aq = &av->aq;
+ struct ipu7_isys *isys = av->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct v4l2_mbus_framefmt fmt;
+ int output_pins;
+ u32 src_stream;
+ int ret;
+
+ src_stream = ipu7_isys_get_src_stream_by_src_pad(sd, src_pad->index);
+ ret = ipu7_isys_get_stream_pad_fmt(sd, src_pad->index, src_stream,
+ &fmt);
+ if (ret < 0) {
+ dev_err(dev, "can't get stream format (%d)\n", ret);
+ return ret;
+ }
+
+ input_pin = &cfg->input_pins[input_pins];
+ input_pin->input_res.width = fmt.width;
+ input_pin->input_res.height = fmt.height;
+ input_pin->dt = av->dt;
+ input_pin->disable_mipi_unpacking = 0;
+ pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+ if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE)
+ input_pin->disable_mipi_unpacking = 1;
+ input_pin->mapped_dt = N_IPU_INSYS_MIPI_DATA_TYPE;
+ input_pin->dt_rename_mode = IPU_INSYS_MIPI_DT_NO_RENAME;
+ /* if enable polling isys interrupt, the follow values maybe set */
+ input_pin->sync_msg_map = IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF |
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED |
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF |
+ IPU_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED;
+
+ output_pins = cfg->nof_output_pins++;
+ aq->fw_output = output_pins;
+ stream->output_pins[output_pins].pin_ready = ipu7_isys_queue_buf_ready;
+ stream->output_pins[output_pins].aq = aq;
+
+ output_pin = &cfg->output_pins[output_pins];
+ /* output pin msg link */
+ output_pin->link.buffer_lines = 0;
+ output_pin->link.foreign_key = IPU_MSG_LINK_FOREIGN_KEY_NONE;
+ output_pin->link.granularity_pointer_update = 0;
+ output_pin->link.msg_link_streaming_mode =
+ IA_GOFO_MSG_LINK_STREAMING_MODE_SOFF;
+
+ output_pin->link.pbk_id = IPU_MSG_LINK_PBK_ID_DONT_CARE;
+ output_pin->link.pbk_slot_id = IPU_MSG_LINK_PBK_SLOT_ID_DONT_CARE;
+ output_pin->link.dest = IPU_INSYS_OUTPUT_LINK_DEST_MEM;
+ output_pin->link.use_sw_managed = 1;
+ /* TODO: set the snoop bit for metadata capture */
+ output_pin->link.is_snoop = 0;
+
+ /* output pin crop */
+ output_pin->crop.line_top = 0;
+ output_pin->crop.line_bottom = 0;
+
+ /* output de-compression */
+ output_pin->dpcm.enable = 0;
+
+ /* frame format type */
+ pfmt = ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+ output_pin->ft = (u16)pfmt->css_pixelformat;
+
+ /* stride in bytes */
+ output_pin->stride = av->pix_fmt.bytesperline;
+ output_pin->send_irq = 1;
+ output_pin->early_ack_en = 0;
+
+ /* input pin id */
+ output_pin->input_pin_id = input_pins;
+
+ return 0;
+}
+
+/* Create stream and start it using the CSS FW ABI. */
+static int start_stream_firmware(struct ipu7_isys_video *av,
+ struct ipu7_isys_buffer_list *bl)
+{
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu7_isys_stream *stream = av->stream;
+ struct ipu7_insys_stream_cfg *stream_cfg;
+ struct ipu7_insys_buffset *buf = NULL;
+ struct isys_fw_msgs *msg = NULL;
+ struct ipu7_isys_queue *aq;
+ int ret, retout, tout;
+ u16 send_type;
+
+ if (WARN_ON(!bl))
+ return -EIO;
+
+ msg = ipu7_get_fw_msg_buf(stream);
+ if (!msg)
+ return -ENOMEM;
+
+ stream_cfg = &msg->fw_msg.stream;
+ stream_cfg->port_id = stream->stream_source;
+ stream_cfg->vc = stream->vc;
+ stream_cfg->stream_msg_map = IPU_INSYS_STREAM_ENABLE_MSG_SEND_RESP |
+ IPU_INSYS_STREAM_ENABLE_MSG_SEND_IRQ;
+
+ list_for_each_entry(aq, &stream->queues, node) {
+ struct ipu7_isys_video *__av = ipu7_isys_queue_to_video(aq);
+
+ ret = ipu7_isys_fw_pin_cfg(__av, stream_cfg);
+ if (ret < 0) {
+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+ return ret;
+ }
+ }
+
+ ipu7_fw_isys_dump_stream_cfg(dev, stream_cfg);
+
+ stream->nr_output_pins = stream_cfg->nof_output_pins;
+
+ reinit_completion(&stream->stream_open_completion);
+
+ ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle,
+ stream_cfg, msg->dma_addr,
+ sizeof(*stream_cfg),
+ IPU_INSYS_SEND_TYPE_STREAM_OPEN);
+ if (ret < 0) {
+ dev_err(dev, "can't open stream (%d)\n", ret);
+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+ return ret;
+ }
+
+ get_stream_opened(av);
+
+ tout = wait_for_completion_timeout(&stream->stream_open_completion,
+ FW_CALL_TIMEOUT_JIFFIES);
+
+ ipu7_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+
+ if (!tout) {
+ dev_err(dev, "stream open time out\n");
+ ret = -ETIMEDOUT;
+ goto out_put_stream_opened;
+ }
+ if (stream->error) {
+ dev_err(dev, "stream open error: %d\n", stream->error);
+ ret = -EIO;
+ goto out_put_stream_opened;
+ }
+ dev_dbg(dev, "start stream: open complete\n");
+
+ msg = ipu7_get_fw_msg_buf(stream);
+ if (!msg) {
+ ret = -ENOMEM;
+ goto out_put_stream_opened;
+ }
+ buf = &msg->fw_msg.frame;
+
+ ipu7_isys_buffer_to_fw_frame_buff(buf, stream, bl);
+ ipu7_isys_buffer_list_queue(bl, IPU_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
+
+ reinit_completion(&stream->stream_start_completion);
+
+ send_type = IPU_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
+ ipu7_fw_isys_dump_frame_buff_set(dev, buf,
+ stream_cfg->nof_output_pins);
+ ret = ipu7_fw_isys_complex_cmd(av->isys, stream->stream_handle, buf,
+ msg->dma_addr, sizeof(*buf),
+ send_type);
+ if (ret < 0) {
+ dev_err(dev, "can't start streaming (%d)\n", ret);
+ goto out_stream_close;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_start_completion,
+ FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout) {
+ dev_err(dev, "stream start time out\n");
+ ret = -ETIMEDOUT;
+ goto out_stream_close;
+ }
+ if (stream->error) {
+ dev_err(dev, "stream start error: %d\n", stream->error);
+ ret = -EIO;
+ goto out_stream_close;
+ }
+ dev_dbg(dev, "start stream: complete\n");
+
+ return 0;
+
+out_stream_close:
+ reinit_completion(&stream->stream_close_completion);
+
+ retout = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle,
+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE);
+ if (retout < 0) {
+ dev_dbg(dev, "can't close stream (%d)\n", retout);
+ goto out_put_stream_opened;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_close_completion,
+ FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout)
+ dev_err(dev, "stream close time out with error %d\n",
+ stream->error);
+ else
+ dev_dbg(dev, "stream close complete\n");
+
+out_put_stream_opened:
+ put_stream_opened(av);
+
+ return ret;
+}
+
+static void stop_streaming_firmware(struct ipu7_isys_video *av)
+{
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu7_isys_stream *stream = av->stream;
+ int ret, tout;
+
+ reinit_completion(&stream->stream_stop_completion);
+
+ ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle,
+ IPU_INSYS_SEND_TYPE_STREAM_FLUSH);
+ if (ret < 0) {
+ dev_err(dev, "can't stop stream (%d)\n", ret);
+ return;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_stop_completion,
+ FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout)
+ dev_warn(dev, "stream stop time out\n");
+ else if (stream->error)
+ dev_warn(dev, "stream stop error: %d\n", stream->error);
+ else
+ dev_dbg(dev, "stop stream: complete\n");
+}
+
+static void close_streaming_firmware(struct ipu7_isys_video *av)
+{
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu7_isys_stream *stream = av->stream;
+ int ret, tout;
+
+ reinit_completion(&stream->stream_close_completion);
+
+ ret = ipu7_fw_isys_simple_cmd(av->isys, stream->stream_handle,
+ IPU_INSYS_SEND_TYPE_STREAM_CLOSE);
+ if (ret < 0) {
+ dev_err(dev, "can't close stream (%d)\n", ret);
+ return;
+ }
+
+ tout = wait_for_completion_timeout(&stream->stream_close_completion,
+ FW_CALL_TIMEOUT_JIFFIES);
+ if (!tout)
+ dev_warn(dev, "stream close time out\n");
+ else if (stream->error)
+ dev_warn(dev, "stream close error: %d\n", stream->error);
+ else
+ dev_dbg(dev, "close stream: complete\n");
+
+ put_stream_opened(av);
+}
+
+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av,
+ struct media_entity *source_entity,
+ int nr_queues)
+{
+ struct ipu7_isys_stream *stream = av->stream;
+ struct ipu7_isys_csi2 *csi2;
+
+ if (WARN_ON(stream->nr_streaming))
+ return -EINVAL;
+
+ stream->nr_queues = nr_queues;
+ atomic_set(&stream->sequence, 0);
+ atomic_set(&stream->buf_id, 0);
+
+ stream->seq_index = 0;
+ memset(stream->seq, 0, sizeof(stream->seq));
+
+ if (WARN_ON(!list_empty(&stream->queues)))
+ return -EINVAL;
+
+ stream->stream_source = stream->asd->source;
+
+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+ csi2->receiver_errors = 0;
+ stream->source_entity = source_entity;
+
+ dev_dbg(&av->isys->adev->auxdev.dev,
+ "prepare stream: external entity %s\n",
+ stream->source_entity->name);
+
+ return 0;
+}
+
+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream)
+{
+ unsigned long flags;
+ struct device *dev;
+ unsigned int i;
+
+ if (!stream) {
+ pr_err("ipu7-isys: no available stream\n");
+ return;
+ }
+
+ dev = &stream->isys->adev->auxdev.dev;
+
+ spin_lock_irqsave(&stream->isys->streams_lock, flags);
+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+ if (&stream->isys->streams[i] == stream) {
+ if (stream->isys->streams_ref_count[i] > 0)
+ stream->isys->streams_ref_count[i]--;
+ else
+ dev_warn(dev, "invalid stream %d\n", i);
+
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&stream->isys->streams_lock, flags);
+}
+
+static struct ipu7_isys_stream *
+ipu7_isys_get_stream(struct ipu7_isys_video *av, struct ipu7_isys_subdev *asd)
+{
+ struct ipu7_isys_stream *stream = NULL;
+ struct ipu7_isys *isys = av->isys;
+ unsigned long flags;
+ unsigned int i;
+ u8 vc = av->vc;
+
+ if (!isys)
+ return NULL;
+
+ spin_lock_irqsave(&isys->streams_lock, flags);
+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+ if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&
+ isys->streams[i].asd == asd) {
+ isys->streams_ref_count[i]++;
+ stream = &isys->streams[i];
+ break;
+ }
+ }
+
+ if (!stream) {
+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+ if (!isys->streams_ref_count[i]) {
+ isys->streams_ref_count[i]++;
+ stream = &isys->streams[i];
+ stream->vc = vc;
+ stream->asd = asd;
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+ return stream;
+}
+
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys, u8 stream_handle)
+{
+ unsigned long flags;
+ struct ipu7_isys_stream *stream = NULL;
+
+ if (!isys)
+ return NULL;
+
+ if (stream_handle >= IPU_ISYS_MAX_STREAMS) {
+ dev_err(&isys->adev->auxdev.dev,
+ "stream_handle %d is invalid\n", stream_handle);
+ return NULL;
+ }
+
+ spin_lock_irqsave(&isys->streams_lock, flags);
+ if (isys->streams_ref_count[stream_handle] > 0) {
+ isys->streams_ref_count[stream_handle]++;
+ stream = &isys->streams[stream_handle];
+ }
+ spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+ return stream;
+}
+
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc)
+{
+ struct ipu7_isys_stream *stream = NULL;
+ unsigned long flags;
+ unsigned int i;
+
+ if (!isys)
+ return NULL;
+
+ if (source < 0) {
+ dev_err(&isys->adev->auxdev.dev,
+ "query stream with invalid port number\n");
+ return NULL;
+ }
+
+ spin_lock_irqsave(&isys->streams_lock, flags);
+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+ if (!isys->streams_ref_count[i])
+ continue;
+
+ if (isys->streams[i].stream_source == source &&
+ isys->streams[i].vc == vc) {
+ stream = &isys->streams[i];
+ isys->streams_ref_count[i]++;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+ return stream;
+}
+
+static u32 get_remote_pad_stream(struct media_pad *r_pad)
+{
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev *sd;
+ u32 stream_id = 0;
+ unsigned int i;
+
+ sd = media_entity_to_v4l2_subdev(r_pad->entity);
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ if (!state)
+ return 0;
+
+ for (i = 0; i < state->stream_configs.num_configs; i++) {
+ struct v4l2_subdev_stream_config *cfg =
+ &state->stream_configs.configs[i];
+ if (cfg->pad == r_pad->index) {
+ stream_id = cfg->stream;
+ break;
+ }
+ }
+
+ v4l2_subdev_unlock_state(state);
+
+ return stream_id;
+}
+
+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state,
+ struct ipu7_isys_buffer_list *bl)
+{
+ struct ipu7_isys_stream *stream = av->stream;
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct media_pad *r_pad;
+ struct v4l2_subdev *sd;
+ u32 r_stream;
+ int ret = 0;
+
+ dev_dbg(dev, "set stream: %d\n", state);
+
+ if (WARN(!stream->source_entity, "No source entity for stream\n"))
+ return -ENODEV;
+
+ sd = &stream->asd->sd;
+ r_pad = media_pad_remote_pad_first(&av->pad);
+ r_stream = get_remote_pad_stream(r_pad);
+ if (!state) {
+ stop_streaming_firmware(av);
+
+ /* stop sub-device which connects with video */
+ dev_dbg(dev, "disable streams %s pad:%d mask:0x%llx\n",
+ sd->name, r_pad->index, BIT_ULL(r_stream));
+ ret = v4l2_subdev_disable_streams(sd, r_pad->index,
+ BIT_ULL(r_stream));
+ if (ret) {
+ dev_err(dev, "disable streams %s failed with %d\n",
+ sd->name, ret);
+ return ret;
+ }
+
+ close_streaming_firmware(av);
+ } else {
+ ret = start_stream_firmware(av, bl);
+ if (ret) {
+ dev_err(dev, "start stream of firmware failed\n");
+ return ret;
+ }
+
+ /* start sub-device which connects with video */
+ dev_dbg(dev, "enable streams %s pad: %d mask:0x%llx\n",
+ sd->name, r_pad->index, BIT_ULL(r_stream));
+ ret = v4l2_subdev_enable_streams(sd, r_pad->index,
+ BIT_ULL(r_stream));
+ if (ret) {
+ dev_err(dev, "enable streams %s failed with %d\n",
+ sd->name, ret);
+ goto out_media_entity_stop_streaming_firmware;
+ }
+ }
+
+ av->streaming = state;
+
+ return 0;
+
+out_media_entity_stop_streaming_firmware:
+ stop_streaming_firmware(av);
+
+ return ret;
+}
+
+static const struct v4l2_ioctl_ops ipu7_v4l2_ioctl_ops = {
+ .vidioc_querycap = ipu7_isys_vidioc_querycap,
+ .vidioc_enum_fmt_vid_cap = ipu7_isys_vidioc_enum_fmt,
+ .vidioc_enum_framesizes = ipu7_isys_vidioc_enum_framesizes,
+ .vidioc_g_fmt_vid_cap = ipu7_isys_vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = ipu7_isys_vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = ipu7_isys_vidioc_try_fmt_vid_cap,
+ .vidioc_reqbufs = ipu7_isys_vidioc_reqbufs,
+ .vidioc_create_bufs = ipu7_isys_vidioc_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_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+};
+
+static const struct media_entity_operations entity_ops = {
+ .link_validate = link_validate,
+};
+
+static const struct v4l2_file_operations isys_fops = {
+ .owner = THIS_MODULE,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+ .open = video_open,
+ .release = vb2_fop_release,
+};
+
+int ipu7_isys_fw_open(struct ipu7_isys *isys)
+{
+ struct ipu7_bus_device *adev = isys->adev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&adev->auxdev.dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&isys->mutex);
+
+ if (isys->ref_count++)
+ goto unlock;
+
+ /*
+ * Buffers could have been left to wrong queue at last closure.
+ * Move them now back to empty buffer queue.
+ */
+ ipu7_cleanup_fw_msg_bufs(isys);
+
+ ret = ipu7_fw_isys_open(isys);
+ if (ret < 0)
+ goto out;
+
+unlock:
+ mutex_unlock(&isys->mutex);
+
+ return 0;
+out:
+ isys->ref_count--;
+ mutex_unlock(&isys->mutex);
+ pm_runtime_put(&adev->auxdev.dev);
+
+ return ret;
+}
+
+void ipu7_isys_fw_close(struct ipu7_isys *isys)
+{
+ mutex_lock(&isys->mutex);
+
+ isys->ref_count--;
+
+ if (!isys->ref_count)
+ ipu7_fw_isys_close(isys);
+
+ mutex_unlock(&isys->mutex);
+}
+
+int ipu7_isys_setup_video(struct ipu7_isys_video *av,
+ struct media_entity **source_entity, int *nr_queues)
+{
+ const struct ipu7_isys_pixelformat *pfmt =
+ ipu7_isys_get_isys_format(av->pix_fmt.pixelformat);
+ struct device *dev = &av->isys->adev->auxdev.dev;
+ struct media_pad *source_pad, *remote_pad;
+ struct v4l2_mbus_frame_desc_entry entry;
+ struct v4l2_subdev_route *route = NULL;
+ struct v4l2_subdev_route *r;
+ struct v4l2_subdev_state *state;
+ struct ipu7_isys_subdev *asd;
+ struct v4l2_subdev *remote_sd;
+ struct media_pipeline *pipeline;
+ int ret = -EINVAL;
+
+ *nr_queues = 0;
+
+ remote_pad = media_pad_remote_pad_unique(&av->pad);
+ if (IS_ERR(remote_pad)) {
+ dev_dbg(dev, "failed to get remote pad\n");
+ return PTR_ERR(remote_pad);
+ }
+
+ remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+ asd = to_ipu7_isys_subdev(remote_sd);
+
+ source_pad = media_pad_remote_pad_first(&remote_pad->entity->pads[0]);
+ if (!source_pad) {
+ dev_dbg(dev, "No external source entity\n");
+ return -ENODEV;
+ }
+
+ *source_entity = source_pad->entity;
+
+ state = v4l2_subdev_lock_and_get_active_state(remote_sd);
+ for_each_active_route(&state->routing, r) {
+ if (r->source_pad == remote_pad->index)
+ route = r;
+ }
+
+ if (!route) {
+ v4l2_subdev_unlock_state(state);
+ dev_dbg(dev, "Failed to find route\n");
+ return -ENODEV;
+ }
+
+ v4l2_subdev_unlock_state(state);
+
+ ret = ipu7_isys_csi2_get_remote_desc(route->sink_stream,
+ to_ipu7_isys_csi2(asd),
+ *source_entity, &entry,
+ nr_queues);
+ if (ret == -ENOIOCTLCMD) {
+ av->vc = 0;
+ av->dt = ipu7_isys_mbus_code_to_mipi(pfmt->code);
+ if (av->dt == 0xff)
+ return -EINVAL;
+ *nr_queues = 1;
+ } else if (*nr_queues && !ret) {
+ dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
+ entry.stream, entry.length, entry.bus.csi2.vc,
+ entry.bus.csi2.dt);
+
+ av->vc = entry.bus.csi2.vc;
+ av->dt = entry.bus.csi2.dt;
+ } else {
+ dev_err(dev, "failed to get remote frame desc\n");
+ return ret;
+ }
+
+ pipeline = media_entity_pipeline(&av->vdev.entity);
+ if (!pipeline)
+ ret = video_device_pipeline_alloc_start(&av->vdev);
+ else
+ ret = video_device_pipeline_start(&av->vdev, pipeline);
+ if (ret < 0) {
+ dev_dbg(dev, "media pipeline start failed\n");
+ return ret;
+ }
+
+ av->stream = ipu7_isys_get_stream(av, asd);
+ if (!av->stream) {
+ video_device_pipeline_stop(&av->vdev);
+ dev_err(dev, "no available stream for firmware\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Do everything that's needed to initialise things related to video
+ * buffer queue, video node, and the related media entity. The caller
+ * is expected to assign isys field and set the name of the video
+ * device.
+ */
+int ipu7_isys_video_init(struct ipu7_isys_video *av)
+{
+ struct v4l2_format format = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = 1920,
+ .height = 1080,
+ },
+ };
+ int ret;
+
+ mutex_init(&av->mutex);
+ av->vdev.device_caps = V4L2_CAP_STREAMING | V4L2_CAP_IO_MC |
+ V4L2_CAP_VIDEO_CAPTURE;
+ av->vdev.vfl_dir = VFL_DIR_RX;
+
+ ret = ipu7_isys_queue_init(&av->aq);
+ if (ret)
+ goto out_mutex_destroy;
+
+ av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
+ ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
+ if (ret)
+ goto out_vb2_queue_cleanup;
+
+ av->vdev.entity.ops = &entity_ops;
+ av->vdev.release = video_device_release_empty;
+ av->vdev.fops = &isys_fops;
+ av->vdev.v4l2_dev = &av->isys->v4l2_dev;
+ av->vdev.dev_parent = &av->isys->adev->isp->pdev->dev;
+ av->vdev.ioctl_ops = &ipu7_v4l2_ioctl_ops;
+ av->vdev.queue = &av->aq.vbq;
+ av->vdev.lock = &av->mutex;
+
+ __ipu_isys_vidioc_try_fmt_vid_cap(av, &format);
+ av->pix_fmt = format.fmt.pix;
+
+ set_bit(V4L2_FL_USES_V4L2_FH, &av->vdev.flags);
+ video_set_drvdata(&av->vdev, av);
+
+ ret = video_register_device(&av->vdev, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ goto out_media_entity_cleanup;
+
+ return ret;
+
+out_media_entity_cleanup:
+ vb2_video_unregister_device(&av->vdev);
+ media_entity_cleanup(&av->vdev.entity);
+
+out_vb2_queue_cleanup:
+ vb2_queue_release(&av->aq.vbq);
+
+out_mutex_destroy:
+ mutex_destroy(&av->mutex);
+
+ return ret;
+}
+
+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av)
+{
+ vb2_video_unregister_device(&av->vdev);
+ media_entity_cleanup(&av->vdev.entity);
+ mutex_destroy(&av->mutex);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-isys-video.h b/drivers/staging/media/ipu7/ipu7-isys-video.h
new file mode 100644
index 000000000000..1ac1787fabef
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys-video.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_VIDEO_H
+#define IPU7_ISYS_VIDEO_H
+
+#include <linux/atomic.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-dev.h>
+
+#include "ipu7-isys-queue.h"
+
+#define IPU_INSYS_OUTPUT_PINS 11U
+#define IPU_ISYS_MAX_PARALLEL_SOF 2U
+
+struct file;
+struct ipu7_isys;
+struct ipu7_isys_csi2;
+struct ipu7_insys_stream_cfg;
+struct ipu7_isys_subdev;
+
+struct ipu7_isys_pixelformat {
+ u32 pixelformat;
+ u32 bpp;
+ u32 bpp_packed;
+ u32 code;
+ u32 css_pixelformat;
+};
+
+struct sequence_info {
+ unsigned int sequence;
+ u64 timestamp;
+};
+
+struct output_pin_data {
+ void (*pin_ready)(struct ipu7_isys_stream *stream,
+ struct ipu7_insys_resp *info);
+ struct ipu7_isys_queue *aq;
+};
+
+/*
+ * Align with firmware stream. Each stream represents a CSI virtual channel.
+ * May map to multiple video devices
+ */
+struct ipu7_isys_stream {
+ struct mutex mutex;
+ struct media_entity *source_entity;
+ atomic_t sequence;
+ atomic_t buf_id;
+ unsigned int seq_index;
+ struct sequence_info seq[IPU_ISYS_MAX_PARALLEL_SOF];
+ int stream_source;
+ int stream_handle;
+ unsigned int nr_output_pins;
+ struct ipu7_isys_subdev *asd;
+
+ int nr_queues; /* Number of capture queues */
+ int nr_streaming;
+ int streaming;
+ struct list_head queues;
+ struct completion stream_open_completion;
+ struct completion stream_close_completion;
+ struct completion stream_start_completion;
+ struct completion stream_stop_completion;
+ struct ipu7_isys *isys;
+
+ struct output_pin_data output_pins[IPU_INSYS_OUTPUT_PINS];
+ int error;
+ u8 vc;
+};
+
+struct ipu7_isys_video {
+ struct ipu7_isys_queue aq;
+ /* Serialise access to other fields in the struct. */
+ struct mutex mutex;
+ struct media_pad pad;
+ struct video_device vdev;
+ struct v4l2_pix_format pix_fmt;
+ struct ipu7_isys *isys;
+ struct ipu7_isys_csi2 *csi2;
+ struct ipu7_isys_stream *stream;
+ unsigned int streaming;
+ u8 vc;
+ u8 dt;
+};
+
+#define ipu7_isys_queue_to_video(__aq) \
+ container_of(__aq, struct ipu7_isys_video, aq)
+
+extern const struct ipu7_isys_pixelformat ipu7_isys_pfmts[];
+
+const struct ipu7_isys_pixelformat *ipu7_isys_get_isys_format(u32 pixelformat);
+int ipu7_isys_video_prepare_stream(struct ipu7_isys_video *av,
+ struct media_entity *source_entity,
+ int nr_queues);
+int ipu7_isys_video_set_streaming(struct ipu7_isys_video *av, int state,
+ struct ipu7_isys_buffer_list *bl);
+int ipu7_isys_fw_open(struct ipu7_isys *isys);
+void ipu7_isys_fw_close(struct ipu7_isys *isys);
+int ipu7_isys_setup_video(struct ipu7_isys_video *av,
+ struct media_entity **source_entity, int *nr_queues);
+int ipu7_isys_video_init(struct ipu7_isys_video *av);
+void ipu7_isys_video_cleanup(struct ipu7_isys_video *av);
+void ipu7_isys_put_stream(struct ipu7_isys_stream *stream);
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_handle(struct ipu7_isys *isys,
+ u8 stream_handle);
+struct ipu7_isys_stream *
+ipu7_isys_query_stream_by_source(struct ipu7_isys *isys, int source, u8 vc);
+#endif /* IPU7_ISYS_VIDEO_H */
diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c
new file mode 100644
index 000000000000..cb2f49f3e0fa
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys.c
@@ -0,0 +1,1166 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <media/ipu-bridge.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7-bus.h"
+#include "ipu7-buttress-regs.h"
+#include "ipu7-cpd.h"
+#include "ipu7-dma.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-mmu.h"
+#include "ipu7-isys.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-csi-phy.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-isys-video.h"
+#include "ipu7-platform-regs.h"
+
+#define ISYS_PM_QOS_VALUE 300
+
+static int
+isys_complete_ext_device_registration(struct ipu7_isys *isys,
+ struct v4l2_subdev *sd,
+ struct ipu7_isys_csi2_config *csi2)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ unsigned int i;
+ int ret;
+
+ v4l2_set_subdev_hostdata(sd, csi2);
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ if (sd->entity.pads[i].flags & MEDIA_PAD_FL_SOURCE)
+ break;
+ }
+
+ if (i == sd->entity.num_pads) {
+ dev_warn(dev, "no source pad in external entity\n");
+ ret = -ENOENT;
+ goto skip_unregister_subdev;
+ }
+
+ ret = media_create_pad_link(&sd->entity, i,
+ &isys->csi2[csi2->port].asd.sd.entity,
+ 0, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_warn(dev, "can't create link\n");
+ goto skip_unregister_subdev;
+ }
+
+ isys->csi2[csi2->port].nlanes = csi2->nlanes;
+ if (csi2->bus_type == V4L2_MBUS_CSI2_DPHY)
+ isys->csi2[csi2->port].phy_mode = PHY_MODE_DPHY;
+ else
+ isys->csi2[csi2->port].phy_mode = PHY_MODE_CPHY;
+
+ return 0;
+
+skip_unregister_subdev:
+ v4l2_device_unregister_subdev(sd);
+ return ret;
+}
+
+static void isys_stream_init(struct ipu7_isys *isys)
+{
+ unsigned int i;
+
+ for (i = 0; i < IPU_ISYS_MAX_STREAMS; i++) {
+ mutex_init(&isys->streams[i].mutex);
+ init_completion(&isys->streams[i].stream_open_completion);
+ init_completion(&isys->streams[i].stream_close_completion);
+ init_completion(&isys->streams[i].stream_start_completion);
+ init_completion(&isys->streams[i].stream_stop_completion);
+ INIT_LIST_HEAD(&isys->streams[i].queues);
+ isys->streams[i].isys = isys;
+ isys->streams[i].stream_handle = i;
+ isys->streams[i].vc = INVALID_VC_ID;
+ }
+}
+
+static int isys_fw_log_init(struct ipu7_isys *isys)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct isys_fw_log *fw_log;
+ void *log_buf;
+
+ if (isys->fw_log)
+ return 0;
+
+ fw_log = devm_kzalloc(dev, sizeof(*fw_log), GFP_KERNEL);
+ if (!fw_log)
+ return -ENOMEM;
+
+ mutex_init(&fw_log->mutex);
+
+ log_buf = devm_kzalloc(dev, FW_LOG_BUF_SIZE, GFP_KERNEL);
+ if (!log_buf)
+ return -ENOMEM;
+
+ fw_log->head = log_buf;
+ fw_log->addr = log_buf;
+ fw_log->count = 0;
+ fw_log->size = 0;
+
+ isys->fw_log = fw_log;
+
+ return 0;
+}
+
+/* The .bound() notifier callback when a match is found */
+static int isys_notifier_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct ipu7_isys *isys = container_of(notifier,
+ struct ipu7_isys, notifier);
+ struct sensor_async_sd *s_asd =
+ container_of(asc, struct sensor_async_sd, asc);
+ struct device *dev = &isys->adev->auxdev.dev;
+ int ret;
+
+ ret = ipu_bridge_instantiate_vcm(sd->dev);
+ if (ret) {
+ dev_err(dev, "instantiate vcm failed\n");
+ return ret;
+ }
+
+ dev_info(dev, "bind %s nlanes is %d port is %d\n",
+ sd->name, s_asd->csi2.nlanes, s_asd->csi2.port);
+ isys_complete_ext_device_registration(isys, sd, &s_asd->csi2);
+
+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
+}
+
+static int isys_notifier_complete(struct v4l2_async_notifier *notifier)
+{
+ struct ipu7_isys *isys = container_of(notifier,
+ struct ipu7_isys, notifier);
+
+ dev_info(&isys->adev->auxdev.dev,
+ "All sensor registration completed.\n");
+
+ return v4l2_device_register_subdev_nodes(&isys->v4l2_dev);
+}
+
+static const struct v4l2_async_notifier_operations isys_async_ops = {
+ .bound = isys_notifier_bound,
+ .complete = isys_notifier_complete,
+};
+
+static int isys_notifier_init(struct ipu7_isys *isys)
+{
+ const struct ipu7_isys_internal_csi2_pdata *csi2 =
+ &isys->pdata->ipdata->csi2;
+ struct ipu7_device *isp = isys->adev->isp;
+ struct device *dev = &isp->pdev->dev;
+ unsigned int i;
+ int ret;
+
+ v4l2_async_nf_init(&isys->notifier, &isys->v4l2_dev);
+
+ for (i = 0; i < csi2->nports; i++) {
+ struct v4l2_fwnode_endpoint vep = {
+ .bus_type = V4L2_MBUS_UNKNOWN
+ };
+ struct sensor_async_sd *s_asd;
+ struct fwnode_handle *ep;
+
+ ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), i, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ continue;
+
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret)
+ goto err_parse;
+
+ if (vep.bus_type != V4L2_MBUS_CSI2_DPHY &&
+ vep.bus_type != V4L2_MBUS_CSI2_CPHY) {
+ ret = -EINVAL;
+ dev_err(dev, "unsupported bus type %d!\n",
+ vep.bus_type);
+ goto err_parse;
+ }
+
+ s_asd = v4l2_async_nf_add_fwnode_remote(&isys->notifier, ep,
+ struct
+ sensor_async_sd);
+ if (IS_ERR(s_asd)) {
+ ret = PTR_ERR(s_asd);
+ goto err_parse;
+ }
+
+ s_asd->csi2.port = vep.base.port;
+ s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
+ s_asd->csi2.bus_type = vep.bus_type;
+
+ fwnode_handle_put(ep);
+
+ continue;
+
+err_parse:
+ fwnode_handle_put(ep);
+ return ret;
+ }
+
+ if (list_empty(&isys->notifier.waiting_list)) {
+ /* isys probe could continue with async subdevs missing */
+ dev_warn(dev, "no subdev found in graph\n");
+ return 0;
+ }
+
+ isys->notifier.ops = &isys_async_ops;
+ ret = v4l2_async_nf_register(&isys->notifier);
+ if (ret) {
+ dev_err(dev, "failed to register async notifier(%d)\n", ret);
+ v4l2_async_nf_cleanup(&isys->notifier);
+ }
+
+ return ret;
+}
+
+static void isys_notifier_cleanup(struct ipu7_isys *isys)
+{
+ v4l2_async_nf_unregister(&isys->notifier);
+ v4l2_async_nf_cleanup(&isys->notifier);
+}
+
+static void isys_unregister_video_devices(struct ipu7_isys *isys)
+{
+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i, j;
+
+ for (i = 0; i < csi2_pdata->nports; i++)
+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++)
+ ipu7_isys_video_cleanup(&isys->csi2[i].av[j]);
+}
+
+static int isys_register_video_devices(struct ipu7_isys *isys)
+{
+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < csi2_pdata->nports; i++) {
+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) {
+ struct ipu7_isys_video *av = &isys->csi2[i].av[j];
+
+ snprintf(av->vdev.name, sizeof(av->vdev.name),
+ IPU_ISYS_ENTITY_PREFIX " ISYS Capture %u",
+ i * IPU7_NR_OF_CSI2_SRC_PADS + j);
+ av->isys = isys;
+ av->aq.vbq.buf_struct_size =
+ sizeof(struct ipu7_isys_video_buffer);
+
+ ret = ipu7_isys_video_init(av);
+ if (ret)
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ i = i + 1U;
+ while (i--) {
+ while (j--)
+ ipu7_isys_video_cleanup(&isys->csi2[i].av[j]);
+ j = IPU7_NR_OF_CSI2_SRC_PADS;
+ }
+
+ return ret;
+}
+
+static void isys_csi2_unregister_subdevices(struct ipu7_isys *isys)
+{
+ const struct ipu7_isys_internal_csi2_pdata *csi2 =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i;
+
+ for (i = 0; i < csi2->nports; i++)
+ ipu7_isys_csi2_cleanup(&isys->csi2[i]);
+}
+
+static int isys_csi2_register_subdevices(struct ipu7_isys *isys)
+{
+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < csi2_pdata->nports; i++) {
+ ret = ipu7_isys_csi2_init(&isys->csi2[i], isys,
+ isys->pdata->base +
+ csi2_pdata->offsets[i], i);
+ if (ret)
+ goto fail;
+ }
+
+ isys->isr_csi2_mask = IPU7_CSI_RX_LEGACY_IRQ_MASK;
+
+ return 0;
+
+fail:
+ while (i--)
+ ipu7_isys_csi2_cleanup(&isys->csi2[i]);
+
+ return ret;
+}
+
+static int isys_csi2_create_media_links(struct ipu7_isys *isys)
+{
+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata =
+ &isys->pdata->ipdata->csi2;
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct media_entity *sd;
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < csi2_pdata->nports; i++) {
+ sd = &isys->csi2[i].asd.sd.entity;
+
+ for (j = 0; j < IPU7_NR_OF_CSI2_SRC_PADS; j++) {
+ struct ipu7_isys_video *av = &isys->csi2[i].av[j];
+
+ ret = media_create_pad_link(sd, IPU7_CSI2_PAD_SRC + j,
+ &av->vdev.entity, 0, 0);
+ if (ret) {
+ dev_err(dev, "CSI2 can't create link\n");
+ return ret;
+ }
+
+ av->csi2 = &isys->csi2[i];
+ }
+ }
+
+ return 0;
+}
+
+static int isys_register_devices(struct ipu7_isys *isys)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ struct pci_dev *pdev = isys->adev->isp->pdev;
+ int ret;
+
+ media_device_pci_init(&isys->media_dev,
+ pdev, IPU_MEDIA_DEV_MODEL_NAME);
+
+ strscpy(isys->v4l2_dev.name, isys->media_dev.model,
+ sizeof(isys->v4l2_dev.name));
+
+ ret = media_device_register(&isys->media_dev);
+ if (ret < 0)
+ goto out_media_device_unregister;
+
+ isys->v4l2_dev.mdev = &isys->media_dev;
+ isys->v4l2_dev.ctrl_handler = NULL;
+
+ ret = v4l2_device_register(dev, &isys->v4l2_dev);
+ if (ret < 0)
+ goto out_media_device_unregister;
+
+ ret = isys_register_video_devices(isys);
+ if (ret)
+ goto out_v4l2_device_unregister;
+
+ ret = isys_csi2_register_subdevices(isys);
+ if (ret)
+ goto out_video_unregister_device;
+
+ ret = isys_csi2_create_media_links(isys);
+ if (ret)
+ goto out_csi2_unregister_subdevices;
+
+ ret = isys_notifier_init(isys);
+ if (ret)
+ goto out_csi2_unregister_subdevices;
+
+ return 0;
+
+out_csi2_unregister_subdevices:
+ isys_csi2_unregister_subdevices(isys);
+
+out_video_unregister_device:
+ isys_unregister_video_devices(isys);
+
+out_v4l2_device_unregister:
+ v4l2_device_unregister(&isys->v4l2_dev);
+
+out_media_device_unregister:
+ media_device_unregister(&isys->media_dev);
+ media_device_cleanup(&isys->media_dev);
+
+ dev_err(dev, "failed to register isys devices\n");
+
+ return ret;
+}
+
+static void isys_unregister_devices(struct ipu7_isys *isys)
+{
+ isys_unregister_video_devices(isys);
+ isys_csi2_unregister_subdevices(isys);
+ v4l2_device_unregister(&isys->v4l2_dev);
+ media_device_unregister(&isys->media_dev);
+ media_device_cleanup(&isys->media_dev);
+}
+
+static void enable_csi2_legacy_irq(struct ipu7_isys *isys, bool enable)
+{
+ u32 offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE;
+ void __iomem *base = isys->pdata->base;
+ u32 mask = isys->isr_csi2_mask;
+
+ if (!enable) {
+ writel(mask, base + offset + IRQ_CTL_CLEAR);
+ writel(0, base + offset + IRQ_CTL_ENABLE);
+ return;
+ }
+
+ writel(mask, base + offset + IRQ_CTL_EDGE);
+ writel(mask, base + offset + IRQ_CTL_CLEAR);
+ writel(mask, base + offset + IRQ_CTL_MASK);
+ writel(mask, base + offset + IRQ_CTL_ENABLE);
+}
+
+static void enable_to_sw_irq(struct ipu7_isys *isys, bool enable)
+{
+ void __iomem *base = isys->pdata->base;
+ u32 mask = IS_UC_TO_SW_IRQ_MASK;
+ u32 offset = IS_UC_CTRL_BASE;
+
+ if (!enable) {
+ writel(0, base + offset + TO_SW_IRQ_CNTL_ENABLE);
+ return;
+ }
+
+ writel(mask, base + offset + TO_SW_IRQ_CNTL_CLEAR);
+ writel(mask, base + offset + TO_SW_IRQ_CNTL_MASK_N);
+ writel(mask, base + offset + TO_SW_IRQ_CNTL_ENABLE);
+}
+
+void ipu7_isys_setup_hw(struct ipu7_isys *isys)
+{
+ u32 offset;
+ void __iomem *base = isys->pdata->base;
+
+ /* soft reset */
+ offset = IS_IO_GPREGS_BASE;
+
+ writel(0x0, base + offset + CLK_EN_TXCLKESC);
+ /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */
+ writel(0x0, base + offset + CLK_DIV_FACTOR_IS_CLK);
+ /* correct the initial printf configuration */
+ writel(0x200, base + IS_UC_CTRL_BASE + PRINTF_AXI_CNTL);
+
+ enable_to_sw_irq(isys, 1);
+ enable_csi2_legacy_irq(isys, 1);
+}
+
+static void isys_cleanup_hw(struct ipu7_isys *isys)
+{
+ enable_csi2_legacy_irq(isys, 0);
+ enable_to_sw_irq(isys, 0);
+}
+
+static int isys_runtime_pm_resume(struct device *dev)
+{
+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+ struct ipu7_device *isp = adev->isp;
+ unsigned long flags;
+ int ret;
+
+ if (!isys)
+ return 0;
+
+ ret = ipu7_mmu_hw_init(adev->mmu);
+ if (ret)
+ return ret;
+
+ cpu_latency_qos_update_request(&isys->pm_qos, ISYS_PM_QOS_VALUE);
+
+ ret = ipu_buttress_start_tsc_sync(isp);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&isys->power_lock, flags);
+ isys->power = 1;
+ spin_unlock_irqrestore(&isys->power_lock, flags);
+
+ return 0;
+}
+
+static int isys_runtime_pm_suspend(struct device *dev)
+{
+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+ unsigned long flags;
+
+ if (!isys)
+ return 0;
+
+ isys_cleanup_hw(isys);
+
+ spin_lock_irqsave(&isys->power_lock, flags);
+ isys->power = 0;
+ spin_unlock_irqrestore(&isys->power_lock, flags);
+
+ cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
+
+ ipu7_mmu_hw_cleanup(adev->mmu);
+
+ return 0;
+}
+
+static int isys_suspend(struct device *dev)
+{
+ struct ipu7_isys *isys = dev_get_drvdata(dev);
+
+ /* If stream is open, refuse to suspend */
+ if (isys->stream_opened)
+ return -EBUSY;
+
+ return 0;
+}
+
+static int isys_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops isys_pm_ops = {
+ .runtime_suspend = isys_runtime_pm_suspend,
+ .runtime_resume = isys_runtime_pm_resume,
+ .suspend = isys_suspend,
+ .resume = isys_resume,
+};
+
+static void isys_remove(struct auxiliary_device *auxdev)
+{
+ struct ipu7_isys *isys = dev_get_drvdata(&auxdev->dev);
+ struct isys_fw_msgs *fwmsg, *safe;
+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev);
+
+ for (int i = 0; i < IPU_ISYS_MAX_STREAMS; i++)
+ mutex_destroy(&isys->streams[i].mutex);
+
+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist, head)
+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs),
+ fwmsg, fwmsg->dma_addr, 0);
+
+ list_for_each_entry_safe(fwmsg, safe, &isys->framebuflist_fw, head)
+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs),
+ fwmsg, fwmsg->dma_addr, 0);
+
+ isys_notifier_cleanup(isys);
+ isys_unregister_devices(isys);
+
+ cpu_latency_qos_remove_request(&isys->pm_qos);
+
+ mutex_destroy(&isys->stream_mutex);
+ mutex_destroy(&isys->mutex);
+}
+
+static int alloc_fw_msg_bufs(struct ipu7_isys *isys, int amount)
+{
+ struct ipu7_bus_device *adev = isys->adev;
+ struct isys_fw_msgs *addr;
+ dma_addr_t dma_addr;
+ unsigned long flags;
+ unsigned int i;
+
+ for (i = 0; i < amount; i++) {
+ addr = ipu7_dma_alloc(adev, sizeof(struct isys_fw_msgs),
+ &dma_addr, GFP_KERNEL, 0);
+ if (!addr)
+ break;
+ addr->dma_addr = dma_addr;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ list_add(&addr->head, &isys->framebuflist);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ }
+
+ if (i == amount)
+ return 0;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ while (!list_empty(&isys->framebuflist)) {
+ addr = list_first_entry(&isys->framebuflist,
+ struct isys_fw_msgs, head);
+ list_del(&addr->head);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ ipu7_dma_free(adev, sizeof(struct isys_fw_msgs),
+ addr, addr->dma_addr, 0);
+ spin_lock_irqsave(&isys->listlock, flags);
+ }
+ spin_unlock_irqrestore(&isys->listlock, flags);
+
+ return -ENOMEM;
+}
+
+struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream)
+{
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu7_isys *isys = stream->isys;
+ struct isys_fw_msgs *msg;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ if (list_empty(&isys->framebuflist)) {
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ dev_dbg(dev, "Frame buffer list empty\n");
+
+ ret = alloc_fw_msg_bufs(isys, 5);
+ if (ret < 0)
+ return NULL;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ if (list_empty(&isys->framebuflist)) {
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ dev_err(dev, "Frame list empty\n");
+ return NULL;
+ }
+ }
+ msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head);
+ list_move(&msg->head, &isys->framebuflist_fw);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+ memset(&msg->fw_msg, 0, sizeof(msg->fw_msg));
+
+ return msg;
+}
+
+void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys)
+{
+ struct isys_fw_msgs *fwmsg, *fwmsg0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ list_for_each_entry_safe(fwmsg, fwmsg0, &isys->framebuflist_fw, head)
+ list_move(&fwmsg->head, &isys->framebuflist);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+}
+
+void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data)
+{
+ struct isys_fw_msgs *msg;
+ void *ptr = (void *)data;
+ unsigned long flags;
+
+ if (WARN_ON_ONCE(!ptr))
+ return;
+
+ spin_lock_irqsave(&isys->listlock, flags);
+ msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy);
+ list_move(&msg->head, &isys->framebuflist);
+ spin_unlock_irqrestore(&isys->listlock, flags);
+}
+
+static int isys_probe(struct auxiliary_device *auxdev,
+ const struct auxiliary_device_id *auxdev_id)
+{
+ const struct ipu7_isys_internal_csi2_pdata *csi2_pdata;
+ struct ipu7_bus_device *adev = auxdev_to_adev(auxdev);
+ struct ipu7_device *isp = adev->isp;
+ struct ipu7_isys *isys;
+ int ret = 0;
+
+ if (!isp->ipu7_bus_ready_to_probe)
+ return -EPROBE_DEFER;
+
+ isys = devm_kzalloc(&auxdev->dev, sizeof(*isys), GFP_KERNEL);
+ if (!isys)
+ return -ENOMEM;
+
+ ret = pm_runtime_resume_and_get(&auxdev->dev);
+ if (ret < 0)
+ return ret;
+
+ adev->auxdrv_data =
+ (const struct ipu7_auxdrv_data *)auxdev_id->driver_data;
+ adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver);
+ isys->adev = adev;
+ isys->pdata = adev->pdata;
+
+ INIT_LIST_HEAD(&isys->requests);
+ csi2_pdata = &isys->pdata->ipdata->csi2;
+
+ isys->csi2 = devm_kcalloc(&auxdev->dev, csi2_pdata->nports,
+ sizeof(*isys->csi2), GFP_KERNEL);
+ if (!isys->csi2) {
+ ret = -ENOMEM;
+ goto out_runtime_put;
+ }
+
+ ret = ipu7_mmu_hw_init(adev->mmu);
+ if (ret)
+ goto out_runtime_put;
+
+ spin_lock_init(&isys->streams_lock);
+ spin_lock_init(&isys->power_lock);
+ isys->power = 0;
+
+ mutex_init(&isys->mutex);
+ mutex_init(&isys->stream_mutex);
+
+ spin_lock_init(&isys->listlock);
+ INIT_LIST_HEAD(&isys->framebuflist);
+ INIT_LIST_HEAD(&isys->framebuflist_fw);
+
+ dev_set_drvdata(&auxdev->dev, isys);
+
+ isys->icache_prefetch = 0;
+ isys->phy_rext_cal = 0;
+
+ isys_stream_init(isys);
+
+ cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
+ ret = alloc_fw_msg_bufs(isys, 20);
+ if (ret < 0)
+ goto out_cleanup_isys;
+
+ ret = ipu7_fw_isys_init(isys);
+ if (ret)
+ goto out_cleanup_isys;
+
+ ret = isys_register_devices(isys);
+ if (ret)
+ goto out_cleanup_fw;
+
+ ret = isys_fw_log_init(isys);
+ if (ret)
+ goto out_cleanup;
+
+ ipu7_mmu_hw_cleanup(adev->mmu);
+ pm_runtime_put(&auxdev->dev);
+
+ return 0;
+
+out_cleanup:
+ isys_unregister_devices(isys);
+out_cleanup_fw:
+ ipu7_fw_isys_release(isys);
+out_cleanup_isys:
+ cpu_latency_qos_remove_request(&isys->pm_qos);
+
+ for (unsigned int i = 0; i < IPU_ISYS_MAX_STREAMS; i++)
+ mutex_destroy(&isys->streams[i].mutex);
+
+ mutex_destroy(&isys->mutex);
+ mutex_destroy(&isys->stream_mutex);
+
+ ipu7_mmu_hw_cleanup(adev->mmu);
+
+out_runtime_put:
+ pm_runtime_put(&auxdev->dev);
+
+ return ret;
+}
+
+struct ipu7_csi2_error {
+ const char *error_string;
+ bool is_info_only;
+};
+
+/*
+ * Strings corresponding to CSI-2 receiver errors are here.
+ * Corresponding macros are defined in the header file.
+ */
+static const struct ipu7_csi2_error dphy_rx_errors[] = {
+ { "Error handler FIFO full", false },
+ { "Reserved Short Packet encoding detected", true },
+ { "Reserved Long Packet encoding detected", true },
+ { "Received packet is too short", false},
+ { "Received packet is too long", false},
+ { "Short packet discarded due to errors", false },
+ { "Long packet discarded due to errors", false },
+ { "CSI Combo Rx interrupt", false },
+ { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false },
+ { "Received NULL packet", true },
+ { "Received blanking packet", true },
+ { "Tie to 0", true },
+ { }
+};
+
+static void ipu7_isys_register_errors(struct ipu7_isys_csi2 *csi2)
+{
+ u32 offset = IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+ u32 status = readl(csi2->base + offset + IRQ_CTL_STATUS);
+ u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+
+ if (!status)
+ return;
+
+ dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n",
+ csi2->port, status);
+
+ writel(status & mask, csi2->base + offset + IRQ_CTL_CLEAR);
+ csi2->receiver_errors |= status & mask;
+}
+
+static void ipu7_isys_csi2_error(struct ipu7_isys_csi2 *csi2)
+{
+ struct ipu7_csi2_error const *errors;
+ unsigned int i;
+ u32 status;
+
+ /* Register errors once more in case of error interrupts are disabled */
+ ipu7_isys_register_errors(csi2);
+ status = csi2->receiver_errors;
+ csi2->receiver_errors = 0;
+ errors = dphy_rx_errors;
+
+ for (i = 0; i < CSI_RX_NUM_ERRORS_IN_IRQ; i++) {
+ if (status & BIT(i))
+ dev_err_ratelimited(&csi2->isys->adev->auxdev.dev,
+ "csi2-%i error: %s\n",
+ csi2->port,
+ errors[i].error_string);
+ }
+}
+
+struct resp_to_msg {
+ enum ipu7_insys_resp_type type;
+ const char *msg;
+};
+
+static const struct resp_to_msg is_fw_msg[] = {
+ {IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE,
+ "IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE"},
+ {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
+ "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK"},
+ {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK,
+ "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK"},
+ {IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK,
+ "IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK"},
+ {IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK,
+ "IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK"},
+ {IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK,
+ "IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK"},
+ {IPU_INSYS_RESP_TYPE_PIN_DATA_READY,
+ "IPU_INSYS_RESP_TYPE_PIN_DATA_READY"},
+ {IPU_INSYS_RESP_TYPE_FRAME_SOF, "IPU_INSYS_RESP_TYPE_FRAME_SOF"},
+ {IPU_INSYS_RESP_TYPE_FRAME_EOF, "IPU_INSYS_RESP_TYPE_FRAME_EOF"},
+ {IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
+ "IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE"},
+ {IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE,
+ "IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE"},
+ {N_IPU_INSYS_RESP_TYPE, "N_IPU_INSYS_RESP_TYPE"},
+};
+
+int isys_isr_one(struct ipu7_bus_device *adev)
+{
+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+ struct ipu7_isys_stream *stream = NULL;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu7_isys_csi2 *csi2 = NULL;
+ struct ia_gofo_msg_err err_info;
+ struct ipu7_insys_resp *resp;
+ u64 ts;
+
+ if (!isys->adev->syscom)
+ return 1;
+
+ resp = ipu7_fw_isys_get_resp(isys);
+ if (!resp)
+ return 1;
+ if (resp->type >= N_IPU_INSYS_RESP_TYPE) {
+ dev_err(dev, "Unknown response type %u stream %u\n",
+ resp->type, resp->stream_id);
+ ipu7_fw_isys_put_resp(isys);
+ return 1;
+ }
+
+ err_info = resp->error_info;
+ ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0];
+ if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE &&
+ err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) {
+ /* receive a sp w/o command, firmware drop it */
+ dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n",
+ resp->type, is_fw_msg[resp->type].msg,
+ resp->stream_id);
+ dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n",
+ resp->pin_id, resp->buf_id, resp->frame_id);
+ dev_dbg(dev, "\terror group %u code %u details [%u %u]\n",
+ err_info.err_group, err_info.err_code,
+ err_info.err_detail[0], err_info.err_detail[1]);
+ } else if (!IA_GOFO_MSG_ERR_IS_OK(err_info)) {
+ dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n",
+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id,
+ resp->pin_id, resp->buf_id, resp->frame_id);
+ dev_err(dev, "\terror group %u code %u details [%u %u]\n",
+ err_info.err_group, err_info.err_code,
+ err_info.err_detail[0], err_info.err_detail[1]);
+ } else {
+ dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n",
+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id,
+ resp->pin_id, resp->buf_id, resp->frame_id);
+ dev_dbg(dev, "\tts %llu\n", ts);
+ }
+
+ if (resp->stream_id >= IPU_ISYS_MAX_STREAMS) {
+ dev_err(dev, "bad stream handle %u\n",
+ resp->stream_id);
+ goto leave;
+ }
+
+ stream = ipu7_isys_query_stream_by_handle(isys, resp->stream_id);
+ if (!stream) {
+ dev_err(dev, "stream of stream_handle %u is unused\n",
+ resp->stream_id);
+ goto leave;
+ }
+
+ stream->error = err_info.err_code;
+
+ if (stream->asd)
+ csi2 = ipu7_isys_subdev_to_csi2(stream->asd);
+
+ switch (resp->type) {
+ case IPU_INSYS_RESP_TYPE_STREAM_OPEN_DONE:
+ complete(&stream->stream_open_completion);
+ break;
+ case IPU_INSYS_RESP_TYPE_STREAM_CLOSE_ACK:
+ complete(&stream->stream_close_completion);
+ break;
+ case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
+ complete(&stream->stream_start_completion);
+ break;
+ case IPU_INSYS_RESP_TYPE_STREAM_ABORT_ACK:
+ complete(&stream->stream_stop_completion);
+ break;
+ case IPU_INSYS_RESP_TYPE_STREAM_FLUSH_ACK:
+ complete(&stream->stream_stop_completion);
+ break;
+ case IPU_INSYS_RESP_TYPE_PIN_DATA_READY:
+ /*
+ * firmware only release the capture msg until software
+ * get pin_data_ready event
+ */
+ ipu7_put_fw_msg_buf(ipu7_bus_get_drvdata(adev), resp->buf_id);
+ if (resp->pin_id < IPU_INSYS_OUTPUT_PINS &&
+ stream->output_pins[resp->pin_id].pin_ready)
+ stream->output_pins[resp->pin_id].pin_ready(stream,
+ resp);
+ else
+ dev_err(dev, "No handler for pin %u ready\n",
+ resp->pin_id);
+ if (csi2)
+ ipu7_isys_csi2_error(csi2);
+
+ break;
+ case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK:
+ break;
+ case IPU_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
+ case IPU_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE:
+ break;
+ case IPU_INSYS_RESP_TYPE_FRAME_SOF:
+ if (csi2)
+ ipu7_isys_csi2_sof_event_by_stream(stream);
+
+ stream->seq[stream->seq_index].sequence =
+ atomic_read(&stream->sequence) - 1U;
+ stream->seq[stream->seq_index].timestamp = ts;
+ dev_dbg(dev,
+ "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n",
+ resp->stream_id, resp->frame_id,
+ stream->seq[stream->seq_index].sequence, ts);
+ stream->seq_index = (stream->seq_index + 1U)
+ % IPU_ISYS_MAX_PARALLEL_SOF;
+ break;
+ case IPU_INSYS_RESP_TYPE_FRAME_EOF:
+ if (csi2)
+ ipu7_isys_csi2_eof_event_by_stream(stream);
+
+ dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n",
+ resp->stream_id,
+ stream->seq[stream->seq_index].sequence, ts);
+ break;
+ default:
+ dev_err(dev, "Unknown response type %u stream %u\n",
+ resp->type, resp->stream_id);
+ break;
+ }
+
+ ipu7_isys_put_stream(stream);
+leave:
+ ipu7_fw_isys_put_resp(isys);
+
+ return 0;
+}
+
+static void ipu7_isys_csi2_isr(struct ipu7_isys_csi2 *csi2)
+{
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct ipu7_device *isp = csi2->isys->adev->isp;
+ struct ipu7_isys_stream *s;
+ u32 sync, offset;
+ u32 fe = 0;
+ u8 vc;
+
+ ipu7_isys_register_errors(csi2);
+
+ offset = IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+ sync = readl(csi2->base + offset + IRQ_CTL_STATUS);
+ writel(sync, csi2->base + offset + IRQ_CTL_CLEAR);
+ dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync);
+
+ if (!is_ipu7(isp->hw_ver)) {
+ fe = readl(csi2->base + offset + IRQ1_CTL_STATUS);
+ writel(fe, csi2->base + offset + IRQ1_CTL_CLEAR);
+ dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe);
+ }
+
+ for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) {
+ s = ipu7_isys_query_stream_by_source(csi2->isys,
+ csi2->asd.source, vc);
+ if (!s)
+ continue;
+
+ if (!is_ipu7(isp->hw_ver)) {
+ if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc))
+ ipu7_isys_csi2_sof_event_by_stream(s);
+
+ if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc))
+ ipu7_isys_csi2_eof_event_by_stream(s);
+ } else {
+ if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2)))
+ ipu7_isys_csi2_sof_event_by_stream(s);
+
+ if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2)))
+ ipu7_isys_csi2_eof_event_by_stream(s);
+ }
+ }
+}
+
+static irqreturn_t isys_isr(struct ipu7_bus_device *adev)
+{
+ struct ipu7_isys *isys = ipu7_bus_get_drvdata(adev);
+ u32 status_csi, status_sw, csi_offset, sw_offset;
+ struct device *dev = &isys->adev->auxdev.dev;
+ void __iomem *base = isys->pdata->base;
+
+ spin_lock(&isys->power_lock);
+ if (!isys->power) {
+ spin_unlock(&isys->power_lock);
+ return IRQ_NONE;
+ }
+
+ csi_offset = IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE;
+ sw_offset = IS_BASE;
+
+ status_csi = readl(base + csi_offset + IRQ_CTL_STATUS);
+ status_sw = readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS);
+ if (!status_csi && !status_sw) {
+ spin_unlock(&isys->power_lock);
+ return IRQ_NONE;
+ }
+
+ if (status_csi)
+ dev_dbg(dev, "status csi 0x%08x\n", status_csi);
+ if (status_sw)
+ dev_dbg(dev, "status to_sw 0x%08x\n", status_sw);
+
+ do {
+ writel(status_sw, base + sw_offset + TO_SW_IRQ_CNTL_CLEAR);
+ writel(status_csi, base + csi_offset + IRQ_CTL_CLEAR);
+
+ if (isys->isr_csi2_mask & status_csi) {
+ unsigned int i;
+
+ for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
+ /* irq from not enabled port */
+ if (!isys->csi2[i].base)
+ continue;
+ if (status_csi & isys->csi2[i].legacy_irq_mask)
+ ipu7_isys_csi2_isr(&isys->csi2[i]);
+ }
+ }
+
+ if (!isys_isr_one(adev))
+ status_sw = TO_SW_IRQ_FW;
+ else
+ status_sw = 0;
+
+ status_csi = readl(base + csi_offset + IRQ_CTL_STATUS);
+ status_sw |= readl(base + sw_offset + TO_SW_IRQ_CNTL_STATUS);
+ } while ((status_csi & isys->isr_csi2_mask) ||
+ (status_sw & TO_SW_IRQ_FW));
+
+ writel(TO_SW_IRQ_MASK, base + sw_offset + TO_SW_IRQ_CNTL_MASK_N);
+
+ spin_unlock(&isys->power_lock);
+
+ return IRQ_HANDLED;
+}
+
+static const struct ipu7_auxdrv_data ipu7_isys_auxdrv_data = {
+ .isr = isys_isr,
+ .isr_threaded = NULL,
+ .wake_isr_thread = false,
+};
+
+static const struct auxiliary_device_id ipu7_isys_id_table[] = {
+ {
+ .name = "intel_ipu7.isys",
+ .driver_data = (kernel_ulong_t)&ipu7_isys_auxdrv_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, ipu7_isys_id_table);
+
+static struct auxiliary_driver isys_driver = {
+ .name = IPU_ISYS_NAME,
+ .probe = isys_probe,
+ .remove = isys_remove,
+ .id_table = ipu7_isys_id_table,
+ .driver = {
+ .pm = &isys_pm_ops,
+ },
+};
+
+module_auxiliary_driver(isys_driver);
+
+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
+MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel ipu7 input system driver");
+MODULE_IMPORT_NS("INTEL_IPU7");
+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE");
diff --git a/drivers/staging/media/ipu7/ipu7-isys.h b/drivers/staging/media/ipu7/ipu7-isys.h
new file mode 100644
index 000000000000..ef1ab1b42f6c
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-isys.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_H
+#define IPU7_ISYS_H
+
+#include <linux/irqreturn.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/pm_qos.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+
+#include <media/media-device.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+
+#include "abi/ipu7_fw_msg_abi.h"
+#include "abi/ipu7_fw_isys_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-isys-csi2.h"
+#include "ipu7-isys-video.h"
+
+#define IPU_ISYS_ENTITY_PREFIX "Intel IPU7"
+
+/* FW support max 16 streams */
+#define IPU_ISYS_MAX_STREAMS 16U
+
+/*
+ * Current message queue configuration. These must be big enough
+ * so that they never gets full. Queues are located in system memory
+ */
+#define IPU_ISYS_SIZE_RECV_QUEUE 40U
+#define IPU_ISYS_SIZE_LOG_QUEUE 256U
+#define IPU_ISYS_SIZE_SEND_QUEUE 40U
+#define IPU_ISYS_NUM_RECV_QUEUE 1U
+
+#define IPU_ISYS_MIN_WIDTH 2U
+#define IPU_ISYS_MIN_HEIGHT 2U
+#define IPU_ISYS_MAX_WIDTH 8160U
+#define IPU_ISYS_MAX_HEIGHT 8190U
+
+#define FW_CALL_TIMEOUT_JIFFIES \
+ msecs_to_jiffies(IPU_LIB_CALL_TIMEOUT_MS)
+
+struct isys_fw_log {
+ struct mutex mutex; /* protect whole struct */
+ void *head;
+ void *addr;
+ u32 count; /* running counter of log */
+ u32 size; /* actual size of log content, in bits */
+};
+
+/*
+ * struct ipu7_isys
+ *
+ * @media_dev: Media device
+ * @v4l2_dev: V4L2 device
+ * @adev: ISYS bus device
+ * @power: Is ISYS powered on or not?
+ * @isr_bits: Which bits does the ISR handle?
+ * @power_lock: Serialise access to power (power state in general)
+ * @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers
+ * @streams_lock: serialise access to streams
+ * @streams: streams per firmware stream ID
+ * @syscom: fw communication layer context
+ * @ref_count: total number of callers fw open
+ * @mutex: serialise access isys video open/release related operations
+ * @stream_mutex: serialise stream start and stop, queueing requests
+ * @pdata: platform data pointer
+ * @csi2: CSI-2 receivers
+ */
+struct ipu7_isys {
+ struct media_device media_dev;
+ struct v4l2_device v4l2_dev;
+ struct ipu7_bus_device *adev;
+
+ int power;
+ spinlock_t power_lock; /* Serialise access to power */
+ u32 isr_csi2_mask;
+ u32 csi2_rx_ctrl_cached;
+ spinlock_t streams_lock;
+ struct ipu7_isys_stream streams[IPU_ISYS_MAX_STREAMS];
+ int streams_ref_count[IPU_ISYS_MAX_STREAMS];
+ u32 phy_rext_cal;
+ bool icache_prefetch;
+ bool csi2_cse_ipc_not_supported;
+ unsigned int ref_count;
+ unsigned int stream_opened;
+
+ struct mutex mutex; /* Serialise isys video open/release related */
+ struct mutex stream_mutex; /* Stream start, stop, queueing reqs */
+
+ struct ipu7_isys_pdata *pdata;
+
+ struct ipu7_isys_csi2 *csi2;
+ struct isys_fw_log *fw_log;
+
+ struct list_head requests;
+ struct pm_qos_request pm_qos;
+ spinlock_t listlock; /* Protect framebuflist */
+ struct list_head framebuflist;
+ struct list_head framebuflist_fw;
+ struct v4l2_async_notifier notifier;
+
+ struct ipu7_insys_config *subsys_config;
+ dma_addr_t subsys_config_dma_addr;
+};
+
+struct isys_fw_msgs {
+ union {
+ u64 dummy;
+ struct ipu7_insys_buffset frame;
+ struct ipu7_insys_stream_cfg stream;
+ } fw_msg;
+ struct list_head head;
+ dma_addr_t dma_addr;
+};
+
+struct ipu7_isys_csi2_config {
+ unsigned int nlanes;
+ unsigned int port;
+ enum v4l2_mbus_type bus_type;
+};
+
+struct sensor_async_sd {
+ struct v4l2_async_connection asc;
+ struct ipu7_isys_csi2_config csi2;
+};
+
+struct isys_fw_msgs *ipu7_get_fw_msg_buf(struct ipu7_isys_stream *stream);
+void ipu7_put_fw_msg_buf(struct ipu7_isys *isys, uintptr_t data);
+void ipu7_cleanup_fw_msg_bufs(struct ipu7_isys *isys);
+int isys_isr_one(struct ipu7_bus_device *adev);
+void ipu7_isys_setup_hw(struct ipu7_isys *isys);
+#endif /* IPU7_ISYS_H */
diff --git a/drivers/staging/media/ipu7/ipu7-mmu.c b/drivers/staging/media/ipu7/ipu7-mmu.c
new file mode 100644
index 000000000000..ded1986eb8ba
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-mmu.c
@@ -0,0 +1,853 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <asm/barrier.h>
+
+#include <linux/align.h>
+#include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/cacheflush.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/gfp.h>
+#include <linux/iopoll.h>
+#include <linux/iova.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
+#include <linux/pci.h>
+#include <linux/pfn.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#include "ipu7.h"
+#include "ipu7-dma.h"
+#include "ipu7-mmu.h"
+#include "ipu7-platform-regs.h"
+
+#define ISP_PAGE_SHIFT 12
+#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT)
+#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1U))
+
+#define ISP_L1PT_SHIFT 22
+#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1))
+
+#define ISP_L2PT_SHIFT 12
+#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK))))
+
+#define ISP_L1PT_PTES 1024U
+#define ISP_L2PT_PTES 1024U
+
+#define ISP_PADDR_SHIFT 12
+
+#define REG_L1_PHYS 0x0004 /* 27-bit pfn */
+#define REG_INFO 0x0008
+
+#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT)
+
+#define MMU_TLB_INVALIDATE_TIMEOUT 2000
+
+static __maybe_unused void mmu_irq_handler(struct ipu7_mmu *mmu)
+{
+ unsigned int i;
+ u32 irq_cause;
+
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ irq_cause = readl(mmu->mmu_hw[i].base + MMU_REG_IRQ_CAUSE);
+ pr_info("mmu %s irq_cause = 0x%x", mmu->mmu_hw[i].name,
+ irq_cause);
+ writel(0x1ffff, mmu->mmu_hw[i].base + MMU_REG_IRQ_CLEAR);
+ }
+}
+
+static void tlb_invalidate(struct ipu7_mmu *mmu)
+{
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+ u32 val;
+
+ spin_lock_irqsave(&mmu->ready_lock, flags);
+ if (!mmu->ready) {
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+ return;
+ }
+
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ writel(0xffffffffU, mmu->mmu_hw[i].base +
+ MMU_REG_INVALIDATE_0);
+
+ /* Need check with HW, use l1streams or l2streams */
+ if (mmu->mmu_hw[i].nr_l2streams > 32)
+ writel(0xffffffffU, mmu->mmu_hw[i].base +
+ MMU_REG_INVALIDATE_1);
+
+ /*
+ * The TLB invalidation is a "single cycle" (IOMMU clock cycles)
+ * When the actual MMIO write reaches the IPU TLB Invalidate
+ * register, wmb() will force the TLB invalidate out if the CPU
+ * attempts to update the IOMMU page table (or sooner).
+ */
+ wmb();
+
+ /* wait invalidation done */
+ ret = readl_poll_timeout_atomic(mmu->mmu_hw[i].base +
+ MMU_REG_INVALIDATION_STATUS,
+ val, !(val & 0x1U), 500,
+ MMU_TLB_INVALIDATE_TIMEOUT);
+ if (ret)
+ dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i);
+ }
+
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+}
+
+static dma_addr_t map_single(struct ipu7_mmu_info *mmu_info, void *ptr)
+{
+ dma_addr_t dma;
+
+ dma = dma_map_single(mmu_info->dev, ptr, PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(mmu_info->dev, dma))
+ return 0;
+
+ return dma;
+}
+
+static int get_dummy_page(struct ipu7_mmu_info *mmu_info)
+{
+ void *pt = (void *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ dma_addr_t dma;
+
+ if (!pt)
+ return -ENOMEM;
+
+ dev_dbg(mmu_info->dev, "dummy_page: get_zeroed_page() == %p\n", pt);
+
+ dma = map_single(mmu_info, pt);
+ if (!dma) {
+ dev_err(mmu_info->dev, "Failed to map dummy page\n");
+ goto err_free_page;
+ }
+
+ mmu_info->dummy_page = pt;
+ mmu_info->dummy_page_pteval = dma >> ISP_PAGE_SHIFT;
+
+ return 0;
+
+err_free_page:
+ free_page((unsigned long)pt);
+ return -ENOMEM;
+}
+
+static void free_dummy_page(struct ipu7_mmu_info *mmu_info)
+{
+ dma_unmap_single(mmu_info->dev,
+ TBL_PHYS_ADDR(mmu_info->dummy_page_pteval),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->dummy_page);
+}
+
+static int alloc_dummy_l2_pt(struct ipu7_mmu_info *mmu_info)
+{
+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ dma_addr_t dma;
+ unsigned int i;
+
+ if (!pt)
+ return -ENOMEM;
+
+ dev_dbg(mmu_info->dev, "dummy_l2: get_zeroed_page() = %p\n", pt);
+
+ dma = map_single(mmu_info, pt);
+ if (!dma) {
+ dev_err(mmu_info->dev, "Failed to map l2pt page\n");
+ goto err_free_page;
+ }
+
+ for (i = 0; i < ISP_L2PT_PTES; i++)
+ pt[i] = mmu_info->dummy_page_pteval;
+
+ mmu_info->dummy_l2_pt = pt;
+ mmu_info->dummy_l2_pteval = dma >> ISP_PAGE_SHIFT;
+
+ return 0;
+
+err_free_page:
+ free_page((unsigned long)pt);
+ return -ENOMEM;
+}
+
+static void free_dummy_l2_pt(struct ipu7_mmu_info *mmu_info)
+{
+ dma_unmap_single(mmu_info->dev,
+ TBL_PHYS_ADDR(mmu_info->dummy_l2_pteval),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->dummy_l2_pt);
+}
+
+static u32 *alloc_l1_pt(struct ipu7_mmu_info *mmu_info)
+{
+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ dma_addr_t dma;
+ unsigned int i;
+
+ if (!pt)
+ return NULL;
+
+ dev_dbg(mmu_info->dev, "alloc_l1: get_zeroed_page() = %p\n", pt);
+
+ for (i = 0; i < ISP_L1PT_PTES; i++)
+ pt[i] = mmu_info->dummy_l2_pteval;
+
+ dma = map_single(mmu_info, pt);
+ if (!dma) {
+ dev_err(mmu_info->dev, "Failed to map l1pt page\n");
+ goto err_free_page;
+ }
+
+ mmu_info->l1_pt_dma = dma >> ISP_PADDR_SHIFT;
+ dev_dbg(mmu_info->dev, "l1 pt %p mapped at %pad\n", pt, &dma);
+
+ return pt;
+
+err_free_page:
+ free_page((unsigned long)pt);
+ return NULL;
+}
+
+static u32 *alloc_l2_pt(struct ipu7_mmu_info *mmu_info)
+{
+ u32 *pt = (u32 *)get_zeroed_page(GFP_ATOMIC | GFP_DMA32);
+ unsigned int i;
+
+ if (!pt)
+ return NULL;
+
+ dev_dbg(mmu_info->dev, "alloc_l2: get_zeroed_page() = %p\n", pt);
+
+ for (i = 0; i < ISP_L1PT_PTES; i++)
+ pt[i] = mmu_info->dummy_page_pteval;
+
+ return pt;
+}
+
+static void l2_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t dummy, size_t size)
+{
+ unsigned int l2_entries;
+ unsigned int l2_idx;
+ unsigned long flags;
+ u32 l1_idx;
+ u32 *l2_pt;
+
+ spin_lock_irqsave(&mmu_info->lock, flags);
+ for (l1_idx = iova >> ISP_L1PT_SHIFT;
+ size > 0U && l1_idx < ISP_L1PT_PTES; l1_idx++) {
+ dev_dbg(mmu_info->dev,
+ "unmapping l2 pgtable (l1 index %u (iova 0x%8.8lx))\n",
+ l1_idx, iova);
+
+ if (mmu_info->l1_pt[l1_idx] == mmu_info->dummy_l2_pteval) {
+ dev_err(mmu_info->dev,
+ "unmap not mapped iova 0x%8.8lx l1 index %u\n",
+ iova, l1_idx);
+ continue;
+ }
+ l2_pt = mmu_info->l2_pts[l1_idx];
+
+ l2_entries = 0;
+ for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
+ size > 0U && l2_idx < ISP_L2PT_PTES; l2_idx++) {
+ phys_addr_t pteval = TBL_PHYS_ADDR(l2_pt[l2_idx]);
+
+ dev_dbg(mmu_info->dev,
+ "unmap l2 index %u with pteval 0x%p\n",
+ l2_idx, &pteval);
+ l2_pt[l2_idx] = mmu_info->dummy_page_pteval;
+
+ iova += ISP_PAGE_SIZE;
+ size -= ISP_PAGE_SIZE;
+
+ l2_entries++;
+ }
+
+ WARN_ON_ONCE(!l2_entries);
+ clflush_cache_range(&l2_pt[l2_idx - l2_entries],
+ sizeof(l2_pt[0]) * l2_entries);
+ }
+
+ WARN_ON_ONCE(size);
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+}
+
+static int l2_map(struct ipu7_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size)
+{
+ struct device *dev = mmu_info->dev;
+ unsigned int l2_entries;
+ u32 *l2_pt, *l2_virt;
+ unsigned int l2_idx;
+ unsigned long flags;
+ size_t mapped = 0;
+ dma_addr_t dma;
+ u32 l1_entry;
+ u32 l1_idx;
+ int err = 0;
+
+ spin_lock_irqsave(&mmu_info->lock, flags);
+
+ paddr = ALIGN(paddr, ISP_PAGE_SIZE);
+ for (l1_idx = iova >> ISP_L1PT_SHIFT;
+ size && l1_idx < ISP_L1PT_PTES; l1_idx++) {
+ dev_dbg(dev,
+ "mapping l2 page table for l1 index %u (iova %8.8x)\n",
+ l1_idx, (u32)iova);
+
+ l1_entry = mmu_info->l1_pt[l1_idx];
+ if (l1_entry == mmu_info->dummy_l2_pteval) {
+ l2_virt = mmu_info->l2_pts[l1_idx];
+ if (likely(!l2_virt)) {
+ l2_virt = alloc_l2_pt(mmu_info);
+ if (!l2_virt) {
+ err = -ENOMEM;
+ goto error;
+ }
+ }
+
+ dma = map_single(mmu_info, l2_virt);
+ if (!dma) {
+ dev_err(dev, "Failed to map l2pt page\n");
+ free_page((unsigned long)l2_virt);
+ err = -EINVAL;
+ goto error;
+ }
+
+ l1_entry = dma >> ISP_PADDR_SHIFT;
+
+ dev_dbg(dev, "page for l1_idx %u %p allocated\n",
+ l1_idx, l2_virt);
+ mmu_info->l1_pt[l1_idx] = l1_entry;
+ mmu_info->l2_pts[l1_idx] = l2_virt;
+
+ clflush_cache_range(&mmu_info->l1_pt[l1_idx],
+ sizeof(mmu_info->l1_pt[l1_idx]));
+ }
+
+ l2_pt = mmu_info->l2_pts[l1_idx];
+ l2_entries = 0;
+
+ for (l2_idx = (iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT;
+ size && l2_idx < ISP_L2PT_PTES; l2_idx++) {
+ l2_pt[l2_idx] = paddr >> ISP_PADDR_SHIFT;
+
+ dev_dbg(dev, "l2 index %u mapped as 0x%8.8x\n", l2_idx,
+ l2_pt[l2_idx]);
+
+ iova += ISP_PAGE_SIZE;
+ paddr += ISP_PAGE_SIZE;
+ mapped += ISP_PAGE_SIZE;
+ size -= ISP_PAGE_SIZE;
+
+ l2_entries++;
+ }
+
+ WARN_ON_ONCE(!l2_entries);
+ clflush_cache_range(&l2_pt[l2_idx - l2_entries],
+ sizeof(l2_pt[0]) * l2_entries);
+ }
+
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+
+ return 0;
+
+error:
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+ /* unroll mapping in case something went wrong */
+ if (mapped)
+ l2_unmap(mmu_info, iova - mapped, paddr - mapped, mapped);
+
+ return err;
+}
+
+static int __ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size)
+{
+ u32 iova_start = round_down(iova, ISP_PAGE_SIZE);
+ u32 iova_end = ALIGN(iova + size, ISP_PAGE_SIZE);
+
+ dev_dbg(mmu_info->dev,
+ "mapping iova 0x%8.8x--0x%8.8x, size %zu at paddr %pap\n",
+ iova_start, iova_end, size, &paddr);
+
+ return l2_map(mmu_info, iova_start, paddr, size);
+}
+
+static void __ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info,
+ unsigned long iova, size_t size)
+{
+ l2_unmap(mmu_info, iova, 0, size);
+}
+
+static int allocate_trash_buffer(struct ipu7_mmu *mmu)
+{
+ unsigned int n_pages = PFN_UP(IPU_MMUV2_TRASH_RANGE);
+ unsigned long iova_addr;
+ struct iova *iova;
+ unsigned int i;
+ dma_addr_t dma;
+ int ret;
+
+ /* Allocate 8MB in iova range */
+ iova = alloc_iova(&mmu->dmap->iovad, n_pages,
+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
+ if (!iova) {
+ dev_err(mmu->dev, "cannot allocate iova range for trash\n");
+ return -ENOMEM;
+ }
+
+ dma = dma_map_page(mmu->dmap->mmu_info->dev, mmu->trash_page, 0,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(mmu->dmap->mmu_info->dev, dma)) {
+ dev_err(mmu->dmap->mmu_info->dev, "Failed to map trash page\n");
+ ret = -ENOMEM;
+ goto out_free_iova;
+ }
+
+ mmu->pci_trash_page = dma;
+
+ /*
+ * Map the 8MB iova address range to the same physical trash page
+ * mmu->trash_page which is already reserved at the probe
+ */
+ iova_addr = iova->pfn_lo;
+ for (i = 0; i < n_pages; i++) {
+ ret = ipu7_mmu_map(mmu->dmap->mmu_info, PFN_PHYS(iova_addr),
+ mmu->pci_trash_page, PAGE_SIZE);
+ if (ret) {
+ dev_err(mmu->dev,
+ "mapping trash buffer range failed\n");
+ goto out_unmap;
+ }
+
+ iova_addr++;
+ }
+
+ mmu->iova_trash_page = PFN_PHYS(iova->pfn_lo);
+ dev_dbg(mmu->dev, "iova trash buffer for MMUID: %d is %u\n",
+ mmu->mmid, (unsigned int)mmu->iova_trash_page);
+ return 0;
+
+out_unmap:
+ ipu7_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+ dma_unmap_page(mmu->dmap->mmu_info->dev, mmu->pci_trash_page,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+out_free_iova:
+ __free_iova(&mmu->dmap->iovad, iova);
+ return ret;
+}
+
+static void __mmu_at_init(struct ipu7_mmu *mmu)
+{
+ struct ipu7_mmu_info *mmu_info;
+ unsigned int i;
+
+ mmu_info = mmu->dmap->mmu_info;
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i];
+ unsigned int j;
+
+ /* Write page table address per MMU */
+ writel((phys_addr_t)mmu_info->l1_pt_dma,
+ mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR);
+ dev_dbg(mmu->dev, "mmu %s base was set as %x\n", mmu_hw->name,
+ readl(mmu_hw->base + MMU_REG_PAGE_TABLE_BASE_ADDR));
+
+ /* Set info bits and axi_refill per MMU */
+ writel(mmu_hw->info_bits,
+ mmu_hw->base + MMU_REG_USER_INFO_BITS);
+ writel(mmu_hw->refill, mmu_hw->base + MMU_REG_AXI_REFILL_IF_ID);
+ writel(mmu_hw->collapse_en_bitmap,
+ mmu_hw->base + MMU_REG_COLLAPSE_ENABLE_BITMAP);
+
+ dev_dbg(mmu->dev, "mmu %s info_bits was set as %x\n",
+ mmu_hw->name,
+ readl(mmu_hw->base + MMU_REG_USER_INFO_BITS));
+
+ if (mmu_hw->at_sp_arb_cfg)
+ writel(mmu_hw->at_sp_arb_cfg,
+ mmu_hw->base + MMU_REG_AT_SP_ARB_CFG);
+
+ /* default irq configuration */
+ writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_MASK);
+ writel(0x3ff, mmu_hw->base + MMU_REG_IRQ_ENABLE);
+
+ /* Configure MMU TLB stream configuration for L1/L2 */
+ for (j = 0; j < mmu_hw->nr_l1streams; j++) {
+ writel(mmu_hw->l1_block_sz[j], mmu_hw->base +
+ mmu_hw->l1_block + 4U * j);
+ }
+
+ for (j = 0; j < mmu_hw->nr_l2streams; j++) {
+ writel(mmu_hw->l2_block_sz[j], mmu_hw->base +
+ mmu_hw->l2_block + 4U * j);
+ }
+
+ for (j = 0; j < mmu_hw->uao_p_num; j++) {
+ if (!mmu_hw->uao_p2tlb[j])
+ continue;
+ writel(mmu_hw->uao_p2tlb[j], mmu_hw->uao_base + 4U * j);
+ }
+ }
+}
+
+static void __mmu_zlx_init(struct ipu7_mmu *mmu)
+{
+ unsigned int i;
+
+ dev_dbg(mmu->dev, "mmu zlx init\n");
+
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ struct ipu7_mmu_hw *mmu_hw = &mmu->mmu_hw[i];
+ unsigned int j;
+
+ dev_dbg(mmu->dev, "mmu %s zlx init\n", mmu_hw->name);
+ for (j = 0; j < IPU_ZLX_POOL_NUM; j++) {
+ if (!mmu_hw->zlx_axi_pool[j])
+ continue;
+ writel(mmu_hw->zlx_axi_pool[j],
+ mmu_hw->zlx_base + ZLX_REG_AXI_POOL + j * 0x4U);
+ }
+
+ for (j = 0; j < mmu_hw->zlx_nr; j++) {
+ if (!mmu_hw->zlx_conf[j])
+ continue;
+
+ writel(mmu_hw->zlx_conf[j],
+ mmu_hw->zlx_base + ZLX_REG_CONF + j * 0x8U);
+ }
+
+ for (j = 0; j < mmu_hw->zlx_nr; j++) {
+ if (!mmu_hw->zlx_en[j])
+ continue;
+
+ writel(mmu_hw->zlx_en[j],
+ mmu_hw->zlx_base + ZLX_REG_EN + j * 0x8U);
+ }
+ }
+}
+
+int ipu7_mmu_hw_init(struct ipu7_mmu *mmu)
+{
+ unsigned long flags;
+
+ dev_dbg(mmu->dev, "IPU mmu hardware init\n");
+
+ /* Initialise the each MMU and ZLX */
+ __mmu_at_init(mmu);
+ __mmu_zlx_init(mmu);
+
+ if (!mmu->trash_page) {
+ int ret;
+
+ mmu->trash_page = alloc_page(GFP_KERNEL);
+ if (!mmu->trash_page) {
+ dev_err(mmu->dev, "insufficient memory for trash buffer\n");
+ return -ENOMEM;
+ }
+
+ ret = allocate_trash_buffer(mmu);
+ if (ret) {
+ __free_page(mmu->trash_page);
+ mmu->trash_page = NULL;
+ dev_err(mmu->dev, "trash buffer allocation failed\n");
+ return ret;
+ }
+ }
+
+ spin_lock_irqsave(&mmu->ready_lock, flags);
+ mmu->ready = true;
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_init, "INTEL_IPU7");
+
+static struct ipu7_mmu_info *ipu7_mmu_alloc(struct ipu7_device *isp)
+{
+ struct ipu7_mmu_info *mmu_info;
+ int ret;
+
+ mmu_info = kzalloc(sizeof(*mmu_info), GFP_KERNEL);
+ if (!mmu_info)
+ return NULL;
+
+ if (isp->secure_mode) {
+ mmu_info->aperture_start = IPU_FW_CODE_REGION_END;
+ mmu_info->aperture_end =
+ (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS);
+ } else {
+ mmu_info->aperture_start = IPU_FW_CODE_REGION_START;
+ mmu_info->aperture_end =
+ (dma_addr_t)DMA_BIT_MASK(IPU_MMU_ADDR_BITS_NON_SECURE);
+ }
+
+ mmu_info->pgsize_bitmap = SZ_4K;
+ mmu_info->dev = &isp->pdev->dev;
+
+ ret = get_dummy_page(mmu_info);
+ if (ret)
+ goto err_free_info;
+
+ ret = alloc_dummy_l2_pt(mmu_info);
+ if (ret)
+ goto err_free_dummy_page;
+
+ mmu_info->l2_pts = vzalloc(ISP_L2PT_PTES * sizeof(*mmu_info->l2_pts));
+ if (!mmu_info->l2_pts)
+ goto err_free_dummy_l2_pt;
+
+ /*
+ * We always map the L1 page table (a single page as well as
+ * the L2 page tables).
+ */
+ mmu_info->l1_pt = alloc_l1_pt(mmu_info);
+ if (!mmu_info->l1_pt)
+ goto err_free_l2_pts;
+
+ spin_lock_init(&mmu_info->lock);
+
+ dev_dbg(mmu_info->dev, "domain initialised\n");
+
+ return mmu_info;
+
+err_free_l2_pts:
+ vfree(mmu_info->l2_pts);
+err_free_dummy_l2_pt:
+ free_dummy_l2_pt(mmu_info);
+err_free_dummy_page:
+ free_dummy_page(mmu_info);
+err_free_info:
+ kfree(mmu_info);
+
+ return NULL;
+}
+
+void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&mmu->ready_lock, flags);
+ mmu->ready = false;
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_mmu_hw_cleanup, "INTEL_IPU7");
+
+static struct ipu7_dma_mapping *alloc_dma_mapping(struct ipu7_device *isp)
+{
+ struct ipu7_dma_mapping *dmap;
+ unsigned long base_pfn;
+
+ dmap = kzalloc(sizeof(*dmap), GFP_KERNEL);
+ if (!dmap)
+ return NULL;
+
+ dmap->mmu_info = ipu7_mmu_alloc(isp);
+ if (!dmap->mmu_info) {
+ kfree(dmap);
+ return NULL;
+ }
+
+ /* 0~64M is forbidden for uctile controller */
+ base_pfn = max_t(unsigned long, 1,
+ PFN_DOWN(dmap->mmu_info->aperture_start));
+ init_iova_domain(&dmap->iovad, SZ_4K, base_pfn);
+ dmap->mmu_info->dmap = dmap;
+
+ dev_dbg(&isp->pdev->dev, "alloc mapping\n");
+
+ iova_cache_get();
+
+ return dmap;
+}
+
+phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info,
+ dma_addr_t iova)
+{
+ phys_addr_t phy_addr;
+ unsigned long flags;
+ u32 *l2_pt;
+
+ spin_lock_irqsave(&mmu_info->lock, flags);
+ l2_pt = mmu_info->l2_pts[iova >> ISP_L1PT_SHIFT];
+ phy_addr = (phys_addr_t)l2_pt[(iova & ISP_L2PT_MASK) >> ISP_L2PT_SHIFT];
+ phy_addr <<= ISP_PAGE_SHIFT;
+ spin_unlock_irqrestore(&mmu_info->lock, flags);
+
+ return phy_addr;
+}
+
+void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova,
+ size_t size)
+{
+ unsigned int min_pagesz;
+
+ dev_dbg(mmu_info->dev, "unmapping iova 0x%lx size 0x%zx\n", iova, size);
+
+ /* find out the minimum page size supported */
+ min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap);
+
+ /*
+ * The virtual address and the size of the mapping must be
+ * aligned (at least) to the size of the smallest page supported
+ * by the hardware
+ */
+ if (!IS_ALIGNED(iova | size, min_pagesz)) {
+ dev_err(mmu_info->dev,
+ "unaligned: iova 0x%lx size 0x%zx min_pagesz 0x%x\n",
+ iova, size, min_pagesz);
+ return;
+ }
+
+ __ipu7_mmu_unmap(mmu_info, iova, size);
+}
+
+int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size)
+{
+ unsigned int min_pagesz;
+
+ if (mmu_info->pgsize_bitmap == 0UL)
+ return -ENODEV;
+
+ /* find out the minimum page size supported */
+ min_pagesz = 1U << __ffs(mmu_info->pgsize_bitmap);
+
+ /*
+ * both the virtual address and the physical one, as well as
+ * the size of the mapping, must be aligned (at least) to the
+ * size of the smallest page supported by the hardware
+ */
+ if (!IS_ALIGNED(iova | paddr | size, min_pagesz)) {
+ dev_err(mmu_info->dev,
+ "unaligned: iova %lx pa %pa size %zx min_pagesz %x\n",
+ iova, &paddr, size, min_pagesz);
+ return -EINVAL;
+ }
+
+ dev_dbg(mmu_info->dev, "map: iova 0x%lx pa %pa size 0x%zx\n",
+ iova, &paddr, size);
+
+ return __ipu7_mmu_map(mmu_info, iova, paddr, size);
+}
+
+static void ipu7_mmu_destroy(struct ipu7_mmu *mmu)
+{
+ struct ipu7_dma_mapping *dmap = mmu->dmap;
+ struct ipu7_mmu_info *mmu_info = dmap->mmu_info;
+ struct iova *iova;
+ u32 l1_idx;
+
+ if (mmu->iova_trash_page) {
+ iova = find_iova(&dmap->iovad, PHYS_PFN(mmu->iova_trash_page));
+ if (iova) {
+ /* unmap and free the trash buffer iova */
+ ipu7_mmu_unmap(mmu_info, PFN_PHYS(iova->pfn_lo),
+ PFN_PHYS(iova_size(iova)));
+ __free_iova(&dmap->iovad, iova);
+ } else {
+ dev_err(mmu->dev, "trash buffer iova not found.\n");
+ }
+
+ mmu->iova_trash_page = 0;
+ dma_unmap_page(mmu_info->dev, mmu->pci_trash_page,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ mmu->pci_trash_page = 0;
+ __free_page(mmu->trash_page);
+ }
+
+ for (l1_idx = 0; l1_idx < ISP_L1PT_PTES; l1_idx++) {
+ if (mmu_info->l1_pt[l1_idx] != mmu_info->dummy_l2_pteval) {
+ dma_unmap_single(mmu_info->dev,
+ TBL_PHYS_ADDR(mmu_info->l1_pt[l1_idx]),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->l2_pts[l1_idx]);
+ }
+ }
+
+ vfree(mmu_info->l2_pts);
+ free_dummy_page(mmu_info);
+ dma_unmap_single(mmu_info->dev, TBL_PHYS_ADDR(mmu_info->l1_pt_dma),
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+ free_page((unsigned long)mmu_info->dummy_l2_pt);
+ free_page((unsigned long)mmu_info->l1_pt);
+ kfree(mmu_info);
+}
+
+struct ipu7_mmu *ipu7_mmu_init(struct device *dev,
+ void __iomem *base, int mmid,
+ const struct ipu7_hw_variants *hw)
+{
+ struct ipu7_device *isp = pci_get_drvdata(to_pci_dev(dev));
+ struct ipu7_mmu_pdata *pdata;
+ struct ipu7_mmu *mmu;
+ unsigned int i;
+
+ if (hw->nr_mmus > IPU_MMU_MAX_NUM)
+ return ERR_PTR(-EINVAL);
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; i < hw->nr_mmus; i++) {
+ struct ipu7_mmu_hw *pdata_mmu = &pdata->mmu_hw[i];
+ const struct ipu7_mmu_hw *src_mmu = &hw->mmu_hw[i];
+
+ if (src_mmu->nr_l1streams > IPU_MMU_MAX_TLB_L1_STREAMS ||
+ src_mmu->nr_l2streams > IPU_MMU_MAX_TLB_L2_STREAMS)
+ return ERR_PTR(-EINVAL);
+
+ *pdata_mmu = *src_mmu;
+ pdata_mmu->base = base + src_mmu->offset;
+ pdata_mmu->zlx_base = base + src_mmu->zlx_offset;
+ pdata_mmu->uao_base = base + src_mmu->uao_offset;
+ }
+
+ mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL);
+ if (!mmu)
+ return ERR_PTR(-ENOMEM);
+
+ mmu->mmid = mmid;
+ mmu->mmu_hw = pdata->mmu_hw;
+ mmu->nr_mmus = hw->nr_mmus;
+ mmu->tlb_invalidate = tlb_invalidate;
+ mmu->ready = false;
+ INIT_LIST_HEAD(&mmu->vma_list);
+ spin_lock_init(&mmu->ready_lock);
+
+ mmu->dmap = alloc_dma_mapping(isp);
+ if (!mmu->dmap) {
+ dev_err(dev, "can't alloc dma mapping\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ return mmu;
+}
+
+void ipu7_mmu_cleanup(struct ipu7_mmu *mmu)
+{
+ struct ipu7_dma_mapping *dmap = mmu->dmap;
+
+ ipu7_mmu_destroy(mmu);
+ mmu->dmap = NULL;
+ iova_cache_put();
+ put_iova_domain(&dmap->iovad);
+ kfree(dmap);
+}
diff --git a/drivers/staging/media/ipu7/ipu7-mmu.h b/drivers/staging/media/ipu7/ipu7-mmu.h
new file mode 100644
index 000000000000..d85bb8ffc711
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-mmu.h
@@ -0,0 +1,414 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_MMU_H
+#define IPU7_MMU_H
+
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+
+struct device;
+struct page;
+struct ipu7_hw_variants;
+struct ipu7_mmu;
+struct ipu7_mmu_info;
+
+#define ISYS_MMID 0x1
+#define PSYS_MMID 0x0
+
+/* IPU7 for LNL */
+/* IS MMU Cmd RD */
+#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000
+#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3
+#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Cmd WR */
+#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000
+#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3
+#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Data WR Snoop */
+#define IPU7_IS_MMU_M0_OFFSET 0x276000
+#define IPU7_IS_MMU_M0_STREAM_NUM 8
+#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74
+
+/* IS MMU Data WR ISOC */
+#define IPU7_IS_MMU_M1_OFFSET 0x277000
+#define IPU7_IS_MMU_M1_STREAM_NUM 16
+#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94
+
+/* PS MMU FW RD */
+#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000
+#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20
+#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4
+
+/* PS MMU FW WR */
+#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000
+#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10
+#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c
+
+/* PS MMU FW Data RD VC0 */
+#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000
+#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40
+#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4
+
+/* PS MMU FW Data WR VC0 */
+#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000
+#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40
+#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4
+
+/* IS UAO UC RD */
+#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000
+#define IPU7_IS_UAO_UC_RD_PLANENUM 4
+
+/* IS UAO UC WR */
+#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000
+#define IPU7_IS_UAO_UC_WR_PLANENUM 4
+
+/* IS UAO M0 WR */
+#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000
+#define IPU7_IS_UAO_M0_WR_PLANENUM 8
+
+/* IS UAO M1 WR */
+#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000
+#define IPU7_IS_UAO_M1_WR_PLANENUM 16
+
+/* PS UAO FW RD */
+#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000
+#define IPU7_PS_UAO_FW_RD_PLANENUM 20
+
+/* PS UAO FW WR */
+#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000
+#define IPU7_PS_UAO_FW_WR_PLANENUM 16
+
+/* PS UAO SRT RD */
+#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000
+#define IPU7_PS_UAO_SRT_RD_PLANENUM 40
+
+/* PS UAO SRT WR */
+#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000
+#define IPU7_PS_UAO_SRT_WR_PLANENUM 40
+
+#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000
+#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000
+#define IPU7_IS_ZLX_M0_OFFSET 0x27a000
+#define IPU7_IS_ZLX_M1_OFFSET 0x27b000
+#define IPU7_IS_ZLX_UC_RD_NUM 4
+#define IPU7_IS_ZLX_UC_WR_NUM 4
+#define IPU7_IS_ZLX_M0_NUM 8
+#define IPU7_IS_ZLX_M1_NUM 16
+
+#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000
+#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000
+#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000
+#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000
+#define IPU7_PS_ZLX_DATA_RD_NUM 32
+#define IPU7_PS_ZLX_DATA_WR_NUM 32
+#define IPU7_PS_ZLX_FW_RD_NUM 16
+#define IPU7_PS_ZLX_FW_WR_NUM 10
+
+/* IPU7P5 for PTL */
+/* IS MMU Cmd RD */
+#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000
+#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3
+#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Cmd WR */
+#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000
+#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3
+#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Data WR Snoop */
+#define IPU7P5_IS_MMU_M0_OFFSET 0x276000
+#define IPU7P5_IS_MMU_M0_STREAM_NUM 16
+#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94
+
+/* IS MMU Data WR ISOC */
+#define IPU7P5_IS_MMU_M1_OFFSET 0x277000
+#define IPU7P5_IS_MMU_M1_STREAM_NUM 16
+#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94
+
+/* PS MMU FW RD */
+#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000
+#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16
+#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94
+
+/* PS MMU FW WR */
+#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000
+#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10
+#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c
+
+/* PS MMU FW Data RD VC0 */
+#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000
+#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22
+#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac
+
+/* PS MMU FW Data WR VC0 */
+#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000
+#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32
+#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4
+
+/* IS UAO UC RD */
+#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000
+#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4
+
+/* IS UAO UC WR */
+#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000
+#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4
+
+/* IS UAO M0 WR */
+#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000
+#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16
+
+/* IS UAO M1 WR */
+#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000
+#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16
+
+/* PS UAO FW RD */
+#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000
+#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16
+
+/* PS UAO FW WR */
+#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000
+#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10
+
+/* PS UAO SRT RD */
+#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000
+#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22
+
+/* PS UAO SRT WR */
+#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000
+#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32
+
+#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000
+#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000
+#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000
+#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000
+#define IPU7P5_IS_ZLX_UC_RD_NUM 4
+#define IPU7P5_IS_ZLX_UC_WR_NUM 4
+#define IPU7P5_IS_ZLX_M0_NUM 16
+#define IPU7P5_IS_ZLX_M1_NUM 16
+
+#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000
+#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000
+#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000
+#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000
+#define IPU7P5_PS_ZLX_DATA_RD_NUM 22
+#define IPU7P5_PS_ZLX_DATA_WR_NUM 32
+#define IPU7P5_PS_ZLX_FW_RD_NUM 16
+#define IPU7P5_PS_ZLX_FW_WR_NUM 10
+
+/* IS MMU Cmd RD */
+#define IPU8_IS_MMU_FW_RD_OFFSET 0x270000
+#define IPU8_IS_MMU_FW_RD_STREAM_NUM 3
+#define IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Cmd WR */
+#define IPU8_IS_MMU_FW_WR_OFFSET 0x271000
+#define IPU8_IS_MMU_FW_WR_STREAM_NUM 3
+#define IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Data WR Snoop */
+#define IPU8_IS_MMU_M0_OFFSET 0x272000
+#define IPU8_IS_MMU_M0_STREAM_NUM 16
+#define IPU8_IS_MMU_M0_L1_BLOCKNR_REG 0x54
+#define IPU8_IS_MMU_M0_L2_BLOCKNR_REG 0x94
+
+/* IS MMU Data WR ISOC */
+#define IPU8_IS_MMU_M1_OFFSET 0x273000
+#define IPU8_IS_MMU_M1_STREAM_NUM 16
+#define IPU8_IS_MMU_M1_L1_BLOCKNR_REG 0x54
+#define IPU8_IS_MMU_M1_L2_BLOCKNR_REG 0x94
+
+/* IS MMU UPIPE ISOC */
+#define IPU8_IS_MMU_UPIPE_OFFSET 0x274000
+#define IPU8_IS_MMU_UPIPE_STREAM_NUM 6
+#define IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG 0x54
+#define IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG 0x6c
+
+/* PS MMU FW RD */
+#define IPU8_PS_MMU_FW_RD_OFFSET 0x148000
+#define IPU8_PS_MMU_FW_RD_STREAM_NUM 12
+#define IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x84
+
+/* PS MMU FW WR */
+#define IPU8_PS_MMU_FW_WR_OFFSET 0x149000
+#define IPU8_PS_MMU_FW_WR_STREAM_NUM 8
+#define IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x74
+
+/* PS MMU FW Data RD VC0 */
+#define IPU8_PS_MMU_SRT_RD_OFFSET 0x14a000
+#define IPU8_PS_MMU_SRT_RD_STREAM_NUM 26
+#define IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54
+#define IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xbc
+
+/* PS MMU FW Data WR VC0 */
+#define IPU8_PS_MMU_SRT_WR_OFFSET 0x14b000
+#define IPU8_PS_MMU_SRT_WR_STREAM_NUM 26
+#define IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54
+#define IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xbc
+
+/* IS UAO UC RD */
+#define IPU8_IS_UAO_UC_RD_OFFSET 0x27a000
+#define IPU8_IS_UAO_UC_RD_PLANENUM 4
+
+/* IS UAO UC WR */
+#define IPU8_IS_UAO_UC_WR_OFFSET 0x27b000
+#define IPU8_IS_UAO_UC_WR_PLANENUM 4
+
+/* IS UAO M0 WR */
+#define IPU8_IS_UAO_M0_WR_OFFSET 0x27c000
+#define IPU8_IS_UAO_M0_WR_PLANENUM 16
+
+/* IS UAO M1 WR */
+#define IPU8_IS_UAO_M1_WR_OFFSET 0x27d000
+#define IPU8_IS_UAO_M1_WR_PLANENUM 16
+
+/* IS UAO UPIPE */
+#define IPU8_IS_UAO_UPIPE_OFFSET 0x27e000
+#define IPU8_IS_UAO_UPIPE_PLANENUM 6
+
+/* PS UAO FW RD */
+#define IPU8_PS_UAO_FW_RD_OFFSET 0x156000
+#define IPU8_PS_UAO_FW_RD_PLANENUM 12
+
+/* PS UAO FW WR */
+#define IPU8_PS_UAO_FW_WR_OFFSET 0x157000
+#define IPU8_PS_UAO_FW_WR_PLANENUM 8
+
+/* PS UAO SRT RD */
+#define IPU8_PS_UAO_SRT_RD_OFFSET 0x154000
+#define IPU8_PS_UAO_SRT_RD_PLANENUM 26
+
+/* PS UAO SRT WR */
+#define IPU8_PS_UAO_SRT_WR_OFFSET 0x155000
+#define IPU8_PS_UAO_SRT_WR_PLANENUM 26
+
+#define IPU8_IS_ZLX_UC_RD_OFFSET 0x275000
+#define IPU8_IS_ZLX_UC_WR_OFFSET 0x276000
+#define IPU8_IS_ZLX_M0_OFFSET 0x277000
+#define IPU8_IS_ZLX_M1_OFFSET 0x278000
+#define IPU8_IS_ZLX_UPIPE_OFFSET 0x279000
+#define IPU8_IS_ZLX_UC_RD_NUM 4
+#define IPU8_IS_ZLX_UC_WR_NUM 4
+#define IPU8_IS_ZLX_M0_NUM 16
+#define IPU8_IS_ZLX_M1_NUM 16
+#define IPU8_IS_ZLX_UPIPE_NUM 6
+
+#define IPU8_PS_ZLX_DATA_RD_OFFSET 0x14e000
+#define IPU8_PS_ZLX_DATA_WR_OFFSET 0x14f000
+#define IPU8_PS_ZLX_FW_RD_OFFSET 0x150000
+#define IPU8_PS_ZLX_FW_WR_OFFSET 0x151000
+#define IPU8_PS_ZLX_DATA_RD_NUM 26
+#define IPU8_PS_ZLX_DATA_WR_NUM 26
+#define IPU8_PS_ZLX_FW_RD_NUM 12
+#define IPU8_PS_ZLX_FW_WR_NUM 8
+
+#define MMU_REG_INVALIDATE_0 0x00
+#define MMU_REG_INVALIDATE_1 0x04
+#define MMU_REG_PAGE_TABLE_BASE_ADDR 0x08
+#define MMU_REG_USER_INFO_BITS 0x0c
+#define MMU_REG_AXI_REFILL_IF_ID 0x10
+#define MMU_REG_PW_EN_BITMAP 0x14
+#define MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18
+#define MMU_REG_GENERAL_REG 0x1c
+#define MMU_REG_AT_SP_ARB_CFG 0x20
+#define MMU_REG_INVALIDATION_STATUS 0x24
+#define MMU_REG_IRQ_LEVEL_NO_PULSE 0x28
+#define MMU_REG_IRQ_MASK 0x2c
+#define MMU_REG_IRQ_ENABLE 0x30
+#define MMU_REG_IRQ_EDGE 0x34
+#define MMU_REG_IRQ_CLEAR 0x38
+#define MMU_REG_IRQ_CAUSE 0x3c
+#define MMU_REG_CG_CTRL_BITS 0x40
+#define MMU_REG_RD_FIFOS_STATUS 0x44
+#define MMU_REG_WR_FIFOS_STATUS 0x48
+#define MMU_REG_COMMON_FIFOS_STATUS 0x4c
+#define MMU_REG_FSM_STATUS 0x50
+
+#define ZLX_REG_AXI_POOL 0x0
+#define ZLX_REG_EN 0x20
+#define ZLX_REG_CONF 0x24
+#define ZLX_REG_CG_CTRL 0x900
+#define ZLX_REG_FORCE_BYPASS 0x904
+
+struct ipu7_mmu_info {
+ struct device *dev;
+
+ u32 *l1_pt;
+ u32 l1_pt_dma;
+ u32 **l2_pts;
+
+ u32 *dummy_l2_pt;
+ u32 dummy_l2_pteval;
+ void *dummy_page;
+ u32 dummy_page_pteval;
+
+ dma_addr_t aperture_start;
+ dma_addr_t aperture_end;
+ unsigned long pgsize_bitmap;
+
+ spinlock_t lock; /* Serialize access to users */
+ struct ipu7_dma_mapping *dmap;
+};
+
+struct ipu7_mmu {
+ struct list_head node;
+
+ struct ipu7_mmu_hw *mmu_hw;
+ unsigned int nr_mmus;
+ unsigned int mmid;
+
+ phys_addr_t pgtbl;
+ struct device *dev;
+
+ struct ipu7_dma_mapping *dmap;
+ struct list_head vma_list;
+
+ struct page *trash_page;
+ dma_addr_t pci_trash_page; /* IOVA from PCI DMA services (parent) */
+ dma_addr_t iova_trash_page; /* IOVA for IPU child nodes to use */
+
+ bool ready;
+ spinlock_t ready_lock; /* Serialize access to bool ready */
+
+ void (*tlb_invalidate)(struct ipu7_mmu *mmu);
+};
+
+struct ipu7_mmu *ipu7_mmu_init(struct device *dev,
+ void __iomem *base, int mmid,
+ const struct ipu7_hw_variants *hw);
+void ipu7_mmu_cleanup(struct ipu7_mmu *mmu);
+int ipu7_mmu_hw_init(struct ipu7_mmu *mmu);
+void ipu7_mmu_hw_cleanup(struct ipu7_mmu *mmu);
+int ipu7_mmu_map(struct ipu7_mmu_info *mmu_info, unsigned long iova,
+ phys_addr_t paddr, size_t size);
+void ipu7_mmu_unmap(struct ipu7_mmu_info *mmu_info, unsigned long iova,
+ size_t size);
+phys_addr_t ipu7_mmu_iova_to_phys(struct ipu7_mmu_info *mmu_info,
+ dma_addr_t iova);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7-platform-regs.h b/drivers/staging/media/ipu7/ipu7-platform-regs.h
new file mode 100644
index 000000000000..eeadc886a8cf
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-platform-regs.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2018 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_PLATFORM_REGS_H
+#define IPU7_PLATFORM_REGS_H
+
+#define IS_BASE 0x230000
+#define IS_UC_CTRL_BASE (IS_BASE + 0x0)
+
+#define PS_BASE 0x130000
+#define PS_UC_CTRL_BASE (PS_BASE + 0x0)
+
+/*
+ * bit 0: IRQ from FW,
+ * bit 1, 2 and 3: IRQ from HW
+ */
+#define TO_SW_IRQ_MASK 0xf
+#define TO_SW_IRQ_FW BIT(0)
+
+#define FW_CODE_BASE 0x0
+#define FW_DATA_BASE 0x4
+#define PRINTF_EN_THROUGH_TRACE 0x3004
+#define PRINTF_EN_DIRECTLY_TO_DDR 0x3008
+#define PRINTF_DDR_BASE_ADDR 0x300c
+#define PRINTF_DDR_SIZE 0x3010
+#define PRINTF_DDR_NEXT_ADDR 0x3014
+#define PRINTF_STATUS 0x3018
+#define PRINTF_AXI_CNTL 0x301c
+#define PRINTF_MSG_LENGTH 0x3020
+#define TO_SW_IRQ_CNTL_EDGE 0x4000
+#define TO_SW_IRQ_CNTL_MASK_N 0x4004
+#define TO_SW_IRQ_CNTL_STATUS 0x4008
+#define TO_SW_IRQ_CNTL_CLEAR 0x400c
+#define TO_SW_IRQ_CNTL_ENABLE 0x4010
+#define TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x4014
+#define ERR_IRQ_CNTL_EDGE 0x4018
+#define ERR_IRQ_CNTL_MASK_N 0x401c
+#define ERR_IRQ_CNTL_STATUS 0x4020
+#define ERR_IRQ_CNTL_CLEAR 0x4024
+#define ERR_IRQ_CNTL_ENABLE 0x4028
+#define ERR_IRQ_CNTL_LEVEL_NOT_PULSE 0x402c
+#define LOCAL_DMEM_BASE_ADDR 0x1300000
+
+/*
+ * IS_UC_TO_SW irqs
+ * bit 0: IRQ from local FW
+ * bit 1~3: IRQ from HW
+ */
+#define IS_UC_TO_SW_IRQ_MASK 0xf
+
+#define IPU_ISYS_SPC_OFFSET 0x210000
+#define IPU7_PSYS_SPC_OFFSET 0x118000
+#define IPU_ISYS_DMEM_OFFSET 0x200000
+#define IPU_PSYS_DMEM_OFFSET 0x100000
+
+#define IPU7_ISYS_CSI_PORT_NUM 4
+
+/* IRQ-related registers in PSYS */
+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_EDGE 0x134000
+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_MASK 0x134004
+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_STATUS 0x134008
+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_CLEAR 0x13400c
+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_ENABLE 0x134010
+#define IPU_REG_PSYS_TO_SW_IRQ_CNTL_LEVEL_NOT_PULSE 0x134014
+#define IRQ_FROM_LOCAL_FW BIT(0)
+
+/*
+ * psys subdomains power request regs
+ */
+enum ipu7_device_buttress_psys_domain_pos {
+ IPU_PSYS_SUBDOMAIN_LB = 0,
+ IPU_PSYS_SUBDOMAIN_BB = 1,
+};
+
+#define IPU7_PSYS_DOMAIN_POWER_MASK (BIT(IPU_PSYS_SUBDOMAIN_LB) | \
+ BIT(IPU_PSYS_SUBDOMAIN_BB))
+#define IPU8_PSYS_DOMAIN_POWER_MASK BIT(IPU_PSYS_SUBDOMAIN_LB)
+#define IPU_PSYS_DOMAIN_POWER_IN_PROGRESS BIT(31)
+
+#endif /* IPU7_PLATFORM_REGS_H */
diff --git a/drivers/staging/media/ipu7/ipu7-syscom.c b/drivers/staging/media/ipu7/ipu7-syscom.c
new file mode 100644
index 000000000000..3f9f9c5c3cce
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-syscom.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/export.h>
+#include <linux/io.h>
+
+#include "abi/ipu7_fw_syscom_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-syscom.h"
+
+static void __iomem *ipu7_syscom_get_indices(struct ipu7_syscom_context *ctx,
+ u32 q)
+{
+ return ctx->queue_indices + (q * sizeof(struct syscom_queue_indices_s));
+}
+
+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q)
+{
+ struct syscom_queue_config *queue_params = &ctx->queue_configs[q];
+ void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q);
+ u32 write_index = readl(queue_indices +
+ offsetof(struct syscom_queue_indices_s,
+ write_index));
+ u32 read_index = readl(queue_indices +
+ offsetof(struct syscom_queue_indices_s,
+ read_index));
+ void *token = NULL;
+
+ if (q < ctx->num_output_queues) {
+ /* Output queue */
+ bool empty = (write_index == read_index);
+
+ if (!empty)
+ token = queue_params->token_array_mem +
+ read_index *
+ queue_params->token_size_in_bytes;
+ } else {
+ /* Input queue */
+ bool full = (read_index == ((write_index + 1U) %
+ (u32)queue_params->max_capacity));
+
+ if (!full)
+ token = queue_params->token_array_mem +
+ write_index * queue_params->token_size_in_bytes;
+ }
+ return token;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_token, "INTEL_IPU7");
+
+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q)
+{
+ struct syscom_queue_config *queue_params = &ctx->queue_configs[q];
+ void __iomem *queue_indices = ipu7_syscom_get_indices(ctx, q);
+ u32 offset, index;
+
+ if (q < ctx->num_output_queues)
+ /* Output queue */
+ offset = offsetof(struct syscom_queue_indices_s, read_index);
+
+ else
+ /* Input queue */
+ offset = offsetof(struct syscom_queue_indices_s, write_index);
+
+ index = readl(queue_indices + offset);
+ writel((index + 1U) % queue_params->max_capacity,
+ queue_indices + offset);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_put_token, "INTEL_IPU7");
+
+struct syscom_queue_params_config *
+ipu7_syscom_get_queue_config(struct syscom_config_s *config)
+{
+ return (struct syscom_queue_params_config *)(&config[1]);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_syscom_get_queue_config, "INTEL_IPU7");
diff --git a/drivers/staging/media/ipu7/ipu7-syscom.h b/drivers/staging/media/ipu7/ipu7-syscom.h
new file mode 100644
index 000000000000..e1fbe3b7914e
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7-syscom.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_SYSCOM_H
+#define IPU7_SYSCOM_H
+
+#include <linux/types.h>
+
+struct syscom_config_s;
+struct syscom_queue_params_config;
+
+struct syscom_queue_config {
+ void *token_array_mem;
+ u32 queue_size;
+ u16 token_size_in_bytes;
+ u16 max_capacity;
+};
+
+struct ipu7_syscom_context {
+ u16 num_input_queues;
+ u16 num_output_queues;
+ struct syscom_queue_config *queue_configs;
+ void __iomem *queue_indices;
+ dma_addr_t queue_mem_dma_addr;
+ void *queue_mem;
+ u32 queue_mem_size;
+};
+
+void ipu7_syscom_put_token(struct ipu7_syscom_context *ctx, int q);
+void *ipu7_syscom_get_token(struct ipu7_syscom_context *ctx, int q);
+struct syscom_queue_params_config *
+ipu7_syscom_get_queue_config(struct syscom_config_s *config);
+#endif
diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c
new file mode 100644
index 000000000000..1b4f01db13ca
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7.c
@@ -0,0 +1,2783 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/bug.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+
+#include <media/ipu-bridge.h>
+
+#include "abi/ipu7_fw_common_abi.h"
+
+#include "ipu7.h"
+#include "ipu7-bus.h"
+#include "ipu7-buttress.h"
+#include "ipu7-buttress-regs.h"
+#include "ipu7-cpd.h"
+#include "ipu7-dma.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-mmu.h"
+#include "ipu7-platform-regs.h"
+
+#define IPU_PCI_BAR 0
+#define IPU_PCI_PBBAR 4
+
+static const unsigned int ipu7_csi_offsets[] = {
+ IPU_CSI_PORT_A_ADDR_OFFSET,
+ IPU_CSI_PORT_B_ADDR_OFFSET,
+ IPU_CSI_PORT_C_ADDR_OFFSET,
+ IPU_CSI_PORT_D_ADDR_OFFSET,
+};
+
+static struct ipu_isys_internal_pdata ipu7p5_isys_ipdata = {
+ .csi2 = {
+ .gpreg = IS_IO_CSI2_GPREGS_BASE,
+ },
+ .hw_variant = {
+ .offset = IPU_UNIFIED_OFFSET,
+ .nr_mmus = IPU7P5_IS_MMU_NUM,
+ .mmu_hw = {
+ {
+ .name = "IS_FW_RD",
+ .offset = IPU7P5_IS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET,
+ .info_bits = 0x20005101,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x8, 0xa,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4,
+ },
+ .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 0, 1, 0, 0
+ },
+ .zlx_conf = {
+ 0x0,
+ },
+ .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000049,
+ 0x0000004c,
+ 0x0000004d,
+ 0x00000000,
+ },
+ },
+ {
+ .name = "IS_FW_WR",
+ .offset = IPU7P5_IS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET,
+ .info_bits = 0x20005001,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x8, 0xa,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4,
+ },
+ .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 0, 1, 1, 0,
+ },
+ .zlx_conf = {
+ 0x0,
+ 0x00010101,
+ 0x00010101,
+ 0x0,
+ },
+ .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000049,
+ 0x0000004a,
+ 0x0000004b,
+ 0x00000000,
+ },
+ },
+ {
+ .name = "IS_DATA_WR_ISOC",
+ .offset = IPU7P5_IS_MMU_M0_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET,
+ .info_bits = 0x20004e01,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .zlx_nr = IPU7P5_IS_ZLX_M0_NUM,
+ .zlx_axi_pool = {
+ 0x00000f10,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ },
+ .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000041,
+ 0x00000042,
+ 0x00000043,
+ 0x00000044,
+ 0x00000041,
+ 0x00000042,
+ 0x00000043,
+ 0x00000044,
+ 0x00000041,
+ 0x00000042,
+ 0x00000043,
+ 0x00000044,
+ 0x00000041,
+ 0x00000042,
+ 0x00000043,
+ 0x00000044,
+ },
+ },
+ {
+ .name = "IS_DATA_WR_SNOOP",
+ .offset = IPU7P5_IS_MMU_M1_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET,
+ .info_bits = 0x20004f01,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .zlx_nr = IPU7P5_IS_ZLX_M1_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ },
+ .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000045,
+ 0x00000046,
+ 0x00000047,
+ 0x00000048,
+ 0x00000045,
+ 0x00000046,
+ 0x00000047,
+ 0x00000048,
+ 0x00000045,
+ 0x00000046,
+ 0x00000047,
+ 0x00000048,
+ 0x00000045,
+ 0x00000046,
+ 0x00000047,
+ 0x00000048,
+ },
+ },
+ },
+ .cdc_fifos = 3,
+ .cdc_fifo_threshold = {6, 8, 2},
+ .dmem_offset = IPU_ISYS_DMEM_OFFSET,
+ .spc_offset = IPU_ISYS_SPC_OFFSET,
+ },
+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN,
+};
+
+static struct ipu_psys_internal_pdata ipu7p5_psys_ipdata = {
+ .hw_variant = {
+ .offset = IPU_UNIFIED_OFFSET,
+ .nr_mmus = IPU7P5_PS_MMU_NUM,
+ .mmu_hw = {
+ {
+ .name = "PS_FW_RD",
+ .offset = IPU7P5_PS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET,
+ .info_bits = 0x20004001,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000d,
+ 0x0000000f,
+ 0x00000011,
+ 0x00000012,
+ 0x00000013,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x00000019,
+ 0x0000001a,
+ 0x0000001a,
+ 0x0000001a,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 0, 1, 0, 0, 1, 1, 0, 0,
+ 0, 1, 1, 0, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00000000,
+ 0x00010101,
+ 0x00000000,
+ 0x00000000,
+ 0x00010101,
+ 0x00010101,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00010101,
+ 0x00010101,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ },
+ .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x0000002e,
+ 0x00000035,
+ 0x00000036,
+ 0x00000031,
+ 0x00000037,
+ 0x00000038,
+ 0x00000039,
+ 0x00000032,
+ 0x00000033,
+ 0x0000003a,
+ 0x0000003b,
+ 0x0000003c,
+ 0x00000034,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ },
+ {
+ .name = "PS_FW_WR",
+ .offset = IPU7P5_PS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET,
+ .info_bits = 0x20003e01,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000d,
+ 0x0000000e,
+ 0x0000000f,
+ 0x00000010,
+ 0x00000010,
+ 0x00000010,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ },
+ .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00000000,
+ 0x00010101,
+ 0x00010101,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ },
+ .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x0000002e,
+ 0x0000002f,
+ 0x00000030,
+ 0x00000031,
+ 0x00000032,
+ 0x00000033,
+ 0x00000034,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ },
+ {
+ .name = "PS_DATA_RD",
+ .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET,
+ .info_bits = 0x20003f01,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000b,
+ 0x0000000d,
+ 0x0000000f,
+ 0x00000013,
+ 0x00000017,
+ 0x00000019,
+ 0x0000001b,
+ 0x0000001d,
+ 0x0000001f,
+ 0x0000002b,
+ 0x00000033,
+ 0x0000003f,
+ 0x00000047,
+ 0x00000049,
+ 0x0000004b,
+ 0x0000004c,
+ 0x0000004d,
+ 0x0000004e,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ 0x00000020,
+ 0x00000022,
+ 0x00000024,
+ 0x00000026,
+ 0x00000028,
+ 0x0000002a,
+ },
+ .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00030303,
+ 0x00010101,
+ 0x00010101,
+ 0x00030202,
+ 0x00010101,
+ 0x00010101,
+ 0x00030303,
+ 0x00030303,
+ 0x00010101,
+ 0x00030800,
+ 0x00030500,
+ 0x00020101,
+ 0x00042000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00020400,
+ 0x00010101,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ },
+ .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x0000001c,
+ 0x0000001d,
+ 0x0000001e,
+ 0x0000001f,
+ 0x00000020,
+ 0x00000021,
+ 0x00000022,
+ 0x00000023,
+ 0x00000024,
+ 0x00000025,
+ 0x00000026,
+ 0x00000027,
+ 0x00000028,
+ 0x00000029,
+ 0x0000002a,
+ 0x0000002b,
+ 0x0000002c,
+ 0x0000002d,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ },
+ },
+ {
+ .name = "PS_DATA_WR",
+ .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET,
+ .info_bits = 0x20003d01,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000006,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ 0x00000020,
+ 0x00000022,
+ 0x00000024,
+ 0x00000028,
+ 0x0000002a,
+ 0x00000036,
+ 0x0000003e,
+ 0x00000040,
+ 0x00000042,
+ 0x0000004e,
+ 0x00000056,
+ 0x0000005c,
+ 0x00000068,
+ 0x00000070,
+ 0x00000076,
+ 0x00000077,
+ 0x00000078,
+ 0x00000079,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000006,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ 0x00000020,
+ 0x00000022,
+ 0x00000024,
+ 0x00000028,
+ 0x0000002a,
+ 0x00000036,
+ 0x0000003e,
+ 0x00000040,
+ 0x00000042,
+ 0x0000004e,
+ 0x00000056,
+ 0x0000005c,
+ 0x00000068,
+ 0x00000070,
+ 0x00000076,
+ 0x00000077,
+ 0x00000078,
+ 0x00000079,
+ },
+ .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f50,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00010102,
+ 0x00030103,
+ 0x00030103,
+ 0x00010101,
+ 0x00010101,
+ 0x00030101,
+ 0x00010101,
+ 0x38010101,
+ 0x00000000,
+ 0x00000000,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x00030303,
+ 0x00010101,
+ 0x00042000,
+ 0x00031000,
+ 0x00010101,
+ 0x00010101,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ },
+ .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000002,
+ 0x00000003,
+ 0x00000004,
+ 0x00000005,
+ 0x00000006,
+ 0x00000007,
+ 0x00000008,
+ 0x00000009,
+ 0x0000000a,
+ 0x0000000b,
+ 0x0000000c,
+ 0x0000000d,
+ 0x0000000e,
+ 0x0000000f,
+ 0x00000010,
+ 0x00000011,
+ 0x00000012,
+ 0x00000013,
+ 0x00000014,
+ 0x00000015,
+ 0x00000016,
+ 0x00000017,
+ 0x00000018,
+ 0x00000019,
+ 0x0000001a,
+ 0x0000001b,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ },
+ },
+ },
+ .dmem_offset = IPU_PSYS_DMEM_OFFSET,
+ },
+};
+
+static struct ipu_isys_internal_pdata ipu7_isys_ipdata = {
+ .csi2 = {
+ .gpreg = IS_IO_CSI2_GPREGS_BASE,
+ },
+ .hw_variant = {
+ .offset = IPU_UNIFIED_OFFSET,
+ .nr_mmus = IPU7_IS_MMU_NUM,
+ .mmu_hw = {
+ {
+ .name = "IS_FW_RD",
+ .offset = IPU7_IS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET,
+ .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET,
+ .info_bits = 0x20006701,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x8, 0xa,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4,
+ },
+ .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 0, 0, 0, 0
+ },
+ .zlx_conf = {
+ 0x0, 0x0, 0x0, 0x0,
+ },
+ .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000061,
+ 0x00000064,
+ 0x00000065,
+ },
+ },
+ {
+ .name = "IS_FW_WR",
+ .offset = IPU7_IS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET,
+ .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET,
+ .info_bits = 0x20006801,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x8, 0xa,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4,
+ },
+ .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 0, 1, 1, 0,
+ },
+ .zlx_conf = {
+ 0x0,
+ 0x00010101,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000061,
+ 0x00000062,
+ 0x00000063,
+ },
+ },
+ {
+ .name = "IS_DATA_WR_ISOC",
+ .offset = IPU7_IS_MMU_M0_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_M0_OFFSET,
+ .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET,
+ .info_bits = 0x20006601,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe,
+ },
+ .zlx_nr = IPU7_IS_ZLX_M0_NUM,
+ .zlx_axi_pool = {
+ 0x00000f10,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x00010103,
+ 0x00010103,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000049,
+ 0x0000004a,
+ 0x0000004b,
+ 0x0000004c,
+ 0x0000004d,
+ 0x0000004e,
+ 0x0000004f,
+ 0x00000050,
+ },
+ },
+ {
+ .name = "IS_DATA_WR_SNOOP",
+ .offset = IPU7_IS_MMU_M1_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_M1_OFFSET,
+ .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET,
+ .info_bits = 0x20006901,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x3, 0x6, 0x9, 0xc,
+ 0xe, 0x10, 0x12, 0x14, 0x16,
+ 0x18, 0x1a, 0x1c, 0x1e, 0x20,
+ 0x22,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c,
+ 0x1e,
+ },
+ .zlx_nr = IPU7_IS_ZLX_M1_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000051,
+ 0x00000052,
+ 0x00000053,
+ 0x00000054,
+ 0x00000055,
+ 0x00000056,
+ 0x00000057,
+ 0x00000058,
+ 0x00000059,
+ 0x0000005a,
+ 0x0000005b,
+ 0x0000005c,
+ 0x0000005d,
+ 0x0000005e,
+ 0x0000005f,
+ 0x00000060,
+ },
+ },
+ },
+ .cdc_fifos = 3,
+ .cdc_fifo_threshold = {6, 8, 2},
+ .dmem_offset = IPU_ISYS_DMEM_OFFSET,
+ .spc_offset = IPU_ISYS_SPC_OFFSET,
+ },
+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN,
+};
+
+static struct ipu_psys_internal_pdata ipu7_psys_ipdata = {
+ .hw_variant = {
+ .offset = IPU_UNIFIED_OFFSET,
+ .nr_mmus = IPU7_PS_MMU_NUM,
+ .mmu_hw = {
+ {
+ .name = "PS_FW_RD",
+ .offset = IPU7_PS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET,
+ .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET,
+ .info_bits = 0x20004801,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0, 0x8, 0xa, 0xc, 0xd,
+ 0xf, 0x11, 0x12, 0x13, 0x14,
+ 0x16, 0x18, 0x19, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c,
+ 0x1e, 0x20, 0x22, 0x24, 0x26,
+ },
+ .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x0,
+ },
+ .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000036,
+ 0x0000003d,
+ 0x0000003e,
+ 0x00000039,
+ 0x0000003f,
+ 0x00000040,
+ 0x00000041,
+ 0x0000003a,
+ 0x0000003b,
+ 0x00000042,
+ 0x00000043,
+ 0x00000044,
+ 0x0000003c,
+ },
+ },
+ {
+ .name = "PS_FW_WR",
+ .offset = IPU7_PS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET,
+ .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET,
+ .info_bits = 0x20004601,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0, 0x8, 0xa, 0xc, 0xd,
+ 0xe, 0xf, 0x10, 0x10, 0x10,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ },
+ .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 0, 1, 1, 0, 0, 0, 0, 0,
+ 0, 0,
+ },
+ .zlx_conf = {
+ 0x0,
+ 0x00010101,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000036,
+ 0x00000037,
+ 0x00000038,
+ 0x00000039,
+ 0x0000003a,
+ 0x0000003b,
+ 0x0000003c,
+ },
+ },
+ {
+ .name = "PS_DATA_RD",
+ .offset = IPU7_PS_MMU_SRT_RD_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET,
+ .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET,
+ .info_bits = 0x20004701,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x4, 0x6, 0x8, 0xb,
+ 0xd, 0xf, 0x11, 0x13, 0x15,
+ 0x17, 0x23, 0x2b, 0x37, 0x3f,
+ 0x41, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x53, 0x55, 0x57,
+ 0x59, 0x5b, 0x5d, 0x5f, 0x61,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c,
+ 0x1e, 0x20, 0x22, 0x24, 0x26,
+ 0x28, 0x2a, 0x2c, 0x2e, 0x30,
+ 0x32, 0x34, 0x36, 0x38, 0x3a,
+ 0x3c, 0x3e, 0x40, 0x42, 0x44,
+ 0x46, 0x48, 0x4a, 0x4c, 0x4e,
+ },
+ .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00030303,
+ 0x00010101,
+ 0x00010101,
+ 0x00030202,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00030800,
+ 0x00030500,
+ 0x00020101,
+ 0x00042000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00020400,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000022,
+ 0x00000023,
+ 0x00000024,
+ 0x00000025,
+ 0x00000026,
+ 0x00000027,
+ 0x00000028,
+ 0x00000029,
+ 0x0000002a,
+ 0x0000002b,
+ 0x0000002c,
+ 0x0000002d,
+ 0x0000002e,
+ 0x0000002f,
+ 0x00000030,
+ 0x00000031,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0000001e,
+ 0x0000001f,
+ 0x00000020,
+ 0x00000021,
+ 0x00000032,
+ 0x00000033,
+ 0x00000034,
+ 0x00000035,
+ },
+ },
+ {
+ .name = "PS_DATA_WR",
+ .offset = IPU7_PS_MMU_SRT_WR_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET,
+ .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET,
+ .info_bits = 0x20004501,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x2, 0x6, 0xa, 0xc,
+ 0xe, 0x10, 0x12, 0x14, 0x16,
+ 0x18, 0x1a, 0x1c, 0x1e, 0x20,
+ 0x22, 0x24, 0x26, 0x32, 0x3a,
+ 0x3c, 0x3e, 0x4a, 0x52, 0x58,
+ 0x64, 0x6c, 0x72, 0x7e, 0x86,
+ 0x8c, 0x8d, 0x8e, 0x8f, 0x90,
+ 0x91, 0x92, 0x94, 0x96, 0x98,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c,
+ 0x1e, 0x20, 0x22, 0x24, 0x26,
+ 0x28, 0x2a, 0x2c, 0x2e, 0x30,
+ 0x32, 0x34, 0x36, 0x38, 0x3a,
+ 0x3c, 0x3e, 0x40, 0x42, 0x44,
+ 0x46, 0x48, 0x4a, 0x4c, 0x4e,
+ },
+ .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f50,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00010102,
+ 0x00030103,
+ 0x00030103,
+ 0x00010101,
+ 0x00010101,
+ 0x00030101,
+ 0x00010101,
+ 0x38010101,
+ 0x0,
+ 0x0,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x00010101,
+ 0x00042000,
+ 0x00031000,
+ 0x00010101,
+ 0x00010101,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x0,
+ 0x0,
+ },
+ .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000002,
+ 0x00000003,
+ 0x00000004,
+ 0x00000005,
+ 0x00000006,
+ 0x00000007,
+ 0x00000008,
+ 0x00000009,
+ 0x0000000a,
+ 0x0000000b,
+ 0x0000000c,
+ 0x0000000d,
+ 0x0000000e,
+ 0x0000000f,
+ 0x00000010,
+ 0x00000011,
+ 0x00000012,
+ 0x00000013,
+ 0x00000014,
+ 0x00000015,
+ 0x00000016,
+ 0x00000017,
+ 0x00000018,
+ 0x00000019,
+ 0x0000001a,
+ 0x0000001b,
+ 0x0000001c,
+ 0x0000001d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0000001e,
+ 0x0000001f,
+ 0x00000020,
+ 0x00000021,
+ },
+ },
+ },
+ .dmem_offset = IPU_PSYS_DMEM_OFFSET,
+ },
+};
+
+static struct ipu_isys_internal_pdata ipu8_isys_ipdata = {
+ .csi2 = {
+ .gpreg = IPU8_IS_IO_CSI2_GPREGS_BASE,
+ },
+ .hw_variant = {
+ .offset = IPU_UNIFIED_OFFSET,
+ .nr_mmus = IPU8_IS_MMU_NUM,
+ .mmu_hw = {
+ {
+ .name = "IS_FW_RD",
+ .offset = IPU8_IS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU8_IS_ZLX_UC_RD_OFFSET,
+ .uao_offset = IPU8_IS_UAO_UC_RD_OFFSET,
+ .info_bits = 0x20005101,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_IS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU8_IS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_IS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU8_IS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x8, 0xa,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4,
+ },
+ .zlx_nr = IPU8_IS_ZLX_UC_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 0, 1, 0, 0
+ },
+ .zlx_conf = {
+ 0, 2, 0, 0
+ },
+ .uao_p_num = IPU8_IS_UAO_UC_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000049,
+ 0x0000004c,
+ 0x0000004d,
+ 0x00000000,
+ },
+ },
+ {
+ .name = "IS_FW_WR",
+ .offset = IPU8_IS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU8_IS_ZLX_UC_WR_OFFSET,
+ .uao_offset = IPU8_IS_UAO_UC_WR_OFFSET,
+ .info_bits = 0x20005001,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_IS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU8_IS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_IS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU8_IS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x8, 0xa,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4,
+ },
+ .zlx_nr = IPU8_IS_ZLX_UC_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 0, 1, 1, 0,
+ },
+ .zlx_conf = {
+ 0x0,
+ 0x2,
+ 0x2,
+ 0x0,
+ },
+ .uao_p_num = IPU8_IS_UAO_UC_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000049,
+ 0x0000004a,
+ 0x0000004b,
+ 0x00000000,
+ },
+ },
+ {
+ .name = "IS_DATA_WR_ISOC",
+ .offset = IPU8_IS_MMU_M0_OFFSET,
+ .zlx_offset = IPU8_IS_ZLX_M0_OFFSET,
+ .uao_offset = IPU8_IS_UAO_M0_WR_OFFSET,
+ .info_bits = 0x20004e01,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_IS_MMU_M0_L1_BLOCKNR_REG,
+ .l2_block = IPU8_IS_MMU_M0_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_IS_MMU_M0_STREAM_NUM,
+ .nr_l2streams = IPU8_IS_MMU_M0_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .zlx_nr = IPU8_IS_ZLX_M0_NUM,
+ .zlx_axi_pool = {
+ 0x00000f10,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ },
+ .uao_p_num = IPU8_IS_UAO_M0_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x0000003b,
+ 0x0000003c,
+ 0x0000003d,
+ 0x0000003e,
+ 0x0000003b,
+ 0x0000003c,
+ 0x0000003d,
+ 0x0000003e,
+ 0x0000003b,
+ 0x0000003c,
+ 0x0000003d,
+ 0x0000003e,
+ 0x0000003b,
+ 0x0000003c,
+ 0x0000003d,
+ 0x0000003e,
+ },
+ },
+ {
+ .name = "IS_DATA_WR_SNOOP",
+ .offset = IPU8_IS_MMU_M1_OFFSET,
+ .zlx_offset = IPU8_IS_ZLX_M1_OFFSET,
+ .uao_offset = IPU8_IS_UAO_M1_WR_OFFSET,
+ .info_bits = 0x20004f01,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_IS_MMU_M1_L1_BLOCKNR_REG,
+ .l2_block = IPU8_IS_MMU_M1_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_IS_MMU_M1_STREAM_NUM,
+ .nr_l2streams = IPU8_IS_MMU_M1_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ },
+ .zlx_nr = IPU8_IS_ZLX_M1_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ },
+ .uao_p_num = IPU8_IS_UAO_M1_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x0000003f,
+ 0x00000040,
+ 0x00000041,
+ 0x00000042,
+ 0x0000003f,
+ 0x00000040,
+ 0x00000041,
+ 0x00000042,
+ 0x0000003f,
+ 0x00000040,
+ 0x00000041,
+ 0x00000042,
+ 0x0000003f,
+ 0x00000040,
+ 0x00000041,
+ 0x00000042,
+ },
+ },
+ {
+ .name = "IS_UPIPE",
+ .offset = IPU8_IS_MMU_UPIPE_OFFSET,
+ .zlx_offset = IPU8_IS_ZLX_UPIPE_OFFSET,
+ .uao_offset = IPU8_IS_UAO_UPIPE_OFFSET,
+ .info_bits = 0x20005201,
+ .refill = 0x00002928,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_IS_MMU_UPIPE_L1_BLOCKNR_REG,
+ .l2_block = IPU8_IS_MMU_UPIPE_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_IS_MMU_UPIPE_STREAM_NUM,
+ .nr_l2streams = IPU8_IS_MMU_UPIPE_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ },
+ .zlx_nr = IPU8_IS_ZLX_UPIPE_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ 0x3,
+ },
+ .uao_p_num = IPU8_IS_UAO_UPIPE_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000043,
+ 0x00000044,
+ 0x00000045,
+ 0x00000046,
+ 0x00000047,
+ 0x00000048,
+ },
+ },
+ },
+ .cdc_fifos = 3,
+ .cdc_fifo_threshold = {6, 8, 2},
+ .dmem_offset = IPU_ISYS_DMEM_OFFSET,
+ .spc_offset = IPU_ISYS_SPC_OFFSET,
+ },
+ .isys_dma_overshoot = IPU_ISYS_OVERALLOC_MIN,
+};
+
+static struct ipu_psys_internal_pdata ipu8_psys_ipdata = {
+ .hw_variant = {
+ .offset = IPU_UNIFIED_OFFSET,
+ .nr_mmus = IPU8_PS_MMU_NUM,
+ .mmu_hw = {
+ {
+ .name = "PS_FW_RD",
+ .offset = IPU8_PS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU8_PS_ZLX_FW_RD_OFFSET,
+ .uao_offset = IPU8_PS_UAO_FW_RD_OFFSET,
+ .info_bits = 0x20003a01,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_PS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU8_PS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_PS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU8_PS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x00000018,
+ 0x00000018,
+ 0x00000018,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ },
+ .zlx_nr = IPU8_PS_ZLX_FW_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 0, 1, 0, 0, 1, 1, 0, 0,
+ 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x2,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ .uao_p_num = IPU8_PS_UAO_FW_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x0000002d,
+ 0x00000032,
+ 0x00000033,
+ 0x00000030,
+ 0x00000034,
+ 0x00000035,
+ 0x00000036,
+ 0x00000031,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ },
+ {
+ .name = "PS_FW_WR",
+ .offset = IPU8_PS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU8_PS_ZLX_FW_WR_OFFSET,
+ .uao_offset = IPU8_PS_UAO_FW_WR_OFFSET,
+ .info_bits = 0x20003901,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_PS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU8_PS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_PS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU8_PS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000010,
+ 0x00000010,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ },
+ .zlx_nr = IPU8_PS_ZLX_FW_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f20,
+ },
+ .zlx_en = {
+ 0, 1, 1, 0, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x0, 0x2, 0x2, 0x0,
+ 0x0, 0x0, 0x0, 0x0,
+ },
+ .uao_p_num = IPU8_PS_UAO_FW_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x0000002d,
+ 0x0000002e,
+ 0x0000002f,
+ 0x00000030,
+ 0x00000031,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ },
+ {
+ .name = "PS_DATA_RD",
+ .offset = IPU8_PS_MMU_SRT_RD_OFFSET,
+ .zlx_offset = IPU8_PS_ZLX_DATA_RD_OFFSET,
+ .uao_offset = IPU8_PS_UAO_SRT_RD_OFFSET,
+ .info_bits = 0x20003801,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_PS_MMU_SRT_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU8_PS_MMU_SRT_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM,
+ .nr_l2streams = IPU8_PS_MMU_SRT_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000014,
+ 0x00000018,
+ 0x0000001c,
+ 0x0000001e,
+ 0x00000022,
+ 0x00000024,
+ 0x00000026,
+ 0x00000028,
+ 0x0000002a,
+ 0x0000002c,
+ 0x0000002e,
+ 0x00000030,
+ 0x00000032,
+ 0x00000036,
+ 0x0000003a,
+ 0x0000003c,
+ 0x0000003c,
+ 0x0000003c,
+ 0x0000003c,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ 0x00000020,
+ 0x00000022,
+ 0x00000024,
+ 0x00000026,
+ 0x00000028,
+ 0x0000002a,
+ 0x0000002c,
+ 0x0000002e,
+ 0x00000030,
+ 0x00000032,
+ },
+ .zlx_nr = IPU8_PS_ZLX_DATA_RD_NUM,
+ .zlx_axi_pool = {
+ 0x00000f30,
+ },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0,
+ },
+ .zlx_conf = {
+ 0x6, 0x3, 0x3, 0x6,
+ 0x2, 0x2, 0x6, 0x6,
+ 0x6, 0x3, 0x6, 0x3,
+ 0x3, 0x2, 0x2, 0x2,
+ 0x2, 0x2, 0x2, 0x6,
+ 0x6, 0x3, 0x0, 0x0,
+ 0x0, 0x0,
+ },
+ .uao_p_num = IPU8_PS_UAO_SRT_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000017,
+ 0x00000018,
+ 0x00000019,
+ 0x0000001a,
+ 0x0000001b,
+ 0x0000001c,
+ 0x0000001d,
+ 0x0000001e,
+ 0x0000001f,
+ 0x00000020,
+ 0x00000021,
+ 0x00000022,
+ 0x00000023,
+ 0x00000024,
+ 0x00000025,
+ 0x00000026,
+ 0x00000027,
+ 0x00000028,
+ 0x00000029,
+ 0x0000002a,
+ 0x0000002b,
+ 0x0000002c,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ },
+ {
+ .name = "PS_DATA_WR",
+ .offset = IPU8_PS_MMU_SRT_WR_OFFSET,
+ .zlx_offset = IPU8_PS_ZLX_DATA_WR_OFFSET,
+ .uao_offset = IPU8_PS_UAO_SRT_WR_OFFSET,
+ .info_bits = 0x20003701,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU8_PS_MMU_SRT_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU8_PS_MMU_SRT_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM,
+ .nr_l2streams = IPU8_PS_MMU_SRT_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001c,
+ 0x0000001e,
+ 0x00000022,
+ 0x00000024,
+ 0x00000028,
+ 0x0000002a,
+ 0x0000002e,
+ 0x00000030,
+ 0x00000032,
+ 0x00000036,
+ 0x00000038,
+ 0x0000003a,
+ 0x0000003a,
+ 0x0000003a,
+ },
+ .l2_block_sz = {
+ 0x00000000,
+ 0x00000002,
+ 0x00000004,
+ 0x00000006,
+ 0x00000008,
+ 0x0000000a,
+ 0x0000000c,
+ 0x0000000e,
+ 0x00000010,
+ 0x00000012,
+ 0x00000014,
+ 0x00000016,
+ 0x00000018,
+ 0x0000001a,
+ 0x0000001c,
+ 0x0000001e,
+ 0x00000020,
+ 0x00000022,
+ 0x00000024,
+ 0x00000026,
+ 0x00000028,
+ 0x0000002a,
+ 0x0000002c,
+ 0x0000002e,
+ 0x00000030,
+ 0x00000032,
+ },
+ .zlx_nr = IPU8_PS_ZLX_DATA_WR_NUM,
+ .zlx_axi_pool = {
+ 0x00000f50,
+ },
+ .zlx_en = {
+ 1, 1, 1, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 0,
+ 0, 0,
+ },
+ .zlx_conf = {
+ 0x3,
+ 0x6,
+ 0x38000002,
+ 0x38000000,
+ 0x3,
+ 0x38000002,
+ 0x38000002,
+ 0x38000002,
+ 0x38000002,
+ 0x38000002,
+ 0x38000002,
+ 0x6,
+ 0x3,
+ 0x6,
+ 0x3,
+ 0x6,
+ 0x3,
+ 0x6,
+ 0x3,
+ 0x3,
+ 0x6,
+ 0x3,
+ 0x3,
+ 0x0,
+ 0x0,
+ 0x0,
+ },
+ .uao_p_num = IPU8_PS_UAO_SRT_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000002,
+ 0x00000003,
+ 0x00000004,
+ 0x00000005,
+ 0x00000006,
+ 0x00000007,
+ 0x00000008,
+ 0x00000009,
+ 0x0000000a,
+ 0x0000000b,
+ 0x0000000c,
+ 0x0000000d,
+ 0x0000000e,
+ 0x0000000f,
+ 0x00000010,
+ 0x00000011,
+ 0x00000012,
+ 0x00000013,
+ 0x00000014,
+ 0x00000015,
+ 0x00000016,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ },
+ },
+ },
+ .dmem_offset = IPU_PSYS_DMEM_OFFSET,
+ },
+};
+
+static const struct ipu_buttress_ctrl ipu7_isys_buttress_ctrl = {
+ .subsys_id = IPU_IS,
+ .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO,
+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT,
+ .cdyn = IPU_FREQ_CTL_CDYN,
+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT,
+ .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ,
+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT,
+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK,
+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE,
+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE,
+ .ovrd_clk = BUTTRESS_OVERRIDE_IS_CLK,
+ .own_clk_ack = BUTTRESS_OWN_ACK_IS_CLK,
+};
+
+static const struct ipu_buttress_ctrl ipu7_psys_buttress_ctrl = {
+ .subsys_id = IPU_PS,
+ .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO,
+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT,
+ .cdyn = IPU_FREQ_CTL_CDYN,
+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT,
+ .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ,
+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT,
+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK,
+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE,
+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE,
+ .ovrd_clk = BUTTRESS_OVERRIDE_PS_CLK,
+ .own_clk_ack = BUTTRESS_OWN_ACK_PS_CLK,
+};
+
+static const struct ipu_buttress_ctrl ipu8_isys_buttress_ctrl = {
+ .subsys_id = IPU_IS,
+ .ratio = IPU8_IS_FREQ_CTL_DEFAULT_RATIO,
+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT,
+ .cdyn = IPU_FREQ_CTL_CDYN,
+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT,
+ .freq_ctl = BUTTRESS_REG_IS_WORKPOINT_REQ,
+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_IS_PWR_SHIFT,
+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_IS_PWR_MASK,
+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE,
+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE,
+};
+
+static const struct ipu_buttress_ctrl ipu8_psys_buttress_ctrl = {
+ .subsys_id = IPU_PS,
+ .ratio = IPU8_PS_FREQ_CTL_DEFAULT_RATIO,
+ .ratio_shift = IPU_FREQ_CTL_RATIO_SHIFT,
+ .cdyn = IPU_FREQ_CTL_CDYN,
+ .cdyn_shift = IPU_FREQ_CTL_CDYN_SHIFT,
+ .freq_ctl = BUTTRESS_REG_PS_WORKPOINT_REQ,
+ .pwr_sts_shift = IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT,
+ .pwr_sts_mask = IPU_BUTTRESS_PWR_STATE_PS_PWR_MASK,
+ .pwr_sts_on = IPU_BUTTRESS_PWR_STATE_UP_DONE,
+ .pwr_sts_off = IPU_BUTTRESS_PWR_STATE_DN_DONE,
+ .own_clk_ack = BUTTRESS_OWN_ACK_PS_PLL,
+};
+
+void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata,
+ struct ipu_psys_internal_pdata *psys_ipdata)
+{
+ isys_ipdata->csi2.nports = ARRAY_SIZE(ipu7_csi_offsets);
+ isys_ipdata->csi2.offsets = ipu7_csi_offsets;
+ isys_ipdata->num_parallel_streams = IPU7_ISYS_NUM_STREAMS;
+ psys_ipdata->hw_variant.spc_offset = IPU7_PSYS_SPC_OFFSET;
+}
+
+static int ipu7_isys_check_fwnode_graph(struct fwnode_handle *fwnode)
+{
+ struct fwnode_handle *endpoint;
+
+ if (IS_ERR_OR_NULL(fwnode))
+ return -EINVAL;
+
+ endpoint = fwnode_graph_get_next_endpoint(fwnode, NULL);
+ if (endpoint) {
+ fwnode_handle_put(endpoint);
+ return 0;
+ }
+
+ return ipu7_isys_check_fwnode_graph(fwnode->secondary);
+}
+
+static struct ipu7_bus_device *
+ipu7_isys_init(struct pci_dev *pdev, struct device *parent,
+ const struct ipu_buttress_ctrl *ctrl, void __iomem *base,
+ const struct ipu_isys_internal_pdata *ipdata,
+ unsigned int nr)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);
+ struct ipu7_bus_device *isys_adev;
+ struct device *dev = &pdev->dev;
+ struct ipu7_isys_pdata *pdata;
+ int ret;
+
+ ret = ipu7_isys_check_fwnode_graph(fwnode);
+ if (ret) {
+ if (fwnode && !IS_ERR_OR_NULL(fwnode->secondary)) {
+ dev_err(dev,
+ "fwnode graph has no endpoints connection\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ ret = ipu_bridge_init(dev, ipu_bridge_parse_ssdb);
+ if (ret) {
+ dev_err_probe(dev, ret, "IPU bridge init failed\n");
+ return ERR_PTR(ret);
+ }
+ }
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->base = base;
+ pdata->ipdata = ipdata;
+
+ isys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl,
+ IPU_ISYS_NAME);
+ if (IS_ERR(isys_adev)) {
+ dev_err_probe(dev, PTR_ERR(isys_adev),
+ "ipu7_bus_initialize_device isys failed\n");
+ kfree(pdata);
+ return ERR_CAST(isys_adev);
+ }
+
+ isys_adev->mmu = ipu7_mmu_init(dev, base, ISYS_MMID,
+ &ipdata->hw_variant);
+ if (IS_ERR(isys_adev->mmu)) {
+ dev_err_probe(dev, PTR_ERR(isys_adev->mmu),
+ "ipu7_mmu_init(isys_adev->mmu) failed\n");
+ put_device(&isys_adev->auxdev.dev);
+ kfree(pdata);
+ return ERR_CAST(isys_adev->mmu);
+ }
+
+ isys_adev->mmu->dev = &isys_adev->auxdev.dev;
+ isys_adev->subsys = IPU_IS;
+
+ ret = ipu7_bus_add_device(isys_adev);
+ if (ret) {
+ kfree(pdata);
+ return ERR_PTR(ret);
+ }
+
+ return isys_adev;
+}
+
+static struct ipu7_bus_device *
+ipu7_psys_init(struct pci_dev *pdev, struct device *parent,
+ const struct ipu_buttress_ctrl *ctrl, void __iomem *base,
+ const struct ipu_psys_internal_pdata *ipdata, unsigned int nr)
+{
+ struct ipu7_bus_device *psys_adev;
+ struct ipu7_psys_pdata *pdata;
+ int ret;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ pdata->base = base;
+ pdata->ipdata = ipdata;
+
+ psys_adev = ipu7_bus_initialize_device(pdev, parent, pdata, ctrl,
+ IPU_PSYS_NAME);
+ if (IS_ERR(psys_adev)) {
+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev),
+ "ipu7_bus_initialize_device psys failed\n");
+ kfree(pdata);
+ return ERR_CAST(psys_adev);
+ }
+
+ psys_adev->mmu = ipu7_mmu_init(&pdev->dev, base, PSYS_MMID,
+ &ipdata->hw_variant);
+ if (IS_ERR(psys_adev->mmu)) {
+ dev_err_probe(&pdev->dev, PTR_ERR(psys_adev->mmu),
+ "ipu7_mmu_init(psys_adev->mmu) failed\n");
+ put_device(&psys_adev->auxdev.dev);
+ kfree(pdata);
+ return ERR_CAST(psys_adev->mmu);
+ }
+
+ psys_adev->mmu->dev = &psys_adev->auxdev.dev;
+ psys_adev->subsys = IPU_PS;
+
+ ret = ipu7_bus_add_device(psys_adev);
+ if (ret) {
+ kfree(pdata);
+ return ERR_PTR(ret);
+ }
+
+ return psys_adev;
+}
+
+static struct ia_gofo_msg_log_info_ts fw_error_log[IPU_SUBSYS_NUM];
+void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev)
+{
+ void __iomem *reg = adev->isp->base + ((adev->subsys == IPU_IS) ?
+ BUTTRESS_REG_FW_GP24 :
+ BUTTRESS_REG_FW_GP8);
+
+ memcpy_fromio(&fw_error_log[adev->subsys], reg,
+ sizeof(fw_error_log[adev->subsys]));
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_dump_fw_error_log, "INTEL_IPU7");
+
+static int ipu7_pci_config_setup(struct pci_dev *dev)
+{
+ u16 pci_command;
+ int ret;
+
+ pci_read_config_word(dev, PCI_COMMAND, &pci_command);
+ pci_command |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+ pci_write_config_word(dev, PCI_COMMAND, pci_command);
+
+ ret = pci_enable_msi(dev);
+ if (ret)
+ dev_err(&dev->dev, "Failed to enable msi (%d)\n", ret);
+
+ return ret;
+}
+
+static int ipu7_map_fw_code_region(struct ipu7_bus_device *sys,
+ void *data, size_t size)
+{
+ struct device *dev = &sys->auxdev.dev;
+ struct ipu7_bus_device *adev = to_ipu7_bus_device(dev);
+ struct sg_table *sgt = &sys->fw_sgt;
+ struct ipu7_device *isp = adev->isp;
+ struct pci_dev *pdev = isp->pdev;
+ unsigned long n_pages, i;
+ unsigned long attr = 0;
+ struct page **pages;
+ int ret;
+
+ n_pages = PFN_UP(size);
+
+ pages = kmalloc_array(n_pages, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
+ return -ENOMEM;
+
+ for (i = 0; i < n_pages; i++) {
+ struct page *p = vmalloc_to_page(data);
+
+ if (!p) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ pages[i] = p;
+ data += PAGE_SIZE;
+ }
+
+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size,
+ GFP_KERNEL);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (!isp->secure_mode)
+ attr |= DMA_ATTR_RESERVE_REGION;
+
+ ret = dma_map_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
+ if (ret < 0) {
+ dev_err(dev, "map fw code[%lu pages %u nents] failed\n",
+ n_pages, sgt->nents);
+ ret = -ENOMEM;
+ sg_free_table(sgt);
+ goto out;
+ }
+
+ ret = ipu7_dma_map_sgtable(sys, sgt, DMA_BIDIRECTIONAL, attr);
+ if (ret) {
+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
+ sg_free_table(sgt);
+ goto out;
+ }
+
+ ipu7_dma_sync_sgtable(sys, sgt);
+
+ dev_dbg(dev, "fw code region mapped at 0x%pad entries %d\n",
+ &sgt->sgl->dma_address, sgt->nents);
+
+out:
+ kfree(pages);
+
+ return ret;
+}
+
+static void ipu7_unmap_fw_code_region(struct ipu7_bus_device *sys)
+{
+ struct pci_dev *pdev = sys->isp->pdev;
+ struct sg_table *sgt = &sys->fw_sgt;
+
+ ipu7_dma_unmap_sgtable(sys, sgt, DMA_BIDIRECTIONAL, 0);
+ dma_unmap_sgtable(&pdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
+ sg_free_table(sgt);
+}
+
+static int ipu7_init_fw_code_region_by_sys(struct ipu7_bus_device *sys,
+ const char *sys_name)
+{
+ struct device *dev = &sys->auxdev.dev;
+ struct ipu7_device *isp = sys->isp;
+ int ret;
+
+ /* Copy FW binaries to specific location. */
+ ret = ipu7_cpd_copy_binary(isp->cpd_fw->data, sys_name,
+ isp->fw_code_region, &sys->fw_entry);
+ if (ret) {
+ dev_err(dev, "%s binary not found.\n", sys_name);
+ return ret;
+ }
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get runtime PM\n");
+ return ret;
+ }
+
+ ret = ipu7_mmu_hw_init(sys->mmu);
+ if (ret) {
+ dev_err(dev, "Failed to set mmu hw\n");
+ pm_runtime_put(dev);
+ return ret;
+ }
+
+ /* Map code region. */
+ ret = ipu7_map_fw_code_region(sys, isp->fw_code_region,
+ IPU_FW_CODE_REGION_SIZE);
+ if (ret)
+ dev_err(dev, "Failed to map fw code region for %s.\n",
+ sys_name);
+
+ ipu7_mmu_hw_cleanup(sys->mmu);
+ pm_runtime_put(dev);
+
+ return ret;
+}
+
+static int ipu7_init_fw_code_region(struct ipu7_device *isp)
+{
+ int ret;
+
+ /*
+ * Allocate and map memory for FW execution.
+ * Not required in secure mode, in which FW runs in IMR.
+ */
+ isp->fw_code_region = vmalloc(IPU_FW_CODE_REGION_SIZE);
+ if (!isp->fw_code_region)
+ return -ENOMEM;
+
+ ret = ipu7_init_fw_code_region_by_sys(isp->isys, "isys");
+ if (ret)
+ goto fail_init;
+
+ ret = ipu7_init_fw_code_region_by_sys(isp->psys, "psys");
+ if (ret)
+ goto fail_init;
+
+ return 0;
+
+fail_init:
+ vfree(isp->fw_code_region);
+
+ return ret;
+}
+
+static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct ipu_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL;
+ struct fwnode_handle *fwnode = dev_fwnode(&pdev->dev);
+ const struct ipu_buttress_ctrl *isys_buttress_ctrl;
+ const struct ipu_buttress_ctrl *psys_buttress_ctrl;
+ struct ipu_isys_internal_pdata *isys_ipdata;
+ struct ipu_psys_internal_pdata *psys_ipdata;
+ unsigned int dma_mask = IPU_DMA_MASK;
+ struct device *dev = &pdev->dev;
+ void __iomem *isys_base = NULL;
+ void __iomem *psys_base = NULL;
+ phys_addr_t phys, pb_phys;
+ struct ipu7_device *isp;
+ u32 is_es;
+ int ret;
+
+ if (!fwnode || fwnode_property_read_u32(fwnode, "is_es", &is_es))
+ is_es = 0;
+
+ isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
+ if (!isp)
+ return -ENOMEM;
+
+ dev_set_name(dev, "intel-ipu7");
+ isp->pdev = pdev;
+ INIT_LIST_HEAD(&isp->devices);
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Enable PCI device failed\n");
+
+ dev_info(dev, "Device 0x%x (rev: 0x%x)\n",
+ pdev->device, pdev->revision);
+
+ phys = pci_resource_start(pdev, IPU_PCI_BAR);
+ pb_phys = pci_resource_start(pdev, IPU_PCI_PBBAR);
+ dev_info(dev, "IPU7 PCI BAR0 base %pap BAR2 base %pap\n",
+ &phys, &pb_phys);
+
+ isp->base = pcim_iomap_region(pdev, IPU_PCI_BAR, IPU_NAME);
+ if (IS_ERR(isp->base))
+ return dev_err_probe(dev, PTR_ERR(isp->base),
+ "Failed to I/O memory remapping bar %u\n",
+ IPU_PCI_BAR);
+
+ isp->pb_base = pcim_iomap_region(pdev, IPU_PCI_PBBAR, IPU_NAME);
+ if (IS_ERR(isp->pb_base))
+ return dev_err_probe(dev, PTR_ERR(isp->pb_base),
+ "Failed to I/O memory remapping bar %u\n",
+ IPU_PCI_PBBAR);
+
+ dev_info(dev, "IPU7 PCI BAR0 mapped at %p\n BAR2 mapped at %p\n",
+ isp->base, isp->pb_base);
+
+ pci_set_drvdata(pdev, isp);
+ pci_set_master(pdev);
+
+ switch (id->device) {
+ case IPU7_PCI_ID:
+ isp->hw_ver = IPU_VER_7;
+ isp->cpd_fw_name = IPU7_FIRMWARE_NAME;
+ isys_ipdata = &ipu7_isys_ipdata;
+ psys_ipdata = &ipu7_psys_ipdata;
+ isys_buttress_ctrl = &ipu7_isys_buttress_ctrl;
+ psys_buttress_ctrl = &ipu7_psys_buttress_ctrl;
+ break;
+ case IPU7P5_PCI_ID:
+ isp->hw_ver = IPU_VER_7P5;
+ isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME;
+ isys_ipdata = &ipu7p5_isys_ipdata;
+ psys_ipdata = &ipu7p5_psys_ipdata;
+ isys_buttress_ctrl = &ipu7_isys_buttress_ctrl;
+ psys_buttress_ctrl = &ipu7_psys_buttress_ctrl;
+ break;
+ case IPU8_PCI_ID:
+ isp->hw_ver = IPU_VER_8;
+ isp->cpd_fw_name = IPU8_FIRMWARE_NAME;
+ isys_ipdata = &ipu8_isys_ipdata;
+ psys_ipdata = &ipu8_psys_ipdata;
+ isys_buttress_ctrl = &ipu8_isys_buttress_ctrl;
+ psys_buttress_ctrl = &ipu8_psys_buttress_ctrl;
+ break;
+ default:
+ WARN(1, "Unsupported IPU device");
+ return -ENODEV;
+ }
+
+ ipu_internal_pdata_init(isys_ipdata, psys_ipdata);
+
+ isys_base = isp->base + isys_ipdata->hw_variant.offset;
+ psys_base = isp->base + psys_ipdata->hw_variant.offset;
+
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(dma_mask));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set DMA mask\n");
+
+ dma_set_max_seg_size(dev, UINT_MAX);
+
+ ret = ipu7_pci_config_setup(pdev);
+ if (ret)
+ return ret;
+
+ ret = ipu_buttress_init(isp);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "firmware cpd file: %s\n", isp->cpd_fw_name);
+
+ ret = request_firmware(&isp->cpd_fw, isp->cpd_fw_name, dev);
+ if (ret) {
+ dev_err_probe(dev, ret,
+ "Requesting signed firmware %s failed\n",
+ isp->cpd_fw_name);
+ goto buttress_exit;
+ }
+
+ ret = ipu7_cpd_validate_cpd_file(isp, isp->cpd_fw->data,
+ isp->cpd_fw->size);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to validate cpd\n");
+ goto out_ipu_bus_del_devices;
+ }
+
+ isys_ctrl = devm_kmemdup(dev, isys_buttress_ctrl,
+ sizeof(*isys_buttress_ctrl), GFP_KERNEL);
+ if (!isys_ctrl) {
+ ret = -ENOMEM;
+ goto out_ipu_bus_del_devices;
+ }
+
+ isp->isys = ipu7_isys_init(pdev, dev, isys_ctrl, isys_base,
+ isys_ipdata, 0);
+ if (IS_ERR(isp->isys)) {
+ ret = PTR_ERR(isp->isys);
+ goto out_ipu_bus_del_devices;
+ }
+
+ psys_ctrl = devm_kmemdup(dev, psys_buttress_ctrl,
+ sizeof(*psys_buttress_ctrl), GFP_KERNEL);
+ if (!psys_ctrl) {
+ ret = -ENOMEM;
+ goto out_ipu_bus_del_devices;
+ }
+
+ isp->psys = ipu7_psys_init(pdev, &isp->isys->auxdev.dev,
+ psys_ctrl, psys_base,
+ psys_ipdata, 0);
+ if (IS_ERR(isp->psys)) {
+ ret = PTR_ERR(isp->psys);
+ goto out_ipu_bus_del_devices;
+ }
+
+ ret = devm_request_threaded_irq(dev, pdev->irq,
+ ipu_buttress_isr,
+ ipu_buttress_isr_threaded,
+ IRQF_SHARED, IPU_NAME, isp);
+ if (ret)
+ goto out_ipu_bus_del_devices;
+
+ if (!isp->secure_mode) {
+ ret = ipu7_init_fw_code_region(isp);
+ if (ret)
+ goto out_ipu_bus_del_devices;
+ } else {
+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev);
+ if (ret < 0) {
+ dev_err(&isp->psys->auxdev.dev,
+ "Failed to get runtime PM\n");
+ goto out_ipu_bus_del_devices;
+ }
+
+ ret = ipu7_mmu_hw_init(isp->psys->mmu);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "Failed to init MMU hardware\n");
+ goto out_ipu_bus_del_devices;
+ }
+
+ ret = ipu7_map_fw_code_region(isp->psys,
+ (void *)isp->cpd_fw->data,
+ isp->cpd_fw->size);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "failed to map fw image\n");
+ goto out_ipu_bus_del_devices;
+ }
+
+ ret = ipu_buttress_authenticate(isp);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "FW authentication failed\n");
+ goto out_ipu_bus_del_devices;
+ }
+
+ ipu7_mmu_hw_cleanup(isp->psys->mmu);
+ pm_runtime_put(&isp->psys->auxdev.dev);
+ }
+
+ pm_runtime_put_noidle(dev);
+ pm_runtime_allow(dev);
+
+ isp->ipu7_bus_ready_to_probe = true;
+
+ return 0;
+
+out_ipu_bus_del_devices:
+ if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents)
+ ipu7_unmap_fw_code_region(isp->isys);
+ if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents)
+ ipu7_unmap_fw_code_region(isp->psys);
+ if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu))
+ ipu7_mmu_cleanup(isp->psys->mmu);
+ if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu))
+ ipu7_mmu_cleanup(isp->isys->mmu);
+ if (!IS_ERR_OR_NULL(isp->psys))
+ pm_runtime_put(&isp->psys->auxdev.dev);
+ ipu7_bus_del_devices(pdev);
+ release_firmware(isp->cpd_fw);
+buttress_exit:
+ ipu_buttress_exit(isp);
+
+ return ret;
+}
+
+static void ipu7_pci_remove(struct pci_dev *pdev)
+{
+ struct ipu7_device *isp = pci_get_drvdata(pdev);
+
+ if (!IS_ERR_OR_NULL(isp->isys) && isp->isys->fw_sgt.nents)
+ ipu7_unmap_fw_code_region(isp->isys);
+ if (!IS_ERR_OR_NULL(isp->psys) && isp->psys->fw_sgt.nents)
+ ipu7_unmap_fw_code_region(isp->psys);
+
+ if (!IS_ERR_OR_NULL(isp->fw_code_region))
+ vfree(isp->fw_code_region);
+
+ ipu7_bus_del_devices(pdev);
+
+ pm_runtime_forbid(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+
+ ipu_buttress_exit(isp);
+
+ release_firmware(isp->cpd_fw);
+
+ ipu7_mmu_cleanup(isp->psys->mmu);
+ ipu7_mmu_cleanup(isp->isys->mmu);
+}
+
+static void ipu7_pci_reset_prepare(struct pci_dev *pdev)
+{
+ struct ipu7_device *isp = pci_get_drvdata(pdev);
+
+ dev_warn(&pdev->dev, "FLR prepare\n");
+ pm_runtime_forbid(&isp->pdev->dev);
+}
+
+static void ipu7_pci_reset_done(struct pci_dev *pdev)
+{
+ struct ipu7_device *isp = pci_get_drvdata(pdev);
+
+ ipu_buttress_restore(isp);
+ if (isp->secure_mode)
+ ipu_buttress_reset_authentication(isp);
+
+ isp->ipc_reinit = true;
+ pm_runtime_allow(&isp->pdev->dev);
+
+ dev_warn(&pdev->dev, "FLR completed\n");
+}
+
+/*
+ * PCI base driver code requires driver to provide these to enable
+ * PCI device level PM state transitions (D0<->D3)
+ */
+static int ipu7_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int ipu7_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct ipu7_device *isp = pci_get_drvdata(pdev);
+ struct ipu_buttress *b = &isp->buttress;
+ int ret;
+
+ isp->secure_mode = ipu_buttress_get_secure_mode(isp);
+ dev_info(dev, "IPU7 in %s mode\n",
+ isp->secure_mode ? "secure" : "non-secure");
+
+ ipu_buttress_restore(isp);
+
+ ret = ipu_buttress_ipc_reset(isp, &b->cse);
+ if (ret)
+ dev_err(dev, "IPC reset protocol failed!\n");
+
+ ret = pm_runtime_get_sync(&isp->psys->auxdev.dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to get runtime PM\n");
+ return 0;
+ }
+
+ ret = ipu_buttress_authenticate(isp);
+ if (ret)
+ dev_err(dev, "FW authentication failed(%d)\n", ret);
+
+ pm_runtime_put(&isp->psys->auxdev.dev);
+
+ return 0;
+}
+
+static int ipu7_runtime_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct ipu7_device *isp = pci_get_drvdata(pdev);
+ int ret;
+
+ ipu_buttress_restore(isp);
+
+ if (isp->ipc_reinit) {
+ struct ipu_buttress *b = &isp->buttress;
+
+ isp->ipc_reinit = false;
+ ret = ipu_buttress_ipc_reset(isp, &b->cse);
+ if (ret)
+ dev_err(dev, "IPC reset protocol failed!\n");
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops ipu7_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(&ipu7_suspend, &ipu7_resume)
+ RUNTIME_PM_OPS(&ipu7_suspend, &ipu7_runtime_resume, NULL)
+};
+
+static const struct pci_device_id ipu7_pci_tbl[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7_PCI_ID)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, IPU7P5_PCI_ID)},
+ {0,}
+};
+MODULE_DEVICE_TABLE(pci, ipu7_pci_tbl);
+
+static const struct pci_error_handlers pci_err_handlers = {
+ .reset_prepare = ipu7_pci_reset_prepare,
+ .reset_done = ipu7_pci_reset_done,
+};
+
+static struct pci_driver ipu7_pci_driver = {
+ .name = IPU_NAME,
+ .id_table = ipu7_pci_tbl,
+ .probe = ipu7_pci_probe,
+ .remove = ipu7_pci_remove,
+ .driver = {
+ .pm = &ipu7_pm_ops,
+ },
+ .err_handler = &pci_err_handlers,
+};
+
+module_pci_driver(ipu7_pci_driver);
+
+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE");
+MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>");
+MODULE_AUTHOR("Tianshu Qiu <tian.shu.qiu@intel.com>");
+MODULE_AUTHOR("Qingwu Zhang <qingwu.zhang@intel.com>");
+MODULE_AUTHOR("Intel");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Intel ipu7 pci driver");
diff --git a/drivers/staging/media/ipu7/ipu7.h b/drivers/staging/media/ipu7/ipu7.h
new file mode 100644
index 000000000000..ac8ac0689468
--- /dev/null
+++ b/drivers/staging/media/ipu7/ipu7.h
@@ -0,0 +1,242 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2013 - 2025 Intel Corporation
+ */
+
+#ifndef IPU7_H
+#define IPU7_H
+
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include "ipu7-buttress.h"
+
+struct ipu7_bus_device;
+struct pci_dev;
+struct firmware;
+
+#define IPU_NAME "intel-ipu7"
+#define IPU_MEDIA_DEV_MODEL_NAME "ipu7"
+
+#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin"
+#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin"
+#define IPU8_FIRMWARE_NAME "intel/ipu/ipu8_fw.bin"
+
+#define IPU7_ISYS_NUM_STREAMS 12
+
+#define IPU7_PCI_ID 0x645d
+#define IPU7P5_PCI_ID 0xb05d
+#define IPU8_PCI_ID 0xd719
+
+#define FW_LOG_BUF_SIZE (2 * 1024 * 1024)
+
+enum ipu_version {
+ IPU_VER_INVALID = 0,
+ IPU_VER_7 = 1,
+ IPU_VER_7P5 = 2,
+ IPU_VER_8 = 3,
+};
+
+static inline bool is_ipu7p5(u8 hw_ver)
+{
+ return hw_ver == IPU_VER_7P5;
+}
+
+static inline bool is_ipu7(u8 hw_ver)
+{
+ return hw_ver == IPU_VER_7;
+}
+
+static inline bool is_ipu8(u8 hw_ver)
+{
+ return hw_ver == IPU_VER_8;
+}
+
+#define IPU_UNIFIED_OFFSET 0
+
+/*
+ * ISYS DMA can overshoot. For higher resolutions over allocation is one line
+ * but it must be at minimum 1024 bytes. Value could be different in
+ * different versions / generations thus provide it via platform data.
+ */
+#define IPU_ISYS_OVERALLOC_MIN 1024
+
+#define IPU_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */
+#define IPU_FW_CODE_REGION_START 0x4000000 /* 64MB */
+#define IPU_FW_CODE_REGION_END (IPU_FW_CODE_REGION_START + \
+ IPU_FW_CODE_REGION_SIZE) /* 80MB */
+
+struct ipu7_device {
+ struct pci_dev *pdev;
+ struct list_head devices;
+ struct ipu7_bus_device *isys;
+ struct ipu7_bus_device *psys;
+ struct ipu_buttress buttress;
+
+ const struct firmware *cpd_fw;
+ const char *cpd_fw_name;
+ /* Only for non-secure mode. */
+ void *fw_code_region;
+
+ void __iomem *base;
+ void __iomem *pb_base;
+ u8 hw_ver;
+ bool ipc_reinit;
+ bool secure_mode;
+ bool ipu7_bus_ready_to_probe;
+};
+
+#define IPU_DMA_MASK 39
+#define IPU_LIB_CALL_TIMEOUT_MS 2000
+#define IPU_PSYS_CMD_TIMEOUT_MS 2000
+#define IPU_PSYS_OPEN_CLOSE_TIMEOUT_US 50
+#define IPU_PSYS_OPEN_CLOSE_RETRY (10000 / IPU_PSYS_OPEN_CLOSE_TIMEOUT_US)
+
+#define IPU_ISYS_NAME "isys"
+#define IPU_PSYS_NAME "psys"
+
+#define IPU_MMU_ADDR_BITS 32
+/* FW is accessible within the first 2 GiB only in non-secure mode. */
+#define IPU_MMU_ADDR_BITS_NON_SECURE 31
+
+#define IPU7_IS_MMU_NUM 4U
+#define IPU7_PS_MMU_NUM 4U
+#define IPU7P5_IS_MMU_NUM 4U
+#define IPU7P5_PS_MMU_NUM 4U
+#define IPU8_IS_MMU_NUM 5U
+#define IPU8_PS_MMU_NUM 4U
+#define IPU_MMU_MAX_NUM 5U /* max(IS, PS) */
+#define IPU_MMU_MAX_TLB_L1_STREAMS 40U
+#define IPU_MMU_MAX_TLB_L2_STREAMS 40U
+#define IPU_ZLX_MAX_NUM 32U
+#define IPU_ZLX_POOL_NUM 8U
+#define IPU_UAO_PLANE_MAX_NUM 64U
+
+/*
+ * To maximize the IOSF utlization, IPU need to send requests in bursts.
+ * At the DMA interface with the buttress, there are CDC FIFOs with burst
+ * collection capability. CDC FIFO burst collectors have a configurable
+ * threshold and is configured based on the outcome of performance measurements.
+ *
+ * isys has 3 ports with IOSF interface for VC0, VC1 and VC2
+ * psys has 4 ports with IOSF interface for VC0, VC1w, VC1r and VC2
+ *
+ * Threshold values are pre-defined and are arrived at after performance
+ * evaluations on a type of IPU
+ */
+#define IPU_MAX_VC_IOSF_PORTS 4
+
+/*
+ * IPU must configure correct arbitration mechanism related to the IOSF VC
+ * requests. There are two options per VC0 and VC1 - > 0 means rearbitrate on
+ * stall and 1 means stall until the request is completed.
+ */
+#define IPU_BTRS_ARB_MODE_TYPE_REARB 0
+#define IPU_BTRS_ARB_MODE_TYPE_STALL 1
+
+/* Currently chosen arbitration mechanism for VC0 */
+#define IPU_BTRS_ARB_STALL_MODE_VC0 IPU_BTRS_ARB_MODE_TYPE_REARB
+
+/* Currently chosen arbitration mechanism for VC1 */
+#define IPU_BTRS_ARB_STALL_MODE_VC1 IPU_BTRS_ARB_MODE_TYPE_REARB
+
+/* One L2 entry maps 1024 L1 entries and one L1 entry per page */
+#define IPU_MMUV2_L2_RANGE (1024 * PAGE_SIZE)
+/* Max L2 blocks per stream */
+#define IPU_MMUV2_MAX_L2_BLOCKS 2
+/* Max L1 blocks per stream */
+#define IPU_MMUV2_MAX_L1_BLOCKS 16
+#define IPU_MMUV2_TRASH_RANGE (IPU_MMUV2_L2_RANGE * \
+ IPU_MMUV2_MAX_L2_BLOCKS)
+/* Entries per L1 block */
+#define MMUV2_ENTRIES_PER_L1_BLOCK 16
+#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE)
+#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU_MMUV2_L2_RANGE
+
+struct ipu7_mmu_hw {
+ char name[32];
+
+ void __iomem *base;
+ void __iomem *zlx_base;
+ void __iomem *uao_base;
+
+ u32 offset;
+ u32 zlx_offset;
+ u32 uao_offset;
+
+ u32 info_bits;
+ u32 refill;
+ u32 collapse_en_bitmap;
+ u32 at_sp_arb_cfg;
+
+ u32 l1_block;
+ u32 l2_block;
+
+ u8 nr_l1streams;
+ u8 nr_l2streams;
+ u32 l1_block_sz[IPU_MMU_MAX_TLB_L1_STREAMS];
+ u32 l2_block_sz[IPU_MMU_MAX_TLB_L2_STREAMS];
+
+ u8 zlx_nr;
+ u32 zlx_axi_pool[IPU_ZLX_POOL_NUM];
+ u32 zlx_en[IPU_ZLX_MAX_NUM];
+ u32 zlx_conf[IPU_ZLX_MAX_NUM];
+
+ u32 uao_p_num;
+ u32 uao_p2tlb[IPU_UAO_PLANE_MAX_NUM];
+};
+
+struct ipu7_mmu_pdata {
+ u32 nr_mmus;
+ struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM];
+ int mmid;
+};
+
+struct ipu7_isys_csi2_pdata {
+ void __iomem *base;
+};
+
+struct ipu7_isys_internal_csi2_pdata {
+ u32 nports;
+ u32 const *offsets;
+ u32 gpreg;
+};
+
+struct ipu7_hw_variants {
+ unsigned long offset;
+ u32 nr_mmus;
+ struct ipu7_mmu_hw mmu_hw[IPU_MMU_MAX_NUM];
+ u8 cdc_fifos;
+ u8 cdc_fifo_threshold[IPU_MAX_VC_IOSF_PORTS];
+ u32 dmem_offset;
+ u32 spc_offset; /* SPC offset from psys base */
+};
+
+struct ipu_isys_internal_pdata {
+ struct ipu7_isys_internal_csi2_pdata csi2;
+ struct ipu7_hw_variants hw_variant;
+ u32 num_parallel_streams;
+ u32 isys_dma_overshoot;
+};
+
+struct ipu7_isys_pdata {
+ void __iomem *base;
+ const struct ipu_isys_internal_pdata *ipdata;
+};
+
+struct ipu_psys_internal_pdata {
+ struct ipu7_hw_variants hw_variant;
+};
+
+struct ipu7_psys_pdata {
+ void __iomem *base;
+ const struct ipu_psys_internal_pdata *ipdata;
+};
+
+int request_cpd_fw(const struct firmware **firmware_p, const char *name,
+ struct device *device);
+void ipu_internal_pdata_init(struct ipu_isys_internal_pdata *isys_ipdata,
+ struct ipu_psys_internal_pdata *psys_ipdata);
+void ipu7_dump_fw_error_log(const struct ipu7_bus_device *adev);
+#endif /* IPU7_H */
diff --git a/drivers/staging/media/rkvdec/TODO b/drivers/staging/media/rkvdec/TODO
deleted file mode 100644
index 2c0779383276..000000000000
--- a/drivers/staging/media/rkvdec/TODO
+++ /dev/null
@@ -1,11 +0,0 @@
-* Support for HEVC is planned for this driver.
-
- Given the V4L controls for that CODEC will be part of
- the uABI, it will be required to have the driver in staging.
-
- For this reason, we are keeping this driver in staging for now.
-
-* Evaluate introducing a helper to consolidate duplicated
- code in rkvdec_request_validate and cedrus_request_validate.
- The helper needs to the driver private data associated with
- the videobuf2 queue, from a media request.
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
index 32af0e96e762..444fb53878d1 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_hw.c
@@ -86,9 +86,26 @@ void cedrus_dst_format_set(struct cedrus_dev *dev,
switch (fmt->pixelformat) {
case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
chroma_size = ALIGN(width, 16) * ALIGN(height, 16) / 2;
- reg = VE_PRIMARY_OUT_FMT_NV12;
+ switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ reg = VE_PRIMARY_OUT_FMT_NV12;
+ break;
+ case V4L2_PIX_FMT_NV21:
+ reg = VE_PRIMARY_OUT_FMT_NV21;
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ reg = VE_PRIMARY_OUT_FMT_YU12;
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ default:
+ reg = VE_PRIMARY_OUT_FMT_YV12;
+ break;
+ }
cedrus_write(dev, VE_PRIMARY_OUT_FMT, reg);
reg = chroma_size / 2;
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_video.c b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
index 77f78266f406..9fae2c7493d0 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_video.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_video.c
@@ -64,6 +64,21 @@ static struct cedrus_format cedrus_formats[] = {
.pixelformat = V4L2_PIX_FMT_NV12_32L32,
.directions = CEDRUS_DECODE_DST,
},
+ {
+ .pixelformat = V4L2_PIX_FMT_NV21,
+ .directions = CEDRUS_DECODE_DST,
+ .capabilities = CEDRUS_CAPABILITY_UNTILED,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_YUV420,
+ .directions = CEDRUS_DECODE_DST,
+ .capabilities = CEDRUS_CAPABILITY_UNTILED,
+ },
+ {
+ .pixelformat = V4L2_PIX_FMT_YVU420,
+ .directions = CEDRUS_DECODE_DST,
+ .capabilities = CEDRUS_CAPABILITY_UNTILED,
+ },
};
#define CEDRUS_FORMATS_COUNT ARRAY_SIZE(cedrus_formats)
@@ -140,6 +155,9 @@ void cedrus_prepare_format(struct v4l2_pix_format *pix_fmt)
break;
case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
/* 16-aligned stride. */
bytesperline = ALIGN(width, 16);