summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-06-19 03:21:54 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2026-06-19 03:21:54 +0300
commit8c13415c8a4383447c21ec832b20b3b283f0e01a (patch)
tree1a2eba52dc89ec2339ef2d63a1a7e496d40a64c0
parent5cd1731cc883a9914d91e3b93d4597317b5b5339 (diff)
parent06cb687a5132fcffe624c0070576ab852ac6b568 (diff)
downloadlinux-8c13415c8a4383447c21ec832b20b3b283f0e01a.tar.xz
Merge tag 'media/v7.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
Pull media updates from Mauro Carvalho Chehab: - v4l2: - core: fix subdev sensor ownership - subdev: Allow accessing routes with STREAMS client capability - ctrls: Add validation for HEVC active reference counts and background detection control - common: Add YUV24 format info and has_alpha helper - vb2: Change vb2_read() and vb2_write() return types to ssize_t - i2c: cvs: Add driver of Intel Computer Vision Sensing Controller(CVS) - atmel-isc: remove deprecated driver - cec: Add CEC Latency Indication Protocol (LIP) support - imon: Add iMON VFD HID OEM v1.2 key mappings - AVMatrix: new HWS capture driver - isp4: new AMD capture driver - qcom: - iris: Add hierarchical coding, B-frame, and Long-Term Reference support for encoder - camss: Add SM6350 platform support - venus: Add SM6115 platform support - chips-media: wave5: Add support for Packed YUV422, CBP profile, and background detection - csi2rx: Add multistream support and 32 dma chans - Several cleanups and fixes * tag 'media/v7.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (394 commits) media: v4l2-fwnode: Fix subdev owner overwritten in v4l2_async_register_subdev_sensor() media: qcom: iris: vdec: allow GEN2 decoding into 10bit format media: qcom: iris: vdec: update find_format to handle 8bit and 10bit formats media: qcom: iris: vdec: update size and stride calculations for 10bit formats media: qcom: iris: gen2: add support for 10bit decoding media: qcom: iris: add QC10C & P010 buffer size calculations media: qcom: iris: add helpers for 8bit and 10bit formats media: qcom: iris: Fix FPS calculation and VPP FW overhead media: qcom: camss: vfe-340: Support for PIX client media: qcom: camss: vfe-340: Proper client handling media: qcom: camss: csid-340: Enable PIX interface routing media: qcom: camss: csid-340: Add port-to-interface mapping media: qcom: camss: csid-340: Switch to generic CSID_CFG/CTRL registers media: iris: Initialize HFI ops after firmware load in core init media: iris: drop struct iris_fmt media: iris: Add platform data for X1P42100 media: iris: Add hardware power on/off ops for X1P42100 media: iris: optimize COMV buffer allocation for VPU3x and VPU4x media: iris: add FPS calculation and VPP FW overhead in frequency formula media: qcom: iris: Simplify COMV size calculation ...
-rw-r--r--Documentation/admin-guide/media/amdisp4-1.rst63
-rw-r--r--Documentation/admin-guide/media/amdisp4.dot6
-rw-r--r--Documentation/admin-guide/media/rkcif-rk3588-vicap.dot29
-rw-r--r--Documentation/admin-guide/media/rkcif.rst32
-rw-r--r--Documentation/admin-guide/media/v4l-drivers.rst1
-rw-r--r--Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml10
-rw-r--r--Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml79
-rw-r--r--Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml6
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml15
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml10
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml471
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml23
-rw-r--r--Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml23
-rw-r--r--Documentation/devicetree/bindings/media/qcom,venus-common.yaml15
-rw-r--r--Documentation/devicetree/bindings/media/renesas,fcp.yaml4
-rw-r--r--Documentation/devicetree/bindings/media/renesas,vsp1.yaml2
-rw-r--r--Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml27
-rw-r--r--Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml173
-rw-r--r--Documentation/devicetree/bindings/media/rockchip-rga.yaml10
-rw-r--r--Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml39
-rw-r--r--Documentation/driver-api/media/camera-sensor.rst2
-rw-r--r--Documentation/driver-api/media/tx-rx.rst3
-rw-r--r--Documentation/userspace-api/media/cec/cec.h.rst.exceptions23
-rw-r--r--Documentation/userspace-api/media/drivers/uvcvideo.rst2
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst2
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst6
-rw-r--r--Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst5
-rw-r--r--MAINTAINERS52
-rw-r--r--drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c4
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c4
-rw-r--r--drivers/media/cec/core/cec-adap.c9
-rw-r--r--drivers/media/cec/i2c/tda9950.c2
-rw-r--r--drivers/media/cec/platform/cros-ec/cros-ec-cec.c6
-rw-r--r--drivers/media/cec/platform/seco/seco-cec.c6
-rw-r--r--drivers/media/common/videobuf2/videobuf2-core.c12
-rw-r--r--drivers/media/dvb-frontends/a8293.c4
-rw-r--r--drivers/media/dvb-frontends/af9013.c4
-rw-r--r--drivers/media/dvb-frontends/af9033.c4
-rw-r--r--drivers/media/dvb-frontends/au8522_decoder.c4
-rw-r--r--drivers/media/dvb-frontends/cxd2099.c4
-rw-r--r--drivers/media/dvb-frontends/cxd2820r_core.c4
-rw-r--r--drivers/media/dvb-frontends/dvb-pll.c44
-rw-r--r--drivers/media/dvb-frontends/helene.c4
-rw-r--r--drivers/media/dvb-frontends/lgdt3306a.c4
-rw-r--r--drivers/media/dvb-frontends/lgdt330x.c4
-rw-r--r--drivers/media/dvb-frontends/m88ds3103.c10
-rw-r--r--drivers/media/dvb-frontends/mn88443x.c8
-rw-r--r--drivers/media/dvb-frontends/mn88472.c4
-rw-r--r--drivers/media/dvb-frontends/mn88473.c4
-rw-r--r--drivers/media/dvb-frontends/mxl692.c4
-rw-r--r--drivers/media/dvb-frontends/rtl2830.c4
-rw-r--r--drivers/media/dvb-frontends/rtl2832.c8
-rw-r--r--drivers/media/dvb-frontends/rtl2832_sdr.c19
-rw-r--r--drivers/media/dvb-frontends/si2165.c4
-rw-r--r--drivers/media/dvb-frontends/si2168.c4
-rw-r--r--drivers/media/dvb-frontends/sp2.c4
-rw-r--r--drivers/media/dvb-frontends/stv090x.c4
-rw-r--r--drivers/media/dvb-frontends/stv6110x.c4
-rw-r--r--drivers/media/dvb-frontends/tc90522.c6
-rw-r--r--drivers/media/dvb-frontends/tda10071.c4
-rw-r--r--drivers/media/dvb-frontends/ts2020.c6
-rw-r--r--drivers/media/i2c/Kconfig14
-rw-r--r--drivers/media/i2c/Makefile1
-rw-r--r--drivers/media/i2c/ad5820.c4
-rw-r--r--drivers/media/i2c/adp1653.c2
-rw-r--r--drivers/media/i2c/adv7170.c4
-rw-r--r--drivers/media/i2c/adv7175.c4
-rw-r--r--drivers/media/i2c/adv7180.c24
-rw-r--r--drivers/media/i2c/adv7183.c4
-rw-r--r--drivers/media/i2c/adv7343.c4
-rw-r--r--drivers/media/i2c/adv7393.c4
-rw-r--r--drivers/media/i2c/adv7511-v4l2.c2
-rw-r--r--drivers/media/i2c/adv7604.c14
-rw-r--r--drivers/media/i2c/adv7842.c2
-rw-r--r--drivers/media/i2c/ak881x.c4
-rw-r--r--drivers/media/i2c/alvium-csi2.c21
-rw-r--r--drivers/media/i2c/bt819.c6
-rw-r--r--drivers/media/i2c/bt856.c2
-rw-r--r--drivers/media/i2c/bt866.c2
-rw-r--r--drivers/media/i2c/cs3308.c2
-rw-r--r--drivers/media/i2c/cs5345.c2
-rw-r--r--drivers/media/i2c/cs53l32a.c2
-rw-r--r--drivers/media/i2c/cvs/Kconfig21
-rw-r--r--drivers/media/i2c/cvs/Makefile4
-rw-r--r--drivers/media/i2c/cvs/core.c1043
-rw-r--r--drivers/media/i2c/cvs/icvs.h495
-rw-r--r--drivers/media/i2c/cvs/v4l2.c618
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c2
-rw-r--r--drivers/media/i2c/ds90ub913.c4
-rw-r--r--drivers/media/i2c/ds90ub953.c6
-rw-r--r--drivers/media/i2c/ds90ub960.c8
-rw-r--r--drivers/media/i2c/dw9714.c2
-rw-r--r--drivers/media/i2c/dw9719.c10
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c2
-rw-r--r--drivers/media/i2c/imx219.c45
-rw-r--r--drivers/media/i2c/imx274.c60
-rw-r--r--drivers/media/i2c/imx334.c93
-rw-r--r--drivers/media/i2c/imx335.c87
-rw-r--r--drivers/media/i2c/imx412.c82
-rw-r--r--drivers/media/i2c/ir-kbd-i2c.c6
-rw-r--r--drivers/media/i2c/isl7998x.c2
-rw-r--r--drivers/media/i2c/ks0127.c6
-rw-r--r--drivers/media/i2c/lm3560.c383
-rw-r--r--drivers/media/i2c/lm3646.c4
-rw-r--r--drivers/media/i2c/m52790.c2
-rw-r--r--drivers/media/i2c/max2175.c4
-rw-r--r--drivers/media/i2c/ml86v7667.c4
-rw-r--r--drivers/media/i2c/msp3400-driver.c2
-rw-r--r--drivers/media/i2c/mt9m001.c2
-rw-r--r--drivers/media/i2c/mt9m111.c2
-rw-r--r--drivers/media/i2c/mt9p031.c3
-rw-r--r--drivers/media/i2c/mt9t112.c2
-rw-r--r--drivers/media/i2c/mt9v011.c2
-rw-r--r--drivers/media/i2c/ov13858.c4
-rw-r--r--drivers/media/i2c/ov13b10.c47
-rw-r--r--drivers/media/i2c/ov2640.c2
-rw-r--r--drivers/media/i2c/ov2659.c2
-rw-r--r--drivers/media/i2c/ov5640.c4
-rw-r--r--drivers/media/i2c/ov5645.c4
-rw-r--r--drivers/media/i2c/ov5647.c2
-rw-r--r--drivers/media/i2c/ov7640.c2
-rw-r--r--drivers/media/i2c/ov7670.c4
-rw-r--r--drivers/media/i2c/ov772x.c2
-rw-r--r--drivers/media/i2c/ov7740.c2
-rw-r--r--drivers/media/i2c/ov9282.c67
-rw-r--r--drivers/media/i2c/ov9640.c2
-rw-r--r--drivers/media/i2c/ov9650.c4
-rw-r--r--drivers/media/i2c/rj54n1cb0c.c2
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c2
-rw-r--r--drivers/media/i2c/s5k5baf.c2
-rw-r--r--drivers/media/i2c/saa6588.c2
-rw-r--r--drivers/media/i2c/saa6752hs.c2
-rw-r--r--drivers/media/i2c/saa7110.c2
-rw-r--r--drivers/media/i2c/saa7115.c14
-rw-r--r--drivers/media/i2c/saa7127.c10
-rw-r--r--drivers/media/i2c/saa717x.c2
-rw-r--r--drivers/media/i2c/saa7185.c2
-rw-r--r--drivers/media/i2c/sony-btf-mpx.c2
-rw-r--r--drivers/media/i2c/tc358743.c4
-rw-r--r--drivers/media/i2c/tda1997x.c6
-rw-r--r--drivers/media/i2c/tda7432.c2
-rw-r--r--drivers/media/i2c/tda9840.c2
-rw-r--r--drivers/media/i2c/tea6415c.c2
-rw-r--r--drivers/media/i2c/tea6420.c2
-rw-r--r--drivers/media/i2c/ths7303.c6
-rw-r--r--drivers/media/i2c/ths8200.c4
-rw-r--r--drivers/media/i2c/tlv320aic23b.c2
-rw-r--r--drivers/media/i2c/tvaudio.c2
-rw-r--r--drivers/media/i2c/tvp514x.c63
-rw-r--r--drivers/media/i2c/tvp5150.c2
-rw-r--r--drivers/media/i2c/tvp7002.c2
-rw-r--r--drivers/media/i2c/tw2804.c2
-rw-r--r--drivers/media/i2c/tw9900.c2
-rw-r--r--drivers/media/i2c/tw9903.c2
-rw-r--r--drivers/media/i2c/tw9906.c2
-rw-r--r--drivers/media/i2c/tw9910.c2
-rw-r--r--drivers/media/i2c/uda1342.c2
-rw-r--r--drivers/media/i2c/upd64031a.c2
-rw-r--r--drivers/media/i2c/upd64083.c2
-rw-r--r--drivers/media/i2c/video-i2c.c13
-rw-r--r--drivers/media/i2c/vp27smpx.c2
-rw-r--r--drivers/media/i2c/vpx3220.c6
-rw-r--r--drivers/media/i2c/wm8739.c2
-rw-r--r--drivers/media/i2c/wm8775.c2
-rw-r--r--drivers/media/pci/Kconfig1
-rw-r--r--drivers/media/pci/Makefile1
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c14
-rw-r--r--drivers/media/pci/dm1105/dm1105.c8
-rw-r--r--drivers/media/pci/hws/Kconfig11
-rw-r--r--drivers/media/pci/hws/Makefile4
-rw-r--r--drivers/media/pci/hws/hws.h173
-rw-r--r--drivers/media/pci/hws/hws_irq.c269
-rw-r--r--drivers/media/pci/hws/hws_irq.h10
-rw-r--r--drivers/media/pci/hws/hws_pci.c865
-rw-r--r--drivers/media/pci/hws/hws_reg.h136
-rw-r--r--drivers/media/pci/hws/hws_v4l2_ioctl.c919
-rw-r--r--drivers/media/pci/hws/hws_v4l2_ioctl.h36
-rw-r--r--drivers/media/pci/hws/hws_video.c1490
-rw-r--r--drivers/media/pci/hws/hws_video.h29
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c13
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c26
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c12
-rw-r--r--drivers/media/pci/mgb4/mgb4_vin.c8
-rw-r--r--drivers/media/pci/mgb4/mgb4_vout.c8
-rw-r--r--drivers/media/pci/saa7134/saa7134-video.c25
-rw-r--r--drivers/media/pci/solo6x10/Kconfig1
-rw-r--r--drivers/media/platform/Kconfig1
-rw-r--r--drivers/media/platform/Makefile1
-rw-r--r--drivers/media/platform/amd/Kconfig3
-rw-r--r--drivers/media/platform/amd/Makefile3
-rw-r--r--drivers/media/platform/amd/isp4/Kconfig17
-rw-r--r--drivers/media/platform/amd/isp4/Makefile10
-rw-r--r--drivers/media/platform/amd/isp4/isp4.c240
-rw-r--r--drivers/media/platform/amd/isp4/isp4.h20
-rw-r--r--drivers/media/platform/amd/isp4/isp4_debug.c271
-rw-r--r--drivers/media/platform/amd/isp4/isp4_debug.h41
-rw-r--r--drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h318
-rw-r--r--drivers/media/platform/amd/isp4/isp4_hw_reg.h124
-rw-r--r--drivers/media/platform/amd/isp4/isp4_interface.c832
-rw-r--r--drivers/media/platform/amd/isp4/isp4_interface.h144
-rw-r--r--drivers/media/platform/amd/isp4/isp4_subdev.c1047
-rw-r--r--drivers/media/platform/amd/isp4/isp4_subdev.h127
-rw-r--r--drivers/media/platform/amd/isp4/isp4_video.c797
-rw-r--r--drivers/media/platform/amd/isp4/isp4_video.h57
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-params.c4
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-core.c24
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-isp.c8
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-resizer.c1
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-tpg.c1
-rw-r--r--drivers/media/platform/aspeed/aspeed-video.c1
-rw-r--r--drivers/media/platform/cadence/Kconfig1
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c449
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.c4
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-helper.h2
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-hw.c7
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c47
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c73
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.c11
-rw-r--r--drivers/media/platform/chips-media/wave5/wave5-vpuapi.h2
-rw-r--r--drivers/media/platform/marvell/cafe-driver.c1
-rw-r--r--drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c3
-rw-r--r--drivers/media/platform/nuvoton/npcm-video.c34
-rw-r--r--drivers/media/platform/nvidia/tegra-vde/Kconfig1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c4
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h16
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c9
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c11
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c20
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c6
-rw-r--r--drivers/media/platform/qcom/camss/Makefile12
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-340.c87
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-680.c44
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen2.c47
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid-gen3.c42
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c45
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h1
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c125
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c1
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h2
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.c14
-rw-r--r--drivers/media/platform/qcom/camss/camss-format.h1
-rw-r--r--drivers/media/platform/qcom/camss/camss-tpg-gen1.c231
-rw-r--r--drivers/media/platform/qcom/camss/camss-tpg.c519
-rw-r--r--drivers/media/platform/qcom/camss/camss-tpg.h118
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-340.c152
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c31
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c19
-rw-r--r--drivers/media/platform/qcom/camss/camss.c433
-rw-r--r--drivers/media/platform/qcom/camss/camss.h6
-rw-r--r--drivers/media/platform/qcom/iris/Kconfig1
-rw-r--r--drivers/media/platform/qcom/iris/Makefile9
-rw-r--r--drivers/media/platform/qcom/iris/iris_buffer.c283
-rw-r--r--drivers/media/platform/qcom/iris/iris_common.c8
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.c5
-rw-r--r--drivers/media/platform/qcom/iris/iris_core.h14
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.c539
-rw-r--r--drivers/media/platform/qcom/iris/iris_ctrls.h16
-rw-r--r--drivers/media/platform/qcom/iris/iris_firmware.c13
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.c6
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_common.h13
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1.c (renamed from drivers/media/platform/qcom/iris/iris_platform_gen1.c)325
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1.h6
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c141
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h49
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c11
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2.c (renamed from drivers/media/platform/qcom/iris/iris_platform_gen2.c)657
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2.h5
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c209
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h19
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c18
-rw-r--r--drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c48
-rw-r--r--drivers/media/platform/qcom/iris/iris_instance.h19
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_common.h140
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_qcs8300.h1
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8250.h29
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_sm8550.h30
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_vpu2.c123
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_vpu3x.c249
-rw-r--r--drivers/media/platform/qcom/iris/iris_platform_x1p42100.h22
-rw-r--r--drivers/media/platform/qcom/iris/iris_probe.c28
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.c21
-rw-r--r--drivers/media/platform/qcom/iris/iris_utils.h2
-rw-r--r--drivers/media/platform/qcom/iris/iris_vb2.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_vdec.c142
-rw-r--r--drivers/media/platform/qcom/iris/iris_venc.c100
-rw-r--r--drivers/media/platform/qcom/iris/iris_vidc.c14
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu2.c2
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_buffer.c87
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.c24
-rw-r--r--drivers/media/platform/qcom/iris/iris_vpu_common.h2
-rw-r--r--drivers/media/platform/qcom/venus/core.c58
-rw-r--r--drivers/media/platform/qcom/venus/core.h17
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c4
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c16
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.c24
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform.h2
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v4.c20
-rw-r--r--drivers/media/platform/qcom/venus/hfi_platform_v6.c16
-rw-r--r--drivers/media/platform/qcom/venus/pm_helpers.c154
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c1
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h4
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h29
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c326
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c31
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c33
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_clu.c15
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c63
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c257
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c27
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgo.c10
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hgt.c16
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c55
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c15
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_lut.c15
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_pipe.c60
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c44
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c13
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c13
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c29
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_video.c157
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_wpf.c30
-rw-r--r--drivers/media/platform/rockchip/rga/Kconfig1
-rw-r--r--drivers/media/platform/rockchip/rga/Makefile2
-rw-r--r--drivers/media/platform/rockchip/rga/rga-buf.c89
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.c357
-rw-r--r--drivers/media/platform/rockchip/rga/rga-hw.h16
-rw-r--r--drivers/media/platform/rockchip/rga/rga.c576
-rw-r--r--drivers/media/platform/rockchip/rga/rga.h88
-rw-r--r--drivers/media/platform/rockchip/rga/rga3-hw.c507
-rw-r--r--drivers/media/platform/rockchip/rga/rga3-hw.h192
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c148
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h1
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-common.h2
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-dev.c18
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-params.c68
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h15
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h39
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c51
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h40
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c109
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c93
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h57
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c171
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c351
-rw-r--r--drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c502
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c1
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c6
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c6
-rw-r--r--drivers/media/platform/synopsys/dw-mipi-csi2rx.c128
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c4
-rw-r--r--drivers/media/platform/ti/Kconfig1
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c2
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c1217
-rw-r--r--drivers/media/platform/ti/vpe/vip.c16
-rw-r--r--drivers/media/platform/ti/vpe/vpe.c3
-rw-r--r--drivers/media/platform/verisilicon/hantro_hw.h1
-rw-r--r--drivers/media/platform/verisilicon/hantro_v4l2.c6
-rw-r--r--drivers/media/radio/radio-si476x.c1
-rw-r--r--drivers/media/radio/radio-tea5764.c2
-rw-r--r--drivers/media/radio/saa7706h.c4
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c2
-rw-r--r--drivers/media/radio/si470x/radio-si470x-usb.c11
-rw-r--r--drivers/media/radio/si4713/si4713.c2
-rw-r--r--drivers/media/radio/tef6862.c4
-rw-r--r--drivers/media/rc/imon.c4
-rw-r--r--drivers/media/rc/imon_raw.c18
-rw-r--r--drivers/media/rc/ir_toy.c23
-rw-r--r--drivers/media/rc/mceusb.c2
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_bridge.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_demod.c4
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_mux.c8
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_tuner.c4
-rw-r--r--drivers/media/test-drivers/vimc/vimc-core.c1
-rw-r--r--drivers/media/test-drivers/visl/visl-core.c4
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.c6
-rw-r--r--drivers/media/test-drivers/vivid/vivid-ctrls.c15
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c38
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.h1
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-out.c6
-rw-r--r--drivers/media/tuners/e4000.c4
-rw-r--r--drivers/media/tuners/fc2580.c4
-rw-r--r--drivers/media/tuners/m88rs6000t.c4
-rw-r--r--drivers/media/tuners/mt2060.c4
-rw-r--r--drivers/media/tuners/mxl301rf.c4
-rw-r--r--drivers/media/tuners/qm1d1b0004.c4
-rw-r--r--drivers/media/tuners/qm1d1c0042.c4
-rw-r--r--drivers/media/tuners/si2157.c10
-rw-r--r--drivers/media/tuners/tda18212.c4
-rw-r--r--drivers/media/tuners/tda18250.c4
-rw-r--r--drivers/media/tuners/tua9001.c4
-rw-r--r--drivers/media/usb/airspy/airspy.c8
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c16
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c4
-rw-r--r--drivers/media/usb/go7007/s2250-board.c2
-rw-r--r--drivers/media/usb/gspca/gspca.c17
-rw-r--r--drivers/media/usb/gspca/sonixb.c2
-rw-r--r--drivers/media/usb/gspca/touptek.c17
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-core.c26
-rw-r--r--drivers/media/usb/msi2500/msi2500.c32
-rw-r--r--drivers/media/usb/pwc/pwc-if.c13
-rw-r--r--drivers/media/usb/s2255/s2255drv.c12
-rw-r--r--drivers/media/usb/uvc/uvc_ctrl.c212
-rw-r--r--drivers/media/usb/uvc/uvc_driver.c4
-rw-r--r--drivers/media/usb/uvc/uvc_status.c28
-rw-r--r--drivers/media/usb/uvc/uvc_v4l2.c11
-rw-r--r--drivers/media/usb/uvc/uvc_video.c204
-rw-r--r--drivers/media/usb/uvc/uvcvideo.h5
-rw-r--r--drivers/media/v4l2-core/tuner-core.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c147
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c13
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-defs.c2
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-request.c14
-rw-r--r--drivers/media/v4l2-core/v4l2-dev.c5
-rw-r--r--drivers/media/v4l2-core/v4l2-fwnode.c6
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c43
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470_board_data.c158
-rw-r--r--drivers/staging/media/Kconfig4
-rw-r--r--drivers/staging/media/Makefile1
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c31
-rw-r--r--drivers/staging/media/atomisp/i2c/ov2722.h649
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp.h182
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h7
-rw-r--r--drivers/staging/media/atomisp/include/linux/atomisp_platform.h42
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.c202
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_css20.c26
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_compat_css20.h2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c2
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c6
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.c20
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.h12
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_v4l2.c5
-rw-r--r--drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h9
-rw-r--r--drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h11
-rw-r--r--drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c3
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c9
-rw-r--r--drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c3
-rw-r--r--drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h3
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h3
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c22
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c3
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c14
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c30
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c3
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c4
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h87
-rw-r--r--drivers/staging/media/atomisp/pci/hive_isp_css_include/math_support.h6
-rw-r--r--drivers/staging/media/atomisp/pci/hmm/hmm_bo.c27
-rw-r--r--drivers/staging/media/atomisp/pci/ia_css_types.h14
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c26
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h20
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c17
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c14
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h14
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c6
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c7
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/dvs/dvs_1.0/ia_css_dvs.host.c14
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c7
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c3
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c6
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c13
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/vf/vf_1.0/ia_css_vf.host.c25
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c114
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/debug/src/ia_css_debug.c17
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c24
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/isys/src/virtual_isys.c3
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c3
-rw-r--r--drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c4
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css.c158
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_firmware.c6
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_dvs.c12
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_param_shading.c22
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_params.c74
-rw-r--r--drivers/staging/media/atomisp/pci/sh_css_sp.c41
-rw-r--r--drivers/staging/media/atomisp/pci/system_local.c3
-rw-r--r--drivers/staging/media/av7110/av7110.c49
-rw-r--r--drivers/staging/media/av7110/av7110.h4
-rw-r--r--drivers/staging/media/av7110/av7110_av.c89
-rw-r--r--drivers/staging/media/av7110/av7110_ca.c3
-rw-r--r--drivers/staging/media/av7110/av7110_hw.c46
-rw-r--r--drivers/staging/media/av7110/av7110_hw.h71
-rw-r--r--drivers/staging/media/av7110/av7110_ir.c4
-rw-r--r--drivers/staging/media/av7110/av7110_v4l.c22
-rw-r--r--drivers/staging/media/deprecated/atmel/Kconfig47
-rw-r--r--drivers/staging/media/deprecated/atmel/Makefile8
-rw-r--r--drivers/staging/media/deprecated/atmel/TODO34
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-base.c2008
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-clk.c311
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc-regs.h413
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-isc.h362
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c644
-rw-r--r--drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c607
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c8
-rw-r--r--drivers/staging/media/imx/imx-media-utils.c16
-rw-r--r--drivers/staging/media/imx/imx-media.h7
-rw-r--r--drivers/staging/media/ipu3/ipu3-css-params.c8
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h78
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h4
-rw-r--r--drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h12
-rw-r--r--drivers/staging/media/ipu7/ipu7-buttress-regs.h10
-rw-r--r--drivers/staging/media/ipu7/ipu7.c22
-rw-r--r--drivers/staging/media/meson/vdec/codec_h264.c2
-rw-r--r--drivers/staging/media/meson/vdec/codec_mpeg12.c2
-rw-r--r--drivers/staging/media/meson/vdec/vdec.c4
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus.c10
-rw-r--r--drivers/staging/media/sunxi/cedrus/cedrus_h264.c3
-rw-r--r--drivers/staging/media/tegra-video/tegra210.c3
-rw-r--r--drivers/staging/media/tegra-video/vi.c4
-rw-r--r--include/linux/usb/uvc.h10
-rw-r--r--include/media/i2c/lm3560.h84
-rw-r--r--include/media/media-entity.h10
-rw-r--r--include/media/v4l2-async.h4
-rw-r--r--include/media/v4l2-common.h6
-rw-r--r--include/media/videobuf2-core.h8
-rw-r--r--include/media/vsp1.h5
-rw-r--r--include/uapi/linux/cec-funcs.h182
-rw-r--r--include/uapi/linux/cec.h31
-rw-r--r--include/uapi/linux/rkisp1-config.h113
-rw-r--r--include/uapi/linux/v4l2-controls.h2
523 files changed, 22836 insertions, 11348 deletions
diff --git a/Documentation/admin-guide/media/amdisp4-1.rst b/Documentation/admin-guide/media/amdisp4-1.rst
new file mode 100644
index 000000000000..878141154f96
--- /dev/null
+++ b/Documentation/admin-guide/media/amdisp4-1.rst
@@ -0,0 +1,63 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+.. include:: <isonum.txt>
+
+====================================
+AMD Image Signal Processor (amdisp4)
+====================================
+
+Introduction
+============
+
+This file documents the driver for the AMD ISP4 that is part of
+AMD Ryzen AI Max 300 Series.
+
+The driver is located under drivers/media/platform/amd/isp4 and uses
+the Media-Controller API.
+
+The driver exposes one video capture device to userspace and provide
+web camera like interface. Internally the video device is connected
+to the isp4 sub-device responsible for communication with the CCPU FW.
+
+Topology
+========
+
+.. _amdisp4_topology_graph:
+
+.. kernel-figure:: amdisp4.dot
+ :alt: Diagram of the media pipeline topology
+ :align: center
+
+
+
+The driver has 1 sub-device: Representing isp4 image signal processor.
+The driver has 1 video device: Capture device for retrieving images.
+
+- ISP4 Image Signal Processing Subdevice Node
+
+---------------------------------------------
+
+The isp4 is represented as a single V4L2 subdev, the sub-device does not
+provide interface to the user space. The sub-device is connected to one video node
+(isp4_capture) with immutable active link. The sub-device represents ISP with
+connected sensor similar to smart cameras (sensors with integrated ISP).
+sub-device has only one link to the video device for capturing the frames.
+The sub-device communicates with CCPU FW for streaming configuration and
+buffer management.
+
+
+- isp4_capture - Frames Capture Video Node
+
+------------------------------------------
+
+Isp4_capture is a capture device to capture frames to memory.
+The entity is connected to isp4 sub-device. The video device
+provides web camera like interface to userspace. It supports
+mmap and dma buf types of memory.
+
+Capturing Video Frames Example
+==============================
+
+.. code-block:: bash
+
+ v4l2-ctl "-d" "/dev/video0" "--set-fmt-video=width=1920,height=1080,pixelformat=NV12" "--stream-mmap" "--stream-count=10"
diff --git a/Documentation/admin-guide/media/amdisp4.dot b/Documentation/admin-guide/media/amdisp4.dot
new file mode 100644
index 000000000000..978f30c1a31a
--- /dev/null
+++ b/Documentation/admin-guide/media/amdisp4.dot
@@ -0,0 +1,6 @@
+digraph board {
+ rankdir=TB
+ n00000001 [label="{{} | amd isp4\n | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000001:port0 -> n00000003 [style=bold]
+ n00000003 [label="Preview\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
+}
diff --git a/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot
new file mode 100644
index 000000000000..f6d3404920b5
--- /dev/null
+++ b/Documentation/admin-guide/media/rkcif-rk3588-vicap.dot
@@ -0,0 +1,29 @@
+digraph board {
+ rankdir=TB
+ n00000007 [label="{{<port0> 0} | rkcif-mipi2\n/dev/v4l-subdev0 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000007:port1 -> n0000000a
+ n00000007:port1 -> n00000010 [style=dashed]
+ n00000007:port1 -> n00000016 [style=dashed]
+ n00000007:port1 -> n0000001c [style=dashed]
+ n0000000a [label="rkcif-mipi2-id0\n/dev/video0", shape=box, style=filled, fillcolor=yellow]
+ n00000010 [label="rkcif-mipi2-id1\n/dev/video1", shape=box, style=filled, fillcolor=yellow]
+ n00000016 [label="rkcif-mipi2-id2\n/dev/video2", shape=box, style=filled, fillcolor=yellow]
+ n0000001c [label="rkcif-mipi2-id3\n/dev/video3", shape=box, style=filled, fillcolor=yellow]
+ n00000025 [label="{{<port0> 0} | rkcif-mipi4\n/dev/v4l-subdev1 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000025:port1 -> n00000028
+ n00000025:port1 -> n0000002e [style=dashed]
+ n00000025:port1 -> n00000034 [style=dashed]
+ n00000025:port1 -> n0000003a [style=dashed]
+ n00000028 [label="rkcif-mipi4-id0\n/dev/video4", shape=box, style=filled, fillcolor=yellow]
+ n0000002e [label="rkcif-mipi4-id1\n/dev/video5", shape=box, style=filled, fillcolor=yellow]
+ n00000034 [label="rkcif-mipi4-id2\n/dev/video6", shape=box, style=filled, fillcolor=yellow]
+ n0000003a [label="rkcif-mipi4-id3\n/dev/video7", shape=box, style=filled, fillcolor=yellow]
+ n00000043 [label="{{<port0> 0} | dw-mipi-csi2rx fdd30000.csi\n/dev/v4l-subdev2 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000043:port1 -> n00000007:port0
+ n00000048 [label="{{<port0> 0} | dw-mipi-csi2rx fdd50000.csi\n/dev/v4l-subdev3 | {<port1> 1}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000048:port1 -> n00000025:port0
+ n0000004d [label="{{} | imx415 3-001a\n/dev/v4l-subdev4 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+ n0000004d:port0 -> n00000043:port0
+ n00000051 [label="{{} | imx415 4-001a\n/dev/v4l-subdev5 | {<port0> 0}}", shape=Mrecord, style=filled, fillcolor=green]
+ n00000051:port0 -> n00000048:port0
+}
diff --git a/Documentation/admin-guide/media/rkcif.rst b/Documentation/admin-guide/media/rkcif.rst
index 2558c121abc4..313a0ea45d16 100644
--- a/Documentation/admin-guide/media/rkcif.rst
+++ b/Documentation/admin-guide/media/rkcif.rst
@@ -77,3 +77,35 @@ and the following video devices:
.. kernel-figure:: rkcif-rk3568-vicap.dot
:alt: Topology of the RK3568 Video Capture (VICAP) unit
:align: center
+
+Rockchip RK3588 Video Capture (VICAP)
+-------------------------------------
+
+The RK3588 Video Capture (VICAP) unit features a digital video port and six
+MIPI CSI-2 capture interfaces that can receive video data independently.
+The DVP accepts parallel video data, BT.656 and BT.1120.
+Since the BT.1120 protocol may feature more than one stream, the RK3588 VICAP
+DVP features four DMA engines that can capture different streams.
+Similarly, the RK3588 VICAP MIPI CSI-2 receivers feature four DMA engines each
+to handle different Virtual Channels (VCs).
+
+The rkcif driver represents this hardware variant by exposing the following
+V4L2 subdevices:
+
+* dw-mipi-csi2rx fdd30000.csi: MIPI CSI-2 receiver connected to MIPI DPHY0
+* dw-mipi-csi2rx fdd50000.csi: MIPI CSI-2 receiver connected to MIPI DPHY1
+* rkcif-mipi2: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to
+ MIPI DPHY0
+* rkcif-mipi4: INTERFACE/CROP block for the MIPI CSI-2 receiver connected to
+ MIPI DPHY1
+
+and the following video devices:
+
+* rkcif-mipi2-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi2
+ INTERFACE/CROP block.
+* rkcif-mipi4-id{0,1,2,3}: The DMA engines connected to the rkcif-mipi4
+ INTERFACE/CROP block.
+
+.. kernel-figure:: rkcif-rk3588-vicap.dot
+ :alt: Topology of the RK3588 Video Capture (VICAP) unit
+ :align: center
diff --git a/Documentation/admin-guide/media/v4l-drivers.rst b/Documentation/admin-guide/media/v4l-drivers.rst
index d31da8e0a54f..4621eae9fa1e 100644
--- a/Documentation/admin-guide/media/v4l-drivers.rst
+++ b/Documentation/admin-guide/media/v4l-drivers.rst
@@ -9,6 +9,7 @@ Video4Linux (V4L) driver-specific documentation
.. toctree::
:maxdepth: 2
+ amdisp4-1
bttv
c3-isp
cafe_ccic
diff --git a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml
index 541325f900a1..01f2afa023f0 100644
--- a/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml
+++ b/Documentation/devicetree/bindings/media/allwinner,sun4i-a10-video-engine.yaml
@@ -63,6 +63,16 @@ properties:
CMA pool to use for buffers allocation instead of the default
CMA pool.
+ # FIXME: This should be made required eventually once every SoC will
+ # have the MBUS declared.
+ interconnects:
+ maxItems: 1
+
+ # FIXME: This should be made required eventually once every SoC will
+ # have the MBUS declared.
+ interconnect-names:
+ const: dma-mem
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml
new file mode 100644
index 000000000000..10a50a410748
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,msm8939-venus.yaml
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/qcom,msm8939-venus.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm MSM8939 Venus video encode and decode accelerators
+
+maintainers:
+ - André Apitzsch <git@apitzsch.eu>
+ - Erikas Bitovtas <xerikasxx@gmail.com>
+
+description:
+ The Venus IP is a video encode and decode accelerator present
+ on Qualcomm platforms
+
+allOf:
+ - $ref: qcom,venus-common.yaml#
+
+properties:
+ compatible:
+ const: qcom,msm8939-venus
+
+ power-domains:
+ maxItems: 3
+
+ power-domain-names:
+ items:
+ - const: venus
+ - const: vcodec0
+ - const: vcodec1
+
+ clocks:
+ maxItems: 5
+
+ clock-names:
+ items:
+ - const: core
+ - const: iface
+ - const: bus
+ - const: vcodec0_core
+ - const: vcodec1_core
+
+ iommus:
+ maxItems: 1
+
+required:
+ - compatible
+ - iommus
+ - power-domain-names
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/qcom,gcc-msm8939.h>
+
+ video-codec@1d00000 {
+ compatible = "qcom,msm8939-venus";
+ reg = <0x01d00000 0xff000>;
+ interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&gcc GCC_VENUS0_VCODEC0_CLK>,
+ <&gcc GCC_VENUS0_AHB_CLK>,
+ <&gcc GCC_VENUS0_AXI_CLK>,
+ <&gcc GCC_VENUS0_CORE0_VCODEC0_CLK>,
+ <&gcc GCC_VENUS0_CORE1_VCODEC0_CLK>;
+ clock-names = "core",
+ "iface",
+ "bus",
+ "vcodec0_core",
+ "vcodec1_core";
+ power-domains = <&gcc VENUS_GDSC>,
+ <&gcc VENUS_CORE0_GDSC>,
+ <&gcc VENUS_CORE1_GDSC>;
+ power-domain-names = "venus", "vcodec0", "vcodec1";
+ iommus = <&apps_iommu 5>;
+ memory-region = <&venus_mem>;
+ };
diff --git a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml
index 7e6dc410c2d2..5977e7d0a71b 100644
--- a/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,qcm2290-venus.yaml
@@ -18,7 +18,11 @@ allOf:
properties:
compatible:
- const: qcom,qcm2290-venus
+ oneOf:
+ - items:
+ - const: qcom,sm6115-venus
+ - const: qcom,qcm2290-venus
+ - const: qcom,qcm2290-venus
power-domains:
maxItems: 3
diff --git a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml
index bfd8b1ad4731..b21bed314848 100644
--- a/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sc7180-venus.yaml
@@ -91,6 +91,21 @@ properties:
deprecated: true
additionalProperties: false
+ video-firmware:
+ type: object
+ additionalProperties: false
+
+ description: |
+ Firmware subnode is needed when the platform does not
+ have TrustZone.
+
+ properties:
+ iommus:
+ maxItems: 1
+
+ required:
+ - iommus
+
required:
- compatible
- power-domain-names
diff --git a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml
index 413c5b4ee650..9725fcb761dc 100644
--- a/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sc7280-venus.yaml
@@ -43,8 +43,7 @@ properties:
- const: vcodec_bus
iommus:
- minItems: 1
- maxItems: 2
+ maxItems: 1
interconnects:
maxItems: 2
@@ -120,12 +119,7 @@ examples:
<&mmss_noc MASTER_VIDEO_P0 0 &mc_virt SLAVE_EBI1 0>;
interconnect-names = "cpu-cfg", "video-mem";
- iommus = <&apps_smmu 0x2180 0x20>,
- <&apps_smmu 0x2184 0x20>;
+ iommus = <&apps_smmu 0x2180 0x20>;
memory-region = <&video_mem>;
-
- video-firmware {
- iommus = <&apps_smmu 0x21a2 0x0>;
- };
};
diff --git a/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml
new file mode 100644
index 000000000000..96974d90d8c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/qcom,sm6350-camss.yaml
@@ -0,0 +1,471 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/qcom,sm6350-camss.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm SM6350 Camera Subsystem (CAMSS)
+
+maintainers:
+ - Luca Weiss <luca.weiss@fairphone.com>
+
+description:
+ The CAMSS IP is a CSI decoder and ISP present on Qualcomm platforms.
+
+properties:
+ compatible:
+ const: qcom,sm6350-camss
+
+ reg:
+ maxItems: 24
+
+ reg-names:
+ items:
+ - const: csid0
+ - const: csid1
+ - const: csid2
+ - const: csid_lite
+ - const: csiphy0
+ - const: csiphy1
+ - const: csiphy2
+ - const: csiphy3
+ - const: vfe0
+ - const: vfe1
+ - const: vfe2
+ - const: vfe_lite
+ - const: a5_csr
+ - const: a5_qgic
+ - const: a5_sierra
+ - const: bps
+ - const: camnoc
+ - const: core_top_csr_tcsr
+ - const: cpas_cdm
+ - const: cpas_top
+ - const: ipe
+ - const: jpeg_dma
+ - const: jpeg_enc
+ - const: lrme
+
+ clocks:
+ maxItems: 39
+
+ clock-names:
+ items:
+ - const: cam_axi
+ - const: soc_ahb
+ - const: camnoc_axi
+ - const: core_ahb
+ - const: cpas_ahb
+ - const: csiphy0
+ - const: csiphy0_timer
+ - const: csiphy1
+ - const: csiphy1_timer
+ - const: csiphy2
+ - const: csiphy2_timer
+ - const: csiphy3
+ - const: csiphy3_timer
+ - const: vfe0_axi
+ - const: vfe0
+ - const: vfe0_cphy_rx
+ - const: vfe0_csid
+ - const: vfe1_axi
+ - const: vfe1
+ - const: vfe1_cphy_rx
+ - const: vfe1_csid
+ - const: vfe2_axi
+ - const: vfe2
+ - const: vfe2_cphy_rx
+ - const: vfe2_csid
+ - const: vfe_lite
+ - const: vfe_lite_cphy_rx
+ - const: vfe_lite_csid
+ - const: bps
+ - const: bps_ahb
+ - const: bps_areg
+ - const: bps_axi
+ - const: icp
+ - const: ipe0
+ - const: ipe0_ahb
+ - const: ipe0_areg
+ - const: ipe0_axi
+ - const: jpeg
+ - const: lrme
+
+ interrupts:
+ maxItems: 18
+
+ interrupt-names:
+ items:
+ - const: csid0
+ - const: csid1
+ - const: csid2
+ - const: csid_lite
+ - const: csiphy0
+ - const: csiphy1
+ - const: csiphy2
+ - const: csiphy3
+ - const: vfe0
+ - const: vfe1
+ - const: vfe2
+ - const: vfe_lite
+ - const: a5
+ - const: cpas
+ - const: cpas_cdm
+ - const: jpeg_dma
+ - const: jpeg_enc
+ - const: lrme
+
+ interconnects:
+ maxItems: 4
+
+ interconnect-names:
+ items:
+ - const: ahb
+ - const: hf_mnoc
+ - const: sf_mnoc
+ - const: sf_icp_mnoc
+
+ iommus:
+ maxItems: 14
+
+ power-domains:
+ maxItems: 6
+
+ power-domain-names:
+ items:
+ - const: ife0
+ - const: ife1
+ - const: ife2
+ - const: top
+ - const: bps
+ - const: ipe
+
+ vdd-csiphy0-0p9-supply:
+ description:
+ Phandle to a 0.9V regulator supply to CSIPHY0.
+
+ vdd-csiphy0-1p25-supply:
+ description:
+ Phandle to a 1.25V regulator supply to CSIPHY0.
+
+ vdd-csiphy1-0p9-supply:
+ description:
+ Phandle to a 0.9V regulator supply to CSIPHY1.
+
+ vdd-csiphy1-1p25-supply:
+ description:
+ Phandle to a 1.25V regulator supply to CSIPHY1.
+
+ vdd-csiphy2-0p9-supply:
+ description:
+ Phandle to a 0.9V regulator supply to CSIPHY2.
+
+ vdd-csiphy2-1p25-supply:
+ description:
+ Phandle to a 1.25V regulator supply to CSIPHY2.
+
+ vdd-csiphy3-0p9-supply:
+ description:
+ Phandle to a 0.9V regulator supply to CSIPHY3.
+
+ vdd-csiphy3-1p25-supply:
+ description:
+ Phandle to a 1.25V regulator supply to CSIPHY3.
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ description:
+ CSI input ports.
+
+ patternProperties:
+ "^port@[0-3]$":
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+
+ description:
+ Input port for receiving CSI data from a CSIPHY.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ minItems: 1
+ maxItems: 4
+
+ bus-type:
+ enum:
+ - 1 # MEDIA_BUS_TYPE_CSI2_CPHY
+ - 4 # MEDIA_BUS_TYPE_CSI2_DPHY
+
+ required:
+ - data-lanes
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - clocks
+ - clock-names
+ - interrupts
+ - interrupt-names
+ - interconnects
+ - interconnect-names
+ - iommus
+ - power-domains
+ - power-domain-names
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/qcom,gcc-sm6350.h>
+ #include <dt-bindings/clock/qcom,sm6350-camcc.h>
+ #include <dt-bindings/interconnect/qcom,icc.h>
+ #include <dt-bindings/interconnect/qcom,sm6350.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/media/video-interfaces.h>
+ #include <dt-bindings/power/qcom-rpmpd.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ isp@acb3000 {
+ compatible = "qcom,sm6350-camss";
+
+ reg = <0x0 0x0acb3000 0x0 0x1000>,
+ <0x0 0x0acba000 0x0 0x1000>,
+ <0x0 0x0acc1000 0x0 0x1000>,
+ <0x0 0x0acc8000 0x0 0x1000>,
+ <0x0 0x0ac65000 0x0 0x1000>,
+ <0x0 0x0ac66000 0x0 0x1000>,
+ <0x0 0x0ac67000 0x0 0x1000>,
+ <0x0 0x0ac68000 0x0 0x1000>,
+ <0x0 0x0acaf000 0x0 0x4000>,
+ <0x0 0x0acb6000 0x0 0x4000>,
+ <0x0 0x0acbd000 0x0 0x4000>,
+ <0x0 0x0acc4000 0x0 0x4000>,
+ <0x0 0x0ac18000 0x0 0x3000>,
+ <0x0 0x0ac00000 0x0 0x6000>,
+ <0x0 0x0ac10000 0x0 0x8000>,
+ <0x0 0x0ac6f000 0x0 0x8000>,
+ <0x0 0x0ac42000 0x0 0x4600>,
+ <0x0 0x01fc0000 0x0 0x40000>,
+ <0x0 0x0ac48000 0x0 0x1000>,
+ <0x0 0x0ac40000 0x0 0x1000>,
+ <0x0 0x0ac87000 0x0 0xa000>,
+ <0x0 0x0ac52000 0x0 0x4000>,
+ <0x0 0x0ac4e000 0x0 0x4000>,
+ <0x0 0x0ac6b000 0x0 0xa00>;
+ reg-names = "csid0",
+ "csid1",
+ "csid2",
+ "csid_lite",
+ "csiphy0",
+ "csiphy1",
+ "csiphy2",
+ "csiphy3",
+ "vfe0",
+ "vfe1",
+ "vfe2",
+ "vfe_lite",
+ "a5_csr",
+ "a5_qgic",
+ "a5_sierra",
+ "bps",
+ "camnoc",
+ "core_top_csr_tcsr",
+ "cpas_cdm",
+ "cpas_top",
+ "ipe",
+ "jpeg_dma",
+ "jpeg_enc",
+ "lrme";
+
+ clocks = <&gcc GCC_CAMERA_AXI_CLK>,
+ <&camcc CAMCC_SOC_AHB_CLK>,
+ <&camcc CAMCC_CAMNOC_AXI_CLK>,
+ <&camcc CAMCC_CORE_AHB_CLK>,
+ <&camcc CAMCC_CPAS_AHB_CLK>,
+ <&camcc CAMCC_CSIPHY0_CLK>,
+ <&camcc CAMCC_CSI0PHYTIMER_CLK>,
+ <&camcc CAMCC_CSIPHY1_CLK>,
+ <&camcc CAMCC_CSI1PHYTIMER_CLK>,
+ <&camcc CAMCC_CSIPHY2_CLK>,
+ <&camcc CAMCC_CSI2PHYTIMER_CLK>,
+ <&camcc CAMCC_CSIPHY3_CLK>,
+ <&camcc CAMCC_CSI3PHYTIMER_CLK>,
+ <&camcc CAMCC_IFE_0_AXI_CLK>,
+ <&camcc CAMCC_IFE_0_CLK>,
+ <&camcc CAMCC_IFE_0_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_0_CSID_CLK>,
+ <&camcc CAMCC_IFE_1_AXI_CLK>,
+ <&camcc CAMCC_IFE_1_CLK>,
+ <&camcc CAMCC_IFE_1_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_1_CSID_CLK>,
+ <&camcc CAMCC_IFE_2_AXI_CLK>,
+ <&camcc CAMCC_IFE_2_CLK>,
+ <&camcc CAMCC_IFE_2_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_2_CSID_CLK>,
+ <&camcc CAMCC_IFE_LITE_CLK>,
+ <&camcc CAMCC_IFE_LITE_CPHY_RX_CLK>,
+ <&camcc CAMCC_IFE_LITE_CSID_CLK>,
+ <&camcc CAMCC_BPS_CLK>,
+ <&camcc CAMCC_BPS_AHB_CLK>,
+ <&camcc CAMCC_BPS_AREG_CLK>,
+ <&camcc CAMCC_BPS_AXI_CLK>,
+ <&camcc CAMCC_ICP_CLK>,
+ <&camcc CAMCC_IPE_0_CLK>,
+ <&camcc CAMCC_IPE_0_AHB_CLK>,
+ <&camcc CAMCC_IPE_0_AREG_CLK>,
+ <&camcc CAMCC_IPE_0_AXI_CLK>,
+ <&camcc CAMCC_JPEG_CLK>,
+ <&camcc CAMCC_LRME_CLK>;
+ clock-names = "cam_axi",
+ "soc_ahb",
+ "camnoc_axi",
+ "core_ahb",
+ "cpas_ahb",
+ "csiphy0",
+ "csiphy0_timer",
+ "csiphy1",
+ "csiphy1_timer",
+ "csiphy2",
+ "csiphy2_timer",
+ "csiphy3",
+ "csiphy3_timer",
+ "vfe0_axi",
+ "vfe0",
+ "vfe0_cphy_rx",
+ "vfe0_csid",
+ "vfe1_axi",
+ "vfe1",
+ "vfe1_cphy_rx",
+ "vfe1_csid",
+ "vfe2_axi",
+ "vfe2",
+ "vfe2_cphy_rx",
+ "vfe2_csid",
+ "vfe_lite",
+ "vfe_lite_cphy_rx",
+ "vfe_lite_csid",
+ "bps",
+ "bps_ahb",
+ "bps_areg",
+ "bps_axi",
+ "icp",
+ "ipe0",
+ "ipe0_ahb",
+ "ipe0_areg",
+ "ipe0_axi",
+ "jpeg",
+ "lrme";
+
+ interrupts = <GIC_SPI 464 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 466 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 717 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 473 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 477 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 478 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 479 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 461 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 465 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 467 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 718 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 472 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 463 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 459 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 469 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 475 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 474 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 476 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "csid0",
+ "csid1",
+ "csid2",
+ "csid_lite",
+ "csiphy0",
+ "csiphy1",
+ "csiphy2",
+ "csiphy3",
+ "vfe0",
+ "vfe1",
+ "vfe2",
+ "vfe_lite",
+ "a5",
+ "cpas",
+ "cpas_cdm",
+ "jpeg_dma",
+ "jpeg_enc",
+ "lrme";
+
+ interconnects = <&gem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ACTIVE_ONLY
+ &config_noc SLAVE_CAMERA_CFG QCOM_ICC_TAG_ACTIVE_ONLY>,
+ <&mmss_noc MASTER_CAMNOC_HF QCOM_ICC_TAG_ALWAYS
+ &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
+ <&mmss_noc MASTER_CAMNOC_SF QCOM_ICC_TAG_ALWAYS
+ &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>,
+ <&mmss_noc MASTER_CAMNOC_ICP QCOM_ICC_TAG_ALWAYS
+ &clk_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "ahb",
+ "hf_mnoc",
+ "sf_mnoc",
+ "sf_icp_mnoc";
+
+ iommus = <&apps_smmu 0x820 0xc0>,
+ <&apps_smmu 0x840 0x0>,
+ <&apps_smmu 0x860 0xc0>,
+ <&apps_smmu 0x880 0x0>,
+ <&apps_smmu 0xc40 0x20>,
+ <&apps_smmu 0xc60 0x20>,
+ <&apps_smmu 0xc80 0x0>,
+ <&apps_smmu 0xca2 0x0>,
+ <&apps_smmu 0xcc0 0x20>,
+ <&apps_smmu 0xce0 0x20>,
+ <&apps_smmu 0xd00 0x20>,
+ <&apps_smmu 0xd20 0x20>,
+ <&apps_smmu 0xd40 0x20>,
+ <&apps_smmu 0xd60 0x20>;
+
+ power-domains = <&camcc IFE_0_GDSC>,
+ <&camcc IFE_1_GDSC>,
+ <&camcc IFE_2_GDSC>,
+ <&camcc TITAN_TOP_GDSC>,
+ <&camcc BPS_GDSC>,
+ <&camcc IPE_0_GDSC>;
+ power-domain-names = "ife0",
+ "ife1",
+ "ife2",
+ "top",
+ "bps",
+ "ipe";
+
+ vdd-csiphy0-0p9-supply = <&vreg_l18a>;
+ vdd-csiphy0-1p25-supply = <&vreg_l22a>;
+ vdd-csiphy1-0p9-supply = <&vreg_l18a>;
+ vdd-csiphy1-1p25-supply = <&vreg_l22a>;
+ vdd-csiphy2-0p9-supply = <&vreg_l18a>;
+ vdd-csiphy2-1p25-supply = <&vreg_l22a>;
+ vdd-csiphy3-0p9-supply = <&vreg_l18a>;
+ vdd-csiphy3-1p25-supply = <&vreg_l22a>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ csiphy0_ep: endpoint {
+ data-lanes = <0 1 2 3>;
+ bus-type = <MEDIA_BUS_TYPE_CSI2_DPHY>;
+ remote-endpoint = <&sensor_ep>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml
index da54493220c9..aca748e42aca 100644
--- a/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sm8250-venus.yaml
@@ -10,19 +10,25 @@ maintainers:
- Stanimir Varbanov <stanimir.varbanov@linaro.org>
description: |
- The Venus IP is a video encode and decode accelerator present
- on Qualcomm platforms
+ The Iris v2.xx IP is a video encode and decode accelerator present on
+ Qualcomm platforms
allOf:
- $ref: qcom,venus-common.yaml#
properties:
compatible:
- const: qcom,sm8250-venus
+ oneOf:
+ - const: qcom,sm8250-venus
+ - items:
+ - enum:
+ - qcom,sc8280xp-iris
+ - qcom,sm8350-iris
+ - const: qcom,sm8250-venus
power-domains:
minItems: 2
- maxItems: 3
+ maxItems: 4
power-domain-names:
minItems: 2
@@ -30,6 +36,7 @@ properties:
- const: venus
- const: vcodec0
- const: mx
+ - const: mmcx
clocks:
maxItems: 3
@@ -114,8 +121,12 @@ examples:
interrupts = <GIC_SPI 174 IRQ_TYPE_LEVEL_HIGH>;
power-domains = <&videocc MVS0C_GDSC>,
<&videocc MVS0_GDSC>,
- <&rpmhpd RPMHPD_MX>;
- power-domain-names = "venus", "vcodec0", "mx";
+ <&rpmhpd RPMHPD_MX>,
+ <&rpmhpd RPMHPD_MMCX>;
+ power-domain-names = "venus",
+ "vcodec0",
+ "mx",
+ "mmcx";
clocks = <&gcc GCC_VIDEO_AXI0_CLK>,
<&videocc VIDEO_CC_MVS0C_CLK>,
diff --git a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml
index 9c4b760508b5..0400ca1bff05 100644
--- a/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,sm8550-iris.yaml
@@ -26,6 +26,7 @@ properties:
- qcom,qcs8300-iris
- qcom,sm8550-iris
- qcom,sm8650-iris
+ - qcom,x1p42100-iris
reg:
maxItems: 1
@@ -41,13 +42,16 @@ properties:
- const: mmcx
clocks:
- maxItems: 3
+ minItems: 3
+ maxItems: 4
clock-names:
+ minItems: 3
items:
- const: iface
- const: core
- const: vcodec0_core
+ - const: vcodec0_bse
firmware-name:
maxItems: 1
@@ -115,6 +119,23 @@ allOf:
maxItems: 1
reset-names:
maxItems: 1
+ - if:
+ properties:
+ compatible:
+ enum:
+ - qcom,x1p42100-iris
+ then:
+ properties:
+ clocks:
+ minItems: 4
+ clock-names:
+ minItems: 4
+ else:
+ properties:
+ clocks:
+ maxItems: 3
+ clock-names:
+ maxItems: 3
unevaluatedProperties: false
diff --git a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml
index 3153d91f9d18..59a3fde846d2 100644
--- a/Documentation/devicetree/bindings/media/qcom,venus-common.yaml
+++ b/Documentation/devicetree/bindings/media/qcom,venus-common.yaml
@@ -47,21 +47,6 @@ properties:
minItems: 1
maxItems: 4
- video-firmware:
- type: object
- additionalProperties: false
-
- description: |
- Firmware subnode is needed when the platform does not
- have TrustZone.
-
- properties:
- iommus:
- maxItems: 1
-
- required:
- - iommus
-
required:
- reg
- clocks
diff --git a/Documentation/devicetree/bindings/media/renesas,fcp.yaml b/Documentation/devicetree/bindings/media/renesas,fcp.yaml
index b5eff6fec8a9..5e11ae0ee456 100644
--- a/Documentation/devicetree/bindings/media/renesas,fcp.yaml
+++ b/Documentation/devicetree/bindings/media/renesas,fcp.yaml
@@ -30,6 +30,8 @@ properties:
- renesas,r9a07g043u-fcpvd # RZ/G2UL
- renesas,r9a07g044-fcpvd # RZ/G2{L,LC}
- renesas,r9a07g054-fcpvd # RZ/V2L
+ - renesas,r9a08g046-fcpvd # RZ/G3L
+ - renesas,r9a09g047-fcpvd # RZ/G3E
- renesas,r9a09g056-fcpvd # RZ/V2N
- renesas,r9a09g057-fcpvd # RZ/V2H(P)
- const: renesas,fcpv # Generic FCP for VSP fallback
@@ -77,6 +79,8 @@ allOf:
- renesas,r9a07g043u-fcpvd
- renesas,r9a07g044-fcpvd
- renesas,r9a07g054-fcpvd
+ - renesas,r9a08g046-fcpvd
+ - renesas,r9a09g047-fcpvd
- renesas,r9a09g056-fcpvd
- renesas,r9a09g057-fcpvd
then:
diff --git a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml
index 07a97dd87a5b..803358780f01 100644
--- a/Documentation/devicetree/bindings/media/renesas,vsp1.yaml
+++ b/Documentation/devicetree/bindings/media/renesas,vsp1.yaml
@@ -25,6 +25,8 @@ properties:
- enum:
- renesas,r9a07g043u-vsp2 # RZ/G2UL
- renesas,r9a07g054-vsp2 # RZ/V2L
+ - renesas,r9a08g046-vsp2 # RZ/G3L
+ - renesas,r9a09g047-vsp2 # RZ/G3E
- renesas,r9a09g056-vsp2 # RZ/V2N
- renesas,r9a09g057-vsp2 # RZ/V2H(P)
- const: renesas,r9a07g044-vsp2 # RZ/G2L fallback
diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
index 4ac4a3b6f406..8bfad0fca3b7 100644
--- a/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
@@ -16,9 +16,15 @@ description:
properties:
compatible:
- enum:
- - fsl,imx93-mipi-csi2
- - rockchip,rk3568-mipi-csi2
+ oneOf:
+ - enum:
+ - fsl,imx93-mipi-csi2
+ - fsl,imx95-mipi-csi2
+ - rockchip,rk3568-mipi-csi2
+ - items:
+ - enum:
+ - rockchip,rk3588-mipi-csi2
+ - const: rockchip,rk3568-mipi-csi2
reg:
maxItems: 1
@@ -135,6 +141,21 @@ allOf:
clock-names:
minItems: 2
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: fsl,imx95-mipi-csi2
+ then:
+ properties:
+ interrupts:
+ maxItems: 1
+ interrupt-names: false
+ clocks:
+ maxItems: 1
+ clock-names:
+ maxItems: 1
+
examples:
- |
#include <dt-bindings/clock/rk3568-cru.h>
diff --git a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
index 18cd0a5a5318..080b64503b1b 100644
--- a/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip,rk3568-vicap.yaml
@@ -15,9 +15,15 @@ description:
the data from camera sensors, video decoders, or other companion ICs and
transfers it into system main memory by AXI bus.
+ The Rockchip RK3588 Video Capture (VICAP) is similar to its RK3568
+ counterpart, but features six MIPI CSI-2 ports and additional connections
+ to the image signal processor (ISP) blocks.
+
properties:
compatible:
- const: rockchip,rk3568-vicap
+ enum:
+ - rockchip,rk3568-vicap
+ - rockchip,rk3588-vicap
reg:
maxItems: 1
@@ -26,11 +32,8 @@ properties:
maxItems: 1
clocks:
- items:
- - description: ACLK
- - description: HCLK
- - description: DCLK
- - description: ICLK
+ minItems: 4
+ maxItems: 5
clock-names:
items:
@@ -38,25 +41,19 @@ properties:
- const: hclk
- const: dclk
- const: iclk
+ - const: iclk1
+ minItems: 4
iommus:
maxItems: 1
resets:
- items:
- - description: ARST
- - description: HRST
- - description: DRST
- - description: PRST
- - description: IRST
+ minItems: 5
+ maxItems: 9
reset-names:
- items:
- - const: arst
- - const: hrst
- - const: drst
- - const: prst
- - const: irst
+ minItems: 5
+ maxItems: 9
rockchip,grf:
$ref: /schemas/types.yaml#/definitions/phandle
@@ -67,8 +64,15 @@ properties:
ports:
$ref: /schemas/graph.yaml#/properties/ports
+ additionalProperties: false
properties:
+ "#address-cells":
+ const: 1
+
+ "#size-cells":
+ const: 0
+
port@0:
$ref: /schemas/graph.yaml#/$defs/port-base
unevaluatedProperties: false
@@ -100,13 +104,75 @@ properties:
port@1:
$ref: /schemas/graph.yaml#/properties/port
- description: Port connected to the MIPI CSI-2 receiver output.
+ description: Port connected to the MIPI CSI-2 receiver 0 output.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@2:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the MIPI CSI-2 receiver 1 output.
properties:
endpoint:
$ref: video-interfaces.yaml#
unevaluatedProperties: false
+ port@3:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the MIPI CSI-2 receiver 2 output.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@4:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the MIPI CSI-2 receiver 3 output.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@5:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the MIPI CSI-2 receiver 4 output.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@6:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the MIPI CSI-2 receiver 5 output.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@10:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the ISP0 input.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ port@11:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Port connected to the ISP1 input.
+
+ properties:
+ endpoint:
+ $ref: video-interfaces.yaml#
+ unevaluatedProperties: false
required:
- compatible
- reg
@@ -114,6 +180,75 @@ required:
- clocks
- ports
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3568-vicap
+ then:
+ properties:
+ clocks:
+ maxItems: 4
+
+ clock-names:
+ maxItems: 4
+
+ resets:
+ maxItems: 5
+
+ reset-names:
+ items:
+ - const: arst
+ - const: hrst
+ - const: drst
+ - const: prst
+ - const: irst
+
+ ports:
+ properties:
+ port@2: false
+
+ port@3: false
+
+ port@4: false
+
+ port@5: false
+
+ port@6: false
+
+ port@10: false
+
+ port@11: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: rockchip,rk3588-vicap
+ then:
+ properties:
+ clocks:
+ minItems: 5
+
+ clock-names:
+ minItems: 5
+
+ resets:
+ minItems: 9
+
+ reset-names:
+ items:
+ - const: arst
+ - const: hrst
+ - const: drst
+ - const: irst0
+ - const: irst1
+ - const: irst2
+ - const: irst3
+ - const: irst4
+ - const: irst5
+
additionalProperties: false
examples:
diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.yaml b/Documentation/devicetree/bindings/media/rockchip-rga.yaml
index ac17cda65191..7bd92f733666 100644
--- a/Documentation/devicetree/bindings/media/rockchip-rga.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip-rga.yaml
@@ -9,7 +9,11 @@ title: Rockchip 2D raster graphic acceleration controller (RGA)
description:
RGA is a standalone 2D raster graphic acceleration unit. It accelerates 2D
graphics operations, such as point/line drawing, image scaling, rotation,
- BitBLT, alpha blending and image blur/sharpness.
+ BitBLT, alpha blending and image blur/sharpness. There exist many versions
+ of this unit that differ in the supported inputs/output formats,
+ the attached IOMMU and the supported operations on the input. As some SoCs
+ include multiple RGA units with different versions, a more specific
+ compatible name to differentiate the concrete unit is used for them.
maintainers:
- Jacob Chen <jacob-chen@iotwrt.com>
@@ -20,6 +24,7 @@ properties:
oneOf:
- const: rockchip,rk3288-rga
- const: rockchip,rk3399-rga
+ - const: rockchip,rk3588-rga3
- items:
- enum:
- rockchip,rk3228-rga
@@ -45,6 +50,9 @@ properties:
power-domains:
maxItems: 1
+ iommus:
+ maxItems: 1
+
resets:
maxItems: 3
diff --git a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml
index b9f033f2f3ce..bf62998b0445 100644
--- a/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml
+++ b/Documentation/devicetree/bindings/media/ti,j721e-csi2rx-shim.yaml
@@ -20,11 +20,44 @@ properties:
const: ti,j721e-csi2rx-shim
dmas:
- maxItems: 1
+ minItems: 1
+ maxItems: 32
dma-names:
+ minItems: 1
items:
- const: rx0
+ - const: rx1
+ - const: rx2
+ - const: rx3
+ - const: rx4
+ - const: rx5
+ - const: rx6
+ - const: rx7
+ - const: rx8
+ - const: rx9
+ - const: rx10
+ - const: rx11
+ - const: rx12
+ - const: rx13
+ - const: rx14
+ - const: rx15
+ - const: rx16
+ - const: rx17
+ - const: rx18
+ - const: rx19
+ - const: rx20
+ - const: rx21
+ - const: rx22
+ - const: rx23
+ - const: rx24
+ - const: rx25
+ - const: rx26
+ - const: rx27
+ - const: rx28
+ - const: rx29
+ - const: rx30
+ - const: rx31
reg:
maxItems: 1
@@ -62,8 +95,8 @@ examples:
ti_csi2rx0: ticsi2rx@4500000 {
compatible = "ti,j721e-csi2rx-shim";
- dmas = <&main_udmap 0x4940>;
- dma-names = "rx0";
+ dmas = <&main_udmap 0x4940>, <&main_udmap 0x4941>;
+ dma-names = "rx0", "rx1";
reg = <0x4500000 0x1000>;
power-domains = <&k3_pds 26 TI_SCI_PD_EXCLUSIVE>;
#address-cells = <1>;
diff --git a/Documentation/driver-api/media/camera-sensor.rst b/Documentation/driver-api/media/camera-sensor.rst
index 94bd1dae82d5..c8552f70f496 100644
--- a/Documentation/driver-api/media/camera-sensor.rst
+++ b/Documentation/driver-api/media/camera-sensor.rst
@@ -114,7 +114,7 @@ of the device. This is because the power state of the device is only changed
after the power state transition has taken place. The ``s_ctrl`` callback can be
used to obtain device's power state after the power state transition:
-.. c:function:: int pm_runtime_get_if_in_use(struct device *dev);
+.. c:function:: int pm_runtime_get_if_active(struct device *dev);
The function returns a non-zero value if it succeeded getting the power count or
runtime PM was disabled, in either of which cases the driver may proceed to
diff --git a/Documentation/driver-api/media/tx-rx.rst b/Documentation/driver-api/media/tx-rx.rst
index 22e1b13ecde9..7df2407817b3 100644
--- a/Documentation/driver-api/media/tx-rx.rst
+++ b/Documentation/driver-api/media/tx-rx.rst
@@ -93,7 +93,8 @@ where
* - variable or constant
- description
* - link_freq
- - The value of the ``V4L2_CID_LINK_FREQ`` integer64 menu item.
+ - The value of the :ref:`V4L2_CID_LINK_FREQ <v4l2-cid-link-freq>` integer64
+ menu item.
* - nr_of_lanes
- Number of data lanes used on the CSI-2 link.
* - 2
diff --git a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions
index 65e8be062bdb..5d6f7747c023 100644
--- a/Documentation/userspace-api/media/cec/cec.h.rst.exceptions
+++ b/Documentation/userspace-api/media/cec/cec.h.rst.exceptions
@@ -524,6 +524,29 @@ ignore define CEC_OP_AUD_OUT_COMPENSATED_DELAY
ignore define CEC_OP_AUD_OUT_COMPENSATED_NO_DELAY
ignore define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY
+ignore define CEC_MSG_REQUEST_LIP_SUPPORT
+ignore define CEC_MSG_REPORT_LIP_SUPPORT
+ignore define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY
+
+ignore define CEC_OP_HDR_FORMAT_GAMMA_SDR
+ignore define CEC_OP_HDR_FORMAT_GAMMA_HDR
+ignore define CEC_OP_HDR_FORMAT_PQ
+ignore define CEC_OP_HDR_FORMAT_HLG
+ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1
+ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2
+ignore define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4
+ignore define CEC_OP_HDR_FORMAT_DV_SINK_LED
+ignore define CEC_OP_HDR_FORMAT_DV_SOURCE_LED
+ignore define CEC_OP_HDR_FORMAT_HDR10PLUS
+ignore define CEC_OP_HDR_FORMAT_ETSI_TS_103_433
+
+ignore define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY
+ignore define CEC_MSG_REQUEST_AUDIO_LATENCY
+ignore define CEC_MSG_REPORT_AUDIO_LATENCY
+ignore define CEC_MSG_REQUEST_VIDEO_LATENCY
+ignore define CEC_MSG_REPORT_VIDEO_LATENCY
+ignore define CEC_MSG_UPDATE_SQID
+
ignore define CEC_MSG_CDC_MESSAGE
ignore define CEC_MSG_CDC_HEC_INQUIRE_STATE
diff --git a/Documentation/userspace-api/media/drivers/uvcvideo.rst b/Documentation/userspace-api/media/drivers/uvcvideo.rst
index dbb30ad389ae..b09d2f8ba66e 100644
--- a/Documentation/userspace-api/media/drivers/uvcvideo.rst
+++ b/Documentation/userspace-api/media/drivers/uvcvideo.rst
@@ -109,6 +109,8 @@ IOCTL reference
UVCIOC_CTRL_MAP - Map a UVC control to a V4L2 control
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+**This IOCTL is deprecated and will be eventually removed**
+
Argument: struct uvc_xu_control_mapping
**Description**:
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst
index 3b1e05c6eb13..d7a3b8ef03a7 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec-stateless.rst
@@ -1877,7 +1877,7 @@ params syntax' of the :ref:`vp9` specification for more details.
:stub-columns: 0
:widths: 1 1 2
- * - __u8
+ * - __s16
- ``feature_data[8][4]``
- Data attached to each feature. Data entry is only valid if the feature
is enabled. The array shall be indexed with segment number as the first dimension
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst
index c8890cb5e00a..ab865a1a6ba9 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-codec.rst
@@ -737,6 +737,12 @@ enum v4l2_mpeg_video_frame_skip_mode -
Enable writing sample aspect ratio in the Video Usability
Information. Applicable to the H264 encoder.
+``V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (boolean)``
+ If enabled, the encoder detect a background region in frame and
+ use low bits or skip mode to encode the background region.
+ If a lot of scenes are stationary or background, It may help to
+ reduce the video bitrate. Applicable to the encoder.
+
.. _v4l2-mpeg-video-h264-vui-sar-idc:
``V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC``
diff --git a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst
index 6d516f041ca2..57c2baf34acf 100644
--- a/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst
+++ b/Documentation/userspace-api/media/v4l/ext-ctrls-image-process.rst
@@ -24,7 +24,10 @@ Image Process Control IDs
.. _v4l2-cid-link-freq:
``V4L2_CID_LINK_FREQ (integer menu)``
- The frequency of the data bus (e.g. parallel or CSI-2).
+ The fundamental frequency of the operating symbol rate (serial interfaces
+ such as CSI-2) or the sampling rate (parallel interfaces such as DVP or
+ Bt.565) of the data interface. For CSI-2, the frequency is equal to
+ _1 / (2 * UI)_.
.. _v4l2-cid-pixel-rate:
diff --git a/MAINTAINERS b/MAINTAINERS
index 0ef0a11a5f25..16ce0c0537e8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1167,6 +1167,31 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
F: drivers/iommu/amd/
F: include/linux/amd-iommu.h
+AMD ISP4 DRIVER
+M: Bin Du <bin.du@amd.com>
+M: Nirujogi Pratap <pratap.nirujogi@amd.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+T: git git://linuxtv.org/media.git
+F: Documentation/admin-guide/media/amdisp4-1.rst
+F: Documentation/admin-guide/media/amdisp4.dot
+F: drivers/media/platform/amd/Kconfig
+F: drivers/media/platform/amd/Makefile
+F: drivers/media/platform/amd/isp4/Kconfig
+F: drivers/media/platform/amd/isp4/Makefile
+F: drivers/media/platform/amd/isp4/isp4.c
+F: drivers/media/platform/amd/isp4/isp4.h
+F: drivers/media/platform/amd/isp4/isp4_debug.c
+F: drivers/media/platform/amd/isp4/isp4_debug.h
+F: drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
+F: drivers/media/platform/amd/isp4/isp4_hw_reg.h
+F: drivers/media/platform/amd/isp4/isp4_interface.c
+F: drivers/media/platform/amd/isp4/isp4_interface.h
+F: drivers/media/platform/amd/isp4/isp4_subdev.c
+F: drivers/media/platform/amd/isp4/isp4_subdev.h
+F: drivers/media/platform/amd/isp4/isp4_video.c
+F: drivers/media/platform/amd/isp4/isp4_video.h
+
AMD KFD
M: Felix Kuehling <Felix.Kuehling@amd.com>
L: amd-gfx@lists.freedesktop.org
@@ -4339,6 +4364,12 @@ S: Maintained
F: Documentation/devicetree/bindings/iio/adc/avia-hx711.yaml
F: drivers/iio/adc/hx711.c
+AVMATRIX HWS CAPTURE DRIVER
+M: Ben Hoff <hoff.benjamin.k@gmail.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/pci/hws/
+
AWINIC AW99706 WLED BACKLIGHT DRIVER
M: Junjie Cao <caojunjie650@gmail.com>
S: Maintained
@@ -12876,6 +12907,12 @@ S: Orphan
T: git git://git.code.sf.net/p/intel-sas/isci
F: drivers/scsi/isci/
+INTEL COMPUTER VISION SENSING (CVS) DRIVER
+M: Miguel Vadillo <miguel.vadillo@intel.com>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/i2c/cvs/
+
INTEL CPU family model numbers
M: Tony Luck <tony.luck@intel.com>
M: x86@kernel.org
@@ -17487,8 +17524,6 @@ F: Documentation/devicetree/bindings/media/atmel,isc.yaml
F: Documentation/devicetree/bindings/media/microchip,xisc.yaml
F: drivers/media/platform/microchip/microchip-isc*
F: drivers/media/platform/microchip/microchip-sama*-isc*
-F: drivers/staging/media/deprecated/atmel/atmel-isc*
-F: drivers/staging/media/deprecated/atmel/atmel-sama*-isc*
F: include/linux/atmel-isc-media.h
MICROCHIP ISI DRIVER
@@ -22001,10 +22036,9 @@ F: drivers/bluetooth/btqcomsmd.c
F: drivers/bluetooth/hci_qca.c
QUALCOMM CAMERA SUBSYSTEM DRIVER
-M: Robert Foss <rfoss@kernel.org>
-M: Todor Tomov <todor.too@gmail.com>
M: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
R: Vladimir Zapolskiy <vladimir.zapolskiy@linaro.org>
+R: Loic Poulain <loic.poulain@oss.qualcomm.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/media/qcom_camss.rst
@@ -22850,6 +22884,15 @@ S: Supported
F: Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml
F: drivers/counter/rz-mtu3-cnt.c
+RENESAS RZ/G2L / RZ/V2H(P) CRU
+M: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
+M: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+L: linux-renesas-soc@vger.kernel.org
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/renesas,rzg2l-cru.yaml
+F: drivers/media/platform/renesas/rzg2l-cru/
+
RENESAS RZ/T2H / RZ/N2H A/D DRIVER
M: Cosmin Tanislav <cosmin-gabriel.tanislav.xa@renesas.com>
L: linux-iio@vger.kernel.org
@@ -22918,6 +22961,7 @@ F: drivers/net/ethernet/stmicro/stmmac/dwmac-renesas-gbeth.c
RENESAS RZ/V2H(P) INPUT VIDEO CONTROL BLOCK DRIVER
M: Daniel Scally <dan.scally@ideasonboard.com>
+M: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/renesas,r9a09g057-ivc.yaml
diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c
index 6b413b379a86..ae9f381b03c8 100644
--- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c
+++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_vsp.c
@@ -87,12 +87,12 @@ void rcar_du_vsp_enable(struct rcar_du_crtc *crtc)
__rcar_du_plane_setup(crtc->group, &state);
- vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
+ vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
}
void rcar_du_vsp_disable(struct rcar_du_crtc *crtc)
{
- vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
+ vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe);
}
void rcar_du_vsp_atomic_begin(struct rcar_du_crtc *crtc)
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
index bc205c25cd21..1efa0f0451fe 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_vsp.c
@@ -56,12 +56,12 @@ void rzg2l_du_vsp_enable(struct rzg2l_du_crtc *crtc)
.callback_data = crtc,
};
- vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
+ vsp1_du_enable(crtc->vsp->vsp, crtc->vsp_pipe, &cfg);
}
void rzg2l_du_vsp_disable(struct rzg2l_du_crtc *crtc)
{
- vsp1_du_setup_lif(crtc->vsp->vsp, crtc->vsp_pipe, NULL);
+ vsp1_du_disable(crtc->vsp->vsp, crtc->vsp_pipe);
}
void rzg2l_du_vsp_atomic_flush(struct rzg2l_du_crtc *crtc)
diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c
index 8f7244ac1d43..a90cb84a4b4d 100644
--- a/drivers/media/cec/core/cec-adap.c
+++ b/drivers/media/cec/core/cec-adap.c
@@ -1098,6 +1098,15 @@ static const u8 cec_msg_size[256] = {
[CEC_MSG_REQUEST_CURRENT_LATENCY] = 4 | BCAST,
[CEC_MSG_REPORT_CURRENT_LATENCY] = 6 | BCAST,
[CEC_MSG_CDC_MESSAGE] = 2 | BCAST,
+ [CEC_MSG_REQUEST_LIP_SUPPORT] = 4 | DIRECTED,
+ [CEC_MSG_REPORT_LIP_SUPPORT] = 6 | DIRECTED,
+ [CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED,
+ [CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY] = 6 | DIRECTED,
+ [CEC_MSG_REQUEST_AUDIO_LATENCY] = 3 | DIRECTED,
+ [CEC_MSG_REPORT_AUDIO_LATENCY] = 4 | DIRECTED,
+ [CEC_MSG_REQUEST_VIDEO_LATENCY] = 5 | DIRECTED,
+ [CEC_MSG_REPORT_VIDEO_LATENCY] = 4 | DIRECTED,
+ [CEC_MSG_UPDATE_SQID] = 6 | DIRECTED,
};
/* Called by the CEC adapter if a message is received */
diff --git a/drivers/media/cec/i2c/tda9950.c b/drivers/media/cec/i2c/tda9950.c
index cbff851e0c85..2bece4e63687 100644
--- a/drivers/media/cec/i2c/tda9950.c
+++ b/drivers/media/cec/i2c/tda9950.c
@@ -486,7 +486,7 @@ static void tda9950_remove(struct i2c_client *client)
}
static struct i2c_device_id tda9950_ids[] = {
- { "tda9950" },
+ { .name = "tda9950" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tda9950_ids);
diff --git a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
index 419b9a7abcce..ec1da9cd3674 100644
--- a/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
+++ b/drivers/media/cec/platform/cros-ec/cros-ec-cec.c
@@ -334,6 +334,12 @@ static const struct cec_dmi_match cec_dmi_match_table[] = {
{ "Google", "Dirks", "0000:00:02.0", port_ab_conns },
/* Google Moxie */
{ "Google", "Moxie", "0000:00:02.0", port_b_conns },
+ /* Google Kulnex */
+ { "Google", "Kulnex", "0000:00:02.0", port_b_conns },
+ /* Google Moxoe */
+ { "Google", "Moxoe", "0000:00:02.0", port_b_conns },
+ /* Google Dirkson */
+ { "Google", "Dirkson", "0000:00:02.0", port_ab_conns },
};
static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
diff --git a/drivers/media/cec/platform/seco/seco-cec.c b/drivers/media/cec/platform/seco/seco-cec.c
index b7bb49f02395..97ed9654c78a 100644
--- a/drivers/media/cec/platform/seco/seco-cec.c
+++ b/drivers/media/cec/platform/seco/seco-cec.c
@@ -649,7 +649,7 @@ static int secocec_probe(struct platform_device *pdev)
ret = secocec_ir_probe(secocec);
if (ret)
- goto err_notifier;
+ goto err_unregister_adapter;
platform_set_drvdata(pdev, secocec);
@@ -657,6 +657,10 @@ static int secocec_probe(struct platform_device *pdev)
return ret;
+err_unregister_adapter:
+ cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
+ cec_unregister_adapter(secocec->cec_adap);
+ goto err;
err_notifier:
cec_notifier_cec_adap_unregister(secocec->notifier, secocec->cec_adap);
err_delete_adapter:
diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c
index adf668b213c2..b0a6084f1757 100644
--- a/drivers/media/common/videobuf2/videobuf2-core.c
+++ b/drivers/media/common/videobuf2/videobuf2-core.c
@@ -2990,8 +2990,8 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q)
* @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
* @read: access mode selector (1 means read, 0 means write)
*/
-static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblock, int read)
+static ssize_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock, int read)
{
struct vb2_fileio_data *fileio;
struct vb2_fileio_buf *buf;
@@ -3154,15 +3154,15 @@ static size_t __vb2_perform_fileio(struct vb2_queue *q, char __user *data, size_
return ret;
}
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblocking)
+ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
{
return __vb2_perform_fileio(q, data, count, ppos, nonblocking, 1);
}
EXPORT_SYMBOL_GPL(vb2_read);
-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
- loff_t *ppos, int nonblocking)
+ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+ loff_t *ppos, int nonblocking)
{
return __vb2_perform_fileio(q, (char __user *) data, count,
ppos, nonblocking, 0);
diff --git a/drivers/media/dvb-frontends/a8293.c b/drivers/media/dvb-frontends/a8293.c
index 7c0963054a8f..52e3dc928327 100644
--- a/drivers/media/dvb-frontends/a8293.c
+++ b/drivers/media/dvb-frontends/a8293.c
@@ -256,8 +256,8 @@ static void a8293_remove(struct i2c_client *client)
}
static const struct i2c_device_id a8293_id_table[] = {
- { "a8293" },
- {}
+ { .name = "a8293" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, a8293_id_table);
diff --git a/drivers/media/dvb-frontends/af9013.c b/drivers/media/dvb-frontends/af9013.c
index 75f3063c193e..d335bfd18e44 100644
--- a/drivers/media/dvb-frontends/af9013.c
+++ b/drivers/media/dvb-frontends/af9013.c
@@ -1553,8 +1553,8 @@ static void af9013_remove(struct i2c_client *client)
}
static const struct i2c_device_id af9013_id_table[] = {
- { "af9013" },
- {}
+ { .name = "af9013" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, af9013_id_table);
diff --git a/drivers/media/dvb-frontends/af9033.c b/drivers/media/dvb-frontends/af9033.c
index 9a3a4d6513dd..01761861b01f 100644
--- a/drivers/media/dvb-frontends/af9033.c
+++ b/drivers/media/dvb-frontends/af9033.c
@@ -1173,8 +1173,8 @@ static void af9033_remove(struct i2c_client *client)
}
static const struct i2c_device_id af9033_id_table[] = {
- { "af9033" },
- {}
+ { .name = "af9033" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, af9033_id_table);
diff --git a/drivers/media/dvb-frontends/au8522_decoder.c b/drivers/media/dvb-frontends/au8522_decoder.c
index 58b959b272c6..9ae47ea3976b 100644
--- a/drivers/media/dvb-frontends/au8522_decoder.c
+++ b/drivers/media/dvb-frontends/au8522_decoder.c
@@ -768,8 +768,8 @@ static void au8522_remove(struct i2c_client *client)
}
static const struct i2c_device_id au8522_id[] = {
- { "au8522" },
- {}
+ { .name = "au8522" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, au8522_id);
diff --git a/drivers/media/dvb-frontends/cxd2099.c b/drivers/media/dvb-frontends/cxd2099.c
index f95950a61307..e6110af1edcd 100644
--- a/drivers/media/dvb-frontends/cxd2099.c
+++ b/drivers/media/dvb-frontends/cxd2099.c
@@ -672,8 +672,8 @@ static void cxd2099_remove(struct i2c_client *client)
}
static const struct i2c_device_id cxd2099_id[] = {
- { "cxd2099" },
- {}
+ { .name = "cxd2099" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cxd2099_id);
diff --git a/drivers/media/dvb-frontends/cxd2820r_core.c b/drivers/media/dvb-frontends/cxd2820r_core.c
index 3deefeff4bd1..fbbffe9e7251 100644
--- a/drivers/media/dvb-frontends/cxd2820r_core.c
+++ b/drivers/media/dvb-frontends/cxd2820r_core.c
@@ -723,8 +723,8 @@ static void cxd2820r_remove(struct i2c_client *client)
}
static const struct i2c_device_id cxd2820r_id_table[] = {
- { "cxd2820r" },
- {}
+ { .name = "cxd2820r" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cxd2820r_id_table);
diff --git a/drivers/media/dvb-frontends/dvb-pll.c b/drivers/media/dvb-frontends/dvb-pll.c
index f8fd23f60e3f..d36c163b772f 100644
--- a/drivers/media/dvb-frontends/dvb-pll.c
+++ b/drivers/media/dvb-frontends/dvb-pll.c
@@ -911,28 +911,28 @@ static void dvb_pll_remove(struct i2c_client *client)
static const struct i2c_device_id dvb_pll_id[] = {
- {"dtt7579", DVB_PLL_THOMSON_DTT7579},
- {"dtt759x", DVB_PLL_THOMSON_DTT759X},
- {"z201", DVB_PLL_LG_Z201},
- {"unknown_1", DVB_PLL_UNKNOWN_1},
- {"tua6010xs", DVB_PLL_TUA6010XS},
- {"env57h1xd5", DVB_PLL_ENV57H1XD5},
- {"tua6034", DVB_PLL_TUA6034},
- {"tda665x", DVB_PLL_TDA665X},
- {"tded4", DVB_PLL_TDED4},
- {"tdhu2", DVB_PLL_TDHU2},
- {"tbmv", DVB_PLL_SAMSUNG_TBMV},
- {"sd1878_tda8261", DVB_PLL_PHILIPS_SD1878_TDA8261},
- {"opera1", DVB_PLL_OPERA1},
- {"dtos403ih102a", DVB_PLL_SAMSUNG_DTOS403IH102A},
- {"tdtc9251dh0", DVB_PLL_SAMSUNG_TDTC9251DH0},
- {"tbdu18132", DVB_PLL_SAMSUNG_TBDU18132},
- {"tbmu24112", DVB_PLL_SAMSUNG_TBMU24112},
- {"tdee4", DVB_PLL_TDEE4},
- {"dtt7520x", DVB_PLL_THOMSON_DTT7520X},
- {"tua6034_friio", DVB_PLL_TUA6034_FRIIO},
- {"tda665x_earthpt1", DVB_PLL_TDA665X_EARTH_PT1},
- {}
+ { .name = "dtt7579", .driver_data = DVB_PLL_THOMSON_DTT7579 },
+ { .name = "dtt759x", .driver_data = DVB_PLL_THOMSON_DTT759X },
+ { .name = "z201", .driver_data = DVB_PLL_LG_Z201 },
+ { .name = "unknown_1", .driver_data = DVB_PLL_UNKNOWN_1 },
+ { .name = "tua6010xs", .driver_data = DVB_PLL_TUA6010XS },
+ { .name = "env57h1xd5", .driver_data = DVB_PLL_ENV57H1XD5 },
+ { .name = "tua6034", .driver_data = DVB_PLL_TUA6034 },
+ { .name = "tda665x", .driver_data = DVB_PLL_TDA665X },
+ { .name = "tded4", .driver_data = DVB_PLL_TDED4 },
+ { .name = "tdhu2", .driver_data = DVB_PLL_TDHU2 },
+ { .name = "tbmv", .driver_data = DVB_PLL_SAMSUNG_TBMV },
+ { .name = "sd1878_tda8261", .driver_data = DVB_PLL_PHILIPS_SD1878_TDA8261 },
+ { .name = "opera1", .driver_data = DVB_PLL_OPERA1 },
+ { .name = "dtos403ih102a", .driver_data = DVB_PLL_SAMSUNG_DTOS403IH102A },
+ { .name = "tdtc9251dh0", .driver_data = DVB_PLL_SAMSUNG_TDTC9251DH0 },
+ { .name = "tbdu18132", .driver_data = DVB_PLL_SAMSUNG_TBDU18132 },
+ { .name = "tbmu24112", .driver_data = DVB_PLL_SAMSUNG_TBMU24112 },
+ { .name = "tdee4", .driver_data = DVB_PLL_TDEE4 },
+ { .name = "dtt7520x", .driver_data = DVB_PLL_THOMSON_DTT7520X },
+ { .name = "tua6034_friio", .driver_data = DVB_PLL_TUA6034_FRIIO },
+ { .name = "tda665x_earthpt1", .driver_data = DVB_PLL_TDA665X_EARTH_PT1 },
+ { }
};
diff --git a/drivers/media/dvb-frontends/helene.c b/drivers/media/dvb-frontends/helene.c
index 1402d124544e..993280fefc2c 100644
--- a/drivers/media/dvb-frontends/helene.c
+++ b/drivers/media/dvb-frontends/helene.c
@@ -1101,8 +1101,8 @@ static int helene_probe(struct i2c_client *client)
}
static const struct i2c_device_id helene_id[] = {
- { "helene", },
- {}
+ { .name = "helene" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, helene_id);
diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c
index b6a66e122ed5..f4a3136baea3 100644
--- a/drivers/media/dvb-frontends/lgdt3306a.c
+++ b/drivers/media/dvb-frontends/lgdt3306a.c
@@ -2244,8 +2244,8 @@ static void lgdt3306a_remove(struct i2c_client *client)
}
static const struct i2c_device_id lgdt3306a_id_table[] = {
- { "lgdt3306a" },
- {}
+ { .name = "lgdt3306a" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table);
diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c
index 19a4a05b3499..d90f642e9237 100644
--- a/drivers/media/dvb-frontends/lgdt330x.c
+++ b/drivers/media/dvb-frontends/lgdt330x.c
@@ -983,8 +983,8 @@ static void lgdt330x_remove(struct i2c_client *client)
}
static const struct i2c_device_id lgdt330x_id_table[] = {
- { "lgdt330x" },
- {}
+ { .name = "lgdt330x" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lgdt330x_id_table);
diff --git a/drivers/media/dvb-frontends/m88ds3103.c b/drivers/media/dvb-frontends/m88ds3103.c
index 44bee1f3c5e9..d79b88848a8c 100644
--- a/drivers/media/dvb-frontends/m88ds3103.c
+++ b/drivers/media/dvb-frontends/m88ds3103.c
@@ -2221,11 +2221,11 @@ static void m88ds3103_remove(struct i2c_client *client)
}
static const struct i2c_device_id m88ds3103_id_table[] = {
- {"m88ds3103", M88DS3103_CHIPTYPE_3103},
- {"m88rs6000", M88DS3103_CHIPTYPE_RS6000},
- {"m88ds3103b", M88DS3103_CHIPTYPE_3103B},
- {"m88ds3103c", M88DS3103_CHIPTYPE_3103C},
- {}
+ { .name = "m88ds3103", .driver_data = M88DS3103_CHIPTYPE_3103 },
+ { .name = "m88rs6000", .driver_data = M88DS3103_CHIPTYPE_RS6000 },
+ { .name = "m88ds3103b", .driver_data = M88DS3103_CHIPTYPE_3103B },
+ { .name = "m88ds3103c", .driver_data = M88DS3103_CHIPTYPE_3103C },
+ { }
};
MODULE_DEVICE_TABLE(i2c, m88ds3103_id_table);
diff --git a/drivers/media/dvb-frontends/mn88443x.c b/drivers/media/dvb-frontends/mn88443x.c
index 818c4e67364c..cc6bd17daf73 100644
--- a/drivers/media/dvb-frontends/mn88443x.c
+++ b/drivers/media/dvb-frontends/mn88443x.c
@@ -787,10 +787,10 @@ static const struct of_device_id mn88443x_of_match[] = {
MODULE_DEVICE_TABLE(of, mn88443x_of_match);
static const struct i2c_device_id mn88443x_i2c_id[] = {
- { "mn884433", (kernel_ulong_t)&mn88443x_spec_pri },
- { "mn884434-0", (kernel_ulong_t)&mn88443x_spec_pri },
- { "mn884434-1", (kernel_ulong_t)&mn88443x_spec_sec },
- {}
+ { .name = "mn884433", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri },
+ { .name = "mn884434-0", .driver_data = (kernel_ulong_t)&mn88443x_spec_pri },
+ { .name = "mn884434-1", .driver_data = (kernel_ulong_t)&mn88443x_spec_sec },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mn88443x_i2c_id);
diff --git a/drivers/media/dvb-frontends/mn88472.c b/drivers/media/dvb-frontends/mn88472.c
index 275c404ce286..42b78b9aba20 100644
--- a/drivers/media/dvb-frontends/mn88472.c
+++ b/drivers/media/dvb-frontends/mn88472.c
@@ -708,8 +708,8 @@ static void mn88472_remove(struct i2c_client *client)
}
static const struct i2c_device_id mn88472_id_table[] = {
- { "mn88472" },
- {}
+ { .name = "mn88472" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mn88472_id_table);
diff --git a/drivers/media/dvb-frontends/mn88473.c b/drivers/media/dvb-frontends/mn88473.c
index 40a0cb1d9c67..b7b11a000969 100644
--- a/drivers/media/dvb-frontends/mn88473.c
+++ b/drivers/media/dvb-frontends/mn88473.c
@@ -743,8 +743,8 @@ static void mn88473_remove(struct i2c_client *client)
}
static const struct i2c_device_id mn88473_id_table[] = {
- { "mn88473" },
- {}
+ { .name = "mn88473" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mn88473_id_table);
diff --git a/drivers/media/dvb-frontends/mxl692.c b/drivers/media/dvb-frontends/mxl692.c
index 2d8eaa20723f..bca4a4c3dd66 100644
--- a/drivers/media/dvb-frontends/mxl692.c
+++ b/drivers/media/dvb-frontends/mxl692.c
@@ -1346,8 +1346,8 @@ static void mxl692_remove(struct i2c_client *client)
}
static const struct i2c_device_id mxl692_id_table[] = {
- { "mxl692" },
- {}
+ { .name = "mxl692" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mxl692_id_table);
diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c
index f0ee7c38ee79..4c44e2d695f8 100644
--- a/drivers/media/dvb-frontends/rtl2830.c
+++ b/drivers/media/dvb-frontends/rtl2830.c
@@ -876,8 +876,8 @@ static void rtl2830_remove(struct i2c_client *client)
}
static const struct i2c_device_id rtl2830_id_table[] = {
- { "rtl2830" },
- {}
+ { .name = "rtl2830" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rtl2830_id_table);
diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c
index d8e1546aea5e..b31cdb44b383 100644
--- a/drivers/media/dvb-frontends/rtl2832.c
+++ b/drivers/media/dvb-frontends/rtl2832.c
@@ -1115,18 +1115,18 @@ static void rtl2832_remove(struct i2c_client *client)
dev_dbg(&client->dev, "\n");
- cancel_delayed_work_sync(&dev->i2c_gate_work);
-
i2c_mux_del_adapters(dev->muxc);
+ cancel_delayed_work_sync(&dev->i2c_gate_work);
+
regmap_exit(dev->regmap);
kfree(dev);
}
static const struct i2c_device_id rtl2832_id_table[] = {
- { "rtl2832" },
- {}
+ { .name = "rtl2832" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rtl2832_id_table);
diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c
index 422d1a7b5456..c564485e3bbb 100644
--- a/drivers/media/dvb-frontends/rtl2832_sdr.c
+++ b/drivers/media/dvb-frontends/rtl2832_sdr.c
@@ -399,7 +399,8 @@ static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev)
}
/* Must be called with vb_queue_lock hold */
-static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev)
+static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev,
+ enum vb2_buffer_state state)
{
struct platform_device *pdev = dev->pdev;
unsigned long flags;
@@ -413,7 +414,7 @@ static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev)
buf = list_entry(dev->queued_bufs.next,
struct rtl2832_sdr_frame_buf, list);
list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
}
spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
}
@@ -855,11 +856,15 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
dev_dbg(&pdev->dev, "\n");
- if (!dev->udev)
+ if (!dev->udev) {
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
return -ENODEV;
+ }
- if (mutex_lock_interruptible(&dev->v4l2_lock))
+ if (mutex_lock_interruptible(&dev->v4l2_lock)) {
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
return -ERESTARTSYS;
+ }
if (d->props->power_ctrl)
d->props->power_ctrl(d, 1);
@@ -900,7 +905,11 @@ static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count)
if (ret)
goto err;
+ mutex_unlock(&dev->v4l2_lock);
+ return 0;
+
err:
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
mutex_unlock(&dev->v4l2_lock);
return ret;
@@ -920,7 +929,7 @@ static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq)
rtl2832_sdr_kill_urbs(dev);
rtl2832_sdr_free_urbs(dev);
rtl2832_sdr_free_stream_bufs(dev);
- rtl2832_sdr_cleanup_queued_bufs(dev);
+ rtl2832_sdr_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR);
rtl2832_sdr_unset_adc(dev);
/* sleep tuner */
diff --git a/drivers/media/dvb-frontends/si2165.c b/drivers/media/dvb-frontends/si2165.c
index 4170696af9f0..f1241b63aa5c 100644
--- a/drivers/media/dvb-frontends/si2165.c
+++ b/drivers/media/dvb-frontends/si2165.c
@@ -1281,8 +1281,8 @@ static void si2165_remove(struct i2c_client *client)
}
static const struct i2c_device_id si2165_id_table[] = {
- { "si2165" },
- {}
+ { .name = "si2165" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, si2165_id_table);
diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c
index 9c5bac8cda47..8bc3b6eb1dd3 100644
--- a/drivers/media/dvb-frontends/si2168.c
+++ b/drivers/media/dvb-frontends/si2168.c
@@ -790,8 +790,8 @@ static void si2168_remove(struct i2c_client *client)
}
static const struct i2c_device_id si2168_id_table[] = {
- { "si2168" },
- {}
+ { .name = "si2168" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, si2168_id_table);
diff --git a/drivers/media/dvb-frontends/sp2.c b/drivers/media/dvb-frontends/sp2.c
index 071c7371c699..63dec4b4fd2f 100644
--- a/drivers/media/dvb-frontends/sp2.c
+++ b/drivers/media/dvb-frontends/sp2.c
@@ -407,8 +407,8 @@ static void sp2_remove(struct i2c_client *client)
}
static const struct i2c_device_id sp2_id[] = {
- { "sp2" },
- {}
+ { .name = "sp2" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sp2_id);
diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
index 657df713865e..932bbed5497a 100644
--- a/drivers/media/dvb-frontends/stv090x.c
+++ b/drivers/media/dvb-frontends/stv090x.c
@@ -5079,8 +5079,8 @@ error:
EXPORT_SYMBOL_GPL(stv090x_attach);
static const struct i2c_device_id stv090x_id_table[] = {
- { "stv090x" },
- {}
+ { .name = "stv090x" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stv090x_id_table);
diff --git a/drivers/media/dvb-frontends/stv6110x.c b/drivers/media/dvb-frontends/stv6110x.c
index 7506f39ead55..5075333d7ffc 100644
--- a/drivers/media/dvb-frontends/stv6110x.c
+++ b/drivers/media/dvb-frontends/stv6110x.c
@@ -470,8 +470,8 @@ const struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe,
EXPORT_SYMBOL_GPL(stv6110x_attach);
static const struct i2c_device_id stv6110x_id_table[] = {
- { "stv6110x" },
- {}
+ { .name = "stv6110x" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, stv6110x_id_table);
diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c
index f1343ba67b97..9a5ffb5f70fc 100644
--- a/drivers/media/dvb-frontends/tc90522.c
+++ b/drivers/media/dvb-frontends/tc90522.c
@@ -830,9 +830,9 @@ static void tc90522_remove(struct i2c_client *client)
static const struct i2c_device_id tc90522_id[] = {
- { TC90522_I2C_DEV_SAT, 0 },
- { TC90522_I2C_DEV_TER, 1 },
- {}
+ { .name = TC90522_I2C_DEV_SAT, .driver_data = 0 },
+ { .name = TC90522_I2C_DEV_TER, .driver_data = 1 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tc90522_id);
diff --git a/drivers/media/dvb-frontends/tda10071.c b/drivers/media/dvb-frontends/tda10071.c
index 6e6c2e5c427e..7a635c86b86c 100644
--- a/drivers/media/dvb-frontends/tda10071.c
+++ b/drivers/media/dvb-frontends/tda10071.c
@@ -1230,8 +1230,8 @@ static void tda10071_remove(struct i2c_client *client)
}
static const struct i2c_device_id tda10071_id_table[] = {
- { "tda10071_cx24118" },
- {}
+ { .name = "tda10071_cx24118" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tda10071_id_table);
diff --git a/drivers/media/dvb-frontends/ts2020.c b/drivers/media/dvb-frontends/ts2020.c
index 8b6006635e49..8775083f4dd6 100644
--- a/drivers/media/dvb-frontends/ts2020.c
+++ b/drivers/media/dvb-frontends/ts2020.c
@@ -716,9 +716,9 @@ static void ts2020_remove(struct i2c_client *client)
}
static const struct i2c_device_id ts2020_id_table[] = {
- { "ts2020" },
- { "ts2022" },
- {}
+ { .name = "ts2020" },
+ { .name = "ts2022" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ts2020_id_table);
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 8f2ba4121586..5d173e0ecf42 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -237,7 +237,7 @@ config VIDEO_IMX319
config VIDEO_IMX334
tristate "Sony IMX334 sensor support"
- depends on OF_GPIO
+ depends on OF
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
@@ -248,7 +248,7 @@ config VIDEO_IMX334
config VIDEO_IMX335
tristate "Sony IMX335 sensor support"
- depends on OF_GPIO
+ depends on OF
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
@@ -268,7 +268,7 @@ config VIDEO_IMX355
config VIDEO_IMX412
tristate "Sony IMX412 sensor support"
- depends on OF_GPIO
+ depends on OF
help
This is a Video4Linux2 sensor driver for the Sony
IMX412 camera.
@@ -278,7 +278,7 @@ config VIDEO_IMX412
config VIDEO_IMX415
tristate "Sony IMX415 sensor support"
- depends on OF_GPIO
+ depends on OF
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the Sony
@@ -703,7 +703,7 @@ config VIDEO_OV8865
config VIDEO_OV9282
tristate "OmniVision OV9282 sensor support"
- depends on OF_GPIO
+ depends on OF
select V4L2_CCI_I2C
help
This is a Video4Linux2 sensor driver for the OmniVision
@@ -1271,7 +1271,6 @@ config VIDEO_BT866
config VIDEO_ISL7998X
tristate "Intersil ISL7998x video decoder"
depends on VIDEO_DEV && I2C
- depends on OF_GPIO
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
@@ -1309,7 +1308,6 @@ config VIDEO_MAX9286
tristate "Maxim MAX9286 GMSL deserializer support"
depends on I2C && I2C_MUX
depends on VIDEO_DEV
- depends on OF_GPIO
select V4L2_FWNODE
select VIDEO_V4L2_SUBDEV_API
select MEDIA_CONTROLLER
@@ -1659,6 +1657,8 @@ endmenu
menu "Miscellaneous helper chips"
visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
+source "drivers/media/i2c/cvs/Kconfig"
+
config VIDEO_I2C
tristate "I2C transport video support"
depends on VIDEO_DEV && I2C
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index 90b276a7417a..e45359efe0e4 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -171,3 +171,4 @@ obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
+obj-$(CONFIG_VIDEO_INTEL_CVS) += cvs/
diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c
index f60271082fb5..fcce2857b69e 100644
--- a/drivers/media/i2c/ad5820.c
+++ b/drivers/media/i2c/ad5820.c
@@ -347,8 +347,8 @@ static void ad5820_remove(struct i2c_client *client)
}
static const struct i2c_device_id ad5820_id_table[] = {
- { "ad5820" },
- { "ad5821" },
+ { .name = "ad5820" },
+ { .name = "ad5821" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad5820_id_table);
diff --git a/drivers/media/i2c/adp1653.c b/drivers/media/i2c/adp1653.c
index 391bc75bfcd0..177a6e8d7fb8 100644
--- a/drivers/media/i2c/adp1653.c
+++ b/drivers/media/i2c/adp1653.c
@@ -522,7 +522,7 @@ static void adp1653_remove(struct i2c_client *client)
}
static const struct i2c_device_id adp1653_id_table[] = {
- { ADP1653_NAME },
+ { .name = ADP1653_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, adp1653_id_table);
diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c
index ef8682b980b4..812998729207 100644
--- a/drivers/media/i2c/adv7170.c
+++ b/drivers/media/i2c/adv7170.c
@@ -377,8 +377,8 @@ static void adv7170_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id adv7170_id[] = {
- { "adv7170" },
- { "adv7171" },
+ { .name = "adv7170" },
+ { .name = "adv7171" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv7170_id);
diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c
index 384da1ec5bf9..f1caab8e2abd 100644
--- a/drivers/media/i2c/adv7175.c
+++ b/drivers/media/i2c/adv7175.c
@@ -432,8 +432,8 @@ static void adv7175_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id adv7175_id[] = {
- { "adv7175" },
- { "adv7176" },
+ { .name = "adv7175" },
+ { .name = "adv7176" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv7175_id);
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index 669b0b3165b1..e5d11a6e6766 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -1626,18 +1626,18 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
#endif
static const struct i2c_device_id adv7180_id[] = {
- { "adv7180", (kernel_ulong_t)&adv7180_info },
- { "adv7180cp", (kernel_ulong_t)&adv7180_info },
- { "adv7180st", (kernel_ulong_t)&adv7180_info },
- { "adv7182", (kernel_ulong_t)&adv7182_info },
- { "adv7280", (kernel_ulong_t)&adv7280_info },
- { "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
- { "adv7281", (kernel_ulong_t)&adv7281_info },
- { "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
- { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
- { "adv7282", (kernel_ulong_t)&adv7282_info },
- { "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
- {}
+ { .name = "adv7180", .driver_data = (kernel_ulong_t)&adv7180_info },
+ { .name = "adv7180cp", .driver_data = (kernel_ulong_t)&adv7180_info },
+ { .name = "adv7180st", .driver_data = (kernel_ulong_t)&adv7180_info },
+ { .name = "adv7182", .driver_data = (kernel_ulong_t)&adv7182_info },
+ { .name = "adv7280", .driver_data = (kernel_ulong_t)&adv7280_info },
+ { .name = "adv7280-m", .driver_data = (kernel_ulong_t)&adv7280_m_info },
+ { .name = "adv7281", .driver_data = (kernel_ulong_t)&adv7281_info },
+ { .name = "adv7281-m", .driver_data = (kernel_ulong_t)&adv7281_m_info },
+ { .name = "adv7281-ma", .driver_data = (kernel_ulong_t)&adv7281_ma_info },
+ { .name = "adv7282", .driver_data = (kernel_ulong_t)&adv7282_info },
+ { .name = "adv7282-m", .driver_data = (kernel_ulong_t)&adv7282_m_info },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adv7180_id);
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index 25a31a6dd456..a04a1a205fe0 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
@@ -619,8 +619,8 @@ static void adv7183_remove(struct i2c_client *client)
}
static const struct i2c_device_id adv7183_id[] = {
- { "adv7183" },
- {}
+ { .name = "adv7183" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adv7183_id);
diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c
index b96443404a26..9b91d7073d3c 100644
--- a/drivers/media/i2c/adv7343.c
+++ b/drivers/media/i2c/adv7343.c
@@ -502,8 +502,8 @@ static void adv7343_remove(struct i2c_client *client)
}
static const struct i2c_device_id adv7343_id[] = {
- { "adv7343" },
- {}
+ { .name = "adv7343" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adv7343_id);
diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c
index c7994bd0bbd4..6f948ba02f86 100644
--- a/drivers/media/i2c/adv7393.c
+++ b/drivers/media/i2c/adv7393.c
@@ -446,8 +446,8 @@ static void adv7393_remove(struct i2c_client *client)
}
static const struct i2c_device_id adv7393_id[] = {
- { "adv7393" },
- {}
+ { .name = "adv7393" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adv7393_id);
diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c
index 853c7806de92..860cff50c522 100644
--- a/drivers/media/i2c/adv7511-v4l2.c
+++ b/drivers/media/i2c/adv7511-v4l2.c
@@ -2008,7 +2008,7 @@ static void adv7511_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id adv7511_id[] = {
- { "adv7511-v4l2" },
+ { .name = "adv7511-v4l2" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv7511_id);
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index 67116a4ef134..ac9c69ce438f 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -3236,10 +3236,10 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = {
};
static const struct i2c_device_id adv76xx_i2c_id[] = {
- { "adv7604", (kernel_ulong_t)&adv76xx_chip_info[ADV7604] },
- { "adv7610", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] },
- { "adv7611", (kernel_ulong_t)&adv76xx_chip_info[ADV7611] },
- { "adv7612", (kernel_ulong_t)&adv76xx_chip_info[ADV7612] },
+ { .name = "adv7604", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7604] },
+ { .name = "adv7610", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] },
+ { .name = "adv7611", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7611] },
+ { .name = "adv7612", .driver_data = (kernel_ulong_t)&adv76xx_chip_info[ADV7612] },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv76xx_i2c_id);
@@ -3668,6 +3668,12 @@ static int adv76xx_probe(struct i2c_client *client)
state->source_pad = state->info->num_dv_ports
+ (state->info->has_afe ? 2 : 0);
+ if (WARN_ON(state->source_pad >= ADV76XX_PAD_MAX)) {
+ err = -EINVAL;
+ v4l2_err(sd, "invalid chip info\n");
+ goto err_i2c;
+ }
+
for (i = 0; i < state->source_pad; ++i)
state->pads[i].flags = MEDIA_PAD_FL_SINK;
state->pads[state->source_pad].flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index ea6966c0605e..3cfae89ce944 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -3675,7 +3675,7 @@ static void adv7842_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id adv7842_id[] = {
- { "adv7842" },
+ { .name = "adv7842" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adv7842_id);
diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c
index ee575d01a676..cea46f01997d 100644
--- a/drivers/media/i2c/ak881x.c
+++ b/drivers/media/i2c/ak881x.c
@@ -304,8 +304,8 @@ static void ak881x_remove(struct i2c_client *client)
}
static const struct i2c_device_id ak881x_id[] = {
- { "ak8813" },
- { "ak8814" },
+ { .name = "ak8813" },
+ { .name = "ak8814" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak881x_id);
diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c
index 955b7072a560..dd991c2ee700 100644
--- a/drivers/media/i2c/alvium-csi2.c
+++ b/drivers/media/i2c/alvium-csi2.c
@@ -2100,20 +2100,21 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
V4L2_CID_PIXEL_RATE, 0,
ALVIUM_DEFAULT_PIXEL_RATE_MHZ, 1,
ALVIUM_DEFAULT_PIXEL_RATE_MHZ);
- ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
/* Link freq is fixed */
ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops,
V4L2_CID_LINK_FREQ,
0, 0, &alvium->link_freq);
- ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if (ctrls->link_freq)
+ ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
/* Auto/manual white balance */
if (alvium->avail_ft.auto_whiteb) {
ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops,
V4L2_CID_AUTO_WHITE_BALANCE,
0, 1, 1, 1);
- v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
+ if (ctrls->auto_wb)
+ v4l2_ctrl_auto_cluster(3, &ctrls->auto_wb, 0, false);
}
ctrls->blue_balance = v4l2_ctrl_new_std(hdl, ops,
@@ -2122,6 +2123,7 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
alvium->max_bbalance,
alvium->inc_bbalance,
alvium->dft_bbalance);
+
ctrls->red_balance = v4l2_ctrl_new_std(hdl, ops,
V4L2_CID_RED_BALANCE,
alvium->min_rbalance,
@@ -2136,7 +2138,9 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
V4L2_CID_EXPOSURE_AUTO,
V4L2_EXPOSURE_MANUAL, 0,
V4L2_EXPOSURE_AUTO);
- v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp, 1, true);
+ if (ctrls->auto_exp)
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_exp,
+ V4L2_EXPOSURE_MANUAL, true);
}
ctrls->exposure = v4l2_ctrl_new_std(hdl, ops,
@@ -2145,14 +2149,16 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
alvium->max_exp,
alvium->inc_exp,
alvium->dft_exp);
- ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ if (ctrls->exposure)
+ ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE;
/* Auto/manual gain */
if (alvium->avail_ft.auto_gain) {
ctrls->auto_gain = v4l2_ctrl_new_std(hdl, ops,
V4L2_CID_AUTOGAIN,
0, 1, 1, 1);
- v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
+ if (ctrls->auto_gain)
+ v4l2_ctrl_auto_cluster(2, &ctrls->auto_gain, 0, true);
}
if (alvium->avail_ft.gain) {
@@ -2162,7 +2168,8 @@ static int alvium_ctrl_init(struct alvium_dev *alvium)
alvium->max_gain,
alvium->inc_gain,
alvium->dft_gain);
- ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ if (ctrls->gain)
+ ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE;
}
if (alvium->avail_ft.sat)
diff --git a/drivers/media/i2c/bt819.c b/drivers/media/i2c/bt819.c
index f97245f91f88..da44100062fa 100644
--- a/drivers/media/i2c/bt819.c
+++ b/drivers/media/i2c/bt819.c
@@ -457,9 +457,9 @@ static void bt819_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id bt819_id[] = {
- { "bt819a" },
- { "bt817a" },
- { "bt815a" },
+ { .name = "bt819a" },
+ { .name = "bt817a" },
+ { .name = "bt815a" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bt819_id);
diff --git a/drivers/media/i2c/bt856.c b/drivers/media/i2c/bt856.c
index 6852aa47cafb..656719158bb4 100644
--- a/drivers/media/i2c/bt856.c
+++ b/drivers/media/i2c/bt856.c
@@ -230,7 +230,7 @@ static void bt856_remove(struct i2c_client *client)
}
static const struct i2c_device_id bt856_id[] = {
- { "bt856" },
+ { .name = "bt856" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bt856_id);
diff --git a/drivers/media/i2c/bt866.c b/drivers/media/i2c/bt866.c
index a2cc34d35ed2..f5104c7d9f90 100644
--- a/drivers/media/i2c/bt866.c
+++ b/drivers/media/i2c/bt866.c
@@ -197,7 +197,7 @@ static void bt866_remove(struct i2c_client *client)
}
static const struct i2c_device_id bt866_id[] = {
- { "bt866" },
+ { .name = "bt866" },
{ }
};
MODULE_DEVICE_TABLE(i2c, bt866_id);
diff --git a/drivers/media/i2c/cs3308.c b/drivers/media/i2c/cs3308.c
index 3f2f993e16a6..ad37aeecf05e 100644
--- a/drivers/media/i2c/cs3308.c
+++ b/drivers/media/i2c/cs3308.c
@@ -109,7 +109,7 @@ static void cs3308_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id cs3308_id[] = {
- { "cs3308" },
+ { .name = "cs3308" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs3308_id);
diff --git a/drivers/media/i2c/cs5345.c b/drivers/media/i2c/cs5345.c
index 3a9797a50e82..49b8020fbea2 100644
--- a/drivers/media/i2c/cs5345.c
+++ b/drivers/media/i2c/cs5345.c
@@ -189,7 +189,7 @@ static void cs5345_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id cs5345_id[] = {
- { "cs5345" },
+ { .name = "cs5345" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs5345_id);
diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c
index c4cad3293905..a894dcf6b5a9 100644
--- a/drivers/media/i2c/cs53l32a.c
+++ b/drivers/media/i2c/cs53l32a.c
@@ -200,7 +200,7 @@ static void cs53l32a_remove(struct i2c_client *client)
}
static const struct i2c_device_id cs53l32a_id[] = {
- { "cs53l32a" },
+ { .name = "cs53l32a" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs53l32a_id);
diff --git a/drivers/media/i2c/cvs/Kconfig b/drivers/media/i2c/cvs/Kconfig
new file mode 100644
index 000000000000..4309d20dd726
--- /dev/null
+++ b/drivers/media/i2c/cvs/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config VIDEO_INTEL_CVS
+ tristate "Intel CVS CSI-2 bridge support"
+ depends on I2C && ACPI && VIDEO_DEV
+ depends on IPU_BRIDGE || !IPU_BRIDGE
+ select MEDIA_CONTROLLER
+ select VIDEO_V4L2_SUBDEV_API
+ select V4L2_FWNODE
+ help
+ This adds support for the Intel Computer Vision Sensing (CVS).
+
+ The driver registers a V4L2 sub-device to arbitrate CSI-2 link
+ ownership between the host and CVS firmware, and configures the
+ device for camera sensor streaming.
+
+ The driver can operate with full I2C transport or in a reduced
+ platform (GPIO-only) mode when I2C is unavailable.
+
+ Say Y to build into the kernel, or M to build as a module.
+ The module will be named intel_cvs. If unsure, say N.
diff --git a/drivers/media/i2c/cvs/Makefile b/drivers/media/i2c/cvs/Makefile
new file mode 100644
index 000000000000..0aeb2c2e3100
--- /dev/null
+++ b/drivers/media/i2c/cvs/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+intel_cvs-y := core.o v4l2.o
+
+obj-$(CONFIG_VIDEO_INTEL_CVS) += intel_cvs.o
diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c
new file mode 100644
index 000000000000..4282f33c7295
--- /dev/null
+++ b/drivers/media/i2c/cvs/core.c
@@ -0,0 +1,1043 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Intel Corporation
+ */
+
+#include <linux/acpi.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/time64.h>
+#include <linux/workqueue.h>
+
+#include <media/ipu-bridge.h>
+#include <media/ipu6-pci-table.h>
+
+#include "icvs.h"
+
+/* Command timeouts determined experimentally */
+#define CMD_TIMEOUT (5 * HZ)
+#define FW_READY_DELAY_MS 100
+
+#define PCI_DEVICE_ID_INTEL_IPU7 0x645d /* MTL / LNL */
+#define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d /* ARL / PTL */
+
+/*
+ * IPU7 PCI device IDs not covered by ipu6_pci_tbl in ipu6-pci-table.h.
+ * Once the IPU6 driver gains support for IPU7, this table can be dropped.
+ */
+static const struct pci_device_id icvs_ipu7_tbl[] = {
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) },
+ { }
+};
+
+static const struct acpi_gpio_params gpio_wake = { 0, 0, false };
+static const struct acpi_gpio_params gpio_rst = { 1, 0, false };
+static const struct acpi_gpio_params gpio_req = { 2, 0, false };
+static const struct acpi_gpio_params gpio_resp = { 3, 0, false };
+static const struct acpi_gpio_mapping icvs_acpi_gpios[] = {
+ { "wake-gpio", &gpio_wake, 1 },
+ { "rst-gpio", &gpio_rst, 1 },
+ { "req-gpio", &gpio_req, 1 },
+ { "resp-gpio", &gpio_resp, 1 },
+ { }
+};
+
+static const struct acpi_gpio_params lgpio_req = { 0, 0, false };
+static const struct acpi_gpio_params lgpio_resp = { 1, 0, false };
+static const struct acpi_gpio_mapping icvs_acpi_lgpios[] = {
+ { "req-gpio", &lgpio_req, 1 },
+ { "resp-gpio", &lgpio_resp, 1 },
+ { }
+};
+
+/* Device quirk table */
+static const struct icvs_device_quirk cvs_quirk_table[] = {
+ { 0x2ac1, 0x20d0, ICVS_NO_MIPI_CONFIG |
+ ICVS_NO_CAPS |
+ ICVS_NO_FW_UPDATE
+ }, /* Lattice NX33 */
+ { 0x06CB, 0x0701, ICVS_SKIP_FW_RESET |
+ ICVS_HOST_SENSOR_PWR_CTRL |
+ ICVS_HOST_PRIV_CTRL |
+ ICVS_FW_BUF_SIZE_256 |
+ ICVS_FW_HEADER_SIZE_256
+ }, /* Synaptics SVP7xxx */
+ { }
+};
+
+/**
+ * cvs_set_quirks - Match device VID/PID and set quirks
+ * @ctx: CVS device context
+ * @vid: Vendor ID
+ * @pid: Product ID
+ *
+ * Searches the quirk table for a matching VID/PID and populates ctx->quirks
+ * with the corresponding quirk flags.
+ * If no match is found, quirks is set to 0.
+ */
+static void cvs_set_quirks(struct icvs *ctx, u16 vid, u16 pid)
+{
+ ctx->quirks = 0;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(cvs_quirk_table); i++) {
+ if (cvs_quirk_table[i].vid == vid &&
+ cvs_quirk_table[i].pid == pid) {
+ ctx->quirks = cvs_quirk_table[i].quirks;
+ dev_info(cvs_dev(ctx),
+ "Quirks: 0x%lx (VID:0x%04x PID:0x%04x)\n",
+ ctx->quirks, vid, pid);
+ return;
+ }
+ }
+
+ dev_info(cvs_dev(ctx),
+ "No quirks for device (VID:0x%04x PID:0x%04x)\n", vid, pid);
+}
+
+/* I2C transport helpers */
+
+/**
+ * cvs_read_i2c - Issue a read-type command and fetch device response
+ * @ctx: CVS device context
+ * @cmd_id: Command identifier (big endian)
+ * @resp: Destination buffer for response payload
+ * @size: Size of payload to read into @resp (without prefix)
+ *
+ * Sends @cmd_id and reads back the response in a single I2C transaction.
+ * When the device prepends a 4-byte protocol prefix, the combined
+ * prefix+payload is read into a temporary buffer and only the payload is
+ * copied to @resp, avoiding any dependency on the layout of the caller's
+ * buffer.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_read_i2c(struct icvs *ctx, __be16 cmd_id, void *resp,
+ size_t size)
+{
+ size_t prefix_size = ctx->prefix ? sizeof(u32) : 0;
+ size_t read_size = size + prefix_size;
+ struct i2c_client *i2c = ctx->i2c_client;
+ u8 *buf __free(kfree) = NULL;
+ int cnt;
+
+ if (!resp || !size)
+ return -EINVAL;
+
+ cnt = i2c_master_send(i2c, (const char *)&cmd_id, sizeof(cmd_id));
+ if (cnt != sizeof(cmd_id))
+ return cnt < 0 ? cnt : -EIO;
+
+ buf = kmalloc(read_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ cnt = i2c_master_recv(i2c, buf, read_size);
+ if (cnt != read_size) {
+ dev_dbg(cvs_dev(ctx), "recv cmd 0x%04x short read (%d/%zu)\n",
+ be16_to_cpu(cmd_id), cnt, read_size);
+ return cnt < 0 ? cnt : -EIO;
+ }
+
+ memcpy(resp, buf + prefix_size, size);
+
+ return 0;
+}
+
+/**
+ * cvs_write_i2c - Write a raw command buffer to the device over I2C
+ * @ctx: CVS device context
+ * @data: Buffer containing command + payload
+ * @size: Total bytes to write
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_write_i2c(struct icvs *ctx, const void *data, int size)
+{
+ struct i2c_client *i2c = ctx->i2c_client;
+ int cnt;
+
+ if (size < 0 || !data)
+ return -EINVAL;
+
+ cnt = i2c_master_send(i2c, data, size);
+ if (cnt != size) {
+ dev_dbg(cvs_dev(ctx), "send short (%d/%d)\n", cnt, size);
+ return cnt < 0 ? cnt : -EIO;
+ }
+
+ return 0;
+}
+
+/**
+ * cvs_checksum - Simple additive checksum helper
+ * @data: 32-bit aligned data buffer
+ * @len: Length in bytes (multiple of 4)
+ *
+ * Return: 32-bit additive checksum of the dwords in @data.
+ */
+static u32 cvs_checksum(const void *data, size_t len)
+{
+ const u32 *words = data;
+ u32 csum = 0;
+
+ if (WARN_ON_ONCE(len % sizeof(u32)))
+ return 0;
+
+ for (unsigned int i = 0; i < len / sizeof(u32); i++)
+ csum += words[i];
+
+ return csum;
+}
+
+/**
+ * cvs_schedule_and_wait - Schedule polling work then wait for completion
+ * @ctx: CVS device context
+ * @work_delay_ms: Delay in milliseconds before polling work executes
+ * @wait_jiffies: Timeout (jiffies) to wait for cmd completion
+ *
+ * Queues ctx->work to run after @work_delay_ms and then waits up to
+ * @wait_jiffies for ctx->cmd_completion.
+ *
+ * Return: 0 on success, -ETIMEDOUT on timeout, negative errno on error.
+ */
+static int cvs_schedule_and_wait(struct icvs *ctx, unsigned int work_delay_ms,
+ unsigned long wait_jiffies)
+{
+ int ret;
+
+ schedule_delayed_work(&ctx->work, msecs_to_jiffies(work_delay_ms));
+ ret = wait_for_completion_killable_timeout(&ctx->cmd_completion,
+ wait_jiffies);
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+/**
+ * cvs_wait_wake_or_sleep - Wait for wake IRQ or sleep fallback
+ * @ctx: CVS device context
+ * @timeout_jiffies: Timeout (jiffies) for wake event on full-cap devices
+ * @sleep_ms: Milliseconds to sleep on light-cap devices
+ *
+ * For full capability devices (ICVS_FULLCAP) waits interruptibly on
+ * ctx->hostwake_event until ctx->hostwake_event_arg becomes true or
+ * @timeout_jiffies elapses. The flag is cleared after the wait.
+ * For light capability devices performs a blocking msleep(@sleep_ms).
+ *
+ * Return codes normalized for caller switch handling:
+ * <0 : error / interrupted
+ * -ETIMEDOUT : timeout (wake event not observed)
+ * 0 : success (wake observed OR light-cap sleep elapsed)
+ *
+ * Return: negative errno, -ETIMEDOUT on timeout, 0 on success.
+ */
+static int cvs_wait_wake_or_sleep(struct icvs *ctx,
+ unsigned long timeout_jiffies,
+ unsigned int sleep_ms)
+{
+ int ret;
+
+ if (ctx->res == ICVS_FULLCAP) {
+ ret = wait_event_interruptible_timeout(ctx->hostwake_event,
+ ctx->hostwake_event_arg,
+ timeout_jiffies);
+ ctx->hostwake_event_arg = false;
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return -ETIMEDOUT;
+ return 0;
+ }
+
+ msleep(sleep_ms);
+
+ return 0; /* treat sleep path as success */
+}
+
+/**
+ * cvs_config_mipi - Send a HOST_SET_MIPI_CONFIG command
+ * @ctx: CVS device context
+ * @c: Command container with conf field populated
+ * @len: Length of original command structure (unused except for symmetry)
+ *
+ * Packages @c->param.conf into a cvs_mipi_data_packet including size and
+ * checksum, then writes it to the device.
+ *
+ * Firmware note: the CVS firmware expects the MIPI configuration to be
+ * sent as a single transaction, including all relevant parameters and checksum.
+ *
+ * Return: 0 on success, NO_MIPI_CONFIG or negative errno from I2C write.
+ */
+static int cvs_config_mipi(struct icvs *ctx, struct icvs_cmd *c, size_t len)
+{
+ struct icvs_mipi_data_packet pkt = {
+ .cmd_id = c->cmd_id,
+ .size = sizeof(c->param.conf),
+ .crc = cvs_checksum(&c->param.conf, sizeof(c->param.conf)),
+ .conf = c->param.conf,
+ };
+
+ if (ctx->quirks & ICVS_NO_MIPI_CONFIG)
+ return 0;
+
+ return cvs_write_i2c(ctx, &pkt, sizeof(pkt));
+}
+
+/**
+ * cvs_get_device_state - Query current device state bitfield
+ * @ctx: CVS device context
+ * @state: Returned state value
+ *
+ * Issues GET_DEV_STATE and fills @state.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_get_device_state(struct icvs *ctx, u8 *state)
+{
+ struct icvs_resp n = {
+ .cmd_id = cpu_to_be16(ICVS_GET_DEV_STATE),
+ };
+ int ret;
+
+ ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.state, sizeof(n.resp.state));
+ if (ret)
+ return ret;
+
+ *state = n.resp.state;
+
+ return 0;
+}
+
+/**
+ * cvs_get_device_caps - Read protocol capabilities
+ * @ctx: CVS device context
+ * @caps: Capability structure to populate
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_get_device_caps(struct icvs *ctx,
+ struct icvs_dev_capabilities *caps)
+{
+ struct icvs_resp n = {
+ .cmd_id = cpu_to_be16(ICVS_GET_DEV_CAPABILITY),
+ };
+ int ret;
+
+ if (ctx->quirks & ICVS_NO_CAPS)
+ return 0;
+
+ ret = cvs_read_i2c(ctx, n.cmd_id, &n.resp.cap, sizeof(n.resp.cap));
+ if (ret)
+ return ret;
+
+ *caps = n.resp.cap;
+
+ return 0;
+}
+
+/**
+ * cvs_hw_init - Probe device for prefix support and apply quirks
+ * @ctx: CVS device context
+ *
+ * Sends GET_DEV_VID_PID and probes for a 32-bit prefix.
+ * If it matches ICVS_PREFIX_VAL, sets ctx->prefix for subsequent reads.
+ * Then reads VID/PID and applies matching quirks.
+ * GET_DEV_VID_PID is supported by all protocol versions.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_hw_init(struct icvs *ctx)
+{
+ struct icvs_resp n = { };
+ __be16 cmd = cpu_to_be16(ICVS_GET_DEV_VID_PID);
+ u32 resp;
+ int ret;
+
+ /*
+ * Clear prefix so cvs_read_i2c always reads exactly sizeof(u32) bytes
+ * here, regardless of any value left over from a previous call (e.g.
+ * on resume).
+ */
+ ctx->prefix = false;
+ ret = cvs_read_i2c(ctx, cmd, &resp, sizeof(resp));
+ if (ret)
+ return ret;
+
+ ctx->prefix = resp == ICVS_PREFIX_VAL;
+
+ /* Now read VID/PID to apply quirks */
+ ret = cvs_read_i2c(ctx, cmd,
+ &n.resp.vid_pid, sizeof(n.resp.vid_pid));
+ if (ret)
+ return ret;
+
+ cvs_set_quirks(ctx, n.resp.vid_pid.v_id, n.resp.vid_pid.p_id);
+
+ return 0;
+}
+
+/**
+ * cvs_irq_handler - Wake IRQ handler (full capability devices)
+ * @irq: IRQ number
+ * @dev_id: Device context pointer
+ *
+ * Sets a waitqueue flag and wakes up sleeping waiters.
+ *
+ * Return: IRQ_HANDLED always.
+ */
+static irqreturn_t cvs_irq_handler(int irq, void *dev_id)
+{
+ struct icvs *ctx = dev_id;
+
+ ctx->hostwake_event_arg = true;
+ wake_up_interruptible(&ctx->hostwake_event);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * cvs_reset - Toggle reset GPIO for full capability devices
+ * @ctx: CVS device context
+ *
+ * Drives reset low briefly then high if device resources indicate full
+ * capability. Light devices have no reset line.
+ */
+static void cvs_reset(struct icvs *ctx)
+{
+ if (ctx->quirks & ICVS_SKIP_FW_RESET)
+ return;
+
+ if (ctx->res == ICVS_FULLCAP) {
+ gpiod_set_value(ctx->rst, 0);
+ fsleep(2000);
+ gpiod_set_value(ctx->rst, 1);
+ }
+}
+
+/**
+ * cvs_recv - Delayed work handler polling for command completion
+ * @work: Embedded delayed_work member
+ *
+ * Re-reads device state; if device_busy remains set, re-schedules itself.
+ * Otherwise stores state into wq_resp and completes the command.
+ */
+static void cvs_recv(struct work_struct *work)
+{
+ struct icvs *ctx = container_of(work, struct icvs, work.work);
+ u8 state = 0;
+ int ret;
+
+ ret = cvs_get_device_state(ctx, &state);
+ if (ret < 0) {
+ dev_dbg(cvs_dev(ctx), "state read failed: %d\n", ret);
+ return;
+ }
+
+ if (state & ICVS_DEV_STATE_BUSY) {
+ dev_dbg(cvs_dev(ctx), "device busy, reschedule\n");
+ schedule_delayed_work(&ctx->work,
+ msecs_to_jiffies(FW_READY_DELAY_MS));
+ return;
+ }
+
+ ctx->wq_resp.resp.state = state;
+ complete(&ctx->cmd_completion);
+}
+
+/**
+ * cvs_send - Common command submission path
+ * @ctx: CVS device context
+ * @cmd: Command buffer (icvs_cmd) with cmd_id and param populated
+ * @len: Buffer length
+ *
+ * Dispatches a set of supported commands:
+ * - ICVS_SET_DEV_HOST_ID,
+ * - ICVS_HOST_SENSOR_OWNER,
+ * - ICVS_HOST_SET_MIPI_CONFIG
+ * - ICVS_FW_LOADER_*
+ *
+ * For I2C based commands it sets big-endian cmd ids, writes to the device
+ * and waits (via delayed work) for completion or timeout.
+ * GPIO based ownership toggles are handled locally.
+ *
+ * Caller must hold ctx->lock when invoking this function and check for i2c
+ * bus availability.
+ *
+ * Return: 0 on success, negative errno, -EINVAL for unsupported command
+ * or status from device in ctx->wq_resp.
+ */
+int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len)
+{
+ int ret, status = 0;
+
+ lockdep_assert_held(&ctx->lock);
+
+ dev_dbg(cvs_dev(ctx), "send cmd = 0x%04x", be16_to_cpu(cmd->cmd_id));
+
+ reinit_completion(&ctx->cmd_completion);
+
+ switch (be16_to_cpu(cmd->cmd_id)) {
+ case ICVS_SET_DEV_HOST_ID:
+ cmd->cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID);
+ ret = cvs_write_i2c(ctx, cmd, len);
+ if (ret < 0)
+ break;
+
+ ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS,
+ CMD_TIMEOUT);
+ if (ret < 0)
+ break;
+
+ status = ctx->wq_resp.resp.state &
+ ICVS_DEV_STATE_ERROR ? -EINVAL : 0;
+ break;
+ case ICVS_HOST_SENSOR_OWNER:
+ gpiod_set_value_cansleep(ctx->req, cmd->param.param);
+ fsleep(FW_READY_DELAY_MS * USEC_PER_MSEC);
+ ret = gpiod_get_value_cansleep(ctx->resp);
+ status = cmd->param.param == ret ? 0 : -EINVAL;
+ ret = 0; /* success */
+ break;
+ case ICVS_HOST_SET_MIPI_CONFIG:
+ cmd->cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG);
+ ret = cvs_config_mipi(ctx, cmd, len);
+ if (ret < 0)
+ break;
+
+ ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS,
+ CMD_TIMEOUT);
+ status = (ctx->wq_resp.resp.state &
+ ICVS_DEV_STATE_ERROR) ? -EINVAL : 0;
+ break;
+ case ICVS_FW_LOADER_START:
+ cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_START);
+ ret = cvs_write_i2c(ctx, cmd, len);
+ if (ret < 0)
+ break;
+
+ ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT,
+ FW_READY_DELAY_MS);
+ if (ret)
+ break;
+
+ ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS,
+ CMD_TIMEOUT);
+ status = (ctx->wq_resp.resp.state &
+ ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL;
+ break;
+ case ICVS_FW_LOADER_DATA:
+ /* Quirk for older protocols */
+ if (ctx->caps.protocol_version_major >= 2 &&
+ ctx->caps.protocol_version_minor >= 2) {
+ cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_DATA);
+ ret = cvs_write_i2c(ctx, cmd, len);
+ } else {
+ ret = cvs_write_i2c(ctx, &cmd->param,
+ len - sizeof(cmd->cmd_id));
+ }
+
+ if (ret < 0)
+ return ret;
+
+ ret = cvs_wait_wake_or_sleep(ctx, FW_READY_DELAY_MS,
+ FW_READY_DELAY_MS);
+ if (ret)
+ break;
+
+ ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS,
+ CMD_TIMEOUT);
+ status = ctx->wq_resp.resp.state &
+ ICVS_DEV_STATE_ERROR ? -EINVAL : 0;
+ break;
+ case ICVS_FW_LOADER_END:
+ cmd->cmd_id = cpu_to_be16(ICVS_FW_LOADER_END);
+ ret = cvs_write_i2c(ctx, cmd, len);
+ if (ret < 0)
+ break;
+
+ ret = cvs_wait_wake_or_sleep(ctx, CMD_TIMEOUT,
+ FW_READY_DELAY_MS);
+ if (ret)
+ break;
+
+ ret = cvs_schedule_and_wait(ctx, FW_READY_DELAY_MS,
+ CMD_TIMEOUT);
+ status = !(ctx->wq_resp.resp.state &
+ ICVS_DEV_STATE_DOWNLOAD) ? 0 : -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return ctx->wq_resp.status = status;
+}
+
+/**
+ * cvs_set_link_owner - Switch CSI-2 link ownership between host and device
+ * @ctx: CVS device context
+ * @owner: Desired owner (ICVS_CSI_LINK_HOST or ICVS_CSI_LINK_CVS)
+ *
+ * Called from runtime PM callbacks to claim or release the CSI-2 link.
+ * Also callable directly for error recovery paths.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner)
+{
+ struct icvs_cmd cmd = {
+ .cmd_id = cpu_to_be16(ICVS_HOST_SENSOR_OWNER),
+ .param.param = owner,
+ };
+ size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.param);
+
+ guard(mutex)(&ctx->lock);
+ return cvs_send(ctx, &cmd, cmd_size);
+}
+
+/**
+ * cvs_configure_dev_caps - Configure device capability ownership bits
+ * @ctx: CVS device context
+ *
+ * Tells the CVS device which of its features (privacy LED, RGB camera
+ * power-up, vision sensing) are controlled by the host, then sends
+ * SET_DEV_HOST_ID.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_configure_dev_caps(struct icvs *ctx)
+{
+ struct icvs_cmd cmd = { .cmd_id = cpu_to_be16(ICVS_SET_DEV_HOST_ID) };
+ size_t sz = sizeof(cmd.cmd_id) + sizeof(cmd.param.host_id);
+
+ if (ctx->quirks & ICVS_NO_CAPS)
+ return 0;
+
+ if (ctx->quirks & ICVS_HOST_VISION_SENSING)
+ cmd.param.host_id |= ICVS_HOST_ID_VISION_SENSING;
+ if (ctx->quirks & ICVS_HOST_PRIV_CTRL)
+ cmd.param.host_id |= ICVS_HOST_ID_PRIVACY_LED;
+ if (ctx->quirks & ICVS_HOST_SENSOR_PWR_CTRL)
+ cmd.param.host_id |= ICVS_HOST_ID_RGBCAMERA_PWRUP;
+
+ guard(mutex)(&ctx->lock);
+ return cvs_send(ctx, &cmd, sz);
+}
+
+/**
+ * cvs_core_probe - Shared probe path for I2C & platform instantiation
+ * @dev: Parent device
+ * @i2c: I2C client (NULL for platform devices)
+ *
+ * Discovers IPU, parses ACPI resources, sets up GPIOs/IRQs, initializes
+ * sub-device (CSI) and host identifier, and exposes sysfs firmware interface.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
+{
+ struct pci_dev *ipu = NULL;
+ struct icvs *ctx;
+ int ret;
+
+ /* Locate IPU device */
+ for (unsigned int i = 0; !ipu && ipu6_pci_tbl[i].vendor; i++)
+ ipu = pci_get_device(ipu6_pci_tbl[i].vendor,
+ ipu6_pci_tbl[i].device, NULL);
+ for (unsigned int i = 0; !ipu && icvs_ipu7_tbl[i].vendor; i++)
+ ipu = pci_get_device(icvs_ipu7_tbl[i].vendor,
+ icvs_ipu7_tbl[i].device, NULL);
+ if (!ipu)
+ return -ENODEV;
+
+ ret = ipu_bridge_init(&ipu->dev, ipu_bridge_parse_ssdb);
+ if (ret < 0)
+ goto err_put_ipu;
+
+ if (!dev_fwnode(dev)) {
+ ret = -ENXIO;
+ goto err_put_ipu;
+ }
+
+ ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ ret = -ENOMEM;
+ goto err_put_ipu;
+ }
+
+ ctx->i2c_client = i2c;
+
+ ret = gpiod_count(dev, NULL);
+ switch (ret) {
+ case ICVS_GPIO_SYNC:
+ ctx->res = ICVS_LIGHTCAP;
+ break;
+ case ICVS_GPIO_ASYNC:
+ ctx->res = ICVS_FULLCAP;
+ break;
+ default:
+ dev_err(dev, "unexpected GPIO count %d\n", ret);
+ ret = -EINVAL;
+ goto err_put_ipu;
+ }
+
+ ret = devm_acpi_dev_add_driver_gpios(dev,
+ ctx->res == ICVS_FULLCAP ?
+ icvs_acpi_gpios :
+ icvs_acpi_lgpios);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to add ACPI GPIOs\n");
+ goto err_put_ipu;
+ }
+
+ ctx->req = devm_gpiod_get(dev, "req", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->req)) {
+ ret = dev_err_probe(dev, PTR_ERR(ctx->req),
+ "failed to get req GPIO\n");
+ goto err_put_ipu;
+ }
+
+ ctx->resp = devm_gpiod_get(dev, "resp", GPIOD_IN);
+ if (IS_ERR(ctx->resp)) {
+ ret = dev_err_probe(dev, PTR_ERR(ctx->resp),
+ "failed to get resp GPIO\n");
+ goto err_put_ipu;
+ }
+
+ if (ctx->res == ICVS_FULLCAP) {
+ struct gpio_desc *wake;
+
+ ctx->rst = devm_gpiod_get(dev, "rst", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctx->rst)) {
+ ret = dev_err_probe(dev, PTR_ERR(ctx->rst),
+ "failed to get rst GPIO\n");
+ goto err_put_ipu;
+ }
+
+ wake = devm_gpiod_get(dev, "wake", GPIOD_IN);
+ if (IS_ERR(wake)) {
+ ret = dev_err_probe(dev, PTR_ERR(wake),
+ "failed to get wake GPIO\n");
+ goto err_put_ipu;
+ }
+
+ ctx->irq = gpiod_to_irq(wake);
+ if (ctx->irq < 0) {
+ ret = dev_err_probe(dev, ctx->irq,
+ "failed to get wake IRQ\n");
+ goto err_put_ipu;
+ }
+
+ ret = devm_request_threaded_irq(dev, ctx->irq, NULL,
+ cvs_irq_handler,
+ IRQF_ONESHOT | IRQF_NO_SUSPEND,
+ "cvs_wake", ctx);
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to request IRQ\n");
+ goto err_put_ipu;
+ }
+ }
+
+ ret = devm_mutex_init(dev, &ctx->lock);
+ if (ret)
+ goto err_put_ipu;
+
+ init_completion(&ctx->cmd_completion);
+ init_waitqueue_head(&ctx->hostwake_event);
+ INIT_DELAYED_WORK(&ctx->work, cvs_recv);
+
+ if (i2c) {
+ ret = cvs_hw_init(ctx);
+ if (ret) {
+ dev_err(dev, "HW init failed (%d)\n", ret);
+ /*
+ * Fallback to GPIO-only mode.
+ * Some BIOS show the device on the I2C bus, however,
+ * the device is not accessible via I2C.
+ */
+ ctx->i2c_client = NULL;
+ goto fail_i2c;
+ }
+
+ ret = cvs_get_device_caps(ctx, &ctx->caps);
+ if (ret) {
+ dev_err_probe(dev, ret, "get caps failed\n");
+ goto err_put_ipu;
+ }
+
+ ret = cvs_configure_dev_caps(ctx);
+ if (ret) {
+ dev_err_probe(dev, ret,
+ "configure dev caps failed\n");
+ goto err_put_ipu;
+ }
+ }
+
+fail_i2c:
+ ret = cvs_csi_init(ctx, dev, i2c);
+ if (ret) {
+ dev_err_probe(dev, ret, "CSI init failed\n");
+ goto err_put_ipu;
+ }
+
+ dev_set_drvdata(dev, ctx);
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ /*
+ * Create a PM runtime device link with IPU as consumer and CVS as
+ * supplier. When the IPU runtime-resumes to start streaming, the PM
+ * framework automatically resumes CVS first, triggering
+ * cvs_runtime_resume() which hands CSI-2 link ownership to the host.
+ */
+ ctx->ipu_link = device_link_add(&ipu->dev, dev,
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE |
+ DL_FLAG_STATELESS);
+ if (!ctx->ipu_link) {
+ dev_err(dev, "IPU device link failed\n");
+ ret = -ENODEV;
+ goto err_csi_remove;
+ }
+
+ if (has_acpi_companion(dev))
+ acpi_dev_clear_dependencies(ACPI_COMPANION(dev));
+
+ put_device(&ipu->dev);
+
+ return 0;
+
+err_csi_remove:
+ if (ctx->ipu_link)
+ device_link_del(ctx->ipu_link);
+ cvs_csi_remove(ctx);
+ pm_runtime_dont_use_autosuspend(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+
+err_put_ipu:
+ put_device(&ipu->dev);
+
+ return ret;
+}
+
+/**
+ * cvs_probe - I2C driver probe entry
+ * @i2c: I2C client
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_probe(struct i2c_client *i2c)
+{
+ return cvs_core_probe(&i2c->dev, i2c);
+}
+
+/**
+ * cvs_core_remove - Shared remove logic
+ * @dev: Device
+ */
+static void cvs_core_remove(struct device *dev)
+{
+ struct icvs *ctx = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&ctx->work);
+ cvs_csi_remove(ctx);
+
+ if (ctx->ipu_link)
+ device_link_del(ctx->ipu_link);
+
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+
+ cvs_reset(ctx);
+}
+
+/**
+ * cvs_remove - I2C driver remove
+ * @client: I2C client
+ */
+static void cvs_remove(struct i2c_client *client)
+{
+ cvs_core_remove(&client->dev);
+}
+
+/**
+ * cvs_suspend - System suspend callback
+ * @dev: Device
+ *
+ * Return: 0.
+ */
+static int __maybe_unused cvs_suspend(struct device *dev)
+{
+ struct icvs *ctx = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&ctx->work);
+
+ return 0;
+}
+
+/**
+ * cvs_resume - System resume callback
+ * @dev: Device
+ *
+ * Re-validates I2C link prefix and re-sends host id if transport available.
+ *
+ * Return: 0 on success or negative errno if I2C check fails.
+ */
+static int __maybe_unused cvs_resume(struct device *dev)
+{
+ struct icvs *ctx = dev_get_drvdata(dev);
+ int ret;
+
+ if (ctx->i2c_client) {
+ ret = cvs_hw_init(ctx);
+ if (ret)
+ return ret;
+ return cvs_configure_dev_caps(ctx);
+ }
+
+ return 0;
+}
+
+/**
+ * cvs_runtime_resume - Runtime PM resume: claim CSI-2 link ownership
+ * @dev: Device
+ *
+ * Triggered automatically when the IPU (consumer) runtime-resumes, because
+ * a DL_FLAG_PM_RUNTIME device link makes CVS the supplier. Transfers CSI-2
+ * link ownership to the host so the IPU can start receiving sensor frames.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int __maybe_unused cvs_runtime_resume(struct device *dev)
+{
+ struct icvs *ctx = dev_get_drvdata(dev);
+
+ return cvs_set_link_owner(ctx, ICVS_CSI_LINK_HOST);
+}
+
+/**
+ * cvs_runtime_suspend - Runtime PM suspend: release CSI-2 link ownership
+ * @dev: Device
+ *
+ * Called when the streaming reference is dropped by cvs_csi_disable_streams
+ * via pm_runtime_put_autosuspend. Returns CSI-2 link ownership to CVS firmware.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int __maybe_unused cvs_runtime_suspend(struct device *dev)
+{
+ struct icvs *ctx = dev_get_drvdata(dev);
+
+ return cvs_set_link_owner(ctx, ICVS_CSI_LINK_CVS);
+}
+
+static const struct dev_pm_ops __maybe_unused cvs_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cvs_suspend, cvs_resume)
+ SET_RUNTIME_PM_OPS(cvs_runtime_suspend, cvs_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id intel_cvs_acpi_match[] = {
+ { "INTC10DE" }, /* LNL */
+ { "INTC10E0" }, /* ARL */
+ { "INTC10E1" }, /* PTL */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, intel_cvs_acpi_match);
+
+static struct i2c_driver cvs_driver = {
+ .driver = {
+ .name = "intel_cvs",
+ .acpi_match_table = intel_cvs_acpi_match,
+ .pm = pm_ptr(&cvs_pm_ops),
+ },
+ .probe = cvs_probe,
+ .remove = cvs_remove,
+};
+
+static int cvs_platform_probe(struct platform_device *pdev)
+{
+ return cvs_core_probe(&pdev->dev, NULL);
+}
+
+static void cvs_platform_remove(struct platform_device *pdev)
+{
+ cvs_core_remove(&pdev->dev);
+}
+
+/*
+ * Platform driver structure.
+ *
+ * Some platforms may instantiate the CVS device as a platform device
+ * without I2C support. This driver binding allows such platforms to use the
+ * CVS core functionality (GPIOs, CSI sub-device) without I2C.
+ */
+static struct platform_driver cvs_platform_driver = {
+ .driver = {
+ .name = "cvs_platform",
+ .acpi_match_table = intel_cvs_acpi_match,
+ .pm = pm_ptr(&cvs_pm_ops),
+ },
+ .probe = cvs_platform_probe,
+ .remove = cvs_platform_remove,
+};
+
+/**
+ * cvs_init - Module init registering I2C and platform drivers
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int __init cvs_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&cvs_driver);
+ if (ret)
+ return ret;
+
+ ret = platform_driver_register(&cvs_platform_driver);
+ if (ret) {
+ i2c_del_driver(&cvs_driver);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * cvs_exit - Module exit unregistering drivers
+ */
+static void __exit cvs_exit(void)
+{
+ platform_driver_unregister(&cvs_platform_driver);
+ i2c_del_driver(&cvs_driver);
+}
+module_init(cvs_init);
+module_exit(cvs_exit);
+
+MODULE_IMPORT_NS("INTEL_IPU_BRIDGE");
+MODULE_AUTHOR("Miguel Vadillo <miguel.vadillo@intel.com>");
+MODULE_DESCRIPTION("Intel Vision Sensing Controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/cvs/icvs.h b/drivers/media/i2c/cvs/icvs.h
new file mode 100644
index 000000000000..cfa8ef5d975c
--- /dev/null
+++ b/drivers/media/i2c/cvs/icvs.h
@@ -0,0 +1,495 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2026 Intel Corporation
+ */
+
+#ifndef _ICVS_H
+#define _ICVS_H
+
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+
+/*
+ * PCI device IDs for all IPU generations that co-exist with the CVS bridge.
+ * IPU7 (0x645d) is shared by MTL and LNL; IPU7P5 (0xb05d) is shared by
+ * ARL and PTL. These are not yet in the shared ipu6-pci-table so they are
+ * listed here alongside the IPU6 family for probe-time IPU discovery.
+ */
+struct gpio_desc;
+struct i2c_client;
+
+/*
+ * GPIO resource counts (ACPI enumerated).
+ *
+ * 4-GPIO configuration has a wake IRQ and supports asynchronous messaging.
+ * 2-GPIO configuration has no IRQ, so communication is synchronous only.
+ */
+#define ICVS_GPIO_ASYNC 4
+#define ICVS_GPIO_SYNC 2
+
+/* Firmware response prefix (optional, for protocol revision 2.x or newer) */
+#define ICVS_PREFIX_VAL 0xCAFEB0BA
+
+/*
+ * CSI bridge sub-device definitions
+ */
+/**
+ * enum icvs_csi_cmd_id - Low-level CSI bridge command identifiers
+ *
+ * These numeric IDs are part of the legacy CSI-side protocol not mapped
+ * directly to the higher level firmware opcodes in enum icvs_command. Only
+ * a minimal subset is presently issued/handled by the driver. Others are
+ * reserved for future expansion of the CSI bridge feature set.
+ *
+ * @ICVS_CSI_SET_OWNER: Set CSI sensor ownership between host and CVS
+ * @ICVS_CSI_SET_CONF: Apply CSI link configuration parameters
+ * @ICVS_CSI_PRIVACY_NOTIF: Notify host of a privacy state transition
+ */
+enum icvs_csi_cmd_id {
+ ICVS_CSI_SET_OWNER = 0,
+ ICVS_CSI_SET_CONF = 2,
+ ICVS_CSI_PRIVACY_NOTIF = 6,
+};
+
+/**
+ * enum icvs_csi_link_owner - CSI-2 link / sensor ownership
+ *
+ * Ownership reflects which endpoint currently controls the attached image
+ * sensor over the CSI-2 link. Transitions may gate streaming or reconfigure
+ * link parameters. The host requests ownership changes via protocol opcodes
+ * and may need to assert GPIO signals on platforms without full capability.
+ *
+ * @ICVS_CSI_LINK_HOST: Host (Linux) owns the sensor and may start streaming
+ * @ICVS_CSI_LINK_CVS: CVS firmware owns the sensor (host must not stream)
+ */
+enum icvs_csi_link_owner {
+ ICVS_CSI_LINK_HOST,
+ ICVS_CSI_LINK_CVS,
+};
+
+/**
+ * enum icvs_csi_privacy_status - Reported privacy state
+ *
+ * Reflects user privacy (e.g. camera LED assertion and stream gating). The
+ * MAX value is a sentinel used for bounds checking and is not a real state.
+ *
+ * @ICVS_CSI_PRIVACY_OFF: Privacy not asserted (LED off, streaming permitted)
+ * @ICVS_CSI_PRIVACY_ON: Privacy asserted (LED on and/or stream gated)
+ * @ICVS_CSI_PRIVACY_MAX: Sentinel; not a valid operational value
+ */
+enum icvs_csi_privacy_status {
+ ICVS_CSI_PRIVACY_OFF,
+ ICVS_CSI_PRIVACY_ON,
+ ICVS_CSI_PRIVACY_MAX
+};
+
+/**
+ * enum icvs_csi_pads - Media pads exposed by the CVS sub-device
+ *
+ * The bridge presents a single sink (from the remote sensor) and a single
+ * source (toward the rest of the media graph / consumers). NUM_PADS is used
+ * for sizing arrays and iteration; it is not a real pad index.
+ *
+ * @ICVS_CSI_PAD_SINK: Sink pad receiving frames from remote sensor
+ * @ICVS_CSI_PAD_SOURCE: Source pad emitting frames to downstream entities
+ * @ICVS_CSI_NUM_PADS: Count sentinel (array size / iteration bound)
+ */
+enum icvs_csi_pads {
+ ICVS_CSI_PAD_SINK,
+ ICVS_CSI_PAD_SOURCE,
+ ICVS_CSI_NUM_PADS
+};
+
+/*
+ * Core driver structures and functions used by both I2C and platform modes
+ */
+
+/**
+ * DOC: CVS device quirk flags
+ *
+ * These bit flag macros describe per-device behavioral adjustments applied
+ * after VID/PID matching (see cvs_i2c_check() and cvs_apply_quirks()). They
+ * allow the driver to selectively alter logic paths for firmware / hardware
+ * variants without introducing hard-coded conditionals at each call site.
+ *
+ * @ICVS_NO_MIPI_CONFIG: Device firmware performs its own MIPI link setup;
+ * skip sending HOST_SET_MIPI_CONFIG.
+ * @ICVS_SKIP_FW_RESET: Skip issuing a post firmware-update reset sequence.
+ * @ICVS_NO_CAPS: Device firmware does not support GET_DEV_CAPABILITY;
+ * skip capability query and treat caps as unsupported.
+ * @ICVS_FW_BUF_SIZE_256: Firmware expects chunk transfer buffer size of 256
+ * bytes (override defaults if they differ).
+ * @ICVS_FW_HEADER_SIZE_256: Firmware header size fixed at 256 bytes offset
+ * for start of payload data.
+ * @ICVS_HOST_SENSOR_PWR_CTRL: Host must control sensor power sequencing.
+ * @ICVS_HOST_PRIV_CTRL: Host owns privacy LED gating.
+ * @ICVS_HOST_VISION_SENSING: Host enables vision sensing capability bit
+ * @ICVS_NO_FW_UPDATE: Device does not support firmware update.
+ */
+#define ICVS_NO_MIPI_CONFIG BIT(0)
+#define ICVS_SKIP_FW_RESET BIT(1)
+#define ICVS_NO_CAPS BIT(2)
+#define ICVS_FW_BUF_SIZE_256 BIT(3)
+#define ICVS_FW_HEADER_SIZE_256 BIT(4)
+#define ICVS_HOST_SENSOR_PWR_CTRL BIT(5)
+#define ICVS_HOST_PRIV_CTRL BIT(6)
+#define ICVS_HOST_VISION_SENSING BIT(7)
+#define ICVS_NO_FW_UPDATE BIT(8)
+
+/**
+ * struct icvs_device_quirk - Device-specific quirk entry
+ * @vid: Vendor ID
+ * @pid: Product ID
+ * @quirks: Quirk flags for this device
+ */
+struct icvs_device_quirk {
+ u16 vid;
+ u16 pid;
+ unsigned long quirks;
+};
+
+/**
+ * struct icvs_dt_config - Data type configuration for a virtual channel
+ * @pixel_width: Pixel width in pixels
+ * @pixel_height: Pixel height in pixels
+ * @data_type: MIPI CSI-2 data type (RAW10, RAW12, etc.)
+ * @reserved: Reserved for future use
+ */
+struct icvs_dt_config {
+ u16 pixel_width;
+ u16 pixel_height;
+ u8 data_type;
+ u8 reserved[3];
+};
+
+/**
+ * struct icvs_vc_config - Virtual channel configuration
+ * @vc: Virtual channel index (0-31)
+ * @dt_count: Number of data types configured
+ * @dt_configs: Array of data type configurations (up to 4)
+ * @reserved: Reserved for future use
+ */
+struct icvs_vc_config {
+ u8 vc;
+ u8 dt_count;
+ struct icvs_dt_config dt_configs[4];
+ u8 reserved[6];
+};
+
+/**
+ * struct icvs_link_cfg - Host to CVS CSI link configuration
+ * @fps: Frames per second
+ * @nr_of_lanes: Number of CSI-2 data lanes used
+ * @phy_mode: 0 = DPHY, 1 = CPHY
+ * @vc_count: Number of virtual channels enabled
+ * @vc_configs: Per-VC configuration
+ * @link_freq: Link frequency in Hz
+ * @reserved: Reserved for future use
+ */
+struct icvs_link_cfg {
+ u8 fps;
+ u8 nr_of_lanes;
+ u8 phy_mode;
+ u8 vc_count;
+ struct icvs_vc_config vc_configs[4];
+ u32 link_freq;
+ u8 reserved[8];
+};
+
+/**
+ * struct icvs_fw_version - Firmware version tuple
+ * @major: Major version
+ * @minor: Minor version
+ * @hotfix: Hotfix/patch level
+ * @build: Build number
+ */
+struct icvs_fw_version {
+ u32 major;
+ u32 minor;
+ u32 hotfix;
+ u32 build;
+};
+
+/**
+ * struct icvs_vid_pid - Device vendor/product IDs
+ * @v_id: Vendor ID
+ * @p_id: Product ID
+ */
+struct icvs_vid_pid {
+ u16 v_id;
+ u16 p_id;
+};
+
+/**
+ * struct icvs_mipi_data_packet - Encapsulated MIPI link configuration packet
+ * @cmd_id: Command identifier
+ * @size: Payload size (bytes)
+ * @crc: Checksum over payload
+ * @conf: CSI link configuration
+ * @reserved: Reserved for future use
+ */
+struct icvs_mipi_data_packet {
+ __be16 cmd_id;
+ u32 size;
+ u32 crc;
+ struct icvs_link_cfg conf;
+ u8 reserved[70];
+} __packed;
+
+/**
+ * struct icvs_mipi_read_packet - Read-back MIPI configuration
+ * @size: Payload size
+ * @crc: Payload checksum
+ * @conf: CSI link configuration
+ */
+struct icvs_mipi_read_packet {
+ u32 size;
+ u32 crc;
+ struct icvs_link_cfg conf;
+};
+
+/* Host identifier bitfield masks */
+#define ICVS_HOST_ID_RGBCAMERA_PWRUP BIT(31)
+#define ICVS_HOST_ID_PRIVACY_LED BIT(30)
+#define ICVS_HOST_ID_DEVICE_POWER GENMASK(29, 28)
+#define ICVS_HOST_ID_VISION_SENSING BIT(27)
+
+/* Device state bitfield masks (u8) */
+#define ICVS_DEV_STATE_PRIVACY BIT(0)
+#define ICVS_DEV_STATE_ON BIT(1)
+#define ICVS_DEV_STATE_SENSOR_OWNER BIT(2)
+#define ICVS_DEV_STATE_DOWNLOAD BIT(4)
+#define ICVS_DEV_STATE_ERROR BIT(6)
+#define ICVS_DEV_STATE_BUSY BIT(7)
+
+/* Device capability bitfield masks (u16) */
+#define ICVS_CAP_HOST_MIPI_REQUIRED BIT(15)
+#define ICVS_CAP_FW_ANTIROLLBACK BIT(14)
+#define ICVS_CAP_PRIVACY2VISIONDRIVER BIT(13)
+#define ICVS_CAP_FWUPDATE_RESET_HOST BIT(12)
+#define ICVS_CAP_NO_CAMERA_FWUPDATE BIT(11)
+#define ICVS_CAP_POWER_DOMAIN_SUPPORT BIT(10)
+#define ICVS_CAP_FW_FLASHED_IN_PLACE BIT(9)
+#define ICVS_CAP_IO_CONTEXT_HOT BIT(8)
+
+/**
+ * struct icvs_dev_capabilities - Protocol capabilities reported by device
+ * @protocol_version_major: Major protocol version
+ * @protocol_version_minor: Minor protocol version
+ * @capability: Capability bitfield - use ICVS_CAP_* masks
+ * @max_packet_time: Max packet processing time (ms)
+ * @max_post_dl_time: Max post-download time (s)
+ */
+struct icvs_dev_capabilities {
+ u8 protocol_version_major;
+ u8 protocol_version_minor;
+ u16 capability;
+ u16 max_packet_time;
+ u16 max_post_dl_time;
+};
+
+/**
+ * struct icvs_cmd - Generic command container
+ * @cmd_id: Command identifier
+ * @param: Parameter union providing multiple variants
+ * @param.param: Raw parameter
+ * @param.host_id: Host identifier (ICVS_SET_DEV_HOST_ID) - use
+ * ICVS_HOST_ID_* masks
+ * @param.conf: CSI link configuration (ICVS_HOST_SET_MIPI_CONFIG)
+ */
+struct icvs_cmd {
+ __be16 cmd_id;
+ union {
+ u32 param;
+ u32 host_id;
+ struct icvs_link_cfg conf;
+ } param;
+} __packed;
+
+/**
+ * struct icvs_resp - Firmware response container
+ * @status: Internal status code
+ * @cmd_id: Original command identifier
+ * @resp: Response union containing variant payload
+ * @resp.state: Device state response (u8, use ICVS_DEV_STATE_* masks)
+ * @resp.cap: Capability response
+ * @resp.conf: Link configuration response
+ * @resp.mipi_read: Raw link config read-back
+ * @resp.vid_pid: Vendor/product identifiers
+ * @resp.fw_version: Firmware version tuple
+ */
+struct icvs_resp {
+ u32 status;
+ __be16 cmd_id;
+ union {
+ u8 state;
+ struct icvs_dev_capabilities cap;
+ struct icvs_link_cfg conf;
+ struct icvs_mipi_read_packet mipi_read;
+ struct icvs_vid_pid vid_pid;
+ struct icvs_fw_version fw_version;
+ } resp;
+};
+
+/**
+ * enum icvs_resources - Device capability / resource category
+ *
+ * Categorizes hardware resource availability which influences protocol
+ * features (e.g. reset control, wake IRQ, GPIO mediated ownership). Light
+ * capability devices expose a reduced set of control GPIOs; full capability
+ * devices provide all optional signals and features. NOTSUP represents an
+ * unsupported or uninitialized state.
+ *
+ * @ICVS_NOTSUP: Capability not supported / not yet determined
+ * @ICVS_LIGHTCAP: Light capability (limited GPIO / no dedicated wake IRQ)
+ * @ICVS_FULLCAP: Full capability (reset GPIO, wake IRQ, extended protocol)
+ */
+enum icvs_resources {
+ ICVS_NOTSUP,
+ ICVS_LIGHTCAP,
+ ICVS_FULLCAP,
+};
+
+/**
+ * enum icvs_command - Protocol command opcodes (firmware space 0x0800+)
+ * @ICVS_GET_DEV_STATE: Query current device state bitfield
+ * @ICVS_GET_DEV_FW_VERSION: Retrieve firmware version tuple
+ * @ICVS_GET_DEV_VID_PID: Read vendor / product identifiers
+ * @ICVS_GET_DEV_ERR_CODE: Fetch last error code (if any)
+ * @ICVS_GET_DEV_CAPABILITY: Read protocol capability structure
+ * @ICVS_SET_DEV_HOST_ID: Set host identity / ownership bits
+ * @ICVS_GET_DEV_HOST_ID: Read back host identity
+ * @ICVS_FW_LOADER_START: Begin firmware download sequence
+ * @ICVS_FW_LOADER_DATA: Stream a chunk of firmware payload
+ * @ICVS_FW_LOADER_END: End firmware download / trigger flash
+ * @ICVS_HOST_GET_MIPI_CONFIG: Request current MIPI CSI link configuration
+ * @ICVS_HOST_SET_MIPI_CONFIG: Apply new MIPI CSI link configuration
+ * @ICVS_HOST_SENSOR_OWNER: Toggle CSI sensor ownership (GPIO assist)
+ */
+enum icvs_command {
+ ICVS_GET_DEV_STATE = 0x0800,
+ ICVS_GET_DEV_FW_VERSION = 0x0801,
+ ICVS_GET_DEV_VID_PID = 0x0802,
+ ICVS_GET_DEV_ERR_CODE = 0x0803,
+ ICVS_GET_DEV_CAPABILITY = 0x0804,
+ ICVS_SET_DEV_HOST_ID = 0x0805,
+ ICVS_GET_DEV_HOST_ID = 0x0806,
+ ICVS_FW_LOADER_START = 0x0820,
+ ICVS_FW_LOADER_DATA = 0x0821,
+ ICVS_FW_LOADER_END = 0x0822,
+ ICVS_HOST_GET_MIPI_CONFIG = 0x082F,
+ ICVS_HOST_SET_MIPI_CONFIG = 0x0830,
+ ICVS_HOST_SENSOR_OWNER = 0x0831,
+};
+
+/**
+ * enum icvs_state - Device state bitfield flags
+ *
+ * These flags correspond to bits in the device state byte returned via
+ * GET_DEV_STATE and decoded in union icvs_dev_state. Multiple bits may be
+ * asserted simultaneously. Reserved bits are omitted.
+ *
+ * @ICVS_DEVICE_OFF_STATE: Raw zero value; device is powered off or
+ * not yet ready
+ * @ICVS_DEVICE_PRIVACY_ON: Privacy mode active (LED asserted and/or
+ * stream gated)
+ * @ICVS_DEVICE_ON_STATE: Device powered and responsive to protocol commands
+ * @ICVS_DEVICE_SENSOR_OWNER: CVS currently owns the attached CSI sensor
+ * @ICVS_DEVICE_DWNLD_STATE: Firmware download / flash operation in progress
+ * @ICVS_DEVICE_ERROR_STATE: Device has reported an error (query
+ * ICVS_GET_DEV_ERR_CODE)
+ * @ICVS_DEVICE_BUSY_STATE: Device is busy processing a prior command
+ */
+enum icvs_state {
+ ICVS_DEVICE_OFF_STATE = 0x00,
+ ICVS_DEVICE_PRIVACY_ON = BIT(0),
+ ICVS_DEVICE_ON_STATE = BIT(1),
+ ICVS_DEVICE_SENSOR_OWNER = BIT(2),
+ ICVS_DEVICE_DWNLD_STATE = BIT(4),
+ ICVS_DEVICE_ERROR_STATE = BIT(6),
+ ICVS_DEVICE_BUSY_STATE = BIT(7),
+};
+
+/**
+ * struct icvs - Core CVS device context
+ * @i2c_client: I2C client (NULL in platform-only mode)
+ * @work: Delayed work for polling device state / completion
+ * @wq_resp: Last response container populated by workqueue
+ * @cmd_completion: Completion for command waiters
+ * @lock: Mutex protecting command submission & shared state
+ * @subdev: V4L2 sub-device representing the CSI bridge entity
+ * @remote: Remote media pad connected to upstream camera sensor
+ * @notifier: Async notifier for remote sensor discovery
+ * @ctrl_handler: V4L2 control handler
+ * @freq_ctrl: (future) frequency control pointer
+ * @pads: Local media pads (sink/source)
+ * @nr_of_lanes: Active CSI-2 lane count
+ * @link_freq: Current link frequency (Hz)
+ * @ipu_link: PM runtime device link (IPU consumer, CVS supplier)
+ * @res: Resource capability (light/full)
+ * @caps: Reported device protocol capabilities
+ * @prefix: Firmware response prefix present
+ * @quirks: Device-specific quirk flags
+ * @rst: Reset GPIO (full capability only)
+ * @req: Request GPIO (ownership signaling)
+ * @resp: Response GPIO (ownership signaling)
+ * @irq: Wake IRQ (full capability)
+ * @hostwake_event: Waitqueue for wake events
+ * @hostwake_event_arg: Wake event flag
+ */
+struct icvs {
+ struct i2c_client *i2c_client;
+ struct delayed_work work;
+ struct icvs_resp wq_resp;
+ struct completion cmd_completion;
+ struct mutex lock; /* Protects command execution and device state */
+ struct v4l2_subdev subdev;
+ struct media_pad *remote;
+ struct v4l2_async_notifier notifier;
+ struct v4l2_ctrl_handler ctrl_handler;
+ struct v4l2_ctrl *freq_ctrl;
+ struct media_pad pads[ICVS_CSI_NUM_PADS];
+ u32 nr_of_lanes;
+ u64 link_freq;
+ struct device_link *ipu_link;
+ enum icvs_resources res;
+ struct icvs_dev_capabilities caps;
+ bool prefix;
+ unsigned long quirks;
+ struct gpio_desc *rst;
+ struct gpio_desc *req;
+ struct gpio_desc *resp;
+ int irq;
+ wait_queue_head_t hostwake_event;
+ bool hostwake_event_arg;
+};
+
+/**
+ * cvs_dev - Helper returning the struct device for a CVS context
+ * @ctx: CVS context
+ *
+ * Avoids repeating transport conditional logic at each call site when
+ * acquiring the device pointer for logging or PM operations.
+ *
+ * Return: Device pointer (never NULL if @ctx is valid).
+ */
+static inline struct device *cvs_dev(struct icvs *ctx)
+{
+ return ctx->i2c_client ? &ctx->i2c_client->dev : ctx->subdev.dev;
+}
+
+/* Cross-unit interfaces */
+int cvs_send(struct icvs *ctx, struct icvs_cmd *cmd, size_t len);
+int cvs_set_link_owner(struct icvs *ctx, enum icvs_csi_link_owner owner);
+int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c);
+void cvs_csi_remove(struct icvs *ctx);
+
+#endif /* _ICVS_H */
diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c
new file mode 100644
index 000000000000..3a1ec0059ef7
--- /dev/null
+++ b/drivers/media/i2c/cvs/v4l2.c
@@ -0,0 +1,618 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Intel Corporation
+ * CVS driver - CSI/V4L2 subdev support
+ */
+
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+
+#include <media/v4l2-async.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+#include "icvs.h"
+
+/*
+ * Helpers
+ */
+static inline struct icvs *notifier_to_csi(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct icvs, notifier);
+}
+
+static inline struct icvs *sd_to_csi(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct icvs, subdev);
+}
+
+/*
+ * Default formats
+ */
+static const struct v4l2_mbus_framefmt cvs_csi_format_mbus_default = {
+ .width = 1,
+ .height = 1,
+ .code = MEDIA_BUS_FMT_Y8_1X8,
+ .field = V4L2_FIELD_NONE,
+};
+
+/**
+ * csi_set_link_cfg - Program default CSI-2 link parameters
+ * @ctx: CVS device context
+ *
+ * Populates a HOST_SET_MIPI_CONFIG command using current lane count and
+ * link frequency, then submits it to the device.
+ * Rest of the link parameters are left at firmware defaults.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int csi_set_link_cfg(struct icvs *ctx)
+{
+ struct icvs_cmd cmd = {
+ .cmd_id = cpu_to_be16(ICVS_HOST_SET_MIPI_CONFIG),
+ .param.conf.nr_of_lanes = ctx->nr_of_lanes,
+ .param.conf.link_freq = ctx->link_freq,
+ };
+ size_t cmd_size = sizeof(cmd.cmd_id) + sizeof(cmd.param.conf);
+
+ guard(mutex)(&ctx->lock);
+ return cvs_send(ctx, &cmd, cmd_size);
+}
+
+/*
+ * Streaming
+ */
+
+/**
+ * cvs_csi_enable_streams - Start streaming through the bridge
+ * @sd: Sub-device pointer
+ * @state: Active state
+ * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE)
+ * @streams_mask: Streams to enable (bit 0 supported)
+ *
+ * Runtime-resumes the bridge (triggering cvs_runtime_resume() to claim CSI-2
+ * link ownership), fetches the link frequency, programs the MIPI configuration,
+ * and forwards the enable request downstream.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_csi_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct icvs *ctx = sd_to_csi(sd);
+ struct v4l2_subdev *remote_sd =
+ media_entity_to_v4l2_subdev(ctx->remote->entity);
+ struct device *dev = cvs_dev(ctx);
+ s64 freq;
+ int ret;
+
+ /* cvs_set_link_owner(ICVS_CSI_LINK_HOST) */
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ freq = v4l2_get_link_freq(ctx->remote, 0, 0);
+ if (freq < 0) {
+ ret = freq;
+ goto err_rpm_put;
+ }
+ ctx->link_freq = freq;
+
+ if (ctx->i2c_client) {
+ ret = csi_set_link_cfg(ctx);
+ if (ret < 0)
+ goto err_rpm_put_sync;
+ }
+
+ ret = v4l2_subdev_enable_streams(remote_sd,
+ ctx->remote->index,
+ streams_mask);
+ if (ret)
+ goto err_rpm_put_sync;
+
+ return 0;
+
+err_rpm_put_sync:
+ /* Bypass autosuspend to immediately release ownership on error */
+ pm_runtime_put_sync(dev);
+ return ret;
+err_rpm_put:
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+/**
+ * cvs_csi_disable_streams - Stop streaming through the bridge
+ * @sd: Sub-device pointer
+ * @state: Active state
+ * @pad: Pad identifier (must be ICVS_CSI_PAD_SOURCE)
+ * @streams_mask: Streams to disable (bit 0 supported)
+ *
+ * Disables the remote sensor stream then drops the PM reference acquired
+ * during enable. After the autosuspend delay, cvs_runtime_suspend() will
+ * return CSI-2 link ownership to CVS firmware.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_csi_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct icvs *ctx = sd_to_csi(sd);
+ struct v4l2_subdev *remote_sd =
+ media_entity_to_v4l2_subdev(ctx->remote->entity);
+ struct device *dev = cvs_dev(ctx);
+ int ret;
+
+ ret = v4l2_subdev_disable_streams(remote_sd,
+ ctx->remote->index,
+ streams_mask);
+ if (ret)
+ dev_err(dev, "disable streams failed: %d\n", ret);
+
+ /* cvs_set_link_owner(ICVS_CSI_LINK_CVS) */
+ pm_runtime_put_autosuspend(dev);
+
+ return ret;
+}
+
+/*
+ * Pad operations / formats
+ */
+/**
+ * cvs_csi_init_state - Initialize pad formats in subdev state
+ * @sd: Sub-device
+ * @state: State container
+ *
+ * Sets all pad formats to a minimal 1x1 default.
+ *
+ * Return: 0.
+ */
+static int cvs_csi_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ for (unsigned int i = 0; i < sd->entity.num_pads; i++)
+ *v4l2_subdev_state_get_format(state, i) =
+ cvs_csi_format_mbus_default;
+
+ return 0;
+}
+
+/**
+ * cvs_csi_set_fmt - Negotiate pad format
+ * @sd: Sub-device
+ * @state: State
+ * @format: Desired / returned format
+ *
+ * Mirrors sink format onto source pad. Accepts many media bus codes, falling
+ * back to Y8 if unsupported. Normalizes field setting.
+ *
+ * Return: 0.
+ */
+static int cvs_csi_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *src =
+ v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SOURCE);
+ struct v4l2_mbus_framefmt *sink =
+ v4l2_subdev_state_get_format(state, ICVS_CSI_PAD_SINK);
+
+ if (format->pad == ICVS_CSI_PAD_SOURCE) { /* source pad mirrors sink */
+ *src = *sink;
+ return 0;
+ }
+
+ v4l_bound_align_image(&format->format.width, 1, 65536, 0,
+ &format->format.height, 1, 65536, 0, 0);
+
+ switch (format->format.code) {
+ /* Accept a large list; default fallback to Y8 */
+ case MEDIA_BUS_FMT_RGB444_1X12:
+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE:
+ case MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE:
+ case MEDIA_BUS_FMT_RGB565_1X16:
+ case MEDIA_BUS_FMT_BGR565_2X8_BE:
+ case MEDIA_BUS_FMT_BGR565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB565_2X8_BE:
+ case MEDIA_BUS_FMT_RGB565_2X8_LE:
+ case MEDIA_BUS_FMT_RGB666_1X18:
+ case MEDIA_BUS_FMT_RBG888_1X24:
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ case MEDIA_BUS_FMT_BGR888_1X24:
+ case MEDIA_BUS_FMT_GBR888_1X24:
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB888_2X12_BE:
+ case MEDIA_BUS_FMT_RGB888_2X12_LE:
+ case MEDIA_BUS_FMT_ARGB8888_1X32:
+ case MEDIA_BUS_FMT_RGB888_1X32_PADHI:
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_RGB121212_1X36:
+ case MEDIA_BUS_FMT_RGB161616_1X48:
+ case MEDIA_BUS_FMT_Y8_1X8:
+ case MEDIA_BUS_FMT_UV8_1X8:
+ case MEDIA_BUS_FMT_UYVY8_1_5X8:
+ case MEDIA_BUS_FMT_VYUY8_1_5X8:
+ case MEDIA_BUS_FMT_YUYV8_1_5X8:
+ case MEDIA_BUS_FMT_YVYU8_1_5X8:
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ case MEDIA_BUS_FMT_Y10_1X10:
+ case MEDIA_BUS_FMT_UYVY10_2X10:
+ case MEDIA_BUS_FMT_VYUY10_2X10:
+ case MEDIA_BUS_FMT_YUYV10_2X10:
+ case MEDIA_BUS_FMT_YVYU10_2X10:
+ case MEDIA_BUS_FMT_Y12_1X12:
+ case MEDIA_BUS_FMT_UYVY12_2X12:
+ case MEDIA_BUS_FMT_VYUY12_2X12:
+ case MEDIA_BUS_FMT_YUYV12_2X12:
+ case MEDIA_BUS_FMT_YVYU12_2X12:
+ case MEDIA_BUS_FMT_UYVY8_1X16:
+ case MEDIA_BUS_FMT_VYUY8_1X16:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ case MEDIA_BUS_FMT_YVYU8_1X16:
+ case MEDIA_BUS_FMT_YDYUYDYV8_1X16:
+ case MEDIA_BUS_FMT_UYVY10_1X20:
+ case MEDIA_BUS_FMT_VYUY10_1X20:
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ case MEDIA_BUS_FMT_YVYU10_1X20:
+ case MEDIA_BUS_FMT_VUY8_1X24:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ case MEDIA_BUS_FMT_UYVY12_1X24:
+ case MEDIA_BUS_FMT_VYUY12_1X24:
+ case MEDIA_BUS_FMT_YUYV12_1X24:
+ case MEDIA_BUS_FMT_YVYU12_1X24:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_AYUV8_1X32:
+ case MEDIA_BUS_FMT_UYYVYY12_0_5X36:
+ case MEDIA_BUS_FMT_YUV12_1X36:
+ case MEDIA_BUS_FMT_YUV16_1X48:
+ case MEDIA_BUS_FMT_UYYVYY16_0_5X48:
+ case MEDIA_BUS_FMT_JPEG_1X8:
+ case MEDIA_BUS_FMT_AHSV8888_1X32:
+ case MEDIA_BUS_FMT_SBGGR8_1X8:
+ case MEDIA_BUS_FMT_SGBRG8_1X8:
+ case MEDIA_BUS_FMT_SGRBG8_1X8:
+ case MEDIA_BUS_FMT_SRGGB8_1X8:
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ case MEDIA_BUS_FMT_SGBRG10_1X10:
+ case MEDIA_BUS_FMT_SGRBG10_1X10:
+ case MEDIA_BUS_FMT_SRGGB10_1X10:
+ case MEDIA_BUS_FMT_SBGGR12_1X12:
+ case MEDIA_BUS_FMT_SGBRG12_1X12:
+ case MEDIA_BUS_FMT_SGRBG12_1X12:
+ case MEDIA_BUS_FMT_SRGGB12_1X12:
+ case MEDIA_BUS_FMT_SBGGR14_1X14:
+ case MEDIA_BUS_FMT_SGBRG14_1X14:
+ case MEDIA_BUS_FMT_SGRBG14_1X14:
+ case MEDIA_BUS_FMT_SRGGB14_1X14:
+ case MEDIA_BUS_FMT_SBGGR16_1X16:
+ case MEDIA_BUS_FMT_SGBRG16_1X16:
+ case MEDIA_BUS_FMT_SGRBG16_1X16:
+ case MEDIA_BUS_FMT_SRGGB16_1X16:
+ break;
+ default:
+ format->format.code = MEDIA_BUS_FMT_Y8_1X8;
+ break;
+ }
+
+ if (format->format.field == V4L2_FIELD_ANY)
+ format->format.field = V4L2_FIELD_NONE;
+
+ *sink = format->format;
+ *src = *sink;
+
+ return 0;
+}
+
+/**
+ * cvs_csi_get_mbus_config - Provide current CSI-2 bus configuration
+ * @sd: Sub-device
+ * @pad: Pad index
+ * @cfg: Returned bus config
+ *
+ * Fills lane ordering and number of lanes; retrieves link frequency from
+ * remote entity.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_csi_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_config *cfg)
+{
+ struct icvs *ctx = sd_to_csi(sd);
+ s64 freq;
+
+ cfg->type = V4L2_MBUS_CSI2_DPHY;
+ for (unsigned int i = 0; i < V4L2_MBUS_CSI2_MAX_DATA_LANES; i++)
+ cfg->bus.mipi_csi2.data_lanes[i] = i + 1;
+ cfg->bus.mipi_csi2.num_data_lanes = ctx->nr_of_lanes;
+
+ freq = v4l2_get_link_freq(ctx->remote, 0, 0);
+ if (freq < 0)
+ return -EINVAL;
+
+ ctx->link_freq = freq;
+ cfg->link_freq = freq;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops cvs_csi_subdev_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops cvs_csi_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops cvs_csi_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = cvs_csi_set_fmt,
+ .get_mbus_config = cvs_csi_get_mbus_config,
+ .enable_streams = cvs_csi_enable_streams,
+ .disable_streams = cvs_csi_disable_streams,
+};
+
+static const struct v4l2_subdev_ops cvs_csi_subdev_ops = {
+ .core = &cvs_csi_subdev_core_ops,
+ .video = &cvs_csi_video_ops,
+ .pad = &cvs_csi_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops cvs_csi_internal_ops = {
+ .init_state = cvs_csi_init_state,
+};
+
+static const struct media_entity_operations cvs_csi_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+/*
+ * Async notifier
+ */
+/**
+ * cvs_csi_notify_bound - Remote sensor bound callback
+ * @notifier: Async notifier
+ * @sd: Remote subdev
+ * @asc: Async match connection
+ *
+ * Locates the source pad of the remote sensor and creates a media link to
+ * the CVS bridge sink pad enabling it by default.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct icvs *ctx = notifier_to_csi(notifier);
+ int pad;
+
+ pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode,
+ MEDIA_PAD_FL_SOURCE);
+ if (pad < 0)
+ return pad;
+
+ ctx->remote = &sd->entity.pads[pad];
+
+ return media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity,
+ ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+}
+
+/**
+ * cvs_csi_notify_unbind - Remote sensor unbind callback
+ * @notifier: Notifier
+ * @sd: Remote subdev
+ * @asc: Connection
+ */
+static void cvs_csi_notify_unbind(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ struct icvs *ctx = notifier_to_csi(notifier);
+
+ ctx->remote = NULL;
+}
+
+static const struct v4l2_async_notifier_operations cvs_csi_notify_ops = {
+ .bound = cvs_csi_notify_bound,
+ .unbind = cvs_csi_notify_unbind,
+};
+
+/*
+ * Controls
+ */
+/**
+ * cvs_csi_init_controls - Initialize V4L2 controls
+ * @ctx: CVS context
+ *
+ * Currently sets up a read-only privacy control placeholder.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_csi_init_controls(struct icvs *ctx)
+{
+ struct v4l2_ctrl *privacy_ctrl;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1);
+
+ privacy_ctrl = v4l2_ctrl_new_std(&ctx->ctrl_handler, NULL,
+ V4L2_CID_PRIVACY, 0, 1, 1, 0);
+ if (privacy_ctrl)
+ privacy_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ if (ctx->ctrl_handler.error) {
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return ctx->ctrl_handler.error;
+ }
+
+ ctx->subdev.ctrl_handler = &ctx->ctrl_handler;
+
+ return 0;
+}
+
+/*
+ * Firmware (graph) parsing
+ */
+/**
+ * cvs_csi_parse_firmware - Parse firmware (ACPI graph) endpoints
+ * @ctx: CVS context
+ *
+ * Discovers sink and remote source endpoints, validates lane counts and
+ * registers an async notifier for the remote sensor.
+ *
+ * Return: 0 on success or negative errno.
+ */
+static int cvs_csi_parse_firmware(struct icvs *ctx)
+{
+ struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
+ struct device *dev = cvs_dev(ctx);
+ struct fwnode_handle *sink_ep, *source_ep;
+ struct v4l2_async_connection *asc;
+ int ret;
+
+ sink_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0, 0);
+ v4l2_async_subdev_nf_init(&ctx->notifier, &ctx->subdev);
+ ctx->notifier.ops = &cvs_csi_notify_ops;
+
+ ret = v4l2_fwnode_endpoint_parse(sink_ep, &ep);
+ if (ret)
+ goto err_nf_cleanup;
+
+ ctx->nr_of_lanes = ep.bus.mipi_csi2.num_data_lanes;
+ source_ep = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 1, 0, 0);
+ ret = v4l2_fwnode_endpoint_parse(source_ep, &ep);
+ fwnode_handle_put(source_ep);
+ if (ret)
+ goto err_nf_cleanup;
+
+ if (ctx->nr_of_lanes != ep.bus.mipi_csi2.num_data_lanes) {
+ ret = -EINVAL;
+ goto err_nf_cleanup;
+ }
+
+ asc = v4l2_async_nf_add_fwnode_remote(&ctx->notifier, sink_ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_nf_cleanup;
+ }
+
+ ret = v4l2_async_nf_register(&ctx->notifier);
+ if (ret)
+ goto err_nf_cleanup;
+
+ fwnode_handle_put(sink_ep);
+
+ return 0;
+
+err_nf_cleanup:
+ v4l2_async_nf_cleanup(&ctx->notifier);
+ fwnode_handle_put(sink_ep);
+
+ return ret;
+}
+
+/*
+ * Public CSI init/cleanup used by core probe/remove
+ */
+/**
+ * cvs_csi_init - Initialize CSI/V4L2 sub-device side of bridge
+ * @ctx: CVS context
+ * @dev: Parent device
+ * @i2c: I2C client (may be NULL for platform mode)
+ *
+ * Sets up sub-device, media entity pads, async notifier, controls and
+ * registers with the V4L2 framework.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int cvs_csi_init(struct icvs *ctx, struct device *dev, struct i2c_client *i2c)
+{
+ int ret;
+
+ if (i2c) {
+ v4l2_i2c_subdev_init(&ctx->subdev, i2c, &cvs_csi_subdev_ops);
+ } else {
+ v4l2_subdev_init(&ctx->subdev, &cvs_csi_subdev_ops);
+ ctx->subdev.dev = dev;
+ }
+
+ ctx->subdev.internal_ops = &cvs_csi_internal_ops;
+ v4l2_set_subdevdata(&ctx->subdev, ctx);
+ ctx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ ctx->subdev.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ ctx->subdev.entity.ops = &cvs_csi_entity_ops;
+ snprintf(ctx->subdev.name, sizeof(ctx->subdev.name), "Intel CVS");
+
+ ret = cvs_csi_parse_firmware(ctx);
+ if (ret)
+ return ret;
+
+ ret = cvs_csi_init_controls(ctx);
+ if (ret)
+ goto err_nf_unreg;
+
+ ctx->pads[ICVS_CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+ ctx->pads[ICVS_CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&ctx->subdev.entity, ICVS_CSI_NUM_PADS,
+ ctx->pads);
+ if (ret)
+ goto err_ctrl_cleanup;
+
+ ctx->subdev.state_lock = ctx->ctrl_handler.lock;
+ ret = v4l2_subdev_init_finalize(&ctx->subdev);
+ if (ret)
+ goto err_entity_cleanup;
+
+ ret = v4l2_async_register_subdev(&ctx->subdev);
+ if (ret)
+ goto err_entity_cleanup;
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&ctx->subdev.entity);
+
+err_ctrl_cleanup:
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+
+err_nf_unreg:
+ v4l2_async_nf_unregister(&ctx->notifier);
+ v4l2_async_nf_cleanup(&ctx->notifier);
+
+ return ret;
+}
+
+/**
+ * cvs_csi_remove - Cleanup CSI/V4L2 sub-device
+ * @ctx: CVS context
+ */
+void cvs_csi_remove(struct icvs *ctx)
+{
+ v4l2_async_nf_unregister(&ctx->notifier);
+ v4l2_async_nf_cleanup(&ctx->notifier);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_async_unregister_subdev(&ctx->subdev);
+ v4l2_subdev_cleanup(&ctx->subdev);
+ media_entity_cleanup(&ctx->subdev.entity);
+}
+
+MODULE_AUTHOR("Miguel Vadillo <miguel.vadillo@intel.com>");
+MODULE_DESCRIPTION("CSI/V4L2 support for Intel Vision Sensing Controller");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 69d5cc648c0f..8110d40931d9 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -3989,7 +3989,7 @@ static void cx25840_remove(struct i2c_client *client)
}
static const struct i2c_device_id cx25840_id[] = {
- { "cx25840" },
+ { .name = "cx25840" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cx25840_id);
diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c
index 49aa5f4a172c..6abb5e324ae1 100644
--- a/drivers/media/i2c/ds90ub913.c
+++ b/drivers/media/i2c/ds90ub913.c
@@ -872,8 +872,8 @@ static void ub913_remove(struct i2c_client *client)
}
static const struct i2c_device_id ub913_id[] = {
- { "ds90ub913a-q1" },
- {}
+ { .name = "ds90ub913a-q1" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ub913_id);
diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index a8ab67f4137f..d4228e1134ff 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -1347,9 +1347,9 @@ static const struct ub953_hw_data ds90ub971_hw = {
};
static const struct i2c_device_id ub953_id[] = {
- { "ds90ub953-q1", (kernel_ulong_t)&ds90ub953_hw },
- { "ds90ub971-q1", (kernel_ulong_t)&ds90ub971_hw },
- {}
+ { .name = "ds90ub953-q1", .driver_data = (kernel_ulong_t)&ds90ub953_hw },
+ { .name = "ds90ub971-q1", .driver_data = (kernel_ulong_t)&ds90ub971_hw },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ub953_id);
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index d50e977cf6ce..15a9797b47ac 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -5266,10 +5266,10 @@ static const struct ub960_hw_data ds90ub9702_hw = {
};
static const struct i2c_device_id ub960_id[] = {
- { "ds90ub954-q1", (kernel_ulong_t)&ds90ub954_hw },
- { "ds90ub960-q1", (kernel_ulong_t)&ds90ub960_hw },
- { "ds90ub9702-q1", (kernel_ulong_t)&ds90ub9702_hw },
- {}
+ { .name = "ds90ub954-q1", .driver_data = (kernel_ulong_t)&ds90ub954_hw },
+ { .name = "ds90ub960-q1", .driver_data = (kernel_ulong_t)&ds90ub960_hw },
+ { .name = "ds90ub9702-q1", .driver_data = (kernel_ulong_t)&ds90ub9702_hw },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ub960_id);
diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c
index 3288de539452..1d686b1d2fd7 100644
--- a/drivers/media/i2c/dw9714.c
+++ b/drivers/media/i2c/dw9714.c
@@ -307,7 +307,7 @@ static int __maybe_unused dw9714_vcm_resume(struct device *dev)
}
static const struct i2c_device_id dw9714_id_table[] = {
- { DW9714_NAME },
+ { .name = DW9714_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, dw9714_id_table);
diff --git a/drivers/media/i2c/dw9719.c b/drivers/media/i2c/dw9719.c
index 59558335989e..3b7ba88fd67c 100644
--- a/drivers/media/i2c/dw9719.c
+++ b/drivers/media/i2c/dw9719.c
@@ -439,6 +439,15 @@ static void dw9719_remove(struct i2c_client *client)
pm_runtime_set_suspended(&client->dev);
}
+static const struct i2c_device_id dw9719_id_table[] = {
+ { .name = "dw9718s", .driver_data = (kernel_ulong_t)DW9718S },
+ { .name = "dw9719", .driver_data = (kernel_ulong_t)DW9719 },
+ { .name = "dw9761", .driver_data = (kernel_ulong_t)DW9761 },
+ { .name = "dw9800k", .driver_data = (kernel_ulong_t)DW9800K },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dw9719_id_table);
+
static const struct of_device_id dw9719_of_table[] = {
{ .compatible = "dongwoon,dw9718s", .data = (const void *)DW9718S },
{ .compatible = "dongwoon,dw9719", .data = (const void *)DW9719 },
@@ -459,6 +468,7 @@ static struct i2c_driver dw9719_i2c_driver = {
},
.probe = dw9719_probe,
.remove = dw9719_remove,
+ .id_table = dw9719_id_table,
};
module_i2c_driver(dw9719_i2c_driver);
diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
index 50121c3e5b48..738e2801016a 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_driver.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -1485,7 +1485,7 @@ static const struct of_device_id et8ek8_of_table[] = {
MODULE_DEVICE_TABLE(of, et8ek8_of_table);
static const struct i2c_device_id et8ek8_id_table[] = {
- { ET8EK8_NAME },
+ { .name = ET8EK8_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, et8ek8_id_table);
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index 7da02ce5da15..223d3753cc93 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -72,7 +72,7 @@
/* V_TIMING internal */
#define IMX219_REG_FRM_LENGTH_A CCI_REG16(0x0160)
-#define IMX219_FLL_MAX 0xffff
+#define IMX219_FLL_MAX 0xfffe
#define IMX219_VBLANK_MIN 32
#define IMX219_REG_LINE_LENGTH_A CCI_REG16(0x0162)
#define IMX219_LLP_MIN 0x0d78
@@ -142,10 +142,10 @@
/* IMX219 native and active pixel array size. */
#define IMX219_NATIVE_WIDTH 3296U
#define IMX219_NATIVE_HEIGHT 2480U
-#define IMX219_PIXEL_ARRAY_LEFT 8U
-#define IMX219_PIXEL_ARRAY_TOP 8U
-#define IMX219_PIXEL_ARRAY_WIDTH 3280U
-#define IMX219_PIXEL_ARRAY_HEIGHT 2464U
+#define IMX219_ACTIVE_AREA_LEFT 8U
+#define IMX219_ACTIVE_AREA_TOP 8U
+#define IMX219_ACTIVE_AREA_WIDTH 3280U
+#define IMX219_ACTIVE_AREA_HEIGHT 2464U
/* Mode : resolution and related config&values */
struct imx219_mode {
@@ -675,13 +675,13 @@ static int imx219_set_framefmt(struct imx219 *imx219,
bpp = imx219_get_format_bpp(format);
cci_write(imx219->regmap, IMX219_REG_X_ADD_STA_A,
- crop->left - IMX219_PIXEL_ARRAY_LEFT, &ret);
+ crop->left - IMX219_ACTIVE_AREA_LEFT, &ret);
cci_write(imx219->regmap, IMX219_REG_X_ADD_END_A,
- crop->left - IMX219_PIXEL_ARRAY_LEFT + crop->width - 1, &ret);
+ crop->left - IMX219_ACTIVE_AREA_LEFT + crop->width - 1, &ret);
cci_write(imx219->regmap, IMX219_REG_Y_ADD_STA_A,
- crop->top - IMX219_PIXEL_ARRAY_TOP, &ret);
+ crop->top - IMX219_ACTIVE_AREA_TOP, &ret);
cci_write(imx219->regmap, IMX219_REG_Y_ADD_END_A,
- crop->top - IMX219_PIXEL_ARRAY_TOP + crop->height - 1, &ret);
+ crop->top - IMX219_ACTIVE_AREA_TOP + crop->height - 1, &ret);
imx219_get_binning(state, &bin_h, &bin_v);
cci_write(imx219->regmap, IMX219_REG_BINNING_MODE_H, bin_h, &ret);
@@ -837,11 +837,9 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
u8 bin_h, bin_v, binning;
- u32 prev_line_len;
int ret;
format = v4l2_subdev_state_get_format(state, 0);
- prev_line_len = format->width + imx219->hblank->val;
/*
* Adjust the requested format to match the closest mode. The Bayer
@@ -867,8 +865,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
* Use binning to maximize the crop rectangle size, and centre it in the
* sensor.
*/
- bin_h = min(IMX219_PIXEL_ARRAY_WIDTH / format->width, 2U);
- bin_v = min(IMX219_PIXEL_ARRAY_HEIGHT / format->height, 2U);
+ bin_h = min(IMX219_ACTIVE_AREA_WIDTH / format->width, 2U);
+ bin_v = min(IMX219_ACTIVE_AREA_HEIGHT / format->height, 2U);
/* Ensure bin_h and bin_v are same to avoid 1:2 or 2:1 stretching */
binning = min(bin_h, bin_v);
@@ -882,7 +880,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
int exposure_max;
int exposure_def;
- int hblank, llp_min;
+ int llp_min;
int pixel_rate;
/* Update limits and set FPS to default */
@@ -924,15 +922,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
llp_min - mode->width);
if (ret)
return ret;
- /*
- * Retain PPL setting from previous mode so that the
- * line time does not change on a mode change.
- * Limits have to be recomputed as the controls define
- * the blanking only, so PPL values need to have the
- * mode width subtracted.
- */
- hblank = prev_line_len - mode->width;
- ret = __v4l2_ctrl_s_ctrl(imx219->hblank, hblank);
+
+ ret = __v4l2_ctrl_s_ctrl(imx219->hblank, llp_min - mode->width);
if (ret)
return ret;
@@ -967,10 +958,10 @@ static int imx219_get_selection(struct v4l2_subdev *sd,
case V4L2_SEL_TGT_CROP_DEFAULT:
case V4L2_SEL_TGT_CROP_BOUNDS:
- sel->r.top = IMX219_PIXEL_ARRAY_TOP;
- sel->r.left = IMX219_PIXEL_ARRAY_LEFT;
- sel->r.width = IMX219_PIXEL_ARRAY_WIDTH;
- sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT;
+ sel->r.top = IMX219_ACTIVE_AREA_TOP;
+ sel->r.left = IMX219_ACTIVE_AREA_LEFT;
+ sel->r.width = IMX219_ACTIVE_AREA_WIDTH;
+ sel->r.height = IMX219_ACTIVE_AREA_HEIGHT;
return 0;
}
diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c
index 8ec78b60bea6..76da647f9cf9 100644
--- a/drivers/media/i2c/imx274.c
+++ b/drivers/media/i2c/imx274.c
@@ -182,7 +182,7 @@ struct imx274_mode {
};
/*
- * imx274 test pattern related structure
+ * imx274 test pattern related enum
*/
enum {
TEST_PATTERN_DISABLED = 0,
@@ -533,7 +533,7 @@ static const struct imx274_mode imx274_modes[] = {
/*
* struct imx274_ctrls - imx274 ctrl structure
* @handler: V4L2 ctrl handler structure
- * @exposure: Pointer to expsure ctrl structure
+ * @exposure: Pointer to exposure ctrl structure
* @gain: Pointer to gain ctrl structure
* @vflip: Pointer to vflip ctrl structure
* @test_pattern: Pointer to test pattern ctrl structure
@@ -547,7 +547,7 @@ struct imx274_ctrls {
};
/*
- * struct stim274 - imx274 device structure
+ * struct stimx274 - imx274 device structure
* @sd: V4L2 subdevice structure
* @pad: Media pad structure
* @client: Pointer to I2C client
@@ -587,9 +587,6 @@ struct stimx274 {
? rounddown((dim), (step)) \
: rounddown((dim) + (step) / 2, (step))))
-/*
- * Function declaration
- */
static int imx274_set_gain(struct stimx274 *priv, struct v4l2_ctrl *ctrl);
static int imx274_set_exposure(struct stimx274 *priv, int val);
static int imx274_set_vflip(struct stimx274 *priv, int val);
@@ -640,9 +637,9 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[])
for (next = table;; next++) {
if ((next->addr != range_start + range_count) ||
- (next->addr == IMX274_TABLE_END) ||
- (next->addr == IMX274_TABLE_WAIT_MS) ||
- (range_count == max_range_vals)) {
+ next->addr == IMX274_TABLE_END ||
+ next->addr == IMX274_TABLE_WAIT_MS ||
+ range_count == max_range_vals) {
if (range_count == 1)
err = regmap_write(regmap,
range_start, range_vals[0]);
@@ -650,8 +647,6 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[])
err = regmap_bulk_write(regmap, range_start,
&range_vals[0],
range_count);
- else
- err = 0;
if (err)
return err;
@@ -897,14 +892,6 @@ static int imx274_regulators_get(struct device *dev, struct stimx274 *imx274)
imx274->supplies);
}
-/**
- * imx274_s_ctrl - This is used to set the imx274 V4L2 controls
- * @ctrl: V4L2 control to be set
- *
- * This function is used to set the V4L2 controls for the imx274 sensor.
- *
- * Return: 0 on success, errors otherwise
- */
static int imx274_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = ctrl_to_sd(ctrl);
@@ -1059,16 +1046,6 @@ static int __imx274_change_compose(struct stimx274 *imx274,
return 0;
}
-/**
- * imx274_get_fmt - Get the pad format
- * @sd: Pointer to V4L2 Sub device structure
- * @sd_state: Pointer to sub device state structure
- * @fmt: Pointer to pad level media bus format
- *
- * This function is used to get the pad format information.
- *
- * Return: 0 on success
- */
static int imx274_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -1081,16 +1058,6 @@ static int imx274_get_fmt(struct v4l2_subdev *sd,
return 0;
}
-/**
- * imx274_set_fmt - This is used to set the pad format
- * @sd: Pointer to V4L2 Sub device structure
- * @sd_state: Pointer to sub device state information structure
- * @format: Pointer to pad level media bus format
- *
- * This function is used to set the pad format.
- *
- * Return: 0 on success
- */
static int imx274_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
@@ -1423,16 +1390,6 @@ static void imx274_load_default(struct stimx274 *priv)
priv->ctrls.test_pattern->val = TEST_PATTERN_DISABLED;
}
-/**
- * imx274_s_stream - It is used to start/stop the streaming.
- * @sd: V4L2 Sub device
- * @on: Flag (True / False)
- *
- * This function controls the start or stop of streaming for the
- * imx274 sensor.
- *
- * Return: 0 on success, errors otherwise
- */
static int imx274_s_stream(struct v4l2_subdev *sd, int on)
{
struct stimx274 *imx274 = to_imx274(sd);
@@ -1951,7 +1908,7 @@ static const struct of_device_id imx274_of_id_table[] = {
MODULE_DEVICE_TABLE(of, imx274_of_id_table);
static const struct i2c_device_id imx274_id[] = {
- { "IMX274" },
+ { .name = "IMX274" },
{ }
};
MODULE_DEVICE_TABLE(i2c, imx274_id);
@@ -1998,7 +1955,6 @@ static int imx274_probe(struct i2c_client *client)
struct device *dev = &client->dev;
int ret;
- /* initialize imx274 */
imx274 = devm_kzalloc(dev, sizeof(*imx274), GFP_KERNEL);
if (!imx274)
return -ENOMEM;
@@ -2163,7 +2119,7 @@ static const struct dev_pm_ops imx274_pm_ops = {
static struct i2c_driver imx274_i2c_driver = {
.driver = {
- .name = DRIVER_NAME,
+ .name = DRIVER_NAME,
.pm = &imx274_pm_ops,
.of_match_table = imx274_of_id_table,
},
diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c
index 9654f9268056..553a16b84f4d 100644
--- a/drivers/media/i2c/imx334.c
+++ b/drivers/media/i2c/imx334.c
@@ -566,18 +566,6 @@ static int imx334_update_exp_gain(struct imx334 *imx334, u32 exposure, u32 gain)
return ret;
}
-/**
- * imx334_set_ctrl() - Set subdevice control
- * @ctrl: pointer to v4l2_ctrl structure
- *
- * Supported controls:
- * - V4L2_CID_VBLANK
- * - cluster controls:
- * - V4L2_CID_ANALOGUE_GAIN
- * - V4L2_CID_EXPOSURE
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx334 *imx334 =
@@ -678,14 +666,6 @@ static int imx334_get_format_code(struct imx334 *imx334, u32 code)
return imx334_mbus_codes[0];
}
-/**
- * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
- * @sd: pointer to imx334 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device state
- * @code: V4L2 sub-device code enumeration need to be filled
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -698,14 +678,6 @@ static int imx334_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
-/**
- * imx334_enum_frame_size() - Enumerate V4L2 sub-device frame sizes
- * @sd: pointer to imx334 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device state
- * @fsize: V4L2 sub-device size enumeration need to be filled
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fsize)
@@ -749,14 +721,6 @@ static void imx334_fill_pad_format(struct imx334 *imx334,
fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
}
-/**
- * imx334_get_pad_format() - Get subdevice pad format
- * @sd: pointer to imx334 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device state
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_get_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -776,14 +740,6 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd,
return 0;
}
-/**
- * imx334_set_pad_format() - Set subdevice pad format
- * @sd: pointer to imx334 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device state
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -815,13 +771,6 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd,
return ret;
}
-/**
- * imx334_init_state() - Initialize sub-device state
- * @sd: pointer to imx334 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device state
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
@@ -856,15 +805,6 @@ static int imx334_set_framefmt(struct imx334 *imx334)
return -EINVAL;
}
-/**
- * imx334_enable_streams() - Enable specified streams for the sensor
- * @sd: pointer to the V4L2 subdevice
- * @state: pointer to the subdevice state
- * @pad: pad number for which streams are enabled
- * @streams_mask: bitmask specifying the streams to enable
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
@@ -929,15 +869,6 @@ err_rpm_put:
return ret;
}
-/**
- * imx334_disable_streams() - Enable specified streams for the sensor
- * @sd: pointer to the V4L2 subdevice
- * @state: pointer to the subdevice state
- * @pad: pad number for which streams are disabled
- * @streams_mask: bitmask specifying the streams to disable
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_disable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
@@ -1067,12 +998,6 @@ static const struct v4l2_subdev_internal_ops imx334_internal_ops = {
.init_state = imx334_init_state,
};
-/**
- * imx334_power_on() - Sensor power on sequence
- * @dev: pointer to i2c device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_power_on(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1101,12 +1026,6 @@ error_reset:
return ret;
}
-/**
- * imx334_power_off() - Sensor power off sequence
- * @dev: pointer to i2c device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_power_off(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1206,12 +1125,6 @@ static int imx334_init_controls(struct imx334 *imx334)
return 0;
}
-/**
- * imx334_probe() - I2C client device binding
- * @client: pointer to i2c client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx334_probe(struct i2c_client *client)
{
struct imx334 *imx334;
@@ -1311,12 +1224,6 @@ error_power_off:
return ret;
}
-/**
- * imx334_remove() - I2C client device unbinding
- * @client: pointer to I2C client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static void imx334_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c
index 5790aa4fabeb..1f777a1a8192 100644
--- a/drivers/media/i2c/imx335.c
+++ b/drivers/media/i2c/imx335.c
@@ -698,18 +698,6 @@ static int imx335_update_test_pattern(struct imx335 *imx335, u32 pattern_index)
return ret;
}
-/**
- * imx335_set_ctrl() - Set subdevice control
- * @ctrl: pointer to v4l2_ctrl structure
- *
- * Supported controls:
- * - V4L2_CID_VBLANK
- * - cluster controls:
- * - V4L2_CID_ANALOGUE_GAIN
- * - V4L2_CID_EXPOSURE
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx335 *imx335 =
@@ -800,14 +788,6 @@ static int imx335_get_format_code(struct imx335 *imx335, u32 code)
return imx335_mbus_codes[0];
}
-/**
- * imx335_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
- * @sd: pointer to imx335 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @code: V4L2 sub-device code enumeration need to be filled
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -820,14 +800,6 @@ static int imx335_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
-/**
- * imx335_enum_frame_size() - Enumerate V4L2 sub-device frame sizes
- * @sd: pointer to imx335 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fsize: V4L2 sub-device size enumeration need to be filled
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fsize)
@@ -871,14 +843,6 @@ static void imx335_fill_pad_format(struct imx335 *imx335,
fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
}
-/**
- * imx335_set_pad_format() - Set subdevice pad format
- * @sd: pointer to imx335 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -923,13 +887,6 @@ static int imx335_set_pad_format(struct v4l2_subdev *sd,
return ret;
}
-/**
- * imx335_init_state() - Initialize sub-device state
- * @sd: pointer to imx335 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
@@ -947,14 +904,6 @@ static int imx335_init_state(struct v4l2_subdev *sd,
return imx335_set_pad_format(sd, sd_state, &fmt);
}
-/**
- * imx335_get_selection() - Selection API
- * @sd: pointer to imx335 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @sel: V4L2 selection info
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
@@ -1011,15 +960,6 @@ static int imx335_set_framefmt(struct imx335 *imx335)
return ret;
}
-/**
- * imx335_enable_streams() - Enable sensor streams
- * @sd: V4L2 subdevice
- * @state: V4L2 subdevice state
- * @pad: The pad to enable
- * @streams_mask: Bitmask of streams to enable
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
@@ -1097,15 +1037,6 @@ err_rpm_put:
return ret;
}
-/**
- * imx335_disable_streams() - Disable sensor streams
- * @sd: V4L2 subdevice
- * @state: V4L2 subdevice state
- * @pad: The pad to disable
- * @streams_mask: Bitmask of streams to disable
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_disable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
@@ -1299,12 +1230,6 @@ error_reset:
return ret;
}
-/**
- * imx335_power_off() - Sensor power off sequence
- * @dev: pointer to i2c device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_power_off(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1430,12 +1355,6 @@ static int imx335_init_controls(struct imx335 *imx335)
return 0;
}
-/**
- * imx335_probe() - I2C client device binding
- * @client: pointer to i2c client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx335_probe(struct i2c_client *client)
{
struct imx335 *imx335;
@@ -1530,12 +1449,6 @@ error_power_off:
return ret;
}
-/**
- * imx335_remove() - I2C client device unbinding
- * @client: pointer to I2C client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static void imx335_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c
index e25e0a9ff65c..2705af2f16c0 100644
--- a/drivers/media/i2c/imx412.c
+++ b/drivers/media/i2c/imx412.c
@@ -570,18 +570,6 @@ error_release_group_hold:
return ret;
}
-/**
- * imx412_set_ctrl() - Set subdevice control
- * @ctrl: pointer to v4l2_ctrl structure
- *
- * Supported controls:
- * - V4L2_CID_VBLANK
- * - cluster controls:
- * - V4L2_CID_ANALOGUE_GAIN
- * - V4L2_CID_EXPOSURE
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx412 *imx412 =
@@ -634,14 +622,6 @@ static const struct v4l2_ctrl_ops imx412_ctrl_ops = {
.s_ctrl = imx412_set_ctrl,
};
-/**
- * imx412_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
- * @sd: pointer to imx412 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @code: V4L2 sub-device code enumeration need to be filled
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -654,14 +634,6 @@ static int imx412_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
-/**
- * imx412_enum_frame_size() - Enumerate V4L2 sub-device frame sizes
- * @sd: pointer to imx412 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fsize: V4L2 sub-device size enumeration need to be filled
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fsize)
@@ -701,14 +673,6 @@ static void imx412_fill_pad_format(struct imx412 *imx412,
fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
}
-/**
- * imx412_get_pad_format() - Get subdevice pad format
- * @sd: pointer to imx412 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_get_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -731,14 +695,6 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd,
return 0;
}
-/**
- * imx412_set_pad_format() - Set subdevice pad format
- * @sd: pointer to imx412 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -768,13 +724,6 @@ static int imx412_set_pad_format(struct v4l2_subdev *sd,
return ret;
}
-/**
- * imx412_init_state() - Initialize sub-device state
- * @sd: pointer to imx412 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
@@ -840,13 +789,6 @@ static int imx412_stop_streaming(struct imx412 *imx412)
1, IMX412_MODE_STANDBY);
}
-/**
- * imx412_set_stream() - Enable sensor streaming
- * @sd: pointer to imx412 subdevice
- * @enable: set to enable sensor streaming
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_set_stream(struct v4l2_subdev *sd, int enable)
{
struct imx412 *imx412 = to_imx412(sd);
@@ -1010,12 +952,6 @@ static const struct v4l2_subdev_internal_ops imx412_internal_ops = {
.init_state = imx412_init_state,
};
-/**
- * imx412_power_on() - Sensor power on sequence
- * @dev: pointer to i2c device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_power_on(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1053,12 +989,6 @@ error_reset:
return ret;
}
-/**
- * imx412_power_off() - Sensor power off sequence
- * @dev: pointer to i2c device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_power_off(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1159,12 +1089,6 @@ static int imx412_init_controls(struct imx412 *imx412)
return 0;
}
-/**
- * imx412_probe() - I2C client device binding
- * @client: pointer to i2c client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int imx412_probe(struct i2c_client *client)
{
struct imx412 *imx412;
@@ -1254,12 +1178,6 @@ error_mutex_destroy:
return ret;
}
-/**
- * imx412_remove() - I2C client device unbinding
- * @client: pointer to I2C client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static void imx412_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
diff --git a/drivers/media/i2c/ir-kbd-i2c.c b/drivers/media/i2c/ir-kbd-i2c.c
index 604745317004..f2bf2b354000 100644
--- a/drivers/media/i2c/ir-kbd-i2c.c
+++ b/drivers/media/i2c/ir-kbd-i2c.c
@@ -978,10 +978,10 @@ static void ir_remove(struct i2c_client *client)
static const struct i2c_device_id ir_kbd_id[] = {
/* Generic entry for any IR receiver */
- { "ir_video", 0 },
+ { .name = "ir_video", .driver_data = 0 },
/* IR device specific entries should be added here */
- { "ir_z8f0811_haup", FLAG_TX },
- { "ir_z8f0811_hdpvr", FLAG_TX | FLAG_HDPVR },
+ { .name = "ir_z8f0811_haup", .driver_data = FLAG_TX },
+ { .name = "ir_z8f0811_hdpvr", .driver_data = FLAG_TX | FLAG_HDPVR },
{ }
};
MODULE_DEVICE_TABLE(i2c, ir_kbd_id);
diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c
index 5ffd53e005ee..a77538d2343c 100644
--- a/drivers/media/i2c/isl7998x.c
+++ b/drivers/media/i2c/isl7998x.c
@@ -1561,7 +1561,7 @@ static const struct of_device_id isl7998x_of_match[] = {
MODULE_DEVICE_TABLE(of, isl7998x_of_match);
static const struct i2c_device_id isl7998x_id[] = {
- { "isl79987" },
+ { .name = "isl79987" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, isl7998x_id);
diff --git a/drivers/media/i2c/ks0127.c b/drivers/media/i2c/ks0127.c
index f3fba9179684..8c66be38adc3 100644
--- a/drivers/media/i2c/ks0127.c
+++ b/drivers/media/i2c/ks0127.c
@@ -677,9 +677,9 @@ static void ks0127_remove(struct i2c_client *client)
}
static const struct i2c_device_id ks0127_id[] = {
- { "ks0127" },
- { "ks0127b" },
- { "ks0122s" },
+ { .name = "ks0127" },
+ { .name = "ks0127b" },
+ { .name = "ks0122s" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ks0127_id);
diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c
index f4cc844f4e3c..c3c90d830ee2 100644
--- a/drivers/media/i2c/lm3560.c
+++ b/drivers/media/i2c/lm3560.c
@@ -9,16 +9,22 @@
* Ldd-Mlp <ldd-mlp@list.ti.com>
*/
+#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/videodev2.h>
-#include <media/i2c/lm3560.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
/* registers definitions */
#define REG_ENABLE 0x10
@@ -33,32 +39,83 @@
#define FAULT_OVERTEMP (1<<1)
#define FAULT_SHORT_CIRCUIT (1<<2)
+#define LM3560_FLASH_TOUT_MIN 32
+#define LM3560_FLASH_TOUT_STEP 32
+#define LM3560_FLASH_TOUT_MAX 1024
+#define LM3560_FLASH_TOUT_ms_TO_REG(a) \
+ ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \
+ (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP))
+#define LM3560_FLASH_TOUT_REG_TO_ms(a) \
+ ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN)
+
+enum lm3560_led_id {
+ LM3560_LED0 = 0,
+ LM3560_LED1,
+ LM3560_LED_MAX
+};
+
+enum lm3560_peak_current {
+ LM3560_PEAK_1600mA = 0x00,
+ LM3560_PEAK_2300mA = 0x20,
+ LM3560_PEAK_3000mA = 0x40,
+ LM3560_PEAK_3600mA = 0x60
+};
+
enum led_enable {
MODE_SHDN = 0x0,
MODE_TORCH = 0x2,
MODE_FLASH = 0x3,
};
+struct lm3560_flash_config {
+ u32 flash_brt_min_ua;
+ u32 flash_brt_max_ua;
+ u32 flash_brt_step_ua;
+
+ u32 torch_brt_min_ua;
+ u32 torch_brt_max_ua;
+ u32 torch_brt_step_ua;
+};
+
/**
* struct lm3560_flash
*
* @dev: pointer to &struct device
- * @pdata: platform data
* @regmap: reg. map for i2c
* @lock: muxtex for serial access.
+ * @hwen_gpio: line connected to HWEN pin
+ * @vin_supply: line connected to IN supply (2.5V - 5.5V)
* @led_mode: V4L2 LED mode
* @ctrls_led: V4L2 controls
* @subdev_led: V4L2 subdev
+ * @led_id: LED status holder
+ * @peak: peak current
+ * @max_flash_timeout: flash timeout
+ * @max_flash_brt: flash mode led brightness
+ * @max_torch_brt: torch mode led brightness
+ * @config: device specific current configuration
*/
struct lm3560_flash {
struct device *dev;
- struct lm3560_platform_data *pdata;
struct regmap *regmap;
struct mutex lock;
+ struct gpio_desc *hwen_gpio;
+ struct regulator *vin_supply;
+
enum v4l2_flash_led_mode led_mode;
struct v4l2_ctrl_handler ctrls_led[LM3560_LED_MAX];
struct v4l2_subdev subdev_led[LM3560_LED_MAX];
+
+ DECLARE_BITMAP(led_id, LM3560_LED_MAX);
+
+ enum lm3560_peak_current peak;
+ u32 max_flash_timeout;
+
+ u32 max_flash_brt[LM3560_LED_MAX];
+ u32 max_torch_brt[LM3560_LED_MAX];
+
+ const struct lm3560_flash_config *config;
};
#define to_lm3560_flash(_ctrl, _no) \
@@ -114,15 +171,20 @@ static int lm3560_enable_ctrl(struct lm3560_flash *flash,
static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash,
enum lm3560_led_id led_no, unsigned int brt)
{
+ const struct lm3560_flash_config *config = flash->config;
int rval;
- u8 br_bits;
+ u32 br_bits;
- if (brt < LM3560_TORCH_BRT_MIN)
+ if (brt < config->torch_brt_min_ua)
return lm3560_enable_ctrl(flash, led_no, false);
else
rval = lm3560_enable_ctrl(flash, led_no, true);
- br_bits = LM3560_TORCH_BRT_uA_TO_REG(brt);
+ br_bits = clamp(brt, config->torch_brt_min_ua,
+ config->torch_brt_max_ua);
+ br_bits = (br_bits - config->torch_brt_min_ua) /
+ config->torch_brt_step_ua;
+
if (led_no == LM3560_LED0)
rval = regmap_update_bits(flash->regmap,
REG_TORCH_BR, 0x07, br_bits);
@@ -137,15 +199,20 @@ static int lm3560_torch_brt_ctrl(struct lm3560_flash *flash,
static int lm3560_flash_brt_ctrl(struct lm3560_flash *flash,
enum lm3560_led_id led_no, unsigned int brt)
{
+ const struct lm3560_flash_config *config = flash->config;
int rval;
- u8 br_bits;
+ u32 br_bits;
- if (brt < LM3560_FLASH_BRT_MIN)
+ if (brt < config->flash_brt_min_ua)
return lm3560_enable_ctrl(flash, led_no, false);
else
rval = lm3560_enable_ctrl(flash, led_no, true);
- br_bits = LM3560_FLASH_BRT_uA_TO_REG(brt);
+ br_bits = clamp(brt, config->flash_brt_min_ua,
+ config->flash_brt_max_ua);
+ br_bits = (br_bits - config->flash_brt_min_ua) /
+ config->flash_brt_step_ua;
+
if (led_no == LM3560_LED0)
rval = regmap_update_bits(flash->regmap,
REG_FLASH_BR, 0x0f, br_bits);
@@ -162,14 +229,17 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
struct lm3560_flash *flash = to_lm3560_flash(ctrl, led_no);
int rval = -EINVAL;
- mutex_lock(&flash->lock);
+ if (!pm_runtime_get_if_active(flash->dev))
+ return 0;
if (ctrl->id == V4L2_CID_FLASH_FAULT) {
s32 fault = 0;
unsigned int reg_val;
rval = regmap_read(flash->regmap, REG_FLAG, &reg_val);
- if (rval < 0)
- goto out;
+ if (rval < 0) {
+ pm_runtime_put(flash->dev);
+ return rval;
+ }
if (reg_val & FAULT_SHORT_CIRCUIT)
fault |= V4L2_FLASH_FAULT_SHORT_CIRCUIT;
if (reg_val & FAULT_OVERTEMP)
@@ -179,8 +249,8 @@ static int lm3560_get_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
ctrl->cur.val = fault;
}
-out:
- mutex_unlock(&flash->lock);
+ pm_runtime_put(flash->dev);
+
return rval;
}
@@ -190,7 +260,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
u8 tout_bits;
int rval = -EINVAL;
- mutex_lock(&flash->lock);
+ if (!pm_runtime_get_if_active(flash->dev))
+ return 0;
switch (ctrl->id) {
case V4L2_CID_FLASH_LED_MODE:
@@ -202,14 +273,12 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
case V4L2_CID_FLASH_STROBE_SOURCE:
rval = regmap_update_bits(flash->regmap,
REG_CONFIG1, 0x04, (ctrl->val) << 2);
- if (rval < 0)
- goto err_out;
break;
case V4L2_CID_FLASH_STROBE:
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
rval = -EBUSY;
- goto err_out;
+ break;
}
flash->led_mode = V4L2_FLASH_LED_MODE_FLASH;
rval = lm3560_mode_ctrl(flash);
@@ -218,7 +287,7 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
case V4L2_CID_FLASH_STROBE_STOP:
if (flash->led_mode != V4L2_FLASH_LED_MODE_FLASH) {
rval = -EBUSY;
- goto err_out;
+ break;
}
flash->led_mode = V4L2_FLASH_LED_MODE_NONE;
rval = lm3560_mode_ctrl(flash);
@@ -239,8 +308,8 @@ static int lm3560_set_ctrl(struct v4l2_ctrl *ctrl, enum lm3560_led_id led_no)
break;
}
-err_out:
- mutex_unlock(&flash->lock);
+ pm_runtime_put(flash->dev);
+
return rval;
}
@@ -279,10 +348,11 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
enum lm3560_led_id led_no)
{
struct v4l2_ctrl *fault;
- u32 max_flash_brt = flash->pdata->max_flash_brt[led_no];
- u32 max_torch_brt = flash->pdata->max_torch_brt[led_no];
+ u32 max_flash_brt = flash->max_flash_brt[led_no];
+ u32 max_torch_brt = flash->max_torch_brt[led_no];
struct v4l2_ctrl_handler *hdl = &flash->ctrls_led[led_no];
const struct v4l2_ctrl_ops *ops = &lm3560_led_ctrl_ops[led_no];
+ const struct lm3560_flash_config *config = flash->config;
v4l2_ctrl_handler_init(hdl, 8);
@@ -305,19 +375,19 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
/* flash strobe timeout */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TIMEOUT,
LM3560_FLASH_TOUT_MIN,
- flash->pdata->max_flash_timeout,
+ flash->max_flash_timeout,
LM3560_FLASH_TOUT_STEP,
- flash->pdata->max_flash_timeout);
+ flash->max_flash_timeout);
/* flash brt */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_INTENSITY,
- LM3560_FLASH_BRT_MIN, max_flash_brt,
- LM3560_FLASH_BRT_STEP, max_flash_brt);
+ config->flash_brt_min_ua, max_flash_brt,
+ config->flash_brt_step_ua, max_flash_brt);
/* torch brt */
v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_TORCH_INTENSITY,
- LM3560_TORCH_BRT_MIN, max_torch_brt,
- LM3560_TORCH_BRT_STEP, max_torch_brt);
+ config->torch_brt_min_ua, max_torch_brt,
+ config->torch_brt_step_ua, max_torch_brt);
/* fault */
fault = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FLASH_FAULT, 0,
@@ -328,6 +398,8 @@ static int lm3560_init_controls(struct lm3560_flash *flash,
if (fault != NULL)
fault->flags |= V4L2_CTRL_FLAG_VOLATILE;
+ hdl->lock = &flash->lock;
+
if (hdl->error)
return hdl->error;
@@ -347,15 +419,18 @@ static const struct regmap_config lm3560_regmap = {
};
static int lm3560_subdev_init(struct lm3560_flash *flash,
- enum lm3560_led_id led_no, char *led_name)
+ enum lm3560_led_id led_no,
+ struct fwnode_handle *fwnode)
{
struct i2c_client *client = to_i2c_client(flash->dev);
int rval;
v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops);
flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- strscpy(flash->subdev_led[led_no].name, led_name,
- sizeof(flash->subdev_led[led_no].name));
+ snprintf(flash->subdev_led[led_no].name,
+ sizeof(flash->subdev_led[led_no].name),
+ "lm3560-led%d", led_no);
+ flash->subdev_led[led_no].fwnode = fwnode_handle_get(fwnode);
rval = lm3560_init_controls(flash, led_no);
if (rval)
goto err_out;
@@ -363,9 +438,17 @@ static int lm3560_subdev_init(struct lm3560_flash *flash,
if (rval < 0)
goto err_out;
flash->subdev_led[led_no].entity.function = MEDIA_ENT_F_FLASH;
+ flash->subdev_led[led_no].state_lock = &flash->lock;
- return rval;
+ rval = v4l2_async_register_subdev(&flash->subdev_led[led_no]);
+ if (rval < 0) {
+ dev_err(flash->dev, "failed to register V4L2 subdev");
+ goto error_out_media;
+ }
+ return rval;
+error_out_media:
+ media_entity_cleanup(&flash->subdev_led[led_no].entity);
err_out:
v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
return rval;
@@ -378,7 +461,7 @@ static int lm3560_init_device(struct lm3560_flash *flash)
/* set peak current */
rval = regmap_update_bits(flash->regmap,
- REG_FLASH_TOUT, 0x60, flash->pdata->peak);
+ REG_FLASH_TOUT, 0x60, flash->peak);
if (rval < 0)
return rval;
/* output disable */
@@ -391,81 +474,247 @@ static int lm3560_init_device(struct lm3560_flash *flash)
return rval;
}
+static int __maybe_unused lm3560_power_off(struct device *dev)
+{
+ struct lm3560_flash *flash = dev_get_drvdata(dev);
+
+ gpiod_set_value_cansleep(flash->hwen_gpio, 0);
+ regulator_disable(flash->vin_supply);
+
+ return 0;
+}
+
+static int __maybe_unused lm3560_power_on(struct device *dev)
+{
+ struct lm3560_flash *flash = dev_get_drvdata(dev);
+ int rval;
+
+ rval = regulator_enable(flash->vin_supply);
+ if (rval < 0) {
+ dev_err(flash->dev, "failed to enable vin power supply\n");
+ return rval;
+ }
+
+ gpiod_set_value_cansleep(flash->hwen_gpio, 1);
+
+ rval = lm3560_init_device(flash);
+ if (rval < 0) {
+ lm3560_power_off(dev);
+ return rval;
+ }
+
+ return 0;
+}
+
+static void lm3560_subdev_cleanup(struct lm3560_flash *flash)
+{
+ int led_no;
+
+ for_each_set_bit(led_no, flash->led_id, LM3560_LED_MAX) {
+ v4l2_async_unregister_subdev(&flash->subdev_led[led_no]);
+ v4l2_ctrl_handler_free(&flash->ctrls_led[led_no]);
+ media_entity_cleanup(&flash->subdev_led[led_no].entity);
+ }
+}
+
static int lm3560_probe(struct i2c_client *client)
{
struct lm3560_flash *flash;
- struct lm3560_platform_data *pdata = dev_get_platdata(&client->dev);
+ u32 peak_ua;
int rval;
flash = devm_kzalloc(&client->dev, sizeof(*flash), GFP_KERNEL);
if (flash == NULL)
return -ENOMEM;
+ flash->config = device_get_match_data(&client->dev);
+ if (!flash->config)
+ return -ENODEV;
+
flash->regmap = devm_regmap_init_i2c(client, &lm3560_regmap);
if (IS_ERR(flash->regmap)) {
rval = PTR_ERR(flash->regmap);
return rval;
}
- /* if there is no platform data, use chip default value */
- if (pdata == NULL) {
- pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
- if (pdata == NULL)
- return -ENODEV;
- pdata->peak = LM3560_PEAK_3600mA;
- pdata->max_flash_timeout = LM3560_FLASH_TOUT_MAX;
- /* led 1 */
- pdata->max_flash_brt[LM3560_LED0] = LM3560_FLASH_BRT_MAX;
- pdata->max_torch_brt[LM3560_LED0] = LM3560_TORCH_BRT_MAX;
- /* led 2 */
- pdata->max_flash_brt[LM3560_LED1] = LM3560_FLASH_BRT_MAX;
- pdata->max_torch_brt[LM3560_LED1] = LM3560_TORCH_BRT_MAX;
- }
- flash->pdata = pdata;
flash->dev = &client->dev;
mutex_init(&flash->lock);
- rval = lm3560_subdev_init(flash, LM3560_LED0, "lm3560-led0");
- if (rval < 0)
- return rval;
+ bitmap_zero(flash->led_id, LM3560_LED_MAX);
+
+ flash->hwen_gpio = devm_gpiod_get_optional(flash->dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(flash->hwen_gpio))
+ return dev_err_probe(flash->dev, PTR_ERR(flash->hwen_gpio),
+ "failed to get hwen gpio\n");
+
+ flash->vin_supply = devm_regulator_get(flash->dev, "vin");
+ if (IS_ERR(flash->vin_supply))
+ return dev_err_probe(flash->dev, PTR_ERR(flash->vin_supply),
+ "failed to get vin-supply\n");
+
+ flash->peak = LM3560_PEAK_1600mA;
+ rval = device_property_read_u32(flash->dev,
+ "ti,peak-current-microamp", &peak_ua);
+ if (!rval) {
+ /*
+ * LM3559 has lower peak current limit, but
+ * bit configuration matches LM3560.
+ * Correct current restrictions are enforced
+ * by the LM3560 schema.
+ */
+ switch (peak_ua) {
+ case 1400000:
+ case 1600000:
+ flash->peak = LM3560_PEAK_1600mA;
+ break;
+ case 2100000:
+ case 2300000:
+ flash->peak = LM3560_PEAK_2300mA;
+ break;
+ case 2700000:
+ case 3000000:
+ flash->peak = LM3560_PEAK_3000mA;
+ break;
+ case 3200000:
+ case 3600000:
+ flash->peak = LM3560_PEAK_3600mA;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ flash->max_flash_timeout = LM3560_FLASH_TOUT_MIN * 1000;
+ device_property_read_u32(flash->dev, "flash-max-timeout-us",
+ &flash->max_flash_timeout);
+ flash->max_flash_timeout /= 1000;
- rval = lm3560_subdev_init(flash, LM3560_LED1, "lm3560-led1");
+ rval = regulator_enable(flash->vin_supply);
if (rval < 0)
- return rval;
+ return dev_err_probe(flash->dev, rval,
+ "failed to enable vin power supply\n");
+
+ gpiod_set_value_cansleep(flash->hwen_gpio, 1);
rval = lm3560_init_device(flash);
if (rval < 0)
- return rval;
+ goto error_disable;
+
+ pm_runtime_set_active(flash->dev);
+ pm_runtime_enable(flash->dev);
+
+ device_for_each_child_node_scoped(flash->dev, node) {
+ const struct lm3560_flash_config *config = flash->config;
+ u32 reg;
+
+ rval = fwnode_property_read_u32(node, "reg", &reg);
+ if (rval < 0)
+ /* We care only about nodes with reg property */
+ continue;
+
+ if (reg == LM3560_LED0 || reg == LM3560_LED1) {
+ flash->max_flash_brt[reg] = config->flash_brt_min_ua;
+ fwnode_property_read_u32(node, "flash-max-microamp",
+ &flash->max_flash_brt[reg]);
+
+ flash->max_torch_brt[reg] = config->torch_brt_min_ua;
+ fwnode_property_read_u32(node, "led-max-microamp",
+ &flash->max_torch_brt[reg]);
+
+ rval = lm3560_subdev_init(flash, reg, node);
+ if (rval < 0) {
+ dev_err(flash->dev,
+ "failed to register led%d: %d\n",
+ reg, rval);
+ goto error_clean;
+ }
+
+ set_bit(reg, flash->led_id);
+ }
+ }
i2c_set_clientdata(client, flash);
+ pm_runtime_set_autosuspend_delay(flash->dev, 1000);
+ pm_runtime_use_autosuspend(flash->dev);
+ pm_runtime_idle(flash->dev);
+
return 0;
+
+error_clean:
+ pm_runtime_disable(flash->dev);
+ pm_runtime_set_suspended(flash->dev);
+
+ lm3560_subdev_cleanup(flash);
+
+error_disable:
+ gpiod_set_value_cansleep(flash->hwen_gpio, 0);
+ regulator_disable(flash->vin_supply);
+
+ return rval;
}
static void lm3560_remove(struct i2c_client *client)
{
struct lm3560_flash *flash = i2c_get_clientdata(client);
- unsigned int i;
- for (i = LM3560_LED0; i < LM3560_LED_MAX; i++) {
- v4l2_device_unregister_subdev(&flash->subdev_led[i]);
- v4l2_ctrl_handler_free(&flash->ctrls_led[i]);
- media_entity_cleanup(&flash->subdev_led[i].entity);
+ lm3560_subdev_cleanup(flash);
+
+ /*
+ * Disable runtime PM. In case runtime PM is disabled in the kernel,
+ * make sure to turn power off manually.
+ */
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ lm3560_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
}
}
-static const struct i2c_device_id lm3560_id_table[] = {
- { LM3559_NAME },
- { LM3560_NAME },
- {}
+static const struct dev_pm_ops lm3560_pm_ops = {
+ SET_RUNTIME_PM_OPS(lm3560_power_off, lm3560_power_on, NULL)
};
+static const struct lm3560_flash_config lm3559_config = {
+ .flash_brt_min_ua = 56250,
+ .flash_brt_max_ua = 900000,
+ .flash_brt_step_ua = 56250,
+
+ .torch_brt_min_ua = 28125,
+ .torch_brt_max_ua = 225000,
+ .torch_brt_step_ua = 28125,
+};
+
+static const struct lm3560_flash_config lm3560_config = {
+ .flash_brt_min_ua = 62500,
+ .flash_brt_max_ua = 1000000,
+ .flash_brt_step_ua = 62500,
+
+ .torch_brt_min_ua = 31250,
+ .torch_brt_max_ua = 250000,
+ .torch_brt_step_ua = 31250,
+};
+
+static const struct of_device_id lm3560_of_match[] = {
+ { .compatible = "ti,lm3559", .data = &lm3559_config },
+ { .compatible = "ti,lm3560", .data = &lm3560_config },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lm3560_of_match);
+
+static const struct i2c_device_id lm3560_id_table[] = {
+ { .name = "lm3559", .driver_data = (kernel_ulong_t)&lm3559_config },
+ { .name = "lm3560", .driver_data = (kernel_ulong_t)&lm3560_config },
+ { }
+};
MODULE_DEVICE_TABLE(i2c, lm3560_id_table);
static struct i2c_driver lm3560_i2c_driver = {
.driver = {
- .name = LM3560_NAME,
- .pm = NULL,
+ .name = "lm3560",
+ .pm = pm_ptr(&lm3560_pm_ops),
+ .of_match_table = lm3560_of_match,
},
.probe = lm3560_probe,
.remove = lm3560_remove,
diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c
index 2d16e42ec224..9030cbe32afc 100644
--- a/drivers/media/i2c/lm3646.c
+++ b/drivers/media/i2c/lm3646.c
@@ -386,8 +386,8 @@ static void lm3646_remove(struct i2c_client *client)
}
static const struct i2c_device_id lm3646_id_table[] = {
- { LM3646_NAME },
- {}
+ { .name = LM3646_NAME },
+ { }
};
MODULE_DEVICE_TABLE(i2c, lm3646_id_table);
diff --git a/drivers/media/i2c/m52790.c b/drivers/media/i2c/m52790.c
index 9e1ecfd01e2a..f3adf8c2b27f 100644
--- a/drivers/media/i2c/m52790.c
+++ b/drivers/media/i2c/m52790.c
@@ -163,7 +163,7 @@ static void m52790_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id m52790_id[] = {
- { "m52790" },
+ { .name = "m52790" },
{ }
};
MODULE_DEVICE_TABLE(i2c, m52790_id);
diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c
index bf02ca23a284..1cc388b52902 100644
--- a/drivers/media/i2c/max2175.c
+++ b/drivers/media/i2c/max2175.c
@@ -1413,8 +1413,8 @@ static void max2175_remove(struct i2c_client *client)
}
static const struct i2c_device_id max2175_id[] = {
- { DRIVER_NAME },
- {}
+ { .name = DRIVER_NAME },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max2175_id);
diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c
index 57ba3693649a..48b7d589df31 100644
--- a/drivers/media/i2c/ml86v7667.c
+++ b/drivers/media/i2c/ml86v7667.c
@@ -424,8 +424,8 @@ static void ml86v7667_remove(struct i2c_client *client)
}
static const struct i2c_device_id ml86v7667_id[] = {
- { DRV_NAME },
- {}
+ { .name = DRV_NAME },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ml86v7667_id);
diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c
index 4c0b0ad68c08..413cfbc2dd94 100644
--- a/drivers/media/i2c/msp3400-driver.c
+++ b/drivers/media/i2c/msp3400-driver.c
@@ -874,7 +874,7 @@ static const struct dev_pm_ops msp3400_pm_ops = {
};
static const struct i2c_device_id msp_id[] = {
- { "msp3400" },
+ { .name = "msp3400" },
{ }
};
MODULE_DEVICE_TABLE(i2c, msp_id);
diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c
index 7a6114d18dfc..0ade967b357b 100644
--- a/drivers/media/i2c/mt9m001.c
+++ b/drivers/media/i2c/mt9m001.c
@@ -855,7 +855,7 @@ static void mt9m001_remove(struct i2c_client *client)
}
static const struct i2c_device_id mt9m001_id[] = {
- { "mt9m001" },
+ { .name = "mt9m001" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9m001_id);
diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c
index 3532c7c38bec..4e748080b798 100644
--- a/drivers/media/i2c/mt9m111.c
+++ b/drivers/media/i2c/mt9m111.c
@@ -1384,7 +1384,7 @@ static const struct of_device_id mt9m111_of_match[] = {
MODULE_DEVICE_TABLE(of, mt9m111_of_match);
static const struct i2c_device_id mt9m111_id[] = {
- { "mt9m111" },
+ { .name = "mt9m111" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9m111_id);
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index ea5d43d925ff..8dc57eeba606 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -796,7 +796,8 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl)
data = (1 << 6) | (ctrl->val >> 1);
} else {
ctrl->val &= ~7;
- data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
+ data = ((ctrl->val - 64) >> 3) & 0x7f;
+ data = (data << 8) | (1 << 6) | 32;
}
return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data);
diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c
index 2d2c840fc002..bd2268154ca7 100644
--- a/drivers/media/i2c/mt9t112.c
+++ b/drivers/media/i2c/mt9t112.c
@@ -1108,7 +1108,7 @@ static void mt9t112_remove(struct i2c_client *client)
}
static const struct i2c_device_id mt9t112_id[] = {
- { "mt9t112" },
+ { .name = "mt9t112" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9t112_id);
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index 055b7915260a..985517f1cff7 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -582,7 +582,7 @@ static void mt9v011_remove(struct i2c_client *c)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id mt9v011_id[] = {
- { "mt9v011" },
+ { .name = "mt9v011" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mt9v011_id);
diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
index 162b49046990..09d58e8b1c7f 100644
--- a/drivers/media/i2c/ov13858.c
+++ b/drivers/media/i2c/ov13858.c
@@ -1747,8 +1747,8 @@ static void ov13858_remove(struct i2c_client *client)
}
static const struct i2c_device_id ov13858_id_table[] = {
- { "ov13858" },
- {}
+ { .name = "ov13858" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ov13858_id_table);
diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c
index 5421874732bc..b0d34141a13a 100644
--- a/drivers/media/i2c/ov13b10.c
+++ b/drivers/media/i2c/ov13b10.c
@@ -8,6 +8,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -699,6 +700,12 @@ static const struct ov13b10_mode supported_2_lanes_modes[] = {
},
};
+static const char * const ov13b10_supply_names[] = {
+ "dovdd", /* Digital I/O power */
+ "avdd", /* Analog power */
+ "dvdd", /* Digital core power */
+};
+
struct ov13b10 {
struct device *dev;
@@ -708,7 +715,7 @@ struct ov13b10 {
struct v4l2_ctrl_handler ctrl_handler;
struct clk *img_clk;
- struct regulator *avdd;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(ov13b10_supply_names)];
struct gpio_desc *reset;
/* V4L2 Controls */
@@ -1194,9 +1201,8 @@ static int ov13b10_power_off(struct device *dev)
struct ov13b10 *ov13b10 = to_ov13b10(sd);
gpiod_set_value_cansleep(ov13b10->reset, 1);
-
- if (ov13b10->avdd)
- regulator_disable(ov13b10->avdd);
+ regulator_bulk_disable(ARRAY_SIZE(ov13b10_supply_names),
+ ov13b10->supplies);
clk_disable_unprepare(ov13b10->img_clk);
@@ -1214,14 +1220,12 @@ static int ov13b10_power_on(struct device *dev)
dev_err(dev, "failed to enable imaging clock: %d", ret);
return ret;
}
-
- if (ov13b10->avdd) {
- ret = regulator_enable(ov13b10->avdd);
- if (ret < 0) {
- dev_err(dev, "failed to enable avdd: %d", ret);
- clk_disable_unprepare(ov13b10->img_clk);
- return ret;
- }
+ ret = regulator_bulk_enable(ARRAY_SIZE(ov13b10_supply_names),
+ ov13b10->supplies);
+ if (ret < 0) {
+ dev_err(dev, "failed to enable regulators\n");
+ clk_disable_unprepare(ov13b10->img_clk);
+ return ret;
}
gpiod_set_value_cansleep(ov13b10->reset, 0);
@@ -1475,7 +1479,8 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b)
unsigned long freq;
int ret;
- ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset", GPIOD_OUT_LOW);
+ ov13b->reset = devm_gpiod_get_optional(ov13b->dev, "reset",
+ GPIOD_OUT_LOW);
if (IS_ERR(ov13b->reset))
return dev_err_probe(ov13b->dev, PTR_ERR(ov13b->reset),
"failed to get reset gpio\n");
@@ -1491,15 +1496,15 @@ static int ov13b10_get_pm_resources(struct ov13b10 *ov13b)
"external clock %lu is not supported\n",
freq);
- ov13b->avdd = devm_regulator_get_optional(ov13b->dev, "avdd");
- if (IS_ERR(ov13b->avdd)) {
- ret = PTR_ERR(ov13b->avdd);
- ov13b->avdd = NULL;
- if (ret != -ENODEV)
- return dev_err_probe(ov13b->dev, ret,
- "failed to get avdd regulator\n");
- }
+ for (unsigned int i = 0; i < ARRAY_SIZE(ov13b10_supply_names); i++)
+ ov13b->supplies[i].supply = ov13b10_supply_names[i];
+ ret = devm_regulator_bulk_get(ov13b->dev,
+ ARRAY_SIZE(ov13b10_supply_names),
+ ov13b->supplies);
+ if (ret)
+ return dev_err_probe(ov13b->dev, ret,
+ "failed to get regulators\n");
return 0;
}
diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index d27fc2df64e6..50feb608b92b 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -1271,7 +1271,7 @@ static void ov2640_remove(struct i2c_client *client)
}
static const struct i2c_device_id ov2640_id[] = {
- { "ov2640" },
+ { .name = "ov2640" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov2640_id);
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index 061401b020fc..7d8c7c3465a4 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -1553,7 +1553,7 @@ static const struct dev_pm_ops ov2659_pm_ops = {
};
static const struct i2c_device_id ov2659_id[] = {
- { "ov2659" },
+ { .name = "ov2659" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ov2659_id);
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 85ecc23b3587..92d2d6cd4ba4 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -3999,8 +3999,8 @@ static const struct dev_pm_ops ov5640_pm_ops = {
};
static const struct i2c_device_id ov5640_id[] = {
- { "ov5640" },
- {}
+ { .name = "ov5640" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ov5640_id);
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
index b10d408034a1..c772ef6e51d2 100644
--- a/drivers/media/i2c/ov5645.c
+++ b/drivers/media/i2c/ov5645.c
@@ -1219,8 +1219,8 @@ static void ov5645_remove(struct i2c_client *client)
}
static const struct i2c_device_id ov5645_id[] = {
- { "ov5645" },
- {}
+ { .name = "ov5645" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ov5645_id);
diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c
index db9bd2892140..3facf92b3841 100644
--- a/drivers/media/i2c/ov5647.c
+++ b/drivers/media/i2c/ov5647.c
@@ -1278,7 +1278,7 @@ static const struct dev_pm_ops ov5647_pm_ops = {
};
static const struct i2c_device_id ov5647_id[] = {
- { "ov5647" },
+ { .name = "ov5647" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ov5647_id);
diff --git a/drivers/media/i2c/ov7640.c b/drivers/media/i2c/ov7640.c
index 9f68d89936eb..0fd90bc67e29 100644
--- a/drivers/media/i2c/ov7640.c
+++ b/drivers/media/i2c/ov7640.c
@@ -77,7 +77,7 @@ static void ov7640_remove(struct i2c_client *client)
}
static const struct i2c_device_id ov7640_id[] = {
- { "ov7640" },
+ { .name = "ov7640" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov7640_id);
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 0cb96b6c9990..b6d238ba0d53 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1997,8 +1997,8 @@ static const struct ov7670_devtype ov7675_devdata = {
};
static const struct i2c_device_id ov7670_id[] = {
- { "ov7670", (kernel_ulong_t)&ov7670_devdata },
- { "ov7675", (kernel_ulong_t)&ov7675_devdata },
+ { .name = "ov7670", .driver_data = (kernel_ulong_t)&ov7670_devdata },
+ { .name = "ov7675", .driver_data = (kernel_ulong_t)&ov7675_devdata },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ov7670_id);
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 062e1023a411..be3ba284ee0b 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -1546,7 +1546,7 @@ static void ov772x_remove(struct i2c_client *client)
}
static const struct i2c_device_id ov772x_id[] = {
- { "ov772x" },
+ { .name = "ov772x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov772x_id);
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
index 632fb80469be..c2e02f191816 100644
--- a/drivers/media/i2c/ov7740.c
+++ b/drivers/media/i2c/ov7740.c
@@ -1149,7 +1149,7 @@ static int __maybe_unused ov7740_runtime_resume(struct device *dev)
}
static const struct i2c_device_id ov7740_id[] = {
- { "ov7740" },
+ { .name = "ov7740" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ov7740_id);
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index 2167fb73ea41..5b6f897a74fc 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -586,18 +586,6 @@ static u32 ov9282_flash_duration_to_us(struct ov9282 *ov9282, u32 value)
return DIV_ROUND_UP(value * frame_width, OV9282_STROBE_SPAN_FACTOR);
}
-/**
- * ov9282_set_ctrl() - Set subdevice control
- * @ctrl: pointer to v4l2_ctrl structure
- *
- * Supported controls:
- * - V4L2_CID_VBLANK
- * - cluster controls:
- * - V4L2_CID_ANALOGUE_GAIN
- * - V4L2_CID_EXPOSURE
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct ov9282 *ov9282 =
@@ -704,14 +692,6 @@ static const struct v4l2_ctrl_ops ov9282_ctrl_ops = {
.try_ctrl = ov9282_try_ctrl,
};
-/**
- * ov9282_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes
- * @sd: pointer to ov9282 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @code: V4L2 sub-device code enumeration need to be filled
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -780,14 +760,6 @@ static void ov9282_fill_pad_format(struct ov9282 *ov9282,
fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
}
-/**
- * ov9282_get_pad_format() - Get subdevice pad format
- * @sd: pointer to ov9282 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_get_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -807,14 +779,6 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd,
return 0;
}
-/**
- * ov9282_set_pad_format() - Set subdevice pad format
- * @sd: pointer to ov9282 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- * @fmt: V4L2 sub-device format need to be set
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -852,13 +816,6 @@ static int ov9282_set_pad_format(struct v4l2_subdev *sd,
return ret;
}
-/**
- * ov9282_init_state() - Initialize sub-device state
- * @sd: pointer to ov9282 V4L2 sub-device structure
- * @sd_state: V4L2 sub-device configuration
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
@@ -1157,12 +1114,6 @@ static const struct v4l2_subdev_internal_ops ov9282_internal_ops = {
.init_state = ov9282_init_state,
};
-/**
- * ov9282_power_on() - Sensor power on sequence
- * @dev: pointer to i2c device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_power_on(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1206,12 +1157,6 @@ error_reset:
return ret;
}
-/**
- * ov9282_power_off() - Sensor power off sequence
- * @dev: pointer to i2c device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_power_off(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
@@ -1333,12 +1278,6 @@ static int ov9282_init_controls(struct ov9282 *ov9282)
return 0;
}
-/**
- * ov9282_probe() - I2C client device binding
- * @client: pointer to i2c client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static int ov9282_probe(struct i2c_client *client)
{
struct ov9282 *ov9282;
@@ -1435,12 +1374,6 @@ error_power_off:
return ret;
}
-/**
- * ov9282_remove() - I2C client device unbinding
- * @client: pointer to I2C client device
- *
- * Return: 0 if successful, error code otherwise.
- */
static void ov9282_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c
index 2190c52b1433..122f411044ce 100644
--- a/drivers/media/i2c/ov9640.c
+++ b/drivers/media/i2c/ov9640.c
@@ -752,7 +752,7 @@ static void ov9640_remove(struct i2c_client *client)
}
static const struct i2c_device_id ov9640_id[] = {
- { "ov9640" },
+ { .name = "ov9640" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov9640_id);
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index c94e8fe29f22..5c85db8a4a38 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -1567,8 +1567,8 @@ static void ov965x_remove(struct i2c_client *client)
}
static const struct i2c_device_id ov965x_id[] = {
- { "OV9650" },
- { "OV9652" },
+ { .name = "OV9650" },
+ { .name = "OV9652" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, ov965x_id);
diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c
index e95342d706c3..23352d71a108 100644
--- a/drivers/media/i2c/rj54n1cb0c.c
+++ b/drivers/media/i2c/rj54n1cb0c.c
@@ -1413,7 +1413,7 @@ static void rj54n1_remove(struct i2c_client *client)
}
static const struct i2c_device_id rj54n1_id[] = {
- { "rj54n1cb0c" },
+ { .name = "rj54n1cb0c" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rj54n1_id);
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index ab31ee2b596b..551387cea521 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -1728,7 +1728,7 @@ static void s5c73m3_remove(struct i2c_client *client)
}
static const struct i2c_device_id s5c73m3_id[] = {
- { DRIVER_NAME },
+ { .name = DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, s5c73m3_id);
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index d1d00eca8708..378d273055ee 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -2007,7 +2007,7 @@ static void s5k5baf_remove(struct i2c_client *c)
}
static const struct i2c_device_id s5k5baf_id[] = {
- { S5K5BAF_DRIVER_NAME },
+ { .name = S5K5BAF_DRIVER_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, s5k5baf_id);
diff --git a/drivers/media/i2c/saa6588.c b/drivers/media/i2c/saa6588.c
index 90ae4121a68a..56bfa3d2604b 100644
--- a/drivers/media/i2c/saa6588.c
+++ b/drivers/media/i2c/saa6588.c
@@ -495,7 +495,7 @@ static void saa6588_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id saa6588_id[] = {
- { "saa6588" },
+ { .name = "saa6588" },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa6588_id);
diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c
index 1c0031ba43b4..c6bf0b0902e8 100644
--- a/drivers/media/i2c/saa6752hs.c
+++ b/drivers/media/i2c/saa6752hs.c
@@ -770,7 +770,7 @@ static void saa6752hs_remove(struct i2c_client *client)
}
static const struct i2c_device_id saa6752hs_id[] = {
- { "saa6752hs" },
+ { .name = "saa6752hs" },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa6752hs_id);
diff --git a/drivers/media/i2c/saa7110.c b/drivers/media/i2c/saa7110.c
index 942aeeb40c52..652058b8f766 100644
--- a/drivers/media/i2c/saa7110.c
+++ b/drivers/media/i2c/saa7110.c
@@ -439,7 +439,7 @@ static void saa7110_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id saa7110_id[] = {
- { "saa7110" },
+ { .name = "saa7110" },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa7110_id);
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index 48d6730d9271..7cce90750c93 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -1928,13 +1928,13 @@ static void saa711x_remove(struct i2c_client *client)
}
static const struct i2c_device_id saa711x_id[] = {
- { "saa7115_auto", 1 }, /* autodetect */
- { "saa7111", 0 },
- { "saa7113", 0 },
- { "saa7114", 0 },
- { "saa7115", 0 },
- { "saa7118", 0 },
- { "gm7113c", 0 },
+ { .name = "saa7115_auto", .driver_data = 1 }, /* autodetect */
+ { .name = "saa7111", .driver_data = 0 },
+ { .name = "saa7113", .driver_data = 0 },
+ { .name = "saa7114", .driver_data = 0 },
+ { .name = "saa7115", .driver_data = 0 },
+ { .name = "saa7118", .driver_data = 0 },
+ { .name = "gm7113c", .driver_data = 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa711x_id);
diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c
index a42a7ffe3768..fdf17f6fae67 100644
--- a/drivers/media/i2c/saa7127.c
+++ b/drivers/media/i2c/saa7127.c
@@ -797,11 +797,11 @@ static void saa7127_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id saa7127_id[] = {
- { "saa7127_auto", 0 }, /* auto-detection */
- { "saa7126", SAA7127 },
- { "saa7127", SAA7127 },
- { "saa7128", SAA7129 },
- { "saa7129", SAA7129 },
+ { .name = "saa7127_auto", .driver_data = 0 }, /* auto-detection */
+ { .name = "saa7126", .driver_data = SAA7127 },
+ { .name = "saa7127", .driver_data = SAA7127 },
+ { .name = "saa7128", .driver_data = SAA7129 },
+ { .name = "saa7129", .driver_data = SAA7129 },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa7127_id);
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index 713331be947c..0536ceb54650 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
@@ -1334,7 +1334,7 @@ static void saa717x_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id saa717x_id[] = {
- { "saa717x" },
+ { .name = "saa717x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa717x_id);
diff --git a/drivers/media/i2c/saa7185.c b/drivers/media/i2c/saa7185.c
index c04e452a332b..8e5c0eab907d 100644
--- a/drivers/media/i2c/saa7185.c
+++ b/drivers/media/i2c/saa7185.c
@@ -334,7 +334,7 @@ static void saa7185_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id saa7185_id[] = {
- { "saa7185" },
+ { .name = "saa7185" },
{ }
};
MODULE_DEVICE_TABLE(i2c, saa7185_id);
diff --git a/drivers/media/i2c/sony-btf-mpx.c b/drivers/media/i2c/sony-btf-mpx.c
index 16072a9f8247..4c87de0fe1f0 100644
--- a/drivers/media/i2c/sony-btf-mpx.c
+++ b/drivers/media/i2c/sony-btf-mpx.c
@@ -366,7 +366,7 @@ static void sony_btf_mpx_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id sony_btf_mpx_id[] = {
- { "sony-btf-mpx" },
+ { .name = "sony-btf-mpx" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sony_btf_mpx_id);
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index a0ca19359c43..fbd38bbfee03 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -2358,8 +2358,8 @@ static void tc358743_remove(struct i2c_client *client)
}
static const struct i2c_device_id tc358743_id[] = {
- { "tc358743" },
- {}
+ { .name = "tc358743" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tc358743_id);
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index 5c6dda5338f5..afa1d6f34c9c 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -2274,9 +2274,9 @@ static int tda1997x_set_power(struct tda1997x_state *state, bool on)
}
static const struct i2c_device_id tda1997x_i2c_id[] = {
- {"tda19971", (kernel_ulong_t)&tda1997x_chip_info[TDA19971]},
- {"tda19973", (kernel_ulong_t)&tda1997x_chip_info[TDA19973]},
- { },
+ { .name = "tda19971", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19971] },
+ { .name = "tda19973", .driver_data = (kernel_ulong_t)&tda1997x_chip_info[TDA19973] },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tda1997x_i2c_id);
diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c
index 76ef0fdddf76..a0b65a595ba8 100644
--- a/drivers/media/i2c/tda7432.c
+++ b/drivers/media/i2c/tda7432.c
@@ -400,7 +400,7 @@ static void tda7432_remove(struct i2c_client *client)
}
static const struct i2c_device_id tda7432_id[] = {
- { "tda7432" },
+ { .name = "tda7432" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tda7432_id);
diff --git a/drivers/media/i2c/tda9840.c b/drivers/media/i2c/tda9840.c
index e3b266db571f..b34d992777fb 100644
--- a/drivers/media/i2c/tda9840.c
+++ b/drivers/media/i2c/tda9840.c
@@ -182,7 +182,7 @@ static void tda9840_remove(struct i2c_client *client)
}
static const struct i2c_device_id tda9840_id[] = {
- { "tda9840" },
+ { .name = "tda9840" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tda9840_id);
diff --git a/drivers/media/i2c/tea6415c.c b/drivers/media/i2c/tea6415c.c
index 0cd2e6c52e20..ea7730a7ee2c 100644
--- a/drivers/media/i2c/tea6415c.c
+++ b/drivers/media/i2c/tea6415c.c
@@ -141,7 +141,7 @@ static void tea6415c_remove(struct i2c_client *client)
}
static const struct i2c_device_id tea6415c_id[] = {
- { "tea6415c" },
+ { .name = "tea6415c" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tea6415c_id);
diff --git a/drivers/media/i2c/tea6420.c b/drivers/media/i2c/tea6420.c
index 400883fc0c0f..bebb9a8095db 100644
--- a/drivers/media/i2c/tea6420.c
+++ b/drivers/media/i2c/tea6420.c
@@ -123,7 +123,7 @@ static void tea6420_remove(struct i2c_client *client)
}
static const struct i2c_device_id tea6420_id[] = {
- { "tea6420" },
+ { .name = "tea6420" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tea6420_id);
diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c
index ff268ebeb4d9..fa5b9c884c1d 100644
--- a/drivers/media/i2c/ths7303.c
+++ b/drivers/media/i2c/ths7303.c
@@ -369,9 +369,9 @@ static void ths7303_remove(struct i2c_client *client)
}
static const struct i2c_device_id ths7303_id[] = {
- { "ths7303" },
- { "ths7353" },
- {}
+ { .name = "ths7303" },
+ { .name = "ths7353" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ths7303_id);
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
index 686f10641c7a..808ef16ec3b3 100644
--- a/drivers/media/i2c/ths8200.c
+++ b/drivers/media/i2c/ths8200.c
@@ -487,8 +487,8 @@ static void ths8200_remove(struct i2c_client *client)
}
static const struct i2c_device_id ths8200_id[] = {
- { "ths8200" },
- {}
+ { .name = "ths8200" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ths8200_id);
diff --git a/drivers/media/i2c/tlv320aic23b.c b/drivers/media/i2c/tlv320aic23b.c
index 6f6bc5236565..7deeef317714 100644
--- a/drivers/media/i2c/tlv320aic23b.c
+++ b/drivers/media/i2c/tlv320aic23b.c
@@ -188,7 +188,7 @@ static void tlv320aic23b_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id tlv320aic23b_id[] = {
- { "tlv320aic23b" },
+ { .name = "tlv320aic23b" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id);
diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c
index 6267e9ad39c0..f136310b899d 100644
--- a/drivers/media/i2c/tvaudio.c
+++ b/drivers/media/i2c/tvaudio.c
@@ -2086,7 +2086,7 @@ static void tvaudio_remove(struct i2c_client *client)
detect which device is present. So rather than listing all supported
devices here, we pretend to support a single, fake device type. */
static const struct i2c_device_id tvaudio_id[] = {
- { "tvaudio" },
+ { .name = "tvaudio" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tvaudio_id);
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index f9c9c80c33ac..376eecb0b673 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -686,13 +686,6 @@ static int tvp514x_s_routing(struct v4l2_subdev *sd,
return 0;
}
-/**
- * tvp514x_s_ctrl() - V4L2 decoder interface handler for s_ctrl
- * @ctrl: pointer to v4l2_ctrl structure
- *
- * If the requested control is supported, sets the control's current
- * value in HW. Otherwise, returns -EINVAL if the control is not supported.
- */
static int tvp514x_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct v4l2_subdev *sd = to_sd(ctrl);
@@ -789,13 +782,6 @@ tvp514x_set_frame_interval(struct v4l2_subdev *sd,
return 0;
}
-/**
- * tvp514x_s_stream() - V4L2 decoder i/f handler for s_stream
- * @sd: pointer to standard V4L2 sub-device structure
- * @enable: streaming enable or disable
- *
- * Sets streaming to enable or disable, if possible.
- */
static int tvp514x_s_stream(struct v4l2_subdev *sd, int enable)
{
int err = 0;
@@ -850,14 +836,6 @@ static const struct v4l2_ctrl_ops tvp514x_ctrl_ops = {
.s_ctrl = tvp514x_s_ctrl,
};
-/**
- * tvp514x_enum_mbus_code() - V4L2 decoder interface handler for enum_mbus_code
- * @sd: pointer to standard V4L2 sub-device structure
- * @sd_state: subdev state
- * @code: pointer to v4l2_subdev_mbus_code_enum structure
- *
- * Enumertaes mbus codes supported
- */
static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
@@ -877,14 +855,6 @@ static int tvp514x_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
-/**
- * tvp514x_get_pad_format() - V4L2 decoder interface handler for get pad format
- * @sd: pointer to standard V4L2 sub-device structure
- * @sd_state: subdev state
- * @format: pointer to v4l2_subdev_format structure
- *
- * Retrieves pad format which is active or tried based on requirement
- */
static int tvp514x_get_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
@@ -909,14 +879,6 @@ static int tvp514x_get_pad_format(struct v4l2_subdev *sd,
return 0;
}
-/**
- * tvp514x_set_pad_format() - V4L2 decoder interface handler for set pad format
- * @sd: pointer to standard V4L2 sub-device structure
- * @sd_state: subdev state
- * @fmt: pointer to v4l2_subdev_format structure
- *
- * Set pad format for the output pad
- */
static int tvp514x_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -1014,15 +976,7 @@ done:
return pdata;
}
-/**
- * tvp514x_probe() - decoder driver i2c probe handler
- * @client: i2c driver client device structure
- *
- * Register decoder as an i2c client device and V4L2
- * device.
- */
-static int
-tvp514x_probe(struct i2c_client *client)
+static int tvp514x_probe(struct i2c_client *client)
{
struct tvp514x_platform_data *pdata = tvp514x_get_pdata(client);
struct tvp514x_decoder *decoder;
@@ -1113,13 +1067,6 @@ done:
return ret;
}
-/**
- * tvp514x_remove() - decoder driver i2c remove handler
- * @client: i2c driver client device structure
- *
- * Unregister decoder as an i2c client device and V4L2
- * device. Complement of tvp514x_probe().
- */
static void tvp514x_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
@@ -1182,10 +1129,10 @@ static const struct tvp514x_reg tvp514xm_init_reg_seq[] = {
* driver_data - Driver data
*/
static const struct i2c_device_id tvp514x_id[] = {
- {"tvp5146", (kernel_ulong_t)tvp5146_init_reg_seq },
- {"tvp5146m2", (kernel_ulong_t)tvp514xm_init_reg_seq },
- {"tvp5147", (kernel_ulong_t)tvp5147_init_reg_seq },
- {"tvp5147m1", (kernel_ulong_t)tvp514xm_init_reg_seq },
+ { .name = "tvp5146", .driver_data = (kernel_ulong_t)tvp5146_init_reg_seq },
+ { .name = "tvp5146m2", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq },
+ { .name = "tvp5147", .driver_data = (kernel_ulong_t)tvp5147_init_reg_seq },
+ { .name = "tvp5147m1", .driver_data = (kernel_ulong_t)tvp514xm_init_reg_seq },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, tvp514x_id);
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index e3675c744d9e..9c204f38935d 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -2265,7 +2265,7 @@ static const struct dev_pm_ops tvp5150_pm_ops = {
};
static const struct i2c_device_id tvp5150_id[] = {
- { "tvp5150" },
+ { .name = "tvp5150" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tvp5150_id);
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index c09a5bd71fd0..3979ccde5a95 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -1070,7 +1070,7 @@ static void tvp7002_remove(struct i2c_client *c)
/* I2C Device ID table */
static const struct i2c_device_id tvp7002_id[] = {
- { "tvp7002" },
+ { .name = "tvp7002" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tvp7002_id);
diff --git a/drivers/media/i2c/tw2804.c b/drivers/media/i2c/tw2804.c
index 3d154f4fb5f9..713e078ff3da 100644
--- a/drivers/media/i2c/tw2804.c
+++ b/drivers/media/i2c/tw2804.c
@@ -414,7 +414,7 @@ static void tw2804_remove(struct i2c_client *client)
}
static const struct i2c_device_id tw2804_id[] = {
- { "tw2804" },
+ { .name = "tw2804" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tw2804_id);
diff --git a/drivers/media/i2c/tw9900.c b/drivers/media/i2c/tw9900.c
index 53efdeaed1db..617fcf7f0b45 100644
--- a/drivers/media/i2c/tw9900.c
+++ b/drivers/media/i2c/tw9900.c
@@ -753,7 +753,7 @@ static const struct dev_pm_ops tw9900_pm_ops = {
};
static const struct i2c_device_id tw9900_id[] = {
- { "tw9900" },
+ { .name = "tw9900" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tw9900_id);
diff --git a/drivers/media/i2c/tw9903.c b/drivers/media/i2c/tw9903.c
index c3eafd5d5dc8..db063b885b09 100644
--- a/drivers/media/i2c/tw9903.c
+++ b/drivers/media/i2c/tw9903.c
@@ -246,7 +246,7 @@ static void tw9903_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id tw9903_id[] = {
- { "tw9903" },
+ { .name = "tw9903" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tw9903_id);
diff --git a/drivers/media/i2c/tw9906.c b/drivers/media/i2c/tw9906.c
index 0ab43fe42d7f..079e2f49f38f 100644
--- a/drivers/media/i2c/tw9906.c
+++ b/drivers/media/i2c/tw9906.c
@@ -214,7 +214,7 @@ static void tw9906_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id tw9906_id[] = {
- { "tw9906" },
+ { .name = "tw9906" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tw9906_id);
diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c
index f3e400304e04..872207e688bf 100644
--- a/drivers/media/i2c/tw9910.c
+++ b/drivers/media/i2c/tw9910.c
@@ -996,7 +996,7 @@ static void tw9910_remove(struct i2c_client *client)
}
static const struct i2c_device_id tw9910_id[] = {
- { "tw9910" },
+ { .name = "tw9910" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tw9910_id);
diff --git a/drivers/media/i2c/uda1342.c b/drivers/media/i2c/uda1342.c
index 2e4540ee2df2..437726788ba0 100644
--- a/drivers/media/i2c/uda1342.c
+++ b/drivers/media/i2c/uda1342.c
@@ -79,7 +79,7 @@ static void uda1342_remove(struct i2c_client *client)
}
static const struct i2c_device_id uda1342_id[] = {
- { "uda1342" },
+ { .name = "uda1342" },
{ }
};
MODULE_DEVICE_TABLE(i2c, uda1342_id);
diff --git a/drivers/media/i2c/upd64031a.c b/drivers/media/i2c/upd64031a.c
index a178af46e695..670118c16872 100644
--- a/drivers/media/i2c/upd64031a.c
+++ b/drivers/media/i2c/upd64031a.c
@@ -219,7 +219,7 @@ static void upd64031a_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id upd64031a_id[] = {
- { "upd64031a" },
+ { .name = "upd64031a" },
{ }
};
MODULE_DEVICE_TABLE(i2c, upd64031a_id);
diff --git a/drivers/media/i2c/upd64083.c b/drivers/media/i2c/upd64083.c
index 5421dc5e32c9..e610dffa9e10 100644
--- a/drivers/media/i2c/upd64083.c
+++ b/drivers/media/i2c/upd64083.c
@@ -190,7 +190,7 @@ static void upd64083_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id upd64083_id[] = {
- { "upd64083" },
+ { .name = "upd64083" },
{ }
};
MODULE_DEVICE_TABLE(i2c, upd64083_id);
diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c
index fef3993f4e2d..56b99eea54a1 100644
--- a/drivers/media/i2c/video-i2c.c
+++ b/drivers/media/i2c/video-i2c.c
@@ -454,8 +454,9 @@ static int video_i2c_thread_vid_cap(void *priv)
spin_lock(&data->slock);
if (!list_empty(&data->vid_cap_active)) {
- vid_cap_buf = list_last_entry(&data->vid_cap_active,
- struct video_i2c_buffer, list);
+ vid_cap_buf = list_first_entry(&data->vid_cap_active,
+ struct video_i2c_buffer,
+ list);
list_del(&vid_cap_buf->list);
}
@@ -888,7 +889,7 @@ static void video_i2c_remove(struct i2c_client *client)
if (data->chip->set_power)
data->chip->set_power(data, false);
- video_unregister_device(&data->vdev);
+ vb2_video_unregister_device(&data->vdev);
}
#ifdef CONFIG_PM
@@ -921,9 +922,9 @@ static const struct dev_pm_ops video_i2c_pm_ops = {
};
static const struct i2c_device_id video_i2c_id_table[] = {
- { "amg88xx", (kernel_ulong_t)&video_i2c_chip[AMG88XX] },
- { "mlx90640", (kernel_ulong_t)&video_i2c_chip[MLX90640] },
- {}
+ { .name = "amg88xx", .driver_data = (kernel_ulong_t)&video_i2c_chip[AMG88XX] },
+ { .name = "mlx90640", .driver_data = (kernel_ulong_t)&video_i2c_chip[MLX90640] },
+ { }
};
MODULE_DEVICE_TABLE(i2c, video_i2c_id_table);
diff --git a/drivers/media/i2c/vp27smpx.c b/drivers/media/i2c/vp27smpx.c
index df21950be24f..21fcfdd4c163 100644
--- a/drivers/media/i2c/vp27smpx.c
+++ b/drivers/media/i2c/vp27smpx.c
@@ -172,7 +172,7 @@ static void vp27smpx_remove(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
static const struct i2c_device_id vp27smpx_id[] = {
- { "vp27smpx" },
+ { .name = "vp27smpx" },
{ }
};
MODULE_DEVICE_TABLE(i2c, vp27smpx_id);
diff --git a/drivers/media/i2c/vpx3220.c b/drivers/media/i2c/vpx3220.c
index 5f1a22284168..29bcbb5a5fbb 100644
--- a/drivers/media/i2c/vpx3220.c
+++ b/drivers/media/i2c/vpx3220.c
@@ -535,9 +535,9 @@ static void vpx3220_remove(struct i2c_client *client)
}
static const struct i2c_device_id vpx3220_id[] = {
- { "vpx3220a" },
- { "vpx3216b" },
- { "vpx3214c" },
+ { .name = "vpx3220a" },
+ { .name = "vpx3216b" },
+ { .name = "vpx3214c" },
{ }
};
MODULE_DEVICE_TABLE(i2c, vpx3220_id);
diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c
index 72eb10339d06..db62bafb447c 100644
--- a/drivers/media/i2c/wm8739.c
+++ b/drivers/media/i2c/wm8739.c
@@ -243,7 +243,7 @@ static void wm8739_remove(struct i2c_client *client)
}
static const struct i2c_device_id wm8739_id[] = {
- { "wm8739" },
+ { .name = "wm8739" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8739_id);
diff --git a/drivers/media/i2c/wm8775.c b/drivers/media/i2c/wm8775.c
index 56778d3bc28a..5db197bb6fda 100644
--- a/drivers/media/i2c/wm8775.c
+++ b/drivers/media/i2c/wm8775.c
@@ -289,7 +289,7 @@ static void wm8775_remove(struct i2c_client *client)
}
static const struct i2c_device_id wm8775_id[] = {
- { "wm8775" },
+ { .name = "wm8775" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8775_id);
diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig
index eebb16c58f3d..bfdb200f85a3 100644
--- a/drivers/media/pci/Kconfig
+++ b/drivers/media/pci/Kconfig
@@ -13,6 +13,7 @@ if MEDIA_PCI_SUPPORT
if MEDIA_CAMERA_SUPPORT
comment "Media capture support"
+source "drivers/media/pci/hws/Kconfig"
source "drivers/media/pci/mgb4/Kconfig"
source "drivers/media/pci/solo6x10/Kconfig"
source "drivers/media/pci/tw5864/Kconfig"
diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile
index 02763ad88511..c4508b6723a9 100644
--- a/drivers/media/pci/Makefile
+++ b/drivers/media/pci/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
obj-$(CONFIG_VIDEO_CX25821) += cx25821/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_DT3155) += dt3155/
+obj-$(CONFIG_VIDEO_HWS) += hws/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_MGB4) += mgb4/
obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 4a8af8b88d84..9b92e8db494c 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -1002,8 +1002,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
}
/* PCIe stuff */
- dev->lmmio = ioremap(pci_resource_start(dev->pci, 0),
- pci_resource_len(dev->pci, 0));
+ dev->lmmio = pci_ioremap_bar(dev->pci, 0);
+ if (!dev->lmmio) {
+ dev_err(&dev->pci->dev, "CORE %s: can't ioremap MMIO memory\n",
+ dev->name);
+ goto err_release_region;
+ }
dev->bmmio = (u8 __iomem *)dev->lmmio;
@@ -1109,6 +1113,12 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
}
return 0;
+
+err_release_region:
+ release_mem_region(pci_resource_start(dev->pci, 0),
+ pci_resource_len(dev->pci, 0));
+ cx23885_devcount--;
+ return -ENODEV;
}
static void cx23885_dev_unregister(struct cx23885_dev *dev)
diff --git a/drivers/media/pci/dm1105/dm1105.c b/drivers/media/pci/dm1105/dm1105.c
index bbd24769ae56..80d2e143384b 100644
--- a/drivers/media/pci/dm1105/dm1105.c
+++ b/drivers/media/pci/dm1105/dm1105.c
@@ -768,6 +768,8 @@ static void dm1105_ir_exit(struct dm1105_dev *dm1105)
static int dm1105_hw_init(struct dm1105_dev *dev)
{
+ int ret;
+
dm1105_disable_irqs(dev);
dm_writeb(DM1105_HOST_CTR, 0);
@@ -778,7 +780,10 @@ static int dm1105_hw_init(struct dm1105_dev *dev)
dm_writew(DM1105_TSCTR, 0xc10a);
/* map DMA and set address */
- dm1105_dma_map(dev);
+ ret = dm1105_dma_map(dev);
+ if (ret)
+ return -ENOMEM;
+
dm1105_set_dma_addr(dev);
/* big buffer */
dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES);
@@ -1194,6 +1199,7 @@ static void dm1105_remove(struct pci_dev *pdev)
dm1105_hw_exit(dev);
free_irq(pdev->irq, dev);
+ destroy_workqueue(dev->wq);
pci_iounmap(pdev, dev->io_mem);
pci_release_regions(pdev);
pci_disable_device(pdev);
diff --git a/drivers/media/pci/hws/Kconfig b/drivers/media/pci/hws/Kconfig
new file mode 100644
index 000000000000..ab0bbf49ca71
--- /dev/null
+++ b/drivers/media/pci/hws/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config VIDEO_HWS
+ tristate "AVMatrix HWS PCIe capture devices"
+ depends on PCI && VIDEO_DEV
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ Enable support for AVMatrix HWS series multi-channel PCIe capture
+ devices that provide HDMI video capture.
+
+ To compile this driver as a module, choose M here: the module
+ will be named hws.
diff --git a/drivers/media/pci/hws/Makefile b/drivers/media/pci/hws/Makefile
new file mode 100644
index 000000000000..f9c7dc4f2d8d
--- /dev/null
+++ b/drivers/media/pci/hws/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+hws-objs := hws_pci.o hws_irq.o hws_video.o hws_v4l2_ioctl.o
+
+obj-$(CONFIG_VIDEO_HWS) += hws.o
diff --git a/drivers/media/pci/hws/hws.h b/drivers/media/pci/hws/hws.h
new file mode 100644
index 000000000000..8fbe1fe27844
--- /dev/null
+++ b/drivers/media/pci/hws/hws.h
@@ -0,0 +1,173 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef HWS_PCIE_H
+#define HWS_PCIE_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/dma-mapping.h>
+#include <linux/kthread.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/sizes.h>
+#include <linux/atomic.h>
+
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/videobuf2-dma-sg.h>
+
+#include "hws_reg.h"
+
+struct hwsmem_param {
+ u32 index;
+ u32 type;
+ u32 status;
+};
+
+struct hws_pix_state {
+ u32 width;
+ u32 height;
+ u32 fourcc; /* V4L2_PIX_FMT_* (YUYV only here) */
+ u32 bytesperline; /* stride */
+ u32 sizeimage; /* full frame */
+ enum v4l2_field field; /* V4L2_FIELD_NONE or INTERLACED */
+ enum v4l2_colorspace colorspace; /* e.g., REC709 */
+ enum v4l2_ycbcr_encoding ycbcr_enc; /* V4L2_YCBCR_ENC_DEFAULT */
+ enum v4l2_quantization quantization; /* V4L2_QUANTIZATION_LIM_RANGE */
+ enum v4l2_xfer_func xfer_func; /* V4L2_XFER_FUNC_DEFAULT */
+ bool interlaced; /* cached hardware state */
+ u32 half_size; /* hardware half-frame size */
+};
+
+#define UNSET (-1U)
+
+struct hws_pcie_dev;
+struct hws_adapter;
+struct hws_video;
+
+struct hwsvideo_buffer {
+ struct vb2_v4l2_buffer vb;
+ struct list_head list;
+ int slot;
+};
+
+struct hws_video {
+ /* Linkage */
+ struct hws_pcie_dev *parent;
+ struct video_device *video_device;
+
+ struct vb2_queue buffer_queue;
+ struct list_head capture_queue;
+ struct hwsvideo_buffer *active;
+ struct hwsvideo_buffer *next_prepared;
+
+ /* Locking */
+ struct mutex state_lock;
+ spinlock_t irq_lock; /* Protects capture_queue and active buffers. */
+
+ /* Indices */
+ int channel_index;
+
+ /* Color controls */
+ int current_brightness;
+ int current_contrast;
+ int current_saturation;
+ int current_hue;
+
+ /* V4L2 controls */
+ struct v4l2_ctrl_handler control_handler;
+ struct v4l2_ctrl *ctrl_brightness;
+ struct v4l2_ctrl *ctrl_contrast;
+ struct v4l2_ctrl *ctrl_saturation;
+ struct v4l2_ctrl *ctrl_hue;
+
+ /* Capture queue status */
+ struct hws_pix_state pix;
+ struct v4l2_dv_timings cur_dv_timings; /* last configured/notified DV timings */
+ u32 current_fps; /* Hz, updated by mode changes, not by read-only queries */
+
+ /* Per-channel capture state */
+ bool cap_active;
+ bool stop_requested;
+ u8 last_buf_half_toggle;
+ bool half_seen;
+ atomic_t sequence_number;
+ u32 queued_count;
+
+ /* Timeout and error handling */
+ u32 timeout_count;
+ u32 error_count;
+
+ bool window_valid;
+ u32 last_dma_hi;
+ u32 last_dma_page;
+ u32 last_pci_addr;
+ u32 last_half16;
+
+ /* Misc counters */
+ int signal_loss_cnt;
+};
+
+static inline void hws_set_current_dv_timings(struct hws_video *vid,
+ u32 width, u32 height,
+ bool interlaced)
+{
+ if (!vid)
+ return;
+
+ vid->cur_dv_timings = (struct v4l2_dv_timings) {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = width,
+ .height = height,
+ .interlaced = interlaced,
+ },
+ };
+}
+
+struct hws_scratch_dma {
+ void *cpu;
+ dma_addr_t dma;
+ size_t size;
+};
+
+struct hws_pcie_dev {
+ /* Core objects */
+ struct pci_dev *pdev;
+ struct hws_video video[MAX_VID_CHANNELS];
+
+ /* BAR and workqueues */
+ void __iomem *bar0_base;
+
+ /* Device identity and capabilities */
+ u16 vendor_id;
+ u16 device_id;
+ u16 device_ver;
+ u16 hw_ver;
+ u32 sub_ver;
+ u32 port_id;
+ /* Tri-state support flag used by set_video_format_size(). */
+ u32 support_yv12;
+ u32 max_hw_video_buf_sz;
+ u8 max_channels;
+ u8 cur_max_video_ch;
+ bool start_run;
+
+ bool buf_allocated;
+
+ /* V4L2 framework objects */
+ struct v4l2_device v4l2_device;
+
+ /* Kernel thread */
+ struct task_struct *main_task;
+ struct hws_scratch_dma scratch_vid[MAX_VID_CHANNELS];
+
+ bool suspended;
+ int irq;
+
+ /* Error flags */
+ int pci_lost;
+};
+
+#endif
diff --git a/drivers/media/pci/hws/hws_irq.c b/drivers/media/pci/hws/hws_irq.c
new file mode 100644
index 000000000000..eebb4b8a5cd5
--- /dev/null
+++ b/drivers/media/pci/hws/hws_irq.c
@@ -0,0 +1,269 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/compiler.h>
+#include <linux/moduleparam.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/minmax.h>
+#include <linux/string.h>
+
+#include <media/videobuf2-dma-contig.h>
+
+#include "hws_irq.h"
+#include "hws_reg.h"
+#include "hws_video.h"
+#include "hws.h"
+
+#define MAX_INT_LOOPS 100
+
+static bool hws_toggle_debug;
+module_param_named(toggle_debug, hws_toggle_debug, bool, 0644);
+MODULE_PARM_DESC(toggle_debug,
+ "Read toggle registers in IRQ handler for debug logging");
+
+static int hws_arm_next(struct hws_pcie_dev *hws, u32 ch)
+{
+ struct hws_video *v = &hws->video[ch];
+ unsigned long flags;
+ struct hwsvideo_buffer *buf;
+
+ dev_dbg(&hws->pdev->dev,
+ "arm_next(ch=%u): stop=%d cap=%d queued=%d\n",
+ ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active),
+ !list_empty(&v->capture_queue));
+
+ if (READ_ONCE(hws->suspended)) {
+ dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): suspended\n", ch);
+ return -EBUSY;
+ }
+
+ if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active)) {
+ dev_dbg(&hws->pdev->dev,
+ "arm_next(ch=%u): stop=%d cap=%d -> cancel\n", ch,
+ v->stop_requested, v->cap_active);
+ return -ECANCELED;
+ }
+
+ spin_lock_irqsave(&v->irq_lock, flags);
+ if (list_empty(&v->capture_queue)) {
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+ dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): queue empty\n", ch);
+ return -EAGAIN;
+ }
+
+ buf = list_first_entry(&v->capture_queue, struct hwsvideo_buffer, list);
+ list_del_init(&buf->list); /* keep buffer safe for later cleanup */
+ if (v->queued_count)
+ v->queued_count--;
+ v->active = buf;
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+ dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): picked buffer %p\n", ch,
+ buf);
+
+ /* Publish descriptor(s) before doorbell/MMIO kicks. */
+ wmb();
+
+ /* Avoid MMIO during suspend */
+ if (READ_ONCE(hws->suspended)) {
+ unsigned long f;
+
+ dev_dbg(&hws->pdev->dev,
+ "arm_next(ch=%u): suspended after pick\n", ch);
+ spin_lock_irqsave(&v->irq_lock, f);
+ if (v->active) {
+ list_add(&buf->list, &v->capture_queue);
+ v->queued_count++;
+ v->active = NULL;
+ }
+ spin_unlock_irqrestore(&v->irq_lock, f);
+ return -EBUSY;
+ }
+
+ /* Also program the DMA address register directly */
+ {
+ dma_addr_t dma_addr =
+ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ hws_program_dma_for_addr(hws, ch, dma_addr);
+ iowrite32(lower_32_bits(dma_addr),
+ hws->bar0_base + HWS_REG_DMA_ADDR(ch));
+ }
+
+ dev_dbg(&hws->pdev->dev, "arm_next(ch=%u): programmed buffer %p\n", ch,
+ buf);
+ spin_lock_irqsave(&v->irq_lock, flags);
+ hws_prime_next_locked(v);
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+ return 0;
+}
+
+static void hws_video_handle_vdone(struct hws_video *v)
+{
+ struct hws_pcie_dev *hws = v->parent;
+ unsigned int ch = v->channel_index;
+ struct hwsvideo_buffer *done;
+ unsigned long flags;
+ bool promoted = false;
+
+ dev_dbg(&hws->pdev->dev,
+ "bh_video(ch=%u): stop=%d cap=%d active=%p\n",
+ ch, READ_ONCE(v->stop_requested), READ_ONCE(v->cap_active),
+ v->active);
+
+ int ret;
+
+ dev_dbg(&hws->pdev->dev,
+ "bh_video(ch=%u): entry stop=%d cap=%d\n", ch,
+ v->stop_requested, v->cap_active);
+ if (READ_ONCE(hws->suspended))
+ return;
+
+ if (READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active))
+ return;
+
+ spin_lock_irqsave(&v->irq_lock, flags);
+ done = v->active;
+ if (done && v->next_prepared) {
+ v->active = v->next_prepared;
+ v->next_prepared = NULL;
+ promoted = true;
+ }
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+
+ /* 1) Complete the buffer the HW just finished (if any) */
+ if (done) {
+ struct vb2_v4l2_buffer *vb2v = &done->vb;
+ size_t expected = v->pix.sizeimage;
+ size_t plane_size = vb2_plane_size(&vb2v->vb2_buf, 0);
+
+ if (expected > plane_size) {
+ dev_warn_ratelimited(&hws->pdev->dev,
+ "bh_video(ch=%u): sizeimage %zu > plane %zu, dropping seq=%u\n",
+ ch, expected, plane_size,
+ (u32)atomic_read(&v->sequence_number) + 1);
+ vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_ERROR);
+ goto arm_next;
+ }
+ vb2_set_plane_payload(&vb2v->vb2_buf, 0, expected);
+
+ dma_rmb(); /* device writes visible before userspace sees it */
+
+ vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number);
+ vb2v->vb2_buf.timestamp = ktime_get_ns();
+ dev_dbg(&hws->pdev->dev,
+ "bh_video(ch=%u): DONE buf=%p seq=%u half_seen=%d toggle=%u\n",
+ ch, done, vb2v->sequence, v->half_seen,
+ v->last_buf_half_toggle);
+
+ if (!promoted)
+ v->active = NULL; /* channel no longer owns this buffer */
+ vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE);
+ }
+
+ if (READ_ONCE(hws->suspended))
+ return;
+
+ if (promoted) {
+ dev_dbg(&hws->pdev->dev,
+ "bh_video(ch=%u): promoted pre-armed buffer active=%p\n",
+ ch, v->active);
+ spin_lock_irqsave(&v->irq_lock, flags);
+ hws_prime_next_locked(v);
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+ return;
+ }
+
+arm_next:
+ /* 2) Immediately arm the next queued buffer (if present) */
+ ret = hws_arm_next(hws, ch);
+ if (ret == -EAGAIN) {
+ dev_dbg(&hws->pdev->dev,
+ "bh_video(ch=%u): no queued buffer to arm\n", ch);
+ return;
+ }
+ dev_dbg(&hws->pdev->dev,
+ "bh_video(ch=%u): armed next buffer, active=%p\n", ch,
+ v->active);
+ /* On success the engine now points at v->active's DMA address */
+}
+
+irqreturn_t hws_irq_handler(int irq, void *info)
+{
+ struct hws_pcie_dev *pdx = info;
+ u32 int_state;
+
+ dev_dbg(&pdx->pdev->dev, "irq: entry\n");
+ if (pdx->bar0_base) {
+ dev_dbg(&pdx->pdev->dev,
+ "irq: INT_EN=0x%08x INT_STATUS=0x%08x\n",
+ readl(pdx->bar0_base + INT_EN_REG_BASE),
+ readl(pdx->bar0_base + HWS_REG_INT_STATUS));
+ }
+
+ /* Fast path: if suspended, quietly ack and exit */
+ if (READ_ONCE(pdx->suspended)) {
+ int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS);
+ if (int_state) {
+ writel(int_state, pdx->bar0_base + HWS_REG_INT_STATUS);
+ (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS);
+ }
+ return int_state ? IRQ_HANDLED : IRQ_NONE;
+ }
+ int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS);
+ if (!int_state || int_state == 0xFFFFFFFF) {
+ dev_dbg(&pdx->pdev->dev,
+ "irq: spurious or device-gone int_state=0x%08x\n",
+ int_state);
+ return IRQ_NONE;
+ }
+ dev_dbg(&pdx->pdev->dev, "irq: entry INT_STATUS=0x%08x\n", int_state);
+
+ /* Loop until all pending bits are serviced (max 100 iterations) */
+ for (u32 cnt = 0; int_state && cnt < MAX_INT_LOOPS; ++cnt) {
+ for (unsigned int ch = 0; ch < pdx->cur_max_video_ch; ++ch) {
+ u32 vbit = HWS_INT_VDONE_BIT(ch);
+
+ if (!(int_state & vbit))
+ continue;
+
+ if (READ_ONCE(pdx->video[ch].cap_active) &&
+ !READ_ONCE(pdx->video[ch].stop_requested)) {
+ if (hws_toggle_debug) {
+ u32 toggle =
+ readl_relaxed(pdx->bar0_base +
+ HWS_REG_VBUF_TOGGLE(ch)) & 0x01;
+ WRITE_ONCE(pdx->video[ch].last_buf_half_toggle,
+ toggle);
+ }
+ dma_rmb();
+ WRITE_ONCE(pdx->video[ch].half_seen, true);
+ dev_dbg(&pdx->pdev->dev,
+ "irq: VDONE ch=%u toggle=%u handling inline (cap=%d)\n",
+ ch,
+ READ_ONCE(pdx->video[ch].last_buf_half_toggle),
+ READ_ONCE(pdx->video[ch].cap_active));
+ hws_video_handle_vdone(&pdx->video[ch]);
+ } else {
+ dev_dbg(&pdx->pdev->dev,
+ "irq: VDONE ch=%u ignored (cap=%d stop=%d)\n",
+ ch,
+ READ_ONCE(pdx->video[ch].cap_active),
+ READ_ONCE(pdx->video[ch].stop_requested));
+ }
+
+ writel(vbit, pdx->bar0_base + HWS_REG_INT_STATUS);
+ (void)readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS);
+ }
+
+ /* Re-read in case new interrupt bits popped while processing */
+ int_state = readl_relaxed(pdx->bar0_base + HWS_REG_INT_STATUS);
+ dev_dbg(&pdx->pdev->dev,
+ "irq: loop cnt=%u new INT_STATUS=0x%08x\n", cnt,
+ int_state);
+ if (cnt + 1 == MAX_INT_LOOPS)
+ dev_warn_ratelimited(&pdx->pdev->dev,
+ "IRQ storm? status=0x%08x\n",
+ int_state);
+ }
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/pci/hws/hws_irq.h b/drivers/media/pci/hws/hws_irq.h
new file mode 100644
index 000000000000..a42867aa0c46
--- /dev/null
+++ b/drivers/media/pci/hws/hws_irq.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef HWS_INTERRUPT_H
+#define HWS_INTERRUPT_H
+
+#include <linux/pci.h>
+#include "hws.h"
+
+irqreturn_t hws_irq_handler(int irq, void *info);
+
+#endif /* HWS_INTERRUPT_H */
diff --git a/drivers/media/pci/hws/hws_pci.c b/drivers/media/pci/hws/hws_pci.c
new file mode 100644
index 000000000000..30bb7d34465b
--- /dev/null
+++ b/drivers/media/pci/hws/hws_pci.c
@@ -0,0 +1,865 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/iopoll.h>
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kthread.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/ktime.h>
+#include <linux/math64.h>
+#include <linux/pm.h>
+#include <linux/freezer.h>
+#include <linux/pci_regs.h>
+
+#include <media/v4l2-ctrls.h>
+
+#include "hws.h"
+#include "hws_reg.h"
+#include "hws_video.h"
+#include "hws_irq.h"
+#include "hws_v4l2_ioctl.h"
+
+#define DRV_NAME "hws"
+#define HWS_BUSY_POLL_DELAY_US 10
+#define HWS_BUSY_POLL_TIMEOUT_US 1000000
+
+static unsigned long long hws_elapsed_us(u64 start_ns)
+{
+ return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000);
+}
+
+/* register layout inside HWS_REG_DEVICE_INFO */
+#define DEVINFO_VER GENMASK(7, 0)
+#define DEVINFO_SUBVER GENMASK(15, 8)
+#define DEVINFO_YV12 GENMASK(31, 28)
+#define DEVINFO_HWKEY GENMASK(27, 24)
+#define DEVINFO_PORTID GENMASK(25, 24) /* low 2 bits of HW-key */
+
+#define MAKE_ENTRY(__vend, __chip, __subven, __subdev, __configptr) \
+ { .vendor = (__vend), \
+ .device = (__chip), \
+ .subvendor = (__subven), \
+ .subdevice = (__subdev), \
+ .driver_data = (unsigned long)(__configptr) }
+
+/*
+ * PCI IDs for HWS family cards.
+ *
+ * The subsystem IDs are fixed at 0x8888:0x0007 for this family. Some boards
+ * enumerate with vendor ID 0x8888 or 0x1f33. Exact SKU names are not fully
+ * pinned down yet; update these comments when vendor documentation or INF
+ * strings are available.
+ */
+static const struct pci_device_id hws_pci_table[] = {
+ /* HWS family, SKU unknown. */
+ MAKE_ENTRY(0x8888, 0x9534, 0x8888, 0x0007, NULL),
+ MAKE_ENTRY(0x1F33, 0x8534, 0x8888, 0x0007, NULL),
+ MAKE_ENTRY(0x1F33, 0x8554, 0x8888, 0x0007, NULL),
+
+ /* HWS 2x2 HDMI family. */
+ MAKE_ENTRY(0x8888, 0x8524, 0x8888, 0x0007, NULL),
+ /* HWS 2x2 SDI family. */
+ MAKE_ENTRY(0x1F33, 0x6524, 0x8888, 0x0007, NULL),
+
+ /* HWS X4 HDMI family. */
+ MAKE_ENTRY(0x8888, 0x8504, 0x8888, 0x0007, NULL),
+ /* HWS X4 SDI family. */
+ MAKE_ENTRY(0x8888, 0x6504, 0x8888, 0x0007, NULL),
+
+ /* HWS family, SKU unknown. */
+ MAKE_ENTRY(0x8888, 0x8532, 0x8888, 0x0007, NULL),
+ MAKE_ENTRY(0x8888, 0x8512, 0x8888, 0x0007, NULL),
+ MAKE_ENTRY(0x8888, 0x8501, 0x8888, 0x0007, NULL),
+ MAKE_ENTRY(0x1F33, 0x6502, 0x8888, 0x0007, NULL),
+
+ /* HWS X4 HDMI family (alternate vendor ID). */
+ MAKE_ENTRY(0x1F33, 0x8504, 0x8888, 0x0007, NULL),
+ /* HWS 2x2 HDMI family (alternate vendor ID). */
+ MAKE_ENTRY(0x1F33, 0x8524, 0x8888, 0x0007, NULL),
+
+ {}
+};
+
+static void enable_pcie_relaxed_ordering(struct pci_dev *dev)
+{
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
+}
+
+static void hws_configure_hardware_capabilities(struct hws_pcie_dev *hdev)
+{
+ u16 id = hdev->device_id;
+
+ /* select per-chip channel counts */
+ switch (id) {
+ case 0x9534:
+ case 0x6524:
+ case 0x8524:
+ case 0x8504:
+ case 0x6504:
+ hdev->cur_max_video_ch = 4;
+ break;
+ case 0x8532:
+ hdev->cur_max_video_ch = 2;
+ break;
+ case 0x8512:
+ case 0x6502:
+ hdev->cur_max_video_ch = 2;
+ break;
+ case 0x8501:
+ hdev->cur_max_video_ch = 1;
+ break;
+ default:
+ hdev->cur_max_video_ch = 4;
+ break;
+ }
+
+ /* universal buffer capacity */
+ hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE;
+
+ /* decide hardware-version and program DMA max size if needed */
+ if (hdev->device_ver > 121) {
+ if (id == 0x8501 && hdev->device_ver == 122) {
+ hdev->hw_ver = 0;
+ } else {
+ hdev->hw_ver = 1;
+ u32 dma_max = (u32)(MAX_VIDEO_SCALER_SIZE / 16);
+
+ writel(dma_max, hdev->bar0_base + HWS_REG_DMA_MAX_SIZE);
+ /* readback to flush posted MMIO write */
+ (void)readl(hdev->bar0_base + HWS_REG_DMA_MAX_SIZE);
+ }
+ } else {
+ hdev->hw_ver = 0;
+ }
+}
+
+static void hws_stop_device(struct hws_pcie_dev *hws);
+
+static void hws_log_lifecycle_snapshot(struct hws_pcie_dev *hws,
+ const char *action,
+ const char *phase)
+{
+ struct device *dev;
+ u32 int_en, int_status, vcap, sys_status, dec_mode;
+
+ if (!hws || !hws->pdev)
+ return;
+
+ dev = &hws->pdev->dev;
+ if (!hws->bar0_base) {
+ dev_dbg(dev,
+ "lifecycle:%s:%s bar0-unmapped suspended=%d start_run=%d pci_lost=%d irq=%d\n",
+ action, phase, READ_ONCE(hws->suspended), hws->start_run,
+ hws->pci_lost, hws->irq);
+ return;
+ }
+
+ int_en = readl(hws->bar0_base + INT_EN_REG_BASE);
+ int_status = readl(hws->bar0_base + HWS_REG_INT_STATUS);
+ vcap = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE);
+ sys_status = readl(hws->bar0_base + HWS_REG_SYS_STATUS);
+ dec_mode = readl(hws->bar0_base + HWS_REG_DEC_MODE);
+
+ dev_dbg(dev,
+ "lifecycle:%s:%s suspended=%d start_run=%d pci_lost=%d irq=%d INT_EN=0x%08x INT_STATUS=0x%08x VCAP=0x%08x SYS=0x%08x DEC=0x%08x\n",
+ action, phase, READ_ONCE(hws->suspended), hws->start_run,
+ hws->pci_lost, hws->irq, int_en, int_status, vcap,
+ sys_status, dec_mode);
+}
+
+static int read_chip_id(struct hws_pcie_dev *hdev)
+{
+ u32 reg;
+ /* mirror PCI IDs for later switches */
+ hdev->device_id = hdev->pdev->device;
+ hdev->vendor_id = hdev->pdev->vendor;
+
+ reg = readl(hdev->bar0_base + HWS_REG_DEVICE_INFO);
+
+ hdev->device_ver = FIELD_GET(DEVINFO_VER, reg);
+ hdev->sub_ver = FIELD_GET(DEVINFO_SUBVER, reg);
+ hdev->support_yv12 = FIELD_GET(DEVINFO_YV12, reg);
+ hdev->port_id = FIELD_GET(DEVINFO_PORTID, reg);
+
+ hdev->max_hw_video_buf_sz = MAX_MM_VIDEO_SIZE;
+ hdev->max_channels = 4;
+ hdev->buf_allocated = false;
+ hdev->main_task = NULL;
+ hdev->start_run = false;
+ hdev->pci_lost = 0;
+
+ writel(0x00, hdev->bar0_base + HWS_REG_DEC_MODE);
+ writel(0x10, hdev->bar0_base + HWS_REG_DEC_MODE);
+
+ hws_configure_hardware_capabilities(hdev);
+
+ dev_info(&hdev->pdev->dev,
+ "chip detected: ver=%u subver=%u port=%u yv12=%u\n",
+ hdev->device_ver, hdev->sub_ver, hdev->port_id,
+ hdev->support_yv12);
+
+ return 0;
+}
+
+static int main_ks_thread_handle(void *data)
+{
+ struct hws_pcie_dev *pdx = data;
+
+ set_freezable();
+
+ while (!kthread_should_stop()) {
+ /* If we're suspending, don't touch hardware; just sleep/freeze. */
+ if (READ_ONCE(pdx->suspended)) {
+ try_to_freeze();
+ schedule_timeout_interruptible(msecs_to_jiffies(1000));
+ continue;
+ }
+
+ /* avoid MMIO when suspended (guarded above) */
+ check_video_format(pdx);
+
+ try_to_freeze(); /* cooperate with freezer each loop */
+
+ /* Sleep 1s or until signaled to wake/stop */
+ schedule_timeout_interruptible(msecs_to_jiffies(1000));
+ }
+
+ dev_dbg(&pdx->pdev->dev, "%s: exiting\n", __func__);
+ return 0;
+}
+
+static void hws_stop_kthread_action(void *data)
+{
+ struct hws_pcie_dev *hws = data;
+ struct task_struct *t;
+ u64 start_ns;
+
+ if (!hws)
+ return;
+
+ t = READ_ONCE(hws->main_task);
+ if (!IS_ERR_OR_NULL(t)) {
+ start_ns = ktime_get_mono_fast_ns();
+ dev_dbg(&hws->pdev->dev,
+ "lifecycle:kthread-stop:begin task=%s[%d]\n",
+ t->comm, t->pid);
+ WRITE_ONCE(hws->main_task, NULL);
+ kthread_stop(t);
+ dev_dbg(&hws->pdev->dev,
+ "lifecycle:kthread-stop:done (%lluus)\n",
+ hws_elapsed_us(start_ns));
+ }
+}
+
+static int hws_alloc_seed_buffers(struct hws_pcie_dev *hws)
+{
+ int ch;
+ /* 64 KiB is plenty for a safe dummy; hardware needs 64-byte alignment. */
+ const size_t need = ALIGN(64 * 1024, 64);
+
+ for (ch = 0; ch < hws->cur_max_video_ch; ch++) {
+#if defined(CONFIG_HAS_DMA) /* normal on PCIe platforms */
+ void *cpu = dma_alloc_coherent(&hws->pdev->dev, need,
+ &hws->scratch_vid[ch].dma,
+ GFP_KERNEL);
+#else
+ void *cpu = NULL;
+#endif
+ if (!cpu) {
+ dev_warn(&hws->pdev->dev,
+ "scratch: dma_alloc_coherent failed ch=%d\n", ch);
+ /* not fatal: free earlier ones and continue without seeding */
+ while (--ch >= 0) {
+ if (hws->scratch_vid[ch].cpu)
+ dma_free_coherent(&hws->pdev->dev,
+ hws->scratch_vid[ch].size,
+ hws->scratch_vid[ch].cpu,
+ hws->scratch_vid[ch].dma);
+ hws->scratch_vid[ch].cpu = NULL;
+ hws->scratch_vid[ch].size = 0;
+ }
+ return -ENOMEM;
+ }
+ hws->scratch_vid[ch].cpu = cpu;
+ hws->scratch_vid[ch].size = need;
+ }
+ return 0;
+}
+
+static void hws_free_seed_buffers(struct hws_pcie_dev *hws)
+{
+ int ch;
+
+ for (ch = 0; ch < hws->cur_max_video_ch; ch++) {
+ if (hws->scratch_vid[ch].cpu) {
+ dma_free_coherent(&hws->pdev->dev,
+ hws->scratch_vid[ch].size,
+ hws->scratch_vid[ch].cpu,
+ hws->scratch_vid[ch].dma);
+ hws->scratch_vid[ch].cpu = NULL;
+ hws->scratch_vid[ch].size = 0;
+ }
+ }
+}
+
+static void hws_seed_channel(struct hws_pcie_dev *hws, int ch)
+{
+ dma_addr_t paddr = hws->scratch_vid[ch].dma;
+ u32 lo = lower_32_bits(paddr);
+ u32 hi = upper_32_bits(paddr);
+ u32 pci_addr = lo & PCI_E_BAR_ADD_LOWMASK;
+
+ lo &= PCI_E_BAR_ADD_MASK;
+
+ /* Program 64-bit BAR remap entry for this channel (table @ 0x208 + ch * 8) */
+ writel_relaxed(hi, hws->bar0_base +
+ PCI_ADDR_TABLE_BASE + 0x208 + ch * 8);
+ writel_relaxed(lo, hws->bar0_base +
+ PCI_ADDR_TABLE_BASE + 0x208 + ch * 8 +
+ PCIE_BARADDROFSIZE);
+
+ /* Program capture engine per-channel base/half */
+ writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE + pci_addr,
+ hws->bar0_base + CVBS_IN_BUF_BASE +
+ ch * PCIE_BARADDROFSIZE);
+
+ /* Half size: use either the current format's half or half of scratch. */
+ {
+ u32 half = hws->video[ch].pix.half_size ?
+ hws->video[ch].pix.half_size :
+ (u32)(hws->scratch_vid[ch].size / 2);
+
+ writel_relaxed(half / 16,
+ hws->bar0_base + CVBS_IN_BUF_BASE2 +
+ ch * PCIE_BARADDROFSIZE);
+ }
+
+ (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush posted writes */
+}
+
+static void hws_seed_all_channels(struct hws_pcie_dev *hws)
+{
+ int ch;
+
+ for (ch = 0; ch < hws->cur_max_video_ch; ch++) {
+ if (hws->scratch_vid[ch].cpu)
+ hws_seed_channel(hws, ch);
+ }
+}
+
+static void hws_irq_mask_gate(struct hws_pcie_dev *hws)
+{
+ writel(0x00000000, hws->bar0_base + INT_EN_REG_BASE);
+ (void)readl(hws->bar0_base + INT_EN_REG_BASE);
+}
+
+static void hws_irq_unmask_gate(struct hws_pcie_dev *hws)
+{
+ writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE);
+ (void)readl(hws->bar0_base + INT_EN_REG_BASE);
+}
+
+static void hws_irq_clear_pending(struct hws_pcie_dev *hws)
+{
+ u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS);
+
+ if (st) {
+ writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */
+ (void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
+ }
+}
+
+static void hws_block_hotpaths(struct hws_pcie_dev *hws)
+{
+ WRITE_ONCE(hws->suspended, true);
+ if (hws->irq >= 0)
+ disable_irq(hws->irq);
+
+ if (!hws->bar0_base)
+ return;
+
+ hws_irq_mask_gate(hws);
+ hws_irq_clear_pending(hws);
+}
+
+static int hws_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+{
+ struct hws_pcie_dev *hws;
+ int i, ret, irq;
+ unsigned long irqf = 0;
+ bool v4l2_registered = false;
+
+ /* devres-backed device object */
+ hws = devm_kzalloc(&pdev->dev, sizeof(*hws), GFP_KERNEL);
+ if (!hws)
+ return -ENOMEM;
+
+ hws->pdev = pdev;
+ hws->irq = -1;
+ hws->suspended = false;
+ pci_set_drvdata(pdev, hws);
+
+ /* 1) Enable device + bus mastering (managed) */
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "pcim_enable_device\n");
+ pci_set_master(pdev);
+
+ /* 2) Map BAR0 (managed) */
+ ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "pcim_iomap_regions BAR0\n");
+ hws->bar0_base = pcim_iomap_table(pdev)[0];
+
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "64-bit DMA mask unavailable, falling back to 32-bit (%d)\n",
+ ret);
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "No suitable DMA configuration\n");
+ } else {
+ dev_dbg(&pdev->dev, "Using 64-bit DMA mask\n");
+ }
+
+ /* 3) Apply optional PCIe tuning. */
+ enable_pcie_relaxed_ordering(pdev);
+#ifdef CONFIG_ARCH_TI816X
+ pcie_set_readrq(pdev, 128);
+#endif
+
+ /* 4) Identify chip & capabilities */
+ read_chip_id(hws);
+ dev_info(&pdev->dev, "Device VID=0x%04x DID=0x%04x\n",
+ pdev->vendor, pdev->device);
+ hws_init_video_sys(hws, false);
+
+ /* 5) Init channels (video state, locks, vb2, ctrls) */
+ for (i = 0; i < hws->max_channels; i++) {
+ ret = hws_video_init_channel(hws, i);
+ if (ret) {
+ dev_err(&pdev->dev, "video channel init failed (ch=%d)\n", i);
+ goto err_unwind_channels;
+ }
+ }
+
+ /* 6) Allocate scratch DMA and seed BAR table + channel base/half (legacy SetDMAAddress) */
+ ret = hws_alloc_seed_buffers(hws);
+ if (!ret)
+ hws_seed_all_channels(hws);
+
+ /* 7) Start-run sequence. */
+ hws_init_video_sys(hws, false);
+
+ /* A) Force legacy INTx; legacy used request_irq(pdev->irq, ..., IRQF_SHARED) */
+ pci_intx(pdev, 1);
+ irqf = IRQF_SHARED;
+ irq = pdev->irq;
+ hws->irq = irq;
+ dev_info(&pdev->dev, "IRQ mode: legacy INTx (shared), irq=%d\n", irq);
+
+ /* B) Mask the device's global/bridge gate (INT_EN_REG_BASE) */
+ hws_irq_mask_gate(hws);
+
+ /* C) Clear any sticky pending interrupt status (W1C) before we arm the line */
+ hws_irq_clear_pending(hws);
+
+ /* D) Request the legacy shared interrupt line (no vectors/MSI/MSI-X) */
+ ret = devm_request_irq(&pdev->dev, irq, hws_irq_handler, irqf,
+ dev_name(&pdev->dev), hws);
+ if (ret) {
+ dev_err(&pdev->dev, "request_irq(%d) failed: %d\n", irq, ret);
+ goto err_unwind_channels;
+ }
+
+ /* E) Set the global interrupt enable bit in main control register */
+ {
+ u32 ctl_reg = readl(hws->bar0_base + HWS_REG_CTL);
+
+ ctl_reg |= HWS_CTL_IRQ_ENABLE_BIT;
+ writel(ctl_reg, hws->bar0_base + HWS_REG_CTL);
+ (void)readl(hws->bar0_base + HWS_REG_CTL); /* flush write */
+ dev_info(&pdev->dev, "Global IRQ enable bit set in control register\n");
+ }
+
+ /* F) Open the global gate just like legacy did */
+ hws_irq_unmask_gate(hws);
+ dev_info(&pdev->dev, "INT_EN_GATE readback=0x%08x\n",
+ readl(hws->bar0_base + INT_EN_REG_BASE));
+
+ /* 11) Register V4L2 */
+ ret = hws_video_register(hws);
+ if (ret) {
+ dev_err(&pdev->dev, "video_register: %d\n", ret);
+ goto err_unwind_channels;
+ }
+ v4l2_registered = true;
+
+ /* 12) Background monitor thread (managed) */
+ hws->main_task = kthread_run(main_ks_thread_handle, hws, "hws-mon");
+ if (IS_ERR(hws->main_task)) {
+ ret = PTR_ERR(hws->main_task);
+ hws->main_task = NULL;
+ dev_err(&pdev->dev, "kthread_run: %d\n", ret);
+ goto err_unregister_va;
+ }
+ ret = devm_add_action_or_reset(&pdev->dev, hws_stop_kthread_action, hws);
+ if (ret) {
+ dev_err(&pdev->dev, "devm_add_action kthread_stop: %d\n", ret);
+ goto err_unregister_va; /* reset already stopped the thread */
+ }
+
+ /* 13) Final: show the line is armed */
+ dev_info(&pdev->dev, "irq handler installed on irq=%d\n", irq);
+ return 0;
+
+err_unregister_va:
+ hws_stop_device(hws);
+ hws_video_unregister(hws);
+ hws_free_seed_buffers(hws);
+ return ret;
+err_unwind_channels:
+ hws_free_seed_buffers(hws);
+ if (!v4l2_registered) {
+ while (--i >= 0)
+ hws_video_cleanup_channel(hws, i);
+ }
+ return ret;
+}
+
+static int hws_check_busy(struct hws_pcie_dev *pdx)
+{
+ void __iomem *reg = pdx->bar0_base + HWS_REG_SYS_STATUS;
+ u32 val;
+ int ret;
+
+ /* poll until !(val & BUSY_BIT), sleeping HWS_BUSY_POLL_DELAY_US between reads */
+ ret = readl_poll_timeout(reg, val, !(val & HWS_SYS_DMA_BUSY_BIT),
+ HWS_BUSY_POLL_DELAY_US,
+ HWS_BUSY_POLL_TIMEOUT_US);
+ if (ret) {
+ dev_err(&pdx->pdev->dev,
+ "SYS_STATUS busy bit never cleared (0x%08x)\n", val);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void hws_stop_dsp(struct hws_pcie_dev *hws)
+{
+ u32 status;
+
+ /* Read the decoder mode/status register */
+ status = readl(hws->bar0_base + HWS_REG_DEC_MODE);
+ dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status);
+
+ /* If the device looks unplugged/stuck, bail out */
+ if (status == 0xFFFFFFFF)
+ return;
+
+ /* Tell the DSP to stop */
+ writel(0x10, hws->bar0_base + HWS_REG_DEC_MODE);
+
+ if (hws_check_busy(hws))
+ dev_warn(&hws->pdev->dev, "DSP busy timeout on stop\n");
+ /* Disable video capture engine in the DSP */
+ writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE);
+}
+
+/* Publish stop so ISR/BH will not touch video buffers anymore. */
+static void hws_publish_stop_flags(struct hws_pcie_dev *hws)
+{
+ unsigned int i;
+
+ for (i = 0; i < hws->cur_max_video_ch; ++i) {
+ struct hws_video *v = &hws->video[i];
+
+ WRITE_ONCE(v->cap_active, false);
+ WRITE_ONCE(v->stop_requested, true);
+ }
+
+ smp_wmb(); /* make flags visible before we touch MMIO/queues */
+}
+
+/* Drain engines + ISR/BH after flags are published. */
+static void hws_drain_after_stop(struct hws_pcie_dev *hws)
+{
+ u32 ackmask = 0;
+ unsigned int i;
+ u64 start_ns = ktime_get_mono_fast_ns();
+
+ /* Mask device enables: no new DMA starts. */
+ writel(0x0, hws->bar0_base + HWS_REG_VCAP_ENABLE);
+ (void)readl(hws->bar0_base + HWS_REG_INT_STATUS); /* flush */
+
+ /* Let any in-flight DMAs finish (best-effort). */
+ (void)hws_check_busy(hws);
+
+ /* Ack any latched VDONE. */
+ for (i = 0; i < hws->cur_max_video_ch; ++i)
+ ackmask |= HWS_INT_VDONE_BIT(i);
+ if (ackmask) {
+ writel(ackmask, hws->bar0_base + HWS_REG_INT_STATUS);
+ (void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
+ }
+
+ /* Ensure no hard IRQ is still running. */
+ if (hws->irq >= 0)
+ synchronize_irq(hws->irq);
+
+ dev_dbg(&hws->pdev->dev, "lifecycle:drain-after-stop:done (%lluus)\n",
+ hws_elapsed_us(start_ns));
+}
+
+static void hws_stop_device(struct hws_pcie_dev *hws)
+{
+ u32 status = readl(hws->bar0_base + HWS_REG_SYS_STATUS);
+ u64 start_ns = ktime_get_mono_fast_ns();
+ bool live = status != 0xFFFFFFFF;
+
+ dev_dbg(&hws->pdev->dev, "%s: status=0x%08x\n", __func__, status);
+ if (!live) {
+ hws->pci_lost = true;
+ goto out;
+ }
+ hws_log_lifecycle_snapshot(hws, "stop-device", "begin");
+
+ /* Make ISR/BH a no-op, then drain engines/IRQ. */
+ hws_publish_stop_flags(hws);
+ hws_drain_after_stop(hws);
+
+ /* 1) Stop the on-board DSP */
+ hws_stop_dsp(hws);
+
+out:
+ hws->start_run = false;
+ if (live)
+ hws_log_lifecycle_snapshot(hws, "stop-device", "end");
+ else
+ dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:device-lost\n");
+ dev_dbg(&hws->pdev->dev, "lifecycle:stop-device:done (%lluus)\n",
+ hws_elapsed_us(start_ns));
+ dev_dbg(&hws->pdev->dev, "%s: complete\n", __func__);
+}
+
+static int hws_quiesce_for_transition(struct hws_pcie_dev *hws,
+ const char *action,
+ bool stop_thread)
+{
+ struct device *dev = &hws->pdev->dev;
+ u64 start_ns = ktime_get_mono_fast_ns();
+ u64 step_ns;
+ int vret;
+
+ hws_log_lifecycle_snapshot(hws, action, "begin");
+
+ step_ns = ktime_get_mono_fast_ns();
+ hws_block_hotpaths(hws);
+ dev_dbg(dev, "lifecycle:%s:block-hotpaths (%lluus)\n", action,
+ hws_elapsed_us(step_ns));
+ hws_log_lifecycle_snapshot(hws, action, "blocked");
+
+ if (stop_thread) {
+ step_ns = ktime_get_mono_fast_ns();
+ hws_stop_kthread_action(hws);
+ dev_dbg(dev, "lifecycle:%s:stop-kthread (%lluus)\n", action,
+ hws_elapsed_us(step_ns));
+ }
+
+ step_ns = ktime_get_mono_fast_ns();
+ vret = hws_video_quiesce(hws, action);
+ dev_dbg(dev, "lifecycle:%s:video-quiesce ret=%d (%lluus)\n", action,
+ vret, hws_elapsed_us(step_ns));
+ if (vret)
+ dev_warn(dev, "lifecycle:%s video quiesce returned %d\n",
+ action, vret);
+
+ step_ns = ktime_get_mono_fast_ns();
+ hws_stop_device(hws);
+ dev_dbg(dev, "lifecycle:%s:stop-device (%lluus)\n", action,
+ hws_elapsed_us(step_ns));
+ hws_log_lifecycle_snapshot(hws, action, "end");
+ dev_dbg(dev, "lifecycle:%s:quiesce-done ret=%d (%lluus)\n", action,
+ vret, hws_elapsed_us(start_ns));
+
+ return vret;
+}
+
+static void hws_remove(struct pci_dev *pdev)
+{
+ struct hws_pcie_dev *hws = pci_get_drvdata(pdev);
+ u64 start_ns;
+
+ if (!hws)
+ return;
+
+ start_ns = ktime_get_mono_fast_ns();
+ dev_info(&pdev->dev, "lifecycle:remove begin\n");
+ hws_log_lifecycle_snapshot(hws, "remove", "begin");
+
+ /* Stop the monitor thread before tearing down V4L2/vb2 objects. */
+ hws_block_hotpaths(hws);
+ hws_stop_kthread_action(hws);
+
+ /* Stop hardware and capture cleanly. */
+ hws_stop_device(hws);
+
+ /* Unregister V4L2 resources. */
+ hws_video_unregister(hws);
+
+ /* Release seeded DMA buffers */
+ hws_free_seed_buffers(hws);
+ /* kthread is stopped by the devm action registered in probe. */
+ hws_log_lifecycle_snapshot(hws, "remove", "end");
+ dev_info(&pdev->dev, "lifecycle:remove done (%lluus)\n",
+ hws_elapsed_us(start_ns));
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hws_pm_suspend(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct hws_pcie_dev *hws = pci_get_drvdata(pdev);
+ int vret;
+ u64 start_ns = ktime_get_mono_fast_ns();
+ u64 step_ns;
+
+ dev_info(dev, "lifecycle:pm_suspend begin\n");
+ vret = hws_quiesce_for_transition(hws, "pm_suspend", false);
+
+ step_ns = ktime_get_mono_fast_ns();
+ pci_save_state(pdev);
+ pci_clear_master(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, PCI_D3hot);
+ dev_dbg(dev, "lifecycle:pm_suspend:pci-d3hot (%lluus)\n",
+ hws_elapsed_us(step_ns));
+ dev_info(dev, "lifecycle:pm_suspend done ret=%d (%lluus)\n", vret,
+ hws_elapsed_us(start_ns));
+
+ return 0;
+}
+
+static int hws_pm_resume(struct device *dev)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct hws_pcie_dev *hws = pci_get_drvdata(pdev);
+ int ret;
+ u64 start_ns = ktime_get_mono_fast_ns();
+ u64 step_ns;
+
+ dev_info(dev, "lifecycle:pm_resume begin\n");
+
+ /* Back to D0 and re-enable the function */
+ step_ns = ktime_get_mono_fast_ns();
+ pci_set_power_state(pdev, PCI_D0);
+
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(dev, "pci_enable_device: %d\n", ret);
+ return ret;
+ }
+ pci_restore_state(pdev);
+ pci_set_master(pdev);
+ dev_dbg(dev, "lifecycle:pm_resume:pci-enable (%lluus)\n",
+ hws_elapsed_us(step_ns));
+
+ /* Reapply any PCIe tuning lost across D3 */
+ enable_pcie_relaxed_ordering(pdev);
+
+ /* Reinitialize chip-side capabilities / registers */
+ step_ns = ktime_get_mono_fast_ns();
+ read_chip_id(hws);
+ /* Re-seed BAR remaps/DMA windows and restart the capture core */
+ hws_seed_all_channels(hws);
+ hws_init_video_sys(hws, true);
+ hws_irq_clear_pending(hws);
+ dev_dbg(dev, "lifecycle:pm_resume:chip-reinit (%lluus)\n",
+ hws_elapsed_us(step_ns));
+
+ /* IRQs can be re-enabled now that MMIO is sane */
+ step_ns = ktime_get_mono_fast_ns();
+ if (hws->irq >= 0)
+ enable_irq(hws->irq);
+
+ WRITE_ONCE(hws->suspended, false);
+ dev_dbg(dev, "lifecycle:pm_resume:irq-unsuspend (%lluus)\n",
+ hws_elapsed_us(step_ns));
+
+ /* vb2: nothing mandatory; userspace will STREAMON again when ready */
+ step_ns = ktime_get_mono_fast_ns();
+ hws_video_pm_resume(hws);
+ dev_dbg(dev, "lifecycle:pm_resume:video-resume (%lluus)\n",
+ hws_elapsed_us(step_ns));
+ hws_log_lifecycle_snapshot(hws, "pm_resume", "end");
+ dev_info(dev, "lifecycle:pm_resume done (%lluus)\n",
+ hws_elapsed_us(start_ns));
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(hws_pm_ops, hws_pm_suspend, hws_pm_resume);
+# define HWS_PM_OPS (&hws_pm_ops)
+#else
+# define HWS_PM_OPS NULL
+#endif
+
+static void hws_shutdown(struct pci_dev *pdev)
+{
+ struct hws_pcie_dev *hws = pci_get_drvdata(pdev);
+ int vret = 0;
+ u64 start_ns = ktime_get_mono_fast_ns();
+ u64 step_ns;
+
+ if (!hws)
+ return;
+
+ dev_info(&pdev->dev, "lifecycle:pci_shutdown begin\n");
+ vret = hws_quiesce_for_transition(hws, "pci_shutdown", true);
+
+ step_ns = ktime_get_mono_fast_ns();
+ pci_clear_master(pdev);
+ dev_dbg(&pdev->dev, "lifecycle:pci_shutdown:clear-master (%lluus)\n",
+ hws_elapsed_us(step_ns));
+ dev_info(&pdev->dev, "lifecycle:pci_shutdown done ret=%d (%lluus)\n",
+ vret, hws_elapsed_us(start_ns));
+}
+
+static struct pci_driver hws_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = hws_pci_table,
+ .probe = hws_probe,
+ .remove = hws_remove,
+ .shutdown = hws_shutdown,
+ .driver = {
+ .pm = HWS_PM_OPS,
+ },
+};
+
+MODULE_DEVICE_TABLE(pci, hws_pci_table);
+
+static int __init pcie_hws_init(void)
+{
+ return pci_register_driver(&hws_pci_driver);
+}
+
+static void __exit pcie_hws_exit(void)
+{
+ pci_unregister_driver(&hws_pci_driver);
+}
+
+module_init(pcie_hws_init);
+module_exit(pcie_hws_exit);
+
+MODULE_DESCRIPTION(DRV_NAME);
+MODULE_AUTHOR("Ben Hoff <hoff.benjamin.k@gmail.com>");
+MODULE_AUTHOR("Sales <sales@avmatrix.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DMA_BUF");
diff --git a/drivers/media/pci/hws/hws_reg.h b/drivers/media/pci/hws/hws_reg.h
new file mode 100644
index 000000000000..e4fb4af44434
--- /dev/null
+++ b/drivers/media/pci/hws/hws_reg.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef _HWS_PCIE_REG_H
+#define _HWS_PCIE_REG_H
+
+#include <linux/bits.h>
+#include <linux/sizes.h>
+
+#define XDMA_CHANNEL_NUM_MAX (1)
+#define MAX_NUM_ENGINES (XDMA_CHANNEL_NUM_MAX * 2)
+
+#define PCIE_BARADDROFSIZE 4u
+
+#define PCI_BUS_ACCESS_BASE 0x00000000U
+#define INT_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0134U)
+#define PCIEBR_EN_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0148U)
+#define PCIE_INT_DEC_REG_BASE (PCI_BUS_ACCESS_BASE + 0x0138U)
+
+#define HWS_INT_EN_MASK 0x0003FFFFU
+
+#define PCIEBAR_AXI_BASE 0x20000000U
+
+#define CTL_REG_ACC_BASE 0x0
+#define PCI_ADDR_TABLE_BASE CTL_REG_ACC_BASE
+
+#define CVBS_IN_BASE 0x00004000U
+#define CVBS_IN_BUF_BASE (CVBS_IN_BASE + (16U * PCIE_BARADDROFSIZE))
+#define CVBS_IN_BUF_BASE2 (CVBS_IN_BASE + (50U * PCIE_BARADDROFSIZE))
+
+/* 2 Mib */
+#define MAX_L_VIDEO_SIZE 0x200000U
+
+#define PCI_E_BAR_PAGE_SIZE 0x20000000
+#define PCI_E_BAR_ADD_MASK 0xE0000000
+#define PCI_E_BAR_ADD_LOWMASK 0x1FFFFFFF
+
+#define MAX_VID_CHANNELS 4
+
+#define MAX_MM_VIDEO_SIZE SZ_4M
+
+#define MAX_VIDEO_HW_W 1920
+#define MAX_VIDEO_HW_H 1080
+#define MAX_VIDEO_SCALER_SIZE (1920U * 1080U * 2U)
+
+#define MIN_VAMP_BRIGHTNESS_UNITS 0
+#define MAX_VAMP_BRIGHTNESS_UNITS 0xff
+
+#define MIN_VAMP_CONTRAST_UNITS 0
+#define MAX_VAMP_CONTRAST_UNITS 0xff
+
+#define MIN_VAMP_SATURATION_UNITS 0
+#define MAX_VAMP_SATURATION_UNITS 0xff
+
+#define MIN_VAMP_HUE_UNITS 0
+#define MAX_VAMP_HUE_UNITS 0xff
+
+#define HWS_BRIGHTNESS_DEFAULT 0x80
+#define HWS_CONTRAST_DEFAULT 0x80
+#define HWS_SATURATION_DEFAULT 0x80
+#define HWS_HUE_DEFAULT 0x00
+
+/* Core/global status. */
+#define HWS_REG_SYS_STATUS (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE)
+/* bit3: DMA busy, bit2: int, ... */
+
+#define HWS_SYS_DMA_BUSY_BIT BIT(3) /* 0x08 = DMA busy flag */
+
+#define HWS_REG_DEC_MODE (CVBS_IN_BASE + 0 * PCIE_BARADDROFSIZE)
+/* Main control register */
+#define HWS_REG_CTL (CVBS_IN_BASE + 4 * PCIE_BARADDROFSIZE)
+#define HWS_CTL_IRQ_ENABLE_BIT BIT(0) /* Global interrupt enable bit */
+/* Write 0x00 to fully reset decoder,
+ * set bit 31=1 to "start run",
+ * low byte=0x13 selects YUYV/BT.709/etc,
+ * in ReadChipId() we also write 0x00 and 0x10 here for chip-ID sequencing.
+ */
+
+/* Per-channel done flags. */
+#define HWS_REG_INT_STATUS (CVBS_IN_BASE + 1 * PCIE_BARADDROFSIZE)
+#define HWS_SYS_BUSY_BIT BIT(2) /* matches old 0x04 test */
+
+/* Capture enable switches. */
+/* bit0-3: CH0-CH3 video enable */
+#define HWS_REG_VCAP_ENABLE (CVBS_IN_BASE + 2 * PCIE_BARADDROFSIZE)
+/* bits0-3: signal present, bits8-11: interlace */
+#define HWS_REG_ACTIVE_STATUS (CVBS_IN_BASE + 5 * PCIE_BARADDROFSIZE)
+/* bits0-3: HDCP detected */
+#define HWS_REG_HDCP_STATUS (CVBS_IN_BASE + 8 * PCIE_BARADDROFSIZE)
+#define HWS_REG_DMA_MAX_SIZE (CVBS_IN_BASE + 9 * PCIE_BARADDROFSIZE)
+
+/* Buffer addresses (written once during init/reset). */
+/* Base of host-visible buffer. */
+#define HWS_REG_VBUF1_ADDR (CVBS_IN_BASE + 25 * PCIE_BARADDROFSIZE)
+/* Per-channel DMA address. */
+#define HWS_REG_DMA_ADDR(ch) (CVBS_IN_BASE + (26 + (ch)) * PCIE_BARADDROFSIZE)
+
+/* Per-channel live buffer toggles (read-only). */
+#define HWS_REG_VBUF_TOGGLE(ch) (CVBS_IN_BASE + (32 + (ch)) * PCIE_BARADDROFSIZE)
+/*
+ * Returns 0 or 1 = which half of the video ring the DMA engine is
+ * currently filling for channel *ch* (0-3).
+ */
+
+/* Per-interrupt bits (video 0-3). */
+#define HWS_INT_VDONE_BIT(ch) BIT(ch) /* 0x01,0x02,0x04,0x08 */
+
+#define HWS_REG_INT_ACK (CVBS_IN_BASE + 0x4000 + 1 * PCIE_BARADDROFSIZE)
+
+/* 16-bit W | 16-bit H. */
+#define HWS_REG_IN_RES(ch) (CVBS_IN_BASE + (90 + (ch) * 2) * PCIE_BARADDROFSIZE)
+/* B|C|H|S packed bytes. */
+#define HWS_REG_BCHS(ch) (CVBS_IN_BASE + (91 + (ch) * 2) * PCIE_BARADDROFSIZE)
+
+/* Input fps. */
+#define HWS_REG_FRAME_RATE(ch) (CVBS_IN_BASE + (110 + (ch)) * PCIE_BARADDROFSIZE)
+/* Programmed out W|H. */
+#define HWS_REG_OUT_RES(ch) (CVBS_IN_BASE + (120 + (ch)) * PCIE_BARADDROFSIZE)
+/* Programmed out fps. */
+#define HWS_REG_OUT_FRAME_RATE(ch) (CVBS_IN_BASE + (130 + (ch)) * PCIE_BARADDROFSIZE)
+
+/* Device version/port ID/subversion register. */
+#define HWS_REG_DEVICE_INFO (CVBS_IN_BASE + 88 * PCIE_BARADDROFSIZE)
+/*
+ * Reading this 32-bit word returns:
+ * bits 7:0 = "device version"
+ * bits 15:8 = "device sub-version"
+ * bits 23:24 = "HW key / port ID" etc.
+ * bits 31:28 = "support YV12" flags
+ */
+
+/* Convenience aliases for individual channels. */
+#define HWS_REG_VBUF_TOGGLE_CH0 HWS_REG_VBUF_TOGGLE(0)
+#define HWS_REG_VBUF_TOGGLE_CH1 HWS_REG_VBUF_TOGGLE(1)
+#define HWS_REG_VBUF_TOGGLE_CH2 HWS_REG_VBUF_TOGGLE(2)
+#define HWS_REG_VBUF_TOGGLE_CH3 HWS_REG_VBUF_TOGGLE(3)
+
+#endif /* _HWS_PCIE_REG_H */
diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.c b/drivers/media/pci/hws/hws_v4l2_ioctl.c
new file mode 100644
index 000000000000..0303e311ee4a
--- /dev/null
+++ b/drivers/media/pci/hws/hws_v4l2_ioctl.c
@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/math64.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+
+#include "hws.h"
+#include "hws_reg.h"
+#include "hws_video.h"
+#include "hws_v4l2_ioctl.h"
+
+struct hws_dv_mode {
+ struct v4l2_dv_timings timings;
+ u32 refresh_hz;
+};
+
+static const struct hws_dv_mode *
+hws_find_dv_by_wh(u32 w, u32 h, bool interlaced);
+static const struct hws_dv_mode *
+hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps);
+static u32 hws_get_live_fps(struct hws_video *vid);
+static u32 hws_input_status(struct hws_video *vid);
+static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps,
+ struct v4l2_dv_timings *timings);
+
+static const struct hws_dv_mode hws_dv_modes[] = {
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1920,
+ .height = 1080,
+ .hfrontporch = 88,
+ .hsync = 44,
+ .hbackporch = 148,
+ .vfrontporch = 4,
+ .vsync = 5,
+ .vbackporch = 36,
+ .pixelclock = 148500000,
+ .polarities = V4L2_DV_VSYNC_POS_POL |
+ V4L2_DV_HSYNC_POS_POL,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1920,
+ .height = 1080,
+ .hfrontporch = 88,
+ .hsync = 44,
+ .hbackporch = 148,
+ .vfrontporch = 4,
+ .vsync = 5,
+ .vbackporch = 36,
+ .pixelclock = 74250000,
+ .polarities = V4L2_DV_VSYNC_POS_POL |
+ V4L2_DV_HSYNC_POS_POL,
+ .interlaced = 0,
+ },
+ },
+ 30,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1280,
+ .height = 720,
+ .hfrontporch = 110,
+ .hsync = 40,
+ .hbackporch = 220,
+ .vfrontporch = 5,
+ .vsync = 5,
+ .vbackporch = 20,
+ .pixelclock = 74250000,
+ .polarities = V4L2_DV_VSYNC_POS_POL |
+ V4L2_DV_HSYNC_POS_POL,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 720,
+ .height = 480,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 720,
+ .height = 576,
+ .interlaced = 0,
+ },
+ },
+ 50,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 800,
+ .height = 600,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 640,
+ .height = 480,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1024,
+ .height = 768,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1280,
+ .height = 768,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1280,
+ .height = 800,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1280,
+ .height = 1024,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1360,
+ .height = 768,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1440,
+ .height = 900,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1680,
+ .height = 1050,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+ /* Portrait */
+ {
+ {
+ .type = V4L2_DV_BT_656_1120,
+ .bt = {
+ .width = 1080,
+ .height = 1920,
+ .interlaced = 0,
+ },
+ },
+ 60,
+ },
+};
+
+static const size_t hws_dv_modes_cnt = ARRAY_SIZE(hws_dv_modes);
+
+/* YUYV: 16 bpp; align to 64 as you did elsewhere */
+static inline u32 hws_calc_bpl_yuyv(u32 w) { return ALIGN(w * 2, 64); }
+static inline u32 hws_calc_size_yuyv(u32 w, u32 h) { return hws_calc_bpl_yuyv(w) * h; }
+static inline u32 hws_calc_half_size(u32 sizeimage)
+{
+ return sizeimage / 2;
+}
+
+static inline void hws_hw_write_bchs(struct hws_pcie_dev *hws, unsigned int ch,
+ u8 br, u8 co, u8 hu, u8 sa)
+{
+ u32 packed = (sa << 24) | (hu << 16) | (co << 8) | br;
+
+ if (!hws || !hws->bar0_base || ch >= hws->max_channels)
+ return;
+ writel_relaxed(packed, hws->bar0_base + HWS_REG_BCHS(ch));
+ (void)readl(hws->bar0_base + HWS_REG_BCHS(ch)); /* post write */
+}
+
+/* Helper: find a supported DV mode by W/H + interlace flag */
+static const struct hws_dv_mode *
+hws_match_supported_dv(const struct v4l2_dv_timings *req)
+{
+ const struct v4l2_bt_timings *bt;
+ u32 fps;
+
+ if (!req || req->type != V4L2_DV_BT_656_1120)
+ return NULL;
+
+ bt = &req->bt;
+ fps = 0;
+ if (bt->pixelclock) {
+ u32 total_w = bt->width + bt->hfrontporch + bt->hsync +
+ bt->hbackporch;
+ u32 total_h = bt->height + bt->vfrontporch + bt->vsync +
+ bt->vbackporch;
+
+ if (total_w && total_h)
+ fps = DIV_ROUND_CLOSEST_ULL((u64)bt->pixelclock,
+ (u64)total_w * total_h);
+ }
+ if (fps) {
+ const struct hws_dv_mode *exact =
+ hws_find_dv_by_wh_fps(bt->width, bt->height,
+ !!bt->interlaced, fps);
+ if (exact)
+ return exact;
+ }
+ return hws_find_dv_by_wh(bt->width, bt->height, !!bt->interlaced);
+}
+
+/* Helper: find a supported DV mode by W/H + interlace flag */
+static const struct hws_dv_mode *
+hws_find_dv_by_wh(u32 w, u32 h, bool interlaced)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) {
+ const struct hws_dv_mode *t = &hws_dv_modes[i];
+ const struct v4l2_bt_timings *bt = &t->timings.bt;
+
+ if (t->timings.type != V4L2_DV_BT_656_1120)
+ continue;
+
+ if (bt->width == w && bt->height == h &&
+ !!bt->interlaced == interlaced)
+ return t;
+ }
+ return NULL;
+}
+
+static const struct hws_dv_mode *
+hws_find_dv_by_wh_fps(u32 w, u32 h, bool interlaced, u32 fps)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) {
+ const struct hws_dv_mode *t = &hws_dv_modes[i];
+ const struct v4l2_bt_timings *bt = &t->timings.bt;
+
+ if (t->timings.type != V4L2_DV_BT_656_1120)
+ continue;
+
+ if (bt->width == w && bt->height == h &&
+ !!bt->interlaced == interlaced &&
+ t->refresh_hz == fps)
+ return t;
+ }
+ return NULL;
+}
+
+static bool hws_get_live_dv_geometry(struct hws_video *vid,
+ u32 *w, u32 *h, bool *interlaced)
+{
+ struct hws_pcie_dev *pdx;
+ u32 reg;
+
+ if (!vid)
+ return false;
+
+ pdx = vid->parent;
+ if (!pdx || !pdx->bar0_base)
+ return false;
+
+ reg = readl(pdx->bar0_base + HWS_REG_IN_RES(vid->channel_index));
+ if (!reg || reg == 0xFFFFFFFF)
+ return false;
+
+ if (w)
+ *w = reg & 0xFFFF;
+ if (h)
+ *h = (reg >> 16) & 0xFFFF;
+ if (interlaced) {
+ reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS);
+ *interlaced = !!(reg & BIT(8 + vid->channel_index));
+ }
+ return true;
+}
+
+static u32 hws_get_live_fps(struct hws_video *vid)
+{
+ struct hws_pcie_dev *pdx;
+ u32 fps;
+
+ if (!vid)
+ return 0;
+
+ pdx = vid->parent;
+ if (!pdx || !pdx->bar0_base)
+ return 0;
+
+ fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(vid->channel_index));
+ if (!fps || fps == 0xFFFFFFFF || fps > 240)
+ return 0;
+
+ return fps;
+}
+
+static u32 hws_pick_fps_from_mode(u32 w, u32 h, bool interlaced)
+{
+ const struct hws_dv_mode *m = hws_find_dv_by_wh(w, h, interlaced);
+
+ if (m && m->refresh_hz)
+ return m->refresh_hz;
+ /* Fallback to a sane default */
+ return 60;
+}
+
+static int hws_fill_dv_timings(u32 w, u32 h, bool interlace, u32 fps,
+ struct v4l2_dv_timings *timings)
+{
+ const struct hws_dv_mode *m;
+
+ m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL;
+ if (!m)
+ m = hws_find_dv_by_wh(w, h, interlace);
+ if (!m)
+ return -ENOLINK;
+
+ *timings = m->timings;
+ return 0;
+}
+
+static u32 hws_input_status(struct hws_video *vid)
+{
+ struct hws_pcie_dev *pdx;
+ u32 reg;
+
+ if (!vid)
+ return V4L2_IN_ST_NO_SIGNAL;
+
+ pdx = vid->parent;
+ if (!pdx || !pdx->bar0_base)
+ return V4L2_IN_ST_NO_SIGNAL;
+
+ reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS);
+ if (reg == 0xffffffff)
+ return V4L2_IN_ST_NO_SIGNAL;
+
+ return (reg & BIT(vid->channel_index)) ? 0 : V4L2_IN_ST_NO_SIGNAL;
+}
+
+/* Query the *current detected* DV timings on the input.
+ * If you have a real hardware detector, call it here; otherwise we
+ * derive from the cached pix state and map to the closest supported DV mode.
+ */
+int hws_vidioc_query_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hws_video *vid = video_drvdata(file);
+ u32 w, h;
+ u32 fps;
+ bool interlace;
+
+ if (!timings)
+ return -EINVAL;
+
+ w = vid->pix.width;
+ h = vid->pix.height;
+ interlace = vid->pix.interlaced;
+ hws_get_live_dv_geometry(vid, &w, &h, &interlace);
+ fps = hws_get_live_fps(vid);
+ if (!fps)
+ fps = vid->current_fps ? vid->current_fps :
+ hws_pick_fps_from_mode(w, h, interlace);
+
+ return hws_fill_dv_timings(w, h, interlace, fps, timings);
+}
+
+/* Enumerate the Nth supported DV timings from our static table. */
+int hws_vidioc_enum_dv_timings(struct file *file, void *fh,
+ struct v4l2_enum_dv_timings *edv)
+{
+ struct hws_video *vid = video_drvdata(file);
+ const struct hws_dv_mode *m;
+ u32 w, h;
+ u32 fps;
+ bool interlace;
+
+ if (!edv)
+ return -EINVAL;
+
+ if (edv->pad)
+ return -EINVAL;
+
+ w = 0;
+ h = 0;
+ interlace = false;
+ if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) {
+ fps = hws_get_live_fps(vid);
+ if (!fps)
+ fps = vid->current_fps ? vid->current_fps :
+ hws_pick_fps_from_mode(w, h, interlace);
+ m = fps ? hws_find_dv_by_wh_fps(w, h, interlace, fps) : NULL;
+ if (!m)
+ m = hws_find_dv_by_wh(w, h, interlace);
+ if (m) {
+ if (edv->index)
+ return -EINVAL;
+ edv->timings = m->timings;
+ return 0;
+ }
+ }
+
+ if (edv->index >= hws_dv_modes_cnt)
+ return -EINVAL;
+
+ edv->timings = hws_dv_modes[edv->index].timings;
+ return 0;
+}
+
+/* Get the *currently configured* DV timings. */
+int hws_vidioc_g_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hws_video *vid = video_drvdata(file);
+ u32 w, h;
+ u32 fps;
+ bool interlace;
+
+ if (!timings)
+ return -EINVAL;
+
+ w = vid->pix.width;
+ h = vid->pix.height;
+ interlace = vid->pix.interlaced;
+ if (hws_get_live_dv_geometry(vid, &w, &h, &interlace)) {
+ fps = hws_get_live_fps(vid);
+ if (!fps)
+ fps = vid->current_fps ? vid->current_fps :
+ hws_pick_fps_from_mode(w, h, interlace);
+ return hws_fill_dv_timings(w, h, interlace, fps, timings);
+ }
+
+ *timings = vid->cur_dv_timings;
+ return 0;
+}
+
+static inline void hws_set_colorimetry_state(struct hws_pix_state *p)
+{
+ bool sd = p->height <= 576;
+
+ p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
+ p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ p->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ p->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+/* Set DV timings: must match one of our supported modes.
+ * If buffers are queued and this implies a size change, we reject with -EBUSY.
+ * Otherwise we update pix state and (optionally) reprogram the HW.
+ */
+int hws_vidioc_s_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hws_video *vid = video_drvdata(file);
+ const struct hws_dv_mode *m;
+ const struct v4l2_bt_timings *bt;
+ u32 new_w, new_h;
+ bool interlaced;
+ int ret = 0;
+ unsigned long was_busy;
+ u32 live_w, live_h;
+ u32 live_fps;
+ bool live_interlaced;
+ bool live_present;
+
+ if (!timings)
+ return -EINVAL;
+
+ m = hws_match_supported_dv(timings);
+ if (!m)
+ return -EINVAL;
+
+ bt = &m->timings.bt;
+ if (bt->interlaced)
+ return -EINVAL; /* only progressive modes are advertised */
+ new_w = bt->width;
+ new_h = bt->height;
+ interlaced = false;
+
+ lockdep_assert_held(&vid->state_lock);
+ live_present = hws_get_live_dv_geometry(vid, &live_w, &live_h,
+ &live_interlaced);
+
+ /* If vb2 has active buffers and size would change, reject. */
+ was_busy = vb2_is_busy(&vid->buffer_queue);
+ if (was_busy &&
+ (new_w != vid->pix.width || new_h != vid->pix.height ||
+ interlaced != vid->pix.interlaced)) {
+ ret = -EBUSY;
+ return ret;
+ }
+
+ /* When a live input signal is present, the receiver owns the timing.
+ * Allow setting the already-active timings so v4l2-compliance can
+ * round-trip them, but reject attempts to retime the live source.
+ */
+ if (live_present) {
+ live_fps = hws_get_live_fps(vid);
+ if (!live_fps)
+ live_fps = vid->current_fps ? vid->current_fps :
+ hws_pick_fps_from_mode(live_w, live_h,
+ live_interlaced);
+ if (live_w == new_w && live_h == new_h &&
+ live_interlaced == interlaced &&
+ m->refresh_hz == live_fps)
+ return 0;
+ return -EBUSY;
+ }
+
+ /* Update software pixel state (and recalc sizes) */
+ vid->pix.width = new_w;
+ vid->pix.height = new_h;
+ vid->pix.field = interlaced ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_NONE;
+ vid->pix.interlaced = interlaced;
+ vid->pix.fourcc = V4L2_PIX_FMT_YUYV;
+
+ hws_set_colorimetry_state(&vid->pix);
+
+ /* Recompute stride, sizeimage, and half_size. */
+ vid->pix.bytesperline = hws_calc_bpl_yuyv(new_w);
+ vid->pix.sizeimage = hws_calc_size_yuyv(new_w, new_h);
+ vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage);
+ vid->cur_dv_timings = m->timings;
+ vid->current_fps = m->refresh_hz;
+ return ret;
+}
+
+/* Report DV timings capability: advertise BT.656/1120 with
+ * the min/max WxH derived from our table and basic progressive support.
+ */
+int hws_vidioc_dv_timings_cap(struct file *file, void *fh,
+ struct v4l2_dv_timings_cap *cap)
+{
+ u32 min_w = ~0U, min_h = ~0U;
+ u32 max_w = 0, max_h = 0;
+ size_t i, n = 0;
+
+ if (!cap)
+ return -EINVAL;
+
+ memset(cap, 0, sizeof(*cap));
+ cap->type = V4L2_DV_BT_656_1120;
+
+ for (i = 0; i < ARRAY_SIZE(hws_dv_modes); i++) {
+ const struct v4l2_bt_timings *bt = &hws_dv_modes[i].timings.bt;
+
+ if (hws_dv_modes[i].timings.type != V4L2_DV_BT_656_1120)
+ continue;
+ n++;
+
+ if (bt->width < min_w)
+ min_w = bt->width;
+ if (bt->height < min_h)
+ min_h = bt->height;
+ if (bt->width > max_w)
+ max_w = bt->width;
+ if (bt->height > max_h)
+ max_h = bt->height;
+ }
+
+ /* If the table was empty, fail gracefully. */
+ if (!n || min_w == U32_MAX)
+ return -ENODATA;
+
+ cap->bt.min_width = min_w;
+ cap->bt.max_width = max_w;
+ cap->bt.min_height = min_h;
+ cap->bt.max_height = max_h;
+
+ /* We support both CEA-861- and VESA-style modes in the list. */
+ cap->bt.standards =
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT;
+
+ /* Only progressive modes are advertised. */
+ cap->bt.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE;
+
+ /* Leave pixelclock/porch limits unconstrained (0) for now. */
+ return 0;
+}
+
+static int hws_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct hws_video *vid =
+ container_of(ctrl->handler, struct hws_video, control_handler);
+ struct hws_pcie_dev *pdx = vid->parent;
+ bool program = false;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ vid->current_brightness = ctrl->val;
+ program = true;
+ break;
+ case V4L2_CID_CONTRAST:
+ vid->current_contrast = ctrl->val;
+ program = true;
+ break;
+ case V4L2_CID_SATURATION:
+ vid->current_saturation = ctrl->val;
+ program = true;
+ break;
+ case V4L2_CID_HUE:
+ vid->current_hue = ctrl->val;
+ program = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (program) {
+ hws_hw_write_bchs(pdx, vid->channel_index,
+ (u8)vid->current_brightness,
+ (u8)vid->current_contrast,
+ (u8)vid->current_hue,
+ (u8)vid->current_saturation);
+ }
+ return 0;
+}
+
+const struct v4l2_ctrl_ops hws_ctrl_ops = {
+ .s_ctrl = hws_s_ctrl,
+};
+
+int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
+{
+ struct hws_video *vid = video_drvdata(file);
+ int vi_index = vid->channel_index + 1; /* keep it simple */
+
+ strscpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver));
+ snprintf(cap->card, sizeof(cap->card),
+ "AVMatrix HWS Capture %d", vi_index);
+ return 0;
+}
+
+int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL; /* only one format */
+
+ f->pixelformat = V4L2_PIX_FMT_YUYV;
+ return 0;
+}
+
+int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt)
+{
+ struct hws_video *vid = video_drvdata(file);
+
+ fmt->fmt.pix.width = vid->pix.width;
+ fmt->fmt.pix.height = vid->pix.height;
+ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
+ fmt->fmt.pix.field = vid->pix.field;
+ fmt->fmt.pix.bytesperline = vid->pix.bytesperline;
+ fmt->fmt.pix.sizeimage = vid->pix.sizeimage;
+ fmt->fmt.pix.colorspace = vid->pix.colorspace;
+ fmt->fmt.pix.ycbcr_enc = vid->pix.ycbcr_enc;
+ fmt->fmt.pix.quantization = vid->pix.quantization;
+ fmt->fmt.pix.xfer_func = vid->pix.xfer_func;
+ return 0;
+}
+
+static inline void hws_set_colorimetry_fmt(struct v4l2_pix_format *p)
+{
+ bool sd = p->height <= 576;
+
+ p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
+ p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ p->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ p->xfer_func = V4L2_XFER_FUNC_DEFAULT;
+}
+
+int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+ struct hws_video *vid = file ? video_drvdata(file) : NULL;
+ struct hws_pcie_dev *pdev = vid ? vid->parent : NULL;
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ u32 req_w = pix->width, req_h = pix->height;
+ u32 w, h, min_bpl, bpl;
+ size_t size; /* wider than u32 for overflow check */
+ size_t max_frame = pdev ? pdev->max_hw_video_buf_sz : MAX_MM_VIDEO_SIZE;
+
+ /* Only YUYV */
+ pix->pixelformat = V4L2_PIX_FMT_YUYV;
+
+ /* Defaults then clamp */
+ w = (req_w ? req_w : 640);
+ h = (req_h ? req_h : 480);
+ if (w > MAX_VIDEO_HW_W)
+ w = MAX_VIDEO_HW_W;
+ if (h > MAX_VIDEO_HW_H)
+ h = MAX_VIDEO_HW_H;
+ if (!w)
+ w = 640; /* hard fallback in case macros are odd */
+ if (!h)
+ h = 480;
+
+ /* Field policy */
+ pix->field = V4L2_FIELD_NONE;
+
+ /* Stride policy for packed 16bpp, 64B align */
+ min_bpl = ALIGN(w * 2, 64);
+
+ /* Bound requested bpl to something sane, then align */
+ bpl = pix->bytesperline;
+ if (bpl < min_bpl) {
+ bpl = min_bpl;
+ } else {
+ /* Cap at 16x width to avoid silly values that overflow sizeimage */
+ u32 max_bpl = ALIGN(w * 2 * 16, 64);
+
+ if (bpl > max_bpl)
+ bpl = max_bpl;
+ bpl = ALIGN(bpl, 64);
+ }
+ if (h && max_frame) {
+ size_t max_bpl_hw = max_frame / h;
+
+ if (max_bpl_hw < min_bpl)
+ return -ERANGE;
+ max_bpl_hw = rounddown(max_bpl_hw, 64);
+ if (!max_bpl_hw)
+ return -ERANGE;
+ if (bpl > max_bpl_hw) {
+ if (pdev)
+ dev_dbg(&pdev->pdev->dev,
+ "try_fmt: clamp bpl %u -> %zu due to hw buf cap %zu\n",
+ bpl, max_bpl_hw, max_frame);
+ bpl = (u32)max_bpl_hw;
+ }
+ }
+ size = (size_t)bpl * (size_t)h;
+ if (size > max_frame)
+ return -ERANGE;
+
+ pix->width = w;
+ pix->height = h;
+ pix->bytesperline = bpl;
+ pix->sizeimage = (u32)size; /* logical size, not page-aligned */
+
+ hws_set_colorimetry_fmt(pix);
+ if (pdev)
+ dev_dbg(&pdev->pdev->dev,
+ "try_fmt: w=%u h=%u bpl=%u size=%u field=%u\n",
+ pix->width, pix->height, pix->bytesperline,
+ pix->sizeimage, pix->field);
+ return 0;
+}
+
+int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f)
+{
+ struct hws_video *vid = video_drvdata(file);
+ int ret;
+
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* Normalize the request */
+ ret = hws_vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ /* Don't allow buffer layout changes while buffers are queued. */
+ if (vb2_is_busy(&vid->buffer_queue)) {
+ if (f->fmt.pix.width != vid->pix.width ||
+ f->fmt.pix.height != vid->pix.height ||
+ f->fmt.pix.bytesperline != vid->pix.bytesperline)
+ return -EBUSY;
+ }
+
+ /* Apply to driver state */
+ vid->pix.width = f->fmt.pix.width;
+ vid->pix.height = f->fmt.pix.height;
+ vid->pix.fourcc = V4L2_PIX_FMT_YUYV;
+ vid->pix.field = f->fmt.pix.field;
+ vid->pix.colorspace = f->fmt.pix.colorspace;
+ vid->pix.ycbcr_enc = f->fmt.pix.ycbcr_enc;
+ vid->pix.quantization = f->fmt.pix.quantization;
+ vid->pix.xfer_func = f->fmt.pix.xfer_func;
+
+ /* Update negotiated buffer sizes. */
+ vid->pix.bytesperline = f->fmt.pix.bytesperline; /* aligned */
+ vid->pix.sizeimage = f->fmt.pix.sizeimage; /* logical */
+ vid->pix.half_size = hws_calc_half_size(vid->pix.sizeimage);
+ vid->pix.interlaced = false;
+ /* S_FMT negotiates buffer layout only. Keep detector-owned DV timing
+ * state unchanged so a harmless restart cannot clobber the live FPS.
+ */
+ /* Or:
+ * hws_calc_sizeimage(vid, vid->pix.width, vid->pix.height, false);
+ */
+
+ dev_dbg(&vid->parent->pdev->dev,
+ "s_fmt: w=%u h=%u bpl=%u size=%u\n",
+ vid->pix.width, vid->pix.height, vid->pix.bytesperline,
+ vid->pix.sizeimage);
+
+ return 0;
+}
+
+int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *param)
+{
+ struct hws_video *vid = video_drvdata(file);
+ u32 fps;
+
+ if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ fps = hws_get_live_fps(vid);
+ if (!fps)
+ fps = vid->current_fps ? vid->current_fps : 60;
+
+ /* HDMI receivers report the detected frame period, they don't set it. */
+ param->parm.capture.capability = 0;
+ param->parm.capture.capturemode = 0;
+ param->parm.capture.timeperframe.numerator = 1;
+ param->parm.capture.timeperframe.denominator = fps;
+ param->parm.capture.extendedmode = 0;
+ param->parm.capture.readbuffers = 0;
+
+ return 0;
+}
+
+int hws_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct hws_video *vid = video_drvdata(file);
+
+ if (input->index)
+ return -EINVAL;
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ strscpy(input->name, KBUILD_MODNAME, sizeof(input->name));
+ input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+ input->status = hws_input_status(vid);
+
+ return 0;
+}
+
+int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ *index = 0;
+ return 0;
+}
+
+int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return i ? -EINVAL : 0;
+}
diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.h b/drivers/media/pci/hws/hws_v4l2_ioctl.h
new file mode 100644
index 000000000000..53044f78d6fa
--- /dev/null
+++ b/drivers/media/pci/hws/hws_v4l2_ioctl.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef HWS_V4L2_IOCTL_H
+#define HWS_V4L2_IOCTL_H
+
+#include <media/v4l2-ctrls.h>
+#include <linux/fs.h>
+
+extern const struct v4l2_ctrl_ops hws_ctrl_ops;
+
+int hws_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap);
+int hws_vidioc_enum_fmt_vid_cap(struct file *file, void *priv_fh, struct v4l2_fmtdesc *f);
+int hws_vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt);
+int hws_vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f);
+int hws_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms);
+int hws_vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms);
+int hws_vidioc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *setfps);
+int hws_vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i);
+int hws_vidioc_g_input(struct file *file, void *priv, unsigned int *i);
+int hws_vidioc_s_input(struct file *file, void *priv, unsigned int i);
+int hws_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a);
+int hws_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a);
+int hws_vidioc_dv_timings_cap(struct file *file, void *fh,
+ struct v4l2_dv_timings_cap *cap);
+int hws_vidioc_s_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings);
+
+int hws_vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
+int hws_vidioc_g_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings);
+int hws_vidioc_enum_dv_timings(struct file *file, void *fh,
+ struct v4l2_enum_dv_timings *edv);
+int hws_vidioc_query_dv_timings(struct file *file, void *fh,
+ struct v4l2_dv_timings *timings);
+int hws_vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f);
+
+#endif
diff --git a/drivers/media/pci/hws/hws_video.c b/drivers/media/pci/hws/hws_video.c
new file mode 100644
index 000000000000..18e4bc6901d3
--- /dev/null
+++ b/drivers/media/pci/hws/hws_video.c
@@ -0,0 +1,1490 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/overflow.h>
+#include <linux/delay.h>
+#include <linux/bits.h>
+#include <linux/jiffies.h>
+#include <linux/ktime.h>
+#include <linux/math64.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-event.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/v4l2-device.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include "hws.h"
+#include "hws_reg.h"
+#include "hws_video.h"
+#include "hws_irq.h"
+#include "hws_v4l2_ioctl.h"
+
+#define HWS_REMAP_SLOT_OFF(ch) (0x208 + (ch) * 8) /* one 64-bit slot per ch */
+#define HWS_BUF_BASE_OFF(ch) (CVBS_IN_BUF_BASE + (ch) * PCIE_BARADDROFSIZE)
+#define HWS_HALF_SZ_OFF(ch) (CVBS_IN_BUF_BASE2 + (ch) * PCIE_BARADDROFSIZE)
+
+static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch,
+ bool interlace);
+static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch,
+ bool *interlace);
+static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch);
+static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch);
+static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h,
+ bool interlaced);
+
+/* DMA helper functions */
+static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma);
+static struct hwsvideo_buffer *
+hws_take_queued_buffer_locked(struct hws_video *vid);
+
+static unsigned long long hws_elapsed_us(u64 start_ns)
+{
+ return div_u64(ktime_get_mono_fast_ns() - start_ns, 1000);
+}
+
+static inline bool list_node_unlinked(const struct list_head *n)
+{
+ return n->next == LIST_POISON1 || n->prev == LIST_POISON2;
+}
+
+static bool dma_window_verify;
+module_param_named(dma_window_verify, dma_window_verify, bool, 0644);
+MODULE_PARM_DESC(dma_window_verify,
+ "Read back DMA window registers after programming (debug)");
+
+void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch,
+ dma_addr_t dma, const char *tag)
+{
+ iowrite32(lower_32_bits(dma), hws->bar0_base + HWS_REG_DMA_ADDR(ch));
+ dev_dbg(&hws->pdev->dev, "dma_doorbell ch%u: dma=0x%llx tag=%s\n", ch,
+ (u64)dma, tag ? tag : "");
+}
+
+static void hws_program_dma_window(struct hws_video *vid, dma_addr_t dma)
+{
+ const u32 addr_mask = PCI_E_BAR_ADD_MASK;
+ const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK;
+ struct hws_pcie_dev *hws = vid->parent;
+ unsigned int ch = vid->channel_index;
+ u32 table_off = HWS_REMAP_SLOT_OFF(ch);
+ u32 lo = lower_32_bits(dma);
+ u32 hi = upper_32_bits(dma);
+ u32 pci_addr = lo & addr_low_mask;
+ u32 page_lo = lo & addr_mask;
+
+ bool wrote = false;
+
+ /* Remap entry only when DMA crosses into a new 512 MB page */
+ if (!vid->window_valid || vid->last_dma_hi != hi ||
+ vid->last_dma_page != page_lo) {
+ writel(hi, hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off);
+ writel(page_lo,
+ hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off +
+ PCIE_BARADDROFSIZE);
+ vid->last_dma_hi = hi;
+ vid->last_dma_page = page_lo;
+ wrote = true;
+ }
+
+ /* Base pointer only needs low 29 bits */
+ if (!vid->window_valid || vid->last_pci_addr != pci_addr) {
+ writel((ch + 1) * PCIEBAR_AXI_BASE + pci_addr,
+ hws->bar0_base + HWS_BUF_BASE_OFF(ch));
+ vid->last_pci_addr = pci_addr;
+ wrote = true;
+ }
+
+ /* Half-size only changes when resolution changes */
+ if (!vid->window_valid || vid->last_half16 != vid->pix.half_size / 16) {
+ writel(vid->pix.half_size / 16,
+ hws->bar0_base + HWS_HALF_SZ_OFF(ch));
+ vid->last_half16 = vid->pix.half_size / 16;
+ wrote = true;
+ }
+
+ vid->window_valid = true;
+
+ if (dma_window_verify && wrote) {
+ u32 r_hi =
+ readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off);
+ u32 r_lo =
+ readl(hws->bar0_base + PCI_ADDR_TABLE_BASE + table_off +
+ PCIE_BARADDROFSIZE);
+ u32 r_base = readl(hws->bar0_base + HWS_BUF_BASE_OFF(ch));
+ u32 r_half = readl(hws->bar0_base + HWS_HALF_SZ_OFF(ch));
+
+ dev_dbg(&hws->pdev->dev,
+ "ch%u remap verify: hi=0x%08x page_lo=0x%08x exp_page=0x%08x base=0x%08x exp_base=0x%08x half16B=0x%08x exp_half=0x%08x\n",
+ ch, r_hi, r_lo, page_lo, r_base,
+ (ch + 1) * PCIEBAR_AXI_BASE + pci_addr, r_half,
+ vid->pix.half_size / 16);
+ } else if (wrote) {
+ /* Flush posted writes before arming DMA */
+ readl_relaxed(hws->bar0_base + HWS_HALF_SZ_OFF(ch));
+ }
+}
+
+static struct hwsvideo_buffer *
+hws_take_queued_buffer_locked(struct hws_video *vid)
+{
+ struct hwsvideo_buffer *buf;
+
+ if (!vid || list_empty(&vid->capture_queue))
+ return NULL;
+
+ buf = list_first_entry(&vid->capture_queue,
+ struct hwsvideo_buffer, list);
+ list_del_init(&buf->list);
+ if (vid->queued_count)
+ vid->queued_count--;
+ return buf;
+}
+
+void hws_prime_next_locked(struct hws_video *vid)
+{
+ struct hws_pcie_dev *hws;
+ struct hwsvideo_buffer *next;
+ dma_addr_t dma;
+
+ if (!vid)
+ return;
+
+ hws = vid->parent;
+ if (!hws || !hws->bar0_base)
+ return;
+
+ if (!READ_ONCE(vid->cap_active) || !vid->active || vid->next_prepared)
+ return;
+
+ next = hws_take_queued_buffer_locked(vid);
+ if (!next)
+ return;
+
+ vid->next_prepared = next;
+ dma = vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0);
+ hws_program_dma_for_addr(hws, vid->channel_index, dma);
+ iowrite32(lower_32_bits(dma),
+ hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index));
+ dev_dbg(&hws->pdev->dev,
+ "ch%u pre-armed next buffer %p dma=0x%llx\n",
+ vid->channel_index, next, (u64)dma);
+}
+
+static bool hws_force_no_signal_frame(struct hws_video *v, const char *tag)
+{
+ struct hws_pcie_dev *hws;
+ unsigned long flags;
+ struct hwsvideo_buffer *buf = NULL, *next = NULL;
+ bool have_next = false;
+ bool doorbell = false;
+
+ if (!v)
+ return false;
+ hws = v->parent;
+ if (!hws || READ_ONCE(v->stop_requested) || !READ_ONCE(v->cap_active))
+ return false;
+ spin_lock_irqsave(&v->irq_lock, flags);
+ if (v->active) {
+ buf = v->active;
+ v->active = NULL;
+ buf->slot = 0;
+ } else if (!list_empty(&v->capture_queue)) {
+ buf = list_first_entry(&v->capture_queue,
+ struct hwsvideo_buffer, list);
+ list_del_init(&buf->list);
+ if (v->queued_count)
+ v->queued_count--;
+ buf->slot = 0;
+ }
+ if (v->next_prepared) {
+ next = v->next_prepared;
+ v->next_prepared = NULL;
+ next->slot = 0;
+ v->active = next;
+ have_next = true;
+ } else if (!list_empty(&v->capture_queue)) {
+ next = list_first_entry(&v->capture_queue,
+ struct hwsvideo_buffer, list);
+ list_del_init(&next->list);
+ if (v->queued_count)
+ v->queued_count--;
+ next->slot = 0;
+ v->active = next;
+ have_next = true;
+ } else {
+ v->active = NULL;
+ }
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+ if (!buf)
+ return false;
+ /* Complete buffer with a neutral frame so dequeuers keep running. */
+ {
+ struct vb2_v4l2_buffer *vb2v = &buf->vb;
+ void *dst = vb2_plane_vaddr(&vb2v->vb2_buf, 0);
+
+ if (dst)
+ memset(dst, 0x10, v->pix.sizeimage);
+ vb2_set_plane_payload(&vb2v->vb2_buf, 0, v->pix.sizeimage);
+ vb2v->sequence = (u32)atomic_inc_return(&v->sequence_number);
+ vb2v->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&vb2v->vb2_buf, VB2_BUF_STATE_DONE);
+ }
+ if (have_next && next) {
+ dma_addr_t dma =
+ vb2_dma_contig_plane_dma_addr(&next->vb.vb2_buf, 0);
+ hws_program_dma_for_addr(hws, v->channel_index, dma);
+ hws_set_dma_doorbell(hws, v->channel_index, dma,
+ tag ? tag : "nosignal_zero");
+ doorbell = true;
+ }
+ if (doorbell) {
+ wmb(); /* ensure descriptors visible before enabling capture */
+ hws_enable_video_capture(hws, v->channel_index, true);
+ }
+ return true;
+}
+
+static int hws_ctrls_init(struct hws_video *vid)
+{
+ struct v4l2_ctrl_handler *hdl = &vid->control_handler;
+
+ /* Create BCHS controls. */
+ v4l2_ctrl_handler_init(hdl, 4);
+
+ vid->ctrl_brightness = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops,
+ V4L2_CID_BRIGHTNESS,
+ MIN_VAMP_BRIGHTNESS_UNITS,
+ MAX_VAMP_BRIGHTNESS_UNITS, 1,
+ HWS_BRIGHTNESS_DEFAULT);
+
+ vid->ctrl_contrast =
+ v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_CONTRAST,
+ MIN_VAMP_CONTRAST_UNITS, MAX_VAMP_CONTRAST_UNITS,
+ 1, HWS_CONTRAST_DEFAULT);
+
+ vid->ctrl_saturation = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops,
+ V4L2_CID_SATURATION,
+ MIN_VAMP_SATURATION_UNITS,
+ MAX_VAMP_SATURATION_UNITS, 1,
+ HWS_SATURATION_DEFAULT);
+
+ vid->ctrl_hue = v4l2_ctrl_new_std(hdl, &hws_ctrl_ops, V4L2_CID_HUE,
+ MIN_VAMP_HUE_UNITS,
+ MAX_VAMP_HUE_UNITS, 1,
+ HWS_HUE_DEFAULT);
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ return err;
+ }
+ return 0;
+}
+
+int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch)
+{
+ struct hws_video *vid;
+
+ /* basic sanity */
+ if (!pdev || ch < 0 || ch >= pdev->max_channels)
+ return -EINVAL;
+
+ vid = &pdev->video[ch];
+
+ /* hard reset the per-channel struct (safe here since we init everything next) */
+ memset(vid, 0, sizeof(*vid));
+
+ /* identity */
+ vid->parent = pdev;
+ vid->channel_index = ch;
+
+ /* locks & lists */
+ mutex_init(&vid->state_lock);
+ spin_lock_init(&vid->irq_lock);
+ INIT_LIST_HEAD(&vid->capture_queue);
+ atomic_set(&vid->sequence_number, 0);
+ vid->active = NULL;
+
+ /* DMA watchdog removed; retain counters for diagnostics */
+ vid->timeout_count = 0;
+ vid->error_count = 0;
+
+ vid->queued_count = 0;
+ vid->window_valid = false;
+
+ /* Default format. */
+ vid->pix.width = 1920;
+ vid->pix.height = 1080;
+ vid->pix.fourcc = V4L2_PIX_FMT_YUYV;
+ vid->pix.bytesperline = ALIGN(vid->pix.width * 2, 64);
+ vid->pix.sizeimage = vid->pix.bytesperline * vid->pix.height;
+ vid->pix.field = V4L2_FIELD_NONE;
+ vid->pix.colorspace = V4L2_COLORSPACE_REC709;
+ vid->pix.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ vid->pix.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ vid->pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
+ vid->pix.interlaced = false;
+ vid->pix.half_size = vid->pix.sizeimage / 2;
+ hws_set_current_dv_timings(vid, vid->pix.width,
+ vid->pix.height, vid->pix.interlaced);
+ vid->current_fps = 60;
+
+ /* color controls default (mid-scale) */
+ vid->current_brightness = 0x80;
+ vid->current_contrast = 0x80;
+ vid->current_saturation = 0x80;
+ vid->current_hue = 0x80;
+
+ /* capture state */
+ vid->cap_active = false;
+ vid->stop_requested = false;
+ vid->last_buf_half_toggle = 0;
+ vid->half_seen = false;
+ vid->signal_loss_cnt = 0;
+
+ /* Create BCHS + DV power-present as modern controls */
+ {
+ int err = hws_ctrls_init(vid);
+
+ if (err) {
+ dev_err(&pdev->pdev->dev,
+ "v4l2 ctrl init failed on ch%d: %d\n", ch, err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void hws_video_drain_queue_locked(struct hws_video *vid)
+{
+ /* Return in-flight first */
+ if (vid->active) {
+ vb2_buffer_done(&vid->active->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vid->active = NULL;
+ }
+
+ /* Then everything queued */
+ while (!list_empty(&vid->capture_queue)) {
+ struct hwsvideo_buffer *b =
+ list_first_entry(&vid->capture_queue,
+ struct hwsvideo_buffer,
+ list);
+ list_del_init(&b->list);
+ vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static void hws_video_release_registration(struct hws_video *vid)
+{
+ if (vid->buffer_queue.ops) {
+ vb2_queue_release(&vid->buffer_queue);
+ vid->buffer_queue.ops = NULL;
+ }
+
+ if (!vid->video_device)
+ return;
+
+ if (video_is_registered(vid->video_device))
+ vb2_video_unregister_device(vid->video_device);
+ else
+ video_device_release(vid->video_device);
+ vid->video_device = NULL;
+}
+
+static void hws_video_collect_done_locked(struct hws_video *vid,
+ struct list_head *done)
+{
+ struct hwsvideo_buffer *b;
+
+ if (vid->active) {
+ if (!list_node_unlinked(&vid->active->list)) {
+ list_move_tail(&vid->active->list, done);
+ } else {
+ INIT_LIST_HEAD(&vid->active->list);
+ list_add_tail(&vid->active->list, done);
+ }
+ vid->active = NULL;
+ }
+
+ if (vid->next_prepared) {
+ if (!list_node_unlinked(&vid->next_prepared->list)) {
+ list_move_tail(&vid->next_prepared->list, done);
+ } else {
+ INIT_LIST_HEAD(&vid->next_prepared->list);
+ list_add_tail(&vid->next_prepared->list, done);
+ }
+ vid->next_prepared = NULL;
+ }
+
+ while (!list_empty(&vid->capture_queue)) {
+ b = list_first_entry(&vid->capture_queue, struct hwsvideo_buffer,
+ list);
+ list_move_tail(&b->list, done);
+ }
+
+ vid->queued_count = 0;
+}
+
+void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch)
+{
+ struct hws_video *vid;
+ unsigned long flags;
+
+ if (!pdev || ch < 0 || ch >= pdev->max_channels)
+ return;
+
+ vid = &pdev->video[ch];
+
+ /* 1) Stop HW best-effort for this channel */
+ hws_enable_video_capture(vid->parent, vid->channel_index, false);
+
+ /* 2) Flip software state so IRQ/BH will be no-ops if they run */
+ WRITE_ONCE(vid->stop_requested, true);
+ WRITE_ONCE(vid->cap_active, false);
+
+ /* 3) Ensure the IRQ handler finished any in-flight completions */
+ if (vid->parent && vid->parent->irq >= 0)
+ synchronize_irq(vid->parent->irq);
+
+ /* 4) Drain SW capture queue & in-flight under lock */
+ spin_lock_irqsave(&vid->irq_lock, flags);
+ hws_video_drain_queue_locked(vid);
+ spin_unlock_irqrestore(&vid->irq_lock, flags);
+
+ /* 5) Release VB2 queue if initialized */
+ hws_video_release_registration(vid);
+
+ /* 6) Free V4L2 controls */
+ v4l2_ctrl_handler_free(&vid->control_handler);
+
+ /* 8) Reset simple state; do not memset the whole struct here. */
+ mutex_destroy(&vid->state_lock);
+ INIT_LIST_HEAD(&vid->capture_queue);
+ vid->active = NULL;
+ vid->stop_requested = false;
+ vid->last_buf_half_toggle = 0;
+ vid->half_seen = false;
+ vid->signal_loss_cnt = 0;
+}
+
+/* Convenience cast */
+static inline struct hwsvideo_buffer *to_hwsbuf(struct vb2_buffer *vb)
+{
+ return container_of(to_vb2_v4l2_buffer(vb), struct hwsvideo_buffer, vb);
+}
+
+static int hws_buf_init(struct vb2_buffer *vb)
+{
+ struct hwsvideo_buffer *b = to_hwsbuf(vb);
+
+ INIT_LIST_HEAD(&b->list);
+ return 0;
+}
+
+static void hws_buf_finish(struct vb2_buffer *vb)
+{
+ /* vb2 core handles cache maintenance for dma-contig buffers */
+ (void)vb;
+}
+
+static void hws_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct hwsvideo_buffer *b = to_hwsbuf(vb);
+
+ if (!list_empty(&b->list))
+ list_del_init(&b->list);
+}
+
+void hws_program_dma_for_addr(struct hws_pcie_dev *hws, unsigned int ch,
+ dma_addr_t dma)
+{
+ struct hws_video *vid = &hws->video[ch];
+
+ hws_program_dma_window(vid, dma);
+}
+
+void hws_enable_video_capture(struct hws_pcie_dev *hws, unsigned int chan,
+ bool on)
+{
+ u32 status;
+
+ if (!hws || hws->pci_lost || chan >= hws->max_channels)
+ return;
+
+ status = readl(hws->bar0_base + HWS_REG_VCAP_ENABLE);
+ status = on ? (status | BIT(chan)) : (status & ~BIT(chan));
+ writel(status, hws->bar0_base + HWS_REG_VCAP_ENABLE);
+ (void)readl(hws->bar0_base + HWS_REG_VCAP_ENABLE);
+
+ WRITE_ONCE(hws->video[chan].cap_active, on);
+
+ dev_dbg(&hws->pdev->dev, "vcap %s ch%u (reg=0x%08x)\n",
+ on ? "ON" : "OFF", chan, status);
+}
+
+static void hws_seed_dma_windows(struct hws_pcie_dev *hws)
+{
+ const u32 addr_mask = PCI_E_BAR_ADD_MASK;
+ const u32 addr_low_mask = PCI_E_BAR_ADD_LOWMASK;
+ u32 table = 0x208; /* one 64-bit entry per channel */
+ unsigned int ch;
+
+ if (!hws || !hws->bar0_base)
+ return;
+
+ /* If cur_max_video_ch is not set yet, default to max_channels. */
+ if (!hws->cur_max_video_ch || hws->cur_max_video_ch > hws->max_channels)
+ hws->cur_max_video_ch = hws->max_channels;
+
+ for (ch = 0; ch < hws->cur_max_video_ch; ch++, table += 8) {
+ if (!hws->scratch_vid[ch].cpu)
+ continue;
+
+ /* Program 64-bit BAR remap entry for this channel */
+ {
+ dma_addr_t p = hws->scratch_vid[ch].dma;
+ u32 lo = lower_32_bits(p) & addr_mask;
+ u32 hi = upper_32_bits(p);
+ u32 pci_addr_low = lower_32_bits(p) & addr_low_mask;
+
+ writel_relaxed(hi,
+ hws->bar0_base + PCI_ADDR_TABLE_BASE +
+ table);
+ writel_relaxed(lo,
+ hws->bar0_base + PCI_ADDR_TABLE_BASE +
+ table + PCIE_BARADDROFSIZE);
+
+ /* Per-channel AXI base + PCI low */
+ writel_relaxed((ch + 1) * PCIEBAR_AXI_BASE +
+ pci_addr_low,
+ hws->bar0_base + CVBS_IN_BUF_BASE +
+ ch * PCIE_BARADDROFSIZE);
+
+ /* Half-frame length in /16 units.
+ * Prefer the current channel's computed half_size if available.
+ * Fall back to half of the probe-owned scratch buffer.
+ */
+ {
+ u32 half_bytes = hws->video[ch].pix.half_size ?
+ hws->video[ch].pix.half_size :
+ (u32)(hws->scratch_vid[ch].size / 2);
+ writel_relaxed(half_bytes / 16,
+ hws->bar0_base +
+ CVBS_IN_BUF_BASE2 +
+ ch * PCIE_BARADDROFSIZE);
+ }
+ }
+ }
+
+ /* Post writes so device sees them before we move on */
+ (void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
+}
+
+static void hws_ack_all_irqs(struct hws_pcie_dev *hws)
+{
+ u32 st = readl(hws->bar0_base + HWS_REG_INT_STATUS);
+
+ if (st) {
+ writel(st, hws->bar0_base + HWS_REG_INT_STATUS); /* W1C */
+ (void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
+ }
+}
+
+static void hws_open_irq_fabric(struct hws_pcie_dev *hws)
+{
+ /* Route all sources to vector 0. */
+ writel(0x00000000, hws->bar0_base + PCIE_INT_DEC_REG_BASE);
+ (void)readl(hws->bar0_base + PCIE_INT_DEC_REG_BASE);
+
+ /* Enable the PCIe bridge. */
+ writel(0x00000001, hws->bar0_base + PCIEBR_EN_REG_BASE);
+ (void)readl(hws->bar0_base + PCIEBR_EN_REG_BASE);
+
+ /* Open the global/bridge gate (legacy 0x3FFFF) */
+ writel(HWS_INT_EN_MASK, hws->bar0_base + INT_EN_REG_BASE);
+ (void)readl(hws->bar0_base + INT_EN_REG_BASE);
+}
+
+void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable)
+{
+ int i;
+
+ if (hws->start_run && !enable)
+ return;
+
+ /* 1) reset the decoder mode register to 0 */
+ writel(0x00000000, hws->bar0_base + HWS_REG_DEC_MODE);
+ hws_seed_dma_windows(hws);
+
+ /* 3) on a full reset, clear all per-channel status and indices */
+ if (!enable) {
+ for (i = 0; i < hws->max_channels; i++) {
+ /* helpers to arm/disable capture engines */
+ hws_enable_video_capture(hws, i, false);
+ }
+ }
+
+ /* 4) Start run: set bit31, wait a bit, then program low 24 bits. */
+ writel(0x80000000, hws->bar0_base + HWS_REG_DEC_MODE);
+ writel(0x80FFFFFF, hws->bar0_base + HWS_REG_DEC_MODE);
+ writel(0x13, hws->bar0_base + HWS_REG_DEC_MODE);
+ hws_ack_all_irqs(hws);
+ hws_open_irq_fabric(hws);
+ /* 6) record that we're now running */
+ hws->start_run = true;
+}
+
+int hws_check_card_status(struct hws_pcie_dev *hws)
+{
+ u32 status;
+
+ if (!hws || !hws->bar0_base)
+ return -ENODEV;
+
+ status = readl(hws->bar0_base + HWS_REG_SYS_STATUS);
+
+ /* Common device-missing pattern. */
+ if (status == 0xFFFFFFFF) {
+ hws->pci_lost = true;
+ dev_err(&hws->pdev->dev, "PCIe device not responding\n");
+ return -ENODEV;
+ }
+
+ /* If RUN/READY bit (bit0) is not set, reinitialize the video core. */
+ if (!(status & BIT(0))) {
+ dev_dbg(&hws->pdev->dev,
+ "SYS_STATUS not ready (0x%08x), reinitializing\n",
+ status);
+ hws_init_video_sys(hws, true);
+ }
+
+ return 0;
+}
+
+void check_video_format(struct hws_pcie_dev *pdx)
+{
+ int i;
+
+ for (i = 0; i < pdx->cur_max_video_ch; i++) {
+ bool interlace = false;
+
+ if (!hws_read_active_state(pdx, i, &interlace)) {
+ /* No active video; optionally feed neutral frames to keep streaming. */
+ if (pdx->video[i].signal_loss_cnt == 0)
+ pdx->video[i].signal_loss_cnt = 1;
+ if (READ_ONCE(pdx->video[i].cap_active))
+ hws_force_no_signal_frame(&pdx->video[i],
+ "monitor_nosignal");
+ } else {
+ if (pdx->hw_ver > 0)
+ handle_hwv2_path(pdx, i);
+ else
+ /* Legacy path stub; see handle_legacy_path() comment. */
+ handle_legacy_path(pdx, i);
+
+ update_live_resolution(pdx, i, interlace);
+ pdx->video[i].signal_loss_cnt = 0;
+ }
+ }
+}
+
+static inline void hws_write_if_diff(struct hws_pcie_dev *hws, u32 reg_off,
+ u32 new_val)
+{
+ void __iomem *addr;
+ u32 old;
+
+ if (!hws || !hws->bar0_base)
+ return;
+
+ addr = hws->bar0_base + reg_off;
+
+ old = readl(addr);
+ /* Treat all-ones as device gone; avoid writing garbage. */
+ if (old == 0xFFFFFFFF) {
+ hws->pci_lost = true;
+ return;
+ }
+
+ if (old != new_val) {
+ writel(new_val, addr);
+ /* Post the write on some bridges / enforce ordering. */
+ (void)readl(addr);
+ }
+}
+
+static bool hws_read_active_state(struct hws_pcie_dev *pdx, unsigned int ch,
+ bool *interlace)
+{
+ u32 reg;
+ bool active;
+
+ if (ch >= pdx->cur_max_video_ch)
+ return false;
+
+ reg = readl(pdx->bar0_base + HWS_REG_ACTIVE_STATUS);
+ active = !!(reg & BIT(ch));
+ if (interlace)
+ *interlace = !!(reg & BIT(8 + ch));
+ return active;
+}
+
+/* Modern hardware path: keep HW registers in sync with current per-channel
+ * software state.
+ */
+static void handle_hwv2_path(struct hws_pcie_dev *hws, unsigned int ch)
+{
+ struct hws_video *vid;
+ u32 reg, in_fps, cur_out_res, want_out_res;
+
+ if (!hws || !hws->bar0_base || ch >= hws->max_channels)
+ return;
+
+ vid = &hws->video[ch];
+
+ /* 1) Input frame rate (read-only; log or export via debugfs if wanted) */
+ in_fps = readl(hws->bar0_base + HWS_REG_FRAME_RATE(ch));
+ /* dev_dbg(&hws->pdev->dev, "ch%u input fps=%u\n", ch, in_fps); */
+ (void)in_fps;
+
+ /* 2) Output resolution programming.
+ * For now, mirror the current format to OUT_RES.
+ */
+ want_out_res = (vid->pix.height << 16) | vid->pix.width;
+ cur_out_res = readl(hws->bar0_base + HWS_REG_OUT_RES(ch));
+ if (cur_out_res != want_out_res)
+ hws_write_if_diff(hws, HWS_REG_OUT_RES(ch), want_out_res);
+
+ /* 3) Output FPS: only program if you actually track a target.
+ * Example heuristic (disabled by default):
+ *
+ * u32 out_fps = (vid->fmt_curr.height >= 1080) ? 60 : 30;
+ * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), out_fps);
+ */
+
+ /* 4) BCHS controls: pack from per-channel current_* fields */
+ reg = readl(hws->bar0_base + HWS_REG_BCHS(ch));
+ {
+ u8 br = reg & 0xFF;
+ u8 co = (reg >> 8) & 0xFF;
+ u8 hu = (reg >> 16) & 0xFF;
+ u8 sa = (reg >> 24) & 0xFF;
+
+ if (br != vid->current_brightness ||
+ co != vid->current_contrast || hu != vid->current_hue ||
+ sa != vid->current_saturation) {
+ u32 packed = (vid->current_saturation << 24) |
+ (vid->current_hue << 16) |
+ (vid->current_contrast << 8) |
+ vid->current_brightness;
+ hws_write_if_diff(hws, HWS_REG_BCHS(ch), packed);
+ }
+ }
+
+ /* 5) HDCP detect: read only (no cache field in your structs today) */
+ reg = readl(hws->bar0_base + HWS_REG_HDCP_STATUS);
+ /* bool hdcp = !!(reg & BIT(ch)); // use if you later add a field/control */
+}
+
+static void handle_legacy_path(struct hws_pcie_dev *hws, unsigned int ch)
+{
+ /*
+ * Legacy (hw_ver == 0) expected behavior:
+ * - A per-channel SW FPS accumulator incremented on each VDONE.
+ * - A once-per-second poll mapped the count to discrete FPS:
+ * >55*2 => 60, >45*2 => 50, >25*2 => 30, >20*2 => 25, else 60,
+ * then reset the accumulator to 0.
+ * - The *2 factor assumed VDONE fired per-field; if legacy VDONE is
+ * per-frame, drop the factor.
+ *
+ * Current code keeps this path as a no-op; vid->current_fps stays at the
+ * default or mode-derived value. If accurate legacy FPS reporting is
+ * needed (V4L2 g_parm/timeperframe), reintroduce the accumulator in the
+ * IRQ path and perform the mapping/reset here.
+ *
+ * No-op by default. If you introduce a SW FPS accumulator, map it here.
+ *
+ * Example skeleton:
+ *
+ * u32 sw_rate = READ_ONCE(hws->sw_fps[ch]); // incremented elsewhere
+ * if (sw_rate > THRESHOLD) {
+ * u32 fps = pick_fps_from_rate(sw_rate);
+ * hws_write_if_diff(hws, HWS_REG_OUT_FRAME_RATE(ch), fps);
+ * WRITE_ONCE(hws->sw_fps[ch], 0);
+ * }
+ */
+ (void)hws;
+ (void)ch;
+}
+
+static void hws_video_apply_mode_change(struct hws_pcie_dev *pdx,
+ unsigned int ch, u16 w, u16 h,
+ bool interlaced, u32 fps)
+{
+ struct hws_video *v = &pdx->video[ch];
+ unsigned long flags;
+ bool queue_busy;
+ bool geometry_changed;
+ struct list_head done;
+ struct hwsvideo_buffer *b, *tmp;
+
+ if (!pdx || !pdx->bar0_base)
+ return;
+ if (ch >= pdx->max_channels)
+ return;
+ if (!w || !h || w > MAX_VIDEO_HW_W ||
+ (!interlaced && h > MAX_VIDEO_HW_H) ||
+ (interlaced && (h * 2) > MAX_VIDEO_HW_H))
+ return;
+ if (!fps || fps == 0xFFFFFFFF || fps > 240)
+ fps = (h == 576) ? 50 : 60;
+
+ geometry_changed = w != v->pix.width || h != v->pix.height ||
+ interlaced != v->pix.interlaced;
+ if (!geometry_changed && fps == v->current_fps)
+ return;
+
+ if (!geometry_changed) {
+ /* Refresh cached live timing state, but don't emit a resolution
+ * change event when only the frame rate changes.
+ */
+ mutex_lock(&v->state_lock);
+ v->pix.interlaced = interlaced;
+ v->pix.field = interlaced ? V4L2_FIELD_INTERLACED :
+ V4L2_FIELD_NONE;
+ hws_set_current_dv_timings(v, w, h, interlaced);
+ v->current_fps = fps;
+ mutex_unlock(&v->state_lock);
+ return;
+ }
+
+ if (!mutex_trylock(&v->state_lock))
+ return;
+
+ INIT_LIST_HEAD(&done);
+ queue_busy = vb2_is_busy(&v->buffer_queue);
+
+ WRITE_ONCE(v->stop_requested, true);
+ WRITE_ONCE(v->cap_active, false);
+ /* Publish software stop first so the IRQ completion path sees the stop
+ * before we touch MMIO or the lists. Pairs with READ_ONCE() checks in the
+ * VDONE handler and hws_arm_next() to prevent completions while modes
+ * change.
+ */
+ smp_wmb();
+
+ hws_enable_video_capture(pdx, ch, false);
+ readl(pdx->bar0_base + HWS_REG_INT_STATUS);
+
+ if (v->parent && v->parent->irq >= 0)
+ synchronize_irq(v->parent->irq);
+
+ spin_lock_irqsave(&v->irq_lock, flags);
+ hws_video_collect_done_locked(v, &done);
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+
+ /* Update software pixel state */
+ v->pix.width = w;
+ v->pix.height = h;
+ v->pix.interlaced = interlaced;
+ hws_set_current_dv_timings(v, w, h, interlaced);
+ v->current_fps = fps;
+
+ hws_calc_sizeimage(v, w, h, interlaced);
+ v->window_valid = false;
+
+ /* Geometry changes require userspace renegotiation once buffers exist.
+ * Emit SOURCE_CHANGE, mark the queue in error, and let userspace
+ * STREAMOFF/REQBUFS/STREAMON rather than trying to restart capture
+ * with partially drained in-flight state.
+ */
+ if (queue_busy) {
+ struct v4l2_event ev = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ };
+
+ ev.u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION;
+ v4l2_event_queue(v->video_device, &ev);
+ vb2_queue_error(&v->buffer_queue);
+ } else {
+ WRITE_ONCE(v->stop_requested, false);
+ }
+
+ /* Program HW with new resolution */
+ hws_write_if_diff(pdx, HWS_REG_OUT_RES(ch), (h << 16) | w);
+
+ /* Legacy half-buffer programming */
+ writel(v->pix.half_size / 16,
+ pdx->bar0_base + CVBS_IN_BUF_BASE2 + ch * PCIE_BARADDROFSIZE);
+ (void)readl(pdx->bar0_base + CVBS_IN_BUF_BASE2 +
+ ch * PCIE_BARADDROFSIZE);
+
+ /* Reset per-channel toggles/counters */
+ WRITE_ONCE(v->last_buf_half_toggle, 0);
+ atomic_set(&v->sequence_number, 0);
+
+ mutex_unlock(&v->state_lock);
+
+ list_for_each_entry_safe(b, tmp, &done, list) {
+ list_del_init(&b->list);
+ vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ }
+}
+
+static void update_live_resolution(struct hws_pcie_dev *pdx, unsigned int ch,
+ bool interlace)
+{
+ u32 reg = readl(pdx->bar0_base + HWS_REG_IN_RES(ch));
+ u32 fps = readl(pdx->bar0_base + HWS_REG_FRAME_RATE(ch));
+ u16 res_w = reg & 0xFFFF;
+ u16 res_h = (reg >> 16) & 0xFFFF;
+ struct hws_video *vid = &pdx->video[ch];
+ bool geometry_changed;
+ bool fps_changed;
+
+ bool within_hw = (res_w <= MAX_VIDEO_HW_W) &&
+ ((!interlace && res_h <= MAX_VIDEO_HW_H) ||
+ (interlace && (res_h * 2) <= MAX_VIDEO_HW_H));
+
+ if (!within_hw)
+ return;
+
+ geometry_changed = res_w != vid->pix.width ||
+ res_h != vid->pix.height ||
+ interlace != vid->pix.interlaced;
+ fps_changed = fps && fps != 0xFFFFFFFF && fps <= 240 &&
+ fps != vid->current_fps;
+
+ if (geometry_changed || fps_changed)
+ hws_video_apply_mode_change(pdx, ch, res_w, res_h, interlace,
+ fps);
+}
+
+static int hws_open(struct file *file)
+{
+ return v4l2_fh_open(file);
+}
+
+static const struct v4l2_file_operations hws_fops = {
+ .owner = THIS_MODULE,
+ .open = hws_open,
+ .release = vb2_fop_release,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int hws_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subscribe(fh, sub);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subscribe_event(fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops hws_ioctl_fops = {
+ /* Core caps/info */
+ .vidioc_querycap = hws_vidioc_querycap,
+
+ /* Pixel format: still needed to report YUYV etc. */
+ .vidioc_enum_fmt_vid_cap = hws_vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = hws_vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = hws_vidioc_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = hws_vidioc_try_fmt_vid_cap,
+
+ /* Buffer queueing / streaming */
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .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_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+
+ /* Inputs */
+ .vidioc_enum_input = hws_vidioc_enum_input,
+ .vidioc_g_input = hws_vidioc_g_input,
+ .vidioc_s_input = hws_vidioc_s_input,
+
+ /* DV timings (HDMI/DVI/VESA modes) */
+ .vidioc_query_dv_timings = hws_vidioc_query_dv_timings,
+ .vidioc_enum_dv_timings = hws_vidioc_enum_dv_timings,
+ .vidioc_g_dv_timings = hws_vidioc_g_dv_timings,
+ .vidioc_s_dv_timings = hws_vidioc_s_dv_timings,
+ .vidioc_dv_timings_cap = hws_vidioc_dv_timings_cap,
+
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = hws_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+ .vidioc_g_parm = hws_vidioc_g_parm,
+};
+
+static u32 hws_calc_sizeimage(struct hws_video *v, u16 w, u16 h,
+ bool interlaced)
+{
+ /* HWS captures packed YUYV only; stride is 16 bpp aligned to 64 bytes. */
+ u32 lines = h; /* full frame lines for sizeimage */
+ u32 bytesperline = ALIGN(w * 2, 64);
+ u32 sizeimage, half0;
+
+ /* publish into pix, since we now carry these in-state */
+ v->pix.bytesperline = bytesperline;
+ sizeimage = bytesperline * lines;
+
+ half0 = sizeimage / 2;
+
+ v->pix.sizeimage = sizeimage;
+ v->pix.half_size = half0; /* first half; second = sizeimage - half0 */
+ v->pix.field = interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
+
+ return v->pix.sizeimage;
+}
+
+static int hws_queue_setup(struct vb2_queue *q, unsigned int *num_buffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct hws_video *vid = q->drv_priv;
+
+ if (*nplanes) {
+ if (sizes[0] < vid->pix.sizeimage)
+ return -EINVAL;
+ } else {
+ *nplanes = 1;
+ sizes[0] = vid->pix.sizeimage;
+ }
+
+ return 0;
+}
+
+static int hws_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct hws_video *vid = vb->vb2_queue->drv_priv;
+ struct hws_pcie_dev *hws = vid->parent;
+ size_t need = vid->pix.sizeimage;
+ dma_addr_t dma_addr;
+
+ if (vb2_plane_size(vb, 0) < need)
+ return -EINVAL;
+
+ /* Validate DMA address alignment */
+ dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
+ if (dma_addr & 0x3F) { /* 64-byte alignment required */
+ dev_err(&hws->pdev->dev,
+ "Buffer DMA address 0x%llx not 64-byte aligned\n",
+ (unsigned long long)dma_addr);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, need);
+ return 0;
+}
+
+static void hws_buffer_queue(struct vb2_buffer *vb)
+{
+ struct hws_video *vid = vb->vb2_queue->drv_priv;
+ struct hwsvideo_buffer *buf = to_hwsbuf(vb);
+ struct hws_pcie_dev *hws = vid->parent;
+ unsigned long flags;
+
+ dev_dbg(&hws->pdev->dev,
+ "buffer_queue(ch=%u): vb=%p sizeimage=%u q_active=%d\n",
+ vid->channel_index, vb, vid->pix.sizeimage,
+ READ_ONCE(vid->cap_active));
+
+ /* Initialize buffer slot */
+ buf->slot = 0;
+
+ spin_lock_irqsave(&vid->irq_lock, flags);
+ list_add_tail(&buf->list, &vid->capture_queue);
+ vid->queued_count++;
+
+ /* If streaming and no in-flight buffer, prime HW immediately */
+ if (READ_ONCE(vid->cap_active) && !vid->active) {
+ dma_addr_t dma_addr;
+
+ dev_dbg(&hws->pdev->dev,
+ "buffer_queue(ch=%u): priming first vb=%p\n",
+ vid->channel_index, &buf->vb.vb2_buf);
+ list_del_init(&buf->list);
+ vid->queued_count--;
+ vid->active = buf;
+
+ dma_addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ hws_program_dma_for_addr(vid->parent, vid->channel_index,
+ dma_addr);
+ iowrite32(lower_32_bits(dma_addr),
+ hws->bar0_base + HWS_REG_DMA_ADDR(vid->channel_index));
+
+ wmb(); /* ensure descriptors visible before enabling capture */
+ hws_enable_video_capture(hws, vid->channel_index, true);
+ hws_prime_next_locked(vid);
+ } else if (READ_ONCE(vid->cap_active) && vid->active) {
+ hws_prime_next_locked(vid);
+ }
+ spin_unlock_irqrestore(&vid->irq_lock, flags);
+}
+
+static int hws_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+ struct hws_video *v = q->drv_priv;
+ struct hws_pcie_dev *hws = v->parent;
+ struct hwsvideo_buffer *to_program = NULL; /* local copy */
+ struct vb2_buffer *prog_vb2 = NULL;
+ unsigned long flags;
+ int ret;
+
+ dev_dbg(&hws->pdev->dev, "start_streaming: ch=%u count=%u\n",
+ v->channel_index, count);
+
+ ret = hws_check_card_status(hws);
+ if (ret) {
+ struct hwsvideo_buffer *b, *tmp;
+ unsigned long f;
+ LIST_HEAD(queued);
+
+ spin_lock_irqsave(&v->irq_lock, f);
+ if (v->active) {
+ list_add_tail(&v->active->list, &queued);
+ v->active = NULL;
+ }
+ if (v->next_prepared) {
+ list_add_tail(&v->next_prepared->list, &queued);
+ v->next_prepared = NULL;
+ }
+ while (!list_empty(&v->capture_queue)) {
+ b = list_first_entry(&v->capture_queue,
+ struct hwsvideo_buffer, list);
+ list_move_tail(&b->list, &queued);
+ }
+ spin_unlock_irqrestore(&v->irq_lock, f);
+
+ list_for_each_entry_safe(b, tmp, &queued, list) {
+ list_del_init(&b->list);
+ vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
+ }
+ return ret;
+ }
+ (void)hws_read_active_state(hws, v->channel_index,
+ &v->pix.interlaced);
+
+ lockdep_assert_held(&v->state_lock);
+ /* init per-stream state */
+ WRITE_ONCE(v->stop_requested, false);
+ WRITE_ONCE(v->cap_active, true);
+ WRITE_ONCE(v->half_seen, false);
+ WRITE_ONCE(v->last_buf_half_toggle, 0);
+
+ /* Try to prime a buffer, but it's OK if none are queued yet */
+ spin_lock_irqsave(&v->irq_lock, flags);
+ if (!v->active && !list_empty(&v->capture_queue)) {
+ to_program = list_first_entry(&v->capture_queue,
+ struct hwsvideo_buffer, list);
+ list_del_init(&to_program->list);
+ v->queued_count--;
+ v->active = to_program;
+ prog_vb2 = &to_program->vb.vb2_buf;
+ dev_dbg(&hws->pdev->dev,
+ "start_streaming: ch=%u took buffer %p\n",
+ v->channel_index, to_program);
+ }
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+
+ /* Only program/enable HW if we actually have a buffer */
+ if (to_program) {
+ if (!prog_vb2)
+ prog_vb2 = &to_program->vb.vb2_buf;
+ {
+ dma_addr_t dma_addr;
+
+ dma_addr = vb2_dma_contig_plane_dma_addr(prog_vb2, 0);
+ hws_program_dma_for_addr(hws, v->channel_index, dma_addr);
+ iowrite32(lower_32_bits(dma_addr),
+ hws->bar0_base +
+ HWS_REG_DMA_ADDR(v->channel_index));
+ dev_dbg(&hws->pdev->dev,
+ "start_streaming: ch=%u programmed buffer %p dma=0x%08x\n",
+ v->channel_index, to_program,
+ lower_32_bits(dma_addr));
+ (void)readl(hws->bar0_base + HWS_REG_INT_STATUS);
+ }
+
+ wmb(); /* ensure descriptors visible before enabling capture */
+ hws_enable_video_capture(hws, v->channel_index, true);
+ {
+ unsigned long pf;
+
+ spin_lock_irqsave(&v->irq_lock, pf);
+ hws_prime_next_locked(v);
+ spin_unlock_irqrestore(&v->irq_lock, pf);
+ }
+ } else {
+ dev_dbg(&hws->pdev->dev,
+ "start_streaming: ch=%u no buffer yet (will arm on QBUF)\n",
+ v->channel_index);
+ }
+
+ return 0;
+}
+
+static void hws_log_video_state(struct hws_video *v, const char *action,
+ const char *phase)
+{
+ struct hws_pcie_dev *hws = v->parent;
+ unsigned long flags;
+ unsigned int queued = 0;
+ unsigned int tracked = 0;
+ unsigned int seq = 0;
+ struct hwsvideo_buffer *b;
+ bool streaming = vb2_is_streaming(&v->buffer_queue);
+ bool cap_active;
+ bool stop_requested;
+ struct hwsvideo_buffer *active;
+ struct hwsvideo_buffer *next_prepared;
+
+ spin_lock_irqsave(&v->irq_lock, flags);
+ list_for_each_entry(b, &v->capture_queue, list)
+ queued++;
+ cap_active = READ_ONCE(v->cap_active);
+ stop_requested = READ_ONCE(v->stop_requested);
+ active = v->active;
+ next_prepared = v->next_prepared;
+ tracked = v->queued_count;
+ seq = (u32)atomic_read(&v->sequence_number);
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+
+ dev_dbg(&hws->pdev->dev,
+ "video:%s:%s ch=%u streaming=%d cap=%d stop=%d active=%p next=%p queued=%u tracked=%u seq=%u\n",
+ action, phase, v->channel_index, streaming, cap_active,
+ stop_requested, active, next_prepared, queued, tracked, seq);
+}
+
+static void hws_stop_streaming(struct vb2_queue *q)
+{
+ struct hws_video *v = q->drv_priv;
+ struct hws_pcie_dev *hws = v->parent;
+ unsigned long flags;
+ struct hwsvideo_buffer *b, *tmp;
+ LIST_HEAD(done);
+ unsigned int done_cnt = 0;
+ u64 start_ns = ktime_get_mono_fast_ns();
+
+ hws_log_video_state(v, "streamoff", "begin");
+
+ /* 1) Quiesce SW/HW first */
+ lockdep_assert_held(&v->state_lock);
+ WRITE_ONCE(v->cap_active, false);
+ WRITE_ONCE(v->stop_requested, true);
+
+ hws_enable_video_capture(v->parent, v->channel_index, false);
+
+ /* 2) Collect in-flight + queued under the IRQ lock */
+ spin_lock_irqsave(&v->irq_lock, flags);
+ hws_video_collect_done_locked(v, &done);
+ spin_unlock_irqrestore(&v->irq_lock, flags);
+
+ /* 3) Complete outside the lock */
+ list_for_each_entry_safe(b, tmp, &done, list) {
+ /* Unlink from 'done' before completing */
+ list_del_init(&b->list);
+ vb2_buffer_done(&b->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ done_cnt++;
+ }
+ dev_dbg(&hws->pdev->dev,
+ "video:streamoff:done ch=%u completed=%u (%lluus)\n",
+ v->channel_index, done_cnt, hws_elapsed_us(start_ns));
+ hws_log_video_state(v, "streamoff", "end");
+}
+
+static const struct vb2_ops hwspcie_video_qops = {
+ .queue_setup = hws_queue_setup,
+ .buf_prepare = hws_buffer_prepare,
+ .buf_init = hws_buf_init,
+ .buf_finish = hws_buf_finish,
+ .buf_cleanup = hws_buf_cleanup,
+ .buf_queue = hws_buffer_queue,
+ .start_streaming = hws_start_streaming,
+ .stop_streaming = hws_stop_streaming,
+};
+
+int hws_video_register(struct hws_pcie_dev *dev)
+{
+ int i, ret;
+
+ ret = v4l2_device_register(&dev->pdev->dev, &dev->v4l2_device);
+ if (ret) {
+ dev_err(&dev->pdev->dev, "v4l2_device_register failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ for (i = 0; i < dev->cur_max_video_ch; i++) {
+ struct hws_video *ch = &dev->video[i];
+ struct video_device *vdev;
+ struct vb2_queue *q;
+
+ /* hws_video_init_channel() should have set:
+ * - ch->parent, ch->channel_index
+ * - locks (state_lock, irq_lock)
+ * - capture_queue (INIT_LIST_HEAD)
+ * - control_handler + controls
+ * - fmt_curr (width/height)
+ * Do not reinitialize any of those here.
+ */
+
+ vdev = video_device_alloc();
+ if (!vdev) {
+ dev_err(&dev->pdev->dev,
+ "video_device_alloc ch%u failed\n", i);
+ ret = -ENOMEM;
+ goto err_unwind;
+ }
+ ch->video_device = vdev;
+
+ /* Basic V4L2 node setup */
+ snprintf(vdev->name, sizeof(vdev->name), "%s-hdmi%u",
+ KBUILD_MODNAME, i);
+ vdev->v4l2_dev = &dev->v4l2_device;
+ vdev->fops = &hws_fops;
+ vdev->ioctl_ops = &hws_ioctl_fops;
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ vdev->lock = &ch->state_lock; /* serialize file ops */
+ vdev->ctrl_handler = &ch->control_handler;
+ vdev->vfl_dir = VFL_DIR_RX;
+ vdev->release = video_device_release;
+ if (ch->control_handler.error) {
+ ret = ch->control_handler.error;
+ goto err_unwind;
+ }
+ video_set_drvdata(vdev, ch);
+
+ /* vb2 queue init (dma-contig) */
+ q = &ch->buffer_queue;
+ memset(q, 0, sizeof(*q));
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = ch;
+ q->buf_struct_size = sizeof(struct hwsvideo_buffer);
+ q->ops = &hwspcie_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->lock = &ch->state_lock;
+ q->min_queued_buffers = 1;
+ q->dev = &dev->pdev->dev;
+
+ ret = vb2_queue_init(q);
+ vdev->queue = q;
+ if (ret) {
+ dev_err(&dev->pdev->dev,
+ "vb2_queue_init ch%u failed: %d\n", i, ret);
+ goto err_unwind;
+ }
+
+ /* Make controls live (no-op if none or already set up) */
+ if (ch->control_handler.error) {
+ ret = ch->control_handler.error;
+ dev_err(&dev->pdev->dev,
+ "ctrl handler ch%u error: %d\n", i, ret);
+ goto err_unwind;
+ }
+ v4l2_ctrl_handler_setup(&ch->control_handler);
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(&dev->pdev->dev,
+ "video_register_device ch%u failed: %d\n", i,
+ ret);
+ goto err_unwind;
+ }
+ }
+
+ return 0;
+
+err_unwind:
+ for (; i >= 0; i--) {
+ struct hws_video *ch = &dev->video[i];
+
+ hws_video_release_registration(ch);
+ }
+ v4l2_device_unregister(&dev->v4l2_device);
+ return ret;
+}
+
+void hws_video_unregister(struct hws_pcie_dev *dev)
+{
+ int i;
+
+ if (!dev)
+ return;
+
+ for (i = 0; i < dev->cur_max_video_ch; i++) {
+ struct hws_video *ch = &dev->video[i];
+
+ hws_video_release_registration(ch);
+ v4l2_ctrl_handler_free(&ch->control_handler);
+ }
+ v4l2_device_unregister(&dev->v4l2_device);
+}
+
+int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason)
+{
+ int i, ret = 0;
+ u64 start_ns = ktime_get_mono_fast_ns();
+
+ dev_dbg(&hws->pdev->dev, "video:%s:begin channels=%u\n", reason,
+ hws->cur_max_video_ch);
+ for (i = 0; i < hws->cur_max_video_ch; i++) {
+ struct hws_video *vid = &hws->video[i];
+ struct vb2_queue *q = &vid->buffer_queue;
+ u64 ch_start_ns = ktime_get_mono_fast_ns();
+ bool streaming;
+
+ if (!q || !q->ops) {
+ dev_dbg(&hws->pdev->dev,
+ "video:%s:ch=%d skipped queue-unavailable\n",
+ reason, i);
+ continue;
+ }
+
+ streaming = vb2_is_streaming(q);
+ hws_log_video_state(vid, reason, "channel");
+ if (streaming) {
+ /* Stop via vb2, which runs .stop_streaming. */
+ int r = vb2_streamoff(q, q->type);
+
+ dev_dbg(&hws->pdev->dev,
+ "video:%s:ch=%d streamoff ret=%d (%lluus)\n",
+ reason, i, r, hws_elapsed_us(ch_start_ns));
+ if (r && !ret)
+ ret = r;
+ } else {
+ dev_dbg(&hws->pdev->dev,
+ "video:%s:ch=%d idle (%lluus)\n",
+ reason, i, hws_elapsed_us(ch_start_ns));
+ }
+ }
+ dev_dbg(&hws->pdev->dev, "video:%s:done ret=%d (%lluus)\n", reason,
+ ret, hws_elapsed_us(start_ns));
+ return ret;
+}
+
+void hws_video_pm_resume(struct hws_pcie_dev *hws)
+{
+ /* Nothing mandatory to do here for vb2; userspace will STREAMON
+ * again when ready.
+ */
+}
diff --git a/drivers/media/pci/hws/hws_video.h b/drivers/media/pci/hws/hws_video.h
new file mode 100644
index 000000000000..4feaf5b2f5a9
--- /dev/null
+++ b/drivers/media/pci/hws/hws_video.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef HWS_VIDEO_H
+#define HWS_VIDEO_H
+
+struct hws_video;
+
+int hws_video_register(struct hws_pcie_dev *dev);
+void hws_video_unregister(struct hws_pcie_dev *dev);
+void hws_enable_video_capture(struct hws_pcie_dev *hws,
+ unsigned int chan,
+ bool on);
+void hws_prime_next_locked(struct hws_video *vid);
+
+int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch);
+void hws_video_cleanup_channel(struct hws_pcie_dev *pdev, int ch);
+void check_video_format(struct hws_pcie_dev *pdx);
+int hws_check_card_status(struct hws_pcie_dev *hws);
+void hws_init_video_sys(struct hws_pcie_dev *hws, bool enable);
+
+void hws_program_dma_for_addr(struct hws_pcie_dev *hws,
+ unsigned int ch,
+ dma_addr_t dma);
+void hws_set_dma_doorbell(struct hws_pcie_dev *hws, unsigned int ch,
+ dma_addr_t dma, const char *tag);
+
+int hws_video_quiesce(struct hws_pcie_dev *hws, const char *reason);
+void hws_video_pm_resume(struct hws_pcie_dev *hws);
+
+#endif
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index fc6608e33de4..88581a4c081d 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -168,6 +168,9 @@ static const struct acpi_device_id ivsc_acpi_ids[] = {
{ "INTC1095" },
{ "INTC100A" },
{ "INTC10CF" },
+ { "INTC10DE" }, /* LNL */
+ { "INTC10E0" }, /* ARL */
+ { "INTC10E1" }, /* PTL */
};
static struct acpi_device *ipu_bridge_get_ivsc_acpi_dev(struct acpi_device *adev)
@@ -221,7 +224,13 @@ static struct device *ipu_bridge_get_ivsc_csi_dev(struct acpi_device *adev)
return csi_dev;
}
- return NULL;
+ /* Try to locate CVS device on the I2C bus */
+ csi_dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev);
+ if (csi_dev)
+ return csi_dev;
+
+ /* Fallback to platform bus for CVS device */
+ return bus_find_device_by_acpi_dev(&platform_bus_type, adev);
}
static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor,
@@ -235,7 +244,7 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor,
csi_dev = ipu_bridge_get_ivsc_csi_dev(adev);
if (!csi_dev) {
acpi_dev_put(adev);
- dev_err(ADEV_DEV(adev), "Failed to find MEI CSI dev\n");
+ dev_err(ADEV_DEV(adev), "Failed to find MEI or CVS CSI dev\n");
return -ENODEV;
}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
index db2874843453..237906cb131a 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-dwc-phy.c
@@ -288,15 +288,27 @@ static const struct dwc_dphy_freq_range freqranges[DPHY_FREQ_RANGE_NUM] = {
static u16 get_hsfreq_by_mbps(u32 mbps)
{
- unsigned int i = DPHY_FREQ_RANGE_NUM;
-
- while (i--) {
- if (freqranges[i].default_mbps == mbps ||
- (mbps >= freqranges[i].min && mbps <= freqranges[i].max))
- return i;
+ u16 best = DPHY_FREQ_RANGE_INVALID_INDEX;
+ unsigned int i;
+
+ for (i = 0; i < DPHY_FREQ_RANGE_NUM; i++) {
+ if (mbps > freqranges[i].max)
+ continue;
+
+ if (mbps < freqranges[i].min)
+ break;
+
+ if (best == DPHY_FREQ_RANGE_INVALID_INDEX ||
+ freqranges[i].osc_freq_target >
+ freqranges[best].osc_freq_target ||
+ (freqranges[i].osc_freq_target ==
+ freqranges[best].osc_freq_target &&
+ abs((int)mbps - (int)freqranges[i].default_mbps) <
+ abs((int)mbps - (int)freqranges[best].default_mbps)))
+ best = i;
}
- return DPHY_FREQ_RANGE_INVALID_INDEX;
+ return best;
}
static int ipu6_isys_dwc_phy_config(struct ipu6_isys *isys,
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index 8d5ea3aec06f..fc95f0bf48d5 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -467,15 +467,13 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format
int h = fmt->fmt.pix.height;
int min_h = 2;
- w = min(w, 720);
- w = max(w, 2);
+ w = clamp(w, 2, 720);
if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
/* YUV height must be a multiple of 32 */
h &= ~0x1f;
min_h = 32;
}
- h = min(h, itv->is_50hz ? 576 : 480);
- h = max(h, min_h);
+ h = clamp(h, min_h, itv->is_50hz ? 576 : 480);
ivtv_g_fmt_vid_cap(file, fh, fmt);
fmt->fmt.pix.width = w;
fmt->fmt.pix.height = h;
@@ -516,8 +514,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format
int field = fmt->fmt.pix.field;
int ret = ivtv_g_fmt_vid_out(file, fh, fmt);
- w = min(w, 720);
- w = max(w, 2);
+ w = clamp(w, 2, 720);
/* Why can the height be 576 even when the output is NTSC?
Internally the buffers of the PVR350 are always set to 720x576. The
@@ -533,8 +530,7 @@ static int ivtv_try_fmt_vid_out(struct file *file, void *fh, struct v4l2_format
resolution is locked to the broadcast standard and not scaled.
Thanks to Ian Armstrong for this explanation. */
- h = min(h, 576);
- h = max(h, 2);
+ h = clamp(h, 2, 576);
if (id->type == IVTV_DEC_STREAM_TYPE_YUV)
fmt->fmt.pix.field = field;
fmt->fmt.pix.width = w;
diff --git a/drivers/media/pci/mgb4/mgb4_vin.c b/drivers/media/pci/mgb4/mgb4_vin.c
index 74fb00c4a408..b24785455691 100644
--- a/drivers/media/pci/mgb4/mgb4_vin.c
+++ b/drivers/media/pci/mgb4/mgb4_vin.c
@@ -85,12 +85,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = {
static const struct v4l2_dv_timings_cap video_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
- .min_width = 240,
+ .min_width = 64,
.max_width = 4096,
- .min_height = 240,
+ .min_height = 64,
.max_height = 4096,
- .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */
- .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */
+ .min_pixelclock = 20000000,
+ .max_pixelclock = 200000000,
.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF,
.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
diff --git a/drivers/media/pci/mgb4/mgb4_vout.c b/drivers/media/pci/mgb4/mgb4_vout.c
index 7725bcd55e4c..22fbc9e53819 100644
--- a/drivers/media/pci/mgb4/mgb4_vout.c
+++ b/drivers/media/pci/mgb4/mgb4_vout.c
@@ -53,12 +53,12 @@ static const struct mgb4_i2c_kv gmsl1_i2c[] = {
static const struct v4l2_dv_timings_cap video_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
- .min_width = 240,
+ .min_width = 64,
.max_width = 4096,
- .min_height = 240,
+ .min_height = 64,
.max_height = 4096,
- .min_pixelclock = 1843200, /* 320 x 240 x 24Hz */
- .max_pixelclock = 530841600, /* 4096 x 2160 x 60Hz */
+ .min_pixelclock = 25000000,
+ .max_pixelclock = 189284000,
.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
V4L2_DV_BT_STD_CVT | V4L2_DV_BT_STD_GTF,
.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index 4a51b873e47a..2b1672737d84 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -1714,8 +1714,10 @@ int saa7134_video_init1(struct saa7134_dev *dev)
q->dev = &dev->pci->dev;
ret = vb2_queue_init(q);
if (ret)
- return ret;
- saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
+ goto err_free_ctrl;
+ ret = saa7134_pgtable_alloc(dev->pci, &dev->video_q.pt);
+ if (ret)
+ goto err_free_ctrl;
q = &dev->vbi_vbq;
q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
@@ -1732,11 +1734,24 @@ int saa7134_video_init1(struct saa7134_dev *dev)
q->lock = &dev->lock;
q->dev = &dev->pci->dev;
ret = vb2_queue_init(q);
- if (ret)
- return ret;
- saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
+ if (ret) {
+ saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
+ goto err_free_ctrl;
+ }
+
+ ret = saa7134_pgtable_alloc(dev->pci, &dev->vbi_q.pt);
+ if (ret) {
+ saa7134_pgtable_free(dev->pci, &dev->video_q.pt);
+ goto err_free_ctrl;
+ }
return 0;
+
+err_free_ctrl:
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+ if (card_has_radio(dev))
+ v4l2_ctrl_handler_free(&dev->radio_ctrl_handler);
+ return ret;
}
void saa7134_video_fini(struct saa7134_dev *dev)
diff --git a/drivers/media/pci/solo6x10/Kconfig b/drivers/media/pci/solo6x10/Kconfig
index adb247847e00..cc4a7b088370 100644
--- a/drivers/media/pci/solo6x10/Kconfig
+++ b/drivers/media/pci/solo6x10/Kconfig
@@ -8,7 +8,6 @@ config VIDEO_SOLO6X10
select VIDEOBUF2_DMA_SG
select VIDEOBUF2_DMA_CONTIG
select SND_PCM
- select FONT_8x16
help
This driver supports the Bluecherry H.264 and MPEG-4 hardware
compression capture cards and other Softlogic-based ones.
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 3f0b7bb68cc9..0b33e927bd59 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -63,6 +63,7 @@ config VIDEO_MUX
# Platform drivers - Please keep it alphabetically sorted
source "drivers/media/platform/allegro-dvt/Kconfig"
+source "drivers/media/platform/amd/Kconfig"
source "drivers/media/platform/amlogic/Kconfig"
source "drivers/media/platform/amphion/Kconfig"
source "drivers/media/platform/arm/Kconfig"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 6d5f79ddfcc3..16c185752474 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -6,6 +6,7 @@
# Place here, alphabetically sorted by directory
# (e. g. LC_ALL=C sort Makefile)
obj-y += allegro-dvt/
+obj-y += amd/
obj-y += amlogic/
obj-y += amphion/
obj-y += arm/
diff --git a/drivers/media/platform/amd/Kconfig b/drivers/media/platform/amd/Kconfig
new file mode 100644
index 000000000000..25af49f246b2
--- /dev/null
+++ b/drivers/media/platform/amd/Kconfig
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+source "drivers/media/platform/amd/isp4/Kconfig"
diff --git a/drivers/media/platform/amd/Makefile b/drivers/media/platform/amd/Makefile
new file mode 100644
index 000000000000..8bfc1955f22e
--- /dev/null
+++ b/drivers/media/platform/amd/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-y += isp4/
diff --git a/drivers/media/platform/amd/isp4/Kconfig b/drivers/media/platform/amd/isp4/Kconfig
new file mode 100644
index 000000000000..9d1927af1cb8
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/Kconfig
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+config VIDEO_AMD_ISP4_CAPTURE
+ tristate "AMD ISP4 and camera driver"
+ depends on DRM_AMDGPU && DRM_AMD_ISP
+ depends on HAS_DMA
+ depends on VIDEO_DEV
+ select VIDEOBUF2_CORE
+ select VIDEOBUF2_MEMOPS
+ select VIDEOBUF2_V4L2
+ select VIDEOBUF2_VMALLOC
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This is support for AMD ISP4 and camera subsystem driver.
+ Say Y here to enable the ISP4 and camera device for video capture.
+ To compile this driver as a module, choose M here. The module will
+ be called amd_isp4_capture.
diff --git a/drivers/media/platform/amd/isp4/Makefile b/drivers/media/platform/amd/isp4/Makefile
new file mode 100644
index 000000000000..3849062e17f3
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2025 Advanced Micro Devices, Inc.
+
+obj-$(CONFIG_VIDEO_AMD_ISP4_CAPTURE) += amd_isp4_capture.o
+amd_isp4_capture-objs := isp4.o \
+ isp4_debug.o \
+ isp4_interface.o \
+ isp4_subdev.o \
+ isp4_video.o
diff --git a/drivers/media/platform/amd/isp4/isp4.c b/drivers/media/platform/amd/isp4/isp4.c
new file mode 100644
index 000000000000..bf6b8e26c2c0
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/vmalloc.h>
+#include <media/v4l2-ioctl.h>
+
+#include "isp4.h"
+#include "isp4_debug.h"
+#include "isp4_hw_reg.h"
+
+#define ISP4_DRV_NAME "amd_isp_capture"
+#define ISP4_FW_RESP_RB_IRQ_STATUS_MASK \
+ (ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK | \
+ ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK)
+
+static const struct {
+ const char *name;
+ u32 status_mask;
+ u32 en_mask;
+ u32 ack_mask;
+ u32 rb_int_num;
+} isp4_irq[ISP4SD_MAX_FW_RESP_STREAM_NUM] = {
+ /* The IRQ order is aligned with the isp4_subdev.fw_resp_thread order */
+ {
+ .name = "isp_irq_global",
+ .status_mask =
+ ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK,
+ .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK,
+ .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK,
+ .rb_int_num = 4, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT12 */
+ },
+ {
+ .name = "isp_irq_stream1",
+ .status_mask =
+ ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK,
+ .en_mask = ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK,
+ .ack_mask = ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK,
+ .rb_int_num = 0, /* ISP_4_1__SRCID__ISP_RINGBUFFER_WPT9 */
+ },
+};
+
+void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable)
+{
+ u32 intr_en;
+
+ /* Synchronize ISP_SYS_INT0_EN writes with the IRQ handler's writes */
+ spin_lock_irq(&isp_subdev->irq_lock);
+ intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN);
+ if (enable)
+ intr_en |= isp4_irq[index].en_mask;
+ else
+ intr_en &= ~isp4_irq[index].en_mask;
+
+ isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
+ spin_unlock_irq(&isp_subdev->irq_lock);
+}
+
+static void isp4_wake_up_resp_thread(struct isp4_subdev *isp_subdev, u32 index)
+{
+ struct isp4sd_thread_handler *thread_ctx =
+ &isp_subdev->fw_resp_thread[index];
+
+ thread_ctx->resp_ready = true;
+ wake_up_interruptible(&thread_ctx->waitq);
+}
+
+static irqreturn_t isp4_irq_handler(int irq, void *arg)
+{
+ struct isp4_subdev *isp_subdev = arg;
+ u32 intr_ack = 0, intr_en = 0, intr_status;
+ int seen = 0;
+
+ /* Get the ISP_SYS interrupt status */
+ intr_status = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_STATUS);
+ intr_status &= ISP4_FW_RESP_RB_IRQ_STATUS_MASK;
+
+ /* Find which ISP_SYS interrupts fired */
+ for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
+ if (intr_status & isp4_irq[i].status_mask) {
+ intr_ack |= isp4_irq[i].ack_mask;
+ intr_en |= isp4_irq[i].en_mask;
+ seen |= BIT(i);
+ }
+ }
+
+ /*
+ * Disable the ISP_SYS interrupts that fired. Must be done before waking
+ * the response threads, since they re-enable interrupts when finished.
+ * The lock synchronizes RMW of INT0_EN with isp4_enable_interrupt().
+ */
+ spin_lock(&isp_subdev->irq_lock);
+ intr_en = isp4hw_rreg(isp_subdev->mmio, ISP_SYS_INT0_EN) & ~intr_en;
+ isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_EN, intr_en);
+ spin_unlock(&isp_subdev->irq_lock);
+
+ /*
+ * Clear the ISP_SYS interrupts. This must be done after the interrupts
+ * are disabled, so that ISP FW won't flag any new interrupts on these
+ * streams, and thus we don't need to clear interrupts again before
+ * re-enabling them in the response thread.
+ */
+ isp4hw_wreg(isp_subdev->mmio, ISP_SYS_INT0_ACK, intr_ack);
+
+ /*
+ * The operation `(seen >> i) << i` is logically equivalent to
+ * `seen &= ~BIT(i)`, with fewer instructions after compilation.
+ */
+ for (int i; (i = ffs(seen)); seen = (seen >> i) << i)
+ isp4_wake_up_resp_thread(isp_subdev, i - 1);
+
+ return IRQ_HANDLED;
+}
+
+static int isp4_capture_probe(struct platform_device *pdev)
+{
+ int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM];
+ struct device *dev = &pdev->dev;
+ struct isp4_subdev *isp_subdev;
+ struct isp4_device *isp_dev;
+ int ret;
+
+ isp_dev = devm_kzalloc(dev, sizeof(*isp_dev), GFP_KERNEL);
+ if (!isp_dev)
+ return -ENOMEM;
+
+ dev->init_name = ISP4_DRV_NAME;
+
+ isp_subdev = &isp_dev->isp_subdev;
+ isp_subdev->mmio = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(isp_subdev->mmio))
+ return dev_err_probe(dev, PTR_ERR(isp_subdev->mmio),
+ "isp ioremap fail\n");
+
+ for (size_t i = 0; i < ARRAY_SIZE(isp4_irq); i++) {
+ irq[i] = platform_get_irq(pdev, isp4_irq[i].rb_int_num);
+ if (irq[i] < 0)
+ return dev_err_probe(dev, irq[i],
+ "fail to get irq %d\n",
+ isp4_irq[i].rb_int_num);
+
+ ret = devm_request_irq(dev, irq[i], isp4_irq_handler,
+ IRQF_NO_AUTOEN, isp4_irq[i].name,
+ isp_subdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "fail to req irq %d\n",
+ irq[i]);
+ }
+
+ isp_dev->v4l2_dev.mdev = &isp_dev->mdev;
+
+ strscpy(isp_dev->mdev.model, "amd_isp41_mdev",
+ sizeof(isp_dev->mdev.model));
+ isp_dev->mdev.dev = dev;
+ media_device_init(&isp_dev->mdev);
+
+ snprintf(isp_dev->v4l2_dev.name, sizeof(isp_dev->v4l2_dev.name),
+ "AMD-V4L2-ROOT");
+ ret = v4l2_device_register(dev, &isp_dev->v4l2_dev);
+ if (ret) {
+ dev_err_probe(dev, ret, "fail register v4l2 device\n");
+ goto err_clean_media;
+ }
+
+ pm_runtime_set_suspended(dev);
+ pm_runtime_enable(dev);
+ spin_lock_init(&isp_subdev->irq_lock);
+ ret = isp4sd_init(&isp_dev->isp_subdev, &isp_dev->v4l2_dev, irq);
+ if (ret) {
+ dev_err_probe(dev, ret, "fail init isp4 sub dev\n");
+ goto err_pm_disable;
+ }
+
+ ret = media_create_pad_link(&isp_dev->isp_subdev.sdev.entity,
+ 0,
+ &isp_dev->isp_subdev.isp_vdev.vdev.entity,
+ 0,
+ MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret) {
+ dev_err_probe(dev, ret, "fail to create pad link\n");
+ goto err_isp4_deinit;
+ }
+
+ ret = media_device_register(&isp_dev->mdev);
+ if (ret) {
+ dev_err_probe(dev, ret, "fail to register media device\n");
+ goto err_isp4_deinit;
+ }
+
+ platform_set_drvdata(pdev, isp_dev);
+ isp_debugfs_create(isp_dev);
+
+ return 0;
+
+err_isp4_deinit:
+ isp4sd_deinit(&isp_dev->isp_subdev);
+err_pm_disable:
+ pm_runtime_disable(dev);
+ v4l2_device_unregister(&isp_dev->v4l2_dev);
+err_clean_media:
+ media_device_cleanup(&isp_dev->mdev);
+
+ return ret;
+}
+
+static void isp4_capture_remove(struct platform_device *pdev)
+{
+ struct isp4_device *isp_dev = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ isp_debugfs_remove(isp_dev);
+
+ media_device_unregister(&isp_dev->mdev);
+ isp4sd_deinit(&isp_dev->isp_subdev);
+ pm_runtime_disable(dev);
+ v4l2_device_unregister(&isp_dev->v4l2_dev);
+ media_device_cleanup(&isp_dev->mdev);
+}
+
+static struct platform_driver isp4_capture_drv = {
+ .probe = isp4_capture_probe,
+ .remove = isp4_capture_remove,
+ .driver = {
+ .name = ISP4_DRV_NAME,
+ }
+};
+
+module_platform_driver(isp4_capture_drv);
+
+MODULE_ALIAS("platform:" ISP4_DRV_NAME);
+MODULE_IMPORT_NS("DMA_BUF");
+
+MODULE_DESCRIPTION("AMD ISP4 Driver");
+MODULE_AUTHOR("Bin Du <bin.du@amd.com>");
+MODULE_AUTHOR("Pratap Nirujogi <pratap.nirujogi@amd.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/amd/isp4/isp4.h b/drivers/media/platform/amd/isp4/isp4.h
new file mode 100644
index 000000000000..2db6683d6d8b
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_H_
+#define _ISP4_H_
+
+#include <drm/amd/isp.h>
+#include "isp4_subdev.h"
+
+struct isp4_device {
+ struct v4l2_device v4l2_dev;
+ struct isp4_subdev isp_subdev;
+ struct media_device mdev;
+};
+
+void isp4_intr_enable(struct isp4_subdev *isp_subdev, u32 index, bool enable);
+
+#endif /* _ISP4_H_ */
diff --git a/drivers/media/platform/amd/isp4/isp4_debug.c b/drivers/media/platform/amd/isp4/isp4_debug.c
new file mode 100644
index 000000000000..2fc00fc9a194
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_debug.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include "isp4.h"
+#include "isp4_debug.h"
+#include "isp4_hw_reg.h"
+#include "isp4_interface.h"
+
+#define ISP4DBG_FW_LOG_RINGBUF_SIZE (2 * 1024 * 1024)
+#define ISP4DBG_MACRO_2_STR(X) #X
+#define ISP4DBG_ONE_TIME_LOG_LEN 510
+
+#ifdef CONFIG_DEBUG_FS
+
+void isp_debugfs_create(struct isp4_device *isp_dev)
+{
+ isp_dev->isp_subdev.debugfs_dir = debugfs_create_dir("amd_isp4", NULL);
+ debugfs_create_bool("fw_log_enable", 0644,
+ isp_dev->isp_subdev.debugfs_dir,
+ &isp_dev->isp_subdev.enable_fw_log);
+ isp_dev->isp_subdev.fw_log_output =
+ devm_kzalloc(isp_dev->isp_subdev.dev,
+ ISP4DBG_FW_LOG_RINGBUF_SIZE + 32,
+ GFP_KERNEL);
+}
+
+void isp_debugfs_remove(struct isp4_device *isp_dev)
+{
+ debugfs_remove_recursive(isp_dev->isp_subdev.debugfs_dir);
+ isp_dev->isp_subdev.debugfs_dir = NULL;
+}
+
+static u32 isp_fw_fill_rb_log(struct isp4_subdev *isp, void *sys, u32 rb_size)
+{
+ struct isp4_interface *ispif = &isp->ispif;
+ char *buf = isp->fw_log_output;
+ struct device *dev = isp->dev;
+ u32 rd_ptr, wr_ptr;
+ u32 total_cnt = 0;
+ u32 offset = 0;
+ u32 cnt;
+
+ if (!sys || !rb_size)
+ return 0;
+
+ guard(mutex)(&ispif->isp4if_mutex);
+
+ rd_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_RPTR0);
+ wr_ptr = isp4hw_rreg(isp->mmio, ISP_LOG_RB_WPTR0);
+
+ do {
+ if (wr_ptr > rd_ptr)
+ cnt = wr_ptr - rd_ptr;
+ else if (wr_ptr < rd_ptr)
+ cnt = rb_size - rd_ptr;
+ else
+ goto quit;
+
+ if (cnt > rb_size) {
+ dev_err(dev, "fail bad fw log size %u\n", cnt);
+ goto quit;
+ }
+
+ memcpy(buf + offset, sys + rd_ptr, cnt);
+
+ offset += cnt;
+ total_cnt += cnt;
+ rd_ptr = (rd_ptr + cnt) % rb_size;
+ } while (rd_ptr < wr_ptr);
+
+ isp4hw_wreg(isp->mmio, ISP_LOG_RB_RPTR0, rd_ptr);
+
+quit:
+ return total_cnt;
+}
+
+void isp_fw_log_print(struct isp4_subdev *isp)
+{
+ struct isp4_interface *ispif = &isp->ispif;
+ char *fw_log_buf = isp->fw_log_output;
+ u32 cnt;
+
+ if (!isp->enable_fw_log || !fw_log_buf)
+ return;
+
+ cnt = isp_fw_fill_rb_log(isp, ispif->fw_log_buf->sys_addr,
+ ispif->fw_log_buf->mem_size);
+
+ if (cnt) {
+ char temp_ch;
+ char *str;
+ char *end;
+ /* line end */
+ char *le;
+
+ str = (char *)fw_log_buf;
+ end = ((char *)fw_log_buf + cnt);
+ fw_log_buf[cnt] = 0;
+
+ while (str < end) {
+ le = strchr(str, 0x0A);
+ if ((le && str + ISP4DBG_ONE_TIME_LOG_LEN >= le) ||
+ (!le && str + ISP4DBG_ONE_TIME_LOG_LEN >= end)) {
+ if (le)
+ *le = 0;
+
+ if (*str != '\0')
+ dev_dbg(isp->dev, "%s", str);
+
+ if (le) {
+ *le = 0x0A;
+ str = le + 1;
+ } else {
+ break;
+ }
+ } else {
+ u32 tmp_len = ISP4DBG_ONE_TIME_LOG_LEN;
+
+ temp_ch = str[tmp_len];
+ str[tmp_len] = 0;
+ dev_dbg(isp->dev, "%s", str);
+ str[tmp_len] = temp_ch;
+ str = &str[tmp_len];
+ }
+ }
+ }
+}
+#endif
+
+char *isp4dbg_get_buf_src_str(u32 src)
+{
+ switch (src) {
+ case ISP4FW_BUFFER_SOURCE_STREAM:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_SOURCE_STREAM);
+ default:
+ return "Unknown buf source";
+ }
+}
+
+char *isp4dbg_get_buf_done_str(u32 status)
+{
+ switch (status) {
+ case ISP4FW_BUFFER_STATUS_INVALID:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_INVALID);
+ case ISP4FW_BUFFER_STATUS_SKIPPED:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_SKIPPED);
+ case ISP4FW_BUFFER_STATUS_EXIST:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_EXIST);
+ case ISP4FW_BUFFER_STATUS_DONE:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DONE);
+ case ISP4FW_BUFFER_STATUS_LACK:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_LACK);
+ case ISP4FW_BUFFER_STATUS_DIRTY:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_DIRTY);
+ case ISP4FW_BUFFER_STATUS_MAX:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_STATUS_MAX);
+ default:
+ return "Unknown Buf Done Status";
+ }
+}
+
+char *isp4dbg_get_img_fmt_str(int fmt /* enum isp4fw_image_format * */)
+{
+ switch (fmt) {
+ case ISP4FW_IMAGE_FORMAT_NV12:
+ return "NV12";
+ case ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED:
+ return "YUV422INTERLEAVED";
+ default:
+ return "unknown fmt";
+ }
+}
+
+void isp4dbg_show_bufmeta_info(struct device *dev, char *pre,
+ void *in, void *orig_buf)
+{
+ struct isp4fw_buffer_meta_info *p;
+ struct isp4if_img_buf_info *orig;
+
+ if (!in)
+ return;
+
+ if (!pre)
+ pre = "";
+
+ p = in;
+ orig = orig_buf;
+
+ dev_dbg(dev, "%s(%s) en:%d,stat:%s(%u),src:%s\n", pre,
+ isp4dbg_get_img_fmt_str(p->image_prop.image_format),
+ p->enabled, isp4dbg_get_buf_done_str(p->status), p->status,
+ isp4dbg_get_buf_src_str(p->source));
+
+ dev_dbg(dev, "%p,0x%llx(%u) %p,0x%llx(%u) %p,0x%llx(%u)\n",
+ orig->planes[0].sys_addr, orig->planes[0].mc_addr,
+ orig->planes[0].len, orig->planes[1].sys_addr,
+ orig->planes[1].mc_addr, orig->planes[1].len,
+ orig->planes[2].sys_addr, orig->planes[2].mc_addr,
+ orig->planes[2].len);
+}
+
+char *isp4dbg_get_buf_type(u32 type)
+{
+ /* enum isp4fw_buffer_type */
+ switch (type) {
+ case ISP4FW_BUFFER_TYPE_PREVIEW:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_PREVIEW);
+ case ISP4FW_BUFFER_TYPE_META_INFO:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_META_INFO);
+ case ISP4FW_BUFFER_TYPE_MEM_POOL:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_BUFFER_TYPE_MEM_POOL);
+ default:
+ return "unknown type";
+ }
+}
+
+char *isp4dbg_get_cmd_str(u32 cmd)
+{
+ switch (cmd) {
+ case ISP4FW_CMD_ID_START_STREAM:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_START_STREAM);
+ case ISP4FW_CMD_ID_STOP_STREAM:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_STOP_STREAM);
+ case ISP4FW_CMD_ID_SEND_BUFFER:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SEND_BUFFER);
+ case ISP4FW_CMD_ID_SET_STREAM_CONFIG:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_STREAM_CONFIG);
+ case ISP4FW_CMD_ID_SET_OUT_CHAN_PROP:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_SET_OUT_CHAN_PROP);
+ case ISP4FW_CMD_ID_ENABLE_OUT_CHAN:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_CMD_ID_ENABLE_OUT_CHAN);
+ default:
+ return "unknown cmd";
+ }
+}
+
+char *isp4dbg_get_resp_str(u32 cmd)
+{
+ switch (cmd) {
+ case ISP4FW_RESP_ID_CMD_DONE:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_CMD_DONE);
+ case ISP4FW_RESP_ID_NOTI_FRAME_DONE:
+ return ISP4DBG_MACRO_2_STR(ISP4FW_RESP_ID_NOTI_FRAME_DONE);
+ default:
+ return "unknown respid";
+ }
+}
+
+char *isp4dbg_get_if_stream_str(u32 stream /* enum fw_cmd_resp_stream_id */)
+{
+ switch (stream) {
+ case ISP4IF_STREAM_ID_GLOBAL:
+ return "STREAM_GLOBAL";
+ case ISP4IF_STREAM_ID_1:
+ return "STREAM1";
+ default:
+ return "unknown streamID";
+ }
+}
+
+char *isp4dbg_get_out_ch_str(int ch /* enum isp4fw_pipe_out_ch */)
+{
+ switch ((enum isp4fw_pipe_out_ch)ch) {
+ case ISP4FW_ISP_PIPE_OUT_CH_PREVIEW:
+ return "prev";
+ default:
+ return "unknown channel";
+ }
+}
diff --git a/drivers/media/platform/amd/isp4/isp4_debug.h b/drivers/media/platform/amd/isp4/isp4_debug.h
new file mode 100644
index 000000000000..d1262e03ae64
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_debug.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_DEBUG_H_
+#define _ISP4_DEBUG_H_
+
+#include <linux/dev_printk.h>
+#include <linux/printk.h>
+
+#include "isp4_subdev.h"
+
+#ifdef CONFIG_DEBUG_FS
+struct isp4_device;
+
+void isp_debugfs_create(struct isp4_device *isp_dev);
+void isp_debugfs_remove(struct isp4_device *isp_dev);
+void isp_fw_log_print(struct isp4_subdev *isp);
+
+#else
+
+/* to avoid checkpatch warning */
+#define isp_debugfs_create(cam) ((void)(cam))
+#define isp_debugfs_remove(cam) ((void)(cam))
+#define isp_fw_log_print(isp) ((void)(isp))
+
+#endif /* CONFIG_DEBUG_FS */
+
+void isp4dbg_show_bufmeta_info(struct device *dev, char *pre, void *p,
+ void *orig_buf /* struct sys_img_buf_handle */);
+char *isp4dbg_get_img_fmt_str(int fmt /* enum _image_format_t */);
+char *isp4dbg_get_out_ch_str(int ch /* enum _isp_pipe_out_ch_t */);
+char *isp4dbg_get_cmd_str(u32 cmd);
+char *isp4dbg_get_buf_type(u32 type);/* enum _buffer_type_t */
+char *isp4dbg_get_resp_str(u32 resp);
+char *isp4dbg_get_buf_src_str(u32 src);
+char *isp4dbg_get_buf_done_str(u32 status);
+char *isp4dbg_get_if_stream_str(u32 stream);
+
+#endif /* _ISP4_DEBUG_H_ */
diff --git a/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
new file mode 100644
index 000000000000..88bacb00355c
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_fw_cmd_resp.h
@@ -0,0 +1,318 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_FW_CMD_RESP_H_
+#define _ISP4_FW_CMD_RESP_H_
+
+/*
+ * Two types of command/response channel.
+ * Type Global Command has one command/response channel.
+ * Type Stream Command has one command/response channel.
+ *----------- ------------
+ *| | --------------------------- | |
+ *| | ---->| Global Command |----> | |
+ *| | --------------------------- | |
+ *| | | |
+ *| | | |
+ *| | --------------------------- | |
+ *| | ---->| Stream Command |----> | |
+ *| | --------------------------- | |
+ *| | | |
+ *| | | |
+ *| | | |
+ *| HOST | | Firmware |
+ *| | | |
+ *| | | |
+ *| | -------------------------- | |
+ *| | <----| Global Response |<---- | |
+ *| | -------------------------- | |
+ *| | | |
+ *| | | |
+ *| | -------------------------- | |
+ *| | <----| Stream Response |<---- | |
+ *| | -------------------------- | |
+ *| | | |
+ *| | | |
+ *----------- ------------
+ */
+
+/*
+ * cmd_id is in the format of following type:
+ * type: indicate command type, global/stream commands.
+ * group: indicate the command group.
+ * id: A unique command identification in one type and group.
+ * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->|
+ * | type | group | id |
+ */
+
+#define ISP4FW_CMD_TYPE_SHIFT 24
+#define ISP4FW_CMD_GROUP_SHIFT 16
+#define ISP4FW_CMD_TYPE_STREAM_CTRL (0x2U << ISP4FW_CMD_TYPE_SHIFT)
+
+#define ISP4FW_CMD_GROUP_STREAM_CTRL (0x1U << ISP4FW_CMD_GROUP_SHIFT)
+#define ISP4FW_CMD_GROUP_STREAM_BUFFER (0x4U << ISP4FW_CMD_GROUP_SHIFT)
+
+/* Stream Command */
+#define ISP4FW_CMD_ID_SET_STREAM_CONFIG (ISP4FW_CMD_TYPE_STREAM_CTRL\
+ | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x1)
+#define ISP4FW_CMD_ID_SET_OUT_CHAN_PROP (ISP4FW_CMD_TYPE_STREAM_CTRL\
+ | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x3)
+#define ISP4FW_CMD_ID_ENABLE_OUT_CHAN (ISP4FW_CMD_TYPE_STREAM_CTRL\
+ | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x5)
+#define ISP4FW_CMD_ID_START_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\
+ | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x7)
+#define ISP4FW_CMD_ID_STOP_STREAM (ISP4FW_CMD_TYPE_STREAM_CTRL\
+ | ISP4FW_CMD_GROUP_STREAM_CTRL | 0x8)
+
+/* Stream Buffer Command */
+#define ISP4FW_CMD_ID_SEND_BUFFER (ISP4FW_CMD_TYPE_STREAM_CTRL\
+ | ISP4FW_CMD_GROUP_STREAM_BUFFER | 0x1)
+
+/*
+ * resp_id is in the format of following type:
+ * type: indicate command type, global/stream commands.
+ * group: indicate the command group.
+ * id: A unique command identification in one type and group.
+ * |<-Bit31 ~ Bit24->|<-Bit23 ~ Bit16->|<-Bit15 ~ Bit0->|
+ * | type | group | id |
+ */
+
+#define ISP4FW_RESP_GROUP_SHIFT 16
+
+#define ISP4FW_RESP_GROUP_GENERAL (0x1 << ISP4FW_RESP_GROUP_SHIFT)
+#define ISP4FW_RESP_GROUP_NOTIFICATION (0x3 << ISP4FW_RESP_GROUP_SHIFT)
+
+/* General Response */
+#define ISP4FW_RESP_ID_CMD_DONE (ISP4FW_RESP_GROUP_GENERAL | 0x1)
+
+/* Notification */
+#define ISP4FW_RESP_ID_NOTI_FRAME_DONE (ISP4FW_RESP_GROUP_NOTIFICATION | 0x1)
+
+#define ISP4FW_CMD_STATUS_SUCCESS 0
+#define ISP4FW_CMD_STATUS_FAIL 1
+#define ISP4FW_CMD_STATUS_SKIPPED 2
+
+#define ISP4FW_ADDR_SPACE_TYPE_GPU_VA 4
+
+#define ISP4FW_MEMORY_POOL_SIZE (100 * 1024 * 1024)
+
+/*
+ * standard ISP pipeline: mipicsi=>isp
+ */
+#define ISP4FW_MIPI0_ISP_PIPELINE_ID 0x5f91
+
+enum isp4fw_sensor_id {
+ /* Sensor id for ISP input from MIPI port 0 */
+ ISP4FW_SENSOR_ID_ON_MIPI0 = 0,
+};
+
+enum isp4fw_stream_id {
+ ISP4FW_STREAM_ID_INVALID = -1,
+ ISP4FW_STREAM_ID_1 = 0,
+ ISP4FW_STREAM_ID_2 = 1,
+ ISP4FW_STREAM_ID_3 = 2,
+ ISP4FW_STREAM_ID_MAXIMUM
+};
+
+enum isp4fw_image_format {
+ /* 4:2:0,semi-planar, 8-bit */
+ ISP4FW_IMAGE_FORMAT_NV12 = 1,
+ /* interleave, 4:2:2, 8-bit */
+ ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED = 7,
+};
+
+enum isp4fw_pipe_out_ch {
+ ISP4FW_ISP_PIPE_OUT_CH_PREVIEW = 0,
+};
+
+enum isp4fw_yuv_range {
+ ISP4FW_ISP_YUV_RANGE_FULL = 0, /* YUV value range in 0~255 */
+ ISP4FW_ISP_YUV_RANGE_NARROW = 1, /* YUV value range in 16~235 */
+ ISP4FW_ISP_YUV_RANGE_MAX
+};
+
+enum isp4fw_buffer_type {
+ ISP4FW_BUFFER_TYPE_PREVIEW = 8,
+ ISP4FW_BUFFER_TYPE_META_INFO = 10,
+ ISP4FW_BUFFER_TYPE_MEM_POOL = 15,
+};
+
+enum isp4fw_buffer_status {
+ /* The buffer is INVALID */
+ ISP4FW_BUFFER_STATUS_INVALID,
+ /* The buffer is not filled with image data */
+ ISP4FW_BUFFER_STATUS_SKIPPED,
+ /* The buffer is available and awaiting to be filled */
+ ISP4FW_BUFFER_STATUS_EXIST,
+ /* The buffer is filled with image data */
+ ISP4FW_BUFFER_STATUS_DONE,
+ /* The buffer is unavailable */
+ ISP4FW_BUFFER_STATUS_LACK,
+ /* The buffer is dirty, probably caused by LMI leakage */
+ ISP4FW_BUFFER_STATUS_DIRTY,
+ ISP4FW_BUFFER_STATUS_MAX
+};
+
+enum isp4fw_buffer_source {
+ /* The buffer is from the stream buffer queue */
+ ISP4FW_BUFFER_SOURCE_STREAM,
+};
+
+struct isp4fw_error_code {
+ u32 code1;
+ u32 code2;
+ u32 code3;
+ u32 code4;
+ u32 code5;
+};
+
+/* Command Structure for FW */
+
+struct isp4fw_cmd {
+ u32 cmd_seq_num;
+ u32 cmd_id;
+ u32 cmd_param[12];
+ u16 cmd_stream_id;
+ u8 cmd_silent_resp;
+ u8 reserved;
+ u32 cmd_check_sum;
+};
+
+struct isp4fw_resp_cmd_done {
+ /*
+ * The host2fw command seqNum.
+ * To indicate which command this response refers to.
+ */
+ u32 cmd_seq_num;
+ /* The host2fw command id for host double check. */
+ u32 cmd_id;
+ /*
+ * Indicate the command process status.
+ * 0 means success. 1 means fail. 2 means skipped
+ */
+ u16 cmd_status;
+ /*
+ * If cmd_status is 1, the command failed. The host can check
+ * isp4fw_error_code for details.
+ */
+ u16 isp4fw_error_code;
+ /* The response payload type varies by cmd. */
+ u8 payload[36];
+};
+
+struct isp4fw_resp_param_package {
+ u32 package_addr_lo; /* The low 32 bit of the pkg address. */
+ u32 package_addr_hi; /* The high 32 bit of the pkg address. */
+ u32 package_size; /* The total pkg size in bytes. */
+ u32 package_check_sum; /* The byte sum of the pkg. */
+};
+
+struct isp4fw_resp {
+ u32 resp_seq_num;
+ u32 resp_id;
+ union {
+ struct isp4fw_resp_cmd_done cmd_done;
+ struct isp4fw_resp_param_package frame_done;
+ u32 resp_param[12];
+ } param;
+ u8 reserved[4];
+ u32 resp_check_sum;
+};
+
+struct isp4fw_mipi_pipe_path_cfg {
+ u32 b_enable;
+ enum isp4fw_sensor_id isp4fw_sensor_id;
+};
+
+struct isp4fw_isp_pipe_path_cfg {
+ u32 isp_pipe_id; /* pipe ids for pipeline construction */
+};
+
+struct isp4fw_isp_stream_cfg {
+ /* Isp mipi path */
+ struct isp4fw_mipi_pipe_path_cfg mipi_pipe_path_cfg;
+ /* Isp pipe path */
+ struct isp4fw_isp_pipe_path_cfg isp_pipe_path_cfg;
+ /* enable TNR */
+ u32 b_enable_tnr;
+ /*
+ * Number of frames for RTA processing.
+ * Set to 0 to use the firmware's default value.
+ */
+ u32 rta_frames_per_proc;
+};
+
+struct isp4fw_image_prop {
+ enum isp4fw_image_format image_format;
+ u32 width;
+ u32 height;
+ u32 luma_pitch;
+ u32 chroma_pitch;
+ enum isp4fw_yuv_range yuv_range;
+};
+
+struct isp4fw_buffer {
+ /*
+ * A check num for debug usage, host can set the buf_tags
+ * to different numbers
+ */
+ u32 buf_tags;
+ union {
+ u32 value;
+ struct {
+ u32 space : 16;
+ u32 vmid : 16;
+ } bit;
+ } vmid_space;
+ u32 buf_base_a_lo; /* Low address of buffer A */
+ u32 buf_base_a_hi; /* High address of buffer A */
+ u32 buf_size_a; /* Buffer size of buffer A */
+
+ u32 buf_base_b_lo; /* Low address of buffer B */
+ u32 buf_base_b_hi; /* High address of buffer B */
+ u32 buf_size_b; /* Buffer size of buffer B */
+
+ u32 buf_base_c_lo; /* Low address of buffer C */
+ u32 buf_base_c_hi; /* High address of buffer C */
+ u32 buf_size_c; /* Buffer size of buffer C */
+};
+
+struct isp4fw_buffer_meta_info {
+ u32 enabled; /* enabled flag */
+ enum isp4fw_buffer_status status; /* BufferStatus */
+ struct isp4fw_error_code err; /* err code */
+ enum isp4fw_buffer_source source; /* BufferSource */
+ struct isp4fw_image_prop image_prop; /* image_prop */
+ struct isp4fw_buffer buffer; /* buffer info */
+};
+
+struct isp4fw_meta_info {
+ u32 poc; /* frame id */
+ u32 fc_id; /* frame ctl id */
+ u32 time_stamp_lo; /* timestamp low 32 bits */
+ u32 time_stamp_hi; /* timestamp_high 32 bits */
+ struct isp4fw_buffer_meta_info preview; /* preview BufferMetaInfo */
+};
+
+struct isp4fw_cmd_send_buffer {
+ enum isp4fw_buffer_type buffer_type;
+ struct isp4fw_buffer buffer; /* buffer info */
+};
+
+struct isp4fw_cmd_set_out_ch_prop {
+ enum isp4fw_pipe_out_ch ch; /* ISP output channel */
+ struct isp4fw_image_prop image_prop; /* image property */
+};
+
+struct isp4fw_cmd_enable_out_ch {
+ enum isp4fw_pipe_out_ch ch; /* ISP output channel */
+ u32 is_enable; /* If channel is enabled or not */
+};
+
+struct isp4fw_cmd_set_stream_cfg {
+ struct isp4fw_isp_stream_cfg stream_cfg; /* stream path config */
+};
+
+#endif /* _ISP4_FW_CMD_RESP_H_ */
diff --git a/drivers/media/platform/amd/isp4/isp4_hw_reg.h b/drivers/media/platform/amd/isp4/isp4_hw_reg.h
new file mode 100644
index 000000000000..09c76f75c5ee
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_hw_reg.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_HW_REG_H_
+#define _ISP4_HW_REG_H_
+
+#include <linux/io.h>
+
+#define ISP_SOFT_RESET 0x62000
+#define ISP_SYS_INT0_EN 0x62010
+#define ISP_SYS_INT0_STATUS 0x62014
+#define ISP_SYS_INT0_ACK 0x62018
+#define ISP_CCPU_CNTL 0x62054
+#define ISP_STATUS 0x62058
+#define ISP_LOG_RB_BASE_LO0 0x62148
+#define ISP_LOG_RB_BASE_HI0 0x6214c
+#define ISP_LOG_RB_SIZE0 0x62150
+#define ISP_LOG_RB_RPTR0 0x62154
+#define ISP_LOG_RB_WPTR0 0x62158
+#define ISP_RB_BASE_LO1 0x62170
+#define ISP_RB_BASE_HI1 0x62174
+#define ISP_RB_SIZE1 0x62178
+#define ISP_RB_RPTR1 0x6217c
+#define ISP_RB_WPTR1 0x62180
+#define ISP_RB_BASE_LO2 0x62184
+#define ISP_RB_BASE_HI2 0x62188
+#define ISP_RB_SIZE2 0x6218c
+#define ISP_RB_RPTR2 0x62190
+#define ISP_RB_WPTR2 0x62194
+#define ISP_RB_BASE_LO3 0x62198
+#define ISP_RB_BASE_HI3 0x6219c
+#define ISP_RB_SIZE3 0x621a0
+#define ISP_RB_RPTR3 0x621a4
+#define ISP_RB_WPTR3 0x621a8
+#define ISP_RB_BASE_LO4 0x621ac
+#define ISP_RB_BASE_HI4 0x621b0
+#define ISP_RB_SIZE4 0x621b4
+#define ISP_RB_RPTR4 0x621b8
+#define ISP_RB_WPTR4 0x621bc
+#define ISP_RB_BASE_LO5 0x621c0
+#define ISP_RB_BASE_HI5 0x621c4
+#define ISP_RB_SIZE5 0x621c8
+#define ISP_RB_RPTR5 0x621cc
+#define ISP_RB_WPTR5 0x621d0
+#define ISP_RB_BASE_LO6 0x621d4
+#define ISP_RB_BASE_HI6 0x621d8
+#define ISP_RB_SIZE6 0x621dc
+#define ISP_RB_RPTR6 0x621e0
+#define ISP_RB_WPTR6 0x621e4
+#define ISP_RB_BASE_LO7 0x621e8
+#define ISP_RB_BASE_HI7 0x621ec
+#define ISP_RB_SIZE7 0x621f0
+#define ISP_RB_RPTR7 0x621f4
+#define ISP_RB_WPTR7 0x621f8
+#define ISP_RB_BASE_LO8 0x621fc
+#define ISP_RB_BASE_HI8 0x62200
+#define ISP_RB_SIZE8 0x62204
+#define ISP_RB_RPTR8 0x62208
+#define ISP_RB_WPTR8 0x6220c
+#define ISP_RB_BASE_LO9 0x62210
+#define ISP_RB_BASE_HI9 0x62214
+#define ISP_RB_SIZE9 0x62218
+#define ISP_RB_RPTR9 0x6221c
+#define ISP_RB_WPTR9 0x62220
+#define ISP_RB_BASE_LO10 0x62224
+#define ISP_RB_BASE_HI10 0x62228
+#define ISP_RB_SIZE10 0x6222c
+#define ISP_RB_RPTR10 0x62230
+#define ISP_RB_WPTR10 0x62234
+#define ISP_RB_BASE_LO11 0x62238
+#define ISP_RB_BASE_HI11 0x6223c
+#define ISP_RB_SIZE11 0x62240
+#define ISP_RB_RPTR11 0x62244
+#define ISP_RB_WPTR11 0x62248
+#define ISP_RB_BASE_LO12 0x6224c
+#define ISP_RB_BASE_HI12 0x62250
+#define ISP_RB_SIZE12 0x62254
+#define ISP_RB_RPTR12 0x62258
+#define ISP_RB_WPTR12 0x6225c
+
+#define ISP_POWER_STATUS 0x60000
+
+/* ISP_SOFT_RESET */
+#define ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK 0x00000001UL
+
+/* ISP_CCPU_CNTL */
+#define ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK 0x00040000UL
+
+/* ISP_STATUS */
+#define ISP_STATUS__CCPU_REPORT_MASK 0x000000feUL
+
+/* ISP_SYS_INT0_STATUS */
+#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT9_INT_MASK 0x00010000UL
+#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT10_INT_MASK 0x00040000UL
+#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT11_INT_MASK 0x00100000UL
+#define ISP_SYS_INT0_STATUS__SYS_INT_RINGBUFFER_WPT12_INT_MASK 0x00400000UL
+
+/* ISP_SYS_INT0_EN */
+#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK 0x00010000UL
+#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT10_EN_MASK 0x00040000UL
+#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT11_EN_MASK 0x00100000UL
+#define ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK 0x00400000UL
+
+/* ISP_SYS_INT0_ACK */
+#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT9_ACK_MASK 0x00010000UL
+#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT10_ACK_MASK 0x00040000UL
+#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT11_ACK_MASK 0x00100000UL
+#define ISP_SYS_INT0_ACK__SYS_INT_RINGBUFFER_WPT12_ACK_MASK 0x00400000UL
+
+/* Helper functions for reading isp registers */
+static inline u32 isp4hw_rreg(void __iomem *base, u32 reg)
+{
+ return readl(base + reg);
+}
+
+/* Helper functions for writing isp registers */
+static inline void isp4hw_wreg(void __iomem *base, u32 reg, u32 val)
+{
+ return writel(val, base + reg);
+}
+
+#endif /* _ISP4_HW_REG_H_ */
diff --git a/drivers/media/platform/amd/isp4/isp4_interface.c b/drivers/media/platform/amd/isp4/isp4_interface.c
new file mode 100644
index 000000000000..8d73f66bb42c
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_interface.c
@@ -0,0 +1,832 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/iopoll.h>
+
+#include "isp4_debug.h"
+#include "isp4_fw_cmd_resp.h"
+#include "isp4_hw_reg.h"
+#include "isp4_interface.h"
+
+#define ISP4IF_FW_RESP_RB_IRQ_EN_MASK \
+ (ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT9_EN_MASK\
+ | ISP_SYS_INT0_EN__SYS_INT_RINGBUFFER_WPT12_EN_MASK)
+
+#define ISP4IF_FW_CMD_TIMEOUT (HZ / 2)
+
+struct isp4if_rb_config {
+ const char *name;
+ u32 index;
+ u32 reg_rptr;
+ u32 reg_wptr;
+ u32 reg_base_lo;
+ u32 reg_base_hi;
+ u32 reg_size;
+ u32 val_size;
+ u64 base_mc_addr;
+ void *base_sys_addr;
+};
+
+/* FW cmd ring buffer configuration */
+static struct isp4if_rb_config isp4if_cmd_rb_config[ISP4IF_STREAM_ID_MAX] = {
+ {
+ .name = "CMD_RB_GBL0",
+ .index = 3,
+ .reg_rptr = ISP_RB_RPTR4,
+ .reg_wptr = ISP_RB_WPTR4,
+ .reg_base_lo = ISP_RB_BASE_LO4,
+ .reg_base_hi = ISP_RB_BASE_HI4,
+ .reg_size = ISP_RB_SIZE4,
+ },
+ {
+ .name = "CMD_RB_STR1",
+ .index = 0,
+ .reg_rptr = ISP_RB_RPTR1,
+ .reg_wptr = ISP_RB_WPTR1,
+ .reg_base_lo = ISP_RB_BASE_LO1,
+ .reg_base_hi = ISP_RB_BASE_HI1,
+ .reg_size = ISP_RB_SIZE1,
+ },
+ {
+ .name = "CMD_RB_STR2",
+ .index = 1,
+ .reg_rptr = ISP_RB_RPTR2,
+ .reg_wptr = ISP_RB_WPTR2,
+ .reg_base_lo = ISP_RB_BASE_LO2,
+ .reg_base_hi = ISP_RB_BASE_HI2,
+ .reg_size = ISP_RB_SIZE2,
+ },
+ {
+ .name = "CMD_RB_STR3",
+ .index = 2,
+ .reg_rptr = ISP_RB_RPTR3,
+ .reg_wptr = ISP_RB_WPTR3,
+ .reg_base_lo = ISP_RB_BASE_LO3,
+ .reg_base_hi = ISP_RB_BASE_HI3,
+ .reg_size = ISP_RB_SIZE3,
+ },
+};
+
+/* FW resp ring buffer configuration */
+static struct isp4if_rb_config isp4if_resp_rb_config[ISP4IF_STREAM_ID_MAX] = {
+ {
+ .name = "RES_RB_GBL0",
+ .index = 3,
+ .reg_rptr = ISP_RB_RPTR12,
+ .reg_wptr = ISP_RB_WPTR12,
+ .reg_base_lo = ISP_RB_BASE_LO12,
+ .reg_base_hi = ISP_RB_BASE_HI12,
+ .reg_size = ISP_RB_SIZE12,
+ },
+ {
+ .name = "RES_RB_STR1",
+ .index = 0,
+ .reg_rptr = ISP_RB_RPTR9,
+ .reg_wptr = ISP_RB_WPTR9,
+ .reg_base_lo = ISP_RB_BASE_LO9,
+ .reg_base_hi = ISP_RB_BASE_HI9,
+ .reg_size = ISP_RB_SIZE9,
+ },
+ {
+ .name = "RES_RB_STR2",
+ .index = 1,
+ .reg_rptr = ISP_RB_RPTR10,
+ .reg_wptr = ISP_RB_WPTR10,
+ .reg_base_lo = ISP_RB_BASE_LO10,
+ .reg_base_hi = ISP_RB_BASE_HI10,
+ .reg_size = ISP_RB_SIZE10,
+ },
+ {
+ .name = "RES_RB_STR3",
+ .index = 2,
+ .reg_rptr = ISP_RB_RPTR11,
+ .reg_wptr = ISP_RB_WPTR11,
+ .reg_base_lo = ISP_RB_BASE_LO11,
+ .reg_base_hi = ISP_RB_BASE_HI11,
+ .reg_size = ISP_RB_SIZE11,
+ },
+};
+
+/* FW log ring buffer configuration */
+static struct isp4if_rb_config isp4if_log_rb_config = {
+ .name = "LOG_RB",
+ .index = 0,
+ .reg_rptr = ISP_LOG_RB_RPTR0,
+ .reg_wptr = ISP_LOG_RB_WPTR0,
+ .reg_base_lo = ISP_LOG_RB_BASE_LO0,
+ .reg_base_hi = ISP_LOG_RB_BASE_HI0,
+ .reg_size = ISP_LOG_RB_SIZE0,
+};
+
+static struct isp4if_gpu_mem_info *
+isp4if_gpu_mem_alloc(struct isp4_interface *ispif, u32 mem_size)
+{
+ struct isp4if_gpu_mem_info *mem_info;
+ struct device *dev = ispif->dev;
+ int ret;
+
+ mem_info = kmalloc_obj(*mem_info, GFP_KERNEL);
+ if (!mem_info)
+ return NULL;
+
+ mem_info->mem_size = mem_size;
+ ret = isp_kernel_buffer_alloc(dev, mem_info->mem_size,
+ &mem_info->mem_handle,
+ &mem_info->gpu_mc_addr,
+ &mem_info->sys_addr);
+ if (ret) {
+ kfree(mem_info);
+ return NULL;
+ }
+
+ return mem_info;
+}
+
+static void isp4if_gpu_mem_free(struct isp4_interface *ispif,
+ struct isp4if_gpu_mem_info **mem_info_ptr)
+{
+ struct isp4if_gpu_mem_info *mem_info = *mem_info_ptr;
+ struct device *dev = ispif->dev;
+
+ if (!mem_info) {
+ dev_err(dev, "invalid mem_info\n");
+ return;
+ }
+
+ *mem_info_ptr = NULL;
+ isp_kernel_buffer_free(&mem_info->mem_handle, &mem_info->gpu_mc_addr,
+ &mem_info->sys_addr);
+ kfree(mem_info);
+}
+
+static void isp4if_dealloc_fw_gpumem(struct isp4_interface *ispif)
+{
+ isp4if_gpu_mem_free(ispif, &ispif->fw_mem_pool);
+ isp4if_gpu_mem_free(ispif, &ispif->fw_cmd_resp_buf);
+ isp4if_gpu_mem_free(ispif, &ispif->fw_log_buf);
+
+ for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++)
+ isp4if_gpu_mem_free(ispif, &ispif->meta_info_buf[i]);
+}
+
+static int isp4if_alloc_fw_gpumem(struct isp4_interface *ispif)
+{
+ struct device *dev = ispif->dev;
+
+ ispif->fw_mem_pool = isp4if_gpu_mem_alloc(ispif,
+ ISP4FW_MEMORY_POOL_SIZE);
+ if (!ispif->fw_mem_pool)
+ goto error_no_memory;
+
+ ispif->fw_cmd_resp_buf =
+ isp4if_gpu_mem_alloc(ispif, ISP4IF_RB_PMBMAP_MEM_SIZE);
+ if (!ispif->fw_cmd_resp_buf)
+ goto error_no_memory;
+
+ ispif->fw_log_buf =
+ isp4if_gpu_mem_alloc(ispif, ISP4IF_FW_LOG_RINGBUF_SIZE);
+ if (!ispif->fw_log_buf)
+ goto error_no_memory;
+
+ for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
+ ispif->meta_info_buf[i] =
+ isp4if_gpu_mem_alloc(ispif, ISP4IF_META_INFO_BUF_SIZE);
+ if (!ispif->meta_info_buf[i])
+ goto error_no_memory;
+ }
+
+ return 0;
+
+error_no_memory:
+ dev_err(dev, "failed to allocate gpu memory\n");
+ return -ENOMEM;
+}
+
+static u32 isp4if_compute_check_sum(const void *buf, size_t buf_size)
+{
+ const u8 *surplus_ptr;
+ const u32 *buffer;
+ u32 checksum = 0;
+ size_t i;
+
+ buffer = (const u32 *)buf;
+ for (i = 0; i < buf_size / sizeof(u32); i++)
+ checksum += buffer[i];
+
+ surplus_ptr = (const u8 *)&buffer[i];
+ /* add surplus data crc checksum */
+ for (i = 0; i < buf_size % sizeof(u32); i++)
+ checksum += surplus_ptr[i];
+
+ return checksum;
+}
+
+void isp4if_clear_cmdq(struct isp4_interface *ispif)
+{
+ struct isp4if_cmd_element *buf_node, *tmp_node;
+ LIST_HEAD(free_list);
+
+ scoped_guard(spinlock, &ispif->cmdq_lock)
+ list_splice_init(&ispif->cmdq, &free_list);
+
+ list_for_each_entry_safe(buf_node, tmp_node, &free_list, list)
+ kfree(buf_node);
+}
+
+static bool isp4if_is_cmdq_rb_full(struct isp4_interface *ispif,
+ enum isp4if_stream_id stream)
+{
+ struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream];
+ u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
+ u32 len = rb_config->val_size;
+ u32 rd_ptr, wr_ptr;
+ u32 bytes_free;
+
+ rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
+ wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
+
+ /*
+ * Read and write pointers are equal, indicating the ring buffer
+ * is empty
+ */
+ if (wr_ptr == rd_ptr)
+ return false;
+
+ if (wr_ptr > rd_ptr)
+ bytes_free = len - (wr_ptr - rd_ptr);
+ else
+ bytes_free = rd_ptr - wr_ptr;
+
+ /*
+ * Ignore one byte from the bytes free to prevent rd_ptr from equaling
+ * wr_ptr when the ring buffer is full, because rd_ptr == wr_ptr is
+ * supposed to indicate that the ring buffer is empty.
+ */
+ return bytes_free <= sizeof(struct isp4fw_cmd);
+}
+
+struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif,
+ u32 seq_num, u32 cmd_id)
+{
+ struct isp4if_cmd_element *ele;
+
+ guard(spinlock)(&ispif->cmdq_lock);
+
+ list_for_each_entry(ele, &ispif->cmdq, list) {
+ if (ele->seq_num == seq_num && ele->cmd_id == cmd_id) {
+ list_del(&ele->list);
+ return ele;
+ }
+ }
+
+ return NULL;
+}
+
+/* Must check that isp4if_is_cmdq_rb_full() == false before calling */
+static int isp4if_insert_isp_fw_cmd(struct isp4_interface *ispif,
+ enum isp4if_stream_id stream,
+ const struct isp4fw_cmd *cmd)
+{
+ struct isp4if_rb_config *rb_config = &isp4if_cmd_rb_config[stream];
+ u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
+ void *mem_sys = rb_config->base_sys_addr;
+ const u32 cmd_sz = sizeof(*cmd);
+ struct device *dev = ispif->dev;
+ u32 len = rb_config->val_size;
+ const void *src = cmd;
+ u32 rd_ptr, wr_ptr;
+ u32 bytes_to_end;
+
+ rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
+ wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
+ if (rd_ptr >= len || wr_ptr >= len) {
+ dev_err(dev,
+ "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, cmd_sz=%u\n",
+ stream, isp4dbg_get_if_stream_str(stream), rd_ptr,
+ wr_ptr, len, cmd_sz);
+ return -EINVAL;
+ }
+
+ bytes_to_end = len - wr_ptr;
+ if (bytes_to_end >= cmd_sz) {
+ /* FW cmd is just a straight copy to the write pointer */
+ memcpy(mem_sys + wr_ptr, src, cmd_sz);
+ isp4hw_wreg(ispif->mmio, wreg, (wr_ptr + cmd_sz) % len);
+ } else {
+ /*
+ * FW cmd is split because the ring buffer needs to wrap
+ * around
+ */
+ memcpy(mem_sys + wr_ptr, src, bytes_to_end);
+ memcpy(mem_sys, src + bytes_to_end, cmd_sz - bytes_to_end);
+ isp4hw_wreg(ispif->mmio, wreg, cmd_sz - bytes_to_end);
+ }
+
+ return 0;
+}
+
+static inline enum isp4if_stream_id isp4if_get_fw_stream(u32 cmd_id)
+{
+ return ISP4IF_STREAM_ID_1;
+}
+
+static int isp4if_send_fw_cmd(struct isp4_interface *ispif, u32 cmd_id,
+ const void *package,
+ u32 package_size, bool sync)
+{
+ enum isp4if_stream_id stream = isp4if_get_fw_stream(cmd_id);
+ struct isp4if_cmd_element *ele = NULL;
+ struct device *dev = ispif->dev;
+ struct isp4fw_cmd cmd;
+ u32 seq_num;
+ int ret;
+
+ if (package_size > sizeof(cmd.cmd_param)) {
+ dev_err(dev, "fail pkgsize(%u) > %zu cmd:0x%x, stream %d\n",
+ package_size, sizeof(cmd.cmd_param), cmd_id, stream);
+ return -EINVAL;
+ }
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.cmd_id = cmd_id;
+ switch (stream) {
+ case ISP4IF_STREAM_ID_GLOBAL:
+ cmd.cmd_stream_id = ISP4FW_STREAM_ID_INVALID;
+ break;
+ case ISP4IF_STREAM_ID_1:
+ cmd.cmd_stream_id = ISP4FW_STREAM_ID_1;
+ break;
+ default:
+ dev_err(dev, "fail bad stream id %d\n", stream);
+ return -EINVAL;
+ }
+
+ /* Allocate the sync command object early and outside of the lock */
+ if (sync) {
+ ele = kmalloc_obj(*ele, GFP_KERNEL);
+ if (!ele)
+ return -ENOMEM;
+
+ /* Get two references: one for the resp thread, one for us */
+ atomic_set(&ele->refcnt, 2);
+ init_completion(&ele->cmd_done);
+ }
+
+ if (package && package_size)
+ memcpy(cmd.cmd_param, package, package_size);
+
+ scoped_guard(mutex, &ispif->isp4if_mutex) {
+ ret = read_poll_timeout(isp4if_is_cmdq_rb_full, ret, !ret,
+ ISP4IF_RB_FULL_SLEEP_US,
+ ISP4IF_RB_FULL_TIMEOUT_US, false, ispif,
+ stream);
+ if (ret) {
+ struct isp4if_rb_config *rb_config =
+ &isp4if_resp_rb_config[stream];
+ u32 rd_ptr = isp4hw_rreg(ispif->mmio,
+ rb_config->reg_rptr);
+ u32 wr_ptr = isp4hw_rreg(ispif->mmio,
+ rb_config->reg_wptr);
+
+ dev_err(dev,
+ "failed to get free cmdq slot, stream %s(%d),rd %u, wr %u\n",
+ isp4dbg_get_if_stream_str(stream), stream,
+ rd_ptr, wr_ptr);
+ ret = -ETIMEDOUT;
+ goto free_ele;
+ }
+
+ seq_num = ispif->host2fw_seq_num++;
+ cmd.cmd_seq_num = seq_num;
+ cmd.cmd_check_sum = isp4if_compute_check_sum(&cmd, sizeof(cmd)
+ - sizeof(u32));
+
+ /*
+ * only append the fw cmd to queue when its response needs to
+ * be waited for, currently there are only two such commands,
+ * disable channel and stop stream which are only sent after
+ * close camera
+ */
+ if (ele) {
+ ele->seq_num = seq_num;
+ ele->cmd_id = cmd_id;
+ scoped_guard(spinlock, &ispif->cmdq_lock)
+ list_add_tail(&ele->list, &ispif->cmdq);
+ }
+
+ ret = isp4if_insert_isp_fw_cmd(ispif, stream, &cmd);
+ if (ret) {
+ dev_err(dev,
+ "fail for insert_isp_fw_cmd cmd_id %s(0x%08x)\n",
+ isp4dbg_get_cmd_str(cmd_id), cmd_id);
+ goto err_dequeue_ele;
+ }
+ }
+
+ if (ele) {
+ ret = wait_for_completion_timeout(&ele->cmd_done,
+ ISP4IF_FW_CMD_TIMEOUT);
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto err_dequeue_ele;
+ }
+
+ ret = 0;
+ goto put_ele_ref;
+ }
+
+ return 0;
+
+err_dequeue_ele:
+ /*
+ * Try to remove the command from the queue. If that fails, then it
+ * means the response thread is currently using the object, and we need
+ * to use the refcount to avoid a use-after-free by either side.
+ */
+ if (ele && isp4if_rm_cmd_from_cmdq(ispif, seq_num, cmd_id))
+ goto free_ele;
+
+put_ele_ref:
+ /* Don't free the command if we didn't put the last reference */
+ if (ele && atomic_dec_return(&ele->refcnt))
+ ele = NULL;
+
+free_ele:
+ kfree(ele);
+ return ret;
+}
+
+static int isp4if_send_buffer(struct isp4_interface *ispif,
+ struct isp4if_img_buf_info *buf_info)
+{
+ struct isp4fw_cmd_send_buffer cmd;
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.buffer_type = ISP4FW_BUFFER_TYPE_PREVIEW;
+ cmd.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
+ isp4if_split_addr64(buf_info->planes[0].mc_addr,
+ &cmd.buffer.buf_base_a_lo,
+ &cmd.buffer.buf_base_a_hi);
+ cmd.buffer.buf_size_a = buf_info->planes[0].len;
+
+ isp4if_split_addr64(buf_info->planes[1].mc_addr,
+ &cmd.buffer.buf_base_b_lo,
+ &cmd.buffer.buf_base_b_hi);
+ cmd.buffer.buf_size_b = buf_info->planes[1].len;
+
+ isp4if_split_addr64(buf_info->planes[2].mc_addr,
+ &cmd.buffer.buf_base_c_lo,
+ &cmd.buffer.buf_base_c_hi);
+ cmd.buffer.buf_size_c = buf_info->planes[2].len;
+
+ return isp4if_send_fw_cmd(ispif, ISP4FW_CMD_ID_SEND_BUFFER, &cmd,
+ sizeof(cmd), false);
+}
+
+static void isp4if_init_rb_config(struct isp4_interface *ispif,
+ struct isp4if_rb_config *rb_config)
+{
+ isp4hw_wreg(ispif->mmio, rb_config->reg_rptr, 0x0);
+ isp4hw_wreg(ispif->mmio, rb_config->reg_wptr, 0x0);
+ isp4hw_wreg(ispif->mmio, rb_config->reg_base_lo,
+ rb_config->base_mc_addr);
+ isp4hw_wreg(ispif->mmio, rb_config->reg_base_hi,
+ rb_config->base_mc_addr >> 32);
+ isp4hw_wreg(ispif->mmio, rb_config->reg_size, rb_config->val_size);
+}
+
+static int isp4if_fw_init(struct isp4_interface *ispif)
+{
+ u32 aligned_rb_chunk_size = ISP4IF_RB_PMBMAP_MEM_CHUNK & 0xffffffc0;
+ struct isp4if_rb_config *rb_config;
+ u32 offset;
+ unsigned int i;
+
+ /* initialize CMD_RB streams */
+ for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) {
+ rb_config = (isp4if_cmd_rb_config + i);
+ offset = aligned_rb_chunk_size * rb_config->index;
+
+ rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE;
+ rb_config->base_sys_addr =
+ ispif->fw_cmd_resp_buf->sys_addr + offset;
+ rb_config->base_mc_addr =
+ ispif->fw_cmd_resp_buf->gpu_mc_addr + offset;
+
+ isp4if_init_rb_config(ispif, rb_config);
+ }
+
+ /* initialize RESP_RB streams */
+ for (i = 0; i < ISP4IF_STREAM_ID_MAX; i++) {
+ rb_config = (isp4if_resp_rb_config + i);
+ offset = aligned_rb_chunk_size *
+ (rb_config->index + ISP4IF_RESP_CHAN_TO_RB_OFFSET - 1);
+
+ rb_config->val_size = ISP4IF_FW_CMD_BUF_SIZE;
+ rb_config->base_sys_addr =
+ ispif->fw_cmd_resp_buf->sys_addr + offset;
+ rb_config->base_mc_addr =
+ ispif->fw_cmd_resp_buf->gpu_mc_addr + offset;
+
+ isp4if_init_rb_config(ispif, rb_config);
+ }
+
+ /* initialize LOG_RB stream */
+ rb_config = &isp4if_log_rb_config;
+ rb_config->val_size = ISP4IF_FW_LOG_RINGBUF_SIZE;
+ rb_config->base_mc_addr = ispif->fw_log_buf->gpu_mc_addr;
+ rb_config->base_sys_addr = ispif->fw_log_buf->sys_addr;
+
+ isp4if_init_rb_config(ispif, rb_config);
+
+ return 0;
+}
+
+static int isp4if_wait_fw_ready(struct isp4_interface *ispif,
+ u32 isp_status_addr)
+{
+ struct device *dev = ispif->dev;
+ u32 timeout_ms = 100;
+ u32 interval_ms = 1;
+ u32 reg_val;
+
+ /* wait for FW initialize done! */
+ if (!read_poll_timeout(isp4hw_rreg, reg_val, reg_val
+ & ISP_STATUS__CCPU_REPORT_MASK,
+ interval_ms * 1000, timeout_ms * 1000, false,
+ ispif->mmio, isp_status_addr))
+ return 0;
+
+ dev_err(dev, "ISP CCPU FW boot failed\n");
+
+ return -ETIME;
+}
+
+static void isp4if_enable_ccpu(struct isp4_interface *ispif)
+{
+ u32 reg_val;
+
+ reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET);
+ reg_val &= (~ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK);
+ isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val);
+
+ usleep_range(100, 150);
+
+ reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL);
+ reg_val &= (~ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK);
+ isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val);
+}
+
+static void isp4if_disable_ccpu(struct isp4_interface *ispif)
+{
+ u32 reg_val;
+
+ reg_val = isp4hw_rreg(ispif->mmio, ISP_CCPU_CNTL);
+ reg_val |= ISP_CCPU_CNTL__CCPU_HOST_SOFT_RST_MASK;
+ isp4hw_wreg(ispif->mmio, ISP_CCPU_CNTL, reg_val);
+
+ usleep_range(100, 150);
+
+ reg_val = isp4hw_rreg(ispif->mmio, ISP_SOFT_RESET);
+ reg_val |= ISP_SOFT_RESET__CCPU_SOFT_RESET_MASK;
+ isp4hw_wreg(ispif->mmio, ISP_SOFT_RESET, reg_val);
+}
+
+static int isp4if_fw_boot(struct isp4_interface *ispif)
+{
+ struct device *dev = ispif->dev;
+
+ if (ispif->status != ISP4IF_STATUS_PWR_ON) {
+ dev_err(dev, "invalid isp power status %d\n", ispif->status);
+ return -EINVAL;
+ }
+
+ isp4if_disable_ccpu(ispif);
+
+ isp4if_fw_init(ispif);
+
+ /* clear ccpu status */
+ isp4hw_wreg(ispif->mmio, ISP_STATUS, 0x0);
+
+ isp4if_enable_ccpu(ispif);
+
+ if (isp4if_wait_fw_ready(ispif, ISP_STATUS)) {
+ isp4if_disable_ccpu(ispif);
+ return -EINVAL;
+ }
+
+ /* enable interrupts */
+ isp4hw_wreg(ispif->mmio, ISP_SYS_INT0_EN,
+ ISP4IF_FW_RESP_RB_IRQ_EN_MASK);
+
+ ispif->status = ISP4IF_STATUS_FW_RUNNING;
+
+ dev_dbg(dev, "ISP CCPU FW boot success\n");
+
+ return 0;
+}
+
+int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream,
+ struct isp4fw_resp *resp)
+{
+ struct isp4if_rb_config *rb_config = &isp4if_resp_rb_config[stream];
+ u32 rreg = rb_config->reg_rptr, wreg = rb_config->reg_wptr;
+ void *mem_sys = rb_config->base_sys_addr;
+ const u32 resp_sz = sizeof(*resp);
+ struct device *dev = ispif->dev;
+ u32 len = rb_config->val_size;
+ u32 rd_ptr, wr_ptr;
+ u32 bytes_to_end;
+ void *dst = resp;
+ u32 checksum;
+
+ rd_ptr = isp4hw_rreg(ispif->mmio, rreg);
+ wr_ptr = isp4hw_rreg(ispif->mmio, wreg);
+ if (rd_ptr >= len || wr_ptr >= len)
+ goto err_rb_invalid;
+
+ /*
+ * Read and write pointers are equal, indicating the ring buffer is
+ * empty
+ */
+ if (rd_ptr == wr_ptr)
+ return -ENODATA;
+
+ bytes_to_end = len - rd_ptr;
+ if (bytes_to_end >= resp_sz) {
+ /* FW response is just a straight copy from the read pointer */
+ if (wr_ptr > rd_ptr && wr_ptr - rd_ptr < resp_sz)
+ goto err_rb_invalid;
+
+ memcpy(dst, mem_sys + rd_ptr, resp_sz);
+ isp4hw_wreg(ispif->mmio, rreg, (rd_ptr + resp_sz) % len);
+ } else {
+ /*
+ * FW response is split because the ring buffer wrapped
+ * around
+ */
+ if (wr_ptr > rd_ptr || wr_ptr < resp_sz - bytes_to_end)
+ goto err_rb_invalid;
+
+ memcpy(dst, mem_sys + rd_ptr, bytes_to_end);
+ memcpy(dst + bytes_to_end, mem_sys, resp_sz - bytes_to_end);
+ isp4hw_wreg(ispif->mmio, rreg, resp_sz - bytes_to_end);
+ }
+
+ checksum = isp4if_compute_check_sum(resp, resp_sz - sizeof(u32));
+ if (checksum != resp->resp_check_sum) {
+ dev_err(dev, "resp checksum 0x%x,should 0x%x,rptr %u,wptr %u\n",
+ checksum, resp->resp_check_sum, rd_ptr, wr_ptr);
+ dev_err(dev, "%s(%u), seqNo %u, resp_id %s(0x%x)\n",
+ isp4dbg_get_if_stream_str(stream), stream,
+ resp->resp_seq_num, isp4dbg_get_resp_str(resp->resp_id),
+ resp->resp_id);
+ return -EINVAL;
+ }
+
+ return 0;
+
+err_rb_invalid:
+ dev_err(dev,
+ "rb invalid: stream=%u(%s), rd=%u, wr=%u, len=%u, resp_sz=%u\n",
+ stream, isp4dbg_get_if_stream_str(stream), rd_ptr, wr_ptr, len,
+ resp_sz);
+ return -EINVAL;
+}
+
+int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id,
+ const void *package, u32 package_size)
+{
+ return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, false);
+}
+
+int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id,
+ const void *package, u32 package_size)
+{
+ return isp4if_send_fw_cmd(ispif, cmd_id, package, package_size, true);
+}
+
+void isp4if_clear_bufq(struct isp4_interface *ispif)
+{
+ struct isp4if_img_buf_node *buf_node, *tmp_node;
+ LIST_HEAD(free_list);
+
+ scoped_guard(spinlock, &ispif->bufq_lock)
+ list_splice_init(&ispif->bufq, &free_list);
+
+ list_for_each_entry_safe(buf_node, tmp_node, &free_list, node)
+ kfree(buf_node);
+}
+
+void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node)
+{
+ kfree(buf_node);
+}
+
+struct isp4if_img_buf_node *
+isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info)
+{
+ struct isp4if_img_buf_node *node;
+
+ node = kmalloc_obj(*node, GFP_KERNEL);
+ if (node)
+ node->buf_info = *buf_info;
+
+ return node;
+}
+
+struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif)
+{
+ struct isp4if_img_buf_node *buf_node;
+
+ guard(spinlock)(&ispif->bufq_lock);
+
+ buf_node = list_first_entry_or_null(&ispif->bufq, typeof(*buf_node),
+ node);
+ if (buf_node)
+ list_del(&buf_node->node);
+
+ return buf_node;
+}
+
+int isp4if_queue_buffer(struct isp4_interface *ispif,
+ struct isp4if_img_buf_node *buf_node)
+{
+ int ret;
+
+ ret = isp4if_send_buffer(ispif, &buf_node->buf_info);
+ if (ret)
+ return ret;
+
+ scoped_guard(spinlock, &ispif->bufq_lock)
+ list_add_tail(&buf_node->node, &ispif->bufq);
+
+ return 0;
+}
+
+int isp4if_stop(struct isp4_interface *ispif)
+{
+ isp4if_disable_ccpu(ispif);
+
+ isp4if_dealloc_fw_gpumem(ispif);
+
+ return 0;
+}
+
+int isp4if_start(struct isp4_interface *ispif)
+{
+ int ret;
+
+ ret = isp4if_alloc_fw_gpumem(ispif);
+ if (ret)
+ return ret;
+
+ ret = isp4if_fw_boot(ispif);
+ if (ret)
+ goto failed_fw_boot;
+
+ return 0;
+
+failed_fw_boot:
+ isp4if_dealloc_fw_gpumem(ispif);
+ return ret;
+}
+
+int isp4if_deinit(struct isp4_interface *ispif)
+{
+ isp4if_clear_cmdq(ispif);
+
+ isp4if_clear_bufq(ispif);
+
+ mutex_destroy(&ispif->isp4if_mutex);
+
+ return 0;
+}
+
+int isp4if_init(struct isp4_interface *ispif, struct device *dev,
+ void __iomem *isp_mmio)
+{
+ ispif->dev = dev;
+ ispif->mmio = isp_mmio;
+
+ spin_lock_init(&ispif->cmdq_lock); /* used for cmdq access */
+ spin_lock_init(&ispif->bufq_lock); /* used for bufq access */
+ mutex_init(&ispif->isp4if_mutex); /* used for commands sent to ispfw */
+
+ INIT_LIST_HEAD(&ispif->cmdq);
+ INIT_LIST_HEAD(&ispif->bufq);
+
+ return 0;
+}
diff --git a/drivers/media/platform/amd/isp4/isp4_interface.h b/drivers/media/platform/amd/isp4/isp4_interface.h
new file mode 100644
index 000000000000..ce3ac9b9e5cd
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_interface.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_INTERFACE_H_
+#define _ISP4_INTERFACE_H_
+
+#include <drm/amd/isp.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+struct isp4fw_resp;
+
+#define ISP4IF_RB_MAX 25
+#define ISP4IF_RESP_CHAN_TO_RB_OFFSET 9
+#define ISP4IF_RB_PMBMAP_MEM_SIZE (SZ_16M - 1)
+#define ISP4IF_RB_PMBMAP_MEM_CHUNK \
+ (ISP4IF_RB_PMBMAP_MEM_SIZE / (ISP4IF_RB_MAX - 1))
+#define ISP4IF_HOST2FW_COMMAND_SIZE sizeof(struct isp4fw_cmd)
+#define ISP4IF_MAX_NUM_HOST2FW_COMMAND 40
+#define ISP4IF_FW_CMD_BUF_SIZE \
+ (ISP4IF_MAX_NUM_HOST2FW_COMMAND * ISP4IF_HOST2FW_COMMAND_SIZE)
+#define ISP4IF_RB_FULL_SLEEP_US (33 * USEC_PER_MSEC)
+#define ISP4IF_RB_FULL_TIMEOUT_US (10 * ISP4IF_RB_FULL_SLEEP_US)
+
+#define ISP4IF_META_INFO_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000)
+#define ISP4IF_MAX_STREAM_BUF_COUNT 8
+
+#define ISP4IF_FW_LOG_RINGBUF_SIZE SZ_2M
+
+enum isp4if_stream_id {
+ ISP4IF_STREAM_ID_GLOBAL = 0,
+ ISP4IF_STREAM_ID_1 = 1,
+ ISP4IF_STREAM_ID_MAX = 4
+};
+
+enum isp4if_status {
+ ISP4IF_STATUS_PWR_OFF,
+ ISP4IF_STATUS_PWR_ON,
+ ISP4IF_STATUS_FW_RUNNING,
+ ISP4IF_FSM_STATUS_MAX
+};
+
+struct isp4if_gpu_mem_info {
+ u64 mem_size;
+ u64 gpu_mc_addr;
+ void *sys_addr;
+ void *mem_handle;
+};
+
+struct isp4if_img_buf_info {
+ struct {
+ void *sys_addr;
+ u64 mc_addr;
+ u32 len;
+ } planes[3];
+};
+
+struct isp4if_img_buf_node {
+ struct list_head node;
+ struct isp4if_img_buf_info buf_info;
+};
+
+struct isp4if_cmd_element {
+ struct list_head list;
+ u32 seq_num;
+ u32 cmd_id;
+ struct completion cmd_done;
+ atomic_t refcnt;
+};
+
+struct isp4_interface {
+ struct device *dev;
+ void __iomem *mmio;
+
+ spinlock_t cmdq_lock; /* used for cmdq access */
+ spinlock_t bufq_lock; /* used for bufq access */
+ struct mutex isp4if_mutex; /* used to send fw cmd and read fw log */
+
+ struct list_head cmdq; /* commands sent to fw */
+ struct list_head bufq; /* buffers sent to fw */
+
+ enum isp4if_status status;
+ u32 host2fw_seq_num;
+
+ /* ISP fw buffers */
+ struct isp4if_gpu_mem_info *fw_log_buf;
+ struct isp4if_gpu_mem_info *fw_cmd_resp_buf;
+ struct isp4if_gpu_mem_info *fw_mem_pool;
+ struct isp4if_gpu_mem_info *meta_info_buf[ISP4IF_MAX_STREAM_BUF_COUNT];
+};
+
+static inline void isp4if_split_addr64(u64 addr, u32 *lo, u32 *hi)
+{
+ if (lo)
+ *lo = addr & 0xffffffff;
+
+ if (hi)
+ *hi = addr >> 32;
+}
+
+static inline u64 isp4if_join_addr64(u32 lo, u32 hi)
+{
+ return (((u64)hi) << 32) | (u64)lo;
+}
+
+int isp4if_f2h_resp(struct isp4_interface *ispif, enum isp4if_stream_id stream,
+ struct isp4fw_resp *resp);
+
+int isp4if_send_command(struct isp4_interface *ispif, u32 cmd_id,
+ const void *package, u32 package_size);
+
+int isp4if_send_command_sync(struct isp4_interface *ispif, u32 cmd_id,
+ const void *package, u32 package_size);
+
+struct isp4if_cmd_element *isp4if_rm_cmd_from_cmdq(struct isp4_interface *ispif,
+ u32 seq_num, u32 cmd_id);
+
+void isp4if_clear_cmdq(struct isp4_interface *ispif);
+
+void isp4if_clear_bufq(struct isp4_interface *ispif);
+
+void isp4if_dealloc_buffer_node(struct isp4if_img_buf_node *buf_node);
+
+struct isp4if_img_buf_node *
+isp4if_alloc_buffer_node(struct isp4if_img_buf_info *buf_info);
+
+struct isp4if_img_buf_node *isp4if_dequeue_buffer(struct isp4_interface *ispif);
+
+int isp4if_queue_buffer(struct isp4_interface *ispif,
+ struct isp4if_img_buf_node *buf_node);
+
+int isp4if_stop(struct isp4_interface *ispif);
+
+int isp4if_start(struct isp4_interface *ispif);
+
+int isp4if_deinit(struct isp4_interface *ispif);
+
+int isp4if_init(struct isp4_interface *ispif, struct device *dev,
+ void __iomem *isp_mmio);
+
+#endif /* _ISP4_INTERFACE_H_ */
diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c
new file mode 100644
index 000000000000..48deea79ce6c
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_subdev.c
@@ -0,0 +1,1047 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <linux/pm_domain.h>
+#include <linux/units.h>
+
+#include "isp4.h"
+#include "isp4_debug.h"
+#include "isp4_fw_cmd_resp.h"
+#include "isp4_interface.h"
+
+#define ISP4SD_MIN_BUF_CNT_BEF_START_STREAM 4
+
+#define ISP4SD_PERFORMANCE_STATE_LOW 0
+#define ISP4SD_PERFORMANCE_STATE_HIGH 1
+
+/* align 32KB */
+#define ISP4SD_META_BUF_SIZE ALIGN(sizeof(struct isp4fw_meta_info), 0x8000)
+
+#define to_isp4_subdev(sd) container_of(sd, struct isp4_subdev, sdev)
+
+static const char *isp4sd_entity_name = "amd isp4";
+
+static const char *isp4sd_thread_name[ISP4SD_MAX_FW_RESP_STREAM_NUM] = {
+ "amd_isp4_thread_global",
+ "amd_isp4_thread_stream1",
+};
+
+static void isp4sd_module_enable(struct isp4_subdev *isp_subdev, bool enable)
+{
+ if (isp_subdev->enable_gpio) {
+ gpiod_set_value(isp_subdev->enable_gpio, enable ? 1 : 0);
+ dev_dbg(isp_subdev->dev, "%s isp_subdev module\n",
+ enable ? "enable" : "disable");
+ }
+}
+
+static int isp4sd_setup_fw_mem_pool(struct isp4_subdev *isp_subdev)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct isp4fw_cmd_send_buffer buf_type;
+ struct device *dev = isp_subdev->dev;
+ int ret;
+
+ if (!ispif->fw_mem_pool) {
+ dev_err(dev, "fail to alloc mem pool\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&buf_type, 0, sizeof(buf_type));
+ buf_type.buffer_type = ISP4FW_BUFFER_TYPE_MEM_POOL;
+ buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
+ isp4if_split_addr64(ispif->fw_mem_pool->gpu_mc_addr,
+ &buf_type.buffer.buf_base_a_lo,
+ &buf_type.buffer.buf_base_a_hi);
+ buf_type.buffer.buf_size_a = ispif->fw_mem_pool->mem_size;
+
+ ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
+ &buf_type, sizeof(buf_type));
+ if (ret) {
+ dev_err(dev, "send fw mem pool 0x%llx(%u) fail %d\n",
+ ispif->fw_mem_pool->gpu_mc_addr,
+ buf_type.buffer.buf_size_a, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "send fw mem pool 0x%llx(%u) suc\n",
+ ispif->fw_mem_pool->gpu_mc_addr, buf_type.buffer.buf_size_a);
+
+ return 0;
+}
+
+static int isp4sd_set_stream_path(struct isp4_subdev *isp_subdev)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct isp4fw_cmd_set_stream_cfg cmd;
+ struct device *dev = isp_subdev->dev;
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id =
+ ISP4FW_SENSOR_ID_ON_MIPI0;
+ cmd.stream_cfg.mipi_pipe_path_cfg.b_enable = true;
+ cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id =
+ ISP4FW_MIPI0_ISP_PIPELINE_ID;
+
+ cmd.stream_cfg.b_enable_tnr = true;
+ dev_dbg(dev, "isp4fw_sensor_id %d, pipeId 0x%x EnableTnr %u\n",
+ cmd.stream_cfg.mipi_pipe_path_cfg.isp4fw_sensor_id,
+ cmd.stream_cfg.isp_pipe_path_cfg.isp_pipe_id,
+ cmd.stream_cfg.b_enable_tnr);
+
+ return isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_STREAM_CONFIG,
+ &cmd, sizeof(cmd));
+}
+
+static int isp4sd_send_meta_buf(struct isp4_subdev *isp_subdev)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct isp4fw_cmd_send_buffer buf_type;
+ struct device *dev = isp_subdev->dev;
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&buf_type, 0, sizeof(buf_type));
+ for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
+ struct isp4if_gpu_mem_info *meta_info_buf =
+ isp_subdev->ispif.meta_info_buf[i];
+ int ret;
+
+ if (!meta_info_buf) {
+ dev_err(dev, "fail for no meta info buf(%u)\n", i);
+ return -ENOMEM;
+ }
+
+ buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO;
+ buf_type.buffer.vmid_space.bit.space =
+ ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
+ isp4if_split_addr64(meta_info_buf->gpu_mc_addr,
+ &buf_type.buffer.buf_base_a_lo,
+ &buf_type.buffer.buf_base_a_hi);
+ buf_type.buffer.buf_size_a = meta_info_buf->mem_size;
+ ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
+ &buf_type, sizeof(buf_type));
+ if (ret) {
+ dev_err(dev, "send meta info(%u) fail\n", i);
+ return ret;
+ }
+ }
+
+ dev_dbg(dev, "send meta info suc\n");
+ return 0;
+}
+
+static bool isp4sd_get_str_out_prop(struct isp4_subdev *isp_subdev,
+ struct isp4fw_image_prop *out_prop,
+ struct v4l2_subdev_state *state, u32 pad)
+{
+ struct device *dev = isp_subdev->dev;
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_state_get_format(state, pad, 0);
+ if (!format) {
+ dev_err(dev, "fail get subdev state format\n");
+ return false;
+ }
+
+ switch (format->code) {
+ case MEDIA_BUS_FMT_YUYV8_1_5X8:
+ out_prop->image_format = ISP4FW_IMAGE_FORMAT_NV12;
+ out_prop->width = format->width;
+ out_prop->height = format->height;
+ out_prop->luma_pitch = format->width;
+ out_prop->chroma_pitch = out_prop->width;
+ break;
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ out_prop->image_format = ISP4FW_IMAGE_FORMAT_YUV422INTERLEAVED;
+ out_prop->width = format->width;
+ out_prop->height = format->height;
+ out_prop->luma_pitch = format->width * 2;
+ out_prop->chroma_pitch = 0;
+ break;
+ default:
+ dev_err(dev, "fail for bad image format:0x%x\n",
+ format->code);
+ return false;
+ }
+
+ if (!out_prop->width || !out_prop->height)
+ return false;
+
+ return true;
+}
+
+static int isp4sd_kickoff_stream(struct isp4_subdev *isp_subdev, u32 w, u32 h)
+{
+ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = isp_subdev->dev;
+
+ if (sensor_info->status == ISP4SD_START_STATUS_STARTED)
+ return 0;
+
+ if (sensor_info->status == ISP4SD_START_STATUS_START_FAIL) {
+ dev_err(dev, "fail for previous start fail\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "w:%u,h:%u\n", w, h);
+
+ if (isp4sd_send_meta_buf(isp_subdev)) {
+ dev_err(dev, "fail to send meta buf\n");
+ sensor_info->status = ISP4SD_START_STATUS_START_FAIL;
+ return -EINVAL;
+ }
+
+ sensor_info->status = ISP4SD_START_STATUS_OFF;
+
+ if (!sensor_info->start_stream_cmd_sent &&
+ sensor_info->buf_sent_cnt >= ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) {
+ int ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_START_STREAM,
+ NULL, 0);
+ if (ret) {
+ dev_err(dev, "fail to start stream\n");
+ return ret;
+ }
+
+ sensor_info->start_stream_cmd_sent = true;
+ } else {
+ dev_dbg(dev,
+ "no send START_STREAM, start_sent %u, buf_sent %u\n",
+ sensor_info->start_stream_cmd_sent,
+ sensor_info->buf_sent_cnt);
+ }
+
+ return 0;
+}
+
+static int isp4sd_setup_output(struct isp4_subdev *isp_subdev,
+ struct v4l2_subdev_state *state, u32 pad)
+{
+ struct isp4sd_output_info *output_info =
+ &isp_subdev->sensor_info.output_info;
+ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct isp4fw_cmd_set_out_ch_prop cmd_ch_prop;
+ struct isp4fw_cmd_enable_out_ch cmd_ch_en;
+ struct device *dev = isp_subdev->dev;
+ int ret;
+
+ if (output_info->start_status == ISP4SD_START_STATUS_STARTED)
+ return 0;
+
+ if (output_info->start_status == ISP4SD_START_STATUS_START_FAIL) {
+ dev_err(dev, "fail for previous start fail\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&cmd_ch_prop, 0, sizeof(cmd_ch_prop));
+ cmd_ch_prop.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
+
+ if (!isp4sd_get_str_out_prop(isp_subdev,
+ &cmd_ch_prop.image_prop, state, pad)) {
+ dev_err(dev, "fail to get out prop\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "channel:%s,fmt %s,w:h=%u:%u,lp:%u,cp%u\n",
+ isp4dbg_get_out_ch_str(cmd_ch_prop.ch),
+ isp4dbg_get_img_fmt_str(cmd_ch_prop.image_prop.image_format),
+ cmd_ch_prop.image_prop.width, cmd_ch_prop.image_prop.height,
+ cmd_ch_prop.image_prop.luma_pitch,
+ cmd_ch_prop.image_prop.chroma_pitch);
+
+ ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_SET_OUT_CHAN_PROP,
+ &cmd_ch_prop, sizeof(cmd_ch_prop));
+ if (ret) {
+ output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
+ dev_err(dev, "fail to set out prop\n");
+ return ret;
+ }
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&cmd_ch_en, 0, sizeof(cmd_ch_en));
+ cmd_ch_en.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
+ cmd_ch_en.is_enable = true;
+ ret = isp4if_send_command(ispif, ISP4FW_CMD_ID_ENABLE_OUT_CHAN,
+ &cmd_ch_en, sizeof(cmd_ch_en));
+ if (ret) {
+ output_info->start_status = ISP4SD_START_STATUS_START_FAIL;
+ dev_err(dev, "fail to enable channel\n");
+ return ret;
+ }
+
+ dev_dbg(dev, "enable channel %s\n",
+ isp4dbg_get_out_ch_str(cmd_ch_en.ch));
+
+ if (!sensor_info->start_stream_cmd_sent) {
+ ret = isp4sd_kickoff_stream(isp_subdev,
+ cmd_ch_prop.image_prop.width,
+ cmd_ch_prop.image_prop.height);
+ if (ret) {
+ dev_err(dev, "kickoff stream fail %d\n", ret);
+ return ret;
+ }
+ /*
+ * sensor_info->start_stream_cmd_sent will be set to true
+ * 1. in isp4sd_kickoff_stream, if app first send buffer then
+ * start stream
+ * 2. in isp_set_stream_buf, if app first start stream, then
+ * send buffer because ISP FW has the requirement, host needs
+ * to send buffer before send start stream cmd
+ */
+ if (sensor_info->start_stream_cmd_sent) {
+ sensor_info->status = ISP4SD_START_STATUS_STARTED;
+ output_info->start_status = ISP4SD_START_STATUS_STARTED;
+ dev_dbg(dev, "kickoff stream suc,start cmd sent\n");
+ }
+ } else {
+ dev_dbg(dev, "stream running, no need kickoff\n");
+ output_info->start_status = ISP4SD_START_STATUS_STARTED;
+ }
+
+ dev_dbg(dev, "setup output suc\n");
+ return 0;
+}
+
+static int isp4sd_init_stream(struct isp4_subdev *isp_subdev)
+{
+ struct device *dev = isp_subdev->dev;
+ int ret;
+
+ ret = isp4sd_setup_fw_mem_pool(isp_subdev);
+ if (ret) {
+ dev_err(dev, "fail to setup fw mem pool\n");
+ return ret;
+ }
+
+ ret = isp4sd_set_stream_path(isp_subdev);
+ if (ret) {
+ dev_err(dev, "fail to setup stream path\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void isp4sd_uninit_stream(struct isp4_subdev *isp_subdev,
+ struct v4l2_subdev_state *state, u32 pad)
+{
+ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
+ struct isp4sd_output_info *output_info = &sensor_info->output_info;
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct v4l2_mbus_framefmt *format;
+
+ format = v4l2_subdev_state_get_format(state, pad, 0);
+ if (!format) {
+ dev_err(isp_subdev->dev, "fail to get v4l2 format\n");
+ } else {
+ memset(format, 0, sizeof(*format));
+ format->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
+ }
+
+ isp4if_clear_bufq(ispif);
+ isp4if_clear_cmdq(ispif);
+
+ sensor_info->start_stream_cmd_sent = false;
+ sensor_info->buf_sent_cnt = 0;
+
+ sensor_info->status = ISP4SD_START_STATUS_OFF;
+ output_info->start_status = ISP4SD_START_STATUS_OFF;
+}
+
+static void isp4sd_fw_resp_cmd_done(struct isp4_subdev *isp_subdev,
+ enum isp4if_stream_id stream_id,
+ struct isp4fw_resp_cmd_done *para)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct isp4if_cmd_element *ele =
+ isp4if_rm_cmd_from_cmdq(ispif, para->cmd_seq_num, para->cmd_id);
+ struct device *dev = isp_subdev->dev;
+
+ dev_dbg(dev, "stream %d,cmd %s(0x%08x)(%d),seq %u, ele %p\n",
+ stream_id,
+ isp4dbg_get_cmd_str(para->cmd_id),
+ para->cmd_id, para->cmd_status, para->cmd_seq_num,
+ ele);
+
+ if (ele) {
+ complete(&ele->cmd_done);
+ if (atomic_dec_and_test(&ele->refcnt))
+ kfree(ele);
+ }
+}
+
+static struct isp4fw_meta_info *
+isp4sd_get_meta_by_mc(struct isp4_subdev *isp_subdev, u64 mc)
+{
+ for (unsigned int i = 0; i < ISP4IF_MAX_STREAM_BUF_COUNT; i++) {
+ struct isp4if_gpu_mem_info *meta_info_buf =
+ isp_subdev->ispif.meta_info_buf[i];
+
+ if (meta_info_buf->gpu_mc_addr == mc)
+ return meta_info_buf->sys_addr;
+ }
+
+ return NULL;
+}
+
+static void isp4sd_send_meta_info(struct isp4_subdev *isp_subdev,
+ u64 meta_info_mc)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct isp4fw_cmd_send_buffer buf_type;
+ struct device *dev = isp_subdev->dev;
+
+ if (isp_subdev->sensor_info.status != ISP4SD_START_STATUS_STARTED) {
+ dev_warn(dev, "not working status %i, meta_info 0x%llx\n",
+ isp_subdev->sensor_info.status, meta_info_mc);
+ return;
+ }
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to guarantee
+ * padding bits are zeroed, since this is not guaranteed on all
+ * compilers.
+ */
+ memset(&buf_type, 0, sizeof(buf_type));
+ buf_type.buffer_type = ISP4FW_BUFFER_TYPE_META_INFO;
+ buf_type.buffer.vmid_space.bit.space = ISP4FW_ADDR_SPACE_TYPE_GPU_VA;
+ isp4if_split_addr64(meta_info_mc,
+ &buf_type.buffer.buf_base_a_lo,
+ &buf_type.buffer.buf_base_a_hi);
+ buf_type.buffer.buf_size_a = ISP4SD_META_BUF_SIZE;
+
+ if (isp4if_send_command(ispif, ISP4FW_CMD_ID_SEND_BUFFER,
+ &buf_type, sizeof(buf_type)))
+ dev_err(dev, "fail send meta_info 0x%llx\n",
+ meta_info_mc);
+ else
+ dev_dbg(dev, "resend meta_info 0x%llx\n", meta_info_mc);
+}
+
+static void isp4sd_fw_resp_frame_done(struct isp4_subdev *isp_subdev,
+ enum isp4if_stream_id stream_id,
+ struct isp4fw_resp_param_package *para)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = isp_subdev->dev;
+ struct isp4if_img_buf_node *prev;
+ struct isp4fw_meta_info *meta;
+ u64 mc;
+
+ mc = isp4if_join_addr64(para->package_addr_lo, para->package_addr_hi);
+ meta = isp4sd_get_meta_by_mc(isp_subdev, mc);
+ if (!meta) {
+ dev_err(dev, "fail to get meta from mc %llx\n", mc);
+ return;
+ }
+
+ dev_dbg(dev, "ts:%llu,streamId:%d,poc:%u,preview_en:%u,status:%s(%i)\n",
+ ktime_get_ns(), stream_id, meta->poc, meta->preview.enabled,
+ isp4dbg_get_buf_done_str(meta->preview.status),
+ meta->preview.status);
+
+ if (meta->preview.enabled &&
+ (meta->preview.status == ISP4FW_BUFFER_STATUS_SKIPPED ||
+ meta->preview.status == ISP4FW_BUFFER_STATUS_DONE ||
+ meta->preview.status == ISP4FW_BUFFER_STATUS_DIRTY)) {
+ prev = isp4if_dequeue_buffer(ispif);
+ if (prev) {
+ isp4dbg_show_bufmeta_info(dev, "prev", &meta->preview,
+ &prev->buf_info);
+ isp4vid_handle_frame_done(&isp_subdev->isp_vdev,
+ &prev->buf_info);
+ isp4if_dealloc_buffer_node(prev);
+ } else {
+ dev_err(dev, "fail null prev buf\n");
+ }
+ } else if (meta->preview.enabled) {
+ dev_err(dev, "fail bad preview status %u(%s)\n",
+ meta->preview.status,
+ isp4dbg_get_buf_done_str(meta->preview.status));
+ }
+
+ if (isp_subdev->sensor_info.status == ISP4SD_START_STATUS_STARTED)
+ isp4sd_send_meta_info(isp_subdev, mc);
+
+ dev_dbg(dev, "stream_id:%d, status:%d\n", stream_id,
+ isp_subdev->sensor_info.status);
+}
+
+static void isp4sd_fw_resp_func(struct isp4_subdev *isp_subdev,
+ enum isp4if_stream_id stream_id)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = isp_subdev->dev;
+ struct isp4fw_resp resp;
+
+ if (stream_id == ISP4IF_STREAM_ID_1)
+ isp_fw_log_print(isp_subdev);
+
+ while (true) {
+ if (isp4if_f2h_resp(ispif, stream_id, &resp)) {
+ /* Re-enable the interrupt */
+ isp4_intr_enable(isp_subdev, stream_id, true);
+ /*
+ * Recheck to see if there is a new response.
+ * To ensure that an in-flight interrupt is not lost,
+ * enabling the interrupt must occur _before_ checking
+ * for a new response, hence a memory barrier is needed.
+ * Disable the interrupt again if there was a new
+ * response.
+ */
+ mb();
+ if (likely(isp4if_f2h_resp(ispif, stream_id, &resp)))
+ break;
+
+ isp4_intr_enable(isp_subdev, stream_id, false);
+ }
+
+ switch (resp.resp_id) {
+ case ISP4FW_RESP_ID_CMD_DONE:
+ isp4sd_fw_resp_cmd_done(isp_subdev, stream_id,
+ &resp.param.cmd_done);
+ break;
+ case ISP4FW_RESP_ID_NOTI_FRAME_DONE:
+ isp4sd_fw_resp_frame_done(isp_subdev, stream_id,
+ &resp.param.frame_done);
+ break;
+ default:
+ dev_err(dev, "-><- fail respid %s(0x%x)\n",
+ isp4dbg_get_resp_str(resp.resp_id),
+ resp.resp_id);
+ break;
+ }
+ }
+}
+
+static s32 isp4sd_fw_resp_thread(void *context)
+{
+ struct isp4_subdev_thread_param *para = context;
+ struct isp4_subdev *isp_subdev = para->isp_subdev;
+ struct isp4sd_thread_handler *thread_ctx =
+ &isp_subdev->fw_resp_thread[para->idx];
+ struct device *dev = isp_subdev->dev;
+
+ dev_dbg(dev, "[%u] fw resp thread started\n", para->idx);
+ while (true) {
+ wait_event_interruptible(thread_ctx->waitq,
+ thread_ctx->resp_ready);
+ thread_ctx->resp_ready = false;
+
+ if (kthread_should_stop()) {
+ dev_dbg(dev, "[%u] fw resp thread quit\n", para->idx);
+ break;
+ }
+
+ isp4sd_fw_resp_func(isp_subdev, para->idx);
+ }
+
+ return 0;
+}
+
+static int isp4sd_stop_resp_proc_threads(struct isp4_subdev *isp_subdev)
+{
+ for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
+ struct isp4sd_thread_handler *thread_ctx =
+ &isp_subdev->fw_resp_thread[i];
+
+ if (thread_ctx->thread) {
+ kthread_stop(thread_ctx->thread);
+ thread_ctx->thread = NULL;
+ }
+ }
+
+ return 0;
+}
+
+static int isp4sd_start_resp_proc_threads(struct isp4_subdev *isp_subdev)
+{
+ struct device *dev = isp_subdev->dev;
+
+ for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++) {
+ struct isp4sd_thread_handler *thread_ctx =
+ &isp_subdev->fw_resp_thread[i];
+
+ isp_subdev->isp_resp_para[i].idx = i;
+ isp_subdev->isp_resp_para[i].isp_subdev = isp_subdev;
+ init_waitqueue_head(&thread_ctx->waitq);
+ thread_ctx->resp_ready = false;
+
+ thread_ctx->thread = kthread_run(isp4sd_fw_resp_thread,
+ &isp_subdev->isp_resp_para[i],
+ isp4sd_thread_name[i]);
+ if (IS_ERR(thread_ctx->thread)) {
+ dev_err(dev, "create thread [%d] fail\n", i);
+ thread_ctx->thread = NULL;
+ isp4sd_stop_resp_proc_threads(isp_subdev);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd)
+{
+ struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
+ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
+ unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_LOW;
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = isp_subdev->dev;
+ int ret;
+
+ guard(mutex)(&isp_subdev->ops_mutex);
+ if (sensor_info->status == ISP4SD_START_STATUS_STARTED) {
+ dev_err(dev, "fail for stream still running\n");
+ return -EINVAL;
+ }
+
+ sensor_info->status = ISP4SD_START_STATUS_OFF;
+
+ if (isp_subdev->irq_enabled) {
+ for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
+ disable_irq(isp_subdev->irq[i]);
+ isp_subdev->irq_enabled = false;
+ }
+
+ isp4sd_stop_resp_proc_threads(isp_subdev);
+ dev_dbg(dev, "isp_subdev stop resp proc threads suc\n");
+
+ isp4if_stop(ispif);
+
+ ret = dev_pm_genpd_set_performance_state(dev, perf_state);
+ if (ret)
+ dev_err(dev,
+ "fail to set isp_subdev performance state %u,ret %d\n",
+ perf_state, ret);
+
+ /* hold ccpu reset */
+ isp4hw_wreg(isp_subdev->mmio, ISP_SOFT_RESET, 0);
+ isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0);
+ ret = pm_runtime_put_sync(dev);
+ if (ret)
+ dev_err(dev, "power off isp_subdev fail %d\n", ret);
+ else
+ dev_dbg(dev, "power off isp_subdev suc\n");
+
+ ispif->status = ISP4IF_STATUS_PWR_OFF;
+ isp4if_clear_cmdq(ispif);
+ isp4sd_module_enable(isp_subdev, false);
+
+ /*
+ * When opening the camera, isp4sd_module_enable(isp_subdev, true) is
+ * called. Hardware requires at least a 20ms delay between disabling
+ * and enabling the module, so a sleep is added to ensure ISP stability
+ * during quick reopen scenarios.
+ */
+ msleep(20);
+
+ return 0;
+}
+
+int isp4sd_pwron_and_init(struct v4l2_subdev *sd)
+{
+ struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = isp_subdev->dev;
+ int ret;
+
+ guard(mutex)(&isp_subdev->ops_mutex);
+ if (ispif->status == ISP4IF_STATUS_FW_RUNNING) {
+ dev_dbg(dev, "camera already opened, do nothing\n");
+ return 0;
+ }
+
+ isp4sd_module_enable(isp_subdev, true);
+
+ if (ispif->status < ISP4IF_STATUS_PWR_ON) {
+ unsigned int perf_state = ISP4SD_PERFORMANCE_STATE_HIGH;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "fail to power on isp_subdev ret %d\n",
+ ret);
+ goto err_deinit;
+ }
+
+ /* ISPPG ISP Power Status */
+ isp4hw_wreg(isp_subdev->mmio, ISP_POWER_STATUS, 0x7FF);
+ ret = dev_pm_genpd_set_performance_state(dev, perf_state);
+ if (ret) {
+ dev_err(dev,
+ "fail to set performance state %u, ret %d\n",
+ perf_state, ret);
+ goto err_deinit;
+ }
+
+ ispif->status = ISP4IF_STATUS_PWR_ON;
+ }
+
+ isp_subdev->sensor_info.start_stream_cmd_sent = false;
+ isp_subdev->sensor_info.buf_sent_cnt = 0;
+
+ ret = isp4if_start(ispif);
+ if (ret) {
+ dev_err(dev, "fail to start isp_subdev interface\n");
+ goto err_deinit;
+ }
+
+ if (isp4sd_start_resp_proc_threads(isp_subdev)) {
+ dev_err(dev, "isp_start_resp_proc_threads fail\n");
+ goto err_deinit;
+ }
+
+ dev_dbg(dev, "create resp threads ok\n");
+
+ for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
+ enable_irq(isp_subdev->irq[i]);
+ isp_subdev->irq_enabled = true;
+
+ return 0;
+err_deinit:
+ isp4sd_pwroff_and_deinit(sd);
+ return -EINVAL;
+}
+
+static int isp4sd_stop_stream(struct isp4_subdev *isp_subdev,
+ struct v4l2_subdev_state *state, u32 pad)
+{
+ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
+ struct isp4sd_output_info *output_info = &sensor_info->output_info;
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = isp_subdev->dev;
+
+ guard(mutex)(&isp_subdev->ops_mutex);
+ dev_dbg(dev, "status %i\n", output_info->start_status);
+
+ if (output_info->start_status == ISP4SD_START_STATUS_STARTED) {
+ struct isp4fw_cmd_enable_out_ch cmd_ch_disable;
+ int ret;
+
+ /*
+ * The struct will be shared with ISP FW, use memset() to
+ * guarantee padding bits are zeroed, since this is not
+ * guaranteed on all compilers.
+ */
+ memset(&cmd_ch_disable, 0, sizeof(cmd_ch_disable));
+ cmd_ch_disable.ch = ISP4FW_ISP_PIPE_OUT_CH_PREVIEW;
+ /* `cmd_ch_disable.is_enable` is already false */
+ ret = isp4if_send_command_sync(ispif,
+ ISP4FW_CMD_ID_ENABLE_OUT_CHAN,
+ &cmd_ch_disable,
+ sizeof(cmd_ch_disable));
+ if (ret)
+ dev_err(dev, "fail to disable stream\n");
+ else
+ dev_dbg(dev, "wait disable stream suc\n");
+
+ ret = isp4if_send_command_sync(ispif, ISP4FW_CMD_ID_STOP_STREAM,
+ NULL, 0);
+ if (ret)
+ dev_err(dev, "fail to stop stream\n");
+ else
+ dev_dbg(dev, "wait stop stream suc\n");
+ }
+
+ isp4sd_uninit_stream(isp_subdev, state, pad);
+
+ /*
+ * Return success to ensure the stop process proceeds,
+ * and disregard any errors since they are not fatal.
+ */
+ return 0;
+}
+
+static int isp4sd_start_stream(struct isp4_subdev *isp_subdev,
+ struct v4l2_subdev_state *state, u32 pad)
+{
+ struct isp4sd_output_info *output_info =
+ &isp_subdev->sensor_info.output_info;
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = isp_subdev->dev;
+ int ret;
+
+ guard(mutex)(&isp_subdev->ops_mutex);
+
+ if (ispif->status != ISP4IF_STATUS_FW_RUNNING) {
+ dev_err(dev, "fail, bad fsm %d\n", ispif->status);
+ return -EINVAL;
+ }
+
+ switch (output_info->start_status) {
+ case ISP4SD_START_STATUS_OFF:
+ break;
+ case ISP4SD_START_STATUS_STARTED:
+ dev_dbg(dev, "stream already started, do nothing\n");
+ return 0;
+ case ISP4SD_START_STATUS_START_FAIL:
+ dev_err(dev, "stream previously failed to start\n");
+ return -EINVAL;
+ }
+
+ ret = isp4sd_init_stream(isp_subdev);
+ if (ret) {
+ dev_err(dev, "fail to init isp_subdev stream\n");
+ goto err_stop_stream;
+ }
+
+ ret = isp4sd_setup_output(isp_subdev, state, pad);
+ if (ret) {
+ dev_err(dev, "fail to setup output\n");
+ goto err_stop_stream;
+ }
+
+ return 0;
+
+err_stop_stream:
+ isp4sd_stop_stream(isp_subdev, state, pad);
+ return ret;
+}
+
+int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd,
+ struct isp4if_img_buf_info *buf_info)
+{
+ struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct isp4if_img_buf_node *buf_node;
+ struct device *dev = isp_subdev->dev;
+ int ret;
+
+ guard(mutex)(&isp_subdev->ops_mutex);
+
+ if (ispif->status != ISP4IF_STATUS_FW_RUNNING) {
+ dev_err(dev, "fail send img buf for bad fsm %d\n",
+ ispif->status);
+ return -EINVAL;
+ }
+
+ buf_node = isp4if_alloc_buffer_node(buf_info);
+ if (!buf_node) {
+ dev_err(dev, "fail alloc sys img buf info node\n");
+ return -ENOMEM;
+ }
+
+ ret = isp4if_queue_buffer(ispif, buf_node);
+ if (ret) {
+ dev_err(dev, "fail to queue image buf, %d\n", ret);
+ goto error_release_buf_node;
+ }
+
+ if (!isp_subdev->sensor_info.start_stream_cmd_sent) {
+ isp_subdev->sensor_info.buf_sent_cnt++;
+
+ if (isp_subdev->sensor_info.buf_sent_cnt >=
+ ISP4SD_MIN_BUF_CNT_BEF_START_STREAM) {
+ ret = isp4if_send_command(ispif,
+ ISP4FW_CMD_ID_START_STREAM,
+ NULL, 0);
+ if (ret) {
+ dev_err(dev, "fail to START_STREAM");
+ goto error_release_buf_node;
+ }
+ isp_subdev->sensor_info.start_stream_cmd_sent = true;
+ isp_subdev->sensor_info.output_info.start_status =
+ ISP4SD_START_STATUS_STARTED;
+ isp_subdev->sensor_info.status =
+ ISP4SD_START_STATUS_STARTED;
+ } else {
+ dev_dbg(dev,
+ "no send start, required %u, buf sent %u\n",
+ ISP4SD_MIN_BUF_CNT_BEF_START_STREAM,
+ isp_subdev->sensor_info.buf_sent_cnt);
+ }
+ }
+
+ return 0;
+
+error_release_buf_node:
+ isp4if_dealloc_buffer_node(buf_node);
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops isp4sd_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static int isp4sd_set_pad_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct isp4sd_output_info *stream_info =
+ &(to_isp4_subdev(sd)->sensor_info.output_info);
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
+
+ if (!fmt) {
+ dev_err(sd->dev, "fail to get state format\n");
+ return -EINVAL;
+ }
+
+ *fmt = format->format;
+ switch (fmt->code) {
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ stream_info->image_size = fmt->width * fmt->height * 2;
+ break;
+ case MEDIA_BUS_FMT_YUYV8_1_5X8:
+ default:
+ stream_info->image_size = fmt->width * fmt->height * 3 / 2;
+ break;
+ }
+
+ if (!stream_info->image_size) {
+ dev_err(sd->dev,
+ "fail set pad format,code 0x%x,width %u, height %u\n",
+ fmt->code, fmt->width, fmt->height);
+ return -EINVAL;
+ }
+
+ dev_dbg(sd->dev, "set pad format suc, code:%x w:%u h:%u size:%u\n",
+ fmt->code, fmt->width, fmt->height,
+ stream_info->image_size);
+
+ return 0;
+}
+
+static int isp4sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
+
+ return isp4sd_start_stream(isp_subdev, state, pad);
+}
+
+static int isp4sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct isp4_subdev *isp_subdev = to_isp4_subdev(sd);
+
+ return isp4sd_stop_stream(isp_subdev, state, pad);
+}
+
+static const struct v4l2_subdev_pad_ops isp4sd_pad_ops = {
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = isp4sd_set_pad_format,
+ .enable_streams = isp4sd_enable_streams,
+ .disable_streams = isp4sd_disable_streams,
+};
+
+static const struct v4l2_subdev_ops isp4sd_subdev_ops = {
+ .video = &isp4sd_video_ops,
+ .pad = &isp4sd_pad_ops,
+};
+
+int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev,
+ int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM])
+{
+ struct isp4sd_sensor_info *sensor_info = &isp_subdev->sensor_info;
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+ struct device *dev = v4l2_dev->dev;
+ int ret;
+
+ isp_subdev->dev = dev;
+ v4l2_subdev_init(&isp_subdev->sdev, &isp4sd_subdev_ops);
+ isp_subdev->sdev.owner = THIS_MODULE;
+ isp_subdev->sdev.dev = dev;
+ snprintf(isp_subdev->sdev.name, sizeof(isp_subdev->sdev.name), "%s",
+ dev_name(dev));
+
+ isp_subdev->sdev.entity.name = isp4sd_entity_name;
+ isp_subdev->sdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
+ isp_subdev->sdev_pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_pads_init(&isp_subdev->sdev.entity, 1,
+ &isp_subdev->sdev_pad);
+ if (ret) {
+ dev_err(dev, "fail to init isp4 subdev entity pad %d\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(&isp_subdev->sdev);
+ if (ret < 0) {
+ dev_err(dev, "fail to init finalize isp4 subdev %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = v4l2_device_register_subdev(v4l2_dev, &isp_subdev->sdev);
+ if (ret) {
+ dev_err(dev, "fail to register isp4 subdev to V4L2 device %d\n",
+ ret);
+ goto err_media_clean_up;
+ }
+
+ isp4if_init(ispif, dev, isp_subdev->mmio);
+
+ mutex_init(&isp_subdev->ops_mutex);
+ sensor_info->status = ISP4SD_START_STATUS_OFF;
+
+ /* create ISP enable gpio control */
+ isp_subdev->enable_gpio = devm_gpiod_get(isp_subdev->dev,
+ "enable_isp",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(isp_subdev->enable_gpio)) {
+ ret = PTR_ERR(isp_subdev->enable_gpio);
+ dev_err(dev, "fail to get gpiod %d\n", ret);
+ goto err_subdev_unreg;
+ }
+
+ for (unsigned int i = 0; i < ISP4SD_MAX_FW_RESP_STREAM_NUM; i++)
+ isp_subdev->irq[i] = irq[i];
+
+ isp_subdev->host2fw_seq_num = 1;
+ ispif->status = ISP4IF_STATUS_PWR_OFF;
+
+ ret = isp4vid_dev_init(&isp_subdev->isp_vdev, &isp_subdev->sdev);
+ if (ret)
+ goto err_subdev_unreg;
+
+ return 0;
+
+err_subdev_unreg:
+ v4l2_device_unregister_subdev(&isp_subdev->sdev);
+err_media_clean_up:
+ v4l2_subdev_cleanup(&isp_subdev->sdev);
+ media_entity_cleanup(&isp_subdev->sdev.entity);
+ return ret;
+}
+
+void isp4sd_deinit(struct isp4_subdev *isp_subdev)
+{
+ struct isp4_interface *ispif = &isp_subdev->ispif;
+
+ isp4vid_dev_deinit(&isp_subdev->isp_vdev);
+ v4l2_device_unregister_subdev(&isp_subdev->sdev);
+ media_entity_cleanup(&isp_subdev->sdev.entity);
+ isp4if_deinit(ispif);
+ isp4sd_module_enable(isp_subdev, false);
+
+ ispif->status = ISP4IF_STATUS_PWR_OFF;
+}
diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.h b/drivers/media/platform/amd/isp4/isp4_subdev.h
new file mode 100644
index 000000000000..20ea08a830af
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_subdev.h
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_SUBDEV_H_
+#define _ISP4_SUBDEV_H_
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <media/v4l2-device.h>
+
+#include "isp4_fw_cmd_resp.h"
+#include "isp4_hw_reg.h"
+#include "isp4_interface.h"
+#include "isp4_video.h"
+
+/*
+ * One is for none sensor specific response which is not used now.
+ * Another is for sensor specific response
+ */
+#define ISP4SD_MAX_FW_RESP_STREAM_NUM 2
+
+/* Indicates the ISP status */
+enum isp4sd_status {
+ ISP4SD_STATUS_PWR_OFF,
+ ISP4SD_STATUS_PWR_ON,
+ ISP4SD_STATUS_FW_RUNNING,
+ ISP4SD_STATUS_MAX
+};
+
+/* Indicates sensor and output stream status */
+enum isp4sd_start_status {
+ ISP4SD_START_STATUS_OFF,
+ ISP4SD_START_STATUS_STARTED,
+ ISP4SD_START_STATUS_START_FAIL,
+};
+
+struct isp4sd_img_buf_node {
+ struct list_head node;
+ struct isp4if_img_buf_info buf_info;
+};
+
+/* This is ISP output after processing Bayer raw sensor input */
+struct isp4sd_output_info {
+ enum isp4sd_start_status start_status;
+ u32 image_size;
+};
+
+/*
+ * Struct for sensor info used as ISP input or source.
+ * status: sensor status.
+ * output_info: ISP output after processing the sensor input.
+ * start_stream_cmd_sent: indicates if ISP4FW_CMD_ID_START_STREAM was sent
+ * to firmware.
+ * buf_sent_cnt: number of buffers sent to receive images.
+ */
+struct isp4sd_sensor_info {
+ struct isp4sd_output_info output_info;
+ enum isp4sd_start_status status;
+ bool start_stream_cmd_sent;
+ u32 buf_sent_cnt;
+};
+
+/*
+ * The thread is created by the driver to handle firmware responses which will
+ * be waken up when a firmware-to-driver response interrupt occurs.
+ */
+struct isp4sd_thread_handler {
+ struct task_struct *thread;
+ wait_queue_head_t waitq;
+ bool resp_ready;
+};
+
+struct isp4_subdev_thread_param {
+ u32 idx;
+ struct isp4_subdev *isp_subdev;
+};
+
+struct isp4_subdev {
+ struct v4l2_subdev sdev;
+ struct isp4_interface ispif;
+ struct isp4vid_dev isp_vdev;
+
+ struct media_pad sdev_pad;
+
+ enum isp4sd_status isp_status;
+ /* mutex used to synchronize the operation with firmware */
+ struct mutex ops_mutex;
+
+ struct isp4sd_thread_handler
+ fw_resp_thread[ISP4SD_MAX_FW_RESP_STREAM_NUM];
+
+ u32 host2fw_seq_num;
+
+ struct isp4sd_sensor_info sensor_info;
+
+ /* gpio descriptor */
+ struct gpio_desc *enable_gpio;
+ struct device *dev;
+ void __iomem *mmio;
+ struct isp4_subdev_thread_param
+ isp_resp_para[ISP4SD_MAX_FW_RESP_STREAM_NUM];
+ int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM];
+ bool irq_enabled;
+ /* spin lock to access ISP_SYS_INT0_EN exclusively */
+ spinlock_t irq_lock;
+#ifdef CONFIG_DEBUG_FS
+ bool enable_fw_log;
+ struct dentry *debugfs_dir;
+ char *fw_log_output;
+#endif
+};
+
+int isp4sd_init(struct isp4_subdev *isp_subdev, struct v4l2_device *v4l2_dev,
+ int irq[ISP4SD_MAX_FW_RESP_STREAM_NUM]);
+void isp4sd_deinit(struct isp4_subdev *isp_subdev);
+int isp4sd_ioc_send_img_buf(struct v4l2_subdev *sd,
+ struct isp4if_img_buf_info *buf_info);
+int isp4sd_pwron_and_init(struct v4l2_subdev *sd);
+int isp4sd_pwroff_and_deinit(struct v4l2_subdev *sd);
+
+#endif /* _ISP4_SUBDEV_H_ */
diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c
new file mode 100644
index 000000000000..0cebb39f98e1
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_video.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-mc.h>
+#include <media/videobuf2-vmalloc.h>
+
+#include "isp4_interface.h"
+#include "isp4_subdev.h"
+#include "isp4_video.h"
+
+#define ISP4VID_ISP_DRV_NAME "amd_isp_capture"
+#define ISP4VID_MAX_PREVIEW_FPS 30
+#define ISP4VID_DEFAULT_FMT V4L2_PIX_FMT_NV12
+
+#define ISP4VID_PAD_VIDEO_OUTPUT 0
+
+/* time perframe default */
+#define ISP4VID_ISP_TPF_DEFAULT isp4vid_tpfs[0]
+
+static const char *const isp4vid_video_dev_name = "Preview";
+
+/* Sizes must be in increasing order */
+static const struct v4l2_frmsize_discrete isp4vid_frmsize[] = {
+ {640, 360},
+ {640, 480},
+ {1280, 720},
+ {1280, 960},
+ {1920, 1080},
+ {1920, 1440},
+ {2560, 1440},
+ {2880, 1620},
+ {2880, 1624},
+ {2888, 1808},
+};
+
+static const u32 isp4vid_formats[] = {
+ V4L2_PIX_FMT_NV12,
+ V4L2_PIX_FMT_YUYV
+};
+
+/* time perframe list */
+static const struct v4l2_fract isp4vid_tpfs[] = {
+ { 1, ISP4VID_MAX_PREVIEW_FPS }
+};
+
+void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev,
+ const struct isp4if_img_buf_info *img_buf)
+{
+ struct isp4vid_capture_buffer *isp4vid_buf;
+ void *vbuf;
+
+ scoped_guard(mutex, &isp_vdev->buf_list_lock) {
+ isp4vid_buf = list_first_entry_or_null(&isp_vdev->buf_list,
+ typeof(*isp4vid_buf),
+ list);
+ if (!isp4vid_buf)
+ return;
+
+ vbuf = vb2_plane_vaddr(&isp4vid_buf->vb2.vb2_buf, 0);
+
+ if (vbuf != img_buf->planes[0].sys_addr) {
+ dev_err(isp_vdev->dev, "Invalid vbuf\n");
+ return;
+ }
+
+ list_del(&isp4vid_buf->list);
+ }
+
+ /* Fill the buffer */
+ isp4vid_buf->vb2.vb2_buf.timestamp = ktime_get_ns();
+ isp4vid_buf->vb2.sequence = isp_vdev->sequence++;
+ isp4vid_buf->vb2.field = V4L2_FIELD_ANY;
+
+ vb2_set_plane_payload(&isp4vid_buf->vb2.vb2_buf,
+ 0, isp_vdev->format.sizeimage);
+
+ vb2_buffer_done(&isp4vid_buf->vb2.vb2_buf, VB2_BUF_STATE_DONE);
+
+ dev_dbg(isp_vdev->dev, "call vb2_buffer_done(size=%u)\n",
+ isp_vdev->format.sizeimage);
+}
+
+static const struct v4l2_pix_format isp4vid_fmt_default = {
+ .width = 1920,
+ .height = 1080,
+ .pixelformat = ISP4VID_DEFAULT_FMT,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+};
+
+static void isp4vid_capture_return_all_buffers(struct isp4vid_dev *isp_vdev,
+ enum vb2_buffer_state state)
+{
+ struct isp4vid_capture_buffer *vbuf, *node;
+
+ scoped_guard(mutex, &isp_vdev->buf_list_lock) {
+ list_for_each_entry_safe(vbuf, node, &isp_vdev->buf_list, list)
+ vb2_buffer_done(&vbuf->vb2.vb2_buf, state);
+ INIT_LIST_HEAD(&isp_vdev->buf_list);
+ }
+
+ dev_dbg(isp_vdev->dev, "call vb2_buffer_done(%d)\n", state);
+}
+
+static int isp4vid_vdev_link_validate(struct media_link *link)
+{
+ return 0;
+}
+
+static const struct media_entity_operations isp4vid_vdev_ent_ops = {
+ .link_validate = isp4vid_vdev_link_validate,
+};
+
+static const struct v4l2_file_operations isp4vid_vdev_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .read = vb2_fop_read,
+ .poll = vb2_fop_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = vb2_fop_mmap,
+};
+
+static int isp4vid_ioctl_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+{
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+
+ strscpy(cap->driver, ISP4VID_ISP_DRV_NAME, sizeof(cap->driver));
+ snprintf(cap->card, sizeof(cap->card), "%s", ISP4VID_ISP_DRV_NAME);
+ cap->capabilities |= V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
+
+ dev_dbg(isp_vdev->dev, "%s|capabilities=0x%X\n", isp_vdev->vdev.name,
+ cap->capabilities);
+
+ return 0;
+}
+
+static int isp4vid_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+
+ f->fmt.pix = isp_vdev->format;
+
+ return 0;
+}
+
+static int isp4vid_fill_buffer_size(struct v4l2_pix_format *fmt)
+{
+ int ret = 0;
+
+ switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ fmt->bytesperline = fmt->width;
+ fmt->sizeimage = fmt->bytesperline * fmt->height * 3 / 2;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ fmt->bytesperline = fmt->width * 2;
+ fmt->sizeimage = fmt->bytesperline * fmt->height;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int isp4vid_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+ struct v4l2_pix_format *format = &f->fmt.pix;
+ const struct v4l2_frmsize_discrete *fsz;
+ size_t i;
+
+ /*
+ * Check if the hardware supports the requested format, use the default
+ * format otherwise.
+ */
+ for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++)
+ if (isp4vid_formats[i] == format->pixelformat)
+ break;
+
+ if (i == ARRAY_SIZE(isp4vid_formats))
+ format->pixelformat = ISP4VID_DEFAULT_FMT;
+
+ switch (format->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_YUYV:
+ fsz = v4l2_find_nearest_size(isp4vid_frmsize,
+ ARRAY_SIZE(isp4vid_frmsize),
+ width, height, format->width,
+ format->height);
+ format->width = fsz->width;
+ format->height = fsz->height;
+ break;
+ default:
+ dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n",
+ isp_vdev->vdev.name,
+ format->pixelformat);
+ return -EINVAL;
+ }
+
+ /*
+ * There is no need to check the return value, as failure will never
+ * happen here
+ */
+ isp4vid_fill_buffer_size(format);
+
+ if (format->field == V4L2_FIELD_ANY)
+ format->field = isp4vid_fmt_default.field;
+
+ if (format->colorspace == V4L2_COLORSPACE_DEFAULT)
+ format->colorspace = isp4vid_fmt_default.colorspace;
+
+ return 0;
+}
+
+static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev,
+ struct v4l2_pix_format *pix_fmt)
+{
+ struct v4l2_subdev_format fmt = {};
+
+ switch (pix_fmt->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ fmt.format.code = MEDIA_BUS_FMT_YUYV8_1_5X8;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ fmt.format.code = MEDIA_BUS_FMT_YUYV8_1X16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+ fmt.pad = ISP4VID_PAD_VIDEO_OUTPUT;
+ fmt.format.width = pix_fmt->width;
+ fmt.format.height = pix_fmt->height;
+ return v4l2_subdev_call(sdev, pad, set_fmt, NULL, &fmt);
+}
+
+static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+ int ret;
+
+ /* Do not change the format while stream is on */
+ if (vb2_is_busy(&isp_vdev->vbq))
+ return -EBUSY;
+
+ ret = isp4vid_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ dev_dbg(isp_vdev->dev, "%s|width height:%ux%u->%ux%u\n",
+ isp_vdev->vdev.name,
+ isp_vdev->format.width, isp_vdev->format.height,
+ f->fmt.pix.width, f->fmt.pix.height);
+ dev_dbg(isp_vdev->dev, "%s|pixelformat:0x%x-0x%x\n",
+ isp_vdev->vdev.name, isp_vdev->format.pixelformat,
+ f->fmt.pix.pixelformat);
+ dev_dbg(isp_vdev->dev, "%s|bytesperline:%u->%u\n",
+ isp_vdev->vdev.name, isp_vdev->format.bytesperline,
+ f->fmt.pix.bytesperline);
+ dev_dbg(isp_vdev->dev, "%s|sizeimage:%u->%u\n",
+ isp_vdev->vdev.name, isp_vdev->format.sizeimage,
+ f->fmt.pix.sizeimage);
+
+ isp_vdev->format = f->fmt.pix;
+ ret = isp4vid_set_fmt_2_isp(isp_vdev->isp_sdev, &isp_vdev->format);
+
+ return ret;
+}
+
+static int isp4vid_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+
+ switch (f->index) {
+ case 0:
+ f->pixelformat = V4L2_PIX_FMT_NV12;
+ break;
+ case 1:
+ f->pixelformat = V4L2_PIX_FMT_YUYV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(isp_vdev->dev, "%s|index=%d, pixelformat=0x%X\n",
+ isp_vdev->vdev.name, f->index, f->pixelformat);
+
+ return 0;
+}
+
+static int isp4vid_enum_framesizes(struct file *file, void *fh,
+ struct v4l2_frmsizeenum *fsize)
+{
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++) {
+ if (isp4vid_formats[i] == fsize->pixel_format)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(isp4vid_formats))
+ return -EINVAL;
+
+ if (fsize->index < ARRAY_SIZE(isp4vid_frmsize)) {
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete = isp4vid_frmsize[fsize->index];
+ dev_dbg(isp_vdev->dev, "%s|size[%d]=%dx%d\n",
+ isp_vdev->vdev.name, fsize->index,
+ fsize->discrete.width, fsize->discrete.height);
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int isp4vid_ioctl_enum_frameintervals(struct file *file, void *priv,
+ struct v4l2_frmivalenum *fival)
+{
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+ size_t i;
+
+ if (fival->index >= ARRAY_SIZE(isp4vid_tpfs))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(isp4vid_formats); i++)
+ if (isp4vid_formats[i] == fival->pixel_format)
+ break;
+
+ if (i == ARRAY_SIZE(isp4vid_formats))
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(isp4vid_frmsize); i++)
+ if (isp4vid_frmsize[i].width == fival->width &&
+ isp4vid_frmsize[i].height == fival->height)
+ break;
+
+ if (i == ARRAY_SIZE(isp4vid_frmsize))
+ return -EINVAL;
+
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete = isp4vid_tpfs[fival->index];
+ v4l2_simplify_fraction(&fival->discrete.numerator,
+ &fival->discrete.denominator, 8, 333);
+
+ dev_dbg(isp_vdev->dev, "%s|interval[%d]=%d/%d\n",
+ isp_vdev->vdev.name, fival->index,
+ fival->discrete.numerator,
+ fival->discrete.denominator);
+
+ return 0;
+}
+
+static int isp4vid_ioctl_g_param(struct file *file, void *priv,
+ struct v4l2_streamparm *param)
+{
+ struct v4l2_captureparm *capture = &param->parm.capture;
+ struct isp4vid_dev *isp_vdev = video_drvdata(file);
+
+ if (param->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ capture->capability = V4L2_CAP_TIMEPERFRAME;
+ capture->timeperframe = isp_vdev->timeperframe;
+ capture->readbuffers = 0;
+
+ dev_dbg(isp_vdev->dev, "%s|timeperframe=%d/%d\n", isp_vdev->vdev.name,
+ capture->timeperframe.numerator,
+ capture->timeperframe.denominator);
+
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops isp4vid_vdev_ioctl_ops = {
+ .vidioc_querycap = isp4vid_ioctl_querycap,
+ .vidioc_enum_fmt_vid_cap = isp4vid_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = isp4vid_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = isp4vid_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = isp4vid_try_fmt_vid_cap,
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+ .vidioc_g_parm = isp4vid_ioctl_g_param,
+ .vidioc_s_parm = isp4vid_ioctl_g_param,
+ .vidioc_enum_framesizes = isp4vid_enum_framesizes,
+ .vidioc_enum_frameintervals = isp4vid_ioctl_enum_frameintervals,
+};
+
+static unsigned int isp4vid_get_image_size(struct v4l2_pix_format *fmt)
+{
+ switch (fmt->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ return fmt->width * fmt->height * 3 / 2;
+ case V4L2_PIX_FMT_YUYV:
+ return fmt->width * fmt->height * 2;
+ default:
+ return 0;
+ }
+}
+
+static int isp4vid_qops_queue_setup(struct vb2_queue *vq,
+ unsigned int *nbuffers,
+ unsigned int *nplanes, unsigned int sizes[],
+ struct device *alloc_devs[])
+{
+ struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq);
+ unsigned int q_num_bufs = vb2_get_num_buffers(vq);
+
+ if (*nplanes > 1) {
+ dev_err(isp_vdev->dev,
+ "fail to setup queue, no mplane supported %u\n",
+ *nplanes);
+ return -EINVAL;
+ }
+
+ if (*nplanes == 1) {
+ unsigned int size;
+
+ size = isp4vid_get_image_size(&isp_vdev->format);
+ if (sizes[0] < size) {
+ dev_err(isp_vdev->dev,
+ "fail for small plane size %u, %u expected\n",
+ sizes[0], size);
+ return -EINVAL;
+ }
+ }
+
+ if (q_num_bufs + *nbuffers < ISP4IF_MAX_STREAM_BUF_COUNT)
+ *nbuffers = ISP4IF_MAX_STREAM_BUF_COUNT - q_num_bufs;
+
+ switch (isp_vdev->format.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_YUYV: {
+ *nplanes = 1;
+ sizes[0] = max(sizes[0], isp_vdev->format.sizeimage);
+ isp_vdev->format.sizeimage = sizes[0];
+ }
+ break;
+ default:
+ dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n",
+ isp_vdev->vdev.name, isp_vdev->format.pixelformat);
+ return -EINVAL;
+ }
+
+ dev_dbg(isp_vdev->dev, "%s|*nbuffers=%u *nplanes=%u sizes[0]=%u\n",
+ isp_vdev->vdev.name,
+ *nbuffers, *nplanes, sizes[0]);
+
+ return 0;
+}
+
+static void isp4vid_qops_buffer_queue(struct vb2_buffer *vb)
+{
+ struct isp4vid_capture_buffer *buf =
+ container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf);
+ struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue);
+ struct isp4if_img_buf_info *img_buf = &buf->img_buf;
+ void *vaddr = vb2_plane_vaddr(vb, 0);
+
+ dev_dbg(isp_vdev->dev, "queue buf, vaddr %p, gpuva 0x%llx, size %u\n",
+ vaddr, buf->gpu_addr, vb->planes[0].length);
+
+ switch (isp_vdev->format.pixelformat) {
+ case V4L2_PIX_FMT_NV12: {
+ u32 y_size = isp_vdev->format.sizeimage / 3 * 2;
+ u32 uv_size = isp_vdev->format.sizeimage / 3;
+
+ img_buf->planes[0].len = y_size;
+ img_buf->planes[0].sys_addr = vaddr;
+ img_buf->planes[0].mc_addr = buf->gpu_addr;
+
+ dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n",
+ img_buf->planes[0].mc_addr,
+ img_buf->planes[0].len);
+
+ img_buf->planes[1].len = uv_size;
+ img_buf->planes[1].sys_addr = vaddr + y_size;
+ img_buf->planes[1].mc_addr = buf->gpu_addr + y_size;
+
+ dev_dbg(isp_vdev->dev, "img_buf[1]: mc=0x%llx size=%u\n",
+ img_buf->planes[1].mc_addr,
+ img_buf->planes[1].len);
+
+ img_buf->planes[2].len = 0;
+ }
+ break;
+ case V4L2_PIX_FMT_YUYV: {
+ img_buf->planes[0].len = isp_vdev->format.sizeimage;
+ img_buf->planes[0].sys_addr = vaddr;
+ img_buf->planes[0].mc_addr = buf->gpu_addr;
+
+ dev_dbg(isp_vdev->dev, "img_buf[0]: mc=0x%llx size=%u\n",
+ img_buf->planes[0].mc_addr,
+ img_buf->planes[0].len);
+
+ img_buf->planes[1].len = 0;
+ img_buf->planes[2].len = 0;
+ }
+ break;
+ default:
+ dev_err(isp_vdev->dev, "%s|unsupported fmt=%u\n",
+ isp_vdev->vdev.name, isp_vdev->format.pixelformat);
+ return;
+ }
+
+ if (isp_vdev->stream_started)
+ isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev, img_buf);
+
+ scoped_guard(mutex, &isp_vdev->buf_list_lock)
+ list_add_tail(&buf->list, &isp_vdev->buf_list);
+}
+
+static int isp4vid_qops_start_streaming(struct vb2_queue *vq,
+ unsigned int count)
+{
+ struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq);
+ struct isp4vid_capture_buffer *isp4vid_buf;
+ struct media_entity *entity;
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ int ret = 0;
+
+ isp_vdev->sequence = 0;
+
+ ret = isp4sd_pwron_and_init(isp_vdev->isp_sdev);
+ if (ret) {
+ dev_err(isp_vdev->dev, "power up isp fail %d\n", ret);
+ goto release_buffers;
+ }
+
+ entity = &isp_vdev->vdev.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_pad_remote_pad_first(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, 1);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_dbg(isp_vdev->dev, "fail start streaming: %s %d\n",
+ subdev->name, ret);
+ goto release_buffers;
+ }
+ }
+
+ list_for_each_entry(isp4vid_buf, &isp_vdev->buf_list, list)
+ isp4sd_ioc_send_img_buf(isp_vdev->isp_sdev,
+ &isp4vid_buf->img_buf);
+
+ isp_vdev->stream_started = true;
+
+ return 0;
+
+release_buffers:
+ isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_QUEUED);
+ return ret;
+}
+
+static void isp4vid_qops_stop_streaming(struct vb2_queue *vq)
+{
+ struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vq);
+ struct media_entity *entity;
+ struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ int ret;
+
+ entity = &isp_vdev->vdev.entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_pad_remote_pad_first(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ ret = v4l2_subdev_call(subdev, video, s_stream, 0);
+
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ dev_dbg(isp_vdev->dev, "fail stop streaming: %s %d\n",
+ subdev->name, ret);
+ }
+
+ isp_vdev->stream_started = false;
+ isp4sd_pwroff_and_deinit(isp_vdev->isp_sdev);
+
+ /* Release all active buffers */
+ isp4vid_capture_return_all_buffers(isp_vdev, VB2_BUF_STATE_ERROR);
+}
+
+static int isp4vid_qops_buf_init(struct vb2_buffer *vb)
+{
+ struct isp4vid_capture_buffer *buf =
+ container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf);
+ struct isp4vid_dev *isp_vdev = vb2_get_drv_priv(vb->vb2_queue);
+ void *mem_priv = vb->planes[0].mem_priv;
+ struct device *dev = isp_vdev->dev;
+ u64 gpu_addr;
+ void *bo;
+ int ret;
+
+ if (vb->planes[0].dbuf) {
+ buf->dbuf = vb->planes[0].dbuf;
+ } else {
+ /*
+ * HAS_DMA is a Kconfig dependency so CONFIG_HAS_DMA is always
+ * defined when this driver is compiled. The #else branch is
+ * kept as a safeguard in case the dependency is ever removed.
+ */
+#ifdef CONFIG_HAS_DMA
+ buf->dbuf = vb2_vmalloc_memops.get_dmabuf(vb, mem_priv, 0);
+ if (IS_ERR_OR_NULL(buf->dbuf)) {
+ dev_err(dev, "fail to get dma buf\n");
+ return -EINVAL;
+ }
+#else
+ dev_err(dev, "get dmabuf fail -- CONFIG_HAS_DMA not defined\n");
+ buf->dbuf = NULL;
+ return -EINVAL;
+#endif
+ }
+
+ /* create isp user BO and obtain gpu_addr */
+ ret = isp_user_buffer_alloc(dev, buf->dbuf, &bo, &gpu_addr);
+ if (ret) {
+ dev_err(dev, "fail to create isp user BO\n");
+ if (!vb->planes[0].dbuf) {
+ dma_buf_put(buf->dbuf);
+ buf->dbuf = NULL;
+ }
+
+ return ret;
+ }
+
+ buf->bo = bo;
+ buf->gpu_addr = gpu_addr;
+ return 0;
+}
+
+static void isp4vid_qops_buf_cleanup(struct vb2_buffer *vb)
+{
+ struct isp4vid_capture_buffer *buf =
+ container_of(vb, struct isp4vid_capture_buffer, vb2.vb2_buf);
+
+ if (buf->bo) {
+ isp_user_buffer_free(buf->bo);
+ buf->bo = NULL;
+ }
+
+ /*
+ * Only put dmabufs we obtained ourselves via get_dmabuf, not ones
+ * provided by the framework for DMABUF import
+ */
+ if (buf->dbuf && buf->dbuf != vb->planes[0].dbuf)
+ dma_buf_put(buf->dbuf);
+
+ buf->dbuf = NULL;
+}
+
+static const struct vb2_ops isp4vid_qops = {
+ .queue_setup = isp4vid_qops_queue_setup,
+ .buf_init = isp4vid_qops_buf_init,
+ .buf_cleanup = isp4vid_qops_buf_cleanup,
+ .start_streaming = isp4vid_qops_start_streaming,
+ .stop_streaming = isp4vid_qops_stop_streaming,
+ .buf_queue = isp4vid_qops_buffer_queue,
+};
+
+int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd)
+{
+ const char *vdev_name = isp4vid_video_dev_name;
+ struct v4l2_device *v4l2_dev;
+ struct video_device *vdev;
+ struct vb2_queue *q;
+ int ret;
+
+ if (!isp_vdev || !isp_sd || !isp_sd->v4l2_dev)
+ return -EINVAL;
+
+ v4l2_dev = isp_sd->v4l2_dev;
+ vdev = &isp_vdev->vdev;
+
+ isp_vdev->isp_sdev = isp_sd;
+ isp_vdev->dev = v4l2_dev->dev;
+
+ /* Initialize the vb2_queue struct */
+ mutex_init(&isp_vdev->vbq_lock);
+ q = &isp_vdev->vbq;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ q->buf_struct_size = sizeof(struct isp4vid_capture_buffer);
+ q->min_queued_buffers = 2;
+ q->ops = &isp4vid_qops;
+ q->drv_priv = isp_vdev;
+ q->mem_ops = &vb2_vmalloc_memops;
+ q->lock = &isp_vdev->vbq_lock;
+ q->dev = v4l2_dev->dev;
+ ret = vb2_queue_init(q);
+ if (ret) {
+ dev_err(v4l2_dev->dev, "vb2_queue_init error:%d\n", ret);
+ return ret;
+ }
+
+ /* Initialize buffer list and its lock */
+ mutex_init(&isp_vdev->buf_list_lock);
+ INIT_LIST_HEAD(&isp_vdev->buf_list);
+
+ /* Set default frame format */
+ isp_vdev->format = isp4vid_fmt_default;
+ isp_vdev->timeperframe = ISP4VID_ISP_TPF_DEFAULT;
+ v4l2_simplify_fraction(&isp_vdev->timeperframe.numerator,
+ &isp_vdev->timeperframe.denominator, 8, 333);
+
+ ret = isp4vid_fill_buffer_size(&isp_vdev->format);
+ if (ret) {
+ dev_err(v4l2_dev->dev, "fail to fill buffer size: %d\n", ret);
+ goto err_release_vb2_queue;
+ }
+
+ ret = isp4vid_set_fmt_2_isp(isp_sd, &isp_vdev->format);
+ if (ret) {
+ dev_err(v4l2_dev->dev, "fail init format :%d\n", ret);
+ goto err_release_vb2_queue;
+ }
+
+ /* Initialize the video_device struct */
+ isp_vdev->vdev.entity.name = vdev_name;
+ isp_vdev->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
+ isp_vdev->vdev_pad.flags = MEDIA_PAD_FL_SINK;
+ ret = media_entity_pads_init(&isp_vdev->vdev.entity, 1,
+ &isp_vdev->vdev_pad);
+
+ if (ret) {
+ dev_err(v4l2_dev->dev, "init media entity pad fail:%d\n", ret);
+ goto err_release_vb2_queue;
+ }
+
+ vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+ vdev->entity.ops = &isp4vid_vdev_ent_ops;
+ vdev->release = video_device_release_empty;
+ vdev->fops = &isp4vid_vdev_fops;
+ vdev->ioctl_ops = &isp4vid_vdev_ioctl_ops;
+ vdev->lock = NULL;
+ vdev->queue = q;
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->vfl_dir = VFL_DIR_RX;
+ strscpy(vdev->name, vdev_name, sizeof(vdev->name));
+ video_set_drvdata(vdev, isp_vdev);
+
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret) {
+ dev_err(v4l2_dev->dev, "register video device fail:%d\n", ret);
+ goto err_entity_cleanup;
+ }
+
+ return 0;
+
+err_entity_cleanup:
+ media_entity_cleanup(&isp_vdev->vdev.entity);
+err_release_vb2_queue:
+ vb2_queue_release(q);
+ return ret;
+}
+
+void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev)
+{
+ vb2_video_unregister_device(&isp_vdev->vdev);
+}
diff --git a/drivers/media/platform/amd/isp4/isp4_video.h b/drivers/media/platform/amd/isp4/isp4_video.h
new file mode 100644
index 000000000000..c66451e26166
--- /dev/null
+++ b/drivers/media/platform/amd/isp4/isp4_video.h
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) 2025 Advanced Micro Devices, Inc.
+ */
+
+#ifndef _ISP4_VIDEO_H_
+#define _ISP4_VIDEO_H_
+
+#include <media/v4l2-dev.h>
+#include <media/videobuf2-memops.h>
+
+#include "isp4_interface.h"
+
+struct isp4vid_capture_buffer {
+ /*
+ * struct vb2_v4l2_buffer must be the first element
+ * the videobuf2 framework will allocate this struct based on
+ * buf_struct_size and use the first sizeof(struct vb2_buffer) bytes of
+ * memory as a vb2_buffer
+ */
+ struct vb2_v4l2_buffer vb2;
+ struct isp4if_img_buf_info img_buf;
+ struct list_head list;
+ struct dma_buf *dbuf;
+ void *bo;
+ u64 gpu_addr;
+};
+
+struct isp4vid_dev {
+ struct video_device vdev;
+ struct media_pad vdev_pad;
+ struct v4l2_pix_format format;
+
+ /* mutex that protects vbq */
+ struct mutex vbq_lock;
+ struct vb2_queue vbq;
+
+ /* mutex that protects buf_list */
+ struct mutex buf_list_lock;
+ struct list_head buf_list;
+
+ u32 sequence;
+ bool stream_started;
+
+ struct device *dev;
+ struct v4l2_subdev *isp_sdev;
+ struct v4l2_fract timeperframe;
+};
+
+int isp4vid_dev_init(struct isp4vid_dev *isp_vdev, struct v4l2_subdev *isp_sd);
+
+void isp4vid_dev_deinit(struct isp4vid_dev *isp_vdev);
+
+void isp4vid_handle_frame_done(struct isp4vid_dev *isp_vdev,
+ const struct isp4if_img_buf_info *img_buf);
+
+#endif /* _ISP4_VIDEO_H_ */
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
index 6f9ca7a7dd88..aec3eed0e443 100644
--- a/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-params.c
@@ -104,6 +104,8 @@ static void c3_isp_params_awb_wt(struct c3_isp_device *isp,
c3_isp_write(isp, ISP_AWB_BLK_WT_ADDR, 0);
zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+ if (zones_num > C3_ISP_AWB_MAX_ZONES)
+ zones_num = C3_ISP_AWB_MAX_ZONES;
/* Need to write 8 weights at once */
for (i = 0; i < zones_num / 8; i++) {
@@ -220,6 +222,8 @@ static void c3_isp_params_ae_wt(struct c3_isp_device *isp,
c3_isp_write(isp, ISP_AE_BLK_WT_ADDR, 0);
zones_num = cfg->horiz_zones_num * cfg->vert_zones_num;
+ if (zones_num > C3_ISP_AE_MAX_ZONES)
+ zones_num = C3_ISP_AE_MAX_ZONES;
/* Need to write 8 weights at once */
for (i = 0; i < zones_num / 8; i++) {
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-core.c b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
index c1a562cd214e..ee4a4267415e 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-core.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-core.c
@@ -458,7 +458,7 @@ static int mali_c55_media_frameworks_init(struct mali_c55 *mali_c55)
if (ret) {
dev_err(mali_c55->dev, "failed to register V4L2 device\n");
goto err_unregister_media_device;
- };
+ }
mali_c55->notifier.ops = &mali_c55_notifier_ops;
v4l2_async_nf_init(&mali_c55->notifier, &mali_c55->v4l2_dev);
@@ -806,8 +806,10 @@ static int mali_c55_probe(struct platform_device *pdev)
vb2_dma_contig_set_max_seg_size(dev, UINT_MAX);
ret = __mali_c55_power_on(mali_c55);
- if (ret)
- return dev_err_probe(dev, ret, "failed to power on\n");
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to power on\n");
+ goto err_release_mem;
+ }
ret = mali_c55_check_hwcfg(mali_c55);
if (ret)
@@ -826,14 +828,13 @@ static int mali_c55_probe(struct platform_device *pdev)
ret = mali_c55_media_frameworks_init(mali_c55);
if (ret)
- goto err_free_context_registers;
+ goto err_pm_runtime_disable;
pm_runtime_idle(&pdev->dev);
mali_c55->irqnum = platform_get_irq(pdev, 0);
if (mali_c55->irqnum < 0) {
ret = mali_c55->irqnum;
- dev_err(dev, "failed to get interrupt\n");
goto err_deinit_media_frameworks;
}
@@ -841,11 +842,14 @@ static int mali_c55_probe(struct platform_device *pdev)
err_deinit_media_frameworks:
mali_c55_media_frameworks_deinit(mali_c55);
+err_pm_runtime_disable:
+ pm_runtime_set_suspended(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-err_free_context_registers:
kfree(mali_c55->context.registers);
err_power_off:
__mali_c55_power_off(mali_c55);
+err_release_mem:
+ of_reserved_mem_device_release(dev);
return ret;
}
@@ -854,8 +858,14 @@ static void mali_c55_remove(struct platform_device *pdev)
{
struct mali_c55 *mali_c55 = platform_get_drvdata(pdev);
- kfree(mali_c55->context.registers);
mali_c55_media_frameworks_deinit(mali_c55);
+ if (!pm_runtime_suspended(&pdev->dev)) {
+ __mali_c55_power_off(mali_c55);
+ pm_runtime_set_suspended(&pdev->dev);
+ }
+ pm_runtime_disable(&pdev->dev);
+ kfree(mali_c55->context.registers);
+ of_reserved_mem_device_release(&pdev->dev);
}
static const struct of_device_id mali_c55_of_match[] = {
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
index 4c0fd1ec741c..e128adf6ee37 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
@@ -333,6 +333,13 @@ static int mali_c55_isp_enable_streams(struct v4l2_subdev *sd,
sink_pad = &isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO];
isp->remote_src = media_pad_remote_pad_unique(sink_pad);
+ if (IS_ERR(isp->remote_src)) {
+ ret = PTR_ERR(isp->remote_src);
+ dev_err(mali_c55->dev, "Failed to get remote source pad: %d\n", ret);
+ isp->remote_src = NULL;
+ return ret;
+ }
+
src_sd = media_entity_to_v4l2_subdev(isp->remote_src->entity);
isp->frame_sequence = 0;
@@ -583,6 +590,7 @@ int mali_c55_register_isp(struct mali_c55 *mali_c55)
sd->entity.ops = &mali_c55_isp_media_ops;
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_ISP;
sd->internal_ops = &mali_c55_isp_internal_ops;
+ sd->dev = mali_c55->dev;
strscpy(sd->name, MALI_C55_DRIVER_NAME " isp", sizeof(sd->name));
isp->pads[MALI_C55_ISP_PAD_SINK_VIDEO].flags = MEDIA_PAD_FL_SINK |
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
index a8d739af74b6..c4f46651dcee 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
@@ -1070,6 +1070,7 @@ static int mali_c55_register_resizer(struct mali_c55 *mali_c55,
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
sd->internal_ops = &mali_c55_resizer_internal_ops;
+ sd->dev = mali_c55->dev;
rsz->pads[MALI_C55_RSZ_SINK_PAD].flags = MEDIA_PAD_FL_SINK;
rsz->pads[MALI_C55_RSZ_SOURCE_PAD].flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c
index 1af5d2759a83..894f4cf377af 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c
@@ -370,6 +370,7 @@ int mali_c55_register_tpg(struct mali_c55 *mali_c55)
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
sd->internal_ops = &mali_c55_tpg_internal_ops;
+ sd->dev = mali_c55->dev;
strscpy(sd->name, MALI_C55_DRIVER_NAME " tpg", sizeof(sd->name));
pad->flags = MEDIA_PAD_FL_SOURCE;
diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c
index 41cb96f60110..a292275f6b7b 100644
--- a/drivers/media/platform/aspeed/aspeed-video.c
+++ b/drivers/media/platform/aspeed/aspeed-video.c
@@ -2343,6 +2343,7 @@ static int aspeed_video_probe(struct platform_device *pdev)
rc = aspeed_video_setup_video(video);
if (rc) {
aspeed_video_free_buf(video, &video->jpeg);
+ of_reserved_mem_device_release(&pdev->dev);
clk_unprepare(video->vclk);
clk_unprepare(video->eclk);
return rc;
diff --git a/drivers/media/platform/cadence/Kconfig b/drivers/media/platform/cadence/Kconfig
index 1aa608c00dbc..ea85ef82760e 100644
--- a/drivers/media/platform/cadence/Kconfig
+++ b/drivers/media/platform/cadence/Kconfig
@@ -5,6 +5,7 @@ comment "Cadence media platform drivers"
config VIDEO_CADENCE_CSI2RX
tristate "Cadence MIPI-CSI2 RX Controller"
depends on VIDEO_DEV
+ depends on PM
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index cde690c6fdee..1ff2d8f78d5b 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -125,12 +125,6 @@ struct csi2rx_priv {
unsigned int count;
int error_irq;
- /*
- * Used to prevent race conditions between multiple,
- * concurrent calls to start and stop.
- */
- struct mutex lock;
-
void __iomem *base;
struct clk *sys_clk;
struct clk *p_clk;
@@ -141,6 +135,7 @@ struct csi2rx_priv {
struct phy *dphy;
u8 num_pixels[CSI2RX_STREAMS_MAX];
+ u32 vc_select[CSI2RX_STREAMS_MAX];
u8 lanes[CSI2RX_LANES_MAX];
u8 num_lanes;
u8 max_lanes;
@@ -235,6 +230,21 @@ static const struct csi2rx_fmt *csi2rx_get_fmt_by_code(u32 code)
return NULL;
}
+static int csi2rx_get_frame_desc_from_source(struct csi2rx_priv *csi2rx,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct media_pad *remote_pad;
+
+ remote_pad = media_entity_remote_source_pad_unique(&csi2rx->subdev.entity);
+ if (IS_ERR(remote_pad)) {
+ dev_err(csi2rx->dev, "No remote pad found for sink\n");
+ return PTR_ERR(remote_pad);
+ }
+
+ return v4l2_subdev_call(csi2rx->source_subdev, pad, get_frame_desc,
+ remote_pad->index, fd);
+}
+
static inline
struct csi2rx_priv *v4l2_subdev_to_csi2rx(struct v4l2_subdev *subdev)
{
@@ -264,29 +274,46 @@ static void csi2rx_reset(struct csi2rx_priv *csi2rx)
static int csi2rx_configure_ext_dphy(struct csi2rx_priv *csi2rx)
{
- struct media_pad *src_pad =
- &csi2rx->source_subdev->entity.pads[csi2rx->source_pad];
union phy_configure_opts opts = { };
struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
- struct v4l2_subdev_format sd_fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .pad = CSI2RX_PAD_SINK,
- };
+ struct v4l2_mbus_framefmt *framefmt;
+ struct v4l2_subdev_state *state;
const struct csi2rx_fmt *fmt;
+ struct v4l2_subdev_route *route;
+ int source_pad = csi2rx->source_pad;
+ struct media_pad *pad = &csi2rx->source_subdev->entity.pads[source_pad];
s64 link_freq;
int ret;
+ u32 bpp;
- ret = v4l2_subdev_call_state_active(&csi2rx->subdev, pad, get_fmt,
- &sd_fmt);
- if (ret < 0)
- return ret;
+ state = v4l2_subdev_get_locked_active_state(&csi2rx->subdev);
+
+ /*
+ * For multi-stream transmitters there is no single pixel rate.
+ *
+ * In multistream usecase pass bpp as 0 so that v4l2_get_link_freq()
+ * returns an error if it falls back to V4L2_CID_PIXEL_RATE.
+ */
+ if (state->routing.num_routes > 1) {
+ bpp = 0;
+ } else {
+ route = &state->routing.routes[0];
+ framefmt = v4l2_subdev_state_get_format(state, CSI2RX_PAD_SINK,
+ route->sink_stream);
+ if (!framefmt) {
+ dev_err(csi2rx->dev, "Did not find active sink format\n");
+ return -EINVAL;
+ }
- fmt = csi2rx_get_fmt_by_code(sd_fmt.format.code);
+ fmt = csi2rx_get_fmt_by_code(framefmt->code);
+ bpp = fmt->bpp;
+ }
- link_freq = v4l2_get_link_freq(src_pad,
- fmt->bpp, 2 * csi2rx->num_lanes);
- if (link_freq < 0)
+ link_freq = v4l2_get_link_freq(pad, bpp, 2 * csi2rx->num_lanes);
+ if (link_freq < 0) {
+ dev_err(csi2rx->dev, "Unable to calculate link frequency\n");
return link_freq;
+ }
ret = phy_mipi_dphy_get_default_config_for_hsclk(link_freq,
csi2rx->num_lanes, cfg);
@@ -313,11 +340,6 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
u32 reg;
int ret;
- ret = clk_prepare_enable(csi2rx->p_clk);
- if (ret)
- return ret;
-
- reset_control_deassert(csi2rx->p_rst);
csi2rx_reset(csi2rx);
if (csi2rx->error_irq >= 0)
@@ -358,7 +380,7 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
if (ret) {
dev_err(csi2rx->dev,
"Failed to configure external DPHY: %d\n", ret);
- goto err_disable_pclk;
+ return ret;
}
}
@@ -373,58 +395,20 @@ static int csi2rx_start(struct csi2rx_priv *csi2rx)
* hence the reference counting.
*/
for (i = 0; i < csi2rx->max_streams; i++) {
- ret = clk_prepare_enable(csi2rx->pixel_clk[i]);
- if (ret)
- goto err_disable_pixclk;
-
- reset_control_deassert(csi2rx->pixel_rst[i]);
-
writel(CSI2RX_STREAM_CFG_FIFO_MODE_LARGE_BUF |
FIELD_PREP(CSI2RX_STREAM_CFG_NUM_PIXELS_MASK,
csi2rx->num_pixels[i]),
csi2rx->base + CSI2RX_STREAM_CFG_REG(i));
- /*
- * Enable one virtual channel. When multiple virtual channels
- * are supported this will have to be changed.
- */
- writel(CSI2RX_STREAM_DATA_CFG_VC_SELECT(0),
+ writel(csi2rx->vc_select[i],
csi2rx->base + CSI2RX_STREAM_DATA_CFG_REG(i));
writel(CSI2RX_STREAM_CTRL_START,
csi2rx->base + CSI2RX_STREAM_CTRL_REG(i));
}
- ret = clk_prepare_enable(csi2rx->sys_clk);
- if (ret)
- goto err_disable_pixclk;
-
- reset_control_deassert(csi2rx->sys_rst);
-
- ret = v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, true);
- if (ret)
- goto err_disable_sysclk;
-
- clk_disable_unprepare(csi2rx->p_clk);
return 0;
-
-err_disable_sysclk:
- clk_disable_unprepare(csi2rx->sys_clk);
-err_disable_pixclk:
- for (; i > 0; i--) {
- reset_control_assert(csi2rx->pixel_rst[i - 1]);
- clk_disable_unprepare(csi2rx->pixel_clk[i - 1]);
- }
-
- if (csi2rx->dphy) {
- writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
- phy_power_off(csi2rx->dphy);
- }
-err_disable_pclk:
- clk_disable_unprepare(csi2rx->p_clk);
-
- return ret;
}
static void csi2rx_stop(struct csi2rx_priv *csi2rx)
@@ -433,10 +417,6 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
u32 val;
int ret;
- clk_prepare_enable(csi2rx->p_clk);
- 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++) {
@@ -451,17 +431,8 @@ static void csi2rx_stop(struct csi2rx_priv *csi2rx)
if (ret)
dev_warn(csi2rx->dev,
"Failed to stop streaming on pad%u\n", i);
-
- reset_control_assert(csi2rx->pixel_rst[i]);
- clk_disable_unprepare(csi2rx->pixel_clk[i]);
}
- reset_control_assert(csi2rx->p_rst);
- clk_disable_unprepare(csi2rx->p_clk);
-
- if (v4l2_subdev_call(csi2rx->source_subdev, video, s_stream, false))
- dev_warn(csi2rx->dev, "Couldn't disable our subdev\n");
-
if (csi2rx->dphy) {
writel(0, csi2rx->base + CSI2RX_DPHY_LANE_CTRL_REG);
@@ -485,40 +456,117 @@ static int csi2rx_log_status(struct v4l2_subdev *sd)
return 0;
}
-static int csi2rx_s_stream(struct v4l2_subdev *subdev, int enable)
+static void csi2rx_update_vc_select(struct csi2rx_priv *csi2rx,
+ struct v4l2_subdev_state *state)
{
- struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
- int ret = 0;
+ struct v4l2_mbus_frame_desc fd = {0};
+ struct v4l2_subdev_route *route;
+ unsigned int i;
+ int ret;
+
+ ret = csi2rx_get_frame_desc_from_source(csi2rx, &fd);
+ if (ret || fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ dev_dbg(csi2rx->dev,
+ "Failed to get source frame desc, allowing only VC=0\n");
+ for (i = 0; i < CSI2RX_STREAMS_MAX; i++)
+ csi2rx->vc_select[i] = CSI2RX_STREAM_DATA_CFG_VC_SELECT(0);
+ return;
+ }
+
+ /* If source provides per-stream VC info, use it to filter by VC */
+ memset(csi2rx->vc_select, 0, sizeof(csi2rx->vc_select));
+
+ for_each_active_route(&state->routing, route) {
+ u32 cdns_stream = route->source_pad - CSI2RX_PAD_SOURCE_STREAM0;
+
+ for (i = 0; i < fd.num_entries; i++) {
+ if (fd.entry[i].stream != route->sink_stream)
+ continue;
- mutex_lock(&csi2rx->lock);
-
- if (enable) {
- /*
- * If we're not the first users, there's no need to
- * enable the whole controller.
- */
- if (!csi2rx->count) {
- ret = csi2rx_start(csi2rx);
- if (ret)
- goto out;
+ csi2rx->vc_select[cdns_stream] |=
+ CSI2RX_STREAM_DATA_CFG_VC_SELECT(fd.entry[i].bus.csi2.vc);
}
+ }
+}
- csi2rx->count++;
- } else {
- csi2rx->count--;
+static int csi2rx_enable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+ u64 sink_streams;
+ int ret;
- /*
- * Let the last user turn off the lights.
- */
- if (!csi2rx->count)
- csi2rx_stop(csi2rx);
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ CSI2RX_PAD_SINK,
+ &streams_mask);
+
+ /*
+ * If we're not the first users, there's no need to
+ * enable the whole controller.
+ */
+ if (!csi2rx->count) {
+ ret = pm_runtime_resume_and_get(csi2rx->dev);
+ if (ret < 0)
+ goto err;
+
+ csi2rx_update_vc_select(csi2rx, state);
+
+ ret = csi2rx_start(csi2rx);
+ if (ret)
+ goto err_put_pm;
}
-out:
- mutex_unlock(&csi2rx->lock);
+ /* Start streaming on the source */
+ ret = v4l2_subdev_enable_streams(csi2rx->source_subdev, csi2rx->source_pad,
+ sink_streams);
+ if (ret) {
+ dev_err(csi2rx->dev,
+ "Failed to start streams %#llx on subdev\n",
+ sink_streams);
+ goto err_stop_csi;
+ }
+
+ csi2rx->count++;
+ return 0;
+
+err_stop_csi:
+ if (!csi2rx->count)
+ csi2rx_stop(csi2rx);
+err_put_pm:
+ if (!csi2rx->count)
+ pm_runtime_put(csi2rx->dev);
+err:
return ret;
}
+static int csi2rx_disable_streams(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+ u64 sink_streams;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ CSI2RX_PAD_SINK,
+ &streams_mask);
+
+ if (v4l2_subdev_disable_streams(csi2rx->source_subdev,
+ csi2rx->source_pad, sink_streams)) {
+ dev_err(csi2rx->dev, "Couldn't disable our subdev\n");
+ }
+
+ csi2rx->count--;
+
+ /* Let the last user turn off the lights. */
+ if (!csi2rx->count) {
+ csi2rx_stop(csi2rx);
+ pm_runtime_put(csi2rx->dev);
+ }
+
+ return 0;
+}
+
static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum *code_enum)
@@ -531,12 +579,53 @@ static int csi2rx_enum_mbus_code(struct v4l2_subdev *subdev,
return 0;
}
+static int _csi2rx_set_routing(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ static const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(subdev, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ return v4l2_subdev_set_routing_with_fmt(subdev, state, routing, &format);
+}
+
+static int csi2rx_set_routing(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
+ int ret;
+
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE && csi2rx->count)
+ return -EBUSY;
+
+ ret = _csi2rx_set_routing(subdev, state, routing);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
static int csi2rx_set_fmt(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt;
- unsigned int i;
/* No transcoding, source and sink formats must match. */
if (format->pad != CSI2RX_PAD_SINK)
@@ -548,14 +637,16 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev,
format->format.field = V4L2_FIELD_NONE;
/* Set sink format */
- fmt = v4l2_subdev_state_get_format(state, format->pad);
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
*fmt = format->format;
- /* Propagate to source formats */
- for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++) {
- fmt = v4l2_subdev_state_get_format(state, i);
- *fmt = format->format;
- }
+ /* Propagate to source format */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
return 0;
}
@@ -563,21 +654,22 @@ static int csi2rx_set_fmt(struct v4l2_subdev *subdev,
static int csi2rx_init_state(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state)
{
- struct v4l2_subdev_format format = {
- .pad = CSI2RX_PAD_SINK,
- .format = {
- .width = 640,
- .height = 480,
- .code = MEDIA_BUS_FMT_UYVY8_1X16,
- .field = V4L2_FIELD_NONE,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .ycbcr_enc = V4L2_YCBCR_ENC_601,
- .quantization = V4L2_QUANTIZATION_LIM_RANGE,
- .xfer_func = V4L2_XFER_FUNC_SRGB,
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = CSI2RX_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = CSI2RX_PAD_SOURCE_STREAM0,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
},
};
- return csi2rx_set_fmt(subdev, state, &format);
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return _csi2rx_set_routing(subdev, state, &routing);
}
int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad,
@@ -585,36 +677,55 @@ int cdns_csi2rx_negotiate_ppc(struct v4l2_subdev *subdev, unsigned int pad,
{
struct csi2rx_priv *csi2rx = v4l2_subdev_to_csi2rx(subdev);
const struct csi2rx_fmt *csi_fmt;
+ struct v4l2_subdev_route *route;
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
if (!ppc || pad < CSI2RX_PAD_SOURCE_STREAM0 || pad >= CSI2RX_PAD_MAX)
return -EINVAL;
state = v4l2_subdev_lock_and_get_active_state(subdev);
- fmt = v4l2_subdev_state_get_format(state, pad);
- csi_fmt = csi2rx_get_fmt_by_code(fmt->code);
+ /* Check all streams on requested pad */
+ for_each_active_route(&state->routing, route) {
+ if (route->source_pad != pad)
+ continue;
+
+ fmt = v4l2_subdev_state_get_format(state, route->source_pad,
+ route->source_stream);
+ if (!fmt) {
+ ret = -EPIPE;
+ *ppc = 1;
+ break;
+ }
- /* Reduce requested PPC if it is too high */
- *ppc = min(*ppc, csi_fmt->max_pixels);
+ csi_fmt = csi2rx_get_fmt_by_code(fmt->code);
+ if (!csi_fmt) {
+ ret = -EINVAL;
+ *ppc = 1;
+ break;
+ }
+ /* Reduce requested PPC if it is too high for this stream */
+ *ppc = min(*ppc, csi_fmt->max_pixels);
+ }
v4l2_subdev_unlock_state(state);
csi2rx->num_pixels[pad - CSI2RX_PAD_SOURCE_STREAM0] =
CSI2RX_STREAM_CFG_NUM_PIXELS(*ppc);
- return 0;
+ return ret;
}
EXPORT_SYMBOL_FOR_MODULES(cdns_csi2rx_negotiate_ppc, "j721e-csi2rx");
static const struct v4l2_subdev_pad_ops csi2rx_pad_ops = {
- .enum_mbus_code = csi2rx_enum_mbus_code,
- .get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = csi2rx_set_fmt,
-};
-
-static const struct v4l2_subdev_video_ops csi2rx_video_ops = {
- .s_stream = csi2rx_s_stream,
+ .enum_mbus_code = csi2rx_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = csi2rx_set_fmt,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
+ .set_routing = csi2rx_set_routing,
+ .enable_streams = csi2rx_enable_streams,
+ .disable_streams = csi2rx_disable_streams,
};
static const struct v4l2_subdev_core_ops csi2rx_core_ops = {
@@ -623,7 +734,6 @@ static const struct v4l2_subdev_core_ops csi2rx_core_ops = {
static const struct v4l2_subdev_ops csi2rx_subdev_ops = {
.core = &csi2rx_core_ops,
- .video = &csi2rx_video_ops,
.pad = &csi2rx_pad_ops,
};
@@ -634,6 +744,7 @@ static const struct v4l2_subdev_internal_ops csi2rx_internal_ops = {
static const struct media_entity_operations csi2rx_media_ops = {
.link_validate = v4l2_subdev_link_validate,
.get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
};
static int csi2rx_async_bound(struct v4l2_async_notifier *notifier,
@@ -829,7 +940,6 @@ static int csi2rx_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, csi2rx);
csi2rx->dev = &pdev->dev;
- mutex_init(&csi2rx->lock);
ret = csi2rx_get_resources(csi2rx, pdev);
if (ret)
@@ -852,7 +962,8 @@ static int csi2rx_probe(struct platform_device *pdev)
csi2rx->pads[CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
for (i = CSI2RX_PAD_SOURCE_STREAM0; i < CSI2RX_PAD_MAX; i++)
csi2rx->pads[i].flags = MEDIA_PAD_FL_SOURCE;
- csi2rx->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ csi2rx->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_STREAMS;
csi2rx->subdev.entity.ops = &csi2rx_media_ops;
ret = media_entity_pads_init(&csi2rx->subdev.entity, CSI2RX_PAD_MAX,
@@ -879,6 +990,7 @@ static int csi2rx_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup;
+ pm_runtime_enable(csi2rx->dev);
ret = v4l2_async_register_subdev(&csi2rx->subdev);
if (ret < 0)
goto err_free_state;
@@ -893,6 +1005,7 @@ static int csi2rx_probe(struct platform_device *pdev)
err_free_state:
v4l2_subdev_cleanup(&csi2rx->subdev);
+ pm_runtime_disable(csi2rx->dev);
err_cleanup:
v4l2_async_nf_unregister(&csi2rx->notifier);
v4l2_async_nf_cleanup(&csi2rx->notifier);
@@ -911,9 +1024,72 @@ static void csi2rx_remove(struct platform_device *pdev)
v4l2_async_unregister_subdev(&csi2rx->subdev);
v4l2_subdev_cleanup(&csi2rx->subdev);
media_entity_cleanup(&csi2rx->subdev.entity);
+ pm_runtime_disable(csi2rx->dev);
kfree(csi2rx);
}
+static int csi2rx_runtime_suspend(struct device *dev)
+{
+ struct csi2rx_priv *csi2rx = dev_get_drvdata(dev);
+
+ reset_control_assert(csi2rx->sys_rst);
+ clk_disable_unprepare(csi2rx->sys_clk);
+
+ for (unsigned int i = 0; i < csi2rx->max_streams; i++) {
+ reset_control_assert(csi2rx->pixel_rst[i]);
+ clk_disable_unprepare(csi2rx->pixel_clk[i]);
+ }
+
+ reset_control_assert(csi2rx->p_rst);
+ clk_disable_unprepare(csi2rx->p_clk);
+
+ return 0;
+}
+
+static int csi2rx_runtime_resume(struct device *dev)
+{
+ struct csi2rx_priv *csi2rx = dev_get_drvdata(dev);
+ unsigned int i;
+ int ret;
+
+ ret = clk_prepare_enable(csi2rx->p_clk);
+ if (ret)
+ return ret;
+
+ reset_control_deassert(csi2rx->p_rst);
+
+ for (i = 0; i < csi2rx->max_streams; i++) {
+ ret = clk_prepare_enable(csi2rx->pixel_clk[i]);
+ if (ret)
+ goto err_disable_pixclk;
+
+ reset_control_deassert(csi2rx->pixel_rst[i]);
+ }
+
+ ret = clk_prepare_enable(csi2rx->sys_clk);
+ if (ret)
+ goto err_disable_pixclk;
+
+ reset_control_deassert(csi2rx->sys_rst);
+
+ return 0;
+
+err_disable_pixclk:
+ while (i--) {
+ reset_control_assert(csi2rx->pixel_rst[i]);
+ clk_disable_unprepare(csi2rx->pixel_clk[i]);
+ }
+
+ reset_control_assert(csi2rx->p_rst);
+ clk_disable_unprepare(csi2rx->p_clk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops csi2rx_pm_ops = {
+ RUNTIME_PM_OPS(csi2rx_runtime_suspend, csi2rx_runtime_resume, NULL)
+};
+
static const struct of_device_id csi2rx_of_table[] = {
{ .compatible = "starfive,jh7110-csi2rx" },
{ .compatible = "cdns,csi2rx" },
@@ -928,6 +1104,7 @@ static struct platform_driver csi2rx_driver = {
.driver = {
.name = "cdns-csi2rx",
.of_match_table = csi2rx_of_table,
+ .pm = &csi2rx_pm_ops,
},
};
module_platform_driver(csi2rx_driver);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.c b/drivers/media/platform/chips-media/wave5/wave5-helper.c
index 53a0ac068c2e..c3d34be833ff 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-helper.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.c
@@ -68,7 +68,6 @@ int wave5_vpu_release_device(struct file *filp,
int ret = 0;
unsigned long flags;
- v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
/*
* To prevent Null reference exception, the existing irq handler were
* separated to two modules.
@@ -89,6 +88,9 @@ int wave5_vpu_release_device(struct file *filp,
list_del_init(&inst->list);
spin_unlock_irqrestore(&inst->dev->irq_spinlock, flags);
mutex_unlock(&inst->dev->irq_lock);
+
+ v4l2_m2m_ctx_release(inst->v4l2_fh.m2m_ctx);
+
if (inst->state != VPU_INST_STATE_NONE) {
u32 fail_res;
diff --git a/drivers/media/platform/chips-media/wave5/wave5-helper.h b/drivers/media/platform/chips-media/wave5/wave5-helper.h
index d61fdbda359d..e6f241012c3b 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-helper.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-helper.h
@@ -11,7 +11,7 @@
#include "wave5-vpu.h"
#define FMT_TYPES 2
-#define MAX_FMTS 12
+#define MAX_FMTS 16
const char *state_to_str(enum vpu_instance_state state);
void wave5_cleanup_instance(struct vpu_instance *inst, struct file *filp);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-hw.c b/drivers/media/platform/chips-media/wave5/wave5-hw.c
index 687ce6ccf3ae..2392bce8d840 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-hw.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-hw.c
@@ -49,6 +49,7 @@
#define FASTIO_ADDRESS_MASK GENMASK(15, 0)
#define SEQ_PARAM_PROFILE_MASK GENMASK(30, 24)
+#define SEQ_BG_PARAM_REG_DATA 0x3800410
static void _wave5_print_reg_err(struct vpu_device *vpu_dev, u32 reg_fail_reason,
const char *func);
@@ -1762,6 +1763,9 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst)
(p_param->skip_intra_trans << 25) |
(p_param->strong_intra_smooth_enable << 27) |
(p_param->en_still_picture << 30);
+ else if (inst->std == W_AVC_ENC)
+ reg_val |= (p_param->constraint_set1_flag << 29);
+
vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_SPS_PARAM, reg_val);
reg_val = (p_param->lossless_enable) |
@@ -1838,7 +1842,8 @@ int wave5_vpu_enc_init_seq(struct vpu_instance *inst)
vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_RC_BIT_RATIO_LAYER_4_7, 0);
vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_ROT_PARAM, rot_mir_mode);
- vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM, 0);
+ vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_BG_PARAM,
+ SEQ_BG_PARAM_REG_DATA | p_param->bg_detection);
vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CUSTOM_LAMBDA_ADDR, 0);
vpu_write_reg(inst->dev, W5_CMD_ENC_SEQ_CONF_WIN_TOP_BOT,
p_param->conf_win_bot << 16 | p_param->conf_win_top);
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
index d419076d7052..bb2ba9204a83 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-dec.c
@@ -283,10 +283,23 @@ static void send_eos_event(struct vpu_instance *inst)
inst->sent_eos = true;
}
+static void wave5_update_min_bufs_ctrl(struct vpu_instance *inst, u32 fbc_buf_count)
+{
+ struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
+ struct v4l2_ctrl *ctrl;
+
+ if (!fbc_buf_count || fbc_buf_count == v4l2_m2m_num_dst_bufs_ready(m2m_ctx))
+ return;
+
+ ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
+ V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
+ if (ctrl)
+ v4l2_ctrl_s_ctrl(ctrl, fbc_buf_count);
+}
+
static int handle_dynamic_resolution_change(struct vpu_instance *inst)
{
struct v4l2_fh *fh = &inst->v4l2_fh;
- struct v4l2_m2m_ctx *m2m_ctx = inst->v4l2_fh.m2m_ctx;
static const struct v4l2_event vpu_event_src_ch = {
.type = V4L2_EVENT_SOURCE_CHANGE,
@@ -305,14 +318,6 @@ static int handle_dynamic_resolution_change(struct vpu_instance *inst)
inst->needs_reallocation = true;
inst->fbc_buf_count = initial_info->min_frame_buffer_count + 1;
- if (inst->fbc_buf_count != v4l2_m2m_num_dst_bufs_ready(m2m_ctx)) {
- struct v4l2_ctrl *ctrl;
-
- ctrl = v4l2_ctrl_find(&inst->v4l2_ctrl_hdl,
- V4L2_CID_MIN_BUFFERS_FOR_CAPTURE);
- if (ctrl)
- v4l2_ctrl_s_ctrl(ctrl, inst->fbc_buf_count);
- }
if (p_dec_info->initial_info_obtained) {
const struct vpu_format *vpu_fmt;
@@ -439,19 +444,24 @@ static void wave5_vpu_dec_finish_decode(struct vpu_instance *inst)
if ((dec_info.index_frame_display == DISPLAY_IDX_FLAG_SEQ_END ||
dec_info.sequence_changed)) {
unsigned long flags;
+ u32 fbc_buf_count = 0;
spin_lock_irqsave(&inst->state_spinlock, flags);
if (!v4l2_m2m_has_stopped(m2m_ctx)) {
switch_state(inst, VPU_INST_STATE_STOP);
- if (dec_info.sequence_changed)
+ if (dec_info.sequence_changed) {
handle_dynamic_resolution_change(inst);
- else
+ fbc_buf_count = inst->fbc_buf_count;
+ } else {
send_eos_event(inst);
+ }
flag_last_buffer_done(inst);
}
spin_unlock_irqrestore(&inst->state_spinlock, flags);
+
+ wave5_update_min_bufs_ctrl(inst, fbc_buf_count);
}
if (inst->sent_eos &&
@@ -1592,8 +1602,9 @@ static const struct vpu_instance_ops wave5_vpu_dec_inst_ops = {
static int initialize_sequence(struct vpu_instance *inst)
{
struct dec_initial_info initial_info;
- int ret = 0;
unsigned long flags;
+ u32 fbc_buf_count;
+ int ret = 0;
memset(&initial_info, 0, sizeof(struct dec_initial_info));
@@ -1617,8 +1628,11 @@ static int initialize_sequence(struct vpu_instance *inst)
spin_lock_irqsave(&inst->state_spinlock, flags);
handle_dynamic_resolution_change(inst);
+ fbc_buf_count = inst->fbc_buf_count;
spin_unlock_irqrestore(&inst->state_spinlock, flags);
+ wave5_update_min_bufs_ctrl(inst, fbc_buf_count);
+
return 0;
}
@@ -1659,6 +1673,7 @@ static void wave5_vpu_dec_device_run(void *priv)
ret = initialize_sequence(inst);
if (ret) {
unsigned long flags;
+ u32 fbc_buf_count = 0;
spin_lock_irqsave(&inst->state_spinlock, flags);
if (wave5_is_draining_or_eos(inst) &&
@@ -1667,14 +1682,18 @@ static void wave5_vpu_dec_device_run(void *priv)
switch_state(inst, VPU_INST_STATE_STOP);
- if (vb2_is_streaming(dst_vq))
+ if (vb2_is_streaming(dst_vq)) {
send_eos_event(inst);
- else
+ } else {
handle_dynamic_resolution_change(inst);
+ fbc_buf_count = inst->fbc_buf_count;
+ }
flag_last_buffer_done(inst);
}
spin_unlock_irqrestore(&inst->state_spinlock, flags);
+
+ wave5_update_min_bufs_ctrl(inst, fbc_buf_count);
} else {
set_instance_state(inst, VPU_INST_STATE_INIT_SEQ);
}
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
index 7613fcdbafed..e6c94b6f2671 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpu-enc.c
@@ -90,6 +90,22 @@ static const struct vpu_format enc_fmt_list[FMT_TYPES][MAX_FMTS] = {
.v4l2_pix_fmt = V4L2_PIX_FMT_NV61M,
.v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
},
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YUYV,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_YVYU,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_UYVY,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
+ {
+ .v4l2_pix_fmt = V4L2_PIX_FMT_VYUY,
+ .v4l2_frmsize = &enc_frmsize[VPU_FMT_TYPE_RAW],
+ },
}
};
@@ -226,13 +242,6 @@ static int start_encode(struct vpu_instance *inst, u32 *fail_res)
} else {
dev_dbg(inst->dev->dev, "%s: wave5_vpu_enc_start_one_frame success\n",
__func__);
- /*
- * Remove the source buffer from the ready-queue now and finish
- * it in the videobuf2 framework once the index is returned by the
- * firmware in finish_encode
- */
- if (src_buf)
- v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, src_buf->vb2_buf.index);
}
return 0;
@@ -259,27 +268,13 @@ static void wave5_vpu_enc_finish_encode(struct vpu_instance *inst)
__func__, enc_output_info.pic_type, enc_output_info.recon_frame_index,
enc_output_info.enc_src_idx, enc_output_info.enc_pic_byte, enc_output_info.pts);
- /*
- * The source buffer will not be found in the ready-queue as it has been
- * dropped after sending of the encode firmware command, locate it in
- * the videobuf2 queue directly
- */
if (enc_output_info.enc_src_idx >= 0) {
- struct vb2_buffer *vb = vb2_get_buffer(v4l2_m2m_get_src_vq(m2m_ctx),
- enc_output_info.enc_src_idx);
- if (vb->state != VB2_BUF_STATE_ACTIVE)
- dev_warn(inst->dev->dev,
- "%s: encoded buffer (%d) was not in ready queue %i.",
- __func__, enc_output_info.enc_src_idx, vb->state);
- else
- src_buf = to_vb2_v4l2_buffer(vb);
-
- if (src_buf) {
+ src_buf = v4l2_m2m_src_buf_remove_by_idx(m2m_ctx, enc_output_info.enc_src_idx);
+ if (!src_buf) {
+ dev_warn(inst->dev->dev, "%s: no source buffer found\n", __func__);
+ } else {
inst->timestamp = src_buf->vb2_buf.timestamp;
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
- } else {
- dev_warn(inst->dev->dev, "%s: no source buffer with index: %d found\n",
- __func__, enc_output_info.enc_src_idx);
}
}
@@ -780,6 +775,9 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_BITRATE:
inst->bit_rate = ctrl->val;
break;
+ case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION:
+ inst->enc_param.bg_detection = ctrl->val;
+ break;
case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
inst->enc_param.avc_idr_period = ctrl->val;
break;
@@ -936,6 +934,8 @@ static int wave5_vpu_enc_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
inst->enc_param.profile = H264_PROFILE_BP;
inst->bit_depth = 8;
+ if (ctrl->val == V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE)
+ inst->enc_param.constraint_set1_flag = 1;
break;
case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
inst->enc_param.profile = H264_PROFILE_MP;
@@ -1156,6 +1156,22 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param,
else
open_param->src_format = FORMAT_420;
+ switch (info->format) {
+ case V4L2_PIX_FMT_YUYV:
+ open_param->packed_format = PACKED_YUYV;
+ break;
+ case V4L2_PIX_FMT_YVYU:
+ open_param->packed_format = PACKED_YVYU;
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ open_param->packed_format = PACKED_UYVY;
+ break;
+ case V4L2_PIX_FMT_VYUY:
+ open_param->packed_format = PACKED_VYUY;
+ break;
+ default:
+ break;
+ }
open_param->wave_param.gop_preset_idx = PRESET_IDX_IPP_SINGLE;
open_param->wave_param.hvs_qp_scale = 2;
open_param->wave_param.hvs_max_delta_qp = 10;
@@ -1205,12 +1221,14 @@ static int wave5_set_enc_openparam(struct enc_open_param *open_param,
open_param->wave_param.beta_offset_div2 = input.beta_offset_div2;
open_param->wave_param.decoding_refresh_type = input.decoding_refresh_type;
open_param->wave_param.intra_period = input.intra_period;
+ open_param->wave_param.bg_detection = input.bg_detection;
if (inst->std == W_HEVC_ENC) {
if (input.intra_period == 0) {
open_param->wave_param.decoding_refresh_type = DEC_REFRESH_TYPE_IDR;
open_param->wave_param.intra_period = input.avc_idr_period;
}
} else {
+ open_param->wave_param.constraint_set1_flag = input.constraint_set1_flag;
open_param->wave_param.avc_idr_period = input.avc_idr_period;
}
open_param->wave_param.entropy_coding_mode = input.entropy_coding_mode;
@@ -1683,7 +1701,7 @@ static int wave5_vpu_open_enc(struct file *filp)
-6, 6, 1, 0);
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM,
- 0, 1, 1, 1);
+ 0, 1, 1, 0);
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_CONSTRAINED_INTRA_PREDICTION,
0, 1, 1, 0);
@@ -1701,6 +1719,9 @@ static int wave5_vpu_open_enc(struct file *filp)
V4L2_CID_MPEG_VIDEO_AU_DELIMITER,
0, 1, 1, 1);
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
V4L2_CID_HFLIP,
0, 1, 1, 0);
v4l2_ctrl_new_std(v4l2_ctrl_hdl, &wave5_vpu_enc_ctrl_ops,
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
index d26ffc942219..f77abd5e122a 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.c
@@ -584,8 +584,15 @@ int wave5_vpu_dec_get_output_info(struct vpu_instance *inst, struct dec_output_i
p_dec_info->num_of_decoding_fbs : p_dec_info->num_of_display_fbs;
if (info->index_frame_display >= 0 &&
- info->index_frame_display < (int)max_dec_index)
- info->disp_frame = inst->frame_buf[val + info->index_frame_display];
+ info->index_frame_display < (int)max_dec_index) {
+ u32 idx = val + info->index_frame_display;
+
+ if (WARN_ON(idx >= MAX_REG_FRAME)) {
+ ret = -EINVAL;
+ goto err_out;
+ }
+ info->disp_frame = inst->frame_buf[idx];
+ }
info->rd_ptr = p_dec_info->stream_rd_ptr;
info->wr_ptr = p_dec_info->stream_wr_ptr;
diff --git a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
index c64135769869..7b08fef58217 100644
--- a/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
+++ b/drivers/media/platform/chips-media/wave5/wave5-vpuapi.h
@@ -570,6 +570,8 @@ struct enc_wave_param {
u32 transform8x8_enable: 1; /* enable 8x8 intra prediction and 8x8 transform */
u32 mb_level_rc_enable: 1; /* enable MB-level rate control */
u32 forced_idr_header_enable: 1; /* enable header encoding before IDR frame */
+ u32 constraint_set1_flag: 1; /* enable CBP */
+ u32 bg_detection: 1; /* enable background detection */
};
struct enc_open_param {
diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c
index 632c15572aa8..22034df6cba9 100644
--- a/drivers/media/platform/marvell/cafe-driver.c
+++ b/drivers/media/platform/marvell/cafe-driver.c
@@ -609,6 +609,7 @@ static void cafe_pci_remove(struct pci_dev *pdev)
return;
}
cafe_shutdown(cam);
+ pci_disable_device(pdev);
kfree(cam);
}
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index 8c684756d5fc..d147ec483081 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -1202,7 +1202,8 @@ static int mtk_jpeg_release(struct file *file)
struct mtk_jpeg_dev *jpeg = video_drvdata(file);
struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file);
- cancel_work_sync(&ctx->jpeg_work);
+ if (jpeg->variant->jpeg_worker)
+ cancel_work_sync(&ctx->jpeg_work);
mutex_lock(&jpeg->lock);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c
index b2a562e1ee1c..52505af35c08 100644
--- a/drivers/media/platform/nuvoton/npcm-video.c
+++ b/drivers/media/platform/nuvoton/npcm-video.c
@@ -1720,10 +1720,12 @@ static int npcm_video_init(struct npcm_video *video)
if (rc) {
dev_err(dev, "Failed to set DMA mask\n");
of_reserved_mem_device_release(dev);
+ return rc;
}
rc = npcm_video_ece_init(video);
if (rc) {
+ of_reserved_mem_device_release(dev);
dev_err(dev, "Failed to initialize ECE\n");
return rc;
}
@@ -1748,42 +1750,55 @@ static int npcm_video_probe(struct platform_device *pdev)
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs)) {
dev_err(&pdev->dev, "Failed to parse VCD reg in DTS\n");
- return PTR_ERR(regs);
+ rc = PTR_ERR(regs);
+ goto err_free;
}
video->vcd_regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&npcm_video_regmap_cfg);
if (IS_ERR(video->vcd_regmap)) {
dev_err(&pdev->dev, "Failed to initialize VCD regmap\n");
- return PTR_ERR(video->vcd_regmap);
+ rc = PTR_ERR(video->vcd_regmap);
+ goto err_free;
}
video->reset = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(video->reset)) {
dev_err(&pdev->dev, "Failed to get VCD reset control in DTS\n");
- return PTR_ERR(video->reset);
+ rc = PTR_ERR(video->reset);
+ goto err_free;
}
video->gcr_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"nuvoton,sysgcr");
- if (IS_ERR(video->gcr_regmap))
- return PTR_ERR(video->gcr_regmap);
+ if (IS_ERR(video->gcr_regmap)) {
+ rc = PTR_ERR(video->gcr_regmap);
+ goto err_free;
+ }
video->gfx_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
"nuvoton,sysgfxi");
- if (IS_ERR(video->gfx_regmap))
- return PTR_ERR(video->gfx_regmap);
+ if (IS_ERR(video->gfx_regmap)) {
+ rc = PTR_ERR(video->gfx_regmap);
+ goto err_free;
+ }
rc = npcm_video_init(video);
if (rc)
- return rc;
+ goto err_free;
rc = npcm_video_setup_video(video);
if (rc)
- return rc;
+ goto err_release_mem;
dev_info(video->dev, "NPCM video driver probed\n");
return 0;
+
+err_release_mem:
+ of_reserved_mem_device_release(&pdev->dev);
+err_free:
+ kfree(video);
+ return rc;
}
static void npcm_video_remove(struct platform_device *pdev)
@@ -1798,6 +1813,7 @@ static void npcm_video_remove(struct platform_device *pdev)
v4l2_device_unregister(v4l2_dev);
if (video->ece.enable)
npcm_video_ece_stop(video);
+ kfree(video);
of_reserved_mem_device_release(dev);
}
diff --git a/drivers/media/platform/nvidia/tegra-vde/Kconfig b/drivers/media/platform/nvidia/tegra-vde/Kconfig
index 2fe13f39c95b..f05fcc94deca 100644
--- a/drivers/media/platform/nvidia/tegra-vde/Kconfig
+++ b/drivers/media/platform/nvidia/tegra-vde/Kconfig
@@ -2,6 +2,7 @@ config VIDEO_TEGRA_VDE
tristate "NVIDIA Tegra Video Decoder Engine driver"
depends on V4L_MEM2MEM_DRIVERS
depends on ARCH_TEGRA || COMPILE_TEST
+ depends on HAS_IOMEM
depends on VIDEO_DEV
select DMA_SHARED_BUFFER
select IOMMU_IOVA
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 4bf8570e1b9e..e8545761b5ff 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.c
@@ -538,6 +538,8 @@ static int mxc_isi_probe(struct platform_device *pdev)
return 0;
err_xbar:
+ while (i--)
+ mxc_isi_pipe_cleanup(&isi->pipes[i]);
mxc_isi_crossbar_cleanup(&isi->crossbar);
return ret;
@@ -556,8 +558,8 @@ static void mxc_isi_remove(struct platform_device *pdev)
mxc_isi_pipe_cleanup(pipe);
}
- mxc_isi_crossbar_cleanup(&isi->crossbar);
mxc_isi_v4l2_cleanup(isi);
+ mxc_isi_crossbar_cleanup(&isi->crossbar);
}
static const struct of_device_id mxc_isi_of_match[] = {
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 14d63ec36416..7547a6559d4c 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
@@ -11,6 +11,7 @@
#define __MXC_ISI_CORE_H__
#include <linux/list.h>
+#include <linux/math.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@@ -414,4 +415,19 @@ static inline void mxc_isi_debug_cleanup(struct mxc_isi_dev *isi)
}
#endif
+/*
+ * ISI scaling engine works in two parts: it performs pre-decimation of
+ * the image followed by bilinear filtering to achieve the desired
+ * downscaling factor.
+ *
+ * The decimation filter provides a maximum downscaling factor of 8, and
+ * the subsequent bilinear filter provides a maximum downscaling factor
+ * of 2. Combined, the maximum scaling factor can be up to 16.
+ */
+static inline unsigned int
+mxc_isi_clamp_downscale_16(unsigned int val, unsigned int max_val)
+{
+ return clamp(val, max(1U, DIV_ROUND_UP(max_val, 16)), max_val);
+}
+
#endif /* __MXC_ISI_CORE_H__ */
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 605a45124103..c580c831972e 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -491,6 +491,7 @@ err_free:
void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar)
{
+ v4l2_subdev_cleanup(&xbar->sd);
media_entity_cleanup(&xbar->sd.entity);
kfree(xbar->pads);
kfree(xbar->inputs);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
index 0187d4ab97e8..16b20ea2d1db 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c
@@ -112,7 +112,14 @@ static u32 mxc_isi_channel_scaling_ratio(unsigned int from, unsigned int to,
else
*dec = 8;
- return min_t(u32, from * 0x1000 / (to * *dec), ISI_DOWNSCALE_THRESHOLD);
+ /*
+ * The ISI rounds output dimensions up to the next integer (i.MX93 RM
+ * section 57.7.8). Calculate the scale factor such that the theoretical
+ * output (input / scale_factor) rounds up to exactly the desired
+ * output.
+ */
+ return min_t(u32, DIV_ROUND_UP(from * 0x1000, to * *dec),
+ ISI_DOWNSCALE_THRESHOLD);
}
static void mxc_isi_channel_set_scaling(struct mxc_isi_pipe *pipe,
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
index a39ad7a1ab18..de398b232d74 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c
@@ -509,9 +509,14 @@ __mxc_isi_m2m_try_fmt_vid(struct mxc_isi_m2m_ctx *ctx,
const enum mxc_isi_video_type type)
{
if (type == MXC_ISI_VIDEO_M2M_CAP) {
- /* Downscaling only */
- pix->width = min(pix->width, ctx->queues.out.format.width);
- pix->height = min(pix->height, ctx->queues.out.format.height);
+ const struct v4l2_pix_format_mplane *format =
+ &ctx->queues.out.format;
+
+ /* Downscaling only, by up to 16. */
+ pix->width = mxc_isi_clamp_downscale_16(pix->width,
+ format->width);
+ pix->height = mxc_isi_clamp_downscale_16(pix->height,
+ format->height);
}
return mxc_isi_format_try(ctx->m2m->pipe, pix, type);
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
index a41c51dd9ce0..2d0843c86534 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
@@ -641,16 +641,19 @@ static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd,
/* Composing is supported on the sink only. */
return -EINVAL;
- /* The sink crop is bound by the sink format downscaling only). */
+ /*
+ * The ISI supports downscaling only, with a factor up to 16.
+ * Clamp the compose rectangle size accordingly.
+ */
format = mxc_isi_pipe_get_pad_format(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
sel->r.left = 0;
sel->r.top = 0;
- sel->r.width = clamp(sel->r.width, MXC_ISI_MIN_WIDTH,
- format->width);
- sel->r.height = clamp(sel->r.height, MXC_ISI_MIN_HEIGHT,
- format->height);
+ sel->r.width = mxc_isi_clamp_downscale_16(sel->r.width,
+ format->width);
+ sel->r.height = mxc_isi_clamp_downscale_16(sel->r.height,
+ format->height);
rect = mxc_isi_pipe_get_pad_compose(pipe, state,
MXC_ISI_PIPE_PAD_SINK);
@@ -796,18 +799,20 @@ int mxc_isi_pipe_init(struct mxc_isi_dev *isi, unsigned int id)
irq = platform_get_irq(to_platform_device(isi->dev), id);
if (irq < 0) {
ret = irq;
- goto error;
+ goto error_subdev;
}
ret = devm_request_irq(isi->dev, irq, mxc_isi_pipe_irq_handler,
0, dev_name(isi->dev), pipe);
if (ret < 0) {
dev_err(isi->dev, "failed to request IRQ (%d)\n", ret);
- goto error;
+ goto error_subdev;
}
return 0;
+error_subdev:
+ v4l2_subdev_cleanup(sd);
error:
media_entity_cleanup(&sd->entity);
mutex_destroy(&pipe->lock);
@@ -819,6 +824,7 @@ void mxc_isi_pipe_cleanup(struct mxc_isi_pipe *pipe)
{
struct v4l2_subdev *sd = &pipe->sd;
+ v4l2_subdev_cleanup(sd);
media_entity_cleanup(&sd->entity);
mutex_destroy(&pipe->lock);
}
diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
index 1be3a728f32f..fe4adfa3a1f0 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
@@ -792,7 +792,11 @@ static void mxc_isi_video_queue_first_buffers(struct mxc_isi_video *video)
struct mxc_isi_buffer *buf;
struct list_head *list;
- list = i < discard ? &video->out_discard : &video->out_pending;
+ /*
+ * Queue buffers: prioritize pending buffers, then discard
+ * buffers.
+ */
+ list = (i < 2 - discard) ? &video->out_pending : &video->out_discard;
buf = list_first_entry(list, struct mxc_isi_buffer, list);
mxc_isi_channel_set_outbuf(video->pipe, buf->dma_addrs, buf_id);
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 5e349b491513..27898b3cc7d3 100644
--- a/drivers/media/platform/qcom/camss/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -10,10 +10,14 @@ qcom-camss-objs += \
camss-csid-680.o \
camss-csid-gen2.o \
camss-csid-gen3.o \
+ camss-csiphy.o \
camss-csiphy-2ph-1-0.o \
camss-csiphy-3ph-1-0.o \
- camss-csiphy.o \
+ camss-format.o \
camss-ispif.o \
+ camss-tpg.o \
+ camss-tpg-gen1.o \
+ camss-vfe.o \
camss-vfe-4-1.o \
camss-vfe-4-7.o \
camss-vfe-4-8.o \
@@ -21,11 +25,9 @@ qcom-camss-objs += \
camss-vfe-340.o \
camss-vfe-480.o \
camss-vfe-680.o \
- camss-vfe-gen3.o \
camss-vfe-gen1.o \
+ camss-vfe-gen3.o \
camss-vfe-vbif.o \
- camss-vfe.o \
- camss-video.o \
- camss-format.o \
+ camss-video.o
obj-$(CONFIG_VIDEO_QCOM_CAMSS) += qcom-camss.o
diff --git a/drivers/media/platform/qcom/camss/camss-csid-340.c b/drivers/media/platform/qcom/camss/camss-csid-340.c
index 2b50f9b96a34..eca3f07b8a8a 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-340.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-340.c
@@ -41,19 +41,33 @@
#define CSI2_RX_CFG1_MISR_EN BIT(6)
#define CSI2_RX_CFG1_CGC_MODE BIT(7)
-#define CSID_RDI_CFG0(rdi) (0x300 + 0x100 * (rdi))
-#define CSID_RDI_CFG0_BYTE_CNTR_EN BIT(0)
-#define CSID_RDI_CFG0_TIMESTAMP_EN BIT(1)
-#define CSID_RDI_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12)
-#define CSID_RDI_CFG0_DECODE_FORMAT_NOP CSID_RDI_CFG0_DECODE_FORMAT_MASK
-#define CSID_RDI_CFG0_DT_MASK GENMASK(21, 16)
-#define CSID_RDI_CFG0_VC_MASK GENMASK(23, 22)
-#define CSID_RDI_CFG0_DTID_MASK GENMASK(28, 27)
-#define CSID_RDI_CFG0_ENABLE BIT(31)
-
-#define CSID_RDI_CTRL(rdi) (0x308 + 0x100 * (rdi))
-#define CSID_RDI_CTRL_HALT_AT_FRAME_BOUNDARY 0
-#define CSID_RDI_CTRL_RESUME_AT_FRAME_BOUNDARY 1
+#define CSID_CFG0(iface) (0x200 + 0x100 * (iface))
+#define CSID_CFG0_BYTE_CNTR_EN BIT(0)
+#define CSID_CFG0_TIMESTAMP_EN BIT(1)
+#define CSID_CFG0_DECODE_FORMAT_MASK GENMASK(15, 12)
+#define CSID_CFG0_DECODE_FORMAT_NOP CSID_CFG0_DECODE_FORMAT_MASK
+#define CSID_CFG0_DT_MASK GENMASK(21, 16)
+#define CSID_CFG0_VC_MASK GENMASK(23, 22)
+#define CSID_CFG0_DTID_MASK GENMASK(28, 27)
+#define CSID_CFG0_ENABLE BIT(31)
+
+#define CSID_CTRL(iface) (0x208 + 0x100 * (iface))
+#define CSID_CTRL_HALT_AT_FRAME_BOUNDARY 0
+#define CSID_CTRL_RESUME_AT_FRAME_BOUNDARY 1
+
+enum csid_iface {
+ CSID_IFACE_PIX,
+ CSID_IFACE_RDI0,
+ CSID_IFACE_RDI1,
+ CSID_IFACE_RDI2,
+};
+
+static enum csid_iface csid_port_iface_map[MSM_CSID_MAX_SRC_STREAMS] = {
+ [0] = CSID_IFACE_RDI0,
+ [1] = CSID_IFACE_RDI1,
+ [2] = CSID_IFACE_RDI2,
+ [3] = CSID_IFACE_PIX,
+};
static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config *phy)
{
@@ -69,17 +83,14 @@ static void __csid_configure_rx(struct csid_device *csid, struct csid_phy_config
writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1);
}
-static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
-{
- writel_relaxed(!!enable, csid->base + CSID_RDI_CTRL(rdi));
-}
-
-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
+ enum csid_iface iface = csid_port_iface_map[port];
+
u8 dt_id;
u32 val;
@@ -88,40 +99,44 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- dt_id = vc & 0x03;
+ dt_id = port & 0x03;
+
+ if (iface == CSID_IFACE_PIX)
+ val = FIELD_PREP(CSID_CFG0_DECODE_FORMAT_MASK, format->decode_format);
+ else /* RDI is raw, no decoding */
+ val = CSID_CFG0_DECODE_FORMAT_NOP;
- val = CSID_RDI_CFG0_DECODE_FORMAT_NOP; /* only for RDI path */
- val |= FIELD_PREP(CSID_RDI_CFG0_DT_MASK, format->data_type);
- val |= FIELD_PREP(CSID_RDI_CFG0_VC_MASK, vc);
- val |= FIELD_PREP(CSID_RDI_CFG0_DTID_MASK, dt_id);
+ val |= FIELD_PREP(CSID_CFG0_DT_MASK, format->data_type);
+ val |= FIELD_PREP(CSID_CFG0_VC_MASK, vc);
+ val |= FIELD_PREP(CSID_CFG0_DTID_MASK, dt_id);
if (enable)
- val |= CSID_RDI_CFG0_ENABLE;
+ val |= CSID_CFG0_ENABLE;
- dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x vc=%u)\n",
- csid->id, enable ? "enable" : "disable", format->data_type, vc);
+ dev_dbg(csid->camss->dev, "CSID%u: Stream %s (dt:0x%x df=0x%x port=%u vc=%u)\n",
+ csid->id, enable ? "enable" : "disable", format->data_type,
+ format->decode_format, port, vc);
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+ writel_relaxed(val, csid->base + CSID_CFG0(iface));
+ writel_relaxed(enable, csid->base + CSID_CTRL(iface));
}
-static void csid_configure_stream(struct csid_device *csid, u8 enable)
+static void csid_configure_streams(struct csid_device *csid, u8 enable)
{
int i;
__csid_configure_rx(csid, &csid->phy);
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
- if (csid->phy.en_vc & BIT(i)) {
- __csid_configure_rdi_stream(csid, enable, i);
- __csid_ctrl_rdi(csid, enable, i);
- }
+ if (csid->phy.en_vc & BIT(i))
+ __csid_configure_stream(csid, !!enable, i, 0);
}
}
@@ -177,7 +192,7 @@ static void csid_subdev_init(struct csid_device *csid) {}
const struct csid_hw_ops csid_ops_340 = {
.configure_testgen_pattern = csid_configure_testgen_pattern,
- .configure_stream = csid_configure_stream,
+ .configure_stream = csid_configure_streams,
.hw_version = csid_hw_version,
.isr = csid_isr,
.reset = csid_reset,
diff --git a/drivers/media/platform/qcom/camss/camss-csid-680.c b/drivers/media/platform/qcom/camss/camss-csid-680.c
index 3ad3a174bcfb..345a67c8fb94 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-680.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-680.c
@@ -103,6 +103,8 @@
#define CSI2_RX_CFG0_PHY_NUM_SEL 20
#define CSI2_RX_CFG0_PHY_SEL_BASE_IDX 1
#define CSI2_RX_CFG0_PHY_TYPE_SEL 24
+#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27)
+#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28)
#define CSID_CSI2_RX_CFG1 0x204
#define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN BIT(0)
@@ -185,10 +187,20 @@ static void __csid_configure_rx(struct csid_device *csid,
struct csid_phy_config *phy, int vc)
{
u32 val;
+ struct camss *camss;
+ camss = csid->camss;
val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
- val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL;
+
+ if (camss->tpg && csid->tpg_linked &&
+ camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) {
+ val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1);
+ val |= CSI2_RX_CFG0_TPG_MUX_EN;
+ } else {
+ val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX)
+ << CSI2_RX_CFG0_PHY_NUM_SEL;
+ }
writel(val, csid->base + CSID_CSI2_RX_CFG0);
@@ -219,9 +231,9 @@ static void __csid_configure_top(struct csid_device *csid)
CSID_TOP_IO_PATH_CFG0(csid->id));
}
-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -233,28 +245,28 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
lane_cnt = 4;
val = 0;
- writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+ writel(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port));
/*
* DT_ID is a two bit bitfield that is concatenated with
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_RDI_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- dt_id = vc & 0x03;
+ dt_id = port & 0x03;
/* note: for non-RDI path, this should be format->decode_format */
val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT;
val |= format->data_type << RDI_CFG0_DATA_TYPE;
val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
val |= dt_id << RDI_CFG0_DT_ID;
- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));
val = RDI_CFG1_TIMESTAMP_STB_FRAME;
val |= RDI_CFG1_BYTE_CNTR_EN;
@@ -265,23 +277,23 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= RDI_CFG1_CROP_V_EN;
val |= RDI_CFG1_PACKING_MIPI;
- writel(val, csid->base + CSID_RDI_CFG1(vc));
+ writel(val, csid->base + CSID_RDI_CFG1(port));
val = 0;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));
val = 1;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));
val = 0;
- writel(val, csid->base + CSID_RDI_CTRL(vc));
+ writel(val, csid->base + CSID_RDI_CTRL(port));
- val = readl(csid->base + CSID_RDI_CFG0(vc));
+ val = readl(csid->base + CSID_RDI_CFG0(port));
if (enable)
val |= RDI_CFG0_ENABLE;
else
val &= ~RDI_CFG0_ENABLE;
- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));
}
static void csid_configure_stream(struct csid_device *csid, u8 enable)
@@ -290,11 +302,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
__csid_configure_top(csid);
- /* Loop through all enabled VCs and configure stream for each */
+ /* Loop through all enabled ports and configure a stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++) {
if (csid->phy.en_vc & BIT(i)) {
- __csid_configure_rdi_stream(csid, enable, i);
- __csid_configure_rx(csid, &csid->phy, i);
+ __csid_configure_rdi_stream(csid, enable, i, 0);
+ __csid_configure_rx(csid, &csid->phy, 0);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
index 2a1746dcc1c5..eadcb2f7e3aa 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
@@ -203,10 +203,10 @@ static void __csid_ctrl_rdi(struct csid_device *csid, int enable, u8 rdi)
writel_relaxed(val, csid->base + CSID_RDI_CTRL(rdi));
}
-static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
struct csid_testgen_config *tg = &csid->testgen;
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -253,10 +253,10 @@ static void __csid_configure_testgen(struct csid_device *csid, u8 enable, u8 vc)
writel_relaxed(val, csid->base + CSID_TPG_CTRL);
}
-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
/* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -267,14 +267,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_RDI_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- u8 dt_id = vc & 0x03;
+ u8 dt_id = port & 0x03;
val = 1 << RDI_CFG0_BYTE_CNTR_EN;
val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN;
@@ -284,56 +284,57 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= format->data_type << RDI_CFG0_DATA_TYPE;
val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
val |= dt_id << RDI_CFG0_DT_ID;
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));
/* CSID_TIMESTAMP_STB_POST_IRQ */
val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL;
- writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CFG1(port));
val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(port));
val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(port));
val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));
val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));
val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(port));
val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(port));
val = 1;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(port));
val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(port));
val = 0;
- writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CTRL(port));
- val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc));
+ val = readl_relaxed(csid->base + CSID_RDI_CFG0(port));
val |= enable << RDI_CFG0_ENABLE;
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(port));
}
static void csid_configure_stream(struct csid_device *csid, u8 enable)
{
struct csid_testgen_config *tg = &csid->testgen;
u8 i;
- /* Loop through all enabled VCs and configure stream for each */
+
+ /* Loop through all enabled ports and configure a stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
if (csid->phy.en_vc & BIT(i)) {
if (tg->enabled)
- __csid_configure_testgen(csid, enable, i);
+ __csid_configure_testgen(csid, enable, i, 0);
- __csid_configure_rdi_stream(csid, enable, i);
- __csid_configure_rx(csid, &csid->phy, i);
+ __csid_configure_rdi_stream(csid, enable, i, 0);
+ __csid_configure_rx(csid, &csid->phy, 0);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen3.c b/drivers/media/platform/qcom/camss/camss-csid-gen3.c
index bd059243790e..0fdbf75fb27d 100644
--- a/drivers/media/platform/qcom/camss/camss-csid-gen3.c
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen3.c
@@ -66,6 +66,8 @@
#define CSI2_RX_CFG0_VC_MODE 3
#define CSI2_RX_CFG0_DL0_INPUT_SEL 4
#define CSI2_RX_CFG0_PHY_NUM_SEL 20
+#define CSI2_RX_CFG0_TPG_MUX_EN BIT(27)
+#define CSI2_RX_CFG0_TPG_MUX_SEL GENMASK(29, 28)
#define CSID_CSI2_RX_CFG1 0x204
#define CSI2_RX_CFG1_ECC_CORRECTION_EN BIT(0)
@@ -109,10 +111,20 @@ static void __csid_configure_rx(struct csid_device *csid,
struct csid_phy_config *phy, int vc)
{
int val;
+ struct camss *camss;
+ camss = csid->camss;
val = (phy->lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES;
val |= phy->lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL;
- val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX) << CSI2_RX_CFG0_PHY_NUM_SEL;
+
+ if (camss->tpg && csid->tpg_linked &&
+ camss->tpg[phy->csiphy_id].testgen.mode != TPG_PAYLOAD_MODE_DISABLED) {
+ val |= FIELD_PREP(CSI2_RX_CFG0_TPG_MUX_SEL, phy->csiphy_id + 1);
+ val |= CSI2_RX_CFG0_TPG_MUX_EN;
+ } else {
+ val |= (phy->csiphy_id + CSI2_RX_CFG0_PHY_SEL_BASE_IDX)
+ << CSI2_RX_CFG0_PHY_NUM_SEL;
+ }
writel(val, csid->base + CSID_CSI2_RX_CFG0);
@@ -145,12 +157,12 @@ static void __csid_configure_wrapper(struct csid_device *csid)
writel(val, csid->camss->csid_wrapper_base + CSID_IO_PATH_CFG0(csid->id));
}
-static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 vc)
+static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8 port, u8 vc)
{
u32 val;
u8 lane_cnt = csid->phy.lane_cnt;
/* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + port];
const struct csid_format_info *format = csid_get_fmt_entry(csid->res->formats->formats,
csid->res->formats->nformats,
input_format->code);
@@ -163,14 +175,14 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
* the four least significant bits of the five bit VC
* bitfield to generate an internal CID value.
*
- * CSID_RDI_CFG0(vc)
+ * CSID_RDI_CFG0(port)
* DT_ID : 28:27
* VC : 26:22
* DT : 21:16
*
* CID : VC 3:0 << 2 | DT_ID 1:0
*/
- u8 dt_id = vc & 0x03;
+ u8 dt_id = port & 0x03;
val = RDI_CFG0_TIMESTAMP_EN;
val |= RDI_CFG0_TIMESTAMP_STB_SEL;
@@ -180,7 +192,7 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= format->data_type << RDI_CFG0_DT;
val |= dt_id << RDI_CFG0_DT_ID;
- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));
val = RDI_CFG1_PACKING_FORMAT_MIPI;
val |= RDI_CFG1_PIX_STORE;
@@ -189,22 +201,22 @@ static void __csid_configure_rdi_stream(struct csid_device *csid, u8 enable, u8
val |= RDI_CFG1_CROP_H_EN;
val |= RDI_CFG1_CROP_V_EN;
- writel(val, csid->base + CSID_RDI_CFG1(vc));
+ writel(val, csid->base + CSID_RDI_CFG1(port));
val = 0;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(port));
val = 1;
- writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
+ writel(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(port));
val = 0;
- writel(val, csid->base + CSID_RDI_CTRL(vc));
+ writel(val, csid->base + CSID_RDI_CTRL(port));
- val = readl(csid->base + CSID_RDI_CFG0(vc));
+ val = readl(csid->base + CSID_RDI_CFG0(port));
if (enable)
val |= RDI_CFG0_EN;
- writel(val, csid->base + CSID_RDI_CFG0(vc));
+ writel(val, csid->base + CSID_RDI_CFG0(port));
}
static void csid_configure_stream(struct csid_device *csid, u8 enable)
@@ -213,11 +225,11 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
__csid_configure_wrapper(csid);
- /* Loop through all enabled VCs and configure stream for each */
+ /* Loop through all enabled ports and configure a stream for each */
for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
if (csid->phy.en_vc & BIT(i)) {
- __csid_configure_rdi_stream(csid, enable, i);
- __csid_configure_rx(csid, &csid->phy, i);
+ __csid_configure_rdi_stream(csid, enable, i, 0);
+ __csid_configure_rx(csid, &csid->phy, 0);
__csid_ctrl_rdi(csid, enable, i);
}
}
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index ed1820488c98..48459b46a981 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -35,6 +35,8 @@
#define HW_VERSION_REVISION 16
#define HW_VERSION_GENERATION 28
+#define LANE_CFG_BITWIDTH 4
+
#define MSM_CSID_NAME "msm_csid"
const char * const csid_testgen_modes[] = {
@@ -1215,18 +1217,22 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id)
}
/*
- * csid_get_lane_assign - Calculate CSI2 lane assign configuration parameter
- * @lane_cfg - CSI2 lane configuration
+ * csid_get_lane_assign - Calculate lane assign by csiphy/tpg lane num
+ * @lane_cfg: CSI2 lane configuration
+ * @num_lanes: lane num
*
* Return lane assign
*/
-static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg)
+static u32 csid_get_lane_assign(struct csiphy_lanes_cfg *lane_cfg, int num_lanes)
{
u32 lane_assign = 0;
+ int pos;
int i;
- for (i = 0; i < lane_cfg->num_data; i++)
- lane_assign |= lane_cfg->data[i].pos << (i * 4);
+ for (i = 0; i < num_lanes; i++) {
+ pos = lane_cfg ? lane_cfg->data[i].pos : i;
+ lane_assign |= pos << (i * LANE_CFG_BITWIDTH);
+ }
return lane_assign;
}
@@ -1251,6 +1257,7 @@ static int csid_link_setup(struct media_entity *entity,
if ((local->flags & MEDIA_PAD_FL_SINK) &&
(flags & MEDIA_LNK_FL_ENABLED)) {
struct v4l2_subdev *sd;
+ struct tpg_device *tpg;
struct csid_device *csid;
struct csiphy_device *csiphy;
struct csiphy_lanes_cfg *lane_cfg;
@@ -1265,18 +1272,28 @@ static int csid_link_setup(struct media_entity *entity,
return -EBUSY;
sd = media_entity_to_v4l2_subdev(remote->entity);
- csiphy = v4l2_get_subdevdata(sd);
+ if (sd->grp_id == TPG_GRP_ID) {
+ tpg = v4l2_get_subdevdata(sd);
- /* If a sensor is not linked to CSIPHY */
- /* do no allow a link from CSIPHY to CSID */
- if (!csiphy->cfg.csi2)
- return -EPERM;
+ csid->phy.lane_cnt = tpg->res->lane_cnt;
+ csid->phy.csiphy_id = tpg->id;
+ csid->phy.lane_assign = csid_get_lane_assign(NULL, csid->phy.lane_cnt);
+ csid->tpg_linked = true;
+ } else {
+ csiphy = v4l2_get_subdevdata(sd);
- csid->phy.csiphy_id = csiphy->id;
+ /* If a sensor is not linked to CSIPHY */
+ /* do no allow a link from CSIPHY to CSID */
+ if (!csiphy->cfg.csi2)
+ return -EPERM;
- lane_cfg = &csiphy->cfg.csi2->lane_cfg;
- csid->phy.lane_cnt = lane_cfg->num_data;
- csid->phy.lane_assign = csid_get_lane_assign(lane_cfg);
+ csid->phy.csiphy_id = csiphy->id;
+
+ lane_cfg = &csiphy->cfg.csi2->lane_cfg;
+ csid->phy.lane_cnt = lane_cfg->num_data;
+ csid->phy.lane_assign = csid_get_lane_assign(lane_cfg, lane_cfg->num_data);
+ csid->tpg_linked = false;
+ }
}
/* Decide which virtual channels to enable based on which source pads are enabled */
if (local->flags & MEDIA_PAD_FL_SOURCE) {
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index aedc96ed84b2..5296b10f6bac 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -161,6 +161,7 @@ struct csid_device {
int num_supplies;
struct completion reset_complete;
struct csid_testgen_config testgen;
+ bool tpg_linked;
struct csid_phy_config phy;
struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM];
struct v4l2_ctrl_handler ctrls;
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 415483274552..dac8d2ecf799 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
@@ -399,6 +399,126 @@ csiphy_lane_regs lane_regs_sm8250[] = {
{0x0884, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
};
+/* GEN2 1.2.3 2PH */
+static const struct
+csiphy_lane_regs lane_regs_sm6350[] = {
+ {0x0030, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0904, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0910, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0900, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0908, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0904, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x002c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0034, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0010, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x001c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0028, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x003c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0004, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0020, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0008, 0x10, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0038, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x005c, 0xc0, 0x00, CSIPHY_SKEW_CAL},
+ {0x0060, 0x0d, 0x00, CSIPHY_SKEW_CAL},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0730, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c84, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c90, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c80, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c88, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c84, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x072c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0734, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x071c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x073c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0704, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0720, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0708, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x070c, 0xff, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0738, 0x1f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0230, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0a04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0a10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0a00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0a08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0a04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x022c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0234, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0210, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x021c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0228, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x023c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0204, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0220, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0208, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0238, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x025c, 0xc0, 0x00, CSIPHY_SKEW_CAL},
+ {0x0260, 0x0d, 0x00, CSIPHY_SKEW_CAL},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0430, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0b04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0b10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0b00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0b08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0b04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x042c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0434, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0410, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x041c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0428, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x043c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0404, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0420, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0408, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0438, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x045c, 0xc0, 0x00, CSIPHY_SKEW_CAL},
+ {0x0460, 0x0d, 0x00, CSIPHY_SKEW_CAL},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+ {0x0630, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c04, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c10, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c00, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c08, 0x06, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0c04, 0x07, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x062c, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0634, 0x0f, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0610, 0x50, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x061c, 0x0a, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0628, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x063c, 0xb8, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0604, 0x0c, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0620, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0608, 0x04, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE},
+ {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0638, 0xfe, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x065c, 0xc0, 0x00, CSIPHY_SKEW_CAL},
+ {0x0660, 0x0d, 0x00, CSIPHY_SKEW_CAL},
+ {0x0800, 0x02, 0x00, CSIPHY_DEFAULT_PARAMS},
+ {0x0000, 0x00, 0x00, CSIPHY_DNP_PARAMS},
+};
+
/* 14nm 2PH v 2.0.1 2p5Gbps 4 lane DPHY mode */
static const struct
csiphy_lane_regs lane_regs_qcm2290[] = {
@@ -1011,6 +1131,7 @@ static bool csiphy_is_gen2(u32 version)
switch (version) {
case CAMSS_2290:
case CAMSS_6150:
+ case CAMSS_6350:
case CAMSS_7280:
case CAMSS_8250:
case CAMSS_8280XP:
@@ -1105,6 +1226,10 @@ static int csiphy_init(struct csiphy_device *csiphy)
regs->lane_regs = &lane_regs_qcm2290[0];
regs->lane_array_size = ARRAY_SIZE(lane_regs_qcm2290);
break;
+ case CAMSS_6350:
+ regs->lane_regs = &lane_regs_sm6350[0];
+ regs->lane_array_size = ARRAY_SIZE(lane_regs_sm6350);
+ break;
case CAMSS_7280:
case CAMSS_8250:
regs->lane_regs = &lane_regs_sm8250[0];
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 78a1b568dbae..539ac4888b60 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -793,6 +793,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy,
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
MSM_CSIPHY_NAME, csiphy->id);
+ sd->grp_id = CSIPHY_GRP_ID;
v4l2_set_subdevdata(sd, csiphy);
ret = csiphy_init_formats(sd, NULL);
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index 2d5054819df7..9d9657b82f74 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -21,6 +21,8 @@
#define MSM_CSIPHY_PAD_SRC 1
#define MSM_CSIPHY_PADS_NUM 2
+#define CSIPHY_GRP_ID 1
+
struct csiphy_lane {
u8 pos;
u8 pol;
diff --git a/drivers/media/platform/qcom/camss/camss-format.c b/drivers/media/platform/qcom/camss/camss-format.c
index 4a3d5549615c..52cb01306ee0 100644
--- a/drivers/media/platform/qcom/camss/camss-format.c
+++ b/drivers/media/platform/qcom/camss/camss-format.c
@@ -7,8 +7,10 @@
* Copyright (c) 2023, The Linux Foundation. All rights reserved.
* Copyright (c) 2023 Qualcomm Technologies, Inc.
*/
+#include <linux/bits.h>
#include <linux/bug.h>
#include <linux/errno.h>
+#include <linux/lcm.h>
#include "camss-format.h"
@@ -34,6 +36,18 @@ u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nf
}
/*
+ * camss_format_get_bpl_alignment - Retrieve required BPL alignment for a given format.
+ * @format: a pointer to the format
+ *
+ * Return the required alignment, in bytes.
+ */
+unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *format)
+{
+ /* Minimal number of bytes required to keep the line length an integer number of pixels */
+ return lcm_not_zero(format->mbus_bpp, BITS_PER_BYTE) / BITS_PER_BYTE;
+}
+
+/*
* camss_format_find_code - Find a format code in an array
* @code: a pointer to media bus format codes array
* @n_code: size of @code array
diff --git a/drivers/media/platform/qcom/camss/camss-format.h b/drivers/media/platform/qcom/camss/camss-format.h
index 923a48c9c3fb..4f87ac8c4975 100644
--- a/drivers/media/platform/qcom/camss/camss-format.h
+++ b/drivers/media/platform/qcom/camss/camss-format.h
@@ -55,6 +55,7 @@ struct camss_formats {
};
u8 camss_format_get_bpp(const struct camss_format_info *formats, unsigned int nformats, u32 code);
+unsigned int camss_format_get_bpl_alignment(const struct camss_format_info *f);
u32 camss_format_find_code(u32 *code, unsigned int n_code, unsigned int index, u32 req_code);
int camss_format_find_format(u32 code, u32 pixelformat, const struct camss_format_info *formats,
unsigned int nformats);
diff --git a/drivers/media/platform/qcom/camss/camss-tpg-gen1.c b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c
new file mode 100644
index 000000000000..d29de5f93c18
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-tpg-gen1.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *
+ * Qualcomm MSM Camera Subsystem - TPG (Test Pattern Generator) Module
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#include <linux/io.h>
+#include <linux/kernel.h>
+
+#include "camss-tpg.h"
+#include "camss.h"
+
+/* TPG global registers */
+#define TPG_HW_VERSION 0x0
+# define HW_VERSION_STEPPING GENMASK(15, 0)
+# define HW_VERSION_REVISION GENMASK(27, 16)
+# define HW_VERSION_GENERATION GENMASK(31, 28)
+
+#define TPG_HW_VER(gen, rev, step) \
+ (((u32)(gen) << 28) | ((u32)(rev) << 16) | (u32)(step))
+
+#define TPG_HW_VER_2_0_0 TPG_HW_VER(2, 0, 0)
+#define TPG_HW_VER_2_1_0 TPG_HW_VER(2, 1, 0)
+
+#define TPG_HW_STATUS 0x4
+
+#define TPG_CTRL 0x64
+# define TPG_CTRL_TEST_EN BIT(0)
+# define TPG_CTRL_PHY_SEL BIT(3)
+# define TPG_CTRL_NUM_ACTIVE_LANES GENMASK(5, 4)
+# define TPG_CTRL_VC_DT_PATTERN_ID GENMASK(8, 6)
+# define TPG_CTRL_OVERLAP_SHDR_EN BIT(10)
+# define TPG_CTRL_NUM_ACTIVE_VC GENMASK(31, 30)
+
+#define TPG_CLEAR 0x1F4
+
+/* TPG VC-based registers */
+#define TPG_VC_n_GAIN_CFG(n) (0x60 + (n) * 0x60)
+
+#define TPG_VC_n_CFG0(n) (0x68 + (n) * 0x60)
+# define TPG_VC_n_CFG0_VC_NUM GENMASK(4, 0)
+# define TPG_VC_n_CFG0_NUM_ACTIVE_DT GENMASK(9, 8)
+# define TPG_VC_n_CFG0_NUM_BATCH GENMASK(15, 12)
+# define TPG_VC_n_CFG0_NUM_FRAMES GENMASK(31, 16)
+
+#define TPG_VC_n_LSFR_SEED(n) (0x6C + (n) * 0x60)
+#define TPG_VC_n_HBI_CFG(n) (0x70 + (n) * 0x60)
+#define TPG_VC_n_VBI_CFG(n) (0x74 + (n) * 0x60)
+
+#define TPG_VC_n_COLOR_BARS_CFG(n) (0x78 + (n) * 0x60)
+# define TPG_VC_n_COLOR_BARS_CFG_PIX_PATTERN GENMASK(2, 0)
+# define TPG_VC_n_COLOR_BARS_CFG_QCFA_EN BIT(3)
+# define TPG_VC_n_COLOR_BARS_CFG_SPLIT_EN BIT(4)
+# define TPG_VC_n_COLOR_BARS_CFG_NOISE_EN BIT(5)
+# define TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD GENMASK(13, 8)
+# define TPG_VC_n_COLOR_BARS_CFG_XCFA_EN BIT(16)
+# define TPG_VC_n_COLOR_BARS_CFG_SIZE_X GENMASK(26, 24)
+# define TPG_VC_n_COLOR_BARS_CFG_SIZE_Y GENMASK(30, 28)
+
+/* TPG DT-based registers */
+#define TPG_VC_m_DT_n_CFG_0(m, n) (0x7C + (m) * 0x60 + (n) * 0xC)
+# define TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT GENMASK(15, 0)
+# define TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH GENMASK(31, 16)
+
+#define TPG_VC_m_DT_n_CFG_1(m, n) (0x80 + (m) * 0x60 + (n) * 0xC)
+# define TPG_VC_m_DT_n_CFG_1_DATA_TYPE GENMASK(5, 0)
+# define TPG_VC_m_DT_n_CFG_1_ECC_XOR_MASK GENMASK(13, 8)
+# define TPG_VC_m_DT_n_CFG_1_CRC_XOR_MASK GENMASK(31, 16)
+
+#define TPG_VC_m_DT_n_CFG_2(m, n) (0x84 + (m) * 0x60 + (n) * 0xC)
+# define TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE GENMASK(3, 0)
+/* v2.0.0: USER[19:4], ENC[23:20] */
+# define TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(19, 4)
+# define TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(23, 20)
+/* v2.1.0: USER[27:4], ENC[31:28] */
+# define TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD GENMASK(27, 4)
+# define TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT GENMASK(31, 28)
+
+#define TPG_HBI_PCT_DEFAULT 545 /* 545% */
+#define TPG_VBI_PCT_DEFAULT 10 /* 10% */
+#define PERCENT_BASE 100
+
+/* Default user-specified payload for TPG test generator.
+ * Keep consistent with CSID TPG default: 0xBE.
+ */
+#define TPG_USER_SPECIFIED_PAYLOAD_DEFAULT 0xBE
+#define TPG_LFSR_SEED_DEFAULT 0x12345678
+#define TPG_COLOR_BARS_CFG_STANDARD \
+ FIELD_PREP(TPG_VC_n_COLOR_BARS_CFG_ROTATE_PERIOD, 0xA)
+
+static const char * const testgen_payload_modes[] = {
+ [TPG_PAYLOAD_MODE_DISABLED] = "Disabled",
+ [TPG_PAYLOAD_MODE_INCREMENTING] = "Incrementing",
+ [TPG_PAYLOAD_MODE_ALTERNATING_55_AA] = "Alternating 0x55/0xAA",
+ [TPG_PAYLOAD_MODE_RANDOM] = "Pseudo-random Data",
+ [TPG_PAYLOAD_MODE_USER_SPECIFIED] = "User Specified",
+ [TPG_PAYLOAD_MODE_COLOR_BARS] = "Color bars",
+};
+
+static int tpg_stream_on(struct tpg_device *tpg)
+{
+ struct tpg_testgen_config *tg = &tpg->testgen;
+ struct v4l2_mbus_framefmt *input_format;
+ const struct tpg_format_info *format;
+ u8 payload_mode = (tg->mode > TPG_PAYLOAD_MODE_DISABLED) ?
+ tg->mode - 1 : 0;
+ u8 lane_cnt = tpg->res->lane_cnt;
+ u8 vc, dt, last_vc = 0;
+ u32 val;
+
+ for (vc = 0; vc <= MSM_TPG_ACTIVE_VC; vc++) {
+ last_vc = vc;
+
+ input_format = &tpg->fmt;
+ format = tpg_get_fmt_entry(tpg->res->formats->formats,
+ tpg->res->formats->nformats,
+ input_format->code);
+ if (IS_ERR(format))
+ return -EINVAL;
+
+ /* VC configuration */
+ val = FIELD_PREP(TPG_VC_n_CFG0_NUM_ACTIVE_DT, MSM_TPG_ACTIVE_DT) |
+ FIELD_PREP(TPG_VC_n_CFG0_NUM_FRAMES, 0);
+ writel(val, tpg->base + TPG_VC_n_CFG0(vc));
+
+ writel(TPG_LFSR_SEED_DEFAULT, tpg->base + TPG_VC_n_LSFR_SEED(vc));
+
+ val = DIV_ROUND_UP(input_format->width * format->bpp * TPG_HBI_PCT_DEFAULT,
+ BITS_PER_BYTE * lane_cnt * PERCENT_BASE);
+ writel(val, tpg->base + TPG_VC_n_HBI_CFG(vc));
+
+ val = input_format->height * TPG_VBI_PCT_DEFAULT / PERCENT_BASE;
+ writel(val, tpg->base + TPG_VC_n_VBI_CFG(vc));
+
+ writel(TPG_COLOR_BARS_CFG_STANDARD, tpg->base + TPG_VC_n_COLOR_BARS_CFG(vc));
+
+ /* DT configuration */
+ for (dt = 0; dt <= MSM_TPG_ACTIVE_DT; dt++) {
+ val = FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_HEIGHT,
+ input_format->height & 0xffff) |
+ FIELD_PREP(TPG_VC_m_DT_n_CFG_0_FRAME_WIDTH,
+ input_format->width & 0xffff);
+ writel(val, tpg->base + TPG_VC_m_DT_n_CFG_0(vc, dt));
+
+ val = FIELD_PREP(TPG_VC_m_DT_n_CFG_1_DATA_TYPE, format->data_type);
+ writel(val, tpg->base + TPG_VC_m_DT_n_CFG_1(vc, dt));
+
+ if (tpg->hw_version == TPG_HW_VER_2_0_0) {
+ val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) |
+ FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD,
+ TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) |
+ FIELD_PREP(TPG_V2_0_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT,
+ format->encode_format);
+ } else if (tpg->hw_version >= TPG_HW_VER_2_1_0) {
+ val = FIELD_PREP(TPG_VC_m_DT_n_CFG_2_PAYLOAD_MODE, payload_mode) |
+ FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD,
+ TPG_USER_SPECIFIED_PAYLOAD_DEFAULT) |
+ FIELD_PREP(TPG_V2_1_0_VC_m_DT_n_CFG_2_ENCODE_FORMAT,
+ format->encode_format);
+ }
+ writel(val, tpg->base + TPG_VC_m_DT_n_CFG_2(vc, dt));
+ }
+ }
+
+ /* Global TPG control */
+ val = FIELD_PREP(TPG_CTRL_TEST_EN, 1) |
+ FIELD_PREP(TPG_CTRL_NUM_ACTIVE_LANES, lane_cnt - 1) |
+ FIELD_PREP(TPG_CTRL_NUM_ACTIVE_VC, last_vc);
+ writel(val, tpg->base + TPG_CTRL);
+
+ return 0;
+}
+
+static int tpg_reset(struct tpg_device *tpg)
+{
+ writel(0, tpg->base + TPG_CTRL);
+ writel(1, tpg->base + TPG_CLEAR);
+
+ return 0;
+}
+
+static void tpg_stream_off(struct tpg_device *tpg)
+{
+ tpg_reset(tpg);
+}
+
+static int tpg_configure_stream(struct tpg_device *tpg, u8 enable)
+{
+ if (enable)
+ return tpg_stream_on(tpg);
+
+ tpg_stream_off(tpg);
+
+ return 0;
+}
+
+static int tpg_configure_testgen_pattern(struct tpg_device *tpg, s32 val)
+{
+ if (val >= 0 && val <= TPG_PAYLOAD_MODE_COLOR_BARS)
+ tpg->testgen.mode = val;
+
+ return 0;
+}
+
+static u32 tpg_hw_version(struct tpg_device *tpg)
+{
+ u32 hw_version = readl(tpg->base + TPG_HW_VERSION);
+
+ tpg->hw_version = hw_version;
+ dev_dbg(tpg->camss->dev, "tpg HW Version = %u.%u.%u\n",
+ (u32)FIELD_GET(HW_VERSION_GENERATION, hw_version),
+ (u32)FIELD_GET(HW_VERSION_REVISION, hw_version),
+ (u32)FIELD_GET(HW_VERSION_STEPPING, hw_version));
+
+ return hw_version;
+}
+
+static void tpg_subdev_init(struct tpg_device *tpg)
+{
+ tpg->testgen.modes = testgen_payload_modes;
+ tpg->testgen.nmodes = TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1;
+}
+
+const struct tpg_hw_ops tpg_ops_gen1 = {
+ .configure_stream = tpg_configure_stream,
+ .configure_testgen_pattern = tpg_configure_testgen_pattern,
+ .hw_version = tpg_hw_version,
+ .reset = tpg_reset,
+ .subdev_init = tpg_subdev_init,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-tpg.c b/drivers/media/platform/qcom/camss/camss-tpg.c
new file mode 100644
index 000000000000..c5b75132add4
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-tpg.c
@@ -0,0 +1,519 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *
+ * Qualcomm MSM Camera Subsystem - TPG Module
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include <media/mipi-csi2.h>
+
+#include "camss-tpg.h"
+#include "camss.h"
+
+static const struct tpg_format_info formats_gen1[] = {
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ ENCODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ ENCODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ ENCODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ ENCODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ ENCODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ ENCODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ ENCODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ ENCODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ ENCODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ ENCODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ ENCODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MIPI_CSI2_DT_RAW12,
+ ENCODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ },
+ {
+ MEDIA_BUS_FMT_Y8_1X8,
+ MIPI_CSI2_DT_RAW8,
+ ENCODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MIPI_CSI2_DT_RAW10,
+ ENCODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ },
+};
+
+const struct tpg_formats tpg_formats_gen1 = {
+ .nformats = ARRAY_SIZE(formats_gen1),
+ .formats = formats_gen1
+};
+
+const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats,
+ unsigned int nformats,
+ u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < nformats; i++)
+ if (code == formats[i].code)
+ return &formats[i];
+
+ return ERR_PTR(-EINVAL);
+}
+
+static int tpg_set_clock_rates(struct tpg_device *tpg)
+{
+ struct device *dev = tpg->camss->dev;
+ int i, ret;
+
+ for (i = 0; i < tpg->nclocks; i++) {
+ struct camss_clock *clock = &tpg->clock[i];
+ long round_rate;
+
+ if (clock->freq) {
+ round_rate = clk_round_rate(clock->clk, clock->freq[0]);
+ if (round_rate < 0) {
+ dev_err(dev, "clk round rate failed: %ld\n",
+ round_rate);
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(clock->clk, round_rate);
+ if (ret < 0) {
+ dev_err(dev, "clk set rate failed: %d\n", ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int tpg_set_power(struct v4l2_subdev *sd, int on)
+{
+ struct tpg_device *tpg = v4l2_get_subdevdata(sd);
+ struct device *dev = tpg->camss->dev;
+
+ if (on) {
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = tpg_set_clock_rates(tpg);
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
+ return ret;
+ }
+
+ ret = camss_enable_clocks(tpg->nclocks, tpg->clock, dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
+ return ret;
+ }
+
+ tpg->res->hw_ops->reset(tpg);
+
+ tpg->res->hw_ops->hw_version(tpg);
+ } else {
+ camss_disable_clocks(tpg->nclocks, tpg->clock);
+
+ pm_runtime_put_sync(dev);
+ }
+
+ return 0;
+}
+
+static int tpg_set_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct tpg_device *tpg = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (enable) {
+ ret = v4l2_ctrl_handler_setup(&tpg->ctrls);
+ if (ret < 0) {
+ dev_err(tpg->camss->dev,
+ "could not sync v4l2 controls: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return tpg->res->hw_ops->configure_stream(tpg, enable);
+}
+
+static struct v4l2_mbus_framefmt *
+__tpg_get_format(struct tpg_device *tpg,
+ struct v4l2_subdev_state *sd_state,
+ unsigned int pad,
+ enum v4l2_subdev_format_whence which)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY)
+ return v4l2_subdev_state_get_format(sd_state,
+ pad);
+
+ return &tpg->fmt;
+}
+
+static void tpg_try_format(struct tpg_device *tpg,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ unsigned int i;
+
+ for (i = 0; i < tpg->res->formats->nformats; i++)
+ if (tpg->res->formats->formats[i].code == fmt->code)
+ break;
+
+ if (i >= tpg->res->formats->nformats)
+ fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8;
+
+ fmt->width = clamp_t(u32, fmt->width, TPG_MIN_WIDTH, TPG_MAX_WIDTH);
+ fmt->height = clamp_t(u32, fmt->height, TPG_MIN_HEIGHT, TPG_MAX_HEIGHT);
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_SRGB;
+}
+
+static int tpg_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct tpg_device *tpg = v4l2_get_subdevdata(sd);
+
+ if (code->index >= tpg->res->formats->nformats)
+ return -EINVAL;
+
+ code->code = tpg->res->formats->formats[code->index].code;
+
+ return 0;
+}
+
+static int tpg_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct tpg_device *tpg = v4l2_get_subdevdata(sd);
+ unsigned int i;
+
+ if (fse->index != 0)
+ return -EINVAL;
+
+ for (i = 0; i < tpg->res->formats->nformats; i++)
+ if (tpg->res->formats->formats[i].code == fse->code)
+ break;
+
+ if (i >= tpg->res->formats->nformats)
+ return -EINVAL;
+
+ fse->min_width = TPG_MIN_WIDTH;
+ fse->min_height = TPG_MIN_HEIGHT;
+ fse->max_width = TPG_MAX_WIDTH;
+ fse->max_height = TPG_MAX_HEIGHT;
+
+ return 0;
+}
+
+static int tpg_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct tpg_device *tpg = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which);
+ if (!format)
+ return -EINVAL;
+
+ fmt->format = *format;
+
+ return 0;
+}
+
+static int tpg_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct tpg_device *tpg = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *format;
+
+ format = __tpg_get_format(tpg, sd_state, fmt->pad, fmt->which);
+ if (!format)
+ return -EINVAL;
+
+ tpg_try_format(tpg, &fmt->format);
+ *format = fmt->format;
+
+ return 0;
+}
+
+static int tpg_init_formats(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct v4l2_subdev_format format = {
+ .pad = MSM_TPG_PAD_SRC,
+ .which = fh ? V4L2_SUBDEV_FORMAT_TRY :
+ V4L2_SUBDEV_FORMAT_ACTIVE,
+ .format = {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .width = 1920,
+ .height = 1080,
+ }
+ };
+
+ return tpg_set_format(sd, fh ? fh->state : NULL, &format);
+}
+
+static int tpg_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct tpg_device *tpg = container_of(ctrl->handler,
+ struct tpg_device, ctrls);
+ int ret = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_TEST_PATTERN:
+ ret = tpg->res->hw_ops->configure_testgen_pattern(tpg, ctrl->val);
+ break;
+ }
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops tpg_ctrl_ops = {
+ .s_ctrl = tpg_s_ctrl,
+};
+
+int msm_tpg_subdev_init(struct camss *camss,
+ struct tpg_device *tpg,
+ const struct camss_subdev_resources *res, u8 id)
+{
+ struct platform_device *pdev;
+ struct device *dev;
+ int i, j;
+
+ dev = camss->dev;
+ pdev = to_platform_device(dev);
+
+ tpg->camss = camss;
+ tpg->id = id;
+ tpg->res = &res->tpg;
+ tpg->res->hw_ops->subdev_init(tpg);
+
+ tpg->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]);
+ if (IS_ERR(tpg->base))
+ return PTR_ERR(tpg->base);
+
+ tpg->nclocks = 0;
+ while (res->clock[tpg->nclocks])
+ tpg->nclocks++;
+
+ if (!tpg->nclocks)
+ return 0;
+
+ tpg->clock = devm_kcalloc(dev, tpg->nclocks,
+ sizeof(*tpg->clock), GFP_KERNEL);
+ if (!tpg->clock)
+ return -ENOMEM;
+
+ for (i = 0; i < tpg->nclocks; i++) {
+ struct camss_clock *clock = &tpg->clock[i];
+
+ clock->clk = devm_clk_get(dev, res->clock[i]);
+ if (IS_ERR(clock->clk))
+ return PTR_ERR(clock->clk);
+
+ clock->name = res->clock[i];
+
+ clock->nfreqs = 0;
+ while (res->clock_rate[i][clock->nfreqs])
+ clock->nfreqs++;
+
+ if (!clock->nfreqs) {
+ clock->freq = NULL;
+ continue;
+ }
+
+ clock->freq = devm_kcalloc(dev, clock->nfreqs,
+ sizeof(*clock->freq), GFP_KERNEL);
+ if (!clock->freq)
+ return -ENOMEM;
+
+ for (j = 0; j < clock->nfreqs; j++)
+ clock->freq[j] = res->clock_rate[i][j];
+ }
+
+ return 0;
+}
+
+static int tpg_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ if (flags & MEDIA_LNK_FL_ENABLED)
+ if (media_pad_remote_pad_first(local))
+ return -EBUSY;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops tpg_core_ops = {
+ .s_power = tpg_set_power,
+};
+
+static const struct v4l2_subdev_video_ops tpg_video_ops = {
+ .s_stream = tpg_set_stream,
+};
+
+static const struct v4l2_subdev_pad_ops tpg_pad_ops = {
+ .enum_mbus_code = tpg_enum_mbus_code,
+ .enum_frame_size = tpg_enum_frame_size,
+ .get_fmt = tpg_get_format,
+ .set_fmt = tpg_set_format,
+};
+
+static const struct v4l2_subdev_ops tpg_v4l2_ops = {
+ .core = &tpg_core_ops,
+ .video = &tpg_video_ops,
+ .pad = &tpg_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops tpg_v4l2_internal_ops = {
+ .open = tpg_init_formats,
+};
+
+static const struct media_entity_operations tpg_media_ops = {
+ .link_setup = tpg_link_setup,
+ .link_validate = v4l2_subdev_link_validate,
+};
+
+int msm_tpg_register_entity(struct tpg_device *tpg,
+ struct v4l2_device *v4l2_dev)
+{
+ struct v4l2_subdev *sd = &tpg->subdev;
+ struct device *dev = tpg->camss->dev;
+ int ret;
+
+ v4l2_subdev_init(sd, &tpg_v4l2_ops);
+ sd->internal_ops = &tpg_v4l2_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
+ snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
+ "msm_tpg", tpg->id);
+ sd->grp_id = TPG_GRP_ID;
+ v4l2_set_subdevdata(sd, tpg);
+
+ ret = v4l2_ctrl_handler_init(&tpg->ctrls, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init ctrl handler: %d\n", ret);
+ return ret;
+ }
+
+ tpg->testgen_mode = v4l2_ctrl_new_std_menu_items(&tpg->ctrls,
+ &tpg_ctrl_ops, V4L2_CID_TEST_PATTERN,
+ tpg->testgen.nmodes, 0, 0,
+ tpg->testgen.modes);
+ if (tpg->ctrls.error) {
+ dev_err(dev, "Failed to init ctrl: %d\n", tpg->ctrls.error);
+ ret = tpg->ctrls.error;
+ goto free_ctrl;
+ }
+
+ tpg->subdev.ctrl_handler = &tpg->ctrls;
+
+ ret = tpg_init_formats(sd, NULL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init format: %d\n", ret);
+ goto free_ctrl;
+ }
+
+ tpg->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ sd->entity.ops = &tpg_media_ops;
+ ret = media_entity_pads_init(&sd->entity, 1, &tpg->pad);
+ if (ret < 0) {
+ dev_err(dev, "Failed to init media entity: %d\n", ret);
+ goto free_ctrl;
+ }
+
+ ret = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register subdev: %d\n", ret);
+ media_entity_cleanup(&sd->entity);
+ goto free_ctrl;
+ }
+
+ return 0;
+
+free_ctrl:
+ v4l2_ctrl_handler_free(&tpg->ctrls);
+
+ return ret;
+}
+
+void msm_tpg_unregister_entity(struct tpg_device *tpg)
+{
+ v4l2_device_unregister_subdev(&tpg->subdev);
+ media_entity_cleanup(&tpg->subdev.entity);
+ v4l2_ctrl_handler_free(&tpg->ctrls);
+}
diff --git a/drivers/media/platform/qcom/camss/camss-tpg.h b/drivers/media/platform/qcom/camss/camss-tpg.h
new file mode 100644
index 000000000000..7fb35a97dd06
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-tpg.h
@@ -0,0 +1,118 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-tpg.h
+ *
+ * Qualcomm MSM Camera Subsystem - TPG Module
+ *
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef QC_MSM_CAMSS_TPG_H
+#define QC_MSM_CAMSS_TPG_H
+
+#include <linux/clk.h>
+#include <linux/bitfield.h>
+#include <media/media-entity.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+#include <media/v4l2-subdev.h>
+
+#define ENCODE_FORMAT_UNCOMPRESSED_8_BIT 0x1
+#define ENCODE_FORMAT_UNCOMPRESSED_10_BIT 0x2
+#define ENCODE_FORMAT_UNCOMPRESSED_12_BIT 0x3
+#define ENCODE_FORMAT_UNCOMPRESSED_14_BIT 0x4
+#define ENCODE_FORMAT_UNCOMPRESSED_16_BIT 0x5
+#define ENCODE_FORMAT_UNCOMPRESSED_20_BIT 0x6
+#define ENCODE_FORMAT_UNCOMPRESSED_24_BIT 0x7
+
+#define MSM_TPG_PAD_SRC 0
+#define MSM_TPG_ACTIVE_VC 0
+#define MSM_TPG_ACTIVE_DT 0
+
+#define TPG_MIN_WIDTH 1
+#define TPG_MIN_HEIGHT 1
+#define TPG_MAX_WIDTH 8191
+#define TPG_MAX_HEIGHT 8191
+
+#define TPG_GRP_ID 0
+
+enum tpg_testgen_mode {
+ TPG_PAYLOAD_MODE_DISABLED = 0,
+ TPG_PAYLOAD_MODE_INCREMENTING = 1,
+ TPG_PAYLOAD_MODE_ALTERNATING_55_AA = 2,
+ TPG_PAYLOAD_MODE_RANDOM = 5,
+ TPG_PAYLOAD_MODE_USER_SPECIFIED = 6,
+ TPG_PAYLOAD_MODE_COLOR_BARS = 9,
+ TPG_PAYLOAD_MODE_NUM_SUPPORTED_GEN1 = 9,
+};
+
+struct tpg_testgen_config {
+ enum tpg_testgen_mode mode;
+ const char * const*modes;
+ u8 nmodes;
+};
+
+struct tpg_format_info {
+ u32 code;
+ u8 data_type;
+ u8 encode_format;
+ u8 bpp;
+};
+
+struct tpg_formats {
+ unsigned int nformats;
+ const struct tpg_format_info *formats;
+};
+
+struct tpg_device;
+
+struct tpg_hw_ops {
+ int (*configure_stream)(struct tpg_device *tpg, u8 enable);
+ int (*configure_testgen_pattern)(struct tpg_device *tpg, s32 val);
+ u32 (*hw_version)(struct tpg_device *tpg);
+ int (*reset)(struct tpg_device *tpg);
+ void (*subdev_init)(struct tpg_device *tpg);
+};
+
+struct tpg_subdev_resources {
+ u8 lane_cnt;
+ const struct tpg_formats *formats;
+ const struct tpg_hw_ops *hw_ops;
+};
+
+struct tpg_device {
+ struct camss *camss;
+ u8 id;
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ void __iomem *base;
+ struct camss_clock *clock;
+ int nclocks;
+ struct tpg_testgen_config testgen;
+ struct v4l2_mbus_framefmt fmt;
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *testgen_mode;
+ const struct tpg_subdev_resources *res;
+ u32 hw_version;
+};
+
+struct camss_subdev_resources;
+
+const struct tpg_format_info *tpg_get_fmt_entry(const struct tpg_format_info *formats,
+ unsigned int nformats,
+ u32 code);
+
+int msm_tpg_subdev_init(struct camss *camss,
+ struct tpg_device *tpg,
+ const struct camss_subdev_resources *res, u8 id);
+
+int msm_tpg_register_entity(struct tpg_device *tpg,
+ struct v4l2_device *v4l2_dev);
+
+void msm_tpg_unregister_entity(struct tpg_device *tpg);
+
+extern const struct tpg_formats tpg_formats_gen1;
+
+extern const struct tpg_hw_ops tpg_ops_gen1;
+
+#endif /* QC_MSM_CAMSS_TPG_H */
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-340.c b/drivers/media/platform/qcom/camss/camss-vfe-340.c
index 30d7630b3e8b..2526d568686d 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe-340.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe-340.c
@@ -54,6 +54,7 @@
#define TFE_BUS_CLIENT_CFG(c) BUS_REG(0x200 + (c) * 0x100)
#define TFE_BUS_CLIENT_CFG_EN BIT(0)
+#define TFE_BUS_CLIENT_CFG_AUTORECOVER BIT(4)
#define TFE_BUS_CLIENT_CFG_MODE_FRAME BIT(16)
#define TFE_BUS_IMAGE_ADDR(c) BUS_REG(0x204 + (c) * 0x100)
#define TFE_BUS_FRAME_INCR(c) BUS_REG(0x208 + (c) * 0x100)
@@ -63,30 +64,36 @@
#define TFE_BUS_IMAGE_CFG_2(c) BUS_REG(0x214 + (c) * 0x100)
#define TFE_BUS_IMAGE_CFG_2_DEFAULT 0xffff
#define TFE_BUS_PACKER_CFG(c) BUS_REG(0x218 + (c) * 0x100)
+#define TFE_BUS_PACKER_CFG_FMT_PLAIN8 0x1
#define TFE_BUS_PACKER_CFG_FMT_PLAIN64 0xa
+#define TFE_BUS_PACKER_CFG_FMT_MIPI10 0xc
+#define TFE_BUS_PACKER_CFG_FMT_MIPI12 0xd
#define TFE_BUS_IRQ_SUBSAMPLE_CFG_0(c) BUS_REG(0x230 + (c) * 0x100)
#define TFE_BUS_IRQ_SUBSAMPLE_CFG_1(c) BUS_REG(0x234 + (c) * 0x100)
#define TFE_BUS_FRAMEDROP_CFG_0(c) BUS_REG(0x238 + (c) * 0x100)
#define TFE_BUS_FRAMEDROP_CFG_1(c) BUS_REG(0x23c + (c) * 0x100)
-/*
- * TODO: differentiate the port id based on requested type of RDI, BHIST etc
- *
- * TFE write master IDs (clients)
- *
- * BAYER 0
- * IDEAL_RAW 1
- * STATS_TINTLESS_BG 2
- * STATS_BHIST 3
- * STATS_AWB_BG 4
- * STATS_AEC_BG 5
- * STATS_BAF 6
- * RDI0 7
- * RDI1 8
- * RDI2 9
- */
-#define RDI_WM(n) (7 + (n))
-#define TFE_WM_NUM 10
+#define PP_CROP_REG(a) (0x2800 + (a))
+#define TFE_PP_CROP_CFG PP_CROP_REG(0x60)
+#define TFE_PP_CROP_CFG_EN (BIT(0) | BIT(9))
+#define TFE_PP_CROP_LINE_CFG PP_CROP_REG(0x68)
+#define TFE_PP_CROP_FIRST GENMASK(29, 16)
+#define TFE_PP_CROP_LAST GENMASK(13, 0)
+#define TFE_PP_CROP_PIX_CFG PP_CROP_REG(0x6C)
+
+enum tfe_client {
+ TFE_CLI_BAYER,
+ TFE_CLI_IDEAL_RAW,
+ TFE_CLI_STATS_TINTLESS_BG,
+ TFE_CLI_STATS_BHIST,
+ TFE_CLI_STATS_AWB_BG,
+ TFE_CLI_STATS_AEC_BG,
+ TFE_CLI_STATS_BAF,
+ TFE_CLI_RDI0,
+ TFE_CLI_RDI1,
+ TFE_CLI_RDI2,
+ TFE_CLI_NUM
+};
enum tfe_iface {
TFE_IFACE_PIX,
@@ -108,6 +115,13 @@ enum tfe_subgroups {
TFE_SUBGROUP_NUM
};
+static enum tfe_client tfe_wm_client_map[VFE_LINE_NUM_MAX] = {
+ [VFE_LINE_RDI0] = TFE_CLI_RDI0,
+ [VFE_LINE_RDI1] = TFE_CLI_RDI1,
+ [VFE_LINE_RDI2] = TFE_CLI_RDI2,
+ [VFE_LINE_PIX] = TFE_CLI_BAYER,
+};
+
static enum tfe_iface tfe_line_iface_map[VFE_LINE_NUM_MAX] = {
[VFE_LINE_RDI0] = TFE_IFACE_RDI0,
[VFE_LINE_RDI1] = TFE_IFACE_RDI1,
@@ -209,10 +223,10 @@ static irqreturn_t vfe_isr(int irq, void *dev)
status = readl_relaxed(vfe->base + TFE_BUS_OVERFLOW_STATUS);
if (status) {
writel_relaxed(status, vfe->base + TFE_BUS_STATUS_CLEAR);
- for (i = 0; i < TFE_WM_NUM; i++) {
+ for (i = 0; i < TFE_CLI_NUM; i++) {
if (status & BIT(i))
dev_err_ratelimited(vfe->camss->dev,
- "VFE%u: bus overflow for wm %u\n",
+ "VFE%u: bus overflow for client %u\n",
vfe->id, i);
}
}
@@ -235,49 +249,99 @@ static void vfe_enable_irq(struct vfe_device *vfe)
TFE_BUS_IRQ_MASK_0_IMG_VIOL, vfe->base + TFE_BUS_IRQ_MASK_0);
}
-static void vfe_wm_update(struct vfe_device *vfe, u8 rdi, u32 addr,
+static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr,
struct vfe_line *line)
{
- u8 wm = RDI_WM(rdi);
+ u8 client = tfe_wm_client_map[wm];
+
+ writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(client));
+}
+
+static u32 vfe_packer_format(struct vfe_device *vfe, u32 pixelformat)
+{
+ const struct camss_formats *fmt = vfe->res->formats_rdi;
+ unsigned int bpp = 0;
+ int i;
- writel_relaxed(addr, vfe->base + TFE_BUS_IMAGE_ADDR(wm));
+ for (i = 0; i < fmt->nformats; i++) {
+ if (fmt->formats[i].pixelformat == pixelformat) {
+ bpp = fmt->formats[i].mbus_bpp;
+ break;
+ }
+ }
+
+ switch (bpp) {
+ case 10:
+ return TFE_BUS_PACKER_CFG_FMT_MIPI10;
+ case 12:
+ return TFE_BUS_PACKER_CFG_FMT_MIPI12;
+ default:
+ return TFE_BUS_PACKER_CFG_FMT_PLAIN8;
+ }
}
-static void vfe_wm_start(struct vfe_device *vfe, u8 rdi, struct vfe_line *line)
+static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line)
{
struct v4l2_pix_format_mplane *pix = &line->video_out.active_fmt.fmt.pix_mp;
u32 stride = pix->plane_fmt[0].bytesperline;
- u8 wm = RDI_WM(rdi);
-
- /* Configuration for plain RDI frames */
- writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_0(wm));
- writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(wm));
- writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT, vfe->base + TFE_BUS_IMAGE_CFG_2(wm));
- writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(wm));
- writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64, vfe->base + TFE_BUS_PACKER_CFG(wm));
+ u8 client = tfe_wm_client_map[wm];
+ u32 cfg = TFE_BUS_CLIENT_CFG_EN;
+
+ if (client == TFE_CLI_BAYER) { /* PIX - Line based */
+ struct v4l2_rect *crop = &line->crop;
+
+ /* Cropping */
+ writel_relaxed(TFE_PP_CROP_CFG_EN, vfe->base + TFE_PP_CROP_CFG);
+ writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->top) |
+ FIELD_PREP(TFE_PP_CROP_LAST, crop->top + crop->height - 1),
+ vfe->base + TFE_PP_CROP_LINE_CFG);
+ writel_relaxed(FIELD_PREP(TFE_PP_CROP_FIRST, crop->left) |
+ FIELD_PREP(TFE_PP_CROP_LAST, crop->left + crop->width - 1),
+ vfe->base + TFE_PP_CROP_PIX_CFG);
+
+ /* Write Engine */
+ writel_relaxed(pix->width | (pix->height << 16),
+ vfe->base + TFE_BUS_IMAGE_CFG_0(client));
+ writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client));
+ writel_relaxed(stride, vfe->base + TFE_BUS_IMAGE_CFG_2(client));
+ writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client));
+ writel_relaxed(vfe_packer_format(vfe, pix->pixelformat),
+ vfe->base + TFE_BUS_PACKER_CFG(client));
+
+ cfg |= TFE_BUS_CLIENT_CFG_AUTORECOVER;
+ } else { /* RDI - Frame based */
+ writel_relaxed(TFE_BUS_IMAGE_CFG_0_DEFAULT,
+ vfe->base + TFE_BUS_IMAGE_CFG_0(client));
+ writel_relaxed(0u, vfe->base + TFE_BUS_IMAGE_CFG_1(client));
+ writel_relaxed(TFE_BUS_IMAGE_CFG_2_DEFAULT,
+ vfe->base + TFE_BUS_IMAGE_CFG_2(client));
+ writel_relaxed(stride * pix->height, vfe->base + TFE_BUS_FRAME_INCR(client));
+ writel_relaxed(TFE_BUS_PACKER_CFG_FMT_PLAIN64,
+ vfe->base + TFE_BUS_PACKER_CFG(client));
+ cfg |= TFE_BUS_CLIENT_CFG_MODE_FRAME;
+ }
/* No dropped frames, one irq per frame */
- writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(wm));
- writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(wm));
- writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(wm));
- writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(wm));
+ writel_relaxed(0, vfe->base + TFE_BUS_FRAMEDROP_CFG_0(client));
+ writel_relaxed(1, vfe->base + TFE_BUS_FRAMEDROP_CFG_1(client));
+ writel_relaxed(0, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_0(client));
+ writel_relaxed(1, vfe->base + TFE_BUS_IRQ_SUBSAMPLE_CFG_1(client));
vfe_enable_irq(vfe);
- writel(TFE_BUS_CLIENT_CFG_EN | TFE_BUS_CLIENT_CFG_MODE_FRAME,
- vfe->base + TFE_BUS_CLIENT_CFG(wm));
+ writel(cfg, vfe->base + TFE_BUS_CLIENT_CFG(client));
- dev_dbg(vfe->camss->dev, "VFE%u: Started RDI%u width %u height %u stride %u\n",
- vfe->id, rdi, pix->width, pix->height, stride);
+ dev_dbg(vfe->camss->dev, "VFE%u: Started client %u width %u height %u stride %u\n",
+ vfe->id, client, pix->width, pix->height, stride);
}
-static void vfe_wm_stop(struct vfe_device *vfe, u8 rdi)
+static void vfe_wm_stop(struct vfe_device *vfe, u8 wm)
{
- u8 wm = RDI_WM(rdi);
+ u8 client = tfe_wm_client_map[wm];
- writel(0, vfe->base + TFE_BUS_CLIENT_CFG(wm));
+ writel(0, vfe->base + TFE_BUS_CLIENT_CFG(client));
- dev_dbg(vfe->camss->dev, "VFE%u: Stopped RDI%u\n", vfe->id, rdi);
+ dev_dbg(vfe->camss->dev, "VFE%u: Stopped client %u\n", vfe->id, client);
}
static const struct camss_video_ops vfe_video_ops_520 = {
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 5baf0e3d4bc4..319d19158988 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -343,6 +343,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
case CAMSS_660:
case CAMSS_2290:
case CAMSS_6150:
+ case CAMSS_6350:
case CAMSS_7280:
case CAMSS_8x96:
case CAMSS_8250:
@@ -1997,12 +1998,13 @@ static const struct media_entity_operations vfe_media_ops = {
.link_validate = v4l2_subdev_link_validate,
};
-static int vfe_bpl_align(struct vfe_device *vfe)
+static int vfe_bpl_align_rdi(struct vfe_device *vfe)
{
int ret = 8;
switch (vfe->camss->res->version) {
case CAMSS_6150:
+ case CAMSS_6350:
case CAMSS_7280:
case CAMSS_8250:
case CAMSS_8280XP:
@@ -2021,6 +2023,24 @@ static int vfe_bpl_align(struct vfe_device *vfe)
return ret;
}
+static int vfe_bpl_align_pix(struct vfe_device *vfe)
+{
+ int ret = 16;
+
+ switch (vfe->camss->res->version) {
+ case CAMSS_2290:
+ /* The alignment/bpl depends solely on the pixel format and is
+ * computed dynamically in camss_format_get_bpl_alignment().
+ */
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
/*
* msm_vfe_register_entities - Register subdev node for VFE module
* @vfe: VFE device
@@ -2053,7 +2073,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
v4l2_subdev_init(sd, &vfe_v4l2_ops);
sd->internal_ops = &vfe_v4l2_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
- if (i == VFE_LINE_PIX)
+ if (i == VFE_LINE_PIX && vfe->res->is_lite == false)
snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s",
MSM_VFE_NAME, vfe->id, "pix");
else
@@ -2087,11 +2107,12 @@ int msm_vfe_register_entities(struct vfe_device *vfe,
}
video_out->ops = &vfe->video_ops;
- video_out->bpl_alignment = vfe_bpl_align(vfe);
- video_out->line_based = 0;
if (i == VFE_LINE_PIX) {
- video_out->bpl_alignment = 16;
+ video_out->bpl_alignment = vfe_bpl_align_pix(vfe);
video_out->line_based = 1;
+ } else {
+ video_out->bpl_alignment = vfe_bpl_align_rdi(vfe);
+ video_out->line_based = 0;
}
video_out->nformats = vfe->line[i].nformats;
diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index 831486e14754..0852eb6f1315 100644
--- a/drivers/media/platform/qcom/camss/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -47,6 +47,9 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
unsigned int i;
u32 bytesperline;
+ if (!alignment)
+ alignment = camss_format_get_bpl_alignment(f);
+
memset(pix, 0, sizeof(*pix));
v4l2_fill_pix_format_mplane(pix, mbus);
pix->pixelformat = f->pixelformat;
@@ -54,7 +57,7 @@ static int video_mbus_to_pix_mp(const struct v4l2_mbus_framefmt *mbus,
for (i = 0; i < pix->num_planes; i++) {
bytesperline = pix->width / f->hsub[i].numerator *
f->hsub[i].denominator * f->bpp[i] / 8;
- bytesperline = ALIGN(bytesperline, alignment);
+ bytesperline = roundup(bytesperline, alignment);
pix->plane_fmt[i].bytesperline = bytesperline;
pix->plane_fmt[i].sizeimage = pix->height /
f->vsub[i].numerator * f->vsub[i].denominator *
@@ -215,6 +218,12 @@ static int video_check_format(struct camss_video *video)
if (ret < 0)
return ret;
+ dev_dbg(video->camss->dev,
+ "%s: format is (%ux%u %p4cc/%up field:%u), trying (%ux%u %p4cc/%up field:%u)",
+ video->vdev.name, sd_pix->width, sd_pix->height, &sd_pix->pixelformat,
+ sd_pix->num_planes, sd_pix->field, pix->width, pix->height, &pix->pixelformat,
+ pix->num_planes, pix->field);
+
if (pix->pixelformat != sd_pix->pixelformat ||
pix->height != sd_pix->height ||
pix->width != sd_pix->width ||
@@ -453,6 +462,7 @@ static int video_g_fmt(struct file *file, void *fh, struct v4l2_format *f)
static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
{
+ unsigned int alignment = video->bpl_alignment;
struct v4l2_pix_format_mplane *pix_mp;
const struct camss_format_info *fi;
struct v4l2_plane_pix_format *p;
@@ -485,6 +495,9 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
width = pix_mp->width;
height = pix_mp->height;
+ if (!alignment)
+ alignment = camss_format_get_bpl_alignment(fi);
+
memset(pix_mp, 0, sizeof(*pix_mp));
pix_mp->pixelformat = fi->pixelformat;
@@ -494,7 +507,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
for (i = 0; i < pix_mp->num_planes; i++) {
bpl = pix_mp->width / fi->hsub[i].numerator *
fi->hsub[i].denominator * fi->bpp[i] / 8;
- bpl = ALIGN(bpl, video->bpl_alignment);
+ bpl = roundup(bpl, alignment);
pix_mp->plane_fmt[i].bytesperline = bpl;
pix_mp->plane_fmt[i].sizeimage = pix_mp->height /
fi->vsub[i].numerator * fi->vsub[i].denominator * bpl;
@@ -519,7 +532,7 @@ static int __video_try_fmt(struct camss_video *video, struct v4l2_format *f)
lines = p->sizeimage / p->bytesperline;
if (p->bytesperline < bytesperline[i])
- p->bytesperline = ALIGN(bytesperline[i], 8);
+ p->bytesperline = roundup(bytesperline[i], alignment);
if (p->sizeimage < p->bytesperline * lines)
p->sizeimage = p->bytesperline * lines;
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 9335636d7c4d..2123f6388e3d 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -1703,6 +1703,253 @@ static const struct resources_icc icc_res_sm6150[] = {
},
};
+static const struct camss_subdev_resources csiphy_res_sm6350[] = {
+ /* CSIPHY0 */
+ {
+ .regulators = {
+ { .supply = "vdd-csiphy0-0p9", .init_load_uA = 80000 },
+ { .supply = "vdd-csiphy0-1p25", .init_load_uA = 80000 },
+ },
+ .clock = { "csiphy0", "csiphy0_timer" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy0" },
+ .interrupt = { "csiphy0" },
+ .csiphy = {
+ .id = 0,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY1 */
+ {
+ .regulators = {
+ { .supply = "vdd-csiphy1-0p9", .init_load_uA = 80000 },
+ { .supply = "vdd-csiphy1-1p25", .init_load_uA = 80000 },
+ },
+ .clock = { "csiphy1", "csiphy1_timer" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy1" },
+ .interrupt = { "csiphy1" },
+ .csiphy = {
+ .id = 1,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY2 */
+ {
+ .regulators = {
+ { .supply = "vdd-csiphy2-0p9", .init_load_uA = 80000 },
+ { .supply = "vdd-csiphy2-1p25", .init_load_uA = 80000 },
+ },
+ .clock = { "csiphy2", "csiphy2_timer" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy2" },
+ .interrupt = { "csiphy2" },
+ .csiphy = {
+ .id = 2,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ },
+ /* CSIPHY3 */
+ {
+ .regulators = {
+ { .supply = "vdd-csiphy3-0p9", .init_load_uA = 80000 },
+ { .supply = "vdd-csiphy3-1p25", .init_load_uA = 80000 },
+ },
+ .clock = { "csiphy3", "csiphy3_timer" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 300000000 } },
+ .reg = { "csiphy3" },
+ .interrupt = { "csiphy3" },
+ .csiphy = {
+ .id = 3,
+ .hw_ops = &csiphy_ops_3ph_1_0,
+ .formats = &csiphy_formats_sdm845
+ }
+ }
+};
+
+static const struct camss_subdev_resources csid_res_sm6350[] = {
+ /* CSID0 */
+ {
+ .regulators = {},
+ .clock = { "vfe0_csid", "vfe0_cphy_rx", "vfe0" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 0 },
+ { 320000000, 404000000, 480000000, 600000000 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID1 */
+ {
+ .regulators = {},
+ .clock = { "vfe1_csid", "vfe1_cphy_rx", "vfe1" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 0 },
+ { 320000000, 404000000, 480000000, 600000000 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID2 */
+ {
+ .regulators = {},
+ .clock = { "vfe2_csid", "vfe2_cphy_rx", "vfe2" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 0 },
+ { 320000000, 404000000, 480000000, 600000000 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" },
+ .csid = {
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ },
+ /* CSID3 (lite) */
+ {
+ .regulators = {},
+ .clock = { "vfe_lite_csid", "vfe_lite_cphy_rx", "vfe_lite" },
+ .clock_rate = { { 300000000, 384000000, 400000000 },
+ { 0 },
+ { 400000000, 480000000 } },
+ .reg = { "csid_lite" },
+ .interrupt = { "csid_lite" },
+ .csid = {
+ .is_lite = true,
+ .hw_ops = &csid_ops_gen2,
+ .parent_dev_ops = &vfe_parent_dev_ops,
+ .formats = &csid_formats_gen2
+ }
+ }
+};
+
+static const struct camss_subdev_resources vfe_res_sm6350[] = {
+ /* VFE0 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "camnoc_axi", "vfe0",
+ "vfe0_axi", "cam_axi", "soc_ahb" },
+ .clock_rate = { { 19200000 },
+ { 0 },
+ { 320000000, 404000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife0",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE1 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "camnoc_axi", "vfe1",
+ "vfe1_axi", "cam_axi", "soc_ahb" },
+ .clock_rate = { { 19200000 },
+ { 0 },
+ { 320000000, 404000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife1",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE2 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "camnoc_axi", "vfe2",
+ "vfe2_axi", "cam_axi", "soc_ahb" },
+ .clock_rate = { { 19200000 },
+ { 0 },
+ { 320000000, 404000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe2" },
+ .interrupt = { "vfe2" },
+ .vfe = {
+ .line_num = 3,
+ .has_pd = true,
+ .pd_name = "ife2",
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+ /* VFE3 (lite) */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "camnoc_axi", "vfe_lite",
+ "cam_axi", "soc_ahb" },
+ .clock_rate = { { 19200000 },
+ { 0 },
+ { 400000000, 480000000 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe_lite" },
+ .interrupt = { "vfe_lite" },
+ .vfe = {
+ .is_lite = true,
+ .line_num = 4,
+ .hw_ops = &vfe_ops_170,
+ .formats_rdi = &vfe_formats_rdi_845,
+ .formats_pix = &vfe_formats_pix_845
+ }
+ },
+};
+
+static const struct resources_icc icc_res_sm6350[] = {
+ {
+ .name = "ahb",
+ .icc_bw_tbl.avg = 0,
+ .icc_bw_tbl.peak = 300000,
+ },
+ {
+ .name = "hf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "sf_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+ {
+ .name = "sf_icp_mnoc",
+ .icc_bw_tbl.avg = 2097152,
+ .icc_bw_tbl.peak = 2097152,
+ },
+};
+
static const struct camss_subdev_resources csiphy_res_8250[] = {
/* CSIPHY0 */
{
@@ -3559,6 +3806,54 @@ static const struct camss_subdev_resources csiphy_res_8775p[] = {
},
};
+static const struct camss_subdev_resources tpg_res_8775p[] = {
+ /* TPG0 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "csiphy_rx" },
+ .clock_rate = {
+ { 0 },
+ { 400000000 },
+ },
+ .reg = { "tpg0" },
+ .tpg = {
+ .lane_cnt = 4,
+ .formats = &tpg_formats_gen1,
+ .hw_ops = &tpg_ops_gen1
+ }
+ },
+ /* TPG1 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "csiphy_rx" },
+ .clock_rate = {
+ { 0 },
+ { 400000000 },
+ },
+ .reg = { "tpg1" },
+ .tpg = {
+ .lane_cnt = 4,
+ .formats = &tpg_formats_gen1,
+ .hw_ops = &tpg_ops_gen1
+ }
+ },
+ /* TPG2 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "csiphy_rx" },
+ .clock_rate = {
+ { 0 },
+ { 400000000 },
+ },
+ .reg = { "tpg2" },
+ .tpg = {
+ .lane_cnt = 4,
+ .formats = &tpg_formats_gen1,
+ .hw_ops = &tpg_ops_gen1
+ }
+ },
+};
+
static const struct camss_subdev_resources csid_res_8775p[] = {
/* CSID0 */
{
@@ -3963,6 +4258,54 @@ static const struct camss_subdev_resources csiphy_res_x1e80100[] = {
},
};
+static const struct camss_subdev_resources tpg_res_x1e80100[] = {
+ /* TPG0 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "csid_csiphy_rx" },
+ .clock_rate = {
+ { 0 },
+ { 400000000 },
+ },
+ .reg = { "csitpg0" },
+ .tpg = {
+ .lane_cnt = 4,
+ .formats = &tpg_formats_gen1,
+ .hw_ops = &tpg_ops_gen1
+ }
+ },
+ /* TPG1 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "csid_csiphy_rx" },
+ .clock_rate = {
+ { 0 },
+ { 400000000 },
+ },
+ .reg = { "csitpg1" },
+ .tpg = {
+ .lane_cnt = 4,
+ .formats = &tpg_formats_gen1,
+ .hw_ops = &tpg_ops_gen1
+ }
+ },
+ /* TPG2 */
+ {
+ .regulators = {},
+ .clock = { "cpas_ahb", "csid_csiphy_rx" },
+ .clock_rate = {
+ { 0 },
+ { 400000000 },
+ },
+ .reg = { "csitpg2" },
+ .tpg = {
+ .lane_cnt = 4,
+ .formats = &tpg_formats_gen1,
+ .hw_ops = &tpg_ops_gen1
+ }
+ },
+};
+
static const struct camss_subdev_resources csid_res_x1e80100[] = {
/* CSID0 */
{
@@ -4076,7 +4419,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = {
.clock = {"camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
"cpas_fast_ahb", "cpas_vfe0", "vfe0_fast_ahb",
"vfe0" },
- .clock_rate = { { 0 },
+ .clock_rate = { { 400000000 },
{ 0 },
{ 0 },
{ 0 },
@@ -4100,7 +4443,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = {
.clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
"cpas_fast_ahb", "cpas_vfe1", "vfe1_fast_ahb",
"vfe1" },
- .clock_rate = { { 0 },
+ .clock_rate = { { 400000000 },
{ 0 },
{ 0 },
{ 0 },
@@ -4124,7 +4467,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = {
.clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
"vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite",
"vfe_lite_csid" },
- .clock_rate = { { 0 },
+ .clock_rate = { { 400000000 },
{ 0 },
{ 0 },
{ 0 },
@@ -4147,7 +4490,7 @@ static const struct camss_subdev_resources vfe_res_x1e80100[] = {
.clock = { "camnoc_rt_axi", "camnoc_nrt_axi", "cpas_ahb",
"vfe_lite_ahb", "cpas_vfe_lite", "vfe_lite",
"vfe_lite_csid" },
- .clock_rate = { { 0 },
+ .clock_rate = { { 400000000 },
{ 0 },
{ 0 },
{ 0 },
@@ -4501,6 +4844,19 @@ static int camss_init_subdevices(struct camss *camss)
}
}
+ if (camss->tpg) {
+ for (i = 0; i < camss->res->tpg_num; i++) {
+ ret = msm_tpg_subdev_init(camss, &camss->tpg[i],
+ &res->tpg_res[i], i);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to init tpg%d sub-device: %d\n",
+ i, ret);
+ return ret;
+ }
+ }
+ }
+
/* note: SM8250 requires VFE to be initialized before CSID */
for (i = 0; i < camss->res->vfe_num; i++) {
ret = msm_vfe_subdev_init(camss, &camss->vfe[i],
@@ -4589,6 +4945,23 @@ static int camss_link_entities(struct camss *camss)
}
}
+ for (i = 0; i < camss->res->tpg_num; i++) {
+ for (j = 0; j < camss->res->csid_num; j++) {
+ ret = media_create_pad_link(&camss->tpg[i].subdev.entity,
+ MSM_TPG_PAD_SRC,
+ &camss->csid[j].subdev.entity,
+ MSM_CSID_PAD_SINK,
+ 0);
+ if (ret < 0) {
+ camss_link_err(camss,
+ camss->tpg[i].subdev.entity.name,
+ camss->csid[j].subdev.entity.name,
+ ret);
+ return ret;
+ }
+ }
+ }
+
if (camss->ispif) {
for (i = 0; i < camss->res->csid_num; i++) {
for (j = 0; j < camss->ispif->line_num; j++) {
@@ -4693,6 +5066,19 @@ static int camss_register_entities(struct camss *camss)
}
}
+ if (camss->tpg) {
+ for (i = 0; i < camss->res->tpg_num; i++) {
+ ret = msm_tpg_register_entity(&camss->tpg[i],
+ &camss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to register tpg%d entity: %d\n",
+ i, ret);
+ goto err_reg_tpg;
+ }
+ }
+ }
+
for (i = 0; i < camss->res->csid_num; i++) {
ret = msm_csid_register_entity(&camss->csid[i],
&camss->v4l2_dev);
@@ -4736,6 +5122,13 @@ err_reg_csid:
for (i--; i >= 0; i--)
msm_csid_unregister_entity(&camss->csid[i]);
+ i = camss->res->tpg_num;
+err_reg_tpg:
+ if (camss->tpg) {
+ for (i--; i >= 0; i--)
+ msm_tpg_unregister_entity(&camss->tpg[i]);
+ }
+
i = camss->res->csiphy_num;
err_reg_csiphy:
for (i--; i >= 0; i--)
@@ -4757,6 +5150,11 @@ static void camss_unregister_entities(struct camss *camss)
for (i = 0; i < camss->res->csiphy_num; i++)
msm_csiphy_unregister_entity(&camss->csiphy[i]);
+ if (camss->tpg) {
+ for (i = 0; i < camss->res->tpg_num; i++)
+ msm_tpg_unregister_entity(&camss->tpg[i]);
+ }
+
for (i = 0; i < camss->res->csid_num; i++)
msm_csid_unregister_entity(&camss->csid[i]);
@@ -4975,6 +5373,13 @@ static int camss_probe(struct platform_device *pdev)
if (!camss->csiphy)
return -ENOMEM;
+ if (camss->res->tpg_num > 0) {
+ camss->tpg = devm_kcalloc(dev, camss->res->tpg_num,
+ sizeof(*camss->tpg), GFP_KERNEL);
+ if (!camss->tpg)
+ return -ENOMEM;
+ }
+
camss->csid = devm_kcalloc(dev, camss->res->csid_num, sizeof(*camss->csid),
GFP_KERNEL);
if (!camss->csid)
@@ -5164,11 +5569,13 @@ static const struct camss_resources qcs8300_resources = {
.version = CAMSS_8300,
.pd_name = "top",
.csiphy_res = csiphy_res_8300,
+ .tpg_res = tpg_res_8775p,
.csid_res = csid_res_8775p,
.csid_wrapper_res = &csid_wrapper_res_sm8550,
.vfe_res = vfe_res_8775p,
.icc_res = icc_res_qcs8300,
.csiphy_num = ARRAY_SIZE(csiphy_res_8300),
+ .tpg_num = ARRAY_SIZE(tpg_res_8775p),
.csid_num = ARRAY_SIZE(csid_res_8775p),
.vfe_num = ARRAY_SIZE(vfe_res_8775p),
.icc_path_num = ARRAY_SIZE(icc_res_qcs8300),
@@ -5178,11 +5585,13 @@ static const struct camss_resources sa8775p_resources = {
.version = CAMSS_8775P,
.pd_name = "top",
.csiphy_res = csiphy_res_8775p,
+ .tpg_res = tpg_res_8775p,
.csid_res = csid_res_8775p,
.csid_wrapper_res = &csid_wrapper_res_sm8550,
.vfe_res = vfe_res_8775p,
.icc_res = icc_res_sa8775p,
.csiphy_num = ARRAY_SIZE(csiphy_res_8775p),
+ .tpg_num = ARRAY_SIZE(tpg_res_8775p),
.csid_num = ARRAY_SIZE(csid_res_8775p),
.vfe_num = ARRAY_SIZE(vfe_res_8775p),
.icc_path_num = ARRAY_SIZE(icc_res_sa8775p),
@@ -5233,6 +5642,19 @@ static const struct camss_resources sm6150_resources = {
.vfe_num = ARRAY_SIZE(vfe_res_sm6150),
};
+static const struct camss_resources sm6350_resources = {
+ .version = CAMSS_6350,
+ .pd_name = "top",
+ .csiphy_res = csiphy_res_sm6350,
+ .csid_res = csid_res_sm6350,
+ .vfe_res = vfe_res_sm6350,
+ .icc_res = icc_res_sm6350,
+ .icc_path_num = ARRAY_SIZE(icc_res_sm6350),
+ .csiphy_num = ARRAY_SIZE(csiphy_res_sm6350),
+ .csid_num = ARRAY_SIZE(csid_res_sm6350),
+ .vfe_num = ARRAY_SIZE(vfe_res_sm6350),
+};
+
static const struct camss_resources sm8250_resources = {
.version = CAMSS_8250,
.pd_name = "top",
@@ -5305,12 +5727,14 @@ static const struct camss_resources x1e80100_resources = {
.version = CAMSS_X1E80100,
.pd_name = "top",
.csiphy_res = csiphy_res_x1e80100,
+ .tpg_res = tpg_res_x1e80100,
.csid_res = csid_res_x1e80100,
.vfe_res = vfe_res_x1e80100,
.csid_wrapper_res = &csid_wrapper_res_x1e80100,
.icc_res = icc_res_x1e80100,
.icc_path_num = ARRAY_SIZE(icc_res_x1e80100),
.csiphy_num = ARRAY_SIZE(csiphy_res_x1e80100),
+ .tpg_num = ARRAY_SIZE(tpg_res_x1e80100),
.csid_num = ARRAY_SIZE(csid_res_x1e80100),
.vfe_num = ARRAY_SIZE(vfe_res_x1e80100),
};
@@ -5329,6 +5753,7 @@ static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,sdm670-camss", .data = &sdm670_resources },
{ .compatible = "qcom,sdm845-camss", .data = &sdm845_resources },
{ .compatible = "qcom,sm6150-camss", .data = &sm6150_resources },
+ { .compatible = "qcom,sm6350-camss", .data = &sm6350_resources },
{ .compatible = "qcom,sm8250-camss", .data = &sm8250_resources },
{ .compatible = "qcom,sm8550-camss", .data = &sm8550_resources },
{ .compatible = "qcom,sm8650-camss", .data = &sm8650_resources },
diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 6d048414c919..93d691c8ac63 100644
--- a/drivers/media/platform/qcom/camss/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -21,6 +21,7 @@
#include "camss-csid.h"
#include "camss-csiphy.h"
#include "camss-ispif.h"
+#include "camss-tpg.h"
#include "camss-vfe.h"
#include "camss-format.h"
@@ -52,6 +53,7 @@ struct camss_subdev_resources {
char *interrupt[CAMSS_RES_MAX];
union {
struct csiphy_subdev_resources csiphy;
+ struct tpg_subdev_resources tpg;
struct csid_subdev_resources csid;
struct vfe_subdev_resources vfe;
};
@@ -81,6 +83,7 @@ enum camss_version {
CAMSS_660,
CAMSS_2290,
CAMSS_6150,
+ CAMSS_6350,
CAMSS_7280,
CAMSS_8x16,
CAMSS_8x39,
@@ -105,6 +108,7 @@ struct camss_resources {
enum camss_version version;
const char *pd_name;
const struct camss_subdev_resources *csiphy_res;
+ const struct camss_subdev_resources *tpg_res;
const struct camss_subdev_resources *csid_res;
const struct camss_subdev_resources *ispif_res;
const struct camss_subdev_resources *vfe_res;
@@ -112,6 +116,7 @@ struct camss_resources {
const struct resources_icc *icc_res;
const unsigned int icc_path_num;
const unsigned int csiphy_num;
+ const unsigned int tpg_num;
const unsigned int csid_num;
const unsigned int vfe_num;
};
@@ -122,6 +127,7 @@ struct camss {
struct media_device media_dev;
struct device *dev;
struct csiphy_device *csiphy;
+ struct tpg_device *tpg;
struct csid_device *csid;
struct ispif_device *ispif;
struct vfe_device *vfe;
diff --git a/drivers/media/platform/qcom/iris/Kconfig b/drivers/media/platform/qcom/iris/Kconfig
index 5498f48362d1..af78a1775937 100644
--- a/drivers/media/platform/qcom/iris/Kconfig
+++ b/drivers/media/platform/qcom/iris/Kconfig
@@ -5,6 +5,7 @@ config VIDEO_QCOM_IRIS
select V4L2_MEM2MEM_DEV
select QCOM_MDT_LOADER
select QCOM_SCM
+ select QCOM_UBWC_CONFIG
select VIDEOBUF2_DMA_CONTIG
help
This is a V4L2 driver for Qualcomm iris video accelerator
diff --git a/drivers/media/platform/qcom/iris/Makefile b/drivers/media/platform/qcom/iris/Makefile
index 2abbd3aeb4af..48e415cbc439 100644
--- a/drivers/media/platform/qcom/iris/Makefile
+++ b/drivers/media/platform/qcom/iris/Makefile
@@ -4,13 +4,16 @@ qcom-iris-objs += iris_buffer.o \
iris_ctrls.o \
iris_firmware.o \
iris_hfi_common.o \
+ iris_hfi_gen1.o \
iris_hfi_gen1_command.o \
iris_hfi_gen1_response.o \
+ iris_hfi_gen2.o \
iris_hfi_gen2_command.o \
iris_hfi_gen2_packet.o \
iris_hfi_gen2_response.o \
iris_hfi_queue.o \
- iris_platform_gen2.o \
+ iris_platform_vpu2.o \
+ iris_platform_vpu3x.o \
iris_power.o \
iris_probe.o \
iris_resources.o \
@@ -26,8 +29,4 @@ qcom-iris-objs += iris_buffer.o \
iris_vpu_buffer.o \
iris_vpu_common.o \
-ifeq ($(CONFIG_VIDEO_QCOM_VENUS),)
-qcom-iris-objs += iris_platform_gen1.o
-endif
-
obj-$(CONFIG_VIDEO_QCOM_IRIS) += qcom-iris.o
diff --git a/drivers/media/platform/qcom/iris/iris_buffer.c b/drivers/media/platform/qcom/iris/iris_buffer.c
index 1d53c7414b75..246ad0abbac3 100644
--- a/drivers/media/platform/qcom/iris/iris_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_buffer.c
@@ -15,8 +15,11 @@
#define MAX_WIDTH 4096
#define MAX_HEIGHT 2304
#define Y_STRIDE_ALIGN 128
+#define Y_STRIDE_ALIGN_P010 256
#define UV_STRIDE_ALIGN 128
+#define UV_STRIDE_ALIGN_P010 256
#define Y_SCANLINE_ALIGN 32
+#define Y_SCANLINE_ALIGN_QC10C 16
#define UV_SCANLINE_ALIGN 16
#define UV_SCANLINE_ALIGN_QC08C 32
#define META_STRIDE_ALIGNED 64
@@ -81,6 +84,63 @@ static u32 iris_yuv_buffer_size_nv12(struct iris_inst *inst)
}
/*
+ * P010:
+ * YUV 4:2:0 image with a plane of 10 bit Y samples followed
+ * by an interleaved U/V plane containing 10 bit 2x2 subsampled
+ * colour difference samples.
+ *
+ * <-Y/UV_Stride (aligned to 256)->
+ * <----- Width*2 ------->
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . ^ ^
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . Height |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | y_scanlines (aligned to 32)
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . | |
+ * Y Y Y Y Y Y Y Y Y Y Y Y . . . . V |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * U V U V U V U V U V U V . . . . ^
+ * U V U V U V U V U V U V . . . . |
+ * U V U V U V U V U V U V . . . . |
+ * U V U V U V U V U V U V . . . . uv_scanlines (aligned to 16)
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . . . --> Buffer size aligned to 4K
+ *
+ * y_stride : Width*2 aligned to 256
+ * uv_stride : Width*2 aligned to 256
+ * y_scanlines: Height aligned to 32
+ * uv_scanlines: Height/2 aligned to 16
+ * Total size = align((y_stride * y_scanlines
+ * + uv_stride * uv_scanlines , 4096)
+ *
+ * Note: All the alignments are hardware requirements.
+ */
+static u32 iris_yuv_buffer_size_p010(struct iris_inst *inst)
+{
+ u32 y_plane, uv_plane, y_stride, uv_stride, y_scanlines, uv_scanlines;
+ struct v4l2_format *f;
+
+ if (inst->domain == DECODER)
+ f = inst->fmt_dst;
+ else
+ f = inst->fmt_src;
+
+ y_stride = ALIGN(f->fmt.pix_mp.width * 2, Y_STRIDE_ALIGN_P010);
+ uv_stride = ALIGN(f->fmt.pix_mp.width * 2, UV_STRIDE_ALIGN_P010);
+ y_scanlines = ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN);
+ uv_scanlines = ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN);
+ y_plane = y_stride * y_scanlines;
+ uv_plane = uv_stride * uv_scanlines;
+
+ return ALIGN(y_plane + uv_plane, PIXELS_4K);
+}
+
+/*
* QC08C:
* Compressed Macro-tile format for NV12.
* Contains 4 planes in the following order -
@@ -204,6 +264,132 @@ static u32 iris_yuv_buffer_size_qc08c(struct iris_inst *inst)
return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K);
}
+/*
+ * QC10C:
+ * UBWC-compressed format for P010.
+ * Contains 4 planes in the following order -
+ * (A) Y_Meta_Plane
+ * (B) Y_UBWC_Plane
+ * (C) UV_Meta_Plane
+ * (D) UV_UBWC_Plane
+ *
+ * Y_Meta_Plane consists of meta information to decode compressed
+ * tile data in Y_UBWC_Plane.
+ * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+ * UBWC decoder block will use the Y_Meta_Plane data together with
+ * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples.
+ *
+ * UV_Meta_Plane consists of meta information to decode compressed
+ * tile data in UV_UBWC_Plane.
+ * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+ * UBWC decoder block will use UV_Meta_Plane data together with
+ * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2
+ * subsampled color difference samples.
+ *
+ * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+ * and randomly accessible. There is no dependency between tiles.
+ *
+ * <----- Y Meta stride -----> (aligned to 64)
+ * <-------- Width ----------> (aligned to 48)
+ * M M M M M M M M M M M M . . ^ ^
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . Height |
+ * M M M M M M M M M M M M . . | Meta_Y_Scanlines (aligned to 16)
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . V |
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * . . . . . . . . . . . . . . V
+ * <--Compressed tile Y stride --> (aligned to 256)
+ * <------- Width * 4/3 ---------> (aligned to 48)
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . ^ ^
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . Height |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | Macro_tile_Y_Scanlines (aligned to 16)
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . | |
+ * Y* Y* Y* Y* Y* Y* Y* Y* . . . . V |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * . . . . . . . . . . . . . . . . V
+ * <---- UV Meta stride ----> (aligned to 64)
+ * <----- Width / 2 --------> (aligned to 24)
+ * M M M M M M M M M M M M . . ^ ^
+ * M M M M M M M M M M M M . . | |
+ * M M M M M M M M M M M M . . Height/2 |
+ * M M M M M M M M M M M M . . V M_UV_Scanlines (aligned to 16)
+ * . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ * <--Compressed tile UV stride--> (aligned to 256)
+ * <------- Width * 4/3 ---------> (aligned to 48)
+ * U* V* U* V* U* V* U* V* . . . . ^
+ * U* V* U* V* U* V* U* V* . . . . |
+ * U* V* U* V* U* V* U* V* . . . . |
+ * U* V* U* V* U* V* U* V* . . . . UV_Scanlines (aligned to 16)
+ * . . . . . . . . . . . . . . . . |
+ * . . . . . . . . . . . . . . . . V
+ * . . . . . . . . . . . . . . . . -------> Buffer size aligned to 4k
+ *
+ * y_stride: width aligned to 256
+ * uv_stride: width aligned to 256
+ * y_scanlines: height aligned to 16
+ * uv_scanlines: height aligned to 16
+ * y_plane: buffer size aligned to 4096
+ * uv_plane: buffer size aligned to 4096
+ * y_meta_stride: width aligned to 64
+ * y_meta_scanlines: height aligned to 16
+ * y_meta_plane: buffer size aligned to 4096
+ * uv_meta_stride: width aligned to 64
+ * uv_meta_scanlines: height aligned to 16
+ * uv_meta_plane: buffer size aligned to 4096
+ *
+ * Total size = align( y_plane + uv_plane +
+ * y_meta_plane + uv_meta_plane, 4096)
+ *
+ * Note: All the alignments are hardware requirements.
+ */
+static u32 iris_yuv_buffer_size_qc10c(struct iris_inst *inst)
+{
+ u32 y_plane, uv_plane, y_stride, uv_stride;
+ u32 uv_meta_stride, uv_meta_plane;
+ u32 y_meta_stride, y_meta_plane;
+ struct v4l2_format *f;
+
+ if (inst->domain == DECODER)
+ f = inst->fmt_dst;
+ else
+ f = inst->fmt_src;
+
+ y_meta_stride = ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.width, 48),
+ META_STRIDE_ALIGNED);
+ y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(f->fmt.pix_mp.height, 4),
+ META_SCANLINE_ALIGNED);
+ y_meta_plane = ALIGN(y_meta_plane, PIXELS_4K);
+
+ y_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, Y_STRIDE_ALIGN_P010);
+ y_plane = ALIGN(y_stride * ALIGN(f->fmt.pix_mp.height, Y_SCANLINE_ALIGN_QC10C),
+ PIXELS_4K);
+
+ uv_meta_stride = ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.width + 1) >> 1, 24),
+ META_STRIDE_ALIGNED);
+ uv_meta_plane = uv_meta_stride *
+ ALIGN(DIV_ROUND_UP((f->fmt.pix_mp.height + 1) >> 1, 4),
+ META_SCANLINE_ALIGNED);
+ uv_meta_plane = ALIGN(uv_meta_plane, PIXELS_4K);
+
+ uv_stride = ALIGN(f->fmt.pix_mp.width * 4 / 3, UV_STRIDE_ALIGN_P010);
+ uv_plane = ALIGN(uv_stride * ALIGN((f->fmt.pix_mp.height + 1) >> 1, UV_SCANLINE_ALIGN),
+ PIXELS_4K);
+
+ return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane, PIXELS_4K);
+}
+
static u32 iris_dec_bitstream_buffer_size(struct iris_inst *inst)
{
struct platform_inst_caps *caps = inst->core->iris_platform_data->inst_caps;
@@ -268,10 +454,17 @@ int iris_get_buffer_size(struct iris_inst *inst,
case BUF_OUTPUT:
if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C)
return iris_yuv_buffer_size_qc08c(inst);
+ else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C)
+ return iris_yuv_buffer_size_qc10c(inst);
+ else if (inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010)
+ return iris_yuv_buffer_size_p010(inst);
else
return iris_yuv_buffer_size_nv12(inst);
case BUF_DPB:
- return iris_yuv_buffer_size_qc08c(inst);
+ if (iris_fmt_is_10bit(inst->fmt_dst->fmt.pix_mp.pixelformat))
+ return iris_yuv_buffer_size_qc10c(inst);
+ else
+ return iris_yuv_buffer_size_qc08c(inst);
default:
return 0;
}
@@ -295,37 +488,37 @@ static void iris_fill_internal_buf_info(struct iris_inst *inst,
{
struct iris_buffers *buffers = &inst->buffers[buffer_type];
- buffers->size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, buffer_type);
+ buffers->size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, buffer_type);
buffers->min_count = iris_vpu_buf_count(inst, buffer_type);
}
void iris_get_internal_buffers(struct iris_inst *inst, u32 plane)
{
- const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
if (inst->domain == DECODER) {
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;
+ internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
} else {
- internal_buf_type = platform_data->dec_op_int_buf_tbl;
- internal_buffer_count = platform_data->dec_op_int_buf_tbl_size;
+ internal_buf_type = firmware_data->dec_op_int_buf_tbl;
+ internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = platform_data->enc_ip_int_buf_tbl;
- internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
+ internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
} else {
- internal_buf_type = platform_data->enc_op_int_buf_tbl;
- internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_op_int_buf_tbl;
+ internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size;
for (i = 0; i < internal_buffer_count; i++)
iris_fill_internal_buf_info(inst, internal_buf_type[i]);
}
@@ -366,7 +559,7 @@ static int iris_create_internal_buffer(struct iris_inst *inst,
int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
{
- const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
u32 internal_buffer_count, i, j;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
@@ -374,19 +567,19 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
if (inst->domain == DECODER) {
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;
+ internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = firmware_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;
+ internal_buf_type = firmware_data->dec_op_int_buf_tbl;
+ internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = platform_data->enc_ip_int_buf_tbl;
- internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
+ internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
} else {
- internal_buf_type = platform_data->enc_op_int_buf_tbl;
- internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_op_int_buf_tbl;
+ internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size;
}
}
@@ -404,7 +597,7 @@ int iris_create_internal_buffers(struct iris_inst *inst, u32 plane)
int iris_queue_buffer(struct iris_inst *inst, struct iris_buffer *buf)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
int ret;
ret = hfi_ops->session_queue_buf(inst, buf);
@@ -442,7 +635,7 @@ int iris_queue_internal_deferred_buffers(struct iris_inst *inst, enum iris_buffe
int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
{
- const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
struct iris_buffer *buffer, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
@@ -451,19 +644,19 @@ int iris_queue_internal_buffers(struct iris_inst *inst, u32 plane)
if (inst->domain == DECODER) {
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;
+ internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = firmware_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;
+ internal_buf_type = firmware_data->dec_op_int_buf_tbl;
+ internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = platform_data->enc_ip_int_buf_tbl;
- internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
+ internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
} else {
- internal_buf_type = platform_data->enc_op_int_buf_tbl;
- internal_buffer_count = platform_data->enc_op_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_op_int_buf_tbl;
+ internal_buffer_count = firmware_data->enc_op_int_buf_tbl_size;
}
}
@@ -501,7 +694,7 @@ int iris_destroy_internal_buffer(struct iris_inst *inst, struct iris_buffer *buf
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;
+ const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
struct iris_buffer *buf, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
@@ -510,19 +703,19 @@ static int iris_destroy_internal_buffers(struct iris_inst *inst, u32 plane, bool
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = platform_data->dec_ip_int_buf_tbl;
- len = platform_data->dec_ip_int_buf_tbl_size;
+ internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
+ len = firmware_data->dec_ip_int_buf_tbl_size;
} else {
- internal_buf_type = platform_data->dec_op_int_buf_tbl;
- len = platform_data->dec_op_int_buf_tbl_size;
+ internal_buf_type = firmware_data->dec_op_int_buf_tbl;
+ len = firmware_data->dec_op_int_buf_tbl_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- internal_buf_type = platform_data->enc_ip_int_buf_tbl;
- len = platform_data->enc_ip_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
+ len = firmware_data->enc_ip_int_buf_tbl_size;
} else {
- internal_buf_type = platform_data->enc_op_int_buf_tbl;
- len = platform_data->enc_op_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_op_int_buf_tbl;
+ len = firmware_data->enc_op_int_buf_tbl_size;
}
}
@@ -572,7 +765,7 @@ int iris_destroy_dequeued_internal_buffers(struct iris_inst *inst, u32 plane)
static int iris_release_internal_buffers(struct iris_inst *inst,
enum iris_buffer_type buffer_type)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
struct iris_buffers *buffers = &inst->buffers[buffer_type];
struct iris_buffer *buffer, *next;
int ret;
@@ -595,17 +788,17 @@ static int iris_release_internal_buffers(struct iris_inst *inst,
static int iris_release_input_internal_buffers(struct iris_inst *inst)
{
- const struct iris_platform_data *platform_data = inst->core->iris_platform_data;
+ const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
const u32 *internal_buf_type;
u32 internal_buffer_count, i;
int ret;
if (inst->domain == DECODER) {
- internal_buf_type = platform_data->dec_ip_int_buf_tbl;
- internal_buffer_count = platform_data->dec_ip_int_buf_tbl_size;
+ internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = firmware_data->dec_ip_int_buf_tbl_size;
} else {
- internal_buf_type = platform_data->enc_ip_int_buf_tbl;
- internal_buffer_count = platform_data->enc_ip_int_buf_tbl_size;
+ internal_buf_type = firmware_data->enc_ip_int_buf_tbl;
+ internal_buffer_count = firmware_data->enc_ip_int_buf_tbl_size;
}
for (i = 0; i < internal_buffer_count; i++) {
diff --git a/drivers/media/platform/qcom/iris/iris_common.c b/drivers/media/platform/qcom/iris/iris_common.c
index 7f1c7fe144f7..25836561bcf3 100644
--- a/drivers/media/platform/qcom/iris/iris_common.c
+++ b/drivers/media/platform/qcom/iris/iris_common.c
@@ -48,7 +48,7 @@ void iris_set_ts_metadata(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
int iris_process_streamon_input(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state set_sub_state = 0;
int ret;
@@ -90,7 +90,7 @@ int iris_process_streamon_input(struct iris_inst *inst)
int iris_process_streamon_output(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state clear_sub_state = 0;
bool drain_active, drc_active, first_ipsc;
int ret = 0;
@@ -189,7 +189,7 @@ static void iris_flush_deferred_buffers(struct iris_inst *inst,
static void iris_kill_session(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
if (!inst->session_id)
return;
@@ -200,7 +200,7 @@ static void iris_kill_session(struct iris_inst *inst)
int iris_session_streamoff(struct iris_inst *inst, u32 plane)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_buffer_type buffer_type;
int ret;
diff --git a/drivers/media/platform/qcom/iris/iris_core.c b/drivers/media/platform/qcom/iris/iris_core.c
index dbaac01eb15a..52bf56e517f9 100644
--- a/drivers/media/platform/qcom/iris/iris_core.c
+++ b/drivers/media/platform/qcom/iris/iris_core.c
@@ -28,14 +28,13 @@ void iris_core_deinit(struct iris_core *core)
static int iris_wait_for_system_response(struct iris_core *core)
{
- u32 hw_response_timeout_val = core->iris_platform_data->hw_response_timeout;
int ret;
if (core->state == IRIS_CORE_ERROR)
return -EIO;
ret = wait_for_completion_timeout(&core->core_init_done,
- msecs_to_jiffies(hw_response_timeout_val));
+ msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE));
if (!ret) {
core->state = IRIS_CORE_ERROR;
return -ETIMEDOUT;
@@ -79,6 +78,8 @@ int iris_core_init(struct iris_core *core)
if (ret)
goto error_unload_fw;
+ core->iris_firmware_data->init_hfi_ops(core);
+
ret = iris_hfi_core_init(core);
if (ret)
goto error_unload_fw;
diff --git a/drivers/media/platform/qcom/iris/iris_core.h b/drivers/media/platform/qcom/iris/iris_core.h
index fb194c967ad4..24da60448cf2 100644
--- a/drivers/media/platform/qcom/iris/iris_core.h
+++ b/drivers/media/platform/qcom/iris/iris_core.h
@@ -30,6 +30,8 @@ enum domain_type {
DECODER = BIT(1),
};
+struct qcom_ubwc_cfg_data;
+
/**
* struct iris_core - holds core parameters valid for all instances
*
@@ -52,6 +54,9 @@ enum domain_type {
* @resets: table of iris reset clocks
* @controller_resets: table of controller reset clocks
* @iris_platform_data: a structure for platform data
+ * @iris_firmware_data: a pointer to the firmware (or HFI) specific data
+ * @iris_firmware_desc: a pointer to the firmware-specific descriptive data
+ * @ubwc_cfg: UBWC configuration for the platform
* @state: current state of core
* @iface_q_table_daddr: device address for interface queue table memory
* @sfr_daddr: device address for SFR (Sub System Failure Reason) register memory
@@ -65,8 +70,7 @@ enum domain_type {
* @header_id: id of packet header
* @packet_id: id of packet
* @power: a structure for clock and bw information
- * @hfi_ops: iris hfi command ops
- * @hfi_response_ops: iris hfi response ops
+ * @hfi_sys_ops: iris HFI system ops
* @core_init_done: structure of signal completion for system response
* @intr_status: interrupt status
* @sys_error_handler: a delayed work for handling system fatal error
@@ -95,6 +99,9 @@ struct iris_core {
struct reset_control_bulk_data *resets;
struct reset_control_bulk_data *controller_resets;
const struct iris_platform_data *iris_platform_data;
+ const struct iris_firmware_data *iris_firmware_data;
+ const struct iris_firmware_desc *iris_firmware_desc;
+ const struct qcom_ubwc_cfg_data *ubwc_cfg;
enum iris_core_state state;
dma_addr_t iface_q_table_daddr;
dma_addr_t sfr_daddr;
@@ -108,8 +115,7 @@ struct iris_core {
u32 header_id;
u32 packet_id;
struct iris_core_power power;
- const struct iris_hfi_command_ops *hfi_ops;
- const struct iris_hfi_response_ops *hfi_response_ops;
+ const struct iris_hfi_sys_ops *hfi_sys_ops;
struct completion core_init_done;
u32 intr_status;
struct delayed_work sys_error_handler;
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.c b/drivers/media/platform/qcom/iris/iris_ctrls.c
index 3cec957580f5..10e33b8a73f6 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.c
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.c
@@ -112,6 +112,48 @@ static enum platform_inst_fw_cap_type iris_get_cap_id(u32 id)
return IR_TYPE;
case V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD:
return IR_PERIOD;
+ case V4L2_CID_MPEG_VIDEO_LTR_COUNT:
+ return LTR_COUNT;
+ case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES:
+ return USE_LTR;
+ case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX:
+ return MARK_LTR;
+ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+ return B_FRAME;
+ case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING:
+ return LAYER_ENABLE;
+ case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE:
+ return LAYER_TYPE_H264;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE:
+ return LAYER_TYPE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER:
+ return LAYER_COUNT_H264;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER:
+ return LAYER_COUNT_HEVC;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR:
+ return LAYER0_BITRATE_H264;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR:
+ return LAYER1_BITRATE_H264;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR:
+ return LAYER2_BITRATE_H264;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR:
+ return LAYER3_BITRATE_H264;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR:
+ return LAYER4_BITRATE_H264;
+ case V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR:
+ return LAYER5_BITRATE_H264;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR:
+ return LAYER0_BITRATE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR:
+ return LAYER1_BITRATE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR:
+ return LAYER2_BITRATE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR:
+ return LAYER3_BITRATE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR:
+ return LAYER4_BITRATE_HEVC;
+ case V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR:
+ return LAYER5_BITRATE_HEVC;
default:
return INST_FW_CAP_MAX;
}
@@ -213,6 +255,48 @@ static u32 iris_get_v4l2_id(enum platform_inst_fw_cap_type cap_id)
return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE;
case IR_PERIOD:
return V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD;
+ case LTR_COUNT:
+ return V4L2_CID_MPEG_VIDEO_LTR_COUNT;
+ case USE_LTR:
+ return V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES;
+ case MARK_LTR:
+ return V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX;
+ case B_FRAME:
+ return V4L2_CID_MPEG_VIDEO_B_FRAMES;
+ case LAYER_ENABLE:
+ return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING;
+ case LAYER_TYPE_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_TYPE;
+ case LAYER_TYPE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_TYPE;
+ case LAYER_COUNT_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIERARCHICAL_CODING_LAYER;
+ case LAYER_COUNT_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_LAYER;
+ case LAYER0_BITRATE_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L0_BR;
+ case LAYER1_BITRATE_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L1_BR;
+ case LAYER2_BITRATE_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L2_BR;
+ case LAYER3_BITRATE_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L3_BR;
+ case LAYER4_BITRATE_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L4_BR;
+ case LAYER5_BITRATE_H264:
+ return V4L2_CID_MPEG_VIDEO_H264_HIER_CODING_L5_BR;
+ case LAYER0_BITRATE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L0_BR;
+ case LAYER1_BITRATE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L1_BR;
+ case LAYER2_BITRATE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L2_BR;
+ case LAYER3_BITRATE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L3_BR;
+ case LAYER4_BITRATE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L4_BR;
+ case LAYER5_BITRATE_HEVC:
+ return V4L2_CID_MPEG_VIDEO_HEVC_HIER_CODING_L5_BR;
default:
return 0;
}
@@ -332,8 +416,8 @@ void iris_session_init_caps(struct iris_core *core)
const struct platform_inst_fw_cap *caps;
u32 i, num_cap, cap_id;
- caps = core->iris_platform_data->inst_fw_caps_dec;
- num_cap = core->iris_platform_data->inst_fw_caps_dec_size;
+ caps = core->iris_firmware_data->inst_fw_caps_dec;
+ num_cap = core->iris_firmware_data->inst_fw_caps_dec_size;
for (i = 0; i < num_cap; i++) {
cap_id = caps[i].cap_id;
@@ -360,8 +444,8 @@ void iris_session_init_caps(struct iris_core *core)
}
}
- caps = core->iris_platform_data->inst_fw_caps_enc;
- num_cap = core->iris_platform_data->inst_fw_caps_enc_size;
+ caps = core->iris_firmware_data->inst_fw_caps_enc;
+ num_cap = core->iris_firmware_data->inst_fw_caps_enc_size;
for (i = 0; i < num_cap; i++) {
cap_id = caps[i].cap_id;
@@ -399,7 +483,7 @@ static u32 iris_get_port_info(struct iris_inst *inst,
int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 hfi_value = inst->fw_caps[cap_id].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
@@ -412,7 +496,7 @@ int iris_set_u32_enum(struct iris_inst *inst, enum platform_inst_fw_cap_type cap
int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 hfi_value = inst->fw_caps[cap_id].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
@@ -425,7 +509,7 @@ int iris_set_u32(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
struct v4l2_format *inp_f = inst->fmt_src;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
u32 height = inp_f->fmt.pix_mp.height;
@@ -446,7 +530,7 @@ int iris_set_stage(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id
int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 work_route = inst->fw_caps[PIPE].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
@@ -459,7 +543,7 @@ int iris_set_pipe(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 hfi_id, hfi_value;
if (inst->codec == V4L2_PIX_FMT_H264) {
@@ -479,7 +563,7 @@ int iris_set_profile(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_
int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 hfi_id, hfi_value;
if (inst->codec == V4L2_PIX_FMT_H264) {
@@ -499,7 +583,7 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id
int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
struct hfi_profile_level pl;
@@ -520,7 +604,7 @@ int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_ca
int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 header_mode = inst->fw_caps[cap_id].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
u32 hfi_val;
@@ -539,7 +623,7 @@ int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_
int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 prepend_sps_pps = inst->fw_caps[PREPEND_SPSPPS_TO_IDR].value;
u32 header_mode = inst->fw_caps[cap_id].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
@@ -559,9 +643,66 @@ int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_
&hfi_val, sizeof(u32));
}
-int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value;
+ u32 bitrate = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ struct hfi_bitrate hfi_val;
+ u32 max_bitrate;
+
+ if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET) && cap_id != BITRATE)
+ return -EINVAL;
+
+ if (inst->codec == V4L2_PIX_FMT_HEVC) {
+ max_bitrate = CABAC_MAX_BITRATE;
+ } else {
+ if (entropy_mode == V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+ max_bitrate = CABAC_MAX_BITRATE;
+ else
+ max_bitrate = CAVLC_MAX_BITRATE;
+ }
+
+ hfi_val.bitrate = min(bitrate, max_bitrate);
+
+ switch (cap_id) {
+ case BITRATE:
+ case LAYER0_BITRATE_H264:
+ hfi_val.layer_id = 0;
+ break;
+ case LAYER1_BITRATE_H264:
+ hfi_val.layer_id = 1;
+ break;
+ case LAYER2_BITRATE_H264:
+ hfi_val.layer_id = 2;
+ break;
+ case LAYER3_BITRATE_H264:
+ hfi_val.layer_id = 3;
+ break;
+ case LAYER4_BITRATE_H264:
+ hfi_val.layer_id = 4;
+ break;
+ case LAYER5_BITRATE_H264:
+ hfi_val.layer_id = 5;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (hfi_val.layer_id > 0 && !inst->fw_caps[LAYER_ENABLE].value)
+ return -EINVAL;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_STRUCTURE,
+ &hfi_val, sizeof(hfi_val));
+}
+
+int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 entropy_mode = inst->fw_caps[ENTROPY_MODE].value;
u32 bitrate = inst->fw_caps[cap_id].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
@@ -586,7 +727,7 @@ int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_
int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 rc_mode = inst->fw_caps[BITRATE_MODE].value;
u32 peak_bitrate = inst->fw_caps[cap_id].value;
u32 bitrate = inst->fw_caps[BITRATE].value;
@@ -613,7 +754,7 @@ int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type
int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value;
u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value;
u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value;
@@ -640,7 +781,7 @@ int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap
int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 bitrate_mode = inst->fw_caps[BITRATE_MODE].value;
u32 frame_rc = inst->fw_caps[FRAME_RC_ENABLE].value;
u32 frame_skip = inst->fw_caps[FRAME_SKIP_MODE].value;
@@ -667,7 +808,7 @@ int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap
int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 entropy_mode = inst->fw_caps[cap_id].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
u32 hfi_val;
@@ -687,7 +828,7 @@ int iris_set_entropy_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap
int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 entropy_mode = inst->fw_caps[cap_id].value;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
u32 profile;
@@ -712,7 +853,7 @@ int iris_set_entropy_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap
int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0;
u32 i_frame_qp = 0, p_frame_qp = 0, b_frame_qp = 0;
u32 min_qp_enable = 0, client_qp_enable = 0;
@@ -776,7 +917,7 @@ int iris_set_min_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i
int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0;
u32 max_qp_enable = 0, client_qp_enable;
u32 i_frame_qp, p_frame_qp, b_frame_qp;
@@ -841,7 +982,7 @@ int iris_set_max_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_i
int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 i_qp_enable = 0, p_qp_enable = 0, b_qp_enable = 0, client_qp_enable;
u32 i_frame_qp, p_frame_qp, b_frame_qp;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
@@ -902,7 +1043,7 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap
int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
struct hfi_quantization_range_v2 range;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
@@ -923,7 +1064,7 @@ int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap
int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
u32 hfi_val;
@@ -953,7 +1094,7 @@ int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap
int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
u32 hfi_val = HFI_DISABLE_FLIP;
@@ -970,9 +1111,46 @@ int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
&hfi_val, sizeof(u32));
}
-int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ struct v4l2_pix_format_mplane *fmt = &inst->fmt_dst->fmt.pix_mp;
+ u32 codec_align = inst->codec == V4L2_PIX_FMT_HEVC ? 32 : 16;
+ u32 ir_period = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ struct hfi_intra_refresh hfi_val;
+
+ if (!ir_period)
+ return -EINVAL;
+
+ if (inst->fw_caps[IR_TYPE].value ==
+ V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) {
+ hfi_val.mode = HFI_INTRA_REFRESH_RANDOM;
+ } else if (inst->fw_caps[IR_TYPE].value ==
+ V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC) {
+ hfi_val.mode = HFI_INTRA_REFRESH_CYCLIC;
+ } else {
+ return -EINVAL;
+ }
+
+ /*
+ * Calculate the number of macroblocks in a frame,
+ * then determine how many macroblocks need to be
+ * refreshed within one ir_period.
+ */
+ hfi_val.mbs = (fmt->width / codec_align) * (fmt->height / codec_align);
+ hfi_val.mbs /= ir_period;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_STRUCTURE,
+ &hfi_val, sizeof(hfi_val));
+}
+
+int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
struct vb2_queue *q = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
u32 ir_period = inst->fw_caps[cap_id].value;
u32 ir_type = 0;
@@ -996,9 +1174,312 @@ int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type ca
&ir_period, sizeof(u32));
}
+int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 ltr_count = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ struct hfi_ltr_mode ltr_mode;
+
+ if (!ltr_count)
+ return -EINVAL;
+
+ ltr_mode.count = ltr_count;
+ ltr_mode.mode = HFI_LTR_MODE_MANUAL;
+ ltr_mode.trust_mode = 1;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_STRUCTURE,
+ &ltr_mode, sizeof(ltr_mode));
+}
+
+int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ u32 ltr_count = inst->fw_caps[LTR_COUNT].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ struct hfi_ltr_use ltr_use;
+
+ if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq))
+ return -EINVAL;
+
+ if (!ltr_count)
+ return -EINVAL;
+
+ ltr_use.ref_ltr = inst->fw_caps[cap_id].value;
+ ltr_use.use_constrnt = true;
+ ltr_use.frames = 0;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_STRUCTURE,
+ &ltr_use, sizeof(ltr_use));
+}
+
+int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ u32 ltr_count = inst->fw_caps[LTR_COUNT].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ struct hfi_ltr_mark ltr_mark;
+
+ if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq))
+ return -EINVAL;
+
+ if (!ltr_count)
+ return -EINVAL;
+
+ ltr_mark.mark_frame = inst->fw_caps[cap_id].value;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_STRUCTURE,
+ &ltr_mark, sizeof(ltr_mark));
+}
+
+int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 ltr_count = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ if (!ltr_count)
+ return -EINVAL;
+
+ if (inst->hfi_rc_type == HFI_RC_CBR_VFR ||
+ inst->hfi_rc_type == HFI_RC_CBR_CFR ||
+ inst->hfi_rc_type == HFI_RC_OFF) {
+ inst->fw_caps[LTR_COUNT].value = 0;
+ return -EINVAL;
+ }
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &ltr_count, sizeof(u32));
+}
+
+int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ u32 ltr_count = inst->fw_caps[LTR_COUNT].value;
+ u32 hfi_val = inst->fw_caps[cap_id].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+
+ if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq))
+ return -EINVAL;
+
+ if (!ltr_count || hfi_val == INVALID_DEFAULT_MARK_OR_USE_LTR)
+ return -EINVAL;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &hfi_val, sizeof(u32));
+}
+
+int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 gop_size = inst->fw_caps[GOP_SIZE].value;
+ u32 b_frame = inst->fw_caps[B_FRAME].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ struct hfi_intra_period intra_period;
+
+ if (!gop_size || b_frame >= gop_size)
+ return -EINVAL;
+
+ /*
+ * intra_period represents the length of a GOP, which includes both P-frames
+ * and B-frames. The counts of P-frames and B-frames within a GOP must be
+ * communicated to the firmware.
+ */
+ intra_period.pframes = (gop_size - 1) / (b_frame + 1);
+ intra_period.bframes = b_frame;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_STRUCTURE,
+ &intra_period, sizeof(intra_period));
+}
+
+int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 layer_type;
+
+ if (inst->hfi_rc_type == HFI_RATE_CONTROL_CQ ||
+ inst->hfi_rc_type == HFI_RATE_CONTROL_OFF)
+ return -EINVAL;
+
+ if (inst->codec == V4L2_PIX_FMT_H264) {
+ if (!layer_enable || !inst->fw_caps[LAYER_COUNT_H264].value)
+ return -EINVAL;
+
+ if (inst->fw_caps[LAYER_TYPE_H264].value ==
+ V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P) {
+ if (inst->hfi_rc_type == HFI_RC_VBR_CFR)
+ layer_type = HFI_HIER_P_HYBRID_LTR;
+ else
+ layer_type = HFI_HIER_P_SLIDING_WINDOW;
+ } else if (inst->fw_caps[LAYER_TYPE_H264].value ==
+ V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) {
+ if (inst->hfi_rc_type == HFI_RC_VBR_CFR)
+ layer_type = HFI_HIER_B;
+ else
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+ } else if (inst->codec == V4L2_PIX_FMT_HEVC) {
+ if (!inst->fw_caps[LAYER_COUNT_HEVC].value)
+ return -EINVAL;
+
+ if (inst->fw_caps[LAYER_TYPE_HEVC].value ==
+ V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P) {
+ layer_type = HFI_HIER_P_SLIDING_WINDOW;
+ } else if (inst->fw_caps[LAYER_TYPE_HEVC].value ==
+ V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) {
+ if (inst->hfi_rc_type == HFI_RC_VBR_CFR)
+ layer_type = HFI_HIER_B;
+ else
+ return -EINVAL;
+ } else {
+ return -EINVAL;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ inst->hfi_layer_type = layer_type;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32_ENUM,
+ &layer_type, sizeof(u32));
+}
+
+int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ u32 layer_enable = inst->fw_caps[LAYER_ENABLE].value;
+ u32 layer_count = inst->fw_caps[cap_id].value;
+ u32 hfi_id, ret;
+
+ if (!layer_enable || !layer_count)
+ return -EINVAL;
+
+ inst->hfi_layer_count = layer_count;
+
+ if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq)) {
+ hfi_id = HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER;
+ ret = hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &layer_count, sizeof(u32));
+ if (ret)
+ return ret;
+ }
+
+ hfi_id = inst->fw_caps[cap_id].hfi_id;
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &layer_count, sizeof(u32));
+}
+
+int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 layer_type = inst->hfi_layer_type;
+ u32 layer_count, layer_count_max;
+
+ layer_count = (inst->codec == V4L2_PIX_FMT_H264) ?
+ inst->fw_caps[LAYER_COUNT_H264].value :
+ inst->fw_caps[LAYER_COUNT_HEVC].value;
+
+ if (!layer_count)
+ return -EINVAL;
+
+ if (layer_type == HFI_HIER_B) {
+ layer_count_max = MAX_LAYER_HB;
+ } else if (layer_type == HFI_HIER_P_HYBRID_LTR) {
+ layer_count_max = MAX_AVC_LAYER_HP_HYBRID_LTR;
+ } else if (layer_type == HFI_HIER_P_SLIDING_WINDOW) {
+ if (inst->codec == V4L2_PIX_FMT_H264) {
+ layer_count_max = MAX_AVC_LAYER_HP_SLIDING_WINDOW;
+ } else {
+ if (inst->hfi_rc_type == HFI_RC_VBR_CFR)
+ layer_count_max = MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW;
+ else
+ layer_count_max = MAX_HEVC_LAYER_HP_SLIDING_WINDOW;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ if (layer_count > layer_count_max)
+ layer_count = layer_count_max;
+
+ layer_count += 1; /* base layer */
+ inst->hfi_layer_count = layer_count;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &layer_count, sizeof(u32));
+}
+
+int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id)
+{
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
+ struct vb2_queue *sq = v4l2_m2m_get_src_vq(inst->m2m_ctx);
+ struct vb2_queue *dq = v4l2_m2m_get_dst_vq(inst->m2m_ctx);
+ u32 hfi_id = inst->fw_caps[cap_id].hfi_id;
+ u32 bitrate = inst->fw_caps[cap_id].value;
+
+ /* ignore layer bitrate when total bitrate is set */
+ if (inst->fw_caps[BITRATE].flags & CAP_FLAG_CLIENT_SET)
+ return 0;
+
+ if (!(inst->fw_caps[cap_id].flags & CAP_FLAG_CLIENT_SET))
+ return -EINVAL;
+
+ if (!vb2_is_streaming(sq) && !vb2_is_streaming(dq))
+ return -EINVAL;
+
+ return hfi_ops->session_set_property(inst, hfi_id,
+ HFI_HOST_FLAGS_NONE,
+ iris_get_port_info(inst, cap_id),
+ HFI_PAYLOAD_U32,
+ &bitrate, sizeof(u32));
+}
+
int iris_set_properties(struct iris_inst *inst, u32 plane)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
struct platform_inst_fw_cap *cap;
int ret;
u32 i;
diff --git a/drivers/media/platform/qcom/iris/iris_ctrls.h b/drivers/media/platform/qcom/iris/iris_ctrls.h
index 9518803577bc..3c462ec9190b 100644
--- a/drivers/media/platform/qcom/iris/iris_ctrls.h
+++ b/drivers/media/platform/qcom/iris/iris_ctrls.h
@@ -22,7 +22,8 @@ int iris_set_level(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id
int iris_set_profile_level_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_header_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_header_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
-int iris_set_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_bitrate_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_bitrate_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_peak_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_bitrate_mode_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_bitrate_mode_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
@@ -34,7 +35,18 @@ int iris_set_frame_qp(struct iris_inst *inst, enum platform_inst_fw_cap_type cap
int iris_set_qp_range(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_rotation(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_flip(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
-int iris_set_ir_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_ir_period_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_ir_period_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_ltr_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_ltr_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_use_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_use_and_mark_ltr(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_intra_period(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_layer_type(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_layer_count_gen1(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_layer_count_gen2(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
+int iris_set_layer_bitrate(struct iris_inst *inst, enum platform_inst_fw_cap_type cap_id);
int iris_set_properties(struct iris_inst *inst, u32 plane);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_firmware.c b/drivers/media/platform/qcom/iris/iris_firmware.c
index 5f408024e967..1a476146d758 100644
--- a/drivers/media/platform/qcom/iris/iris_firmware.c
+++ b/drivers/media/platform/qcom/iris/iris_firmware.c
@@ -12,11 +12,12 @@
#include "iris_core.h"
#include "iris_firmware.h"
+#define IRIS_PAS_ID 9
+
#define MAX_FIRMWARE_NAME_SIZE 128
static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
{
- u32 pas_id = core->iris_platform_data->pas_id;
const struct firmware *firmware = NULL;
struct device *dev = core->dev;
struct resource res;
@@ -53,7 +54,7 @@ static int iris_load_fw_to_memory(struct iris_core *core, const char *fw_name)
}
ret = qcom_mdt_load(dev, firmware, fw_name,
- pas_id, mem_virt, mem_phys, res_size, NULL);
+ IRIS_PAS_ID, mem_virt, mem_phys, res_size, NULL);
memunmap(mem_virt);
err_release_fw:
@@ -71,7 +72,7 @@ int iris_fw_load(struct iris_core *core)
ret = of_property_read_string_index(core->dev->of_node, "firmware-name", 0,
&fwpath);
if (ret)
- fwpath = core->iris_platform_data->fwname;
+ fwpath = core->iris_firmware_desc->fwname;
ret = iris_load_fw_to_memory(core, fwpath);
if (ret) {
@@ -79,7 +80,7 @@ int iris_fw_load(struct iris_core *core)
return -ENOMEM;
}
- ret = qcom_scm_pas_auth_and_reset(core->iris_platform_data->pas_id);
+ ret = qcom_scm_pas_auth_and_reset(IRIS_PAS_ID);
if (ret) {
dev_err(core->dev, "auth and reset failed: %d\n", ret);
return ret;
@@ -93,7 +94,7 @@ int iris_fw_load(struct iris_core *core)
cp_config->cp_nonpixel_size);
if (ret) {
dev_err(core->dev, "qcom_scm_mem_protect_video_var failed: %d\n", ret);
- qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+ qcom_scm_pas_shutdown(IRIS_PAS_ID);
return ret;
}
}
@@ -103,7 +104,7 @@ int iris_fw_load(struct iris_core *core)
int iris_fw_unload(struct iris_core *core)
{
- return qcom_scm_pas_shutdown(core->iris_platform_data->pas_id);
+ return qcom_scm_pas_shutdown(IRIS_PAS_ID);
}
int iris_set_hw_state(struct iris_core *core, bool resume)
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.c b/drivers/media/platform/qcom/iris/iris_hfi_common.c
index 621c66593d88..8769ec61f117 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.c
@@ -76,7 +76,7 @@ u32 iris_hfi_get_v4l2_matrix_coefficients(u32 hfi_coefficients)
int iris_hfi_core_init(struct iris_core *core)
{
- const struct iris_hfi_command_ops *hfi_ops = core->hfi_ops;
+ const struct iris_hfi_sys_ops *hfi_ops = core->hfi_sys_ops;
int ret;
ret = hfi_ops->sys_init(core);
@@ -109,7 +109,7 @@ irqreturn_t iris_hfi_isr_handler(int irq, void *data)
iris_vpu_clear_interrupt(core);
mutex_unlock(&core->lock);
- core->hfi_response_ops->hfi_response_handler(core);
+ core->hfi_sys_ops->sys_hfi_response_handler(core);
if (!iris_vpu_watchdog(core, core->intr_status))
enable_irq(irq);
@@ -144,7 +144,7 @@ error:
int iris_hfi_pm_resume(struct iris_core *core)
{
- const struct iris_hfi_command_ops *ops = core->hfi_ops;
+ const struct iris_hfi_sys_ops *ops = core->hfi_sys_ops;
int ret;
ret = iris_vpu_power_on(core);
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_common.h b/drivers/media/platform/qcom/iris/iris_hfi_common.h
index 3edb5ae582b4..a27447eb2519 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_common.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_common.h
@@ -105,11 +105,18 @@ struct iris_hfi_prop_type_handle {
int (*handle)(struct iris_inst *inst, u32 plane);
};
-struct iris_hfi_command_ops {
+struct iris_hfi_sys_ops {
int (*sys_init)(struct iris_core *core);
int (*sys_image_version)(struct iris_core *core);
int (*sys_interframe_powercollapse)(struct iris_core *core);
int (*sys_pc_prep)(struct iris_core *core);
+
+ void (*sys_hfi_response_handler)(struct iris_core *core);
+
+ struct iris_inst *(*sys_get_instance)(void);
+};
+
+struct iris_hfi_session_ops {
int (*session_set_config_params)(struct iris_inst *inst, u32 plane);
int (*session_set_property)(struct iris_inst *inst,
u32 packet_type, u32 flag, u32 plane, u32 payload_type,
@@ -126,10 +133,6 @@ struct iris_hfi_command_ops {
int (*session_close)(struct iris_inst *inst);
};
-struct iris_hfi_response_ops {
- void (*hfi_response_handler)(struct iris_core *core);
-};
-
struct hfi_subscription_params {
u32 bitstream_resolution;
u32 crop_offsets[2];
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen1.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c
index df8e6bf9430e..ca1545d28b53 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_gen1.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.c
@@ -3,38 +3,16 @@
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
-#include "iris_core.h"
#include "iris_ctrls.h"
#include "iris_platform_common.h"
-#include "iris_resources.h"
#include "iris_hfi_gen1.h"
#include "iris_hfi_gen1_defines.h"
#include "iris_vpu_buffer.h"
-#include "iris_vpu_common.h"
-#include "iris_instance.h"
-
-#include "iris_platform_sc7280.h"
#define BITRATE_MIN 32000
#define BITRATE_MAX 160000000
-#define BITRATE_PEAK_DEFAULT (BITRATE_DEFAULT * 2)
#define BITRATE_STEP 100
-static struct iris_fmt platform_fmts_sm8250_dec[] = {
- [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 struct platform_inst_fw_cap inst_fw_cap_sm8250_dec[] = {
{
.cap_id = PIPE,
@@ -158,7 +136,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
.hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
- .set = iris_set_bitrate,
+ .set = iris_set_bitrate_gen1,
},
{
.cap_id = BITRATE_MODE,
@@ -246,60 +224,164 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8250_enc[] = {
.flags = CAP_FLAG_OUTPUT_PORT,
.set = iris_set_qp_range,
},
-};
-
-static struct platform_inst_caps platform_inst_cap_sm8250 = {
- .min_frame_width = 128,
- .max_frame_width = 8192,
- .min_frame_height = 128,
- .max_frame_height = 8192,
- .max_mbpf = 138240,
- .mb_cycles_vsp = 25,
- .mb_cycles_vpp = 200,
- .max_frame_rate = MAXIMUM_FPS,
- .max_operating_rate = MAXIMUM_FPS,
-};
-
-static void iris_set_sm8250_preset_registers(struct iris_core *core)
-{
- writel(0x0, core->reg_base + 0xB0088);
-}
-
-static const struct icc_info sm8250_icc_table[] = {
- { "cpu-cfg", 1000, 1000 },
- { "video-mem", 1000, 15000000 },
-};
-
-static const char * const sm8250_clk_reset_table[] = { "bus", "core" };
-
-static const struct bw_info sm8250_bw_table_dec[] = {
- { ((4096 * 2160) / 256) * 60, 2403000 },
- { ((4096 * 2160) / 256) * 30, 1224000 },
- { ((1920 * 1080) / 256) * 60, 812000 },
- { ((1920 * 1080) / 256) * 30, 416000 },
-};
-
-static const char * const sm8250_pmdomain_table[] = { "venus", "vcodec0" };
-
-static const char * const sm8250_opp_pd_table[] = { "mx" };
-
-static const struct platform_clk_data sm8250_clk_table[] = {
- {IRIS_AXI_CLK, "iface" },
- {IRIS_CTRL_CLK, "core" },
- {IRIS_HW_CLK, "vcodec0_core" },
-};
-
-static const char * const sm8250_opp_clk_table[] = {
- "vcodec0_core",
- NULL,
-};
-
-static const struct tz_cp_config tz_cp_config_sm8250[] = {
{
- .cp_start = 0,
- .cp_size = 0x25800000,
- .cp_nonpixel_start = 0x01000000,
- .cp_nonpixel_size = 0x24800000,
+ .cap_id = IR_TYPE,
+ .min = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
+ .max = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC,
+ .step_or_mask = BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM) |
+ BIT(V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC),
+ .value = V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_RANDOM,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ },
+ {
+ .cap_id = IR_PERIOD,
+ .min = 0,
+ .max = ((4096 * 2304) >> 8),
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_ir_period_gen1,
+ },
+ {
+ .cap_id = LTR_COUNT,
+ .min = 0,
+ .max = MAX_LTR_FRAME_COUNT_GEN1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_PARAM_VENC_LTRMODE,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_ltr_count_gen1,
+ },
+ {
+ .cap_id = USE_LTR,
+ .min = 0,
+ .max = ((1 << MAX_LTR_FRAME_COUNT_GEN1) - 1),
+ .step_or_mask = 0,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_use_ltr,
+ },
+ {
+ .cap_id = MARK_LTR,
+ .min = 0,
+ .max = (MAX_LTR_FRAME_COUNT_GEN1 - 1),
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_mark_ltr,
+ },
+ {
+ .cap_id = B_FRAME,
+ .min = 0,
+ .max = 3,
+ .step_or_mask = 1,
+ .value = 0,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ },
+ {
+ .cap_id = INTRA_PERIOD,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_intra_period,
+ },
+ {
+ .cap_id = LAYER_ENABLE,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ },
+ {
+ .cap_id = LAYER_TYPE_H264,
+ .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P),
+ .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ },
+ {
+ .cap_id = LAYER_COUNT_H264,
+ .min = 0,
+ .max = MAX_HIER_CODING_LAYER_GEN1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_layer_count_gen1,
+ },
+ {
+ .cap_id = LAYER0_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_bitrate_gen1,
+ },
+ {
+ .cap_id = LAYER1_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_bitrate_gen1,
+ },
+ {
+ .cap_id = LAYER2_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_bitrate_gen1,
+ },
+ {
+ .cap_id = LAYER3_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_bitrate_gen1,
+ },
+ {
+ .cap_id = LAYER4_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_bitrate_gen1,
+ },
+ {
+ .cap_id = LAYER5_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_bitrate_gen1,
},
};
@@ -337,99 +419,14 @@ static const u32 sm8250_enc_ip_int_buf_tbl[] = {
BUF_SCRATCH_2,
};
-const struct iris_platform_data sm8250_data = {
- .get_instance = iris_hfi_gen1_get_instance,
- .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init,
- .init_hfi_response_ops = iris_hfi_gen1_response_ops_init,
- .get_vpu_buffer_size = iris_vpu_buf_size,
- .vpu_ops = &iris_vpu2_ops,
- .set_preset_registers = iris_set_sm8250_preset_registers,
- .icc_tbl = sm8250_icc_table,
- .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table),
- .clk_rst_tbl = sm8250_clk_reset_table,
- .clk_rst_tbl_size = ARRAY_SIZE(sm8250_clk_reset_table),
- .bw_tbl_dec = sm8250_bw_table_dec,
- .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec),
- .pmdomain_tbl = sm8250_pmdomain_table,
- .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table),
- .opp_pd_tbl = sm8250_opp_pd_table,
- .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table),
- .clk_tbl = sm8250_clk_table,
- .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table),
- .opp_clk_tbl = sm8250_opp_clk_table,
- /* Upper bound of DMA address range */
- .dma_mask = 0xe0000000 - 1,
- .fwname = "qcom/vpu-1.0/venus.mbn",
- .pas_id = IRIS_PAS_ID,
- .inst_iris_fmts = platform_fmts_sm8250_dec,
- .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec),
- .inst_caps = &platform_inst_cap_sm8250,
- .inst_fw_caps_dec = inst_fw_cap_sm8250_dec,
- .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec),
- .inst_fw_caps_enc = inst_fw_cap_sm8250_enc,
- .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc),
- .tz_cp_config_data = tz_cp_config_sm8250,
- .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250),
- .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
- .num_vpp_pipe = 4,
- .max_session_count = 16,
- .max_core_mbpf = NUM_MBS_8K,
- .max_core_mbps = ((7680 * 4320) / 256) * 60,
- .dec_input_config_params_default =
- sm8250_vdec_input_config_param_default,
- .dec_input_config_params_default_size =
- ARRAY_SIZE(sm8250_vdec_input_config_param_default),
- .enc_input_config_params = sm8250_venc_input_config_param,
- .enc_input_config_params_size =
- ARRAY_SIZE(sm8250_venc_input_config_param),
-
- .dec_ip_int_buf_tbl = sm8250_dec_ip_int_buf_tbl,
- .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_ip_int_buf_tbl),
- .dec_op_int_buf_tbl = sm8250_dec_op_int_buf_tbl,
- .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8250_dec_op_int_buf_tbl),
-
- .enc_ip_int_buf_tbl = sm8250_enc_ip_int_buf_tbl,
- .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8250_enc_ip_int_buf_tbl),
-};
+const struct iris_firmware_data iris_hfi_gen1_data = {
+ .init_hfi_ops = &iris_hfi_gen1_sys_ops_init,
-const struct iris_platform_data sc7280_data = {
- .get_instance = iris_hfi_gen1_get_instance,
- .init_hfi_command_ops = &iris_hfi_gen1_command_ops_init,
- .init_hfi_response_ops = iris_hfi_gen1_response_ops_init,
- .get_vpu_buffer_size = iris_vpu_buf_size,
- .vpu_ops = &iris_vpu2_ops,
- .set_preset_registers = iris_set_sm8250_preset_registers,
- .icc_tbl = sm8250_icc_table,
- .icc_tbl_size = ARRAY_SIZE(sm8250_icc_table),
- .bw_tbl_dec = sc7280_bw_table_dec,
- .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec),
- .pmdomain_tbl = sm8250_pmdomain_table,
- .pmdomain_tbl_size = ARRAY_SIZE(sm8250_pmdomain_table),
- .opp_pd_tbl = sc7280_opp_pd_table,
- .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table),
- .clk_tbl = sc7280_clk_table,
- .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table),
- .opp_clk_tbl = sc7280_opp_clk_table,
- /* Upper bound of DMA address range */
- .dma_mask = 0xe0000000 - 1,
- .fwname = "qcom/vpu/vpu20_p1.mbn",
- .pas_id = IRIS_PAS_ID,
- .inst_iris_fmts = platform_fmts_sm8250_dec,
- .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8250_dec),
- .inst_caps = &platform_inst_cap_sm8250,
.inst_fw_caps_dec = inst_fw_cap_sm8250_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8250_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8250_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8250_enc),
- .tz_cp_config_data = tz_cp_config_sm8250,
- .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8250),
- .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
- .num_vpp_pipe = 1,
- .no_aon = true,
- .max_session_count = 16,
- .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256,
- /* max spec for SC7280 is 4096x2176@60fps */
- .max_core_mbps = 4096 * 2176 / 256 * 60,
+
.dec_input_config_params_default =
sm8250_vdec_input_config_param_default,
.dec_input_config_params_default_size =
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h
index 19b8e9054a75..c37adf65055a 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1.h
@@ -7,10 +7,8 @@
#define __IRIS_HFI_GEN1_H__
struct iris_core;
-struct iris_inst;
-void iris_hfi_gen1_command_ops_init(struct iris_core *core);
-void iris_hfi_gen1_response_ops_init(struct iris_core *core);
-struct iris_inst *iris_hfi_gen1_get_instance(void);
+void iris_hfi_gen1_sys_ops_init(struct iris_core *core);
+void iris_hfi_gen1_response_handler(struct iris_core *core);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
index e42d17653c2c..7674b47ad6c4 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_command.c
@@ -485,7 +485,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
{
void *prop_data = &packet->data[1];
- packet->shdr.hdr.size = sizeof(*packet);
+ packet->shdr.hdr.size = sizeof(*packet) + sizeof(ptype);
packet->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY;
packet->shdr.session_id = inst->session_id;
packet->num_properties = 1;
@@ -498,14 +498,14 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
fsize->buffer_type = in->buffer_type;
fsize->height = in->height;
fsize->width = in->width;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*fsize);
+ packet->shdr.hdr.size += sizeof(*fsize);
break;
}
case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: {
struct hfi_videocores_usage_type *in = pdata, *cu = prop_data;
cu->video_core_enable_mask = in->video_core_enable_mask;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*cu);
+ packet->shdr.hdr.size += sizeof(*cu);
break;
}
case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT: {
@@ -514,7 +514,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
hfi->buffer_type = in->buffer_type;
hfi->format = in->format;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*hfi);
+ packet->shdr.hdr.size += sizeof(*hfi);
break;
}
case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: {
@@ -533,7 +533,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
info->plane_format[1].buffer_alignment = 256;
}
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*info);
+ packet->shdr.hdr.size += sizeof(*info);
break;
}
case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: {
@@ -543,7 +543,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
count->type = in->type;
count->count_actual = in->count_actual;
count->count_min_host = in->count_min_host;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*count);
+ packet->shdr.hdr.size += sizeof(*count);
break;
}
case HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM: {
@@ -552,7 +552,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
multi->buffer_type = in->buffer_type;
multi->enable = in->enable;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*multi);
+ packet->shdr.hdr.size += sizeof(*multi);
break;
}
case HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL: {
@@ -560,7 +560,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
sz->size = in->size;
sz->type = in->type;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*sz);
+ packet->shdr.hdr.size += sizeof(*sz);
break;
}
case HFI_PROPERTY_PARAM_WORK_ROUTE: {
@@ -568,7 +568,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
u32 *in = pdata;
wr->video_work_route = *in;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*wr);
+ packet->shdr.hdr.size += sizeof(*wr);
break;
}
case HFI_PROPERTY_PARAM_WORK_MODE: {
@@ -576,7 +576,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
u32 *in = pdata;
wm->video_work_mode = *in;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*wm);
+ packet->shdr.hdr.size += sizeof(*wm);
break;
}
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT: {
@@ -592,7 +592,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
/* Level not supported, falling back to 1 */
pl->level = 1;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*pl);
+ packet->shdr.hdr.size += sizeof(*pl);
break;
}
case HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER: {
@@ -600,16 +600,15 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
u32 *in = pdata;
en->enable = *in;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*en);
+ packet->shdr.hdr.size += sizeof(*en);
break;
}
case HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE: {
- struct hfi_bitrate *brate = prop_data;
- u32 *in = pdata;
+ struct hfi_bitrate *in = pdata, *brate = prop_data;
- brate->bitrate = *in;
- brate->layer_id = 0;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*brate);
+ brate->bitrate = in->bitrate;
+ brate->layer_id = in->layer_id;
+ packet->shdr.hdr.size += sizeof(*brate);
break;
}
case HFI_PROPERTY_PARAM_VENC_RATE_CONTROL: {
@@ -628,7 +627,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
}
packet->data[1] = *in;
- packet->shdr.hdr.size += sizeof(u32) * 2;
+ packet->shdr.hdr.size += sizeof(u32);
break;
}
case HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL: {
@@ -638,7 +637,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
entropy->entropy_mode = *in;
if (entropy->entropy_mode == HFI_H264_ENTROPY_CABAC)
entropy->cabac_model = HFI_H264_CABAC_MODEL_0;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*entropy);
+ packet->shdr.hdr.size += sizeof(*entropy);
break;
}
case HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2: {
@@ -663,7 +662,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
((max_qp & 0xFF) << 16);
range->min_qp.enable = 7;
range->max_qp.enable = 7;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*range);
+ packet->shdr.hdr.size += sizeof(*range);
break;
}
case HFI_PROPERTY_CONFIG_FRAME_RATE: {
@@ -672,7 +671,7 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
frate->buffer_type = in->buffer_type;
frate->framerate = in->framerate;
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*frate);
+ packet->shdr.hdr.size += sizeof(*frate);
break;
}
case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: {
@@ -684,7 +683,62 @@ iris_hfi_gen1_packet_session_set_property(struct hfi_session_set_property_pkt *p
plane_actual_info->plane_format[0] = in->plane_format[0];
if (in->num_planes > 1)
plane_actual_info->plane_format[1] = in->plane_format[1];
- packet->shdr.hdr.size += sizeof(u32) + sizeof(*plane_actual_info);
+ packet->shdr.hdr.size += sizeof(*plane_actual_info);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: {
+ struct hfi_intra_refresh *in = pdata, *intra_refresh = prop_data;
+
+ intra_refresh->mode = in->mode;
+ intra_refresh->mbs = in->mbs;
+ packet->shdr.hdr.size += sizeof(*intra_refresh);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_LTRMODE: {
+ struct hfi_ltr_mode *in = pdata, *ltr_mode = prop_data;
+
+ ltr_mode->mode = in->mode;
+ ltr_mode->count = in->count;
+ ltr_mode->trust_mode = in->trust_mode;
+ packet->shdr.hdr.size += sizeof(*ltr_mode);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_USELTRFRAME: {
+ struct hfi_ltr_use *in = pdata, *ltr_use = prop_data;
+
+ ltr_use->frames = in->frames;
+ ltr_use->ref_ltr = in->ref_ltr;
+ ltr_use->use_constrnt = in->use_constrnt;
+ packet->shdr.hdr.size += sizeof(*ltr_use);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME: {
+ struct hfi_ltr_mark *in = pdata, *ltr_mark = prop_data;
+
+ ltr_mark->mark_frame = in->mark_frame;
+ packet->shdr.hdr.size += sizeof(*ltr_mark);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD: {
+ struct hfi_intra_period *in = pdata, *intra_period = prop_data;
+
+ intra_period->pframes = in->pframes;
+ intra_period->bframes = in->bframes;
+ packet->shdr.hdr.size += sizeof(*intra_period);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER: {
+ u32 *in = pdata;
+
+ packet->data[1] = *in;
+ packet->shdr.hdr.size += sizeof(u32);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER: {
+ u32 *in = pdata;
+
+ packet->data[1] = *in;
+ packet->shdr.hdr.size += sizeof(u32);
break;
}
default:
@@ -918,7 +972,7 @@ static int iris_hfi_gen1_set_bufsize(struct iris_inst *inst, u32 plane)
if (iris_split_mode_enabled(inst)) {
bufsz.type = HFI_BUFFER_OUTPUT;
- bufsz.size = inst->core->iris_platform_data->get_vpu_buffer_size(inst, BUF_DPB);
+ bufsz.size = inst->core->iris_firmware_desc->get_vpu_buffer_size(inst, BUF_DPB);
ret = hfi_gen1_set_property(inst, ptype, &bufsz, sizeof(bufsz));
if (ret)
@@ -1033,8 +1087,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p
};
if (inst->domain == DECODER) {
- config_params = core->iris_platform_data->dec_input_config_params_default;
- config_params_size = core->iris_platform_data->dec_input_config_params_default_size;
+ config_params = core->iris_firmware_data->dec_input_config_params_default;
+ config_params_size = core->iris_firmware_data->dec_input_config_params_default_size;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
handler = vdec_prop_type_handle_inp_arr;
handler_size = ARRAY_SIZE(vdec_prop_type_handle_inp_arr);
@@ -1043,8 +1097,8 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p
handler_size = ARRAY_SIZE(vdec_prop_type_handle_out_arr);
}
} else {
- config_params = core->iris_platform_data->enc_input_config_params;
- config_params_size = core->iris_platform_data->enc_input_config_params_size;
+ config_params = core->iris_firmware_data->enc_input_config_params;
+ config_params_size = core->iris_firmware_data->enc_input_config_params_size;
handler = venc_prop_type_handle_inp_arr;
handler_size = ARRAY_SIZE(venc_prop_type_handle_inp_arr);
}
@@ -1063,11 +1117,7 @@ static int iris_hfi_gen1_session_set_config_params(struct iris_inst *inst, u32 p
return 0;
}
-static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = {
- .sys_init = iris_hfi_gen1_sys_init,
- .sys_image_version = iris_hfi_gen1_sys_image_version,
- .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse,
- .sys_pc_prep = iris_hfi_gen1_sys_pc_prep,
+static const struct iris_hfi_session_ops iris_hfi_gen1_session_ops = {
.session_open = iris_hfi_gen1_session_open,
.session_set_config_params = iris_hfi_gen1_session_set_config_params,
.session_set_property = iris_hfi_gen1_session_set_property,
@@ -1080,12 +1130,31 @@ static const struct iris_hfi_command_ops iris_hfi_gen1_command_ops = {
.session_close = iris_hfi_gen1_session_close,
};
-void iris_hfi_gen1_command_ops_init(struct iris_core *core)
+static struct iris_inst *iris_hfi_gen1_get_instance(void)
{
- core->hfi_ops = &iris_hfi_gen1_command_ops;
+ struct iris_inst *out;
+
+ out = kzalloc_obj(*out);
+ if (!out)
+ return NULL;
+
+ out->hfi_session_ops = &iris_hfi_gen1_session_ops;
+
+ return out;
}
-struct iris_inst *iris_hfi_gen1_get_instance(void)
+static const struct iris_hfi_sys_ops iris_hfi_gen1_sys_ops = {
+ .sys_init = iris_hfi_gen1_sys_init,
+ .sys_image_version = iris_hfi_gen1_sys_image_version,
+ .sys_interframe_powercollapse = iris_hfi_gen1_sys_interframe_powercollapse,
+ .sys_pc_prep = iris_hfi_gen1_sys_pc_prep,
+
+ .sys_hfi_response_handler = iris_hfi_gen1_response_handler,
+
+ .sys_get_instance = iris_hfi_gen1_get_instance,
+};
+
+void iris_hfi_gen1_sys_ops_init(struct iris_core *core)
{
- return kzalloc_obj(struct iris_inst);
+ core->hfi_sys_ops = &iris_hfi_gen1_sys_ops;
}
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 42226ccee3d9..0e4dee192384 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_defines.h
@@ -139,9 +139,28 @@
#define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL 0x2005003
#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL 0x2005004
#define HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE_V2 0x2005009
+
+#define HFI_INTRA_REFRESH_NONE 0x1
+#define HFI_INTRA_REFRESH_CYCLIC 0x2
+#define HFI_INTRA_REFRESH_ADAPTIVE 0x3
+#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE 0x4
+#define HFI_INTRA_REFRESH_RANDOM 0x5
+
+#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH 0x200500d
+
+#define HFI_LTR_MODE_DISABLE 0x0
+#define HFI_LTR_MODE_MANUAL 0x1
+#define HFI_LTR_MODE_PERIODIC 0x2
+
+#define HFI_PROPERTY_PARAM_VENC_LTRMODE 0x200501c
#define HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES 0x2005020
+#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER 0x2005026
#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE 0x2006001
+#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD 0x2006003
+#define HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME 0x2006009
+#define HFI_PROPERTY_CONFIG_VENC_USELTRFRAME 0x200600a
#define HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER 0x2006008
+#define HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER 0x200600b
struct hfi_pkt_hdr {
u32 size;
@@ -447,6 +466,36 @@ struct hfi_framerate {
u32 framerate;
};
+struct hfi_intra_refresh {
+ u32 mode;
+ u32 mbs;
+};
+
+struct hfi_ltr_mode {
+ u32 mode;
+ u32 count;
+ u32 trust_mode;
+};
+
+struct hfi_ltr_use {
+ u32 ref_ltr;
+ u32 use_constrnt;
+ u32 frames;
+};
+
+struct hfi_ltr_mark {
+ u32 mark_frame;
+};
+
+struct hfi_max_num_b_frames {
+ u32 max_num_b_frames;
+};
+
+struct hfi_intra_period {
+ u32 pframes;
+ u32 bframes;
+};
+
struct hfi_event_data {
u32 error;
u32 height;
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 8e864c239e29..bfd7495bf44f 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen1_response.c
@@ -688,7 +688,7 @@ static void iris_hfi_gen1_flush_debug_queue(struct iris_core *core, u8 *packet)
}
}
-static void iris_hfi_gen1_response_handler(struct iris_core *core)
+void iris_hfi_gen1_response_handler(struct iris_core *core)
{
memset(core->response_packet, 0, sizeof(struct hfi_pkt_hdr));
while (!iris_hfi_queue_msg_read(core, core->response_packet)) {
@@ -698,12 +698,3 @@ static void iris_hfi_gen1_response_handler(struct iris_core *core)
iris_hfi_gen1_flush_debug_queue(core, core->response_packet);
}
-
-static const struct iris_hfi_response_ops iris_hfi_gen1_response_ops = {
- .hfi_response_handler = iris_hfi_gen1_response_handler,
-};
-
-void iris_hfi_gen1_response_ops_init(struct iris_core *core)
-{
- core->hfi_response_ops = &iris_hfi_gen1_response_ops;
-}
diff --git a/drivers/media/platform/qcom/iris/iris_platform_gen2.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
index 5da90d47f9c6..acc0ed8adda1 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_gen2.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.c
@@ -4,40 +4,15 @@
* Copyright (c) 2025 Linaro Ltd
*/
-#include "iris_core.h"
#include "iris_ctrls.h"
#include "iris_hfi_gen2.h"
#include "iris_hfi_gen2_defines.h"
#include "iris_platform_common.h"
#include "iris_vpu_buffer.h"
-#include "iris_vpu_common.h"
-
-#include "iris_platform_qcs8300.h"
-#include "iris_platform_sm8650.h"
-#include "iris_platform_sm8750.h"
#define VIDEO_ARCH_LX 1
#define BITRATE_MAX 245000000
-static struct iris_fmt platform_fmts_sm8550_dec[] = {
- [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,
- },
- [IRIS_FMT_AV1] = {
- .pixfmt = V4L2_PIX_FMT_AV1,
- .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- },
-};
-
static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
{
.cap_id = PROFILE_H264,
@@ -56,9 +31,10 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
{
.cap_id = PROFILE_HEVC,
.min = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
- .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE,
+ .max = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10,
.step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN) |
- BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE),
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10),
.value = V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN,
.hfi_id = HFI_PROP_PROFILE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
@@ -287,7 +263,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_dec[] = {
{
.cap_id = BIT_DEPTH,
.min = BIT_DEPTH_8,
- .max = BIT_DEPTH_8,
+ .max = BIT_DEPTH_10,
.step_or_mask = 1,
.value = BIT_DEPTH_8,
.hfi_id = HFI_PROP_LUMA_CHROMA_BIT_DEPTH,
@@ -416,7 +392,7 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
.hfi_id = HFI_PROP_TOTAL_BITRATE,
.flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
- .set = iris_set_bitrate,
+ .set = iris_set_bitrate_gen2,
},
{
.cap_id = BITRATE_PEAK,
@@ -738,75 +714,230 @@ static const struct platform_inst_fw_cap inst_fw_cap_sm8550_enc[] = {
.value = 0,
.flags = CAP_FLAG_OUTPUT_PORT |
CAP_FLAG_DYNAMIC_ALLOWED,
- .set = iris_set_ir_period,
+ .set = iris_set_ir_period_gen2,
+ },
+ {
+ .cap_id = LTR_COUNT,
+ .min = 0,
+ .max = MAX_LTR_FRAME_COUNT_GEN2,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_LTR_COUNT,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_ltr_count_gen2,
+ },
+ {
+ .cap_id = USE_LTR,
+ .min = 0,
+ .max = ((1 << MAX_LTR_FRAME_COUNT_GEN2) - 1),
+ .step_or_mask = 0,
+ .value = 0,
+ .hfi_id = HFI_PROP_LTR_USE,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_use_and_mark_ltr,
+ },
+ {
+ .cap_id = MARK_LTR,
+ .min = INVALID_DEFAULT_MARK_OR_USE_LTR,
+ .max = (MAX_LTR_FRAME_COUNT_GEN2 - 1),
+ .step_or_mask = 1,
+ .value = INVALID_DEFAULT_MARK_OR_USE_LTR,
+ .hfi_id = HFI_PROP_LTR_MARK,
+ .flags = CAP_FLAG_INPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_use_and_mark_ltr,
+ },
+ {
+ .cap_id = B_FRAME,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_MAX_B_FRAMES,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ .set = iris_set_u32,
+ },
+ {
+ .cap_id = LAYER_ENABLE,
+ .min = 0,
+ .max = 1,
+ .step_or_mask = 1,
+ .value = 0,
+ .flags = CAP_FLAG_OUTPUT_PORT,
+ },
+ {
+ .cap_id = LAYER_TYPE_H264,
+ .min = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B,
+ .max = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_B) |
+ BIT(V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P),
+ .value = V4L2_MPEG_VIDEO_H264_HIERARCHICAL_CODING_P,
+ .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_layer_type,
+ },
+ {
+ .cap_id = LAYER_TYPE_HEVC,
+ .min = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B,
+ .max = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P,
+ .step_or_mask = BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_B) |
+ BIT(V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P),
+ .value = V4L2_MPEG_VIDEO_HEVC_HIERARCHICAL_CODING_P,
+ .hfi_id = HFI_PROP_LAYER_ENCODING_TYPE,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_MENU,
+ .set = iris_set_layer_type,
+ },
+ {
+ .cap_id = LAYER_COUNT_H264,
+ .min = 0,
+ .max = 5,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_LAYER_COUNT,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_count_gen2,
+ },
+ {
+ .cap_id = LAYER_COUNT_HEVC,
+ .min = 0,
+ .max = 5,
+ .step_or_mask = 1,
+ .value = 0,
+ .hfi_id = HFI_PROP_LAYER_COUNT,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_count_gen2,
+ },
+ {
+ .cap_id = LAYER0_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER1,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
},
-};
-
-static struct platform_inst_caps platform_inst_cap_sm8550 = {
- .min_frame_width = 96,
- .max_frame_width = 8192,
- .min_frame_height = 96,
- .max_frame_height = 8192,
- .max_mbpf = (8192 * 4352) / 256,
- .mb_cycles_vpp = 200,
- .mb_cycles_fw = 489583,
- .mb_cycles_fw_vpp = 66234,
- .num_comv = 0,
- .max_frame_rate = MAXIMUM_FPS,
- .max_operating_rate = MAXIMUM_FPS,
-};
-
-static void iris_set_sm8550_preset_registers(struct iris_core *core)
-{
- writel(0x0, core->reg_base + 0xB0088);
-}
-
-static const struct icc_info sm8550_icc_table[] = {
- { "cpu-cfg", 1000, 1000 },
- { "video-mem", 1000, 15000000 },
-};
-
-static const char * const sm8550_clk_reset_table[] = { "bus" };
-
-static const struct bw_info sm8550_bw_table_dec[] = {
- { ((4096 * 2160) / 256) * 60, 1608000 },
- { ((4096 * 2160) / 256) * 30, 826000 },
- { ((1920 * 1080) / 256) * 60, 567000 },
- { ((1920 * 1080) / 256) * 30, 294000 },
-};
-
-static const char * const sm8550_pmdomain_table[] = { "venus", "vcodec0" };
-
-static const char * const sm8550_opp_pd_table[] = { "mxc", "mmcx" };
-
-static const struct platform_clk_data sm8550_clk_table[] = {
- {IRIS_AXI_CLK, "iface" },
- {IRIS_CTRL_CLK, "core" },
- {IRIS_HW_CLK, "vcodec0_core" },
-};
-
-static const char * const sm8550_opp_clk_table[] = {
- "vcodec0_core",
- NULL,
-};
-
-static struct ubwc_config_data ubwc_config_sm8550 = {
- .max_channels = 8,
- .mal_length = 32,
- .highest_bank_bit = 16,
- .bank_swzl_level = 0,
- .bank_swz2_level = 1,
- .bank_swz3_level = 1,
- .bank_spreading = 1,
-};
-
-static const struct tz_cp_config tz_cp_config_sm8550[] = {
{
- .cp_start = 0,
- .cp_size = 0x25800000,
- .cp_nonpixel_start = 0x01000000,
- .cp_nonpixel_size = 0x24800000,
+ .cap_id = LAYER1_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER2,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
},
+ {
+ .cap_id = LAYER2_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER3,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER3_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER4,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER4_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER5,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER5_BITRATE_H264,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER6,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER0_BITRATE_HEVC,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER1,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER1_BITRATE_HEVC,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER2,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER2_BITRATE_HEVC,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER3,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER3_BITRATE_HEVC,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER4,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER4_BITRATE_HEVC,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER5,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ },
+ {
+ .cap_id = LAYER5_BITRATE_HEVC,
+ .min = 1,
+ .max = BITRATE_MAX,
+ .step_or_mask = 1,
+ .value = BITRATE_DEFAULT,
+ .hfi_id = HFI_PROP_BITRATE_LAYER6,
+ .flags = CAP_FLAG_OUTPUT_PORT | CAP_FLAG_INPUT_PORT |
+ CAP_FLAG_DYNAMIC_ALLOWED,
+ .set = iris_set_layer_bitrate,
+ }
};
static const u32 sm8550_vdec_input_config_params_default[] = {
@@ -866,6 +997,7 @@ static const u32 sm8550_vdec_output_config_params[] = {
HFI_PROP_OPB_ENABLE,
HFI_PROP_COLOR_FORMAT,
HFI_PROP_LINEAR_STRIDE_SCANLINE,
+ HFI_PROP_UBWC_STRIDE_SCANLINE,
};
static const u32 sm8550_venc_output_config_params[] = {
@@ -921,347 +1053,16 @@ static const u32 sm8550_enc_op_int_buf_tbl[] = {
BUF_SCRATCH_2,
};
-const struct iris_platform_data sm8550_data = {
- .get_instance = iris_hfi_gen2_get_instance,
- .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
- .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
- .get_vpu_buffer_size = iris_vpu_buf_size,
- .vpu_ops = &iris_vpu3_ops,
- .set_preset_registers = iris_set_sm8550_preset_registers,
- .icc_tbl = sm8550_icc_table,
- .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
- .clk_rst_tbl = sm8550_clk_reset_table,
- .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
- .bw_tbl_dec = sm8550_bw_table_dec,
- .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
- .pmdomain_tbl = sm8550_pmdomain_table,
- .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
- .opp_pd_tbl = sm8550_opp_pd_table,
- .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
- .clk_tbl = sm8550_clk_table,
- .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
- .opp_clk_tbl = sm8550_opp_clk_table,
- /* Upper bound of DMA address range */
- .dma_mask = 0xe0000000 - 1,
- .fwname = "qcom/vpu/vpu30_p4.mbn",
- .pas_id = IRIS_PAS_ID,
- .inst_iris_fmts = platform_fmts_sm8550_dec,
- .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
- .inst_caps = &platform_inst_cap_sm8550,
- .inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
- .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
- .inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
- .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = tz_cp_config_sm8550,
- .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
- .core_arch = VIDEO_ARCH_LX,
- .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
- .ubwc_config = &ubwc_config_sm8550,
- .num_vpp_pipe = 4,
- .max_session_count = 16,
- .max_core_mbpf = NUM_MBS_8K * 2,
- .max_core_mbps = ((7680 * 4320) / 256) * 60,
- .dec_input_config_params_default =
- sm8550_vdec_input_config_params_default,
- .dec_input_config_params_default_size =
- ARRAY_SIZE(sm8550_vdec_input_config_params_default),
- .dec_input_config_params_hevc =
- sm8550_vdec_input_config_param_hevc,
- .dec_input_config_params_hevc_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
- .dec_input_config_params_vp9 =
- sm8550_vdec_input_config_param_vp9,
- .dec_input_config_params_vp9_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
- .dec_input_config_params_av1 =
- sm8550_vdec_input_config_param_av1,
- .dec_input_config_params_av1_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_av1),
- .dec_output_config_params =
- sm8550_vdec_output_config_params,
- .dec_output_config_params_size =
- ARRAY_SIZE(sm8550_vdec_output_config_params),
-
- .enc_input_config_params =
- sm8550_venc_input_config_params,
- .enc_input_config_params_size =
- ARRAY_SIZE(sm8550_venc_input_config_params),
- .enc_output_config_params =
- sm8550_venc_output_config_params,
- .enc_output_config_params_size =
- ARRAY_SIZE(sm8550_venc_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_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_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1,
- .dec_output_prop_av1_size =
- ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1),
-
- .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
- .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
- .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
- .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
-
- .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
- .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
- .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
- .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
-};
+const struct iris_firmware_data iris_hfi_gen2_data = {
+ .init_hfi_ops = iris_hfi_gen2_sys_ops_init,
-/*
- * Shares most of SM8550 data except:
- * - vpu_ops to iris_vpu33_ops
- * - clk_rst_tbl to sm8650_clk_reset_table
- * - controller_rst_tbl to sm8650_controller_reset_table
- * - fwname to "qcom/vpu/vpu33_p4.mbn"
- */
-const struct iris_platform_data sm8650_data = {
- .get_instance = iris_hfi_gen2_get_instance,
- .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
- .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
- .get_vpu_buffer_size = iris_vpu33_buf_size,
- .vpu_ops = &iris_vpu33_ops,
- .set_preset_registers = iris_set_sm8550_preset_registers,
- .icc_tbl = sm8550_icc_table,
- .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
- .clk_rst_tbl = sm8650_clk_reset_table,
- .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table),
- .controller_rst_tbl = sm8650_controller_reset_table,
- .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table),
- .bw_tbl_dec = sm8550_bw_table_dec,
- .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
- .pmdomain_tbl = sm8550_pmdomain_table,
- .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
- .opp_pd_tbl = sm8550_opp_pd_table,
- .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
- .clk_tbl = sm8550_clk_table,
- .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
- .opp_clk_tbl = sm8550_opp_clk_table,
- /* Upper bound of DMA address range */
- .dma_mask = 0xe0000000 - 1,
- .fwname = "qcom/vpu/vpu33_p4.mbn",
- .pas_id = IRIS_PAS_ID,
- .inst_iris_fmts = platform_fmts_sm8550_dec,
- .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
- .inst_caps = &platform_inst_cap_sm8550,
- .inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
- .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
- .inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
- .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = tz_cp_config_sm8550,
- .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
.core_arch = VIDEO_ARCH_LX,
- .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
- .ubwc_config = &ubwc_config_sm8550,
- .num_vpp_pipe = 4,
- .max_session_count = 16,
- .max_core_mbpf = NUM_MBS_8K * 2,
- .max_core_mbps = ((7680 * 4320) / 256) * 60,
- .dec_input_config_params_default =
- sm8550_vdec_input_config_params_default,
- .dec_input_config_params_default_size =
- ARRAY_SIZE(sm8550_vdec_input_config_params_default),
- .dec_input_config_params_hevc =
- sm8550_vdec_input_config_param_hevc,
- .dec_input_config_params_hevc_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
- .dec_input_config_params_vp9 =
- sm8550_vdec_input_config_param_vp9,
- .dec_input_config_params_vp9_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
- .dec_input_config_params_av1 =
- sm8550_vdec_input_config_param_av1,
- .dec_input_config_params_av1_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_av1),
- .dec_output_config_params =
- sm8550_vdec_output_config_params,
- .dec_output_config_params_size =
- ARRAY_SIZE(sm8550_vdec_output_config_params),
- .enc_input_config_params =
- sm8550_venc_input_config_params,
- .enc_input_config_params_size =
- ARRAY_SIZE(sm8550_venc_input_config_params),
- .enc_output_config_params =
- sm8550_venc_output_config_params,
- .enc_output_config_params_size =
- ARRAY_SIZE(sm8550_venc_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_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_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1,
- .dec_output_prop_av1_size =
- ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1),
-
- .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
- .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
- .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
- .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
-
- .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
- .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
- .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
- .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
-};
-
-const struct iris_platform_data sm8750_data = {
- .get_instance = iris_hfi_gen2_get_instance,
- .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
- .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
- .get_vpu_buffer_size = iris_vpu33_buf_size,
- .vpu_ops = &iris_vpu35_ops,
- .set_preset_registers = iris_set_sm8550_preset_registers,
- .icc_tbl = sm8550_icc_table,
- .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
- .clk_rst_tbl = sm8750_clk_reset_table,
- .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table),
- .bw_tbl_dec = sm8550_bw_table_dec,
- .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
- .pmdomain_tbl = sm8550_pmdomain_table,
- .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
- .opp_pd_tbl = sm8550_opp_pd_table,
- .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
- .clk_tbl = sm8750_clk_table,
- .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table),
- .opp_clk_tbl = sm8550_opp_clk_table,
- /* Upper bound of DMA address range */
- .dma_mask = 0xe0000000 - 1,
- .fwname = "qcom/vpu/vpu35_p4.mbn",
- .pas_id = IRIS_PAS_ID,
- .inst_iris_fmts = platform_fmts_sm8550_dec,
- .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
- .inst_caps = &platform_inst_cap_sm8550,
.inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
.inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
.inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
.inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = tz_cp_config_sm8550,
- .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
- .core_arch = VIDEO_ARCH_LX,
- .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
- .ubwc_config = &ubwc_config_sm8550,
- .num_vpp_pipe = 4,
- .max_session_count = 16,
- .max_core_mbpf = NUM_MBS_8K * 2,
- .max_core_mbps = ((7680 * 4320) / 256) * 60,
- .dec_input_config_params_default =
- sm8550_vdec_input_config_params_default,
- .dec_input_config_params_default_size =
- ARRAY_SIZE(sm8550_vdec_input_config_params_default),
- .dec_input_config_params_hevc =
- sm8550_vdec_input_config_param_hevc,
- .dec_input_config_params_hevc_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_hevc),
- .dec_input_config_params_vp9 =
- sm8550_vdec_input_config_param_vp9,
- .dec_input_config_params_vp9_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_vp9),
- .dec_input_config_params_av1 =
- sm8550_vdec_input_config_param_av1,
- .dec_input_config_params_av1_size =
- ARRAY_SIZE(sm8550_vdec_input_config_param_av1),
- .dec_output_config_params =
- sm8550_vdec_output_config_params,
- .dec_output_config_params_size =
- ARRAY_SIZE(sm8550_vdec_output_config_params),
- .enc_input_config_params =
- sm8550_venc_input_config_params,
- .enc_input_config_params_size =
- ARRAY_SIZE(sm8550_venc_input_config_params),
- .enc_output_config_params =
- sm8550_venc_output_config_params,
- .enc_output_config_params_size =
- ARRAY_SIZE(sm8550_venc_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_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_output_prop_av1 = sm8550_vdec_subscribe_output_properties_av1,
- .dec_output_prop_av1_size =
- ARRAY_SIZE(sm8550_vdec_subscribe_output_properties_av1),
-
- .dec_ip_int_buf_tbl = sm8550_dec_ip_int_buf_tbl,
- .dec_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_ip_int_buf_tbl),
- .dec_op_int_buf_tbl = sm8550_dec_op_int_buf_tbl,
- .dec_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_dec_op_int_buf_tbl),
-
- .enc_ip_int_buf_tbl = sm8550_enc_ip_int_buf_tbl,
- .enc_ip_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_ip_int_buf_tbl),
- .enc_op_int_buf_tbl = sm8550_enc_op_int_buf_tbl,
- .enc_op_int_buf_tbl_size = ARRAY_SIZE(sm8550_enc_op_int_buf_tbl),
-};
-
-/*
- * Shares most of SM8550 data except:
- * - inst_caps to platform_inst_cap_qcs8300
- */
-const struct iris_platform_data qcs8300_data = {
- .get_instance = iris_hfi_gen2_get_instance,
- .init_hfi_command_ops = iris_hfi_gen2_command_ops_init,
- .init_hfi_response_ops = iris_hfi_gen2_response_ops_init,
- .get_vpu_buffer_size = iris_vpu_buf_size,
- .vpu_ops = &iris_vpu3_ops,
- .set_preset_registers = iris_set_sm8550_preset_registers,
- .icc_tbl = sm8550_icc_table,
- .icc_tbl_size = ARRAY_SIZE(sm8550_icc_table),
- .clk_rst_tbl = sm8550_clk_reset_table,
- .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
- .bw_tbl_dec = sm8550_bw_table_dec,
- .bw_tbl_dec_size = ARRAY_SIZE(sm8550_bw_table_dec),
- .pmdomain_tbl = sm8550_pmdomain_table,
- .pmdomain_tbl_size = ARRAY_SIZE(sm8550_pmdomain_table),
- .opp_pd_tbl = sm8550_opp_pd_table,
- .opp_pd_tbl_size = ARRAY_SIZE(sm8550_opp_pd_table),
- .clk_tbl = sm8550_clk_table,
- .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
- .opp_clk_tbl = sm8550_opp_clk_table,
- /* Upper bound of DMA address range */
- .dma_mask = 0xe0000000 - 1,
- .fwname = "qcom/vpu/vpu30_p4_s6.mbn",
- .pas_id = IRIS_PAS_ID,
- .inst_iris_fmts = platform_fmts_sm8550_dec,
- .inst_iris_fmts_size = ARRAY_SIZE(platform_fmts_sm8550_dec),
- .inst_caps = &platform_inst_cap_qcs8300,
- .inst_fw_caps_dec = inst_fw_cap_sm8550_dec,
- .inst_fw_caps_dec_size = ARRAY_SIZE(inst_fw_cap_sm8550_dec),
- .inst_fw_caps_enc = inst_fw_cap_sm8550_enc,
- .inst_fw_caps_enc_size = ARRAY_SIZE(inst_fw_cap_sm8550_enc),
- .tz_cp_config_data = tz_cp_config_sm8550,
- .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_sm8550),
- .core_arch = VIDEO_ARCH_LX,
- .hw_response_timeout = HW_RESPONSE_TIMEOUT_VALUE,
- .ubwc_config = &ubwc_config_sm8550,
- .num_vpp_pipe = 2,
- .max_session_count = 16,
- .max_core_mbpf = ((4096 * 2176) / 256) * 4,
- .max_core_mbps = (((3840 * 2176) / 256) * 120),
.dec_input_config_params_default =
sm8550_vdec_input_config_params_default,
.dec_input_config_params_default_size =
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
index b9d3749a10ef..21ab58e0aa84 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2.h
@@ -34,8 +34,7 @@ struct iris_inst_hfi_gen2 {
struct hfi_subscription_params dst_subcr_params;
};
-void iris_hfi_gen2_command_ops_init(struct iris_core *core);
-void iris_hfi_gen2_response_ops_init(struct iris_core *core);
-struct iris_inst *iris_hfi_gen2_get_instance(void);
+void iris_hfi_gen2_sys_ops_init(struct iris_core *core);
+void iris_hfi_gen2_response_handler(struct iris_core *core);
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
index 30bfd90d423b..ca2954f8bd3a 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_command.c
@@ -10,7 +10,6 @@
#define UNSPECIFIED_COLOR_FORMAT 5
#define NUM_SYS_INIT_PACKETS 8
-#define NUM_COMV_AV1 18
#define SYS_INIT_PKT_SIZE (sizeof(struct iris_hfi_header) + \
NUM_SYS_INIT_PACKETS * (sizeof(struct iris_hfi_packet) + sizeof(u32)))
@@ -481,8 +480,20 @@ static int iris_hfi_gen2_set_colorformat(struct iris_inst *inst, u32 plane)
if (inst->domain == DECODER) {
pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
- hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ?
- HFI_COLOR_FMT_NV12 : HFI_COLOR_FMT_NV12_UBWC;
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ hfi_colorformat = HFI_COLOR_FMT_NV12;
+ break;
+ case V4L2_PIX_FMT_QC08C:
+ hfi_colorformat = HFI_COLOR_FMT_NV12_UBWC;
+ break;
+ case V4L2_PIX_FMT_P010:
+ hfi_colorformat = HFI_COLOR_FMT_P010;
+ break;
+ case V4L2_PIX_FMT_QC10C:
+ hfi_colorformat = HFI_COLOR_FMT_TP10_UBWC;
+ break;
+ }
} else {
pixelformat = inst->fmt_src->fmt.pix_mp.pixelformat;
hfi_colorformat = pixelformat == V4L2_PIX_FMT_NV12 ?
@@ -517,7 +528,8 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32
stride_uv = stride_y;
scanline_uv = scanline_y / 2;
- if (pixelformat != V4L2_PIX_FMT_NV12)
+ if (pixelformat != V4L2_PIX_FMT_NV12 &&
+ pixelformat != V4L2_PIX_FMT_P010)
return 0;
payload[0] = stride_y << 16 | scanline_y;
@@ -532,6 +544,61 @@ static int iris_hfi_gen2_set_linear_stride_scanline(struct iris_inst *inst, u32
sizeof(u64));
}
+static int iris_hfi_gen2_set_ubwc_stride_scanline(struct iris_inst *inst, u32 plane)
+{
+ u32 meta_stride_y, meta_scanline_y, meta_stride_uv, meta_scanline_uv;
+ u32 stride_y, scanline_y, stride_uv, scanline_uv;
+ u32 port = iris_hfi_gen2_get_port(inst, plane);
+ u32 pixelformat, width, height;
+ u32 payload[4];
+
+ if (inst->domain != DECODER ||
+ inst->fmt_src->fmt.pix_mp.pixelformat != V4L2_PIX_FMT_AV1)
+ return 0;
+
+ pixelformat = inst->fmt_dst->fmt.pix_mp.pixelformat;
+ width = inst->fmt_dst->fmt.pix_mp.width;
+ height = inst->fmt_dst->fmt.pix_mp.height;
+
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_QC08C:
+ stride_y = ALIGN(width, 128);
+ scanline_y = ALIGN(height, 32);
+ stride_uv = ALIGN(width, 128);
+ scanline_uv = ALIGN((height + 1) >> 1, 32);
+ meta_stride_y = ALIGN(DIV_ROUND_UP(width, 32), 64);
+ meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 8), 16);
+ meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 16), 64);
+ meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 8), 16);
+ break;
+ case V4L2_PIX_FMT_QC10C:
+ stride_y = ALIGN(width * 4 / 3, 256);
+ scanline_y = ALIGN(height, 16);
+ stride_uv = ALIGN(width * 4 / 3, 256);
+ scanline_uv = ALIGN((height + 1) >> 1, 16);
+ meta_stride_y = ALIGN(DIV_ROUND_UP(width, 48), 64);
+ meta_scanline_y = ALIGN(DIV_ROUND_UP(height, 4), 16);
+ meta_stride_uv = ALIGN(DIV_ROUND_UP((width + 1) >> 1, 24), 64);
+ meta_scanline_uv = ALIGN(DIV_ROUND_UP((height + 1) >> 1, 4), 16);
+ break;
+ default:
+ return 0;
+ }
+
+ payload[0] = stride_y << 16 | scanline_y;
+ payload[1] = stride_uv << 16 | scanline_uv;
+ payload[2] = meta_stride_y << 16 | meta_scanline_y;
+ payload[3] = meta_stride_uv << 16 | meta_scanline_uv;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_UBWC_STRIDE_SCANLINE,
+ HFI_HOST_FLAGS_NONE,
+ port,
+ HFI_PAYLOAD_U32_ARRAY,
+ &payload[0],
+ sizeof(u32) * 4);
+}
+
static int iris_hfi_gen2_set_tier(struct iris_inst *inst, u32 plane)
{
u32 port = iris_hfi_gen2_get_port(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
@@ -601,7 +668,7 @@ static int iris_hfi_gen2_set_super_block(struct iris_inst *inst, u32 plane)
static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 plane)
{
- const struct iris_platform_data *pdata = inst->core->iris_platform_data;
+ const struct iris_firmware_data *fdata = inst->core->iris_firmware_data;
u32 config_params_size = 0, i, j;
const u32 *config_params = NULL;
int ret;
@@ -620,6 +687,7 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
{HFI_PROP_OPB_ENABLE, iris_hfi_gen2_set_opb_enable },
{HFI_PROP_COLOR_FORMAT, iris_hfi_gen2_set_colorformat },
{HFI_PROP_LINEAR_STRIDE_SCANLINE, iris_hfi_gen2_set_linear_stride_scanline },
+ {HFI_PROP_UBWC_STRIDE_SCANLINE, iris_hfi_gen2_set_ubwc_stride_scanline },
{HFI_PROP_TIER, iris_hfi_gen2_set_tier },
{HFI_PROP_FRAME_RATE, iris_hfi_gen2_set_frame_rate },
{HFI_PROP_AV1_FILM_GRAIN_PRESENT, iris_hfi_gen2_set_film_grain },
@@ -630,31 +698,31 @@ static int iris_hfi_gen2_session_set_config_params(struct iris_inst *inst, u32 p
if (inst->domain == DECODER) {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
if (inst->codec == V4L2_PIX_FMT_H264) {
- config_params = pdata->dec_input_config_params_default;
- config_params_size = pdata->dec_input_config_params_default_size;
+ config_params = fdata->dec_input_config_params_default;
+ config_params_size = fdata->dec_input_config_params_default_size;
} else if (inst->codec == V4L2_PIX_FMT_HEVC) {
- config_params = pdata->dec_input_config_params_hevc;
- config_params_size = pdata->dec_input_config_params_hevc_size;
+ config_params = fdata->dec_input_config_params_hevc;
+ config_params_size = fdata->dec_input_config_params_hevc_size;
} else if (inst->codec == V4L2_PIX_FMT_VP9) {
- config_params = pdata->dec_input_config_params_vp9;
- config_params_size = pdata->dec_input_config_params_vp9_size;
+ config_params = fdata->dec_input_config_params_vp9;
+ config_params_size = fdata->dec_input_config_params_vp9_size;
} else if (inst->codec == V4L2_PIX_FMT_AV1) {
- config_params = pdata->dec_input_config_params_av1;
- config_params_size = pdata->dec_input_config_params_av1_size;
+ config_params = fdata->dec_input_config_params_av1;
+ config_params_size = fdata->dec_input_config_params_av1_size;
} else {
return -EINVAL;
}
} else {
- config_params = pdata->dec_output_config_params;
- config_params_size = pdata->dec_output_config_params_size;
+ config_params = fdata->dec_output_config_params;
+ config_params_size = fdata->dec_output_config_params_size;
}
} else {
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- config_params = pdata->enc_input_config_params;
- config_params_size = pdata->enc_input_config_params_size;
+ config_params = fdata->enc_input_config_params;
+ config_params_size = fdata->enc_input_config_params_size;
} else {
- config_params = pdata->enc_output_config_params;
- config_params_size = pdata->enc_output_config_params_size;
+ config_params = fdata->enc_output_config_params;
+ config_params_size = fdata->enc_output_config_params_size;
}
}
@@ -849,24 +917,24 @@ static int iris_hfi_gen2_subscribe_change_param(struct iris_inst *inst, u32 plan
switch (inst->codec) {
case V4L2_PIX_FMT_H264:
- change_param = core->iris_platform_data->dec_input_config_params_default;
+ change_param = core->iris_firmware_data->dec_input_config_params_default;
change_param_size =
- core->iris_platform_data->dec_input_config_params_default_size;
+ core->iris_firmware_data->dec_input_config_params_default_size;
break;
case V4L2_PIX_FMT_HEVC:
- change_param = core->iris_platform_data->dec_input_config_params_hevc;
+ change_param = core->iris_firmware_data->dec_input_config_params_hevc;
change_param_size =
- core->iris_platform_data->dec_input_config_params_hevc_size;
+ core->iris_firmware_data->dec_input_config_params_hevc_size;
break;
case V4L2_PIX_FMT_VP9:
- change_param = core->iris_platform_data->dec_input_config_params_vp9;
+ change_param = core->iris_firmware_data->dec_input_config_params_vp9;
change_param_size =
- core->iris_platform_data->dec_input_config_params_vp9_size;
+ core->iris_firmware_data->dec_input_config_params_vp9_size;
break;
case V4L2_PIX_FMT_AV1:
- change_param = core->iris_platform_data->dec_input_config_params_av1;
+ change_param = core->iris_firmware_data->dec_input_config_params_av1;
change_param_size =
- core->iris_platform_data->dec_input_config_params_av1_size;
+ core->iris_firmware_data->dec_input_config_params_av1_size;
break;
}
@@ -996,29 +1064,29 @@ static int iris_hfi_gen2_subscribe_property(struct iris_inst *inst, u32 plane)
return 0;
if (V4L2_TYPE_IS_OUTPUT(plane)) {
- subscribe_prop_size = core->iris_platform_data->dec_input_prop_size;
- subcribe_prop = core->iris_platform_data->dec_input_prop;
+ subscribe_prop_size = core->iris_firmware_data->dec_input_prop_size;
+ subcribe_prop = core->iris_firmware_data->dec_input_prop;
} else {
switch (inst->codec) {
case V4L2_PIX_FMT_H264:
- subcribe_prop = core->iris_platform_data->dec_output_prop_avc;
+ subcribe_prop = core->iris_firmware_data->dec_output_prop_avc;
subscribe_prop_size =
- core->iris_platform_data->dec_output_prop_avc_size;
+ core->iris_firmware_data->dec_output_prop_avc_size;
break;
case V4L2_PIX_FMT_HEVC:
- subcribe_prop = core->iris_platform_data->dec_output_prop_hevc;
+ subcribe_prop = core->iris_firmware_data->dec_output_prop_hevc;
subscribe_prop_size =
- core->iris_platform_data->dec_output_prop_hevc_size;
+ core->iris_firmware_data->dec_output_prop_hevc_size;
break;
case V4L2_PIX_FMT_VP9:
- subcribe_prop = core->iris_platform_data->dec_output_prop_vp9;
+ subcribe_prop = core->iris_firmware_data->dec_output_prop_vp9;
subscribe_prop_size =
- core->iris_platform_data->dec_output_prop_vp9_size;
+ core->iris_firmware_data->dec_output_prop_vp9_size;
break;
case V4L2_PIX_FMT_AV1:
- subcribe_prop = core->iris_platform_data->dec_output_prop_av1;
+ subcribe_prop = core->iris_firmware_data->dec_output_prop_av1;
subscribe_prop_size =
- core->iris_platform_data->dec_output_prop_av1_size;
+ core->iris_firmware_data->dec_output_prop_av1_size;
break;
}
}
@@ -1205,27 +1273,19 @@ static u32 iris_hfi_gen2_buf_type_from_driver(u32 domain, enum iris_buffer_type
}
}
-static int iris_set_num_comv(struct iris_inst *inst)
+static int iris_hfi_gen2_set_num_comv(struct iris_inst *inst)
{
- struct platform_inst_caps *caps;
- struct iris_core *core = inst->core;
- u32 num_comv;
-
- caps = core->iris_platform_data->inst_caps;
-
- /*
- * AV1 needs more comv buffers than other codecs.
- * Update accordingly.
- */
- num_comv = (inst->codec == V4L2_PIX_FMT_AV1) ?
- NUM_COMV_AV1 : caps->num_comv;
-
- return core->hfi_ops->session_set_property(inst,
- HFI_PROP_COMV_BUFFER_COUNT,
- HFI_HOST_FLAGS_NONE,
- HFI_PORT_BITSTREAM,
- HFI_PAYLOAD_U32,
- &num_comv, sizeof(u32));
+ u32 num_comv = inst->buffers[BUF_OUTPUT].min_count;
+
+ if (inst->fw_min_count)
+ num_comv = inst->fw_min_count;
+
+ return iris_hfi_gen2_session_set_property(inst,
+ HFI_PROP_COMV_BUFFER_COUNT,
+ HFI_HOST_FLAGS_NONE,
+ HFI_PORT_BITSTREAM,
+ HFI_PAYLOAD_U32,
+ &num_comv, sizeof(u32));
}
static void iris_hfi_gen2_get_buffer(u32 domain, struct iris_buffer *buffer,
@@ -1257,7 +1317,7 @@ static int iris_hfi_gen2_session_queue_buffer(struct iris_inst *inst, struct iri
iris_hfi_gen2_get_buffer(inst->domain, buffer, &hfi_buffer);
if (buffer->type == BUF_COMV) {
- ret = iris_set_num_comv(inst);
+ ret = iris_hfi_gen2_set_num_comv(inst);
if (ret)
return ret;
}
@@ -1300,11 +1360,7 @@ static int iris_hfi_gen2_session_release_buffer(struct iris_inst *inst, struct i
inst_hfi_gen2->packet->size);
}
-static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = {
- .sys_init = iris_hfi_gen2_sys_init,
- .sys_image_version = iris_hfi_gen2_sys_image_version,
- .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse,
- .sys_pc_prep = iris_hfi_gen2_sys_pc_prep,
+static const struct iris_hfi_session_ops iris_hfi_gen2_session_ops = {
.session_open = iris_hfi_gen2_session_open,
.session_set_config_params = iris_hfi_gen2_session_set_config_params,
.session_set_property = iris_hfi_gen2_session_set_property,
@@ -1319,17 +1375,32 @@ static const struct iris_hfi_command_ops iris_hfi_gen2_command_ops = {
.session_close = iris_hfi_gen2_session_close,
};
-void iris_hfi_gen2_command_ops_init(struct iris_core *core)
-{
- core->hfi_ops = &iris_hfi_gen2_command_ops;
-}
-
-struct iris_inst *iris_hfi_gen2_get_instance(void)
+static struct iris_inst *iris_hfi_gen2_get_instance(void)
{
struct iris_inst_hfi_gen2 *out;
/* The allocation is intentionally larger than struct iris_inst. */
out = kzalloc_obj(*out);
+ if (!out)
+ return NULL;
+
+ out->inst.hfi_session_ops = &iris_hfi_gen2_session_ops;
return &out->inst;
}
+
+static const struct iris_hfi_sys_ops iris_hfi_gen2_sys_ops = {
+ .sys_init = iris_hfi_gen2_sys_init,
+ .sys_image_version = iris_hfi_gen2_sys_image_version,
+ .sys_interframe_powercollapse = iris_hfi_gen2_sys_interframe_powercollapse,
+ .sys_pc_prep = iris_hfi_gen2_sys_pc_prep,
+
+ .sys_hfi_response_handler = iris_hfi_gen2_response_handler,
+
+ .sys_get_instance = iris_hfi_gen2_get_instance,
+};
+
+void iris_hfi_gen2_sys_ops_init(struct iris_core *core)
+{
+ core->hfi_sys_ops = &iris_hfi_gen2_sys_ops;
+}
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 cecf771c55dd..776b21cd11b2 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_defines.h
@@ -71,7 +71,25 @@ enum hfi_rate_control {
#define HFI_PROP_MIN_QP_PACKED 0x0300012f
#define HFI_PROP_MAX_QP_PACKED 0x03000130
#define HFI_PROP_IR_RANDOM_PERIOD 0x03000131
+#define HFI_PROP_LTR_COUNT 0x03000134
+#define HFI_PROP_LTR_MARK 0x03000135
+#define HFI_PROP_LTR_USE 0x03000136
+
+enum hfi_layer_encoding_type {
+ HFI_HIER_P_SLIDING_WINDOW = 0x1,
+ HFI_HIER_P_HYBRID_LTR = 0x2,
+ HFI_HIER_B = 0x3,
+};
+
+#define HFI_PROP_LAYER_ENCODING_TYPE 0x03000138
+#define HFI_PROP_LAYER_COUNT 0x03000139
#define HFI_PROP_TOTAL_BITRATE 0x0300013b
+#define HFI_PROP_BITRATE_LAYER1 0x0300013c
+#define HFI_PROP_BITRATE_LAYER2 0x0300013d
+#define HFI_PROP_BITRATE_LAYER3 0x0300013e
+#define HFI_PROP_BITRATE_LAYER4 0x0300013f
+#define HFI_PROP_BITRATE_LAYER5 0x03000140
+#define HFI_PROP_BITRATE_LAYER6 0x03000141
#define HFI_PROP_MAX_GOP_FRAMES 0x03000146
#define HFI_PROP_MAX_B_FRAMES 0x03000147
#define HFI_PROP_QUALITY_MODE 0x03000148
@@ -118,6 +136,7 @@ enum hfi_flip {
#define HFI_PROP_OPB_ENABLE 0x03000184
#define HFI_PROP_AV1_TILE_ROWS_COLUMNS 0x03000187
#define HFI_PROP_AV1_DRAP_CONFIG 0x03000189
+#define HFI_PROP_UBWC_STRIDE_SCANLINE 0x03000190
#define HFI_PROP_COMV_BUFFER_COUNT 0x03000193
#define HFI_PROP_AV1_UNIFORM_TILE_SPACING 0x03000197
#define HFI_PROP_END 0x03FFFFFF
diff --git a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
index d77fa29f44fc..0d05dd2afc07 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_packet.c
@@ -3,6 +3,9 @@
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
+#include <linux/printk.h>
+#include <linux/soc/qcom/ubwc.h>
+
#include "iris_hfi_common.h"
#include "iris_hfi_gen2.h"
#include "iris_hfi_gen2_packet.h"
@@ -120,6 +123,7 @@ static void iris_hfi_gen2_create_packet(struct iris_hfi_header *hdr, u32 pkt_typ
void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_header *hdr)
{
+ const struct qcom_ubwc_cfg_data *ubwc = core->ubwc_cfg;
u32 payload = 0;
iris_hfi_gen2_create_header(hdr, 0, core->header_id++);
@@ -136,7 +140,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade
&payload,
sizeof(u32));
- payload = core->iris_platform_data->ubwc_config->max_channels;
+ payload = qcom_ubwc_macrotile_mode(ubwc) ? 8 : 4;
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_MAX_CHANNELS,
HFI_HOST_FLAGS_NONE,
@@ -146,7 +150,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade
&payload,
sizeof(u32));
- payload = core->iris_platform_data->ubwc_config->mal_length;
+ payload = qcom_ubwc_min_acc_length_64b(ubwc) ? 64 : 32;
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_MAL_LENGTH,
HFI_HOST_FLAGS_NONE,
@@ -156,7 +160,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade
&payload,
sizeof(u32));
- payload = core->iris_platform_data->ubwc_config->highest_bank_bit;
+ payload = ubwc->highest_bank_bit;
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_HBB,
HFI_HOST_FLAGS_NONE,
@@ -166,7 +170,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade
&payload,
sizeof(u32));
- payload = core->iris_platform_data->ubwc_config->bank_swzl_level;
+ payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL1);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SWZL_LEVEL1,
HFI_HOST_FLAGS_NONE,
@@ -176,7 +180,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade
&payload,
sizeof(u32));
- payload = core->iris_platform_data->ubwc_config->bank_swz2_level;
+ payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL2);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SWZL_LEVEL2,
HFI_HOST_FLAGS_NONE,
@@ -186,7 +190,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade
&payload,
sizeof(u32));
- payload = core->iris_platform_data->ubwc_config->bank_swz3_level;
+ payload = !!(qcom_ubwc_swizzle(ubwc) & UBWC_SWIZZLE_ENABLE_LVL3);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SWZL_LEVEL3,
HFI_HOST_FLAGS_NONE,
@@ -196,7 +200,7 @@ void iris_hfi_gen2_packet_sys_init(struct iris_core *core, struct iris_hfi_heade
&payload,
sizeof(u32));
- payload = core->iris_platform_data->ubwc_config->bank_spreading;
+ payload = qcom_ubwc_bank_spread(ubwc);
iris_hfi_gen2_create_packet(hdr,
HFI_PROP_UBWC_BANK_SPREADING,
HFI_HOST_FLAGS_NONE,
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 8e19f61bbbf9..25162ae71357 100644
--- a/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
+++ b/drivers/media/platform/qcom/iris/iris_hfi_gen2_response.c
@@ -542,9 +542,33 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
pixmp_ip->width = width;
pixmp_ip->height = height;
- pixmp_op->width = ALIGN(width, 128);
- pixmp_op->height = ALIGN(height, 32);
- pixmp_op->plane_fmt[0].bytesperline = ALIGN(width, 128);
+ if (subsc_params.bit_depth == BIT_DEPTH_8 &&
+ pixmp_op->pixelformat != V4L2_PIX_FMT_NV12 &&
+ pixmp_op->pixelformat != V4L2_PIX_FMT_QC08C)
+ pixmp_op->pixelformat = V4L2_PIX_FMT_NV12;
+ else if (subsc_params.bit_depth == BIT_DEPTH_10 &&
+ pixmp_op->pixelformat != V4L2_PIX_FMT_P010 &&
+ pixmp_op->pixelformat != V4L2_PIX_FMT_QC10C)
+ pixmp_op->pixelformat = V4L2_PIX_FMT_P010;
+
+ switch (pixmp_op->pixelformat) {
+ case V4L2_PIX_FMT_P010:
+ pixmp_op->width = ALIGN(width, 128);
+ pixmp_op->height = ALIGN(height, 32);
+ pixmp_op->plane_fmt[0].bytesperline = ALIGN(width * 2, 256);
+ break;
+ case V4L2_PIX_FMT_QC10C:
+ pixmp_op->width = roundup(width, 192);
+ pixmp_op->height = ALIGN(height, 16);
+ pixmp_op->plane_fmt[0].bytesperline = ALIGN(pixmp_op->width * 4 / 3, 256);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_QC08C:
+ pixmp_op->width = ALIGN(width, 128);
+ pixmp_op->height = ALIGN(height, 32);
+ pixmp_op->plane_fmt[0].bytesperline = pixmp_op->width;
+ break;
+ }
pixmp_op->plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
matrix_coeff = subsc_params.color_info & 0xFF;
@@ -610,7 +634,12 @@ static void iris_hfi_gen2_read_input_subcr_params(struct iris_inst *inst)
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 ||
+ if (subsc_params.bit_depth == BIT_DEPTH_8)
+ inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_8;
+ else
+ inst->fw_caps[BIT_DEPTH].value = BIT_DEPTH_10;
+
+ if ((subsc_params.bit_depth != BIT_DEPTH_8 && subsc_params.bit_depth != BIT_DEPTH_10) ||
!(subsc_params.coded_frames & HFI_BITMASK_FRAME_MBS_ONLY_FLAG)) {
dev_err(core->dev, "unsupported content, bit depth: %x, pic_struct = %x\n",
subsc_params.bit_depth, subsc_params.coded_frames);
@@ -977,7 +1006,7 @@ static void iris_hfi_gen2_flush_debug_queue(struct iris_core *core, u8 *packet)
}
}
-static void iris_hfi_gen2_response_handler(struct iris_core *core)
+void iris_hfi_gen2_response_handler(struct iris_core *core)
{
if (iris_vpu_watchdog(core, core->intr_status)) {
struct iris_hfi_packet pkt = {.type = HFI_SYS_ERROR_WD_TIMEOUT};
@@ -997,12 +1026,3 @@ static void iris_hfi_gen2_response_handler(struct iris_core *core)
iris_hfi_gen2_flush_debug_queue(core, core->response_packet);
}
-
-static const struct iris_hfi_response_ops iris_hfi_gen2_response_ops = {
- .hfi_response_handler = iris_hfi_gen2_response_handler,
-};
-
-void iris_hfi_gen2_response_ops_init(struct iris_core *core)
-{
- core->hfi_response_ops = &iris_hfi_gen2_response_ops;
-}
diff --git a/drivers/media/platform/qcom/iris/iris_instance.h b/drivers/media/platform/qcom/iris/iris_instance.h
index 16965150f427..a770331d1675 100644
--- a/drivers/media/platform/qcom/iris/iris_instance.h
+++ b/drivers/media/platform/qcom/iris/iris_instance.h
@@ -15,6 +15,8 @@
#define DEFAULT_WIDTH 320
#define DEFAULT_HEIGHT 240
+struct iris_hfi_session_ops;
+
enum iris_fmt_type_out {
IRIS_FMT_H264,
IRIS_FMT_HEVC,
@@ -25,11 +27,8 @@ enum iris_fmt_type_out {
enum iris_fmt_type_cap {
IRIS_FMT_NV12,
IRIS_FMT_QC08C,
-};
-
-struct iris_fmt {
- u32 pixfmt;
- u32 type;
+ IRIS_FMT_TP10,
+ IRIS_FMT_QC10C,
};
/**
@@ -38,6 +37,7 @@ struct iris_fmt {
* @list: used for attach an instance to the core
* @core: pointer to core structure
* @session_id: id of current video session
+ * @hfi_session_ops: iris HFI session ops
* @ctx_q_lock: lock to serialize queues related ioctls
* @lock: lock to seralise forward and reverse threads
* @fh: reference of v4l2 file handler
@@ -67,6 +67,8 @@ struct iris_fmt {
* @metadata_idx: index for metadata buffer
* @codec: codec type
* @last_buffer_dequeued: a flag to indicate that last buffer is sent by driver
+ * @last_buf_ns: start time of received input buffer for current one second FPS window
+ * @frame_counter: input buffer counter for current one second FPS window
* @frame_rate: frame rate of current instance
* @operating_rate: operating rate of current instance
* @hfi_rc_type: rate control type
@@ -74,12 +76,15 @@ struct iris_fmt {
* @enc_raw_height: source image height for encoder instance
* @enc_scale_width: scale width for encoder instance
* @enc_scale_height: scale height for encoder instance
+ * @hfi_layer_type: hierarchical coding layer type
+ * @hfi_layer_count: hierarchical coding layer count
*/
struct iris_inst {
struct list_head list;
struct iris_core *core;
u32 session_id;
+ const struct iris_hfi_session_ops *hfi_session_ops;
struct mutex ctx_q_lock;/* lock to serialize queues related ioctls */
struct mutex lock; /* lock to serialize forward and reverse threads */
struct v4l2_fh fh;
@@ -109,6 +114,8 @@ struct iris_inst {
u32 metadata_idx;
u32 codec;
bool last_buffer_dequeued;
+ u64 last_buf_ns;
+ u32 frame_counter;
u32 frame_rate;
u32 operating_rate;
u32 hfi_rc_type;
@@ -116,6 +123,8 @@ struct iris_inst {
u32 enc_raw_height;
u32 enc_scale_width;
u32 enc_scale_height;
+ u32 hfi_layer_type;
+ u32 hfi_layer_count;
};
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_common.h b/drivers/media/platform/qcom/iris/iris_platform_common.h
index 5a489917580e..c9256f2323dc 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_common.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_common.h
@@ -12,12 +12,12 @@
struct iris_core;
struct iris_inst;
-#define IRIS_PAS_ID 9
#define HW_RESPONSE_TIMEOUT_VALUE (1000) /* milliseconds */
#define AUTOSUSPEND_DELAY_VALUE (HW_RESPONSE_TIMEOUT_VALUE + 500) /* milliseconds */
#define REGISTER_BIT_DEPTH(luma, chroma) ((luma) << 16 | (chroma))
#define BIT_DEPTH_8 REGISTER_BIT_DEPTH(8, 8)
+#define BIT_DEPTH_10 REGISTER_BIT_DEPTH(10, 10)
#define CODED_FRAMES_PROGRESSIVE 0x0
#define DEFAULT_MAX_HOST_BUF_COUNT 64
#define DEFAULT_MAX_HOST_BURST_BUF_COUNT 256
@@ -29,6 +29,15 @@ struct iris_inst;
#define MAX_QP_HEVC 63
#define DEFAULT_QP 20
#define BITRATE_DEFAULT 20000000
+#define INVALID_DEFAULT_MARK_OR_USE_LTR -1
+#define MAX_LTR_FRAME_COUNT_GEN1 4
+#define MAX_LTR_FRAME_COUNT_GEN2 2
+#define MAX_LAYER_HB 3
+#define MAX_AVC_LAYER_HP_HYBRID_LTR 5
+#define MAX_AVC_LAYER_HP_SLIDING_WINDOW 3
+#define MAX_HEVC_LAYER_HP_SLIDING_WINDOW 3
+#define MAX_HEVC_VBR_LAYER_HP_SLIDING_WINDOW 5
+#define MAX_HIER_CODING_LAYER_GEN1 6
enum stage_type {
STAGE_1 = 1,
@@ -41,12 +50,16 @@ enum pipe_type {
PIPE_4 = 4,
};
+extern const struct iris_firmware_data iris_hfi_gen1_data;
+extern const struct iris_firmware_data iris_hfi_gen2_data;
+
extern const struct iris_platform_data qcs8300_data;
extern const struct iris_platform_data sc7280_data;
extern const struct iris_platform_data sm8250_data;
extern const struct iris_platform_data sm8550_data;
extern const struct iris_platform_data sm8650_data;
extern const struct iris_platform_data sm8750_data;
+extern const struct iris_platform_data x1p42100_data;
enum platform_clk_type {
IRIS_AXI_CLK, /* AXI0 in case of platforms with multiple AXI clocks */
@@ -75,16 +88,6 @@ struct tz_cp_config {
u32 cp_nonpixel_size;
};
-struct ubwc_config_data {
- u32 max_channels;
- u32 mal_length;
- u32 highest_bank_bit;
- u32 bank_swzl_level;
- u32 bank_swz2_level;
- u32 bank_swz3_level;
- u32 bank_spreading;
-};
-
struct platform_inst_caps {
u32 min_frame_width;
u32 max_frame_width;
@@ -95,7 +98,6 @@ struct platform_inst_caps {
u32 mb_cycles_vpp;
u32 mb_cycles_fw;
u32 mb_cycles_fw_vpp;
- u32 num_comv;
u32 max_frame_rate;
u32 max_operating_rate;
};
@@ -159,6 +161,28 @@ enum platform_inst_fw_cap_type {
VFLIP,
IR_TYPE,
IR_PERIOD,
+ LTR_COUNT,
+ USE_LTR,
+ MARK_LTR,
+ B_FRAME,
+ INTRA_PERIOD,
+ LAYER_ENABLE,
+ LAYER_TYPE_H264,
+ LAYER_TYPE_HEVC,
+ LAYER_COUNT_H264,
+ LAYER_COUNT_HEVC,
+ LAYER0_BITRATE_H264,
+ LAYER1_BITRATE_H264,
+ LAYER2_BITRATE_H264,
+ LAYER3_BITRATE_H264,
+ LAYER4_BITRATE_H264,
+ LAYER5_BITRATE_H264,
+ LAYER0_BITRATE_HEVC,
+ LAYER1_BITRATE_HEVC,
+ LAYER2_BITRATE_HEVC,
+ LAYER3_BITRATE_HEVC,
+ LAYER4_BITRATE_HEVC,
+ LAYER5_BITRATE_HEVC,
INST_FW_CAP_MAX,
};
@@ -212,50 +236,16 @@ enum platform_pm_domain_type {
IRIS_APV_HW_POWER_DOMAIN,
};
-struct iris_platform_data {
- void (*init_hfi_command_ops)(struct iris_core *core);
- void (*init_hfi_response_ops)(struct iris_core *core);
- struct iris_inst *(*get_instance)(void);
- u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type);
- const struct vpu_ops *vpu_ops;
- void (*set_preset_registers)(struct iris_core *core);
- const struct icc_info *icc_tbl;
- unsigned int icc_tbl_size;
- const struct bw_info *bw_tbl_dec;
- unsigned int bw_tbl_dec_size;
- const char * const *pmdomain_tbl;
- unsigned int pmdomain_tbl_size;
- const char * const *opp_pd_tbl;
- unsigned int opp_pd_tbl_size;
- const struct platform_clk_data *clk_tbl;
- const char * const *opp_clk_tbl;
- unsigned int clk_tbl_size;
- const char * const *clk_rst_tbl;
- unsigned int clk_rst_tbl_size;
- const char * const *controller_rst_tbl;
- unsigned int controller_rst_tbl_size;
- u64 dma_mask;
- const char *fwname;
- u32 pas_id;
- struct iris_fmt *inst_iris_fmts;
- u32 inst_iris_fmts_size;
- struct platform_inst_caps *inst_caps;
+struct iris_firmware_data {
+ void (*init_hfi_ops)(struct iris_core *core);
+
+ u32 core_arch;
+
const struct platform_inst_fw_cap *inst_fw_caps_dec;
u32 inst_fw_caps_dec_size;
const struct platform_inst_fw_cap *inst_fw_caps_enc;
u32 inst_fw_caps_enc_size;
- const struct tz_cp_config *tz_cp_config_data;
- u32 tz_cp_config_data_size;
- u32 core_arch;
- u32 hw_response_timeout;
- struct ubwc_config_data *ubwc_config;
- u32 num_vpp_pipe;
- bool no_aon;
- u32 max_session_count;
- /* max number of macroblocks per frame supported */
- u32 max_core_mbpf;
- /* max number of macroblocks per second supported */
- u32 max_core_mbps;
+
const u32 *dec_input_config_params_default;
unsigned int dec_input_config_params_default_size;
const u32 *dec_input_config_params_hevc;
@@ -270,6 +260,7 @@ struct iris_platform_data {
unsigned int enc_input_config_params_size;
const u32 *enc_output_config_params;
unsigned int enc_output_config_params_size;
+
const u32 *dec_input_prop;
unsigned int dec_input_prop_size;
const u32 *dec_output_prop_avc;
@@ -280,6 +271,7 @@ struct iris_platform_data {
unsigned int dec_output_prop_vp9_size;
const u32 *dec_output_prop_av1;
unsigned int dec_output_prop_av1_size;
+
const u32 *dec_ip_int_buf_tbl;
unsigned int dec_ip_int_buf_tbl_size;
const u32 *dec_op_int_buf_tbl;
@@ -290,4 +282,48 @@ struct iris_platform_data {
unsigned int enc_op_int_buf_tbl_size;
};
+struct iris_firmware_desc {
+ const struct iris_firmware_data *firmware_data;
+ u32 (*get_vpu_buffer_size)(struct iris_inst *inst, enum iris_buffer_type buffer_type);
+ const char *fwname;
+};
+
+struct iris_platform_data {
+ /*
+ * XXX: replace with gen1 / gen2 pointers once we have platforms
+ * supporting both firmware kinds.
+ */
+ const struct iris_firmware_desc *firmware_desc;
+
+ const struct vpu_ops *vpu_ops;
+ const struct icc_info *icc_tbl;
+ unsigned int icc_tbl_size;
+ const struct bw_info *bw_tbl_dec;
+ unsigned int bw_tbl_dec_size;
+ const char * const *pmdomain_tbl;
+ unsigned int pmdomain_tbl_size;
+ const char * const *opp_pd_tbl;
+ unsigned int opp_pd_tbl_size;
+ const struct platform_clk_data *clk_tbl;
+ const char * const *opp_clk_tbl;
+ unsigned int clk_tbl_size;
+ const char * const *clk_rst_tbl;
+ unsigned int clk_rst_tbl_size;
+ const char * const *controller_rst_tbl;
+ unsigned int controller_rst_tbl_size;
+ u64 dma_mask;
+ const u32 *inst_iris_fmts;
+ u32 inst_iris_fmts_size;
+ struct platform_inst_caps *inst_caps;
+ const struct tz_cp_config *tz_cp_config_data;
+ u32 tz_cp_config_data_size;
+ u32 num_vpp_pipe;
+ bool no_aon;
+ u32 max_session_count;
+ /* max number of macroblocks per frame supported */
+ u32 max_core_mbpf;
+ /* max number of macroblocks per second supported */
+ u32 max_core_mbps;
+};
+
#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
index 61025f1e965b..3cfecae80d1e 100644
--- a/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
+++ b/drivers/media/platform/qcom/iris/iris_platform_qcs8300.h
@@ -15,7 +15,6 @@ static struct platform_inst_caps platform_inst_cap_qcs8300 = {
.mb_cycles_vpp = 200,
.mb_cycles_fw = 326389,
.mb_cycles_fw_vpp = 44156,
- .num_comv = 0,
.max_frame_rate = MAXIMUM_FPS,
.max_operating_rate = MAXIMUM_FPS,
};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8250.h b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h
new file mode 100644
index 000000000000..50306043eb8e
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8250.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __IRIS_PLATFORM_SM8250_H__
+#define __IRIS_PLATFORM_SM8250_H__
+
+static const struct bw_info sm8250_bw_table_dec[] = {
+ { ((4096 * 2160) / 256) * 60, 2403000 },
+ { ((4096 * 2160) / 256) * 30, 1224000 },
+ { ((1920 * 1080) / 256) * 60, 812000 },
+ { ((1920 * 1080) / 256) * 30, 416000 },
+};
+
+static const char * const sm8250_opp_pd_table[] = { "mx", "mmcx" };
+
+static const struct platform_clk_data sm8250_clk_table[] = {
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_HW_CLK, "vcodec0_core" },
+};
+
+static const char * const sm8250_opp_clk_table[] = {
+ "vcodec0_core",
+ NULL,
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_sm8550.h b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h
new file mode 100644
index 000000000000..3c9dae995bb2
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_sm8550.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef __IRIS_PLATFORM_SM8550_H__
+#define __IRIS_PLATFORM_SM8550_H__
+
+static const char * const sm8550_clk_reset_table[] = { "bus" };
+
+static const struct platform_clk_data sm8550_clk_table[] = {
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_HW_CLK, "vcodec0_core" },
+};
+
+static struct platform_inst_caps platform_inst_cap_sm8550 = {
+ .min_frame_width = 96,
+ .max_frame_width = 8192,
+ .min_frame_height = 96,
+ .max_frame_height = 8192,
+ .max_mbpf = (8192 * 4352) / 256,
+ .mb_cycles_vpp = 200,
+ .mb_cycles_fw = 489583,
+ .mb_cycles_fw_vpp = 66234,
+ .max_frame_rate = MAXIMUM_FPS,
+ .max_operating_rate = MAXIMUM_FPS,
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu2.c b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c
new file mode 100644
index 000000000000..6e06a32822bb
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_vpu2.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_platform_common.h"
+#include "iris_resources.h"
+#include "iris_hfi_gen1.h"
+#include "iris_hfi_gen1_defines.h"
+#include "iris_vpu_buffer.h"
+#include "iris_vpu_common.h"
+#include "iris_instance.h"
+
+#include "iris_platform_sc7280.h"
+#include "iris_platform_sm8250.h"
+
+static const struct iris_firmware_desc iris_vpu20_p1_gen1_desc = {
+ .firmware_data = &iris_hfi_gen1_data,
+ .get_vpu_buffer_size = iris_vpu_buf_size,
+ .fwname = "qcom/vpu/vpu20_p1.mbn",
+};
+
+static const struct iris_firmware_desc iris_vpu20_p4_gen1_desc = {
+ .firmware_data = &iris_hfi_gen1_data,
+ .get_vpu_buffer_size = iris_vpu_buf_size,
+ .fwname = "qcom/vpu/vpu20_p4.mbn",
+};
+
+static const u32 iris_fmts_vpu2_dec[] = {
+ [IRIS_FMT_H264] = V4L2_PIX_FMT_H264,
+ [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC,
+ [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9,
+};
+
+static struct platform_inst_caps platform_inst_cap_vpu2 = {
+ .min_frame_width = 128,
+ .max_frame_width = 8192,
+ .min_frame_height = 128,
+ .max_frame_height = 8192,
+ .max_mbpf = 138240,
+ .mb_cycles_vsp = 25,
+ .mb_cycles_vpp = 200,
+ .max_frame_rate = MAXIMUM_FPS,
+ .max_operating_rate = MAXIMUM_FPS,
+};
+
+static const struct icc_info iris_icc_info_vpu2[] = {
+ { "cpu-cfg", 1000, 1000 },
+ { "video-mem", 1000, 15000000 },
+};
+
+static const char * const iris_clk_reset_table_vpu2[] = { "bus", "core" };
+
+static const char * const iris_pmdomain_table_vpu2[] = { "venus", "vcodec0" };
+
+static const struct tz_cp_config tz_cp_config_vpu2[] = {
+ {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+ },
+};
+
+const struct iris_platform_data sc7280_data = {
+ .firmware_desc = &iris_vpu20_p1_gen1_desc,
+ .vpu_ops = &iris_vpu2_ops,
+ .icc_tbl = iris_icc_info_vpu2,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2),
+ .bw_tbl_dec = sc7280_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sc7280_bw_table_dec),
+ .pmdomain_tbl = iris_pmdomain_table_vpu2,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2),
+ .opp_pd_tbl = sc7280_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sc7280_opp_pd_table),
+ .clk_tbl = sc7280_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sc7280_clk_table),
+ .opp_clk_tbl = sc7280_opp_clk_table,
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu2_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec),
+ .inst_caps = &platform_inst_cap_vpu2,
+ .tz_cp_config_data = tz_cp_config_vpu2,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2),
+ .num_vpp_pipe = 1,
+ .no_aon = true,
+ .max_session_count = 16,
+ .max_core_mbpf = 4096 * 2176 / 256 * 2 + 1920 * 1088 / 256,
+ /* max spec for SC7280 is 4096x2176@60fps */
+ .max_core_mbps = 4096 * 2176 / 256 * 60,
+};
+
+const struct iris_platform_data sm8250_data = {
+ .firmware_desc = &iris_vpu20_p4_gen1_desc,
+ .vpu_ops = &iris_vpu2_ops,
+ .icc_tbl = iris_icc_info_vpu2,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu2),
+ .clk_rst_tbl = iris_clk_reset_table_vpu2,
+ .clk_rst_tbl_size = ARRAY_SIZE(iris_clk_reset_table_vpu2),
+ .bw_tbl_dec = sm8250_bw_table_dec,
+ .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec),
+ .pmdomain_tbl = iris_pmdomain_table_vpu2,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu2),
+ .opp_pd_tbl = sm8250_opp_pd_table,
+ .opp_pd_tbl_size = ARRAY_SIZE(sm8250_opp_pd_table),
+ .clk_tbl = sm8250_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8250_clk_table),
+ .opp_clk_tbl = sm8250_opp_clk_table,
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu2_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu2_dec),
+ .inst_caps = &platform_inst_cap_vpu2,
+ .tz_cp_config_data = tz_cp_config_vpu2,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu2),
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = NUM_MBS_8K,
+ .max_core_mbps = ((7680 * 4320) / 256) * 60,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c
new file mode 100644
index 000000000000..2c63adbc5579
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_vpu3x.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2025 Linaro Ltd
+ */
+
+#include "iris_core.h"
+#include "iris_ctrls.h"
+#include "iris_hfi_gen2.h"
+#include "iris_hfi_gen2_defines.h"
+#include "iris_platform_common.h"
+#include "iris_vpu_buffer.h"
+#include "iris_vpu_common.h"
+
+#include "iris_platform_qcs8300.h"
+#include "iris_platform_sm8550.h"
+#include "iris_platform_sm8650.h"
+#include "iris_platform_sm8750.h"
+#include "iris_platform_x1p42100.h"
+
+static const struct iris_firmware_desc iris_vpu30_p4_s6_gen2_desc = {
+ .firmware_data = &iris_hfi_gen2_data,
+ .get_vpu_buffer_size = iris_vpu_buf_size,
+ .fwname = "qcom/vpu/vpu30_p4_s6.mbn",
+};
+
+static const struct iris_firmware_desc iris_vpu30_p4_gen2_desc = {
+ .firmware_data = &iris_hfi_gen2_data,
+ .get_vpu_buffer_size = iris_vpu_buf_size,
+ .fwname = "qcom/vpu/vpu30_p4.mbn",
+};
+
+static const struct iris_firmware_desc iris_vpu30_p1_gen2_desc = {
+ .firmware_data = &iris_hfi_gen2_data,
+ .get_vpu_buffer_size = iris_vpu_buf_size,
+ .fwname = "qcom/vpu/vpu30_p1_s7.mbn",
+};
+
+static const struct iris_firmware_desc iris_vpu33_p4_gen2_desc = {
+ .firmware_data = &iris_hfi_gen2_data,
+ .get_vpu_buffer_size = iris_vpu33_buf_size,
+ .fwname = "qcom/vpu/vpu33_p4.mbn",
+};
+
+static const struct iris_firmware_desc iris_vpu35_p4_gen2_desc = {
+ .firmware_data = &iris_hfi_gen2_data,
+ .get_vpu_buffer_size = iris_vpu33_buf_size,
+ .fwname = "qcom/vpu/vpu35_p4.mbn",
+};
+
+static const u32 iris_fmts_vpu3x_dec[] = {
+ [IRIS_FMT_H264] = V4L2_PIX_FMT_H264,
+ [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC,
+ [IRIS_FMT_VP9] = V4L2_PIX_FMT_VP9,
+ [IRIS_FMT_AV1] = V4L2_PIX_FMT_AV1,
+};
+
+static const struct icc_info iris_icc_info_vpu3x[] = {
+ { "cpu-cfg", 1000, 1000 },
+ { "video-mem", 1000, 15000000 },
+};
+
+static const struct bw_info iris_bw_table_dec_vpu3x[] = {
+ { ((4096 * 2160) / 256) * 60, 1608000 },
+ { ((4096 * 2160) / 256) * 30, 826000 },
+ { ((1920 * 1080) / 256) * 60, 567000 },
+ { ((1920 * 1080) / 256) * 30, 294000 },
+};
+
+static const char * const iris_pmdomain_table_vpu3x[] = { "venus", "vcodec0" };
+
+static const char * const iris_opp_pd_table_vpu3x[] = { "mxc", "mmcx" };
+
+static const char * const iris_opp_clk_table_vpu3x[] = {
+ "vcodec0_core",
+ NULL,
+};
+
+static const struct tz_cp_config tz_cp_config_vpu3[] = {
+ {
+ .cp_start = 0,
+ .cp_size = 0x25800000,
+ .cp_nonpixel_start = 0x01000000,
+ .cp_nonpixel_size = 0x24800000,
+ },
+};
+
+/*
+ * Shares most of SM8550 data except:
+ * - inst_caps to platform_inst_cap_qcs8300
+ */
+const struct iris_platform_data qcs8300_data = {
+ .firmware_desc = &iris_vpu30_p4_s6_gen2_desc,
+ .vpu_ops = &iris_vpu3_ops,
+ .icc_tbl = iris_icc_info_vpu3x,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
+ .clk_rst_tbl = sm8550_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
+ .bw_tbl_dec = iris_bw_table_dec_vpu3x,
+ .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
+ .pmdomain_tbl = iris_pmdomain_table_vpu3x,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
+ .opp_pd_tbl = iris_opp_pd_table_vpu3x,
+ .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ .opp_clk_tbl = iris_opp_clk_table_vpu3x,
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu3x_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
+ .inst_caps = &platform_inst_cap_qcs8300,
+ .tz_cp_config_data = tz_cp_config_vpu3,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
+ .num_vpp_pipe = 2,
+ .max_session_count = 16,
+ .max_core_mbpf = ((4096 * 2176) / 256) * 4,
+ .max_core_mbps = (((3840 * 2176) / 256) * 120),
+};
+
+const struct iris_platform_data sm8550_data = {
+ .firmware_desc = &iris_vpu30_p4_gen2_desc,
+ .vpu_ops = &iris_vpu3_ops,
+ .icc_tbl = iris_icc_info_vpu3x,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
+ .clk_rst_tbl = sm8550_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
+ .bw_tbl_dec = iris_bw_table_dec_vpu3x,
+ .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
+ .pmdomain_tbl = iris_pmdomain_table_vpu3x,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
+ .opp_pd_tbl = iris_opp_pd_table_vpu3x,
+ .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ .opp_clk_tbl = iris_opp_clk_table_vpu3x,
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu3x_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
+ .inst_caps = &platform_inst_cap_sm8550,
+ .tz_cp_config_data = tz_cp_config_vpu3,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .max_core_mbps = ((7680 * 4320) / 256) * 60,
+};
+
+/*
+ * Shares most of SM8550 data except:
+ * - vpu_ops to iris_vpu33_ops
+ * - clk_rst_tbl to sm8650_clk_reset_table
+ * - controller_rst_tbl to sm8650_controller_reset_table
+ */
+const struct iris_platform_data sm8650_data = {
+ .firmware_desc = &iris_vpu33_p4_gen2_desc,
+ .vpu_ops = &iris_vpu33_ops,
+ .icc_tbl = iris_icc_info_vpu3x,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
+ .clk_rst_tbl = sm8650_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8650_clk_reset_table),
+ .controller_rst_tbl = sm8650_controller_reset_table,
+ .controller_rst_tbl_size = ARRAY_SIZE(sm8650_controller_reset_table),
+ .bw_tbl_dec = iris_bw_table_dec_vpu3x,
+ .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
+ .pmdomain_tbl = iris_pmdomain_table_vpu3x,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
+ .opp_pd_tbl = iris_opp_pd_table_vpu3x,
+ .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
+ .clk_tbl = sm8550_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8550_clk_table),
+ .opp_clk_tbl = iris_opp_clk_table_vpu3x,
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu3x_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
+ .inst_caps = &platform_inst_cap_sm8550,
+ .tz_cp_config_data = tz_cp_config_vpu3,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .max_core_mbps = ((7680 * 4320) / 256) * 60,
+};
+
+const struct iris_platform_data sm8750_data = {
+ .firmware_desc = &iris_vpu35_p4_gen2_desc,
+ .vpu_ops = &iris_vpu35_ops,
+ .icc_tbl = iris_icc_info_vpu3x,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
+ .clk_rst_tbl = sm8750_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8750_clk_reset_table),
+ .bw_tbl_dec = iris_bw_table_dec_vpu3x,
+ .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
+ .pmdomain_tbl = iris_pmdomain_table_vpu3x,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
+ .opp_pd_tbl = iris_opp_pd_table_vpu3x,
+ .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
+ .clk_tbl = sm8750_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(sm8750_clk_table),
+ .opp_clk_tbl = iris_opp_clk_table_vpu3x,
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu3x_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
+ .inst_caps = &platform_inst_cap_sm8550,
+ .tz_cp_config_data = tz_cp_config_vpu3,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
+ .num_vpp_pipe = 4,
+ .max_session_count = 16,
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .max_core_mbps = ((7680 * 4320) / 256) * 60,
+};
+
+/*
+ * Shares most of SM8550 data except:
+ * - clk_tbl and opp_clk_tbl for x1p42100
+ * - different firmware
+ * - different num_vpp_pipe
+ */
+const struct iris_platform_data x1p42100_data = {
+ .firmware_desc = &iris_vpu30_p1_gen2_desc,
+ .vpu_ops = &iris_vpu3_ops,
+ .icc_tbl = iris_icc_info_vpu3x,
+ .icc_tbl_size = ARRAY_SIZE(iris_icc_info_vpu3x),
+ .clk_rst_tbl = sm8550_clk_reset_table,
+ .clk_rst_tbl_size = ARRAY_SIZE(sm8550_clk_reset_table),
+ .bw_tbl_dec = iris_bw_table_dec_vpu3x,
+ .bw_tbl_dec_size = ARRAY_SIZE(iris_bw_table_dec_vpu3x),
+ .pmdomain_tbl = iris_pmdomain_table_vpu3x,
+ .pmdomain_tbl_size = ARRAY_SIZE(iris_pmdomain_table_vpu3x),
+ .opp_pd_tbl = iris_opp_pd_table_vpu3x,
+ .opp_pd_tbl_size = ARRAY_SIZE(iris_opp_pd_table_vpu3x),
+ .clk_tbl = x1p42100_clk_table,
+ .clk_tbl_size = ARRAY_SIZE(x1p42100_clk_table),
+ .opp_clk_tbl = x1p42100_opp_clk_table,
+ /* Upper bound of DMA address range */
+ .dma_mask = 0xe0000000 - 1,
+ .inst_iris_fmts = iris_fmts_vpu3x_dec,
+ .inst_iris_fmts_size = ARRAY_SIZE(iris_fmts_vpu3x_dec),
+ .inst_caps = &platform_inst_cap_sm8550,
+ .tz_cp_config_data = tz_cp_config_vpu3,
+ .tz_cp_config_data_size = ARRAY_SIZE(tz_cp_config_vpu3),
+ .num_vpp_pipe = 1,
+ .max_session_count = 16,
+ .max_core_mbpf = NUM_MBS_8K * 2,
+ .max_core_mbps = ((7680 * 4320) / 256) * 60,
+};
diff --git a/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h
new file mode 100644
index 000000000000..d89acfbc1233
--- /dev/null
+++ b/drivers/media/platform/qcom/iris/iris_platform_x1p42100.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef __IRIS_PLATFORM_X1P42100_H__
+#define __IRIS_PLATFORM_X1P42100_H__
+
+static const struct platform_clk_data x1p42100_clk_table[] = {
+ {IRIS_AXI_CLK, "iface" },
+ {IRIS_CTRL_CLK, "core" },
+ {IRIS_HW_CLK, "vcodec0_core" },
+ {IRIS_BSE_HW_CLK, "vcodec0_bse" },
+};
+
+static const char *const x1p42100_opp_clk_table[] = {
+ "vcodec0_core",
+ "vcodec0_bse",
+ NULL,
+};
+
+#endif
diff --git a/drivers/media/platform/qcom/iris/iris_probe.c b/drivers/media/platform/qcom/iris/iris_probe.c
index ddaacda523ec..c2dcb50a2782 100644
--- a/drivers/media/platform/qcom/iris/iris_probe.c
+++ b/drivers/media/platform/qcom/iris/iris_probe.c
@@ -10,6 +10,7 @@
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
+#include <linux/soc/qcom/ubwc.h>
#include "iris_core.h"
#include "iris_ctrls.h"
@@ -64,6 +65,13 @@ static int iris_init_power_domains(struct iris_core *core)
return ret;
ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data, &core->opp_pmdomain_tbl);
+ /* backwards compatibility for incomplete ABI SM8250 */
+ if (ret == -ENODEV &&
+ of_device_is_compatible(core->dev->of_node, "qcom,sm8250-venus")) {
+ iris_opp_pd_data.num_pd_names--;
+ ret = devm_pm_domain_attach_list(core->dev, &iris_opp_pd_data,
+ &core->opp_pmdomain_tbl);
+ }
if (ret < 0)
return ret;
@@ -243,17 +251,21 @@ static int iris_probe(struct platform_device *pdev)
return core->irq;
core->iris_platform_data = of_device_get_match_data(core->dev);
+ core->iris_firmware_desc = core->iris_platform_data->firmware_desc;
+ core->iris_firmware_data = core->iris_firmware_desc->firmware_data;
+
+ core->ubwc_cfg = qcom_ubwc_config_get_data();
+ if (IS_ERR(core->ubwc_cfg))
+ return PTR_ERR(core->ubwc_cfg);
ret = devm_request_threaded_irq(core->dev, core->irq, iris_hfi_isr,
- iris_hfi_isr_handler, IRQF_TRIGGER_HIGH, "iris", core);
+ iris_hfi_isr_handler,
+ IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
+ "iris", core);
if (ret)
return ret;
- disable_irq_nosync(core->irq);
-
iris_init_ops(core);
- core->iris_platform_data->init_hfi_command_ops(core);
- core->iris_platform_data->init_hfi_response_ops(core);
ret = iris_init_resources(core);
if (ret)
@@ -352,7 +364,6 @@ static const struct of_device_id iris_dt_match[] = {
.compatible = "qcom,qcs8300-iris",
.data = &qcs8300_data,
},
-#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_VENUS))
{
.compatible = "qcom,sc7280-venus",
.data = &sc7280_data,
@@ -361,7 +372,6 @@ static const struct of_device_id iris_dt_match[] = {
.compatible = "qcom,sm8250-venus",
.data = &sm8250_data,
},
-#endif
{
.compatible = "qcom,sm8550-iris",
.data = &sm8550_data,
@@ -374,6 +384,10 @@ static const struct of_device_id iris_dt_match[] = {
.compatible = "qcom,sm8750-iris",
.data = &sm8750_data,
},
+ {
+ .compatible = "qcom,x1p42100-iris",
+ .data = &x1p42100_data,
+ },
{ },
};
MODULE_DEVICE_TABLE(of, iris_dt_match);
diff --git a/drivers/media/platform/qcom/iris/iris_utils.c b/drivers/media/platform/qcom/iris/iris_utils.c
index cfc5b576ec56..ba5c8dc1280c 100644
--- a/drivers/media/platform/qcom/iris/iris_utils.c
+++ b/drivers/media/platform/qcom/iris/iris_utils.c
@@ -35,7 +35,21 @@ int iris_get_mbpf(struct iris_inst *inst)
bool iris_split_mode_enabled(struct iris_inst *inst)
{
return inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_NV12 ||
- inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C;
+ inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC08C ||
+ inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_P010 ||
+ inst->fmt_dst->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_QC10C;
+}
+
+bool iris_fmt_is_8bit(u32 pixelformat)
+{
+ return pixelformat == V4L2_PIX_FMT_NV12 ||
+ pixelformat == V4L2_PIX_FMT_QC08C;
+}
+
+bool iris_fmt_is_10bit(u32 pixelformat)
+{
+ return pixelformat == V4L2_PIX_FMT_P010 ||
+ pixelformat == V4L2_PIX_FMT_QC10C;
}
void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
@@ -55,16 +69,13 @@ void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
int iris_wait_for_session_response(struct iris_inst *inst, bool is_flush)
{
- struct iris_core *core = inst->core;
- u32 hw_response_timeout_val;
struct completion *done;
int ret;
- hw_response_timeout_val = core->iris_platform_data->hw_response_timeout;
done = is_flush ? &inst->flush_completion : &inst->completion;
mutex_unlock(&inst->lock);
- ret = wait_for_completion_timeout(done, msecs_to_jiffies(hw_response_timeout_val));
+ ret = wait_for_completion_timeout(done, msecs_to_jiffies(HW_RESPONSE_TIMEOUT_VALUE));
mutex_lock(&inst->lock);
if (!ret) {
iris_inst_change_state(inst, IRIS_INST_ERROR);
diff --git a/drivers/media/platform/qcom/iris/iris_utils.h b/drivers/media/platform/qcom/iris/iris_utils.h
index b5705d156431..228a5f963812 100644
--- a/drivers/media/platform/qcom/iris/iris_utils.h
+++ b/drivers/media/platform/qcom/iris/iris_utils.h
@@ -45,6 +45,8 @@ bool iris_res_is_less_than(u32 width, u32 height,
u32 ref_width, u32 ref_height);
int iris_get_mbpf(struct iris_inst *inst);
bool iris_split_mode_enabled(struct iris_inst *inst);
+bool iris_fmt_is_8bit(u32 pixelformat);
+bool iris_fmt_is_10bit(u32 pixelformat);
struct iris_inst *iris_get_instance(struct iris_core *core, u32 session_id);
void iris_helper_buffers_done(struct iris_inst *inst, unsigned int type,
enum vb2_buffer_state state);
diff --git a/drivers/media/platform/qcom/iris/iris_vb2.c b/drivers/media/platform/qcom/iris/iris_vb2.c
index bf0b8400996e..a2ea2d67f60d 100644
--- a/drivers/media/platform/qcom/iris/iris_vb2.c
+++ b/drivers/media/platform/qcom/iris/iris_vb2.c
@@ -129,7 +129,7 @@ int iris_vb2_queue_setup(struct vb2_queue *q,
if (!inst->once_per_session_set) {
inst->once_per_session_set = true;
- ret = core->hfi_ops->session_open(inst);
+ ret = inst->hfi_session_ops->session_open(inst);
if (ret) {
ret = -EINVAL;
dev_err(core->dev, "session open failed\n");
diff --git a/drivers/media/platform/qcom/iris/iris_vdec.c b/drivers/media/platform/qcom/iris/iris_vdec.c
index 99d544e2af4f..9e228b70420e 100644
--- a/drivers/media/platform/qcom/iris/iris_vdec.c
+++ b/drivers/media/platform/qcom/iris/iris_vdec.c
@@ -24,7 +24,7 @@ int iris_vdec_inst_init(struct iris_inst *inst)
inst->fmt_src = kzalloc_obj(*inst->fmt_src);
inst->fmt_dst = kzalloc_obj(*inst->fmt_dst);
- inst->fw_min_count = MIN_BUFFERS;
+ inst->fw_min_count = 0;
f = inst->fmt_src;
f->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
@@ -54,6 +54,7 @@ int iris_vdec_inst_init(struct iris_inst *inst)
f->fmt.pix_mp.quantization = V4L2_QUANTIZATION_DEFAULT;
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = f->fmt.pix_mp.plane_fmt[0].sizeimage;
+ inst->frame_rate = MAXIMUM_FPS;
memcpy(&inst->fw_caps[0], &core->inst_fw_caps_dec[0],
INST_FW_CAP_MAX * sizeof(struct platform_inst_fw_cap));
@@ -61,23 +62,18 @@ int iris_vdec_inst_init(struct iris_inst *inst)
return iris_ctrls_init(inst);
}
-static const struct iris_fmt iris_vdec_formats_cap[] = {
- [IRIS_FMT_NV12] = {
- .pixfmt = V4L2_PIX_FMT_NV12,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
- },
- [IRIS_FMT_QC08C] = {
- .pixfmt = V4L2_PIX_FMT_QC08C,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
- },
+static const u32 iris_vdec_formats_cap[] = {
+ [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12,
+ [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C,
+ [IRIS_FMT_TP10] = V4L2_PIX_FMT_P010,
+ [IRIS_FMT_QC10C] = V4L2_PIX_FMT_QC10C,
};
-static const struct iris_fmt *
-find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
+static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type)
{
- const struct iris_fmt *fmt = NULL;
- unsigned int size = 0;
- unsigned int i;
+ unsigned int size, i;
+ const u32 *fmt;
+
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = inst->core->iris_platform_data->inst_iris_fmts;
@@ -88,25 +84,34 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
size = ARRAY_SIZE(iris_vdec_formats_cap);
break;
default:
- return NULL;
+ return false;
}
for (i = 0; i < size; i++) {
- if (fmt[i].pixfmt == pixfmt)
+ if (fmt[i] == pixfmt)
break;
}
- if (i == size || fmt[i].type != type)
- return NULL;
+ if (i == size)
+ return false;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ if (iris_fmt_is_8bit(pixfmt) &&
+ inst->fw_caps[BIT_DEPTH].value == BIT_DEPTH_10)
+ return false;
+
+ if (iris_fmt_is_10bit(pixfmt) &&
+ inst->fw_caps[BIT_DEPTH].value != BIT_DEPTH_10)
+ return false;
+ }
- return &fmt[i];
+ return true;
}
-static const struct iris_fmt *
-find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
+static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
{
- const struct iris_fmt *fmt = NULL;
- unsigned int size = 0;
+ unsigned int size;
+ const u32 *fmt;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
@@ -118,18 +123,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
size = ARRAY_SIZE(iris_vdec_formats_cap);
break;
default:
- return NULL;
+ return 0;
}
- if (index >= size || fmt[index].type != type)
- return NULL;
+ if (index >= size)
+ return 0;
- return &fmt[index];
+ return fmt[index];
}
int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
{
- const struct iris_fmt *fmt;
+ u32 fmt;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
@@ -137,14 +142,14 @@ int iris_vdec_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
if (!fmt)
return -EINVAL;
- f->pixelformat = fmt->pixfmt;
+ f->pixelformat = fmt;
f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_DYN_RESOLUTION;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = find_format_by_index(inst, f->index, f->type);
if (!fmt)
return -EINVAL;
- f->pixelformat = fmt->pixfmt;
+ f->pixelformat = fmt;
break;
default:
return -EINVAL;
@@ -157,15 +162,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;
+ bool supported;
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
- fmt = find_format(inst, pixmp->pixelformat, f->type);
+ supported = check_format(inst, pixmp->pixelformat, f->type);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (!fmt) {
+ if (!supported) {
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;
@@ -173,7 +178,7 @@ int iris_vdec_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (!fmt) {
+ if (!supported) {
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;
@@ -222,7 +227,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
+ if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type))
return -EINVAL;
fmt = inst->fmt_src;
@@ -252,6 +257,7 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
/* 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].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = iris_get_buffer_size(inst, BUF_OUTPUT);
inst->crop.left = 0;
@@ -260,16 +266,34 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
inst->crop.height = f->fmt.pix_mp.height;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
+ if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type))
return -EINVAL;
fmt = inst->fmt_dst;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
fmt->fmt.pix_mp.pixelformat = f->fmt.pix_mp.pixelformat;
- fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
- fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
fmt->fmt.pix_mp.num_planes = 1;
- fmt->fmt.pix_mp.plane_fmt[0].bytesperline = ALIGN(f->fmt.pix_mp.width, 128);
+ switch (f->fmt.pix_mp.pixelformat) {
+ case V4L2_PIX_FMT_P010:
+ fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline =
+ ALIGN(f->fmt.pix_mp.width * 2, 256);
+ break;
+ case V4L2_PIX_FMT_QC10C:
+ fmt->fmt.pix_mp.width = roundup(f->fmt.pix_mp.width, 192);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 16);
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline =
+ ALIGN(f->fmt.pix_mp.width * 4 / 3, 256);
+ break;
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_QC08C:
+ fmt->fmt.pix_mp.width = ALIGN(f->fmt.pix_mp.width, 128);
+ fmt->fmt.pix_mp.height = ALIGN(f->fmt.pix_mp.height, 32);
+ fmt->fmt.pix_mp.plane_fmt[0].bytesperline =
+ ALIGN(f->fmt.pix_mp.width, 128);
+ break;
+ }
fmt->fmt.pix_mp.plane_fmt[0].sizeimage = iris_get_buffer_size(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].min_count = iris_vpu_buf_count(inst, BUF_OUTPUT);
inst->buffers[BUF_OUTPUT].size = fmt->fmt.pix_mp.plane_fmt[0].sizeimage;
@@ -289,16 +313,13 @@ int iris_vdec_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
int iris_vdec_validate_format(struct iris_inst *inst, u32 pixelformat)
{
- const struct iris_fmt *fmt = NULL;
+ bool supported;
- fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
- if (!fmt) {
- fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- if (!fmt)
- return -EINVAL;
- }
+ supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+ if (!supported)
+ supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- return 0;
+ return supported ? 0 : -EINVAL;
}
int iris_vdec_subscribe_event(struct iris_inst *inst, const struct v4l2_event_subscription *sub)
@@ -363,12 +384,14 @@ int iris_vdec_streamon_input(struct iris_inst *inst)
if (ret)
return ret;
+ inst->frame_counter = 0;
+
return iris_process_streamon_input(inst);
}
int iris_vdec_streamon_output(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
int ret;
ret = hfi_ops->session_set_config_params(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
@@ -405,6 +428,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
{
struct iris_buffer *buf = to_iris_buffer(vbuf);
struct vb2_buffer *vb2 = &vbuf->vb2_buf;
+ u64 cur_buf_ns, delta_ns;
struct vb2_queue *q;
int ret;
@@ -421,6 +445,22 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
return 0;
}
+ if (buf->type == BUF_INPUT) {
+ cur_buf_ns = ktime_get_ns();
+
+ if (!inst->frame_counter)
+ inst->last_buf_ns = cur_buf_ns;
+
+ inst->frame_counter++;
+ delta_ns = cur_buf_ns - inst->last_buf_ns;
+
+ if (delta_ns >= NSEC_PER_SEC) {
+ inst->frame_rate = clamp_t(u32, inst->frame_counter, DEFAULT_FPS,
+ MAXIMUM_FPS);
+ inst->frame_counter = 0;
+ }
+ }
+
iris_scale_power(inst);
return iris_queue_buffer(inst, buf);
@@ -428,7 +468,7 @@ int iris_vdec_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
int iris_vdec_start_cmd(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state clear_sub_state = 0;
struct vb2_queue *dst_vq;
int ret;
@@ -491,7 +531,7 @@ int iris_vdec_start_cmd(struct iris_inst *inst)
int iris_vdec_stop_cmd(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
int ret;
ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
diff --git a/drivers/media/platform/qcom/iris/iris_venc.c b/drivers/media/platform/qcom/iris/iris_venc.c
index 4d886769d958..a945992f63aa 100644
--- a/drivers/media/platform/qcom/iris/iris_venc.c
+++ b/drivers/media/platform/qcom/iris/iris_venc.c
@@ -79,34 +79,21 @@ int iris_venc_inst_init(struct iris_inst *inst)
return iris_ctrls_init(inst);
}
-static const struct iris_fmt iris_venc_formats_cap[] = {
- [IRIS_FMT_H264] = {
- .pixfmt = V4L2_PIX_FMT_H264,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
- },
- [IRIS_FMT_HEVC] = {
- .pixfmt = V4L2_PIX_FMT_HEVC,
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
- },
+static const u32 iris_venc_formats_cap[] = {
+ [IRIS_FMT_H264] = V4L2_PIX_FMT_H264,
+ [IRIS_FMT_HEVC] = V4L2_PIX_FMT_HEVC,
};
-static const struct iris_fmt iris_venc_formats_out[] = {
- [IRIS_FMT_NV12] = {
- .pixfmt = V4L2_PIX_FMT_NV12,
- .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- },
- [IRIS_FMT_QC08C] = {
- .pixfmt = V4L2_PIX_FMT_QC08C,
- .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
- },
+static const u32 iris_venc_formats_out[] = {
+ [IRIS_FMT_NV12] = V4L2_PIX_FMT_NV12,
+ [IRIS_FMT_QC08C] = V4L2_PIX_FMT_QC08C,
};
-static const struct iris_fmt *
-find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
+static bool check_format(struct iris_inst *inst, u32 pixfmt, u32 type)
{
- const struct iris_fmt *fmt = NULL;
- unsigned int size = 0;
- unsigned int i;
+ unsigned int size, i;
+ const u32 *fmt;
+
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
fmt = iris_venc_formats_out;
@@ -117,25 +104,21 @@ find_format(struct iris_inst *inst, u32 pixfmt, u32 type)
size = ARRAY_SIZE(iris_venc_formats_cap);
break;
default:
- return NULL;
+ return false;
}
for (i = 0; i < size; i++) {
- if (fmt[i].pixfmt == pixfmt)
- break;
+ if (fmt[i] == pixfmt)
+ return true;
}
- if (i == size || fmt[i].type != type)
- return NULL;
-
- return &fmt[i];
+ return false;
}
-static const struct iris_fmt *
-find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
+static u32 find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
{
- const struct iris_fmt *fmt = NULL;
- unsigned int size = 0;
+ unsigned int size;
+ const u32 *fmt;
switch (type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
@@ -147,18 +130,18 @@ find_format_by_index(struct iris_inst *inst, u32 index, u32 type)
size = ARRAY_SIZE(iris_venc_formats_cap);
break;
default:
- return NULL;
+ return 0;
}
- if (index >= size || fmt[index].type != type)
- return NULL;
+ if (index >= size)
+ return 0;
- return &fmt[index];
+ return fmt[index];
}
int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
{
- const struct iris_fmt *fmt;
+ u32 fmt;
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
@@ -166,14 +149,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
if (!fmt)
return -EINVAL;
- f->pixelformat = fmt->pixfmt;
+ f->pixelformat = fmt;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
fmt = find_format_by_index(inst, f->index, f->type);
if (!fmt)
return -EINVAL;
- f->pixelformat = fmt->pixfmt;
+ f->pixelformat = fmt;
f->flags = V4L2_FMT_FLAG_COMPRESSED | V4L2_FMT_FLAG_ENC_CAP_FRAME_INTERVAL;
break;
default:
@@ -186,14 +169,14 @@ int iris_venc_enum_fmt(struct iris_inst *inst, struct v4l2_fmtdesc *f)
int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
- const struct iris_fmt *fmt;
struct v4l2_format *f_inst;
+ bool supported;
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
- fmt = find_format(inst, pixmp->pixelformat, f->type);
+ supported = check_format(inst, pixmp->pixelformat, f->type);
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
- if (!fmt) {
+ if (!supported) {
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;
@@ -201,7 +184,7 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
}
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
- if (!fmt) {
+ if (!supported) {
f_inst = inst->fmt_dst;
f->fmt.pix_mp.width = f_inst->fmt.pix_mp.width;
f->fmt.pix_mp.height = f_inst->fmt.pix_mp.height;
@@ -222,17 +205,17 @@ int iris_venc_try_fmt(struct iris_inst *inst, struct v4l2_format *f)
static int iris_venc_s_fmt_output(struct iris_inst *inst, struct v4l2_format *f)
{
- const struct iris_fmt *venc_fmt;
struct v4l2_format *fmt;
u32 codec_align;
+ bool supported;
iris_venc_try_fmt(inst, f);
- venc_fmt = find_format(inst, f->fmt.pix_mp.pixelformat, f->type);
- if (!venc_fmt)
+ supported = check_format(inst, f->fmt.pix_mp.pixelformat, f->type);
+ if (!supported)
return -EINVAL;
- codec_align = venc_fmt->pixfmt == V4L2_PIX_FMT_HEVC ? 32 : 16;
+ codec_align = (f->fmt.pix_mp.pixelformat == V4L2_PIX_FMT_HEVC) ? 32 : 16;
fmt = inst->fmt_dst;
fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
@@ -275,7 +258,7 @@ static int iris_venc_s_fmt_input(struct iris_inst *inst, struct v4l2_format *f)
iris_venc_try_fmt(inst, f);
- if (!(find_format(inst, f->fmt.pix_mp.pixelformat, f->type)))
+ if (!check_format(inst, f->fmt.pix_mp.pixelformat, f->type))
return -EINVAL;
fmt = inst->fmt_src;
@@ -344,16 +327,13 @@ int iris_venc_s_fmt(struct iris_inst *inst, struct v4l2_format *f)
int iris_venc_validate_format(struct iris_inst *inst, u32 pixelformat)
{
- const struct iris_fmt *fmt = NULL;
+ bool supported;
- fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
- if (!fmt) {
- fmt = find_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
- if (!fmt)
- return -EINVAL;
- }
+ supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+ if (!supported)
+ supported = check_format(inst, pixelformat, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
- return 0;
+ return supported ? 0 : -EINVAL;
}
int iris_venc_subscribe_event(struct iris_inst *inst,
@@ -575,7 +555,7 @@ int iris_venc_qbuf(struct iris_inst *inst, struct vb2_v4l2_buffer *vbuf)
int iris_venc_start_cmd(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
enum iris_inst_sub_state clear_sub_state = 0;
struct vb2_queue *dst_vq;
int ret;
@@ -617,7 +597,7 @@ int iris_venc_start_cmd(struct iris_inst *inst)
int iris_venc_stop_cmd(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
int ret;
ret = hfi_ops->session_drain(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
diff --git a/drivers/media/platform/qcom/iris/iris_vidc.c b/drivers/media/platform/qcom/iris/iris_vidc.c
index 5eb1786b0737..14d63dc76c9b 100644
--- a/drivers/media/platform/qcom/iris/iris_vidc.c
+++ b/drivers/media/platform/qcom/iris/iris_vidc.c
@@ -156,7 +156,7 @@ int iris_open(struct file *filp)
pm_runtime_put_sync(core->dev);
- inst = core->iris_platform_data->get_instance();
+ inst = core->hfi_sys_ops->sys_get_instance();
if (!inst)
return -ENOMEM;
@@ -224,7 +224,7 @@ fail_v4l2_fh_deinit:
static void iris_session_close(struct iris_inst *inst)
{
- const struct iris_hfi_command_ops *hfi_ops = inst->core->hfi_ops;
+ const struct iris_hfi_session_ops *hfi_ops = inst->hfi_session_ops;
bool wait_for_response = true;
int ret;
@@ -243,7 +243,7 @@ static void iris_session_close(struct iris_inst *inst)
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;
+ const struct iris_firmware_data *firmware_data = inst->core->iris_firmware_data;
struct iris_buffer *buf, *next;
struct iris_buffers *buffers;
const u32 *internal_buf_type;
@@ -251,11 +251,11 @@ static void iris_check_num_queued_internal_buffers(struct iris_inst *inst, u32 p
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;
+ internal_buf_type = firmware_data->dec_ip_int_buf_tbl;
+ internal_buffer_count = firmware_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;
+ internal_buf_type = firmware_data->dec_op_int_buf_tbl;
+ internal_buffer_count = firmware_data->dec_op_int_buf_tbl_size;
}
for (i = 0; i < internal_buffer_count; i++) {
diff --git a/drivers/media/platform/qcom/iris/iris_vpu2.c b/drivers/media/platform/qcom/iris/iris_vpu2.c
index 01ef40f38957..b8714dcbad10 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu2.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu2.c
@@ -18,7 +18,7 @@ static u64 iris_vpu2_calc_freq(struct iris_inst *inst, size_t data_size)
struct v4l2_format *inp_f = inst->fmt_src;
u32 mbs_per_second, mbpf, height, width;
unsigned long vpp_freq, vsp_freq;
- u32 fps = DEFAULT_FPS;
+ u32 fps = inst->frame_rate;
width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
index 9270422c1601..fb6f1016415e 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_buffer.c
@@ -735,6 +735,24 @@ static u32 iris_vpu_dec_comv_size(struct iris_inst *inst)
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_vpu3x_4x_dec_comv_size(struct iris_inst *inst)
+{
+ u32 num_comv = inst->buffers[BUF_OUTPUT].min_count;
+ struct v4l2_format *f = inst->fmt_src;
+ u32 height = f->fmt.pix_mp.height;
+ u32 width = f->fmt.pix_mp.width;
+
+ if (inst->fw_min_count)
+ num_comv = inst->fw_min_count;
+
+ 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);
else if (inst->codec == V4L2_PIX_FMT_AV1) {
if (inst->fw_caps[DRAP].value)
return 0;
@@ -934,6 +952,51 @@ static u32 iris_vpu_enc_bin_size(struct iris_inst *inst)
num_vpp_pipes, inst->hfi_rc_type);
}
+static inline u32 hfi_buffer_get_recon_count(struct iris_inst *inst)
+{
+ u32 layer_count = inst->hfi_layer_count;
+ u32 layer_type = inst->hfi_layer_type;
+ u32 bframe_count, ltr_count;
+ u32 num_ref = 1;
+
+ bframe_count = inst->fw_caps[B_FRAME].value;
+ ltr_count = inst->fw_caps[LTR_COUNT].value;
+
+ if (bframe_count)
+ num_ref = 2;
+
+ /* The shift operation here is rounding logic, similar to [(x+1)/2]. */
+ if (layer_type == HFI_HIER_P_HYBRID_LTR)
+ num_ref = (layer_count + 1) >> 1;
+
+ if (layer_type == HFI_HIER_P_SLIDING_WINDOW) {
+ if (inst->codec == V4L2_PIX_FMT_HEVC)
+ num_ref = (layer_count + 1) >> 1;
+ else if (inst->codec == V4L2_PIX_FMT_H264 && layer_count < 4)
+ num_ref = (layer_count - 1);
+ else
+ num_ref = layer_count;
+ }
+
+ if (ltr_count)
+ num_ref = num_ref + ltr_count;
+
+ /*
+ * The expression (1 << layers - 2) + 1 accounts for the number of reference
+ * frames in the Adaptive Hierarchical B-frame encoding case. In this scheme,
+ * the number of frames in a sub-GOP is related to (2^(number of layers) - 1),
+ * hence the use of the shift operation.
+ */
+ if (layer_type == HFI_HIER_B) {
+ if (inst->codec == V4L2_PIX_FMT_HEVC)
+ num_ref = layer_count;
+ else
+ num_ref = (1 << (layer_count - 2)) + 1;
+ }
+
+ return num_ref;
+}
+
static u32 iris_vpu_dec_partial_size(struct iris_inst *inst)
{
struct v4l2_format *f = inst->fmt_src;
@@ -968,17 +1031,14 @@ static u32 iris_vpu_enc_comv_size(struct iris_inst *inst)
{
u32 height = iris_vpu_enc_get_bitstream_height(inst);
u32 width = iris_vpu_enc_get_bitstream_width(inst);
- u32 num_recon = 1;
- u32 lcu_size = 16;
+ u32 num_recon = hfi_buffer_get_recon_count(inst);
+ u32 codec, lcu_size;
- if (inst->codec == V4L2_PIX_FMT_HEVC) {
- lcu_size = 32;
- return hfi_buffer_comv_enc(width, height, lcu_size,
- num_recon + 1, HFI_CODEC_ENCODE_HEVC);
- }
+ codec = (inst->codec == V4L2_PIX_FMT_HEVC) ?
+ HFI_CODEC_ENCODE_HEVC : HFI_CODEC_ENCODE_AVC;
+ lcu_size = (inst->codec == V4L2_PIX_FMT_HEVC) ? 32 : 16;
- return hfi_buffer_comv_enc(width, height, lcu_size,
- num_recon + 1, HFI_CODEC_ENCODE_AVC);
+ return hfi_buffer_comv_enc(width, height, lcu_size, num_recon + 1, codec);
}
static inline
@@ -1677,10 +1737,9 @@ static u32 iris_vpu_enc_scratch2_size(struct iris_inst *inst)
{
u32 frame_height = iris_vpu_enc_get_bitstream_height(inst);
u32 frame_width = iris_vpu_enc_get_bitstream_width(inst);
- u32 num_ref = 1;
+ u32 num_ref = hfi_buffer_get_recon_count(inst);
- return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref,
- false);
+ return hfi_buffer_scratch2_enc(frame_width, frame_height, num_ref, false);
}
static u32 iris_vpu_enc_vpss_size(struct iris_inst *inst)
@@ -2025,7 +2084,7 @@ u32 iris_vpu_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_type)
static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = {
{BUF_BIN, iris_vpu_dec_bin_size },
- {BUF_COMV, iris_vpu_dec_comv_size },
+ {BUF_COMV, iris_vpu3x_4x_dec_comv_size },
{BUF_NON_COMV, iris_vpu_dec_non_comv_size },
{BUF_LINE, iris_vpu_dec_line_size },
{BUF_PERSIST, iris_vpu_dec_persist_size },
@@ -2098,7 +2157,7 @@ u32 iris_vpu4x_buf_size(struct iris_inst *inst, enum iris_buffer_type buffer_typ
static const struct iris_vpu_buf_type_handle dec_internal_buf_type_handle[] = {
{BUF_BIN, iris_vpu_dec_bin_size },
- {BUF_COMV, iris_vpu_dec_comv_size },
+ {BUF_COMV, iris_vpu3x_4x_dec_comv_size },
{BUF_NON_COMV, iris_vpu_dec_non_comv_size },
{BUF_LINE, iris_vpu4x_dec_line_size },
{BUF_PERSIST, iris_vpu4x_dec_persist_size },
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.c b/drivers/media/platform/qcom/iris/iris_vpu_common.c
index 69e6126dc4d9..ab41da1f47c8 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.c
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.c
@@ -63,7 +63,7 @@ static void iris_vpu_setup_ucregion_memory_map(struct iris_core *core)
writel(QTBL_ENABLE, core->reg_base + QTBL_INFO);
if (core->sfr_daddr) {
- value = (u32)core->sfr_daddr + core->iris_platform_data->core_arch;
+ value = (u32)core->sfr_daddr + core->iris_firmware_data->core_arch;
writel(value, core->reg_base + SFR_ADDR);
}
@@ -149,7 +149,7 @@ int iris_vpu_prepare_pc(struct iris_core *core)
if (!wfi_status || !idle_status)
goto skip_power_off;
- ret = core->hfi_ops->sys_pc_prep(core);
+ ret = core->hfi_sys_ops->sys_pc_prep(core);
if (ret)
goto skip_power_off;
@@ -224,6 +224,7 @@ void iris_vpu_power_off_hw(struct iris_core *core)
{
dev_pm_genpd_set_hwmode(core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN], false);
iris_disable_power_domains(core, core->pmdomain_tbl->pd_devs[IRIS_HW_POWER_DOMAIN]);
+ iris_disable_unprepare_clock(core, IRIS_BSE_HW_CLK);
iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK);
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
}
@@ -292,8 +293,14 @@ int iris_vpu_power_on_hw(struct iris_core *core)
if (ret && ret != -ENOENT)
goto err_disable_hw_clock;
+ ret = iris_prepare_enable_clock(core, IRIS_BSE_HW_CLK);
+ if (ret && ret != -ENOENT)
+ goto err_disable_hw_ahb_clock;
+
return 0;
+err_disable_hw_ahb_clock:
+ iris_disable_unprepare_clock(core, IRIS_HW_AHB_CLK);
err_disable_hw_clock:
iris_disable_unprepare_clock(core, IRIS_HW_CLK);
err_disable_power:
@@ -420,7 +427,7 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz
u32 height, width, mbs_per_second, mbpf;
u64 fw_cycles, fw_vpp_cycles;
u64 vsp_cycles, vpp_cycles;
- u32 fps = DEFAULT_FPS;
+ u32 fps = inst->frame_rate;
width = max(inp_f->fmt.pix_mp.width, inst->crop.width);
height = max(inp_f->fmt.pix_mp.height, inst->crop.height);
@@ -439,6 +446,10 @@ u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_siz
if (inst->fw_caps[PIPE].value > 1)
vpp_cycles += div_u64(vpp_cycles * 59, 1000);
+ /* 1.05 is VPP FW overhead */
+ if (inst->fw_caps[STAGE].value == STAGE_2)
+ vpp_cycles += div_u64(vpp_cycles * 5, 100);
+
vsp_cycles = fps * data_size * 8;
vsp_cycles = div_u64(vsp_cycles, 2);
/* VSP FW overhead 1.05 */
@@ -472,7 +483,7 @@ int iris_vpu_power_on(struct iris_core *core)
iris_opp_set_rate(core->dev, freq);
- core->iris_platform_data->set_preset_registers(core);
+ iris_vpu_set_preset_registers(core);
iris_vpu_interrupt_init(core);
core->intr_status = 0;
@@ -489,3 +500,8 @@ err:
return ret;
}
+
+void iris_vpu_set_preset_registers(struct iris_core *core)
+{
+ writel(0x0, core->reg_base + 0xb0088);
+}
diff --git a/drivers/media/platform/qcom/iris/iris_vpu_common.h b/drivers/media/platform/qcom/iris/iris_vpu_common.h
index dee3b1349c5e..09799a375c14 100644
--- a/drivers/media/platform/qcom/iris/iris_vpu_common.h
+++ b/drivers/media/platform/qcom/iris/iris_vpu_common.h
@@ -42,4 +42,6 @@ int iris_vpu35_vpu4x_power_on_controller(struct iris_core *core);
void iris_vpu35_vpu4x_program_bootup_registers(struct iris_core *core);
u64 iris_vpu3x_vpu4x_calculate_frequency(struct iris_inst *inst, size_t data_size);
+void iris_vpu_set_preset_registers(struct iris_core *core);
+
#endif
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index 7e639760c41d..243e342b0ae7 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -18,6 +18,7 @@
#include <linux/types.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
+#include <linux/videodev2.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-mem2mem.h>
@@ -178,6 +179,8 @@ static void venus_sys_error_handler(struct work_struct *work)
static u32 to_v4l2_codec_type(u32 codec)
{
switch (codec) {
+ case HFI_VIDEO_CODEC_HEVC:
+ return V4L2_PIX_FMT_HEVC;
case HFI_VIDEO_CODEC_H264:
return V4L2_PIX_FMT_H264;
case HFI_VIDEO_CODEC_H263:
@@ -684,6 +687,47 @@ static const struct venus_resources msm8916_res = {
.vmem_addr = 0,
.dma_mask = 0xddc00000 - 1,
.fwname = "qcom/venus-1.8/venus.mbn",
+ .dec_codec_blacklist = HFI_VIDEO_CODEC_HEVC | HFI_VIDEO_CODEC_SPARK,
+ .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC,
+ .dec_nodename = "video-decoder",
+ .enc_nodename = "video-encoder",
+};
+
+static const struct freq_tbl msm8939_freq_table[] = {
+ { 489600, 266670000 }, /* 1080p @ 60 */
+ { 244800, 133330000 }, /* 1080p @ 30 */
+ { 220800, 133330000 }, /* 720p @ 60 */
+ { 108000, 133330000 }, /* 720p @ 30 */
+ { 72000, 133330000 }, /* VGA @ 60 */
+ { 36000, 133330000 }, /* VGA @ 30 */
+};
+
+static const struct reg_val msm8939_reg_preset[] = {
+ { 0xe0020, 0x0aaaaaaa },
+ { 0xe0024, 0x0aaaaaaa },
+ { 0x80124, 0x00000003 },
+};
+
+static const struct venus_resources msm8939_res = {
+ .freq_tbl = msm8939_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(msm8939_freq_table),
+ .reg_tbl = msm8939_reg_preset,
+ .reg_tbl_size = ARRAY_SIZE(msm8939_reg_preset),
+ .clks = { "core", "iface", "bus", },
+ .clks_num = 3,
+ .vcodec_clks = { "vcodec0_core", "vcodec1_core" },
+ .vcodec_clks_num = 2,
+ .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" },
+ .vcodec_pmdomains_num = 3,
+ .max_load = 489600, /* 1080p@30 + 1080p@30 */
+ .hfi_version = HFI_VERSION_1XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xddc00000 - 1,
+ .fwname = "qcom/venus-1.8/venus.mbn",
+ .dec_codec_blacklist = HFI_VIDEO_CODEC_SPARK,
+ .enc_codec_blacklist = HFI_VIDEO_CODEC_HEVC,
.dec_nodename = "video-decoder",
.enc_nodename = "video-encoder",
};
@@ -882,6 +926,7 @@ static const struct venus_resources sdm845_res_v2 = {
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" },
.vcodec_pmdomains_num = 3,
.opp_pmdomain = (const char *[]) { "cx" },
+ .opp_pmdomain_num = 1,
.vcodec_num = 2,
.max_load = 3110400, /* 4096x2160@90 */
.hfi_version = HFI_VERSION_4XX,
@@ -933,6 +978,7 @@ static const struct venus_resources sc7180_res = {
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "cx" },
+ .opp_pmdomain_num = 1,
.vcodec_num = 1,
.hfi_version = HFI_VERSION_4XX,
.vpu_version = VPU_VERSION_AR50,
@@ -949,6 +995,7 @@ static const struct venus_resources sc7180_res = {
.enc_nodename = "video-encoder",
};
+#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS))
static const struct freq_tbl sm8250_freq_table[] = {
{ 0, 444000000 },
{ 0, 366000000 },
@@ -991,7 +1038,8 @@ static const struct venus_resources sm8250_res = {
.vcodec_clks_num = 1,
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
- .opp_pmdomain = (const char *[]) { "mx" },
+ .opp_pmdomain = (const char *[]) { "mx", "mmcx" },
+ .opp_pmdomain_num = 2,
.vcodec_num = 1,
.max_load = 7833600,
.hfi_version = HFI_VERSION_6XX,
@@ -1053,6 +1101,7 @@ static const struct venus_resources sc7280_res = {
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "cx" },
+ .opp_pmdomain_num = 1,
.vcodec_num = 1,
.hfi_version = HFI_VERSION_6XX,
.vpu_version = VPU_VERSION_IRIS2_1,
@@ -1069,6 +1118,7 @@ static const struct venus_resources sc7280_res = {
.dec_nodename = "video-decoder",
.enc_nodename = "video-encoder",
};
+#endif
static const struct bw_tbl qcm2290_bw_table_dec[] = {
{ 352800, 597000, 0, 746000, 0 }, /* 1080p@30 + 720p@30 */
@@ -1100,6 +1150,7 @@ static const struct venus_resources qcm2290_res = {
.vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "cx" },
+ .opp_pmdomain_num = 1,
.vcodec_num = 1,
.hfi_version = HFI_VERSION_4XX,
.vpu_version = VPU_VERSION_AR50_LITE,
@@ -1121,15 +1172,18 @@ static const struct venus_resources qcm2290_res = {
static const struct of_device_id venus_dt_match[] = {
{ .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
+ { .compatible = "qcom,msm8939-venus", .data = &msm8939_res, },
{ .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
{ .compatible = "qcom,msm8998-venus", .data = &msm8998_res, },
{ .compatible = "qcom,qcm2290-venus", .data = &qcm2290_res, },
{ .compatible = "qcom,sc7180-venus", .data = &sc7180_res, },
- { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, },
{ .compatible = "qcom,sdm660-venus", .data = &sdm660_res, },
{ .compatible = "qcom,sdm845-venus", .data = &sdm845_res, },
{ .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, },
+#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS))
+ { .compatible = "qcom,sc7280-venus", .data = &sc7280_res, },
{ .compatible = "qcom,sm8250-venus", .data = &sm8250_res, },
+#endif
{ }
};
MODULE_DEVICE_TABLE(of, venus_dt_match);
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 7506f5d0f609..46705a666776 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -54,8 +54,10 @@ enum vpu_version {
VPU_VERSION_AR50,
VPU_VERSION_AR50_LITE,
VPU_VERSION_IRIS1,
+#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS))
VPU_VERSION_IRIS2,
VPU_VERSION_IRIS2_1,
+#endif
};
struct firmware_version {
@@ -77,13 +79,17 @@ struct venus_resources {
const struct hfi_ubwc_config *ubwc_conf;
const char * const clks[VIDC_CLKS_NUM_MAX];
unsigned int clks_num;
+ const char * const vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX];
const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
unsigned int vcodec_clks_num;
const char **vcodec_pmdomains;
unsigned int vcodec_pmdomains_num;
const char **opp_pmdomain;
+ unsigned int opp_pmdomain_num;
unsigned int vcodec_num;
+ const u32 dec_codec_blacklist;
+ const u32 enc_codec_blacklist;
const char * const resets[VIDC_RESETS_NUM_MAX];
unsigned int resets_num;
enum hfi_version hfi_version;
@@ -140,6 +146,7 @@ struct venus_format {
* @aon_base: AON base address
* @irq: Venus irq
* @clks: an array of struct clk pointers
+ * @vcodec_clks: an array of vcodec struct clk pointers
* @vcodec0_clks: an array of vcodec0 struct clk pointers
* @vcodec1_clks: an array of vcodec1 struct clk pointers
* @video_path: an interconnect handle to video to/from memory path
@@ -194,6 +201,7 @@ struct venus_core {
void __iomem *aon_base;
int irq;
struct clk *clks[VIDC_CLKS_NUM_MAX];
+ struct clk *vcodec_clks[VIDC_VCODEC_CLKS_NUM_MAX];
struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
struct clk *vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
struct icc_path *video_path;
@@ -525,13 +533,22 @@ struct venus_inst {
#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
#define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX)
#define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX)
+#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS))
#define IS_V6(core) ((core)->res->hfi_version == HFI_VERSION_6XX)
+#else
+#define IS_V6(core) (((void)(core), 0))
+#endif
#define IS_AR50(core) ((core)->res->vpu_version == VPU_VERSION_AR50)
#define IS_AR50_LITE(core) ((core)->res->vpu_version == VPU_VERSION_AR50_LITE)
#define IS_IRIS1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS1)
+#if (!IS_ENABLED(CONFIG_VIDEO_QCOM_IRIS))
#define IS_IRIS2(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2)
#define IS_IRIS2_1(core) ((core)->res->vpu_version == VPU_VERSION_IRIS2_1)
+#else
+#define IS_IRIS2(core) (((void)(core), 0))
+#define IS_IRIS2_1(core) (((void)(core), 0))
+#endif
static inline bool is_lite(struct venus_core *core)
{
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 747c388fe25f..59eee3dd9e06 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -954,8 +954,8 @@ static u32 get_framesize_raw_nv12(u32 width, u32 height)
uv_sclines = ALIGN(((height + 1) >> 1), 16);
y_plane = y_stride * y_sclines;
- uv_plane = uv_stride * uv_sclines + SZ_4K;
- size = y_plane + uv_plane + SZ_8K;
+ uv_plane = uv_stride * uv_sclines;
+ size = y_plane + uv_plane;
return ALIGN(size, SZ_4K);
}
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
index 92765f9c8873..b1657443f23f 100644
--- a/drivers/media/platform/qcom/venus/hfi_parser.c
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -206,11 +206,11 @@ static int parse_codecs(struct venus_core *core, void *data)
core->dec_codecs = codecs->dec_codecs;
core->enc_codecs = codecs->enc_codecs;
- if (IS_V1(core)) {
- core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC;
- core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK;
- core->enc_codecs &= ~HFI_VIDEO_CODEC_HEVC;
- }
+ if (core->res->dec_codec_blacklist)
+ core->dec_codecs &= ~core->res->dec_codec_blacklist;
+
+ if (core->res->enc_codec_blacklist)
+ core->enc_codecs &= ~core->res->enc_codec_blacklist;
return sizeof(*codecs);
}
@@ -268,7 +268,6 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst)
const struct hfi_plat_caps *caps = NULL;
u32 enc_codecs, dec_codecs, count = 0;
unsigned int entries;
- int ret;
plat = hfi_platform_get(core->res->hfi_version);
if (!plat)
@@ -277,9 +276,8 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst)
if (inst)
return 0;
- ret = hfi_platform_get_codecs(core, &enc_codecs, &dec_codecs, &count);
- if (ret)
- return ret;
+ if (plat->codecs)
+ plat->codecs(core, &enc_codecs, &dec_codecs, &count);
if (plat->capabilities)
caps = plat->capabilities(core, &entries);
diff --git a/drivers/media/platform/qcom/venus/hfi_platform.c b/drivers/media/platform/qcom/venus/hfi_platform.c
index cde7f93045ac..f19572ab1d16 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform.c
@@ -2,9 +2,7 @@
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
*/
-#include <linux/of.h>
#include "hfi_platform.h"
-#include "core.h"
const struct hfi_platform *hfi_platform_get(enum hfi_version version)
{
@@ -73,25 +71,3 @@ hfi_platform_get_codec_lp_freq(struct venus_core *core,
return freq;
}
-
-int
-hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs,
- u32 *dec_codecs, u32 *count)
-{
- const struct hfi_platform *plat;
-
- plat = hfi_platform_get(core->res->hfi_version);
- if (!plat)
- return -EINVAL;
-
- if (plat->codecs)
- plat->codecs(core, enc_codecs, dec_codecs, count);
-
- if (IS_IRIS2_1(core)) {
- *enc_codecs &= ~HFI_VIDEO_CODEC_VP8;
- *dec_codecs &= ~HFI_VIDEO_CODEC_VP8;
- }
-
- return 0;
-}
-
diff --git a/drivers/media/platform/qcom/venus/hfi_platform.h b/drivers/media/platform/qcom/venus/hfi_platform.h
index 5e4f8013a6b1..a0b6d19f3e1a 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform.h
+++ b/drivers/media/platform/qcom/venus/hfi_platform.h
@@ -74,6 +74,4 @@ unsigned long hfi_platform_get_codec_vsp_freq(struct venus_core *core,
unsigned long hfi_platform_get_codec_lp_freq(struct venus_core *core,
enum hfi_version version,
u32 codec, u32 session_type);
-int hfi_platform_get_codecs(struct venus_core *core, u32 *enc_codecs,
- u32 *dec_codecs, u32 *count);
#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v4.c b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
index cda888b56b5d..e0b3652bb440 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform_v4.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v4.c
@@ -136,8 +136,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_H264,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_HEVC,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
.caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
- .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.num_caps = 24,
.pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
.pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
@@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_VP8,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1},
.caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
.caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
- .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
.num_caps = 23,
diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
index d8568c08cc36..fb8d10ab3404 100644
--- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c
+++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c
@@ -173,8 +173,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_HEVC,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -195,8 +195,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
.caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[21] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
- .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[22] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[23] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.num_caps = 24,
.pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
.pl[1] = {HFI_HEVC_PROFILE_MAIN10, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0},
@@ -210,8 +210,8 @@ static const struct hfi_plat_caps caps[] = {
.codec = HFI_VIDEO_CODEC_VP8,
.domain = VIDC_SESSION_TYPE_ENC,
.cap_bufs_mode_dynamic = true,
- .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16},
- .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16},
+ .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1},
+ .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1},
.caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1},
.caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1},
.caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1},
@@ -229,8 +229,8 @@ static const struct hfi_plat_caps caps[] = {
.caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1},
.caps[17] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1},
.caps[18] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1},
- .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 16},
- .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 16},
+ .caps[19] = {HFI_CAPABILITY_BLUR_WIDTH, 96, 4096, 1},
+ .caps[20] = {HFI_CAPABILITY_BLUR_HEIGHT, 96, 4096, 1},
.caps[21] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1},
.caps[22] = {HFI_CAPABILITY_ROTATION, 1, 4, 90},
.num_caps = 23,
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c
index f0269524ac70..be1cbd5cfe84 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/platform/qcom/venus/pm_helpers.c
@@ -89,12 +89,23 @@ static void core_clks_disable(struct venus_core *core)
static int core_clks_set_rate(struct venus_core *core, unsigned long freq)
{
- int ret;
+ const struct venus_resources *res = core->res;
+ int ret, i;
ret = dev_pm_opp_set_rate(core->dev, freq);
if (ret)
return ret;
+ if (!res->vcodec_clks_num)
+ goto set_rates;
+
+ for (i = 0; i < res->vcodec_clks_num; i++) {
+ ret = clk_set_rate(core->vcodec_clks[i], freq);
+ if (ret)
+ return ret;
+ }
+
+set_rates:
ret = clk_set_rate(core->vcodec0_clks[0], freq);
if (ret)
return ret;
@@ -297,10 +308,33 @@ exit:
return ret;
}
+static int vcodec_domains_get_v1(struct venus_core *core)
+{
+ struct device *dev = core->dev;
+ const struct venus_resources *res = core->res;
+ const struct dev_pm_domain_attach_data vcodec_data = {
+ .pd_names = res->vcodec_pmdomains,
+ .num_pd_names = res->vcodec_pmdomains_num,
+ .pd_flags = PD_FLAG_NO_DEV_LINK,
+ };
+
+ if (!res->vcodec_pmdomains)
+ return 0;
+
+ return devm_pm_domain_attach_list(dev, &vcodec_data,
+ &core->pmdomains);
+}
+
static int core_get_v1(struct venus_core *core)
{
+ const struct venus_resources *res = core->res;
+ struct device *dev = core->dev;
int ret;
+ ret = vcodec_domains_get_v1(core);
+ if (ret < 0)
+ return ret;
+
ret = core_clks_get(core);
if (ret)
return ret;
@@ -309,9 +343,79 @@ static int core_get_v1(struct venus_core *core)
if (ret)
return ret;
+ if (!res->vcodec_pmdomains)
+ return 0;
+
+ ret = vcodec_clks_get(core, dev, core->vcodec_clks,
+ res->vcodec_clks);
+ if (ret)
+ return ret;
+
return 0;
}
+static int vcodec_domains_enable(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ struct device *pd_dev;
+ int i = 0, ret;
+
+ if (!res->vcodec_pmdomains)
+ return 0;
+
+ for (; i < res->vcodec_pmdomains_num; i++) {
+ pd_dev = core->pmdomains->pd_devs[i];
+ ret = pm_runtime_resume_and_get(pd_dev);
+ if (ret)
+ goto err;
+ }
+
+ return 0;
+err:
+ while (i--) {
+ pd_dev = core->pmdomains->pd_devs[i];
+ pm_runtime_put_sync(pd_dev);
+ }
+ return ret;
+}
+
+static void vcodec_domains_disable(struct venus_core *core)
+{
+ const struct venus_resources *res = core->res;
+ struct device *pd_dev;
+ int i = res->vcodec_pmdomains_num;
+
+ if (!res->vcodec_pmdomains)
+ return;
+
+ while (i--) {
+ pd_dev = core->pmdomains->pd_devs[i];
+ pm_runtime_put_sync(pd_dev);
+ }
+}
+
+static int vcodec_domains_set_hw(struct venus_core *core, bool is_hw)
+{
+ const struct venus_resources *res = core->res;
+ struct device *pd_dev;
+ int i = 0, ret;
+
+ for (; i < res->vcodec_pmdomains_num; i++) {
+ pd_dev = core->pmdomains->pd_devs[i];
+ ret = dev_pm_genpd_set_hwmode(pd_dev, is_hw);
+ if (ret && ret != -EOPNOTSUPP)
+ goto err;
+ }
+
+ return 0;
+err:
+ while (i--) {
+ pd_dev = core->pmdomains->pd_devs[i];
+ dev_pm_genpd_set_hwmode(pd_dev, !is_hw);
+ }
+ return ret;
+}
+
static void core_put_v1(struct venus_core *core)
{
}
@@ -320,11 +424,43 @@ static int core_power_v1(struct venus_core *core, int on)
{
int ret = 0;
- if (on == POWER_ON)
+ if (on == POWER_ON) {
+ ret = vcodec_domains_enable(core);
+ if (ret)
+ return ret;
+
ret = core_clks_enable(core);
- else
+ if (ret)
+ goto fail_pmdomains;
+
+ if (!core->res->vcodec_pmdomains)
+ return 0;
+
+ ret = vcodec_clks_enable(core, core->vcodec_clks);
+ if (ret)
+ goto fail_core_clks;
+
+ ret = vcodec_domains_set_hw(core, true);
+ if (ret)
+ goto fail_vcodec_clks;
+
+ } else {
+ if (core->res->vcodec_pmdomains) {
+ vcodec_domains_set_hw(core, false);
+ vcodec_clks_disable(core, core->vcodec_clks);
+ }
core_clks_disable(core);
+ vcodec_domains_disable(core);
+ }
+ return 0;
+
+fail_vcodec_clks:
+ vcodec_clks_disable(core, core->vcodec_clks);
+fail_core_clks:
+ core_clks_disable(core);
+fail_pmdomains:
+ vcodec_domains_disable(core);
return ret;
}
@@ -875,7 +1011,7 @@ static int venc_power_v4(struct device *dev, int on)
return ret;
}
-static int vcodec_domains_get(struct venus_core *core)
+static int vcodec_domains_get_v4(struct venus_core *core)
{
int ret;
struct device *dev = core->dev;
@@ -887,7 +1023,7 @@ static int vcodec_domains_get(struct venus_core *core)
};
struct dev_pm_domain_attach_data opp_pd_data = {
.pd_names = res->opp_pmdomain,
- .num_pd_names = 1,
+ .num_pd_names = res->opp_pmdomain_num,
.pd_flags = PD_FLAG_DEV_LINK_ON | PD_FLAG_REQUIRED_OPP,
};
@@ -904,6 +1040,12 @@ skip_pmdomains:
/* Attach the power domain for setting performance state */
ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain);
+ /* backwards compatibility for incomplete ABI SM8250 */
+ if (ret == -ENODEV &&
+ of_device_is_compatible(dev->of_node, "qcom,sm8250-venus")) {
+ opp_pd_data.num_pd_names--;
+ ret = devm_pm_domain_attach_list(dev, &opp_pd_data, &core->opp_pmdomain);
+ }
if (ret < 0)
return ret;
@@ -993,7 +1135,7 @@ static int core_get_v4(struct venus_core *core)
if (ret)
return ret;
- ret = vcodec_domains_get(core);
+ ret = vcodec_domains_get_v4(core);
if (ret)
return ret;
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index f9af9177e02f..73cda0e2d45a 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -1494,7 +1494,6 @@ int rvin_dma_register(struct rvin_dev *vin, int irq)
q->ops = &rvin_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->min_queued_buffers = 4;
q->dev = vin->dev;
ret = vb2_queue_init(q);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
index a5a57369ef0e..23cb50ee8e57 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru-regs.h
@@ -14,12 +14,11 @@
#define CRUnIE_EFE BIT(17)
-#define CRUnIE2_FSxE(x) BIT(((x) * 3))
#define CRUnIE2_FExE(x) BIT(((x) * 3) + 1)
#define CRUnINTS_SFS BIT(16)
-#define CRUnINTS2_FSxS(x) BIT(((x) * 3))
+#define CRUnINTS2_FExS(x) BIT(((x) * 3) + 1)
#define CRUnRST_VRESETN BIT(0)
@@ -60,6 +59,7 @@
#define ICnMC_CSCTHR BIT(5)
#define ICnMC_INF(x) ((x) << 16)
#define ICnMC_VCSEL(x) ((x) << 22)
+#define ICnMC_VCSEL_MASK GENMASK(23, 22)
#define ICnMC_INF_MASK GENMASK(21, 16)
#define ICnMS_IA BIT(2)
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
index 3a200db15730..5bf334e173d2 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
@@ -9,7 +9,9 @@
#define __RZG2L_CRU__
#include <linux/irqreturn.h>
+#include <linux/mutex.h>
#include <linux/reset.h>
+#include <linux/spinlock.h>
#include <media/v4l2-async.h>
#include <media/v4l2-dev.h>
@@ -36,20 +38,6 @@ enum rzg2l_csi2_pads {
struct rzg2l_cru_dev;
-/**
- * enum rzg2l_cru_dma_state - DMA states
- * @RZG2L_CRU_DMA_STOPPED: No operation in progress
- * @RZG2L_CRU_DMA_STARTING: Capture starting up
- * @RZG2L_CRU_DMA_RUNNING: Operation in progress have buffers
- * @RZG2L_CRU_DMA_STOPPING: Stopping operation
- */
-enum rzg2l_cru_dma_state {
- RZG2L_CRU_DMA_STOPPED = 0,
- RZG2L_CRU_DMA_STARTING,
- RZG2L_CRU_DMA_RUNNING,
- RZG2L_CRU_DMA_STOPPING,
-};
-
struct rzg2l_cru_csi {
struct v4l2_async_connection *asd;
struct v4l2_subdev *subdev;
@@ -109,7 +97,6 @@ struct rzg2l_cru_info {
* @v4l2_dev: V4L2 device
* @num_buf: Holds the current number of buffers enabled
* @svc_channel: SVC0/1/2/3 to use for RZ/G3E
- * @buf_addr: Memory addresses where current video data is written.
* @notifier: V4L2 asynchronous subdevs notifier
*
* @ip: Image processing subdev info
@@ -118,6 +105,11 @@ struct rzg2l_cru_info {
* @mdev_lock: protects the count, notifier and csi members
* @pad: media pad for the video device entity
*
+ * @hw_lock: protects the @active_slot counter, hardware programming
+ * of slot addresses and the @buf_addr[] list
+ * @buf_addr: Memory addresses where current video data is written
+ * @active_slot: The slot in use
+ *
* @lock: protects @queue
* @queue: vb2 buffers queue
* @scratch: cpu address for scratch buffer
@@ -147,8 +139,6 @@ struct rzg2l_cru_dev {
u8 num_buf;
u8 svc_channel;
- dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT];
-
struct v4l2_async_notifier notifier;
struct rzg2l_cru_ip ip;
@@ -157,6 +147,10 @@ struct rzg2l_cru_dev {
struct mutex mdev_lock;
struct media_pad pad;
+ spinlock_t hw_lock;
+ dma_addr_t buf_addr[RZG2L_CRU_HW_BUFFER_DEFAULT];
+ unsigned int active_slot;
+
struct mutex lock;
struct vb2_queue queue;
void *scratch;
@@ -166,7 +160,6 @@ struct rzg2l_cru_dev {
struct vb2_v4l2_buffer *queue_buf[RZG2L_CRU_HW_BUFFER_MAX];
struct list_head buf_list;
unsigned int sequence;
- enum rzg2l_cru_dma_state state;
struct v4l2_pix_format format;
};
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index 162e2ace6931..5185a547461d 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -42,6 +42,24 @@ struct rzg2l_cru_buffer {
#define to_buf_list(vb2_buffer) \
(&container_of(vb2_buffer, struct rzg2l_cru_buffer, vb)->list)
+/*
+ * The CRU hardware cycles over its slots when transferring frames. All drivers
+ * structure that contains programming data for the slots, such as the memory
+ * destination addresses have to be iterated as they were circular buffers.
+ *
+ * Provide here utilities to iterate over slots and the associated data.
+ */
+static inline unsigned int rzg2l_cru_slot_next(struct rzg2l_cru_dev *cru,
+ unsigned int slot)
+{
+ return (slot + 1) % cru->num_buf;
+}
+
+/* Start cycling on cru slots from the one after 'start'. */
+#define for_each_cru_slot_from(cru, slot, start) \
+ for ((slot) = rzg2l_cru_slot_next((cru), (start)); \
+ (slot) != (start); (slot) = rzg2l_cru_slot_next((cru), (slot)))
+
/* -----------------------------------------------------------------------------
* DMA operations
*/
@@ -105,28 +123,36 @@ __rzg2l_cru_read_constant(struct rzg2l_cru_dev *cru, u32 offset)
__rzg2l_cru_read_constant(cru, offset) : \
__rzg2l_cru_read(cru, offset))
-/* Need to hold qlock before calling */
-static void return_unused_buffers(struct rzg2l_cru_dev *cru,
- enum vb2_buffer_state state)
+static void rzg2l_cru_return_buffers(struct rzg2l_cru_dev *cru,
+ enum vb2_buffer_state state)
{
struct rzg2l_cru_buffer *buf, *node;
- unsigned long flags;
- unsigned int i;
- spin_lock_irqsave(&cru->qlock, flags);
- for (i = 0; i < cru->num_buf; i++) {
- if (cru->queue_buf[i]) {
- vb2_buffer_done(&cru->queue_buf[i]->vb2_buf,
- state);
- cru->queue_buf[i] = NULL;
+ scoped_guard(spinlock_irq, &cru->hw_lock) {
+ /* Return the buffer in progress first, if not completed yet. */
+ unsigned int slot = cru->active_slot;
+
+ if (cru->queue_buf[slot]) {
+ vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state);
+ cru->queue_buf[slot] = NULL;
+ }
+
+ /* Return all the pending buffers after the active one. */
+ for_each_cru_slot_from(cru, slot, cru->active_slot) {
+ if (!cru->queue_buf[slot])
+ continue;
+
+ vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf, state);
+ cru->queue_buf[slot] = NULL;
}
}
+ guard(spinlock_irq)(&cru->qlock);
+
list_for_each_entry_safe(buf, node, &cru->buf_list, list) {
vb2_buffer_done(&buf->vb.vb2_buf, state);
list_del(&buf->list);
}
- spin_unlock_irqrestore(&cru->qlock, flags);
}
static int rzg2l_cru_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
@@ -165,13 +191,9 @@ static void rzg2l_cru_buffer_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct rzg2l_cru_dev *cru = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long flags;
-
- spin_lock_irqsave(&cru->qlock, flags);
+ guard(spinlock_irq)(&cru->qlock);
list_add_tail(to_buf_list(vbuf), &cru->buf_list);
-
- spin_unlock_irqrestore(&cru->qlock, flags);
}
static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru,
@@ -192,45 +214,52 @@ static void rzg2l_cru_set_slot_addr(struct rzg2l_cru_dev *cru,
}
/*
- * Moves a buffer from the queue to the HW slot. If no buffer is
- * available use the scratch buffer. The scratch buffer is never
- * returned to userspace, its only function is to enable the capture
- * loop to keep running.
+ * Move as many buffers as possible from the queue to HW slots If no buffer is
+ * available use the scratch buffer. The scratch buffer is never returned to
+ * userspace, its only function is to enable the capture loop to keep running.
+ *
+ * @cru: the CRU device
+ * @slot: the slot that has just completed
*/
static void rzg2l_cru_fill_hw_slot(struct rzg2l_cru_dev *cru, int slot)
{
- struct vb2_v4l2_buffer *vbuf;
struct rzg2l_cru_buffer *buf;
+ struct vb2_v4l2_buffer *vbuf;
+ unsigned int next_slot;
dma_addr_t phys_addr;
- /* A already populated slot shall never be overwritten. */
- if (WARN_ON(cru->queue_buf[slot]))
- return;
+ lockdep_assert_held(&cru->hw_lock);
- dev_dbg(cru->dev, "Filling HW slot: %d\n", slot);
+ /* Find the next slot which hasn't a valid address programmed. */
+ for_each_cru_slot_from(cru, next_slot, slot) {
+ if (cru->queue_buf[next_slot])
+ continue;
- if (list_empty(&cru->buf_list)) {
- cru->queue_buf[slot] = NULL;
- phys_addr = cru->scratch_phys;
- } else {
- /* Keep track of buffer we give to HW */
- buf = list_entry(cru->buf_list.next,
- struct rzg2l_cru_buffer, list);
- vbuf = &buf->vb;
- list_del_init(to_buf_list(vbuf));
- cru->queue_buf[slot] = vbuf;
+ scoped_guard(spinlock_irqsave, &cru->qlock) {
+ buf = list_first_entry_or_null(&cru->buf_list,
+ struct rzg2l_cru_buffer, list);
+ if (buf)
+ list_del_init(&buf->list);
+ }
- /* Setup DMA */
+ if (!buf) {
+ /* Direct frames to the scratch buffer. */
+ phys_addr = cru->scratch_phys;
+ cru->queue_buf[next_slot] = NULL;
+ rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr);
+ return;
+ }
+
+ vbuf = &buf->vb;
+ cru->queue_buf[next_slot] = vbuf;
phys_addr = vb2_dma_contig_plane_dma_addr(&vbuf->vb2_buf, 0);
+ rzg2l_cru_set_slot_addr(cru, next_slot, phys_addr);
}
-
- rzg2l_cru_set_slot_addr(cru, slot, phys_addr);
}
static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru)
{
const struct rzg2l_cru_info *info = cru->info;
- unsigned int slot;
u32 amnaxiattr;
/*
@@ -239,8 +268,14 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru)
*/
rzg2l_cru_write(cru, AMnMBVALID, AMnMBVALID_MBVALID(cru->num_buf - 1));
- for (slot = 0; slot < cru->num_buf; slot++)
- rzg2l_cru_fill_hw_slot(cru, slot);
+ /*
+ * Program slot#0 with the first available buffer, if any. Pass to the
+ * function 'num_buf - 1' as rzg2l_cru_fill_hw_slot() calculates which
+ * is the next slot to program.
+ */
+ scoped_guard(spinlock_irq, &cru->hw_lock) {
+ rzg2l_cru_fill_hw_slot(cru, cru->num_buf - 1);
+ }
if (info->has_stride) {
u32 stride = cru->format.bytesperline;
@@ -262,19 +297,24 @@ static void rzg2l_cru_csi2_setup(struct rzg2l_cru_dev *cru,
u8 csi_vc)
{
const struct rzg2l_cru_info *info = cru->info;
- u32 icnmc = ICnMC_INF(ip_fmt->datatype);
+ u32 icnmc = rzg2l_cru_read(cru, info->image_conv) & ~(ICnMC_INF_MASK |
+ ICnMC_VCSEL_MASK);
+ icnmc |= ICnMC_INF(ip_fmt->datatype);
+ /*
+ * VC filtering goes through SVC register on G3E/V2H.
+ *
+ * FIXME: virtual channel filtering is likely broken and only VC=0
+ * works.
+ */
if (cru->info->regs[ICnSVC]) {
rzg2l_cru_write(cru, ICnSVCNUM, csi_vc);
rzg2l_cru_write(cru, ICnSVC, ICnSVC_SVC0(0) | ICnSVC_SVC1(1) |
ICnSVC_SVC2(2) | ICnSVC_SVC3(3));
+ } else {
+ icnmc |= ICnMC_VCSEL(csi_vc);
}
- icnmc |= rzg2l_cru_read(cru, info->image_conv) & ~ICnMC_INF_MASK;
-
- /* Set virtual channel CSI2 */
- icnmc |= ICnMC_VCSEL(csi_vc);
-
rzg2l_cru_write(cru, info->image_conv, icnmc);
}
@@ -340,30 +380,24 @@ bool rzg2l_fifo_empty(struct rzg2l_cru_dev *cru)
void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
{
unsigned int retries = 0;
- unsigned long flags;
u32 icnms;
- spin_lock_irqsave(&cru->qlock, flags);
-
- /* Disable and clear the interrupt */
- cru->info->disable_interrupts(cru);
+ scoped_guard(spinlock_irq, &cru->hw_lock) {
+ /* Disable and clear the interrupt */
+ cru->info->disable_interrupts(cru);
+ }
/* Stop the operation of image conversion */
rzg2l_cru_write(cru, ICnEN, 0);
/* Wait for streaming to stop */
- while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES) {
- spin_unlock_irqrestore(&cru->qlock, flags);
+ while ((rzg2l_cru_read(cru, ICnMS) & ICnMS_IA) && retries++ < RZG2L_RETRIES)
msleep(RZG2L_TIMEOUT_MS);
- spin_lock_irqsave(&cru->qlock, flags);
- }
icnms = rzg2l_cru_read(cru, ICnMS) & ICnMS_IA;
if (icnms)
dev_err(cru->dev, "Failed stop HW, something is seriously broken\n");
- cru->state = RZG2L_CRU_DMA_STOPPED;
-
/* Wait until the FIFO becomes empty */
for (retries = 5; retries > 0; retries--) {
if (cru->info->fifo_empty(cru))
@@ -400,8 +434,6 @@ void rzg2l_cru_stop_image_processing(struct rzg2l_cru_dev *cru)
/* Resets the image processing module */
rzg2l_cru_write(cru, CRUnRST, 0);
-
- spin_unlock_irqrestore(&cru->qlock, flags);
}
static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru)
@@ -435,7 +467,6 @@ static int rzg2l_cru_get_virtual_channel(struct rzg2l_cru_dev *cru)
void rzg3e_cru_enable_interrupts(struct rzg2l_cru_dev *cru)
{
- rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FSxE(cru->svc_channel));
rzg2l_cru_write(cru, CRUnIE2, CRUnIE2_FExE(cru->svc_channel));
}
@@ -461,7 +492,6 @@ void rzg2l_cru_disable_interrupts(struct rzg2l_cru_dev *cru)
int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
{
struct v4l2_mbus_framefmt *fmt = rzg2l_cru_ip_get_src_fmt(cru);
- unsigned long flags;
u8 csi_vc;
int ret;
@@ -471,8 +501,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
csi_vc = ret;
cru->svc_channel = csi_vc;
- spin_lock_irqsave(&cru->qlock, flags);
-
/* Select a video input */
rzg2l_cru_write(cru, CRUnCTRL, CRUnCTRL_VINSEL(0));
@@ -488,7 +516,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
/* Initialize image convert */
ret = rzg2l_cru_initialize_image_conv(cru, fmt, csi_vc);
if (ret) {
- spin_unlock_irqrestore(&cru->qlock, flags);
return ret;
}
@@ -498,8 +525,6 @@ int rzg2l_cru_start_image_processing(struct rzg2l_cru_dev *cru)
/* Enable image processing reception */
rzg2l_cru_write(cru, ICnEN, ICnEN_ICEN);
- spin_unlock_irqrestore(&cru->qlock, flags);
-
return 0;
}
@@ -560,69 +585,36 @@ pipe_line_stop:
static void rzg2l_cru_stop_streaming(struct rzg2l_cru_dev *cru)
{
- cru->state = RZG2L_CRU_DMA_STOPPING;
-
rzg2l_cru_set_stream(cru, 0);
}
irqreturn_t rzg2l_cru_irq(int irq, void *data)
{
struct rzg2l_cru_dev *cru = data;
- unsigned int handled = 0;
- unsigned long flags;
u32 irq_status;
u32 amnmbs;
int slot;
- spin_lock_irqsave(&cru->qlock, flags);
-
irq_status = rzg2l_cru_read(cru, CRUnINTS);
if (!irq_status)
- goto done;
-
- handled = 1;
+ return IRQ_NONE;
rzg2l_cru_write(cru, CRUnINTS, rzg2l_cru_read(cru, CRUnINTS));
- /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */
- if (cru->state == RZG2L_CRU_DMA_STOPPED) {
- dev_dbg(cru->dev, "IRQ while state stopped\n");
- goto done;
- }
-
- /* Increase stop retries if capture status is 'RZG2L_CRU_DMA_STOPPING' */
- if (cru->state == RZG2L_CRU_DMA_STOPPING) {
- if (irq_status & CRUnINTS_SFS)
- dev_dbg(cru->dev, "IRQ while state stopping\n");
- goto done;
- }
+ /* Calculate slot and prepare for new capture. */
+ guard(spinlock_irqsave)(&cru->hw_lock);
- /* Prepare for capture and update state */
amnmbs = rzg2l_cru_read(cru, AMnMBS);
- slot = amnmbs & AMnMBS_MBSTS;
+ cru->active_slot = amnmbs & AMnMBS_MBSTS;
/*
* AMnMBS.MBSTS indicates the destination of Memory Bank (MB).
* Recalculate to get the current transfer complete MB.
*/
- if (slot == 0)
+ if (cru->active_slot == 0)
slot = cru->num_buf - 1;
else
- slot--;
-
- /*
- * To hand buffers back in a known order to userspace start
- * to capture first from slot 0.
- */
- if (cru->state == RZG2L_CRU_DMA_STARTING) {
- if (slot != 0) {
- dev_dbg(cru->dev, "Starting sync slot: %d\n", slot);
- goto done;
- }
-
- dev_dbg(cru->dev, "Capture start synced!\n");
- cru->state = RZG2L_CRU_DMA_RUNNING;
- }
+ slot = cru->active_slot - 1;
/* Capture frame */
if (cru->queue_buf[slot]) {
@@ -632,9 +624,6 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data)
vb2_buffer_done(&cru->queue_buf[slot]->vb2_buf,
VB2_BUF_STATE_DONE);
cru->queue_buf[slot] = NULL;
- } else {
- /* Scratch buffer was used, dropping frame. */
- dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence);
}
cru->sequence++;
@@ -642,35 +631,7 @@ irqreturn_t rzg2l_cru_irq(int irq, void *data)
/* Prepare for next frame */
rzg2l_cru_fill_hw_slot(cru, slot);
-done:
- spin_unlock_irqrestore(&cru->qlock, flags);
-
- return IRQ_RETVAL(handled);
-}
-
-static int rzg3e_cru_get_current_slot(struct rzg2l_cru_dev *cru)
-{
- u64 amnmadrs;
- int slot;
-
- /*
- * When AMnMADRSL is read, AMnMADRSH of the higher-order
- * address also latches the address.
- *
- * AMnMADRSH must be read after AMnMADRSL has been read.
- */
- amnmadrs = rzg2l_cru_read(cru, AMnMADRSL);
- amnmadrs |= (u64)rzg2l_cru_read(cru, AMnMADRSH) << 32;
-
- /* Ensure amnmadrs is within this buffer range */
- for (slot = 0; slot < cru->num_buf; slot++) {
- if (amnmadrs >= cru->buf_addr[slot] &&
- amnmadrs < cru->buf_addr[slot] + cru->format.sizeimage)
- return slot;
- }
-
- dev_err(cru->dev, "Invalid MB address 0x%llx (out of range)\n", amnmadrs);
- return -EINVAL;
+ return IRQ_HANDLED;
}
irqreturn_t rzg3e_cru_irq(int irq, void *data)
@@ -679,69 +640,31 @@ irqreturn_t rzg3e_cru_irq(int irq, void *data)
u32 irq_status;
int slot;
- scoped_guard(spinlock, &cru->qlock) {
- irq_status = rzg2l_cru_read(cru, CRUnINTS2);
- if (!irq_status)
- return IRQ_NONE;
-
- dev_dbg(cru->dev, "CRUnINTS2 0x%x\n", irq_status);
-
- rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2));
-
- /* Nothing to do if capture status is 'RZG2L_CRU_DMA_STOPPED' */
- if (cru->state == RZG2L_CRU_DMA_STOPPED) {
- dev_dbg(cru->dev, "IRQ while state stopped\n");
- return IRQ_HANDLED;
- }
+ irq_status = rzg2l_cru_read(cru, CRUnINTS2);
+ if (!irq_status)
+ return IRQ_NONE;
- if (cru->state == RZG2L_CRU_DMA_STOPPING) {
- if (irq_status & CRUnINTS2_FSxS(0) ||
- irq_status & CRUnINTS2_FSxS(1) ||
- irq_status & CRUnINTS2_FSxS(2) ||
- irq_status & CRUnINTS2_FSxS(3))
- dev_dbg(cru->dev, "IRQ while state stopping\n");
- return IRQ_HANDLED;
- }
+ rzg2l_cru_write(cru, CRUnINTS2, rzg2l_cru_read(cru, CRUnINTS2));
- slot = rzg3e_cru_get_current_slot(cru);
- if (slot < 0)
- return IRQ_HANDLED;
-
- dev_dbg(cru->dev, "Current written slot: %d\n", slot);
- cru->buf_addr[slot] = 0;
-
- /*
- * To hand buffers back in a known order to userspace start
- * to capture first from slot 0.
- */
- if (cru->state == RZG2L_CRU_DMA_STARTING) {
- if (slot != 0) {
- dev_dbg(cru->dev, "Starting sync slot: %d\n", slot);
- return IRQ_HANDLED;
- }
- dev_dbg(cru->dev, "Capture start synced!\n");
- cru->state = RZG2L_CRU_DMA_RUNNING;
- }
+ guard(spinlock)(&cru->hw_lock);
+ slot = cru->active_slot;
+ cru->active_slot = rzg2l_cru_slot_next(cru, cru->active_slot);
- /* Capture frame */
- if (cru->queue_buf[slot]) {
- struct vb2_v4l2_buffer *buf = cru->queue_buf[slot];
+ /* Capture frame */
+ if (cru->queue_buf[slot]) {
+ struct vb2_v4l2_buffer *buf = cru->queue_buf[slot];
- buf->field = cru->format.field;
- buf->sequence = cru->sequence;
- buf->vb2_buf.timestamp = ktime_get_ns();
- vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE);
- cru->queue_buf[slot] = NULL;
- } else {
- /* Scratch buffer was used, dropping frame. */
- dev_dbg(cru->dev, "Dropping frame %u\n", cru->sequence);
- }
+ buf->field = cru->format.field;
+ buf->sequence = cru->sequence;
+ buf->vb2_buf.timestamp = ktime_get_ns();
+ vb2_buffer_done(&buf->vb2_buf, VB2_BUF_STATE_DONE);
+ cru->queue_buf[slot] = NULL;
+ }
- cru->sequence++;
+ cru->sequence++;
- /* Prepare for next frame */
- rzg2l_cru_fill_hw_slot(cru, slot);
- }
+ /* Prepare for next frame */
+ rzg2l_cru_fill_hw_slot(cru, slot);
return IRQ_HANDLED;
}
@@ -777,21 +700,21 @@ static int rzg2l_cru_start_streaming_vq(struct vb2_queue *vq, unsigned int count
cru->scratch = dma_alloc_coherent(cru->dev, cru->format.sizeimage,
&cru->scratch_phys, GFP_KERNEL);
if (!cru->scratch) {
- return_unused_buffers(cru, VB2_BUF_STATE_QUEUED);
+ rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED);
dev_err(cru->dev, "Failed to allocate scratch buffer\n");
ret = -ENOMEM;
goto assert_presetn;
}
+ cru->active_slot = 0;
cru->sequence = 0;
ret = rzg2l_cru_set_stream(cru, 1);
if (ret) {
- return_unused_buffers(cru, VB2_BUF_STATE_QUEUED);
+ rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_QUEUED);
goto out;
}
- cru->state = RZG2L_CRU_DMA_STARTING;
dev_dbg(cru->dev, "Starting to capture\n");
return 0;
@@ -824,7 +747,7 @@ static void rzg2l_cru_stop_streaming_vq(struct vb2_queue *vq)
dma_free_coherent(cru->dev, cru->format.sizeimage,
cru->scratch, cru->scratch_phys);
- return_unused_buffers(cru, VB2_BUF_STATE_ERROR);
+ rzg2l_cru_return_buffers(cru, VB2_BUF_STATE_ERROR);
reset_control_assert(cru->presetn);
clk_disable_unprepare(cru->vclk);
@@ -861,10 +784,9 @@ int rzg2l_cru_dma_register(struct rzg2l_cru_dev *cru)
mutex_init(&cru->lock);
INIT_LIST_HEAD(&cru->buf_list);
+ spin_lock_init(&cru->hw_lock);
spin_lock_init(&cru->qlock);
- cru->state = RZG2L_CRU_DMA_STOPPED;
-
for (i = 0; i < RZG2L_CRU_HW_BUFFER_MAX; i++)
cru->queue_buf[i] = NULL;
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
index b167f1bab7ef..932fed38cf3f 100644
--- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-video.c
@@ -297,12 +297,33 @@ err_return_buffers:
static void rzv2h_ivc_stop_streaming(struct vb2_queue *q)
{
struct rzv2h_ivc *ivc = vb2_get_drv_priv(q);
- u32 val = 0;
+ unsigned int loop = 5;
- rzv2h_ivc_write(ivc, RZV2H_IVC_REG_FM_STOP, RZV2H_IVC_REG_FM_STOP_FSTOP);
- readl_poll_timeout(ivc->base + RZV2H_IVC_REG_FM_STOP,
- val, !(val & RZV2H_IVC_REG_FM_STOP_FSTOP),
- 10 * USEC_PER_MSEC, 250 * USEC_PER_MSEC);
+ /*
+ * If no frame transfer is in progress, we're done, otherwise, wait for
+ * the transfer to complete.
+ *
+ * Transferring a 1920x1080@10bit frame to the ISP takes less than 5
+ * msec so sleep for 2.5 msec (+- 25%) and give up after 5 attempts.
+ */
+ for (; loop > 0; loop--) {
+ unsigned int vvalid_ifp;
+
+ /*
+ * Inspect the ivc->vvalid_ifp variable holding the spinlock not
+ * to the race with the rzv2h_ivc_buffer_done() call in the irq
+ * handler.
+ */
+ scoped_guard(spinlock_irq, &ivc->spinlock) {
+ vvalid_ifp = ivc->vvalid_ifp;
+ }
+ if (vvalid_ifp < 2)
+ break;
+
+ fsleep(2500);
+ }
+ if (!loop)
+ dev_err(ivc->dev, "Failed to stop streaming\n");
rzv2h_ivc_return_buffers(ivc, VB2_BUF_STATE_ERROR);
video_device_pipeline_stop(&ivc->vdev.dev);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
index 9d93cb8b8e82..325be30836d7 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
@@ -130,15 +130,12 @@ static int brx_set_format(struct v4l2_subdev *subdev,
struct vsp1_brx *brx = to_brx(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
- int ret = 0;
- mutex_lock(&brx->entity.lock);
+ guard(mutex)(&brx->entity.lock);
state = vsp1_entity_get_state(&brx->entity, sd_state, fmt->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
brx_try_format(brx, state, fmt->pad, &fmt->format);
@@ -166,9 +163,7 @@ static int brx_set_format(struct v4l2_subdev *subdev,
}
}
-done:
- mutex_unlock(&brx->entity.lock);
- return ret;
+ return 0;
}
static int brx_get_selection(struct v4l2_subdev *subdev,
@@ -195,9 +190,10 @@ static int brx_get_selection(struct v4l2_subdev *subdev,
if (!state)
return -EINVAL;
- mutex_lock(&brx->entity.lock);
- sel->r = *v4l2_subdev_state_get_compose(state, sel->pad);
- mutex_unlock(&brx->entity.lock);
+ scoped_guard(mutex, &brx->entity.lock) {
+ sel->r = *v4l2_subdev_state_get_compose(state, sel->pad);
+ }
+
return 0;
default:
@@ -213,7 +209,6 @@ static int brx_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *compose;
- int ret = 0;
if (sel->pad == brx->entity.source_pad)
return -EINVAL;
@@ -221,13 +216,11 @@ static int brx_set_selection(struct v4l2_subdev *subdev,
if (sel->target != V4L2_SEL_TGT_COMPOSE)
return -EINVAL;
- mutex_lock(&brx->entity.lock);
+ guard(mutex)(&brx->entity.lock);
state = vsp1_entity_get_state(&brx->entity, sd_state, sel->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
/*
* The compose rectangle top left corner must be inside the output
@@ -248,9 +241,7 @@ static int brx_set_selection(struct v4l2_subdev *subdev,
compose = v4l2_subdev_state_get_compose(state, sel->pad);
*compose = sel->r;
-done:
- mutex_unlock(&brx->entity.lock);
- return ret;
+ return 0;
}
static const struct v4l2_subdev_pad_ops brx_pad_ops = {
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_clu.c b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
index 04c466c4da81..a6e4bcab5101 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_clu.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_clu.c
@@ -53,9 +53,9 @@ static int clu_set_table(struct vsp1_clu *clu, struct v4l2_ctrl *ctrl)
for (i = 0; i < CLU_SIZE; ++i)
vsp1_dl_body_write(dlb, VI6_CLU_DATA, ctrl->p_new.p_u32[i]);
- spin_lock_irq(&clu->lock);
- swap(clu->clu, dlb);
- spin_unlock_irq(&clu->lock);
+ scoped_guard(spinlock_irq, &clu->lock) {
+ swap(clu->clu, dlb);
+ }
vsp1_dl_body_put(dlb);
return 0;
@@ -162,7 +162,6 @@ static void clu_configure_frame(struct vsp1_entity *entity,
{
struct vsp1_clu *clu = to_clu(&entity->subdev);
struct vsp1_dl_body *clu_dlb;
- unsigned long flags;
u32 ctrl = VI6_CLU_CTRL_AAI | VI6_CLU_CTRL_MVS | VI6_CLU_CTRL_EN;
/* 2D mode can only be used with the YCbCr pixel encoding. */
@@ -173,10 +172,10 @@ static void clu_configure_frame(struct vsp1_entity *entity,
vsp1_clu_write(clu, dlb, VI6_CLU_CTRL, ctrl);
- spin_lock_irqsave(&clu->lock, flags);
- clu_dlb = clu->clu;
- clu->clu = NULL;
- spin_unlock_irqrestore(&clu->lock, flags);
+ scoped_guard(spinlock_irqsave, &clu->lock) {
+ clu_dlb = clu->clu;
+ clu->clu = NULL;
+ }
if (clu_dlb) {
vsp1_dl_list_add_body(dl, clu_dlb);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index 6c5578d9d2de..3dc74fed91dc 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
@@ -336,9 +336,8 @@ void vsp1_dl_body_pool_destroy(struct vsp1_dl_body_pool *pool)
struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool)
{
struct vsp1_dl_body *dlb = NULL;
- unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irqsave)(&pool->lock);
if (!list_empty(&pool->free)) {
dlb = list_first_entry(&pool->free, struct vsp1_dl_body, free);
@@ -346,8 +345,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool)
refcount_set(&dlb->refcnt, 1);
}
- spin_unlock_irqrestore(&pool->lock, flags);
-
return dlb;
}
@@ -359,8 +356,6 @@ struct vsp1_dl_body *vsp1_dl_body_get(struct vsp1_dl_body_pool *pool)
*/
void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
{
- unsigned long flags;
-
if (!dlb)
return;
@@ -369,9 +364,9 @@ void vsp1_dl_body_put(struct vsp1_dl_body *dlb)
dlb->num_entries = 0;
- spin_lock_irqsave(&dlb->pool->lock, flags);
+ guard(spinlock_irqsave)(&dlb->pool->lock);
+
list_add_tail(&dlb->free, &dlb->pool->free);
- spin_unlock_irqrestore(&dlb->pool->lock, flags);
}
/**
@@ -493,9 +488,8 @@ static
struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool)
{
struct vsp1_dl_ext_cmd *cmd = NULL;
- unsigned long flags;
- spin_lock_irqsave(&pool->lock, flags);
+ guard(spinlock_irqsave)(&pool->lock);
if (!list_empty(&pool->free)) {
cmd = list_first_entry(&pool->free, struct vsp1_dl_ext_cmd,
@@ -503,24 +497,20 @@ struct vsp1_dl_ext_cmd *vsp1_dl_ext_cmd_get(struct vsp1_dl_cmd_pool *pool)
list_del(&cmd->free);
}
- spin_unlock_irqrestore(&pool->lock, flags);
-
return cmd;
}
static void vsp1_dl_ext_cmd_put(struct vsp1_dl_ext_cmd *cmd)
{
- unsigned long flags;
-
if (!cmd)
return;
/* Reset flags, these mark data usage. */
cmd->flags = 0;
- spin_lock_irqsave(&cmd->pool->lock, flags);
+ guard(spinlock_irqsave)(&cmd->pool->lock);
+
list_add_tail(&cmd->free, &cmd->pool->free);
- spin_unlock_irqrestore(&cmd->pool->lock, flags);
}
static void vsp1_dl_ext_cmd_pool_destroy(struct vsp1_dl_cmd_pool *pool)
@@ -611,11 +601,10 @@ static void vsp1_dl_list_free(struct vsp1_dl_list *dl)
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);
+ guard(spinlock_irqsave)(&dlm->lock);
if (!list_empty(&dlm->free)) {
dl = list_first_entry(&dlm->free, struct vsp1_dl_list, list);
@@ -629,8 +618,6 @@ struct vsp1_dl_list *vsp1_dl_list_get(struct vsp1_dl_manager *dlm)
dl->allocated = true;
}
- spin_unlock_irqrestore(&dlm->lock, flags);
-
return dl;
}
@@ -690,14 +677,12 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
*/
void vsp1_dl_list_put(struct vsp1_dl_list *dl)
{
- unsigned long flags;
-
if (!dl)
return;
- spin_lock_irqsave(&dl->dlm->lock, flags);
+ guard(spinlock_irqsave)(&dl->dlm->lock);
+
__vsp1_dl_list_put(dl);
- spin_unlock_irqrestore(&dl->dlm->lock, flags);
}
/**
@@ -937,7 +922,6 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
{
struct vsp1_dl_manager *dlm = dl->dlm;
struct vsp1_dl_list *dl_next;
- unsigned long flags;
/* Fill the header for the head and chained display lists. */
vsp1_dl_list_fill_header(dl, list_empty(&dl->chain));
@@ -950,14 +934,12 @@ void vsp1_dl_list_commit(struct vsp1_dl_list *dl, unsigned int dl_flags)
dl->flags = dl_flags & ~VSP1_DL_FRAME_END_COMPLETED;
- spin_lock_irqsave(&dlm->lock, flags);
+ guard(spinlock_irqsave)(&dlm->lock);
if (dlm->singleshot)
vsp1_dl_list_commit_singleshot(dl);
else
vsp1_dl_list_commit_continuous(dl);
-
- spin_unlock_irqrestore(&dlm->lock, flags);
}
/* -----------------------------------------------------------------------------
@@ -991,7 +973,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
u32 status = vsp1_read(vsp1, VI6_STATUS);
unsigned int flags = 0;
- spin_lock(&dlm->lock);
+ guard(spinlock)(&dlm->lock);
/*
* The mem-to-mem pipelines work in single-shot mode. No new display
@@ -1001,7 +983,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
__vsp1_dl_list_put(dlm->active);
dlm->active = NULL;
flags |= VSP1_DL_FRAME_END_COMPLETED;
- goto done;
+ return flags;
}
/*
@@ -1011,7 +993,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
* and retry.
*/
if (vsp1_dl_list_hw_update_pending(dlm))
- goto done;
+ return flags;
/*
* Progressive streams report only TOP fields. If we have a BOTTOM
@@ -1019,7 +1001,7 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
* next frame end interrupt.
*/
if (status & VI6_STATUS_FLD_STD(dlm->index))
- goto done;
+ return flags;
/*
* If the active display list has the writeback flag set, the frame
@@ -1058,9 +1040,6 @@ unsigned int vsp1_dlm_irq_frame_end(struct vsp1_dl_manager *dlm)
dlm->pending = NULL;
}
-done:
- spin_unlock(&dlm->lock);
-
return flags;
}
@@ -1085,17 +1064,15 @@ 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);
-
- __vsp1_dl_list_put(dlm->active);
- __vsp1_dl_list_put(dlm->queued);
- __vsp1_dl_list_put(dlm->pending);
+ scoped_guard(spinlock_irqsave, &dlm->lock) {
+ __vsp1_dl_list_put(dlm->active);
+ __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);
+ list_count = list_count_nodes(&dlm->free);
+ }
WARN_ON_ONCE(list_count != dlm->list_count);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index 15d266439564..f6fbd3475329 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
@@ -57,6 +57,50 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
* Pipeline Configuration
*/
+/* Configure all entities in the pipeline. */
+static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
+{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ struct vsp1_entity *entity;
+ struct vsp1_entity *next;
+ struct vsp1_dl_list *dl;
+ struct vsp1_dl_body *dlb;
+ unsigned int dl_flags = 0;
+
+ vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0],
+ drm_pipe->width, 0);
+
+ if (drm_pipe->force_brx_release)
+ dl_flags |= VSP1_DL_FRAME_END_INTERNAL;
+ if (pipe->output->writeback)
+ dl_flags |= VSP1_DL_FRAME_END_WRITEBACK;
+
+ dl = vsp1_dl_list_get(pipe->output->dlm);
+ dlb = vsp1_dl_list_get_body0(dl);
+
+ list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
+ /* Disconnect unused entities from the pipeline. */
+ if (!entity->pipe) {
+ vsp1_dl_body_write(dlb, entity->route->reg,
+ VI6_DPR_NODE_UNUSED);
+
+ entity->sink = NULL;
+ list_del(&entity->list_pipe);
+
+ continue;
+ }
+
+ vsp1_entity_route_setup(entity, pipe, dlb);
+ vsp1_entity_configure_stream(entity, entity->state, pipe,
+ dl, dlb);
+ vsp1_entity_configure_frame(entity, pipe, dl, dlb);
+ vsp1_entity_configure_partition(entity, pipe,
+ &pipe->part_table[0], dl, dlb);
+ }
+
+ vsp1_dl_list_commit(dl, dl_flags);
+}
+
/*
* Insert the UIF in the pipeline between the prev and next entities. If no UIF
* is available connect the two entities directly.
@@ -224,8 +268,6 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
/* Setup the BRx source pad. */
static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
struct vsp1_pipeline *pipe);
-static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe);
-
static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
struct vsp1_pipeline *pipe)
{
@@ -541,50 +583,6 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1,
return 0;
}
-/* Configure all entities in the pipeline. */
-static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
-{
- struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
- struct vsp1_entity *entity;
- struct vsp1_entity *next;
- struct vsp1_dl_list *dl;
- struct vsp1_dl_body *dlb;
- unsigned int dl_flags = 0;
-
- vsp1_pipeline_calculate_partition(pipe, &pipe->part_table[0],
- drm_pipe->width, 0);
-
- if (drm_pipe->force_brx_release)
- dl_flags |= VSP1_DL_FRAME_END_INTERNAL;
- if (pipe->output->writeback)
- dl_flags |= VSP1_DL_FRAME_END_WRITEBACK;
-
- dl = vsp1_dl_list_get(pipe->output->dlm);
- dlb = vsp1_dl_list_get_body0(dl);
-
- list_for_each_entry_safe(entity, next, &pipe->entities, list_pipe) {
- /* Disconnect unused entities from the pipeline. */
- if (!entity->pipe) {
- vsp1_dl_body_write(dlb, entity->route->reg,
- VI6_DPR_NODE_UNUSED);
-
- entity->sink = NULL;
- list_del(&entity->list_pipe);
-
- continue;
- }
-
- vsp1_entity_route_setup(entity, pipe, dlb);
- vsp1_entity_configure_stream(entity, entity->state, pipe,
- dl, dlb);
- vsp1_entity_configure_frame(entity, pipe, dl, dlb);
- vsp1_entity_configure_partition(entity, pipe,
- &pipe->part_table[0], dl, dlb);
- }
-
- vsp1_dl_list_commit(dl, dl_flags);
-}
-
static int vsp1_du_pipeline_set_rwpf_format(struct vsp1_device *vsp1,
struct vsp1_rwpf *rwpf,
u32 pixelformat, unsigned int pitch)
@@ -631,14 +629,14 @@ int vsp1_du_init(struct device *dev)
EXPORT_SYMBOL_GPL(vsp1_du_init);
/**
- * vsp1_du_setup_lif - Setup the output part of the VSP pipeline
+ * vsp1_du_enable - Setup and enable a DU pipeline
* @dev: the VSP device
* @pipe_index: the DRM pipeline index
* @cfg: the LIF configuration
*
* Configure the output part of VSP DRM pipeline for the given frame @cfg.width
* and @cfg.height. This sets up formats on the BRx source pad, the WPF sink and
- * source pads, and the LIF sink pad.
+ * source pads, and the LIF sink pad, and then starts the pipeline.
*
* The @pipe_index argument selects which DRM pipeline to setup. The number of
* available pipelines depend on the VSP instance.
@@ -651,14 +649,12 @@ EXPORT_SYMBOL_GPL(vsp1_du_init);
*
* Return 0 on success or a negative error code on failure.
*/
-int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
- const struct vsp1_du_lif_config *cfg)
+int vsp1_du_enable(struct device *dev, unsigned int pipe_index,
+ const struct vsp1_du_lif_config *cfg)
{
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe;
struct vsp1_pipeline *pipe;
- unsigned long flags;
- unsigned int i;
int ret;
if (pipe_index >= vsp1->info->lif_count)
@@ -667,17 +663,87 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe = &vsp1->drm->pipe[pipe_index];
pipe = &drm_pipe->pipe;
- if (!cfg) {
- struct vsp1_brx *brx;
+ /* Reset the underrun counter */
+ pipe->underrun_count = 0;
+
+ drm_pipe->width = cfg->width;
+ drm_pipe->height = cfg->height;
+ pipe->interlaced = cfg->interlaced;
+
+ dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u%s\n",
+ __func__, pipe_index, cfg->width, cfg->height,
+ pipe->interlaced ? "i" : "");
+
+ scoped_guard(mutex, &vsp1->drm->lock) {
+ /* Setup formats through the pipeline. */
+ ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe);
+ if (ret < 0)
+ return ret;
- mutex_lock(&vsp1->drm->lock);
+ ret = vsp1_du_pipeline_setup_output(vsp1, pipe);
+ if (ret < 0)
+ return ret;
- brx = to_brx(&pipe->brx->subdev);
+ vsp1_pipeline_dump(pipe, "DU enable");
+
+ /* Enable the VSP1. */
+ ret = vsp1_device_get(vsp1);
+ if (ret < 0)
+ return ret;
/*
- * NULL configuration means the CRTC is being disabled, stop
- * the pipeline and turn the light off.
+ * Register a callback to allow us to notify the DRM driver of frame
+ * completion events.
*/
+ drm_pipe->du_complete = cfg->callback;
+ drm_pipe->du_private = cfg->callback_data;
+
+ /* Disable the display interrupts. */
+ vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0);
+ vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0);
+
+ /* Configure all entities in the pipeline. */
+ vsp1_du_pipeline_configure(pipe);
+ }
+
+ /* Start the pipeline. */
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ vsp1_pipeline_run(pipe);
+ }
+
+ dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(vsp1_du_enable);
+
+/**
+ * vsp1_du_disable - Disable and stop a DU pipeline
+ * @dev: the VSP device
+ * @pipe_index: the DRM pipeline index
+ *
+ * The @pipe_index argument selects which DRM pipeline to disable. The number
+ * of available pipelines depend on the VSP instance.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+int vsp1_du_disable(struct device *dev, unsigned int pipe_index)
+{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+ struct vsp1_drm_pipeline *drm_pipe;
+ struct vsp1_pipeline *pipe;
+ unsigned int i;
+ int ret;
+
+ if (pipe_index >= vsp1->info->lif_count)
+ return -EINVAL;
+
+ drm_pipe = &vsp1->drm->pipe[pipe_index];
+ pipe = &drm_pipe->pipe;
+
+ scoped_guard(mutex, &vsp1->drm->lock) {
+ struct vsp1_brx *brx = to_brx(&pipe->brx->subdev);
+
ret = vsp1_pipeline_stop(pipe);
if (ret == -ETIMEDOUT)
dev_err(vsp1->dev, "DRM pipeline stop timeout\n");
@@ -710,76 +776,16 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
list_del(&pipe->brx->list_pipe);
pipe->brx->pipe = NULL;
pipe->brx = NULL;
-
- mutex_unlock(&vsp1->drm->lock);
-
- vsp1_dlm_reset(pipe->output->dlm);
- vsp1_device_put(vsp1);
-
- dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__);
-
- return 0;
}
- /* Reset the underrun counter */
- pipe->underrun_count = 0;
-
- drm_pipe->width = cfg->width;
- drm_pipe->height = cfg->height;
- pipe->interlaced = cfg->interlaced;
-
- dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u%s\n",
- __func__, pipe_index, cfg->width, cfg->height,
- pipe->interlaced ? "i" : "");
-
- mutex_lock(&vsp1->drm->lock);
-
- /* Setup formats through the pipeline. */
- ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe);
- if (ret < 0)
- goto unlock;
-
- ret = vsp1_du_pipeline_setup_output(vsp1, pipe);
- if (ret < 0)
- goto unlock;
-
- vsp1_pipeline_dump(pipe, "LIF setup");
-
- /* Enable the VSP1. */
- ret = vsp1_device_get(vsp1);
- if (ret < 0)
- goto unlock;
-
- /*
- * Register a callback to allow us to notify the DRM driver of frame
- * completion events.
- */
- drm_pipe->du_complete = cfg->callback;
- drm_pipe->du_private = cfg->callback_data;
-
- /* Disable the display interrupts. */
- vsp1_write(vsp1, VI6_DISP_IRQ_STA(pipe_index), 0);
- vsp1_write(vsp1, VI6_DISP_IRQ_ENB(pipe_index), 0);
-
- /* Configure all entities in the pipeline. */
- vsp1_du_pipeline_configure(pipe);
-
-unlock:
- mutex_unlock(&vsp1->drm->lock);
+ vsp1_dlm_reset(pipe->output->dlm);
+ vsp1_device_put(vsp1);
- if (ret < 0)
- return ret;
-
- /* Start the pipeline. */
- spin_lock_irqsave(&pipe->irqlock, flags);
- vsp1_pipeline_run(pipe);
- spin_unlock_irqrestore(&pipe->irqlock, flags);
-
- dev_dbg(vsp1->dev, "%s: pipeline enabled\n", __func__);
+ dev_dbg(vsp1->dev, "%s: pipeline disabled\n", __func__);
return 0;
}
-EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
+EXPORT_SYMBOL_GPL(vsp1_du_disable);
/**
* vsp1_du_atomic_begin - Prepare for an atomic update
@@ -904,7 +910,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
drm_pipe->crc = cfg->crc;
- mutex_lock(&vsp1->drm->lock);
+ guard(mutex)(&vsp1->drm->lock);
if (cfg->writeback.pixelformat) {
const struct vsp1_du_writeback_config *wb_cfg = &cfg->writeback;
@@ -913,7 +919,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
wb_cfg->pixelformat,
wb_cfg->pitch);
if (WARN_ON(ret < 0))
- goto done;
+ return;
pipe->output->mem.addr[0] = wb_cfg->mem[0];
pipe->output->mem.addr[1] = wb_cfg->mem[1];
@@ -926,9 +932,6 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index,
vsp1_pipeline_dump(pipe, "atomic update");
vsp1_du_pipeline_configure(pipe);
-
-done:
- mutex_unlock(&vsp1->drm->lock);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
index 839b75b62ceb..2ae2a573f0de 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
@@ -172,9 +172,9 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
if (!state)
return -EINVAL;
- mutex_lock(&entity->lock);
+ guard(mutex)(&entity->lock);
+
fmt->format = *v4l2_subdev_state_get_format(state, fmt->pad);
- mutex_unlock(&entity->lock);
return 0;
}
@@ -216,10 +216,10 @@ int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
if (!state)
return -EINVAL;
- mutex_lock(&entity->lock);
- format = v4l2_subdev_state_get_format(state, 0);
- code->code = format->code;
- mutex_unlock(&entity->lock);
+ scoped_guard(mutex, &entity->lock) {
+ format = v4l2_subdev_state_get_format(state, 0);
+ code->code = format->code;
+ }
}
return 0;
@@ -308,22 +308,19 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *selection;
unsigned int i;
- int ret = 0;
- mutex_lock(&entity->lock);
+ guard(mutex)(&entity->lock);
state = vsp1_entity_get_state(entity, sd_state, fmt->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
format = v4l2_subdev_state_get_format(state, fmt->pad);
if (fmt->pad == entity->source_pad) {
/* The output format can't be modified. */
fmt->format = *format;
- goto done;
+ return 0;
}
/*
@@ -369,9 +366,7 @@ int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
selection->width = format->width;
selection->height = format->height;
-done:
- mutex_unlock(&entity->lock);
- return ret;
+ return 0;
}
static int vsp1_entity_init_state(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
index 2c8ce7175a4e..0ef512e3a94b 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgo.c
@@ -153,11 +153,11 @@ static void hgo_configure_stream(struct vsp1_entity *entity,
(crop->width << VI6_HGO_SIZE_HSIZE_SHIFT) |
(crop->height << VI6_HGO_SIZE_VSIZE_SHIFT));
- mutex_lock(hgo->ctrls.handler.lock);
- hgo->max_rgb = hgo->ctrls.max_rgb->cur.val;
- if (hgo->ctrls.num_bins)
- hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val];
- mutex_unlock(hgo->ctrls.handler.lock);
+ scoped_guard(mutex, hgo->ctrls.handler.lock) {
+ hgo->max_rgb = hgo->ctrls.max_rgb->cur.val;
+ if (hgo->ctrls.num_bins)
+ hgo->num_bins = hgo_num_bins[hgo->ctrls.num_bins->cur.val];
+ }
hratio = crop->width * 2 / compose->width / 3;
vratio = crop->height * 2 / compose->height / 3;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
index 858f330d44fa..78b5a9201c70 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hgt.c
@@ -152,15 +152,15 @@ static void hgt_configure_stream(struct vsp1_entity *entity,
(crop->width << VI6_HGT_SIZE_HSIZE_SHIFT) |
(crop->height << VI6_HGT_SIZE_VSIZE_SHIFT));
- mutex_lock(hgt->ctrls.lock);
- for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) {
- lower = hgt->hue_areas[i*2 + 0];
- upper = hgt->hue_areas[i*2 + 1];
- vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i),
- (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) |
- (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT));
+ scoped_guard(mutex, hgt->ctrls.lock) {
+ for (i = 0; i < HGT_NUM_HUE_AREAS; ++i) {
+ lower = hgt->hue_areas[i*2 + 0];
+ upper = hgt->hue_areas[i*2 + 1];
+ vsp1_hgt_write(hgt, dlb, VI6_HGT_HUE_AREA(i),
+ (lower << VI6_HGT_HUE_AREA_LOWER_SHIFT) |
+ (upper << VI6_HGT_HUE_AREA_UPPER_SHIFT));
+ }
}
- mutex_unlock(hgt->ctrls.lock);
hratio = crop->width * 2 / compose->width / 3;
vratio = crop->height * 2 / compose->height / 3;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
index 3f87a2c9df0e..97dbfb93abe9 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
@@ -35,20 +35,18 @@ to_vsp1_histogram_buffer(struct vb2_v4l2_buffer *vbuf)
struct vsp1_histogram_buffer *
vsp1_histogram_buffer_get(struct vsp1_histogram *histo)
{
- struct vsp1_histogram_buffer *buf = NULL;
+ struct vsp1_histogram_buffer *buf;
- spin_lock(&histo->irqlock);
+ guard(spinlock)(&histo->irqlock);
if (list_empty(&histo->irqqueue))
- goto done;
+ return NULL;
buf = list_first_entry(&histo->irqqueue, struct vsp1_histogram_buffer,
queue);
list_del(&buf->queue);
histo->readout = true;
-done:
- spin_unlock(&histo->irqlock);
return buf;
}
@@ -68,10 +66,10 @@ void vsp1_histogram_buffer_complete(struct vsp1_histogram *histo,
vb2_set_plane_payload(&buf->buf.vb2_buf, 0, size);
vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
- spin_lock(&histo->irqlock);
+ guard(spinlock)(&histo->irqlock);
+
histo->readout = false;
wake_up(&histo->wait_queue);
- spin_unlock(&histo->irqlock);
}
/* -----------------------------------------------------------------------------
@@ -123,9 +121,9 @@ static void histo_buffer_queue(struct vb2_buffer *vb)
struct vsp1_histogram *histo = vb2_get_drv_priv(vb->vb2_queue);
struct vsp1_histogram_buffer *buf = to_vsp1_histogram_buffer(vbuf);
- spin_lock_irq(&histo->irqlock);
+ guard(spinlock_irq)(&histo->irqlock);
+
list_add_tail(&buf->queue, &histo->irqqueue);
- spin_unlock_irq(&histo->irqlock);
}
static int histo_start_streaming(struct vb2_queue *vq, unsigned int count)
@@ -138,7 +136,7 @@ static void histo_stop_streaming(struct vb2_queue *vq)
struct vsp1_histogram *histo = vb2_get_drv_priv(vq);
struct vsp1_histogram_buffer *buffer;
- spin_lock_irq(&histo->irqlock);
+ guard(spinlock_irq)(&histo->irqlock);
/* Remove all buffers from the IRQ queue. */
list_for_each_entry(buffer, &histo->irqqueue, queue)
@@ -147,8 +145,6 @@ static void histo_stop_streaming(struct vb2_queue *vq)
/* Wait for the buffer being read out (if any) to complete. */
wait_event_lock_irq(histo->wait_queue, !histo->readout, histo->irqlock);
-
- spin_unlock_irq(&histo->irqlock);
}
static const struct vb2_ops histo_video_queue_qops = {
@@ -196,18 +192,15 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
- int ret = 0;
if (sel->pad != HISTO_PAD_SINK)
return -EINVAL;
- mutex_lock(&histo->entity.lock);
+ guard(mutex)(&histo->entity.lock);
state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
@@ -237,13 +230,10 @@ static int histo_get_selection(struct v4l2_subdev *subdev,
break;
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
-done:
- mutex_unlock(&histo->entity.lock);
- return ret;
+ return 0;
}
static int histo_set_crop(struct v4l2_subdev *subdev,
@@ -321,29 +311,22 @@ static int histo_set_selection(struct v4l2_subdev *subdev,
{
struct vsp1_histogram *histo = subdev_to_histo(subdev);
struct v4l2_subdev_state *state;
- int ret;
if (sel->pad != HISTO_PAD_SINK)
return -EINVAL;
- mutex_lock(&histo->entity.lock);
+ guard(mutex)(&histo->entity.lock);
state = vsp1_entity_get_state(&histo->entity, sd_state, sel->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
if (sel->target == V4L2_SEL_TGT_CROP)
- ret = histo_set_crop(subdev, state, sel);
+ return histo_set_crop(subdev, state, sel);
else if (sel->target == V4L2_SEL_TGT_COMPOSE)
- ret = histo_set_compose(subdev, state, sel);
+ return histo_set_compose(subdev, state, sel);
else
- ret = -EINVAL;
-
-done:
- mutex_unlock(&histo->entity.lock);
- return ret;
+ return -EINVAL;
}
static int histo_set_format(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
index 830e124beb7b..df069c228243 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
@@ -115,15 +115,12 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
struct vsp1_hsit *hsit = to_hsit(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
- int ret = 0;
- mutex_lock(&hsit->entity.lock);
+ guard(mutex)(&hsit->entity.lock);
state = vsp1_entity_get_state(&hsit->entity, sd_state, fmt->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
format = v4l2_subdev_state_get_format(state, fmt->pad);
@@ -133,7 +130,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
* modified.
*/
fmt->format = *format;
- goto done;
+ return 0;
}
format->code = hsit->inverse ? MEDIA_BUS_FMT_AHSV8888_1X32
@@ -161,9 +158,7 @@ static int hsit_set_format(struct v4l2_subdev *subdev,
vsp1_entity_adjust_color_space(format);
-done:
- mutex_unlock(&hsit->entity.lock);
- return ret;
+ return 0;
}
static const struct v4l2_subdev_pad_ops hsit_pad_ops = {
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_lut.c b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
index 94bdedcc5c92..a22c31e17cb7 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_lut.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_lut.c
@@ -50,9 +50,9 @@ static int lut_set_table(struct vsp1_lut *lut, struct v4l2_ctrl *ctrl)
vsp1_dl_body_write(dlb, VI6_LUT_TABLE + 4 * i,
ctrl->p_new.p_u32[i]);
- spin_lock_irq(&lut->lock);
- swap(lut->lut, dlb);
- spin_unlock_irq(&lut->lock);
+ scoped_guard(spinlock_irq, &lut->lock) {
+ swap(lut->lut, dlb);
+ }
vsp1_dl_body_put(dlb);
return 0;
@@ -132,12 +132,11 @@ static void lut_configure_frame(struct vsp1_entity *entity,
{
struct vsp1_lut *lut = to_lut(&entity->subdev);
struct vsp1_dl_body *lut_dlb;
- unsigned long flags;
- spin_lock_irqsave(&lut->lock, flags);
- lut_dlb = lut->lut;
- lut->lut = NULL;
- spin_unlock_irqrestore(&lut->lock, flags);
+ scoped_guard(spinlock_irqsave, &lut->lock) {
+ lut_dlb = lut->lut;
+ lut->lut = NULL;
+ }
if (lut_dlb) {
vsp1_dl_list_add_body(dl, lut_dlb);
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
index 5d769cc42fe1..32bb02ce0366 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_pipe.c
@@ -229,6 +229,10 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = {
1, { 32, 0, 0 }, false, false, 1, 1, false },
};
+#define vsp1_for_each_format(info, formats) \
+ for (const struct vsp1_format_info *info = &formats[0]; \
+ info < formats + ARRAY_SIZE(formats); ++info)
+
/**
* vsp1_get_format_info - Retrieve format information for a 4CC
* @vsp1: the VSP1 device
@@ -240,30 +244,20 @@ static const struct vsp1_format_info vsp1_video_hsit_formats[] = {
const struct vsp1_format_info *vsp1_get_format_info(struct vsp1_device *vsp1,
u32 fourcc)
{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
- const struct vsp1_format_info *info = &vsp1_video_formats[i];
-
+ vsp1_for_each_format(info, vsp1_video_formats) {
if (info->fourcc == fourcc)
return info;
}
if (vsp1->info->gen == 2) {
- for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) {
- const struct vsp1_format_info *info =
- &vsp1_video_gen2_formats[i];
-
+ vsp1_for_each_format(info, vsp1_video_gen2_formats) {
if (info->fourcc == fourcc)
return info;
}
}
if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
- for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) {
- const struct vsp1_format_info *info =
- &vsp1_video_hsit_formats[i];
-
+ vsp1_for_each_format(info, vsp1_video_hsit_formats) {
if (info->fourcc == fourcc)
return info;
}
@@ -287,8 +281,6 @@ const struct vsp1_format_info *
vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
u32 code)
{
- unsigned int i;
-
if (!code) {
if (index < ARRAY_SIZE(vsp1_video_formats))
return &vsp1_video_formats[index];
@@ -308,9 +300,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
return NULL;
}
- for (i = 0; i < ARRAY_SIZE(vsp1_video_formats); ++i) {
- const struct vsp1_format_info *info = &vsp1_video_formats[i];
-
+ vsp1_for_each_format(info, vsp1_video_formats) {
if (info->mbus == code) {
if (!index)
return info;
@@ -319,10 +309,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
}
if (vsp1->info->gen == 2) {
- for (i = 0; i < ARRAY_SIZE(vsp1_video_gen2_formats); ++i) {
- const struct vsp1_format_info *info =
- &vsp1_video_gen2_formats[i];
-
+ vsp1_for_each_format(info, vsp1_video_gen2_formats) {
if (info->mbus == code) {
if (!index)
return info;
@@ -332,10 +319,7 @@ vsp1_get_format_info_by_index(struct vsp1_device *vsp1, unsigned int index,
}
if (vsp1_feature(vsp1, VSP1_HAS_HSIT)) {
- for (i = 0; i < ARRAY_SIZE(vsp1_video_hsit_formats); ++i) {
- const struct vsp1_format_info *info =
- &vsp1_video_hsit_formats[i];
-
+ vsp1_for_each_format(info, vsp1_video_hsit_formats) {
if (info->mbus == code) {
if (!index)
return info;
@@ -487,21 +471,15 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe)
bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe)
{
- unsigned long flags;
- bool stopped;
+ guard(spinlock_irqsave)(&pipe->irqlock);
- spin_lock_irqsave(&pipe->irqlock, flags);
- stopped = pipe->state == VSP1_PIPELINE_STOPPED;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
-
- return stopped;
+ return pipe->state == VSP1_PIPELINE_STOPPED;
}
int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
{
struct vsp1_device *vsp1 = pipe->output->entity.vsp1;
struct vsp1_entity *entity;
- unsigned long flags;
int ret;
if (pipe->lif) {
@@ -511,16 +489,16 @@ int vsp1_pipeline_stop(struct vsp1_pipeline *pipe)
*/
ret = vsp1_reset_wpf(vsp1, pipe->output->entity.index);
if (ret == 0) {
- spin_lock_irqsave(&pipe->irqlock, flags);
- pipe->state = VSP1_PIPELINE_STOPPED;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ pipe->state = VSP1_PIPELINE_STOPPED;
+ }
}
} else {
/* Otherwise just request a stop and wait. */
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (pipe->state == VSP1_PIPELINE_RUNNING)
- pipe->state = VSP1_PIPELINE_STOPPING;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ }
ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe),
msecs_to_jiffies(500));
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
index c72518b29f84..ced01870acd6 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
@@ -116,15 +116,12 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
- int ret = 0;
- mutex_lock(&rwpf->entity.lock);
+ guard(mutex)(&rwpf->entity.lock);
state = vsp1_entity_get_state(&rwpf->entity, sd_state, fmt->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
/* Default to YUV if the requested format is not supported. */
if (fmt->format.code != MEDIA_BUS_FMT_ARGB8888_1X32 &&
@@ -174,7 +171,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
fmt->format = *format;
fmt->format.flags = flags;
- goto done;
+ return 0;
}
format->code = fmt->format.code;
@@ -213,9 +210,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
format->height = fmt->format.width;
}
-done:
- mutex_unlock(&rwpf->entity.lock);
- return ret;
+ return 0;
}
static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
@@ -225,7 +220,6 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
struct vsp1_rwpf *rwpf = to_rwpf(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
- int ret = 0;
/*
* Cropping is only supported on the RPF and is implemented on the sink
@@ -234,13 +228,11 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
if (rwpf->entity.type == VSP1_ENTITY_WPF || sel->pad != RWPF_PAD_SINK)
return -EINVAL;
- mutex_lock(&rwpf->entity.lock);
+ guard(mutex)(&rwpf->entity.lock);
state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
@@ -256,13 +248,10 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
break;
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
-done:
- mutex_unlock(&rwpf->entity.lock);
- return ret;
+ return 0;
}
static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
@@ -275,7 +264,6 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
- int ret = 0;
/*
* Cropping is only supported on the RPF and is implemented on the sink
@@ -287,13 +275,11 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
if (sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
- mutex_lock(&rwpf->entity.lock);
+ guard(mutex)(&rwpf->entity.lock);
state = vsp1_entity_get_state(&rwpf->entity, sd_state, sel->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
/* Make sure the crop rectangle is entirely contained in the image. */
format = v4l2_subdev_state_get_format(state, RWPF_PAD_SINK);
@@ -342,9 +328,7 @@ static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
format->width = crop->width;
format->height = crop->height;
-done:
- mutex_unlock(&rwpf->entity.lock);
- return ret;
+ return 0;
}
static const struct v4l2_subdev_pad_ops vsp1_rwpf_pad_ops = {
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
index 94149da0c900..3fd9fde5c724 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
@@ -216,15 +216,12 @@ static int sru_set_format(struct v4l2_subdev *subdev,
struct vsp1_sru *sru = to_sru(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
- int ret = 0;
- mutex_lock(&sru->entity.lock);
+ guard(mutex)(&sru->entity.lock);
state = vsp1_entity_get_state(&sru->entity, sd_state, fmt->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
sru_try_format(sru, state, fmt->pad, &fmt->format);
@@ -239,9 +236,7 @@ static int sru_set_format(struct v4l2_subdev *subdev,
sru_try_format(sru, state, SRU_PAD_SOURCE, format);
}
-done:
- mutex_unlock(&sru->entity.lock);
- return ret;
+ return 0;
}
static const struct v4l2_subdev_pad_ops sru_pad_ops = {
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
index dd4722315c56..9f7bb112929e 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
@@ -199,15 +199,12 @@ static int uds_set_format(struct v4l2_subdev *subdev,
struct vsp1_uds *uds = to_uds(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
- int ret = 0;
- mutex_lock(&uds->entity.lock);
+ guard(mutex)(&uds->entity.lock);
state = vsp1_entity_get_state(&uds->entity, sd_state, fmt->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
uds_try_format(uds, state, fmt->pad, &fmt->format);
@@ -222,9 +219,7 @@ static int uds_set_format(struct v4l2_subdev *subdev,
uds_try_format(uds, state, UDS_PAD_SOURCE, format);
}
-done:
- mutex_unlock(&uds->entity.lock);
- return ret;
+ return 0;
}
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
index 3aefe5c9d421..52dbfe58a70d 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
@@ -60,18 +60,15 @@ static int uif_get_selection(struct v4l2_subdev *subdev,
struct vsp1_uif *uif = to_uif(subdev);
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
- int ret = 0;
if (sel->pad != UIF_PAD_SINK)
return -EINVAL;
- mutex_lock(&uif->entity.lock);
+ guard(mutex)(&uif->entity.lock);
state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
switch (sel->target) {
case V4L2_SEL_TGT_CROP_BOUNDS:
@@ -88,13 +85,10 @@ static int uif_get_selection(struct v4l2_subdev *subdev,
break;
default:
- ret = -EINVAL;
- break;
+ return -EINVAL;
}
-done:
- mutex_unlock(&uif->entity.lock);
- return ret;
+ return 0;
}
static int uif_set_selection(struct v4l2_subdev *subdev,
@@ -105,19 +99,16 @@ static int uif_set_selection(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *selection;
- int ret = 0;
if (sel->pad != UIF_PAD_SINK ||
sel->target != V4L2_SEL_TGT_CROP)
return -EINVAL;
- mutex_lock(&uif->entity.lock);
+ guard(mutex)(&uif->entity.lock);
state = vsp1_entity_get_state(&uif->entity, sd_state, sel->which);
- if (!state) {
- ret = -EINVAL;
- goto done;
- }
+ if (!state)
+ return -EINVAL;
/* The crop rectangle must be inside the input frame. */
format = v4l2_subdev_state_get_format(state, UIF_PAD_SINK);
@@ -133,9 +124,7 @@ static int uif_set_selection(struct v4l2_subdev *subdev,
selection = v4l2_subdev_state_get_crop(state, sel->pad);
*selection = sel->r;
-done:
- mutex_unlock(&uif->entity.lock);
- return ret;
+ return 0;
}
/* -----------------------------------------------------------------------------
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_video.c b/drivers/media/platform/renesas/vsp1/vsp1_video.c
index fe1dac11d4ae..5a1d284213ad 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_video.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_video.c
@@ -209,26 +209,21 @@ vsp1_video_complete_buffer(struct vsp1_video *video)
struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
struct vsp1_vb2_buffer *next = NULL;
struct vsp1_vb2_buffer *done;
- unsigned long flags;
unsigned int i;
- spin_lock_irqsave(&video->irqlock, flags);
-
- if (list_empty(&video->irqqueue)) {
- spin_unlock_irqrestore(&video->irqlock, flags);
- return NULL;
- }
-
- done = list_first_entry(&video->irqqueue,
- struct vsp1_vb2_buffer, queue);
-
- list_del(&done->queue);
+ scoped_guard(spinlock_irqsave, &video->irqlock) {
+ if (list_empty(&video->irqqueue))
+ return NULL;
- if (!list_empty(&video->irqqueue))
- next = list_first_entry(&video->irqqueue,
+ done = list_first_entry(&video->irqqueue,
struct vsp1_vb2_buffer, queue);
- spin_unlock_irqrestore(&video->irqlock, flags);
+ list_del(&done->queue);
+
+ if (!list_empty(&video->irqqueue))
+ next = list_first_entry(&video->irqqueue,
+ struct vsp1_vb2_buffer, queue);
+ }
done->buf.sequence = pipe->sequence;
done->buf.vb2_buf.timestamp = ktime_get_ns();
@@ -595,9 +590,9 @@ static void vsp1_video_pipeline_put(struct vsp1_pipeline *pipe)
{
struct media_device *mdev = &pipe->output->entity.vsp1->media_dev;
- mutex_lock(&mdev->graph_mutex);
+ guard(mutex)(&mdev->graph_mutex);
+
kref_put(&pipe->kref, vsp1_video_pipeline_release);
- mutex_unlock(&mdev->graph_mutex);
}
/* -----------------------------------------------------------------------------
@@ -661,18 +656,17 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue);
struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf);
- unsigned long flags;
bool empty;
- spin_lock_irqsave(&video->irqlock, flags);
- empty = list_empty(&video->irqqueue);
- list_add_tail(&buf->queue, &video->irqqueue);
- spin_unlock_irqrestore(&video->irqlock, flags);
+ scoped_guard(spinlock_irqsave, &video->irqlock) {
+ empty = list_empty(&video->irqqueue);
+ list_add_tail(&buf->queue, &video->irqqueue);
+ }
if (!empty)
return;
- spin_lock_irqsave(&pipe->irqlock, flags);
+ guard(spinlock_irqsave)(&pipe->irqlock);
video->rwpf->mem = buf->mem;
pipe->buffers_ready |= 1 << video->pipe_index;
@@ -680,8 +674,6 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb)
if (vb2_start_streaming_called(&video->queue) &&
vsp1_pipeline_ready(pipe))
vsp1_video_pipeline_run(pipe);
-
- spin_unlock_irqrestore(&pipe->irqlock, flags);
}
static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe)
@@ -783,14 +775,13 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe)
static void vsp1_video_release_buffers(struct vsp1_video *video)
{
struct vsp1_vb2_buffer *buffer;
- unsigned long flags;
/* Remove all buffers from the IRQ queue. */
- spin_lock_irqsave(&video->irqlock, flags);
+ guard(spinlock_irqsave)(&video->irqlock);
+
list_for_each_entry(buffer, &video->irqqueue, queue)
vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&video->irqqueue);
- spin_unlock_irqrestore(&video->irqlock, flags);
}
static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe)
@@ -812,25 +803,23 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
struct vsp1_video *video = vb2_get_drv_priv(vq);
struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
bool start_pipeline = false;
- unsigned long flags;
int ret;
- mutex_lock(&pipe->lock);
- if (pipe->stream_count == pipe->num_inputs) {
- ret = vsp1_video_setup_pipeline(pipe);
- if (ret < 0) {
- vsp1_video_release_buffers(video);
- vsp1_video_cleanup_pipeline(pipe);
- mutex_unlock(&pipe->lock);
- return ret;
+ scoped_guard(mutex, &pipe->lock) {
+ if (pipe->stream_count == pipe->num_inputs) {
+ ret = vsp1_video_setup_pipeline(pipe);
+ if (ret < 0) {
+ vsp1_video_release_buffers(video);
+ vsp1_video_cleanup_pipeline(pipe);
+ return ret;
+ }
+
+ start_pipeline = true;
}
- start_pipeline = true;
+ pipe->stream_count++;
}
- pipe->stream_count++;
- mutex_unlock(&pipe->lock);
-
/*
* vsp1_pipeline_ready() is not sufficient to establish that all streams
* are prepared and the pipeline is configured, as multiple streams
@@ -841,10 +830,10 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count)
if (!start_pipeline)
return 0;
- spin_lock_irqsave(&pipe->irqlock, flags);
+ guard(spinlock_irqsave)(&pipe->irqlock);
+
if (vsp1_pipeline_ready(pipe))
vsp1_video_pipeline_run(pipe);
- spin_unlock_irqrestore(&pipe->irqlock, flags);
return 0;
}
@@ -853,27 +842,27 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq)
{
struct vsp1_video *video = vb2_get_drv_priv(vq);
struct vsp1_pipeline *pipe = video->rwpf->entity.pipe;
- unsigned long flags;
int ret;
/*
* Clear the buffers ready flag to make sure the device won't be started
* by a QBUF on the video node on the other side of the pipeline.
*/
- spin_lock_irqsave(&video->irqlock, flags);
- pipe->buffers_ready &= ~(1 << video->pipe_index);
- spin_unlock_irqrestore(&video->irqlock, flags);
-
- mutex_lock(&pipe->lock);
- if (--pipe->stream_count == pipe->num_inputs) {
- /* Stop the pipeline. */
- ret = vsp1_pipeline_stop(pipe);
- if (ret == -ETIMEDOUT)
- dev_err(video->vsp1->dev, "pipeline stop timeout\n");
-
- vsp1_video_cleanup_pipeline(pipe);
+ scoped_guard(spinlock_irqsave, &video->irqlock) {
+ pipe->buffers_ready &= ~(1 << video->pipe_index);
+ }
+
+ scoped_guard(mutex, &pipe->lock) {
+ if (--pipe->stream_count == pipe->num_inputs) {
+ /* Stop the pipeline. */
+ ret = vsp1_pipeline_stop(pipe);
+ if (ret == -ETIMEDOUT)
+ dev_err(video->vsp1->dev,
+ "pipeline stop timeout\n");
+
+ vsp1_video_cleanup_pipeline(pipe);
+ }
}
- mutex_unlock(&pipe->lock);
video_device_pipeline_stop(&video->video);
vsp1_video_release_buffers(video);
@@ -938,9 +927,9 @@ vsp1_video_get_format(struct file *file, void *fh, struct v4l2_format *format)
if (format->type != video->queue.type)
return -EINVAL;
- mutex_lock(&video->lock);
+ guard(mutex)(&video->lock);
+
format->fmt.pix_mp = video->rwpf->format;
- mutex_unlock(&video->lock);
return 0;
}
@@ -972,19 +961,15 @@ vsp1_video_set_format(struct file *file, void *fh, struct v4l2_format *format)
if (ret < 0)
return ret;
- mutex_lock(&video->lock);
+ guard(mutex)(&video->lock);
- if (vb2_is_busy(&video->queue)) {
- ret = -EBUSY;
- goto done;
- }
+ if (vb2_is_busy(&video->queue))
+ return -EBUSY;
video->rwpf->format = format->fmt.pix_mp;
video->rwpf->fmtinfo = info;
-done:
- mutex_unlock(&video->lock);
- return ret;
+ return 0;
}
static int
@@ -1004,22 +989,16 @@ vsp1_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type)
* touching an entity in the pipeline can be activated or deactivated
* once streaming is started.
*/
- mutex_lock(&mdev->graph_mutex);
-
- pipe = vsp1_video_pipeline_get(video);
- if (IS_ERR(pipe)) {
- mutex_unlock(&mdev->graph_mutex);
- return PTR_ERR(pipe);
- }
+ scoped_guard(mutex, &mdev->graph_mutex) {
+ pipe = vsp1_video_pipeline_get(video);
+ if (IS_ERR(pipe))
+ return PTR_ERR(pipe);
- ret = __video_device_pipeline_start(&video->video, &pipe->pipe);
- if (ret < 0) {
- mutex_unlock(&mdev->graph_mutex);
- goto err_pipe;
+ ret = __video_device_pipeline_start(&video->video, &pipe->pipe);
+ if (ret < 0)
+ goto err_pipe;
}
- mutex_unlock(&mdev->graph_mutex);
-
/*
* Verify that the configured format matches the output of the connected
* subdev.
@@ -1137,7 +1116,6 @@ static const struct media_entity_operations vsp1_video_media_ops = {
void vsp1_video_suspend(struct vsp1_device *vsp1)
{
- unsigned long flags;
unsigned int i;
int ret;
@@ -1157,10 +1135,10 @@ void vsp1_video_suspend(struct vsp1_device *vsp1)
if (pipe == NULL)
continue;
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (pipe->state == VSP1_PIPELINE_RUNNING)
- pipe->state = VSP1_PIPELINE_STOPPING;
- spin_unlock_irqrestore(&pipe->irqlock, flags);
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ if (pipe->state == VSP1_PIPELINE_RUNNING)
+ pipe->state = VSP1_PIPELINE_STOPPING;
+ }
}
for (i = 0; i < vsp1->info->wpf_count; ++i) {
@@ -1184,7 +1162,6 @@ void vsp1_video_suspend(struct vsp1_device *vsp1)
void vsp1_video_resume(struct vsp1_device *vsp1)
{
- unsigned long flags;
unsigned int i;
/* Resume all running pipelines. */
@@ -1205,10 +1182,10 @@ void vsp1_video_resume(struct vsp1_device *vsp1)
*/
pipe->configured = false;
- spin_lock_irqsave(&pipe->irqlock, flags);
- if (vsp1_pipeline_ready(pipe))
- vsp1_video_pipeline_run(pipe);
- spin_unlock_irqrestore(&pipe->irqlock, flags);
+ scoped_guard(spinlock_irqsave, &pipe->irqlock) {
+ if (vsp1_pipeline_ready(pipe))
+ vsp1_video_pipeline_run(pipe);
+ }
}
}
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
index cd6c5592221b..0ec707d2913f 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_wpf.c
@@ -47,7 +47,6 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation)
struct v4l2_mbus_framefmt *sink_format;
struct v4l2_mbus_framefmt *source_format;
bool rotate;
- int ret = 0;
/*
* Only consider the 0°/180° from/to 90°/270° modifications, the rest
@@ -58,19 +57,17 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation)
return 0;
/* Changing rotation isn't allowed when buffers are allocated. */
- mutex_lock(&video->lock);
+ guard(mutex)(&video->lock);
- if (vb2_is_busy(&video->queue)) {
- ret = -EBUSY;
- goto done;
- }
+ if (vb2_is_busy(&video->queue))
+ return -EBUSY;
sink_format = v4l2_subdev_state_get_format(wpf->entity.state,
RWPF_PAD_SINK);
source_format = v4l2_subdev_state_get_format(wpf->entity.state,
RWPF_PAD_SOURCE);
- mutex_lock(&wpf->entity.lock);
+ guard(mutex)(&wpf->entity.lock);
if (rotate) {
source_format->width = sink_format->height;
@@ -82,11 +79,7 @@ static int vsp1_wpf_set_rotation(struct vsp1_rwpf *wpf, unsigned int rotation)
wpf->flip.rotate = rotate;
- mutex_unlock(&wpf->entity.lock);
-
-done:
- mutex_unlock(&video->lock);
- return ret;
+ return 0;
}
static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -118,9 +111,9 @@ static int vsp1_wpf_s_ctrl(struct v4l2_ctrl *ctrl)
if (rotation == 180 || rotation == 270)
flip ^= BIT(WPF_CTRL_VFLIP) | BIT(WPF_CTRL_HFLIP);
- spin_lock_irq(&wpf->flip.lock);
+ guard(spinlock_irq)(&wpf->flip.lock);
+
wpf->flip.pending = flip;
- spin_unlock_irq(&wpf->flip.lock);
return 0;
}
@@ -373,13 +366,12 @@ static void wpf_configure_frame(struct vsp1_entity *entity,
const unsigned int mask = BIT(WPF_CTRL_VFLIP)
| BIT(WPF_CTRL_HFLIP);
struct vsp1_rwpf *wpf = to_rwpf(&entity->subdev);
- unsigned long flags;
u32 outfmt;
- spin_lock_irqsave(&wpf->flip.lock, flags);
- wpf->flip.active = (wpf->flip.active & ~mask)
- | (wpf->flip.pending & mask);
- spin_unlock_irqrestore(&wpf->flip.lock, flags);
+ scoped_guard(spinlock_irqsave, &wpf->flip.lock) {
+ wpf->flip.active = (wpf->flip.active & ~mask)
+ | (wpf->flip.pending & mask);
+ }
outfmt = (wpf->alpha << VI6_WPF_OUTFMT_PDV_SHIFT) | wpf->outfmt;
diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig
index 727a0f6ea466..846e555829f3 100644
--- a/drivers/media/platform/rockchip/rga/Kconfig
+++ b/drivers/media/platform/rockchip/rga/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_ROCKCHIP_RGA
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_DMA_SG
select V4L2_MEM2MEM_DEV
help
diff --git a/drivers/media/platform/rockchip/rga/Makefile b/drivers/media/platform/rockchip/rga/Makefile
index 1bbecdc3d8df..7326a548f3dc 100644
--- a/drivers/media/platform/rockchip/rga/Makefile
+++ b/drivers/media/platform/rockchip/rga/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-rockchip-rga-objs := rga.o rga-hw.o rga-buf.o
+rockchip-rga-objs := rga.o rga-hw.o rga3-hw.o rga-buf.o
obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip-rga.o
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index bb575873f2b2..c0ea6003336b 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -12,9 +12,9 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>
-#include "rga-hw.h"
#include "rga.h"
static ssize_t fill_descriptors(struct rga_dma_desc *desc, size_t max_desc,
@@ -79,11 +79,18 @@ static int rga_buf_init(struct vb2_buffer *vb)
struct rockchip_rga *rga = ctx->rga;
struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type);
size_t n_desc = 0;
+ u32 size = 0;
+ u8 i;
if (IS_ERR(f))
return PTR_ERR(f);
- n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE);
+ if (!rga_has_internal_iommu(rga))
+ return 0;
+
+ for (i = 0; i < f->pix.num_planes; i++)
+ size += f->pix.plane_fmt[i].sizeimage;
+ n_desc = DIV_ROUND_UP(size, PAGE_SIZE);
rbuf->n_desc = n_desc;
rbuf->dma_desc = dma_alloc_coherent(rga->dev,
@@ -95,14 +102,19 @@ static int rga_buf_init(struct vb2_buffer *vb)
return 0;
}
-static int get_plane_offset(struct rga_frame *f, int plane)
+static int get_plane_offset(struct rga_frame *f,
+ const struct v4l2_format_info *info,
+ int plane)
{
+ u32 stride = f->pix.plane_fmt[0].bytesperline;
+
if (plane == 0)
return 0;
if (plane == 1)
- return f->width * f->height;
+ return stride * f->pix.height;
if (plane == 2)
- return f->width * f->height + (f->width * f->height / f->fmt->uv_factor);
+ return stride * f->pix.height +
+ (stride * f->pix.height / info->hdiv / info->vdiv);
return -EINVAL;
}
@@ -117,7 +129,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
size_t curr_desc = 0;
int i;
const struct v4l2_format_info *info;
- unsigned int offsets[VIDEO_MAX_PLANES];
+ dma_addr_t dma_addrs[VIDEO_MAX_PLANES];
if (IS_ERR(f))
return PTR_ERR(f);
@@ -132,27 +144,31 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
for (i = 0; i < vb->num_planes; i++) {
vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage);
- /* Create local MMU table for RGA */
- n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc],
- rbuf->n_desc - curr_desc,
- vb2_dma_sg_plane_desc(vb, i));
- if (n_desc < 0) {
- v4l2_err(&ctx->rga->v4l2_dev,
- "Failed to map video buffer to RGA\n");
- return n_desc;
+ if (rga_has_internal_iommu(ctx->rga)) {
+ /* Create local MMU table for RGA */
+ n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc],
+ rbuf->n_desc - curr_desc,
+ vb2_dma_sg_plane_desc(vb, i));
+ if (n_desc < 0) {
+ v4l2_err(&ctx->rga->v4l2_dev,
+ "Failed to map video buffer to RGA\n");
+ return n_desc;
+ }
+ dma_addrs[i] = curr_desc << PAGE_SHIFT;
+ curr_desc += n_desc;
+ } else {
+ dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb, i);
}
- offsets[i] = curr_desc << PAGE_SHIFT;
- curr_desc += n_desc;
}
/* Fill the remaining planes */
- info = v4l2_format_info(f->fmt->fourcc);
+ info = v4l2_format_info(f->pix.pixelformat);
for (i = info->mem_planes; i < info->comp_planes; i++)
- offsets[i] = get_plane_offset(f, i);
+ dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i);
- rbuf->offset.y_off = offsets[0];
- rbuf->offset.u_off = offsets[1];
- rbuf->offset.v_off = offsets[2];
+ rbuf->dma_addrs.y_addr = dma_addrs[0];
+ rbuf->dma_addrs.u_addr = dma_addrs[1];
+ rbuf->dma_addrs.v_addr = dma_addrs[2];
return 0;
}
@@ -172,6 +188,9 @@ static void rga_buf_cleanup(struct vb2_buffer *vb)
struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct rockchip_rga *rga = ctx->rga;
+ if (!rga_has_internal_iommu(rga))
+ return;
+
dma_free_coherent(rga->dev, rbuf->n_desc * sizeof(*rbuf->dma_desc),
rbuf->dma_desc, rbuf->dma_desc_pa);
}
@@ -193,6 +212,33 @@ static void rga_buf_return_buffers(struct vb2_queue *q,
}
}
+static int rga_buf_prepare_streaming(struct vb2_queue *q)
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(q);
+ const struct rga_hw *hw = ctx->rga->hw;
+ int ret;
+
+ /* It's safe to check the streaming state of the other queue,
+ * as the streamon ioctl's can't race due to the lock set in
+ * the queue_init function.
+ */
+ if ((V4L2_TYPE_IS_OUTPUT(q->type) &&
+ vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) ||
+ (V4L2_TYPE_IS_CAPTURE(q->type) &&
+ vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)))) {
+ /*
+ * As the other side is already streaming,
+ * check that the max scaling factor isn't exceeded.
+ */
+ ret = rga_check_scaling(hw, &ctx->in.crop, &ctx->out.crop,
+ ctx->rotate);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct rga_ctx *ctx = vb2_get_drv_priv(q);
@@ -228,6 +274,7 @@ const struct vb2_ops rga_qops = {
.buf_prepare = rga_buf_prepare,
.buf_queue = rga_buf_queue,
.buf_cleanup = rga_buf_cleanup,
+ .prepare_streaming = rga_buf_prepare_streaming,
.start_streaming = rga_buf_start_streaming,
.stop_streaming = rga_buf_stop_streaming,
};
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 43ed742a1649..be1bc8ddbd03 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -16,11 +16,11 @@ enum e_rga_start_pos {
RB = 3,
};
-struct rga_corners_addr_offset {
- struct rga_addr_offset left_top;
- struct rga_addr_offset right_top;
- struct rga_addr_offset left_bottom;
- struct rga_addr_offset right_bottom;
+struct rga_corners_addrs {
+ struct rga_addrs left_top;
+ struct rga_addrs right_top;
+ struct rga_addrs left_bottom;
+ struct rga_addrs right_bottom;
};
static unsigned int rga_get_scaling(unsigned int src, unsigned int dst)
@@ -36,48 +36,57 @@ static unsigned int rga_get_scaling(unsigned int src, unsigned int dst)
return (src > dst) ? ((dst << 16) / src) : ((src << 16) / dst);
}
-static struct rga_corners_addr_offset
-rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
- unsigned int x, unsigned int y, unsigned int w, unsigned int h)
+static struct rga_corners_addrs
+rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs,
+ unsigned int x, unsigned int y, unsigned int w, unsigned int h)
{
- struct rga_corners_addr_offset offsets;
- struct rga_addr_offset *lt, *lb, *rt, *rb;
+ struct rga_corners_addrs corner_addrs;
+ struct rga_addrs *lt, *lb, *rt, *rb;
+ const struct v4l2_format_info *format_info;
unsigned int x_div = 0,
- y_div = 0, uv_stride = 0, pixel_width = 0;
+ y_div = 0, y_stride = 0, uv_stride = 0, pixel_width = 0;
- lt = &offsets.left_top;
- lb = &offsets.left_bottom;
- rt = &offsets.right_top;
- rb = &offsets.right_bottom;
+ lt = &corner_addrs.left_top;
+ lb = &corner_addrs.left_bottom;
+ rt = &corner_addrs.right_top;
+ rb = &corner_addrs.right_bottom;
- x_div = frm->fmt->x_div;
- y_div = frm->fmt->y_div;
- uv_stride = frm->stride / x_div;
- pixel_width = frm->stride / frm->width;
-
- lt->y_off = offset->y_off + y * frm->stride + x * pixel_width;
- lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div;
- lt->v_off = offset->v_off + (y / y_div) * uv_stride + x / x_div;
-
- lb->y_off = lt->y_off + (h - 1) * frm->stride;
- lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride;
- lb->v_off = lt->v_off + (h / y_div - 1) * uv_stride;
-
- rt->y_off = lt->y_off + (w - 1) * pixel_width;
- rt->u_off = lt->u_off + w / x_div - 1;
- rt->v_off = lt->v_off + w / x_div - 1;
-
- rb->y_off = lb->y_off + (w - 1) * pixel_width;
- rb->u_off = lb->u_off + w / x_div - 1;
- rb->v_off = lb->v_off + w / x_div - 1;
-
- return offsets;
+ format_info = v4l2_format_info(frm->pix.pixelformat);
+ /* x_div is only used for the u/v planes.
+ * When the format doesn't have these, use 1 to avoid a division by zero.
+ */
+ if (format_info->bpp[1])
+ x_div = format_info->hdiv * format_info->bpp_div[1] /
+ format_info->bpp[1];
+ else
+ x_div = 1;
+ y_div = format_info->vdiv;
+ y_stride = frm->pix.plane_fmt[0].bytesperline;
+ uv_stride = y_stride / x_div;
+ pixel_width = y_stride / frm->pix.width;
+
+ lt->y_addr = addrs->y_addr + y * y_stride + x * pixel_width;
+ lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div;
+ lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div;
+
+ lb->y_addr = lt->y_addr + (h - 1) * y_stride;
+ lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride;
+ lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride;
+
+ rt->y_addr = lt->y_addr + (w - 1) * pixel_width;
+ rt->u_addr = lt->u_addr + w / x_div - 1;
+ rt->v_addr = lt->v_addr + w / x_div - 1;
+
+ rb->y_addr = lb->y_addr + (w - 1) * pixel_width;
+ rb->u_addr = lb->u_addr + w / x_div - 1;
+ rb->v_addr = lb->v_addr + w / x_div - 1;
+
+ return corner_addrs;
}
-static struct rga_addr_offset *rga_lookup_draw_pos(struct
- rga_corners_addr_offset
- * offsets, u32 rotate_mode,
- u32 mirr_mode)
+static struct rga_addrs *rga_lookup_draw_pos(struct rga_corners_addrs *corner_addrs,
+ u32 rotate_mode,
+ u32 mirr_mode)
{
static enum e_rga_start_pos rot_mir_point_matrix[4][4] = {
{
@@ -94,18 +103,18 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct
},
};
- if (!offsets)
+ if (!corner_addrs)
return NULL;
switch (rot_mir_point_matrix[rotate_mode][mirr_mode]) {
case LT:
- return &offsets->left_top;
+ return &corner_addrs->left_top;
case LB:
- return &offsets->left_bottom;
+ return &corner_addrs->left_bottom;
case RT:
- return &offsets->right_top;
+ return &corner_addrs->right_top;
case RB:
- return &offsets->right_bottom;
+ return &corner_addrs->right_bottom;
}
return NULL;
@@ -113,8 +122,7 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct
static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int reg;
reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG;
@@ -126,8 +134,7 @@ static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int reg;
reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG;
@@ -139,8 +146,7 @@ static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int reg;
reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG;
@@ -153,7 +159,7 @@ static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
{
struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int scale_dst_w, scale_dst_h;
unsigned int src_h, src_w, dst_h, dst_w;
union rga_src_info src_info;
@@ -164,6 +170,9 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
union rga_src_act_info src_act_info;
union rga_dst_vir_info dst_vir_info;
union rga_dst_act_info dst_act_info;
+ u32 in_stride, out_stride;
+ struct rga_fmt *in_fmt = ctx->in.fmt;
+ struct rga_fmt *out_fmt = ctx->out.fmt;
src_h = ctx->in.crop.height;
src_w = ctx->in.crop.width;
@@ -179,19 +188,19 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
dst_vir_info.val = dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2];
dst_act_info.val = dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2];
- src_info.data.format = ctx->in.fmt->hw_format;
- src_info.data.swap = ctx->in.fmt->color_swap;
- dst_info.data.format = ctx->out.fmt->hw_format;
- dst_info.data.swap = ctx->out.fmt->color_swap;
+ src_info.data.format = in_fmt->hw_format;
+ src_info.data.swap = in_fmt->color_swap;
+ dst_info.data.format = out_fmt->hw_format;
+ dst_info.data.swap = out_fmt->color_swap;
/*
* CSC mode must only be set when the colorspace families differ between
* input and output. It must remain unset (zeroed) if both are the same.
*/
- if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) &&
- RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) {
- switch (ctx->in.colorspace) {
+ if (RGA_COLOR_FMT_IS_YUV(in_fmt->hw_format) &&
+ RGA_COLOR_FMT_IS_RGB(out_fmt->hw_format)) {
+ switch (ctx->in.pix.colorspace) {
case V4L2_COLORSPACE_REC709:
src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
break;
@@ -201,9 +210,9 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
}
}
- if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) &&
- RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) {
- switch (ctx->out.colorspace) {
+ if (RGA_COLOR_FMT_IS_RGB(in_fmt->hw_format) &&
+ RGA_COLOR_FMT_IS_YUV(out_fmt->hw_format)) {
+ switch (ctx->out.pix.colorspace) {
case V4L2_COLORSPACE_REC709:
dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
break;
@@ -286,13 +295,15 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
* Calculate the framebuffer virtual strides and active size,
* note that the step of vir_stride / vir_width is 4 byte words
*/
- src_vir_info.data.vir_stride = ctx->in.stride >> 2;
- src_vir_info.data.vir_width = ctx->in.stride >> 2;
+ in_stride = ctx->in.pix.plane_fmt[0].bytesperline;
+ src_vir_info.data.vir_stride = in_stride >> 2;
+ src_vir_info.data.vir_width = in_stride >> 2;
src_act_info.data.act_height = src_h - 1;
src_act_info.data.act_width = src_w - 1;
- dst_vir_info.data.vir_stride = ctx->out.stride >> 2;
+ out_stride = ctx->out.pix.plane_fmt[0].bytesperline;
+ dst_vir_info.data.vir_stride = out_stride >> 2;
dst_act_info.data.act_height = dst_h - 1;
dst_act_info.data.act_width = dst_w - 1;
@@ -310,11 +321,10 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
}
static void rga_cmd_set_src_info(struct rga_ctx *ctx,
- struct rga_addr_offset *offset)
+ struct rga_addrs *addrs)
{
- struct rga_corners_addr_offset src_offsets;
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ struct rga_corners_addrs src_corner_addrs;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int src_h, src_w, src_x, src_y;
src_h = ctx->in.crop.height;
@@ -325,24 +335,23 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx,
/*
* Calculate the source framebuffer base address with offset pixel.
*/
- src_offsets = rga_get_addr_offset(&ctx->in, offset,
- src_x, src_y, src_w, src_h);
+ src_corner_addrs = rga_get_corner_addrs(&ctx->in, addrs,
+ src_x, src_y, src_w, src_h);
dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- src_offsets.left_top.y_off;
+ src_corner_addrs.left_top.y_addr;
dest[(RGA_SRC_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- src_offsets.left_top.u_off;
+ src_corner_addrs.left_top.u_addr;
dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- src_offsets.left_top.v_off;
+ src_corner_addrs.left_top.v_addr;
}
static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
- struct rga_addr_offset *offset)
+ struct rga_addrs *addrs)
{
- struct rga_addr_offset *dst_offset;
- struct rga_corners_addr_offset offsets;
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ struct rga_addrs *dst_addrs;
+ struct rga_corners_addrs corner_addrs;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int dst_h, dst_w, dst_x, dst_y;
unsigned int mir_mode = 0;
unsigned int rot_mode = 0;
@@ -375,21 +384,20 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
/*
* Configure the dest framebuffer base address with pixel offset.
*/
- offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h);
- dst_offset = rga_lookup_draw_pos(&offsets, rot_mode, mir_mode);
+ corner_addrs = rga_get_corner_addrs(&ctx->out, addrs, dst_x, dst_y, dst_w, dst_h);
+ dst_addrs = rga_lookup_draw_pos(&corner_addrs, rot_mode, mir_mode);
dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- dst_offset->y_off;
+ dst_addrs->y_addr;
dest[(RGA_DST_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- dst_offset->u_off;
+ dst_addrs->u_addr;
dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- dst_offset->v_off;
+ dst_addrs->v_addr;
}
static void rga_cmd_set_mode(struct rga_ctx *ctx)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
union rga_mode_ctrl mode;
union rga_alpha_ctrl0 alpha_ctrl0;
union rga_alpha_ctrl1 alpha_ctrl1;
@@ -414,8 +422,6 @@ static void rga_cmd_set(struct rga_ctx *ctx,
{
struct rockchip_rga *rga = ctx->rga;
- memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4);
-
rga_cmd_set_src_addr(ctx, src->dma_desc_pa);
/*
* Due to hardware bug,
@@ -424,21 +430,27 @@ static void rga_cmd_set(struct rga_ctx *ctx,
rga_cmd_set_src1_addr(ctx, dst->dma_desc_pa);
rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa);
- rga_cmd_set_mode(ctx);
- rga_cmd_set_src_info(ctx, &src->offset);
- rga_cmd_set_dst_info(ctx, &dst->offset);
- rga_cmd_set_trans_info(ctx);
+ rga_cmd_set_src_info(ctx, &src->dma_addrs);
+ rga_cmd_set_dst_info(ctx, &dst->dma_addrs);
- rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy);
+ rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy);
/* sync CMD buf for RGA */
- dma_sync_single_for_device(rga->dev, rga->cmdbuf_phy,
- PAGE_SIZE, DMA_BIDIRECTIONAL);
+ dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
}
-void rga_hw_start(struct rockchip_rga *rga,
- struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
+static void rga_hw_setup_cmdbuf(struct rga_ctx *ctx)
+{
+ memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
+
+ rga_cmd_set_mode(ctx);
+ rga_cmd_set_trans_info(ctx);
+}
+
+static void rga_hw_start(struct rockchip_rga *rga,
+ struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
{
struct rga_ctx *ctx = rga->curr;
@@ -452,3 +464,152 @@ void rga_hw_start(struct rockchip_rga *rga,
rga_write(rga, RGA_CMD_CTRL, 0x1);
}
+
+static bool rga_handle_irq(struct rockchip_rga *rga)
+{
+ int intr;
+
+ intr = rga_read(rga, RGA_INT) & 0xf;
+
+ rga_mod(rga, RGA_INT, intr << 4, 0xf << 4);
+
+ return intr & RGA_INT_COMMAND_FINISHED;
+}
+
+static void rga_get_version(struct rockchip_rga *rga)
+{
+ rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF;
+ rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F;
+}
+
+static struct rga_fmt formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .color_swap = RGA_COLOR_ALPHA_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_XBGR8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_RGB888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_RGB888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB444,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR4444,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB555,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR1555,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_BGR565,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422SP,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422SP,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420P,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422P,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420P,
+ },
+};
+
+static void *rga_adjust_and_map_format(struct rga_ctx *ctx,
+ struct v4l2_pix_format_mplane *format,
+ bool is_output)
+{
+ unsigned int i;
+
+ if (!format)
+ return &formats[0];
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].fourcc == format->pixelformat)
+ return &formats[i];
+ }
+
+ format->pixelformat = formats[0].fourcc;
+ return &formats[0];
+}
+
+static int rga_enum_format(struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].fourcc;
+ return 0;
+}
+
+const struct rga_hw rga2_hw = {
+ .card_type = "rga2",
+ .has_internal_iommu = true,
+ .cmdbuf_size = RGA_CMDBUF_SIZE,
+ .min_width = MIN_WIDTH,
+ .max_width = MAX_WIDTH,
+ .min_height = MIN_HEIGHT,
+ .max_height = MAX_HEIGHT,
+ .max_scaling_factor = MAX_SCALING_FACTOR,
+ .stride_alignment = 4,
+ .features = RGA_FEATURE_FLIP
+ | RGA_FEATURE_ROTATE
+ | RGA_FEATURE_BG_COLOR,
+
+ .setup_cmdbuf = rga_hw_setup_cmdbuf,
+ .start = rga_hw_start,
+ .handle_irq = rga_handle_irq,
+ .get_version = rga_get_version,
+ .adjust_and_map_format = rga_adjust_and_map_format,
+ .enum_format = rga_enum_format,
+};
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
index cc6bd7f5b030..14ffa5ebd453 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.h
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -6,7 +6,9 @@
#ifndef __RGA_HW_H__
#define __RGA_HW_H__
-#define RGA_CMDBUF_SIZE 0x20
+#include <linux/types.h>
+
+#define RGA_CMDBUF_SIZE 0x80
/* Hardware limits */
#define MAX_WIDTH 8192
@@ -14,9 +16,7 @@
#define MIN_WIDTH 34
#define MIN_HEIGHT 34
-
-#define DEFAULT_WIDTH 100
-#define DEFAULT_HEIGHT 100
+#define MAX_SCALING_FACTOR 16
#define RGA_TIMEOUT 500
@@ -178,6 +178,8 @@
#define RGA_ALPHA_COLOR_NORMAL 0
#define RGA_ALPHA_COLOR_MULTIPLY_CAL 1
+#define RGA_INT_COMMAND_FINISHED 4
+
/* Registers union */
union rga_mode_ctrl {
unsigned int val;
@@ -431,4 +433,10 @@ union rga_pat_con {
} data;
};
+struct rga_fmt {
+ u32 fourcc;
+ u8 color_swap;
+ u8 hw_format;
+};
+
#endif
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index fea63b94c5f3..b3cb6bf8eb86 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -23,9 +23,9 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>
-#include "rga-hw.h"
#include "rga.h"
static int debug;
@@ -39,6 +39,11 @@ static void device_run(void *prv)
unsigned long flags;
spin_lock_irqsave(&rga->ctrl_lock, flags);
+ if (ctx->cmdbuf_dirty) {
+ ctx->cmdbuf_dirty = false;
+ rga->hw->setup_cmdbuf(ctx);
+ }
+ spin_unlock_irqrestore(&rga->ctrl_lock, flags);
rga->curr = ctx;
@@ -47,21 +52,14 @@ static void device_run(void *prv)
dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst));
-
- spin_unlock_irqrestore(&rga->ctrl_lock, flags);
+ rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst));
}
static irqreturn_t rga_isr(int irq, void *prv)
{
struct rockchip_rga *rga = prv;
- int intr;
-
- intr = rga_read(rga, RGA_INT) & 0xf;
- rga_mod(rga, RGA_INT, intr << 4, 0xf << 4);
-
- if (intr & 0x04) {
+ if (rga->hw->handle_irq(rga)) {
struct vb2_v4l2_buffer *src, *dst;
struct rga_ctx *ctx = rga->curr;
@@ -101,7 +99,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->ops = &rga_qops;
- src_vq->mem_ops = &vb2_dma_sg_memops;
+ if (rga_has_internal_iommu(ctx->rga))
+ src_vq->mem_ops = &vb2_dma_sg_memops;
+ else
+ src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->gfp_flags = __GFP_DMA32;
src_vq->buf_struct_size = sizeof(struct rga_vb_buffer);
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -116,7 +117,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->ops = &rga_qops;
- dst_vq->mem_ops = &vb2_dma_sg_memops;
+ if (rga_has_internal_iommu(ctx->rga))
+ dst_vq->mem_ops = &vb2_dma_sg_memops;
+ else
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->gfp_flags = __GFP_DMA32;
dst_vq->buf_struct_size = sizeof(struct rga_vb_buffer);
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -130,7 +134,9 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct rga_ctx *ctx = container_of(ctrl->handler, struct rga_ctx,
ctrl_handler);
+ const struct rga_hw *hw = ctx->rga->hw;
unsigned long flags;
+ int ret = 0;
spin_lock_irqsave(&ctx->rga->ctrl_lock, flags);
switch (ctrl->id) {
@@ -141,14 +147,24 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
ctx->vflip = ctrl->val;
break;
case V4L2_CID_ROTATE:
+ if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) &&
+ vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) {
+ ret = rga_check_scaling(hw, &ctx->in.crop,
+ &ctx->out.crop, ctrl->val);
+ if (ret < 0)
+ goto s_ctrl_done;
+ }
ctx->rotate = ctrl->val;
break;
case V4L2_CID_BG_COLOR:
ctx->fill_color = ctrl->val;
break;
}
+ ctx->cmdbuf_dirty = true;
+
+s_ctrl_done:
spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags);
- return 0;
+ return ret;
}
static const struct v4l2_ctrl_ops rga_ctrl_ops = {
@@ -161,17 +177,21 @@ static int rga_setup_ctrls(struct rga_ctx *ctx)
v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (rga->hw->features & RGA_FEATURE_FLIP) {
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ }
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_ROTATE, 0, 270, 90, 0);
+ if (rga->hw->features & RGA_FEATURE_ROTATE)
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_ROTATE, 0, 270, 90, 0);
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0);
+ if (rga->hw->features & RGA_FEATURE_BG_COLOR)
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0);
if (ctx->ctrl_handler.error) {
int err = ctx->ctrl_handler.error;
@@ -184,176 +204,37 @@ static int rga_setup_ctrls(struct rga_ctx *ctx)
return 0;
}
-static struct rga_fmt formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_ARGB32,
- .color_swap = RGA_COLOR_ALPHA_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR8888,
- .depth = 32,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ABGR32,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR8888,
- .depth = 32,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_XBGR32,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_XBGR8888,
- .depth = 32,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB24,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_RGB888,
- .depth = 24,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGR24,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_RGB888,
- .depth = 24,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB444,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR4444,
- .depth = 16,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB555,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR1555,
- .depth = 16,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB565,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_BGR565,
- .depth = 16,
- .uv_factor = 1,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV21,
- .color_swap = RGA_COLOR_UV_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
- .uv_factor = 4,
- .y_div = 2,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV61,
- .color_swap = RGA_COLOR_UV_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV422SP,
- .depth = 16,
- .uv_factor = 2,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV12,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
- .uv_factor = 4,
- .y_div = 2,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV12M,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
- .uv_factor = 4,
- .y_div = 2,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV16,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV422SP,
- .depth = 16,
- .uv_factor = 2,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV420,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420P,
- .depth = 12,
- .uv_factor = 4,
- .y_div = 2,
- .x_div = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV422P,
- .depth = 16,
- .uv_factor = 2,
- .y_div = 1,
- .x_div = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVU420,
- .color_swap = RGA_COLOR_UV_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420P,
- .depth = 12,
- .uv_factor = 4,
- .y_div = 2,
- .x_div = 2,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
+static bool check_scaling_factor(const struct rga_hw *hw, u32 src_size,
+ u32 dst_size)
+{
+ if (src_size < dst_size)
+ return src_size * hw->max_scaling_factor >= dst_size;
+ else
+ return dst_size * hw->max_scaling_factor >= src_size;
+}
-static struct rga_fmt *rga_fmt_find(u32 pixelformat)
+int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in,
+ const struct v4l2_rect *crop_out, u32 rotate)
{
- unsigned int i;
+ u32 scaled_width;
+ u32 scaled_height;
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].fourcc == pixelformat)
- return &formats[i];
+ if (rotate == 90 || rotate == 270) {
+ scaled_width = crop_out->height;
+ scaled_height = crop_out->width;
+ } else {
+ scaled_width = crop_out->width;
+ scaled_height = crop_out->height;
}
- return NULL;
-}
-static struct rga_frame def_frame = {
- .width = DEFAULT_WIDTH,
- .height = DEFAULT_HEIGHT,
- .colorspace = V4L2_COLORSPACE_DEFAULT,
- .crop.left = 0,
- .crop.top = 0,
- .crop.width = DEFAULT_WIDTH,
- .crop.height = DEFAULT_HEIGHT,
- .fmt = &formats[0],
-};
+ if (!check_scaling_factor(hw, crop_in->width, scaled_width))
+ return -EINVAL;
+
+ if (!check_scaling_factor(hw, crop_in->height, scaled_height))
+ return -EINVAL;
+
+ return 0;
+}
struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type)
{
@@ -369,30 +250,50 @@ static int rga_open(struct file *file)
struct rockchip_rga *rga = video_drvdata(file);
struct rga_ctx *ctx = NULL;
int ret = 0;
+ u32 def_width = clamp(DEFAULT_WIDTH, rga->hw->min_width, rga->hw->max_width);
+ u32 def_height = clamp(DEFAULT_HEIGHT, rga->hw->min_height, rga->hw->max_height);
+ struct rga_frame def_frame = {
+ .crop.left = 0,
+ .crop.top = 0,
+ .crop.width = def_width,
+ .crop.height = def_height,
+ };
ctx = kzalloc_obj(*ctx);
if (!ctx)
return -ENOMEM;
+
+ /* Create CMD buffer */
+ ctx->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size,
+ &ctx->cmdbuf_phy, GFP_KERNEL,
+ DMA_ATTR_WRITE_COMBINE);
+ if (!ctx->cmdbuf_virt) {
+ ret = -ENOMEM;
+ goto rel_ctx;
+ }
+ ctx->cmdbuf_dirty = true;
+
ctx->rga = rga;
/* Set default formats */
ctx->in = def_frame;
ctx->out = def_frame;
- v4l2_fill_pixfmt_mp(&ctx->in.pix,
- ctx->in.fmt->fourcc, ctx->out.width, ctx->out.height);
- v4l2_fill_pixfmt_mp(&ctx->out.pix,
- ctx->out.fmt->fourcc, ctx->out.width, ctx->out.height);
+ ctx->in.fmt = rga->hw->adjust_and_map_format(ctx, &ctx->in.pix, true);
+ v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.pix.pixelformat,
+ def_width, def_height, rga->hw->stride_alignment);
+ ctx->out.fmt =
+ rga->hw->adjust_and_map_format(ctx, &ctx->out.pix, false);
+ v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.pix.pixelformat,
+ def_width, def_height, rga->hw->stride_alignment);
if (mutex_lock_interruptible(&rga->mutex)) {
- kfree(ctx);
- return -ERESTARTSYS;
+ ret = -ERESTARTSYS;
+ goto rel_cmdbuf;
}
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init);
if (IS_ERR(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx);
- mutex_unlock(&rga->mutex);
- kfree(ctx);
- return ret;
+ goto unlock_mutex;
}
v4l2_fh_init(&ctx->fh, video_devdata(file));
v4l2_fh_add(&ctx->fh, file);
@@ -406,6 +307,15 @@ static int rga_open(struct file *file)
mutex_unlock(&rga->mutex);
return 0;
+
+unlock_mutex:
+ mutex_unlock(&rga->mutex);
+rel_cmdbuf:
+ dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt,
+ ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
+rel_ctx:
+ kfree(ctx);
+ return ret;
}
static int rga_release(struct file *file)
@@ -420,6 +330,10 @@ static int rga_release(struct file *file)
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
v4l2_fh_del(&ctx->fh, file);
v4l2_fh_exit(&ctx->fh);
+
+ dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt,
+ ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
+
kfree(ctx);
mutex_unlock(&rga->mutex);
@@ -439,8 +353,10 @@ static const struct v4l2_file_operations rga_fops = {
static int
vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
{
+ struct rockchip_rga *rga = video_drvdata(file);
+
strscpy(cap->driver, RGA_NAME, sizeof(cap->driver));
- strscpy(cap->card, "rockchip-rga", sizeof(cap->card));
+ strscpy(cap->card, rga->hw->card_type, sizeof(cap->card));
strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info));
return 0;
@@ -448,13 +364,21 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
{
- struct rga_fmt *fmt;
+ struct rockchip_rga *rga = video_drvdata(file);
+ int ret;
- if (f->index >= NUM_FORMATS)
- return -EINVAL;
+ ret = rga->hw->enum_format(f);
+ if (ret != 0)
+ return ret;
- fmt = &formats[f->index];
- f->pixelformat = fmt->fourcc;
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return 0;
+
+ /* allow changing the quantization and xfer func for YUV formats */
+ if (v4l2_is_format_yuv(v4l2_format_info(f->pixelformat)))
+ f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION |
+ V4L2_FMT_FLAG_CSC_YCBCR_ENC;
return 0;
}
@@ -469,10 +393,8 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
if (IS_ERR(frm))
return PTR_ERR(frm);
- v4l2_fill_pixfmt_mp(pix_fmt, frm->fmt->fourcc, frm->width, frm->height);
-
+ *pix_fmt = frm->pix;
pix_fmt->field = V4L2_FIELD_NONE;
- pix_fmt->colorspace = frm->colorspace;
return 0;
}
@@ -480,18 +402,43 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp;
- struct rga_fmt *fmt;
+ struct rga_ctx *ctx = file_to_rga_ctx(file);
+ const struct rga_hw *hw = ctx->rga->hw;
+ struct v4l2_frmsize_stepwise frmsize = {
+ .min_width = hw->min_width,
+ .max_width = hw->max_width,
+ .min_height = hw->min_height,
+ .max_height = hw->max_height,
+ .step_width = 1,
+ .step_height = 1,
+ };
+
+ if (v4l2_is_format_yuv(v4l2_format_info(pix_fmt->pixelformat))) {
+ frmsize.step_width = 2;
+ frmsize.step_height = 2;
+ }
+
+ if (V4L2_TYPE_IS_CAPTURE(f->type)) {
+ const struct rga_frame *frm;
- fmt = rga_fmt_find(pix_fmt->pixelformat);
- if (!fmt)
- fmt = &formats[0];
+ frm = rga_get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
- pix_fmt->width = clamp(pix_fmt->width,
- (u32)MIN_WIDTH, (u32)MAX_WIDTH);
- pix_fmt->height = clamp(pix_fmt->height,
- (u32)MIN_HEIGHT, (u32)MAX_HEIGHT);
+ if (!(pix_fmt->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
+ pix_fmt->quantization = frm->pix.quantization;
+ pix_fmt->ycbcr_enc = frm->pix.ycbcr_enc;
+ }
+ /* disallow values not announced in vidioc_enum_fmt */
+ pix_fmt->colorspace = frm->pix.colorspace;
+ pix_fmt->xfer_func = frm->pix.xfer_func;
+ }
+
+ hw->adjust_and_map_format(ctx, pix_fmt, V4L2_TYPE_IS_OUTPUT(f->type));
- v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height);
+ v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize);
+ v4l2_fill_pixfmt_mp_aligned(pix_fmt, pix_fmt->pixelformat,
+ pix_fmt->width, pix_fmt->height, hw->stride_alignment);
pix_fmt->field = V4L2_FIELD_NONE;
return 0;
@@ -521,28 +468,34 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frm = rga_get_frame(ctx, f->type);
if (IS_ERR(frm))
return PTR_ERR(frm);
- frm->width = pix_fmt->width;
- frm->height = pix_fmt->height;
- frm->size = 0;
- for (i = 0; i < pix_fmt->num_planes; i++)
- frm->size += pix_fmt->plane_fmt[i].sizeimage;
- frm->fmt = rga_fmt_find(pix_fmt->pixelformat);
- frm->stride = pix_fmt->plane_fmt[0].bytesperline;
- frm->colorspace = pix_fmt->colorspace;
+ frm->fmt = rga->hw->adjust_and_map_format(ctx, pix_fmt,
+ V4L2_TYPE_IS_OUTPUT(f->type));
+
+ /*
+ * Copy colorimetry from output to capture as required by the
+ * v4l2-compliance tests
+ */
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ ctx->out.pix.colorspace = pix_fmt->colorspace;
+ ctx->out.pix.ycbcr_enc = pix_fmt->ycbcr_enc;
+ ctx->out.pix.quantization = pix_fmt->quantization;
+ ctx->out.pix.xfer_func = pix_fmt->xfer_func;
+ }
/* Reset crop settings */
frm->crop.left = 0;
frm->crop.top = 0;
- frm->crop.width = frm->width;
- frm->crop.height = frm->height;
+ frm->crop.width = pix_fmt->width;
+ frm->crop.height = pix_fmt->height;
frm->pix = *pix_fmt;
+ ctx->cmdbuf_dirty = true;
v4l2_dbg(debug, 1, &rga->v4l2_dev,
- "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n",
+ "[%s] fmt - %p4cc %dx%d (stride %d)\n",
V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE",
- &frm->fmt->fourcc, frm->width, frm->height,
- frm->stride, frm->size);
+ &pix_fmt->pixelformat, pix_fmt->width, pix_fmt->height,
+ pix_fmt->plane_fmt[0].bytesperline);
for (i = 0; i < pix_fmt->num_planes; i++) {
v4l2_dbg(debug, 1, &rga->v4l2_dev,
@@ -595,8 +548,8 @@ static int vidioc_g_selection(struct file *file, void *priv,
} else {
s->r.left = 0;
s->r.top = 0;
- s->r.width = f->width;
- s->r.height = f->height;
+ s->r.width = f->pix.width;
+ s->r.height = f->pix.height;
}
return 0;
@@ -608,7 +561,6 @@ static int vidioc_s_selection(struct file *file, void *priv,
struct rga_ctx *ctx = file_to_rga_ctx(file);
struct rockchip_rga *rga = ctx->rga;
struct rga_frame *f;
- int ret = 0;
f = rga_get_frame(ctx, s->type);
if (IS_ERR(f))
@@ -645,16 +597,32 @@ static int vidioc_s_selection(struct file *file, void *priv,
return -EINVAL;
}
- if (s->r.left + s->r.width > f->width ||
- s->r.top + s->r.height > f->height ||
- s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) {
+ if (s->r.left + s->r.width > f->pix.width ||
+ s->r.top + s->r.height > f->pix.height ||
+ s->r.width < rga->hw->min_width || s->r.height < rga->hw->min_height) {
v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n");
return -EINVAL;
}
+ if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) &&
+ vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) {
+ int ret = 0;
+
+ if (V4L2_TYPE_IS_OUTPUT(s->type))
+ ret = rga_check_scaling(rga->hw, &s->r, &ctx->out.crop,
+ ctx->rotate);
+ else
+ ret = rga_check_scaling(rga->hw, &ctx->in.crop, &s->r,
+ ctx->rotate);
+
+ if (ret < 0)
+ return ret;
+ }
+
f->crop = s->r;
+ ctx->cmdbuf_dirty = true;
- return ret;
+ return 0;
}
static const struct v4l2_ioctl_ops rga_ioctl_ops = {
@@ -698,48 +666,10 @@ static const struct video_device rga_videodev = {
.device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING,
};
-static int rga_enable_clocks(struct rockchip_rga *rga)
-{
- int ret;
-
- ret = clk_prepare_enable(rga->sclk);
- if (ret) {
- dev_err(rga->dev, "Cannot enable rga sclk: %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(rga->aclk);
- if (ret) {
- dev_err(rga->dev, "Cannot enable rga aclk: %d\n", ret);
- goto err_disable_sclk;
- }
-
- ret = clk_prepare_enable(rga->hclk);
- if (ret) {
- dev_err(rga->dev, "Cannot enable rga hclk: %d\n", ret);
- goto err_disable_aclk;
- }
-
- return 0;
-
-err_disable_aclk:
- clk_disable_unprepare(rga->aclk);
-err_disable_sclk:
- clk_disable_unprepare(rga->sclk);
-
- return ret;
-}
-
-static void rga_disable_clocks(struct rockchip_rga *rga)
-{
- clk_disable_unprepare(rga->sclk);
- clk_disable_unprepare(rga->hclk);
- clk_disable_unprepare(rga->aclk);
-}
-
static int rga_parse_dt(struct rockchip_rga *rga)
{
struct reset_control *core_rst, *axi_rst, *ahb_rst;
+ int ret;
core_rst = devm_reset_control_get(rga->dev, "core");
if (IS_ERR(core_rst)) {
@@ -771,22 +701,54 @@ static int rga_parse_dt(struct rockchip_rga *rga)
udelay(1);
reset_control_deassert(ahb_rst);
- rga->sclk = devm_clk_get(rga->dev, "sclk");
- if (IS_ERR(rga->sclk)) {
- dev_err(rga->dev, "failed to get sclk clock\n");
- return PTR_ERR(rga->sclk);
+ ret = devm_clk_bulk_get_all(rga->dev, &rga->clks);
+ if (ret < 0) {
+ dev_err(rga->dev, "failed to get clocks\n");
+ return ret;
}
+ rga->num_clks = ret;
- rga->aclk = devm_clk_get(rga->dev, "aclk");
- if (IS_ERR(rga->aclk)) {
- dev_err(rga->dev, "failed to get aclk clock\n");
- return PTR_ERR(rga->aclk);
- }
+ return 0;
+}
+
+/*
+ * Some SoCs, like RK3588 have multiple identical RGA3 cores, but the
+ * kernel is currently missing support for multi-core handling. Exposing
+ * separate devices for each core to userspace is bad, since that does
+ * not allow scheduling tasks properly (and creates ABI). With this workaround
+ * the driver will only probe for the first core and early exit for the other
+ * cores. Once the driver gains multi-core support, the same technique
+ * for detecting the main core can be used to cluster all cores together.
+ */
+static int rga_disable_multicore(struct device *dev)
+{
+ struct device_node *node = NULL;
+ const char *compatible;
+ bool is_main_core;
+ int ret;
+
+ /* Intentionally ignores the fallback strings */
+ ret = of_property_read_string(dev->of_node, "compatible", &compatible);
+ if (ret)
+ return ret;
+
+ /* The first compatible and available node found is considered the main core */
+ do {
+ node = of_find_compatible_node(node, NULL, compatible);
+ if (of_device_is_available(node))
+ break;
+ } while (node);
+
+ if (!node)
+ return -EINVAL;
- rga->hclk = devm_clk_get(rga->dev, "hclk");
- if (IS_ERR(rga->hclk)) {
- dev_err(rga->dev, "failed to get hclk clock\n");
- return PTR_ERR(rga->hclk);
+ is_main_core = (dev->of_node == node);
+
+ of_node_put(node);
+
+ if (!is_main_core) {
+ dev_info(dev, "missing multi-core support, ignoring this instance\n");
+ return -ENODEV;
}
return 0;
@@ -802,10 +764,18 @@ static int rga_probe(struct platform_device *pdev)
if (!pdev->dev.of_node)
return -ENODEV;
+ ret = rga_disable_multicore(&pdev->dev);
+ if (ret)
+ return ret;
+
rga = devm_kzalloc(&pdev->dev, sizeof(*rga), GFP_KERNEL);
if (!rga)
return -ENOMEM;
+ rga->hw = of_device_get_match_data(&pdev->dev);
+ if (!rga->hw)
+ return dev_err_probe(&pdev->dev, -ENODEV, "failed to get match data\n");
+
rga->dev = &pdev->dev;
spin_lock_init(&rga->ctrl_lock);
mutex_init(&rga->mutex);
@@ -828,7 +798,8 @@ static int rga_probe(struct platform_device *pdev)
goto err_put_clk;
}
- ret = devm_request_irq(rga->dev, irq, rga_isr, 0,
+ ret = devm_request_irq(rga->dev, irq, rga_isr,
+ rga_has_internal_iommu(rga) ? 0 : IRQF_SHARED,
dev_name(rga->dev), rga);
if (ret < 0) {
dev_err(rga->dev, "failed to request irq\n");
@@ -869,30 +840,17 @@ static int rga_probe(struct platform_device *pdev)
if (ret < 0)
goto rel_m2m;
- rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF;
- rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F;
+ rga->hw->get_version(rga);
v4l2_info(&rga->v4l2_dev, "HW Version: 0x%02x.%02x\n",
rga->version.major, rga->version.minor);
pm_runtime_put(rga->dev);
- /* Create CMD buffer */
- rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE,
- &rga->cmdbuf_phy, GFP_KERNEL,
- DMA_ATTR_WRITE_COMBINE);
- if (!rga->cmdbuf_virt) {
- ret = -ENOMEM;
- goto rel_m2m;
- }
-
- def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
- def_frame.size = def_frame.stride * def_frame.height;
-
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
- goto free_dma;
+ goto rel_m2m;
}
v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n",
@@ -900,9 +858,6 @@ static int rga_probe(struct platform_device *pdev)
return 0;
-free_dma:
- dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
- rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
rel_m2m:
v4l2_m2m_release(rga->m2m_dev);
rel_vdev:
@@ -919,9 +874,6 @@ static void rga_remove(struct platform_device *pdev)
{
struct rockchip_rga *rga = platform_get_drvdata(pdev);
- dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
- rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
-
v4l2_info(&rga->v4l2_dev, "Removing\n");
v4l2_m2m_release(rga->m2m_dev);
@@ -935,7 +887,7 @@ static int __maybe_unused rga_runtime_suspend(struct device *dev)
{
struct rockchip_rga *rga = dev_get_drvdata(dev);
- rga_disable_clocks(rga);
+ clk_bulk_disable_unprepare(rga->num_clks, rga->clks);
return 0;
}
@@ -944,7 +896,7 @@ static int __maybe_unused rga_runtime_resume(struct device *dev)
{
struct rockchip_rga *rga = dev_get_drvdata(dev);
- return rga_enable_clocks(rga);
+ return clk_bulk_prepare_enable(rga->num_clks, rga->clks);
}
static const struct dev_pm_ops rga_pm = {
@@ -955,9 +907,15 @@ static const struct dev_pm_ops rga_pm = {
static const struct of_device_id rockchip_rga_match[] = {
{
.compatible = "rockchip,rk3288-rga",
+ .data = &rga2_hw,
},
{
.compatible = "rockchip,rk3399-rga",
+ .data = &rga2_hw,
+ },
+ {
+ .compatible = "rockchip,rk3588-rga3",
+ .data = &rga3_hw,
},
{},
};
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 72a28b120fab..bd431534d0d3 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -6,6 +6,8 @@
#ifndef __RGA_H__
#define __RGA_H__
+#include <linux/bits.h>
+#include <linux/clk.h>
#include <linux/platform_device.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-ctrls.h>
@@ -13,32 +15,16 @@
#define RGA_NAME "rockchip-rga"
-struct rga_fmt {
- u32 fourcc;
- int depth;
- u8 uv_factor;
- u8 y_div;
- u8 x_div;
- u8 color_swap;
- u8 hw_format;
-};
+#define DEFAULT_WIDTH 100
+#define DEFAULT_HEIGHT 100
struct rga_frame {
- /* Original dimensions */
- u32 width;
- u32 height;
- u32 colorspace;
-
/* Crop */
struct v4l2_rect crop;
/* Image format */
- struct rga_fmt *fmt;
+ void *fmt;
struct v4l2_pix_format_mplane pix;
-
- /* Variables that can calculated once and reused */
- u32 stride;
- u32 size;
};
struct rga_dma_desc {
@@ -57,6 +43,10 @@ struct rga_ctx {
struct rga_frame out;
struct v4l2_ctrl_handler ctrl_handler;
+ void *cmdbuf_virt;
+ dma_addr_t cmdbuf_phy;
+ bool cmdbuf_dirty;
+
int osequence;
int csequence;
@@ -73,6 +63,8 @@ static inline struct rga_ctx *file_to_rga_ctx(struct file *filp)
return container_of(file_to_v4l2_fh(filp), struct rga_ctx, fh);
}
+struct rga_hw;
+
struct rockchip_rga {
struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
@@ -81,9 +73,8 @@ struct rockchip_rga {
struct device *dev;
struct regmap *grf;
void __iomem *regs;
- struct clk *sclk;
- struct clk *aclk;
- struct clk *hclk;
+ struct clk_bulk_data *clks;
+ int num_clks;
struct rockchip_rga_version version;
/* vfd lock */
@@ -92,14 +83,14 @@ struct rockchip_rga {
spinlock_t ctrl_lock;
struct rga_ctx *curr;
- dma_addr_t cmdbuf_phy;
- void *cmdbuf_virt;
+
+ const struct rga_hw *hw;
};
-struct rga_addr_offset {
- unsigned int y_off;
- unsigned int u_off;
- unsigned int v_off;
+struct rga_addrs {
+ dma_addr_t y_addr;
+ dma_addr_t u_addr;
+ dma_addr_t v_addr;
};
struct rga_vb_buffer {
@@ -111,8 +102,8 @@ struct rga_vb_buffer {
dma_addr_t dma_desc_pa;
size_t n_desc;
- /* Plane offsets of this buffer into the mapping */
- struct rga_addr_offset offset;
+ /* Plane DMA addresses after the MMU mapping of the buffer */
+ struct rga_addrs dma_addrs;
};
static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb)
@@ -122,6 +113,9 @@ static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb)
struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type);
+int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in,
+ const struct v4l2_rect *crop_out, u32 rotate);
+
/* RGA Buffers Manage */
extern const struct vb2_ops rga_qops;
@@ -144,7 +138,37 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
rga_write(rga, reg, temp);
};
-void rga_hw_start(struct rockchip_rga *rga,
- struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
+#define RGA_FEATURE_FLIP BIT(0)
+#define RGA_FEATURE_ROTATE BIT(1)
+#define RGA_FEATURE_BG_COLOR BIT(2)
+
+struct rga_hw {
+ const char *card_type;
+ bool has_internal_iommu;
+ size_t cmdbuf_size;
+ u32 min_width, min_height;
+ u32 max_width, max_height;
+ u8 max_scaling_factor;
+ u8 stride_alignment;
+ u8 features;
+
+ void (*setup_cmdbuf)(struct rga_ctx *ctx);
+ void (*start)(struct rockchip_rga *rga,
+ struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
+ bool (*handle_irq)(struct rockchip_rga *rga);
+ void (*get_version)(struct rockchip_rga *rga);
+ void *(*adjust_and_map_format)(struct rga_ctx *ctx,
+ struct v4l2_pix_format_mplane *format,
+ bool is_output);
+ int (*enum_format)(struct v4l2_fmtdesc *f);
+};
+
+static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga)
+{
+ return rga->hw->has_internal_iommu;
+}
+
+extern const struct rga_hw rga2_hw;
+extern const struct rga_hw rga3_hw;
#endif
diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c
new file mode 100644
index 000000000000..ca1c268303dd
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga3-hw.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025-2026 Pengutronix e.K.
+ * Author: Sven Püschel <s.pueschel@pengutronix.de>
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/printk.h>
+
+#include <media/v4l2-common.h>
+
+#include "rga3-hw.h"
+#include "rga.h"
+
+static unsigned int rga3_get_scaling(unsigned int src, unsigned int dst)
+{
+ /*
+ * RGA3 scaling factor calculation as described in chapter 5.4.7 Resize
+ * of the TRM Part 2. The resulting scaling factor is a 16-bit value
+ * and therefore normalized with 2^16.
+ *
+ * While the TRM also mentions (dst-1)/(src-1) for the up-scaling case,
+ * it didn't work as the value always exceeds 16 bit. Flipping the
+ * factors results in a correct up-scaling. This is possible as the
+ * RGA3 has the RGA3_WIN_SCALE_XXX_UP bit to determine if it does
+ * an up or downscale.
+ *
+ * The scaling factor can potentially cause a slightly larger scaling
+ * (e.g. 1/2px larger scale and then cropped to the destination size).
+ * This can be seen when scaling 128x128px RGBA to 256x256px RGBA.
+ * The RGA2 scaling factor calculation (without the various +/-1
+ * doesn't work for the RGA3. It's assumed that this is an hardware
+ * accuracy limitation, as the vendor kernel driver uses the same
+ * scaling factor calculation.
+ *
+ * With a scaling factor of 1.0 the calculation technically also
+ * overflows 16 bit. This isn't relevant, as in this case the
+ * RGA3_WIN_SCALE_XXX_BYPASS bit completely skips the scaling operation.
+ */
+ if (dst > src) {
+ if (((src - 1) << 16) % (dst - 1) == 0)
+ return ((src - 1) << 16) / (dst - 1) - 1;
+ else
+ return ((src - 1) << 16) / (dst - 1);
+ } else {
+ return ((dst - 1) << 16) / (src - 1) + 1;
+ }
+}
+
+/*
+ * Check if the given format can be captured, as the RGA3 doesn't support all
+ * input formats also on it's output.
+ */
+static bool rga3_can_capture(const struct rga3_fmt *fmt)
+{
+ return fmt->hw_format <= RGA3_COLOR_FMT_LAST_OUTPUT;
+}
+
+/*
+ * Map the transformations to the RGA3 command buffer.
+ * Currently this is just the scaling settings and a fixed alpha value.
+ */
+static void rga3_cmd_set_trans_info(struct rga_ctx *ctx)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ unsigned int src_h, src_w, dst_h, dst_w;
+ unsigned int reg;
+ u16 hor_scl_fac, ver_scl_fac;
+ const struct rga3_fmt *in = ctx->in.fmt;
+
+ /* Support basic input cropping to support 1088px inputs */
+ src_h = ctx->in.crop.height;
+ src_w = ctx->in.crop.width;
+ dst_h = ctx->out.pix.height;
+ dst_w = ctx->out.pix.width;
+
+ reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_SCALE_HOR_UP, dst_w > src_w)
+ | FIELD_PREP(RGA3_WIN_SCALE_HOR_BYPASS, dst_w == src_w)
+ | FIELD_PREP(RGA3_WIN_SCALE_VER_UP, dst_h > src_h)
+ | FIELD_PREP(RGA3_WIN_SCALE_VER_BYPASS, dst_h == src_h);
+
+ hor_scl_fac = rga3_get_scaling(src_w, dst_w);
+ ver_scl_fac = rga3_get_scaling(src_h, dst_h);
+ reg = RGA3_WIN0_SCL_FAC - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_SCALE_HOR_FAC, hor_scl_fac)
+ | FIELD_PREP(RGA3_SCALE_VER_FAC, ver_scl_fac);
+
+ if (v4l2_format_info(in->fourcc)->has_alpha) {
+ /* copy alpha from input */
+ reg = RGA3_OVLP_TOP_ALPHA - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1)
+ | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1);
+ reg = RGA3_OVLP_BOT_ALPHA - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1)
+ | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1);
+ } else {
+ /* just use a 255 alpha value */
+ reg = RGA3_OVLP_TOP_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff)
+ | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1);
+ reg = RGA3_OVLP_BOT_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff)
+ | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1);
+ }
+}
+
+static void rga3_cmd_set_win0_addr(struct rga_ctx *ctx,
+ const struct rga_addrs *addrs)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ unsigned int reg;
+
+ reg = RGA3_WIN0_Y_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->y_addr;
+ reg = RGA3_WIN0_U_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->u_addr;
+}
+
+static void rga3_cmd_set_wr_addr(struct rga_ctx *ctx,
+ const struct rga_addrs *addrs)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ unsigned int reg;
+
+ reg = RGA3_WR_Y_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->y_addr;
+ reg = RGA3_WR_U_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->u_addr;
+}
+
+/* Map the input pixel format to win0 of the comamnd buffer. */
+static void rga3_cmd_set_win0_format(struct rga_ctx *ctx)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ const struct rga3_fmt *in = ctx->in.fmt;
+ const struct rga3_fmt *out = ctx->out.fmt;
+ const struct v4l2_format_info *in_fmt, *out_fmt;
+ unsigned int act_h, act_w, src_h, src_w;
+ bool r2y, y2r;
+ u8 rd_format;
+ const struct v4l2_pix_format_mplane *csc_pix;
+ u8 csc_mode;
+ unsigned int reg;
+
+ act_h = ctx->in.pix.height;
+ act_w = ctx->in.pix.width;
+ /* Support basic input cropping to support 1088px inputs */
+ src_h = ctx->in.crop.height;
+ src_w = ctx->in.crop.width;
+
+ in_fmt = v4l2_format_info(in->fourcc);
+ out_fmt = v4l2_format_info(out->fourcc);
+ r2y = v4l2_is_format_rgb(in_fmt) && v4l2_is_format_yuv(out_fmt);
+ y2r = v4l2_is_format_yuv(in_fmt) && v4l2_is_format_rgb(out_fmt);
+
+ /* The Hardware only supports formats with 1/2 planes */
+ if (in_fmt->comp_planes == 2)
+ rd_format = RGA3_RDWR_FORMAT_SEMI_PLANAR;
+ else
+ rd_format = RGA3_RDWR_FORMAT_INTERLEAVED;
+
+ /* set pixel format and CSC */
+ csc_pix = r2y ? &ctx->out.pix : &ctx->in.pix;
+ switch (csc_pix->ycbcr_enc) {
+ case V4L2_YCBCR_ENC_BT2020:
+ csc_mode = RGA3_WIN_CSC_MODE_BT2020_L;
+ break;
+ case V4L2_YCBCR_ENC_709:
+ csc_mode = RGA3_WIN_CSC_MODE_BT709_L;
+ break;
+ default: /* should be fixed to BT601 in adjust_and_map_format */
+ if (csc_pix->quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ csc_mode = RGA3_WIN_CSC_MODE_BT601_L;
+ else
+ csc_mode = RGA3_WIN_CSC_MODE_BT601_F;
+ break;
+ }
+
+ reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_ENABLE, 1)
+ | FIELD_PREP(RGA3_WIN_PIC_FORMAT, in->hw_format)
+ | FIELD_PREP(RGA3_WIN_YC_SWAP, in->yc_swap)
+ | FIELD_PREP(RGA3_WIN_RBUV_SWAP, in->rbuv_swap)
+ | FIELD_PREP(RGA3_WIN_RD_FORMAT, rd_format)
+ | FIELD_PREP(RGA3_WIN_R2Y, r2y)
+ | FIELD_PREP(RGA3_WIN_Y2R, y2r)
+ | FIELD_PREP(RGA3_WIN_CSC_MODE, csc_mode);
+
+ /* set stride */
+ reg = RGA3_WIN0_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* stride needs to be in words */
+ cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2;
+ reg = RGA3_WIN0_UV_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* The Hardware only supports formats with 1/2 planes */
+ if (ctx->in.pix.num_planes == 2)
+ cmd[reg >> 2] = ctx->in.pix.plane_fmt[1].bytesperline >> 2;
+ else
+ cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2;
+
+ /* set size */
+ reg = RGA3_WIN0_ACT_SIZE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, act_w)
+ | FIELD_PREP(RGA3_HEIGHT, act_h);
+ reg = RGA3_WIN0_SRC_SIZE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, src_w)
+ | FIELD_PREP(RGA3_HEIGHT, src_h);
+}
+
+/* Map the output pixel format to the command buffer */
+static void rga3_cmd_set_wr_format(struct rga_ctx *ctx)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ const struct rga3_fmt *out = ctx->out.fmt;
+ const struct v4l2_format_info *out_fmt;
+ unsigned int dst_h, dst_w;
+ u8 wr_format;
+ unsigned int reg;
+
+ dst_h = ctx->out.pix.height;
+ dst_w = ctx->out.pix.width;
+
+ out_fmt = v4l2_format_info(out->fourcc);
+
+ /* The Hardware only supports formats with 1/2 planes */
+ if (out_fmt->comp_planes == 2)
+ wr_format = RGA3_RDWR_FORMAT_SEMI_PLANAR;
+ else
+ wr_format = RGA3_RDWR_FORMAT_INTERLEAVED;
+
+ /* set pixel format */
+ reg = RGA3_WR_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WR_PIC_FORMAT, out->hw_format)
+ | FIELD_PREP(RGA3_WR_YC_SWAP, out->yc_swap)
+ | FIELD_PREP(RGA3_WR_RBUV_SWAP, out->rbuv_swap)
+ | FIELD_PREP(RGA3_WR_FORMAT, wr_format)
+ /* Use the max value to avoid limiting the write speed */
+ | FIELD_PREP(RGA3_WR_SW_OUTSTANDING_MAX, 63);
+
+ /* set stride */
+ reg = RGA3_WR_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* stride needs to be in words */
+ cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2;
+ reg = RGA3_WR_PL_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* The Hardware only supports formats with 1/2 planes */
+ if (ctx->out.pix.num_planes == 2)
+ cmd[reg >> 2] = ctx->out.pix.plane_fmt[1].bytesperline >> 2;
+ else
+ cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2;
+
+ /* Set size.
+ * As two inputs are not supported, we don't use win1.
+ * Therefore only set the size for win0.
+ */
+ reg = RGA3_WIN0_DST_SIZE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, dst_w)
+ | FIELD_PREP(RGA3_HEIGHT, dst_h);
+}
+
+static void rga3_hw_setup_cmdbuf(struct rga_ctx *ctx)
+{
+ memset(ctx->cmdbuf_virt, 0, RGA3_CMDBUF_SIZE);
+
+ rga3_cmd_set_win0_format(ctx);
+ rga3_cmd_set_trans_info(ctx);
+ rga3_cmd_set_wr_format(ctx);
+}
+
+static void rga3_hw_start(struct rockchip_rga *rga,
+ struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
+{
+ struct rga_ctx *ctx = rga->curr;
+
+ rga3_cmd_set_win0_addr(ctx, &src->dma_addrs);
+ rga3_cmd_set_wr_addr(ctx, &dst->dma_addrs);
+
+ rga_write(rga, RGA3_CMD_ADDR, ctx->cmdbuf_phy);
+
+ /* sync CMD buf for RGA */
+ dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+ /* set to master mode and start the conversion */
+ rga_write(rga, RGA3_SYS_CTRL,
+ FIELD_PREP(RGA3_CMD_MODE, RGA3_CMD_MODE_MASTER));
+ rga_write(rga, RGA3_INT_EN, FIELD_PREP(RGA3_INT_FRM_DONE, 1));
+ rga_write(rga, RGA3_CMD_CTRL,
+ FIELD_PREP(RGA3_CMD_LINE_START_PULSE, 1));
+}
+
+static bool rga3_handle_irq(struct rockchip_rga *rga)
+{
+ u32 intr;
+
+ intr = rga_read(rga, RGA3_INT_RAW);
+ /* clear all interrupts */
+ rga_write(rga, RGA3_INT_CLR, intr);
+
+ return FIELD_GET(RGA3_INT_FRM_DONE, intr);
+}
+
+static void rga3_get_version(struct rockchip_rga *rga)
+{
+ u32 version = rga_read(rga, RGA3_VERSION_NUM);
+
+ rga->version.major = FIELD_GET(RGA3_VERSION_NUM_MAJOR, version);
+ rga->version.minor = FIELD_GET(RGA3_VERSION_NUM_MINOR, version);
+}
+
+static struct rga3_fmt rga3_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .hw_format = RGA3_COLOR_FMT_BGR888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .hw_format = RGA3_COLOR_FMT_BGR888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGBA32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGBX32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .hw_format = RGA3_COLOR_FMT_BGR565,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .yc_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .yc_swap = 1,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .rbuv_swap = 1,
+ },
+ /* Input only formats last to keep rga3_enum_format simple */
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGRA32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XRGB32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGRX32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ },
+};
+
+static int rga3_enum_format(struct v4l2_fmtdesc *f)
+{
+ struct rga3_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(rga3_formats))
+ return -EINVAL;
+
+ fmt = &rga3_formats[f->index];
+ if (V4L2_TYPE_IS_CAPTURE(f->type) && !rga3_can_capture(fmt))
+ return -EINVAL;
+
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static void *rga3_adjust_and_map_format(struct rga_ctx *ctx,
+ struct v4l2_pix_format_mplane *format,
+ bool is_output)
+{
+ unsigned int i;
+ const struct v4l2_format_info *format_info;
+ const struct v4l2_pix_format_mplane *other_format;
+ const struct v4l2_format_info *other_format_info;
+
+ if (!format)
+ return &rga3_formats[0];
+
+ format_info = v4l2_format_info(format->pixelformat);
+ other_format = is_output ? &ctx->out.pix : &ctx->in.pix;
+ other_format_info = v4l2_format_info(other_format->pixelformat);
+
+ if ((v4l2_is_format_rgb(format_info) &&
+ v4l2_is_format_yuv(other_format_info)) ||
+ (v4l2_is_format_yuv(format_info) &&
+ v4l2_is_format_rgb(other_format_info))) {
+ /*
+ * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions
+ * Additionally BT709 and BT2020 only support limited range YUV.
+ */
+ switch (format->ycbcr_enc) {
+ case V4L2_YCBCR_ENC_601:
+ /* supports full and limited range */
+ break;
+ case V4L2_YCBCR_ENC_709:
+ case V4L2_YCBCR_ENC_BT2020:
+ format->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ default:
+ format->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rga3_formats); i++) {
+ if (!is_output && !rga3_can_capture(&rga3_formats[i]))
+ continue;
+
+ if (rga3_formats[i].fourcc == format->pixelformat)
+ return &rga3_formats[i];
+ }
+
+ format->pixelformat = rga3_formats[0].fourcc;
+ return &rga3_formats[0];
+}
+
+const struct rga_hw rga3_hw = {
+ .card_type = "rga3",
+ .has_internal_iommu = false,
+ .cmdbuf_size = RGA3_CMDBUF_SIZE,
+ .min_width = RGA3_MIN_WIDTH,
+ .min_height = RGA3_MIN_HEIGHT,
+ /* use output size, as it's a bit smaller than the input size */
+ .max_width = RGA3_MAX_OUTPUT_WIDTH,
+ .max_height = RGA3_MAX_OUTPUT_HEIGHT,
+ .max_scaling_factor = RGA3_MAX_SCALING_FACTOR,
+ .stride_alignment = 16,
+ .features = 0,
+
+ .setup_cmdbuf = rga3_hw_setup_cmdbuf,
+ .start = rga3_hw_start,
+ .handle_irq = rga3_handle_irq,
+ .get_version = rga3_get_version,
+ .enum_format = rga3_enum_format,
+ .adjust_and_map_format = rga3_adjust_and_map_format,
+};
diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.h b/drivers/media/platform/rockchip/rga/rga3-hw.h
new file mode 100644
index 000000000000..85fd8ae257ec
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga3-hw.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Pengutronix e.K.
+ * Author: Sven Püschel <s.pueschel@pengutronix.de>
+ */
+#ifndef __RGA3_HW_H__
+#define __RGA3_HW_H__
+
+#include <linux/bits.h>
+#include <linux/types.h>
+
+#define RGA3_CMDBUF_SIZE 0xb8
+
+#define RGA3_MIN_WIDTH 128
+#define RGA3_MIN_HEIGHT 128
+#define RGA3_MAX_INPUT_WIDTH (8192 - 16)
+#define RGA3_MAX_INPUT_HEIGHT (8192 - 16)
+#define RGA3_MAX_OUTPUT_WIDTH (8192 - 64)
+#define RGA3_MAX_OUTPUT_HEIGHT (8192 - 64)
+#define RGA3_MAX_SCALING_FACTOR 8
+#define RGA3_RESET_TIMEOUT 1000
+
+/* Registers address */
+/* sys reg */
+#define RGA3_SYS_CTRL 0x000
+#define RGA3_CMD_CTRL 0x004
+#define RGA3_CMD_ADDR 0x008
+#define RGA3_MI_GROUP_CTRL 0x00c
+#define RGA3_ARQOS_CTRL 0x010
+#define RGA3_VERSION_NUM 0x018
+#define RGA3_VERSION_TIM 0x01c
+#define RGA3_INT_EN 0x020
+#define RGA3_INT_RAW 0x024
+#define RGA3_INT_MSK 0x028
+#define RGA3_INT_CLR 0x02c
+#define RGA3_RO_SRST 0x030
+#define RGA3_STATUS0 0x034
+#define RGA3_SCAN_CNT 0x038
+#define RGA3_CMD_STATE 0x040
+
+/* cmd reg */
+#define RGA3_WIN0_RD_CTRL 0x100
+#define RGA3_FIRST_CMD_REG RGA3_WIN0_RD_CTRL
+#define RGA3_WIN0_Y_BASE 0x110
+#define RGA3_WIN0_U_BASE 0x114
+#define RGA3_WIN0_V_BASE 0x118
+#define RGA3_WIN0_VIR_STRIDE 0x11c
+#define RGA3_WIN0_FBC_OFF 0x120
+#define RGA3_WIN0_SRC_SIZE 0x124
+#define RGA3_WIN0_ACT_OFF 0x128
+#define RGA3_WIN0_ACT_SIZE 0x12c
+#define RGA3_WIN0_DST_SIZE 0x130
+#define RGA3_WIN0_SCL_FAC 0x134
+#define RGA3_WIN0_UV_VIR_STRIDE 0x138
+#define RGA3_WIN1_RD_CTRL 0x140
+#define RGA3_WIN1_Y_BASE 0x150
+#define RGA3_WIN1_U_BASE 0x154
+#define RGA3_WIN1_V_BASE 0x158
+#define RGA3_WIN1_VIR_STRIDE 0x15c
+#define RGA3_WIN1_FBC_OFF 0x160
+#define RGA3_WIN1_SRC_SIZE 0x164
+#define RGA3_WIN1_ACT_OFF 0x168
+#define RGA3_WIN1_ACT_SIZE 0x16c
+#define RGA3_WIN1_DST_SIZE 0x170
+#define RGA3_WIN1_SCL_FAC 0x174
+#define RGA3_WIN1_UV_VIR_STRIDE 0x178
+#define RGA3_OVLP_CTRL 0x180
+#define RGA3_OVLP_OFF 0x184
+#define RGA3_OVLP_TOP_KEY_MIN 0x188
+#define RGA3_OVLP_TOP_KEY_MAX 0x18c
+#define RGA3_OVLP_TOP_CTRL 0x190
+#define RGA3_OVLP_BOT_CTRL 0x194
+#define RGA3_OVLP_TOP_ALPHA 0x198
+#define RGA3_OVLP_BOT_ALPHA 0x19c
+#define RGA3_WR_CTRL 0x1a0
+#define RGA3_WR_FBCE_CTRL 0x1a4
+#define RGA3_WR_VIR_STRIDE 0x1a8
+#define RGA3_WR_PL_VIR_STRIDE 0x1ac
+#define RGA3_WR_Y_BASE 0x1b0
+#define RGA3_WR_U_BASE 0x1b4
+#define RGA3_WR_V_BASE 0x1b8
+
+/* Registers value */
+#define RGA3_COLOR_FMT_YUV420 0x0
+#define RGA3_COLOR_FMT_YUV422 0x1
+#define RGA3_COLOR_FMT_YUV420_10B 0x2
+#define RGA3_COLOR_FMT_YUV422_10B 0x3
+/*
+ * Use memory ordering names
+ * instead of the datasheet naming RGB formats in big endian order
+ */
+#define RGA3_COLOR_FMT_BGR565 0x4
+#define RGA3_COLOR_FMT_BGR888 0x5
+#define RGA3_COLOR_FMT_FIRST_HAS_ALPHA RGA3_COLOR_FMT_BGRA8888
+#define RGA3_COLOR_FMT_BGRA8888 0x6
+#define RGA3_COLOR_FMT_LAST_OUTPUT RGA3_COLOR_FMT_BGRA8888
+/* the following are only supported as inputs */
+#define RGA3_COLOR_FMT_ABGR8888 0x7
+/*
+ * the following seem to be unnecessary,
+ * as they can be achieved with RB swaps
+ */
+#define RGA3_COLOR_FMT_RGBA8888 0x8
+#define RGA3_COLOR_FMT_ARGB8888 0x9
+
+#define RGA3_RDWR_FORMAT_SEMI_PLANAR 0x1
+#define RGA3_RDWR_FORMAT_INTERLEAVED 0x2
+
+#define RGA3_CMD_MODE_MASTER 0x1
+
+#define RGA3_WIN_CSC_MODE_BT601_L 0x0
+#define RGA3_WIN_CSC_MODE_BT709_L 0x1
+#define RGA3_WIN_CSC_MODE_BT601_F 0x2
+#define RGA3_WIN_CSC_MODE_BT2020_L 0x3
+
+/* RGA masks */
+/* SYS_CTRL */
+#define RGA3_CCLK_SRESET BIT(4)
+#define RGA3_ACLK_SRESET BIT(3)
+#define RGA3_CMD_MODE BIT(1)
+
+/* CMD_CTRL */
+#define RGA3_CMD_LINE_START_PULSE BIT(0)
+
+/* VERSION_NUM */
+#define RGA3_VERSION_NUM_MAJOR GENMASK(31, 28)
+#define RGA3_VERSION_NUM_MINOR GENMASK(27, 20)
+
+/* INT_* */
+#define RGA3_INT_FRM_DONE BIT(0)
+#define RGA3_INT_DMA_READ_BUS_ERR BIT(2)
+#define RGA3_INT_WIN0_FBC_DEC_ERR BIT(5)
+#define RGA3_INT_WIN0_HOR_ERR BIT(6)
+#define RGA3_INT_WIN0_VER_ERR BIT(7)
+#define RGA3_INT_WR_VER_ERR BIT(13)
+#define RGA3_INT_WR_HOR_ERR BIT(14)
+#define RGA3_INT_WR_BUS_ERR BIT(15)
+#define RGA3_INT_WIN0_IN_FIFO_WR_ERR BIT(16)
+#define RGA3_INT_WIN0_IN_FIFO_RD_ERR BIT(17)
+#define RGA3_INT_WIN0_HOR_FIFO_WR_ERR BIT(18)
+#define RGA3_INT_WIN0_HOR_FIFO_RD_ERR BIT(19)
+#define RGA3_INT_WIN0_VER_FIFO_WR_ERR BIT(20)
+#define RGA3_INT_WIN0_VER_FIFO_RD_ERR BIT(21)
+
+/* RO_SRST */
+#define RGA3_RO_SRST_DONE GENMASK(5, 0)
+
+/* *_SIZE */
+#define RGA3_HEIGHT GENMASK(28, 16)
+#define RGA3_WIDTH GENMASK(12, 0)
+
+/* SCL_FAC */
+#define RGA3_SCALE_VER_FAC GENMASK(31, 16)
+#define RGA3_SCALE_HOR_FAC GENMASK(15, 0)
+
+/* WINx_CTRL */
+#define RGA3_WIN_CSC_MODE GENMASK(27, 26)
+#define RGA3_WIN_R2Y BIT(25)
+#define RGA3_WIN_Y2R BIT(24)
+#define RGA3_WIN_SCALE_VER_UP BIT(23)
+#define RGA3_WIN_SCALE_VER_BYPASS BIT(22)
+#define RGA3_WIN_SCALE_HOR_UP BIT(21)
+#define RGA3_WIN_SCALE_HOR_BYPASS BIT(20)
+#define RGA3_WIN_YC_SWAP BIT(13)
+#define RGA3_WIN_RBUV_SWAP BIT(12)
+#define RGA3_WIN_RD_FORMAT GENMASK(9, 8)
+#define RGA3_WIN_PIC_FORMAT GENMASK(7, 4)
+#define RGA3_WIN_ENABLE BIT(0)
+
+/* COLOR_CTRL */
+#define RGA3_OVLP_GLOBAL_ALPHA GENMASK(23, 16)
+#define RGA3_OVLP_COLOR_MODE BIT(0)
+
+/* ALPHA_CTRL */
+#define RGA3_ALPHA_SELECT_MODE BIT(4)
+#define RGA3_ALPHA_BLEND_MODE GENMASK(3, 2)
+
+/* WR_CTRL */
+#define RGA3_WR_YC_SWAP BIT(20)
+#define RGA3_WR_SW_OUTSTANDING_MAX GENMASK(18, 13)
+#define RGA3_WR_RBUV_SWAP BIT(12)
+#define RGA3_WR_FORMAT GENMASK(9, 8)
+#define RGA3_WR_PIC_FORMAT GENMASK(7, 4)
+
+struct rga3_fmt {
+ u32 fourcc;
+ u8 hw_format;
+ bool rbuv_swap;
+ bool yc_swap;
+};
+
+#endif
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
index 9e67160a16e4..bc9518f8db50 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.c
@@ -30,6 +30,14 @@
#define RK3568_MIPI_CTRL0_CROP_EN BIT(5)
#define RK3568_MIPI_CTRL0_WRDDR(type) ((type) << 1)
+#define RK3588_MIPI_CTRL0_DMA_EN BIT(28)
+#define RK3588_MIPI_CTRL0_HIGH_ALIGN BIT(27)
+#define RK3588_MIPI_CTRL0_WRDDR(type) ((type) << 5)
+#define RK3588_MIPI_CTRL0_CROP_EN BIT(4)
+#define RK3588_MIPI_CTRL0_PARSE(type) ((type) << 1)
+
+#define RK3588_MIPI_CTRL_CAP_EN BIT(0)
+
#define RKCIF_MIPI_CTRL0_DT_ID(id) ((id) << 10)
#define RKCIF_MIPI_CTRL0_VC_ID(id) ((id) << 8)
#define RKCIF_MIPI_CTRL0_CAP_EN BIT(0)
@@ -375,11 +383,8 @@ static u32
rkcif_rk3568_mipi_ctrl0(struct rkcif_stream *stream,
const struct rkcif_output_fmt *active_out_fmt)
{
- u32 ctrl0 = 0;
-
- ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt);
- ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN;
- ctrl0 |= RK3568_MIPI_CTRL0_CROP_EN;
+ u32 ctrl0 = RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt) |
+ RKCIF_MIPI_CTRL0_CAP_EN | RK3568_MIPI_CTRL0_CROP_EN;
if (active_out_fmt->mipi.compact)
ctrl0 |= RK3568_MIPI_CTRL0_COMPACT_EN;
@@ -481,6 +486,132 @@ const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data = {
},
};
+static u32
+rkcif_rk3588_mipi_ctrl0(struct rkcif_stream *stream,
+ const struct rkcif_output_fmt *active_out_fmt)
+{
+ u32 ctrl0 = 0;
+
+ ctrl0 |= RK3588_MIPI_CTRL0_DMA_EN;
+ ctrl0 |= RKCIF_MIPI_CTRL0_DT_ID(active_out_fmt->mipi.dt);
+ ctrl0 |= RK3588_MIPI_CTRL0_CROP_EN;
+ ctrl0 |= RKCIF_MIPI_CTRL0_CAP_EN;
+
+ switch (active_out_fmt->mipi.type) {
+ case RKCIF_MIPI_TYPE_RAW8:
+ break;
+ case RKCIF_MIPI_TYPE_RAW10:
+ ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x1);
+ if (!active_out_fmt->mipi.compact)
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1);
+ break;
+ case RKCIF_MIPI_TYPE_RAW12:
+ ctrl0 |= RK3588_MIPI_CTRL0_PARSE(0x2);
+ if (!active_out_fmt->mipi.compact)
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x1);
+ break;
+ case RKCIF_MIPI_TYPE_RGB888:
+ break;
+ case RKCIF_MIPI_TYPE_YUV422SP:
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x4);
+ break;
+ case RKCIF_MIPI_TYPE_YUV420SP:
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x5);
+ break;
+ case RKCIF_MIPI_TYPE_YUV400:
+ ctrl0 |= RK3588_MIPI_CTRL0_WRDDR(0x3);
+ break;
+ default:
+ break;
+ }
+
+ return ctrl0;
+}
+
+const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data = {
+ .mipi_num = 6,
+ .mipi_ctrl0 = rkcif_rk3588_mipi_ctrl0,
+ .regs = {
+ [RKCIF_MIPI_CTRL] = 0x20,
+ [RKCIF_MIPI_INTEN] = 0x74,
+ [RKCIF_MIPI_INTSTAT] = 0x78,
+ },
+ .regs_id = {
+ [RKCIF_ID0] = {
+ [RKCIF_MIPI_CTRL0] = 0x00,
+ [RKCIF_MIPI_CTRL1] = 0x04,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x24,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x2c,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x34,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x28,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x30,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_CROP_START] = 0x8c,
+ },
+ [RKCIF_ID1] = {
+ [RKCIF_MIPI_CTRL0] = 0x08,
+ [RKCIF_MIPI_CTRL1] = 0x0c,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x38,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x40,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x48,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x3c,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x44,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_CROP_START] = 0x90,
+ },
+ [RKCIF_ID2] = {
+ [RKCIF_MIPI_CTRL0] = 0x10,
+ [RKCIF_MIPI_CTRL1] = 0x14,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x4c,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x54,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x5c,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x50,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x58,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_CROP_START] = 0x94,
+ },
+ [RKCIF_ID3] = {
+ [RKCIF_MIPI_CTRL0] = 0x18,
+ [RKCIF_MIPI_CTRL1] = 0x1c,
+ [RKCIF_MIPI_FRAME0_ADDR_Y] = 0x60,
+ [RKCIF_MIPI_FRAME0_ADDR_UV] = 0x68,
+ [RKCIF_MIPI_FRAME0_VLW_Y] = 0x70,
+ [RKCIF_MIPI_FRAME0_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_ADDR_Y] = 0x64,
+ [RKCIF_MIPI_FRAME1_ADDR_UV] = 0x6c,
+ [RKCIF_MIPI_FRAME1_VLW_Y] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_FRAME1_VLW_UV] = RKCIF_REGISTER_NOTSUPPORTED,
+ [RKCIF_MIPI_CROP_START] = 0x98,
+ },
+ },
+ .blocks = {
+ {
+ .offset = 0x100,
+ },
+ {
+ .offset = 0x200,
+ },
+ {
+ .offset = 0x300,
+ },
+ {
+ .offset = 0x400,
+ },
+ {
+ .offset = 0x500,
+ },
+ {
+ .offset = 0x600,
+ },
+ },
+};
+
static inline unsigned int rkcif_mipi_get_reg(struct rkcif_interface *interface,
unsigned int index)
{
@@ -631,6 +762,13 @@ static int rkcif_mipi_start_streaming(struct rkcif_stream *stream)
rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL1, ctrl1);
rkcif_mipi_stream_write(stream, RKCIF_MIPI_CTRL0, ctrl0);
+ /*
+ * TODO: This bit has a different meaning on the RK3568, but it is
+ * set there by default anyway. While correct, this is not exactly
+ * nice and shall be reworked during the next refactoring.
+ */
+ rkcif_mipi_write(interface, RKCIF_MIPI_CTRL, RK3588_MIPI_CTRL_CAP_EN);
+
ret = 0;
out:
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
index 7f16eadc474c..7edaca44f653 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-capture-mipi.h
@@ -13,6 +13,7 @@
#include "rkcif-common.h"
extern const struct rkcif_mipi_match_data rkcif_rk3568_vicap_mipi_match_data;
+extern const struct rkcif_mipi_match_data rkcif_rk3588_vicap_mipi_match_data;
int rkcif_mipi_register(struct rkcif_device *rkcif);
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-common.h b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
index dd92cfbc879f..4d9211ba9bda 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-common.h
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-common.h
@@ -27,7 +27,7 @@
#include "rkcif-regs.h"
#define RKCIF_DRIVER_NAME "rockchip-cif"
-#define RKCIF_CLK_MAX 4
+#define RKCIF_CLK_MAX 5
enum rkcif_format_type {
RKCIF_FMT_TYPE_INVALID,
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
index b4cf1146f131..be3a174b9aab 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-dev.c
@@ -53,6 +53,20 @@ static const struct rkcif_match_data rk3568_vicap_match_data = {
.mipi = &rkcif_rk3568_vicap_mipi_match_data,
};
+static const char *const rk3588_vicap_clks[] = {
+ "aclk",
+ "hclk",
+ "dclk",
+ "iclk",
+ "iclk1",
+};
+
+static const struct rkcif_match_data rk3588_vicap_match_data = {
+ .clks = rk3588_vicap_clks,
+ .clks_num = ARRAY_SIZE(rk3588_vicap_clks),
+ .mipi = &rkcif_rk3588_vicap_mipi_match_data,
+};
+
static const struct of_device_id rkcif_plat_of_match[] = {
{
.compatible = "rockchip,px30-vip",
@@ -62,6 +76,10 @@ static const struct of_device_id rkcif_plat_of_match[] = {
.compatible = "rockchip,rk3568-vicap",
.data = &rk3568_vicap_match_data,
},
+ {
+ .compatible = "rockchip,rk3588-vicap",
+ .data = &rk3588_vicap_match_data,
+ },
{}
};
MODULE_DEVICE_TABLE(of, rkcif_plat_of_match);
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
index 6442436a5e42..042b759eba62 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-params.c
@@ -64,6 +64,7 @@ union rkisp1_ext_params_config {
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;
+ struct rkisp1_ext_params_cac_config cac;
};
enum rkisp1_params_formats {
@@ -1413,6 +1414,47 @@ static void rkisp1_wdr_config(struct rkisp1_params *params,
RKISP1_CIF_ISP_WDR_TONE_CURVE_YM_MASK);
}
+static void rkisp1_cac_config(struct rkisp1_params *params,
+ const struct rkisp1_cif_isp_cac_config *arg)
+{
+ u32 val;
+
+ /*
+ * The enable bit is in the same register (RKISP1_CIF_ISP_CAC_CTRL),
+ * so only set the clipping mode, and do not modify the other bits.
+ */
+ val = rkisp1_read(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL);
+ val &= ~(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE |
+ RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE);
+ val |= FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE, arg->h_clip_mode) |
+ FIELD_PREP(RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE, arg->v_clip_mode);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_CTRL, val);
+
+ val = FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK, arg->h_count_start) |
+ FIELD_PREP(RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK, arg->v_count_start);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_COUNT_START, val);
+
+ val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[0]) |
+ FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[0]);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_A, val);
+
+ val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[1]) |
+ FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[1]);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_B, val);
+
+ val = FIELD_PREP(RKISP1_CIF_ISP_CAC_RED_MASK, arg->red[2]) |
+ FIELD_PREP(RKISP1_CIF_ISP_CAC_BLUE_MASK, arg->blue[2]);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_C, val);
+
+ val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->x_nf) |
+ FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->x_ns);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_X_NORM, val);
+
+ val = FIELD_PREP(RKISP1_CIF_ISP_CAC_NF_MASK, arg->y_nf) |
+ FIELD_PREP(RKISP1_CIF_ISP_CAC_NS_MASK, arg->y_ns);
+ rkisp1_write(params->rkisp1, RKISP1_CIF_ISP_CAC_Y_NORM, val);
+}
+
static void
rkisp1_isp_isr_other_config(struct rkisp1_params *params,
const struct rkisp1_params_cfg *new_params)
@@ -2089,6 +2131,25 @@ static void rkisp1_ext_params_wdr(struct rkisp1_params *params,
RKISP1_CIF_ISP_WDR_CTRL_ENABLE);
}
+static void rkisp1_ext_params_cac(struct rkisp1_params *params,
+ const union rkisp1_ext_params_config *block)
+{
+ const struct rkisp1_ext_params_cac_config *cac = &block->cac;
+
+ if (cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_DISABLE) {
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+ RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
+ return;
+ }
+
+ rkisp1_cac_config(params, &cac->config);
+
+ if ((cac->header.flags & RKISP1_EXT_PARAMS_FL_BLOCK_ENABLE) &&
+ !(params->enabled_blocks & BIT(cac->header.type)))
+ rkisp1_param_set_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+ RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
+}
+
typedef void (*rkisp1_block_handler)(struct rkisp1_params *params,
const union rkisp1_ext_params_config *config);
@@ -2185,6 +2246,10 @@ static const struct rkisp1_ext_params_handler {
.handler = rkisp1_ext_params_wdr,
.group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
},
+ [RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC] = {
+ .handler = rkisp1_ext_params_cac,
+ .group = RKISP1_EXT_PARAMS_BLOCK_GROUP_OTHERS,
+ },
};
#define RKISP1_PARAMS_BLOCK_INFO(block, data) \
@@ -2215,6 +2280,7 @@ rkisp1_ext_params_block_types_info[] = {
RKISP1_PARAMS_BLOCK_INFO(COMPAND_EXPAND, compand_curve),
RKISP1_PARAMS_BLOCK_INFO(COMPAND_COMPRESS, compand_curve),
RKISP1_PARAMS_BLOCK_INFO(WDR, wdr),
+ RKISP1_PARAMS_BLOCK_INFO(CAC, cac),
};
static_assert(ARRAY_SIZE(rkisp1_ext_params_handlers) ==
@@ -2474,6 +2540,8 @@ void rkisp1_params_disable(struct rkisp1_params *params)
rkisp1_ie_enable(params, false);
rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_DPF_MODE,
RKISP1_CIF_ISP_DPF_MODE_EN);
+ rkisp1_param_clear_bits(params, RKISP1_CIF_ISP_CAC_CTRL,
+ RKISP1_CIF_ISP_CAC_CTRL_ENABLE);
}
static const struct rkisp1_params_ops rkisp1_v10_params_ops = {
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
index fbeb186cde0d..2b842194de8f 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-regs.h
@@ -724,6 +724,17 @@
#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MASK GENMASK(20, 16)
#define RKISP1_CIF_ISP_WDR_DMIN_STRENGTH_MAX 16U
+/* CAC */
+#define RKISP1_CIF_ISP_CAC_CTRL_ENABLE BIT(0)
+#define RKISP1_CIF_ISP_CAC_CTRL_V_CLIP_MODE GENMASK(2, 1)
+#define RKISP1_CIF_ISP_CAC_CTRL_H_CLIP_MODE BIT(3)
+#define RKISP1_CIF_ISP_CAC_COUNT_START_H_MASK GENMASK(12, 0)
+#define RKISP1_CIF_ISP_CAC_COUNT_START_V_MASK GENMASK(28, 16)
+#define RKISP1_CIF_ISP_CAC_RED_MASK GENMASK(8, 0)
+#define RKISP1_CIF_ISP_CAC_BLUE_MASK GENMASK(24, 16)
+#define RKISP1_CIF_ISP_CAC_NF_MASK GENMASK(4, 0)
+#define RKISP1_CIF_ISP_CAC_NS_MASK GENMASK(19, 16)
+
/* =================================================================== */
/* CIF Registers */
/* =================================================================== */
@@ -1196,8 +1207,8 @@
#define RKISP1_CIF_ISP_CAC_A (RKISP1_CIF_ISP_CAC_BASE + 0x00000008)
#define RKISP1_CIF_ISP_CAC_B (RKISP1_CIF_ISP_CAC_BASE + 0x0000000c)
#define RKISP1_CIF_ISP_CAC_C (RKISP1_CIF_ISP_CAC_BASE + 0x00000010)
-#define RKISP1_CIF_ISP_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014)
-#define RKISP1_CIF_ISP_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018)
+#define RKISP1_CIF_ISP_CAC_X_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000014)
+#define RKISP1_CIF_ISP_CAC_Y_NORM (RKISP1_CIF_ISP_CAC_BASE + 0x00000018)
#define RKISP1_CIF_ISP_EXP_BASE 0x00002600
#define RKISP1_CIF_ISP_EXP_CTRL (RKISP1_CIF_ISP_EXP_BASE + 0x00000000)
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h
new file mode 100644
index 000000000000..288467941abf
--- /dev/null
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-bitwriter.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Rockchip Video Decoder bit writer
+ *
+ * Copyright (C) 2026 Collabora, Ltd.
+ * Detlev Casanova <detlev.casanova@collabora.com>
+ * Copyright (C) 2019 Collabora, Ltd.
+ * Boris Brezillon <boris.brezillon@collabora.com>
+ */
+
+#ifndef RKVDEC_BIT_WRITER_H_
+#define RKVDEC_BIT_WRITER_H_
+
+#include <linux/types.h>
+#include <linux/bits.h>
+
+struct rkvdec_bw_field {
+ u16 offset;
+ u8 len;
+};
+
+#define BW_FIELD(_offset, _len) ((struct rkvdec_bw_field){ _offset, _len })
+
+static inline void rkvdec_set_bw_field(u32 *buf, struct rkvdec_bw_field field, u32 value)
+{
+ u8 bit = field.offset % 32;
+ u16 word = field.offset / 32;
+ u64 mask = GENMASK_ULL(bit + field.len - 1, bit);
+ u64 val = ((u64)value << bit) & mask;
+
+ buf[word] &= ~mask;
+ buf[word] |= val;
+ if (bit + field.len > 32) {
+ buf[word + 1] &= ~(mask >> 32);
+ buf[word + 1] |= val >> 32;
+ }
+}
+
+#endif /* RKVDEC_BIT_WRITER_H_ */
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c
index e28f06394470..54639512e456 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.c
@@ -21,51 +21,6 @@
#define RKVDEC_NUM_REFLIST 3
-static void set_dpb_info(struct rkvdec_rps_entry *entries,
- u8 reflist,
- u8 refnum,
- u8 info,
- bool bottom)
-{
- struct rkvdec_rps_entry *entry = &entries[(reflist * 4) + refnum / 8];
- u8 idx = refnum % 8;
-
- switch (idx) {
- case 0:
- entry->dpb_info0 = info;
- entry->bottom_flag0 = bottom;
- break;
- case 1:
- entry->dpb_info1 = info;
- entry->bottom_flag1 = bottom;
- break;
- case 2:
- entry->dpb_info2 = info;
- entry->bottom_flag2 = bottom;
- break;
- case 3:
- entry->dpb_info3 = info;
- entry->bottom_flag3 = bottom;
- break;
- case 4:
- entry->dpb_info4 = info;
- entry->bottom_flag4 = bottom;
- break;
- case 5:
- entry->dpb_info5 = info;
- entry->bottom_flag5 = bottom;
- break;
- case 6:
- entry->dpb_info6 = info;
- entry->bottom_flag6 = bottom;
- break;
- case 7:
- entry->dpb_info7 = info;
- entry->bottom_flag7 = bottom;
- break;
- }
-}
-
void lookup_ref_buf_idx(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
@@ -111,7 +66,7 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder,
if (!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
continue;
- hw_rps->frame_num[i] = builder->refs[i].frame_num;
+ rkvdec_set_bw_field(hw_rps->info, RPS_FRAME_NUM(i), builder->refs[i].frame_num);
}
for (j = 0; j < RKVDEC_NUM_REFLIST; j++) {
@@ -138,7 +93,9 @@ void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder,
dpb_valid = !!(run->ref_buf[ref->index]);
bottom = ref->fields == V4L2_H264_BOTTOM_FIELD_REF;
- set_dpb_info(hw_rps->entries, j, i, ref->index | (dpb_valid << 4), bottom);
+ rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_DPB_INFO(j, i),
+ ref->index | (dpb_valid << 4));
+ rkvdec_set_bw_field(hw_rps->info, RPS_ENTRY_BOTTOM_FLAG(j, i), bottom);
}
}
}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h
index 5336370507d6..f04b700b863c 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264-common.h
@@ -16,6 +16,7 @@
#include <media/v4l2-mem2mem.h>
#include "rkvdec.h"
+#include "rkvdec-bitwriter.h"
struct rkvdec_h264_scaling_list {
u8 scaling_list_4x4[6][16];
@@ -38,39 +39,16 @@ struct rkvdec_h264_run {
struct vb2_buffer *ref_buf[V4L2_H264_NUM_DPB_ENTRIES];
};
-struct rkvdec_rps_entry {
- u32 dpb_info0: 5;
- u32 bottom_flag0: 1;
- u32 view_index_off0: 1;
- u32 dpb_info1: 5;
- u32 bottom_flag1: 1;
- u32 view_index_off1: 1;
- u32 dpb_info2: 5;
- u32 bottom_flag2: 1;
- u32 view_index_off2: 1;
- u32 dpb_info3: 5;
- u32 bottom_flag3: 1;
- u32 view_index_off3: 1;
- u32 dpb_info4: 5;
- u32 bottom_flag4: 1;
- u32 view_index_off4: 1;
- u32 dpb_info5: 5;
- u32 bottom_flag5: 1;
- u32 view_index_off5: 1;
- u32 dpb_info6: 5;
- u32 bottom_flag6: 1;
- u32 view_index_off6: 1;
- u32 dpb_info7: 5;
- u32 bottom_flag7: 1;
- u32 view_index_off7: 1;
-} __packed;
+#define RPS_FRAME_NUM(i) BW_FIELD((i) * 16, 16)
+#define RPS_ENTRY_DPB_INFO(l, e) BW_FIELD(288 + (l) * 7 * 32 + (e) * 7, 5) //l: 0-2, e: 0-31
+#define RPS_ENTRY_BOTTOM_FLAG(l, e) BW_FIELD(293 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31
+#define RPS_ENTRY_VIEW_INDEX_OFF(l, e) BW_FIELD(294 + (l) * 7 * 32 + (e) * 7, 1) //l: 0-2, e: 0-31
+
+#define RKVDEC_H264_RPS_SIZE ALIGN(288 + 3 * 7 * 32, 128)
struct rkvdec_rps {
- u16 frame_num[16];
- u32 reserved0;
- struct rkvdec_rps_entry entries[12];
- u32 reserved1[66];
-} __packed;
+ u32 info[RKVDEC_H264_RPS_SIZE / 8 / 4];
+};
void lookup_ref_buf_idx(struct rkvdec_ctx *ctx, struct rkvdec_h264_run *run);
void assemble_hw_rps(struct v4l2_h264_reflist_builder *builder,
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
index d3202cecb988..ffa606038192 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-h264.c
@@ -16,6 +16,7 @@
#include "rkvdec-regs.h"
#include "rkvdec-cabac.h"
#include "rkvdec-h264-common.h"
+#include "rkvdec-bitwriter.h"
/* Size with u32 units. */
#define RKV_CABAC_INIT_BUFFER_SIZE (3680 + 128)
@@ -25,56 +26,48 @@ struct rkvdec_sps_pps_packet {
u32 info[8];
};
-struct rkvdec_ps_field {
- u16 offset;
- u8 len;
-};
-
-#define PS_FIELD(_offset, _len) \
- ((struct rkvdec_ps_field){ _offset, _len })
-
-#define SEQ_PARAMETER_SET_ID PS_FIELD(0, 4)
-#define PROFILE_IDC PS_FIELD(4, 8)
-#define CONSTRAINT_SET3_FLAG PS_FIELD(12, 1)
-#define CHROMA_FORMAT_IDC PS_FIELD(13, 2)
-#define BIT_DEPTH_LUMA PS_FIELD(15, 3)
-#define BIT_DEPTH_CHROMA PS_FIELD(18, 3)
-#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG PS_FIELD(21, 1)
-#define LOG2_MAX_FRAME_NUM_MINUS4 PS_FIELD(22, 4)
-#define MAX_NUM_REF_FRAMES PS_FIELD(26, 5)
-#define PIC_ORDER_CNT_TYPE PS_FIELD(31, 2)
-#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 PS_FIELD(33, 4)
-#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG PS_FIELD(37, 1)
-#define PIC_WIDTH_IN_MBS PS_FIELD(38, 9)
-#define PIC_HEIGHT_IN_MBS PS_FIELD(47, 9)
-#define FRAME_MBS_ONLY_FLAG PS_FIELD(56, 1)
-#define MB_ADAPTIVE_FRAME_FIELD_FLAG PS_FIELD(57, 1)
-#define DIRECT_8X8_INFERENCE_FLAG PS_FIELD(58, 1)
-#define MVC_EXTENSION_ENABLE PS_FIELD(59, 1)
-#define NUM_VIEWS PS_FIELD(60, 2)
-#define VIEW_ID(i) PS_FIELD(62 + ((i) * 10), 10)
-#define NUM_ANCHOR_REFS_L(i) PS_FIELD(82 + ((i) * 11), 1)
-#define ANCHOR_REF_L(i) PS_FIELD(83 + ((i) * 11), 10)
-#define NUM_NON_ANCHOR_REFS_L(i) PS_FIELD(104 + ((i) * 11), 1)
-#define NON_ANCHOR_REFS_L(i) PS_FIELD(105 + ((i) * 11), 10)
-#define PIC_PARAMETER_SET_ID PS_FIELD(128, 8)
-#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(136, 5)
-#define ENTROPY_CODING_MODE_FLAG PS_FIELD(141, 1)
-#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG PS_FIELD(142, 1)
-#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) PS_FIELD(143 + ((i) * 5), 5)
-#define WEIGHTED_PRED_FLAG PS_FIELD(153, 1)
-#define WEIGHTED_BIPRED_IDC PS_FIELD(154, 2)
-#define PIC_INIT_QP_MINUS26 PS_FIELD(156, 7)
-#define PIC_INIT_QS_MINUS26 PS_FIELD(163, 6)
-#define CHROMA_QP_INDEX_OFFSET PS_FIELD(169, 5)
-#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG PS_FIELD(174, 1)
-#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(175, 1)
-#define REDUNDANT_PIC_CNT_PRESENT PS_FIELD(176, 1)
-#define TRANSFORM_8X8_MODE_FLAG PS_FIELD(177, 1)
-#define SECOND_CHROMA_QP_INDEX_OFFSET PS_FIELD(178, 5)
-#define SCALING_LIST_ENABLE_FLAG PS_FIELD(183, 1)
-#define SCALING_LIST_ADDRESS PS_FIELD(184, 32)
-#define IS_LONG_TERM(i) PS_FIELD(216 + (i), 1)
+#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4)
+#define PROFILE_IDC BW_FIELD(4, 8)
+#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1)
+#define CHROMA_FORMAT_IDC BW_FIELD(13, 2)
+#define BIT_DEPTH_LUMA BW_FIELD(15, 3)
+#define BIT_DEPTH_CHROMA BW_FIELD(18, 3)
+#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1)
+#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4)
+#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5)
+#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2)
+#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4)
+#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1)
+#define PIC_WIDTH_IN_MBS BW_FIELD(38, 9)
+#define PIC_HEIGHT_IN_MBS BW_FIELD(47, 9)
+#define FRAME_MBS_ONLY_FLAG BW_FIELD(56, 1)
+#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(57, 1)
+#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(58, 1)
+#define MVC_EXTENSION_ENABLE BW_FIELD(59, 1)
+#define NUM_VIEWS BW_FIELD(60, 2)
+#define VIEW_ID(i) BW_FIELD(62 + ((i) * 10), 10)
+#define NUM_ANCHOR_REFS_L(i) BW_FIELD(82 + ((i) * 11), 1)
+#define ANCHOR_REF_L(i) BW_FIELD(83 + ((i) * 11), 10)
+#define NUM_NON_ANCHOR_REFS_L(i) BW_FIELD(104 + ((i) * 11), 1)
+#define NON_ANCHOR_REFS_L(i) BW_FIELD(105 + ((i) * 11), 10)
+#define PIC_PARAMETER_SET_ID BW_FIELD(128, 8)
+#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(136, 5)
+#define ENTROPY_CODING_MODE_FLAG BW_FIELD(141, 1)
+#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(142, 1)
+#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(143 + ((i) * 5), 5)
+#define WEIGHTED_PRED_FLAG BW_FIELD(153, 1)
+#define WEIGHTED_BIPRED_IDC BW_FIELD(154, 2)
+#define PIC_INIT_QP_MINUS26 BW_FIELD(156, 7)
+#define PIC_INIT_QS_MINUS26 BW_FIELD(163, 6)
+#define CHROMA_QP_INDEX_OFFSET BW_FIELD(169, 5)
+#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(174, 1)
+#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(175, 1)
+#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(176, 1)
+#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(177, 1)
+#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(178, 5)
+#define SCALING_LIST_ENABLE_FLAG BW_FIELD(183, 1)
+#define SCALING_LIST_ADDRESS BW_FIELD(184, 32)
+#define IS_LONG_TERM(i) BW_FIELD(216 + (i), 1)
/* Data structure describing auxiliary buffer format. */
struct rkvdec_h264_priv_tbl {
@@ -91,20 +84,6 @@ struct rkvdec_h264_ctx {
struct rkvdec_regs regs;
};
-static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value)
-{
- u8 bit = field.offset % 32, word = field.offset / 32;
- u64 mask = GENMASK_ULL(bit + field.len - 1, bit);
- u64 val = ((u64)value << bit) & mask;
-
- buf[word] &= ~mask;
- buf[word] |= val;
- if (bit + field.len > 32) {
- buf[word + 1] &= ~(mask >> 32);
- buf[word + 1] |= val >> 32;
- }
-}
-
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
@@ -128,7 +107,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
memset(hw_ps, 0, sizeof(*hw_ps));
-#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value)
+#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value)
/* write sps */
WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID);
WRITE_PPS(sps->profile_idc, PROFILE_IDC);
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
index 3119f3bc9f98..f89602075121 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
@@ -74,72 +74,6 @@ void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size
row_height[i] = pic_in_cts_height - sum;
}
-static void set_ref_poc(struct rkvdec_rps_short_term_ref_set *set, int poc, int value, int flag)
-{
- switch (poc) {
- case 0:
- set->delta_poc0 = value;
- set->used_flag0 = flag;
- break;
- case 1:
- set->delta_poc1 = value;
- set->used_flag1 = flag;
- break;
- case 2:
- set->delta_poc2 = value;
- set->used_flag2 = flag;
- break;
- case 3:
- set->delta_poc3 = value;
- set->used_flag3 = flag;
- break;
- case 4:
- set->delta_poc4 = value;
- set->used_flag4 = flag;
- break;
- case 5:
- set->delta_poc5 = value;
- set->used_flag5 = flag;
- break;
- case 6:
- set->delta_poc6 = value;
- set->used_flag6 = flag;
- break;
- case 7:
- set->delta_poc7 = value;
- set->used_flag7 = flag;
- break;
- case 8:
- set->delta_poc8 = value;
- set->used_flag8 = flag;
- break;
- case 9:
- set->delta_poc9 = value;
- set->used_flag9 = flag;
- break;
- case 10:
- set->delta_poc10 = value;
- set->used_flag10 = flag;
- break;
- case 11:
- set->delta_poc11 = value;
- set->used_flag11 = flag;
- break;
- case 12:
- set->delta_poc12 = value;
- set->used_flag12 = flag;
- break;
- case 13:
- set->delta_poc13 = value;
- set->used_flag13 = flag;
- break;
- case 14:
- set->delta_poc14 = value;
- set->used_flag14 = flag;
- break;
- }
-}
-
static void assemble_scalingfactor0(struct rkvdec_ctx *ctx, u8 *output,
const struct v4l2_ctrl_hevc_scaling_matrix *input)
{
@@ -218,10 +152,11 @@ static void rkvdec_hevc_assemble_hw_lt_rps(struct rkvdec_hevc_run *run, struct r
return;
for (int i = 0; i < sps->num_long_term_ref_pics_sps; i++) {
- rps->refs[i].lt_ref_pic_poc_lsb =
- run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps;
- rps->refs[i].used_by_curr_pic_lt_flag =
- !!(run->ext_sps_lt_rps[i].flags & V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT);
+ rkvdec_set_bw_field(rps->info, RPS_LT_REF_PIC_POC_LSB(i),
+ run->ext_sps_lt_rps[i].lt_ref_pic_poc_lsb_sps);
+ rkvdec_set_bw_field(rps->info, RPS_LT_REF_USED_BY_CURR_PIC(i),
+ !!(run->ext_sps_lt_rps[i].flags &
+ V4L2_HEVC_EXT_SPS_LT_RPS_FLAG_USED_LT));
}
}
@@ -235,18 +170,24 @@ static void rkvdec_hevc_assemble_hw_st_rps(struct rkvdec_hevc_run *run, struct r
int j = 0;
const struct calculated_rps_st_set *set = &calculated_rps_st_sets[i];
- rps->short_term_ref_sets[i].num_negative = set->num_negative_pics;
- rps->short_term_ref_sets[i].num_positive = set->num_positive_pics;
+ rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_NEGATIVE(i),
+ set->num_negative_pics);
+ rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_NUM_POSITIVE(i),
+ set->num_positive_pics);
for (; j < set->num_negative_pics; j++) {
- set_ref_poc(&rps->short_term_ref_sets[i], j,
- set->delta_poc_s0[j], set->used_by_curr_pic_s0[j]);
+ rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, j),
+ set->delta_poc_s0[j]);
+ rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, j),
+ set->used_by_curr_pic_s0[j]);
}
poc = j;
for (j = 0; j < set->num_positive_pics; j++) {
- set_ref_poc(&rps->short_term_ref_sets[i], poc + j,
- set->delta_poc_s1[j], set->used_by_curr_pic_s1[j]);
+ rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_DELTA_POC(i, poc + j),
+ set->delta_poc_s1[j]);
+ rkvdec_set_bw_field(rps->info, RPS_ST_REF_SET_USED(i, poc + j),
+ set->used_by_curr_pic_s1[j]);
}
}
}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h
index 6f4faca4c091..2a9b7719ab2d 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.h
@@ -19,53 +19,24 @@
#include <linux/types.h>
#include "rkvdec.h"
+#include "rkvdec-bitwriter.h"
-struct rkvdec_rps_refs {
- u16 lt_ref_pic_poc_lsb;
- u16 used_by_curr_pic_lt_flag : 1;
- u16 reserved : 15;
-} __packed;
+#define RPS_LT_REF_PIC_POC_LSB(i) BW_FIELD(0 + (i) * 32, 16) // i: 0-31
+#define RPS_LT_REF_USED_BY_CURR_PIC(i) BW_FIELD(16 + (i) * 32, 1) // i: 0-31
-struct rkvdec_rps_short_term_ref_set {
- u32 num_negative : 4;
- u32 num_positive : 4;
- u32 delta_poc0 : 16;
- u32 used_flag0 : 1;
- u32 delta_poc1 : 16;
- u32 used_flag1 : 1;
- u32 delta_poc2 : 16;
- u32 used_flag2 : 1;
- u32 delta_poc3 : 16;
- u32 used_flag3 : 1;
- u32 delta_poc4 : 16;
- u32 used_flag4 : 1;
- u32 delta_poc5 : 16;
- u32 used_flag5 : 1;
- u32 delta_poc6 : 16;
- u32 used_flag6 : 1;
- u32 delta_poc7 : 16;
- u32 used_flag7 : 1;
- u32 delta_poc8 : 16;
- u32 used_flag8 : 1;
- u32 delta_poc9 : 16;
- u32 used_flag9 : 1;
- u32 delta_poc10 : 16;
- u32 used_flag10 : 1;
- u32 delta_poc11 : 16;
- u32 used_flag11 : 1;
- u32 delta_poc12 : 16;
- u32 used_flag12 : 1;
- u32 delta_poc13 : 16;
- u32 used_flag13 : 1;
- u32 delta_poc14 : 16;
- u32 used_flag14 : 1;
- u32 reserved_bits : 25;
- u32 reserved[3];
-} __packed;
+#define RPS_ST_REF_SET_NUM_NEGATIVE(i) BW_FIELD(1024 + ((i) * 384), 4) // i: 0-63
+#define RPS_ST_REF_SET_NUM_POSITIVE(i) BW_FIELD(1028 + ((i) * 384), 4) // i: 0-63
+
+// i: 0-63, j: 0-14
+#define RPS_ST_REF_SET_DELTA_POC(i, j) BW_FIELD(1032 + ((i) * 384) + ((j) * 17), 16)
+
+// i: 0-63, j: 0-14
+#define RPS_ST_REF_SET_USED(i, j) BW_FIELD(1048 + ((i) * 384) + ((j) * 17), 1)
+
+#define RKVDEC_RPS_HEVC_SIZE ALIGN(1032 + 64 * 384, 128)
struct rkvdec_rps {
- struct rkvdec_rps_refs refs[32];
- struct rkvdec_rps_short_term_ref_set short_term_ref_sets[64];
+ u32 info[RKVDEC_RPS_HEVC_SIZE / 8 / 4];
} __packed;
struct rkvdec_hevc_run {
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
index ac8b825d080a..87abf93dfd5e 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
@@ -18,6 +18,7 @@
#include "rkvdec-regs.h"
#include "rkvdec-cabac.h"
#include "rkvdec-hevc-common.h"
+#include "rkvdec-bitwriter.h"
/* Size in u8/u32 units. */
#define RKV_SCALING_LIST_SIZE 1360
@@ -34,80 +35,72 @@ struct rkvdec_rps_packet {
u32 info[RKV_RPS_SIZE];
};
-struct rkvdec_ps_field {
- u16 offset;
- u8 len;
-};
-
-#define PS_FIELD(_offset, _len) \
- ((struct rkvdec_ps_field){ _offset, _len })
-
/* SPS */
-#define VIDEO_PARAMETER_SET_ID PS_FIELD(0, 4)
-#define SEQ_PARAMETER_SET_ID PS_FIELD(4, 4)
-#define CHROMA_FORMAT_IDC PS_FIELD(8, 2)
-#define PIC_WIDTH_IN_LUMA_SAMPLES PS_FIELD(10, 13)
-#define PIC_HEIGHT_IN_LUMA_SAMPLES PS_FIELD(23, 13)
-#define BIT_DEPTH_LUMA PS_FIELD(36, 4)
-#define BIT_DEPTH_CHROMA PS_FIELD(40, 4)
-#define LOG2_MAX_PIC_ORDER_CNT_LSB PS_FIELD(44, 5)
-#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(49, 2)
-#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE PS_FIELD(51, 3)
-#define LOG2_MIN_TRANSFORM_BLOCK_SIZE PS_FIELD(54, 3)
-#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE PS_FIELD(57, 2)
-#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER PS_FIELD(59, 3)
-#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA PS_FIELD(62, 3)
-#define SCALING_LIST_ENABLED_FLAG PS_FIELD(65, 1)
-#define AMP_ENABLED_FLAG PS_FIELD(66, 1)
-#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG PS_FIELD(67, 1)
-#define PCM_ENABLED_FLAG PS_FIELD(68, 1)
-#define PCM_SAMPLE_BIT_DEPTH_LUMA PS_FIELD(69, 4)
-#define PCM_SAMPLE_BIT_DEPTH_CHROMA PS_FIELD(73, 4)
-#define PCM_LOOP_FILTER_DISABLED_FLAG PS_FIELD(77, 1)
-#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(78, 3)
-#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE PS_FIELD(81, 3)
-#define NUM_SHORT_TERM_REF_PIC_SETS PS_FIELD(84, 7)
-#define LONG_TERM_REF_PICS_PRESENT_FLAG PS_FIELD(91, 1)
-#define NUM_LONG_TERM_REF_PICS_SPS PS_FIELD(92, 6)
-#define SPS_TEMPORAL_MVP_ENABLED_FLAG PS_FIELD(98, 1)
-#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG PS_FIELD(99, 1)
+#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4)
+#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4)
+#define CHROMA_FORMAT_IDC BW_FIELD(8, 2)
+#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 13)
+#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(23, 13)
+#define BIT_DEPTH_LUMA BW_FIELD(36, 4)
+#define BIT_DEPTH_CHROMA BW_FIELD(40, 4)
+#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(44, 5)
+#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(49, 2)
+#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(51, 3)
+#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(54, 3)
+#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(57, 2)
+#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(59, 3)
+#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(62, 3)
+#define SCALING_LIST_ENABLED_FLAG BW_FIELD(65, 1)
+#define AMP_ENABLED_FLAG BW_FIELD(66, 1)
+#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(67, 1)
+#define PCM_ENABLED_FLAG BW_FIELD(68, 1)
+#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(69, 4)
+#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(73, 4)
+#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(77, 1)
+#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(78, 3)
+#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(81, 3)
+#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(84, 7)
+#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(91, 1)
+#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(92, 6)
+#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(98, 1)
+#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(99, 1)
/* PPS */
-#define PIC_PARAMETER_SET_ID PS_FIELD(128, 6)
-#define PPS_SEQ_PARAMETER_SET_ID PS_FIELD(134, 4)
-#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG PS_FIELD(138, 1)
-#define OUTPUT_FLAG_PRESENT_FLAG PS_FIELD(139, 1)
-#define NUM_EXTRA_SLICE_HEADER_BITS PS_FIELD(140, 13)
-#define SIGN_DATA_HIDING_ENABLED_FLAG PS_FIELD(153, 1)
-#define CABAC_INIT_PRESENT_FLAG PS_FIELD(154, 1)
-#define NUM_REF_IDX_L0_DEFAULT_ACTIVE PS_FIELD(155, 4)
-#define NUM_REF_IDX_L1_DEFAULT_ACTIVE PS_FIELD(159, 4)
-#define INIT_QP_MINUS26 PS_FIELD(163, 7)
-#define CONSTRAINED_INTRA_PRED_FLAG PS_FIELD(170, 1)
-#define TRANSFORM_SKIP_ENABLED_FLAG PS_FIELD(171, 1)
-#define CU_QP_DELTA_ENABLED_FLAG PS_FIELD(172, 1)
-#define LOG2_MIN_CU_QP_DELTA_SIZE PS_FIELD(173, 3)
-#define PPS_CB_QP_OFFSET PS_FIELD(176, 5)
-#define PPS_CR_QP_OFFSET PS_FIELD(181, 5)
-#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG PS_FIELD(186, 1)
-#define WEIGHTED_PRED_FLAG PS_FIELD(187, 1)
-#define WEIGHTED_BIPRED_FLAG PS_FIELD(188, 1)
-#define TRANSQUANT_BYPASS_ENABLED_FLAG PS_FIELD(189, 1)
-#define TILES_ENABLED_FLAG PS_FIELD(190, 1)
-#define ENTROPY_CODING_SYNC_ENABLED_FLAG PS_FIELD(191, 1)
-#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG PS_FIELD(192, 1)
-#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG PS_FIELD(193, 1)
-#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG PS_FIELD(194, 1)
-#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG PS_FIELD(195, 1)
-#define PPS_BETA_OFFSET_DIV2 PS_FIELD(196, 4)
-#define PPS_TC_OFFSET_DIV2 PS_FIELD(200, 4)
-#define LISTS_MODIFICATION_PRESENT_FLAG PS_FIELD(204, 1)
-#define LOG2_PARALLEL_MERGE_LEVEL PS_FIELD(205, 3)
-#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG PS_FIELD(208, 1)
-#define NUM_TILE_COLUMNS PS_FIELD(212, 5)
-#define NUM_TILE_ROWS PS_FIELD(217, 5)
-#define COLUMN_WIDTH(i) PS_FIELD(256 + ((i) * 8), 8)
-#define ROW_HEIGHT(i) PS_FIELD(416 + ((i) * 8), 8)
-#define SCALING_LIST_ADDRESS PS_FIELD(592, 32)
+#define PIC_PARAMETER_SET_ID BW_FIELD(128, 6)
+#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(134, 4)
+#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(138, 1)
+#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(139, 1)
+#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(140, 13)
+#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(153, 1)
+#define CABAC_INIT_PRESENT_FLAG BW_FIELD(154, 1)
+#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(155, 4)
+#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(159, 4)
+#define INIT_QP_MINUS26 BW_FIELD(163, 7)
+#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(170, 1)
+#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(171, 1)
+#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(172, 1)
+#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(173, 3)
+#define PPS_CB_QP_OFFSET BW_FIELD(176, 5)
+#define PPS_CR_QP_OFFSET BW_FIELD(181, 5)
+#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(186, 1)
+#define WEIGHTED_PRED_FLAG BW_FIELD(187, 1)
+#define WEIGHTED_BIPRED_FLAG BW_FIELD(188, 1)
+#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(189, 1)
+#define TILES_ENABLED_FLAG BW_FIELD(190, 1)
+#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(191, 1)
+#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(192, 1)
+#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(193, 1)
+#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(194, 1)
+#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(195, 1)
+#define PPS_BETA_OFFSET_DIV2 BW_FIELD(196, 4)
+#define PPS_TC_OFFSET_DIV2 BW_FIELD(200, 4)
+#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(204, 1)
+#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(205, 3)
+#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(208, 1)
+#define NUM_TILE_COLUMNS BW_FIELD(212, 5)
+#define NUM_TILE_ROWS BW_FIELD(217, 5)
+#define COLUMN_WIDTH(i) BW_FIELD(256 + ((i) * 8), 8)
+#define ROW_HEIGHT(i) BW_FIELD(416 + ((i) * 8), 8)
+#define SCALING_LIST_ADDRESS BW_FIELD(592, 32)
/* Data structure describing auxiliary buffer format. */
struct rkvdec_hevc_priv_tbl {
@@ -123,20 +116,6 @@ struct rkvdec_hevc_ctx {
struct rkvdec_regs regs;
};
-static void set_ps_field(u32 *buf, struct rkvdec_ps_field field, u32 value)
-{
- u8 bit = field.offset % 32, word = field.offset / 32;
- u64 mask = GENMASK_ULL(bit + field.len - 1, bit);
- u64 val = ((u64)value << bit) & mask;
-
- buf[word] &= ~mask;
- buf[word] |= val;
- if (bit + field.len > 32) {
- buf[word + 1] &= ~(mask >> 32);
- buf[word + 1] |= val >> 32;
- }
-}
-
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
@@ -159,7 +138,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
memset(hw_ps, 0, sizeof(*hw_ps));
-#define WRITE_PPS(value, field) set_ps_field(hw_ps->info, field, value)
+#define WRITE_PPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value)
/* write sps */
WRITE_PPS(sps->video_parameter_set_id, VIDEO_PARAMETER_SET_ID);
WRITE_PPS(sps->seq_parameter_set_id, SEQ_PARAMETER_SET_ID);
@@ -321,17 +300,17 @@ static void assemble_sw_rps(struct rkvdec_ctx *ctx,
int i, j;
unsigned int lowdelay;
-#define WRITE_RPS(value, field) set_ps_field(hw_ps->info, field, value)
+#define WRITE_RPS(value, field) rkvdec_set_bw_field(hw_ps->info, field, value)
-#define REF_PIC_LONG_TERM_L0(i) PS_FIELD((i) * 5, 1)
-#define REF_PIC_IDX_L0(i) PS_FIELD(1 + ((i) * 5), 4)
-#define REF_PIC_LONG_TERM_L1(i) PS_FIELD(((i) < 5 ? 75 : 132) + ((i) * 5), 1)
-#define REF_PIC_IDX_L1(i) PS_FIELD(((i) < 4 ? 76 : 128) + ((i) * 5), 4)
+#define REF_PIC_LONG_TERM_L0(n) BW_FIELD((n) * 5, 1)
+#define REF_PIC_IDX_L0(n) BW_FIELD(1 + ((n) * 5), 4)
+#define REF_PIC_LONG_TERM_L1(n) BW_FIELD(((n) < 5 ? 75 : 132) + ((n) * 5), 1)
+#define REF_PIC_IDX_L1(n) BW_FIELD(((n) < 4 ? 76 : 128) + ((n) * 5), 4)
-#define LOWDELAY PS_FIELD(182, 1)
-#define LONG_TERM_RPS_BIT_OFFSET PS_FIELD(183, 10)
-#define SHORT_TERM_RPS_BIT_OFFSET PS_FIELD(193, 9)
-#define NUM_RPS_POC PS_FIELD(202, 4)
+#define LOWDELAY BW_FIELD(182, 1)
+#define LONG_TERM_RPS_BIT_OFFSET BW_FIELD(183, 10)
+#define SHORT_TERM_RPS_BIT_OFFSET BW_FIELD(193, 9)
+#define NUM_RPS_POC BW_FIELD(202, 4)
for (j = 0; j < run->num_slices; j++) {
uint st_bit_offset = 0;
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
index fb4f849d7366..5ec755733916 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-h264.c
@@ -15,105 +15,64 @@
#include "rkvdec-cabac.h"
#include "rkvdec-vdpu383-regs.h"
#include "rkvdec-h264-common.h"
-
-struct rkvdec_sps {
- u16 seq_parameter_set_id: 4;
- u16 profile_idc: 8;
- u16 constraint_set3_flag: 1;
- u16 chroma_format_idc: 2;
- u16 bit_depth_luma: 3;
- u16 bit_depth_chroma: 3;
- u16 qpprime_y_zero_transform_bypass_flag: 1;
- u16 log2_max_frame_num_minus4: 4;
- u16 max_num_ref_frames: 5;
- u16 pic_order_cnt_type: 2;
- u16 log2_max_pic_order_cnt_lsb_minus4: 4;
- u16 delta_pic_order_always_zero_flag: 1;
-
- u16 pic_width_in_mbs: 16;
- u16 pic_height_in_mbs: 16;
-
- u16 frame_mbs_only_flag: 1;
- u16 mb_adaptive_frame_field_flag: 1;
- u16 direct_8x8_inference_flag: 1;
- u16 mvc_extension_enable: 1;
- u16 num_views: 2;
- u16 view_id0: 10;
- u16 view_id1: 10;
-} __packed;
-
-struct rkvdec_pps {
- u32 pic_parameter_set_id: 8;
- u32 pps_seq_parameter_set_id: 5;
- u32 entropy_coding_mode_flag: 1;
- u32 bottom_field_pic_order_in_frame_present_flag: 1;
- u32 num_ref_idx_l0_default_active_minus1: 5;
- u32 num_ref_idx_l1_default_active_minus1: 5;
- u32 weighted_pred_flag: 1;
- u32 weighted_bipred_idc: 2;
- u32 pic_init_qp_minus26: 7;
- u32 pic_init_qs_minus26: 6;
- u32 chroma_qp_index_offset: 5;
- u32 deblocking_filter_control_present_flag: 1;
- u32 constrained_intra_pred_flag: 1;
- u32 redundant_pic_cnt_present: 1;
- u32 transform_8x8_mode_flag: 1;
- u32 second_chroma_qp_index_offset: 5;
- u32 scaling_list_enable_flag: 1;
- u32 is_longterm: 16;
- u32 voidx: 16;
-
- // dpb
- u32 pic_field_flag: 1;
- u32 pic_associated_flag: 1;
- u32 cur_top_field: 32;
- u32 cur_bot_field: 32;
-
- u32 top_field_order_cnt0: 32;
- u32 bot_field_order_cnt0: 32;
- u32 top_field_order_cnt1: 32;
- u32 bot_field_order_cnt1: 32;
- u32 top_field_order_cnt2: 32;
- u32 bot_field_order_cnt2: 32;
- u32 top_field_order_cnt3: 32;
- u32 bot_field_order_cnt3: 32;
- u32 top_field_order_cnt4: 32;
- u32 bot_field_order_cnt4: 32;
- u32 top_field_order_cnt5: 32;
- u32 bot_field_order_cnt5: 32;
- u32 top_field_order_cnt6: 32;
- u32 bot_field_order_cnt6: 32;
- u32 top_field_order_cnt7: 32;
- u32 bot_field_order_cnt7: 32;
- u32 top_field_order_cnt8: 32;
- u32 bot_field_order_cnt8: 32;
- u32 top_field_order_cnt9: 32;
- u32 bot_field_order_cnt9: 32;
- u32 top_field_order_cnt10: 32;
- u32 bot_field_order_cnt10: 32;
- u32 top_field_order_cnt11: 32;
- u32 bot_field_order_cnt11: 32;
- u32 top_field_order_cnt12: 32;
- u32 bot_field_order_cnt12: 32;
- u32 top_field_order_cnt13: 32;
- u32 bot_field_order_cnt13: 32;
- u32 top_field_order_cnt14: 32;
- u32 bot_field_order_cnt14: 32;
- u32 top_field_order_cnt15: 32;
- u32 bot_field_order_cnt15: 32;
-
- u32 ref_field_flags: 16;
- u32 ref_topfield_used: 16;
- u32 ref_botfield_used: 16;
- u32 ref_colmv_use_flag: 16;
-
- u32 reserved0: 30;
- u32 reserved[3];
-} __packed;
+#include "rkvdec-bitwriter.h"
+
+#define SEQ_PARAMETER_SET_ID BW_FIELD(0, 4)
+#define PROFILE_IDC BW_FIELD(4, 8)
+#define CONSTRAINT_SET3_FLAG BW_FIELD(12, 1)
+#define CHROMA_FORMAT_IDC BW_FIELD(13, 2)
+#define BIT_DEPTH_LUMA BW_FIELD(15, 3)
+#define BIT_DEPTH_CHROMA BW_FIELD(18, 3)
+#define QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG BW_FIELD(21, 1)
+#define LOG2_MAX_FRAME_NUM_MINUS4 BW_FIELD(22, 4)
+#define MAX_NUM_REF_FRAMES BW_FIELD(26, 5)
+#define PIC_ORDER_CNT_TYPE BW_FIELD(31, 2)
+#define LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4 BW_FIELD(33, 4)
+#define DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG BW_FIELD(37, 1)
+#define PIC_WIDTH_IN_MBS BW_FIELD(38, 16)
+#define PIC_HEIGHT_IN_MBS BW_FIELD(54, 16)
+#define FRAME_MBS_ONLY_FLAG BW_FIELD(70, 1)
+#define MB_ADAPTIVE_FRAME_FIELD_FLAG BW_FIELD(71, 1)
+#define DIRECT_8X8_INFERENCE_FLAG BW_FIELD(72, 1)
+#define MVC_EXTENSION_ENABLE BW_FIELD(73, 1)
+#define NUM_VIEWS BW_FIELD(74, 2)
+#define VIEW_ID(i) BW_FIELD(76 + ((i) * 10), 10) // i: 0-1
+
+#define PIC_PARAMETER_SET_ID BW_FIELD(96, 8)
+#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(104, 5)
+#define ENTROPY_CODING_MODE_FLAG BW_FIELD(109, 1)
+#define BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG BW_FIELD(110, 1)
+#define NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(i) BW_FIELD(111 + ((i) * 5), 5) // i: 0-1
+#define WEIGHTED_PRED_FLAG BW_FIELD(121, 1)
+#define WEIGHTED_BIPRED_IDC BW_FIELD(122, 2)
+#define PIC_INIT_QP_MINUS26 BW_FIELD(124, 7)
+#define PIC_INIT_QS_MINUS26 BW_FIELD(131, 6)
+#define CHROMA_QP_INDEX_OFFSET BW_FIELD(137, 5)
+#define DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG BW_FIELD(142, 1)
+#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(143, 1)
+#define REDUNDANT_PIC_CNT_PRESENT BW_FIELD(144, 1)
+#define TRANSFORM_8X8_MODE_FLAG BW_FIELD(145, 1)
+#define SECOND_CHROMA_QP_INDEX_OFFSET BW_FIELD(146, 5)
+#define SCALING_LIST_ENABLE_FLAG BW_FIELD(151, 1)
+#define IS_LONG_TERM(i) BW_FIELD(152 + (i), 1) // i: 0-15
+
+#define PIC_FIELD_FLAG BW_FIELD(184, 1)
+#define PIC_ASSOCIATED_FLAG BW_FIELD(185, 1)
+#define CUR_TOP_FIELD BW_FIELD(186, 32)
+#define CUR_BOT_FIELD BW_FIELD(218, 32)
+
+#define TOP_FIELD_ORDER_CNT(i) BW_FIELD(250 + (i) * 64, 32) // i: 0-15
+#define BOT_FIELD_ORDER_CNT(i) BW_FIELD(282 + (i) * 64, 32) // i: 0-15
+
+#define REF_FIELD_FLAGS(i) BW_FIELD(1274 + (i), 1) // i: 0-15
+#define REF_TOPFIELD_USED(i) BW_FIELD(1290 + (i), 1) // i: 0-15
+#define REF_BOTFIELD_USED(i) BW_FIELD(1306 + (i), 1) // i: 0-15
+#define REF_COLMV_USE_FLAG(i) BW_FIELD(1322 + (i), 1) // i: 0-15
+
+#define SPS_SIZE ALIGN(1322 + 16, 128)
struct rkvdec_sps_pps {
- struct rkvdec_sps sps;
- struct rkvdec_pps pps;
+ u32 info[SPS_SIZE / 8 / 4];
} __packed;
/* Data structure describing auxiliary buffer format. */
@@ -130,67 +89,6 @@ struct rkvdec_h264_ctx {
struct vdpu383_regs_h26x regs;
};
-static noinline_for_stack void set_field_order_cnt(struct rkvdec_pps *pps, const struct v4l2_h264_dpb_entry *dpb)
-{
- pps->top_field_order_cnt0 = dpb[0].top_field_order_cnt;
- pps->bot_field_order_cnt0 = dpb[0].bottom_field_order_cnt;
- pps->top_field_order_cnt1 = dpb[1].top_field_order_cnt;
- pps->bot_field_order_cnt1 = dpb[1].bottom_field_order_cnt;
- pps->top_field_order_cnt2 = dpb[2].top_field_order_cnt;
- pps->bot_field_order_cnt2 = dpb[2].bottom_field_order_cnt;
- pps->top_field_order_cnt3 = dpb[3].top_field_order_cnt;
- pps->bot_field_order_cnt3 = dpb[3].bottom_field_order_cnt;
- pps->top_field_order_cnt4 = dpb[4].top_field_order_cnt;
- pps->bot_field_order_cnt4 = dpb[4].bottom_field_order_cnt;
- pps->top_field_order_cnt5 = dpb[5].top_field_order_cnt;
- pps->bot_field_order_cnt5 = dpb[5].bottom_field_order_cnt;
- pps->top_field_order_cnt6 = dpb[6].top_field_order_cnt;
- pps->bot_field_order_cnt6 = dpb[6].bottom_field_order_cnt;
- pps->top_field_order_cnt7 = dpb[7].top_field_order_cnt;
- pps->bot_field_order_cnt7 = dpb[7].bottom_field_order_cnt;
- pps->top_field_order_cnt8 = dpb[8].top_field_order_cnt;
- pps->bot_field_order_cnt8 = dpb[8].bottom_field_order_cnt;
- pps->top_field_order_cnt9 = dpb[9].top_field_order_cnt;
- pps->bot_field_order_cnt9 = dpb[9].bottom_field_order_cnt;
- pps->top_field_order_cnt10 = dpb[10].top_field_order_cnt;
- pps->bot_field_order_cnt10 = dpb[10].bottom_field_order_cnt;
- pps->top_field_order_cnt11 = dpb[11].top_field_order_cnt;
- pps->bot_field_order_cnt11 = dpb[11].bottom_field_order_cnt;
- pps->top_field_order_cnt12 = dpb[12].top_field_order_cnt;
- pps->bot_field_order_cnt12 = dpb[12].bottom_field_order_cnt;
- pps->top_field_order_cnt13 = dpb[13].top_field_order_cnt;
- pps->bot_field_order_cnt13 = dpb[13].bottom_field_order_cnt;
- pps->top_field_order_cnt14 = dpb[14].top_field_order_cnt;
- pps->bot_field_order_cnt14 = dpb[14].bottom_field_order_cnt;
- pps->top_field_order_cnt15 = dpb[15].top_field_order_cnt;
- pps->bot_field_order_cnt15 = dpb[15].bottom_field_order_cnt;
-}
-
-static noinline_for_stack void set_dec_params(struct rkvdec_pps *pps, const struct v4l2_ctrl_h264_decode_params *dec_params)
-{
- const struct v4l2_h264_dpb_entry *dpb = dec_params->dpb;
-
- for (int i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
- if (dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM)
- pps->is_longterm |= (1 << i);
- pps->ref_field_flags |=
- (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD)) << i;
- pps->ref_colmv_use_flag |=
- (!!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE)) << i;
- pps->ref_topfield_used |=
- (!!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF)) << i;
- pps->ref_botfield_used |=
- (!!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF)) << i;
- }
- pps->pic_field_flag =
- !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC);
- pps->pic_associated_flag =
- !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD);
-
- pps->cur_top_field = dec_params->top_field_order_cnt;
- pps->cur_bot_field = dec_params->bottom_field_order_cnt;
-}
-
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_h264_run *run)
{
@@ -202,6 +100,7 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_h264_priv_tbl *priv_tbl = h264_ctx->priv_tbl.cpu;
struct rkvdec_sps_pps *hw_ps;
u32 pic_width, pic_height;
+ int i;
/*
* HW read the SPS/PPS information from PPS packet index by PPS id.
@@ -213,23 +112,25 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
memset(hw_ps, 0, sizeof(*hw_ps));
/* write sps */
- hw_ps->sps.seq_parameter_set_id = sps->seq_parameter_set_id;
- hw_ps->sps.profile_idc = sps->profile_idc;
- hw_ps->sps.constraint_set3_flag = !!(sps->constraint_set_flags & (1 << 3));
- hw_ps->sps.chroma_format_idc = sps->chroma_format_idc;
- hw_ps->sps.bit_depth_luma = sps->bit_depth_luma_minus8;
- hw_ps->sps.bit_depth_chroma = sps->bit_depth_chroma_minus8;
- hw_ps->sps.qpprime_y_zero_transform_bypass_flag =
- !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS);
- hw_ps->sps.log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4;
- hw_ps->sps.max_num_ref_frames = sps->max_num_ref_frames;
- hw_ps->sps.pic_order_cnt_type = sps->pic_order_cnt_type;
- hw_ps->sps.log2_max_pic_order_cnt_lsb_minus4 =
- sps->log2_max_pic_order_cnt_lsb_minus4;
- hw_ps->sps.delta_pic_order_always_zero_flag =
- !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO);
- hw_ps->sps.mvc_extension_enable = 0;
- hw_ps->sps.num_views = 0;
+ rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id);
+ rkvdec_set_bw_field(hw_ps->info, PROFILE_IDC, sps->profile_idc);
+ rkvdec_set_bw_field(hw_ps->info, CONSTRAINT_SET3_FLAG,
+ !!(sps->constraint_set_flags & (1 << 3)));
+ rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc);
+ rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8);
+ rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8);
+ rkvdec_set_bw_field(hw_ps->info, QPPRIME_Y_ZERO_TRANSFORM_BYPASS_FLAG,
+ !!(sps->flags & V4L2_H264_SPS_FLAG_QPPRIME_Y_ZERO_TRANSFORM_BYPASS));
+ rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_FRAME_NUM_MINUS4,
+ sps->log2_max_frame_num_minus4);
+ rkvdec_set_bw_field(hw_ps->info, MAX_NUM_REF_FRAMES, sps->max_num_ref_frames);
+ rkvdec_set_bw_field(hw_ps->info, PIC_ORDER_CNT_TYPE, sps->pic_order_cnt_type);
+ rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB_MINUS4,
+ sps->log2_max_pic_order_cnt_lsb_minus4);
+ rkvdec_set_bw_field(hw_ps->info, DELTA_PIC_ORDER_ALWAYS_ZERO_FLAG,
+ !!(sps->flags & V4L2_H264_SPS_FLAG_DELTA_PIC_ORDER_ALWAYS_ZERO));
+ rkvdec_set_bw_field(hw_ps->info, MVC_EXTENSION_ENABLE, 0);
+ rkvdec_set_bw_field(hw_ps->info, NUM_VIEWS, 0);
/*
* Use the SPS values since they are already in macroblocks
@@ -245,48 +146,72 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
if (!!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC))
pic_height /= 2;
- hw_ps->sps.pic_width_in_mbs = pic_width;
- hw_ps->sps.pic_height_in_mbs = pic_height;
+ rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_MBS, pic_width);
+ rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_MBS, pic_height);
- hw_ps->sps.frame_mbs_only_flag =
- !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY);
- hw_ps->sps.mb_adaptive_frame_field_flag =
- !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD);
- hw_ps->sps.direct_8x8_inference_flag =
- !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE);
+ rkvdec_set_bw_field(hw_ps->info, FRAME_MBS_ONLY_FLAG,
+ !!(sps->flags & V4L2_H264_SPS_FLAG_FRAME_MBS_ONLY));
+ rkvdec_set_bw_field(hw_ps->info, MB_ADAPTIVE_FRAME_FIELD_FLAG,
+ !!(sps->flags & V4L2_H264_SPS_FLAG_MB_ADAPTIVE_FRAME_FIELD));
+ rkvdec_set_bw_field(hw_ps->info, DIRECT_8X8_INFERENCE_FLAG,
+ !!(sps->flags & V4L2_H264_SPS_FLAG_DIRECT_8X8_INFERENCE));
/* write pps */
- hw_ps->pps.pic_parameter_set_id = pps->pic_parameter_set_id;
- hw_ps->pps.pps_seq_parameter_set_id = pps->seq_parameter_set_id;
- hw_ps->pps.entropy_coding_mode_flag =
- !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE);
- hw_ps->pps.bottom_field_pic_order_in_frame_present_flag =
- !!(pps->flags & V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT);
- hw_ps->pps.num_ref_idx_l0_default_active_minus1 =
- pps->num_ref_idx_l0_default_active_minus1;
- hw_ps->pps.num_ref_idx_l1_default_active_minus1 =
- pps->num_ref_idx_l1_default_active_minus1;
- hw_ps->pps.weighted_pred_flag =
- !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED);
- hw_ps->pps.weighted_bipred_idc = pps->weighted_bipred_idc;
- hw_ps->pps.pic_init_qp_minus26 = pps->pic_init_qp_minus26;
- hw_ps->pps.pic_init_qs_minus26 = pps->pic_init_qs_minus26;
- hw_ps->pps.chroma_qp_index_offset = pps->chroma_qp_index_offset;
- hw_ps->pps.deblocking_filter_control_present_flag =
- !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT);
- hw_ps->pps.constrained_intra_pred_flag =
- !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED);
- hw_ps->pps.redundant_pic_cnt_present =
- !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT);
- hw_ps->pps.transform_8x8_mode_flag =
- !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE);
- hw_ps->pps.second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset;
- hw_ps->pps.scaling_list_enable_flag =
- !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT);
-
- set_field_order_cnt(&hw_ps->pps, dpb);
- set_dec_params(&hw_ps->pps, dec_params);
+ rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id);
+ rkvdec_set_bw_field(hw_ps->info, PPS_SEQ_PARAMETER_SET_ID, pps->seq_parameter_set_id);
+ rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_MODE_FLAG,
+ !!(pps->flags & V4L2_H264_PPS_FLAG_ENTROPY_CODING_MODE));
+ rkvdec_set_bw_field(hw_ps->info, BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT_FLAG,
+ !!(pps->flags &
+ V4L2_H264_PPS_FLAG_BOTTOM_FIELD_PIC_ORDER_IN_FRAME_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(0),
+ pps->num_ref_idx_l0_default_active_minus1);
+ rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L_DEFAULT_ACTIVE_MINUS1(1),
+ pps->num_ref_idx_l1_default_active_minus1);
+ rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG,
+ !!(pps->flags & V4L2_H264_PPS_FLAG_WEIGHTED_PRED));
+ rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_IDC, pps->weighted_bipred_idc);
+ rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QP_MINUS26, pps->pic_init_qp_minus26);
+ rkvdec_set_bw_field(hw_ps->info, PIC_INIT_QS_MINUS26, pps->pic_init_qs_minus26);
+ rkvdec_set_bw_field(hw_ps->info, CHROMA_QP_INDEX_OFFSET, pps->chroma_qp_index_offset);
+ rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_CONTROL_PRESENT_FLAG,
+ !!(pps->flags & V4L2_H264_PPS_FLAG_DEBLOCKING_FILTER_CONTROL_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG,
+ !!(pps->flags & V4L2_H264_PPS_FLAG_CONSTRAINED_INTRA_PRED));
+ rkvdec_set_bw_field(hw_ps->info, REDUNDANT_PIC_CNT_PRESENT,
+ !!(pps->flags & V4L2_H264_PPS_FLAG_REDUNDANT_PIC_CNT_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, TRANSFORM_8X8_MODE_FLAG,
+ !!(pps->flags & V4L2_H264_PPS_FLAG_TRANSFORM_8X8_MODE));
+ rkvdec_set_bw_field(hw_ps->info, SECOND_CHROMA_QP_INDEX_OFFSET,
+ pps->second_chroma_qp_index_offset);
+ rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLE_FLAG,
+ !!(pps->flags & V4L2_H264_PPS_FLAG_SCALING_MATRIX_PRESENT));
+
+ for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
+ rkvdec_set_bw_field(hw_ps->info, TOP_FIELD_ORDER_CNT(i),
+ dpb[i].top_field_order_cnt);
+ rkvdec_set_bw_field(hw_ps->info, BOT_FIELD_ORDER_CNT(i),
+ dpb[i].bottom_field_order_cnt);
+
+ rkvdec_set_bw_field(hw_ps->info, IS_LONG_TERM(i),
+ !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_LONG_TERM));
+ rkvdec_set_bw_field(hw_ps->info, REF_FIELD_FLAGS(i),
+ !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_FIELD));
+ rkvdec_set_bw_field(hw_ps->info, REF_COLMV_USE_FLAG(i),
+ !!(dpb[i].flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE));
+ rkvdec_set_bw_field(hw_ps->info, REF_TOPFIELD_USED(i),
+ !!(dpb[i].fields & V4L2_H264_TOP_FIELD_REF));
+ rkvdec_set_bw_field(hw_ps->info, REF_BOTFIELD_USED(i),
+ !!(dpb[i].fields & V4L2_H264_BOTTOM_FIELD_REF));
+ }
+
+ rkvdec_set_bw_field(hw_ps->info, PIC_FIELD_FLAG,
+ !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_FIELD_PIC));
+ rkvdec_set_bw_field(hw_ps->info, PIC_ASSOCIATED_FLAG,
+ !!(dec_params->flags & V4L2_H264_DECODE_PARAM_FLAG_BOTTOM_FIELD));
+ rkvdec_set_bw_field(hw_ps->info, CUR_TOP_FIELD, dec_params->top_field_order_cnt);
+ rkvdec_set_bw_field(hw_ps->info, CUR_BOT_FIELD, dec_params->bottom_field_order_cnt);
}
static void rkvdec_write_regs(struct rkvdec_ctx *ctx)
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
index 96d938ee70b0..3575338a531a 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu383-hevc.c
@@ -13,149 +13,106 @@
#include "rkvdec-rcb.h"
#include "rkvdec-hevc-common.h"
#include "rkvdec-vdpu383-regs.h"
+#include "rkvdec-bitwriter.h"
+
+#define VIDEO_PARAMETER_SET_ID BW_FIELD(0, 4)
+#define SEQ_PARAMETER_SET_ID BW_FIELD(4, 4)
+#define CHROMA_FORMAT_IDC BW_FIELD(8, 2)
+#define PIC_WIDTH_IN_LUMA_SAMPLES BW_FIELD(10, 16)
+#define PIC_HEIGHT_IN_LUMA_SAMPLES BW_FIELD(26, 16)
+#define BIT_DEPTH_LUMA BW_FIELD(42, 3)
+#define BIT_DEPTH_CHROMA BW_FIELD(45, 3)
+#define LOG2_MAX_PIC_ORDER_CNT_LSB BW_FIELD(48, 5)
+#define LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(53, 2)
+#define LOG2_MIN_LUMA_CODING_BLOCK_SIZE BW_FIELD(55, 3)
+#define LOG2_MIN_TRANSFORM_BLOCK_SIZE BW_FIELD(58, 3)
+#define LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE BW_FIELD(61, 2)
+#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTER BW_FIELD(63, 3)
+#define MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA BW_FIELD(66, 3)
+#define SCALING_LIST_ENABLED_FLAG BW_FIELD(69, 1)
+#define AMP_ENABLED_FLAG BW_FIELD(70, 1)
+#define SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG BW_FIELD(71, 1)
+#define PCM_ENABLED_FLAG BW_FIELD(72, 1)
+#define PCM_SAMPLE_BIT_DEPTH_LUMA BW_FIELD(73, 4)
+#define PCM_SAMPLE_BIT_DEPTH_CHROMA BW_FIELD(77, 4)
+#define PCM_LOOP_FILTER_DISABLED_FLAG BW_FIELD(81, 1)
+#define LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(82, 3)
+#define LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE BW_FIELD(85, 3)
+#define NUM_SHORT_TERM_REF_PIC_SETS BW_FIELD(88, 7)
+#define LONG_TERM_REF_PICS_PRESENT_FLAG BW_FIELD(95, 1)
+#define NUM_LONG_TERM_REF_PICS_SPS BW_FIELD(96, 6)
+#define SPS_TEMPORAL_MVP_ENABLED_FLAG BW_FIELD(102, 1)
+#define STRONG_INTRA_SMOOTHING_ENABLED_FLAG BW_FIELD(103, 1)
+#define SPS_MAX_DEC_PIC_BUFFERING_MINUS1 BW_FIELD(111, 4)
+#define SEPARATE_COLOUR_PLANE_FLAG BW_FIELD(115, 1)
+#define HIGH_PRECISION_OFFSETS_ENABLED_FLAG BW_FIELD(116, 1)
+#define PERSISTENT_RICE_ADAPTATION_ENABLED_FLAG BW_FIELD(117, 1)
+
+/* PPS */
+#define PIC_PARAMETER_SET_ID BW_FIELD(118, 6)
+#define PPS_SEQ_PARAMETER_SET_ID BW_FIELD(124, 4)
+#define DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG BW_FIELD(128, 1)
+#define OUTPUT_FLAG_PRESENT_FLAG BW_FIELD(129, 1)
+#define NUM_EXTRA_SLICE_HEADER_BITS BW_FIELD(130, 13)
+#define SIGN_DATA_HIDING_ENABLED_FLAG BW_FIELD(143, 1)
+#define CABAC_INIT_PRESENT_FLAG BW_FIELD(144, 1)
+#define NUM_REF_IDX_L0_DEFAULT_ACTIVE BW_FIELD(145, 4)
+#define NUM_REF_IDX_L1_DEFAULT_ACTIVE BW_FIELD(149, 4)
+#define INIT_QP_MINUS26 BW_FIELD(153, 7)
+#define CONSTRAINED_INTRA_PRED_FLAG BW_FIELD(160, 1)
+#define TRANSFORM_SKIP_ENABLED_FLAG BW_FIELD(161, 1)
+#define CU_QP_DELTA_ENABLED_FLAG BW_FIELD(162, 1)
+#define LOG2_MIN_CU_QP_DELTA_SIZE BW_FIELD(163, 3)
+#define PPS_CB_QP_OFFSET BW_FIELD(166, 5)
+#define PPS_CR_QP_OFFSET BW_FIELD(171, 5)
+#define PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG BW_FIELD(176, 1)
+#define WEIGHTED_PRED_FLAG BW_FIELD(177, 1)
+#define WEIGHTED_BIPRED_FLAG BW_FIELD(178, 1)
+#define TRANSQUANT_BYPASS_ENABLED_FLAG BW_FIELD(179, 1)
+#define TILES_ENABLED_FLAG BW_FIELD(180, 1)
+#define ENTROPY_CODING_SYNC_ENABLED_FLAG BW_FIELD(181, 1)
+#define PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG BW_FIELD(182, 1)
+#define LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG BW_FIELD(183, 1)
+#define DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG BW_FIELD(184, 1)
+#define PPS_DEBLOCKING_FILTER_DISABLED_FLAG BW_FIELD(185, 1)
+#define PPS_BETA_OFFSET_DIV2 BW_FIELD(186, 4)
+#define PPS_TC_OFFSET_DIV2 BW_FIELD(190, 4)
+#define LISTS_MODIFICATION_PRESENT_FLAG BW_FIELD(194, 1)
+#define LOG2_PARALLEL_MERGE_LEVEL BW_FIELD(195, 3)
+#define SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG BW_FIELD(198, 1)
+
+/* pps extensions */
+#define LOG2_MAX_TRANSFORM_SKIP_BLOCK_SIZE BW_FIELD(202, 2)
+#define CROSS_COMPONENT_PREDICTION_ENABLED_FLAG BW_FIELD(204, 1)
+#define CHROMA_QP_OFFSET_LIST_ENABLED_FLAG BW_FIELD(205, 1)
+#define LOG2_MIN_CU_CHROMA_QP_DELTA_SIZE BW_FIELD(206, 3)
+#define CB_QP_OFFSET_LIST(i) BW_FIELD(209 + (i) * 5, 5) // i: 0-5
+#define CB_CR_OFFSET_LIST(i) BW_FIELD(239 + (i) * 5, 5) // i: 0-5
+#define CHROMA_QP_OFFSET_LIST_LEN_MINUS1 BW_FIELD(269, 3)
+
+/* mvc0 && mvc1 */
+#define MVC_FF BW_FIELD(272, 16)
+#define MVC_00 BW_FIELD(288, 9)
+
+/* poc info */
+#define RESERVED2 BW_FIELD(297, 3)
+#define CURRENT_POC BW_FIELD(300, 32)
+#define REF_PIC_POC(i) BW_FIELD(332 + (i) * 32, 32) // i: 0-14
+#define RESERVED3 BW_FIELD(812, 32)
+#define REF_IS_VALID(i) BW_FIELD(844 + (i), 1) // i: 0-14
+#define RESERVED4 BW_FIELD(859, 1)
+
+/* tile info*/
+#define NUM_TILE_COLUMNS BW_FIELD(860, 5)
+#define NUM_TILE_ROWS BW_FIELD(865, 5)
+#define COLUMN_WIDTH(i) BW_FIELD(870 + (i) * 12, 12) // i: 0-19
+#define ROW_HEIGHT(i) BW_FIELD(1110 + (i) * 12, 12) // i: 0-21
+
+#define HEVC_SPS_SIZE ALIGN(1110 + 22 * 12, 256)
struct rkvdec_hevc_sps_pps {
- // SPS
- u16 video_parameters_set_id : 4;
- u16 seq_parameters_set_id_sps : 4;
- u16 chroma_format_idc : 2;
- u16 width : 16;
- u16 height : 16;
- u16 bit_depth_luma : 3;
- u16 bit_depth_chroma : 3;
- u16 max_pic_order_count_lsb : 5;
- u16 diff_max_min_luma_coding_block_size : 2;
- u16 min_luma_coding_block_size : 3;
- u16 min_transform_block_size : 3;
- u16 diff_max_min_transform_block_size : 2;
- u16 max_transform_hierarchy_depth_inter : 3;
- u16 max_transform_hierarchy_depth_intra : 3;
- u16 scaling_list_enabled_flag : 1;
- u16 amp_enabled_flag : 1;
- u16 sample_adaptive_offset_enabled_flag : 1;
- u16 pcm_enabled_flag : 1;
- u16 pcm_sample_bit_depth_luma : 4;
- u16 pcm_sample_bit_depth_chroma : 4;
- u16 pcm_loop_filter_disabled_flag : 1;
- u16 diff_max_min_pcm_luma_coding_block_size : 3;
- u16 min_pcm_luma_coding_block_size : 3;
- u16 num_short_term_ref_pic_sets : 7;
- u16 long_term_ref_pics_present_flag : 1;
- u16 num_long_term_ref_pics_sps : 6;
- u16 sps_temporal_mvp_enabled_flag : 1;
- u16 strong_intra_smoothing_enabled_flag : 1;
- u16 reserved0 : 7;
- u16 sps_max_dec_pic_buffering_minus1 : 4;
- u16 separate_colour_plane_flag : 1;
- u16 high_precision_offsets_enabled_flag : 1;
- u16 persistent_rice_adaptation_enabled_flag : 1;
-
- // PPS
- u16 picture_parameters_set_id : 6;
- u16 seq_parameters_set_id_pps : 4;
- u16 dependent_slice_segments_enabled_flag : 1;
- u16 output_flag_present_flag : 1;
- u16 num_extra_slice_header_bits : 13;
- u16 sign_data_hiding_enabled_flag : 1;
- u16 cabac_init_present_flag : 1;
- u16 num_ref_idx_l0_default_active : 4;
- u16 num_ref_idx_l1_default_active : 4;
- u16 init_qp_minus26 : 7;
- u16 constrained_intra_pred_flag : 1;
- u16 transform_skip_enabled_flag : 1;
- u16 cu_qp_delta_enabled_flag : 1;
- u16 log2_min_cb_size : 3;
- u16 pps_cb_qp_offset : 5;
- u16 pps_cr_qp_offset : 5;
- u16 pps_slice_chroma_qp_offsets_present_flag : 1;
- u16 weighted_pred_flag : 1;
- u16 weighted_bipred_flag : 1;
- u16 transquant_bypass_enabled_flag : 1;
- u16 tiles_enabled_flag : 1;
- u16 entropy_coding_sync_enabled_flag : 1;
- u16 pps_loop_filter_across_slices_enabled_flag : 1;
- u16 loop_filter_across_tiles_enabled_flag : 1;
- u16 deblocking_filter_override_enabled_flag : 1;
- u16 pps_deblocking_filter_disabled_flag : 1;
- u16 pps_beta_offset_div2 : 4;
- u16 pps_tc_offset_div2 : 4;
- u16 lists_modification_present_flag : 1;
- u16 log2_parallel_merge_level : 3;
- u16 slice_segment_header_extension_present_flag : 1;
- u16 reserved1 : 3;
-
- // pps extensions
- u16 log2_max_transform_skip_block_size : 2;
- u16 cross_component_prediction_enabled_flag : 1;
- u16 chroma_qp_offset_list_enabled_flag : 1;
- u16 log2_min_cu_chroma_qp_delta_size : 3;
- u16 cb_qp_offset_list0 : 5;
- u16 cb_qp_offset_list1 : 5;
- u16 cb_qp_offset_list2 : 5;
- u16 cb_qp_offset_list3 : 5;
- u16 cb_qp_offset_list4 : 5;
- u16 cb_qp_offset_list5 : 5;
- u16 cb_cr_offset_list0 : 5;
- u16 cb_cr_offset_list1 : 5;
- u16 cb_cr_offset_list2 : 5;
- u16 cb_cr_offset_list3 : 5;
- u16 cb_cr_offset_list4 : 5;
- u16 cb_cr_offset_list5 : 5;
- u16 chroma_qp_offset_list_len_minus1 : 3;
-
- /* mvc0 && mvc1 */
- u16 mvc_ff : 16;
- u16 mvc_00 : 9;
-
- /* poc info */
- u16 reserved2 : 3;
- u32 current_poc : 32;
- u32 ref_pic_poc0 : 32;
- u32 ref_pic_poc1 : 32;
- u32 ref_pic_poc2 : 32;
- u32 ref_pic_poc3 : 32;
- u32 ref_pic_poc4 : 32;
- u32 ref_pic_poc5 : 32;
- u32 ref_pic_poc6 : 32;
- u32 ref_pic_poc7 : 32;
- u32 ref_pic_poc8 : 32;
- u32 ref_pic_poc9 : 32;
- u32 ref_pic_poc10 : 32;
- u32 ref_pic_poc11 : 32;
- u32 ref_pic_poc12 : 32;
- u32 ref_pic_poc13 : 32;
- u32 ref_pic_poc14 : 32;
- u32 reserved3 : 32;
- u32 ref_is_valid : 15;
- u32 reserved4 : 1;
-
- /* tile info*/
- u16 num_tile_columns : 5;
- u16 num_tile_rows : 5;
- u32 column_width0 : 24;
- u32 column_width1 : 24;
- u32 column_width2 : 24;
- u32 column_width3 : 24;
- u32 column_width4 : 24;
- u32 column_width5 : 24;
- u32 column_width6 : 24;
- u32 column_width7 : 24;
- u32 column_width8 : 24;
- u32 column_width9 : 24;
- u32 row_height0 : 24;
- u32 row_height1 : 24;
- u32 row_height2 : 24;
- u32 row_height3 : 24;
- u32 row_height4 : 24;
- u32 row_height5 : 24;
- u32 row_height6 : 24;
- u32 row_height7 : 24;
- u32 row_height8 : 24;
- u32 row_height9 : 24;
- u32 row_height10 : 24;
- u32 reserved5 : 2;
- u32 padding;
-} __packed;
+ u32 info[HEVC_SPS_SIZE / 8 / 4];
+};
struct rkvdec_hevc_priv_tbl {
struct rkvdec_hevc_sps_pps param_set;
@@ -171,51 +128,6 @@ struct rkvdec_hevc_ctx {
struct vdpu383_regs_h26x regs;
};
-static void set_column_row(struct rkvdec_hevc_sps_pps *hw_ps, u16 *column, u16 *row)
-{
- hw_ps->column_width0 = column[0] | (column[1] << 12);
- hw_ps->row_height0 = row[0] | (row[1] << 12);
- hw_ps->column_width1 = column[2] | (column[3] << 12);
- hw_ps->row_height1 = row[2] | (row[3] << 12);
- hw_ps->column_width2 = column[4] | (column[5] << 12);
- hw_ps->row_height2 = row[4] | (row[5] << 12);
- hw_ps->column_width3 = column[6] | (column[7] << 12);
- hw_ps->row_height3 = row[6] | (row[7] << 12);
- hw_ps->column_width4 = column[8] | (column[9] << 12);
- hw_ps->row_height4 = row[8] | (row[9] << 12);
- hw_ps->column_width5 = column[10] | (column[11] << 12);
- hw_ps->row_height5 = row[10] | (row[11] << 12);
- hw_ps->column_width6 = column[12] | (column[13] << 12);
- hw_ps->row_height6 = row[12] | (row[13] << 12);
- hw_ps->column_width7 = column[14] | (column[15] << 12);
- hw_ps->row_height7 = row[14] | (row[15] << 12);
- hw_ps->column_width8 = column[16] | (column[17] << 12);
- hw_ps->row_height8 = row[16] | (row[17] << 12);
- hw_ps->column_width9 = column[18] | (column[19] << 12);
- hw_ps->row_height9 = row[18] | (row[19] << 12);
-
- hw_ps->row_height10 = row[20] | (row[21] << 12);
-}
-
-static void set_pps_ref_pic_poc(struct rkvdec_hevc_sps_pps *hw_ps, const struct v4l2_hevc_dpb_entry *dpb)
-{
- hw_ps->ref_pic_poc0 = dpb[0].pic_order_cnt_val;
- hw_ps->ref_pic_poc1 = dpb[1].pic_order_cnt_val;
- hw_ps->ref_pic_poc2 = dpb[2].pic_order_cnt_val;
- hw_ps->ref_pic_poc3 = dpb[3].pic_order_cnt_val;
- hw_ps->ref_pic_poc4 = dpb[4].pic_order_cnt_val;
- hw_ps->ref_pic_poc5 = dpb[5].pic_order_cnt_val;
- hw_ps->ref_pic_poc6 = dpb[6].pic_order_cnt_val;
- hw_ps->ref_pic_poc7 = dpb[7].pic_order_cnt_val;
- hw_ps->ref_pic_poc8 = dpb[8].pic_order_cnt_val;
- hw_ps->ref_pic_poc9 = dpb[9].pic_order_cnt_val;
- hw_ps->ref_pic_poc10 = dpb[10].pic_order_cnt_val;
- hw_ps->ref_pic_poc11 = dpb[11].pic_order_cnt_val;
- hw_ps->ref_pic_poc12 = dpb[12].pic_order_cnt_val;
- hw_ps->ref_pic_poc13 = dpb[13].pic_order_cnt_val;
- hw_ps->ref_pic_poc14 = dpb[14].pic_order_cnt_val;
-}
-
static void assemble_hw_pps(struct rkvdec_ctx *ctx,
struct rkvdec_hevc_run *run)
{
@@ -245,104 +157,130 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
memset(hw_ps, 0, sizeof(*hw_ps));
/* write sps */
- hw_ps->video_parameters_set_id = sps->video_parameter_set_id;
- hw_ps->seq_parameters_set_id_sps = sps->seq_parameter_set_id;
- hw_ps->chroma_format_idc = sps->chroma_format_idc;
+ rkvdec_set_bw_field(hw_ps->info, VIDEO_PARAMETER_SET_ID, sps->video_parameter_set_id);
+ rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id);
+ rkvdec_set_bw_field(hw_ps->info, CHROMA_FORMAT_IDC, sps->chroma_format_idc);
log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
width = sps->pic_width_in_luma_samples;
height = sps->pic_height_in_luma_samples;
- hw_ps->width = width;
- hw_ps->height = height;
- hw_ps->bit_depth_luma = sps->bit_depth_luma_minus8 + 8;
- hw_ps->bit_depth_chroma = sps->bit_depth_chroma_minus8 + 8;
- hw_ps->max_pic_order_count_lsb = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
- hw_ps->diff_max_min_luma_coding_block_size = sps->log2_diff_max_min_luma_coding_block_size;
- hw_ps->min_luma_coding_block_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
- hw_ps->min_transform_block_size = sps->log2_min_luma_transform_block_size_minus2 + 2;
- hw_ps->diff_max_min_transform_block_size =
- sps->log2_diff_max_min_luma_transform_block_size;
- hw_ps->max_transform_hierarchy_depth_inter = sps->max_transform_hierarchy_depth_inter;
- hw_ps->max_transform_hierarchy_depth_intra = sps->max_transform_hierarchy_depth_intra;
- hw_ps->scaling_list_enabled_flag =
- !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED);
- hw_ps->amp_enabled_flag = !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED);
- hw_ps->sample_adaptive_offset_enabled_flag =
- !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET);
+
+ rkvdec_set_bw_field(hw_ps->info, PIC_WIDTH_IN_LUMA_SAMPLES, width);
+ rkvdec_set_bw_field(hw_ps->info, PIC_HEIGHT_IN_LUMA_SAMPLES, height);
+ rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_LUMA, sps->bit_depth_luma_minus8 + 8);
+ rkvdec_set_bw_field(hw_ps->info, BIT_DEPTH_CHROMA, sps->bit_depth_chroma_minus8 + 8);
+ rkvdec_set_bw_field(hw_ps->info, LOG2_MAX_PIC_ORDER_CNT_LSB,
+ sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
+ rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_CODING_BLOCK_SIZE,
+ sps->log2_diff_max_min_luma_coding_block_size);
+ rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_LUMA_CODING_BLOCK_SIZE,
+ sps->log2_min_luma_coding_block_size_minus3 + 3);
+ rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_TRANSFORM_BLOCK_SIZE,
+ sps->log2_min_luma_transform_block_size_minus2 + 2);
+ rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_LUMA_TRANSFORM_BLOCK_SIZE,
+ sps->log2_diff_max_min_luma_transform_block_size);
+ rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTER,
+ sps->max_transform_hierarchy_depth_inter);
+ rkvdec_set_bw_field(hw_ps->info, MAX_TRANSFORM_HIERARCHY_DEPTH_INTRA,
+ sps->max_transform_hierarchy_depth_intra);
+ rkvdec_set_bw_field(hw_ps->info, SCALING_LIST_ENABLED_FLAG,
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, AMP_ENABLED_FLAG,
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_AMP_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG,
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET));
pcm_enabled = !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_ENABLED);
- hw_ps->pcm_enabled_flag = pcm_enabled;
- hw_ps->pcm_sample_bit_depth_luma =
- pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0;
- hw_ps->pcm_sample_bit_depth_chroma =
- pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0;
- hw_ps->pcm_loop_filter_disabled_flag =
- !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED);
- hw_ps->diff_max_min_pcm_luma_coding_block_size =
- sps->log2_diff_max_min_pcm_luma_coding_block_size;
- hw_ps->min_pcm_luma_coding_block_size =
- pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0;
- hw_ps->num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets;
- hw_ps->long_term_ref_pics_present_flag =
- !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT);
- hw_ps->num_long_term_ref_pics_sps = sps->num_long_term_ref_pics_sps;
- hw_ps->sps_temporal_mvp_enabled_flag =
- !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED);
- hw_ps->strong_intra_smoothing_enabled_flag =
- !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED);
- hw_ps->sps_max_dec_pic_buffering_minus1 = sps->sps_max_dec_pic_buffering_minus1;
+ rkvdec_set_bw_field(hw_ps->info, PCM_ENABLED_FLAG, pcm_enabled);
+ rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_LUMA,
+ pcm_enabled ? sps->pcm_sample_bit_depth_luma_minus1 + 1 : 0);
+ rkvdec_set_bw_field(hw_ps->info, PCM_SAMPLE_BIT_DEPTH_CHROMA,
+ pcm_enabled ? sps->pcm_sample_bit_depth_chroma_minus1 + 1 : 0);
+ rkvdec_set_bw_field(hw_ps->info, PCM_LOOP_FILTER_DISABLED_FLAG,
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED));
+ rkvdec_set_bw_field(hw_ps->info, LOG2_DIFF_MAX_MIN_PCM_LUMA_CODING_BLOCK_SIZE,
+ sps->log2_diff_max_min_pcm_luma_coding_block_size);
+ rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_PCM_LUMA_CODING_BLOCK_SIZE,
+ pcm_enabled ? sps->log2_min_pcm_luma_coding_block_size_minus3 + 3 : 0);
+ rkvdec_set_bw_field(hw_ps->info, NUM_SHORT_TERM_REF_PIC_SETS,
+ sps->num_short_term_ref_pic_sets);
+ rkvdec_set_bw_field(hw_ps->info, LONG_TERM_REF_PICS_PRESENT_FLAG,
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, NUM_LONG_TERM_REF_PICS_SPS,
+ sps->num_long_term_ref_pics_sps);
+ rkvdec_set_bw_field(hw_ps->info, SPS_TEMPORAL_MVP_ENABLED_FLAG,
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, STRONG_INTRA_SMOOTHING_ENABLED_FLAG,
+ !!(sps->flags & V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, SPS_MAX_DEC_PIC_BUFFERING_MINUS1,
+ sps->sps_max_dec_pic_buffering_minus1);
/* write pps */
- hw_ps->picture_parameters_set_id = pps->pic_parameter_set_id;
- hw_ps->seq_parameters_set_id_pps = sps->seq_parameter_set_id;
- hw_ps->dependent_slice_segments_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED);
- hw_ps->output_flag_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT);
- hw_ps->num_extra_slice_header_bits = pps->num_extra_slice_header_bits;
- hw_ps->sign_data_hiding_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED);
- hw_ps->cabac_init_present_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT);
- hw_ps->num_ref_idx_l0_default_active = pps->num_ref_idx_l0_default_active_minus1 + 1;
- hw_ps->num_ref_idx_l1_default_active = pps->num_ref_idx_l1_default_active_minus1 + 1;
- hw_ps->init_qp_minus26 = pps->init_qp_minus26;
- hw_ps->constrained_intra_pred_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED);
- hw_ps->transform_skip_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED);
- hw_ps->cu_qp_delta_enabled_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED);
- hw_ps->log2_min_cb_size = log2_min_cb_size +
- sps->log2_diff_max_min_luma_coding_block_size -
- pps->diff_cu_qp_delta_depth;
- hw_ps->pps_cb_qp_offset = pps->pps_cb_qp_offset;
- hw_ps->pps_cr_qp_offset = pps->pps_cr_qp_offset;
- hw_ps->pps_slice_chroma_qp_offsets_present_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT);
- hw_ps->weighted_pred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED);
- hw_ps->weighted_bipred_flag = !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED);
- hw_ps->transquant_bypass_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED);
+ rkvdec_set_bw_field(hw_ps->info, PIC_PARAMETER_SET_ID, pps->pic_parameter_set_id);
+ rkvdec_set_bw_field(hw_ps->info, SEQ_PARAMETER_SET_ID, sps->seq_parameter_set_id);
+ rkvdec_set_bw_field(hw_ps->info, DEPENDENT_SLICE_SEGMENTS_ENABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, OUTPUT_FLAG_PRESENT_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, NUM_EXTRA_SLICE_HEADER_BITS,
+ pps->num_extra_slice_header_bits);
+ rkvdec_set_bw_field(hw_ps->info, SIGN_DATA_HIDING_ENABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, CABAC_INIT_PRESENT_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L0_DEFAULT_ACTIVE,
+ pps->num_ref_idx_l0_default_active_minus1 + 1);
+ rkvdec_set_bw_field(hw_ps->info, NUM_REF_IDX_L1_DEFAULT_ACTIVE,
+ pps->num_ref_idx_l1_default_active_minus1 + 1);
+ rkvdec_set_bw_field(hw_ps->info, INIT_QP_MINUS26, pps->init_qp_minus26);
+ rkvdec_set_bw_field(hw_ps->info, CONSTRAINED_INTRA_PRED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED));
+ rkvdec_set_bw_field(hw_ps->info, TRANSFORM_SKIP_ENABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, CU_QP_DELTA_ENABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, LOG2_MIN_CU_QP_DELTA_SIZE, log2_min_cb_size +
+ sps->log2_diff_max_min_luma_coding_block_size -
+ pps->diff_cu_qp_delta_depth);
+ rkvdec_set_bw_field(hw_ps->info, PPS_CB_QP_OFFSET, pps->pps_cb_qp_offset);
+ rkvdec_set_bw_field(hw_ps->info, PPS_CR_QP_OFFSET, pps->pps_cr_qp_offset);
+ rkvdec_set_bw_field(hw_ps->info, PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT_FLAG,
+ !!(pps->flags &
+ V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, WEIGHTED_PRED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED));
+ rkvdec_set_bw_field(hw_ps->info, WEIGHTED_BIPRED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED));
+ rkvdec_set_bw_field(hw_ps->info, TRANSQUANT_BYPASS_ENABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED));
tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED);
- hw_ps->tiles_enabled_flag = tiles_enabled;
- hw_ps->entropy_coding_sync_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED);
- hw_ps->pps_loop_filter_across_slices_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED);
- hw_ps->loop_filter_across_tiles_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED);
- hw_ps->deblocking_filter_override_enabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED);
- hw_ps->pps_deblocking_filter_disabled_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER);
- hw_ps->pps_beta_offset_div2 = pps->pps_beta_offset_div2;
- hw_ps->pps_tc_offset_div2 = pps->pps_tc_offset_div2;
- hw_ps->lists_modification_present_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT);
- hw_ps->log2_parallel_merge_level = pps->log2_parallel_merge_level_minus2 + 2;
- hw_ps->slice_segment_header_extension_present_flag =
- !!(pps->flags & V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT);
- hw_ps->num_tile_columns = tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1;
- hw_ps->num_tile_rows = tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1;
- hw_ps->mvc_ff = 0xffff;
+ rkvdec_set_bw_field(hw_ps->info, TILES_ENABLED_FLAG, tiles_enabled);
+ rkvdec_set_bw_field(hw_ps->info, ENTROPY_CODING_SYNC_ENABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED_FLAG,
+ !!(pps->flags &
+ V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, LOOP_FILTER_ACROSS_TILES_ENABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, DEBLOCKING_FILTER_OVERRIDE_ENABLED_FLAG,
+ !!(pps->flags &
+ V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED));
+ rkvdec_set_bw_field(hw_ps->info, PPS_DEBLOCKING_FILTER_DISABLED_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER));
+ rkvdec_set_bw_field(hw_ps->info, PPS_BETA_OFFSET_DIV2, pps->pps_beta_offset_div2);
+ rkvdec_set_bw_field(hw_ps->info, PPS_TC_OFFSET_DIV2, pps->pps_tc_offset_div2);
+ rkvdec_set_bw_field(hw_ps->info, LISTS_MODIFICATION_PRESENT_FLAG,
+ !!(pps->flags & V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, LOG2_PARALLEL_MERGE_LEVEL,
+ pps->log2_parallel_merge_level_minus2 + 2);
+ rkvdec_set_bw_field(hw_ps->info, SLICE_SEGMENT_HEADER_EXTENSION_PRESENT_FLAG,
+ !!(pps->flags &
+ V4L2_HEVC_PPS_FLAG_SLICE_SEGMENT_HEADER_EXTENSION_PRESENT));
+ rkvdec_set_bw_field(hw_ps->info, NUM_TILE_COLUMNS,
+ tiles_enabled ? pps->num_tile_columns_minus1 + 1 : 1);
+ rkvdec_set_bw_field(hw_ps->info, NUM_TILE_ROWS,
+ tiles_enabled ? pps->num_tile_rows_minus1 + 1 : 1);
+ rkvdec_set_bw_field(hw_ps->info, MVC_FF, 0xffff);
// Setup tiles information
memset(column_width, 0, sizeof(column_width));
@@ -367,15 +305,19 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
row_height[0] = (height + max_cu_width - 1) / max_cu_width;
}
- set_column_row(hw_ps, column_width, row_height);
+ for (i = 0; i < 20; i++)
+ rkvdec_set_bw_field(hw_ps->info, COLUMN_WIDTH(i), column_width[i]);
+ for (i = 0; i < 22; i++)
+ rkvdec_set_bw_field(hw_ps->info, ROW_HEIGHT(i), row_height[i]);
// Setup POC information
- hw_ps->current_poc = dec_params->pic_order_cnt_val;
+ rkvdec_set_bw_field(hw_ps->info, CURRENT_POC, dec_params->pic_order_cnt_val);
- set_pps_ref_pic_poc(hw_ps, dec_params->dpb);
for (i = 0; i < ARRAY_SIZE(dec_params->dpb); i++) {
- u32 valid = !!(dec_params->num_active_dpb_entries > i);
- hw_ps->ref_is_valid |= valid << i;
+ rkvdec_set_bw_field(hw_ps->info, REF_IS_VALID(i),
+ !!(dec_params->num_active_dpb_entries > i));
+ rkvdec_set_bw_field(hw_ps->info, REF_PIC_POC(i),
+ dec_params->dpb[i].pic_order_cnt_val);
}
}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index e5663fbe6422..eeb0199864dd 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -2195,6 +2195,7 @@ static int dcmi_probe(struct platform_device *pdev)
return 0;
err_cleanup:
+ v4l2_async_nf_unregister(&dcmi->notifier);
v4l2_async_nf_cleanup(&dcmi->notifier);
err_media_entity_cleanup:
media_entity_cleanup(&dcmi->vdev->entity);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
index a42f43d19f9e..f0e809458489 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
@@ -401,8 +401,10 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
*/
if (!vcap->s_subdev) {
pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
- if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
- return -EINVAL;
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity)) {
+ ret = -EINVAL;
+ goto err_buffer_done;
+ }
vcap->s_subdev = media_entity_to_v4l2_subdev(pad->entity);
vcap->s_subdev_pad_nb = pad->index;
}
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
index e911c7f7acc5..4781db21c205 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_dma.c
@@ -234,8 +234,10 @@ static int sun4i_csi_start_streaming(struct vb2_queue *vq, unsigned int count)
int ret;
csi_fmt = sun4i_csi_find_format(&csi->fmt.pixelformat, NULL);
- if (!csi_fmt)
- return -EINVAL;
+ if (!csi_fmt) {
+ ret = -EINVAL;
+ goto err_clear_dma_queue;
+ }
dev_dbg(csi->dev, "Starting capture\n");
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
index 02eb4a6cafad..41e48365167e 100644
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -11,6 +11,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
@@ -35,6 +36,8 @@
#define DW_REG_EXIST BIT(31)
#define DW_REG(x) (DW_REG_EXIST | (x))
+#define DPHY_STOPSTATE_CLK_LANE BIT(16)
+
#define DPHY_TEST_CTRL0_TEST_CLR BIT(0)
#define IPI_VCID_VC(x) FIELD_PREP(GENMASK(1, 0), (x))
@@ -65,6 +68,7 @@ enum dw_mipi_csi2rx_regs_index {
DW_MIPI_CSI2RX_PHY_TST_CTRL0,
DW_MIPI_CSI2RX_PHY_TST_CTRL1,
DW_MIPI_CSI2RX_PHY_SHUTDOWNZ,
+ DW_MIPI_CSI2RX_PHY_STOPSTATE,
DW_MIPI_CSI2RX_IPI_DATATYPE,
DW_MIPI_CSI2RX_IPI_MEM_FLUSH,
DW_MIPI_CSI2RX_IPI_MODE,
@@ -87,6 +91,7 @@ struct dw_mipi_csi2rx_drvdata {
void (*dphy_assert_reset)(struct dw_mipi_csi2rx_device *csi2);
void (*dphy_deassert_reset)(struct dw_mipi_csi2rx_device *csi2);
void (*ipi_enable)(struct dw_mipi_csi2rx_device *csi2);
+ int (*wait_for_phy_stopstate)(struct dw_mipi_csi2rx_device *csi2);
};
struct dw_mipi_csi2rx_format {
@@ -113,6 +118,7 @@ struct dw_mipi_csi2rx_device {
enum v4l2_mbus_type bus_type;
u32 lanes_num;
+ u64 enabled_streams;
const struct dw_mipi_csi2rx_drvdata *drvdata;
};
@@ -138,6 +144,7 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = {
[DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
[DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
[DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
+ [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c),
[DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
[DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
[DW_MIPI_CSI2RX_IPI_MODE] = DW_REG(0x80),
@@ -147,6 +154,17 @@ static const u32 imx93_regs[DW_MIPI_CSI2RX_MAX] = {
[DW_MIPI_CSI2RX_IPI_SOFTRSTN] = DW_REG(0xa0),
};
+static const u32 imx95_regs[DW_MIPI_CSI2RX_MAX] = {
+ [DW_MIPI_CSI2RX_N_LANES] = DW_REG(0x4),
+ [DW_MIPI_CSI2RX_RESETN] = DW_REG(0x8),
+ [DW_MIPI_CSI2RX_PHY_SHUTDOWNZ] = DW_REG(0x40),
+ [DW_MIPI_CSI2RX_DPHY_RSTZ] = DW_REG(0x44),
+ [DW_MIPI_CSI2RX_PHY_STATE] = DW_REG(0x48),
+ [DW_MIPI_CSI2RX_PHY_STOPSTATE] = DW_REG(0x4c),
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL0] = DW_REG(0x50),
+ [DW_MIPI_CSI2RX_PHY_TST_CTRL1] = DW_REG(0x54),
+};
+
static const struct v4l2_mbus_framefmt default_format = {
.width = 3840,
.height = 2160,
@@ -252,6 +270,26 @@ static const struct dw_mipi_csi2rx_format formats[] = {
.depth = 12,
.csi_dt = MIPI_CSI2_DT_RAW12,
},
+ {
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .depth = 16,
+ .csi_dt = MIPI_CSI2_DT_RAW16,
+ },
};
static inline struct dw_mipi_csi2rx_device *to_csi2(struct v4l2_subdev *sd)
@@ -311,7 +349,7 @@ dw_mipi_csi2rx_find_format(struct dw_mipi_csi2rx_device *csi2, u32 mbus_code)
WARN_ON(csi2->formats_num == 0);
for (unsigned int i = 0; i < csi2->formats_num; i++) {
- const struct dw_mipi_csi2rx_format *format = &csi2->formats[i];
+ const struct dw_mipi_csi2rx_format *format = &formats[i];
if (format->code == mbus_code)
return format;
@@ -433,7 +471,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd,
if (code->index >= csi2->formats_num)
return -EINVAL;
- code->code = csi2->formats[code->index].code;
+ code->code = formats[code->index].code;
return 0;
default:
return -EINVAL;
@@ -470,6 +508,17 @@ static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd,
*src = *sink;
+ /* Store the CSIS format descriptor for active formats. */
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ csi2->formats = fmt ? :
+ dw_mipi_csi2rx_find_format(csi2, default_format.code);
+
+ if (!csi2->formats) {
+ dev_err(csi2->dev, "Failed to find valid format\n");
+ return -EINVAL;
+ }
+ }
+
return 0;
}
@@ -508,26 +557,42 @@ static int dw_mipi_csi2rx_enable_streams(struct v4l2_subdev *sd,
DW_MIPI_CSI2RX_PAD_SRC,
&streams_mask);
- ret = pm_runtime_resume_and_get(dev);
- if (ret)
- goto err;
+ if (!csi2->enabled_streams) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ goto err;
- ret = dw_mipi_csi2rx_start(csi2);
- if (ret) {
- dev_err(dev, "failed to enable CSI hardware\n");
- goto err_pm_runtime_put;
+ ret = dw_mipi_csi2rx_start(csi2);
+ if (ret) {
+ dev_err(dev, "failed to enable CSI hardware\n");
+ goto err_pm_runtime_put;
+ }
}
ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index, mask);
if (ret)
goto err_csi_stop;
+ if (!csi2->enabled_streams &&
+ csi2->drvdata->wait_for_phy_stopstate) {
+ ret = csi2->drvdata->wait_for_phy_stopstate(csi2);
+ if (ret)
+ goto err_disable_streams;
+ }
+
+ csi2->enabled_streams |= streams_mask;
+
return 0;
+err_disable_streams:
+ v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
err_csi_stop:
- dw_mipi_csi2rx_stop(csi2);
+ /* Stop CSI hardware if no streams are enabled */
+ if (!csi2->enabled_streams)
+ dw_mipi_csi2rx_stop(csi2);
err_pm_runtime_put:
- pm_runtime_put(dev);
+ if (!csi2->enabled_streams)
+ pm_runtime_put(dev);
err:
return ret;
}
@@ -552,10 +617,15 @@ static int dw_mipi_csi2rx_disable_streams(struct v4l2_subdev *sd,
&streams_mask);
ret = v4l2_subdev_disable_streams(remote_sd, remote_pad->index, mask);
+ if (ret)
+ dev_err(dev, "failed to disable streams on remote subdev: %d\n", ret);
- dw_mipi_csi2rx_stop(csi2);
+ csi2->enabled_streams &= ~streams_mask;
- pm_runtime_put(dev);
+ if (!csi2->enabled_streams) {
+ dw_mipi_csi2rx_stop(csi2);
+ pm_runtime_put(dev);
+ }
return ret;
}
@@ -827,11 +897,39 @@ static void imx93_csi2rx_dphy_ipi_enable(struct dw_mipi_csi2rx_device *csi2)
dw_mipi_csi2rx_write(csi2, DW_MIPI_CSI2RX_IPI_MODE, val);
}
+static int imx93_csi2rx_wait_for_phy_stopstate(struct dw_mipi_csi2rx_device *csi2)
+{
+ struct device *dev = csi2->dev;
+ u32 stopstate_mask;
+ u32 val;
+ int ret;
+
+ stopstate_mask = DPHY_STOPSTATE_CLK_LANE | GENMASK(csi2->lanes_num - 1, 0);
+
+ ret = read_poll_timeout(dw_mipi_csi2rx_read, val,
+ (val & stopstate_mask) == stopstate_mask,
+ 10, 1000, true,
+ csi2, DW_MIPI_CSI2RX_PHY_STOPSTATE);
+ if (ret)
+ dev_err(dev, "lanes are not in stop state: %#x, expected %#x\n",
+ val, stopstate_mask);
+
+ return ret;
+}
+
static const struct dw_mipi_csi2rx_drvdata imx93_drvdata = {
.regs = imx93_regs,
.dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
.dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
.ipi_enable = imx93_csi2rx_dphy_ipi_enable,
+ .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate,
+};
+
+static const struct dw_mipi_csi2rx_drvdata imx95_drvdata = {
+ .regs = imx95_regs,
+ .dphy_assert_reset = imx93_csi2rx_dphy_assert_reset,
+ .dphy_deassert_reset = imx93_csi2rx_dphy_deassert_reset,
+ .wait_for_phy_stopstate = imx93_csi2rx_wait_for_phy_stopstate,
};
static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
@@ -840,6 +938,10 @@ static const struct of_device_id dw_mipi_csi2rx_of_match[] = {
.data = &imx93_drvdata,
},
{
+ .compatible = "fsl,imx95-mipi-csi2",
+ .data = &imx95_drvdata,
+ },
+ {
.compatible = "rockchip,rk3568-mipi-csi2",
.data = &rk3568_drvdata,
},
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index 1061ab50dd64..25f8ca0d6d94 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -506,9 +506,9 @@ static void hdmirx_hpd_ctrl(struct snps_hdmirx_dev *hdmirx_dev, bool en)
hdmirx_writel(hdmirx_dev, CORE_CONFIG,
hdmirx_dev->hpd_trigger_level_high ? en : !en);
- /* 100ms delay as per HDMI spec */
+ /* 100ms delay as per HDMI spec + extra 50ms to cover internal delay */
if (!en)
- msleep(100);
+ msleep(100 + 50);
}
static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev,
diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig
index da33facf4467..d0cb05481bd8 100644
--- a/drivers/media/platform/ti/Kconfig
+++ b/drivers/media/platform/ti/Kconfig
@@ -83,6 +83,7 @@ config VIDEO_TI_J721E_CSI2RX
depends on VIDEO_CADENCE_CSI2RX
depends on PHY_CADENCE_DPHY_RX || COMPILE_TEST
depends on ARCH_K3 || COMPILE_TEST
+ depends on PM
select VIDEOBUF2_DMA_CONTIG
select V4L2_FWNODE
help
diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index 15df3ea2f77e..91cb6223561a 100644
--- a/drivers/media/platform/ti/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -1498,7 +1498,7 @@ vpif_capture_get_pdata(struct platform_device *pdev,
* video ports & endpoints data.
*/
if (pdev->dev.parent && pdev->dev.parent->of_node)
- pdev->dev.of_node = pdev->dev.parent->of_node;
+ device_set_of_node_from_dev(&pdev->dev, pdev->dev.parent);
if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node)
return pdev->dev.platform_data;
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index b75aa363d1bf..4769931b1930 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <media/cadence/cdns-csi2rx.h>
@@ -27,23 +28,25 @@
#define SHIM_CNTL 0x10
#define SHIM_CNTL_PIX_RST BIT(0)
-#define SHIM_DMACNTX 0x20
+#define SHIM_DMACNTX(i) (0x20 + ((i) * 0x20))
#define SHIM_DMACNTX_EN BIT(31)
#define SHIM_DMACNTX_YUV422 GENMASK(27, 26)
#define SHIM_DMACNTX_DUAL_PCK_CFG BIT(24)
#define SHIM_DMACNTX_SIZE GENMASK(21, 20)
+#define SHIM_DMACNTX_VC GENMASK(9, 6)
#define SHIM_DMACNTX_FMT GENMASK(5, 0)
#define SHIM_DMACNTX_YUV422_MODE_11 3
#define SHIM_DMACNTX_SIZE_8 0
#define SHIM_DMACNTX_SIZE_16 1
#define SHIM_DMACNTX_SIZE_32 2
-#define SHIM_PSI_CFG0 0x24
+#define SHIM_PSI_CFG0(i) (0x24 + ((i) * 0x20))
#define SHIM_PSI_CFG0_SRC_TAG GENMASK(15, 0)
#define SHIM_PSI_CFG0_DST_TAG GENMASK(31, 16)
#define TI_CSI2RX_MAX_PIX_PER_CLK 4
-#define PSIL_WORD_SIZE_BYTES 16
+#define TI_CSI2RX_MAX_CTX 32
+
/*
* There are no hard limits on the width or height. The DMA engine can handle
* all sizes. The max width and height are arbitrary numbers for this driver.
@@ -53,6 +56,11 @@
#define MAX_WIDTH_BYTES SZ_16K
#define MAX_HEIGHT_LINES SZ_16K
+#define TI_CSI2RX_PAD_SINK 0
+#define TI_CSI2RX_PAD_FIRST_SOURCE 1
+#define TI_CSI2RX_MAX_SOURCE_PADS TI_CSI2RX_MAX_CTX
+#define TI_CSI2RX_MAX_PADS (1 + TI_CSI2RX_MAX_SOURCE_PADS)
+
#define DRAIN_TIMEOUT_MS 50
#define DRAIN_BUFFER_SIZE SZ_32K
@@ -70,13 +78,13 @@ struct ti_csi2rx_buffer {
/* Common v4l2 buffer. Must be first. */
struct vb2_v4l2_buffer vb;
struct list_head list;
- struct ti_csi2rx_dev *csi;
+ struct ti_csi2rx_ctx *ctx;
};
enum ti_csi2rx_dma_state {
TI_CSI2RX_DMA_STOPPED, /* Streaming not started yet. */
- TI_CSI2RX_DMA_IDLE, /* Streaming but no pending DMA operation. */
TI_CSI2RX_DMA_ACTIVE, /* Streaming and pending DMA operation. */
+ TI_CSI2RX_DMA_DRAINING, /* Dumping all the data in drain buffer */
};
struct ti_csi2rx_dma {
@@ -90,32 +98,54 @@ struct ti_csi2rx_dma {
* Queue of buffers submitted to DMA engine.
*/
struct list_head submitted;
- /* Buffer to drain stale data from PSI-L endpoint */
- struct {
- void *vaddr;
- dma_addr_t paddr;
- size_t len;
- } drain;
+};
+
+struct ti_csi2rx_dev;
+
+struct ti_csi2rx_ctx {
+ struct ti_csi2rx_dev *csi;
+ struct video_device vdev;
+ struct vb2_queue vidq;
+ struct mutex mutex; /* To serialize ioctls. */
+ struct v4l2_format v_fmt;
+ struct ti_csi2rx_dma dma;
+ struct media_pad pad;
+ struct completion drain_complete;
+ u32 sequence;
+ u32 idx;
+ u32 vc;
+ u32 dt;
+ u32 stream;
};
struct ti_csi2rx_dev {
struct device *dev;
void __iomem *shim;
+ unsigned int enable_count;
+ unsigned int num_ctx;
struct v4l2_device v4l2_dev;
- struct video_device vdev;
struct media_device mdev;
struct media_pipeline pipe;
- struct media_pad pad;
+ struct media_pad pads[TI_CSI2RX_MAX_PADS];
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source;
- struct vb2_queue vidq;
- struct mutex mutex; /* To serialize ioctls. */
- struct v4l2_format v_fmt;
- struct ti_csi2rx_dma dma;
- u32 sequence;
+ struct v4l2_subdev subdev;
+ struct ti_csi2rx_ctx ctx[TI_CSI2RX_MAX_CTX];
+ struct notifier_block pm_notifier;
u8 pix_per_clk;
+ /* Buffer to drain stale data from PSI-L endpoint */
+ struct {
+ void *vaddr;
+ dma_addr_t paddr;
+ size_t len;
+ } drain;
};
+static inline struct ti_csi2rx_dev *to_csi2rx_dev(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct ti_csi2rx_dev, subdev);
+}
+
static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = {
{
.fourcc = V4L2_PIX_FMT_YUYV,
@@ -219,9 +249,13 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = {
};
/* Forward declaration needed by ti_csi2rx_dma_callback. */
-static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
+static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx,
struct ti_csi2rx_buffer *buf);
+/* Forward declarations needed by ti_csi2rx_drain_callback. */
+static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx);
+static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx);
+
static const struct ti_csi2rx_fmt *find_format_by_fourcc(u32 pixelformat)
{
unsigned int i;
@@ -250,19 +284,12 @@ static void ti_csi2rx_fill_fmt(const struct ti_csi2rx_fmt *csi_fmt,
struct v4l2_format *v4l2_fmt)
{
struct v4l2_pix_format *pix = &v4l2_fmt->fmt.pix;
- unsigned int pixels_in_word;
-
- pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / csi_fmt->bpp;
/* Clamp width and height to sensible maximums (16K x 16K) */
pix->width = clamp_t(unsigned int, pix->width,
- pixels_in_word,
- MAX_WIDTH_BYTES * 8 / csi_fmt->bpp);
+ 1, MAX_WIDTH_BYTES * 8 / csi_fmt->bpp);
pix->height = clamp_t(unsigned int, pix->height, 1, MAX_HEIGHT_LINES);
- /* Width should be a multiple of transfer word-size */
- pix->width = rounddown(pix->width, pixels_in_word);
-
v4l2_fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
pix->pixelformat = csi_fmt->fourcc;
pix->bytesperline = pix->width * (csi_fmt->bpp / 8);
@@ -309,7 +336,7 @@ static int ti_csi2rx_enum_fmt_vid_cap(struct file *file, void *priv,
static int ti_csi2rx_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct ti_csi2rx_dev *csi = video_drvdata(file);
+ struct ti_csi2rx_ctx *csi = video_drvdata(file);
*f = csi->v_fmt;
@@ -340,7 +367,7 @@ static int ti_csi2rx_try_fmt_vid_cap(struct file *file, void *priv,
static int ti_csi2rx_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct ti_csi2rx_dev *csi = video_drvdata(file);
+ struct ti_csi2rx_ctx *csi = video_drvdata(file);
struct vb2_queue *q = &csi->vidq;
int ret;
@@ -360,23 +387,15 @@ static int ti_csi2rx_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
const struct ti_csi2rx_fmt *fmt;
- unsigned int pixels_in_word;
fmt = find_format_by_fourcc(fsize->pixel_format);
if (!fmt || fsize->index != 0)
return -EINVAL;
- /*
- * Number of pixels in one PSI-L word. The transfer happens in multiples
- * of PSI-L word sizes.
- */
- pixels_in_word = PSIL_WORD_SIZE_BYTES * 8 / fmt->bpp;
-
fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
- fsize->stepwise.min_width = pixels_in_word;
- fsize->stepwise.max_width = rounddown(MAX_WIDTH_BYTES * 8 / fmt->bpp,
- pixels_in_word);
- fsize->stepwise.step_width = pixels_in_word;
+ fsize->stepwise.min_width = 1;
+ fsize->stepwise.max_width = MAX_WIDTH_BYTES * 8 / fmt->bpp;
+ fsize->stepwise.step_width = 1;
fsize->stepwise.min_height = 1;
fsize->stepwise.max_height = MAX_HEIGHT_LINES;
fsize->stepwise.step_height = 1;
@@ -426,26 +445,50 @@ static int csi_async_notifier_bound(struct v4l2_async_notifier *notifier,
static int csi_async_notifier_complete(struct v4l2_async_notifier *notifier)
{
struct ti_csi2rx_dev *csi = dev_get_drvdata(notifier->v4l2_dev->dev);
- struct video_device *vdev = &csi->vdev;
- int ret;
+ int ret, i;
+
+ /* Create link from source to subdev */
+ ret = media_create_pad_link(&csi->source->entity,
+ CSI2RX_BRIDGE_SOURCE_PAD,
+ &csi->subdev.entity,
+ TI_CSI2RX_PAD_SINK,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
- ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret)
return ret;
- ret = media_create_pad_link(&csi->source->entity, CSI2RX_BRIDGE_SOURCE_PAD,
- &vdev->entity, csi->pad.index,
- MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED);
+ /* Create and link video nodes for all DMA contexts */
+ for (i = 0; i < csi->num_ctx; i++) {
+ struct ti_csi2rx_ctx *ctx = &csi->ctx[i];
+ struct video_device *vdev = &ctx->vdev;
- if (ret) {
- video_unregister_device(vdev);
- return ret;
+ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
+ if (ret)
+ goto unregister_dev;
+
+ ret = media_create_pad_link(&csi->subdev.entity,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ &vdev->entity, 0,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret) {
+ video_unregister_device(vdev);
+ goto unregister_dev;
+ }
}
ret = v4l2_device_register_subdev_nodes(&csi->v4l2_dev);
if (ret)
- video_unregister_device(vdev);
+ goto unregister_dev;
+ return 0;
+
+unregister_dev:
+ while (i--) {
+ media_entity_remove_links(&csi->ctx[i].vdev.entity);
+ video_unregister_device(&csi->ctx[i].vdev);
+ }
return ret;
}
@@ -496,7 +539,7 @@ static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi)
struct media_pad *pad;
int ret;
- pad = media_entity_remote_source_pad_unique(&csi->vdev.entity);
+ pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
if (IS_ERR(pad))
return;
@@ -509,22 +552,19 @@ static void ti_csi2rx_request_max_ppc(struct ti_csi2rx_dev *csi)
}
}
-static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi)
+static void ti_csi2rx_setup_shim(struct ti_csi2rx_ctx *ctx)
{
+ struct ti_csi2rx_dev *csi = ctx->csi;
const struct ti_csi2rx_fmt *fmt;
unsigned int reg;
- fmt = find_format_by_fourcc(csi->v_fmt.fmt.pix.pixelformat);
-
- /* De-assert the pixel interface reset. */
- reg = SHIM_CNTL_PIX_RST;
- writel(reg, csi->shim + SHIM_CNTL);
+ fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat);
/* Negotiate pixel count from the source */
ti_csi2rx_request_max_ppc(csi);
reg = SHIM_DMACNTX_EN;
- reg |= FIELD_PREP(SHIM_DMACNTX_FMT, fmt->csi_dt);
+ reg |= FIELD_PREP(SHIM_DMACNTX_FMT, ctx->dt);
/*
* The hardware assumes incoming YUV422 8-bit data on MIPI CSI2 bus
@@ -563,18 +603,43 @@ static void ti_csi2rx_setup_shim(struct ti_csi2rx_dev *csi)
break;
}
- writel(reg, csi->shim + SHIM_DMACNTX);
+ reg |= FIELD_PREP(SHIM_DMACNTX_VC, ctx->vc);
+
+ writel(reg, csi->shim + SHIM_DMACNTX(ctx->idx));
reg = FIELD_PREP(SHIM_PSI_CFG0_SRC_TAG, 0) |
FIELD_PREP(SHIM_PSI_CFG0_DST_TAG, 0);
- writel(reg, csi->shim + SHIM_PSI_CFG0);
+ writel(reg, csi->shim + SHIM_PSI_CFG0(ctx->idx));
}
static void ti_csi2rx_drain_callback(void *param)
{
- struct completion *drain_complete = param;
+ struct ti_csi2rx_ctx *ctx = param;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ if (dma->state == TI_CSI2RX_DMA_STOPPED) {
+ complete(&ctx->drain_complete);
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return;
+ }
- complete(drain_complete);
+ /*
+ * If dma->queue is empty, it indicates that no buffer has been
+ * provided by user space. In this case, initiate a transactions
+ * to drain the DMA. Since one drain of size DRAIN_BUFFER_SIZE
+ * will be done here, the subsequent frame will be a
+ * partial frame, with a size of frame_size - DRAIN_BUFFER_SIZE
+ */
+ if (list_empty(&dma->queue)) {
+ if (ti_csi2rx_drain_dma(ctx))
+ dev_warn(ctx->csi->dev, "DMA drain failed\n");
+ } else {
+ ti_csi2rx_dma_submit_pending(ctx);
+ }
+ spin_unlock_irqrestore(&dma->lock, flags);
}
/*
@@ -588,17 +653,15 @@ static void ti_csi2rx_drain_callback(void *param)
* To prevent that stale data corrupting the subsequent transactions, it is
* required to issue DMA requests to drain it out.
*/
-static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_drain_dma(struct ti_csi2rx_ctx *ctx)
{
+ struct ti_csi2rx_dev *csi = ctx->csi;
struct dma_async_tx_descriptor *desc;
- struct completion drain_complete;
dma_cookie_t cookie;
int ret;
- init_completion(&drain_complete);
-
- desc = dmaengine_prep_slave_single(csi->dma.chan, csi->dma.drain.paddr,
- csi->dma.drain.len, DMA_DEV_TO_MEM,
+ desc = dmaengine_prep_slave_single(ctx->dma.chan, csi->drain.paddr,
+ csi->drain.len, DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
ret = -EIO;
@@ -606,31 +669,46 @@ static int ti_csi2rx_drain_dma(struct ti_csi2rx_dev *csi)
}
desc->callback = ti_csi2rx_drain_callback;
- desc->callback_param = &drain_complete;
+ desc->callback_param = ctx;
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret)
goto out;
- dma_async_issue_pending(csi->dma.chan);
+ dma_async_issue_pending(ctx->dma.chan);
- if (!wait_for_completion_timeout(&drain_complete,
- msecs_to_jiffies(DRAIN_TIMEOUT_MS))) {
- dmaengine_terminate_sync(csi->dma.chan);
- dev_dbg(csi->dev, "DMA transfer timed out for drain buffer\n");
- ret = -ETIMEDOUT;
- goto out;
- }
out:
return ret;
}
+static int ti_csi2rx_dma_submit_pending(struct ti_csi2rx_ctx *ctx)
+{
+ struct ti_csi2rx_dma *dma = &ctx->dma;
+ struct ti_csi2rx_buffer *buf;
+ int ret = 0;
+
+ /* If there are more buffers to process then start their transfer. */
+ while (!list_empty(&dma->queue)) {
+ buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);
+ ret = ti_csi2rx_start_dma(ctx, buf);
+ if (ret) {
+ dev_err(ctx->csi->dev,
+ "Failed to queue the next buffer for DMA\n");
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ list_del(&buf->list);
+ } else {
+ list_move_tail(&buf->list, &dma->submitted);
+ }
+ }
+ return ret;
+}
+
static void ti_csi2rx_dma_callback(void *param)
{
struct ti_csi2rx_buffer *buf = param;
- struct ti_csi2rx_dev *csi = buf->csi;
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_ctx *ctx = buf->ctx;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
unsigned long flags;
/*
@@ -638,44 +716,43 @@ static void ti_csi2rx_dma_callback(void *param)
* hardware monitor registers.
*/
buf->vb.vb2_buf.timestamp = ktime_get_ns();
- buf->vb.sequence = csi->sequence++;
+ buf->vb.sequence = ctx->sequence++;
spin_lock_irqsave(&dma->lock, flags);
WARN_ON(!list_is_first(&buf->list, &dma->submitted));
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
- list_del(&buf->list);
- /* If there are more buffers to process then start their transfer. */
- while (!list_empty(&dma->queue)) {
- buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);
-
- 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);
- }
+ if (dma->state == TI_CSI2RX_DMA_DRAINING) {
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ dma->state = TI_CSI2RX_DMA_ACTIVE;
+ } else {
+ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
}
- if (list_empty(&dma->submitted))
- dma->state = TI_CSI2RX_DMA_IDLE;
+ list_del(&buf->list);
+
+ ti_csi2rx_dma_submit_pending(ctx);
+ if (list_empty(&dma->submitted)) {
+ dma->state = TI_CSI2RX_DMA_DRAINING;
+ if (ti_csi2rx_drain_dma(ctx))
+ dev_warn(ctx->csi->dev,
+ "DMA drain failed on one of the transactions\n");
+ }
spin_unlock_irqrestore(&dma->lock, flags);
}
-static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
+static int ti_csi2rx_start_dma(struct ti_csi2rx_ctx *ctx,
struct ti_csi2rx_buffer *buf)
{
unsigned long addr;
struct dma_async_tx_descriptor *desc;
- size_t len = csi->v_fmt.fmt.pix.sizeimage;
+ size_t len = ctx->v_fmt.fmt.pix.sizeimage;
dma_cookie_t cookie;
int ret = 0;
addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
- desc = dmaengine_prep_slave_single(csi->dma.chan, addr, len,
+ desc = dmaengine_prep_slave_single(ctx->dma.chan, addr, len,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc)
@@ -689,23 +766,25 @@ static int ti_csi2rx_start_dma(struct ti_csi2rx_dev *csi,
if (ret)
return ret;
- dma_async_issue_pending(csi->dma.chan);
+ dma_async_issue_pending(ctx->dma.chan);
return 0;
}
-static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi)
+static void ti_csi2rx_stop_dma(struct ti_csi2rx_ctx *ctx)
{
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
enum ti_csi2rx_dma_state state;
unsigned long flags;
int ret;
spin_lock_irqsave(&dma->lock, flags);
- state = csi->dma.state;
+ state = ctx->dma.state;
dma->state = TI_CSI2RX_DMA_STOPPED;
spin_unlock_irqrestore(&dma->lock, flags);
+ init_completion(&ctx->drain_complete);
+
if (state != TI_CSI2RX_DMA_STOPPED) {
/*
* Normal DMA termination does not clean up pending data on
@@ -713,30 +792,38 @@ static void ti_csi2rx_stop_dma(struct ti_csi2rx_dev *csi)
* is stopped, as the module-level pixel reset cannot be
* enforced before terminating DMA.
*/
- ret = ti_csi2rx_drain_dma(csi);
- if (ret && ret != -ETIMEDOUT)
- dev_warn(csi->dev,
+ ret = ti_csi2rx_drain_dma(ctx);
+ if (ret)
+ dev_warn(ctx->csi->dev,
"Failed to drain DMA. Next frame might be bogus\n");
}
- ret = dmaengine_terminate_sync(csi->dma.chan);
+ /* We wait for the drain to complete so that the stream stops
+ * cleanly, making sure the shared hardware FIFO is cleared of
+ * data from the current stream. No more data will be coming from
+ * the source after this.
+ */
+ wait_for_completion_timeout(&ctx->drain_complete,
+ msecs_to_jiffies(DRAIN_TIMEOUT_MS));
+
+ ret = dmaengine_terminate_sync(ctx->dma.chan);
if (ret)
- dev_err(csi->dev, "Failed to stop DMA: %d\n", ret);
+ dev_err(ctx->csi->dev, "Failed to stop DMA: %d\n", ret);
}
-static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_dev *csi,
+static void ti_csi2rx_cleanup_buffers(struct ti_csi2rx_ctx *ctx,
enum vb2_buffer_state state)
{
- struct ti_csi2rx_dma *dma = &csi->dma;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
struct ti_csi2rx_buffer *buf, *tmp;
unsigned long flags;
spin_lock_irqsave(&dma->lock, flags);
- list_for_each_entry_safe(buf, tmp, &csi->dma.queue, list) {
+ list_for_each_entry_safe(buf, tmp, &ctx->dma.queue, list) {
list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, state);
}
- list_for_each_entry_safe(buf, tmp, &csi->dma.submitted, list) {
+ list_for_each_entry_safe(buf, tmp, &ctx->dma.submitted, list) {
list_del(&buf->list);
vb2_buffer_done(&buf->vb.vb2_buf, state);
}
@@ -747,8 +834,8 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
unsigned int *nplanes, unsigned int sizes[],
struct device *alloc_devs[])
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(q);
- unsigned int size = csi->v_fmt.fmt.pix.sizeimage;
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(q);
+ unsigned int size = ctx->v_fmt.fmt.pix.sizeimage;
if (*nplanes) {
if (sizes[0] < size)
@@ -764,11 +851,11 @@ static int ti_csi2rx_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long size = csi->v_fmt.fmt.pix.sizeimage;
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = ctx->v_fmt.fmt.pix.sizeimage;
if (vb2_plane_size(vb, 0) < size) {
- dev_err(csi->dev, "Data will not fit into plane\n");
+ dev_err(ctx->csi->dev, "Data will not fit into plane\n");
return -EINVAL;
}
@@ -778,132 +865,161 @@ static int ti_csi2rx_buffer_prepare(struct vb2_buffer *vb)
static void ti_csi2rx_buffer_queue(struct vb2_buffer *vb)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vb->vb2_queue);
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct ti_csi2rx_buffer *buf;
- struct ti_csi2rx_dma *dma = &csi->dma;
- bool restart_dma = false;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
unsigned long flags = 0;
- int ret;
buf = container_of(vb, struct ti_csi2rx_buffer, vb.vb2_buf);
- buf->csi = csi;
+ buf->ctx = ctx;
spin_lock_irqsave(&dma->lock, flags);
- /*
- * Usually the DMA callback takes care of queueing the pending buffers.
- * But if DMA has stalled due to lack of buffers, restart it now.
- */
- if (dma->state == TI_CSI2RX_DMA_IDLE) {
- /*
- * Do not restart DMA with the lock held because
- * ti_csi2rx_drain_dma() might block for completion.
- * There won't be a race on queueing DMA anyway since the
- * callback is not being fired.
- */
- restart_dma = true;
- dma->state = TI_CSI2RX_DMA_ACTIVE;
- } else {
- list_add_tail(&buf->list, &dma->queue);
- }
+ list_add_tail(&buf->list, &dma->queue);
spin_unlock_irqrestore(&dma->lock, flags);
+}
- if (restart_dma) {
- /*
- * Once frames start dropping, some data gets stuck in the DMA
- * pipeline somewhere. So the first DMA transfer after frame
- * drops gives a partial frame. This is obviously not useful to
- * the application and will only confuse it. Issue a DMA
- * transaction to drain that up.
- */
- ret = ti_csi2rx_drain_dma(csi);
- if (ret && ret != -ETIMEDOUT)
- dev_warn(csi->dev,
- "Failed to drain DMA. Next frame might be bogus\n");
+static int ti_csi2rx_get_stream(struct ti_csi2rx_ctx *ctx)
+{
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct media_pad *pad;
+ struct v4l2_subdev_state *state;
+ struct v4l2_subdev_route *r;
+
+ /* Get the source pad connected to this ctx */
+ pad = media_entity_remote_source_pad_unique(ctx->pad.entity);
+ if (IS_ERR(pad)) {
+ dev_err(csi->dev, "No pad connected to ctx %d\n", ctx->idx);
+ return PTR_ERR(pad);
+ }
- spin_lock_irqsave(&dma->lock, flags);
- ret = ti_csi2rx_start_dma(csi, buf);
- if (ret) {
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- dma->state = TI_CSI2RX_DMA_IDLE;
- spin_unlock_irqrestore(&dma->lock, flags);
- dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
- } else {
- list_add_tail(&buf->list, &dma->submitted);
- spin_unlock_irqrestore(&dma->lock, flags);
+ state = v4l2_subdev_get_locked_active_state(&csi->subdev);
+
+ for_each_active_route(&state->routing, r) {
+ if (r->source_pad == pad->index) {
+ ctx->stream = r->sink_stream;
+ return 0;
}
}
+
+ /* No route found for this ctx */
+ return -ENODEV;
+}
+
+static int ti_csi2rx_get_vc_and_dt(struct ti_csi2rx_ctx *ctx)
+{
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct ti_csi2rx_ctx *curr_ctx;
+ struct v4l2_mbus_frame_desc fd;
+ struct media_pad *source_pad;
+ const struct ti_csi2rx_fmt *fmt;
+ int ret;
+ unsigned int i, j;
+
+ /* Get the frame desc from source */
+ source_pad = media_entity_remote_pad_unique(&csi->subdev.entity, MEDIA_PAD_FL_SOURCE);
+ if (IS_ERR(source_pad))
+ return PTR_ERR(source_pad);
+
+ ret = v4l2_subdev_call(csi->source, pad, get_frame_desc, source_pad->index, &fd);
+ if (ret) {
+ if (ret == -ENOIOCTLCMD) {
+ ctx->vc = 0;
+ fmt = find_format_by_fourcc(ctx->v_fmt.fmt.pix.pixelformat);
+ ctx->dt = fmt->csi_dt;
+ }
+ return ret;
+ }
+
+ if (fd.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2)
+ return -EINVAL;
+
+ for (i = 0; i < csi->num_ctx; i++) {
+ curr_ctx = &csi->ctx[i];
+
+ /* Capture VC 0 by default */
+ curr_ctx->vc = 0;
+
+ ret = ti_csi2rx_get_stream(curr_ctx);
+ if (ret)
+ continue;
+
+ for (j = 0; j < fd.num_entries; j++) {
+ if (curr_ctx->stream == fd.entry[j].stream) {
+ curr_ctx->vc = fd.entry[j].bus.csi2.vc;
+ curr_ctx->dt = fd.entry[j].bus.csi2.dt;
+ break;
+ }
+
+ /* Return error if no matching stream found */
+ if (j == fd.num_entries)
+ return -EINVAL;
+ }
+ }
+
+ return 0;
}
static int ti_csi2rx_start_streaming(struct vb2_queue *vq, unsigned int count)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq);
- struct ti_csi2rx_dma *dma = &csi->dma;
- struct ti_csi2rx_buffer *buf;
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq);
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct ti_csi2rx_dma *dma = &ctx->dma;
unsigned long flags;
- int ret = 0;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(csi->dev);
+ if (ret)
+ return ret;
spin_lock_irqsave(&dma->lock, flags);
if (list_empty(&dma->queue))
ret = -EIO;
spin_unlock_irqrestore(&dma->lock, flags);
if (ret)
- return ret;
+ goto err;
- ret = video_device_pipeline_start(&csi->vdev, &csi->pipe);
+ ret = video_device_pipeline_start(&ctx->vdev, &csi->pipe);
if (ret)
goto err;
- ti_csi2rx_setup_shim(csi);
-
- csi->sequence = 0;
-
- spin_lock_irqsave(&dma->lock, flags);
- buf = list_entry(dma->queue.next, struct ti_csi2rx_buffer, list);
-
- ret = ti_csi2rx_start_dma(csi, buf);
- if (ret) {
- dev_err(csi->dev, "Failed to start DMA: %d\n", ret);
- spin_unlock_irqrestore(&dma->lock, flags);
- goto err_pipeline;
- }
-
- list_move_tail(&buf->list, &dma->submitted);
- dma->state = TI_CSI2RX_DMA_ACTIVE;
- spin_unlock_irqrestore(&dma->lock, flags);
-
- ret = v4l2_subdev_call(csi->source, video, s_stream, 1);
+ /* Start stream 0, we don't allow multiple streams on the source pad */
+ ret = v4l2_subdev_enable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT_U64(0));
if (ret)
goto err_dma;
return 0;
err_dma:
- ti_csi2rx_stop_dma(csi);
-err_pipeline:
- video_device_pipeline_stop(&csi->vdev);
+ ti_csi2rx_stop_dma(ctx);
+ video_device_pipeline_stop(&ctx->vdev);
writel(0, csi->shim + SHIM_CNTL);
- writel(0, csi->shim + SHIM_DMACNTX);
+ writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
err:
- ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_QUEUED);
+ ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_QUEUED);
+ pm_runtime_put(csi->dev);
+
return ret;
}
static void ti_csi2rx_stop_streaming(struct vb2_queue *vq)
{
- struct ti_csi2rx_dev *csi = vb2_get_drv_priv(vq);
+ struct ti_csi2rx_ctx *ctx = vb2_get_drv_priv(vq);
+ struct ti_csi2rx_dev *csi = ctx->csi;
int ret;
- video_device_pipeline_stop(&csi->vdev);
+ video_device_pipeline_stop(&ctx->vdev);
- writel(0, csi->shim + SHIM_CNTL);
- writel(0, csi->shim + SHIM_DMACNTX);
-
- ret = v4l2_subdev_call(csi->source, video, s_stream, 0);
+ ret = v4l2_subdev_disable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT_U64(0));
if (ret)
dev_err(csi->dev, "Failed to stop subdev stream\n");
- ti_csi2rx_stop_dma(csi);
- ti_csi2rx_cleanup_buffers(csi, VB2_BUF_STATE_ERROR);
+ ti_csi2rx_stop_dma(ctx);
+ ti_csi2rx_cleanup_buffers(ctx, VB2_BUF_STATE_ERROR);
+ pm_runtime_put(csi->dev);
}
static const struct vb2_ops csi_vb2_qops = {
@@ -914,20 +1030,265 @@ static const struct vb2_ops csi_vb2_qops = {
.stop_streaming = ti_csi2rx_stop_streaming,
};
-static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code_enum)
{
- struct vb2_queue *q = &csi->vidq;
+ if (code_enum->index >= ARRAY_SIZE(ti_csi2rx_formats))
+ return -EINVAL;
+
+ code_enum->code = ti_csi2rx_formats[code_enum->index].code;
+
+ return 0;
+}
+
+static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ /* No transcoding, don't allow setting source fmt */
+ if (format->pad > TI_CSI2RX_PAD_SINK)
+ return v4l2_subdev_get_fmt(sd, state, format);
+
+ if (!find_format_by_code(format->format.code))
+ format->format.code = ti_csi2rx_formats[0].code;
+
+ format->format.field = V4L2_FIELD_NONE;
+
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ *fmt = format->format;
+
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ return 0;
+}
+
+static int _ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ static const struct v4l2_mbus_framefmt format = {
+ .width = 640,
+ .height = 480,
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .ycbcr_enc = V4L2_YCBCR_ENC_601,
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ .xfer_func = V4L2_XFER_FUNC_SRGB,
+ };
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+
+ if (ret)
+ return ret;
+
+ /* Only stream ID 0 allowed on source pads */
+ for (unsigned int i = 0; i < routing->num_routes; ++i) {
+ const struct v4l2_subdev_route *route = &routing->routes[i];
+
+ if (route->source_stream != 0)
+ return -EINVAL;
+ }
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
+
+ return ret;
+}
+
+static int ti_csi2rx_sd_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+
+ if (csi->enable_count > 0)
+ return -EBUSY;
+
+ return _ti_csi2rx_sd_set_routing(sd, state, routing);
+}
+
+static int ti_csi2rx_sd_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route routes[] = { {
+ .sink_pad = 0,
+ .sink_stream = 0,
+ .source_pad = TI_CSI2RX_PAD_FIRST_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ } };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = 1,
+ .routes = routes,
+ };
+
+ /* Initialize routing to single route to the fist source pad */
+ return _ti_csi2rx_sd_set_routing(sd, state, &routing);
+}
+
+static int ti_csi2rx_sd_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+ struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE];
+ struct ti_csi2rx_dma *dma = &ctx->dma;
+ struct media_pad *remote_pad;
+ unsigned long flags;
+ u64 sink_streams;
+ int ret = 0;
+ unsigned int reg;
+
+ ret = ti_csi2rx_get_stream(ctx);
+ if (ret)
+ return ret;
+
+ /* Get the VC and DT for all enabled ctx on first stream start */
+ if (!csi->enable_count) {
+ ret = ti_csi2rx_get_vc_and_dt(ctx);
+ if (ret < 0 && ret != -ENOIOCTLCMD)
+ return ret;
+
+ /* De-assert the pixel interface reset. */
+ reg = SHIM_CNTL_PIX_RST;
+ writel(reg, csi->shim + SHIM_CNTL);
+ }
+
+ ti_csi2rx_setup_shim(ctx);
+ ctx->sequence = 0;
+
+ spin_lock_irqsave(&dma->lock, flags);
+
+ ret = ti_csi2rx_dma_submit_pending(ctx);
+ if (ret) {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ return ret;
+ }
+
+ dma->state = TI_CSI2RX_DMA_ACTIVE;
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
+ if (IS_ERR(remote_pad))
+ return PTR_ERR(remote_pad);
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ TI_CSI2RX_PAD_SINK,
+ &streams_mask);
+
+ ret = v4l2_subdev_enable_streams(csi->source, remote_pad->index,
+ sink_streams);
+ if (ret)
+ return ret;
+
+ csi->enable_count++;
+
+ return 0;
+}
+
+static int ti_csi2rx_sd_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct ti_csi2rx_dev *csi = to_csi2rx_dev(sd);
+ struct ti_csi2rx_ctx *ctx = &csi->ctx[pad - TI_CSI2RX_PAD_FIRST_SOURCE];
+ struct media_pad *remote_pad;
+ u64 sink_streams;
+ int ret = 0;
+
+ WARN_ON(csi->enable_count == 0);
+
+ writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
+
+ /* assert pixel reset to prevent stale data */
+ if (csi->enable_count == 1)
+ writel(0, csi->shim + SHIM_CNTL);
+
+ remote_pad = media_entity_remote_source_pad_unique(&csi->subdev.entity);
+ if (IS_ERR(remote_pad))
+ return PTR_ERR(remote_pad);
+ sink_streams = v4l2_subdev_state_xlate_streams(state, pad,
+ TI_CSI2RX_PAD_SINK,
+ &streams_mask);
+
+ ret = v4l2_subdev_disable_streams(csi->source, remote_pad->index,
+ sink_streams);
+ if (!ret)
+ --csi->enable_count;
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops ti_csi2rx_subdev_pad_ops = {
+ .enum_mbus_code = ti_csi2rx_enum_mbus_code,
+ .set_routing = ti_csi2rx_sd_set_routing,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = ti_csi2rx_sd_set_fmt,
+ .enable_streams = ti_csi2rx_sd_enable_streams,
+ .disable_streams = ti_csi2rx_sd_disable_streams,
+};
+
+static const struct v4l2_subdev_ops ti_csi2rx_subdev_ops = {
+ .pad = &ti_csi2rx_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops ti_csi2rx_internal_ops = {
+ .init_state = ti_csi2rx_sd_init_state,
+};
+
+static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi)
+{
+ v4l2_subdev_cleanup(&csi->subdev);
+ media_device_unregister(&csi->mdev);
+ v4l2_device_unregister(&csi->v4l2_dev);
+ media_device_cleanup(&csi->mdev);
+}
+
+static void ti_csi2rx_cleanup_notifier(struct ti_csi2rx_dev *csi)
+{
+ v4l2_async_nf_unregister(&csi->notifier);
+ v4l2_async_nf_cleanup(&csi->notifier);
+}
+
+static void ti_csi2rx_cleanup_ctx(struct ti_csi2rx_ctx *ctx)
+{
+ if (!pm_runtime_status_suspended(ctx->csi->dev))
+ dma_release_channel(ctx->dma.chan);
+
+ vb2_queue_release(&ctx->vidq);
+
+ video_unregister_device(&ctx->vdev);
+
+ mutex_destroy(&ctx->mutex);
+}
+
+static int ti_csi2rx_init_vb2q(struct ti_csi2rx_ctx *ctx)
+{
+ struct vb2_queue *q = &ctx->vidq;
int ret;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_MMAP | VB2_DMABUF;
- q->drv_priv = csi;
+ q->drv_priv = ctx;
q->buf_struct_size = sizeof(struct ti_csi2rx_buffer);
q->ops = &csi_vb2_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->dev = dmaengine_get_dma_device(csi->dma.chan);
- q->lock = &csi->mutex;
+ q->dev = dmaengine_get_dma_device(ctx->dma.chan);
+ q->lock = &ctx->mutex;
q->min_queued_buffers = 1;
q->allow_cache_hints = 1;
@@ -935,7 +1296,7 @@ static int ti_csi2rx_init_vb2q(struct ti_csi2rx_dev *csi)
if (ret)
return ret;
- csi->vdev.queue = q;
+ ctx->vdev.queue = q;
return 0;
}
@@ -944,50 +1305,55 @@ static int ti_csi2rx_link_validate(struct media_link *link)
{
struct media_entity *entity = link->sink->entity;
struct video_device *vdev = media_entity_to_video_device(entity);
- struct ti_csi2rx_dev *csi = container_of(vdev, struct ti_csi2rx_dev, vdev);
- struct v4l2_pix_format *csi_fmt = &csi->v_fmt.fmt.pix;
- struct v4l2_subdev_format source_fmt = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- .pad = link->source->index,
- };
+ struct ti_csi2rx_ctx *ctx = container_of(vdev, struct ti_csi2rx_ctx, vdev);
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct v4l2_pix_format *csi_fmt = &ctx->v_fmt.fmt.pix;
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_subdev_state *state;
const struct ti_csi2rx_fmt *ti_fmt;
- int ret;
- ret = v4l2_subdev_call_state_active(csi->source, pad,
- get_fmt, &source_fmt);
- if (ret)
- return ret;
+ state = v4l2_subdev_lock_and_get_active_state(&csi->subdev);
+ format = v4l2_subdev_state_get_format(state, link->source->index, 0);
+ v4l2_subdev_unlock_state(state);
- if (source_fmt.format.width != csi_fmt->width) {
+ if (!format) {
+ dev_err(csi->dev,
+ "No format present on \"%s\":%u:0\n",
+ link->source->entity->name, link->source->index);
+ return 0;
+ }
+
+ if (format->width != csi_fmt->width) {
dev_dbg(csi->dev, "Width does not match (source %u, sink %u)\n",
- source_fmt.format.width, csi_fmt->width);
+ format->width, csi_fmt->width);
return -EPIPE;
}
- if (source_fmt.format.height != csi_fmt->height) {
+ if (format->height != csi_fmt->height) {
dev_dbg(csi->dev, "Height does not match (source %u, sink %u)\n",
- source_fmt.format.height, csi_fmt->height);
+ format->height, csi_fmt->height);
return -EPIPE;
}
- if (source_fmt.format.field != csi_fmt->field &&
+ if (format->field != csi_fmt->field &&
csi_fmt->field != V4L2_FIELD_NONE) {
dev_dbg(csi->dev, "Field does not match (source %u, sink %u)\n",
- source_fmt.format.field, csi_fmt->field);
+ format->field, csi_fmt->field);
return -EPIPE;
}
- ti_fmt = find_format_by_code(source_fmt.format.code);
+ ti_fmt = find_format_by_code(format->code);
if (!ti_fmt) {
dev_dbg(csi->dev, "Media bus format 0x%x not supported\n",
- source_fmt.format.code);
+ format->code);
return -EPIPE;
}
if (ti_fmt->fourcc != csi_fmt->pixelformat) {
dev_dbg(csi->dev,
- "Cannot transform source fmt 0x%x to sink fmt 0x%x\n",
- ti_fmt->fourcc, csi_fmt->pixelformat);
+ "Cannot transform \"%s\":%u format %p4cc to %p4cc\n",
+ link->source->entity->name, link->source->index,
+ &ti_fmt->fourcc, &csi_fmt->pixelformat);
return -EPIPE;
}
@@ -998,47 +1364,107 @@ static const struct media_entity_operations ti_csi2rx_video_entity_ops = {
.link_validate = ti_csi2rx_link_validate,
};
-static int ti_csi2rx_init_dma(struct ti_csi2rx_dev *csi)
+static const struct media_entity_operations ti_csi2rx_subdev_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .has_pad_interdep = v4l2_subdev_has_pad_interdep,
+};
+
+static int ti_csi2rx_init_dma(struct ti_csi2rx_ctx *ctx)
{
struct dma_slave_config cfg = {
.src_addr_width = DMA_SLAVE_BUSWIDTH_16_BYTES,
};
+ char name[5];
int ret;
- INIT_LIST_HEAD(&csi->dma.queue);
- INIT_LIST_HEAD(&csi->dma.submitted);
- spin_lock_init(&csi->dma.lock);
-
- csi->dma.state = TI_CSI2RX_DMA_STOPPED;
-
- csi->dma.chan = dma_request_chan(csi->dev, "rx0");
- if (IS_ERR(csi->dma.chan))
- return PTR_ERR(csi->dma.chan);
+ snprintf(name, sizeof(name), "rx%u", ctx->idx);
+ ctx->dma.chan = dma_request_chan(ctx->csi->dev, name);
+ if (IS_ERR(ctx->dma.chan))
+ return PTR_ERR(ctx->dma.chan);
- ret = dmaengine_slave_config(csi->dma.chan, &cfg);
+ ret = dmaengine_slave_config(ctx->dma.chan, &cfg);
if (ret) {
- dma_release_channel(csi->dma.chan);
+ dma_release_channel(ctx->dma.chan);
return ret;
}
- csi->dma.drain.len = DRAIN_BUFFER_SIZE;
- csi->dma.drain.vaddr = dma_alloc_coherent(csi->dev, csi->dma.drain.len,
- &csi->dma.drain.paddr,
- GFP_KERNEL);
- if (!csi->dma.drain.vaddr)
- return -ENOMEM;
-
return 0;
}
static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
{
struct media_device *mdev = &csi->mdev;
- struct video_device *vdev = &csi->vdev;
+ struct v4l2_subdev *sd = &csi->subdev;
+ int ret;
+
+ mdev->dev = csi->dev;
+ mdev->hw_revision = 1;
+ strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model));
+
+ media_device_init(mdev);
+
+ csi->v4l2_dev.mdev = mdev;
+
+ ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
+ if (ret)
+ goto cleanup_media;
+
+ ret = media_device_register(mdev);
+ if (ret)
+ goto unregister_v4l2;
+
+ v4l2_subdev_init(sd, &ti_csi2rx_subdev_ops);
+ sd->internal_ops = &ti_csi2rx_internal_ops;
+ sd->entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
+ sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
+ strscpy(sd->name, dev_name(csi->dev), sizeof(sd->name));
+ sd->dev = csi->dev;
+ sd->entity.ops = &ti_csi2rx_subdev_entity_ops;
+
+ csi->pads[TI_CSI2RX_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
+
+ for (unsigned int i = TI_CSI2RX_PAD_FIRST_SOURCE;
+ i < TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx; i++)
+ csi->pads[i].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity,
+ TI_CSI2RX_PAD_FIRST_SOURCE + csi->num_ctx,
+ csi->pads);
+ if (ret)
+ goto unregister_media;
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ goto unregister_media;
+
+ ret = v4l2_device_register_subdev(&csi->v4l2_dev, sd);
+ if (ret)
+ goto cleanup_subdev;
+
+ return 0;
+
+cleanup_subdev:
+ v4l2_subdev_cleanup(sd);
+unregister_media:
+ media_device_unregister(mdev);
+unregister_v4l2:
+ v4l2_device_unregister(&csi->v4l2_dev);
+cleanup_media:
+ media_device_cleanup(mdev);
+
+ return ret;
+}
+
+static int ti_csi2rx_init_ctx(struct ti_csi2rx_ctx *ctx)
+{
+ struct ti_csi2rx_dev *csi = ctx->csi;
+ struct video_device *vdev = &ctx->vdev;
const struct ti_csi2rx_fmt *fmt;
- struct v4l2_pix_format *pix_fmt = &csi->v_fmt.fmt.pix;
+ struct v4l2_pix_format *pix_fmt = &ctx->v_fmt.fmt.pix;
int ret;
+ mutex_init(&ctx->mutex);
+
fmt = find_format_by_fourcc(V4L2_PIX_FMT_UYVY);
if (!fmt)
return -EINVAL;
@@ -1047,19 +1473,20 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
pix_fmt->height = 480;
pix_fmt->field = V4L2_FIELD_NONE;
pix_fmt->colorspace = V4L2_COLORSPACE_SRGB;
- pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
- pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE;
- pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB;
-
- ti_csi2rx_fill_fmt(fmt, &csi->v_fmt);
+ pix_fmt->ycbcr_enc = V4L2_YCBCR_ENC_601,
+ pix_fmt->quantization = V4L2_QUANTIZATION_LIM_RANGE,
+ pix_fmt->xfer_func = V4L2_XFER_FUNC_SRGB,
- mdev->dev = csi->dev;
- mdev->hw_revision = 1;
- strscpy(mdev->model, "TI-CSI2RX", sizeof(mdev->model));
+ ti_csi2rx_fill_fmt(fmt, &ctx->v_fmt);
- media_device_init(mdev);
+ ctx->pad.flags = MEDIA_PAD_FL_SINK;
+ vdev->entity.ops = &ti_csi2rx_video_entity_ops;
+ ret = media_entity_pads_init(&ctx->vdev.entity, 1, &ctx->pad);
+ if (ret)
+ return ret;
- strscpy(vdev->name, TI_CSI2RX_MODULE_NAME, sizeof(vdev->name));
+ snprintf(vdev->name, sizeof(vdev->name), "%s context %u",
+ dev_name(csi->dev), ctx->idx);
vdev->v4l2_dev = &csi->v4l2_dev;
vdev->vfl_dir = VFL_DIR_RX;
vdev->fops = &csi_fops;
@@ -1067,61 +1494,184 @@ static int ti_csi2rx_v4l2_init(struct ti_csi2rx_dev *csi)
vdev->release = video_device_release_empty;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_IO_MC;
- vdev->lock = &csi->mutex;
- video_set_drvdata(vdev, csi);
+ vdev->lock = &ctx->mutex;
+ video_set_drvdata(vdev, ctx);
- csi->pad.flags = MEDIA_PAD_FL_SINK;
- vdev->entity.ops = &ti_csi2rx_video_entity_ops;
- ret = media_entity_pads_init(&csi->vdev.entity, 1, &csi->pad);
+ INIT_LIST_HEAD(&ctx->dma.queue);
+ INIT_LIST_HEAD(&ctx->dma.submitted);
+ spin_lock_init(&ctx->dma.lock);
+ ctx->dma.state = TI_CSI2RX_DMA_STOPPED;
+
+ ret = ti_csi2rx_init_dma(ctx);
if (ret)
return ret;
- csi->v4l2_dev.mdev = mdev;
-
- ret = v4l2_device_register(csi->dev, &csi->v4l2_dev);
+ ret = ti_csi2rx_init_vb2q(ctx);
if (ret)
- return ret;
+ goto cleanup_dma;
- ret = media_device_register(mdev);
- if (ret) {
- v4l2_device_unregister(&csi->v4l2_dev);
- media_device_cleanup(mdev);
- return ret;
- }
+ return 0;
+
+cleanup_dma:
+ dma_release_channel(ctx->dma.chan);
+ return ret;
+}
+
+static int ti_csi2rx_runtime_suspend(struct device *dev)
+{
+ struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+
+ if (csi->enable_count != 0)
+ return -EBUSY;
+
+ for (unsigned int i = 0; i < csi->num_ctx; i++)
+ dma_release_channel(csi->ctx[i].dma.chan);
return 0;
}
-static void ti_csi2rx_cleanup_dma(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_runtime_resume(struct device *dev)
{
- dma_free_coherent(csi->dev, csi->dma.drain.len,
- csi->dma.drain.vaddr, csi->dma.drain.paddr);
- csi->dma.drain.vaddr = NULL;
- dma_release_channel(csi->dma.chan);
+ struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+ int ret;
+
+ for (unsigned int i = 0; i < csi->num_ctx; i++) {
+ ret = ti_csi2rx_init_dma(&csi->ctx[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
-static void ti_csi2rx_cleanup_v4l2(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_suspend(struct device *dev)
{
- media_device_unregister(&csi->mdev);
- v4l2_device_unregister(&csi->v4l2_dev);
- media_device_cleanup(&csi->mdev);
+ struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+ enum ti_csi2rx_dma_state state;
+ struct ti_csi2rx_ctx *ctx;
+ struct ti_csi2rx_dma *dma;
+ unsigned long flags = 0;
+ int ret = 0;
+
+ /* If device was not in use we can simply suspend */
+ if (pm_runtime_status_suspended(dev))
+ return 0;
+
+ /*
+ * If device is running, assert the pixel reset to cleanly stop any
+ * on-going streams before we suspend.
+ */
+ writel(0, csi->shim + SHIM_CNTL);
+
+ for (unsigned int i = 0; i < csi->num_ctx; i++) {
+ ctx = &csi->ctx[i];
+ dma = &ctx->dma;
+
+ spin_lock_irqsave(&dma->lock, flags);
+ state = dma->state;
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ if (state != TI_CSI2RX_DMA_STOPPED) {
+ /* Disable source */
+ ret = v4l2_subdev_disable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT(0));
+ if (ret)
+ dev_err(csi->dev, "Failed to stop subdev stream\n");
+ }
+
+ /* Stop any on-going streams */
+ writel(0, csi->shim + SHIM_DMACNTX(ctx->idx));
+
+ /* Drain DMA */
+ ti_csi2rx_drain_dma(ctx);
+
+ /* Terminate DMA */
+ ret = dmaengine_terminate_sync(ctx->dma.chan);
+ if (ret)
+ dev_err(csi->dev, "Failed to stop DMA\n");
+ }
+
+ return ret;
}
-static void ti_csi2rx_cleanup_subdev(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_resume(struct device *dev)
{
- v4l2_async_nf_unregister(&csi->notifier);
- v4l2_async_nf_cleanup(&csi->notifier);
+ struct ti_csi2rx_dev *csi = dev_get_drvdata(dev);
+ struct ti_csi2rx_ctx *ctx;
+ struct ti_csi2rx_dma *dma;
+ struct ti_csi2rx_buffer *buf;
+ unsigned long flags = 0;
+ unsigned int reg;
+ int ret = 0;
+
+ /* If device was not in use, we can simply wakeup */
+ if (pm_runtime_status_suspended(dev))
+ return 0;
+
+ /* If device was in use before, restore all the running streams */
+ reg = SHIM_CNTL_PIX_RST;
+ writel(reg, csi->shim + SHIM_CNTL);
+
+ for (unsigned int i = 0; i < csi->num_ctx; i++) {
+ ctx = &csi->ctx[i];
+ dma = &ctx->dma;
+ spin_lock_irqsave(&dma->lock, flags);
+ if (dma->state != TI_CSI2RX_DMA_STOPPED) {
+ /* Re-submit all previously submitted buffers to DMA */
+ list_for_each_entry(buf, &ctx->dma.submitted, list) {
+ ti_csi2rx_start_dma(ctx, buf);
+ }
+ spin_unlock_irqrestore(&dma->lock, flags);
+
+ /* Restore stream config */
+ ti_csi2rx_setup_shim(ctx);
+
+ ret = v4l2_subdev_enable_streams(&csi->subdev,
+ TI_CSI2RX_PAD_FIRST_SOURCE + ctx->idx,
+ BIT(0));
+ if (ret)
+ dev_err(ctx->csi->dev, "Failed to start subdev\n");
+ } else {
+ spin_unlock_irqrestore(&dma->lock, flags);
+ }
+ }
+
+ return ret;
}
-static void ti_csi2rx_cleanup_vb2q(struct ti_csi2rx_dev *csi)
+static int ti_csi2rx_pm_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
{
- vb2_queue_release(&csi->vidq);
+ struct ti_csi2rx_dev *csi =
+ container_of(nb, struct ti_csi2rx_dev, pm_notifier);
+
+ switch (action) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ case PM_RESTORE_PREPARE:
+ ti_csi2rx_suspend(csi->dev);
+ break;
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ ti_csi2rx_resume(csi->dev);
+ break;
+ }
+
+ return NOTIFY_DONE;
}
+static const struct dev_pm_ops ti_csi2rx_pm_ops = {
+ RUNTIME_PM_OPS(ti_csi2rx_runtime_suspend, ti_csi2rx_runtime_resume,
+ NULL)
+};
+
static int ti_csi2rx_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
struct ti_csi2rx_dev *csi;
- int ret;
+ int ret = 0, i, count;
csi = devm_kzalloc(&pdev->dev, sizeof(*csi), GFP_KERNEL);
if (!csi)
@@ -1130,47 +1680,86 @@ static int ti_csi2rx_probe(struct platform_device *pdev)
csi->dev = &pdev->dev;
platform_set_drvdata(pdev, csi);
- mutex_init(&csi->mutex);
csi->shim = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(csi->shim)) {
ret = PTR_ERR(csi->shim);
- goto err_mutex;
+ return ret;
}
- ret = ti_csi2rx_init_dma(csi);
- if (ret)
- goto err_mutex;
+ csi->drain.len = DRAIN_BUFFER_SIZE;
+ csi->drain.vaddr = dma_alloc_coherent(csi->dev, csi->drain.len,
+ &csi->drain.paddr,
+ GFP_KERNEL);
+ if (!csi->drain.vaddr)
+ return -ENOMEM;
+
+ /* Only use as many contexts as the number of DMA channels allocated. */
+ count = of_property_count_strings(np, "dma-names");
+ if (count < 0) {
+ dev_err(csi->dev, "Failed to get DMA channel count: %d\n", count);
+ ret = count;
+ goto err_dma_chan;
+ }
+
+ csi->num_ctx = count;
+ if (csi->num_ctx > TI_CSI2RX_MAX_CTX) {
+ dev_err(csi->dev,
+ "%u DMA channels passed. Maximum is %u.\n",
+ csi->num_ctx, TI_CSI2RX_MAX_CTX);
+ ret = -EINVAL;
+ goto err_dma_chan;
+ }
ret = ti_csi2rx_v4l2_init(csi);
if (ret)
- goto err_dma;
+ goto err_dma_chan;
+
+ for (i = 0; i < csi->num_ctx; i++) {
+ csi->ctx[i].idx = i;
+ csi->ctx[i].csi = csi;
+ ret = ti_csi2rx_init_ctx(&csi->ctx[i]);
+ if (ret)
+ goto err_ctx;
+ }
- ret = ti_csi2rx_init_vb2q(csi);
- if (ret)
- goto err_v4l2;
+ pm_runtime_set_active(csi->dev);
+ pm_runtime_enable(csi->dev);
ret = ti_csi2rx_notifier_register(csi);
if (ret)
- goto err_vb2q;
+ goto err_ctx;
ret = devm_of_platform_populate(csi->dev);
if (ret) {
dev_err(csi->dev, "Failed to create children: %d\n", ret);
- goto err_subdev;
+ goto err_notifier;
+ }
+
+ /*
+ * Use PM notifier instead of .suspend/.resume callbacks because the
+ * ordering of callbacks among camera pipeline devices (sensor, serdes,
+ * CSI bridge) cannot be enforced even with device links. The notifier
+ * is called when the system is fully functional, ensuring all
+ * dependencies are available when stopping/starting streams.
+ */
+ csi->pm_notifier.notifier_call = ti_csi2rx_pm_notifier;
+ ret = register_pm_notifier(&csi->pm_notifier);
+ if (ret) {
+ dev_err(csi->dev, "Failed to create PM notifier: %d\n", ret);
+ goto err_notifier;
}
return 0;
-err_subdev:
- ti_csi2rx_cleanup_subdev(csi);
-err_vb2q:
- ti_csi2rx_cleanup_vb2q(csi);
-err_v4l2:
+err_notifier:
+ ti_csi2rx_cleanup_notifier(csi);
+err_ctx:
+ while (i--)
+ ti_csi2rx_cleanup_ctx(&csi->ctx[i]);
ti_csi2rx_cleanup_v4l2(csi);
-err_dma:
- ti_csi2rx_cleanup_dma(csi);
-err_mutex:
- mutex_destroy(&csi->mutex);
+err_dma_chan:
+ dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr,
+ csi->drain.paddr);
return ret;
}
@@ -1178,14 +1767,19 @@ static void ti_csi2rx_remove(struct platform_device *pdev)
{
struct ti_csi2rx_dev *csi = platform_get_drvdata(pdev);
- video_unregister_device(&csi->vdev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ pm_runtime_set_suspended(&pdev->dev);
- ti_csi2rx_cleanup_vb2q(csi);
- ti_csi2rx_cleanup_subdev(csi);
- ti_csi2rx_cleanup_v4l2(csi);
- ti_csi2rx_cleanup_dma(csi);
+ for (unsigned int i = 0; i < csi->num_ctx; i++)
+ ti_csi2rx_cleanup_ctx(&csi->ctx[i]);
+
+ ti_csi2rx_cleanup_notifier(csi);
+ unregister_pm_notifier(&csi->pm_notifier);
- mutex_destroy(&csi->mutex);
+ ti_csi2rx_cleanup_v4l2(csi);
+ dma_free_coherent(csi->dev, csi->drain.len, csi->drain.vaddr,
+ csi->drain.paddr);
+ pm_runtime_disable(&pdev->dev);
}
static const struct of_device_id ti_csi2rx_of_match[] = {
@@ -1200,6 +1794,7 @@ static struct platform_driver ti_csi2rx_pdrv = {
.driver = {
.name = TI_CSI2RX_MODULE_NAME,
.of_match_table = ti_csi2rx_of_match,
+ .pm = &ti_csi2rx_pm_ops,
},
};
diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c
index 0e91e87bda9b..cb0a5a07a3d4 100644
--- a/drivers/media/platform/ti/vpe/vip.c
+++ b/drivers/media/platform/ti/vpe/vip.c
@@ -9,6 +9,7 @@
*/
#include <linux/clk.h>
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -3389,7 +3390,6 @@ static int vip_probe_complete(struct platform_device *pdev)
struct vip_port *port;
struct vip_dev *dev;
struct device_node *parent = pdev->dev.of_node;
- struct fwnode_handle *ep = NULL;
unsigned int syscon_args[5];
int ret, i, slice_id, port_id, p;
@@ -3411,8 +3411,9 @@ static int vip_probe_complete(struct platform_device *pdev)
ctrl->syscon_bit_field[i] = syscon_args[i + 1];
for (p = 0; p < (VIP_NUM_PORTS * VIP_NUM_SLICES); p++) {
- ep = fwnode_graph_get_next_endpoint_by_regs(of_fwnode_handle(parent),
- p, 0);
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_next_endpoint_by_regs(
+ of_fwnode_handle(parent), p, 0);
if (!ep)
continue;
@@ -3447,7 +3448,6 @@ static int vip_probe_complete(struct platform_device *pdev)
port = dev->ports[port_id];
vip_register_subdev_notify(port, ep);
- fwnode_handle_put(ep);
}
return 0;
}
@@ -3472,7 +3472,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)
ret = devm_request_irq(&pdev->dev, dev->irq, vip_irq,
0, VIP_MODULE_NAME, dev);
if (ret < 0)
- return -ENOMEM;
+ return ret;
spin_lock_init(&dev->slock);
mutex_init(&dev->mutex);
@@ -3490,7 +3490,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)
parser = devm_kzalloc(&pdev->dev, sizeof(*dev->parser), GFP_KERNEL);
if (!parser)
- return PTR_ERR_OR_ZERO(parser);
+ return -ENOMEM;
parser->base = dev->base + (slice ? VIP_SLICE1_PARSER : VIP_SLICE0_PARSER);
if (IS_ERR(parser->base))
@@ -3502,7 +3502,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)
dev->sc_assigned = VIP_NOT_ASSIGNED;
sc = devm_kzalloc(&pdev->dev, sizeof(*dev->sc), GFP_KERNEL);
if (!sc)
- return PTR_ERR_OR_ZERO(sc);
+ return -ENOMEM;
sc->base = dev->base + (slice ? VIP_SLICE1_SC : VIP_SLICE0_SC);
if (IS_ERR(sc->base))
@@ -3514,7 +3514,7 @@ static int vip_probe_slice(struct platform_device *pdev, int slice)
dev->csc_assigned = VIP_NOT_ASSIGNED;
csc = devm_kzalloc(&pdev->dev, sizeof(*dev->csc), GFP_KERNEL);
if (!csc)
- return PTR_ERR_OR_ZERO(csc);
+ return -ENOMEM;
csc->base = dev->base + (slice ? VIP_SLICE1_CSC : VIP_SLICE0_CSC);
if (IS_ERR(csc->base))
diff --git a/drivers/media/platform/ti/vpe/vpe.c b/drivers/media/platform/ti/vpe/vpe.c
index a7e5a85e72a1..81bd1f9cee30 100644
--- a/drivers/media/platform/ti/vpe/vpe.c
+++ b/drivers/media/platform/ti/vpe/vpe.c
@@ -2539,7 +2539,8 @@ static int vpe_probe(struct platform_device *pdev)
"vpe_top");
if (!dev->res) {
dev_err(&pdev->dev, "missing 'vpe_top' resources data\n");
- return -ENODEV;
+ ret = -ENODEV;
+ goto v4l2_dev_unreg;
}
/*
diff --git a/drivers/media/platform/verisilicon/hantro_hw.h b/drivers/media/platform/verisilicon/hantro_hw.h
index 5f2011529f02..13e573f1f19d 100644
--- a/drivers/media/platform/verisilicon/hantro_hw.h
+++ b/drivers/media/platform/verisilicon/hantro_hw.h
@@ -427,7 +427,6 @@ extern const struct hantro_postproc_ops rockchip_vpu981_postproc_ops;
extern const u32 hantro_vp8_dec_mc_filter[8][6];
void hantro_watchdog(struct work_struct *work);
-void hantro_run(struct hantro_ctx *ctx);
void hantro_irq_done(struct hantro_dev *vpu,
enum vb2_buffer_state result);
void hantro_start_prepare_run(struct hantro_ctx *ctx);
diff --git a/drivers/media/platform/verisilicon/hantro_v4l2.c b/drivers/media/platform/verisilicon/hantro_v4l2.c
index fcf3bd9bcda2..83af9fa1ce94 100644
--- a/drivers/media/platform/verisilicon/hantro_v4l2.c
+++ b/drivers/media/platform/verisilicon/hantro_v4l2.c
@@ -222,6 +222,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
unsigned int num_fmts, i, j = 0;
bool skip_mode_none, enum_all_formats;
u32 index = f->index & ~V4L2_FMTDESC_FLAG_ENUM_ALL;
+ bool need_postproc = ctx->need_postproc;
/*
* If the V4L2_FMTDESC_FLAG_ENUM_ALL flag is set, we want to enumerate all
@@ -230,6 +231,9 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
enum_all_formats = !!(f->index & V4L2_FMTDESC_FLAG_ENUM_ALL);
f->index = index;
+ if (enum_all_formats)
+ need_postproc = HANTRO_AUTO_POSTPROC;
+
/*
* When dealing with an encoder:
* - on the capture side we want to filter out all MODE_NONE formats.
@@ -242,7 +246,7 @@ static int vidioc_enum_fmt(struct file *file, void *priv,
*/
skip_mode_none = capture == ctx->is_encoder;
- formats = hantro_get_formats(ctx, &num_fmts, HANTRO_AUTO_POSTPROC);
+ formats = hantro_get_formats(ctx, &num_fmts, need_postproc);
for (i = 0; i < num_fmts; i++) {
bool mode_none = formats[i].codec_mode == HANTRO_MODE_NONE;
fmt = &formats[i];
diff --git a/drivers/media/radio/radio-si476x.c b/drivers/media/radio/radio-si476x.c
index 9980346cb5ea..bfe89782dce4 100644
--- a/drivers/media/radio/radio-si476x.c
+++ b/drivers/media/radio/radio-si476x.c
@@ -1493,6 +1493,7 @@ static int si476x_radio_probe(struct platform_device *pdev)
return 0;
exit:
v4l2_ctrl_handler_free(radio->videodev.ctrl_handler);
+ v4l2_device_unregister(&radio->v4l2dev);
return rval;
}
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index 156cca2866aa..d9547f625e30 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -502,7 +502,7 @@ static void tea5764_i2c_remove(struct i2c_client *client)
/* I2C subsystem interface */
static const struct i2c_device_id tea5764_id[] = {
- { "radio-tea5764" },
+ { .name = "radio-tea5764" },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE(i2c, tea5764_id);
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
index 9572a866defb..bd8bd295a9ec 100644
--- a/drivers/media/radio/saa7706h.c
+++ b/drivers/media/radio/saa7706h.c
@@ -395,8 +395,8 @@ static void saa7706h_remove(struct i2c_client *client)
}
static const struct i2c_device_id saa7706h_id[] = {
- { DRIVER_NAME },
- {}
+ { .name = DRIVER_NAME },
+ { }
};
MODULE_DEVICE_TABLE(i2c, saa7706h_id);
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index 3932a449a1b1..a1e570af9c59 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -28,7 +28,7 @@
/* I2C Device ID List */
static const struct i2c_device_id si470x_i2c_id[] = {
/* Generic Entry */
- { "si470x" },
+ { .name = "si470x" },
/* Terminating entry */
{ }
};
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 318b5f6d4202..869b1e7e34b9 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -565,8 +565,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
{
struct si470x_device *radio;
struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *endpoint;
- int i, int_end_size, retval;
+ int int_end_size, retval;
unsigned char version_warning = 0;
/* private data allocation and initialization */
@@ -595,12 +594,8 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
iface_desc = intf->cur_altsetting;
/* Set up interrupt endpoint information. */
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (usb_endpoint_is_int_in(endpoint))
- radio->int_in_endpoint = endpoint;
- }
- if (!radio->int_in_endpoint) {
+ retval = usb_find_int_in_endpoint(iface_desc, &radio->int_in_endpoint);
+ if (retval) {
dev_info(&intf->dev, "could not find interrupt in endpoint\n");
retval = -EIO;
goto err_usbbuf;
diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c
index e71272c6de37..0c0354566b0a 100644
--- a/drivers/media/radio/si4713/si4713.c
+++ b/drivers/media/radio/si4713/si4713.c
@@ -1639,7 +1639,7 @@ static void si4713_remove(struct i2c_client *client)
/* si4713_i2c_driver - i2c driver interface */
static const struct i2c_device_id si4713_id[] = {
- { "si4713" },
+ { .name = "si4713" },
{ }
};
MODULE_DEVICE_TABLE(i2c, si4713_id);
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
index 3a6d7926e856..2596d7f4f3f1 100644
--- a/drivers/media/radio/tef6862.c
+++ b/drivers/media/radio/tef6862.c
@@ -173,8 +173,8 @@ static void tef6862_remove(struct i2c_client *client)
}
static const struct i2c_device_id tef6862_id[] = {
- { DRIVER_NAME },
- {}
+ { .name = DRIVER_NAME },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tef6862_id);
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 9bb27ba8240f..049a73b5f882 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -290,6 +290,10 @@ static const struct imon_usb_dev_descr imon_OEM_VFD = {
{ 0x000100000000ffeell, KEY_VOLUMEUP },
{ 0x010000000000ffeell, KEY_VOLUMEDOWN },
{ 0x000000000100ffeell, KEY_MUTE },
+ /* iMON VFD HID OEM v1.2 */
+ { 0x000000000a00ffeell, KEY_VOLUMEUP },
+ { 0x000000000b00ffeell, KEY_VOLUMEDOWN },
+ { 0x000000000c00ffeell, KEY_MUTE },
/* 0xffdc iMON MCE VFD */
{ 0x00010000ffffffeell, KEY_VOLUMEUP },
{ 0x01000000ffffffeell, KEY_VOLUMEDOWN },
diff --git a/drivers/media/rc/imon_raw.c b/drivers/media/rc/imon_raw.c
index 3a526dea6532..295acd6ba9e1 100644
--- a/drivers/media/rc/imon_raw.c
+++ b/drivers/media/rc/imon_raw.c
@@ -105,26 +105,16 @@ static void imon_ir_rx(struct urb *urb)
static int imon_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- struct usb_endpoint_descriptor *ir_ep = NULL;
- struct usb_host_interface *idesc;
+ struct usb_endpoint_descriptor *ir_ep;
struct usb_device *udev;
struct rc_dev *rcdev;
struct imon *imon;
- int i, ret;
+ int ret;
udev = interface_to_usbdev(intf);
- idesc = intf->cur_altsetting;
-
- for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
- struct usb_endpoint_descriptor *ep = &idesc->endpoint[i].desc;
-
- if (usb_endpoint_is_int_in(ep)) {
- ir_ep = ep;
- break;
- }
- }
- if (!ir_ep) {
+ ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ir_ep);
+ if (ret) {
dev_err(&intf->dev, "IR endpoint missing");
return -ENODEV;
}
diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c
index 089833e41178..ee645882dd5a 100644
--- a/drivers/media/rc/ir_toy.c
+++ b/drivers/media/rc/ir_toy.c
@@ -393,27 +393,15 @@ static int irtoy_probe(struct usb_interface *intf,
{
struct usb_host_interface *idesc = intf->cur_altsetting;
struct usb_device *usbdev = interface_to_usbdev(intf);
- struct usb_endpoint_descriptor *ep_in = NULL;
- struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_endpoint_descriptor *ep = NULL;
+ struct usb_endpoint_descriptor *ep_in, *ep_out;
struct irtoy *irtoy;
struct rc_dev *rc;
struct urb *urb;
- int i, pipe, err = -ENOMEM;
+ int pipe, err;
- for (i = 0; i < idesc->desc.bNumEndpoints; i++) {
- ep = &idesc->endpoint[i].desc;
-
- if (!ep_in && usb_endpoint_is_bulk_in(ep) &&
- usb_endpoint_maxp(ep) == MAX_PACKET)
- ep_in = ep;
-
- if (!ep_out && usb_endpoint_is_bulk_out(ep) &&
- usb_endpoint_maxp(ep) == MAX_PACKET)
- ep_out = ep;
- }
-
- if (!ep_in || !ep_out) {
+ err = usb_find_common_endpoints(idesc, &ep_in, &ep_out, NULL, NULL);
+ if (err || usb_endpoint_maxp(ep_in) != MAX_PACKET ||
+ usb_endpoint_maxp(ep_out) != MAX_PACKET) {
dev_err(&intf->dev, "required endpoints not found\n");
return -ENODEV;
}
@@ -422,6 +410,7 @@ static int irtoy_probe(struct usb_interface *intf,
if (!irtoy)
return -ENOMEM;
+ err = -ENOMEM;
irtoy->in = kmalloc(MAX_PACKET, GFP_KERNEL);
if (!irtoy->in)
goto free_irtoy;
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 6a9e4382a224..39ba7f6a2549 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -397,6 +397,8 @@ static const struct usb_device_id mceusb_dev_table[] = {
{ USB_DEVICE(VENDOR_COMPRO, 0x3082) },
/* Northstar Systems, Inc. eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
+ /* Northstar Systems, Inc. eHome Infrared Transceiver - variant */
+ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe033) },
/* TiVo PC IR Receiver */
{ USB_DEVICE(VENDOR_TIVO, 0x2000),
.driver_info = TIVO_KIT },
diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
index a8a76434989c..fd69b4ee16f4 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
@@ -594,8 +594,10 @@ static int __init vidtv_bridge_init(void)
int ret;
ret = platform_device_register(&vidtv_bridge_dev);
- if (ret)
+ if (ret) {
+ platform_device_put(&vidtv_bridge_dev);
return ret;
+ }
ret = platform_driver_register(&vidtv_bridge_driver);
if (ret)
diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c
index c382e9e94c32..6e5fe402976b 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_demod.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c
@@ -407,8 +407,8 @@ static const struct dvb_frontend_ops vidtv_demod_ops = {
};
static const struct i2c_device_id vidtv_demod_i2c_id_table[] = {
- { "dvb_vidtv_demod" },
- {}
+ { .name = "dvb_vidtv_demod" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, vidtv_demod_i2c_id_table);
diff --git a/drivers/media/test-drivers/vidtv/vidtv_mux.c b/drivers/media/test-drivers/vidtv/vidtv_mux.c
index f0134e38a1fb..ea2ea53102f9 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_mux.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_mux.c
@@ -101,7 +101,8 @@ static int vidtv_mux_pid_ctx_init(struct vidtv_mux *m)
/* add a ctx for all PMT sections */
while (p) {
pid = vidtv_psi_get_pat_program_pid(p);
- vidtv_mux_create_pid_ctx_once(m, pid);
+ if (!vidtv_mux_create_pid_ctx_once(m, pid))
+ goto free;
p = p->next;
}
@@ -170,6 +171,9 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m)
nit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_NIT_PID);
eit_ctx = vidtv_mux_get_pid_ctx(m, VIDTV_EIT_PID);
+ if (!pat_ctx || !sdt_ctx || !nit_ctx || !eit_ctx)
+ return 0;
+
pat_args.offset = m->mux_buf_offset;
pat_args.continuity_counter = &pat_ctx->cc;
@@ -186,6 +190,8 @@ static u32 vidtv_mux_push_si(struct vidtv_mux *m)
}
pmt_ctx = vidtv_mux_get_pid_ctx(m, pmt_pid);
+ if (!pmt_ctx)
+ continue;
pmt_args.offset = m->mux_buf_offset;
pmt_args.pmt = m->si.pmt_secs[i];
diff --git a/drivers/media/test-drivers/vidtv/vidtv_tuner.c b/drivers/media/test-drivers/vidtv/vidtv_tuner.c
index ee55df4029bc..bd50b86e927c 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_tuner.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_tuner.c
@@ -385,8 +385,8 @@ static const struct dvb_tuner_ops vidtv_tuner_ops = {
};
static const struct i2c_device_id vidtv_tuner_i2c_id_table[] = {
- { "dvb_vidtv_tuner" },
- {}
+ { .name = "dvb_vidtv_tuner" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, vidtv_tuner_i2c_id_table);
diff --git a/drivers/media/test-drivers/vimc/vimc-core.c b/drivers/media/test-drivers/vimc/vimc-core.c
index 15167e127461..fee0c7a09c4f 100644
--- a/drivers/media/test-drivers/vimc/vimc-core.c
+++ b/drivers/media/test-drivers/vimc/vimc-core.c
@@ -421,6 +421,7 @@ static int __init vimc_init(void)
if (ret) {
dev_err(&vimc_pdev.dev,
"platform device registration failed (err=%d)\n", ret);
+ platform_device_put(&vimc_pdev);
return ret;
}
diff --git a/drivers/media/test-drivers/visl/visl-core.c b/drivers/media/test-drivers/visl/visl-core.c
index 127ab18bce99..5e9bdd425d4a 100644
--- a/drivers/media/test-drivers/visl/visl-core.c
+++ b/drivers/media/test-drivers/visl/visl-core.c
@@ -339,6 +339,10 @@ static int visl_open(struct file *file)
}
ctx->tpg_str_buf = kzalloc(TPG_STR_BUF_SZ, GFP_KERNEL);
+ if (!ctx->tpg_str_buf) {
+ rc = -ENOMEM;
+ goto free_ctx;
+ }
v4l2_fh_init(&ctx->fh, video_devdata(file));
ctx->dev = dev;
diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index c8bf9b4d406c..62cfb5feb2cf 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -2289,8 +2289,10 @@ static int __init vivid_init(void)
}
}
ret = platform_device_register(&vivid_pdev);
- if (ret)
+ if (ret) {
+ platform_device_put(&vivid_pdev);
goto free_output_strings;
+ }
ret = platform_driver_register(&vivid_pdrv);
if (ret)
goto unreg_device;
@@ -2311,7 +2313,7 @@ static int __init vivid_init(void)
destroy_hdmi_wq:
destroy_workqueue(update_hdmi_ctrls_workqueue);
unreg_driver:
- platform_driver_register(&vivid_pdrv);
+ platform_driver_unregister(&vivid_pdrv);
unreg_device:
platform_device_unregister(&vivid_pdev);
free_output_strings:
diff --git a/drivers/media/test-drivers/vivid/vivid-ctrls.c b/drivers/media/test-drivers/vivid/vivid-ctrls.c
index f94c15ff84f7..a8a134b36720 100644
--- a/drivers/media/test-drivers/vivid/vivid-ctrls.c
+++ b/drivers/media/test-drivers/vivid/vivid-ctrls.c
@@ -609,17 +609,24 @@ static int vivid_vid_cap_s_ctrl(struct v4l2_ctrl *ctrl)
break;
case VIVID_CID_REDUCED_FPS:
dev->reduced_fps = ctrl->val;
- vivid_update_format_cap(dev, true);
+ if (dev->input_type[dev->input] == HDMI)
+ vivid_update_reduced_fps(dev);
break;
case VIVID_CID_HAS_CROP_CAP:
+ if (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
dev->has_crop_cap = ctrl->val;
vivid_update_format_cap(dev, true);
break;
case VIVID_CID_HAS_COMPOSE_CAP:
+ if (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
dev->has_compose_cap = ctrl->val;
vivid_update_format_cap(dev, true);
break;
case VIVID_CID_HAS_SCALER_CAP:
+ if (vb2_is_busy(&dev->vb_vid_cap_q))
+ return -EBUSY;
dev->has_scaler_cap = ctrl->val;
vivid_update_format_cap(dev, true);
break;
@@ -1116,14 +1123,20 @@ static int vivid_vid_out_s_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case VIVID_CID_HAS_CROP_OUT:
+ if (vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
dev->has_crop_out = ctrl->val;
vivid_update_format_out(dev);
break;
case VIVID_CID_HAS_COMPOSE_OUT:
+ if (vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
dev->has_compose_out = ctrl->val;
vivid_update_format_out(dev);
break;
case VIVID_CID_HAS_SCALER_OUT:
+ if (vb2_is_busy(&dev->vb_vid_out_q))
+ return -EBUSY;
dev->has_scaler_out = ctrl->val;
vivid_update_format_out(dev);
break;
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index b95f06a9b5ae..e20449084709 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -364,6 +364,24 @@ static enum tpg_pixel_aspect vivid_get_pixel_aspect(const struct vivid_dev *dev)
return TPG_PIXEL_ASPECT_SQUARE;
}
+void vivid_update_reduced_fps(struct vivid_dev *dev)
+{
+ struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
+ unsigned int size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
+ u64 pixelclock;
+
+ if (dev->reduced_fps && can_reduce_fps(bt)) {
+ pixelclock = div_u64(bt->pixelclock * 1000, 1001);
+ bt->flags |= V4L2_DV_FL_REDUCED_FPS;
+ } else {
+ pixelclock = bt->pixelclock;
+ bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
+ }
+ dev->timeperframe_vid_cap = (struct v4l2_fract) {
+ size / 100, (u32)pixelclock / 100
+ };
+}
+
/*
* Called whenever the format has to be reset which can occur when
* changing inputs, standard, timings, etc.
@@ -372,8 +390,12 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
{
struct v4l2_bt_timings *bt = &dev->dv_timings_cap[dev->input].bt;
u32 dims[V4L2_CTRL_MAX_DIMS] = {};
- unsigned size;
- u64 pixelclock;
+
+ /*
+ * This resets the format, so must never be called while vb2_is_busy().
+ */
+ if (WARN_ON(vb2_is_busy(&dev->vb_vid_cap_q)))
+ return;
switch (dev->input_type[dev->input]) {
case WEBCAM:
@@ -402,17 +424,7 @@ void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls)
case HDMI:
dev->src_rect.width = bt->width;
dev->src_rect.height = bt->height;
- size = V4L2_DV_BT_FRAME_WIDTH(bt) * V4L2_DV_BT_FRAME_HEIGHT(bt);
- if (dev->reduced_fps && can_reduce_fps(bt)) {
- pixelclock = div_u64(bt->pixelclock * 1000, 1001);
- bt->flags |= V4L2_DV_FL_REDUCED_FPS;
- } else {
- pixelclock = bt->pixelclock;
- bt->flags &= ~V4L2_DV_FL_REDUCED_FPS;
- }
- dev->timeperframe_vid_cap = (struct v4l2_fract) {
- size / 100, (u32)pixelclock / 100
- };
+ vivid_update_reduced_fps(dev);
if (bt->interlaced)
dev->field_cap = V4L2_FIELD_ALTERNATE;
else
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.h b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
index 38a99f7e038e..d08a85927510 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.h
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.h
@@ -9,6 +9,7 @@
#define _VIVID_VID_CAP_H_
void vivid_update_quality(struct vivid_dev *dev);
+void vivid_update_reduced_fps(struct vivid_dev *dev);
void vivid_update_format_cap(struct vivid_dev *dev, bool keep_controls);
void vivid_update_outputs(struct vivid_dev *dev);
void vivid_update_connected_outputs(struct vivid_dev *dev);
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-out.c b/drivers/media/test-drivers/vivid/vivid-vid-out.c
index 8c037b90833e..23e1d5a189ee 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-out.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-out.c
@@ -214,6 +214,12 @@ void vivid_update_format_out(struct vivid_dev *dev)
unsigned size, p;
u64 pixelclock;
+ /*
+ * This resets the format, so must never be called while vb2_is_busy().
+ */
+ if (WARN_ON(vb2_is_busy(&dev->vb_vid_out_q)))
+ return;
+
switch (dev->output_type[dev->output]) {
case SVID:
default:
diff --git a/drivers/media/tuners/e4000.c b/drivers/media/tuners/e4000.c
index b83f37a77224..94abb3715401 100644
--- a/drivers/media/tuners/e4000.c
+++ b/drivers/media/tuners/e4000.c
@@ -719,8 +719,8 @@ static void e4000_remove(struct i2c_client *client)
}
static const struct i2c_device_id e4000_id_table[] = {
- { "e4000" },
- {}
+ { .name = "e4000" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, e4000_id_table);
diff --git a/drivers/media/tuners/fc2580.c b/drivers/media/tuners/fc2580.c
index 75087d9b224f..b76fa1320f5f 100644
--- a/drivers/media/tuners/fc2580.c
+++ b/drivers/media/tuners/fc2580.c
@@ -600,8 +600,8 @@ static void fc2580_remove(struct i2c_client *client)
}
static const struct i2c_device_id fc2580_id_table[] = {
- { "fc2580" },
- {}
+ { .name = "fc2580" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, fc2580_id_table);
diff --git a/drivers/media/tuners/m88rs6000t.c b/drivers/media/tuners/m88rs6000t.c
index 0a724cdf0f6d..1addb3d229cf 100644
--- a/drivers/media/tuners/m88rs6000t.c
+++ b/drivers/media/tuners/m88rs6000t.c
@@ -709,8 +709,8 @@ static void m88rs6000t_remove(struct i2c_client *client)
}
static const struct i2c_device_id m88rs6000t_id[] = {
- { "m88rs6000t" },
- {}
+ { .name = "m88rs6000t" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, m88rs6000t_id);
diff --git a/drivers/media/tuners/mt2060.c b/drivers/media/tuners/mt2060.c
index ef3196e6bd30..c57233c7441a 100644
--- a/drivers/media/tuners/mt2060.c
+++ b/drivers/media/tuners/mt2060.c
@@ -514,8 +514,8 @@ static void mt2060_remove(struct i2c_client *client)
}
static const struct i2c_device_id mt2060_id_table[] = {
- { "mt2060" },
- {}
+ { .name = "mt2060" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mt2060_id_table);
diff --git a/drivers/media/tuners/mxl301rf.c b/drivers/media/tuners/mxl301rf.c
index cfc78891ce03..1d84456cb19b 100644
--- a/drivers/media/tuners/mxl301rf.c
+++ b/drivers/media/tuners/mxl301rf.c
@@ -317,8 +317,8 @@ static void mxl301rf_remove(struct i2c_client *client)
static const struct i2c_device_id mxl301rf_id[] = {
- { "mxl301rf" },
- {}
+ { .name = "mxl301rf" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mxl301rf_id);
diff --git a/drivers/media/tuners/qm1d1b0004.c b/drivers/media/tuners/qm1d1b0004.c
index 07ad84f42c9f..59d98681e674 100644
--- a/drivers/media/tuners/qm1d1b0004.c
+++ b/drivers/media/tuners/qm1d1b0004.c
@@ -243,8 +243,8 @@ static void qm1d1b0004_remove(struct i2c_client *client)
static const struct i2c_device_id qm1d1b0004_id[] = {
- { "qm1d1b0004" },
- {}
+ { .name = "qm1d1b0004" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, qm1d1b0004_id);
diff --git a/drivers/media/tuners/qm1d1c0042.c b/drivers/media/tuners/qm1d1c0042.c
index db60562ad698..2d19cfdb67b9 100644
--- a/drivers/media/tuners/qm1d1c0042.c
+++ b/drivers/media/tuners/qm1d1c0042.c
@@ -434,8 +434,8 @@ static void qm1d1c0042_remove(struct i2c_client *client)
static const struct i2c_device_id qm1d1c0042_id[] = {
- { "qm1d1c0042" },
- {}
+ { .name = "qm1d1c0042" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, qm1d1c0042_id);
diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c
index 4d67e347c22f..d517a91e6fbc 100644
--- a/drivers/media/tuners/si2157.c
+++ b/drivers/media/tuners/si2157.c
@@ -1097,11 +1097,11 @@ static void si2157_remove(struct i2c_client *client)
* all SiLabs TER tuners, as the driver should auto-detect it.
*/
static const struct i2c_device_id si2157_id_table[] = {
- {"si2157", SI2157},
- {"si2146", SI2146},
- {"si2141", SI2141},
- {"si2177", SI2177},
- {}
+ { .name = "si2157", .driver_data = SI2157 },
+ { .name = "si2146", .driver_data = SI2146 },
+ { .name = "si2141", .driver_data = SI2141 },
+ { .name = "si2177", .driver_data = SI2177 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, si2157_id_table);
diff --git a/drivers/media/tuners/tda18212.c b/drivers/media/tuners/tda18212.c
index 5f583c010408..18d1850691fb 100644
--- a/drivers/media/tuners/tda18212.c
+++ b/drivers/media/tuners/tda18212.c
@@ -254,8 +254,8 @@ static void tda18212_remove(struct i2c_client *client)
}
static const struct i2c_device_id tda18212_id[] = {
- { "tda18212" },
- {}
+ { .name = "tda18212" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tda18212_id);
diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c
index caaf5e0d0c9b..7bb945ba0989 100644
--- a/drivers/media/tuners/tda18250.c
+++ b/drivers/media/tuners/tda18250.c
@@ -868,8 +868,8 @@ static void tda18250_remove(struct i2c_client *client)
}
static const struct i2c_device_id tda18250_id_table[] = {
- { "tda18250" },
- {}
+ { .name = "tda18250" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tda18250_id_table);
diff --git a/drivers/media/tuners/tua9001.c b/drivers/media/tuners/tua9001.c
index c0aed1b441e2..fcdbf1a0c1d8 100644
--- a/drivers/media/tuners/tua9001.c
+++ b/drivers/media/tuners/tua9001.c
@@ -245,8 +245,8 @@ static void tua9001_remove(struct i2c_client *client)
}
static const struct i2c_device_id tua9001_id_table[] = {
- { "tua9001" },
- {}
+ { .name = "tua9001" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tua9001_id_table);
diff --git a/drivers/media/usb/airspy/airspy.c b/drivers/media/usb/airspy/airspy.c
index 8f6b721ba107..57edb42463e8 100644
--- a/drivers/media/usb/airspy/airspy.c
+++ b/drivers/media/usb/airspy/airspy.c
@@ -522,11 +522,13 @@ static int airspy_start_streaming(struct vb2_queue *vq, unsigned int count)
dev_dbg(s->dev, "\n");
- if (!s->udev)
- return -ENODEV;
-
mutex_lock(&s->v4l2_lock);
+ if (!s->udev) {
+ ret = -ENODEV;
+ goto err_clear_bit;
+ }
+
s->sequence = 0;
set_bit(POWER_ON, &s->flags);
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index b75535d6abaf..69b24205bc56 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -1573,7 +1573,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
dev->video_mode.end_point_addr,
dev->video_mode.num_alt);
- dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->video_mode.num_alt, GFP_KERNEL);
+ dev->video_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->video_mode.num_alt, GFP_KERNEL);
if (dev->video_mode.alt_max_pkt_size == NULL)
return -ENOMEM;
@@ -1614,7 +1615,8 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
dev->vbi_mode.num_alt);
/* compute alternate max packet sizes for vbi */
- dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->vbi_mode.num_alt, GFP_KERNEL);
+ dev->vbi_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->vbi_mode.num_alt, GFP_KERNEL);
if (dev->vbi_mode.alt_max_pkt_size == NULL)
return -ENOMEM;
@@ -1656,7 +1658,9 @@ static int cx231xx_init_v4l2(struct cx231xx *dev,
"sliced CC EndPoint Addr 0x%x, Alternate settings: %i\n",
dev->sliced_cc_mode.end_point_addr,
dev->sliced_cc_mode.num_alt);
- dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->sliced_cc_mode.num_alt, GFP_KERNEL);
+ dev->sliced_cc_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->sliced_cc_mode.num_alt,
+ GFP_KERNEL);
if (dev->sliced_cc_mode.alt_max_pkt_size == NULL)
return -ENOMEM;
@@ -1720,7 +1724,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
udev = interface_to_usbdev(interface);
/* allocate memory for our device state and initialize it */
- dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL);
+ dev = devm_kzalloc(&interface->dev, sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
retval = -ENOMEM;
goto err_if;
@@ -1850,7 +1854,9 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
dev->ts1_mode.end_point_addr,
dev->ts1_mode.num_alt);
- dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&udev->dev, 32, dev->ts1_mode.num_alt, GFP_KERNEL);
+ dev->ts1_mode.alt_max_pkt_size = devm_kmalloc_array(&interface->dev, 32,
+ dev->ts1_mode.num_alt,
+ GFP_KERNEL);
if (dev->ts1_mode.alt_max_pkt_size == NULL) {
retval = -ENOMEM;
goto err_video_alt;
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 4a0ce9c5ee4b..da0422c65e5f 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -1248,8 +1248,10 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count)
dev->max_pkt_size,
dev->packet_multiplier,
em28xx_urb_data_copy);
- if (rc < 0)
+ if (rc < 0) {
+ res_free(dev, vq->type);
return rc;
+ }
/*
* djh: it's not clear whether this code is still needed. I'm
diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c
index 567f851d5896..0901d79e827d 100644
--- a/drivers/media/usb/go7007/s2250-board.c
+++ b/drivers/media/usb/go7007/s2250-board.c
@@ -611,7 +611,7 @@ static void s2250_remove(struct i2c_client *client)
}
static const struct i2c_device_id s2250_id[] = {
- { "s2250" },
+ { .name = "s2250" },
{ }
};
MODULE_DEVICE_TABLE(i2c, s2250_id);
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index f3d3f441c851..594d73e50b9f 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -208,22 +208,17 @@ error:
static void gspca_input_create_urb(struct gspca_dev *gspca_dev)
{
struct usb_interface *intf;
- struct usb_host_interface *intf_desc;
struct usb_endpoint_descriptor *ep;
- int i;
+ int ret;
if (gspca_dev->sd_desc->int_pkt_scan) {
intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
- intf_desc = intf->cur_altsetting;
- for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
- ep = &intf_desc->endpoint[i].desc;
- if (usb_endpoint_dir_in(ep) &&
- usb_endpoint_xfer_int(ep)) {
- alloc_and_submit_int_urb(gspca_dev, ep);
- break;
- }
- }
+ ret = usb_find_int_in_endpoint(intf->cur_altsetting, &ep);
+ if (ret)
+ return;
+
+ alloc_and_submit_int_urb(gspca_dev, ep);
}
}
diff --git a/drivers/media/usb/gspca/sonixb.c b/drivers/media/usb/gspca/sonixb.c
index 4d655e2da9cb..f251a886024f 100644
--- a/drivers/media/usb/gspca/sonixb.c
+++ b/drivers/media/usb/gspca/sonixb.c
@@ -943,7 +943,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev)
{
- const __u8 stop = 0x09; /* Disable stream turn of LED */
+ const __u8 stop = 0x09; /* Disable stream, turn off LED */
reg_w(gspca_dev, 0x01, &stop, 1);
diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c
index dde311c25d9b..734644d928ab 100644
--- a/drivers/media/usb/gspca/touptek.c
+++ b/drivers/media/usb/gspca/touptek.c
@@ -709,19 +709,4 @@ static struct usb_driver sd_driver = {
#endif
};
-static int __init sd_mod_init(void)
-{
- int ret;
-
- ret = usb_register(&sd_driver);
- if (ret < 0)
- return ret;
- return 0;
-}
-static void __exit sd_mod_exit(void)
-{
- usb_deregister(&sd_driver);
-}
-
-module_init(sd_mod_init);
-module_exit(sd_mod_exit);
+module_usb_driver(sd_driver);
diff --git a/drivers/media/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
index d42336836b18..849a2be416bd 100644
--- a/drivers/media/usb/hdpvr/hdpvr-core.c
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
@@ -265,13 +265,10 @@ static int hdpvr_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct hdpvr_device *dev;
- struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
#if IS_ENABLED(CONFIG_I2C)
struct i2c_client *client;
#endif
- size_t buffer_size;
- int i;
int dev_num;
int retval = -ENOMEM;
@@ -321,25 +318,18 @@ static int hdpvr_probe(struct usb_interface *interface,
/* set up the endpoint information */
/* use only the first bulk-in and bulk-out endpoints */
- iface_desc = interface->cur_altsetting;
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
-
- if (!dev->bulk_in_endpointAddr &&
- usb_endpoint_is_bulk_in(endpoint)) {
- /* USB interface description is buggy, reported max
- * packet size is 512 bytes, windows driver uses 8192 */
- buffer_size = 8192;
- dev->bulk_in_size = buffer_size;
- dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
- }
-
- }
- if (!dev->bulk_in_endpointAddr) {
+ if (usb_find_bulk_in_endpoint(interface->cur_altsetting, &endpoint)) {
v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n");
goto error_put_usb;
}
+ /*
+ * USB interface description is buggy, reported max packet size is 512
+ * bytes, windows driver uses 8192
+ */
+ dev->bulk_in_size = 8192;
+ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+
/* init the device */
if (hdpvr_device_init(dev)) {
v4l2_err(&dev->v4l2_dev, "device init failed\n");
diff --git a/drivers/media/usb/msi2500/msi2500.c b/drivers/media/usb/msi2500/msi2500.c
index 5c512d61dc1b..282256ab812a 100644
--- a/drivers/media/usb/msi2500/msi2500.c
+++ b/drivers/media/usb/msi2500/msi2500.c
@@ -541,7 +541,8 @@ static int msi2500_isoc_init(struct msi2500_dev *dev)
}
/* Must be called with vb_queue_lock hold */
-static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev)
+static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev,
+ enum vb2_buffer_state state)
{
unsigned long flags;
@@ -554,7 +555,7 @@ static void msi2500_cleanup_queued_bufs(struct msi2500_dev *dev)
buf = list_entry(dev->queued_bufs.next,
struct msi2500_frame_buf, list);
list_del(&buf->list);
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
+ vb2_buffer_done(&buf->vb.vb2_buf, state);
}
spin_unlock_irqrestore(&dev->queued_bufs_lock, flags);
}
@@ -831,25 +832,40 @@ static int msi2500_start_streaming(struct vb2_queue *vq, unsigned int count)
dev_dbg(dev->dev, "\n");
- if (!dev->udev)
- return -ENODEV;
+ if (!dev->udev) {
+ ret = -ENODEV;
+ goto err_cleanup;
+ }
- if (mutex_lock_interruptible(&dev->v4l2_lock))
- return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&dev->v4l2_lock)) {
+ ret = -ERESTARTSYS;
+ goto err_cleanup;
+ }
/* wake-up tuner */
v4l2_subdev_call(dev->v4l2_subdev, core, s_power, 1);
ret = msi2500_set_usb_adc(dev);
+ if (ret)
+ goto err_unlock_cleanup;
ret = msi2500_isoc_init(dev);
if (ret)
- msi2500_cleanup_queued_bufs(dev);
+ goto err_unlock_cleanup;
ret = msi2500_ctrl_msg(dev, CMD_START_STREAMING, 0);
+ if (ret)
+ goto err_isoc_cleanup;
mutex_unlock(&dev->v4l2_lock);
+ return 0;
+err_isoc_cleanup:
+ msi2500_isoc_cleanup(dev);
+err_unlock_cleanup:
+ mutex_unlock(&dev->v4l2_lock);
+err_cleanup:
+ msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_QUEUED);
return ret;
}
@@ -864,7 +880,7 @@ static void msi2500_stop_streaming(struct vb2_queue *vq)
if (dev->udev)
msi2500_isoc_cleanup(dev);
- msi2500_cleanup_queued_bufs(dev);
+ msi2500_cleanup_queued_bufs(dev, VB2_BUF_STATE_ERROR);
/* according to tests, at least 700us delay is required */
msleep(20);
diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c
index c416e2fc5754..e2884b04d952 100644
--- a/drivers/media/usb/pwc/pwc-if.c
+++ b/drivers/media/usb/pwc/pwc-if.c
@@ -710,11 +710,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
struct pwc_device *pdev = vb2_get_drv_priv(vq);
int r;
- if (!pdev->udev)
+ if (!pdev->udev) {
+ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
return -ENODEV;
+ }
- if (mutex_lock_interruptible(&pdev->v4l2_lock))
+ if (mutex_lock_interruptible(&pdev->v4l2_lock)) {
+ pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
return -ERESTARTSYS;
+ }
/* Turn on camera and set LEDS on */
pwc_camera_power(pdev, 1);
pwc_set_leds(pdev, leds[0], leds[1]);
@@ -726,6 +730,11 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count)
pwc_camera_power(pdev, 0);
/* And cleanup any queued bufs!! */
pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
+ if (pdev->fill_buf) {
+ vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
+ VB2_BUF_STATE_QUEUED);
+ pdev->fill_buf = NULL;
+ }
}
mutex_unlock(&pdev->v4l2_lock);
diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c
index 2c02873d09b5..0b8182edf8e4 100644
--- a/drivers/media/usb/s2255/s2255drv.c
+++ b/drivers/media/usb/s2255/s2255drv.c
@@ -2240,18 +2240,14 @@ static int s2255_probe(struct usb_interface *interface,
iface_desc = interface->cur_altsetting;
dev_dbg(&interface->dev, "num EP: %d\n",
iface_desc->desc.bNumEndpoints);
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- endpoint = &iface_desc->endpoint[i].desc;
- if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) {
- /* we found the bulk in endpoint */
- dev->read_endpoint = endpoint->bEndpointAddress;
- }
- }
- if (!dev->read_endpoint) {
+ if (usb_find_bulk_in_endpoint(iface_desc, &endpoint)) {
dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
goto errorEP;
}
+
+ dev->read_endpoint = endpoint->bEndpointAddress;
+
timer_setup(&dev->timer, s2255_timer, 0);
init_waitqueue_head(&dev->fw_data->wait_fw);
for (i = 0; i < MAX_CHANNELS; i++) {
diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index b9a3d9257a11..3ca108b83f1d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -385,6 +385,99 @@ static const struct uvc_control_info uvc_ctrls[] = {
| UVC_CTRL_FLAG_GET_RANGE
| UVC_CTRL_FLAG_RESTORE,
},
+ /*
+ * Allows the control of pan/tilt motor movements for camera models
+ * that support mechanical pan/tilt.
+ *
+ * Bits 0 to 15 control pan, bits 16 to 31 control tilt.
+ * The unit of the pan/tilt values is 1/64th of a degree and the
+ * resolution is 1 degree.
+ */
+ {
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 1,
+ .index = 0,
+ .size = 4,
+ .flags = UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_SET_CUR,
+ },
+ /*
+ * Reset the pan/tilt motors to their original position for camera
+ * models that support mechanical pan/tilt.
+ *
+ * Setting bit 0 resets the pan position.
+ * Setting bit 1 resets the tilt position.
+ *
+ * Both bits can be set at the same time to reset both, pan and tilt,
+ * at the same time.
+ */
+ {
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 2,
+ .index = 1,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_SET_CUR,
+ },
+ /*
+ * Allows the control of focus motor movements for camera models that
+ * support mechanical focus.
+ *
+ * Bits 0 to 7 allow selection of the desired lens position.
+ * There are no physical units, instead, the focus range is spread over
+ * 256 logical units with 0 representing infinity focus and 255 being
+ * macro focus.
+ */
+ {
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 3,
+ .index = 2,
+ .size = 6,
+ .flags = UVC_CTRL_FLAG_GET_CUR
+ | UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_SET_CUR,
+ },
+ /*
+ * Allows the control of pan/tilt motor movements for camera models
+ * that support mechanical pan/tilt.
+ *
+ * Bits 0 to 15 control pan, bits 16 to 31 control tilt.
+ */
+ {
+ .entity = UVC_GUID_LOGITECH_PERIPHERAL,
+ .selector = 1,
+ .index = 0,
+ .size = 4,
+ .flags = UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_GET_RES
+ | UVC_CTRL_FLAG_SET_CUR,
+ },
+ /*
+ * Reset the pan/tilt motors to their original position for camera
+ * models that support mechanical pan/tilt.
+ *
+ * Setting bit 0 resets the pan position.
+ * Setting bit 1 resets the tilt position.
+ */
+ {
+ .entity = UVC_GUID_LOGITECH_PERIPHERAL,
+ .selector = 2,
+ .index = 1,
+ .size = 1,
+ .flags = UVC_CTRL_FLAG_GET_DEF
+ | UVC_CTRL_FLAG_GET_MAX
+ | UVC_CTRL_FLAG_GET_MIN
+ | UVC_CTRL_FLAG_GET_RES
+ | UVC_CTRL_FLAG_SET_CUR,
+ },
};
static const u32 uvc_control_classes[] = {
@@ -1009,6 +1102,87 @@ static const struct uvc_control_mapping uvc_ctrl_mappings[] = {
.menu_mask = BIT(V4L2_COLORFX_VIVID) |
BIT(V4L2_COLORFX_NONE),
},
+ {
+ .id = V4L2_CID_PAN_RELATIVE,
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 1,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_TILT_RELATIVE,
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 1,
+ .size = 16,
+ .offset = 16,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_PAN_RESET,
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 2,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BUTTON,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_TILT_RESET,
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 2,
+ .size = 1,
+ .offset = 1,
+ .v4l2_type = V4L2_CTRL_TYPE_BUTTON,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_PAN_RELATIVE,
+ .entity = UVC_GUID_LOGITECH_PERIPHERAL,
+ .selector = 1,
+ .size = 16,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_TILT_RELATIVE,
+ .entity = UVC_GUID_LOGITECH_PERIPHERAL,
+ .selector = 1,
+ .size = 16,
+ .offset = 16,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_SIGNED,
+ },
+ {
+ .id = V4L2_CID_PAN_RESET,
+ .entity = UVC_GUID_LOGITECH_PERIPHERAL,
+ .selector = 2,
+ .size = 1,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_BUTTON,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_TILT_RESET,
+ .entity = UVC_GUID_LOGITECH_PERIPHERAL,
+ .selector = 2,
+ .size = 1,
+ .offset = 1,
+ .v4l2_type = V4L2_CTRL_TYPE_BUTTON,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
+ {
+ .id = V4L2_CID_FOCUS_ABSOLUTE,
+ .entity = UVC_GUID_LOGITECH_MOTOR_CONTROL_V1,
+ .selector = 3,
+ .size = 8,
+ .offset = 0,
+ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
+ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
+ },
};
/* ------------------------------------------------------------------------
@@ -2827,6 +3001,35 @@ static int uvc_ctrl_init_xu_ctrl(struct uvc_device *dev,
return ret;
}
+bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector)
+{
+ /*
+ * This list is not exhaustive, it is a best effort to block access to
+ * non documented controls that can affect user's privacy.
+ */
+ struct privacy_control {
+ u8 entity[16];
+ u8 selector;
+ } privacy_control[] = {
+ {
+ .entity = UVC_GUID_LOGITECH_USER_HW_CONTROL_V1,
+ .selector = 1,
+ },
+ {
+ .entity = UVC_GUID_LOGITECH_PERIPHERAL,
+ .selector = 9,
+ },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(privacy_control); i++)
+ if (!memcmp(entity, privacy_control[i].entity, 16) &&
+ selector == privacy_control[i].selector)
+ return true;
+
+ return false;
+}
+
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry)
{
@@ -2871,6 +3074,15 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
return -ENOENT;
}
+ if (uvc_ctrl_is_privacy_control(entity->guid, xqry->selector) &&
+ !uvc_allow_privacy_override_param) {
+ dev_warn_once(&chain->dev->intf->dev,
+ "Privacy related controls can only be accessed if module parameter allow_privacy_override is true\n");
+ uvc_dbg(chain->dev, CONTROL, "Blocking access to privacy related Control %pUl/%u\n",
+ entity->guid, xqry->selector);
+ return -EACCES;
+ }
+
if (mutex_lock_interruptible(&chain->ctrl_mutex))
return -ERESTARTSYS;
diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c
index 31b4ac3b48c1..e289cc71ba98 100644
--- a/drivers/media/usb/uvc/uvc_driver.c
+++ b/drivers/media/usb/uvc/uvc_driver.c
@@ -36,6 +36,7 @@ unsigned int uvc_no_drop_param = 1;
static unsigned int uvc_quirks_param = -1;
unsigned int uvc_dbg_param;
unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT;
+bool uvc_allow_privacy_override_param;
static struct usb_driver uvc_driver;
@@ -2504,6 +2505,9 @@ module_param_named(trace, uvc_dbg_param, uint, 0644);
MODULE_PARM_DESC(trace, "Trace level bitmask");
module_param_named(timeout, uvc_timeout_param, uint, 0644);
MODULE_PARM_DESC(timeout, "Streaming control requests timeout");
+module_param_named(allow_privacy_override, uvc_allow_privacy_override_param, bool, 0644);
+MODULE_PARM_DESC(allow_privacy_override,
+ "Allow access to privacy related controls");
/* ------------------------------------------------------------------------
* Driver initialization and cleanup
diff --git a/drivers/media/usb/uvc/uvc_status.c b/drivers/media/usb/uvc/uvc_status.c
index 65f5356bebb3..b632cf5e3fe9 100644
--- a/drivers/media/usb/uvc/uvc_status.c
+++ b/drivers/media/usb/uvc/uvc_status.c
@@ -316,6 +316,16 @@ static int uvc_status_start(struct uvc_device *dev, gfp_t flags)
if (!dev->int_urb)
return 0;
+ /*
+ * If the previous uvc_status_stop() call was from the async work,
+ * the work may still be running. Wait for it to finish before we submit
+ * the urb.
+ */
+ flush_work(&dev->async_ctrl.work);
+
+ /* Clear the flush status if we were previously stopped. */
+ smp_store_release(&dev->flush_status, false);
+
return usb_submit_urb(dev->int_urb, flags);
}
@@ -337,6 +347,15 @@ static void uvc_status_stop(struct uvc_device *dev)
smp_store_release(&dev->flush_status, true);
/*
+ * If we are called from the event work function, the URB is guaranteed
+ * to not be in flight as it has completed and has not been resubmitted.
+ * There's no need to cancel the work (which would deadlock), or to kill
+ * the URB.
+ */
+ if (current_work() == &w->work)
+ return;
+
+ /*
* Cancel any pending asynchronous work. If any status event was queued,
* process it synchronously.
*/
@@ -354,15 +373,6 @@ static void uvc_status_stop(struct uvc_device *dev)
*/
if (cancel_work_sync(&w->work))
uvc_ctrl_status_event(w->chain, w->ctrl, w->data);
-
- /*
- * From this point, there are no events on the queue and the status URB
- * is dead. No events will be queued until uvc_status_start() is called.
- * The barrier is needed to make sure that flush_status is visible to
- * uvc_ctrl_status_event_work() when uvc_status_start() will be called
- * again.
- */
- smp_store_release(&dev->flush_status, false);
}
int uvc_status_resume(struct uvc_device *dev)
diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index cda1697204ea..644c2f1cd8e6 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -133,6 +133,13 @@ static int uvc_ioctl_xu_ctrl_map(struct uvc_video_chain *chain,
return -EINVAL;
}
+ if (uvc_ctrl_is_privacy_control(xmap->entity, xmap->selector) &&
+ !uvc_allow_privacy_override_param) {
+ dev_warn_once(&chain->dev->intf->dev,
+ "Privacy related controls can only be mapped if module parameter allow_privacy_override is true\n");
+ return -EACCES;
+ }
+
map = kzalloc_obj(*map);
if (map == NULL)
return -ENOMEM;
@@ -1049,6 +1056,8 @@ static long uvc_ioctl_default(struct file *file, void *priv, bool valid_prio,
switch (cmd) {
/* Dynamic controls. */
case UVCIOC_CTRL_MAP:
+ pr_warn_once("uvcvideo: " DEPRECATED
+ "UVCIOC_CTRL_MAP ioctl will be eventually removed.\n");
return uvc_ioctl_xu_ctrl_map(chain, arg);
case UVCIOC_CTRL_QUERY:
@@ -1163,6 +1172,8 @@ static long uvc_v4l2_compat_ioctl32(struct file *file,
switch (cmd) {
case UVCIOC_CTRL_MAP32:
+ pr_warn_once("uvcvideo: " DEPRECATED
+ "UVCIOC_CTRL_MAP32 ioctl will be eventually removed.\n");
ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
if (ret)
break;
diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index f6c8e3223796..fc3536a4399f 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -494,6 +494,13 @@ static int uvc_commit_video(struct uvc_streaming *stream,
* Clocks and timestamps
*/
+/*
+ * The accuracy of the hardware timestamping depends on having enough data to
+ * interpolate between the different clock domains. This value is sof cycles,
+ * this is, milliseconds.
+ */
+#define UVC_MIN_HW_TIMESTAMP_DIFF 100
+
static inline ktime_t uvc_video_get_time(void)
{
if (uvc_clock_param == CLOCK_MONOTONIC)
@@ -517,7 +524,7 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock,
spin_lock_irqsave(&clock->lock, flags);
- if (clock->count > 0 && clock->last_sof > sample->dev_sof) {
+ if (clock->count > 0 && clock->last_sof_processed > sample->dev_sof) {
/*
* Remove data from the circular buffer that is older than the
* last SOF overflow. We only support one SOF overflow per
@@ -537,6 +544,15 @@ static void uvc_video_clock_add_sample(struct uvc_clock *clock,
spin_unlock_irqrestore(&clock->lock, flags);
}
+static inline u16 sof_diff(u16 a, u16 b)
+{
+ /*
+ * Because the result is modulo 2048 (via & 2047), we do not need a
+ * special case for a < b.
+ */
+ return (a - b) & 2047;
+}
+
static void
uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
const u8 *data, int len)
@@ -583,15 +599,11 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
if (!has_scr)
return;
- /*
- * To limit the amount of data, drop SCRs with an SOF identical to the
- * previous one. This filtering is also needed to support UVC 1.5, where
- * all the data packets of the same frame contains the same SOF. In that
- * case only the first one will match the host_sof.
- */
- sample.dev_sof = get_unaligned_le16(&data[header_size - 2]);
- if (sample.dev_sof == stream->clock.last_sof)
+ sample.dev_sof = get_unaligned_le16(&data[header_size - 2]) & 2047;
+ /* If the sample SOF is identical to the previous one, quit early. */
+ if (stream->clock.last_sof_raw == sample.dev_sof)
return;
+ stream->clock.last_sof_raw = sample.dev_sof;
sample.dev_stc = get_unaligned_le32(&data[header_size - 6]);
@@ -633,8 +645,6 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
if (stream->dev->quirks & UVC_QUIRK_INVALID_DEVICE_SOF)
sample.dev_sof = sample.host_sof;
- sample.host_time = uvc_video_get_time();
-
/*
* The UVC specification allows device implementations that can't obtain
* the USB frame number to keep their own frame counters as long as they
@@ -664,15 +674,30 @@ uvc_video_clock_decode(struct uvc_streaming *stream, struct uvc_buffer *buf,
}
sample.dev_sof = (sample.dev_sof + stream->clock.sof_offset) & 2047;
+
+ /*
+ * To limit the amount of data, drop SCRs with an SOF similar to the
+ * previous one. This filtering is also needed to support UVC 1.5, where
+ * all the data packets of the same frame contains the same SOF. In that
+ * case only the first one will match the host_sof.
+ */
+ if (sof_diff(sample.dev_sof, stream->clock.last_sof_processed) <=
+ (UVC_MIN_HW_TIMESTAMP_DIFF / stream->clock.size))
+ return;
+
+ /* This is expensive, only do it if the sample will be added. */
+ sample.host_time = uvc_video_get_time();
+
uvc_video_clock_add_sample(&stream->clock, &sample);
- stream->clock.last_sof = sample.dev_sof;
+ stream->clock.last_sof_processed = sample.dev_sof;
}
static void uvc_video_clock_reset(struct uvc_clock *clock)
{
clock->head = 0;
clock->count = 0;
- clock->last_sof = -1;
+ clock->last_sof_processed = -1;
+ clock->last_sof_raw = -1;
clock->last_sof_overflow = -1;
clock->sof_offset = -1;
}
@@ -833,15 +858,22 @@ void uvc_video_clock_update(struct uvc_streaming *stream,
y2 += 2048 << 16;
/*
- * Have at least 1/4 of a second of timestamps before we
- * try to do any calculation. Otherwise we do not have enough
- * precision. This value was determined by running Android CTS
- * on different devices.
+ * If the buffer is not full, we want to gather at least 1/4th of
+ * timestamps before using HW timestamping. We do this to avoid jitter
+ * on the initial frames.
+ *
+ * If the buffer is full we would use it regardless of how much data
+ * it represents. This could be solved with an infinite big circular
+ * buffer, but RAM is expensive these days, specially the infinitely
+ * big.
*
- * dev_sof runs at 1KHz, and we have a fixed point precision of
- * 16 bits.
+ * The value of UVC_MIN_HW_TIMESTAMP_DIFF was determined by running
+ * Android's CTS on different devices.
+ *
+ * y1 and y2 are dev_sof with a fixed point precision of 16 bits.
*/
- if ((y2 - y1) < ((1000 / 4) << 16))
+ if (clock->size != clock->count &&
+ (y2 - y1) < (UVC_MIN_HW_TIMESTAMP_DIFF << 16))
goto done;
y = (u64)(y2 - y1) * (1ULL << 31) + (u64)y1 * (u64)x2
@@ -1149,7 +1181,9 @@ static void uvc_video_stats_stop(struct uvc_streaming *stream)
* uvc_video_decode_end will never be called with a NULL buffer.
*/
static int uvc_video_decode_start(struct uvc_streaming *stream,
- struct uvc_buffer *buf, const u8 *data, int len)
+ struct uvc_buffer *buf,
+ struct uvc_buffer *meta_buf,
+ const u8 *data, int len)
{
u8 header_len;
u8 fid;
@@ -1169,6 +1203,53 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
fid = data[1] & UVC_STREAM_FID;
/*
+ * Mark the buffer as done if we're at the beginning of a new frame.
+ * End of frame detection is better implemented by checking the EOF
+ * bit (FID bit toggling is delayed by one frame compared to the EOF
+ * bit), but some devices don't set the bit at end of frame (and the
+ * last payload can be lost anyway). We thus must check if the FID has
+ * been toggled.
+ *
+ * stream->last_fid is initialized to -1, and buf->bytesused to 0,
+ * so the first isochronous frame will never trigger an end of frame
+ * detection.
+ *
+ * Empty buffers (bytesused == 0) don't trigger end of frame detection
+ * as it doesn't make sense to return an empty buffer. This also
+ * avoids detecting end of frame conditions at FID toggling if the
+ * previous payload had the EOF bit set.
+ */
+ if (fid != stream->last_fid && buf && buf->bytesused != 0) {
+ uvc_dbg(stream->dev, FRAME,
+ "Frame complete (FID bit toggled)\n");
+ buf->state = UVC_BUF_STATE_READY;
+
+ return -EAGAIN;
+ }
+
+ /*
+ * Some cameras, when running two parallel streams (one MJPEG alongside
+ * another non-MJPEG stream), are known to lose the EOF packet for a frame.
+ * We can detect the end of a frame by checking for a new SOI marker, as
+ * the SOI always lies on the packet boundary between two frames for
+ * these devices.
+ */
+ if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF &&
+ (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG ||
+ stream->cur_format->fcc == V4L2_PIX_FMT_JPEG) &&
+ buf && buf->bytesused != 0) {
+ const u8 *packet = data + header_len;
+
+ if (len >= header_len + 2 &&
+ packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI) {
+ buf->state = UVC_BUF_STATE_READY;
+ buf->error = 1;
+ stream->last_fid ^= UVC_STREAM_FID;
+ return -EAGAIN;
+ }
+ }
+
+ /*
* Increase the sequence number regardless of any buffer states, so
* that discontinuous sequence numbers always indicate lost frames.
*/
@@ -1176,6 +1257,19 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
stream->sequence++;
if (stream->sequence)
uvc_video_stats_update(stream);
+
+ /*
+ * On a FID flip initialize sequence number and timestamp.
+ *
+ * The driver already takes care of injecting FID flips for
+ * UVC_QUIRK_STREAM_NO_FID and UVC_QUIRK_MJPEG_NO_EOF.
+ */
+ if (buf) {
+ buf->buf.field = V4L2_FIELD_NONE;
+ buf->buf.sequence = stream->sequence;
+ buf->buf.vb2_buf.timestamp =
+ ktime_to_ns(uvc_video_get_time());
+ }
}
uvc_video_clock_decode(stream, buf, data, len);
@@ -1216,57 +1310,10 @@ static int uvc_video_decode_start(struct uvc_streaming *stream,
return -ENODATA;
}
- buf->buf.field = V4L2_FIELD_NONE;
- buf->buf.sequence = stream->sequence;
- buf->buf.vb2_buf.timestamp = ktime_to_ns(uvc_video_get_time());
-
/* TODO: Handle PTS and SCR. */
buf->state = UVC_BUF_STATE_ACTIVE;
- }
-
- /*
- * Mark the buffer as done if we're at the beginning of a new frame.
- * End of frame detection is better implemented by checking the EOF
- * bit (FID bit toggling is delayed by one frame compared to the EOF
- * bit), but some devices don't set the bit at end of frame (and the
- * last payload can be lost anyway). We thus must check if the FID has
- * been toggled.
- *
- * stream->last_fid is initialized to -1, so the first isochronous
- * frame will never trigger an end of frame detection.
- *
- * Empty buffers (bytesused == 0) don't trigger end of frame detection
- * as it doesn't make sense to return an empty buffer. This also
- * avoids detecting end of frame conditions at FID toggling if the
- * previous payload had the EOF bit set.
- */
- if (fid != stream->last_fid && buf->bytesused != 0) {
- uvc_dbg(stream->dev, FRAME,
- "Frame complete (FID bit toggled)\n");
- buf->state = UVC_BUF_STATE_READY;
- return -EAGAIN;
- }
-
- /*
- * Some cameras, when running two parallel streams (one MJPEG alongside
- * another non-MJPEG stream), are known to lose the EOF packet for a frame.
- * We can detect the end of a frame by checking for a new SOI marker, as
- * the SOI always lies on the packet boundary between two frames for
- * these devices.
- */
- if (stream->dev->quirks & UVC_QUIRK_MJPEG_NO_EOF &&
- (stream->cur_format->fcc == V4L2_PIX_FMT_MJPEG ||
- stream->cur_format->fcc == V4L2_PIX_FMT_JPEG)) {
- const u8 *packet = data + header_len;
-
- if (len >= header_len + 2 &&
- packet[0] == 0xff && packet[1] == JPEG_MARKER_SOI &&
- buf->bytesused != 0) {
- buf->state = UVC_BUF_STATE_READY;
- buf->error = 1;
- stream->last_fid ^= UVC_STREAM_FID;
- return -EAGAIN;
- }
+ if (meta_buf)
+ meta_buf->state = UVC_BUF_STATE_ACTIVE;
}
stream->last_fid = fid;
@@ -1424,7 +1471,7 @@ static void uvc_video_decode_meta(struct uvc_streaming *stream,
ktime_t time;
const u8 *scr;
- if (!meta_buf || length == 2)
+ if (length <= 2 || !meta_buf || meta_buf->state != UVC_BUF_STATE_ACTIVE)
return;
has_pts = mem[1] & UVC_STREAM_PTS;
@@ -1541,7 +1588,7 @@ static void uvc_video_decode_isoc(struct uvc_urb *uvc_urb,
/* Decode the payload header. */
mem = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
do {
- ret = uvc_video_decode_start(stream, buf, mem,
+ ret = uvc_video_decode_start(stream, buf, meta_buf, mem,
urb->iso_frame_desc[i].actual_length);
if (ret == -EAGAIN)
uvc_video_next_buffers(stream, &buf, &meta_buf);
@@ -1590,7 +1637,8 @@ static void uvc_video_decode_bulk(struct uvc_urb *uvc_urb,
*/
if (stream->bulk.header_size == 0 && !stream->bulk.skip_payload) {
do {
- ret = uvc_video_decode_start(stream, buf, mem, len);
+ ret = uvc_video_decode_start(stream, buf, meta_buf, mem,
+ len);
if (ret == -EAGAIN)
uvc_video_next_buffers(stream, &buf, &meta_buf);
} while (ret == -EAGAIN);
@@ -1693,7 +1741,6 @@ static void uvc_video_complete(struct urb *urb)
struct vb2_queue *vb2_qmeta = stream->meta.queue.vdev.queue;
struct uvc_buffer *buf = NULL;
struct uvc_buffer *buf_meta = NULL;
- unsigned long flags;
int ret;
switch (urb->status) {
@@ -1719,13 +1766,8 @@ static void uvc_video_complete(struct urb *urb)
buf = uvc_queue_get_current_buffer(queue);
- if (vb2_qmeta) {
- spin_lock_irqsave(&qmeta->irqlock, flags);
- if (!list_empty(&qmeta->irqqueue))
- buf_meta = list_first_entry(&qmeta->irqqueue,
- struct uvc_buffer, queue);
- spin_unlock_irqrestore(&qmeta->irqlock, flags);
- }
+ if (vb2_qmeta)
+ buf_meta = uvc_queue_get_current_buffer(qmeta);
/* Re-initialise the URB async work. */
uvc_urb->async_operations = 0;
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 0a0c01b2420f..b6bcee4a222f 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -522,7 +522,8 @@ struct uvc_streaming {
unsigned int size;
unsigned int last_sof_overflow;
- u16 last_sof;
+ u16 last_sof_processed;
+ u16 last_sof_raw;
u16 sof_offset;
u8 last_scr[6];
@@ -666,6 +667,7 @@ extern unsigned int uvc_no_drop_param;
extern unsigned int uvc_dbg_param;
extern unsigned int uvc_timeout_param;
extern unsigned int uvc_hw_timestamps_param;
+extern bool uvc_allow_privacy_override_param;
#define uvc_dbg(_dev, flag, fmt, ...) \
do { \
@@ -791,6 +793,7 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
struct uvc_xu_control_query *xqry);
void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
+bool uvc_ctrl_is_privacy_control(u8 entity[16], u8 selector);
/* Utility functions */
struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
diff --git a/drivers/media/v4l2-core/tuner-core.c b/drivers/media/v4l2-core/tuner-core.c
index 004ec4d7beea..1e130e6f903f 100644
--- a/drivers/media/v4l2-core/tuner-core.c
+++ b/drivers/media/v4l2-core/tuner-core.c
@@ -1401,7 +1401,7 @@ static const struct dev_pm_ops tuner_pm_ops = {
};
static const struct i2c_device_id tuner_id[] = {
- { "tuner", }, /* autodetect */
+ { .name = "tuner" }, /* autodetect */
{ }
};
MODULE_DEVICE_TABLE(i2c, tuner_id);
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 554c591e1113..65db7340ad38 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -245,32 +245,59 @@ EXPORT_SYMBOL_GPL(v4l2_s_parm_cap);
const struct v4l2_format_info *v4l2_format_info(u32 format)
{
static const struct v4l2_format_info formats[] = {
- /* RGB formats */
- { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ /* RGB formats (1 or 2 bytes per pixel) */
+ { .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .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_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
+ /* RGB formats (3 or 4 bytes per pixel) */
+ { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+
+ /* RGB formats (6 or 8 bytes per pixel) */
+ { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+
+ /* HSV formats */
+ { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
/* YUV packed formats */
{ .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
@@ -281,6 +308,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_Y212, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_Y216, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_YUV48_12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_YUV24, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_MT2110T, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
.block_w = { 16, 8, 0, 0 }, .block_h = { 32, 16, 0, 0 }},
{ .format = V4L2_PIX_FMT_MT2110R, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 2, .comp_planes = 2, .bpp = { 5, 10, 0, 0 }, .bpp_div = { 4, 4, 1, 1 }, .hdiv = 2, .vdiv = 2,
@@ -404,14 +432,33 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf
}
static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane,
- unsigned int width)
+ unsigned int width, unsigned int byte_alignment)
{
unsigned int hdiv = plane ? info->hdiv : 1;
unsigned int aligned_width =
ALIGN(width, v4l2_format_block_width(info, plane));
- return DIV_ROUND_UP(aligned_width, hdiv) *
- info->bpp[plane] / info->bpp_div[plane];
+ /*
+ * Formats with a single memory plane derive the stride of the
+ * other planes from the y stride. To avoid hardware or software
+ * deriving a different stride for the composite plane,
+ * multiply the alignment accordingly.
+ *
+ * It assumes the following format properties:
+ * - bpp_div[0] == bpp_div[1]
+ * - The multiplication factor doesn't differ between the non y planes
+ * - The multiplication factor is a power of 2
+ */
+ if (info->mem_planes == 1 && info->comp_planes > 1) {
+ if (plane == 0)
+ byte_alignment *= DIV_ROUND_UP(info->hdiv * info->bpp[0], info->bpp[1]);
+ else
+ byte_alignment *= DIV_ROUND_UP(info->bpp[1], info->hdiv * info->bpp[0]);
+ }
+
+ return ALIGN(DIV_ROUND_UP(aligned_width, hdiv) * info->bpp[plane] /
+ info->bpp_div[plane],
+ byte_alignment);
}
static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane,
@@ -425,9 +472,10 @@ static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_inf
}
static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane,
- unsigned int width, unsigned int height)
+ unsigned int width, unsigned int height,
+ u8 stride_alignment)
{
- return v4l2_format_plane_stride(info, plane, width) *
+ return v4l2_format_plane_stride(info, plane, width, stride_alignment) *
v4l2_format_plane_height(info, plane, height);
}
@@ -448,8 +496,9 @@ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
}
EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints);
-int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
- u32 pixelformat, u32 width, u32 height)
+int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt,
+ u32 pixelformat, u32 width, u32 height,
+ u8 stride_alignment)
{
const struct v4l2_format_info *info;
struct v4l2_plane_pix_format *plane;
@@ -466,23 +515,34 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
if (info->mem_planes == 1) {
plane = &pixfmt->plane_fmt[0];
- plane->bytesperline = v4l2_format_plane_stride(info, 0, width);
+ plane->bytesperline = v4l2_format_plane_stride(info, 0, width,
+ stride_alignment);
plane->sizeimage = 0;
for (i = 0; i < info->comp_planes; i++)
plane->sizeimage +=
- v4l2_format_plane_size(info, i, width, height);
+ v4l2_format_plane_size(info, i, width, height,
+ stride_alignment);
} else {
for (i = 0; i < info->comp_planes; i++) {
plane = &pixfmt->plane_fmt[i];
plane->bytesperline =
- v4l2_format_plane_stride(info, i, width);
+ v4l2_format_plane_stride(info, i, width,
+ stride_alignment);
plane->sizeimage = plane->bytesperline *
v4l2_format_plane_height(info, i, height);
}
}
return 0;
}
+EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp_aligned);
+
+int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
+ u32 pixelformat, u32 width, u32 height)
+{
+ return v4l2_fill_pixfmt_mp_aligned(pixfmt, pixelformat,
+ width, height, 1);
+}
EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp);
int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
@@ -502,12 +562,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
pixfmt->width = width;
pixfmt->height = height;
pixfmt->pixelformat = pixelformat;
- pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width);
+ pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width, 1);
pixfmt->sizeimage = 0;
for (i = 0; i < info->comp_planes; i++)
pixfmt->sizeimage +=
- v4l2_format_plane_size(info, i, width, height);
+ v4l2_format_plane_size(info, i, width, height, 1);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt);
@@ -792,14 +852,15 @@ struct clk *__devm_v4l2_sensor_clk_get(struct device *dev, const char *id,
if (ret)
return ERR_PTR(ret == -EINVAL ? -EPROBE_DEFER : ret);
- if (!id) {
+ if (id)
+ clk_id = kasprintf(GFP_KERNEL, "clk-%s-%s", dev_name(dev), id);
+ else
clk_id = kasprintf(GFP_KERNEL, "clk-%s", dev_name(dev));
- if (!clk_id)
- return ERR_PTR(-ENOMEM);
- id = clk_id;
- }
- clk_hw = devm_clk_hw_register_fixed_rate(dev, id, NULL, 0, rate);
+ if (!clk_id)
+ return ERR_PTR(-ENOMEM);
+
+ clk_hw = devm_clk_hw_register_fixed_rate(dev, clk_id, NULL, 0, rate);
if (IS_ERR(clk_hw))
return ERR_CAST(clk_hw);
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 6b375720e395..ba047d7d8601 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -971,6 +971,7 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
struct v4l2_ctrl_hevc_ext_sps_st_rps *p_hevc_st_rps;
struct v4l2_ctrl_hevc_sps *p_hevc_sps;
struct v4l2_ctrl_hevc_pps *p_hevc_pps;
+ struct v4l2_ctrl_hevc_slice_params *p_hevc_slice_params;
struct v4l2_ctrl_hdr10_mastering_display *p_hdr10_mastering;
struct v4l2_ctrl_hevc_decode_params *p_hevc_decode_params;
struct v4l2_area *area;
@@ -1260,6 +1261,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
break;
case V4L2_CTRL_TYPE_HEVC_SLICE_PARAMS:
+ p_hevc_slice_params = p;
+
+ if (p_hevc_slice_params->num_ref_idx_l0_active_minus1 >=
+ V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
+ return -EINVAL;
+
+ if (p_hevc_slice_params->slice_type != V4L2_HEVC_SLICE_TYPE_B)
+ break;
+
+ if (p_hevc_slice_params->num_ref_idx_l1_active_minus1 >=
+ V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
+ return -EINVAL;
break;
case V4L2_CTRL_TYPE_HEVC_EXT_SPS_ST_RPS:
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-defs.c b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
index 551426c4cd01..e062f2088490 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-defs.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-defs.c
@@ -889,6 +889,7 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: return "Display Delay";
case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: return "Display Delay Enable";
case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: return "Generate Access Unit Delimiters";
+ case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION: return "Background Detection";
case V4L2_CID_MPEG_VIDEO_H263_I_FRAME_QP: return "H263 I-Frame QP Value";
case V4L2_CID_MPEG_VIDEO_H263_P_FRAME_QP: return "H263 P-Frame QP Value";
case V4L2_CID_MPEG_VIDEO_H263_B_FRAME_QP: return "H263 B-Frame QP Value";
@@ -1296,6 +1297,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL:
case V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER:
case V4L2_CID_MPEG_VIDEO_AU_DELIMITER:
+ case V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION:
case V4L2_CID_WIDE_DYNAMIC_RANGE:
case V4L2_CID_IMAGE_STABILIZATION:
case V4L2_CID_RDS_RECEPTION:
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-request.c b/drivers/media/v4l2-core/v4l2-ctrls-request.c
index 4b7e6981b8d6..5c2fc767c6a6 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-request.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-request.c
@@ -348,13 +348,12 @@ void v4l2_ctrl_request_complete(struct media_request *req,
ret = v4l2_ctrl_handler_init(hdl, (main_hdl->nr_of_buckets - 1) * 8);
if (!ret)
ret = v4l2_ctrl_request_bind(req, hdl, main_hdl);
- if (ret) {
- v4l2_ctrl_handler_free(hdl);
- kfree(hdl);
- return;
- }
+ if (ret)
+ goto error;
hdl->request_is_queued = true;
obj = media_request_object_find(req, &req_ops, main_hdl);
+ if (!obj)
+ goto error;
}
hdl = container_of(obj, struct v4l2_ctrl_handler, req_obj);
@@ -389,6 +388,11 @@ void v4l2_ctrl_request_complete(struct media_request *req,
mutex_unlock(main_hdl->lock);
media_request_object_complete(obj);
media_request_object_put(obj);
+ return;
+
+error:
+ v4l2_ctrl_handler_free(hdl);
+ kfree(hdl);
}
EXPORT_SYMBOL(v4l2_ctrl_request_complete);
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 6ce623a1245a..5516b2bbb08f 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -1032,6 +1032,11 @@ int __video_register_device(struct video_device *vdev,
vdev->minor = i + minor_offset;
vdev->num = nr;
+ if (WARN_ON(vdev->minor >= VIDEO_NUM_DEVICES)) {
+ mutex_unlock(&videodev_lock);
+ return -EINVAL;
+ }
+
/* Should not happen since we thought this minor was free */
if (WARN_ON(video_devices[vdev->minor])) {
mutex_unlock(&videodev_lock);
diff --git a/drivers/media/v4l2-core/v4l2-fwnode.c b/drivers/media/v4l2-core/v4l2-fwnode.c
index 77f3298821b5..62a3a452f788 100644
--- a/drivers/media/v4l2-core/v4l2-fwnode.c
+++ b/drivers/media/v4l2-core/v4l2-fwnode.c
@@ -1256,7 +1256,7 @@ v4l2_async_nf_parse_fwnode_sensor(struct device *dev,
return 0;
}
-int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
+int __v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module)
{
struct v4l2_async_notifier *notifier;
int ret;
@@ -1282,7 +1282,7 @@ int v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd)
if (ret < 0)
goto out_cleanup;
- ret = v4l2_async_register_subdev(sd);
+ ret = __v4l2_async_register_subdev(sd, module);
if (ret < 0)
goto out_unregister;
@@ -1300,7 +1300,7 @@ out_cleanup:
return ret;
}
-EXPORT_SYMBOL_GPL(v4l2_async_register_subdev_sensor);
+EXPORT_SYMBOL_GPL(__v4l2_async_register_subdev_sensor);
MODULE_DESCRIPTION("V4L2 fwnode binding parsing library");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 831c69c958b8..e9f81b9be9e2 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -629,6 +629,18 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh,
v4l2_subdev_get_unlocked_active_state(sd);
}
+static void v4l2_subdev_copy_routes(struct v4l2_subdev_routing *routing,
+ const struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_route *routes =
+ (struct v4l2_subdev_route *)(uintptr_t)routing->routes;
+ u32 copy_routes = min(routing->len_routes, state->routing.num_routes);
+
+ memcpy(routes, state->routing.routes, sizeof(*routes) * copy_routes);
+
+ routing->num_routes = state->routing.num_routes;
+}
+
static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
struct v4l2_subdev_state *state)
{
@@ -1000,7 +1012,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_G_ROUTING: {
struct v4l2_subdev_routing *routing = arg;
- struct v4l2_subdev_krouting *krouting;
if (!v4l2_subdev_enable_streams_api)
return -ENOIOCTLCMD;
@@ -1008,15 +1019,12 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
return -ENOIOCTLCMD;
- memset(routing->reserved, 0, sizeof(routing->reserved));
+ if (!client_supports_streams)
+ return -EINVAL;
- krouting = &state->routing;
+ memset(routing->reserved, 0, sizeof(routing->reserved));
- memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
- krouting->routes,
- min(krouting->num_routes, routing->len_routes) *
- sizeof(*krouting->routes));
- routing->num_routes = krouting->num_routes;
+ v4l2_subdev_copy_routes(routing, state);
return 0;
}
@@ -1035,6 +1043,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (!(sd->flags & V4L2_SUBDEV_FL_STREAMS))
return -ENOIOCTLCMD;
+ if (!client_supports_streams)
+ return -EINVAL;
+
if (routing->which != V4L2_SUBDEV_FORMAT_TRY && ro_subdev)
return -EPERM;
@@ -1084,11 +1095,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
* the routing table.
*/
if (!v4l2_subdev_has_op(sd, pad, set_routing)) {
- memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
- state->routing.routes,
- min(state->routing.num_routes, routing->len_routes) *
- sizeof(*state->routing.routes));
- routing->num_routes = state->routing.num_routes;
+ v4l2_subdev_copy_routes(routing, state);
return 0;
}
@@ -1102,11 +1109,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
if (rval < 0)
return rval;
- memcpy((struct v4l2_subdev_route *)(uintptr_t)routing->routes,
- state->routing.routes,
- min(state->routing.num_routes, routing->len_routes) *
- sizeof(*state->routing.routes));
- routing->num_routes = state->routing.num_routes;
+ v4l2_subdev_copy_routes(routing, state);
return 0;
}
@@ -2504,6 +2507,10 @@ int v4l2_subdev_s_stream_helper(struct v4l2_subdev *sd, int enable)
u64 source_mask = 0;
int pad_index = -1;
+ if (WARN_ON(!v4l2_subdev_has_op(sd, pad, enable_streams) ||
+ !v4l2_subdev_has_op(sd, pad, disable_streams)))
+ return -ENOIOCTLCMD;
+
/*
* Find the source pad. This helper is meant for subdevs that have a
* single source pad, so failures shouldn't happen, but catch them
diff --git a/drivers/platform/x86/intel/int3472/tps68470_board_data.c b/drivers/platform/x86/intel/int3472/tps68470_board_data.c
index c1ddbf9a82c0..c53542465fb4 100644
--- a/drivers/platform/x86/intel/int3472/tps68470_board_data.c
+++ b/drivers/platform/x86/intel/int3472/tps68470_board_data.c
@@ -238,7 +238,7 @@ static struct regulator_consumer_supply ovti5675_dvdd_consumer_supplies[] = {
REGULATOR_SUPPLY("dvdd", "i2c-OVTI5675:00"),
};
-static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_data = {
+static const struct regulator_init_data msi_prestige_ai_evo_tps68470_core_reg_init_data = {
.constraints = {
.min_uV = 1200000,
.max_uV = 1200000,
@@ -249,7 +249,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_core_reg_init_da
.consumer_supplies = ovti5675_dvdd_consumer_supplies,
};
-static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_data = {
+static const struct regulator_init_data msi_prestige_ai_evo_tps68470_ana_reg_init_data = {
.constraints = {
.min_uV = 2815200,
.max_uV = 2815200,
@@ -260,7 +260,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_ana_reg_init_dat
.consumer_supplies = ovti5675_avdd_consumer_supplies,
};
-static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_data = {
+static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vio_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
@@ -269,7 +269,7 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vio_reg_init_dat
},
};
-static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_data = {
+static const struct regulator_init_data msi_prestige_ai_evo_tps68470_vsio_reg_init_data = {
.constraints = {
.min_uV = 1800600,
.max_uV = 1800600,
@@ -280,12 +280,92 @@ static const struct regulator_init_data msi_p14_ai_evo_tps68470_vsio_reg_init_da
.consumer_supplies = ovti5675_dovdd_consumer_supplies,
};
-static const struct tps68470_regulator_platform_data msi_p14_ai_evo_tps68470_pdata = {
+static const struct tps68470_regulator_platform_data msi_prestige_ai_evo_tps68470_pdata = {
.reg_init_data = {
- [TPS68470_CORE] = &msi_p14_ai_evo_tps68470_core_reg_init_data,
- [TPS68470_ANA] = &msi_p14_ai_evo_tps68470_ana_reg_init_data,
- [TPS68470_VIO] = &msi_p14_ai_evo_tps68470_vio_reg_init_data,
- [TPS68470_VSIO] = &msi_p14_ai_evo_tps68470_vsio_reg_init_data,
+ [TPS68470_CORE] = &msi_prestige_ai_evo_tps68470_core_reg_init_data,
+ [TPS68470_ANA] = &msi_prestige_ai_evo_tps68470_ana_reg_init_data,
+ [TPS68470_VIO] = &msi_prestige_ai_evo_tps68470_vio_reg_init_data,
+ [TPS68470_VSIO] = &msi_prestige_ai_evo_tps68470_vsio_reg_init_data,
+ },
+};
+
+/* Settings for Intel NVL platform */
+
+static struct regulator_consumer_supply ovti13b1_core_consumer_supplies[] = {
+ REGULATOR_SUPPLY("dvdd", "i2c-OVTI13B1:01"),
+};
+
+static struct regulator_consumer_supply ovti13b1_ana_consumer_supplies[] = {
+ REGULATOR_SUPPLY("avdd", "i2c-OVTI13B1:01"),
+};
+
+static struct regulator_consumer_supply ovti13b1_vcm_consumer_supplies[] = {
+ REGULATOR_SUPPLY("vcc", "i2c-OVTI13B1:01-VCM"),
+};
+
+static struct regulator_consumer_supply ovti13b1_vsio_consumer_supplies[] = {
+ REGULATOR_SUPPLY("dovdd", "i2c-OVTI13B1:01"),
+};
+
+static const struct regulator_init_data intel_nvl_tps68470_core_reg_init_data = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .apply_uV = true,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ovti13b1_core_consumer_supplies),
+ .consumer_supplies = ovti13b1_core_consumer_supplies,
+};
+
+static const struct regulator_init_data intel_nvl_tps68470_ana_reg_init_data = {
+ .constraints = {
+ .min_uV = 2815200,
+ .max_uV = 2815200,
+ .apply_uV = true,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ovti13b1_ana_consumer_supplies),
+ .consumer_supplies = ovti13b1_ana_consumer_supplies,
+};
+static const struct regulator_init_data intel_nvl_tps68470_vcm_reg_init_data = {
+ .constraints = {
+ .min_uV = 2815200,
+ .max_uV = 2815200,
+ .apply_uV = true,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vcm_consumer_supplies),
+ .consumer_supplies = ovti13b1_vcm_consumer_supplies,
+};
+
+/* Ensure the always-on VIO regulator has the same voltage as VSIO */
+static const struct regulator_init_data intel_nvl_tps68470_vio_reg_init_data = {
+ .constraints = {
+ .min_uV = 1800600,
+ .max_uV = 1800600,
+ .apply_uV = true,
+ .always_on = true,
+ },
+};
+static const struct regulator_init_data intel_nvl_tps68470_vsio_reg_init_data = {
+ .constraints = {
+ .min_uV = 1800600,
+ .max_uV = 1800600,
+ .apply_uV = true,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(ovti13b1_vsio_consumer_supplies),
+ .consumer_supplies = ovti13b1_vsio_consumer_supplies,
+};
+
+static const struct tps68470_regulator_platform_data intel_nvl_tps68470_pdata = {
+ .reg_init_data = {
+ [TPS68470_CORE] = &intel_nvl_tps68470_core_reg_init_data,
+ [TPS68470_ANA] = &intel_nvl_tps68470_ana_reg_init_data,
+ [TPS68470_VCM] = &intel_nvl_tps68470_vcm_reg_init_data,
+ [TPS68470_VIO] = &intel_nvl_tps68470_vio_reg_init_data,
+ [TPS68470_VSIO] = &intel_nvl_tps68470_vsio_reg_init_data,
},
};
@@ -315,7 +395,7 @@ static struct gpiod_lookup_table dell_7212_int3479_gpios = {
}
};
-static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = {
+static struct gpiod_lookup_table msi_prestige_ai_evo_ovti5675_gpios = {
.dev_id = "i2c-OVTI5675:00",
.table = {
GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW),
@@ -323,13 +403,21 @@ static struct gpiod_lookup_table msi_p14_ai_evo_ovti5675_gpios = {
}
};
-static const struct property_entry msi_p14_ai_evo_gpio_props[] = {
+static struct gpiod_lookup_table intel_nvl_tps68470_gpios = {
+ .dev_id = "i2c-OVTI13B1:01",
+ .table = {
+ GPIO_LOOKUP("tps68470-gpio", 9, "reset", GPIO_ACTIVE_LOW),
+ { }
+ }
+};
+
+static const struct property_entry int3472_tps68470_daisy_chain_gpio_props[] = {
PROPERTY_ENTRY_BOOL("daisy-chain-enable"),
{ }
};
-static const struct software_node msi_p14_ai_evo_tps68470_gpio_swnode = {
- .properties = msi_p14_ai_evo_gpio_props,
+static const struct software_node int3472_tps68470_daisy_chain_gpio_swnode = {
+ .properties = int3472_tps68470_daisy_chain_gpio_props,
};
static const struct int3472_tps68470_board_data surface_go_tps68470_board_data = {
@@ -361,13 +449,23 @@ static const struct int3472_tps68470_board_data dell_7212_tps68470_board_data =
},
};
-static const struct int3472_tps68470_board_data msi_p14_ai_evo_tps68470_board_data = {
+static const struct int3472_tps68470_board_data msi_prestige_ai_evo_tps68470_board_data = {
.dev_name = "i2c-INT3472:06",
- .tps68470_regulator_pdata = &msi_p14_ai_evo_tps68470_pdata,
- .tps68470_gpio_swnode = &msi_p14_ai_evo_tps68470_gpio_swnode,
+ .tps68470_regulator_pdata = &msi_prestige_ai_evo_tps68470_pdata,
+ .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode,
.n_gpiod_lookups = 1,
.tps68470_gpio_lookup_tables = {
- &msi_p14_ai_evo_ovti5675_gpios,
+ &msi_prestige_ai_evo_ovti5675_gpios,
+ },
+};
+
+static const struct int3472_tps68470_board_data intel_nvl_tps68470_board_data = {
+ .dev_name = "i2c-INT3472:04",
+ .tps68470_regulator_pdata = &intel_nvl_tps68470_pdata,
+ .tps68470_gpio_swnode = &int3472_tps68470_daisy_chain_gpio_swnode,
+ .n_gpiod_lookups = 1,
+ .tps68470_gpio_lookup_tables = {
+ &intel_nvl_tps68470_gpios,
},
};
@@ -403,9 +501,33 @@ static const struct dmi_system_id int3472_tps68470_board_data_table[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 13 AI+ Evo A2VMG"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-13Q3"),
+ },
+ .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data,
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 14 AI+ Evo C2VMG"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-14N3"),
+ },
+ .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data,
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Prestige 16 AI+ Evo B2VMG"),
+ DMI_EXACT_MATCH(DMI_BOARD_NAME, "MS-15A3"),
+ },
+ .driver_data = (void *)&msi_prestige_ai_evo_tps68470_board_data,
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Nova Lake Client Platform"),
},
- .driver_data = (void *)&msi_p14_ai_evo_tps68470_board_data,
+ .driver_data = (void *)&intel_nvl_tps68470_board_data,
},
{ }
};
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 1aa31bddf970..06cbc5c548ca 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -50,8 +50,4 @@ menuconfig STAGING_MEDIA_DEPRECATED
If in doubt, say N here.
-if STAGING_MEDIA_DEPRECATED
-source "drivers/staging/media/deprecated/atmel/Kconfig"
-endif
-
endif
diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile
index 6f78b0edde1e..6fd7179733d8 100644
--- a/drivers/staging/media/Makefile
+++ b/drivers/staging/media/Makefile
@@ -1,5 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += deprecated/atmel/
obj-$(CONFIG_INTEL_ATOMISP) += atomisp/
obj-$(CONFIG_VIDEO_IMX_MEDIA) += imx/
obj-$(CONFIG_VIDEO_MAX96712) += max96712/
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
index d3414312e1de..f9cc5f45fe00 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
@@ -433,7 +433,7 @@ static int power_up(struct v4l2_subdev *sd)
goto fail_power;
}
- msleep(5);
+ fsleep(5000);
return 0;
fail_clk:
@@ -809,7 +809,7 @@ static int gc2235_probe(struct i2c_client *client)
ret = gc2235_s_config(&dev->sd, client->irq, gcpdev);
if (ret)
- goto out_free;
+ goto err_unregister_subdev;
dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
dev->pad.flags = MEDIA_PAD_FL_SOURCE;
@@ -818,18 +818,16 @@ static int gc2235_probe(struct i2c_client *client)
ret =
v4l2_ctrl_handler_init(&dev->ctrl_handler,
ARRAY_SIZE(gc2235_controls));
- if (ret) {
- gc2235_remove(client);
- return ret;
- }
+ if (ret)
+ goto err_csi_cfg;
for (i = 0; i < ARRAY_SIZE(gc2235_controls); i++)
v4l2_ctrl_new_custom(&dev->ctrl_handler, &gc2235_controls[i],
NULL);
if (dev->ctrl_handler.error) {
- gc2235_remove(client);
- return dev->ctrl_handler.error;
+ ret = dev->ctrl_handler.error;
+ goto err_ctrl_handler;
}
/* Use same lock for controls as for everything else. */
@@ -838,14 +836,23 @@ static int gc2235_probe(struct i2c_client *client)
ret = media_entity_pads_init(&dev->sd.entity, 1, &dev->pad);
if (ret)
- gc2235_remove(client);
+ goto err_ctrl_handler;
+
+ ret = atomisp_register_i2c_module(&dev->sd, gcpdev);
+ if (ret)
+ goto err_media_cleanup;
- return atomisp_register_i2c_module(&dev->sd, gcpdev);
+ return 0;
-out_free:
+err_media_cleanup:
+ media_entity_cleanup(&dev->sd.entity);
+err_ctrl_handler:
+ v4l2_ctrl_handler_free(&dev->ctrl_handler);
+err_csi_cfg:
+ dev->platform_data->csi_cfg(&dev->sd, 0);
+err_unregister_subdev:
v4l2_device_unregister_subdev(&dev->sd);
kfree(dev);
-
return ret;
}
diff --git a/drivers/staging/media/atomisp/i2c/ov2722.h b/drivers/staging/media/atomisp/i2c/ov2722.h
index 00317d105305..7d5ea8801fe7 100644
--- a/drivers/staging/media/atomisp/i2c/ov2722.h
+++ b/drivers/staging/media/atomisp/i2c/ov2722.h
@@ -232,342 +232,9 @@ struct ov2722_write_ctrl {
struct ov2722_write_buffer buffer;
};
-/*
- * Register settings for various resolution
- */
-#if 0
-static const struct ov2722_reg ov2722_QVGA_30fps[] = {
- {OV2722_8BIT, 0x3718, 0x10},
- {OV2722_8BIT, 0x3702, 0x0c},
- {OV2722_8BIT, 0x373a, 0x1c},
- {OV2722_8BIT, 0x3715, 0x01},
- {OV2722_8BIT, 0x3703, 0x0c},
- {OV2722_8BIT, 0x3705, 0x06},
- {OV2722_8BIT, 0x3730, 0x0e},
- {OV2722_8BIT, 0x3704, 0x1c},
- {OV2722_8BIT, 0x3f06, 0x00},
- {OV2722_8BIT, 0x371c, 0x00},
- {OV2722_8BIT, 0x371d, 0x46},
- {OV2722_8BIT, 0x371e, 0x00},
- {OV2722_8BIT, 0x371f, 0x63},
- {OV2722_8BIT, 0x3708, 0x61},
- {OV2722_8BIT, 0x3709, 0x12},
- {OV2722_8BIT, 0x3800, 0x01},
- {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */
- {OV2722_8BIT, 0x3802, 0x00},
- {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32 */
- {OV2722_8BIT, 0x3804, 0x06},
- {OV2722_8BIT, 0x3805, 0x95}, /* H crop end: 1685 */
- {OV2722_8BIT, 0x3806, 0x04},
- {OV2722_8BIT, 0x3807, 0x27}, /* V crop end: 1063 */
- {OV2722_8BIT, 0x3808, 0x01},
- {OV2722_8BIT, 0x3809, 0x50}, /* H output size: 336 */
- {OV2722_8BIT, 0x380a, 0x01},
- {OV2722_8BIT, 0x380b, 0x00}, /* V output size: 256 */
-
- /* H blank timing */
- {OV2722_8BIT, 0x380c, 0x08},
- {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */
- {OV2722_8BIT, 0x380e, 0x04},
- {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */
- {OV2722_8BIT, 0x3810, 0x00},
- {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */
- {OV2722_8BIT, 0x3812, 0x00},
- {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */
- {OV2722_8BIT, 0x3820, 0xc0},
- {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/
- {OV2722_8BIT, 0x3814, 0x71},
- {OV2722_8BIT, 0x3815, 0x71},
- {OV2722_8BIT, 0x3612, 0x49},
- {OV2722_8BIT, 0x3618, 0x00},
- {OV2722_8BIT, 0x3a08, 0x01},
- {OV2722_8BIT, 0x3a09, 0xc3},
- {OV2722_8BIT, 0x3a0a, 0x01},
- {OV2722_8BIT, 0x3a0b, 0x77},
- {OV2722_8BIT, 0x3a0d, 0x00},
- {OV2722_8BIT, 0x3a0e, 0x00},
- {OV2722_8BIT, 0x4520, 0x09},
- {OV2722_8BIT, 0x4837, 0x1b},
- {OV2722_8BIT, 0x3000, 0xff},
- {OV2722_8BIT, 0x3001, 0xff},
- {OV2722_8BIT, 0x3002, 0xf0},
- {OV2722_8BIT, 0x3600, 0x08},
- {OV2722_8BIT, 0x3621, 0xc0},
- {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */
- {OV2722_8BIT, 0x3633, 0x63},
- {OV2722_8BIT, 0x3634, 0x24},
- {OV2722_8BIT, 0x3f01, 0x0c},
- {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */
- {OV2722_8BIT, 0x3614, 0xf0},
- {OV2722_8BIT, 0x3630, 0x2d},
- {OV2722_8BIT, 0x370b, 0x62},
- {OV2722_8BIT, 0x3706, 0x61},
- {OV2722_8BIT, 0x4000, 0x02},
- {OV2722_8BIT, 0x4002, 0xc5},
- {OV2722_8BIT, 0x4005, 0x08},
- {OV2722_8BIT, 0x404f, 0x84},
- {OV2722_8BIT, 0x4051, 0x00},
- {OV2722_8BIT, 0x5000, 0xff},
- {OV2722_8BIT, 0x3a18, 0x00},
- {OV2722_8BIT, 0x3a19, 0x80},
- {OV2722_8BIT, 0x4521, 0x00},
- {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */
- {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */
- {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */
- {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */
- {OV2722_8BIT, 0x370c, 0x0c},
- {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */
- {OV2722_8BIT, 0x3035, 0x00},
- {OV2722_8BIT, 0x3036, 0x26},
- {OV2722_8BIT, 0x3037, 0xa1},
- {OV2722_8BIT, 0x303e, 0x19},
- {OV2722_8BIT, 0x3038, 0x06},
- {OV2722_8BIT, 0x3018, 0x04},
-
- /* Added for power optimization */
- {OV2722_8BIT, 0x3000, 0x00},
- {OV2722_8BIT, 0x3001, 0x00},
- {OV2722_8BIT, 0x3002, 0x00},
- {OV2722_8BIT, 0x3a0f, 0x40},
- {OV2722_8BIT, 0x3a10, 0x38},
- {OV2722_8BIT, 0x3a1b, 0x48},
- {OV2722_8BIT, 0x3a1e, 0x30},
- {OV2722_8BIT, 0x3a11, 0x90},
- {OV2722_8BIT, 0x3a1f, 0x10},
- {OV2722_8BIT, 0x3011, 0x22},
- {OV2722_8BIT, 0x3a00, 0x58},
- {OV2722_8BIT, 0x3503, 0x17},
- {OV2722_8BIT, 0x3500, 0x00},
- {OV2722_8BIT, 0x3501, 0x46},
- {OV2722_8BIT, 0x3502, 0x00},
- {OV2722_8BIT, 0x3508, 0x00},
- {OV2722_8BIT, 0x3509, 0x10},
- {OV2722_TOK_TERM, 0, 0},
-
-};
-
-static const struct ov2722_reg ov2722_480P_30fps[] = {
- {OV2722_8BIT, 0x3718, 0x10},
- {OV2722_8BIT, 0x3702, 0x18},
- {OV2722_8BIT, 0x373a, 0x3c},
- {OV2722_8BIT, 0x3715, 0x01},
- {OV2722_8BIT, 0x3703, 0x1d},
- {OV2722_8BIT, 0x3705, 0x12},
- {OV2722_8BIT, 0x3730, 0x1f},
- {OV2722_8BIT, 0x3704, 0x3f},
- {OV2722_8BIT, 0x3f06, 0x1d},
- {OV2722_8BIT, 0x371c, 0x00},
- {OV2722_8BIT, 0x371d, 0x83},
- {OV2722_8BIT, 0x371e, 0x00},
- {OV2722_8BIT, 0x371f, 0xbd},
- {OV2722_8BIT, 0x3708, 0x63},
- {OV2722_8BIT, 0x3709, 0x52},
- {OV2722_8BIT, 0x3800, 0x00},
- {OV2722_8BIT, 0x3801, 0xf2}, /* H crop start: 322 - 80 = 242*/
- {OV2722_8BIT, 0x3802, 0x00},
- {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/
- {OV2722_8BIT, 0x3804, 0x06},
- {OV2722_8BIT, 0x3805, 0xBB}, /* H crop end: 1643 + 80 = 1723*/
- {OV2722_8BIT, 0x3806, 0x04},
- {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/
- {OV2722_8BIT, 0x3808, 0x02},
- {OV2722_8BIT, 0x3809, 0xE0}, /* H output size: 656 +80 = 736*/
- {OV2722_8BIT, 0x380a, 0x01},
- {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */
-
- /* H blank timing */
- {OV2722_8BIT, 0x380c, 0x08},
- {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */
- {OV2722_8BIT, 0x380e, 0x04},
- {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */
- {OV2722_8BIT, 0x3810, 0x00},
- {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */
- {OV2722_8BIT, 0x3812, 0x00},
- {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */
- {OV2722_8BIT, 0x3820, 0x80},
- {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/
- {OV2722_8BIT, 0x3814, 0x31},
- {OV2722_8BIT, 0x3815, 0x31},
- {OV2722_8BIT, 0x3612, 0x4b},
- {OV2722_8BIT, 0x3618, 0x04},
- {OV2722_8BIT, 0x3a08, 0x02},
- {OV2722_8BIT, 0x3a09, 0x67},
- {OV2722_8BIT, 0x3a0a, 0x02},
- {OV2722_8BIT, 0x3a0b, 0x00},
- {OV2722_8BIT, 0x3a0d, 0x00},
- {OV2722_8BIT, 0x3a0e, 0x00},
- {OV2722_8BIT, 0x4520, 0x0a},
- {OV2722_8BIT, 0x4837, 0x1b},
- {OV2722_8BIT, 0x3000, 0xff},
- {OV2722_8BIT, 0x3001, 0xff},
- {OV2722_8BIT, 0x3002, 0xf0},
- {OV2722_8BIT, 0x3600, 0x08},
- {OV2722_8BIT, 0x3621, 0xc0},
- {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */
- {OV2722_8BIT, 0x3633, 0x63},
- {OV2722_8BIT, 0x3634, 0x24},
- {OV2722_8BIT, 0x3f01, 0x0c},
- {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */
- {OV2722_8BIT, 0x3614, 0xf0},
- {OV2722_8BIT, 0x3630, 0x2d},
- {OV2722_8BIT, 0x370b, 0x62},
- {OV2722_8BIT, 0x3706, 0x61},
- {OV2722_8BIT, 0x4000, 0x02},
- {OV2722_8BIT, 0x4002, 0xc5},
- {OV2722_8BIT, 0x4005, 0x08},
- {OV2722_8BIT, 0x404f, 0x84},
- {OV2722_8BIT, 0x4051, 0x00},
- {OV2722_8BIT, 0x5000, 0xff},
- {OV2722_8BIT, 0x3a18, 0x00},
- {OV2722_8BIT, 0x3a19, 0x80},
- {OV2722_8BIT, 0x4521, 0x00},
- {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */
- {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */
- {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */
- {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */
- {OV2722_8BIT, 0x370c, 0x0c},
- {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */
- {OV2722_8BIT, 0x3035, 0x00},
- {OV2722_8BIT, 0x3036, 0x26},
- {OV2722_8BIT, 0x3037, 0xa1},
- {OV2722_8BIT, 0x303e, 0x19},
- {OV2722_8BIT, 0x3038, 0x06},
- {OV2722_8BIT, 0x3018, 0x04},
-
- /* Added for power optimization */
- {OV2722_8BIT, 0x3000, 0x00},
- {OV2722_8BIT, 0x3001, 0x00},
- {OV2722_8BIT, 0x3002, 0x00},
- {OV2722_8BIT, 0x3a0f, 0x40},
- {OV2722_8BIT, 0x3a10, 0x38},
- {OV2722_8BIT, 0x3a1b, 0x48},
- {OV2722_8BIT, 0x3a1e, 0x30},
- {OV2722_8BIT, 0x3a11, 0x90},
- {OV2722_8BIT, 0x3a1f, 0x10},
- {OV2722_8BIT, 0x3011, 0x22},
- {OV2722_8BIT, 0x3a00, 0x58},
- {OV2722_8BIT, 0x3503, 0x17},
- {OV2722_8BIT, 0x3500, 0x00},
- {OV2722_8BIT, 0x3501, 0x46},
- {OV2722_8BIT, 0x3502, 0x00},
- {OV2722_8BIT, 0x3508, 0x00},
- {OV2722_8BIT, 0x3509, 0x10},
- {OV2722_TOK_TERM, 0, 0},
-};
-
-static const struct ov2722_reg ov2722_VGA_30fps[] = {
- {OV2722_8BIT, 0x3718, 0x10},
- {OV2722_8BIT, 0x3702, 0x18},
- {OV2722_8BIT, 0x373a, 0x3c},
- {OV2722_8BIT, 0x3715, 0x01},
- {OV2722_8BIT, 0x3703, 0x1d},
- {OV2722_8BIT, 0x3705, 0x12},
- {OV2722_8BIT, 0x3730, 0x1f},
- {OV2722_8BIT, 0x3704, 0x3f},
- {OV2722_8BIT, 0x3f06, 0x1d},
- {OV2722_8BIT, 0x371c, 0x00},
- {OV2722_8BIT, 0x371d, 0x83},
- {OV2722_8BIT, 0x371e, 0x00},
- {OV2722_8BIT, 0x371f, 0xbd},
- {OV2722_8BIT, 0x3708, 0x63},
- {OV2722_8BIT, 0x3709, 0x52},
- {OV2722_8BIT, 0x3800, 0x01},
- {OV2722_8BIT, 0x3801, 0x42}, /* H crop start: 322 */
- {OV2722_8BIT, 0x3802, 0x00},
- {OV2722_8BIT, 0x3803, 0x20}, /* V crop start: 32*/
- {OV2722_8BIT, 0x3804, 0x06},
- {OV2722_8BIT, 0x3805, 0x6B}, /* H crop end: 1643*/
- {OV2722_8BIT, 0x3806, 0x04},
- {OV2722_8BIT, 0x3807, 0x03}, /* V crop end: 1027*/
- {OV2722_8BIT, 0x3808, 0x02},
- {OV2722_8BIT, 0x3809, 0x90}, /* H output size: 656 */
- {OV2722_8BIT, 0x380a, 0x01},
- {OV2722_8BIT, 0x380b, 0xF0}, /* V output size: 496 */
-
- /* H blank timing */
- {OV2722_8BIT, 0x380c, 0x08},
- {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */
- {OV2722_8BIT, 0x380e, 0x04},
- {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */
- {OV2722_8BIT, 0x3810, 0x00},
- {OV2722_8BIT, 0x3811, 0x04}, /* H window offset: 5 */
- {OV2722_8BIT, 0x3812, 0x00},
- {OV2722_8BIT, 0x3813, 0x01}, /* V window offset: 2 */
- {OV2722_8BIT, 0x3820, 0x80},
- {OV2722_8BIT, 0x3821, 0x06}, /* flip isp*/
- {OV2722_8BIT, 0x3814, 0x31},
- {OV2722_8BIT, 0x3815, 0x31},
- {OV2722_8BIT, 0x3612, 0x4b},
- {OV2722_8BIT, 0x3618, 0x04},
- {OV2722_8BIT, 0x3a08, 0x02},
- {OV2722_8BIT, 0x3a09, 0x67},
- {OV2722_8BIT, 0x3a0a, 0x02},
- {OV2722_8BIT, 0x3a0b, 0x00},
- {OV2722_8BIT, 0x3a0d, 0x00},
- {OV2722_8BIT, 0x3a0e, 0x00},
- {OV2722_8BIT, 0x4520, 0x0a},
- {OV2722_8BIT, 0x4837, 0x29},
- {OV2722_8BIT, 0x3000, 0xff},
- {OV2722_8BIT, 0x3001, 0xff},
- {OV2722_8BIT, 0x3002, 0xf0},
- {OV2722_8BIT, 0x3600, 0x08},
- {OV2722_8BIT, 0x3621, 0xc0},
- {OV2722_8BIT, 0x3632, 0x53}, /* added for power opt */
- {OV2722_8BIT, 0x3633, 0x63},
- {OV2722_8BIT, 0x3634, 0x24},
- {OV2722_8BIT, 0x3f01, 0x0c},
- {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */
- {OV2722_8BIT, 0x3614, 0xf0},
- {OV2722_8BIT, 0x3630, 0x2d},
- {OV2722_8BIT, 0x370b, 0x62},
- {OV2722_8BIT, 0x3706, 0x61},
- {OV2722_8BIT, 0x4000, 0x02},
- {OV2722_8BIT, 0x4002, 0xc5},
- {OV2722_8BIT, 0x4005, 0x08},
- {OV2722_8BIT, 0x404f, 0x84},
- {OV2722_8BIT, 0x4051, 0x00},
- {OV2722_8BIT, 0x5000, 0xff},
- {OV2722_8BIT, 0x3a18, 0x00},
- {OV2722_8BIT, 0x3a19, 0x80},
- {OV2722_8BIT, 0x4521, 0x00},
- {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */
- {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */
- {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */
- {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */
- {OV2722_8BIT, 0x370c, 0x0c},
- {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */
- {OV2722_8BIT, 0x3035, 0x00},
- {OV2722_8BIT, 0x3036, 0x26},
- {OV2722_8BIT, 0x3037, 0xa1},
- {OV2722_8BIT, 0x303e, 0x19},
- {OV2722_8BIT, 0x3038, 0x06},
- {OV2722_8BIT, 0x3018, 0x04},
-
- /* Added for power optimization */
- {OV2722_8BIT, 0x3000, 0x00},
- {OV2722_8BIT, 0x3001, 0x00},
- {OV2722_8BIT, 0x3002, 0x00},
- {OV2722_8BIT, 0x3a0f, 0x40},
- {OV2722_8BIT, 0x3a10, 0x38},
- {OV2722_8BIT, 0x3a1b, 0x48},
- {OV2722_8BIT, 0x3a1e, 0x30},
- {OV2722_8BIT, 0x3a11, 0x90},
- {OV2722_8BIT, 0x3a1f, 0x10},
- {OV2722_8BIT, 0x3011, 0x22},
- {OV2722_8BIT, 0x3a00, 0x58},
- {OV2722_8BIT, 0x3503, 0x17},
- {OV2722_8BIT, 0x3500, 0x00},
- {OV2722_8BIT, 0x3501, 0x46},
- {OV2722_8BIT, 0x3502, 0x00},
- {OV2722_8BIT, 0x3508, 0x00},
- {OV2722_8BIT, 0x3509, 0x10},
- {OV2722_TOK_TERM, 0, 0},
-};
-#endif
-
static const struct ov2722_reg ov2722_1632_1092_30fps[] = {
- {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for
- a whole frame complete.(vblank) */
+ /* For stand wait for a whole frame complete.(vblank) */
+ {OV2722_8BIT, 0x3021, 0x03},
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x24},
{OV2722_8BIT, 0x373a, 0x60},
@@ -668,8 +335,8 @@ static const struct ov2722_reg ov2722_1632_1092_30fps[] = {
};
static const struct ov2722_reg ov2722_1452_1092_30fps[] = {
- {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for
- a whole frame complete.(vblank) */
+ /* For stand wait for a whole frame complete.(vblank) */
+ {OV2722_8BIT, 0x3021, 0x03},
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x24},
{OV2722_8BIT, 0x373a, 0x60},
@@ -768,118 +435,9 @@ static const struct ov2722_reg ov2722_1452_1092_30fps[] = {
{OV2722_TOK_TERM, 0, 0}
};
-#if 0
-static const struct ov2722_reg ov2722_1M3_30fps[] = {
- {OV2722_8BIT, 0x3718, 0x10},
- {OV2722_8BIT, 0x3702, 0x24},
- {OV2722_8BIT, 0x373a, 0x60},
- {OV2722_8BIT, 0x3715, 0x01},
- {OV2722_8BIT, 0x3703, 0x2e},
- {OV2722_8BIT, 0x3705, 0x10},
- {OV2722_8BIT, 0x3730, 0x30},
- {OV2722_8BIT, 0x3704, 0x62},
- {OV2722_8BIT, 0x3f06, 0x3a},
- {OV2722_8BIT, 0x371c, 0x00},
- {OV2722_8BIT, 0x371d, 0xc4},
- {OV2722_8BIT, 0x371e, 0x01},
- {OV2722_8BIT, 0x371f, 0x0d},
- {OV2722_8BIT, 0x3708, 0x61},
- {OV2722_8BIT, 0x3709, 0x12},
- {OV2722_8BIT, 0x3800, 0x01},
- {OV2722_8BIT, 0x3801, 0x4a}, /* H crop start: 330 */
- {OV2722_8BIT, 0x3802, 0x00},
- {OV2722_8BIT, 0x3803, 0x03}, /* V crop start: 3 */
- {OV2722_8BIT, 0x3804, 0x06},
- {OV2722_8BIT, 0x3805, 0xe1}, /* H crop end: 1761 */
- {OV2722_8BIT, 0x3806, 0x04},
- {OV2722_8BIT, 0x3807, 0x47}, /* V crop end: 1095 */
- {OV2722_8BIT, 0x3808, 0x05},
- {OV2722_8BIT, 0x3809, 0x88}, /* H output size: 1416 */
- {OV2722_8BIT, 0x380a, 0x04},
- {OV2722_8BIT, 0x380b, 0x0a}, /* V output size: 1034 */
-
- /* H blank timing */
- {OV2722_8BIT, 0x380c, 0x08},
- {OV2722_8BIT, 0x380d, 0x00}, /* H total size: 2048 */
- {OV2722_8BIT, 0x380e, 0x04},
- {OV2722_8BIT, 0x380f, 0xa0}, /* V total size: 1184 */
- {OV2722_8BIT, 0x3810, 0x00},
- {OV2722_8BIT, 0x3811, 0x05}, /* H window offset: 5 */
- {OV2722_8BIT, 0x3812, 0x00},
- {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */
- {OV2722_8BIT, 0x3820, 0x80},
- {OV2722_8BIT, 0x3821, 0x06}, /* flip isp */
- {OV2722_8BIT, 0x3814, 0x11},
- {OV2722_8BIT, 0x3815, 0x11},
- {OV2722_8BIT, 0x3612, 0x0b},
- {OV2722_8BIT, 0x3618, 0x04},
- {OV2722_8BIT, 0x3a08, 0x01},
- {OV2722_8BIT, 0x3a09, 0x50},
- {OV2722_8BIT, 0x3a0a, 0x01},
- {OV2722_8BIT, 0x3a0b, 0x18},
- {OV2722_8BIT, 0x3a0d, 0x03},
- {OV2722_8BIT, 0x3a0e, 0x03},
- {OV2722_8BIT, 0x4520, 0x00},
- {OV2722_8BIT, 0x4837, 0x1b},
- {OV2722_8BIT, 0x3000, 0xff},
- {OV2722_8BIT, 0x3001, 0xff},
- {OV2722_8BIT, 0x3002, 0xf0},
- {OV2722_8BIT, 0x3600, 0x08},
- {OV2722_8BIT, 0x3621, 0xc0},
- {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */
- {OV2722_8BIT, 0x3633, 0x23},
- {OV2722_8BIT, 0x3634, 0x54},
- {OV2722_8BIT, 0x3f01, 0x0c},
- {OV2722_8BIT, 0x5001, 0xc1}, /* v_en, h_en, blc_en */
- {OV2722_8BIT, 0x3614, 0xf0},
- {OV2722_8BIT, 0x3630, 0x2d},
- {OV2722_8BIT, 0x370b, 0x62},
- {OV2722_8BIT, 0x3706, 0x61},
- {OV2722_8BIT, 0x4000, 0x02},
- {OV2722_8BIT, 0x4002, 0xc5},
- {OV2722_8BIT, 0x4005, 0x08},
- {OV2722_8BIT, 0x404f, 0x84},
- {OV2722_8BIT, 0x4051, 0x00},
- {OV2722_8BIT, 0x5000, 0xcf},
- {OV2722_8BIT, 0x3a18, 0x00},
- {OV2722_8BIT, 0x3a19, 0x80},
- {OV2722_8BIT, 0x4521, 0x00},
- {OV2722_8BIT, 0x5183, 0xb0}, /* AWB red */
- {OV2722_8BIT, 0x5184, 0xb0}, /* AWB green */
- {OV2722_8BIT, 0x5185, 0xb0}, /* AWB blue */
- {OV2722_8BIT, 0x5180, 0x03}, /* AWB manual mode */
- {OV2722_8BIT, 0x370c, 0x0c},
- {OV2722_8BIT, 0x4800, 0x24}, /* clk lane gate enable */
- {OV2722_8BIT, 0x3035, 0x00},
- {OV2722_8BIT, 0x3036, 0x26},
- {OV2722_8BIT, 0x3037, 0xa1},
- {OV2722_8BIT, 0x303e, 0x19},
- {OV2722_8BIT, 0x3038, 0x06},
- {OV2722_8BIT, 0x3018, 0x04},
-
- /* Added for power optimization */
- {OV2722_8BIT, 0x3000, 0x00},
- {OV2722_8BIT, 0x3001, 0x00},
- {OV2722_8BIT, 0x3002, 0x00},
- {OV2722_8BIT, 0x3a0f, 0x40},
- {OV2722_8BIT, 0x3a10, 0x38},
- {OV2722_8BIT, 0x3a1b, 0x48},
- {OV2722_8BIT, 0x3a1e, 0x30},
- {OV2722_8BIT, 0x3a11, 0x90},
- {OV2722_8BIT, 0x3a1f, 0x10},
- {OV2722_8BIT, 0x3503, 0x17},
- {OV2722_8BIT, 0x3500, 0x00},
- {OV2722_8BIT, 0x3501, 0x46},
- {OV2722_8BIT, 0x3502, 0x00},
- {OV2722_8BIT, 0x3508, 0x00},
- {OV2722_8BIT, 0x3509, 0x10},
- {OV2722_TOK_TERM, 0, 0},
-};
-#endif
-
static const struct ov2722_reg ov2722_1080p_30fps[] = {
- {OV2722_8BIT, 0x3021, 0x03}, /* For stand wait for a whole
- frame complete.(vblank) */
+ /* For stand wait for a whole frame complete.(vblank) */
+ {OV2722_8BIT, 0x3021, 0x03},
{OV2722_8BIT, 0x3718, 0x10},
{OV2722_8BIT, 0x3702, 0x24},
{OV2722_8BIT, 0x373a, 0x60},
@@ -982,108 +540,6 @@ static const struct ov2722_reg ov2722_1080p_30fps[] = {
{OV2722_TOK_TERM, 0, 0}
};
-#if 0 /* Currently unused */
-static const struct ov2722_reg ov2722_720p_30fps[] = {
- {OV2722_8BIT, 0x3021, 0x03},
- {OV2722_8BIT, 0x3718, 0x10},
- {OV2722_8BIT, 0x3702, 0x24},
- {OV2722_8BIT, 0x373a, 0x60},
- {OV2722_8BIT, 0x3715, 0x01},
- {OV2722_8BIT, 0x3703, 0x2e},
- {OV2722_8BIT, 0x3705, 0x10},
- {OV2722_8BIT, 0x3730, 0x30},
- {OV2722_8BIT, 0x3704, 0x62},
- {OV2722_8BIT, 0x3f06, 0x3a},
- {OV2722_8BIT, 0x371c, 0x00},
- {OV2722_8BIT, 0x371d, 0xc4},
- {OV2722_8BIT, 0x371e, 0x01},
- {OV2722_8BIT, 0x371f, 0x0d},
- {OV2722_8BIT, 0x3708, 0x61},
- {OV2722_8BIT, 0x3709, 0x12},
- {OV2722_8BIT, 0x3800, 0x01},
- {OV2722_8BIT, 0x3801, 0x40}, /* H crop start: 320 */
- {OV2722_8BIT, 0x3802, 0x00},
- {OV2722_8BIT, 0x3803, 0xb1}, /* V crop start: 177 */
- {OV2722_8BIT, 0x3804, 0x06},
- {OV2722_8BIT, 0x3805, 0x55}, /* H crop end: 1621 */
- {OV2722_8BIT, 0x3806, 0x03},
- {OV2722_8BIT, 0x3807, 0x95}, /* V crop end: 918 */
- {OV2722_8BIT, 0x3808, 0x05},
- {OV2722_8BIT, 0x3809, 0x10}, /* H output size: 0x0788==1928 */
- {OV2722_8BIT, 0x380a, 0x02},
- {OV2722_8BIT, 0x380b, 0xe0}, /* output size: 0x02DE==734 */
- {OV2722_8BIT, 0x380c, 0x08},
- {OV2722_8BIT, 0x380d, 0x00}, /* H timing: 2048 */
- {OV2722_8BIT, 0x380e, 0x04},
- {OV2722_8BIT, 0x380f, 0xa3}, /* V timing: 1187 */
- {OV2722_8BIT, 0x3810, 0x00},
- {OV2722_8BIT, 0x3811, 0x03}, /* H window offset: 3 */
- {OV2722_8BIT, 0x3812, 0x00},
- {OV2722_8BIT, 0x3813, 0x02}, /* V window offset: 2 */
- {OV2722_8BIT, 0x3820, 0x80},
- {OV2722_8BIT, 0x3821, 0x06}, /* mirror */
- {OV2722_8BIT, 0x3814, 0x11},
- {OV2722_8BIT, 0x3815, 0x11},
- {OV2722_8BIT, 0x3612, 0x0b},
- {OV2722_8BIT, 0x3618, 0x04},
- {OV2722_8BIT, 0x3a08, 0x01},
- {OV2722_8BIT, 0x3a09, 0x50},
- {OV2722_8BIT, 0x3a0a, 0x01},
- {OV2722_8BIT, 0x3a0b, 0x18},
- {OV2722_8BIT, 0x3a0d, 0x03},
- {OV2722_8BIT, 0x3a0e, 0x03},
- {OV2722_8BIT, 0x4520, 0x00},
- {OV2722_8BIT, 0x4837, 0x1b},
- {OV2722_8BIT, 0x3600, 0x08},
- {OV2722_8BIT, 0x3621, 0xc0},
- {OV2722_8BIT, 0x3632, 0xd2}, /* added for power opt */
- {OV2722_8BIT, 0x3633, 0x23},
- {OV2722_8BIT, 0x3634, 0x54},
- {OV2722_8BIT, 0x3f01, 0x0c},
- {OV2722_8BIT, 0x5001, 0xc1},
- {OV2722_8BIT, 0x3614, 0xf0},
- {OV2722_8BIT, 0x3630, 0x2d},
- {OV2722_8BIT, 0x370b, 0x62},
- {OV2722_8BIT, 0x3706, 0x61},
- {OV2722_8BIT, 0x4000, 0x02},
- {OV2722_8BIT, 0x4002, 0xc5},
- {OV2722_8BIT, 0x4005, 0x08},
- {OV2722_8BIT, 0x404f, 0x84},
- {OV2722_8BIT, 0x4051, 0x00},
- {OV2722_8BIT, 0x5000, 0xcf}, /* manual 3a */
- {OV2722_8BIT, 0x301d, 0xf0}, /* enable group hold */
- {OV2722_8BIT, 0x3a18, 0x00},
- {OV2722_8BIT, 0x3a19, 0x80},
- {OV2722_8BIT, 0x4521, 0x00},
- {OV2722_8BIT, 0x5183, 0xb0},
- {OV2722_8BIT, 0x5184, 0xb0},
- {OV2722_8BIT, 0x5185, 0xb0},
- {OV2722_8BIT, 0x370c, 0x0c},
- {OV2722_8BIT, 0x3035, 0x00},
- {OV2722_8BIT, 0x3036, 0x26}, /* {0x3036, 0x2c}, //422.4 MHz */
- {OV2722_8BIT, 0x3037, 0xa1},
- {OV2722_8BIT, 0x303e, 0x19},
- {OV2722_8BIT, 0x3038, 0x06},
- {OV2722_8BIT, 0x3018, 0x04},
- {OV2722_8BIT, 0x3000, 0x00}, /* added for power optimization */
- {OV2722_8BIT, 0x3001, 0x00},
- {OV2722_8BIT, 0x3002, 0x00},
- {OV2722_8BIT, 0x3a0f, 0x40},
- {OV2722_8BIT, 0x3a10, 0x38},
- {OV2722_8BIT, 0x3a1b, 0x48},
- {OV2722_8BIT, 0x3a1e, 0x30},
- {OV2722_8BIT, 0x3a11, 0x90},
- {OV2722_8BIT, 0x3a1f, 0x10},
- {OV2722_8BIT, 0x3503, 0x17}, /* manual 3a */
- {OV2722_8BIT, 0x3500, 0x00},
- {OV2722_8BIT, 0x3501, 0x3F},
- {OV2722_8BIT, 0x3502, 0x00},
- {OV2722_8BIT, 0x3508, 0x00},
- {OV2722_8BIT, 0x3509, 0x00},
- {OV2722_TOK_TERM, 0, 0},
-};
-#endif
-
static struct ov2722_resolution ov2722_res_preview[] = {
{
.desc = "ov2722_1632_1092_30fps",
@@ -1128,99 +584,6 @@ static struct ov2722_resolution ov2722_res_preview[] = {
#define N_RES_PREVIEW (ARRAY_SIZE(ov2722_res_preview))
-/*
- * Disable non-preview configurations until the configuration selection is
- * improved.
- */
-#if 0
-struct ov2722_resolution ov2722_res_still[] = {
- {
- .desc = "ov2722_480P_30fps",
- .width = 1632,
- .height = 1092,
- .fps = 30,
- .pix_clk_freq = 85,
- .used = 0,
- .pixels_per_line = 2260,
- .lines_per_frame = 1244,
- .skip_frames = 3,
- .regs = ov2722_1632_1092_30fps,
- .mipi_freq = 422400,
- },
- {
- .desc = "ov2722_1452_1092_30fps",
- .width = 1452,
- .height = 1092,
- .fps = 30,
- .pix_clk_freq = 85,
- .used = 0,
- .pixels_per_line = 2260,
- .lines_per_frame = 1244,
- .skip_frames = 3,
- .regs = ov2722_1452_1092_30fps,
- .mipi_freq = 422400,
- },
- {
- .desc = "ov2722_1080P_30fps",
- .width = 1932,
- .height = 1092,
- .pix_clk_freq = 69,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2068,
- .lines_per_frame = 1114,
- .skip_frames = 3,
- .regs = ov2722_1080p_30fps,
- .mipi_freq = 345600,
- },
-};
-
-#define N_RES_STILL (ARRAY_SIZE(ov2722_res_still))
-
-struct ov2722_resolution ov2722_res_video[] = {
- {
- .desc = "ov2722_QVGA_30fps",
- .width = 336,
- .height = 256,
- .fps = 30,
- .pix_clk_freq = 73,
- .used = 0,
- .pixels_per_line = 2048,
- .lines_per_frame = 1184,
- .skip_frames = 3,
- .regs = ov2722_QVGA_30fps,
- .mipi_freq = 364800,
- },
- {
- .desc = "ov2722_480P_30fps",
- .width = 736,
- .height = 496,
- .fps = 30,
- .pix_clk_freq = 73,
- .used = 0,
- .pixels_per_line = 2048,
- .lines_per_frame = 1184,
- .skip_frames = 3,
- .regs = ov2722_480P_30fps,
- },
- {
- .desc = "ov2722_1080P_30fps",
- .width = 1932,
- .height = 1092,
- .pix_clk_freq = 69,
- .fps = 30,
- .used = 0,
- .pixels_per_line = 2068,
- .lines_per_frame = 1114,
- .skip_frames = 3,
- .regs = ov2722_1080p_30fps,
- .mipi_freq = 345600,
- },
-};
-
-#define N_RES_VIDEO (ARRAY_SIZE(ov2722_res_video))
-#endif
-
static struct ov2722_resolution *ov2722_res = ov2722_res_preview;
static unsigned long N_RES = N_RES_PREVIEW;
#endif
diff --git a/drivers/staging/media/atomisp/include/linux/atomisp.h b/drivers/staging/media/atomisp/include/linux/atomisp.h
index 3c8fa3f5808d..7d7f57c2d67c 100644
--- a/drivers/staging/media/atomisp/include/linux/atomisp.h
+++ b/drivers/staging/media/atomisp/include/linux/atomisp.h
@@ -22,7 +22,7 @@
#define ATOMISP_HW_STEPPING_A0 0x00
#define ATOMISP_HW_STEPPING_B0 0x10
-/*ISP binary running mode*/
+/* ISP binary running mode */
#define CI_MODE_PREVIEW 0x8000
#define CI_MODE_VIDEO 0x4000
#define CI_MODE_STILL_CAPTURE 0x2000
@@ -59,9 +59,9 @@
/* Configuration used by Bayer noise reduction and YCC noise reduction */
struct atomisp_nr_config {
- /* [gain] Strength of noise reduction for Bayer NR (Used by Bayer NR) */
+ /* [gain] Strength of noise reduction for Bayer NR */
unsigned int bnr_gain;
- /* [gain] Strength of noise reduction for YCC NR (Used by YCC NR) */
+ /* [gain] Strength of noise reduction for YCC NR */
unsigned int ynr_gain;
/* [intensity] Sensitivity of Edge (Used by Bayer NR) */
unsigned int direction;
@@ -78,7 +78,8 @@ struct atomisp_tnr_config {
unsigned int threshold_uv;/* [intensity] Motion sensitivity for U/V */
};
-/* Histogram. This contains num_elements values of type unsigned int.
+/*
+ * Contains num_elements values of type unsigned int.
* The data pointer is a DDR pointer (virtual address).
*/
struct atomisp_histogram {
@@ -94,7 +95,7 @@ enum atomisp_ob_mode {
/* Optical black level configuration */
struct atomisp_ob_config {
- /* Obtical black level mode (Fixed / Raster) */
+ /* Optical black level mode (Fixed / Raster) */
enum atomisp_ob_mode mode;
/* [intensity] optical black level for GR (relevant for fixed mode) */
unsigned int level_gr;
@@ -146,8 +147,7 @@ struct atomisp_3a_config {
unsigned int ae_y_coef_r; /* [gain] Weight of R for Y */
unsigned int ae_y_coef_g; /* [gain] Weight of G for Y */
unsigned int ae_y_coef_b; /* [gain] Weight of B for Y */
- unsigned int awb_lg_high_raw; /* [intensity]
- AWB level gate high for raw */
+ unsigned int awb_lg_high_raw; /* [intensity] AWB level gate high for raw */
unsigned int awb_lg_low; /* [intensity] AWB level gate low */
unsigned int awb_lg_high; /* [intensity] AWB level gate high */
int af_fir1_coef[7]; /* [factor] AF FIR coefficients of fir1 */
@@ -188,14 +188,15 @@ struct atomisp_dis_vector {
int y;
};
-/* DVS 2.0 Coefficient types. This structure contains 4 pointers to
- * arrays that contain the coefficients for each type.
+/*
+ * DVS 2.0 Coefficient types. This structure contains 4 pointers to
+ * arrays that contain the coefficients for each type.
*/
struct atomisp_dvs2_coef_types {
- short __user *odd_real; /** real part of the odd coefficients*/
- short __user *odd_imag; /** imaginary part of the odd coefficients*/
- short __user *even_real;/** real part of the even coefficients*/
- short __user *even_imag;/** imaginary part of the even coefficients*/
+ short __user *odd_real; /* Real part of the odd coefficients */
+ short __user *odd_imag; /* Imaginary part of the odd coefficients */
+ short __user *even_real;/* Real part of the even coefficients */
+ short __user *even_imag;/* Imaginary part of the even coefficients */
};
/*
@@ -203,10 +204,10 @@ struct atomisp_dvs2_coef_types {
* arrays that contain the statistics for each type.
*/
struct atomisp_dvs2_stat_types {
- int __user *odd_real; /** real part of the odd statistics*/
- int __user *odd_imag; /** imaginary part of the odd statistics*/
- int __user *even_real;/** real part of the even statistics*/
- int __user *even_imag;/** imaginary part of the even statistics*/
+ int __user *odd_real; /* Real part of the odd statistics */
+ int __user *odd_imag; /* Imaginary part of the odd statistics */
+ int __user *even_real;/* Real part of the even statistics */
+ int __user *even_imag;/* Imaginary part of the even statistics */
};
struct atomisp_dis_coefficients {
@@ -234,12 +235,12 @@ struct atomisp_3a_rgby_output {
};
/*
- * Because we have 2 pipes at max to output metadata, therefore driver will use
- * ATOMISP_MAIN_METADATA to specify the metadata from the pipe which keeps
- * streaming always and use ATOMISP_SEC_METADATA to specify the metadata from
- * the pipe which is streaming by request like capture pipe of ZSL or SDV mode
- * as secondary metadata. And for the use case which has only one pipe
- * streaming like online capture, ATOMISP_MAIN_METADATA will be used.
+ * As the driver can output metadata on two pipes at max,
+ * ATOMISP_MAIN_METADATA is used for the pipe that streams continuously.
+ * ATOMISP_SEC_METADATA is used for the pipe that streams on demand, e.g.,
+ * the capture pipe in ZSL or SDV modes.
+ * In use cases with a single streaming pipe (like online capture),
+ * only ATOMISP_MAIN_METADATA is used.
*/
enum atomisp_metadata_type {
ATOMISP_MAIN_METADATA = 0,
@@ -257,7 +258,7 @@ struct atomisp_3a_statistics {
struct atomisp_3a_output __user *data;
struct atomisp_3a_rgby_output __user *rgby_data;
u32 exp_id; /* exposure ID */
- u32 isp_config_id; /* isp config ID */
+ u32 isp_config_id;
};
/* White Balance (Gain Adjust) */
@@ -272,8 +273,8 @@ struct atomisp_wb_config {
/* Color Space Conversion settings */
struct atomisp_cc_config {
unsigned int fraction_bits;
- int matrix[3 * 3]; /* RGB2YUV Color matrix, signed
- <13-fraction_bits>.<fraction_bits> */
+ /* RGB2YUV Color matrix, signed <13-fraction_bits>.<fraction_bits> */
+ int matrix[3 * 3];
};
/* De pixel noise configuration */
@@ -291,13 +292,15 @@ struct atomisp_ce_config {
/* Defect pixel correction configuration */
struct atomisp_dp_config {
- /* [intensity] The threshold of defect Pixel Correction, representing
+ /*
+ * [intensity] The threshold of defect Pixel Correction, representing
* the permissible difference of intensity between one pixel and its
* surrounding pixels. Smaller values result in more frequent pixel
* corrections. u0_16
*/
unsigned int threshold;
- /* [gain] The sensitivity of mis-correction. ISP will miss a lot of
+ /*
+ * [gain] The sensitivity of mis-correction. ISP will miss a lot of
* defects if the value is set too large. u8_8
*/
unsigned int gain;
@@ -312,7 +315,7 @@ struct atomisp_xnr_config {
__u16 threshold;
};
-/* metadata config */
+/* Metadata config */
struct atomisp_metadata_config {
u32 metadata_height;
u32 metadata_stride;
@@ -322,31 +325,30 @@ struct atomisp_metadata_config {
* Generic resolution structure.
*/
struct atomisp_resolution {
- u32 width; /** Width */
- u32 height; /** Height */
+ u32 width;
+ u32 height;
};
/*
- * This specifies the coordinates (x,y)
+ * Specifies the zoom point coordinates (x,y)
*/
struct atomisp_zoom_point {
- s32 x; /** x coordinate */
- s32 y; /** y coordinate */
+ s32 x;
+ s32 y;
};
/*
- * This specifies the region
+ * Specifies the zoom region
*/
struct atomisp_zoom_region {
- struct atomisp_zoom_point
- origin; /* Starting point coordinates for the region */
+ struct atomisp_zoom_point origin; /* Starting point coordinates for the region */
struct atomisp_resolution resolution; /* Region resolution */
};
struct atomisp_dz_config {
- u32 dx; /** Horizontal zoom factor */
- u32 dy; /** Vertical zoom factor */
- struct atomisp_zoom_region zoom_region; /** region for zoom */
+ u32 dx; /* Horizontal zoom factor */
+ u32 dy; /* Vertical zoom factor */
+ struct atomisp_zoom_region zoom_region;
};
struct atomisp_parm {
@@ -367,8 +369,8 @@ struct atomisp_parm {
};
struct dvs2_bq_resolution {
- int width_bq; /* width [BQ] */
- int height_bq; /* height [BQ] */
+ int width_bq;
+ int height_bq;
};
struct atomisp_dvs2_bq_resolutions {
@@ -378,9 +380,9 @@ struct atomisp_dvs2_bq_resolutions {
struct dvs2_bq_resolution output_bq;
/* GDC effective envelope size [BQ] */
struct dvs2_bq_resolution envelope_bq;
- /* isp pipe filter size [BQ] */
+ /* ISP pipe filter size [BQ] */
struct dvs2_bq_resolution ispfilter_bq;
- /* GDC shit size [BQ] */
+ /* GDC shift size [BQ] */
struct dvs2_bq_resolution gdc_shift_bq;
};
@@ -411,8 +413,8 @@ struct atomisp_parameters {
struct atomisp_cnr_config *cnr_config; /* Chroma Noise Reduction */
struct atomisp_macc_config *macc_config; /* MACC */
struct atomisp_ctc_config *ctc_config; /* Chroma Tone Control */
- struct atomisp_aa_config *aa_config; /* Anti-Aliasing */
- struct atomisp_aa_config *baa_config; /* Anti-Aliasing */
+ struct atomisp_aa_config *aa_config; /* Anti-Aliasing */
+ struct atomisp_aa_config *baa_config; /* Anti-Aliasing */
struct atomisp_ce_config *ce_config;
struct atomisp_dvs_6axis_config *dvs_6axis_config;
struct atomisp_ob_config *ob_config; /* Objective Black config */
@@ -425,10 +427,8 @@ struct atomisp_parameters {
struct atomisp_3a_config *a3a_config; /* 3A Statistics config */
struct atomisp_xnr_config *xnr_config; /* eXtra Noise Reduction */
struct atomisp_dz_config *dz_config; /* Digital Zoom */
- struct atomisp_cc_config *yuv2rgb_cc_config; /* Color
- Correction config */
- struct atomisp_cc_config *rgb2yuv_cc_config; /* Color
- Correction config */
+ struct atomisp_cc_config *yuv2rgb_cc_config; /* Color Correction config */
+ struct atomisp_cc_config *rgb2yuv_cc_config; /* Color Correction config */
struct atomisp_macc_table *macc_table;
struct atomisp_gamma_table *gamma_table;
struct atomisp_ctc_table *ctc_table;
@@ -466,8 +466,8 @@ struct atomisp_parameters {
void *res_mgr_2500_config;
/*
- * Output frame pointer the config is to be applied to (optional),
- * set to NULL to make this config is applied as global.
+ * Output frame pointer to which the config will be applied to (optional),
+ * Set to NULL to make this config be applied as global.
*/
void *output_frame;
/*
@@ -490,9 +490,9 @@ struct atomisp_gamma_table {
unsigned short data[ATOMISP_GAMMA_TABLE_SIZE];
};
-/* Morphing table for advanced ISP.
- * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements
- * in memory.
+/*
+ * Morphing table for advanced ISP.
+ * Each line of width elements takes up COORD_TABLE_EXT_WIDTH elements in memory.
*/
#define ATOMISP_MORPH_TABLE_NUM_PLANES 6
struct atomisp_morph_table {
@@ -519,7 +519,7 @@ struct atomisp_shading_table {
__u16 *data[ATOMISP_NUM_SC_COLORS];
};
-/* parameter for MACC */
+/* Parameter for MACC */
#define ATOMISP_NUM_MACC_AXES 16
struct atomisp_macc_table {
short data[4 * ATOMISP_NUM_MACC_AXES];
@@ -530,7 +530,7 @@ struct atomisp_macc_config {
struct atomisp_macc_table table;
};
-/* Parameter for ctc parameter control */
+/* CTC parameter control */
#define ATOMISP_CTC_TABLE_SIZE 1024
struct atomisp_ctc_table {
unsigned short data[ATOMISP_CTC_TABLE_SIZE];
@@ -538,9 +538,10 @@ struct atomisp_ctc_table {
/* Parameter for overlay image loading */
struct atomisp_overlay {
- /* the frame containing the overlay data The overlay frame width should
- * be the multiples of 2*ISP_VEC_NELEMS. The overlay frame height
- * should be the multiples of 2.
+ /*
+ * The frame containing the overlay data. The overlay frame width should
+ * be a multiple of 2 * ISP_VEC_NELEMS. The overlay frame height
+ * should be a multiple of 2.
*/
struct v4l2_framebuffer *frame;
/* Y value of overlay background */
@@ -549,23 +550,27 @@ struct atomisp_overlay {
char bg_u;
/* V value of overlay background */
char bg_v;
- /* the blending percent of input data for Y subpixels */
+ /* The blending percentage of input data for Y subpixels */
unsigned char blend_input_perc_y;
- /* the blending percent of input data for U subpixels */
+ /* The blending percentage of input data for U subpixels */
unsigned char blend_input_perc_u;
- /* the blending percent of input data for V subpixels */
+ /* The blending percentage of input data for V subpixels */
unsigned char blend_input_perc_v;
- /* the blending percent of overlay data for Y subpixels */
+ /* The blending percentage of overlay data for Y subpixels */
unsigned char blend_overlay_perc_y;
- /* the blending percent of overlay data for U subpixels */
+ /* The blending percentage of overlay data for U subpixels */
unsigned char blend_overlay_perc_u;
- /* the blending percent of overlay data for V subpixels */
+ /* The blending percentage of overlay data for V subpixels */
unsigned char blend_overlay_perc_v;
- /* the overlay start x pixel position on output frame It should be the
- multiples of 2*ISP_VEC_NELEMS. */
+ /*
+ * The overlay "start x" pixel position on the output frame. It should be a
+ * multiple of 2 * ISP_VEC_NELEMS.
+ */
unsigned int overlay_start_x;
- /* the overlay start y pixel position on output frame It should be the
- multiples of 2. */
+ /*
+ * The overlay "start y" pixel position on the output frame. It should be a
+ * multiple of 2.
+ */
unsigned int overlay_start_y;
};
@@ -659,7 +664,7 @@ enum atomisp_burst_capture_options {
#define EXT_ISP_SHOT_MODE_ANIMATED_PHOTO 10
#define EXT_ISP_SHOT_MODE_SPORTS 11
-/*Private IOCTLs for ISP */
+/* Private IOCTLs for ISP */
#define ATOMISP_IOC_G_XNR \
_IOR('v', BASE_VIDIOC_PRIVATE + 0, int)
#define ATOMISP_IOC_S_XNR \
@@ -684,7 +689,8 @@ enum atomisp_burst_capture_options {
_IOR('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config)
#define ATOMISP_IOC_S_EE \
_IOW('v', BASE_VIDIOC_PRIVATE + 5, struct atomisp_ee_config)
-/* Digital Image Stabilization:
+/*
+ * Digital Image Stabilization:
* 1. get dis statistics: reads DIS statistics from ISP (every frame)
* 2. set dis coefficients: set DIS filter coefficients (one time)
* 3. set dis motion vector: set motion vector (result of DIS, every frame)
@@ -716,54 +722,54 @@ enum atomisp_burst_capture_options {
#define ATOMISP_IOC_S_ISP_GDC_TAB \
_IOW('v', BASE_VIDIOC_PRIVATE + 10, struct atomisp_morph_table)
-/* macc parameter control*/
+/* MACC parameter control */
#define ATOMISP_IOC_G_ISP_MACC \
_IOR('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config)
#define ATOMISP_IOC_S_ISP_MACC \
_IOW('v', BASE_VIDIOC_PRIVATE + 12, struct atomisp_macc_config)
-/* Defect pixel detection & Correction */
+/* Defect pixel detection & correction */
#define ATOMISP_IOC_G_ISP_BAD_PIXEL_DETECTION \
_IOR('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config)
#define ATOMISP_IOC_S_ISP_BAD_PIXEL_DETECTION \
_IOW('v', BASE_VIDIOC_PRIVATE + 13, struct atomisp_dp_config)
-/* False Color Correction */
+/* False color correction */
#define ATOMISP_IOC_G_ISP_FALSE_COLOR_CORRECTION \
_IOR('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config)
#define ATOMISP_IOC_S_ISP_FALSE_COLOR_CORRECTION \
_IOW('v', BASE_VIDIOC_PRIVATE + 14, struct atomisp_de_config)
-/* ctc parameter control */
+/* CTC parameter control */
#define ATOMISP_IOC_G_ISP_CTC \
_IOR('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table)
#define ATOMISP_IOC_S_ISP_CTC \
_IOW('v', BASE_VIDIOC_PRIVATE + 15, struct atomisp_ctc_table)
-/* white balance Correction */
+/* White balance correction */
#define ATOMISP_IOC_G_ISP_WHITE_BALANCE \
_IOR('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config)
#define ATOMISP_IOC_S_ISP_WHITE_BALANCE \
_IOW('v', BASE_VIDIOC_PRIVATE + 16, struct atomisp_wb_config)
-/* fpn table loading */
+/* FPN table loading */
#define ATOMISP_IOC_S_ISP_FPN_TABLE \
_IOW('v', BASE_VIDIOC_PRIVATE + 17, struct v4l2_framebuffer)
-/* overlay image loading */
+/* Overlay image loading */
#define ATOMISP_IOC_G_ISP_OVERLAY \
_IOWR('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay)
#define ATOMISP_IOC_S_ISP_OVERLAY \
_IOW('v', BASE_VIDIOC_PRIVATE + 18, struct atomisp_overlay)
-/* bcd driver bridge */
+/* BCD driver bridge */
#define ATOMISP_IOC_CAMERA_BRIDGE \
_IOWR('v', BASE_VIDIOC_PRIVATE + 19, struct atomisp_bc_video_package)
#define ATOMISP_IOC_S_EXPOSURE \
_IOW('v', BASE_VIDIOC_PRIVATE + 21, struct atomisp_exposure)
-/* white balance Correction */
+/* White balance correction */
#define ATOMISP_IOC_G_3A_CONFIG \
_IOR('v', BASE_VIDIOC_PRIVATE + 23, struct atomisp_3a_config)
#define ATOMISP_IOC_S_3A_CONFIG \
@@ -773,7 +779,7 @@ enum atomisp_burst_capture_options {
#define ATOMISP_IOC_S_ISP_SHD_TAB \
_IOWR('v', BASE_VIDIOC_PRIVATE + 27, struct atomisp_shading_table)
-/* Gamma Correction */
+/* Gamma correction */
#define ATOMISP_IOC_G_ISP_GAMMA_CORRECTION \
_IOR('v', BASE_VIDIOC_PRIVATE + 28, struct atomisp_gc_config)
@@ -804,7 +810,7 @@ enum atomisp_burst_capture_options {
#define ATOMISP_IOC_S_ARRAY_RESOLUTION \
_IOW('v', BASE_VIDIOC_PRIVATE + 45, struct atomisp_resolution)
-/* for depth mode sensor frame sync compensation */
+/* For depth mode sensor frame sync compensation */
#define ATOMISP_IOC_G_DEPTH_SYNC_COMP \
_IOR('v', BASE_VIDIOC_PRIVATE + 46, unsigned int)
@@ -822,7 +828,7 @@ enum atomisp_burst_capture_options {
* _IOW('v', BASE_VIDIOC_PRIVATE + 56, struct atomisp_sensor_regs)
*/
-/* ISP Private control IDs */
+/* ISP Private control IDs */
#define V4L2_CID_ATOMISP_BAD_PIXEL_DETECTION \
(V4L2_CID_PRIVATE_BASE + 0)
#define V4L2_CID_ATOMISP_POSTPROCESS_GDC_CAC \
@@ -836,8 +842,10 @@ enum atomisp_burst_capture_options {
#define V4L2_CID_ATOMISP_LOW_LIGHT \
(V4L2_CID_PRIVATE_BASE + 5)
-/* Camera class:
- * Exposure, Flash and privacy (indicator) light controls, to be upstreamed */
+/*
+ * Camera class:
+ * Exposure, Flash and privacy (indicator) light controls, to be upstreamed
+ */
#define V4L2_CID_CAMERA_LASTP1 (V4L2_CID_CAMERA_CLASS_BASE + 1024)
#define V4L2_CID_RUN_MODE (V4L2_CID_CAMERA_LASTP1 + 20)
@@ -879,7 +887,7 @@ enum atomisp_burst_capture_options {
#define V4L2_EVENT_ATOMISP_ACC_COMPLETE (V4L2_EVENT_PRIVATE_START + 4)
#define V4L2_EVENT_ATOMISP_PAUSE_BUFFER (V4L2_EVENT_PRIVATE_START + 5)
#define V4L2_EVENT_ATOMISP_CSS_RESET (V4L2_EVENT_PRIVATE_START + 6)
-/* Nonstandard color effects for V4L2_CID_COLORFX */
+/* Non-standard color effects for V4L2_CID_COLORFX */
enum {
V4L2_COLORFX_SKIN_WHITEN_LOW = 1001,
V4L2_COLORFX_SKIN_WHITEN_HIGH = 1002,
diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h
index 426c5ee4ec18..74092af1c659 100644
--- a/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h
+++ b/drivers/staging/media/atomisp/include/linux/atomisp_gmin_platform.h
@@ -15,8 +15,7 @@ int atomisp_gmin_remove_subdev(struct v4l2_subdev *sd);
int gmin_get_var_int(struct device *dev, bool is_gmin,
const char *var, int def);
struct camera_sensor_platform_data *
-gmin_camera_platform_data(
- struct v4l2_subdev *subdev,
- enum atomisp_input_format csi_format,
- enum atomisp_bayer_order csi_bayer);
+ gmin_camera_platform_data(struct v4l2_subdev *subdev,
+ enum atomisp_input_format csi_format,
+ enum atomisp_bayer_order csi_bayer);
#endif
diff --git a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
index 6146555fe9cf..0d6f2a4039f1 100644
--- a/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
+++ b/drivers/staging/media/atomisp/include/linux/atomisp_platform.h
@@ -57,7 +57,8 @@ enum atomisp_input_format {
ATOMISP_INPUT_FORMAT_RAW_16, /* RAW data, 16 bits per pixel */
ATOMISP_INPUT_FORMAT_BINARY_8, /* Binary byte stream. */
- /* CSI2-MIPI specific format: Generic short packet data. It is used to
+ /*
+ * CSI2-MIPI specific format: Generic short packet data. It is used to
* keep the timing information for the opening/closing of shutters,
* triggering of flashes and etc.
*/
@@ -70,18 +71,18 @@ enum atomisp_input_format {
ATOMISP_INPUT_FORMAT_GENERIC_SHORT7, /* Generic Short Packet Code 7 */
ATOMISP_INPUT_FORMAT_GENERIC_SHORT8, /* Generic Short Packet Code 8 */
- /* CSI2-MIPI specific format: YUV data.
- */
- ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT, /* YUV420 8-bit (Chroma Shifted
- Pixel Sampling) */
- ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT, /* YUV420 8-bit (Chroma Shifted
- Pixel Sampling) */
+ /* YUV data */
+ /* YUV420 8-bit (Chroma Shifted Pixel Sampling) */
+ ATOMISP_INPUT_FORMAT_YUV420_8_SHIFT,
+ /* YUV420 10-bit (Chroma Shifted Pixel Sampling) */
+ ATOMISP_INPUT_FORMAT_YUV420_10_SHIFT,
- /* CSI2-MIPI specific format: Generic long packet data
- */
- ATOMISP_INPUT_FORMAT_EMBEDDED, /* Embedded 8-bit non Image Data */
+ /* CSI2-MIPI specific format: Generic long packet data */
+ /* Embedded 8-bit non Image Data */
+ ATOMISP_INPUT_FORMAT_EMBEDDED,
- /* CSI2-MIPI specific format: User defined byte-based data. For example,
+ /*
+ * User defined byte-based data. For example,
* the data transmitter (e.g. the SoC sensor) can keep the JPEG data as
* the User Defined Data Type 4 and the MPEG data as the
* User Defined Data Type 7.
@@ -105,9 +106,9 @@ struct intel_v4l2_subdev_table {
};
/*
- * Sensor of external ISP can send multiple streams with different mipi data
+ * Sensor of external ISP can send multiple streams with different MIPI data
* type in the same virtual channel. This information needs to come from the
- * sensor or external ISP
+ * sensor or external ISP.
*/
struct atomisp_isys_config_info {
u8 input_format;
@@ -118,16 +119,17 @@ struct atomisp_isys_config_info {
struct atomisp_input_stream_info {
enum atomisp_input_stream_id stream;
u8 enable;
- /* Sensor driver fills ch_id with the id
- of the virtual channel. */
+ /* Sensor driver fills ch_id with the id of the virtual channel. */
u8 ch_id;
- /* Tells how many streams in this virtual channel. If 0 ignore rest
- * and the input format will be from mipi_info */
+ /*
+ * Tells the number of streams in this virtual channel. If 0, ignore the
+ * rest and the input format will be from mipi_info.
+ */
u8 isys_configs;
/*
- * if more isys_configs is more than 0, sensor needs to configure the
- * input format differently. width and height can be 0. If width and
- * height is not zero, then the corresponding data needs to be set
+ * If isys_configs is greater than 0, sensor needs to configure the
+ * input format differently. Width and height can be 0. If width and
+ * height are not zero, then the corresponding data needs to be set.
*/
struct atomisp_isys_config_info isys_info[MAX_STREAMS_PER_CHANNEL];
};
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index fec369575d88..6cd500d9fd26 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -811,7 +811,7 @@ void atomisp_buf_done(struct atomisp_sub_device *asd, int error,
/* New global dvs 6axis config should be blocked
* here if there's a buffer with per-frame parameters
* pending in CSS frame buffer queue.
- * This is to aviod zooming vibration since global
+ * This is to avoid zooming vibration since global
* parameters take effect immediately while
* per-frame parameters are taken after previous
* buffers in CSS got processed.
@@ -974,7 +974,7 @@ irqreturn_t atomisp_isr_thread(int irq, void *isp_ptr)
* to a FIFO, then process the event in the FIFO.
* This will not have issue in single stream mode, but it do have some
* issue in multiple stream case. The issue is that
- * ia_css_pipe_dequeue_buffer() will not return the corrent buffer in
+ * ia_css_pipe_dequeue_buffer() will not return the correct buffer in
* a specific pipe.
*
* This is due to ia_css_pipe_dequeue_buffer() does not take the
@@ -1380,8 +1380,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd,
if (atomisp_css_get_grid_info(asd, pipe_id))
return;
- /* We must free all buffers because they no longer match
- the grid size. */
+ /*
+ * We must free all buffers because they no longer match
+ * the grid size.
+ */
atomisp_css_free_stat_buffers(asd);
err = atomisp_alloc_css_stat_bufs(asd, ATOMISP_INPUT_STREAM_GENERAL);
@@ -1393,8 +1395,10 @@ static void atomisp_update_grid_info(struct atomisp_sub_device *asd,
if (atomisp_alloc_3a_output_buf(asd)) {
/* Failure for 3A buffers does not influence DIS buffers */
if (asd->params.s3a_output_bytes != 0) {
- /* For SOC sensor happens s3a_output_bytes == 0,
- * using if condition to exclude false error log */
+ /*
+ * For SOC sensor happens s3a_output_bytes == 0,
+ * using if condition to exclude false error log
+ */
dev_err(isp->dev, "Failed to allocate memory for 3A statistics\n");
}
goto err;
@@ -1575,7 +1579,7 @@ int atomisp_set_dis_vector(struct atomisp_sub_device *asd,
}
/*
- * Function to set/get image stablization statistics
+ * Function to set/get image stabilization statistics
*/
int atomisp_get_dis_stat(struct atomisp_sub_device *asd,
struct atomisp_dis_statistics *stats)
@@ -1686,9 +1690,11 @@ int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag,
return -EINVAL;
if (atomisp_compare_grid(asd, &config->grid_info) != 0) {
- /* If the grid info in the argument differs from the current
- grid info, we tell the caller to reset the grid size and
- try again. */
+ /*
+ * If the grid info in the argument differs from the current
+ * grid info, we tell the caller to reset the grid size and
+ * try again.
+ */
return -EAGAIN;
}
@@ -1762,15 +1768,13 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd,
return -EINVAL;
}
- if (dz_config->zoom_region.resolution.width
- == asd->sensor_array_res.width
- || dz_config->zoom_region.resolution.height
- == asd->sensor_array_res.height) {
+ if (dz_config->zoom_region.width == asd->sensor_array_res.width ||
+ dz_config->zoom_region.height == asd->sensor_array_res.height) {
/*no need crop region*/
- dz_config->zoom_region.origin.x = 0;
- dz_config->zoom_region.origin.y = 0;
- dz_config->zoom_region.resolution.width = eff_res.width;
- dz_config->zoom_region.resolution.height = eff_res.height;
+ dz_config->zoom_region.left = 0;
+ dz_config->zoom_region.top = 0;
+ dz_config->zoom_region.width = eff_res.width;
+ dz_config->zoom_region.height = eff_res.height;
return 0;
}
@@ -1781,18 +1785,14 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd,
*/
if (!IS_ISP2401) {
- dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x
- * eff_res.width
- / asd->sensor_array_res.width;
- dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y
- * eff_res.height
- / asd->sensor_array_res.height;
- dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width
- * eff_res.width
- / asd->sensor_array_res.width;
- dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height
- * eff_res.height
- / asd->sensor_array_res.height;
+ dz_config->zoom_region.left = dz_config->zoom_region.left *
+ eff_res.width / asd->sensor_array_res.width;
+ dz_config->zoom_region.top = dz_config->zoom_region.top *
+ eff_res.height / asd->sensor_array_res.height;
+ dz_config->zoom_region.width = dz_config->zoom_region.width *
+ eff_res.width / asd->sensor_array_res.width;
+ dz_config->zoom_region.height = dz_config->zoom_region.height *
+ eff_res.height / asd->sensor_array_res.height;
/*
* Set same ratio of crop region resolution and current pipe output
* resolution
@@ -1819,62 +1819,62 @@ int atomisp_calculate_real_zoom_region(struct atomisp_sub_device *asd,
- asd->sensor_array_res.width
* out_res.height / out_res.width;
h_offset = h_offset / 2;
- if (dz_config->zoom_region.origin.y < h_offset)
- dz_config->zoom_region.origin.y = 0;
+ if (dz_config->zoom_region.top < h_offset)
+ dz_config->zoom_region.top = 0;
else
- dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y - h_offset;
+ dz_config->zoom_region.top =
+ dz_config->zoom_region.top - h_offset;
w_offset = 0;
} else {
w_offset = asd->sensor_array_res.width
- asd->sensor_array_res.height
* out_res.width / out_res.height;
w_offset = w_offset / 2;
- if (dz_config->zoom_region.origin.x < w_offset)
- dz_config->zoom_region.origin.x = 0;
+ if (dz_config->zoom_region.left < w_offset)
+ dz_config->zoom_region.left = 0;
else
- dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x - w_offset;
+ dz_config->zoom_region.left =
+ dz_config->zoom_region.left - w_offset;
h_offset = 0;
}
- dz_config->zoom_region.origin.x = dz_config->zoom_region.origin.x
- * eff_res.width
- / (asd->sensor_array_res.width - 2 * w_offset);
- dz_config->zoom_region.origin.y = dz_config->zoom_region.origin.y
- * eff_res.height
- / (asd->sensor_array_res.height - 2 * h_offset);
- dz_config->zoom_region.resolution.width = dz_config->zoom_region.resolution.width
- * eff_res.width
- / (asd->sensor_array_res.width - 2 * w_offset);
- dz_config->zoom_region.resolution.height = dz_config->zoom_region.resolution.height
- * eff_res.height
- / (asd->sensor_array_res.height - 2 * h_offset);
- }
-
- if (out_res.width * dz_config->zoom_region.resolution.height
- > dz_config->zoom_region.resolution.width * out_res.height) {
- dz_config->zoom_region.resolution.height =
- dz_config->zoom_region.resolution.width
- * out_res.height / out_res.width;
+ dz_config->zoom_region.left = dz_config->zoom_region.left *
+ eff_res.width /
+ (asd->sensor_array_res.width - 2 * w_offset);
+ dz_config->zoom_region.top = dz_config->zoom_region.top *
+ eff_res.height /
+ (asd->sensor_array_res.height - 2 * h_offset);
+ dz_config->zoom_region.width = dz_config->zoom_region.width *
+ eff_res.width /
+ (asd->sensor_array_res.width - 2 * w_offset);
+ dz_config->zoom_region.height = dz_config->zoom_region.height *
+ eff_res.height /
+ (asd->sensor_array_res.height - 2 * h_offset);
+ }
+
+ if (out_res.width * dz_config->zoom_region.height >
+ dz_config->zoom_region.width * out_res.height) {
+ dz_config->zoom_region.height = dz_config->zoom_region.width *
+ out_res.height / out_res.width;
} else {
- dz_config->zoom_region.resolution.width =
- dz_config->zoom_region.resolution.height
- * out_res.width / out_res.height;
+ dz_config->zoom_region.width = dz_config->zoom_region.height *
+ out_res.width / out_res.height;
}
dev_dbg(asd->isp->dev,
"%s crop region:(%d,%d),(%d,%d) eff_res(%d, %d) array_size(%d,%d) out_res(%d, %d)\n",
- __func__, dz_config->zoom_region.origin.x,
- dz_config->zoom_region.origin.y,
- dz_config->zoom_region.resolution.width,
- dz_config->zoom_region.resolution.height,
+ __func__, dz_config->zoom_region.left,
+ dz_config->zoom_region.top,
+ dz_config->zoom_region.width,
+ dz_config->zoom_region.height,
eff_res.width, eff_res.height,
asd->sensor_array_res.width,
asd->sensor_array_res.height,
out_res.width, out_res.height);
- if ((dz_config->zoom_region.origin.x +
- dz_config->zoom_region.resolution.width
+ if ((dz_config->zoom_region.left +
+ dz_config->zoom_region.width
> eff_res.width) ||
- (dz_config->zoom_region.origin.y +
- dz_config->zoom_region.resolution.height
+ (dz_config->zoom_region.top +
+ dz_config->zoom_region.height
> eff_res.height))
return -EINVAL;
@@ -1899,10 +1899,10 @@ static bool atomisp_check_zoom_region(
config.width = asd->sensor_array_res.width;
config.height = asd->sensor_array_res.height;
- w = dz_config->zoom_region.origin.x +
- dz_config->zoom_region.resolution.width;
- h = dz_config->zoom_region.origin.y +
- dz_config->zoom_region.resolution.height;
+ w = dz_config->zoom_region.left +
+ dz_config->zoom_region.width;
+ h = dz_config->zoom_region.top +
+ dz_config->zoom_region.height;
if ((w <= config.width) && (h <= config.height) && w > 0 && h > 0)
flag = true;
@@ -1910,10 +1910,10 @@ static bool atomisp_check_zoom_region(
/* setting error zoom region */
dev_err(asd->isp->dev,
"%s zoom region ERROR:dz_config:(%d,%d),(%d,%d)array_res(%d, %d)\n",
- __func__, dz_config->zoom_region.origin.x,
- dz_config->zoom_region.origin.y,
- dz_config->zoom_region.resolution.width,
- dz_config->zoom_region.resolution.height,
+ __func__, dz_config->zoom_region.left,
+ dz_config->zoom_region.top,
+ dz_config->zoom_region.width,
+ dz_config->zoom_region.height,
config.width, config.height);
return flag;
@@ -2032,8 +2032,8 @@ static unsigned int long copy_from_compatible(void *to, const void *from,
{
if (from_user)
return copy_from_user(to, (void __user *)from, n);
- else
- memcpy(to, from, n);
+
+ memcpy(to, from, n);
return 0;
}
@@ -2462,9 +2462,11 @@ int atomisp_css_cp_dvs2_coefs(struct atomisp_sub_device *asd,
if (sizeof(*cur) != sizeof(coefs->grid) ||
memcmp(&coefs->grid, cur, sizeof(coefs->grid))) {
dev_err(asd->isp->dev, "dvs grid mismatch!\n");
- /* If the grid info in the argument differs from the current
- grid info, we tell the caller to reset the grid size and
- try again. */
+ /*
+ * If the grid info in the argument differs from the current
+ * grid info, we tell the caller to reset the grid size and
+ * try again.
+ */
return -EAGAIN;
}
@@ -3025,9 +3027,11 @@ int atomisp_param(struct atomisp_sub_device *asd, int flag,
atomisp_curr_user_grid_info(asd, &config->info);
- /* We always return the resolution and stride even if there is
+ /*
+ * We always return the resolution and stride even if there is
* no valid metadata. This allows the caller to get the
- * information needed to allocate user-space buffers. */
+ * information needed to allocate user-space buffers.
+ */
config->metadata_config.metadata_height = asd->
stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream_info.
metadata_info.resolution.height;
@@ -3232,7 +3236,7 @@ int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag,
}
/*
- * Function to enable/disable video image stablization
+ * Function to enable/disable video image stabilization
*/
int atomisp_video_stable(struct atomisp_sub_device *asd, int flag,
__s32 *value)
@@ -3277,8 +3281,10 @@ atomisp_bytesperline_to_padded_width(unsigned int bytesperline,
return bytesperline / 2;
case IA_CSS_FRAME_FORMAT_RGBA888:
return bytesperline / 4;
- /* The following cases could be removed, but we leave them
- in to document the formats that are included. */
+ /*
+ * The following cases could be removed, but we leave them
+ * in to document the formats that are included.
+ */
case IA_CSS_FRAME_FORMAT_NV11:
case IA_CSS_FRAME_FORMAT_NV12:
case IA_CSS_FRAME_FORMAT_NV16:
@@ -3314,9 +3320,11 @@ atomisp_v4l2_framebuffer_to_css_frame(const struct v4l2_framebuffer *arg,
padded_width = atomisp_bytesperline_to_padded_width(
arg->fmt.bytesperline, sh_format);
- /* Note: the padded width on an ia_css_frame is in elements, not in
- bytes. The RAW frame we use here should always be a 16bit RAW
- frame. This is why we bytesperline/2 is equal to the padded with */
+ /*
+ * Note: the padded width on an ia_css_frame is in elements, not in
+ * bytes. The RAW frame we use here should always be a 16bit RAW
+ * frame. This is why we bytesperline/2 is equal to the padded with
+ */
if (ia_css_frame_allocate(&res, arg->fmt.width, arg->fmt.height,
sh_format, padded_width, 0)) {
ret = -ENOMEM;
@@ -3364,10 +3372,8 @@ int atomisp_fixed_pattern_table(struct atomisp_sub_device *asd,
if (ret)
return ret;
- if (sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
- raw_black_frame) != 0)
- return -ENOMEM;
-
+ ret = sh_css_set_black_frame(asd->stream_env[ATOMISP_INPUT_STREAM_GENERAL].stream,
+ raw_black_frame);
ia_css_frame_free(raw_black_frame);
return ret;
}
@@ -3925,8 +3931,10 @@ static inline int atomisp_set_sensor_mipi_to_isp(
asd->stream_env[stream_id].isys_info[1].height);
}
- /* Compatibility for sensors which provide no media bus code
- * in s_mbus_framefmt() nor support pad formats. */
+ /*
+ * Compatibility for sensors which provide no media bus code
+ * in s_mbus_framefmt() nor support pad formats.
+ */
if (mipi_info && mipi_info->input_format != -1) {
bayer_order = mipi_info->raw_bayer_order;
@@ -4003,7 +4011,7 @@ static int css_input_resolution_changed(struct atomisp_sub_device *asd,
struct atomisp_metadata_buf *md_buf = NULL, *_md_buf;
unsigned int i;
- dev_dbg(asd->isp->dev, "css_input_resolution_changed to %ux%u\n",
+ dev_dbg(asd->isp->dev, "%s: to %ux%u\n", __func__,
ffmt->width, ffmt->height);
if (IS_ISP2401)
@@ -4384,8 +4392,10 @@ int atomisp_set_fmt(struct video_device *vdev, struct v4l2_format *f)
ATOMISP_SUBDEV_PAD_SINK,
V4L2_SEL_TGT_CROP);
- /* Try to enable YUV downscaling if ISP input is 10 % (either
- * width or height) bigger than the desired result. */
+ /*
+ * Try to enable YUV downscaling if ISP input is 10 % (either
+ * width or height) bigger than the desired result.
+ */
if (!IS_MOFD ||
isp_sink_crop.width * 9 / 10 < f->fmt.pix.width ||
isp_sink_crop.height * 9 / 10 < f->fmt.pix.height ||
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.h b/drivers/staging/media/atomisp/pci/atomisp_cmd.h
index 82199dc9284e..d3d1f2574e77 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.h
@@ -153,7 +153,7 @@ int atomisp_bad_pixel(struct atomisp_sub_device *asd, int flag,
int atomisp_bad_pixel_param(struct atomisp_sub_device *asd, int flag,
struct atomisp_dp_config *config);
-/* Function to enable/disable video image stablization */
+/* Function to enable/disable video image stabilization */
int atomisp_video_stable(struct atomisp_sub_device *asd, int flag,
__s32 *value);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
index be5f37f4a6fd..0ee52637ea30 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.c
@@ -27,6 +27,7 @@
#include <linux/io.h>
#include <linux/pm_runtime.h>
+#include <linux/string_choices.h>
/* Assume max number of ACC stages */
#define MAX_ACC_STAGES 20
@@ -841,7 +842,7 @@ int atomisp_css_irq_enable(struct atomisp_device *isp,
{
dev_dbg(isp->dev, "%s: css irq info 0x%08x: %s (%d).\n",
__func__, info,
- enable ? "enable" : "disable", enable);
+ str_enable_disable(enable), enable);
if (ia_css_irq_enable(info, enable)) {
dev_warn(isp->dev, "%s:Invalid irq info: 0x%08x when %s.\n",
__func__, info,
@@ -1116,8 +1117,11 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd,
dvs_grid_info);
if (!dis_buf->dis_data) {
dev_err(isp->dev, "dvs buf allocation failed.\n");
- if (s3a_buf)
+ if (s3a_buf) {
+ hmm_vunmap(s3a_buf->s3a_data->data_ptr);
+ ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map);
ia_css_isp_3a_statistics_free(s3a_buf->s3a_data);
+ }
return -EINVAL;
}
@@ -1131,10 +1135,16 @@ int atomisp_css_allocate_stat_buffers(struct atomisp_sub_device *asd,
md_buf->metadata = ia_css_metadata_allocate(
&asd->stream_env[stream_id].stream_info.metadata_info);
if (!md_buf->metadata) {
- if (s3a_buf)
+ if (s3a_buf) {
+ hmm_vunmap(s3a_buf->s3a_data->data_ptr);
+ ia_css_isp_3a_statistics_map_free(s3a_buf->s3a_map);
ia_css_isp_3a_statistics_free(s3a_buf->s3a_data);
- if (dis_buf)
+ }
+ if (dis_buf) {
+ hmm_vunmap(dis_buf->dis_data->data_ptr);
+ ia_css_isp_dvs_statistics_map_free(dis_buf->dvs_map);
ia_css_isp_dvs2_statistics_free(dis_buf->dis_data);
+ }
dev_err(isp->dev, "metadata buf allocation failed.\n");
return -EINVAL;
}
@@ -1957,7 +1967,7 @@ static void __configure_capture_pp_input(struct atomisp_sub_device *asd,
/*
* For CSS2.1, preview pipe could support bayer downscaling, yuv decimation and
- * yuv downscaling, which needs addtional configurations.
+ * yuv downscaling, which needs additional configurations.
*/
static void __configure_preview_pp_input(struct atomisp_sub_device *asd,
unsigned int width, unsigned int height,
@@ -2044,7 +2054,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd,
}
}
/*
- * calculate YUV Decimation, YUV downscaling facor:
+ * calculate YUV Decimation, YUV downscaling factor:
* YUV Downscaling factor must not exceed 2.
* YUV Decimation factor could be 2, 4.
*/
@@ -2085,7 +2095,7 @@ static void __configure_preview_pp_input(struct atomisp_sub_device *asd,
/*
* For CSS2.1, offline video pipe could support bayer decimation, and
- * yuv downscaling, which needs addtional configurations.
+ * yuv downscaling, which needs additional configurations.
*/
static void __configure_video_pp_input(struct atomisp_sub_device *asd,
unsigned int width, unsigned int height,
@@ -3002,7 +3012,7 @@ int atomisp_css_get_zoom_factor(struct atomisp_sub_device *asd,
}
/*
- * Function to set/get image stablization statistics
+ * Function to set/get image stabilization statistics
*/
int atomisp_css_get_dis_stat(struct atomisp_sub_device *asd,
struct atomisp_dis_statistics *stats)
diff --git a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h
index 75781807544a..5188df4f469c 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_compat_css20.h
@@ -40,7 +40,7 @@ enum atomisp_css_stream_state {
};
/*
- * Sensor of external ISP can send multiple steams with different mipi data
+ * Sensor of external ISP can send multiple streams with different mipi data
* type in the same virtual channel. This information needs to come from the
* sensor or external ISP
*/
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
index ba61cc28fac1..cca91c6d71a5 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2_bridge.c
@@ -113,7 +113,7 @@ static char *gmin_cfg_get_dsm(struct acpi_device *adev, const char *key)
if (!obj)
return NULL;
- for (i = 0; i < obj->package.count - 1; i += 2) {
+ for (i = 0; i + 1 < obj->package.count; i += 2) {
key_el = &obj->package.elements[i + 0];
val_el = &obj->package.elements[i + 1];
diff --git a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
index 4026e98c5845..a1d3a16921ea 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_gmin_platform.c
@@ -11,8 +11,8 @@
#include <linux/mfd/intel_soc_pmic.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
-#include <linux/gpio.h>
#include <linux/platform_device.h>
+#include <linux/string_choices.h>
#include "../../include/linux/atomisp_platform.h"
#include "../../include/linux/atomisp_gmin_platform.h"
@@ -917,7 +917,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on)
return 0;
dev_dbg(subdev->dev, "Setting power state to %s\n",
- on ? "on" : "off");
+ str_on_off(on));
if (on)
ret = acpi_device_set_power(adev,
@@ -930,7 +930,7 @@ static int gmin_acpi_pm_ctrl(struct v4l2_subdev *subdev, int on)
gs->clock_on = on;
else
dev_err(subdev->dev, "Couldn't set power state to %s\n",
- on ? "on" : "off");
+ str_on_off(on));
return ret;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
index 3d56ca83ecb7..9de9cd884d99 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
@@ -180,8 +180,8 @@ static int isp_subdev_enum_mbus_code(struct v4l2_subdev *sd,
return 0;
}
-static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad,
- uint32_t target)
+static int isp_subdev_validate_rect(struct v4l2_subdev *sd, u32 pad,
+ u32 target)
{
switch (pad) {
case ATOMISP_SUBDEV_PAD_SINK:
@@ -203,8 +203,8 @@ static int isp_subdev_validate_rect(struct v4l2_subdev *sd, uint32_t pad,
struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
- u32 which, uint32_t pad,
- uint32_t target)
+ u32 which, u32 pad,
+ u32 target)
{
struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
@@ -229,8 +229,8 @@ struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt
*atomisp_subdev_get_ffmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state, uint32_t which,
- uint32_t pad)
+ struct v4l2_subdev_state *sd_state, u32 which,
+ u32 pad)
{
struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
@@ -242,7 +242,7 @@ struct v4l2_mbus_framefmt
static void isp_get_fmt_rect(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
- uint32_t which,
+ u32 which,
struct v4l2_mbus_framefmt **ffmt,
struct v4l2_rect *crop[ATOMISP_SUBDEV_PADS_NUM],
struct v4l2_rect *comp[ATOMISP_SUBDEV_PADS_NUM])
@@ -291,7 +291,7 @@ static const char *atomisp_pad_str(unsigned int pad)
int atomisp_subdev_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
- u32 which, uint32_t pad, uint32_t target,
+ u32 which, u32 pad, u32 target,
u32 flags, struct v4l2_rect *r)
{
struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
@@ -467,7 +467,7 @@ static int isp_subdev_set_selection(struct v4l2_subdev *sd,
void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
- uint32_t which,
+ u32 which,
u32 pad, struct v4l2_mbus_framefmt *ffmt)
{
struct atomisp_sub_device *isp_sd = v4l2_get_subdevdata(sd);
@@ -808,7 +808,7 @@ static int isp_subdev_init_entities(struct atomisp_sub_device *asd)
int ret;
v4l2_subdev_init(sd, &isp_subdev_v4l2_ops);
- sprintf(sd->name, "Atom ISP");
+ strscpy(sd->name, "Atom ISP");
v4l2_set_subdevdata(sd, asd);
sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE;
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.h b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
index e1d0168cb91d..b12bb65be3f2 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.h
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.h
@@ -315,20 +315,20 @@ bool atomisp_subdev_format_conversion(struct atomisp_sub_device *asd);
/* Get pointer to appropriate format */
struct v4l2_mbus_framefmt
*atomisp_subdev_get_ffmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_state *sd_state, uint32_t which,
- uint32_t pad);
+ struct v4l2_subdev_state *sd_state, u32 which,
+ u32 pad);
struct v4l2_rect *atomisp_subdev_get_rect(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
- u32 which, uint32_t pad,
- uint32_t target);
+ u32 which, u32 pad,
+ u32 target);
int atomisp_subdev_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
- u32 which, uint32_t pad, uint32_t target,
+ u32 which, u32 pad, u32 target,
u32 flags, struct v4l2_rect *r);
/* Actually set the format */
void atomisp_subdev_set_ffmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
- uint32_t which,
+ u32 which,
u32 pad, struct v4l2_mbus_framefmt *ffmt);
void atomisp_subdev_cleanup_pending_events(struct atomisp_sub_device *asd);
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 900a67552d6a..812230397409 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -16,6 +16,7 @@
#include <linux/dmi.h>
#include <linux/interrupt.h>
#include <linux/bits.h>
+#include <linux/string_choices.h>
#include <media/v4l2-fwnode.h>
#include <asm/iosf_mbi.h>
@@ -527,7 +528,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable)
u32 val = enable ? MRFLD_ISPSSPM0_IUNIT_POWER_ON :
MRFLD_ISPSSPM0_IUNIT_POWER_OFF;
- dev_dbg(isp->dev, "IUNIT power-%s.\n", enable ? "on" : "off");
+ dev_dbg(isp->dev, "IUNIT power-%s.\n", str_on_off(enable));
/* WA for P-Unit, if DVFS enabled, ISP timeout observed */
if (IS_CHT && enable && !isp->pm_only) {
@@ -569,7 +570,7 @@ static int atomisp_mrfld_power(struct atomisp_device *isp, bool enable)
usleep_range(100, 150);
} while (1);
- dev_err(isp->dev, "IUNIT power-%s timeout.\n", enable ? "on" : "off");
+ dev_err(isp->dev, "IUNIT power-%s timeout.\n", str_on_off(enable));
return -EBUSY;
}
diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h
index 86300991d30e..4732b45b25ee 100644
--- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h
+++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf.h
@@ -139,21 +139,16 @@ static inline uint8_t ia_css_circbuf_get_pos_at_offset(
u32 base,
int offset)
{
- u8 dest;
-
OP___assert(cb);
OP___assert(cb->desc);
OP___assert(cb->desc->size > 0);
/* step 1: adjudst the offset */
- while (offset < 0) {
+ while (offset < 0)
offset += cb->desc->size;
- }
/* step 2: shift and round by the upper limit */
- dest = OP_std_modadd(base, offset, cb->desc->size);
-
- return dest;
+ return (base + offset) % cb->desc->size;
}
/**
diff --git a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h
index 5645a7bf493c..64f754f1d49b 100644
--- a/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h
+++ b/drivers/staging/media/atomisp/pci/base/circbuf/interface/ia_css_circbuf_desc.h
@@ -47,7 +47,7 @@ static inline bool ia_css_circbuf_desc_is_full(
ia_css_circbuf_desc_t *cb_desc)
{
OP___assert(cb_desc);
- return (OP_std_modadd(cb_desc->end, 1, cb_desc->size) == cb_desc->start);
+ return ((cb_desc->end + 1) % cb_desc->size) == cb_desc->start;
}
/**
@@ -78,20 +78,15 @@ static inline uint8_t ia_css_circbuf_desc_get_pos_at_offset(
u32 base,
int offset)
{
- u8 dest;
-
OP___assert(cb_desc);
OP___assert(cb_desc->size > 0);
/* step 1: adjust the offset */
- while (offset < 0) {
+ while (offset < 0)
offset += cb_desc->size;
- }
/* step 2: shift and round by the upper limit */
- dest = OP_std_modadd(base, offset, cb_desc->size);
-
- return dest;
+ return (base + offset) % cb_desc->size;
}
/**
diff --git a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c
index 58e4e3173b40..4a8675d0129a 100644
--- a/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c
+++ b/drivers/staging/media/atomisp/pci/base/refcount/src/refcount.c
@@ -238,9 +238,8 @@ void ia_css_refcount_clear(s32 id, clear_func clear_func_ptr)
hmm_free(entry->data);
}
- if (entry->count != 0) {
+ if (entry->count != 0)
IA_CSS_WARNING("Ref count for entry %x is not zero!", entry->id);
- }
assert(entry->count == 0);
diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c
index a9f736398f50..fa2f8ed5b053 100644
--- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c
+++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_stagedesc.c
@@ -32,9 +32,8 @@ void ia_css_pipe_get_generic_stage_desc(
stage_desc->max_input_width = 0;
stage_desc->mode = binary->info->sp.pipeline.mode;
stage_desc->in_frame = in_frame;
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
stage_desc->out_frame[i] = out_frame[i];
- }
stage_desc->vf_frame = vf_frame;
ERR:
IA_CSS_LEAVE_PRIVATE("");
@@ -59,9 +58,8 @@ void ia_css_pipe_get_firmwares_stage_desc(
stage_desc->max_input_width = 0;
stage_desc->mode = mode;
stage_desc->in_frame = in_frame;
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
stage_desc->out_frame[i] = out_frame[i];
- }
stage_desc->vf_frame = vf_frame;
}
@@ -82,8 +80,7 @@ void ia_css_pipe_get_sp_func_stage_desc(
stage_desc->mode = (unsigned int)-1;
stage_desc->in_frame = NULL;
stage_desc->out_frame[0] = out_frame;
- for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
+ for (i = 1; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
stage_desc->out_frame[i] = NULL;
- }
stage_desc->vf_frame = NULL;
}
diff --git a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c
index c7c42b472cc7..6cb3ecbd7297 100644
--- a/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c
+++ b/drivers/staging/media/atomisp/pci/camera/pipe/src/pipe_util.c
@@ -26,9 +26,8 @@ void ia_css_pipe_util_create_output_frames(
unsigned int i;
assert(frames);
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
frames[i] = NULL;
- }
}
void ia_css_pipe_util_set_output_frames(
diff --git a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h
index 3210dd6bf9ca..8e295cd78129 100644
--- a/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h
+++ b/drivers/staging/media/atomisp/pci/css_2401_system/host/isys_stream2mmio_private.h
@@ -41,9 +41,8 @@ STORAGE_CLASS_STREAM2MMIO_C void stream2mmio_get_state(
* Get the values of the register-set per
* stream2mmio-controller sids.
*/
- for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++) {
+ for (i = STREAM2MMIO_SID0_ID; i < N_STREAM2MMIO_SID_PROCS[ID]; i++)
stream2mmio_get_sid_state(ID, i, &state->sid_state[i]);
- }
}
/**
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h
index 439c69444942..e77d7cf61356 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/event_fifo_private.h
@@ -26,9 +26,8 @@ STORAGE_CLASS_EVENT_C void event_wait_for(const event_ID_t ID)
STORAGE_CLASS_EVENT_C void cnd_event_wait_for(const event_ID_t ID,
const bool cnd)
{
- if (cnd) {
+ if (cnd)
event_wait_for(ID);
- }
}
STORAGE_CLASS_EVENT_C hrt_data event_receive_token(const event_ID_t ID)
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c
index 8bb78b4d7c67..b31e3809c0e4 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/gdc.c
@@ -26,9 +26,7 @@ static inline void gdc_reg_store(
/*
* Exported function implementations
*/
-void gdc_lut_store(
- const gdc_ID_t ID,
- const int data[4][HRT_GDC_N])
+void gdc_lut_store(const gdc_ID_t ID, const int data[4][HRT_GDC_N])
{
unsigned int i, lut_offset = HRT_GDC_LUT_IDX;
@@ -36,20 +34,17 @@ void gdc_lut_store(
assert(HRT_GDC_LUT_COEFF_OFFSET <= (4 * sizeof(hrt_data)));
for (i = 0; i < HRT_GDC_N; i++) {
- hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK;
- hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK;
- hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK;
- hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK;
+ hrt_data entry_0 = data[0][i] & HRT_GDC_BCI_COEF_MASK;
+ hrt_data entry_1 = data[1][i] & HRT_GDC_BCI_COEF_MASK;
+ hrt_data entry_2 = data[2][i] & HRT_GDC_BCI_COEF_MASK;
+ hrt_data entry_3 = data[3][i] & HRT_GDC_BCI_COEF_MASK;
- hrt_data word_0 = entry_0 |
- (entry_1 << HRT_GDC_LUT_COEFF_OFFSET);
- hrt_data word_1 = entry_2 |
- (entry_3 << HRT_GDC_LUT_COEFF_OFFSET);
+ hrt_data word_0 = entry_0 | (entry_1 << HRT_GDC_LUT_COEFF_OFFSET);
+ hrt_data word_1 = entry_2 | (entry_3 << HRT_GDC_LUT_COEFF_OFFSET);
gdc_reg_store(ID, lut_offset++, word_0);
gdc_reg_store(ID, lut_offset++, word_1);
}
- return;
}
/*
@@ -85,8 +80,7 @@ void gdc_lut_convert_to_isp_format(const int in_lut[4][HRT_GDC_N],
}
}
-int gdc_get_unity(
- const gdc_ID_t ID)
+int gdc_get_unity(const gdc_ID_t ID)
{
assert(ID < N_GDC_ID);
(void)ID;
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c
index 40b3f1e48c56..c353461dcdf2 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_formatter.c
@@ -62,9 +62,8 @@ void input_formatter_rst(
* WICH USES THE STREAM2MEMRY BLOCK.
* MUST BE FIXED PROPERLY
*/
- if (!HIVE_IF_BIN_COPY[ID]) {
+ if (!HIVE_IF_BIN_COPY[ID])
input_formatter_reg_store(ID, addr, rst);
- }
return;
}
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c
index 9f1199c4761c..68b0ad27615d 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/input_system.c
@@ -138,11 +138,10 @@ void receiver_port_enable(
hrt_data reg = receiver_port_reg_load(ID, port_ID,
_HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX);
- if (cnd) {
+ if (cnd)
reg |= 0x01;
- } else {
+ else
reg &= ~0x01;
- }
receiver_port_reg_store(ID, port_ID,
_HRT_CSS_RECEIVER_DEVICE_READY_REG_IDX, reg);
@@ -194,9 +193,8 @@ static void receiver_rst(
assert(ID < N_RX_ID);
// Disable all ports.
- for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++) {
+ for (port_id = MIPI_PORT0_ID; port_id < N_MIPI_PORT_ID; port_id++)
receiver_port_enable(ID, port_id, false);
- }
// AM: Additional actions for stopping receiver?
}
@@ -830,15 +828,13 @@ input_system_err_t input_system_configuration_commit(void)
// The last configuration step is to configure the input buffer.
input_system_err_t error = input_buffer_configuration();
- if (error != INPUT_SYSTEM_ERR_NO_ERROR) {
+ if (error != INPUT_SYSTEM_ERR_NO_ERROR)
return error;
- }
// Translate the whole configuration into registers.
error = configuration_to_registers();
- if (error != INPUT_SYSTEM_ERR_NO_ERROR) {
+ if (error != INPUT_SYSTEM_ERR_NO_ERROR)
return error;
- }
// Translate the whole configuration into ctrl commands etc.
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c
index b66560bca625..61dea386361a 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/irq.c
@@ -55,9 +55,8 @@ void irq_clear_all(
assert(ID < N_IRQ_ID);
assert(IRQ_N_CHANNEL[ID] <= HRT_DATA_WIDTH);
- if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH) {
+ if (IRQ_N_CHANNEL[ID] < HRT_DATA_WIDTH)
mask = ~((~(hrt_data)0) >> IRQ_N_CHANNEL[ID]);
- }
irq_reg_store(ID,
_HRT_IRQ_CONTROLLER_CLEAR_REG_IDX, mask);
@@ -115,9 +114,9 @@ void irq_enable_pulse(
{
unsigned int edge_out = 0x0;
- if (pulse) {
+ if (pulse)
edge_out = 0xffffffff;
- }
+
/* output is given as edge, not pulse */
irq_reg_store(ID,
_HRT_IRQ_CONTROLLER_EDGE_NOT_PULSE_REG_IDX, edge_out);
@@ -259,9 +258,9 @@ void virq_clear_all(void)
{
irq_ID_t irq_id;
- for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++) {
+ for (irq_id = (irq_ID_t)0; irq_id < N_IRQ_ID; irq_id++)
irq_clear_all(irq_id);
- }
+
return;
}
@@ -301,9 +300,9 @@ void virq_clear_info(struct virq_info *irq_info)
assert(irq_info);
- for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) {
+ for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++)
irq_info->irq_status_reg[ID] = 0;
- }
+
return;
}
@@ -324,20 +323,17 @@ enum hrt_isp_css_irq_status virq_get_channel_id(
break;
}
- if (idx == IRQ_N_CHANNEL[IRQ0_ID]) {
+ if (idx == IRQ_N_CHANNEL[IRQ0_ID])
return hrt_isp_css_irq_status_error;
- }
/* Check whether there are more bits set on device 0 */
- if (irq_status != (1U << idx)) {
+ if (irq_status != (1U << idx))
status = hrt_isp_css_irq_status_more_irqs;
- }
/* Check whether we have an IRQ on one of the nested devices */
for (ID = N_IRQ_ID - 1 ; ID > (irq_ID_t)0; ID--) {
- if (IRQ_NESTING_ID[ID] == (enum virq_id)idx) {
+ if (IRQ_NESTING_ID[ID] == (enum virq_id)idx)
break;
- }
}
/* If we have a nested IRQ, load that state, discard the device 0 state */
@@ -350,9 +346,8 @@ enum hrt_isp_css_irq_status virq_get_channel_id(
break;
}
- if (idx == IRQ_N_CHANNEL[ID]) {
+ if (idx == IRQ_N_CHANNEL[ID])
return hrt_isp_css_irq_status_error;
- }
/* Alternatively check whether there are more bits set on this device */
if (irq_status != (1U << idx)) {
@@ -408,9 +403,8 @@ static inline irq_ID_t virq_get_irq_id(
assert(channel_ID);
for (ID = (irq_ID_t)0 ; ID < N_IRQ_ID; ID++) {
- if (irq_ID < IRQ_N_ID_OFFSET[ID + 1]) {
+ if (irq_ID < IRQ_N_ID_OFFSET[ID + 1])
break;
- }
}
*channel_ID = (unsigned int)irq_ID - IRQ_N_ID_OFFSET[ID];
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c
index 064e88a5e064..70d118fe1e70 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/mmu.c
@@ -32,7 +32,6 @@ void mmu_invalidate_cache_all(void)
{
mmu_ID_t mmu_id;
- for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++) {
+ for (mmu_id = (mmu_ID_t)0; mmu_id < N_MMU_ID; mmu_id++)
mmu_invalidate_cache(mmu_id);
- }
}
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c
index 722b684fbc37..1c5c50406633 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_common/host/vmem.c
@@ -168,9 +168,9 @@ static void store_vector(
//load_vector (&v[1][0], &to[ISP_NWAY]); /* Fetch the next vector, since it will be overwritten. */
hive_uedge *data = (hive_uedge *)v;
- for (i = 0; i < ISP_NWAY; i++) {
+ for (i = 0; i < ISP_NWAY; i++)
hive_sim_wide_pack(data, (hive_wide)&from[i], ISP_VEC_ELEMBITS, i);
- }
+
assert(ISP_BAMEM_BASE[ID] != (hrt_address) - 1);
#if !defined(HRT_MEMORY_ACCESS)
ia_css_device_store(ISP_BAMEM_BASE[ID] + (unsigned long)to, &v, size);
diff --git a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h
index 1a435a348318..b321f4101193 100644
--- a/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h
+++ b/drivers/staging/media/atomisp/pci/hive_isp_css_include/host/mmu_public.h
@@ -11,72 +11,61 @@
#include "device_access.h"
#include "assert_support.h"
-/*! Set the page table base index of MMU[ID]
-
- \param ID[in] MMU identifier
- \param base_index[in] page table base index
-
- \return none, MMU[ID].page_table_base_index = base_index
+/**
+ * mmu_set_page_table_base_index() - Set the page table base index of MMU[ID]
+ * @ID: MMU identifier
+ * @base_index: page table base index
+ *
+ * Return: none, MMU[ID].page_table_base_index = base_index
*/
-void mmu_set_page_table_base_index(
- const mmu_ID_t ID,
- const hrt_data base_index);
-
-/*! Get the page table base index of MMU[ID]
+void mmu_set_page_table_base_index(const mmu_ID_t ID, const hrt_data base_index);
- \param ID[in] MMU identifier
- \param base_index[in] page table base index
-
- \return MMU[ID].page_table_base_index
+/**
+ * mmu_get_page_table_base_index() - Get the page table base index of MMU[ID]
+ * @ID: MMU identifier
+ *
+ * Return: MMU[ID].page_table_base_index
*/
-hrt_data mmu_get_page_table_base_index(
- const mmu_ID_t ID);
-
-/*! Invalidate the page table cache of MMU[ID]
-
- \param ID[in] MMU identifier
+hrt_data mmu_get_page_table_base_index(const mmu_ID_t ID);
- \return none
+/**
+ * mmu_invalidate_cache() - nvalidate the page table cache of MMU[ID]
+ * @ID: MMU identifier
+ *
+ * Return: none
*/
-void mmu_invalidate_cache(
- const mmu_ID_t ID);
+void mmu_invalidate_cache(const mmu_ID_t ID);
-/*! Invalidate the page table cache of all MMUs
-
- \return none
+/**
+ * mmu_invalidate_cache_all() - Invalidate the page table cache of all MMUs
+ *
+ * Return: none
*/
void mmu_invalidate_cache_all(void);
-/*! Write to a control register of MMU[ID]
-
- \param ID[in] MMU identifier
- \param reg[in] register index
- \param value[in] The data to be written
-
- \return none, MMU[ID].ctrl[reg] = value
+/**
+ * mmu_reg_store() - Write to a control register of MMU[ID]
+ * @ID: MMU identifier
+ * @reg: register index
+ * @value: The data to be written
+ *
+ * Return: none, MMU[ID].ctrl[reg] = value
*/
-static inline void mmu_reg_store(
- const mmu_ID_t ID,
- const unsigned int reg,
- const hrt_data value)
+static inline void mmu_reg_store(const mmu_ID_t ID, const unsigned int reg, const hrt_data value)
{
assert(ID < N_MMU_ID);
assert(MMU_BASE[ID] != (hrt_address) - 1);
ia_css_device_store_uint32(MMU_BASE[ID] + reg * sizeof(hrt_data), value);
- return;
}
-/*! Read from a control register of MMU[ID]
-
- \param ID[in] MMU identifier
- \param reg[in] register index
- \param value[in] The data to be written
-
- \return MMU[ID].ctrl[reg]
+/**
+ * mmu_reg_load() - Read from a control register of MMU[ID]
+ * @ID: MMU identifier
+ * @reg: register index
+ *
+ * Return: MMU[ID].ctrl[reg]
*/
-static inline hrt_data mmu_reg_load(
- const mmu_ID_t ID,
- const unsigned int reg)
+static inline hrt_data mmu_reg_load(const mmu_ID_t ID, const unsigned int reg)
{
assert(ID < N_MMU_ID);
assert(MMU_BASE[ID] != (hrt_address) - 1);
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 2cb5c986790a..72a070d94736 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
@@ -14,10 +14,4 @@
#define CEIL_MUL(a, b) (CEIL_DIV(a, b) * (b))
#define CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b))
-/*
- * For SP and ISP, SDK provides the definition of OP_std_modadd.
- * We need it only for host
- */
-#define OP_std_modadd(base, offset, size) ((base + offset) % (size))
-
#endif /* __MATH_SUPPORT_H */
diff --git a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
index 856561e951a5..0a8f401a1ca1 100644
--- a/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
+++ b/drivers/staging/media/atomisp/pci/hmm/hmm_bo.c
@@ -620,14 +620,23 @@ static void free_private_bo_pages(struct hmm_buffer_object *bo)
/*Allocate pages which will be used only by ISP*/
static int alloc_private_pages(struct hmm_buffer_object *bo)
{
- const gfp_t gfp = __GFP_NOWARN | __GFP_RECLAIM | __GFP_FS;
+ unsigned int nr_allocated = 0;
+ struct page *page;
int ret;
- ret = alloc_pages_bulk(gfp, bo->pgnr, bo->pages);
- if (ret != bo->pgnr) {
- free_pages_bulk_array(ret, bo->pages);
- dev_err(atomisp_dev, "alloc_pages_bulk() failed\n");
- return -ENOMEM;
+ nr_allocated = alloc_pages_bulk(GFP_KERNEL, bo->pgnr, bo->pages);
+ /*
+ * alloc_pages_bulk() does not try very hard to get pages under memory
+ * pressure. If necessary fall back to alloc_page().
+ */
+ while (nr_allocated < bo->pgnr) {
+ page = alloc_pages(GFP_KERNEL, 0);
+ if (!page) {
+ free_pages_bulk_array(nr_allocated, bo->pages);
+ return -ENOMEM;
+ }
+ bo->pages[nr_allocated] = page;
+ nr_allocated++;
}
ret = set_pages_array_uc(bo->pages, bo->pgnr);
@@ -975,8 +984,7 @@ void hmm_bo_unref(struct hmm_buffer_object *bo)
static void hmm_bo_vm_open(struct vm_area_struct *vma)
{
- struct hmm_buffer_object *bo =
- (struct hmm_buffer_object *)vma->vm_private_data;
+ struct hmm_buffer_object *bo = vma->vm_private_data;
check_bo_null_return_void(bo);
@@ -993,8 +1001,7 @@ static void hmm_bo_vm_open(struct vm_area_struct *vma)
static void hmm_bo_vm_close(struct vm_area_struct *vma)
{
- struct hmm_buffer_object *bo =
- (struct hmm_buffer_object *)vma->vm_private_data;
+ struct hmm_buffer_object *bo = vma->vm_private_data;
check_bo_null_return_void(bo);
diff --git a/drivers/staging/media/atomisp/pci/ia_css_types.h b/drivers/staging/media/atomisp/pci/ia_css_types.h
index 676d7e20b282..2b7db9cda23a 100644
--- a/drivers/staging/media/atomisp/pci/ia_css_types.h
+++ b/drivers/staging/media/atomisp/pci/ia_css_types.h
@@ -15,6 +15,8 @@
* directly but still need to forward parameters for it.
*/
+#include <linux/videodev2.h>
+
#include <type_support.h>
#include "ia_css_frac.h"
@@ -428,21 +430,13 @@ struct ia_css_point {
};
/**
- * This specifies the region
- */
-struct ia_css_region {
- struct ia_css_point origin; /** Starting point coordinates for the region */
- struct ia_css_resolution resolution; /** Region resolution */
-};
-
-/**
* Digital zoom:
* This feature is currently available only for video, but will become
* available for preview and capture as well.
* Set the digital zoom factor, this is a logarithmic scale. The actual zoom
* factor will be 64/x.
* Setting dx or dy to 0 disables digital zoom for that direction.
- * New API change for Digital zoom:(added struct ia_css_region zoom_region)
+ *
* zoom_region specifies the origin of the zoom region and width and
* height of that region.
* origin : This is the coordinate (x,y) within the effective input resolution
@@ -455,7 +449,7 @@ struct ia_css_region {
struct ia_css_dz_config {
u32 dx; /** Horizontal zoom factor */
u32 dy; /** Vertical zoom factor */
- struct ia_css_region zoom_region; /** region for zoom */
+ struct v4l2_rect zoom_region; /** region for zoom */
};
/* The still capture mode, this can be RAW (simply copy sensor input to DDR),
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h
index 2f568a7062da..f825f537a536 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/aa/aa_2/ia_css_aa2_types.h
@@ -28,11 +28,13 @@
* ISP block: BAA2
* ISP1: BAA2 is used.
* ISP2: BAA2 is used.
+ *
+ * @strength: Strength of the filter, in u0.13 fixed-point format.
+ * Valid range: [0, 8191]. A value of 0 means the filter is
+ * ineffective (default).
*/
struct ia_css_aa_config {
- u16 strength; /** Strength of the filter.
- u0.13, [0,8191],
- default/ineffective 0 */
+ u16 strength;
};
#endif /* __IA_CSS_AA2_TYPES_H */
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c
index 899d566234b9..f4dd3ca03d75 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.c
@@ -11,30 +11,26 @@
#include "ia_css_anr.host.h"
const struct ia_css_anr_config default_anr_config = {
- 10,
- {
+ .threshold = 10,
+ .thresholds = {
0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4,
0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4,
0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4,
0, 3, 1, 2, 3, 6, 4, 5, 1, 4, 2, 3, 2, 5, 3, 4
},
- {10, 20, 30}
+ .factors = {10, 20, 30},
};
-void
-ia_css_anr_encode(
- struct sh_css_isp_anr_params *to,
- const struct ia_css_anr_config *from,
- unsigned int size)
+void ia_css_anr_encode(struct sh_css_isp_anr_params *to,
+ const struct ia_css_anr_config *from,
+ unsigned int size)
{
(void)size;
to->threshold = from->threshold;
}
-void
-ia_css_anr_dump(
- const struct sh_css_isp_anr_params *anr,
- unsigned int level)
+void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr,
+ unsigned int level)
{
if (!anr) return;
ia_css_debug_dtrace(level, "Advance Noise Reduction:\n");
@@ -42,10 +38,8 @@ ia_css_anr_dump(
"anr_threshold", anr->threshold);
}
-void
-ia_css_anr_debug_dtrace(
- const struct ia_css_anr_config *config,
- unsigned int level)
+void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config,
+ unsigned int level)
{
ia_css_debug_dtrace(level,
"config.threshold=%d\n",
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h
index 4f77900871c8..2f17d62b9208 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_1.0/ia_css_anr.host.h
@@ -12,20 +12,14 @@
extern const struct ia_css_anr_config default_anr_config;
-void
-ia_css_anr_encode(
- struct sh_css_isp_anr_params *to,
- const struct ia_css_anr_config *from,
- unsigned int size);
+void ia_css_anr_encode(struct sh_css_isp_anr_params *to,
+ const struct ia_css_anr_config *from,
+ unsigned int size);
-void
-ia_css_anr_dump(
- const struct sh_css_isp_anr_params *anr,
- unsigned int level);
+void ia_css_anr_dump(const struct sh_css_isp_anr_params *anr,
+ unsigned int level);
-void
-ia_css_anr_debug_dtrace(
- const struct ia_css_anr_config *config, unsigned int level)
-;
+void ia_css_anr_debug_dtrace(const struct ia_css_anr_config *config,
+ unsigned int level);
#endif /* __IA_CSS_ANR_HOST_H */
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c
index 09599884bdae..6ba34dc48ad4 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.c
@@ -10,11 +10,9 @@
#include "ia_css_anr2.host.h"
-void
-ia_css_anr2_vmem_encode(
- struct ia_css_isp_anr2_params *to,
- const struct ia_css_anr_thres *from,
- size_t size)
+void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to,
+ const struct ia_css_anr_thres *from,
+ size_t size)
{
unsigned int i;
@@ -22,16 +20,13 @@ ia_css_anr2_vmem_encode(
for (i = 0; i < ANR_PARAM_SIZE; i++) {
unsigned int j;
- for (j = 0; j < ISP_VEC_NELEMS; j++) {
+ for (j = 0; j < ISP_VEC_NELEMS; j++)
to->data[i][j] = from->data[i * ISP_VEC_NELEMS + j];
- }
}
}
-void
-ia_css_anr2_debug_dtrace(
- const struct ia_css_anr_thres *config,
- unsigned int level)
+void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config,
+ unsigned int level)
{
(void)config;
(void)level;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h
index 2b1105f21c1e..36fb6c259699 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/anr/anr_2/ia_css_anr2.host.h
@@ -13,15 +13,12 @@
#include "ia_css_anr2_param.h"
#include "ia_css_anr2_table.host.h"
-void
-ia_css_anr2_vmem_encode(
- struct ia_css_isp_anr2_params *to,
- const struct ia_css_anr_thres *from,
- size_t size);
+void ia_css_anr2_vmem_encode(struct ia_css_isp_anr2_params *to,
+ const struct ia_css_anr_thres *from,
+ size_t size);
-void
-ia_css_anr2_debug_dtrace(
- const struct ia_css_anr_thres *config, unsigned int level)
+void ia_css_anr2_debug_dtrace(const struct ia_css_anr_thres *config,
+ unsigned int level)
;
#endif /* __IA_CSS_ANR2_HOST_H */
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c
index 69c87e53f3c2..b87eb1a21b21 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.c
@@ -12,10 +12,8 @@
#include "ia_css_bh.host.h"
-void
-ia_css_bh_hmem_decode(
- struct ia_css_3a_rgby_output *out_ptr,
- const struct ia_css_bh_table *hmem_buf)
+void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr,
+ const struct ia_css_bh_table *hmem_buf)
{
int i;
@@ -37,11 +35,9 @@ ia_css_bh_hmem_decode(
}
}
-void
-ia_css_bh_encode(
- struct sh_css_isp_bh_params *to,
- const struct ia_css_3a_config *from,
- unsigned int size)
+void ia_css_bh_encode(struct sh_css_isp_bh_params *to,
+ const struct ia_css_3a_config *from,
+ unsigned int size)
{
(void)size;
/* coefficients to calculate Y */
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h
index 36b360cfe62e..964d658ceec3 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/bh/bh_2/ia_css_bh.host.h
@@ -10,15 +10,11 @@
#include "ia_css_bh_param.h"
#include "s3a/s3a_1.0/ia_css_s3a_types.h"
-void
-ia_css_bh_hmem_decode(
- struct ia_css_3a_rgby_output *out_ptr,
- const struct ia_css_bh_table *hmem_buf);
+void ia_css_bh_hmem_decode(struct ia_css_3a_rgby_output *out_ptr,
+ const struct ia_css_bh_table *hmem_buf);
-void
-ia_css_bh_encode(
- struct sh_css_isp_bh_params *to,
- const struct ia_css_3a_config *from,
- unsigned int size);
+void ia_css_bh_encode(struct sh_css_isp_bh_params *to,
+ const struct ia_css_3a_config *from,
+ unsigned int size);
#endif /* __IA_CSS_BH_HOST_H */
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c
index cd867937ee13..fcd7d7e2afe8 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/bnlm/ia_css_bnlm.host.c
@@ -125,14 +125,12 @@ ia_css_bnlm_vmem_encode(
bnlm_lut_encode(&to->div_lut, div_lut_nearests, div_lut_slopes,
BNLM_DIV_LUT_SIZE);
memset(to->div_lut_intercepts, 0, sizeof(to->div_lut_intercepts));
- for (i = 0; i < BNLM_DIV_LUT_SIZE; i++) {
+ for (i = 0; i < BNLM_DIV_LUT_SIZE; i++)
to->div_lut_intercepts[0][i] = div_lut_intercepts[i];
- }
memset(to->power_of_2, 0, sizeof(to->power_of_2));
- for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++) {
+ for (i = 0; i < (ISP_VEC_ELEMBITS - 1); i++)
to->power_of_2[0][i] = 1 << i;
- }
}
/* - Encodes BNLM public parameters into DMEM parameters */
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c
index 38751b8e9e6a..177487b6b237 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/ctc/ctc2/ia_css_ctc2.host.c
@@ -52,13 +52,12 @@ static int ctc2_slope(int y1, int y0, int x1, int x0)
/*the slope must lie within the range
(-max_slope-1) >= (dydx) >= (max_slope)
*/
- if (slope <= -max_slope - 1) {
+ if (slope <= -max_slope - 1)
dydx = -max_slope - 1;
- } else if (slope >= max_slope) {
+ else if (slope >= max_slope)
dydx = max_slope;
- } else {
+ else
dydx = slope;
- }
return dydx;
}
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 e9d6dd0bbfe2..cf6c29155758 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
@@ -258,12 +258,11 @@ convert_allocate_dvs_6axis_config(
return me;
}
-int
-store_dvs_6axis_config(
- const struct ia_css_dvs_6axis_config *dvs_6axis_config,
- const struct ia_css_binary *binary,
- const struct ia_css_frame_info *dvs_in_frame_info,
- ia_css_ptr ddr_addr_y) {
+int store_dvs_6axis_config(const struct ia_css_dvs_6axis_config *dvs_6axis_config,
+ const struct ia_css_binary *binary,
+ const struct ia_css_frame_info *dvs_in_frame_info,
+ ia_css_ptr ddr_addr_y)
+{
struct ia_css_host_data *me;
assert(dvs_6axis_config);
@@ -274,8 +273,7 @@ store_dvs_6axis_config(
binary,
dvs_in_frame_info);
- if (!me)
- {
+ if (!me) {
IA_CSS_LEAVE_ERR_PRIVATE(-ENOMEM);
return -ENOMEM;
}
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c
index 8e4451fcc8e3..76d76ca4401a 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/eed1_8/ia_css_eed1_8.host.c
@@ -140,17 +140,14 @@ ia_css_eed1_8_vmem_encode(
assert(tcinv_x[0] == 0);
assert(fcinv_x[0] == 0);
- for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++) {
+ for (j = 1; j < NUMBER_OF_CHGRINV_POINTS; j++)
assert(chgrinv_x[j] > chgrinv_x[j - 1]);
- }
- for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++) {
+ for (j = 1; j < NUMBER_OF_TCINV_POINTS; j++)
assert(tcinv_x[j] > tcinv_x[j - 1]);
- }
- for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++) {
+ for (j = 1; j < NUMBER_OF_FCINV_POINTS; j++)
assert(fcinv_x[j] > fcinv_x[j - 1]);
- }
/* The implementation of the calculating 1/x is based on the availability
* of the OP_vec_shuffle16 operation.
@@ -260,9 +257,9 @@ ia_css_eed1_8_encode(
to->margin_neg_diff = (from->neg_margin1 - from->neg_margin0);
/* Encode DEWEnhance exp (e_dew_enh_asr) */
- for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++) {
+ for (i = 0; i < (IA_CSS_NUMBER_OF_DEW_ENHANCE_SEGMENTS - 1); i++)
min_exp = max(min_exp, from->dew_enhance_seg_exp[i]);
- }
+
to->e_dew_enh_asr = 13 - clamp(min_exp, 0, 13);
to->dedgew_max = from->dedgew_max;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c
index a00f8d049a33..fb0e2a88cadb 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/raw/raw_1.0/ia_css_raw.host.c
@@ -21,10 +21,9 @@ static const struct ia_css_raw_configuration default_config = {
};
/* MW: These areMIPI / ISYS properties, not camera function properties */
-static enum sh_stream_format
-css2isp_stream_format(enum atomisp_input_format from) {
- switch (from)
- {
+static enum sh_stream_format css2isp_stream_format(enum atomisp_input_format from)
+{
+ switch (from) {
case ATOMISP_INPUT_FORMAT_YUV420_8_LEGACY:
return sh_stream_format_yuv420_legacy;
case ATOMISP_INPUT_FORMAT_YUV420_8:
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c
index 13678138c48c..823a1b0c0991 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a.host.c
@@ -240,9 +240,8 @@ ia_css_s3a_hmem_decode(
/* Calculate sum of histogram of R,
which should not be less than count_for_3a */
sum_r = 0;
- for (i = 0; i < HMEM_UNIT_SIZE; i++) {
+ for (i = 0; i < HMEM_UNIT_SIZE; i++)
sum_r += out_ptr[i].r;
- }
if (sum_r < count_for_3a) {
/* histogram is invalid */
return;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
index bd2def6c341a..0ca7ef5bb064 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_1.0/ia_css_sdis.host.c
@@ -50,9 +50,8 @@ void ia_css_sdis_horicoef_vmem_encode(
assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof(
short)) == 0);
- for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) {
+ for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++)
fill_row(&private[type * stride], &public[type * width], width, padding);
- }
}
void ia_css_sdis_vertcoef_vmem_encode(
@@ -77,9 +76,8 @@ void ia_css_sdis_vertcoef_vmem_encode(
assert(size % (IA_CSS_DVS_NUM_COEF_TYPES * ISP_VEC_NELEMS * sizeof(
short)) == 0);
- for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++) {
+ for (type = 0; type < IA_CSS_DVS_NUM_COEF_TYPES; type++)
fill_row(&private[type * stride], &public[type * height], height, padding);
- }
}
void ia_css_sdis_horiproj_encode(
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c
index 4a38d678f334..67860a5e4441 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/sdis/sdis_2/ia_css_sdis2.host.c
@@ -164,10 +164,9 @@ void ia_css_sdis2_clear_coefficients(
dvs2_coefs->ver_coefs.even_imag = NULL;
}
-int
-ia_css_get_dvs2_statistics(
- struct ia_css_dvs2_statistics *host_stats,
- const struct ia_css_isp_dvs_statistics *isp_stats) {
+int ia_css_get_dvs2_statistics(struct ia_css_dvs2_statistics *host_stats,
+ const struct ia_css_isp_dvs_statistics *isp_stats)
+{
struct ia_css_isp_dvs_statistics_map *map;
int ret = 0;
@@ -177,13 +176,11 @@ ia_css_get_dvs2_statistics(
assert(isp_stats);
map = ia_css_isp_dvs_statistics_map_allocate(isp_stats, NULL);
- if (map)
- {
+ if (map) {
hmm_load(isp_stats->data_ptr, map->data_ptr, isp_stats->size);
ia_css_translate_dvs2_statistics(host_stats, map);
ia_css_isp_dvs_statistics_map_free(map);
- } else
- {
+ } else {
IA_CSS_ERROR("out of memory");
ret = -ENOMEM;
}
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 3c675063c4a7..152faab2b169 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
@@ -45,11 +45,10 @@ int ia_css_vf_config(struct sh_css_isp_vf_isp_config *to,
* to the requested viewfinder resolution on the upper side. The output cannot
* be smaller than the requested viewfinder resolution.
*/
-int
-sh_css_vf_downscale_log2(
- const struct ia_css_frame_info *out_info,
- const struct ia_css_frame_info *vf_info,
- unsigned int *downscale_log2) {
+int sh_css_vf_downscale_log2(const struct ia_css_frame_info *out_info,
+ const struct ia_css_frame_info *vf_info,
+ unsigned int *downscale_log2)
+{
unsigned int ds_log2 = 0;
unsigned int out_width;
@@ -65,8 +64,7 @@ sh_css_vf_downscale_log2(
* test for the height since the vmem buffers only put restrictions on
* the width of a line, not on the number of lines in a frame.
*/
- while (out_width >= vf_info->res.width)
- {
+ while (out_width >= vf_info->res.width) {
ds_log2++;
out_width /= 2;
}
@@ -80,13 +78,12 @@ sh_css_vf_downscale_log2(
return 0;
}
-static int
-configure_kernel(
- const struct ia_css_binary_info *info,
- const struct ia_css_frame_info *out_info,
- const struct ia_css_frame_info *vf_info,
- unsigned int *downscale_log2,
- struct ia_css_vf_configuration *config) {
+static int configure_kernel(const struct ia_css_binary_info *info,
+ const struct ia_css_frame_info *out_info,
+ const struct ia_css_frame_info *vf_info,
+ unsigned int *downscale_log2,
+ struct ia_css_vf_configuration *config)
+{
int err;
unsigned int vf_log_ds = 0;
diff --git a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c
index af93ca96747c..e9016d7775dc 100644
--- a/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c
+++ b/drivers/staging/media/atomisp/pci/runtime/binary/src/binary.c
@@ -5,6 +5,7 @@
*/
#include <linux/math.h>
+#include <linux/string_choices.h>
#include <math_support.h>
#include <gdc_device.h> /* HR_GDC_N */
@@ -347,10 +348,10 @@ ia_css_binary_dvs_stat_grid_info(
return;
}
-int
-ia_css_binary_3a_grid_info(const struct ia_css_binary *binary,
- struct ia_css_grid_info *info,
- struct ia_css_pipe *pipe) {
+int ia_css_binary_3a_grid_info(const struct ia_css_binary *binary,
+ struct ia_css_grid_info *info,
+ struct ia_css_pipe *pipe)
+{
struct ia_css_3a_grid_info *s3a_info;
int err = 0;
@@ -439,9 +440,9 @@ supports_bds_factor(u32 supported_factors,
return ((supported_factors & PACK_BDS_FACTOR(bds_factor)) != 0);
}
-static int
-binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i,
- bool *binary_found) {
+static int binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i,
+ bool *binary_found)
+{
const unsigned char *blob = sh_css_blob_info[i].blob;
unsigned int size = sh_css_blob_info[i].header.blob.size;
@@ -464,8 +465,8 @@ binary_init_info(struct ia_css_binary_xinfo *info, unsigned int i,
/* When binaries are put at the beginning, they will only
* be selected if no other primary matches.
*/
-int
-ia_css_binary_init_infos(void) {
+int ia_css_binary_init_infos(void)
+{
unsigned int i;
unsigned int num_of_isp_binaries = sh_css_num_binaries - NUM_OF_SPS - NUM_OF_BLS;
@@ -477,8 +478,7 @@ ia_css_binary_init_infos(void) {
if (!all_binaries)
return -ENOMEM;
- for (i = 0; i < num_of_isp_binaries; i++)
- {
+ for (i = 0; i < num_of_isp_binaries; i++) {
int ret;
struct ia_css_binary_xinfo *binary = &all_binaries[i];
bool binary_found;
@@ -497,13 +497,12 @@ ia_css_binary_init_infos(void) {
return 0;
}
-int
-ia_css_binary_uninit(void) {
+int ia_css_binary_uninit(void)
+{
unsigned int i;
struct ia_css_binary_xinfo *b;
- for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++)
- {
+ for (i = 0; i < IA_CSS_BINARY_NUM_MODES; i++) {
for (b = binary_infos[i]; b; b = b->next) {
if (b->xmem_addr)
hmm_free(b->xmem_addr);
@@ -625,19 +624,19 @@ binary_in_frame_padded_width(int in_frame_width,
return rval;
}
-int
-ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
- bool online,
- bool two_ppc,
- enum atomisp_input_format stream_format,
- const struct ia_css_frame_info *in_info, /* can be NULL */
- const struct ia_css_frame_info *bds_out_info, /* can be NULL */
- const struct ia_css_frame_info *out_info[], /* can be NULL */
- const struct ia_css_frame_info *vf_info, /* can be NULL */
- struct ia_css_binary *binary,
- struct ia_css_resolution *dvs_env,
- int stream_config_left_padding,
- bool accelerator) {
+int ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
+ bool online,
+ bool two_ppc,
+ enum atomisp_input_format stream_format,
+ const struct ia_css_frame_info *in_info, /* can be NULL */
+ const struct ia_css_frame_info *bds_out_info, /* can be NULL */
+ const struct ia_css_frame_info *out_info[], /* can be NULL */
+ const struct ia_css_frame_info *vf_info, /* can be NULL */
+ struct ia_css_binary *binary,
+ struct ia_css_resolution *dvs_env,
+ int stream_config_left_padding,
+ bool accelerator)
+{
const struct ia_css_binary_info *info = &xinfo->sp;
unsigned int dvs_env_width = 0,
dvs_env_height = 0,
@@ -664,25 +663,21 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
assert(binary);
binary->info = xinfo;
- if (!accelerator)
- {
+ if (!accelerator) {
/* binary->css_params has been filled by accelerator itself. */
err = ia_css_isp_param_allocate_isp_parameters(
&binary->mem_params, &binary->css_params,
&info->mem_initializers);
- if (err) {
+ if (err)
return err;
- }
}
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
- {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
if (out_info[i] && (out_info[i]->res.width != 0)) {
bin_out_info = out_info[i];
break;
}
}
- if (in_info && bin_out_info)
- {
+ if (in_info && bin_out_info) {
need_scaling = (in_info->res.width != bin_out_info->res.width) ||
(in_info->res.height != bin_out_info->res.height);
}
@@ -713,8 +708,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
binary->internal_frame_info.res.height = isp_internal_height;
binary->internal_frame_info.raw_bit_depth = bits_per_pixel;
- if (in_info)
- {
+ if (in_info) {
binary->effective_in_frame_res.width = in_info->res.width;
binary->effective_in_frame_res.height = in_info->res.height;
@@ -742,15 +736,13 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
binary->in_frame_info.crop_info = in_info->crop_info;
}
- if (online)
- {
+ if (online) {
bits_per_pixel = ia_css_util_input_format_bpp(
stream_format, two_ppc);
}
binary->in_frame_info.raw_bit_depth = bits_per_pixel;
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
- {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
if (out_info[i]) {
binary->out_frame_info[i].res.width = out_info[i]->res.width;
binary->out_frame_info[i].res.height = out_info[i]->res.height;
@@ -769,8 +761,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
}
}
- if (vf_info && (vf_info->res.width != 0))
- {
+ if (vf_info && (vf_info->res.width != 0)) {
err = ia_css_vf_configure(binary, bin_out_info,
(struct ia_css_frame_info *)vf_info, &vf_log_ds);
if (err) {
@@ -788,8 +779,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
binary->input_format = stream_format;
/* viewfinder output info */
- if ((vf_info) && (vf_info->res.width != 0))
- {
+ if ((vf_info) && (vf_info->res.width != 0)) {
unsigned int vf_out_vecs, vf_out_width, vf_out_height;
binary->vf_frame_info.format = vf_info->format;
@@ -821,23 +811,20 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
binary->vf_frame_info.padded_width = vf_out_width;
binary->vf_frame_info.res.height = vf_out_height;
}
- } else
- {
+ } else {
binary->vf_frame_info.res.width = 0;
binary->vf_frame_info.padded_width = 0;
binary->vf_frame_info.res.height = 0;
}
- if (info->enable.ca_gdc)
- {
+ if (info->enable.ca_gdc) {
binary->morph_tbl_width =
_ISP_MORPH_TABLE_WIDTH(isp_internal_width);
binary->morph_tbl_aligned_width =
_ISP_MORPH_TABLE_ALIGNED_WIDTH(isp_internal_width);
binary->morph_tbl_height =
_ISP_MORPH_TABLE_HEIGHT(isp_internal_height);
- } else
- {
+ } else {
binary->morph_tbl_width = 0;
binary->morph_tbl_aligned_width = 0;
binary->morph_tbl_height = 0;
@@ -847,8 +834,7 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
sc_3a_dis_padded_width = binary->in_frame_info.padded_width;
sc_3a_dis_height = binary->in_frame_info.res.height;
if (bds_out_info && in_info &&
- bds_out_info->res.width != in_info->res.width)
- {
+ bds_out_info->res.width != in_info->res.width) {
/* TODO: Next, "internal_frame_info" should be derived from
* bds_out. So this part will change once it is in place! */
sc_3a_dis_width = bds_out_info->res.width + info->pipeline.left_cropping;
@@ -858,18 +844,15 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
s3a_isp_width = _ISP_S3A_ELEMS_ISP_WIDTH(sc_3a_dis_padded_width,
info->pipeline.left_cropping);
- if (info->s3a.fixed_s3a_deci_log)
- {
+ if (info->s3a.fixed_s3a_deci_log) {
s3a_log_deci = info->s3a.fixed_s3a_deci_log;
- } else
- {
+ } else {
s3a_log_deci = binary_grid_deci_factor_log2(s3a_isp_width,
sc_3a_dis_height);
}
binary->deci_factor_log2 = s3a_log_deci;
- if (info->enable.s3a)
- {
+ if (info->enable.s3a) {
binary->s3atbl_width =
_ISP_S3ATBL_WIDTH(sc_3a_dis_width,
s3a_log_deci);
@@ -882,21 +865,18 @@ ia_css_binary_fill_info(const struct ia_css_binary_xinfo *xinfo,
binary->s3atbl_isp_height =
_ISP_S3ATBL_ISP_HEIGHT(sc_3a_dis_height,
s3a_log_deci);
- } else
- {
+ } else {
binary->s3atbl_width = 0;
binary->s3atbl_height = 0;
binary->s3atbl_isp_width = 0;
binary->s3atbl_isp_height = 0;
}
- if (info->enable.sc)
- {
+ if (info->enable.sc) {
binary->sctbl_width_per_color = _ISP_SCTBL_WIDTH_PER_COLOR(sc_3a_dis_padded_width, s3a_log_deci);
binary->sctbl_aligned_width_per_color = SH_CSS_MAX_SCTBL_ALIGNED_WIDTH_PER_COLOR;
binary->sctbl_height = _ISP_SCTBL_HEIGHT(sc_3a_dis_height, s3a_log_deci);
- } else
- {
+ } else {
binary->sctbl_width_per_color = 0;
binary->sctbl_aligned_width_per_color = 0;
binary->sctbl_height = 0;
@@ -1241,7 +1221,7 @@ int ia_css_binary_find(struct ia_css_binary_descr *descr, struct ia_css_binary *
dev_dbg(atomisp_dev, "Using binary %s (id %d), type %d, mode %d, continuous %s\n",
xcandidate->blob->name, xcandidate->sp.id, xcandidate->type,
xcandidate->sp.pipeline.mode,
- xcandidate->sp.enable.continuous ? "true" : "false");
+ str_true_false(xcandidate->sp.enable.continuous));
if (err)
dev_err(atomisp_dev, "Failed to find a firmware binary matching the pipeline parameters\n");
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 b411ca2f415e..5113aa5973f3 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
@@ -1123,9 +1123,8 @@ ia_css_debug_pipe_graph_dump_prologue(void)
void ia_css_debug_pipe_graph_dump_epilogue(void)
{
- if (strlen(ring_buffer) > 0) {
+ if (strlen(ring_buffer) > 0)
dtrace_dot(ring_buffer);
- }
if (pg_inst.stream_format != N_ATOMISP_INPUT_FORMAT) {
/* An input stream format has been set so assume we have
@@ -1256,8 +1255,7 @@ ia_css_debug_pipe_graph_dump_stage(
while (ei[p] != ',')
p--;
/* Last comma found, copy till that comma */
- strscpy(enable_info1, ei,
- p > sizeof(enable_info1) ? sizeof(enable_info1) : p);
+ strscpy(enable_info1, ei, umin(p, sizeof(enable_info1)));
ei += p + 1;
l = strlen(ei);
@@ -1268,8 +1266,7 @@ ia_css_debug_pipe_graph_dump_stage(
* it is not guaranteed dword aligned
*/
- strscpy(enable_info2, ei,
- l > sizeof(enable_info2) ? sizeof(enable_info2) : l);
+ strscpy(enable_info2, ei, umin(l, sizeof(enable_info2)));
snprintf(enable_info, sizeof(enable_info), "%s\\n%s",
enable_info1, enable_info2);
@@ -1280,8 +1277,7 @@ ia_css_debug_pipe_graph_dump_stage(
while (ei[p] != ',')
p--;
- strscpy(enable_info2, ei,
- p > sizeof(enable_info2) ? sizeof(enable_info2) : p);
+ strscpy(enable_info2, ei, umin(p, sizeof(enable_info2)));
ei += p + 1;
l = strlen(ei);
@@ -1303,7 +1299,7 @@ ia_css_debug_pipe_graph_dump_stage(
while (ei[p] != ',')
p--;
strscpy(enable_info3, ei,
- p > sizeof(enable_info3) ? sizeof(enable_info3) : p);
+ umin(p, sizeof(enable_info3)));
ei += p + 1;
strscpy(enable_info3, ei,
sizeof(enable_info3));
@@ -1778,9 +1774,8 @@ static void debug_dump_one_trace(enum TRACE_CORE_ID proc_id)
* When tid value is 111b, the data will be interpreted differently:
* tid val is ignored, major field contains 2 bits (msb) for format type
*/
- if (tid_val == FIELD_TID_SEL_FORMAT_PAT) {
+ if (tid_val == FIELD_TID_SEL_FORMAT_PAT)
dump_format = FIELD_FORMAT_UNPACK(trace_read_buf[j]);
- }
}
switch (dump_format) {
case TRACE_DUMP_FORMAT_POINT:
diff --git a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c
index 251dd75a7613..1d20eb650757 100644
--- a/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c
+++ b/drivers/staging/media/atomisp/pci/runtime/isp_param/src/isp_param.c
@@ -93,17 +93,15 @@ ia_css_init_memory_interface(
}
}
-int
-ia_css_isp_param_allocate_isp_parameters(
- struct ia_css_isp_param_host_segments *mem_params,
- struct ia_css_isp_param_css_segments *css_params,
- const struct ia_css_isp_param_isp_segments *mem_initializers) {
+int ia_css_isp_param_allocate_isp_parameters(struct ia_css_isp_param_host_segments *mem_params,
+ struct ia_css_isp_param_css_segments *css_params,
+ const struct ia_css_isp_param_isp_segments *mem_initializers)
+{
int err = 0;
unsigned int mem, pclass;
pclass = IA_CSS_PARAM_CLASS_PARAM;
- for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++)
- {
+ for (mem = 0; mem < IA_CSS_NUM_MEMORIES; mem++) {
for (pclass = 0; pclass < IA_CSS_NUM_PARAM_CLASSES; pclass++) {
u32 size = 0;
@@ -171,15 +169,13 @@ ia_css_isp_param_load_fw_params(
}
}
-int
-ia_css_isp_param_copy_isp_mem_if_to_ddr(
- struct ia_css_isp_param_css_segments *ddr,
- const struct ia_css_isp_param_host_segments *host,
- enum ia_css_param_class pclass) {
+int ia_css_isp_param_copy_isp_mem_if_to_ddr(struct ia_css_isp_param_css_segments *ddr,
+ const struct ia_css_isp_param_host_segments *host,
+ enum ia_css_param_class pclass)
+{
unsigned int mem;
- for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++)
- {
+ for (mem = 0; mem < N_IA_CSS_ISP_MEMORIES; mem++) {
size_t size = host->params[pclass][mem].size;
ia_css_ptr ddr_mem_ptr = ddr->params[pclass][mem].address;
char *host_mem_ptr = host->params[pclass][mem].address;
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 e6c11d5f77b4..8e8433f5b07a 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
@@ -295,9 +295,8 @@ static bool create_input_system_channel(
if (!rc)
return false;
- if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id)) {
+ if (!acquire_sid(me->stream2mmio_id, &me->stream2mmio_sid_id))
return false;
- }
if (!acquire_ib_buffer(
metadata ? cfg->metadata.bits_per_pixel :
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 0470871f8fff..8d11466fda1b 100644
--- a/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
+++ b/drivers/staging/media/atomisp/pci/runtime/pipeline/src/pipeline.c
@@ -575,9 +575,8 @@ static int pipeline_stage_create(
binary = stage_desc->binary;
firmware = stage_desc->firmware;
vf_frame = stage_desc->vf_frame;
- for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++) {
+ for (i = 0; i < IA_CSS_BINARY_MAX_OUTPUT_PORTS; i++)
out_frame[i] = stage_desc->out_frame[i];
- }
stage = kvzalloc_obj(*stage);
if (!stage) {
diff --git a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c
index afe77d4373f8..d27c6567daeb 100644
--- a/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c
+++ b/drivers/staging/media/atomisp/pci/runtime/queue/src/queue.c
@@ -167,7 +167,7 @@ int ia_css_queue_dequeue(ia_css_queue_t *qhandle, uint32_t *item)
*item = cb_elem.val;
- cb_desc.start = OP_std_modadd(cb_desc.start, 1, cb_desc.size);
+ cb_desc.start = (cb_desc.start + 1) % cb_desc.size;
/* c. Store the queue object */
/* Set only fields requiring update with
@@ -315,7 +315,7 @@ int ia_css_queue_peek(ia_css_queue_t *qhandle, u32 offset, uint32_t *element)
if (offset > num_elems)
return -EINVAL;
- offset = OP_std_modadd(cb_desc.start, offset, cb_desc.size);
+ offset = (cb_desc.start + offset) % cb_desc.size;
error = ia_css_queue_item_load(qhandle, (uint8_t)offset, &cb_elem);
if (error != 0)
return error;
diff --git a/drivers/staging/media/atomisp/pci/sh_css.c b/drivers/staging/media/atomisp/pci/sh_css.c
index 6cda5925fa45..00082276f1db 100644
--- a/drivers/staging/media/atomisp/pci/sh_css.c
+++ b/drivers/staging/media/atomisp/pci/sh_css.c
@@ -7,6 +7,7 @@
/*! \file */
#include <linux/mm.h>
#include <linux/slab.h>
+#include <linux/string_choices.h>
#include <linux/vmalloc.h>
#include "hmm.h"
@@ -1478,7 +1479,7 @@ map_sp_threads(struct ia_css_stream *stream, bool map)
enum ia_css_pipe_id pipe_id;
IA_CSS_ENTER_PRIVATE("stream = %p, map = %s",
- stream, map ? "true" : "false");
+ stream, str_true_false(map));
if (!stream) {
IA_CSS_LEAVE_ERR_PRIVATE(-EINVAL);
@@ -2256,8 +2257,7 @@ alloc_continuous_frames(struct ia_css_pipe *pipe, bool init_time)
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW_PACKED\n");
ref_info.format = IA_CSS_FRAME_FORMAT_RAW_PACKED;
- } else
- {
+ } else {
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE_PRIVATE,
"alloc_continuous_frames() IA_CSS_FRAME_FORMAT_RAW\n");
ref_info.format = IA_CSS_FRAME_FORMAT_RAW;
@@ -5020,7 +5020,6 @@ static int load_primary_binaries(
struct ia_css_capture_settings *mycs;
unsigned int i;
bool need_extra_yuv_scaler = false;
- struct ia_css_binary_descr prim_descr[MAX_NUM_PRIMARY_STAGES];
IA_CSS_ENTER_PRIVATE("");
assert(pipe);
@@ -5189,15 +5188,16 @@ static int load_primary_binaries(
/* Primary */
for (i = 0; i < mycs->num_primary_stage; i++) {
+ struct ia_css_binary_descr prim_descr;
struct ia_css_frame_info *local_vf_info = NULL;
if (pipe->enable_viewfinder[IA_CSS_PIPE_OUTPUT_STAGE_0] &&
(i == mycs->num_primary_stage - 1))
local_vf_info = &vf_info;
- ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr[i],
+ ia_css_pipe_get_primary_binarydesc(pipe, &prim_descr,
&prim_in_info, &prim_out_info,
local_vf_info, i);
- err = ia_css_binary_find(&prim_descr[i], &mycs->primary_binary[i]);
+ err = ia_css_binary_find(&prim_descr, &mycs->primary_binary[i]);
if (err) {
IA_CSS_LEAVE_ERR_PRIVATE(err);
return err;
@@ -5819,36 +5819,37 @@ static int ia_css_pipe_create_cas_scaler_desc_single_output(
i *= max_scale_factor_per_stage;
}
- descr->in_info = kmalloc(descr->num_stage *
- sizeof(struct ia_css_frame_info),
- GFP_KERNEL);
+ descr->in_info = kmalloc_objs(*descr->in_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->in_info) {
err = -ENOMEM;
goto ERR;
}
- descr->internal_out_info = kmalloc(descr->num_stage *
- sizeof(struct ia_css_frame_info),
- GFP_KERNEL);
+ descr->internal_out_info = kmalloc_objs(*descr->internal_out_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->internal_out_info) {
err = -ENOMEM;
goto ERR;
}
- descr->out_info = kmalloc(descr->num_stage *
- sizeof(struct ia_css_frame_info),
- GFP_KERNEL);
+ descr->out_info = kmalloc_objs(*descr->out_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->out_info) {
err = -ENOMEM;
goto ERR;
}
- descr->vf_info = kmalloc(descr->num_stage *
- sizeof(struct ia_css_frame_info),
- GFP_KERNEL);
+ descr->vf_info = kmalloc_objs(*descr->vf_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->vf_info) {
err = -ENOMEM;
goto ERR;
}
- descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool),
- GFP_KERNEL);
+ descr->is_output_stage = kmalloc_objs(*descr->is_output_stage,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->is_output_stage) {
err = -ENOMEM;
goto ERR;
@@ -5968,35 +5969,37 @@ ia_css_pipe_create_cas_scaler_desc(struct ia_css_pipe *pipe,
descr->num_stage = num_stages;
- descr->in_info = kmalloc_objs(struct ia_css_frame_info,
- descr->num_stage);
+ descr->in_info = kmalloc_objs(*descr->in_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->in_info) {
err = -ENOMEM;
goto ERR;
}
- descr->internal_out_info = kmalloc(descr->num_stage *
- sizeof(struct ia_css_frame_info),
- GFP_KERNEL);
+ descr->internal_out_info = kmalloc_objs(*descr->internal_out_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->internal_out_info) {
err = -ENOMEM;
goto ERR;
}
- descr->out_info = kmalloc(descr->num_stage *
- sizeof(struct ia_css_frame_info),
- GFP_KERNEL);
+ descr->out_info = kmalloc_objs(*descr->out_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->out_info) {
err = -ENOMEM;
goto ERR;
}
- descr->vf_info = kmalloc(descr->num_stage *
- sizeof(struct ia_css_frame_info),
- GFP_KERNEL);
+ descr->vf_info = kmalloc_objs(*descr->vf_info,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->vf_info) {
err = -ENOMEM;
goto ERR;
}
- descr->is_output_stage = kmalloc(descr->num_stage * sizeof(bool),
- GFP_KERNEL);
+ descr->is_output_stage = kmalloc_objs(*descr->is_output_stage,
+ descr->num_stage,
+ GFP_KERNEL);
if (!descr->is_output_stage) {
err = -ENOMEM;
goto ERR;
@@ -7856,8 +7859,7 @@ ia_css_stream_create(const struct ia_css_stream_config *stream_config,
/* check if mipi size specified */
if (stream_config->mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR)
- if (!IS_ISP2401 || !stream_config->online)
- {
+ if (!IS_ISP2401 || !stream_config->online) {
unsigned int port = (unsigned int)stream_config->source.port.port;
if (port >= N_MIPI_PORT_ID) {
@@ -8189,6 +8191,48 @@ ERR:
return err;
}
+static void ia_css_stream_destroy_isp2401(struct ia_css_stream *stream)
+{
+ int i, j;
+
+ for (i = 0; i < stream->num_pipes; i++) {
+ struct ia_css_pipe *entry = stream->pipes[i];
+ unsigned int sp_thread_id;
+ struct sh_css_sp_pipeline_terminal *terminal;
+
+ if (!entry)
+ continue;
+
+ if (!ia_css_pipeline_get_sp_thread_id(
+ ia_css_pipe_get_pipe_num(entry), &sp_thread_id))
+ continue;
+
+ terminal = &sh_css_sp_group.pipe_io[sp_thread_id].input;
+
+ for (j = 0; j < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; j++) {
+ ia_css_isys_stream_h isys_stream =
+ &terminal->context.virtual_input_system_stream[j];
+ if (stream->config.isys_config[j].valid && isys_stream->valid)
+ ia_css_isys_stream_destroy(isys_stream);
+ }
+ }
+
+ if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
+ for (i = 0; i < stream->num_pipes; i++) {
+ /*
+ * free any mipi frames that are remaining:
+ * some test stream create-destroy cycles do
+ * not generate output frames
+ * and the mipi buffer is not freed in the
+ * deque function
+ */
+ if (stream->pipes[i])
+ free_mipi_frames(stream->pipes[i]);
+ }
+ }
+ stream_unregister_with_csi_rx(stream);
+}
+
int
ia_css_stream_destroy(struct ia_css_stream *stream)
{
@@ -8206,48 +8250,8 @@ ia_css_stream_destroy(struct ia_css_stream *stream)
if ((stream->last_pipe) &&
ia_css_pipeline_is_mapped(stream->last_pipe->pipe_num)) {
- if (IS_ISP2401) {
- for (i = 0; i < stream->num_pipes; i++) {
- struct ia_css_pipe *entry = stream->pipes[i];
- unsigned int sp_thread_id;
- struct sh_css_sp_pipeline_terminal *sp_pipeline_input_terminal;
-
- assert(entry);
- if (entry) {
- /* get the SP thread id */
- if (!ia_css_pipeline_get_sp_thread_id(
- ia_css_pipe_get_pipe_num(entry), &sp_thread_id))
- return -EINVAL;
-
- /* get the target input terminal */
- sp_pipeline_input_terminal =
- &sh_css_sp_group.pipe_io[sp_thread_id].input;
-
- for (i = 0; i < IA_CSS_STREAM_MAX_ISYS_STREAM_PER_CH; i++) {
- ia_css_isys_stream_h isys_stream =
- &sp_pipeline_input_terminal->context.virtual_input_system_stream[i];
- if (stream->config.isys_config[i].valid && isys_stream->valid)
- ia_css_isys_stream_destroy(isys_stream);
- }
- }
- }
-
- if (stream->config.mode == IA_CSS_INPUT_MODE_BUFFERED_SENSOR) {
- for (i = 0; i < stream->num_pipes; i++) {
- struct ia_css_pipe *entry = stream->pipes[i];
- /*
- * free any mipi frames that are remaining:
- * some test stream create-destroy cycles do
- * not generate output frames
- * and the mipi buffer is not freed in the
- * deque function
- */
- if (entry)
- free_mipi_frames(entry);
- }
- }
- stream_unregister_with_csi_rx(stream);
- }
+ if (IS_ISP2401)
+ ia_css_stream_destroy_isp2401(stream);
for (i = 0; i < stream->num_pipes; i++) {
struct ia_css_pipe *curr_pipe = stream->pipes[i];
diff --git a/drivers/staging/media/atomisp/pci/sh_css_firmware.c b/drivers/staging/media/atomisp/pci/sh_css_firmware.c
index 57ecf5549c23..af12df2f9b09 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_firmware.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_firmware.c
@@ -253,9 +253,9 @@ sh_css_load_firmware(struct device *dev, const char *fw_data,
sh_css_num_binaries = file_header->binary_nr;
/* Only allocate memory for ISP blob info */
if (sh_css_num_binaries > NUM_OF_SPS) {
- sh_css_blob_info = kmalloc(
- (sh_css_num_binaries - NUM_OF_SPS) *
- sizeof(*sh_css_blob_info), GFP_KERNEL);
+ sh_css_blob_info =
+ kmalloc_array(sh_css_num_binaries - NUM_OF_SPS,
+ sizeof(*sh_css_blob_info), GFP_KERNEL);
if (!sh_css_blob_info)
return -ENOMEM;
} else {
diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c
index 9ccdb66de2df..ad2a9b84e232 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_param_dvs.c
@@ -4,6 +4,7 @@
* Copyright (c) 2015, Intel Corporation.
*/
+#include <linux/overflow.h>
#include "sh_css_param_dvs.h"
#include <assert_support.h>
#include <type_support.h>
@@ -48,7 +49,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res,
}
/* Generate Y buffers */
- dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t),
+ dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->xcoords_y) {
IA_CSS_ERROR("out of memory");
@@ -56,7 +57,7 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res,
goto exit;
}
- dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t),
+ dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y, sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->ycoords_y) {
IA_CSS_ERROR("out of memory");
@@ -67,7 +68,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res,
/* Generate UV buffers */
IA_CSS_LOG("UV W %d H %d", width_uv, height_uv);
- dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t),
+ dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv,
+ sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->xcoords_uv) {
IA_CSS_ERROR("out of memory");
@@ -75,7 +77,8 @@ alloc_dvs_6axis_table(const struct ia_css_resolution *frame_res,
goto exit;
}
- dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv * sizeof(uint32_t),
+ dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv,
+ sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->ycoords_uv) {
IA_CSS_ERROR("out of memory");
@@ -269,5 +272,4 @@ ia_css_dvs_statistics_get(enum dvs_statistics_type type,
ia_css_get_dvs2_statistics(host_stats->p_dvs2_statistics_host,
isp_stats->p_dvs_statistics_isp);
}
- return;
}
diff --git a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c
index 9105334c71b1..0ac85cdea010 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_param_shading.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_param_shading.c
@@ -4,6 +4,7 @@
* Copyright (c) 2015, Intel Corporation.
*/
+#include <linux/overflow.h>
#include <linux/math.h>
#include <linux/slab.h>
@@ -20,7 +21,8 @@
#include "platform_support.h"
-/* Bilinear interpolation on shading tables:
+/*
+ * Bilinear interpolation on shading tables:
* For each target point T, we calculate the 4 surrounding source points:
* ul (upper left), ur (upper right), ll (lower left) and lr (lower right).
* We then calculate the distances from the T to the source points: x0, x1,
@@ -116,8 +118,10 @@ crop_and_interpolate(unsigned int cropped_width,
*/
ty = out_start_row + i * out_cell_size;
- /* calculate closest source points in shading table and
- make sure they fall within the table */
+ /*
+ * calculate closest source points in shading table and
+ * make sure they fall within the table
+ */
src_y0 = ty / (int)in_cell_size;
if (in_cell_size < out_cell_size)
src_y1 = (ty + out_cell_size) / in_cell_size;
@@ -173,7 +177,8 @@ crop_and_interpolate(unsigned int cropped_width,
dx0 = tx - sx0;
dx1 = sx1 - tx;
divx = sx1 - sx0;
- /* if we're at the edge, we just use the closest
+ /*
+ * if we're at the edge, we just use the closest
* point still in the grid. We make up for the divider
* in this case by setting the distance to
* out_cell_size, since it's actually 0.
@@ -291,8 +296,10 @@ prepare_shading_table(const struct ia_css_shading_table *in_table,
input_width = min(input_width, in_table->sensor_width);
input_height = min(input_height, in_table->sensor_height);
- /* This prepare_shading_table() function is called only in legacy API (not in new API).
- Then, the legacy shading table width and height should be used. */
+ /*
+ * This prepare_shading_table() function is called only in legacy API (not in new API).
+ * Then, the legacy shading table width and height should be used.
+ */
table_width = binary->sctbl_width_per_color;
table_height = binary->sctbl_height;
@@ -339,7 +346,8 @@ ia_css_shading_table_alloc(
me->fraction_bits = 0;
for (i = 0; i < IA_CSS_SC_NUM_COLORS; i++) {
me->data[i] =
- kvmalloc(width * height * sizeof(*me->data[0]),
+ kvmalloc(array3_size(width, height,
+ sizeof(*me->data[0])),
GFP_KERNEL);
if (!me->data[i]) {
unsigned int j;
diff --git a/drivers/staging/media/atomisp/pci/sh_css_params.c b/drivers/staging/media/atomisp/pci/sh_css_params.c
index fcebace11daf..8420a22fd8f0 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_params.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_params.c
@@ -4,6 +4,7 @@
* Copyright (c) 2015, Intel Corporation.
*/
+#include <linux/overflow.h>
#include <linux/math.h>
#include "gdc_device.h" /* gdc_lut_store(), ... */
@@ -657,11 +658,7 @@ static const int zoom_table[4][HRT_GDC_N] = {
static const struct ia_css_dz_config default_dz_config = {
HRT_GDC_N,
HRT_GDC_N,
- {
- \
- {0, 0}, \
- {0, 0}, \
- }
+ { 0, 0, 0, 0 }
};
static const struct ia_css_vector default_motion_config = {
@@ -875,7 +872,8 @@ ia_css_process_kernel(struct ia_css_stream *stream,
/* update the other buffers to the pipe specific copies */
for (stage = pipeline->stages; stage; stage = stage->next) {
- if (!stage || !stage->binary) continue;
+ if (!stage || !stage->binary)
+ continue;
process(pipeline->pipe_id, stage, params);
}
}
@@ -1210,8 +1208,8 @@ ia_css_process_zoom_and_motion(
}
assert(stage->stage_num < SH_CSS_MAX_STAGES);
- if (params->dz_config.zoom_region.resolution.width == 0 &&
- params->dz_config.zoom_region.resolution.height == 0) {
+ if (params->dz_config.zoom_region.width == 0 &&
+ params->dz_config.zoom_region.height == 0) {
sh_css_update_uds_and_crop_info(
&info->sp,
&binary->in_frame_info,
@@ -1381,11 +1379,11 @@ struct ia_css_morph_table *ia_css_morph_table_allocate(
}
for (i = 0; i < IA_CSS_MORPH_TABLE_NUM_PLANES; i++) {
- me->coordinates_x[i] = kvmalloc(height * width *
- sizeof(*me->coordinates_x[i]),
+ me->coordinates_x[i] = kvmalloc(array3_size(height, width,
+ sizeof(*me->coordinates_x[i])),
GFP_KERNEL);
- me->coordinates_y[i] = kvmalloc(height * width *
- sizeof(*me->coordinates_y[i]),
+ me->coordinates_y[i] = kvmalloc(array3_size(height, width,
+ sizeof(*me->coordinates_y[i])),
GFP_KERNEL);
if ((!me->coordinates_x[i]) ||
@@ -1928,9 +1926,8 @@ sh_css_set_per_frame_isp_config_on_pipe(
params = stream->per_frame_isp_params_configs;
/* update new ISP params object with the new config */
- if (!sh_css_init_isp_params_from_global(stream, params, false, pipe)) {
+ if (!sh_css_init_isp_params_from_global(stream, params, false, pipe))
err1 = -EINVAL;
- }
err2 = sh_css_init_isp_params_from_config(stream->pipes[0], params, config, pipe);
@@ -2004,9 +2001,8 @@ sh_css_init_isp_params_from_config(struct ia_css_pipe *pipe,
* user. */
/* we do not exit from this point immediately to allow internal
* firmware feature testing. */
- if (is_dp_10bpp) {
+ if (is_dp_10bpp)
err = -EINVAL;
- }
} else {
err = -EINVAL;
goto exit;
@@ -3034,9 +3030,8 @@ process_kernel_parameters(unsigned int pipe_id,
ia_css_ob_configure(&params->stream_configs.ob,
isp_pipe_version, raw_bit_depth);
}
- if (params->config_changed[IA_CSS_S3A_ID]) {
+ if (params->config_changed[IA_CSS_S3A_ID])
ia_css_s3a_configure(raw_bit_depth);
- }
/* Copy stage uds parameters to config, since they can differ per stage.
*/
params->crop_config.crop_pos = params->uds[stage->stage_num].crop_pos;
@@ -3045,7 +3040,8 @@ process_kernel_parameters(unsigned int pipe_id,
/* Call parameter process functions for all kernels */
/* Skip SC, since that is called on a temp sc table */
for (param_id = 0; param_id < IA_CSS_NUM_PARAMETER_IDS; param_id++) {
- if (param_id == IA_CSS_SC_ID) continue;
+ if (param_id == IA_CSS_SC_ID)
+ continue;
if (params->config_changed[param_id])
ia_css_kernel_process_param[param_id](pipe_id, stage, params);
}
@@ -3600,7 +3596,8 @@ sh_css_params_write_to_ddr_internal(
IA_CSS_PARAM_CLASS_PARAM, mem);
size_t size = isp_data->size;
- if (!size) continue;
+ if (!size)
+ continue;
buff_realloced = reallocate_buffer(&ddr_map->isp_mem_param[stage_num][mem],
&ddr_map_size->isp_mem_param[stage_num][mem],
size,
@@ -3899,9 +3896,8 @@ sh_css_invalidate_params(struct ia_css_stream *stream)
params->isp_params_changed = true;
for (i = 0; i < IA_CSS_PIPE_ID_NUM; i++) {
for (j = 0; j < SH_CSS_MAX_STAGES; j++) {
- for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++) {
+ for (mem = 0; mem < N_IA_CSS_MEMORIES; mem++)
params->isp_mem_params_changed[i][j][mem] = true;
- }
}
}
@@ -4096,10 +4092,10 @@ sh_css_update_uds_and_crop_info_based_on_zoom_region(
assert(motion_vector);
assert(uds);
assert(sp_out_crop_pos);
- x0 = zoom->zoom_region.origin.x;
- y0 = zoom->zoom_region.origin.y;
- x1 = zoom->zoom_region.resolution.width + x0;
- y1 = zoom->zoom_region.resolution.height + y0;
+ x0 = zoom->zoom_region.left;
+ y0 = zoom->zoom_region.top;
+ x1 = zoom->zoom_region.width + x0;
+ y1 = zoom->zoom_region.height + y0;
if ((x0 > x1) || (y0 > y1) || (x1 > pipe_in_res.width) || (y1 > pipe_in_res.height))
return -EINVAL;
@@ -4206,13 +4202,17 @@ ia_css_dvs_statistics_allocate(const struct ia_css_dvs_grid_info *grid)
goto err;
me->grid = *grid;
- me->hor_proj = kvmalloc(grid->height * IA_CSS_DVS_NUM_COEF_TYPES *
- sizeof(*me->hor_proj), GFP_KERNEL);
+ me->hor_proj = kvmalloc(array3_size(grid->height,
+ IA_CSS_DVS_NUM_COEF_TYPES,
+ sizeof(*me->hor_proj)),
+ GFP_KERNEL);
if (!me->hor_proj)
goto err;
- me->ver_proj = kvmalloc(grid->width * IA_CSS_DVS_NUM_COEF_TYPES *
- sizeof(*me->ver_proj), GFP_KERNEL);
+ me->ver_proj = kvmalloc(array3_size(grid->width,
+ IA_CSS_DVS_NUM_COEF_TYPES,
+ sizeof(*me->ver_proj)),
+ GFP_KERNEL);
if (!me->ver_proj)
goto err;
@@ -4478,24 +4478,26 @@ ia_css_dvs2_6axis_config_allocate(const struct ia_css_stream *stream)
params->pipe_dvs_6axis_config[IA_CSS_PIPE_ID_VIDEO]->height_uv;
IA_CSS_LOG("table Y: W %d H %d", width_y, height_y);
IA_CSS_LOG("table UV: W %d H %d", width_uv, height_uv);
- dvs_config->xcoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t),
+ dvs_config->xcoords_y = kvmalloc(array3_size(width_y, height_y,
+ sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->xcoords_y)
goto err;
- dvs_config->ycoords_y = kvmalloc(width_y * height_y * sizeof(uint32_t),
+ dvs_config->ycoords_y = kvmalloc(array3_size(width_y, height_y,
+ sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->ycoords_y)
goto err;
- dvs_config->xcoords_uv = kvmalloc(width_uv * height_uv *
- sizeof(uint32_t),
+ dvs_config->xcoords_uv = kvmalloc(array3_size(width_uv, height_uv,
+ sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->xcoords_uv)
goto err;
- dvs_config->ycoords_uv = kvmalloc(width_uv * height_uv *
- sizeof(uint32_t),
+ dvs_config->ycoords_uv = kvmalloc(array3_size(width_uv, height_uv,
+ sizeof(uint32_t)),
GFP_KERNEL);
if (!dvs_config->ycoords_uv)
goto err;
diff --git a/drivers/staging/media/atomisp/pci/sh_css_sp.c b/drivers/staging/media/atomisp/pci/sh_css_sp.c
index 6da151e7a873..2beb7168517f 100644
--- a/drivers/staging/media/atomisp/pci/sh_css_sp.c
+++ b/drivers/staging/media/atomisp/pci/sh_css_sp.c
@@ -775,9 +775,13 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline,
ret = ia_css_fpn_configure(binary, &binary->in_frame_info);
if (ret)
return ret;
- ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0]));
- if (ret)
- return ret;
+
+ if (binary->info->sp.enable.ref_frame) {
+ ret = ia_css_crop_configure(binary, ia_css_frame_get_info(args->delay_frames[0]));
+ if (ret)
+ return ret;
+ }
+
ret = ia_css_qplane_configure(pipeline, binary, &binary->in_frame_info);
if (ret)
return ret;
@@ -790,9 +794,6 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline,
ret = ia_css_copy_output_configure(binary, args->copy_output);
if (ret)
return ret;
- ret = ia_css_output0_configure(binary, ia_css_frame_get_info(args->out_frame[0]));
- if (ret)
- return ret;
ret = ia_css_iterator_configure(binary, ia_css_frame_get_info(args->in_frame));
if (ret)
return ret;
@@ -807,22 +808,18 @@ static int configure_isp_from_args(const struct sh_css_sp_pipeline *pipeline,
if (ret)
return ret;
- /*
- * FIXME: args->delay_frames can be NULL here
- *
- * Somehow, the driver at the Intel Atom Yocto tree doesn't seem to
- * suffer from the same issue.
- *
- * Anyway, the function below should now handle a NULL delay_frames
- * without crashing, but the pipeline should likely be built without
- * adding it at the first place (or there are a hidden bug somewhere)
- */
- ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay);
- if (ret)
- return ret;
- ret = ia_css_tnr_configure(binary, args->tnr_frames);
- if (ret)
- return ret;
+ if (binary->info->sp.enable.ref_frame) {
+ ret = ia_css_ref_configure(binary, args->delay_frames, pipeline->dvs_frame_delay);
+ if (ret)
+ return ret;
+ }
+
+ if (binary->info->sp.enable.tnr) {
+ ret = ia_css_tnr_configure(binary, args->tnr_frames);
+ if (ret)
+ return ret;
+ }
+
return ia_css_bayer_io_config(binary, args);
}
diff --git a/drivers/staging/media/atomisp/pci/system_local.c b/drivers/staging/media/atomisp/pci/system_local.c
index a8a93760d5b1..69bcd557a821 100644
--- a/drivers/staging/media/atomisp/pci/system_local.c
+++ b/drivers/staging/media/atomisp/pci/system_local.c
@@ -86,8 +86,7 @@ const hrt_address GP_DEVICE_BASE[N_GP_DEVICE_ID] = {
/*GP TIMER , all timer registers are inter-twined,
* so, having multiple base addresses for
* different timers does not help*/
-const hrt_address GP_TIMER_BASE =
- (hrt_address)0x0000000000000600ULL;
+const hrt_address GP_TIMER_BASE = (hrt_address)0x0000000000000600ULL;
/* GPIO */
const hrt_address GPIO_BASE[N_GPIO_ID] = {
diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c
index 014d0c6f0a8b..862aee993889 100644
--- a/drivers/staging/media/av7110/av7110.c
+++ b/drivers/staging/media/av7110/av7110.c
@@ -121,19 +121,22 @@ static void init_av7110_av(struct av7110 *av7110)
if (ret < 0)
pr_err("cannot set internal volume to maximum:%d\n", ret);
- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType,
- 1, (u16)av7110->display_ar);
+ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_MONITOR_TYPE, 1, av7110->display_ar);
if (ret < 0)
pr_err("unable to set aspect ratio\n");
- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType,
- 1, av7110->display_panscan);
+ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_PANSCAN_TYPE, 1,
+ av7110->display_panscan);
if (ret < 0)
pr_err("unable to set pan scan\n");
- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3);
+ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_WSS_CONFIG, 2, 2, wss_cfg_4_3);
if (ret < 0)
pr_err("unable to configure 4:3 wss\n");
- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 3, wss_cfg_16_9);
+ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_WSS_CONFIG, 2, 3, wss_cfg_16_9);
if (ret < 0)
pr_err("unable to configure 16:9 wss\n");
@@ -314,17 +317,6 @@ static int DvbDmxFilterCallback(u8 *buffer1, size_t buffer1_len,
}
}
-//#define DEBUG_TIMING
-static inline void print_time(char *s)
-{
-#ifdef DEBUG_TIMING
- struct timespec64 ts;
-
- ktime_get_real_ts64(&ts);
- pr_info("%s(): %ptSp\n", s, &ts);
-#endif
-}
-
#define DEBI_READ 0
#define DEBI_WRITE 1
static inline void start_debi_dma(struct av7110 *av7110, int dir,
@@ -353,7 +345,6 @@ static void debiirq(struct tasklet_struct *t)
int handle = (type >> 8) & 0x1f;
unsigned int xfer = 0;
- print_time("debi");
dprintk(4, "type 0x%04x\n", type);
if (type == -1) {
@@ -473,7 +464,6 @@ static void gpioirq(struct tasklet_struct *t)
txbuf = irdebi(av7110, DEBINOSWAP, TX_BUFF, 0, 2);
len = (av7110->debilen + 3) & ~3;
- print_time("gpio");
dprintk(8, "GPIO0 irq 0x%04x %d\n", av7110->debitype, av7110->debilen);
switch (av7110->debitype & 0xff) {
@@ -717,7 +707,8 @@ static inline int SetPIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid,
if (av7110->audiostate.bypass_mode)
aflags |= 0x8000;
- return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, MultiPID, 6,
+ return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER,
+ AV7110_MULTI_PID, 6,
pcrpid, vpid, apid, ttpid, subpid, aflags);
}
@@ -762,7 +753,6 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter)
u16 buf[20];
int ret, i;
u16 handle;
-// u16 mode = 0x0320;
u16 mode = 0xb96a;
dprintk(4, "%p\n", av7110);
@@ -785,7 +775,7 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter)
av7110_p2t_init(&av7110->p2t_filter[dvbdmxfilter->index], dvbdmxfeed);
}
- buf[0] = (COMTYPE_PID_FILTER << 8) + AddPIDFilter;
+ buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_ADD_PID_FILTER;
buf[1] = 16;
buf[2] = dvbdmxfeed->pid;
buf[3] = mode;
@@ -828,7 +818,7 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter)
av7110->handle2filter[handle] = NULL;
- buf[0] = (COMTYPE_PID_FILTER << 8) + DelPIDFilter;
+ buf[0] = (COMTYPE_PID_FILTER << 8) + AV7110_DEL_PID_FILTER;
buf[1] = 1;
buf[2] = handle;
ret = av7110_fw_request(av7110, buf, 3, answ, 2);
@@ -873,7 +863,8 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed)
if (dvbdmxfeed->pes_type < 2 && npids[0])
if (av7110->fe_synced) {
- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER,
+ AV7110_SCAN, 0);
if (ret)
return ret;
}
@@ -1911,11 +1902,13 @@ static int av7110_fe_lock_fix(struct av7110 *av7110, enum fe_status status)
av7110->pids[DMX_PES_TELETEXT], 0,
av7110->pids[DMX_PES_PCR]);
if (!ret)
- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER,
+ AV7110_SCAN, 0);
} else {
ret = SetPIDs(av7110, 0, 0, 0, 0, 0);
if (!ret) {
- ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER, FlushTSQueue, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_PID_FILTER,
+ AV7110_FLUSH_TS_QUEUE, 0);
if (!ret)
ret = av7110_wait_msgstate(av7110, GPMQBusy);
}
@@ -2272,7 +2265,7 @@ static int frontend_init(struct av7110 *av7110)
* original Roberto Deza's hardware:
*
* rps1 code for budgetpatch will copy internal HS event to GPIO3 pin.
- * GPIO3 is in budget-patch hardware connectd to port B VSYNC
+ * GPIO3 is in budget-patch hardware connected to port B VSYNC
* HS is an internal event of 7146, accessible with RPS
* and temporarily raised high every n lines
* (n in defined in the RPS_THRESH1 counter threshold)
@@ -2785,8 +2778,6 @@ static void av7110_irq(struct saa7146_dev *dev, u32 *isr)
{
struct av7110 *av7110 = dev->ext_priv;
- //print_time("av7110_irq");
-
/* Note: Don't try to handle the DEBI error irq (MASK_18), in
* intel mode the timeout is asserted all the time...
*/
diff --git a/drivers/staging/media/av7110/av7110.h b/drivers/staging/media/av7110/av7110.h
index b584754f4be0..797dda437cbe 100644
--- a/drivers/staging/media/av7110/av7110.h
+++ b/drivers/staging/media/av7110/av7110.h
@@ -243,8 +243,8 @@ struct av7110 {
struct dvb_video_events video_events;
video_size_t video_size;
- u16 wssMode;
- u16 wssData;
+ u16 wss_mode;
+ u16 wss_data;
struct infrared ir;
diff --git a/drivers/staging/media/av7110/av7110_av.c b/drivers/staging/media/av7110/av7110_av.c
index 2993ac43c49c..22f1335d371b 100644
--- a/drivers/staging/media/av7110/av7110_av.c
+++ b/drivers/staging/media/av7110/av7110_av.c
@@ -111,7 +111,7 @@ int av7110_av_start_record(struct av7110 *av7110, int av,
if (av7110->playing || (av7110->rec_mode & av))
return -EBUSY;
- av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+ av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0);
dvbdmx->recording = 1;
av7110->rec_mode |= av;
@@ -121,7 +121,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av,
dvbdmx->pesfilter[0]->pid,
dvb_filter_pes2ts_cb,
(void *)dvbdmx->pesfilter[0]);
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_RECORD, 2,
+ AV7110_AUDIO_PES, 0);
break;
case RP_VIDEO:
@@ -129,7 +131,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av,
dvbdmx->pesfilter[1]->pid,
dvb_filter_pes2ts_cb,
(void *)dvbdmx->pesfilter[1]);
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_RECORD, 2,
+ AV7110_VIDEO_PES, 0);
break;
case RP_AV:
@@ -141,7 +145,9 @@ int av7110_av_start_record(struct av7110 *av7110, int av,
dvbdmx->pesfilter[1]->pid,
dvb_filter_pes2ts_cb,
(void *)dvbdmx->pesfilter[1]);
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AV_PES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_RECORD, 2,
+ AV7110_AV_PES, 0);
break;
}
return ret;
@@ -158,7 +164,7 @@ int av7110_av_start_play(struct av7110 *av7110, int av)
if (av7110->playing & av)
return -EBUSY;
- av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+ av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0);
if (av7110->playing == RP_NONE) {
av7110_ipack_reset(&av7110->ipack[0]);
@@ -168,15 +174,21 @@ int av7110_av_start_play(struct av7110 *av7110, int av)
av7110->playing |= av;
switch (av7110->playing) {
case RP_AUDIO:
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_PLAY, 2,
+ AV7110_AUDIO_PES, 0);
break;
case RP_VIDEO:
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_PLAY, 2,
+ AV7110_VIDEO_PES, 0);
av7110->sinfo = 0;
break;
case RP_AV:
av7110->sinfo = 0;
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AV_PES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_PLAY, 2,
+ AV7110_AV_PES, 0);
break;
}
return ret;
@@ -190,15 +202,19 @@ int av7110_av_stop(struct av7110 *av7110, int av)
if (!(av7110->playing & av) && !(av7110->rec_mode & av))
return 0;
- av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+ av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, AV7110_REC_PLAY_STOP, 0);
if (av7110->playing) {
av7110->playing &= ~av;
switch (av7110->playing) {
case RP_AUDIO:
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, AudioPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_PLAY, 2,
+ AV7110_AUDIO_PES, 0);
break;
case RP_VIDEO:
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Play, 2, VideoPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_PLAY, 2,
+ AV7110_VIDEO_PES, 0);
break;
case RP_NONE:
ret = av7110_set_vidmode(av7110, av7110->vidmode);
@@ -208,10 +224,14 @@ int av7110_av_stop(struct av7110 *av7110, int av)
av7110->rec_mode &= ~av;
switch (av7110->rec_mode) {
case RP_AUDIO:
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, AudioPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_RECORD, 2,
+ AV7110_AUDIO_PES, 0);
break;
case RP_VIDEO:
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Record, 2, VideoPES, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_RECORD, 2,
+ AV7110_VIDEO_PES, 0);
break;
case RP_NONE:
break;
@@ -325,7 +345,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode)
dprintk(2, "av7110:%p\n", av7110);
- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, LoadVidCode, 1, mode);
+ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_LOAD_VID_CODE, 1, mode);
if (!ret && !av7110->playing) {
ret = ChangePIDs(av7110, av7110->pids[DMX_PES_VIDEO],
@@ -333,7 +354,8 @@ int av7110_set_vidmode(struct av7110 *av7110, enum av7110_video_mode mode)
av7110->pids[DMX_PES_TELETEXT],
0, av7110->pids[DMX_PES_PCR]);
if (!ret)
- ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, Scan, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER,
+ AV7110_SCAN, 0);
}
return ret;
}
@@ -1168,7 +1190,8 @@ static int dvb_video_ioctl(struct file *file,
}
if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) {
if (av7110->playing == RP_AV) {
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Stop, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_STOP, 0);
if (ret)
break;
av7110->playing &= ~RP_VIDEO;
@@ -1184,7 +1207,8 @@ static int dvb_video_ioctl(struct file *file,
case VIDEO_FREEZE:
av7110->videostate.play_state = VIDEO_FREEZED;
if (av7110->playing & RP_VIDEO)
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_PAUSE, 0);
else
ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1);
if (!ret)
@@ -1193,7 +1217,8 @@ static int dvb_video_ioctl(struct file *file,
case VIDEO_CONTINUE:
if (av7110->playing & RP_VIDEO)
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_CONTINUE, 0);
if (!ret)
ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0);
if (!ret) {
@@ -1248,8 +1273,9 @@ static int dvb_video_ioctl(struct file *file,
if (ret < 0)
break;
av7110->videostate.display_format = format;
- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType,
- 1, av7110->display_panscan);
+ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_PANSCAN_TYPE, 1,
+ av7110->display_panscan);
break;
}
@@ -1259,8 +1285,8 @@ static int dvb_video_ioctl(struct file *file,
break;
}
av7110->display_ar = arg;
- ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType,
- 1, (u16)arg);
+ ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_MONITOR_TYPE, 1, arg);
break;
#ifdef CONFIG_COMPAT
@@ -1291,7 +1317,8 @@ static int dvb_video_ioctl(struct file *file,
//note: arg is ignored by firmware
if (av7110->playing & RP_VIDEO)
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
- __Scan_I, 2, AV_PES, 0);
+ AV7110_REC_PLAY_SCAN_I, 2,
+ AV7110_AV_PES, 0);
else
ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg);
if (!ret) {
@@ -1303,7 +1330,9 @@ static int dvb_video_ioctl(struct file *file,
case VIDEO_SLOWMOTION:
if (av7110->playing & RP_VIDEO) {
if (av7110->trickmode != TRICK_SLOW)
- ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0);
+ ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
+ AV7110_REC_PLAY_SLOW, 2,
+ 0, 0);
if (!ret)
ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
} else {
@@ -1329,15 +1358,18 @@ static int dvb_video_ioctl(struct file *file,
av7110_ipack_reset(&av7110->ipack[1]);
if (av7110->playing == RP_AV) {
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
- __Play, 2, AV_PES, 0);
+ AV7110_REC_PLAY_PLAY, 2,
+ AV7110_AV_PES, 0);
if (ret)
break;
if (av7110->trickmode == TRICK_FAST)
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
- __Scan_I, 2, AV_PES, 0);
+ AV7110_REC_PLAY_SCAN_I, 2,
+ AV7110_AV_PES, 0);
if (av7110->trickmode == TRICK_SLOW) {
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
- __Slow, 2, 0, 0);
+ AV7110_REC_PLAY_SLOW, 2,
+ 0, 0);
if (!ret)
ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg);
}
@@ -1483,7 +1515,8 @@ static int dvb_audio_ioctl(struct file *file,
av7110_ipack_reset(&av7110->ipack[0]);
if (av7110->playing == RP_AV)
ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY,
- __Play, 2, AV_PES, 0);
+ AV7110_REC_PLAY_PLAY, 2,
+ AV7110_AV_PES, 0);
break;
case AUDIO_SET_ID:
diff --git a/drivers/staging/media/av7110/av7110_ca.c b/drivers/staging/media/av7110/av7110_ca.c
index 63d9c97a5190..24876c8fdc8c 100644
--- a/drivers/staging/media/av7110/av7110_ca.c
+++ b/drivers/staging/media/av7110/av7110_ca.c
@@ -307,7 +307,8 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg)
mutex_unlock(&av7110->ioctl_mutex);
return -EINVAL;
}
- av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5,
+ av7110_fw_cmd(av7110, COMTYPE_PIDFILTER,
+ AV7110_SET_DESCR, 5,
(descr->index << 8) | descr->parity,
(descr->cw[0] << 8) | descr->cw[1],
(descr->cw[2] << 8) | descr->cw[3],
diff --git a/drivers/staging/media/av7110/av7110_hw.c b/drivers/staging/media/av7110/av7110_hw.c
index 49ce295771e4..b0bd7666d48b 100644
--- a/drivers/staging/media/av7110/av7110_hw.c
+++ b/drivers/staging/media/av7110/av7110_hw.c
@@ -95,29 +95,6 @@ u32 av7110_debiread(struct av7110 *av7110, u32 config, int addr, unsigned int co
return result;
}
-/* av7110 ARM core boot stuff */
-#if 0
-void av7110_reset_arm(struct av7110 *av7110)
-{
- saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTLO);
-
- /* Disable DEBI and GPIO irq */
- SAA7146_IER_DISABLE(av7110->dev, MASK_19 | MASK_03);
- SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
-
- saa7146_setgpio(av7110->dev, RESET_LINE, SAA7146_GPIO_OUTHI);
- msleep(30); /* the firmware needs some time to initialize */
-
- ARM_ResetMailBox(av7110);
-
- SAA7146_ISR_CLEAR(av7110->dev, MASK_19 | MASK_03);
- SAA7146_IER_ENABLE(av7110->dev, MASK_03);
-
- av7110->arm_ready = 1;
- dprintk(1, "reset ARM\n");
-}
-#endif /* 0 */
-
static int waitdebi(struct av7110 *av7110, int adr, int state)
{
int k;
@@ -498,29 +475,6 @@ int av7110_fw_cmd(struct av7110 *av7110, int type, int com, int num, ...)
return ret;
}
-#if 0
-int av7110_send_ci_cmd(struct av7110 *av7110, u8 subcom, u8 *buf, u8 len)
-{
- int i, ret;
- u16 cmd[18] = { ((COMTYPE_COMMON_IF << 8) + subcom),
- 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
-
- dprintk(4, "%p\n", av7110);
-
- for (i = 0; i < len && i < 32; i++) {
- if (i % 2 == 0)
- cmd[(i / 2) + 2] = (u16)(buf[i]) << 8;
- else
- cmd[(i / 2) + 2] |= buf[i];
- }
-
- ret = av7110_send_fw_cmd(av7110, cmd, 18);
- if (ret && ret != -ERESTARTSYS)
- pr_err("%s(): error %d\n", __func__, ret);
- return ret;
-}
-#endif /* 0 */
-
int av7110_fw_request(struct av7110 *av7110, u16 *request_buf,
int request_buf_len, u16 *reply_buf, int reply_buf_len)
{
diff --git a/drivers/staging/media/av7110/av7110_hw.h b/drivers/staging/media/av7110/av7110_hw.h
index d4579f411c56..3afe474da871 100644
--- a/drivers/staging/media/av7110/av7110_hw.h
+++ b/drivers/staging/media/av7110/av7110_hw.h
@@ -21,12 +21,12 @@ enum av7110_bootstate {
};
enum av7110_type_rec_play_format {
- RP_None,
- AudioPES,
- AudioMp2,
- AudioPCM,
- VideoPES,
- AV_PES
+ AV7110_RP_NONE,
+ AV7110_AUDIO_PES,
+ AV7110_AUDIO_MP2,
+ AV7110_AUDIO_PCM,
+ AV7110_VIDEO_PES,
+ AV7110_AV_PES,
};
enum av7110_osd_palette_type {
@@ -112,19 +112,19 @@ enum av7110_osd_command {
};
enum av7110_pid_command {
- MultiPID,
- VideoPID,
- AudioPID,
- InitFilt,
- FiltError,
- NewVersion,
- CacheError,
- AddPIDFilter,
- DelPIDFilter,
- Scan,
- SetDescr,
- SetIR,
- FlushTSQueue
+ AV7110_MULTI_PID,
+ AV7110_VIDEO_PID,
+ AV7110_AUDIO_PID,
+ AV7110_INIT_FILT,
+ AV7110_FILT_ERROR,
+ AV7110_NEW_VERSION,
+ AV7110_CACHE_ERROR,
+ AV7110_ADD_PID_FILTER,
+ AV7110_DEL_PID_FILTER,
+ AV7110_SCAN,
+ AV7110_SET_DESCR,
+ AV7110_SET_IR,
+ AV7110_FLUSH_TS_QUEUE,
};
enum av7110_mpeg_command {
@@ -158,24 +158,24 @@ enum av7110_request_command {
};
enum av7110_encoder_command {
- SetVidMode,
- SetTestMode,
- LoadVidCode,
- SetMonitorType,
- SetPanScanType,
- SetFreezeMode,
- SetWSSConfig
+ AV7110_SET_VID_MODE,
+ AV7110_SET_TEST_MODE,
+ AV7110_LOAD_VID_CODE,
+ AV7110_SET_MONITOR_TYPE,
+ AV7110_SET_PANSCAN_TYPE,
+ AV7110_SET_FREEZE_MODE,
+ AV7110_SET_WSS_CONFIG,
};
enum av7110_rec_play_state {
- __Record,
- __Stop,
- __Play,
- __Pause,
- __Slow,
- __FF_IP,
- __Scan_I,
- __Continue
+ AV7110_REC_PLAY_RECORD,
+ AV7110_REC_PLAY_STOP,
+ AV7110_REC_PLAY_PLAY,
+ AV7110_REC_PLAY_PAUSE,
+ AV7110_REC_PLAY_SLOW,
+ AV7110_REC_PLAY_FF_IP,
+ AV7110_REC_PLAY_SCAN_I,
+ AV7110_REC_PLAY_CONTINUE,
};
enum av7110_fw_cmd_misc {
@@ -452,7 +452,8 @@ static inline int SendDAC(struct av7110 *av7110, u8 addr, u8 data)
static inline int av7710_set_video_mode(struct av7110 *av7110, int mode)
{
- return av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetVidMode, 1, mode);
+ return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_VID_MODE, 1, mode);
}
static inline int vidcom(struct av7110 *av7110, u32 com, u32 arg)
diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c
index fdae467fd7ab..9b7bf1857868 100644
--- a/drivers/staging/media/av7110/av7110_ir.c
+++ b/drivers/staging/media/av7110/av7110_ir.c
@@ -71,8 +71,8 @@ int av7110_set_ir_config(struct av7110 *av7110)
{
dprintk(4, "ir config = %08x\n", av7110->ir.ir_config);
- return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1,
- av7110->ir.ir_config);
+ return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER,
+ AV7110_SET_IR, 1, av7110->ir.ir_config);
}
static int change_protocol(struct rc_dev *rcdev, u64 *rc_type)
diff --git a/drivers/staging/media/av7110/av7110_v4l.c b/drivers/staging/media/av7110/av7110_v4l.c
index 200a7a29ea31..aed7581fa8a5 100644
--- a/drivers/staging/media/av7110/av7110_v4l.c
+++ b/drivers/staging/media/av7110/av7110_v4l.c
@@ -556,7 +556,7 @@ static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh,
if (FW_VERSION(av7110->arm_app) < 0x2623)
return -EINVAL;
memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
- if (av7110->wssMode) {
+ if (av7110->wss_mode) {
f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
}
@@ -596,14 +596,14 @@ static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
return -EINVAL;
if (f->fmt.sliced.service_set & V4L2_SLICED_WSS_625) {
/* WSS controlled by userspace */
- av7110->wssMode = 1;
- av7110->wssData = 0;
+ av7110->wss_mode = 1;
+ av7110->wss_data = 0;
} else {
/* WSS controlled by firmware */
- av7110->wssMode = 0;
- av7110->wssData = 0;
+ av7110->wss_mode = 0;
+ av7110->wss_data = 0;
return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
- SetWSSConfig, 1, 0);
+ AV7110_SET_WSS_CONFIG, 1, 0);
}
return 0;
}
@@ -616,17 +616,19 @@ static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size
int rc;
dprintk(2, "\n");
- if (FW_VERSION(av7110->arm_app) < 0x2623 || !av7110->wssMode || count != sizeof(d))
+ if (FW_VERSION(av7110->arm_app) < 0x2623 ||
+ !av7110->wss_mode || count != sizeof(d))
return -EINVAL;
if (copy_from_user(&d, data, count))
return -EFAULT;
if ((d.id != 0 && d.id != V4L2_SLICED_WSS_625) || d.field != 0 || d.line != 23)
return -EINVAL;
if (d.id)
- av7110->wssData = ((d.data[1] << 8) & 0x3f00) | d.data[0];
+ av7110->wss_data = ((d.data[1] << 8) & 0x3f00) | d.data[0];
else
- av7110->wssData = 0x8000;
- rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 1, av7110->wssData);
+ av7110->wss_data = 0x8000;
+ rc = av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+ AV7110_SET_WSS_CONFIG, 2, 1, av7110->wss_data);
return (rc < 0) ? rc : count;
}
diff --git a/drivers/staging/media/deprecated/atmel/Kconfig b/drivers/staging/media/deprecated/atmel/Kconfig
deleted file mode 100644
index 418841ea5a0d..000000000000
--- a/drivers/staging/media/deprecated/atmel/Kconfig
+++ /dev/null
@@ -1,47 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-
-comment "Atmel media platform drivers"
-
-config VIDEO_ATMEL_ISC
- tristate "ATMEL Image Sensor Controller (ISC) support (DEPRECATED)"
- depends on V4L_PLATFORM_DRIVERS
- depends on VIDEO_DEV && COMMON_CLK
- depends on ARCH_AT91 || COMPILE_TEST
- depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- select VIDEOBUF2_DMA_CONTIG
- select REGMAP_MMIO
- select V4L2_FWNODE
- select VIDEO_ATMEL_ISC_BASE
- help
- This module makes the ATMEL Image Sensor Controller available
- as a v4l2 device.
-
- This driver is deprecated and is scheduled for removal by
- the beginning of 2026. See the TODO file for more information.
-
-config VIDEO_ATMEL_XISC
- tristate "ATMEL eXtended Image Sensor Controller (XISC) support (DEPRECATED)"
- depends on V4L_PLATFORM_DRIVERS
- depends on VIDEO_DEV && COMMON_CLK
- depends on ARCH_AT91 || COMPILE_TEST
- depends on !VIDEO_MICROCHIP_ISC_BASE || COMPILE_TEST
- select VIDEOBUF2_DMA_CONTIG
- select REGMAP_MMIO
- select V4L2_FWNODE
- select VIDEO_ATMEL_ISC_BASE
- select MEDIA_CONTROLLER
- select VIDEO_V4L2_SUBDEV_API
- help
- This module makes the ATMEL eXtended Image Sensor Controller
- available as a v4l2 device.
-
- This driver is deprecated and is scheduled for removal by
- the beginning of 2026. See the TODO file for more information.
-
-config VIDEO_ATMEL_ISC_BASE
- tristate
- default n
- help
- ATMEL ISC and XISC common code base.
diff --git a/drivers/staging/media/deprecated/atmel/Makefile b/drivers/staging/media/deprecated/atmel/Makefile
deleted file mode 100644
index 34eaeeac5bba..000000000000
--- a/drivers/staging/media/deprecated/atmel/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-atmel-isc-objs = atmel-sama5d2-isc.o
-atmel-xisc-objs = atmel-sama7g5-isc.o
-atmel-isc-common-objs = atmel-isc-base.o atmel-isc-clk.o
-
-obj-$(CONFIG_VIDEO_ATMEL_ISC_BASE) += atmel-isc-common.o
-obj-$(CONFIG_VIDEO_ATMEL_ISC) += atmel-isc.o
-obj-$(CONFIG_VIDEO_ATMEL_XISC) += atmel-xisc.o
diff --git a/drivers/staging/media/deprecated/atmel/TODO b/drivers/staging/media/deprecated/atmel/TODO
deleted file mode 100644
index 71691df07a80..000000000000
--- a/drivers/staging/media/deprecated/atmel/TODO
+++ /dev/null
@@ -1,34 +0,0 @@
-The Atmel ISC driver is not compliant with media controller specification.
-In order to evolve this driver, it has to move to media controller, to
-support enhanced features and future products which embed it.
-The move to media controller involves several changes which are
-not backwards compatible with the current usability of the driver.
-
-The best example is the way the format is propagated from the top video
-driver /dev/videoX down to the sensor.
-
-In a simple configuration sensor ==> isc , the isc just calls subdev s_fmt
-and controls the sensor directly. This is achieved by having a lot of code
-inside the driver that will query the subdev at probe time and make a list
-of formats which are usable.
-Basically the user has nothing to configure, as the isc will handle
-everything at the top level. This is an easy way to capture, but also comes
-with the drawback of lack of flexibility.
-In a more complicated pipeline
-sensor ==> controller 1 ==> controller 2 ==> isc
-this will not be achievable, as controller 1 and controller 2 might be
-media-controller configurable, and will not propagate the formats down to
-the sensor.
-
-After discussions with the media maintainers, the decision is to move
-Atmel ISC to staging as-is, to keep the Kconfig symbols and the users
-to the driver in staging. Thus, all the existing users of the non
-media-controller paradigm will continue to be happy and use the old config
-way.
-
-The new driver was added in the media subsystem with a different
-symbol, with the conversion to media controller done, and new users
-of the driver will be able to use all the new features.
-
-The replacement driver is named VIDEO_MICROCHIP_ISC or
-VIDEO_MICROCHIP_XISC depending on the product flavor.
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c b/drivers/staging/media/deprecated/atmel/atmel-isc-base.c
deleted file mode 100644
index fb9ee8547392..000000000000
--- a/drivers/staging/media/deprecated/atmel/atmel-isc-base.c
+++ /dev/null
@@ -1,2008 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Microchip Image Sensor Controller (ISC) common driver base
- *
- * Copyright (C) 2016-2019 Microchip Technology, Inc.
- *
- * Author: Songjun Wu
- * Author: Eugen Hristev <eugen.hristev@microchip.com>
- *
- */
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/math64.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/videodev2.h>
-#include <linux/atmel-isc-media.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-image-sizes.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fwnode.h>
-#include <media/v4l2-subdev.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "atmel-isc-regs.h"
-#include "atmel-isc.h"
-
-static unsigned int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "debug level (0-2)");
-
-static unsigned int sensor_preferred = 1;
-module_param(sensor_preferred, uint, 0644);
-MODULE_PARM_DESC(sensor_preferred,
- "Sensor is preferred to output the specified format (1-on 0-off), default 1");
-
-#define ISC_IS_FORMAT_RAW(mbus_code) \
- (((mbus_code) & 0xf000) == 0x3000)
-
-#define ISC_IS_FORMAT_GREY(mbus_code) \
- (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \
- (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8)))
-
-static inline void isc_update_v4l2_ctrls(struct isc_device *isc)
-{
- struct isc_ctrls *ctrls = &isc->ctrls;
-
- /* In here we set the v4l2 controls w.r.t. our pipeline config */
- v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]);
- v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]);
- v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]);
- v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]);
-
- v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]);
- v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]);
- v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]);
- v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]);
-}
-
-static inline void isc_update_awb_ctrls(struct isc_device *isc)
-{
- struct isc_ctrls *ctrls = &isc->ctrls;
-
- /* In here we set our actual hw pipeline config */
-
- regmap_write(isc->regmap, ISC_WB_O_RGR,
- ((ctrls->offset[ISC_HIS_CFG_MODE_R])) |
- ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16));
- regmap_write(isc->regmap, ISC_WB_O_BGB,
- ((ctrls->offset[ISC_HIS_CFG_MODE_B])) |
- ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16));
- regmap_write(isc->regmap, ISC_WB_G_RGR,
- ctrls->gain[ISC_HIS_CFG_MODE_R] |
- (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16));
- regmap_write(isc->regmap, ISC_WB_G_BGB,
- ctrls->gain[ISC_HIS_CFG_MODE_B] |
- (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16));
-}
-
-static inline void isc_reset_awb_ctrls(struct isc_device *isc)
-{
- unsigned int c;
-
- for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
- /* gains have a fixed point at 9 decimals */
- isc->ctrls.gain[c] = 1 << 9;
- /* offsets are in 2's complements */
- isc->ctrls.offset[c] = 0;
- }
-}
-
-
-static int isc_queue_setup(struct vb2_queue *vq,
- unsigned int *nbuffers, unsigned int *nplanes,
- unsigned int sizes[], struct device *alloc_devs[])
-{
- struct isc_device *isc = vb2_get_drv_priv(vq);
- unsigned int size = isc->fmt.fmt.pix.sizeimage;
-
- if (*nplanes)
- return sizes[0] < size ? -EINVAL : 0;
-
- *nplanes = 1;
- sizes[0] = size;
-
- return 0;
-}
-
-static int isc_buffer_prepare(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long size = isc->fmt.fmt.pix.sizeimage;
-
- if (vb2_plane_size(vb, 0) < size) {
- v4l2_err(&isc->v4l2_dev, "buffer too small (%lu < %lu)\n",
- vb2_plane_size(vb, 0), size);
- return -EINVAL;
- }
-
- vb2_set_plane_payload(vb, 0, size);
-
- vbuf->field = isc->fmt.fmt.pix.field;
-
- return 0;
-}
-
-static void isc_crop_pfe(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
- u32 h, w;
-
- h = isc->fmt.fmt.pix.height;
- w = isc->fmt.fmt.pix.width;
-
- /*
- * In case the sensor is not RAW, it will output a pixel (12-16 bits)
- * with two samples on the ISC Data bus (which is 8-12)
- * ISC will count each sample, so, we need to multiply these values
- * by two, to get the real number of samples for the required pixels.
- */
- if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) {
- h <<= 1;
- w <<= 1;
- }
-
- /*
- * We limit the column/row count that the ISC will output according
- * to the configured resolution that we want.
- * This will avoid the situation where the sensor is misconfigured,
- * sending more data, and the ISC will just take it and DMA to memory,
- * causing corruption.
- */
- regmap_write(regmap, ISC_PFE_CFG1,
- (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) |
- (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK));
-
- regmap_write(regmap, ISC_PFE_CFG2,
- (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) |
- (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK));
-
- regmap_update_bits(regmap, ISC_PFE_CFG0,
- ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN,
- ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN);
-}
-
-static void isc_start_dma(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
- u32 sizeimage = isc->fmt.fmt.pix.sizeimage;
- u32 dctrl_dview;
- dma_addr_t addr0;
-
- addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0);
- regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0);
-
- switch (isc->config.fourcc) {
- case V4L2_PIX_FMT_YUV420:
- regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
- addr0 + (sizeimage * 2) / 3);
- regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
- addr0 + (sizeimage * 5) / 6);
- break;
- case V4L2_PIX_FMT_YUV422P:
- regmap_write(regmap, ISC_DAD1 + isc->offsets.dma,
- addr0 + sizeimage / 2);
- regmap_write(regmap, ISC_DAD2 + isc->offsets.dma,
- addr0 + (sizeimage * 3) / 4);
- break;
- default:
- break;
- }
-
- dctrl_dview = isc->config.dctrl_dview;
-
- regmap_write(regmap, ISC_DCTRL + isc->offsets.dma,
- dctrl_dview | ISC_DCTRL_IE_IS);
- spin_lock(&isc->awb_lock);
- regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE);
- spin_unlock(&isc->awb_lock);
-}
-
-static void isc_set_pipeline(struct isc_device *isc, u32 pipeline)
-{
- struct regmap *regmap = isc->regmap;
- struct isc_ctrls *ctrls = &isc->ctrls;
- u32 val, bay_cfg;
- const u32 *gamma;
- unsigned int i;
-
- /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */
- for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
- val = pipeline & BIT(i) ? 1 : 0;
- regmap_field_write(isc->pipeline[i], val);
- }
-
- if (!pipeline)
- return;
-
- bay_cfg = isc->config.sd_format->cfa_baycfg;
-
- regmap_write(regmap, ISC_WB_CFG, bay_cfg);
- isc_update_awb_ctrls(isc);
- isc_update_v4l2_ctrls(isc);
-
- regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL);
-
- gamma = &isc->gamma_table[ctrls->gamma_index][0];
- regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES);
- regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES);
- regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES);
-
- isc->config_dpc(isc);
- isc->config_csc(isc);
- isc->config_cbc(isc);
- isc->config_cc(isc);
- isc->config_gam(isc);
-}
-
-static int isc_update_profile(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
- u32 sr;
- int counter = 100;
-
- regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO);
-
- regmap_read(regmap, ISC_CTRLSR, &sr);
- while ((sr & ISC_CTRL_UPPRO) && counter--) {
- usleep_range(1000, 2000);
- regmap_read(regmap, ISC_CTRLSR, &sr);
- }
-
- if (counter < 0) {
- v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n");
- return -ETIMEDOUT;
- }
-
- return 0;
-}
-
-static void isc_set_histogram(struct isc_device *isc, bool enable)
-{
- struct regmap *regmap = isc->regmap;
- struct isc_ctrls *ctrls = &isc->ctrls;
-
- if (enable) {
- regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
- ISC_HIS_CFG_MODE_GR |
- (isc->config.sd_format->cfa_baycfg
- << ISC_HIS_CFG_BAYSEL_SHIFT) |
- ISC_HIS_CFG_RAR);
- regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
- ISC_HIS_CTRL_EN);
- regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE);
- ctrls->hist_id = ISC_HIS_CFG_MODE_GR;
- isc_update_profile(isc);
- regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
-
- ctrls->hist_stat = HIST_ENABLED;
- } else {
- regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE);
- regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his,
- ISC_HIS_CTRL_DIS);
-
- ctrls->hist_stat = HIST_DISABLED;
- }
-}
-
-static int isc_configure(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
- u32 pfe_cfg0, dcfg, mask, pipeline;
- struct isc_subdev_entity *subdev = isc->current_subdev;
-
- pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps;
- pipeline = isc->config.bits_pipeline;
-
- dcfg = isc->config.dcfg_imode | isc->dcfg;
-
- pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE;
- mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW |
- ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW |
- ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC |
- ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI;
-
- regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0);
-
- isc->config_rlp(isc);
-
- regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg);
-
- /* Set the pipeline */
- isc_set_pipeline(isc, pipeline);
-
- /*
- * The current implemented histogram is available for RAW R, B, GB, GR
- * channels. We need to check if sensor is outputting RAW BAYER
- */
- if (isc->ctrls.awb &&
- ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
- isc_set_histogram(isc, true);
- else
- isc_set_histogram(isc, false);
-
- /* Update profile */
- return isc_update_profile(isc);
-}
-
-static int isc_start_streaming(struct vb2_queue *vq, unsigned int count)
-{
- struct isc_device *isc = vb2_get_drv_priv(vq);
- struct regmap *regmap = isc->regmap;
- struct isc_buffer *buf;
- unsigned long flags;
- int ret;
-
- /* Enable stream on the sub device */
- ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1);
- if (ret && ret != -ENOIOCTLCMD) {
- v4l2_err(&isc->v4l2_dev, "stream on failed in subdev %d\n",
- ret);
- goto err_start_stream;
- }
-
- ret = pm_runtime_resume_and_get(isc->dev);
- if (ret < 0) {
- v4l2_err(&isc->v4l2_dev, "RPM resume failed in subdev %d\n",
- ret);
- goto err_pm_get;
- }
-
- ret = isc_configure(isc);
- if (unlikely(ret))
- goto err_configure;
-
- /* Enable DMA interrupt */
- regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE);
-
- spin_lock_irqsave(&isc->dma_queue_lock, flags);
-
- isc->sequence = 0;
- isc->stop = false;
- reinit_completion(&isc->comp);
-
- isc->cur_frm = list_first_entry(&isc->dma_queue,
- struct isc_buffer, list);
- list_del(&isc->cur_frm->list);
-
- isc_crop_pfe(isc);
- isc_start_dma(isc);
-
- spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
-
- /* if we streaming from RAW, we can do one-shot white balance adj */
- if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
- v4l2_ctrl_activate(isc->do_wb_ctrl, true);
-
- return 0;
-
-err_configure:
- pm_runtime_put_sync(isc->dev);
-err_pm_get:
- v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
-
-err_start_stream:
- spin_lock_irqsave(&isc->dma_queue_lock, flags);
- list_for_each_entry(buf, &isc->dma_queue, list)
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
- INIT_LIST_HEAD(&isc->dma_queue);
- spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
-
- return ret;
-}
-
-static void isc_stop_streaming(struct vb2_queue *vq)
-{
- struct isc_device *isc = vb2_get_drv_priv(vq);
- unsigned long flags;
- struct isc_buffer *buf;
- int ret;
-
- mutex_lock(&isc->awb_mutex);
- v4l2_ctrl_activate(isc->do_wb_ctrl, false);
-
- isc->stop = true;
-
- /* Wait until the end of the current frame */
- if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ))
- v4l2_err(&isc->v4l2_dev,
- "Timeout waiting for end of the capture\n");
-
- mutex_unlock(&isc->awb_mutex);
-
- /* Disable DMA interrupt */
- regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE);
-
- pm_runtime_put_sync(isc->dev);
-
- /* Disable stream on the sub device */
- ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0);
- if (ret && ret != -ENOIOCTLCMD)
- v4l2_err(&isc->v4l2_dev, "stream off failed in subdev\n");
-
- /* Release all active buffers */
- spin_lock_irqsave(&isc->dma_queue_lock, flags);
- if (unlikely(isc->cur_frm)) {
- vb2_buffer_done(&isc->cur_frm->vb.vb2_buf,
- VB2_BUF_STATE_ERROR);
- isc->cur_frm = NULL;
- }
- list_for_each_entry(buf, &isc->dma_queue, list)
- vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
- INIT_LIST_HEAD(&isc->dma_queue);
- spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
-}
-
-static void isc_buffer_queue(struct vb2_buffer *vb)
-{
- struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
- struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb);
- struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue);
- unsigned long flags;
-
- spin_lock_irqsave(&isc->dma_queue_lock, flags);
- if (!isc->cur_frm && list_empty(&isc->dma_queue) &&
- vb2_start_streaming_called(vb->vb2_queue)) {
- isc->cur_frm = buf;
- isc_start_dma(isc);
- } else
- list_add_tail(&buf->list, &isc->dma_queue);
- spin_unlock_irqrestore(&isc->dma_queue_lock, flags);
-}
-
-static struct isc_format *find_format_by_fourcc(struct isc_device *isc,
- unsigned int fourcc)
-{
- unsigned int num_formats = isc->num_user_formats;
- struct isc_format *fmt;
- unsigned int i;
-
- for (i = 0; i < num_formats; i++) {
- fmt = isc->user_formats[i];
- if (fmt->fourcc == fourcc)
- return fmt;
- }
-
- return NULL;
-}
-
-static const struct vb2_ops isc_vb2_ops = {
- .queue_setup = isc_queue_setup,
- .buf_prepare = isc_buffer_prepare,
- .start_streaming = isc_start_streaming,
- .stop_streaming = isc_stop_streaming,
- .buf_queue = isc_buffer_queue,
-};
-
-static int isc_querycap(struct file *file, void *priv,
- struct v4l2_capability *cap)
-{
- strscpy(cap->driver, "microchip-isc", sizeof(cap->driver));
- strscpy(cap->card, "Atmel Image Sensor Controller", sizeof(cap->card));
-
- return 0;
-}
-
-static int isc_enum_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_fmtdesc *f)
-{
- struct isc_device *isc = video_drvdata(file);
- u32 index = f->index;
- u32 i, supported_index;
-
- if (index < isc->controller_formats_size) {
- f->pixelformat = isc->controller_formats[index].fourcc;
- return 0;
- }
-
- index -= isc->controller_formats_size;
-
- supported_index = 0;
-
- for (i = 0; i < isc->formats_list_size; i++) {
- if (!ISC_IS_FORMAT_RAW(isc->formats_list[i].mbus_code) ||
- !isc->formats_list[i].sd_support)
- continue;
- if (supported_index == index) {
- f->pixelformat = isc->formats_list[i].fourcc;
- return 0;
- }
- supported_index++;
- }
-
- return -EINVAL;
-}
-
-static int isc_g_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *fmt)
-{
- struct isc_device *isc = video_drvdata(file);
-
- *fmt = isc->fmt;
-
- return 0;
-}
-
-/*
- * Checks the current configured format, if ISC can output it,
- * considering which type of format the ISC receives from the sensor
- */
-static int isc_try_validate_formats(struct isc_device *isc)
-{
- int ret;
- bool bayer = false, yuv = false, rgb = false, grey = false;
-
- /* all formats supported by the RLP module are OK */
- switch (isc->try_config.fourcc) {
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- case V4L2_PIX_FMT_SBGGR10:
- case V4L2_PIX_FMT_SGBRG10:
- case V4L2_PIX_FMT_SGRBG10:
- case V4L2_PIX_FMT_SRGGB10:
- case V4L2_PIX_FMT_SBGGR12:
- case V4L2_PIX_FMT_SGBRG12:
- case V4L2_PIX_FMT_SGRBG12:
- case V4L2_PIX_FMT_SRGGB12:
- ret = 0;
- bayer = true;
- break;
-
- case V4L2_PIX_FMT_YUV420:
- case V4L2_PIX_FMT_YUV422P:
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- ret = 0;
- yuv = true;
- break;
-
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_ABGR32:
- case V4L2_PIX_FMT_XBGR32:
- case V4L2_PIX_FMT_ARGB444:
- case V4L2_PIX_FMT_ARGB555:
- ret = 0;
- rgb = true;
- break;
- case V4L2_PIX_FMT_GREY:
- case V4L2_PIX_FMT_Y10:
- case V4L2_PIX_FMT_Y16:
- ret = 0;
- grey = true;
- break;
- default:
- /* any other different formats are not supported */
- ret = -EINVAL;
- }
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n",
- rgb, yuv, grey, bayer);
-
- /* we cannot output RAW if we do not receive RAW */
- if ((bayer) && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
- return -EINVAL;
-
- /* we cannot output GREY if we do not receive RAW/GREY */
- if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) &&
- !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code))
- return -EINVAL;
-
- return ret;
-}
-
-/*
- * Configures the RLP and DMA modules, depending on the output format
- * configured for the ISC.
- * If direct_dump == true, just dump raw data 8/16 bits depending on format.
- */
-static int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump)
-{
- isc->try_config.rlp_cfg_mode = 0;
-
- switch (isc->try_config.fourcc) {
- case V4L2_PIX_FMT_SBGGR8:
- case V4L2_PIX_FMT_SGBRG8:
- case V4L2_PIX_FMT_SGRBG8:
- case V4L2_PIX_FMT_SRGGB8:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 8;
- isc->try_config.bpp_v4l2 = 8;
- break;
- case V4L2_PIX_FMT_SBGGR10:
- case V4L2_PIX_FMT_SGBRG10:
- case V4L2_PIX_FMT_SGRBG10:
- case V4L2_PIX_FMT_SRGGB10:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_SBGGR12:
- case V4L2_PIX_FMT_SGBRG12:
- case V4L2_PIX_FMT_SGRBG12:
- case V4L2_PIX_FMT_SRGGB12:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_RGB565:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_ARGB444:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_ARGB555:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_ABGR32:
- case V4L2_PIX_FMT_XBGR32:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 32;
- isc->try_config.bpp_v4l2 = 32;
- break;
- case V4L2_PIX_FMT_YUV420:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
- isc->try_config.bpp = 12;
- isc->try_config.bpp_v4l2 = 8; /* only first plane */
- break;
- case V4L2_PIX_FMT_YUV422P:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 8; /* only first plane */
- break;
- case V4L2_PIX_FMT_YUYV:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_UYVY:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_VYUY:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- case V4L2_PIX_FMT_GREY:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 8;
- isc->try_config.bpp_v4l2 = 8;
- break;
- case V4L2_PIX_FMT_Y16:
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH;
- fallthrough;
- case V4L2_PIX_FMT_Y10:
- isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- isc->try_config.bpp = 16;
- isc->try_config.bpp_v4l2 = 16;
- break;
- default:
- return -EINVAL;
- }
-
- if (direct_dump) {
- isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8;
- isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8;
- isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED;
- return 0;
- }
-
- return 0;
-}
-
-/*
- * Configuring pipeline modules, depending on which format the ISC outputs
- * and considering which format it has as input from the sensor.
- */
-static int isc_try_configure_pipeline(struct isc_device *isc)
-{
- switch (isc->try_config.fourcc) {
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_ARGB555:
- case V4L2_PIX_FMT_ARGB444:
- case V4L2_PIX_FMT_ABGR32:
- case V4L2_PIX_FMT_XBGR32:
- /* if sensor format is RAW, we convert inside ISC */
- if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
- isc->try_config.bits_pipeline = CFA_ENABLE |
- WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE |
- CC_ENABLE;
- } else {
- isc->try_config.bits_pipeline = 0x0;
- }
- break;
- case V4L2_PIX_FMT_YUV420:
- /* if sensor format is RAW, we convert inside ISC */
- if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
- isc->try_config.bits_pipeline = CFA_ENABLE |
- CSC_ENABLE | GAM_ENABLES | WB_ENABLE |
- SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE |
- DPC_BLCENABLE;
- } else {
- isc->try_config.bits_pipeline = 0x0;
- }
- break;
- case V4L2_PIX_FMT_YUV422P:
- /* if sensor format is RAW, we convert inside ISC */
- if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
- isc->try_config.bits_pipeline = CFA_ENABLE |
- CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
- SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
- } else {
- isc->try_config.bits_pipeline = 0x0;
- }
- break;
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- /* if sensor format is RAW, we convert inside ISC */
- if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
- isc->try_config.bits_pipeline = CFA_ENABLE |
- CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
- SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE;
- } else {
- isc->try_config.bits_pipeline = 0x0;
- }
- break;
- case V4L2_PIX_FMT_GREY:
- case V4L2_PIX_FMT_Y16:
- /* if sensor format is RAW, we convert inside ISC */
- if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) {
- isc->try_config.bits_pipeline = CFA_ENABLE |
- CSC_ENABLE | WB_ENABLE | GAM_ENABLES |
- CBC_ENABLE | DPC_BLCENABLE;
- } else {
- isc->try_config.bits_pipeline = 0x0;
- }
- break;
- default:
- if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code))
- isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE;
- else
- isc->try_config.bits_pipeline = 0x0;
- }
-
- /* Tune the pipeline to product specific */
- isc->adapt_pipeline(isc);
-
- return 0;
-}
-
-static void isc_try_fse(struct isc_device *isc,
- struct v4l2_subdev_state *sd_state)
-{
- struct v4l2_rect *try_crop =
- v4l2_subdev_state_get_crop(sd_state, 0);
- struct v4l2_subdev_frame_size_enum fse = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- };
- int ret;
-
- /*
- * If we do not know yet which format the subdev is using, we cannot
- * do anything.
- */
- if (!isc->try_config.sd_format)
- return;
-
- fse.code = isc->try_config.sd_format->mbus_code;
-
- ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size,
- sd_state, &fse);
- /*
- * Attempt to obtain format size from subdev. If not available,
- * just use the maximum ISC can receive.
- */
- if (ret) {
- try_crop->width = isc->max_width;
- try_crop->height = isc->max_height;
- } else {
- try_crop->width = fse.max_width;
- try_crop->height = fse.max_height;
- }
-}
-
-static int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f,
- u32 *code)
-{
- int i;
- struct isc_format *sd_fmt = NULL, *direct_fmt = NULL;
- struct v4l2_pix_format *pixfmt = &f->fmt.pix;
- struct v4l2_subdev_pad_config pad_cfg = {};
- struct v4l2_subdev_state pad_state = {
- .pads = &pad_cfg,
- };
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_TRY,
- };
- u32 mbus_code;
- int ret;
- bool rlp_dma_direct_dump = false;
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
-
- /* Step 1: find a RAW format that is supported */
- for (i = 0; i < isc->num_user_formats; i++) {
- if (ISC_IS_FORMAT_RAW(isc->user_formats[i]->mbus_code)) {
- sd_fmt = isc->user_formats[i];
- break;
- }
- }
- /* Step 2: We can continue with this RAW format, or we can look
- * for better: maybe sensor supports directly what we need.
- */
- direct_fmt = find_format_by_fourcc(isc, pixfmt->pixelformat);
-
- /* Step 3: We have both. We decide given the module parameter which
- * one to use.
- */
- if (direct_fmt && sd_fmt && sensor_preferred)
- sd_fmt = direct_fmt;
-
- /* Step 4: we do not have RAW but we have a direct format. Use it. */
- if (direct_fmt && !sd_fmt)
- sd_fmt = direct_fmt;
-
- /* Step 5: if we are using a direct format, we need to package
- * everything as 8 bit data and just dump it
- */
- if (sd_fmt == direct_fmt)
- rlp_dma_direct_dump = true;
-
- /* Step 6: We have no format. This can happen if the userspace
- * requests some weird/invalid format.
- * In this case, default to whatever we have
- */
- if (!sd_fmt && !direct_fmt) {
- sd_fmt = isc->user_formats[isc->num_user_formats - 1];
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "Sensor not supporting %.4s, using %.4s\n",
- (char *)&pixfmt->pixelformat, (char *)&sd_fmt->fourcc);
- }
-
- if (!sd_fmt) {
- ret = -EINVAL;
- goto isc_try_fmt_err;
- }
-
- /* Step 7: Print out what we decided for debugging */
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "Preferring to have sensor using format %.4s\n",
- (char *)&sd_fmt->fourcc);
-
- /* Step 8: at this moment we decided which format the subdev will use */
- isc->try_config.sd_format = sd_fmt;
-
- /* Limit to Atmel ISC hardware capabilities */
- if (pixfmt->width > isc->max_width)
- pixfmt->width = isc->max_width;
- if (pixfmt->height > isc->max_height)
- pixfmt->height = isc->max_height;
-
- /*
- * The mbus format is the one the subdev outputs.
- * The pixels will be transferred in this format Sensor -> ISC
- */
- mbus_code = sd_fmt->mbus_code;
-
- /*
- * Validate formats. If the required format is not OK, default to raw.
- */
-
- isc->try_config.fourcc = pixfmt->pixelformat;
-
- if (isc_try_validate_formats(isc)) {
- pixfmt->pixelformat = isc->try_config.fourcc = sd_fmt->fourcc;
- /* Re-try to validate the new format */
- ret = isc_try_validate_formats(isc);
- if (ret)
- goto isc_try_fmt_err;
- }
-
- ret = isc_try_configure_rlp_dma(isc, rlp_dma_direct_dump);
- if (ret)
- goto isc_try_fmt_err;
-
- ret = isc_try_configure_pipeline(isc);
- if (ret)
- goto isc_try_fmt_err;
-
- /* Obtain frame sizes if possible to have crop requirements ready */
- isc_try_fse(isc, &pad_state);
-
- v4l2_fill_mbus_format(&format.format, pixfmt, mbus_code);
- ret = v4l2_subdev_call(isc->current_subdev->sd, pad, set_fmt,
- &pad_state, &format);
- if (ret < 0)
- goto isc_try_fmt_subdev_err;
-
- v4l2_fill_pix_format(pixfmt, &format.format);
-
- /* Limit to Atmel ISC hardware capabilities */
- if (pixfmt->width > isc->max_width)
- pixfmt->width = isc->max_width;
- if (pixfmt->height > isc->max_height)
- pixfmt->height = isc->max_height;
-
- pixfmt->field = V4L2_FIELD_NONE;
- pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3;
- pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) *
- pixfmt->height;
-
- if (code)
- *code = mbus_code;
-
- return 0;
-
-isc_try_fmt_err:
- v4l2_err(&isc->v4l2_dev, "Could not find any possible format for a working pipeline\n");
-isc_try_fmt_subdev_err:
- memset(&isc->try_config, 0, sizeof(isc->try_config));
-
- return ret;
-}
-
-static int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f)
-{
- struct v4l2_subdev_format format = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
- u32 mbus_code = 0;
- int ret;
-
- ret = isc_try_fmt(isc, f, &mbus_code);
- if (ret)
- return ret;
-
- v4l2_fill_mbus_format(&format.format, &f->fmt.pix, mbus_code);
- ret = v4l2_subdev_call(isc->current_subdev->sd, pad,
- set_fmt, NULL, &format);
- if (ret < 0)
- return ret;
-
- /* Limit to Atmel ISC hardware capabilities */
- if (f->fmt.pix.width > isc->max_width)
- f->fmt.pix.width = isc->max_width;
- if (f->fmt.pix.height > isc->max_height)
- f->fmt.pix.height = isc->max_height;
-
- isc->fmt = *f;
-
- if (isc->try_config.sd_format && isc->config.sd_format &&
- isc->try_config.sd_format != isc->config.sd_format) {
- isc->ctrls.hist_stat = HIST_INIT;
- isc_reset_awb_ctrls(isc);
- isc_update_v4l2_ctrls(isc);
- }
- /* make the try configuration active */
- isc->config = isc->try_config;
-
- v4l2_dbg(1, debug, &isc->v4l2_dev, "New ISC configuration in place\n");
-
- return 0;
-}
-
-static int isc_s_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct isc_device *isc = video_drvdata(file);
-
- if (vb2_is_busy(&isc->vb2_vidq))
- return -EBUSY;
-
- return isc_set_fmt(isc, f);
-}
-
-static int isc_try_fmt_vid_cap(struct file *file, void *priv,
- struct v4l2_format *f)
-{
- struct isc_device *isc = video_drvdata(file);
-
- return isc_try_fmt(isc, f, NULL);
-}
-
-static int isc_enum_input(struct file *file, void *priv,
- struct v4l2_input *inp)
-{
- if (inp->index != 0)
- return -EINVAL;
-
- inp->type = V4L2_INPUT_TYPE_CAMERA;
- inp->std = 0;
- strscpy(inp->name, "Camera", sizeof(inp->name));
-
- return 0;
-}
-
-static int isc_g_input(struct file *file, void *priv, unsigned int *i)
-{
- *i = 0;
-
- return 0;
-}
-
-static int isc_s_input(struct file *file, void *priv, unsigned int i)
-{
- if (i > 0)
- return -EINVAL;
-
- return 0;
-}
-
-static int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
-{
- struct isc_device *isc = video_drvdata(file);
-
- return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
-}
-
-static int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a)
-{
- struct isc_device *isc = video_drvdata(file);
-
- return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a);
-}
-
-static int isc_enum_framesizes(struct file *file, void *fh,
- struct v4l2_frmsizeenum *fsize)
-{
- struct isc_device *isc = video_drvdata(file);
- int ret = -EINVAL;
- int i;
-
- if (fsize->index)
- return -EINVAL;
-
- for (i = 0; i < isc->num_user_formats; i++)
- if (isc->user_formats[i]->fourcc == fsize->pixel_format)
- ret = 0;
-
- for (i = 0; i < isc->controller_formats_size; i++)
- if (isc->controller_formats[i].fourcc == fsize->pixel_format)
- ret = 0;
-
- if (ret)
- return ret;
-
- fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
-
- fsize->stepwise.min_width = 16;
- fsize->stepwise.max_width = isc->max_width;
- fsize->stepwise.min_height = 16;
- fsize->stepwise.max_height = isc->max_height;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.step_height = 1;
-
- return 0;
-}
-
-static const struct v4l2_ioctl_ops isc_ioctl_ops = {
- .vidioc_querycap = isc_querycap,
- .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap,
- .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap,
-
- .vidioc_enum_input = isc_enum_input,
- .vidioc_g_input = isc_g_input,
- .vidioc_s_input = isc_s_input,
-
- .vidioc_reqbufs = vb2_ioctl_reqbufs,
- .vidioc_querybuf = vb2_ioctl_querybuf,
- .vidioc_qbuf = vb2_ioctl_qbuf,
- .vidioc_expbuf = vb2_ioctl_expbuf,
- .vidioc_dqbuf = vb2_ioctl_dqbuf,
- .vidioc_create_bufs = vb2_ioctl_create_bufs,
- .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
- .vidioc_streamon = vb2_ioctl_streamon,
- .vidioc_streamoff = vb2_ioctl_streamoff,
-
- .vidioc_g_parm = isc_g_parm,
- .vidioc_s_parm = isc_s_parm,
- .vidioc_enum_framesizes = isc_enum_framesizes,
-
- .vidioc_log_status = v4l2_ctrl_log_status,
- .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
- .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
-};
-
-static int isc_open(struct file *file)
-{
- struct isc_device *isc = video_drvdata(file);
- struct v4l2_subdev *sd = isc->current_subdev->sd;
- int ret;
-
- if (mutex_lock_interruptible(&isc->lock))
- return -ERESTARTSYS;
-
- ret = v4l2_fh_open(file);
- if (ret < 0)
- goto unlock;
-
- if (!v4l2_fh_is_singular_file(file))
- goto unlock;
-
- ret = v4l2_subdev_call(sd, core, s_power, 1);
- if (ret < 0 && ret != -ENOIOCTLCMD) {
- v4l2_fh_release(file);
- goto unlock;
- }
-
- ret = isc_set_fmt(isc, &isc->fmt);
- if (ret) {
- v4l2_subdev_call(sd, core, s_power, 0);
- v4l2_fh_release(file);
- }
-
-unlock:
- mutex_unlock(&isc->lock);
- return ret;
-}
-
-static int isc_release(struct file *file)
-{
- struct isc_device *isc = video_drvdata(file);
- struct v4l2_subdev *sd = isc->current_subdev->sd;
- bool fh_singular;
- int ret;
-
- mutex_lock(&isc->lock);
-
- fh_singular = v4l2_fh_is_singular_file(file);
-
- ret = _vb2_fop_release(file, NULL);
-
- if (fh_singular)
- v4l2_subdev_call(sd, core, s_power, 0);
-
- mutex_unlock(&isc->lock);
-
- return ret;
-}
-
-static const struct v4l2_file_operations isc_fops = {
- .owner = THIS_MODULE,
- .open = isc_open,
- .release = isc_release,
- .unlocked_ioctl = video_ioctl2,
- .read = vb2_fop_read,
- .mmap = vb2_fop_mmap,
- .poll = vb2_fop_poll,
-};
-
-irqreturn_t atmel_isc_interrupt(int irq, void *dev_id)
-{
- struct isc_device *isc = (struct isc_device *)dev_id;
- struct regmap *regmap = isc->regmap;
- u32 isc_intsr, isc_intmask, pending;
- irqreturn_t ret = IRQ_NONE;
-
- regmap_read(regmap, ISC_INTSR, &isc_intsr);
- regmap_read(regmap, ISC_INTMASK, &isc_intmask);
-
- pending = isc_intsr & isc_intmask;
-
- if (likely(pending & ISC_INT_DDONE)) {
- spin_lock(&isc->dma_queue_lock);
- if (isc->cur_frm) {
- struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb;
- struct vb2_buffer *vb = &vbuf->vb2_buf;
-
- vb->timestamp = ktime_get_ns();
- vbuf->sequence = isc->sequence++;
- vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
- isc->cur_frm = NULL;
- }
-
- if (!list_empty(&isc->dma_queue) && !isc->stop) {
- isc->cur_frm = list_first_entry(&isc->dma_queue,
- struct isc_buffer, list);
- list_del(&isc->cur_frm->list);
-
- isc_start_dma(isc);
- }
-
- if (isc->stop)
- complete(&isc->comp);
-
- ret = IRQ_HANDLED;
- spin_unlock(&isc->dma_queue_lock);
- }
-
- if (pending & ISC_INT_HISDONE) {
- schedule_work(&isc->awb_work);
- ret = IRQ_HANDLED;
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(atmel_isc_interrupt);
-
-static void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max)
-{
- struct regmap *regmap = isc->regmap;
- struct isc_ctrls *ctrls = &isc->ctrls;
- u32 *hist_count = &ctrls->hist_count[ctrls->hist_id];
- u32 *hist_entry = &ctrls->hist_entry[0];
- u32 i;
-
- *min = 0;
- *max = HIST_ENTRIES;
-
- regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry,
- hist_entry, HIST_ENTRIES);
-
- *hist_count = 0;
- /*
- * we deliberately ignore the end of the histogram,
- * the most white pixels
- */
- for (i = 1; i < HIST_ENTRIES; i++) {
- if (*hist_entry && !*min)
- *min = i;
- if (*hist_entry)
- *max = i;
- *hist_count += i * (*hist_entry++);
- }
-
- if (!*min)
- *min = 1;
-
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: hist_id %u, hist_count %u",
- ctrls->hist_id, *hist_count);
-}
-
-static void isc_wb_update(struct isc_ctrls *ctrls)
-{
- struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls);
- u32 *hist_count = &ctrls->hist_count[0];
- u32 c, offset[4];
- u64 avg = 0;
- /* We compute two gains, stretch gain and grey world gain */
- u32 s_gain[4], gw_gain[4];
-
- /*
- * According to Grey World, we need to set gains for R/B to normalize
- * them towards the green channel.
- * Thus we want to keep Green as fixed and adjust only Red/Blue
- * Compute the average of the both green channels first
- */
- avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] +
- (u64)hist_count[ISC_HIS_CFG_MODE_GB];
- avg >>= 1;
-
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: green components average %llu\n", avg);
-
- /* Green histogram is null, nothing to do */
- if (!avg)
- return;
-
- for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) {
- /*
- * the color offset is the minimum value of the histogram.
- * we stretch this color to the full range by substracting
- * this value from the color component.
- */
- offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX];
- /*
- * The offset is always at least 1. If the offset is 1, we do
- * not need to adjust it, so our result must be zero.
- * the offset is computed in a histogram on 9 bits (0..512)
- * but the offset in register is based on
- * 12 bits pipeline (0..4096).
- * we need to shift with the 3 bits that the histogram is
- * ignoring
- */
- ctrls->offset[c] = (offset[c] - 1) << 3;
-
- /*
- * the offset is then taken and converted to 2's complements,
- * and must be negative, as we subtract this value from the
- * color components
- */
- ctrls->offset[c] = -ctrls->offset[c];
-
- /*
- * the stretch gain is the total number of histogram bins
- * divided by the actual range of color component (Max - Min)
- * If we compute gain like this, the actual color component
- * will be stretched to the full histogram.
- * We need to shift 9 bits for precision, we have 9 bits for
- * decimals
- */
- s_gain[c] = (HIST_ENTRIES << 9) /
- (ctrls->hist_minmax[c][HIST_MAX_INDEX] -
- ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1);
-
- /*
- * Now we have to compute the gain w.r.t. the average.
- * Add/lose gain to the component towards the average.
- * If it happens that the component is zero, use the
- * fixed point value : 1.0 gain.
- */
- if (hist_count[c])
- gw_gain[c] = div_u64(avg << 9, hist_count[c]);
- else
- gw_gain[c] = 1 << 9;
-
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: component %d, s_gain %u, gw_gain %u\n",
- c, s_gain[c], gw_gain[c]);
- /* multiply both gains and adjust for decimals */
- ctrls->gain[c] = s_gain[c] * gw_gain[c];
- ctrls->gain[c] >>= 9;
-
- /* make sure we are not out of range */
- ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0));
-
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb: component %d, final gain %u\n",
- c, ctrls->gain[c]);
- }
-}
-
-static void isc_awb_work(struct work_struct *w)
-{
- struct isc_device *isc =
- container_of(w, struct isc_device, awb_work);
- struct regmap *regmap = isc->regmap;
- struct isc_ctrls *ctrls = &isc->ctrls;
- u32 hist_id = ctrls->hist_id;
- u32 baysel;
- unsigned long flags;
- u32 min, max;
- int ret;
-
- if (ctrls->hist_stat != HIST_ENABLED)
- return;
-
- isc_hist_count(isc, &min, &max);
-
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max);
-
- ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min;
- ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max;
-
- if (hist_id != ISC_HIS_CFG_MODE_B) {
- hist_id++;
- } else {
- isc_wb_update(ctrls);
- hist_id = ISC_HIS_CFG_MODE_GR;
- }
-
- ctrls->hist_id = hist_id;
- baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT;
-
- ret = pm_runtime_resume_and_get(isc->dev);
- if (ret < 0)
- return;
-
- /*
- * only update if we have all the required histograms and controls
- * if awb has been disabled, we need to reset registers as well.
- */
- if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) {
- /*
- * It may happen that DMA Done IRQ will trigger while we are
- * updating white balance registers here.
- * In that case, only parts of the controls have been updated.
- * We can avoid that by locking the section.
- */
- spin_lock_irqsave(&isc->awb_lock, flags);
- isc_update_awb_ctrls(isc);
- spin_unlock_irqrestore(&isc->awb_lock, flags);
-
- /*
- * if we are doing just the one time white balance adjustment,
- * we are basically done.
- */
- if (ctrls->awb == ISC_WB_ONETIME) {
- v4l2_info(&isc->v4l2_dev,
- "Completed one time white-balance adjustment.\n");
- /* update the v4l2 controls values */
- isc_update_v4l2_ctrls(isc);
- ctrls->awb = ISC_WB_NONE;
- }
- }
- regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his,
- hist_id | baysel | ISC_HIS_CFG_RAR);
-
- /*
- * We have to make sure the streaming has not stopped meanwhile.
- * ISC requires a frame to clock the internal profile update.
- * To avoid issues, lock the sequence with a mutex
- */
- mutex_lock(&isc->awb_mutex);
-
- /* streaming is not active anymore */
- if (isc->stop) {
- mutex_unlock(&isc->awb_mutex);
- return;
- }
-
- isc_update_profile(isc);
-
- mutex_unlock(&isc->awb_mutex);
-
- /* if awb has been disabled, we don't need to start another histogram */
- if (ctrls->awb)
- regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
-
- pm_runtime_put_sync(isc->dev);
-}
-
-static int isc_s_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct isc_device *isc = container_of(ctrl->handler,
- struct isc_device, ctrls.handler);
- struct isc_ctrls *ctrls = &isc->ctrls;
-
- if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_BRIGHTNESS:
- ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK;
- break;
- case V4L2_CID_CONTRAST:
- ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK;
- break;
- case V4L2_CID_GAMMA:
- ctrls->gamma_index = ctrl->val;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static const struct v4l2_ctrl_ops isc_ctrl_ops = {
- .s_ctrl = isc_s_ctrl,
-};
-
-static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct isc_device *isc = container_of(ctrl->handler,
- struct isc_device, ctrls.handler);
- struct isc_ctrls *ctrls = &isc->ctrls;
-
- if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
- return 0;
-
- switch (ctrl->id) {
- case V4L2_CID_AUTO_WHITE_BALANCE:
- if (ctrl->val == 1)
- ctrls->awb = ISC_WB_AUTO;
- else
- ctrls->awb = ISC_WB_NONE;
-
- /* configure the controls with new values from v4l2 */
- if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new)
- ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val;
- if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new)
- ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val;
- if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new)
- ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val;
- if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new)
- ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val;
-
- if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new)
- ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val;
- if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new)
- ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val;
- if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new)
- ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val;
- if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new)
- ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val;
-
- isc_update_awb_ctrls(isc);
-
- mutex_lock(&isc->awb_mutex);
- if (vb2_is_streaming(&isc->vb2_vidq)) {
- /*
- * If we are streaming, we can update profile to
- * have the new settings in place.
- */
- isc_update_profile(isc);
- } else {
- /*
- * The auto cluster will activate automatically this
- * control. This has to be deactivated when not
- * streaming.
- */
- v4l2_ctrl_activate(isc->do_wb_ctrl, false);
- }
- mutex_unlock(&isc->awb_mutex);
-
- /* if we have autowhitebalance on, start histogram procedure */
- if (ctrls->awb == ISC_WB_AUTO &&
- vb2_is_streaming(&isc->vb2_vidq) &&
- ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code))
- isc_set_histogram(isc, true);
-
- /*
- * for one time whitebalance adjustment, check the button,
- * if it's pressed, perform the one time operation.
- */
- if (ctrls->awb == ISC_WB_NONE &&
- ctrl->cluster[ISC_CTRL_DO_WB]->is_new &&
- !(ctrl->cluster[ISC_CTRL_DO_WB]->flags &
- V4L2_CTRL_FLAG_INACTIVE)) {
- ctrls->awb = ISC_WB_ONETIME;
- isc_set_histogram(isc, true);
- v4l2_dbg(1, debug, &isc->v4l2_dev,
- "One time white-balance started.\n");
- }
- return 0;
- }
- return 0;
-}
-
-static int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl)
-{
- struct isc_device *isc = container_of(ctrl->handler,
- struct isc_device, ctrls.handler);
- struct isc_ctrls *ctrls = &isc->ctrls;
-
- switch (ctrl->id) {
- /* being a cluster, this id will be called for every control */
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ctrl->cluster[ISC_CTRL_R_GAIN]->val =
- ctrls->gain[ISC_HIS_CFG_MODE_R];
- ctrl->cluster[ISC_CTRL_B_GAIN]->val =
- ctrls->gain[ISC_HIS_CFG_MODE_B];
- ctrl->cluster[ISC_CTRL_GR_GAIN]->val =
- ctrls->gain[ISC_HIS_CFG_MODE_GR];
- ctrl->cluster[ISC_CTRL_GB_GAIN]->val =
- ctrls->gain[ISC_HIS_CFG_MODE_GB];
-
- ctrl->cluster[ISC_CTRL_R_OFF]->val =
- ctrls->offset[ISC_HIS_CFG_MODE_R];
- ctrl->cluster[ISC_CTRL_B_OFF]->val =
- ctrls->offset[ISC_HIS_CFG_MODE_B];
- ctrl->cluster[ISC_CTRL_GR_OFF]->val =
- ctrls->offset[ISC_HIS_CFG_MODE_GR];
- ctrl->cluster[ISC_CTRL_GB_OFF]->val =
- ctrls->offset[ISC_HIS_CFG_MODE_GB];
- break;
- }
- return 0;
-}
-
-static const struct v4l2_ctrl_ops isc_awb_ops = {
- .s_ctrl = isc_s_awb_ctrl,
- .g_volatile_ctrl = isc_g_volatile_awb_ctrl,
-};
-
-#define ISC_CTRL_OFF(_name, _id, _name_str) \
- static const struct v4l2_ctrl_config _name = { \
- .ops = &isc_awb_ops, \
- .id = _id, \
- .name = _name_str, \
- .type = V4L2_CTRL_TYPE_INTEGER, \
- .flags = V4L2_CTRL_FLAG_SLIDER, \
- .min = -4095, \
- .max = 4095, \
- .step = 1, \
- .def = 0, \
- }
-
-ISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset");
-ISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset");
-ISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset");
-ISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset");
-
-#define ISC_CTRL_GAIN(_name, _id, _name_str) \
- static const struct v4l2_ctrl_config _name = { \
- .ops = &isc_awb_ops, \
- .id = _id, \
- .name = _name_str, \
- .type = V4L2_CTRL_TYPE_INTEGER, \
- .flags = V4L2_CTRL_FLAG_SLIDER, \
- .min = 0, \
- .max = 8191, \
- .step = 1, \
- .def = 512, \
- }
-
-ISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain");
-ISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain");
-ISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain");
-ISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain");
-
-static int isc_ctrl_init(struct isc_device *isc)
-{
- const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops;
- struct isc_ctrls *ctrls = &isc->ctrls;
- struct v4l2_ctrl_handler *hdl = &ctrls->handler;
- int ret;
-
- ctrls->hist_stat = HIST_INIT;
- isc_reset_awb_ctrls(isc);
-
- ret = v4l2_ctrl_handler_init(hdl, 13);
- if (ret < 0)
- return ret;
-
- /* Initialize product specific controls. For example, contrast */
- isc->config_ctrls(isc, ops);
-
- ctrls->brightness = 0;
-
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0);
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1,
- isc->gamma_max);
- isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
- V4L2_CID_AUTO_WHITE_BALANCE,
- 0, 1, 1, 1);
-
- /* do_white_balance is a button, so min,max,step,default are ignored */
- isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops,
- V4L2_CID_DO_WHITE_BALANCE,
- 0, 0, 0, 0);
-
- if (!isc->do_wb_ctrl) {
- ret = hdl->error;
- v4l2_ctrl_handler_free(hdl);
- return ret;
- }
-
- v4l2_ctrl_activate(isc->do_wb_ctrl, false);
-
- isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL);
- isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL);
- isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL);
- isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL);
- isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL);
- isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL);
- isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL);
- isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL);
-
- /*
- * The cluster is in auto mode with autowhitebalance enabled
- * and manual mode otherwise.
- */
- v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true);
-
- v4l2_ctrl_handler_setup(hdl);
-
- return 0;
-}
-
-static int isc_async_bound(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_connection *asd)
-{
- struct isc_device *isc = container_of(notifier->v4l2_dev,
- struct isc_device, v4l2_dev);
- struct isc_subdev_entity *subdev_entity =
- container_of(notifier, struct isc_subdev_entity, notifier);
-
- if (video_is_registered(&isc->video_dev)) {
- v4l2_err(&isc->v4l2_dev, "only supports one sub-device.\n");
- return -EBUSY;
- }
-
- subdev_entity->sd = subdev;
-
- return 0;
-}
-
-static void isc_async_unbind(struct v4l2_async_notifier *notifier,
- struct v4l2_subdev *subdev,
- struct v4l2_async_connection *asd)
-{
- struct isc_device *isc = container_of(notifier->v4l2_dev,
- struct isc_device, v4l2_dev);
- mutex_destroy(&isc->awb_mutex);
- cancel_work_sync(&isc->awb_work);
- video_unregister_device(&isc->video_dev);
- v4l2_ctrl_handler_free(&isc->ctrls.handler);
-}
-
-static struct isc_format *find_format_by_code(struct isc_device *isc,
- unsigned int code, int *index)
-{
- struct isc_format *fmt = &isc->formats_list[0];
- unsigned int i;
-
- for (i = 0; i < isc->formats_list_size; i++) {
- if (fmt->mbus_code == code) {
- *index = i;
- return fmt;
- }
-
- fmt++;
- }
-
- return NULL;
-}
-
-static int isc_formats_init(struct isc_device *isc)
-{
- struct isc_format *fmt;
- struct v4l2_subdev *subdev = isc->current_subdev->sd;
- unsigned int num_fmts, i, j;
- u32 list_size = isc->formats_list_size;
- struct v4l2_subdev_mbus_code_enum mbus_code = {
- .which = V4L2_SUBDEV_FORMAT_ACTIVE,
- };
-
- num_fmts = 0;
- while (!v4l2_subdev_call(subdev, pad, enum_mbus_code,
- NULL, &mbus_code)) {
- mbus_code.index++;
-
- fmt = find_format_by_code(isc, mbus_code.code, &i);
- if (!fmt) {
- v4l2_warn(&isc->v4l2_dev, "Mbus code %x not supported\n",
- mbus_code.code);
- continue;
- }
-
- fmt->sd_support = true;
- num_fmts++;
- }
-
- if (!num_fmts)
- return -ENXIO;
-
- isc->num_user_formats = num_fmts;
- isc->user_formats = devm_kcalloc(isc->dev,
- num_fmts, sizeof(*isc->user_formats),
- GFP_KERNEL);
- if (!isc->user_formats)
- return -ENOMEM;
-
- fmt = &isc->formats_list[0];
- for (i = 0, j = 0; i < list_size; i++) {
- if (fmt->sd_support)
- isc->user_formats[j++] = fmt;
- fmt++;
- }
-
- return 0;
-}
-
-static int isc_set_default_fmt(struct isc_device *isc)
-{
- struct v4l2_format f = {
- .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- .fmt.pix = {
- .width = VGA_WIDTH,
- .height = VGA_HEIGHT,
- .field = V4L2_FIELD_NONE,
- .pixelformat = isc->user_formats[0]->fourcc,
- },
- };
- int ret;
-
- ret = isc_try_fmt(isc, &f, NULL);
- if (ret)
- return ret;
-
- isc->fmt = f;
- return 0;
-}
-
-static int isc_async_complete(struct v4l2_async_notifier *notifier)
-{
- struct isc_device *isc = container_of(notifier->v4l2_dev,
- struct isc_device, v4l2_dev);
- struct video_device *vdev = &isc->video_dev;
- struct vb2_queue *q = &isc->vb2_vidq;
- int ret = 0;
-
- INIT_WORK(&isc->awb_work, isc_awb_work);
-
- ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev);
- if (ret < 0) {
- v4l2_err(&isc->v4l2_dev, "Failed to register subdev nodes\n");
- return ret;
- }
-
- isc->current_subdev = container_of(notifier,
- struct isc_subdev_entity, notifier);
- mutex_init(&isc->lock);
- mutex_init(&isc->awb_mutex);
-
- init_completion(&isc->comp);
-
- /* Initialize videobuf2 queue */
- q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ;
- q->drv_priv = isc;
- q->buf_struct_size = sizeof(struct isc_buffer);
- q->ops = &isc_vb2_ops;
- q->mem_ops = &vb2_dma_contig_memops;
- q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
- q->lock = &isc->lock;
- q->min_queued_buffers = 1;
- q->dev = isc->dev;
-
- ret = vb2_queue_init(q);
- if (ret < 0) {
- v4l2_err(&isc->v4l2_dev,
- "vb2_queue_init() failed: %d\n", ret);
- goto isc_async_complete_err;
- }
-
- /* Init video dma queues */
- INIT_LIST_HEAD(&isc->dma_queue);
- spin_lock_init(&isc->dma_queue_lock);
- spin_lock_init(&isc->awb_lock);
-
- ret = isc_formats_init(isc);
- if (ret < 0) {
- v4l2_err(&isc->v4l2_dev,
- "Init format failed: %d\n", ret);
- goto isc_async_complete_err;
- }
-
- ret = isc_set_default_fmt(isc);
- if (ret) {
- v4l2_err(&isc->v4l2_dev, "Could not set default format\n");
- goto isc_async_complete_err;
- }
-
- ret = isc_ctrl_init(isc);
- if (ret) {
- v4l2_err(&isc->v4l2_dev, "Init isc ctrols failed: %d\n", ret);
- goto isc_async_complete_err;
- }
-
- /* Register video device */
- strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name));
- vdev->release = video_device_release_empty;
- vdev->fops = &isc_fops;
- vdev->ioctl_ops = &isc_ioctl_ops;
- vdev->v4l2_dev = &isc->v4l2_dev;
- vdev->vfl_dir = VFL_DIR_RX;
- vdev->queue = q;
- vdev->lock = &isc->lock;
- vdev->ctrl_handler = &isc->ctrls.handler;
- vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE;
- video_set_drvdata(vdev, isc);
-
- ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
- if (ret < 0) {
- v4l2_err(&isc->v4l2_dev,
- "video_register_device failed: %d\n", ret);
- goto isc_async_complete_err;
- }
-
- return 0;
-
-isc_async_complete_err:
- mutex_destroy(&isc->awb_mutex);
- mutex_destroy(&isc->lock);
- return ret;
-}
-
-const struct v4l2_async_notifier_operations atmel_isc_async_ops = {
- .bound = isc_async_bound,
- .unbind = isc_async_unbind,
- .complete = isc_async_complete,
-};
-EXPORT_SYMBOL_GPL(atmel_isc_async_ops);
-
-void atmel_isc_subdev_cleanup(struct isc_device *isc)
-{
- struct isc_subdev_entity *subdev_entity;
-
- list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
- v4l2_async_nf_unregister(&subdev_entity->notifier);
- v4l2_async_nf_cleanup(&subdev_entity->notifier);
- }
-
- INIT_LIST_HEAD(&isc->subdev_entities);
-}
-EXPORT_SYMBOL_GPL(atmel_isc_subdev_cleanup);
-
-int atmel_isc_pipeline_init(struct isc_device *isc)
-{
- struct device *dev = isc->dev;
- struct regmap *regmap = isc->regmap;
- struct regmap_field *regs;
- unsigned int i;
-
- /*
- * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC-->
- * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420
- */
- const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = {
- REG_FIELD(ISC_DPC_CTRL, 0, 0),
- REG_FIELD(ISC_DPC_CTRL, 1, 1),
- REG_FIELD(ISC_DPC_CTRL, 2, 2),
- REG_FIELD(ISC_WB_CTRL, 0, 0),
- REG_FIELD(ISC_CFA_CTRL, 0, 0),
- REG_FIELD(ISC_CC_CTRL, 0, 0),
- REG_FIELD(ISC_GAM_CTRL, 0, 0),
- REG_FIELD(ISC_GAM_CTRL, 1, 1),
- REG_FIELD(ISC_GAM_CTRL, 2, 2),
- REG_FIELD(ISC_GAM_CTRL, 3, 3),
- REG_FIELD(ISC_VHXS_CTRL, 0, 0),
- REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0),
- REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0),
- REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0),
- REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0),
- };
-
- for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) {
- regs = devm_regmap_field_alloc(dev, regmap, regfields[i]);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- isc->pipeline[i] = regs;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(atmel_isc_pipeline_init);
-
-/* regmap configuration */
-#define ATMEL_ISC_REG_MAX 0xd5c
-const struct regmap_config atmel_isc_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = ATMEL_ISC_REG_MAX,
-};
-EXPORT_SYMBOL_GPL(atmel_isc_regmap_config);
-
-MODULE_AUTHOR("Songjun Wu");
-MODULE_AUTHOR("Eugen Hristev");
-MODULE_DESCRIPTION("Atmel ISC common code base");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c b/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c
deleted file mode 100644
index d442b5f4c931..000000000000
--- a/drivers/staging/media/deprecated/atmel/atmel-isc-clk.c
+++ /dev/null
@@ -1,311 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Microchip Image Sensor Controller (ISC) common clock driver setup
- *
- * Copyright (C) 2016 Microchip Technology, Inc.
- *
- * Author: Songjun Wu
- * Author: Eugen Hristev <eugen.hristev@microchip.com>
- *
- */
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-
-#include "atmel-isc-regs.h"
-#include "atmel-isc.h"
-
-static int isc_wait_clk_stable(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- struct regmap *regmap = isc_clk->regmap;
- unsigned long timeout = jiffies + usecs_to_jiffies(1000);
- unsigned int status;
-
- while (time_before(jiffies, timeout)) {
- regmap_read(regmap, ISC_CLKSR, &status);
- if (!(status & ISC_CLKSR_SIP))
- return 0;
-
- usleep_range(10, 250);
- }
-
- return -ETIMEDOUT;
-}
-
-static int isc_clk_prepare(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- int ret;
-
- ret = pm_runtime_resume_and_get(isc_clk->dev);
- if (ret < 0)
- return ret;
-
- return isc_wait_clk_stable(hw);
-}
-
-static void isc_clk_unprepare(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- isc_wait_clk_stable(hw);
-
- pm_runtime_put_sync(isc_clk->dev);
-}
-
-static int isc_clk_enable(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 id = isc_clk->id;
- struct regmap *regmap = isc_clk->regmap;
- unsigned long flags;
- unsigned int status;
-
- dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n",
- __func__, id, isc_clk->div, isc_clk->parent_id);
-
- spin_lock_irqsave(&isc_clk->lock, flags);
- regmap_update_bits(regmap, ISC_CLKCFG,
- ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id),
- (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) |
- (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id)));
-
- regmap_write(regmap, ISC_CLKEN, ISC_CLK(id));
- spin_unlock_irqrestore(&isc_clk->lock, flags);
-
- regmap_read(regmap, ISC_CLKSR, &status);
- if (status & ISC_CLK(id))
- return 0;
- else
- return -EINVAL;
-}
-
-static void isc_clk_disable(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 id = isc_clk->id;
- unsigned long flags;
-
- spin_lock_irqsave(&isc_clk->lock, flags);
- regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id));
- spin_unlock_irqrestore(&isc_clk->lock, flags);
-}
-
-static int isc_clk_is_enabled(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 status;
- int ret;
-
- ret = pm_runtime_resume_and_get(isc_clk->dev);
- if (ret < 0)
- return 0;
-
- regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
-
- pm_runtime_put_sync(isc_clk->dev);
-
- return status & ISC_CLK(isc_clk->id) ? 1 : 0;
-}
-
-static unsigned long
-isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1);
-}
-
-static int isc_clk_determine_rate(struct clk_hw *hw,
- struct clk_rate_request *req)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- long best_rate = -EINVAL;
- int best_diff = -1;
- unsigned int i, div;
-
- for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
- struct clk_hw *parent;
- unsigned long parent_rate;
-
- parent = clk_hw_get_parent_by_index(hw, i);
- if (!parent)
- continue;
-
- parent_rate = clk_hw_get_rate(parent);
- if (!parent_rate)
- continue;
-
- for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) {
- unsigned long rate;
- int diff;
-
- rate = DIV_ROUND_CLOSEST(parent_rate, div);
- diff = abs(req->rate - rate);
-
- if (best_diff < 0 || best_diff > diff) {
- best_rate = rate;
- best_diff = diff;
- req->best_parent_rate = parent_rate;
- req->best_parent_hw = parent;
- }
-
- if (!best_diff || rate < req->rate)
- break;
- }
-
- if (!best_diff)
- break;
- }
-
- dev_dbg(isc_clk->dev,
- "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
- __func__, best_rate,
- __clk_get_name((req->best_parent_hw)->clk),
- req->best_parent_rate);
-
- if (best_rate < 0)
- return best_rate;
-
- req->rate = best_rate;
-
- return 0;
-}
-
-static int isc_clk_set_parent(struct clk_hw *hw, u8 index)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- if (index >= clk_hw_get_num_parents(hw))
- return -EINVAL;
-
- isc_clk->parent_id = index;
-
- return 0;
-}
-
-static u8 isc_clk_get_parent(struct clk_hw *hw)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
-
- return isc_clk->parent_id;
-}
-
-static int isc_clk_set_rate(struct clk_hw *hw,
- unsigned long rate,
- unsigned long parent_rate)
-{
- struct isc_clk *isc_clk = to_isc_clk(hw);
- u32 div;
-
- if (!rate)
- return -EINVAL;
-
- div = DIV_ROUND_CLOSEST(parent_rate, rate);
- if (div > (ISC_CLK_MAX_DIV + 1) || !div)
- return -EINVAL;
-
- isc_clk->div = div - 1;
-
- return 0;
-}
-
-static const struct clk_ops isc_clk_ops = {
- .prepare = isc_clk_prepare,
- .unprepare = isc_clk_unprepare,
- .enable = isc_clk_enable,
- .disable = isc_clk_disable,
- .is_enabled = isc_clk_is_enabled,
- .recalc_rate = isc_clk_recalc_rate,
- .determine_rate = isc_clk_determine_rate,
- .set_parent = isc_clk_set_parent,
- .get_parent = isc_clk_get_parent,
- .set_rate = isc_clk_set_rate,
-};
-
-static int isc_clk_register(struct isc_device *isc, unsigned int id)
-{
- struct regmap *regmap = isc->regmap;
- struct device_node *np = isc->dev->of_node;
- struct isc_clk *isc_clk;
- struct clk_init_data init;
- const char *clk_name = np->name;
- const char *parent_names[3];
- int num_parents;
-
- if (id == ISC_ISPCK && !isc->ispck_required)
- return 0;
-
- num_parents = of_clk_get_parent_count(np);
- if (num_parents < 1 || num_parents > 3)
- return -EINVAL;
-
- if (num_parents > 2 && id == ISC_ISPCK)
- num_parents = 2;
-
- of_clk_parent_fill(np, parent_names, num_parents);
-
- if (id == ISC_MCK)
- of_property_read_string(np, "clock-output-names", &clk_name);
- else
- clk_name = "isc-ispck";
-
- init.parent_names = parent_names;
- init.num_parents = num_parents;
- init.name = clk_name;
- init.ops = &isc_clk_ops;
- init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
-
- isc_clk = &isc->isc_clks[id];
- isc_clk->hw.init = &init;
- isc_clk->regmap = regmap;
- isc_clk->id = id;
- isc_clk->dev = isc->dev;
- spin_lock_init(&isc_clk->lock);
-
- isc_clk->clk = clk_register(isc->dev, &isc_clk->hw);
- if (IS_ERR(isc_clk->clk)) {
- dev_err(isc->dev, "%s: clock register fail\n", clk_name);
- return PTR_ERR(isc_clk->clk);
- } else if (id == ISC_MCK) {
- of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk);
- }
-
- return 0;
-}
-
-int atmel_isc_clk_init(struct isc_device *isc)
-{
- unsigned int i;
- int ret;
-
- for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++)
- isc->isc_clks[i].clk = ERR_PTR(-EINVAL);
-
- for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
- ret = isc_clk_register(isc, i);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(atmel_isc_clk_init);
-
-void atmel_isc_clk_cleanup(struct isc_device *isc)
-{
- unsigned int i;
-
- of_clk_del_provider(isc->dev->of_node);
-
- for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) {
- struct isc_clk *isc_clk = &isc->isc_clks[i];
-
- if (!IS_ERR(isc_clk->clk))
- clk_unregister(isc_clk->clk);
- }
-}
-EXPORT_SYMBOL_GPL(atmel_isc_clk_cleanup);
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h b/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h
deleted file mode 100644
index d06b72228d4f..000000000000
--- a/drivers/staging/media/deprecated/atmel/atmel-isc-regs.h
+++ /dev/null
@@ -1,413 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __ATMEL_ISC_REGS_H
-#define __ATMEL_ISC_REGS_H
-
-#include <linux/bitops.h>
-
-/* ISC Control Enable Register 0 */
-#define ISC_CTRLEN 0x00000000
-
-/* ISC Control Disable Register 0 */
-#define ISC_CTRLDIS 0x00000004
-
-/* ISC Control Status Register 0 */
-#define ISC_CTRLSR 0x00000008
-
-#define ISC_CTRL_CAPTURE BIT(0)
-#define ISC_CTRL_UPPRO BIT(1)
-#define ISC_CTRL_HISREQ BIT(2)
-#define ISC_CTRL_HISCLR BIT(3)
-
-/* ISC Parallel Front End Configuration 0 Register */
-#define ISC_PFE_CFG0 0x0000000c
-
-#define ISC_PFE_CFG0_HPOL_LOW BIT(0)
-#define ISC_PFE_CFG0_VPOL_LOW BIT(1)
-#define ISC_PFE_CFG0_PPOL_LOW BIT(2)
-#define ISC_PFE_CFG0_CCIR656 BIT(9)
-#define ISC_PFE_CFG0_CCIR_CRC BIT(10)
-#define ISC_PFE_CFG0_MIPI BIT(14)
-
-#define ISC_PFE_CFG0_MODE_PROGRESSIVE (0x0 << 4)
-#define ISC_PFE_CFG0_MODE_MASK GENMASK(6, 4)
-
-#define ISC_PFE_CFG0_BPS_EIGHT (0x4 << 28)
-#define ISC_PFG_CFG0_BPS_NINE (0x3 << 28)
-#define ISC_PFG_CFG0_BPS_TEN (0x2 << 28)
-#define ISC_PFG_CFG0_BPS_ELEVEN (0x1 << 28)
-#define ISC_PFG_CFG0_BPS_TWELVE (0x0 << 28)
-#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28)
-
-#define ISC_PFE_CFG0_COLEN BIT(12)
-#define ISC_PFE_CFG0_ROWEN BIT(13)
-
-/* ISC Parallel Front End Configuration 1 Register */
-#define ISC_PFE_CFG1 0x00000010
-
-#define ISC_PFE_CFG1_COLMIN(v) ((v))
-#define ISC_PFE_CFG1_COLMIN_MASK GENMASK(15, 0)
-#define ISC_PFE_CFG1_COLMAX(v) ((v) << 16)
-#define ISC_PFE_CFG1_COLMAX_MASK GENMASK(31, 16)
-
-/* ISC Parallel Front End Configuration 2 Register */
-#define ISC_PFE_CFG2 0x00000014
-
-#define ISC_PFE_CFG2_ROWMIN(v) ((v))
-#define ISC_PFE_CFG2_ROWMIN_MASK GENMASK(15, 0)
-#define ISC_PFE_CFG2_ROWMAX(v) ((v) << 16)
-#define ISC_PFE_CFG2_ROWMAX_MASK GENMASK(31, 16)
-
-/* ISC Clock Enable Register */
-#define ISC_CLKEN 0x00000018
-
-/* ISC Clock Disable Register */
-#define ISC_CLKDIS 0x0000001c
-
-/* ISC Clock Status Register */
-#define ISC_CLKSR 0x00000020
-#define ISC_CLKSR_SIP BIT(31)
-
-#define ISC_CLK(n) BIT(n)
-
-/* ISC Clock Configuration Register */
-#define ISC_CLKCFG 0x00000024
-#define ISC_CLKCFG_DIV_SHIFT(n) ((n)*16)
-#define ISC_CLKCFG_DIV_MASK(n) GENMASK(((n)*16 + 7), (n)*16)
-#define ISC_CLKCFG_SEL_SHIFT(n) ((n)*16 + 8)
-#define ISC_CLKCFG_SEL_MASK(n) GENMASK(((n)*17 + 8), ((n)*16 + 8))
-
-/* ISC Interrupt Enable Register */
-#define ISC_INTEN 0x00000028
-
-/* ISC Interrupt Disable Register */
-#define ISC_INTDIS 0x0000002c
-
-/* ISC Interrupt Mask Register */
-#define ISC_INTMASK 0x00000030
-
-/* ISC Interrupt Status Register */
-#define ISC_INTSR 0x00000034
-
-#define ISC_INT_DDONE BIT(8)
-#define ISC_INT_HISDONE BIT(12)
-
-/* ISC DPC Control Register */
-#define ISC_DPC_CTRL 0x40
-
-#define ISC_DPC_CTRL_DPCEN BIT(0)
-#define ISC_DPC_CTRL_GDCEN BIT(1)
-#define ISC_DPC_CTRL_BLCEN BIT(2)
-
-/* ISC DPC Config Register */
-#define ISC_DPC_CFG 0x44
-
-#define ISC_DPC_CFG_BAYSEL_SHIFT 0
-
-#define ISC_DPC_CFG_EITPOL BIT(4)
-
-#define ISC_DPC_CFG_TA_ENABLE BIT(14)
-#define ISC_DPC_CFG_TC_ENABLE BIT(13)
-#define ISC_DPC_CFG_TM_ENABLE BIT(12)
-
-#define ISC_DPC_CFG_RE_MODE BIT(17)
-
-#define ISC_DPC_CFG_GDCCLP_SHIFT 20
-#define ISC_DPC_CFG_GDCCLP_MASK GENMASK(22, 20)
-
-#define ISC_DPC_CFG_BLOFF_SHIFT 24
-#define ISC_DPC_CFG_BLOFF_MASK GENMASK(31, 24)
-
-#define ISC_DPC_CFG_BAYCFG_SHIFT 0
-#define ISC_DPC_CFG_BAYCFG_MASK GENMASK(1, 0)
-/* ISC DPC Threshold Median Register */
-#define ISC_DPC_THRESHM 0x48
-
-/* ISC DPC Threshold Closest Register */
-#define ISC_DPC_THRESHC 0x4C
-
-/* ISC DPC Threshold Average Register */
-#define ISC_DPC_THRESHA 0x50
-
-/* ISC DPC STatus Register */
-#define ISC_DPC_SR 0x54
-
-/* ISC White Balance Control Register */
-#define ISC_WB_CTRL 0x00000058
-
-/* ISC White Balance Configuration Register */
-#define ISC_WB_CFG 0x0000005c
-
-/* ISC White Balance Offset for R, GR Register */
-#define ISC_WB_O_RGR 0x00000060
-
-/* ISC White Balance Offset for B, GB Register */
-#define ISC_WB_O_BGB 0x00000064
-
-/* ISC White Balance Gain for R, GR Register */
-#define ISC_WB_G_RGR 0x00000068
-
-/* ISC White Balance Gain for B, GB Register */
-#define ISC_WB_G_BGB 0x0000006c
-
-/* ISC Color Filter Array Control Register */
-#define ISC_CFA_CTRL 0x00000070
-
-/* ISC Color Filter Array Configuration Register */
-#define ISC_CFA_CFG 0x00000074
-#define ISC_CFA_CFG_EITPOL BIT(4)
-
-#define ISC_BAY_CFG_GRGR 0x0
-#define ISC_BAY_CFG_RGRG 0x1
-#define ISC_BAY_CFG_GBGB 0x2
-#define ISC_BAY_CFG_BGBG 0x3
-
-/* ISC Color Correction Control Register */
-#define ISC_CC_CTRL 0x00000078
-
-/* ISC Color Correction RR RG Register */
-#define ISC_CC_RR_RG 0x0000007c
-
-/* ISC Color Correction RB OR Register */
-#define ISC_CC_RB_OR 0x00000080
-
-/* ISC Color Correction GR GG Register */
-#define ISC_CC_GR_GG 0x00000084
-
-/* ISC Color Correction GB OG Register */
-#define ISC_CC_GB_OG 0x00000088
-
-/* ISC Color Correction BR BG Register */
-#define ISC_CC_BR_BG 0x0000008c
-
-/* ISC Color Correction BB OB Register */
-#define ISC_CC_BB_OB 0x00000090
-
-/* ISC Gamma Correction Control Register */
-#define ISC_GAM_CTRL 0x00000094
-
-#define ISC_GAM_CTRL_BIPART BIT(4)
-
-/* ISC_Gamma Correction Blue Entry Register */
-#define ISC_GAM_BENTRY 0x00000098
-
-/* ISC_Gamma Correction Green Entry Register */
-#define ISC_GAM_GENTRY 0x00000198
-
-/* ISC_Gamma Correction Green Entry Register */
-#define ISC_GAM_RENTRY 0x00000298
-
-/* ISC VHXS Control Register */
-#define ISC_VHXS_CTRL 0x398
-
-/* ISC VHXS Source Size Register */
-#define ISC_VHXS_SS 0x39C
-
-/* ISC VHXS Destination Size Register */
-#define ISC_VHXS_DS 0x3A0
-
-/* ISC Vertical Factor Register */
-#define ISC_VXS_FACT 0x3a4
-
-/* ISC Horizontal Factor Register */
-#define ISC_HXS_FACT 0x3a8
-
-/* ISC Vertical Config Register */
-#define ISC_VXS_CFG 0x3ac
-
-/* ISC Horizontal Config Register */
-#define ISC_HXS_CFG 0x3b0
-
-/* ISC Vertical Tap Register */
-#define ISC_VXS_TAP 0x3b4
-
-/* ISC Horizontal Tap Register */
-#define ISC_HXS_TAP 0x434
-
-/* Offset for CSC register specific to sama5d2 product */
-#define ISC_SAMA5D2_CSC_OFFSET 0
-/* Offset for CSC register specific to sama7g5 product */
-#define ISC_SAMA7G5_CSC_OFFSET 0x11c
-
-/* Color Space Conversion Control Register */
-#define ISC_CSC_CTRL 0x00000398
-
-/* Color Space Conversion YR YG Register */
-#define ISC_CSC_YR_YG 0x0000039c
-
-/* Color Space Conversion YB OY Register */
-#define ISC_CSC_YB_OY 0x000003a0
-
-/* Color Space Conversion CBR CBG Register */
-#define ISC_CSC_CBR_CBG 0x000003a4
-
-/* Color Space Conversion CBB OCB Register */
-#define ISC_CSC_CBB_OCB 0x000003a8
-
-/* Color Space Conversion CRR CRG Register */
-#define ISC_CSC_CRR_CRG 0x000003ac
-
-/* Color Space Conversion CRB OCR Register */
-#define ISC_CSC_CRB_OCR 0x000003b0
-
-/* Offset for CBC register specific to sama5d2 product */
-#define ISC_SAMA5D2_CBC_OFFSET 0
-/* Offset for CBC register specific to sama7g5 product */
-#define ISC_SAMA7G5_CBC_OFFSET 0x11c
-
-/* Contrast And Brightness Control Register */
-#define ISC_CBC_CTRL 0x000003b4
-
-/* Contrast And Brightness Configuration Register */
-#define ISC_CBC_CFG 0x000003b8
-
-/* Brightness Register */
-#define ISC_CBC_BRIGHT 0x000003bc
-#define ISC_CBC_BRIGHT_MASK GENMASK(10, 0)
-
-/* Contrast Register */
-#define ISC_CBC_CONTRAST 0x000003c0
-#define ISC_CBC_CONTRAST_MASK GENMASK(11, 0)
-
-/* Hue Register */
-#define ISC_CBCHS_HUE 0x4e0
-/* Saturation Register */
-#define ISC_CBCHS_SAT 0x4e4
-
-/* Offset for SUB422 register specific to sama5d2 product */
-#define ISC_SAMA5D2_SUB422_OFFSET 0
-/* Offset for SUB422 register specific to sama7g5 product */
-#define ISC_SAMA7G5_SUB422_OFFSET 0x124
-
-/* Subsampling 4:4:4 to 4:2:2 Control Register */
-#define ISC_SUB422_CTRL 0x000003c4
-
-/* Offset for SUB420 register specific to sama5d2 product */
-#define ISC_SAMA5D2_SUB420_OFFSET 0
-/* Offset for SUB420 register specific to sama7g5 product */
-#define ISC_SAMA7G5_SUB420_OFFSET 0x124
-/* Subsampling 4:2:2 to 4:2:0 Control Register */
-#define ISC_SUB420_CTRL 0x000003cc
-
-/* Offset for RLP register specific to sama5d2 product */
-#define ISC_SAMA5D2_RLP_OFFSET 0
-/* Offset for RLP register specific to sama7g5 product */
-#define ISC_SAMA7G5_RLP_OFFSET 0x124
-/* Rounding, Limiting and Packing Configuration Register */
-#define ISC_RLP_CFG 0x000003d0
-
-#define ISC_RLP_CFG_MODE_DAT8 0x0
-#define ISC_RLP_CFG_MODE_DAT9 0x1
-#define ISC_RLP_CFG_MODE_DAT10 0x2
-#define ISC_RLP_CFG_MODE_DAT11 0x3
-#define ISC_RLP_CFG_MODE_DAT12 0x4
-#define ISC_RLP_CFG_MODE_DATY8 0x5
-#define ISC_RLP_CFG_MODE_DATY10 0x6
-#define ISC_RLP_CFG_MODE_ARGB444 0x7
-#define ISC_RLP_CFG_MODE_ARGB555 0x8
-#define ISC_RLP_CFG_MODE_RGB565 0x9
-#define ISC_RLP_CFG_MODE_ARGB32 0xa
-#define ISC_RLP_CFG_MODE_YYCC 0xb
-#define ISC_RLP_CFG_MODE_YYCC_LIMITED 0xc
-#define ISC_RLP_CFG_MODE_YCYC 0xd
-#define ISC_RLP_CFG_MODE_MASK GENMASK(3, 0)
-
-#define ISC_RLP_CFG_LSH BIT(5)
-
-#define ISC_RLP_CFG_YMODE_YUYV (3 << 6)
-#define ISC_RLP_CFG_YMODE_YVYU (2 << 6)
-#define ISC_RLP_CFG_YMODE_VYUY (0 << 6)
-#define ISC_RLP_CFG_YMODE_UYVY (1 << 6)
-
-#define ISC_RLP_CFG_YMODE_MASK GENMASK(7, 6)
-
-/* Offset for HIS register specific to sama5d2 product */
-#define ISC_SAMA5D2_HIS_OFFSET 0
-/* Offset for HIS register specific to sama7g5 product */
-#define ISC_SAMA7G5_HIS_OFFSET 0x124
-/* Histogram Control Register */
-#define ISC_HIS_CTRL 0x000003d4
-
-#define ISC_HIS_CTRL_EN BIT(0)
-#define ISC_HIS_CTRL_DIS 0x0
-
-/* Histogram Configuration Register */
-#define ISC_HIS_CFG 0x000003d8
-
-#define ISC_HIS_CFG_MODE_GR 0x0
-#define ISC_HIS_CFG_MODE_R 0x1
-#define ISC_HIS_CFG_MODE_GB 0x2
-#define ISC_HIS_CFG_MODE_B 0x3
-#define ISC_HIS_CFG_MODE_Y 0x4
-#define ISC_HIS_CFG_MODE_RAW 0x5
-#define ISC_HIS_CFG_MODE_YCCIR656 0x6
-
-#define ISC_HIS_CFG_BAYSEL_SHIFT 4
-
-#define ISC_HIS_CFG_RAR BIT(8)
-
-/* Offset for DMA register specific to sama5d2 product */
-#define ISC_SAMA5D2_DMA_OFFSET 0
-/* Offset for DMA register specific to sama7g5 product */
-#define ISC_SAMA7G5_DMA_OFFSET 0x13c
-
-/* DMA Configuration Register */
-#define ISC_DCFG 0x000003e0
-#define ISC_DCFG_IMODE_PACKED8 0x0
-#define ISC_DCFG_IMODE_PACKED16 0x1
-#define ISC_DCFG_IMODE_PACKED32 0x2
-#define ISC_DCFG_IMODE_YC422SP 0x3
-#define ISC_DCFG_IMODE_YC422P 0x4
-#define ISC_DCFG_IMODE_YC420SP 0x5
-#define ISC_DCFG_IMODE_YC420P 0x6
-#define ISC_DCFG_IMODE_MASK GENMASK(2, 0)
-
-#define ISC_DCFG_YMBSIZE_SINGLE (0x0 << 4)
-#define ISC_DCFG_YMBSIZE_BEATS4 (0x1 << 4)
-#define ISC_DCFG_YMBSIZE_BEATS8 (0x2 << 4)
-#define ISC_DCFG_YMBSIZE_BEATS16 (0x3 << 4)
-#define ISC_DCFG_YMBSIZE_BEATS32 (0x4 << 4)
-#define ISC_DCFG_YMBSIZE_MASK GENMASK(6, 4)
-
-#define ISC_DCFG_CMBSIZE_SINGLE (0x0 << 8)
-#define ISC_DCFG_CMBSIZE_BEATS4 (0x1 << 8)
-#define ISC_DCFG_CMBSIZE_BEATS8 (0x2 << 8)
-#define ISC_DCFG_CMBSIZE_BEATS16 (0x3 << 8)
-#define ISC_DCFG_CMBSIZE_BEATS32 (0x4 << 8)
-#define ISC_DCFG_CMBSIZE_MASK GENMASK(10, 8)
-
-/* DMA Control Register */
-#define ISC_DCTRL 0x000003e4
-
-#define ISC_DCTRL_DVIEW_PACKED (0x0 << 1)
-#define ISC_DCTRL_DVIEW_SEMIPLANAR (0x1 << 1)
-#define ISC_DCTRL_DVIEW_PLANAR (0x2 << 1)
-#define ISC_DCTRL_DVIEW_MASK GENMASK(2, 1)
-
-#define ISC_DCTRL_IE_IS (0x0 << 4)
-
-/* DMA Descriptor Address Register */
-#define ISC_DNDA 0x000003e8
-
-/* DMA Address 0 Register */
-#define ISC_DAD0 0x000003ec
-
-/* DMA Address 1 Register */
-#define ISC_DAD1 0x000003f4
-
-/* DMA Address 2 Register */
-#define ISC_DAD2 0x000003fc
-
-/* Offset for version register specific to sama5d2 product */
-#define ISC_SAMA5D2_VERSION_OFFSET 0
-#define ISC_SAMA7G5_VERSION_OFFSET 0x13c
-/* Version Register */
-#define ISC_VERSION 0x0000040c
-
-/* Offset for version register specific to sama5d2 product */
-#define ISC_SAMA5D2_HIS_ENTRY_OFFSET 0
-/* Offset for version register specific to sama7g5 product */
-#define ISC_SAMA7G5_HIS_ENTRY_OFFSET 0x14c
-/* Histogram Entry */
-#define ISC_HIS_ENTRY 0x00000410
-
-#endif
diff --git a/drivers/staging/media/deprecated/atmel/atmel-isc.h b/drivers/staging/media/deprecated/atmel/atmel-isc.h
deleted file mode 100644
index 31767ea74be6..000000000000
--- a/drivers/staging/media/deprecated/atmel/atmel-isc.h
+++ /dev/null
@@ -1,362 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Microchip Image Sensor Controller (ISC) driver header file
- *
- * Copyright (C) 2016-2019 Microchip Technology, Inc.
- *
- * Author: Songjun Wu
- * Author: Eugen Hristev <eugen.hristev@microchip.com>
- *
- */
-#ifndef _ATMEL_ISC_H_
-
-#include <linux/clk-provider.h>
-#include <linux/platform_device.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/videobuf2-dma-contig.h>
-
-#define ISC_CLK_MAX_DIV 255
-
-enum isc_clk_id {
- ISC_ISPCK = 0,
- ISC_MCK = 1,
-};
-
-struct isc_clk {
- struct clk_hw hw;
- struct clk *clk;
- struct regmap *regmap;
- spinlock_t lock; /* serialize access to clock registers */
- u8 id;
- u8 parent_id;
- u32 div;
- struct device *dev;
-};
-
-#define to_isc_clk(v) container_of(v, struct isc_clk, hw)
-
-struct isc_buffer {
- struct vb2_v4l2_buffer vb;
- struct list_head list;
-};
-
-struct isc_subdev_entity {
- struct v4l2_subdev *sd;
- struct v4l2_async_connection *asd;
- struct device_node *epn;
- struct v4l2_async_notifier notifier;
-
- u32 pfe_cfg0;
-
- struct list_head list;
-};
-
-/*
- * struct isc_format - ISC media bus format information
- This structure represents the interface between the ISC
- and the sensor. It's the input format received by
- the ISC.
- * @fourcc: Fourcc code for this format
- * @mbus_code: V4L2 media bus format code.
- * @cfa_baycfg: If this format is RAW BAYER, indicate the type of bayer.
- this is either BGBG, RGRG, etc.
- * @pfe_cfg0_bps: Number of hardware data lines connected to the ISC
- */
-
-struct isc_format {
- u32 fourcc;
- u32 mbus_code;
- u32 cfa_baycfg;
-
- bool sd_support;
- u32 pfe_cfg0_bps;
-};
-
-/* Pipeline bitmap */
-#define DPC_DPCENABLE BIT(0)
-#define DPC_GDCENABLE BIT(1)
-#define DPC_BLCENABLE BIT(2)
-#define WB_ENABLE BIT(3)
-#define CFA_ENABLE BIT(4)
-#define CC_ENABLE BIT(5)
-#define GAM_ENABLE BIT(6)
-#define GAM_BENABLE BIT(7)
-#define GAM_GENABLE BIT(8)
-#define GAM_RENABLE BIT(9)
-#define VHXS_ENABLE BIT(10)
-#define CSC_ENABLE BIT(11)
-#define CBC_ENABLE BIT(12)
-#define SUB422_ENABLE BIT(13)
-#define SUB420_ENABLE BIT(14)
-
-#define GAM_ENABLES (GAM_RENABLE | GAM_GENABLE | GAM_BENABLE | GAM_ENABLE)
-
-/*
- * struct fmt_config - ISC format configuration and internal pipeline
- This structure represents the internal configuration
- of the ISC.
- It also holds the format that ISC will present to v4l2.
- * @sd_format: Pointer to an isc_format struct that holds the sensor
- configuration.
- * @fourcc: Fourcc code for this format.
- * @bpp: Bytes per pixel in the current format.
- * @bpp_v4l2: Bytes per pixel in the current format, for v4l2.
- This differs from 'bpp' in the sense that in planar
- formats, it refers only to the first plane.
- * @rlp_cfg_mode: Configuration of the RLP (rounding, limiting packaging)
- * @dcfg_imode: Configuration of the input of the DMA module
- * @dctrl_dview: Configuration of the output of the DMA module
- * @bits_pipeline: Configuration of the pipeline, which modules are enabled
- */
-struct fmt_config {
- struct isc_format *sd_format;
-
- u32 fourcc;
- u8 bpp;
- u8 bpp_v4l2;
-
- u32 rlp_cfg_mode;
- u32 dcfg_imode;
- u32 dctrl_dview;
-
- u32 bits_pipeline;
-};
-
-#define HIST_ENTRIES 512
-#define HIST_BAYER (ISC_HIS_CFG_MODE_B + 1)
-
-enum{
- HIST_INIT = 0,
- HIST_ENABLED,
- HIST_DISABLED,
-};
-
-struct isc_ctrls {
- struct v4l2_ctrl_handler handler;
-
- u32 brightness;
- u32 contrast;
- u8 gamma_index;
-#define ISC_WB_NONE 0
-#define ISC_WB_AUTO 1
-#define ISC_WB_ONETIME 2
- u8 awb;
-
- /* one for each component : GR, R, GB, B */
- u32 gain[HIST_BAYER];
- s32 offset[HIST_BAYER];
-
- u32 hist_entry[HIST_ENTRIES];
- u32 hist_count[HIST_BAYER];
- u8 hist_id;
- u8 hist_stat;
-#define HIST_MIN_INDEX 0
-#define HIST_MAX_INDEX 1
- u32 hist_minmax[HIST_BAYER][2];
-};
-
-#define ISC_PIPE_LINE_NODE_NUM 15
-
-/*
- * struct isc_reg_offsets - ISC device register offsets
- * @csc: Offset for the CSC register
- * @cbc: Offset for the CBC register
- * @sub422: Offset for the SUB422 register
- * @sub420: Offset for the SUB420 register
- * @rlp: Offset for the RLP register
- * @his: Offset for the HIS related registers
- * @dma: Offset for the DMA related registers
- * @version: Offset for the version register
- * @his_entry: Offset for the HIS entries registers
- */
-struct isc_reg_offsets {
- u32 csc;
- u32 cbc;
- u32 sub422;
- u32 sub420;
- u32 rlp;
- u32 his;
- u32 dma;
- u32 version;
- u32 his_entry;
-};
-
-/*
- * struct isc_device - ISC device driver data/config struct
- * @regmap: Register map
- * @hclock: Hclock clock input (refer datasheet)
- * @ispck: iscpck clock (refer datasheet)
- * @isc_clks: ISC clocks
- * @ispck_required: ISC requires ISP Clock initialization
- * @dcfg: DMA master configuration, architecture dependent
- *
- * @dev: Registered device driver
- * @v4l2_dev: v4l2 registered device
- * @video_dev: registered video device
- *
- * @vb2_vidq: video buffer 2 video queue
- * @dma_queue_lock: lock to serialize the dma buffer queue
- * @dma_queue: the queue for dma buffers
- * @cur_frm: current isc frame/buffer
- * @sequence: current frame number
- * @stop: true if isc is not streaming, false if streaming
- * @comp: completion reference that signals frame completion
- *
- * @fmt: current v42l format
- * @user_formats: list of formats that are supported and agreed with sd
- * @num_user_formats: how many formats are in user_formats
- *
- * @config: current ISC format configuration
- * @try_config: the current ISC try format , not yet activated
- *
- * @ctrls: holds information about ISC controls
- * @do_wb_ctrl: control regarding the DO_WHITE_BALANCE button
- * @awb_work: workqueue reference for autowhitebalance histogram
- * analysis
- *
- * @lock: lock for serializing userspace file operations
- * with ISC operations
- * @awb_mutex: serialize access to streaming status from awb work queue
- * @awb_lock: lock for serializing awb work queue operations
- * with DMA/buffer operations
- *
- * @pipeline: configuration of the ISC pipeline
- *
- * @current_subdev: current subdevice: the sensor
- * @subdev_entities: list of subdevice entitites
- *
- * @gamma_table: pointer to the table with gamma values, has
- * gamma_max sets of GAMMA_ENTRIES entries each
- * @gamma_max: maximum number of sets of inside the gamma_table
- *
- * @max_width: maximum frame width, dependent on the internal RAM
- * @max_height: maximum frame height, dependent on the internal RAM
- *
- * @config_dpc: pointer to a function that initializes product
- * specific DPC module
- * @config_csc: pointer to a function that initializes product
- * specific CSC module
- * @config_cbc: pointer to a function that initializes product
- * specific CBC module
- * @config_cc: pointer to a function that initializes product
- * specific CC module
- * @config_gam: pointer to a function that initializes product
- * specific GAMMA module
- * @config_rlp: pointer to a function that initializes product
- * specific RLP module
- * @config_ctrls: pointer to a functoin that initializes product
- * specific v4l2 controls.
- *
- * @adapt_pipeline: pointer to a function that adapts the pipeline bits
- * to the product specific pipeline
- *
- * @offsets: struct holding the product specific register offsets
- * @controller_formats: pointer to the array of possible formats that the
- * controller can output
- * @formats_list: pointer to the array of possible formats that can
- * be used as an input to the controller
- * @controller_formats_size: size of controller_formats array
- * @formats_list_size: size of formats_list array
- */
-struct isc_device {
- struct regmap *regmap;
- struct clk *hclock;
- struct clk *ispck;
- struct isc_clk isc_clks[2];
- bool ispck_required;
- u32 dcfg;
-
- struct device *dev;
- struct v4l2_device v4l2_dev;
- struct video_device video_dev;
-
- struct vb2_queue vb2_vidq;
- spinlock_t dma_queue_lock;
- struct list_head dma_queue;
- struct isc_buffer *cur_frm;
- unsigned int sequence;
- bool stop;
- struct completion comp;
-
- struct v4l2_format fmt;
- struct isc_format **user_formats;
- unsigned int num_user_formats;
-
- struct fmt_config config;
- struct fmt_config try_config;
-
- struct isc_ctrls ctrls;
- struct work_struct awb_work;
-
- struct mutex lock;
- struct mutex awb_mutex;
- spinlock_t awb_lock;
-
- struct regmap_field *pipeline[ISC_PIPE_LINE_NODE_NUM];
-
- struct isc_subdev_entity *current_subdev;
- struct list_head subdev_entities;
-
- struct {
-#define ISC_CTRL_DO_WB 1
-#define ISC_CTRL_R_GAIN 2
-#define ISC_CTRL_B_GAIN 3
-#define ISC_CTRL_GR_GAIN 4
-#define ISC_CTRL_GB_GAIN 5
-#define ISC_CTRL_R_OFF 6
-#define ISC_CTRL_B_OFF 7
-#define ISC_CTRL_GR_OFF 8
-#define ISC_CTRL_GB_OFF 9
- struct v4l2_ctrl *awb_ctrl;
- struct v4l2_ctrl *do_wb_ctrl;
- struct v4l2_ctrl *r_gain_ctrl;
- struct v4l2_ctrl *b_gain_ctrl;
- struct v4l2_ctrl *gr_gain_ctrl;
- struct v4l2_ctrl *gb_gain_ctrl;
- struct v4l2_ctrl *r_off_ctrl;
- struct v4l2_ctrl *b_off_ctrl;
- struct v4l2_ctrl *gr_off_ctrl;
- struct v4l2_ctrl *gb_off_ctrl;
- };
-
-#define GAMMA_ENTRIES 64
- /* pointer to the defined gamma table */
- const u32 (*gamma_table)[GAMMA_ENTRIES];
- u32 gamma_max;
-
- u32 max_width;
- u32 max_height;
-
- struct {
- void (*config_dpc)(struct isc_device *isc);
- void (*config_csc)(struct isc_device *isc);
- void (*config_cbc)(struct isc_device *isc);
- void (*config_cc)(struct isc_device *isc);
- void (*config_gam)(struct isc_device *isc);
- void (*config_rlp)(struct isc_device *isc);
-
- void (*config_ctrls)(struct isc_device *isc,
- const struct v4l2_ctrl_ops *ops);
-
- void (*adapt_pipeline)(struct isc_device *isc);
- };
-
- struct isc_reg_offsets offsets;
- const struct isc_format *controller_formats;
- struct isc_format *formats_list;
- u32 controller_formats_size;
- u32 formats_list_size;
-};
-
-extern const struct regmap_config atmel_isc_regmap_config;
-extern const struct v4l2_async_notifier_operations atmel_isc_async_ops;
-
-irqreturn_t atmel_isc_interrupt(int irq, void *dev_id);
-int atmel_isc_pipeline_init(struct isc_device *isc);
-int atmel_isc_clk_init(struct isc_device *isc);
-void atmel_isc_subdev_cleanup(struct isc_device *isc);
-void atmel_isc_clk_cleanup(struct isc_device *isc);
-
-#endif
diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c
deleted file mode 100644
index 71e6e278a4b3..000000000000
--- a/drivers/staging/media/deprecated/atmel/atmel-sama5d2-isc.c
+++ /dev/null
@@ -1,644 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Microchip Image Sensor Controller (ISC) driver
- *
- * Copyright (C) 2016-2019 Microchip Technology, Inc.
- *
- * Author: Songjun Wu
- * Author: Eugen Hristev <eugen.hristev@microchip.com>
- *
- *
- * Sensor-->PFE-->WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB-->RLP-->DMA
- *
- * ISC video pipeline integrates the following submodules:
- * PFE: Parallel Front End to sample the camera sensor input stream
- * WB: Programmable white balance in the Bayer domain
- * CFA: Color filter array interpolation module
- * CC: Programmable color correction
- * GAM: Gamma correction
- * CSC: Programmable color space conversion
- * CBC: Contrast and Brightness control
- * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
- * RLP: This module performs rounding, range limiting
- * and packing of the incoming data
- */
-
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/math64.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/videodev2.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-image-sizes.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fwnode.h>
-#include <media/v4l2-subdev.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "atmel-isc-regs.h"
-#include "atmel-isc.h"
-
-#define ISC_SAMA5D2_MAX_SUPPORT_WIDTH 2592
-#define ISC_SAMA5D2_MAX_SUPPORT_HEIGHT 1944
-
-#define ISC_SAMA5D2_PIPELINE \
- (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
- CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
-
-/* This is a list of the formats that the ISC can *output* */
-static const struct isc_format sama5d2_controller_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_ARGB444,
- }, {
- .fourcc = V4L2_PIX_FMT_ARGB555,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB565,
- }, {
- .fourcc = V4L2_PIX_FMT_ABGR32,
- }, {
- .fourcc = V4L2_PIX_FMT_XBGR32,
- }, {
- .fourcc = V4L2_PIX_FMT_YUV420,
- }, {
- .fourcc = V4L2_PIX_FMT_YUYV,
- }, {
- .fourcc = V4L2_PIX_FMT_YUV422P,
- }, {
- .fourcc = V4L2_PIX_FMT_GREY,
- }, {
- .fourcc = V4L2_PIX_FMT_Y10,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG10,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB10,
- },
-};
-
-/* This is a list of formats that the ISC can receive as *input* */
-static struct isc_format sama5d2_formats_list[] = {
- {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_BGBG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_GBGB,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_GRGR,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG10,
- .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_GBGB,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_GRGR,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB10,
- .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR12,
- .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_BGBG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG12,
- .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_GBGB,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG12,
- .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_GRGR,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB12,
- .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_GREY,
- .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB565,
- .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- },
- {
- .fourcc = V4L2_PIX_FMT_Y10,
- .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- },
-
-};
-
-static void isc_sama5d2_config_csc(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
-
- /* Convert RGB to YUV */
- regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
- 0x42 | (0x81 << 16));
- regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
- 0x19 | (0x10 << 16));
- regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
- 0xFDA | (0xFB6 << 16));
- regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
- 0x70 | (0x80 << 16));
- regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
- 0x70 | (0xFA2 << 16));
- regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
- 0xFEE | (0x80 << 16));
-}
-
-static void isc_sama5d2_config_cbc(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
-
- regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc,
- isc->ctrls.brightness);
- regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc,
- isc->ctrls.contrast);
-}
-
-static void isc_sama5d2_config_cc(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
-
- /* Configure each register at the neutral fixed point 1.0 or 0.0 */
- regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
- regmap_write(regmap, ISC_CC_RB_OR, 0);
- regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
- regmap_write(regmap, ISC_CC_GB_OG, 0);
- regmap_write(regmap, ISC_CC_BR_BG, 0);
- regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
-}
-
-static void isc_sama5d2_config_ctrls(struct isc_device *isc,
- const struct v4l2_ctrl_ops *ops)
-{
- struct isc_ctrls *ctrls = &isc->ctrls;
- struct v4l2_ctrl_handler *hdl = &ctrls->handler;
-
- ctrls->contrast = 256;
-
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 256);
-}
-
-static void isc_sama5d2_config_dpc(struct isc_device *isc)
-{
- /* This module is not present on sama5d2 pipeline */
-}
-
-static void isc_sama5d2_config_gam(struct isc_device *isc)
-{
- /* No specific gamma configuration */
-}
-
-static void isc_sama5d2_config_rlp(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
- u32 rlp_mode = isc->config.rlp_cfg_mode;
-
- /*
- * In sama5d2, the YUV planar modes and the YUYV modes are treated
- * in the same way in RLP register.
- * Normally, YYCC mode should be Luma(n) - Color B(n) - Color R (n)
- * and YCYC should be Luma(n + 1) - Color B (n) - Luma (n) - Color R (n)
- * but in sama5d2, the YCYC mode does not exist, and YYCC must be
- * selected for both planar and interleaved modes, as in fact
- * both modes are supported.
- *
- * Thus, if the YCYC mode is selected, replace it with the
- * sama5d2-compliant mode which is YYCC .
- */
- if ((rlp_mode & ISC_RLP_CFG_MODE_MASK) == ISC_RLP_CFG_MODE_YCYC) {
- rlp_mode &= ~ISC_RLP_CFG_MODE_MASK;
- rlp_mode |= ISC_RLP_CFG_MODE_YYCC;
- }
-
- regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
- ISC_RLP_CFG_MODE_MASK, rlp_mode);
-}
-
-static void isc_sama5d2_adapt_pipeline(struct isc_device *isc)
-{
- isc->try_config.bits_pipeline &= ISC_SAMA5D2_PIPELINE;
-}
-
-/* Gamma table with gamma 1/2.2 */
-static const u32 isc_sama5d2_gamma_table[][GAMMA_ENTRIES] = {
- /* 0 --> gamma 1/1.8 */
- { 0x65, 0x66002F, 0x950025, 0xBB0020, 0xDB001D, 0xF8001A,
- 0x1130018, 0x12B0017, 0x1420016, 0x1580014, 0x16D0013, 0x1810012,
- 0x1940012, 0x1A60012, 0x1B80011, 0x1C90010, 0x1DA0010, 0x1EA000F,
- 0x1FA000F, 0x209000F, 0x218000F, 0x227000E, 0x235000E, 0x243000E,
- 0x251000E, 0x25F000D, 0x26C000D, 0x279000D, 0x286000D, 0x293000C,
- 0x2A0000C, 0x2AC000C, 0x2B8000C, 0x2C4000C, 0x2D0000B, 0x2DC000B,
- 0x2E7000B, 0x2F3000B, 0x2FE000B, 0x309000B, 0x314000B, 0x31F000A,
- 0x32A000A, 0x334000B, 0x33F000A, 0x349000A, 0x354000A, 0x35E000A,
- 0x368000A, 0x372000A, 0x37C000A, 0x386000A, 0x3900009, 0x399000A,
- 0x3A30009, 0x3AD0009, 0x3B60009, 0x3BF000A, 0x3C90009, 0x3D20009,
- 0x3DB0009, 0x3E40009, 0x3ED0009, 0x3F60009 },
-
- /* 1 --> gamma 1/2 */
- { 0x7F, 0x800034, 0xB50028, 0xDE0021, 0x100001E, 0x11E001B,
- 0x1390019, 0x1520017, 0x16A0015, 0x1800014, 0x1940014, 0x1A80013,
- 0x1BB0012, 0x1CD0011, 0x1DF0010, 0x1EF0010, 0x200000F, 0x20F000F,
- 0x21F000E, 0x22D000F, 0x23C000E, 0x24A000E, 0x258000D, 0x265000D,
- 0x273000C, 0x27F000D, 0x28C000C, 0x299000C, 0x2A5000C, 0x2B1000B,
- 0x2BC000C, 0x2C8000B, 0x2D3000C, 0x2DF000B, 0x2EA000A, 0x2F5000A,
- 0x2FF000B, 0x30A000A, 0x314000B, 0x31F000A, 0x329000A, 0x333000A,
- 0x33D0009, 0x3470009, 0x350000A, 0x35A0009, 0x363000A, 0x36D0009,
- 0x3760009, 0x37F0009, 0x3880009, 0x3910009, 0x39A0009, 0x3A30009,
- 0x3AC0008, 0x3B40009, 0x3BD0008, 0x3C60008, 0x3CE0008, 0x3D60009,
- 0x3DF0008, 0x3E70008, 0x3EF0008, 0x3F70008 },
-
- /* 2 --> gamma 1/2.2 */
- { 0x99, 0x9B0038, 0xD4002A, 0xFF0023, 0x122001F, 0x141001B,
- 0x15D0019, 0x1760017, 0x18E0015, 0x1A30015, 0x1B80013, 0x1CC0012,
- 0x1DE0011, 0x1F00010, 0x2010010, 0x2110010, 0x221000F, 0x230000F,
- 0x23F000E, 0x24D000E, 0x25B000D, 0x269000C, 0x276000C, 0x283000C,
- 0x28F000C, 0x29B000C, 0x2A7000C, 0x2B3000B, 0x2BF000B, 0x2CA000B,
- 0x2D5000B, 0x2E0000A, 0x2EB000A, 0x2F5000A, 0x2FF000A, 0x30A000A,
- 0x3140009, 0x31E0009, 0x327000A, 0x3310009, 0x33A0009, 0x3440009,
- 0x34D0009, 0x3560009, 0x35F0009, 0x3680008, 0x3710008, 0x3790009,
- 0x3820008, 0x38A0008, 0x3930008, 0x39B0008, 0x3A30008, 0x3AB0008,
- 0x3B30008, 0x3BB0008, 0x3C30008, 0x3CB0007, 0x3D20008, 0x3DA0007,
- 0x3E20007, 0x3E90007, 0x3F00008, 0x3F80007 },
-};
-
-static int isc_parse_dt(struct device *dev, struct isc_device *isc)
-{
- struct device_node *np = dev->of_node;
- struct device_node *epn;
- struct isc_subdev_entity *subdev_entity;
- unsigned int flags;
- int ret = -EINVAL;
-
- INIT_LIST_HEAD(&isc->subdev_entities);
-
- for_each_endpoint_of_node(np, epn) {
- struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
-
- ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
- &v4l2_epn);
- if (ret) {
- ret = -EINVAL;
- dev_err(dev, "Could not parse the endpoint\n");
- break;
- }
-
- subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
- GFP_KERNEL);
- if (!subdev_entity) {
- ret = -ENOMEM;
- break;
- }
- subdev_entity->epn = epn;
-
- flags = v4l2_epn.bus.parallel.flags;
-
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
-
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
-
- if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
-
- if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
- subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
- ISC_PFE_CFG0_CCIR656;
-
- list_add_tail(&subdev_entity->list, &isc->subdev_entities);
- }
- of_node_put(epn);
-
- return ret;
-}
-
-static int atmel_isc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct isc_device *isc;
- void __iomem *io_base;
- struct isc_subdev_entity *subdev_entity;
- int irq;
- int ret;
- u32 ver;
-
- isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
- if (!isc)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, isc);
- isc->dev = dev;
-
- io_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(io_base))
- return PTR_ERR(io_base);
-
- isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config);
- if (IS_ERR(isc->regmap)) {
- ret = PTR_ERR(isc->regmap);
- dev_err(dev, "failed to init register map: %d\n", ret);
- return ret;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
- ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0,
- "atmel-sama5d2-isc", isc);
- if (ret < 0) {
- dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
- irq, ret);
- return ret;
- }
-
- isc->gamma_table = isc_sama5d2_gamma_table;
- isc->gamma_max = 2;
-
- isc->max_width = ISC_SAMA5D2_MAX_SUPPORT_WIDTH;
- isc->max_height = ISC_SAMA5D2_MAX_SUPPORT_HEIGHT;
-
- isc->config_dpc = isc_sama5d2_config_dpc;
- isc->config_csc = isc_sama5d2_config_csc;
- isc->config_cbc = isc_sama5d2_config_cbc;
- isc->config_cc = isc_sama5d2_config_cc;
- isc->config_gam = isc_sama5d2_config_gam;
- isc->config_rlp = isc_sama5d2_config_rlp;
- isc->config_ctrls = isc_sama5d2_config_ctrls;
-
- isc->adapt_pipeline = isc_sama5d2_adapt_pipeline;
-
- isc->offsets.csc = ISC_SAMA5D2_CSC_OFFSET;
- isc->offsets.cbc = ISC_SAMA5D2_CBC_OFFSET;
- isc->offsets.sub422 = ISC_SAMA5D2_SUB422_OFFSET;
- isc->offsets.sub420 = ISC_SAMA5D2_SUB420_OFFSET;
- isc->offsets.rlp = ISC_SAMA5D2_RLP_OFFSET;
- isc->offsets.his = ISC_SAMA5D2_HIS_OFFSET;
- isc->offsets.dma = ISC_SAMA5D2_DMA_OFFSET;
- isc->offsets.version = ISC_SAMA5D2_VERSION_OFFSET;
- isc->offsets.his_entry = ISC_SAMA5D2_HIS_ENTRY_OFFSET;
-
- isc->controller_formats = sama5d2_controller_formats;
- isc->controller_formats_size = ARRAY_SIZE(sama5d2_controller_formats);
- isc->formats_list = sama5d2_formats_list;
- isc->formats_list_size = ARRAY_SIZE(sama5d2_formats_list);
-
- /* sama5d2-isc - 8 bits per beat */
- isc->dcfg = ISC_DCFG_YMBSIZE_BEATS8 | ISC_DCFG_CMBSIZE_BEATS8;
-
- /* sama5d2-isc : ISPCK is required and mandatory */
- isc->ispck_required = true;
-
- ret = atmel_isc_pipeline_init(isc);
- if (ret)
- return ret;
-
- isc->hclock = devm_clk_get(dev, "hclock");
- if (IS_ERR(isc->hclock)) {
- ret = PTR_ERR(isc->hclock);
- dev_err(dev, "failed to get hclock: %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(isc->hclock);
- if (ret) {
- dev_err(dev, "failed to enable hclock: %d\n", ret);
- return ret;
- }
-
- ret = atmel_isc_clk_init(isc);
- if (ret) {
- dev_err(dev, "failed to init isc clock: %d\n", ret);
- goto unprepare_hclk;
- }
- ret = v4l2_device_register(dev, &isc->v4l2_dev);
- if (ret) {
- dev_err(dev, "unable to register v4l2 device.\n");
- goto unprepare_clk;
- }
-
- ret = isc_parse_dt(dev, isc);
- if (ret) {
- dev_err(dev, "fail to parse device tree\n");
- goto unregister_v4l2_device;
- }
-
- if (list_empty(&isc->subdev_entities)) {
- dev_err(dev, "no subdev found\n");
- ret = -ENODEV;
- goto unregister_v4l2_device;
- }
-
- list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
- struct v4l2_async_connection *asd;
- struct fwnode_handle *fwnode =
- of_fwnode_handle(subdev_entity->epn);
-
- v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);
-
- asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
- fwnode,
- struct v4l2_async_connection);
-
- of_node_put(subdev_entity->epn);
- subdev_entity->epn = NULL;
-
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
- goto cleanup_subdev;
- }
-
- subdev_entity->notifier.ops = &atmel_isc_async_ops;
-
- ret = v4l2_async_nf_register(&subdev_entity->notifier);
- if (ret) {
- dev_err(dev, "fail to register async notifier\n");
- goto cleanup_subdev;
- }
-
- if (video_is_registered(&isc->video_dev))
- break;
- }
-
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- pm_request_idle(dev);
-
- isc->ispck = isc->isc_clks[ISC_ISPCK].clk;
-
- ret = clk_prepare_enable(isc->ispck);
- if (ret) {
- dev_err(dev, "failed to enable ispck: %d\n", ret);
- goto disable_pm;
- }
-
- /* ispck should be greater or equal to hclock */
- ret = clk_set_rate(isc->ispck, clk_get_rate(isc->hclock));
- if (ret) {
- dev_err(dev, "failed to set ispck rate: %d\n", ret);
- goto unprepare_clk;
- }
-
- regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
- dev_info(dev, "Microchip ISC version %x\n", ver);
-
- return 0;
-
-unprepare_clk:
- clk_disable_unprepare(isc->ispck);
-
-disable_pm:
- pm_runtime_disable(dev);
-
-cleanup_subdev:
- atmel_isc_subdev_cleanup(isc);
-
-unregister_v4l2_device:
- v4l2_device_unregister(&isc->v4l2_dev);
-
-unprepare_hclk:
- clk_disable_unprepare(isc->hclock);
-
- atmel_isc_clk_cleanup(isc);
-
- return ret;
-}
-
-static void atmel_isc_remove(struct platform_device *pdev)
-{
- struct isc_device *isc = platform_get_drvdata(pdev);
-
- pm_runtime_disable(&pdev->dev);
-
- atmel_isc_subdev_cleanup(isc);
-
- v4l2_device_unregister(&isc->v4l2_dev);
-
- clk_disable_unprepare(isc->ispck);
- clk_disable_unprepare(isc->hclock);
-
- atmel_isc_clk_cleanup(isc);
-}
-
-static int __maybe_unused isc_runtime_suspend(struct device *dev)
-{
- struct isc_device *isc = dev_get_drvdata(dev);
-
- clk_disable_unprepare(isc->ispck);
- clk_disable_unprepare(isc->hclock);
-
- return 0;
-}
-
-static int __maybe_unused isc_runtime_resume(struct device *dev)
-{
- struct isc_device *isc = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(isc->hclock);
- if (ret)
- return ret;
-
- ret = clk_prepare_enable(isc->ispck);
- if (ret)
- clk_disable_unprepare(isc->hclock);
-
- return ret;
-}
-
-static const struct dev_pm_ops atmel_isc_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(isc_runtime_suspend, isc_runtime_resume, NULL)
-};
-
-#if IS_ENABLED(CONFIG_OF)
-static const struct of_device_id atmel_isc_of_match[] = {
- { .compatible = "atmel,sama5d2-isc" },
- { }
-};
-MODULE_DEVICE_TABLE(of, atmel_isc_of_match);
-#endif
-
-static struct platform_driver atmel_isc_driver = {
- .probe = atmel_isc_probe,
- .remove = atmel_isc_remove,
- .driver = {
- .name = "atmel-sama5d2-isc",
- .pm = &atmel_isc_dev_pm_ops,
- .of_match_table = of_match_ptr(atmel_isc_of_match),
- },
-};
-
-module_platform_driver(atmel_isc_driver);
-
-MODULE_AUTHOR("Songjun Wu");
-MODULE_DESCRIPTION("The V4L2 driver for Atmel-ISC");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c b/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c
deleted file mode 100644
index 1f74c2dd044c..000000000000
--- a/drivers/staging/media/deprecated/atmel/atmel-sama7g5-isc.c
+++ /dev/null
@@ -1,607 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Microchip eXtended Image Sensor Controller (XISC) driver
- *
- * Copyright (C) 2019-2021 Microchip Technology, Inc. and its subsidiaries
- *
- * Author: Eugen Hristev <eugen.hristev@microchip.com>
- *
- * Sensor-->PFE-->DPC-->WB-->CFA-->CC-->GAM-->VHXS-->CSC-->CBHS-->SUB-->RLP-->DMA-->HIS
- *
- * ISC video pipeline integrates the following submodules:
- * PFE: Parallel Front End to sample the camera sensor input stream
- * DPC: Defective Pixel Correction with black offset correction, green disparity
- * correction and defective pixel correction (3 modules total)
- * WB: Programmable white balance in the Bayer domain
- * CFA: Color filter array interpolation module
- * CC: Programmable color correction
- * GAM: Gamma correction
- *VHXS: Vertical and Horizontal Scaler
- * CSC: Programmable color space conversion
- *CBHS: Contrast Brightness Hue and Saturation control
- * SUB: This module performs YCbCr444 to YCbCr420 chrominance subsampling
- * RLP: This module performs rounding, range limiting
- * and packing of the incoming data
- * DMA: This module performs DMA master accesses to write frames to external RAM
- * HIS: Histogram module performs statistic counters on the frames
- */
-
-#include <linux/clk.h>
-#include <linux/clkdev.h>
-#include <linux/clk-provider.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/math64.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_graph.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/regmap.h>
-#include <linux/videodev2.h>
-
-#include <media/v4l2-ctrls.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
-#include <media/v4l2-image-sizes.h>
-#include <media/v4l2-ioctl.h>
-#include <media/v4l2-fwnode.h>
-#include <media/v4l2-subdev.h>
-#include <media/videobuf2-dma-contig.h>
-
-#include "atmel-isc-regs.h"
-#include "atmel-isc.h"
-
-#define ISC_SAMA7G5_MAX_SUPPORT_WIDTH 3264
-#define ISC_SAMA7G5_MAX_SUPPORT_HEIGHT 2464
-
-#define ISC_SAMA7G5_PIPELINE \
- (WB_ENABLE | CFA_ENABLE | CC_ENABLE | GAM_ENABLES | CSC_ENABLE | \
- CBC_ENABLE | SUB422_ENABLE | SUB420_ENABLE)
-
-/* This is a list of the formats that the ISC can *output* */
-static const struct isc_format sama7g5_controller_formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_ARGB444,
- }, {
- .fourcc = V4L2_PIX_FMT_ARGB555,
- }, {
- .fourcc = V4L2_PIX_FMT_RGB565,
- }, {
- .fourcc = V4L2_PIX_FMT_ABGR32,
- }, {
- .fourcc = V4L2_PIX_FMT_XBGR32,
- }, {
- .fourcc = V4L2_PIX_FMT_YUV420,
- }, {
- .fourcc = V4L2_PIX_FMT_UYVY,
- }, {
- .fourcc = V4L2_PIX_FMT_VYUY,
- }, {
- .fourcc = V4L2_PIX_FMT_YUYV,
- }, {
- .fourcc = V4L2_PIX_FMT_YUV422P,
- }, {
- .fourcc = V4L2_PIX_FMT_GREY,
- }, {
- .fourcc = V4L2_PIX_FMT_Y10,
- }, {
- .fourcc = V4L2_PIX_FMT_Y16,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- }, {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- }, {
- .fourcc = V4L2_PIX_FMT_SGBRG10,
- }, {
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- }, {
- .fourcc = V4L2_PIX_FMT_SRGGB10,
- },
-};
-
-/* This is a list of formats that the ISC can receive as *input* */
-static struct isc_format sama7g5_formats_list[] = {
- {
- .fourcc = V4L2_PIX_FMT_SBGGR8,
- .mbus_code = MEDIA_BUS_FMT_SBGGR8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_BGBG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG8,
- .mbus_code = MEDIA_BUS_FMT_SGBRG8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_GBGB,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG8,
- .mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_GRGR,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB8,
- .mbus_code = MEDIA_BUS_FMT_SRGGB8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR10,
- .mbus_code = MEDIA_BUS_FMT_SBGGR10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG10,
- .mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_GBGB,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG10,
- .mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_GRGR,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB10,
- .mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SBGGR12,
- .mbus_code = MEDIA_BUS_FMT_SBGGR12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_BGBG,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGBRG12,
- .mbus_code = MEDIA_BUS_FMT_SGBRG12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_GBGB,
- },
- {
- .fourcc = V4L2_PIX_FMT_SGRBG12,
- .mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_GRGR,
- },
- {
- .fourcc = V4L2_PIX_FMT_SRGGB12,
- .mbus_code = MEDIA_BUS_FMT_SRGGB12_1X12,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TWELVE,
- .cfa_baycfg = ISC_BAY_CFG_RGRG,
- },
- {
- .fourcc = V4L2_PIX_FMT_GREY,
- .mbus_code = MEDIA_BUS_FMT_Y8_1X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUYV,
- .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- },
- {
- .fourcc = V4L2_PIX_FMT_UYVY,
- .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB565,
- .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE,
- .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_EIGHT,
- },
- {
- .fourcc = V4L2_PIX_FMT_Y10,
- .mbus_code = MEDIA_BUS_FMT_Y10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
- },
-};
-
-static void isc_sama7g5_config_csc(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
-
- /* Convert RGB to YUV */
- regmap_write(regmap, ISC_CSC_YR_YG + isc->offsets.csc,
- 0x42 | (0x81 << 16));
- regmap_write(regmap, ISC_CSC_YB_OY + isc->offsets.csc,
- 0x19 | (0x10 << 16));
- regmap_write(regmap, ISC_CSC_CBR_CBG + isc->offsets.csc,
- 0xFDA | (0xFB6 << 16));
- regmap_write(regmap, ISC_CSC_CBB_OCB + isc->offsets.csc,
- 0x70 | (0x80 << 16));
- regmap_write(regmap, ISC_CSC_CRR_CRG + isc->offsets.csc,
- 0x70 | (0xFA2 << 16));
- regmap_write(regmap, ISC_CSC_CRB_OCR + isc->offsets.csc,
- 0xFEE | (0x80 << 16));
-}
-
-static void isc_sama7g5_config_cbc(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
-
- /* Configure what is set via v4l2 ctrls */
- regmap_write(regmap, ISC_CBC_BRIGHT + isc->offsets.cbc, isc->ctrls.brightness);
- regmap_write(regmap, ISC_CBC_CONTRAST + isc->offsets.cbc, isc->ctrls.contrast);
- /* Configure Hue and Saturation as neutral midpoint */
- regmap_write(regmap, ISC_CBCHS_HUE, 0);
- regmap_write(regmap, ISC_CBCHS_SAT, (1 << 4));
-}
-
-static void isc_sama7g5_config_cc(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
-
- /* Configure each register at the neutral fixed point 1.0 or 0.0 */
- regmap_write(regmap, ISC_CC_RR_RG, (1 << 8));
- regmap_write(regmap, ISC_CC_RB_OR, 0);
- regmap_write(regmap, ISC_CC_GR_GG, (1 << 8) << 16);
- regmap_write(regmap, ISC_CC_GB_OG, 0);
- regmap_write(regmap, ISC_CC_BR_BG, 0);
- regmap_write(regmap, ISC_CC_BB_OB, (1 << 8));
-}
-
-static void isc_sama7g5_config_ctrls(struct isc_device *isc,
- const struct v4l2_ctrl_ops *ops)
-{
- struct isc_ctrls *ctrls = &isc->ctrls;
- struct v4l2_ctrl_handler *hdl = &ctrls->handler;
-
- ctrls->contrast = 16;
-
- v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -2048, 2047, 1, 16);
-}
-
-static void isc_sama7g5_config_dpc(struct isc_device *isc)
-{
- u32 bay_cfg = isc->config.sd_format->cfa_baycfg;
- struct regmap *regmap = isc->regmap;
-
- regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BLOFF_MASK,
- (64 << ISC_DPC_CFG_BLOFF_SHIFT));
- regmap_update_bits(regmap, ISC_DPC_CFG, ISC_DPC_CFG_BAYCFG_MASK,
- (bay_cfg << ISC_DPC_CFG_BAYCFG_SHIFT));
-}
-
-static void isc_sama7g5_config_gam(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
-
- regmap_update_bits(regmap, ISC_GAM_CTRL, ISC_GAM_CTRL_BIPART,
- ISC_GAM_CTRL_BIPART);
-}
-
-static void isc_sama7g5_config_rlp(struct isc_device *isc)
-{
- struct regmap *regmap = isc->regmap;
- u32 rlp_mode = isc->config.rlp_cfg_mode;
-
- regmap_update_bits(regmap, ISC_RLP_CFG + isc->offsets.rlp,
- ISC_RLP_CFG_MODE_MASK | ISC_RLP_CFG_LSH |
- ISC_RLP_CFG_YMODE_MASK, rlp_mode);
-}
-
-static void isc_sama7g5_adapt_pipeline(struct isc_device *isc)
-{
- isc->try_config.bits_pipeline &= ISC_SAMA7G5_PIPELINE;
-}
-
-/* Gamma table with gamma 1/2.2 */
-static const u32 isc_sama7g5_gamma_table[][GAMMA_ENTRIES] = {
- /* index 0 --> gamma bipartite */
- {
- 0x980, 0x4c0320, 0x650260, 0x7801e0, 0x8701a0, 0x940180,
- 0xa00160, 0xab0120, 0xb40120, 0xbd0120, 0xc60100, 0xce0100,
- 0xd600e0, 0xdd00e0, 0xe400e0, 0xeb00c0, 0xf100c0, 0xf700c0,
- 0xfd00c0, 0x10300a0, 0x10800c0, 0x10e00a0, 0x11300a0, 0x11800a0,
- 0x11d00a0, 0x12200a0, 0x12700a0, 0x12c0080, 0x13000a0, 0x1350080,
- 0x13900a0, 0x13e0080, 0x1420076, 0x17d0062, 0x1ae0054, 0x1d8004a,
- 0x1fd0044, 0x21f003e, 0x23e003a, 0x25b0036, 0x2760032, 0x28f0030,
- 0x2a7002e, 0x2be002c, 0x2d4002c, 0x2ea0028, 0x2fe0028, 0x3120026,
- 0x3250024, 0x3370024, 0x3490022, 0x35a0022, 0x36b0020, 0x37b0020,
- 0x38b0020, 0x39b001e, 0x3aa001e, 0x3b9001c, 0x3c7001c, 0x3d5001c,
- 0x3e3001c, 0x3f1001c, 0x3ff001a, 0x40c001a },
-};
-
-static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
-{
- struct device_node *np = dev->of_node;
- struct device_node *epn;
- struct isc_subdev_entity *subdev_entity;
- unsigned int flags;
- int ret = -EINVAL;
- bool mipi_mode;
-
- INIT_LIST_HEAD(&isc->subdev_entities);
-
- mipi_mode = of_property_read_bool(np, "microchip,mipi-mode");
-
- for_each_endpoint_of_node(np, epn) {
- struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
-
- ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
- &v4l2_epn);
- if (ret) {
- ret = -EINVAL;
- dev_err(dev, "Could not parse the endpoint\n");
- break;
- }
-
- subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
- GFP_KERNEL);
- if (!subdev_entity) {
- ret = -ENOMEM;
- break;
- }
- subdev_entity->epn = epn;
-
- flags = v4l2_epn.bus.parallel.flags;
-
- if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
- subdev_entity->pfe_cfg0 = ISC_PFE_CFG0_HPOL_LOW;
-
- if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
- subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_VPOL_LOW;
-
- if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
- subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_PPOL_LOW;
-
- if (v4l2_epn.bus_type == V4L2_MBUS_BT656)
- subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_CCIR_CRC |
- ISC_PFE_CFG0_CCIR656;
-
- if (mipi_mode)
- subdev_entity->pfe_cfg0 |= ISC_PFE_CFG0_MIPI;
-
- list_add_tail(&subdev_entity->list, &isc->subdev_entities);
- }
- of_node_put(epn);
-
- return ret;
-}
-
-static int microchip_xisc_probe(struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct isc_device *isc;
- void __iomem *io_base;
- struct isc_subdev_entity *subdev_entity;
- int irq;
- int ret;
- u32 ver;
-
- isc = devm_kzalloc(dev, sizeof(*isc), GFP_KERNEL);
- if (!isc)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, isc);
- isc->dev = dev;
-
- io_base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(io_base))
- return PTR_ERR(io_base);
-
- isc->regmap = devm_regmap_init_mmio(dev, io_base, &atmel_isc_regmap_config);
- if (IS_ERR(isc->regmap)) {
- ret = PTR_ERR(isc->regmap);
- dev_err(dev, "failed to init register map: %d\n", ret);
- return ret;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
-
- ret = devm_request_irq(dev, irq, atmel_isc_interrupt, 0,
- "microchip-sama7g5-xisc", isc);
- if (ret < 0) {
- dev_err(dev, "can't register ISR for IRQ %u (ret=%i)\n",
- irq, ret);
- return ret;
- }
-
- isc->gamma_table = isc_sama7g5_gamma_table;
- isc->gamma_max = 0;
-
- isc->max_width = ISC_SAMA7G5_MAX_SUPPORT_WIDTH;
- isc->max_height = ISC_SAMA7G5_MAX_SUPPORT_HEIGHT;
-
- isc->config_dpc = isc_sama7g5_config_dpc;
- isc->config_csc = isc_sama7g5_config_csc;
- isc->config_cbc = isc_sama7g5_config_cbc;
- isc->config_cc = isc_sama7g5_config_cc;
- isc->config_gam = isc_sama7g5_config_gam;
- isc->config_rlp = isc_sama7g5_config_rlp;
- isc->config_ctrls = isc_sama7g5_config_ctrls;
-
- isc->adapt_pipeline = isc_sama7g5_adapt_pipeline;
-
- isc->offsets.csc = ISC_SAMA7G5_CSC_OFFSET;
- isc->offsets.cbc = ISC_SAMA7G5_CBC_OFFSET;
- isc->offsets.sub422 = ISC_SAMA7G5_SUB422_OFFSET;
- isc->offsets.sub420 = ISC_SAMA7G5_SUB420_OFFSET;
- isc->offsets.rlp = ISC_SAMA7G5_RLP_OFFSET;
- isc->offsets.his = ISC_SAMA7G5_HIS_OFFSET;
- isc->offsets.dma = ISC_SAMA7G5_DMA_OFFSET;
- isc->offsets.version = ISC_SAMA7G5_VERSION_OFFSET;
- isc->offsets.his_entry = ISC_SAMA7G5_HIS_ENTRY_OFFSET;
-
- isc->controller_formats = sama7g5_controller_formats;
- isc->controller_formats_size = ARRAY_SIZE(sama7g5_controller_formats);
- isc->formats_list = sama7g5_formats_list;
- isc->formats_list_size = ARRAY_SIZE(sama7g5_formats_list);
-
- /* sama7g5-isc RAM access port is full AXI4 - 32 bits per beat */
- isc->dcfg = ISC_DCFG_YMBSIZE_BEATS32 | ISC_DCFG_CMBSIZE_BEATS32;
-
- /* sama7g5-isc : ISPCK does not exist, ISC is clocked by MCK */
- isc->ispck_required = false;
-
- ret = atmel_isc_pipeline_init(isc);
- if (ret)
- return ret;
-
- isc->hclock = devm_clk_get(dev, "hclock");
- if (IS_ERR(isc->hclock)) {
- ret = PTR_ERR(isc->hclock);
- dev_err(dev, "failed to get hclock: %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(isc->hclock);
- if (ret) {
- dev_err(dev, "failed to enable hclock: %d\n", ret);
- return ret;
- }
-
- ret = atmel_isc_clk_init(isc);
- if (ret) {
- dev_err(dev, "failed to init isc clock: %d\n", ret);
- goto unprepare_hclk;
- }
-
- ret = v4l2_device_register(dev, &isc->v4l2_dev);
- if (ret) {
- dev_err(dev, "unable to register v4l2 device.\n");
- goto unprepare_hclk;
- }
-
- ret = xisc_parse_dt(dev, isc);
- if (ret) {
- dev_err(dev, "fail to parse device tree\n");
- goto unregister_v4l2_device;
- }
-
- if (list_empty(&isc->subdev_entities)) {
- dev_err(dev, "no subdev found\n");
- ret = -ENODEV;
- goto unregister_v4l2_device;
- }
-
- list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
- struct v4l2_async_connection *asd;
- struct fwnode_handle *fwnode =
- of_fwnode_handle(subdev_entity->epn);
-
- v4l2_async_nf_init(&subdev_entity->notifier, &isc->v4l2_dev);
-
- asd = v4l2_async_nf_add_fwnode_remote(&subdev_entity->notifier,
- fwnode,
- struct v4l2_async_connection);
-
- of_node_put(subdev_entity->epn);
- subdev_entity->epn = NULL;
-
- if (IS_ERR(asd)) {
- ret = PTR_ERR(asd);
- goto cleanup_subdev;
- }
-
- subdev_entity->notifier.ops = &atmel_isc_async_ops;
-
- ret = v4l2_async_nf_register(&subdev_entity->notifier);
- if (ret) {
- dev_err(dev, "fail to register async notifier\n");
- goto cleanup_subdev;
- }
-
- if (video_is_registered(&isc->video_dev))
- break;
- }
-
- pm_runtime_set_active(dev);
- pm_runtime_enable(dev);
- pm_request_idle(dev);
-
- regmap_read(isc->regmap, ISC_VERSION + isc->offsets.version, &ver);
- dev_info(dev, "Microchip XISC version %x\n", ver);
-
- return 0;
-
-cleanup_subdev:
- atmel_isc_subdev_cleanup(isc);
-
-unregister_v4l2_device:
- v4l2_device_unregister(&isc->v4l2_dev);
-
-unprepare_hclk:
- clk_disable_unprepare(isc->hclock);
-
- atmel_isc_clk_cleanup(isc);
-
- return ret;
-}
-
-static void microchip_xisc_remove(struct platform_device *pdev)
-{
- struct isc_device *isc = platform_get_drvdata(pdev);
-
- pm_runtime_disable(&pdev->dev);
-
- atmel_isc_subdev_cleanup(isc);
-
- v4l2_device_unregister(&isc->v4l2_dev);
-
- clk_disable_unprepare(isc->hclock);
-
- atmel_isc_clk_cleanup(isc);
-}
-
-static int __maybe_unused xisc_runtime_suspend(struct device *dev)
-{
- struct isc_device *isc = dev_get_drvdata(dev);
-
- clk_disable_unprepare(isc->hclock);
-
- return 0;
-}
-
-static int __maybe_unused xisc_runtime_resume(struct device *dev)
-{
- struct isc_device *isc = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(isc->hclock);
- if (ret)
- return ret;
-
- return ret;
-}
-
-static const struct dev_pm_ops microchip_xisc_dev_pm_ops = {
- SET_RUNTIME_PM_OPS(xisc_runtime_suspend, xisc_runtime_resume, NULL)
-};
-
-#if IS_ENABLED(CONFIG_OF)
-static const struct of_device_id microchip_xisc_of_match[] = {
- { .compatible = "microchip,sama7g5-isc" },
- { }
-};
-MODULE_DEVICE_TABLE(of, microchip_xisc_of_match);
-#endif
-
-static struct platform_driver microchip_xisc_driver = {
- .probe = microchip_xisc_probe,
- .remove = microchip_xisc_remove,
- .driver = {
- .name = "microchip-sama7g5-xisc",
- .pm = &microchip_xisc_dev_pm_ops,
- .of_match_table = of_match_ptr(microchip_xisc_of_match),
- },
-};
-
-module_platform_driver(microchip_xisc_driver);
-
-MODULE_AUTHOR("Eugen Hristev <eugen.hristev@microchip.com>");
-MODULE_DESCRIPTION("The V4L2 driver for Microchip-XISC");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
index e9cef7af000a..bfd71d25facc 100644
--- a/drivers/staging/media/imx/imx-media-capture.c
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -722,8 +722,8 @@ static int capture_start_streaming(struct vb2_queue *vq, unsigned int count)
goto return_bufs;
}
- ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity,
- true);
+ ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev,
+ &priv->src_sd->entity, true);
if (ret) {
dev_err(priv->dev, "pipeline start failed with %d\n", ret);
goto return_bufs;
@@ -749,8 +749,8 @@ static void capture_stop_streaming(struct vb2_queue *vq)
unsigned long flags;
int ret;
- ret = imx_media_pipeline_set_stream(priv->md, &priv->src_sd->entity,
- false);
+ ret = imx_media_pipeline_set_stream(priv->md, &priv->vdev,
+ &priv->src_sd->entity, false);
if (ret)
dev_warn(priv->dev, "pipeline stop failed with %d\n", ret);
diff --git a/drivers/staging/media/imx/imx-media-utils.c b/drivers/staging/media/imx/imx-media-utils.c
index 1b5af8945e6b..f119477cac6b 100644
--- a/drivers/staging/media/imx/imx-media-utils.c
+++ b/drivers/staging/media/imx/imx-media-utils.c
@@ -589,10 +589,8 @@ int imx_media_alloc_dma_buf(struct device *dev,
buf->len = PAGE_ALIGN(size);
buf->virt = dma_alloc_coherent(dev, buf->len, &buf->phys,
GFP_DMA | GFP_KERNEL);
- if (!buf->virt) {
- dev_err(dev, "%s: failed\n", __func__);
+ if (!buf->virt)
return -ENOMEM;
- }
return 0;
}
@@ -749,10 +747,12 @@ EXPORT_SYMBOL_GPL(imx_media_pipeline_subdev);
* Turn current pipeline streaming on/off starting from entity.
*/
int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
+ struct imx_media_video_dev *vdev,
struct media_entity *entity,
bool on)
{
struct v4l2_subdev *sd;
+ struct media_pad *pad;
int ret = 0;
if (!is_media_entity_v4l2_subdev(entity))
@@ -761,17 +761,19 @@ int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
mutex_lock(&imxmd->md.graph_mutex);
+ pad = &entity->pads[0];
+
if (on) {
- ret = __media_pipeline_start(entity->pads, &imxmd->pipe);
+ ret = __media_pipeline_start(pad, &vdev->pipe);
if (ret)
goto out;
ret = v4l2_subdev_call(sd, video, s_stream, 1);
if (ret)
- __media_pipeline_stop(entity->pads);
+ __media_pipeline_stop(pad);
} else {
v4l2_subdev_call(sd, video, s_stream, 0);
- if (media_pad_pipeline(entity->pads))
- __media_pipeline_stop(entity->pads);
+ if (media_pad_is_streaming(pad))
+ __media_pipeline_stop(pad);
}
out:
diff --git a/drivers/staging/media/imx/imx-media.h b/drivers/staging/media/imx/imx-media.h
index 135daca7d55b..537558a7bece 100644
--- a/drivers/staging/media/imx/imx-media.h
+++ b/drivers/staging/media/imx/imx-media.h
@@ -104,6 +104,9 @@ struct imx_media_buffer {
struct imx_media_video_dev {
struct video_device *vfd;
+ /* the pipeline object */
+ struct media_pipeline pipe;
+
/* the user format */
struct v4l2_pix_format fmt;
/* the compose rectangle */
@@ -145,9 +148,6 @@ struct imx_media_dev {
struct media_device md;
struct v4l2_device v4l2_dev;
- /* the pipeline object */
- struct media_pipeline pipe;
-
struct mutex mutex; /* protect elements below */
/* master video device list */
@@ -223,6 +223,7 @@ int imx_media_alloc_dma_buf(struct device *dev,
int size);
int imx_media_pipeline_set_stream(struct imx_media_dev *imxmd,
+ struct imx_media_video_dev *vdev,
struct media_entity *entity,
bool on);
diff --git a/drivers/staging/media/ipu3/ipu3-css-params.c b/drivers/staging/media/ipu3/ipu3-css-params.c
index 2c48d57a3180..92cce31e35c5 100644
--- a/drivers/staging/media/ipu3/ipu3-css-params.c
+++ b/drivers/staging/media/ipu3/ipu3-css-params.c
@@ -1770,6 +1770,8 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe,
acc->stripe.bds_out_stripes[0].width =
ALIGN(css_pipe->rect[IPU3_CSS_RECT_BDS].width, f);
} else {
+ u32 offset;
+
/* Image processing is divided into two stripes */
acc->stripe.bds_out_stripes[0].width =
acc->stripe.bds_out_stripes[1].width =
@@ -1788,8 +1790,10 @@ static int imgu_css_cfg_acc_stripe(struct imgu_css *css, unsigned int pipe,
acc->stripe.bds_out_stripes[1].width += f;
}
/* Overlap between stripes is IPU3_UAPI_ISP_VEC_ELEMS * 4 */
- acc->stripe.bds_out_stripes[1].offset =
- acc->stripe.bds_out_stripes[0].width - 2 * f;
+ offset = acc->stripe.bds_out_stripes[0].width - 2 * f;
+ if (offset > 65535)
+ return -EINVAL;
+ acc->stripe.bds_out_stripes[1].offset = offset;
}
acc->stripe.effective_stripes[0].height =
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h
index a1519c4fe661..56b90aab83ea 100644
--- a/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_boot_abi.h
@@ -42,7 +42,7 @@ struct ia_gofo_logger_config {
((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_CCG_IPU_BUTTRESS_FW_BOOT_PARAMS_SECONDARY_OFFSET (0x3000 / 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 \
@@ -75,7 +75,7 @@ enum ia_gofo_boot_uc_tile_frequency_units {
};
#define IA_GOFO_FW_BOOT_STATE_IS_CRITICAL(boot_state) \
- (0xdead0000U == ((boot_state) & 0xffff0000U))
+ (0xdead0000 == ((boot_state) & 0xffff0000))
struct ia_gofo_boot_config {
u32 length;
@@ -104,35 +104,35 @@ struct ia_gofo_secondary_boot_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
+#define IA_GOFO_WDT_TIMEOUT_ERR 0xdead0401
+#define IA_GOFO_MEM_FATAL_DME_ERR 0xdead0801
+#define IA_GOFO_MEM_UNCORRECTABLE_LOCAL_ERR 0xdead0802
+#define IA_GOFO_MEM_UNCORRECTABLE_DIRTY_ERR 0xdead0803
+#define IA_GOFO_MEM_UNCORRECTABLE_DTAG_ERR 0xdead0804
+#define IA_GOFO_MEM_UNCORRECTABLE_CACHE_ERR 0xdead0805
+#define IA_GOFO_DOUBLE_EXCEPTION_ERR 0xdead0806
+#define IA_GOFO_BIST_DMEM_FAULT_DETECTION_ERR 0xdead1000
+#define IA_GOFO_BIST_DATA_INTEGRITY_FAILURE 0xdead1010
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_SECONDARY_BOOT_CONFIG_READY = 0x57a7b000,
+ IA_GOFO_FW_BOOT_STATE_UNINIT = 0x57a7e000,
+ IA_GOFO_FW_BOOT_STATE_STARTING_0 = 0x57a7d000,
+ IA_GOFO_FW_BOOT_STATE_CACHE_INIT_DONE = 0x57a7d010,
+ IA_GOFO_FW_BOOT_STATE_MEM_INIT_DONE = 0x57a7d020,
+ IA_GOFO_FW_BOOT_STATE_STACK_INIT_DONE = 0x57a7d030,
+ IA_GOFO_FW_BOOT_STATE_EARLY_BOOT_DONE = 0x57a7d100,
+ IA_GOFO_FW_BOOT_STATE_BOOT_CONFIG_START = 0x57a7d200,
+ IA_GOFO_FW_BOOT_STATE_QUEUE_INIT_DONE = 0x57a7d300,
+ IA_GOFO_FW_BOOT_STATE_READY = 0x57a7e100,
+ IA_GOFO_FW_BOOT_STATE_CRIT_UNSPECIFIED = 0xdead0001,
+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_PTR = 0xdead0101,
+ IA_GOFO_FW_BOOT_STATE_CRIT_CFG_VERSION = 0xdead0201,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MSG_VERSION = 0xdead0301,
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_WRONG_DATA_SECTION_UNPACKING = 0xdead0501,
+ IA_GOFO_FW_BOOT_STATE_WRONG_RO_DATA_SECTION_UNPACKING = 0xdead0601,
+ IA_GOFO_FW_BOOT_STATE_INVALID_UNTRUSTED_ADDR_MIN = 0xdead0701,
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,
@@ -146,18 +146,18 @@ enum ia_gofo_boot_state {
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_DATA_INTEGRITY_FAILURE = 0xdead1010,
+ IA_GOFO_FW_BOOT_STATE_CRIT_STACK_CHK_FAILURE = 0xdead1011,
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
+ 0xdead1012,
+ IA_GOFO_FW_BOOT_STATE_CRIT_MPU_CONFIG_FAILURE = 0xdead1013,
+ IA_GOFO_FW_BOOT_STATE_CRIT_SHARED_BUFFER_FAILURE = 0xdead1014,
+ IA_GOFO_FW_BOOT_STATE_CRIT_CMEM_FAILURE = 0xdead1015,
+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001,
+ IA_GOFO_FW_BOOT_STATE_SHUTDOWN_START = 0x57a7e200,
+ IA_GOFO_FW_BOOT_STATE_INACTIVE = 0x57a7e300,
+ IA_GOFO_FW_BOOT_HW_CMD_ACK_TIMEOUT = 0x57a7e400,
+ IA_GOFO_FW_BOOT_SYSTEM_CYCLES_ERROR = 0x57a7e500
};
#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
index 7bb6fac585a3..398a13350480 100644
--- a/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_common_abi.h
@@ -60,7 +60,7 @@ struct ia_gofo_tlv_list {
#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_UNSPECIFED (0xffffffff)
#define IA_GOFO_MSG_ERR_GROUP_UNSPECIFIED (0U)
#define IA_GOFO_MSG_ERR_IS_OK(err) (IA_GOFO_MSG_ERR_OK == (err).err_code)
@@ -145,7 +145,7 @@ struct ia_gofo_msg_indirect {
#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)
+#define IA_GOFO_MSG_LOG_FMT_ID_INVALID (0xfffffff)
struct ia_gofo_msg_log_info {
u16 log_counter;
diff --git a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h
index 8a78dd0936df..311248385993 100644
--- a/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h
+++ b/drivers/staging/media/ipu7/abi/ipu7_fw_msg_abi.h
@@ -69,11 +69,11 @@ struct ipu7_msg_cb_profile {
#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_RSRC_ID_EXT_IP (0xff)
-#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)
+#define IPU_MSG_NODE_DONT_CARE_TEB_HI (0xffffffff)
+#define IPU_MSG_NODE_DONT_CARE_TEB_LO (0xffffffff)
+#define IPU_MSG_NODE_RSRC_ID_IS (0xfe)
struct ipu7_msg_node {
struct ia_gofo_tlv_header tlv_header;
@@ -160,7 +160,7 @@ struct ipu7_msg_link_ep_pair {
#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)
+#define IPU_MSG_LINK_TERM_ID_DONT_CARE (0xff)
struct ipu7_msg_link {
struct ia_gofo_tlv_header tlv_header;
@@ -333,7 +333,7 @@ enum ipu7_msg_err_device {
#pragma pack(pop)
#pragma pack(push, 1)
-#define IPU_MSG_GRAPH_ID_UNKNOWN (0xffU)
+#define IPU_MSG_GRAPH_ID_UNKNOWN (0xff)
#define IPU_MSG_GRAPH_SEND_MSG_ENABLED 1U
#define IPU_MSG_GRAPH_SEND_MSG_DISABLED 0U
diff --git a/drivers/staging/media/ipu7/ipu7-buttress-regs.h b/drivers/staging/media/ipu7/ipu7-buttress-regs.h
index 3eafd6a3813d..7b646aa538cf 100644
--- a/drivers/staging/media/ipu7/ipu7-buttress-regs.h
+++ b/drivers/staging/media/ipu7/ipu7-buttress-regs.h
@@ -287,7 +287,7 @@
#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_PWR_STATUS_HH_STATUS_MASK (0x3 << 11)
#define BUTTRESS_TSW_WA_SOFT_RESET BIT(8)
/* new for PTL */
#define BUTTRESS_SEL_PB_TIMESTAMP BIT(9)
@@ -326,8 +326,8 @@
#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 BUTTRESS_IS_FREQ_CTL_RATIO_MASK 0xff
+#define BUTTRESS_PS_FREQ_CTL_RATIO_MASK 0xff
#define IPU7_IS_FREQ_MAX 450
#define IPU7_IS_FREQ_MIN 50
@@ -350,11 +350,11 @@
/* 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)
+ (0x3 << 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)
+ (0x3 << IPU_BUTTRESS_PWR_STATE_PS_PWR_SHIFT)
#define IPU_BUTTRESS_PWR_STATE_DN_DONE 0x0
#define IPU_BUTTRESS_PWR_STATE_UP_PROCESS 0x1
diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c
index c771e763f8c5..310e3f24e571 100644
--- a/drivers/staging/media/ipu7/ipu7.c
+++ b/drivers/staging/media/ipu7/ipu7.c
@@ -2169,21 +2169,18 @@ ipu7_isys_init(struct pci_dev *pdev, struct device *parent,
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");
+ ret = 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);
+ return ERR_PTR(ret);
}
isys_adev->mmu->dev = &isys_adev->auxdev.dev;
isys_adev->subsys = IPU_IS;
ret = ipu7_bus_add_device(isys_adev);
- if (ret) {
- kfree(pdata);
+ if (ret)
return ERR_PTR(ret);
- }
return isys_adev;
}
@@ -2216,21 +2213,18 @@ ipu7_psys_init(struct pci_dev *pdev, struct device *parent,
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");
+ ret = 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);
+ return ERR_PTR(ret);
}
psys_adev->mmu->dev = &psys_adev->auxdev.dev;
psys_adev->subsys = IPU_PS;
ret = ipu7_bus_add_device(psys_adev);
- if (ret) {
- kfree(pdata);
+ if (ret)
return ERR_PTR(ret);
- }
return psys_adev;
}
diff --git a/drivers/staging/media/meson/vdec/codec_h264.c b/drivers/staging/media/meson/vdec/codec_h264.c
index 89e0f8624e5b..a6074de15118 100644
--- a/drivers/staging/media/meson/vdec/codec_h264.c
+++ b/drivers/staging/media/meson/vdec/codec_h264.c
@@ -16,7 +16,7 @@
#define SIZE_SEI (8 * SZ_1K)
/*
- * Offset added by the firmware which must be substracted
+ * Offset added by the firmware which must be subtracted
* from the workspace phyaddr
*/
#define WORKSPACE_BUF_OFFSET 0x1000000
diff --git a/drivers/staging/media/meson/vdec/codec_mpeg12.c b/drivers/staging/media/meson/vdec/codec_mpeg12.c
index 76e9ca7191ab..ab4374e3b2ef 100644
--- a/drivers/staging/media/meson/vdec/codec_mpeg12.c
+++ b/drivers/staging/media/meson/vdec/codec_mpeg12.c
@@ -12,7 +12,7 @@
#include "vdec_helpers.h"
#define SIZE_WORKSPACE SZ_128K
-/* Offset substracted by the firmware from the workspace paddr */
+/* Offset subtracted by the firmware from the workspace paddr */
#define WORKSPACE_OFFSET (5 * SZ_1K)
/* map firmware registers to known MPEG1/2 functions */
diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c
index 4b77ec1af5a7..a039d925c0fe 100644
--- a/drivers/staging/media/meson/vdec/vdec.c
+++ b/drivers/staging/media/meson/vdec/vdec.c
@@ -889,7 +889,7 @@ static int vdec_open(struct file *file)
ret = vdec_init_ctrls(sess);
if (ret)
- goto err_m2m_release;
+ goto err_m2m_ctx_release;
sess->pixfmt_cap = formats[0].pixfmts_cap[0];
sess->fmt_out = &formats[0];
@@ -913,6 +913,8 @@ static int vdec_open(struct file *file)
return 0;
+err_m2m_ctx_release:
+ v4l2_m2m_ctx_release(sess->m2m_ctx);
err_m2m_release:
v4l2_m2m_release(sess->m2m_dev);
err_free_sess:
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus.c b/drivers/staging/media/sunxi/cedrus/cedrus.c
index 6600245dff0e..bbd186b8035b 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus.c
@@ -391,6 +391,7 @@ static int cedrus_open(struct file *file)
err_m2m_release:
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
err_free:
+ v4l2_fh_exit(&ctx->fh);
kfree(ctx);
mutex_unlock(&dev->dev_mutex);
@@ -476,7 +477,7 @@ static int cedrus_probe(struct platform_device *pdev)
ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
if (ret) {
dev_err(&pdev->dev, "Failed to register V4L2 device\n");
- return ret;
+ goto err_hw;
}
vfd = &dev->vfd;
@@ -507,7 +508,7 @@ static int cedrus_probe(struct platform_device *pdev)
ret = video_register_device(vfd, VFL_TYPE_VIDEO, 0);
if (ret) {
v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
- goto err_m2m;
+ goto err_media;
}
v4l2_info(&dev->v4l2_dev,
@@ -533,10 +534,13 @@ err_m2m_mc:
v4l2_m2m_unregister_media_controller(dev->m2m_dev);
err_video:
video_unregister_device(&dev->vfd);
-err_m2m:
+err_media:
+ media_device_cleanup(&dev->mdev);
v4l2_m2m_release(dev->m2m_dev);
err_v4l2:
v4l2_device_unregister(&dev->v4l2_dev);
+err_hw:
+ cedrus_hw_remove(dev);
return ret;
}
diff --git a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
index 3e2843ef6cce..fc54d993b11f 100644
--- a/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
+++ b/drivers/staging/media/sunxi/cedrus/cedrus_h264.c
@@ -210,6 +210,9 @@ static void _cedrus_write_ref_list(struct cedrus_ctx *ctx,
u8 dpb_idx;
dpb_idx = ref_list[i].index;
+ if (dpb_idx >= V4L2_H264_NUM_DPB_ENTRIES)
+ continue;
+
dpb = &decode->dpb[dpb_idx];
if (!(dpb->flags & V4L2_H264_DPB_ENTRY_FLAG_ACTIVE))
diff --git a/drivers/staging/media/tegra-video/tegra210.c b/drivers/staging/media/tegra-video/tegra210.c
index da99f19a39e7..a6606768fa03 100644
--- a/drivers/staging/media/tegra-video/tegra210.c
+++ b/drivers/staging/media/tegra-video/tegra210.c
@@ -362,8 +362,7 @@ dequeue_buf_done(struct tegra_vi_channel *chan)
buf = list_first_entry(&chan->done,
struct tegra_channel_buffer, queue);
- if (buf)
- list_del_init(&buf->queue);
+ list_del_init(&buf->queue);
spin_unlock(&chan->done_lock);
return buf;
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index f14cdc7b5211..456134a9e8cf 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -80,8 +80,8 @@ static int tegra_get_format_idx_by_code(struct tegra_vi *vi,
static u32 tegra_get_format_fourcc_by_idx(struct tegra_vi *vi,
unsigned int index)
{
- if (index >= vi->soc->nformats)
- return -EINVAL;
+ if (WARN_ON_ONCE(index >= vi->soc->nformats))
+ return vi->soc->video_formats[0].fourcc;
return vi->soc->video_formats[index].fourcc;
}
diff --git a/include/linux/usb/uvc.h b/include/linux/usb/uvc.h
index 05bfebab42b6..99b070ab860f 100644
--- a/include/linux/usb/uvc.h
+++ b/include/linux/usb/uvc.h
@@ -43,6 +43,16 @@
#define UVC_GUID_MSXU_1_5 \
{0xdc, 0x95, 0x3f, 0x0f, 0x32, 0x26, 0x4e, 0x4c, \
0x92, 0xc9, 0xa0, 0x47, 0x82, 0xf4, 0x3b, 0xc8}
+#define UVC_GUID_LOGITECH_MOTOR_CONTROL_V1 \
+ {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+ 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x56 }
+#define UVC_GUID_LOGITECH_PERIPHERAL \
+ {0x21, 0x2d, 0xe5, 0xff, 0x30, 0x80, 0x2c, 0x4e, \
+ 0x82, 0xd9, 0xf5, 0x87, 0xd0, 0x05, 0x40, 0xbd }
+#define UVC_GUID_LOGITECH_USER_HW_CONTROL_V1 \
+ {0x82, 0x06, 0x61, 0x63, 0x70, 0x50, 0xab, 0x49, \
+ 0xb8, 0xcc, 0xb3, 0x85, 0x5e, 0x8d, 0x22, 0x1f }
+
/* https://learn.microsoft.com/en-us/windows-hardware/drivers/stream/uvc-extensions-1-5#222-extension-unit-controls */
#define UVC_MSXU_CONTROL_FOCUS 0x01
diff --git a/include/media/i2c/lm3560.h b/include/media/i2c/lm3560.h
deleted file mode 100644
index 770d8c72c94a..000000000000
--- a/include/media/i2c/lm3560.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * include/media/i2c/lm3560.h
- *
- * Copyright (C) 2013 Texas Instruments
- *
- * Contact: Daniel Jeong <gshark.jeong@gmail.com>
- * Ldd-Mlp <ldd-mlp@list.ti.com>
- */
-
-#ifndef __LM3560_H__
-#define __LM3560_H__
-
-#include <media/v4l2-subdev.h>
-
-#define LM3559_NAME "lm3559"
-#define LM3560_NAME "lm3560"
-#define LM3560_I2C_ADDR (0x53)
-
-/* FLASH Brightness
- * min 62500uA, step 62500uA, max 1000000uA
- */
-#define LM3560_FLASH_BRT_MIN 62500
-#define LM3560_FLASH_BRT_STEP 62500
-#define LM3560_FLASH_BRT_MAX 1000000
-#define LM3560_FLASH_BRT_uA_TO_REG(a) \
- ((a) < LM3560_FLASH_BRT_MIN ? 0 : \
- (((a) - LM3560_FLASH_BRT_MIN) / LM3560_FLASH_BRT_STEP))
-#define LM3560_FLASH_BRT_REG_TO_uA(a) \
- ((a) * LM3560_FLASH_BRT_STEP + LM3560_FLASH_BRT_MIN)
-
-/* FLASH TIMEOUT DURATION
- * min 32ms, step 32ms, max 1024ms
- */
-#define LM3560_FLASH_TOUT_MIN 32
-#define LM3560_FLASH_TOUT_STEP 32
-#define LM3560_FLASH_TOUT_MAX 1024
-#define LM3560_FLASH_TOUT_ms_TO_REG(a) \
- ((a) < LM3560_FLASH_TOUT_MIN ? 0 : \
- (((a) - LM3560_FLASH_TOUT_MIN) / LM3560_FLASH_TOUT_STEP))
-#define LM3560_FLASH_TOUT_REG_TO_ms(a) \
- ((a) * LM3560_FLASH_TOUT_STEP + LM3560_FLASH_TOUT_MIN)
-
-/* TORCH BRT
- * min 31250uA, step 31250uA, max 250000uA
- */
-#define LM3560_TORCH_BRT_MIN 31250
-#define LM3560_TORCH_BRT_STEP 31250
-#define LM3560_TORCH_BRT_MAX 250000
-#define LM3560_TORCH_BRT_uA_TO_REG(a) \
- ((a) < LM3560_TORCH_BRT_MIN ? 0 : \
- (((a) - LM3560_TORCH_BRT_MIN) / LM3560_TORCH_BRT_STEP))
-#define LM3560_TORCH_BRT_REG_TO_uA(a) \
- ((a) * LM3560_TORCH_BRT_STEP + LM3560_TORCH_BRT_MIN)
-
-enum lm3560_led_id {
- LM3560_LED0 = 0,
- LM3560_LED1,
- LM3560_LED_MAX
-};
-
-enum lm3560_peak_current {
- LM3560_PEAK_1600mA = 0x00,
- LM3560_PEAK_2300mA = 0x20,
- LM3560_PEAK_3000mA = 0x40,
- LM3560_PEAK_3600mA = 0x60
-};
-
-/* struct lm3560_platform_data
- *
- * @peak : peak current
- * @max_flash_timeout: flash timeout
- * @max_flash_brt: flash mode led brightness
- * @max_torch_brt: torch mode led brightness
- */
-struct lm3560_platform_data {
- enum lm3560_peak_current peak;
-
- u32 max_flash_timeout;
- u32 max_flash_brt[LM3560_LED_MAX];
- u32 max_torch_brt[LM3560_LED_MAX];
-};
-
-#endif /* __LM3560_H__ */
diff --git a/include/media/media-entity.h b/include/media/media-entity.h
index b91ff6f8c3bb..d9b72cd87d52 100644
--- a/include/media/media-entity.h
+++ b/include/media/media-entity.h
@@ -726,14 +726,12 @@ int media_entity_pads_init(struct media_entity *entity, u16 num_pads,
* the entity (currently, it does nothing).
*
* Calling media_entity_cleanup() on a media_entity whose memory has been
- * zeroed but that has not been initialized with media_entity_pad_init() is
+ * zeroed but that has not been initialized with media_entity_pads_init() is
* valid and is a no-op.
*/
-#if IS_ENABLED(CONFIG_MEDIA_CONTROLLER)
-static inline void media_entity_cleanup(struct media_entity *entity) {}
-#else
-#define media_entity_cleanup(entity) do { } while (false)
-#endif
+static inline void media_entity_cleanup(struct media_entity *entity)
+{
+}
/**
* media_get_pad_index() - retrieves a pad index from an entity
diff --git a/include/media/v4l2-async.h b/include/media/v4l2-async.h
index f26c323e9c96..54a2d9620ed5 100644
--- a/include/media/v4l2-async.h
+++ b/include/media/v4l2-async.h
@@ -333,8 +333,10 @@ int __v4l2_async_register_subdev(struct v4l2_subdev *sd, struct module *module);
* An error is returned if the module is no longer loaded on any attempts
* to register it.
*/
+#define v4l2_async_register_subdev_sensor(sd) \
+ __v4l2_async_register_subdev_sensor(sd, THIS_MODULE)
int __must_check
-v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd);
+__v4l2_async_register_subdev_sensor(struct v4l2_subdev *sd, struct module *module);
/**
* v4l2_async_unregister_subdev - unregisters a sub-device to the asynchronous
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index f8b1faced79c..edd416178c33 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -520,6 +520,7 @@ enum v4l2_pixel_encoding {
* @vdiv: Vertical chroma subsampling factor
* @block_w: Per-plane macroblock pixel width (optional)
* @block_h: Per-plane macroblock pixel height (optional)
+ * @has_alpha: Does the format embeds an alpha component?
*/
struct v4l2_format_info {
u32 format;
@@ -532,6 +533,7 @@ struct v4l2_format_info {
u8 vdiv;
u8 block_w[4];
u8 block_h[4];
+ bool has_alpha;
};
static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f)
@@ -556,6 +558,10 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
u32 width, u32 height);
int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat,
u32 width, u32 height);
+/* @stride_alignment is a power of 2 value in bytes */
+int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt,
+ u32 pixelformat, u32 width, u32 height,
+ u8 stride_alignment);
/**
* v4l2_get_link_freq - Get link rate from transmitter
diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h
index 4424d481d7f7..4b4f4c15c53a 100644
--- a/include/media/videobuf2-core.h
+++ b/include/media/videobuf2-core.h
@@ -1093,8 +1093,8 @@ __poll_t vb2_core_poll(struct vb2_queue *q, struct file *file,
* @ppos: file handle position tracking pointer
* @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
*/
-size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
- loff_t *ppos, int nonblock);
+ssize_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
+ loff_t *ppos, int nonblock);
/**
* vb2_write() - implements write() syscall logic.
* @q: pointer to &struct vb2_queue with videobuf2 queue.
@@ -1103,8 +1103,8 @@ size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count,
* @ppos: file handle position tracking pointer
* @nonblock: mode selector (1 means blocking calls, 0 means nonblocking)
*/
-size_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
- loff_t *ppos, int nonblock);
+ssize_t vb2_write(struct vb2_queue *q, const char __user *data, size_t count,
+ loff_t *ppos, int nonblock);
/**
* typedef vb2_thread_fnc - callback function for use with vb2_thread.
diff --git a/include/media/vsp1.h b/include/media/vsp1.h
index d9b91ff02761..98089e0a4385 100644
--- a/include/media/vsp1.h
+++ b/include/media/vsp1.h
@@ -44,8 +44,9 @@ struct vsp1_du_lif_config {
void *callback_data;
};
-int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
- const struct vsp1_du_lif_config *cfg);
+int vsp1_du_enable(struct device *dev, unsigned int pipe_index,
+ const struct vsp1_du_lif_config *cfg);
+int vsp1_du_disable(struct device *dev, unsigned int pipe_index);
/**
* struct vsp1_du_atomic_config - VSP atomic configuration parameters
diff --git a/include/uapi/linux/cec-funcs.h b/include/uapi/linux/cec-funcs.h
index 189ecf0e13cd..ba4b47de9bf0 100644
--- a/include/uapi/linux/cec-funcs.h
+++ b/include/uapi/linux/cec-funcs.h
@@ -1701,6 +1701,188 @@ static inline void cec_ops_request_current_latency(const struct cec_msg *msg,
}
+/* Latency Indication Protocol Feature */
+/* Only for CEC 2.0 and up */
+static inline void cec_msg_request_lip_support(struct cec_msg *msg,
+ int reply, __u16 phys_addr)
+{
+ msg->len = 4;
+ msg->msg[1] = CEC_MSG_REQUEST_LIP_SUPPORT;
+ msg->msg[2] = phys_addr >> 8;
+ msg->msg[3] = phys_addr & 0xff;
+ msg->reply = reply ? CEC_MSG_REPORT_LIP_SUPPORT : 0;
+}
+
+static inline void cec_ops_request_lip_support(const struct cec_msg *msg,
+ __u16 *phys_addr)
+{
+ *phys_addr = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_report_lip_support(struct cec_msg *msg, __u32 sqid)
+{
+ msg->len = 6;
+ msg->msg[1] = CEC_MSG_REPORT_LIP_SUPPORT;
+ msg->msg[2] = sqid >> 24;
+ msg->msg[3] = (sqid >> 16) & 0xff;
+ msg->msg[4] = (sqid >> 8) & 0xff;
+ msg->msg[5] = sqid & 0xff;
+}
+
+static inline void cec_ops_report_lip_support(const struct cec_msg *msg,
+ __u32 *sqid)
+{
+ *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) |
+ (msg->msg[4] << 8) | msg->msg[5];
+}
+
+static inline void cec_msg_request_audio_and_video_latency(struct cec_msg *msg,
+ int reply, __u8 video_format,
+ __u8 hdr_format, __u8 vrr_format,
+ __u8 audio_format,
+ __u8 audio_format_extension)
+{
+ msg->len = 6;
+ msg->msg[1] = CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY;
+ msg->msg[2] = video_format;
+ msg->msg[3] = hdr_format;
+ msg->msg[4] = vrr_format;
+ msg->msg[5] = audio_format;
+ if (audio_format >= 1 && audio_format <= 31) {
+ msg->msg[6] = audio_format_extension;
+ msg->len++;
+ }
+ msg->reply = reply ? CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY : 0;
+}
+
+static inline void cec_ops_request_audio_and_video_latency(const struct cec_msg *msg,
+ __u8 *video_format,
+ __u8 *hdr_format,
+ __u8 *vrr_format,
+ __u8 *audio_format,
+ __u8 *audio_format_extension)
+{
+ *video_format = msg->msg[2];
+ *hdr_format = msg->msg[3];
+ *vrr_format = msg->msg[4];
+ *audio_format = msg->msg[5];
+ *audio_format_extension = msg->len > 6 ? msg->msg[6] : 0;
+}
+
+static inline void cec_msg_report_audio_and_video_latency(struct cec_msg *msg,
+ __u16 video_latency,
+ __u16 audio_latency)
+{
+ msg->len = 6;
+ msg->msg[1] = CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY;
+ msg->msg[2] = video_latency >> 8;
+ msg->msg[3] = video_latency & 0xff;
+ msg->msg[4] = audio_latency >> 8;
+ msg->msg[5] = audio_latency & 0xff;
+}
+
+static inline void cec_ops_report_audio_and_video_latency(const struct cec_msg *msg,
+ __u16 *video_latency,
+ __u16 *audio_latency)
+{
+ *video_latency = (msg->msg[2] << 8) | msg->msg[3];
+ *audio_latency = (msg->msg[4] << 8) | msg->msg[5];
+}
+
+static inline void cec_msg_request_audio_latency(struct cec_msg *msg,
+ int reply,
+ __u8 audio_format,
+ __u8 audio_format_extension)
+{
+ msg->len = 3;
+ msg->msg[1] = CEC_MSG_REQUEST_AUDIO_LATENCY;
+ msg->msg[2] = audio_format;
+ if (audio_format >= 1 && audio_format <= 31) {
+ msg->msg[3] = audio_format_extension;
+ msg->len++;
+ }
+ msg->reply = reply ? CEC_MSG_REPORT_AUDIO_LATENCY : 0;
+}
+
+static inline void cec_ops_request_audio_latency(const struct cec_msg *msg,
+ __u8 *audio_format,
+ __u8 *audio_format_extension)
+{
+ *audio_format = msg->msg[2];
+ *audio_format_extension = msg->len > 3 ? msg->msg[3] : 0;
+}
+
+static inline void cec_msg_report_audio_latency(struct cec_msg *msg,
+ __u16 audio_latency)
+{
+ msg->len = 4;
+ msg->msg[1] = CEC_MSG_REPORT_AUDIO_LATENCY;
+ msg->msg[2] = audio_latency >> 8;
+ msg->msg[3] = audio_latency & 0xff;
+}
+
+static inline void cec_ops_report_audio_latency(const struct cec_msg *msg,
+ __u16 *audio_latency)
+{
+ *audio_latency = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_request_video_latency(struct cec_msg *msg,
+ int reply, __u8 video_format,
+ __u8 hdr_format,
+ __u8 vrr_format)
+{
+ msg->len = 5;
+ msg->msg[1] = CEC_MSG_REQUEST_VIDEO_LATENCY;
+ msg->msg[2] = video_format;
+ msg->msg[3] = hdr_format;
+ msg->msg[4] = vrr_format;
+ msg->reply = reply ? CEC_MSG_REPORT_VIDEO_LATENCY : 0;
+}
+
+static inline void cec_ops_request_video_latency(const struct cec_msg *msg,
+ __u8 *video_format,
+ __u8 *hdr_format,
+ __u8 *vrr_format)
+{
+ *video_format = msg->msg[2];
+ *hdr_format = msg->msg[3];
+ *vrr_format = msg->msg[4];
+}
+
+static inline void cec_msg_report_video_latency(struct cec_msg *msg,
+ __u16 video_latency)
+{
+ msg->len = 4;
+ msg->msg[1] = CEC_MSG_REPORT_VIDEO_LATENCY;
+ msg->msg[2] = video_latency >> 8;
+ msg->msg[3] = video_latency & 0xff;
+}
+
+static inline void cec_ops_report_video_latency(const struct cec_msg *msg,
+ __u16 *video_latency)
+{
+ *video_latency = (msg->msg[2] << 8) | msg->msg[3];
+}
+
+static inline void cec_msg_update_sqid(struct cec_msg *msg, __u32 sqid)
+{
+ msg->len = 6;
+ msg->msg[1] = CEC_MSG_UPDATE_SQID;
+ msg->msg[2] = sqid >> 24;
+ msg->msg[3] = (sqid >> 16) & 0xff;
+ msg->msg[4] = (sqid >> 8) & 0xff;
+ msg->msg[5] = sqid & 0xff;
+}
+
+static inline void cec_ops_update_sqid(const struct cec_msg *msg,
+ __u32 *sqid)
+{
+ *sqid = (msg->msg[2] << 24) | (msg->msg[3] << 16) |
+ (msg->msg[4] << 8) | msg->msg[5];
+}
+
+
/* Capability Discovery and Control Feature */
static inline void cec_msg_cdc_hec_inquire_state(struct cec_msg *msg,
__u16 phys_addr1,
diff --git a/include/uapi/linux/cec.h b/include/uapi/linux/cec.h
index b2af1dddd4d7..81a05c9c0706 100644
--- a/include/uapi/linux/cec.h
+++ b/include/uapi/linux/cec.h
@@ -742,7 +742,7 @@ struct cec_event {
#define CEC_OP_PRIM_DEVTYPE_PROCESSOR 7
#define CEC_MSG_SET_MENU_LANGUAGE 0x32
-#define CEC_MSG_REPORT_FEATURES 0xa6 /* HDMI 2.0 */
+#define CEC_MSG_REPORT_FEATURES 0xa6 /* CEC 2.0 */
/* All Device Types Operand (all_device_types) */
#define CEC_OP_ALL_DEVTYPE_TV 0x80
#define CEC_OP_ALL_DEVTYPE_RECORD 0x40
@@ -777,7 +777,7 @@ struct cec_event {
#define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02
#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL 0x01
-#define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */
+#define CEC_MSG_GIVE_FEATURES 0xa5 /* CEC 2.0 */
/* Deck Control Feature */
@@ -1067,7 +1067,7 @@ struct cec_event {
#define CEC_OP_AUD_FMT_ID_CEA861 0
#define CEC_OP_AUD_FMT_ID_CEA861_CXT 1
-#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73
+#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73 /* CEC 2.0 */
/* Audio Rate Control Feature */
#define CEC_MSG_SET_AUDIO_RATE 0x9a
@@ -1091,7 +1091,6 @@ struct cec_event {
/* Dynamic Audio Lipsync Feature */
-/* Only for CEC 2.0 and up */
#define CEC_MSG_REQUEST_CURRENT_LATENCY 0xa7
#define CEC_MSG_REPORT_CURRENT_LATENCY 0xa8
/* Low Latency Mode Operand (low_latency_mode) */
@@ -1104,6 +1103,30 @@ struct cec_event {
#define CEC_OP_AUD_OUT_COMPENSATED_PARTIAL_DELAY 3
+/* Latency Indication Protocol Feature */
+#define CEC_MSG_REQUEST_LIP_SUPPORT 0x50 /* CEC 2.0 */
+#define CEC_MSG_REPORT_LIP_SUPPORT 0x51 /* CEC 2.0 */
+#define CEC_MSG_REQUEST_AUDIO_AND_VIDEO_LATENCY 0x52 /* CEC 2.0 */
+/* HDR Format Operand (hdr_format) */
+#define CEC_OP_HDR_FORMAT_GAMMA_SDR 0
+#define CEC_OP_HDR_FORMAT_GAMMA_HDR 1
+#define CEC_OP_HDR_FORMAT_PQ 2
+#define CEC_OP_HDR_FORMAT_HLG 3
+#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_1 8
+#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_2 9
+#define CEC_OP_HDR_FORMAT_DYNAMIC_HDR_TYPE_4 11
+#define CEC_OP_HDR_FORMAT_DV_SINK_LED 16
+#define CEC_OP_HDR_FORMAT_DV_SOURCE_LED 17
+#define CEC_OP_HDR_FORMAT_HDR10PLUS 24
+#define CEC_OP_HDR_FORMAT_ETSI_TS_103_433 32
+#define CEC_MSG_REPORT_AUDIO_AND_VIDEO_LATENCY 0x53 /* CEC 2.0 */
+#define CEC_MSG_REQUEST_AUDIO_LATENCY 0x54 /* CEC 2.0 */
+#define CEC_MSG_REPORT_AUDIO_LATENCY 0x55 /* CEC 2.0 */
+#define CEC_MSG_REQUEST_VIDEO_LATENCY 0x56 /* CEC 2.0 */
+#define CEC_MSG_REPORT_VIDEO_LATENCY 0x57 /* CEC 2.0 */
+#define CEC_MSG_UPDATE_SQID 0x58 /* CEC 2.0 */
+
+
/* Capability Discovery and Control Feature */
#define CEC_MSG_CDC_MESSAGE 0xf8
/* Ethernet-over-HDMI: nobody ever does this... */
diff --git a/include/uapi/linux/rkisp1-config.h b/include/uapi/linux/rkisp1-config.h
index b2d2a71f7baf..d97384abc157 100644
--- a/include/uapi/linux/rkisp1-config.h
+++ b/include/uapi/linux/rkisp1-config.h
@@ -967,6 +967,92 @@ struct rkisp1_cif_isp_wdr_config {
__u8 use_iref;
};
+/*
+ * enum rkisp1_cif_isp_cac_h_clip_mode - horizontal clipping mode
+ *
+ * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX: +/- 4 pixels
+ * @RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX: +/- 4/5 pixels depending on bayer position
+ */
+enum rkisp1_cif_isp_cac_h_clip_mode {
+ RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4PX = 0,
+ RKISP1_CIF_ISP_CAC_H_CLIP_MODE_4_5PX = 1,
+};
+
+/**
+ * enum rkisp1_cif_isp_cac_v_clip_mode - vertical clipping mode
+ *
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX: +/- 2 pixels
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX: +/- 3 pixels
+ * @RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX: +/- 3/4 pixels depending on bayer position
+ */
+enum rkisp1_cif_isp_cac_v_clip_mode {
+ RKISP1_CIF_ISP_CAC_V_CLIP_MODE_2PX = 0,
+ RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3PX = 1,
+ RKISP1_CIF_ISP_CAC_V_CLIP_MODE_3_4PX = 2,
+};
+
+/**
+ * struct rkisp1_cif_isp_cac_config - chromatic aberration correction configuration
+ *
+ * The correction is carried out by shifting the red and blue pixels relative
+ * to the green ones, depending on the distance from the optical center:
+ *
+ * @h_count_start: horizontal coordinate of the optical center (13-bit unsigned integer; [1,8191])
+ * @v_count_start: vertical coordinate of the optical center (13-bit unsigned integer; [1,8191])
+ *
+ * For each pixel, the x/y distances from the optical center are calculated and
+ * then transformed into the [0,255] range based on the following formula:
+ *
+ * (((d << 4) >> ns) * nf) >> 5
+ *
+ * where `d` is the distance, `ns` and `nf` are the normalization parameters:
+ *
+ * @x_nf: horizontal normalization scale parameter (5-bit unsigned integer; [0,31])
+ * @x_ns: horizontal normalization shift parameter (4-bit unsigned integer; [0,15])
+ *
+ * @y_nf: vertical normalization scale parameter (5-bit unsigned integer; [0,31])
+ * @y_ns: vertical normalization shift parameter (4-bit unsigned integer; [0,15])
+ *
+ * These parameters should be chosen based on the image resolution, the position
+ * of the optical center, and the shape of pixels, so that no normalized distance
+ * is larger than 255. If the pixels have square shape, the two sets of parameters
+ * should be equal.
+ *
+ * The actual amount of correction is calculated with a third degree polynomial:
+ *
+ * c[0] * r + c[1] * r^2 + c[2] * r^3
+ *
+ * where `c` is the set of coefficients for the given color, and `r` is distance:
+ *
+ * @red: red coefficients (5.4 two's complement; [-16,15.9375])
+ * @blue: blue coefficients (5.4 two's complement; [-16,15.9375])
+ *
+ * Finally, the amount is clipped as requested:
+ *
+ * @h_clip_mode: maximum horizontal shift (from enum rkisp1_cif_isp_cac_h_clip_mode)
+ * @v_clip_mode: maximum vertical shift (from enum rkisp1_cif_isp_cac_v_clip_mode)
+ *
+ * A positive result will shift away from the optical center, while a negative
+ * one will shift towards the optical center. In the latter case, the pixel
+ * values at the edges are duplicated.
+ */
+struct rkisp1_cif_isp_cac_config {
+ __u8 h_clip_mode;
+ __u8 v_clip_mode;
+
+ __u16 h_count_start;
+ __u16 v_count_start;
+
+ __u16 red[3];
+ __u16 blue[3];
+
+ __u8 x_nf;
+ __u8 x_ns;
+
+ __u8 y_nf;
+ __u8 y_ns;
+};
+
/*---------- PART2: Measurement Statistics ------------*/
/**
@@ -1138,6 +1224,7 @@ struct rkisp1_stat_buffer {
* @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND: Companding expand curve
* @RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS: Companding compress curve
* @RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR: Wide dynamic range
+ * @RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC: Chromatic aberration correction
*/
enum rkisp1_ext_params_block_type {
RKISP1_EXT_PARAMS_BLOCK_TYPE_BLS,
@@ -1161,6 +1248,7 @@ enum rkisp1_ext_params_block_type {
RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_EXPAND,
RKISP1_EXT_PARAMS_BLOCK_TYPE_COMPAND_COMPRESS,
RKISP1_EXT_PARAMS_BLOCK_TYPE_WDR,
+ RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC,
};
/* For backward compatibility */
@@ -1507,6 +1595,22 @@ struct rkisp1_ext_params_wdr_config {
struct rkisp1_cif_isp_wdr_config config;
} __attribute__((aligned(8)));
+/**
+ * struct rkisp1_ext_params_cac_config - RkISP1 extensible params CAC config
+ *
+ * RkISP1 extensible parameters CAC block.
+ * Identified by :c:type:`RKISP1_EXT_PARAMS_BLOCK_TYPE_CAC`.
+ *
+ * @header: The RkISP1 extensible parameters header, see
+ * :c:type:`rkisp1_ext_params_block_header`
+ * @config: CAC configuration, see
+ * :c:type:`rkisp1_cif_isp_cac_config`
+ */
+struct rkisp1_ext_params_cac_config {
+ struct rkisp1_ext_params_block_header header;
+ struct rkisp1_cif_isp_cac_config config;
+} __attribute__((aligned(8)));
+
/*
* The rkisp1_ext_params_compand_curve_config structure is counted twice as it
* is used for both the COMPAND_EXPAND and COMPAND_COMPRESS block types.
@@ -1532,14 +1636,15 @@ struct rkisp1_ext_params_wdr_config {
sizeof(struct rkisp1_ext_params_compand_bls_config) +\
sizeof(struct rkisp1_ext_params_compand_curve_config) +\
sizeof(struct rkisp1_ext_params_compand_curve_config) +\
- sizeof(struct rkisp1_ext_params_wdr_config))
+ sizeof(struct rkisp1_ext_params_wdr_config) +\
+ sizeof(struct rkisp1_ext_params_cac_config))
/**
- * enum rksip1_ext_param_buffer_version - RkISP1 extensible parameters version
+ * enum rkisp1_ext_param_buffer_version - RkISP1 extensible parameters version
*
* @RKISP1_EXT_PARAM_BUFFER_V1: First version of RkISP1 extensible parameters
*/
-enum rksip1_ext_param_buffer_version {
+enum rkisp1_ext_param_buffer_version {
RKISP1_EXT_PARAM_BUFFER_V1 = V4L2_ISP_PARAMS_VERSION_V1,
};
@@ -1601,7 +1706,7 @@ enum rksip1_ext_param_buffer_version {
* +---------------------------------------------------------------------+
*
* @version: The RkISP1 extensible parameters buffer version, see
- * :c:type:`rksip1_ext_param_buffer_version`
+ * :c:type:`rkisp1_ext_param_buffer_version`
* @data_size: The RkISP1 configuration data effective size, excluding this
* header
* @data: The RkISP1 extensible configuration data blocks
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index 68dd0c4e47b2..affec0ab4781 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -464,6 +464,8 @@ enum v4l2_mpeg_video_intra_refresh_period_type {
V4L2_CID_MPEG_VIDEO_INTRA_REFRESH_PERIOD_TYPE_CYCLIC = 1,
};
+#define V4L2_CID_MPEG_VIDEO_BACKGROUND_DETECTION (V4L2_CID_CODEC_BASE + 238)
+
/* CIDs for the MPEG-2 Part 2 (H.262) codec */
#define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270)
enum v4l2_mpeg_video_mpeg2_level {