summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-30 13:10:28 +0100
committerMark Brown <broonie@kernel.org>2026-09-30 13:10:28 +0100
commitf68b7d2660db090105778a2899dd9edeb94bf51c (patch)
treec9483418b6cbc27f7474720e3a6dec70e5db0fb8
parentfb3b8808dfbf9b41df01e90c68c40a9757cb7694 (diff)
parent58348f64125e9a3e44d3abb275ca7f4e6c9641e5 (diff)
downloadlinux-next-f68b7d2660db090105778a2899dd9edeb94bf51c.tar.gz
linux-next-f68b7d2660db090105778a2899dd9edeb94bf51c.zip
Merge branch 'next' of git://linuxtv.org/media-ci/media-pending.git
-rw-r--r--Documentation/admin-guide/media/vivid.rst4
-rw-r--r--Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml71
-rw-r--r--Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml88
-rw-r--r--Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml121
-rw-r--r--Documentation/devicetree/bindings/media/i2c/hynix,hi846.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ite,it6625.yaml175
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml94
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov5675.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml5
-rw-r--r--Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/thine,thp7312.yaml3
-rw-r--r--Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt48
-rw-r--r--Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.yaml93
-rw-r--r--Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml4
-rw-r--r--Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml13
-rw-r--r--Documentation/devicetree/bindings/media/video-interface-devices.yaml17
-rw-r--r--Documentation/driver-api/media/tx-rx.rst6
-rw-r--r--Documentation/userspace-api/media/drivers/dcmipp.rst14
-rw-r--r--Documentation/userspace-api/media/drivers/index.rst1
-rw-r--r--MAINTAINERS51
-rw-r--r--arch/arm/boot/dts/nvidia/tegra30-asus-nexus7-grouper-common.dtsi3
-rw-r--r--arch/arm/boot/dts/nvidia/tegra30-asus-transformer-common.dtsi3
-rw-r--r--arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts4
-rw-r--r--arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi3
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtso3
-rw-r--r--arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi3
-rw-r--r--arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts3
-rw-r--r--arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts3
-rw-r--r--arch/arm64/boot/dts/qcom/sdm670-google-common.dtsi3
-rw-r--r--arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx219.dtso3
-rw-r--r--arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx462.dtso3
-rw-r--r--arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx219.dtso3
-rw-r--r--arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx462.dtso3
-rw-r--r--arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi3
-rw-r--r--arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso3
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts5
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam0.dtso3
-rw-r--r--arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam1.dtso3
-rw-r--r--drivers/media/cec/platform/meson/ao-cec-g12a.c2
-rw-r--r--drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c4
-rw-r--r--drivers/media/common/cypress_firmware.c2
-rw-r--r--drivers/media/common/saa7146/saa7146_core.c12
-rw-r--r--drivers/media/common/saa7146/saa7146_video.c11
-rw-r--r--drivers/media/dvb-frontends/drxd_map_firm.h4
-rw-r--r--drivers/media/dvb-frontends/stv0900_core.c2
-rw-r--r--drivers/media/dvb-frontends/stv090x.c2
-rw-r--r--drivers/media/i2c/Kconfig51
-rw-r--r--drivers/media/i2c/Makefile3
-rw-r--r--drivers/media/i2c/adv7170.c1
-rw-r--r--drivers/media/i2c/adv7175.c3
-rw-r--r--drivers/media/i2c/adv7180.c5
-rw-r--r--drivers/media/i2c/adv7183.c3
-rw-r--r--drivers/media/i2c/adv7343.c10
-rw-r--r--drivers/media/i2c/adv7393.c10
-rw-r--r--drivers/media/i2c/adv748x/adv748x-afe.c1
-rw-r--r--drivers/media/i2c/adv748x/adv748x-core.c13
-rw-r--r--drivers/media/i2c/adv748x/adv748x-csi2.c1
-rw-r--r--drivers/media/i2c/adv748x/adv748x-hdmi.c1
-rw-r--r--drivers/media/i2c/adv7511-v4l2.c1
-rw-r--r--drivers/media/i2c/adv7604.c7
-rw-r--r--drivers/media/i2c/adv7842.c11
-rw-r--r--drivers/media/i2c/ak881x.c2
-rw-r--r--drivers/media/i2c/alvium-csi2.c4
-rw-r--r--drivers/media/i2c/ar0521.c2
-rw-r--r--drivers/media/i2c/ccs/ccs-core.c12
-rw-r--r--drivers/media/i2c/ccs/ccs-data.h4
-rw-r--r--drivers/media/i2c/cvs/Kconfig1
-rw-r--r--drivers/media/i2c/cvs/core.c30
-rw-r--r--drivers/media/i2c/cvs/v4l2.c96
-rw-r--r--drivers/media/i2c/cx25840/cx25840-core.c41
-rw-r--r--drivers/media/i2c/ds90ub913.c1
-rw-r--r--drivers/media/i2c/ds90ub953.c1
-rw-r--r--drivers/media/i2c/ds90ub960.c1
-rw-r--r--drivers/media/i2c/et8ek8/et8ek8_driver.c1
-rw-r--r--drivers/media/i2c/gc0308.c1
-rw-r--r--drivers/media/i2c/gc0310.c2
-rw-r--r--drivers/media/i2c/gc05a2.c4
-rw-r--r--drivers/media/i2c/gc08a3.c4
-rw-r--r--drivers/media/i2c/gc2145.c2
-rw-r--r--drivers/media/i2c/hi556.c2
-rw-r--r--drivers/media/i2c/hi846.c2
-rw-r--r--drivers/media/i2c/hi847.c3
-rw-r--r--drivers/media/i2c/hm1246.c1287
-rw-r--r--drivers/media/i2c/imx111.c1
-rw-r--r--drivers/media/i2c/imx208.c1
-rw-r--r--drivers/media/i2c/imx214.c4
-rw-r--r--drivers/media/i2c/imx219.c42
-rw-r--r--drivers/media/i2c/imx258.c2
-rw-r--r--drivers/media/i2c/imx274.c3
-rw-r--r--drivers/media/i2c/imx283.c2
-rw-r--r--drivers/media/i2c/imx290.c4
-rw-r--r--drivers/media/i2c/imx296.c7
-rw-r--r--drivers/media/i2c/imx319.c1
-rw-r--r--drivers/media/i2c/imx334.c135
-rw-r--r--drivers/media/i2c/imx335.c4
-rw-r--r--drivers/media/i2c/imx355.c4
-rw-r--r--drivers/media/i2c/imx412.c3
-rw-r--r--drivers/media/i2c/imx415.c4
-rw-r--r--drivers/media/i2c/imx471.c6
-rw-r--r--drivers/media/i2c/imx678.c2
-rw-r--r--drivers/media/i2c/isl7998x.c1
-rw-r--r--drivers/media/i2c/it6625.c2391
-rw-r--r--drivers/media/i2c/lt6911uxe.c5
-rw-r--r--drivers/media/i2c/max9286.c1
-rw-r--r--drivers/media/i2c/max96714.c1
-rw-r--r--drivers/media/i2c/max96717.c1
-rw-r--r--drivers/media/i2c/ml86v7667.c1
-rw-r--r--drivers/media/i2c/mt9m001.c9
-rw-r--r--drivers/media/i2c/mt9m111.c3
-rw-r--r--drivers/media/i2c/mt9m114.c6
-rw-r--r--drivers/media/i2c/mt9p031.c3
-rw-r--r--drivers/media/i2c/mt9t112.c3
-rw-r--r--drivers/media/i2c/mt9v011.c1
-rw-r--r--drivers/media/i2c/mt9v032.c3
-rw-r--r--drivers/media/i2c/mt9v111.c1
-rw-r--r--drivers/media/i2c/og01a1b.c8
-rw-r--r--drivers/media/i2c/og0ve1b.c3
-rw-r--r--drivers/media/i2c/os02g10.c934
-rw-r--r--drivers/media/i2c/os05b10.c2
-rw-r--r--drivers/media/i2c/ov01a10.c3
-rw-r--r--drivers/media/i2c/ov02a10.c3
-rw-r--r--drivers/media/i2c/ov02c10.c1
-rw-r--r--drivers/media/i2c/ov02e10.c1
-rw-r--r--drivers/media/i2c/ov08d10.c1
-rw-r--r--drivers/media/i2c/ov08x40.c1
-rw-r--r--drivers/media/i2c/ov13858.c1
-rw-r--r--drivers/media/i2c/ov13b10.c1
-rw-r--r--drivers/media/i2c/ov2640.c2
-rw-r--r--drivers/media/i2c/ov2659.c1
-rw-r--r--drivers/media/i2c/ov2680.c3
-rw-r--r--drivers/media/i2c/ov2685.c2
-rw-r--r--drivers/media/i2c/ov2732.c4
-rw-r--r--drivers/media/i2c/ov2735.c5
-rw-r--r--drivers/media/i2c/ov2740.c1
-rw-r--r--drivers/media/i2c/ov4689.c2
-rw-r--r--drivers/media/i2c/ov5640.c2
-rw-r--r--drivers/media/i2c/ov5645.c4
-rw-r--r--drivers/media/i2c/ov5647.c6
-rw-r--r--drivers/media/i2c/ov5648.c5
-rw-r--r--drivers/media/i2c/ov5670.c2
-rw-r--r--drivers/media/i2c/ov5675.c2
-rw-r--r--drivers/media/i2c/ov5693.c28
-rw-r--r--drivers/media/i2c/ov5695.c1
-rw-r--r--drivers/media/i2c/ov6211.c3
-rw-r--r--drivers/media/i2c/ov64a40.c2
-rw-r--r--drivers/media/i2c/ov7251.c4
-rw-r--r--drivers/media/i2c/ov7670.c1
-rw-r--r--drivers/media/i2c/ov772x.c4
-rw-r--r--drivers/media/i2c/ov7740.c1
-rw-r--r--drivers/media/i2c/ov8856.c33
-rw-r--r--drivers/media/i2c/ov8858.c3
-rw-r--r--drivers/media/i2c/ov8865.c2
-rw-r--r--drivers/media/i2c/ov9282.c4
-rw-r--r--drivers/media/i2c/ov9640.c2
-rw-r--r--drivers/media/i2c/ov9650.c1
-rw-r--r--drivers/media/i2c/ov9734.c1
-rw-r--r--drivers/media/i2c/rdacm20.c1
-rw-r--r--drivers/media/i2c/rdacm21.c1
-rw-r--r--drivers/media/i2c/rj54n1cb0c.c3
-rw-r--r--drivers/media/i2c/s5c73m3/s5c73m3-core.c2
-rw-r--r--drivers/media/i2c/s5k3m5.c4
-rw-r--r--drivers/media/i2c/s5k5baf.c3
-rw-r--r--drivers/media/i2c/s5k6a3.c1
-rw-r--r--drivers/media/i2c/s5kjn1.c4
-rw-r--r--drivers/media/i2c/saa6752hs.c1
-rw-r--r--drivers/media/i2c/saa7115.c1
-rw-r--r--drivers/media/i2c/saa717x.c1
-rw-r--r--drivers/media/i2c/st-mipid02.c1
-rw-r--r--drivers/media/i2c/t4ka3.c4
-rw-r--r--drivers/media/i2c/tc358743.c6
-rw-r--r--drivers/media/i2c/tc358746.c1
-rw-r--r--drivers/media/i2c/tda1997x.c3
-rw-r--r--drivers/media/i2c/thp7312.c1
-rw-r--r--drivers/media/i2c/ths7303.c10
-rw-r--r--drivers/media/i2c/ths8200.c14
-rw-r--r--drivers/media/i2c/ths8200_regs.h14
-rw-r--r--drivers/media/i2c/tvp514x.c1
-rw-r--r--drivers/media/i2c/tvp5150.c3
-rw-r--r--drivers/media/i2c/tvp7002.c1
-rw-r--r--drivers/media/i2c/tw9900.c1
-rw-r--r--drivers/media/i2c/tw9910.c2
-rw-r--r--drivers/media/i2c/vd55g1.c4
-rw-r--r--drivers/media/i2c/vd56g3.c4
-rw-r--r--drivers/media/i2c/vgxy61.c4
-rw-r--r--drivers/media/i2c/wm8739.c2
-rw-r--r--drivers/media/pci/bt8xx/bttv-driver.c8
-rw-r--r--drivers/media/pci/cobalt/cobalt-driver.c8
-rw-r--r--drivers/media/pci/cobalt/cobalt-v4l2.c10
-rw-r--r--drivers/media/pci/cx18/cx18-av-core.c1
-rw-r--r--drivers/media/pci/cx18/cx18-controls.c2
-rw-r--r--drivers/media/pci/cx18/cx18-driver.c9
-rw-r--r--drivers/media/pci/cx18/cx18-ioctl.c2
-rw-r--r--drivers/media/pci/cx18/cx18-queue.c5
-rw-r--r--drivers/media/pci/cx18/cx23418.h2
-rw-r--r--drivers/media/pci/cx23885/cx23885-core.c4
-rw-r--r--drivers/media/pci/cx23885/cx23885-dvb.c14
-rw-r--r--drivers/media/pci/cx23885/cx23885-video.c4
-rw-r--r--drivers/media/pci/cx88/cx88-input.c22
-rw-r--r--drivers/media/pci/cx88/cx88-mpeg.c9
-rw-r--r--drivers/media/pci/cx88/cx88-video.c8
-rw-r--r--drivers/media/pci/hws/hws.h4
-rw-r--r--drivers/media/pci/hws/hws_pci.c6
-rw-r--r--drivers/media/pci/hws/hws_reg.h10
-rw-r--r--drivers/media/pci/hws/hws_v4l2_ioctl.c5
-rw-r--r--drivers/media/pci/hws/hws_video.c6
-rw-r--r--drivers/media/pci/intel/ipu-bridge.c148
-rw-r--r--drivers/media/pci/intel/ipu3/ipu3-cio2.c1
-rw-r--r--drivers/media/pci/intel/ipu6/Kconfig9
-rw-r--r--drivers/media/pci/intel/ipu6/Makefile10
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-bus.h6
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.c568
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-buttress.h50
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.c195
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-cpd.h43
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.c24
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-dma.h2
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.c586
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-fw-isys.h48
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c576
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h18
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.c215
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-queue.h6
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c23
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h3
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.c750
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys-video.h62
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.c605
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-isys.h105
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu-hw.c296
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.c133
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-mmu.h158
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h111
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.c556
-rw-r--r--drivers/media/pci/intel/ipu6/ipu6.h200
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-boot.c405
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-boot.h46
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-fw-com.c74
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-fw-com.h53
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-fw-isys.c796
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-fw-isys.h296
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.c1072
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.h16
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-isys-csi2-regs.h1189
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-mmu-hw.c858
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-mmu-hw.h254
-rw-r--r--drivers/media/pci/intel/ipu6/ipu7-platform-regs.h32
-rw-r--r--drivers/media/pci/intel/ivsc/mei_ace.c4
-rw-r--r--drivers/media/pci/intel/ivsc/mei_csi.c8
-rw-r--r--drivers/media/pci/ivtv/ivtv-controls.c2
-rw-r--r--drivers/media/pci/ivtv/ivtv-ioctl.c2
-rw-r--r--drivers/media/pci/saa7134/saa7134-core.c8
-rw-r--r--drivers/media/pci/saa7134/saa7134-empress.c4
-rw-r--r--drivers/media/pci/saa7134/saa7134-input.c7
-rw-r--r--drivers/media/pci/saa7146/mxb.c4
-rw-r--r--drivers/media/pci/saa7164/saa7164-core.c32
-rw-r--r--drivers/media/pci/saa7164/saa7164.h1
-rw-r--r--drivers/media/pci/tw68/tw68-core.c10
-rw-r--r--drivers/media/platform/amd/isp4/isp4_subdev.c1
-rw-r--r--drivers/media/platform/amd/isp4/isp4_video.c2
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-core.c1
-rw-r--r--drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c3
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c1
-rw-r--r--drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c1
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-isp.c3
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-resizer.c15
-rw-r--r--drivers/media/platform/arm/mali-c55/mali-c55-tpg.c1
-rw-r--r--drivers/media/platform/aspeed/aspeed-video.c12
-rw-r--r--drivers/media/platform/atmel/atmel-isi.c11
-rw-r--r--drivers/media/platform/broadcom/bcm2835-unicam.c1
-rw-r--r--drivers/media/platform/cadence/cdns-csi2rx.c5
-rw-r--r--drivers/media/platform/cadence/cdns-csi2tx.c1
-rw-r--r--drivers/media/platform/intel/pxa_camera.c6
-rw-r--r--drivers/media/platform/m2m-deinterlace.c2
-rw-r--r--drivers/media/platform/marvell/Kconfig1
-rw-r--r--drivers/media/platform/marvell/cafe-driver.c2
-rw-r--r--drivers/media/platform/marvell/mcam-core.c24
-rw-r--r--drivers/media/platform/marvell/mmp-driver.c5
-rw-r--r--drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h2
-rw-r--r--drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h4
-rw-r--r--drivers/media/platform/microchip/microchip-csi2dc.c2
-rw-r--r--drivers/media/platform/microchip/microchip-isc-base.c82
-rw-r--r--drivers/media/platform/microchip/microchip-isc-clk.c7
-rw-r--r--drivers/media/platform/microchip/microchip-isc-regs.h16
-rw-r--r--drivers/media/platform/microchip/microchip-isc-scaler.c2
-rw-r--r--drivers/media/platform/microchip/microchip-isc.h5
-rw-r--r--drivers/media/platform/microchip/microchip-sama5d2-isc.c48
-rw-r--r--drivers/media/platform/microchip/microchip-sama7g5-isc.c48
-rw-r--r--drivers/media/platform/nuvoton/npcm-video.c18
-rw-r--r--drivers/media/platform/nxp/Kconfig16
-rw-r--r--drivers/media/platform/nxp/Makefile1
-rw-r--r--drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c23
-rw-r--r--drivers/media/platform/nxp/imx-mipi-csis.c3
-rw-r--r--drivers/media/platform/nxp/imx7-media-csi.c1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c1
-rw-r--r--drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c3
-rw-r--r--drivers/media/platform/nxp/imx8mq-mipi-csi2.c1
-rw-r--r--drivers/media/platform/nxp/imx95-csi-formatter.c759
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c3
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c3
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c3
-rw-r--r--drivers/media/platform/qcom/camss/camss-tpg.c3
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c12
-rw-r--r--drivers/media/platform/qcom/camss/camss.c17
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/csi2.c1
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c1
-rw-r--r--drivers/media/platform/renesas/rcar-csi2.c377
-rw-r--r--drivers/media/platform/renesas/rcar-isp/csisp.c228
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-core.c27
-rw-r--r--drivers/media/platform/renesas/rcar-vin/rcar-dma.c2
-rw-r--r--drivers/media/platform/renesas/rcar_drif.c2
-rw-r--r--drivers/media/platform/renesas/renesas-ceu.c7
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c3
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h2
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c3
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c3
-rw-r--r--drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c13
-rw-r--r--drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c1
-rw-r--r--drivers/media/platform/renesas/sh_vou.c6
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_brx.c3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_dl.c2
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_drm.c18
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.c4
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_entity.h1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_histo.c3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_hsit.c1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_rwpf.c3
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_sru.c1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uds.c1
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_uif.c2
-rw-r--r--drivers/media/platform/renesas/vsp1/vsp1_vspx.c3
-rw-r--r--drivers/media/platform/rockchip/rkcif/rkcif-interface.c3
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c1
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c4
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c3
-rw-r--r--drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-capture.c8
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-isp.c1
-rw-r--r--drivers/media/platform/samsung/exynos4-is/fimc-lite.c3
-rw-r--r--drivers/media/platform/samsung/exynos4-is/mipi-csis.c1
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-capture.c3
-rw-r--r--drivers/media/platform/samsung/s3c-camif/camif-core.c2
-rw-r--r--drivers/media/platform/st/stm32/stm32-csi.c6
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmi.c31
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/Makefile3
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c30
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c (renamed from drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c)600
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h99
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c124
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c125
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c493
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c181
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h42
-rw-r--r--drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c942
-rw-r--r--drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c1
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c156
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h9
-rw-r--r--drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c27
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c108
-rw-r--r--drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h2
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c113
-rw-r--r--drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h2
-rw-r--r--drivers/media/platform/synopsys/dw-mipi-csi2rx.c1
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c423
-rw-r--r--drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h8
-rw-r--r--drivers/media/platform/ti/Kconfig11
-rw-r--r--drivers/media/platform/ti/am437x/am437x-vpfe.c2
-rw-r--r--drivers/media/platform/ti/cal/cal-camerarx.c1
-rw-r--r--drivers/media/platform/ti/cal/cal-video.c19
-rw-r--r--drivers/media/platform/ti/cal/cal.c10
-rw-r--r--drivers/media/platform/ti/davinci/vpif_capture.c5
-rw-r--r--drivers/media/platform/ti/davinci/vpif_display.c3
-rw-r--r--drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c25
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccdc.c5
-rw-r--r--drivers/media/platform/ti/omap3isp/ispccp2.c3
-rw-r--r--drivers/media/platform/ti/omap3isp/ispcsi2.c3
-rw-r--r--drivers/media/platform/ti/omap3isp/isppreview.c5
-rw-r--r--drivers/media/platform/ti/omap3isp/ispresizer.c5
-rw-r--r--drivers/media/platform/ti/omap3isp/ispvideo.c4
-rw-r--r--drivers/media/platform/ti/vpe/vip.c4
-rw-r--r--drivers/media/platform/via/via-camera.c4
-rw-r--r--drivers/media/platform/video-mux.c1
-rw-r--r--drivers/media/platform/xilinx/xilinx-csi2rxss.c1
-rw-r--r--drivers/media/platform/xilinx/xilinx-tpg.c1
-rw-r--r--drivers/media/radio/si4713/radio-usb-si4713.c4
-rw-r--r--drivers/media/radio/si4713/si4713.c2
-rw-r--r--drivers/media/rc/bpf-lirc.c18
-rw-r--r--drivers/media/rc/ene_ir.c4
-rw-r--r--drivers/media/rc/fintek-cir.c13
-rw-r--r--drivers/media/rc/fintek-cir.h22
-rw-r--r--drivers/media/rc/imon.c173
-rw-r--r--drivers/media/rc/ir-hix5hd2.c5
-rw-r--r--drivers/media/rc/ir-mce_kbd-decoder.c5
-rw-r--r--drivers/media/rc/ir_toy.c4
-rw-r--r--drivers/media/rc/ite-cir.c1
-rw-r--r--drivers/media/rc/ite-cir.h1
-rw-r--r--drivers/media/rc/lirc_dev.c5
-rw-r--r--drivers/media/rc/mceusb.c1
-rw-r--r--drivers/media/rc/meson-ir-tx.c20
-rw-r--r--drivers/media/rc/nuvoton-cir.h3
-rw-r--r--drivers/media/rc/rc-ir-raw.c86
-rw-r--r--drivers/media/rc/rc-loopback.c5
-rw-r--r--drivers/media/rc/rc-main.c206
-rw-r--r--drivers/media/rc/redrat3.c32
-rw-r--r--drivers/media/rc/serial_ir.c2
-rw-r--r--drivers/media/rc/streamzap.c1
-rw-r--r--drivers/media/rc/sunxi-cir.c2
-rw-r--r--drivers/media/spi/Kconfig4
-rw-r--r--drivers/media/test-drivers/vicodec/codec-fwht.h2
-rw-r--r--drivers/media/test-drivers/vicodec/vicodec-core.c14
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_bridge.c75
-rw-r--r--drivers/media/test-drivers/vidtv/vidtv_demod.c9
-rw-r--r--drivers/media/test-drivers/vim2m.c33
-rw-r--r--drivers/media/test-drivers/vimc/vimc-debayer.c1
-rw-r--r--drivers/media/test-drivers/vimc/vimc-scaler.c3
-rw-r--r--drivers/media/test-drivers/vimc/vimc-sensor.c9
-rw-r--r--drivers/media/test-drivers/vivid/vivid-cec.c10
-rw-r--r--drivers/media/test-drivers/vivid/vivid-core.c1
-rw-r--r--drivers/media/test-drivers/vivid/vivid-vid-cap.c13
-rw-r--r--drivers/media/tuners/tda18250.c3
-rw-r--r--drivers/media/usb/au0828/au0828-core.c4
-rw-r--r--drivers/media/usb/au0828/au0828-dvb.c20
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-417.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-audio.c15
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-cards.c2
-rw-r--r--drivers/media/usb/cx231xx/cx231xx-video.c4
-rw-r--r--drivers/media/usb/cx231xx/cx231xx.h2
-rw-r--r--drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c4
-rw-r--r--drivers/media/usb/dvb-usb/cxusb-analog.c6
-rw-r--r--drivers/media/usb/dvb-usb/dib0700_core.c2
-rw-r--r--drivers/media/usb/dvb-usb/dvb-usb-firmware.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-camera.c2
-rw-r--r--drivers/media/usb/em28xx/em28xx-video.c4
-rw-r--r--drivers/media/usb/go7007/go7007-driver.c5
-rw-r--r--drivers/media/usb/go7007/go7007-usb.c8
-rw-r--r--drivers/media/usb/go7007/go7007-v4l2.c2
-rw-r--r--drivers/media/usb/go7007/s2250-board.c1
-rw-r--r--drivers/media/usb/gspca/gspca.c3
-rw-r--r--drivers/media/usb/gspca/ov519.c2
-rw-r--r--drivers/media/usb/gspca/w996Xcf.c2
-rw-r--r--drivers/media/usb/hackrf/hackrf.c7
-rw-r--r--drivers/media/usb/pvrusb2/pvrusb2-hdw.c14
-rw-r--r--drivers/media/usb/usbtv/usbtv-core.c2
-rw-r--r--drivers/media/usb/usbtv/usbtv-video.c4
-rw-r--r--drivers/media/v4l2-core/v4l2-common.c21
-rw-r--r--drivers/media/v4l2-core/v4l2-ctrls-core.c19
-rw-r--r--drivers/media/v4l2-core/v4l2-mc.c5
-rw-r--r--drivers/media/v4l2-core/v4l2-subdev.c163
-rw-r--r--drivers/platform/x86/intel/int3472/discrete.c81
-rw-r--r--drivers/platform/x86/intel/int3472/tps68470.c2
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-gc2235.c1
-rw-r--r--drivers/staging/media/atomisp/i2c/atomisp-ov2722.c1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_cmd.c16
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_csi2.c1
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_subdev.c3
-rw-r--r--drivers/staging/media/atomisp/pci/atomisp_v4l2.c8
-rw-r--r--drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h4
-rw-r--r--drivers/staging/media/av7110/av7110.c2
-rw-r--r--drivers/staging/media/av7110/av7110_ir.c2
-rw-r--r--drivers/staging/media/av7110/sp8870.c9
-rw-r--r--drivers/staging/media/imx/imx-ic-prp.c2
-rw-r--r--drivers/staging/media/imx/imx-ic-prpencvf.c4
-rw-r--r--drivers/staging/media/imx/imx-media-capture.c1
-rw-r--r--drivers/staging/media/imx/imx-media-csc-scaler.c3
-rw-r--r--drivers/staging/media/imx/imx-media-csi.c3
-rw-r--r--drivers/staging/media/imx/imx-media-dev.c1
-rw-r--r--drivers/staging/media/imx/imx-media-vdic.c1
-rw-r--r--drivers/staging/media/imx/imx6-mipi-csi2.c1
-rw-r--r--drivers/staging/media/ipu3/ipu3-css.c1
-rw-r--r--drivers/staging/media/ipu3/ipu3-v4l2.c3
-rw-r--r--drivers/staging/media/ipu7/TODO28
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-csi2.c2
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-subdev.c1
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys-subdev.h1
-rw-r--r--drivers/staging/media/ipu7/ipu7-isys.c1
-rw-r--r--drivers/staging/media/ipu7/ipu7.c7
-rw-r--r--drivers/staging/media/max96712/max96712.c1
-rw-r--r--drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c1
-rw-r--r--drivers/staging/media/tegra-video/csi.c1
-rw-r--r--drivers/staging/media/tegra-video/vi.c102
-rw-r--r--include/dt-bindings/media/video-interface-devices.h13
-rw-r--r--include/linux/platform_data/x86/int3472.h2
-rw-r--r--include/linux/property.h5
-rw-r--r--include/media/ipu-bridge.h52
-rw-r--r--include/media/ipu6-pci-table.h4
-rw-r--r--include/media/rc-map.h2
-rw-r--r--include/media/v4l2-common.h75
-rw-r--r--include/media/v4l2-ctrls.h6
-rw-r--r--include/media/v4l2-dv-timings.h14
-rw-r--r--include/media/v4l2-subdev.h34
-rw-r--r--include/uapi/linux/it6625.h25
-rw-r--r--include/uapi/linux/media/st/dcmipp_config.h16
-rw-r--r--include/uapi/linux/v4l2-controls.h21
499 files changed, 20194 insertions, 4278 deletions
diff --git a/Documentation/admin-guide/media/vivid.rst b/Documentation/admin-guide/media/vivid.rst
index 034ca7c77fb9..6741f25220fc 100644
--- a/Documentation/admin-guide/media/vivid.rst
+++ b/Documentation/admin-guide/media/vivid.rst
@@ -6,7 +6,7 @@ The Virtual Video Test Driver (vivid)
This driver emulates video4linux hardware of various types: video capture, video
output, vbi capture and output, metadata capture and output, radio receivers and
transmitters, touch capture and a software defined radio receiver. In addition a
-simple framebuffer device is available for testing capture and output overlays.
+simple framebuffer device is available for testing output overlays.
Up to 64 vivid instances can be created, each with up to 16 inputs and 16 outputs.
@@ -35,7 +35,7 @@ This document describes the features implemented by this driver:
- Raw and Sliced VBI capture and output support
- Radio receiver and transmitter support, including RDS support
- Software defined radio (SDR) support
-- Capture and output overlay support
+- Output overlay support
- Metadata capture and output support
- Touch capture support
diff --git a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
index 27403b4c52d6..fbbf1b3f1790 100644
--- a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
+++ b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
@@ -39,6 +39,18 @@ properties:
ID in its "clocks" phandle cell. See
include/dt-bindings/clock/nxp,imx95-clock.h
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 1
+
+patternProperties:
+ '^formatter@[0-9a-f]+$':
+ type: object
+ $ref: /schemas/media/fsl,imx95-csi-formatter.yaml#
+ unevaluatedProperties: false
+
required:
- compatible
- reg
@@ -46,6 +58,23 @@ required:
- power-domains
- clocks
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nxp,imx95-camera-csr
+ then:
+ required:
+ - '#address-cells'
+ - '#size-cells'
+ else:
+ properties:
+ '#address-cells': false
+ '#size-cells': false
+ patternProperties:
+ '^formatter@[0-9a-f]+$': false
+
additionalProperties: false
examples:
@@ -57,4 +86,46 @@ examples:
clocks = <&scmi_clk 114>;
power-domains = <&scmi_devpd 21>;
};
+
+ - |
+ #include <dt-bindings/clock/nxp,imx95-clock.h>
+
+ syscon@4ac10000 {
+ compatible = "nxp,imx95-camera-csr", "syscon";
+ reg = <0x4ac10000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ #clock-cells = <1>;
+ clocks = <&scmi_clk 62>;
+ power-domains = <&scmi_devpd 3>;
+
+ formatter@20 {
+ compatible = "fsl,imx95-csi-formatter";
+ reg = <0x20 0x100>;
+ clocks = <&cameramix_csr IMX95_CLK_CAMBLK_CSI2_FOR0>;
+ power-domains = <&scmi_devpd 3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&mipi_csi_0_out>;
+ };
+
+ };
+
+ port@1 {
+ reg = <1>;
+
+ endpoint {
+ remote-endpoint = <&isi_in_2>;
+ };
+ };
+ };
+ };
+ };
...
diff --git a/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml b/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
new file mode 100644
index 000000000000..58c4e1cc056b
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/fsl,imx95-csi-formatter.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: i.MX95 CSI Pixel Formatter
+
+maintainers:
+ - Guoniu Zhou <guoniu.zhou@nxp.com>
+
+description:
+ The CSI pixel formatting module found on i.MX95 uses packet info, pixel
+ and non-pixel data from the CSI-2 host controller and reformat them to
+ match Pixel Link(PL) definition.
+
+properties:
+ compatible:
+ const: fsl,imx95-csi-formatter
+
+ reg:
+ maxItems: 1
+ description: Register offset and size within the parent syscon
+
+ clocks:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Input port, connects to MIPI CSI-2 receiver output (IDI interface)
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Output port, connects to ISI input via Pixel Link (PL)
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - power-domains
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/nxp,imx95-clock.h>
+
+ formatter@20 {
+ compatible = "fsl,imx95-csi-formatter";
+ reg = <0x20 0x100>;
+ clocks = <&cameramix_csr IMX95_CLK_CAMBLK_CSI2_FOR0>;
+ power-domains = <&scmi_devpd 3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&mipi_csi_0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ endpoint {
+ remote-endpoint = <&isi_in_2>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml b/Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml
new file mode 100644
index 000000000000..9994105552e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright 2025 Matthias Fend <matthias.fend@emfend.at>
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/himax,hm1246.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Himax HM1246-AWD 1/3.7-Inch megapixel SoC image sensor
+
+maintainers:
+ - Matthias Fend <matthias.fend@emfend.at>
+
+description:
+ The Himax HM1246-AWD is a 1/3.7-Inch CMOS image sensor SoC with an active
+ array size of 1296 x 976. It is programmable through an I2C interface and
+ connected via parallel bus.
+
+allOf:
+ - $ref: /schemas/media/video-interface-devices.yaml#
+
+properties:
+ compatible:
+ const: himax,hm1246
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ description: Input reference clock (6 - 27 MHz)
+ maxItems: 1
+
+ reset-gpios:
+ description: Active low XSHUTDOWN pin
+ maxItems: 1
+
+ avdd-supply:
+ description: Power for analog circuit (3.0 - 3.6 V)
+
+ iovdd-supply:
+ description: Power for I/O circuit (1.7 - 3.6 V)
+
+ dvdd-supply:
+ description: Power for digital circuit (1.5 / 1.8 V)
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+ description: Parallel video output port
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ bus-width:
+ default: 10
+
+ hsync-active:
+ default: 1
+
+ vsync-active:
+ default: 1
+
+ pclk-sample:
+ default: 0
+
+ required:
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - avdd-supply
+ - iovdd-supply
+ - dvdd-supply
+ - port
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interfaces.h>
+ #include <dt-bindings/media/video-interface-devices.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ sensor@24 {
+ compatible = "himax,hm1246";
+ reg = <0x24>;
+
+ clocks = <&hm1246_clk>;
+
+ reset-gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&hm1246_avdd>;
+ iovdd-supply = <&hm1246_iovdd>;
+ dvdd-supply = <&hm1246_dvdd>;
+
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
+ rotation = <0>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&isp_par_in>;
+ bus-width = <10>;
+ hsync-active = <1>; /* active high */
+ vsync-active = <1>; /* active high */
+ pclk-sample = <1>; /* sample on rising edge */
+ link-frequencies = /bits/ 64 <42200000>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/hynix,hi846.yaml b/Documentation/devicetree/bindings/media/i2c/hynix,hi846.yaml
index 1a57f2aa1982..b7bc6ba26e6e 100644
--- a/Documentation/devicetree/bindings/media/i2c/hynix,hi846.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/hynix,hi846.yaml
@@ -86,6 +86,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -102,7 +103,7 @@ examples:
vddio-supply = <&reg_camera_vddio>;
reset-gpios = <&gpio1 25 GPIO_ACTIVE_LOW>;
shutdown-gpios = <&gpio5 4 GPIO_ACTIVE_LOW>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
rotation = <0>;
port {
diff --git a/Documentation/devicetree/bindings/media/i2c/ite,it6625.yaml b/Documentation/devicetree/bindings/media/i2c/ite,it6625.yaml
new file mode 100644
index 000000000000..756fe655f534
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ite,it6625.yaml
@@ -0,0 +1,175 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ite,it6625.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ITE IT6625/IT6626 HDMI to dual MIPI CSI-2 bridge
+
+maintainers:
+ - Hermes Wu <Hermes.wu@ite.com.tw>
+
+description:
+ The ITE IT6625 and IT6626 are HDMI to MIPI CSI-2 bridge devices.
+ IT6625 supports an HDMI 2.0 input and converts it to one or two D-PHY CSI
+ outputs, while IT6626 supports an HDMI 2.1 input and converts it to one or
+ two C/D-PHY CSI outputs. The bridges are programmable through I2C and
+ expose two selectable CSI-2 output ports and an HDMI input port. The
+ bridge can operate in split mode or clone mode.
+
+properties:
+ compatible:
+ enum:
+ - ite,it6625
+ - ite,it6626
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ reset-gpios:
+ description:
+ GPIO connected to the active-low reset line.
+ maxItems: 1
+
+ vcc10-supply:
+ description: 1.0V core supply
+
+ vdd33-supply:
+ description: 3.3V supply
+
+ ovdd-supply:
+ description: I/O supply voltage
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: CSI-2 output port MIPI0
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/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
+
+ port@1:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: CSI-2 output port MIPI1
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/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
+
+ port@2:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+ description: HDMI input port
+
+ required:
+ - port@0
+
+required:
+ - compatible
+ - reg
+ - ports
+ - vcc10-supply
+ - vdd33-supply
+ - ovdd-supply
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: ite,it6626
+ then:
+ properties:
+ ports:
+ properties:
+ port@0:
+ properties:
+ endpoint:
+ required:
+ - bus-type
+ port@1:
+ properties:
+ endpoint:
+ required:
+ - bus-type
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hdmi-bridge@4c {
+ compatible = "ite,it6625";
+ reg = <0x4c>;
+ interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+
+ reset-gpios = <&gpio 2 GPIO_ACTIVE_LOW>;
+
+ vcc10-supply = <&vcc10_reg>;
+ vdd33-supply = <&vdd33_reg>;
+ ovdd-supply = <&ovdd_reg>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ csi_out0: endpoint {
+ remote-endpoint = <&csi2_rx0>;
+ bus-type = <4>; /* MEDIA_BUS_TYPE_CSI2_DPHY */
+ data-lanes = <1 2 3 4>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ endpoint {
+ remote-endpoint = <&hdmi_con>;
+ };
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
new file mode 100644
index 000000000000..e240974dcc9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/ovti,os02g10.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: OmniVision OS02G10 Image Sensor
+
+maintainers:
+ - Tarang Raval <tarang.raval@siliconsignals.io>
+
+description:
+ The OmniVision OS02G10 is a 2MP (1920x1080) color CMOS image sensor controlled
+ through an I2C-compatible SCCB bus. It outputs RAW10 format data and supports
+ a 2-lane MIPI interface.
+
+properties:
+ compatible:
+ const: ovti,os02g10
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: XCLK clock
+
+ avdd-supply:
+ description: Analog Domain Power Supply (2.8v)
+
+ dovdd-supply:
+ description: I/O Domain Power Supply (1.8v)
+
+ dvdd-supply:
+ description: Digital core Power Supply (1.5v)
+
+ reset-gpios:
+ maxItems: 1
+ description: Reset Pin GPIO Control (active low)
+
+ port:
+ description: MIPI CSI-2 transmitter port
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ additionalProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ required:
+ - link-frequencies
+
+ required:
+ - endpoint
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - avdd-supply
+ - dovdd-supply
+ - dvdd-supply
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ camera-sensor@3c {
+ compatible = "ovti,os02g10";
+ reg = <0x3c>;
+ clocks = <&os02g10_clk>;
+ reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+
+ avdd-supply = <&os02g10_avdd_2v8>;
+ dvdd-supply = <&os02g10_dvdd_1v5>;
+ dovdd-supply = <&os02g10_dovdd_1v8>;
+
+ port {
+ cam_out: endpoint {
+ remote-endpoint = <&mipi_in_cam>;
+ data-lanes = <1 2>;
+ link-frequencies = /bits/ 64 <720000000>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
index 6f2017c75125..b9c61395b24f 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov08d10.yaml
@@ -69,6 +69,7 @@ examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -84,7 +85,7 @@ examples:
avdd-supply = <&ov08d10_vdda_2v8>;
dvdd-supply = <&ov08d10_vddd_1v2>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
reset-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
index d96199031b66..fcd617848ce3 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov4689.yaml
@@ -96,6 +96,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -114,7 +115,7 @@ examples:
powerdown-gpios = <&pio 107 GPIO_ACTIVE_LOW>;
reset-gpios = <&pio 109 GPIO_ACTIVE_LOW>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5675.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5675.yaml
index ad07204057f9..6df62fd0c0c0 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,ov5675.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5675.yaml
@@ -85,6 +85,7 @@ examples:
- |
#include <dt-bindings/clock/px30-cru.h>
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/rockchip.h>
i2c {
@@ -108,7 +109,7 @@ examples:
dovdd-supply = <&vcc_2v8>;
rotation = <90>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
port {
ucam_out: endpoint {
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml
index 3368b3bd8ef2..b2eb63d7d40a 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov5693.yaml
@@ -82,6 +82,8 @@ properties:
unevaluatedProperties: false
properties:
+ clock-noncontinuous: true
+
link-frequencies: true
data-lanes:
@@ -103,6 +105,7 @@ examples:
- |
#include <dt-bindings/clock/px30-cru.h>
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/rockchip.h>
i2c {
@@ -126,7 +129,7 @@ examples:
dovdd-supply = <&vcc_2v8>;
rotation = <90>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
port {
ucam_out: endpoint {
diff --git a/Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml b/Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
index 2b6143aff391..24787c9aa155 100644
--- a/Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
@@ -72,6 +72,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -87,7 +88,7 @@ examples:
powerdown-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
rotation = <180>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
port {
endpoint {
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
index 20f48d5e9b2d..56fb5f18f07b 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx111.yaml
@@ -69,6 +69,7 @@ examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -84,7 +85,7 @@ examples:
dvdd-supply = <&camera_vddd_1v2>;
avdd-supply = <&camera_vdda_2v7>;
- orientation = <1>;
+ orientation = <MEDIA_ORIENTATION_BACK>;
rotation = <90>;
nvmem = <&eeprom>;
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml
index d9cdfda699bf..6699c238ee0e 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx355.yaml
@@ -81,6 +81,7 @@ examples:
- |
#include <dt-bindings/clock/qcom,camcc-sdm845.h>
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -105,7 +106,7 @@ examples:
pinctrl-0 = <&cam_front_default>;
rotation = <270>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
port {
cam_front_endpoint: endpoint {
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
index 7c11e871dca6..69a37ff68db3 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx415.yaml
@@ -86,6 +86,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -98,7 +99,7 @@ examples:
clocks = <&clock_cam>;
dvdd-supply = <&vcc1v1_cam>;
lens-focus = <&vcm>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
ovdd-supply = <&vcc1v8_cam>;
reset-gpios = <&gpio_expander 14 GPIO_ACTIVE_LOW>;
rotation = <180>;
diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
index 58b1f9e85a9d..dfd87beba065 100644
--- a/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/st,vd55g1.yaml
@@ -106,6 +106,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -124,7 +125,7 @@ examples:
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
st,leds = <2>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml b/Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml
index c6673b8539db..48db22ca4a7e 100644
--- a/Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/st,vd56g3.yaml
@@ -107,6 +107,7 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -125,7 +126,7 @@ examples:
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
st,leds = <6>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/Documentation/devicetree/bindings/media/i2c/thine,thp7312.yaml b/Documentation/devicetree/bindings/media/i2c/thine,thp7312.yaml
index bc339a7374b2..4a66cb711372 100644
--- a/Documentation/devicetree/bindings/media/i2c/thine,thp7312.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/thine,thp7312.yaml
@@ -173,6 +173,7 @@ examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+ #include <dt-bindings/media/video-interface-devices.h>
i2c {
#address-cells = <1>;
@@ -196,7 +197,7 @@ examples:
vddgpio-0-supply = <&vsys_v4p2>;
vddgpio-1-supply = <&vsys_v4p2>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
rotation = <0>;
sensors {
diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt b/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt
deleted file mode 100644
index 59102edcf01e..000000000000
--- a/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-* Toshiba TC358743 HDMI-RX to MIPI CSI2-TX Bridge
-
-The Toshiba TC358743 HDMI-RX to MIPI CSI2-TX (H2C) is a bridge that converts
-a HDMI stream to MIPI CSI-2 TX. It is programmable through I2C.
-
-Required Properties:
-
-- compatible: value should be "toshiba,tc358743"
-- clocks, clock-names: should contain a phandle link to the reference clock
- source, the clock input is named "refclk".
-
-Optional Properties:
-
-- reset-gpios: gpio phandle GPIO connected to the reset pin
-- interrupts: GPIO connected to the interrupt pin
-- data-lanes: should be <1 2 3 4> for four-lane operation,
- or <1 2> for two-lane operation
-- clock-lanes: should be <0>
-- clock-noncontinuous: Presence of this boolean property decides whether the
- MIPI CSI-2 clock is continuous or non-continuous.
-- link-frequencies: List of allowed link frequencies in Hz. Each frequency is
- expressed as a 64-bit big-endian integer. The frequency
- is half of the bps per lane due to DDR transmission.
-
-For further information on the MIPI CSI-2 endpoint node properties, see
-Documentation/devicetree/bindings/media/video-interfaces.txt.
-
-Example:
-
- tc358743@f {
- compatible = "toshiba,tc358743";
- reg = <0x0f>;
- clocks = <&hdmi_osc>;
- clock-names = "refclk";
- reset-gpios = <&gpio6 9 GPIO_ACTIVE_LOW>;
- interrupt-parent = <&gpio2>;
- interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
-
- port {
- tc358743_out: endpoint {
- remote-endpoint = <&mipi_csi2_in>;
- data-lanes = <1 2 3 4>;
- clock-lanes = <0>;
- clock-noncontinuous;
- link-frequencies = /bits/ 64 <297000000>;
- };
- };
- };
diff --git a/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.yaml b/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.yaml
new file mode 100644
index 000000000000..29dd1d58f766
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.yaml
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/i2c/toshiba,tc358743.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Toshiba TC358743 HDMI-RX to MIPI CSI2-TX Bridge
+
+maintainers:
+ - Hans Verkuil <hverkuil@kernel.org>
+
+description:
+ The Toshiba TC358743 HDMI-RX to MIPI CSI2-TX (H2C) is a bridge that converts
+ a HDMI stream to MIPI CSI-2 TX. It is programmable through I2C.
+
+properties:
+ compatible:
+ const: toshiba,tc358743
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: refclk
+
+ reset-gpios:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ port:
+ $ref: /schemas/graph.yaml#/$defs/port-base
+ unevaluatedProperties: false
+
+ properties:
+ endpoint:
+ $ref: /schemas/media/video-interfaces.yaml#
+ unevaluatedProperties: false
+
+ properties:
+ data-lanes:
+ oneOf:
+ - items:
+ - const: 1
+ - const: 2
+ - items:
+ - const: 1
+ - const: 2
+ - const: 3
+ - const: 4
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - port
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ bridge@f {
+ compatible = "toshiba,tc358743";
+ reg = <0x0f>;
+ clocks = <&hdmi_osc>;
+ clock-names = "refclk";
+ reset-gpios = <&gpio6 9 GPIO_ACTIVE_LOW>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <5 IRQ_TYPE_LEVEL_HIGH>;
+
+ port {
+ endpoint {
+ remote-endpoint = <&mipi_csi2_in>;
+ data-lanes = <1 2 3 4>;
+ clock-lanes = <0>;
+ clock-noncontinuous;
+ link-frequencies = /bits/ 64 <297000000>;
+ };
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
index 4fcfc4fd3565..9eee67ed2685 100644
--- a/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
+++ b/Documentation/devicetree/bindings/media/nxp,imx8mq-mipi-csi2.yaml
@@ -220,7 +220,7 @@ examples:
port@0 {
reg = <0>;
- imx8mm_mipi_csi_in: endpoint {
+ mipi_csi_in: endpoint {
remote-endpoint = <&imx477_out>;
data-lanes = <1 2 3 4>;
};
@@ -229,7 +229,7 @@ examples:
port@1 {
reg = <1>;
- imx8mm_mipi_csi_out: endpoint {
+ mipi_csi_out: endpoint {
remote-endpoint = <&csi_in>;
};
};
diff --git a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
index b7f6c87d0e06..b80660da1050 100644
--- a/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
+++ b/Documentation/devicetree/bindings/media/snps,dw-hdmi-rx.yaml
@@ -78,6 +78,13 @@ properties:
The phandle of the syscon node for the Video Output GRF register
to enable EDID transfer through SDAIN and SCLIN.
+ "#sound-dai-cells":
+ const: 1
+ description:
+ The HDMI RX controller has two digital audio interfaces, one for
+ I2S and one for S/PDIF. The DAI cell selects the interface, 0 for
+ I2S and 1 for S/PDIF.
+
required:
- compatible
- reg
@@ -90,7 +97,10 @@ required:
- pinctrl-0
- hpd-gpios
-additionalProperties: false
+allOf:
+ - $ref: /schemas/sound/dai-common.yaml#
+
+unevaluatedProperties: false
examples:
- |
@@ -129,4 +139,5 @@ examples:
pinctrl-0 = <&hdmim1_rx_cec &hdmim1_rx_hpdin &hdmim1_rx_scl &hdmim1_rx_sda &hdmirx_5v_detection>;
pinctrl-names = "default";
hpd-gpios = <&gpio1 22 GPIO_ACTIVE_LOW>;
+ #sound-dai-cells = <1>;
};
diff --git a/Documentation/devicetree/bindings/media/video-interface-devices.yaml b/Documentation/devicetree/bindings/media/video-interface-devices.yaml
index a81d2a155fe6..c9c3f4f16719 100644
--- a/Documentation/devicetree/bindings/media/video-interface-devices.yaml
+++ b/Documentation/devicetree/bindings/media/video-interface-devices.yaml
@@ -392,17 +392,22 @@ properties:
The orientation of a device (typically an image sensor or a flash LED)
describing its mounting position relative to the usage orientation of the
system where the device is installed on.
+ See include/dt-bindings/media/video-interface-devices.h.
+
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- # Front. The device is mounted on the front facing side of the system. For
- # mobile devices such as smartphones, tablets and laptops the front side
- # is the user facing side.
+ # MEDIA_ORIENTATION_FRONT
+ # The device is mounted on the front facing side of the system. For
+ # mobile devices such as smartphones, tablets and laptops the front
+ # side is the user facing side.
- 0
- # Back. The device is mounted on the back side of the system, which is
+ # MEDIA_ORIENTATION_BACK
+ # The device is mounted on the back side of the system, which is
# defined as the opposite side of the front facing one.
- 1
- # External. The device is not attached directly to the system but is
- # attached in a way that allows it to move freely.
+ # MEDIA_ORIENTATION_EXTERNAL
+ # The device is not attached directly to the system but is attached in
+ # a way that allows it to move freely.
- 2
additionalProperties: true
diff --git a/Documentation/driver-api/media/tx-rx.rst b/Documentation/driver-api/media/tx-rx.rst
index 7df2407817b3..9b231fa0216a 100644
--- a/Documentation/driver-api/media/tx-rx.rst
+++ b/Documentation/driver-api/media/tx-rx.rst
@@ -104,7 +104,11 @@ where
* - k
- 16 for D-PHY and 7 for C-PHY.
-Information on whether D-PHY or C-PHY is used, and the value of ``nr_of_lanes``, can be obtained from the OF endpoint configuration.
+Information on whether D-PHY or C-PHY is used as well as the value of
+``nr_of_lanes`` can be obtained from the V4L2 endpoint configuration; see
+:c:func:`v4l2_fwnode_endpoint_alloc_parse()`,
+:c:func:`v4l2_fwnode_endpoint_parse()` and
+:c:func:`v4l2_get_active_data_lanes()`.
.. note::
diff --git a/Documentation/userspace-api/media/drivers/dcmipp.rst b/Documentation/userspace-api/media/drivers/dcmipp.rst
new file mode 100644
index 000000000000..ed4272da4b68
--- /dev/null
+++ b/Documentation/userspace-api/media/drivers/dcmipp.rst
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+ST DCMIPP driver
+================
+
+The ST DCMIPP driver implements a driver specific control as part of its
+pixelproc subdev.
+
+``V4L2_CID_DCMIPP_PIXELPROC_GAMMA_CORRECTION_ENABLE (boolean)``
+ Enable / disable the gamma correction block.
+
+ The DCMIPP PixelProc stage implements a gamma compression on each R, G, B
+ component, using a static gamma exponent 2.2.
+ The gamma is implemented as a 7-segment linear curve.
diff --git a/Documentation/userspace-api/media/drivers/index.rst b/Documentation/userspace-api/media/drivers/index.rst
index 02967c9b18d6..8d2c489b8453 100644
--- a/Documentation/userspace-api/media/drivers/index.rst
+++ b/Documentation/userspace-api/media/drivers/index.rst
@@ -30,6 +30,7 @@ For more details see the file COPYING in the source distribution of Linux.
camera-sensor
ccs
cx2341x-uapi
+ dcmipp
dw100
imx-uapi
mali-c55
diff --git a/MAINTAINERS b/MAINTAINERS
index fafe90d6ca4d..c33ef2191a13 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11772,6 +11772,13 @@ W: http://www.highpoint-tech.com
F: Documentation/scsi/hptiop.rst
F: drivers/scsi/hptiop.c
+HIMAX HM1246 SENSOR DRIVER
+M: Matthias Fend <matthias.fend@emfend.at>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml
+F: drivers/media/i2c/hm1246.c
+
HIMAX HX83112B TOUCHSCREEN SUPPORT
M: Job Noorman <job@noorman.info>
L: linux-input@vger.kernel.org
@@ -13081,6 +13088,8 @@ F: drivers/scsi/isci/
INTEL COMPUTER VISION SENSING (CVS) DRIVER
M: Miguel Vadillo <miguel.vadillo@intel.com>
+M: Sakari Ailus <sakari.ailus@linux.intel.com>
+R: Antti Laakso <antti.laakso@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
F: drivers/media/i2c/cvs/
@@ -13268,6 +13277,15 @@ S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
F: drivers/iommu/intel/
+INTEL IPU BRIDGE
+M: Sakari Ailus <sakari.ailus@linux.intel.com>
+M: Dan Scally <dan.scally@ideasonboard.com>
+R: Hans de Goede <hansg@kernel.org>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: drivers/media/pci/intel/ipu-bridge.c
+F: include/media/ipu-bridge.h
+
INTEL IPU3 CSI-2 CIO2 DRIVER
M: Yong Zhi <yong.zhi@intel.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
@@ -13289,6 +13307,8 @@ F: drivers/staging/media/ipu3/
INTEL IPU6 INPUT SYSTEM DRIVER
M: Sakari Ailus <sakari.ailus@linux.intel.com>
+R: Antti Laakso <antti.laakso@linux.intel.com>
+R: "Sapre, Sarang" <sarang.sapre@intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
@@ -13525,6 +13545,7 @@ K: \bSGX_
INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER
M: Daniel Scally <dan.scally@ideasonboard.com>
M: Sakari Ailus <sakari.ailus@linux.intel.com>
+L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media.git
F: drivers/platform/x86/intel/int3472/
@@ -13991,6 +14012,14 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
F: drivers/gpu/drm/bridge/ite-it66121.c
+ITE IT6625 HDMI to MIPI MEDIA DRIVER
+M: Hermes Wu <Hermes.Wu@ite.com.tw>
+S: Maintained
+T: git git://linuxtv.org/media.git
+F: Documentation/devicetree/bindings/media/i2c/ite,it6625.yaml
+F: drivers/media/i2c/it6625.c
+F: include/uapi/linux/it6625.h
+
IVTV VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
L: linux-media@vger.kernel.org
@@ -16468,6 +16497,7 @@ M: Frank Li <Frank.Li@nxp.com>
M: Martin Kepplinger-Novakovic <martink@posteo.de>
R: Rui Miguel Silva <rmfrfs@gmail.com>
R: Purism Kernel Team <kernel@puri.sm>
+R: Bryan O'Donoghue <bod@kernel.org>
L: imx@lists.linux.dev
L: linux-media@vger.kernel.org
S: Maintained
@@ -19822,6 +19852,14 @@ S: Maintained
F: Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
F: drivers/media/platform/nxp/imx-jpeg
+NXP i.MX 95 CSI PIXEL FORMATTER V4L2 DRIVER
+M: Guoniu Zhou <guoniu.zhou@nxp.com>
+L: imx@lists.linux.dev
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
+F: drivers/media/platform/nxp/imx95-csi-formatter.c
+
NXP i.MX CLOCK DRIVERS
M: Abel Vesa <abelvesa@kernel.org>
R: Peng Fan <peng.fan@nxp.com>
@@ -20212,6 +20250,14 @@ T: git git://linuxtv.org/media_tree.git
F: Documentation/devicetree/bindings/media/i2c/ovti,og0ve1b.yaml
F: drivers/media/i2c/og0ve1b.c
+OMNIVISION OS02G10 SENSOR DRIVER
+M: Tarang Raval <tarang.raval@siliconsignals.io>
+M: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/i2c/ovti,os02g10.yaml
+F: drivers/media/i2c/os02g10.c
+
OMNIVISION OS05B10 SENSOR DRIVER
M: Himanshu Bhavani <himanshu.bhavani@siliconsignals.io>
M: Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>
@@ -26669,6 +26715,7 @@ F: include/linux/soc/amd/isp4_misc.h
SYNOPSYS DESIGNWARE MIPI CSI-2 RECEIVER DRIVER
M: Michael Riesch <michael.riesch@collabora.com>
+R: Bryan O'Donoghue <bod@kernel.org>
L: linux-media@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/media/rockchip,rk3568-mipi-csi2.yaml
@@ -27048,9 +27095,11 @@ M: Luca Ceresoli <luca.ceresoli@bootlin.com>
L: linux-media@vger.kernel.org
L: linux-tegra@vger.kernel.org
S: Maintained
+F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-csi.yaml
F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vi.yaml
F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-vip.yaml
+F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra210-csi.yaml
F: drivers/staging/media/tegra-video/
TEGRA XUSB PADCTL DRIVER
@@ -27780,7 +27829,7 @@ TOSHIBA TC358743 DRIVER
M: Hans Verkuil <hverkuil@kernel.org>
L: linux-media@vger.kernel.org
S: Maintained
-F: Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.txt
+F: Documentation/devicetree/bindings/media/i2c/toshiba,tc358743.yaml
F: drivers/media/i2c/tc358743*
F: include/media/i2c/tc358743.h
diff --git a/arch/arm/boot/dts/nvidia/tegra30-asus-nexus7-grouper-common.dtsi b/arch/arm/boot/dts/nvidia/tegra30-asus-nexus7-grouper-common.dtsi
index 892d718294dd..a7fdd194300c 100644
--- a/arch/arm/boot/dts/nvidia/tegra30-asus-nexus7-grouper-common.dtsi
+++ b/arch/arm/boot/dts/nvidia/tegra30-asus-nexus7-grouper-common.dtsi
@@ -3,6 +3,7 @@
#include <dt-bindings/input/gpio-keys.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/power/summit,smb347-charger.h>
#include <dt-bindings/thermal/thermal.h>
@@ -991,7 +992,7 @@
vdd-supply = <&vddio_cam>;
vaa-supply = <&avdd_cam1>;
- orientation = <0>; /* Front camera */
+ orientation = <MEDIA_ORIENTATION_FRONT>;
assigned-clocks = <&tegra_car TEGRA30_CLK_VI_SENSOR>,
<&tegra_car TEGRA30_CLK_CSUS>;
diff --git a/arch/arm/boot/dts/nvidia/tegra30-asus-transformer-common.dtsi b/arch/arm/boot/dts/nvidia/tegra30-asus-transformer-common.dtsi
index bf1c3a31d406..76286e15684c 100644
--- a/arch/arm/boot/dts/nvidia/tegra30-asus-transformer-common.dtsi
+++ b/arch/arm/boot/dts/nvidia/tegra30-asus-transformer-common.dtsi
@@ -3,6 +3,7 @@
#include <dt-bindings/input/gpio-keys.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/thermal/thermal.h>
#include "tegra30.dtsi"
@@ -1262,7 +1263,7 @@
vdd-supply = <&vdd_1v8_cam>;
vaa-supply = <&avdd_2v85_fcam>;
- orientation = <0>; /* Front camera */
+ orientation = <MEDIA_ORIENTATION_FRONT>;
assigned-clocks = <&tegra_car TEGRA30_CLK_VI_SENSOR>,
<&tegra_car TEGRA30_CLK_CSUS>;
diff --git a/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts b/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts
index 896639599c12..28680063bcc0 100644
--- a/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts
+++ b/arch/arm/boot/dts/nvidia/tegra30-lg-p895.dts
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0
/dts-v1/;
+#include <dt-bindings/media/video-interface-devices.h>
+
#include "tegra30-lg-x3.dtsi"
/ {
@@ -132,7 +134,7 @@
vdd-supply = <&vt_1v8_front>;
vaa-supply = <&vt_2v8_front>;
- orientation = <0>; /* Front camera */
+ orientation = <MEDIA_ORIENTATION_FRONT>;
assigned-clocks = <&tegra_car TEGRA30_CLK_VI_SENSOR>,
<&tegra_car TEGRA30_CLK_CSUS>;
diff --git a/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi b/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi
index 60e8a19aa70e..77cab695d3da 100644
--- a/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi
+++ b/arch/arm/boot/dts/nvidia/tegra30-lg-x3.dtsi
@@ -4,6 +4,7 @@
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/mfd/max77620.h>
#include <dt-bindings/thermal/thermal.h>
@@ -1216,7 +1217,7 @@
dvdd-supply = <&vdd_1v2_rear>;
avdd-supply = <&vdd_2v7_rear>;
- orientation = <1>; /* Rear camera */
+ orientation = <MEDIA_ORIENTATION_BACK>;
rotation = <90>;
nvmem = <&m24c08>;
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtso b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtso
index e5a2b3780215..7b44ae0f19b2 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtso
+++ b/arch/arm64/boot/dts/freescale/imx8mp-tqma8mpql-mba8mp-ras314-imx219.dtso
@@ -9,6 +9,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include "imx8mp-pinfunc.h"
@@ -47,7 +48,7 @@
VANA-supply = <&reg_cam>;
VDIG-supply = <&reg_cam>;
VDDL-supply = <&reg_cam>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi
index ad7c0e7422ca..a212c031d4c6 100644
--- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dtsi
@@ -8,6 +8,7 @@
#include "dt-bindings/input/input.h"
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/leds/common.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include "dt-bindings/pwm/pwm.h"
#include "dt-bindings/usb/pd.h"
#include "imx8mq.dtsi"
@@ -1116,7 +1117,7 @@
vddd-supply = <&reg_vcam_1v2>;
vddio-supply = <&reg_csi_1v8>;
rotation = <90>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
port {
camera1_ep: endpoint {
diff --git a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
index e9bf2faf6628..2758f1f57ac0 100644
--- a/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
+++ b/arch/arm64/boot/dts/qcom/qcm6490-fairphone-fp5.dts
@@ -13,6 +13,7 @@
#include <dt-bindings/iio/qcom,spmi-adc7-pmk8350.h>
#include <dt-bindings/leds/common.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include <dt-bindings/sound/qcom,q6asm.h>
@@ -755,7 +756,7 @@
pinctrl-0 = <&cam_mclk3_default>;
pinctrl-names = "default";
- orientation = <0>; /* Front facing */
+ orientation = <MEDIA_ORIENTATION_FRONT>;
rotation = <270>;
port {
diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts
index 52e5e239382b..ee0928721cdf 100644
--- a/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts
+++ b/arch/arm64/boot/dts/qcom/sc8280xp-lenovo-thinkpad-x13s.dts
@@ -11,6 +11,7 @@
#include <dt-bindings/input/gpio-keys.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/regulator/qcom,rpmh-regulator.h>
#include "sc8280xp.dtsi"
@@ -683,7 +684,7 @@
clocks = <&camcc CAMCC_MCLK3_CLK>;
- orientation = <0>; /* Front facing */
+ orientation = <MEDIA_ORIENTATION_FRONT>;
avdd-supply = <&vreg_l6q>;
dvdd-supply = <&vreg_l2q>;
diff --git a/arch/arm64/boot/dts/qcom/sdm670-google-common.dtsi b/arch/arm64/boot/dts/qcom/sdm670-google-common.dtsi
index 55f313b5efec..4ce9b62abe53 100644
--- a/arch/arm64/boot/dts/qcom/sdm670-google-common.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm670-google-common.dtsi
@@ -9,6 +9,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
#include <dt-bindings/leds/common.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/qcom,pmic-gpio.h>
#include <dt-bindings/power/qcom-rpmpd.h>
#include "sdm670.dtsi"
@@ -462,7 +463,7 @@
pinctrl-names = "default";
rotation = <270>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
port {
cam_front_endpoint: endpoint {
diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx219.dtso b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx219.dtso
index 3acaf714cf24..b816382bba0a 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx219.dtso
+++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx219.dtso
@@ -12,6 +12,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
&{/} {
clk_cam_j1: clk-cam-j1 {
@@ -44,7 +45,7 @@
VDIG-supply = <&reg_cam_j1>;
VDDL-supply = <&reg_cam_j1>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx462.dtso b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx462.dtso
index a19bc0840392..4019b80a88b7 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx462.dtso
+++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j1-imx462.dtso
@@ -12,6 +12,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
&{/} {
clk_cam_j1: clk-cam-j1 {
@@ -46,7 +47,7 @@
vdda-supply = <&reg_cam_j1>;
vddd-supply = <&reg_cam_j1>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx219.dtso b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx219.dtso
index 512810b861aa..fea1ef4a1178 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx219.dtso
+++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx219.dtso
@@ -12,6 +12,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
&{/} {
clk_cam_j2: clk-cam-j2 {
@@ -44,7 +45,7 @@
VDIG-supply = <&reg_cam_j2>;
VDDL-supply = <&reg_cam_j2>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx462.dtso b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx462.dtso
index a31524b59834..177201a8a6d2 100644
--- a/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx462.dtso
+++ b/arch/arm64/boot/dts/renesas/r8a779g3-sparrow-hawk-camera-j2-imx462.dtso
@@ -12,6 +12,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
&{/} {
clk_cam_j2: clk-cam-j2 {
@@ -46,7 +47,7 @@
vdda-supply = <&reg_cam_j2>;
vddd-supply = <&reg_cam_j2>;
- orientation = <2>;
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
rotation = <0>;
port {
diff --git a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi
index 02200de695d3..3ae65dbcfffe 100644
--- a/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi
+++ b/arch/arm64/boot/dts/rockchip/px30-pp1516.dtsi
@@ -6,6 +6,7 @@
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/rockchip.h>
#include "px30.dtsi"
@@ -413,7 +414,7 @@
dvdd-supply = <&vcc_cam_dvdd>;
dovdd-supply = <&vcc_cam_dovdd>;
lens-focus = <&focus>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
pinctrl-names = "default";
pinctrl-0 = <&cif_clkout_m0 &cam_pwdn>;
reset-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_LOW>;
diff --git a/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso b/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso
index d0725595ade0..1813f06bbff5 100644
--- a/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso
+++ b/arch/arm64/boot/dts/rockchip/px30-ringneck-haikou-video-demo.dtso
@@ -16,6 +16,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/leds/common.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/rockchip.h>
&{/} {
@@ -185,7 +186,7 @@
dvdd-supply = <&cam_dvdd_1v2>;
dovdd-supply = <&cam_dovdd_1v8>;
lens-focus = <&focus>;
- orientation = <0>;
+ orientation = <MEDIA_ORIENTATION_FRONT>;
pinctrl-names = "default";
pinctrl-0 = <&cif_clkout_m0>;
reset-gpios = <&pca9670 6 GPIO_ACTIVE_LOW>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
index d46cdfe3f784..1a36d54ddfa2 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-pinephone-pro.dts
@@ -13,6 +13,7 @@
#include <dt-bindings/input/gpio-keys.h>
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/leds/common.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include "rk3399-s.dtsi"
/ {
@@ -455,7 +456,7 @@
reg = <0x1a>;
clocks = <&cru SCLK_CIF_OUT>; /* MIPI_MCLK0, derived from CIF_CLKO */
lens-focus = <&wcam_lens>;
- orientation = <1>; /* V4L2_CAMERA_ORIENTATION_BACK */
+ orientation = <MEDIA_ORIENTATION_BACK>;
pinctrl-names = "default";
pinctrl-0 = <&camera_rst_l>;
reset-gpios = <&gpio1 RK_PA0 GPIO_ACTIVE_LOW>;
@@ -487,7 +488,7 @@
clocks = <&cru SCLK_CIF_OUT>; /* MIPI_MCLK1, derived from CIF_CLK0 */
clock-names = "xvclk";
dovdd-supply = <&vcc1v8_dvp>;
- orientation = <0>; /* V4L2_CAMERA_ORIENTATION_FRONT */
+ orientation = <MEDIA_ORIENTATION_FRONT>;
pinctrl-names = "default";
pinctrl-0 = <&camera2_rst_l &dvp_pdn0_h>;
powerdown-gpios = <&gpio2 RK_PB4 GPIO_ACTIVE_LOW>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam0.dtso b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam0.dtso
index 5c7fb960b099..5420ec3fe2ed 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam0.dtso
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam0.dtso
@@ -10,6 +10,7 @@
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/rockchip.h>
&{/} {
@@ -51,7 +52,7 @@
avdd-supply = <&savdd_cam0>;
clocks = <&cru CLK_MIPI_CAMARAOUT_M3>;
dvdd-supply = <&sdvdd_cam0>;
- orientation = <2>; /* External */
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
ovdd-supply = <&siovdd_cam0>;
pinctrl-names = "default";
pinctrl-0 = <&cam0_rstn &mipim0_camera3_clk>;
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam1.dtso b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam1.dtso
index 1ee9036b9095..8ef764aab73a 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam1.dtso
+++ b/arch/arm64/boot/dts/rockchip/rk3588-rock-5b-plus-radxa-cam4k-cam1.dtso
@@ -10,6 +10,7 @@
#include <dt-bindings/clock/rockchip,rk3588-cru.h>
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/media/video-interfaces.h>
+#include <dt-bindings/media/video-interface-devices.h>
#include <dt-bindings/pinctrl/rockchip.h>
&{/} {
@@ -51,7 +52,7 @@
avdd-supply = <&savdd_cam1>;
clocks = <&cru CLK_MIPI_CAMARAOUT_M4>;
dvdd-supply = <&sdvdd_cam1>;
- orientation = <2>; /* External */
+ orientation = <MEDIA_ORIENTATION_EXTERNAL>;
ovdd-supply = <&siovdd_cam1>;
pinctrl-names = "default";
pinctrl-0 = <&cam1_rstn &mipim0_camera4_clk>;
diff --git a/drivers/media/cec/platform/meson/ao-cec-g12a.c b/drivers/media/cec/platform/meson/ao-cec-g12a.c
index b175be3f2bf4..55bd0d416460 100644
--- a/drivers/media/cec/platform/meson/ao-cec-g12a.c
+++ b/drivers/media/cec/platform/meson/ao-cec-g12a.c
@@ -334,7 +334,7 @@ static int meson_ao_cec_g12a_setup_clk(struct meson_ao_cec_g12a_device *ao_cec)
{
struct meson_ao_cec_g12a_dualdiv_clk *dualdiv_clk;
struct device *dev = &ao_cec->pdev->dev;
- struct clk_init_data init;
+ struct clk_init_data init = {};
const char *parent_name;
struct clk *clk;
char *name;
diff --git a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c
index 3c6ce6f3d93e..1f8ed7ff31e5 100644
--- a/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c
+++ b/drivers/media/cec/usb/extron-da-hd-4k-plus/extron-da-hd-4k-plus.c
@@ -847,7 +847,9 @@ static irqreturn_t extron_interrupt(struct serio *serio, unsigned char data,
return IRQ_HANDLED;
memcpy(extron->data, extron->buf, extron->idx);
extron->len = extron->idx;
- extron->data[extron->len] = 0;
+ /* Keep fixed-offset response tests from using stale tail bytes. */
+ memset(extron->data + extron->len, 0,
+ min_t(size_t, 10, sizeof(extron->data) - extron->len));
if (debug)
dev_info(extron->dev, "received %s\n", extron->data);
extron->idx = 0;
diff --git a/drivers/media/common/cypress_firmware.c b/drivers/media/common/cypress_firmware.c
index 66274fdf5243..d0f66ed01c4b 100644
--- a/drivers/media/common/cypress_firmware.c
+++ b/drivers/media/common/cypress_firmware.c
@@ -59,6 +59,8 @@ static int cypress_get_hexline(const struct firmware *fw,
if (hx->type == 0x04) {
/* b[4] and b[5] are the Extended linear address record data
* field */
+ if (hx->len != 2)
+ return -EINVAL;
hx->addr |= (b[4] << 24) | (b[5] << 16);
}
diff --git a/drivers/media/common/saa7146/saa7146_core.c b/drivers/media/common/saa7146/saa7146_core.c
index c297d019f2b3..b705c4bcaffa 100644
--- a/drivers/media/common/saa7146/saa7146_core.c
+++ b/drivers/media/common/saa7146/saa7146_core.c
@@ -252,6 +252,15 @@ int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt
ptr = pt->cpu;
for_each_sg_dma_page(list, &dma_iter, sglen, 0) {
+ /*
+ * The page table is exactly PAGE_SIZE large, i.e. it holds
+ * PAGE_SIZE / sizeof(__le32) entries. Buffers needing more
+ * pages would overflow it.
+ */
+ if (nr_pages >= PAGE_SIZE / sizeof(__le32)) {
+ pr_err("page table too small\n");
+ return -EIO;
+ }
*ptr++ = cpu_to_le32(sg_page_iter_dma_address(&dma_iter));
nr_pages++;
}
@@ -340,6 +349,8 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
goto out;
}
+ spin_lock_init(&dev->int_slock);
+
/* create a nice device name */
sprintf(dev->name, "saa7146 (%d)", saa7146_num);
@@ -425,7 +436,6 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
dev->ext = ext;
mutex_init(&dev->v4l2_lock);
- spin_lock_init(&dev->int_slock);
spin_lock_init(&dev->slock);
mutex_init(&dev->i2c_lock);
diff --git a/drivers/media/common/saa7146/saa7146_video.c b/drivers/media/common/saa7146/saa7146_video.c
index 733e18001d0d..c895c90b1378 100644
--- a/drivers/media/common/saa7146/saa7146_video.c
+++ b/drivers/media/common/saa7146/saa7146_video.c
@@ -410,6 +410,17 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma
f->fmt.pix.bytesperline = calc_bpl;
f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+
+ /*
+ * The DMA page tables hold one entry per page and are exactly
+ * PAGE_SIZE large. Reject formats whose buffer would need more
+ * entries than the tables can hold.
+ */
+ if (f->fmt.pix.sizeimage > PAGE_SIZE / sizeof(__le32) * PAGE_SIZE) {
+ DEB_D("sizeimage %d too large\n", f->fmt.pix.sizeimage);
+ return -EINVAL;
+ }
+
DEB_D("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",
f->fmt.pix.width, f->fmt.pix.height,
f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
diff --git a/drivers/media/dvb-frontends/drxd_map_firm.h b/drivers/media/dvb-frontends/drxd_map_firm.h
index bdcc63576df1..a6585e53d879 100644
--- a/drivers/media/dvb-frontends/drxd_map_firm.h
+++ b/drivers/media/dvb-frontends/drxd_map_firm.h
@@ -10,8 +10,8 @@
/*
* Note: originally, this file contained 12000+ lines of data
- * Probably a few lines for every firwmare assembler instruction. However,
- * only a few defines were actually used. So, removed all uneeded lines.
+ * Probably a few lines for every firmware assembler instruction. However,
+ * only a few defines were actually used. So, removed all unneeded lines.
* If ever needed, the other lines can be easily obtained via git history.
*/
diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c
index d15c55de2723..0ca6b6d81273 100644
--- a/drivers/media/dvb-frontends/stv0900_core.c
+++ b/drivers/media/dvb-frontends/stv0900_core.c
@@ -1773,6 +1773,8 @@ static int stv0900_recv_slave_reply(struct dvb_frontend *fe,
if (stv0900_get_bits(intp, RX_END)) {
reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR);
+ if (reply->msg_len > sizeof(reply->msg))
+ reply->msg_len = sizeof(reply->msg);
for (i = 0; i < reply->msg_len; i++)
reply->msg[i] = stv0900_read_reg(intp, DISRXDATA);
diff --git a/drivers/media/dvb-frontends/stv090x.c b/drivers/media/dvb-frontends/stv090x.c
index 932bbed5497a..63be9ee104ab 100644
--- a/drivers/media/dvb-frontends/stv090x.c
+++ b/drivers/media/dvb-frontends/stv090x.c
@@ -3902,6 +3902,8 @@ static int stv090x_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_s
if (rx_end) {
reply->msg_len = STV090x_GETFIELD_Px(reg, FIFO_BYTENBR_FIELD);
+ if (reply->msg_len > sizeof(reply->msg))
+ reply->msg_len = sizeof(reply->msg);
for (i = 0; i < reply->msg_len; i++)
reply->msg[i] = STV090x_READ_DEMOD(state, DISRXDATA);
}
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
index 5c52007f9cbe..9b28e91b8a67 100644
--- a/drivers/media/i2c/Kconfig
+++ b/drivers/media/i2c/Kconfig
@@ -137,6 +137,17 @@ config VIDEO_HI847
To compile this driver as a module, choose M here: the
module will be called hi847.
+config VIDEO_HM1246
+ tristate "Himax HM1246 sensor support"
+ depends on OF
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for the Himax
+ HM1246 camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called hm1246.
+
config VIDEO_IMX111
tristate "Sony IMX111 sensor support"
select V4L2_CCI_I2C
@@ -396,6 +407,16 @@ config VIDEO_OG0VE1B
To compile this driver as a module, choose M here: the
module will be called og0ve1b.
+config VIDEO_OS02G10
+ tristate "OmniVision OS02G10 sensor support"
+ select V4L2_CCI_I2C
+ help
+ This is a Video4Linux2 sensor driver for Omnivision
+ OS02G10 camera sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called os02g10.
+
config VIDEO_OS05B10
tristate "OmniVision OS05B10 sensor support"
select V4L2_CCI_I2C
@@ -989,14 +1010,12 @@ endmenu
# V4L2 I2C drivers that aren't related with Camera support
#
-comment "audio, video and radio I2C drivers auto-selected by 'Autoselect ancillary drivers'"
- depends on MEDIA_HIDE_ANCILLARY_SUBDRV
+comment "audio, video and radio I2C drivers"
#
# Encoder / Decoder module configuration
#
menu "Audio decoders, processors and mixers"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
config VIDEO_CS3308
tristate "Cirrus Logic CS3308 audio ADC"
@@ -1159,7 +1178,6 @@ config VIDEO_WM8775
endmenu
menu "RDS decoders"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
config VIDEO_SAA6588
tristate "SAA6588 Radio Chip RDS decoder support"
@@ -1176,7 +1194,6 @@ config VIDEO_SAA6588
endmenu
menu "Video decoders"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
config VIDEO_ADV7180
tristate "Analog Devices ADV7180 decoder"
@@ -1301,6 +1318,25 @@ config VIDEO_ISL7998X
Support for Intersil ISL7998x analog to MIPI-CSI2 or
BT.656 decoder.
+config VIDEO_IT6625
+ tristate "IT6625 HDMI to MIPI CSI bridge"
+ depends on VIDEO_DEV && I2C
+ depends on OF
+ select CEC_CORE
+ select HDMI
+ select MEDIA_CONTROLLER
+ select REGMAP_I2C
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ V4L2 subdevice driver for the ITE IT6625/IT6626 HDMI to MIPI
+ CSI-2 bridge chips. IT6625 accepts an HDMI 2.0 input and
+ IT6626 an HDMI 2.1 input, converting it to a MIPI CSI-2
+ output. The driver also exposes an HDMI CEC adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called it6625.
+
config VIDEO_LT6911UXE
tristate "Lontium LT6911UXE decoder"
depends on ACPI && VIDEO_DEV && I2C
@@ -1517,7 +1553,6 @@ source "drivers/media/i2c/cx25840/Kconfig"
endmenu
menu "Video encoders"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
config VIDEO_ADV7170
tristate "Analog Devices ADV7170 video encoder"
@@ -1616,7 +1651,6 @@ config VIDEO_THS8200
endmenu
menu "Video improvement chips"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
config VIDEO_UPD64031A
tristate "NEC Electronics uPD64031A Ghost Reduction"
@@ -1645,7 +1679,6 @@ config VIDEO_UPD64083
endmenu
menu "Audio/Video compression chips"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
config VIDEO_SAA6752HS
tristate "Philips SAA6752HS MPEG-2 Audio/Video Encoder"
@@ -1661,7 +1694,6 @@ config VIDEO_SAA6752HS
endmenu
menu "SDR tuner chips"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
config SDR_MAX2175
tristate "Maxim 2175 RF to Bits tuner"
@@ -1678,7 +1710,6 @@ config SDR_MAX2175
endmenu
menu "Miscellaneous helper chips"
- visible if !MEDIA_HIDE_ANCILLARY_SUBDRV
source "drivers/media/i2c/cvs/Kconfig"
diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
index d04bd5724552..fd1cb25718c0 100644
--- a/drivers/media/i2c/Makefile
+++ b/drivers/media/i2c/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
obj-$(CONFIG_VIDEO_HI556) += hi556.o
obj-$(CONFIG_VIDEO_HI846) += hi846.o
obj-$(CONFIG_VIDEO_HI847) += hi847.o
+obj-$(CONFIG_VIDEO_HM1246) += hm1246.o
obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
obj-$(CONFIG_VIDEO_IMX111) += imx111.o
obj-$(CONFIG_VIDEO_IMX208) += imx208.o
@@ -65,6 +66,7 @@ obj-$(CONFIG_VIDEO_IMX678) += imx678.o
obj-$(CONFIG_VIDEO_IMX471) += imx471.o
obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
obj-$(CONFIG_VIDEO_ISL7998X) += isl7998x.o
+obj-$(CONFIG_VIDEO_IT6625) += it6625.o
obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
obj-$(CONFIG_VIDEO_LM3560) += lm3560.o
obj-$(CONFIG_VIDEO_LM3646) += lm3646.o
@@ -86,6 +88,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o
obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o
obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o
obj-$(CONFIG_VIDEO_OG0VE1B) += og0ve1b.o
+obj-$(CONFIG_VIDEO_OS02G10) += os02g10.o
obj-$(CONFIG_VIDEO_OS05B10) += os05b10.o
obj-$(CONFIG_VIDEO_OV01A10) += ov01a10.o
obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o
diff --git a/drivers/media/i2c/adv7170.c b/drivers/media/i2c/adv7170.c
index 812998729207..9a7edd6583c1 100644
--- a/drivers/media/i2c/adv7170.c
+++ b/drivers/media/i2c/adv7170.c
@@ -284,6 +284,7 @@ static int adv7170_get_fmt(struct v4l2_subdev *sd,
}
static int adv7170_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/adv7175.c b/drivers/media/i2c/adv7175.c
index f1caab8e2abd..b77737e23a47 100644
--- a/drivers/media/i2c/adv7175.c
+++ b/drivers/media/i2c/adv7175.c
@@ -209,7 +209,7 @@ static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
/* This is an attempt to convert
* SECAM->PAL (typically it does not work
* due to genlock: when decoder is in SECAM
- * and encoder in in PAL the subcarrier can
+ * and encoder in PAL the subcarrier can
* not be synchronized with horizontal
* quency) */
adv7175_write_block(sd, init_pal, sizeof(init_pal));
@@ -322,6 +322,7 @@ static int adv7175_get_fmt(struct v4l2_subdev *sd,
}
static int adv7175_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c
index a1c7f68225b4..02197f813758 100644
--- a/drivers/media/i2c/adv7180.c
+++ b/drivers/media/i2c/adv7180.c
@@ -770,6 +770,7 @@ static int adv7180_get_pad_format(struct v4l2_subdev *sd,
}
static int adv7180_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -807,7 +808,7 @@ static int adv7180_init_state(struct v4l2_subdev *sd,
: V4L2_SUBDEV_FORMAT_ACTIVE,
};
- return adv7180_set_pad_format(sd, sd_state, &fmt);
+ return adv7180_set_pad_format(sd, NULL, sd_state, &fmt);
}
static int adv7180_get_mbus_config(struct v4l2_subdev *sd,
@@ -1454,6 +1455,7 @@ static int adv7180_probe(struct i2c_client *client)
if (state == NULL)
return -ENOMEM;
+ mutex_init(&state->mutex);
state->client = client;
state->field = V4L2_FIELD_ALTERNATE;
state->chip_info = i2c_get_match_data(client);
@@ -1497,7 +1499,6 @@ static int adv7180_probe(struct i2c_client *client)
}
state->irq = client->irq;
- mutex_init(&state->mutex);
state->curr_norm = V4L2_STD_NTSC;
state->input = 0;
diff --git a/drivers/media/i2c/adv7183.c b/drivers/media/i2c/adv7183.c
index a04a1a205fe0..9e4cbebe6e5a 100644
--- a/drivers/media/i2c/adv7183.c
+++ b/drivers/media/i2c/adv7183.c
@@ -420,6 +420,7 @@ static int adv7183_enum_mbus_code(struct v4l2_subdev *sd,
}
static int adv7183_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -598,7 +599,7 @@ static int adv7183_probe(struct i2c_client *client)
adv7183_s_std(sd, decoder->std);
fmt.format.width = 720;
fmt.format.height = 576;
- adv7183_set_fmt(sd, NULL, &fmt);
+ adv7183_set_fmt(sd, NULL, NULL, &fmt);
/* initialize the hardware to the default control values */
ret = v4l2_ctrl_handler_setup(hdl);
diff --git a/drivers/media/i2c/adv7343.c b/drivers/media/i2c/adv7343.c
index 9b91d7073d3c..2bf17cd633c7 100644
--- a/drivers/media/i2c/adv7343.c
+++ b/drivers/media/i2c/adv7343.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* adv7343 - ADV7343 Video Encoder Driver
*
* The encoder hardware does not support SECAM.
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
diff --git a/drivers/media/i2c/adv7393.c b/drivers/media/i2c/adv7393.c
index 6f948ba02f86..b00783c141a9 100644
--- a/drivers/media/i2c/adv7393.c
+++ b/drivers/media/i2c/adv7393.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* adv7393 - ADV7393 Video Encoder Driver
*
@@ -9,15 +10,6 @@
* Based on ADV7343 driver,
*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/kernel.h>
diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c
index 678199196b84..28b755df30cf 100644
--- a/drivers/media/i2c/adv748x/adv748x-afe.c
+++ b/drivers/media/i2c/adv748x/adv748x-afe.c
@@ -349,6 +349,7 @@ static int adv748x_afe_get_format(struct v4l2_subdev *sd,
}
static int adv748x_afe_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c
index 3eb6d5e8f082..70594d001804 100644
--- a/drivers/media/i2c/adv748x/adv748x-core.c
+++ b/drivers/media/i2c/adv748x/adv748x-core.c
@@ -675,9 +675,6 @@ static int adv748x_parse_dt(struct adv748x_state *state)
continue;
}
- of_node_get(ep_np);
- state->endpoints[ep.port] = ep_np;
-
/*
* At least one input endpoint and one output endpoint shall
* be defined.
@@ -689,8 +686,13 @@ static int adv748x_parse_dt(struct adv748x_state *state)
/* Store number of CSI-2 lanes used for TXA and TXB. */
ret = adv748x_parse_csi2_lanes(state, ep.port, ep_np);
- if (ret)
+ if (ret) {
+ of_node_put(ep_np);
return ret;
+ }
+
+ of_node_get(ep_np);
+ state->endpoints[ep.port] = ep_np;
}
return in_found && out_found ? 0 : -ENODEV;
@@ -739,7 +741,7 @@ static int adv748x_probe(struct i2c_client *client)
ret = adv748x_parse_dt(state);
if (ret) {
adv_err(state, "Failed to parse device tree");
- goto err_free_mutex;
+ goto err_cleanup_dt;
}
/* Configure IO Regmap region */
@@ -809,7 +811,6 @@ err_cleanup_clients:
adv748x_unregister_clients(state);
err_cleanup_dt:
adv748x_dt_cleanup(state);
-err_free_mutex:
mutex_destroy(&state->mutex);
return ret;
diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c
index ebe7da8ebed7..1d7ab685d229 100644
--- a/drivers/media/i2c/adv748x/adv748x-csi2.c
+++ b/drivers/media/i2c/adv748x/adv748x-csi2.c
@@ -226,6 +226,7 @@ static bool adv748x_csi2_is_fmt_supported(struct adv748x_csi2 *tx, u32 code)
}
static int adv748x_csi2_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c
index b154dea29ba2..8d5ebb369961 100644
--- a/drivers/media/i2c/adv748x/adv748x-hdmi.c
+++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c
@@ -440,6 +440,7 @@ static int adv748x_hdmi_get_format(struct v4l2_subdev *sd,
}
static int adv748x_hdmi_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c
index 860cff50c522..27e0e2c31dbd 100644
--- a/drivers/media/i2c/adv7511-v4l2.c
+++ b/drivers/media/i2c/adv7511-v4l2.c
@@ -1273,6 +1273,7 @@ static int adv7511_get_fmt(struct v4l2_subdev *sd,
}
static int adv7511_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index ac9c69ce438f..2de743d8ca21 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -1943,6 +1943,7 @@ static int adv76xx_get_format(struct v4l2_subdev *sd,
}
static int adv76xx_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1963,6 +1964,7 @@ static int adv76xx_get_selection(struct v4l2_subdev *sd,
}
static int adv76xx_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -2449,7 +2451,7 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid)
cec_s_phys_addr(state->cec_adap, parent_pa, false);
/* enable hotplug after 143 ms */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7);
+ schedule_delayed_work(&state->delayed_work_enable_hotplug, V4L2_SET_EDID_HPD_LOW_JIFFIES);
return 0;
}
@@ -2641,8 +2643,9 @@ static int adv76xx_log_status(struct v4l2_subdev *sd)
"(16-235)" : "(0-255)",
(reg_io_0x02 & 0x08) ? "enabled" : "disabled");
}
+ ret = cp_read(sd, info->cp_csc) >> 4;
v4l2_info(sd, "Color space conversion: %s\n",
- csc_coeff_sel_rb[cp_read(sd, info->cp_csc) >> 4]);
+ ret < 0 ? "" : csc_coeff_sel_rb[ret]);
if (!is_digital_input(sd))
return 0;
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 3cfae89ce944..df6601d6d2b6 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -747,8 +747,9 @@ static int edid_write_vga_segment(struct v4l2_subdev *sd)
return -EIO;
}
- /* enable hotplug after 200 ms */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
+ /* enable hotplug after 143 ms */
+ schedule_delayed_work(&state->delayed_work_enable_hotplug,
+ V4L2_SET_EDID_HPD_LOW_JIFFIES);
return 0;
}
@@ -830,8 +831,9 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port)
}
cec_s_phys_addr(state->cec_adap, parent_pa, false);
- /* enable hotplug after 200 ms */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 5);
+ /* enable hotplug after 143 ms */
+ schedule_delayed_work(&state->delayed_work_enable_hotplug,
+ V4L2_SET_EDID_HPD_LOW_JIFFIES);
return 0;
}
@@ -2110,6 +2112,7 @@ static int adv7842_get_format(struct v4l2_subdev *sd,
}
static int adv7842_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c
index cea46f01997d..3449d95ee8a0 100644
--- a/drivers/media/i2c/ak881x.c
+++ b/drivers/media/i2c/ak881x.c
@@ -122,6 +122,7 @@ static int ak881x_enum_mbus_code(struct v4l2_subdev *sd,
}
static int ak881x_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -216,7 +217,6 @@ static const struct v4l2_subdev_video_ops ak881x_subdev_video_ops = {
static const struct v4l2_subdev_pad_ops ak881x_subdev_pad_ops = {
.enum_mbus_code = ak881x_enum_mbus_code,
.get_selection = ak881x_get_selection,
- .set_fmt = ak881x_fill_fmt,
.get_fmt = ak881x_fill_fmt,
};
diff --git a/drivers/media/i2c/alvium-csi2.c b/drivers/media/i2c/alvium-csi2.c
index f51f9b987759..d9a73565457f 100644
--- a/drivers/media/i2c/alvium-csi2.c
+++ b/drivers/media/i2c/alvium-csi2.c
@@ -1887,6 +1887,7 @@ static int alvium_init_state(struct v4l2_subdev *sd,
}
static int alvium_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -1922,6 +1923,7 @@ static int alvium_set_fmt(struct v4l2_subdev *sd,
}
static int alvium_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1957,6 +1959,7 @@ static int alvium_set_selection(struct v4l2_subdev *sd,
}
static int alvium_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2523,6 +2526,7 @@ static void alvium_remove(struct i2c_client *client)
* make sure to turn power off manually.
*/
pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
if (!pm_runtime_status_suspended(dev))
alvium_set_power(alvium, false);
pm_runtime_set_suspended(dev);
diff --git a/drivers/media/i2c/ar0521.c b/drivers/media/i2c/ar0521.c
index ed324c2d87aa..3ddbf10ef8a4 100644
--- a/drivers/media/i2c/ar0521.c
+++ b/drivers/media/i2c/ar0521.c
@@ -457,6 +457,7 @@ static int ar0521_get_fmt(struct v4l2_subdev *sd,
}
static int ar0521_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -1142,7 +1143,6 @@ static int ar0521_probe(struct i2c_client *client)
disable:
v4l2_async_unregister_subdev(&sensor->sd);
- media_entity_cleanup(&sensor->sd.entity);
free_ctrls:
v4l2_ctrl_handler_free(&sensor->ctrls.handler);
entity_cleanup:
diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c
index 8e25f970fd12..1cc4350856fd 100644
--- a/drivers/media/i2c/ccs/ccs-core.c
+++ b/drivers/media/i2c/ccs/ccs-core.c
@@ -2189,6 +2189,7 @@ static void ccs_propagate(struct v4l2_subdev *subdev,
}
static int ccs_set_format_source(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -2242,6 +2243,7 @@ static int ccs_set_format_source(struct v4l2_subdev *subdev,
}
static int ccs_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -2252,7 +2254,7 @@ static int ccs_set_format(struct v4l2_subdev *subdev,
if (fmt->pad == ssd->source_pad) {
int rval;
- rval = ccs_set_format_source(subdev, sd_state, fmt);
+ rval = ccs_set_format_source(subdev, ci, sd_state, fmt);
return rval;
}
@@ -2467,6 +2469,7 @@ static void ccs_set_compose_scaler(struct v4l2_subdev *subdev,
}
/* We're only called on source pads. This function sets scaling. */
static int ccs_set_compose(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2536,6 +2539,7 @@ static int ccs_sel_supported(struct v4l2_subdev *subdev,
}
static int ccs_set_crop(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2587,6 +2591,7 @@ static void ccs_get_native_size(struct ccs_subdev *ssd, struct v4l2_rect *r)
}
static int ccs_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2633,6 +2638,7 @@ static int ccs_get_selection(struct v4l2_subdev *subdev,
}
static int ccs_set_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2655,10 +2661,10 @@ static int ccs_set_selection(struct v4l2_subdev *subdev,
switch (sel->target) {
case V4L2_SEL_TGT_CROP:
- ret = ccs_set_crop(subdev, sd_state, sel);
+ ret = ccs_set_crop(subdev, ci, sd_state, sel);
break;
case V4L2_SEL_TGT_COMPOSE:
- ret = ccs_set_compose(subdev, sd_state, sel);
+ ret = ccs_set_compose(subdev, ci, sd_state, sel);
break;
default:
ret = -EINVAL;
diff --git a/drivers/media/i2c/ccs/ccs-data.h b/drivers/media/i2c/ccs/ccs-data.h
index 638df69804ec..522466b81929 100644
--- a/drivers/media/i2c/ccs/ccs-data.h
+++ b/drivers/media/i2c/ccs/ccs-data.h
@@ -160,7 +160,7 @@ struct ccs_pdaf_pix_loc_pixel_desc_group {
* @main_offset_y: Start Y coordinate of PDAF pixel blocks
* @global_pdaf_type: PDAF pattern type
* @block_width: Width of a block in pixels
- * @block_height: Heigth of a block in pixels
+ * @block_height: Height of a block in pixels
* @num_block_desc_groups: Number of block descriptor groups
* @block_desc_groups: Block descriptor groups
* @num_pixel_desc_grups: Number of pixel descriptor groups
@@ -197,7 +197,7 @@ struct ccs_pdaf_pix_loc {
* @module_rules: Rules for the module
* @sensor_pdaf: PDAF data for the sensor
* @module_pdaf: PDAF data for the module
- * @license_length: Lenght of the license data
+ * @license_length: Length of the license data
* @license: License data
* @end: Whether or not there's an end block
* @backing: Raw data, pointed to from elsewhere so keep it around
diff --git a/drivers/media/i2c/cvs/Kconfig b/drivers/media/i2c/cvs/Kconfig
index 4309d20dd726..f482f1ccc38a 100644
--- a/drivers/media/i2c/cvs/Kconfig
+++ b/drivers/media/i2c/cvs/Kconfig
@@ -4,6 +4,7 @@ config VIDEO_INTEL_CVS
tristate "Intel CVS CSI-2 bridge support"
depends on I2C && ACPI && VIDEO_DEV
depends on IPU_BRIDGE || !IPU_BRIDGE
+ default VIDEO_INTEL_IPU6
select MEDIA_CONTROLLER
select VIDEO_V4L2_SUBDEV_API
select V4L2_FWNODE
diff --git a/drivers/media/i2c/cvs/core.c b/drivers/media/i2c/cvs/core.c
index d4a3b9c3bab1..d9c51e1c0fb5 100644
--- a/drivers/media/i2c/cvs/core.c
+++ b/drivers/media/i2c/cvs/core.c
@@ -21,7 +21,6 @@
#include <linux/workqueue.h>
#include <media/ipu-bridge.h>
-#include <media/ipu6-pci-table.h>
#include "icvs.h"
@@ -70,6 +69,13 @@ static const struct icvs_device_quirk cvs_quirk_table[] = {
ICVS_NO_CAPS |
ICVS_NO_FW_UPDATE
}, /* Lattice NX33 */
+ { 0x2ac1, 0x20d1, ICVS_NO_MIPI_CONFIG |
+ ICVS_NO_CAPS |
+ ICVS_NO_FW_UPDATE
+ }, /*
+ * The Lattice device was found on a Dell laptop
+ * XPS 14 (Dell 14 Premium) DA14250
+ */
{ 0x06CB, 0x0701, ICVS_SKIP_FW_RESET |
ICVS_HOST_SENSOR_PWR_CTRL |
ICVS_HOST_PRIV_CTRL |
@@ -656,14 +662,12 @@ static int cvs_configure_dev_caps(struct icvs *ctx)
*/
static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
{
- struct pci_dev *ipu = NULL;
+ struct pci_dev *ipu;
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);
+ ipu = ipu_bridge_get_ipu6();
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);
@@ -725,8 +729,6 @@ static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
}
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),
@@ -734,14 +736,12 @@ static int cvs_core_probe(struct device *dev, struct i2c_client *i2c)
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);
+ /*
+ * Do not request the line: another device's _CRS may list
+ * the same pin, and its driver would then fail with -EBUSY.
+ */
+ ctx->irq = acpi_dev_gpio_irq_get_by(ACPI_COMPANION(dev),
+ "wake", 0);
if (ctx->irq < 0) {
ret = dev_err_probe(dev, ctx->irq,
"failed to get wake IRQ\n");
diff --git a/drivers/media/i2c/cvs/v4l2.c b/drivers/media/i2c/cvs/v4l2.c
index 9fadca7a3bee..776fe9460168 100644
--- a/drivers/media/i2c/cvs/v4l2.c
+++ b/drivers/media/i2c/cvs/v4l2.c
@@ -13,6 +13,7 @@
#include <media/v4l2-async.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mc.h>
@@ -71,19 +72,6 @@ static int csi_set_link_cfg(struct icvs *ctx, u64 link_freq)
* 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)
@@ -129,19 +117,6 @@ err_rpm_put:
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)
@@ -167,15 +142,6 @@ static int cvs_csi_disable_streams(struct v4l2_subdev *sd,
/*
* 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)
{
@@ -186,18 +152,8 @@ static int cvs_csi_init_state(struct v4l2_subdev *sd,
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,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -319,17 +275,6 @@ static int cvs_csi_set_fmt(struct v4l2_subdev *sd,
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)
{
@@ -384,23 +329,12 @@ static const struct media_entity_operations cvs_csi_entity_ops = {
/*
* 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;
+ int pad, ret;
pad = media_entity_get_fwnode_pad(&sd->entity, asc->match.fwnode,
MEDIA_PAD_FL_SOURCE);
@@ -409,17 +343,15 @@ static int cvs_csi_notify_bound(struct v4l2_async_notifier *notifier,
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);
+ ret = media_create_pad_link(&sd->entity, pad, &ctx->subdev.entity,
+ ICVS_CSI_PAD_SINK, MEDIA_LNK_FL_ENABLED |
+ MEDIA_LNK_FL_IMMUTABLE);
+ if (ret)
+ return ret;
+
+ return v4l2_device_register_subdev_nodes(sd->v4l2_dev);
}
-/**
- * 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)
@@ -437,14 +369,6 @@ static const struct v4l2_async_notifier_operations cvs_csi_notify_ops = {
/*
* 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;
diff --git a/drivers/media/i2c/cx25840/cx25840-core.c b/drivers/media/i2c/cx25840/cx25840-core.c
index 8b7dd43ed208..e90d71fe9959 100644
--- a/drivers/media/i2c/cx25840/cx25840-core.c
+++ b/drivers/media/i2c/cx25840/cx25840-core.c
@@ -1771,6 +1771,7 @@ static int cx25840_s_ctrl(struct v4l2_ctrl *ctrl)
/* ----------------------------------------------------------------------- */
static int cx25840_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -2289,29 +2290,27 @@ static int cx25840_init(struct v4l2_subdev *sd, u32 val)
{
struct cx25840_state *state = to_state(sd);
+ if (!is_cx2584x(state))
+ return -EOPNOTSUPP;
+
state->generic_mode = true;
- if (is_cx2584x(state)) {
- /* set datasheet video output defaults */
- state->vid_config = CX25840_VCONFIG_FMT_BT656 |
- CX25840_VCONFIG_RES_8BIT |
- CX25840_VCONFIG_VBIRAW_DISABLED |
- CX25840_VCONFIG_ANCDATA_ENABLED |
- CX25840_VCONFIG_TASKBIT_ONE |
- CX25840_VCONFIG_ACTIVE_HORIZONTAL |
- CX25840_VCONFIG_VALID_NORMAL |
- CX25840_VCONFIG_HRESETW_NORMAL |
- CX25840_VCONFIG_CLKGATE_NONE |
- CX25840_VCONFIG_DCMODE_DWORDS |
- CX25840_VCONFIG_IDID0S_NORMAL |
- CX25840_VCONFIG_VIPCLAMP_DISABLED;
-
- /* add additional settings */
- cx25840_vconfig_add(state, val);
- } else {
- /* TODO: generic mode needs to be developed for other chips */
- WARN_ON(1);
- }
+ /* set datasheet video output defaults */
+ state->vid_config = CX25840_VCONFIG_FMT_BT656 |
+ CX25840_VCONFIG_RES_8BIT |
+ CX25840_VCONFIG_VBIRAW_DISABLED |
+ CX25840_VCONFIG_ANCDATA_ENABLED |
+ CX25840_VCONFIG_TASKBIT_ONE |
+ CX25840_VCONFIG_ACTIVE_HORIZONTAL |
+ CX25840_VCONFIG_VALID_NORMAL |
+ CX25840_VCONFIG_HRESETW_NORMAL |
+ CX25840_VCONFIG_CLKGATE_NONE |
+ CX25840_VCONFIG_DCMODE_DWORDS |
+ CX25840_VCONFIG_IDID0S_NORMAL |
+ CX25840_VCONFIG_VIPCLAMP_DISABLED;
+
+ /* add additional settings */
+ cx25840_vconfig_add(state, val);
return 0;
}
diff --git a/drivers/media/i2c/ds90ub913.c b/drivers/media/i2c/ds90ub913.c
index 6abb5e324ae1..ca0b07d7109b 100644
--- a/drivers/media/i2c/ds90ub913.c
+++ b/drivers/media/i2c/ds90ub913.c
@@ -373,6 +373,7 @@ static int ub913_set_routing(struct v4l2_subdev *sd,
}
static int ub913_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ds90ub953.c b/drivers/media/i2c/ds90ub953.c
index d4228e1134ff..e6ae9cadefa2 100644
--- a/drivers/media/i2c/ds90ub953.c
+++ b/drivers/media/i2c/ds90ub953.c
@@ -426,6 +426,7 @@ static int ub953_set_routing(struct v4l2_subdev *sd,
static int ub953_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ds90ub960.c b/drivers/media/i2c/ds90ub960.c
index 15a9797b47ac..7d7ba6d349fc 100644
--- a/drivers/media/i2c/ds90ub960.c
+++ b/drivers/media/i2c/ds90ub960.c
@@ -4045,6 +4045,7 @@ out_unlock:
}
static int ub960_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/et8ek8/et8ek8_driver.c b/drivers/media/i2c/et8ek8/et8ek8_driver.c
index 738e2801016a..e8a7b7d87bb1 100644
--- a/drivers/media/i2c/et8ek8/et8ek8_driver.c
+++ b/drivers/media/i2c/et8ek8/et8ek8_driver.c
@@ -1013,6 +1013,7 @@ static int et8ek8_get_pad_format(struct v4l2_subdev *subdev,
}
static int et8ek8_set_pad_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/gc0308.c b/drivers/media/i2c/gc0308.c
index 15900d5414cf..893dfe2e1c3e 100644
--- a/drivers/media/i2c/gc0308.c
+++ b/drivers/media/i2c/gc0308.c
@@ -1043,6 +1043,7 @@ static void gc0308_update_pad_format(const struct gc0308_frame_size *mode,
}
static int gc0308_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/gc0310.c b/drivers/media/i2c/gc0310.c
index 754e82ad50ae..5e941291dbf7 100644
--- a/drivers/media/i2c/gc0310.c
+++ b/drivers/media/i2c/gc0310.c
@@ -365,6 +365,7 @@ static void gc0310_fill_format(struct v4l2_mbus_framefmt *fmt)
}
static int gc0310_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -537,7 +538,6 @@ static const struct v4l2_subdev_pad_ops gc0310_pad_ops = {
.enum_mbus_code = gc0310_enum_mbus_code,
.enum_frame_size = gc0310_enum_frame_size,
.get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = v4l2_subdev_get_fmt, /* Only 1 fixed mode supported */
.get_selection = gc0310_get_selection,
.set_selection = gc0310_get_selection,
.enable_streams = gc0310_enable_streams,
diff --git a/drivers/media/i2c/gc05a2.c b/drivers/media/i2c/gc05a2.c
index 7cf7cde1f936..5b95d8881649 100644
--- a/drivers/media/i2c/gc05a2.c
+++ b/drivers/media/i2c/gc05a2.c
@@ -730,6 +730,7 @@ static void gc05a2_update_pad_format(struct gc05a2 *gc08a3,
}
static int gc05a2_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -761,6 +762,7 @@ static int gc05a2_set_format(struct v4l2_subdev *sd,
}
static int gc05a2_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -795,7 +797,7 @@ static int gc05a2_init_state(struct v4l2_subdev *sd,
},
};
- gc05a2_set_format(sd, state, &fmt);
+ gc05a2_set_format(sd, NULL, state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/gc08a3.c b/drivers/media/i2c/gc08a3.c
index 4144aad8f2da..c1ae05ed1f2a 100644
--- a/drivers/media/i2c/gc08a3.c
+++ b/drivers/media/i2c/gc08a3.c
@@ -705,6 +705,7 @@ static void gc08a3_update_pad_format(struct gc08a3 *gc08a3,
}
static int gc08a3_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -737,6 +738,7 @@ static int gc08a3_set_format(struct v4l2_subdev *sd,
}
static int gc08a3_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -771,7 +773,7 @@ static int gc08a3_init_state(struct v4l2_subdev *sd,
},
};
- gc08a3_set_format(sd, state, &fmt);
+ gc08a3_set_format(sd, NULL, state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c
index b215963a2648..661a59641f19 100644
--- a/drivers/media/i2c/gc2145.c
+++ b/drivers/media/i2c/gc2145.c
@@ -709,6 +709,7 @@ static int gc2145_init_state(struct v4l2_subdev *sd,
}
static int gc2145_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -774,6 +775,7 @@ static int gc2145_enum_frame_size(struct v4l2_subdev *sd,
}
static int gc2145_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c
index de573cee4451..4c13adf4a900 100644
--- a/drivers/media/i2c/hi556.c
+++ b/drivers/media/i2c/hi556.c
@@ -960,6 +960,7 @@ __hi556_get_pad_crop(struct hi556 *hi556,
}
static int hi556_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1071,6 +1072,7 @@ static int hi556_set_stream(struct v4l2_subdev *sd, int enable)
}
static int hi556_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c
index a3f77b8434ca..025f8bcd17bc 100644
--- a/drivers/media/i2c/hi846.c
+++ b/drivers/media/i2c/hi846.c
@@ -1688,6 +1688,7 @@ static int __maybe_unused hi846_resume(struct device *dev)
}
static int hi846_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -1840,6 +1841,7 @@ static int hi846_enum_frame_size(struct v4l2_subdev *sd,
}
static int hi846_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/hi847.c b/drivers/media/i2c/hi847.c
index def01aa07b2f..41b8cac6a5e3 100644
--- a/drivers/media/i2c/hi847.c
+++ b/drivers/media/i2c/hi847.c
@@ -2187,7 +2187,7 @@ struct hi847 {
/* Current mode */
const struct hi847_mode *cur_mode;
- /* To serialize asynchronus callbacks */
+ /* To serialize asynchronous callbacks */
struct mutex mutex;
};
@@ -2639,6 +2639,7 @@ static int hi847_set_stream(struct v4l2_subdev *sd, int enable)
}
static int hi847_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/hm1246.c b/drivers/media/i2c/hm1246.c
new file mode 100644
index 000000000000..be1c3d75657e
--- /dev/null
+++ b/drivers/media/i2c/hm1246.c
@@ -0,0 +1,1287 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Himax HM1246 image sensor
+ *
+ * Copyright 2026 Matthias Fend <matthias.fend@emfend.at>
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/limits.h>
+#include <linux/math.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/types.h>
+#include <linux/units.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-cci.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+/* Status registers */
+#define HM1246_MODEL_ID_REG CCI_REG16(0x0000)
+
+/* General setup registers */
+#define HM1246_MODE_SELECT_REG CCI_REG8(0x0100)
+#define HM1246_MODE_SELECT_STANDBY 0x00
+#define HM1246_MODE_SELECT_STREAM 0x01
+#define HM1246_MODE_SELECT_STOP 0x02
+#define HM1246_IMAGE_ORIENTATION_REG CCI_REG8(0x0101)
+#define HM1246_IMAGE_ORIENTATION_VFLIP BIT(1)
+#define HM1246_IMAGE_ORIENTATION_HFLIP BIT(0)
+#define HM1246_CMU_UPDATE_REG CCI_REG8(0x0104)
+
+/* Output setup registers */
+#define HM1246_COARSE_INTG_REG CCI_REG16(0x0202)
+#define HM1246_ANALOG_GLOBAL_GAIN_REG CCI_REG8(0x0205)
+
+/* Clock setup registers */
+#define HM1246_PLL1CFG_REG CCI_REG8(0x0303)
+#define HM1246_PLL1CFG_MULTIPLIER(x) (((x) & 0xff) << 0)
+#define HM1246_PLL2CFG_REG CCI_REG8(0x0305)
+#define HM1246_PLL2CFG_PRE_DIV(x) (((x) & 0x1f) << 1)
+#define HM1246_PLL2CFG_MULTIPLIER(x) (((x) & 0x01) << 0)
+#define HM1246_PLL3CFG_REG CCI_REG8(0x0307)
+#define HM1246_PLL3CFG_POST_DIV(x) (((x) & 0x3) << 6)
+#define HM1246_PLL3CFG_SYSCLK_DIV(x) (((x) & 0x3) << 4)
+#define HM1246_PLL3CFG_PCLK_DIV(x) (((x) & 0x7) << 0)
+
+/* Frame timing registers */
+#define HM1246_FRAME_LENGTH_LINES_REG CCI_REG16(0x0340)
+#define HM1246_LINE_LENGTH_PCK_REG CCI_REG16(0x0342)
+
+/* Image size registers */
+#define HM1246_X_ADDR_START_REG CCI_REG16(0x0344)
+#define HM1246_Y_ADDR_START_REG CCI_REG16(0x0346)
+#define HM1246_X_ADDR_END_REG CCI_REG16(0x0348)
+#define HM1246_Y_ADDR_END_REG CCI_REG16(0x034a)
+#define HM1246_X_LA_START_REG CCI_REG16(0x0351)
+#define HM1246_X_LA_END_REG CCI_REG16(0x0353)
+#define HM1246_Y_LA_START_REG CCI_REG16(0x0355)
+#define HM1246_Y_LA_END_REG CCI_REG16(0x0357)
+
+/* Test pattern registers */
+#define HM1246_TEST_PATTERN_MODE_REG CCI_REG8(0x0601)
+#define HM1246_TEST_PATTERN_MODE_MODE(x) (((x) & 0xf) << 4)
+#define HM1246_TEST_PATTERN_MODE_ENABLE BIT(0)
+#define HM1246_TEST_DATA_BLUE_REG CCI_REG16(0x0602)
+#define HM1246_TEST_DATA_GB_REG CCI_REG16(0x0604)
+#define HM1246_TEST_DATA_RED_REG CCI_REG16(0x0606)
+#define HM1246_TEST_DATA_GR_REG CCI_REG16(0x0608)
+
+/* SBC registers */
+#define HM1246_SBC_BOOT_REF2_REG CCI_REG8(0x2001)
+#define HM1246_SBC_BOOT_REF2_PLL_LOCK BIT(4)
+#define HM1246_SBC_CTRL_REG CCI_REG8(0x2003)
+#define HM1246_SBC_CTRL_PLL_EN BIT(0)
+
+/* System registers */
+#define HM1246_OUTPUT_PRT_CTRL_REG CCI_REG8(0x2f02)
+#define HM1246_POLARITY_CTRL_REG CCI_REG8(0x2f20)
+#define HM1246_POLARITY_CTRL_HSYNC BIT(7)
+#define HM1246_POLARITY_CTRL_VSYNC BIT(6)
+#define HM1246_PCLK_CTRL_REG CCI_REG8(0x2f24)
+#define HM1246_PCLK_CTRL_POL BIT(3)
+
+/* Digital window control & parameter registers */
+#define HM1246_DWIN_XOFFSET_REG CCI_REG16(0xd5e4)
+#define HM1246_DWIN_XSIZE_REG CCI_REG16(0xd5e6)
+#define HM1246_DWIN_YOFFSET_REG CCI_REG16(0xd5e8)
+#define HM1246_DWIN_YSIZE_REG CCI_REG16(0xd5ea)
+
+#define HM1246_MODEL_ID 0x1245
+
+#define HM1246_NATIVE_WIDTH 1296
+#define HM1246_NATIVE_HEIGHT 976
+
+#define HM1246_VTS_MAX 65535
+
+#define HM1246_COARSE_INTG_MARGIN 2
+#define HM1246_COARSE_INTG_MIN 4
+#define HM1246_COARSE_INTG_STEP 1
+
+#define HM1246_ANALOG_GLOBAL_GAIN_MIN 0x00
+#define HM1246_ANALOG_GLOBAL_GAIN_MAX 0xe8
+#define HM1246_ANALOG_GLOBAL_GAIN_STEP 0x01
+
+#define HM1246_XCLK_MIN (6 * HZ_PER_MHZ)
+#define HM1246_XCLK_MAX (27 * HZ_PER_MHZ)
+
+#define HM1246_PCLK_MIN (8 * HZ_PER_MHZ)
+#define HM1246_PCLK_MAX (96 * HZ_PER_MHZ)
+
+#define HM1246_PLL_VCO_MIN (360 * HZ_PER_MHZ)
+#define HM1246_PLL_VCO_MAX (680 * HZ_PER_MHZ)
+
+#define HM1246_PLL_INCLK_MIN (1000 * HZ_PER_KHZ)
+#define HM1246_PLL_INCLK_MAX (2500 * HZ_PER_KHZ)
+
+#define HM1246_PLL_MULTI_L_MIN 1
+#define HM1246_PLL_MULTI_L_MAX 256
+
+#define HM1246_PLL_MULTI_H_MIN 2
+#define HM1246_PLL_MULTI_H_MAX 3
+
+#define HM1246_PLL_MULTI_MIN \
+ (HM1246_PLL_MULTI_H_MIN * HM1246_PLL_MULTI_L_MIN)
+#define HM1246_PLL_MULTI_MAX \
+ (HM1246_PLL_MULTI_H_MAX * HM1246_PLL_MULTI_L_MAX)
+
+static const char *const hm1246_test_pattern_menu[] = {
+ "Disabled",
+ "Checkerboard",
+ "Ramp",
+ "Moving ones",
+ "Blending color bars",
+ "Color bars",
+ "Solid white",
+ "Solid black",
+ "Solid red",
+ "Solid green",
+ "Solid blue",
+};
+
+static const char *const hm1246_supply_names[] = {
+ "avdd",
+ "iovdd",
+ "dvdd",
+};
+
+struct hm1246 {
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct device *dev;
+
+ struct regulator_bulk_data supplies[ARRAY_SIZE(hm1246_supply_names)];
+ struct clk *xclk;
+ unsigned long xclk_freq;
+ struct reset_control *reset;
+ unsigned int mbus_flags;
+ s64 link_frequency;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct v4l2_ctrl *exposure_ctrl;
+ struct v4l2_ctrl *hflip_ctrl;
+ struct v4l2_ctrl *vflip_ctrl;
+
+ struct regmap *regmap;
+
+ bool identified;
+};
+
+static const struct cci_reg_sequence mode_1296x976_raw[] = {
+ { HM1246_X_LA_START_REG, 60 },
+ { HM1246_X_LA_END_REG, 1355 },
+ { HM1246_Y_LA_START_REG, 0 },
+ { HM1246_Y_LA_END_REG, 975 },
+ { HM1246_OUTPUT_PRT_CTRL_REG, 0x20 },
+ { CCI_REG8(0x300a), 0x01 },
+ { CCI_REG8(0x300b), 0x00 },
+ { CCI_REG8(0x50f5), 0x01 },
+ { CCI_REG8(0x50dd), 0x00 },
+ { CCI_REG8(0x50a1), 0x02 },
+ { CCI_REG8(0x50aa), 0x1c },
+ { CCI_REG8(0x50ac), 0xdd },
+ { CCI_REG8(0x50ad), 0x08 },
+ { CCI_REG8(0x50ab), 0x04 },
+ { CCI_REG8(0x50a0), 0x40 },
+ { CCI_REG8(0x50a2), 0x12 },
+ { CCI_REG8(0x50ae), 0x30 },
+ { CCI_REG8(0x50b3), 0x04 },
+ { CCI_REG8(0x5204), 0x40 },
+ { CCI_REG8(0x5208), 0x55 },
+ { CCI_REG8(0x520b), 0x05 },
+ { CCI_REG8(0x520d), 0x40 },
+ { CCI_REG8(0x5214), 0x18 },
+ { CCI_REG8(0x5215), 0x0f },
+ { CCI_REG8(0x5217), 0x01 },
+ { CCI_REG8(0x5218), 0x07 },
+ { CCI_REG8(0x5219), 0x01 },
+ { CCI_REG8(0x521a), 0x50 },
+ { CCI_REG8(0x521b), 0x24 },
+ { CCI_REG8(0x5232), 0x01 },
+ { CCI_REG8(0x5220), 0x11 },
+ { CCI_REG8(0x5227), 0x01 },
+ { CCI_REG8(0x5106), 0xc1 },
+ { CCI_REG8(0x5115), 0xc0 },
+ { CCI_REG8(0x5116), 0xc1 },
+ { CCI_REG8(0x5138), 0x40 },
+ { CCI_REG8(0x5139), 0x60 },
+ { CCI_REG8(0x513a), 0x80 },
+ { CCI_REG8(0x513b), 0xa0 },
+ { CCI_REG8(0x513c), 0xa1 },
+ { CCI_REG8(0x513d), 0xa2 },
+ { CCI_REG8(0x513e), 0xa3 },
+ { CCI_REG8(0x5140), 0x40 },
+ { CCI_REG8(0x5141), 0x60 },
+ { CCI_REG8(0x5142), 0x80 },
+ { CCI_REG8(0x5143), 0x81 },
+ { CCI_REG8(0x5144), 0x82 },
+ { CCI_REG8(0x5145), 0x83 },
+ { CCI_REG8(0x5146), 0x93 },
+ { CCI_REG8(0x51c1), 0xc3 },
+ { CCI_REG8(0x51c5), 0xc3 },
+ { CCI_REG8(0x51c9), 0xc3 },
+ { CCI_REG8(0x51cd), 0xc2 },
+ { CCI_REG8(0x51d1), 0xc1 },
+ { CCI_REG8(0x51d5), 0xc1 },
+ { CCI_REG8(0x51d9), 0x81 },
+ { CCI_REG8(0x51dd), 0x81 },
+ { CCI_REG8(0x51c2), 0x49 },
+ { CCI_REG8(0x51c6), 0x49 },
+ { CCI_REG8(0x51ca), 0x49 },
+ { CCI_REG8(0x51ce), 0x49 },
+ { CCI_REG8(0x51d2), 0x49 },
+ { CCI_REG8(0x51d6), 0x59 },
+ { CCI_REG8(0x51da), 0x59 },
+ { CCI_REG8(0x51de), 0x59 },
+ { CCI_REG8(0x51c3), 0x20 },
+ { CCI_REG8(0x51c7), 0x38 },
+ { CCI_REG8(0x51cb), 0x21 },
+ { CCI_REG8(0x51cf), 0x11 },
+ { CCI_REG8(0x51d3), 0x11 },
+ { CCI_REG8(0x51d7), 0x13 },
+ { CCI_REG8(0x51db), 0x13 },
+ { CCI_REG8(0x51df), 0x13 },
+ { CCI_REG8(0x51e0), 0x03 },
+ { CCI_REG8(0x51e2), 0x03 },
+ { CCI_REG8(0x51f0), 0x42 },
+ { CCI_REG8(0x51f1), 0x40 },
+ { CCI_REG8(0x51f2), 0x4a },
+ { CCI_REG8(0x51f3), 0x48 },
+ { CCI_REG8(0x5015), 0x73 },
+ { CCI_REG8(0x504a), 0x04 },
+ { CCI_REG8(0x5044), 0x07 },
+ { CCI_REG8(0x5040), 0x03 },
+ { CCI_REG8(0x5135), 0xc4 },
+ { CCI_REG8(0x5136), 0xc5 },
+ { CCI_REG8(0x5166), 0xc4 },
+ { CCI_REG8(0x5196), 0xc4 },
+ { CCI_REG8(0x51c0), 0x10 },
+ { CCI_REG8(0x51c4), 0x10 },
+ { CCI_REG8(0x51c8), 0xa0 },
+ { CCI_REG8(0x51cc), 0xa0 },
+ { CCI_REG8(0x51d0), 0xa1 },
+ { CCI_REG8(0x51d4), 0xa5 },
+ { CCI_REG8(0x51d8), 0xa5 },
+ { CCI_REG8(0x51dc), 0xa5 },
+ { CCI_REG8(0x5200), 0xe4 },
+ { CCI_REG8(0x5209), 0x04 },
+ { CCI_REG8(0x301b), 0x01 },
+ { CCI_REG8(0x3130), 0x01 },
+ { CCI_REG8(0x5013), 0x07 },
+ { CCI_REG8(0x5016), 0x01 },
+ { CCI_REG8(0x501d), 0x50 },
+ { CCI_REG8(0x0350), 0xfe },
+ { CCI_REG8(0x2f03), 0x15 },
+ { CCI_REG8(0xd380), 0x00 },
+ { CCI_REG8(0x3047), 0x7f },
+ { CCI_REG8(0x304d), 0x34 },
+ { CCI_REG8(0x3041), 0x4b },
+ { CCI_REG8(0x3042), 0x2d },
+ { CCI_REG8(0x3056), 0x64 },
+ { CCI_REG8(0x3059), 0x1e },
+ { CCI_REG8(0x305e), 0x10 },
+ { CCI_REG8(0x305f), 0x10 },
+ { CCI_REG8(0x306d), 0x10 },
+ { CCI_REG8(0x306e), 0x0c },
+ { CCI_REG8(0x3064), 0x50 },
+ { CCI_REG8(0x3067), 0x78 },
+ { CCI_REG8(0x3068), 0x4b },
+ { CCI_REG8(0x306a), 0x78 },
+ { CCI_REG8(0x306b), 0x4b },
+ { CCI_REG8(0xd442), 0x3d },
+ { CCI_REG8(0xd443), 0x06 },
+ { CCI_REG8(0xd440), 0x63 },
+ { CCI_REG8(0xd446), 0xb0 },
+ { CCI_REG8(0xd447), 0x60 },
+ { CCI_REG8(0xd448), 0x48 },
+ { CCI_REG8(0xd449), 0x30 },
+ { CCI_REG8(0xd44a), 0x18 },
+ { CCI_REG8(0xd360), 0x03 },
+ { CCI_REG8(0x30ac), 0x10 },
+ { CCI_REG8(0x30ad), 0x10 },
+ { CCI_REG8(0x30ae), 0x10 },
+ { CCI_REG8(0x3040), 0x0b },
+ { CCI_REG8(0x2002), 0x00 },
+ { CCI_REG8(0x2000), 0x08 },
+};
+
+struct hm1246_reg_list {
+ u32 num_of_regs;
+ const struct cci_reg_sequence *regs;
+};
+
+struct hm1246_mode {
+ u32 codes[4];
+ u32 clocks_per_pixel;
+ struct v4l2_rect rect;
+ u32 hts;
+ u32 vts_min;
+ const struct hm1246_reg_list reg_list;
+};
+
+#define FLIP_FORMAT_INDEX(v, h) ((v ? 2 : 0) | (h ? 1 : 0))
+
+/* Get the format code of the mode considering current flip setting. */
+static u32 hm1246_get_format_code(struct hm1246 *hm1246,
+ const struct hm1246_mode *hm1246_mode)
+{
+ return hm1246_mode->codes[FLIP_FORMAT_INDEX(hm1246->vflip_ctrl->val,
+ hm1246->hflip_ctrl->val)];
+}
+
+static const struct hm1246_mode hm1246_modes[] = {
+ {
+ .codes = {
+ [FLIP_FORMAT_INDEX(0, 0)] = MEDIA_BUS_FMT_SBGGR10_1X10,
+ [FLIP_FORMAT_INDEX(0, 1)] = MEDIA_BUS_FMT_SGBRG10_1X10,
+ [FLIP_FORMAT_INDEX(1, 0)] = MEDIA_BUS_FMT_SGRBG10_1X10,
+ [FLIP_FORMAT_INDEX(1, 1)] = MEDIA_BUS_FMT_SRGGB10_1X10,
+ },
+ .clocks_per_pixel = 1,
+ .rect.top = 0,
+ .rect.left = 0,
+ .rect.width = 1296,
+ .rect.height = 976,
+ .hts = 1420,
+ .vts_min = 990,
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(mode_1296x976_raw),
+ .regs = mode_1296x976_raw,
+ },
+ },
+};
+
+static inline struct hm1246 *to_hm1246(struct v4l2_subdev *sd)
+{
+ return container_of_const(sd, struct hm1246, sd);
+}
+
+static const struct hm1246_mode *
+hm1246_find_mode_by_mbus_code(struct hm1246 *hm1246, u32 code)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(hm1246_modes); i++) {
+ if (code == hm1246_get_format_code(hm1246, &hm1246_modes[i]))
+ return &hm1246_modes[i];
+ }
+
+ return NULL;
+}
+
+static int hm1246_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hm1246 *hm1246 = to_hm1246(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(hm1246_supply_names),
+ hm1246->supplies);
+ if (ret) {
+ dev_err(hm1246->dev, "failed to enable regulators\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(hm1246->xclk);
+ if (ret) {
+ regulator_bulk_disable(ARRAY_SIZE(hm1246_supply_names),
+ hm1246->supplies);
+ dev_err(hm1246->dev, "failed to enable clock\n");
+ return ret;
+ }
+
+ reset_control_deassert(hm1246->reset);
+
+ /*
+ * XSHUTDOWN to crystal clock oscillation (tcrystal): 650us (typical)
+ * Sample bootstrap pin (tsample): 2000us (maximum)
+ * Built in self test (tbist): 3000us (maximum)
+ */
+ fsleep(6 * USEC_PER_MSEC);
+
+ return 0;
+}
+
+static int hm1246_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct hm1246 *hm1246 = to_hm1246(sd);
+
+ reset_control_assert(hm1246->reset);
+
+ clk_disable_unprepare(hm1246->xclk);
+
+ regulator_bulk_disable(ARRAY_SIZE(hm1246_supply_names),
+ hm1246->supplies);
+
+ return 0;
+}
+
+static int hm1246_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct hm1246 *hm1246 = to_hm1246(sd);
+
+ if (code->index >= ARRAY_SIZE(hm1246_modes))
+ return -EINVAL;
+
+ code->code = hm1246_get_format_code(hm1246, &hm1246_modes[code->index]);
+
+ return 0;
+}
+
+static int hm1246_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct hm1246 *hm1246 = to_hm1246(subdev);
+ const struct hm1246_mode *mode;
+
+ if (fse->index > 0)
+ return -EINVAL;
+
+ mode = hm1246_find_mode_by_mbus_code(hm1246, fse->code);
+ if (!mode)
+ return -EINVAL;
+
+ fse->min_width = mode->rect.width;
+ fse->max_width = mode->rect.width;
+ fse->min_height = mode->rect.height;
+ fse->max_height = mode->rect.height;
+
+ return 0;
+}
+
+static void hm1246_update_pad_format(struct hm1246 *hm1246,
+ const struct hm1246_mode *hm1246_mode,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ fmt->width = hm1246_mode->rect.width;
+ fmt->height = hm1246_mode->rect.height;
+ fmt->code = hm1246_get_format_code(hm1246, hm1246_mode);
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = V4L2_COLORSPACE_RAW;
+ fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
+ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
+}
+
+static int hm1246_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct hm1246 *hm1246 = to_hm1246(sd);
+ struct v4l2_mbus_framefmt *mbus_fmt;
+ struct v4l2_rect *crop;
+ const struct hm1246_mode *mode;
+
+ mode = hm1246_find_mode_by_mbus_code(hm1246, fmt->format.code);
+ if (!mode)
+ mode = &hm1246_modes[0];
+
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ *crop = mode->rect;
+
+ hm1246_update_pad_format(hm1246, mode, &fmt->format);
+ mbus_fmt = v4l2_subdev_state_get_format(state, 0);
+ *mbus_fmt = fmt->format;
+
+ return 0;
+}
+
+static int hm1246_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *sel)
+{
+ const struct v4l2_mbus_framefmt *format;
+ const struct hm1246_mode *mode;
+
+ format = v4l2_subdev_state_get_format(state, 0);
+ mode = v4l2_find_nearest_size(hm1246_modes, ARRAY_SIZE(hm1246_modes),
+ rect.width, rect.height, format->width,
+ format->height);
+
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP:
+ sel->r = *v4l2_subdev_state_get_crop(state, 0);
+ return 0;
+
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.top = 0;
+ sel->r.left = 0;
+ sel->r.width = HM1246_NATIVE_WIDTH;
+ sel->r.height = HM1246_NATIVE_HEIGHT;
+ return 0;
+
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r = mode->rect;
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int hm1246_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct hm1246 *hm1246 = to_hm1246(sd);
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .pad = 0,
+ .format = {
+ .code = hm1246_get_format_code(hm1246,
+ &hm1246_modes[0]),
+ .width = hm1246_modes[0].rect.width,
+ .height = hm1246_modes[0].rect.height,
+ },
+ };
+
+ hm1246_set_format(sd, NULL, state, &fmt);
+
+ return 0;
+}
+
+static int hm1246_calc_pll(u32 xclk, u32 link_freq, u32 clocks_per_pixel,
+ u8 *pll1, u8 *pll2, u8 *pll3)
+{
+ static const u8 pclk_div_table[] = { 4, 5, 6, 7, 8, 12, 14, 16 };
+ static const u8 sysclk_div_table[] = { 1, 2, 3, 4 };
+ static const u8 post_div_table[] = { 1, 2, 4, 8 };
+ static const int sysclk_pclk_ratio = 3; /* Recommended value */
+ u32 pclk, vco_out;
+ int pclk_div_index, sysclk_div_index, post_div_index;
+ bool sysclk_pclk_ratio_found = false;
+
+ if (link_freq < HM1246_PCLK_MIN || link_freq > HM1246_PCLK_MAX)
+ return -EINVAL;
+
+ /*
+ * In raw mode (1 pixel per clock) the pixel clock is internally
+ * divided by two.
+ */
+ pclk = 2 * link_freq / clocks_per_pixel;
+
+ /* Find suitable PCLK and SYSCLK dividers. */
+ for (pclk_div_index = 0; pclk_div_index < ARRAY_SIZE(pclk_div_table);
+ pclk_div_index++) {
+ for (sysclk_div_index = 0;
+ sysclk_div_index < ARRAY_SIZE(sysclk_div_table);
+ sysclk_div_index++) {
+ if (sysclk_div_table[sysclk_div_index] *
+ sysclk_pclk_ratio ==
+ pclk_div_table[pclk_div_index]) {
+ sysclk_pclk_ratio_found = true;
+ break;
+ }
+ }
+ if (sysclk_pclk_ratio_found)
+ break;
+ }
+
+ if (!sysclk_pclk_ratio_found)
+ return -EINVAL;
+
+ /* Determine an appropriate post divider. */
+ for (post_div_index = 0; post_div_index < ARRAY_SIZE(post_div_table);
+ post_div_index++) {
+ vco_out = pclk * pclk_div_table[pclk_div_index] *
+ post_div_table[post_div_index];
+
+ if (vco_out >= HM1246_PLL_VCO_MIN &&
+ vco_out <= HM1246_PLL_VCO_MAX)
+ break;
+ }
+ if (post_div_index >= ARRAY_SIZE(post_div_table))
+ return -EINVAL;
+
+ /* Find pre-divider and multiplier values. */
+ for (u32 div = DIV_ROUND_UP(xclk, HM1246_PLL_INCLK_MAX);
+ div <= xclk / HM1246_PLL_INCLK_MIN; div++) {
+ u32 multi, multi_h, multi_l, vco;
+
+ multi = DIV_ROUND_CLOSEST_ULL((u64)vco_out * div, xclk);
+ if (multi < HM1246_PLL_MULTI_MIN ||
+ multi > HM1246_PLL_MULTI_MAX)
+ continue;
+
+ multi_h = multi / (HM1246_PLL_MULTI_H_MIN *
+ HM1246_PLL_MULTI_L_MAX) +
+ 2;
+ multi_l = multi / multi_h;
+ vco = div_u64((u64)xclk * multi_h * multi_l, div);
+
+ if (vco != vco_out)
+ continue;
+
+ if (pll1 && pll2 && pll3) {
+ *pll1 = HM1246_PLL1CFG_MULTIPLIER(multi_l - 1);
+ *pll2 = HM1246_PLL2CFG_PRE_DIV(div - 1) |
+ HM1246_PLL2CFG_MULTIPLIER(multi_h - 2);
+ *pll3 = HM1246_PLL3CFG_POST_DIV(post_div_index) |
+ HM1246_PLL3CFG_SYSCLK_DIV(sysclk_div_index) |
+ HM1246_PLL3CFG_PCLK_DIV(pclk_div_index);
+ }
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int hm1246_cci_write_pll(struct hm1246 *hm1246, u8 pll1, u8 pll2,
+ u8 pll3)
+{
+ const struct cci_reg_sequence pll_regs[] = {
+ { HM1246_PLL1CFG_REG, pll1 },
+ { HM1246_PLL2CFG_REG, pll2 },
+ { HM1246_PLL3CFG_REG, pll3 },
+ { HM1246_SBC_CTRL_REG, HM1246_SBC_CTRL_PLL_EN },
+ };
+
+ return cci_multi_reg_write(hm1246->regmap, pll_regs,
+ ARRAY_SIZE(pll_regs), NULL);
+}
+
+static int hm1246_pll_check_locked(struct hm1246 *hm1246)
+{
+ u64 boot_ref2;
+ int ret;
+
+ ret = cci_read(hm1246->regmap, HM1246_SBC_BOOT_REF2_REG, &boot_ref2,
+ NULL);
+ if (ret)
+ return ret;
+
+ return (boot_ref2 & HM1246_SBC_BOOT_REF2_PLL_LOCK) ? 0 : -EIO;
+}
+
+static int hm1246_setup_pll(struct hm1246 *hm1246,
+ const struct hm1246_mode *mode)
+{
+ u8 pll1, pll2, pll3;
+ int ret;
+
+ ret = hm1246_calc_pll(hm1246->xclk_freq, hm1246->link_frequency,
+ mode->clocks_per_pixel, &pll1, &pll2, &pll3);
+ if (ret)
+ return ret;
+
+ ret = hm1246_cci_write_pll(hm1246, pll1, pll2, pll3);
+ if (ret)
+ return ret;
+
+ /* PLL lock time (tpll): 100us (typical) */
+ fsleep(200);
+
+ return hm1246_pll_check_locked(hm1246);
+}
+
+static int hm1246_cci_write_test_pattern(struct hm1246 *hm1246, u8 mode,
+ u16 r, u16 g, u16 b)
+{
+ const struct cci_reg_sequence tpg_enable_regs[] = {
+ { HM1246_TEST_DATA_RED_REG, r },
+ { HM1246_TEST_DATA_GR_REG, g },
+ { HM1246_TEST_DATA_GB_REG, g },
+ { HM1246_TEST_DATA_BLUE_REG, b },
+ { HM1246_TEST_PATTERN_MODE_REG, mode },
+ };
+
+ return cci_multi_reg_write(hm1246->regmap, tpg_enable_regs,
+ ARRAY_SIZE(tpg_enable_regs), NULL);
+}
+
+static int hm1246_test_pattern(struct hm1246 *hm1246, u32 index)
+{
+ static const u16 RGBMIN = 0, RGBMAX = 0x3ff;
+ static const struct tp {
+ int pattern;
+ u16 r, g, b;
+ } tps[] = {
+ /* Disabled */
+ [0] = { .pattern = 0, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
+ /* Checkboard pattern */
+ [1] = { .pattern = 0, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
+ /* Ramp */
+ [2] = { .pattern = 1, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
+ /* Moving ones */
+ [3] = { .pattern = 2, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
+ /* Blending color bars */
+ [4] = { .pattern = 3, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
+ /* Color bars */
+ [5] = { .pattern = 4, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
+ /* Solid white */
+ [6] = { .pattern = 15, .r = RGBMAX, .g = RGBMAX, .b = RGBMAX },
+ /* Solid black */
+ [7] = { .pattern = 15, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
+ /* Solid red */
+ [8] = { .pattern = 15, .r = RGBMAX, .g = RGBMIN, .b = RGBMIN },
+ /* Solid green */
+ [9] = { .pattern = 15, .r = RGBMIN, .g = RGBMAX, .b = RGBMIN },
+ /* Solid blue */
+ [10] = { .pattern = 15, .r = RGBMIN, .g = RGBMIN, .b = RGBMAX },
+ };
+ u8 mode;
+
+ if (index >= ARRAY_SIZE(tps))
+ return -EINVAL;
+
+ mode = HM1246_TEST_PATTERN_MODE_MODE(tps[index].pattern);
+ if (index)
+ mode |= HM1246_TEST_PATTERN_MODE_ENABLE;
+
+ return hm1246_cci_write_test_pattern(hm1246, mode, tps[index].r,
+ tps[index].g, tps[index].b);
+}
+
+static int hm1246_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct hm1246 *hm1246 =
+ container_of_const(ctrl->handler, struct hm1246, ctrls);
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *format;
+ u32 val;
+ bool needs_cmu_update = true;
+ int ret;
+
+ state = v4l2_subdev_get_locked_active_state(&hm1246->sd);
+ format = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ s64 exposure_max;
+
+ exposure_max =
+ format->height + ctrl->val - HM1246_COARSE_INTG_MARGIN;
+ ret = __v4l2_ctrl_modify_range(hm1246->exposure_ctrl,
+ hm1246->exposure_ctrl->minimum,
+ exposure_max,
+ hm1246->exposure_ctrl->step,
+ exposure_max);
+
+ if (ret) {
+ dev_err(hm1246->dev, "exposure ctrl range update failed\n");
+ return ret;
+ }
+ }
+
+ if (!pm_runtime_get_if_active(hm1246->dev))
+ return 0;
+
+ ret = 0;
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ cci_write(hm1246->regmap, HM1246_COARSE_INTG_REG, ctrl->val,
+ &ret);
+ break;
+
+ case V4L2_CID_ANALOGUE_GAIN:
+ cci_write(hm1246->regmap, HM1246_ANALOG_GLOBAL_GAIN_REG,
+ ctrl->val, &ret);
+ break;
+
+ case V4L2_CID_VBLANK:
+ val = format->height + ctrl->val;
+ cci_write(hm1246->regmap, HM1246_FRAME_LENGTH_LINES_REG, val,
+ &ret);
+ break;
+
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ val = 0;
+ if (hm1246->hflip_ctrl->val)
+ val |= HM1246_IMAGE_ORIENTATION_HFLIP;
+ if (hm1246->vflip_ctrl->val)
+ val |= HM1246_IMAGE_ORIENTATION_VFLIP;
+
+ cci_write(hm1246->regmap, HM1246_IMAGE_ORIENTATION_REG, val,
+ &ret);
+ break;
+
+ case V4L2_CID_TEST_PATTERN:
+ ret = hm1246_test_pattern(hm1246, ctrl->val);
+ needs_cmu_update = false;
+ break;
+
+ default:
+ ret = -EINVAL;
+ needs_cmu_update = false;
+ break;
+ }
+
+ if (needs_cmu_update)
+ cci_write(hm1246->regmap, HM1246_CMU_UPDATE_REG, 0, &ret);
+
+ pm_runtime_put(hm1246->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops hm1246_ctrl_ops = {
+ .s_ctrl = hm1246_set_ctrl,
+};
+
+static int hm1246_identify_module(struct hm1246 *hm1246)
+{
+ u64 model_id;
+ int ret;
+
+ if (hm1246->identified)
+ return 0;
+
+ ret = cci_read(hm1246->regmap, HM1246_MODEL_ID_REG, &model_id, NULL);
+ if (ret)
+ return ret;
+
+ if (model_id != HM1246_MODEL_ID) {
+ dev_err(hm1246->dev, "model id mismatch: 0x%llx!=0x%x\n",
+ model_id, HM1246_MODEL_ID);
+ return -ENXIO;
+ }
+
+ hm1246->identified = true;
+
+ return 0;
+}
+
+static int hm1246_setup_moderegs(struct hm1246 *hm1246,
+ const struct hm1246_mode *mode)
+{
+ const struct hm1246_reg_list *reg_list = &mode->reg_list;
+ const struct cci_reg_sequence modeaw[] = {
+ { HM1246_X_ADDR_START_REG, mode->rect.left },
+ { HM1246_Y_ADDR_START_REG, mode->rect.top },
+ { HM1246_X_ADDR_END_REG, mode->rect.width - 1 },
+ { HM1246_Y_ADDR_END_REG, mode->rect.height - 1 },
+ { HM1246_DWIN_XOFFSET_REG, mode->rect.left },
+ { HM1246_DWIN_YOFFSET_REG, mode->rect.top },
+ { HM1246_DWIN_XSIZE_REG, mode->rect.width },
+ { HM1246_DWIN_YSIZE_REG, mode->rect.height },
+ { HM1246_LINE_LENGTH_PCK_REG, mode->hts },
+ };
+ int ret = 0;
+
+ cci_multi_reg_write(hm1246->regmap, modeaw, ARRAY_SIZE(modeaw), &ret);
+ cci_multi_reg_write(hm1246->regmap, reg_list->regs,
+ reg_list->num_of_regs, &ret);
+
+ return ret;
+}
+
+static int hm1246_setup_bus(struct hm1246 *hm1246)
+{
+ u64 polarity_ctrl = 0, pclk_ctrl = 0;
+ int ret = 0;
+
+ if (hm1246->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
+ polarity_ctrl |= HM1246_POLARITY_CTRL_HSYNC;
+
+ if (hm1246->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
+ polarity_ctrl |= HM1246_POLARITY_CTRL_VSYNC;
+
+ cci_write(hm1246->regmap, HM1246_POLARITY_CTRL_REG, polarity_ctrl,
+ &ret);
+
+ /*
+ * If the clock output polarity flag PCLK_CTRL[3] is set (high), the
+ * data lines change state on the falling edge of PCLK and should
+ * therefore be sampled on the rising edge.
+ * This is different than described in the data sheet.
+ */
+ if (hm1246->mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
+ pclk_ctrl |= HM1246_PCLK_CTRL_POL;
+
+ cci_write(hm1246->regmap, HM1246_PCLK_CTRL_REG, pclk_ctrl, &ret);
+
+ return ret;
+}
+
+static int hm1246_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct hm1246 *hm1246 = to_hm1246(sd);
+ const struct v4l2_mbus_framefmt *format;
+ const struct hm1246_mode *mode;
+ int ret;
+
+ format = v4l2_subdev_state_get_format(state, 0);
+ mode = v4l2_find_nearest_size(hm1246_modes, ARRAY_SIZE(hm1246_modes),
+ rect.width, rect.height, format->width,
+ format->height);
+
+ ret = pm_runtime_resume_and_get(hm1246->dev);
+ if (ret)
+ return ret;
+
+ ret = hm1246_identify_module(hm1246);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = hm1246_setup_pll(hm1246, mode);
+ if (ret) {
+ dev_err(hm1246->dev, "failed to setup PLL\n");
+ goto err_rpm_put;
+ }
+
+ ret = hm1246_setup_moderegs(hm1246, mode);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = hm1246_setup_bus(hm1246);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = __v4l2_ctrl_handler_setup(&hm1246->ctrls);
+ if (ret) {
+ dev_err(hm1246->dev, "failed to setup v4l2 controls\n");
+ goto err_rpm_put;
+ }
+
+ ret = cci_write(hm1246->regmap, HM1246_MODE_SELECT_REG,
+ HM1246_MODE_SELECT_STREAM, NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ /*
+ * Since mirroring may change the actual pixel format, it must not be
+ * changed during streaming.
+ */
+ __v4l2_ctrl_grab(hm1246->vflip_ctrl, true);
+ __v4l2_ctrl_grab(hm1246->hflip_ctrl, true);
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put_autosuspend(hm1246->dev);
+
+ return ret;
+}
+
+static int hm1246_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct hm1246 *hm1246 = to_hm1246(sd);
+ int ret;
+
+ ret = cci_write(hm1246->regmap, HM1246_MODE_SELECT_REG,
+ HM1246_MODE_SELECT_STANDBY, NULL);
+
+ __v4l2_ctrl_grab(hm1246->vflip_ctrl, false);
+ __v4l2_ctrl_grab(hm1246->hflip_ctrl, false);
+
+ pm_runtime_put_autosuspend(hm1246->dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_video_ops hm1246_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops hm1246_subdev_pad_ops = {
+ .enum_mbus_code = hm1246_enum_mbus_code,
+ .enum_frame_size = hm1246_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = hm1246_set_format,
+ .get_selection = hm1246_get_selection,
+ .enable_streams = hm1246_enable_streams,
+ .disable_streams = hm1246_disable_streams,
+};
+
+static const struct v4l2_subdev_ops hm1246_subdev_ops = {
+ .video = &hm1246_video_ops,
+ .pad = &hm1246_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops hm1246_internal_ops = {
+ .init_state = hm1246_init_state,
+};
+
+static int hm1246_get_regulators(struct device *dev, struct hm1246 *hm1246)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(hm1246_supply_names); i++)
+ hm1246->supplies[i].supply = hm1246_supply_names[i];
+
+ return devm_regulator_bulk_get(dev, ARRAY_SIZE(hm1246_supply_names),
+ hm1246->supplies);
+}
+
+static int hm1246_parse_fwnode(struct hm1246 *hm1246)
+{
+ struct fwnode_handle *endpoint;
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_PARALLEL,
+ };
+ int ret;
+
+ endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(hm1246->dev),
+ 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
+ fwnode_handle_put(endpoint);
+ if (ret)
+ return dev_err_probe(hm1246->dev, ret,
+ "parsing endpoint node failed\n");
+
+ hm1246->mbus_flags = bus_cfg.bus.parallel.flags;
+
+ if (bus_cfg.nr_of_link_frequencies > 0)
+ hm1246->link_frequency = bus_cfg.link_frequencies[0];
+
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ if (!hm1246->link_frequency)
+ return dev_err_probe(hm1246->dev, -EINVAL,
+ "one link frequency expected\n");
+
+ return 0;
+}
+
+static int hm1246_init_controls(struct hm1246 *hm1246)
+{
+ const struct hm1246_mode *mode = &hm1246_modes[0];
+ struct v4l2_fwnode_device_properties props;
+ struct v4l2_ctrl_handler *ctrl_hdlr = &hm1246->ctrls;
+ struct v4l2_ctrl *ctrl;
+ s64 pixel_rate, exposure_max, vblank_min, hblank;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(hm1246->dev, &props);
+ if (ret)
+ return ret;
+
+ v4l2_ctrl_handler_init(ctrl_hdlr, 11);
+
+ hm1246->hflip_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (hm1246->hflip_ctrl)
+ hm1246->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ hm1246->vflip_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ if (hm1246->vflip_ctrl)
+ hm1246->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ v4l2_ctrl_cluster(2, &hm1246->hflip_ctrl);
+
+ ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ 0, 0,
+ &hm1246->link_frequency);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ pixel_rate = div_u64(hm1246->link_frequency, mode->clocks_per_pixel);
+ ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ pixel_rate, pixel_rate, 1,
+ pixel_rate);
+
+ vblank_min = mode->vts_min - mode->rect.height;
+ ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_min,
+ HM1246_VTS_MAX - mode->rect.height,
+ 1, vblank_min);
+
+ hblank = mode->hts - mode->rect.width;
+ ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_HBLANK, hblank, hblank,
+ 1, hblank);
+ if (ctrl)
+ ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
+ HM1246_ANALOG_GLOBAL_GAIN_MIN,
+ HM1246_ANALOG_GLOBAL_GAIN_MAX,
+ HM1246_ANALOG_GLOBAL_GAIN_STEP,
+ HM1246_ANALOG_GLOBAL_GAIN_MIN);
+
+ exposure_max = mode->vts_min - HM1246_COARSE_INTG_MARGIN;
+ hm1246->exposure_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ HM1246_COARSE_INTG_MIN,
+ exposure_max,
+ HM1246_COARSE_INTG_STEP,
+ exposure_max);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hm1246_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(hm1246_test_pattern_menu) - 1,
+ 0, 0, hm1246_test_pattern_menu);
+
+ v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &hm1246_ctrl_ops, &props);
+
+ if (ctrl_hdlr->error) {
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+ return ctrl_hdlr->error;
+ }
+
+ hm1246->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+}
+
+static int hm1246_probe(struct i2c_client *client)
+{
+ struct hm1246 *hm1246;
+ int ret;
+
+ hm1246 = devm_kzalloc(&client->dev, sizeof(*hm1246), GFP_KERNEL);
+ if (!hm1246)
+ return -ENOMEM;
+
+ hm1246->dev = &client->dev;
+
+ ret = hm1246_parse_fwnode(hm1246);
+ if (ret)
+ return ret;
+
+ hm1246->regmap = devm_cci_regmap_init_i2c(client, 16);
+ if (IS_ERR(hm1246->regmap))
+ return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->regmap),
+ "failed to init CCI\n");
+
+ hm1246->xclk = devm_v4l2_sensor_clk_get(hm1246->dev, NULL);
+ if (IS_ERR(hm1246->xclk))
+ return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->xclk),
+ "failed to get xclk\n");
+
+ hm1246->xclk_freq = clk_get_rate(hm1246->xclk);
+ if (hm1246->xclk_freq < HM1246_XCLK_MIN ||
+ hm1246->xclk_freq > HM1246_XCLK_MAX)
+ return dev_err_probe(hm1246->dev, -EINVAL,
+ "xclk frequency out of range: %luHz\n",
+ hm1246->xclk_freq);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(hm1246_modes); i++) {
+ ret = hm1246_calc_pll(hm1246->xclk_freq, hm1246->link_frequency,
+ hm1246_modes[i].clocks_per_pixel,
+ NULL, NULL, NULL);
+ if (ret)
+ return dev_err_probe(hm1246->dev, ret,
+ "no PLL setup for %lld Hz\n",
+ hm1246->link_frequency);
+ }
+
+ ret = hm1246_get_regulators(hm1246->dev, hm1246);
+ if (ret)
+ return dev_err_probe(hm1246->dev, ret,
+ "failed to get regulators\n");
+
+ hm1246->reset = devm_reset_control_get_optional(hm1246->dev, NULL);
+ if (IS_ERR(hm1246->reset))
+ return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->reset),
+ "failed to get reset\n");
+ reset_control_assert(hm1246->reset);
+
+ v4l2_i2c_subdev_init(&hm1246->sd, client, &hm1246_subdev_ops);
+ hm1246->sd.internal_ops = &hm1246_internal_ops;
+
+ ret = hm1246_init_controls(hm1246);
+ if (ret)
+ return dev_err_probe(hm1246->dev, ret,
+ "failed to init controls\n");
+
+ hm1246->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ hm1246->pad.flags = MEDIA_PAD_FL_SOURCE;
+ hm1246->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+
+ ret = media_entity_pads_init(&hm1246->sd.entity, 1, &hm1246->pad);
+ if (ret) {
+ dev_err_probe(hm1246->dev, ret, "failed to init media pads\n");
+ goto err_v4l2_ctrl_handler_free;
+ }
+
+ hm1246->sd.state_lock = hm1246->ctrls.lock;
+ ret = v4l2_subdev_init_finalize(&hm1246->sd);
+ if (ret) {
+ dev_err_probe(hm1246->dev, ret, "failed to init v4l2 subdev\n");
+ goto err_media_entity_cleanup;
+ }
+
+ pm_runtime_enable(hm1246->dev);
+ pm_runtime_set_autosuspend_delay(hm1246->dev, 1000);
+ pm_runtime_use_autosuspend(hm1246->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&hm1246->sd);
+ if (ret) {
+ dev_err_probe(hm1246->dev, ret,
+ "failed to register v4l2 subdev\n");
+ goto err_subdev_cleanup;
+ }
+
+ return 0;
+
+err_subdev_cleanup:
+ v4l2_subdev_cleanup(&hm1246->sd);
+ pm_runtime_disable(hm1246->dev);
+ pm_runtime_set_suspended(hm1246->dev);
+
+err_media_entity_cleanup:
+ media_entity_cleanup(&hm1246->sd.entity);
+
+err_v4l2_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&hm1246->ctrls);
+
+ return ret;
+}
+
+static void hm1246_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct hm1246 *hm1246 = to_hm1246(sd);
+
+ v4l2_async_unregister_subdev(&hm1246->sd);
+ v4l2_subdev_cleanup(sd);
+ media_entity_cleanup(&hm1246->sd.entity);
+ v4l2_ctrl_handler_free(&hm1246->ctrls);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ hm1246_power_off(hm1246->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
+}
+
+static const struct of_device_id hm1246_of_match[] = {
+ { .compatible = "himax,hm1246" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, hm1246_of_match);
+
+static DEFINE_RUNTIME_DEV_PM_OPS(hm1246_pm_ops,
+ hm1246_power_off, hm1246_power_on, NULL);
+
+static struct i2c_driver hm1246_i2c_driver = {
+ .driver = {
+ .of_match_table = hm1246_of_match,
+ .pm = pm_ptr(&hm1246_pm_ops),
+ .name = "hm1246",
+ },
+ .probe = hm1246_probe,
+ .remove = hm1246_remove,
+};
+module_i2c_driver(hm1246_i2c_driver);
+
+MODULE_DESCRIPTION("Himax HM1246 camera driver");
+MODULE_AUTHOR("Matthias Fend <matthias.fend@emfend.at>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/imx111.c b/drivers/media/i2c/imx111.c
index 8eb919788ef7..5b860fa052b1 100644
--- a/drivers/media/i2c/imx111.c
+++ b/drivers/media/i2c/imx111.c
@@ -1122,6 +1122,7 @@ static int imx111_enum_frame_size(struct v4l2_subdev *sd,
}
static int imx111_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/imx208.c b/drivers/media/i2c/imx208.c
index d5350bb46f14..3a899b41a9d2 100644
--- a/drivers/media/i2c/imx208.c
+++ b/drivers/media/i2c/imx208.c
@@ -574,6 +574,7 @@ static int imx208_get_pad_format(struct v4l2_subdev *sd,
}
static int imx208_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c
index d4945b192776..96833f2bf287 100644
--- a/drivers/media/i2c/imx214.c
+++ b/drivers/media/i2c/imx214.c
@@ -662,6 +662,7 @@ static const struct v4l2_subdev_core_ops imx214_core_ops = {
};
static int imx214_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -717,6 +718,7 @@ static int imx214_set_format(struct v4l2_subdev *sd,
}
static int imx214_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -754,7 +756,7 @@ static int imx214_entity_init_state(struct v4l2_subdev *subdev,
fmt.format.width = imx214_modes[0].width;
fmt.format.height = imx214_modes[0].height;
- imx214_set_format(subdev, sd_state, &fmt);
+ imx214_set_format(subdev, NULL, sd_state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/imx219.c b/drivers/media/i2c/imx219.c
index 9571f3622d2d..1580562093e8 100644
--- a/drivers/media/i2c/imx219.c
+++ b/drivers/media/i2c/imx219.c
@@ -78,6 +78,7 @@
#define IMX219_LLP_MIN 0x0d78
#define IMX219_BINNED_LLP_MIN 0x0de8
#define IMX219_LLP_MAX 0x7ff0
+#define IMX219_LLP_STEP 8
#define IMX219_REG_X_ADD_STA_A CCI_REG16(0x0164)
#define IMX219_REG_X_ADD_END_A CCI_REG16(0x0166)
@@ -340,13 +341,13 @@ static const struct imx219_mode supported_modes[] = {
/* 2x2 binned 60fps mode */
.width = 1640,
.height = 1232,
- .fll_def = 1707,
+ .fll_def = 1706,
},
{
/* 640x480 60fps mode */
.width = 640,
.height = 480,
- .fll_def = 1707,
+ .fll_def = 1706,
},
};
@@ -466,14 +467,14 @@ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
int exposure_max, exposure_def;
/* Update max exposure while meeting expected vblanking */
- exposure_max = format->height + ctrl->val - IMX219_EXPOSURE_OFFSET;
+ exposure_max = format->height + ctrl->val -
+ IMX219_EXPOSURE_OFFSET * rate_factor;
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
exposure_max : IMX219_EXPOSURE_DEFAULT;
ret = __v4l2_ctrl_modify_range(imx219->exposure,
imx219->exposure->minimum,
exposure_max,
- imx219->exposure->step,
- exposure_def);
+ rate_factor, exposure_def);
if (ret)
return ret;
@@ -593,7 +594,8 @@ static int imx219_init_controls(struct imx219 *imx219)
imx219->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
V4L2_CID_HBLANK,
IMX219_LLP_MIN - mode->width,
- IMX219_LLP_MAX - mode->width, 1,
+ IMX219_LLP_MAX - mode->width,
+ IMX219_LLP_STEP,
IMX219_LLP_MIN - mode->width);
exposure_max = mode->fll_def - IMX219_EXPOSURE_OFFSET;
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
@@ -844,6 +846,7 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd,
}
static int imx219_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -851,7 +854,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
const struct imx219_mode *mode;
struct v4l2_mbus_framefmt *format;
struct v4l2_rect *crop;
- u8 bin_h, bin_v, binning;
+ u8 bin_h, bin_v, bin_hv;
int ret;
format = v4l2_subdev_state_get_format(state, 0);
@@ -884,15 +887,16 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
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);
+ bin_hv = min(bin_h, bin_v);
crop = v4l2_subdev_state_get_crop(state, 0);
- crop->width = format->width * binning;
- crop->height = format->height * binning;
+ crop->width = format->width * bin_hv;
+ crop->height = format->height * bin_hv;
crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2;
crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2;
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ int rate_factor = imx219_get_rate_factor(state);
int exposure_max;
int exposure_def;
int llp_min;
@@ -900,7 +904,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
/* Update limits and set FPS to default */
ret = __v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
- IMX219_FLL_MAX - mode->height, 1,
+ IMX219_FLL_MAX - mode->height,
+ rate_factor,
mode->fll_def - mode->height);
if (ret)
return ret;
@@ -911,14 +916,14 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
return ret;
/* Update max exposure while meeting expected vblanking */
- exposure_max = mode->fll_def - IMX219_EXPOSURE_OFFSET;
+ exposure_max = mode->fll_def -
+ IMX219_EXPOSURE_OFFSET * rate_factor;
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
exposure_max : IMX219_EXPOSURE_DEFAULT;
ret = __v4l2_ctrl_modify_range(imx219->exposure,
imx219->exposure->minimum,
exposure_max,
- imx219->exposure->step,
- exposure_def);
+ rate_factor, exposure_def);
if (ret)
return ret;
@@ -933,7 +938,8 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
IMX219_BINNED_LLP_MIN : IMX219_LLP_MIN;
ret = __v4l2_ctrl_modify_range(imx219->hblank,
llp_min - mode->width,
- IMX219_LLP_MAX - mode->width, 1,
+ IMX219_LLP_MAX - mode->width,
+ IMX219_LLP_STEP,
llp_min - mode->width);
if (ret)
return ret;
@@ -943,8 +949,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
return ret;
/* Scale the pixel rate based on the mode specific factor */
- pixel_rate = imx219_get_pixel_rate(imx219) *
- imx219_get_rate_factor(state);
+ pixel_rate = imx219_get_pixel_rate(imx219) * rate_factor;
ret = __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate,
pixel_rate, 1, pixel_rate);
if (ret)
@@ -955,6 +960,7 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
}
static int imx219_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -997,7 +1003,7 @@ static int imx219_init_state(struct v4l2_subdev *sd,
},
};
- return imx219_set_pad_format(sd, state, &fmt);
+ return imx219_set_pad_format(sd, NULL, state, &fmt);
}
static const struct v4l2_subdev_video_ops imx219_video_ops = {
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index bc9ee449a87c..065c33380f60 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -913,6 +913,7 @@ static int imx258_get_pad_format(struct v4l2_subdev *sd,
}
static int imx258_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -988,6 +989,7 @@ __imx258_get_pad_crop(struct imx258 *imx258,
}
static int imx258_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c
index 76da647f9cf9..3245afc8ae5b 100644
--- a/drivers/media/i2c/imx274.c
+++ b/drivers/media/i2c/imx274.c
@@ -1059,6 +1059,7 @@ static int imx274_get_fmt(struct v4l2_subdev *sd,
}
static int imx274_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -1092,6 +1093,7 @@ out:
}
static int imx274_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1208,6 +1210,7 @@ static int imx274_set_selection_crop(struct stimx274 *imx274,
}
static int imx274_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c
index 38ea1902f2c2..e3cd27ca238e 100644
--- a/drivers/media/i2c/imx283.c
+++ b/drivers/media/i2c/imx283.c
@@ -958,6 +958,7 @@ static void imx283_set_framing_limits(struct imx283 *imx283,
}
static int imx283_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1260,6 +1261,7 @@ static int imx283_identify_module(struct imx283 *imx283)
}
static int imx283_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c
index 21cbc81cb2ed..d562a9aa455c 100644
--- a/drivers/media/i2c/imx290.c
+++ b/drivers/media/i2c/imx290.c
@@ -1149,6 +1149,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd,
}
static int imx290_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1187,6 +1188,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd,
}
static int imx290_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1246,7 +1248,7 @@ static int imx290_entity_init_state(struct v4l2_subdev *subdev,
},
};
- imx290_set_fmt(subdev, sd_state, &fmt);
+ imx290_set_fmt(subdev, NULL, sd_state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c
index 69636db11a2b..74bb295799fd 100644
--- a/drivers/media/i2c/imx296.c
+++ b/drivers/media/i2c/imx296.c
@@ -675,6 +675,7 @@ static int imx296_enum_frame_size(struct v4l2_subdev *sd,
}
static int imx296_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -726,6 +727,7 @@ static int imx296_set_format(struct v4l2_subdev *sd,
}
static int imx296_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -751,6 +753,7 @@ static int imx296_get_selection(struct v4l2_subdev *sd,
}
static int imx296_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -812,8 +815,8 @@ static int imx296_init_state(struct v4l2_subdev *sd,
},
};
- imx296_set_selection(sd, state, &sel);
- imx296_set_format(sd, state, &format);
+ imx296_set_selection(sd, NULL, state, &sel);
+ imx296_set_format(sd, NULL, state, &format);
return 0;
}
diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c
index 953310ef3046..281d39ed4194 100644
--- a/drivers/media/i2c/imx319.c
+++ b/drivers/media/i2c/imx319.c
@@ -2030,6 +2030,7 @@ static int imx319_get_pad_format(struct v4l2_subdev *sd,
static int
imx319_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c
index 553a16b84f4d..da19d5fc19f5 100644
--- a/drivers/media/i2c/imx334.c
+++ b/drivers/media/i2c/imx334.c
@@ -109,6 +109,7 @@
/* CSI2 HW configuration */
#define IMX334_LINK_FREQ_891M 891000000
#define IMX334_LINK_FREQ_445M 445500000
+#define IMX334_LINK_FREQ_222M 222750000
#define IMX334_NUM_DATA_LANES 4
#define IMX334_REG_MIN 0x00
@@ -154,7 +155,6 @@ struct imx334_reg_list {
* @vblank_min: Minimal vertical blanking in lines
* @vblank_max: Maximum vertical blanking in lines
* @pclk: Sensor pixel clock
- * @link_freq_idx: Link frequency index
* @reg_list: Register list for sensor mode
*/
struct imx334_mode {
@@ -165,7 +165,28 @@ struct imx334_mode {
u32 vblank_min;
u32 vblank_max;
u64 pclk;
- u32 link_freq_idx;
+ struct imx334_reg_list reg_list;
+};
+
+/**
+ * struct imx334_clk_params - imx334 sensor clock parameters
+ * @data_rate_per_lane: Data rate per lane in bits per second
+ * @link_freq: Link frequency in Hz
+ * @width_max: Maximum image width in pixels
+ * @height_max: Maximum image height in pixels
+ * @width_min: Minimum image width in pixels
+ * @height_min: Minimum image height in pixels
+ * @default_mode: Pointer to the default sensor mode
+ * @reg_list: Register list for clock configuration
+ */
+struct imx334_clk_params {
+ u32 data_rate_per_lane;
+ u32 link_freq;
+ u32 width_max;
+ u32 height_max;
+ u32 width_min;
+ u32 height_min;
+ const struct imx334_mode *default_mode;
struct imx334_reg_list reg_list;
};
@@ -216,6 +237,7 @@ struct imx334 {
static const s64 link_freq[] = {
IMX334_LINK_FREQ_891M,
IMX334_LINK_FREQ_445M,
+ IMX334_LINK_FREQ_222M,
};
/* Sensor common mode registers values */
@@ -233,13 +255,6 @@ static const struct cci_reg_sequence common_mode_regs[] = {
{ IMX334_REG_UNREAD_PARAM6, 0x0008 },
{ IMX334_REG_XVS_XHS_OUTSEL, 0x20 },
{ IMX334_REG_XVS_XHS_DRV, 0x0f },
- { IMX334_REG_BCWAIT_TIME, 0x3b },
- { IMX334_REG_CPWAIT_TIME, 0x2a },
- { IMX334_REG_INCKSEL1, 0x0129 },
- { IMX334_REG_INCKSEL2, 0x06 },
- { IMX334_REG_INCKSEL3, 0xa0 },
- { IMX334_REG_INCKSEL4, 0x7e },
- { IMX334_REG_SYS_MODE, 0x02 },
{ IMX334_REG_HADD_VADD, 0x00 },
{ IMX334_REG_VALID_EXPAND, 0x03 },
{ IMX334_REG_TCYCLE, 0x00 },
@@ -397,6 +412,39 @@ static const struct cci_reg_sequence mode_3840x2160_regs[] = {
{ IMX334_REG_TPLX, 0x005f },
};
+/* Data rate 1782Mbps per lane and 891Mhz link frequency */
+static const struct cci_reg_sequence link_freq_891m_regs[] = {
+ { IMX334_REG_BCWAIT_TIME, 0x3b },
+ { IMX334_REG_CPWAIT_TIME, 0x2a },
+ { IMX334_REG_INCKSEL1, 0x0129 },
+ { IMX334_REG_INCKSEL2, 0x02 },
+ { IMX334_REG_INCKSEL3, 0xa0 },
+ { IMX334_REG_INCKSEL4, 0x7e },
+ { IMX334_REG_SYS_MODE, 0x00 },
+};
+
+/* Data rate 891Mbps per lane and 445Mhz link frequency */
+static const struct cci_reg_sequence link_freq_445m_regs[] = {
+ { IMX334_REG_BCWAIT_TIME, 0x3b },
+ { IMX334_REG_CPWAIT_TIME, 0x2a },
+ { IMX334_REG_INCKSEL1, 0x0129 },
+ { IMX334_REG_INCKSEL2, 0x06 },
+ { IMX334_REG_INCKSEL3, 0xa0 },
+ { IMX334_REG_INCKSEL4, 0x7e },
+ { IMX334_REG_SYS_MODE, 0x02 },
+};
+
+/* Data rate 445Mbps per lane and 222Mhz link frequency */
+static const struct cci_reg_sequence link_freq_222m_regs[] = {
+ { IMX334_REG_BCWAIT_TIME, 0x3b },
+ { IMX334_REG_CPWAIT_TIME, 0x2a },
+ { IMX334_REG_INCKSEL1, 0x0129 },
+ { IMX334_REG_INCKSEL2, 0x0a },
+ { IMX334_REG_INCKSEL3, 0xa0 },
+ { IMX334_REG_INCKSEL4, 0x7e },
+ { IMX334_REG_SYS_MODE, 0x02 },
+};
+
static const char * const imx334_test_pattern_menu[] = {
"Disabled",
"Vertical Color Bars",
@@ -442,7 +490,6 @@ static const struct imx334_mode supported_modes[] = {
.vblank_min = 90,
.vblank_max = 132840,
.pclk = 594000000,
- .link_freq_idx = 0,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_3840x2160_regs),
.regs = mode_3840x2160_regs,
@@ -455,7 +502,6 @@ static const struct imx334_mode supported_modes[] = {
.vblank_min = 45,
.vblank_max = 132840,
.pclk = 297000000,
- .link_freq_idx = 1,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1920x1080_regs),
.regs = mode_1920x1080_regs,
@@ -468,7 +514,6 @@ static const struct imx334_mode supported_modes[] = {
.vblank_min = 45,
.vblank_max = 132840,
.pclk = 297000000,
- .link_freq_idx = 1,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1280x720_regs),
.regs = mode_1280x720_regs,
@@ -481,7 +526,6 @@ static const struct imx334_mode supported_modes[] = {
.vblank_min = 45,
.vblank_max = 132840,
.pclk = 297000000,
- .link_freq_idx = 1,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_640x480_regs),
.regs = mode_640x480_regs,
@@ -489,6 +533,46 @@ static const struct imx334_mode supported_modes[] = {
},
};
+static const struct imx334_clk_params imx334_clk_params[] = {
+ {
+ .data_rate_per_lane = 1782000000,
+ .link_freq = IMX334_LINK_FREQ_891M,
+ .width_max = 3840,
+ .height_max = 2160,
+ .width_min = 3840,
+ .height_min = 2160,
+ .default_mode = &supported_modes[0], /* 3840x2160 */
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(link_freq_891m_regs),
+ .regs = link_freq_891m_regs,
+ },
+ }, {
+ .data_rate_per_lane = 891000000,
+ .link_freq = IMX334_LINK_FREQ_445M,
+ .width_max = 1920,
+ .height_max = 1080,
+ .width_min = 640,
+ .height_min = 480,
+ .default_mode = &supported_modes[1], /* 1920x1080 */
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(link_freq_445m_regs),
+ .regs = link_freq_445m_regs,
+ },
+ }, {
+ .data_rate_per_lane = 445500000,
+ .link_freq = IMX334_LINK_FREQ_222M,
+ .width_max = 1920,
+ .height_max = 1080,
+ .width_min = 640,
+ .height_min = 480,
+ .default_mode = &supported_modes[1], /* 1920x1080 */
+ .reg_list = {
+ .num_of_regs = ARRAY_SIZE(link_freq_222m_regs),
+ .regs = link_freq_222m_regs,
+ },
+ }
+};
+
/**
* to_imx334() - imv334 V4L2 sub-device to imx334 device.
* @subdev: pointer to imx334 V4L2 sub-device
@@ -512,10 +596,6 @@ static int imx334_update_controls(struct imx334 *imx334,
{
int ret;
- ret = __v4l2_ctrl_s_ctrl(imx334->link_freq_ctrl, mode->link_freq_idx);
- if (ret)
- return ret;
-
ret = __v4l2_ctrl_modify_range(imx334->pclk_ctrl, mode->pclk,
mode->pclk, 1, mode->pclk);
if (ret)
@@ -741,11 +821,13 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd,
}
static int imx334_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct imx334 *imx334 = to_imx334(sd);
const struct imx334_mode *mode;
+ const struct imx334_clk_params *clk_params;
int ret = 0;
mode = v4l2_find_nearest_size(supported_modes,
@@ -753,6 +835,11 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd,
width, height,
fmt->format.width, fmt->format.height);
+ clk_params = &imx334_clk_params[imx334->link_freq_ctrl->val];
+ if (mode->width > clk_params->width_max || mode->height > clk_params->height_max ||
+ mode->width < clk_params->width_min || mode->height < clk_params->height_min)
+ mode = clk_params->default_mode;
+
imx334_fill_pad_format(imx334, mode, fmt);
fmt->format.code = imx334_get_format_code(imx334, fmt->format.code);
@@ -786,7 +873,7 @@ static int imx334_init_state(struct v4l2_subdev *sd,
~(imx334->link_freq_bitmap),
__ffs(imx334->link_freq_bitmap));
- return imx334_set_pad_format(sd, sd_state, &fmt);
+ return imx334_set_pad_format(sd, NULL, sd_state, &fmt);
}
static int imx334_set_framefmt(struct imx334 *imx334)
@@ -824,6 +911,15 @@ static int imx334_enable_streams(struct v4l2_subdev *sd,
goto err_rpm_put;
}
+ /* Write sensor link freq registers */
+ reg_list = &imx334_clk_params[imx334->link_freq_ctrl->val].reg_list;
+ ret = cci_multi_reg_write(imx334->cci, reg_list->regs,
+ reg_list->num_of_regs, NULL);
+ if (ret) {
+ dev_err(imx334->dev, "fail to write initial registers\n");
+ goto err_rpm_put;
+ }
+
/* Write sensor mode registers */
reg_list = &imx334->cur_mode->reg_list;
ret = cci_multi_reg_write(imx334->cci, reg_list->regs,
@@ -1096,9 +1192,6 @@ static int imx334_init_controls(struct imx334 *imx334)
__ffs(imx334->link_freq_bitmap),
link_freq);
- if (imx334->link_freq_ctrl)
- imx334->link_freq_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-
imx334->hblank_ctrl = v4l2_ctrl_new_std(ctrl_hdlr,
&imx334_ctrl_ops,
V4L2_CID_HBLANK,
diff --git a/drivers/media/i2c/imx335.c b/drivers/media/i2c/imx335.c
index 1f777a1a8192..8f29d9f2da83 100644
--- a/drivers/media/i2c/imx335.c
+++ b/drivers/media/i2c/imx335.c
@@ -844,6 +844,7 @@ static void imx335_fill_pad_format(struct imx335 *imx335,
}
static int imx335_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -901,10 +902,11 @@ static int imx335_init_state(struct v4l2_subdev *sd,
~(imx335->link_freq_bitmap),
__ffs(imx335->link_freq_bitmap));
- return imx335_set_pad_format(sd, sd_state, &fmt);
+ return imx335_set_pad_format(sd, NULL, sd_state, &fmt);
}
static int imx335_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c
index 8eb8588cb71b..45e3002bd93c 100644
--- a/drivers/media/i2c/imx355.c
+++ b/drivers/media/i2c/imx355.c
@@ -714,6 +714,7 @@ static void imx355_update_pad_format(struct imx355 *imx355,
static int
imx355_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -765,6 +766,7 @@ imx355_set_pad_format(struct v4l2_subdev *sd,
}
static int imx355_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -796,7 +798,7 @@ static int imx355_entity_init_state(struct v4l2_subdev *subdev,
fmt.format.width = supported_modes[0].width;
fmt.format.height = supported_modes[0].height;
- imx355_set_pad_format(subdev, sd_state, &fmt);
+ imx355_set_pad_format(subdev, NULL, sd_state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/imx412.c b/drivers/media/i2c/imx412.c
index d2760e9e6194..4628e2eda231 100644
--- a/drivers/media/i2c/imx412.c
+++ b/drivers/media/i2c/imx412.c
@@ -588,6 +588,7 @@ static int imx412_get_pad_format(struct v4l2_subdev *sd,
}
static int imx412_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -621,7 +622,7 @@ static int imx412_init_state(struct v4l2_subdev *sd,
fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE;
imx412_fill_pad_format(imx412, &supported_mode, &fmt);
- return imx412_set_pad_format(sd, sd_state, &fmt);
+ return imx412_set_pad_format(sd, NULL, sd_state, &fmt);
}
/**
diff --git a/drivers/media/i2c/imx415.c b/drivers/media/i2c/imx415.c
index c3b22b238ea0..2571718cffff 100644
--- a/drivers/media/i2c/imx415.c
+++ b/drivers/media/i2c/imx415.c
@@ -1021,6 +1021,7 @@ static int imx415_enum_frame_size(struct v4l2_subdev *sd,
}
static int imx415_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -1042,6 +1043,7 @@ static int imx415_set_format(struct v4l2_subdev *sd,
}
static int imx415_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1070,7 +1072,7 @@ static int imx415_init_state(struct v4l2_subdev *sd,
},
};
- imx415_set_format(sd, state, &format);
+ imx415_set_format(sd, NULL, state, &format);
return 0;
}
diff --git a/drivers/media/i2c/imx471.c b/drivers/media/i2c/imx471.c
index 4053aed84340..03173197f332 100644
--- a/drivers/media/i2c/imx471.c
+++ b/drivers/media/i2c/imx471.c
@@ -41,7 +41,7 @@
/* Analog gain control */
#define IMX471_REG_ANALOG_GAIN CCI_REG16(0x0204)
#define IMX471_ANA_GAIN_MIN 0
-#define IMX471_ANA_GAIN_MAX 800
+#define IMX471_ANA_GAIN_MAX 960
#define IMX471_ANA_GAIN_STEP 1
#define IMX471_ANA_GAIN_DEFAULT 0
@@ -416,6 +416,7 @@ static void imx471_update_pad_format(struct imx471 *sensor,
}
static int imx471_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -455,6 +456,7 @@ static int imx471_set_pad_format(struct v4l2_subdev *sd,
}
static int imx471_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -494,7 +496,7 @@ static int imx471_init_state(struct v4l2_subdev *sd,
},
};
- return imx471_set_pad_format(sd, sd_state, &fmt);
+ return imx471_set_pad_format(sd, NULL, sd_state, &fmt);
}
static int imx471_identify_module(struct imx471 *sensor)
diff --git a/drivers/media/i2c/imx678.c b/drivers/media/i2c/imx678.c
index 0efbf43d2fe6..8413fd44d657 100644
--- a/drivers/media/i2c/imx678.c
+++ b/drivers/media/i2c/imx678.c
@@ -849,6 +849,7 @@ static int imx678_enum_frame_size(struct v4l2_subdev *sd,
}
static int imx678_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1130,7 +1131,6 @@ static const struct v4l2_subdev_video_ops imx678_video_ops = {
static const struct v4l2_subdev_pad_ops imx678_pad_ops = {
.enum_mbus_code = imx678_enum_mbus_code,
.get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = v4l2_subdev_get_fmt,
.get_selection = imx678_get_selection,
.enum_frame_size = imx678_enum_frame_size,
.enable_streams = imx678_enable_streams,
diff --git a/drivers/media/i2c/isl7998x.c b/drivers/media/i2c/isl7998x.c
index 8244d4296a02..adb49eb62642 100644
--- a/drivers/media/i2c/isl7998x.c
+++ b/drivers/media/i2c/isl7998x.c
@@ -1027,6 +1027,7 @@ out:
}
static int isl7998x_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/it6625.c b/drivers/media/i2c/it6625.c
new file mode 100644
index 000000000000..25f9e61a7b03
--- /dev/null
+++ b/drivers/media/i2c/it6625.c
@@ -0,0 +1,2391 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * it6625 - ite HDMI to MIPI bridge
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/hdmi.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/v4l2-dv-timings.h>
+#include <linux/videodev2.h>
+#include <linux/workqueue.h>
+
+#include <media/cec.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <uapi/linux/it6625.h>
+
+static int debug = 3;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-3)");
+
+#define REG_CHIP_ID_0 0x00
+#define REG_CHIP_ID_1 0x01
+#define REG_FW_VER_MAJOR 0x03
+#define REG_FW_VER_MINOR 0x04
+#define REG_PROTOCOL_VERSION 0x05
+#define B_PVER_MINOR BIT(0)
+#define B_PVER_MAJOR BIT(4)
+
+#define REG_CMD_SET 0x10
+#define CMD_SET_CEC_LA 0xC0
+#define CMD_SET_CEC_ENABLE 0xC1
+
+#define REG_EDID_START 0x20
+#define REG_CEC_RX_DATA 0x20
+#define REG_CEC_TX_DATA 0x30
+
+#define REG_H_ACTIVE_1 0x50
+#define REG_H_ACTIVE_0 0x51
+#define REG_V_ACTIVE_1 0x52
+#define REG_V_ACTIVE_0 0x53
+#define REG_H_TOTAL_1 0x54
+#define REG_H_TOTAL_0 0x55
+#define REG_V_TOTAL_1 0x56
+#define REG_V_TOTAL_0 0x57
+#define REG_VID_PCLK 0x58
+#define REG_H_FP_1 0x5C
+#define REG_H_FP_0 0x5D
+#define REG_H_SW_1 0x5E
+#define REG_H_SW_0 0x5F
+#define REG_H_BP_1 0x60
+#define REG_H_BP_0 0x61
+#define REG_V_FP_1 0x62
+#define REG_V_FP_0 0x63
+#define REG_V_SW_1 0x64
+#define REG_V_SW_0 0x65
+#define REG_V_BP_1 0x66
+#define REG_V_BP_0 0x67
+#define REG_VID_INFO 0x68
+#define B_INTERLACE BIT(0)
+#define B_HSWPOL BIT(1)
+#define B_VSWPOL BIT(2)
+#define B_PIXEL_REP BIT(4)
+
+#define REG_VIC 0x69
+#define REG_HDMI_VIDEO_INFO 0x6A
+#define B_COLOR_MODE BIT(0)
+#define B_COLOR_DEPTH BIT(4)
+#define REG_HDMI_AUDIO_INFO1 0x6B
+#define B_AUD_FS BIT(0)
+
+#define REG_HDMI_AUDIO_INFO2 0x6C
+#define B_AUD_CH BIT(0)
+#define B_AUD_WL BIT(4)
+
+#define REG_HDMI_AUDIO_INFO3 0x6D
+#define B_AUD_TYPE BIT(0)
+#define B_AUD_MS BIT(2)
+#define B_AUD_3D BIT(3)
+
+#define REG_AUDIO_FMT 0x6E
+#define B_I2S_WL BIT(0)
+#define B_I2S_ALN BIT(2)
+#define B_I2S_DLY BIT(3)
+#define B_I2S_LR BIT(4)
+#define B_I2S_SFT BIT(5)
+#define B_AUD_OUT_IF BIT(6)
+
+#define REG_IF_LATCH_HB 0x6F
+#define REG_IF_DATA 0x70
+#define REG_EMP_DATA 0xA0
+#define REG_AVI_DATA 0xAE
+
+#define REG_TX_STATUS 0xE0
+
+#define REG_RX_STATUS 0xE2
+#define B_RX_5V BIT(0)
+#define B_RX_HPD BIT(1)
+#define B_RX_STABLE BIT(2)
+#define B_RX_HDMI BIT(3)
+#define B_RX_AVMUTE BIT(4)
+#define B_RX_AUD_ON BIT(5)
+
+#define REG_RX_HDCP_STS 0xE3
+#define B_HDCP1_AUTH_START BIT(0)
+#define B_HDCP2_AUTH_START BIT(1)
+#define B_HDCP_AUTH_DONE BIT(2)
+#define B_HDCP_ENC BIT(3)
+
+#define REG_CEC_STATUS 0xE4
+#define B_CEC_TX_DONE BIT(0)
+#define B_CEC_TX_NACK BIT(1)
+
+#define REG_CEC_RX_DATA_LEN 0xE5
+#define REG_CEC_TX_DATA_LEN 0xE6
+
+#define REG_SYS_MIPI_INT 0xEB
+#define B_MIPI_OUTPUT_ENABLE BIT(0)
+#define B_MIPI_VIDEO_UNSTABLE BIT(1)
+
+#define REG_RX_INT_STATUS1 0xEC
+#define B_HDMI_5V_CHG BIT(0)
+#define B_HDMI_VID_CHG BIT(1)
+#define B_HDMI_AUD_CHG BIT(2)
+#define B_HDMI_CP_CHG BIT(3)
+#define B_HDMI_IF_LATCH BIT(4)
+#define B_HDMI_NO_IF_LATCH BIT(5)
+#define B_HDMI_EMP BIT(6)
+#define B_HDMI_NO_EMP BIT(7)
+
+#define REG_RX_INT_STATUS2 0xED
+#define B_HDMI_AVI BIT(0)
+#define B_HDMI_NO_AVI BIT(1)
+#define B_HDMI_VSIF BIT(2)
+#define B_HDMI_NO_VSIF BIT(3)
+#define B_HDMI_AVMUTE_CHG BIT(4)
+
+#define REG_CHIP_CONTROL 0xF0
+#define B_HDMI_RESET BIT(5)
+#define B_FW_START BIT(6)
+
+#define REG_MIPI_CFG 0xF1
+#define M_MIPI_LANE 0x03
+#define B_MIPI_SPLIT BIT(2)
+#define B_MIPI_SPLIT_CFG BIT(3)
+#define B_MIPI_DPHY BIT(4)
+#define B_MIPI_USE_DSI BIT(5)
+#define B_MIPI_CONTINU_CLK BIT(6)
+
+#define REG_MIPI_DATA_TYPE 0xF2
+#define CSI_RGB444 0x20
+#define CSI_RGB555 0x21
+#define CSI_RGB565 0x22
+#define CSI_RGB666 0x23
+#define CSI_RGB888 0x24
+#define CSI_YUV420_8b_L 0x1A
+#define CSI_YUV420_8b 0x1C
+#define CSI_YUV420_10b 0x1D
+#define CSI_YUV422_8b 0x1E
+#define CSI_YUV422_10b 0x1F
+#define CSI_RGB_10b 0x30
+#define CSI_RGB_12b 0x31
+#define CSI_YUV422_12b 0x32
+#define CSI_YUV420_10b_L 0x33
+#define CSI_YUV420_12b 0x34
+#define CSI_YUV444_8b 0x35
+#define CSI_YUV444_10b 0x36
+#define CSI_YUV444_12b 0x37
+
+#define REG_MIPI_CONTROL 0xF3
+#define B_MIPI_OUTPUT BIT(0)
+
+#define REG_RX_CFG 0xF4
+#define B_MANUAL_HPD BIT(0)
+#define B_HPD_HIGH BIT(1)
+#define B_HPD_TOGGLE BIT(3)
+
+#define REG_CSC_CFG 0xF5
+#define B_DYNAMIC_RANGE BIT(0)
+
+#define REG_MISC_CFG 0xF6
+#define B_BAUD_RATE BIT(0)
+#define B_DEBUG_MSG BIT(1)
+
+#define REG_HPD_DELAY 0xF7
+#define B_DELAY_COUNT BIT(0)
+#define B_DELAY_UNIT BIT(7)
+
+#define REG_INFO_BANK_SEL 0xFD
+#define CTL_BANK_EDID_READ 1
+#define CTL_BANK_EDID_WRITE 5
+
+#define REG_HOST_CTRL_INT 0xFE
+#define B_CMD_SET BIT(4)
+#define B_CEC_SEND_DATA BIT(5)
+#define B_CONFIG_UPDATE BIT(6)
+#define B_IF_BANK BIT(7)
+
+#define REG_MCU_INTERRUPT 0xFF
+#define B_SYS_INT_ACTIVE BIT(0)
+#define B_CEC_RX_RECEIVED BIT(1)
+#define B_CEC_TX_UPDATE BIT(2)
+
+#define EDID_NUM_BLOCKS_MAX 4
+#define EDID_BLOCK_SIZE 128
+
+#define I2C_MAX_XFER_SIZE 8
+#define POLL_INTERVAL_CEC_MS 10
+#define POLL_INTERVAL_MS 40
+
+#define AUD32K 0x03
+#define AUD44K 0x00
+#define AUD48K 0x02
+#define AUD64K 0x0B
+#define AUD88K 0x08
+#define AUD96K 0x0A
+#define AUD128K 0x2B
+#define AUD176K 0x0C
+#define AUD192K 0x0E
+#define AUD256K 0x1B
+#define AUD352K 0x0D
+#define AUD384K 0x05
+#define AUD512K 0x3B
+#define AUD705K 0x2D
+#define AUD768K 0x09
+#define AUD1024K 0x35
+#define AUD1411K 0x1D
+#define AUD1536K 0x15
+
+enum it6625_chip_type {
+ IT6625_CHIP = 0,
+ IT6626_CHIP = 1,
+};
+
+struct it6625 {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct regmap *it6625_regmap;
+ enum it6625_chip_type chip_type;
+
+ /* protects concurrent access to the chip's registers and state */
+ struct mutex it6625_lock;
+ /* serializes the complete VIDIOC_S_EDID sequence against itself */
+ struct mutex edid_lock;
+ /* serializes a full InfoFrame debugfs read transaction against itself */
+ struct mutex if_read_lock;
+ /*
+ * protects if_active/if_type/if_snapshot/if_snapshot_err/
+ * if_snapshot_done and the REG_MCU_INTERRUPT/REG_RX_INT_STATUS1/2/
+ * REG_IF_LATCH_HB/REG_IF_DATA register group between the interrupt
+ * path and the InfoFrame debugfs callback. Never held across
+ * wait_for_completion_timeout(). Nests outside it6625_lock.
+ */
+ struct mutex if_state_lock;
+
+ struct v4l2_subdev sd;
+ struct v4l2_mbus_config_mipi_csi2 bus;
+ struct video_device *vdev;
+ struct media_pad pad;
+ struct v4l2_ctrl_handler hdl;
+
+ /* controls */
+ struct v4l2_ctrl *ctrl_5v_detect;
+ struct v4l2_ctrl *ctrl_audio_sampling_rate;
+ struct v4l2_ctrl *ctrl_audio_present;
+ struct v4l2_ctrl *ctrl_link_freq;
+
+ struct delayed_work hpd_delayed_work;
+
+ struct timer_list timer;
+ struct work_struct polling_work;
+
+ struct v4l2_dv_timings timings;
+
+ u8 csi_lanes;
+ u8 port_num;
+ enum v4l2_mbus_type bus_type;
+ u8 csi_format;
+ u32 mbus_fmt_code;
+ /* number of EDID blocks currently loaded, protected by edid_lock */
+ u8 edid_blocks;
+
+ struct gpio_desc *reset_gpio;
+
+ struct cec_adapter *cec_adap;
+
+ struct dentry *debugfs_dir;
+
+ struct v4l2_debugfs_if *infoframes;
+ struct completion if_latched;
+ /* true while an InfoFrame debugfs request is outstanding */
+ bool if_active;
+ /* HDMI packet-type byte currently armed in REG_IF_LATCH_HB */
+ u32 if_type;
+ /* driver-private copy of REG_IF_DATA, captured at the genuine latch */
+ u8 if_snapshot[31];
+ /* it6625_read_bytes() result for the if_snapshot capture */
+ int if_snapshot_err;
+ /* set just before complete(), to disambiguate timeout vs. capture */
+ bool if_snapshot_done;
+};
+
+/*
+ * Index 0: D-PHY (4-lane). Index 1: C-PHY (3-trio) -- the confirmed
+ * hardware max C-PHY capability, tested single-port/three-trio.
+ */
+static const s64 it6625_link_freq[] = {
+ 445500000,
+ 2500000000LL,
+};
+
+/*
+ * Shared cap for every topology except the reference exception below:
+ * IT6625 (no C-PHY support at all), IT6626 running D-PHY, and IT6626
+ * running C-PHY with fewer than three trios. This is conservative
+ * scoping, not a claim that one-/two-trio C-PHY can't also support a
+ * higher rate -- they're simply unvalidated.
+ */
+static const struct v4l2_dv_timings_cap it6625_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ /* keep this initialization for compatibility with GCC < 4.4.6 */
+ .reserved = { 0 },
+
+ V4L2_INIT_BT_TIMINGS(640, 3840, 480, 2160, 27000000, 300000000,
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
+ V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED |
+ V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM)
+};
+
+/*
+ * IT6626 C-PHY, three trios: raised pixel-clock ceiling (594 MHz vs
+ * the shared 300 MHz cap) for this topology's higher C-PHY capability.
+ */
+static const struct v4l2_dv_timings_cap it6626_cphy_3trio_timings_cap = {
+ .type = V4L2_DV_BT_656_1120,
+ .reserved = { 0 },
+
+ V4L2_INIT_BT_TIMINGS(640, 3840, 480, 2160, 27000000, 594000000,
+ V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
+ V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED |
+ V4L2_DV_BT_CAP_REDUCED_BLANKING | V4L2_DV_BT_CAP_CUSTOM)
+};
+
+static const struct v4l2_dv_timings_cap *
+it6625_get_timings_cap(struct it6625 *it6625)
+{
+ if (it6625->chip_type == IT6626_CHIP &&
+ it6625->bus_type == V4L2_MBUS_CSI2_CPHY &&
+ it6625->csi_lanes == 3)
+ return &it6626_cphy_3trio_timings_cap;
+
+ return &it6625_timings_cap;
+}
+
+static const struct it6625_format_info {
+ u8 csi_format;
+ u32 mbus_fmt_code;
+} it6625_formats[] = {
+ { CSI_YUV422_8b, MEDIA_BUS_FMT_UYVY8_1X16 },
+ { CSI_RGB888, MEDIA_BUS_FMT_RGB888_1X24 },
+ { CSI_YUV444_8b, MEDIA_BUS_FMT_YUV8_1X24 },
+};
+
+static inline int it6625_csi_format_idx(u8 csi_format)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(it6625_formats); i++) {
+ if (it6625_formats[i].csi_format == csi_format)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static inline int it6625_csi_mbus_code_idx(u32 mbus_fmt_code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(it6625_formats); i++) {
+ if (it6625_formats[i].mbus_fmt_code == mbus_fmt_code)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static inline struct it6625 *sd_to_6625(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct it6625, sd);
+}
+
+static const struct regmap_config it6625_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .cache_type = REGCACHE_NONE,
+ .max_raw_read = I2C_MAX_XFER_SIZE,
+ .max_raw_write = I2C_MAX_XFER_SIZE,
+};
+
+static int it6625_regmap_i2c_init(struct i2c_client *client,
+ struct it6625 *it6625)
+{
+ it6625->i2c_client = client;
+ it6625->dev = &client->dev;
+
+ it6625->it6625_regmap = devm_regmap_init_i2c(it6625->i2c_client,
+ &it6625_regmap_config);
+ if (IS_ERR(it6625->it6625_regmap))
+ return PTR_ERR(it6625->it6625_regmap);
+
+ return 0;
+}
+
+static int it6625_read_byte(struct it6625 *it6625, u8 reg)
+{
+ unsigned int val;
+ int err;
+ struct device *dev = it6625->dev;
+
+ err = regmap_read(it6625->it6625_regmap, reg, &val);
+ if (err < 0) {
+ dev_err(dev, "reg[0x%x] read failed err: %d", reg, err);
+ return err;
+ }
+
+ return val;
+}
+
+static int it6625_write_byte(struct it6625 *it6625, u8 reg, u8 val)
+{
+ int err;
+ struct device *dev = it6625->dev;
+
+ err = regmap_write(it6625->it6625_regmap, reg, val);
+ if (err < 0) {
+ dev_err(dev, "reg[0x%x] write failed err: %d", reg, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int it6625_set_bits(struct it6625 *it6625, u8 reg, u8 mask, u8 val)
+{
+ int err;
+ struct device *dev = it6625->dev;
+
+ err = regmap_update_bits(it6625->it6625_regmap, reg, mask, val);
+ if (err < 0) {
+ dev_err(dev, "reg[0x%x] set bits failed err: %d", reg, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int it6625_read_bytes(struct it6625 *it6625, u8 reg, u8 *buf, int len)
+{
+ int err;
+ struct device *dev = it6625->dev;
+
+ err = regmap_bulk_read(it6625->it6625_regmap, reg, buf, len);
+ if (err < 0) {
+ dev_err(dev, "reg[0x%x] read failed err: %d", reg, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int it6625_write_bytes(struct it6625 *it6625, u8 reg, u8 *buf, int len)
+{
+ int err;
+ struct device *dev = it6625->dev;
+
+ err = regmap_bulk_write(it6625->it6625_regmap, reg, buf, len);
+ if (err < 0) {
+ dev_err(dev, "reg[0x%x] write failed err: %d", reg, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int it6625_wait_for_status(struct it6625 *it6625, u8 reg, u8 val,
+ int timeout_ms)
+{
+ struct device *dev = it6625->dev;
+ int status;
+ int rval;
+ int sleep_ms = 10;
+ int timeout_round_ms = DIV_ROUND_UP(timeout_ms, sleep_ms) * sleep_ms;
+
+ status = read_poll_timeout(it6625_read_byte, rval, rval == val,
+ sleep_ms * 1000,
+ timeout_round_ms * 1000,
+ false, it6625, reg);
+
+ dev_info(dev, "%s status = %d %d", __func__, status, (int)rval);
+ if (status < 0) {
+ dev_err(dev, "%s err status = %d", __func__, status);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void it6625_write_command(struct it6625 *it6625, u8 *cmds, int cmd_len)
+{
+ it6625_write_bytes(it6625, REG_CMD_SET, cmds, cmd_len);
+ it6625_write_byte(it6625, REG_HOST_CTRL_INT, B_CMD_SET);
+ it6625_wait_for_status(it6625, REG_HOST_CTRL_INT, 0x00, 25);
+}
+
+static int it6625_update_config(struct it6625 *it6625)
+{
+ int err;
+
+ err = it6625_set_bits(it6625, REG_HOST_CTRL_INT,
+ B_CONFIG_UPDATE, B_CONFIG_UPDATE);
+ if (err < 0)
+ return err;
+
+ return it6625_wait_for_status(it6625, REG_HOST_CTRL_INT, 0x00, 25);
+}
+
+static int it6625_set_bank(struct it6625 *it6625, u8 bank)
+{
+ int err;
+
+ err = it6625_write_byte(it6625, REG_INFO_BANK_SEL, bank);
+ if (err < 0)
+ return err;
+
+ err = it6625_write_byte(it6625, REG_HOST_CTRL_INT, B_IF_BANK);
+ if (err < 0)
+ return err;
+
+ return it6625_wait_for_status(it6625, REG_HOST_CTRL_INT, 0x00, 25);
+}
+
+static inline bool is_hdmi(struct it6625 *it6625)
+{
+ int val;
+
+ val = it6625_read_byte(it6625, REG_RX_STATUS);
+ return (val < 0) ? false : (val & B_RX_HDMI);
+}
+
+static inline bool hdmi_5v_power_present(struct it6625 *it6625)
+{
+ int val;
+
+ val = it6625_read_byte(it6625, REG_RX_STATUS);
+ return (val < 0) ? false : (val & B_RX_5V);
+}
+
+static inline bool no_signal(struct it6625 *it6625)
+{
+ int val;
+
+ val = it6625_read_byte(it6625, REG_RX_STATUS);
+ return (val < 0) ? true : !(val & B_RX_STABLE);
+}
+
+static inline bool audio_present(struct it6625 *it6625)
+{
+ int val;
+
+ val = it6625_read_byte(it6625, REG_RX_STATUS);
+ return (val < 0) ? false : (val & B_RX_AUD_ON);
+}
+
+static int get_audio_sampling_rate(struct it6625 *it6625)
+{
+ int fs_id;
+ int i, freq = 0;
+ const struct fs_id_map {
+ u8 fs_id;
+ u32 freq;
+ } s_fsid_map[] = {
+ { AUD32K, 32000 },
+ { AUD44K, 44100 },
+ { AUD48K, 48000 },
+ { AUD64K, 64000 },
+ { AUD88K, 88200 },
+ { AUD96K, 96000 },
+
+ { AUD128K, 128000 },
+ { AUD176K, 176400 },
+ { AUD192K, 192000 },
+ { AUD256K, 256000 },
+ { AUD352K, 352800 },
+ { AUD384K, 384000 },
+
+ { AUD512K, 512000 },
+ { AUD705K, 705600 },
+ { AUD768K, 768000 },
+ { AUD1024K, 1024000 },
+ { AUD1411K, 1411200 },
+ { AUD1536K, 1536000 },
+ };
+
+ if (no_signal(it6625) || !audio_present(it6625))
+ return 0;
+
+ guard(mutex)(&it6625->it6625_lock);
+
+ fs_id = it6625_read_byte(it6625, REG_HDMI_AUDIO_INFO1);
+ if (fs_id < 0)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(s_fsid_map); i++) {
+ if (s_fsid_map[i].fs_id == fs_id) {
+ freq = s_fsid_map[i].freq;
+ break;
+ }
+ }
+
+ return freq;
+}
+
+static u64 it6625_get_pclk(struct it6625 *it6625)
+{
+ u32 pclk;
+ u8 ck[4];
+ int ret;
+
+ ret = it6625_read_bytes(it6625, REG_VID_PCLK, ck, 4);
+ if (ret < 0) {
+ dev_err(it6625->dev, "failed to read pixel clock");
+ return 0;
+ }
+
+ pclk = ck[0];
+ pclk <<= 8;
+ pclk |= ck[1];
+ pclk <<= 8;
+ pclk |= ck[2];
+ pclk <<= 8;
+ pclk |= ck[3];
+
+ v4l2_dbg(1, debug, &it6625->sd, "%s: pclk=%u (%08x)",
+ __func__, pclk, pclk);
+
+ return (u64)pclk * 1000;
+}
+
+static int it6625_read_edid(struct it6625 *it6625, u8 *edid, int start_block,
+ int num_blocks)
+{
+ int i, bank_ctrl, err = 0;
+ struct device *dev = it6625->dev;
+
+ if (!edid) {
+ dev_err(dev, "edid buffer is NULL");
+ return -EINVAL;
+ }
+
+ if (start_block < 0 || num_blocks <= 0 ||
+ start_block > EDID_NUM_BLOCKS_MAX ||
+ num_blocks > EDID_NUM_BLOCKS_MAX ||
+ start_block + num_blocks > EDID_NUM_BLOCKS_MAX) {
+ dev_err(dev,
+ "invalid block range: start_block=%d, num_blocks=%d",
+ start_block, num_blocks);
+ return -EINVAL;
+ }
+
+ guard(mutex)(&it6625->it6625_lock);
+ for (i = 0; i < num_blocks; i++) {
+ bank_ctrl = CTL_BANK_EDID_READ + start_block + i;
+ err = it6625_set_bank(it6625, bank_ctrl);
+ if (err < 0)
+ break;
+
+ err = it6625_read_bytes(it6625, REG_EDID_START,
+ edid + (i * 128), 128);
+ if (err < 0)
+ break;
+ }
+
+ it6625_set_bank(it6625, 0);
+
+ return err < 0 ? err : num_blocks;
+}
+
+static int it6625_write_edid(struct it6625 *it6625, u8 *edid, int start_block,
+ int num_blocks)
+{
+ int i, bank_ctrl, err = 0;
+ struct device *dev = it6625->dev;
+
+ if (start_block < 0 || num_blocks <= 0 ||
+ start_block > EDID_NUM_BLOCKS_MAX ||
+ num_blocks > EDID_NUM_BLOCKS_MAX ||
+ start_block + num_blocks > EDID_NUM_BLOCKS_MAX) {
+ dev_err(dev,
+ "invalid block range: start_block=%d, num_blocks=%d",
+ start_block, num_blocks);
+ return -EINVAL;
+ }
+
+ guard(mutex)(&it6625->it6625_lock);
+ for (i = 0; i < num_blocks; i++) {
+ bank_ctrl = CTL_BANK_EDID_WRITE + start_block + i;
+ err = it6625_set_bank(it6625, bank_ctrl);
+ if (err < 0)
+ break;
+
+ err = it6625_write_bytes(it6625, REG_EDID_START,
+ edid + (i * 128), 128);
+ if (err < 0)
+ break;
+
+ err = it6625_update_config(it6625);
+ if (err < 0)
+ break;
+ }
+
+ it6625_set_bank(it6625, 0);
+
+ return err < 0 ? err : num_blocks;
+}
+
+static void it6625_enable_auto_hpd(struct it6625 *it6625)
+{
+ dev_dbg(it6625->dev, "%s: auto HPD", __func__);
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_set_bits(it6625, REG_RX_CFG, 0x03, 0x00);
+ it6625_update_config(it6625);
+}
+
+static void it6625_disable_hpd(struct it6625 *it6625)
+{
+ cancel_delayed_work_sync(&it6625->hpd_delayed_work);
+
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_set_bits(it6625, REG_RX_CFG, 0x03, 0x01);
+ it6625_update_config(it6625);
+}
+
+static void it6625_enable_hpd(struct it6625 *it6625)
+{
+ schedule_delayed_work(&it6625->hpd_delayed_work, V4L2_SET_EDID_HPD_LOW_JIFFIES);
+}
+
+static void it6625_hpd_delayed_work(struct work_struct *work)
+{
+ struct it6625 *it6625 = container_of(work,
+ struct it6625, hpd_delayed_work.work);
+ int val = 0;
+
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_set_bits(it6625, REG_RX_CFG, 0x03, val);
+ it6625_update_config(it6625);
+}
+
+static int it6625_get_detected_timings(struct it6625 *it6625,
+ struct v4l2_dv_timings *timings)
+{
+ struct v4l2_bt_timings *bt = &timings->bt;
+ int val;
+ unsigned int width, height;
+ u8 buffer[4];
+ u8 buffer2[12];
+
+ if (no_signal(it6625)) {
+ dev_err(it6625->dev, "no signal detected");
+ return -ENOLINK;
+ }
+
+ guard(mutex)(&it6625->it6625_lock);
+
+ memset(timings, 0, sizeof(struct v4l2_dv_timings));
+ timings->type = V4L2_DV_BT_656_1120;
+ val = it6625_read_byte(it6625, REG_VID_INFO);
+ if (val < 0) {
+ dev_err(it6625->dev, "failed to read video info");
+ return -EIO;
+ }
+
+ bt->interlaced = val & B_INTERLACE ?
+ V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
+
+ if (it6625_read_bytes(it6625, REG_H_ACTIVE_1, buffer, 4) < 0)
+ return -EIO;
+
+ width = ((buffer[0] & 0xff) << 8) + buffer[1];
+ height = ((buffer[2] & 0xff) << 8) + buffer[3];
+
+ bt->width = width;
+ bt->height = height;
+
+ if (it6625_read_bytes(it6625, REG_H_FP_1, buffer2, 12) < 0)
+ return -EIO;
+
+ bt->hfrontporch = ((buffer2[0] & 0xff) << 8) + buffer2[1];
+ bt->hsync = ((buffer2[2] & 0xff) << 8) + buffer2[3];
+ bt->hbackporch = ((buffer2[4] & 0xff) << 8) + buffer2[5];
+ bt->vfrontporch = ((buffer2[6] & 0xff) << 8) + buffer2[7];
+ bt->vsync = ((buffer2[8] & 0xff) << 8) + buffer2[9];
+ bt->vbackporch = ((buffer2[10] & 0xff) << 8) + buffer2[11];
+
+ bt->pixelclock = it6625_get_pclk(it6625);
+ if (bt->interlaced == V4L2_DV_INTERLACED) {
+ bt->height *= 2;
+ bt->il_vsync = bt->vsync + 1;
+ }
+
+ return 0;
+}
+
+static void it6625_show_avi_infoframe(struct it6625 *it6625)
+{
+ struct device *dev = it6625->dev;
+ union hdmi_infoframe frame;
+ u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
+ u8 ver, len;
+ int ret;
+
+ if (!is_hdmi(it6625)) {
+ dev_err(dev, "not HDMI signal, skip AVI infoframe log");
+ return;
+ }
+
+ ret = it6625_read_bytes(it6625, REG_AVI_DATA, buffer + 1,
+ HDMI_INFOFRAME_SIZE(AVI) - 1);
+ if (ret < 0) {
+ dev_err(dev, "failed to read AVI infoframe data");
+ return;
+ }
+
+ len = buffer[1];
+ ver = buffer[2];
+
+ buffer[0] = HDMI_INFOFRAME_TYPE_AVI;
+ buffer[1] = ver;
+ buffer[2] = len;
+
+ ret = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer));
+ if (ret < 0) {
+ dev_err(dev, "unpack of AVI infoframe failed");
+ return;
+ }
+
+ hdmi_infoframe_log(KERN_INFO, dev, &frame);
+}
+
+static int it6625_s_ctrl_detect_hdmi_5v(struct v4l2_subdev *sd)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ return v4l2_ctrl_s_ctrl(it6625->ctrl_5v_detect,
+ hdmi_5v_power_present(it6625));
+}
+
+static int it6625_s_ctrl_audio_sampling_rate(struct v4l2_subdev *sd)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ return v4l2_ctrl_s_ctrl(it6625->ctrl_audio_sampling_rate,
+ get_audio_sampling_rate(it6625));
+}
+
+static int it6625_s_ctrl_audio_present(struct v4l2_subdev *sd)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ return v4l2_ctrl_s_ctrl(it6625->ctrl_audio_present,
+ audio_present(it6625));
+}
+
+static void it6625_v4l2_sd_ctrl_update(struct v4l2_subdev *sd)
+{
+ it6625_s_ctrl_detect_hdmi_5v(sd);
+ it6625_s_ctrl_audio_sampling_rate(sd);
+ it6625_s_ctrl_audio_present(sd);
+}
+
+static void it6625_enable_stream_locked(struct it6625 *it6625, bool enable)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+ int val;
+
+ lockdep_assert_held(&it6625->it6625_lock);
+
+ v4l2_dbg(3, debug, sd, "%s: %sable",
+ __func__, enable ? "en" : "dis");
+
+ val = enable ? B_MIPI_OUTPUT : 0;
+ it6625_set_bits(it6625, REG_MIPI_CONTROL, B_MIPI_OUTPUT, val);
+ it6625_update_config(it6625);
+}
+
+static void it6625_enable_stream(struct it6625 *it6625, bool enable)
+{
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_enable_stream_locked(it6625, enable);
+}
+
+static void it6625_set_mipi_config_locked(struct it6625 *it6625, u32 cfg_val)
+{
+ u8 mipi_data_type;
+
+ lockdep_assert_held(&it6625->it6625_lock);
+
+ dev_dbg(it6625->dev, "mipi_data_type = 0x%x", cfg_val);
+
+ mipi_data_type = cfg_val & 0xFF;
+ it6625_write_byte(it6625, REG_MIPI_DATA_TYPE, mipi_data_type);
+ it6625_update_config(it6625);
+}
+
+static inline unsigned int fps_from_bt_timings(const struct v4l2_bt_timings *t)
+{
+ if (!V4L2_DV_BT_FRAME_HEIGHT(t) || !V4L2_DV_BT_FRAME_WIDTH(t))
+ return 0;
+
+ return DIV_ROUND_CLOSEST((unsigned int)t->pixelclock,
+ V4L2_DV_BT_FRAME_HEIGHT(t) *
+ V4L2_DV_BT_FRAME_WIDTH(t));
+}
+
+static void it6625_initial_setup(struct it6625 *it6625)
+{
+ int val = 0;
+
+ guard(mutex)(&it6625->it6625_lock);
+
+ /*
+ * REG_MIPI_CFG[0:2] lane count field: 1 lane -> 0, 2 lanes -> 1,
+ * 3 lanes (C-PHY only) -> 3, 4 lanes (D-PHY only) -> 3.
+ */
+ switch (it6625->csi_lanes) {
+ case 1:
+ val = FIELD_PREP(M_MIPI_LANE, 0);
+ break;
+ case 2:
+ val = FIELD_PREP(M_MIPI_LANE, 1);
+ break;
+ default:
+ val = FIELD_PREP(M_MIPI_LANE, 3);
+ break;
+ }
+
+ if (it6625->bus_type == V4L2_MBUS_CSI2_DPHY)
+ val |= FIELD_PREP(B_MIPI_DPHY, 1);
+
+ if (it6625->port_num == 2)
+ val |= FIELD_PREP(B_MIPI_SPLIT, 1);
+
+ it6625_write_byte(it6625, REG_MIPI_CFG, val);
+ it6625_write_byte(it6625, REG_MIPI_DATA_TYPE, it6625->csi_format);
+ it6625_write_byte(it6625, REG_MIPI_CONTROL, 0x00);
+ it6625_write_byte(it6625, REG_RX_CFG, 0x00);
+
+ it6625_set_bits(it6625, REG_HOST_CTRL_INT, B_CONFIG_UPDATE, B_CONFIG_UPDATE);
+ it6625_wait_for_status(it6625, REG_HOST_CTRL_INT, 0x00, 25);
+}
+
+static int it6625_cec_adap_enable(struct cec_adapter *adap, bool enable)
+{
+ struct it6625 *it6625 = adap->priv;
+ u8 cmds[2];
+
+ cmds[0] = CMD_SET_CEC_ENABLE;
+ cmds[1] = enable ? 1 : 0;
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_write_command(it6625, cmds, sizeof(cmds));
+
+ return 0;
+}
+
+static void it6625_cec_reset_la(struct it6625 *it6625, bool keep_enabled)
+{
+ u8 cmds[2];
+
+ if (keep_enabled) {
+ cmds[0] = CMD_SET_CEC_LA;
+ cmds[1] = CEC_LOG_ADDR_UNREGISTERED;
+ } else {
+ cmds[0] = CMD_SET_CEC_ENABLE;
+ cmds[1] = 0;
+ }
+
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_write_command(it6625, cmds, sizeof(cmds));
+}
+
+static int it6625_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
+{
+ struct it6625 *it6625 = adap->priv;
+ u8 cmds[2] = {CMD_SET_CEC_LA, log_addr};
+
+ dev_dbg(it6625->dev, "%s: la=%d", __func__, log_addr);
+
+ if (log_addr == CEC_LOG_ADDR_INVALID) {
+ it6625_cec_reset_la(it6625, adap->is_enabled);
+ return 0;
+ }
+
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_write_command(it6625, cmds, sizeof(cmds));
+
+ return 0;
+}
+
+static int it6625_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
+ u32 signal_free_time, struct cec_msg *msg)
+{
+ struct it6625 *it6625 = adap->priv;
+
+ guard(mutex)(&it6625->it6625_lock);
+ it6625_write_bytes(it6625, REG_CEC_TX_DATA, msg->msg, msg->len);
+ it6625_write_byte(it6625, REG_CEC_TX_DATA_LEN, msg->len);
+ it6625_set_bits(it6625, REG_HOST_CTRL_INT, B_CEC_SEND_DATA, B_CEC_SEND_DATA);
+ it6625_wait_for_status(it6625, REG_HOST_CTRL_INT, 0x00, 25);
+
+ return 0;
+}
+
+static const struct cec_adap_ops it6625_cec_adap_ops = {
+ .adap_enable = it6625_cec_adap_enable,
+ .adap_log_addr = it6625_cec_adap_log_addr,
+ .adap_transmit = it6625_cec_adap_transmit,
+};
+
+static void it6625_cec_handler(struct it6625 *it6625, u8 intstatus)
+{
+ struct cec_msg rxmsg = {};
+ int val = 0;
+
+ if (intstatus & B_CEC_RX_RECEIVED) {
+ scoped_guard(mutex, &it6625->it6625_lock) {
+ val = it6625_read_byte(it6625, REG_CEC_RX_DATA_LEN);
+ if (val > 0 && val <= CEC_MAX_MSG_SIZE)
+ it6625_read_bytes(it6625, REG_CEC_RX_DATA, &rxmsg.msg[0], val);
+ it6625_write_byte(it6625, REG_CEC_RX_DATA_LEN, 0);
+ }
+
+ if (val > 0 && val <= CEC_MAX_MSG_SIZE) {
+ rxmsg.len = val;
+ cec_received_msg(it6625->cec_adap, &rxmsg);
+ } else {
+ dev_err(it6625->dev, "invalid CEC RX length %d", val);
+ }
+ }
+
+ if (intstatus & B_CEC_TX_UPDATE) {
+ val = it6625_read_byte(it6625, REG_CEC_STATUS);
+ if (val < 0) {
+ dev_err(it6625->dev, "read CEC status failed");
+ return;
+ }
+
+ if (val & BIT(0)) {
+ cec_transmit_attempt_done(it6625->cec_adap,
+ CEC_TX_STATUS_OK);
+ } else if (val & BIT(1)) {
+ cec_transmit_attempt_done(it6625->cec_adap,
+ CEC_TX_STATUS_NACK);
+ } else {
+ dev_info(it6625->dev, "unknown CEC status %02X", val);
+ cec_transmit_attempt_done(it6625->cec_adap,
+ CEC_TX_STATUS_NACK);
+ }
+ }
+}
+
+static void it6625_irq_format_change(struct it6625 *it6625)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+ struct v4l2_dv_timings timings;
+ const struct v4l2_event it6625_ev_fmt = {
+ .type = V4L2_EVENT_SOURCE_CHANGE,
+ .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION,
+ };
+ int ret;
+
+ if (no_signal(it6625)) {
+ if (sd->devnode)
+ v4l2_subdev_notify_event(sd, &it6625_ev_fmt);
+ return;
+ }
+
+ ret = it6625_get_detected_timings(it6625, &timings);
+ if (ret < 0) {
+ v4l2_dbg(1, debug, sd, "Failed to get detected timings");
+ return;
+ }
+
+ if (sd->devnode)
+ v4l2_subdev_notify_event(sd, &it6625_ev_fmt);
+}
+
+static void it6625_irq_hdmi_audio_change(struct it6625 *it6625)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+
+ it6625_s_ctrl_audio_sampling_rate(sd);
+ it6625_s_ctrl_audio_present(sd);
+}
+
+static void it6625_get_timings(struct it6625 *it6625,
+ struct v4l2_dv_timings *timings)
+{
+ guard(mutex)(&it6625->it6625_lock);
+ *timings = it6625->timings;
+}
+
+static void it6625_clear_timings(struct it6625 *it6625)
+{
+ guard(mutex)(&it6625->it6625_lock);
+ memset(&it6625->timings, 0, sizeof(it6625->timings));
+}
+
+static void it6625_irq_hdmi_5v_change(struct it6625 *it6625)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+
+ it6625_clear_timings(it6625);
+ it6625_v4l2_sd_ctrl_update(sd);
+}
+
+static void it6625_irq_hdcp_change(struct it6625 *it6625)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+ u8 cp_sts;
+
+ cp_sts = it6625_read_byte(it6625, REG_RX_HDCP_STS);
+ v4l2_info(sd, "HDCP change to %02X", cp_sts);
+}
+
+static void it6625_irq_infoframe_latch(struct it6625 *it6625)
+{
+ if (!it6625->if_active)
+ return;
+
+ it6625->if_active = false;
+
+ scoped_guard(mutex, &it6625->it6625_lock) {
+ it6625->if_snapshot_err =
+ it6625_read_bytes(it6625, REG_IF_DATA,
+ it6625->if_snapshot, 31);
+ }
+ it6625->if_snapshot_done = true;
+ complete(&it6625->if_latched);
+}
+
+static void it6625_irq_emata_packet_latch(struct it6625 *it6625)
+{
+ u8 extend_packet[14];
+
+ it6625_read_bytes(it6625, REG_EMP_DATA, extend_packet, 13);
+}
+
+static void it6625_irq_avmute_change(struct it6625 *it6625)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+ int val;
+ u8 avmute;
+
+ val = it6625_read_byte(it6625, REG_RX_STATUS);
+ if (val < 0)
+ return;
+
+ avmute = (val & B_RX_AVMUTE) ? 1 : 0;
+ v4l2_info(sd, "AVMute change to %d", avmute);
+}
+
+static void it6625_dispatch_int_status1(struct it6625 *it6625, int int_sts1)
+{
+ if (int_sts1 & B_HDMI_5V_CHG)
+ it6625_irq_hdmi_5v_change(it6625);
+
+ if (int_sts1 & B_HDMI_VID_CHG)
+ it6625_irq_format_change(it6625);
+
+ if (int_sts1 & B_HDMI_AUD_CHG)
+ it6625_irq_hdmi_audio_change(it6625);
+
+ if (int_sts1 & B_HDMI_CP_CHG)
+ it6625_irq_hdcp_change(it6625);
+
+ if (int_sts1 & B_HDMI_IF_LATCH)
+ it6625_irq_infoframe_latch(it6625);
+
+ if (int_sts1 & B_HDMI_EMP)
+ it6625_irq_emata_packet_latch(it6625);
+}
+
+static void it6625_dispatch_int_status2(struct it6625 *it6625, int int_sts2)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+
+ if (int_sts2 & B_HDMI_AVI)
+ it6625_show_avi_infoframe(it6625);
+
+ if (int_sts2 & B_HDMI_NO_AVI)
+ v4l2_info(sd, "AVI-info stopped");
+
+ if (int_sts2 & B_HDMI_AVMUTE_CHG)
+ it6625_irq_avmute_change(it6625);
+}
+
+/* caller must already hold if_state_lock */
+static int it6625_drain_rx_int_status(struct it6625 *it6625)
+{
+ int val1, val2, err;
+
+ val1 = it6625_read_byte(it6625, REG_RX_INT_STATUS1);
+ val2 = it6625_read_byte(it6625, REG_RX_INT_STATUS2);
+
+ err = 0;
+ if (val1 < 0)
+ err = val1;
+ else if (val2 < 0)
+ err = val2;
+
+ if (val1 >= 0) {
+ int werr = it6625_write_byte(it6625, REG_RX_INT_STATUS1, 0x00);
+
+ if (werr && !err)
+ err = werr;
+ it6625_dispatch_int_status1(it6625, val1);
+ }
+
+ if (val2 >= 0) {
+ int werr = it6625_write_byte(it6625, REG_RX_INT_STATUS2, 0x00);
+
+ if (werr && !err)
+ err = werr;
+ it6625_dispatch_int_status2(it6625, val2);
+ }
+
+ return err;
+}
+
+/* caller must already hold if_state_lock */
+static int it6625_drain_interrupts(struct it6625 *it6625, bool *had_event)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+ int val, err;
+
+ val = it6625_read_byte(it6625, REG_MCU_INTERRUPT);
+ if (val < 0) {
+ if (had_event)
+ *had_event = false;
+ return val;
+ }
+ if (had_event)
+ *had_event = val > 0;
+ if (val == 0)
+ return 0;
+
+ err = it6625_write_byte(it6625, REG_MCU_INTERRUPT, val);
+
+ v4l2_dbg(1, debug, sd, "%s: INT = 0x%02X", __func__, val);
+
+ if (val & (B_CEC_RX_RECEIVED | B_CEC_TX_UPDATE) && it6625->cec_adap)
+ it6625_cec_handler(it6625, val & (B_CEC_RX_RECEIVED | B_CEC_TX_UPDATE));
+
+ if (val & B_SYS_INT_ACTIVE) {
+ int child_err = it6625_drain_rx_int_status(it6625);
+
+ if (child_err && !err)
+ err = child_err;
+ }
+
+ return err;
+}
+
+static bool it6625_interrupt_handler(struct it6625 *it6625)
+{
+ bool had_event = false;
+
+ guard(mutex)(&it6625->if_state_lock);
+ it6625_drain_interrupts(it6625, &had_event);
+ return had_event;
+}
+
+static irqreturn_t it6625_irq_handler(int unused, void *data)
+{
+ struct it6625 *it6625 = data;
+
+ return it6625_interrupt_handler(it6625) ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void it6625_irq_poll_timer(struct timer_list *t)
+{
+ struct it6625 *it6625 = timer_container_of(it6625, t, timer);
+ unsigned int msecs;
+
+ schedule_work(&it6625->polling_work);
+ /*
+ * If CEC is present, then we need to poll more frequently,
+ * otherwise we will miss CEC messages.
+ */
+ msecs = it6625->cec_adap ? POLL_INTERVAL_CEC_MS : POLL_INTERVAL_MS;
+ mod_timer(&it6625->timer, jiffies + msecs_to_jiffies(msecs));
+}
+
+static void it6625_polling_work(struct work_struct *work)
+{
+ struct it6625 *it6625 = container_of(work, struct it6625,
+ polling_work);
+
+ it6625_interrupt_handler(it6625);
+}
+
+static const char *it6625_csi_format_name(u8 csi_format)
+{
+ switch (csi_format) {
+ case CSI_YUV422_8b:
+ return "YUV422 8bit";
+ case CSI_RGB888:
+ return "RGB888 8bit";
+ case CSI_YUV444_8b:
+ return "YUV444 8bit";
+ default:
+ return "unknown";
+ }
+}
+
+static int it6625_log_status(struct v4l2_subdev *sd)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ struct v4l2_dv_timings timings, configured_timings;
+ struct v4l2_bt_timings bt;
+ u8 csi_format;
+
+ if (it6625_get_detected_timings(it6625, &timings))
+ v4l2_info(sd, "No video detected");
+ else
+ v4l2_print_dv_timings(sd->name, "Detected format: ", &timings,
+ true);
+
+ it6625_get_timings(it6625, &configured_timings);
+ v4l2_print_dv_timings(sd->name, "Configured format: ",
+ &configured_timings, true);
+
+ /* snapshot together so the reported pair was actually configured together */
+ scoped_guard(mutex, &it6625->it6625_lock) {
+ csi_format = it6625->csi_format;
+ bt = it6625->timings.bt;
+ }
+
+ v4l2_info(sd, "CSI format: %s @ %uHz",
+ it6625_csi_format_name(csi_format),
+ fps_from_bt_timings(&bt));
+
+ it6625_show_avi_infoframe(it6625);
+
+ return 0;
+}
+
+static int it6625_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ schedule_work(&it6625->polling_work);
+ *handled = true;
+
+ return 0;
+}
+
+static int it6625_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_SOURCE_CHANGE:
+ return v4l2_src_change_event_subdev_subscribe(sd, fh, sub);
+ case V4L2_EVENT_CTRL:
+ return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int it6625_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ bool val;
+
+ val = no_signal(it6625);
+ *status = 0;
+ *status |= val ? V4L2_IN_ST_NO_SIGNAL : 0;
+ *status |= val ? V4L2_IN_ST_NO_SYNC : 0;
+
+ v4l2_dbg(1, debug, sd, "%s: status = 0x%x", __func__, *status);
+
+ return 0;
+}
+
+static int
+it6625_update_timings_if_changed(struct it6625 *it6625,
+ const struct v4l2_dv_timings *timings)
+{
+ int ret;
+
+ guard(mutex)(&it6625->it6625_lock);
+ if (v4l2_match_dv_timings(&it6625->timings, timings, 0, false)) {
+ ret = 0;
+ } else if (!v4l2_valid_dv_timings(timings, it6625_get_timings_cap(it6625),
+ NULL, NULL)) {
+ ret = -ERANGE;
+ } else {
+ it6625->timings = *timings;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int it6625_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ if (timings->pad != 0)
+ return -EINVAL;
+
+ return v4l2_enum_dv_timings_cap(timings,
+ it6625_get_timings_cap(it6625), NULL, NULL);
+}
+
+static int it6625_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ if (cap->pad != 0)
+ return -EINVAL;
+
+ *cap = *it6625_get_timings_cap(it6625);
+
+ return 0;
+}
+
+static int it6625_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ it6625_enable_stream(it6625, enable);
+ return 0;
+}
+
+static int it6625_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ dev_dbg(sd->dev, "%s: index=%d", __func__, code->index);
+
+ if (code->index >= ARRAY_SIZE(it6625_formats))
+ return -EINVAL;
+
+ dev_dbg(sd->dev, "%s: code=0x%08x", __func__,
+ it6625_formats[code->index].mbus_fmt_code);
+ code->code = it6625_formats[code->index].mbus_fmt_code;
+
+ return 0;
+}
+
+static int it6625_get_mbus_config(struct v4l2_subdev *sd,
+ unsigned int pad,
+ struct v4l2_mbus_config *cfg)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ if (pad != 0)
+ return -EINVAL;
+
+ cfg->type = it6625->bus_type;
+ cfg->bus.mipi_csi2.flags = 0;
+ cfg->bus.mipi_csi2.num_data_lanes = it6625->csi_lanes;
+
+ return 0;
+}
+
+static int it6625_pad_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ int ret;
+
+ if (pad != 0)
+ return -EINVAL;
+
+ if (!timings)
+ return -EINVAL;
+
+ if (debug)
+ v4l2_print_dv_timings(sd->name, __func__, timings, false);
+
+ ret = it6625_update_timings_if_changed(it6625, timings);
+ if (ret == -ERANGE) {
+ v4l2_dbg(1, debug, sd, "%s: timings out of range", __func__);
+ return ret;
+ }
+
+ if (ret == 0)
+ v4l2_dbg(1, debug, sd, "%s: no change", __func__);
+
+ return 0;
+}
+
+static int it6625_pad_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ if (pad != 0)
+ return -EINVAL;
+
+ it6625_get_timings(it6625, timings);
+
+ return 0;
+}
+
+static int it6625_pad_query_dv_timings(struct v4l2_subdev *sd,
+ unsigned int pad,
+ struct v4l2_dv_timings *timings)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ int ret;
+
+ if (pad != 0)
+ return -EINVAL;
+
+ ret = it6625_get_detected_timings(it6625, timings);
+ if (ret)
+ return ret;
+
+ if (debug)
+ v4l2_print_dv_timings(sd->name, __func__, timings, false);
+
+ if (!v4l2_valid_dv_timings(timings, it6625_get_timings_cap(it6625), NULL, NULL)) {
+ v4l2_dbg(1, debug, sd, "%s: timings out of range", __func__);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static inline u32 format_to_colorspace(u8 csi_format)
+{
+ switch (csi_format) {
+ case CSI_RGB444:
+ case CSI_RGB555:
+ case CSI_RGB565:
+ case CSI_RGB666:
+ case CSI_RGB888:
+ case CSI_RGB_10b:
+ case CSI_RGB_12b:
+ return V4L2_COLORSPACE_SRGB;
+ case CSI_YUV420_8b_L:
+ case CSI_YUV420_8b:
+ case CSI_YUV420_10b:
+ case CSI_YUV422_8b:
+ case CSI_YUV422_10b:
+ case CSI_YUV422_12b:
+ case CSI_YUV420_10b_L:
+ case CSI_YUV420_12b:
+ case CSI_YUV444_8b:
+ case CSI_YUV444_10b:
+ case CSI_YUV444_12b:
+ return V4L2_COLORSPACE_REC709;
+ default:
+ return 0;
+ }
+}
+
+static int it6625_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ struct v4l2_dv_timings timings;
+
+ if (format->pad != 0)
+ return -EINVAL;
+
+ it6625_get_timings(it6625, &timings);
+ format->format.width = timings.bt.width;
+ format->format.height = timings.bt.height;
+ format->format.field = timings.bt.interlaced == V4L2_DV_INTERLACED ?
+ V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
+ format->format.code = fmt->code;
+ format->format.colorspace = fmt->colorspace;
+ } else {
+ scoped_guard(mutex, &it6625->it6625_lock) {
+ format->format.colorspace =
+ format_to_colorspace(it6625->csi_format);
+ format->format.code = it6625->mbus_fmt_code;
+ }
+ }
+
+ return 0;
+}
+
+static int it6625_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ int ret;
+ u32 mbus_fmt_code = format->format.code;
+
+ ret = it6625_get_fmt(sd, sd_state, format);
+ format->format.code = mbus_fmt_code;
+
+ if (ret)
+ return ret;
+
+ ret = it6625_csi_mbus_code_idx(mbus_fmt_code);
+
+ if (ret < 0) {
+ v4l2_dbg(1, debug, sd,
+ "%s: unsupported format code 0x%x, falling back to default",
+ __func__, mbus_fmt_code);
+ ret = 0;
+ mbus_fmt_code = it6625_formats[ret].mbus_fmt_code;
+ format->format.code = mbus_fmt_code;
+ }
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, format->pad);
+ fmt->code = format->format.code;
+ fmt->colorspace = format_to_colorspace(it6625_formats[ret].csi_format);
+ format->format.colorspace = fmt->colorspace;
+ v4l2_dbg(1, debug, sd, "%s: try format code = 0x%x",
+ __func__, format->format.code);
+ return 0;
+ }
+
+ scoped_guard(mutex, &it6625->it6625_lock) {
+ it6625->csi_format = it6625_formats[ret].csi_format;
+ it6625->mbus_fmt_code = format->format.code;
+ it6625_enable_stream_locked(it6625, false);
+ it6625_set_mipi_config_locked(it6625, it6625->csi_format);
+ }
+
+ format->format.colorspace = format_to_colorspace(it6625_formats[ret].csi_format);
+
+ return 0;
+}
+
+static int it6625_g_edid(struct v4l2_subdev *sd,
+ struct v4l2_subdev_edid *edid)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ int err;
+
+ if (edid->pad != 0)
+ return -EINVAL;
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ guard(mutex)(&it6625->edid_lock);
+
+ if (edid->start_block == 0 && edid->blocks == 0) {
+ edid->blocks = it6625->edid_blocks;
+ return 0;
+ }
+
+ if (it6625->edid_blocks == 0)
+ return -ENODATA;
+
+ if (edid->start_block >= it6625->edid_blocks || edid->blocks == 0)
+ return -EINVAL;
+
+ if (edid->blocks > it6625->edid_blocks - edid->start_block)
+ edid->blocks = it6625->edid_blocks - edid->start_block;
+
+ err = it6625_read_edid(it6625, edid->edid, edid->start_block,
+ edid->blocks);
+
+ return (err < 0) ? err : 0;
+}
+
+static int it6625_s_edid(struct v4l2_subdev *sd,
+ struct v4l2_subdev_edid *edid)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ int err;
+ u16 parent_pa = CEC_PHYS_ADDR_INVALID;
+
+ if (edid->pad != 0) {
+ v4l2_err(sd, "invalid pad %d", edid->pad);
+ return -EINVAL;
+ }
+
+ memset(edid->reserved, 0, sizeof(edid->reserved));
+
+ if (edid->start_block != 0) {
+ v4l2_err(sd, "start_block must be 0 for set edid");
+ return -EINVAL;
+ }
+
+ if (edid->blocks > EDID_NUM_BLOCKS_MAX) {
+ v4l2_err(sd, "too many edid blocks: %d", edid->blocks);
+ edid->blocks = EDID_NUM_BLOCKS_MAX;
+ return -E2BIG;
+ }
+
+ if (edid->blocks != 0) {
+ u16 pa = v4l2_get_edid_phys_addr(edid->edid,
+ edid->blocks * 128, NULL);
+ err = v4l2_phys_addr_validate(pa, &parent_pa, NULL);
+ if (err) {
+ v4l2_err(sd, "invalid CEC physical address in EDID");
+ return err;
+ }
+ }
+
+ guard(mutex)(&it6625->edid_lock);
+
+ it6625_disable_hpd(it6625);
+ cec_phys_addr_invalidate(it6625->cec_adap);
+ it6625->edid_blocks = 0;
+
+ if (edid->blocks == 0)
+ return 0;
+
+ err = it6625_write_edid(it6625, edid->edid,
+ edid->start_block, edid->blocks);
+ if (err < 0) {
+ v4l2_err(sd, "write edid failed");
+ return err;
+ }
+
+ it6625->edid_blocks = edid->blocks;
+ cec_s_phys_addr(it6625->cec_adap, parent_pa, false);
+
+ if (hdmi_5v_power_present(it6625)) {
+ it6625_enable_hpd(it6625);
+ it6625_s_ctrl_detect_hdmi_5v(sd);
+ } else {
+ it6625_enable_auto_hpd(it6625);
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops it6625_core_ops = {
+ .log_status = it6625_log_status,
+ .interrupt_service_routine = it6625_isr,
+ .subscribe_event = it6625_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops it6625_video_ops = {
+ .g_input_status = it6625_g_input_status,
+ .s_stream = it6625_s_stream,
+};
+
+static const struct v4l2_subdev_pad_ops it6625_pad_ops = {
+ .enum_mbus_code = it6625_enum_mbus_code,
+ .set_fmt = it6625_set_fmt,
+ .get_fmt = it6625_get_fmt,
+ .get_edid = it6625_g_edid,
+ .set_edid = it6625_s_edid,
+ .enum_dv_timings = it6625_enum_dv_timings,
+ .dv_timings_cap = it6625_dv_timings_cap,
+ .get_mbus_config = it6625_get_mbus_config,
+ .s_dv_timings = it6625_pad_s_dv_timings,
+ .g_dv_timings = it6625_pad_g_dv_timings,
+ .query_dv_timings = it6625_pad_query_dv_timings,
+};
+
+static const struct v4l2_subdev_ops it6625_ops = {
+ .core = &it6625_core_ops,
+ .video = &it6625_video_ops,
+ .pad = &it6625_pad_ops,
+};
+
+static int it6625_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_mbus_framefmt *fmt = v4l2_subdev_state_get_format(sd_state, 0);
+
+ fmt->code = it6625_formats[0].mbus_fmt_code;
+ fmt->colorspace = format_to_colorspace(it6625_formats[0].csi_format);
+
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops it6625_internal_ops = {
+ .init_state = it6625_init_state,
+};
+
+static const struct v4l2_ctrl_config it6625_ctrl_audio_sampling_rate = {
+ .id = V4L2_CID_IT6625_AUDIO_SAMPLING_RATE,
+ .name = "Audio Sampling Rate",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .min = 0,
+ .max = 1536000,
+ .step = 1,
+ .def = 0,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+};
+
+static const struct v4l2_ctrl_config it6625_ctrl_audio_present = {
+ .id = V4L2_CID_IT6625_AUDIO_PRESENT,
+ .name = "Audio Present",
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+ .flags = V4L2_CTRL_FLAG_READ_ONLY,
+};
+
+static int it6625_v4l2_init_controls(struct v4l2_subdev *sd)
+{
+ struct it6625 *it6625 = sd_to_6625(sd);
+ struct v4l2_ctrl_handler *hdl = &it6625->hdl;
+
+ v4l2_ctrl_handler_init(hdl, 4);
+ it6625->ctrl_5v_detect =
+ v4l2_ctrl_new_std(hdl, NULL, V4L2_CID_DV_RX_POWER_PRESENT,
+ 0, 1, 0, 0);
+
+ it6625->ctrl_audio_sampling_rate =
+ v4l2_ctrl_new_custom(hdl,
+ &it6625_ctrl_audio_sampling_rate,
+ NULL);
+ it6625->ctrl_audio_present =
+ v4l2_ctrl_new_custom(hdl, &it6625_ctrl_audio_present, NULL);
+ it6625->ctrl_link_freq =
+ v4l2_ctrl_new_int_menu(hdl, NULL, V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(it6625_link_freq) - 1,
+ it6625->bus_type == V4L2_MBUS_CSI2_CPHY ? 1 : 0,
+ it6625_link_freq);
+ if (hdl->error) {
+ v4l2_err(sd, "Failed to initialize controls");
+ v4l2_ctrl_handler_free(hdl);
+ return hdl->error;
+ }
+
+ sd->ctrl_handler = hdl;
+
+ return 0;
+}
+
+static void it6625_regdump_print(struct seq_file *s, const u8 *reg_buf)
+{
+ int i;
+
+ seq_puts(s, " 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F\n");
+
+ for (i = 0; i < 256; i++) {
+ if (i % 16 == 0)
+ seq_printf(s, "[%02X] ", i & 0xF0);
+ seq_printf(s, "0x%02X ", reg_buf[i]);
+ if (i % 16 == 15)
+ seq_putc(s, '\n');
+ }
+}
+
+static int it6625_mipi_reg_show(struct seq_file *s, void *data)
+{
+ struct it6625 *it6625 = s->private;
+ u8 reg_buf[256];
+ int ret;
+
+ scoped_guard(mutex, &it6625->it6625_lock)
+ ret = it6625_read_bytes(it6625, 0x00, reg_buf, sizeof(reg_buf));
+ if (ret < 0)
+ return ret;
+ it6625_regdump_print(s, reg_buf);
+
+ return 0;
+}
+
+static int it6625_mipi_reg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, it6625_mipi_reg_show, inode->i_private);
+}
+
+static ssize_t it6625_mipi_reg_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct it6625 *it6625 = file_inode(file)->i_private;
+ char buf[32] = {};
+ unsigned int addr, val;
+ ssize_t len;
+ loff_t pos = 0;
+
+ len = simple_write_to_buffer(buf, sizeof(buf) - 1, &pos, user_buf, count);
+ if (len < 0)
+ return len;
+ buf[len] = '\0';
+
+ if (sscanf(buf, "%X %X", &addr, &val) != 2)
+ return -EINVAL;
+
+ scoped_guard(mutex, &it6625->it6625_lock)
+ it6625_write_byte(it6625, addr, val);
+
+ return count;
+}
+
+static const struct file_operations it6625_mipi_reg_fops = {
+ .owner = THIS_MODULE,
+ .open = it6625_mipi_reg_open,
+ .read = seq_read,
+ .write = it6625_mipi_reg_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+/*
+ * Maps a V4L2_DEBUGFS_IF_* framework flag to the actual HDMI packet-type
+ * header byte REG_IF_LATCH_HB expects to arm that InfoFrame type.
+ */
+static int it6625_if_packet_type(u32 type)
+{
+ switch (type) {
+ case V4L2_DEBUGFS_IF_AVI:
+ return 0x82;
+ case V4L2_DEBUGFS_IF_AUDIO:
+ return 0x84;
+ case V4L2_DEBUGFS_IF_SPD:
+ return 0x83;
+ case V4L2_DEBUGFS_IF_HDMI:
+ return 0x81;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t it6625_debugfs_if_read(u32 type, void *priv, struct file *filp,
+ char __user *ubuf, size_t count,
+ loff_t *ppos)
+{
+ struct v4l2_subdev *sd = priv;
+ struct it6625 *it6625 = sd_to_6625(sd);
+ u8 buf[32] = {};
+ int packet_type;
+ int err, err_reset;
+ bool captured;
+ int len;
+
+ packet_type = it6625_if_packet_type(type);
+ if (packet_type < 0)
+ return 0;
+
+ guard(mutex)(&it6625->if_read_lock);
+
+ scoped_guard(mutex, &it6625->if_state_lock) {
+ scoped_guard(mutex, &it6625->it6625_lock)
+ err = it6625_write_byte(it6625, REG_IF_LATCH_HB, 0);
+ if (err)
+ return err;
+
+ err = it6625_drain_interrupts(it6625, NULL);
+ if (err)
+ return err;
+
+ reinit_completion(&it6625->if_latched);
+
+ it6625->if_active = true;
+ it6625->if_type = packet_type;
+ it6625->if_snapshot_done = false;
+ it6625->if_snapshot_err = 0;
+
+ scoped_guard(mutex, &it6625->it6625_lock)
+ err = it6625_write_byte(it6625, REG_IF_LATCH_HB,
+ packet_type);
+ if (err) {
+ scoped_guard(mutex, &it6625->it6625_lock)
+ it6625_write_byte(it6625, REG_IF_LATCH_HB, 0);
+ it6625->if_active = false;
+ return err;
+ }
+ }
+
+ wait_for_completion_timeout(&it6625->if_latched, msecs_to_jiffies(100));
+
+ scoped_guard(mutex, &it6625->if_state_lock) {
+ captured = it6625->if_snapshot_done;
+ it6625->if_active = false;
+
+ scoped_guard(mutex, &it6625->it6625_lock)
+ err_reset = it6625_write_byte(it6625, REG_IF_LATCH_HB, 0);
+
+ if (!captured) {
+ if (err_reset)
+ return err_reset;
+ return 0;
+ }
+
+ if (!it6625->if_snapshot_err) {
+ buf[0] = packet_type;
+ memcpy(&buf[1], it6625->if_snapshot, 31);
+ }
+
+ err = it6625->if_snapshot_err ? it6625->if_snapshot_err : err_reset;
+ if (err)
+ return err;
+ }
+
+ len = buf[2] ? buf[2] + 4 : -ENOENT;
+ if (len > (int)sizeof(buf))
+ len = -ENOENT;
+ if (len < 0)
+ return 0;
+ return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static void it6625_debugfs_init(struct it6625 *it6625, struct i2c_client *client)
+{
+ it6625->debugfs_dir = debugfs_create_dir(dev_name(&client->dev), NULL);
+
+ debugfs_create_file("mipi_reg", 0600, it6625->debugfs_dir, it6625,
+ &it6625_mipi_reg_fops);
+
+ it6625->infoframes = v4l2_debugfs_if_alloc(it6625->debugfs_dir,
+ V4L2_DEBUGFS_IF_AVI | V4L2_DEBUGFS_IF_AUDIO |
+ V4L2_DEBUGFS_IF_SPD | V4L2_DEBUGFS_IF_HDMI,
+ &it6625->sd, it6625_debugfs_if_read);
+}
+
+static void it6625_init_data(struct it6625 *it6625)
+{
+ static struct v4l2_dv_timings default_timing =
+ V4L2_DV_BT_CEA_1920X1080P60;
+
+ it6625->csi_lanes = 4;
+ it6625->port_num = 1;
+ it6625->bus_type = V4L2_MBUS_CSI2_DPHY;
+ it6625->csi_format = it6625_formats[0].csi_format;
+ it6625->mbus_fmt_code = it6625_formats[0].mbus_fmt_code;
+ it6625->timings = default_timing;
+ /* firmware ships with a verified 2-block default EDID in EDID RAM */
+ it6625->edid_blocks = 2;
+}
+
+static int it6625_parse_endpoint(struct it6625 *it6625)
+{
+ struct device *dev = it6625->dev;
+ /*
+ * Pre-setting bus_type here makes v4l2_fwnode_endpoint_alloc_parse()
+ * treat it as a hard requirement and reject any endpoint whose DT
+ * bus-type disagrees, so this must stay V4L2_MBUS_UNKNOWN to let it
+ * autodetect C-PHY vs D-PHY from the endpoint itself.
+ */
+ struct v4l2_fwnode_endpoint endpoint = { .bus_type = V4L2_MBUS_UNKNOWN };
+ struct device_node *ep = NULL;
+ unsigned int max_lanes;
+ unsigned int port;
+ int ret;
+
+ /*
+ * port@0 and port@1 are the two CSI-2 output ports MIPI0/MIPI1
+ * (port@2 is the HDMI input). This chip series can drive both
+ * simultaneously in split or mirror mode, so port_num counts how
+ * many of MIPI0/MIPI1 have an endpoint wired up. This driver only
+ * wires up a single source pad, so lane/bus-type configuration is
+ * parsed from whichever of the two is found first.
+ */
+ it6625->port_num = 0;
+ for (port = 0; port < 2; port++) {
+ struct device_node *port_ep =
+ of_graph_get_endpoint_by_regs(dev->of_node, port, -1);
+
+ if (!port_ep)
+ continue;
+
+ it6625->port_num++;
+ if (!ep)
+ ep = port_ep;
+ else
+ of_node_put(port_ep);
+ }
+
+ if (!ep) {
+ it6625->port_num = 1;
+ dev_dbg(dev, "no CSI-2 endpoint node found, using default %u CSI lanes",
+ it6625->csi_lanes);
+ return 0;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint);
+ of_node_put(ep);
+ if (ret) {
+ dev_err(dev, "failed to parse endpoint: %d", ret);
+ return ret;
+ }
+
+ if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY &&
+ endpoint.bus_type != V4L2_MBUS_CSI2_CPHY) {
+ dev_err(dev, "unsupported bus type %d, expected CSI-2 D-PHY or C-PHY",
+ endpoint.bus_type);
+ v4l2_fwnode_endpoint_free(&endpoint);
+ return -EINVAL;
+ }
+
+ if (endpoint.bus_type == V4L2_MBUS_CSI2_CPHY &&
+ it6625->chip_type != IT6626_CHIP) {
+ dev_err(dev, "IT6625 does not support C-PHY, only IT6626 does");
+ v4l2_fwnode_endpoint_free(&endpoint);
+ return -EINVAL;
+ }
+
+ max_lanes = (endpoint.bus_type == V4L2_MBUS_CSI2_CPHY) ? 3 : 4;
+
+ if (endpoint.bus.mipi_csi2.num_data_lanes == 0 ||
+ endpoint.bus.mipi_csi2.num_data_lanes > max_lanes) {
+ dev_err(dev,
+ "invalid number of CSI data lanes: %u (max %u for this bus type)",
+ endpoint.bus.mipi_csi2.num_data_lanes, max_lanes);
+ v4l2_fwnode_endpoint_free(&endpoint);
+ return -EINVAL;
+ }
+
+ it6625->csi_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
+ it6625->bus_type = endpoint.bus_type;
+ v4l2_fwnode_endpoint_free(&endpoint);
+
+ return 0;
+}
+
+static int it6625_parse_dt(struct it6625 *it6625)
+{
+ return it6625_parse_endpoint(it6625);
+}
+
+static int it6625_init_v4l2_subdev(struct it6625 *it6625)
+{
+ struct v4l2_subdev *sd = &it6625->sd;
+ int err;
+
+ sd->dev = it6625->dev;
+
+ v4l2_i2c_subdev_init(sd, it6625->i2c_client, &it6625_ops);
+ sd->internal_ops = &it6625_internal_ops;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
+ if (it6625_v4l2_init_controls(sd)) {
+ dev_err(it6625->dev, "Failed to initialize v4l2 controls");
+ return -ENOMEM;
+ }
+
+ it6625->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ err = media_entity_pads_init(&sd->entity, 1, &it6625->pad);
+ if (err < 0) {
+ dev_err(it6625->dev, "%s %d err=%d", __func__, __LINE__, err);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ return err;
+ }
+
+ return 0;
+}
+
+static int it6625_check_device(struct it6625 *it6625)
+{
+ static const u8 chip_ids[][2] = {
+ { 0x66, 0x25 },
+ { 0x66, 0x26 },
+ };
+ int chip_id0, chip_id1;
+
+ chip_id0 = it6625_read_byte(it6625, REG_CHIP_ID_0);
+ chip_id1 = it6625_read_byte(it6625, REG_CHIP_ID_1);
+ if (chip_id0 != chip_ids[it6625->chip_type][0] ||
+ chip_id1 != chip_ids[it6625->chip_type][1]) {
+ dev_err(it6625->dev,
+ "chip ID mismatch: got 0x%02x%02x, expected 0x%02x%02x",
+ chip_id0, chip_id1,
+ chip_ids[it6625->chip_type][0],
+ chip_ids[it6625->chip_type][1]);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int it6625_probe(struct i2c_client *client)
+{
+ struct it6625 *it6625;
+ struct v4l2_subdev *sd;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ it6625 = devm_kzalloc(&client->dev, sizeof(struct it6625), GFP_KERNEL);
+ if (!it6625)
+ return -ENOMEM;
+
+ it6625->chip_type = (uintptr_t)i2c_get_match_data(client);
+
+ it6625->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(it6625->reset_gpio))
+ return PTR_ERR(it6625->reset_gpio);
+
+ if (it6625->reset_gpio) {
+ usleep_range(1000, 2000);
+ gpiod_set_value_cansleep(it6625->reset_gpio, 0);
+ usleep_range(10000, 11000);
+ }
+
+ err = it6625_regmap_i2c_init(client, it6625);
+ if (err)
+ return err;
+
+ err = it6625_check_device(it6625);
+ if (err)
+ return err;
+
+ it6625_init_data(it6625);
+
+ err = it6625_parse_dt(it6625);
+ if (err)
+ return err;
+
+ mutex_init(&it6625->it6625_lock);
+ mutex_init(&it6625->edid_lock);
+ mutex_init(&it6625->if_read_lock);
+ mutex_init(&it6625->if_state_lock);
+ init_completion(&it6625->if_latched);
+ INIT_DELAYED_WORK(&it6625->hpd_delayed_work, it6625_hpd_delayed_work);
+ INIT_WORK(&it6625->polling_work, it6625_polling_work);
+
+ if (client->irq) {
+ err = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, it6625_irq_handler,
+ IRQF_ONESHOT |
+ IRQF_NO_AUTOEN,
+ "it6625", it6625);
+ if (err)
+ goto err_clean_work_queues;
+ } else {
+ dev_info(it6625->dev, "no IRQ, falling back to polling");
+ timer_setup(&it6625->timer, it6625_irq_poll_timer, 0);
+ }
+
+ sd = &it6625->sd;
+ err = it6625_init_v4l2_subdev(it6625);
+ if (err)
+ goto err_clean_work_queues;
+
+ err = v4l2_ctrl_handler_setup(sd->ctrl_handler);
+ if (err)
+ goto err_clean_hdl;
+
+ it6625->cec_adap = cec_allocate_adapter(&it6625_cec_adap_ops,
+ it6625, dev_name(it6625->dev),
+ CEC_CAP_DEFAULTS |
+ CEC_CAP_MONITOR_ALL |
+ CEC_CAP_PHYS_ADDR,
+ 1);
+ if (IS_ERR(it6625->cec_adap)) {
+ err = PTR_ERR(it6625->cec_adap);
+ dev_err(it6625->dev, "%s %d", __func__, __LINE__);
+ goto err_clean_hdl;
+ }
+
+ err = cec_register_adapter(it6625->cec_adap, &client->dev);
+ if (err < 0) {
+ dev_err(it6625->dev, "%s: failed to register the cec device", __func__);
+ cec_delete_adapter(it6625->cec_adap);
+ it6625->cec_adap = NULL;
+ goto err_clean_hdl;
+ }
+
+ it6625_debugfs_init(it6625, client);
+
+ it6625_initial_setup(it6625);
+ it6625_v4l2_sd_ctrl_update(sd);
+
+ err = v4l2_async_register_subdev(sd);
+ if (err < 0) {
+ dev_err(it6625->dev, "%s %d err=%d", __func__, __LINE__, err);
+ goto err_clean_debugfs;
+ }
+
+ if (client->irq)
+ enable_irq(client->irq);
+ else
+ mod_timer(&it6625->timer, jiffies + msecs_to_jiffies(POLL_INTERVAL_MS));
+
+ return 0;
+
+err_clean_debugfs:
+ v4l2_debugfs_if_free(it6625->infoframes);
+ debugfs_remove_recursive(it6625->debugfs_dir);
+ cec_unregister_adapter(it6625->cec_adap);
+err_clean_hdl:
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&it6625->hdl);
+
+err_clean_work_queues:
+ if (!client->irq)
+ timer_shutdown_sync(&it6625->timer);
+ cancel_work_sync(&it6625->polling_work);
+ cancel_delayed_work_sync(&it6625->hpd_delayed_work);
+ mutex_destroy(&it6625->it6625_lock);
+ mutex_destroy(&it6625->edid_lock);
+ mutex_destroy(&it6625->if_read_lock);
+ mutex_destroy(&it6625->if_state_lock);
+ return err;
+}
+
+static void it6625_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct it6625 *it6625 = sd_to_6625(sd);
+
+ v4l2_debugfs_if_free(it6625->infoframes);
+
+ if (client->irq)
+ disable_irq(client->irq);
+ else
+ timer_shutdown_sync(&it6625->timer);
+
+ cancel_work_sync(&it6625->polling_work);
+ cancel_delayed_work_sync(&it6625->hpd_delayed_work);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_device_unregister_subdev(sd);
+
+ debugfs_remove_recursive(it6625->debugfs_dir);
+ cec_unregister_adapter(it6625->cec_adap);
+ mutex_destroy(&it6625->it6625_lock);
+ mutex_destroy(&it6625->edid_lock);
+ mutex_destroy(&it6625->if_read_lock);
+ mutex_destroy(&it6625->if_state_lock);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(&it6625->hdl);
+}
+
+static const struct i2c_device_id it6625_id[] = {
+ { .name = "it6625", .driver_data = IT6625_CHIP },
+ { .name = "it6626", .driver_data = IT6626_CHIP },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, it6625_id);
+
+static const struct of_device_id it6625_of_match[] = {
+ { .compatible = "ite,it6625", .data = (void *)IT6625_CHIP },
+ { .compatible = "ite,it6626", .data = (void *)IT6626_CHIP },
+ {},
+};
+MODULE_DEVICE_TABLE(of, it6625_of_match);
+
+static struct i2c_driver it6625_driver = {
+ .driver = {
+ .name = "it6625",
+ .of_match_table = it6625_of_match,
+ },
+ .probe = it6625_probe,
+ .remove = it6625_remove,
+ .id_table = it6625_id,
+};
+module_i2c_driver(it6625_driver);
+
+MODULE_DESCRIPTION("iTE it6625/it6626 HDMI to MIPI CSI bridge driver");
+MODULE_AUTHOR("Hermes Wu <Hermes.wu@ite.com.tw>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/lt6911uxe.c b/drivers/media/i2c/lt6911uxe.c
index bdefdd157e69..c9da174dbfa6 100644
--- a/drivers/media/i2c/lt6911uxe.c
+++ b/drivers/media/i2c/lt6911uxe.c
@@ -384,6 +384,7 @@ static int lt6911uxe_disable_streams(struct v4l2_subdev *sd,
}
static int lt6911uxe_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -439,7 +440,7 @@ static int lt6911uxe_init_state(struct v4l2_subdev *sd,
: V4L2_SUBDEV_FORMAT_ACTIVE,
};
- return lt6911uxe_set_format(sd, sd_state, &fmt);
+ return lt6911uxe_set_format(sd, NULL, sd_state, &fmt);
}
static const struct v4l2_subdev_video_ops lt6911uxe_video_ops = {
@@ -562,7 +563,7 @@ static irqreturn_t lt6911uxe_threaded_irq_fn(int irq, void *dev_id)
* As a HDMI to CSI2 bridge, it needs to update the format in time
* when the HDMI source changes.
*/
- lt6911uxe_set_format(sd, state, &fmt);
+ lt6911uxe_set_format(sd, NULL, state, &fmt);
v4l2_subdev_unlock_state(state);
return IRQ_HANDLED;
diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c
index 79eab9045e24..050ad9405c5f 100644
--- a/drivers/media/i2c/max9286.c
+++ b/drivers/media/i2c/max9286.c
@@ -910,6 +910,7 @@ static int max9286_enum_mbus_code(struct v4l2_subdev *sd,
}
static int max9286_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/max96714.c b/drivers/media/i2c/max96714.c
index e3e625e6f11a..0c2ff62a77ea 100644
--- a/drivers/media/i2c/max96714.c
+++ b/drivers/media/i2c/max96714.c
@@ -327,6 +327,7 @@ static int max96714_disable_streams(struct v4l2_subdev *sd,
}
static int max96714_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/max96717.c b/drivers/media/i2c/max96717.c
index 72f021b1a7b9..ff1df1d84049 100644
--- a/drivers/media/i2c/max96717.c
+++ b/drivers/media/i2c/max96717.c
@@ -414,6 +414,7 @@ static int max96717_set_routing(struct v4l2_subdev *sd,
}
static int max96717_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ml86v7667.c b/drivers/media/i2c/ml86v7667.c
index 48b7d589df31..eda41a80d20c 100644
--- a/drivers/media/i2c/ml86v7667.c
+++ b/drivers/media/i2c/ml86v7667.c
@@ -298,7 +298,6 @@ static const struct v4l2_subdev_video_ops ml86v7667_subdev_video_ops = {
static const struct v4l2_subdev_pad_ops ml86v7667_subdev_pad_ops = {
.enum_mbus_code = ml86v7667_enum_mbus_code,
.get_fmt = ml86v7667_fill_fmt,
- .set_fmt = ml86v7667_fill_fmt,
.get_mbus_config = ml86v7667_get_mbus_config,
};
diff --git a/drivers/media/i2c/mt9m001.c b/drivers/media/i2c/mt9m001.c
index 0ade967b357b..fe46ea65550a 100644
--- a/drivers/media/i2c/mt9m001.c
+++ b/drivers/media/i2c/mt9m001.c
@@ -248,6 +248,7 @@ unlock:
}
static int mt9m001_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -289,6 +290,7 @@ static int mt9m001_set_selection(struct v4l2_subdev *sd,
}
static int mt9m001_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -343,6 +345,8 @@ static int mt9m001_get_fmt(struct v4l2_subdev *sd,
}
static int mt9m001_s_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
const struct mt9m001_datafmt *fmt,
struct v4l2_mbus_framefmt *mf)
{
@@ -359,7 +363,7 @@ static int mt9m001_s_fmt(struct v4l2_subdev *sd,
int ret;
/* No support for scaling so far, just crop. TODO: use skipping */
- ret = mt9m001_set_selection(sd, NULL, &sel);
+ ret = mt9m001_set_selection(sd, ci, state, &sel);
if (!ret) {
mf->width = mt9m001->rect.width;
mf->height = mt9m001->rect.height;
@@ -371,6 +375,7 @@ static int mt9m001_s_fmt(struct v4l2_subdev *sd,
}
static int mt9m001_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -404,7 +409,7 @@ static int mt9m001_set_fmt(struct v4l2_subdev *sd,
mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
- return mt9m001_s_fmt(sd, fmt, mf);
+ return mt9m001_s_fmt(sd, ci, sd_state, fmt, mf);
*v4l2_subdev_state_get_format(sd_state, 0) = *mf;
return 0;
}
diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c
index 4e748080b798..f911c1cb43e1 100644
--- a/drivers/media/i2c/mt9m111.c
+++ b/drivers/media/i2c/mt9m111.c
@@ -446,6 +446,7 @@ static int mt9m111_reset(struct mt9m111 *mt9m111)
}
static int mt9m111_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -490,6 +491,7 @@ static int mt9m111_set_selection(struct v4l2_subdev *sd,
}
static int mt9m111_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -617,6 +619,7 @@ static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111,
}
static int mt9m111_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/mt9m114.c b/drivers/media/i2c/mt9m114.c
index 848ea06e70ab..c6c950d5c8f3 100644
--- a/drivers/media/i2c/mt9m114.c
+++ b/drivers/media/i2c/mt9m114.c
@@ -1254,6 +1254,7 @@ static int mt9m114_pa_enum_framesizes(struct v4l2_subdev *sd,
}
static int mt9m114_pa_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -1281,6 +1282,7 @@ static int mt9m114_pa_set_fmt(struct v4l2_subdev *sd,
}
static int mt9m114_pa_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -1304,6 +1306,7 @@ static int mt9m114_pa_get_selection(struct v4l2_subdev *sd,
}
static int mt9m114_pa_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -1877,6 +1880,7 @@ static void mt9m114_ifp_update_sel_and_src_fmt(struct v4l2_subdev_state *state)
}
static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -1923,6 +1927,7 @@ static int mt9m114_ifp_set_fmt(struct v4l2_subdev *sd,
}
static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -1982,6 +1987,7 @@ static int mt9m114_ifp_get_selection(struct v4l2_subdev *sd,
}
static int mt9m114_ifp_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c
index 2b09e8315c8e..5b0a84c66043 100644
--- a/drivers/media/i2c/mt9p031.c
+++ b/drivers/media/i2c/mt9p031.c
@@ -580,6 +580,7 @@ static int mt9p031_get_format(struct v4l2_subdev *subdev,
}
static int mt9p031_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -618,6 +619,7 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev,
}
static int mt9p031_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -642,6 +644,7 @@ static int mt9p031_get_selection(struct v4l2_subdev *subdev,
}
static int mt9p031_set_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c
index b3a6c6d76632..7a47756b9356 100644
--- a/drivers/media/i2c/mt9t112.c
+++ b/drivers/media/i2c/mt9t112.c
@@ -872,6 +872,7 @@ static int mt9t112_set_params(struct mt9t112_priv *priv,
}
static int mt9t112_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -897,6 +898,7 @@ static int mt9t112_get_selection(struct v4l2_subdev *sd,
}
static int mt9t112_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -953,6 +955,7 @@ static int mt9t112_s_fmt(struct v4l2_subdev *sd,
}
static int mt9t112_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/mt9v011.c b/drivers/media/i2c/mt9v011.c
index ff55a8ff32b4..6ea31a4a6463 100644
--- a/drivers/media/i2c/mt9v011.c
+++ b/drivers/media/i2c/mt9v011.c
@@ -336,6 +336,7 @@ static int mt9v011_enum_mbus_code(struct v4l2_subdev *sd,
}
static int mt9v011_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c
index 5113826534d7..29823378f0c7 100644
--- a/drivers/media/i2c/mt9v032.c
+++ b/drivers/media/i2c/mt9v032.c
@@ -502,6 +502,7 @@ static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output)
}
static int mt9v032_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -546,6 +547,7 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev,
}
static int mt9v032_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -560,6 +562,7 @@ static int mt9v032_get_selection(struct v4l2_subdev *subdev,
}
static int mt9v032_set_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/mt9v111.c b/drivers/media/i2c/mt9v111.c
index 64a758c95ab7..6e9b3e190bca 100644
--- a/drivers/media/i2c/mt9v111.c
+++ b/drivers/media/i2c/mt9v111.c
@@ -886,6 +886,7 @@ static int mt9v111_get_format(struct v4l2_subdev *subdev,
}
static int mt9v111_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/og01a1b.c b/drivers/media/i2c/og01a1b.c
index 1675f0460969..b207b1d853a4 100644
--- a/drivers/media/i2c/og01a1b.c
+++ b/drivers/media/i2c/og01a1b.c
@@ -677,6 +677,7 @@ static int og01a1b_disable_streams(struct v4l2_subdev *sd,
}
static int og01a1b_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -763,7 +764,7 @@ static int og01a1b_init_state(struct v4l2_subdev *sd,
},
};
- og01a1b_set_format(sd, state, &fmt);
+ og01a1b_set_format(sd, NULL, state, &fmt);
return 0;
}
@@ -956,6 +957,11 @@ static void og01a1b_remove(struct i2c_client *client)
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(sd->ctrl_handler);
pm_runtime_disable(og01a1b->dev);
+
+ if (!pm_runtime_status_suspended(og01a1b->dev)) {
+ og01a1b_power_off(og01a1b->dev);
+ pm_runtime_set_suspended(og01a1b->dev);
+ }
}
static int og01a1b_probe(struct i2c_client *client)
diff --git a/drivers/media/i2c/og0ve1b.c b/drivers/media/i2c/og0ve1b.c
index 84a28cdcade1..84682389d989 100644
--- a/drivers/media/i2c/og0ve1b.c
+++ b/drivers/media/i2c/og0ve1b.c
@@ -481,6 +481,7 @@ static int og0ve1b_disable_streams(struct v4l2_subdev *sd,
}
static int og0ve1b_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -544,7 +545,7 @@ static int og0ve1b_init_state(struct v4l2_subdev *sd,
},
};
- og0ve1b_set_pad_format(sd, state, &fmt);
+ og0ve1b_set_pad_format(sd, NULL, state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/os02g10.c b/drivers/media/i2c/os02g10.c
new file mode 100644
index 000000000000..141b7cb11fc5
--- /dev/null
+++ b/drivers/media/i2c/os02g10.c
@@ -0,0 +1,934 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * V4L2 Support for the OS02G10
+ *
+ * Copyright (C) 2026 Silicon Signals Pvt. Ltd.
+ *
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitops.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.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/units.h>
+#include <linux/types.h>
+#include <linux/time.h>
+
+#include <media/v4l2-cci.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mediabus.h>
+
+#define OS02G10_XCLK_FREQ_24MHZ (24 * HZ_PER_MHZ)
+
+/* Page 0 */
+#define OS02G10_REG_CHIPID CCI_REG24(0x002)
+#define OS02G10_CHIPID 0x560247
+
+#define OS02G10_REG_PLL_DIV_CTRL CCI_REG8(0x030)
+#define OS02G10_REG_PLL_DCTL_BIAS_CTRL CCI_REG8(0x035)
+#define OS02G10_REG_GATE_EN_CTRL CCI_REG8(0x038)
+#define OS02G10_REG_DPLL_NC CCI_REG8(0x041)
+#define OS02G10_REG_MP_PHASE_CTRL CCI_REG8(0x044)
+
+/* Page 1 */
+#define OS02G10_REG_FRAME_SYNC CCI_REG8(0x101)
+
+#define OS02G10_REG_LONG_EXPOSURE CCI_REG16(0x103)
+#define OS02G10_EXPOSURE_MIN 4
+#define OS02G10_EXPOSURE_STEP 1
+#define OS02G10_EXPOSURE_MARGIN 9
+
+#define OS02G10_REG_HBLANK CCI_REG16(0x109)
+
+#define OS02G10_REG_FRAME_TEST_CTRL CCI_REG8(0x10d)
+#define OS02G10_FRAME_EXP_SEPERATE_EN BIT(4)
+#define OS02G10_TEST_PATTERN_ENABLE BIT(0)
+
+#define OS02G10_REG_FRAME_LENGTH CCI_REG16(0x10e)
+#define OS02G10_FRAME_LENGTH_MAX (BIT(16) - 1)
+
+#define OS02G10_REG_ANALOG_GAIN CCI_REG8(0x124)
+#define OS02G10_ANALOG_GAIN_MIN 16
+#define OS02G10_ANALOG_GAIN_MAX 248
+#define OS02G10_ANALOG_GAIN_STEP 1
+#define OS02G10_ANALOG_GAIN_DEFAULT 16
+
+#define OS02G10_REG_DIGITAL_GAIN_H CCI_REG8(0x137)
+#define OS02G10_REG_DIGITAL_GAIN_L CCI_REG8(0x139)
+#define OS02G10_DIGITAL_GAIN_MIN 64
+#define OS02G10_DIGITAL_GAIN_MAX 2048
+#define OS02G10_DIGITAL_GAIN_STEP 64
+#define OS02G10_DIGITAL_GAIN_DEFAULT 64
+
+#define OS02G10_REG_ULP_PWD_DUMMY_CTRL CCI_REG8(0x13c)
+
+#define OS02G10_REG_FLIP_MIRROR CCI_REG8(0x13f)
+#define OS02G10_FLIP BIT(1)
+#define OS02G10_MIRROR BIT(0)
+
+#define OS02G10_REG_DC_LEVEL_LIMIT_EN CCI_REG8(0x146)
+#define OS02G10_REG_DC_LEVEL_LIMIT_L CCI_REG8(0x147)
+#define OS02G10_REG_BLC_DATA_LIMIT_L CCI_REG8(0x148)
+#define OS02G10_REG_DC_BLC_LIMIT_H CCI_REG8(0x149)
+
+#define OS02G10_REG_H_SIZE_MIPI CCI_REG16(0x18e)
+#define OS02G10_REG_V_SIZE_MIPI CCI_REG16(0x190)
+
+#define OS02G10_REG_HS_LP_CTRL CCI_REG8(0x192)
+#define OS02G10_REG_HS_LEVEL CCI_REG8(0x19d)
+#define OS02G10_REG_HS_DRV CCI_REG8(0x19e)
+
+#define OS02G10_REG_MIPI_TX_SPEED_CTRL CCI_REG8(0x1a1)
+
+#define OS02G10_REG_STREAM_CTRL CCI_REG8(0x1b1)
+#define OS02G10_STREAM_CTRL_ON 0x03
+#define OS02G10_STREAM_CTRL_OFF 0x00
+
+#define OS02G10_REG_GB_SUBOFFSET CCI_REG8(0x1f0)
+#define OS02G10_REG_BLUE_SUBOFFSET CCI_REG8(0x1f1)
+#define OS02G10_REG_RED_SUBOFFSET CCI_REG8(0x1f2)
+#define OS02G10_REG_GR_SUBOFFSET CCI_REG8(0x1f3)
+
+#define OS02G10_REG_ABL_TRIGGER CCI_REG8(0x1fa)
+#define OS02G10_REG_ABL CCI_REG8(0x1fb)
+
+/* Page 2 */
+#define OS02G10_REG_SIF_CTRL CCI_REG8(0x25e)
+#define OS02G10_ORIENTATION_BAYER_FIX 0x32
+
+#define OS02G10_REG_V_START CCI_REG16(0x2a0)
+#define OS02G10_REG_V_SIZE CCI_REG16(0x2a2)
+#define OS02G10_REG_H_START CCI_REG16(0x2a4)
+#define OS02G10_REG_H_SIZE CCI_REG16(0x2a6)
+
+#define OS02G10_LINK_FREQ_720MHZ (720 * HZ_PER_MHZ)
+#define OS02G10_DATA_LANES 2
+
+/* OS02G10 native and active pixel array size */
+static const struct v4l2_rect os02g10_native_area = {
+ .top = 0,
+ .left = 0,
+ .width = 1928,
+ .height = 1088,
+};
+
+static const struct v4l2_rect os02g10_active_area = {
+ .top = 4,
+ .left = 4,
+ .width = 1920,
+ .height = 1080,
+};
+
+static const char * const os02g10_supply_name[] = {
+ "avdd", /* Analog power */
+ "dovdd", /* Digital I/O power */
+ "dvdd", /* Digital core power */
+};
+
+struct os02g10 {
+ struct device *dev;
+ struct regmap *cci;
+ struct v4l2_subdev sd;
+ struct media_pad pad;
+ struct clk *xclk;
+ struct gpio_desc *reset_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(os02g10_supply_name)];
+
+ /* V4L2 Controls */
+ struct v4l2_ctrl_handler handler;
+ struct v4l2_ctrl *vblank;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *hflip;
+};
+
+struct os02g10_mode {
+ u32 width;
+ u32 height;
+ u32 vts_def;
+ u32 exp_def;
+ u32 x_start;
+ u32 y_start;
+};
+
+static const struct cci_reg_sequence os02g10_common_regs[] = {
+ { OS02G10_REG_PLL_DIV_CTRL, 0x0a},
+ { OS02G10_REG_PLL_DCTL_BIAS_CTRL, 0x04},
+ { OS02G10_REG_GATE_EN_CTRL, 0x11},
+ { OS02G10_REG_DPLL_NC, 0x06},
+ { OS02G10_REG_MP_PHASE_CTRL, 0x20},
+ { CCI_REG8(0x119), 0x50},
+ { CCI_REG8(0x11a), 0x0c},
+ { CCI_REG8(0x11b), 0x0d},
+ { CCI_REG8(0x11c), 0x00},
+ { CCI_REG8(0x11d), 0x75},
+ { CCI_REG8(0x11e), 0x52},
+ { CCI_REG8(0x122), 0x14},
+ { CCI_REG8(0x125), 0x44},
+ { CCI_REG8(0x126), 0x0f},
+ { OS02G10_REG_ULP_PWD_DUMMY_CTRL, 0xca},
+ { CCI_REG8(0x13d), 0x4a},
+ { CCI_REG8(0x140), 0x0f},
+ { CCI_REG8(0x143), 0x38},
+ { OS02G10_REG_DC_LEVEL_LIMIT_EN, 0x01},
+ { OS02G10_REG_DC_LEVEL_LIMIT_L, 0x00},
+ { OS02G10_REG_DC_BLC_LIMIT_H, 0x32},
+ { CCI_REG8(0x150), 0x01},
+ { CCI_REG8(0x151), 0x28},
+ { CCI_REG8(0x152), 0x20},
+ { CCI_REG8(0x153), 0x03},
+ { CCI_REG8(0x157), 0x16},
+ { CCI_REG8(0x159), 0x01},
+ { CCI_REG8(0x15a), 0x01},
+ { CCI_REG8(0x15d), 0x04},
+ { CCI_REG8(0x16a), 0x04},
+ { CCI_REG8(0x16b), 0x03},
+ { CCI_REG8(0x16e), 0x28},
+ { CCI_REG8(0x171), 0xc2},
+ { CCI_REG8(0x172), 0x04},
+ { CCI_REG8(0x173), 0x38},
+ { CCI_REG8(0x174), 0x04},
+ { CCI_REG8(0x179), 0x00},
+ { CCI_REG8(0x17a), 0xb2},
+ { CCI_REG8(0x17b), 0x10},
+ { OS02G10_REG_HS_LP_CTRL, 0x02},
+ { OS02G10_REG_HS_LEVEL, 0x03},
+ { OS02G10_REG_HS_DRV, 0x55},
+ { CCI_REG8(0x1b8), 0x70},
+ { CCI_REG8(0x1b9), 0x70},
+ { CCI_REG8(0x1ba), 0x70},
+ { CCI_REG8(0x1bb), 0x70},
+ { CCI_REG8(0x1bc), 0x00},
+ { CCI_REG8(0x1c4), 0x6d},
+ { CCI_REG8(0x1c5), 0x6d},
+ { CCI_REG8(0x1c6), 0x6d},
+ { CCI_REG8(0x1c7), 0x6d},
+ { CCI_REG8(0x1cc), 0x11},
+ { CCI_REG8(0x1cd), 0xe0},
+ { CCI_REG8(0x1d0), 0x1b},
+ { CCI_REG8(0x1d2), 0x76},
+ { CCI_REG8(0x1d3), 0x68},
+ { CCI_REG8(0x1d4), 0x68},
+ { CCI_REG8(0x1d5), 0x73},
+ { CCI_REG8(0x1d6), 0x73},
+ { CCI_REG8(0x1e8), 0x55},
+ { OS02G10_REG_GB_SUBOFFSET, 0x40},
+ { OS02G10_REG_BLUE_SUBOFFSET, 0x40},
+ { OS02G10_REG_RED_SUBOFFSET, 0x40},
+ { OS02G10_REG_GR_SUBOFFSET, 0x40},
+ { OS02G10_REG_ABL_TRIGGER, 0x1c},
+ { OS02G10_REG_ABL, 0x33},
+ { CCI_REG8(0x1fc), 0x80},
+ { CCI_REG8(0x1fe), 0x80},
+ { CCI_REG8(0x303), 0x67},
+ { CCI_REG8(0x300), 0x59},
+ { CCI_REG8(0x304), 0x11},
+ { CCI_REG8(0x305), 0x04},
+ { CCI_REG8(0x306), 0x0c},
+ { CCI_REG8(0x307), 0x08},
+ { CCI_REG8(0x308), 0x08},
+ { CCI_REG8(0x309), 0x4f},
+ { CCI_REG8(0x30b), 0x08},
+ { CCI_REG8(0x30d), 0x26},
+ { CCI_REG8(0x30f), 0x00},
+ { CCI_REG8(0x234), 0xfe},
+ { OS02G10_REG_MIPI_TX_SPEED_CTRL, 0x05},
+};
+
+static const struct os02g10_mode supported_modes[] = {
+ {
+ .width = 1920,
+ .height = 1080,
+ .vts_def = 1246,
+ .exp_def = 1100,
+ .x_start = 2,
+ .y_start = 6,
+ },
+};
+
+static const s64 link_freq_menu_items[] = {
+ OS02G10_LINK_FREQ_720MHZ,
+};
+
+static const char * const os02g10_test_pattern_menu[] = {
+ "Disabled",
+ "Colorbar",
+};
+
+static inline struct os02g10 *to_os02g10(struct v4l2_subdev *sd)
+{
+ return container_of_const(sd, struct os02g10, sd);
+}
+
+static u32 os02g10_get_format_code(struct os02g10 *os02g10)
+{
+ static const u32 codes[2][2] = {
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SGBRG10_1X10, },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MEDIA_BUS_FMT_SRGGB10_1X10, },
+ };
+
+ return codes[os02g10->vflip->val][os02g10->hflip->val];
+}
+
+static int os02g10_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct os02g10 *os02g10 = container_of_const(ctrl->handler,
+ struct os02g10, handler);
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ state = v4l2_subdev_get_locked_active_state(&os02g10->sd);
+ fmt = v4l2_subdev_state_get_format(state, 0);
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ /* Honour the VBLANK limits when setting exposure */
+ s64 max = fmt->height + ctrl->val - OS02G10_EXPOSURE_MARGIN;
+
+ ret = __v4l2_ctrl_modify_range(os02g10->exposure,
+ os02g10->exposure->minimum, max,
+ os02g10->exposure->step,
+ os02g10->exposure->default_value);
+ if (ret)
+ return ret;
+ }
+
+ if (pm_runtime_get_if_active(os02g10->dev) == 0)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_EXPOSURE:
+ cci_write(os02g10->cci, OS02G10_REG_LONG_EXPOSURE,
+ ctrl->val, &ret);
+ break;
+ case V4L2_CID_ANALOGUE_GAIN:
+ cci_write(os02g10->cci, OS02G10_REG_ANALOG_GAIN,
+ ctrl->val, &ret);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ cci_write(os02g10->cci, OS02G10_REG_DIGITAL_GAIN_L,
+ (ctrl->val & 0xff), &ret);
+ cci_write(os02g10->cci, OS02G10_REG_DIGITAL_GAIN_H,
+ ((ctrl->val >> 8) & 0x7), &ret);
+ break;
+ case V4L2_CID_VBLANK: {
+ u64 vts = ctrl->val + fmt->height;
+
+ cci_update_bits(os02g10->cci, OS02G10_REG_FRAME_TEST_CTRL,
+ OS02G10_FRAME_EXP_SEPERATE_EN,
+ OS02G10_FRAME_EXP_SEPERATE_EN, &ret);
+ cci_write(os02g10->cci, OS02G10_REG_FRAME_LENGTH, vts, &ret);
+ break;
+ }
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ cci_write(os02g10->cci, OS02G10_REG_FLIP_MIRROR,
+ os02g10->hflip->val | os02g10->vflip->val << 1, &ret);
+ cci_write(os02g10->cci, OS02G10_REG_SIF_CTRL,
+ OS02G10_ORIENTATION_BAYER_FIX, &ret);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+ cci_update_bits(os02g10->cci,
+ OS02G10_REG_FRAME_TEST_CTRL,
+ OS02G10_TEST_PATTERN_ENABLE,
+ ctrl->val ? OS02G10_TEST_PATTERN_ENABLE : 0,
+ &ret);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ cci_write(os02g10->cci, OS02G10_REG_FRAME_SYNC, 0x01, &ret);
+
+ pm_runtime_put(os02g10->dev);
+
+ return ret;
+}
+
+static const struct v4l2_ctrl_ops os02g10_ctrl_ops = {
+ .s_ctrl = os02g10_set_ctrl,
+};
+
+static int os02g10_init_controls(struct os02g10 *os02g10)
+{
+ const struct os02g10_mode *mode = &supported_modes[0];
+ struct v4l2_fwnode_device_properties props;
+ u64 vblank_def, exp_max, pixel_rate;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ struct v4l2_ctrl *link_freq;
+ int ret;
+
+ ret = v4l2_fwnode_device_parse(os02g10->dev, &props);
+ if (ret)
+ return ret;
+
+ ctrl_hdlr = &os02g10->handler;
+ v4l2_ctrl_handler_init(ctrl_hdlr, 11);
+
+ /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */
+ pixel_rate = div_u64(OS02G10_LINK_FREQ_720MHZ * 2 * OS02G10_DATA_LANES, 10);
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops, V4L2_CID_PIXEL_RATE, 0,
+ pixel_rate, 1, pixel_rate);
+
+ link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_LINK_FREQ,
+ ARRAY_SIZE(link_freq_menu_items) - 1,
+ 0, link_freq_menu_items);
+
+ vblank_def = mode->vts_def - mode->height;
+ os02g10->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_VBLANK, vblank_def,
+ OS02G10_FRAME_LENGTH_MAX - mode->height,
+ 1, vblank_def);
+
+ exp_max = mode->vts_def - OS02G10_EXPOSURE_MARGIN;
+ os02g10->exposure =
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_EXPOSURE,
+ OS02G10_EXPOSURE_MIN, exp_max,
+ OS02G10_EXPOSURE_STEP, mode->exp_def);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_ANALOGUE_GAIN, OS02G10_ANALOG_GAIN_MIN,
+ OS02G10_ANALOG_GAIN_MAX, OS02G10_ANALOG_GAIN_STEP,
+ OS02G10_ANALOG_GAIN_DEFAULT);
+
+ v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_DIGITAL_GAIN, OS02G10_DIGITAL_GAIN_MIN,
+ OS02G10_DIGITAL_GAIN_MAX, OS02G10_DIGITAL_GAIN_STEP,
+ OS02G10_DIGITAL_GAIN_DEFAULT);
+
+ os02g10->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ os02g10->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &os02g10_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(os02g10_test_pattern_menu) - 1,
+ 0, 0, os02g10_test_pattern_menu);
+
+ ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr,
+ &os02g10_ctrl_ops, &props);
+ if (ret)
+ goto err_handler_free;
+
+ if (ctrl_hdlr->error) {
+ ret = ctrl_hdlr->error;
+ goto err_handler_free;
+ }
+
+ link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ os02g10->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+ os02g10->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
+
+ os02g10->sd.ctrl_handler = ctrl_hdlr;
+
+ return 0;
+
+err_handler_free:
+ v4l2_ctrl_handler_free(ctrl_hdlr);
+
+ return ret;
+}
+
+static int os02g10_set_framefmt(struct os02g10 *os02g10,
+ struct v4l2_subdev_state *state)
+{
+ const struct v4l2_mbus_framefmt *format;
+ const struct os02g10_mode *mode;
+ int ret = 0;
+
+ format = v4l2_subdev_state_get_format(state, 0);
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes), width,
+ height, format->width, format->height);
+
+ cci_write(os02g10->cci, OS02G10_REG_V_START, mode->y_start, &ret);
+ cci_write(os02g10->cci, OS02G10_REG_V_SIZE, mode->height, &ret);
+ cci_write(os02g10->cci, OS02G10_REG_V_SIZE_MIPI, mode->height, &ret);
+ cci_write(os02g10->cci, OS02G10_REG_H_START, mode->x_start, &ret);
+ cci_write(os02g10->cci, OS02G10_REG_H_SIZE, mode->width, &ret);
+ cci_write(os02g10->cci, OS02G10_REG_H_SIZE_MIPI, mode->width, &ret);
+
+ return ret;
+}
+
+static int os02g10_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(os02g10->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = cci_multi_reg_write(os02g10->cci, os02g10_common_regs,
+ ARRAY_SIZE(os02g10_common_regs), NULL);
+ if (ret) {
+ dev_err(os02g10->dev, "failed to write common registers\n");
+ goto err_rpm_put;
+ }
+
+ ret = os02g10_set_framefmt(os02g10, state);
+ if (ret) {
+ dev_err(os02g10->dev, "failed to set frame foramt\n");
+ goto err_rpm_put;
+ }
+
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(os02g10->sd.ctrl_handler);
+ if (ret)
+ goto err_rpm_put;
+
+ ret = cci_write(os02g10->cci, OS02G10_REG_STREAM_CTRL,
+ OS02G10_STREAM_CTRL_ON, NULL);
+ if (ret)
+ goto err_rpm_put;
+
+ /* vflip and hflip cannot change during streaming */
+ __v4l2_ctrl_grab(os02g10->vflip, true);
+ __v4l2_ctrl_grab(os02g10->hflip, true);
+
+ return 0;
+
+err_rpm_put:
+ pm_runtime_put(os02g10->dev);
+ return ret;
+}
+
+static int os02g10_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ int ret;
+
+ ret = cci_write(os02g10->cci, OS02G10_REG_STREAM_CTRL,
+ OS02G10_STREAM_CTRL_OFF, NULL);
+ if (ret)
+ dev_err(os02g10->dev, "Failed to stop stream\n");
+
+ __v4l2_ctrl_grab(os02g10->vflip, false);
+ __v4l2_ctrl_grab(os02g10->hflip, false);
+
+ pm_runtime_put(os02g10->dev);
+
+ return ret;
+}
+
+static int os02g10_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r = os02g10_native_area;
+ return 0;
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sel->r = os02g10_active_area;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int os02g10_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ if (code->index)
+ return -EINVAL;
+
+ code->code = os02g10_get_format_code(os02g10);
+
+ return 0;
+}
+
+static int os02g10_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ if (fse->index >= ARRAY_SIZE(supported_modes))
+ return -EINVAL;
+
+ if (fse->code != os02g10_get_format_code(os02g10))
+ return -EINVAL;
+
+ fse->min_width = supported_modes[fse->index].width;
+ fse->max_width = fse->min_width;
+ fse->min_height = supported_modes[fse->index].height;
+ fse->max_height = fse->min_height;
+
+ return 0;
+}
+
+static int os02g10_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ struct v4l2_mbus_framefmt *format;
+ const struct os02g10_mode *mode;
+
+ format = v4l2_subdev_state_get_format(sd_state, 0);
+
+ mode = v4l2_find_nearest_size(supported_modes,
+ ARRAY_SIZE(supported_modes),
+ width, height,
+ fmt->format.width, fmt->format.height);
+
+ fmt->format.code = os02g10_get_format_code(os02g10);
+ fmt->format.width = mode->width;
+ fmt->format.height = mode->height;
+ fmt->format.field = V4L2_FIELD_NONE;
+ fmt->format.colorspace = V4L2_COLORSPACE_RAW;
+ fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ fmt->format.xfer_func = V4L2_XFER_FUNC_NONE;
+
+ *format = fmt->format;
+
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ u32 vblank_def = mode->vts_def - mode->height;
+
+ return __v4l2_ctrl_modify_range(os02g10->vblank, vblank_def,
+ OS02G10_FRAME_LENGTH_MAX -
+ mode->height, 1, vblank_def);
+ }
+
+ return 0;
+}
+
+static int os02g10_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_subdev_format fmt = {
+ .which = V4L2_SUBDEV_FORMAT_TRY,
+ .format = {
+ .width = supported_modes[0].width,
+ .height = supported_modes[0].height,
+ },
+ };
+
+ return os02g10_set_pad_format(sd, NULL, state, &fmt);
+}
+
+static const struct v4l2_subdev_video_ops os02g10_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_pad_ops os02g10_pad_ops = {
+ .enum_mbus_code = os02g10_enum_mbus_code,
+ .enum_frame_size = os02g10_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = os02g10_set_pad_format,
+ .get_selection = os02g10_get_selection,
+ .enable_streams = os02g10_enable_streams,
+ .disable_streams = os02g10_disable_streams,
+};
+
+static const struct v4l2_subdev_ops os02g10_subdev_ops = {
+ .video = &os02g10_video_ops,
+ .pad = &os02g10_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops os02g10_internal_ops = {
+ .init_state = os02g10_init_state,
+};
+
+static int os02g10_power_on(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct os02g10 *os02g10 = to_os02g10(sd);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(os02g10_supply_name),
+ os02g10->supplies);
+ if (ret) {
+ dev_err(os02g10->dev, "failed to enable regulators\n");
+ return ret;
+ }
+
+ /* Wait for T3/T4 timing requirements after supplies become stable */
+ fsleep(5 * USEC_PER_MSEC);
+
+ ret = clk_prepare_enable(os02g10->xclk);
+ if (ret)
+ goto err_regulator_off;
+
+ gpiod_set_value_cansleep(os02g10->reset_gpio, 0);
+
+ /* T5: delay from sensor power up stable to SCCB initialization */
+ fsleep(5 * USEC_PER_MSEC);
+
+ return 0;
+
+err_regulator_off:
+ regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
+
+ return ret;
+}
+
+static int os02g10_power_off(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ clk_disable_unprepare(os02g10->xclk);
+ gpiod_set_value_cansleep(os02g10->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(os02g10_supply_name), os02g10->supplies);
+
+ return 0;
+}
+
+static int os02g10_identify_module(struct os02g10 *os02g10)
+{
+ u64 chip_id;
+ int ret;
+
+ ret = cci_read(os02g10->cci, OS02G10_REG_CHIPID, &chip_id, NULL);
+ if (ret)
+ return dev_err_probe(os02g10->dev, ret,
+ "failed to read chip id %x\n",
+ OS02G10_CHIPID);
+
+ if (chip_id != OS02G10_CHIPID)
+ return dev_err_probe(os02g10->dev, -EIO,
+ "chip id mismatch: %x!=%llx\n",
+ OS02G10_CHIPID, chip_id);
+
+ return 0;
+}
+
+static int os02g10_parse_endpoint(struct os02g10 *os02g10)
+{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY,
+ };
+ unsigned long link_freq_bitmap;
+ struct fwnode_handle *ep;
+ int ret;
+
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(os02g10->dev), NULL);
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ fwnode_handle_put(ep);
+ if (ret)
+ return ret;
+
+ ret = v4l2_link_freq_to_bitmap(os02g10->dev, bus_cfg.link_frequencies,
+ bus_cfg.nr_of_link_frequencies,
+ link_freq_menu_items,
+ ARRAY_SIZE(link_freq_menu_items),
+ &link_freq_bitmap);
+
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+ return ret;
+};
+
+static const struct regmap_range_cfg os02g10_ranges[] = {
+ {
+ .range_min = 0x0000,
+ .range_max = 0x03ff,
+ .selector_reg = 0xfd,
+ .selector_mask = 0x03,
+ .selector_shift = 0,
+ .window_start = 0x00,
+ .window_len = 0x100,
+ },
+};
+
+static const struct regmap_config os02g10_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = 0x3ff,
+ .ranges = os02g10_ranges,
+ .num_ranges = ARRAY_SIZE(os02g10_ranges),
+ .disable_locking = true,
+};
+
+static int os02g10_probe(struct i2c_client *client)
+{
+ struct os02g10 *os02g10;
+ unsigned int xclk_freq;
+ int ret;
+
+ os02g10 = devm_kzalloc(&client->dev, sizeof(*os02g10), GFP_KERNEL);
+ if (!os02g10)
+ return -ENOMEM;
+
+ os02g10->dev = &client->dev;
+
+ v4l2_i2c_subdev_init(&os02g10->sd, client, &os02g10_subdev_ops);
+ os02g10->sd.internal_ops = &os02g10_internal_ops;
+
+ /*
+ * This is not using devm_cci_regmap_init_i2c(), because the driver
+ * makes use of regmap's pagination feature. The chosen settings are
+ * compatible with the CCI helpers.
+ */
+ os02g10->cci = devm_regmap_init_i2c(client, &os02g10_regmap_config);
+ if (IS_ERR(os02g10->cci))
+ return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->cci),
+ "failed to initialize CCI\n");
+
+ ret = os02g10_parse_endpoint(os02g10);
+ if (ret)
+ return dev_err_probe(os02g10->dev, ret,
+ "failed to parse endpoint configuration\n");
+
+ /* Get system clock (xvclk) */
+ os02g10->xclk = devm_v4l2_sensor_clk_get(os02g10->dev, NULL);
+ if (IS_ERR(os02g10->xclk))
+ return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->xclk),
+ "failed to get xclk\n");
+
+ xclk_freq = clk_get_rate(os02g10->xclk);
+ if (xclk_freq != OS02G10_XCLK_FREQ_24MHZ)
+ return dev_err_probe(os02g10->dev, -EINVAL,
+ "xclk frequency not supported: %u Hz\n",
+ xclk_freq);
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(os02g10_supply_name); i++)
+ os02g10->supplies[i].supply = os02g10_supply_name[i];
+
+ ret = devm_regulator_bulk_get(os02g10->dev,
+ ARRAY_SIZE(os02g10_supply_name),
+ os02g10->supplies);
+ if (ret)
+ return dev_err_probe(os02g10->dev, ret,
+ "failed to get regulators\n");
+
+ os02g10->reset_gpio = devm_gpiod_get_optional(os02g10->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(os02g10->reset_gpio))
+ return dev_err_probe(os02g10->dev, PTR_ERR(os02g10->reset_gpio),
+ "failed to get reset GPIO\n");
+
+ ret = os02g10_power_on(os02g10->dev);
+ if (ret)
+ return ret;
+
+ ret = os02g10_identify_module(os02g10);
+ if (ret)
+ goto error_power_off;
+
+ ret = os02g10_init_controls(os02g10);
+ if (ret)
+ goto error_power_off;
+
+ /* Initialize subdev */
+ os02g10->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ os02g10->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
+ os02g10->pad.flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&os02g10->sd.entity, 1, &os02g10->pad);
+ if (ret) {
+ dev_err_probe(os02g10->dev, ret, "failed to init entity pads\n");
+ goto error_handler_free;
+ }
+
+ os02g10->sd.state_lock = os02g10->handler.lock;
+ ret = v4l2_subdev_init_finalize(&os02g10->sd);
+ if (ret) {
+ dev_err_probe(os02g10->dev, ret, "subdev init error\n");
+ goto error_media_entity;
+ }
+
+ pm_runtime_set_active(os02g10->dev);
+ pm_runtime_enable(os02g10->dev);
+
+ ret = v4l2_async_register_subdev_sensor(&os02g10->sd);
+ if (ret) {
+ dev_err_probe(os02g10->dev, ret,
+ "failed to register os02g10 sub-device\n");
+ goto error_subdev_cleanup;
+ }
+
+ pm_runtime_idle(os02g10->dev);
+
+ return 0;
+
+error_subdev_cleanup:
+ v4l2_subdev_cleanup(&os02g10->sd);
+ pm_runtime_disable(os02g10->dev);
+ pm_runtime_set_suspended(os02g10->dev);
+
+error_media_entity:
+ media_entity_cleanup(&os02g10->sd.entity);
+
+error_handler_free:
+ v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
+
+error_power_off:
+ os02g10_power_off(os02g10->dev);
+
+ return ret;
+}
+
+static void os02g10_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct os02g10 *os02g10 = to_os02g10(sd);
+
+ v4l2_async_unregister_subdev(sd);
+ v4l2_subdev_cleanup(&os02g10->sd);
+ media_entity_cleanup(&sd->entity);
+ v4l2_ctrl_handler_free(os02g10->sd.ctrl_handler);
+
+ pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ os02g10_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(os02g10_pm_ops,
+ os02g10_power_off, os02g10_power_on, NULL);
+
+static const struct of_device_id os02g10_id[] = {
+ { .compatible = "ovti,os02g10" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, os02g10_id);
+
+static struct i2c_driver os02g10_driver = {
+ .driver = {
+ .name = "os02g10",
+ .pm = pm_ptr(&os02g10_pm_ops),
+ .of_match_table = os02g10_id,
+ },
+ .probe = os02g10_probe,
+ .remove = os02g10_remove,
+};
+module_i2c_driver(os02g10_driver);
+
+MODULE_DESCRIPTION("OS02G10 Camera Sensor Driver");
+MODULE_AUTHOR("Tarang Raval <tarang.raval@siliconsignals.io>");
+MODULE_AUTHOR("Elgin Perumbilly <elgin.perumbilly@siliconsignals.io>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/i2c/os05b10.c b/drivers/media/i2c/os05b10.c
index e0453c988e4a..a46cfa513e98 100644
--- a/drivers/media/i2c/os05b10.c
+++ b/drivers/media/i2c/os05b10.c
@@ -595,6 +595,7 @@ static int os05b10_set_framing_limits(struct os05b10 *os05b10,
}
static int os05b10_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -624,6 +625,7 @@ static int os05b10_set_pad_format(struct v4l2_subdev *sd,
}
static int os05b10_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov01a10.c b/drivers/media/i2c/ov01a10.c
index 8a29e5b4b6ba..b9a7dacd603e 100644
--- a/drivers/media/i2c/ov01a10.c
+++ b/drivers/media/i2c/ov01a10.c
@@ -634,6 +634,7 @@ static void ov01a10_update_blank_ctrls(struct ov01a10 *ov01a10,
}
static int ov01a10_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -715,6 +716,7 @@ static int ov01a10_enum_frame_size(struct v4l2_subdev *sd,
}
static int ov01a10_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -747,6 +749,7 @@ static int ov01a10_get_selection(struct v4l2_subdev *sd,
}
static int ov01a10_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov02a10.c b/drivers/media/i2c/ov02a10.c
index 0150e4d296af..375cd52d61b9 100644
--- a/drivers/media/i2c/ov02a10.c
+++ b/drivers/media/i2c/ov02a10.c
@@ -296,6 +296,7 @@ static void ov02a10_fill_fmt(const struct ov02a10_mode *mode,
}
static int ov02a10_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -523,7 +524,7 @@ static int ov02a10_init_state(struct v4l2_subdev *sd,
}
};
- ov02a10_set_fmt(sd, sd_state, &fmt);
+ ov02a10_set_fmt(sd, NULL, sd_state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/ov02c10.c b/drivers/media/i2c/ov02c10.c
index cf93d36032e1..d622f5dcac60 100644
--- a/drivers/media/i2c/ov02c10.c
+++ b/drivers/media/i2c/ov02c10.c
@@ -701,6 +701,7 @@ static int ov02c10_power_on(struct device *dev)
}
static int ov02c10_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov02e10.c b/drivers/media/i2c/ov02e10.c
index 4a64cba99991..5c1e7013f559 100644
--- a/drivers/media/i2c/ov02e10.c
+++ b/drivers/media/i2c/ov02e10.c
@@ -590,6 +590,7 @@ disable_clk:
}
static int ov02e10_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov08d10.c b/drivers/media/i2c/ov08d10.c
index 9adef5446a61..0710d2243cc0 100644
--- a/drivers/media/i2c/ov08d10.c
+++ b/drivers/media/i2c/ov08d10.c
@@ -1178,6 +1178,7 @@ static int ov08d10_set_stream(struct v4l2_subdev *sd, int enable)
}
static int ov08d10_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov08x40.c b/drivers/media/i2c/ov08x40.c
index 5eaf454f4763..6de0c17e633d 100644
--- a/drivers/media/i2c/ov08x40.c
+++ b/drivers/media/i2c/ov08x40.c
@@ -1844,6 +1844,7 @@ static int ov08x40_get_pad_format(struct v4l2_subdev *sd,
static int
ov08x40_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c
index 09d58e8b1c7f..de2b79a9a0e3 100644
--- a/drivers/media/i2c/ov13858.c
+++ b/drivers/media/i2c/ov13858.c
@@ -1345,6 +1345,7 @@ static int ov13858_get_pad_format(struct v4l2_subdev *sd,
static int
ov13858_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c
index b0d34141a13a..242a95834126 100644
--- a/drivers/media/i2c/ov13b10.c
+++ b/drivers/media/i2c/ov13b10.c
@@ -1118,6 +1118,7 @@ static int ov13b10_get_pad_format(struct v4l2_subdev *sd,
static int
ov13b10_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c
index 50feb608b92b..3dbefac6d305 100644
--- a/drivers/media/i2c/ov2640.c
+++ b/drivers/media/i2c/ov2640.c
@@ -938,6 +938,7 @@ static int ov2640_get_fmt(struct v4l2_subdev *sd,
}
static int ov2640_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -1028,6 +1029,7 @@ static int ov2640_enum_mbus_code(struct v4l2_subdev *sd,
}
static int ov2640_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c
index 7d8c7c3465a4..5bc30e7b8325 100644
--- a/drivers/media/i2c/ov2659.c
+++ b/drivers/media/i2c/ov2659.c
@@ -1080,6 +1080,7 @@ static void __ov2659_try_frame_size(struct v4l2_mbus_framefmt *mf,
}
static int ov2659_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c
index 5f1938c7a944..147862a813c5 100644
--- a/drivers/media/i2c/ov2680.c
+++ b/drivers/media/i2c/ov2680.c
@@ -639,6 +639,7 @@ static int ov2680_get_fmt(struct v4l2_subdev *sd,
}
static int ov2680_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -703,6 +704,7 @@ unlock:
}
static int ov2680_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -733,6 +735,7 @@ static int ov2680_get_selection(struct v4l2_subdev *sd,
}
static int ov2680_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c
index 4911a4eea126..162859a140eb 100644
--- a/drivers/media/i2c/ov2685.c
+++ b/drivers/media/i2c/ov2685.c
@@ -340,6 +340,7 @@ static void ov2685_fill_fmt(const struct ov2685_mode *mode,
}
static int ov2685_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -413,6 +414,7 @@ __ov2685_get_pad_crop(struct ov2685 *ov2685,
}
static int ov2685_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov2732.c b/drivers/media/i2c/ov2732.c
index 40035320fec6..57dd0dd3fd85 100644
--- a/drivers/media/i2c/ov2732.c
+++ b/drivers/media/i2c/ov2732.c
@@ -281,6 +281,7 @@ static int ov2732_enum_frame_size(struct v4l2_subdev *sd,
}
static int ov2732_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -317,6 +318,7 @@ static int ov2732_set_fmt(struct v4l2_subdev *sd,
}
static int ov2732_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -442,7 +444,7 @@ static int ov2732_init_state(struct v4l2_subdev *sd,
}
};
- return ov2732_set_fmt(sd, sd_state, &fmt);
+ return ov2732_set_fmt(sd, NULL, sd_state, &fmt);
}
static const struct v4l2_subdev_internal_ops ov2732_internal_ops = {
diff --git a/drivers/media/i2c/ov2735.c b/drivers/media/i2c/ov2735.c
index dcb1add1fd9f..0361904c4841 100644
--- a/drivers/media/i2c/ov2735.c
+++ b/drivers/media/i2c/ov2735.c
@@ -674,6 +674,7 @@ static int ov2735_disable_streams(struct v4l2_subdev *sd,
}
static int ov2735_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -742,6 +743,7 @@ static int ov2735_set_framing_limits(struct ov2735 *ov2735,
}
static int ov2735_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -792,7 +794,7 @@ static int ov2735_init_state(struct v4l2_subdev *sd,
},
};
- ov2735_set_pad_format(sd, state, &fmt);
+ ov2735_set_pad_format(sd, NULL, state, &fmt);
return 0;
}
@@ -1081,6 +1083,7 @@ static void ov2735_remove(struct i2c_client *client)
v4l2_subdev_cleanup(&ov2735->sd);
media_entity_cleanup(&sd->entity);
v4l2_ctrl_handler_free(ov2735->sd.ctrl_handler);
+ ov2735_power_off(ov2735->dev);
}
static DEFINE_RUNTIME_DEV_PM_OPS(ov2735_pm_ops,
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index 39003c1632ad..536325bb7b92 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -1023,6 +1023,7 @@ out_unlock:
}
static int ov2740_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov4689.c b/drivers/media/i2c/ov4689.c
index a59d25b09b5b..466b6f74d982 100644
--- a/drivers/media/i2c/ov4689.c
+++ b/drivers/media/i2c/ov4689.c
@@ -330,6 +330,7 @@ static void ov4689_fill_fmt(const struct ov4689_mode *mode,
}
static int ov4689_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -385,6 +386,7 @@ static int ov4689_enable_test_pattern(struct ov4689 *ov4689, u32 pattern)
}
static int ov4689_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index 8deb5f5501fa..22d08d0a90cf 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -2947,6 +2947,7 @@ static int ov5640_update_pixel_rate(struct ov5640_dev *sensor)
}
static int ov5640_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -2993,6 +2994,7 @@ out:
}
static int ov5640_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c
index c772ef6e51d2..a8d740ed4dc9 100644
--- a/drivers/media/i2c/ov5645.c
+++ b/drivers/media/i2c/ov5645.c
@@ -848,6 +848,7 @@ static int ov5645_enum_frame_size(struct v4l2_subdev *subdev,
}
static int ov5645_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -905,12 +906,13 @@ static int ov5645_init_state(struct v4l2_subdev *subdev,
},
};
- ov5645_set_format(subdev, sd_state, &fmt);
+ ov5645_set_format(subdev, NULL, sd_state, &fmt);
return 0;
}
static int ov5645_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c
index 3facf92b3841..e1120ca68828 100644
--- a/drivers/media/i2c/ov5647.c
+++ b/drivers/media/i2c/ov5647.c
@@ -787,6 +787,7 @@ static int ov5647_get_pad_fmt(struct v4l2_subdev *sd,
}
static int ov5647_set_pad_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -837,6 +838,7 @@ static int ov5647_set_pad_fmt(struct v4l2_subdev *sd,
}
static int ov5647_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1271,6 +1273,10 @@ static void ov5647_remove(struct i2c_client *client)
v4l2_ctrl_handler_free(&sensor->ctrls);
v4l2_device_unregister_subdev(sd);
pm_runtime_disable(&client->dev);
+ if (!pm_runtime_status_suspended(&client->dev)) {
+ ov5647_power_off(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ }
}
static const struct dev_pm_ops ov5647_pm_ops = {
diff --git a/drivers/media/i2c/ov5648.c b/drivers/media/i2c/ov5648.c
index f0b839cd65f1..6a1efa12c38d 100644
--- a/drivers/media/i2c/ov5648.c
+++ b/drivers/media/i2c/ov5648.c
@@ -2145,13 +2145,13 @@ static int ov5648_s_stream(struct v4l2_subdev *subdev, int enable)
mutex_lock(&sensor->mutex);
ret = ov5648_sw_standby(sensor, !enable);
+ if (!ret)
+ state->streaming = !!enable;
mutex_unlock(&sensor->mutex);
if (ret)
return ret;
- state->streaming = !!enable;
-
if (!enable)
pm_runtime_put(sensor->dev);
@@ -2215,6 +2215,7 @@ static int ov5648_get_fmt(struct v4l2_subdev *subdev,
}
static int ov5648_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c
index 01fa892de079..940f21fe0dd3 100644
--- a/drivers/media/i2c/ov5670.c
+++ b/drivers/media/i2c/ov5670.c
@@ -2285,6 +2285,7 @@ static int ov5670_get_pad_format(struct v4l2_subdev *sd,
}
static int ov5670_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -2553,6 +2554,7 @@ __ov5670_get_pad_crop(struct ov5670 *sensor, struct v4l2_subdev_state *state,
}
static int ov5670_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov5675.c b/drivers/media/i2c/ov5675.c
index 1c31b2a57eea..c5eb07576129 100644
--- a/drivers/media/i2c/ov5675.c
+++ b/drivers/media/i2c/ov5675.c
@@ -1015,6 +1015,7 @@ static int ov5675_power_on(struct device *dev)
}
static int ov5675_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1074,6 +1075,7 @@ static int ov5675_get_format(struct v4l2_subdev *sd,
}
static int ov5675_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov5693.c b/drivers/media/i2c/ov5693.c
index 4cc796bbee92..56599f9ca74b 100644
--- a/drivers/media/i2c/ov5693.c
+++ b/drivers/media/i2c/ov5693.c
@@ -35,6 +35,11 @@
#define OV5693_STOP_STREAMING 0x00
#define OV5693_SW_RESET 0x01
+/* MIPI transmitter control */
+#define OV5693_MIPI_CTRL00_REG CCI_REG8(0x4800)
+/* Gate the clock lane when there is no packet to transmit */
+#define OV5693_MIPI_CTRL00_CLOCK_LANE_GATE BIT(5)
+
#define OV5693_REG_CHIP_ID CCI_REG16(0x300a)
/* Yes, this is right. The datasheet for the OV5693 gives its ID as 0x5690 */
#define OV5693_CHIP_ID 0x5690
@@ -144,6 +149,9 @@ struct ov5693_device {
struct regulator_bulk_data supplies[OV5693_NUM_SUPPLIES];
struct clk *xvclk;
+ /* Gate the MIPI clock lane when idle (CSI-2 non-continuous clock) */
+ bool clock_ncont;
+
struct ov5693_mode {
struct v4l2_rect crop;
struct v4l2_mbus_framefmt format;
@@ -611,6 +619,19 @@ static int ov5693_enable_streaming(struct ov5693_device *ov5693, bool enable)
{
int ret = 0;
+ /*
+ * Gate the MIPI clock lane while idle if the CSI-2 link is configured
+ * for a non-continuous clock. Only that bit is touched, and only in
+ * that case, so the register keeps whatever the platform left in it
+ * and the clock stays free-running as before everywhere else. It
+ * needs no counterpart at stream off: the link is down by then, and
+ * the register returns to its default when the sensor is powered off.
+ */
+ if (enable && ov5693->clock_ncont)
+ cci_update_bits(ov5693->regmap, OV5693_MIPI_CTRL00_REG,
+ OV5693_MIPI_CTRL00_CLOCK_LANE_GATE,
+ OV5693_MIPI_CTRL00_CLOCK_LANE_GATE, &ret);
+
cci_write(ov5693->regmap, OV5693_SW_STREAM_REG,
enable ? OV5693_START_STREAMING : OV5693_STOP_STREAMING,
&ret);
@@ -807,6 +828,7 @@ static int ov5693_get_fmt(struct v4l2_subdev *sd,
}
static int ov5693_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -882,6 +904,7 @@ static int ov5693_set_fmt(struct v4l2_subdev *sd,
}
static int ov5693_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -915,6 +938,7 @@ static int ov5693_get_selection(struct v4l2_subdev *sd,
}
static int ov5693_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -1259,6 +1283,9 @@ static int ov5693_check_hwcfg(struct ov5693_device *ov5693)
goto out_free_bus_cfg;
}
+ ov5693->clock_ncont = bus_cfg.bus.mipi_csi2.flags &
+ V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;
+
out_free_bus_cfg:
v4l2_fwnode_endpoint_free(&bus_cfg);
@@ -1396,6 +1423,7 @@ static const struct dev_pm_ops ov5693_pm_ops = {
static const struct acpi_device_id ov5693_acpi_match[] = {
{"INT33BE"},
+ {"OVTI5693"},
{},
};
MODULE_DEVICE_TABLE(acpi, ov5693_acpi_match);
diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c
index 5bb6ce7b3237..5b23f04ab9f8 100644
--- a/drivers/media/i2c/ov5695.c
+++ b/drivers/media/i2c/ov5695.c
@@ -805,6 +805,7 @@ ov5695_find_best_fit(struct v4l2_subdev_format *fmt)
}
static int ov5695_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov6211.c b/drivers/media/i2c/ov6211.c
index 034d5d57d67e..2ad31b2c5249 100644
--- a/drivers/media/i2c/ov6211.c
+++ b/drivers/media/i2c/ov6211.c
@@ -459,6 +459,7 @@ static int ov6211_disable_streams(struct v4l2_subdev *sd,
}
static int ov6211_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -522,7 +523,7 @@ static int ov6211_init_state(struct v4l2_subdev *sd,
},
};
- ov6211_set_pad_format(sd, state, &fmt);
+ ov6211_set_pad_format(sd, NULL, state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/ov64a40.c b/drivers/media/i2c/ov64a40.c
index ed59b4818c55..b369c002a25a 100644
--- a/drivers/media/i2c/ov64a40.c
+++ b/drivers/media/i2c/ov64a40.c
@@ -3106,6 +3106,7 @@ static int ov64a40_enum_frame_size(struct v4l2_subdev *sd,
}
static int ov64a40_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -3137,6 +3138,7 @@ static int ov64a40_get_selection(struct v4l2_subdev *sd,
}
static int ov64a40_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c
index 311c61d9e25d..fce6344b5120 100644
--- a/drivers/media/i2c/ov7251.c
+++ b/drivers/media/i2c/ov7251.c
@@ -1213,6 +1213,7 @@ ov7251_find_mode_by_ival(struct ov7251 *ov7251, struct v4l2_fract *timeperframe)
}
static int ov7251_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -1295,12 +1296,13 @@ static int ov7251_init_state(struct v4l2_subdev *subdev,
}
};
- ov7251_set_format(subdev, sd_state, &fmt);
+ ov7251_set_format(subdev, NULL, sd_state, &fmt);
return 0;
}
static int ov7251_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c
index 4d040e9feeac..5a553d50f3a3 100644
--- a/drivers/media/i2c/ov7670.c
+++ b/drivers/media/i2c/ov7670.c
@@ -1097,6 +1097,7 @@ static int ov7670_apply_fmt(struct v4l2_subdev *sd)
* Set a format.
*/
static int ov7670_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index be3ba284ee0b..cb3f3f813837 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -122,7 +122,7 @@
#define LC_COEFB 0x4B /* Lens B channel compensation coefficient */
#define LC_COEFR 0x4C /* Lens R channel compensation coefficient */
-#define FIXGAIN 0x4D /* Analog fix gain amplifer */
+#define FIXGAIN 0x4D /* Analog fix gain amplifier */
#define AREF0 0x4E /* Sensor reference control */
#define AREF1 0x4F /* Sensor reference current control */
#define AREF2 0x50 /* Analog reference control */
@@ -1171,6 +1171,7 @@ ov772x_set_fmt_error:
}
static int ov772x_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1212,6 +1213,7 @@ static int ov772x_get_fmt(struct v4l2_subdev *sd,
}
static int ov772x_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c
index b4e14171556f..c8a74ce39841 100644
--- a/drivers/media/i2c/ov7740.c
+++ b/drivers/media/i2c/ov7740.c
@@ -766,6 +766,7 @@ static int ov7740_try_fmt_internal(struct v4l2_subdev *sd,
}
static int ov7740_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c
index 8bedb47cd7cf..42c4b9c4e380 100644
--- a/drivers/media/i2c/ov8856.c
+++ b/drivers/media/i2c/ov8856.c
@@ -2082,9 +2082,6 @@ static int ov8856_power_on(struct device *dev)
struct ov8856 *ov8856 = to_ov8856(sd);
int ret;
- if (is_acpi_node(dev_fwnode(dev)))
- return 0;
-
ret = clk_prepare_enable(ov8856->xvclk);
if (ret < 0) {
dev_err(dev, "failed to enable xvclk\n");
@@ -2120,9 +2117,6 @@ static int ov8856_power_off(struct device *dev)
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov8856 *ov8856 = to_ov8856(sd);
- if (is_acpi_node(dev_fwnode(dev)))
- return 0;
-
gpiod_set_value_cansleep(ov8856->reset_gpio, 1);
regulator_bulk_disable(ARRAY_SIZE(ov8856_supply_names),
ov8856->supplies);
@@ -2132,6 +2126,7 @@ static int ov8856_power_off(struct device *dev)
}
static int ov8856_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -2293,21 +2288,18 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856)
dev_warn(dev, "external clock rate %u is unsupported",
xvclk_rate);
- if (!is_acpi_node(fwnode)) {
- ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset",
- GPIOD_OUT_LOW);
- if (IS_ERR(ov8856->reset_gpio))
- return PTR_ERR(ov8856->reset_gpio);
+ ov8856->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ov8856->reset_gpio))
+ return PTR_ERR(ov8856->reset_gpio);
- for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++)
- ov8856->supplies[i].supply = ov8856_supply_names[i];
+ for (i = 0; i < ARRAY_SIZE(ov8856_supply_names); i++)
+ ov8856->supplies[i].supply = ov8856_supply_names[i];
- ret = devm_regulator_bulk_get(dev,
- ARRAY_SIZE(ov8856_supply_names),
- ov8856->supplies);
- if (ret)
- return ret;
- }
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ov8856_supply_names),
+ ov8856->supplies);
+ if (ret)
+ return ret;
ep = fwnode_graph_get_next_endpoint(fwnode, NULL);
if (!ep)
@@ -2327,7 +2319,8 @@ static int ov8856_get_hwcfg(struct ov8856 *ov8856)
goto check_hwcfg_error;
}
- dev_dbg(dev, "Using %u data lanes\n", ov8856->cur_mode->data_lanes);
+ dev_dbg(dev, "Using %u data lanes\n",
+ bus_cfg.bus.mipi_csi2.num_data_lanes);
if (bus_cfg.bus.mipi_csi2.num_data_lanes == 2)
ov8856->priv_lane = &lane_cfg_2;
diff --git a/drivers/media/i2c/ov8858.c b/drivers/media/i2c/ov8858.c
index 3f45f7fab833..0bfc4350a8c9 100644
--- a/drivers/media/i2c/ov8858.c
+++ b/drivers/media/i2c/ov8858.c
@@ -1409,6 +1409,7 @@ static const struct v4l2_subdev_video_ops ov8858_video_ops = {
*/
static int ov8858_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -1486,7 +1487,7 @@ static int ov8858_init_state(struct v4l2_subdev *sd,
},
};
- ov8858_set_fmt(sd, sd_state, &fmt);
+ ov8858_set_fmt(sd, NULL, sd_state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/ov8865.c b/drivers/media/i2c/ov8865.c
index 289a34a3eeb1..5a59cca8eeae 100644
--- a/drivers/media/i2c/ov8865.c
+++ b/drivers/media/i2c/ov8865.c
@@ -2702,6 +2702,7 @@ static int ov8865_get_fmt(struct v4l2_subdev *subdev,
}
static int ov8865_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -2810,6 +2811,7 @@ __ov8865_get_pad_crop(struct ov8865_sensor *sensor,
}
static int ov8865_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov9282.c b/drivers/media/i2c/ov9282.c
index 5d301660a87d..3204e8d60809 100644
--- a/drivers/media/i2c/ov9282.c
+++ b/drivers/media/i2c/ov9282.c
@@ -780,6 +780,7 @@ static int ov9282_get_pad_format(struct v4l2_subdev *sd,
}
static int ov9282_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -826,7 +827,7 @@ static int ov9282_init_state(struct v4l2_subdev *sd,
ov9282_fill_pad_format(ov9282, &supported_modes[DEFAULT_MODE],
ov9282->code, &fmt);
- return ov9282_set_pad_format(sd, sd_state, &fmt);
+ return ov9282_set_pad_format(sd, NULL, sd_state, &fmt);
}
static const struct v4l2_rect *
@@ -845,6 +846,7 @@ __ov9282_get_pad_crop(struct ov9282 *ov9282,
}
static int ov9282_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov9640.c b/drivers/media/i2c/ov9640.c
index 122f411044ce..d24b3be1e0b6 100644
--- a/drivers/media/i2c/ov9640.c
+++ b/drivers/media/i2c/ov9640.c
@@ -519,6 +519,7 @@ static int ov9640_s_fmt(struct v4l2_subdev *sd,
}
static int ov9640_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -563,6 +564,7 @@ static int ov9640_enum_mbus_code(struct v4l2_subdev *sd,
}
static int ov9640_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c
index 5c85db8a4a38..b596cfb2d7f3 100644
--- a/drivers/media/i2c/ov9650.c
+++ b/drivers/media/i2c/ov9650.c
@@ -1226,6 +1226,7 @@ static void __ov965x_try_frame_size(struct v4l2_mbus_framefmt *mf,
}
static int ov965x_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/ov9734.c b/drivers/media/i2c/ov9734.c
index 0eaf33807fc9..8f39609ad1b6 100644
--- a/drivers/media/i2c/ov9734.c
+++ b/drivers/media/i2c/ov9734.c
@@ -681,6 +681,7 @@ static int ov9734_set_stream(struct v4l2_subdev *sd, int enable)
}
static int ov9734_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/rdacm20.c b/drivers/media/i2c/rdacm20.c
index 52e8e2620b4d..0179508626b5 100644
--- a/drivers/media/i2c/rdacm20.c
+++ b/drivers/media/i2c/rdacm20.c
@@ -442,7 +442,6 @@ static const struct v4l2_subdev_video_ops rdacm20_video_ops = {
static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
.enum_mbus_code = rdacm20_enum_mbus_code,
.get_fmt = rdacm20_get_fmt,
- .set_fmt = rdacm20_get_fmt,
};
static const struct v4l2_subdev_ops rdacm20_subdev_ops = {
diff --git a/drivers/media/i2c/rdacm21.c b/drivers/media/i2c/rdacm21.c
index ece8a410e7ce..68d2b9d83c3c 100644
--- a/drivers/media/i2c/rdacm21.c
+++ b/drivers/media/i2c/rdacm21.c
@@ -322,7 +322,6 @@ static const struct v4l2_subdev_video_ops rdacm21_video_ops = {
static const struct v4l2_subdev_pad_ops rdacm21_subdev_pad_ops = {
.enum_mbus_code = rdacm21_enum_mbus_code,
.get_fmt = rdacm21_get_fmt,
- .set_fmt = rdacm21_get_fmt,
};
static const struct v4l2_subdev_ops rdacm21_subdev_ops = {
diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c
index 23352d71a108..211f890a9cbd 100644
--- a/drivers/media/i2c/rj54n1cb0c.c
+++ b/drivers/media/i2c/rj54n1cb0c.c
@@ -541,6 +541,7 @@ static int rj54n1_sensor_scale(struct v4l2_subdev *sd, s32 *in_w, s32 *in_h,
s32 *out_w, s32 *out_h);
static int rj54n1_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -578,6 +579,7 @@ static int rj54n1_set_selection(struct v4l2_subdev *sd,
}
static int rj54n1_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -973,6 +975,7 @@ static int rj54n1_reg_init(struct i2c_client *client)
}
static int rj54n1_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
index 551387cea521..680add1da244 100644
--- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c
+++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c
@@ -1069,6 +1069,7 @@ static int s5c73m3_oif_get_fmt(struct v4l2_subdev *sd,
}
static int s5c73m3_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1108,6 +1109,7 @@ static int s5c73m3_set_fmt(struct v4l2_subdev *sd,
}
static int s5c73m3_oif_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/s5k3m5.c b/drivers/media/i2c/s5k3m5.c
index c591b580d2e7..bdf5bea78685 100644
--- a/drivers/media/i2c/s5k3m5.c
+++ b/drivers/media/i2c/s5k3m5.c
@@ -958,6 +958,7 @@ static void s5k3m5_update_pad_format(struct s5k3m5 *s5k3m5,
}
static int s5k3m5_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -1038,6 +1039,7 @@ static int s5k3m5_enum_frame_size(struct v4l2_subdev *sd,
}
static int s5k3m5_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1075,7 +1077,7 @@ static int s5k3m5_init_state(struct v4l2_subdev *sd,
},
};
- s5k3m5_set_pad_format(sd, state, &fmt);
+ s5k3m5_set_pad_format(sd, NULL, state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c
index 378d273055ee..b13c4754ec3c 100644
--- a/drivers/media/i2c/s5k5baf.c
+++ b/drivers/media/i2c/s5k5baf.c
@@ -1307,6 +1307,7 @@ static int s5k5baf_get_fmt(struct v4l2_subdev *sd,
}
static int s5k5baf_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1370,6 +1371,7 @@ static int s5k5baf_is_bound_target(u32 target)
}
static int s5k5baf_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1463,6 +1465,7 @@ static bool s5k5baf_cmp_rect(const struct v4l2_rect *r1,
}
static int s5k5baf_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/i2c/s5k6a3.c b/drivers/media/i2c/s5k6a3.c
index ba6477e88da3..071b31728a00 100644
--- a/drivers/media/i2c/s5k6a3.c
+++ b/drivers/media/i2c/s5k6a3.c
@@ -131,6 +131,7 @@ static struct v4l2_mbus_framefmt *__s5k6a3_get_format(
}
static int s5k6a3_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/s5kjn1.c b/drivers/media/i2c/s5kjn1.c
index a707cb740556..80d713ba3a1e 100644
--- a/drivers/media/i2c/s5kjn1.c
+++ b/drivers/media/i2c/s5kjn1.c
@@ -985,6 +985,7 @@ static void s5kjn1_update_pad_format(struct s5kjn1 *s5kjn1,
}
static int s5kjn1_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -1065,6 +1066,7 @@ static int s5kjn1_enum_frame_size(struct v4l2_subdev *sd,
}
static int s5kjn1_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1102,7 +1104,7 @@ static int s5kjn1_init_state(struct v4l2_subdev *sd,
},
};
- s5kjn1_set_pad_format(sd, state, &fmt);
+ s5kjn1_set_pad_format(sd, NULL, state, &fmt);
return 0;
}
diff --git a/drivers/media/i2c/saa6752hs.c b/drivers/media/i2c/saa6752hs.c
index c6bf0b0902e8..6e2a3e4a63f7 100644
--- a/drivers/media/i2c/saa6752hs.c
+++ b/drivers/media/i2c/saa6752hs.c
@@ -563,6 +563,7 @@ static int saa6752hs_get_fmt(struct v4l2_subdev *sd,
}
static int saa6752hs_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c
index d20695238b48..88b159418c7c 100644
--- a/drivers/media/i2c/saa7115.c
+++ b/drivers/media/i2c/saa7115.c
@@ -1159,6 +1159,7 @@ static int saa711x_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_f
}
static int saa711x_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/saa717x.c b/drivers/media/i2c/saa717x.c
index 0536ceb54650..367539667948 100644
--- a/drivers/media/i2c/saa717x.c
+++ b/drivers/media/i2c/saa717x.c
@@ -980,6 +980,7 @@ static int saa717x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_regi
#endif
static int saa717x_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/st-mipid02.c b/drivers/media/i2c/st-mipid02.c
index 4675181af5fb..f9eabd9632d3 100644
--- a/drivers/media/i2c/st-mipid02.c
+++ b/drivers/media/i2c/st-mipid02.c
@@ -597,6 +597,7 @@ static int mipid02_enum_mbus_code(struct v4l2_subdev *sd,
}
static int mipid02_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/t4ka3.c b/drivers/media/i2c/t4ka3.c
index a5a68e3fbec2..6506225683bb 100644
--- a/drivers/media/i2c/t4ka3.c
+++ b/drivers/media/i2c/t4ka3.c
@@ -363,6 +363,7 @@ static void t4ka3_get_vblank_limits(struct t4ka3_data *sensor,
}
static int t4ka3_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -626,6 +627,7 @@ static int t4ka3_disable_stream(struct v4l2_subdev *sd,
}
static int t4ka3_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -651,6 +653,7 @@ static int t4ka3_get_selection(struct v4l2_subdev *sd,
}
static int t4ka3_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -1034,7 +1037,6 @@ err_media_entity:
err_pm_disable:
pm_runtime_disable(&client->dev);
- pm_runtime_put_noidle(&client->dev);
t4ka3_pm_suspend(&client->dev);
return ret;
diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c
index fbd38bbfee03..d9cd0563d50b 100644
--- a/drivers/media/i2c/tc358743.c
+++ b/drivers/media/i2c/tc358743.c
@@ -453,7 +453,7 @@ static void tc358743_enable_edid(struct v4l2_subdev *sd)
/* Enable hotplug after 143 ms. DDC access to EDID is also enabled when
* hotplug is enabled. See register DDC_CTL */
- schedule_delayed_work(&state->delayed_work_enable_hotplug, HZ / 7);
+ schedule_delayed_work(&state->delayed_work_enable_hotplug, V4L2_SET_EDID_HPD_LOW_JIFFIES);
tc358743_enable_interrupts(sd, true);
tc358743_s_ctrl_detect_tx_5v(sd);
@@ -1373,7 +1373,8 @@ static int tc358743_log_status(struct v4l2_subdev *sd)
static const char * const input_color_space[] = {
"RGB", "YCbCr 601", "opRGB", "YCbCr 709", "NA (4)",
"xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601",
- "NA(10)", "NA(11)", "NA(12)", "opYCC 601"};
+ "NA(10)", "NA(11)", "NA(12)", "opYCC 601", "NA(14)",
+ "NA(15)"};
v4l2_info(sd, "-----Chip status-----\n");
v4l2_info(sd, "Chip ID: 0x%02x\n",
@@ -1816,6 +1817,7 @@ static int tc358743_get_fmt(struct v4l2_subdev *sd,
}
static int tc358743_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c
index 86d9ba3ea4e5..d55539c35a26 100644
--- a/drivers/media/i2c/tc358746.c
+++ b/drivers/media/i2c/tc358746.c
@@ -873,6 +873,7 @@ static int tc358746_enum_mbus_code(struct v4l2_subdev *sd,
}
static int tc358746_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c
index afa1d6f34c9c..30dfd8a7b42b 100644
--- a/drivers/media/i2c/tda1997x.c
+++ b/drivers/media/i2c/tda1997x.c
@@ -590,7 +590,7 @@ static void tda1997x_enable_edid(struct v4l2_subdev *sd)
v4l2_dbg(1, debug, sd, "%s\n", __func__);
/* Enable hotplug after 143ms */
- schedule_delayed_work(&state->delayed_work_enable_hpd, HZ / 7);
+ schedule_delayed_work(&state->delayed_work_enable_hpd, V4L2_SET_EDID_HPD_LOW_JIFFIES);
}
/* -----------------------------------------------------------------------------
@@ -1798,6 +1798,7 @@ static int tda1997x_get_format(struct v4l2_subdev *sd,
}
static int tda1997x_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/thp7312.c b/drivers/media/i2c/thp7312.c
index 775cfba188d8..f1c7f149c06c 100644
--- a/drivers/media/i2c/thp7312.c
+++ b/drivers/media/i2c/thp7312.c
@@ -729,6 +729,7 @@ static int thp7312_enum_frame_interval(struct v4l2_subdev *sd,
}
static int thp7312_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/ths7303.c b/drivers/media/i2c/ths7303.c
index fa5b9c884c1d..e04da5519a39 100644
--- a/drivers/media/i2c/ths7303.c
+++ b/drivers/media/i2c/ths7303.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ths7303/53- THS7303/53 Video Amplifier driver
*
@@ -10,15 +11,6 @@
* Hans Verkuil <hverkuil@kernel.org>
* Lad, Prabhakar <prabhakar.lad@ti.com>
* Martin Bugge <marbugge@cisco.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/i2c.h>
diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c
index 808ef16ec3b3..00535958faf1 100644
--- a/drivers/media/i2c/ths8200.c
+++ b/drivers/media/i2c/ths8200.c
@@ -1,20 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ths8200 - Texas Instruments THS8200 video encoder driver
*
* Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/i2c.h>
diff --git a/drivers/media/i2c/ths8200_regs.h b/drivers/media/i2c/ths8200_regs.h
index 6bc9fd1111db..6a96b6a94406 100644
--- a/drivers/media/i2c/ths8200_regs.h
+++ b/drivers/media/i2c/ths8200_regs.h
@@ -1,20 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ths8200 - Texas Instruments THS8200 video encoder driver
*
* Copyright 2013 Cisco Systems, Inc. and/or its affiliates.
- *
- * This program is free software; you may redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation version 2.
- *
- * This program is distributed .as is. WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef THS8200_REGS_H
diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c
index 99ace2acdb35..1e927896a4b3 100644
--- a/drivers/media/i2c/tvp514x.c
+++ b/drivers/media/i2c/tvp514x.c
@@ -879,6 +879,7 @@ static int tvp514x_get_pad_format(struct v4l2_subdev *sd,
}
static int tvp514x_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c
index 9c204f38935d..91064f06f8c1 100644
--- a/drivers/media/i2c/tvp5150.c
+++ b/drivers/media/i2c/tvp5150.c
@@ -1104,6 +1104,7 @@ static void tvp5150_set_hw_selection(struct v4l2_subdev *sd,
}
static int tvp5150_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1156,6 +1157,7 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd,
}
static int tvp5150_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1724,7 +1726,6 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = {
.enum_mbus_code = tvp5150_enum_mbus_code,
.enum_frame_size = tvp5150_enum_frame_size,
- .set_fmt = tvp5150_fill_fmt,
.get_fmt = tvp5150_fill_fmt,
.get_selection = tvp5150_get_selection,
.set_selection = tvp5150_set_selection,
diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c
index 3979ccde5a95..ada28cb8af7c 100644
--- a/drivers/media/i2c/tvp7002.c
+++ b/drivers/media/i2c/tvp7002.c
@@ -854,6 +854,7 @@ tvp7002_get_pad_format(struct v4l2_subdev *sd,
*/
static int
tvp7002_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/tw9900.c b/drivers/media/i2c/tw9900.c
index 617fcf7f0b45..60c102aba750 100644
--- a/drivers/media/i2c/tw9900.c
+++ b/drivers/media/i2c/tw9900.c
@@ -198,6 +198,7 @@ static int tw9900_get_fmt(struct v4l2_subdev *sd,
}
static int tw9900_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/i2c/tw9910.c b/drivers/media/i2c/tw9910.c
index 872207e688bf..731c97a40c82 100644
--- a/drivers/media/i2c/tw9910.c
+++ b/drivers/media/i2c/tw9910.c
@@ -715,6 +715,7 @@ tw9910_set_fmt_error:
}
static int tw9910_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -792,6 +793,7 @@ static int tw9910_s_fmt(struct v4l2_subdev *sd,
}
static int tw9910_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/i2c/vd55g1.c b/drivers/media/i2c/vd55g1.c
index 6f458f611f63..22b1497e8851 100644
--- a/drivers/media/i2c/vd55g1.c
+++ b/drivers/media/i2c/vd55g1.c
@@ -1255,6 +1255,7 @@ static int vd55g1_patch(struct vd55g1 *sensor)
}
static int vd55g1_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1332,6 +1333,7 @@ static int vd55g1_new_format_change_controls(struct vd55g1 *sensor,
}
static int vd55g1_set_pad_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sd_fmt)
{
@@ -1402,7 +1404,7 @@ static int vd55g1_init_state(struct v4l2_subdev *sd,
fmt.format.width = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].width;
fmt.format.height = vd55g1_supported_modes[VD55G1_MODE_IDX_DEF].height;
- return vd55g1_set_pad_fmt(sd, sd_state, &fmt);
+ return vd55g1_set_pad_fmt(sd, NULL, sd_state, &fmt);
}
static int vd55g1_enum_frame_size(struct v4l2_subdev *sd,
diff --git a/drivers/media/i2c/vd56g3.c b/drivers/media/i2c/vd56g3.c
index 157acea9e286..ca0dd1b24072 100644
--- a/drivers/media/i2c/vd56g3.c
+++ b/drivers/media/i2c/vd56g3.c
@@ -823,6 +823,7 @@ static void vd56g3_update_img_pad_format(struct vd56g3 *sensor,
}
static int vd56g3_set_pad_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sd_fmt)
{
@@ -858,6 +859,7 @@ static int vd56g3_set_pad_fmt(struct v4l2_subdev *sd,
}
static int vd56g3_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1038,7 +1040,7 @@ static int vd56g3_init_state(struct v4l2_subdev *sd,
},
};
- return vd56g3_set_pad_fmt(sd, sd_state, &fmt);
+ return vd56g3_set_pad_fmt(sd, NULL, sd_state, &fmt);
}
static const struct v4l2_subdev_video_ops vd56g3_video_ops = {
diff --git a/drivers/media/i2c/vgxy61.c b/drivers/media/i2c/vgxy61.c
index 3fb2166c81ef..e7819691723d 100644
--- a/drivers/media/i2c/vgxy61.c
+++ b/drivers/media/i2c/vgxy61.c
@@ -652,6 +652,7 @@ static int vgxy61_try_fmt_internal(struct v4l2_subdev *sd,
}
static int vgxy61_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1197,6 +1198,7 @@ static int vgxy61_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
}
static int vgxy61_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -1260,7 +1262,7 @@ static int vgxy61_init_state(struct v4l2_subdev *sd,
vgxy61_fill_framefmt(sensor, sensor->current_mode, &fmt.format,
VGXY61_MEDIA_BUS_FMT_DEF);
- return vgxy61_set_fmt(sd, sd_state, &fmt);
+ return vgxy61_set_fmt(sd, NULL, sd_state, &fmt);
}
static int vgxy61_s_ctrl(struct v4l2_ctrl *ctrl)
diff --git a/drivers/media/i2c/wm8739.c b/drivers/media/i2c/wm8739.c
index db62bafb447c..9eaf48f1c3a2 100644
--- a/drivers/media/i2c/wm8739.c
+++ b/drivers/media/i2c/wm8739.c
@@ -217,7 +217,7 @@ static int wm8739_probe(struct i2c_client *client)
/* reset */
wm8739_write(sd, R15, 0x00);
- /* filter setting, high path, offet clear */
+ /* filter setting, high path, offset clear */
wm8739_write(sd, R5, 0x000);
/* ADC, OSC, Power Off mode Disable */
wm8739_write(sd, R6, 0x000);
diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c
index c631b8bbd386..8b9ca59f9434 100644
--- a/drivers/media/pci/bt8xx/bttv-driver.c
+++ b/drivers/media/pci/bt8xx/bttv-driver.c
@@ -3504,7 +3504,7 @@ static void bttv_remove(struct pci_dev *pci_dev)
return;
}
-static int __maybe_unused bttv_suspend(struct device *dev)
+static int bttv_suspend(struct device *dev)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
struct bttv *btv = to_bttv(v4l2_dev);
@@ -3535,7 +3535,7 @@ static int __maybe_unused bttv_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused bttv_resume(struct device *dev)
+static int bttv_resume(struct device *dev)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
struct bttv *btv = to_bttv(v4l2_dev);
@@ -3573,7 +3573,7 @@ static const struct pci_device_id bttv_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
-static SIMPLE_DEV_PM_OPS(bttv_pm_ops,
+static DEFINE_SIMPLE_DEV_PM_OPS(bttv_pm_ops,
bttv_suspend,
bttv_resume);
@@ -3582,7 +3582,7 @@ static struct pci_driver bttv_pci_driver = {
.id_table = bttv_pci_tbl,
.probe = bttv_probe,
.remove = bttv_remove,
- .driver.pm = &bttv_pm_ops,
+ .driver.pm = pm_sleep_ptr(&bttv_pm_ops),
};
static int __init bttv_init_module(void)
diff --git a/drivers/media/pci/cobalt/cobalt-driver.c b/drivers/media/pci/cobalt/cobalt-driver.c
index 7b1ca1238c8d..ca96cbe4f194 100644
--- a/drivers/media/pci/cobalt/cobalt-driver.c
+++ b/drivers/media/pci/cobalt/cobalt-driver.c
@@ -524,8 +524,8 @@ static int cobalt_subdevs_init(struct cobalt *cobalt)
&cobalt_edid);
if (err)
return err;
- err = v4l2_subdev_call(s[i].sd, pad, set_fmt, NULL,
- &sd_fmt);
+ err = v4l2_subdev_call(s[i].sd, pad, set_fmt, NULL, NULL,
+ &sd_fmt);
if (err)
return err;
/* Reset channel video module */
@@ -608,8 +608,8 @@ static int cobalt_subdevs_hsma_init(struct cobalt *cobalt)
if (err)
return err;
- err = v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
- &sd_fmt);
+ err = v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL,
+ &sd_fmt);
if (err)
return err;
cobalt->have_hsma_rx = true;
diff --git a/drivers/media/pci/cobalt/cobalt-v4l2.c b/drivers/media/pci/cobalt/cobalt-v4l2.c
index 51fd9576c6c2..ad89e62a2747 100644
--- a/drivers/media/pci/cobalt/cobalt-v4l2.c
+++ b/drivers/media/pci/cobalt/cobalt-v4l2.c
@@ -172,7 +172,7 @@ static void cobalt_enable_output(struct cobalt_stream *s)
sd_fmt.format.code = MEDIA_BUS_FMT_RGB888_1X24;
break;
}
- v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt);
+ v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, &sd_fmt);
iowrite32(0, &vo->control);
/* 1080p60 */
@@ -223,14 +223,14 @@ static void cobalt_enable_input(struct cobalt_stream *s)
iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK |
(1 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST),
&packer->control);
- v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
+ v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL,
&sd_fmt_yuyv);
break;
case V4L2_PIX_FMT_RGB24:
iowrite32(M00235_CONTROL_BITMAP_ENABLE_MSK |
(2 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST),
&packer->control);
- v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
+ v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL,
&sd_fmt_rgb);
break;
case V4L2_PIX_FMT_BGR32:
@@ -238,7 +238,7 @@ static void cobalt_enable_input(struct cobalt_stream *s)
M00235_CONTROL_BITMAP_ENDIAN_FORMAT_MSK |
(3 << M00235_CONTROL_BITMAP_PACK_FORMAT_OFST),
&packer->control);
- v4l2_subdev_call(s->sd, pad, set_fmt, NULL,
+ v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL,
&sd_fmt_rgb);
break;
}
@@ -938,7 +938,7 @@ static int cobalt_s_fmt_vid_out(struct file *file, void *priv,
s->ycbcr_enc = pix->ycbcr_enc;
s->quantization = pix->quantization;
v4l2_fill_mbus_format(&sd_fmt.format, pix, code);
- v4l2_subdev_call(s->sd, pad, set_fmt, NULL, &sd_fmt);
+ v4l2_subdev_call(s->sd, pad, set_fmt, NULL, NULL, &sd_fmt);
return 0;
}
diff --git a/drivers/media/pci/cx18/cx18-av-core.c b/drivers/media/pci/cx18/cx18-av-core.c
index 4fb19d26ee29..f5961853eeae 100644
--- a/drivers/media/pci/cx18/cx18-av-core.c
+++ b/drivers/media/pci/cx18/cx18-av-core.c
@@ -930,6 +930,7 @@ static int cx18_av_s_ctrl(struct v4l2_ctrl *ctrl)
}
static int cx18_av_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/pci/cx18/cx18-controls.c b/drivers/media/pci/cx18/cx18-controls.c
index 78eadad8b6e8..41f388ba22ab 100644
--- a/drivers/media/pci/cx18/cx18-controls.c
+++ b/drivers/media/pci/cx18/cx18-controls.c
@@ -85,7 +85,7 @@ static int cx18_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
fmt->width = cxhdl->width / (is_mpeg1 ? 2 : 1);
fmt->height = cxhdl->height;
fmt->code = MEDIA_BUS_FMT_FIXED;
- v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format);
+ v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, NULL, &format);
return 0;
}
diff --git a/drivers/media/pci/cx18/cx18-driver.c b/drivers/media/pci/cx18/cx18-driver.c
index 214fac7af61e..ac90ce6cd044 100644
--- a/drivers/media/pci/cx18/cx18-driver.c
+++ b/drivers/media/pci/cx18/cx18-driver.c
@@ -805,12 +805,12 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
}
if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) {
CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
- return -EIO;
+ goto err_disable_device;
}
if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
CX18_ERR("Cannot request encoder memory region, card %d\n",
cx->instance);
- return -EIO;
+ goto err_disable_device;
}
/* Enable bus mastering and memory mapped IO for the CX23418 */
@@ -834,6 +834,10 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
return 0;
+
+err_disable_device:
+ pci_disable_device(pci_dev);
+ return -EIO;
}
static void cx18_init_subdevs(struct cx18 *cx)
@@ -1120,6 +1124,7 @@ free_map:
cx18_iounmap(cx);
free_mem:
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+ pci_disable_device(pci_dev);
free_workqueues:
destroy_workqueue(cx->in_work_queue);
err:
diff --git a/drivers/media/pci/cx18/cx18-ioctl.c b/drivers/media/pci/cx18/cx18-ioctl.c
index 0d676a57e24e..6f8d7f700016 100644
--- a/drivers/media/pci/cx18/cx18-ioctl.c
+++ b/drivers/media/pci/cx18/cx18-ioctl.c
@@ -150,7 +150,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
format.format.width = cx->cxhdl.width = w;
format.format.height = cx->cxhdl.height = h;
format.format.code = MEDIA_BUS_FMT_FIXED;
- v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, &format);
+ v4l2_subdev_call(cx->sd_av, pad, set_fmt, NULL, NULL, &format);
return cx18_g_fmt_vid_cap(file, fh, fmt);
}
diff --git a/drivers/media/pci/cx18/cx18-queue.c b/drivers/media/pci/cx18/cx18-queue.c
index 04d6828f0259..a973367d4cde 100644
--- a/drivers/media/pci/cx18/cx18-queue.c
+++ b/drivers/media/pci/cx18/cx18-queue.c
@@ -114,7 +114,10 @@ static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s,
if (list_is_singular(&mdl->buf_list)) {
buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
list);
- buf->bytesused = mdl->bytesused;
+ if (mdl->bytesused > s->buf_size)
+ buf->bytesused = s->buf_size;
+ else
+ buf->bytesused = mdl->bytesused;
buf->readpos = 0;
cx18_buf_sync_for_cpu(s, buf);
} else {
diff --git a/drivers/media/pci/cx18/cx23418.h b/drivers/media/pci/cx18/cx23418.h
index 22486f39bcda..4ee45be757cb 100644
--- a/drivers/media/pci/cx18/cx23418.h
+++ b/drivers/media/pci/cx18/cx23418.h
@@ -24,7 +24,7 @@
#define CX18_CREATE_TASK (MGR_CMD_MASK | 0x0001)
/* Description: This command destroys an instance of a task
- IN[0] - Task handle. Hanlde of the task to destroy
+ IN[0] - Task handle. Handle of the task to destroy
ReturnCode - One of the ERR_SYS_... */
#define CX18_DESTROY_TASK (MGR_CMD_MASK | 0x0002)
diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c
index 5fb26285e4af..7498091176b7 100644
--- a/drivers/media/pci/cx23885/cx23885-core.c
+++ b/drivers/media/pci/cx23885/cx23885-core.c
@@ -2246,6 +2246,10 @@ static void cx23885_finidev(struct pci_dev *pci_dev)
/* unregister stuff */
free_irq(pci_dev->irq, dev);
+ cancel_work_sync(&dev->cx25840_work);
+ cancel_work_sync(&dev->ir_rx_work);
+ cancel_work_sync(&dev->ir_tx_work);
+
pci_disable_device(pci_dev);
cx23885_dev_unregister(dev);
diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c
index f240ccda40ed..348abde0ba26 100644
--- a/drivers/media/pci/cx23885/cx23885-dvb.c
+++ b/drivers/media/pci/cx23885/cx23885-dvb.c
@@ -1158,8 +1158,12 @@ static int dvb_register_ci_mac(struct cx23885_tsport *port)
info.platform_data = &sp2_config;
request_module(info.type);
client_ci = i2c_new_client_device(&i2c_bus->i2c_adap, &info);
- if (!i2c_client_has_driver(client_ci))
+ if (IS_ERR(client_ci))
+ return PTR_ERR(client_ci);
+ if (!client_ci->dev.driver) {
+ i2c_unregister_device(client_ci);
return -ENODEV;
+ }
if (!try_module_get(client_ci->dev.driver->owner)) {
i2c_unregister_device(client_ci);
return -ENODEV;
@@ -1202,7 +1206,7 @@ static int dvb_register(struct cx23885_tsport *port)
int (*p_set_voltage)(struct dvb_frontend *fe,
enum fe_sec_voltage voltage) = NULL;
int mfe_shared = 0; /* bus not shared by default */
- int ret;
+ int ret = -EINVAL;
/* Get the first frontend */
fe0 = vb2_dvb_get_frontend(&port->frontends, 1);
@@ -2586,8 +2590,10 @@ static int dvb_register(struct cx23885_tsport *port)
goto frontend_detach;
ret = dvb_register_ci_mac(port);
- if (ret)
+ if (ret) {
+ vb2_dvb_unregister_bus(&port->frontends);
goto frontend_detach;
+ }
return 0;
@@ -2618,7 +2624,7 @@ frontend_detach:
port->gate_ctrl = NULL;
vb2_dvb_dealloc_frontends(&port->frontends);
- return -EINVAL;
+ return ret;
}
int cx23885_dvb_register(struct cx23885_tsport *port)
diff --git a/drivers/media/pci/cx23885/cx23885-video.c b/drivers/media/pci/cx23885/cx23885-video.c
index 14d219fd1d8a..94ce00154e1f 100644
--- a/drivers/media/pci/cx23885/cx23885-video.c
+++ b/drivers/media/pci/cx23885/cx23885-video.c
@@ -134,7 +134,7 @@ int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
format.format.width = dev->width;
format.format.height = dev->height;
format.format.field = dev->field;
- call_all(dev, pad, set_fmt, NULL, &format);
+ call_all(dev, pad, set_fmt, NULL, NULL, &format);
return 0;
}
@@ -619,7 +619,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
dev->width, dev->height, dev->field);
v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
- call_all(dev, pad, set_fmt, NULL, &format);
+ call_all(dev, pad, set_fmt, NULL, NULL, &format);
v4l2_fill_pix_format(&f->fmt.pix, &format.format);
/* set_fmt overwrites f->fmt.pix.field, restore it */
f->fmt.pix.field = dev->field;
diff --git a/drivers/media/pci/cx88/cx88-input.c b/drivers/media/pci/cx88/cx88-input.c
index 5d9ce4f9af01..c96d289e5ab7 100644
--- a/drivers/media/pci/cx88/cx88-input.c
+++ b/drivers/media/pci/cx88/cx88-input.c
@@ -259,7 +259,7 @@ static void cx88_ir_close(struct rc_dev *rc)
int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
{
struct cx88_IR *ir;
- struct rc_dev *dev;
+ struct rc_dev *dev = NULL;
char *ir_codes = NULL;
u64 rc_proto = RC_PROTO_BIT_OTHER;
int err = -ENOMEM;
@@ -268,11 +268,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
*/
ir = kzalloc_obj(*ir);
- dev = rc_allocate_device(RC_DRIVER_IR_RAW);
- if (!ir || !dev)
- goto err_out_free;
-
- ir->dev = dev;
+ if (!ir)
+ return -ENOMEM;
/* detect & configure */
switch (core->boardnr) {
@@ -439,6 +436,13 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
goto err_out_free;
}
+ dev = rc_allocate_device(ir->sampling ?
+ RC_DRIVER_IR_RAW : RC_DRIVER_SCANCODE);
+ if (!dev)
+ goto err_out_free;
+
+ ir->dev = dev;
+
/*
* The usage of mask_keycode were very convenient, due to several
* reasons. Among others, the scancode tables were using the scancode
@@ -477,12 +481,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
dev->close = cx88_ir_close;
dev->scancode_mask = hardware_mask;
- if (ir->sampling) {
+ if (ir->sampling)
dev->timeout = MS_TO_US(10); /* 10 ms */
- } else {
- dev->driver_type = RC_DRIVER_SCANCODE;
+ else
dev->allowed_protocols = rc_proto;
- }
ir->core = core;
core->ir = ir;
diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c
index 0c07ad335799..cd2366768f4a 100644
--- a/drivers/media/pci/cx88/cx88-mpeg.c
+++ b/drivers/media/pci/cx88/cx88-mpeg.c
@@ -392,7 +392,8 @@ static int cx8802_init_common(struct cx8802_dev *dev)
err = dma_set_mask(&dev->pci->dev, DMA_BIT_MASK(32));
if (err) {
pr_err("Oops: no 32bit PCI DMA ???\n");
- return -EIO;
+ err = -EIO;
+ goto fail_disable_device;
}
dev->pci_rev = dev->pci->revision;
@@ -413,13 +414,17 @@ static int cx8802_init_common(struct cx8802_dev *dev)
IRQF_SHARED, dev->core->name, dev);
if (err < 0) {
pr_err("can't get IRQ %d\n", dev->pci->irq);
- return err;
+ goto fail_disable_device;
}
cx_set(MO_PCI_INTMSK, core->pci_irqmask);
/* everything worked */
pci_set_drvdata(dev->pci, dev);
return 0;
+
+fail_disable_device:
+ pci_disable_device(dev->pci);
+ return err;
}
static void cx8802_fini_common(struct cx8802_dev *dev)
diff --git a/drivers/media/pci/cx88/cx88-video.c b/drivers/media/pci/cx88/cx88-video.c
index eaa46a2f92e7..f8f2843495f3 100644
--- a/drivers/media/pci/cx88/cx88-video.c
+++ b/drivers/media/pci/cx88/cx88-video.c
@@ -1549,7 +1549,7 @@ static void cx8800_finidev(struct pci_dev *pci_dev)
kfree(dev);
}
-static int __maybe_unused cx8800_suspend(struct device *dev_d)
+static int cx8800_suspend(struct device *dev_d)
{
struct cx8800_dev *dev = dev_get_drvdata(dev_d);
struct cx88_core *core = dev->core;
@@ -1576,7 +1576,7 @@ static int __maybe_unused cx8800_suspend(struct device *dev_d)
return 0;
}
-static int __maybe_unused cx8800_resume(struct device *dev_d)
+static int cx8800_resume(struct device *dev_d)
{
struct cx8800_dev *dev = dev_get_drvdata(dev_d);
struct cx88_core *core = dev->core;
@@ -1617,14 +1617,14 @@ static const struct pci_device_id cx8800_pci_tbl[] = {
};
MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl);
-static SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume);
static struct pci_driver cx8800_pci_driver = {
.name = "cx8800",
.id_table = cx8800_pci_tbl,
.probe = cx8800_initdev,
.remove = cx8800_finidev,
- .driver.pm = &cx8800_pm_ops,
+ .driver.pm = pm_sleep_ptr(&cx8800_pm_ops),
};
module_pci_driver(cx8800_pci_driver);
diff --git a/drivers/media/pci/hws/hws.h b/drivers/media/pci/hws/hws.h
index 8fbe1fe27844..d87d52674b69 100644
--- a/drivers/media/pci/hws/hws.h
+++ b/drivers/media/pci/hws/hws.h
@@ -33,8 +33,8 @@ struct hws_pix_state {
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_ycbcr_encoding ycbcr_enc; /* V4L2_YCBCR_ENC_601 */
+ enum v4l2_quantization quantization; /* V4L2_QUANTIZATION_FULL_RANGE */
enum v4l2_xfer_func xfer_func; /* V4L2_XFER_FUNC_DEFAULT */
bool interlaced; /* cached hardware state */
u32 half_size; /* hardware half-frame size */
diff --git a/drivers/media/pci/hws/hws_pci.c b/drivers/media/pci/hws/hws_pci.c
index 30bb7d34465b..f06e60dc2ee6 100644
--- a/drivers/media/pci/hws/hws_pci.c
+++ b/drivers/media/pci/hws/hws_pci.c
@@ -33,8 +33,8 @@ static unsigned long long hws_elapsed_us(u64 start_ns)
}
/* register layout inside HWS_REG_DEVICE_INFO */
-#define DEVINFO_VER GENMASK(7, 0)
-#define DEVINFO_SUBVER GENMASK(15, 8)
+#define DEVINFO_VER GENMASK(15, 8)
+#define DEVINFO_SUBVER GENMASK(23, 16)
#define DEVINFO_YV12 GENMASK(31, 28)
#define DEVINFO_HWKEY GENMASK(27, 24)
#define DEVINFO_PORTID GENMASK(25, 24) /* low 2 bits of HW-key */
@@ -441,7 +441,7 @@ static int hws_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
hws_init_video_sys(hws, false);
/* 5) Init channels (video state, locks, vb2, ctrls) */
- for (i = 0; i < hws->max_channels; i++) {
+ for (i = 0; i < hws->cur_max_video_ch; i++) {
ret = hws_video_init_channel(hws, i);
if (ret) {
dev_err(&pdev->dev, "video channel init failed (ch=%d)\n", i);
diff --git a/drivers/media/pci/hws/hws_reg.h b/drivers/media/pci/hws/hws_reg.h
index e4fb4af44434..ba1d22f4376c 100644
--- a/drivers/media/pci/hws/hws_reg.h
+++ b/drivers/media/pci/hws/hws_reg.h
@@ -70,7 +70,8 @@
#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,
+ * low byte=0x13 is the vendor baseline capture-mode value; its individual
+ * bit meanings are not documented,
* in ReadChipId() we also write 0x00 and 0x10 here for chip-ID sequencing.
*/
@@ -121,9 +122,10 @@
#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 7:0 = unused by the baseline driver
+ * bits 15:8 = device version
+ * bits 23:16 = device sub-version
+ * bits 27:24 = HW key (port ID in bits 25:24)
* bits 31:28 = "support YV12" flags
*/
diff --git a/drivers/media/pci/hws/hws_v4l2_ioctl.c b/drivers/media/pci/hws/hws_v4l2_ioctl.c
index 0303e311ee4a..ce396b7225d2 100644
--- a/drivers/media/pci/hws/hws_v4l2_ioctl.c
+++ b/drivers/media/pci/hws/hws_v4l2_ioctl.c
@@ -511,8 +511,9 @@ static inline void hws_set_colorimetry_state(struct hws_pix_state *p)
{
bool sd = p->height <= 576;
+ /* Captured samples use full-range BT.601 Y'CbCr at every resolution. */
p->colorspace = sd ? V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
- p->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT;
+ p->ycbcr_enc = V4L2_YCBCR_ENC_601;
p->quantization = V4L2_QUANTIZATION_FULL_RANGE;
p->xfer_func = V4L2_XFER_FUNC_DEFAULT;
}
@@ -736,7 +737,7 @@ 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->ycbcr_enc = V4L2_YCBCR_ENC_601;
p->quantization = V4L2_QUANTIZATION_FULL_RANGE;
p->xfer_func = V4L2_XFER_FUNC_DEFAULT;
}
diff --git a/drivers/media/pci/hws/hws_video.c b/drivers/media/pci/hws/hws_video.c
index 18e4bc6901d3..dbe0fc2a66b5 100644
--- a/drivers/media/pci/hws/hws_video.c
+++ b/drivers/media/pci/hws/hws_video.c
@@ -327,7 +327,7 @@ int hws_video_init_channel(struct hws_pcie_dev *pdev, int ch)
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.ycbcr_enc = V4L2_YCBCR_ENC_601;
vid->pix.quantization = V4L2_QUANTIZATION_FULL_RANGE;
vid->pix.xfer_func = V4L2_XFER_FUNC_DEFAULT;
vid->pix.interlaced = false;
@@ -895,6 +895,8 @@ static void hws_video_apply_mode_change(struct hws_pcie_dev *pdx,
v->pix.width = w;
v->pix.height = h;
v->pix.interlaced = interlaced;
+ v->pix.colorspace = h <= 576 ? V4L2_COLORSPACE_SMPTE170M :
+ V4L2_COLORSPACE_REC709;
hws_set_current_dv_timings(v, w, h, interlaced);
v->current_fps = fps;
@@ -1292,6 +1294,8 @@ static void hws_stop_streaming(struct vb2_queue *q)
WRITE_ONCE(v->stop_requested, true);
hws_enable_video_capture(v->parent, v->channel_index, false);
+ if (hws->irq >= 0)
+ synchronize_irq(hws->irq);
/* 2) Collect in-flight + queued under the IRQ lock */
spin_lock_irqsave(&v->irq_lock, flags);
diff --git a/drivers/media/pci/intel/ipu-bridge.c b/drivers/media/pci/intel/ipu-bridge.c
index bd64c0400c0d..e476e269a50a 100644
--- a/drivers/media/pci/intel/ipu-bridge.c
+++ b/drivers/media/pci/intel/ipu-bridge.c
@@ -8,6 +8,7 @@
#include <linux/dmi.h>
#include <linux/i2c.h>
#include <linux/mei_cl_bus.h>
+#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
@@ -15,6 +16,7 @@
#include <linux/workqueue.h>
#include <media/ipu-bridge.h>
+#include <media/ipu6-pci-table.h>
#include <media/v4l2-fwnode.h>
#define ADEV_DEV(adev) ACPI_PTR(&((adev)->dev))
@@ -48,6 +50,14 @@
*
* Please keep the list sorted by ACPI HID.
*/
+/* IPU6 variants whose CSI-2 receiver needs the ov5693 clock lane gated */
+static const u16 ipu6_ov5693_ncont_clk[] = {
+ PCI_DEVICE_ID_INTEL_IPU6, /* Tiger Lake */
+ PCI_DEVICE_ID_INTEL_IPU6EP_ADLP, /* Alder Lake-P */
+ PCI_DEVICE_ID_INTEL_IPU6EP_ADLN, /* Alder Lake-N */
+ 0
+};
+
static const struct ipu_sensor_config ipu_supported_sensors[] = {
/* Himax HM1092 */
IPU_SENSOR_CONFIG("HIMX1092", 2, 180000000, 180480000),
@@ -60,6 +70,9 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
/* GalaxyCore GC0310 */
IPU_SENSOR_CONFIG("INT0310", 1, 55692000),
/* Omnivision OV5693 */
+ IPU_SENSOR_CONFIG_MATCH_FL("INT33BE", ipu6_ov5693_ncont_clk,
+ IPU_BR_FL_CSI2_CLK_NONCONTINUOUS,
+ 1, 419200000),
IPU_SENSOR_CONFIG("INT33BE", 1, 419200000),
/* Onsemi MT9M114 */
IPU_SENSOR_CONFIG("INT33F0", 1, 384000000),
@@ -75,19 +88,22 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("INT3537", 1, 437000000),
/* Lontium lt6911uxe */
IPU_SENSOR_CONFIG("INTC10C5", 0),
- /* Omnivision OV01A10 / OV01A1S */
+ /* Omnivision OV01A10 / OV01A1B / OV01A1S */
IPU_SENSOR_CONFIG("OVTI01A0", 1, 400000000),
+ IPU_SENSOR_CONFIG("OVTI01AB", 1, 400000000),
IPU_SENSOR_CONFIG("OVTI01AS", 1, 400000000),
/* Omnivision OV02C10 */
IPU_SENSOR_CONFIG("OVTI02C1", 1, 400000000),
/* Omnivision OV02E10 */
IPU_SENSOR_CONFIG("OVTI02E1", 1, 360000000),
/* Omnivision ov05c10 */
- IPU_SENSOR_CONFIG("OVTI05C1", 1, 480000000),
+ IPU_SENSOR_CONFIG("OVTI05C1", 2, 480000000, 900000000),
/* Omnivision OV08A10 */
IPU_SENSOR_CONFIG("OVTI08A1", 1, 500000000),
/* Omnivision OV08x40 */
IPU_SENSOR_CONFIG("OVTI08F4", 3, 400000000, 749000000, 800000000),
+ /* Omnivision OV13858 */
+ IPU_SENSOR_CONFIG("OVTID858", 2, 540000000, 270000000),
/* Omnivision OV13B10 */
IPU_SENSOR_CONFIG("OVTI13B1", 1, 560000000),
IPU_SENSOR_CONFIG("OVTIDB10", 1, 560000000),
@@ -95,6 +111,11 @@ static const struct ipu_sensor_config ipu_supported_sensors[] = {
IPU_SENSOR_CONFIG("OVTI2680", 1, 331200000),
/* Omnivision OV5675 */
IPU_SENSOR_CONFIG("OVTI5675", 1, 450000000),
+ /* Omnivision OV5693 */
+ IPU_SENSOR_CONFIG_MATCH_FL("OVTI5693", ipu6_ov5693_ncont_clk,
+ IPU_BR_FL_CSI2_CLK_NONCONTINUOUS,
+ 1, 419200000),
+ IPU_SENSOR_CONFIG("OVTI5693", 1, 419200000),
/* Omnivision OV8856 */
IPU_SENSOR_CONFIG("OVTI8856", 3, 180000000, 360000000, 720000000),
/* Sony IMX471 */
@@ -127,6 +148,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
{
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 14 (Dell 14 Premium) DA14250"),
+ },
+ .driver_data = "OVTI02C1",
+ },
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 14 9440"),
},
.driver_data = "OVTI02C1",
@@ -145,6 +173,13 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
},
.driver_data = "OVTI02C1",
},
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell Pro 14 Premium PA14260"),
+ },
+ .driver_data = "OVTI08F4",
+ },
/*
* The first four characters of DMI_BOARD_NAME identify the Lenovo
* machine type/model. For example, a DMI_BOARD_NAME starting with
@@ -185,6 +220,14 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
.driver_data = "SONY471A",
},
{
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME,
+ "Surface Pro for Business 11th Edition with Intel"),
+ },
+ .driver_data = "OVTID858",
+ },
+ {
/* Samsung Galaxy Book5 Pro 360 */
.matches = {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
@@ -192,6 +235,14 @@ static const struct dmi_system_id upside_down_sensor_dmi_ids[] = {
},
.driver_data = "OVTI02E1",
},
+ {
+ /* Samsung Galaxy Book3 Ultra */
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "960XFH"),
+ },
+ .driver_data = "OVTI02C1",
+ },
{} /* Terminating entry */
};
@@ -326,8 +377,8 @@ static int ipu_bridge_check_ivsc_dev(struct ipu_sensor *sensor,
if (adev) {
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 or CVS CSI dev\n");
+ acpi_dev_put(adev);
return -ENODEV;
}
@@ -436,6 +487,7 @@ static enum v4l2_fwnode_orientation ipu_bridge_parse_orientation(struct acpi_dev
int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor)
{
+ acpi_handle handle = acpi_device_handle(ACPI_PTR(adev));
struct ipu_sensor_ssdb ssdb = {};
int ret;
@@ -459,8 +511,15 @@ int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor)
sensor->rotation = ipu_bridge_parse_rotation(adev, &ssdb);
sensor->orientation = ipu_bridge_parse_orientation(adev);
- if (ssdb.vcmtype)
+ acpi_handle_debug(handle,
+ "CSI-2 port %u, lanes %u, mclkspeed %u, rotation %u (SSDB %u), orientation %u\n",
+ sensor->link, sensor->lanes, sensor->mclkspeed,
+ sensor->rotation, ssdb.degree, sensor->orientation);
+
+ if (ssdb.vcmtype) {
sensor->vcm_type = ipu_vcm_types[ssdb.vcmtype - 1];
+ acpi_handle_debug(handle, "VCM %s\n", sensor->vcm_type);
+ }
return 0;
}
@@ -473,6 +532,7 @@ static void ipu_bridge_create_fwnode_properties(
{
struct ipu_property_names *names = &sensor->prop_names;
struct software_node *nodes = sensor->swnodes;
+ unsigned int i = 0;
sensor->prop_names = prop_names;
@@ -530,21 +590,25 @@ static void ipu_bridge_create_fwnode_properties(
PROPERTY_ENTRY_REF_ARRAY("lens-focus", sensor->vcm_ref);
}
- sensor->ep_properties[0] = PROPERTY_ENTRY_U32(
- sensor->prop_names.bus_type,
- V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
- sensor->ep_properties[1] = PROPERTY_ENTRY_U32_ARRAY_LEN(
- sensor->prop_names.data_lanes,
- bridge->data_lanes, sensor->lanes);
- sensor->ep_properties[2] = PROPERTY_ENTRY_REF_ARRAY(
- sensor->prop_names.remote_endpoint,
- sensor->local_ref);
+ sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_BUS_TYPE)] =
+ PROPERTY_ENTRY_U32(names->bus_type,
+ V4L2_FWNODE_BUS_TYPE_CSI2_DPHY);
+ sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_DATA_LANES)] =
+ PROPERTY_ENTRY_U32_ARRAY_LEN(names->data_lanes,
+ bridge->data_lanes, sensor->lanes);
+ sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_REMOTE_EP)] =
+ PROPERTY_ENTRY_REF_ARRAY(names->remote_endpoint,
+ sensor->local_ref);
if (cfg->nr_link_freqs > 0)
- sensor->ep_properties[3] = PROPERTY_ENTRY_U64_ARRAY_LEN(
- sensor->prop_names.link_frequencies,
- cfg->link_freqs,
- cfg->nr_link_freqs);
+ sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_LINK_FREQUENCIES)] =
+ PROPERTY_ENTRY_U64_ARRAY_LEN(names->link_frequencies,
+ cfg->link_freqs,
+ cfg->nr_link_freqs);
+
+ if (cfg->flags & IPU_BR_FL_CSI2_CLK_NONCONTINUOUS)
+ sensor->ep_properties[IPU_BRIDGE_NEXT_PROPERTY(i, EP_CLOCK_NONCONTINUOUS)] =
+ PROPERTY_ENTRY_BOOL("clock-noncontinuous");
sensor->ipu_properties[0] = PROPERTY_ENTRY_U32_ARRAY_LEN(
sensor->prop_names.data_lanes,
@@ -856,8 +920,8 @@ static int ipu_bridge_connect_sensor(const struct ipu_sensor_config *cfg,
if (ret)
goto err_free_swnodes;
- dev_info(bridge->dev, "Found supported sensor %s\n",
- acpi_dev_name(adev));
+ dev_info(bridge->dev, "Found supported sensor %s (%pfw)\n",
+ acpi_dev_name(adev), primary);
bridge->n_sensors++;
}
@@ -874,8 +938,28 @@ err_put_adev:
return ret;
}
+/*
+ * Whether a sensor config applies to the IPU this bridge sits on. A config
+ * listing PCI product IDs only applies to those IPUs.
+ */
+static bool ipu_bridge_config_matches(const struct ipu_sensor_config *cfg,
+ struct ipu_bridge *bridge)
+{
+ const u16 *id;
+
+ if (!cfg->pci_ids)
+ return true;
+
+ for (id = cfg->pci_ids; *id; id++)
+ if (*id == bridge->pci_id)
+ return true;
+
+ return false;
+}
+
static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge)
{
+ const char *done_hid = NULL;
unsigned int i;
int ret;
@@ -883,9 +967,22 @@ static int ipu_bridge_connect_sensors(struct ipu_bridge *bridge)
const struct ipu_sensor_config *cfg =
&ipu_supported_sensors[i];
+ /*
+ * Entries for one HID are adjacent, IPU-specific ones first,
+ * so the generic entry is skipped once a specific one has
+ * matched and the sensor is not connected twice.
+ */
+ if (done_hid && !strcmp(cfg->hid, done_hid))
+ continue;
+
+ if (!ipu_bridge_config_matches(cfg, bridge))
+ continue;
+
ret = ipu_bridge_connect_sensor(cfg, bridge);
if (ret)
goto err_unregister_sensors;
+
+ done_hid = cfg->hid;
}
return 0;
@@ -942,6 +1039,18 @@ static int ipu_bridge_check_fwnode_graph(struct fwnode_handle *fwnode)
return ipu_bridge_check_fwnode_graph(fwnode->secondary);
}
+struct pci_dev *ipu_bridge_get_ipu6(void)
+{
+ struct pci_dev *ipu = NULL;
+
+ 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);
+
+ return ipu;
+}
+EXPORT_SYMBOL_NS_GPL(ipu_bridge_get_ipu6, "INTEL_IPU_BRIDGE");
+
static DEFINE_MUTEX(ipu_bridge_mutex);
int ipu_bridge_init(struct device *dev,
@@ -969,6 +1078,7 @@ int ipu_bridge_init(struct device *dev,
sizeof(bridge->ipu_node_name));
bridge->ipu_hid_node.name = bridge->ipu_node_name;
bridge->dev = dev;
+ bridge->pci_id = dev_is_pci(dev) ? to_pci_dev(dev)->device : 0;
bridge->parse_sensor_fwnode = parse_sensor_fwnode;
ret = software_node_register(&bridge->ipu_hid_node);
diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
index eb1824ee86fd..4d2b231291f5 100644
--- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c
+++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c
@@ -1229,6 +1229,7 @@ static int cio2_subdev_init_state(struct v4l2_subdev *sd,
}
static int cio2_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/pci/intel/ipu6/Kconfig b/drivers/media/pci/intel/ipu6/Kconfig
index 1129e2beb4be..a1d036a78b57 100644
--- a/drivers/media/pci/intel/ipu6/Kconfig
+++ b/drivers/media/pci/intel/ipu6/Kconfig
@@ -16,3 +16,12 @@ config VIDEO_INTEL_IPU6
To compile this driver, say Y here! It contains 2 modules -
intel_ipu6 and intel_ipu6_isys.
+
+config VIDEO_INTEL_IPU6_IPU7
+ bool "Support IPU 7 and IPU 7.5 in ipu6 driver by default"
+ depends on VIDEO_INTEL_IPU6
+ default y
+ help
+ Support Intel IPU 7 and IPU 7.5 in ipu6 driver by default, instead of
+ the ipu7 staging driver. This behaviour can be changed at runtime with
+ module parameters in the respective drivers.
diff --git a/drivers/media/pci/intel/ipu6/Makefile b/drivers/media/pci/intel/ipu6/Makefile
index a821b0a1567f..676bc978b972 100644
--- a/drivers/media/pci/intel/ipu6/Makefile
+++ b/drivers/media/pci/intel/ipu6/Makefile
@@ -4,20 +4,26 @@ intel-ipu6-y := ipu6.o \
ipu6-bus.o \
ipu6-dma.o \
ipu6-mmu.o \
+ ipu6-mmu-hw.o \
+ ipu7-mmu-hw.o \
ipu6-buttress.o \
ipu6-cpd.o \
- ipu6-fw-com.o
+ ipu6-fw-com.o \
+ ipu7-fw-com.o \
+ ipu7-boot.o
obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6.o
intel-ipu6-isys-y := ipu6-isys.o \
ipu6-isys-csi2.o \
ipu6-fw-isys.o \
+ ipu7-fw-isys.o \
ipu6-isys-video.o \
ipu6-isys-queue.o \
ipu6-isys-subdev.o \
ipu6-isys-mcd-phy.o \
ipu6-isys-jsl-phy.o \
- ipu6-isys-dwc-phy.o
+ ipu6-isys-dwc-phy.o \
+ ipu7-isys-csi-phy.o
obj-$(CONFIG_VIDEO_INTEL_IPU6) += intel-ipu6-isys.o
diff --git a/drivers/media/pci/intel/ipu6/ipu6-bus.h b/drivers/media/pci/intel/ipu6/ipu6-bus.h
index a08c5468d536..d2f93eb03dea 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-bus.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-bus.h
@@ -26,17 +26,18 @@ struct ipu6_bus_device {
struct ipu6_mmu *mmu;
struct ipu6_device *isp;
const struct ipu6_buttress_ctrl *ctrl;
- const struct firmware *fw;
struct sg_table fw_sgt;
u64 *pkg_dir;
dma_addr_t pkg_dir_dma_addr;
unsigned int pkg_dir_size;
+ u32 fw_entry;
};
struct ipu6_auxdrv_data {
irqreturn_t (*isr)(struct ipu6_bus_device *adev);
irqreturn_t (*isr_threaded)(struct ipu6_bus_device *adev);
bool wake_isr_thread;
+ const struct ipu6_fw_isys_ops *fw_ops;
};
#define to_ipu6_bus_device(_dev) \
@@ -45,6 +46,9 @@ struct ipu6_auxdrv_data {
container_of(_auxdev, struct ipu6_bus_device, auxdev)
#define ipu6_bus_get_drvdata(adev) dev_get_drvdata(&(adev)->auxdev.dev)
+extern const struct ipu6_fw_isys_ops ipu6_fw_isys_ops;
+extern const struct ipu6_fw_isys_ops ipu7_fw_isys_ops;
+
struct ipu6_bus_device *
ipu6_bus_initialize_device(struct pci_dev *pdev, struct device *parent,
void *pdata, const struct ipu6_buttress_ctrl *ctrl,
diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.c b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
index e0ecb4c8b081..105de1744dff 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-buttress.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.c
@@ -3,6 +3,7 @@
* Copyright (C) 2013--2024 Intel Corporation
*/
+#include <asm/cpu_device_id.h>
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/completion.h>
@@ -55,16 +56,11 @@
#define BUTTRESS_MAX_CONSECUTIVE_IRQS 100
-static const u32 ipu6_adev_irq_mask[2] = {
- BUTTRESS_ISR_IS_IRQ,
- BUTTRESS_ISR_PS_IRQ
-};
-
-int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
- struct ipu6_buttress_ipc *ipc)
+int ipu6_buttress_ipc_reset(struct ipu6_device *isp)
{
unsigned int retries = BUTTRESS_IPC_RESET_RETRY;
struct ipu6_buttress *b = &isp->buttress;
+ const struct ipu6_buttress_registers *regs = b->regs;
u32 val = 0, csr_in_clr;
if (!isp->secure_mode) {
@@ -75,11 +71,11 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
mutex_lock(&b->ipc_mutex);
/* Clear-by-1 CSR (all bits), corresponding internal states. */
- val = readl(isp->base + ipc->csr_in);
- writel(val, isp->base + ipc->csr_in);
+ val = readl(isp->base + regs->csr_in);
+ writel(val, isp->base + regs->csr_in);
/* Set peer CSR bit IPC_PEER_COMP_ACTIONS_RST_PHASE1 */
- writel(ENTRY, isp->base + ipc->csr_out);
+ writel(ENTRY, isp->base + regs->csr_out);
/*
* Clear-by-1 all CSR bits EXCEPT following
* bits:
@@ -94,7 +90,7 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
do {
usleep_range(400, 500);
- val = readl(isp->base + ipc->csr_in);
+ val = readl(isp->base + regs->csr_in);
switch (val) {
case ENTRY | EXIT:
case ENTRY | EXIT | QUERY:
@@ -105,8 +101,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
* 2) Set peer CSR bit
* IPC_PEER_QUERIED_IP_COMP_ACTIONS_RST_PHASE.
*/
- writel(ENTRY | EXIT, isp->base + ipc->csr_in);
- writel(QUERY, isp->base + ipc->csr_out);
+ writel(ENTRY | EXIT, isp->base + regs->csr_in);
+ writel(QUERY, isp->base + regs->csr_out);
break;
case ENTRY:
case ENTRY | QUERY:
@@ -117,8 +113,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
* 2) Set peer CSR bit
* IPC_PEER_COMP_ACTIONS_RST_PHASE1.
*/
- writel(ENTRY | QUERY, isp->base + ipc->csr_in);
- writel(ENTRY, isp->base + ipc->csr_out);
+ writel(ENTRY | QUERY, isp->base + regs->csr_in);
+ writel(ENTRY, isp->base + regs->csr_out);
break;
case EXIT:
case EXIT | QUERY:
@@ -135,17 +131,17 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
* 3) Set peer CSR bit
* IPC_PEER_COMP_ACTIONS_RST_PHASE2.
*/
- writel(EXIT, isp->base + ipc->csr_in);
- writel(0, isp->base + ipc->db0_in);
- writel(csr_in_clr, isp->base + ipc->csr_in);
- writel(EXIT, isp->base + ipc->csr_out);
+ writel(EXIT, isp->base + regs->csr_in);
+ writel(0, isp->base + regs->db0_in);
+ writel(csr_in_clr, isp->base + regs->csr_in);
+ writel(EXIT, isp->base + regs->csr_out);
/*
* Read csr_in again to make sure if RST_PHASE2 is done.
* If csr_in is QUERY, it should be handled again.
*/
usleep_range(200, 300);
- val = readl(isp->base + ipc->csr_in);
+ val = readl(isp->base + regs->csr_in);
if (val & QUERY) {
dev_dbg(&isp->pdev->dev,
"RST_PHASE2 retry csr_in = %x\n", val);
@@ -160,8 +156,8 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
* 2) Set peer CSR bit
* IPC_PEER_COMP_ACTIONS_RST_PHASE1
*/
- writel(QUERY, isp->base + ipc->csr_in);
- writel(ENTRY, isp->base + ipc->csr_out);
+ writel(QUERY, isp->base + regs->csr_in);
+ writel(ENTRY, isp->base + regs->csr_out);
break;
default:
dev_dbg_ratelimited(&isp->pdev->dev,
@@ -176,42 +172,42 @@ int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
return -ETIMEDOUT;
}
-static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp,
- struct ipu6_buttress_ipc *ipc)
+static void ipu6_buttress_ipc_validity_close(struct ipu6_device *isp)
{
writel(BUTTRESS_IU2CSECSR_IPC_PEER_DEASSERTED_REG_VALID_REQ,
- isp->base + ipc->csr_out);
+ isp->base + isp->buttress.regs->csr_out);
}
static int
-ipu6_buttress_ipc_validity_open(struct ipu6_device *isp,
- struct ipu6_buttress_ipc *ipc)
+ipu6_buttress_ipc_validity_open(struct ipu6_device *isp)
{
unsigned int mask = BUTTRESS_IU2CSECSR_IPC_PEER_ACKED_REG_VALID;
+ const struct ipu6_buttress_registers *regs = isp->buttress.regs;
void __iomem *addr;
int ret;
u32 val;
writel(BUTTRESS_IU2CSECSR_IPC_PEER_ASSERTED_REG_VALID_REQ,
- isp->base + ipc->csr_out);
+ isp->base + regs->csr_out);
- addr = isp->base + ipc->csr_in;
+ addr = isp->base + regs->csr_in;
ret = readl_poll_timeout(addr, val, val & mask, 200,
BUTTRESS_IPC_VALIDITY_TIMEOUT_US);
if (ret) {
dev_err(&isp->pdev->dev, "CSE validity timeout 0x%x\n", val);
- ipu6_buttress_ipc_validity_close(isp, ipc);
+ ipu6_buttress_ipc_validity_close(isp);
}
return ret;
}
-static void ipu6_buttress_ipc_recv(struct ipu6_device *isp,
- struct ipu6_buttress_ipc *ipc, u32 *ipc_msg)
+static void ipu6_buttress_ipc_recv(struct ipu6_device *isp, u32 *ipc_msg)
{
+ const struct ipu6_buttress_registers *regs = isp->buttress.regs;
+
if (ipc_msg)
- *ipc_msg = readl(isp->base + ipc->data0_in);
- writel(0, isp->base + ipc->db0_in);
+ *ipc_msg = readl(isp->base + regs->data0_in);
+ writel(0, isp->base + regs->db0_in);
}
static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
@@ -221,14 +217,15 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
unsigned long tx_timeout_jiffies, rx_timeout_jiffies;
unsigned int i, retry = BUTTRESS_IPC_CMD_SEND_RETRY;
struct ipu6_buttress *b = &isp->buttress;
- struct ipu6_buttress_ipc *ipc = &b->cse;
+ struct ipu6_buttress_ipc *ipc = &b->ipc;
+ const struct ipu6_buttress_registers *regs = b->regs;
u32 val;
int ret;
int tout;
mutex_lock(&b->ipc_mutex);
- ret = ipu6_buttress_ipc_validity_open(isp, ipc);
+ ret = ipu6_buttress_ipc_validity_open(isp);
if (ret) {
dev_err(&isp->pdev->dev, "IPC validity open failed\n");
goto out;
@@ -244,9 +241,9 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
dev_dbg(&isp->pdev->dev, "bulk IPC command: 0x%x\n",
msgs[i].cmd);
- writel(msgs[i].cmd, isp->base + ipc->data0_out);
+ writel(msgs[i].cmd, isp->base + regs->data0_out);
val = BUTTRESS_IU2CSEDB0_BUSY | msgs[i].cmd_size;
- writel(val, isp->base + ipc->db0_out);
+ writel(val, isp->base + regs->db0_out);
tout = wait_for_completion_timeout(&ipc->send_complete,
tx_timeout_jiffies);
@@ -258,7 +255,7 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
}
/* Try again if CSE is not responding on first try */
- writel(0, isp->base + ipc->db0_out);
+ writel(0, isp->base + regs->db0_out);
i--;
continue;
}
@@ -276,8 +273,8 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
goto out;
}
- if (ipc->nack_mask &&
- (ipc->recv_data & ipc->nack_mask) == ipc->nack) {
+ if ((ipc->recv_data & BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK) ==
+ BUTTRESS_CSE2IUDATA0_IPC_NACK) {
dev_err(&isp->pdev->dev,
"IPC NACK for cmd 0x%x\n", msgs[i].cmd);
ret = -EIO;
@@ -296,7 +293,7 @@ static int ipu6_buttress_ipc_send_bulk(struct ipu6_device *isp,
dev_dbg(&isp->pdev->dev, "bulk IPC commands done\n");
out:
- ipu6_buttress_ipc_validity_close(isp, ipc);
+ ipu6_buttress_ipc_validity_close(isp);
mutex_unlock(&b->ipc_mutex);
return ret;
}
@@ -337,7 +334,8 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
struct ipu6_device *isp = isp_ptr;
struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
struct ipu6_buttress *b = &isp->buttress;
- u32 reg_irq_sts = BUTTRESS_REG_ISR_STATUS;
+ const struct ipu6_buttress_registers *regs = b->regs;
+ const u32 adev_irq_mask[] = { regs->irq_is, regs->irq_ps };
irqreturn_t ret = IRQ_NONE;
u32 disable_irqs = 0;
u32 irq_status;
@@ -348,7 +346,14 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
if (!active)
return IRQ_NONE;
- irq_status = readl(isp->base + reg_irq_sts);
+ if (IS_IPU7(isp)) {
+ u32 pb_irq;
+
+ pb_irq = readl(isp->pb_base + IPU7_PB_INTERRUPT_STATUS);
+ writel(pb_irq, isp->pb_base + IPU7_PB_INTERRUPT_STATUS);
+ }
+
+ irq_status = readl(isp->base + regs->irq_status);
if (irq_status == 0 || WARN_ON_ONCE(irq_status == 0xffffffffu)) {
if (active > 0)
pm_runtime_put_noidle(&isp->pdev->dev);
@@ -356,39 +361,40 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
}
do {
- writel(irq_status, isp->base + BUTTRESS_REG_ISR_CLEAR);
+ writel(irq_status, isp->base + regs->irq_clear);
- for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask); i++) {
+ for (i = 0; i < ARRAY_SIZE(adev_irq_mask); i++) {
irqreturn_t r = ipu6_buttress_call_isr(adev[i]);
- if (!(irq_status & ipu6_adev_irq_mask[i]))
+ if (!(irq_status & adev_irq_mask[i]))
continue;
if (r == IRQ_WAKE_THREAD) {
ret = IRQ_WAKE_THREAD;
- disable_irqs |= ipu6_adev_irq_mask[i];
+ disable_irqs |= adev_irq_mask[i];
} else if (ret == IRQ_NONE && r == IRQ_HANDLED) {
ret = IRQ_HANDLED;
}
}
- if ((irq_status & BUTTRESS_EVENT) && ret == IRQ_NONE)
+ if ((irq_status & regs->irq_events) && ret == IRQ_NONE)
ret = IRQ_HANDLED;
- if (irq_status & BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING) {
+ if (irq_status & regs->irq_cse_ipc) {
dev_dbg(&isp->pdev->dev,
"BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING\n");
- ipu6_buttress_ipc_recv(isp, &b->cse, &b->cse.recv_data);
- complete(&b->cse.recv_complete);
+
+ ipu6_buttress_ipc_recv(isp, &b->ipc.recv_data);
+ complete(&b->ipc.recv_complete);
}
- if (irq_status & BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE) {
+ if (irq_status & regs->irq_exec_done) {
dev_dbg(&isp->pdev->dev,
"BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE\n");
- complete(&b->cse.send_complete);
+ complete(&b->ipc.send_complete);
}
- if (irq_status & BUTTRESS_ISR_SAI_VIOLATION &&
+ if (irq_status & regs->irq_sai &&
ipu6_buttress_get_secure_mode(isp))
dev_err(&isp->pdev->dev,
"BUTTRESS_ISR_SAI_VIOLATION\n");
@@ -407,12 +413,12 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
break;
}
- irq_status = readl(isp->base + reg_irq_sts);
+ irq_status = readl(isp->base + regs->irq_status);
} while (irq_status);
if (disable_irqs)
- writel(BUTTRESS_IRQS & ~disable_irqs,
- isp->base + BUTTRESS_REG_ISR_ENABLE);
+ writel(regs->irq_all & ~disable_irqs,
+ isp->base + regs->irq_enable);
if (active > 0)
pm_runtime_put(&isp->pdev->dev);
@@ -423,12 +429,13 @@ irqreturn_t ipu6_buttress_isr(int irq, void *isp_ptr)
irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
{
struct ipu6_device *isp = isp_ptr;
+ const struct ipu6_buttress_registers *regs = isp->buttress.regs;
struct ipu6_bus_device *adev[] = { isp->isys, isp->psys };
const struct ipu6_auxdrv_data *drv_data = NULL;
irqreturn_t ret = IRQ_NONE;
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(ipu6_adev_irq_mask) && adev[i]; i++) {
+ for (i = 0; i < ARRAY_SIZE(adev) && adev[i]; i++) {
drv_data = adev[i]->auxdrv_data;
if (!drv_data)
continue;
@@ -438,46 +445,179 @@ irqreturn_t ipu6_buttress_isr_threaded(int irq, void *isp_ptr)
ret = IRQ_HANDLED;
}
- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
+ writel(regs->irq_all, isp->base + regs->irq_enable);
return ret;
}
-int ipu6_buttress_power(struct device *dev,
- const struct ipu6_buttress_ctrl *ctrl, bool on)
+static int ipu7_isys_d2d_power(struct ipu6_device *isp, bool on)
+{
+ u32 target = on ? IPU7_BUTTRESS_D2D_PWR_ACK : 0U;
+ u32 val;
+ int ret;
+
+ val = readl(isp->base + IPU7_BUTTRESS_REG_D2D_CTL);
+ if ((val & IPU7_BUTTRESS_D2D_PWR_ACK) == target)
+ return 0;
+
+ if (on)
+ val |= IPU7_BUTTRESS_D2D_PWR_EN;
+ else
+ val &= ~IPU7_BUTTRESS_D2D_PWR_EN;
+ writel(val, isp->base + IPU7_BUTTRESS_REG_D2D_CTL);
+
+ ret = readl_poll_timeout(isp->base + IPU7_BUTTRESS_REG_D2D_CTL, val,
+ (val & IPU7_BUTTRESS_D2D_PWR_ACK) == target,
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret)
+ dev_err(&isp->pdev->dev, "D2D power %s timeout: 0x%x\n",
+ on ? "up" : "down", val);
+
+ return ret;
+}
+
+static void ipu7_nde_control(struct ipu6_device *isp, bool on)
+{
+ u32 val;
+
+ val = FIELD_PREP(IPU7_NDE_VAL_MASK,
+ on ? IPU7_NDE_VAL_ACTIVE : IPU7_NDE_VAL_DEFAULT) |
+ FIELD_PREP(IPU7_NDE_SCALE_MASK,
+ on ? IPU7_NDE_SCALE_ACTIVE : IPU7_NDE_SCALE_DEFAULT) |
+ FIELD_PREP(IPU7_NDE_VALID_MASK,
+ on ? IPU7_NDE_VALID_ACTIVE : IPU7_NDE_VALID_DEFAULT) |
+ FIELD_PREP(IPU7_NDE_RESVEC_MASK, IPU7_NDE_RESVEC);
+ writel(val, isp->base + IPU7_BUTTRESS_REG_NDE_CONTROL);
+}
+
+static int __ipu7_power_on(struct device *dev,
+ const struct ipu6_buttress_ctrl *ctrl)
+{
+ struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
+ bool is_isys = ctrl->subsys_id == IPU_ISYS;
+ u32 pwr_sts, val, ovrd_clk, slp, own_clk_ack;
+ int ret;
+
+ val = ctrl->ratio | (IPU7_FREQ_CTL_CDYN << IPU7_FREQ_CTL_CDYN_SHIFT);
+ pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
+
+ if (is_isys) {
+ ret = ipu7_isys_d2d_power(isp, true);
+ if (ret)
+ return ret;
+
+ ipu7_nde_control(isp, true);
+ }
+
+ /* Request clock ownership. */
+ ovrd_clk = is_isys ? IPU7_BUTTRESS_OVERRIDE_IS_CLK :
+ IPU7_BUTTRESS_OVERRIDE_PS_CLK;
+
+ slp = readl(isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG);
+ writel(slp | ovrd_clk, isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG);
+
+ own_clk_ack = is_isys ? IPU7_BUTTRESS_OWN_ACK_IS_CLK :
+ IPU7_BUTTRESS_OWN_ACK_PS_CLK;
+ ret = readl_poll_timeout(isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_STS,
+ slp, (slp & own_clk_ack),
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret)
+ dev_warn(&isp->pdev->dev, "clock ownership timeout: 0x%x\n",
+ slp);
+
+ writel(val, isp->base + ctrl->freq_ctl);
+
+ ret = readl_poll_timeout(isp->base + isp->buttress.regs->pwr_status,
+ val, (val & ctrl->pwr_sts_mask) == pwr_sts,
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev,
+ "Change power status timeout with 0x%x\n", val);
+ return ret;
+ }
+
+ slp = readl(isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG);
+ writel(slp & ~ovrd_clk, isp->base + IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG);
+
+ return 0;
+}
+
+static int __ipu7_power_off(struct device *dev,
+ const struct ipu6_buttress_ctrl *ctrl)
{
struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
u32 pwr_sts, val;
int ret;
- if (!ctrl)
- return 0;
+ writel(0x8U, isp->base + ctrl->freq_ctl);
- mutex_lock(&isp->buttress.power_mutex);
+ pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
+ ret = readl_poll_timeout(isp->base + isp->buttress.regs->pwr_status,
+ val, (val & ctrl->pwr_sts_mask) == pwr_sts,
+ 100, BUTTRESS_POWER_TIMEOUT_US);
+ if (ret) {
+ dev_err(&isp->pdev->dev,
+ "Change power status timeout with 0x%x\n", val);
+ return ret;
+ }
+
+ if (ctrl->subsys_id == IPU_ISYS) {
+ ipu7_isys_d2d_power(isp, false);
+ ipu7_nde_control(isp, false);
+ }
+
+ return 0;
+}
+
+static int __ipu6_power(struct device *dev,
+ const struct ipu6_buttress_ctrl *ctrl, bool on)
+{
+ struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
+ u32 pwr_sts, val;
+ int ret;
if (!on) {
val = 0;
pwr_sts = ctrl->pwr_sts_off << ctrl->pwr_sts_shift;
} else {
val = BUTTRESS_FREQ_CTL_START |
- FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK,
- ctrl->ratio) |
- FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK,
- ctrl->qos_floor) |
- BUTTRESS_FREQ_CTL_ICCMAX_LEVEL;
+ FIELD_PREP(BUTTRESS_FREQ_CTL_RATIO_MASK, ctrl->ratio) |
+ FIELD_PREP(BUTTRESS_FREQ_CTL_QOS_FLOOR_MASK,
+ ctrl->qos_floor) |
+ BUTTRESS_FREQ_CTL_ICCMAX_LEVEL;
pwr_sts = ctrl->pwr_sts_on << ctrl->pwr_sts_shift;
}
writel(val, isp->base + ctrl->freq_ctl);
- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE,
+ ret = readl_poll_timeout(isp->base + isp->buttress.regs->pwr_status,
val, (val & ctrl->pwr_sts_mask) == pwr_sts,
100, BUTTRESS_POWER_TIMEOUT_US);
if (ret)
dev_err(&isp->pdev->dev,
"Change power status timeout with 0x%x\n", val);
+ return ret;
+}
+
+int ipu6_buttress_power(struct device *dev,
+ const struct ipu6_buttress_ctrl *ctrl, bool on)
+{
+ struct ipu6_device *isp = to_ipu6_bus_device(dev)->isp;
+ int ret;
+
+ if (!ctrl)
+ return 0;
+
+ mutex_lock(&isp->buttress.power_mutex);
+
+ if (IS_IPU7(isp))
+ ret = on ? __ipu7_power_on(dev, ctrl) :
+ __ipu7_power_off(dev, ctrl);
+ else
+ ret = __ipu6_power(dev, ctrl, on);
+
mutex_unlock(&isp->buttress.power_mutex);
return ret;
@@ -487,7 +627,7 @@ bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp)
{
u32 val;
- val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
+ val = readl(isp->base + isp->buttress.regs->security_ctl);
return val & BUTTRESS_SECURITY_CTL_FW_SECURE_MODE;
}
@@ -499,7 +639,7 @@ bool ipu6_buttress_auth_done(struct ipu6_device *isp)
if (!isp->secure_mode)
return true;
- val = readl(isp->base + BUTTRESS_REG_SECURITY_CTL);
+ val = readl(isp->base + isp->buttress.regs->security_ctl);
val = FIELD_GET(BUTTRESS_SECURITY_CTL_FW_SETUP_MASK, val);
return val == BUTTRESS_SECURITY_CTL_AUTH_DONE;
@@ -517,10 +657,10 @@ int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
}
writel(BUTTRESS_FW_RESET_CTL_START, isp->base +
- BUTTRESS_REG_FW_RESET_CTL);
+ isp->buttress.regs->fw_reset_ctl);
- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_FW_RESET_CTL, val,
- val & BUTTRESS_FW_RESET_CTL_DONE, 500,
+ ret = readl_poll_timeout(isp->base + isp->buttress.regs->fw_reset_ctl,
+ val, val & BUTTRESS_FW_RESET_CTL_DONE, 500,
BUTTRESS_CSE_FWRESET_TIMEOUT_US);
if (ret) {
dev_err(&isp->pdev->dev,
@@ -529,62 +669,63 @@ int ipu6_buttress_reset_authentication(struct ipu6_device *isp)
}
dev_dbg(&isp->pdev->dev, "FW reset for authentication done\n");
- writel(0, isp->base + BUTTRESS_REG_FW_RESET_CTL);
+ writel(0, isp->base + isp->buttress.regs->fw_reset_ctl);
+
/* leave some time for HW restore */
usleep_range(800, 1000);
return 0;
}
-int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
- const struct firmware *fw, struct sg_table *sgt)
+int ipu6_map_fw_region(struct ipu6_bus_device *sys, const void *data,
+ size_t size, enum dma_data_direction dir,
+ unsigned long attrs)
{
- bool is_vmalloc = is_vmalloc_addr(fw->data);
+ bool is_vmalloc = is_vmalloc_addr(data);
struct pci_dev *pdev = sys->isp->pdev;
+ struct sg_table *sgt = &sys->fw_sgt;
struct page **pages;
- const void *addr;
unsigned long n_pages;
unsigned int i;
int ret;
- if (!is_vmalloc && !virt_addr_valid(fw->data))
+ if (!is_vmalloc && !virt_addr_valid(data))
return -EDOM;
- n_pages = PFN_UP(fw->size);
+ n_pages = PFN_UP(size);
pages = kmalloc_objs(*pages, n_pages);
if (!pages)
return -ENOMEM;
- addr = fw->data;
for (i = 0; i < n_pages; i++) {
struct page *p = is_vmalloc ?
- vmalloc_to_page(addr) : virt_to_page(addr);
+ vmalloc_to_page(data) : virt_to_page(data);
if (!p) {
ret = -ENOMEM;
goto out;
}
pages[i] = p;
- addr += PAGE_SIZE;
+ data += PAGE_SIZE;
}
- ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, fw->size,
+ ret = sg_alloc_table_from_pages(sgt, pages, n_pages, 0, size,
GFP_KERNEL);
if (ret) {
ret = -ENOMEM;
goto out;
}
- ret = dma_map_sgtable(&pdev->dev, sgt, DMA_TO_DEVICE, 0);
+ ret = dma_map_sgtable(&pdev->dev, sgt, dir, 0);
if (ret) {
sg_free_table(sgt);
goto out;
}
- ret = ipu6_dma_map_sgtable(sys, sgt, DMA_TO_DEVICE, 0);
+ ret = ipu6_dma_map_sgtable(sys, sgt, dir, attrs);
if (ret) {
- dma_unmap_sgtable(&pdev->dev, sgt, DMA_TO_DEVICE, 0);
+ dma_unmap_sgtable(&pdev->dev, sgt, dir, 0);
sg_free_table(sgt);
goto out;
}
@@ -596,18 +737,18 @@ out:
return ret;
}
-EXPORT_SYMBOL_NS_GPL(ipu6_buttress_map_fw_image, "INTEL_IPU6");
+EXPORT_SYMBOL_NS_GPL(ipu6_map_fw_region, "INTEL_IPU6");
-void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
- struct sg_table *sgt)
+void ipu6_unmap_fw_region(struct ipu6_bus_device *sys,
+ enum dma_data_direction dir)
{
struct pci_dev *pdev = sys->isp->pdev;
- ipu6_dma_unmap_sgtable(sys, sgt, DMA_TO_DEVICE, 0);
- dma_unmap_sgtable(&pdev->dev, sgt, DMA_TO_DEVICE, 0);
- sg_free_table(sgt);
+ ipu6_dma_unmap_sgtable(sys, &sys->fw_sgt, dir, 0);
+ dma_unmap_sgtable(&pdev->dev, &sys->fw_sgt, dir, 0);
+ sg_free_table(&sys->fw_sgt);
}
-EXPORT_SYMBOL_NS_GPL(ipu6_buttress_unmap_fw_image, "INTEL_IPU6");
+EXPORT_SYMBOL_NS_GPL(ipu6_unmap_fw_region, "INTEL_IPU6");
int ipu6_buttress_authenticate(struct ipu6_device *isp)
{
@@ -634,11 +775,18 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp)
* Write address of FIT table to FW_SOURCE register
* Let's use fw address. I.e. not using FIT table yet
*/
- data = lower_32_bits(isp->psys->pkg_dir_dma_addr);
- writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO);
+ if (IS_IPU7(isp)) {
+ writel(isp->cpd_fw->size,
+ isp->base + IPU7_BUTTRESS_REG_FW_SOURCE_SIZE);
+ writel(sg_dma_address(isp->psys->fw_sgt.sgl),
+ isp->base + IPU7_BUTTRESS_REG_FW_SOURCE_BASE);
+ } else {
+ data = lower_32_bits(isp->psys->pkg_dir_dma_addr);
+ writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_LO);
- data = upper_32_bits(isp->psys->pkg_dir_dma_addr);
- writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI);
+ data = upper_32_bits(isp->psys->pkg_dir_dma_addr);
+ writel(data, isp->base + BUTTRESS_REG_FW_SOURCE_BASE_HI);
+ }
/*
* Write boot_load into IU2CSEDATA0
@@ -659,7 +807,7 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp)
mask = BUTTRESS_SECURITY_CTL_FW_SETUP_MASK;
done = BUTTRESS_SECURITY_CTL_FW_SETUP_DONE;
fail = BUTTRESS_SECURITY_CTL_AUTH_FAILED;
- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
+ ret = readl_poll_timeout(isp->base + b->regs->security_ctl, data,
((data & mask) == done ||
(data & mask) == fail), 500,
BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
@@ -674,8 +822,11 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp)
goto out_unlock;
}
- ret = readl_poll_timeout(psys_pdata->base + BOOTLOADER_STATUS_OFFSET,
- data, data == BOOTLOADER_MAGIC_KEY, 500,
+ void __iomem *base = IS_IPU7(isp) ?
+ isp->base + IPU7_BUTTRESS_REG_FW_BOOT_PARAMS7 :
+ psys_pdata->base + BOOTLOADER_STATUS_OFFSET;
+
+ ret = readl_poll_timeout(base, data, data == BOOTLOADER_MAGIC_KEY, 500,
BUTTRESS_CSE_BOOTLOAD_TIMEOUT_US);
if (ret) {
dev_err(&isp->pdev->dev, "Unexpected magic number 0x%x\n",
@@ -699,7 +850,7 @@ int ipu6_buttress_authenticate(struct ipu6_device *isp)
}
done = BUTTRESS_SECURITY_CTL_AUTH_DONE;
- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_SECURITY_CTL, data,
+ ret = readl_poll_timeout(isp->base + b->regs->security_ctl, data,
((data & mask) == done ||
(data & mask) == fail), 500,
BUTTRESS_CSE_AUTHENTICATE_TIMEOUT_US);
@@ -724,15 +875,16 @@ out_unlock:
static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
{
+ const struct ipu6_buttress_registers *regs = isp->buttress.regs;
u32 val, mask, done;
int ret;
mask = BUTTRESS_PWR_STATE_HH_STATUS_MASK;
writel(BUTTRESS_FABRIC_CMD_START_TSC_SYNC,
- isp->base + BUTTRESS_REG_FABRIC_CMD);
+ isp->base + regs->fabric_cmd);
- val = readl(isp->base + BUTTRESS_REG_PWR_STATE);
+ val = readl(isp->base + regs->pwr_status);
val = FIELD_GET(mask, val);
if (val == BUTTRESS_PWR_STATE_HH_STATE_ERR) {
dev_err(&isp->pdev->dev, "Start tsc sync failed\n");
@@ -740,8 +892,8 @@ static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
}
done = BUTTRESS_PWR_STATE_HH_STATE_DONE;
- ret = readl_poll_timeout(isp->base + BUTTRESS_REG_PWR_STATE, val,
- FIELD_GET(mask, val) == done, 500,
+ ret = readl_poll_timeout(isp->base + regs->pwr_status,
+ val, FIELD_GET(mask, val) == done, 500,
BUTTRESS_TSC_SYNC_TIMEOUT_US);
if (ret)
dev_err(&isp->pdev->dev, "Start tsc sync timeout\n");
@@ -749,11 +901,30 @@ static int ipu6_buttress_send_tsc_request(struct ipu6_device *isp)
return ret;
}
-int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
+static int __ipu7p5_start_tsc_sync(struct ipu6_device *isp)
{
- unsigned int i;
+ u32 val;
+
+ val = readl(isp->base + IPU7_BUTTRESS_REG_TSC_CTL);
+ val |= IPU7_BUTTRESS_SEL_PB_TIMESTAMP;
+ writel(val, isp->base + IPU7_BUTTRESS_REG_TSC_CTL);
+
+ for (unsigned int i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
+ val = readl(isp->base + IPU7_BUTTRESS_REG_PB_TIMESTAMP_VALID);
+ if (val == 1)
+ return 0;
+
+ usleep_range(40, 50);
+ }
+
+ dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
- for (i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
+ return -ETIMEDOUT;
+}
+
+static int __ipu6_start_tsc_sync(struct ipu6_device *isp)
+{
+ for (unsigned int i = 0; i < BUTTRESS_TSC_SYNC_RESET_TRIAL_MAX; i++) {
u32 val;
int ret;
@@ -761,28 +932,39 @@ int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
if (ret != -ETIMEDOUT)
return ret;
- val = readl(isp->base + BUTTRESS_REG_TSW_CTL);
+ u32 tsw_ctl = isp->buttress.regs->tsw_ctl;
+
+ val = readl(isp->base + tsw_ctl);
val = val | BUTTRESS_TSW_CTL_SOFT_RESET;
- writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
+ writel(val, isp->base + tsw_ctl);
val = val & ~BUTTRESS_TSW_CTL_SOFT_RESET;
- writel(val, isp->base + BUTTRESS_REG_TSW_CTL);
+ writel(val, isp->base + tsw_ctl);
}
dev_err(&isp->pdev->dev, "TSC sync failed (timeout)\n");
return -ETIMEDOUT;
}
+
+int ipu6_buttress_start_tsc_sync(struct ipu6_device *isp)
+{
+ if (IS_IPU7P5(isp))
+ return __ipu7p5_start_tsc_sync(isp);
+
+ return __ipu6_start_tsc_sync(isp);
+}
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_start_tsc_sync, "INTEL_IPU6");
void ipu6_buttress_tsc_read(struct ipu6_device *isp, u64 *val)
{
+ void __iomem *tsc = isp->base + isp->buttress.regs->tsc_lo;
u32 tsc_hi_1, tsc_hi_2, tsc_lo;
unsigned long flags;
local_irq_save(flags);
- tsc_hi_1 = readl(isp->base + BUTTRESS_REG_TSC_HI);
- tsc_lo = readl(isp->base + BUTTRESS_REG_TSC_LO);
- tsc_hi_2 = readl(isp->base + BUTTRESS_REG_TSC_HI);
+ tsc_hi_1 = readl(tsc + BUTTRESS_TSC_HI_OFFSET);
+ tsc_lo = readl(tsc);
+ tsc_hi_2 = readl(tsc + BUTTRESS_TSC_HI_OFFSET);
if (tsc_hi_1 == tsc_hi_2) {
*val = (u64)tsc_hi_1 << 32 | tsc_lo;
} else {
@@ -811,13 +993,77 @@ u64 ipu6_buttress_tsc_ticks_to_ns(u64 ticks, const struct ipu6_device *isp)
}
EXPORT_SYMBOL_NS_GPL(ipu6_buttress_tsc_ticks_to_ns, "INTEL_IPU6");
+/* trigger uc control to wakeup fw */
+void ipu7_buttress_wakeup_isys(const struct ipu6_device *isp)
+{
+ u32 val;
+
+ val = readl(isp->base + IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS);
+ val |= IPU7_UCX_CTL_WAKEUP;
+ writel(val, isp->base + IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_buttress_wakeup_isys, "INTEL_IPU6");
+
+u32 ipu7_buttress_get_isys_freq(struct ipu6_device *isp)
+{
+ u32 val;
+
+ val = readl(isp->base + IPU7_BUTTRESS_REG_IS_WORKPOINT_REQ);
+ val &= IPU7_BUTTRESS_IS_FREQ_CTL_RATIO_MASK;
+
+ return val * 50 / 3;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_buttress_get_isys_freq, "INTEL_IPU6");
+
+static const struct x86_cpu_id ipu7_misc_cfg_exclusion[] = {
+ X86_MATCH_VFM_STEPS(INTEL_PANTHERLAKE_L, 0x1, 0x1, 0),
+ {},
+};
+
+#define IPU7_WRXREQOP_OVRD_VAL_MASK GENMASK(22, 19)
+
+static void ipu7_buttress_setup(struct ipu6_device *isp)
+{
+ struct ipu6_buttress *b = &isp->buttress;
+ u32 val;
+
+ /* program PB BAR */
+ writel(0, isp->pb_base + IPU7_GLOBAL_INTERRUPT_MASK);
+ val = readl(isp->pb_base + IPU7_BAR2_MISC_CONFIG);
+
+ val |= 0x100U;
+ if (!IS_IPU7_MTL(isp) && !x86_match_cpu(ipu7_misc_cfg_exclusion))
+ val |= FIELD_PREP(IPU7_WRXREQOP_OVRD_VAL_MASK, 0xf) | BIT(18);
+
+ writel(val, isp->pb_base + IPU7_BAR2_MISC_CONFIG);
+
+ if (IS_IPU7P5(isp)) {
+ writel(BIT(14), isp->pb_base + IPU7_TLBID_HASH_ENABLE_63_32);
+ writel(BIT(9), isp->pb_base + IPU7_TLBID_HASH_ENABLE_95_64);
+ } else {
+ writel(BIT(22), isp->pb_base + IPU7_TLBID_HASH_ENABLE_63_32);
+ writel(BIT(1), isp->pb_base + IPU7_TLBID_HASH_ENABLE_127_96);
+ }
+
+ writel(b->regs->irq_all, isp->base + b->regs->irq_clear);
+ writel(b->regs->irq_all, isp->base + IPU7_BUTTRESS_REG_IRQ_MASK);
+ writel(b->regs->irq_all, isp->base + b->regs->irq_enable);
+
+ /* LNL SW workaround for PS PD hang when PS sub-domain during PD */
+ writel(IPU7_BUTTRESS_CG_CTRL_PS_FSM_CG, isp->base + IPU7_BUTTRESS_REG_CG_CTRL_BITS);
+}
+
void ipu6_buttress_restore(struct ipu6_device *isp)
{
struct ipu6_buttress *b = &isp->buttress;
- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
- writel(b->wdt_cached_value, isp->base + BUTTRESS_REG_WDT);
+ if (IS_IPU7(isp)) {
+ ipu7_buttress_setup(isp);
+ } else {
+ writel(b->regs->irq_all, isp->base + b->regs->irq_clear);
+ writel(b->regs->irq_all, isp->base + b->regs->irq_enable);
+ }
+ writel(b->wdt_cached_value, isp->base + b->regs->wdt);
}
int ipu6_buttress_init(struct ipu6_device *isp)
@@ -830,54 +1076,52 @@ int ipu6_buttress_init(struct ipu6_device *isp)
mutex_init(&b->auth_mutex);
mutex_init(&b->cons_mutex);
mutex_init(&b->ipc_mutex);
- init_completion(&b->cse.send_complete);
- init_completion(&b->cse.recv_complete);
-
- b->cse.nack = BUTTRESS_CSE2IUDATA0_IPC_NACK;
- b->cse.nack_mask = BUTTRESS_CSE2IUDATA0_IPC_NACK_MASK;
- b->cse.csr_in = BUTTRESS_REG_CSE2IUCSR;
- b->cse.csr_out = BUTTRESS_REG_IU2CSECSR;
- b->cse.db0_in = BUTTRESS_REG_CSE2IUDB0;
- b->cse.db0_out = BUTTRESS_REG_IU2CSEDB0;
- b->cse.data0_in = BUTTRESS_REG_CSE2IUDATA0;
- b->cse.data0_out = BUTTRESS_REG_IU2CSEDATA0;
+ init_completion(&b->ipc.send_complete);
+ init_completion(&b->ipc.recv_complete);
INIT_LIST_HEAD(&b->constraints);
-
isp->secure_mode = ipu6_buttress_get_secure_mode(isp);
- dev_dbg(&isp->pdev->dev, "IPU6 in %s mode touch 0x%x mask 0x%x\n",
- isp->secure_mode ? "secure" : "non-secure",
- readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH),
- readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
-
- b->wdt_cached_value = readl(isp->base + BUTTRESS_REG_WDT);
- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_CLEAR);
- writel(BUTTRESS_IRQS, isp->base + BUTTRESS_REG_ISR_ENABLE);
-
- /* get ref_clk frequency by reading the indication in btrs control */
- val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
- val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val);
-
- switch (val) {
- case 0x0:
- b->ref_clk = 240;
- break;
- case 0x1:
- b->ref_clk = 192;
- break;
- case 0x2:
+
+ dev_dbg(&isp->pdev->dev, "IPU in %s mode\n",
+ isp->secure_mode ? "secure" : "non-secure");
+
+ if (IS_IPU7(isp)) {
+ ipu7_buttress_setup(isp);
b->ref_clk = 384;
- break;
- default:
- dev_warn(&isp->pdev->dev,
- "Unsupported ref clock, use 19.2Mhz by default.\n");
- b->ref_clk = 192;
- break;
+ } else {
+ dev_dbg(&isp->pdev->dev, "IPU6 touch 0x%x mask 0x%x\n",
+ readl(isp->base + BUTTRESS_REG_SECURITY_TOUCH),
+ readl(isp->base + BUTTRESS_REG_CAMERA_MASK));
+
+ writel(b->regs->irq_all, isp->base + b->regs->irq_clear);
+ writel(b->regs->irq_all, isp->base + b->regs->irq_enable);
+
+ /* get ref_clk frequency by reading the indication in btrs control */
+ val = readl(isp->base + b->regs->btrs_ctrl);
+ val = FIELD_GET(BUTTRESS_REG_BTRS_CTRL_REF_CLK_IND, val);
+
+ switch (val) {
+ case 0x0:
+ b->ref_clk = 240;
+ break;
+ case 0x1:
+ b->ref_clk = 192;
+ break;
+ case 0x2:
+ b->ref_clk = 384;
+ break;
+ default:
+ dev_warn(&isp->pdev->dev,
+ "Unsupported ref clock, use 19.2Mhz by default.\n");
+ b->ref_clk = 192;
+ break;
+ }
}
+ b->wdt_cached_value = readl(isp->base + b->regs->wdt);
/* Retry couple of times in case of CSE initialization is delayed */
do {
- ret = ipu6_buttress_ipc_reset(isp, &b->cse);
+ ret = ipu6_buttress_ipc_reset(isp);
if (ret) {
dev_warn(&isp->pdev->dev,
"IPC reset protocol failed, retrying\n");
@@ -901,7 +1145,7 @@ void ipu6_buttress_exit(struct ipu6_device *isp)
{
struct ipu6_buttress *b = &isp->buttress;
- writel(0, isp->base + BUTTRESS_REG_ISR_ENABLE);
+ writel(0, isp->base + b->regs->irq_enable);
mutex_destroy(&b->power_mutex);
mutex_destroy(&b->auth_mutex);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-buttress.h b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
index 51e5ad48db82..79d8f5893686 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-buttress.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-buttress.h
@@ -26,25 +26,50 @@ struct ipu6_buttress_ctrl {
u32 freq_ctl, pwr_sts_shift, pwr_sts_mask, pwr_sts_on, pwr_sts_off;
unsigned int ratio;
unsigned int qos_floor;
+ int subsys_id;
};
struct ipu6_buttress_ipc {
struct completion send_complete;
struct completion recv_complete;
- u32 nack;
- u32 nack_mask;
u32 recv_data;
- u32 csr_out;
+};
+
+struct ipu6_buttress_registers {
+ /* Registers */
+ u32 irq_status;
+ u32 irq_clear;
+ u32 irq_enable;
+ u32 pwr_status;
+ u32 security_ctl;
+ u32 fw_reset_ctl;
+ u32 fabric_cmd;
+ u32 tsw_ctl;
+ u32 tsc_lo;
+ u32 wdt;
+ u32 btrs_ctrl;
u32 csr_in;
+ u32 csr_out;
u32 db0_in;
u32 db0_out;
- u32 data0_out;
u32 data0_in;
+ u32 data0_out;
+ u32 sku_id;
+
+ /* Bitmasks */
+ u32 irq_is;
+ u32 irq_ps;
+ u32 irq_all;
+ u32 irq_events;
+ u32 irq_cse_ipc;
+ u32 irq_exec_done;
+ u32 irq_sai;
};
struct ipu6_buttress {
struct mutex power_mutex, auth_mutex, cons_mutex, ipc_mutex;
- struct ipu6_buttress_ipc cse;
+ struct ipu6_buttress_ipc ipc;
+ const struct ipu6_buttress_registers *regs;
struct list_head constraints;
u32 wdt_cached_value;
bool force_suspend;
@@ -58,13 +83,12 @@ struct ipu6_ipc_buttress_bulk_msg {
u8 cmd_size;
};
-int ipu6_buttress_ipc_reset(struct ipu6_device *isp,
- struct ipu6_buttress_ipc *ipc);
-int ipu6_buttress_map_fw_image(struct ipu6_bus_device *sys,
- const struct firmware *fw,
- struct sg_table *sgt);
-void ipu6_buttress_unmap_fw_image(struct ipu6_bus_device *sys,
- struct sg_table *sgt);
+int ipu6_buttress_ipc_reset(struct ipu6_device *isp);
+int ipu6_map_fw_region(struct ipu6_bus_device *sys, const void *data,
+ size_t size, enum dma_data_direction dir,
+ unsigned long attrs);
+void ipu6_unmap_fw_region(struct ipu6_bus_device *sys,
+ enum dma_data_direction dir);
int ipu6_buttress_power(struct device *dev,
const struct ipu6_buttress_ctrl *ctrl, bool on);
bool ipu6_buttress_get_secure_mode(struct ipu6_device *isp);
@@ -82,4 +106,6 @@ void ipu6_buttress_exit(struct ipu6_device *isp);
void ipu6_buttress_csi_port_config(struct ipu6_device *isp,
u32 legacy, u32 combo);
void ipu6_buttress_restore(struct ipu6_device *isp);
+void ipu7_buttress_wakeup_isys(const struct ipu6_device *isp);
+u32 ipu7_buttress_get_isys_freq(struct ipu6_device *isp);
#endif /* IPU6_BUTTRESS_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.c b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
index b7013f6524ec..1fadb0f2f6c1 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-cpd.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.c
@@ -16,6 +16,7 @@
#include "ipu6-bus.h"
#include "ipu6-cpd.h"
#include "ipu6-dma.h"
+#include "ipu7-mmu-hw.h"
/* 15 entries + header*/
#define MAX_PKG_DIR_ENT_CNT 16
@@ -62,6 +63,20 @@ static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)
#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)
+#define IPU7_CPD_BINARY_START_IDX 1U
+#define IPU7_CPD_METADATA_START_IDX 2U
+#define IPU7_CPD_BINARY_NUM 2U /* ISYS + PSYS */
+#define IPU7_CPD_METADATA_ATTR 0xa
+#define IPU7_CPD_METADATA_IPL 0x1c
+/*
+ * Entries include:
+ * 1 manifest entry.
+ * 1 metadata entry for each sub system(ISYS and PSYS).
+ * 1 binary entry for each sub system(ISYS and PSYS).
+ */
+#define IPU7_CPD_ENTRY_NUM (IPU7_CPD_BINARY_NUM * 2U + 1U)
+#define IPU7_MAX_MANIFEST_SIZE (SZ_4K * sizeof(u32))
+
static const struct ipu6_cpd_metadata_cmpnt_hdr *
ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,
unsigned int metadata_size, u8 idx)
@@ -220,8 +235,9 @@ EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, "INTEL_IPU6");
void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
{
- ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir,
- adev->pkg_dir_dma_addr, 0);
+ if (adev->pkg_dir)
+ ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir,
+ adev->pkg_dir_dma_addr, 0);
}
EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, "INTEL_IPU6");
@@ -266,7 +282,6 @@ static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
u32 moduledata_size)
{
const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;
- int ret;
/* Ensure moduledata hdr is within moduledata */
if (moduledata_size < sizeof(*mod_hdr) ||
@@ -276,15 +291,9 @@ static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
}
dev_dbg(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date);
- ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,
- moduledata_size - mod_hdr->hdr_len,
- moduledata_size);
- if (ret) {
- dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n");
- return ret;
- }
-
- return 0;
+ return ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,
+ moduledata_size - mod_hdr->hdr_len,
+ moduledata_size);
}
static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
@@ -316,47 +325,163 @@ static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
return 0;
}
-int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
- unsigned long cpd_file_size)
+static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx)
{
- const struct ipu6_cpd_hdr *hdr = cpd_file;
- const struct ipu6_cpd_ent *ent;
- int ret;
+ const struct ipu6_cpd_ent *cpd_ent =
+ ipu6_cpd_get_entry(cpd, IPU7_CPD_METADATA_START_IDX + idx * 2);
- ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,
- cpd_file_size);
- if (ret) {
- dev_err(&isp->pdev->dev, "Invalid CPD in file\n");
- return ret;
+ return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset);
+}
+
+static int ipu7_cpd_validate_metadata(struct ipu6_device *isp,
+ const void *cpd, int idx)
+{
+ const struct ipu6_cpd_ent *cpd_ent =
+ ipu6_cpd_get_entry(cpd, IPU7_CPD_METADATA_START_IDX + idx * 2);
+ const struct ipu6_cpd_ent *bin_ent =
+ ipu6_cpd_get_entry(cpd, IPU7_CPD_BINARY_START_IDX + idx * 2);
+ const struct ipu7_cpd_metadata *metadata =
+ ipu7_cpd_get_metadata(cpd, idx);
+ struct device *dev = &isp->pdev->dev;
+ u32 offset;
+
+ /* Sanity check for metadata size */
+ if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) {
+ dev_err(dev, "Invalid metadata size\n");
+ return -EINVAL;
}
- /* Check for CPD file marker */
- if (hdr->hdr_mark != CPD_HDR_MARK) {
- dev_err(&isp->pdev->dev, "Invalid CPD header\n");
+ /* Validate type and length of metadata sections */
+ if (metadata->attr.hdr.type != IPU7_CPD_METADATA_ATTR) {
+ dev_err(dev, "Invalid metadata attr type (%d)\n",
+ metadata->attr.hdr.type);
+ return -EINVAL;
+ }
+ if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) {
+ dev_err(dev, "Invalid metadata attr size (%d)\n",
+ metadata->attr.hdr.len);
+ return -EINVAL;
+ }
+ if (metadata->ipl.hdr.type != IPU7_CPD_METADATA_IPL) {
+ dev_err(dev, "Invalid metadata ipl type (%d)\n",
+ metadata->ipl.hdr.type);
+ return -EINVAL;
+ }
+ if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) {
+ dev_err(dev, "Invalid metadata ipl size (%d)\n",
+ metadata->ipl.hdr.len);
+ return -EINVAL;
+ }
+
+ offset = metadata->ipl.param[0];
+ if (offset > IPU7_FW_CODE_REGION_SIZE ||
+ bin_ent->len > IPU7_FW_CODE_REGION_SIZE - offset) {
+ dev_err(dev, "Incorrect binary size %u and offset %u\n",
+ bin_ent->len, offset);
return -EINVAL;
}
- /* Sanity check for manifest size */
+ return 0;
+}
+
+static int __ipu7_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
+ unsigned long cpd_file_size)
+{
+ const struct ipu6_cpd_ent *ent;
+ const struct ipu7_cpd_hdr *hdr = cpd_file;
+ unsigned int i;
+
ent = ipu6_cpd_get_manifest(cpd_file);
- if (ent->len > MAX_MANIFEST_SIZE) {
+ if (ent->len > IPU7_MAX_MANIFEST_SIZE) {
dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");
return -EINVAL;
}
+ /* Sanity check for CPD entry header */
+ if (hdr->ent_cnt != IPU7_CPD_ENTRY_NUM) {
+ dev_err(&isp->pdev->dev, "Invalid CPD entry number %d\n",
+ hdr->ent_cnt);
+ return -EINVAL;
+ }
/* Validate metadata */
+ for (i = 0; i < IPU7_CPD_BINARY_NUM; i++) {
+ int ret = ipu7_cpd_validate_metadata(isp, cpd_file, i);
+
+ if (ret) {
+ dev_err(&isp->pdev->dev, "Invalid metadata(%d)\n", i);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int __ipu6_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
+ unsigned long cpd_file_size)
+{
+ const struct ipu6_cpd_ent *ent;
+ int ret;
+
+ ent = ipu6_cpd_get_manifest(cpd_file);
+ if (ent->len > MAX_MANIFEST_SIZE) {
+ dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");
+ return -EINVAL;
+ }
+
ent = ipu6_cpd_get_metadata(cpd_file);
ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len);
- if (ret) {
- dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");
+ if (ret)
return ret;
- }
- /* Validate moduledata */
ent = ipu6_cpd_get_moduledata(cpd_file);
- ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
- ent->len);
+ return ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,
+ ent->len);
+}
+
+int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
+ unsigned long cpd_file_size)
+{
+ const struct ipu6_cpd_hdr *hdr = cpd_file;
+ int ret;
+
+ ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,
+ cpd_file_size);
if (ret)
- dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");
+ return ret;
+
+ /* Check for CPD file marker */
+ if (hdr->hdr_mark != CPD_HDR_MARK) {
+ dev_err(&isp->pdev->dev, "Invalid CPD header\n");
+ return -EINVAL;
+ }
+
+ if (IS_IPU7(isp))
+ return __ipu7_validate_cpd_file(isp, cpd_file, cpd_file_size);
+
+ return __ipu6_validate_cpd_file(isp, cpd_file, cpd_file_size);
+}
+
+int ipu6_ipu7_cpd_copy_binary(const void *cpd, const char *name, void *dst,
+ u32 *entry)
+{
+ unsigned int i;
+
+ for (i = 0; i < IPU7_CPD_BINARY_NUM; i++) {
+ const struct ipu7_cpd_metadata *metadata;
+ u8 idx = IPU7_CPD_BINARY_START_IDX + i * 2U;
+ const struct ipu6_cpd_ent *ent =
+ ipu6_cpd_get_entry(cpd, idx);
+
+ if (strncmp(ent->name, name, sizeof(ent->name)))
+ continue;
+
+ metadata = ipu7_cpd_get_metadata(cpd, i);
+ memcpy(dst + metadata->ipl.param[0], cpd + ent->offset,
+ ent->len);
+ *entry = metadata->ipl.param[2];
+
+ return 0;
+ }
- return ret;
+ return -ENOENT;
}
+EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_cpd_copy_binary, "INTEL_IPU6");
diff --git a/drivers/media/pci/intel/ipu6/ipu6-cpd.h b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
index e0e4fdeca902..c3a4859d8e79 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-cpd.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-cpd.h
@@ -98,8 +98,51 @@ struct ipu6_cpd_client_pkg_hdr {
u32 prog_bin_size;
} __packed;
+/* IPU7 */
+
+struct ipu7_cpd_hdr {
+ u32 hdr_mark;
+ u32 ent_cnt;
+ u8 hdr_ver;
+ u8 ent_ver;
+ u8 hdr_len;
+ u8 rsvd;
+ u8 partition_name[4];
+ u32 crc32;
+} __packed;
+
+struct ipu7_cpd_metadata_hdr {
+ u32 type;
+ u32 len;
+} __packed;
+
+struct ipu7_cpd_metadata_attr {
+ struct ipu7_cpd_metadata_hdr hdr;
+ u8 compression_type;
+ u8 encryption_type;
+ u8 rsvd[2];
+ u32 uncompressed_size;
+ u32 compressed_size;
+ u32 module_id;
+ u8 hash[48];
+} __packed;
+
+struct ipu7_cpd_metadata_ipl {
+ struct ipu7_cpd_metadata_hdr hdr;
+ u32 param[4];
+ u8 rsvd[8];
+} __packed;
+
+struct ipu7_cpd_metadata {
+ struct ipu7_cpd_metadata_attr attr;
+ struct ipu7_cpd_metadata_ipl ipl;
+} __packed;
+
int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src);
void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev);
int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
unsigned long cpd_file_size);
+int ipu6_ipu7_cpd_copy_binary(const void *cpd, const char *name, void *dst,
+ u32 *entry);
+
#endif /* IPU6_CPD_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.c b/drivers/media/pci/intel/ipu6/ipu6-dma.c
index fdcdb15b073c..90f530a93779 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-dma.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-dma.c
@@ -286,7 +286,7 @@ void ipu6_dma_free(struct ipu6_bus_device *sys, size_t size, void *vaddr,
__free_buffer(pages, size, attrs);
- mmu->tlb_invalidate(mmu);
+ mmu->ops->tlb_invalidate(mmu);
__free_iova(&mmu->dmap->iovad, iova);
@@ -366,11 +366,22 @@ void ipu6_dma_unmap_sg(struct ipu6_bus_device *sys, struct scatterlist *sglist,
ipu6_mmu_unmap(mmu->dmap->mmu_info, PFN_PHYS(iova->pfn_lo),
PFN_PHYS(iova_size(iova)));
- mmu->tlb_invalidate(mmu);
+ mmu->ops->tlb_invalidate(mmu);
__free_iova(&mmu->dmap->iovad, iova);
}
EXPORT_SYMBOL_NS_GPL(ipu6_dma_unmap_sg, "INTEL_IPU6");
+static struct iova *ipu7_get_fw_code_region(struct ipu6_bus_device *sys)
+{
+ struct ipu6_mmu *mmu = sys->mmu;
+ unsigned long lo, hi;
+
+ lo = iova_pfn(&mmu->dmap->iovad, IPU7_FW_CODE_REGION_START);
+ hi = iova_pfn(&mmu->dmap->iovad, IPU7_FW_CODE_REGION_END) - 1U;
+
+ return reserve_iova(&mmu->dmap->iovad, lo, hi);
+}
+
int ipu6_dma_map_sg(struct ipu6_bus_device *sys, struct scatterlist *sglist,
int nents, enum dma_data_direction dir,
unsigned long attrs)
@@ -397,10 +408,13 @@ int ipu6_dma_map_sg(struct ipu6_bus_device *sys, struct scatterlist *sglist,
dev_dbg(dev, "dmamap trying to map %d ents %zu pages\n",
nents, npages);
- iova = alloc_iova(&mmu->dmap->iovad, npages,
- PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
+ if (attrs & DMA_ATTR_RESERVE_REGION)
+ iova = ipu7_get_fw_code_region(sys);
+ else
+ iova = alloc_iova(&mmu->dmap->iovad, npages,
+ PHYS_PFN(mmu->dmap->mmu_info->aperture_end), 0);
if (!iova)
- return 0;
+ return -ENOMEM;
dev_dbg(dev, "dmamap: iova low pfn %lu, high pfn %lu\n", iova->pfn_lo,
iova->pfn_hi);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-dma.h b/drivers/media/pci/intel/ipu6/ipu6-dma.h
index ae9b9a5df57f..e5e0e7860423 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-dma.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-dma.h
@@ -10,6 +10,8 @@
#include "ipu6-bus.h"
+#define DMA_ATTR_RESERVE_REGION BIT(31)
+
struct ipu6_mmu_info;
struct ipu6_dma_mapping {
diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
index 62ed92ff1d30..f4f1cf7c86d2 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.c
@@ -7,6 +7,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
+#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@@ -15,6 +16,7 @@
#include "ipu6-isys.h"
#include "ipu6-platform-isys-csi2-reg.h"
#include "ipu6-platform-regs.h"
+#include "ipu7-fw-isys.h"
static const char send_msg_types[N_IPU6_FW_ISYS_SEND_TYPE][32] = {
"STREAM_OPEN",
@@ -32,7 +34,7 @@ static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id)
struct ipu6_fw_isys_proxy_resp_info_abi *resp;
int ret;
- resp = ipu6_recv_get_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
+ resp = ipu6_recv_get_token(isys->fwctx, IPU6_BASE_PROXY_RECV_QUEUES);
if (!resp)
return 1;
@@ -42,7 +44,7 @@ static int handle_proxy_response(struct ipu6_isys *isys, unsigned int req_id)
ret = req_id == resp->request_id ? 0 : -EIO;
- ipu6_recv_put_token(isys->fwcom, IPU6_BASE_PROXY_RECV_QUEUES);
+ ipu6_recv_put_token(isys->fwctx, IPU6_BASE_PROXY_RECV_QUEUES);
return ret;
}
@@ -52,7 +54,7 @@ int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
unsigned int index,
unsigned int offset, u32 value)
{
- struct ipu6_fw_com_context *ctx = isys->fwcom;
+ struct ipu6_fw_com_context *ctx = isys->fwctx;
struct device *dev = &isys->adev->auxdev.dev;
struct ipu6_fw_proxy_send_queue_token *token;
unsigned int timeout = 1000;
@@ -90,13 +92,13 @@ int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
return ret;
}
-int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
- const unsigned int stream_handle,
- void *cpu_mapped_buf,
- dma_addr_t dma_mapped_buf,
- size_t size, u16 send_type)
+static int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ void *cpu_mapped_buf,
+ dma_addr_t dma_mapped_buf,
+ size_t size, u16 send_type)
{
- struct ipu6_fw_com_context *ctx = isys->fwcom;
+ struct ipu6_fw_com_context *ctx = isys->fwctx;
struct device *dev = &isys->adev->auxdev.dev;
struct ipu6_fw_send_queue_token *token;
@@ -126,19 +128,11 @@ int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
return 0;
}
-int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
- const unsigned int stream_handle, u16 send_type)
-{
- return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
- send_type);
-}
-
-int ipu6_fw_isys_close(struct ipu6_isys *isys)
+static int ipu6_fw_isys_close(struct ipu6_isys *isys)
{
struct device *dev = &isys->adev->auxdev.dev;
int retry = IPU6_ISYS_CLOSE_RETRY;
- unsigned long flags;
- void *fwcom;
+ void *fwctx;
int ret;
/*
@@ -147,40 +141,36 @@ int ipu6_fw_isys_close(struct ipu6_isys *isys)
* to SP icache.
* spinlock to wait the interrupt handler to be finished
*/
- spin_lock_irqsave(&isys->power_lock, flags);
- ret = ipu6_fw_com_close(isys->fwcom);
- fwcom = isys->fwcom;
- isys->fwcom = NULL;
- spin_unlock_irqrestore(&isys->power_lock, flags);
+ ret = ipu6_fw_com_close(isys->fwctx);
+ fwctx = isys->fwctx;
+ isys->fwctx = NULL;
if (ret)
dev_err(dev, "Device close failure: %d\n", ret);
/* release probably fails if the close failed. Let's try still */
do {
usleep_range(400, 500);
- ret = ipu6_fw_com_release(fwcom, 0);
+ ret = ipu6_fw_com_release(fwctx, 0);
retry--;
} while (ret && retry);
if (ret) {
dev_err(dev, "Device release time out %d\n", ret);
- spin_lock_irqsave(&isys->power_lock, flags);
- isys->fwcom = fwcom;
- spin_unlock_irqrestore(&isys->power_lock, flags);
+ isys->fwctx = fwctx;
}
return ret;
}
-void ipu6_fw_isys_cleanup(struct ipu6_isys *isys)
+static void ipu6_fw_isys_cleanup(struct ipu6_isys *isys)
{
int ret;
- ret = ipu6_fw_com_release(isys->fwcom, 1);
+ ret = ipu6_fw_com_release(isys->fwctx, 1);
if (ret < 0)
dev_warn(&isys->adev->auxdev.dev,
"Device busy, fw_com release failed.");
- isys->fwcom = NULL;
+ isys->fwctx = NULL;
}
static void start_sp(struct ipu6_bus_device *adev)
@@ -212,7 +202,7 @@ static int query_sp(struct ipu6_bus_device *adev)
}
static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
- struct ipu6_fw_com_cfg *fwcom,
+ struct ipu6_fw_com_cfg *fwcom_cfg,
unsigned int num_streams)
{
unsigned int max_send_queues, max_sram_blocks, max_devq_size;
@@ -258,14 +248,16 @@ static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
if (!output_queue_cfg)
return -ENOMEM;
- fwcom->input = input_queue_cfg;
- fwcom->output = output_queue_cfg;
+ fwcom_cfg->input = input_queue_cfg;
+ fwcom_cfg->output = output_queue_cfg;
- fwcom->num_input_queues = isys_fw_cfg->num_send_queues[type_proxy] +
+ fwcom_cfg->num_input_queues =
+ isys_fw_cfg->num_send_queues[type_proxy] +
isys_fw_cfg->num_send_queues[type_dev] +
isys_fw_cfg->num_send_queues[type_msg];
- fwcom->num_output_queues = isys_fw_cfg->num_recv_queues[type_proxy] +
+ fwcom_cfg->num_output_queues =
+ isys_fw_cfg->num_recv_queues[type_proxy] +
isys_fw_cfg->num_recv_queues[type_dev] +
isys_fw_cfg->num_recv_queues[type_msg];
@@ -280,7 +272,7 @@ static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
isys_fw_cfg->buffer_partition.num_gda_pages[i] = 0;
}
- /* FW assumes proxy interface at fwcom queue 0 */
+ /* FW assumes proxy interface at fwcom_cfg queue 0 */
for (i = 0; i < isys_fw_cfg->num_send_queues[type_proxy]; i++) {
input_queue_cfg[i].token_size =
sizeof(struct ipu6_fw_proxy_send_queue_token);
@@ -314,34 +306,34 @@ static int ipu6_isys_fwcom_cfg_init(struct ipu6_isys *isys,
IPU6_ISYS_SIZE_RECV_QUEUE;
}
- fwcom->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset;
- fwcom->specific_addr = isys_fw_cfg;
- fwcom->specific_size = sizeof(*isys_fw_cfg);
+ fwcom_cfg->dmem_addr = isys->pdata->ipdata->hw_variant.dmem_offset;
+ fwcom_cfg->specific_addr = isys_fw_cfg;
+ fwcom_cfg->specific_size = sizeof(*isys_fw_cfg);
return 0;
}
-int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
+static int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
{
struct device *dev = &isys->adev->auxdev.dev;
int retry = IPU6_ISYS_OPEN_RETRY;
- struct ipu6_fw_com_cfg fwcom = {
+ struct ipu6_fw_com_cfg fwcom_cfg = {
.cell_start = start_sp,
.cell_ready = query_sp,
.buttress_boot_offset = SYSCOM_BUTTRESS_FW_PARAMS_ISYS_OFFSET,
};
int ret;
- ipu6_isys_fwcom_cfg_init(isys, &fwcom, num_streams);
+ ipu6_isys_fwcom_cfg_init(isys, &fwcom_cfg, num_streams);
- isys->fwcom = ipu6_fw_com_prepare(&fwcom, isys->adev,
+ isys->fwctx = ipu6_fw_com_prepare(&fwcom_cfg, isys->adev,
isys->pdata->base);
- if (!isys->fwcom) {
+ if (!isys->fwctx) {
dev_err(dev, "isys fw com prepare failed\n");
return -EIO;
}
- ret = ipu6_fw_com_open(isys->fwcom);
+ ret = ipu6_fw_com_open(isys->fwctx);
if (ret) {
dev_err(dev, "isys fw com open failed %d\n", ret);
return ret;
@@ -349,7 +341,7 @@ int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
do {
usleep_range(400, 500);
- if (ipu6_fw_com_ready(isys->fwcom))
+ if (ipu6_fw_com_ready(isys->fwctx))
break;
retry--;
} while (retry > 0);
@@ -363,20 +355,21 @@ int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
return ret;
}
-struct ipu6_fw_isys_resp_info_abi *
-ipu6_fw_isys_get_resp(void *context, unsigned int queue)
+static struct ipu6_fw_isys_resp_info_abi *
+ipu6_fw_isys_get_resp(struct ipu6_isys *isys)
{
- return ipu6_recv_get_token(context, queue);
+ return ipu6_recv_get_token(isys->fwctx, IPU6_BASE_MSG_RECV_QUEUES);
}
-void ipu6_fw_isys_put_resp(void *context, unsigned int queue)
+static void ipu6_fw_isys_put_resp(struct ipu6_isys *isys)
{
- ipu6_recv_put_token(context, queue);
+ ipu6_recv_put_token(isys->fwctx, IPU6_BASE_MSG_RECV_QUEUES);
}
-void ipu6_fw_isys_dump_stream_cfg(struct device *dev,
- struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
+static void ipu6_fw_isys_dump_stream_cfg(struct device *dev,
+ struct isys_fw_msgs *msg)
{
+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg = &msg->ipu6.stream;
unsigned int i;
dev_dbg(dev, "-----------------------------------------------------\n");
@@ -451,13 +444,15 @@ void ipu6_fw_isys_dump_stream_cfg(struct device *dev,
dev_dbg(dev, "-----------------------------------------------------\n");
}
-void
-ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
- struct ipu6_fw_isys_frame_buff_set_abi *buf,
- unsigned int outputs)
+static void
+ipu6_fw_isys_dump_frame_buf_set(struct device *dev, struct isys_fw_msgs *msg,
+ unsigned int outputs)
{
+ struct ipu6_fw_isys_frame_buff_set_abi *buf;
unsigned int i;
+ buf = &msg->ipu6.frame;
+
dev_dbg(dev, "-----------------------------------------------------\n");
dev_dbg(dev, "IPU6_FW_ISYS_FRAME_BUFF_SET\n");
@@ -485,3 +480,478 @@ ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
dev_dbg(dev, "-----------------------------------------------------\n");
}
+
+struct fwmsg {
+ int type;
+ char *msg;
+ bool valid_ts;
+};
+
+static const struct fwmsg fw_msg[] = {
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
+ "STREAM_START_AND_CAPTURE_ACK", 0},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0},
+ {IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
+ "STREAM_START_AND_CAPTURE_DONE", 1},
+ {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1},
+ {IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1},
+ {IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1},
+ {IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1},
+ {-1, "UNKNOWN MESSAGE", 0}
+};
+
+static u32 resp_type_to_index(int type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(fw_msg); i++)
+ if (fw_msg[i].type == type)
+ return i;
+
+ return ARRAY_SIZE(fw_msg) - 1;
+}
+
+int ipu6_isys_isr_one(struct ipu6_bus_device *adev)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ struct ipu6_fw_isys_resp_info_abi *resp;
+ struct ipu6_isys_stream *stream;
+ struct ipu6_isys_csi2 *csi2 = NULL;
+ struct isys_fw_msgs *isys_fw_msg = NULL;
+ u32 index;
+ u64 ts;
+
+ resp = ipu6_fw_isys_get_resp(isys);
+ if (!resp)
+ return 1;
+
+ ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0];
+
+ index = resp_type_to_index(resp->type);
+ dev_dbg(&adev->auxdev.dev,
+ "FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n",
+ resp->type, fw_msg[index].msg, resp->stream_handle,
+ fw_msg[index].valid_ts ? ts : 0, resp->pin_id);
+
+ if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION)
+ /* Suspension is kind of special case: not enough buffers */
+ dev_dbg(&adev->auxdev.dev,
+ "FW error resp SUSPENSION, details %d\n",
+ resp->error_info.error_details);
+ else if (resp->error_info.error)
+ dev_dbg(&adev->auxdev.dev,
+ "FW error resp error %d, details %d\n",
+ resp->error_info.error, resp->error_info.error_details);
+
+ guard(spinlock_irqsave)(&isys->streams_lock);
+
+ if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) {
+ dev_err(&adev->auxdev.dev, "bad stream handle %u\n",
+ resp->stream_handle);
+ goto leave;
+ }
+
+ stream = resp->stream_handle < IPU6_ISYS_MAX_STREAMS ?
+ isys->streams_by_handle[resp->stream_handle] : NULL;
+ if (!stream) {
+ dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n",
+ resp->stream_handle);
+ goto leave;
+ }
+ stream->error = resp->error_info.error;
+
+ csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
+
+ switch (resp->type) {
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE:
+ complete(&stream->stream_open_completion);
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK:
+ complete(&stream->stream_close_completion);
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK:
+ complete(&stream->stream_start_completion);
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
+ complete(&stream->stream_start_completion);
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK:
+ complete(&stream->stream_stop_completion);
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK:
+ complete(&stream->stream_stop_completion);
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY:
+ /*
+ * firmware only release the capture message until software
+ * get pin_data_ready event
+ */
+ if (!resp->buf_id) {
+ dev_warn(&adev->auxdev.dev, "%d: Invalid buf ID\n",
+ resp->stream_handle);
+ goto leave;
+ }
+
+ isys_fw_msg = container_of((void *)(uintptr_t)resp->buf_id,
+ struct isys_fw_msgs, dummy);
+
+ ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), isys_fw_msg);
+ if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS &&
+ stream->output_pins_queue[resp->pin_id])
+ ipu6_isys_queue_buf_ready(stream, resp);
+ else
+ dev_warn(&adev->auxdev.dev,
+ "%d:No queue for pin id %d\n",
+ resp->stream_handle, resp->pin_id);
+ if (csi2)
+ ipu6_isys_csi2_error(csi2);
+
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK:
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
+ case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE:
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF:
+
+ ipu6_isys_csi2_sof_event_by_stream(stream);
+ stream->seq[stream->seq_index].sequence =
+ atomic_read(&stream->sequence) - 1;
+ stream->seq[stream->seq_index].timestamp = ts;
+ dev_dbg(&adev->auxdev.dev,
+ "sof: handle %d: (index %u), timestamp 0x%16.16llx\n",
+ resp->stream_handle,
+ stream->seq[stream->seq_index].sequence, ts);
+ stream->seq_index = (stream->seq_index + 1)
+ % IPU6_ISYS_MAX_PARALLEL_SOF;
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF:
+ ipu6_isys_csi2_eof_event_by_stream(stream);
+ dev_dbg(&adev->auxdev.dev,
+ "eof: handle %d: (index %u), timestamp 0x%16.16llx\n",
+ resp->stream_handle,
+ stream->seq[stream->seq_index].sequence, ts);
+ break;
+ case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY:
+ break;
+ default:
+ dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n",
+ resp->stream_handle, resp->type);
+ break;
+ }
+
+leave:
+ ipu6_fw_isys_put_resp(isys);
+ return 0;
+}
+
+static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys_stream *stream;
+ unsigned int i;
+ u32 status;
+
+ ipu6_isys_register_errors(csi2);
+
+ status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
+
+ writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
+
+ scoped_guard(spinlock, &csi2->isys->streams_lock) {
+ for (i = 0; i < NR_OF_CSI2_VC; i++) {
+ if (status & IPU_CSI_RX_IRQ_FS_VC(i)) {
+ stream = csi2->streams_by_vc[i];
+ if (!stream)
+ continue;
+
+ ipu6_isys_csi2_sof_event_by_stream(stream);
+ }
+
+ if (status & IPU_CSI_RX_IRQ_FE_VC(i)) {
+ stream = csi2->streams_by_vc[i];
+ if (!stream)
+ continue;
+
+ ipu6_isys_csi2_eof_event_by_stream(stream);
+ }
+ }
+ }
+}
+
+irqreturn_t ipu6_isys_isr(struct ipu6_bus_device *adev)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ void __iomem *base = isys->pdata->base;
+ u32 status_sw, status_csi;
+ u32 ctrl0_status, ctrl0_clear;
+ int pm_status;
+
+ pm_status = pm_runtime_get_if_active(&adev->auxdev.dev);
+ if (!pm_status)
+ return 0;
+
+ ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status;
+ ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear;
+
+ status_csi = readl(isys->pdata->base + ctrl0_status);
+ status_sw = readl(isys->pdata->base +
+ IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
+
+ writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW,
+ base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
+
+ do {
+ writel(status_csi, isys->pdata->base + ctrl0_clear);
+
+ writel(status_sw, isys->pdata->base +
+ IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
+
+ if (isys->isr_csi2_bits & status_csi) {
+ unsigned int i;
+
+ for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
+ /* irq from not enabled port */
+ if (!isys->csi2[i].base)
+ continue;
+ if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i))
+ ipu6_isys_csi2_isr(&isys->csi2[i]);
+ }
+ }
+
+ writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
+
+ if (!ipu6_isys_isr_one(adev))
+ status_sw = IPU6_ISYS_UNISPART_IRQ_SW;
+ else
+ status_sw = 0;
+
+ status_csi = readl(isys->pdata->base + ctrl0_status);
+ status_sw |= readl(isys->pdata->base +
+ IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
+ } while ((status_csi & isys->isr_csi2_bits) ||
+ (status_sw & IPU6_ISYS_UNISPART_IRQ_SW));
+
+ writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
+
+ if (pm_status > 0)
+ pm_runtime_put(&adev->auxdev.dev);
+
+ return IRQ_HANDLED;
+}
+
+static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
+ struct ipu6_isys_stream *stream,
+ struct media_pad *src_pad,
+ struct v4l2_mbus_frame_desc_entry *entry,
+ void *__cfg)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd);
+ struct ipu6_fw_isys_stream_cfg_data_abi *cfg = __cfg;
+ struct ipu6_fw_isys_input_pin_info_abi *input_pin;
+ struct ipu6_fw_isys_output_pin_info_abi *output_pin;
+ struct ipu6_isys_queue *aq = &av->aq;
+ struct v4l2_mbus_framefmt *fmt;
+ const struct ipu6_isys_pixelformat *pfmt =
+ ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
+ struct v4l2_rect v4l2_crop;
+ struct ipu6_isys *isys = av->isys;
+ int input_pins = cfg->nof_input_pins++;
+ int output_pins;
+ u32 src_stream;
+
+ src_stream = __ipu6_isys_get_src_stream_by_src_pad(state, src_pad->index);
+ fmt = v4l2_subdev_state_get_format(state, src_pad->index, src_stream);
+ v4l2_crop = *v4l2_subdev_state_get_crop(state, src_pad->index, src_stream);
+
+ input_pin = &cfg->input_pins[input_pins];
+ input_pin->input_res.width = fmt->width;
+ input_pin->input_res.height = fmt->height;
+ input_pin->dt = entry->bus.csi2.dt;
+ input_pin->bits_per_pix = pfmt->bpp_packed;
+ input_pin->mapped_dt = 0x40; /* invalid mipi data type */
+ input_pin->mipi_decompression = 0;
+ input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR;
+ input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ?
+ IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER :
+ IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL;
+ input_pin->crop_first_and_last_lines = v4l2_crop.top & 1;
+
+ output_pins = cfg->nof_output_pins++;
+ aq->fw_output = output_pins;
+ stream->output_pins_queue[output_pins] = aq;
+
+ output_pin = &cfg->output_pins[output_pins];
+ output_pin->input_pin_id = input_pins;
+ output_pin->output_res.width = ipu6_isys_get_frame_width(av);
+ output_pin->output_res.height = ipu6_isys_get_frame_height(av);
+
+ output_pin->stride = ipu6_isys_get_bytes_per_line(av);
+ if (pfmt->bpp != pfmt->bpp_packed)
+ output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
+ else
+ output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI;
+ output_pin->ft = pfmt->css_pixelformat;
+ output_pin->send_irq = 1;
+ memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets));
+ output_pin->s2m_pixel_soc_pixel_remapping =
+ S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
+ output_pin->csi_be_soc_pixel_remapping =
+ CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
+
+ output_pin->snoopable = true;
+ output_pin->error_handling_enable = false;
+ output_pin->sensor_type = isys->sensor_type++;
+ if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end)
+ isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
+
+ return 0;
+}
+
+static int ipu6_fw_isys_prepare_stream_cfg(struct ipu6_isys_stream *stream,
+ struct v4l2_mbus_frame_desc *desc,
+ struct isys_fw_msgs *msg)
+{
+ struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg;
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ int ret;
+
+ stream_cfg = &msg->ipu6.stream;
+ stream_cfg->src = stream->asd->source;
+ stream_cfg->vc = stream->vc;
+ stream_cfg->isl_use = 0;
+ stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL;
+
+ ret = ipu6_isys_fw_pins_prepare(stream, desc, ipu6_isys_fw_pin_cfg,
+ stream_cfg);
+ if (ret)
+ return ret;
+
+ ipu6_fw_isys_dump_stream_cfg(dev, msg);
+
+ stream->nr_output_pins = stream_cfg->nof_output_pins;
+
+ return 0;
+}
+
+static int ipu6_fw_isys_stream_open(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg)
+{
+ return ipu6_fw_isys_complex_cmd(isys, stream_handle,
+ &msg->ipu6.stream, msg->dma_addr,
+ sizeof(msg->ipu6.stream),
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN);
+}
+
+static int ipu6_fw_isys_stream_close(struct ipu6_isys *isys,
+ const unsigned int stream_handle)
+{
+ return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
+}
+
+static int ipu6_fw_isys_stream_flush(struct ipu6_isys *isys,
+ const unsigned int stream_handle)
+{
+ return ipu6_fw_isys_complex_cmd(isys, stream_handle, NULL, 0, 0,
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH);
+}
+
+static int ipu6_fw_isys_stream_start(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg, bool capture)
+{
+ u16 cmd_type;
+
+ if (capture)
+ cmd_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
+ else
+ cmd_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START;
+
+ return ipu6_fw_isys_complex_cmd(isys, stream_handle,
+ &msg->ipu6.stream, msg->dma_addr,
+ sizeof(msg->ipu6.stream), cmd_type);
+}
+
+static int ipu6_fw_isys_stream_capture(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg)
+{
+ return ipu6_fw_isys_complex_cmd(isys, stream_handle,
+ &msg->ipu6.stream, msg->dma_addr,
+ sizeof(msg->ipu6.stream),
+ IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE);
+}
+
+static void
+ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
+ struct ipu6_fw_isys_frame_buff_set_abi *set)
+{
+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+ struct ipu6_isys_video_buffer *ivb =
+ vb2_buffer_to_ipu6_isys_video_buffer(vvb);
+
+ set->output_pins[aq->fw_output].addr = ivb->dma_addr;
+ set->output_pins[aq->fw_output].out_buf_id = vb->index + 1;
+}
+
+/*
+ * Convert a buffer list to a isys fw ABI framebuffer set. The
+ * buffer list is not modified.
+ */
+#define IPU6_ISYS_FRAME_NUM_THRESHOLD (30)
+static void ipu6_fw_isys_prepare_buf_set(struct isys_fw_msgs *msg,
+ struct ipu6_isys_stream *stream,
+ struct ipu6_isys_buffer_list *bl)
+{
+ struct ipu6_fw_isys_frame_buff_set_abi *set = &msg->ipu6.frame;
+ struct ipu6_isys_buffer *ib;
+
+ WARN_ON(!bl->nbufs);
+
+ set->send_irq_sof = 1;
+ set->send_resp_sof = 1;
+ set->send_irq_eof = 0;
+ set->send_resp_eof = 0;
+
+ set->send_irq_capture_ack = 1;
+ set->send_irq_capture_done = 0;
+
+ set->send_resp_capture_ack = 1;
+ set->send_resp_capture_done = 1;
+ if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) {
+ set->send_resp_capture_ack = 0;
+ set->send_resp_capture_done = 0;
+ }
+
+ list_for_each_entry(ib, &bl->head, head) {
+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+
+ ipu6_isys_buf_to_fw_frame_buf_pin(vb, set);
+ }
+}
+
+const struct ipu6_fw_isys_ops ipu6_fw_isys_ops = {
+ .init = ipu6_fw_isys_init,
+ .close = ipu6_fw_isys_close,
+ .cleanup = ipu6_fw_isys_cleanup,
+ .prepare_stream_cfg = ipu6_fw_isys_prepare_stream_cfg,
+ .prepare_buf_set = ipu6_fw_isys_prepare_buf_set,
+ .stream_open = ipu6_fw_isys_stream_open,
+ .stream_start = ipu6_fw_isys_stream_start,
+ .stream_capture = ipu6_fw_isys_stream_capture,
+ .stream_flush = ipu6_fw_isys_stream_flush,
+ .stream_close = ipu6_fw_isys_stream_close,
+ .dump_stream_cfg = ipu6_fw_isys_dump_stream_cfg,
+ .dump_frame_buf_set = ipu6_fw_isys_dump_frame_buf_set,
+};
diff --git a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
index b60f02076d8a..2a679d995961 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-fw-isys.h
@@ -8,6 +8,10 @@
struct device;
struct ipu6_isys;
+struct ipu6_isys_video;
+struct isys_fw_msgs;
+struct ipu6_isys_stream;
+struct ipu6_isys_buffer_list;
/* Max number of Input/Output Pins */
#define IPU6_MAX_IPINS 4
@@ -512,21 +516,6 @@ struct ipu6_fw_isys_proxy_resp_info_abi {
};
/**
- * struct ipu6_fw_proxy_write_queue_token - ISYS proxy write queue token
- * @request_id: update id for the specific proxy write request
- * @region_index: Region id for the proxy write request
- * @offset: Offset of the write request according to the base address
- * of the region
- * @value: Value that is requested to be written with the proxy write request
- */
-struct ipu6_fw_proxy_write_queue_token {
- u32 request_id;
- u32 region_index;
- u32 offset;
- u32 value;
-};
-
-/**
* struct ipu6_fw_resp_queue_token - ISYS response queue token
* @resp_info: response info
*/
@@ -570,27 +559,10 @@ struct ipu6_fw_proxy_send_queue_token {
u32 value;
};
-void
-ipu6_fw_isys_dump_stream_cfg(struct device *dev,
- struct ipu6_fw_isys_stream_cfg_data_abi *cfg);
-void
-ipu6_fw_isys_dump_frame_buff_set(struct device *dev,
- struct ipu6_fw_isys_frame_buff_set_abi *buf,
- unsigned int outputs);
-int ipu6_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams);
-int ipu6_fw_isys_close(struct ipu6_isys *isys);
-int ipu6_fw_isys_simple_cmd(struct ipu6_isys *isys,
- const unsigned int stream_handle, u16 send_type);
-int ipu6_fw_isys_complex_cmd(struct ipu6_isys *isys,
- const unsigned int stream_handle,
- void *cpu_mapped_buf, dma_addr_t dma_mapped_buf,
- size_t size, u16 send_type);
-int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys,
- unsigned int req_id,
- unsigned int index,
- unsigned int offset, u32 value);
-void ipu6_fw_isys_cleanup(struct ipu6_isys *isys);
-struct ipu6_fw_isys_resp_info_abi *
-ipu6_fw_isys_get_resp(void *context, unsigned int queue);
-void ipu6_fw_isys_put_resp(void *context, unsigned int queue);
+int ipu6_fw_isys_send_proxy_token(struct ipu6_isys *isys, unsigned int req_id,
+ unsigned int index, unsigned int offset,
+ u32 value);
+int ipu6_isys_isr_one(struct ipu6_bus_device *adev);
+irqreturn_t ipu6_isys_isr(struct ipu6_bus_device *adev);
+
#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
index 7e539a0c6c92..0db6971d8d3e 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.c
@@ -11,9 +11,12 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/minmax.h>
+#include <linux/pm_runtime.h>
#include <linux/sprintf.h>
+#include <linux/string_choices.h>
#include <media/media-entity.h>
+#include <media/mipi-csi2.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
@@ -23,7 +26,9 @@
#include "ipu6-isys.h"
#include "ipu6-isys-csi2.h"
#include "ipu6-isys-subdev.h"
+#include "ipu6-isys-video.h"
#include "ipu6-platform-isys-csi2-reg.h"
+#include "ipu7-isys-csi2-regs.h"
static const u32 csi2_supported_codes[] = {
MEDIA_BUS_FMT_RGB565_1X16,
@@ -228,56 +233,124 @@ void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
}
}
-static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
- const struct ipu6_isys_csi2_timing *timing,
- unsigned int nlanes, int enable)
+static void ipu6_isys_csi2_setup_watermark(struct ipu6_isys_csi2 *csi2,
+ struct v4l2_subdev_state *csi2_state,
+ struct v4l2_subdev *remote_sd)
+{
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 };
+ struct v4l2_subdev_route *route;
+ u32 max_stream_data_rate = 0, hblank = 0;
+ s64 link_freq;
+ int ret;
+
+ if (IS_IPU7(csi2->asd.isys->adev->isp))
+ return;
+
+ ret = v4l2_g_ctrl(remote_sd->ctrl_handler, &hb);
+ if (!ret)
+ hblank = max(0, hb.value);
+
+ link_freq = ipu6_isys_csi2_get_link_freq(csi2);
+ if (link_freq <= 0) {
+ csi2->watermark.force_iwake_disable = true;
+ dev_warn(dev, "unexpected link_freq %lld (source %s)\n",
+ link_freq, remote_sd->entity.name);
+ ipu6_isys_update_watermark_setting(csi2->isys);
+ return;
+ }
+
+ for_each_active_route(&csi2_state->routing, route) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_state_get_format(csi2_state, CSI2_PAD_SINK,
+ route->sink_stream);
+ if (WARN_ON(!fmt))
+ continue;
+
+ u32 bpp = ipu6_isys_mbus_code_to_bpp(fmt->code);
+ u64 pixel_rate = mul_u64_u32_div(link_freq, csi2->nlanes * 2,
+ bpp);
+ u32 pixels_per_line = fmt->width + hblank;
+ u64 line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC,
+ pixel_rate);
+ u32 bytes_per_line = fmt->width * bpp / 8;
+ u32 pages_per_line =
+ DIV_ROUND_UP(bytes_per_line,
+ csi2->isys->pdata->ipdata->sram_gran_size);
+ u32 pb_bytes_per_line =
+ pages_per_line << csi2->isys->pdata->ipdata->sram_gran_shift;
+ u64 stream_data_rate =
+ div64_u64(pb_bytes_per_line * 1000, line_time_ns);
+
+ dev_dbg(dev, "stream %u:%u -> %u:%u data rate %lld\n",
+ route->sink_pad, route->sink_stream, route->source_pad,
+ route->source_stream, stream_data_rate);
+
+ max_stream_data_rate = max(max_stream_data_rate,
+ stream_data_rate);
+ }
+
+ csi2->watermark.stream_data_rate = max_stream_data_rate;
+
+ ipu6_isys_update_watermark_setting(csi2->isys);
+}
+
+static void ipu6_isys_csi2_clear_watermark(struct ipu6_isys_csi2 *csi2)
+{
+ if (IS_IPU7(csi2->asd.isys->adev->isp))
+ return;
+
+ csi2->watermark.force_iwake_disable = false;
+ csi2->watermark.stream_data_rate = 0;
+ ipu6_isys_update_watermark_setting(csi2->isys);
+}
+
+static void ipu6_isys_csi2_stream_disable(struct ipu6_isys_csi2 *csi2)
{
- struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
- struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
struct ipu6_isys *isys = csi2->isys;
- struct device *dev = &isys->adev->auxdev.dev;
struct ipu6_isys_csi2_config cfg;
- unsigned int nports;
- int ret = 0;
- u32 mask = 0;
- u32 i;
-
- dev_dbg(dev, "stream %s CSI2-%u with %u lanes\n", enable ? "on" : "off",
- csi2->port, nlanes);
+ u32 mask = isys->pdata->ipdata->csi2.irq_mask;
cfg.port = csi2->port;
- cfg.nlanes = nlanes;
+ cfg.nlanes = csi2->nlanes;
- mask = isys->pdata->ipdata->csi2.irq_mask;
- nports = isys->pdata->ipdata->csi2.nports;
+ writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE);
+ writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE);
+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
+ writel(mask, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
+ writel(0, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
+ writel(0xffffffff, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
+ CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
- if (!enable) {
- writel(0, csi2->base + CSI_REG_CSI_FE_ENABLE);
- writel(0, csi2->base + CSI_REG_PPI2CSI_ENABLE);
-
- writel(0,
- csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
- CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
- writel(mask,
- csi2->base + CSI_PORT_REG_BASE_IRQ_CSI +
- CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
- writel(0,
- csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
- CSI_PORT_REG_BASE_IRQ_ENABLE_OFFSET);
- writel(0xffffffff,
- csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
- CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
-
- isys->phy_set_power(isys, &cfg, timing, false);
-
- writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
- (isys->pdata->ipdata->csi2.fw_access_port_ofs,
- csi2->port));
- writel(0, isys->pdata->base +
- CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port));
+ isys->phy_set_power(isys, &cfg, NULL, false);
+ writel(0, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
+ (isys->pdata->ipdata->csi2.fw_access_port_ofs, csi2->port));
+ writel(0, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(csi2->port));
+}
+
+static int ipu6_isys_csi2_stream_enable(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ struct ipu6_isys_csi2_timing timing = { };
+ struct ipu6_isys_csi2_config cfg;
+ unsigned int nports;
+ u32 mask;
+ int ret;
+
+ cfg.port = csi2->port;
+ cfg.nlanes = csi2->nlanes;
+
+ ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
+ if (ret)
return ret;
- }
+
+ mask = isys->pdata->ipdata->csi2.irq_mask;
+ nports = isys->pdata->ipdata->csi2.nports;
/* reset port reset */
writel(0x1, csi2->base + CSI_REG_PORT_GPREG_SRST);
@@ -285,7 +358,7 @@ static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
writel(0x0, csi2->base + CSI_REG_PORT_GPREG_SRST);
/* enable port clock */
- for (i = 0; i < nports; i++) {
+ for (unsigned int i = 0; i < nports; i++) {
writel(1, isys->pdata->base + CSI_REG_HUB_DRV_ACCESS_PORT(i));
writel(1, isys->pdata->base + CSI_REG_HUB_FW_ACCESS_PORT
(isys->pdata->ipdata->csi2.fw_access_port_ofs, i));
@@ -329,18 +402,217 @@ static int ipu6_isys_csi2_set_stream(struct v4l2_subdev *sd,
writel(CSI_SENSOR_INPUT, csi2->base + CSI_REG_CSI_FE_MUX_CTRL);
writel(CSI_CNTR_SENSOR_LINE_ID | CSI_CNTR_SENSOR_FRAME_ID,
csi2->base + CSI_REG_CSI_FE_SYNC_CNTR_SEL);
- writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK, nlanes - 1),
+ writel(FIELD_PREP(PPI_INTF_CONFIG_NOF_ENABLED_DLANES_MASK,
+ csi2->nlanes - 1),
csi2->base + CSI_REG_PPI2CSI_CONFIG_PPI_INTF);
writel(1, csi2->base + CSI_REG_PPI2CSI_ENABLE);
writel(1, csi2->base + CSI_REG_CSI_FE_ENABLE);
- ret = isys->phy_set_power(isys, &cfg, timing, true);
+ return isys->phy_set_power(isys, &cfg, &timing, true);
+}
+
+static void ipu7_csi2_irq_enable(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ unsigned int offset, mask;
+
+ /* enable CSI2 legacy error irq */
+ offset = IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR);
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_MASK);
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_ENABLE);
+
+ /* enable CSI2 legacy sync irq */
+ offset = IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR);
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_MASK);
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_ENABLE);
+
+ if (!IS_IPU7_MTL(isys->adev->isp)) {
+ mask = IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK;
+ writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_CLEAR);
+ writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_MASK);
+ writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_ENABLE);
+ }
+}
+
+static void ipu7_csi2_irq_disable(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ unsigned int offset, mask;
+
+ /* disable CSI2 legacy error irq */
+ offset = IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR);
+ writel(0, csi2->base + offset + IPU7_IRQ_CTL_MASK);
+ writel(0, csi2->base + offset + IPU7_IRQ_CTL_ENABLE);
+
+ /* disable CSI2 legacy sync irq */
+ offset = IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+ mask = IPU7_CSI_RX_SYNC_IRQ_MASK;
+ writel(mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR);
+ writel(0, csi2->base + offset + IPU7_IRQ_CTL_MASK);
+ writel(0, csi2->base + offset + IPU7_IRQ_CTL_ENABLE);
+
+ if (!IS_IPU7_MTL(isys->adev->isp)) {
+ writel(mask, csi2->base + offset + IPU7_IRQ1_CTL_CLEAR);
+ writel(0, csi2->base + offset + IPU7_IRQ1_CTL_MASK);
+ writel(0, csi2->base + offset + IPU7_IRQ1_CTL_ENABLE);
+ }
+}
+
+static void ipu7_isys_csi2_stream_disable(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ void __iomem *base = isys->pdata->base;
+ struct ipu6_isys_csi2_config cfg;
+
+ cfg.port = csi2->port;
+ cfg.nlanes = csi2->nlanes;
+
+ isys->phy_set_power(isys, &cfg, NULL, false);
+
+ writel(0x4,
+ base + IPU7_IS_IO_GPREGS_BASE + IPU7_CLK_DIV_FACTOR_APB_CLK);
+ ipu7_csi2_irq_disable(csi2);
+}
+
+static int ipu7_isys_csi2_stream_enable(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ void __iomem *base = isys->pdata->base;
+ struct ipu6_isys_csi2_config cfg;
+ unsigned int offset = IPU7_IS_IO_GPREGS_BASE;
+ int ret;
+
+ writel(0x2, base + offset + IPU7_CLK_DIV_FACTOR_APB_CLK);
+ if (csi2->port == 0U && csi2->nlanes == 4U &&
+ !IS_IPU7_MTL(isys->adev->isp))
+ writel(0x1, base + offset + IPU7_CSI_PORTAB_AGGREGATION);
+
+ /* input is coming from CSI receiver (sensor) */
+ offset = IPU7_IS_IO_CSI2_ADPL_PORT_BASE(csi2->port);
+ writel(CSI_SENSOR_INPUT, base + offset + IPU7_CSI2_ADPL_INPUT_MODE);
+ writel(1, base + offset + IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN);
+
+ cfg.port = csi2->port;
+ cfg.nlanes = csi2->nlanes;
+
+ ret = isys->phy_set_power(isys, &cfg, NULL, true);
if (ret)
- dev_err(dev, "csi-%d phy power up failed %d\n", csi2->port,
- ret);
+ return ret;
- return ret;
+ ipu7_csi2_irq_enable(csi2);
+
+ return 0;
+}
+
+static int ipu6_isys_csi2_streaming_change(struct ipu6_isys_subdev *asd,
+ struct v4l2_subdev_state *state,
+ u32 pad,
+ struct v4l2_mbus_frame_desc *desc,
+ u8 *vc, bool enable)
+{
+ struct v4l2_mbus_frame_desc_entry *this_entry = NULL;
+ struct v4l2_subdev_route *route, *this_route = NULL;
+ u32 streams_enabled = 0, nodes_streaming = 0;
+
+ for_each_active_route(&state->routing, this_route)
+ if (pad == this_route->source_pad)
+ break;
+ if (!this_route) {
+ dev_dbg(asd->sd.dev, "no route found for pad %u\n", pad);
+ return -EINVAL;
+ }
+
+ for (unsigned int i = 0; i < desc->num_entries; i++) {
+ if (desc->entry[i].stream == this_route->sink_stream) {
+ this_entry = &desc->entry[i];
+ break;
+ }
+ }
+ if (!this_entry) {
+ dev_dbg(asd->sd.dev,
+ "no frame descriptor entry found for stream %u\n",
+ this_route->sink_stream);
+ return -EINVAL;
+ }
+
+ for_each_active_route(&state->routing, route) {
+ struct v4l2_mbus_frame_desc_entry *entry = NULL;
+
+ for (unsigned int i = 0; i < desc->num_entries; i++) {
+ if (desc->entry[i].stream == route->sink_stream) {
+ entry = &desc->entry[i];
+ break;
+ }
+ }
+
+ if (!entry) {
+ dev_dbg(asd->sd.dev, "cannot find stream %u from frame descriptor\n",
+ route->sink_stream);
+ return -EINVAL;
+ }
+
+ if (entry->bus.csi2.vc != this_entry->bus.csi2.vc)
+ continue;
+
+ struct media_pad *video_pad =
+ media_pad_remote_pad_first(&asd->sd.entity.pads[route->source_pad]);
+ if (!video_pad)
+ return -EINVAL;
+
+ struct ipu6_isys_video *av =
+ container_of_const(video_pad, struct ipu6_isys_video,
+ pad);
+
+ streams_enabled++;
+ if (av->streaming || (enable && pad == route->source_pad))
+ nodes_streaming++;
+ }
+
+ if (vc)
+ *vc = this_entry->bus.csi2.vc;
+
+ if (streams_enabled == nodes_streaming) {
+ dev_dbg(asd->sd.dev,
+ "changing streaming state to %s on \"%s\"\n",
+ str_enabled_disabled(enable), asd->sd.entity.name);
+ return 1;
+ }
+
+ dev_dbg(asd->sd.dev, "not setting streaming %s %s (%u/%u)\n",
+ str_enabled_disabled(enable), enable ? "yet" : "anymore",
+ nodes_streaming, streams_enabled);
+
+ return 0;
+}
+
+static int ipu6_isys_get_frame_desc(struct v4l2_subdev *remote_sd,
+ unsigned int pad,
+ struct v4l2_mbus_frame_desc *desc)
+{
+ struct v4l2_subdev_format fmt = { .which = V4L2_SUBDEV_FORMAT_ACTIVE };
+ int ret;
+
+ ret = v4l2_subdev_call(remote_sd, pad, get_frame_desc, pad, desc);
+ if (!ret || ret != -ENOIOCTLCMD)
+ return ret;
+
+ ret = v4l2_subdev_call_state_active(remote_sd, pad, get_fmt, &fmt);
+ if (ret)
+ return ret;
+
+ desc->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+ desc->num_entries = 1;
+ desc->entry[0].pixelcode = fmt.format.code;
+ desc->entry[0].bus.csi2.dt = ipu6_isys_mbus_code_to_mipi(fmt.format.code);
+
+ return 0;
}
static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd,
@@ -349,60 +621,174 @@ static int ipu6_isys_csi2_enable_streams(struct v4l2_subdev *sd,
{
struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
- struct ipu6_isys_csi2_timing timing = { };
- struct v4l2_subdev *remote_sd;
- struct media_pad *remote_pad;
+ struct ipu6_device *isp = asd->isys->adev->isp;
+ struct media_pad *remote_pad =
+ media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]),
+ *vdev_pad = media_pad_remote_pad_unique(&sd->entity.pads[pad]);
+ struct ipu6_isys_video *av =
+ container_of_const(vdev_pad, struct ipu6_isys_video, pad);
+ struct v4l2_subdev *remote_sd =
+ media_entity_to_v4l2_subdev(remote_pad->entity);
+ struct v4l2_mbus_frame_desc desc = { 0 };
+ struct ipu6_isys_stream *stream;
+ struct ipu6_isys_buffer_list bl;
u64 sink_streams;
int ret;
+ u8 vc;
- remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
- remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+ lockdep_assert_held(&csi2->isys->stream_mutex);
+
+ ret = ipu6_isys_get_frame_desc(remote_sd, remote_pad->index, &desc);
+ if (ret)
+ return ret;
+
+ list_add(&av->csi2_entry, &csi2->av_head);
sink_streams =
v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
&streams_mask);
+ csi2->stream_ids |= sink_streams;
- ret = ipu6_isys_csi2_calc_timing(csi2, &timing, CSI2_ACCINV);
- if (ret)
+ ret = ipu6_isys_csi2_streaming_change(asd, state, pad, &desc, &vc,
+ true);
+ if (!ret)
return ret;
+ if (ret < 0)
+ goto err_av_del;
+
+ ret = pm_runtime_resume_and_get(sd->dev);
+ if (ret < 0)
+ goto err_del_av;
- ret = ipu6_isys_csi2_set_stream(sd, &timing, csi2->nlanes, true);
+ ipu6_isys_csi2_setup_watermark(csi2, state, remote_sd);
+
+ stream = ipu6_isys_alloc_stream_firmware(csi2, state, &desc, vc);
+ if (IS_ERR(stream)) {
+ ret = PTR_ERR(stream);
+ dev_err(sd->dev, "allocating firmware stream failed\n");
+ goto err_clear_watermark;
+ }
+
+ ret = ipu6_isys_buffer_list_get(stream, &bl);
+ if (ret < 0) {
+ dev_warn(sd->dev, "no buffer available, DRIVER BUG?\n");
+ goto err_free_stream_firmware;
+ }
+
+ ret = ipu6_isys_start_stream_firmware(stream, &bl, &desc);
if (ret)
- return ret;
+ goto err_requeue_buffers;
- ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index,
- sink_streams);
- if (ret) {
- ipu6_isys_csi2_set_stream(sd, NULL, 0, false);
- return ret;
+ if (!csi2->streaming_vc) {
+ ret = IS_IPU7(isp) ? ipu7_isys_csi2_stream_enable(csi2) :
+ ipu6_isys_csi2_stream_enable(csi2);
+ if (ret)
+ goto err_stop_stream_firmware;
}
+ ret = v4l2_subdev_enable_streams(remote_sd, remote_pad->index,
+ csi2->stream_ids);
+ if (ret)
+ goto err_stop_stream_csi2;
+
+ csi2->streaming_vc |= BIT(vc);
+
return 0;
+
+err_stop_stream_csi2:
+ if (IS_IPU7(isp))
+ ipu7_isys_csi2_stream_disable(csi2);
+ else
+ ipu6_isys_csi2_stream_disable(csi2);
+
+err_requeue_buffers:
+ ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_INCOMING, 0);
+
+err_stop_stream_firmware:
+ ipu6_isys_stop_stream_firmware(stream);
+ ipu6_isys_close_stream_firmware(stream);
+
+err_free_stream_firmware:
+ ipu6_isys_free_stream_firmware(stream);
+
+err_clear_watermark:
+ ipu6_isys_csi2_clear_watermark(csi2);
+ pm_runtime_put(sd->dev);
+
+err_del_av:
+ ipu6_isys_csi2_streaming_change(asd, state, pad, &desc, NULL, false);
+ csi2->stream_ids &= ~sink_streams;
+err_av_del:
+ list_del(&av->csi2_entry);
+
+ return ret;
}
static int ipu6_isys_csi2_disable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
u32 pad, u64 streams_mask)
{
- struct v4l2_subdev *remote_sd;
- struct media_pad *remote_pad;
+ struct media_pad *remote_pad =
+ media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]),
+ *vdev_pad = media_pad_remote_pad_unique(&sd->entity.pads[pad]);
+ struct ipu6_isys_video *av =
+ container_of_const(vdev_pad, struct ipu6_isys_video, pad);
+ struct v4l2_subdev *remote_sd =
+ media_entity_to_v4l2_subdev(remote_pad->entity);
+ struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(sd);
+ struct ipu6_isys_csi2 *csi2 = to_ipu6_isys_csi2(asd);
+ struct ipu6_device *isp = asd->isys->adev->isp;
+ struct v4l2_mbus_frame_desc desc = { 0 };
u64 sink_streams;
+ int ret;
+ u8 vc;
+
+ lockdep_assert_held(&csi2->isys->stream_mutex);
+
+ ret = ipu6_isys_get_frame_desc(remote_sd, remote_pad->index, &desc);
+ if (ret)
+ return ret;
sink_streams =
v4l2_subdev_state_xlate_streams(state, pad, CSI2_PAD_SINK,
&streams_mask);
- remote_pad = media_pad_remote_pad_first(&sd->entity.pads[CSI2_PAD_SINK]);
- remote_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
+ csi2->stream_ids &= ~sink_streams;
+
+ ret = ipu6_isys_csi2_streaming_change(asd, state, pad, &desc, &vc,
+ false);
+ if (ret <= 0)
+ goto out_del_csi2_entry;
+
+ csi2->streaming_vc &= ~BIT(vc);
+
+ struct ipu6_isys_stream *stream =
+ ipu6_isys_find_stream_firmware(csi2, vc);
+ ipu6_isys_stop_stream_firmware(stream);
- ipu6_isys_csi2_set_stream(sd, NULL, 0, false);
+ if IS_IPU7(isp)
+ ipu7_isys_csi2_stream_disable(csi2);
+ else
+ ipu6_isys_csi2_stream_disable(csi2);
- v4l2_subdev_disable_streams(remote_sd, remote_pad->index, sink_streams);
+ v4l2_subdev_disable_streams(remote_sd, remote_pad->index,
+ csi2->stream_ids | sink_streams);
+
+ ipu6_isys_close_stream_firmware(stream);
+ ipu6_isys_free_stream_firmware(stream);
+
+ ipu6_isys_csi2_clear_watermark(csi2);
+
+ pm_runtime_put(sd->dev);
+
+out_del_csi2_entry:
+ list_del(&av->csi2_entry);
return 0;
}
static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -454,6 +840,7 @@ static int ipu6_isys_csi2_set_sel(struct v4l2_subdev *sd,
}
static int ipu6_isys_csi2_get_sel(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -542,6 +929,8 @@ int ipu6_isys_csi2_init(struct ipu6_isys_csi2 *csi2,
if (ret)
goto fail;
+ INIT_LIST_HEAD(&csi2->av_head);
+ INIT_LIST_HEAD(&csi2->streams);
csi2->asd.source = IPU6_FW_ISYS_STREAM_SRC_CSI2_PORT0 + index;
csi2->asd.supported_codes = csi2_supported_codes;
snprintf(csi2->asd.sd.name, sizeof(csi2->asd.sd.name),
@@ -592,56 +981,3 @@ void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream)
dev_dbg(dev, "eof_event::csi2-%i sequence: %i\n",
csi2->port, frame_sequence);
}
-
-int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
- struct ipu6_isys_csi2 *csi2,
- struct media_entity *source_entity,
- struct v4l2_mbus_frame_desc_entry *entry)
-{
- struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
- struct device *dev = &csi2->isys->adev->auxdev.dev;
- struct v4l2_mbus_frame_desc desc;
- struct v4l2_subdev *source;
- struct media_pad *pad;
- unsigned int i;
- int ret;
-
- source = media_entity_to_v4l2_subdev(source_entity);
- if (!source)
- return -EPIPE;
-
- pad = media_pad_remote_pad_first(&csi2->asd.pad[CSI2_PAD_SINK]);
- if (!pad)
- return -EPIPE;
-
- ret = v4l2_subdev_call(source, pad, get_frame_desc, pad->index, &desc);
- if (ret)
- return ret;
-
- if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
- dev_err(dev, "Unsupported frame descriptor type\n");
- return -EINVAL;
- }
-
- for (i = 0; i < desc.num_entries; i++) {
- if (source_stream == desc.entry[i].stream) {
- desc_entry = &desc.entry[i];
- break;
- }
- }
-
- if (!desc_entry) {
- dev_err(dev, "Failed to find stream %u from remote subdev\n",
- source_stream);
- return -EINVAL;
- }
-
- if (desc_entry->bus.csi2.vc >= NR_OF_CSI2_VC) {
- dev_err(dev, "invalid vc %d\n", desc_entry->bus.csi2.vc);
- return -EINVAL;
- }
-
- *entry = *desc_entry;
-
- return 0;
-}
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
index ce8eed91065c..1620ac16f90d 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-csi2.h
@@ -16,6 +16,9 @@ struct ipu6_isys_video;
struct ipu6_isys;
struct ipu6_isys_stream;
+#define PHY_MODE_DPHY 0
+#define PHY_MODE_CPHY 1
+
#define NR_OF_CSI2_VC 16
#define INVALID_VC_ID -1
#define NR_OF_CSI2_SINK_PADS 1
@@ -38,11 +41,22 @@ struct ipu6_isys_csi2 {
struct ipu6_isys_subdev asd;
struct ipu6_isys *isys;
struct ipu6_isys_video av[NR_OF_CSI2_SRC_PADS];
+ struct list_head av_head;
+ struct list_head streams;
+ struct ipu6_isys_stream *streams_by_vc[NR_OF_CSI2_VC];
void __iomem *base;
u32 receiver_errors;
unsigned int nlanes;
unsigned int port;
+ u32 legacy_irq_mask;
+ unsigned int phy_mode;
+ struct {
+ u32 stream_data_rate;
+ bool force_iwake_disable;
+ } watermark;
+ u32 streaming_vc;
+ u64 stream_ids;
};
struct ipu6_isys_csi2_timing {
@@ -70,9 +84,5 @@ void ipu6_isys_csi2_sof_event_by_stream(struct ipu6_isys_stream *stream);
void ipu6_isys_csi2_eof_event_by_stream(struct ipu6_isys_stream *stream);
void ipu6_isys_register_errors(struct ipu6_isys_csi2 *csi2);
void ipu6_isys_csi2_error(struct ipu6_isys_csi2 *csi2);
-int ipu6_isys_csi2_get_remote_desc(u32 source_stream,
- struct ipu6_isys_csi2 *csi2,
- struct media_entity *source_entity,
- struct v4l2_mbus_frame_desc_entry *entry);
#endif /* IPU6_ISYS_CSI2_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
index fabaed63df0c..2608e3b91acb 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.c
@@ -21,6 +21,7 @@
#include "ipu6-dma.h"
#include "ipu6-fw-isys.h"
#include "ipu6-isys.h"
+#include "ipu6-isys-queue.h"
#include "ipu6-isys-video.h"
static int ipu6_isys_buf_init(struct vb2_buffer *vb)
@@ -191,8 +192,8 @@ static void flush_firmware_streamon_fail(struct ipu6_isys_stream *stream)
* that contains one entry from each video buffer queue. If a buffer can't be
* obtained from every queue, the buffers are returned back to the queue.
*/
-static int buffer_list_get(struct ipu6_isys_stream *stream,
- struct ipu6_isys_buffer_list *bl)
+int ipu6_isys_buffer_list_get(struct ipu6_isys_stream *stream,
+ struct ipu6_isys_buffer_list *bl)
{
struct device *dev = &stream->isys->adev->auxdev.dev;
struct ipu6_isys_queue *aq;
@@ -233,106 +234,53 @@ static int buffer_list_get(struct ipu6_isys_stream *stream,
return 0;
}
-static void
-ipu6_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
- struct ipu6_fw_isys_frame_buff_set_abi *set)
-{
- struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
- struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
- struct ipu6_isys_video_buffer *ivb =
- vb2_buffer_to_ipu6_isys_video_buffer(vvb);
-
- set->output_pins[aq->fw_output].addr = ivb->dma_addr;
- set->output_pins[aq->fw_output].out_buf_id = vb->index + 1;
-}
-
-/*
- * Convert a buffer list to a isys fw ABI framebuffer set. The
- * buffer list is not modified.
- */
-#define IPU6_ISYS_FRAME_NUM_THRESHOLD (30)
-void
-ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
- struct ipu6_isys_stream *stream,
- struct ipu6_isys_buffer_list *bl)
-{
- struct ipu6_isys_buffer *ib;
-
- WARN_ON(!bl->nbufs);
-
- set->send_irq_sof = 1;
- set->send_resp_sof = 1;
- set->send_irq_eof = 0;
- set->send_resp_eof = 0;
-
- if (stream->streaming)
- set->send_irq_capture_ack = 0;
- else
- set->send_irq_capture_ack = 1;
- set->send_irq_capture_done = 0;
-
- set->send_resp_capture_ack = 1;
- set->send_resp_capture_done = 1;
- if (atomic_read(&stream->sequence) >= IPU6_ISYS_FRAME_NUM_THRESHOLD) {
- set->send_resp_capture_ack = 0;
- set->send_resp_capture_done = 0;
- }
-
- list_for_each_entry(ib, &bl->head, head) {
- struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
-
- ipu6_isys_buf_to_fw_frame_buf_pin(vb, set);
- }
-}
-
/* Start streaming for real. The buffer list must be available. */
-static int ipu6_isys_stream_start(struct ipu6_isys_video *av,
- struct ipu6_isys_buffer_list *bl)
+static int ipu6_isys_stream_start(struct ipu6_isys_video *av)
{
- struct ipu6_isys_stream *stream = av->stream;
- struct device *dev = &stream->isys->adev->auxdev.dev;
- struct ipu6_isys_buffer_list __bl;
+ struct ipu6_bus_device *adev = av->isys->adev;
+ const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu6_isys_buffer_list bl;
+ struct isys_fw_msgs *msg;
int ret;
- guard(mutex)(&stream->isys->stream_mutex);
- ret = ipu6_isys_video_set_streaming(av, 1, bl);
+ guard(mutex)(&av->isys->stream_mutex);
+ ret = ipu6_isys_video_set_streaming(av, 1);
if (ret)
- goto out_requeue;
+ return ret;
- stream->streaming = 1;
+ if (!av->stream || !(BIT(av->stream->vc) & av->csi2->streaming_vc))
+ return 0;
+
+ struct ipu6_isys_stream *stream = av->stream;
- bl = &__bl;
+ guard(mutex)(&stream->mutex);
do {
- struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
- struct isys_fw_msgs *msg;
- u16 send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE;
-
- ret = buffer_list_get(stream, bl);
+ ret = ipu6_isys_buffer_list_get(stream, &bl);
if (ret < 0)
- break;
+ return 0;
msg = ipu6_get_fw_msg_buf(stream);
- if (!msg)
- return -ENOMEM;
-
- buf = &msg->fw_msg.frame;
- ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
- ipu6_fw_isys_dump_frame_buff_set(dev, buf,
- stream->nr_output_pins);
- ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE,
- 0);
- ret = ipu6_fw_isys_complex_cmd(stream->isys,
- stream->stream_handle, buf,
- msg->dma_addr, sizeof(*buf),
- send_type);
- } while (!WARN_ON(ret));
+ if (WARN_ON(!msg))
+ goto out_requeue;
+
+ fw_ops->prepare_buf_set(msg, stream, &bl);
+ fw_ops->dump_frame_buf_set(dev, msg, stream->nr_output_pins);
+ ipu6_isys_buffer_list_queue(&bl,
+ IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
+ ret = fw_ops->stream_capture(stream->isys,
+ stream->stream_handle, msg);
+ if (WARN_ON(ret))
+ break;
+ } while (true);
- return 0;
+ /* Error handling begins here. */
+ ipu6_put_fw_msg_buf(stream->isys, msg);
out_requeue:
- if (bl && bl->nbufs)
- ipu6_isys_buffer_list_queue(bl,
+ if (bl.nbufs)
+ ipu6_isys_buffer_list_queue(&bl,
IPU6_ISYS_BUFFER_LIST_FL_INCOMING,
VB2_BUF_STATE_QUEUED);
flush_firmware_streamon_fail(stream);
@@ -344,13 +292,15 @@ static void buf_queue(struct vb2_buffer *vb)
{
struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
+ struct ipu6_bus_device *adev = av->isys->adev;
+ const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops;
struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
struct ipu6_isys_video_buffer *ivb =
vb2_buffer_to_ipu6_isys_video_buffer(vvb);
struct ipu6_isys_buffer *ib = &ivb->ib;
- struct device *dev = &av->isys->adev->auxdev.dev;
- struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
+ struct device *dev = &adev->auxdev.dev;
struct ipu6_isys_stream *stream = av->stream;
+ struct ipu6_isys_csi2 *csi2;
struct ipu6_isys_buffer_list bl;
struct isys_fw_msgs *msg;
unsigned long flags;
@@ -374,7 +324,8 @@ static void buf_queue(struct vb2_buffer *vb)
mutex_lock(&stream->mutex);
- if (stream->nr_streaming != stream->nr_queues) {
+ csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
+ if (!(BIT(stream->vc) & csi2->streaming_vc)) {
dev_dbg(dev, "not streaming yet, adding to incoming\n");
goto out;
}
@@ -384,7 +335,7 @@ static void buf_queue(struct vb2_buffer *vb)
* (above). Let's see whether all queues in the pipeline would
* have a buffer.
*/
- ret = buffer_list_get(stream, &bl);
+ ret = ipu6_isys_buffer_list_get(stream, &bl);
if (ret < 0) {
dev_dbg(dev, "No buffers available\n");
goto out;
@@ -396,9 +347,8 @@ static void buf_queue(struct vb2_buffer *vb)
goto out;
}
- buf = &msg->fw_msg.frame;
- ipu6_isys_buf_to_fw_frame_buf(buf, stream, &bl);
- ipu6_fw_isys_dump_frame_buff_set(dev, buf, stream->nr_output_pins);
+ fw_ops->prepare_buf_set(msg, stream, &bl);
+ fw_ops->dump_frame_buf_set(dev, msg, stream->nr_output_pins);
/*
* We must queue the buffers in the buffer list to the
@@ -408,9 +358,7 @@ static void buf_queue(struct vb2_buffer *vb)
*/
ipu6_isys_buffer_list_queue(&bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
- ret = ipu6_fw_isys_complex_cmd(stream->isys, stream->stream_handle,
- buf, msg->dma_addr, sizeof(*buf),
- IPU6_FW_ISYS_SEND_TYPE_STREAM_CAPTURE);
+ ret = fw_ops->stream_capture(stream->isys, stream->stream_handle, msg);
if (ret < 0)
dev_err(dev, "send stream capture failed\n");
@@ -525,7 +473,6 @@ static void return_buffers(struct ipu6_isys_queue *aq,
static void ipu6_isys_stream_cleanup(struct ipu6_isys_video *av)
{
video_device_pipeline_stop(&av->vdev);
- ipu6_isys_put_stream(av->stream);
av->stream = NULL;
}
@@ -536,10 +483,8 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
struct device *dev = &av->isys->adev->auxdev.dev;
const struct ipu6_isys_pixelformat *pfmt =
ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
- struct ipu6_isys_buffer_list __bl, *bl = NULL;
- struct ipu6_isys_stream *stream;
struct media_pad *source_pad, *remote_pad;
- int nr_queues, ret;
+ int ret;
dev_dbg(dev, "stream: %s: width %u, height %u, css pixelformat %u\n",
av->vdev.name, ipu6_isys_get_frame_width(av),
@@ -559,11 +504,9 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
goto out_return_buffers;
}
- ret = ipu6_isys_setup_video(av, remote_pad, source_pad, &nr_queues);
- if (ret < 0) {
- dev_dbg(dev, "failed to setup video\n");
+ ret = video_device_pipeline_alloc_start(&av->vdev);
+ if (ret < 0)
goto out_return_buffers;
- }
ret = ipu6_isys_link_fmt_validate(aq);
if (ret) {
@@ -573,55 +516,12 @@ static int start_streaming(struct vb2_queue *q, unsigned int count)
goto out_pipeline_stop;
}
- ret = ipu6_isys_fw_open(av->isys);
+ ret = ipu6_isys_stream_start(av);
if (ret)
goto out_pipeline_stop;
- stream = av->stream;
- mutex_lock(&stream->mutex);
- if (!stream->nr_streaming) {
- ret = ipu6_isys_video_prepare_stream(av, source_pad->entity,
- nr_queues);
- if (ret)
- goto out_fw_close;
- }
-
- stream->nr_streaming++;
- dev_dbg(dev, "queue %u of %u\n", stream->nr_streaming,
- stream->nr_queues);
-
- list_add(&aq->node, &stream->queues);
- ipu6_isys_configure_stream_watermark(av, source_pad->entity);
- ipu6_isys_update_stream_watermark(av, true);
-
- if (stream->nr_streaming != stream->nr_queues)
- goto out;
-
- bl = &__bl;
- ret = buffer_list_get(stream, bl);
- if (ret < 0) {
- dev_warn(dev, "no buffer available, DRIVER BUG?\n");
- goto out;
- }
-
- ret = ipu6_isys_stream_start(av, bl);
- if (ret)
- goto out_stream_start;
-
-out:
- mutex_unlock(&stream->mutex);
-
return 0;
-out_stream_start:
- ipu6_isys_update_stream_watermark(av, false);
- list_del(&aq->node);
- stream->nr_streaming--;
-
-out_fw_close:
- mutex_unlock(&stream->mutex);
- ipu6_isys_fw_close(av->isys);
-
out_pipeline_stop:
ipu6_isys_stream_cleanup(av);
@@ -635,27 +535,14 @@ static void stop_streaming(struct vb2_queue *q)
{
struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(q);
struct ipu6_isys_video *av = ipu6_isys_queue_to_video(aq);
- struct ipu6_isys_stream *stream = av->stream;
-
- mutex_lock(&stream->mutex);
-
- ipu6_isys_update_stream_watermark(av, false);
mutex_lock(&av->isys->stream_mutex);
- if (stream->nr_streaming == stream->nr_queues && stream->streaming)
- ipu6_isys_video_set_streaming(av, 0, NULL);
- list_del(&aq->node);
+ ipu6_isys_video_set_streaming(av, 0);
mutex_unlock(&av->isys->stream_mutex);
- stream->nr_streaming--;
- stream->streaming = 0;
- mutex_unlock(&stream->mutex);
-
ipu6_isys_stream_cleanup(av);
return_buffers(aq, VB2_BUF_STATE_ERROR);
-
- ipu6_isys_fw_close(av->isys);
}
static unsigned int
@@ -741,7 +628,7 @@ static void ipu6_isys_queue_buf_done(struct ipu6_isys_buffer *ib)
}
}
-static void
+void
ipu6_stream_buf_ready(struct ipu6_isys_stream *stream, u8 pin_id, u32 pin_addr,
u64 time, bool error_check)
{
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
index dec1fed44dd2..0e0886f59150 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-queue.h
@@ -60,11 +60,11 @@ struct ipu6_isys_buffer_list {
void ipu6_isys_buffer_list_queue(struct ipu6_isys_buffer_list *bl,
unsigned long op_flags,
enum vb2_buffer_state state);
-void
-ipu6_isys_buf_to_fw_frame_buf(struct ipu6_fw_isys_frame_buff_set_abi *set,
- struct ipu6_isys_stream *stream,
+int ipu6_isys_buffer_list_get(struct ipu6_isys_stream *stream,
struct ipu6_isys_buffer_list *bl);
void ipu6_isys_queue_buf_ready(struct ipu6_isys_stream *stream,
struct ipu6_fw_isys_resp_info_abi *info);
int ipu6_isys_queue_init(struct ipu6_isys_queue *aq);
+void ipu6_stream_buf_ready(struct ipu6_isys_stream *stream, u8 pin_id,
+ u32 pin_addr, u64 time, bool error_check);
#endif /* IPU6_ISYS_QUEUE_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
index dbd6f76a066d..60070842ee68 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.c
@@ -160,6 +160,7 @@ u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y)
}
int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -265,17 +266,13 @@ static int subdev_set_routing(struct v4l2_subdev *sd,
return v4l2_subdev_set_routing_with_fmt(sd, state, routing, &format);
}
-u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
+u32 __ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev_state *state,
+ u32 pad)
{
- struct v4l2_subdev_state *state;
struct v4l2_subdev_route *routes;
unsigned int i;
u32 source_stream = 0;
- state = v4l2_subdev_lock_and_get_active_state(sd);
- if (!state)
- return 0;
-
routes = state->routing.routes;
for (i = 0; i < state->routing.num_routes; i++) {
if (routes[i].source_pad == pad) {
@@ -284,6 +281,20 @@ u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
}
}
+ return source_stream;
+}
+
+u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad)
+{
+ struct v4l2_subdev_state *state;
+ u32 source_stream = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+ if (!state)
+ return 0;
+
+ source_stream = __ipu6_isys_get_src_stream_by_src_pad(state, pad);
+
v4l2_subdev_unlock_state(state);
return source_stream;
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
index 35069099c364..b892d96992af 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-subdev.h
@@ -31,12 +31,15 @@ bool ipu6_isys_is_bayer_format(u32 code);
u32 ipu6_isys_convert_bayer_order(u32 code, int x, int y);
int ipu6_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt);
int ipu6_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_mbus_code_enum
*code);
+u32 __ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev_state *state,
+ u32 pad);
u32 ipu6_isys_get_src_stream_by_src_pad(struct v4l2_subdev *sd, u32 pad);
int ipu6_isys_subdev_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
index 3ac48d2076da..129016e57446 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.c
@@ -432,185 +432,110 @@ unlock:
return ret;
}
-static void get_stream_opened(struct ipu6_isys_video *av)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&av->isys->streams_lock, flags);
- av->isys->stream_opened++;
- spin_unlock_irqrestore(&av->isys->streams_lock, flags);
-}
+int ipu6_isys_fw_pins_prepare(struct ipu6_isys_stream *stream,
+ struct v4l2_mbus_frame_desc *desc,
+ int (*fw_pin_cfg)(struct ipu6_isys_video *av,
+ struct ipu6_isys_stream *stream,
+ struct media_pad *src_pad,
+ struct v4l2_mbus_frame_desc_entry *entry,
+ void *__cfg), void *stream_cfg)
+{
+ struct v4l2_subdev_state *csi2_state =
+ v4l2_subdev_get_locked_active_state(&stream->asd->sd);
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ struct ipu6_isys_queue *aq;
-static void put_stream_opened(struct ipu6_isys_video *av)
-{
- unsigned long flags;
+ list_for_each_entry(aq, &stream->queues, node) {
+ struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq);
+ struct media_pad *remote_pad =
+ media_pad_remote_pad_first(&__av->pad);
+ u64 source_streams = 1;
+ unsigned int sink_stream =
+ __ffs(v4l2_subdev_state_xlate_streams(csi2_state,
+ remote_pad->index,
+ CSI2_PAD_SINK,
+ &source_streams));
+ struct v4l2_mbus_frame_desc_entry *entry = NULL;
+ int ret;
+
+ for (unsigned int i = 0; i < desc->num_entries; i++) {
+ if (desc->entry[i].stream == sink_stream) {
+ entry = &desc->entry[i];
+ break;
+ }
+ }
- spin_lock_irqsave(&av->isys->streams_lock, flags);
- av->isys->stream_opened--;
- spin_unlock_irqrestore(&av->isys->streams_lock, flags);
-}
+ if (!entry) {
+ dev_err(dev, "cannot find frame desc entry for sink stream %u\n",
+ sink_stream);
+ return -EINVAL;
+ }
-static int ipu6_isys_fw_pin_cfg(struct ipu6_isys_video *av,
- struct ipu6_fw_isys_stream_cfg_data_abi *cfg)
-{
- struct media_pad *src_pad = media_pad_remote_pad_first(&av->pad);
- struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
- struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd);
- struct ipu6_fw_isys_input_pin_info_abi *input_pin;
- struct ipu6_fw_isys_output_pin_info_abi *output_pin;
- struct ipu6_isys_stream *stream = av->stream;
- struct ipu6_isys_queue *aq = &av->aq;
- struct v4l2_mbus_framefmt fmt;
- const struct ipu6_isys_pixelformat *pfmt =
- ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
- struct v4l2_rect v4l2_crop;
- struct ipu6_isys *isys = av->isys;
- int input_pins = cfg->nof_input_pins++;
- int output_pins;
- u32 src_stream;
-
- src_stream = ipu6_isys_get_src_stream_by_src_pad(sd, src_pad->index);
- fmt = *v4l2_subdev_state_get_format(state, src_pad->index, src_stream);
- v4l2_crop = *v4l2_subdev_state_get_crop(state, src_pad->index, src_stream);
-
- input_pin = &cfg->input_pins[input_pins];
- input_pin->input_res.width = fmt.width;
- input_pin->input_res.height = fmt.height;
- input_pin->dt = av->dt;
- input_pin->bits_per_pix = pfmt->bpp_packed;
- input_pin->mapped_dt = 0x40; /* invalid mipi data type */
- input_pin->mipi_decompression = 0;
- input_pin->capture_mode = IPU6_FW_ISYS_CAPTURE_MODE_REGULAR;
- input_pin->mipi_store_mode = pfmt->bpp == pfmt->bpp_packed ?
- IPU6_FW_ISYS_MIPI_STORE_MODE_DISCARD_LONG_HEADER :
- IPU6_FW_ISYS_MIPI_STORE_MODE_NORMAL;
- input_pin->crop_first_and_last_lines = v4l2_crop.top & 1;
-
- output_pins = cfg->nof_output_pins++;
- aq->fw_output = output_pins;
- stream->output_pins_queue[output_pins] = aq;
-
- output_pin = &cfg->output_pins[output_pins];
- output_pin->input_pin_id = input_pins;
- output_pin->output_res.width = ipu6_isys_get_frame_width(av);
- output_pin->output_res.height = ipu6_isys_get_frame_height(av);
-
- output_pin->stride = ipu6_isys_get_bytes_per_line(av);
- if (pfmt->bpp != pfmt->bpp_packed)
- output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_RAW_SOC;
- else
- output_pin->pt = IPU6_FW_ISYS_PIN_TYPE_MIPI;
- output_pin->ft = pfmt->css_pixelformat;
- output_pin->send_irq = 1;
- memset(output_pin->ts_offsets, 0, sizeof(output_pin->ts_offsets));
- output_pin->s2m_pixel_soc_pixel_remapping =
- S2M_PIXEL_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
- output_pin->csi_be_soc_pixel_remapping =
- CSI_BE_SOC_PIXEL_REMAPPING_FLAG_NO_REMAPPING;
-
- output_pin->snoopable = true;
- output_pin->error_handling_enable = false;
- output_pin->sensor_type = isys->sensor_type++;
- if (isys->sensor_type > isys->pdata->ipdata->sensor_type_end)
- isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
+ ret = fw_pin_cfg(__av, stream, remote_pad, entry, stream_cfg);
+ if (ret < 0)
+ return ret;
+ }
return 0;
}
-static int start_stream_firmware(struct ipu6_isys_video *av,
- struct ipu6_isys_buffer_list *bl)
+int ipu6_isys_start_stream_firmware(struct ipu6_isys_stream *stream,
+ struct ipu6_isys_buffer_list *bl,
+ struct v4l2_mbus_frame_desc *desc)
{
- struct ipu6_fw_isys_stream_cfg_data_abi *stream_cfg;
- struct ipu6_fw_isys_frame_buff_set_abi *buf = NULL;
- struct ipu6_isys_stream *stream = av->stream;
- struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu6_bus_device *adev = stream->asd->isys->adev;
+ const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops;
+ struct device *dev = &adev->auxdev.dev;
struct isys_fw_msgs *msg = NULL;
- struct ipu6_isys_queue *aq;
int ret, retout, tout;
- u16 send_type;
+ bool capture = bl ? true : false;
msg = ipu6_get_fw_msg_buf(stream);
if (!msg)
return -ENOMEM;
- stream_cfg = &msg->fw_msg.stream;
- stream_cfg->src = stream->stream_source;
- stream_cfg->vc = stream->vc;
- stream_cfg->isl_use = 0;
- stream_cfg->sensor_type = IPU6_FW_ISYS_SENSOR_MODE_NORMAL;
-
- list_for_each_entry(aq, &stream->queues, node) {
- struct ipu6_isys_video *__av = ipu6_isys_queue_to_video(aq);
-
- ret = ipu6_isys_fw_pin_cfg(__av, stream_cfg);
- if (ret < 0) {
- ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
- return ret;
- }
+ ret = fw_ops->prepare_stream_cfg(stream, desc, msg);
+ if (ret < 0) {
+ ipu6_put_fw_msg_buf(stream->isys, msg);
+ return ret;
}
- ipu6_fw_isys_dump_stream_cfg(dev, stream_cfg);
-
- stream->nr_output_pins = stream_cfg->nof_output_pins;
-
reinit_completion(&stream->stream_open_completion);
- ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
- stream_cfg, msg->dma_addr,
- sizeof(*stream_cfg),
- IPU6_FW_ISYS_SEND_TYPE_STREAM_OPEN);
+ ret = fw_ops->stream_open(stream->isys, stream->stream_handle, msg);
if (ret < 0) {
dev_err(dev, "can't open stream (%d)\n", ret);
- ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+ ipu6_put_fw_msg_buf(stream->isys, msg);
return ret;
}
- get_stream_opened(av);
-
tout = wait_for_completion_timeout(&stream->stream_open_completion,
IPU6_FW_CALL_TIMEOUT_JIFFIES);
- ipu6_put_fw_msg_buf(av->isys, (uintptr_t)stream_cfg);
+ ipu6_put_fw_msg_buf(stream->isys, msg);
if (!tout) {
dev_err(dev, "stream open time out\n");
- ret = -ETIMEDOUT;
- goto out_put_stream_opened;
+ return -ETIMEDOUT;
}
if (stream->error) {
dev_err(dev, "stream open error: %d\n", stream->error);
- ret = -EIO;
- goto out_put_stream_opened;
+ return -EIO;
}
dev_dbg(dev, "start stream: open complete\n");
- if (bl) {
- msg = ipu6_get_fw_msg_buf(stream);
- if (!msg) {
- ret = -ENOMEM;
- goto out_put_stream_opened;
- }
- buf = &msg->fw_msg.frame;
- ipu6_isys_buf_to_fw_frame_buf(buf, stream, bl);
- ipu6_isys_buffer_list_queue(bl,
- IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
+ msg = ipu6_get_fw_msg_buf(stream);
+ if (!msg) {
+ return -ENOMEM;
}
- reinit_completion(&stream->stream_start_completion);
+ fw_ops->prepare_buf_set(msg, stream, bl);
+ ipu6_isys_buffer_list_queue(bl, IPU6_ISYS_BUFFER_LIST_FL_ACTIVE, 0);
- if (bl) {
- send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START_AND_CAPTURE;
- ipu6_fw_isys_dump_frame_buff_set(dev, buf,
- stream_cfg->nof_output_pins);
- ret = ipu6_fw_isys_complex_cmd(av->isys, stream->stream_handle,
- buf, msg->dma_addr,
- sizeof(*buf), send_type);
- } else {
- send_type = IPU6_FW_ISYS_SEND_TYPE_STREAM_START;
- ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
- send_type);
- }
+ reinit_completion(&stream->stream_start_completion);
+ ret = fw_ops->stream_start(stream->isys, stream->stream_handle, msg,
+ capture);
if (ret < 0) {
dev_err(dev, "can't start streaming (%d)\n", ret);
goto out_stream_close;
@@ -635,12 +560,10 @@ static int start_stream_firmware(struct ipu6_isys_video *av,
out_stream_close:
reinit_completion(&stream->stream_close_completion);
- retout = ipu6_fw_isys_simple_cmd(av->isys,
- stream->stream_handle,
- IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
+ retout = fw_ops->stream_close(stream->isys, stream->stream_handle);
if (retout < 0) {
dev_dbg(dev, "can't close stream (%d)\n", retout);
- goto out_put_stream_opened;
+ return retout;
}
tout = wait_for_completion_timeout(&stream->stream_close_completion,
@@ -652,23 +575,19 @@ out_stream_close:
else
dev_dbg(dev, "stream close complete\n");
-out_put_stream_opened:
- put_stream_opened(av);
-
return ret;
}
-static void stop_streaming_firmware(struct ipu6_isys_video *av)
+void ipu6_isys_stop_stream_firmware(struct ipu6_isys_stream *stream)
{
- struct device *dev = &av->isys->adev->auxdev.dev;
- struct ipu6_isys_stream *stream = av->stream;
+ struct ipu6_bus_device *adev = stream->asd->isys->adev;
+ const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops;
+ struct device *dev = &adev->auxdev.dev;
int ret, tout;
reinit_completion(&stream->stream_stop_completion);
- ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
- IPU6_FW_ISYS_SEND_TYPE_STREAM_FLUSH);
-
+ ret = fw_ops->stream_flush(stream->isys, stream->stream_handle);
if (ret < 0) {
dev_err(dev, "can't stop stream (%d)\n", ret);
return;
@@ -684,16 +603,17 @@ static void stop_streaming_firmware(struct ipu6_isys_video *av)
dev_dbg(dev, "stop stream: complete\n");
}
-static void close_streaming_firmware(struct ipu6_isys_video *av)
+void ipu6_isys_close_stream_firmware(struct ipu6_isys_stream *stream)
{
- struct ipu6_isys_stream *stream = av->stream;
- struct device *dev = &av->isys->adev->auxdev.dev;
+ struct ipu6_bus_device *adev = stream->asd->isys->adev;
+ const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
int ret, tout;
reinit_completion(&stream->stream_close_completion);
- ret = ipu6_fw_isys_simple_cmd(av->isys, stream->stream_handle,
- IPU6_FW_ISYS_SEND_TYPE_STREAM_CLOSE);
+ ret = fw_ops->stream_close(stream->isys, stream->stream_handle);
if (ret < 0) {
dev_err(dev, "can't close stream (%d)\n", ret);
return;
@@ -708,349 +628,153 @@ static void close_streaming_firmware(struct ipu6_isys_video *av)
else
dev_dbg(dev, "close stream: complete\n");
- put_stream_opened(av);
+ scoped_guard(spinlock_irqsave, &stream->isys->streams_lock) {
+ stream->isys->streams_by_handle[stream->stream_handle] = NULL;
+ csi2->streams_by_vc[stream->vc] = NULL;
+ }
}
-int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
- struct media_entity *source_entity,
- int nr_queues)
+struct ipu6_isys_stream *
+ipu6_isys_find_stream_firmware(struct ipu6_isys_csi2 *csi2, u8 vc)
{
- struct ipu6_isys_stream *stream = av->stream;
- struct ipu6_isys_csi2 *csi2;
+ struct ipu6_isys_stream *stream;
- if (WARN_ON(stream->nr_streaming))
- return -EINVAL;
+ list_for_each_entry(stream, &csi2->streams, csi2_entry)
+ if (stream->vc == vc)
+ return stream;
- stream->nr_queues = nr_queues;
- atomic_set(&stream->sequence, 0);
-
- stream->seq_index = 0;
- memset(stream->seq, 0, sizeof(stream->seq));
-
- if (WARN_ON(!list_empty(&stream->queues)))
- return -EINVAL;
-
- stream->stream_source = stream->asd->source;
- csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
- csi2->receiver_errors = 0;
-
- dev_dbg(&av->isys->adev->auxdev.dev,
- "prepare stream: external entity %s\n",
- source_entity->name);
-
- return 0;
+ return NULL;
}
-void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
- struct media_entity *source)
+void ipu6_isys_free_stream_firmware(struct ipu6_isys_stream *stream)
{
- struct ipu6_isys *isys = av->isys;
- struct ipu6_isys_csi2 *csi2 = NULL;
- struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
- struct device *dev = &isys->adev->auxdev.dev;
- struct v4l2_mbus_framefmt format;
- struct v4l2_subdev *esd;
- struct v4l2_control hb = { .id = V4L2_CID_HBLANK, .value = 0 };
- unsigned int bpp, lanes;
- s64 link_freq = 0;
- u64 pixel_rate = 0;
- int ret;
-
- esd = media_entity_to_v4l2_subdev(source);
-
- av->watermark.width = ipu6_isys_get_frame_width(av);
- av->watermark.height = ipu6_isys_get_frame_height(av);
- av->watermark.sram_gran_shift = isys->pdata->ipdata->sram_gran_shift;
- av->watermark.sram_gran_size = isys->pdata->ipdata->sram_gran_size;
-
- ret = v4l2_g_ctrl(esd->ctrl_handler, &hb);
- if (!ret && hb.value >= 0)
- av->watermark.hblank = hb.value;
- else
- av->watermark.hblank = 0;
-
- csi2 = ipu6_isys_subdev_to_csi2(av->stream->asd);
- link_freq = ipu6_isys_csi2_get_link_freq(csi2);
- if (link_freq > 0) {
- struct v4l2_subdev_state *state =
- v4l2_subdev_lock_and_get_active_state(&csi2->asd.sd);
-
- lanes = csi2->nlanes;
- format = *v4l2_subdev_state_get_format(state, 0,
- av->source_stream);
- bpp = ipu6_isys_mbus_code_to_bpp(format.code);
- pixel_rate = mul_u64_u32_div(link_freq, lanes * 2, bpp);
+ struct ipu6_isys_csi2 *csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
+ struct ipu6_isys_queue *aq, *aq_safe;
- v4l2_subdev_unlock_state(state);
- }
-
- av->watermark.pixel_rate = pixel_rate;
+ list_for_each_entry_safe(aq, aq_safe, &stream->queues, node) {
+ struct ipu6_isys_video *av =
+ container_of_const(aq, struct ipu6_isys_video, aq);
- if (!pixel_rate) {
- mutex_lock(&iwake_watermark->mutex);
- iwake_watermark->force_iwake_disable = true;
- mutex_unlock(&iwake_watermark->mutex);
- dev_warn(dev, "unexpected pixel_rate from %s, disable iwake.\n",
- source->name);
+ list_del(&aq->node);
+ av->stream = NULL;
}
-}
-static void calculate_stream_datarate(struct ipu6_isys_video *av)
-{
- struct video_stream_watermark *watermark = &av->watermark;
- const struct ipu6_isys_pixelformat *pfmt =
- ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
- u32 pages_per_line, pb_bytes_per_line, pixels_per_line, bytes_per_line;
- u64 line_time_ns, stream_data_rate;
- u16 shift, size;
-
- shift = watermark->sram_gran_shift;
- size = watermark->sram_gran_size;
-
- pixels_per_line = watermark->width + watermark->hblank;
- line_time_ns = div_u64(pixels_per_line * NSEC_PER_SEC,
- watermark->pixel_rate);
- bytes_per_line = watermark->width * pfmt->bpp / 8;
- pages_per_line = DIV_ROUND_UP(bytes_per_line, size);
- pb_bytes_per_line = pages_per_line << shift;
- stream_data_rate = div64_u64(pb_bytes_per_line * 1000, line_time_ns);
-
- watermark->stream_data_rate = stream_data_rate;
+ list_del(&stream->csi2_entry);
+ ida_free(&csi2->isys->streams, stream->stream_handle);
+ kfree(stream);
}
-void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state)
-{
- struct isys_iwake_watermark *iwake_watermark =
- &av->isys->iwake_watermark;
-
- if (!av->watermark.pixel_rate)
- return;
+struct ipu6_isys_stream *
+ipu6_isys_alloc_stream_firmware(struct ipu6_isys_csi2 *csi2,
+ struct v4l2_subdev_state *csi2_state,
+ struct v4l2_mbus_frame_desc *desc,
+ u8 vc)
+{
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct ipu6_isys_stream *stream;
+ struct v4l2_subdev_route *route;
+ int ret;
- if (state) {
- calculate_stream_datarate(av);
- mutex_lock(&iwake_watermark->mutex);
- list_add(&av->watermark.stream_node,
- &iwake_watermark->video_list);
- mutex_unlock(&iwake_watermark->mutex);
- } else {
- av->watermark.stream_data_rate = 0;
- mutex_lock(&iwake_watermark->mutex);
- list_del(&av->watermark.stream_node);
- mutex_unlock(&iwake_watermark->mutex);
- }
+ stream = kzalloc_obj(*stream);
+ if (!stream)
+ return ERR_PTR(-ENOMEM);
- update_watermark_setting(av->isys);
-}
+ ret = ida_alloc_max(&csi2->isys->streams, IPU6_ISYS_MAX_STREAMS - 1,
+ GFP_KERNEL);
+ if (ret < 0)
+ goto err_free_stream;
-void ipu6_isys_put_stream(struct ipu6_isys_stream *stream)
-{
- struct device *dev;
- unsigned int i;
- unsigned long flags;
+ stream->stream_handle = ret;
+ mutex_init(&stream->mutex);
+ init_completion(&stream->stream_open_completion);
+ init_completion(&stream->stream_close_completion);
+ init_completion(&stream->stream_start_completion);
+ init_completion(&stream->stream_stop_completion);
+ INIT_LIST_HEAD(&stream->queues);
+ stream->isys = csi2->asd.isys;
+ stream->asd = &csi2->asd;
+ stream->vc = vc;
- if (!stream) {
- pr_err("ipu6-isys: no available stream\n");
- return;
+ scoped_guard(spinlock_irqsave, &stream->isys->streams_lock) {
+ stream->isys->streams_by_handle[stream->stream_handle] = stream;
+ csi2->streams_by_vc[stream->vc] = stream;
}
- dev = &stream->isys->adev->auxdev.dev;
-
- spin_lock_irqsave(&stream->isys->streams_lock, flags);
- for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
- if (&stream->isys->streams[i] == stream) {
- if (stream->isys->streams_ref_count[i] > 0)
- stream->isys->streams_ref_count[i]--;
- else
- dev_warn(dev, "invalid stream %d\n", i);
-
- break;
- }
- }
- spin_unlock_irqrestore(&stream->isys->streams_lock, flags);
-}
+ list_add(&stream->csi2_entry, &csi2->streams);
-static struct ipu6_isys_stream *
-ipu6_isys_get_stream(struct ipu6_isys_video *av, struct ipu6_isys_subdev *asd)
-{
- struct ipu6_isys_stream *stream = NULL;
- struct ipu6_isys *isys = av->isys;
- unsigned long flags;
- unsigned int i;
- u8 vc = av->vc;
+ for_each_active_route(&csi2_state->routing, route) {
+ struct media_pad *vdev_pad =
+ media_pad_remote_pad_first(&csi2->asd.pad[route->source_pad]);
+ struct v4l2_mbus_frame_desc_entry *entry = NULL;
- if (!isys)
- return NULL;
+ for (unsigned int i = 0; i < desc->num_entries; i++) {
+ if (desc->entry[i].stream != route->sink_stream)
+ continue;
- spin_lock_irqsave(&isys->streams_lock, flags);
- for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
- if (isys->streams_ref_count[i] && isys->streams[i].vc == vc &&
- isys->streams[i].asd == asd) {
- isys->streams_ref_count[i]++;
- stream = &isys->streams[i];
+ entry = &desc->entry[i];
break;
}
- }
- if (!stream) {
- for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
- if (!isys->streams_ref_count[i]) {
- isys->streams_ref_count[i]++;
- stream = &isys->streams[i];
- stream->vc = vc;
- stream->asd = asd;
- break;
- }
+ if (!entry) {
+ dev_dbg(dev, "cannot find stream %u in frame desc\n",
+ route->sink_stream);
+ ret = -EINVAL;
+ goto err_ida_free;
}
- }
- spin_unlock_irqrestore(&isys->streams_lock, flags);
-
- return stream;
-}
-struct ipu6_isys_stream *
-ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle)
-{
- unsigned long flags;
- struct ipu6_isys_stream *stream = NULL;
-
- if (!isys)
- return NULL;
-
- if (stream_handle >= IPU6_ISYS_MAX_STREAMS) {
- dev_err(&isys->adev->auxdev.dev,
- "stream_handle %d is invalid\n", stream_handle);
- return NULL;
- }
-
- spin_lock_irqsave(&isys->streams_lock, flags);
- if (isys->streams_ref_count[stream_handle] > 0) {
- isys->streams_ref_count[stream_handle]++;
- stream = &isys->streams[stream_handle];
- }
- spin_unlock_irqrestore(&isys->streams_lock, flags);
-
- return stream;
-}
-
-struct ipu6_isys_stream *
-ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc)
-{
- struct ipu6_isys_stream *stream = NULL;
- unsigned long flags;
- unsigned int i;
-
- if (!isys)
- return NULL;
+ if (entry->bus.csi2.vc != vc)
+ continue;
- if (source < 0) {
- dev_err(&isys->adev->auxdev.dev,
- "query stream with invalid port number\n");
- return NULL;
- }
+ struct ipu6_isys_video *av =
+ container_of_const(vdev_pad, struct ipu6_isys_video,
+ pad);
- spin_lock_irqsave(&isys->streams_lock, flags);
- for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
- if (!isys->streams_ref_count[i])
- continue;
+ list_add(&av->aq.node, &stream->queues);
- if (isys->streams[i].stream_source == source &&
- isys->streams[i].vc == vc) {
- stream = &isys->streams[i];
- isys->streams_ref_count[i]++;
- break;
- }
+ stream->nr_output_pins++;
+ av->stream = stream;
}
- spin_unlock_irqrestore(&isys->streams_lock, flags);
return stream;
-}
-
-static u64 get_stream_mask_by_pipeline(struct ipu6_isys_video *__av)
-{
- struct media_pipeline *pipeline =
- media_entity_pipeline(&__av->vdev.entity);
- unsigned int i;
- u64 stream_mask = 0;
- for (i = 0; i < NR_OF_CSI2_SRC_PADS; i++) {
- struct ipu6_isys_video *av = &__av->csi2->av[i];
+err_ida_free:
+ list_del(&stream->csi2_entry);
+ ida_free(&csi2->isys->streams, stream->stream_handle);
- if (pipeline == media_entity_pipeline(&av->vdev.entity))
- stream_mask |= BIT_ULL(av->source_stream);
- }
+err_free_stream:
+ kfree(stream);
- return stream_mask;
+ return ERR_PTR(ret);
}
-int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
- struct ipu6_isys_buffer_list *bl)
+int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state)
{
- struct v4l2_subdev_krouting *routing;
- struct ipu6_isys_stream *stream = av->stream;
- struct v4l2_subdev_state *subdev_state;
struct device *dev = &av->isys->adev->auxdev.dev;
struct v4l2_subdev *sd;
struct media_pad *r_pad;
- u32 sink_pad, sink_stream;
- u64 r_stream;
- u64 stream_mask = 0;
int ret = 0;
- dev_dbg(dev, "set stream: %d\n", state);
-
- sd = &stream->asd->sd;
+ sd = &av->csi2->asd.sd;
r_pad = media_pad_remote_pad_first(&av->pad);
- r_stream = ipu6_isys_get_src_stream_by_src_pad(sd, r_pad->index);
-
- subdev_state = v4l2_subdev_lock_and_get_active_state(sd);
- routing = &subdev_state->routing;
- ret = v4l2_subdev_routing_find_opposite_end(routing, r_pad->index,
- r_stream, &sink_pad,
- &sink_stream);
- v4l2_subdev_unlock_state(subdev_state);
- if (ret)
- return ret;
- stream_mask = get_stream_mask_by_pipeline(av);
if (!state) {
- stop_streaming_firmware(av);
-
/* stop sub-device which connects with video */
- dev_dbg(dev, "stream off entity %s pad:%d mask:0x%llx\n",
- sd->name, r_pad->index, stream_mask);
- ret = v4l2_subdev_disable_streams(sd, r_pad->index,
- stream_mask);
+ dev_dbg(dev, "stream off %s pad:%d\n", sd->name, r_pad->index);
+ ret = v4l2_subdev_disable_streams(sd, r_pad->index, 1);
if (ret)
dev_err(dev, "stream off %s failed with %d\n", sd->name,
ret);
-
- close_streaming_firmware(av);
} else {
- ret = start_stream_firmware(av, bl);
- if (ret) {
- dev_err(dev, "start stream of firmware failed\n");
- return ret;
- }
-
/* start sub-device which connects with video */
- dev_dbg(dev, "stream on %s pad %d mask 0x%llx\n", sd->name,
- r_pad->index, stream_mask);
- ret = v4l2_subdev_enable_streams(sd, r_pad->index, stream_mask);
- if (ret) {
+ dev_dbg(dev, "stream on %s pad %d\n", sd->name, r_pad->index);
+ ret = v4l2_subdev_enable_streams(sd, r_pad->index, 1);
+ if (ret)
dev_err(dev, "stream on %s failed with %d\n", sd->name,
ret);
- goto out_media_entity_stop_streaming_firmware;
- }
}
av->streaming = state;
- return 0;
-
-out_media_entity_stop_streaming_firmware:
- stop_streaming_firmware(av);
- close_streaming_firmware(av);
-
return ret;
}
@@ -1089,148 +813,6 @@ static const struct v4l2_file_operations isys_fops = {
.release = vb2_fop_release,
};
-int ipu6_isys_fw_open(struct ipu6_isys *isys)
-{
- struct ipu6_bus_device *adev = isys->adev;
- const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata;
- int ret;
-
- ret = pm_runtime_resume_and_get(&adev->auxdev.dev);
- if (ret < 0)
- return ret;
-
- mutex_lock(&isys->mutex);
-
- if (isys->ref_count++)
- goto unlock;
-
- ipu6_configure_spc(adev->isp, &ipdata->hw_variant,
- IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base,
- adev->pkg_dir, adev->pkg_dir_dma_addr);
-
- /*
- * Buffers could have been left to wrong queue at last closure.
- * Move them now back to empty buffer queue.
- */
- ipu6_cleanup_fw_msg_bufs(isys);
-
- if (isys->fwcom) {
- /*
- * Something went wrong in previous shutdown. As we are now
- * restarting isys we can safely delete old context.
- */
- dev_warn(&adev->auxdev.dev, "clearing old context\n");
- ipu6_fw_isys_cleanup(isys);
- }
-
- ret = ipu6_fw_isys_init(isys, ipdata->num_parallel_streams);
- if (ret < 0)
- goto out;
-
-unlock:
- mutex_unlock(&isys->mutex);
-
- return 0;
-
-out:
- isys->ref_count--;
- mutex_unlock(&isys->mutex);
- pm_runtime_put(&adev->auxdev.dev);
-
- return ret;
-}
-
-void ipu6_isys_fw_close(struct ipu6_isys *isys)
-{
- mutex_lock(&isys->mutex);
-
- isys->ref_count--;
- if (!isys->ref_count) {
- ipu6_fw_isys_close(isys);
- if (isys->fwcom) {
- isys->need_reset = true;
- dev_warn(&isys->adev->auxdev.dev,
- "failed to close fw isys\n");
- }
- }
-
- mutex_unlock(&isys->mutex);
-
- if (isys->need_reset)
- pm_runtime_put_sync(&isys->adev->auxdev.dev);
- else
- pm_runtime_put(&isys->adev->auxdev.dev);
-}
-
-int ipu6_isys_setup_video(struct ipu6_isys_video *av,
- struct media_pad *remote_pad,
- struct media_pad *source_pad, int *nr_queues)
-{
- const struct ipu6_isys_pixelformat *pfmt =
- ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
- struct device *dev = &av->isys->adev->auxdev.dev;
- struct v4l2_mbus_frame_desc_entry entry;
- struct v4l2_subdev_route *route = NULL;
- struct v4l2_subdev_route *r;
- struct v4l2_subdev_state *state;
- struct v4l2_subdev *remote_sd =
- media_entity_to_v4l2_subdev(remote_pad->entity);
- struct ipu6_isys_subdev *asd = to_ipu6_isys_subdev(remote_sd);
- int ret = -EINVAL;
-
- *nr_queues = 0;
-
- /* Find the root */
- state = v4l2_subdev_lock_and_get_active_state(remote_sd);
- for_each_active_route(&state->routing, r) {
- (*nr_queues)++;
-
- if (r->source_pad == remote_pad->index)
- route = r;
- }
-
- if (!route) {
- v4l2_subdev_unlock_state(state);
- dev_dbg(dev, "Failed to find route\n");
- return -ENODEV;
- }
- av->source_stream = route->sink_stream;
- v4l2_subdev_unlock_state(state);
-
- ret = ipu6_isys_csi2_get_remote_desc(av->source_stream,
- to_ipu6_isys_csi2(asd),
- source_pad->entity, &entry);
- if (ret == -ENOIOCTLCMD) {
- av->vc = 0;
- av->dt = ipu6_isys_mbus_code_to_mipi(pfmt->code);
- } else if (!ret) {
- dev_dbg(dev, "Framedesc: stream %u, len %u, vc %u, dt %#x\n",
- entry.stream, entry.length, entry.bus.csi2.vc,
- entry.bus.csi2.dt);
-
- av->vc = entry.bus.csi2.vc;
- av->dt = entry.bus.csi2.dt;
- } else {
- dev_err(dev, "failed to get remote frame desc\n");
- return ret;
- }
-
- ret = video_device_pipeline_alloc_start(&av->vdev);
- if (ret < 0) {
- dev_dbg(dev, "media pipeline start failed\n");
- return ret;
- }
-
- av->stream = ipu6_isys_get_stream(av, asd);
- if (!av->stream) {
- video_device_pipeline_stop(&av->vdev);
- dev_err(dev, "no available stream for firmware\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
/*
* Do everything that's needed to initialise things related to video
* buffer queue, video node, and the related media entity. The caller
@@ -1262,7 +844,7 @@ int ipu6_isys_video_init(struct ipu6_isys_video *av)
ret = ipu6_isys_queue_init(&av->aq);
if (ret)
- goto out_free_watermark;
+ goto out_mutex_destroy;
av->pad.flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
ret = media_entity_pads_init(&av->vdev.entity, 1, &av->pad);
@@ -1299,7 +881,7 @@ out_media_entity_cleanup:
out_vb2_queue_release:
vb2_queue_release(&av->aq.vbq);
-out_free_watermark:
+out_mutex_destroy:
mutex_destroy(&av->mutex);
return ret;
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
index 2ff53315d7b9..2821b9b1b943 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys-video.h
@@ -22,6 +22,7 @@ struct file;
struct ipu6_isys;
struct ipu6_isys_csi2;
struct ipu6_isys_subdev;
+struct v4l2_mbus_frame_desc;
struct ipu6_isys_pixelformat {
u32 pixelformat;
@@ -44,17 +45,15 @@ struct sequence_info {
struct ipu6_isys_stream {
struct mutex mutex;
atomic_t sequence;
+ atomic_t buf_id;
unsigned int seq_index;
struct sequence_info seq[IPU6_ISYS_MAX_PARALLEL_SOF];
- int stream_source;
int stream_handle;
unsigned int nr_output_pins;
struct ipu6_isys_subdev *asd;
-
- int nr_queues; /* Number of capture queues */
- int nr_streaming;
- int streaming; /* Has streaming been really started? */
struct list_head queues;
+ struct list_head csi2_entry;
+
struct completion stream_open_completion;
struct completion stream_close_completion;
struct completion stream_start_completion;
@@ -66,20 +65,9 @@ struct ipu6_isys_stream {
u8 vc;
};
-struct video_stream_watermark {
- u32 width;
- u32 height;
- u32 hblank;
- u32 frame_rate;
- u64 pixel_rate;
- u64 stream_data_rate;
- u16 sram_gran_shift;
- u16 sram_gran_size;
- struct list_head stream_node;
-};
-
struct ipu6_isys_video {
struct ipu6_isys_queue aq;
+ struct list_head csi2_entry;
/* Serialise access to other fields in the struct. */
struct mutex mutex;
struct media_pad pad;
@@ -90,10 +78,7 @@ struct ipu6_isys_video {
struct ipu6_isys_csi2 *csi2;
struct ipu6_isys_stream *stream;
unsigned int streaming;
- struct video_stream_watermark watermark;
u32 source_stream;
- u8 vc;
- u8 dt;
};
#define ipu6_isys_queue_to_video(__aq) \
@@ -104,27 +89,34 @@ extern const struct ipu6_isys_pixelformat ipu6_isys_pfmts_packed[];
const struct ipu6_isys_pixelformat *
ipu6_isys_get_isys_format(u32 pixelformat, u32 code);
-int ipu6_isys_video_prepare_stream(struct ipu6_isys_video *av,
- struct media_entity *source_entity,
- int nr_queues);
-int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state,
- struct ipu6_isys_buffer_list *bl);
+int ipu6_isys_fw_pins_prepare(struct ipu6_isys_stream *stream,
+ struct v4l2_mbus_frame_desc *desc,
+ int (*fw_pin_cfg)(struct ipu6_isys_video *av,
+ struct ipu6_isys_stream *stream,
+ struct media_pad *src_pad,
+ struct v4l2_mbus_frame_desc_entry *entry,
+ void *__cfg), void *stream_cfg);
+int ipu6_isys_start_stream_firmware(struct ipu6_isys_stream *stream,
+ struct ipu6_isys_buffer_list *bl,
+ struct v4l2_mbus_frame_desc *desc);
+void ipu6_isys_stop_stream_firmware(struct ipu6_isys_stream *stream);
+void ipu6_isys_close_stream_firmware(struct ipu6_isys_stream *stream);
+struct ipu6_isys_stream *
+ipu6_isys_find_stream_firmware(struct ipu6_isys_csi2 *csi2, u8 vc);
+void ipu6_isys_free_stream_firmware(struct ipu6_isys_stream *stream);
+struct ipu6_isys_stream *
+ipu6_isys_alloc_stream_firmware(struct ipu6_isys_csi2 *csi2,
+ struct v4l2_subdev_state *state,
+ struct v4l2_mbus_frame_desc *desc,
+ u8 vc);
+int ipu6_isys_video_set_streaming(struct ipu6_isys_video *av, int state);
int ipu6_isys_fw_open(struct ipu6_isys *isys);
void ipu6_isys_fw_close(struct ipu6_isys *isys);
int ipu6_isys_setup_video(struct ipu6_isys_video *av,
struct media_pad *remote_pad,
- struct media_pad *source_pad, int *nr_queues);
+ struct media_pad *source_pad);
int ipu6_isys_video_init(struct ipu6_isys_video *av);
void ipu6_isys_video_cleanup(struct ipu6_isys_video *av);
-void ipu6_isys_put_stream(struct ipu6_isys_stream *stream);
-struct ipu6_isys_stream *
-ipu6_isys_query_stream_by_handle(struct ipu6_isys *isys, u8 stream_handle);
-struct ipu6_isys_stream *
-ipu6_isys_query_stream_by_source(struct ipu6_isys *isys, int source, u8 vc);
-
-void ipu6_isys_configure_stream_watermark(struct ipu6_isys_video *av,
- struct media_entity *source);
-void ipu6_isys_update_stream_watermark(struct ipu6_isys_video *av, bool state);
u32 ipu6_isys_get_format(struct ipu6_isys_video *av);
u32 ipu6_isys_get_data_size(struct ipu6_isys_video *av);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.c b/drivers/media/pci/intel/ipu6/ipu6-isys.c
index 24db2763de54..60f5f9ea2910 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.c
@@ -41,6 +41,9 @@
#include "ipu6-platform-buttress-regs.h"
#include "ipu6-platform-isys-csi2-reg.h"
#include "ipu6-platform-regs.h"
+#include "ipu7-isys-csi-phy.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-platform-regs.h"
#define IPU6_BUTTRESS_FABIC_CONTROL 0x68
#define GDA_ENABLE_IWAKE_INDEX 2
@@ -98,9 +101,28 @@ enum ltr_did_type {
LTR_TYPE_MAX
};
-#define ISYS_PM_QOS_VALUE 300
+struct ltr_did {
+ union {
+ u32 value;
+ struct {
+ u8 val0;
+ u8 val1;
+ u8 val2;
+ u8 val3;
+ } bits;
+ } lut_ltr;
+ union {
+ u32 value;
+ struct {
+ u8 th0;
+ u8 th1;
+ u8 th2;
+ u8 th3;
+ } bits;
+ } lut_fill_time;
+};
-static int isys_isr_one(struct ipu6_bus_device *adev);
+#define ISYS_PM_QOS_VALUE 300
static int
isys_complete_ext_device_registration(struct ipu6_isys *isys,
@@ -132,6 +154,9 @@ isys_complete_ext_device_registration(struct ipu6_isys *isys,
}
isys->csi2[csi2->port].nlanes = csi2->nlanes;
+ isys->csi2[csi2->port].phy_mode =
+ csi2->bus_type == V4L2_MBUS_CSI2_DPHY ?
+ PHY_MODE_DPHY : PHY_MODE_CPHY;
return 0;
@@ -141,23 +166,6 @@ unregister_subdev:
return ret;
}
-static void isys_stream_init(struct ipu6_isys *isys)
-{
- u32 i;
-
- for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++) {
- mutex_init(&isys->streams[i].mutex);
- init_completion(&isys->streams[i].stream_open_completion);
- init_completion(&isys->streams[i].stream_close_completion);
- init_completion(&isys->streams[i].stream_start_completion);
- init_completion(&isys->streams[i].stream_stop_completion);
- INIT_LIST_HEAD(&isys->streams[i].queues);
- isys->streams[i].isys = isys;
- isys->streams[i].stream_handle = i;
- isys->streams[i].vc = INVALID_VC_ID;
- }
-}
-
static void isys_csi2_unregister_subdevices(struct ipu6_isys *isys)
{
const struct ipu6_isys_internal_csi2_pdata *csi2 =
@@ -176,13 +184,23 @@ static int isys_csi2_register_subdevices(struct ipu6_isys *isys)
int ret;
for (i = 0; i < csi2_pdata->nports; i++) {
- ret = ipu6_isys_csi2_init(&isys->csi2[i], isys,
- isys->pdata->base +
- CSI_REG_PORT_BASE(i), i);
+ void __iomem *base = isys->pdata->base;
+
+ if (IS_IPU7(isys->adev->isp)) {
+ u32 mask = IS_IPU7_MTL(isys->adev->isp) ?
+ IPU7_CSI_LEGACY_IRQ_MASK(i) :
+ IPU7P5_CSI_LEGACY_IRQ_MASK(i);
+
+ isys->isr_csi2_bits |= mask;
+ isys->csi2[i].legacy_irq_mask = mask;
+ } else {
+ base += CSI_REG_PORT_BASE(i);
+ isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
+ }
+
+ ret = ipu6_isys_csi2_init(&isys->csi2[i], isys, base, i);
if (ret)
goto fail;
-
- isys->isr_csi2_bits |= IPU6_ISYS_UNISPART_IRQ_CSI2(i);
}
return 0;
@@ -269,7 +287,36 @@ fail:
return ret;
}
-static void isys_setup_hw(struct ipu6_isys *isys)
+static void ipu7_isys_setup_hw(struct ipu6_isys *isys)
+{
+ u32 offset, mask;
+ void __iomem *base = isys->pdata->base;
+
+ offset = IPU7_IS_IO_GPREGS_BASE;
+
+ writel(0x0, base + offset + IPU7_CLK_EN_TXCLKESC);
+ /* Update if ISYS freq updated (0: 400/1, 1:400/2, 63:400/64) */
+ writel(0x0, base + offset + IPU7_CLK_DIV_FACTOR_IS_CLK);
+ /* correct the initial printf configuration */
+ writel(0x200, base + IPU7_IS_UC_CTRL_BASE + IPU7_REG_PRINTF_AXI_CNTL);
+
+ offset = IPU7_IS_UC_CTRL_BASE;
+ mask = IPU7_IS_UC_TO_SW_IRQ_MASK;
+
+ writel(mask, base + offset + IPU7_TO_SW_IRQ_CNTL_CLEAR);
+ writel(mask, base + offset + IPU7_TO_SW_IRQ_CNTL_MASK_N);
+ writel(mask, base + offset + IPU7_TO_SW_IRQ_CNTL_ENABLE);
+
+ offset = IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE;
+ mask = IPU7_CSI_RX_LEGACY_IRQ_MASK;
+
+ writel(mask, base + offset + IPU7_IRQ_CTL_EDGE);
+ writel(mask, base + offset + IPU7_IRQ_CTL_CLEAR);
+ writel(mask, base + offset + IPU7_IRQ_CTL_MASK);
+ writel(mask, base + offset + IPU7_IRQ_CTL_ENABLE);
+}
+
+static void ipu6_isys_setup_hw(struct ipu6_isys *isys)
{
void __iomem *base = isys->pdata->base;
const u8 *thd = isys->pdata->ipdata->hw_variant.cdc_fifo_threshold;
@@ -304,118 +351,6 @@ static void isys_setup_hw(struct ipu6_isys *isys)
writel(thd[i], base + IPU6_REG_ISYS_CDC_THRESHOLD(i));
}
-static void ipu6_isys_csi2_isr(struct ipu6_isys_csi2 *csi2)
-{
- struct ipu6_isys_stream *stream;
- unsigned int i;
- u32 status;
- int source;
-
- ipu6_isys_register_errors(csi2);
-
- status = readl(csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
- CSI_PORT_REG_BASE_IRQ_STATUS_OFFSET);
-
- writel(status, csi2->base + CSI_PORT_REG_BASE_IRQ_CSI_SYNC +
- CSI_PORT_REG_BASE_IRQ_CLEAR_OFFSET);
-
- source = csi2->asd.source;
- for (i = 0; i < NR_OF_CSI2_VC; i++) {
- if (status & IPU_CSI_RX_IRQ_FS_VC(i)) {
- stream = ipu6_isys_query_stream_by_source(csi2->isys,
- source, i);
- if (stream) {
- ipu6_isys_csi2_sof_event_by_stream(stream);
- ipu6_isys_put_stream(stream);
- }
- }
-
- if (status & IPU_CSI_RX_IRQ_FE_VC(i)) {
- stream = ipu6_isys_query_stream_by_source(csi2->isys,
- source, i);
- if (stream) {
- ipu6_isys_csi2_eof_event_by_stream(stream);
- ipu6_isys_put_stream(stream);
- }
- }
- }
-}
-
-static irqreturn_t isys_isr(struct ipu6_bus_device *adev)
-{
- struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
- void __iomem *base = isys->pdata->base;
- u32 status_sw, status_csi;
- u32 ctrl0_status, ctrl0_clear;
-
- spin_lock(&isys->power_lock);
- if (!isys->power) {
- spin_unlock(&isys->power_lock);
- return IRQ_NONE;
- }
-
- ctrl0_status = isys->pdata->ipdata->csi2.ctrl0_irq_status;
- ctrl0_clear = isys->pdata->ipdata->csi2.ctrl0_irq_clear;
-
- status_csi = readl(isys->pdata->base + ctrl0_status);
- status_sw = readl(isys->pdata->base +
- IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
-
- writel(ISYS_UNISPART_IRQS & ~IPU6_ISYS_UNISPART_IRQ_SW,
- base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
-
- do {
- writel(status_csi, isys->pdata->base + ctrl0_clear);
-
- writel(status_sw, isys->pdata->base +
- IPU6_REG_ISYS_UNISPART_IRQ_CLEAR);
-
- if (isys->isr_csi2_bits & status_csi) {
- unsigned int i;
-
- for (i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
- /* irq from not enabled port */
- if (!isys->csi2[i].base)
- continue;
- if (status_csi & IPU6_ISYS_UNISPART_IRQ_CSI2(i))
- ipu6_isys_csi2_isr(&isys->csi2[i]);
- }
- }
-
- writel(0, base + IPU6_REG_ISYS_UNISPART_SW_IRQ_REG);
-
- if (!isys_isr_one(adev))
- status_sw = IPU6_ISYS_UNISPART_IRQ_SW;
- else
- status_sw = 0;
-
- status_csi = readl(isys->pdata->base + ctrl0_status);
- status_sw |= readl(isys->pdata->base +
- IPU6_REG_ISYS_UNISPART_IRQ_STATUS);
- } while ((status_csi & isys->isr_csi2_bits) ||
- (status_sw & IPU6_ISYS_UNISPART_IRQ_SW));
-
- writel(ISYS_UNISPART_IRQS, base + IPU6_REG_ISYS_UNISPART_IRQ_MASK);
-
- spin_unlock(&isys->power_lock);
-
- return IRQ_HANDLED;
-}
-
-static void get_lut_ltrdid(struct ipu6_isys *isys, struct ltr_did *pltr_did)
-{
- struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
- struct ltr_did ltrdid_default;
-
- ltrdid_default.lut_ltr.value = LTR_DEFAULT_VALUE;
- ltrdid_default.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE;
-
- if (iwake_watermark->ltrdid.lut_ltr.value)
- *pltr_did = iwake_watermark->ltrdid;
- else
- *pltr_did = ltrdid_default;
-}
-
static int set_iwake_register(struct ipu6_isys *isys, u32 index, u32 value)
{
struct device *dev = &isys->adev->auxdev.dev;
@@ -511,69 +446,57 @@ static void set_iwake_ltrdid(struct ipu6_isys *isys, u16 ltr, u16 did,
*/
static void enable_iwake(struct ipu6_isys *isys, bool enable)
{
- struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
int ret;
- mutex_lock(&iwake_watermark->mutex);
-
- if (iwake_watermark->iwake_enabled == enable) {
- mutex_unlock(&iwake_watermark->mutex);
+ if (isys->iwake_watermark_enabled == enable)
return;
- }
ret = set_iwake_register(isys, GDA_ENABLE_IWAKE_INDEX, enable);
if (!ret)
- iwake_watermark->iwake_enabled = enable;
-
- mutex_unlock(&iwake_watermark->mutex);
+ isys->iwake_watermark_enabled = enable;
}
-void update_watermark_setting(struct ipu6_isys *isys)
+void ipu6_isys_update_watermark_setting(struct ipu6_isys *isys)
{
- struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
u32 iwake_threshold, iwake_critical_threshold, page_num;
struct device *dev = &isys->adev->auxdev.dev;
u32 calc_fill_time_us = 0, ltr = 0, did = 0;
- struct video_stream_watermark *p_watermark;
enum ltr_did_type ltr_did_type;
- struct list_head *stream_node;
u64 isys_pb_datarate_mbs = 0;
u32 mem_open_threshold = 0;
struct ltr_did ltrdid;
u64 threshold_bytes;
u32 max_sram_size;
u32 shift;
+ bool force_iwake_disable = false;
+
+ lockdep_assert_held(&isys->stream_mutex);
shift = isys->pdata->ipdata->sram_gran_shift;
max_sram_size = isys->pdata->ipdata->max_sram_size;
- mutex_lock(&iwake_watermark->mutex);
- if (iwake_watermark->force_iwake_disable) {
+ for (unsigned int i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
+ isys_pb_datarate_mbs +=
+ isys->csi2[i].watermark.stream_data_rate;
+ force_iwake_disable |=
+ isys->csi2[i].watermark.force_iwake_disable;
+ }
+
+ if (force_iwake_disable) {
+ dev_dbg(dev, "watermark: forcing iwake disabled\n");
set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
CRITICAL_THRESHOLD_IWAKE_DISABLE);
- goto unlock_exit;
- }
-
- if (list_empty(&iwake_watermark->video_list)) {
- isys_pb_datarate_mbs = 0;
- } else {
- list_for_each(stream_node, &iwake_watermark->video_list) {
- p_watermark = list_entry(stream_node,
- struct video_stream_watermark,
- stream_node);
- isys_pb_datarate_mbs += p_watermark->stream_data_rate;
- }
+ return;
}
- mutex_unlock(&iwake_watermark->mutex);
if (!isys_pb_datarate_mbs) {
+ dev_dbg(dev, "watermark: disabled iwake\n");
enable_iwake(isys, false);
set_iwake_ltrdid(isys, 0, 0, LTR_IWAKE_OFF);
- mutex_lock(&iwake_watermark->mutex);
set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
CRITICAL_THRESHOLD_IWAKE_DISABLE);
- goto unlock_exit;
+ return;
}
enable_iwake(isys, true);
@@ -584,7 +507,8 @@ void update_watermark_setting(struct ipu6_isys *isys)
did = calc_fill_time_us * DEFAULT_DID_RATIO / 100;
ltr_did_type = LTR_ENHANNCE_IWAKE;
} else {
- get_lut_ltrdid(isys, &ltrdid);
+ ltrdid.lut_ltr.value = LTR_DEFAULT_VALUE;
+ ltrdid.lut_fill_time.value = FILL_TIME_DEFAULT_VALUE;
if (calc_fill_time_us <= ltrdid.lut_fill_time.bits.th0)
ltr = 0;
@@ -608,7 +532,6 @@ void update_watermark_setting(struct ipu6_isys *isys)
iwake_threshold = max_t(u32, 1, threshold_bytes >> shift);
iwake_threshold = min_t(u32, iwake_threshold, max_sram_size);
- mutex_lock(&iwake_watermark->mutex);
if (isys->pdata->ipdata->enhanced_iwake) {
set_iwake_register(isys, GDA_IWAKE_THRESHOLD_INDEX,
DEFAULT_IWAKE_THRESHOLD);
@@ -630,7 +553,7 @@ void update_watermark_setting(struct ipu6_isys *isys)
iwake_critical_threshold = iwake_threshold +
(IS_PIXEL_BUFFER_PAGES - iwake_threshold) / 2;
- dev_dbg(dev, "threshold: %u critical: %u\n", iwake_threshold,
+ dev_dbg(dev, "watermark: threshold: %u critical: %u\n", iwake_threshold,
iwake_critical_threshold);
set_iwake_register(isys, GDA_IRQ_CRITICAL_THRESHOLD_INDEX,
@@ -640,32 +563,6 @@ void update_watermark_setting(struct ipu6_isys *isys)
isys->adev->isp->base + REG_PKGC_PMON_CFG);
writel(VAL_PKGC_PMON_CFG_START,
isys->adev->isp->base + REG_PKGC_PMON_CFG);
-unlock_exit:
- mutex_unlock(&iwake_watermark->mutex);
-}
-
-static void isys_iwake_watermark_init(struct ipu6_isys *isys)
-{
- struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
-
- INIT_LIST_HEAD(&iwake_watermark->video_list);
- mutex_init(&iwake_watermark->mutex);
-
- iwake_watermark->ltrdid.lut_ltr.value = 0;
- iwake_watermark->isys = isys;
- iwake_watermark->iwake_enabled = false;
- iwake_watermark->force_iwake_disable = false;
-}
-
-static void isys_iwake_watermark_cleanup(struct ipu6_isys *isys)
-{
- struct isys_iwake_watermark *iwake_watermark = &isys->iwake_watermark;
-
- mutex_lock(&iwake_watermark->mutex);
- list_del(&iwake_watermark->video_list);
- mutex_unlock(&iwake_watermark->mutex);
-
- mutex_destroy(&iwake_watermark->mutex);
}
/* The .bound() notifier callback when a match is found */
@@ -736,6 +633,10 @@ static int isys_notifier_init(struct ipu6_isys *isys)
continue;
ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ if (ret && IS_IPU7(isp)) {
+ vep.bus_type = V4L2_MBUS_CSI2_CPHY;
+ ret = v4l2_fwnode_endpoint_parse(ep, &vep);
+ }
if (ret) {
dev_err(dev, "fwnode endpoint parse failed: %d\n", ret);
goto err_parse;
@@ -751,6 +652,7 @@ static int isys_notifier_init(struct ipu6_isys *isys)
s_asd->csi2.port = vep.base.port;
s_asd->csi2.nlanes = vep.bus.mipi_csi2.num_data_lanes;
+ s_asd->csi2.bus_type = vep.bus_type;
dev_dbg(dev, "remote endpoint port %d with %d lanes added\n",
s_asd->csi2.port, s_asd->csi2.nlanes);
@@ -789,7 +691,7 @@ static int isys_register_devices(struct ipu6_isys *isys)
isys->media_dev.dev = dev;
media_device_pci_init(&isys->media_dev,
- pdev, IPU6_MEDIA_DEV_MODEL_NAME);
+ pdev, isys->adev->isp->model_name);
strscpy(isys->v4l2_dev.name, isys->media_dev.model,
sizeof(isys->v4l2_dev.name));
@@ -853,9 +755,10 @@ static void isys_unregister_devices(struct ipu6_isys *isys)
static int isys_runtime_pm_resume(struct device *dev)
{
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
+ const struct ipu6_fw_isys_ops *fw_ops = adev->auxdrv_data->fw_ops;
struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ const struct ipu6_isys_internal_pdata *ipdata = isys->pdata->ipdata;
struct ipu6_device *isp = adev->isp;
- unsigned long flags;
int ret;
ret = ipu6_mmu_hw_init(adev->mmu);
@@ -866,49 +769,82 @@ static int isys_runtime_pm_resume(struct device *dev)
ret = ipu6_buttress_start_tsc_sync(isp);
if (ret)
- return ret;
+ goto err_mmu_hw_cleanup;
- spin_lock_irqsave(&isys->power_lock, flags);
- isys->power = 1;
- spin_unlock_irqrestore(&isys->power_lock, flags);
+ if (IS_IPU7(isp)) {
+ ipu7_isys_setup_hw(isys);
+ } else {
+ ipu6_isys_setup_hw(isys);
+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON);
+ }
- isys_setup_hw(isys);
+ ipu6_configure_spc(adev->isp, &ipdata->hw_variant,
+ IPU6_CPD_PKG_DIR_ISYS_SERVER_IDX, isys->pdata->base,
+ adev->pkg_dir, adev->pkg_dir_dma_addr);
- set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_ON);
+ /*
+ * Buffers could have been left to wrong queue at last closure.
+ * Move them now back to empty buffer queue.
+ */
+ ipu6_cleanup_fw_msg_bufs(isys);
- return 0;
+ if (isys->fwctx) {
+ /*
+ * Something went wrong in previous shutdown. As we are now
+ * restarting isys we can safely delete old context.
+ */
+ dev_warn(&adev->auxdev.dev, "clearing old context\n");
+ fw_ops->cleanup(isys);
+ }
+
+ ret = fw_ops->init(isys, ipdata->num_parallel_streams);
+ if (!ret)
+ return 0;
+
+ isys->phy_termcal_val = 0;
+ cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
+
+ if (!IS_IPU7(isp))
+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF);
+
+err_mmu_hw_cleanup:
+ ipu6_mmu_hw_cleanup(adev->mmu);
+
+ return ret;
}
static int isys_runtime_pm_suspend(struct device *dev)
{
struct ipu6_bus_device *adev = to_ipu6_bus_device(dev);
struct ipu6_isys *isys = dev_get_drvdata(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&isys->power_lock, flags);
- isys->power = 0;
- spin_unlock_irqrestore(&isys->power_lock, flags);
+ struct ipu6_device *isp = adev->isp;
+ int ret = 0;
- mutex_lock(&isys->mutex);
- isys->need_reset = false;
- mutex_unlock(&isys->mutex);
+ isys->adev->auxdrv_data->fw_ops->close(isys);
+ if (isys->fwctx) {
+ dev_warn(&isys->adev->auxdev.dev, "failed to close fw isys\n");
+ ret = -EIO;
+ }
isys->phy_termcal_val = 0;
cpu_latency_qos_update_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
- set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF);
+ if (!IS_IPU7(isp))
+ set_iwake_ltrdid(isys, 0, 0, LTR_ISYS_OFF);
ipu6_mmu_hw_cleanup(adev->mmu);
- return 0;
+ return ret;
}
static int isys_suspend(struct device *dev)
{
struct ipu6_isys *isys = dev_get_drvdata(dev);
+ guard(mutex)(&isys->stream_mutex);
+
/* If stream is open, refuse to suspend */
- if (isys->stream_opened)
+ if (!ida_is_empty(&isys->streams))
return -EBUSY;
return 0;
@@ -1003,7 +939,7 @@ struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream)
msg = list_last_entry(&isys->framebuflist, struct isys_fw_msgs, head);
list_move(&msg->head, &isys->framebuflist_fw);
spin_unlock_irqrestore(&isys->listlock, flags);
- memset(&msg->fw_msg, 0, sizeof(msg->fw_msg));
+ memset(&msg->ipu6, 0, sizeof(msg->ipu6));
return msg;
}
@@ -1019,30 +955,28 @@ void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys)
spin_unlock_irqrestore(&isys->listlock, flags);
}
-void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, uintptr_t data)
+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, struct isys_fw_msgs *msg)
{
- struct isys_fw_msgs *msg;
unsigned long flags;
- void *ptr = (void *)data;
- if (!ptr)
+ if (!msg)
return;
spin_lock_irqsave(&isys->listlock, flags);
- msg = container_of(ptr, struct isys_fw_msgs, fw_msg.dummy);
list_move(&msg->head, &isys->framebuflist);
spin_unlock_irqrestore(&isys->listlock, flags);
}
+static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data;
+static const struct ipu6_auxdrv_data ipu7_isys_auxdrv_data;
+
static int isys_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *auxdev_id)
{
const struct ipu6_isys_internal_csi2_pdata *csi2_pdata;
struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
struct ipu6_device *isp = adev->isp;
- const struct firmware *fw;
struct ipu6_isys *isys;
- unsigned int i;
int ret;
if (!isp->bus_ready_to_probe)
@@ -1052,8 +986,8 @@ static int isys_probe(struct auxiliary_device *auxdev,
if (!isys)
return -ENOMEM;
- adev->auxdrv_data =
- (const struct ipu6_auxdrv_data *)auxdev_id->driver_data;
+ adev->auxdrv_data = IS_IPU7(isp) ? &ipu7_isys_auxdrv_data :
+ &ipu6_isys_auxdrv_data;
adev->auxdrv = to_auxiliary_drv(auxdev->dev.driver);
isys->adev = adev;
isys->pdata = adev->pdata;
@@ -1068,8 +1002,6 @@ static int isys_probe(struct auxiliary_device *auxdev,
isys->sensor_type = isys->pdata->ipdata->sensor_type_start;
spin_lock_init(&isys->streams_lock);
- spin_lock_init(&isys->power_lock);
- isys->power = 0;
isys->phy_termcal_val = 0;
mutex_init(&isys->mutex);
@@ -1083,18 +1015,7 @@ static int isys_probe(struct auxiliary_device *auxdev,
dev_set_drvdata(&auxdev->dev, isys);
- isys_stream_init(isys);
-
- if (!isp->secure_mode) {
- fw = isp->cpd_fw;
- ret = ipu6_buttress_map_fw_image(adev, fw, &adev->fw_sgt);
- if (ret)
- goto release_firmware;
-
- ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data);
- if (ret)
- goto remove_shared_buffer;
- }
+ ida_init(&isys->streams);
cpu_latency_qos_add_request(&isys->pm_qos, PM_QOS_DEFAULT_VALUE);
@@ -1102,11 +1023,11 @@ static int isys_probe(struct auxiliary_device *auxdev,
if (ret < 0)
goto out_remove_pkg_dir_shared_buffer;
- isys_iwake_watermark_init(isys);
-
- if (is_ipu6se(adev->isp->hw_ver))
+ if (IS_IPU7(adev->isp))
+ isys->phy_set_power = ipu7_isys_csi_phy_set_power;
+ else if (IS_IPU6SE(adev->isp))
isys->phy_set_power = ipu6_isys_jsl_phy_set_power;
- else if (is_ipu6ep_mtl(adev->isp->hw_ver))
+ else if (IS_IPU6EP_MTL(adev->isp))
isys->phy_set_power = ipu6_isys_dwc_phy_set_power;
else
isys->phy_set_power = ipu6_isys_mcd_phy_set_power;
@@ -1121,17 +1042,6 @@ free_fw_msg_bufs:
free_fw_msg_bufs(isys);
out_remove_pkg_dir_shared_buffer:
cpu_latency_qos_remove_request(&isys->pm_qos);
- if (!isp->secure_mode)
- ipu6_cpd_free_pkg_dir(adev);
-remove_shared_buffer:
- if (!isp->secure_mode)
- ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
-release_firmware:
- if (!isp->secure_mode)
- release_firmware(adev->fw);
-
- for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
- mutex_destroy(&isys->streams[i].mutex);
mutex_destroy(&isys->mutex);
mutex_destroy(&isys->stream_mutex);
@@ -1141,10 +1051,9 @@ release_firmware:
static void isys_remove(struct auxiliary_device *auxdev)
{
- struct ipu6_bus_device *adev = auxdev_to_adev(auxdev);
struct ipu6_isys *isys = dev_get_drvdata(&auxdev->dev);
- struct ipu6_device *isp = adev->isp;
- unsigned int i;
+
+ ida_destroy(&isys->streams);
free_fw_msg_bufs(isys);
@@ -1153,191 +1062,27 @@ static void isys_remove(struct auxiliary_device *auxdev)
cpu_latency_qos_remove_request(&isys->pm_qos);
- if (!isp->secure_mode) {
- ipu6_cpd_free_pkg_dir(adev);
- ipu6_buttress_unmap_fw_image(adev, &adev->fw_sgt);
- release_firmware(adev->fw);
- }
-
- for (i = 0; i < IPU6_ISYS_MAX_STREAMS; i++)
- mutex_destroy(&isys->streams[i].mutex);
-
- isys_iwake_watermark_cleanup(isys);
mutex_destroy(&isys->stream_mutex);
mutex_destroy(&isys->mutex);
}
-struct fwmsg {
- int type;
- char *msg;
- bool valid_ts;
-};
-
-static const struct fwmsg fw_msg[] = {
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE", 0},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK", 0},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK, "STREAM_START_ACK", 0},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
- "STREAM_START_AND_CAPTURE_ACK", 0},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK, "STREAM_STOP_ACK", 0},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK", 0},
- {IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY", 1},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK", 0},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
- "STREAM_START_AND_CAPTURE_DONE", 1},
- {IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE", 1},
- {IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF", 1},
- {IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF", 1},
- {IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY, "STATS_READY", 1},
- {-1, "UNKNOWN MESSAGE", 0}
+static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = {
+ .isr = ipu6_isys_isr,
+ .isr_threaded = NULL,
+ .wake_isr_thread = false,
+ .fw_ops = &ipu6_fw_isys_ops,
};
-static u32 resp_type_to_index(int type)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(fw_msg); i++)
- if (fw_msg[i].type == type)
- return i;
-
- return ARRAY_SIZE(fw_msg) - 1;
-}
-
-static int isys_isr_one(struct ipu6_bus_device *adev)
-{
- struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
- struct ipu6_fw_isys_resp_info_abi *resp;
- struct ipu6_isys_stream *stream;
- struct ipu6_isys_csi2 *csi2 = NULL;
- u32 index;
- u64 ts;
-
- if (!isys->fwcom)
- return 1;
-
- resp = ipu6_fw_isys_get_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
- if (!resp)
- return 1;
-
- ts = (u64)resp->timestamp[1] << 32 | resp->timestamp[0];
-
- index = resp_type_to_index(resp->type);
- dev_dbg(&adev->auxdev.dev,
- "FW resp %02d %s, stream %u, ts 0x%16.16llx, pin %d\n",
- resp->type, fw_msg[index].msg, resp->stream_handle,
- fw_msg[index].valid_ts ? ts : 0, resp->pin_id);
-
- if (resp->error_info.error == IPU6_FW_ISYS_ERROR_STREAM_IN_SUSPENSION)
- /* Suspension is kind of special case: not enough buffers */
- dev_dbg(&adev->auxdev.dev,
- "FW error resp SUSPENSION, details %d\n",
- resp->error_info.error_details);
- else if (resp->error_info.error)
- dev_dbg(&adev->auxdev.dev,
- "FW error resp error %d, details %d\n",
- resp->error_info.error, resp->error_info.error_details);
-
- if (resp->stream_handle >= IPU6_ISYS_MAX_STREAMS) {
- dev_err(&adev->auxdev.dev, "bad stream handle %u\n",
- resp->stream_handle);
- goto leave;
- }
-
- stream = ipu6_isys_query_stream_by_handle(isys, resp->stream_handle);
- if (!stream) {
- dev_err(&adev->auxdev.dev, "stream of stream_handle %u is unused\n",
- resp->stream_handle);
- goto leave;
- }
- stream->error = resp->error_info.error;
-
- csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
-
- switch (resp->type) {
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_OPEN_DONE:
- complete(&stream->stream_open_completion);
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_CLOSE_ACK:
- complete(&stream->stream_close_completion);
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_ACK:
- complete(&stream->stream_start_completion);
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
- complete(&stream->stream_start_completion);
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_STOP_ACK:
- complete(&stream->stream_stop_completion);
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_FLUSH_ACK:
- complete(&stream->stream_stop_completion);
- break;
- case IPU6_FW_ISYS_RESP_TYPE_PIN_DATA_READY:
- /*
- * firmware only release the capture msg until software
- * get pin_data_ready event
- */
- ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), resp->buf_id);
- if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS &&
- stream->output_pins_queue[resp->pin_id])
- ipu6_isys_queue_buf_ready(stream, resp);
- else
- dev_warn(&adev->auxdev.dev,
- "%d:No queue for pin id %d\n",
- resp->stream_handle, resp->pin_id);
- if (csi2)
- ipu6_isys_csi2_error(csi2);
-
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_ACK:
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
- case IPU6_FW_ISYS_RESP_TYPE_STREAM_CAPTURE_DONE:
- break;
- case IPU6_FW_ISYS_RESP_TYPE_FRAME_SOF:
-
- ipu6_isys_csi2_sof_event_by_stream(stream);
- stream->seq[stream->seq_index].sequence =
- atomic_read(&stream->sequence) - 1;
- stream->seq[stream->seq_index].timestamp = ts;
- dev_dbg(&adev->auxdev.dev,
- "sof: handle %d: (index %u), timestamp 0x%16.16llx\n",
- resp->stream_handle,
- stream->seq[stream->seq_index].sequence, ts);
- stream->seq_index = (stream->seq_index + 1)
- % IPU6_ISYS_MAX_PARALLEL_SOF;
- break;
- case IPU6_FW_ISYS_RESP_TYPE_FRAME_EOF:
- ipu6_isys_csi2_eof_event_by_stream(stream);
- dev_dbg(&adev->auxdev.dev,
- "eof: handle %d: (index %u), timestamp 0x%16.16llx\n",
- resp->stream_handle,
- stream->seq[stream->seq_index].sequence, ts);
- break;
- case IPU6_FW_ISYS_RESP_TYPE_STATS_DATA_READY:
- break;
- default:
- dev_err(&adev->auxdev.dev, "%d:unknown response type %u\n",
- resp->stream_handle, resp->type);
- break;
- }
-
- ipu6_isys_put_stream(stream);
-leave:
- ipu6_fw_isys_put_resp(isys->fwcom, IPU6_BASE_MSG_RECV_QUEUES);
- return 0;
-}
-
-static const struct ipu6_auxdrv_data ipu6_isys_auxdrv_data = {
- .isr = isys_isr,
+static const struct ipu6_auxdrv_data ipu7_isys_auxdrv_data = {
+ .isr = ipu7_isys_isr,
.isr_threaded = NULL,
.wake_isr_thread = false,
+ .fw_ops = &ipu7_fw_isys_ops,
};
static const struct auxiliary_device_id ipu6_isys_id_table[] = {
{
.name = "intel_ipu6.isys",
- .driver_data = (kernel_ulong_t)&ipu6_isys_auxdrv_data,
},
{ }
};
diff --git a/drivers/media/pci/intel/ipu6/ipu6-isys.h b/drivers/media/pci/intel/ipu6/ipu6-isys.h
index 7fb8cb820912..2af20f56a965 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-isys.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-isys.h
@@ -4,6 +4,7 @@
#ifndef IPU6_ISYS_H
#define IPU6_ISYS_H
+#include <linux/idr.h>
#include <linux/irqreturn.h>
#include <linux/list.h>
#include <linux/mutex.h>
@@ -14,9 +15,11 @@
#include <media/media-device.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
#include "ipu6.h"
#include "ipu6-fw-isys.h"
+#include "ipu7-fw-isys.h"
#include "ipu6-isys-csi2.h"
#include "ipu6-isys-video.h"
@@ -60,41 +63,10 @@ struct ipu6_bus_device;
#define IPU6EP_MTL_LTR_VALUE 1023
#define IPU6EP_MTL_MIN_MEMOPEN_TH 0xc
-struct ltr_did {
- union {
- u32 value;
- struct {
- u8 val0;
- u8 val1;
- u8 val2;
- u8 val3;
- } bits;
- } lut_ltr;
- union {
- u32 value;
- struct {
- u8 th0;
- u8 th1;
- u8 th2;
- u8 th3;
- } bits;
- } lut_fill_time;
-};
-
-struct isys_iwake_watermark {
- bool iwake_enabled;
- bool force_iwake_disable;
- u32 iwake_threshold;
- u64 isys_pixelbuffer_datarate;
- struct ltr_did ltrdid;
- struct mutex mutex; /* protect whole struct */
- struct ipu6_isys *isys;
- struct list_head video_list;
-};
-
struct ipu6_isys_csi2_config {
u32 nlanes;
u32 port;
+ enum v4l2_mbus_type bus_type;
};
struct sensor_async_sd {
@@ -108,17 +80,12 @@ struct sensor_async_sd {
* @media_dev: Media device
* @v4l2_dev: V4L2 device
* @adev: ISYS bus device
- * @power: Is ISYS powered on or not?
- * @isr_bits: Which bits does the ISR handle?
- * @power_lock: Serialise access to power (power state in general)
* @csi2_rx_ctrl_cached: cached shared value between all CSI2 receivers
* @streams_lock: serialise access to streams
* @streams: streams per firmware stream ID
- * @fwcom: fw communication layer private pointer
- * or optional external library private pointer
+ * @fwctx: fw communication layer context pointer
* @phy_termcal_val: the termination calibration value, only used for DWC PHY
* @need_reset: Isys requires d0i0->i3 transition
- * @ref_count: total number of callers fw open
* @mutex: serialise access isys video open/release related operations
* @stream_mutex: serialise stream start and stop, queueing requests
* @pdata: platform data pointer
@@ -129,20 +96,17 @@ struct ipu6_isys {
struct v4l2_device v4l2_dev;
struct ipu6_bus_device *adev;
- int power;
- spinlock_t power_lock;
u32 isr_csi2_bits;
u32 csi2_rx_ctrl_cached;
spinlock_t streams_lock;
- struct ipu6_isys_stream streams[IPU6_ISYS_MAX_STREAMS];
- int streams_ref_count[IPU6_ISYS_MAX_STREAMS];
- void *fwcom;
+ struct ipu6_isys_stream *streams_by_handle[IPU6_ISYS_MAX_STREAMS];
+ void *fwctx;
u32 phy_termcal_val;
+ u32 phy_rext_cal;
bool need_reset;
bool icache_prefetch;
bool csi2_cse_ipc_not_supported;
- unsigned int ref_count;
- unsigned int stream_opened;
+ bool iwake_watermark_enabled;
unsigned int sensor_type;
struct mutex mutex;
@@ -162,26 +126,65 @@ struct ipu6_isys {
struct list_head framebuflist;
struct list_head framebuflist_fw;
struct v4l2_async_notifier notifier;
- struct isys_iwake_watermark iwake_watermark;
+ struct ida streams;
};
struct isys_fw_msgs {
union {
u64 dummy;
- struct ipu6_fw_isys_frame_buff_set_abi frame;
- struct ipu6_fw_isys_stream_cfg_data_abi stream;
- } fw_msg;
+ union {
+ struct ipu6_fw_isys_frame_buff_set_abi frame;
+ struct ipu6_fw_isys_stream_cfg_data_abi stream;
+ } ipu6;
+ union {
+ struct ipu7_fw_isys_frame_buff_set frame;
+ struct ipu7_fw_isys_stream_cfg stream;
+ } ipu7;
+ };
struct list_head head;
dma_addr_t dma_addr;
};
+struct ipu6_fw_isys_ops {
+ int (*init)(struct ipu6_isys *isys, unsigned int num_streams);
+ int (*close)(struct ipu6_isys *isys);
+ int (*send_cmd)(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ void *cpu_mapped_buf,
+ dma_addr_t dma_mapped_buf,
+ size_t size, u16 send_type);
+ void (*cleanup)(struct ipu6_isys *isys);
+ int (*prepare_stream_cfg)(struct ipu6_isys_stream *stream,
+ struct v4l2_mbus_frame_desc *desc,
+ struct isys_fw_msgs *msg);
+ void (*prepare_buf_set)(struct isys_fw_msgs *msg,
+ struct ipu6_isys_stream *stream,
+ struct ipu6_isys_buffer_list *bl);
+ int (*stream_open)(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg);
+ int (*stream_start)(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg, bool capture);
+ int (*stream_capture)(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg);
+ int (*stream_flush)(struct ipu6_isys *isys,
+ const unsigned int stream_handle);
+ int (*stream_close)(struct ipu6_isys *isys,
+ const unsigned int stream_handle);
+ void (*dump_stream_cfg)(struct device *dev, struct isys_fw_msgs *msg);
+ void (*dump_frame_buf_set)(struct device *dev, struct isys_fw_msgs *msg,
+ unsigned int outputs);
+};
+
struct isys_fw_msgs *ipu6_get_fw_msg_buf(struct ipu6_isys_stream *stream);
-void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, uintptr_t data);
+void ipu6_put_fw_msg_buf(struct ipu6_isys *isys, struct isys_fw_msgs *msg);
void ipu6_cleanup_fw_msg_bufs(struct ipu6_isys *isys);
extern const struct v4l2_ioctl_ops ipu6_isys_ioctl_ops;
-void update_watermark_setting(struct ipu6_isys *isys);
+void ipu6_isys_update_watermark_setting(struct ipu6_isys *isys);
int ipu6_isys_mcd_phy_set_power(struct ipu6_isys *isys,
struct ipu6_isys_csi2_config *cfg,
diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu-hw.c b/drivers/media/pci/intel/ipu6/ipu6-mmu-hw.c
new file mode 100644
index 000000000000..2b395fbd5969
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu-hw.c
@@ -0,0 +1,296 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Intel Corporation
+ */
+#include <asm/barrier.h>
+
+#include <linux/bits.h>
+#include <linux/gfp.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include "ipu6.h"
+#include "ipu6-dma.h"
+#include "ipu6-mmu.h"
+#include "ipu6-platform-regs.h"
+
+#define ISP_PAGE_SHIFT 12
+#define ISP_PAGE_SIZE BIT(ISP_PAGE_SHIFT)
+#define ISP_PAGE_MASK (~(ISP_PAGE_SIZE - 1))
+
+#define ISP_L1PT_SHIFT 22
+#define ISP_L1PT_MASK (~((1U << ISP_L1PT_SHIFT) - 1))
+
+#define ISP_L2PT_SHIFT 12
+#define ISP_L2PT_MASK (~(ISP_L1PT_MASK | (~(ISP_PAGE_MASK))))
+
+#define ISP_L1PT_PTES 1024
+#define ISP_L2PT_PTES 1024
+
+#define ISP_PADDR_SHIFT 12
+
+#define REG_TLB_INVALIDATE 0x0000
+
+#define REG_L1_PHYS 0x0004 /* 27-bit pfn */
+#define REG_INFO 0x0008
+
+#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT)
+
+static struct ipu6_mmu_hw ipu6_isys_mmu_hwdata[] = {
+ {
+ .offset = IPU6_ISYS_IOMMU0_OFFSET,
+ .info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF,
+ .nr_l1streams = 16,
+ .l1_block_sz = {
+ 3, 8, 2, 2, 2, 2, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1
+ },
+ .nr_l2streams = 16,
+ .l2_block_sz = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2
+ },
+ .insert_read_before_invalidate = false,
+ .l1_stream_id_reg_offset =
+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
+ .l2_stream_id_reg_offset =
+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
+ },
+ {
+ .offset = IPU6_ISYS_IOMMU1_OFFSET,
+ .info_bits = 0,
+ .nr_l1streams = 16,
+ .l1_block_sz = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 1, 1, 4
+ },
+ .nr_l2streams = 16,
+ .l2_block_sz = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2
+ },
+ .insert_read_before_invalidate = false,
+ .l1_stream_id_reg_offset =
+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
+ .l2_stream_id_reg_offset =
+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
+ },
+ {
+ .offset = IPU6_ISYS_IOMMUI_OFFSET,
+ .info_bits = 0,
+ .nr_l1streams = 0,
+ .nr_l2streams = 0,
+ .insert_read_before_invalidate = false,
+ },
+};
+
+static struct ipu6_mmu_hw ipu6_psys_mmu_hwdata[] = {
+ {
+ .offset = IPU6_PSYS_IOMMU0_OFFSET,
+ .info_bits =
+ IPU6_INFO_REQUEST_DESTINATION_IOSF,
+ .nr_l1streams = 16,
+ .l1_block_sz = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2
+ },
+ .nr_l2streams = 16,
+ .l2_block_sz = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2
+ },
+ .insert_read_before_invalidate = false,
+ .l1_stream_id_reg_offset =
+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
+ .l2_stream_id_reg_offset =
+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
+ },
+ {
+ .offset = IPU6_PSYS_IOMMU1_OFFSET,
+ .info_bits = 0,
+ .nr_l1streams = 32,
+ .l1_block_sz = {
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 10,
+ 5, 4, 14, 6, 4, 14, 6, 4, 8,
+ 4, 2, 1, 1, 1, 1, 14
+ },
+ .nr_l2streams = 32,
+ .l2_block_sz = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2
+ },
+ .insert_read_before_invalidate = false,
+ .l1_stream_id_reg_offset =
+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
+ .l2_stream_id_reg_offset =
+ IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET,
+ },
+ {
+ .offset = IPU6_PSYS_IOMMU1R_OFFSET,
+ .info_bits = 0,
+ .nr_l1streams = 16,
+ .l1_block_sz = {
+ 1, 4, 4, 4, 4, 16, 8, 4, 32,
+ 16, 16, 2, 2, 2, 1, 12
+ },
+ .nr_l2streams = 16,
+ .l2_block_sz = {
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2
+ },
+ .insert_read_before_invalidate = false,
+ .l1_stream_id_reg_offset =
+ IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
+ .l2_stream_id_reg_offset =
+ IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
+ },
+ {
+ .offset = IPU6_PSYS_IOMMUI_OFFSET,
+ .info_bits = 0,
+ .nr_l1streams = 0,
+ .nr_l2streams = 0,
+ .insert_read_before_invalidate = false,
+ },
+};
+
+struct ipu6_mmu_hwdata {
+ struct ipu6_mmu_hw *hwdata;
+ unsigned int nr_mmus;
+};
+
+static const struct ipu6_mmu_hwdata ipu6_mmu_hwdata_lookup[IPU_SUBSYS_NUM] = {
+ [IPU_PSYS] = {
+ .hwdata = ipu6_psys_mmu_hwdata,
+ .nr_mmus = ARRAY_SIZE(ipu6_psys_mmu_hwdata),
+ },
+ [IPU_ISYS] = {
+ .hwdata = ipu6_isys_mmu_hwdata,
+ .nr_mmus = ARRAY_SIZE(ipu6_isys_mmu_hwdata),
+ },
+};
+
+static void __ipu6_tlb_invalidate(struct ipu6_mmu *mmu)
+{
+ struct ipu6_mmu_hw *mmu_hw = mmu->ipu6_mmu_hw;
+ unsigned long flags;
+ unsigned int i;
+
+ spin_lock_irqsave(&mmu->ready_lock, flags);
+ if (!mmu->ready) {
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+ return;
+ }
+
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ /*
+ * To avoid the HW bug induced dead lock in some of the IPU6
+ * MMUs on successive invalidate calls, we need to first do a
+ * read to the page table base before writing the invalidate
+ * register. MMUs which need to implement this WA, will have
+ * the insert_read_before_invalidate flags set as true.
+ * Disregard the return value of the read.
+ */
+ if (mmu_hw[i].insert_read_before_invalidate)
+ readl(mmu_hw[i].base + REG_L1_PHYS);
+
+ writel(0xffffffff, mmu_hw[i].base + REG_TLB_INVALIDATE);
+ /*
+ * The TLB invalidation is a "single cycle" (IOMMU clock cycles)
+ * When the actual MMIO write reaches the IPU6 TLB Invalidate
+ * register, wmb() will force the TLB invalidate out if the CPU
+ * attempts to update the IOMMU page table (or sooner).
+ */
+ wmb();
+ }
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+}
+
+static int __ipu6_mmu_hw_init(struct ipu6_mmu *mmu)
+{
+ struct ipu6_mmu_info *mmu_info = mmu->dmap->mmu_info;
+ struct ipu6_mmu_hw *mmu_hw = mmu->ipu6_mmu_hw;
+
+ /* Initialise the each MMU HW block */
+ for (unsigned int i = 0; i < mmu->nr_mmus; i++) {
+ unsigned int j;
+ u16 block_addr;
+
+ /* Write page table address per MMU */
+ writel((phys_addr_t)mmu_info->l1_pt_dma,
+ mmu_hw[i].base + REG_L1_PHYS);
+
+ /* Set info bits per MMU */
+ writel(mmu_hw[i].info_bits, mmu_hw[i].base + REG_INFO);
+
+ /* Configure MMU TLB stream configuration for L1 */
+ for (j = 0, block_addr = 0; j < mmu_hw[i].nr_l1streams;
+ block_addr += mmu_hw[i].l1_block_sz[j], j++) {
+ if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) {
+ dev_err(mmu->dev, "invalid L1 configuration\n");
+ return -EINVAL;
+ }
+
+ /* Write block start address for each streams */
+ writel(block_addr, mmu_hw[i].base +
+ mmu_hw[i].l1_stream_id_reg_offset + 4 * j);
+ }
+
+ /* Configure MMU TLB stream configuration for L2 */
+ for (j = 0, block_addr = 0; j < mmu_hw[i].nr_l2streams;
+ block_addr += mmu_hw[i].l2_block_sz[j], j++) {
+ if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) {
+ dev_err(mmu->dev, "invalid L2 configuration\n");
+ return -EINVAL;
+ }
+
+ writel(block_addr, mmu_hw[i].base +
+ mmu_hw[i].l2_stream_id_reg_offset + 4 * j);
+ }
+ }
+
+ return 0;
+}
+
+static int __ipu6_mmu_init_hw_data(struct ipu6_mmu *mmu, struct device *dev,
+ void __iomem *base)
+{
+ const struct ipu6_mmu_hwdata *lookup;
+ struct ipu6_mmu_hw *mmu_hw, *src;
+ unsigned int i, nr_mmus;
+
+ if (mmu->mmid >= IPU_SUBSYS_NUM)
+ return -EINVAL;
+
+ lookup = &ipu6_mmu_hwdata_lookup[mmu->mmid];
+ src = lookup->hwdata;
+ nr_mmus = lookup->nr_mmus;
+
+ mmu_hw = devm_kcalloc(dev, nr_mmus, sizeof(*mmu_hw), GFP_KERNEL);
+ if (!mmu_hw)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_mmus; i++) {
+ if (src[i].nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS ||
+ src[i].nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS)
+ return -EINVAL;
+
+ mmu_hw[i] = src[i];
+ mmu_hw[i].base = base + src[i].offset;
+ }
+
+ mmu->nr_mmus = nr_mmus;
+ mmu->ipu6_mmu_hw = mmu_hw;
+
+ return 0;
+}
+
+const struct ipu6_mmu_hw_ops ipu6_mmu_ops = {
+ .init_hw_data = __ipu6_mmu_init_hw_data,
+ .hw_init = __ipu6_mmu_hw_init,
+ .tlb_invalidate = __ipu6_tlb_invalidate,
+};
diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.c b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
index 0e6dca36b6ba..243d438786ba 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-mmu.c
+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.c
@@ -5,7 +5,6 @@
#include <asm/barrier.h>
#include <linux/align.h>
-#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/bug.h>
@@ -13,10 +12,8 @@
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/gfp.h>
-#include <linux/io.h>
#include <linux/iova.h>
#include <linux/math.h>
-#include <linux/minmax.h>
#include <linux/mm.h>
#include <linux/pfn.h>
#include <linux/slab.h>
@@ -51,42 +48,6 @@
#define TBL_PHYS_ADDR(a) ((phys_addr_t)(a) << ISP_PADDR_SHIFT)
-static void tlb_invalidate(struct ipu6_mmu *mmu)
-{
- unsigned long flags;
- unsigned int i;
-
- spin_lock_irqsave(&mmu->ready_lock, flags);
- if (!mmu->ready) {
- spin_unlock_irqrestore(&mmu->ready_lock, flags);
- return;
- }
-
- for (i = 0; i < mmu->nr_mmus; i++) {
- /*
- * To avoid the HW bug induced dead lock in some of the IPU6
- * MMUs on successive invalidate calls, we need to first do a
- * read to the page table base before writing the invalidate
- * register. MMUs which need to implement this WA, will have
- * the insert_read_before_invalidate flags set as true.
- * Disregard the return value of the read.
- */
- if (mmu->mmu_hw[i].insert_read_before_invalidate)
- readl(mmu->mmu_hw[i].base + REG_L1_PHYS);
-
- writel(0xffffffff, mmu->mmu_hw[i].base +
- REG_TLB_INVALIDATE);
- /*
- * The TLB invalidation is a "single cycle" (IOMMU clock cycles)
- * When the actual MMIO write reaches the IPU6 TLB Invalidate
- * register, wmb() will force the TLB invalidate out if the CPU
- * attempts to update the IOMMU page table (or sooner).
- */
- wmb();
- }
- spin_unlock_irqrestore(&mmu->ready_lock, flags);
-}
-
#ifdef DEBUG
static void page_table_dump(struct ipu6_mmu_info *mmu_info)
{
@@ -472,55 +433,14 @@ out_free_iova:
int ipu6_mmu_hw_init(struct ipu6_mmu *mmu)
{
- struct ipu6_mmu_info *mmu_info;
unsigned long flags;
- unsigned int i;
-
- mmu_info = mmu->dmap->mmu_info;
-
- /* Initialise the each MMU HW block */
- for (i = 0; i < mmu->nr_mmus; i++) {
- struct ipu6_mmu_hw *mmu_hw = &mmu->mmu_hw[i];
- unsigned int j;
- u16 block_addr;
-
- /* Write page table address per MMU */
- writel((phys_addr_t)mmu_info->l1_pt_dma,
- mmu->mmu_hw[i].base + REG_L1_PHYS);
-
- /* Set info bits per MMU */
- writel(mmu->mmu_hw[i].info_bits,
- mmu->mmu_hw[i].base + REG_INFO);
-
- /* Configure MMU TLB stream configuration for L1 */
- for (j = 0, block_addr = 0; j < mmu_hw->nr_l1streams;
- block_addr += mmu->mmu_hw[i].l1_block_sz[j], j++) {
- if (block_addr > IPU6_MAX_LI_BLOCK_ADDR) {
- dev_err(mmu->dev, "invalid L1 configuration\n");
- return -EINVAL;
- }
-
- /* Write block start address for each streams */
- writel(block_addr, mmu_hw->base +
- mmu_hw->l1_stream_id_reg_offset + 4 * j);
- }
-
- /* Configure MMU TLB stream configuration for L2 */
- for (j = 0, block_addr = 0; j < mmu_hw->nr_l2streams;
- block_addr += mmu->mmu_hw[i].l2_block_sz[j], j++) {
- if (block_addr > IPU6_MAX_L2_BLOCK_ADDR) {
- dev_err(mmu->dev, "invalid L2 configuration\n");
- return -EINVAL;
- }
+ int ret;
- writel(block_addr, mmu_hw->base +
- mmu_hw->l2_stream_id_reg_offset + 4 * j);
- }
- }
+ ret = mmu->ops->hw_init(mmu);
+ if (ret)
+ return ret;
if (!mmu->trash_page) {
- int ret;
-
mmu->trash_page = alloc_page(GFP_KERNEL);
if (!mmu->trash_page) {
dev_err(mmu->dev, "insufficient memory for trash buffer\n");
@@ -553,7 +473,12 @@ static struct ipu6_mmu_info *ipu6_mmu_alloc(struct ipu6_device *isp)
if (!mmu_info)
return NULL;
- mmu_info->aperture_start = 0;
+ if (IS_IPU7(isp))
+ mmu_info->aperture_start = isp->secure_mode ?
+ IPU7_FW_CODE_REGION_END : IPU7_FW_CODE_REGION_START;
+ else
+ mmu_info->aperture_start = 0;
+
mmu_info->aperture_end =
(dma_addr_t)DMA_BIT_MASK(isp->secure_mode ?
IPU6_MMU_ADDR_BITS :
@@ -612,6 +537,7 @@ EXPORT_SYMBOL_NS_GPL(ipu6_mmu_hw_cleanup, "INTEL_IPU6");
static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp)
{
struct ipu6_dma_mapping *dmap;
+ unsigned long base_pfn;
dmap = kzalloc_obj(*dmap);
if (!dmap)
@@ -623,7 +549,9 @@ static struct ipu6_dma_mapping *alloc_dma_mapping(struct ipu6_device *isp)
return NULL;
}
- init_iova_domain(&dmap->iovad, SZ_4K, 1);
+ base_pfn = max_t(unsigned long, 1,
+ PFN_DOWN(dmap->mmu_info->aperture_start));
+ init_iova_domain(&dmap->iovad, SZ_4K, base_pfn);
dmap->mmu_info->dmap = dmap;
dev_dbg(&isp->pdev->dev, "alloc mapping\n");
@@ -746,45 +674,26 @@ static void ipu6_mmu_destroy(struct ipu6_mmu *mmu)
}
struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
- void __iomem *base, int mmid,
- const struct ipu6_hw_variants *hw)
+ void __iomem *base, int mmid)
{
struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev));
- struct ipu6_mmu_pdata *pdata;
struct ipu6_mmu *mmu;
- unsigned int i;
-
- if (hw->nr_mmus > IPU6_MMU_MAX_DEVICES)
- return ERR_PTR(-EINVAL);
-
- pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return ERR_PTR(-ENOMEM);
-
- for (i = 0; i < hw->nr_mmus; i++) {
- struct ipu6_mmu_hw *pdata_mmu = &pdata->mmu_hw[i];
- const struct ipu6_mmu_hw *src_mmu = &hw->mmu_hw[i];
-
- if (src_mmu->nr_l1streams > IPU6_MMU_MAX_TLB_L1_STREAMS ||
- src_mmu->nr_l2streams > IPU6_MMU_MAX_TLB_L2_STREAMS)
- return ERR_PTR(-EINVAL);
-
- *pdata_mmu = *src_mmu;
- pdata_mmu->base = base + src_mmu->offset;
- }
+ int ret;
mmu = devm_kzalloc(dev, sizeof(*mmu), GFP_KERNEL);
if (!mmu)
return ERR_PTR(-ENOMEM);
+ mmu->ops = IS_IPU7(isp) ? &ipu7_mmu_ops : &ipu6_mmu_ops;
mmu->mmid = mmid;
- mmu->mmu_hw = pdata->mmu_hw;
- mmu->nr_mmus = hw->nr_mmus;
- mmu->tlb_invalidate = tlb_invalidate;
mmu->ready = false;
INIT_LIST_HEAD(&mmu->vma_list);
spin_lock_init(&mmu->ready_lock);
+ ret = mmu->ops->init_hw_data(mmu, dev, base);
+ if (ret)
+ return ERR_PTR(ret);
+
mmu->dmap = alloc_dma_mapping(isp);
if (!mmu->dmap) {
dev_err(dev, "can't alloc dma mapping\n");
diff --git a/drivers/media/pci/intel/ipu6/ipu6-mmu.h b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
index 8e40b4a66d7d..44880478d242 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-mmu.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-mmu.h
@@ -1,19 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0-only */
-/* Copyright (C) 2013--2024 Intel Corporation */
+/* Copyright (C) 2013--2026 Intel Corporation */
#ifndef IPU6_MMU_H
#define IPU6_MMU_H
-#define ISYS_MMID 1
-#define PSYS_MMID 0
-
#include <linux/list.h>
#include <linux/spinlock_types.h>
#include <linux/types.h>
+#include "ipu7-mmu-hw.h"
+
struct device;
struct page;
-struct ipu6_hw_variants;
struct ipu6_mmu_info {
struct device *dev;
@@ -35,10 +33,150 @@ struct ipu6_mmu_info {
struct ipu6_dma_mapping *dmap;
};
+/*
+ * MMU Invalidation HW bug workaround by ZLW mechanism
+ *
+ * Old IPU6 MMUV2 has a bug in the invalidation mechanism which might result in
+ * wrong translation or replication of the translation. This will cause data
+ * corruption. So we cannot directly use the MMU V2 invalidation registers
+ * to invalidate the MMU. Instead, whenever an invalidate is called, we need to
+ * clear the TLB by evicting all the valid translations by filling it with trash
+ * buffer (which is guaranteed not to be used by any other processes). ZLW is
+ * used to fill the L1 and L2 caches with the trash buffer translations. ZLW
+ * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in
+ * advance to the L1 and L2 caches without triggering any memory operations.
+ *
+ * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream
+ * One L1 block has 16 entries, hence points to 16 * 4K pages
+ * L2 -> 16 streams and 32 blocks. 2 blocks per streams
+ * One L2 block maps to 1024 L1 entries, hence points to 4MB address range
+ * 2 blocks per L2 stream means, 1 stream points to 8MB range
+ *
+ * As we need to clear the caches and 8MB being the biggest cache size, we need
+ * to have trash buffer which points to 8MB address range. As these trash
+ * buffers are not used for any memory transactions, we need only the least
+ * amount of physical memory. So we reserve 8MB IOVA address range but only
+ * one page is reserved from physical memory. Each of this 8MB IOVA address
+ * range is then mapped to the same physical memory page.
+ */
+/* One L2 entry maps 1024 L1 entries and one L1 entry per page */
+#define IPU6_MMUV2_L2_RANGE (1024 * PAGE_SIZE)
+/* Max L2 blocks per stream */
+#define IPU6_MMUV2_MAX_L2_BLOCKS 2
+/* Max L1 blocks per stream */
+#define IPU6_MMUV2_MAX_L1_BLOCKS 16
+#define IPU6_MMUV2_TRASH_RANGE (IPU6_MMUV2_L2_RANGE * IPU6_MMUV2_MAX_L2_BLOCKS)
+/* Entries per L1 block */
+#define MMUV2_ENTRIES_PER_L1_BLOCK 16
+#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE)
+#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU6_MMUV2_L2_RANGE
+
+/*
+ * In some of the IPU6 MMUs, there is provision to configure L1 and L2 page
+ * table caches. Both these L1 and L2 caches are divided into multiple sections
+ * called streams. There is maximum 16 streams for both caches. Each of these
+ * sections are subdivided into multiple blocks. When nr_l1streams = 0 and
+ * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support
+ * L1/L2 page table caches.
+ *
+ * L1 stream per block sizes are configurable and varies per usecase.
+ * L2 has constant block sizes - 2 blocks per stream.
+ *
+ * MMU1 support pre-fetching of the pages to have less cache lookup misses. To
+ * enable the pre-fetching, MMU1 AT (Address Translator) device registers
+ * need to be configured.
+ *
+ * There are four types of memory accesses which requires ZLW configuration.
+ * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU.
+ *
+ * 1. Sequential Access or 1D mode
+ * Set ZLW_EN -> 1
+ * set ZLW_PAGE_CROSS_1D -> 1
+ * Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where
+ * N is pre-defined and hardcoded in the platform data
+ * Set ZLW_2D -> 0
+ *
+ * 2. ZLW 2D mode
+ * Set ZLW_EN -> 1
+ * set ZLW_PAGE_CROSS_1D -> 1,
+ * Set ZLW_N -> 0
+ * Set ZLW_2D -> 1
+ *
+ * 3. ZLW Enable (no 1D or 2D mode)
+ * Set ZLW_EN -> 1
+ * set ZLW_PAGE_CROSS_1D -> 0,
+ * Set ZLW_N -> 0
+ * Set ZLW_2D -> 0
+ *
+ * 4. ZLW disable
+ * Set ZLW_EN -> 0
+ * set ZLW_PAGE_CROSS_1D -> 0,
+ * Set ZLW_N -> 0
+ * Set ZLW_2D -> 0
+ *
+ * To configure the ZLW for the above memory access, four registers are
+ * available. Hence to track these four settings, we have the following entries
+ * in the struct ipu6_mmu_hw. Each of these entries are per stream and
+ * available only for the L1 streams.
+ *
+ * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN)
+ * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary
+ * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted
+ * Insert ZLW request N pages ahead address.
+ * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D)
+ *
+ *
+ * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined
+ * as per the usecase specific calculations. Any change to this pre-defined
+ * table has to happen in sync with IPU6 FW.
+ */
+
+struct ipu6_mmu_hw {
+ union {
+ unsigned long offset;
+ void __iomem *base;
+ };
+ u32 info_bits;
+ u8 nr_l1streams;
+ /*
+ * L1 has variable blocks per stream - total of 64 blocks and maximum of
+ * 16 blocks per stream. Configurable by using the block start address
+ * per stream. Block start address is calculated from the block size
+ */
+ u8 l1_block_sz[IPU6_MMU_MAX_TLB_L1_STREAMS];
+ /* Is ZLW is enabled in each stream */
+ bool l1_zlw_en[IPU6_MMU_MAX_TLB_L1_STREAMS];
+ bool l1_zlw_1d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
+ u8 l1_ins_zlw_ahead_pages[IPU6_MMU_MAX_TLB_L1_STREAMS];
+ bool l1_zlw_2d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
+
+ u32 l1_stream_id_reg_offset;
+ u32 l2_stream_id_reg_offset;
+
+ u8 nr_l2streams;
+ /*
+ * L2 has fixed 2 blocks per stream. Block address is calculated
+ * from the block size
+ */
+ u8 l2_block_sz[IPU6_MMU_MAX_TLB_L2_STREAMS];
+ /* flag to track if WA is needed for successive invalidate HW bug */
+ bool insert_read_before_invalidate;
+};
+
+struct ipu6_mmu_hw_ops {
+ int (*init_hw_data)(struct ipu6_mmu *mmu, struct device *dev,
+ void __iomem *base);
+ int (*hw_init)(struct ipu6_mmu *mmu);
+ void (*tlb_invalidate)(struct ipu6_mmu *mmu);
+};
+
struct ipu6_mmu {
struct list_head node;
- struct ipu6_mmu_hw *mmu_hw;
+ union {
+ struct ipu6_mmu_hw *ipu6_mmu_hw;
+ struct ipu7_mmu_hw *ipu7_mmu_hw;
+ };
unsigned int nr_mmus;
unsigned int mmid;
@@ -55,12 +193,14 @@ struct ipu6_mmu {
bool ready;
spinlock_t ready_lock; /* Serialize access to bool ready */
- void (*tlb_invalidate)(struct ipu6_mmu *mmu);
+ const struct ipu6_mmu_hw_ops *ops;
};
+extern const struct ipu6_mmu_hw_ops ipu6_mmu_ops;
+extern const struct ipu6_mmu_hw_ops ipu7_mmu_ops;
+
struct ipu6_mmu *ipu6_mmu_init(struct device *dev,
- void __iomem *base, int mmid,
- const struct ipu6_hw_variants *hw);
+ void __iomem *base, int mmid);
void ipu6_mmu_cleanup(struct ipu6_mmu *mmu);
int ipu6_mmu_hw_init(struct ipu6_mmu *mmu);
void ipu6_mmu_hw_cleanup(struct ipu6_mmu *mmu);
diff --git a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
index efd65e494c16..57e661cc2177 100644
--- a/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
+++ b/drivers/media/pci/intel/ipu6/ipu6-platform-buttress-regs.h
@@ -212,7 +212,7 @@ enum {
#define BUTTRESS_TSW_CTL_SOFT_RESET BIT(8)
#define BUTTRESS_REG_TSC_LO 0x164
-#define BUTTRESS_REG_TSC_HI 0x168
+#define BUTTRESS_TSC_HI_OFFSET 4
#define BUTTRESS_IRQS (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \
@@ -221,4 +221,113 @@ enum {
#define BUTTRESS_EVENT (BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING | \
BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE | \
BUTTRESS_ISR_SAI_VIOLATION)
+
+/* IPU7 */
+
+/* IPU7 buttress register offsets */
+#define IPU7_BUTTRESS_REG_SKU 0x3030
+#define IPU7_BUTTRESS_REG_IRQ_STATUS 0x2000
+#define IPU7_BUTTRESS_REG_IRQ_ENABLE 0x2008
+#define IPU7_BUTTRESS_REG_IRQ_CLEAR 0x200c
+#define IPU7_BUTTRESS_REG_IRQ_MASK 0x2010
+#define IPU7_BUTTRESS_REG_TSC_CMD 0x2014
+#define IPU7_BUTTRESS_REG_TSC_CTL 0x2018
+#define IPU7_BUTTRESS_REG_TSC_LO 0x201c
+#define IPU7_BUTTRESS_REG_PB_TIMESTAMP_LO 0x2030
+#define IPU7_BUTTRESS_REG_PB_TIMESTAMP_VALID 0x2038
+#define IPU7_BUTTRESS_REG_IS_WORKPOINT_REQ 0x2104
+#define IPU7_BUTTRESS_REG_PS_WORKPOINT_REQ 0x2100
+#define IPU7_BUTTRESS_REG_IDLE_WDT 0x218c
+#define IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS 0x2200
+#define IPU7_BUTTRESS_REG_ISYS_UCX_START_ADDR 0x2204
+#define IPU7_BUTTRESS_REG_PSYS_UCX_CTRL_STATUS 0x2208
+#define IPU7_BUTTRESS_REG_PSYS_UCX_START_ADDR 0x220c
+#define IPU7_BUTTRESS_REG_SECURITY_CTL 0x2318
+#define IPU7_BUTTRESS_REG_FW_RESET_CTL 0x2334
+#define IPU7_BUTTRESS_REG_FW_SOURCE_SIZE 0x2338
+#define IPU7_BUTTRESS_REG_FW_SOURCE_BASE 0x233c
+#define IPU7_BUTTRESS_REG_CSE2IUDB0 0x2500
+#define IPU7_BUTTRESS_REG_CSE2IUDATA0 0x2504
+#define IPU7_BUTTRESS_REG_CSE2IUCSR 0x2508
+#define IPU7_BUTTRESS_REG_IU2CSEDB0 0x250c
+#define IPU7_BUTTRESS_REG_IU2CSEDATA0 0x2510
+#define IPU7_BUTTRESS_REG_IU2CSECSR 0x2514
+#define IPU7_BUTTRESS_REG_CG_CTRL_BITS 0x3014
+#define IPU7_BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000
+#define IPU7_BUTTRESS_REG_FW_BOOT_PARAMS7 0x401c
+#define IPU7_BUTTRESS_REG_PWR_STATUS 0x2114
+
+#define IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE BIT(0)
+#define IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING BIT(1)
+#define IPU7_BUTTRESS_IRQ_CSE_CSR_SET BIT(2)
+#define IPU7_BUTTRESS_IRQ_SAI_VIOLATION BIT(4)
+#define IPU7_BUTTRESS_IRQ_IS_IRQ BIT(30)
+#define IPU7_BUTTRESS_IRQ_PS_IRQ BIT(31)
+
+#define IPU7_BUTTRESS_IRQS (IPU7_BUTTRESS_IRQ_IS_IRQ | \
+ IPU7_BUTTRESS_IRQ_PS_IRQ | \
+ IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING | \
+ IPU7_BUTTRESS_IRQ_CSE_CSR_SET | \
+ IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE)
+
+/* LNL SW workaround for PS PD hang */
+#define IPU7_BUTTRESS_CG_CTRL_PS_FSM_CG BIT(3)
+
+/* P-unit Bridge (PB) BAR registers */
+#define IPU7_PB_INTERRUPT_STATUS 0x0
+
+/* Frequency control encoding */
+#define IPU7_FREQ_CTL_CDYN 0x80
+#define IPU7_FREQ_CTL_CDYN_SHIFT 8
+#define IPU7_BUTTRESS_IS_FREQ_CTL_RATIO_MASK GENMASK(7, 0)
+#define IPU7_IS_FREQ_CTL_DEFAULT_RATIO 0x1b
+#define IPU7_PS_FREQ_CTL_DEFAULT_RATIO 0x14
+
+/* D2D power control */
+#define IPU7_BUTTRESS_REG_D2D_CTL 0x21d4
+#define IPU7_BUTTRESS_D2D_PWR_EN BIT(0)
+#define IPU7_BUTTRESS_D2D_PWR_ACK BIT(4)
+
+#define IPU7_BUTTRESS_PWR_STATE_IS_PWR_SHIFT 0
+#define IPU7_BUTTRESS_PWR_STATE_IS_PWR_MASK (0x3U << 0)
+#define IPU7_BUTTRESS_PWR_STATE_PS_PWR_SHIFT 4
+#define IPU7_BUTTRESS_PWR_STATE_PS_PWR_MASK (0x3U << 4)
+
+/* NDE */
+#define IPU7_BUTTRESS_REG_NDE_CONTROL 0x21a4
+#define IPU7_NDE_VAL_MASK GENMASK(9, 0)
+#define IPU7_NDE_SCALE_MASK GENMASK(12, 10)
+#define IPU7_NDE_VALID_MASK BIT(13)
+#define IPU7_NDE_RESVEC_MASK GENMASK(19, 16)
+#define IPU7_NDE_VAL_ACTIVE 48
+#define IPU7_NDE_SCALE_ACTIVE 2
+#define IPU7_NDE_VALID_ACTIVE 1
+#define IPU7_NDE_VAL_DEFAULT 1023
+#define IPU7_NDE_SCALE_DEFAULT 2
+#define IPU7_NDE_VALID_DEFAULT 0
+#define IPU7_NDE_RESVEC 0xe
+
+/* IS UCX control */
+#define IPU7_UCX_CTL_RESET BIT(0)
+#define IPU7_UCX_CTL_RUN BIT(1)
+#define IPU7_UCX_CTL_WAKEUP BIT(2)
+
+#define IPU7_BUTTRESS_REG_SLEEP_LEVEL_CFG 0x21b0
+#define IPU7_BUTTRESS_REG_SLEEP_LEVEL_STS 0x21b4
+#define IPU7_BUTTRESS_OVERRIDE_IS_CLK BIT(1)
+#define IPU7_BUTTRESS_OWN_ACK_IS_CLK BIT(9)
+#define IPU7_BUTTRESS_OVERRIDE_PS_CLK BIT(2)
+#define IPU7_BUTTRESS_OWN_ACK_PS_CLK BIT(10)
+
+/* PB registers */
+#define IPU7_GLOBAL_INTERRUPT_MASK 0x8
+#define IPU7_TLBID_HASH_ENABLE_31_0 0x30
+#define IPU7_TLBID_HASH_ENABLE_63_32 0x34
+#define IPU7_TLBID_HASH_ENABLE_95_64 0x38
+#define IPU7_TLBID_HASH_ENABLE_127_96 0x3c
+#define IPU7_BAR2_MISC_CONFIG 0x64
+
+/* IPU7.5 */
+#define IPU7_BUTTRESS_SEL_PB_TIMESTAMP BIT(9)
+
#endif /* IPU6_PLATFORM_BUTTRESS_REGS_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu6.c b/drivers/media/pci/intel/ipu6/ipu6.c
index 5449a2006bcc..43d951735f72 100644
--- a/drivers/media/pci/intel/ipu6/ipu6.c
+++ b/drivers/media/pci/intel/ipu6/ipu6.c
@@ -19,6 +19,7 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/vmalloc.h>
#include <media/ipu-bridge.h>
#include <media/ipu6-pci-table.h>
@@ -27,13 +28,20 @@
#include "ipu6-bus.h"
#include "ipu6-buttress.h"
#include "ipu6-cpd.h"
+#include "ipu6-dma.h"
#include "ipu6-isys.h"
#include "ipu6-mmu.h"
#include "ipu6-platform-buttress-regs.h"
#include "ipu6-platform-isys-csi2-reg.h"
#include "ipu6-platform-regs.h"
+#include "ipu7-isys-csi2-regs.h"
#define IPU6_PCI_BAR 0
+#define IPU7_PCI_PBBAR 4
+
+static int force_no_probe_ipu7 = !IS_BUILTIN(CONFIG_VIDEO_INTEL_IPU6_IPU7);
+module_param(force_no_probe_ipu7, int, 0644);
+MODULE_PARM_DESC(force_no_probe_ipu7, "Don't probe ipu7 and ipu7.5 devices");
struct ipu6_cell_program {
u32 magic_number;
@@ -73,54 +81,6 @@ struct ipu6_cell_program {
static struct ipu6_isys_internal_pdata isys_ipdata = {
.hw_variant = {
.offset = IPU6_UNIFIED_OFFSET,
- .nr_mmus = 3,
- .mmu_hw = {
- {
- .offset = IPU6_ISYS_IOMMU0_OFFSET,
- .info_bits = IPU6_INFO_REQUEST_DESTINATION_IOSF,
- .nr_l1streams = 16,
- .l1_block_sz = {
- 3, 8, 2, 2, 2, 2, 2, 2, 1, 1,
- 1, 1, 1, 1, 1, 1
- },
- .nr_l2streams = 16,
- .l2_block_sz = {
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2
- },
- .insert_read_before_invalidate = false,
- .l1_stream_id_reg_offset =
- IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
- .l2_stream_id_reg_offset =
- IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
- },
- {
- .offset = IPU6_ISYS_IOMMU1_OFFSET,
- .info_bits = 0,
- .nr_l1streams = 16,
- .l1_block_sz = {
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 1, 1, 4
- },
- .nr_l2streams = 16,
- .l2_block_sz = {
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2
- },
- .insert_read_before_invalidate = false,
- .l1_stream_id_reg_offset =
- IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
- .l2_stream_id_reg_offset =
- IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
- },
- {
- .offset = IPU6_ISYS_IOMMUI_OFFSET,
- .info_bits = 0,
- .nr_l1streams = 0,
- .nr_l2streams = 0,
- .insert_read_before_invalidate = false,
- },
- },
.cdc_fifos = 3,
.cdc_fifo_threshold = {6, 8, 2},
.dmem_offset = IPU6_ISYS_DMEM_OFFSET,
@@ -132,83 +92,12 @@ static struct ipu6_isys_internal_pdata isys_ipdata = {
static struct ipu6_psys_internal_pdata psys_ipdata = {
.hw_variant = {
.offset = IPU6_UNIFIED_OFFSET,
- .nr_mmus = 4,
- .mmu_hw = {
- {
- .offset = IPU6_PSYS_IOMMU0_OFFSET,
- .info_bits =
- IPU6_INFO_REQUEST_DESTINATION_IOSF,
- .nr_l1streams = 16,
- .l1_block_sz = {
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2
- },
- .nr_l2streams = 16,
- .l2_block_sz = {
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2
- },
- .insert_read_before_invalidate = false,
- .l1_stream_id_reg_offset =
- IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
- .l2_stream_id_reg_offset =
- IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
- },
- {
- .offset = IPU6_PSYS_IOMMU1_OFFSET,
- .info_bits = 0,
- .nr_l1streams = 32,
- .l1_block_sz = {
- 1, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 10,
- 5, 4, 14, 6, 4, 14, 6, 4, 8,
- 4, 2, 1, 1, 1, 1, 14
- },
- .nr_l2streams = 32,
- .l2_block_sz = {
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2
- },
- .insert_read_before_invalidate = false,
- .l1_stream_id_reg_offset =
- IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
- .l2_stream_id_reg_offset =
- IPU6_PSYS_MMU1W_L2_STREAM_ID_REG_OFFSET,
- },
- {
- .offset = IPU6_PSYS_IOMMU1R_OFFSET,
- .info_bits = 0,
- .nr_l1streams = 16,
- .l1_block_sz = {
- 1, 4, 4, 4, 4, 16, 8, 4, 32,
- 16, 16, 2, 2, 2, 1, 12
- },
- .nr_l2streams = 16,
- .l2_block_sz = {
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2
- },
- .insert_read_before_invalidate = false,
- .l1_stream_id_reg_offset =
- IPU6_MMU_L1_STREAM_ID_REG_OFFSET,
- .l2_stream_id_reg_offset =
- IPU6_MMU_L2_STREAM_ID_REG_OFFSET,
- },
- {
- .offset = IPU6_PSYS_IOMMUI_OFFSET,
- .info_bits = 0,
- .nr_l1streams = 0,
- .nr_l2streams = 0,
- .insert_read_before_invalidate = false,
- },
- },
.dmem_offset = IPU6_PSYS_DMEM_OFFSET,
},
};
-static const struct ipu6_buttress_ctrl isys_buttress_ctrl = {
+static const struct ipu6_buttress_ctrl ipu6_isys_buttress_ctrl = {
+ .subsys_id = IPU_ISYS,
.ratio = IPU6_IS_FREQ_CTL_DEFAULT_RATIO,
.qos_floor = IPU6_IS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO,
.freq_ctl = IPU6_BUTTRESS_REG_IS_FREQ_CTL,
@@ -218,7 +107,8 @@ static const struct ipu6_buttress_ctrl isys_buttress_ctrl = {
.pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
};
-static const struct ipu6_buttress_ctrl psys_buttress_ctrl = {
+static const struct ipu6_buttress_ctrl ipu6_psys_buttress_ctrl = {
+ .subsys_id = IPU_PSYS,
.ratio = IPU6_PS_FREQ_CTL_DEFAULT_RATIO,
.qos_floor = IPU6_PS_FREQ_CTL_DEFAULT_QOS_FLOOR_RATIO,
.freq_ctl = IPU6_BUTTRESS_REG_PS_FREQ_CTL,
@@ -228,6 +118,119 @@ static const struct ipu6_buttress_ctrl psys_buttress_ctrl = {
.pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
};
+static const struct ipu6_buttress_ctrl ipu7_isys_buttress_ctrl = {
+ .subsys_id = IPU_ISYS,
+ .ratio = IPU7_IS_FREQ_CTL_DEFAULT_RATIO,
+ .qos_floor = 0,
+ .freq_ctl = IPU7_BUTTRESS_REG_IS_WORKPOINT_REQ,
+ .pwr_sts_shift = IPU7_BUTTRESS_PWR_STATE_IS_PWR_SHIFT,
+ .pwr_sts_mask = IPU7_BUTTRESS_PWR_STATE_IS_PWR_MASK,
+ .pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE,
+ .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
+};
+
+static const struct ipu6_buttress_ctrl ipu7_psys_buttress_ctrl = {
+ .subsys_id = IPU_PSYS,
+ .ratio = IPU7_PS_FREQ_CTL_DEFAULT_RATIO,
+ .qos_floor = 0,
+ .freq_ctl = IPU7_BUTTRESS_REG_PS_WORKPOINT_REQ,
+ .pwr_sts_shift = IPU7_BUTTRESS_PWR_STATE_PS_PWR_SHIFT,
+ .pwr_sts_mask = IPU7_BUTTRESS_PWR_STATE_PS_PWR_MASK,
+ .pwr_sts_on = IPU6_BUTTRESS_PWR_STATE_UP_DONE,
+ .pwr_sts_off = IPU6_BUTTRESS_PWR_STATE_DN_DONE,
+};
+
+static const struct ipu6_buttress_registers ipu6_buttress_regs = {
+ /* Registers */
+ .irq_status = BUTTRESS_REG_ISR_STATUS,
+ .irq_clear = BUTTRESS_REG_ISR_CLEAR,
+ .irq_enable = BUTTRESS_REG_ISR_ENABLE,
+ .pwr_status = BUTTRESS_REG_PWR_STATE,
+ .security_ctl = BUTTRESS_REG_SECURITY_CTL,
+ .fw_reset_ctl = BUTTRESS_REG_FW_RESET_CTL,
+ .fabric_cmd = BUTTRESS_REG_FABRIC_CMD,
+ .tsw_ctl = BUTTRESS_REG_TSW_CTL,
+ .tsc_lo = BUTTRESS_REG_TSC_LO,
+ .wdt = BUTTRESS_REG_WDT,
+ .btrs_ctrl = BUTTRESS_REG_BTRS_CTRL,
+ .csr_in = BUTTRESS_REG_CSE2IUCSR,
+ .csr_out = BUTTRESS_REG_IU2CSECSR,
+ .db0_in = BUTTRESS_REG_CSE2IUDB0,
+ .db0_out = BUTTRESS_REG_IU2CSEDB0,
+ .data0_in = BUTTRESS_REG_CSE2IUDATA0,
+ .data0_out = BUTTRESS_REG_IU2CSEDATA0,
+ .sku_id = BUTTRESS_REG_SKU,
+
+ /* Bitmasks */
+ .irq_is = BUTTRESS_ISR_IS_IRQ,
+ .irq_ps = BUTTRESS_ISR_PS_IRQ,
+ .irq_all = BUTTRESS_IRQS,
+ .irq_events = BUTTRESS_EVENT,
+ .irq_cse_ipc = BUTTRESS_ISR_IPC_FROM_CSE_IS_WAITING,
+ .irq_exec_done = BUTTRESS_ISR_IPC_EXEC_DONE_BY_CSE,
+ .irq_sai = BUTTRESS_ISR_SAI_VIOLATION,
+};
+
+static const struct ipu6_buttress_registers ipu7_buttress_regs = {
+ /* Registers */
+ .irq_status = IPU7_BUTTRESS_REG_IRQ_STATUS,
+ .irq_clear = IPU7_BUTTRESS_REG_IRQ_CLEAR,
+ .irq_enable = IPU7_BUTTRESS_REG_IRQ_ENABLE,
+ .pwr_status = IPU7_BUTTRESS_REG_PWR_STATUS,
+ .security_ctl = IPU7_BUTTRESS_REG_SECURITY_CTL,
+ .fw_reset_ctl = IPU7_BUTTRESS_REG_FW_RESET_CTL,
+ .fabric_cmd = IPU7_BUTTRESS_REG_TSC_CMD,
+ .tsw_ctl = IPU7_BUTTRESS_REG_TSC_CTL,
+ .tsc_lo = IPU7_BUTTRESS_REG_TSC_LO,
+ .wdt = IPU7_BUTTRESS_REG_IDLE_WDT,
+ .csr_in = IPU7_BUTTRESS_REG_CSE2IUCSR,
+ .csr_out = IPU7_BUTTRESS_REG_IU2CSECSR,
+ .db0_in = IPU7_BUTTRESS_REG_CSE2IUDB0,
+ .db0_out = IPU7_BUTTRESS_REG_IU2CSEDB0,
+ .data0_in = IPU7_BUTTRESS_REG_CSE2IUDATA0,
+ .data0_out = IPU7_BUTTRESS_REG_IU2CSEDATA0,
+ .sku_id = IPU7_BUTTRESS_REG_SKU,
+
+ /* Bitmasks */
+ .irq_is = IPU7_BUTTRESS_IRQ_IS_IRQ,
+ .irq_ps = IPU7_BUTTRESS_IRQ_PS_IRQ,
+ .irq_all = IPU7_BUTTRESS_IRQS,
+ .irq_events = IPU7_BUTTRESS_IRQS,
+ .irq_cse_ipc = IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING,
+ .irq_exec_done = IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE,
+ .irq_sai = IPU7_BUTTRESS_IRQ_SAI_VIOLATION,
+};
+
+static const struct ipu6_buttress_registers ipu7p5_buttress_regs = {
+ /* Registers */
+ .irq_status = IPU7_BUTTRESS_REG_IRQ_STATUS,
+ .irq_clear = IPU7_BUTTRESS_REG_IRQ_CLEAR,
+ .irq_enable = IPU7_BUTTRESS_REG_IRQ_ENABLE,
+ .pwr_status = IPU7_BUTTRESS_REG_PWR_STATUS,
+ .security_ctl = IPU7_BUTTRESS_REG_SECURITY_CTL,
+ .fw_reset_ctl = IPU7_BUTTRESS_REG_FW_RESET_CTL,
+ .fabric_cmd = IPU7_BUTTRESS_REG_TSC_CMD,
+ .tsw_ctl = IPU7_BUTTRESS_REG_TSC_CTL,
+ .tsc_lo = IPU7_BUTTRESS_REG_PB_TIMESTAMP_LO,
+ .wdt = IPU7_BUTTRESS_REG_IDLE_WDT,
+ .csr_in = IPU7_BUTTRESS_REG_CSE2IUCSR,
+ .csr_out = IPU7_BUTTRESS_REG_IU2CSECSR,
+ .db0_in = IPU7_BUTTRESS_REG_CSE2IUDB0,
+ .db0_out = IPU7_BUTTRESS_REG_IU2CSEDB0,
+ .data0_in = IPU7_BUTTRESS_REG_CSE2IUDATA0,
+ .data0_out = IPU7_BUTTRESS_REG_IU2CSEDATA0,
+ .sku_id = IPU7_BUTTRESS_REG_SKU,
+
+ /* Bitmasks */
+ .irq_is = IPU7_BUTTRESS_IRQ_IS_IRQ,
+ .irq_ps = IPU7_BUTTRESS_IRQ_PS_IRQ,
+ .irq_all = IPU7_BUTTRESS_IRQS,
+ .irq_events = IPU7_BUTTRESS_IRQS,
+ .irq_cse_ipc = IPU7_BUTTRESS_IRQ_IPC_FROM_CSE_IS_WAITING,
+ .irq_exec_done = IPU7_BUTTRESS_IRQ_IPC_EXEC_DONE_BY_CSE,
+ .irq_sai = IPU7_BUTTRESS_IRQ_SAI_VIOLATION,
+};
+
static void
ipu6_pkg_dir_configure_spc(struct ipu6_device *isp,
const struct ipu6_hw_variants *hw_variant,
@@ -267,10 +270,16 @@ void ipu6_configure_spc(struct ipu6_device *isp,
int pkg_dir_idx, void __iomem *base, u64 *pkg_dir,
dma_addr_t pkg_dir_dma_addr)
{
- void __iomem *dmem_base = base + hw_variant->dmem_offset;
- void __iomem *spc_regs_base = base + hw_variant->spc_offset;
+ void __iomem *dmem_base;
+ void __iomem *spc_regs_base;
u32 val;
+ if (IS_IPU7(isp))
+ return;
+
+ dmem_base = base + hw_variant->dmem_offset;
+ spc_regs_base = base + hw_variant->spc_offset;
+
val = readl(spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL);
val |= IPU6_PSYS_SPC_STATUS_CTRL_ICACHE_INVALIDATE;
writel(val, spc_regs_base + IPU6_PSYS_REG_SPC_STATUS_CTRL);
@@ -290,8 +299,6 @@ EXPORT_SYMBOL_NS_GPL(ipu6_configure_spc, "INTEL_IPU6");
static void ipu6_internal_pdata_init(struct ipu6_device *isp)
{
- u8 hw_ver = isp->hw_ver;
-
isys_ipdata.num_parallel_streams = IPU6_ISYS_NUM_STREAMS;
isys_ipdata.sram_gran_shift = IPU6_SRAM_GRANULARITY_SHIFT;
isys_ipdata.sram_gran_size = IPU6_SRAM_GRANULARITY_SIZE;
@@ -314,19 +321,19 @@ static void ipu6_internal_pdata_init(struct ipu6_device *isp)
IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_STATUS;
isys_ipdata.csi2.ctrl0_irq_lnp =
IPU6_REG_ISYS_CSI_TOP_CTRL0_IRQ_LEVEL_NOT_PULSE;
- isys_ipdata.enhanced_iwake = is_ipu6ep_mtl(hw_ver) || is_ipu6ep(hw_ver);
+ isys_ipdata.enhanced_iwake = IS_IPU6EP_MTL(isp) || IS_IPU6EP(isp);
psys_ipdata.hw_variant.spc_offset = IPU6_PSYS_SPC_OFFSET;
isys_ipdata.csi2.fw_access_port_ofs = CSI_REG_HUB_FW_ACCESS_PORT_OFS;
- if (is_ipu6ep(hw_ver)) {
+ if (IS_IPU6EP(isp)) {
isys_ipdata.ltr = IPU6EP_LTR_VALUE;
isys_ipdata.memopen_threshold = IPU6EP_MIN_MEMOPEN_TH;
}
- if (is_ipu6_tgl(hw_ver))
+ if (IS_IPU6_TGL(isp))
isys_ipdata.csi2.nports = IPU6_TGL_ISYS_CSI2_NPORTS;
- if (is_ipu6ep_mtl(hw_ver)) {
+ if (IS_IPU6EP_MTL(isp)) {
isys_ipdata.csi2.nports = IPU6EP_MTL_ISYS_CSI2_NPORTS;
isys_ipdata.csi2.ctrl0_irq_edge =
@@ -347,7 +354,7 @@ static void ipu6_internal_pdata_init(struct ipu6_device *isp)
isys_ipdata.memopen_threshold = IPU6EP_MTL_MIN_MEMOPEN_TH;
}
- if (is_ipu6se(hw_ver)) {
+ if (IS_IPU6SE(isp)) {
isys_ipdata.csi2.nports = IPU6SE_ISYS_CSI2_NPORTS;
isys_ipdata.csi2.irq_mask = IPU6SE_CSI_RX_ERROR_IRQ_MASK;
isys_ipdata.num_parallel_streams = IPU6SE_ISYS_NUM_STREAMS;
@@ -363,15 +370,21 @@ static void ipu6_internal_pdata_init(struct ipu6_device *isp)
isys_ipdata.max_devq_size = IPU6SE_DEV_SEND_QUEUE_SIZE;
psys_ipdata.hw_variant.spc_offset = IPU6SE_PSYS_SPC_OFFSET;
}
+
+ if (IS_IPU7(isp)) {
+ isys_ipdata.csi2.gpreg = IPU7_IS_IO_CSI2_GPREGS_BASE;
+ isys_ipdata.csi2.nports = 4;
+ }
}
static struct ipu6_bus_device *
ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
- struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
+ const struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
const struct ipu6_isys_internal_pdata *ipdata)
{
struct device *dev = &pdev->dev;
struct ipu6_bus_device *isys_adev;
+ struct ipu6_buttress_ctrl *devm_ctrl;
struct ipu6_isys_pdata *pdata;
int ret;
@@ -381,6 +394,10 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
return ERR_PTR(ret);
}
+ devm_ctrl = devm_kmemdup(dev, ctrl, sizeof(*ctrl), GFP_KERNEL);
+ if (!devm_ctrl)
+ return ERR_PTR(-ENOMEM);
+
pdata = kzalloc_obj(*pdata);
if (!pdata)
return ERR_PTR(-ENOMEM);
@@ -388,7 +405,7 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
pdata->base = base;
pdata->ipdata = ipdata;
- isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
+ isys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, devm_ctrl,
IPU6_ISYS_NAME);
if (IS_ERR(isys_adev)) {
kfree(pdata);
@@ -396,8 +413,7 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
"ipu6_bus_initialize_device isys failed\n");
}
- isys_adev->mmu = ipu6_mmu_init(dev, base, ISYS_MMID,
- &ipdata->hw_variant);
+ isys_adev->mmu = ipu6_mmu_init(dev, base, IPU_ISYS);
if (IS_ERR(isys_adev->mmu)) {
put_device(&isys_adev->auxdev.dev);
return dev_err_cast_probe(dev, isys_adev->mmu,
@@ -415,13 +431,19 @@ ipu6_isys_init(struct pci_dev *pdev, struct device *parent,
static struct ipu6_bus_device *
ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
- struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
+ const struct ipu6_buttress_ctrl *ctrl, void __iomem *base,
const struct ipu6_psys_internal_pdata *ipdata)
{
+ struct device *dev = &pdev->dev;
struct ipu6_bus_device *psys_adev;
+ struct ipu6_buttress_ctrl *devm_ctrl;
struct ipu6_psys_pdata *pdata;
int ret;
+ devm_ctrl = devm_kmemdup(dev, ctrl, sizeof(*ctrl), GFP_KERNEL);
+ if (!devm_ctrl)
+ return ERR_PTR(-ENOMEM);
+
pdata = kzalloc_obj(*pdata);
if (!pdata)
return ERR_PTR(-ENOMEM);
@@ -429,7 +451,7 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
pdata->base = base;
pdata->ipdata = ipdata;
- psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, ctrl,
+ psys_adev = ipu6_bus_initialize_device(pdev, parent, pdata, devm_ctrl,
IPU6_PSYS_NAME);
if (IS_ERR(psys_adev)) {
kfree(pdata);
@@ -437,8 +459,7 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
"ipu6_bus_initialize_device psys failed\n");
}
- psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, PSYS_MMID,
- &ipdata->hw_variant);
+ psys_adev->mmu = ipu6_mmu_init(&pdev->dev, base, IPU_PSYS);
if (IS_ERR(psys_adev->mmu)) {
put_device(&psys_adev->auxdev.dev);
return dev_err_cast_probe(&pdev->dev, psys_adev->mmu,
@@ -454,12 +475,13 @@ ipu6_psys_init(struct pci_dev *pdev, struct device *parent,
return psys_adev;
}
-static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver)
+static int ipu6_pci_config_setup(struct pci_dev *dev)
{
+ struct ipu6_device *isp = pci_get_drvdata(dev);
int ret;
/* No PCI msi capability for IPU6EP */
- if (is_ipu6ep(hw_ver) || is_ipu6ep_mtl(hw_ver)) {
+ if (IS_IPU6EP(isp) || IS_IPU6EP_MTL(isp)) {
/* likely do nothing as msi not enabled by default */
pci_disable_msi(dev);
return 0;
@@ -474,7 +496,12 @@ static int ipu6_pci_config_setup(struct pci_dev *dev, u8 hw_ver)
static void ipu6_configure_vc_mechanism(struct ipu6_device *isp)
{
- u32 val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
+ u32 val;
+
+ if (IS_IPU7(isp))
+ return;
+
+ val = readl(isp->base + BUTTRESS_REG_BTRS_CTRL);
if (IPU6_BTRS_ARB_STALL_MODE_VC0 == IPU6_BTRS_ARB_MODE_TYPE_STALL)
val |= BUTTRESS_REG_BTRS_CTRL_STALL_MODE_VC0;
@@ -489,70 +516,193 @@ static void ipu6_configure_vc_mechanism(struct ipu6_device *isp)
writel(val, isp->base + BUTTRESS_REG_BTRS_CTRL);
}
+static int __ipu6_map_fw_by_sys(struct ipu6_device *isp, struct ipu6_bus_device *adev)
+{
+ int ret;
+
+ ret = ipu6_map_fw_region(adev, isp->cpd_fw->data, isp->cpd_fw->size,
+ DMA_TO_DEVICE, 0);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "Firmware mapping failed\n");
+ return ret;
+ }
+
+ ret = ipu6_cpd_create_pkg_dir(adev, isp->cpd_fw->data);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "failed to create pkg dir\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ipu6_map_fw(struct ipu6_device *isp)
+{
+ int ret;
+
+ ret = __ipu6_map_fw_by_sys(isp, isp->psys);
+ if (ret)
+ return ret;
+
+ if (!isp->secure_mode)
+ return __ipu6_map_fw_by_sys(isp, isp->isys);
+
+ return 0;
+}
+
+static int __ipu7_map_fw_non_secure(struct ipu6_device *isp)
+{
+ int ret;
+
+ /*
+ * Allocate and map memory for running the firmware. Not
+ * required in secure mode, in which firmware runs in IMR.
+ */
+ isp->fw_code_region = vmalloc(IPU7_FW_CODE_REGION_SIZE);
+ if (!isp->fw_code_region)
+ return -ENOMEM;
+
+ ret = ipu6_ipu7_cpd_copy_binary(isp->cpd_fw->data, "isys",
+ isp->fw_code_region,
+ &isp->isys->fw_entry);
+ if (ret)
+ return ret;
+
+ ret = ipu6_map_fw_region(isp->isys, isp->fw_code_region,
+ IPU7_FW_CODE_REGION_SIZE, DMA_BIDIRECTIONAL,
+ DMA_ATTR_RESERVE_REGION);
+ if (ret)
+ return ret;
+
+ ret = ipu6_ipu7_cpd_copy_binary(isp->cpd_fw->data, "psys",
+ isp->fw_code_region,
+ &isp->psys->fw_entry);
+ if (ret)
+ return ret;
+
+ return ipu6_map_fw_region(isp->psys, isp->fw_code_region,
+ IPU7_FW_CODE_REGION_SIZE, DMA_BIDIRECTIONAL,
+ DMA_ATTR_RESERVE_REGION);
+}
+
+static int ipu7_map_fw(struct ipu6_device *isp)
+{
+ int ret;
+
+ ret = isp->secure_mode ?
+ ipu6_map_fw_region(isp->psys, isp->cpd_fw->data,
+ isp->cpd_fw->size, DMA_BIDIRECTIONAL, 0) :
+ __ipu7_map_fw_non_secure(isp);
+ if (ret) {
+ dev_err_probe(&isp->pdev->dev, ret,
+ "Failed to init ipu7 firmware region\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
- struct ipu6_buttress_ctrl *isys_ctrl = NULL, *psys_ctrl = NULL;
+ const struct ipu6_buttress_ctrl *isys_ctrl, *psys_ctrl;
struct device *dev = &pdev->dev;
void __iomem *isys_base = NULL;
void __iomem *psys_base = NULL;
struct ipu6_device *isp;
phys_addr_t phys;
u32 val, version, sku_id;
+ unsigned long dir;
int ret;
+ if ((id->device == PCI_DEVICE_ID_INTEL_IPU7 ||
+ id->device == PCI_DEVICE_ID_INTEL_IPU7P5) && force_no_probe_ipu7)
+ return -ENODEV;
+
isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
if (!isp)
return -ENOMEM;
- isp->pdev = pdev;
- INIT_LIST_HEAD(&isp->devices);
-
- ret = pcim_enable_device(pdev);
- if (ret)
- return dev_err_probe(dev, ret, "Enable PCI device failed\n");
-
- phys = pci_resource_start(pdev, IPU6_PCI_BAR);
- dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys);
-
- isp->base = pcim_iomap_region(pdev, IPU6_PCI_BAR, IPU6_NAME);
- if (IS_ERR(isp->base))
- return dev_err_probe(dev, PTR_ERR(isp->base),
- "Failed to I/O mem remapping\n");
-
- pci_set_drvdata(pdev, isp);
- pci_set_master(pdev);
-
isp->cpd_metadata_cmpnt_size = sizeof(struct ipu6_cpd_metadata_cmpnt);
+ isp->buttress.regs = &ipu6_buttress_regs;
+ isp->model_name = IPU6_MEDIA_DEV_MODEL_NAME;
+ isys_ctrl = &ipu6_isys_buttress_ctrl;
+ psys_ctrl = &ipu6_psys_buttress_ctrl;
+
switch (id->device) {
case PCI_DEVICE_ID_INTEL_IPU6:
- isp->hw_ver = IPU6_VER_6;
+ isp->hw_ver = IPU_VERSION_6;
isp->cpd_fw_name = IPU6_FIRMWARE_NAME;
break;
case PCI_DEVICE_ID_INTEL_IPU6SE:
- isp->hw_ver = IPU6_VER_6SE;
+ isp->hw_ver = IPU_VERSION_6SE;
isp->cpd_fw_name = IPU6SE_FIRMWARE_NAME;
isp->cpd_metadata_cmpnt_size =
sizeof(struct ipu6se_cpd_metadata_cmpnt);
break;
case PCI_DEVICE_ID_INTEL_IPU6EP_ADLP:
case PCI_DEVICE_ID_INTEL_IPU6EP_RPLP:
- isp->hw_ver = IPU6_VER_6EP;
+ isp->hw_ver = IPU_VERSION_6EP;
isp->cpd_fw_name = IPU6EP_FIRMWARE_NAME;
break;
case PCI_DEVICE_ID_INTEL_IPU6EP_ADLN:
- isp->hw_ver = IPU6_VER_6EP;
+ isp->hw_ver = IPU_VERSION_6EP;
isp->cpd_fw_name = IPU6EPADLN_FIRMWARE_NAME;
break;
case PCI_DEVICE_ID_INTEL_IPU6EP_MTL:
- isp->hw_ver = IPU6_VER_6EP_MTL;
+ isp->hw_ver = IPU_VERSION_6EP_MTL;
isp->cpd_fw_name = IPU6EPMTL_FIRMWARE_NAME;
break;
+ case PCI_DEVICE_ID_INTEL_IPU7:
+ isp->hw_ver = IPU_VERSION_7;
+ isp->cpd_fw_name = IPU7_FIRMWARE_NAME;
+ isp->model_name = IPU7_MEDIA_DEV_MODEL_NAME;
+ isp->buttress.regs = &ipu7_buttress_regs;
+ isys_ctrl = &ipu7_isys_buttress_ctrl;
+ psys_ctrl = &ipu7_psys_buttress_ctrl;
+ break;
+ case PCI_DEVICE_ID_INTEL_IPU7P5:
+ isp->hw_ver = IPU_VERSION_7P5;
+ isp->cpd_fw_name = IPU7P5_FIRMWARE_NAME;
+ isp->model_name = IPU7P5_MEDIA_DEV_MODEL_NAME;
+ isp->buttress.regs = &ipu7p5_buttress_regs;
+ isys_ctrl = &ipu7_isys_buttress_ctrl;
+ psys_ctrl = &ipu7_psys_buttress_ctrl;
+ break;
default:
return dev_err_probe(dev, -ENODEV,
"Unsupported IPU6 device %x\n",
id->device);
}
+ isp->pdev = pdev;
+ INIT_LIST_HEAD(&isp->devices);
+
+ ret = pcim_enable_device(pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Enable PCI device failed\n");
+
+ phys = pci_resource_start(pdev, IPU6_PCI_BAR);
+ dev_dbg(dev, "IPU6 PCI bar[%u] = %pa\n", IPU6_PCI_BAR, &phys);
+
+ isp->base = pcim_iomap_region(pdev, IPU6_PCI_BAR, IPU6_NAME);
+ if (IS_ERR(isp->base))
+ return dev_err_probe(dev, PTR_ERR(isp->base),
+ "Failed to I/O mem remapping\n");
+
+ if (IS_IPU7(isp)) {
+ isp->pb_base = pcim_iomap_region(pdev, IPU7_PCI_PBBAR,
+ IPU6_NAME);
+ if (IS_ERR(isp->pb_base))
+ return dev_err_probe(dev, PTR_ERR(isp->pb_base),
+ "I/O remapping PB BAR %u failed\n",
+ IPU7_PCI_PBBAR);
+ }
+
+ pci_set_drvdata(pdev, isp);
+ pci_set_master(pdev);
+
ipu6_internal_pdata_init(isp);
isys_base = isp->base + isys_ipdata.hw_variant.offset;
@@ -564,7 +714,7 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dma_set_max_seg_size(dev, UINT_MAX);
- ret = ipu6_pci_config_setup(pdev, isp->hw_ver);
+ ret = ipu6_pci_config_setup(pdev);
if (ret)
return ret;
@@ -588,13 +738,6 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_ipu6_bus_del_devices;
}
- isys_ctrl = devm_kmemdup(dev, &isys_buttress_ctrl,
- sizeof(isys_buttress_ctrl), GFP_KERNEL);
- if (!isys_ctrl) {
- ret = -ENOMEM;
- goto out_ipu6_bus_del_devices;
- }
-
isp->isys = ipu6_isys_init(pdev, dev, isys_ctrl, isys_base,
&isys_ipdata);
if (IS_ERR(isp->isys)) {
@@ -602,13 +745,6 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_ipu6_bus_del_devices;
}
- psys_ctrl = devm_kmemdup(dev, &psys_buttress_ctrl,
- sizeof(psys_buttress_ctrl), GFP_KERNEL);
- if (!psys_ctrl) {
- ret = -ENOMEM;
- goto out_ipu6_bus_del_devices;
- }
-
isp->psys = ipu6_psys_init(pdev, &isp->isys->auxdev.dev, psys_ctrl,
psys_base, &psys_ipdata);
if (IS_ERR(isp->psys)) {
@@ -627,19 +763,9 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_ipu6_rpm_put;
}
- ret = ipu6_buttress_map_fw_image(isp->psys, isp->cpd_fw,
- &isp->psys->fw_sgt);
- if (ret) {
- dev_err_probe(&isp->pdev->dev, ret, "failed to map fw image\n");
- goto out_ipu6_rpm_put;
- }
-
- ret = ipu6_cpd_create_pkg_dir(isp->psys, isp->cpd_fw->data);
- if (ret) {
- dev_err_probe(&isp->pdev->dev, ret,
- "failed to create pkg dir\n");
+ ret = IS_IPU7(isp) ? ipu7_map_fw(isp) : ipu6_map_fw(isp);
+ if (ret)
goto out_ipu6_rpm_put;
- }
ret = devm_request_threaded_irq(dev, pdev->irq, ipu6_buttress_isr,
ipu6_buttress_isr_threaded,
@@ -662,7 +788,7 @@ static int ipu6_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* Configure the arbitration mechanisms for VC requests */
ipu6_configure_vc_mechanism(isp);
- val = readl(isp->base + BUTTRESS_REG_SKU);
+ val = readl(isp->base + isp->buttress.regs->sku_id);
sku_id = FIELD_GET(GENMASK(6, 4), val);
version = FIELD_GET(GENMASK(3, 0), val);
dev_info(dev, "IPU%u-v%u[%x] hardware version %d\n", version, sku_id,
@@ -680,10 +806,21 @@ out_free_irq:
out_ipu6_rpm_put:
pm_runtime_put_sync(&isp->psys->auxdev.dev);
out_ipu6_bus_del_devices:
+ dir = IS_IPU7(isp) ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
if (!IS_ERR_OR_NULL(isp->psys)) {
ipu6_cpd_free_pkg_dir(isp->psys);
- ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
+ if (isp->psys->fw_sgt.nents)
+ ipu6_unmap_fw_region(isp->psys, dir);
+ }
+ if (!IS_ERR_OR_NULL(isp->isys)) {
+ ipu6_cpd_free_pkg_dir(isp->isys);
+ if (isp->isys->fw_sgt.nents)
+ ipu6_unmap_fw_region(isp->isys, dir);
}
+
+ vfree(isp->fw_code_region);
+ isp->fw_code_region = NULL;
+
if (!IS_ERR_OR_NULL(isp->psys) && !IS_ERR_OR_NULL(isp->psys->mmu))
ipu6_mmu_cleanup(isp->psys->mmu);
if (!IS_ERR_OR_NULL(isp->isys) && !IS_ERR_OR_NULL(isp->isys->mmu))
@@ -699,13 +836,23 @@ buttress_exit:
static void ipu6_pci_remove(struct pci_dev *pdev)
{
struct ipu6_device *isp = pci_get_drvdata(pdev);
- struct ipu6_mmu *isys_mmu = isp->isys->mmu;
- struct ipu6_mmu *psys_mmu = isp->psys->mmu;
+ unsigned long dir;
devm_free_irq(&pdev->dev, pdev->irq, isp);
+
+ dir = IS_IPU7(isp) ? DMA_BIDIRECTIONAL : DMA_TO_DEVICE;
ipu6_cpd_free_pkg_dir(isp->psys);
+ ipu6_unmap_fw_region(isp->psys, dir);
+
+ if (isp->isys) {
+ ipu6_cpd_free_pkg_dir(isp->isys);
+ if (isp->isys->fw_sgt.nents)
+ ipu6_unmap_fw_region(isp->isys, dir);
+ }
+
+ vfree(isp->fw_code_region);
+ isp->fw_code_region = NULL;
- ipu6_buttress_unmap_fw_image(isp->psys, &isp->psys->fw_sgt);
ipu6_buttress_exit(isp);
ipu6_bus_del_devices(pdev);
@@ -715,8 +862,10 @@ static void ipu6_pci_remove(struct pci_dev *pdev)
release_firmware(isp->cpd_fw);
- ipu6_mmu_cleanup(psys_mmu);
- ipu6_mmu_cleanup(isys_mmu);
+ if (isp->psys)
+ ipu6_mmu_cleanup(isp->psys->mmu);
+ if (isp->isys)
+ ipu6_mmu_cleanup(isp->isys->mmu);
}
static void ipu6_pci_reset_prepare(struct pci_dev *pdev)
@@ -754,7 +903,6 @@ static int ipu6_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct ipu6_device *isp = pci_get_drvdata(pdev);
- struct ipu6_buttress *b = &isp->buttress;
int ret;
/* Configure the arbitration mechanisms for VC requests */
@@ -766,7 +914,7 @@ static int ipu6_resume(struct device *dev)
ipu6_buttress_restore(isp);
- ret = ipu6_buttress_ipc_reset(isp, &b->cse);
+ ret = ipu6_buttress_ipc_reset(isp);
if (ret)
dev_err(&isp->pdev->dev, "IPC reset protocol failed!\n");
@@ -795,10 +943,8 @@ static int ipu6_runtime_resume(struct device *dev)
ipu6_buttress_restore(isp);
if (isp->need_ipc_reset) {
- struct ipu6_buttress *b = &isp->buttress;
-
isp->need_ipc_reset = false;
- ret = ipu6_buttress_ipc_reset(isp, &b->cse);
+ ret = ipu6_buttress_ipc_reset(isp);
if (ret)
dev_err(&isp->pdev->dev, "IPC reset protocol failed\n");
}
diff --git a/drivers/media/pci/intel/ipu6/ipu6.h b/drivers/media/pci/intel/ipu6/ipu6.h
index 92e3c3414c91..72c5063d5198 100644
--- a/drivers/media/pci/intel/ipu6/ipu6.h
+++ b/drivers/media/pci/intel/ipu6/ipu6.h
@@ -16,46 +16,32 @@ struct ipu6_bus_device;
#define IPU6_NAME "intel-ipu6"
#define IPU6_MEDIA_DEV_MODEL_NAME "ipu6"
+#define IPU7_MEDIA_DEV_MODEL_NAME "ipu7"
+#define IPU7P5_MEDIA_DEV_MODEL_NAME "ipu7.5"
#define IPU6SE_FIRMWARE_NAME "intel/ipu/ipu6se_fw.bin"
#define IPU6EP_FIRMWARE_NAME "intel/ipu/ipu6ep_fw.bin"
#define IPU6_FIRMWARE_NAME "intel/ipu/ipu6_fw.bin"
#define IPU6EPMTL_FIRMWARE_NAME "intel/ipu/ipu6epmtl_fw.bin"
#define IPU6EPADLN_FIRMWARE_NAME "intel/ipu/ipu6epadln_fw.bin"
-
-enum ipu6_version {
- IPU6_VER_INVALID = 0,
- IPU6_VER_6 = 1,
- IPU6_VER_6SE = 3,
- IPU6_VER_6EP = 5,
- IPU6_VER_6EP_MTL = 6,
-};
-
-/*
- * IPU6 - TGL
- * IPU6SE - JSL
- * IPU6EP - ADL/RPL
- * IPU6EP_MTL - MTL
- */
-static inline bool is_ipu6se(u8 hw_ver)
-{
- return hw_ver == IPU6_VER_6SE;
-}
-
-static inline bool is_ipu6ep(u8 hw_ver)
-{
- return hw_ver == IPU6_VER_6EP;
-}
-
-static inline bool is_ipu6ep_mtl(u8 hw_ver)
-{
- return hw_ver == IPU6_VER_6EP_MTL;
-}
-
-static inline bool is_ipu6_tgl(u8 hw_ver)
-{
- return hw_ver == IPU6_VER_6;
-}
+#define IPU7_FIRMWARE_NAME "intel/ipu/ipu7_fw.bin"
+#define IPU7P5_FIRMWARE_NAME "intel/ipu/ipu7ptl_fw.bin"
+
+#define IPU_VERSION_6 BIT(0) /* TGL */
+#define IPU_VERSION_6SE BIT(1) /* JSL */
+#define IPU_VERSION_6EP BIT(2) /* ADL/RPL */
+#define IPU_VERSION_6EP_MTL BIT(3) /* MTL */
+#define IPU_VERSION_7 BIT(4) /* LNL */
+#define IPU_VERSION_7P5 BIT(5) /* PTL */
+
+#define IS_IPU6_TGL(isp) ((isp)->hw_ver & IPU_VERSION_6)
+#define IS_IPU6SE(isp) ((isp)->hw_ver & IPU_VERSION_6SE)
+#define IS_IPU6EP(isp) ((isp)->hw_ver & IPU_VERSION_6EP)
+#define IS_IPU6EP_MTL(isp) ((isp)->hw_ver & IPU_VERSION_6EP_MTL)
+#define IS_IPU7(isp) ((isp)->hw_ver & \
+ (IPU_VERSION_7 | IPU_VERSION_7P5))
+#define IS_IPU7_MTL(isp) ((isp)->hw_ver & IPU_VERSION_7)
+#define IS_IPU7P5(isp) ((isp)->hw_ver & IPU_VERSION_7P5)
/*
* ISYS DMA can overshoot. For higher resolutions over allocation is one line
@@ -80,14 +66,21 @@ struct ipu6_device {
const struct firmware *cpd_fw;
const char *cpd_fw_name;
u32 cpd_metadata_cmpnt_size;
+ const char *model_name;
void __iomem *base;
+ void __iomem *pb_base;
bool need_ipc_reset;
bool secure_mode;
u8 hw_ver;
bool bus_ready_to_probe;
+ u32 *fw_code_region;
};
+#define IPU_PSYS 0
+#define IPU_ISYS 1
+#define IPU_SUBSYS_NUM 2
+
#define IPU6_ISYS_NAME "isys"
#define IPU6_PSYS_NAME "psys"
@@ -134,141 +127,6 @@ struct ipu6_device {
#define IPU6_BTRS_ARB_STALL_MODE_VC1 \
IPU6_BTRS_ARB_MODE_TYPE_REARB
-/*
- * MMU Invalidation HW bug workaround by ZLW mechanism
- *
- * Old IPU6 MMUV2 has a bug in the invalidation mechanism which might result in
- * wrong translation or replication of the translation. This will cause data
- * corruption. So we cannot directly use the MMU V2 invalidation registers
- * to invalidate the MMU. Instead, whenever an invalidate is called, we need to
- * clear the TLB by evicting all the valid translations by filling it with trash
- * buffer (which is guaranteed not to be used by any other processes). ZLW is
- * used to fill the L1 and L2 caches with the trash buffer translations. ZLW
- * or Zero length write, is pre-fetch mechanism to pre-fetch the pages in
- * advance to the L1 and L2 caches without triggering any memory operations.
- *
- * In MMU V2, L1 -> 16 streams and 64 blocks, maximum 16 blocks per stream
- * One L1 block has 16 entries, hence points to 16 * 4K pages
- * L2 -> 16 streams and 32 blocks. 2 blocks per streams
- * One L2 block maps to 1024 L1 entries, hence points to 4MB address range
- * 2 blocks per L2 stream means, 1 stream points to 8MB range
- *
- * As we need to clear the caches and 8MB being the biggest cache size, we need
- * to have trash buffer which points to 8MB address range. As these trash
- * buffers are not used for any memory transactions, we need only the least
- * amount of physical memory. So we reserve 8MB IOVA address range but only
- * one page is reserved from physical memory. Each of this 8MB IOVA address
- * range is then mapped to the same physical memory page.
- */
-/* One L2 entry maps 1024 L1 entries and one L1 entry per page */
-#define IPU6_MMUV2_L2_RANGE (1024 * PAGE_SIZE)
-/* Max L2 blocks per stream */
-#define IPU6_MMUV2_MAX_L2_BLOCKS 2
-/* Max L1 blocks per stream */
-#define IPU6_MMUV2_MAX_L1_BLOCKS 16
-#define IPU6_MMUV2_TRASH_RANGE (IPU6_MMUV2_L2_RANGE * IPU6_MMUV2_MAX_L2_BLOCKS)
-/* Entries per L1 block */
-#define MMUV2_ENTRIES_PER_L1_BLOCK 16
-#define MMUV2_TRASH_L1_BLOCK_OFFSET (MMUV2_ENTRIES_PER_L1_BLOCK * PAGE_SIZE)
-#define MMUV2_TRASH_L2_BLOCK_OFFSET IPU6_MMUV2_L2_RANGE
-
-/*
- * In some of the IPU6 MMUs, there is provision to configure L1 and L2 page
- * table caches. Both these L1 and L2 caches are divided into multiple sections
- * called streams. There is maximum 16 streams for both caches. Each of these
- * sections are subdivided into multiple blocks. When nr_l1streams = 0 and
- * nr_l2streams = 0, means the MMU is of type MMU_V1 and do not support
- * L1/L2 page table caches.
- *
- * L1 stream per block sizes are configurable and varies per usecase.
- * L2 has constant block sizes - 2 blocks per stream.
- *
- * MMU1 support pre-fetching of the pages to have less cache lookup misses. To
- * enable the pre-fetching, MMU1 AT (Address Translator) device registers
- * need to be configured.
- *
- * There are four types of memory accesses which requires ZLW configuration.
- * ZLW(Zero Length Write) is a mechanism to enable VT-d pre-fetching on IOMMU.
- *
- * 1. Sequential Access or 1D mode
- * Set ZLW_EN -> 1
- * set ZLW_PAGE_CROSS_1D -> 1
- * Set ZLW_N to "N" pages so that ZLW will be inserte N pages ahead where
- * N is pre-defined and hardcoded in the platform data
- * Set ZLW_2D -> 0
- *
- * 2. ZLW 2D mode
- * Set ZLW_EN -> 1
- * set ZLW_PAGE_CROSS_1D -> 1,
- * Set ZLW_N -> 0
- * Set ZLW_2D -> 1
- *
- * 3. ZLW Enable (no 1D or 2D mode)
- * Set ZLW_EN -> 1
- * set ZLW_PAGE_CROSS_1D -> 0,
- * Set ZLW_N -> 0
- * Set ZLW_2D -> 0
- *
- * 4. ZLW disable
- * Set ZLW_EN -> 0
- * set ZLW_PAGE_CROSS_1D -> 0,
- * Set ZLW_N -> 0
- * Set ZLW_2D -> 0
- *
- * To configure the ZLW for the above memory access, four registers are
- * available. Hence to track these four settings, we have the following entries
- * in the struct ipu6_mmu_hw. Each of these entries are per stream and
- * available only for the L1 streams.
- *
- * a. l1_zlw_en -> To track zlw enabled per stream (ZLW_EN)
- * b. l1_zlw_1d_mode -> Track 1D mode per stream. ZLW inserted at page boundary
- * c. l1_ins_zlw_ahead_pages -> to track how advance the ZLW need to be inserted
- * Insert ZLW request N pages ahead address.
- * d. l1_zlw_2d_mode -> To track 2D mode per stream (ZLW_2D)
- *
- *
- * Currently L1/L2 streams, blocks, AT ZLW configurations etc. are pre-defined
- * as per the usecase specific calculations. Any change to this pre-defined
- * table has to happen in sync with IPU6 FW.
- */
-struct ipu6_mmu_hw {
- union {
- unsigned long offset;
- void __iomem *base;
- };
- u32 info_bits;
- u8 nr_l1streams;
- /*
- * L1 has variable blocks per stream - total of 64 blocks and maximum of
- * 16 blocks per stream. Configurable by using the block start address
- * per stream. Block start address is calculated from the block size
- */
- u8 l1_block_sz[IPU6_MMU_MAX_TLB_L1_STREAMS];
- /* Is ZLW is enabled in each stream */
- bool l1_zlw_en[IPU6_MMU_MAX_TLB_L1_STREAMS];
- bool l1_zlw_1d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
- u8 l1_ins_zlw_ahead_pages[IPU6_MMU_MAX_TLB_L1_STREAMS];
- bool l1_zlw_2d_mode[IPU6_MMU_MAX_TLB_L1_STREAMS];
-
- u32 l1_stream_id_reg_offset;
- u32 l2_stream_id_reg_offset;
-
- u8 nr_l2streams;
- /*
- * L2 has fixed 2 blocks per stream. Block address is calculated
- * from the block size
- */
- u8 l2_block_sz[IPU6_MMU_MAX_TLB_L2_STREAMS];
- /* flag to track if WA is needed for successive invalidate HW bug */
- bool insert_read_before_invalidate;
-};
-
-struct ipu6_mmu_pdata {
- u32 nr_mmus;
- struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES];
- int mmid;
-};
-
struct ipu6_isys_csi2_pdata {
void __iomem *base;
};
@@ -283,6 +141,8 @@ struct ipu6_isys_internal_csi2_pdata {
u32 ctrl0_irq_lnp;
u32 ctrl0_irq_status;
u32 fw_access_port_ofs;
+ /* IPU7-specific field */
+ u32 gpreg;
};
struct ipu6_isys_internal_tpg_pdata {
@@ -293,8 +153,6 @@ struct ipu6_isys_internal_tpg_pdata {
struct ipu6_hw_variants {
unsigned long offset;
- u32 nr_mmus;
- struct ipu6_mmu_hw mmu_hw[IPU6_MMU_MAX_DEVICES];
u8 cdc_fifos;
u8 cdc_fifo_threshold[IPU6_MAX_VC_IOSF_PORTS];
u32 dmem_offset;
diff --git a/drivers/media/pci/intel/ipu6/ipu7-boot.c b/drivers/media/pci/intel/ipu6/ipu7-boot.c
new file mode 100644
index 000000000000..952c73efd237
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-boot.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2022 - 2026 Intel Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/types.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-buttress.h"
+#include "ipu6-dma.h"
+#include "ipu6-isys.h"
+#include "ipu6-platform-buttress-regs.h"
+#include "ipu7-boot.h"
+#include "ipu7-platform-regs.h"
+
+#define IPU7_FW_START_STOP_TIMEOUT 2000
+#define IPU7_BOOT_CELL_RESET_TIMEOUT (2 * USEC_PER_SEC)
+#define IPU7_BOOT_STATE_CRITICAL(s) (((s) & 0xffff0000U) == 0xdead0000U)
+#define IPU7_BOOT_STATE_READY(s) ((s) == 0x57a7e100U)
+#define IPU7_BOOT_STATE_INACTIVE(s) ((s) == 0x57a7e300U)
+#define IPU7_BUTTRESS_REG_FW_BOOT_PARAMS0 0x4000
+#define IPU7_BUTTRESS_FW_BOOT_PARAMS_ENTRY(i) \
+ (IPU7_BUTTRESS_REG_FW_BOOT_PARAMS0 + ((i) * 4U))
+
+struct boot_regs {
+ u32 base;
+ u32 dmem_address;
+ u32 status_ctrl_reg;
+ u32 fw_start_address_reg;
+ u32 fw_code_base_reg;
+};
+
+enum ipu7_boot_reg_id {
+ IPU7_FW_BOOT_CONFIG_ID = 0,
+ IPU7_FW_BOOT_STATE_ID = 1,
+ IPU7_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID = 2,
+ IPU7_FW_BOOT_UNTRUSTED_ADDR_MIN_ID = 3,
+ IPU7_FW_BOOT_MESSAGING_VERSION_ID = 4,
+ IPU7_FW_BOOT_ID_MAX,
+};
+
+enum ipu7_boot_state {
+ IPU7_FW_BOOT_STATE_UNINIT = 0x57a7e000U,
+ IPU7_FW_BOOT_STATE_READY = 0x57a7e100U,
+ IPU7_FW_BOOT_STATE_SHUTDOWN_CMD = 0x57a7f001U,
+ IPU7_FW_BOOT_STATE_INACTIVE = 0x57a7e300U,
+};
+
+static const struct boot_regs boot_regs[IPU_SUBSYS_NUM] = {
+ [IPU_ISYS] = {
+ .dmem_address = IPU7_ISYS_DMEM_OFFSET,
+ .status_ctrl_reg = IPU7_BUTTRESS_REG_ISYS_UCX_CTRL_STATUS,
+ .fw_start_address_reg = IPU7_BUTTRESS_REG_ISYS_UCX_START_ADDR,
+ .fw_code_base_reg = IPU7_IS_UC_CTRL_BASE
+ },
+ [IPU_PSYS] = {
+ .dmem_address = IPU7_PSYS_DMEM_OFFSET,
+ .status_ctrl_reg = IPU7_BUTTRESS_REG_PSYS_UCX_CTRL_STATUS,
+ .fw_start_address_reg = IPU7_BUTTRESS_REG_PSYS_UCX_START_ADDR,
+ .fw_code_base_reg = IPU7_PS_UC_CTRL_BASE
+ }
+};
+
+static u32 get_fw_boot_reg_addr(const struct ipu6_bus_device *adev,
+ enum ipu7_boot_reg_id reg)
+{
+ u32 base = (adev->ctrl->subsys_id == IPU_ISYS) ?
+ 0U : (u32)IPU7_FW_BOOT_ID_MAX;
+
+ return IPU7_BUTTRESS_FW_BOOT_PARAMS_ENTRY(base + (u32)reg);
+}
+
+static void write_fw_boot_param(const struct ipu6_bus_device *adev,
+ enum ipu7_boot_reg_id reg,
+ u32 val)
+{
+ void __iomem *base = adev->isp->base;
+
+ dev_dbg(&adev->auxdev.dev,
+ "write boot param reg: %d addr: %x val: 0x%x\n",
+ reg, get_fw_boot_reg_addr(adev, reg), val);
+ writel(val, base + get_fw_boot_reg_addr(adev, reg));
+}
+
+static u32 read_fw_boot_param(const struct ipu6_bus_device *adev,
+ enum ipu7_boot_reg_id reg)
+{
+ void __iomem *base = adev->isp->base;
+
+ return readl(base + get_fw_boot_reg_addr(adev, reg));
+}
+
+static int ipu7_boot_cell_reset(const struct ipu6_bus_device *adev)
+{
+ const struct device *dev = &adev->auxdev.dev;
+ const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id];
+ u32 ucx_ctrl_status = regs->status_ctrl_reg;
+ u32 timeout = IPU7_BOOT_CELL_RESET_TIMEOUT;
+ void __iomem *base = adev->isp->base;
+ u32 val, val2;
+ int ret;
+
+ val = readl(base + ucx_ctrl_status);
+ val |= IPU7_UCX_CTL_RESET;
+ val &= ~IPU7_UCX_CTL_RUN;
+
+ writel(val, base + ucx_ctrl_status);
+
+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
+ (val2 & 0x3U) == (val & 0x3U), 100, timeout);
+ if (ret) {
+ dev_err(dev, "cell enter reset timeout. status: 0x%x\n", val2);
+ return -ETIMEDOUT;
+ }
+
+ val = readl(base + ucx_ctrl_status);
+ val &= ~(IPU7_UCX_CTL_RESET | IPU7_UCX_CTL_RUN);
+ writel(val, base + ucx_ctrl_status);
+
+ ret = readl_poll_timeout(base + ucx_ctrl_status, val2,
+ (val2 & 0x3U) == (val & 0x3U), 100, timeout);
+ if (ret) {
+ dev_err(dev, "cell exit reset timeout. status: 0x%x\n", val2);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void ipu7_boot_cell_start(const struct ipu6_bus_device *adev)
+{
+ const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id];
+ void __iomem *base = adev->isp->base;
+ u32 val;
+
+ val = readl(base + regs->status_ctrl_reg);
+ WARN_ON(val & (IPU7_UCX_CTL_RESET | IPU7_UCX_CTL_RUN));
+
+ val &= ~IPU7_UCX_CTL_RESET;
+ val |= IPU7_UCX_CTL_RUN;
+ writel(val, base + regs->status_ctrl_reg);
+}
+
+static void ipu7_boot_cell_stop(const struct ipu6_bus_device *adev)
+{
+ const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id];
+ void __iomem *base = adev->isp->base;
+ u32 val;
+
+ val = readl(base + regs->status_ctrl_reg);
+ val &= ~IPU7_UCX_CTL_RUN;
+ writel(val, base + regs->status_ctrl_reg);
+
+ /* Wait for uC transactions complete */
+ usleep_range(10, 20);
+
+ val = readl(base + regs->status_ctrl_reg);
+ val |= IPU7_UCX_CTL_RESET;
+ writel(val, base + regs->status_ctrl_reg);
+}
+
+static int ipu7_boot_cell_init(const struct ipu6_bus_device *adev)
+{
+ const struct boot_regs *regs = &boot_regs[adev->ctrl->subsys_id];
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ struct ipu7_fw_com_context *fwctx = isys->fwctx;
+ void __iomem *base = adev->isp->base;
+
+ writel(fwctx->fw_entry, base + regs->fw_start_address_reg);
+
+ return ipu7_boot_cell_reset(adev);
+}
+
+static void init_cfg_versions(struct ipu7_boot_abi_cfg *boot_cfg, u32 length, u8 major)
+{
+ boot_cfg->length = length;
+ boot_cfg->config_version.major = 1U;
+ boot_cfg->config_version.minor = 0U;
+ boot_cfg->config_version.subminor = 0U;
+ boot_cfg->config_version.patch = 0U;
+
+ boot_cfg->client_version_support.num_versions = 1U;
+ boot_cfg->client_version_support.versions[0].major = major;
+ boot_cfg->client_version_support.versions[0].minor = 0U;
+ boot_cfg->client_version_support.versions[0].subminor = 0U;
+ boot_cfg->client_version_support.versions[0].patch = 0U;
+}
+
+int ipu6_ipu7_init_boot_config(struct ipu6_bus_device *adev,
+ struct ipu7_fw_com_queue_config *qconfigs,
+ int num_queues, u32 uc_freq,
+ dma_addr_t subsys_config, u8 major)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ struct ipu7_fw_com_context *fwctx = isys->fwctx;
+ struct ipu7_boot_abi_cfg *boot_config;
+ struct ipu7_fw_com_queue_params_config *cfgs;
+ struct device *dev = &adev->auxdev.dev;
+ u32 total_queue_size_aligned = 0;
+ dma_addr_t queue_mem_dma_ptr;
+ void *queue_mem_ptr;
+ unsigned int i;
+
+ /* Allocate boot config. */
+ fwctx->boot_config_size =
+ sizeof(*cfgs) * num_queues + sizeof(*boot_config);
+ fwctx->boot_config = ipu6_dma_alloc(adev, fwctx->boot_config_size,
+ &fwctx->boot_config_dma_addr,
+ GFP_KERNEL, 0);
+ if (!fwctx->boot_config) {
+ dev_err(dev, "Failed to allocate boot config.\n");
+ return -ENOMEM;
+ }
+
+ boot_config = fwctx->boot_config;
+ memset(boot_config, 0, sizeof(*boot_config));
+ init_cfg_versions(boot_config, fwctx->boot_config_size, major);
+ boot_config->subsys_config = subsys_config;
+
+ boot_config->uc_tile_frequency = uc_freq;
+ boot_config->uc_tile_frequency_units = 0;
+ boot_config->fw_com_config.max_output_queues =
+ fwctx->num_output_queues;
+ boot_config->fw_com_config.max_input_queues =
+ fwctx->num_input_queues;
+
+ ipu6_dma_sync_single(adev, fwctx->boot_config_dma_addr,
+ fwctx->boot_config_size);
+
+ for (i = 0; i < num_queues; i++) {
+ u32 queue_size = qconfigs[i].max_capacity *
+ qconfigs[i].token_size_in_bytes;
+
+ queue_size = ALIGN(queue_size, 64U);
+ total_queue_size_aligned += queue_size;
+ qconfigs[i].queue_size = queue_size;
+ }
+
+ /* Allocate queue memory */
+ fwctx->queue_mem = ipu6_dma_alloc(adev, total_queue_size_aligned,
+ &fwctx->queue_mem_dma_addr,
+ GFP_KERNEL, 0);
+ if (!fwctx->queue_mem) {
+ dev_err(dev, "Failed to allocate queue memory.\n");
+ return -ENOMEM;
+ }
+ fwctx->queue_mem_size = total_queue_size_aligned;
+
+ cfgs = ipu7_fw_com_get_queue_config(&boot_config->fw_com_config);
+ queue_mem_ptr = fwctx->queue_mem;
+ queue_mem_dma_ptr = fwctx->queue_mem_dma_addr;
+ for (i = 0; i < num_queues; i++) {
+ cfgs[i].token_array_mem = queue_mem_dma_ptr;
+ cfgs[i].max_capacity = qconfigs[i].max_capacity;
+ cfgs[i].token_size_in_bytes = qconfigs[i].token_size_in_bytes;
+ qconfigs[i].token_array_mem = queue_mem_ptr;
+ queue_mem_dma_ptr += qconfigs[i].queue_size;
+ queue_mem_ptr += qconfigs[i].queue_size;
+ }
+
+ ipu6_dma_sync_single(adev, fwctx->queue_mem_dma_addr,
+ total_queue_size_aligned);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_init_boot_config, "INTEL_IPU6");
+
+void ipu6_ipu7_release_boot_config(struct ipu6_bus_device *adev)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ struct ipu7_fw_com_context *fwctx;
+
+ if (!isys || !isys->fwctx)
+ return;
+
+ fwctx = isys->fwctx;
+
+ if (fwctx->queue_mem) {
+ ipu6_dma_free(adev, fwctx->queue_mem_size,
+ fwctx->queue_mem,
+ fwctx->queue_mem_dma_addr, 0);
+ fwctx->queue_mem = NULL;
+ fwctx->queue_mem_dma_addr = 0;
+ }
+
+ if (fwctx->boot_config) {
+ ipu6_dma_free(adev, fwctx->boot_config_size,
+ fwctx->boot_config,
+ fwctx->boot_config_dma_addr, 0);
+ fwctx->boot_config = NULL;
+ fwctx->boot_config_dma_addr = 0;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_release_boot_config, "INTEL_IPU6");
+
+int ipu6_ipu7_boot_start_fw(const struct ipu6_bus_device *adev)
+{
+ const struct device *dev = &adev->auxdev.dev;
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ struct ipu7_fw_com_context *fwctx = isys->fwctx;
+ u32 timeout = IPU7_FW_START_STOP_TIMEOUT;
+ void __iomem *base = adev->isp->base;
+ u32 boot_state, last_boot_state;
+ u32 indices_addr, msg_ver, id;
+ int ret;
+
+ ret = ipu7_boot_cell_init(adev);
+ if (ret)
+ return ret;
+
+ /* store "uninit" state to boot state reg */
+ write_fw_boot_param(adev, IPU7_FW_BOOT_STATE_ID,
+ IPU7_FW_BOOT_STATE_UNINIT);
+ /* Set registers to zero, recommended for diagnostics. */
+ write_fw_boot_param(adev,
+ IPU7_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID, 0);
+ write_fw_boot_param(adev, IPU7_FW_BOOT_MESSAGING_VERSION_ID, 0);
+ /* store firmware configuration address */
+ write_fw_boot_param(adev, IPU7_FW_BOOT_CONFIG_ID,
+ fwctx->boot_config_dma_addr);
+
+ ipu7_boot_cell_start(adev);
+
+ last_boot_state = IPU7_FW_BOOT_STATE_UNINIT;
+ while (timeout--) {
+ boot_state = read_fw_boot_param(adev,
+ IPU7_FW_BOOT_STATE_ID);
+ if (boot_state != last_boot_state) {
+ dev_dbg(dev, "boot state changed from 0x%x to 0x%x\n",
+ last_boot_state, boot_state);
+ last_boot_state = boot_state;
+ }
+ if (IPU7_BOOT_STATE_CRITICAL(boot_state) ||
+ IPU7_BOOT_STATE_READY(boot_state))
+ break;
+ usleep_range(1000, 1200);
+ }
+
+ if (IPU7_BOOT_STATE_CRITICAL(boot_state)) {
+ dev_err(dev, "critical boot state error 0x%x\n", boot_state);
+ return -EINVAL;
+ } else if (!IPU7_BOOT_STATE_READY(boot_state)) {
+ dev_err(dev, "fw boot timeout. state: 0x%x\n", boot_state);
+ return -ETIMEDOUT;
+ }
+ dev_dbg(dev, "fw boot done.\n");
+
+ id = IPU7_FW_BOOT_SYSCOM_QUEUE_INDICES_BASE_ID;
+ indices_addr = read_fw_boot_param(adev, id);
+ fwctx->queue_indices = base + indices_addr;
+ dev_dbg(dev, "fw queue indices offset is 0x%x\n", indices_addr);
+
+ msg_ver = read_fw_boot_param(adev,
+ IPU7_FW_BOOT_MESSAGING_VERSION_ID);
+ dev_dbg(dev, "ipu message version is 0x%08x\n", msg_ver);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_boot_start_fw, "INTEL_IPU6");
+
+int ipu6_ipu7_boot_stop_fw(const struct ipu6_bus_device *adev)
+{
+ const struct device *dev = &adev->auxdev.dev;
+ u32 timeout = IPU7_FW_START_STOP_TIMEOUT;
+ u32 boot_state;
+
+ boot_state = read_fw_boot_param(adev, IPU7_FW_BOOT_STATE_ID);
+ if (IPU7_BOOT_STATE_CRITICAL(boot_state) ||
+ !IPU7_BOOT_STATE_READY(boot_state)) {
+ dev_err(dev, "fw not ready for shutdown, state 0x%x\n",
+ boot_state);
+ return -EBUSY;
+ }
+
+ /* Issue shutdown to start shutdown process */
+ dev_dbg(dev, "stopping fw...\n");
+ write_fw_boot_param(adev, IPU7_FW_BOOT_STATE_ID,
+ IPU7_FW_BOOT_STATE_SHUTDOWN_CMD);
+ while (timeout--) {
+ boot_state = read_fw_boot_param(adev,
+ IPU7_FW_BOOT_STATE_ID);
+ if (IPU7_BOOT_STATE_CRITICAL(boot_state) ||
+ IPU7_BOOT_STATE_INACTIVE(boot_state))
+ break;
+ usleep_range(1000, 1200);
+ }
+
+ if (IPU7_BOOT_STATE_CRITICAL(boot_state)) {
+ dev_err(dev, "critical boot state error 0x%x\n", boot_state);
+ return -EINVAL;
+ } else if (!IPU7_BOOT_STATE_INACTIVE(boot_state)) {
+ dev_err(dev, "stop fw timeout. state: 0x%x\n", boot_state);
+ return -ETIMEDOUT;
+ }
+
+ ipu7_boot_cell_stop(adev);
+ dev_dbg(dev, "stop fw done.\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(ipu6_ipu7_boot_stop_fw, "INTEL_IPU6");
diff --git a/drivers/media/pci/intel/ipu6/ipu7-boot.h b/drivers/media/pci/intel/ipu6/ipu7-boot.h
new file mode 100644
index 000000000000..f2172b53bed5
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-boot.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2026 Intel Corporation */
+
+#ifndef IPU7_BOOT_H
+#define IPU7_BOOT_H
+
+#include "ipu7-fw-com.h"
+
+#define IPU7_BOOT_MSG_VER_MAX_ENTRIES 3U
+
+struct ipu7_boot_abi_version {
+ u8 patch;
+ u8 subminor;
+ u8 minor;
+ u8 major;
+};
+
+struct ipu7_boot_abi_msg_versions {
+ u8 num_versions;
+ u8 reserved[3];
+ struct ipu7_boot_abi_version versions[IPU7_BOOT_MSG_VER_MAX_ENTRIES];
+};
+
+struct ipu7_boot_abi_cfg {
+ u32 length;
+ struct ipu7_boot_abi_version config_version;
+ struct ipu7_boot_abi_msg_versions client_version_support;
+ u32 pkg_dir;
+ u32 subsys_config;
+ u32 uc_tile_frequency;
+ u16 checksum;
+ u8 uc_tile_frequency_units;
+ u8 padding[1];
+ u32 reserved[58];
+ struct ipu7_fw_com_config fw_com_config;
+} __packed;
+
+int ipu6_ipu7_init_boot_config(struct ipu6_bus_device *adev,
+ struct ipu7_fw_com_queue_config *qconfigs,
+ int num_queues, u32 uc_freq,
+ dma_addr_t subsys_config, u8 major);
+void ipu6_ipu7_release_boot_config(struct ipu6_bus_device *adev);
+int ipu6_ipu7_boot_start_fw(const struct ipu6_bus_device *adev);
+int ipu6_ipu7_boot_stop_fw(const struct ipu6_bus_device *adev);
+
+#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-com.c b/drivers/media/pci/intel/ipu6/ipu7-fw-com.c
new file mode 100644
index 000000000000..7dd1e683aa92
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-fw-com.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Intel Corporation
+ */
+
+#include <linux/io.h>
+
+#include "ipu7-fw-com.h"
+
+static void __iomem *ipu7_fw_com_get_indices(struct ipu7_fw_com_context *ctx,
+ u32 q)
+{
+ return ctx->queue_indices + (q * sizeof(struct ipu7_fw_com_queue_indices));
+}
+
+void *ipu7_fw_com_get_token(struct ipu7_fw_com_context *ctx, int q)
+{
+ struct ipu7_fw_com_queue_config *queue_params = &ctx->queue_configs[q];
+ void __iomem *queue_indices = ipu7_fw_com_get_indices(ctx, q);
+ u32 write_index = readl(queue_indices +
+ offsetof(struct ipu7_fw_com_queue_indices,
+ write_index));
+ u32 read_index = readl(queue_indices +
+ offsetof(struct ipu7_fw_com_queue_indices,
+ read_index));
+ void *token = NULL;
+
+ if (q < ctx->num_output_queues) {
+ /* Output queue */
+ bool empty = (write_index == read_index);
+
+ if (!empty)
+ token = queue_params->token_array_mem +
+ read_index *
+ queue_params->token_size_in_bytes;
+ } else {
+ /* Input queue */
+ bool full = (read_index == ((write_index + 1U) %
+ (u32)queue_params->max_capacity));
+
+ if (!full)
+ token = queue_params->token_array_mem +
+ write_index * queue_params->token_size_in_bytes;
+ }
+ return token;
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_fw_com_get_token, "INTEL_IPU6");
+
+void ipu7_fw_com_put_token(struct ipu7_fw_com_context *ctx, int q)
+{
+ struct ipu7_fw_com_queue_config *queue_params = &ctx->queue_configs[q];
+ void __iomem *queue_indices = ipu7_fw_com_get_indices(ctx, q);
+ u32 offset, index;
+
+ if (q < ctx->num_output_queues)
+ /* Output queue */
+ offset = offsetof(struct ipu7_fw_com_queue_indices, read_index);
+
+ else
+ /* Input queue */
+ offset = offsetof(struct ipu7_fw_com_queue_indices, write_index);
+
+ index = readl(queue_indices + offset);
+ writel((index + 1U) % queue_params->max_capacity,
+ queue_indices + offset);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_fw_com_put_token, "INTEL_IPU6");
+
+struct ipu7_fw_com_queue_params_config *
+ipu7_fw_com_get_queue_config(struct ipu7_fw_com_config *config)
+{
+ return (struct ipu7_fw_com_queue_params_config *)(&config[1]);
+}
+EXPORT_SYMBOL_NS_GPL(ipu7_fw_com_get_queue_config, "INTEL_IPU6");
diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-com.h b/drivers/media/pci/intel/ipu6/ipu7-fw-com.h
new file mode 100644
index 000000000000..097eaab99547
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-fw-com.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2026 Intel Corporation */
+
+#ifndef IPU7_FW_COM_H
+#define IPU7_FW_COM_H
+
+#include <linux/types.h>
+
+struct ipu7_fw_com_queue_config {
+ void *token_array_mem;
+ u32 queue_size;
+ u16 token_size_in_bytes;
+ u16 max_capacity;
+};
+
+struct ipu7_fw_com_context {
+ u16 num_input_queues;
+ u16 num_output_queues;
+ struct ipu7_fw_com_queue_config *queue_configs;
+ void __iomem *queue_indices;
+ dma_addr_t queue_mem_dma_addr;
+ void *queue_mem;
+ u32 queue_mem_size;
+ struct ipu7_boot_abi_cfg *boot_config;
+ dma_addr_t boot_config_dma_addr;
+ u32 boot_config_size;
+ u32 fw_entry;
+ struct ipu7_insys_config *fw_config;
+ dma_addr_t fw_config_dma_addr;
+};
+
+struct ipu7_fw_com_queue_params_config {
+ u32 token_array_mem;
+ u16 token_size_in_bytes;
+ u16 max_capacity;
+};
+
+struct ipu7_fw_com_config {
+ u16 max_output_queues;
+ u16 max_input_queues;
+};
+
+struct ipu7_fw_com_queue_indices {
+ u32 read_index;
+ u32 write_index;
+};
+
+void ipu7_fw_com_put_token(struct ipu7_fw_com_context *ctx, int q);
+void *ipu7_fw_com_get_token(struct ipu7_fw_com_context *ctx, int q);
+struct ipu7_fw_com_queue_params_config *
+ipu7_fw_com_get_queue_config(struct ipu7_fw_com_config *config);
+
+#endif /* IPU7_FW_COM_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-isys.c b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.c
new file mode 100644
index 000000000000..0876cc54faa7
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.c
@@ -0,0 +1,796 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Intel Corporation
+ */
+
+#include <linux/cleanup.h>
+#include <linux/cacheflush.h>
+#include <linux/pm_runtime.h>
+
+#include "ipu6-bus.h"
+#include "ipu6-dma.h"
+#include "ipu6-isys.h"
+#include "ipu6-platform-regs.h"
+#include "ipu7-boot.h"
+#include "ipu7-fw-com.h"
+#include "ipu7-fw-isys.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-platform-regs.h"
+
+static void ipu7_fw_isys_cleanup(struct ipu6_isys *isys)
+{
+ struct ipu6_bus_device *adev = isys->adev;
+ struct ipu7_fw_com_context *fwctx = isys->fwctx;
+
+ if (!fwctx)
+ return;
+
+ ipu6_ipu7_release_boot_config(adev);
+
+ if (fwctx->fw_config) {
+ ipu6_dma_free(adev, sizeof(*fwctx->fw_config), fwctx->fw_config,
+ fwctx->fw_config_dma_addr, 0);
+ fwctx->fw_config = NULL;
+ fwctx->fw_config_dma_addr = 0;
+ }
+
+ isys->fwctx = NULL;
+}
+
+static int ipu7_fw_isys_open(struct ipu6_isys *isys)
+{
+ return ipu6_ipu7_boot_start_fw(isys->adev);
+}
+
+static int ipu7_fw_isys_close(struct ipu6_isys *isys)
+{
+ int ret;
+
+ ret = ipu6_ipu7_boot_stop_fw(isys->adev);
+ if (ret)
+ return ret;
+
+ ipu7_fw_isys_cleanup(isys);
+
+ return ret;
+}
+
+static int ipu7_fw_isys_init(struct ipu6_isys *isys, unsigned int num_streams)
+{
+ struct ipu7_fw_com_queue_config *queue_configs;
+ struct ipu6_bus_device *adev = isys->adev;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu7_insys_config *fw_config;
+ struct ipu7_fw_com_context *fwctx;
+ dma_addr_t fw_config_dma_addr;
+ unsigned int num_queues;
+ u32 freq;
+ int ret;
+
+ /* Allocate and init firmware context. */
+ fwctx = devm_kzalloc(dev, sizeof(struct ipu7_fw_com_context),
+ GFP_KERNEL);
+ if (!fwctx)
+ return -ENOMEM;
+
+ fwctx->num_input_queues = IPU7_INSYS_MAX_INPUT_QUEUES;
+ fwctx->num_output_queues = IPU7_INSYS_MAX_OUTPUT_QUEUES;
+ num_queues = fwctx->num_input_queues + fwctx->num_output_queues;
+
+ queue_configs = devm_kcalloc(dev, num_queues, sizeof(*queue_configs),
+ GFP_KERNEL);
+ if (!queue_configs) {
+ ipu7_fw_isys_cleanup(isys);
+ return -ENOMEM;
+ }
+ fwctx->fw_entry = adev->fw_entry;
+ fwctx->queue_configs = queue_configs;
+ queue_configs[IPU7_INSYS_OUTPUT_MSG_QUEUE].max_capacity =
+ IPU7_ISYS_SIZE_RECV_QUEUE;
+ queue_configs[IPU7_INSYS_OUTPUT_MSG_QUEUE].token_size_in_bytes =
+ sizeof(struct ipu7_insys_resp);
+ queue_configs[IPU7_INSYS_OUTPUT_LOG_QUEUE].max_capacity =
+ IPU7_ISYS_SIZE_LOG_QUEUE;
+ queue_configs[IPU7_INSYS_OUTPUT_LOG_QUEUE].token_size_in_bytes =
+ sizeof(struct ipu7_insys_resp);
+ queue_configs[IPU7_INSYS_OUTPUT_RESERVED_QUEUE].max_capacity = 0;
+ queue_configs[IPU7_INSYS_OUTPUT_RESERVED_QUEUE].token_size_in_bytes = 0;
+
+ queue_configs[IPU7_INSYS_INPUT_DEV_QUEUE].max_capacity =
+ IPU7_ISYS_MAX_STREAMS;
+ queue_configs[IPU7_INSYS_INPUT_DEV_QUEUE].token_size_in_bytes =
+ sizeof(struct ipu7_insys_send_queue_token);
+
+ for (unsigned int i = IPU7_INSYS_INPUT_MSG_QUEUE; i < num_queues; i++) {
+ queue_configs[i].max_capacity = IPU7_ISYS_SIZE_SEND_QUEUE;
+ queue_configs[i].token_size_in_bytes =
+ sizeof(struct ipu7_insys_send_queue_token);
+ }
+
+ /* Allocate ISYS subsys config. */
+ fw_config = ipu6_dma_alloc(adev, sizeof(*fw_config),
+ &fw_config_dma_addr, GFP_KERNEL, 0);
+ if (!fw_config) {
+ dev_err(dev, "Failed to allocate isys subsys config.\n");
+ ipu7_fw_isys_cleanup(isys);
+ return -ENOMEM;
+ }
+ fwctx->fw_config = fw_config;
+ fwctx->fw_config_dma_addr = fw_config_dma_addr;
+ memset(fw_config, 0, sizeof(*fw_config));
+ fw_config->logger_config.use_source_severity = 0;
+ fw_config->logger_config.use_channels_enable_bitmask = 1;
+ fw_config->logger_config.channels_enable_bitmask =
+ IPU7_LOGGER_CFG_CHANNEL_ENABLE_SYSCOM;
+ fw_config->logger_config.hw_printf_buffer_base_addr = 0U;
+ fw_config->logger_config.hw_printf_buffer_size_bytes = 0U;
+ fw_config->wdt_config.wdt_timer1_us = 0;
+ fw_config->wdt_config.wdt_timer2_us = 0;
+ freq = ipu7_buttress_get_isys_freq(adev->isp);
+
+ ipu6_dma_sync_single(adev, fw_config_dma_addr,
+ sizeof(struct ipu7_insys_config));
+
+ isys->fwctx = fwctx;
+
+ ret = ipu6_ipu7_init_boot_config(adev, queue_configs, num_queues,
+ freq, fw_config_dma_addr, 1U);
+ if (ret) {
+ ipu7_fw_isys_cleanup(isys);
+ return ret;
+ }
+
+ ret = ipu7_fw_isys_open(isys);
+ if (ret)
+ ipu7_fw_isys_cleanup(isys);
+
+ return ret;
+}
+
+static struct ipu7_insys_resp *ipu7_fw_isys_get_resp(struct ipu6_isys *isys)
+{
+ return ipu7_fw_com_get_token(isys->fwctx, IPU7_INSYS_OUTPUT_MSG_QUEUE);
+}
+
+static void ipu7_fw_isys_put_resp(struct ipu6_isys *isys)
+{
+ ipu7_fw_com_put_token(isys->fwctx, IPU7_INSYS_OUTPUT_MSG_QUEUE);
+}
+
+static int ipu7_isys_fw_pin_cfg(struct ipu6_isys_video *av,
+ struct ipu6_isys_stream *stream,
+ struct media_pad *src_pad,
+ struct v4l2_mbus_frame_desc_entry *entry,
+ void *__cfg)
+{
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(src_pad->entity);
+ struct v4l2_subdev_state *state = v4l2_subdev_get_locked_active_state(sd);
+ struct ipu7_fw_isys_stream_cfg *cfg = __cfg;
+ struct ipu7_fw_isys_input_pin *input_pin;
+ struct ipu7_fw_isys_output_pin *output_pin;
+ struct ipu6_isys_queue *aq = &av->aq;
+ struct v4l2_mbus_framefmt *fmt;
+ const struct ipu6_isys_pixelformat *pfmt =
+ ipu6_isys_get_isys_format(ipu6_isys_get_format(av), 0);
+ int input_pins = cfg->nof_input_pins++;
+ int output_pins;
+ u32 src_stream;
+
+ src_stream = __ipu6_isys_get_src_stream_by_src_pad(state, src_pad->index);
+ fmt = v4l2_subdev_state_get_format(state, src_pad->index, src_stream);
+
+ input_pin = &cfg->input_pins[input_pins];
+ input_pin->input_res.width = fmt->width;
+ input_pin->input_res.height = fmt->height;
+ input_pin->dt = entry->bus.csi2.dt;
+ input_pin->disable_mipi_unpacking = 0;
+ if (pfmt->bpp == pfmt->bpp_packed && pfmt->bpp % BITS_PER_BYTE)
+ input_pin->disable_mipi_unpacking = 1;
+ input_pin->mapped_dt = IPU7_N_INSYS_MIPI_DATA_TYPE;
+ input_pin->dt_rename_mode = IPU7_INSYS_MIPI_DT_NO_RENAME;
+ input_pin->sync_msg_map =
+ IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF |
+ IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED |
+ IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF |
+ IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED;
+
+ output_pins = cfg->nof_output_pins++;
+ aq->fw_output = output_pins;
+ av->stream->output_pins_queue[output_pins] = aq;
+
+ output_pin = &cfg->output_pins[output_pins];
+ memset(output_pin, 0, sizeof(*output_pin));
+ output_pin->link.buffer_lines = 0;
+ output_pin->link.foreign_key = IPU7_MSG_LINK_FOREIGN_KEY_NONE;
+ output_pin->link.pbk_id = IPU7_MSG_LINK_PBK_ID_DONT_CARE;
+ output_pin->link.pbk_slot_id = IPU7_MSG_LINK_PBK_SLOT_ID_DONT_CARE;
+ output_pin->link.dest = 0; /* IPU_INSYS_OUTPUT_LINK_DEST_MEM */
+ output_pin->link.use_sw_managed = 1;
+ output_pin->crop.line_top = 0;
+ output_pin->crop.line_bottom = 0;
+ output_pin->dpcm.enable = 0;
+ output_pin->ft = pfmt->css_pixelformat;
+ output_pin->stride = ipu6_isys_get_bytes_per_line(av);
+ output_pin->send_irq = 1;
+ output_pin->input_pin_id = input_pins;
+
+ return 0;
+}
+
+static int
+ipu7_fw_isys_send_cmd(struct ipu6_isys *isys, const unsigned int stream_handle,
+ void *cpu_mapped_buf, dma_addr_t dma_mapped_buf,
+ size_t size, u16 send_type)
+{
+ struct ipu7_fw_com_context *ctx = isys->fwctx;
+ /*struct device *dev = &isys->adev->auxdev.dev;*/
+ struct ipu7_insys_send_queue_token *token;
+
+ if (send_type >= N_IPU7_INSYS_SEND_TYPE)
+ return -EINVAL;
+
+ if (cpu_mapped_buf)
+ clflush_cache_range(cpu_mapped_buf, size);
+
+ token = ipu7_fw_com_get_token(ctx, stream_handle +
+ IPU7_INSYS_INPUT_MSG_QUEUE);
+ if (!token)
+ return -EBUSY;
+
+ token->addr = dma_mapped_buf;
+ token->buf_handle = (unsigned long)cpu_mapped_buf;
+ token->send_type = send_type;
+ token->stream_id = stream_handle;
+ token->flag = IPU7_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE;
+
+ ipu7_fw_com_put_token(ctx, stream_handle + IPU7_INSYS_INPUT_MSG_QUEUE);
+ /* now wakeup FW */
+ ipu7_buttress_wakeup_isys(isys->adev->isp);
+
+ return 0;
+}
+
+static void ipu7_fw_isys_dump_stream_cfg(struct device *dev,
+ struct isys_fw_msgs *msg)
+{
+ struct ipu7_fw_isys_stream_cfg *cfg = &msg->ipu7.stream;
+ unsigned int i;
+
+ dev_dbg(dev, "---------------------------\n");
+ dev_dbg(dev, "IPU_FW_ISYS_STREAM_CFG_DATA\n");
+
+ dev_dbg(dev, ".port id %d\n", cfg->port_id);
+ dev_dbg(dev, ".vc %d\n", cfg->vc);
+ dev_dbg(dev, ".nof_input_pins = %d\n", cfg->nof_input_pins);
+ dev_dbg(dev, ".nof_output_pins = %d\n", cfg->nof_output_pins);
+ dev_dbg(dev, ".stream_msg_map = 0x%x\n", cfg->stream_msg_map);
+
+ for (i = 0; i < cfg->nof_input_pins; i++) {
+ dev_dbg(dev, ".input_pin[%d]:\n", i);
+ dev_dbg(dev, "\t.dt = 0x%0x\n",
+ cfg->input_pins[i].dt);
+ dev_dbg(dev, "\t.disable_mipi_unpacking = %d\n",
+ cfg->input_pins[i].disable_mipi_unpacking);
+ dev_dbg(dev, "\t.dt_rename_mode = %d\n",
+ cfg->input_pins[i].dt_rename_mode);
+ dev_dbg(dev, "\t.mapped_dt = 0x%0x\n",
+ cfg->input_pins[i].mapped_dt);
+ dev_dbg(dev, "\t.input_res = %d x %d\n",
+ cfg->input_pins[i].input_res.width,
+ cfg->input_pins[i].input_res.height);
+ dev_dbg(dev, "\t.sync_msg_map = 0x%x\n",
+ cfg->input_pins[i].sync_msg_map);
+ }
+
+ for (i = 0; i < cfg->nof_output_pins; i++) {
+ dev_dbg(dev, ".output_pin[%d]:\n", i);
+ dev_dbg(dev, "\t.input_pin_id = %d\n",
+ cfg->output_pins[i].input_pin_id);
+ dev_dbg(dev, "\t.stride = %d\n", cfg->output_pins[i].stride);
+ dev_dbg(dev, "\t.send_irq = %d\n",
+ cfg->output_pins[i].send_irq);
+ dev_dbg(dev, "\t.ft = %d\n", cfg->output_pins[i].ft);
+
+ dev_dbg(dev, "\t.link.buffer_lines = %d\n",
+ cfg->output_pins[i].link.buffer_lines);
+ dev_dbg(dev, "\t.link.foreign_key = %d\n",
+ cfg->output_pins[i].link.foreign_key);
+ dev_dbg(dev, "\t.link.granularity_pointer_update = %d\n",
+ cfg->output_pins[i].link.granularity_pointer_update);
+ dev_dbg(dev, "\t.link.msg_link_streaming_mode = %d\n",
+ cfg->output_pins[i].link.msg_link_streaming_mode);
+ dev_dbg(dev, "\t.link.pbk_id = %d\n",
+ cfg->output_pins[i].link.pbk_id);
+ dev_dbg(dev, "\t.link.pbk_slot_id = %d\n",
+ cfg->output_pins[i].link.pbk_slot_id);
+ dev_dbg(dev, "\t.link.dest = %d\n",
+ cfg->output_pins[i].link.dest);
+ dev_dbg(dev, "\t.link.use_sw_managed = %d\n",
+ cfg->output_pins[i].link.use_sw_managed);
+ dev_dbg(dev, "\t.link.is_snoop = %d\n",
+ cfg->output_pins[i].link.is_snoop);
+
+ dev_dbg(dev, "\t.crop.line_top = %d\n",
+ cfg->output_pins[i].crop.line_top);
+ dev_dbg(dev, "\t.crop.line_bottom = %d\n",
+ cfg->output_pins[i].crop.line_bottom);
+
+ dev_dbg(dev, "\t.dpcm_enable = %d\n",
+ cfg->output_pins[i].dpcm.enable);
+ dev_dbg(dev, "\t.dpcm.type = %d\n",
+ cfg->output_pins[i].dpcm.type);
+ dev_dbg(dev, "\t.dpcm.predictor = %d\n",
+ cfg->output_pins[i].dpcm.predictor);
+ }
+ dev_dbg(dev, "---------------------------\n");
+}
+
+static void ipu7_fw_isys_dump_frame_buf_set(struct device *dev,
+ struct isys_fw_msgs *msg,
+ unsigned int outputs)
+{
+ struct ipu7_fw_isys_frame_buff_set *buf = &msg->ipu7.frame;
+
+ dev_dbg(dev, "--------------------------\n");
+ dev_dbg(dev, "IPU_ISYS_BUFF_SET\n");
+ dev_dbg(dev, ".capture_msg_map = %d\n", buf->capture_msg_map);
+ dev_dbg(dev, ".frame_id = %d\n", buf->frame_id);
+ dev_dbg(dev, ".skip_frame = %d\n", buf->skip_frame);
+
+ for (unsigned int i = 0; i < outputs; i++) {
+ dev_dbg(dev, ".output_pin[%d]:\n", i);
+ dev_dbg(dev, "\t.user_token = %llx\n",
+ buf->output_pins[i].user_token);
+ dev_dbg(dev, "\t.addr = 0x%x\n", buf->output_pins[i].addr);
+ }
+ dev_dbg(dev, "---------------------------\n");
+}
+
+static int ipu7_fw_isys_prepare_stream_cfg(struct ipu6_isys_stream *stream,
+ struct v4l2_mbus_frame_desc *desc,
+ struct isys_fw_msgs *msg)
+{
+ struct ipu7_fw_isys_stream_cfg *cfg = &msg->ipu7.stream;
+ struct device *dev = &stream->isys->adev->auxdev.dev;
+ int ret;
+
+ memset(cfg, 0, sizeof(*cfg));
+ cfg->port_id = stream->asd->source;
+ cfg->vc = stream->vc;
+ cfg->stream_msg_map = IPU7_INSYS_STREAM_ENABLE_MSG_SEND_RESP |
+ IPU7_INSYS_STREAM_ENABLE_MSG_SEND_IRQ;
+
+ ret = ipu6_isys_fw_pins_prepare(stream, desc, ipu7_isys_fw_pin_cfg,
+ cfg);
+ if (ret)
+ return ret;
+
+ stream->nr_output_pins = cfg->nof_output_pins;
+
+ ipu7_fw_isys_dump_stream_cfg(dev, msg);
+
+ return 0;
+}
+
+static void
+ipu7_isys_buf_to_fw_frame_buf_pin(struct vb2_buffer *vb,
+ struct ipu7_fw_isys_frame_buff_set *set)
+{
+ struct ipu6_isys_queue *aq = vb2_queue_to_isys_queue(vb->vb2_queue);
+ struct vb2_v4l2_buffer *vvb = to_vb2_v4l2_buffer(vb);
+ struct ipu6_isys_video_buffer *ivb =
+ vb2_buffer_to_ipu6_isys_video_buffer(vvb);
+
+ set->output_pins[aq->fw_output].addr = ivb->dma_addr;
+ set->output_pins[aq->fw_output].user_token = (u64)(uintptr_t)set;
+}
+
+static void
+ipu7_fw_isys_prepare_buf_set(struct isys_fw_msgs *msg,
+ struct ipu6_isys_stream *stream,
+ struct ipu6_isys_buffer_list *bl)
+{
+ struct ipu7_fw_isys_frame_buff_set *set = &msg->ipu7.frame;
+ struct ipu6_isys_buffer *ib;
+
+ WARN_ON(!bl->nbufs);
+
+ memset(set, 0, sizeof(*set));
+ set->capture_msg_map = IPU7_INSYS_FRAME_ENABLE_MSG_SEND_RESP |
+ IPU7_INSYS_FRAME_ENABLE_MSG_SEND_IRQ;
+ set->frame_id = atomic_fetch_inc(&stream->buf_id) % 256;
+
+ list_for_each_entry(ib, &bl->head, head) {
+ struct vb2_buffer *vb = ipu6_isys_buffer_to_vb2_buffer(ib);
+
+ ipu7_isys_buf_to_fw_frame_buf_pin(vb, set);
+ }
+
+ dev_dbg(&stream->isys->adev->auxdev.dev,
+ "ipu7 frame_buf: cap_map=0x%x fid=%u opin[0].addr=0x%x token=0x%llx\n",
+ set->capture_msg_map, set->frame_id,
+ set->output_pins[0].addr, set->output_pins[0].user_token);
+}
+
+static int ipu7_fw_isys_stream_open(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg)
+{
+ return ipu7_fw_isys_send_cmd(isys, stream_handle, &msg->ipu7.stream,
+ msg->dma_addr, sizeof(msg->ipu7.stream),
+ IPU7_INSYS_SEND_TYPE_STREAM_OPEN);
+}
+
+static int ipu7_fw_isys_stream_close(struct ipu6_isys *isys,
+ const unsigned int stream_handle)
+{
+ return ipu7_fw_isys_send_cmd(isys, stream_handle, NULL, 0, 0,
+ IPU7_INSYS_SEND_TYPE_STREAM_CLOSE);
+}
+
+static int ipu7_fw_isys_stream_flush(struct ipu6_isys *isys,
+ const unsigned int stream_handle)
+{
+ return ipu7_fw_isys_send_cmd(isys, stream_handle, NULL, 0, 0,
+ IPU7_INSYS_SEND_TYPE_STREAM_FLUSH);
+}
+
+static int ipu7_fw_isys_stream_start(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg, bool capture)
+{
+ return ipu7_fw_isys_send_cmd(isys, stream_handle, &msg->ipu7.frame,
+ msg->dma_addr, sizeof(msg->ipu7.frame),
+ IPU7_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE);
+}
+
+static int ipu7_fw_isys_stream_capture(struct ipu6_isys *isys,
+ const unsigned int stream_handle,
+ struct isys_fw_msgs *msg)
+{
+ return ipu7_fw_isys_send_cmd(isys, stream_handle, &msg->ipu7.frame,
+ msg->dma_addr, sizeof(msg->ipu7.frame),
+ IPU7_INSYS_SEND_TYPE_STREAM_CAPTURE);
+}
+
+const struct ipu6_fw_isys_ops ipu7_fw_isys_ops = {
+ .init = ipu7_fw_isys_init,
+ .close = ipu7_fw_isys_close,
+ .send_cmd = ipu7_fw_isys_send_cmd,
+ .cleanup = ipu7_fw_isys_cleanup,
+ .prepare_stream_cfg = ipu7_fw_isys_prepare_stream_cfg,
+ .prepare_buf_set = ipu7_fw_isys_prepare_buf_set,
+ .stream_open = ipu7_fw_isys_stream_open,
+ .stream_start = ipu7_fw_isys_stream_start,
+ .stream_capture = ipu7_fw_isys_stream_capture,
+ .stream_flush = ipu7_fw_isys_stream_flush,
+ .stream_close = ipu7_fw_isys_stream_close,
+ .dump_stream_cfg = ipu7_fw_isys_dump_stream_cfg,
+ .dump_frame_buf_set = ipu7_fw_isys_dump_frame_buf_set,
+};
+
+static const struct ipu7_csi2_error {
+ const char *error_string;
+ bool is_info_only;
+} dphy_rx_errors[] = {
+ { "Error handler FIFO full", false },
+ { "Reserved Short Packet encoding detected", true },
+ { "Reserved Long Packet encoding detected", true },
+ { "Received packet is too short", false},
+ { "Received packet is too long", false},
+ { "Short packet discarded due to errors", false },
+ { "Long packet discarded due to errors", false },
+ { "CSI Combo Rx interrupt", false },
+ { "IDI CDC FIFO overflow(remaining bits are reserved as 0)", false },
+ { "Received NULL packet", true },
+ { "Received blanking packet", true },
+ { "Tie to 0", true },
+};
+
+static void ipu7_isys_register_errors(struct ipu6_isys_csi2 *csi2)
+{
+ u32 offset = IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(csi2->port);
+ u32 status = readl(csi2->base + offset + IPU7_IRQ_CTL_STATUS);
+ u32 mask = IPU7_CSI_RX_ERROR_IRQ_MASK;
+
+ if (!status)
+ return;
+
+ dev_dbg(&csi2->isys->adev->auxdev.dev, "csi2-%u error status 0x%08x\n",
+ csi2->port, status);
+
+ writel(status & mask, csi2->base + offset + IPU7_IRQ_CTL_CLEAR);
+ csi2->receiver_errors |= status & mask;
+}
+
+static void ipu7_isys_csi2_error(struct ipu6_isys_csi2 *csi2)
+{
+ u32 status;
+
+ /* Register errors once more in case of error interrupts are disabled */
+ ipu7_isys_register_errors(csi2);
+ status = csi2->receiver_errors;
+ csi2->receiver_errors = 0;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(dphy_rx_errors); i++) {
+ if (status & BIT(i))
+ dev_err_ratelimited(&csi2->isys->adev->auxdev.dev,
+ "csi2-%i error: %s\n",
+ csi2->port,
+ dphy_rx_errors[i].error_string);
+ }
+}
+
+static const struct resp_to_msg {
+ enum ipu7_insys_resp_type type;
+ const char *msg;
+} is_fw_msg[] = {
+ { IPU7_INSYS_RESP_TYPE_STREAM_OPEN_DONE, "STREAM_OPEN_DONE" },
+ { IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK,
+ "STREAM_START_AND_CAPTURE_ACK" },
+ { IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK, "STREAM_CAPTURE_ACK" },
+ { IPU7_INSYS_RESP_TYPE_STREAM_ABORT_ACK, "STREAM_ABORT_ACK" },
+ { IPU7_INSYS_RESP_TYPE_STREAM_FLUSH_ACK, "STREAM_FLUSH_ACK" },
+ { IPU7_INSYS_RESP_TYPE_STREAM_CLOSE_ACK, "STREAM_CLOSE_ACK" },
+ { IPU7_INSYS_RESP_TYPE_PIN_DATA_READY, "PIN_DATA_READY" },
+ { IPU7_INSYS_RESP_TYPE_FRAME_SOF, "FRAME_SOF" },
+ { IPU7_INSYS_RESP_TYPE_FRAME_EOF, "FRAME_EOF" },
+ { IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE,
+ "STREAM_START_AND_CAPTURE_DONE" },
+ { IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE, "STREAM_CAPTURE_DONE" },
+ { N_IPU7_INSYS_RESP_TYPE, "N_IPU7_INSYS_RESP_TYPE" },
+};
+
+static int ipu7_isys_isr_one(struct ipu6_bus_device *adev)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ struct ipu6_isys_stream *stream = NULL;
+ struct device *dev = &adev->auxdev.dev;
+ struct ipu6_isys_csi2 *csi2 = NULL;
+ struct ipu7_fw_isys_msg_err err_info;
+ struct isys_fw_msgs *isys_fw_msg;
+ struct ipu7_insys_resp *resp;
+ unsigned long flags;
+ u64 ts;
+
+ resp = ipu7_fw_isys_get_resp(isys);
+ if (!resp)
+ return 1;
+
+ if (resp->type >= N_IPU7_INSYS_RESP_TYPE) {
+ dev_err(dev, "Unknown response type %u stream %u\n",
+ resp->type, resp->stream_id);
+ ipu7_fw_isys_put_resp(isys);
+ return 1;
+ }
+
+ err_info = resp->error_info;
+ ts = ((u64)resp->timestamp[1] << 32) | resp->timestamp[0];
+
+ if (err_info.err_group == INSYS_MSG_ERR_GROUP_CAPTURE &&
+ err_info.err_code == INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP) {
+ /* receive a sp w/o command, firmware drop it */
+ dev_dbg(dev, "FRAME DROP: %02u %s stream %u\n",
+ resp->type, is_fw_msg[resp->type].msg,
+ resp->stream_id);
+ dev_dbg(dev, "\tpin %u buf_id %llx frame %u\n",
+ resp->pin_id, resp->buf_id, resp->frame_id);
+ dev_dbg(dev, "\terror group %u code %u details [%u %u]\n",
+ err_info.err_group, err_info.err_code,
+ err_info.err_detail[0], err_info.err_detail[1]);
+ } else if (err_info.err_code) {
+ dev_err(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n",
+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id,
+ resp->pin_id, resp->buf_id, resp->frame_id);
+ dev_err(dev, "\terror group %u code %u details [%u %u]\n",
+ err_info.err_group, err_info.err_code,
+ err_info.err_detail[0], err_info.err_detail[1]);
+ } else {
+ dev_dbg(dev, "%02u %s stream %u pin %u buf_id %llx frame %u\n",
+ resp->type, is_fw_msg[resp->type].msg, resp->stream_id,
+ resp->pin_id, resp->buf_id, resp->frame_id);
+ dev_dbg(dev, "\tts %llu\n", ts);
+ }
+
+ if (resp->stream_id >= IPU7_ISYS_MAX_STREAMS) {
+ dev_err(dev, "bad stream handle %u\n",
+ resp->stream_id);
+ goto leave_nounlock;
+ }
+
+ spin_lock_irqsave(&isys->streams_lock, flags);
+
+ stream = resp->stream_id < IPU6_ISYS_MAX_STREAMS ?
+ isys->streams_by_handle[resp->stream_id] : NULL;
+ if (!stream) {
+ dev_err(dev, "stream of stream_handle %u is unused\n",
+ resp->stream_id);
+ goto leave;
+ }
+
+ stream->error = err_info.err_code;
+
+ if (stream->asd)
+ csi2 = ipu6_isys_subdev_to_csi2(stream->asd);
+
+ switch (resp->type) {
+ case IPU7_INSYS_RESP_TYPE_STREAM_OPEN_DONE:
+ complete(&stream->stream_open_completion);
+ break;
+ case IPU7_INSYS_RESP_TYPE_STREAM_CLOSE_ACK:
+ complete(&stream->stream_close_completion);
+ break;
+ case IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK:
+ complete(&stream->stream_start_completion);
+ break;
+ case IPU7_INSYS_RESP_TYPE_STREAM_ABORT_ACK:
+ complete(&stream->stream_stop_completion);
+ break;
+ case IPU7_INSYS_RESP_TYPE_STREAM_FLUSH_ACK:
+ complete(&stream->stream_stop_completion);
+ break;
+ case IPU7_INSYS_RESP_TYPE_PIN_DATA_READY:
+ /*
+ * firmware only release the capture msg until software
+ * get pin_data_ready event
+ */
+ isys_fw_msg = container_of((void *)(uintptr_t)resp->buf_id,
+ struct isys_fw_msgs, dummy);
+
+ ipu6_put_fw_msg_buf(ipu6_bus_get_drvdata(adev), isys_fw_msg);
+ if (resp->pin_id < IPU6_ISYS_OUTPUT_PINS)
+ ipu6_stream_buf_ready(stream, resp->pin_id,
+ resp->pin.addr, ts, 0);
+ else
+ dev_err(dev, "No handler for pin %u ready\n",
+ resp->pin_id);
+ if (csi2)
+ ipu7_isys_csi2_error(csi2);
+
+ break;
+ case IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK:
+ break;
+ case IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE:
+ case IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE:
+ break;
+ case IPU7_INSYS_RESP_TYPE_FRAME_SOF:
+ if (csi2)
+ ipu6_isys_csi2_sof_event_by_stream(stream);
+
+ stream->seq[stream->seq_index].sequence =
+ atomic_read(&stream->sequence) - 1U;
+ stream->seq[stream->seq_index].timestamp = ts;
+ dev_dbg(dev,
+ "SOF: stream %u frame %u (index %u), ts 0x%16.16llx\n",
+ resp->stream_id, resp->frame_id,
+ stream->seq[stream->seq_index].sequence, ts);
+ stream->seq_index = (stream->seq_index + 1U)
+ % IPU6_ISYS_MAX_PARALLEL_SOF;
+ break;
+ case IPU7_INSYS_RESP_TYPE_FRAME_EOF:
+ if (csi2)
+ ipu6_isys_csi2_eof_event_by_stream(stream);
+
+ dev_dbg(dev, "eof: stream %d(index %u) ts 0x%16.16llx\n",
+ resp->stream_id,
+ stream->seq[stream->seq_index].sequence, ts);
+ break;
+ default:
+ dev_err(dev, "Unknown response type %u stream %u\n",
+ resp->type, resp->stream_id);
+ break;
+ }
+
+leave:
+ spin_unlock_irqrestore(&isys->streams_lock, flags);
+
+leave_nounlock:
+ ipu7_fw_isys_put_resp(isys);
+
+ return 0;
+}
+
+#define IPU7_NR_OF_CSI2_VC 16U
+static void ipu7_isys_csi2_isr(struct ipu6_isys_csi2 *csi2)
+{
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct ipu6_device *isp = csi2->isys->adev->isp;
+ struct ipu6_isys_stream *s;
+ u32 sync, offset;
+ u32 fe = 0;
+ u8 vc;
+
+ ipu7_isys_register_errors(csi2);
+
+ offset = IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(csi2->port);
+ sync = readl(csi2->base + offset + IPU7_IRQ_CTL_STATUS);
+ writel(sync, csi2->base + offset + IPU7_IRQ_CTL_CLEAR);
+ dev_dbg(dev, "csi2-%u sync status 0x%08x\n", csi2->port, sync);
+
+ if (!IS_IPU7_MTL(isp)) {
+ fe = readl(csi2->base + offset + IPU7_IRQ1_CTL_STATUS);
+ writel(fe, csi2->base + offset + IPU7_IRQ1_CTL_CLEAR);
+ dev_dbg(dev, "csi2-%u FE status 0x%08x\n", csi2->port, fe);
+ }
+
+ for (vc = 0; vc < IPU7_NR_OF_CSI2_VC && (sync || fe); vc++) {
+ s = csi2->streams_by_vc[vc];
+ if (!s)
+ continue;
+
+ if (!IS_IPU7_MTL(isp)) {
+ if (sync & IPU7P5_CSI_RX_SYNC_FS_VC & (1U << vc))
+ ipu6_isys_csi2_sof_event_by_stream(s);
+
+ if (fe & IPU7P5_CSI_RX_SYNC_FE_VC & (1U << vc))
+ ipu6_isys_csi2_eof_event_by_stream(s);
+ } else {
+ if (sync & IPU7_CSI_RX_SYNC_FS_VC & (1U << (vc * 2)))
+ ipu6_isys_csi2_sof_event_by_stream(s);
+
+ if (sync & IPU7_CSI_RX_SYNC_FE_VC & (2U << (vc * 2)))
+ ipu6_isys_csi2_eof_event_by_stream(s);
+ }
+ }
+}
+
+static void ipu7_dispatch_csi2_isr(struct ipu6_isys *isys, u32 status)
+{
+ for (unsigned int i = 0; i < isys->pdata->ipdata->csi2.nports; i++) {
+ if (!isys->csi2[i].base)
+ continue;
+ if (status & isys->csi2[i].legacy_irq_mask)
+ ipu7_isys_csi2_isr(&isys->csi2[i]);
+ }
+}
+
+irqreturn_t ipu7_isys_isr(struct ipu6_bus_device *adev)
+{
+ struct ipu6_isys *isys = ipu6_bus_get_drvdata(adev);
+ void __iomem *base = isys->pdata->base;
+ u32 status_sw, status_csi;
+ u32 csi_offset, sw_offset;
+ int pm_status;
+
+ pm_status = pm_runtime_get_if_active(&adev->auxdev.dev);
+ if (!pm_status)
+ return 0;
+
+ csi_offset = IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE;
+ sw_offset = IPU7_IS_UC_CTRL_BASE;
+
+ status_csi = readl(base + csi_offset + IPU7_IRQ_CTL_STATUS);
+ status_sw = readl(base + sw_offset + IPU7_TO_SW_IRQ_CNTL_STATUS);
+
+ if (!status_csi && !status_sw) {
+ if (pm_status > 0)
+ pm_runtime_put(&adev->auxdev.dev);
+ return IRQ_NONE;
+ }
+
+ do {
+ writel(status_sw, base + sw_offset + IPU7_TO_SW_IRQ_CNTL_CLEAR);
+ writel(status_csi, base + csi_offset + IPU7_IRQ_CTL_CLEAR);
+
+ if (isys->isr_csi2_bits & status_csi)
+ ipu7_dispatch_csi2_isr(isys, status_csi);
+
+ if (!ipu7_isys_isr_one(adev))
+ status_sw = IPU7_TO_SW_IRQ_FW;
+ else
+ status_sw = 0;
+
+ status_csi = readl(base + csi_offset + IPU7_IRQ_CTL_STATUS);
+ status_sw |= readl(base + sw_offset +
+ IPU7_TO_SW_IRQ_CNTL_STATUS);
+ } while ((status_csi & isys->isr_csi2_bits) ||
+ (status_sw & IPU7_TO_SW_IRQ_FW));
+
+ writel(IPU7_IS_UC_TO_SW_IRQ_MASK,
+ base + sw_offset + IPU7_TO_SW_IRQ_CNTL_MASK_N);
+
+ if (pm_status > 0)
+ pm_runtime_put(&adev->auxdev.dev);
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu7-fw-isys.h b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.h
new file mode 100644
index 000000000000..d5289f6add8c
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-fw-isys.h
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2026 Intel Corporation */
+
+#ifndef IPU7_FW_ISYS_H
+#define IPU7_FW_ISYS_H
+
+#define IPU7_FWLOG_MAX_LOGGER_SOURCES (64U)
+#define IPU7_INSYS_MAX_OUTPUT_QUEUES 3U
+#define IPU7_INSYS_STREAM_ID_MAX 16U
+#define IPU7_INSYS_MAX_INPUT_QUEUES (IPU7_INSYS_STREAM_ID_MAX + 1U)
+#define IPU7_INSYS_OUTPUT_MSG_QUEUE 0U
+#define IPU7_INSYS_OUTPUT_LOG_QUEUE 1U
+#define IPU7_INSYS_OUTPUT_RESERVED_QUEUE 2U
+#define IPU7_INSYS_INPUT_DEV_QUEUE 3U
+
+#define IPU7_INSYS_INPUT_FIRST_QUEUE 3U
+#define IPU7_INSYS_INPUT_MSG_QUEUE 4U
+#define IPU7_INSYS_INPUT_MSG_MAX_QUEUE 16U
+
+#define IPU7_MSG_ERR_MAX_DETAILS 4U
+#define IPU7_ISYS_SIZE_RECV_QUEUE 40U
+#define IPU7_ISYS_SIZE_LOG_QUEUE 256U
+#define IPU7_ISYS_SIZE_SEND_QUEUE 40U
+#define IPU7_ISYS_NUM_RECV_QUEUE 1U
+#define IPU7_INSYS_SEND_QUEUE_TOKEN_FLAG_NONE 0U
+
+#define IPU7_LOGGER_CFG_CHANNEL_ENABLE_SYSCOM BIT(1)
+
+#define IPU7_ISYS_MAX_STREAMS 16U
+#define IPU7_MAX_OPINS 4
+#define IPU7_MAX_IPINS 4
+
+#define IPU7_N_INSYS_MIPI_DATA_TYPE 0x40
+
+#define IPU7_MSG_LINK_FOREIGN_KEY_NONE (65535U)
+#define IPU7_MSG_LINK_PBK_ID_DONT_CARE (255U)
+#define IPU7_MSG_LINK_PBK_SLOT_ID_DONT_CARE (255U)
+
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF BIT(0)
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF BIT(1)
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF BIT(2)
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF BIT(3)
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_SOF_DISCARDED BIT(4)
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_RESP_EOF_DISCARDED BIT(5)
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_SOF_DISCARDED BIT(6)
+#define IPU7_INSYS_STREAM_SYNC_MSG_SEND_IRQ_EOF_DISCARDED BIT(7)
+
+#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE BIT(0)
+#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE BIT(1)
+#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK BIT(2)
+#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK BIT(3)
+#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK BIT(4)
+#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK BIT(5)
+#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK BIT(6)
+#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK BIT(7)
+#define IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK BIT(8)
+#define IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK BIT(9)
+
+#define IPU7_INSYS_STREAM_ENABLE_MSG_SEND_RESP ( \
+ IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_OPEN_DONE | \
+ IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_START_ACK | \
+ IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_CLOSE_ACK | \
+ IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_FLUSH_ACK | \
+ IPU7_INSYS_STREAM_MSG_SEND_RESP_STREAM_ABORT_ACK)
+#define IPU7_INSYS_STREAM_ENABLE_MSG_SEND_IRQ ( \
+ IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_OPEN_DONE | \
+ IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_START_ACK | \
+ IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_CLOSE_ACK | \
+ IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_FLUSH_ACK | \
+ IPU7_INSYS_STREAM_MSG_SEND_IRQ_STREAM_ABORT_ACK)
+
+#define IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK BIT(0)
+#define IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK BIT(1)
+#define IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE BIT(2)
+#define IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE BIT(3)
+#define IPU7_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY BIT(4)
+#define IPU7_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY BIT(5)
+
+#define IPU7_INSYS_FRAME_ENABLE_MSG_SEND_RESP ( \
+ IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_ACK | \
+ IPU7_INSYS_FRAME_MSG_SEND_RESP_CAPTURE_DONE | \
+ IPU7_INSYS_FRAME_MSG_SEND_RESP_PIN_DATA_READY)
+#define IPU7_INSYS_FRAME_ENABLE_MSG_SEND_IRQ ( \
+ IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_ACK | \
+ IPU7_INSYS_FRAME_MSG_SEND_IRQ_CAPTURE_DONE | \
+ IPU7_INSYS_FRAME_MSG_SEND_IRQ_PIN_DATA_READY)
+
+enum ipu7_insys_send_type {
+ IPU7_INSYS_SEND_TYPE_STREAM_OPEN = 0,
+ IPU7_INSYS_SEND_TYPE_STREAM_START_AND_CAPTURE = 1,
+ IPU7_INSYS_SEND_TYPE_STREAM_CAPTURE = 2,
+ IPU7_INSYS_SEND_TYPE_STREAM_ABORT = 3,
+ IPU7_INSYS_SEND_TYPE_STREAM_FLUSH = 4,
+ IPU7_INSYS_SEND_TYPE_STREAM_CLOSE = 5,
+ N_IPU7_INSYS_SEND_TYPE
+};
+
+enum ipu7_insys_resp_type {
+ IPU7_INSYS_RESP_TYPE_STREAM_OPEN_DONE = 0,
+ IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_ACK = 1,
+ IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_ACK = 2,
+ IPU7_INSYS_RESP_TYPE_STREAM_ABORT_ACK = 3,
+ IPU7_INSYS_RESP_TYPE_STREAM_FLUSH_ACK = 4,
+ IPU7_INSYS_RESP_TYPE_STREAM_CLOSE_ACK = 5,
+ IPU7_INSYS_RESP_TYPE_PIN_DATA_READY = 6,
+ IPU7_INSYS_RESP_TYPE_FRAME_SOF = 7,
+ IPU7_INSYS_RESP_TYPE_FRAME_EOF = 8,
+ IPU7_INSYS_RESP_TYPE_STREAM_START_AND_CAPTURE_DONE = 9,
+ IPU7_INSYS_RESP_TYPE_STREAM_CAPTURE_DONE = 10,
+ IPU7_INSYS_RESP_TYPE_PWM_IRQ = 11,
+ N_IPU7_INSYS_RESP_TYPE
+};
+
+enum ipu7_insys_mipi_dt_rename_mode {
+ IPU7_INSYS_MIPI_DT_NO_RENAME = 0,
+ IPU7_INSYS_MIPI_DT_RENAMED_MODE = 1,
+ N_IPU7_INSYS_MIPI_DT_MODE
+};
+
+enum insys_msg_err_capture {
+ INSYS_MSG_ERR_CAPTURE_OK = 0,
+ INSYS_MSG_ERR_CAPTURE_STREAM_ID = 1,
+ INSYS_MSG_ERR_CAPTURE_PAYLOAD_PTR = 2,
+ INSYS_MSG_ERR_CAPTURE_MEM_SLOT = 3,
+ INSYS_MSG_ERR_CAPTURE_STREAMING_MODE = 4,
+ INSYS_MSG_ERR_CAPTURE_AVAILABLE_CMD_SLOT = 5,
+ INSYS_MSG_ERR_CAPTURE_CONSUMED_CMD_SLOT = 6,
+ INSYS_MSG_ERR_CAPTURE_CMD_SLOT_PAYLOAD_PTR = 7,
+ INSYS_MSG_ERR_CAPTURE_CMD_PREPARE = 8,
+ INSYS_MSG_ERR_CAPTURE_OUTPUT_PIN = 9,
+ INSYS_MSG_ERR_CAPTURE_SYNC_FRAME_DROP = 10,
+ INSYS_MSG_ERR_CAPTURE_FRAME_MESSAGES_MAP = 11,
+ INSYS_MSG_ERR_CAPTURE_TIMEOUT = 12,
+ INSYS_MSG_ERR_CAPTURE_INVALID_STREAM_STATE = 13,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MULTIBIT_PH_ERROR_DETECTED = 14,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PAYLOAD_CRC_ERROR = 15,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_INPUT_DATA_LOSS_ELASTIC_FIFO_OVFL = 16,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PIXEL_BUFFER_OVERFLOW = 17,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_BAD_FRAME_DIM = 18,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_PHY_SYNC_ERR = 19,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_SECURE_TOUCH = 20,
+ INSYS_MSG_ERR_CAPTURE_HW_ERR_MASTER_SLAVE_SYNC_ERR = 21,
+ INSYS_MSG_ERR_CAPTURE_FRAME_SKIP_ERR = 22,
+ INSYS_MSG_ERR_CAPTURE_FE_INPUT_FIFO_OVERFLOW_ERR = 23,
+ INSYS_MSG_ERR_CAPTURE_CMD_SUBMIT_TO_HW = 24,
+ INSYS_MSG_ERR_CAPTURE_N
+};
+
+enum insys_msg_err_groups {
+ INSYS_MSG_ERR_GROUP_RESERVED = 0,
+ INSYS_MSG_ERR_GROUP_GENERAL = 1,
+ INSYS_MSG_ERR_GROUP_STREAM = 2,
+ INSYS_MSG_ERR_GROUP_CAPTURE = 3,
+ INSYS_MSG_ERR_GROUP_N,
+};
+
+struct ipu7_fw_isys_logger_config {
+ u8 use_source_severity;
+ u8 source_severity[IPU7_FWLOG_MAX_LOGGER_SOURCES];
+ u8 use_channels_enable_bitmask;
+ u8 channels_enable_bitmask;
+ u8 padding[1];
+ u32 hw_printf_buffer_base_addr;
+ u32 hw_printf_buffer_size_bytes;
+};
+
+struct ipu7_wdt_abi {
+ u32 wdt_timer1_us;
+ u32 wdt_timer2_us;
+};
+
+struct ipu7_insys_config {
+ u32 timeout_val_ms;
+ struct ipu7_fw_isys_logger_config logger_config;
+ struct ipu7_wdt_abi wdt_config;
+};
+
+struct ipu7_insys_capture_output_pin_payload {
+ u64 user_token;
+ u32 addr;
+ u8 pad[4];
+};
+
+struct ipu7_fw_isys_msg_err {
+ u32 err_group;
+ u32 err_code;
+ u32 err_detail[IPU7_MSG_ERR_MAX_DETAILS];
+};
+
+struct ipu7_insys_resp {
+ u64 buf_id;
+ struct ipu7_insys_capture_output_pin_payload pin;
+ struct ipu7_fw_isys_msg_err error_info;
+ u32 timestamp[2];
+ u8 type;
+ u8 msg_link_streaming_mode;
+ u8 stream_id;
+ u8 pin_id;
+ u8 frame_id;
+ u8 skip_frame;
+ u8 pad[2];
+};
+
+struct ipu7_insys_resp_queue_token {
+ struct ipu7_insys_resp resp_info;
+};
+
+struct ipu7_insys_send_queue_token {
+ u64 buf_handle;
+ u32 addr;
+ u16 stream_id;
+ u8 send_type;
+ u8 flag;
+};
+
+struct ipu7_fw_isys_output_link {
+ u32 buffer_lines;
+ u16 foreign_key;
+ u16 granularity_pointer_update;
+ u8 msg_link_streaming_mode;
+ u8 pbk_id;
+ u8 pbk_slot_id;
+ u8 dest;
+ u8 use_sw_managed;
+ u8 is_snoop;
+ u8 pad[2];
+} __packed;
+
+struct ipu7_fw_isys_output_cropping {
+ u16 line_top;
+ u16 line_bottom;
+} __packed;
+
+struct ipu7_fw_isys_output_dpcm {
+ u8 enable;
+ u8 type;
+ u8 predictor;
+ u8 pad;
+} __packed;
+
+struct ipu7_fw_isys_output_pin {
+ struct ipu7_fw_isys_output_link link;
+ struct ipu7_fw_isys_output_cropping crop;
+ struct ipu7_fw_isys_output_dpcm dpcm;
+ u32 stride;
+ u16 ft;
+ u8 send_irq;
+ u8 input_pin_id;
+ u8 early_ack_en;
+ u8 pad[3];
+} __packed;
+
+struct ipu7_fw_isys_resolution {
+ u32 width;
+ u32 height;
+} __packed;
+
+struct ipu7_fw_isys_input_pin {
+ struct ipu7_fw_isys_resolution input_res;
+ u16 sync_msg_map;
+ u8 dt;
+ u8 disable_mipi_unpacking;
+ u8 dt_rename_mode;
+ u8 mapped_dt;
+ u8 pad[2];
+} __packed;
+
+struct ipu7_fw_isys_stream_cfg {
+ struct ipu7_fw_isys_input_pin input_pins[IPU7_MAX_IPINS];
+ struct ipu7_fw_isys_output_pin output_pins[IPU7_MAX_OPINS];
+ u16 stream_msg_map;
+ u8 port_id;
+ u8 vc;
+ u8 nof_input_pins;
+ u8 nof_output_pins;
+ u8 pad[2];
+} __packed;
+
+struct ipu7_fw_isys_capture_output_pin {
+ u64 user_token;
+ u32 addr;
+ u8 pad[4];
+} __packed;
+
+struct ipu7_fw_isys_frame_buff_set {
+ struct ipu7_fw_isys_capture_output_pin output_pins[IPU7_MAX_OPINS];
+ u8 capture_msg_map;
+ u8 frame_id;
+ u8 skip_frame;
+ u8 pad[5];
+} __packed;
+
+struct ipu6_fw_isys_ops *ipu7_fw_isys_get_ops(void);
+irqreturn_t ipu7_isys_isr(struct ipu6_bus_device *adev);
+
+#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.c b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.c
new file mode 100644
index 000000000000..10273c687faa
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.c
@@ -0,0 +1,1072 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 - 2026 Intel Corporation
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bug.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-device.h>
+
+#include "ipu6.h"
+#include "ipu6-bus.h"
+#include "ipu6-isys.h"
+#include "ipu6-isys-csi2.h"
+#include "ipu7-isys-csi2-regs.h"
+#include "ipu7-isys-csi-phy.h"
+
+#define PORT_A 0U
+#define PORT_B 1U
+#define PORT_C 2U
+#define PORT_D 3U
+
+#define N_DATA_IDS 8U
+static DECLARE_BITMAP(data_ids, N_DATA_IDS);
+
+struct ddlcal_counter_ref_s {
+ u16 min_mbps;
+ u16 max_mbps;
+
+ u16 ddlcal_counter_ref;
+};
+
+struct ddlcal_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 oa_lanex_hsrx_cdphy_sel_fast;
+ u16 ddlcal_max_phase;
+ u16 phase_bound;
+ u16 ddlcal_dll_fbk;
+ u16 ddlcal_ddl_coarse_bank;
+ u16 fjump_deskew;
+ u16 min_eye_opening_deskew;
+};
+
+struct i_thssettle_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 i_thssettle;
+};
+
+/* lane2 for 4l3t, lane1 for 2l2t */
+struct oa_lane_clk_div_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 oa_lane_hsrx_hs_clk_div;
+};
+
+struct cdr_fbk_cap_prog_params {
+ u16 min_mbps;
+ u16 max_mbps;
+ u16 val;
+};
+
+static const struct ddlcal_counter_ref_s table0[] = {
+ { 1500, 1999, 118 },
+ { 2000, 2499, 157 },
+ { 2500, 3499, 196 },
+ { 3500, 4499, 274 },
+ { 4500, 4500, 352 },
+ { }
+};
+
+static const struct ddlcal_params table1[] = {
+ { 1500, 1587, 0, 143, 167, 17, 3, 4, 29 },
+ { 1588, 1687, 0, 135, 167, 15, 3, 4, 27 },
+ { 1688, 1799, 0, 127, 135, 15, 2, 4, 26 },
+ { 1800, 1928, 0, 119, 135, 13, 2, 3, 24 },
+ { 1929, 2076, 0, 111, 135, 13, 2, 3, 23 },
+ { 2077, 2249, 0, 103, 135, 11, 2, 3, 21 },
+ { 2250, 2454, 0, 95, 103, 11, 1, 3, 19 },
+ { 2455, 2699, 0, 87, 103, 9, 1, 3, 18 },
+ { 2700, 2999, 0, 79, 103, 9, 1, 2, 16 },
+ { 3000, 3229, 0, 71, 71, 7, 1, 2, 15 },
+ { 3230, 3599, 1, 87, 103, 9, 1, 3, 18 },
+ { 3600, 3999, 1, 79, 103, 9, 1, 2, 16 },
+ { 4000, 4499, 1, 71, 103, 7, 1, 2, 15 },
+ { 4500, 4500, 1, 63, 71, 7, 0, 2, 13 },
+ { }
+};
+
+static const struct i_thssettle_params table2[] = {
+ { 80, 124, 24 },
+ { 125, 249, 20 },
+ { 250, 499, 16 },
+ { 500, 749, 14 },
+ { 750, 1499, 13 },
+ { 1500, 4500, 12 },
+ { }
+};
+
+static const struct oa_lane_clk_div_params table6[] = {
+ { 80, 159, 0x1 },
+ { 160, 319, 0x2 },
+ { 320, 639, 0x3 },
+ { 640, 1279, 0x4 },
+ { 1280, 2560, 0x5 },
+ { 2561, 4500, 0x6 },
+ { }
+};
+
+static const struct cdr_fbk_cap_prog_params table7[] = {
+ { 80, 919, 0 },
+ { 920, 1029, 1 },
+ { 1030, 1169, 2 },
+ { 1170, 1349, 3 },
+ { 1350, 1589, 4 },
+ { 1590, 1949, 5 },
+ { 1950, 2499, 6 },
+ { 2500, 3500, 7 },
+ { }
+};
+
+static void dwc_phy_write(struct ipu6_isys *isys, u32 id, u32 addr, u16 data)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU7_IS_IO_CDPHY_BASE(id);
+
+ dev_dbg(&isys->adev->auxdev.dev, "phy write: reg 0x%lx = data 0x%04x",
+ (unsigned long)(base + addr - isys_base), data);
+ writew(data, base + addr);
+}
+
+static u16 dwc_phy_read(struct ipu6_isys *isys, u32 id, u32 addr)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU7_IS_IO_CDPHY_BASE(id);
+ u16 data;
+
+ data = readw(base + addr);
+ dev_dbg(&isys->adev->auxdev.dev, "phy read: reg 0x%lx = data 0x%04x",
+ (unsigned long)(base + addr - isys_base), data);
+
+ return data;
+}
+
+static void dwc_csi_write(struct ipu6_isys *isys, u32 id, u32 addr, u32 data)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU7_IS_IO_CSI2_HOST_BASE(id);
+ struct device *dev = &isys->adev->auxdev.dev;
+
+ dev_dbg(dev, "csi write: reg 0x%lx = data 0x%08x",
+ (unsigned long)(base + addr - isys_base), data);
+ writel(data, base + addr);
+ dev_dbg(dev, "csi read: reg 0x%lx = data 0x%08x",
+ (unsigned long)(base + addr - isys_base),
+ readl(base + addr));
+}
+
+static void gpreg_write(struct ipu6_isys *isys, u32 id, u32 addr, u32 data)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ u32 gpreg = isys->pdata->ipdata->csi2.gpreg;
+ void __iomem *base = isys_base + gpreg + 0x1000 * id;
+ struct device *dev = &isys->adev->auxdev.dev;
+
+ dev_dbg(dev, "gpreg write: reg 0x%lx = data 0x%08x",
+ (unsigned long)(base + addr - isys_base), data);
+ writel(data, base + addr);
+ dev_dbg(dev, "gpreg read: reg 0x%lx = data 0x%08x",
+ (unsigned long)(base + addr - isys_base),
+ readl(base + addr));
+}
+
+static u32 dwc_csi_read(struct ipu6_isys *isys, u32 id, u32 addr)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ void __iomem *base = isys_base + IPU7_IS_IO_CSI2_HOST_BASE(id);
+ u32 data;
+
+ data = readl(base + addr);
+ dev_dbg(&isys->adev->auxdev.dev, "csi read: reg 0x%lx = data 0x%x",
+ (unsigned long)(base + addr - isys_base), data);
+
+ return data;
+}
+
+static void dwc_phy_write_mask(struct ipu6_isys *isys, u32 id, u32 addr,
+ u16 val, u8 lo, u8 hi)
+{
+ u32 temp, mask;
+
+ WARN_ON(lo > hi);
+ WARN_ON(hi > 15);
+
+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi));
+ temp = dwc_phy_read(isys, id, addr);
+ temp &= ~mask;
+ temp |= (val << lo) & mask;
+ dwc_phy_write(isys, id, addr, temp);
+}
+
+static void dwc_csi_write_mask(struct ipu6_isys *isys, u32 id, u32 addr,
+ u32 val, u8 hi, u8 lo)
+{
+ u32 temp, mask;
+
+ WARN_ON(lo > hi);
+
+ mask = ((~0U - (1U << lo) + 1U)) & (~0U >> (31 - hi));
+ temp = dwc_csi_read(isys, id, addr);
+ temp &= ~mask;
+ temp |= (val << lo) & mask;
+ dwc_csi_write(isys, id, addr, temp);
+}
+
+static void ipu7_isys_csi_ctrl_cfg(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ struct device *dev = &isys->adev->auxdev.dev;
+ u32 id, lanes, phy_mode;
+ u32 val;
+
+ id = csi2->port;
+ lanes = csi2->nlanes;
+ phy_mode = csi2->phy_mode;
+ dev_dbg(dev, "csi-%d controller init with %u lanes, phy mode %u",
+ id, lanes, phy_mode);
+
+ val = dwc_csi_read(isys, id, IPU7_VERSION);
+ dev_dbg(dev, "csi-%d controller version = 0x%x", id, val);
+
+ /* num of active data lanes */
+ dwc_csi_write(isys, id, IPU7_N_LANES, lanes - 1);
+ dwc_csi_write(isys, id, IPU7_CDPHY_MODE, phy_mode);
+ dwc_csi_write(isys, id, IPU7_VC_EXTENSION, 0);
+
+ /* only mask PHY_FATAL and PKT_FATAL interrupts */
+ dwc_csi_write(isys, id, IPU7_INT_MSK_PHY_FATAL, 0xff);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_PKT_FATAL, 0x3);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_PHY, 0x0);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_LINE, 0x0);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_BNDRY_FRAME_FATAL, 0x0);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_SEQ_FRAME_FATAL, 0x0);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_CRC_FRAME_FATAL, 0x0);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_PLD_CRC_FATAL, 0x0);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_DATA_ID, 0x0);
+ dwc_csi_write(isys, id, IPU7_INT_MSK_ECC_CORRECTED, 0x0);
+}
+
+static void ipu7_isys_csi_phy_reset(struct ipu6_isys *isys, u32 id)
+{
+ dwc_csi_write(isys, id, IPU7_PHY_SHUTDOWNZ, 0);
+ dwc_csi_write(isys, id, IPU7_DPHY_RSTZ, 0);
+ dwc_csi_write(isys, id, IPU7_CSI2_RESETN, 0);
+ gpreg_write(isys, id, IPU7_PHY_RESET, 0);
+ gpreg_write(isys, id, IPU7_PHY_SHUTDOWN, 0);
+}
+
+/* 8 Data ID monitors, each Data ID is composed by pair of VC and data type */
+static int __dids_config(struct ipu6_isys_csi2 *csi2, u32 id, u8 vc, u8 dt)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ u32 reg, n;
+ u8 lo, hi;
+ int ret;
+
+ dev_dbg(&isys->adev->auxdev.dev,
+ "config CSI-%u with vc:%u dt:0x%02x\n", id, vc, dt);
+
+ dwc_csi_write(isys, id, IPU7_VC_EXTENSION, 0x0);
+ n = find_first_zero_bit(data_ids, N_DATA_IDS);
+ if (n == N_DATA_IDS)
+ return -ENOSPC;
+
+ ret = test_and_set_bit(n, data_ids);
+ if (ret)
+ return -EBUSY;
+
+ reg = n < 4 ? IPU7_DATA_IDS_VC_1 : IPU7_DATA_IDS_VC_2;
+ lo = (n % 4) * 8;
+ hi = lo + 4;
+ dwc_csi_write_mask(isys, id, reg, vc & GENMASK(4, 0), hi, lo);
+
+ reg = n < 4 ? IPU7_DATA_IDS_1 : IPU7_DATA_IDS_2;
+ lo = (n % 4) * 8;
+ hi = lo + 5;
+ dwc_csi_write_mask(isys, id, reg, dt & GENMASK(5, 0), hi, lo);
+
+ return 0;
+}
+
+static int ipu7_isys_csi_ctrl_dids_config(struct ipu6_isys_csi2 *csi2, u32 id)
+{
+ struct v4l2_mbus_frame_desc_entry *desc_entry = NULL;
+ struct device *dev = &csi2->isys->adev->auxdev.dev;
+ struct v4l2_mbus_frame_desc desc;
+ struct v4l2_subdev *ext_sd;
+ struct media_pad *pad;
+ int ret;
+
+ pad = media_entity_remote_source_pad_unique(&csi2->asd.sd.entity);
+ if (IS_ERR(pad)) {
+ dev_warn(dev, "can't get remote source pad of %s (%pe)\n",
+ csi2->asd.sd.name, pad);
+ return PTR_ERR(pad);
+ }
+
+ ext_sd = media_entity_to_v4l2_subdev(pad->entity);
+ if (WARN(!ext_sd, "Failed to get subdev for entity %s\n",
+ pad->entity->name))
+ return -ENODEV;
+
+ ret = v4l2_subdev_call(ext_sd, pad, get_frame_desc, pad->index, &desc);
+ if (ret)
+ return ret;
+
+ if (desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
+ dev_warn(dev, "Unsupported frame descriptor type\n");
+ return -EINVAL;
+ }
+
+ for (unsigned int i = 0; i < desc.num_entries; i++) {
+ desc_entry = &desc.entry[i];
+ if (desc_entry->bus.csi2.vc < NR_OF_CSI2_VC) {
+ ret = __dids_config(csi2, id, desc_entry->bus.csi2.vc,
+ desc_entry->bus.csi2.dt);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#define CDPHY_TIMEOUT 5000000U
+static int ipu7_isys_phy_ready(struct ipu6_isys *isys, u32 id)
+{
+ void __iomem *isys_base = isys->pdata->base;
+ u32 gpreg_offset = isys->pdata->ipdata->csi2.gpreg;
+ void __iomem *gpreg = isys_base + gpreg_offset + 0x1000 * id;
+ struct device *dev = &isys->adev->auxdev.dev;
+ u32 phy_ready;
+ u32 reg, rext;
+ int ret;
+
+ dev_dbg(dev, "waiting phy ready...\n");
+ ret = readl_poll_timeout(gpreg + IPU7_PHY_READY, phy_ready,
+ phy_ready & BIT(0) && phy_ready != ~0U,
+ 100, CDPHY_TIMEOUT);
+ dev_dbg(dev, "phy %u ready = 0x%08x\n",
+ id, readl(gpreg + IPU7_PHY_READY));
+ dev_dbg(dev, "csi %u IPU7_PHY_RX = 0x%08x\n", id,
+ dwc_csi_read(isys, id, IPU7_PHY_RX));
+ dev_dbg(dev, "csi %u IPU7_PHY_STOPSTATE = 0x%08x\n", id,
+ dwc_csi_read(isys, id, IPU7_PHY_STOPSTATE));
+ dev_dbg(dev, "csi %u IPU7_PHY_CAL = 0x%08x\n", id,
+ dwc_csi_read(isys, id, IPU7_PHY_CAL));
+ for (unsigned int i = 0; i < 4U; i++) {
+ reg = IPU7_CORE_DIG_DLANE_0_R_HS_RX_0 + (i * 0x400U);
+ dev_dbg(dev, "phy %u DLANE%u skewcal = 0x%04x\n",
+ id, i, dwc_phy_read(isys, id, reg));
+ }
+ dev_dbg(dev, "phy %u DDLCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id,
+ IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5));
+ dev_dbg(dev, "phy %u TERMCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id, IPU7_PPI_R_TERMCAL_DEBUG_0));
+ dev_dbg(dev, "phy %u LPDCOCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id, IPU7_PPI_R_LPDCOCAL_DEBUG_RB));
+ dev_dbg(dev, "phy %u HSDCOCAL = 0x%04x\n", id,
+ dwc_phy_read(isys, id, IPU7_PPI_R_HSDCOCAL_DEBUG_RB));
+ dev_dbg(dev, "phy %u LPDCOCAL_VT = 0x%04x\n", id,
+ dwc_phy_read(isys, id, IPU7_PPI_R_LPDCOCAL_DEBUG_VT));
+
+ if (!ret) {
+ if (id) {
+ dev_dbg(dev, "ignore phy %u rext\n", id);
+ return 0;
+ }
+
+ rext = dwc_phy_read(isys, id,
+ IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15) &
+ 0xfU;
+ dev_dbg(dev, "phy %u rext value = %u\n", id, rext);
+ isys->phy_rext_cal = (rext ? rext : 5);
+
+ return 0;
+ }
+
+ dev_err(dev, "wait phy ready timeout!\n");
+
+ return ret;
+}
+
+static int lookup_table1(u64 mbps)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(table1); i++) {
+ if (mbps >= table1[i].min_mbps && mbps <= table1[i].max_mbps)
+ return i;
+ }
+
+ return -ENXIO;
+}
+
+static const u16 deskew_fine_mem[] = {
+ 0x0404, 0x040c, 0x0414, 0x041c,
+ 0x0423, 0x0429, 0x0430, 0x043a,
+ 0x0445, 0x044a, 0x0450, 0x045a,
+ 0x0465, 0x0469, 0x0472, 0x047a,
+ 0x0485, 0x0489, 0x0490, 0x049a,
+ 0x04a4, 0x04ac, 0x04b4, 0x04bc,
+ 0x04c4, 0x04cc, 0x04d4, 0x04dc,
+ 0x04e4, 0x04ec, 0x04f4, 0x04fc,
+ 0x0504, 0x050c, 0x0514, 0x051c,
+ 0x0523, 0x0529, 0x0530, 0x053a,
+ 0x0545, 0x054a, 0x0550, 0x055a,
+ 0x0565, 0x0569, 0x0572, 0x057a,
+ 0x0585, 0x0589, 0x0590, 0x059a,
+ 0x05a4, 0x05ac, 0x05b4, 0x05bc,
+ 0x05c4, 0x05cc, 0x05d4, 0x05dc,
+ 0x05e4, 0x05ec, 0x05f4, 0x05fc,
+ 0x0604, 0x060c, 0x0614, 0x061c,
+ 0x0623, 0x0629, 0x0632, 0x063a,
+ 0x0645, 0x064a, 0x0650, 0x065a,
+ 0x0665, 0x0669, 0x0672, 0x067a,
+ 0x0685, 0x0689, 0x0690, 0x069a,
+ 0x06a4, 0x06ac, 0x06b4, 0x06bc,
+ 0x06c4, 0x06cc, 0x06d4, 0x06dc,
+ 0x06e4, 0x06ec, 0x06f4, 0x06fc,
+ 0x0704, 0x070c, 0x0714, 0x071c,
+ 0x0723, 0x072a, 0x0730, 0x073a,
+ 0x0745, 0x074a, 0x0750, 0x075a,
+ 0x0765, 0x0769, 0x0772, 0x077a,
+ 0x0785, 0x0789, 0x0790, 0x079a,
+ 0x07a4, 0x07ac, 0x07b4, 0x07bc,
+ 0x07c4, 0x07cc, 0x07d4, 0x07dc,
+ 0x07e4, 0x07ec, 0x07f4, 0x07fc,
+};
+
+static void ipu7_isys_dphy_config(struct ipu6_isys *isys, u8 id, u8 lanes,
+ bool aggregation, u64 mbps)
+{
+ struct ipu6_device *isp = isys->adev->isp;
+ u16 hsrxval0 = 0;
+ u16 hsrxval1 = 0;
+ u16 hsrxval2 = 0;
+ int index;
+ u16 reg;
+ u16 val;
+ u32 i;
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_7, 0, 0, 9);
+ if (mbps > 1500)
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_7,
+ 40, 0, 7);
+ else
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_7,
+ 104, 0, 7);
+
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_8,
+ 80, 0, 7);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_0, 191, 0, 9);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_7, 34, 7, 12);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_1, 38, 8, 15);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 4, 12, 15);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 2, 10, 11);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 1, 8, 8);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 38, 0, 7);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_2, 1, 9, 9);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_4, 10, 0, 9);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_6, 20, 0, 9);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_7, 19, 0, 6);
+
+ for (i = 0; i < ARRAY_SIZE(table0); i++) {
+ if (mbps >= table0[i].min_mbps && mbps <= table0[i].max_mbps) {
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_3,
+ table0[i].ddlcal_counter_ref,
+ 0, 9);
+ break;
+ }
+ }
+
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_1,
+ table1[index].phase_bound, 0, 7);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_5,
+ table1[index].ddlcal_dll_fbk, 4, 9);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_DDLCAL_CFG_5,
+ table1[index].ddlcal_ddl_coarse_bank, 0, 3);
+
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8;
+ val = table1[index].oa_lanex_hsrx_cdphy_sel_fast;
+ for (i = 0; i < lanes + 1; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+ 12, 12);
+ }
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_LP_0;
+ for (i = 0; i < lanes; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2,
+ 0, 0, 0);
+ if (!IS_IPU7_MTL(isp) || id == PORT_B || id == PORT_C) {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2,
+ 1, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2,
+ 0, 0, 0);
+ } else {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2,
+ 0, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2,
+ 1, 0, 0);
+ }
+
+ if (lanes == 4 && IS_IPU7_MTL(isp)) {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2,
+ 0, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2,
+ 0, 0, 0);
+ }
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_6, 1, 0, 2);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_6, 1, 3, 5);
+
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12;
+ val = (mbps > 1500) ? 0 : 1;
+ for (i = 0; i < lanes + 1; i++) {
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), !val, 3, 3);
+ }
+
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13;
+ val = (mbps > 1500) ? 0 : 1;
+ for (i = 0; i < lanes + 1; i++) {
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 3, 3);
+ }
+
+ if (!IS_IPU7_MTL(isp) || id == PORT_B || id == PORT_C)
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9;
+ else
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9;
+
+ for (i = 0; i < ARRAY_SIZE(table6); i++) {
+ if (mbps >= table6[i].min_mbps && mbps <= table6[i].max_mbps) {
+ dwc_phy_write_mask(isys, id, reg,
+ table6[i].oa_lane_hsrx_hs_clk_div,
+ 5, 7);
+ break;
+ }
+ }
+
+ if (aggregation) {
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_0, 1,
+ 1, 1);
+
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15;
+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4);
+
+ val = (id == PORT_A) ? 3 : 0;
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15;
+ dwc_phy_write_mask(isys, id, reg, val, 3, 4);
+
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15;
+ dwc_phy_write_mask(isys, id, reg, 3, 3, 4);
+ }
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_0,
+ 28, 0, 7);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_7,
+ 6, 0, 7);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_0;
+ for (i = 0; i < ARRAY_SIZE(table2); i++) {
+ if (mbps >= table2[i].min_mbps && mbps <= table2[i].max_mbps) {
+ u8 j;
+
+ for (j = 0; j < lanes; j++)
+ dwc_phy_write_mask(isys, id, reg + (j * 0x400),
+ table2[i].i_thssettle,
+ 8, 15);
+ break;
+ }
+ }
+
+ /* deskew */
+ for (i = 0; i < lanes; i++) {
+ reg = IPU7_CORE_DIG_DLANE_0_RW_CFG_1;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+ ((mbps > 1500) ? 0x1 : 0x2), 2, 3);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_2;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+ ((mbps > 2500) ? 0 : 1), 15, 15);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 13, 13);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 7, 9, 12);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_LP_0;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 12, 15);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_LP_2;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 0);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_1;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 16, 0, 7);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_3;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 2);
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ val = table1[index].fjump_deskew;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+ 3, 8);
+ }
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_4;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 150, 0, 15);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_5;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 0, 7);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 8, 15);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_6;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 2, 0, 7);
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ val = table1[index].min_eye_opening_deskew;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val,
+ 8, 15);
+ }
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_7;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 13, 13);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 0, 15, 15);
+
+ reg = IPU7_CORE_DIG_DLANE_0_RW_HS_RX_9;
+ index = lookup_table1(mbps);
+ if (index >= 0) {
+ val = table1[index].ddlcal_max_phase;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400),
+ val, 0, 7);
+ }
+ }
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_LP_0,
+ 1, 12, 15);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_DLANE_CLK_RW_LP_2, 0, 0, 0);
+
+ for (i = 0; i < ARRAY_SIZE(deskew_fine_mem); i++)
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM,
+ deskew_fine_mem[i], 0, 15);
+
+ if (mbps > 1500) {
+ hsrxval0 = 4;
+ hsrxval2 = 3;
+ }
+
+ if (mbps > 2500)
+ hsrxval1 = 2;
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9,
+ hsrxval0, 0, 2);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9,
+ hsrxval0, 0, 2);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9,
+ hsrxval0, 0, 2);
+ if (lanes == 4 && IS_IPU7_MTL(isp)) {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9,
+ hsrxval0, 0, 2);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9,
+ hsrxval0, 0, 2);
+ }
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9,
+ hsrxval1, 3, 4);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9,
+ hsrxval1, 3, 4);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9,
+ hsrxval1, 3, 4);
+ if (lanes == 4 && IS_IPU7_MTL(isp)) {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9,
+ hsrxval1, 3, 4);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9,
+ hsrxval1, 3, 4);
+ }
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15,
+ hsrxval2, 0, 2);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15,
+ hsrxval2, 0, 2);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15,
+ hsrxval2, 0, 2);
+ if (lanes == 4 && IS_IPU7_MTL(isp)) {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15,
+ hsrxval2, 0, 2);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15,
+ hsrxval2, 0, 2);
+ }
+
+ /* force and override rext */
+ if (isys->phy_rext_cal && id) {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8,
+ isys->phy_rext_cal, 0, 3);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
+ 1, 11, 11);
+ }
+}
+
+static void ipu7_isys_cphy_config(struct ipu6_isys *isys, u8 id, u8 lanes,
+ bool aggregation, u64 mbps)
+{
+ struct ipu6_device *isp = isys->adev->isp;
+ u8 trios = 2;
+ u16 coarse_target;
+ u16 deass_thresh;
+ u16 delay_thresh;
+ u16 reset_thresh;
+ u16 cap_prog = 6U;
+ u16 reg;
+ u16 val;
+ u32 i;
+ u64 r64;
+ u32 r;
+
+ if (IS_IPU7P5(isp))
+ val = 0x15;
+ else
+ val = 0x155;
+
+ if (IS_IPU7_MTL(isp))
+ trios = 3;
+
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_RW_COMMON_7, val, 0, 9);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_7,
+ 104, 0, 7);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_8,
+ 16, 0, 7);
+
+ reg = IPU7_CORE_DIG_CLANE_0_RW_LP_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 6, 8, 11);
+
+ val = (mbps > 900U) ? 1U : 0U;
+ for (i = 0; i < trios; i++) {
+ reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_0;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 1, 0, 0);
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), val, 1, 1);
+
+ reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_1;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15);
+
+ reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_5;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 38, 0, 15);
+
+ reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_6;
+ dwc_phy_write_mask(isys, id, reg + (i * 0x400), 10, 0, 15);
+ }
+
+ /*
+ * Below 900Msps, always use the same value.
+ * The formula is suitable for data rate 80-3500Msps.
+ * Timebase (us) = 1, DIV = 32, TDDL (UI) = 0.5
+ */
+ if (mbps >= 80U)
+ coarse_target = DIV_ROUND_UP_ULL(mbps, 16) - 1;
+ else
+ coarse_target = 56;
+
+ for (i = 0; i < trios; i++) {
+ reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_2 + i * 0x400;
+ dwc_phy_write_mask(isys, id, reg, coarse_target, 0, 15);
+ }
+
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2, 1, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2, 0, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2, 1, 0, 0);
+
+ if (!IS_IPU7P5(isp) && lanes == 4) {
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2,
+ 1, 0, 0);
+ dwc_phy_write_mask(isys, id,
+ IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2,
+ 0, 0, 0);
+ }
+
+ for (i = 0; i < trios; i++) {
+ reg = IPU7_CORE_DIG_RW_TRIO0_0 + i * 0x400;
+ dwc_phy_write_mask(isys, id, reg, 1, 6, 8);
+ dwc_phy_write_mask(isys, id, reg, 1, 3, 5);
+ dwc_phy_write_mask(isys, id, reg, 2, 0, 2);
+ }
+
+ deass_thresh = (u16)div64_u64_rem(7 * 1000 * 6, mbps * 5U, &r64) + 1;
+ if (r64 != 0)
+ deass_thresh++;
+
+ reg = IPU7_CORE_DIG_RW_TRIO0_2;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+ deass_thresh, 0, 7);
+
+ delay_thresh = div64_u64((224U - (9U * 7U)) * 1000U, 5U * mbps) - 7u;
+
+ if (delay_thresh < 1)
+ delay_thresh = 1;
+
+ reg = IPU7_CORE_DIG_RW_TRIO0_1;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+ delay_thresh, 0, 15);
+
+ reset_thresh = (u16)div_u64_rem(2U * 5U * mbps, 7U * 1000U, &r);
+ if (!r)
+ reset_thresh--;
+
+ if (reset_thresh < 1)
+ reset_thresh = 1;
+
+ reg = IPU7_CORE_DIG_RW_TRIO0_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i,
+ reset_thresh, 9, 11);
+
+ /* Tuning ITMINRX to 2 for CPHY */
+ reg = IPU7_CORE_DIG_CLANE_0_RW_LP_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 2, 12, 15);
+
+ reg = IPU7_CORE_DIG_CLANE_0_RW_LP_2;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 0, 0, 0);
+
+ reg = IPU7_CORE_DIG_CLANE_0_RW_HS_RX_0;
+ for (i = 0; i < trios; i++)
+ dwc_phy_write_mask(isys, id, reg + 0x400 * i, 12, 2, 6);
+
+ for (i = 0; i < ARRAY_SIZE(table7); i++) {
+ if (mbps >= table7[i].min_mbps && mbps <= table7[i].max_mbps) {
+ cap_prog = table7[i].val;
+ break;
+ }
+ }
+
+ for (i = 0; i < (lanes + 1); i++) {
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 + 0x400 * i;
+ dwc_phy_write_mask(isys, id, reg, 4U, 0, 2);
+ /* Set GMODE to 2 when CPHY >= 1.5Gsps */
+ if (mbps >= 1500)
+ dwc_phy_write_mask(isys, id, reg, 2U, 3, 4);
+ else
+ dwc_phy_write_mask(isys, id, reg, 0U, 3, 4);
+
+ reg = IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 + 0x400 * i;
+ dwc_phy_write_mask(isys, id, reg, cap_prog, 10, 12);
+ }
+}
+
+static int ipu7_isys_phy_config(struct ipu6_isys *isys, u8 id, u8 lanes,
+ bool aggregation)
+{
+ struct device *dev = &isys->adev->auxdev.dev;
+ u32 phy_mode;
+ s64 link_freq;
+ u64 mbps;
+
+ if (aggregation)
+ link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[0]);
+ else
+ link_freq = ipu6_isys_csi2_get_link_freq(&isys->csi2[id]);
+
+ if (link_freq < 0) {
+ dev_err(dev, "get link freq failed (%lld)\n", link_freq);
+ return link_freq;
+ }
+
+ mbps = div_u64(link_freq, 500000);
+ dev_dbg(dev, "config phy %u with lanes %u aggregation %d mbps %lld\n",
+ id, lanes, aggregation, mbps);
+
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_10,
+ 48, 0, 7);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2,
+ 1, 12, 13);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0,
+ 63, 2, 7);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_STARTUP_1_1,
+ 563, 0, 11);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_2,
+ 5, 0, 7);
+ /* bypass the RCAL state (bit6) */
+ if (aggregation && id != PORT_A)
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_2,
+ 0x45, 0, 7);
+
+ dwc_phy_write_mask(isys, id, IPU7_PPI_STARTUP_RW_COMMON_DPHY_6,
+ 39, 0, 7);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_CALIBCTRL_RW_COMMON_BG_0,
+ 500, 0, 8);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_TERMCAL_CFG_0, 38, 0, 6);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_OFFSETCAL_CFG_0, 7, 0, 4);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_TIMEBASE, 153, 0, 9);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_NREF, 800, 0, 10);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_NREF_RANGE,
+ 27, 0, 4);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_TWAIT_CONFIG,
+ 47, 0, 8);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_TWAIT_CONFIG,
+ 127, 9, 15);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_VT_CONFIG, 47, 7, 15);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_VT_CONFIG, 27, 2, 6);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_VT_CONFIG, 3, 0, 1);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_LPDCOCAL_COARSE_CFG, 1, 0, 1);
+ dwc_phy_write_mask(isys, id, IPU7_PPI_RW_COMMON_CFG, 3, 0, 1);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0,
+ 0, 10, 10);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1,
+ 1, 10, 10);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1,
+ 0, 15, 15);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3,
+ 3, 8, 9);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0,
+ 0, 15, 15);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6,
+ 7, 12, 14);
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7,
+ 0, 8, 10);
+ /* resistance tuning: 1 for 45ohm, 0 for 50ohm */
+ dwc_phy_write_mask(isys, id, IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5,
+ 1, 8, 8);
+
+ if (aggregation)
+ phy_mode = isys->csi2[0].phy_mode;
+ else
+ phy_mode = isys->csi2[id].phy_mode;
+
+ if (phy_mode == PHY_MODE_DPHY) {
+ ipu7_isys_dphy_config(isys, id, lanes, aggregation, mbps);
+ } else if (phy_mode == PHY_MODE_CPHY) {
+ ipu7_isys_cphy_config(isys, id, lanes, aggregation, mbps);
+ } else {
+ dev_err(dev, "unsupported phy mode %d!\n",
+ isys->csi2[id].phy_mode);
+ }
+
+ return 0;
+}
+
+static int ipu7_isys_csi_phy_powerup(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+ u32 lanes = csi2->nlanes;
+ bool aggregation = false;
+ u32 id = csi2->port;
+ int ret;
+
+ /* lanes remapping for aggregation (port AB) mode */
+ if (!IS_IPU7_MTL(isys->adev->isp) && lanes > 2 && id == PORT_A) {
+ aggregation = true;
+ lanes = 2;
+ }
+
+ ipu7_isys_csi_phy_reset(isys, id);
+ gpreg_write(isys, id, IPU7_PHY_CLK_LANE_CONTROL, 0x1);
+ gpreg_write(isys, id, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0x2);
+ gpreg_write(isys, id, IPU7_PHY_LANE_CONTROL_EN, (1U << lanes) - 1U);
+ gpreg_write(isys, id, IPU7_PHY_LANE_FORCE_CONTROL, 0xf);
+ gpreg_write(isys, id, IPU7_PHY_MODE, csi2->phy_mode);
+
+ /* config PORT_B if aggregation mode */
+ if (aggregation) {
+ ipu7_isys_csi_phy_reset(isys, PORT_B);
+ gpreg_write(isys, PORT_B, IPU7_PHY_CLK_LANE_CONTROL, 0x0);
+ gpreg_write(isys, PORT_B, IPU7_PHY_LANE_CONTROL_EN, 0x3);
+ gpreg_write(isys, PORT_B, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0x2);
+ gpreg_write(isys, PORT_B, IPU7_PHY_LANE_FORCE_CONTROL, 0xf);
+ gpreg_write(isys, PORT_B, IPU7_PHY_MODE, csi2->phy_mode);
+ }
+
+ ipu7_isys_csi_ctrl_cfg(csi2);
+ ipu7_isys_csi_ctrl_dids_config(csi2, id);
+
+ ret = ipu7_isys_phy_config(isys, id, lanes, aggregation);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, id, IPU7_PHY_RESET, 1);
+ gpreg_write(isys, id, IPU7_PHY_SHUTDOWN, 1);
+ dwc_csi_write(isys, id, IPU7_DPHY_RSTZ, 1);
+ dwc_csi_write(isys, id, IPU7_PHY_SHUTDOWNZ, 1);
+ dwc_csi_write(isys, id, IPU7_CSI2_RESETN, 1);
+
+ ret = ipu7_isys_phy_ready(isys, id);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, id, IPU7_PHY_LANE_FORCE_CONTROL, 0);
+ gpreg_write(isys, id, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0);
+
+ /* config PORT_B if aggregation mode */
+ if (aggregation) {
+ ret = ipu7_isys_phy_config(isys, PORT_B, 2, aggregation);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, PORT_B, IPU7_PHY_RESET, 1);
+ gpreg_write(isys, PORT_B, IPU7_PHY_SHUTDOWN, 1);
+ dwc_csi_write(isys, PORT_B, IPU7_DPHY_RSTZ, 1);
+ dwc_csi_write(isys, PORT_B, IPU7_PHY_SHUTDOWNZ, 1);
+ dwc_csi_write(isys, PORT_B, IPU7_CSI2_RESETN, 1);
+ ret = ipu7_isys_phy_ready(isys, PORT_B);
+ if (ret < 0)
+ return ret;
+
+ gpreg_write(isys, PORT_B, IPU7_PHY_LANE_FORCE_CONTROL, 0);
+ gpreg_write(isys, PORT_B, IPU7_PHY_CLK_LANE_FORCE_CONTROL, 0);
+ }
+
+ return 0;
+}
+
+static void ipu7_isys_csi_phy_powerdown(struct ipu6_isys_csi2 *csi2)
+{
+ struct ipu6_isys *isys = csi2->isys;
+
+ ipu7_isys_csi_phy_reset(isys, csi2->port);
+ if (!IS_IPU7_MTL(isys->adev->isp) &&
+ csi2->nlanes > 2U && csi2->port == PORT_A)
+ ipu7_isys_csi_phy_reset(isys, PORT_B);
+}
+
+int ipu7_isys_csi_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on)
+{
+ struct ipu6_isys_csi2 *csi2 = &isys->csi2[cfg->port];
+
+ if (on)
+ return ipu7_isys_csi_phy_powerup(csi2);
+
+ ipu7_isys_csi_phy_powerdown(csi2);
+
+ return 0;
+}
diff --git a/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.h b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.h
new file mode 100644
index 000000000000..849fe888db07
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-isys-csi-phy.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2026 Intel Corporation */
+
+#ifndef IPU7_ISYS_CSI_PHY_H
+#define IPU7_ISYS_CSI_PHY_H
+
+struct ipu6_isys;
+struct ipu6_isys_csi2_config;
+struct ipu6_isys_csi2_timing;
+
+int ipu7_isys_csi_phy_set_power(struct ipu6_isys *isys,
+ struct ipu6_isys_csi2_config *cfg,
+ const struct ipu6_isys_csi2_timing *timing,
+ bool on);
+
+#endif /* IPU7_ISYS_CSI_PHY_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu7-isys-csi2-regs.h b/drivers/media/pci/intel/ipu6/ipu7-isys-csi2-regs.h
new file mode 100644
index 000000000000..3859401d4802
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-isys-csi2-regs.h
@@ -0,0 +1,1189 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 - 2026 Intel Corporation
+ */
+
+#ifndef IPU7_ISYS_CSI2_REG_H
+#define IPU7_ISYS_CSI2_REG_H
+
+/* IS main regs base */
+#define IPU7_IS_MAIN_BASE 0x240000
+#define IPU7_IS_MAIN_S2B_BASE (IPU7_IS_MAIN_BASE + 0x22000)
+#define IPU7_IS_MAIN_B2O_BASE (IPU7_IS_MAIN_BASE + 0x26000)
+#define IPU7_IS_MAIN_ISD_M0_BASE (IPU7_IS_MAIN_BASE + 0x2b000)
+#define IPU7_IS_MAIN_ISD_M1_BASE (IPU7_IS_MAIN_BASE + 0x2b100)
+#define IPU7_IS_MAIN_ISD_INT_BASE (IPU7_IS_MAIN_BASE + 0x2b200)
+#define IPU7_IS_MAIN_GDA_BASE (IPU7_IS_MAIN_BASE + 0x32000)
+#define IPU7_IS_MAIN_GPREGS_MAIN_BASE (IPU7_IS_MAIN_BASE + 0x32500)
+#define IPU7_IS_MAIN_IRQ_CTRL_BASE (IPU7_IS_MAIN_BASE + 0x32700)
+#define IPU7_IS_MAIN_PWM_CTRL_BASE (IPU7_IS_MAIN_BASE + 0x32b00)
+
+#define IPU7_S2B_IRQ_COMMON_0_CTL_STATUS (IPU7_IS_MAIN_S2B_BASE + 0x1c)
+#define IPU7_S2B_IRQ_COMMON_0_CTL_CLEAR (IPU7_IS_MAIN_S2B_BASE + 0x20)
+#define IPU7_S2B_IRQ_COMMON_0_CTL_ENABLE (IPU7_IS_MAIN_S2B_BASE + 0x24)
+#define IPU7_S2B_IID_IRQ_CTL_STATUS(iid) (IPU7_IS_MAIN_S2B_BASE + 0x94 + \
+ 0x100 * (iid))
+
+#define IPU7_B2O_IRQ_COMMON_0_CTL_STATUS (IPU7_IS_MAIN_B2O_BASE + 0x30)
+#define IPU7_B2O_IRQ_COMMON_0_CTL_CLEAR (IPU7_IS_MAIN_B2O_BASE + 0x34)
+#define IPU7_B2O_IRQ_COMMON_0_CTL_ENABLE (IPU7_IS_MAIN_B2O_BASE + 0x38)
+#define IPU7_B2O_IID_IRQ_CTL_STATUS(oid) (IPU7_IS_MAIN_B2O_BASE + 0x3dc + \
+ 0x200 * (oid))
+
+#define IPU7_ISD_M0_IRQ_CTL_STATUS (IPU7_IS_MAIN_ISD_M0_BASE + 0x1c)
+#define IPU7_ISD_M0_IRQ_CTL_CLEAR (IPU7_IS_MAIN_ISD_M0_BASE + 0x20)
+#define IPU7_ISD_M0_IRQ_CTL_ENABLE (IPU7_IS_MAIN_ISD_M0_BASE + 0x24)
+
+#define IPU7_ISD_M1_IRQ_CTL_STATUS (IPU7_IS_MAIN_ISD_M1_BASE + 0x1c)
+#define IPU7_ISD_M1_IRQ_CTL_CLEAR (IPU7_IS_MAIN_ISD_M1_BASE + 0x20)
+#define IPU7_ISD_M1_IRQ_CTL_ENABLE (IPU7_IS_MAIN_ISD_M1_BASE + 0x24)
+
+#define IPU7_ISD_INT_IRQ_CTL_STATUS (IPU7_IS_MAIN_ISD_INT_BASE + 0x1c)
+#define IPU7_ISD_INT_IRQ_CTL_CLEAR (IPU7_IS_MAIN_ISD_INT_BASE + 0x20)
+#define IPU7_ISD_INT_IRQ_CTL_ENABLE (IPU7_IS_MAIN_ISD_INT_BASE + 0x24)
+
+#define IPU7_GDA_IRQ_CTL_STATUS (IPU7_IS_MAIN_GDA_BASE + 0x1c)
+#define IPU7_GDA_IRQ_CTL_CLEAR (IPU7_IS_MAIN_GDA_BASE + 0x20)
+#define IPU7_GDA_IRQ_CTL_ENABLE (IPU7_IS_MAIN_GDA_BASE + 0x24)
+
+#define IPU7_IS_MAIN_IRQ_CTL_EDGE IPU7_IS_MAIN_IRQ_CTRL_BASE
+#define IPU7_IS_MAIN_IRQ_CTL_MASK (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x4)
+#define IPU7_IS_MAIN_IRQ_CTL_STATUS (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x8)
+#define IPU7_IS_MAIN_IRQ_CTL_CLEAR (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0xc)
+#define IPU7_IS_MAIN_IRQ_CTL_ENABLE (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x10)
+#define IPU7_IS_MAIN_IRQ_CTL_LEVEL_NOT_PULSE (IPU7_IS_MAIN_IRQ_CTRL_BASE + 0x14)
+
+/* IS IO regs base */
+#define IPU7_IS_PHY_NUM 4U
+#define IPU7_IS_IO_BASE 0x280000
+
+/* dwc csi cdphy registers */
+#define IPU7_IS_IO_CDPHY_BASE(i) (IPU7_IS_IO_BASE + 0x10000 * (i))
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_0 0x1800
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_1 0x1802
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_2 0x1804
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_3 0x1806
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_4 0x1808
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_5 0x180a
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_6 0x180c
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_7 0x180e
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_8 0x1810
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_9 0x1812
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_A 0x1814
+#define IPU7_PPI_STARTUP_RW_COMMON_DPHY_10 0x1820
+#define IPU7_PPI_STARTUP_RW_COMMON_STARTUP_1_1 0x1822
+#define IPU7_PPI_STARTUP_RW_COMMON_STARTUP_1_2 0x1824
+#define IPU7_PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_0 0x1840
+#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_1 0x1842
+#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_2 0x1844
+#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_3 0x1846
+#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_4 0x1848
+#define IPU7_PPI_CALIBCTRL_R_COMMON_CALIBCTRL_2_5 0x184a
+#define IPU7_PPI_CALIBCTRL_RW_COMMON_BG_0 0x184c
+#define IPU7_PPI_CALIBCTRL_RW_COMMON_CALIBCTRL_2_7 0x184e
+#define IPU7_PPI_CALIBCTRL_RW_ADC_CFG_0 0x1850
+#define IPU7_PPI_CALIBCTRL_RW_ADC_CFG_1 0x1852
+#define IPU7_PPI_CALIBCTRL_R_ADC_DEBUG 0x1854
+#define IPU7_PPI_RW_LPDCOCAL_TOP_OVERRIDE 0x1c00
+#define IPU7_PPI_RW_LPDCOCAL_TIMEBASE 0x1c02
+#define IPU7_PPI_RW_LPDCOCAL_NREF 0x1c04
+#define IPU7_PPI_RW_LPDCOCAL_NREF_RANGE 0x1c06
+#define IPU7_PPI_RW_LPDCOCAL_NREF_TRIGGER_MAN 0x1c08
+#define IPU7_PPI_RW_LPDCOCAL_TWAIT_CONFIG 0x1c0a
+#define IPU7_PPI_RW_LPDCOCAL_VT_CONFIG 0x1c0c
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_RB 0x1c0e
+#define IPU7_PPI_RW_LPDCOCAL_COARSE_CFG 0x1c10
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_RB 0x1c12
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_0_RB 0x1c14
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_MEAS_1_RB 0x1c16
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_COARSE_FWORD_RB 0x1c18
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_MEASURE_CURR_ERROR 0x1c1a
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_MEASURE_LAST_ERROR 0x1c1c
+#define IPU7_PPI_R_LPDCOCAL_DEBUG_VT 0x1c1e
+#define IPU7_PPI_RW_LB_TIMEBASE_CONFIG 0x1c20
+#define IPU7_PPI_RW_LB_STARTCMU_CONFIG 0x1c22
+#define IPU7_PPI_R_LBPULSE_COUNTER_RB 0x1c24
+#define IPU7_PPI_R_LB_START_CMU_RB 0x1c26
+#define IPU7_PPI_RW_LB_DPHY_BURST_START 0x1c28
+#define IPU7_PPI_RW_LB_CPHY_BURST_START 0x1c2a
+#define IPU7_PPI_RW_DDLCAL_CFG_0 0x1c40
+#define IPU7_PPI_RW_DDLCAL_CFG_1 0x1c42
+#define IPU7_PPI_RW_DDLCAL_CFG_2 0x1c44
+#define IPU7_PPI_RW_DDLCAL_CFG_3 0x1c46
+#define IPU7_PPI_RW_DDLCAL_CFG_4 0x1c48
+#define IPU7_PPI_RW_DDLCAL_CFG_5 0x1c4a
+#define IPU7_PPI_RW_DDLCAL_CFG_6 0x1c4c
+#define IPU7_PPI_RW_DDLCAL_CFG_7 0x1c4e
+#define IPU7_PPI_R_DDLCAL_DEBUG_0 0x1c50
+#define IPU7_PPI_R_DDLCAL_DEBUG_1 0x1c52
+#define IPU7_PPI_RW_PARITY_TEST 0x1c60
+#define IPU7_PPI_RW_STARTUP_OVR_0 0x1c62
+#define IPU7_PPI_RW_STARTUP_STATE_OVR_1 0x1c64
+#define IPU7_PPI_RW_DTB_SELECTOR 0x1c66
+#define IPU7_PPI_RW_DPHY_CLK_SPARE 0x1c6a
+#define IPU7_PPI_RW_COMMON_CFG 0x1c6c
+#define IPU7_PPI_RW_TERMCAL_CFG_0 0x1c80
+#define IPU7_PPI_R_TERMCAL_DEBUG_0 0x1c82
+#define IPU7_PPI_RW_TERMCAL_CTRL_0 0x1c84
+#define IPU7_PPI_RW_OFFSETCAL_CFG_0 0x1ca0
+#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE0 0x1ca2
+#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE1 0x1ca4
+#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE2 0x1ca6
+#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE3 0x1ca8
+#define IPU7_PPI_R_OFFSETCAL_DEBUG_LANE4 0x1caa
+#define IPU7_PPI_RW_HSDCOCAL_CFG_O 0x1d00
+#define IPU7_PPI_RW_HSDCOCAL_CFG_1 0x1d02
+#define IPU7_PPI_RW_HSDCOCAL_CFG_2 0x1d04
+#define IPU7_PPI_RW_HSDCOCAL_CFG_3 0x1d06
+#define IPU7_PPI_RW_HSDCOCAL_CFG_4 0x1d08
+#define IPU7_PPI_RW_HSDCOCAL_CFG_5 0x1d0a
+#define IPU7_PPI_RW_HSDCOCAL_CFG_6 0x1d0c
+#define IPU7_PPI_RW_HSDCOCAL_CFG_7 0x1d0e
+#define IPU7_PPI_RW_HSDCOCAL_CFG_8 0x1d10
+#define IPU7_PPI_R_HSDCOCAL_DEBUG_RB 0x1d12
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_0 0x2000
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_1 0x2002
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_2 0x2004
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_3 0x2006
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_4 0x2008
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_5 0x200a
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_6 0x200c
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_7 0x200e
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE0_OVR_0_8 0x2010
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_9 0x2012
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_10 0x2014
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_11 0x2016
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_12 0x2018
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_13 0x201a
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_14 0x201c
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE0_OVR_0_15 0x201e
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_0 0x2020
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_1 0x2022
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_2 0x2024
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_3 0x2026
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_4 0x2028
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_5 0x202a
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_6 0x202c
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_7 0x202e
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_8 0x2030
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_1_9 0x2032
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_10 0x2034
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_11 0x2036
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_12 0x2038
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_13 0x203a
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_14 0x203c
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_1_15 0x203e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_0 0x2040
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_1 0x2042
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_2 0x2044
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_3 0x2046
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_4 0x2048
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_5 0x204a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_6 0x204c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_7 0x204e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_8 0x2050
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_9 0x2052
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_10 0x2054
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_11 0x2056
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_12 0x2058
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_13 0x205a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_14 0x205c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_2_15 0x205e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_0 0x2060
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_1 0x2062
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_2 0x2064
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_3 0x2066
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_4 0x2068
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_5 0x206a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_6 0x206c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE0_CTRL_3_7 0x206e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_8 0x2070
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_9 0x2072
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_10 0x2074
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_11 0x2076
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_12 0x2078
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_13 0x207a
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_14 0x207c
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_3_15 0x207e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_0 0x2080
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_1 0x2082
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_2 0x2084
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_3 0x2086
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE0_CTRL_4_4 0x2088
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_0 0x20a0
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_1 0x20a2
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_2 0x20a4
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE0_OVR_5_3 0x20a6
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE0_OVR_5_4 0x20a8
+#define IPU7_CORE_DIG_RW_TRIO0_0 0x2100
+#define IPU7_CORE_DIG_RW_TRIO0_1 0x2102
+#define IPU7_CORE_DIG_RW_TRIO0_2 0x2104
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_0 0x2400
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_1 0x2402
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_2 0x2404
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_3 0x2406
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_4 0x2408
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_5 0x240a
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_6 0x240c
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_7 0x240e
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE1_OVR_0_8 0x2410
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_9 0x2412
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_10 0x2414
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_11 0x2416
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_12 0x2418
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_13 0x241a
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_14 0x241c
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE1_OVR_0_15 0x241e
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_0 0x2420
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_1 0x2422
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_2 0x2424
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_3 0x2426
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_4 0x2428
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_5 0x242a
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_6 0x242c
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_7 0x242e
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_8 0x2430
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_1_9 0x2432
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_10 0x2434
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_11 0x2436
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_12 0x2438
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_13 0x243a
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_14 0x243c
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_1_15 0x243e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_0 0x2440
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_1 0x2442
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_2 0x2444
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_3 0x2446
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_4 0x2448
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_5 0x244a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_6 0x244c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_7 0x244e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_8 0x2450
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_9 0x2452
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_10 0x2454
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_11 0x2456
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_12 0x2458
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_13 0x245a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_14 0x245c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_2_15 0x245e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_0 0x2460
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_1 0x2462
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_2 0x2464
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_3 0x2466
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_4 0x2468
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_5 0x246a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_6 0x246c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE1_CTRL_3_7 0x246e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_8 0x2470
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_9 0x2472
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_10 0x2474
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_11 0x2476
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_12 0x2478
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_13 0x247a
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_14 0x247c
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_3_15 0x247e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_0 0x2480
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_1 0x2482
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_2 0x2484
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_3 0x2486
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE1_CTRL_4_4 0x2488
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_0 0x24a0
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_1 0x24a2
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_2 0x24a4
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE1_OVR_5_3 0x24a6
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE1_OVR_5_4 0x24a8
+#define IPU7_CORE_DIG_RW_TRIO1_0 0x2500
+#define IPU7_CORE_DIG_RW_TRIO1_1 0x2502
+#define IPU7_CORE_DIG_RW_TRIO1_2 0x2504
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_0 0x2800
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_1 0x2802
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_2 0x2804
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_3 0x2806
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_4 0x2808
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_5 0x280a
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_6 0x280c
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_7 0x280e
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE2_OVR_0_8 0x2810
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_9 0x2812
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_10 0x2814
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_11 0x2816
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_12 0x2818
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_13 0x281a
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_14 0x281c
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE2_OVR_0_15 0x281e
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_0 0x2820
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_1 0x2822
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_2 0x2824
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_3 0x2826
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_4 0x2828
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_5 0x282a
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_6 0x282c
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_7 0x282e
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_8 0x2830
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_1_9 0x2832
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_10 0x2834
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_11 0x2836
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_12 0x2838
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_13 0x283a
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_14 0x283c
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_1_15 0x283e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_0 0x2840
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_1 0x2842
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_2 0x2844
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_3 0x2846
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_4 0x2848
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_5 0x284a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_6 0x284c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_7 0x284e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_8 0x2850
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_9 0x2852
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_10 0x2854
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_11 0x2856
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_12 0x2858
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_13 0x285a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_14 0x285c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_2_15 0x285e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_0 0x2860
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_1 0x2862
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_2 0x2864
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_3 0x2866
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_4 0x2868
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_5 0x286a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_6 0x286c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE2_CTRL_3_7 0x286e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_8 0x2870
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_9 0x2872
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_10 0x2874
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_11 0x2876
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_12 0x2878
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_13 0x287a
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_14 0x287c
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_3_15 0x287e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_0 0x2880
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_1 0x2882
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_2 0x2884
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_3 0x2886
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE2_CTRL_4_4 0x2888
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_0 0x28a0
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_1 0x28a2
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_2 0x28a4
+#define IPU7_CORE_DIG_IOCTRL_RW_CPHY_PPI_LANE2_OVR_5_3 0x28a6
+#define IPU7_CORE_DIG_IOCTRL_R_CPHY_PPI_LANE2_OVR_5_4 0x28a8
+#define IPU7_CORE_DIG_RW_TRIO2_0 0x2900
+#define IPU7_CORE_DIG_RW_TRIO2_1 0x2902
+#define IPU7_CORE_DIG_RW_TRIO2_2 0x2904
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_0 0x2c00
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_1 0x2c02
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_2 0x2c04
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_3 0x2c06
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_4 0x2c08
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_5 0x2c0a
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_6 0x2c0c
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_7 0x2c0e
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_LANE3_OVR_0_8 0x2c10
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_9 0x2c12
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_10 0x2c14
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_11 0x2c16
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_12 0x2c18
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_13 0x2c1a
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_14 0x2c1c
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_LANE3_OVR_0_15 0x2c1e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_0 0x2c40
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_1 0x2c42
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_2 0x2c44
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_3 0x2c46
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_4 0x2c48
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_5 0x2c4a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_6 0x2c4c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_7 0x2c4e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_8 0x2c50
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_9 0x2c52
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_10 0x2c54
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_11 0x2c56
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_12 0x2c58
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_13 0x2c5a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_14 0x2c5c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_2_15 0x2c5e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_0 0x2c60
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_1 0x2c62
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_2 0x2c64
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_3 0x2c66
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_4 0x2c68
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_5 0x2c6a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_6 0x2c6c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE3_CTRL_3_7 0x2c6e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_8 0x2c70
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_9 0x2c72
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_10 0x2c74
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_11 0x2c76
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_12 0x2c78
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_13 0x2c7a
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_14 0x2c7c
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_3_15 0x2c7e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_0 0x2c80
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_1 0x2c82
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_2 0x2c84
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_3 0x2c86
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE3_CTRL_4_4 0x2c88
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_0 0x3040
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_1 0x3042
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_2 0x3044
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_3 0x3046
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_4 0x3048
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_5 0x304a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_6 0x304c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_7 0x304e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_8 0x3050
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_9 0x3052
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_10 0x3054
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_11 0x3056
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_12 0x3058
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_13 0x305a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_14 0x305c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_2_15 0x305e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_0 0x3060
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_1 0x3062
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_2 0x3064
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_3 0x3066
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_4 0x3068
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_5 0x306a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_6 0x306c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_LANE4_CTRL_3_7 0x306e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_8 0x3070
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_9 0x3072
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_10 0x3074
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_11 0x3076
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_12 0x3078
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_13 0x307a
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_14 0x307c
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_3_15 0x307e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_0 0x3080
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_1 0x3082
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_2 0x3084
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_3 0x3086
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_LANE4_CTRL_4_4 0x3088
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_0 0x3400
+#define IPU7_CORE_DIG_IOCTRL_RW_DPHY_PPI_CLK_OVR_0_1 0x3402
+#define IPU7_CORE_DIG_IOCTRL_R_DPHY_PPI_CLK_OVR_0_2 0x3404
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_0 0x3800
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_1 0x3802
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_2 0x3804
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_3 0x3806
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_4 0x3808
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_5 0x380a
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_6 0x380c
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_7 0x380e
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_8 0x3810
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_9 0x3812
+#define IPU7_CORE_DIG_IOCTRL_RW_COMMON_PPI_OVR_0_10 0x3814
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_11 0x3816
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_12 0x3818
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_13 0x381a
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_14 0x381c
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_0_15 0x381e
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_0 0x3820
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_1 0x3822
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_2 0x3824
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_3 0x3826
+#define IPU7_CORE_DIG_IOCTRL_R_COMMON_PPI_OVR_1_4 0x3828
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_0 0x3840
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_1 0x3842
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_2 0x3844
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_3 0x3846
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_4 0x3848
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_5 0x384a
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_6 0x384c
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_7 0x384e
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_8 0x3850
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_9 0x3852
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_10 0x3854
+#define IPU7_CORE_DIG_IOCTRL_RW_AFE_CB_CTRL_2_11 0x3856
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_12 0x3858
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_13 0x385a
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_14 0x385c
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_2_15 0x385e
+#define IPU7_CORE_DIG_IOCTRL_R_AFE_CB_CTRL_3_0 0x3860
+#define IPU7_CORE_DIG_RW_COMMON_0 0x3880
+#define IPU7_CORE_DIG_RW_COMMON_1 0x3882
+#define IPU7_CORE_DIG_RW_COMMON_2 0x3884
+#define IPU7_CORE_DIG_RW_COMMON_3 0x3886
+#define IPU7_CORE_DIG_RW_COMMON_4 0x3888
+#define IPU7_CORE_DIG_RW_COMMON_5 0x388a
+#define IPU7_CORE_DIG_RW_COMMON_6 0x388c
+#define IPU7_CORE_DIG_RW_COMMON_7 0x388e
+#define IPU7_CORE_DIG_RW_COMMON_8 0x3890
+#define IPU7_CORE_DIG_RW_COMMON_9 0x3892
+#define IPU7_CORE_DIG_RW_COMMON_10 0x3894
+#define IPU7_CORE_DIG_RW_COMMON_11 0x3896
+#define IPU7_CORE_DIG_RW_COMMON_12 0x3898
+#define IPU7_CORE_DIG_RW_COMMON_13 0x389a
+#define IPU7_CORE_DIG_RW_COMMON_14 0x389c
+#define IPU7_CORE_DIG_RW_COMMON_15 0x389e
+#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_0 0x39e0
+#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_1 0x39e2
+#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_2 0x39e4
+#define IPU7_CORE_DIG_ANACTRL_RW_COMMON_ANACTRL_3 0x39e6
+#define IPU7_CORE_DIG_COMMON_RW_DESKEW_FINE_MEM 0x3fe0
+#define IPU7_CORE_DIG_COMMON_R_DESKEW_FINE_MEM 0x3fe2
+#define IPU7_PPI_RW_DPHY_LANE0_LBERT_0 0x4000
+#define IPU7_PPI_RW_DPHY_LANE0_LBERT_1 0x4002
+#define IPU7_PPI_R_DPHY_LANE0_LBERT_0 0x4004
+#define IPU7_PPI_R_DPHY_LANE0_LBERT_1 0x4006
+#define IPU7_PPI_RW_DPHY_LANE0_SPARE 0x4008
+#define IPU7_PPI_RW_DPHY_LANE1_LBERT_0 0x4400
+#define IPU7_PPI_RW_DPHY_LANE1_LBERT_1 0x4402
+#define IPU7_PPI_R_DPHY_LANE1_LBERT_0 0x4404
+#define IPU7_PPI_R_DPHY_LANE1_LBERT_1 0x4406
+#define IPU7_PPI_RW_DPHY_LANE1_SPARE 0x4408
+#define IPU7_PPI_RW_DPHY_LANE2_LBERT_0 0x4800
+#define IPU7_PPI_RW_DPHY_LANE2_LBERT_1 0x4802
+#define IPU7_PPI_R_DPHY_LANE2_LBERT_0 0x4804
+#define IPU7_PPI_R_DPHY_LANE2_LBERT_1 0x4806
+#define IPU7_PPI_RW_DPHY_LANE2_SPARE 0x4808
+#define IPU7_PPI_RW_DPHY_LANE3_LBERT_0 0x4c00
+#define IPU7_PPI_RW_DPHY_LANE3_LBERT_1 0x4c02
+#define IPU7_PPI_R_DPHY_LANE3_LBERT_0 0x4c04
+#define IPU7_PPI_R_DPHY_LANE3_LBERT_1 0x4c06
+#define IPU7_PPI_RW_DPHY_LANE3_SPARE 0x4c08
+#define IPU7_CORE_DIG_DLANE_0_RW_CFG_0 0x6000
+#define IPU7_CORE_DIG_DLANE_0_RW_CFG_1 0x6002
+#define IPU7_CORE_DIG_DLANE_0_RW_CFG_2 0x6004
+#define IPU7_CORE_DIG_DLANE_0_RW_LP_0 0x6080
+#define IPU7_CORE_DIG_DLANE_0_RW_LP_1 0x6082
+#define IPU7_CORE_DIG_DLANE_0_RW_LP_2 0x6084
+#define IPU7_CORE_DIG_DLANE_0_R_LP_0 0x60a0
+#define IPU7_CORE_DIG_DLANE_0_R_LP_1 0x60a2
+#define IPU7_CORE_DIG_DLANE_0_R_HS_TX_0 0x60e0
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_0 0x6100
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_1 0x6102
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_2 0x6104
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_3 0x6106
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_4 0x6108
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_5 0x610a
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_6 0x610c
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_7 0x610e
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_8 0x6110
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_RX_9 0x6112
+#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_0 0x6120
+#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_1 0x6122
+#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_2 0x6124
+#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_3 0x6126
+#define IPU7_CORE_DIG_DLANE_0_R_HS_RX_4 0x6128
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_0 0x6200
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_1 0x6202
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_2 0x6204
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_3 0x6206
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_4 0x6208
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_5 0x620a
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_6 0x620c
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_7 0x620e
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_8 0x6210
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_9 0x6212
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_10 0x6214
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_11 0x6216
+#define IPU7_CORE_DIG_DLANE_0_RW_HS_TX_12 0x6218
+#define IPU7_CORE_DIG_DLANE_1_RW_CFG_0 0x6400
+#define IPU7_CORE_DIG_DLANE_1_RW_CFG_1 0x6402
+#define IPU7_CORE_DIG_DLANE_1_RW_CFG_2 0x6404
+#define IPU7_CORE_DIG_DLANE_1_RW_LP_0 0x6480
+#define IPU7_CORE_DIG_DLANE_1_RW_LP_1 0x6482
+#define IPU7_CORE_DIG_DLANE_1_RW_LP_2 0x6484
+#define IPU7_CORE_DIG_DLANE_1_R_LP_0 0x64a0
+#define IPU7_CORE_DIG_DLANE_1_R_LP_1 0x64a2
+#define IPU7_CORE_DIG_DLANE_1_R_HS_TX_0 0x64e0
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_0 0x6500
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_1 0x6502
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_2 0x6504
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_3 0x6506
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_4 0x6508
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_5 0x650a
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_6 0x650c
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_7 0x650e
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_8 0x6510
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_RX_9 0x6512
+#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_0 0x6520
+#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_1 0x6522
+#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_2 0x6524
+#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_3 0x6526
+#define IPU7_CORE_DIG_DLANE_1_R_HS_RX_4 0x6528
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_0 0x6600
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_1 0x6602
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_2 0x6604
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_3 0x6606
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_4 0x6608
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_5 0x660a
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_6 0x660c
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_7 0x660e
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_8 0x6610
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_9 0x6612
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_10 0x6614
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_11 0x6616
+#define IPU7_CORE_DIG_DLANE_1_RW_HS_TX_12 0x6618
+#define IPU7_CORE_DIG_DLANE_2_RW_CFG_0 0x6800
+#define IPU7_CORE_DIG_DLANE_2_RW_CFG_1 0x6802
+#define IPU7_CORE_DIG_DLANE_2_RW_CFG_2 0x6804
+#define IPU7_CORE_DIG_DLANE_2_RW_LP_0 0x6880
+#define IPU7_CORE_DIG_DLANE_2_RW_LP_1 0x6882
+#define IPU7_CORE_DIG_DLANE_2_RW_LP_2 0x6884
+#define IPU7_CORE_DIG_DLANE_2_R_LP_0 0x68a0
+#define IPU7_CORE_DIG_DLANE_2_R_LP_1 0x68a2
+#define IPU7_CORE_DIG_DLANE_2_R_HS_TX_0 0x68e0
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_0 0x6900
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_1 0x6902
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_2 0x6904
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_3 0x6906
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_4 0x6908
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_5 0x690a
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_6 0x690c
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_7 0x690e
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_8 0x6910
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_RX_9 0x6912
+#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_0 0x6920
+#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_1 0x6922
+#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_2 0x6924
+#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_3 0x6926
+#define IPU7_CORE_DIG_DLANE_2_R_HS_RX_4 0x6928
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_0 0x6a00
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_1 0x6a02
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_2 0x6a04
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_3 0x6a06
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_4 0x6a08
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_5 0x6a0a
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_6 0x6a0c
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_7 0x6a0e
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_8 0x6a10
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_9 0x6a12
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_10 0x6a14
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_11 0x6a16
+#define IPU7_CORE_DIG_DLANE_2_RW_HS_TX_12 0x6a18
+#define IPU7_CORE_DIG_DLANE_3_RW_CFG_0 0x6c00
+#define IPU7_CORE_DIG_DLANE_3_RW_CFG_1 0x6c02
+#define IPU7_CORE_DIG_DLANE_3_RW_CFG_2 0x6c04
+#define IPU7_CORE_DIG_DLANE_3_RW_LP_0 0x6c80
+#define IPU7_CORE_DIG_DLANE_3_RW_LP_1 0x6c82
+#define IPU7_CORE_DIG_DLANE_3_RW_LP_2 0x6c84
+#define IPU7_CORE_DIG_DLANE_3_R_LP_0 0x6ca0
+#define IPU7_CORE_DIG_DLANE_3_R_LP_1 0x6ca2
+#define IPU7_CORE_DIG_DLANE_3_R_HS_TX_0 0x6ce0
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_0 0x6d00
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_1 0x6d02
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_2 0x6d04
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_3 0x6d06
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_4 0x6d08
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_5 0x6d0a
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_6 0x6d0c
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_7 0x6d0e
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_8 0x6d10
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_RX_9 0x6d12
+#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_0 0x6d20
+#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_1 0x6d22
+#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_2 0x6d24
+#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_3 0x6d26
+#define IPU7_CORE_DIG_DLANE_3_R_HS_RX_4 0x6d28
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_0 0x6e00
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_1 0x6e02
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_2 0x6e04
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_3 0x6e06
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_4 0x6e08
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_5 0x6e0a
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_6 0x6e0c
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_7 0x6e0e
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_8 0x6e10
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_9 0x6e12
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_10 0x6e14
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_11 0x6e16
+#define IPU7_CORE_DIG_DLANE_3_RW_HS_TX_12 0x6e18
+#define IPU7_CORE_DIG_DLANE_CLK_RW_CFG_0 0x7000
+#define IPU7_CORE_DIG_DLANE_CLK_RW_CFG_1 0x7002
+#define IPU7_CORE_DIG_DLANE_CLK_RW_CFG_2 0x7004
+#define IPU7_CORE_DIG_DLANE_CLK_RW_LP_0 0x7080
+#define IPU7_CORE_DIG_DLANE_CLK_RW_LP_1 0x7082
+#define IPU7_CORE_DIG_DLANE_CLK_RW_LP_2 0x7084
+#define IPU7_CORE_DIG_DLANE_CLK_R_LP_0 0x70a0
+#define IPU7_CORE_DIG_DLANE_CLK_R_LP_1 0x70a2
+#define IPU7_CORE_DIG_DLANE_CLK_R_HS_TX_0 0x70e0
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_0 0x7100
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_1 0x7102
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_2 0x7104
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_3 0x7106
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_4 0x7108
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_5 0x710a
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_6 0x710c
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_7 0x710e
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_8 0x7110
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_RX_9 0x7112
+#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_0 0x7120
+#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_1 0x7122
+#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_2 0x7124
+#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_3 0x7126
+#define IPU7_CORE_DIG_DLANE_CLK_R_HS_RX_4 0x7128
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_0 0x7200
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_1 0x7202
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_2 0x7204
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_3 0x7206
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_4 0x7208
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_5 0x720a
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_6 0x720c
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_7 0x720e
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_8 0x7210
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_9 0x7212
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_10 0x7214
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_11 0x7216
+#define IPU7_CORE_DIG_DLANE_CLK_RW_HS_TX_12 0x7218
+#define IPU7_PPI_RW_CPHY_TRIO0_LBERT_0 0x8000
+#define IPU7_PPI_RW_CPHY_TRIO0_LBERT_1 0x8002
+#define IPU7_PPI_R_CPHY_TRIO0_LBERT_0 0x8004
+#define IPU7_PPI_R_CPHY_TRIO0_LBERT_1 0x8006
+#define IPU7_PPI_RW_CPHY_TRIO0_SPARE 0x8008
+#define IPU7_PPI_RW_CPHY_TRIO1_LBERT_0 0x8400
+#define IPU7_PPI_RW_CPHY_TRIO1_LBERT_1 0x8402
+#define IPU7_PPI_R_CPHY_TRIO1_LBERT_0 0x8404
+#define IPU7_PPI_R_CPHY_TRIO1_LBERT_1 0x8406
+#define IPU7_PPI_RW_CPHY_TRIO1_SPARE 0x8408
+#define IPU7_PPI_RW_CPHY_TRIO2_LBERT_0 0x8800
+#define IPU7_PPI_RW_CPHY_TRIO2_LBERT_1 0x8802
+#define IPU7_PPI_R_CPHY_TRIO2_LBERT_0 0x8804
+#define IPU7_PPI_R_CPHY_TRIO2_LBERT_1 0x8806
+#define IPU7_PPI_RW_CPHY_TRIO2_SPARE 0x8808
+#define IPU7_CORE_DIG_CLANE_0_RW_CFG_0 0xa000
+#define IPU7_CORE_DIG_CLANE_0_RW_CFG_2 0xa004
+#define IPU7_CORE_DIG_CLANE_0_RW_LP_0 0xa080
+#define IPU7_CORE_DIG_CLANE_0_RW_LP_1 0xa082
+#define IPU7_CORE_DIG_CLANE_0_RW_LP_2 0xa084
+#define IPU7_CORE_DIG_CLANE_0_R_LP_0 0xa0a0
+#define IPU7_CORE_DIG_CLANE_0_R_LP_1 0xa0a2
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_0 0xa100
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_1 0xa102
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_2 0xa104
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_3 0xa106
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_4 0xa108
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_5 0xa10a
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_RX_6 0xa10c
+#define IPU7_CORE_DIG_CLANE_0_R_RX_0 0xa120
+#define IPU7_CORE_DIG_CLANE_0_R_RX_1 0xa122
+#define IPU7_CORE_DIG_CLANE_0_R_TX_0 0xa124
+#define IPU7_CORE_DIG_CLANE_0_R_RX_2 0xa126
+#define IPU7_CORE_DIG_CLANE_0_R_RX_3 0xa128
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_0 0xa200
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_1 0xa202
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_2 0xa204
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_3 0xa206
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_4 0xa208
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_5 0xa20a
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_6 0xa20c
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_7 0xa20e
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_8 0xa210
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_9 0xa212
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_10 0xa214
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_11 0xa216
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_12 0xa218
+#define IPU7_CORE_DIG_CLANE_0_RW_HS_TX_13 0xa21a
+#define IPU7_CORE_DIG_CLANE_1_RW_CFG_0 0xa400
+#define IPU7_CORE_DIG_CLANE_1_RW_CFG_2 0xa404
+#define IPU7_CORE_DIG_CLANE_1_RW_LP_0 0xa480
+#define IPU7_CORE_DIG_CLANE_1_RW_LP_1 0xa482
+#define IPU7_CORE_DIG_CLANE_1_RW_LP_2 0xa484
+#define IPU7_CORE_DIG_CLANE_1_R_LP_0 0xa4a0
+#define IPU7_CORE_DIG_CLANE_1_R_LP_1 0xa4a2
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_0 0xa500
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_1 0xa502
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_2 0xa504
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_3 0xa506
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_4 0xa508
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_5 0xa50a
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_RX_6 0xa50c
+#define IPU7_CORE_DIG_CLANE_1_R_RX_0 0xa520
+#define IPU7_CORE_DIG_CLANE_1_R_RX_1 0xa522
+#define IPU7_CORE_DIG_CLANE_1_R_TX_0 0xa524
+#define IPU7_CORE_DIG_CLANE_1_R_RX_2 0xa526
+#define IPU7_CORE_DIG_CLANE_1_R_RX_3 0xa528
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_0 0xa600
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_1 0xa602
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_2 0xa604
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_3 0xa606
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_4 0xa608
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_5 0xa60a
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_6 0xa60c
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_7 0xa60e
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_8 0xa610
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_9 0xa612
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_10 0xa614
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_11 0xa616
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_12 0xa618
+#define IPU7_CORE_DIG_CLANE_1_RW_HS_TX_13 0xa61a
+#define IPU7_CORE_DIG_CLANE_2_RW_CFG_0 0xa800
+#define IPU7_CORE_DIG_CLANE_2_RW_CFG_2 0xa804
+#define IPU7_CORE_DIG_CLANE_2_RW_LP_0 0xa880
+#define IPU7_CORE_DIG_CLANE_2_RW_LP_1 0xa882
+#define IPU7_CORE_DIG_CLANE_2_RW_LP_2 0xa884
+#define IPU7_CORE_DIG_CLANE_2_R_LP_0 0xa8a0
+#define IPU7_CORE_DIG_CLANE_2_R_LP_1 0xa8a2
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_0 0xa900
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_1 0xa902
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_2 0xa904
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_3 0xa906
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_4 0xa908
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_5 0xa90a
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_RX_6 0xa90c
+#define IPU7_CORE_DIG_CLANE_2_R_RX_0 0xa920
+#define IPU7_CORE_DIG_CLANE_2_R_RX_1 0xa922
+#define IPU7_CORE_DIG_CLANE_2_R_TX_0 0xa924
+#define IPU7_CORE_DIG_CLANE_2_R_RX_2 0xa926
+#define IPU7_CORE_DIG_CLANE_2_R_RX_3 0xa928
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_0 0xaa00
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_1 0xaa02
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_2 0xaa04
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_3 0xaa06
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_4 0xaa08
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_5 0xaa0a
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_6 0xaa0c
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_7 0xaa0e
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_8 0xaa10
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_9 0xaa12
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_10 0xaa14
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_11 0xaa16
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_12 0xaa18
+#define IPU7_CORE_DIG_CLANE_2_RW_HS_TX_13 0xaa1a
+
+/* dwc csi host controller registers */
+#define IPU7_IS_IO_CSI2_HOST_BASE(i) (IPU7_IS_IO_BASE + 0x40000 + \
+ 0x2000 * (i))
+#define IPU7_VERSION 0x0
+#define IPU7_N_LANES 0x4
+#define IPU7_CSI2_RESETN 0x8
+#define IPU7_INT_ST_MAIN 0xc
+#define IPU7_DATA_IDS_1 0x10
+#define IPU7_DATA_IDS_2 0x14
+#define IPU7_CDPHY_MODE 0x1c
+#define IPU7_DATA_IDS_VC_1 0x30
+#define IPU7_DATA_IDS_VC_2 0x34
+#define IPU7_PHY_SHUTDOWNZ 0x40
+#define IPU7_DPHY_RSTZ 0x44
+#define IPU7_PHY_RX 0x48
+#define IPU7_PHY_STOPSTATE 0x4c
+#define IPU7_PHY_TEST_CTRL0 0x50
+#define IPU7_PHY_TEST_CTRL1 0x54
+#define IPU7_PPI_PG_PATTERN_VRES 0x60
+#define IPU7_PPI_PG_PATTERN_HRES 0x64
+#define IPU7_PPI_PG_CONFIG 0x68
+#define IPU7_PPI_PG_ENABLE 0x6c
+#define IPU7_PPI_PG_STATUS 0x70
+#define IPU7_VC_EXTENSION 0xc8
+#define IPU7_PHY_CAL 0xcc
+#define IPU7_INT_ST_PHY_FATAL 0xe0
+#define IPU7_INT_MSK_PHY_FATAL 0xe4
+#define IPU7_INT_FORCE_PHY_FATAL 0xe8
+#define IPU7_INT_ST_PKT_FATAL 0xf0
+#define IPU7_INT_MSK_PKT_FATAL 0xf4
+#define IPU7_INT_FORCE_PKT_FATAL 0xf8
+#define IPU7_INT_ST_PHY 0x110
+#define IPU7_INT_MSK_PHY 0x114
+#define IPU7_INT_FORCE_PHY 0x118
+#define IPU7_INT_ST_LINE 0x130
+#define IPU7_INT_MSK_LINE 0x134
+#define IPU7_INT_FORCE_LINE 0x138
+#define IPU7_INT_ST_BNDRY_FRAME_FATAL 0x280
+#define IPU7_INT_MSK_BNDRY_FRAME_FATAL 0x284
+#define IPU7_INT_FORCE_BNDRY_FRAME_FATAL 0x288
+#define IPU7_INT_ST_SEQ_FRAME_FATAL 0x290
+#define IPU7_INT_MSK_SEQ_FRAME_FATAL 0x294
+#define IPU7_INT_FORCE_SEQ_FRAME_FATAL 0x298
+#define IPU7_INT_ST_CRC_FRAME_FATAL 0x2a0
+#define IPU7_INT_MSK_CRC_FRAME_FATAL 0x2a4
+#define IPU7_INT_FORCE_CRC_FRAME_FATAL 0x2a8
+#define IPU7_INT_ST_PLD_CRC_FATAL 0x2b0
+#define IPU7_INT_MSK_PLD_CRC_FATAL 0x2b4
+#define IPU7_INT_FORCE_PLD_CRC_FATAL 0x2b8
+#define IPU7_INT_ST_DATA_ID 0x2c0
+#define IPU7_INT_MSK_DATA_ID 0x2c4
+#define IPU7_INT_FORCE_DATA_ID 0x2c8
+#define IPU7_INT_ST_ECC_CORRECTED 0x2d0
+#define IPU7_INT_MSK_ECC_CORRECTED 0x2d4
+#define IPU7_INT_FORCE_ECC_CORRECTED 0x2d8
+#define IPU7_SCRAMBLING 0x300
+#define IPU7_SCRAMBLING_SEED1 0x304
+#define IPU7_SCRAMBLING_SEED2 0x308
+#define IPU7_SCRAMBLING_SEED3 0x30c
+#define IPU7_SCRAMBLING_SEED4 0x310
+#define IPU7_SCRAMBLING 0x300
+
+#define IPU7_IS_IO_CSI2_ADPL_PORT_BASE(i) (IPU7_IS_IO_BASE + 0x40800 + \
+ 0x2000 * (i))
+#define IPU7_CSI2_ADPL_INPUT_MODE 0x0
+#define IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_EN 0x4
+#define IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_CLEAR_ADDR 0x8
+#define IPU7_CSI2_ADPL_CSI_RX_ERR_IRQ_STATUS 0xc
+#define IPU7_CSI2_ADPL_IRQ_CTL_COMMON_STATUS 0xa4
+#define IPU7_CSI2_ADPL_IRQ_CTL_COMMON_CLEAR 0xa8
+#define IPU7_CSI2_ADPL_IRQ_CTL_COMMON_ENABLE 0xac
+#define IPU7_CSI2_ADPL_IRQ_CTL_FS_STATUS 0xbc
+#define IPU7_CSI2_ADPL_IRQ_CTL_FS_CLEAR 0xc0
+#define IPU7_CSI2_ADPL_IRQ_CTL_FS_ENABLE 0xc4
+#define IPU7_CSI2_ADPL_IRQ_CTL_FE_STATUS 0xc8
+#define IPU7_CSI2_ADPL_IRQ_CTL_FE_CLEAR 0xcc
+#define IPU7_CSI2_ADPL_IRQ_CTL_FE_ENABLE 0xd0
+
+/* software control the legacy csi irq */
+#define IPU7_IS_IO_CSI2_ERR_LEGACY_IRQ_CTL_BASE(i) (IPU7_IS_IO_BASE + \
+ 0x40c00 + 0x2000 * (i))
+#define IPU7_IS_IO_CSI2_SYNC_LEGACY_IRQ_CTL_BASE(i) (IPU7_IS_IO_BASE + \
+ 0x40d00 + 0x2000 * (i))
+#define IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IPU7_IS_IO_BASE + \
+ 0x49000)
+#define IPU7_IS_IO_CSI2_IRQ_CTRL_BASE (IPU7_IS_IO_BASE + 0x4e100)
+
+#define IPU7_IRQ_CTL_EDGE 0x0
+#define IPU7_IRQ_CTL_MASK 0x4
+#define IPU7_IRQ_CTL_STATUS 0x8
+#define IPU7_IRQ_CTL_CLEAR 0xc
+#define IPU7_IRQ_CTL_ENABLE 0x10
+/* FE irq for PTL */
+#define IPU7_IRQ1_CTL_MASK 0x14
+#define IPU7_IRQ1_CTL_STATUS 0x18
+#define IPU7_IRQ1_CTL_CLEAR 0x1c
+#define IPU7_IRQ1_CTL_ENABLE 0x20
+
+/* software to set the clock gate to use the port or mgc */
+#define IPU7_IS_IO_GPREGS_BASE (IPU7_IS_IO_BASE + 0x49200)
+#define IPU7_SRST_PORT_ARB 0x0
+#define IPU7_SRST_MGC 0x4
+#define IPU7_SRST_WIDTH_CONV 0x8
+#define IPU7_SRST_CSI_IRQ 0xc
+#define IPU7_SRST_CSI_LEGACY_IRQ 0x10
+#define IPU7_CLK_EN_TXCLKESC 0x14
+#define IPU7_CLK_DIV_FACTOR_IS_CLK 0x18
+#define IPU7_CLK_DIV_FACTOR_APB_CLK 0x1c
+#define IPU7_CSI_PORT_CLK_GATE 0x20
+#define IPU7_CSI_PORTAB_AGGREGATION 0x24
+#define IPU7_MGC_CLK_GATE 0x2c
+#define IPU7_CG_CTRL_BITS 0x3c
+#define IPU7_SPARE_RW 0xf8
+#define IPU7_SPARE_RO 0xfc
+
+#define IPU7_IS_IO_CSI2_MPF_PORT_BASE(i) (IPU7_IS_IO_BASE + 0x53000 + \
+ 0x1000 * (i))
+#define IPU7_MPF_16_IRQ_CNTRL_STATUS 0x238
+#define IPU7_MPF_16_IRQ_CNTRL_CLEAR 0x23c
+#define IPU7_MPF_16_IRQ_CNTRL_ENABLE 0x240
+
+/* software config the phy */
+#define IPU7_IS_IO_CSI2_GPREGS_BASE (IPU7_IS_IO_BASE + 0x53400)
+#define IPU8_IS_IO_CSI2_GPREGS_BASE (IPU7_IS_IO_BASE + 0x40e00)
+#define IPU7_CSI_ADAPT_LAYER_SRST 0x0
+#define IPU7_MPF_SRST_RST 0x4
+#define IPU7_CSI_ERR_IRQ_CTRL_SRST 0x8
+#define IPU7_CSI_SYNC_RC_SRST 0xc
+#define IPU7_CSI_CG_CTRL_BITS 0x10
+#define IPU7_SOC_CSI2HOST_SELECT 0x14
+#define IPU7_PHY_RESET 0x18
+#define IPU7_PHY_SHUTDOWN 0x1c
+#define IPU7_PHY_MODE 0x20
+#define IPU7_PHY_READY 0x24
+#define IPU7_PHY_CLK_LANE_FORCE_CONTROL 0x28
+#define IPU7_PHY_CLK_LANE_CONTROL 0x2c
+#define IPU7_PHY_CLK_LANE_STATUS 0x30
+#define IPU7_PHY_LANE_RX_ESC_REQ 0x34
+#define IPU7_PHY_LANE_RX_ESC_DATA 0x38
+#define IPU7_PHY_LANE_TURNDISABLE 0x3c
+#define IPU7_PHY_LANE_DIRECTION 0x40
+#define IPU7_PHY_LANE_FORCE_CONTROL 0x44
+#define IPU7_PHY_LANE_CONTROL_EN 0x48
+#define IPU7_PHY_LANE_CONTROL_DATAWIDTH 0x4c
+#define IPU7_PHY_LANE_STATUS 0x50
+#define IPU7_PHY_LANE_ERR 0x54
+#define IPU7_PHY_LANE_RXALP 0x58
+#define IPU7_PHY_LANE_RXALP_NIBBLE 0x5c
+#define IPU7_PHY_PARITY_ERROR 0x60
+#define IPU7_PHY_DEBUG_REGS_CLK_GATE_EN 0x64
+#define IPU7_SPARE_RW 0xf8
+#define IPU7_SPARE_RO 0xfc
+
+/* software not touch */
+#define IPU7_PORT_ARB_BASE (IPU7_IS_IO_BASE + 0x4e000)
+#define IPU7_PORT_ARB_IRQ_CTL_STATUS 0x4
+#define IPU7_PORT_ARB_IRQ_CTL_CLEAR 0x8
+#define IPU7_PORT_ARB_IRQ_CTL_ENABLE 0xc
+
+#define IPU7_MGC_PPC 4U
+#define IPU7_MGC_DTYPE_RAW(i) (((i) - 8) / 2)
+#define IPU7_IS_IO_MGC_BASE (IPU7_IS_IO_BASE + 0x48000)
+#define IPU7_MGC_KICK 0x0
+#define IPU7_MGC_ASYNC_STOP 0x4
+#define IPU7_MGC_PORT_OFFSET 0x100
+#define IPU7_MGC_CSI_PORT_MAP(i) (0x8 + (i) * 0x4)
+#define IPU7_MGC_MG_PORT(i) (IPU7_IS_IO_MGC_BASE + \
+ (i) * IPU7_MGC_PORT_OFFSET)
+/* per mgc instance */
+#define IPU7_MGC_MG_CSI_ADAPT_LAYER_TYPE 0x28
+#define IPU7_MGC_MG_MODE 0x2c
+#define IPU7_MGC_MG_INIT_COUNTER 0x30
+#define IPU7_MGC_MG_MIPI_VC 0x34
+#define IPU7_MGC_MG_MIPI_DTYPES 0x38
+#define IPU7_MGC_MG_MULTI_DTYPES_MODE 0x3c
+#define IPU7_MGC_MG_NOF_FRAMES 0x40
+#define IPU7_MGC_MG_FRAME_DIM 0x44
+#define IPU7_MGC_MG_HBLANK 0x48
+#define IPU7_MGC_MG_VBLANK 0x4c
+#define IPU7_MGC_MG_TPG_MODE 0x50
+#define IPU7_MGC_MG_TPG_R0 0x54
+#define IPU7_MGC_MG_TPG_G0 0x58
+#define IPU7_MGC_MG_TPG_B0 0x5c
+#define IPU7_MGC_MG_TPG_R1 0x60
+#define IPU7_MGC_MG_TPG_G1 0x64
+#define IPU7_MGC_MG_TPG_B1 0x68
+#define IPU7_MGC_MG_TPG_FACTORS 0x6c
+#define IPU7_MGC_MG_TPG_MASKS 0x70
+#define IPU7_MGC_MG_TPG_XY_MASK 0x74
+#define IPU7_MGC_MG_TPG_TILE_DIM 0x78
+#define IPU7_MGC_MG_PRBS_LFSR_INIT_0 0x7c
+#define IPU7_MGC_MG_PRBS_LFSR_INIT_1 0x80
+#define IPU7_MGC_MG_SYNC_STOP_POINT 0x84
+#define IPU7_MGC_MG_SYNC_STOP_POINT_LOC 0x88
+#define IPU7_MGC_MG_ERR_INJECT 0x8c
+#define IPU7_MGC_MG_ERR_LOCATION 0x90
+#define IPU7_MGC_MG_DTO_SPEED_CTRL_EN 0x94
+#define IPU7_MGC_MG_DTO_SPEED_CTRL_INCR_VAL 0x98
+#define IPU7_MGC_MG_HOR_LOC_STTS 0x9c
+#define IPU7_MGC_MG_VER_LOC_STTS 0xa0
+#define IPU7_MGC_MG_FRAME_NUM_STTS 0xa4
+#define IPU7_MGC_MG_BUSY_STTS 0xa8
+#define IPU7_MGC_MG_STOPPED_STTS 0xac
+/* tile width and height in pixels for Chess board and Color palette */
+#define IPU7_MGC_TPG_TILE_WIDTH 64U
+#define IPU7_MGC_TPG_TILE_HEIGHT 64U
+
+#define IPU7_CSI_PORT_A_ADDR_OFFSET 0x0
+#define IPU7_CSI_PORT_B_ADDR_OFFSET 0x0
+#define IPU7_CSI_PORT_C_ADDR_OFFSET 0x0
+#define IPU7_CSI_PORT_D_ADDR_OFFSET 0x0
+
+/*
+ * 0 - CSI RX Port 0 interrupt;
+ * 1 - MPF Port 0 interrupt;
+ * 2 - CSI RX Port 1 interrupt;
+ * 3 - MPF Port 1 interrupt;
+ * 4 - CSI RX Port 2 interrupt;
+ * 5 - MPF Port 2 interrupt;
+ * 6 - CSI RX Port 3 interrupt;
+ * 7 - MPF Port 3 interrupt;
+ * 8 - Port ARB FIFO 0 overflow;
+ * 9 - Port ARB FIFO 1 overflow;
+ * 10 - Port ARB FIFO 2 overflow;
+ * 11 - Port ARB FIFO 3 overflow;
+ * 12 - isys_cfgnoc_err_probe_intl;
+ * 13-15 - reserved
+ */
+#define IPU7_CSI_IS_IO_IRQ_MASK 0xffff
+
+/* Adapter layer irq */
+#define IPU7_CSI_ADPL_IRQ_MASK 0xffff
+
+/* sw irq from legacy irq control
+ * legacy irq status
+ * IPU7
+ * 0 - CSI Port 0 error interrupt
+ * 1 - CSI Port 0 sync interrupt
+ * 2 - CSI Port 1 error interrupt
+ * 3 - CSI Port 1 sync interrupt
+ * 4 - CSI Port 2 error interrupt
+ * 5 - CSI Port 2 sync interrupt
+ * 6 - CSI Port 3 error interrupt
+ * 7 - CSI Port 3 sync interrupt
+ * IPU7P5
+ * 0 - CSI Port 0 error interrupt
+ * 1 - CSI Port 0 fs interrupt
+ * 2 - CSI Port 0 fe interrupt
+ * 3 - CSI Port 1 error interrupt
+ * 4 - CSI Port 1 fs interrupt
+ * 5 - CSI Port 1 fe interrupt
+ * 6 - CSI Port 2 error interrupt
+ * 7 - CSI Port 2 fs interrupt
+ * 8 - CSI Port 2 fe interrupt
+ */
+#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff
+
+/* legacy error status per port
+ * 0 - Error handler FIFO full;
+ * 1 - Reserved Short Packet encoding detected;
+ * 2 - Reserved Long Packet encoding detected;
+ * 3 - Received packet is too short (fewer data words than specified in header);
+ * 4 - Received packet is too long (more data words than specified in header);
+ * 5 - Short packet discarded due to errors;
+ * 6 - Long packet discarded due to errors;
+ * 7 - CSI Combo Rx interrupt;
+ * 8 - IDI CDC FIFO overflow; remaining bits are reserved and tied to 0;
+ */
+#define IPU7_CSI_RX_ERROR_IRQ_MASK 0xfff
+
+/*
+ * 0 - VC0 frame start received
+ * 1 - VC0 frame end received
+ * 2 - VC1 frame start received
+ * 3 - VC1 frame end received
+ * 4 - VC2 frame start received
+ * 5 - VC2 frame end received
+ * 6 - VC3 frame start received
+ * 7 - VC3 frame end received
+ * 8 - VC4 frame start received
+ * 9 - VC4 frame end received
+ * 10 - VC5 frame start received
+ * 11 - VC5 frame end received
+ * 12 - VC6 frame start received
+ * 13 - VC6 frame end received
+ * 14 - VC7 frame start received
+ * 15 - VC7 frame end received
+ * 16 - VC8 frame start received
+ * 17 - VC8 frame end received
+ * 18 - VC9 frame start received
+ * 19 - VC9 frame end received
+ * 20 - VC10 frame start received
+ * 21 - VC10 frame end received
+ * 22 - VC11 frame start received
+ * 23 - VC11 frame end received
+ * 24 - VC12 frame start received
+ * 25 - VC12 frame end received
+ * 26 - VC13 frame start received
+ * 27 - VC13 frame end received
+ * 28 - VC14 frame start received
+ * 29 - VC14 frame end received
+ * 30 - VC15 frame start received
+ * 31 - VC15 frame end received
+ */
+
+#define IPU7_CSI_RX_SYNC_IRQ_MASK 0x0
+#define IPU7P5_CSI_RX_SYNC_FE_IRQ_MASK 0x0
+
+#define IPU7_CSI_RX_NUM_ERRORS_IN_IRQ 12U
+#define IPU7_CSI_RX_NUM_SYNC_IN_IRQ 32U
+
+enum IPU7_MGC_CSI_ADPL_TYPE {
+ IPU7_MGC_MAPPED_2_LANES = 0,
+ IPU7_MGC_MAPPED_4_LANES = 1,
+};
+
+enum IPU7_CSI2HOST_SELECTION {
+ IPU7_CSI2HOST_SEL_SOC = 0,
+ IPU7_CSI2HOST_SEL_CSI2HOST = 1,
+};
+
+#define IPU7_CSI_LEGACY_IRQ_MASK(port) (0x3 << ((port) * 2))
+#define IPU7P5_CSI_LEGACY_IRQ_MASK(port) (0x7 << ((port) * 3))
+
+#define IPU7_ISYS_LEGACY_IRQ_CSI2(port) (0x3 << (port))
+#define IPU7P5_ISYS_LEGACY_IRQ_CSI2(port) (0x7 << (port))
+
+/* ---------------------------------------------------------------- */
+#define IPU7_CSI_REG_BASE 0x220000
+#define IPU7_CSI_REG_BASE_PORT(id) ((id) * 0x1000)
+
+/* CSI Port General Purpose Registers */
+#define IPU7_CSI_REG_PORT_GPREG_SRST 0x0
+#define IPU7_CSI_REG_PORT_GPREG_CSI2_SLV_REG_SRST 0x4
+#define IPU7_CSI_REG_PORT_GPREG_CSI2_PORT_CONTROL 0x8
+
+#define IPU7_CSI_RX_SYNC_FS_VC 0x55555555
+#define IPU7_CSI_RX_SYNC_FE_VC 0xaaaaaaaa
+#define IPU7P5_CSI_RX_SYNC_FS_VC 0xffff
+#define IPU7P5_CSI_RX_SYNC_FE_VC 0xffff
+
+#endif /* IPU7_ISYS_CSI2_REG_H */
diff --git a/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.c b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.c
new file mode 100644
index 000000000000..c9b015e89ee6
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.c
@@ -0,0 +1,858 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Intel Corporation
+ */
+
+#include <linux/types.h>
+#include <linux/iopoll.h>
+
+#include "ipu6.h"
+#include "ipu6-dma.h"
+#include "ipu6-mmu.h"
+
+static struct ipu7_mmu_hw ipu7_isys_mmu_hwdata[] = {
+ {
+ .offset = IPU7_IS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_UC_RD_OFFSET,
+ .uao_offset = IPU7_IS_UAO_UC_RD_OFFSET,
+ .info_bits = 0x20006701,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = { 0x0, 0x8, 0xa },
+ .l2_block_sz = { 0x0, 0x2, 0x4 },
+ .zlx_nr = IPU7_IS_ZLX_UC_RD_NUM,
+ .zlx_axi_pool = { 0x00000f30 },
+ .zlx_en = { 0, 0, 0, 0 },
+ .zlx_conf = { 0x0, 0x0, 0x0, 0x0 },
+ .uao_p_num = IPU7_IS_UAO_UC_RD_PLANENUM,
+ .uao_p2tlb = { 0x61, 0x64, 0x65 },
+ },
+ {
+ .offset = IPU7_IS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_UC_WR_OFFSET,
+ .uao_offset = IPU7_IS_UAO_UC_WR_OFFSET,
+ .info_bits = 0x20006801,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = { 0x0, 0x8, 0xa },
+ .l2_block_sz = { 0x0, 0x2, 0x4 },
+ .zlx_nr = IPU7_IS_ZLX_UC_WR_NUM,
+ .zlx_axi_pool = { 0x00000f20 },
+ .zlx_en = { 0, 1, 1, 0 },
+ .zlx_conf = { 0x0, 0x00010101, 0x00010101 },
+ .uao_p_num = IPU7_IS_UAO_UC_WR_PLANENUM,
+ .uao_p2tlb = { 0x61, 0x62, 0x63 },
+ },
+ {
+ .offset = IPU7_IS_MMU_M0_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_M0_OFFSET,
+ .uao_offset = IPU7_IS_UAO_M0_WR_OFFSET,
+ .info_bits = 0x20006601,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_M0_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_M0_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_M0_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_M0_STREAM_NUM,
+ .l1_block_sz = { 0x0, 0x3, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10 },
+ .l2_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe },
+ .zlx_nr = IPU7_IS_ZLX_M0_NUM,
+ .zlx_axi_pool = { 0x00000f10 },
+ .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1 },
+ .zlx_conf = {
+ 0x00010103,
+ 0x00010103,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_IS_UAO_M0_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000049,
+ 0x0000004a,
+ 0x0000004b,
+ 0x0000004c,
+ 0x0000004d,
+ 0x0000004e,
+ 0x0000004f,
+ 0x00000050,
+ },
+ },
+ {
+ .offset = IPU7_IS_MMU_M1_OFFSET,
+ .zlx_offset = IPU7_IS_ZLX_M1_OFFSET,
+ .uao_offset = IPU7_IS_UAO_M1_WR_OFFSET,
+ .info_bits = 0x20006901,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_IS_MMU_M1_L1_BLOCKNR_REG,
+ .l2_block = IPU7_IS_MMU_M1_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_IS_MMU_M1_STREAM_NUM,
+ .nr_l2streams = IPU7_IS_MMU_M1_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x3, 0x6, 0x9, 0xc,
+ 0xe, 0x10, 0x12, 0x14, 0x16,
+ 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ },
+ .zlx_nr = IPU7_IS_ZLX_M1_NUM,
+ .zlx_axi_pool = { 0x00000f20 },
+ .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ },
+ .zlx_conf = {
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010103,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_IS_UAO_M1_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000051,
+ 0x00000052,
+ 0x00000053,
+ 0x00000054,
+ 0x00000055,
+ 0x00000056,
+ 0x00000057,
+ 0x00000058,
+ 0x00000059,
+ 0x0000005a,
+ 0x0000005b,
+ 0x0000005c,
+ 0x0000005d,
+ 0x0000005e,
+ 0x0000005f,
+ 0x00000060,
+ },
+ },
+};
+
+static struct ipu7_mmu_hw ipu7_psys_mmu_hwdata[] = {
+ {
+ .name = "PS_FW_RD",
+ .offset = IPU7_PS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_FW_RD_OFFSET,
+ .uao_offset = IPU7_PS_UAO_FW_RD_OFFSET,
+ .info_bits = 0x20004801,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0, 0x8, 0xa, 0xc, 0xd,
+ 0xf, 0x11, 0x12, 0x13, 0x14,
+ 0x16, 0x18, 0x19, 0x1a, 0x1a,
+ 0x1a, 0x1a, 0x1a, 0x1a, 0x1a,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c,
+ 0x1e, 0x20, 0x22, 0x24, 0x26,
+ },
+ .zlx_nr = IPU7_PS_ZLX_FW_RD_NUM,
+ .zlx_axi_pool = { 0x00000f30 },
+ .zlx_en = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ .zlx_conf = { 0x0 },
+ .uao_p_num = IPU7_PS_UAO_FW_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000036,
+ 0x0000003d,
+ 0x0000003e,
+ 0x00000039,
+ 0x0000003f,
+ 0x00000040,
+ 0x00000041,
+ 0x0000003a,
+ 0x0000003b,
+ 0x00000042,
+ 0x00000043,
+ 0x00000044,
+ 0x0000003c,
+ },
+ },
+ {
+ .offset = IPU7_PS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_FW_WR_OFFSET,
+ .uao_offset = IPU7_PS_UAO_FW_WR_OFFSET,
+ .info_bits = 0x20004601,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0, 0x8, 0xa, 0xc, 0xd,
+ 0xe, 0xf, 0x10, 0x10, 0x10,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ },
+ .zlx_nr = IPU7_PS_ZLX_FW_WR_NUM,
+ .zlx_axi_pool = { 0x00000f20 },
+ .zlx_en = { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
+ .zlx_conf = { 0x0, 0x00010101, 0x00010101 },
+ .uao_p_num = IPU7_PS_UAO_FW_WR_PLANENUM,
+ .uao_p2tlb = { 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c },
+ },
+ {
+ .offset = IPU7_PS_MMU_SRT_RD_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_DATA_RD_OFFSET,
+ .uao_offset = IPU7_PS_UAO_SRT_RD_OFFSET,
+ .info_bits = 0x20004701,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_SRT_RD_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x4, 0x6, 0x8, 0xb,
+ 0xd, 0xf, 0x11, 0x13, 0x15,
+ 0x17, 0x23, 0x2b, 0x37, 0x3f,
+ 0x41, 0x43, 0x44, 0x45, 0x46,
+ 0x47, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
+ 0x51, 0x52, 0x53, 0x55, 0x57,
+ 0x59, 0x5b, 0x5d, 0x5f, 0x61,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c,
+ 0x1e, 0x20, 0x22, 0x24, 0x26,
+ 0x28, 0x2a, 0x2c, 0x2e, 0x30,
+ 0x32, 0x34, 0x36, 0x38, 0x3a,
+ 0x3c, 0x3e, 0x40, 0x42, 0x44,
+ 0x46, 0x48, 0x4a, 0x4c, 0x4e,
+ },
+ .zlx_nr = IPU7_PS_ZLX_DATA_RD_NUM,
+ .zlx_axi_pool = { 0x00000f30 },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00030303,
+ 0x00010101,
+ 0x00010101,
+ 0x00030202,
+ 0x00010101,
+ 0x00010101,
+ 0x00010101,
+ 0x00030800,
+ 0x00030500,
+ 0x00020101,
+ 0x00042000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00020400,
+ 0x00010101,
+ },
+ .uao_p_num = IPU7_PS_UAO_SRT_RD_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000022,
+ 0x00000023,
+ 0x00000024,
+ 0x00000025,
+ 0x00000026,
+ 0x00000027,
+ 0x00000028,
+ 0x00000029,
+ 0x0000002a,
+ 0x0000002b,
+ 0x0000002c,
+ 0x0000002d,
+ 0x0000002e,
+ 0x0000002f,
+ 0x00000030,
+ 0x00000031,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0000001e,
+ 0x0000001f,
+ 0x00000020,
+ 0x00000021,
+ 0x00000032,
+ 0x00000033,
+ 0x00000034,
+ 0x00000035,
+ },
+ },
+ {
+ .offset = IPU7_PS_MMU_SRT_WR_OFFSET,
+ .zlx_offset = IPU7_PS_ZLX_DATA_WR_OFFSET,
+ .uao_offset = IPU7_PS_UAO_SRT_WR_OFFSET,
+ .info_bits = 0x20004501,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x0,
+ .l1_block = IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM,
+ .nr_l2streams = IPU7_PS_MMU_SRT_WR_STREAM_NUM,
+ .l1_block_sz = {
+ 0x0, 0x2, 0x6, 0xa, 0xc,
+ 0xe, 0x10, 0x12, 0x14, 0x16,
+ 0x18, 0x1a, 0x1c, 0x1e, 0x20,
+ 0x22, 0x24, 0x26, 0x32, 0x3a,
+ 0x3c, 0x3e, 0x4a, 0x52, 0x58,
+ 0x64, 0x6c, 0x72, 0x7e, 0x86,
+ 0x8c, 0x8d, 0x8e, 0x8f, 0x90,
+ 0x91, 0x92, 0x94, 0x96, 0x98,
+ },
+ .l2_block_sz = {
+ 0x0, 0x2, 0x4, 0x6, 0x8,
+ 0xa, 0xc, 0xe, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c,
+ 0x1e, 0x20, 0x22, 0x24, 0x26,
+ 0x28, 0x2a, 0x2c, 0x2e, 0x30,
+ 0x32, 0x34, 0x36, 0x38, 0x3a,
+ 0x3c, 0x3e, 0x40, 0x42, 0x44,
+ 0x46, 0x48, 0x4a, 0x4c, 0x4e,
+ },
+ .zlx_nr = IPU7_PS_ZLX_DATA_WR_NUM,
+ .zlx_axi_pool = { 0x00000f50 },
+ .zlx_en = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 0, 0,
+ },
+ .zlx_conf = {
+ 0x00010102,
+ 0x00030103,
+ 0x00030103,
+ 0x00010101,
+ 0x00010101,
+ 0x00030101,
+ 0x00010101,
+ 0x38010101,
+ 0x0,
+ 0x0,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x38010101,
+ 0x00010101,
+ 0x00042000,
+ 0x00031000,
+ 0x00010101,
+ 0x00010101,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x00042000,
+ 0x00031000,
+ 0x00031000,
+ 0x0,
+ 0x0,
+ },
+ .uao_p_num = IPU7_PS_UAO_SRT_WR_PLANENUM,
+ .uao_p2tlb = {
+ 0x00000000,
+ 0x00000001,
+ 0x00000002,
+ 0x00000003,
+ 0x00000004,
+ 0x00000005,
+ 0x00000006,
+ 0x00000007,
+ 0x00000008,
+ 0x00000009,
+ 0x0000000a,
+ 0x0000000b,
+ 0x0000000c,
+ 0x0000000d,
+ 0x0000000e,
+ 0x0000000f,
+ 0x00000010,
+ 0x00000011,
+ 0x00000012,
+ 0x00000013,
+ 0x00000014,
+ 0x00000015,
+ 0x00000016,
+ 0x00000017,
+ 0x00000018,
+ 0x00000019,
+ 0x0000001a,
+ 0x0000001b,
+ 0x0000001c,
+ 0x0000001d,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0000001e,
+ 0x0000001f,
+ 0x00000020,
+ 0x00000021,
+ },
+ },
+};
+
+static struct ipu7_mmu_hw ipu7p5_isys_mmu_hwdata[] = {
+ {
+ .name = "IS_FW_RD",
+ .offset = IPU7P5_IS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_UC_RD_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_UC_RD_OFFSET,
+ .info_bits = 0x20005101,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = { 0x0, 0x8, 0xa },
+ .l2_block_sz = { 0x0, 0x2, 0x4 },
+ .zlx_nr = IPU7P5_IS_ZLX_UC_RD_NUM,
+ .zlx_axi_pool = { 0x00000f30 },
+ .zlx_en = { 0, 1, 0, 0 },
+ .zlx_conf = { 0x0 },
+ .uao_p_num = IPU7P5_IS_UAO_UC_RD_PLANENUM,
+ .uao_p2tlb = { 0x49, 0x4c, 0x4d, 0x0 },
+ },
+ {
+ .name = "IS_FW_WR",
+ .offset = IPU7P5_IS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_UC_WR_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_UC_WR_OFFSET,
+ .info_bits = 0x20005001,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = { 0x0, 0x8, 0xa },
+ .l2_block_sz = { 0x0, 0x2, 0x4 },
+ .zlx_nr = IPU7P5_IS_ZLX_UC_WR_NUM,
+ .zlx_axi_pool = { 0x00000f20 },
+ .zlx_en = { 0, 1, 1, 0 },
+ .zlx_conf = { 0x0, 0x00010101, 0x00010101, 0x0 },
+ .uao_p_num = IPU7P5_IS_UAO_UC_WR_PLANENUM,
+ .uao_p2tlb = { 0x49, 0x4a, 0x4b, 0x0 },
+ },
+ {
+ .name = "IS_DATA_WR_ISOC",
+ .offset = IPU7P5_IS_MMU_M0_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_M0_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_M0_WR_OFFSET,
+ .info_bits = 0x20004e01,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_M0_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_M0_STREAM_NUM,
+ .l1_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10,
+ 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x01e },
+ .l2_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10,
+ 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e },
+ .zlx_nr = IPU7P5_IS_ZLX_M0_NUM,
+ .zlx_axi_pool = { 0x00000f10 },
+ .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ .zlx_conf = { 0x00010103, 0x00010103, 0x00010103, 0x00010103,
+ 0x00010103, 0x00010103, 0x00010103, 0x00010103,
+ 0x00010103, 0x00010103, 0x00010103, 0x00010103,
+ 0x00010103, 0x00010103, 0x00010103, 0x00010103 },
+ .uao_p_num = IPU7P5_IS_UAO_M0_WR_PLANENUM,
+ .uao_p2tlb = { 0x00000041, 0x00000042, 0x00000043, 0x00000044,
+ 0x00000041, 0x00000042, 0x00000043, 0x00000044,
+ 0x00000041, 0x00000042, 0x00000043, 0x00000044,
+ 0x00000041, 0x00000042, 0x00000043, 0x00000044 },
+ },
+ {
+ .name = "IS_DATA_WR_SNOOP",
+ .offset = IPU7P5_IS_MMU_M1_OFFSET,
+ .zlx_offset = IPU7P5_IS_ZLX_M1_OFFSET,
+ .uao_offset = IPU7P5_IS_UAO_M1_WR_OFFSET,
+ .info_bits = 0x20004f01,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_IS_MMU_M1_STREAM_NUM,
+ .nr_l2streams = IPU7P5_IS_MMU_M1_STREAM_NUM,
+ .l1_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10,
+ 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e },
+ .l2_block_sz = { 0x0, 0x2, 0x4, 0x6, 0x8, 0xa, 0xc, 0xe, 0x10,
+ 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e },
+ .zlx_nr = IPU7P5_IS_ZLX_M1_NUM,
+ .zlx_axi_pool = { 0x00000f20 },
+ .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+ .zlx_conf = { 0x00010103, 0x00010103, 0x00010103, 0x00010103,
+ 0x00010103, 0x00010103, 0x00010103, 0x00010103,
+ 0x00010103, 0x00010103, 0x00010103, 0x00010103,
+ 0x00010103, 0x00010103, 0x00010103, 0x00010103 },
+ .uao_p_num = IPU7P5_IS_UAO_M1_WR_PLANENUM,
+ .uao_p2tlb = { 0x00000045, 0x00000046, 0x00000047, 0x00000048,
+ 0x00000045, 0x00000046, 0x00000047, 0x00000048,
+ 0x00000045, 0x00000046, 0x00000047, 0x00000048,
+ 0x00000045, 0x00000046, 0x00000047, 0x00000048 },
+ },
+};
+
+static struct ipu7_mmu_hw ipu7p5_psys_mmu_hwdata[] = {
+ {
+ .name = "PS_FW_RD",
+ .offset = IPU7P5_PS_MMU_FW_RD_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_FW_RD_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_FW_RD_OFFSET,
+ .info_bits = 0x20004001,
+ .refill = 0x00002726,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_FW_RD_STREAM_NUM,
+ .l1_block_sz = { 0x00, 0x08, 0x0a, 0x0c, 0x0d, 0x0f, 0x11,
+ 0x12, 0x13, 0x14, 0x16, 0x18, 0x19, 0x1a,
+ 0x1a, 0x1a },
+ .l2_block_sz = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c,
+ 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a,
+ 0x1c, 0x1e },
+ .zlx_nr = IPU7P5_PS_ZLX_FW_RD_NUM,
+ .zlx_axi_pool = { 0x00000f30 },
+ .zlx_en = { 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
+ .zlx_conf = { 0x00000000, 0x00010101, 0x00000000, 0x00000000,
+ 0x00010101, 0x00010101, 0x00000000, 0x00000000,
+ 0x00000000, 0x00010101, 0x00010101, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ .uao_p_num = IPU7P5_PS_UAO_FW_RD_PLANENUM,
+ .uao_p2tlb = { 0x2e, 0x35, 0x36, 0x31, 0x37, 0x38, 0x39, 0x32,
+ 0x33, 0x3a, 0x3b, 0x3c, 0x34, 0x00, 0x00, 0x00 },
+ },
+ {
+ .name = "PS_FW_WR",
+ .offset = IPU7P5_PS_MMU_FW_WR_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_FW_WR_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_FW_WR_OFFSET,
+ .info_bits = 0x20003e01,
+ .refill = 0x00002322,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_FW_WR_STREAM_NUM,
+ .l1_block_sz = { 0x00, 0x08, 0x0a, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x10, 0x10 },
+ .l2_block_sz = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12 },
+ .zlx_nr = IPU7P5_PS_ZLX_FW_WR_NUM,
+ .zlx_axi_pool = { 0x00000f20 },
+ .zlx_en = { 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 },
+ .zlx_conf = { 0x00000000, 0x00010101, 0x00010101, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 },
+ .uao_p_num = IPU7P5_PS_UAO_FW_WR_PLANENUM,
+ .uao_p2tlb = { 0x0000002e, 0x0000002f, 0x00000030, 0x00000031,
+ 0x00000032, 0x00000033, 0x00000034, 0x00000000,
+ 0x00000000, 0x00000000 },
+ },
+ {
+ .name = "PS_DATA_RD",
+ .offset = IPU7P5_PS_MMU_SRT_RD_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_DATA_RD_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_SRT_RD_OFFSET,
+ .info_bits = 0x20003f01,
+ .refill = 0x00002524,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_SRT_RD_STREAM_NUM,
+ .l1_block_sz = { 0x00, 0x04, 0x06, 0x08, 0x0b, 0x0d, 0x0f, 0x13,
+ 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x2b, 0x33, 0x3f,
+ 0x47, 0x49, 0x4b, 0x4c, 0x4d, 0x4e },
+ .l2_block_sz = { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
+ 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
+ 0x20, 0x22, 0x24, 0x26, 0x28, 0x2a },
+ .zlx_nr = IPU7P5_PS_ZLX_DATA_RD_NUM,
+ .zlx_axi_pool = { 0x00000f30 },
+ .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 0, 0, 0, 0 },
+ .zlx_conf = { 0x00030303, 0x00010101, 0x00010101, 0x00030202,
+ 0x00010101, 0x00010101, 0x00030303, 0x00030303,
+ 0x00010101, 0x00030800, 0x00030500, 0x00020101,
+ 0x00042000, 0x00031000, 0x00042000, 0x00031000,
+ 0x00020400, 0x00010101, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 },
+ .uao_p_num = IPU7P5_PS_UAO_SRT_RD_PLANENUM,
+ .uao_p2tlb = { 0x0000001c, 0x0000001d, 0x0000001e, 0x0000001f,
+ 0x00000020, 0x00000021, 0x00000022, 0x00000023,
+ 0x00000024, 0x00000025, 0x00000026, 0x00000027,
+ 0x00000028, 0x00000029, 0x0000002a, 0x0000002b,
+ 0x0000002c, 0x0000002d, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000 },
+ },
+ {
+ .name = "PS_DATA_WR",
+ .offset = IPU7P5_PS_MMU_SRT_WR_OFFSET,
+ .zlx_offset = IPU7P5_PS_ZLX_DATA_WR_OFFSET,
+ .uao_offset = IPU7P5_PS_UAO_SRT_WR_OFFSET,
+ .info_bits = 0x20003d01,
+ .refill = 0x00002120,
+ .collapse_en_bitmap = 0x1,
+ .at_sp_arb_cfg = 0x1,
+ .l1_block = IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG,
+ .l2_block = IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG,
+ .nr_l1streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM,
+ .nr_l2streams = IPU7P5_PS_MMU_SRT_WR_STREAM_NUM,
+ .l1_block_sz = { 0x00, 0x02, 0x06, 0x0a, 0x0c, 0x0e, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22,
+ 0x24, 0x28, 0x2a, 0x36, 0x3e, 0x40, 0x42, 0x4e,
+ 0x56, 0x5c, 0x68, 0x70, 0x76, 0x77, 0x78, 0x79 },
+ .l2_block_sz = { 0x00, 0x02, 0x06, 0x0a, 0x0c, 0x0e, 0x10, 0x12,
+ 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e, 0x20, 0x22,
+ 0x24, 0x28, 0x2a, 0x36, 0x3e, 0x40, 0x42, 0x4e,
+ 0x56, 0x5c, 0x68, 0x70, 0x76, 0x77, 0x78, 0x79 },
+ .zlx_nr = IPU7P5_PS_ZLX_DATA_WR_NUM,
+ .zlx_axi_pool = { 0x00000f50 },
+ .zlx_en = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 },
+ .zlx_conf = { 0x00010102, 0x00030103, 0x00030103, 0x00010101,
+ 0x00010101, 0x00030101, 0x00010101, 0x38010101,
+ 0x00000000, 0x00000000, 0x38010101, 0x38010101,
+ 0x38010101, 0x38010101, 0x38010101, 0x38010101,
+ 0x00030303, 0x00010101, 0x00042000, 0x00031000,
+ 0x00010101, 0x00010101, 0x00042000, 0x00031000,
+ 0x00031000, 0x00042000, 0x00031000, 0x00031000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
+ .uao_p_num = IPU7P5_PS_UAO_SRT_WR_PLANENUM,
+ .uao_p2tlb = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x00, 0x00, 0x00, 0x00 },
+ },
+};
+
+static const struct ipu7_mmu_hwdata ipu7_mmu_hwdata_lookup[IPU_SUBSYS_NUM] = {
+ [IPU_PSYS] = {
+ .hwdata = ipu7_psys_mmu_hwdata,
+ .nr_mmus = ARRAY_SIZE(ipu7_psys_mmu_hwdata),
+ },
+ [IPU_ISYS] = {
+ .hwdata = ipu7_isys_mmu_hwdata,
+ .nr_mmus = ARRAY_SIZE(ipu7_isys_mmu_hwdata),
+ },
+};
+
+static const struct ipu7_mmu_hwdata ipu7p5_mmu_hwdata_lookup[IPU_SUBSYS_NUM] = {
+ [IPU_PSYS] = {
+ .hwdata = ipu7p5_psys_mmu_hwdata,
+ .nr_mmus = ARRAY_SIZE(ipu7p5_psys_mmu_hwdata),
+ },
+ [IPU_ISYS] = {
+ .hwdata = ipu7p5_isys_mmu_hwdata,
+ .nr_mmus = ARRAY_SIZE(ipu7p5_isys_mmu_hwdata),
+ },
+};
+
+static void __ipu7_tlb_invalidate(struct ipu6_mmu *mmu)
+{
+ struct ipu7_mmu_hw *mmu_hw = mmu->ipu7_mmu_hw;
+ unsigned long flags;
+ unsigned int i;
+ int ret;
+ u32 val;
+
+ spin_lock_irqsave(&mmu->ready_lock, flags);
+ if (!mmu->ready) {
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+ return;
+ }
+
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ writel(0xffffffffU, mmu_hw[i].base +
+ IPU7_MMU_REG_INVALIDATE_0);
+
+ /* Need check with HW, use l1streams or l2streams */
+ if (mmu_hw[i].nr_l2streams > 32)
+ writel(0xffffffffU, mmu_hw[i].base +
+ IPU7_MMU_REG_INVALIDATE_1);
+
+ /*
+ * The TLB invalidation is a "single cycle" (IOMMU clock cycles)
+ * When the actual MMIO write reaches the IPU TLB Invalidate
+ * register, wmb() will force the TLB invalidate out if the CPU
+ * attempts to update the IOMMU page table (or sooner).
+ */
+ wmb();
+
+ /* wait invalidation done */
+ ret = readl_poll_timeout_atomic(mmu_hw[i].base +
+ IPU7_MMU_REG_INVALIDATION_STATUS,
+ val, !(val & 0x1U), 500,
+ IPU7_MMU_TLB_INVALIDATE_TIMEOUT);
+ if (ret)
+ dev_err(mmu->dev, "MMU[%u] TLB invalidate failed\n", i);
+ }
+
+ spin_unlock_irqrestore(&mmu->ready_lock, flags);
+}
+
+static int __ipu7_mmu_hw_init(struct ipu6_mmu *mmu)
+{
+ struct ipu6_mmu_info *mmu_info;
+ struct ipu7_mmu_hw *mmu_hw = mmu->ipu7_mmu_hw;
+ unsigned int i, j;
+
+ mmu_info = mmu->dmap->mmu_info;
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ /* Write page table address per MMU */
+ writel((phys_addr_t)mmu_info->l1_pt_dma,
+ mmu_hw[i].base + IPU7_MMU_REG_PAGE_TABLE_BASE_ADDR);
+
+ /* Set info bits and axi_refill per MMU */
+ writel(mmu_hw[i].info_bits,
+ mmu_hw[i].base + IPU7_MMU_REG_USER_INFO_BITS);
+ writel(mmu_hw[i].refill, mmu_hw[i].base + IPU7_MMU_REG_AXI_REFILL_IF_ID);
+ writel(mmu_hw[i].collapse_en_bitmap,
+ mmu_hw[i].base + IPU7_MMU_REG_COLLAPSE_ENABLE_BITMAP);
+
+ if (mmu_hw[i].at_sp_arb_cfg)
+ writel(mmu_hw[i].at_sp_arb_cfg,
+ mmu_hw[i].base + IPU7_MMU_REG_AT_SP_ARB_CFG);
+
+ /* Default irq configuration */
+ writel(0x3ff, mmu_hw[i].base + IPU7_MMU_REG_IRQ_MASK);
+ writel(0x3ff, mmu_hw[i].base + IPU7_MMU_REG_IRQ_ENABLE);
+
+ /* Configure MMU TLB stream configuration for L1/L2 */
+ for (j = 0; j < mmu_hw[i].nr_l1streams; j++) {
+ writel(mmu_hw[i].l1_block_sz[j], mmu_hw[i].base +
+ mmu_hw[i].l1_block + 4U * j);
+ }
+
+ for (j = 0; j < mmu_hw[i].nr_l2streams; j++) {
+ writel(mmu_hw[i].l2_block_sz[j], mmu_hw[i].base +
+ mmu_hw[i].l2_block + 4U * j);
+ }
+
+ for (j = 0; j < mmu_hw[i].uao_p_num; j++) {
+ if (!mmu_hw[i].uao_p2tlb[j])
+ continue;
+ writel(mmu_hw[i].uao_p2tlb[j], mmu_hw[i].uao_base + 4U * j);
+ }
+ }
+
+ for (i = 0; i < mmu->nr_mmus; i++) {
+ for (j = 0; j < IPU7_ZLX_POOL_NUM; j++) {
+ if (!mmu_hw[i].zlx_axi_pool[j])
+ continue;
+ writel(mmu_hw[i].zlx_axi_pool[j],
+ mmu_hw[i].zlx_base + IPU7_ZLX_REG_AXI_POOL + j * 0x4U);
+ }
+
+ for (j = 0; j < mmu_hw[i].zlx_nr; j++) {
+ if (!mmu_hw[i].zlx_conf[j])
+ continue;
+
+ writel(mmu_hw[i].zlx_conf[j],
+ mmu_hw[i].zlx_base + IPU7_ZLX_REG_CONF + j * 0x8U);
+ }
+
+ for (j = 0; j < mmu_hw[i].zlx_nr; j++) {
+ if (!mmu_hw[i].zlx_en[j])
+ continue;
+
+ writel(mmu_hw[i].zlx_en[j],
+ mmu_hw[i].zlx_base + IPU7_ZLX_REG_EN + j * 0x8U);
+ }
+ }
+
+ return 0;
+}
+
+static int __ipu7_mmu_init_hw_data(struct ipu6_mmu *mmu, struct device *dev,
+ void __iomem *base)
+{
+ struct ipu6_device *isp = pci_get_drvdata(to_pci_dev(dev));
+ const struct ipu7_mmu_hwdata *lookup;
+ struct ipu7_mmu_hw *mmu_hw, *src;
+ unsigned int i, nr_mmus;
+
+ if (mmu->mmid >= IPU_SUBSYS_NUM)
+ return -EINVAL;
+
+ lookup = IS_IPU7P5(isp) ? ipu7p5_mmu_hwdata_lookup :
+ ipu7_mmu_hwdata_lookup;
+
+ src = lookup[mmu->mmid].hwdata;
+ nr_mmus = lookup[mmu->mmid].nr_mmus;
+
+ mmu_hw = devm_kcalloc(dev, nr_mmus, sizeof(*mmu_hw), GFP_KERNEL);
+ if (!mmu_hw)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_mmus; i++) {
+ if (src[i].nr_l1streams > IPU7_MMU_MAX_TLB_L1_STREAMS ||
+ src[i].nr_l2streams > IPU7_MMU_MAX_TLB_L2_STREAMS)
+ return -EINVAL;
+
+ mmu_hw[i] = src[i];
+ mmu_hw[i].base = base + src[i].offset;
+ mmu_hw[i].zlx_base = base + src[i].zlx_offset;
+ mmu_hw[i].uao_base = base + src[i].uao_offset;
+ }
+
+ mmu->nr_mmus = nr_mmus;
+ mmu->ipu7_mmu_hw = mmu_hw;
+
+ return 0;
+}
+
+const struct ipu6_mmu_hw_ops ipu7_mmu_ops = {
+ .init_hw_data = __ipu7_mmu_init_hw_data,
+ .hw_init = __ipu7_mmu_hw_init,
+ .tlb_invalidate = __ipu7_tlb_invalidate,
+};
diff --git a/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.h b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.h
new file mode 100644
index 000000000000..ba31ed31b245
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-mmu-hw.h
@@ -0,0 +1,254 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2026 Intel Corporation */
+
+#ifndef IPU7_MMU_HW_H
+#define IPU7_MMU_HW_H
+
+#define IPU7_MMU_REG_IRQ_MASK 0x2c
+#define IPU7_MMU_REG_IRQ_ENABLE 0x30
+#define IPU7_MMU_REG_PAGE_TABLE_BASE_ADDR 0x08
+#define IPU7_MMU_REG_USER_INFO_BITS 0x0c
+#define IPU7_MMU_REG_AXI_REFILL_IF_ID 0x10
+#define IPU7_MMU_REG_COLLAPSE_ENABLE_BITMAP 0x18
+#define IPU7_MMU_REG_AT_SP_ARB_CFG 0x20
+
+#define IPU7_ZLX_REG_AXI_POOL 0x0
+#define IPU7_ZLX_REG_EN 0x20
+#define IPU7_ZLX_REG_CONF 0x24
+#define IPU7_ZLX_POOL_NUM 8U
+
+#define IPU7_MMU_MAX_TLB_L1_STREAMS 40U
+#define IPU7_MMU_MAX_TLB_L2_STREAMS 40U
+#define IPU7_UAO_PLANE_MAX_NUM 64U
+#define IPU7_ZLX_MAX_NUM 32U
+
+#define IPU7_MMU_REG_INVALIDATE_0 0x00
+#define IPU7_MMU_REG_INVALIDATE_1 0x04
+#define IPU7_MMU_REG_INVALIDATION_STATUS 0x24
+#define IPU7_MMU_TLB_INVALIDATE_TIMEOUT 2000
+
+#define IPU7_FW_CODE_REGION_SIZE 0x1000000 /* 16MB */
+#define IPU7_FW_CODE_REGION_START 0x4000000 /* 64MB */
+#define IPU7_FW_CODE_REGION_END (IPU7_FW_CODE_REGION_START + \
+ IPU7_FW_CODE_REGION_SIZE) /* 80MB */
+
+#define IPU7_IS_MMU_FW_RD_OFFSET 0x274000
+#define IPU7_IS_MMU_FW_RD_STREAM_NUM 3
+#define IPU7_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60
+
+#define IPU7_IS_MMU_FW_WR_OFFSET 0x275000
+#define IPU7_IS_MMU_FW_WR_STREAM_NUM 3
+#define IPU7_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60
+
+#define IPU7_IS_MMU_M0_OFFSET 0x276000
+#define IPU7_IS_MMU_M0_STREAM_NUM 8
+#define IPU7_IS_MMU_M0_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_M0_L2_BLOCKNR_REG 0x74
+
+#define IPU7_IS_MMU_M1_OFFSET 0x277000
+#define IPU7_IS_MMU_M1_STREAM_NUM 16
+#define IPU7_IS_MMU_M1_L1_BLOCKNR_REG 0x54
+#define IPU7_IS_MMU_M1_L2_BLOCKNR_REG 0x94
+
+#define IPU7_PS_MMU_FW_RD_OFFSET 0x148000
+#define IPU7_PS_MMU_FW_RD_STREAM_NUM 20
+#define IPU7_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_FW_RD_L2_BLOCKNR_REG 0xa4
+
+#define IPU7_PS_MMU_FW_WR_OFFSET 0x149000
+#define IPU7_PS_MMU_FW_WR_STREAM_NUM 10
+#define IPU7_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c
+
+#define IPU7_PS_MMU_SRT_RD_OFFSET 0x14a000
+#define IPU7_PS_MMU_SRT_RD_STREAM_NUM 40
+#define IPU7_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xf4
+
+#define IPU7_PS_MMU_SRT_WR_OFFSET 0x14b000
+#define IPU7_PS_MMU_SRT_WR_STREAM_NUM 40
+#define IPU7_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54
+#define IPU7_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xf4
+
+#define IPU7_IS_UAO_UC_RD_OFFSET 0x27c000
+#define IPU7_IS_UAO_UC_RD_PLANENUM 4
+
+#define IPU7_IS_UAO_UC_WR_OFFSET 0x27d000
+#define IPU7_IS_UAO_UC_WR_PLANENUM 4
+
+#define IPU7_IS_UAO_M0_WR_OFFSET 0x27e000
+#define IPU7_IS_UAO_M0_WR_PLANENUM 8
+
+#define IPU7_IS_UAO_M1_WR_OFFSET 0x27f000
+#define IPU7_IS_UAO_M1_WR_PLANENUM 16
+
+#define IPU7_PS_UAO_FW_RD_OFFSET 0x156000
+#define IPU7_PS_UAO_FW_RD_PLANENUM 20
+
+#define IPU7_PS_UAO_FW_WR_OFFSET 0x157000
+#define IPU7_PS_UAO_FW_WR_PLANENUM 16
+
+#define IPU7_PS_UAO_SRT_RD_OFFSET 0x154000
+#define IPU7_PS_UAO_SRT_RD_PLANENUM 40
+
+#define IPU7_PS_UAO_SRT_WR_OFFSET 0x155000
+#define IPU7_PS_UAO_SRT_WR_PLANENUM 40
+
+#define IPU7_IS_ZLX_UC_RD_OFFSET 0x278000
+#define IPU7_IS_ZLX_UC_WR_OFFSET 0x279000
+#define IPU7_IS_ZLX_M0_OFFSET 0x27a000
+#define IPU7_IS_ZLX_M1_OFFSET 0x27b000
+#define IPU7_IS_ZLX_UC_RD_NUM 4
+#define IPU7_IS_ZLX_UC_WR_NUM 4
+#define IPU7_IS_ZLX_M0_NUM 8
+#define IPU7_IS_ZLX_M1_NUM 16
+
+#define IPU7_PS_ZLX_DATA_RD_OFFSET 0x14e000
+#define IPU7_PS_ZLX_DATA_WR_OFFSET 0x14f000
+#define IPU7_PS_ZLX_FW_RD_OFFSET 0x150000
+#define IPU7_PS_ZLX_FW_WR_OFFSET 0x151000
+#define IPU7_PS_ZLX_DATA_RD_NUM 32
+#define IPU7_PS_ZLX_DATA_WR_NUM 32
+#define IPU7_PS_ZLX_FW_RD_NUM 16
+#define IPU7_PS_ZLX_FW_WR_NUM 10
+
+/* IPU7P5 */
+/* IS MMU Cmd RD */
+#define IPU7P5_IS_MMU_FW_RD_OFFSET 0x274000
+#define IPU7P5_IS_MMU_FW_RD_STREAM_NUM 3
+#define IPU7P5_IS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_FW_RD_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Cmd WR */
+#define IPU7P5_IS_MMU_FW_WR_OFFSET 0x275000
+#define IPU7P5_IS_MMU_FW_WR_STREAM_NUM 3
+#define IPU7P5_IS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_FW_WR_L2_BLOCKNR_REG 0x60
+
+/* IS MMU Data WR Snoop */
+#define IPU7P5_IS_MMU_M0_OFFSET 0x276000
+#define IPU7P5_IS_MMU_M0_STREAM_NUM 16
+#define IPU7P5_IS_MMU_M0_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_M0_L2_BLOCKNR_REG 0x94
+
+/* IS MMU Data WR ISOC */
+#define IPU7P5_IS_MMU_M1_OFFSET 0x277000
+#define IPU7P5_IS_MMU_M1_STREAM_NUM 16
+#define IPU7P5_IS_MMU_M1_L1_BLOCKNR_REG 0x54
+#define IPU7P5_IS_MMU_M1_L2_BLOCKNR_REG 0x94
+
+/* PS MMU FW RD */
+#define IPU7P5_PS_MMU_FW_RD_OFFSET 0x148000
+#define IPU7P5_PS_MMU_FW_RD_STREAM_NUM 16
+#define IPU7P5_PS_MMU_FW_RD_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_FW_RD_L2_BLOCKNR_REG 0x94
+
+/* PS MMU FW WR */
+#define IPU7P5_PS_MMU_FW_WR_OFFSET 0x149000
+#define IPU7P5_PS_MMU_FW_WR_STREAM_NUM 10
+#define IPU7P5_PS_MMU_FW_WR_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_FW_WR_L2_BLOCKNR_REG 0x7c
+
+/* PS MMU FW Data RD VC0 */
+#define IPU7P5_PS_MMU_SRT_RD_OFFSET 0x14a000
+#define IPU7P5_PS_MMU_SRT_RD_STREAM_NUM 22
+#define IPU7P5_PS_MMU_SRT_RD_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_SRT_RD_L2_BLOCKNR_REG 0xac
+
+/* PS MMU FW Data WR VC0 */
+#define IPU7P5_PS_MMU_SRT_WR_OFFSET 0x14b000
+#define IPU7P5_PS_MMU_SRT_WR_STREAM_NUM 32
+#define IPU7P5_PS_MMU_SRT_WR_L1_BLOCKNR_REG 0x54
+#define IPU7P5_PS_MMU_SRT_WR_L2_BLOCKNR_REG 0xd4
+
+/* IS UAO UC RD */
+#define IPU7P5_IS_UAO_UC_RD_OFFSET 0x27c000
+#define IPU7P5_IS_UAO_UC_RD_PLANENUM 4
+
+/* IS UAO UC WR */
+#define IPU7P5_IS_UAO_UC_WR_OFFSET 0x27d000
+#define IPU7P5_IS_UAO_UC_WR_PLANENUM 4
+
+/* IS UAO M0 WR */
+#define IPU7P5_IS_UAO_M0_WR_OFFSET 0x27e000
+#define IPU7P5_IS_UAO_M0_WR_PLANENUM 16
+
+/* IS UAO M1 WR */
+#define IPU7P5_IS_UAO_M1_WR_OFFSET 0x27f000
+#define IPU7P5_IS_UAO_M1_WR_PLANENUM 16
+
+/* PS UAO FW RD */
+#define IPU7P5_PS_UAO_FW_RD_OFFSET 0x156000
+#define IPU7P5_PS_UAO_FW_RD_PLANENUM 16
+
+/* PS UAO FW WR */
+#define IPU7P5_PS_UAO_FW_WR_OFFSET 0x157000
+#define IPU7P5_PS_UAO_FW_WR_PLANENUM 10
+
+/* PS UAO SRT RD */
+#define IPU7P5_PS_UAO_SRT_RD_OFFSET 0x154000
+#define IPU7P5_PS_UAO_SRT_RD_PLANENUM 22
+
+/* PS UAO SRT WR */
+#define IPU7P5_PS_UAO_SRT_WR_OFFSET 0x155000
+#define IPU7P5_PS_UAO_SRT_WR_PLANENUM 32
+
+#define IPU7P5_IS_ZLX_UC_RD_OFFSET 0x278000
+#define IPU7P5_IS_ZLX_UC_WR_OFFSET 0x279000
+#define IPU7P5_IS_ZLX_M0_OFFSET 0x27a000
+#define IPU7P5_IS_ZLX_M1_OFFSET 0x27b000
+#define IPU7P5_IS_ZLX_UC_RD_NUM 4
+#define IPU7P5_IS_ZLX_UC_WR_NUM 4
+#define IPU7P5_IS_ZLX_M0_NUM 16
+#define IPU7P5_IS_ZLX_M1_NUM 16
+
+#define IPU7P5_PS_ZLX_DATA_RD_OFFSET 0x14e000
+#define IPU7P5_PS_ZLX_DATA_WR_OFFSET 0x14f000
+#define IPU7P5_PS_ZLX_FW_RD_OFFSET 0x150000
+#define IPU7P5_PS_ZLX_FW_WR_OFFSET 0x151000
+#define IPU7P5_PS_ZLX_DATA_RD_NUM 22
+#define IPU7P5_PS_ZLX_DATA_WR_NUM 32
+#define IPU7P5_PS_ZLX_FW_RD_NUM 16
+#define IPU7P5_PS_ZLX_FW_WR_NUM 10
+
+struct ipu7_mmu_hw {
+ char name[32];
+
+ void __iomem *base;
+ void __iomem *zlx_base;
+ void __iomem *uao_base;
+
+ u32 offset;
+ u32 zlx_offset;
+ u32 uao_offset;
+
+ u32 info_bits;
+ u32 refill;
+ u32 collapse_en_bitmap;
+ u32 at_sp_arb_cfg;
+
+ u32 l1_block;
+ u32 l2_block;
+
+ u8 nr_l1streams;
+ u8 nr_l2streams;
+ u32 l1_block_sz[IPU7_MMU_MAX_TLB_L1_STREAMS];
+ u32 l2_block_sz[IPU7_MMU_MAX_TLB_L2_STREAMS];
+
+ u8 zlx_nr;
+ u32 zlx_axi_pool[IPU7_ZLX_POOL_NUM];
+ u32 zlx_en[IPU7_ZLX_MAX_NUM];
+ u32 zlx_conf[IPU7_ZLX_MAX_NUM];
+
+ u32 uao_p_num;
+ u32 uao_p2tlb[IPU7_UAO_PLANE_MAX_NUM];
+};
+
+struct ipu7_mmu_hwdata {
+ struct ipu7_mmu_hw *hwdata;
+ unsigned int nr_mmus;
+};
+
+#endif
diff --git a/drivers/media/pci/intel/ipu6/ipu7-platform-regs.h b/drivers/media/pci/intel/ipu6/ipu7-platform-regs.h
new file mode 100644
index 000000000000..39cd5c0890e5
--- /dev/null
+++ b/drivers/media/pci/intel/ipu6/ipu7-platform-regs.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2026 Intel Corporation */
+
+#ifndef IPU7_PLATFORM_REGS_H
+#define IPU7_PLATFORM_REGS_H
+
+#define IPU7_IS_UC_CTRL_BASE 0x230000
+#define IPU7_ISYS_DMEM_OFFSET 0x200000
+#define IPU7_PS_UC_CTRL_BASE 0x130000
+#define IPU7_PSYS_DMEM_OFFSET 0x100000
+
+#define IPU7_IS_IO_BASE 0x280000
+#define IPU7_IS_IO_CSI2_GPREGS_BASE (IPU7_IS_IO_BASE + 0x53400)
+
+#define IPU7_IS_IO_CSI2_LEGACY_IRQ_CTRL_BASE (IPU7_IS_IO_BASE + 0x49000)
+#define IPU7_IRQ_CTL_EDGE 0x0
+#define IPU7_IRQ_CTL_MASK 0x4
+#define IPU7_IRQ_CTL_STATUS 0x8
+#define IPU7_IRQ_CTL_CLEAR 0xc
+#define IPU7_IRQ_CTL_ENABLE 0x10
+#define IPU7_CSI_RX_LEGACY_IRQ_MASK 0x1ff
+
+#define IPU7_TO_SW_IRQ_CNTL_EDGE 0x4000
+#define IPU7_TO_SW_IRQ_CNTL_MASK_N 0x4004
+#define IPU7_TO_SW_IRQ_CNTL_STATUS 0x4008
+#define IPU7_TO_SW_IRQ_CNTL_CLEAR 0x400c
+#define IPU7_TO_SW_IRQ_CNTL_ENABLE 0x4010
+#define IPU7_IS_UC_TO_SW_IRQ_MASK 0xf
+#define IPU7_TO_SW_IRQ_FW BIT(0)
+#define IPU7_REG_PRINTF_AXI_CNTL 0x301c
+
+#endif
diff --git a/drivers/media/pci/intel/ivsc/mei_ace.c b/drivers/media/pci/intel/ivsc/mei_ace.c
index b306a320b70f..bb57656fc85a 100644
--- a/drivers/media/pci/intel/ivsc/mei_ace.c
+++ b/drivers/media/pci/intel/ivsc/mei_ace.c
@@ -414,13 +414,13 @@ static int mei_ace_setup_dev_link(struct mei_ace *ace)
/* setup link between mei_ace and mei_csi */
ace->csi_link = device_link_add(csi_dev, dev, DL_FLAG_PM_RUNTIME |
DL_FLAG_RPM_ACTIVE | DL_FLAG_STATELESS);
- put_device(csi_dev);
if (!ace->csi_link) {
ret = -EINVAL;
dev_err(dev, "failed to link to %s\n", dev_name(csi_dev));
- goto err;
+ goto err_put;
}
+ put_device(csi_dev);
ace->csi_dev = csi_dev;
return 0;
diff --git a/drivers/media/pci/intel/ivsc/mei_csi.c b/drivers/media/pci/intel/ivsc/mei_csi.c
index c2917e156345..005b1414a252 100644
--- a/drivers/media/pci/intel/ivsc/mei_csi.c
+++ b/drivers/media/pci/intel/ivsc/mei_csi.c
@@ -27,7 +27,6 @@
#include <linux/workqueue.h>
#include <media/ipu-bridge.h>
-#include <media/ipu6-pci-table.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fwnode.h>
@@ -338,6 +337,7 @@ static int mei_csi_init_state(struct v4l2_subdev *sd,
}
static int mei_csi_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -644,13 +644,9 @@ static int mei_csi_probe(struct mei_cl_device *cldev,
struct device *dev = &cldev->dev;
struct pci_dev *ipu;
struct mei_csi *csi;
- unsigned int i;
int ret;
- for (i = 0, ipu = NULL; !ipu && ipu6_pci_tbl[i].vendor; i++)
- ipu = pci_get_device(ipu6_pci_tbl[i].vendor,
- ipu6_pci_tbl[i].device, NULL);
-
+ ipu = ipu_bridge_get_ipu6();
if (!ipu)
return -ENODEV;
diff --git a/drivers/media/pci/ivtv/ivtv-controls.c b/drivers/media/pci/ivtv/ivtv-controls.c
index f087a12c4ebd..816e634de1eb 100644
--- a/drivers/media/pci/ivtv/ivtv-controls.c
+++ b/drivers/media/pci/ivtv/ivtv-controls.c
@@ -60,7 +60,7 @@ static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
format.format.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
format.format.height = cxhdl->height;
format.format.code = MEDIA_BUS_FMT_FIXED;
- v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, &format);
+ v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, NULL, &format);
return 0;
}
diff --git a/drivers/media/pci/ivtv/ivtv-ioctl.c b/drivers/media/pci/ivtv/ivtv-ioctl.c
index fc95f0bf48d5..aba55fcc1e22 100644
--- a/drivers/media/pci/ivtv/ivtv-ioctl.c
+++ b/drivers/media/pci/ivtv/ivtv-ioctl.c
@@ -587,7 +587,7 @@ static int ivtv_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
format.format.width = fmt->fmt.pix.width;
format.format.height = h;
format.format.code = MEDIA_BUS_FMT_FIXED;
- v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, &format);
+ v4l2_subdev_call(itv->sd_video, pad, set_fmt, NULL, NULL, &format);
return ivtv_g_fmt_vid_cap(file, fh, fmt);
}
diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c
index 2f5b258d682b..507ad23776e0 100644
--- a/drivers/media/pci/saa7134/saa7134-core.c
+++ b/drivers/media/pci/saa7134/saa7134-core.c
@@ -1368,7 +1368,7 @@ static int __maybe_unused saa7134_buffer_requeue(struct saa7134_dev *dev,
return 0;
}
-static int __maybe_unused saa7134_suspend(struct device *dev_d)
+static int saa7134_suspend(struct device *dev_d)
{
struct pci_dev *pci_dev = to_pci_dev(dev_d);
struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
@@ -1400,7 +1400,7 @@ static int __maybe_unused saa7134_suspend(struct device *dev_d)
return 0;
}
-static int __maybe_unused saa7134_resume(struct device *dev_d)
+static int saa7134_resume(struct device *dev_d)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d);
struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
@@ -1484,14 +1484,14 @@ EXPORT_SYMBOL(saa7134_ts_unregister);
/* ----------------------------------------------------------- */
-static SIMPLE_DEV_PM_OPS(saa7134_pm_ops, saa7134_suspend, saa7134_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(saa7134_pm_ops, saa7134_suspend, saa7134_resume);
static struct pci_driver saa7134_pci_driver = {
.name = "saa7134",
.id_table = saa7134_pci_tbl,
.probe = saa7134_initdev,
.remove = saa7134_finidev,
- .driver.pm = &saa7134_pm_ops,
+ .driver.pm = pm_sleep_ptr(&saa7134_pm_ops),
};
static int __init saa7134_init(void)
diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c
index 8c4f70e4177d..d04a68bb05d8 100644
--- a/drivers/media/pci/saa7134/saa7134-empress.c
+++ b/drivers/media/pci/saa7134/saa7134-empress.c
@@ -122,7 +122,7 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv,
};
v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
- saa_call_all(dev, pad, set_fmt, NULL, &format);
+ saa_call_all(dev, pad, set_fmt, NULL, NULL, &format);
v4l2_fill_pix_format(&f->fmt.pix, &format.format);
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
@@ -145,7 +145,7 @@ static int empress_try_fmt_vid_cap(struct file *file, void *priv,
};
v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
- saa_call_all(dev, pad, set_fmt, &pad_state, &format);
+ saa_call_all(dev, pad, set_fmt, NULL, &pad_state, &format);
v4l2_fill_pix_format(&f->fmt.pix, &format.format);
f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c
index 7f6680de3156..b2a2372d149a 100644
--- a/drivers/media/pci/saa7134/saa7134-input.c
+++ b/drivers/media/pci/saa7134/saa7134-input.c
@@ -769,7 +769,8 @@ int saa7134_input_init1(struct saa7134_dev *dev)
}
ir = kzalloc_obj(*ir);
- rc = rc_allocate_device(RC_DRIVER_SCANCODE);
+ rc = rc_allocate_device(raw_decode ?
+ RC_DRIVER_IR_RAW : RC_DRIVER_SCANCODE);
if (!ir || !rc) {
err = -ENOMEM;
goto err_out_free;
@@ -792,10 +793,8 @@ int saa7134_input_init1(struct saa7134_dev *dev)
rc->priv = dev;
rc->open = saa7134_ir_open;
rc->close = saa7134_ir_close;
- if (raw_decode) {
- rc->driver_type = RC_DRIVER_IR_RAW;
+ if (raw_decode)
rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
- }
rc->device_name = saa7134_boards[dev->board].name;
rc->input_phys = ir->phys;
diff --git a/drivers/media/pci/saa7146/mxb.c b/drivers/media/pci/saa7146/mxb.c
index d759e8a87e24..129d41c1862b 100644
--- a/drivers/media/pci/saa7146/mxb.c
+++ b/drivers/media/pci/saa7146/mxb.c
@@ -37,7 +37,7 @@
/* global variable */
static int mxb_num;
-/* initial frequence the tuner will be tuned to.
+/* initial frequency the tuner will be tuned to.
in verden (lower saxony, germany) 4148 is a
channel called "phoenix" */
static int freq = 4148;
@@ -429,7 +429,7 @@ err:
/* some stuff is done via direct write to the registers */
/* this is ugly, but because of the fact that this is completely
- hardware dependend, it should be done directly... */
+ hardware dependent, it should be done directly... */
saa7146_write(dev, DD1_STREAM_B, 0x00000000);
saa7146_write(dev, DD1_INIT, 0x02000200);
saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26));
diff --git a/drivers/media/pci/saa7164/saa7164-core.c b/drivers/media/pci/saa7164/saa7164-core.c
index ac5eb6b923e2..debcfaaf36dc 100644
--- a/drivers/media/pci/saa7164/saa7164-core.c
+++ b/drivers/media/pci/saa7164/saa7164-core.c
@@ -742,35 +742,6 @@ u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev)
return reg;
}
-/* TODO: Debugging func, remove */
-void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr)
-{
- int i;
-
- dprintk(1, "--------------------> 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n");
-
- for (i = 0; i < 0x100; i += 16)
- dprintk(1, "region0[0x%08x] = %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
- i,
- (u8)saa7164_readb(addr + i + 0),
- (u8)saa7164_readb(addr + i + 1),
- (u8)saa7164_readb(addr + i + 2),
- (u8)saa7164_readb(addr + i + 3),
- (u8)saa7164_readb(addr + i + 4),
- (u8)saa7164_readb(addr + i + 5),
- (u8)saa7164_readb(addr + i + 6),
- (u8)saa7164_readb(addr + i + 7),
- (u8)saa7164_readb(addr + i + 8),
- (u8)saa7164_readb(addr + i + 9),
- (u8)saa7164_readb(addr + i + 10),
- (u8)saa7164_readb(addr + i + 11),
- (u8)saa7164_readb(addr + i + 12),
- (u8)saa7164_readb(addr + i + 13),
- (u8)saa7164_readb(addr + i + 14),
- (u8)saa7164_readb(addr + i + 15)
- );
-}
-
static void saa7164_dump_hwdesc(struct saa7164_dev *dev)
{
dprintk(1, "@0x%p hwdesc sizeof(struct tmComResHWDescr) = %d bytes\n",
@@ -856,7 +827,7 @@ static void saa7164_get_descriptors(struct saa7164_dev *dev)
saa7164_dump_hwdesc(dev);
if (dev->intfdesc.bLength != sizeof(struct tmComResInterfaceDescr)) {
- printk(KERN_ERR "struct struct tmComResInterfaceDescr is mangled\n");
+ printk(KERN_ERR "struct tmComResInterfaceDescr is mangled\n");
printk(KERN_ERR "Need %x got %d\n", dev->intfdesc.bLength,
(u32)sizeof(struct tmComResInterfaceDescr));
} else
@@ -1344,7 +1315,6 @@ static int saa7164_initdev(struct pci_dev *pci_dev,
}
saa7164_get_descriptors(dev);
- saa7164_dumpregs(dev, 0);
saa7164_getcurrentfirmwareversion(dev);
saa7164_getfirmwarestatus(dev);
err = saa7164_bus_setup(dev);
diff --git a/drivers/media/pci/saa7164/saa7164.h b/drivers/media/pci/saa7164/saa7164.h
index 94e987e7b5e5..53ef9c3343ba 100644
--- a/drivers/media/pci/saa7164/saa7164.h
+++ b/drivers/media/pci/saa7164/saa7164.h
@@ -490,7 +490,6 @@ extern unsigned int vbi_buffers;
/* ----------------------------------------------------------- */
/* saa7164-core.c */
-void saa7164_dumpregs(struct saa7164_dev *dev, u32 addr);
void saa7164_getfirmwarestatus(struct saa7164_dev *dev);
u32 saa7164_getcurrentfirmwareversion(struct saa7164_dev *dev);
void saa7164_histogram_update(struct saa7164_histogram *hg, u32 val);
diff --git a/drivers/media/pci/tw68/tw68-core.c b/drivers/media/pci/tw68/tw68-core.c
index 509d7ddec150..0909b2d9fcdd 100644
--- a/drivers/media/pci/tw68/tw68-core.c
+++ b/drivers/media/pci/tw68/tw68-core.c
@@ -228,7 +228,7 @@ static int tw68_initdev(struct pci_dev *pci_dev,
/* pci init */
dev->pci = pci_dev;
- if (pci_enable_device(pci_dev)) {
+ if (pcim_enable_device(pci_dev)) {
err = -EIO;
goto fail1;
}
@@ -359,7 +359,7 @@ static void tw68_finidev(struct pci_dev *pci_dev)
v4l2_device_unregister(&dev->v4l2_dev);
}
-static int __maybe_unused tw68_suspend(struct device *dev_d)
+static int tw68_suspend(struct device *dev_d)
{
struct pci_dev *pci_dev = to_pci_dev(dev_d);
struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
@@ -377,7 +377,7 @@ static int __maybe_unused tw68_suspend(struct device *dev_d)
return 0;
}
-static int __maybe_unused tw68_resume(struct device *dev_d)
+static int tw68_resume(struct device *dev_d)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev_d);
struct tw68_dev *dev = container_of(v4l2_dev,
@@ -405,14 +405,14 @@ static int __maybe_unused tw68_resume(struct device *dev_d)
/* ----------------------------------------------------------- */
-static SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(tw68_pm_ops, tw68_suspend, tw68_resume);
static struct pci_driver tw68_pci_driver = {
.name = "tw68",
.id_table = tw68_pci_tbl,
.probe = tw68_initdev,
.remove = tw68_finidev,
- .driver.pm = &tw68_pm_ops,
+ .driver.pm = pm_sleep_ptr(&tw68_pm_ops),
};
module_pci_driver(tw68_pci_driver);
diff --git a/drivers/media/platform/amd/isp4/isp4_subdev.c b/drivers/media/platform/amd/isp4/isp4_subdev.c
index 6716ab9c128a..a108d90ace2d 100644
--- a/drivers/media/platform/amd/isp4/isp4_subdev.c
+++ b/drivers/media/platform/amd/isp4/isp4_subdev.c
@@ -906,6 +906,7 @@ static const struct v4l2_subdev_video_ops isp4sd_video_ops = {
};
static int isp4sd_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/amd/isp4/isp4_video.c b/drivers/media/platform/amd/isp4/isp4_video.c
index 0cebb39f98e1..856a2a0b4a12 100644
--- a/drivers/media/platform/amd/isp4/isp4_video.c
+++ b/drivers/media/platform/amd/isp4/isp4_video.c
@@ -240,7 +240,7 @@ static int isp4vid_set_fmt_2_isp(struct v4l2_subdev *sdev,
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);
+ return v4l2_subdev_call(sdev, pad, set_fmt, NULL, NULL, &fmt);
}
static int isp4vid_s_fmt_vid_cap(struct file *file, void *priv,
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c
index ff6413fff889..553c808f8f3b 100644
--- a/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-core.c
@@ -461,6 +461,7 @@ static void c3_isp_core_set_source_fmt(struct v4l2_subdev_state *state,
}
static int c3_isp_core_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c
index 453a889e0b27..1f9c16eb0842 100644
--- a/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c
+++ b/drivers/media/platform/amlogic/c3/isp/c3-isp-resizer.c
@@ -621,6 +621,7 @@ static void c3_isp_rsz_set_source_fmt(struct v4l2_subdev_state *state,
}
static int c3_isp_rsz_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -633,6 +634,7 @@ static int c3_isp_rsz_set_fmt(struct v4l2_subdev *sd,
}
static int c3_isp_rsz_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -674,6 +676,7 @@ static int c3_isp_rsz_get_selection(struct v4l2_subdev *sd,
}
static int c3_isp_rsz_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c
index 4bd98fb9c7e9..6f75112723b5 100644
--- a/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c
+++ b/drivers/media/platform/amlogic/c3/mipi-adapter/c3-mipi-adap.c
@@ -521,6 +521,7 @@ static int c3_mipi_adap_enum_mbus_code(struct v4l2_subdev *sd,
}
static int c3_mipi_adap_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c
index b9e4ef3fc308..0c399665f7fa 100644
--- a/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c
+++ b/drivers/media/platform/amlogic/c3/mipi-csi2/c3-mipi-csi2.c
@@ -491,6 +491,7 @@ static int c3_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
}
static int c3_mipi_csi_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
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 e128adf6ee37..c8464dec9a21 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-isp.c
@@ -203,6 +203,7 @@ static int mali_c55_isp_enum_frame_size(struct v4l2_subdev *sd,
}
static int mali_c55_isp_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -265,6 +266,7 @@ static int mali_c55_isp_set_fmt(struct v4l2_subdev *sd,
}
static int mali_c55_isp_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -278,6 +280,7 @@ static int mali_c55_isp_get_selection(struct v4l2_subdev *sd,
}
static int mali_c55_isp_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
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 6706939b4a90..774352c8a939 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-resizer.c
@@ -715,6 +715,7 @@ static int mali_c55_rsz_enum_frame_size(struct v4l2_subdev *sd,
}
static int mali_c55_rsz_set_sink_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -773,6 +774,7 @@ static int mali_c55_rsz_set_sink_fmt(struct v4l2_subdev *sd,
}
static int mali_c55_rsz_set_source_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -812,6 +814,7 @@ static int mali_c55_rsz_set_source_fmt(struct v4l2_subdev *sd,
}
static int mali_c55_rsz_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -826,12 +829,13 @@ static int mali_c55_rsz_set_fmt(struct v4l2_subdev *sd,
if (format->pad == MALI_C55_RSZ_SINK_PAD ||
format->pad == MALI_C55_RSZ_SINK_BYPASS_PAD)
- return mali_c55_rsz_set_sink_fmt(sd, state, format);
+ return mali_c55_rsz_set_sink_fmt(sd, ci, state, format);
- return mali_c55_rsz_set_source_fmt(sd, state, format);
+ return mali_c55_rsz_set_source_fmt(sd, ci, state, format);
}
static int mali_c55_rsz_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -850,6 +854,7 @@ static int mali_c55_rsz_get_selection(struct v4l2_subdev *sd,
}
static int mali_c55_rsz_set_crop(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -908,6 +913,7 @@ static int mali_c55_rsz_set_crop(struct v4l2_subdev *sd,
}
static int mali_c55_rsz_set_compose(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -956,6 +962,7 @@ static int mali_c55_rsz_set_compose(struct v4l2_subdev *sd,
}
static int mali_c55_rsz_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -963,10 +970,10 @@ static int mali_c55_rsz_set_selection(struct v4l2_subdev *sd,
return -EINVAL;
if (sel->target == V4L2_SEL_TGT_CROP)
- return mali_c55_rsz_set_crop(sd, state, sel);
+ return mali_c55_rsz_set_crop(sd, ci, state, sel);
if (sel->target == V4L2_SEL_TGT_COMPOSE)
- return mali_c55_rsz_set_compose(sd, state, sel);
+ return mali_c55_rsz_set_compose(sd, ci, state, sel);
return -EINVAL;
}
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 894f4cf377af..cc390a3fc253 100644
--- a/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c
+++ b/drivers/media/platform/arm/mali-c55/mali-c55-tpg.c
@@ -180,6 +180,7 @@ static int mali_c55_tpg_enum_frame_size(struct v4l2_subdev *sd,
}
static int mali_c55_tpg_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/aspeed/aspeed-video.c b/drivers/media/platform/aspeed/aspeed-video.c
index a292275f6b7b..7ba9314fdbd2 100644
--- a/drivers/media/platform/aspeed/aspeed-video.c
+++ b/drivers/media/platform/aspeed/aspeed-video.c
@@ -2267,19 +2267,19 @@ static int aspeed_video_init(struct aspeed_video *video)
if (rc)
goto err_unprepare_eclk;
- of_reserved_mem_device_init(dev);
+ devm_of_reserved_mem_device_init(dev);
rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (rc) {
dev_err(dev, "Failed to set DMA mask\n");
- goto err_release_reserved_mem;
+ goto err_unprepare_vclk;
}
if (!aspeed_video_alloc_buf(video, &video->jpeg,
VE_JPEG_HEADER_SIZE)) {
dev_err(dev, "Failed to allocate DMA for JPEG header\n");
rc = -ENOMEM;
- goto err_release_reserved_mem;
+ goto err_unprepare_vclk;
}
dev_info(video->dev, "alloc mem size(%d) at %pad for jpeg header\n",
VE_JPEG_HEADER_SIZE, &video->jpeg.dma);
@@ -2288,8 +2288,7 @@ static int aspeed_video_init(struct aspeed_video *video)
return 0;
-err_release_reserved_mem:
- of_reserved_mem_device_release(dev);
+err_unprepare_vclk:
clk_unprepare(video->vclk);
err_unprepare_eclk:
clk_unprepare(video->eclk);
@@ -2343,7 +2342,6 @@ 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;
@@ -2374,8 +2372,6 @@ static void aspeed_video_remove(struct platform_device *pdev)
v4l2_device_unregister(v4l2_dev);
aspeed_video_free_buf(video, &video->jpeg);
-
- of_reserved_mem_device_release(dev);
}
static struct platform_driver aspeed_video_driver = {
diff --git a/drivers/media/platform/atmel/atmel-isi.c b/drivers/media/platform/atmel/atmel-isi.c
index a05a744cbb75..fa33d11f736f 100644
--- a/drivers/media/platform/atmel/atmel-isi.c
+++ b/drivers/media/platform/atmel/atmel-isi.c
@@ -609,7 +609,7 @@ static int isi_try_fmt(struct atmel_isi *isi, struct v4l2_format *f,
isi_try_fse(isi, isi_fmt, &pad_state);
- ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt,
+ ret = v4l2_subdev_call(isi->entity.subdev, pad, set_fmt, NULL,
&pad_state, &format);
if (ret < 0)
return ret;
@@ -641,7 +641,7 @@ static int isi_set_fmt(struct atmel_isi *isi, struct v4l2_format *f)
v4l2_fill_mbus_format(&format.format, &f->fmt.pix,
current_fmt->mbus_code);
ret = v4l2_subdev_call(isi->entity.subdev, pad,
- set_fmt, NULL, &format);
+ set_fmt, NULL, NULL, &format);
if (ret < 0)
return ret;
@@ -1121,10 +1121,13 @@ static void isi_graph_notify_unbind(struct v4l2_async_notifier *notifier,
{
struct atmel_isi *isi = notifier_to_isi(notifier);
+ if (!video_is_registered(isi->vdev))
+ return;
+
dev_dbg(isi->dev, "Removing %s\n", video_device_node_name(isi->vdev));
- /* Checks internally if vdev have been init or not */
video_unregister_device(isi->vdev);
+ isi->vdev = NULL;
}
static int isi_graph_notify_bound(struct v4l2_async_notifier *notifier,
@@ -1323,6 +1326,8 @@ static void atmel_isi_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
v4l2_async_nf_unregister(&isi->notifier);
v4l2_async_nf_cleanup(&isi->notifier);
+ if (isi->vdev)
+ video_device_release(isi->vdev);
v4l2_device_unregister(&isi->v4l2_dev);
}
diff --git a/drivers/media/platform/broadcom/bcm2835-unicam.c b/drivers/media/platform/broadcom/bcm2835-unicam.c
index 14bb916dd7b1..e7c708cdd7f8 100644
--- a/drivers/media/platform/broadcom/bcm2835-unicam.c
+++ b/drivers/media/platform/broadcom/bcm2835-unicam.c
@@ -1327,6 +1327,7 @@ static int unicam_subdev_enum_frame_size(struct v4l2_subdev *sd,
}
static int unicam_subdev_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/cadence/cdns-csi2rx.c b/drivers/media/platform/cadence/cdns-csi2rx.c
index 1ff2d8f78d5b..de7cce76397a 100644
--- a/drivers/media/platform/cadence/cdns-csi2rx.c
+++ b/drivers/media/platform/cadence/cdns-csi2rx.c
@@ -166,6 +166,10 @@ static const struct csi2rx_fmt formats[] = {
{ .code = MEDIA_BUS_FMT_SGBRG10_1X10, .bpp = 10, .max_pixels = 2, },
{ .code = MEDIA_BUS_FMT_SGRBG10_1X10, .bpp = 10, .max_pixels = 2, },
{ .code = MEDIA_BUS_FMT_SRGGB10_1X10, .bpp = 10, .max_pixels = 2, },
+ { .code = MEDIA_BUS_FMT_SBGGR12_1X12, .bpp = 12, .max_pixels = 2, },
+ { .code = MEDIA_BUS_FMT_SGBRG12_1X12, .bpp = 12, .max_pixels = 2, },
+ { .code = MEDIA_BUS_FMT_SGRBG12_1X12, .bpp = 12, .max_pixels = 2, },
+ { .code = MEDIA_BUS_FMT_SRGGB12_1X12, .bpp = 12, .max_pixels = 2, },
{ .code = MEDIA_BUS_FMT_RGB565_1X16, .bpp = 16, .max_pixels = 1, },
{ .code = MEDIA_BUS_FMT_RGB888_1X24, .bpp = 24, .max_pixels = 1, },
{ .code = MEDIA_BUS_FMT_BGR888_1X24, .bpp = 24, .max_pixels = 1, },
@@ -622,6 +626,7 @@ static int csi2rx_set_routing(struct v4l2_subdev *subdev,
}
static int csi2rx_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/cadence/cdns-csi2tx.c b/drivers/media/platform/cadence/cdns-csi2tx.c
index 629b0fa838a2..07277866dca7 100644
--- a/drivers/media/platform/cadence/cdns-csi2tx.c
+++ b/drivers/media/platform/cadence/cdns-csi2tx.c
@@ -201,6 +201,7 @@ static int csi2tx_get_pad_format(struct v4l2_subdev *subdev,
}
static int csi2tx_set_pad_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/intel/pxa_camera.c b/drivers/media/platform/intel/pxa_camera.c
index 2269d852ab0e..9e1fdd963b55 100644
--- a/drivers/media/platform/intel/pxa_camera.c
+++ b/drivers/media/platform/intel/pxa_camera.c
@@ -1819,7 +1819,7 @@ static int pxac_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
pixfmt == V4L2_PIX_FMT_YUV422P ? 4 : 0);
v4l2_fill_mbus_format(mf, pix, xlate->code);
- ret = sensor_call(pcdev, pad, set_fmt, &pad_state, &format);
+ ret = sensor_call(pcdev, pad, set_fmt, NULL, &pad_state, &format);
if (ret < 0)
return ret;
@@ -1882,7 +1882,7 @@ static int pxac_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
xlate = pxa_mbus_xlate_by_fourcc(pcdev->user_formats,
pix->pixelformat);
v4l2_fill_mbus_format(&format.format, pix, xlate->code);
- ret = sensor_call(pcdev, pad, set_fmt, NULL, &format);
+ ret = sensor_call(pcdev, pad, set_fmt, NULL, NULL, &format);
if (ret < 0) {
dev_warn(pcdev_to_dev(pcdev),
"Failed to configure for format %x\n",
@@ -2091,7 +2091,7 @@ static int pxa_camera_sensor_bound(struct v4l2_async_notifier *notifier,
if (err)
goto out;
- err = sensor_call(pcdev, pad, set_fmt, NULL, &format);
+ err = sensor_call(pcdev, pad, set_fmt, NULL, NULL, &format);
if (err)
goto out_sensor_poweroff;
diff --git a/drivers/media/platform/m2m-deinterlace.c b/drivers/media/platform/m2m-deinterlace.c
index 1d0e70eaea3d..9dcc4bd6cbdd 100644
--- a/drivers/media/platform/m2m-deinterlace.c
+++ b/drivers/media/platform/m2m-deinterlace.c
@@ -822,7 +822,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
q_data[V4L2_M2M_DST].width = 640;
q_data[V4L2_M2M_DST].height = 480;
q_data[V4L2_M2M_DST].sizeimage = (640 * 480 * 3) / 2;
- q_data[V4L2_M2M_SRC].field = V4L2_FIELD_INTERLACED_TB;
+ q_data[V4L2_M2M_DST].field = V4L2_FIELD_INTERLACED_TB;
return vb2_queue_init(dst_vq);
}
diff --git a/drivers/media/platform/marvell/Kconfig b/drivers/media/platform/marvell/Kconfig
index d31f4730f2a3..ab58175e433c 100644
--- a/drivers/media/platform/marvell/Kconfig
+++ b/drivers/media/platform/marvell/Kconfig
@@ -22,6 +22,7 @@ config VIDEO_MMP_CAMERA
depends on V4L_PLATFORM_DRIVERS
depends on I2C && VIDEO_DEV
depends on ARCH_MMP || COMPILE_TEST
+ depends on GPIOLIB || COMPILE_TEST
depends on COMMON_CLK
select VIDEO_OV7670 if MEDIA_SUBDRV_AUTOSELECT && VIDEO_CAMERA_SENSOR
select I2C_GPIO
diff --git a/drivers/media/platform/marvell/cafe-driver.c b/drivers/media/platform/marvell/cafe-driver.c
index 22034df6cba9..13d8faaabb50 100644
--- a/drivers/media/platform/marvell/cafe-driver.c
+++ b/drivers/media/platform/marvell/cafe-driver.c
@@ -127,7 +127,7 @@ struct cafe_camera {
* Debugging and related.
*/
#define cam_err(cam, fmt, arg...) \
- dev_err(&(cam)->pdev->dev, fmt, ##arg);
+ dev_err(&(cam)->pdev->dev, fmt, ##arg)
#define cam_warn(cam, fmt, arg...) \
dev_warn(&(cam)->pdev->dev, fmt, ##arg);
diff --git a/drivers/media/platform/marvell/mcam-core.c b/drivers/media/platform/marvell/mcam-core.c
index b8360d37000a..34809d075a42 100644
--- a/drivers/media/platform/marvell/mcam-core.c
+++ b/drivers/media/platform/marvell/mcam-core.c
@@ -224,11 +224,11 @@ static void mcam_buffer_done(struct mcam_camera *cam, int frame,
* Debugging and related.
*/
#define cam_err(cam, fmt, arg...) \
- dev_err((cam)->dev, fmt, ##arg);
+ dev_err((cam)->dev, fmt, ##arg)
#define cam_warn(cam, fmt, arg...) \
- dev_warn((cam)->dev, fmt, ##arg);
+ dev_warn((cam)->dev, fmt, ##arg)
#define cam_dbg(cam, fmt, arg...) \
- dev_dbg((cam)->dev, fmt, ##arg);
+ dev_dbg((cam)->dev, fmt, ##arg)
/*
@@ -406,6 +406,8 @@ static void mcam_free_dma_bufs(struct mcam_camera *cam)
{
int i;
+ cancel_work_sync(&cam->s_bh_work);
+
for (i = 0; i < cam->nbufs; i++) {
dma_free_coherent(cam->dev, cam->dma_buf_size,
cam->dma_bufs[i], cam->dma_handles[i]);
@@ -1022,7 +1024,7 @@ static int mcam_cam_configure(struct mcam_camera *cam)
v4l2_fill_mbus_format(&format.format, &cam->pix_format, cam->mbus_code);
ret = sensor_call(cam, core, init, 0);
if (ret == 0)
- ret = sensor_call(cam, pad, set_fmt, NULL, &format);
+ ret = sensor_call(cam, pad, set_fmt, NULL, NULL, &format);
/*
* OV7670 does weird things if flip is set *before* format...
*/
@@ -1306,7 +1308,6 @@ static int mcam_setup_vb2(struct mcam_camera *cam)
break;
case B_vmalloc:
#ifdef MCAM_MODE_VMALLOC
- INIT_WORK(&cam->s_bh_work, mcam_frame_work);
vq->ops = &mcam_vb2_ops;
vq->mem_ops = &vb2_vmalloc_memops;
cam->dma_setup = mcam_ctlr_dma_vmalloc;
@@ -1362,7 +1363,7 @@ static int mcam_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
f = mcam_find_format(pix->pixelformat);
pix->pixelformat = f->pixelformat;
v4l2_fill_mbus_format(&format.format, pix, f->mbus_code);
- ret = sensor_call(cam, pad, set_fmt, &pad_state, &format);
+ ret = sensor_call(cam, pad, set_fmt, NULL, &pad_state, &format);
v4l2_fill_pix_format(pix, &format.format);
pix->bytesperline = pix->width * f->bpp;
switch (f->pixelformat) {
@@ -1864,6 +1865,12 @@ int mccic_register(struct mcam_camera *cam)
goto out;
}
+#ifdef MCAM_MODE_VMALLOC
+ /* Init before sensor bind: armed by IRQ, cancelled on probe-error paths. */
+ if (cam->buffer_mode == B_vmalloc)
+ INIT_WORK(&cam->s_bh_work, mcam_frame_work);
+#endif
+
mutex_init(&cam->s_mutex);
cam->state = S_NOTREADY;
mcam_set_config_needed(cam, 1);
@@ -1922,10 +1929,15 @@ void mccic_shutdown(struct mcam_camera *cam)
* take it down again will wedge the machine, which is frowned
* upon.
*/
+ mutex_lock(&cam->s_mutex);
if (!list_empty(&cam->vdev.fh_list)) {
cam_warn(cam, "Removing a device with users!\n");
+ /* Stop so the IRQ can't re-arm s_bh_work after the buffers are freed. */
+ if (cam->state == S_STREAMING)
+ mcam_ctlr_stop_dma(cam);
sensor_call(cam, core, s_power, 0);
}
+ mutex_unlock(&cam->s_mutex);
if (cam->buffer_mode == B_vmalloc)
mcam_free_dma_bufs(cam);
v4l2_ctrl_handler_free(&cam->ctrl_handler);
diff --git a/drivers/media/platform/marvell/mmp-driver.c b/drivers/media/platform/marvell/mmp-driver.c
index d3da7ebb4a2b..4c31b62751a1 100644
--- a/drivers/media/platform/marvell/mmp-driver.c
+++ b/drivers/media/platform/marvell/mmp-driver.c
@@ -25,6 +25,7 @@
#include <linux/list.h>
#include <linux/pm.h>
#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include "mcam-core.h"
@@ -269,8 +270,8 @@ static int mmpcam_probe(struct platform_device *pdev)
/*
* Add OF clock provider.
*/
- ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
- mcam->mclk);
+ ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_simple_get,
+ &mcam->mclk_hw);
if (ret) {
dev_err(&pdev->dev, "can't add DT clock provider\n");
goto out;
diff --git a/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h
index b810c96695c8..9744e00b0066 100644
--- a/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h
+++ b/drivers/media/platform/mediatek/mdp/mtk_mdp_ipi.h
@@ -56,7 +56,7 @@ struct mdp_ipi_comm {
* @ipi_id : IPI_MDP
* @ap_inst : AP mtk_mdp_vpu address
* @vpu_inst_addr : VPU MDP instance address
- * @status : VPU exeuction result
+ * @status : VPU execution result
*/
struct mdp_ipi_comm_ack {
uint32_t msg_id;
diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h b/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
index 47070be2a991..c2c2a3a63dfd 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec_ipi_msg.h
@@ -53,7 +53,7 @@ struct vdec_ap_ipi_cmd {
/**
* struct vdec_vpu_ipi_ack - generic VPU to AP ipi command format
* @msg_id : vdec_ipi_msgid
- * @status : VPU exeuction result
+ * @status : VPU execution result
* @ap_inst_addr : AP video decoder instance address
*/
struct vdec_vpu_ipi_ack {
@@ -98,7 +98,7 @@ struct vdec_ap_ipi_dec_start {
/**
* struct vdec_vpu_ipi_init_ack - for VPU_IPIMSG_DEC_INIT_ACK
* @msg_id : VPU_IPIMSG_DEC_INIT_ACK
- * @status : VPU exeuction result
+ * @status : VPU execution result
* @ap_inst_addr : AP vcodec_vpu_inst instance address
* @vpu_inst_addr : VPU decoder instance address
* @vdec_abi_version: ABI version of the firmware. Kernel can use it to
diff --git a/drivers/media/platform/microchip/microchip-csi2dc.c b/drivers/media/platform/microchip/microchip-csi2dc.c
index e69292f3b2a9..0368f3e80c0c 100644
--- a/drivers/media/platform/microchip/microchip-csi2dc.c
+++ b/drivers/media/platform/microchip/microchip-csi2dc.c
@@ -244,6 +244,7 @@ static int csi2dc_get_fmt(struct v4l2_subdev *csi2dc_sd,
}
static int csi2dc_set_fmt(struct v4l2_subdev *csi2dc_sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *req_fmt)
{
@@ -735,6 +736,7 @@ static int csi2dc_probe(struct platform_device *pdev)
return 0;
csi2dc_probe_cleanup_notifier:
+ v4l2_async_nf_unregister(&csi2dc->notifier);
v4l2_async_nf_cleanup(&csi2dc->notifier);
csi2dc_probe_cleanup_entity:
media_entity_cleanup(&csi2dc->csi2dc_sd.entity);
diff --git a/drivers/media/platform/microchip/microchip-isc-base.c b/drivers/media/platform/microchip/microchip-isc-base.c
index a7cdc743fda7..963d5fd53b58 100644
--- a/drivers/media/platform/microchip/microchip-isc-base.c
+++ b/drivers/media/platform/microchip/microchip-isc-base.c
@@ -8,6 +8,7 @@
* Author: Eugen Hristev <eugen.hristev@microchip.com>
*
*/
+#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/math64.h>
@@ -62,17 +63,17 @@ static inline void isc_update_awb_ctrls(struct isc_device *isc)
/* 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));
+ FIELD_PREP(ISC_WB_O_LO, ctrls->offset[ISC_HIS_CFG_MODE_R]) |
+ FIELD_PREP(ISC_WB_O_HI, ctrls->offset[ISC_HIS_CFG_MODE_GR]));
regmap_write(isc->regmap, ISC_WB_O_BGB,
- ((ctrls->offset[ISC_HIS_CFG_MODE_B])) |
- ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16));
+ FIELD_PREP(ISC_WB_O_LO, ctrls->offset[ISC_HIS_CFG_MODE_B]) |
+ FIELD_PREP(ISC_WB_O_HI, ctrls->offset[ISC_HIS_CFG_MODE_GB]));
regmap_write(isc->regmap, ISC_WB_G_RGR,
- ctrls->gain[ISC_HIS_CFG_MODE_R] |
- (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16));
+ FIELD_PREP(ISC_WB_G_LO, ctrls->gain[ISC_HIS_CFG_MODE_R]) |
+ FIELD_PREP(ISC_WB_G_HI, ctrls->gain[ISC_HIS_CFG_MODE_GR]));
regmap_write(isc->regmap, ISC_WB_G_BGB,
- ctrls->gain[ISC_HIS_CFG_MODE_B] |
- (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16));
+ FIELD_PREP(ISC_WB_G_LO, ctrls->gain[ISC_HIS_CFG_MODE_B]) |
+ FIELD_PREP(ISC_WB_G_HI, ctrls->gain[ISC_HIS_CFG_MODE_GB]));
}
static inline void isc_reset_awb_ctrls(struct isc_device *isc)
@@ -289,8 +290,10 @@ 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;
+ int ret;
- pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps;
+ pfe_cfg0 = FIELD_PREP(ISC_PFE_CFG0_BPS_MASK,
+ isc->config.sd_format->pfe_cfg0_bps);
pipeline = isc->config.bits_pipeline;
dcfg = isc->config.dcfg_imode | isc->dcfg;
@@ -321,7 +324,15 @@ static int isc_configure(struct isc_device *isc)
isc_set_histogram(isc, false);
/* Update profile */
- return isc_update_profile(isc);
+ ret = isc_update_profile(isc);
+ if (ret) {
+ /* flush the histogram work before the clocks are gated */
+ isc_set_histogram(isc, false);
+ synchronize_irq(isc->irq);
+ cancel_work_sync(&isc->awb_work);
+ }
+
+ return ret;
}
static int isc_prepare_streaming(struct vb2_queue *vq)
@@ -425,6 +436,13 @@ static void isc_stop_streaming(struct vb2_queue *vq)
/* Disable DMA interrupt */
regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE);
+ isc_set_histogram(isc, false);
+
+ /* let a running IRQ handler finish before the clock is disabled */
+ synchronize_irq(isc->irq);
+
+ cancel_work_sync(&isc->awb_work);
+
pm_runtime_put_sync(isc->dev);
/* Disable stream on the sub device */
@@ -1416,7 +1434,7 @@ static void isc_awb_work(struct work_struct *w)
/* streaming is not active anymore */
if (isc->stop) {
mutex_unlock(&isc->awb_mutex);
- return;
+ goto out_pm_put;
}
isc_update_profile(isc);
@@ -1427,6 +1445,7 @@ static void isc_awb_work(struct work_struct *w)
if (ctrls->awb)
regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ);
+out_pm_put:
pm_runtime_put_sync(isc->dev);
}
@@ -1495,20 +1514,24 @@ static int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl)
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 (vb2_is_streaming(&isc->vb2_vidq) && !isc->stop) {
+ unsigned long flags;
+
/*
- * If we are streaming, we can update profile to
- * have the new settings in place.
+ * awb_lock keeps the DMA done IRQ from latching a
+ * partially written WB pipeline.
*/
+ spin_lock_irqsave(&isc->awb_lock, flags);
+ isc_update_awb_ctrls(isc);
+ spin_unlock_irqrestore(&isc->awb_lock, flags);
+
isc_update_profile(isc);
} else {
/*
- * The auto cluster will activate automatically this
- * control. This has to be deactivated when not
- * streaming.
+ * Not streaming: keep the cached values for the next
+ * stream start and deactivate the cluster-activated
+ * do_white_balance button.
*/
v4l2_ctrl_activate(isc->do_wb_ctrl, false);
}
@@ -1703,7 +1726,6 @@ static void isc_async_unbind(struct v4l2_async_notifier *notifier,
{
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);
@@ -1767,8 +1789,6 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
isc->current_subdev = container_of(notifier,
struct isc_subdev_entity, notifier);
- mutex_init(&isc->lock);
- mutex_init(&isc->awb_mutex);
init_completion(&isc->comp);
@@ -1787,7 +1807,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = vb2_queue_init(q);
if (ret < 0) {
dev_err(isc->dev, "vb2_queue_init() failed: %d\n", ret);
- goto isc_async_complete_err;
+ return ret;
}
/* Init video dma queues */
@@ -1798,13 +1818,13 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = isc_set_default_fmt(isc);
if (ret) {
dev_err(isc->dev, "Could not set default format\n");
- goto isc_async_complete_err;
+ return ret;
}
ret = isc_ctrl_init(isc);
if (ret) {
dev_err(isc->dev, "Init isc ctrols failed: %d\n", ret);
- goto isc_async_complete_err;
+ return ret;
}
/* Register video device */
@@ -1824,7 +1844,7 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
if (ret < 0) {
dev_err(isc->dev, "video_register_device failed: %d\n", ret);
- goto isc_async_complete_err;
+ return ret;
}
ret = isc_scaler_link(isc);
@@ -1839,10 +1859,6 @@ static int isc_async_complete(struct v4l2_async_notifier *notifier)
isc_async_complete_unregister_device:
video_unregister_device(vdev);
-
-isc_async_complete_err:
- mutex_destroy(&isc->awb_mutex);
- mutex_destroy(&isc->lock);
return ret;
}
@@ -1860,6 +1876,12 @@ void microchip_isc_subdev_cleanup(struct isc_device *isc)
list_for_each_entry(subdev_entity, &isc->subdev_entities, list) {
v4l2_async_nf_unregister(&subdev_entity->notifier);
v4l2_async_nf_cleanup(&subdev_entity->notifier);
+ /*
+ * Release the endpoint reference taken while parsing. It is
+ * NULL for entities the bind loop already consumed, so this
+ * only drops the ones left over on an early exit.
+ */
+ of_node_put(subdev_entity->epn);
}
INIT_LIST_HEAD(&isc->subdev_entities);
diff --git a/drivers/media/platform/microchip/microchip-isc-clk.c b/drivers/media/platform/microchip/microchip-isc-clk.c
index 24358d804e75..66dc522a6190 100644
--- a/drivers/media/platform/microchip/microchip-isc-clk.c
+++ b/drivers/media/platform/microchip/microchip-isc-clk.c
@@ -98,15 +98,14 @@ 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)
+ /* Runs in atomic context, so must not sleep to resume the ISC. */
+ if (pm_runtime_get_if_active(isc_clk->dev) <= 0)
return 0;
regmap_read(isc_clk->regmap, ISC_CLKSR, &status);
- pm_runtime_put_sync(isc_clk->dev);
+ pm_runtime_put(isc_clk->dev);
return status & ISC_CLK(isc_clk->id) ? 1 : 0;
}
diff --git a/drivers/media/platform/microchip/microchip-isc-regs.h b/drivers/media/platform/microchip/microchip-isc-regs.h
index e77e1d9a1db8..fe145b142b82 100644
--- a/drivers/media/platform/microchip/microchip-isc-regs.h
+++ b/drivers/media/platform/microchip/microchip-isc-regs.h
@@ -31,11 +31,11 @@
#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_EIGHT 0x4
+#define ISC_PFE_CFG0_BPS_NINE 0x3
+#define ISC_PFE_CFG0_BPS_TEN 0x2
+#define ISC_PFE_CFG0_BPS_ELEVEN 0x1
+#define ISC_PFE_CFG0_BPS_TWELVE 0x0
#define ISC_PFE_CFG0_BPS_MASK GENMASK(30, 28)
#define ISC_PFE_CFG0_COLEN BIT(12)
@@ -149,6 +149,12 @@
/* ISC White Balance Gain for B, GB Register */
#define ISC_WB_G_BGB 0x0000006c
+/* Each WB offset/gain register packs two 13-bit fields, low and high */
+#define ISC_WB_O_LO GENMASK(12, 0) /* R or B offset [12:0] */
+#define ISC_WB_O_HI GENMASK(28, 16) /* GR or GB offset [28:16] */
+#define ISC_WB_G_LO GENMASK(12, 0) /* R or B gain [12:0] */
+#define ISC_WB_G_HI GENMASK(28, 16) /* GR or GB gain [28:16] */
+
/* ISC Color Filter Array Control Register */
#define ISC_CFA_CTRL 0x00000070
diff --git a/drivers/media/platform/microchip/microchip-isc-scaler.c b/drivers/media/platform/microchip/microchip-isc-scaler.c
index e83463543e21..192532f87271 100644
--- a/drivers/media/platform/microchip/microchip-isc-scaler.c
+++ b/drivers/media/platform/microchip/microchip-isc-scaler.c
@@ -46,6 +46,7 @@ static int isc_scaler_get_fmt(struct v4l2_subdev *sd,
}
static int isc_scaler_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *req_fmt)
{
@@ -124,6 +125,7 @@ static int isc_scaler_enum_mbus_code(struct v4l2_subdev *sd,
}
static int isc_scaler_g_sel(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/microchip/microchip-isc.h b/drivers/media/platform/microchip/microchip-isc.h
index ad4e98a1dd8f..d7bcd74efff9 100644
--- a/drivers/media/platform/microchip/microchip-isc.h
+++ b/drivers/media/platform/microchip/microchip-isc.h
@@ -62,7 +62,9 @@ struct isc_subdev_entity {
* @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
+ * @pfe_cfg0_bps: ISC_PFE_CFG0 BPS field value (e.g. ISC_PFE_CFG0_BPS_EIGHT),
+ written into PFE_CFG0 with FIELD_PREP(ISC_PFE_CFG0_BPS_MASK)
+ at configure time.
* @raw: If the format is raw bayer.
*/
@@ -287,6 +289,7 @@ struct isc_device {
u32 dcfg;
struct device *dev;
+ int irq;
struct v4l2_device v4l2_dev;
struct video_device video_dev;
diff --git a/drivers/media/platform/microchip/microchip-sama5d2-isc.c b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
index 66d3d7891991..5e41eee45dbd 100644
--- a/drivers/media/platform/microchip/microchip-sama5d2-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama5d2-isc.c
@@ -30,6 +30,7 @@
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
@@ -146,49 +147,49 @@ static struct isc_format sama5d2_formats_list[] = {
{
.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,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_BGBG,
},
{
.fourcc = V4L2_PIX_FMT_SGBRG10,
.mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_TWELVE,
.cfa_baycfg = ISC_BAY_CFG_RGRG,
},
{
@@ -209,7 +210,7 @@ static struct isc_format sama5d2_formats_list[] = {
{
.fourcc = V4L2_PIX_FMT_Y10,
.mbus_code = MEDIA_BUS_FMT_Y10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_TEN,
},
};
@@ -356,28 +357,30 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
struct device_node *epn;
struct isc_subdev_entity *subdev_entity;
unsigned int flags;
+ int ret;
INIT_LIST_HEAD(&isc->subdev_entities);
for_each_endpoint_of_node(np, epn) {
struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
- int ret;
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
&v4l2_epn);
if (ret) {
- of_node_put(epn);
dev_err(dev, "Could not parse the endpoint\n");
- return -EINVAL;
+ of_node_put(epn);
+ ret = -EINVAL;
+ goto err_cleanup;
}
subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
GFP_KERNEL);
if (!subdev_entity) {
of_node_put(epn);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_cleanup;
}
- subdev_entity->epn = epn;
+ subdev_entity->epn = of_node_get(epn);
flags = v4l2_epn.bus.parallel.flags;
@@ -398,6 +401,11 @@ static int isc_parse_dt(struct device *dev, struct isc_device *isc)
}
return 0;
+
+err_cleanup:
+ list_for_each_entry(subdev_entity, &isc->subdev_entities, list)
+ of_node_put(subdev_entity->epn);
+ return ret;
}
static int microchip_isc_probe(struct platform_device *pdev)
@@ -417,6 +425,14 @@ static int microchip_isc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, isc);
isc->dev = dev;
+ ret = devm_mutex_init(dev, &isc->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(dev, &isc->awb_mutex);
+ if (ret)
+ return ret;
+
io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(io_base))
return PTR_ERR(io_base);
@@ -432,6 +448,8 @@ static int microchip_isc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ isc->irq = irq;
+
ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
"microchip-sama5d2-isc", isc);
if (ret < 0) {
diff --git a/drivers/media/platform/microchip/microchip-sama7g5-isc.c b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
index 7383341ec51d..9e78ea07b013 100644
--- a/drivers/media/platform/microchip/microchip-sama7g5-isc.c
+++ b/drivers/media/platform/microchip/microchip-sama7g5-isc.c
@@ -33,6 +33,7 @@
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/module.h>
+#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
@@ -155,49 +156,49 @@ static struct isc_format sama7g5_formats_list[] = {
{
.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,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_TEN,
+ .cfa_baycfg = ISC_BAY_CFG_BGBG,
},
{
.fourcc = V4L2_PIX_FMT_SGBRG10,
.mbus_code = MEDIA_BUS_FMT_SGBRG10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_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,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_TWELVE,
.cfa_baycfg = ISC_BAY_CFG_RGRG,
},
{
@@ -223,7 +224,7 @@ static struct isc_format sama7g5_formats_list[] = {
{
.fourcc = V4L2_PIX_FMT_Y10,
.mbus_code = MEDIA_BUS_FMT_Y10_1X10,
- .pfe_cfg0_bps = ISC_PFG_CFG0_BPS_TEN,
+ .pfe_cfg0_bps = ISC_PFE_CFG0_BPS_TEN,
},
};
@@ -340,6 +341,7 @@ static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
struct isc_subdev_entity *subdev_entity;
unsigned int flags;
bool mipi_mode;
+ int ret;
INIT_LIST_HEAD(&isc->subdev_entities);
@@ -347,23 +349,24 @@ static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
for_each_endpoint_of_node(np, epn) {
struct v4l2_fwnode_endpoint v4l2_epn = { .bus_type = 0 };
- int ret;
ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(epn),
&v4l2_epn);
if (ret) {
- of_node_put(epn);
dev_err(dev, "Could not parse the endpoint\n");
- return -EINVAL;
+ of_node_put(epn);
+ ret = -EINVAL;
+ goto err_cleanup;
}
subdev_entity = devm_kzalloc(dev, sizeof(*subdev_entity),
GFP_KERNEL);
if (!subdev_entity) {
of_node_put(epn);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto err_cleanup;
}
- subdev_entity->epn = epn;
+ subdev_entity->epn = of_node_get(epn);
flags = v4l2_epn.bus.parallel.flags;
@@ -387,6 +390,11 @@ static int xisc_parse_dt(struct device *dev, struct isc_device *isc)
}
return 0;
+
+err_cleanup:
+ list_for_each_entry(subdev_entity, &isc->subdev_entities, list)
+ of_node_put(subdev_entity->epn);
+ return ret;
}
static int microchip_xisc_probe(struct platform_device *pdev)
@@ -406,6 +414,14 @@ static int microchip_xisc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, isc);
isc->dev = dev;
+ ret = devm_mutex_init(dev, &isc->lock);
+ if (ret)
+ return ret;
+
+ ret = devm_mutex_init(dev, &isc->awb_mutex);
+ if (ret)
+ return ret;
+
io_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(io_base))
return PTR_ERR(io_base);
@@ -421,6 +437,8 @@ static int microchip_xisc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
+ isc->irq = irq;
+
ret = devm_request_irq(dev, irq, microchip_isc_interrupt, 0,
"microchip-sama7g5-xisc", isc);
if (ret < 0) {
diff --git a/drivers/media/platform/nuvoton/npcm-video.c b/drivers/media/platform/nuvoton/npcm-video.c
index 52505af35c08..a6737c65d9b2 100644
--- a/drivers/media/platform/nuvoton/npcm-video.c
+++ b/drivers/media/platform/nuvoton/npcm-video.c
@@ -120,6 +120,7 @@ struct npcm_video {
struct list_head buffers;
struct mutex buffer_lock; /* buffer list lock */
+ int irq;
unsigned long flags;
unsigned int sequence;
@@ -1486,6 +1487,7 @@ static int npcm_video_start_streaming(struct vb2_queue *q, unsigned int count)
}
set_bit(VIDEO_STREAMING, &video->flags);
+ enable_irq(video->irq);
return 0;
}
@@ -1494,6 +1496,7 @@ static void npcm_video_stop_streaming(struct vb2_queue *q)
struct npcm_video *video = vb2_get_drv_priv(q);
struct regmap *vcd = video->vcd_regmap;
+ disable_irq(video->irq);
clear_bit(VIDEO_STREAMING, &video->flags);
regmap_write(vcd, VCD_INTE, 0);
regmap_write(vcd, VCD_STAT, VCD_STAT_CLEAR);
@@ -1707,25 +1710,24 @@ static int npcm_video_init(struct npcm_video *video)
dev_err(dev, "Failed to find VCD IRQ\n");
return -ENODEV;
}
+ video->irq = irq;
rc = devm_request_threaded_irq(dev, irq, NULL, npcm_video_irq,
- IRQF_ONESHOT, DEVICE_NAME, video);
+ IRQF_ONESHOT | IRQF_NO_AUTOEN, DEVICE_NAME, video);
if (rc < 0) {
dev_err(dev, "Failed to request IRQ %d\n", irq);
return rc;
}
- of_reserved_mem_device_init(dev);
+ devm_of_reserved_mem_device_init(dev);
rc = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
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;
}
@@ -1789,13 +1791,11 @@ static int npcm_video_probe(struct platform_device *pdev)
rc = npcm_video_setup_video(video);
if (rc)
- goto err_release_mem;
+ goto err_free;
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;
@@ -1807,14 +1807,12 @@ static void npcm_video_remove(struct platform_device *pdev)
struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
struct npcm_video *video = to_npcm_video(v4l2_dev);
- video_unregister_device(&video->vdev);
- vb2_queue_release(&video->queue);
+ vb2_video_unregister_device(&video->vdev);
v4l2_ctrl_handler_free(&video->ctrl_handler);
v4l2_device_unregister(v4l2_dev);
if (video->ece.enable)
npcm_video_ece_stop(video);
kfree(video);
- of_reserved_mem_device_release(dev);
}
static const struct of_device_id npcm_video_match[] = {
diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig
index 40e3436669e2..5ff263fe7c6f 100644
--- a/drivers/media/platform/nxp/Kconfig
+++ b/drivers/media/platform/nxp/Kconfig
@@ -28,6 +28,22 @@ config VIDEO_IMX8MQ_MIPI_CSI2
Video4Linux2 driver for the MIPI CSI-2 receiver found on the i.MX8MQ
SoC.
+config VIDEO_IMX95_CSI_FORMATTER
+ tristate "NXP i.MX95 CSI Pixel Formatter driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on PM_CLK
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select MFD_SYSCON
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This driver provides support for the CSI Pixel Formatter found on
+ i.MX95 series SoCs. This module unpacks the pixels received from the
+ CSI-2 interface and reformats them to meet pixel link requirements.
+
+ Say Y here to enable CSI Pixel Formatter module for i.MX95 SoC.
+
config VIDEO_IMX_MIPI_CSIS
tristate "NXP MIPI CSI-2 CSIS receiver found on i.MX7 and i.MX8 models"
depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile
index 4d90eb713652..6410115d870e 100644
--- a/drivers/media/platform/nxp/Makefile
+++ b/drivers/media/platform/nxp/Makefile
@@ -6,6 +6,7 @@ obj-y += imx8-isi/
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o
+obj-$(CONFIG_VIDEO_IMX95_CSI_FORMATTER) += imx95-csi-formatter.o
obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o
obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
diff --git a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
index 725e94152884..e17085935435 100644
--- a/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
+++ b/drivers/media/platform/nxp/imx-jpeg/mxc-jpeg.c
@@ -1590,15 +1590,15 @@ static void mxc_jpeg_device_run(void *priv)
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
if (!src_buf || !dst_buf) {
dev_err(dev, "Null src or dst buf\n");
- goto end;
+ goto job_finish;
}
q_data_cap = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
if (!q_data_cap)
- goto end;
+ goto buf_finish;
q_data_out = mxc_jpeg_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (!q_data_out)
- goto end;
+ goto buf_finish;
src_buf->sequence = q_data_out->sequence++;
dst_buf->sequence = q_data_cap->sequence++;
@@ -1636,11 +1636,11 @@ static void mxc_jpeg_device_run(void *priv)
ctx->slot = mxc_get_free_slot(&jpeg->slot_data);
if (ctx->slot < 0) {
dev_err(dev, "No more free slots\n");
- goto end;
+ goto buf_finish;
}
if (!mxc_jpeg_alloc_slot_data(jpeg)) {
dev_err(dev, "Cannot allocate slot data\n");
- goto end;
+ goto buf_finish;
}
mxc_jpeg_enable_slot(reg, ctx->slot);
@@ -1660,8 +1660,17 @@ static void mxc_jpeg_device_run(void *priv)
mxc_jpeg_dec_mode_go(dev, reg);
}
schedule_delayed_work(&ctx->task_timer, msecs_to_jiffies(hw_timeout));
-end:
+
+ spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
+ return;
+buf_finish:
+ v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
+ v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
+ v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
+job_finish:
spin_unlock_irqrestore(&ctx->mxc_jpeg->hw_lock, flags);
+ v4l2_m2m_job_finish(jpeg->m2m_dev, ctx->fh.m2m_ctx);
}
static int mxc_jpeg_decoder_cmd(struct file *file, void *priv,
@@ -1798,6 +1807,8 @@ static void mxc_jpeg_stop_streaming(struct vb2_queue *q)
dev_dbg(ctx->mxc_jpeg->dev, "Stop streaming ctx=%p", ctx);
+ cancel_delayed_work_sync(&ctx->task_timer);
+
/* Release all active buffers */
for (;;) {
if (V4L2_TYPE_IS_OUTPUT(q->type))
diff --git a/drivers/media/platform/nxp/imx-mipi-csis.c b/drivers/media/platform/nxp/imx-mipi-csis.c
index 5f9691d76434..1b9012dbae39 100644
--- a/drivers/media/platform/nxp/imx-mipi-csis.c
+++ b/drivers/media/platform/nxp/imx-mipi-csis.c
@@ -1104,6 +1104,7 @@ static int mipi_csis_enum_mbus_code(struct v4l2_subdev *sd,
}
static int mipi_csis_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *sdformat)
{
@@ -1225,7 +1226,7 @@ static int mipi_csis_init_state(struct v4l2_subdev *sd,
V4L2_MAP_QUANTIZATION_DEFAULT(false, fmt.format.colorspace,
fmt.format.ycbcr_enc);
- return mipi_csis_set_fmt(sd, state, &fmt);
+ return mipi_csis_set_fmt(sd, NULL, state, &fmt);
}
static int mipi_csis_log_status(struct v4l2_subdev *sd)
diff --git a/drivers/media/platform/nxp/imx7-media-csi.c b/drivers/media/platform/nxp/imx7-media-csi.c
index 22c0cbdc92bf..f06b03967aef 100644
--- a/drivers/media/platform/nxp/imx7-media-csi.c
+++ b/drivers/media/platform/nxp/imx7-media-csi.c
@@ -1890,6 +1890,7 @@ static void imx7_csi_try_fmt(struct v4l2_subdev *sd,
}
static int imx7_csi_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
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 7bb1335f2110..8d50e5fb3b13 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
@@ -260,6 +260,7 @@ static int mxc_isi_crossbar_enum_mbus_code(struct v4l2_subdev *sd,
}
static int mxc_isi_crossbar_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
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 934f7b356258..a12e7445073c 100644
--- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
+++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-pipe.c
@@ -524,6 +524,7 @@ static int mxc_isi_pipe_enum_mbus_code(struct v4l2_subdev *sd,
}
static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -619,6 +620,7 @@ static int mxc_isi_pipe_set_fmt(struct v4l2_subdev *sd,
}
static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -678,6 +680,7 @@ static int mxc_isi_pipe_get_selection(struct v4l2_subdev *sd,
}
static int mxc_isi_pipe_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
index 04ebed8a0493..17756b7fe1dd 100644
--- a/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
+++ b/drivers/media/platform/nxp/imx8mq-mipi-csi2.c
@@ -597,6 +597,7 @@ static int imx8mq_mipi_csi_enum_mbus_code(struct v4l2_subdev *sd,
}
static int imx8mq_mipi_csi_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/media/platform/nxp/imx95-csi-formatter.c b/drivers/media/platform/nxp/imx95-csi-formatter.c
new file mode 100644
index 000000000000..37dfcefbf0d1
--- /dev/null
+++ b/drivers/media/platform/nxp/imx95-csi-formatter.c
@@ -0,0 +1,759 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bits.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/* CSI Pixel Formatter registers map */
+
+#define CSI_VC_INTERLACED_LINE_CNT(vc) (0x00 + (vc) * 0x04)
+#define INTERLACED_ODD_LINE_CNT_SET(x) FIELD_PREP(GENMASK(13, 0), (x))
+#define INTERLACED_EVEN_LINE_CNT_SET(x) FIELD_PREP(GENMASK(29, 16), (x))
+
+#define CSI_VC_INTERLACED_CTRL 0x20
+
+#define CSI_VC_INTERLACED_ERR 0x24
+#define CSI_VC_ERR_MASK GENMASK(7, 0)
+#define CSI_VC_ERR(vc) BIT((vc))
+
+#define CSI_VC_YUV420_FIRST_LINE_EVEN 0x28
+#define YUV420_FIRST_LINE_EVEN(vc) BIT((vc))
+
+#define CSI_RAW32_CTRL 0x30
+#define CSI_VC_RAW32_MODE(vc) BIT((vc))
+#define CSI_VC_RAW32_SWAP_MODE(vc) BIT((vc) + 8)
+
+#define CSI_STREAM_FENCING_CTRL 0x34
+#define CSI_VC_STREAM_FENCING(vc) BIT((vc))
+#define CSI_VC_STREAM_FENCING_RST(vc) BIT((vc) + 8)
+
+#define CSI_STREAM_FENCING_STS 0x38
+#define CSI_STREAM_FENCING_STS_MASK GENMASK(7, 0)
+
+#define CSI_VC_NON_PIXEL_DATA_TYPE(vc) (0x40 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_CTRL(vc) (0x60 + (vc) * 0x04)
+#define NEW_VC(vc) FIELD_PREP(GENMASK(3, 1), vc)
+#define REROUTE_VC_ENABLE BIT(0)
+
+#define CSI_VC_ROUTE_PIXEL_DATA_TYPE(vc) (0x80 + (vc) * 0x04)
+
+#define CSI_VC_NON_PIXEL_DATA_CTRL(vc) (0xa0 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_TYPE(vc) (0xc0 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_TYPE_ERR(vc) (0xe0 + (vc) * 0x04)
+
+#define CSI_FORMATTER_PAD_SINK 0
+#define CSI_FORMATTER_PAD_SOURCE 1
+#define CSI_FORMATTER_PAD_NUM 2
+
+#define CSI_FORMATTER_VC_NUM 8 /* Number of virtual channels */
+
+struct csi_formatter_pix_format {
+ u32 code;
+ u32 data_type;
+};
+
+struct csi_formatter {
+ struct device *dev;
+ struct regmap *regs;
+ u32 reg_offset;
+
+ struct v4l2_subdev sd;
+ struct v4l2_async_notifier notifier;
+ struct media_pad pads[CSI_FORMATTER_PAD_NUM];
+
+ struct v4l2_subdev *remote_sd;
+ u32 remote_pad;
+
+ u64 enabled_streams;
+};
+
+struct csi_formatter_dt_index {
+ u8 dtype;
+ u8 index;
+};
+
+/*
+ * The index corresponds to the bit index in the register that enables
+ * the data type of pixel data transported by the Formatter.
+ */
+static const struct csi_formatter_dt_index csi_formatter_dt_to_index_map[] = {
+ { .dtype = MIPI_CSI2_DT_YUV420_8B, .index = 0 },
+ { .dtype = MIPI_CSI2_DT_YUV420_8B_LEGACY, .index = 2 },
+ { .dtype = MIPI_CSI2_DT_YUV422_8B, .index = 6 },
+ { .dtype = MIPI_CSI2_DT_RGB444, .index = 8 },
+ { .dtype = MIPI_CSI2_DT_RGB555, .index = 9 },
+ { .dtype = MIPI_CSI2_DT_RGB565, .index = 10 },
+ { .dtype = MIPI_CSI2_DT_RGB666, .index = 11 },
+ { .dtype = MIPI_CSI2_DT_RGB888, .index = 12 },
+ { .dtype = MIPI_CSI2_DT_RAW6, .index = 16 },
+ { .dtype = MIPI_CSI2_DT_RAW7, .index = 17 },
+ { .dtype = MIPI_CSI2_DT_RAW8, .index = 18 },
+ { .dtype = MIPI_CSI2_DT_RAW10, .index = 19 },
+ { .dtype = MIPI_CSI2_DT_RAW12, .index = 20 },
+ { .dtype = MIPI_CSI2_DT_RAW14, .index = 21 },
+ { .dtype = MIPI_CSI2_DT_RAW16, .index = 22 },
+};
+
+static const struct csi_formatter_pix_format csi_formatter_formats[] = {
+ /* YUV formats */
+ { MEDIA_BUS_FMT_UYVY8_1X16, MIPI_CSI2_DT_YUV422_8B },
+ /* RGB formats */
+ { MEDIA_BUS_FMT_RGB565_1X16, MIPI_CSI2_DT_RGB565 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888 },
+ /* RAW (Bayer and greyscale) formats */
+ { MEDIA_BUS_FMT_SBGGR8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_Y8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_Y10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_Y12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SBGGR16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SGBRG16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SGRBG16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SRGGB16_1X16, MIPI_CSI2_DT_RAW16 },
+};
+
+static const struct v4l2_mbus_framefmt formatter_default_fmt = {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .width = 1920U,
+ .height = 1080U,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
+ .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+};
+
+static const struct csi_formatter_pix_format *csi_formatter_find_format(u32 code)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(csi_formatter_formats); i++) {
+ if (code == csi_formatter_formats[i].code)
+ return &csi_formatter_formats[i];
+ }
+
+ return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static inline struct csi_formatter *sd_to_formatter(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_formatter, sd);
+}
+
+static int __csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
+ &formatter_default_fmt);
+}
+
+static int csi_formatter_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = CSI_FORMATTER_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = CSI_FORMATTER_PAD_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return __csi_formatter_subdev_set_routing(sd, sd_state, &routing);
+}
+
+static int csi_formatter_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad == CSI_FORMATTER_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, code->pad,
+ code->stream);
+ code->code = fmt->code;
+ return 0;
+ }
+
+ if (code->index >= ARRAY_SIZE(csi_formatter_formats))
+ return -EINVAL;
+
+ code->code = csi_formatter_formats[code->index].code;
+
+ return 0;
+}
+
+static int csi_formatter_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *format)
+{
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ media_entity_is_streaming(&sd->entity))
+ return -EBUSY;
+
+ if (format->pad == CSI_FORMATTER_PAD_SOURCE)
+ return v4l2_subdev_get_fmt(sd, sd_state, format);
+
+ if (!csi_formatter_find_format(format->format.code))
+ format->format.code = csi_formatter_formats[0].code;
+
+ v4l_bound_align_image(&format->format.width, 1, 0xffff, 2,
+ &format->format.height, 1, 0xffff, 0, 0);
+
+ /* TODO: Add interlaced format support */
+ format->format.field = V4L2_FIELD_NONE;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, format->pad,
+ format->stream);
+ *fmt = format->format;
+
+ /* Propagate the format from sink stream to source stream */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(sd_state, format->pad,
+ format->stream);
+ *fmt = format->format;
+
+ return 0;
+}
+
+static int csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ media_entity_is_streaming(&sd->entity))
+ return -EBUSY;
+
+ return __csi_formatter_subdev_set_routing(sd, state, routing);
+}
+
+static u8 csi_formatter_get_index_by_dt(struct csi_formatter *formatter,
+ u8 data_type)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(csi_formatter_dt_to_index_map); ++i) {
+ const struct csi_formatter_dt_index *entry =
+ &csi_formatter_dt_to_index_map[i];
+
+ if (data_type == entry->dtype)
+ return entry->index;
+ }
+
+ dev_WARN(formatter->dev, "Unsupported data type 0x%x, using default\n",
+ data_type);
+
+ return csi_formatter_dt_to_index_map[0].index;
+}
+
+static int csi_formatter_get_vc(struct csi_formatter *formatter,
+ struct v4l2_mbus_frame_desc *fd,
+ unsigned int stream)
+{
+ struct v4l2_mbus_frame_desc_entry *entry = NULL;
+ u8 vc;
+
+ for (unsigned int i = 0; i < fd->num_entries; ++i) {
+ if (fd->entry[i].stream == stream) {
+ entry = &fd->entry[i];
+ break;
+ }
+ }
+
+ if (!entry) {
+ dev_err(formatter->dev,
+ "No frame desc entry for stream %u\n", stream);
+ return -EPIPE;
+ }
+
+ vc = entry->bus.csi2.vc;
+
+ if (vc >= CSI_FORMATTER_VC_NUM) {
+ dev_err(formatter->dev, "Invalid virtual channel %u\n", vc);
+ return -EPIPE;
+ }
+
+ return vc;
+}
+
+static int csi_formatter_get_frame_desc(struct csi_formatter *formatter,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ int ret;
+
+ ret = v4l2_subdev_call(formatter->remote_sd, pad, get_frame_desc,
+ formatter->remote_pad, fd);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_err(formatter->dev, "Failed to get frame desc: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * If the source doesn't implement .get_frame_desc(), assume a single
+ * stream on VC 0. fd is zero-initialized, only set the fields that have
+ * a non-zero value.
+ */
+ if (ret == -ENOIOCTLCMD) {
+ fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+ fd->num_entries = 1;
+ }
+
+ return 0;
+}
+
+static void csi_formatter_stop_stream(struct csi_formatter *formatter,
+ struct v4l2_subdev_state *state,
+ u64 stream_mask)
+{
+ const struct csi_formatter_pix_format *pix_fmt;
+ struct v4l2_mbus_frame_desc fd = {};
+ struct v4l2_subdev_route *route;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int reg;
+ unsigned int mask;
+ int vc;
+ int ret;
+
+ ret = csi_formatter_get_frame_desc(formatter, &fd);
+ if (ret)
+ return;
+
+ for_each_active_route(&state->routing, route) {
+ if (!(stream_mask & BIT_ULL(route->source_stream)))
+ continue;
+
+ vc = csi_formatter_get_vc(formatter, &fd, route->sink_stream);
+ if (vc < 0)
+ continue;
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+
+ pix_fmt = csi_formatter_find_format(fmt->code);
+ if (WARN_ON(!pix_fmt))
+ continue;
+
+ reg = CSI_VC_PIXEL_DATA_TYPE(vc) + formatter->reg_offset;
+ mask = BIT(csi_formatter_get_index_by_dt(formatter,
+ pix_fmt->data_type));
+
+ /* Clear the data type bit to disable this VC */
+ regmap_clear_bits(formatter->regs, reg, mask);
+ }
+}
+
+static int csi_formatter_start_stream(struct csi_formatter *formatter,
+ struct v4l2_subdev_state *state,
+ u64 stream_mask)
+{
+ const struct csi_formatter_pix_format *pix_fmt;
+ struct v4l2_subdev_route *route;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_mbus_frame_desc fd = {};
+ u64 configured_streams = 0;
+ unsigned int reg;
+ unsigned int mask;
+ int vc;
+ int ret;
+
+ ret = csi_formatter_get_frame_desc(formatter, &fd);
+ if (ret)
+ return ret;
+
+ for_each_active_route(&state->routing, route) {
+ if (!(stream_mask & BIT_ULL(route->source_stream)))
+ continue;
+
+ vc = csi_formatter_get_vc(formatter, &fd, route->sink_stream);
+ if (vc < 0) {
+ ret = vc;
+ goto err_cleanup;
+ }
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+
+ pix_fmt = csi_formatter_find_format(fmt->code);
+ if (WARN_ON(!pix_fmt)) {
+ ret = -EINVAL;
+ goto err_cleanup;
+ }
+
+ reg = CSI_VC_PIXEL_DATA_TYPE(vc) + formatter->reg_offset;
+ mask = BIT(csi_formatter_get_index_by_dt(formatter,
+ pix_fmt->data_type));
+
+ /* Set the data type bit to enable this VC */
+ regmap_set_bits(formatter->regs, reg, mask);
+
+ configured_streams |= BIT_ULL(route->source_stream);
+ }
+
+ return 0;
+
+err_cleanup:
+ csi_formatter_stop_stream(formatter, state, configured_streams);
+ return ret;
+}
+
+static int csi_formatter_subdev_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+ struct device *dev = formatter->dev;
+ u64 sink_streams;
+ int ret;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ CSI_FORMATTER_PAD_SOURCE,
+ CSI_FORMATTER_PAD_SINK,
+ &streams_mask);
+ if (!sink_streams || !streams_mask)
+ return -EINVAL;
+
+ if (!formatter->enabled_streams) {
+ ret = pm_runtime_resume_and_get(formatter->dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to resume runtime PM: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = csi_formatter_start_stream(formatter, state, streams_mask);
+ if (ret)
+ goto err_runtime_put;
+
+ ret = v4l2_subdev_enable_streams(formatter->remote_sd,
+ formatter->remote_pad,
+ sink_streams);
+ if (ret)
+ goto err_stop_stream;
+
+ formatter->enabled_streams |= streams_mask;
+
+ return 0;
+
+err_stop_stream:
+ csi_formatter_stop_stream(formatter, state, streams_mask);
+err_runtime_put:
+ if (!formatter->enabled_streams)
+ pm_runtime_put_autosuspend(formatter->dev);
+ return ret;
+}
+
+static int csi_formatter_subdev_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+ u64 sink_streams;
+ int ret;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ CSI_FORMATTER_PAD_SOURCE,
+ CSI_FORMATTER_PAD_SINK,
+ &streams_mask);
+ if (!sink_streams || !streams_mask)
+ return -EINVAL;
+
+ ret = v4l2_subdev_disable_streams(formatter->remote_sd, formatter->remote_pad,
+ sink_streams);
+ if (ret)
+ dev_err(formatter->dev, "Failed to disable streams: %d\n", ret);
+
+ csi_formatter_stop_stream(formatter, state, streams_mask);
+
+ formatter->enabled_streams &= ~streams_mask;
+
+ if (!formatter->enabled_streams)
+ pm_runtime_put_autosuspend(formatter->dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops formatter_subdev_pad_ops = {
+ .enum_mbus_code = csi_formatter_subdev_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = csi_formatter_subdev_set_fmt,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
+ .set_routing = csi_formatter_subdev_set_routing,
+ .enable_streams = csi_formatter_subdev_enable_streams,
+ .disable_streams = csi_formatter_subdev_disable_streams,
+};
+
+static const struct v4l2_subdev_ops formatter_subdev_ops = {
+ .pad = &formatter_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops formatter_internal_ops = {
+ .init_state = csi_formatter_subdev_init_state,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static const struct media_entity_operations formatter_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
+};
+
+static int csi_formatter_subdev_init(struct csi_formatter *formatter)
+{
+ struct v4l2_subdev *sd = &formatter->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &formatter_subdev_ops);
+
+ strscpy(sd->name, dev_name(formatter->dev), sizeof(sd->name));
+ sd->internal_ops = &formatter_internal_ops;
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_STREAMS;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->entity.ops = &formatter_entity_ops;
+ sd->dev = formatter->dev;
+
+ formatter->pads[CSI_FORMATTER_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
+ formatter->pads[CSI_FORMATTER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, CSI_FORMATTER_PAD_NUM,
+ formatter->pads);
+ if (ret) {
+ dev_err(formatter->dev, "Failed to init pads\n");
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ media_entity_cleanup(&sd->entity);
+
+ return ret;
+}
+
+static inline struct csi_formatter *
+notifier_to_csi_formatter(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_formatter, notifier);
+}
+
+static int csi_formatter_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ struct csi_formatter *formatter = notifier_to_csi_formatter(notifier);
+ struct v4l2_subdev *sdev = &formatter->sd;
+ struct media_pad *sink = &sdev->entity.pads[CSI_FORMATTER_PAD_SINK];
+ int ret;
+
+ formatter->remote_sd = sd;
+
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink, link_flags);
+ if (ret < 0)
+ return ret;
+
+ formatter->remote_pad = media_pad_remote_pad_first(sink)->index;
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations formatter_notify_ops = {
+ .bound = csi_formatter_notify_bound,
+};
+
+static int csi_formatter_async_register(struct csi_formatter *formatter)
+{
+ struct device *dev = formatter->dev;
+ struct v4l2_async_connection *asc;
+ int ret;
+
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ v4l2_async_subdev_nf_init(&formatter->notifier, &formatter->sd);
+
+ asc = v4l2_async_nf_add_fwnode_remote(&formatter->notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_cleanup_notifier;
+ }
+
+ formatter->notifier.ops = &formatter_notify_ops;
+
+ ret = v4l2_async_nf_register(&formatter->notifier);
+ if (ret)
+ goto err_cleanup_notifier;
+
+ ret = v4l2_async_register_subdev(&formatter->sd);
+ if (ret)
+ goto err_unregister_notifier;
+
+ return 0;
+
+err_unregister_notifier:
+ v4l2_async_nf_unregister(&formatter->notifier);
+err_cleanup_notifier:
+ v4l2_async_nf_cleanup(&formatter->notifier);
+ return ret;
+}
+
+static void csi_formatter_async_unregister(struct csi_formatter *formatter)
+{
+ v4l2_async_unregister_subdev(&formatter->sd);
+ v4l2_async_nf_unregister(&formatter->notifier);
+ v4l2_async_nf_cleanup(&formatter->notifier);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(csi_formatter_pm_ops,
+ pm_clk_suspend, pm_clk_resume, NULL);
+
+static int csi_formatter_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct csi_formatter *formatter;
+ int ret;
+
+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
+ if (!formatter)
+ return -ENOMEM;
+
+ formatter->dev = dev;
+
+ formatter->regs = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(formatter->regs))
+ return dev_err_probe(dev, PTR_ERR(formatter->regs),
+ "Failed to get csi formatter regmap\n");
+
+ ret = of_property_read_u32(dev->of_node, "reg", &formatter->reg_offset);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to get csi formatter reg property\n");
+
+ ret = devm_pm_clk_create(dev);
+ if (ret)
+ return ret;
+
+ ret = of_pm_clk_add_clks(dev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to add clocks\n");
+
+ ret = csi_formatter_subdev_init(formatter);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to initialize formatter subdev\n");
+
+ platform_set_drvdata(pdev, formatter);
+
+ /* Enable runtime PM with autosuspend. */
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ goto err_pm_disable;
+
+ ret = csi_formatter_async_register(formatter);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "Failed to register async subdevice\n");
+ goto err_pm_disable;
+ }
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_dont_use_autosuspend(dev);
+ v4l2_subdev_cleanup(&formatter->sd);
+ media_entity_cleanup(&formatter->sd.entity);
+ return ret;
+}
+
+static void csi_formatter_remove(struct platform_device *pdev)
+{
+ struct csi_formatter *formatter = platform_get_drvdata(pdev);
+
+ csi_formatter_async_unregister(formatter);
+
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_suspend(&pdev->dev);
+
+ v4l2_subdev_cleanup(&formatter->sd);
+ media_entity_cleanup(&formatter->sd.entity);
+}
+
+static const struct of_device_id csi_formatter_of_match[] = {
+ { .compatible = "fsl,imx95-csi-formatter" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, csi_formatter_of_match);
+
+static struct platform_driver csi_formatter_device_driver = {
+ .driver = {
+ .name = "csi-pixel-formatter",
+ .of_match_table = csi_formatter_of_match,
+ .pm = pm_ptr(&csi_formatter_pm_ops),
+ },
+ .probe = csi_formatter_probe,
+ .remove = csi_formatter_remove,
+};
+
+module_platform_driver(csi_formatter_device_driver);
+
+MODULE_AUTHOR("NXP Semiconductor, Inc.");
+MODULE_DESCRIPTION("NXP i.MX95 CSI Pixel Formatter driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index 48459b46a981..a00791b00feb 100644
--- a/drivers/media/platform/qcom/camss/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -987,6 +987,7 @@ static int csid_get_format(struct v4l2_subdev *sd,
* Return -EINVAL or zero on success
*/
static int csid_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1036,7 +1037,7 @@ static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
}
};
- return csid_set_format(sd, fh ? fh->state : NULL, &format);
+ return csid_set_format(sd, NULL, fh ? fh->state : NULL, &format);
}
/*
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 539ac4888b60..0dd50f3879d9 100644
--- a/drivers/media/platform/qcom/camss/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -503,6 +503,7 @@ static int csiphy_get_format(struct v4l2_subdev *sd,
* Return -EINVAL or zero on success
*/
static int csiphy_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -555,7 +556,7 @@ static int csiphy_init_formats(struct v4l2_subdev *sd,
}
};
- return csiphy_set_format(sd, fh ? fh->state : NULL, &format);
+ return csiphy_set_format(sd, NULL, fh ? fh->state : NULL, &format);
}
static bool __printf(2, 3)
diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index 20ccd7b1f11f..f266f8532ecc 100644
--- a/drivers/media/platform/qcom/camss/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -1037,6 +1037,7 @@ static int ispif_get_format(struct v4l2_subdev *sd,
* Return -EINVAL or zero on success
*/
static int ispif_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1085,7 +1086,7 @@ static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
}
};
- return ispif_set_format(sd, fh ? fh->state : NULL, &format);
+ return ispif_set_format(sd, NULL, fh ? fh->state : NULL, &format);
}
/*
diff --git a/drivers/media/platform/qcom/camss/camss-tpg.c b/drivers/media/platform/qcom/camss/camss-tpg.c
index c5b75132add4..5764a60fa34c 100644
--- a/drivers/media/platform/qcom/camss/camss-tpg.c
+++ b/drivers/media/platform/qcom/camss/camss-tpg.c
@@ -292,6 +292,7 @@ static int tpg_get_format(struct v4l2_subdev *sd,
}
static int tpg_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -322,7 +323,7 @@ static int tpg_init_formats(struct v4l2_subdev *sd,
}
};
- return tpg_set_format(sd, fh ? fh->state : NULL, &format);
+ return tpg_set_format(sd, NULL, fh ? fh->state : NULL, &format);
}
static int tpg_s_ctrl(struct v4l2_ctrl *ctrl)
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index 319d19158988..c14d97a131f6 100644
--- a/drivers/media/platform/qcom/camss/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -1562,6 +1562,7 @@ static int vfe_get_format(struct v4l2_subdev *sd,
}
static int vfe_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel);
@@ -1574,6 +1575,7 @@ static int vfe_set_selection(struct v4l2_subdev *sd,
* Return -EINVAL or zero on success
*/
static int vfe_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1608,7 +1610,7 @@ static int vfe_set_format(struct v4l2_subdev *sd,
sel.target = V4L2_SEL_TGT_COMPOSE;
sel.r.width = fmt->format.width;
sel.r.height = fmt->format.height;
- ret = vfe_set_selection(sd, sd_state, &sel);
+ ret = vfe_set_selection(sd, ci, sd_state, &sel);
if (ret < 0)
return ret;
}
@@ -1625,6 +1627,7 @@ static int vfe_set_format(struct v4l2_subdev *sd,
* Return -EINVAL or zero on success
*/
static int vfe_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1695,6 +1698,7 @@ static int vfe_get_selection(struct v4l2_subdev *sd,
* Return -EINVAL or zero on success
*/
static int vfe_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1721,7 +1725,7 @@ static int vfe_set_selection(struct v4l2_subdev *sd,
crop.pad = MSM_VFE_PAD_SRC;
crop.target = V4L2_SEL_TGT_CROP;
crop.r = *rect;
- ret = vfe_set_selection(sd, sd_state, &crop);
+ ret = vfe_set_selection(sd, ci, sd_state, &crop);
} else if (sel->target == V4L2_SEL_TGT_CROP &&
sel->pad == MSM_VFE_PAD_SRC) {
struct v4l2_subdev_format fmt = { 0 };
@@ -1742,7 +1746,7 @@ static int vfe_set_selection(struct v4l2_subdev *sd,
fmt.format.width = rect->width;
fmt.format.height = rect->height;
- ret = vfe_set_format(sd, sd_state, &fmt);
+ ret = vfe_set_format(sd, ci, sd_state, &fmt);
} else {
ret = -EINVAL;
}
@@ -1772,7 +1776,7 @@ static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
}
};
- return vfe_set_format(sd, fh ? fh->state : NULL, &format);
+ return vfe_set_format(sd, NULL, fh ? fh->state : NULL, &format);
}
/*
diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 2123f6388e3d..23f3cc30a15a 100644
--- a/drivers/media/platform/qcom/camss/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -4793,30 +4793,23 @@ static int camss_parse_endpoint_node(struct device *dev,
static int camss_parse_ports(struct camss *camss)
{
struct device *dev = camss->dev;
- struct fwnode_handle *fwnode = dev_fwnode(dev), *ep;
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
int ret;
- fwnode_graph_for_each_endpoint(fwnode, ep) {
+ fwnode_graph_for_each_endpoint_scoped(fwnode, ep) {
struct camss_async_subdev *csd;
csd = v4l2_async_nf_add_fwnode_remote(&camss->notifier, ep,
typeof(*csd));
- if (IS_ERR(csd)) {
- ret = PTR_ERR(csd);
- goto err_cleanup;
- }
+ if (IS_ERR(csd))
+ return PTR_ERR(csd);
ret = camss_parse_endpoint_node(dev, ep, csd);
if (ret < 0)
- goto err_cleanup;
+ return ret;
}
return 0;
-
-err_cleanup:
- fwnode_handle_put(ep);
-
- return ret;
}
/*
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
index 104908afbf41..66d0dde6d840 100644
--- a/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
@@ -404,6 +404,7 @@ static int csi2_init_state(struct v4l2_subdev *sd,
}
static int csi2_pad_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c
index 05762b1be2bc..ea21fb1f60cf 100644
--- a/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c
@@ -429,6 +429,7 @@ static int pisp_fe_init_state(struct v4l2_subdev *sd,
}
static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/renesas/rcar-csi2.c b/drivers/media/platform/renesas/rcar-csi2.c
index 6635f5782175..4851fe649d8a 100644
--- a/drivers/media/platform/renesas/rcar-csi2.c
+++ b/drivers/media/platform/renesas/rcar-csi2.c
@@ -702,6 +702,17 @@ static const struct rcar_csi2_format rcar_csi2_formats[] = {
},
};
+static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = {
+ .width = 1920,
+ .height = 1080,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+};
+
static const struct rcar_csi2_format *rcsi2_code_to_fmt(unsigned int code)
{
unsigned int i;
@@ -773,7 +784,7 @@ struct rcar_csi2 {
int channel_vc[4];
- int stream_count;
+ u64 enabled_sink_streams_mask;
bool cphy;
unsigned short lanes;
@@ -1023,17 +1034,24 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv,
*/
freq = v4l2_get_link_freq(remote_pad, 0, 0);
if (freq < 0) {
+ const struct v4l2_subdev_route *route;
const struct rcar_csi2_format *format;
const struct v4l2_mbus_framefmt *fmt;
unsigned int lanes;
unsigned int bpp;
int ret;
+ if (state->routing.num_routes != 1)
+ return -EINVAL;
+
ret = rcsi2_get_active_lanes(priv, &lanes);
if (ret)
return ret;
- fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
+ route = &state->routing.routes[0];
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
if (!fmt)
return -EINVAL;
@@ -1062,52 +1080,93 @@ static int rcsi2_calc_mbps(struct rcar_csi2 *priv,
static int rcsi2_start_receiver_gen3(struct rcar_csi2 *priv,
struct v4l2_subdev_state *state)
{
- const struct rcar_csi2_format *format;
- u32 phycnt, vcdt = 0, vcdt2 = 0, fld = 0;
- const struct v4l2_mbus_framefmt *fmt;
+ u32 phycnt, vcdt = 0, vcdt2 = 0;
+ u32 fld = FLD_DET_SEL(1);
+ struct v4l2_mbus_frame_desc source_fd;
+ struct v4l2_subdev_route *route;
unsigned int lanes;
- unsigned int i;
int mbps, ret;
+ u8 ch = 0;
+
+ ret = v4l2_subdev_call(priv->remote, pad, get_frame_desc,
+ priv->remote_pad, &source_fd);
+ if (ret && ret != -ENOIOCTLCMD)
+ return ret;
- /* Use the format on the sink pad to compute the receiver config. */
- fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK);
+ if (ret == -ENOIOCTLCMD) {
+ /* Create a fallback source_fd */
+ struct v4l2_mbus_frame_desc *fd = &source_fd;
+ const struct v4l2_subdev_route *route;
+ const struct rcar_csi2_format *format;
+ struct v4l2_mbus_framefmt *fmt;
- dev_dbg(priv->dev, "Input size (%ux%u%c)\n",
- fmt->width, fmt->height,
- fmt->field == V4L2_FIELD_NONE ? 'p' : 'i');
+ if (state->routing.num_routes != 1)
+ return -EINVAL;
- /* Code is validated in set_fmt. */
- format = rcsi2_code_to_fmt(fmt->code);
- if (!format)
- return -EINVAL;
+ route = &state->routing.routes[0];
- /*
- * Enable all supported CSI-2 channels with virtual channel and
- * data type matching.
- *
- * NOTE: It's not possible to get individual datatype for each
- * source virtual channel. Once this is possible in V4L2
- * it should be used here.
- */
- for (i = 0; i < priv->info->num_channels; i++) {
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+ if (!fmt)
+ return -EINVAL;
+
+ format = rcsi2_code_to_fmt(fmt->code);
+ if (!format)
+ return -EINVAL;
+
+ memset(fd, 0, sizeof(*fd));
+
+ fd->num_entries = 1;
+ fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+ fd->entry[0].stream = 0;
+ fd->entry[0].pixelcode = fmt->code;
+ fd->entry[0].bus.csi2.vc = 0;
+ fd->entry[0].bus.csi2.dt = format->datatype;
+ }
+
+ for_each_active_route(&state->routing, route) {
+ const struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
+ const struct v4l2_mbus_framefmt *fmt;
+ unsigned int i;
u32 vcdt_part;
- if (priv->channel_vc[i] < 0)
- continue;
+ for (i = 0; i < source_fd.num_entries; i++) {
+ if (source_fd.entry[i].stream == route->sink_stream) {
+ source_entry = &source_fd.entry[i];
+ break;
+ }
+ }
+
+ if (!source_entry) {
+ dev_err(priv->dev,
+ "Failed to find stream from source frame desc\n");
+ return -EPIPE;
+ }
- vcdt_part = VCDT_SEL_VC(priv->channel_vc[i]) | VCDT_VCDTN_EN |
- VCDT_SEL_DTN_ON | VCDT_SEL_DT(format->datatype);
+ vcdt_part = VCDT_SEL_VC(source_entry->bus.csi2.vc) |
+ VCDT_VCDTN_EN | VCDT_SEL_DTN_ON |
+ VCDT_SEL_DT(source_entry->bus.csi2.dt);
/* Store in correct reg and offset. */
- if (i < 2)
- vcdt |= vcdt_part << ((i % 2) * 16);
+ if (ch < 2)
+ vcdt |= vcdt_part << ((ch % 2) * 16);
else
- vcdt2 |= vcdt_part << ((i % 2) * 16);
- }
+ vcdt2 |= vcdt_part << ((ch % 2) * 16);
+
+ fmt = v4l2_subdev_state_get_format(state, RCAR_CSI2_SINK,
+ route->sink_stream);
+ if (!fmt)
+ return -EINVAL;
- if (fmt->field == V4L2_FIELD_ALTERNATE)
- fld = FLD_DET_SEL(1) | FLD_FLD_EN(3) | FLD_FLD_EN(2) |
- FLD_FLD_EN(1) | FLD_FLD_EN(0);
+ dev_dbg(priv->dev, "Input size (%ux%u%c)\n",
+ fmt->width, fmt->height,
+ fmt->field == V4L2_FIELD_NONE ? 'p' : 'i');
+
+ if (fmt->field == V4L2_FIELD_ALTERNATE)
+ fld |= FLD_FLD_EN(ch);
+
+ ch++;
+ }
/*
* Get the number of active data lanes inspecting the remote mbus
@@ -1822,20 +1881,12 @@ static int rcsi2_start(struct rcar_csi2 *priv, struct v4l2_subdev_state *state)
return ret;
}
- ret = v4l2_subdev_enable_streams(priv->remote, priv->remote_pad,
- BIT_ULL(0));
- if (ret) {
- rcsi2_enter_standby(priv);
- return ret;
- }
-
return 0;
}
static void rcsi2_stop(struct rcar_csi2 *priv)
{
rcsi2_enter_standby(priv);
- v4l2_subdev_disable_streams(priv->remote, priv->remote_pad, BIT_ULL(0));
}
static int rcsi2_enable_streams(struct v4l2_subdev *sd,
@@ -1843,21 +1894,32 @@ static int rcsi2_enable_streams(struct v4l2_subdev *sd,
u64 source_streams_mask)
{
struct rcar_csi2 *priv = sd_to_csi2(sd);
- int ret = 0;
-
- if (source_streams_mask != 1)
- return -EINVAL;
+ u64 sink_streams;
+ int ret;
if (!priv->remote)
return -ENODEV;
- if (priv->stream_count == 0) {
+ if (!priv->enabled_sink_streams_mask) {
ret = rcsi2_start(priv, state);
if (ret)
return ret;
}
- priv->stream_count += 1;
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ source_pad,
+ RCAR_CSI2_SINK,
+ &source_streams_mask);
+
+ ret = v4l2_subdev_enable_streams(priv->remote, priv->remote_pad,
+ sink_streams);
+ if (ret) {
+ if (!priv->enabled_sink_streams_mask)
+ rcsi2_stop(priv);
+ return ret;
+ }
+
+ priv->enabled_sink_streams_mask |= sink_streams;
return ret;
}
@@ -1867,28 +1929,36 @@ static int rcsi2_disable_streams(struct v4l2_subdev *sd,
u32 source_pad, u64 source_streams_mask)
{
struct rcar_csi2 *priv = sd_to_csi2(sd);
- int ret = 0;
-
- if (source_streams_mask != 1)
- return -EINVAL;
+ u64 sink_streams;
+ int ret;
if (!priv->remote)
return -ENODEV;
- if (priv->stream_count == 1)
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ source_pad,
+ RCAR_CSI2_SINK,
+ &source_streams_mask);
+
+ if (priv->enabled_sink_streams_mask == sink_streams)
rcsi2_stop(priv);
- priv->stream_count -= 1;
+ ret = v4l2_subdev_disable_streams(priv->remote, priv->remote_pad,
+ sink_streams);
+ if (ret)
+ return ret;
- return ret;
+ priv->enabled_sink_streams_mask &= ~sink_streams;
+
+ return 0;
}
static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- struct rcar_csi2 *priv = sd_to_csi2(sd);
- unsigned int num_pads = rcsi2_num_pads(priv);
+ struct v4l2_mbus_framefmt *fmt;
if (format->pad > RCAR_CSI2_SINK)
return v4l2_subdev_get_fmt(sd, state, format);
@@ -1896,21 +1966,136 @@ static int rcsi2_set_pad_format(struct v4l2_subdev *sd,
if (!rcsi2_code_to_fmt(format->format.code))
format->format.code = rcar_csi2_formats[0].code;
- *v4l2_subdev_state_get_format(state, format->pad) = format->format;
+ /* Set sink format. */
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ /* Propagate the format to the source pad. */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
- /* Propagate the format to the source pads. */
- for (unsigned int i = RCAR_CSI2_SOURCE_VC0; i < num_pads; i++)
- *v4l2_subdev_state_get_format(state, i) = format->format;
+ *fmt = format->format;
return 0;
}
+static int rcsi2_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+ int ret;
+
+ if (priv->info->use_isp) {
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ } else {
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+ }
+
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing,
+ &rcar_csi2_default_fmt);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int rcsi2_get_frame_desc_fallback(struct v4l2_subdev *sd,
+ unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct v4l2_subdev_route *route;
+ const struct rcar_csi2_format *format;
+ struct v4l2_subdev_state *state;
+ struct v4l2_mbus_framefmt *fmt;
+ int ret = 0;
+
+ state = v4l2_subdev_lock_and_get_active_state(sd);
+
+ if (state->routing.num_routes != 1) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ route = &state->routing.routes[0];
+
+ if (route->source_pad != pad) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+ if (!fmt) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ format = rcsi2_code_to_fmt(fmt->code);
+ if (!format) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fd->num_entries = 1;
+ fd->type = V4L2_MBUS_FRAME_DESC_TYPE_CSI2;
+ fd->entry[0].stream = route->source_stream;
+ fd->entry[0].pixelcode = fmt->code;
+ fd->entry[0].bus.csi2.vc = 0;
+ fd->entry[0].bus.csi2.dt = format->datatype;
+
+out:
+ v4l2_subdev_unlock_state(state);
+
+ return ret;
+}
+
+static int rcsi2_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
+ struct v4l2_mbus_frame_desc *fd)
+{
+ struct rcar_csi2 *priv = sd_to_csi2(sd);
+ int ret;
+
+ /*
+ * Providing a frame descriptor on the source pad only makes sense on
+ * Gen4. On Gen3 the CSI-2 IP is used to demux the CSI streams to each
+ * VIN instance, while on Gen4 this job is done by the Channel Selector
+ * of the ISP, and that needs the frame descriptors. So WARN if someone
+ * calls this on Gen3, as it indicates a driver bug.
+ */
+ if (WARN_ON(!priv->info->use_isp))
+ return -ENOTTY;
+
+ if (WARN_ON(pad != RCAR_CSI2_SOURCE_VC0))
+ return -EINVAL;
+
+ ret = v4l2_subdev_get_frame_desc_passthrough(sd, pad, fd);
+ if (ret == -ENOIOCTLCMD)
+ ret = rcsi2_get_frame_desc_fallback(sd, pad, fd);
+ return ret;
+}
+
static const struct v4l2_subdev_pad_ops rcar_csi2_pad_ops = {
.enable_streams = rcsi2_enable_streams,
.disable_streams = rcsi2_disable_streams,
.set_fmt = rcsi2_set_pad_format,
.get_fmt = v4l2_subdev_get_fmt,
+
+ .set_routing = rcsi2_set_routing,
+ .get_frame_desc = rcsi2_get_frame_desc,
};
static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
@@ -1920,24 +2105,23 @@ static const struct v4l2_subdev_ops rcar_csi2_subdev_ops = {
static int rcsi2_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
- struct rcar_csi2 *priv = sd_to_csi2(sd);
- unsigned int num_pads = rcsi2_num_pads(priv);
-
- static const struct v4l2_mbus_framefmt rcar_csi2_default_fmt = {
- .width = 1920,
- .height = 1080,
- .code = MEDIA_BUS_FMT_RGB888_1X24,
- .colorspace = V4L2_COLORSPACE_SRGB,
- .field = V4L2_FIELD_NONE,
- .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
- .quantization = V4L2_QUANTIZATION_DEFAULT,
- .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+ static struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = RCAR_CSI2_SINK,
+ .sink_stream = 0,
+ .source_pad = RCAR_CSI2_SOURCE_VC0,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
};
- for (unsigned int i = RCAR_CSI2_SINK; i < num_pads; i++)
- *v4l2_subdev_state_get_format(state, i) = rcar_csi2_default_fmt;
+ static const struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
- return 0;
+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
+ &rcar_csi2_default_fmt);
}
static const struct v4l2_subdev_internal_ops rcar_csi2_internal_ops = {
@@ -1971,14 +2155,44 @@ static irqreturn_t rcsi2_irq_thread(int irq, void *data)
{
struct v4l2_subdev_state *state;
struct rcar_csi2 *priv = data;
+ int ret;
state = v4l2_subdev_lock_and_get_active_state(&priv->subdev);
+ if (!priv->enabled_sink_streams_mask)
+ goto out;
+
rcsi2_stop(priv);
+
+ ret = v4l2_subdev_disable_streams(priv->remote, priv->remote_pad,
+ priv->enabled_sink_streams_mask);
+ if (ret) {
+ dev_warn(priv->dev,
+ "Error recovery: failed to disable streams: %d\n",
+ ret);
+ goto out;
+ }
+
usleep_range(1000, 2000);
- if (rcsi2_start(priv, state))
- dev_warn(priv->dev, "Failed to restart CSI-2 receiver\n");
+ ret = rcsi2_start(priv, state);
+ if (ret) {
+ dev_warn(priv->dev,
+ "Error recovery: failed to start CSI-2 receiver: %d\n",
+ ret);
+ goto out;
+ }
+
+ ret = v4l2_subdev_enable_streams(priv->remote, priv->remote_pad,
+ priv->enabled_sink_streams_mask);
+ if (ret) {
+ dev_warn(priv->dev,
+ "Error recovery: failed to start streams: %d\n",
+ ret);
+ goto out;
+ }
+
+out:
v4l2_subdev_unlock_state(state);
return IRQ_HANDLED;
@@ -2569,8 +2783,6 @@ static int rcsi2_probe(struct platform_device *pdev)
priv->dev = &pdev->dev;
- priv->stream_count = 0;
-
ret = rcsi2_probe_resources(priv, pdev);
if (ret) {
dev_err(priv->dev, "Failed to get resources\n");
@@ -2590,7 +2802,8 @@ static int rcsi2_probe(struct platform_device *pdev)
v4l2_set_subdevdata(&priv->subdev, &pdev->dev);
snprintf(priv->subdev.name, sizeof(priv->subdev.name), "%s %s",
KBUILD_MODNAME, dev_name(&pdev->dev));
- priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ priv->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_STREAMS;
priv->subdev.entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
priv->subdev.entity.ops = &rcar_csi2_entity_ops;
diff --git a/drivers/media/platform/renesas/rcar-isp/csisp.c b/drivers/media/platform/renesas/rcar-isp/csisp.c
index 53ce47020d17..635eeacb3b19 100644
--- a/drivers/media/platform/renesas/rcar-isp/csisp.c
+++ b/drivers/media/platform/renesas/rcar-isp/csisp.c
@@ -41,6 +41,9 @@
#define ISPCS_DT_CODE03_EN0 BIT(7)
#define ISPCS_DT_CODE03_DT0(dt) ((dt) & 0x3f)
+/* ISP has 12 channels, of which channels 4 to 11 are connected to VINs */
+#define ISPCS_NUM_CHANNELS 12
+
struct rcar_isp_format {
u32 code;
unsigned int datatype;
@@ -123,6 +126,17 @@ static const struct rcar_isp_format rcar_isp_formats[] = {
},
};
+static const struct v4l2_mbus_framefmt risp_default_fmt = {
+ .width = 1920,
+ .height = 1080,
+ .code = MEDIA_BUS_FMT_RGB888_1X24,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .field = V4L2_FIELD_NONE,
+ .ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT,
+ .quantization = V4L2_QUANTIZATION_DEFAULT,
+ .xfer_func = V4L2_XFER_FUNC_DEFAULT,
+};
+
static const struct rcar_isp_format *risp_code_to_fmt(unsigned int code)
{
unsigned int i;
@@ -214,24 +228,82 @@ static void risp_power_off(struct rcar_isp *isp)
pm_runtime_put(isp->dev);
}
-static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state)
+static int risp_configure_routing(struct rcar_isp *isp,
+ struct v4l2_subdev_state *state)
{
- const struct v4l2_mbus_framefmt *fmt;
- const struct rcar_isp_format *format;
- unsigned int vc;
- u32 sel_csi = 0;
+ struct v4l2_mbus_frame_desc source_fd;
+ struct v4l2_subdev_route *route;
int ret;
- fmt = v4l2_subdev_state_get_format(state, RCAR_ISP_SINK);
- if (!fmt)
- return -EINVAL;
+ ret = v4l2_subdev_call(isp->remote, pad, get_frame_desc,
+ isp->remote_pad, &source_fd);
+ if (ret)
+ return ret;
- format = risp_code_to_fmt(fmt->code);
- if (!format) {
- dev_err(isp->dev, "Unsupported bus format\n");
- return -EINVAL;
+ /* Clear the channel registers */
+ for (unsigned int ch = 0; ch < ISPCS_NUM_CHANNELS; ++ch) {
+ risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), 0);
+ risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch), 0);
}
+ for_each_active_route(&state->routing, route) {
+ struct v4l2_mbus_frame_desc_entry *source_entry = NULL;
+ const struct rcar_isp_format *format;
+ const struct v4l2_mbus_framefmt *fmt;
+ unsigned int i;
+ u8 vc, dt, ch;
+ u32 v;
+
+ for (i = 0; i < source_fd.num_entries; i++) {
+ if (source_fd.entry[i].stream == route->sink_stream) {
+ source_entry = &source_fd.entry[i];
+ break;
+ }
+ }
+
+ if (!source_entry) {
+ dev_err(isp->dev,
+ "Failed to find source frame desc entry for stream\n");
+ return -EPIPE;
+ }
+
+ vc = source_entry->bus.csi2.vc;
+ dt = source_entry->bus.csi2.dt;
+ /* Channels 4 - 11 go to VIN */
+ ch = route->source_pad - 1 + 4;
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+ if (!fmt)
+ return -EINVAL;
+
+ format = risp_code_to_fmt(fmt->code);
+ if (!format) {
+ dev_err(isp->dev, "Unsupported bus format\n");
+ return -EINVAL;
+ }
+
+ /* VC Filtering */
+ risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
+
+ /* DT Filtering */
+ risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch),
+ ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
+
+ /* Proc mode */
+ v = risp_read_cs(isp, ISPPROCMODE_DT_REG(dt));
+ v |= ISPPROCMODE_DT_PROC_MODE_VCn(vc, format->procmode);
+ risp_write_cs(isp, ISPPROCMODE_DT_REG(dt), v);
+ }
+
+ return 0;
+}
+
+static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state)
+{
+ u32 sel_csi = 0;
+ int ret;
+
ret = risp_power_on(isp);
if (ret) {
dev_err(isp->dev, "Failed to power on ISP\n");
@@ -245,41 +317,18 @@ static int risp_start(struct rcar_isp *isp, struct v4l2_subdev_state *state)
risp_write_cs(isp, ISPINPUTSEL0_REG,
risp_read_cs(isp, ISPINPUTSEL0_REG) | sel_csi);
- /* Configure Channel Selector. */
- for (vc = 0; vc < 4; vc++) {
- u8 ch = vc + 4;
- u8 dt = format->datatype;
-
- risp_write_cs(isp, ISPCS_FILTER_ID_CH_REG(ch), BIT(vc));
- risp_write_cs(isp, ISPCS_DT_CODE03_CH_REG(ch),
- ISPCS_DT_CODE03_EN3 | ISPCS_DT_CODE03_DT3(dt) |
- ISPCS_DT_CODE03_EN2 | ISPCS_DT_CODE03_DT2(dt) |
- ISPCS_DT_CODE03_EN1 | ISPCS_DT_CODE03_DT1(dt) |
- ISPCS_DT_CODE03_EN0 | ISPCS_DT_CODE03_DT0(dt));
- }
-
- /* Setup processing method. */
- risp_write_cs(isp, ISPPROCMODE_DT_REG(format->datatype),
- ISPPROCMODE_DT_PROC_MODE_VCn(3, format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VCn(2, format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VCn(1, format->procmode) |
- ISPPROCMODE_DT_PROC_MODE_VCn(0, format->procmode));
+ ret = risp_configure_routing(isp, state);
+ if (ret)
+ return ret;
/* Start ISP. */
risp_write_cs(isp, ISPSTART_REG, ISPSTART_START);
- ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad,
- BIT_ULL(0));
- if (ret)
- risp_power_off(isp);
-
- return ret;
+ return 0;
}
static void risp_stop(struct rcar_isp *isp)
{
- v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, BIT_ULL(0));
-
/* Stop ISP. */
risp_write_cs(isp, ISPSTART_REG, ISPSTART_STOP);
@@ -291,7 +340,8 @@ static int risp_enable_streams(struct v4l2_subdev *sd,
u64 source_streams_mask)
{
struct rcar_isp *isp = sd_to_isp(sd);
- int ret = 0;
+ u64 sink_streams;
+ int ret;
if (source_streams_mask != 1)
return -EINVAL;
@@ -305,9 +355,22 @@ static int risp_enable_streams(struct v4l2_subdev *sd,
return ret;
}
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ source_pad,
+ RCAR_ISP_SINK,
+ &source_streams_mask);
+
+ ret = v4l2_subdev_enable_streams(isp->remote, isp->remote_pad,
+ sink_streams);
+ if (ret) {
+ if (isp->stream_count == 0)
+ risp_stop(isp);
+ return ret;
+ }
+
isp->stream_count += 1;
- return ret;
+ return 0;
}
static int risp_disable_streams(struct v4l2_subdev *sd,
@@ -315,6 +378,8 @@ static int risp_disable_streams(struct v4l2_subdev *sd,
u64 source_streams_mask)
{
struct rcar_isp *isp = sd_to_isp(sd);
+ u64 sink_streams;
+ int ret;
if (source_streams_mask != 1)
return -EINVAL;
@@ -322,6 +387,15 @@ static int risp_disable_streams(struct v4l2_subdev *sd,
if (!isp->remote)
return -ENODEV;
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ source_pad,
+ RCAR_ISP_SINK,
+ &source_streams_mask);
+
+ ret = v4l2_subdev_disable_streams(isp->remote, isp->remote_pad, sink_streams);
+ if (ret)
+ return ret;
+
if (isp->stream_count == 1)
risp_stop(isp);
@@ -331,10 +405,11 @@ static int risp_disable_streams(struct v4l2_subdev *sd,
}
static int risp_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- struct v4l2_mbus_framefmt *framefmt;
+ struct v4l2_mbus_framefmt *fmt;
if (format->pad > RCAR_ISP_SINK)
return v4l2_subdev_get_fmt(sd, state, format);
@@ -342,10 +417,41 @@ static int risp_set_pad_format(struct v4l2_subdev *sd,
if (!risp_code_to_fmt(format->format.code))
format->format.code = rcar_isp_formats[0].code;
- for (unsigned int i = 0; i < RCAR_ISP_NUM_PADS; i++) {
- framefmt = v4l2_subdev_state_get_format(state, i);
- *framefmt = format->format;
- }
+ /* Set sink format. */
+ fmt = v4l2_subdev_state_get_format(state, format->pad, format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ /* Propagate the format to the source pad. */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
+ format->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = format->format;
+
+ return 0;
+}
+
+static int risp_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
+ V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
+ if (ret)
+ return ret;
+
+ ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing,
+ &risp_default_fmt);
+ if (ret)
+ return ret;
return 0;
}
@@ -356,12 +462,39 @@ static const struct v4l2_subdev_pad_ops risp_pad_ops = {
.set_fmt = risp_set_pad_format,
.get_fmt = v4l2_subdev_get_fmt,
.link_validate = v4l2_subdev_link_validate_default,
+ .set_routing = risp_set_routing,
};
static const struct v4l2_subdev_ops rcar_isp_subdev_ops = {
.pad = &risp_pad_ops,
};
+static int risp_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ static struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = RCAR_ISP_SINK,
+ .sink_stream = 0,
+ .source_pad = RCAR_ISP_PORT0,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ static const struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
+ &risp_default_fmt);
+}
+
+static const struct v4l2_subdev_internal_ops risp_internal_ops = {
+ .init_state = risp_init_state,
+};
+
/* -----------------------------------------------------------------------------
* Async handling and registration of subdevices and links
*/
@@ -534,11 +667,12 @@ static int risp_probe(struct platform_device *pdev)
isp->subdev.owner = THIS_MODULE;
isp->subdev.dev = &pdev->dev;
+ isp->subdev.internal_ops = &risp_internal_ops;
v4l2_subdev_init(&isp->subdev, &rcar_isp_subdev_ops);
v4l2_set_subdevdata(&isp->subdev, &pdev->dev);
snprintf(isp->subdev.name, sizeof(isp->subdev.name), "%s %s",
KBUILD_MODNAME, dev_name(&pdev->dev));
- isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
+ isp->subdev.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
isp->subdev.entity.function = MEDIA_ENT_F_VID_MUX;
isp->subdev.entity.ops = &risp_entity_ops;
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-core.c b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
index c8d564aa1eba..2fcea715101c 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-core.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-core.c
@@ -673,23 +673,26 @@ static int rvin_csi2_create_link(struct rvin_group *group, unsigned int id,
struct media_entity *source = &group->remotes[route->csi].subdev->entity;
struct media_entity *sink = &group->vin[id]->vdev.entity;
struct media_pad *sink_pad = &sink->pads[0];
+ struct media_pad *source_pad;
+ unsigned int source_idx;
unsigned int channel;
- int ret;
- for (channel = 0; channel < 4; channel++) {
- unsigned int source_idx = rvin_group_csi_channel_to_pad(channel);
- struct media_pad *source_pad = &source->pads[source_idx];
+ /*
+ * The channels from CSI-2 blocks and the VIN groups have a set of
+ * hardcoded routing options to choose from. We only support the routing
+ * where all VINs in a group are connected to the same CSI-2 block,
+ * and the Nth VIN in the group is connected to the Nth CSI-2 channel.
+ */
- /* Skip if link already exists. */
- if (media_entity_find_link(source_pad, sink_pad))
- continue;
+ channel = id % 4;
+ source_idx = rvin_group_csi_channel_to_pad(channel);
+ source_pad = &source->pads[source_idx];
- ret = media_create_pad_link(source, source_idx, sink, 0, 0);
- if (ret)
- return ret;
- }
+ /* Skip if link already exists. */
+ if (media_entity_find_link(source_pad, sink_pad))
+ return 0;
- return 0;
+ return media_create_pad_link(source, source_idx, sink, 0, 0);
}
static int rvin_parallel_setup_links(struct rvin_group *group)
diff --git a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
index 73cda0e2d45a..5e515d16c164 100644
--- a/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
+++ b/drivers/media/platform/renesas/rcar-vin/rcar-dma.c
@@ -678,7 +678,7 @@ void rvin_crop_scale_comp(struct rvin_dev *vin)
/*
* VNIS_REG has four lowest bits always 0, i.e. the stride has to be
- * aligned to 16 bytes. This is done in rvin_format_bytesperline().
+ * aligned to 16 pixels. This is done in rvin_format_bytesperline().
*/
fmt = rvin_format_from_pixel(vin, vin->format.pixelformat);
diff --git a/drivers/media/platform/renesas/rcar_drif.c b/drivers/media/platform/renesas/rcar_drif.c
index 0844934f7aa6..bd67e8e16f53 100644
--- a/drivers/media/platform/renesas/rcar_drif.c
+++ b/drivers/media/platform/renesas/rcar_drif.c
@@ -464,7 +464,7 @@ rcar_drif_get_fbuf(struct rcar_drif_sdr *sdr)
rcar_drif_frame_buf, list);
if (!fbuf) {
/*
- * App is late in enqueing buffers. Samples lost & there will
+ * App is late in enqueuing buffers. Samples lost & there will
* be a gap in sequence number when app recovers
*/
rdrif_dbg(sdr, "\napp late: prod %u\n", sdr->produced);
diff --git a/drivers/media/platform/renesas/renesas-ceu.c b/drivers/media/platform/renesas/renesas-ceu.c
index 65f7659a9e02..47b9e4753301 100644
--- a/drivers/media/platform/renesas/renesas-ceu.c
+++ b/drivers/media/platform/renesas/renesas-ceu.c
@@ -841,12 +841,13 @@ static int __ceu_try_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt
* time.
*/
sd_format.format.code = mbus_code;
- ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, &pad_state, &sd_format);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &pad_state,
+ &sd_format);
if (ret) {
if (ret == -EINVAL) {
/* fallback */
sd_format.format.code = mbus_code_old;
- ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt,
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL,
&pad_state, &sd_format);
}
@@ -900,7 +901,7 @@ static int ceu_set_fmt(struct ceu_device *ceudev, struct v4l2_format *v4l2_fmt)
format.format.code = mbus_code;
v4l2_fill_mbus_format_mplane(&format.format, &v4l2_fmt->fmt.pix_mp);
- ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, &format);
+ ret = v4l2_subdev_call(v4l2_sd, pad, set_fmt, NULL, NULL, &format);
if (ret)
return ret;
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
index 798ef2916262..8c7f46ccce5f 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-core.c
@@ -360,7 +360,7 @@ static const struct rzg2l_cru_info rzg3e_cru_info = {
.max_width = 4095,
.max_height = 4095,
.image_conv = ICnIPMC_C0,
- .has_stride = true,
+ .stride_align = 128,
.regs = rzg3e_cru_regs,
.irq_handler = rzg3e_cru_irq,
.enable_interrupts = rzg3e_cru_enable_interrupts,
@@ -405,6 +405,7 @@ static const struct rzg2l_cru_info rzg2l_cru_info = {
.max_width = 2800,
.max_height = 4095,
.image_conv = ICnMC,
+ .stride_align = 1,
.regs = rzg2l_cru_regs,
.irq_handler = rzg2l_cru_irq,
.enable_interrupts = rzg2l_cru_enable_interrupts,
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
index b426bc7898bf..2c192d370dcb 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-cru.h
@@ -75,7 +75,7 @@ struct rzg2l_cru_info {
unsigned int max_height;
u16 image_conv;
const u16 *regs;
- bool has_stride;
+ u8 stride_align;
irqreturn_t (*irq_handler)(int irq, void *data);
void (*enable_interrupts)(struct rzg2l_cru_dev *cru);
void (*disable_interrupts)(struct rzg2l_cru_dev *cru);
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
index 6dc4b53607b4..c17283cba3a6 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-csi2.c
@@ -633,6 +633,7 @@ static int rzg2l_csi2_post_streamoff(struct v4l2_subdev *sd)
}
static int rzg2l_csi2_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -687,7 +688,7 @@ static int rzg2l_csi2_init_state(struct v4l2_subdev *sd,
fmt.format.quantization = V4L2_QUANTIZATION_DEFAULT;
fmt.format.xfer_func = V4L2_XFER_FUNC_DEFAULT;
- return rzg2l_csi2_set_format(sd, sd_state, &fmt);
+ return rzg2l_csi2_set_format(sd, NULL, sd_state, &fmt);
}
static int rzg2l_csi2_enum_mbus_code(struct v4l2_subdev *sd,
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
index 5f2c87858bfe..21dd0162fd6e 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-ip.c
@@ -200,6 +200,7 @@ static int rzg2l_cru_ip_s_stream(struct v4l2_subdev *sd, int enable)
}
static int rzg2l_cru_ip_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *fmt)
{
@@ -300,7 +301,7 @@ static int rzg2l_cru_ip_init_state(struct v4l2_subdev *sd,
fmt.format.quantization = V4L2_QUANTIZATION_DEFAULT;
fmt.format.xfer_func = V4L2_XFER_FUNC_DEFAULT;
- return rzg2l_cru_ip_set_format(sd, sd_state, &fmt);
+ return rzg2l_cru_ip_set_format(sd, NULL, sd_state, &fmt);
}
static const struct v4l2_subdev_video_ops rzg2l_cru_ip_video_ops = {
diff --git a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
index 91eda5034248..a7b6dce66570 100644
--- a/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
+++ b/drivers/media/platform/renesas/rzg2l-cru/rzg2l-video.c
@@ -32,7 +32,6 @@
#define RZG2L_CRU_DEFAULT_COLORSPACE V4L2_COLORSPACE_SRGB
#define RZG2L_CRU_STRIDE_MAX 32640
-#define RZG2L_CRU_STRIDE_ALIGN 128
struct rzg2l_cru_buffer {
struct vb2_v4l2_buffer vb;
@@ -277,11 +276,11 @@ static void rzg2l_cru_initialize_axi(struct rzg2l_cru_dev *cru)
rzg2l_cru_fill_hw_slot(cru, cru->num_buf - 1);
}
- if (info->has_stride) {
+ if (info->stride_align > 1) {
u32 stride = cru->format.bytesperline;
u32 amnis;
- stride /= RZG2L_CRU_STRIDE_ALIGN;
+ stride /= info->stride_align;
amnis = rzg2l_cru_read(cru, AMnIS) & ~AMnIS_IS_MASK;
rzg2l_cru_write(cru, AMnIS, amnis | AMnIS_IS(stride));
}
@@ -849,12 +848,8 @@ static void rzg2l_cru_format_align(struct rzg2l_cru_dev *cru,
v4l_bound_align_image(&pix->width, 320, info->max_width, 1,
&pix->height, 240, info->max_height, 2, 0);
- v4l2_fill_pixfmt(pix, pix->pixelformat, pix->width, pix->height);
-
- if (info->has_stride) {
- pix->bytesperline = ALIGN(pix->bytesperline, RZG2L_CRU_STRIDE_ALIGN);
- pix->sizeimage = pix->bytesperline * pix->height;
- }
+ v4l2_fill_pixfmt_aligned(pix, pix->pixelformat, pix->width, pix->height,
+ info->stride_align);
dev_dbg(cru->dev, "Format %ux%u bpl: %u size: %u\n",
pix->width, pix->height, pix->bytesperline, pix->sizeimage);
diff --git a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c
index b1659544eaa0..04e168b9526f 100644
--- a/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c
+++ b/drivers/media/platform/renesas/rzv2h-ivc/rzv2h-ivc-subdev.c
@@ -157,6 +157,7 @@ static int rzv2h_ivc_enum_frame_size(struct v4l2_subdev *sd,
}
static int rzv2h_ivc_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/renesas/sh_vou.c b/drivers/media/platform/renesas/sh_vou.c
index 4ad7ae188d5b..6d4ec80edd8f 100644
--- a/drivers/media/platform/renesas/sh_vou.c
+++ b/drivers/media/platform/renesas/sh_vou.c
@@ -714,7 +714,7 @@ static int sh_vou_set_fmt_vid_out(struct sh_vou_device *vou_dev,
mbfmt->width = geo.output.width;
mbfmt->height = geo.output.height;
ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,
- set_fmt, NULL, &format);
+ set_fmt, NULL, NULL, &format);
/* Must be implemented, so, don't check for -ENOIOCTLCMD */
if (ret < 0)
return ret;
@@ -974,11 +974,11 @@ static int sh_vou_s_selection(struct file *file, void *fh,
* final encoder configuration.
*/
v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,
- set_selection, NULL, &sd_sel);
+ set_selection, NULL, NULL, &sd_sel);
format.format.width = geo.output.width;
format.format.height = geo.output.height;
ret = v4l2_device_call_until_err(&vou_dev->v4l2_dev, 0, pad,
- set_fmt, NULL, &format);
+ set_fmt, NULL, NULL, &format);
/* Must be implemented, so, don't check for -ENOIOCTLCMD */
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_brx.c b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
index 360a42502947..a70b82e22e69 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_brx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_brx.c
@@ -124,6 +124,7 @@ static void brx_try_format(struct vsp1_brx *brx,
}
static int brx_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -165,6 +166,7 @@ static int brx_set_format(struct v4l2_subdev *subdev,
}
static int brx_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -200,6 +202,7 @@ static int brx_get_selection(struct v4l2_subdev *subdev,
}
static int brx_set_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_dl.c b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
index 6430f2ec8b32..8257759421f3 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_dl.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_dl.c
@@ -649,7 +649,7 @@ static void __vsp1_dl_list_put(struct vsp1_dl_list *dl)
dl->post_cmd = NULL;
/*
- * body0 is reused as as an optimisation as presently every display list
+ * body0 is reused as an optimisation as presently every display list
* has at least one body, thus we reinitialise the entries list.
*/
dl->body0->num_entries = 0;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_drm.c b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
index 9cd5c025d2be..adb89e699389 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_drm.c
@@ -137,7 +137,8 @@ static int vsp1_du_insert_uif(struct vsp1_device *vsp1,
format.pad = UIF_PAD_SINK;
- ret = v4l2_subdev_call(&uif->subdev, pad, set_fmt, NULL, &format);
+ ret = v4l2_subdev_call(&uif->subdev, pad, set_fmt, NULL, NULL,
+ &format);
if (ret < 0)
return ret;
@@ -184,7 +185,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
format.format.ycbcr_enc = input->ycbcr_enc;
format.format.quantization = input->quantization;
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, NULL,
&format);
if (ret < 0)
return ret;
@@ -199,6 +200,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
sel.r = input->crop;
ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_selection, NULL,
+ NULL,
&sel);
if (ret < 0)
return ret;
@@ -226,7 +228,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
- ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL,
+ ret = v4l2_subdev_call(&rpf->entity.subdev, pad, set_fmt, NULL, NULL,
&format);
if (ret < 0)
return ret;
@@ -240,7 +242,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
/* BRx sink, propagate the format from the RPF source. */
format.pad = brx_input;
- ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL,
+ ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_fmt, NULL, NULL,
&format);
if (ret < 0)
return ret;
@@ -254,6 +256,7 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
sel.r = vsp1->drm->inputs[rpf->entity.index].compose;
ret = v4l2_subdev_call(&pipe->brx->subdev, pad, set_selection, NULL,
+ NULL,
&sel);
if (ret < 0)
return ret;
@@ -387,7 +390,7 @@ static int vsp1_du_pipeline_setup_brx(struct vsp1_device *vsp1,
format.format.height = drm_pipe->height;
format.format.field = V4L2_FIELD_NONE;
- ret = v4l2_subdev_call(&brx->subdev, pad, set_fmt, NULL,
+ ret = v4l2_subdev_call(&brx->subdev, pad, set_fmt, NULL, NULL,
&format);
if (ret < 0)
return ret;
@@ -538,7 +541,8 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1,
format.format.code = MEDIA_BUS_FMT_ARGB8888_1X32;
format.format.field = V4L2_FIELD_NONE;
- ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt, NULL,
+ ret = v4l2_subdev_call(&pipe->output->entity.subdev, pad, set_fmt,
+ NULL, NULL,
&format);
if (ret < 0)
return ret;
@@ -558,7 +562,7 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1,
format.format.code, pipe->output->entity.index);
format.pad = LIF_PAD_SINK;
- ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL,
+ ret = v4l2_subdev_call(&pipe->lif->subdev, pad, set_fmt, NULL, NULL,
&format);
if (ret < 0)
return ret;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.c b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
index 26b21559878d..f7359d347947 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.c
@@ -300,6 +300,7 @@ int vsp1_subdev_enum_frame_size(struct v4l2_subdev *subdev,
* entity's limits, and propagates the sink pad format to the source pad.
*/
int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -380,7 +381,8 @@ static int vsp1_entity_init_state(struct v4l2_subdev *subdev,
: V4L2_SUBDEV_FORMAT_ACTIVE,
};
- v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &format);
+ v4l2_subdev_call(subdev, pad, set_fmt, NULL, sd_state,
+ &format);
}
return 0;
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_entity.h b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
index c0c1fe7d3e40..677a1efc104a 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_entity.h
+++ b/drivers/media/platform/renesas/vsp1/vsp1_entity.h
@@ -188,6 +188,7 @@ int vsp1_subdev_get_pad_format(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt);
int vsp1_subdev_set_pad_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt);
int vsp1_subdev_enum_mbus_code(struct v4l2_subdev *subdev,
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_histo.c b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
index 97dbfb93abe9..bd9efb42fd1f 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_histo.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_histo.c
@@ -185,6 +185,7 @@ static int histo_enum_frame_size(struct v4l2_subdev *subdev,
}
static int histo_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -306,6 +307,7 @@ static int histo_set_compose(struct v4l2_subdev *subdev,
}
static int histo_set_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -330,6 +332,7 @@ static int histo_set_selection(struct v4l2_subdev *subdev,
}
static int histo_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
index df069c228243..d734b5775af6 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_hsit.c
@@ -109,6 +109,7 @@ static int hsit_enum_frame_size(struct v4l2_subdev *subdev,
}
static int hsit_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
index ced01870acd6..ff5894265b54 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_rwpf.c
@@ -110,6 +110,7 @@ static int vsp1_rwpf_enum_frame_size(struct v4l2_subdev *subdev,
}
static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -214,6 +215,7 @@ static int vsp1_rwpf_set_format(struct v4l2_subdev *subdev,
}
static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -255,6 +257,7 @@ static int vsp1_rwpf_get_selection(struct v4l2_subdev *subdev,
}
static int vsp1_rwpf_set_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_sru.c b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
index 3fd9fde5c724..5e1cf60be311 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_sru.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_sru.c
@@ -210,6 +210,7 @@ static void sru_try_format(struct vsp1_sru *sru,
}
static int sru_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uds.c b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
index 9f7bb112929e..d129964ec14d 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uds.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uds.c
@@ -193,6 +193,7 @@ static void uds_try_format(struct vsp1_uds *uds,
}
static int uds_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_uif.c b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
index 52dbfe58a70d..16ddcd594134 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_uif.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_uif.c
@@ -54,6 +54,7 @@ static const unsigned int uif_codes[] = {
};
static int uif_get_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -92,6 +93,7 @@ static int uif_get_selection(struct v4l2_subdev *subdev,
}
static int uif_set_selection(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/renesas/vsp1/vsp1_vspx.c b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
index 1673479be0ff..5c39bacfb13f 100644
--- a/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
+++ b/drivers/media/platform/renesas/vsp1/vsp1_vspx.c
@@ -120,7 +120,8 @@ static int vsp1_vspx_rwpf_set_subdev_fmt(struct vsp1_device *vsp1,
format.format.field = V4L2_FIELD_NONE;
format.format.code = rwpf->fmtinfo->mbus;
- return v4l2_subdev_call(&ent->subdev, pad, set_fmt, NULL, &format);
+ return v4l2_subdev_call(&ent->subdev, pad, set_fmt, NULL, NULL,
+ &format);
}
/* Configure the RPF->IIF->WPF pipeline for ConfigDMA or RAW image transfer. */
diff --git a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
index 414a9980cf2e..2cba42d7c5d9 100644
--- a/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
+++ b/drivers/media/platform/rockchip/rkcif/rkcif-interface.c
@@ -25,6 +25,7 @@ static const struct media_entity_operations rkcif_interface_media_ops = {
};
static int rkcif_interface_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -77,6 +78,7 @@ static int rkcif_interface_set_fmt(struct v4l2_subdev *sd,
}
static int rkcif_interface_get_sel(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -115,6 +117,7 @@ static int rkcif_interface_get_sel(struct v4l2_subdev *sd,
}
static int rkcif_interface_set_sel(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
index ddc6182f3e4b..9fa8a0407c90 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-csi.c
@@ -304,6 +304,7 @@ static int rkisp1_csi_init_state(struct v4l2_subdev *sd,
}
static int rkisp1_csi_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
index 1791c02a40ae..f90e01301943 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-dev.c
@@ -187,7 +187,6 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
{
struct v4l2_async_notifier *ntf = &rkisp1->notifier;
struct fwnode_handle *fwnode = dev_fwnode(rkisp1->dev);
- struct fwnode_handle *ep;
unsigned int index = 0;
int ret = 0;
@@ -195,7 +194,7 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
ntf->ops = &rkisp1_subdev_notifier_ops;
- fwnode_graph_for_each_endpoint(fwnode, ep) {
+ fwnode_graph_for_each_endpoint_scoped(fwnode, ep) {
struct fwnode_handle *port;
struct v4l2_fwnode_endpoint vep = { };
struct rkisp1_sensor_async *rk_asd;
@@ -286,7 +285,6 @@ static int rkisp1_subdev_notifier_register(struct rkisp1_device *rkisp1)
}
if (ret) {
- fwnode_handle_put(ep);
v4l2_async_nf_cleanup(ntf);
return ret;
}
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
index 2311672cedb1..4043a75ca3f0 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-isp.c
@@ -817,6 +817,7 @@ static void rkisp1_isp_set_sink_fmt(struct rkisp1_isp *isp,
}
static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -834,6 +835,7 @@ static int rkisp1_isp_set_fmt(struct v4l2_subdev *sd,
}
static int rkisp1_isp_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -872,6 +874,7 @@ static int rkisp1_isp_get_selection(struct v4l2_subdev *sd,
}
static int rkisp1_isp_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
index 8e6b753d3081..5926917434af 100644
--- a/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
+++ b/drivers/media/platform/rockchip/rkisp1/rkisp1-resizer.c
@@ -543,6 +543,7 @@ static void rkisp1_rsz_set_sink_fmt(struct rkisp1_resizer *rsz,
}
static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -558,6 +559,7 @@ static int rkisp1_rsz_set_fmt(struct v4l2_subdev *sd,
}
static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -591,6 +593,7 @@ static int rkisp1_rsz_get_selection(struct v4l2_subdev *sd,
}
static int rkisp1_rsz_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
index d85811f4b8c5..8d7e7b190532 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-capture.c
@@ -796,7 +796,8 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
sd = media_entity_to_v4l2_subdev(me);
sfmt.pad = 0;
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, NULL,
+ &sfmt);
if (ret)
return ret;
@@ -804,7 +805,7 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx,
sfmt.pad = me->num_pads - 1;
mf->code = tfmt->code;
ret = v4l2_subdev_call(sd, pad, set_fmt, NULL,
- &sfmt);
+ NULL, &sfmt);
if (ret)
return ret;
}
@@ -1509,6 +1510,7 @@ static int fimc_subdev_get_fmt(struct v4l2_subdev *sd,
}
static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1575,6 +1577,7 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd,
}
static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1631,6 +1634,7 @@ static int fimc_subdev_get_selection(struct v4l2_subdev *sd,
}
static int fimc_subdev_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c
index 3c5d7bee2655..a0a9093081dd 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-isp.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-isp.c
@@ -191,6 +191,7 @@ static void __isp_subdev_try_format(struct fimc_isp *isp,
}
static int fimc_isp_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
index 8be20fd32d1c..1b7bae15189b 100644
--- a/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
+++ b/drivers/media/platform/samsung/exynos4-is/fimc-lite.c
@@ -1052,6 +1052,7 @@ static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd,
}
static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1113,6 +1114,7 @@ static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd,
}
static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1148,6 +1150,7 @@ static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd,
}
static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
index 452880b5350c..da6e67a79c09 100644
--- a/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
+++ b/drivers/media/platform/samsung/exynos4-is/mipi-csis.c
@@ -575,6 +575,7 @@ static struct v4l2_mbus_framefmt *__s5pcsis_get_format(
}
static int s5pcsis_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/samsung/s3c-camif/camif-capture.c b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
index ed1a1d693293..98dfd394a1aa 100644
--- a/drivers/media/platform/samsung/s3c-camif/camif-capture.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-capture.c
@@ -1275,6 +1275,7 @@ static void __camif_subdev_try_format(struct camif_dev *camif,
}
static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1342,6 +1343,7 @@ static int s3c_camif_subdev_set_fmt(struct v4l2_subdev *sd,
}
static int s3c_camif_subdev_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1429,6 +1431,7 @@ static void __camif_try_crop(struct camif_dev *camif, struct v4l2_rect *r)
}
static int s3c_camif_subdev_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/platform/samsung/s3c-camif/camif-core.c b/drivers/media/platform/samsung/s3c-camif/camif-core.c
index bb06847f3a63..2ff71e375efe 100644
--- a/drivers/media/platform/samsung/s3c-camif/camif-core.c
+++ b/drivers/media/platform/samsung/s3c-camif/camif-core.c
@@ -227,7 +227,7 @@ static int camif_register_sensor(struct camif_dev *camif)
return 0;
format.pad = CAMIF_SD_PAD_SINK;
- v4l2_subdev_call(&camif->subdev, pad, set_fmt, NULL, &format);
+ v4l2_subdev_call(&camif->subdev, pad, set_fmt, NULL, NULL, &format);
v4l2_info(sd, "Initial format from sensor: %dx%d, %#x\n",
format.format.width, format.format.height,
diff --git a/drivers/media/platform/st/stm32/stm32-csi.c b/drivers/media/platform/st/stm32/stm32-csi.c
index fd2b6dfbd44c..e71142634a17 100644
--- a/drivers/media/platform/st/stm32/stm32-csi.c
+++ b/drivers/media/platform/st/stm32/stm32-csi.c
@@ -736,6 +736,7 @@ static int stm32_csi_enum_mbus_code(struct v4l2_subdev *sd,
}
static int stm32_csi_set_pad_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
@@ -825,7 +826,7 @@ static int stm32_csi_async_bound(struct v4l2_async_notifier *notifier,
int remote_pad;
remote_pad = media_entity_get_fwnode_pad(&s_subdev->entity,
- s_subdev->fwnode,
+ asd->match.fwnode,
MEDIA_PAD_FL_SOURCE);
if (remote_pad < 0) {
dev_err(csidev->dev, "Couldn't find output pad for subdev %s\n",
@@ -1059,6 +1060,7 @@ static int stm32_csi_probe(struct platform_device *pdev)
return 0;
err_cleanup:
+ v4l2_async_nf_unregister(&csidev->notifier);
v4l2_async_nf_cleanup(&csidev->notifier);
return ret;
}
@@ -1067,6 +1069,8 @@ static void stm32_csi_remove(struct platform_device *pdev)
{
struct stm32_csi_dev *csidev = platform_get_drvdata(pdev);
+ v4l2_async_nf_unregister(&csidev->notifier);
+ v4l2_async_nf_cleanup(&csidev->notifier);
v4l2_async_unregister_subdev(&csidev->sd);
pm_runtime_disable(&pdev->dev);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmi.c b/drivers/media/platform/st/stm32/stm32-dcmi.c
index aa79fe60ff0c..3d774cb4907b 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmi.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmi.c
@@ -738,7 +738,7 @@ static int dcmi_pipeline_s_fmt(struct stm32_dcmi *dcmi,
format->format.width, format->format.height);
fmt.pad = pad->index;
- ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, &fmt);
+ ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, NULL, &fmt);
if (ret < 0) {
dev_err(dcmi->dev, "%s: Failed to set format 0x%x %ux%u on \"%s\":%d pad (%d)\n",
__func__, format->format.code,
@@ -1022,6 +1022,27 @@ static void __find_outer_frame_size(struct stm32_dcmi *dcmi,
*framesize = *match;
}
+static int dcmi_source_call_try_state_set_fmt(struct v4l2_subdev *source,
+ struct v4l2_subdev_format *fmt)
+{
+ static struct lock_class_key lock_key;
+ const char *lock_name = KBUILD_BASENAME ":" __stringify(__LINE__)
+ ":state->lock";
+ struct v4l2_subdev_state *source_state;
+ int ret;
+
+ source_state = __v4l2_subdev_state_alloc(source, lock_name, &lock_key);
+ if (IS_ERR(source_state))
+ return PTR_ERR(source_state);
+
+ v4l2_subdev_lock_state(source_state);
+ ret = v4l2_subdev_call(source, pad, set_fmt, NULL, source_state, fmt);
+ v4l2_subdev_unlock_state(source_state);
+ __v4l2_subdev_state_free(source_state);
+
+ return ret;
+}
+
static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
const struct dcmi_format **sd_format,
struct dcmi_framesize *sd_framesize)
@@ -1063,8 +1084,8 @@ static int dcmi_try_fmt(struct stm32_dcmi *dcmi, struct v4l2_format *f,
}
v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
- ret = v4l2_subdev_call_state_try(dcmi->source, pad, set_fmt, &format);
- if (ret < 0)
+ ret = dcmi_source_call_try_state_set_fmt(dcmi->source, &format);
+ if (ret)
return ret;
/* Update pix regarding to what sensor can do */
@@ -1224,7 +1245,7 @@ static int dcmi_set_sensor_format(struct stm32_dcmi *dcmi,
}
v4l2_fill_mbus_format(&format.format, pix, sd_fmt->mbus_code);
- ret = v4l2_subdev_call_state_try(dcmi->source, pad, set_fmt, &format);
+ ret = dcmi_source_call_try_state_set_fmt(dcmi->source, &format);
if (ret < 0)
return ret;
@@ -1246,7 +1267,7 @@ static int dcmi_get_sensor_bounds(struct stm32_dcmi *dcmi,
/*
* Get sensor bounds first
*/
- ret = v4l2_subdev_call(dcmi->source, pad, get_selection,
+ ret = v4l2_subdev_call(dcmi->source, pad, get_selection, NULL,
NULL, &bounds);
if (!ret)
*r = bounds.r;
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
index 159105fb40b8..e35d45a0aca2 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/Makefile
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-bytecap.o
+stm32-dcmipp-y := dcmipp-core.o dcmipp-common.o dcmipp-input.o dcmipp-byteproc.o dcmipp-capture.o
+stm32-dcmipp-y += dcmipp-pixelcommon.o dcmipp-isp.o dcmipp-pixelproc.o
obj-$(CONFIG_VIDEO_STM32_DCMIPP) += stm32-dcmipp.o
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
index 0c7aeb0888f6..346cfddcf229 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-byteproc.c
@@ -25,6 +25,7 @@
#define DCMIPP_P0SCSZR_HSIZE_SHIFT 0
#define DCMIPP_P0SCSZR_VSIZE_SHIFT 16
#define DCMIPP_P0PPCR 0x5c0
+#define DCMIPP_P0PPCR_SWAPYUV BIT(0)
#define DCMIPP_P0PPCR_BSM_1_2 0x1
#define DCMIPP_P0PPCR_BSM_1_4 0x2
#define DCMIPP_P0PPCR_BSM_2_4 0x3
@@ -264,6 +265,7 @@ dcmipp_byteproc_enum_frame_size(struct v4l2_subdev *sd,
}
static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -298,6 +300,7 @@ static int dcmipp_byteproc_set_fmt(struct v4l2_subdev *sd,
}
static int dcmipp_byteproc_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *s)
{
@@ -351,6 +354,7 @@ static int dcmipp_byteproc_get_selection(struct v4l2_subdev *sd,
}
static int dcmipp_byteproc_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *s)
{
@@ -428,9 +432,11 @@ static int dcmipp_byteproc_configure_scale_crop
if (!vpix)
return -EINVAL;
- /* clear decimation/crop */
+ /* clear decimation/crop/swap yuv */
reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_BSM_MASK);
reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_LSM);
+ if (byteproc->ved.dcmipp->pipe_cfg->has_swapyuv)
+ reg_clear(byteproc, DCMIPP_P0PPCR, DCMIPP_P0PPCR_SWAPYUV);
reg_write(byteproc, DCMIPP_P0SCSTR, 0);
reg_write(byteproc, DCMIPP_P0SCSZR, 0);
@@ -451,6 +457,17 @@ static int dcmipp_byteproc_configure_scale_crop
if (vprediv == 2)
val |= DCMIPP_P0PPCR_LSM | DCMIPP_P0PPCR_OELS;
+ /*
+ * Perform a SWAP YUV if input is parallel since in this mode
+ * the DCMIPP will swap YUV by default
+ */
+ if (byteproc->ved.dcmipp->pipe_cfg->has_swapyuv &&
+ (sink_fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ sink_fmt->code == MEDIA_BUS_FMT_YVYU8_2X8 ||
+ sink_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+ sink_fmt->code == MEDIA_BUS_FMT_VYUY8_2X8))
+ val |= DCMIPP_P0PPCR_SWAPYUV;
+
/* decimate using bytes and lines skipping */
if (val) {
reg_set(byteproc, DCMIPP_P0PPCR, val);
@@ -571,8 +588,8 @@ void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved)
}
struct dcmipp_ent_device *
-dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
- struct v4l2_device *v4l2_dev, void __iomem *regs)
+dcmipp_byteproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
{
struct dcmipp_byteproc_device *byteproc;
const unsigned long pads_flag[] = {
@@ -585,11 +602,11 @@ dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
if (!byteproc)
return ERR_PTR(-ENOMEM);
- byteproc->regs = regs;
+ byteproc->regs = dcmipp->regs;
/* Initialize ved and sd */
ret = dcmipp_ent_sd_register(&byteproc->ved, &byteproc->sd,
- v4l2_dev, entity_name,
+ &dcmipp->v4l2_dev, entity_name,
MEDIA_ENT_F_PROC_VIDEO_SCALER,
ARRAY_SIZE(pads_flag), pads_flag,
&dcmipp_byteproc_int_ops,
@@ -600,7 +617,8 @@ dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
return ERR_PTR(ret);
}
- byteproc->dev = dev;
+ byteproc->ved.dcmipp = dcmipp;
+ byteproc->dev = dcmipp->dev;
return &byteproc->ved;
}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c
index f0e809458489..fe7767f908b7 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-bytecap.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-capture.c
@@ -25,27 +25,81 @@
#define DCMIPP_CMIER_P0ALL (DCMIPP_CMIER_P0VSYNCIE |\
DCMIPP_CMIER_P0FRAMEIE |\
DCMIPP_CMIER_P0OVRIE)
+#define DCMIPP_CMIER_P1FRAMEIE BIT(17)
+#define DCMIPP_CMIER_P1VSYNCIE BIT(18)
+#define DCMIPP_CMIER_P1OVRIE BIT(23)
+#define DCMIPP_CMIER_P1ALL (DCMIPP_CMIER_P1VSYNCIE |\
+ DCMIPP_CMIER_P1FRAMEIE |\
+ DCMIPP_CMIER_P1OVRIE)
+#define DCMIPP_CMIER_P2FRAMEIE BIT(25)
+#define DCMIPP_CMIER_P2VSYNCIE BIT(26)
+#define DCMIPP_CMIER_P2OVRIE BIT(31)
+#define DCMIPP_CMIER_P2ALL (DCMIPP_CMIER_P2VSYNCIE |\
+ DCMIPP_CMIER_P2FRAMEIE |\
+ DCMIPP_CMIER_P2OVRIE)
+#define DCMIPP_CMIER_PxALL(id) (((id) == 0) ? DCMIPP_CMIER_P0ALL : \
+ (((id) == 1) ? DCMIPP_CMIER_P1ALL : \
+ DCMIPP_CMIER_P2ALL))
#define DCMIPP_CMSR1 0x3f4
#define DCMIPP_CMSR2 0x3f8
#define DCMIPP_CMSR2_P0FRAMEF BIT(9)
#define DCMIPP_CMSR2_P0VSYNCF BIT(10)
#define DCMIPP_CMSR2_P0OVRF BIT(15)
+#define DCMIPP_CMSR2_P1FRAMEF BIT(17)
+#define DCMIPP_CMSR2_P1VSYNCF BIT(18)
+#define DCMIPP_CMSR2_P1OVRF BIT(23)
+#define DCMIPP_CMSR2_P2FRAMEF BIT(25)
+#define DCMIPP_CMSR2_P2VSYNCF BIT(26)
+#define DCMIPP_CMSR2_P2OVRF BIT(31)
+#define DCMIPP_CMSR2_PxFRAMEF(id) (((id) == 0) ? DCMIPP_CMSR2_P0FRAMEF :\
+ (((id) == 1) ? DCMIPP_CMSR2_P1FRAMEF :\
+ DCMIPP_CMSR2_P2FRAMEF))
+#define DCMIPP_CMSR2_PxVSYNCF(id) (((id) == 0) ? DCMIPP_CMSR2_P0VSYNCF :\
+ (((id) == 1) ? DCMIPP_CMSR2_P1VSYNCF :\
+ DCMIPP_CMSR2_P2VSYNCF))
+#define DCMIPP_CMSR2_PxOVRF(id) (((id) == 0) ? DCMIPP_CMSR2_P0OVRF :\
+ (((id) == 1) ? DCMIPP_CMSR2_P1OVRF :\
+ DCMIPP_CMSR2_P2OVRF))
#define DCMIPP_CMFCR 0x3fc
-#define DCMIPP_P0FSCR 0x404
-#define DCMIPP_P0FSCR_PIPEN BIT(31)
-#define DCMIPP_P0FCTCR 0x500
-#define DCMIPP_P0FCTCR_CPTREQ BIT(3)
+#define DCMIPP_PxFSCR(id) (0x404 + ((id) * 0x400))
+#define DCMIPP_PxFSCR_PIPEN BIT(31)
+#define DCMIPP_PxFCTCR(id) (0x500 + ((id) * 0x400))
+#define DCMIPP_PxFCTCR_CPTREQ BIT(3)
#define DCMIPP_P0DCCNTR 0x5b0
#define DCMIPP_P0DCLMTR 0x5b4
#define DCMIPP_P0DCLMTR_ENABLE BIT(31)
#define DCMIPP_P0DCLMTR_LIMIT_MASK GENMASK(23, 0)
-#define DCMIPP_P0PPM0AR1 0x5c4
-#define DCMIPP_P0SR 0x5f8
-#define DCMIPP_P0SR_CPTACT BIT(23)
-struct dcmipp_bytecap_pix_map {
+#define DCMIPP_PxPPM0AR1(id) (0x5c4 + ((id) * 0x400))
+#define DCMIPP_PxPPM0PR(id) (0x9cc + (((id) - 1) * 0x400))
+#define DCMIPP_P1PPM1AR1 0x9d4
+#define DCMIPP_P1PPM1PR 0x9dc
+#define DCMIPP_P1PPM2AR1 0x9e4
+
+#define DCMIPP_PxSR(id) (0x5f8 + ((id) * 0x400))
+#define DCMIPP_PxSR_CPTACT BIT(23)
+
+#define DCMIPP_PxPPCR(id) (0x9c0 + (((id) - 1) * 0x400))
+#define DCMIPP_PxPPCR_FORMAT_RGB888 0x0
+#define DCMIPP_PxPPCR_FORMAT_RGB565 0x1
+#define DCMIPP_PxPPCR_FORMAT_ARGB8888 0x2
+#define DCMIPP_PxPPCR_FORMAT_RGBA8888 0x3
+#define DCMIPP_PxPPCR_FORMAT_Y8 0x4
+#define DCMIPP_PxPPCR_FORMAT_YUV444 0x5
+#define DCMIPP_PxPPCR_FORMAT_YUYV 0x6
+#define DCMIPP_P1PPCR_FORMAT_NV61 0x7
+#define DCMIPP_P1PPCR_FORMAT_NV21 0x8
+#define DCMIPP_P1PPCR_FORMAT_YV12 0x9
+#define DCMIPP_PxPPCR_FORMAT_UYVY 0xa
+
+#define DCMIPP_PxPPCR_SWAPRB BIT(4)
+
+struct dcmipp_capture_pix_map {
unsigned int code;
u32 pixelformat;
+ u32 plane_nb;
+ unsigned int ppcr_fmt;
+ unsigned int swap_uv;
};
#define PIXMAP_MBUS_PFMT(mbus, fmt) \
@@ -54,7 +108,7 @@ struct dcmipp_bytecap_pix_map {
.pixelformat = V4L2_PIX_FMT_##fmt \
}
-static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = {
+static const struct dcmipp_capture_pix_map dcmipp_capture_dump_pix_map_list[] = {
PIXMAP_MBUS_PFMT(RGB565_2X8_LE, RGB565),
PIXMAP_MBUS_PFMT(RGB565_1X16, RGB565),
PIXMAP_MBUS_PFMT(RGB888_1X24, RGB24),
@@ -89,34 +143,51 @@ static const struct dcmipp_bytecap_pix_map dcmipp_bytecap_pix_map_list[] = {
PIXMAP_MBUS_PFMT(JPEG_1X8, JPEG),
};
-static const struct dcmipp_bytecap_pix_map *
-dcmipp_bytecap_pix_map_by_pixelformat(u32 pixelformat)
-{
- unsigned int i;
-
- for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
- if (dcmipp_bytecap_pix_map_list[i].pixelformat == pixelformat)
- return &dcmipp_bytecap_pix_map_list[i];
+#define PIXMAP_MBUS_PIXEL_PFMT(mbus, fmt, nb_plane, pp_code, swap) \
+ { \
+ .code = MEDIA_BUS_FMT_##mbus, \
+ .pixelformat = V4L2_PIX_FMT_##fmt, \
+ .plane_nb = nb_plane, \
+ .ppcr_fmt = pp_code, \
+ .swap_uv = swap, \
}
- return NULL;
-}
+static const struct dcmipp_capture_pix_map dcmipp_capture_pixel_pix_map_list[] = {
+ /* Coplanar formats are supported on main & aux pipe */
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, RGB565, 1, DCMIPP_PxPPCR_FORMAT_RGB565, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YUYV, 1, DCMIPP_PxPPCR_FORMAT_YUYV, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YVYU, 1, DCMIPP_PxPPCR_FORMAT_YUYV, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, UYVY, 1, DCMIPP_PxPPCR_FORMAT_UYVY, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, VYUY, 1, DCMIPP_PxPPCR_FORMAT_UYVY, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, GREY, 1, DCMIPP_PxPPCR_FORMAT_Y8, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, RGB24, 1, DCMIPP_PxPPCR_FORMAT_RGB888, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, BGR24, 1, DCMIPP_PxPPCR_FORMAT_RGB888, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, ARGB32, 1, DCMIPP_PxPPCR_FORMAT_RGBA8888, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, ABGR32, 1, DCMIPP_PxPPCR_FORMAT_ARGB8888, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, RGBA32, 1, DCMIPP_PxPPCR_FORMAT_ARGB8888, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(RGB888_1X24, BGRA32, 1, DCMIPP_PxPPCR_FORMAT_RGBA8888, 0),
+
+ /* Semiplanar & planar formats (plane_nb > 1) are only supported on main pipe */
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV12, 2, DCMIPP_P1PPCR_FORMAT_NV21, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV21, 2, DCMIPP_P1PPCR_FORMAT_NV21, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV16, 2, DCMIPP_P1PPCR_FORMAT_NV61, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, NV61, 2, DCMIPP_P1PPCR_FORMAT_NV61, 1),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YUV420, 3, DCMIPP_P1PPCR_FORMAT_YV12, 0),
+ PIXMAP_MBUS_PIXEL_PFMT(YUV8_1X24, YVU420, 3, DCMIPP_P1PPCR_FORMAT_YV12, 1),
+};
struct dcmipp_buf {
struct vb2_v4l2_buffer vb;
bool prepared;
dma_addr_t addr;
size_t size;
+ dma_addr_t addrs[3];
+ u32 strides[3];
+ u64 sizes[3];
struct list_head list;
};
-enum dcmipp_state {
- DCMIPP_STOPPED = 0,
- DCMIPP_WAIT_FOR_BUFFER,
- DCMIPP_RUNNING,
-};
-
-struct dcmipp_bytecap_device {
+struct dcmipp_capture_device {
struct dcmipp_ent_device ved;
struct video_device vdev;
struct device *dev;
@@ -131,7 +202,6 @@ struct dcmipp_bytecap_device {
/* mutex used as vdev and queue lock */
struct mutex lock;
u32 sequence;
- struct media_pipeline pipe;
struct v4l2_subdev *s_subdev;
u32 s_subdev_pad_nb;
@@ -147,6 +217,11 @@ struct dcmipp_bytecap_device {
void __iomem *regs;
+ int pipe_id;
+
+ const struct dcmipp_capture_pix_map *pix_map;
+ unsigned int pix_map_array_size;
+
u32 cmsr2;
struct {
@@ -162,6 +237,30 @@ struct dcmipp_bytecap_device {
} count;
};
+static const struct dcmipp_capture_pix_map *
+dcmipp_capture_pix_map_by_pixelformat(struct dcmipp_capture_device *vcap,
+ u32 pixelformat)
+{
+ for (unsigned int i = 0; i < vcap->pix_map_array_size; i++) {
+ if (vcap->pix_map[i].pixelformat == pixelformat)
+ return &vcap->pix_map[i];
+ }
+
+ return NULL;
+}
+
+static bool dcmipp_capture_is_format_valid(struct dcmipp_capture_device *vcap,
+ unsigned int pixelformat)
+{
+ const struct dcmipp_capture_pix_map *vpix =
+ dcmipp_capture_pix_map_by_pixelformat(vcap, pixelformat);
+
+ if (!vpix || (vpix->plane_nb > 1 && vcap->pipe_id != 1))
+ return false;
+
+ return true;
+}
+
static const struct v4l2_pix_format fmt_default = {
.width = DCMIPP_FMT_WIDTH_DEFAULT,
.height = DCMIPP_FMT_HEIGHT_DEFAULT,
@@ -175,7 +274,74 @@ static const struct v4l2_pix_format fmt_default = {
.xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
};
-static int dcmipp_bytecap_querycap(struct file *file, void *priv,
+static inline int hdw_pixel_alignment(u32 format)
+{
+ /* 16 bytes alignment required by hardware */
+ switch (format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_YVU420:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ case V4L2_PIX_FMT_GREY:
+ return 4;/* 2^4 = 16 pixels = 16 bytes */
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ return 3;/* 2^3 = 8 pixels = 16 bytes */
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ return 4;/* 2^4 = 16 pixels = 48 bytes */
+ case V4L2_PIX_FMT_ARGB32:
+ case V4L2_PIX_FMT_ABGR32:
+ case V4L2_PIX_FMT_RGBA32:
+ case V4L2_PIX_FMT_BGRA32:
+ return 2;/* 2^2 = 4 pixels = 16 bytes */
+ default:
+ return 0;
+ }
+}
+
+static inline int frame_planes(dma_addr_t base_addr, dma_addr_t addrs[],
+ u32 strides[], u64 sizes[],
+ u32 width, u32 height, u32 format)
+{
+ const struct v4l2_format_info *info;
+
+ /* Only used by dump pipe hence addrs[0] is enough */
+ if (format == V4L2_PIX_FMT_JPEG) {
+ addrs[0] = base_addr;
+ return 0;
+ }
+
+ info = v4l2_format_info(format);
+ if (!info)
+ return -EINVAL;
+
+ /* Fill-in each plane information */
+ addrs[0] = base_addr;
+ strides[0] = width * info->bpp[0];
+ sizes[0] = strides[0] * height;
+
+ if (info->comp_planes > 1) {
+ addrs[1] = addrs[0] + sizes[0];
+ strides[1] = width * info->bpp[1] / info->hdiv;
+ sizes[1] = strides[1] * height / info->vdiv;
+ }
+
+ if (info->comp_planes > 2) {
+ addrs[2] = addrs[1] + sizes[1];
+ strides[2] = width * info->bpp[2] / info->hdiv;
+ sizes[2] = strides[2] * height / info->vdiv;
+ }
+
+ return 0;
+}
+
+static int dcmipp_capture_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strscpy(cap->driver, DCMIPP_PDEV_NAME, sizeof(cap->driver));
@@ -184,36 +350,39 @@ static int dcmipp_bytecap_querycap(struct file *file, void *priv,
return 0;
}
-static int dcmipp_bytecap_g_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
f->fmt.pix = vcap->format;
return 0;
}
-static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
struct v4l2_pix_format *format = &f->fmt.pix;
- const struct dcmipp_bytecap_pix_map *vpix;
u32 in_w, in_h;
/* Don't accept a pixelformat that is not on the table */
- vpix = dcmipp_bytecap_pix_map_by_pixelformat(format->pixelformat);
- if (!vpix)
+ if (!dcmipp_capture_is_format_valid(vcap, format->pixelformat))
format->pixelformat = fmt_default.pixelformat;
/* Adjust width & height */
in_w = format->width;
in_h = format->height;
- v4l_bound_align_image(&format->width, DCMIPP_FRAME_MIN_WIDTH,
- DCMIPP_FRAME_MAX_WIDTH, 0, &format->height,
- DCMIPP_FRAME_MIN_HEIGHT, DCMIPP_FRAME_MAX_HEIGHT,
- 0, 0);
+ format->width = clamp_t(u32, format->width, DCMIPP_FRAME_MIN_WIDTH,
+ vcap->pipe_id != 0 ?
+ DCMIPP_FRAME_MAX_WIDTH : DCMIPP_FRAME_MAX_WIDTH);
+ if (vcap->pipe_id != 0)
+ format->width = round_up(format->width,
+ 1 << hdw_pixel_alignment(format->pixelformat));
+ format->height = clamp_t(u32, format->height, DCMIPP_FRAME_MIN_HEIGHT,
+ vcap->pipe_id != 0 ?
+ DCMIPP_FRAME_MAX_HEIGHT : DCMIPP_FRAME_MAX_HEIGHT);
if (format->width != in_w || format->height != in_h)
dev_dbg(vcap->dev, "resolution updated: %dx%d -> %dx%d\n",
in_w, in_h, format->width, format->height);
@@ -234,17 +403,17 @@ static int dcmipp_bytecap_try_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int dcmipp_bytecap_s_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct dcmipp_bytecap_device *vcap = video_drvdata(file);
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
int ret;
/* Do not change the format while stream is on */
if (vb2_is_busy(&vcap->queue))
return -EBUSY;
- ret = dcmipp_bytecap_try_fmt_vid_cap(file, priv, f);
+ ret = dcmipp_capture_try_fmt_vid_cap(file, priv, f);
if (ret)
return ret;
@@ -266,10 +435,10 @@ static int dcmipp_bytecap_s_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
+static int dcmipp_capture_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- const struct dcmipp_bytecap_pix_map *vpix;
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
unsigned int index = f->index;
unsigned int i, prev_pixelformat = 0;
@@ -278,17 +447,20 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
* care of removing duplicated entries (due to support of both
* parallel & csi 16 bits formats
*/
- for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
- vpix = &dcmipp_bytecap_pix_map_list[i];
+ for (i = 0; i < vcap->pix_map_array_size; i++) {
+ /* Only main pipe supports (Semi)-planar formats */
+ if (vcap->pipe_id != 1 && vcap->pix_map[i].plane_nb > 1)
+ continue;
+
/* Skip formats not matching requested mbus code */
- if (f->mbus_code && vpix->code != f->mbus_code)
+ if (f->mbus_code && vcap->pix_map[i].code != f->mbus_code)
continue;
/* Skip duplicated pixelformat */
- if (vpix->pixelformat == prev_pixelformat)
+ if (vcap->pix_map[i].pixelformat == prev_pixelformat)
continue;
- prev_pixelformat = vpix->pixelformat;
+ prev_pixelformat = vcap->pix_map[i].pixelformat;
if (index == 0)
break;
@@ -296,25 +468,25 @@ static int dcmipp_bytecap_enum_fmt_vid_cap(struct file *file, void *priv,
index--;
}
- if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list))
+ if (i == vcap->pix_map_array_size)
return -EINVAL;
- f->pixelformat = vpix->pixelformat;
+ f->pixelformat = vcap->pix_map[i].pixelformat;
return 0;
}
-static int dcmipp_bytecap_enum_framesizes(struct file *file, void *fh,
+static int dcmipp_capture_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
- const struct dcmipp_bytecap_pix_map *vpix;
+ struct dcmipp_capture_device *vcap = video_drvdata(file);
+
if (fsize->index)
return -EINVAL;
/* Only accept code in the pix map table */
- vpix = dcmipp_bytecap_pix_map_by_pixelformat(fsize->pixel_format);
- if (!vpix)
+ if (!dcmipp_capture_is_format_valid(vcap, fsize->pixel_format))
return -EINVAL;
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
@@ -328,7 +500,7 @@ static int dcmipp_bytecap_enum_framesizes(struct file *file, void *fh,
return 0;
}
-static const struct v4l2_file_operations dcmipp_bytecap_fops = {
+static const struct v4l2_file_operations dcmipp_capture_fops = {
.owner = THIS_MODULE,
.open = v4l2_fh_open,
.release = vb2_fop_release,
@@ -338,14 +510,14 @@ static const struct v4l2_file_operations dcmipp_bytecap_fops = {
.mmap = vb2_fop_mmap,
};
-static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = {
- .vidioc_querycap = dcmipp_bytecap_querycap,
+static const struct v4l2_ioctl_ops dcmipp_capture_ioctl_ops = {
+ .vidioc_querycap = dcmipp_capture_querycap,
- .vidioc_g_fmt_vid_cap = dcmipp_bytecap_g_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = dcmipp_bytecap_s_fmt_vid_cap,
- .vidioc_try_fmt_vid_cap = dcmipp_bytecap_try_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = dcmipp_bytecap_enum_fmt_vid_cap,
- .vidioc_enum_framesizes = dcmipp_bytecap_enum_framesizes,
+ .vidioc_g_fmt_vid_cap = dcmipp_capture_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = dcmipp_capture_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = dcmipp_capture_try_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = dcmipp_capture_enum_fmt_vid_cap,
+ .vidioc_enum_framesizes = dcmipp_capture_enum_framesizes,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
@@ -358,21 +530,34 @@ static const struct v4l2_ioctl_ops dcmipp_bytecap_ioctl_ops = {
.vidioc_streamoff = vb2_ioctl_streamoff,
};
-static void dcmipp_start_capture(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_start_capture(struct dcmipp_capture_device *vcap,
struct dcmipp_buf *buf)
{
/* Set buffer address */
- reg_write(vcap, DCMIPP_P0PPM0AR1, buf->addr);
+ reg_write(vcap, DCMIPP_PxPPM0AR1(vcap->pipe_id), buf->addrs[0]);
- /* Set buffer size */
- reg_write(vcap, DCMIPP_P0DCLMTR, DCMIPP_P0DCLMTR_ENABLE |
- ((buf->size / 4) & DCMIPP_P0DCLMTR_LIMIT_MASK));
+ if (vcap->pipe_id == 0) {
+ /* Set buffer size */
+ reg_write(vcap, DCMIPP_P0DCLMTR, DCMIPP_P0DCLMTR_ENABLE |
+ ((buf->size / 4) & DCMIPP_P0DCLMTR_LIMIT_MASK));
+ } else {
+ reg_write(vcap, DCMIPP_PxPPM0PR(vcap->pipe_id),
+ buf->strides[0]);
+
+ if (buf->addrs[1]) {
+ reg_write(vcap, DCMIPP_P1PPM1AR1, buf->addrs[1]);
+ reg_write(vcap, DCMIPP_P1PPM1PR, buf->strides[1]);
+ }
+
+ if (buf->addrs[2])
+ reg_write(vcap, DCMIPP_P1PPM2AR1, buf->addrs[2]);
+ }
/* Capture request */
- reg_set(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
+ reg_set(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
}
-static void dcmipp_bytecap_all_buffers_done(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_capture_all_buffers_done(struct dcmipp_capture_device *vcap,
enum vb2_buffer_state state)
{
struct dcmipp_buf *buf, *node;
@@ -383,10 +568,10 @@ static void dcmipp_bytecap_all_buffers_done(struct dcmipp_bytecap_device *vcap,
}
}
-static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
+static int dcmipp_capture_start_streaming(struct vb2_queue *vq,
unsigned int count)
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vq);
struct media_entity *entity = &vcap->vdev.entity;
struct dcmipp_buf *buf;
struct media_pad *pad;
@@ -397,7 +582,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
/*
* Get source subdev - since link is IMMUTABLE, pointer is cached
- * within the dcmipp_bytecap_device structure
+ * within the dcmipp_capture_device structure
*/
if (!vcap->s_subdev) {
pad = media_pad_remote_pad_first(&vcap->vdev.entity.pads[0]);
@@ -416,7 +601,7 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
goto err_buffer_done;
}
- ret = media_pipeline_start(entity->pads, &vcap->pipe);
+ ret = media_pipeline_start(entity->pads, &vcap->ved.dcmipp->pipe);
if (ret) {
dev_dbg(vcap->dev, "%s: Failed to start streaming, media pipeline start error (%d)\n",
__func__, ret);
@@ -430,8 +615,25 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
spin_lock_irq(&vcap->irqlock);
+ if (vcap->pipe_id != 0) {
+ const struct dcmipp_capture_pix_map *vpix =
+ dcmipp_capture_pix_map_by_pixelformat(vcap, vcap->format.pixelformat);
+ unsigned int ppcr = 0;
+
+ /*
+ * Configure the Pixel Packer
+ * vpix is guaranteed to be valid since pixelformat is validated
+ * in dcmipp_pixelcap_s_fmt_vid_cap function before
+ */
+ ppcr = vpix->ppcr_fmt;
+ if (vpix->swap_uv)
+ ppcr |= DCMIPP_PxPPCR_SWAPRB;
+
+ reg_write(vcap, DCMIPP_PxPPCR(vcap->pipe_id), ppcr);
+ }
+
/* Enable pipe at the end of programming */
- reg_set(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN);
+ reg_set(vcap, DCMIPP_PxFSCR(vcap->pipe_id), DCMIPP_PxFSCR_PIPEN);
/*
* vb2 framework guarantee that we have at least 'min_queued_buffers'
@@ -444,7 +646,9 @@ static int dcmipp_bytecap_start_streaming(struct vb2_queue *vq,
dcmipp_start_capture(vcap, vcap->next);
/* Enable interruptions */
- reg_set(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
+ spin_lock(&vcap->vdev.v4l2_dev->lock);
+ reg_set(vcap, DCMIPP_CMIER, DCMIPP_CMIER_PxALL(vcap->pipe_id));
+ spin_unlock(&vcap->vdev.v4l2_dev->lock);
vcap->state = DCMIPP_RUNNING;
@@ -462,19 +666,19 @@ err_buffer_done:
* Return all buffers to vb2 in QUEUED state.
* This will give ownership back to userspace
*/
- dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_QUEUED);
+ dcmipp_capture_all_buffers_done(vcap, VB2_BUF_STATE_QUEUED);
vcap->active = NULL;
spin_unlock_irq(&vcap->irqlock);
return ret;
}
-static void dcmipp_dump_status(struct dcmipp_bytecap_device *vcap)
+static void dcmipp_dump_status(struct dcmipp_capture_device *vcap)
{
struct device *dev = vcap->dev;
dev_dbg(dev, "[DCMIPP_PRSR] =%#10.8x\n", reg_read(vcap, DCMIPP_PRSR));
- dev_dbg(dev, "[DCMIPP_P0SR] =%#10.8x\n", reg_read(vcap, DCMIPP_P0SR));
+ dev_dbg(dev, "[DCMIPP_P0SR] =%#10.8x\n", reg_read(vcap, DCMIPP_PxSR(0)));
dev_dbg(dev, "[DCMIPP_P0DCCNTR]=%#10.8x\n",
reg_read(vcap, DCMIPP_P0DCCNTR));
dev_dbg(dev, "[DCMIPP_CMSR1] =%#10.8x\n", reg_read(vcap, DCMIPP_CMSR1));
@@ -485,9 +689,9 @@ static void dcmipp_dump_status(struct dcmipp_bytecap_device *vcap)
* Stop the stream engine. Any remaining buffers in the stream queue are
* dequeued and passed on to the vb2 framework marked as STATE_ERROR.
*/
-static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
+static void dcmipp_capture_stop_streaming(struct vb2_queue *vq)
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vq);
int ret;
u32 status;
@@ -500,29 +704,32 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
media_pipeline_stop(vcap->vdev.entity.pads);
/* Disable interruptions */
- reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_P0ALL);
+ spin_lock(&vcap->vdev.v4l2_dev->lock);
+ reg_clear(vcap, DCMIPP_CMIER, DCMIPP_CMIER_PxALL(vcap->pipe_id));
+ spin_unlock(&vcap->vdev.v4l2_dev->lock);
/* Stop capture */
- reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
+ reg_clear(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
/* Wait until CPTACT become 0 */
- ret = readl_relaxed_poll_timeout(vcap->regs + DCMIPP_P0SR, status,
- !(status & DCMIPP_P0SR_CPTACT),
+ ret = readl_relaxed_poll_timeout(vcap->regs + DCMIPP_PxSR(vcap->pipe_id),
+ status,
+ !(status & DCMIPP_PxSR_CPTACT),
20 * USEC_PER_MSEC,
1000 * USEC_PER_MSEC);
if (ret)
dev_warn(vcap->dev, "Timeout when stopping\n");
/* Disable pipe */
- reg_clear(vcap, DCMIPP_P0FSCR, DCMIPP_P0FSCR_PIPEN);
+ reg_clear(vcap, DCMIPP_PxFSCR(vcap->pipe_id), DCMIPP_PxFSCR_PIPEN);
/* Clear any pending interrupts */
- reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_P0ALL);
+ reg_write(vcap, DCMIPP_CMFCR, DCMIPP_CMIER_PxALL(vcap->pipe_id));
spin_lock_irq(&vcap->irqlock);
/* Return all queued buffers to vb2 in ERROR state */
- dcmipp_bytecap_all_buffers_done(vcap, VB2_BUF_STATE_ERROR);
+ dcmipp_capture_all_buffers_done(vcap, VB2_BUF_STATE_ERROR);
INIT_LIST_HEAD(&vcap->buffers);
vcap->active = NULL;
@@ -530,7 +737,8 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
spin_unlock_irq(&vcap->irqlock);
- dcmipp_dump_status(vcap);
+ if (vcap->pipe_id == 0)
+ dcmipp_dump_status(vcap);
pm_runtime_put(vcap->dev);
@@ -541,12 +749,14 @@ static void dcmipp_bytecap_stop_streaming(struct vb2_queue *vq)
vcap->count.underrun, vcap->count.buffers);
}
-static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
+static int dcmipp_capture_buf_prepare(struct vb2_buffer *vb)
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
+ struct v4l2_pix_format *format = &vcap->format;
unsigned long size;
+ int ret;
size = vcap->format.sizeimage;
@@ -562,6 +772,26 @@ static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
/* Get memory addresses */
buf->addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
buf->size = vb2_plane_size(&buf->vb.vb2_buf, 0);
+
+ ret = frame_planes(buf->addr,
+ buf->addrs, buf->strides, buf->sizes,
+ format->width, format->height,
+ format->pixelformat);
+ if (ret) {
+ dev_err(vcap->dev, "%s: Unsupported pixel format (%x)\n",
+ __func__, format->pixelformat);
+ return ret;
+ }
+
+ /* Check for 16 bytes alignment required by hardware */
+ WARN_ON(buf->addrs[0] & 15);
+ if (vcap->pipe_id != 0) {
+ WARN_ON(buf->strides[0] & 15);
+ WARN_ON(buf->addrs[1] & 15);
+ WARN_ON(buf->strides[1] & 15);
+ WARN_ON(buf->addrs[2] & 15);
+ }
+
buf->prepared = true;
vb2_set_plane_payload(&buf->vb.vb2_buf, 0, buf->size);
@@ -573,9 +803,9 @@ static int dcmipp_bytecap_buf_prepare(struct vb2_buffer *vb)
return 0;
}
-static void dcmipp_bytecap_buf_queue(struct vb2_buffer *vb2_buf)
+static void dcmipp_capture_buf_queue(struct vb2_buffer *vb2_buf)
{
- struct dcmipp_bytecap_device *vcap =
+ struct dcmipp_capture_device *vcap =
vb2_get_drv_priv(vb2_buf->vb2_queue);
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb2_buf);
struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
@@ -599,13 +829,13 @@ static void dcmipp_bytecap_buf_queue(struct vb2_buffer *vb2_buf)
spin_unlock_irq(&vcap->irqlock);
}
-static int dcmipp_bytecap_queue_setup(struct vb2_queue *vq,
+static int dcmipp_capture_queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers,
unsigned int *nplanes,
unsigned int sizes[],
struct device *alloc_devs[])
{
- struct dcmipp_bytecap_device *vcap = vb2_get_drv_priv(vq);
+ struct dcmipp_capture_device *vcap = vb2_get_drv_priv(vq);
unsigned int size;
size = vcap->format.sizeimage;
@@ -623,7 +853,7 @@ static int dcmipp_bytecap_queue_setup(struct vb2_queue *vq,
return 0;
}
-static int dcmipp_bytecap_buf_init(struct vb2_buffer *vb)
+static int dcmipp_capture_buf_init(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct dcmipp_buf *buf = container_of(vbuf, struct dcmipp_buf, vb);
@@ -633,19 +863,19 @@ static int dcmipp_bytecap_buf_init(struct vb2_buffer *vb)
return 0;
}
-static const struct vb2_ops dcmipp_bytecap_qops = {
- .start_streaming = dcmipp_bytecap_start_streaming,
- .stop_streaming = dcmipp_bytecap_stop_streaming,
- .buf_init = dcmipp_bytecap_buf_init,
- .buf_prepare = dcmipp_bytecap_buf_prepare,
- .buf_queue = dcmipp_bytecap_buf_queue,
- .queue_setup = dcmipp_bytecap_queue_setup,
+static const struct vb2_ops dcmipp_capture_qops = {
+ .start_streaming = dcmipp_capture_start_streaming,
+ .stop_streaming = dcmipp_capture_stop_streaming,
+ .buf_init = dcmipp_capture_buf_init,
+ .buf_prepare = dcmipp_capture_buf_prepare,
+ .buf_queue = dcmipp_capture_buf_queue,
+ .queue_setup = dcmipp_capture_queue_setup,
};
-static void dcmipp_bytecap_release(struct video_device *vdev)
+static void dcmipp_capture_release(struct video_device *vdev)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(vdev, struct dcmipp_bytecap_device, vdev);
+ struct dcmipp_capture_device *vcap =
+ container_of(vdev, struct dcmipp_capture_device, vdev);
dcmipp_pads_cleanup(vcap->ved.pads);
mutex_destroy(&vcap->lock);
@@ -653,16 +883,16 @@ static void dcmipp_bytecap_release(struct video_device *vdev)
kfree(vcap);
}
-void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved)
+void dcmipp_capture_ent_release(struct dcmipp_ent_device *ved)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(ved, struct dcmipp_bytecap_device, ved);
+ struct dcmipp_capture_device *vcap =
+ container_of(ved, struct dcmipp_capture_device, ved);
media_entity_cleanup(ved->ent);
vb2_video_unregister_device(&vcap->vdev);
}
-static void dcmipp_buffer_done(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_buffer_done(struct dcmipp_capture_device *vcap,
struct dcmipp_buf *buf,
size_t bytesused,
int err)
@@ -686,7 +916,7 @@ static void dcmipp_buffer_done(struct dcmipp_bytecap_device *vcap,
/* irqlock must be held */
static void
-dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
+dcmipp_capture_set_next_frame_or_stop(struct dcmipp_capture_device *vcap)
{
if (!vcap->next && list_is_singular(&vcap->buffers)) {
/*
@@ -695,7 +925,7 @@ dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
* for next frame). On-going frame capture will continue until
* FRAME END but no further capture will be done.
*/
- reg_clear(vcap, DCMIPP_P0FCTCR, DCMIPP_P0FCTCR_CPTREQ);
+ reg_clear(vcap, DCMIPP_PxFCTCR(vcap->pipe_id), DCMIPP_PxFCTCR_CPTREQ);
dev_dbg(vcap->dev, "Capture restart is deferred to next buffer queueing\n");
vcap->next = NULL;
@@ -712,13 +942,19 @@ dcmipp_bytecap_set_next_frame_or_stop(struct dcmipp_bytecap_device *vcap)
* This register is shadowed and will be taken into
* account on next VSYNC (start of next frame)
*/
- reg_write(vcap, DCMIPP_P0PPM0AR1, vcap->next->addr);
+ reg_write(vcap, DCMIPP_PxPPM0AR1(vcap->pipe_id), vcap->next->addrs[0]);
+ if (vcap->pipe_id == 1) {
+ if (vcap->next->addrs[1])
+ reg_write(vcap, DCMIPP_P1PPM1AR1, vcap->next->addrs[1]);
+ if (vcap->next->addrs[2])
+ reg_write(vcap, DCMIPP_P1PPM2AR1, vcap->next->addrs[2]);
+ }
dev_dbg(vcap->dev, "Write [%d] %p phy=%pad\n",
vcap->next->vb.vb2_buf.index, vcap->next, &vcap->next->addr);
}
/* irqlock must be held */
-static void dcmipp_bytecap_process_frame(struct dcmipp_bytecap_device *vcap,
+static void dcmipp_capture_process_frame(struct dcmipp_capture_device *vcap,
size_t bytesused)
{
int err = 0;
@@ -744,33 +980,43 @@ static void dcmipp_bytecap_process_frame(struct dcmipp_bytecap_device *vcap,
vcap->active = NULL;
}
-static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
+static irqreturn_t dcmipp_capture_irq_thread(int irq, void *arg)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(arg, struct dcmipp_bytecap_device, ved);
+ struct dcmipp_capture_device *vcap =
+ container_of(arg, struct dcmipp_capture_device, ved);
+ u32 cmsr2_pxframef;
+ u32 cmsr2_pxvsyncf;
+ u32 cmsr2_pxovrf;
size_t bytesused = 0;
spin_lock_irq(&vcap->irqlock);
+ cmsr2_pxovrf = DCMIPP_CMSR2_PxOVRF(vcap->pipe_id);
+ cmsr2_pxvsyncf = DCMIPP_CMSR2_PxVSYNCF(vcap->pipe_id);
+ cmsr2_pxframef = DCMIPP_CMSR2_PxFRAMEF(vcap->pipe_id);
+
/*
* If we have an overrun, a frame-end will probably not be generated,
* in that case the active buffer will be recycled as next buffer by
* the VSYNC handler
*/
- if (vcap->cmsr2 & DCMIPP_CMSR2_P0OVRF) {
+ if (vcap->cmsr2 & cmsr2_pxovrf) {
vcap->count.errors++;
vcap->count.overrun++;
}
- if (vcap->cmsr2 & DCMIPP_CMSR2_P0FRAMEF) {
+ if (vcap->cmsr2 & cmsr2_pxframef) {
vcap->count.frame++;
/* Read captured buffer size */
- bytesused = reg_read(vcap, DCMIPP_P0DCCNTR);
- dcmipp_bytecap_process_frame(vcap, bytesused);
+ if (vcap->pipe_id == 0)
+ bytesused = reg_read(vcap, DCMIPP_P0DCCNTR);
+ else
+ bytesused = vcap->format.sizeimage;
+ dcmipp_capture_process_frame(vcap, bytesused);
}
- if (vcap->cmsr2 & DCMIPP_CMSR2_P0VSYNCF) {
+ if (vcap->cmsr2 & cmsr2_pxvsyncf) {
vcap->count.vsync++;
if (vcap->state == DCMIPP_WAIT_FOR_BUFFER) {
vcap->count.underrun++;
@@ -787,7 +1033,7 @@ static irqreturn_t dcmipp_bytecap_irq_thread(int irq, void *arg)
* active (but not used) buffer and put it back into next.
*/
swap(vcap->active, vcap->next);
- dcmipp_bytecap_set_next_frame_or_stop(vcap);
+ dcmipp_capture_set_next_frame_or_stop(vcap);
}
out:
@@ -795,13 +1041,16 @@ out:
return IRQ_HANDLED;
}
-static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg)
+static irqreturn_t dcmipp_capture_irq_callback(int irq, void *arg)
{
- struct dcmipp_bytecap_device *vcap =
- container_of(arg, struct dcmipp_bytecap_device, ved);
+ struct dcmipp_capture_device *vcap =
+ container_of(arg, struct dcmipp_capture_device, ved);
+ struct dcmipp_ent_device *ved = arg;
/* Store interrupt status register */
- vcap->cmsr2 = reg_read(vcap, DCMIPP_CMSR2) & DCMIPP_CMIER_P0ALL;
+ vcap->cmsr2 = ved->cmsr2 & DCMIPP_CMIER_PxALL(vcap->pipe_id);
+ if (!vcap->cmsr2)
+ return IRQ_HANDLED;
vcap->count.it++;
/* Clear interrupt */
@@ -810,41 +1059,52 @@ static irqreturn_t dcmipp_bytecap_irq_callback(int irq, void *arg)
return IRQ_WAKE_THREAD;
}
-static int dcmipp_bytecap_link_validate(struct media_link *link)
+static int dcmipp_capture_link_validate(struct media_link *link)
{
struct media_entity *entity = link->sink->entity;
struct video_device *vd = media_entity_to_video_device(entity);
- struct dcmipp_bytecap_device *vcap = container_of(vd,
- struct dcmipp_bytecap_device, vdev);
+ struct dcmipp_capture_device *vcap = container_of(vd,
+ struct dcmipp_capture_device, vdev);
struct v4l2_subdev *source_sd =
media_entity_to_v4l2_subdev(link->source->entity);
struct v4l2_subdev_format source_fmt = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
.pad = link->source->index,
};
+ u32 width_aligned;
int ret, i;
ret = v4l2_subdev_call(source_sd, pad, get_fmt, NULL, &source_fmt);
if (ret < 0)
return 0;
- if (source_fmt.format.width != vcap->format.width ||
+ width_aligned = source_fmt.format.width;
+
+ /*
+ * On pixel pipes there can be alignment constraints.
+ * Depending on the format & pixelpacker constraints, vcap width is
+ * different from mbus width. Compute expected vcap width based on
+ * mbus width
+ */
+ if (vcap->pipe_id != 0)
+ width_aligned = round_up(source_fmt.format.width,
+ 1 << hdw_pixel_alignment(vcap->format.pixelformat));
+
+ if (width_aligned != vcap->format.width ||
source_fmt.format.height != vcap->format.height) {
dev_err(vcap->dev, "Wrong width or height %ux%u (%ux%u expected)\n",
vcap->format.width, vcap->format.height,
- source_fmt.format.width, source_fmt.format.height);
+ width_aligned, source_fmt.format.height);
return -EINVAL;
}
- for (i = 0; i < ARRAY_SIZE(dcmipp_bytecap_pix_map_list); i++) {
- if (dcmipp_bytecap_pix_map_list[i].pixelformat ==
- vcap->format.pixelformat &&
- dcmipp_bytecap_pix_map_list[i].code ==
- source_fmt.format.code)
+ for (i = 0; i < vcap->pix_map_array_size; i++) {
+ if (vcap->pix_map[i].pixelformat == vcap->format.pixelformat &&
+ vcap->pix_map[i].code == source_fmt.format.code)
break;
}
- if (i == ARRAY_SIZE(dcmipp_bytecap_pix_map_list)) {
+ if (i == vcap->pix_map_array_size) {
dev_err(vcap->dev, "mbus code 0x%x do not match capture device format (0x%x)\n",
vcap->format.pixelformat, source_fmt.format.code);
return -EINVAL;
@@ -853,26 +1113,54 @@ static int dcmipp_bytecap_link_validate(struct media_link *link)
return 0;
}
-static const struct media_entity_operations dcmipp_bytecap_entity_ops = {
- .link_validate = dcmipp_bytecap_link_validate,
+static const struct media_entity_operations dcmipp_capture_entity_ops = {
+ .link_validate = dcmipp_capture_link_validate,
};
-struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs)
+static int dcmipp_name_to_pipe_id(const char *name)
+{
+ if (strstr(name, "dump"))
+ return 0;
+ else if (strstr(name, "main"))
+ return 1;
+ else if (strstr(name, "aux"))
+ return 2;
+ else
+ return -EINVAL;
+}
+
+struct dcmipp_ent_device *dcmipp_capture_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
{
- struct dcmipp_bytecap_device *vcap;
+ struct dcmipp_capture_device *vcap;
+ struct device *dev = dcmipp->dev;
struct video_device *vdev;
struct vb2_queue *q;
const unsigned long pad_flag = MEDIA_PAD_FL_SINK;
int ret = 0;
- /* Allocate the dcmipp_bytecap_device struct */
+ /* Allocate the dcmipp_capture_device struct */
vcap = kzalloc_obj(*vcap);
if (!vcap)
return ERR_PTR(-ENOMEM);
+ /* Retrieve the pipe_id */
+ vcap->pipe_id = dcmipp_name_to_pipe_id(entity_name);
+ if (vcap->pipe_id < 0) {
+ ret = -EIO;
+ dev_err(dev, "failed to retrieve pipe_id\n");
+ goto err_free_vcap;
+ }
+
+ /* Initialize supported format table format */
+ if (vcap->pipe_id == 0) {
+ vcap->pix_map = dcmipp_capture_dump_pix_map_list;
+ vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_capture_dump_pix_map_list);
+ } else {
+ vcap->pix_map = dcmipp_capture_pixel_pix_map_list;
+ vcap->pix_map_array_size = ARRAY_SIZE(dcmipp_capture_pixel_pix_map_list);
+ }
+
/* Allocate the pads */
vcap->ved.pads = dcmipp_pads_init(1, &pad_flag);
if (IS_ERR(vcap->ved.pads)) {
@@ -880,10 +1168,12 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
goto err_free_vcap;
}
+ vcap->ved.dcmipp = dcmipp;
+
/* Initialize the media entity */
vcap->vdev.entity.name = entity_name;
vcap->vdev.entity.function = MEDIA_ENT_F_IO_V4L;
- vcap->vdev.entity.ops = &dcmipp_bytecap_entity_ops;
+ vcap->vdev.entity.ops = &dcmipp_capture_entity_ops;
ret = media_entity_pads_init(&vcap->vdev.entity, 1, vcap->ved.pads);
if (ret)
goto err_clean_pads;
@@ -898,7 +1188,7 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
q->lock = &vcap->lock;
q->drv_priv = vcap;
q->buf_struct_size = sizeof(struct dcmipp_buf);
- q->ops = &dcmipp_bytecap_qops;
+ q->ops = &dcmipp_capture_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->min_queued_buffers = 1;
@@ -927,21 +1217,21 @@ struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
/* Fill the dcmipp_ent_device struct */
vcap->ved.ent = &vcap->vdev.entity;
- vcap->ved.handler = dcmipp_bytecap_irq_callback;
- vcap->ved.thread_fn = dcmipp_bytecap_irq_thread;
+ vcap->ved.handler = dcmipp_capture_irq_callback;
+ vcap->ved.thread_fn = dcmipp_capture_irq_thread;
vcap->dev = dev;
- vcap->regs = regs;
+ vcap->regs = dcmipp->regs;
/* Initialize the video_device struct */
vdev = &vcap->vdev;
vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_IO_MC;
- vdev->release = dcmipp_bytecap_release;
- vdev->fops = &dcmipp_bytecap_fops;
- vdev->ioctl_ops = &dcmipp_bytecap_ioctl_ops;
+ vdev->release = dcmipp_capture_release;
+ vdev->fops = &dcmipp_capture_fops;
+ vdev->ioctl_ops = &dcmipp_capture_ioctl_ops;
vdev->lock = &vcap->lock;
vdev->queue = q;
- vdev->v4l2_dev = v4l2_dev;
+ vdev->v4l2_dev = &dcmipp->v4l2_dev;
strscpy(vdev->name, entity_name, sizeof(vdev->name));
video_set_drvdata(vdev, &vcap->ved);
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
index fe5f97233f5e..007912c3a7f8 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-common.h
@@ -58,16 +58,82 @@ do { \
(fmt)->xfer_func = DCMIPP_XFER_FUNC_DEFAULT; \
} while (0)
+struct dcmipp_device {
+ /* The platform device */
+ struct platform_device pdev;
+ struct device *dev;
+
+ /* Hardware resources */
+ void __iomem *regs;
+ struct clk *mclk;
+ struct clk *kclk;
+
+ /* The pipeline configuration */
+ const struct dcmipp_pipeline_config *pipe_cfg;
+
+ /* The Associated media_device parent */
+ struct media_device mdev;
+ struct media_pipeline pipe;
+
+ /* Internal v4l2 parent device*/
+ struct v4l2_device v4l2_dev;
+
+ /* Entities */
+ struct dcmipp_ent_device **entity;
+
+ struct v4l2_async_notifier notifier;
+};
+
+#define DCMIPP_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
+ .src_ent = src, \
+ .src_pad = srcpad, \
+ .sink_ent = sink, \
+ .sink_pad = sinkpad, \
+ .flags = link_flags, \
+}
+
+/* Structure which describes individual configuration for each entity */
+struct dcmipp_ent_config {
+ const char *name;
+ struct dcmipp_ent_device *(*init)
+ (const char *entity_name,
+ struct dcmipp_device *dcmipp);
+ void (*release)(struct dcmipp_ent_device *ved);
+};
+
+/* Structure which describes links between entities */
+struct dcmipp_ent_link {
+ unsigned int src_ent;
+ u16 src_pad;
+ unsigned int sink_ent;
+ u16 sink_pad;
+ u32 flags;
+};
+
+/* Structure which describes the whole topology */
+struct dcmipp_pipeline_config {
+ const struct dcmipp_ent_config *ents;
+ size_t num_ents;
+ const struct dcmipp_ent_link *links;
+ size_t num_links;
+ u32 hw_revision;
+ bool has_csi2;
+ bool needs_mclk;
+ bool has_swapyuv;
+};
+
/**
* struct dcmipp_ent_device - core struct that represents a node in the topology
*
* @ent: the pointer to struct media_entity for the node
+ * @dcmipp: the pointer to the parent dcmipp_device
* @pads: the list of pads of the node
* @bus: struct v4l2_mbus_config_parallel describing input bus
* @bus_type: type of input bus (parallel or BT656)
* @handler: irq handler dedicated to the subdev
* @handler_ret: value returned by the irq handler
* @thread_fn: threaded irq handler
+ * @cmsr2: dcmipp status reg value captured upon an interrupt
*
* The DCMIPP provides a single IRQ line and a IRQ status registers for all
* subdevs, hence once the main irq handler (registered at probe time) is
@@ -84,6 +150,7 @@ do { \
*/
struct dcmipp_ent_device {
struct media_entity *ent;
+ struct dcmipp_device *dcmipp;
struct media_pad *pads;
/* Parallel input device */
@@ -92,6 +159,13 @@ struct dcmipp_ent_device {
irq_handler_t handler;
irqreturn_t handler_ret;
irq_handler_t thread_fn;
+ u32 cmsr2;
+};
+
+enum dcmipp_state {
+ DCMIPP_STOPPED = 0,
+ DCMIPP_WAIT_FOR_BUFFER,
+ DCMIPP_RUNNING,
};
/**
@@ -199,19 +273,22 @@ static inline void __reg_clear(struct device *dev, void __iomem *base, u32 reg,
}
/* DCMIPP subdev init / release entry points */
-struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs);
+struct dcmipp_ent_device *dcmipp_inp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved);
struct dcmipp_ent_device *
-dcmipp_byteproc_ent_init(struct device *dev, const char *entity_name,
- struct v4l2_device *v4l2_dev, void __iomem *regs);
+dcmipp_byteproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
void dcmipp_byteproc_ent_release(struct dcmipp_ent_device *ved);
-struct dcmipp_ent_device *dcmipp_bytecap_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs);
-void dcmipp_bytecap_ent_release(struct dcmipp_ent_device *ved);
+struct dcmipp_ent_device *dcmipp_capture_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
+void dcmipp_capture_ent_release(struct dcmipp_ent_device *ved);
+struct dcmipp_ent_device *dcmipp_isp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
+void dcmipp_isp_ent_release(struct dcmipp_ent_device *ved);
+struct dcmipp_ent_device *
+dcmipp_pixelproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp);
+void dcmipp_pixelproc_ent_release(struct dcmipp_ent_device *ved);
#endif
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
index 516334541b2c..54e28b4a5b2c 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-core.c
@@ -25,38 +25,7 @@
#define DCMIPP_MDEV_MODEL_NAME "DCMIPP MDEV"
-#define DCMIPP_ENT_LINK(src, srcpad, sink, sinkpad, link_flags) { \
- .src_ent = src, \
- .src_pad = srcpad, \
- .sink_ent = sink, \
- .sink_pad = sinkpad, \
- .flags = link_flags, \
-}
-
-struct dcmipp_device {
- /* The platform device */
- struct platform_device pdev;
- struct device *dev;
-
- /* Hardware resources */
- void __iomem *regs;
- struct clk *mclk;
- struct clk *kclk;
-
- /* The pipeline configuration */
- const struct dcmipp_pipeline_config *pipe_cfg;
-
- /* The Associated media_device parent */
- struct media_device mdev;
-
- /* Internal v4l2 parent device*/
- struct v4l2_device v4l2_dev;
-
- /* Entities */
- struct dcmipp_ent_device **entity;
-
- struct v4l2_async_notifier notifier;
-};
+#define DCMIPP_CMSR2 0x3f8
static inline struct dcmipp_device *
notifier_to_dcmipp(struct v4l2_async_notifier *n)
@@ -64,35 +33,6 @@ notifier_to_dcmipp(struct v4l2_async_notifier *n)
return container_of(n, struct dcmipp_device, notifier);
}
-/* Structure which describes individual configuration for each entity */
-struct dcmipp_ent_config {
- const char *name;
- struct dcmipp_ent_device *(*init)
- (struct device *dev, const char *entity_name,
- struct v4l2_device *v4l2_dev, void __iomem *regs);
- void (*release)(struct dcmipp_ent_device *ved);
-};
-
-/* Structure which describes links between entities */
-struct dcmipp_ent_link {
- unsigned int src_ent;
- u16 src_pad;
- unsigned int sink_ent;
- u16 sink_pad;
- u32 flags;
-};
-
-/* Structure which describes the whole topology */
-struct dcmipp_pipeline_config {
- const struct dcmipp_ent_config *ents;
- size_t num_ents;
- const struct dcmipp_ent_link *links;
- size_t num_links;
- u32 hw_revision;
- bool has_csi2;
- bool needs_mclk;
-};
-
/* --------------------------------------------------------------------------
* Topology Configuration
*/
@@ -110,8 +50,8 @@ static const struct dcmipp_ent_config stm32mp13_ent_config[] = {
},
{
.name = "dcmipp_dump_capture",
- .init = dcmipp_bytecap_ent_init,
- .release = dcmipp_bytecap_ent_release,
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
},
};
@@ -135,6 +75,11 @@ static const struct dcmipp_pipeline_config stm32mp13_pipe_cfg = {
.hw_revision = DCMIPP_STM32MP13_VERR
};
+#define ID_MAIN_ISP 3
+#define ID_MAIN_POSTPROC 4
+#define ID_MAIN_CAPTURE 5
+#define ID_AUX_POSTPROC 6
+#define ID_AUX_CAPTURE 7
static const struct dcmipp_ent_config stm32mp25_ent_config[] = {
{
.name = "dcmipp_input",
@@ -148,16 +93,49 @@ static const struct dcmipp_ent_config stm32mp25_ent_config[] = {
},
{
.name = "dcmipp_dump_capture",
- .init = dcmipp_bytecap_ent_init,
- .release = dcmipp_bytecap_ent_release,
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
+ },
+ {
+ .name = "dcmipp_main_isp",
+ .init = dcmipp_isp_ent_init,
+ .release = dcmipp_isp_ent_release,
+ },
+ {
+ .name = "dcmipp_main_postproc",
+ .init = dcmipp_pixelproc_ent_init,
+ .release = dcmipp_pixelproc_ent_release,
+ },
+ {
+ .name = "dcmipp_main_capture",
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
+ },
+ {
+ .name = "dcmipp_aux_postproc",
+ .init = dcmipp_pixelproc_ent_init,
+ .release = dcmipp_pixelproc_ent_release,
+ },
+ {
+ .name = "dcmipp_aux_capture",
+ .init = dcmipp_capture_ent_init,
+ .release = dcmipp_capture_ent_release,
},
};
static const struct dcmipp_ent_link stm32mp25_ent_links[] = {
- DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0,
- MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_INPUT, 1, ID_DUMP_BYTEPROC, 0, MEDIA_LNK_FL_ENABLED),
DCMIPP_ENT_LINK(ID_DUMP_BYTEPROC, 1, ID_DUMP_CAPTURE, 0,
MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_INPUT, 2, ID_MAIN_ISP, 0, 0),
+ DCMIPP_ENT_LINK(ID_MAIN_ISP, 1, ID_MAIN_POSTPROC, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_MAIN_ISP, 2, ID_AUX_POSTPROC, 0, 0),
+ DCMIPP_ENT_LINK(ID_MAIN_POSTPROC, 1, ID_MAIN_CAPTURE, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
+ DCMIPP_ENT_LINK(ID_INPUT, 3, ID_AUX_POSTPROC, 0, 0),
+ DCMIPP_ENT_LINK(ID_AUX_POSTPROC, 1, ID_AUX_CAPTURE, 0,
+ MEDIA_LNK_FL_ENABLED | MEDIA_LNK_FL_IMMUTABLE),
};
#define DCMIPP_STM32MP25_VERR 0x30
@@ -168,7 +146,8 @@ static const struct dcmipp_pipeline_config stm32mp25_pipe_cfg = {
.num_links = ARRAY_SIZE(stm32mp25_ent_links),
.hw_revision = DCMIPP_STM32MP25_VERR,
.has_csi2 = true,
- .needs_mclk = true
+ .needs_mclk = true,
+ .has_swapyuv = true
};
#define LINK_FLAG_TO_STR(f) ((f) == 0 ? "" :\
@@ -221,9 +200,7 @@ static int dcmipp_create_subdevs(struct dcmipp_device *dcmipp)
dev_dbg(dcmipp->dev, "add subdev %s\n", name);
dcmipp->entity[i] =
- dcmipp->pipe_cfg->ents[i].init(dcmipp->dev, name,
- &dcmipp->v4l2_dev,
- dcmipp->regs);
+ dcmipp->pipe_cfg->ents[i].init(name, dcmipp);
if (IS_ERR(dcmipp->entity[i])) {
dev_err(dcmipp->dev, "failed to init subdev %s\n",
name);
@@ -278,10 +255,15 @@ static irqreturn_t dcmipp_irq_callback(int irq, void *arg)
struct dcmipp_ent_device *ved;
irqreturn_t ret = IRQ_HANDLED;
unsigned int i;
+ u32 cmsr2;
+
+ /* Centralized read of CMSR2 */
+ cmsr2 = reg_read(dcmipp, DCMIPP_CMSR2);
/* Call irq handler of each entities of pipeline */
for (i = 0; i < dcmipp->pipe_cfg->num_ents; i++) {
ved = dcmipp->entity[i];
+ ved->cmsr2 = cmsr2;
if (ved->handler)
ved->handler_ret = ved->handler(irq, ved);
else if (ved->thread_fn)
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
index 7d3f5857cbfe..e66a9456deb6 100644
--- a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-input.c
@@ -43,12 +43,18 @@
#define DCMIPP_CMCR_INSEL BIT(0)
#define DCMIPP_P0FSCR 0x404
-#define DCMIPP_P0FSCR_DTMODE_MASK GENMASK(17, 16)
-#define DCMIPP_P0FSCR_DTMODE_SHIFT 16
-#define DCMIPP_P0FSCR_DTMODE_DTIDA 0x00
+#define DCMIPP_P1FSCR 0x804
+#define DCMIPP_P2FSCR 0xC04
+#define DCMIPP_PXFSCR_DTMODE_MASK GENMASK(17, 16)
+#define DCMIPP_PXFSCR_DTMODE_SHIFT 16
+#define DCMIPP_PXFSCR_DTMODE_DTIDA 0x00
#define DCMIPP_P0FSCR_DTMODE_ALLDT 0x03
-#define DCMIPP_P0FSCR_DTIDA_MASK GENMASK(5, 0)
-#define DCMIPP_P0FSCR_DTIDA_SHIFT 0
+#define DCMIPP_PXFSCR_DTIDA_MASK GENMASK(5, 0)
+#define DCMIPP_PXFSCR_DTIDA_SHIFT 0
+
+#define DCMIPP_PXFSCR(a) (((a) == 0) ? DCMIPP_P0FSCR :\
+ ((a) == 1) ? DCMIPP_P1FSCR :\
+ DCMIPP_P2FSCR)
#define IS_SINK(pad) (!(pad))
#define IS_SRC(pad) ((pad))
@@ -80,15 +86,15 @@ static const struct dcmipp_inp_pix_map dcmipp_inp_pix_map_list[] = {
PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_3X8, RGB888_3X8, RGB888, 0, MIPI_CSI2_DT_RGB888),
PIXMAP_SINK_SRC_PRCR_SWAP(RGB888_1X24, RGB888_1X24, RGB888, 0, MIPI_CSI2_DT_RGB888),
/* YUV422 */
- PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_1X16, YUYV8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YUYV8_2X8, UYVY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, UYVY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_1X16, UYVY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(UYVY8_2X8, YUYV8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_2X8, YVYU8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(YVYU8_1X16, YVYU8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
- PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 1, MIPI_CSI2_DT_YUV422_8B),
+ PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_2X8, VYUY8_2X8, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
PIXMAP_SINK_SRC_PRCR_SWAP(VYUY8_1X16, VYUY8_1X16, YUV422, 0, MIPI_CSI2_DT_YUV422_8B),
/* GREY */
PIXMAP_SINK_SRC_PRCR_SWAP(Y8_1X8, Y8_1X8, G8, 0, MIPI_CSI2_DT_RAW8),
@@ -269,11 +275,13 @@ static void dcmipp_inp_adjust_fmt(struct dcmipp_inp_device *inp,
}
static int dcmipp_inp_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
struct dcmipp_inp_device *inp = v4l2_get_subdevdata(sd);
struct v4l2_mbus_framefmt *mf;
+ int pad;
if (v4l2_subdev_is_streaming(sd))
return -EBUSY;
@@ -298,9 +306,11 @@ static int dcmipp_inp_set_fmt(struct v4l2_subdev *sd,
/* When setting the sink format, report that format on the src pad */
if (IS_SINK(fmt->pad)) {
- mf = v4l2_subdev_state_get_format(sd_state, 1);
- *mf = fmt->format;
- dcmipp_inp_adjust_fmt(inp, mf, 1);
+ for (pad = 1; pad < sd->entity.num_pads; pad++) {
+ mf = v4l2_subdev_state_get_format(sd_state, pad);
+ *mf = fmt->format;
+ dcmipp_inp_adjust_fmt(inp, mf, pad);
+ }
}
return 0;
@@ -356,8 +366,20 @@ static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp,
val |= vpix->prcr_format << DCMIPP_PRCR_FORMAT_SHIFT;
/* swap cycles */
- if (vpix->prcr_swapcycles)
- val |= DCMIPP_PRCR_SWAPCYCLES;
+ /*
+ * Table dcmipp_inp_pix_map_list take into consideration that SWAPYUV
+ * bit is available when dealing with 16bit YUV formats. If it is not
+ * available (such as on stm32mp13), swapcycle setting should be
+ * reversed
+ */
+ if (!inp->ved.dcmipp->pipe_cfg->has_swapyuv &&
+ (src_fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 ||
+ src_fmt->code == MEDIA_BUS_FMT_YVYU8_2X8 ||
+ src_fmt->code == MEDIA_BUS_FMT_UYVY8_2X8 ||
+ src_fmt->code == MEDIA_BUS_FMT_VYUY8_2X8))
+ val |= (!vpix->prcr_swapcycles ? DCMIPP_PRCR_SWAPCYCLES : 0);
+ else
+ val |= (vpix->prcr_swapcycles ? DCMIPP_PRCR_SWAPCYCLES : 0);
reg_write(inp, DCMIPP_PRCR, val);
@@ -371,7 +393,8 @@ static int dcmipp_inp_configure_parallel(struct dcmipp_inp_device *inp,
}
static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp,
- struct v4l2_subdev_state *state)
+ struct v4l2_subdev_state *state,
+ u32 pad)
{
const struct dcmipp_inp_pix_map *vpix;
struct v4l2_mbus_framefmt *sink_fmt;
@@ -379,7 +402,7 @@ static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp,
/* Get format information */
sink_fmt = v4l2_subdev_state_get_format(state, 0);
- src_fmt = v4l2_subdev_state_get_format(state, 1);
+ src_fmt = v4l2_subdev_state_get_format(state, pad);
vpix = dcmipp_inp_pix_map_by_code(sink_fmt->code, src_fmt->code);
if (!vpix) {
@@ -387,22 +410,28 @@ static int dcmipp_inp_configure_csi(struct dcmipp_inp_device *inp,
return -EINVAL;
}
- /* Apply configuration on each input pipe */
- reg_clear(inp, DCMIPP_P0FSCR,
- DCMIPP_P0FSCR_DTMODE_MASK | DCMIPP_P0FSCR_DTIDA_MASK);
+ /* Perform the configuration on the related pad/pipe */
+ reg_clear(inp, DCMIPP_PXFSCR(pad - 1),
+ DCMIPP_PXFSCR_DTMODE_MASK | DCMIPP_PXFSCR_DTIDA_MASK);
/* In case of JPEG we don't know the DT so we allow all data */
/*
* TODO - check instead dt == 0 for the time being to allow other
* unknown data-type
*/
- if (!vpix->dt)
- reg_set(inp, DCMIPP_P0FSCR,
- DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_P0FSCR_DTMODE_SHIFT);
- else
+ if (!vpix->dt) {
+ if (pad != 1) {
+ dev_err(inp->dev, "JPEG only available on pipe 0\n");
+ return -EINVAL;
+ }
+ /* Only available on Pipe #0 */
reg_set(inp, DCMIPP_P0FSCR,
- vpix->dt << DCMIPP_P0FSCR_DTIDA_SHIFT |
- DCMIPP_P0FSCR_DTMODE_DTIDA);
+ DCMIPP_P0FSCR_DTMODE_ALLDT << DCMIPP_PXFSCR_DTMODE_SHIFT);
+ } else {
+ reg_set(inp, DCMIPP_PXFSCR(pad - 1),
+ vpix->dt << DCMIPP_PXFSCR_DTIDA_SHIFT |
+ DCMIPP_PXFSCR_DTMODE_DTIDA);
+ }
/* Select the DCMIPP CSI interface */
reg_write(inp, DCMIPP_CMCR, DCMIPP_CMCR_INSEL);
@@ -420,20 +449,24 @@ static int dcmipp_inp_enable_streams(struct v4l2_subdev *sd,
struct media_pad *s_pad;
int ret = 0;
- /* Get source subdev */
- s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
- if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
- return -EINVAL;
- s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
-
if (inp->ved.bus_type == V4L2_MBUS_PARALLEL ||
inp->ved.bus_type == V4L2_MBUS_BT656)
ret = dcmipp_inp_configure_parallel(inp, state);
else if (inp->ved.bus_type == V4L2_MBUS_CSI2_DPHY)
- ret = dcmipp_inp_configure_csi(inp, state);
+ ret = dcmipp_inp_configure_csi(inp, state, pad);
if (ret)
return ret;
+ /* If there where no other pad enabled, then enable the source subdev */
+ if (sd->enabled_pads)
+ return 0;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
if (ret < 0) {
dev_err(inp->dev,
@@ -454,6 +487,10 @@ static int dcmipp_inp_disable_streams(struct v4l2_subdev *sd,
struct media_pad *s_pad;
int ret;
+ /* Don't do anything if there are still other pads enabled */
+ if ((sd->enabled_pads & ~BIT(pad)))
+ return 0;
+
/* Get source subdev */
s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
@@ -515,15 +552,16 @@ void dcmipp_inp_ent_release(struct dcmipp_ent_device *ved)
dcmipp_ent_sd_unregister(ved, &inp->sd);
}
-struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
- const char *entity_name,
- struct v4l2_device *v4l2_dev,
- void __iomem *regs)
+struct dcmipp_ent_device *dcmipp_inp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
{
struct dcmipp_inp_device *inp;
const unsigned long pads_flag[] = {
MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ MEDIA_PAD_FL_SOURCE, MEDIA_PAD_FL_SOURCE,
};
+ struct device *dev = dcmipp->dev;
+ u16 num_pads = ARRAY_SIZE(pads_flag);
int ret;
/* Allocate the inp struct */
@@ -531,12 +569,17 @@ struct dcmipp_ent_device *dcmipp_inp_ent_init(struct device *dev,
if (!inp)
return ERR_PTR(-ENOMEM);
- inp->regs = regs;
+ inp->regs = dcmipp->regs;
+ inp->ved.dcmipp = dcmipp;
+
+ /* For DCMIPP without CSI2, there is only a single pipe hence 2 pads */
+ if (!inp->ved.dcmipp->pipe_cfg->has_csi2)
+ num_pads = 2;
/* Initialize ved and sd */
- ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, v4l2_dev,
+ ret = dcmipp_ent_sd_register(&inp->ved, &inp->sd, &dcmipp->v4l2_dev,
entity_name, MEDIA_ENT_F_VID_IF_BRIDGE,
- ARRAY_SIZE(pads_flag), pads_flag,
+ num_pads, pads_flag,
&dcmipp_inp_int_ops, &dcmipp_inp_ops,
NULL, NULL);
if (ret) {
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c
new file mode 100644
index 000000000000..088301c20547
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-isp.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-rect.h>
+#include <media/v4l2-subdev.h>
+
+#include "dcmipp-common.h"
+#include "dcmipp-pixelcommon.h"
+
+#define DCMIPP_P1FSCR 0x804
+#define DCMIPP_P1FSCR_PIPEDIFF BIT(18)
+
+#define DCMIPP_P1SRCR 0x820
+#define DCMIPP_P1SRCR_LASTLINE_SHIFT 0
+#define DCMIPP_P1SRCR_FIRSTLINEDEL_SHIFT 12
+#define DCMIPP_P1SRCR_CROPEN BIT(15)
+
+#define DCMIPP_P1DECR 0x830
+#define DCMIPP_P1DECR_ENABLE BIT(0)
+#define DCMIPP_P1DECR_HDEC_SHIFT 1
+#define DCMIPP_P1DECR_VDEC_SHIFT 3
+
+#define DCMIPP_P1DMCR 0x870
+#define DCMIPP_P1DMCR_ENABLE BIT(0)
+#define DCMIPP_P1DMCR_TYPE_SHIFT 1
+#define DCMIPP_P1DMCR_TYPE_MASK GENMASK(2, 1)
+#define DCMIPP_P1DMCR_TYPE_RGGB 0x0
+#define DCMIPP_P1DMCR_TYPE_GRBG 0x1
+#define DCMIPP_P1DMCR_TYPE_GBRG 0x2
+#define DCMIPP_P1DMCR_TYPE_BGGR 0x3
+
+#define ISP_MEDIA_BUS_SINK_FMT_DEFAULT MEDIA_BUS_FMT_RGB565_1X16
+#define ISP_MEDIA_BUS_SRC_FMT_DEFAULT MEDIA_BUS_FMT_RGB888_1X24
+
+struct dcmipp_isp_device {
+ struct dcmipp_ent_device ved;
+ struct v4l2_subdev sd;
+ struct device *dev;
+
+ void __iomem *regs;
+};
+
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ .code = ISP_MEDIA_BUS_SINK_FMT_DEFAULT,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = DCMIPP_COLORSPACE_DEFAULT,
+ .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,
+ .quantization = DCMIPP_QUANTIZATION_DEFAULT,
+ .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
+};
+
+static inline unsigned int dcmipp_isp_set_compose(__u32 size, __u32 req)
+{
+ unsigned int i = 0;
+
+ if (req > size)
+ return size;
+
+ /* Maximum decimation factor is 8 */
+ while (size > req && i++ < 3)
+ size /= 2;
+
+ return size;
+}
+
+static void dcmipp_isp_adjust_fmt(struct v4l2_mbus_framefmt *fmt, u32 pad)
+{
+ /* Only accept code in the pix map table */
+ if (!dcmipp_pixelpipe_pix_map_by_code(fmt->code, DCMIPP_ISP, pad))
+ fmt->code = IS_SRC(pad) ? ISP_MEDIA_BUS_SRC_FMT_DEFAULT :
+ ISP_MEDIA_BUS_SINK_FMT_DEFAULT;
+
+ fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,
+ DCMIPP_FRAME_MAX_WIDTH) & ~1;
+ fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,
+ DCMIPP_FRAME_MAX_HEIGHT);
+
+ if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
+ fmt->field = V4L2_FIELD_NONE;
+
+ dcmipp_colorimetry_clamp(fmt);
+}
+
+static int dcmipp_isp_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ for (unsigned int i = 0; i < sd->entity.num_pads; i++) {
+ struct v4l2_mbus_framefmt *mf;
+
+ mf = v4l2_subdev_state_get_format(state, i);
+ *mf = fmt_default;
+ mf->code = IS_SRC(i) ? ISP_MEDIA_BUS_SRC_FMT_DEFAULT :
+ ISP_MEDIA_BUS_SINK_FMT_DEFAULT;
+
+ if (IS_SINK(i)) {
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ };
+
+ *v4l2_subdev_state_get_crop(state, i) = r;
+ *v4l2_subdev_state_get_compose(state, i) = r;
+ }
+ }
+
+ return 0;
+}
+
+static int dcmipp_isp_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ return dcmipp_pixelpipe_enum_mbus_code(DCMIPP_ISP, code);
+}
+
+static int dcmipp_isp_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ return dcmipp_pixelpipe_enum_frame_size(DCMIPP_ISP, fse);
+}
+
+static int dcmipp_isp_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ dcmipp_isp_adjust_fmt(&fmt->format, fmt->pad);
+
+ if (IS_SINK(fmt->pad)) {
+ struct v4l2_mbus_framefmt *src_fmt =
+ v4l2_subdev_state_get_format(state, 1);
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = fmt->format.width,
+ .height = fmt->format.height,
+ };
+
+ /* Adjust SINK pad crop/compose */
+ *v4l2_subdev_state_get_crop(state, 0) = r;
+ *v4l2_subdev_state_get_compose(state, 0) = r;
+
+ /* Forward format to SRC pads */
+ *src_fmt = fmt->format;
+ src_fmt->code = dcmipp_pixelpipe_src_format(fmt->format.code);
+ *v4l2_subdev_state_get_format(state, 2) = *src_fmt;
+ } else {
+ struct v4l2_mbus_framefmt *sink_fmt =
+ v4l2_subdev_state_get_format(state, 0);
+ struct v4l2_rect *compose =
+ v4l2_subdev_state_get_compose(state, 0);
+
+ fmt->format = *sink_fmt;
+ fmt->format.code = dcmipp_pixelpipe_src_format(sink_fmt->code);
+ if (compose->width && compose->height) {
+ fmt->format.width = compose->width;
+ fmt->format.height = compose->height;
+ }
+ /* Set to the 2nd SRC pad */
+ *v4l2_subdev_state_get_format(state, fmt->pad == 1 ? 2 : 1) =
+ fmt->format;
+ }
+
+ /* Update the selected pad format */
+ *v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format;
+
+ return 0;
+}
+
+static void dcmipp_isp_adjust_crop(struct v4l2_rect *r,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_rect crop_min = {
+ .width = fmt->width,
+ .height = DCMIPP_FRAME_MIN_HEIGHT,
+ };
+
+ /*
+ * Crop is related here to the statistics removal, that is
+ * firsts and/or lasts lines of the frame. Up to the first
+ * 7 lines can be skipped
+ */
+#define DCMIPP_ISP_MAX_TOP_CROP 7
+ v4l2_rect_set_min_size(r, &crop_min);
+ if (r->top > DCMIPP_ISP_MAX_TOP_CROP)
+ r->top = DCMIPP_ISP_MAX_TOP_CROP;
+ if ((r->height + r->top) > fmt->height)
+ r->height = fmt->height - r->top;
+}
+
+static int dcmipp_isp_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *crop, *compose;
+ __u32 pad;
+
+ if (IS_SRC(s->pad))
+ return -EINVAL;
+
+ if (s->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ crop = v4l2_subdev_state_get_crop(state, s->pad);
+ compose = v4l2_subdev_state_get_compose(state, s->pad);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ sink_fmt = v4l2_subdev_state_get_format(state, s->pad);
+ dcmipp_isp_adjust_crop(&s->r, sink_fmt);
+
+ *crop = s->r;
+ *compose = s->r;
+
+ dev_dbg(isp->dev, "s_selection: crop (%d,%d)/%ux%u\n",
+ crop->left, crop->top, crop->width, crop->height);
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.top = 0;
+ s->r.left = 0;
+ if (s->r.width < DCMIPP_FRAME_MIN_WIDTH)
+ s->r.width = DCMIPP_FRAME_MIN_WIDTH;
+ s->r.width = dcmipp_isp_set_compose(crop->width, s->r.width);
+ if (s->r.height < DCMIPP_FRAME_MIN_HEIGHT)
+ s->r.height = DCMIPP_FRAME_MIN_HEIGHT;
+ s->r.height = dcmipp_isp_set_compose(crop->height, s->r.height);
+ *compose = s->r;
+
+ dev_dbg(isp->dev, "s_selection: compose (%d,%d)/%ux%u\n",
+ compose->left, compose->top,
+ compose->width, compose->height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Update the source pad size */
+ for (pad = 1; pad < sd->entity.num_pads; pad++) {
+ src_fmt = v4l2_subdev_state_get_format(state, pad);
+ src_fmt->width = s->r.width;
+ src_fmt->height = s->r.height;
+ }
+
+ return 0;
+}
+
+#define STM32_DCMIPP_IS_BAYER_VARIANT(code, variant) \
+ ((code) == MEDIA_BUS_FMT_S##variant##8_1X8 || \
+ (code) == MEDIA_BUS_FMT_S##variant##10_1X10 || \
+ (code) == MEDIA_BUS_FMT_S##variant##12_1X12 || \
+ (code) == MEDIA_BUS_FMT_S##variant##14_1X14 || \
+ (code) == MEDIA_BUS_FMT_S##variant##16_1X16)
+static void dcmipp_isp_config_demosaicing(struct dcmipp_isp_device *isp,
+ struct v4l2_subdev_state *state)
+{
+ __u32 code = v4l2_subdev_state_get_format(state, 0)->code;
+ unsigned int val = 0;
+
+ /* Disable demosaicing */
+ reg_clear(isp, DCMIPP_P1DMCR,
+ DCMIPP_P1DMCR_ENABLE | DCMIPP_P1DMCR_TYPE_MASK);
+
+ /* Only perform demosaicing if format is bayer */
+ if (code < MEDIA_BUS_FMT_SBGGR8_1X8 || code >= MEDIA_BUS_FMT_JPEG_1X8)
+ return;
+
+ dev_dbg(isp->dev, "Input is RawBayer, enable Demosaicing\n");
+
+ if (STM32_DCMIPP_IS_BAYER_VARIANT(code, BGGR))
+ val = DCMIPP_P1DMCR_TYPE_BGGR << DCMIPP_P1DMCR_TYPE_SHIFT;
+ else if (STM32_DCMIPP_IS_BAYER_VARIANT(code, GBRG))
+ val = DCMIPP_P1DMCR_TYPE_GBRG << DCMIPP_P1DMCR_TYPE_SHIFT;
+ else if (STM32_DCMIPP_IS_BAYER_VARIANT(code, GRBG))
+ val = DCMIPP_P1DMCR_TYPE_GRBG << DCMIPP_P1DMCR_TYPE_SHIFT;
+ else if (STM32_DCMIPP_IS_BAYER_VARIANT(code, RGGB))
+ val = DCMIPP_P1DMCR_TYPE_RGGB << DCMIPP_P1DMCR_TYPE_SHIFT;
+
+ val |= DCMIPP_P1DMCR_ENABLE;
+
+ reg_set(isp, DCMIPP_P1DMCR, val);
+}
+
+static bool dcmipp_isp_is_aux_output_enabled(struct dcmipp_isp_device *isp)
+{
+ struct media_link *link;
+
+ for_each_media_entity_data_link(isp->ved.ent, link) {
+ if (link->source != &isp->ved.pads[2])
+ continue;
+
+ if (!(link->flags & MEDIA_LNK_FL_ENABLED))
+ continue;
+
+ if (!strcmp(link->sink->entity->name, "dcmipp_aux_postproc"))
+ return true;
+ }
+
+ return false;
+}
+
+static void dcmipp_isp_config_decimation(struct dcmipp_isp_device *isp,
+ struct v4l2_subdev_state *state)
+{
+ struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+ struct v4l2_rect *compose = v4l2_subdev_state_get_compose(state, 0);
+ u32 decr;
+
+ decr = (fls(crop->width / compose->width) - 1) << DCMIPP_P1DECR_HDEC_SHIFT |
+ (fls(crop->height / compose->height) - 1) << DCMIPP_P1DECR_VDEC_SHIFT;
+ if (decr)
+ decr |= DCMIPP_P1DECR_ENABLE;
+
+ reg_write(isp, DCMIPP_P1DECR, decr);
+}
+
+static int dcmipp_isp_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+ struct v4l2_rect *crop = v4l2_subdev_state_get_crop(state, 0);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Perform configuration only if no other pad is enabled */
+ if (sd->enabled_pads)
+ return 0;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ /* Check if link between ISP & Pipe2 postproc is enabled */
+ if (dcmipp_isp_is_aux_output_enabled(isp))
+ reg_clear(isp, DCMIPP_P1FSCR, DCMIPP_P1FSCR_PIPEDIFF);
+ else
+ reg_set(isp, DCMIPP_P1FSCR, DCMIPP_P1FSCR_PIPEDIFF);
+
+ /* Configure Statistic Removal */
+ crop = v4l2_subdev_state_get_crop(state, 0);
+ reg_write(isp, DCMIPP_P1SRCR,
+ ((crop->top << DCMIPP_P1SRCR_FIRSTLINEDEL_SHIFT) |
+ (crop->height << DCMIPP_P1SRCR_LASTLINE_SHIFT) |
+ DCMIPP_P1SRCR_CROPEN));
+
+ /* Configure Decimation */
+ dcmipp_isp_config_decimation(isp, state);
+
+ /* Configure Demosaicing */
+ dcmipp_isp_config_demosaicing(isp, state);
+
+ ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(isp->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dcmipp_isp_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Don't do anything if there are still other pads enabled */
+ if ((sd->enabled_pads & ~BIT(pad)))
+ return 0;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ /* Disable all blocks */
+ reg_write(isp, DCMIPP_P1SRCR, 0);
+ reg_write(isp, DCMIPP_P1DECR, 0);
+ reg_write(isp, DCMIPP_P1DMCR, 0);
+
+ ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(isp->dev,
+ "failed to disable source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct v4l2_subdev_pad_ops dcmipp_isp_pad_ops = {
+ .enum_mbus_code = dcmipp_isp_enum_mbus_code,
+ .enum_frame_size = dcmipp_isp_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = dcmipp_isp_set_fmt,
+ .get_selection = dcmipp_pixelpipe_get_selection,
+ .set_selection = dcmipp_isp_set_selection,
+ .enable_streams = dcmipp_isp_enable_streams,
+ .disable_streams = dcmipp_isp_disable_streams,
+};
+
+static const struct v4l2_subdev_video_ops dcmipp_isp_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_ops dcmipp_isp_ops = {
+ .pad = &dcmipp_isp_pad_ops,
+ .video = &dcmipp_isp_video_ops,
+};
+
+static void dcmipp_isp_release(struct v4l2_subdev *sd)
+{
+ struct dcmipp_isp_device *isp = v4l2_get_subdevdata(sd);
+
+ kfree(isp);
+}
+
+static const struct v4l2_subdev_internal_ops dcmipp_isp_int_ops = {
+ .init_state = dcmipp_isp_init_state,
+ .release = dcmipp_isp_release,
+};
+
+void dcmipp_isp_ent_release(struct dcmipp_ent_device *ved)
+{
+ struct dcmipp_isp_device *isp =
+ container_of(ved, struct dcmipp_isp_device, ved);
+
+ dcmipp_ent_sd_unregister(ved, &isp->sd);
+}
+
+struct dcmipp_ent_device *dcmipp_isp_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
+{
+ struct dcmipp_isp_device *isp;
+ const unsigned long pads_flag[] = {
+ MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ MEDIA_PAD_FL_SOURCE,
+ };
+ int ret;
+
+ /* Allocate the isp struct */
+ isp = kzalloc_obj(*isp);
+ if (!isp)
+ return ERR_PTR(-ENOMEM);
+
+ isp->regs = dcmipp->regs;
+
+ /* Initialize ved and sd */
+ ret = dcmipp_ent_sd_register(&isp->ved, &isp->sd,
+ &dcmipp->v4l2_dev, entity_name,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER,
+ ARRAY_SIZE(pads_flag), pads_flag,
+ &dcmipp_isp_int_ops, &dcmipp_isp_ops,
+ NULL, NULL);
+ if (ret) {
+ kfree(isp);
+ return ERR_PTR(ret);
+ }
+
+ isp->ved.dcmipp = dcmipp;
+ isp->dev = dcmipp->dev;
+
+ return &isp->ved;
+}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c
new file mode 100644
index 000000000000..d6f19db848d5
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-rect.h>
+#include <media/v4l2-subdev.h>
+
+#include "dcmipp-common.h"
+#include "dcmipp-pixelcommon.h"
+
+#define DCMIPP_ENT(id, pad) (1 << (2 * (id) + (pad)))
+#define DCMIPP_ISP_SINK (DCMIPP_ENT(DCMIPP_ISP, 0))
+#define DCMIPP_ISP_SRC (DCMIPP_ENT(DCMIPP_ISP, 1))
+#define DCMIPP_ISP_INOUT (DCMIPP_ISP_SINK | DCMIPP_ISP_SRC)
+#define DCMIPP_MAIN_POSTPROC_SINK (DCMIPP_ENT(DCMIPP_MAIN, 0))
+#define DCMIPP_MAIN_POSTPROC_SRC (DCMIPP_ENT(DCMIPP_MAIN, 1))
+#define DCMIPP_MAIN_POSTPROC_INOUT \
+ (DCMIPP_MAIN_POSTPROC_SINK | DCMIPP_MAIN_POSTPROC_SRC)
+#define DCMIPP_AUX_POSTPROC_SINK (DCMIPP_ENT(DCMIPP_AUX, 0))
+#define DCMIPP_AUX_POSTPROC_SRC (DCMIPP_ENT(DCMIPP_AUX, 1))
+#define DCMIPP_AUX_POSTPROC_INOUT \
+ (DCMIPP_AUX_POSTPROC_SINK | DCMIPP_AUX_POSTPROC_SRC)
+#define DCMIPP_ALL_POSTPROC_SINK \
+ (DCMIPP_MAIN_POSTPROC_SINK | DCMIPP_AUX_POSTPROC_SINK)
+#define DCMIPP_ALL_POSTPROC_INOUT \
+ (DCMIPP_MAIN_POSTPROC_INOUT | DCMIPP_AUX_POSTPROC_INOUT)
+
+#define PIXMAP_MBUS(mbus, applicable_pipes) \
+ { \
+ .code = MEDIA_BUS_FMT_##mbus, \
+ .pipes = applicable_pipes, \
+ }
+static const struct dcmipp_pixelpipe_pix_map
+dcmipp_pixel_formats_list[] = {
+ /* RGB formats */
+ /* RGB565 / RGB888 */
+ PIXMAP_MBUS(RGB565_2X8_LE, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(RGB565_1X16, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(RGB888_3X8, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(RGB888_1X24, DCMIPP_ALL_POSTPROC_INOUT | DCMIPP_ISP_INOUT),
+ /* YUV formats */
+ PIXMAP_MBUS(YUYV8_2X8, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(UYVY8_1X16, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(YUV8_1X24, DCMIPP_ALL_POSTPROC_INOUT | DCMIPP_ISP_SRC),
+ /* GREY */
+ PIXMAP_MBUS(Y8_1X8, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(Y10_1X10, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(Y12_1X12, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(Y14_1X14, DCMIPP_AUX_POSTPROC_SINK | DCMIPP_ISP_SINK),
+ /* Raw Bayer */
+ /* Raw 8 */
+ PIXMAP_MBUS(SBGGR8_1X8, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG8_1X8, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG8_1X8, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB8_1X8, DCMIPP_ISP_SINK),
+ /* Raw 10 */
+ PIXMAP_MBUS(SBGGR10_1X10, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG10_1X10, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG10_1X10, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB10_1X10, DCMIPP_ISP_SINK),
+ /* Raw 12 */
+ PIXMAP_MBUS(SBGGR12_1X12, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG12_1X12, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG12_1X12, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB12_1X12, DCMIPP_ISP_SINK),
+ /* Raw 14 */
+ PIXMAP_MBUS(SBGGR14_1X14, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGBRG14_1X14, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SGRBG14_1X14, DCMIPP_ISP_SINK),
+ PIXMAP_MBUS(SRGGB14_1X14, DCMIPP_ISP_SINK),
+};
+
+const struct dcmipp_pixelpipe_pix_map *
+dcmipp_pixelpipe_pix_map_by_code(__u32 code, unsigned int id, unsigned int pad)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dcmipp_pixel_formats_list); i++) {
+ if (dcmipp_pixel_formats_list[i].code == code &&
+ dcmipp_pixel_formats_list[i].pipes & DCMIPP_ENT(id, pad))
+ return &dcmipp_pixel_formats_list[i];
+ }
+
+ return NULL;
+}
+
+int dcmipp_pixelpipe_enum_mbus_code(unsigned int id,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ unsigned int index = code->index;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(dcmipp_pixel_formats_list); i++) {
+ if (!(dcmipp_pixel_formats_list[i].pipes &
+ DCMIPP_ENT(id, code->pad)))
+ continue;
+
+ if (index == 0)
+ break;
+
+ index--;
+ }
+
+ if (i == ARRAY_SIZE(dcmipp_pixel_formats_list))
+ return -EINVAL;
+
+ code->code = dcmipp_pixel_formats_list[i].code;
+
+ return 0;
+}
+
+int dcmipp_pixelpipe_enum_frame_size(unsigned int id,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ const struct dcmipp_pixelpipe_pix_map *vpix;
+
+ if (fse->index)
+ return -EINVAL;
+
+ /* Only accept code in the pix map table */
+ vpix = dcmipp_pixelpipe_pix_map_by_code(fse->code, id, fse->pad);
+ if (!vpix)
+ return -EINVAL;
+
+ fse->min_width = DCMIPP_FRAME_MIN_WIDTH;
+ fse->max_width = DCMIPP_FRAME_MAX_WIDTH;
+ fse->min_height = DCMIPP_FRAME_MIN_HEIGHT;
+ fse->max_height = DCMIPP_FRAME_MAX_HEIGHT;
+
+ return 0;
+}
+
+int dcmipp_pixelpipe_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s)
+{
+ struct v4l2_mbus_framefmt *sink_fmt;
+
+ if (IS_SRC(s->pad))
+ return -EINVAL;
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ s->r = *v4l2_subdev_state_get_crop(state, s->pad);
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ sink_fmt = v4l2_subdev_state_get_format(state, s->pad);
+ s->r.top = 0;
+ s->r.left = 0;
+ s->r.width = sink_fmt->width;
+ s->r.height = sink_fmt->height;
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r = *v4l2_subdev_state_get_compose(state, s->pad);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+__u32 dcmipp_pixelpipe_src_format(__u32 input_format)
+{
+ if (input_format >= MEDIA_BUS_FMT_Y8_1X8 &&
+ input_format < MEDIA_BUS_FMT_SBGGR8_1X8)
+ return MEDIA_BUS_FMT_YUV8_1X24;
+
+ return MEDIA_BUS_FMT_RGB888_1X24;
+}
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h
new file mode 100644
index 000000000000..2d7c16c36c7d
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelcommon.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#ifndef _DCMIPP_PIXELCOMMON_H
+#define _DCMIPP_PIXELCOMMON_H
+
+#define IS_SINK(pad) (!(pad))
+#define IS_SRC(pad) ((pad))
+
+#define DCMIPP_ISP 0
+#define DCMIPP_MAIN 1
+#define DCMIPP_AUX 2
+
+struct dcmipp_pixelpipe_pix_map {
+ __u32 code;
+ __u32 pipes;
+};
+
+const struct dcmipp_pixelpipe_pix_map *
+dcmipp_pixelpipe_pix_map_by_code(__u32 code, unsigned int id, unsigned int pad);
+
+int dcmipp_pixelpipe_enum_mbus_code(unsigned int id,
+ struct v4l2_subdev_mbus_code_enum *code);
+
+int dcmipp_pixelpipe_enum_frame_size(unsigned int id,
+ struct v4l2_subdev_frame_size_enum *fse);
+
+int dcmipp_pixelpipe_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s);
+
+__u32 dcmipp_pixelpipe_src_format(__u32 input_format);
+
+#endif
diff --git a/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c
new file mode 100644
index 000000000000..7d8e5f0863bf
--- /dev/null
+++ b/drivers/media/platform/st/stm32/stm32-dcmipp/dcmipp-pixelproc.c
@@ -0,0 +1,942 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for STM32 Digital Camera Memory Interface Pixel Processor
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ * Authors: Hugues Fruchet <hugues.fruchet@foss.st.com>
+ * Alain Volmat <alain.volmat@foss.st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/v4l2-mediabus.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-rect.h>
+#include <media/v4l2-subdev.h>
+#include <uapi/linux/media/st/dcmipp_config.h>
+
+#include "dcmipp-common.h"
+#include "dcmipp-pixelcommon.h"
+
+#define DCMIPP_P1CRSTR 0x904
+#define DCMIPP_P2CRSTR 0xD04
+#define DCMIPP_PxCRSTR(id) (((id) == 1) ? DCMIPP_P1CRSTR :\
+ DCMIPP_P2CRSTR)
+#define DCMIPP_PxCRSTR_HSTART_SHIFT 0
+#define DCMIPP_PxCRSTR_VSTART_SHIFT 16
+#define DCMIPP_P1CRSZR 0x908
+#define DCMIPP_P2CRSZR 0xD08
+#define DCMIPP_PxCRSZR(id) (((id) == 1) ? DCMIPP_P1CRSZR :\
+ DCMIPP_P2CRSZR)
+#define DCMIPP_PxCRSZR_ENABLE BIT(31)
+#define DCMIPP_PxCRSZR_HSIZE_SHIFT 0
+#define DCMIPP_PxCRSZR_VSIZE_SHIFT 16
+
+#define DCMIPP_P1DCCR 0x90C
+#define DCMIPP_P2DCCR 0xD0C
+#define DCMIPP_PxDCCR(id) (((id) == 1) ? DCMIPP_P1DCCR :\
+ DCMIPP_P2DCCR)
+#define DCMIPP_PxDCCR_ENABLE BIT(0)
+#define DCMIPP_PxDCCR_HDEC_SHIFT 1
+#define DCMIPP_PxDCCR_VDEC_SHIFT 3
+
+#define DCMIPP_P1DSCR 0x910
+#define DCMIPP_P2DSCR 0xD10
+#define DCMIPP_PxDSCR(id) (((id) == 1) ? DCMIPP_P1DSCR :\
+ DCMIPP_P2DSCR)
+#define DCMIPP_PxDSCR_HDIV_SHIFT 0
+#define DCMIPP_PxDSCR_VDIV_SHIFT 16
+#define DCMIPP_PxDSCR_ENABLE BIT(31)
+
+#define DCMIPP_P1DSRTIOR 0x914
+#define DCMIPP_P2DSRTIOR 0xD14
+#define DCMIPP_PxDSRTIOR(id) (((id) == 1) ? DCMIPP_P1DSRTIOR :\
+ DCMIPP_P2DSRTIOR)
+#define DCMIPP_PxDSRTIOR_HRATIO_SHIFT 0
+#define DCMIPP_PxDSRTIOR_HRATIO_MASK GENMASK(15, 0)
+#define DCMIPP_PxDSRTIOR_VRATIO_SHIFT 16
+#define DCMIPP_PxDSRTIOR_VRATIO_MASK GENMASK(31, 16)
+
+#define DCMIPP_P1DSSZR 0x918
+#define DCMIPP_P2DSSZR 0xD18
+#define DCMIPP_PxDSSZR(id) (((id) == 1) ? DCMIPP_P1DSSZR :\
+ DCMIPP_P2DSSZR)
+#define DCMIPP_PxDSSZR_HSIZE_SHIFT 0
+#define DCMIPP_PxDSSZR_HSIZE_MASK GENMASK(11, 0)
+#define DCMIPP_PxDSSZR_VSIZE_SHIFT 16
+#define DCMIPP_PxDSSZR_VSIZE_MASK GENMASK(27, 16)
+
+#define DCMIPP_P1GMCR 0x970
+#define DCMIPP_P2GMCR 0xD70
+#define DCMIPP_PxGMCR(id) (((id) == 1) ? DCMIPP_P1GMCR :\
+ DCMIPP_P2GMCR)
+#define DCMIPP_PxGMCR_ENABLE BIT(0)
+
+#define DCMIPP_P1YUVCR 0x980
+#define DCMIPP_P1YUVCR_ENABLE BIT(0)
+#define DCMIPP_P1YUVCR_TYPE_RGB BIT(1)
+#define DCMIPP_P1YUVCR_CLAMP BIT(2)
+#define DCMIPP_P1YUVRR1 0x984
+#define DCMIPP_P1YUVRR2 0x988
+#define DCMIPP_P1YUVGR1 0x98C
+#define DCMIPP_P1YUVGR2 0x990
+#define DCMIPP_P1YUVBR1 0x994
+#define DCMIPP_P1YUVBR2 0x998
+
+#define PIXELPROC_MEDIA_BUS_FMT_DEFAULT MEDIA_BUS_FMT_RGB888_1X24
+
+/* Macro for negative coefficient, 11 bits coded */
+#define N11(val) (((val) ^ 0x7ff) + 1)
+/* Macro for added value, 10 bits coded */
+#define N10(val) (((val) ^ 0x3ff) + 1)
+
+/* Macro to convert row matrix to DCMIPP PxCCCyy register value */
+#define CCTBL(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba) \
+ .conv_matrix = { \
+ ((rg) << 16 | (rr)), ((ra) << 16 | (rb)), \
+ ((gg) << 16 | (gr)), ((ga) << 16 | (gb)), \
+ ((bg) << 16 | (br)), ((ba) << 16 | (bb)) }
+
+struct dcmipp_colorconv_config {
+ unsigned int conv_matrix[6];
+ bool clamping;
+ bool clamping_as_rgb;
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv601full = {
+ /* R G B Add */
+ CCTBL(131, N11(110), N11(21), 128, /* Cr */
+ 77, 150, 29, 0, /* Y */
+ N11(44), N11(87), 131, 128), /* Cb */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv601lim = {
+ /* R G B Add */
+ CCTBL(112, N11(94), N11(18), 128, /* Cr */
+ 66, 129, 25, 16, /* Y */
+ N11(38), N11(74), 112, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv709full = {
+ /* R G B Add */
+ CCTBL(131, N11(119), N11(12), 128, /* Cr */
+ 55, 183, 18, 0, /* Y */
+ N11(30), N11(101), 131, 128), /* Cb */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgbfull_to_yuv709lim = {
+ /* R G B Add */
+ CCTBL(112, N11(102), N11(10), 128, /* Cr */
+ 47, 157, 16, 16, /* Y */
+ N11(26), N11(87), 112, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgblim_to_yuv601lim = {
+ /* R G B Add */
+ CCTBL(131, N11(110), N11(21), 128, /* Cr */
+ 77, 150, 29, 0, /* Y */
+ N11(44), N11(87), 131, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_rgblim_to_yuv709lim = {
+ /* R G B Add */
+ CCTBL(131, N11(119), N11(12), 128, /* Cr */
+ 55, 183, 18, 0, /* Y */
+ N11(30), N11(101), 131, 128), /* Cb */
+ .clamping = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv601full_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(351, 256, 0, N10(175), /* R */
+ N11(179), 256, N11(86), 132, /* G */
+ 0, 256, 443, N10(222)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv601lim_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(409, 298, 0, N10(223), /* R */
+ N11(208), 298, N11(100), 135, /* G */
+ 0, 298, 517, N10(277)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv601lim_to_rgblim = {
+ /* Cr Y Cb Add */
+ CCTBL(351, 256, 0, N10(175), /* R */
+ N11(179), 256, N11(86), 132, /* G */
+ 0, 256, 443, N10(222)), /* B */
+ .clamping = true,
+ .clamping_as_rgb = true,
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv709full_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(394, 256, 0, N10(197), /* R */
+ N11(118), 256, N11(47), 82, /* G */
+ 0, 256, 456, N10(232)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv709lim_to_rgbfull = {
+ /* Cr Y Cb Add */
+ CCTBL(459, 298, 0, N10(248), /* R */
+ N11(137), 298, N11(55), 77, /* G */
+ 0, 298, 541, N10(289)), /* B */
+};
+
+static const struct dcmipp_colorconv_config dcmipp_yuv709lim_to_rgblim = {
+ /* Cr Y Cb Add */
+ CCTBL(394, 256, 0, N10(197), /* R */
+ N11(118), 256, N11(47), 82, /* G */
+ 0, 256, 465, N10(232)), /* B */
+ .clamping = true,
+ .clamping_as_rgb = true,
+};
+
+/* cconv_matrices[src_fmt][src_range][sink_fmt][sink_range] */
+static const struct dcmipp_colorconv_config *dcmipp_cconv_cfgs[3][2][3][2] = {
+ /* RGB */
+ {
+ /* RGB full range */
+ {
+ /* RGB full range => RGB */
+ {
+ NULL, NULL,
+ },
+ /* RGB full range => YUV601 */
+ {
+ &dcmipp_rgbfull_to_yuv601full,
+ &dcmipp_rgbfull_to_yuv601lim,
+ },
+ /* RGB full range => YUV709 */
+ {
+ &dcmipp_rgbfull_to_yuv709full,
+ &dcmipp_rgbfull_to_yuv709lim,
+ },
+ },
+ /* RGB limited range */
+ {
+ /* RGB limited range => RGB */
+ {
+ NULL, NULL,
+ },
+ /* RGB limited range => YUV601 */
+ {
+ NULL, &dcmipp_rgblim_to_yuv601lim,
+ },
+ /* RGB limited range => YUV709 */
+ {
+ NULL, &dcmipp_rgblim_to_yuv709lim,
+ },
+ },
+ },
+ /* YUV601 */
+ {
+ /* YUV601 full range */
+ {
+ /* YUV601 full range => RGB */
+ {
+ &dcmipp_yuv601full_to_rgbfull, NULL,
+ },
+ /* YUV601 full range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV601 full range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ /* YUV601 limited range */
+ {
+ /* YUV601 limited range => RGB */
+ {
+ &dcmipp_yuv601lim_to_rgbfull,
+ &dcmipp_yuv601lim_to_rgblim,
+ },
+ /* YUV601 limited range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV601 limited range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ },
+ /* YUV709 */
+ {
+ /* YUV709 full range */
+ {
+ /* YUV709 full range => RGB */
+ {
+ &dcmipp_yuv709full_to_rgbfull, NULL,
+ },
+ /* YUV709 full range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV709 full range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ /* YUV709 limited range */
+ {
+ /* YUV709 limited range => RGB */
+ {
+ &dcmipp_yuv709lim_to_rgbfull,
+ &dcmipp_yuv709lim_to_rgblim,
+ },
+ /* YUV709 limited range => YUV601 */
+ {
+ NULL, NULL,
+ },
+ /* YUV709 limited range => YUV709 */
+ {
+ NULL, NULL,
+ },
+ },
+ },
+};
+
+enum dcmipp_cconv_fmt {
+ FMT_RGB = 0,
+ FMT_YUV601,
+ FMT_YUV709
+};
+
+static inline enum dcmipp_cconv_fmt to_cconv_fmt(struct v4l2_mbus_framefmt *fmt)
+{
+ /* YUV format codes are within the 0x2xxx */
+ if (fmt->code >= MEDIA_BUS_FMT_Y8_1X8 &&
+ fmt->code < MEDIA_BUS_FMT_SBGGR8_1X8) {
+ if (fmt->ycbcr_enc == V4L2_YCBCR_ENC_709)
+ return FMT_YUV709;
+ else
+ return FMT_YUV601;
+ }
+
+ /* All other formats are referred as RGB, indeed, demosaicing bloc
+ * generate RGB format
+ */
+ return FMT_RGB;
+};
+
+#define FMT_STR(f) ({ \
+ typeof(f) __f = (f); \
+ (__f) == FMT_RGB ? "RGB" : \
+ (__f) == FMT_YUV601 ? "YUV601" : \
+ (__f) == FMT_YUV709 ? "YUV709" : "?"; })
+
+enum dcmipp_cconv_range {
+ RANGE_FULL = 0,
+ RANGE_LIMITED,
+};
+
+static inline enum dcmipp_cconv_range
+to_cconv_range(struct v4l2_mbus_framefmt *fmt)
+{
+ if (fmt->quantization == V4L2_QUANTIZATION_FULL_RANGE)
+ return RANGE_FULL;
+
+ return RANGE_LIMITED;
+};
+
+#define RANGE_STR(range) ((range) == RANGE_FULL ? "full" : "limited")
+
+struct dcmipp_pixelproc_device {
+ struct dcmipp_ent_device ved;
+ struct v4l2_subdev sd;
+ struct device *dev;
+ bool streaming;
+
+ void __iomem *regs;
+ struct v4l2_ctrl_handler ctrls;
+
+ u32 pipe_id;
+};
+
+static const struct v4l2_mbus_framefmt fmt_default = {
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ .code = PIXELPROC_MEDIA_BUS_FMT_DEFAULT,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = DCMIPP_COLORSPACE_DEFAULT,
+ .ycbcr_enc = DCMIPP_YCBCR_ENC_DEFAULT,
+ .quantization = DCMIPP_QUANTIZATION_DEFAULT,
+ .xfer_func = DCMIPP_XFER_FUNC_DEFAULT,
+};
+
+static const struct v4l2_rect min_rect = {
+ .width = DCMIPP_FRAME_MIN_WIDTH,
+ .height = DCMIPP_FRAME_MIN_HEIGHT,
+ .top = 0,
+ .left = 0,
+};
+
+/*
+ * Downscale is a combination of both decimation block (1/2/4/8)
+ * and downsize block (up to 8x) for a total of maximum downscale of 64
+ */
+#define DCMIPP_MAX_DECIMATION_RATIO 8
+#define DCMIPP_MAX_DOWNSIZE_RATIO 8
+#define DCMIPP_MAX_DOWNSCALE_RATIO 64
+
+/*
+ * Functions handling controls
+ */
+static int dcmipp_pixelproc_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct dcmipp_pixelproc_device *pixelproc =
+ container_of(ctrl->handler,
+ struct dcmipp_pixelproc_device, ctrls);
+
+ if (!pm_runtime_get_if_in_use(pixelproc->dev))
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_DCMIPP_PIXELPROC_GAMMA_CORRECTION_ENABLE:
+ reg_write(pixelproc, DCMIPP_PxGMCR(pixelproc->pipe_id),
+ (ctrl->val ? DCMIPP_PxGMCR_ENABLE : 0));
+ break;
+ }
+
+ pm_runtime_put(pixelproc->dev);
+
+ return 0;
+};
+
+static const struct v4l2_ctrl_ops dcmipp_pixelproc_ctrl_ops = {
+ .s_ctrl = dcmipp_pixelproc_s_ctrl,
+};
+
+static const struct v4l2_ctrl_config dcmipp_pixelproc_ctrls[] = {
+ {
+ .ops = &dcmipp_pixelproc_ctrl_ops,
+ .id = V4L2_CID_DCMIPP_PIXELPROC_GAMMA_CORRECTION_ENABLE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Gamma Correction Enable",
+ .min = 0,
+ .max = 1,
+ .step = 1,
+ .def = 0,
+ }
+};
+
+static void dcmipp_pixelproc_adjust_crop(struct v4l2_rect *r,
+ const struct v4l2_mbus_framefmt *fmt)
+{
+ struct v4l2_rect src_rect = {
+ .top = 0,
+ .left = 0,
+ .width = fmt->width,
+ .height = fmt->height,
+ };
+
+ /* Disallow rectangles smaller than the minimal one. */
+ v4l2_rect_set_min_size(r, &min_rect);
+ v4l2_rect_map_inside(r, &src_rect);
+}
+
+static void
+dcmipp_pixelproc_adjust_fmt(struct dcmipp_pixelproc_device *pixelproc,
+ struct v4l2_mbus_framefmt *fmt, u32 pad)
+{
+ const struct dcmipp_pixelpipe_pix_map *vpix;
+
+ /* Only accept code in the pix map table */
+ vpix = dcmipp_pixelpipe_pix_map_by_code(fmt->code,
+ pixelproc->pipe_id == 1 ? DCMIPP_MAIN : DCMIPP_AUX,
+ pad);
+ if (!vpix)
+ fmt->code = PIXELPROC_MEDIA_BUS_FMT_DEFAULT;
+
+ fmt->width = clamp_t(u32, fmt->width, DCMIPP_FRAME_MIN_WIDTH,
+ DCMIPP_FRAME_MAX_WIDTH);
+ fmt->height = clamp_t(u32, fmt->height, DCMIPP_FRAME_MIN_HEIGHT,
+ DCMIPP_FRAME_MAX_HEIGHT);
+
+ if (fmt->field == V4L2_FIELD_ANY || fmt->field == V4L2_FIELD_ALTERNATE)
+ fmt->field = V4L2_FIELD_NONE;
+
+ dcmipp_colorimetry_clamp(fmt);
+}
+
+static int dcmipp_pixelproc_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+{
+ unsigned int i;
+
+ for (i = 0; i < sd->entity.num_pads; i++) {
+ *v4l2_subdev_state_get_format(state, i) = fmt_default;
+
+ if (IS_SINK(i)) {
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = DCMIPP_FMT_WIDTH_DEFAULT,
+ .height = DCMIPP_FMT_HEIGHT_DEFAULT,
+ };
+ *v4l2_subdev_state_get_crop(state, i) = r;
+ *v4l2_subdev_state_get_compose(state, i) = r;
+ }
+ }
+
+ return 0;
+}
+
+static int
+dcmipp_pixelproc_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ return dcmipp_pixelpipe_enum_mbus_code(pixelproc->pipe_id == 1 ? DCMIPP_MAIN : DCMIPP_AUX,
+ code);
+}
+
+static int
+dcmipp_pixelproc_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ return dcmipp_pixelpipe_enum_frame_size(pixelproc->pipe_id == 1 ? DCMIPP_MAIN : DCMIPP_AUX,
+ fse);
+}
+
+static int dcmipp_pixelproc_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *fmt)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ if (v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ dcmipp_pixelproc_adjust_fmt(pixelproc, &fmt->format, fmt->pad);
+
+ if (IS_SINK(fmt->pad)) {
+ struct v4l2_mbus_framefmt *src_fmt =
+ v4l2_subdev_state_get_format(state, 1);
+ struct v4l2_rect r = {
+ .top = 0,
+ .left = 0,
+ .width = fmt->format.width,
+ .height = fmt->format.height,
+ };
+
+ /* Adjust SINK pad crop/compose */
+ *v4l2_subdev_state_get_crop(state, 0) = r;
+ *v4l2_subdev_state_get_compose(state, 0) = r;
+
+ /* Forward format to SRC pad */
+ *src_fmt = fmt->format;
+ src_fmt->code = dcmipp_pixelpipe_src_format(fmt->format.code);
+ } else {
+ struct v4l2_rect *compose =
+ v4l2_subdev_state_get_compose(state, 0);
+
+ /* AUX (pipe_nb 2) cannot perform color conv */
+ if (pixelproc->pipe_id == 2) {
+ struct v4l2_mbus_framefmt *sink_fmt =
+ v4l2_subdev_state_get_format(state, 0);
+
+ fmt->format = *sink_fmt;
+ fmt->format.code =
+ dcmipp_pixelpipe_src_format(fmt->format.code);
+ }
+
+ fmt->format.width = compose->width;
+ fmt->format.height = compose->height;
+ }
+
+ /* Update the selected pad format */
+ *v4l2_subdev_state_get_format(state, fmt->pad) = fmt->format;
+
+ return 0;
+}
+
+static int dcmipp_pixelproc_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_selection *s)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+ struct v4l2_mbus_framefmt *sink_fmt, *src_fmt;
+ struct v4l2_rect *crop, *compose;
+
+ if (IS_SRC(s->pad))
+ return -EINVAL;
+
+ if (v4l2_subdev_is_streaming(sd))
+ return -EBUSY;
+
+ crop = v4l2_subdev_state_get_crop(state, s->pad);
+ compose = v4l2_subdev_state_get_compose(state, s->pad);
+
+ switch (s->target) {
+ case V4L2_SEL_TGT_CROP:
+ sink_fmt = v4l2_subdev_state_get_format(state, s->pad);
+ dcmipp_pixelproc_adjust_crop(&s->r, sink_fmt);
+
+ *crop = s->r;
+ *compose = s->r;
+
+ dev_dbg(pixelproc->dev, "s_selection: crop (%d,%d)/%ux%u\n",
+ crop->left, crop->top, crop->width, crop->height);
+ break;
+ case V4L2_SEL_TGT_COMPOSE:
+ s->r.top = 0;
+ s->r.left = 0;
+ s->r.width = clamp_t(u32, s->r.width,
+ crop->width / DCMIPP_MAX_DOWNSCALE_RATIO,
+ crop->width);
+ s->r.height = clamp_t(u32, s->r.height,
+ crop->height / DCMIPP_MAX_DOWNSCALE_RATIO,
+ crop->height);
+ v4l2_rect_set_min_size(&s->r, &min_rect);
+ *compose = s->r;
+
+ dev_dbg(pixelproc->dev, "s_selection: compose (%d,%d)/%ux%u\n",
+ compose->left, compose->top,
+ compose->width, compose->height);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Update the source pad size */
+ src_fmt = v4l2_subdev_state_get_format(state, 1);
+ src_fmt->width = s->r.width;
+ src_fmt->height = s->r.height;
+
+ return 0;
+}
+
+static int
+dcmipp_pixelproc_colorconv_config(struct dcmipp_pixelproc_device *pixelproc,
+ struct v4l2_mbus_framefmt *sink,
+ struct v4l2_mbus_framefmt *src)
+{
+ const struct dcmipp_colorconv_config *cconv_cfg;
+ enum dcmipp_cconv_fmt sink_fmt = to_cconv_fmt(sink);
+ enum dcmipp_cconv_range sink_range = to_cconv_range(sink);
+ enum dcmipp_cconv_fmt src_fmt = to_cconv_fmt(src);
+ enum dcmipp_cconv_range src_range = to_cconv_range(src);
+ unsigned int val = 0;
+ int i;
+
+ /* Disable color conversion by default */
+ reg_write(pixelproc, DCMIPP_P1YUVCR, 0);
+
+ if (sink_fmt == src_fmt && sink_range == src_range)
+ return 0;
+
+ /* color conversion */
+ cconv_cfg = dcmipp_cconv_cfgs[sink_fmt][sink_range][src_fmt][src_range];
+ if (!cconv_cfg) {
+ dev_err(pixelproc->dev,
+ "Unsupported color conversion %s-%s => %s-%s\n",
+ FMT_STR(sink_fmt), RANGE_STR(sink_range),
+ FMT_STR(src_fmt), RANGE_STR(src_range));
+ return -EINVAL;
+ }
+
+ dev_dbg(pixelproc->dev, "color conversion %s-%s => %s-%s\n",
+ FMT_STR(sink_fmt), RANGE_STR(sink_range),
+ FMT_STR(src_fmt), RANGE_STR(src_range));
+
+ for (i = 0; i < 6; i++)
+ reg_write(pixelproc, DCMIPP_P1YUVRR1 + (4 * i),
+ cconv_cfg->conv_matrix[i]);
+
+ if (cconv_cfg->clamping)
+ val |= DCMIPP_P1YUVCR_CLAMP;
+ if (cconv_cfg->clamping_as_rgb)
+ val |= DCMIPP_P1YUVCR_TYPE_RGB;
+ val |= DCMIPP_P1YUVCR_ENABLE;
+
+ reg_write(pixelproc, DCMIPP_P1YUVCR, val);
+
+ return 0;
+}
+
+#define DCMIPP_PIXELPROC_HVRATIO_CONS 8192
+#define DCMIPP_PIXELPROC_HVRATIO_MAX 65535
+#define DCMIPP_PIXELPROC_HVDIV_CONS 1024
+#define DCMIPP_PIXELPROC_HVDIV_MAX 1023
+static void
+dcmipp_pixelproc_set_crop_downscale(struct dcmipp_pixelproc_device *pixelproc,
+ struct v4l2_rect *compose,
+ struct v4l2_rect *crop)
+{
+ unsigned int hratio, vratio, hdiv, vdiv;
+ unsigned int hdec = 0, vdec = 0;
+ unsigned int h_post_dec = crop->width;
+ unsigned int v_post_dec = crop->height;
+
+ /* Configure cropping */
+ reg_write(pixelproc, DCMIPP_PxCRSTR(pixelproc->pipe_id),
+ (crop->top << DCMIPP_PxCRSTR_VSTART_SHIFT) |
+ (crop->left << DCMIPP_PxCRSTR_HSTART_SHIFT));
+ reg_write(pixelproc, DCMIPP_PxCRSZR(pixelproc->pipe_id),
+ (crop->width << DCMIPP_PxCRSZR_HSIZE_SHIFT) |
+ (crop->height << DCMIPP_PxCRSZR_VSIZE_SHIFT) |
+ DCMIPP_PxCRSZR_ENABLE);
+
+ /* Compute decimation factors (HDEC/VDEC) */
+ while (compose->width * DCMIPP_MAX_DOWNSIZE_RATIO < h_post_dec) {
+ hdec++;
+ h_post_dec /= 2;
+ }
+ while (compose->height * DCMIPP_MAX_DOWNSIZE_RATIO < v_post_dec) {
+ vdec++;
+ v_post_dec /= 2;
+ }
+
+ /* Compute downsize factor */
+ hratio = h_post_dec * DCMIPP_PIXELPROC_HVRATIO_CONS /
+ compose->width;
+ if (hratio > DCMIPP_PIXELPROC_HVRATIO_MAX)
+ hratio = DCMIPP_PIXELPROC_HVRATIO_MAX;
+ vratio = v_post_dec * DCMIPP_PIXELPROC_HVRATIO_CONS /
+ compose->height;
+ if (vratio > DCMIPP_PIXELPROC_HVRATIO_MAX)
+ vratio = DCMIPP_PIXELPROC_HVRATIO_MAX;
+ hdiv = (DCMIPP_PIXELPROC_HVDIV_CONS * compose->width) /
+ h_post_dec;
+ if (hdiv > DCMIPP_PIXELPROC_HVDIV_MAX)
+ hdiv = DCMIPP_PIXELPROC_HVDIV_MAX;
+ vdiv = (DCMIPP_PIXELPROC_HVDIV_CONS * compose->height) /
+ v_post_dec;
+ if (vdiv > DCMIPP_PIXELPROC_HVDIV_MAX)
+ vdiv = DCMIPP_PIXELPROC_HVDIV_MAX;
+
+ dev_dbg(pixelproc->dev, "%s: decimation config: hdec: 0x%x, vdec: 0x%x\n",
+ pixelproc->sd.name,
+ hdec, vdec);
+ dev_dbg(pixelproc->dev, "%s: downsize config: hratio: 0x%x, vratio: 0x%x, hdiv: 0x%x, vdiv: 0x%x\n",
+ pixelproc->sd.name,
+ hratio, vratio,
+ hdiv, vdiv);
+
+ reg_clear(pixelproc, DCMIPP_PxDCCR(pixelproc->pipe_id),
+ DCMIPP_PxDCCR_ENABLE);
+ if (hdec || vdec)
+ reg_write(pixelproc, DCMIPP_PxDCCR(pixelproc->pipe_id),
+ (hdec << DCMIPP_PxDCCR_HDEC_SHIFT) |
+ (vdec << DCMIPP_PxDCCR_VDEC_SHIFT) |
+ DCMIPP_PxDCCR_ENABLE);
+
+ reg_clear(pixelproc, DCMIPP_PxDSCR(pixelproc->pipe_id),
+ DCMIPP_PxDSCR_ENABLE);
+ reg_write(pixelproc, DCMIPP_PxDSRTIOR(pixelproc->pipe_id),
+ (hratio << DCMIPP_PxDSRTIOR_HRATIO_SHIFT) |
+ (vratio << DCMIPP_PxDSRTIOR_VRATIO_SHIFT));
+ reg_write(pixelproc, DCMIPP_PxDSSZR(pixelproc->pipe_id),
+ (compose->width << DCMIPP_PxDSSZR_HSIZE_SHIFT) |
+ (compose->height << DCMIPP_PxDSSZR_VSIZE_SHIFT));
+ reg_write(pixelproc, DCMIPP_PxDSCR(pixelproc->pipe_id),
+ (hdiv << DCMIPP_PxDSCR_HDIV_SHIFT) |
+ (vdiv << DCMIPP_PxDSCR_VDIV_SHIFT) |
+ DCMIPP_PxDSCR_ENABLE);
+}
+
+static int dcmipp_pixelproc_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ /* Configure crop/downscale */
+ dcmipp_pixelproc_set_crop_downscale(pixelproc,
+ v4l2_subdev_state_get_compose(state, 0),
+ v4l2_subdev_state_get_crop(state, 0));
+
+ /* Configure YUV Conversion (if applicable) */
+ if (pixelproc->pipe_id == 1) {
+ ret = dcmipp_pixelproc_colorconv_config(pixelproc,
+ v4l2_subdev_state_get_format(state, 0),
+ v4l2_subdev_state_get_format(state, 1));
+ if (ret)
+ return ret;
+ }
+
+ /* Apply customized values from user when stream starts. */
+ ret = v4l2_ctrl_handler_setup(pixelproc->sd.ctrl_handler);
+ if (ret < 0) {
+ dev_err(pixelproc->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ ret = v4l2_subdev_enable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0) {
+ dev_err(pixelproc->dev,
+ "failed to start source subdev streaming (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dcmipp_pixelproc_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+ struct v4l2_subdev *s_subdev;
+ struct media_pad *s_pad;
+ int ret;
+
+ /* Get source subdev */
+ s_pad = media_pad_remote_pad_first(&sd->entity.pads[0]);
+ if (!s_pad || !is_media_entity_v4l2_subdev(s_pad->entity))
+ return -EINVAL;
+ s_subdev = media_entity_to_v4l2_subdev(s_pad->entity);
+
+ ret = v4l2_subdev_disable_streams(s_subdev, s_pad->index, BIT_ULL(0));
+ if (ret < 0)
+ dev_err(pixelproc->dev,
+ "failed to stop source subdev streaming (%d)\n",
+ ret);
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops dcmipp_pixelproc_pad_ops = {
+ .enum_mbus_code = dcmipp_pixelproc_enum_mbus_code,
+ .enum_frame_size = dcmipp_pixelproc_enum_frame_size,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = dcmipp_pixelproc_set_fmt,
+ .get_selection = dcmipp_pixelpipe_get_selection,
+ .set_selection = dcmipp_pixelproc_set_selection,
+ .enable_streams = dcmipp_pixelproc_enable_streams,
+ .disable_streams = dcmipp_pixelproc_disable_streams,
+};
+
+static const struct v4l2_subdev_core_ops dcmipp_pixelproc_core_ops = {
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
+};
+
+static const struct v4l2_subdev_video_ops dcmipp_pixelproc_video_ops = {
+ .s_stream = v4l2_subdev_s_stream_helper,
+};
+
+static const struct v4l2_subdev_ops dcmipp_pixelproc_ops = {
+ .core = &dcmipp_pixelproc_core_ops,
+ .pad = &dcmipp_pixelproc_pad_ops,
+ .video = &dcmipp_pixelproc_video_ops,
+};
+
+static void dcmipp_pixelproc_release(struct v4l2_subdev *sd)
+{
+ struct dcmipp_pixelproc_device *pixelproc = v4l2_get_subdevdata(sd);
+
+ v4l2_ctrl_handler_free(&pixelproc->ctrls);
+ kfree(pixelproc);
+}
+
+static const struct v4l2_subdev_internal_ops dcmipp_pixelproc_int_ops = {
+ .init_state = dcmipp_pixelproc_init_state,
+ .release = dcmipp_pixelproc_release,
+};
+
+void dcmipp_pixelproc_ent_release(struct dcmipp_ent_device *ved)
+{
+ struct dcmipp_pixelproc_device *pixelproc =
+ container_of(ved, struct dcmipp_pixelproc_device, ved);
+
+ dcmipp_ent_sd_unregister(ved, &pixelproc->sd);
+}
+
+static int dcmipp_name_to_pipe_id(const char *name)
+{
+ if (strstr(name, "main"))
+ return 1;
+ else if (strstr(name, "aux"))
+ return 2;
+ else
+ return -EINVAL;
+}
+
+struct dcmipp_ent_device *
+dcmipp_pixelproc_ent_init(const char *entity_name,
+ struct dcmipp_device *dcmipp)
+{
+ struct dcmipp_pixelproc_device *pixelproc;
+ const unsigned long pads_flag[] = {
+ MEDIA_PAD_FL_SINK, MEDIA_PAD_FL_SOURCE,
+ };
+ int ret, i;
+
+ /* Allocate the pixelproc struct */
+ pixelproc = kzalloc_obj(*pixelproc);
+ if (!pixelproc)
+ return ERR_PTR(-ENOMEM);
+
+ pixelproc->regs = dcmipp->regs;
+ pixelproc->dev = dcmipp->dev;
+
+ /* Pipe identifier */
+ pixelproc->pipe_id = dcmipp_name_to_pipe_id(entity_name);
+ if (pixelproc->pipe_id != 1 && pixelproc->pipe_id != 2) {
+ dev_err(pixelproc->dev, "failed to retrieve pipe_id\n");
+ ret = -EIO;
+ goto err_kfree;
+ }
+
+ /* Initialize controls */
+ v4l2_ctrl_handler_init(&pixelproc->ctrls,
+ ARRAY_SIZE(dcmipp_pixelproc_ctrls));
+
+ for (i = 0; i < ARRAY_SIZE(dcmipp_pixelproc_ctrls); i++)
+ v4l2_ctrl_new_custom(&pixelproc->ctrls,
+ &dcmipp_pixelproc_ctrls[i], NULL);
+
+ pixelproc->sd.ctrl_handler = &pixelproc->ctrls;
+ if (pixelproc->ctrls.error) {
+ ret = pixelproc->ctrls.error;
+ dev_err(pixelproc->dev, "control initialization error %d\n", ret);
+ goto err_ctrl_handler_free;
+ }
+
+ /* Initialize ved and sd */
+ ret = dcmipp_ent_sd_register(&pixelproc->ved, &pixelproc->sd,
+ &dcmipp->v4l2_dev, entity_name,
+ MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER,
+ ARRAY_SIZE(pads_flag), pads_flag,
+ &dcmipp_pixelproc_int_ops,
+ &dcmipp_pixelproc_ops,
+ NULL, NULL);
+ if (ret)
+ goto err_ctrl_handler_free;
+
+ pixelproc->ved.dcmipp = dcmipp;
+
+ return &pixelproc->ved;
+
+err_ctrl_handler_free:
+ v4l2_ctrl_handler_free(&pixelproc->ctrls);
+err_kfree:
+ kfree(pixelproc);
+
+ return ERR_PTR(ret);
+}
diff --git a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
index 744197b0fccb..af35ece1d5cf 100644
--- a/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
+++ b/drivers/media/platform/sunxi/sun4i-csi/sun4i_v4l2.c
@@ -295,6 +295,7 @@ static int sun4i_csi_subdev_get_fmt(struct v4l2_subdev *subdev,
}
static int sun4i_csi_subdev_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
index d006d9dd0170..f9ee94fc1293 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.c
@@ -13,26 +13,6 @@
#include "sun6i_csi_bridge.h"
#include "sun6i_csi_reg.h"
-/* Helpers */
-
-void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev,
- unsigned int *width, unsigned int *height)
-{
- if (width)
- *width = csi_dev->bridge.mbus_format.width;
- if (height)
- *height = csi_dev->bridge.mbus_format.height;
-}
-
-void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev,
- u32 *mbus_code, u32 *field)
-{
- if (mbus_code)
- *mbus_code = csi_dev->bridge.mbus_format.code;
- if (field)
- *field = csi_dev->bridge.mbus_format.field;
-}
-
/* Format */
static const struct sun6i_csi_bridge_format sun6i_csi_bridge_formats[] = {
@@ -226,7 +206,8 @@ static void sun6i_csi_bridge_disable(struct sun6i_csi_device *csi_dev)
}
static void
-sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev)
+sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev,
+ const struct v4l2_mbus_framefmt *mbus_format)
{
struct device *dev = csi_dev->dev;
struct regmap *regmap = csi_dev->regmap;
@@ -234,11 +215,9 @@ sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev)
&csi_dev->bridge.source_parallel.endpoint;
unsigned char bus_width = endpoint->bus.parallel.bus_width;
unsigned int flags = endpoint->bus.parallel.flags;
- u32 field;
+ u32 field = mbus_format->field;
u32 value = SUN6I_CSI_IF_CFG_IF_CSI;
- sun6i_csi_bridge_format(csi_dev, NULL, &field);
-
if (field == V4L2_FIELD_INTERLACED ||
field == V4L2_FIELD_INTERLACED_TB ||
field == V4L2_FIELD_INTERLACED_BT)
@@ -317,13 +296,12 @@ sun6i_csi_bridge_configure_parallel(struct sun6i_csi_device *csi_dev)
}
static void
-sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev)
+sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev,
+ const struct v4l2_mbus_framefmt *mbus_format)
{
struct regmap *regmap = csi_dev->regmap;
u32 value = SUN6I_CSI_IF_CFG_IF_MIPI;
- u32 field;
-
- sun6i_csi_bridge_format(csi_dev, NULL, &field);
+ u32 field = mbus_format->field;
if (field == V4L2_FIELD_INTERLACED ||
field == V4L2_FIELD_INTERLACED_TB ||
@@ -335,19 +313,20 @@ sun6i_csi_bridge_configure_mipi_csi2(struct sun6i_csi_device *csi_dev)
regmap_write(regmap, SUN6I_CSI_IF_CFG_REG, value);
}
-static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev)
+static void
+sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev,
+ const struct v4l2_mbus_framefmt *mbus_format)
{
struct regmap *regmap = csi_dev->regmap;
bool capture_streaming = csi_dev->capture.state.streaming;
const struct sun6i_csi_bridge_format *bridge_format;
const struct sun6i_csi_capture_format *capture_format;
- u32 mbus_code, field, pixelformat;
+ u32 pixelformat;
+ u32 field = mbus_format->field;
u8 input_format, input_yuv_seq, output_format;
u32 value = 0;
- sun6i_csi_bridge_format(csi_dev, &mbus_code, &field);
-
- bridge_format = sun6i_csi_bridge_format_find(mbus_code);
+ bridge_format = sun6i_csi_bridge_format_find(mbus_format->code);
if (WARN_ON(!bridge_format))
return;
@@ -391,16 +370,17 @@ static void sun6i_csi_bridge_configure_format(struct sun6i_csi_device *csi_dev)
}
static void sun6i_csi_bridge_configure(struct sun6i_csi_device *csi_dev,
- struct sun6i_csi_bridge_source *source)
+ struct sun6i_csi_bridge_source *source,
+ const struct v4l2_mbus_framefmt *mbus_format)
{
struct sun6i_csi_bridge *bridge = &csi_dev->bridge;
if (source == &bridge->source_parallel)
- sun6i_csi_bridge_configure_parallel(csi_dev);
+ sun6i_csi_bridge_configure_parallel(csi_dev, mbus_format);
else
- sun6i_csi_bridge_configure_mipi_csi2(csi_dev);
+ sun6i_csi_bridge_configure_mipi_csi2(csi_dev, mbus_format);
- sun6i_csi_bridge_configure_format(csi_dev);
+ sun6i_csi_bridge_configure_format(csi_dev, mbus_format);
}
/* V4L2 Subdev */
@@ -415,6 +395,8 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
struct sun6i_csi_bridge_source *source;
struct v4l2_subdev *source_subdev;
struct media_pad *remote_pad;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *mbus_format;
int ret;
/* Source */
@@ -433,6 +415,10 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
else
source = &bridge->source_mipi_csi2;
+ /* Active State */
+
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
if (!on) {
v4l2_subdev_call(source_subdev, video, s_stream, 0);
ret = 0;
@@ -443,7 +429,7 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
- return ret;
+ goto unlock;
/* Clear */
@@ -451,7 +437,9 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
/* Configure */
- sun6i_csi_bridge_configure(csi_dev, source);
+ mbus_format = v4l2_subdev_state_get_format(state,
+ SUN6I_CSI_BRIDGE_PAD_SINK);
+ sun6i_csi_bridge_configure(csi_dev, source, mbus_format);
if (capture_streaming)
sun6i_csi_capture_configure(csi_dev);
@@ -472,7 +460,8 @@ static int sun6i_csi_bridge_s_stream(struct v4l2_subdev *subdev, int on)
if (ret && ret != -ENOIOCTLCMD)
goto disable;
- return 0;
+ ret = 0;
+ goto unlock;
disable:
if (capture_streaming)
@@ -482,6 +471,8 @@ disable:
pm_runtime_put(dev);
+unlock:
+ v4l2_subdev_unlock_state(state);
return ret;
}
@@ -504,21 +495,23 @@ sun6i_csi_bridge_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
static int sun6i_csi_bridge_init_state(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state)
{
- struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
- unsigned int pad = SUN6I_CSI_BRIDGE_PAD_SINK;
- struct v4l2_mbus_framefmt *mbus_format =
- v4l2_subdev_state_get_format(state, pad);
- struct mutex *lock = &csi_dev->bridge.lock;
+ unsigned int pad;
- mutex_lock(lock);
+ /*
+ * This subdev does not perform format conversion,
+ * initialize both pads identically.
+ */
+ for (pad = 0; pad < subdev->entity.num_pads; pad++) {
+ struct v4l2_mbus_framefmt *mbus_format;
- mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
- mbus_format->width = 1280;
- mbus_format->height = 720;
+ mbus_format = v4l2_subdev_state_get_format(state, pad);
- sun6i_csi_bridge_mbus_format_prepare(mbus_format);
+ mbus_format->code = sun6i_csi_bridge_formats[0].mbus_code;
+ mbus_format->width = 1280;
+ mbus_format->height = 720;
- mutex_unlock(lock);
+ sun6i_csi_bridge_mbus_format_prepare(mbus_format);
+ }
return 0;
}
@@ -536,53 +529,33 @@ sun6i_csi_bridge_enum_mbus_code(struct v4l2_subdev *subdev,
return 0;
}
-static int sun6i_csi_bridge_get_fmt(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_format *format)
-{
- struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
- struct v4l2_mbus_framefmt *mbus_format = &format->format;
- struct mutex *lock = &csi_dev->bridge.lock;
-
- mutex_lock(lock);
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *mbus_format = *v4l2_subdev_state_get_format(state,
- format->pad);
- else
- *mbus_format = csi_dev->bridge.mbus_format;
-
- mutex_unlock(lock);
-
- return 0;
-}
-
static int sun6i_csi_bridge_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- struct sun6i_csi_device *csi_dev = v4l2_get_subdevdata(subdev);
- struct v4l2_mbus_framefmt *mbus_format = &format->format;
- struct mutex *lock = &csi_dev->bridge.lock;
+ struct v4l2_mbus_framefmt *fmt;
- mutex_lock(lock);
+ /* The format on the source pad always matches the sink pad. */
+ if (format->pad != SUN6I_CSI_BRIDGE_PAD_SINK)
+ return v4l2_subdev_get_fmt(subdev, state, format);
- sun6i_csi_bridge_mbus_format_prepare(mbus_format);
+ sun6i_csi_bridge_mbus_format_prepare(&format->format);
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *v4l2_subdev_state_get_format(state, format->pad) =
- *mbus_format;
- else
- csi_dev->bridge.mbus_format = *mbus_format;
+ /* Set the format on the sink pad. */
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+ *fmt = format->format;
- mutex_unlock(lock);
+ /* Propagate the format to the source pad. */
+ fmt = v4l2_subdev_state_get_format(state, SUN6I_CSI_BRIDGE_PAD_SOURCE);
+ *fmt = format->format;
return 0;
}
static const struct v4l2_subdev_pad_ops sun6i_csi_bridge_pad_ops = {
.enum_mbus_code = sun6i_csi_bridge_enum_mbus_code,
- .get_fmt = sun6i_csi_bridge_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = sun6i_csi_bridge_set_fmt,
};
@@ -780,8 +753,6 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
};
int ret;
- mutex_init(&bridge->lock);
-
/* V4L2 Subdev */
v4l2_subdev_init(subdev, &sun6i_csi_bridge_subdev_ops);
@@ -809,6 +780,12 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
if (ret < 0)
return ret;
+ /* V4L2 Subdev finalize */
+
+ ret = v4l2_subdev_init_finalize(subdev);
+ if (ret < 0)
+ goto error_media_entity;
+
/* V4L2 Subdev */
if (csi_dev->isp_available)
@@ -818,7 +795,7 @@ int sun6i_csi_bridge_setup(struct sun6i_csi_device *csi_dev)
if (ret) {
dev_err(dev, "failed to register v4l2 subdev: %d\n", ret);
- goto error_media_entity;
+ goto error_subdev_finalize;
}
/* V4L2 Async */
@@ -852,6 +829,9 @@ error_v4l2_async_notifier:
else
v4l2_device_unregister_subdev(subdev);
+error_subdev_finalize:
+ v4l2_subdev_cleanup(subdev);
+
error_media_entity:
media_entity_cleanup(&subdev->entity);
@@ -868,5 +848,7 @@ void sun6i_csi_bridge_cleanup(struct sun6i_csi_device *csi_dev)
v4l2_device_unregister_subdev(subdev);
+ v4l2_subdev_cleanup(subdev);
+
media_entity_cleanup(&subdev->entity);
}
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
index 44653b38f722..a5b0a6f064dd 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_bridge.h
@@ -42,20 +42,11 @@ struct sun6i_csi_bridge {
struct v4l2_subdev subdev;
struct v4l2_async_notifier notifier;
struct media_pad pads[2];
- struct v4l2_mbus_framefmt mbus_format;
- struct mutex lock; /* Mbus format lock. */
struct sun6i_csi_bridge_source source_parallel;
struct sun6i_csi_bridge_source source_mipi_csi2;
};
-/* Helpers */
-
-void sun6i_csi_bridge_dimensions(struct sun6i_csi_device *csi_dev,
- unsigned int *width, unsigned int *height);
-void sun6i_csi_bridge_format(struct sun6i_csi_device *csi_dev,
- u32 *mbus_code, u32 *field);
-
/* Format */
const struct sun6i_csi_bridge_format *
diff --git a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
index 65879f4802c0..d90abba21309 100644
--- a/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
+++ b/drivers/media/platform/sunxi/sun6i-csi/sun6i_csi_capture.c
@@ -888,14 +888,19 @@ static int sun6i_csi_capture_link_validate(struct media_link *link)
media_entity_to_video_device(link->sink->entity);
struct sun6i_csi_device *csi_dev = video_get_drvdata(video_dev);
struct v4l2_device *v4l2_dev = csi_dev->v4l2_dev;
+ struct v4l2_subdev *src_subdev =
+ media_entity_to_v4l2_subdev(link->source->entity);
const struct sun6i_csi_capture_format *capture_format;
const struct sun6i_csi_bridge_format *bridge_format;
unsigned int capture_width, capture_height;
- unsigned int bridge_width, bridge_height;
const struct v4l2_format_info *format_info;
+ struct v4l2_subdev_format src_fmt = {
+ .which = V4L2_SUBDEV_FORMAT_ACTIVE,
+ .pad = link->source->index
+ };
u32 pixelformat, capture_field;
- u32 mbus_code, bridge_field;
bool match;
+ int ret;
sun6i_csi_capture_dimensions(csi_dev, &capture_width, &capture_height);
@@ -904,19 +909,22 @@ static int sun6i_csi_capture_link_validate(struct media_link *link)
if (WARN_ON(!capture_format))
return -EINVAL;
- sun6i_csi_bridge_dimensions(csi_dev, &bridge_width, &bridge_height);
+ /* Resolve csi bridge format. */
+ ret = v4l2_subdev_call(src_subdev, pad, get_fmt, NULL, &src_fmt);
+ if (ret)
+ return ret;
- sun6i_csi_bridge_format(csi_dev, &mbus_code, &bridge_field);
- bridge_format = sun6i_csi_bridge_format_find(mbus_code);
+ bridge_format = sun6i_csi_bridge_format_find(src_fmt.format.code);
if (WARN_ON(!bridge_format))
return -EINVAL;
/* No cropping/scaling is supported. */
- if (capture_width != bridge_width || capture_height != bridge_height) {
+ if (capture_width != src_fmt.format.width ||
+ capture_height != src_fmt.format.height) {
v4l2_err(v4l2_dev,
"invalid input/output dimensions: %ux%u/%ux%u\n",
- bridge_width, bridge_height, capture_width,
- capture_height);
+ src_fmt.format.width, src_fmt.format.height,
+ capture_width, capture_height);
return -EINVAL;
}
@@ -947,7 +955,8 @@ static int sun6i_csi_capture_link_validate(struct media_link *link)
/* With raw input mode, we need a 1:1 match between input and output. */
if (bridge_format->input_format == SUN6I_CSI_INPUT_FMT_RAW ||
capture_format->input_format_raw) {
- match = sun6i_csi_capture_format_match(pixelformat, mbus_code);
+ match = sun6i_csi_capture_format_match(pixelformat,
+ src_fmt.format.code);
if (!match)
goto invalid;
}
diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
index b06cb73015cd..dcc8c7eadf9a 100644
--- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
+++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.c
@@ -95,12 +95,12 @@ static void sun6i_mipi_csi2_disable(struct sun6i_mipi_csi2_device *csi2_dev)
SUN6I_MIPI_CSI2_CTL_EN, 0);
}
-static void sun6i_mipi_csi2_configure(struct sun6i_mipi_csi2_device *csi2_dev)
+static void sun6i_mipi_csi2_configure(struct sun6i_mipi_csi2_device *csi2_dev,
+ const struct v4l2_mbus_framefmt *mbus_format)
{
struct regmap *regmap = csi2_dev->regmap;
unsigned int lanes_count =
csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes;
- struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format;
const struct sun6i_mipi_csi2_format *format;
struct device *dev = csi2_dev->dev;
u32 version = 0;
@@ -173,7 +173,8 @@ static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
struct v4l2_subdev *source_subdev = csi2_dev->bridge.source_subdev;
union phy_configure_opts dphy_opts = { 0 };
struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy;
- struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *mbus_format;
const struct sun6i_mipi_csi2_format *format;
struct phy *dphy = csi2_dev->dphy;
struct device *dev = csi2_dev->dev;
@@ -183,8 +184,12 @@ static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
unsigned long pixel_rate;
int ret;
- if (!source_subdev)
- return -ENODEV;
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
+ if (!source_subdev) {
+ ret = -ENODEV;
+ goto unlock;
+ }
if (!on) {
v4l2_subdev_call(source_subdev, video, s_stream, 0);
@@ -196,7 +201,7 @@ static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
- return ret;
+ goto unlock;
/* Sensor Pixel Rate */
@@ -222,6 +227,8 @@ static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
goto error_pm;
}
+ mbus_format = v4l2_subdev_state_get_format(state,
+ SUN6I_MIPI_CSI2_PAD_SINK);
format = sun6i_mipi_csi2_format_find(mbus_format->code);
if (WARN_ON(!format)) {
ret = -ENODEV;
@@ -260,7 +267,7 @@ static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
/* Controller */
- sun6i_mipi_csi2_configure(csi2_dev);
+ sun6i_mipi_csi2_configure(csi2_dev, mbus_format);
sun6i_mipi_csi2_enable(csi2_dev);
/* D-PHY */
@@ -277,7 +284,8 @@ static int sun6i_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
if (ret && ret != -ENOIOCTLCMD)
goto disable;
- return 0;
+ ret = 0;
+ goto unlock;
disable:
phy_power_off(dphy);
@@ -286,6 +294,8 @@ disable:
error_pm:
pm_runtime_put(dev);
+unlock:
+ v4l2_subdev_unlock_state(state);
return ret;
}
@@ -308,21 +318,23 @@ sun6i_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
static int sun6i_mipi_csi2_init_state(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state)
{
- struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev);
- unsigned int pad = SUN6I_MIPI_CSI2_PAD_SINK;
- struct v4l2_mbus_framefmt *mbus_format =
- v4l2_subdev_state_get_format(state, pad);
- struct mutex *lock = &csi2_dev->bridge.lock;
+ unsigned int pad;
- mutex_lock(lock);
+ /*
+ * This subdev does not perform format conversion,
+ * initialize both pads identically.
+ */
+ for (pad = 0; pad < subdev->entity.num_pads; pad++) {
+ struct v4l2_mbus_framefmt *mbus_format;
- mbus_format->code = sun6i_mipi_csi2_formats[0].mbus_code;
- mbus_format->width = 640;
- mbus_format->height = 480;
+ mbus_format = v4l2_subdev_state_get_format(state, pad);
- sun6i_mipi_csi2_mbus_format_prepare(mbus_format);
+ mbus_format->code = sun6i_mipi_csi2_formats[0].mbus_code;
+ mbus_format->width = 640;
+ mbus_format->height = 480;
- mutex_unlock(lock);
+ sun6i_mipi_csi2_mbus_format_prepare(mbus_format);
+ }
return 0;
}
@@ -340,53 +352,33 @@ sun6i_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev,
return 0;
}
-static int sun6i_mipi_csi2_get_fmt(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_format *format)
-{
- struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev);
- struct v4l2_mbus_framefmt *mbus_format = &format->format;
- struct mutex *lock = &csi2_dev->bridge.lock;
-
- mutex_lock(lock);
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *mbus_format = *v4l2_subdev_state_get_format(state,
- format->pad);
- else
- *mbus_format = csi2_dev->bridge.mbus_format;
-
- mutex_unlock(lock);
-
- return 0;
-}
-
static int sun6i_mipi_csi2_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- struct sun6i_mipi_csi2_device *csi2_dev = v4l2_get_subdevdata(subdev);
- struct v4l2_mbus_framefmt *mbus_format = &format->format;
- struct mutex *lock = &csi2_dev->bridge.lock;
+ struct v4l2_mbus_framefmt *fmt;
- mutex_lock(lock);
+ /* The format on the source pad always matches the sink pad. */
+ if (format->pad != SUN6I_MIPI_CSI2_PAD_SINK)
+ return v4l2_subdev_get_fmt(subdev, state, format);
- sun6i_mipi_csi2_mbus_format_prepare(mbus_format);
+ sun6i_mipi_csi2_mbus_format_prepare(&format->format);
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *v4l2_subdev_state_get_format(state, format->pad) =
- *mbus_format;
- else
- csi2_dev->bridge.mbus_format = *mbus_format;
+ /* Set the format on the sink pad. */
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+ *fmt = format->format;
- mutex_unlock(lock);
+ /* Propagate the format to the source pad. */
+ fmt = v4l2_subdev_state_get_format(state, SUN6I_MIPI_CSI2_PAD_SOURCE);
+ *fmt = format->format;
return 0;
}
static const struct v4l2_subdev_pad_ops sun6i_mipi_csi2_pad_ops = {
.enum_mbus_code = sun6i_mipi_csi2_enum_mbus_code,
- .get_fmt = sun6i_mipi_csi2_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = sun6i_mipi_csi2_set_fmt,
};
@@ -502,8 +494,6 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev)
bool notifier_registered = false;
int ret;
- mutex_init(&bridge->lock);
-
/* V4L2 Subdev */
v4l2_subdev_init(subdev, &sun6i_mipi_csi2_subdev_ops);
@@ -532,6 +522,12 @@ static int sun6i_mipi_csi2_bridge_setup(struct sun6i_mipi_csi2_device *csi2_dev)
if (ret)
return ret;
+ /* V4L2 Subdev finalize */
+
+ ret = v4l2_subdev_init_finalize(subdev);
+ if (ret < 0)
+ goto error_media_entity_cleanup;
+
/* V4L2 Async */
v4l2_async_subdev_nf_init(notifier, subdev);
@@ -565,6 +561,9 @@ error_v4l2_notifier_unregister:
error_v4l2_notifier_cleanup:
v4l2_async_nf_cleanup(notifier);
+ v4l2_subdev_cleanup(subdev);
+
+error_media_entity_cleanup:
media_entity_cleanup(&subdev->entity);
return ret;
@@ -579,6 +578,7 @@ sun6i_mipi_csi2_bridge_cleanup(struct sun6i_mipi_csi2_device *csi2_dev)
v4l2_async_unregister_subdev(subdev);
v4l2_async_nf_unregister(notifier);
v4l2_async_nf_cleanup(notifier);
+ v4l2_subdev_cleanup(subdev);
media_entity_cleanup(&subdev->entity);
}
diff --git a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
index 24b15e34b5e8..d72dfbd6a993 100644
--- a/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
+++ b/drivers/media/platform/sunxi/sun6i-mipi-csi2/sun6i_mipi_csi2.h
@@ -32,8 +32,6 @@ struct sun6i_mipi_csi2_bridge {
struct media_pad pads[SUN6I_MIPI_CSI2_PAD_COUNT];
struct v4l2_fwnode_endpoint endpoint;
struct v4l2_async_notifier notifier;
- struct v4l2_mbus_framefmt mbus_format;
- struct mutex lock; /* Mbus format lock. */
struct v4l2_subdev *source_subdev;
};
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
index dbc51daa4fe3..46604dcc8dcc 100644
--- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.c
@@ -144,12 +144,12 @@ sun8i_a83t_mipi_csi2_disable(struct sun8i_a83t_mipi_csi2_device *csi2_dev)
}
static void
-sun8i_a83t_mipi_csi2_configure(struct sun8i_a83t_mipi_csi2_device *csi2_dev)
+sun8i_a83t_mipi_csi2_configure(struct sun8i_a83t_mipi_csi2_device *csi2_dev,
+ const struct v4l2_mbus_framefmt *mbus_format)
{
struct regmap *regmap = csi2_dev->regmap;
unsigned int lanes_count =
csi2_dev->bridge.endpoint.bus.mipi_csi2.num_data_lanes;
- struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format;
const struct sun8i_a83t_mipi_csi2_format *format;
struct device *dev = csi2_dev->dev;
u32 version = 0;
@@ -205,7 +205,8 @@ static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
struct v4l2_subdev *source_subdev = csi2_dev->bridge.source_subdev;
union phy_configure_opts dphy_opts = { 0 };
struct phy_configure_opts_mipi_dphy *dphy_cfg = &dphy_opts.mipi_dphy;
- struct v4l2_mbus_framefmt *mbus_format = &csi2_dev->bridge.mbus_format;
+ struct v4l2_subdev_state *state;
+ const struct v4l2_mbus_framefmt *mbus_format;
const struct sun8i_a83t_mipi_csi2_format *format;
struct phy *dphy = csi2_dev->dphy;
struct device *dev = csi2_dev->dev;
@@ -215,8 +216,12 @@ static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
unsigned long pixel_rate;
int ret;
- if (!source_subdev)
- return -ENODEV;
+ state = v4l2_subdev_lock_and_get_active_state(subdev);
+
+ if (!source_subdev) {
+ ret = -ENODEV;
+ goto unlock;
+ }
if (!on) {
v4l2_subdev_call(source_subdev, video, s_stream, 0);
@@ -228,7 +233,7 @@ static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
- return ret;
+ goto unlock;
/* Sensor pixel rate */
@@ -254,6 +259,9 @@ static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
goto error_pm;
}
+ mbus_format =
+ v4l2_subdev_state_get_format(state,
+ SUN8I_A83T_MIPI_CSI2_PAD_SINK);
format = sun8i_a83t_mipi_csi2_format_find(mbus_format->code);
if (WARN_ON(!format)) {
ret = -ENODEV;
@@ -292,7 +300,7 @@ static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
/* Controller */
- sun8i_a83t_mipi_csi2_configure(csi2_dev);
+ sun8i_a83t_mipi_csi2_configure(csi2_dev, mbus_format);
sun8i_a83t_mipi_csi2_enable(csi2_dev);
/* D-PHY */
@@ -309,7 +317,8 @@ static int sun8i_a83t_mipi_csi2_s_stream(struct v4l2_subdev *subdev, int on)
if (ret && ret != -ENOIOCTLCMD)
goto disable;
- return 0;
+ ret = 0;
+ goto unlock;
disable:
phy_power_off(dphy);
@@ -318,6 +327,8 @@ disable:
error_pm:
pm_runtime_put(dev);
+unlock:
+ v4l2_subdev_unlock_state(state);
return ret;
}
@@ -341,22 +352,23 @@ sun8i_a83t_mipi_csi2_mbus_format_prepare(struct v4l2_mbus_framefmt *mbus_format)
static int sun8i_a83t_mipi_csi2_init_state(struct v4l2_subdev *subdev,
struct v4l2_subdev_state *state)
{
- struct sun8i_a83t_mipi_csi2_device *csi2_dev =
- v4l2_get_subdevdata(subdev);
- unsigned int pad = SUN8I_A83T_MIPI_CSI2_PAD_SINK;
- struct v4l2_mbus_framefmt *mbus_format =
- v4l2_subdev_state_get_format(state, pad);
- struct mutex *lock = &csi2_dev->bridge.lock;
+ unsigned int pad;
- mutex_lock(lock);
+ /*
+ * This subdev does not perform format conversion,
+ * initialize both pads identically.
+ */
+ for (pad = 0; pad < subdev->entity.num_pads; pad++) {
+ struct v4l2_mbus_framefmt *mbus_format;
- mbus_format->code = sun8i_a83t_mipi_csi2_formats[0].mbus_code;
- mbus_format->width = 640;
- mbus_format->height = 480;
+ mbus_format = v4l2_subdev_state_get_format(state, pad);
- sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format);
+ mbus_format->code = sun8i_a83t_mipi_csi2_formats[0].mbus_code;
+ mbus_format->width = 640;
+ mbus_format->height = 480;
- mutex_unlock(lock);
+ sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format);
+ }
return 0;
}
@@ -375,55 +387,34 @@ sun8i_a83t_mipi_csi2_enum_mbus_code(struct v4l2_subdev *subdev,
return 0;
}
-static int sun8i_a83t_mipi_csi2_get_fmt(struct v4l2_subdev *subdev,
- struct v4l2_subdev_state *state,
- struct v4l2_subdev_format *format)
-{
- struct sun8i_a83t_mipi_csi2_device *csi2_dev =
- v4l2_get_subdevdata(subdev);
- struct v4l2_mbus_framefmt *mbus_format = &format->format;
- struct mutex *lock = &csi2_dev->bridge.lock;
-
- mutex_lock(lock);
-
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *mbus_format = *v4l2_subdev_state_get_format(state,
- format->pad);
- else
- *mbus_format = csi2_dev->bridge.mbus_format;
-
- mutex_unlock(lock);
-
- return 0;
-}
-
static int sun8i_a83t_mipi_csi2_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- struct sun8i_a83t_mipi_csi2_device *csi2_dev =
- v4l2_get_subdevdata(subdev);
- struct v4l2_mbus_framefmt *mbus_format = &format->format;
- struct mutex *lock = &csi2_dev->bridge.lock;
+ struct v4l2_mbus_framefmt *fmt;
- mutex_lock(lock);
+ /* The format on the source pad always matches the sink pad. */
+ if (format->pad != SUN8I_A83T_MIPI_CSI2_PAD_SINK)
+ return v4l2_subdev_get_fmt(subdev, state, format);
- sun8i_a83t_mipi_csi2_mbus_format_prepare(mbus_format);
+ sun8i_a83t_mipi_csi2_mbus_format_prepare(&format->format);
- if (format->which == V4L2_SUBDEV_FORMAT_TRY)
- *v4l2_subdev_state_get_format(state, format->pad) =
- *mbus_format;
- else
- csi2_dev->bridge.mbus_format = *mbus_format;
+ /* Set the format on the sink pad. */
+ fmt = v4l2_subdev_state_get_format(state, format->pad);
+ *fmt = format->format;
- mutex_unlock(lock);
+ /* Propagate the format to the source pad. */
+ fmt = v4l2_subdev_state_get_format(state,
+ SUN8I_A83T_MIPI_CSI2_PAD_SOURCE);
+ *fmt = format->format;
return 0;
}
static const struct v4l2_subdev_pad_ops sun8i_a83t_mipi_csi2_pad_ops = {
.enum_mbus_code = sun8i_a83t_mipi_csi2_enum_mbus_code,
- .get_fmt = sun8i_a83t_mipi_csi2_get_fmt,
+ .get_fmt = v4l2_subdev_get_fmt,
.set_fmt = sun8i_a83t_mipi_csi2_set_fmt,
};
@@ -540,8 +531,6 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev)
bool notifier_registered = false;
int ret;
- mutex_init(&bridge->lock);
-
/* V4L2 Subdev */
v4l2_subdev_init(subdev, &sun8i_a83t_mipi_csi2_subdev_ops);
@@ -570,6 +559,12 @@ sun8i_a83t_mipi_csi2_bridge_setup(struct sun8i_a83t_mipi_csi2_device *csi2_dev)
if (ret)
return ret;
+ /* V4L2 Subdev finalize */
+
+ ret = v4l2_subdev_init_finalize(subdev);
+ if (ret < 0)
+ goto error_media_entity_cleanup;
+
/* V4L2 Async */
v4l2_async_subdev_nf_init(notifier, subdev);
@@ -603,6 +598,9 @@ error_v4l2_notifier_unregister:
error_v4l2_notifier_cleanup:
v4l2_async_nf_cleanup(notifier);
+ v4l2_subdev_cleanup(subdev);
+
+error_media_entity_cleanup:
media_entity_cleanup(&subdev->entity);
return ret;
@@ -617,6 +615,7 @@ sun8i_a83t_mipi_csi2_bridge_cleanup(struct sun8i_a83t_mipi_csi2_device *csi2_dev
v4l2_async_unregister_subdev(subdev);
v4l2_async_nf_unregister(notifier);
v4l2_async_nf_cleanup(notifier);
+ v4l2_subdev_cleanup(subdev);
media_entity_cleanup(&subdev->entity);
}
diff --git a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h
index f1e64c53434c..819527bcd64d 100644
--- a/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h
+++ b/drivers/media/platform/sunxi/sun8i-a83t-mipi-csi2/sun8i_a83t_mipi_csi2.h
@@ -33,8 +33,6 @@ struct sun8i_a83t_mipi_csi2_bridge {
struct media_pad pads[SUN8I_A83T_MIPI_CSI2_PAD_COUNT];
struct v4l2_fwnode_endpoint endpoint;
struct v4l2_async_notifier notifier;
- struct v4l2_mbus_framefmt mbus_format;
- struct mutex lock; /* Mbus format lock. */
struct v4l2_subdev *source_subdev;
};
diff --git a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
index 41e48365167e..b7247d597c9c 100644
--- a/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
+++ b/drivers/media/platform/synopsys/dw-mipi-csi2rx.c
@@ -479,6 +479,7 @@ dw_mipi_csi2rx_enum_mbus_code(struct v4l2_subdev *sd,
}
static int dw_mipi_csi2rx_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
index 25f8ca0d6d94..c62614077d97 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.c
@@ -41,6 +41,8 @@
#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>
+#include <sound/hdmi-codec.h>
+
#include "snps_hdmirx.h"
#include "snps_hdmirx_cec.h"
@@ -132,6 +134,13 @@ struct snps_hdmirx_dev {
struct delayed_work delayed_work_hotplug;
struct delayed_work delayed_work_res_change;
struct hdmirx_cec *cec;
+ struct platform_device *audio_pdev;
+ struct clk *audio_clk;
+ struct delayed_work audio_work;
+ u32 audio_clkrate;
+ u32 audio_fs;
+ int audio_pre_state;
+ bool audio_streaming;
struct mutex phy_rw_lock; /* to protect phy r/w configuration */
struct mutex stream_lock; /* to lock video stream capture */
struct mutex work_lock; /* to lock the critical section of hotplug event */
@@ -156,7 +165,6 @@ struct snps_hdmirx_dev {
int num_clks;
u32 edid_blocks_written;
u32 cur_fmt_fourcc;
- u32 color_depth;
spinlock_t rst_lock; /* to lock register access */
u8 edid[EDID_NUM_BLOCKS_MAX * EDID_BLOCK_SIZE];
};
@@ -380,6 +388,38 @@ static void hdmirx_toggle_polarity(struct snps_hdmirx_dev *hdmirx_dev)
VPROC_HSYNC_POL_OVR_EN, 0);
}
+static u32 hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+ u32 val, color_depth_reg, color_depth;
+
+ val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
+ color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
+
+ switch (color_depth_reg) {
+ case 0x4:
+ color_depth = 24;
+ break;
+ case 0x5:
+ color_depth = 30;
+ break;
+ case 0x6:
+ color_depth = 36;
+ break;
+ case 0x7:
+ color_depth = 48;
+ break;
+ default:
+ color_depth = 24;
+ break;
+ }
+
+ v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
+ __func__, color_depth, color_depth_reg);
+
+ return color_depth;
+}
+
/*
* When querying DV timings during preview, if the DMA's timing is stable,
* we retrieve the timings directly from the DMA. However, if the current
@@ -393,7 +433,7 @@ static int hdmirx_get_detected_timings(struct snps_hdmirx_dev *hdmirx_dev,
struct v4l2_bt_timings *bt = &timings->bt;
u32 val, tmdsqpclk_freq, pix_clk;
unsigned int num_retries = 0;
- u32 field_type, deframer_st;
+ u32 field_type, deframer_st, color_depth;
u64 tmp_data, tmds_clk;
bool is_dvi_mode;
int ret;
@@ -414,10 +454,11 @@ retry:
deframer_st = hdmirx_readl(hdmirx_dev, DEFRAMER_STATUS);
is_dvi_mode = !(deframer_st & OPMODE_STS_MASK);
+ color_depth = hdmirx_get_colordepth(hdmirx_dev);
tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
tmds_clk = tmdsqpclk_freq * 4 * 1000;
tmp_data = tmds_clk * 24;
- do_div(tmp_data, hdmirx_dev->color_depth);
+ do_div(tmp_data, color_depth);
pix_clk = tmp_data;
bt->pixelclock = pix_clk;
@@ -429,7 +470,7 @@ retry:
v4l2_dbg(2, debug, v4l2_dev, "tmds_clk:%llu, pix_clk:%d\n", tmds_clk, pix_clk);
v4l2_dbg(1, debug, v4l2_dev, "interlace:%d, fmt:%d, color:%d, mode:%s\n",
bt->interlaced, hdmirx_dev->pix_fmt,
- hdmirx_dev->color_depth,
+ color_depth,
is_dvi_mode ? "dvi" : "hdmi");
v4l2_dbg(2, debug, v4l2_dev, "deframer_st:%#x\n", deframer_st);
@@ -470,12 +511,12 @@ static int hdmirx_query_dv_timings(struct file *file, void *priv,
int ret;
if (port_no_link(hdmirx_dev)) {
- v4l2_err(v4l2_dev, "%s: port has no link\n", __func__);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: port has no link\n", __func__);
return -ENOLINK;
}
if (signal_not_lock(hdmirx_dev)) {
- v4l2_err(v4l2_dev, "%s: signal is not locked\n", __func__);
+ v4l2_dbg(1, debug, v4l2_dev, "%s: signal is not locked\n", __func__);
return -ENOLCK;
}
@@ -506,9 +547,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 + extra 50ms to cover internal delay */
+ /* 100ms delay as per HDMI spec + extra 43ms to cover internal delay */
if (!en)
- msleep(100 + 50);
+ msleep(jiffies_to_msecs(V4L2_SET_EDID_HPD_LOW_JIFFIES));
}
static void hdmirx_write_edid_data(struct snps_hdmirx_dev *hdmirx_dev,
@@ -988,36 +1029,6 @@ static void hdmirx_controller_init(struct snps_hdmirx_dev *hdmirx_dev)
VS_REMAPFILTER_EN_QST | VS_FILTER_ORDER_QST(0x3));
}
-static void hdmirx_get_colordepth(struct snps_hdmirx_dev *hdmirx_dev)
-{
- struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
- u32 val, color_depth_reg;
-
- val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
- color_depth_reg = (val & HDMIRX_COLOR_DEPTH_MASK) >> 3;
-
- switch (color_depth_reg) {
- case 0x4:
- hdmirx_dev->color_depth = 24;
- break;
- case 0x5:
- hdmirx_dev->color_depth = 30;
- break;
- case 0x6:
- hdmirx_dev->color_depth = 36;
- break;
- case 0x7:
- hdmirx_dev->color_depth = 48;
- break;
- default:
- hdmirx_dev->color_depth = 24;
- break;
- }
-
- v4l2_dbg(1, debug, v4l2_dev, "%s: color_depth: %d, reg_val:%d\n",
- __func__, hdmirx_dev->color_depth, color_depth_reg);
-}
-
static void hdmirx_get_pix_fmt(struct snps_hdmirx_dev *hdmirx_dev)
{
struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
@@ -1128,7 +1139,6 @@ static void hdmirx_format_change(struct snps_hdmirx_dev *hdmirx_dev)
};
hdmirx_get_pix_fmt(hdmirx_dev);
- hdmirx_get_colordepth(hdmirx_dev);
hdmirx_get_avi_infoframe(hdmirx_dev);
v4l2_dbg(1, debug, v4l2_dev, "%s: queue res_chg_event\n", __func__);
@@ -1198,6 +1208,9 @@ static void hdmirx_submodule_init(struct snps_hdmirx_dev *hdmirx_dev)
static int hdmirx_enum_input(struct file *file, void *priv,
struct v4l2_input *input)
{
+ struct hdmirx_stream *stream = video_drvdata(file);
+ struct snps_hdmirx_dev *hdmirx_dev = stream->hdmirx_dev;
+
if (input->index > 0)
return -EINVAL;
@@ -1206,6 +1219,12 @@ static int hdmirx_enum_input(struct file *file, void *priv,
strscpy(input->name, "HDMI IN", sizeof(input->name));
input->capabilities = V4L2_IN_CAP_DV_TIMINGS;
+ input->status = 0;
+ if (port_no_link(hdmirx_dev))
+ input->status |= V4L2_IN_ST_NO_POWER;
+ if (signal_not_lock(hdmirx_dev))
+ input->status |= V4L2_IN_ST_NO_SIGNAL;
+
return 0;
}
@@ -2265,11 +2284,6 @@ static const struct hdmirx_cec_ops hdmirx_cec_ops = {
.read = hdmirx_readl,
};
-static void devm_hdmirx_of_reserved_mem_device_release(void *dev)
-{
- of_reserved_mem_device_release(dev);
-}
-
static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
{
struct device *dev = hdmirx_dev->dev;
@@ -2279,6 +2293,13 @@ static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
if (hdmirx_dev->num_clks < 1)
return -ENODEV;
+ for (int i = 0; i < hdmirx_dev->num_clks; i++) {
+ if (!strcmp(hdmirx_dev->clks[i].id, "audio")) {
+ hdmirx_dev->audio_clk = hdmirx_dev->clks[i].clk;
+ break;
+ }
+ }
+
hdmirx_dev->resets[HDMIRX_RST_A].id = "axi";
hdmirx_dev->resets[HDMIRX_RST_P].id = "apb";
hdmirx_dev->resets[HDMIRX_RST_REF].id = "ref";
@@ -2316,16 +2337,9 @@ static int hdmirx_parse_dt(struct snps_hdmirx_dev *hdmirx_dev)
if (!device_property_read_bool(dev, "hpd-is-active-low"))
hdmirx_dev->hpd_trigger_level_high = true;
- ret = of_reserved_mem_device_init(dev);
- if (ret) {
+ ret = devm_of_reserved_mem_device_init(dev);
+ if (ret)
dev_warn(dev, "no reserved memory for HDMIRX, use default CMA\n");
- } else {
- ret = devm_add_action_or_reset(dev,
- devm_hdmirx_of_reserved_mem_device_release,
- dev);
- if (ret)
- return ret;
- }
return 0;
}
@@ -2523,10 +2537,19 @@ static void hdmirx_enable_irq(struct device *dev)
msecs_to_jiffies(110));
}
+static void hdmirx_audio_setup(struct snps_hdmirx_dev *hdmirx_dev, u32 fs);
+
static __maybe_unused int hdmirx_suspend(struct device *dev)
{
struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ /*
+ * Stop the audio worker before the controller clocks are gated;
+ * the audio path is restored and the worker re-armed from
+ * resume() while a capture stream is active.
+ */
+ cancel_delayed_work_sync(&hdmirx_dev->audio_work);
+
hdmirx_disable_irq(dev);
/* TODO store CEC HW state */
@@ -2549,6 +2572,19 @@ static __maybe_unused int hdmirx_resume(struct device *dev)
hdmirx_hpd_ctrl(hdmirx_dev, true);
}
+ /*
+ * hdmirx_enable() fully reset the controller, wiping the audio
+ * configuration. If a capture stream is active across suspend,
+ * re-program the audio path with the last known sample rate and
+ * restart the worker; its rate change and FIFO error paths
+ * resynchronize once the source delivers audio again.
+ */
+ if (READ_ONCE(hdmirx_dev->audio_streaming)) {
+ hdmirx_audio_setup(hdmirx_dev, hdmirx_dev->audio_fs);
+ mod_delayed_work(system_unbound_wq, &hdmirx_dev->audio_work,
+ msecs_to_jiffies(200));
+ }
+
/* TODO restore CEC HW state */
enable_irq(hdmirx_dev->cec->irq);
@@ -2595,10 +2631,8 @@ static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
ret = devm_request_threaded_irq(dev, irq, NULL, hdmirx_dma_irq_handler,
IRQF_ONESHOT, "rk_hdmirx-dma",
hdmirx_dev);
- if (ret) {
- dev_err_probe(dev, ret, "failed to request dma irq\n");
+ if (ret)
return ret;
- }
irq = gpiod_to_irq(hdmirx_dev->detect_5v_gpio);
if (irq < 0) {
@@ -2609,10 +2643,11 @@ static int hdmirx_setup_irq(struct snps_hdmirx_dev *hdmirx_dev,
irq_set_status_flags(irq, IRQ_NOAUTOEN);
hdmirx_dev->det_irq = irq;
- ret = devm_request_irq(dev, irq, hdmirx_5v_det_irq_handler,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "rk_hdmirx-5v", hdmirx_dev);
- if (ret) {
+ ret = devm_request_any_context_irq(dev, irq, hdmirx_5v_det_irq_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_TRIGGER_RISING,
+ "rk_hdmirx-5v", hdmirx_dev);
+ if (ret < 0) {
dev_err_probe(dev, ret, "failed to request hdmirx-5v irq\n");
return ret;
}
@@ -2646,6 +2681,266 @@ static int hdmirx_register_cec(struct snps_hdmirx_dev *hdmirx_dev,
return 0;
}
+#define HDMIRX_AUDIO_INIT_FIFO_STATE 128
+#define HDMIRX_AUDIO_INIT_STATE (HDMIRX_AUDIO_INIT_FIFO_STATE * 4)
+
+static const int hdmirx_supported_fs[] = {
+ 32000, 44100, 48000, 88200, 96000, 176400, 192000, 768000, -1
+};
+
+static int hdmirx_audio_closest_fs(int fs)
+{
+ int i = 0, fs_t = hdmirx_supported_fs[0];
+
+ while (fs_t > 0) {
+ if (abs(fs - fs_t) <= 2000)
+ return fs_t;
+ fs_t = hdmirx_supported_fs[++i];
+ }
+ return 0;
+}
+
+/* Recover the incoming audio sample rate from the ACR N/CTS + TMDS clock. */
+static u32 hdmirx_audio_fs(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ u64 tmds_clk, fs_audio = 0;
+ u32 acr_cts, acr_n, tmdsqpclk_freq;
+ u32 acr_pb3_0, acr_pb7_4;
+
+ tmdsqpclk_freq = hdmirx_readl(hdmirx_dev, CMU_TMDSQPCLK_FREQ);
+ hdmirx_readl(hdmirx_dev, PKTDEC_ACR_PH2_1);
+ acr_pb3_0 = hdmirx_readl(hdmirx_dev, PKTDEC_ACR_PB3_0);
+ acr_pb7_4 = hdmirx_readl(hdmirx_dev, PKTDEC_ACR_PB7_4);
+ /*
+ * The packet decoder stores the ACR subpacket bytes with packet byte
+ * 0 in register bits [7:0], so byte-reverse each word to line the
+ * bytes up: CTS is packet bytes 1-3 (PKTDEC_ACR_PB3_0) and N is
+ * packet bytes 4-6 (PKTDEC_ACR_PB7_4), 20 bits each. readl()
+ * already abstracts the bus endianness, so the reversal is
+ * unconditional.
+ */
+ acr_cts = swab32(acr_pb3_0) & 0xfffff;
+ acr_n = (swab32(acr_pb7_4) & 0x0fffff00) >> 8;
+ tmds_clk = tmdsqpclk_freq * 4 * 1000U;
+ if (acr_cts != 0) {
+ fs_audio = div_u64((tmds_clk * acr_n), acr_cts);
+ fs_audio /= 128;
+ fs_audio = hdmirx_audio_closest_fs(fs_audio);
+ }
+ return (u32)fs_audio;
+}
+
+/* Nudge the audio reference clock by +/- ppm to keep the FIFO balanced. */
+static void hdmirx_audio_clk_ppm_inc(struct snps_hdmirx_dev *hdmirx_dev, int ppm)
+{
+ int delta, inc;
+ long rate = hdmirx_dev->audio_clkrate;
+
+ if (ppm < 0) {
+ ppm = -ppm;
+ inc = -1;
+ } else {
+ inc = 1;
+ }
+ delta = (int)div64_u64((u64)rate * ppm + 500000, 1000000);
+ delta *= inc;
+ rate = hdmirx_dev->audio_clkrate + delta;
+ clk_set_rate(hdmirx_dev->audio_clk, rate);
+ hdmirx_dev->audio_clkrate = rate;
+}
+
+static int hdmirx_audio_clk_adjust(struct snps_hdmirx_dev *hdmirx_dev,
+ int total_offset, int single_offset)
+{
+ int schedule_time = 500;
+ int ppm = 10;
+ u32 offset_abs = abs(total_offset);
+
+ if (offset_abs > 200) {
+ ppm += 200;
+ schedule_time -= 100;
+ }
+ if (offset_abs > 100) {
+ ppm += 200;
+ schedule_time -= 100;
+ }
+ if (offset_abs > 32) {
+ ppm += 20;
+ schedule_time -= 100;
+ }
+ if (offset_abs > 16)
+ ppm += 20;
+ if (total_offset > 16 && single_offset > 0)
+ hdmirx_audio_clk_ppm_inc(hdmirx_dev, ppm);
+ else if (total_offset < -16 && single_offset < 0)
+ hdmirx_audio_clk_ppm_inc(hdmirx_dev, -ppm);
+ return schedule_time;
+}
+
+static void hdmirx_audio_fifo_reinit(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_CONTROL, 1);
+ usleep_range(200, 210);
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_CONTROL, 0);
+}
+
+/*
+ * Program the audio clock, FIFO thresholds and enables for the given
+ * sample rate. Shared by hw_params and system resume: the controller is
+ * fully reset on resume, so the whole configuration must be re-applied.
+ */
+static void hdmirx_audio_setup(struct snps_hdmirx_dev *hdmirx_dev, u32 fs)
+{
+ hdmirx_dev->audio_fs = fs;
+ hdmirx_dev->audio_clkrate = fs * 128;
+ clk_set_rate(hdmirx_dev->audio_clk, hdmirx_dev->audio_clkrate);
+
+ hdmirx_audio_fifo_reinit(hdmirx_dev);
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_THR_PASS, HDMIRX_AUDIO_INIT_FIFO_STATE);
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_THR,
+ AFIFO_THR_LOW_QST(0x20) | AFIFO_THR_HIGH_QST(0x160));
+ hdmirx_writel(hdmirx_dev, AUDIO_FIFO_MUTE_THR,
+ AFIFO_THR_MUTE_LOW_QST(0x8) | AFIFO_THR_MUTE_HIGH_QST(0x178));
+
+ hdmirx_update_bits(hdmirx_dev, AUDIO_PROC_CONFIG0, I2S_EN, I2S_EN);
+ hdmirx_update_bits(hdmirx_dev, GLOBAL_SWENABLE, AUDIO_ENABLE, AUDIO_ENABLE);
+
+ hdmirx_dev->audio_pre_state = 0;
+}
+
+/*
+ * Periodic worker that locks the local audio clock to the source by keeping
+ * the audio FIFO fill level close to its target, avoiding under/overflow.
+ */
+static void hdmirx_audio_work(struct work_struct *work)
+{
+ struct snps_hdmirx_dev *hdmirx_dev =
+ container_of(to_delayed_work(work), struct snps_hdmirx_dev, audio_work);
+ unsigned long delay = 200;
+ int cur, total, single;
+ u32 fifo, fs;
+
+ fs = hdmirx_audio_fs(hdmirx_dev);
+ fifo = hdmirx_readl(hdmirx_dev, AUDIO_FIFO_STATUS2);
+
+ if (fifo & (AFIFO_UNDERFLOW_ST | AFIFO_OVERFLOW_ST)) {
+ if (fs) {
+ clk_set_rate(hdmirx_dev->audio_clk, fs * 128);
+ hdmirx_dev->audio_clkrate = fs * 128;
+ hdmirx_dev->audio_fs = fs;
+ }
+ hdmirx_audio_fifo_reinit(hdmirx_dev);
+ hdmirx_dev->audio_pre_state = 0;
+ goto out;
+ }
+
+ cur = fifo & 0xffff;
+ total = cur - HDMIRX_AUDIO_INIT_STATE;
+ single = cur - hdmirx_dev->audio_pre_state;
+
+ if (fs && abs((int)fs - (int)hdmirx_dev->audio_fs) > 1000) {
+ clk_set_rate(hdmirx_dev->audio_clk, fs * 128);
+ hdmirx_dev->audio_clkrate = fs * 128;
+ hdmirx_dev->audio_fs = fs;
+ hdmirx_audio_fifo_reinit(hdmirx_dev);
+ hdmirx_dev->audio_pre_state = 0;
+ goto out;
+ }
+
+ if (cur != 0)
+ delay = hdmirx_audio_clk_adjust(hdmirx_dev, total, single);
+ hdmirx_dev->audio_pre_state = cur;
+out:
+ /* Only re-arm while streaming; avoids a self-reschedule race with
+ * the cancel_delayed_work_sync() callers (hw_params and
+ * audio_shutdown).
+ */
+ if (READ_ONCE(hdmirx_dev->audio_streaming))
+ queue_delayed_work(system_unbound_wq, &hdmirx_dev->audio_work,
+ msecs_to_jiffies(delay));
+}
+
+static int hdmirx_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *fmt,
+ struct hdmi_codec_params *hparms)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ u32 fs;
+
+ /* Only the I2S interface (DAI 0) is wired up so far. */
+ if (fmt->fmt == HDMI_SPDIF)
+ return -EOPNOTSUPP;
+
+ /*
+ * Stop the worker before touching the shared audio state; it is
+ * re-armed below once the new parameters are in place.
+ */
+ WRITE_ONCE(hdmirx_dev->audio_streaming, false);
+ cancel_delayed_work_sync(&hdmirx_dev->audio_work);
+
+ fs = hdmirx_audio_fs(hdmirx_dev);
+ if (!fs)
+ fs = hparms ? hparms->sample_rate : 48000;
+ if (!fs)
+ fs = 48000;
+
+ hdmirx_audio_setup(hdmirx_dev, fs);
+
+ WRITE_ONCE(hdmirx_dev->audio_streaming, true);
+ mod_delayed_work(system_unbound_wq, &hdmirx_dev->audio_work,
+ msecs_to_jiffies(200));
+
+ dev_dbg(dev, "audio hw_params: fs=%u\n", fs);
+ return 0;
+}
+
+static void hdmirx_audio_shutdown(struct device *dev, void *data)
+{
+ struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+
+ WRITE_ONCE(hdmirx_dev->audio_streaming, false);
+ cancel_delayed_work_sync(&hdmirx_dev->audio_work);
+ hdmirx_update_bits(hdmirx_dev, GLOBAL_SWENABLE, AUDIO_ENABLE, 0);
+}
+
+static const struct hdmi_codec_ops hdmirx_audio_codec_ops = {
+ .hw_params = hdmirx_audio_hw_params,
+ .audio_shutdown = hdmirx_audio_shutdown,
+};
+
+static int hdmirx_register_audio_device(struct snps_hdmirx_dev *hdmirx_dev)
+{
+ struct hdmi_codec_pdata codec_data = {
+ .ops = &hdmirx_audio_codec_ops,
+ .i2s = 1,
+ .no_i2s_playback = 1,
+ .max_i2s_channels = 8,
+ /*
+ * The controller also has an S/PDIF audio interface (DAI 1 in
+ * the binding). Register it so DAI indexes match the binding,
+ * but reject its use in hw_params() until it is wired up.
+ */
+ .spdif = 1,
+ .no_spdif_playback = 1,
+ .data = hdmirx_dev,
+ };
+ struct platform_device *audio_pdev;
+
+ if (!hdmirx_dev->audio_clk)
+ return -ENODEV;
+
+ audio_pdev = platform_device_register_data(hdmirx_dev->dev,
+ HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO,
+ &codec_data, sizeof(codec_data));
+ if (IS_ERR(audio_pdev))
+ return PTR_ERR(audio_pdev);
+
+ hdmirx_dev->audio_pdev = audio_pdev;
+
+ return 0;
+}
+
static int hdmirx_probe(struct platform_device *pdev)
{
struct snps_hdmirx_dev *hdmirx_dev;
@@ -2697,6 +2992,7 @@ static int hdmirx_probe(struct platform_device *pdev)
hdmirx_delayed_work_hotplug);
INIT_DELAYED_WORK(&hdmirx_dev->delayed_work_res_change,
hdmirx_delayed_work_res_change);
+ INIT_DELAYED_WORK(&hdmirx_dev->audio_work, hdmirx_audio_work);
hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
hdmirx_dev->timings = cea640x480;
@@ -2765,6 +3061,10 @@ static int hdmirx_probe(struct platform_device *pdev)
V4L2_DEBUGFS_IF_AVI, hdmirx_dev,
hdmirx_debugfs_if_read);
+ ret = hdmirx_register_audio_device(hdmirx_dev);
+ if (ret)
+ dev_warn(dev, "failed to register HDMI audio codec: %d\n", ret);
+
return 0;
err_unreg_video_dev:
@@ -2784,6 +3084,9 @@ static void hdmirx_remove(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct snps_hdmirx_dev *hdmirx_dev = dev_get_drvdata(dev);
+ if (hdmirx_dev->audio_pdev)
+ platform_device_unregister(hdmirx_dev->audio_pdev);
+
v4l2_debugfs_if_free(hdmirx_dev->infoframes);
debugfs_remove_recursive(hdmirx_dev->debugfs_dir);
diff --git a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
index 31b887e94e87..a99f54fd1744 100644
--- a/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
+++ b/drivers/media/platform/synopsys/hdmirx/snps_hdmirx.h
@@ -81,6 +81,7 @@
#define DATAPATH_ENABLE BIT(12)
#define PKTFIFO_ENABLE BIT(11)
#define AVPUNIT_ENABLE BIT(8)
+#define AUDIO_ENABLE BIT(9)
#define MAIN_ENABLE BIT(0)
#define GLOBAL_TIMER_REF_BASE 0x0028
#define CORE_CONFIG 0x0050
@@ -177,20 +178,27 @@
#define VPROC_FMT_OVR_VALUE(x) UPDATE(x, 6, 4)
#define VPROC_FMT_OVR_EN BIT(0)
+#define AUDIO_FIFO_CONFIG 0x0460
#define AFIFO_FILL_RESTART BIT(0)
+#define AUDIO_FIFO_CONTROL 0x0464
#define AFIFO_INIT_P BIT(0)
+#define AUDIO_FIFO_THR_PASS 0x0468
+#define AUDIO_FIFO_THR 0x046c
#define AFIFO_THR_LOW_QST_MASK GENMASK(25, 16)
#define AFIFO_THR_LOW_QST(x) UPDATE(x, 25, 16)
#define AFIFO_THR_HIGH_QST_MASK GENMASK(9, 0)
#define AFIFO_THR_HIGH_QST(x) UPDATE(x, 9, 0)
+#define AUDIO_FIFO_MUTE_THR 0x0470
#define AFIFO_THR_MUTE_LOW_QST_MASK GENMASK(25, 16)
#define AFIFO_THR_MUTE_LOW_QST(x) UPDATE(x, 25, 16)
#define AFIFO_THR_MUTE_HIGH_QST_MASK GENMASK(9, 0)
#define AFIFO_THR_MUTE_HIGH_QST(x) UPDATE(x, 9, 0)
+#define AUDIO_FIFO_STATUS2 0x0478
#define AFIFO_UNDERFLOW_ST BIT(25)
#define AFIFO_OVERFLOW_ST BIT(24)
+#define AUDIO_PROC_CONFIG0 0x0480
#define SPEAKER_ALLOC_OVR_EN BIT(16)
#define I2S_BPCUV_EN BIT(4)
#define SPDIF_EN BIT(2)
diff --git a/drivers/media/platform/ti/Kconfig b/drivers/media/platform/ti/Kconfig
index 1a020b2bbb4f..03df5617e99d 100644
--- a/drivers/media/platform/ti/Kconfig
+++ b/drivers/media/platform/ti/Kconfig
@@ -30,17 +30,6 @@ config VIDEO_TI_CAL
In TI Technical Reference Manual this module is referred as
Camera Interface Subsystem (CAMSS).
-config VIDEO_TI_CAL_MC
- bool "Media Controller centric mode by default"
- depends on VIDEO_TI_CAL
- default n
- help
- Enables Media Controller centric mode by default.
-
- If set, CAL driver will start in Media Controller mode by
- default. Note that this behavior can be overridden via
- module parameter 'mc_api'.
-
config VIDEO_TI_VIP
tristate "TI Video Input Port"
depends on VIDEO_DEV
diff --git a/drivers/media/platform/ti/am437x/am437x-vpfe.c b/drivers/media/platform/ti/am437x/am437x-vpfe.c
index 1ca559df7e59..3cd5ee69d3c4 100644
--- a/drivers/media/platform/ti/am437x/am437x-vpfe.c
+++ b/drivers/media/platform/ti/am437x/am437x-vpfe.c
@@ -1314,7 +1314,7 @@ static int __subdev_set_format(struct vpfe_device *vpfe,
*mbus_fmt = *fmt;
- ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sd_fmt);
+ ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, NULL, &sd_fmt);
if (ret)
return ret;
diff --git a/drivers/media/platform/ti/cal/cal-camerarx.c b/drivers/media/platform/ti/cal/cal-camerarx.c
index 00a71dac0ff4..a978d27fe029 100644
--- a/drivers/media/platform/ti/cal/cal-camerarx.c
+++ b/drivers/media/platform/ti/cal/cal-camerarx.c
@@ -762,6 +762,7 @@ static int cal_camerarx_sd_enum_frame_size(struct v4l2_subdev *sd,
}
static int cal_camerarx_sd_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/ti/cal/cal-video.c b/drivers/media/platform/ti/cal/cal-video.c
index d40e24ab1127..1990cab77344 100644
--- a/drivers/media/platform/ti/cal/cal-video.c
+++ b/drivers/media/platform/ti/cal/cal-video.c
@@ -123,6 +123,21 @@ static int __subdev_get_format(struct cal_ctx *ctx,
return 0;
}
+static int cal_call_active_state_set_fmt(struct v4l2_subdev *source,
+ struct v4l2_subdev_format *fmt)
+{
+ struct v4l2_subdev_state *source_state;
+ int ret;
+
+ source_state = v4l2_subdev_lock_and_get_active_state(source);
+
+ ret = v4l2_subdev_call(source, pad, set_fmt, NULL, source_state, fmt);
+ if (source_state)
+ v4l2_subdev_unlock_state(source_state);
+
+ return ret;
+}
+
static int __subdev_set_format(struct cal_ctx *ctx,
struct v4l2_mbus_framefmt *fmt)
{
@@ -136,7 +151,7 @@ static int __subdev_set_format(struct cal_ctx *ctx,
*mbus_fmt = *fmt;
- ret = v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt);
+ ret = cal_call_active_state_set_fmt(sd, &sd_fmt);
if (ret)
return ret;
@@ -281,7 +296,7 @@ static int cal_legacy_s_fmt_vid_cap(struct file *file, void *priv,
ctx->v_fmt.fmt.pix.field = sd_fmt.format.field;
cal_calc_format_size(ctx, fmtinfo, &ctx->v_fmt);
- v4l2_subdev_call_state_active(sd, pad, set_fmt, &sd_fmt);
+ cal_call_active_state_set_fmt(sd, &sd_fmt);
ctx->fmtinfo = fmtinfo;
*f = ctx->v_fmt;
diff --git a/drivers/media/platform/ti/cal/cal.c b/drivers/media/platform/ti/cal/cal.c
index b7e77b6b8950..5c82378d1eee 100644
--- a/drivers/media/platform/ti/cal/cal.c
+++ b/drivers/media/platform/ti/cal/cal.c
@@ -43,13 +43,7 @@ unsigned int cal_debug;
module_param_named(debug, cal_debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
-#ifdef CONFIG_VIDEO_TI_CAL_MC
-#define CAL_MC_API_DEFAULT 1
-#else
-#define CAL_MC_API_DEFAULT 0
-#endif
-
-bool cal_mc_api = CAL_MC_API_DEFAULT;
+bool cal_mc_api = 1;
module_param_named(mc_api, cal_mc_api, bool, 0444);
MODULE_PARM_DESC(mc_api, "activates the MC API");
@@ -1228,6 +1222,8 @@ static int cal_probe(struct platform_device *pdev)
/* Create contexts. */
if (!cal_mc_api) {
+ dev_warn(cal->dev, "The legacy non-MC API is deprecated\n");
+
for (i = 0; i < cal->data->num_csi2_phy; ++i) {
struct cal_ctx *ctx;
diff --git a/drivers/media/platform/ti/davinci/vpif_capture.c b/drivers/media/platform/ti/davinci/vpif_capture.c
index 91cb6223561a..397f3115af5b 100644
--- a/drivers/media/platform/ti/davinci/vpif_capture.c
+++ b/drivers/media/platform/ti/davinci/vpif_capture.c
@@ -1602,7 +1602,7 @@ err_cleanup:
static int vpif_probe(struct platform_device *pdev)
{
struct vpif_subdev_info *subdevdata;
- struct i2c_adapter *i2c_adap;
+ struct i2c_adapter *i2c_adap = NULL;
int subdev_count;
int res_idx = 0;
int i, err;
@@ -1692,9 +1692,12 @@ static int vpif_probe(struct platform_device *pdev)
}
}
+ i2c_put_adapter(i2c_adap);
+
return 0;
probe_subdev_out:
+ i2c_put_adapter(i2c_adap);
v4l2_async_nf_cleanup(&vpif_obj.notifier);
/* free sub devices memory */
kfree(vpif_obj.sd);
diff --git a/drivers/media/platform/ti/davinci/vpif_display.c b/drivers/media/platform/ti/davinci/vpif_display.c
index 08877ae1513a..8bea1de6ccc8 100644
--- a/drivers/media/platform/ti/davinci/vpif_display.c
+++ b/drivers/media/platform/ti/davinci/vpif_display.c
@@ -1289,9 +1289,12 @@ static int vpif_probe(struct platform_device *pdev)
if (err)
goto probe_subdev_out;
+ i2c_put_adapter(i2c_adap);
+
return 0;
probe_subdev_out:
+ i2c_put_adapter(i2c_adap);
kfree(vpif_obj.sd);
vpif_unregister:
v4l2_device_unregister(&vpif_obj.v4l2_dev);
diff --git a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
index 4769931b1930..022ea3d616c9 100644
--- a/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
+++ b/drivers/media/platform/ti/j721e-csi2rx/j721e-csi2rx.c
@@ -226,6 +226,30 @@ static const struct ti_csi2rx_fmt ti_csi2rx_formats[] = {
.bpp = 16,
.size = SHIM_DMACNTX_SIZE_16,
}, {
+ .fourcc = V4L2_PIX_FMT_SBGGR12,
+ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .bpp = 16,
+ .size = SHIM_DMACNTX_SIZE_16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGBRG12,
+ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .bpp = 16,
+ .size = SHIM_DMACNTX_SIZE_16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SGRBG12,
+ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .bpp = 16,
+ .size = SHIM_DMACNTX_SIZE_16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_SRGGB12,
+ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
+ .csi_dt = MIPI_CSI2_DT_RAW12,
+ .bpp = 16,
+ .size = SHIM_DMACNTX_SIZE_16,
+ }, {
.fourcc = V4L2_PIX_FMT_RGB565X,
.code = MEDIA_BUS_FMT_RGB565_1X16,
.csi_dt = MIPI_CSI2_DT_RGB565,
@@ -1043,6 +1067,7 @@ static int ti_csi2rx_enum_mbus_code(struct v4l2_subdev *subdev,
}
static int ti_csi2rx_sd_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/platform/ti/omap3isp/ispccdc.c b/drivers/media/platform/ti/omap3isp/ispccdc.c
index 4708b6303493..f422d70ab6e4 100644
--- a/drivers/media/platform/ti/omap3isp/ispccdc.c
+++ b/drivers/media/platform/ti/omap3isp/ispccdc.c
@@ -2232,6 +2232,7 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd,
* Return 0 on success or a negative error code otherwise.
*/
static int ccdc_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2276,6 +2277,7 @@ static int ccdc_get_selection(struct v4l2_subdev *sd,
* Return 0 on success or a negative error code otherwise.
*/
static int ccdc_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2346,6 +2348,7 @@ static int ccdc_get_format(struct v4l2_subdev *sd,
* to the format type.
*/
static int ccdc_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -2472,7 +2475,7 @@ static int ccdc_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
- ccdc_set_format(sd, fh ? fh->state : NULL, &format);
+ ccdc_set_format(sd, NULL, fh ? fh->state : NULL, &format);
return 0;
}
diff --git a/drivers/media/platform/ti/omap3isp/ispccp2.c b/drivers/media/platform/ti/omap3isp/ispccp2.c
index d668111b44f4..c0da5cca9ddc 100644
--- a/drivers/media/platform/ti/omap3isp/ispccp2.c
+++ b/drivers/media/platform/ti/omap3isp/ispccp2.c
@@ -776,6 +776,7 @@ static int ccp2_get_format(struct v4l2_subdev *sd,
* returns zero
*/
static int ccp2_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -820,7 +821,7 @@ static int ccp2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
- ccp2_set_format(sd, fh ? fh->state : NULL, &format);
+ ccp2_set_format(sd, NULL, fh ? fh->state : NULL, &format);
return 0;
}
diff --git a/drivers/media/platform/ti/omap3isp/ispcsi2.c b/drivers/media/platform/ti/omap3isp/ispcsi2.c
index f227042b61b6..8fc2948d654c 100644
--- a/drivers/media/platform/ti/omap3isp/ispcsi2.c
+++ b/drivers/media/platform/ti/omap3isp/ispcsi2.c
@@ -994,6 +994,7 @@ static int csi2_get_format(struct v4l2_subdev *sd,
* return -EINVAL or zero on success
*/
static int csi2_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1038,7 +1039,7 @@ static int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
- csi2_set_format(sd, fh ? fh->state : NULL, &format);
+ csi2_set_format(sd, NULL, fh ? fh->state : NULL, &format);
return 0;
}
diff --git a/drivers/media/platform/ti/omap3isp/isppreview.c b/drivers/media/platform/ti/omap3isp/isppreview.c
index 3f3b5bd9cdc7..ac5025ce4315 100644
--- a/drivers/media/platform/ti/omap3isp/isppreview.c
+++ b/drivers/media/platform/ti/omap3isp/isppreview.c
@@ -1925,6 +1925,7 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd,
* Return 0 on success or a negative error code otherwise.
*/
static int preview_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1968,6 +1969,7 @@ static int preview_get_selection(struct v4l2_subdev *sd,
* Return 0 on success or a negative error code otherwise.
*/
static int preview_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -2035,6 +2037,7 @@ static int preview_get_format(struct v4l2_subdev *sd,
* return -EINVAL or zero on success
*/
static int preview_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -2090,7 +2093,7 @@ static int preview_init_formats(struct v4l2_subdev *sd,
format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10;
format.format.width = 4096;
format.format.height = 4096;
- preview_set_format(sd, fh ? fh->state : NULL, &format);
+ preview_set_format(sd, NULL, fh ? fh->state : NULL, &format);
return 0;
}
diff --git a/drivers/media/platform/ti/omap3isp/ispresizer.c b/drivers/media/platform/ti/omap3isp/ispresizer.c
index ad0127f5b5cb..874aa451a2d0 100644
--- a/drivers/media/platform/ti/omap3isp/ispresizer.c
+++ b/drivers/media/platform/ti/omap3isp/ispresizer.c
@@ -1222,6 +1222,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink,
* Return 0 on success or a negative error code otherwise.
*/
static int resizer_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1275,6 +1276,7 @@ static int resizer_get_selection(struct v4l2_subdev *sd,
* Return 0 on success or a negative error code otherwise.
*/
static int resizer_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1500,6 +1502,7 @@ static int resizer_get_format(struct v4l2_subdev *sd,
* return -EINVAL or zero on success
*/
static int resizer_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -1577,7 +1580,7 @@ static int resizer_init_formats(struct v4l2_subdev *sd,
format.format.code = MEDIA_BUS_FMT_YUYV8_1X16;
format.format.width = 4096;
format.format.height = 4096;
- resizer_set_format(sd, fh ? fh->state : NULL, &format);
+ resizer_set_format(sd, NULL, fh ? fh->state : NULL, &format);
return 0;
}
diff --git a/drivers/media/platform/ti/omap3isp/ispvideo.c b/drivers/media/platform/ti/omap3isp/ispvideo.c
index b946c8087c77..cd324c730318 100644
--- a/drivers/media/platform/ti/omap3isp/ispvideo.c
+++ b/drivers/media/platform/ti/omap3isp/ispvideo.c
@@ -839,7 +839,7 @@ isp_video_get_selection(struct file *file, void *fh, struct v4l2_selection *sel)
* implemented.
*/
sdsel.pad = pad;
- ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel);
+ ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, NULL, &sdsel);
if (!ret)
sel->r = sdsel.r;
if (ret != -ENOIOCTLCMD)
@@ -890,7 +890,7 @@ isp_video_set_selection(struct file *file, void *fh, struct v4l2_selection *sel)
sdsel.pad = pad;
mutex_lock(&video->mutex);
- ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel);
+ ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, NULL, &sdsel);
mutex_unlock(&video->mutex);
if (!ret)
sel->r = sdsel.r;
diff --git a/drivers/media/platform/ti/vpe/vip.c b/drivers/media/platform/ti/vpe/vip.c
index ccb688f4d7af..2bbe8eb31555 100644
--- a/drivers/media/platform/ti/vpe/vip.c
+++ b/drivers/media/platform/ti/vpe/vip.c
@@ -1748,7 +1748,7 @@ static int vip_s_fmt_vid_cap(struct file *file, void *priv,
sfmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sfmt.pad = 0;
- ret = v4l2_subdev_call(port->subdev, pad, set_fmt, NULL, &sfmt);
+ ret = v4l2_subdev_call(port->subdev, pad, set_fmt, NULL, NULL, &sfmt);
if (ret) {
v4l2_dbg(1, debug, &dev->v4l2_dev, "set_fmt failed in subdev\n");
return ret;
@@ -2620,7 +2620,7 @@ static int vip_init_port(struct vip_port *port)
mbus_fmt->code = fmt->code;
sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sd_fmt.pad = 0;
- ret = v4l2_subdev_call(port->subdev, pad, set_fmt,
+ ret = v4l2_subdev_call(port->subdev, pad, set_fmt, NULL,
NULL, &sd_fmt);
if (ret)
v4l2_dbg(1, debug, &dev->v4l2_dev, "init_port set_fmt failed in subdev: (%d)\n",
diff --git a/drivers/media/platform/via/via-camera.c b/drivers/media/platform/via/via-camera.c
index 1b81acba7da0..6a6e10c6f649 100644
--- a/drivers/media/platform/via/via-camera.c
+++ b/drivers/media/platform/via/via-camera.c
@@ -249,7 +249,7 @@ static int viacam_configure_sensor(struct via_camera *cam)
v4l2_fill_mbus_format(&format.format, &cam->sensor_format, cam->mbus_code);
ret = sensor_call(cam, core, init, 0);
if (ret == 0)
- ret = sensor_call(cam, pad, set_fmt, NULL, &format);
+ ret = sensor_call(cam, pad, set_fmt, NULL, NULL, &format);
/*
* OV7670 does weird things if flip is set *before* format...
*/
@@ -842,7 +842,7 @@ static int viacam_do_try_fmt(struct via_camera *cam,
upix->pixelformat = f->pixelformat;
viacam_fmt_pre(upix, spix);
v4l2_fill_mbus_format(&format.format, spix, f->mbus_code);
- ret = sensor_call(cam, pad, set_fmt, &pad_state, &format);
+ ret = sensor_call(cam, pad, set_fmt, NULL, &pad_state, &format);
v4l2_fill_pix_format(spix, &format.format);
viacam_fmt_post(upix, spix);
return ret;
diff --git a/drivers/media/platform/video-mux.c b/drivers/media/platform/video-mux.c
index cba34893258a..b253b260b605 100644
--- a/drivers/media/platform/video-mux.c
+++ b/drivers/media/platform/video-mux.c
@@ -146,6 +146,7 @@ static const struct v4l2_subdev_video_ops video_mux_subdev_video_ops = {
};
static int video_mux_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/media/platform/xilinx/xilinx-csi2rxss.c b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
index 146131b8f37e..2f6f3af4e492 100644
--- a/drivers/media/platform/xilinx/xilinx-csi2rxss.c
+++ b/drivers/media/platform/xilinx/xilinx-csi2rxss.c
@@ -694,6 +694,7 @@ static int xcsi2rxss_get_format(struct v4l2_subdev *sd,
}
static int xcsi2rxss_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/platform/xilinx/xilinx-tpg.c b/drivers/media/platform/xilinx/xilinx-tpg.c
index 7deec6e37edc..bf39ddb9c296 100644
--- a/drivers/media/platform/xilinx/xilinx-tpg.c
+++ b/drivers/media/platform/xilinx/xilinx-tpg.c
@@ -278,6 +278,7 @@ static int xtpg_get_format(struct v4l2_subdev *subdev,
}
static int xtpg_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/radio/si4713/radio-usb-si4713.c b/drivers/media/radio/si4713/radio-usb-si4713.c
index 6e6764143e21..62877980fc04 100644
--- a/drivers/media/radio/si4713/radio-usb-si4713.c
+++ b/drivers/media/radio/si4713/radio-usb-si4713.c
@@ -192,7 +192,7 @@ static int si4713_send_startup_command(struct si4713_usb_device *radio)
}
if (time_is_before_jiffies(until_jiffies))
return -EIO;
- msleep(3);
+ usleep_range(3000, 4000);
}
return retval;
@@ -339,7 +339,7 @@ static int si4713_i2c_read(struct si4713_usb_device *radio, char *data, int len)
data[0] = 0;
return 0;
}
- msleep(3);
+ usleep_range(3000, 4000);
}
}
diff --git a/drivers/media/radio/si4713/si4713.c b/drivers/media/radio/si4713/si4713.c
index 0c0354566b0a..150d07f6b319 100644
--- a/drivers/media/radio/si4713/si4713.c
+++ b/drivers/media/radio/si4713/si4713.c
@@ -667,7 +667,7 @@ static int si4713_tx_tune_measure(struct si4713_device *sdev, u16 frequency,
/*
* si4713_tx_tune_status- Returns the status of the tx_tune_freq, tx_tune_mea or
* tx_tune_power commands. This command return the current
- * frequency, output voltage in dBuV, the antenna tunning
+ * frequency, output voltage in dBuV, the antenna tuning
* capacitance value and the received noise level. The
* command also clears the stcint interrupt bit when the
* first bit of its arguments is high.
diff --git a/drivers/media/rc/bpf-lirc.c b/drivers/media/rc/bpf-lirc.c
index 2f7564f26445..14ab611e7445 100644
--- a/drivers/media/rc/bpf-lirc.c
+++ b/drivers/media/rc/bpf-lirc.c
@@ -148,12 +148,13 @@ static int lirc_bpf_attach(struct rc_dev *rcdev, struct bpf_prog *prog)
if (ret)
return ret;
- raw = rcdev->raw;
- if (!raw) {
+ if (!rcdev->registered) {
ret = -ENODEV;
goto unlock;
}
+ raw = rcdev->raw;
+
old_array = lirc_rcu_dereference(raw->progs);
if (old_array && bpf_prog_array_length(old_array) >= BPF_MAX_PROGS) {
ret = -E2BIG;
@@ -186,12 +187,13 @@ static int lirc_bpf_detach(struct rc_dev *rcdev, struct bpf_prog *prog)
if (ret)
return ret;
- raw = rcdev->raw;
- if (!raw) {
+ if (!rcdev->registered) {
ret = -ENODEV;
goto unlock;
}
+ raw = rcdev->raw;
+
old_array = lirc_rcu_dereference(raw->progs);
ret = bpf_prog_array_copy(old_array, prog, NULL, 0, &new_array);
/*
@@ -235,7 +237,8 @@ void lirc_bpf_free(struct rc_dev *rcdev)
struct bpf_prog_array_item *item;
struct bpf_prog_array *array;
- array = lirc_rcu_dereference(rcdev->raw->progs);
+ array = rcu_replace_pointer(rcdev->raw->progs, NULL,
+ lockdep_is_held(&ir_raw_handler_lock));
if (!array)
return;
@@ -316,6 +319,11 @@ int lirc_prog_query(const union bpf_attr *attr, union bpf_attr __user *uattr)
if (ret)
goto put;
+ if (!rcdev->registered) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
progs = lirc_rcu_dereference(rcdev->raw->progs);
cnt = progs ? bpf_prog_array_length(progs) : 0;
diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c
index 6f7dccc965e7..f98d76277b62 100644
--- a/drivers/media/rc/ene_ir.c
+++ b/drivers/media/rc/ene_ir.c
@@ -1103,15 +1103,15 @@ static void ene_remove(struct pnp_dev *pnp_dev)
unsigned long flags;
rc_unregister_device(dev->rdev);
- timer_delete_sync(&dev->tx_sim_timer);
spin_lock_irqsave(&dev->hw_lock, flags);
ene_rx_disable(dev);
ene_rx_restore_hw_buffer(dev);
spin_unlock_irqrestore(&dev->hw_lock, flags);
- rc_free_device(dev->rdev);
free_irq(dev->irq, dev);
+ timer_delete_sync(&dev->tx_sim_timer);
release_region(dev->hw_io, ENE_IO_SIZE);
+ rc_free_device(dev->rdev);
kfree(dev);
}
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index c196ee923ecd..d4368181fef6 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -140,17 +140,6 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS);
fit_dbg("ir_class reg: 0x%02x", ir_class);
- switch (ir_class) {
- case CLASS_RX_2TX:
- case CLASS_RX_1TX:
- fintek->hw_tx_capable = true;
- break;
- case CLASS_RX_ONLY:
- default:
- fintek->hw_tx_capable = false;
- break;
- }
-
chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
chip = chip_major << 8 | chip_minor;
@@ -169,7 +158,6 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
spin_lock_irqsave(&fintek->fintek_lock, flags);
fintek->chip_major = chip_major;
fintek->chip_minor = chip_minor;
- fintek->chip_vendor = vendor;
/*
* Newer reviews of this chipset uses port 8 instead of 5
@@ -496,7 +484,6 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
spin_lock_init(&fintek->fintek_lock);
pnp_set_drvdata(pdev, fintek);
- fintek->pdev = pdev;
ret = fintek_hw_detect(fintek);
if (ret)
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h
index 20696359e9ba..9ade9d7e7bfb 100644
--- a/drivers/media/rc/fintek-cir.h
+++ b/drivers/media/rc/fintek-cir.h
@@ -40,11 +40,9 @@ static int debug;
KBUILD_MODNAME ": " text "\n" , ## __VA_ARGS__)
-#define TX_BUF_LEN 256
#define RX_BUF_LEN 32
struct fintek_dev {
- struct pnp_dev *pdev;
struct rc_dev *rdev;
spinlock_t fintek_lock;
@@ -53,14 +51,6 @@ struct fintek_dev {
u8 buf[RX_BUF_LEN];
unsigned int pkts;
- struct {
- spinlock_t lock;
- u8 buf[TX_BUF_LEN];
- unsigned int buf_count;
- unsigned int cur_buf_num;
- wait_queue_head_t queue;
- } tx;
-
/* Config register index/data port pair */
u32 cr_ip;
u32 cr_dp;
@@ -73,17 +63,8 @@ struct fintek_dev {
/* hardware id */
u8 chip_major;
u8 chip_minor;
- u16 chip_vendor;
u8 logical_dev_cir;
- /* hardware features */
- bool hw_learning_capable;
- bool hw_tx_capable;
-
- /* rx settings */
- bool learning_enabled;
- bool carrier_detect_enabled;
-
enum {
CMD_HEADER = 0,
SUBCMD,
@@ -92,9 +73,6 @@ struct fintek_dev {
} parser_state;
u8 cmd, rem;
-
- /* carrier period = 1 / frequency */
- u32 carrier;
};
/* buffer packet constants, largely identical to mceusb.c */
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 049a73b5f882..2668461ed409 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -96,7 +96,6 @@ struct imon_context {
bool dev_present_intf1; /* USB device presence, interface 1 */
struct mutex lock; /* to lock this object */
- wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
struct usb_endpoint_descriptor *rx_endpoint_intf0;
struct usb_endpoint_descriptor *rx_endpoint_intf1;
@@ -378,7 +377,7 @@ static const struct usb_device_id imon_usb_id_table[] = {
* SoundGraph iMON PAD (IR & LCD)
* SoundGraph iMON Knob (IR only)
*/
- { USB_DEVICE(0x15c2, 0xffdc),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0xffdc, 0),
.driver_info = (unsigned long)&imon_default_table },
/*
@@ -387,61 +386,61 @@ static const struct usb_device_id imon_usb_id_table[] = {
* Need user input to fill in details on unknown devices.
*/
/* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */
- { USB_DEVICE(0x15c2, 0x0034),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0034, 0),
.driver_info = (unsigned long)&imon_DH102 },
/* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */
- { USB_DEVICE(0x15c2, 0x0035),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0035, 0),
.driver_info = (unsigned long)&imon_default_table},
/* SoundGraph iMON OEM VFD (IR & VFD) */
- { USB_DEVICE(0x15c2, 0x0036),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0036, 0),
.driver_info = (unsigned long)&imon_OEM_VFD },
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x0037),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0037, 0),
.driver_info = (unsigned long)&imon_default_table},
/* SoundGraph iMON OEM LCD (IR & LCD) */
- { USB_DEVICE(0x15c2, 0x0038),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0038, 0),
.driver_info = (unsigned long)&imon_default_table},
/* SoundGraph iMON UltraBay (IR & LCD) */
- { USB_DEVICE(0x15c2, 0x0039),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0039, 0),
.driver_info = (unsigned long)&imon_default_table},
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x003a),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x003a, 0),
.driver_info = (unsigned long)&imon_default_table},
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x003b),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x003b, 0),
.driver_info = (unsigned long)&imon_default_table},
/* SoundGraph iMON OEM Inside (IR only) */
- { USB_DEVICE(0x15c2, 0x003c),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x003c, 0),
.driver_info = (unsigned long)&imon_default_table},
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x003d),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x003d, 0),
.driver_info = (unsigned long)&imon_default_table},
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x003e),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x003e, 0),
.driver_info = (unsigned long)&imon_default_table},
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x003f),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x003f, 0),
.driver_info = (unsigned long)&imon_default_table},
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x0040),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0040, 0),
.driver_info = (unsigned long)&imon_default_table},
/* SoundGraph iMON MINI (IR only) */
- { USB_DEVICE(0x15c2, 0x0041),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0041, 0),
.driver_info = (unsigned long)&imon_default_table},
/* Antec Veris Multimedia Station EZ External (IR only) */
- { USB_DEVICE(0x15c2, 0x0042),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0042, 0),
.driver_info = (unsigned long)&imon_default_table},
/* Antec Veris Multimedia Station Basic Internal (IR only) */
- { USB_DEVICE(0x15c2, 0x0043),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0043, 0),
.driver_info = (unsigned long)&imon_default_table},
/* Antec Veris Multimedia Station Elite (IR & VFD) */
- { USB_DEVICE(0x15c2, 0x0044),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0044, 0),
.driver_info = (unsigned long)&imon_default_table},
/* Antec Veris Multimedia Station Premiere (IR & LCD) */
- { USB_DEVICE(0x15c2, 0x0045),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0045, 0),
.driver_info = (unsigned long)&imon_default_table},
/* device specifics unknown */
- { USB_DEVICE(0x15c2, 0x0046),
+ { USB_DEVICE_INTERFACE_NUMBER(0x15c2, 0x0046, 0),
.driver_info = (unsigned long)&imon_default_table},
{}
};
@@ -805,19 +804,16 @@ static ssize_t associate_remote_show(struct device *d,
char *buf)
{
struct imon_context *ictx = dev_get_drvdata(d);
+ int len;
if (!ictx)
return -ENODEV;
mutex_lock(&ictx->lock);
- if (ictx->rf_isassociating)
- strscpy(buf, "associating\n", PAGE_SIZE);
- else
- strscpy(buf, "closed\n", PAGE_SIZE);
-
+ len = sysfs_emit(buf, (ictx->rf_isassociating ? "associating\n" : "closed\n"));
dev_info(d, "Visit https://www.lirc.org/html/imon-24g.html for instructions on how to associate your iMON 2.4G DT/LT remote\n");
mutex_unlock(&ictx->lock);
- return strlen(buf);
+ return len;
}
static ssize_t associate_remote_store(struct device *d,
@@ -2331,15 +2327,20 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
struct usb_host_interface *iface_desc;
int ret = -ENOMEM;
+ ret = usb_driver_claim_interface(&imon_driver, intf, ictx);
+ if (ret) {
+ dev_err(ictx->dev, "failed to claim second interface (%d)\n", ret);
+ return NULL;
+ }
+
rx_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!rx_urb)
goto rx_urb_alloc_failed;
mutex_lock(&ictx->lock);
- if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
+ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
timer_setup(&ictx->ttimer, imon_touch_display_timeout, 0);
- }
ictx->usbdev_intf1 = interface_to_usbdev(intf);
ictx->rx_urb_intf1 = rx_urb;
@@ -2366,13 +2367,14 @@ static struct imon_context *imon_init_intf1(struct usb_interface *intf,
ret = usb_submit_urb(ictx->rx_urb_intf1, GFP_KERNEL);
if (ret) {
- pr_err("usb_submit_urb failed for intf1 (%d)\n", ret);
+ dev_err(ictx->dev, "usb_submit_urb failed for intf1 (%d)\n", ret);
goto urb_submit_failed;
}
ictx->dev_present_intf1 = true;
mutex_unlock(&ictx->lock);
+
return ictx;
urb_submit_failed:
@@ -2380,11 +2382,14 @@ urb_submit_failed:
input_unregister_device(ictx->touch);
touch_setup_failed:
find_endpoint_failed:
+ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
+ timer_delete_sync(&ictx->ttimer);
ictx->usbdev_intf1 = NULL;
mutex_unlock(&ictx->lock);
usb_free_urb(rx_urb);
ictx->rx_urb_intf1 = NULL;
rx_urb_alloc_failed:
+ usb_driver_release_interface(&imon_driver, intf);
dev_err(ictx->dev, "unable to initialize intf1, err %d\n", ret);
return NULL;
@@ -2419,90 +2424,50 @@ static void imon_init_display(struct imon_context *ictx,
static int imon_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
- struct usb_device *usbdev = NULL;
- struct usb_host_interface *iface_desc = NULL;
- struct usb_interface *first_if;
+ struct usb_device *usbdev;
+ struct usb_interface *second_if;
struct device *dev = &interface->dev;
- int ifnum, sysfs_err;
- int ret = 0;
- struct imon_context *ictx = NULL;
+ int sysfs_err;
+ struct imon_context *ictx;
u16 vendor, product;
- usbdev = interface_to_usbdev(interface);
- iface_desc = interface->cur_altsetting;
- ifnum = iface_desc->desc.bInterfaceNumber;
- vendor = le16_to_cpu(usbdev->descriptor.idVendor);
- product = le16_to_cpu(usbdev->descriptor.idProduct);
-
- dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
- __func__, vendor, product, ifnum);
+ if (interface->cur_altsetting->desc.bInterfaceNumber != 0)
+ return -ENODEV;
- first_if = usb_ifnum_to_if(usbdev, 0);
- if (!first_if) {
- ret = -ENODEV;
- goto fail;
- }
+ usbdev = interface_to_usbdev(interface);
+ vendor = le16_to_cpu(usbdev->descriptor.idVendor);
+ product = le16_to_cpu(usbdev->descriptor.idProduct);
- if (first_if->dev.driver != interface->dev.driver) {
- dev_err(&interface->dev, "inconsistent driver matching\n");
- ret = -EINVAL;
- goto fail;
- }
+ dev_dbg(dev, "found iMON device (%04x:%04x)\n", vendor, product);
- if (ifnum == 0) {
- ictx = imon_init_intf0(interface, id);
- if (!ictx) {
- pr_err("failed to initialize context!\n");
- ret = -ENODEV;
- goto fail;
- }
- refcount_set(&ictx->users, 1);
+ ictx = imon_init_intf0(interface, id);
+ if (!ictx)
+ return -ENODEV;
- } else {
- /* this is the secondary interface on the device */
- struct imon_context *first_if_ctx = usb_get_intfdata(first_if);
+ refcount_set(&ictx->users, 1);
- /* fail early if first intf failed to register */
- if (!first_if_ctx) {
- ret = -ENODEV;
- goto fail;
- }
+ usb_set_intfdata(interface, ictx);
- ictx = imon_init_intf1(interface, first_if_ctx);
- if (!ictx) {
- pr_err("failed to attach to context!\n");
- ret = -ENODEV;
- goto fail;
- }
+ /* Newer devices export a second interface for display/touchscreen */
+ second_if = usb_ifnum_to_if(usbdev, 1);
+ if (second_if && imon_init_intf1(second_if, ictx))
refcount_inc(&ictx->users);
+ if (product == 0xffdc && ictx->rf_device) {
+ sysfs_err = sysfs_create_group(&interface->dev.kobj,
+ &imon_rf_attr_group);
+ if (sysfs_err)
+ pr_err("Could not create RF sysfs entries(%d)\n",
+ sysfs_err);
}
- usb_set_intfdata(interface, ictx);
+ if (ictx->display_supported)
+ imon_init_display(ictx, interface);
- if (ifnum == 0) {
- if (product == 0xffdc && ictx->rf_device) {
- sysfs_err = sysfs_create_group(&interface->dev.kobj,
- &imon_rf_attr_group);
- if (sysfs_err)
- pr_err("Could not create RF sysfs entries(%d)\n",
- sysfs_err);
- }
-
- if (ictx->display_supported)
- imon_init_display(ictx, interface);
- }
-
- dev_info(dev, "iMON device (%04x:%04x, intf%d) on usb<%d:%d> initialized\n",
- vendor, product, ifnum,
- usbdev->bus->busnum, usbdev->devnum);
+ dev_info(dev, "iMON device (%04x:%04x) on usb<%d:%d> initialized\n",
+ vendor, product, usbdev->bus->busnum, usbdev->devnum);
return 0;
-
-fail:
- dev_err(dev, "unable to register, err %d\n", ret);
-
- return ret;
}
/*
@@ -2510,11 +2475,15 @@ fail:
*/
static void imon_disconnect(struct usb_interface *interface)
{
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+ struct usb_interface *other_if;
struct imon_context *ictx;
struct device *dev;
int ifnum;
ictx = usb_get_intfdata(interface);
+ if (!ictx)
+ return;
mutex_lock(&ictx->lock);
ictx->disconnected = true;
@@ -2550,6 +2519,7 @@ static void imon_disconnect(struct usb_interface *interface)
else if (ictx->display_type == IMON_DISPLAY_TYPE_VFD)
usb_deregister_dev(interface, &imon_vfd_class);
}
+ other_if = usb_ifnum_to_if(usbdev, 1);
} else {
ictx->dev_present_intf1 = false;
usb_kill_urb(ictx->rx_urb_intf1);
@@ -2557,13 +2527,16 @@ static void imon_disconnect(struct usb_interface *interface)
timer_delete_sync(&ictx->ttimer);
input_unregister_device(ictx->touch);
}
+ other_if = usb_ifnum_to_if(usbdev, 0);
}
+ if (other_if)
+ usb_driver_release_interface(&imon_driver, other_if);
+
if (refcount_dec_and_test(&ictx->users))
free_imon_context(ictx);
- dev_dbg(dev, "%s: iMON device (intf%d) disconnected\n",
- __func__, ifnum);
+ dev_dbg(dev, "iMON device (intf%d) disconnected\n", ifnum);
}
static int imon_suspend(struct usb_interface *intf, pm_message_t message)
diff --git a/drivers/media/rc/ir-hix5hd2.c b/drivers/media/rc/ir-hix5hd2.c
index 1b061e4a3dcf..aa3de4d57a58 100644
--- a/drivers/media/rc/ir-hix5hd2.c
+++ b/drivers/media/rc/ir-hix5hd2.c
@@ -316,6 +316,9 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
if (ret < 0)
goto clkerr;
+ priv->rdev = rdev;
+ priv->dev = dev;
+
if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
0, pdev->name, priv) < 0) {
dev_err(dev, "IRQ %d register failed\n", priv->irq);
@@ -323,8 +326,6 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
goto regerr;
}
- priv->rdev = rdev;
- priv->dev = dev;
platform_set_drvdata(pdev, priv);
return ret;
diff --git a/drivers/media/rc/ir-mce_kbd-decoder.c b/drivers/media/rc/ir-mce_kbd-decoder.c
index bb2d7c37c263..d1c9263511b0 100644
--- a/drivers/media/rc/ir-mce_kbd-decoder.c
+++ b/drivers/media/rc/ir-mce_kbd-decoder.c
@@ -219,6 +219,7 @@ static int ir_mce_kbd_decode(struct rc_dev *dev, struct ir_raw_event ev)
struct mce_kbd_dec *data = &dev->raw->mce_kbd;
u32 scancode;
unsigned long delay;
+ unsigned long flags;
struct lirc_scancode lsc = {};
if (!is_timing_event(ev)) {
@@ -319,7 +320,7 @@ again:
scancode = data->body & 0xffffff;
dev_dbg(&dev->dev, "keyboard data 0x%08x\n",
data->body);
- spin_lock(&data->keylock);
+ spin_lock_irqsave(&data->keylock, flags);
if (scancode) {
delay = usecs_to_jiffies(dev->timeout) +
msecs_to_jiffies(100);
@@ -329,7 +330,7 @@ again:
}
/* Pass data to keyboard buffer parser */
ir_mce_kbd_process_keyboard_data(dev, scancode);
- spin_unlock(&data->keylock);
+ spin_unlock_irqrestore(&data->keylock, flags);
lsc.rc_proto = RC_PROTO_MCIR2_KBD;
break;
case MCIR2_MOUSE_NBITS:
diff --git a/drivers/media/rc/ir_toy.c b/drivers/media/rc/ir_toy.c
index ee645882dd5a..58b23d08a897 100644
--- a/drivers/media/rc/ir_toy.c
+++ b/drivers/media/rc/ir_toy.c
@@ -55,7 +55,6 @@ enum state {
struct irtoy {
struct device *dev;
- struct usb_device *usbdev;
struct rc_dev *rc;
struct urb *urb_in, *urb_out;
@@ -373,7 +372,7 @@ static int irtoy_tx_carrier(struct rc_dev *rc, uint32_t carrier)
u8 buf[3];
int err;
- if (carrier < 11800)
+ if (carrier < 11800 || carrier > 3000000)
return -EINVAL;
buf[0] = 0x06;
@@ -441,7 +440,6 @@ static int irtoy_probe(struct usb_interface *intf,
irtoy_out_callback, irtoy);
irtoy->dev = &intf->dev;
- irtoy->usbdev = usbdev;
irtoy->rc = rc;
irtoy->urb_out = urb;
irtoy->pulse = true;
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 1fbafcd8219e..d0e98095d1b6 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1359,7 +1359,6 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
/* set driver data into the pnp device */
pnp_set_drvdata(pdev, itdev);
- itdev->pdev = pdev;
/* initialize waitqueues for transmission */
init_waitqueue_head(&itdev->tx_queue);
diff --git a/drivers/media/rc/ite-cir.h b/drivers/media/rc/ite-cir.h
index 4b4294d77555..a8382aa9dc35 100644
--- a/drivers/media/rc/ite-cir.h
+++ b/drivers/media/rc/ite-cir.h
@@ -76,7 +76,6 @@ struct ite_dev_params {
/* ITE CIR device structure */
struct ite_dev {
- struct pnp_dev *pdev;
struct rc_dev *rdev;
/* sync data */
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 183f1939b941..f7b8f1f66d9b 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -492,6 +492,8 @@ static long lirc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
ret = -ENOTTY;
else if (val <= 0)
ret = -EINVAL;
+ else if (fh->carrier_low && fh->carrier_low > val)
+ ret = -EINVAL;
else
ret = dev->s_rx_carrier_range(dev, fh->carrier_low,
val);
@@ -775,6 +777,9 @@ void lirc_unregister(struct rc_dev *dev)
unsigned long flags;
struct lirc_fh *fh;
+ if (!device_is_registered(&dev->lirc_dev))
+ return;
+
dev_dbg(&dev->dev, "lirc_dev: driver %s unregistered from minor = %d\n",
dev->driver_name, MINOR(dev->lirc_dev.devt));
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 39ba7f6a2549..356a54f91d05 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -484,7 +484,6 @@ struct mceusb_dev {
u8 cmd, rem; /* Remaining IR data bytes in packet */
struct {
- u32 connected:1;
u32 tx_mask_normal:1;
u32 microsoft_gen1:1;
u32 no_tx:1;
diff --git a/drivers/media/rc/meson-ir-tx.c b/drivers/media/rc/meson-ir-tx.c
index fded2c256f2a..a0d07da09f67 100644
--- a/drivers/media/rc/meson-ir-tx.c
+++ b/drivers/media/rc/meson-ir-tx.c
@@ -219,6 +219,9 @@ static int meson_irtx_set_carrier(struct rc_dev *rc, u32 carrier)
if (carrier == 0)
return -EINVAL;
+ if (!DIV_ROUND_CLOSEST(USEC_PER_SEC, carrier))
+ return -EINVAL;
+
ir->carrier = carrier;
meson_irtx_set_mod(ir);
@@ -288,11 +291,12 @@ static int meson_irtx_mod_clock_probe(struct meson_irtx *ir,
if (!np)
return -ENODEV;
- clock = devm_clk_get(ir->dev, "xtal");
- if (IS_ERR(clock) || clk_prepare_enable(clock))
- return -ENODEV;
-
*clk_nr = IRB_MOD_XTAL3_CLK;
+
+ clock = devm_clk_get_enabled(ir->dev, "xtal");
+ if (IS_ERR(clock))
+ return PTR_ERR(clock);
+
ir->clk_rate = clk_get_rate(clock) / 3;
if (ir->clk_rate < IRB_MOD_1US_CLK_RATE) {
@@ -324,7 +328,7 @@ static int meson_irtx_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0)
- return -ENODEV;
+ return irq;
ir->dev = dev;
ir->carrier = MIRTX_DEFAULT_CARRIER;
@@ -345,7 +349,7 @@ static int meson_irtx_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "irq request failed\n");
- rc = rc_allocate_device(RC_DRIVER_IR_RAW_TX);
+ rc = devm_rc_allocate_device(dev, RC_DRIVER_IR_RAW_TX);
if (!rc)
return -ENOMEM;
@@ -358,10 +362,8 @@ static int meson_irtx_probe(struct platform_device *pdev)
rc->s_tx_duty_cycle = meson_irtx_set_duty_cycle;
ret = devm_rc_register_device(dev, rc);
- if (ret < 0) {
- rc_free_device(rc);
+ if (ret < 0)
return dev_err_probe(dev, ret, "rc_dev registration failed\n");
- }
return 0;
}
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index ed7d93beaa28..e66e3cfedeec 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -77,9 +77,6 @@ struct nvt_dev {
/* hardware id */
u8 chip_major;
u8 chip_minor;
-
- /* carrier period = 1 / frequency */
- u32 carrier;
};
/* buffer packet constants */
diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c
index ba24c2f22d39..75dee4a483d4 100644
--- a/drivers/media/rc/rc-ir-raw.c
+++ b/drivers/media/rc/rc-ir-raw.c
@@ -10,7 +10,8 @@
#include <linux/sched.h>
#include "rc-core-priv.h"
-/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
+/* Used to keep track of IR raw clients, protected by ir_raw_client_lock */
+static DEFINE_MUTEX(ir_raw_client_lock);
static LIST_HEAD(ir_raw_client_list);
/* Used to handle IR raw handler extensions */
@@ -71,9 +72,6 @@ static int ir_raw_event_thread(void *data)
*/
int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev)
{
- if (!dev->raw)
- return -EINVAL;
-
dev_dbg(&dev->dev, "sample: (%05dus %s)\n",
ev->duration, TO_STR(ev->pulse));
@@ -102,9 +100,6 @@ int ir_raw_event_store_edge(struct rc_dev *dev, bool pulse)
ktime_t now;
struct ir_raw_event ev = {};
- if (!dev->raw)
- return -EINVAL;
-
now = ktime_get();
ev.duration = ktime_to_us(ktime_sub(now, dev->raw->last_event));
ev.pulse = !pulse;
@@ -129,9 +124,6 @@ int ir_raw_event_store_with_timeout(struct rc_dev *dev, struct ir_raw_event *ev)
ktime_t now;
int rc = 0;
- if (!dev->raw)
- return -EINVAL;
-
now = ktime_get();
spin_lock(&dev->raw->edge_spinlock);
@@ -166,9 +158,6 @@ EXPORT_SYMBOL_GPL(ir_raw_event_store_with_timeout);
*/
int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev)
{
- if (!dev->raw)
- return -EINVAL;
-
/* Ignore spaces in idle mode */
if (dev->idle && !ev->pulse)
return 0;
@@ -200,9 +189,6 @@ EXPORT_SYMBOL_GPL(ir_raw_event_store_with_filter);
*/
void ir_raw_event_set_idle(struct rc_dev *dev, bool idle)
{
- if (!dev->raw)
- return;
-
dev_dbg(&dev->dev, "%s idle mode\n", idle ? "enter" : "leave");
if (idle) {
@@ -226,7 +212,7 @@ EXPORT_SYMBOL_GPL(ir_raw_event_set_idle);
*/
void ir_raw_event_handle(struct rc_dev *dev)
{
- if (!dev->raw || !dev->raw->thread)
+ if (!dev->raw->thread)
return;
wake_up_process(dev->raw->thread);
@@ -288,13 +274,6 @@ static int change_protocol(struct rc_dev *dev, u64 *rc_proto)
return 0;
}
-static void ir_raw_disable_protocols(struct rc_dev *dev, u64 protocols)
-{
- mutex_lock(&dev->lock);
- dev->enabled_protocols &= ~protocols;
- mutex_unlock(&dev->lock);
-}
-
/**
* ir_raw_gen_manchester() - Encode data with Manchester (bi-phase) modulation.
* @ev: Pointer to pointer to next free event. *@ev is incremented for
@@ -612,9 +591,6 @@ EXPORT_SYMBOL(ir_raw_encode_carrier);
*/
int ir_raw_event_prepare(struct rc_dev *dev)
{
- if (!dev)
- return -EINVAL;
-
dev->raw = kzalloc_obj(*dev->raw);
if (!dev->raw)
return -ENOMEM;
@@ -633,50 +609,63 @@ int ir_raw_event_register(struct rc_dev *dev)
{
struct task_struct *thread;
+ /* Holding dev->lock could result in a dead-lock */
+ lockdep_assert_not_held(&dev->lock);
+
thread = kthread_run(ir_raw_event_thread, dev->raw, "rc%u", dev->minor);
if (IS_ERR(thread))
return PTR_ERR(thread);
dev->raw->thread = thread;
- mutex_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_client_lock);
list_add_tail(&dev->raw->list, &ir_raw_client_list);
- mutex_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&ir_raw_client_lock);
return 0;
}
void ir_raw_event_free(struct rc_dev *dev)
{
- kfree(dev->raw);
- dev->raw = NULL;
+ if (dev->raw) {
+ timer_delete_sync(&dev->raw->edge_handle);
+ mutex_lock(&ir_raw_handler_lock);
+ if (dev->raw->thread)
+ put_task_struct(dev->raw->thread);
+ lirc_bpf_free(dev);
+ mutex_unlock(&ir_raw_handler_lock);
+ kfree(dev->raw);
+ dev->raw = NULL;
+ }
}
void ir_raw_event_unregister(struct rc_dev *dev)
{
struct ir_raw_handler *handler;
- if (!dev || !dev->raw)
- return;
-
+ /*
+ * After ir_raw_event_unregister() is called, an sync
+ * call to ir_raw_event_handle() can still arrive. This function
+ * may call wake_up_process(dev->raw->thread). Ensure this memory
+ * is not freed by kthread_stop().
+ */
+ get_task_struct(dev->raw->thread);
kthread_stop(dev->raw->thread);
timer_delete_sync(&dev->raw->edge_handle);
- mutex_lock(&ir_raw_handler_lock);
+ mutex_lock(&ir_raw_client_lock);
list_del(&dev->raw->list);
+
+ mutex_lock(&ir_raw_handler_lock);
list_for_each_entry(handler, &ir_raw_handler_list, list)
if (handler->raw_unregister &&
(handler->protocols & dev->enabled_protocols))
handler->raw_unregister(dev);
lirc_bpf_free(dev);
-
- /*
- * A user can be calling bpf(BPF_PROG_{QUERY|ATTACH|DETACH}), so
- * ensure that the raw member is null on unlock; this is how
- * "device gone" is checked.
- */
mutex_unlock(&ir_raw_handler_lock);
+
+ mutex_unlock(&ir_raw_client_lock);
}
/*
@@ -699,15 +688,24 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
struct ir_raw_event_ctrl *raw;
u64 protocols = ir_raw_handler->protocols;
+ mutex_lock(&ir_raw_client_lock);
+
mutex_lock(&ir_raw_handler_lock);
list_del(&ir_raw_handler->list);
+ atomic64_andnot(protocols, &available_protocols);
+ mutex_unlock(&ir_raw_handler_lock);
+
list_for_each_entry(raw, &ir_raw_client_list, list) {
+ mutex_lock(&raw->dev->lock);
+ mutex_lock(&ir_raw_handler_lock);
if (ir_raw_handler->raw_unregister &&
(raw->dev->enabled_protocols & protocols))
ir_raw_handler->raw_unregister(raw->dev);
- ir_raw_disable_protocols(raw->dev, protocols);
+ raw->dev->enabled_protocols &= ~protocols;
+ mutex_unlock(&ir_raw_handler_lock);
+ mutex_unlock(&raw->dev->lock);
}
- atomic64_andnot(protocols, &available_protocols);
- mutex_unlock(&ir_raw_handler_lock);
+
+ mutex_unlock(&ir_raw_client_lock);
}
EXPORT_SYMBOL(ir_raw_handler_unregister);
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 53d0540717b3..9d56e77c9351 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -74,11 +74,6 @@ static int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max)
{
struct loopback_dev *lodev = dev->priv;
- if (min < 1 || min > max) {
- dev_dbg(&dev->dev, "invalid rx carrier range %u to %u\n", min, max);
- return -EINVAL;
- }
-
dev_dbg(&dev->dev, "setting rx carrier range %u to %u\n", min, max);
lodev->rxcarriermin = min;
lodev->rxcarriermax = max;
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index dda3479ea3ad..a867bf9e8416 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -17,9 +17,8 @@
#include <linux/module.h>
#include "rc-core-priv.h"
-/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
-#define IR_TAB_MIN_SIZE 256
-#define IR_TAB_MAX_SIZE 8192
+#define IR_TAB_MIN_ENTRIES 32
+#define IR_TAB_MAX_ENTRIES 1024
static const struct {
const char *name;
@@ -105,7 +104,6 @@ static struct rc_map_list *seek_rc_map(const char *name)
struct rc_map *rc_map_get(const char *name)
{
-
struct rc_map_list *map;
map = seek_rc_map(name);
@@ -202,7 +200,7 @@ static int scancode_to_u64(const struct input_keymap_entry *ke, u64 *scancode)
* ir_create_table() - initializes a scancode table
* @dev: the rc_dev device
* @rc_map: the rc_map to initialize
- * @name: name to assign to the table
+ * @map_name: name to assign to the table
* @rc_proto: ir type to assign to the new table
* @size: initial size of the table
*
@@ -212,23 +210,33 @@ static int scancode_to_u64(const struct input_keymap_entry *ke, u64 *scancode)
* return: zero on success or a negative error code
*/
static int ir_create_table(struct rc_dev *dev, struct rc_map *rc_map,
- const char *name, u64 rc_proto, size_t size)
+ const char *map_name, u64 rc_proto, size_t size)
{
- rc_map->name = kstrdup(name, GFP_KERNEL);
- if (!rc_map->name)
+ struct rc_map_table *scan;
+ unsigned int alloc;
+ char *name;
+
+ name = kstrdup(map_name, GFP_KERNEL);
+ if (!name)
return -ENOMEM;
- rc_map->rc_proto = rc_proto;
- rc_map->alloc = roundup_pow_of_two(size * sizeof(struct rc_map_table));
- rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
- rc_map->scan = kmalloc(rc_map->alloc, GFP_KERNEL);
- if (!rc_map->scan) {
- kfree(rc_map->name);
- rc_map->name = NULL;
+
+ alloc = roundup_pow_of_two(size);
+ scan = kmalloc_objs(struct rc_map_table, alloc, GFP_KERNEL);
+ if (!scan) {
+ kfree(name);
return -ENOMEM;
}
- dev_dbg(&dev->dev, "Allocated space for %u keycode entries (%u bytes)\n",
- rc_map->size, rc_map->alloc);
+ scoped_guard(spinlock_irqsave, &rc_map->lock) {
+ rc_map->name = name;
+ rc_map->scan = scan;
+ rc_map->rc_proto = rc_proto;
+ rc_map->len = 0;
+ rc_map->size = alloc;
+ }
+
+ dev_dbg(&dev->dev, "Allocated space for %u keycode entries (%zu bytes)\n",
+ alloc, alloc * sizeof(struct rc_map_table));
return 0;
}
@@ -236,16 +244,26 @@ static int ir_create_table(struct rc_dev *dev, struct rc_map *rc_map,
* ir_free_table() - frees memory allocated by a scancode table
* @rc_map: the table whose mappings need to be freed
*
- * This routine will free memory alloctaed for key mappings used by given
+ * This routine will free memory allocated for key mappings used by given
* scancode table.
*/
static void ir_free_table(struct rc_map *rc_map)
{
- rc_map->size = 0;
- kfree(rc_map->name);
- rc_map->name = NULL;
- kfree(rc_map->scan);
- rc_map->scan = NULL;
+ struct rc_map_table *scan;
+ const char *name;
+
+ scoped_guard(spinlock_irqsave, &rc_map->lock) {
+ name = rc_map->name;
+ scan = rc_map->scan;
+
+ rc_map->size = 0;
+ rc_map->len = 0;
+ rc_map->name = NULL;
+ rc_map->scan = NULL;
+ }
+
+ kfree(name);
+ kfree(scan);
}
/**
@@ -262,38 +280,38 @@ static void ir_free_table(struct rc_map *rc_map)
static int ir_resize_table(struct rc_dev *dev, struct rc_map *rc_map,
gfp_t gfp_flags)
{
- unsigned int oldalloc = rc_map->alloc;
- unsigned int newalloc = oldalloc;
- struct rc_map_table *oldscan = rc_map->scan;
+ unsigned int newsize = rc_map->size;
struct rc_map_table *newscan;
+ lockdep_assert_held(&rc_map->lock);
+
if (rc_map->size == rc_map->len) {
/* All entries in use -> grow keytable */
- if (rc_map->alloc >= IR_TAB_MAX_SIZE)
+ if (newsize >= IR_TAB_MAX_ENTRIES)
return -ENOMEM;
- newalloc *= 2;
- dev_dbg(&dev->dev, "Growing table to %u bytes\n", newalloc);
+ newsize *= 2;
+
+ dev_dbg(&dev->dev, "Growing table to %u entries\n", newsize);
}
- if ((rc_map->len * 3 < rc_map->size) && (oldalloc > IR_TAB_MIN_SIZE)) {
+ if (rc_map->len * 3 < rc_map->size && rc_map->size > IR_TAB_MIN_ENTRIES) {
/* Less than 1/3 of entries in use -> shrink keytable */
- newalloc /= 2;
- dev_dbg(&dev->dev, "Shrinking table to %u bytes\n", newalloc);
+ newsize /= 2;
+ dev_dbg(&dev->dev, "Shrinking table to %u entries\n", newsize);
}
- if (newalloc == oldalloc)
+ if (newsize == rc_map->size)
return 0;
- newscan = kmalloc(newalloc, gfp_flags);
+ newscan = krealloc_array(rc_map->scan, newsize,
+ sizeof(struct rc_map_table), gfp_flags);
if (!newscan)
return -ENOMEM;
- memcpy(newscan, rc_map->scan, rc_map->len * sizeof(struct rc_map_table));
rc_map->scan = newscan;
- rc_map->alloc = newalloc;
- rc_map->size = rc_map->alloc / sizeof(struct rc_map_table);
- kfree(oldscan);
+ rc_map->size = newsize;
+
return 0;
}
@@ -318,6 +336,8 @@ static unsigned int ir_update_mapping(struct rc_dev *dev,
int old_keycode = rc_map->scan[index].keycode;
int i;
+ lockdep_assert_held(&rc_map->lock);
+
/* Did the user wish to remove the mapping? */
if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) {
dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04llx\n",
@@ -371,7 +391,9 @@ static unsigned int ir_establish_scancode(struct rc_dev *dev,
struct rc_map *rc_map,
u64 scancode, bool resize)
{
- unsigned int i;
+ unsigned int i, lo, hi;
+
+ lockdep_assert_held(&rc_map->lock);
/*
* Unfortunately, some hardware-based IR decoders don't provide
@@ -384,20 +406,26 @@ static unsigned int ir_establish_scancode(struct rc_dev *dev,
if (dev->scancode_mask)
scancode &= dev->scancode_mask;
- /* First check if we already have a mapping for this ir command */
- for (i = 0; i < rc_map->len; i++) {
+ /*
+ * Binary search for an existing mapping for this ir command.
+ */
+ lo = 0;
+ hi = rc_map->len;
+ while (lo < hi) {
+ i = lo + (hi - lo) / 2;
if (rc_map->scan[i].scancode == scancode)
return i;
-
- /* Keytable is sorted from lowest to highest scancode */
- if (rc_map->scan[i].scancode >= scancode)
- break;
+ if (rc_map->scan[i].scancode < scancode)
+ lo = i + 1;
+ else
+ hi = i;
}
+ i = lo;
/* No previous mapping found, we might need to grow the table */
if (rc_map->size == rc_map->len) {
if (!resize || ir_resize_table(dev, rc_map, GFP_ATOMIC))
- return -1U;
+ return UINT_MAX;
}
/* i is the proper index to insert our new keycode */
@@ -479,16 +507,18 @@ static int ir_setkeytable(struct rc_dev *dev, const struct rc_map *from)
if (rc)
return rc;
- for (i = 0; i < from->size; i++) {
- index = ir_establish_scancode(dev, rc_map,
- from->scan[i].scancode, false);
- if (index >= rc_map->len) {
- rc = -ENOMEM;
- break;
- }
+ scoped_guard(spinlock_irqsave, &rc_map->lock) {
+ for (i = 0; i < from->size; i++) {
+ index = ir_establish_scancode(dev, rc_map,
+ from->scan[i].scancode, false);
+ if (index >= rc_map->len) {
+ rc = -ENOMEM;
+ break;
+ }
- ir_update_mapping(dev, rc_map, index,
- from->scan[i].keycode);
+ ir_update_mapping(dev, rc_map, index,
+ from->scan[i].keycode);
+ }
}
if (rc)
@@ -524,6 +554,8 @@ static unsigned int ir_lookup_by_scancode(const struct rc_map *rc_map,
{
struct rc_map_table *res;
+ lockdep_assert_held(&rc_map->lock);
+
res = bsearch(&scancode, rc_map->scan, rc_map->len,
sizeof(struct rc_map_table), rc_map_cmp);
if (!res)
@@ -1701,14 +1733,24 @@ static const struct device_type rc_dev_type = {
struct rc_dev *rc_allocate_device(enum rc_driver_type type)
{
struct rc_dev *dev;
+ int ret;
dev = kzalloc_obj(*dev);
if (!dev)
return NULL;
+ if (type == RC_DRIVER_IR_RAW) {
+ ret = ir_raw_event_prepare(dev);
+ if (ret < 0) {
+ kfree(dev);
+ return NULL;
+ }
+ }
+
if (type != RC_DRIVER_IR_RAW_TX) {
dev->input_dev = input_allocate_device();
if (!dev->input_dev) {
+ ir_raw_event_free(dev);
kfree(dev);
return NULL;
}
@@ -1724,6 +1766,7 @@ struct rc_dev *rc_allocate_device(enum rc_driver_type type)
spin_lock_init(&dev->rc_map.lock);
spin_lock_init(&dev->keylock);
}
+
mutex_init(&dev->lock);
dev->dev.type = &rc_dev_type;
@@ -1742,7 +1785,12 @@ void rc_free_device(struct rc_dev *dev)
if (!dev)
return;
- input_free_device(dev->input_dev);
+ if (dev->input_dev) {
+ timer_delete_sync(&dev->timer_keyup);
+ timer_delete_sync(&dev->timer_repeat);
+ }
+
+ input_put_device(dev->input_dev);
put_device(&dev->dev);
@@ -1854,6 +1902,8 @@ static int rc_setup_rx_device(struct rc_dev *dev)
if (rc)
return rc;
+ input_get_device(dev->input_dev);
+
/*
* Default delay of 250ms is too short for some protocols, especially
* since the timeout is currently set to 250ms. Increase it to 500ms,
@@ -1880,10 +1930,8 @@ static void rc_free_rx_device(struct rc_dev *dev)
if (!dev)
return;
- if (dev->input_dev) {
+ if (dev->input_dev)
input_unregister_device(dev->input_dev);
- dev->input_dev = NULL;
- }
ir_free_table(&dev->rc_map);
}
@@ -1917,19 +1965,14 @@ int rc_register_device(struct rc_dev *dev)
dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp;
dev->sysfs_groups[attr++] = NULL;
- if (dev->driver_type == RC_DRIVER_IR_RAW) {
- rc = ir_raw_event_prepare(dev);
- if (rc < 0)
- goto out_minor;
- }
-
if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
rc = rc_prepare_rx_device(dev);
if (rc)
goto out_raw;
}
- dev->registered = true;
+ scoped_guard(mutex, &dev->lock)
+ dev->registered = true;
rc = device_add(&dev->dev);
if (rc)
@@ -1949,7 +1992,7 @@ int rc_register_device(struct rc_dev *dev)
if (dev->allowed_protocols != RC_PROTO_BIT_CEC) {
rc = lirc_register(dev);
if (rc < 0)
- goto out_dev;
+ goto out_lirc;
}
if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
@@ -1972,15 +2015,20 @@ int rc_register_device(struct rc_dev *dev)
out_rx:
rc_free_rx_device(dev);
out_lirc:
- if (dev->allowed_protocols != RC_PROTO_BIT_CEC)
- lirc_unregister(dev);
-out_dev:
+ scoped_guard(mutex, &dev->lock)
+ dev->registered = false;
+
+ lirc_unregister(dev);
device_del(&dev->dev);
+ /* registered already cleared above */
+ goto out_free_table;
out_rx_free:
- ir_free_table(&dev->rc_map);
+ scoped_guard(mutex, &dev->lock)
+ dev->registered = false;
+out_free_table:
+ if (dev->driver_type != RC_DRIVER_IR_RAW_TX)
+ ir_free_table(&dev->rc_map);
out_raw:
- ir_raw_event_free(dev);
-out_minor:
ida_free(&rc_ida, minor);
return rc;
}
@@ -2018,18 +2066,18 @@ void rc_unregister_device(struct rc_dev *dev)
if (!dev)
return;
+ mutex_lock(&dev->lock);
+ dev->registered = false;
+ if (dev->users && dev->close)
+ dev->close(dev);
+ mutex_unlock(&dev->lock);
+
if (dev->driver_type == RC_DRIVER_IR_RAW)
ir_raw_event_unregister(dev);
timer_delete_sync(&dev->timer_keyup);
timer_delete_sync(&dev->timer_repeat);
- mutex_lock(&dev->lock);
- if (dev->users && dev->close)
- dev->close(dev);
- dev->registered = false;
- mutex_unlock(&dev->lock);
-
rc_free_rx_device(dev);
/*
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
index 3f828a564e19..3e82b5e66477 100644
--- a/drivers/media/rc/redrat3.c
+++ b/drivers/media/rc/redrat3.c
@@ -358,8 +358,24 @@ static void redrat3_process_ir_data(struct redrat3_dev *rr3)
/* process each rr3 encoded byte into an int */
sig_size = be16_to_cpu(rr3->irdata.sig_size);
+
+ /*
+ * Note we are not checking if we are reading beyond the end of the
+ * packet which was sent, and reading stale data. If the device
+ * sends a packet which is short then we get garbage IR, but no
+ * out of bounds read.
+ */
+ if (sig_size > RR3_MAX_SIG_SIZE) {
+ dev_err(dev, "length %u is incorrect\n", sig_size);
+ return;
+ }
+
for (i = 0; i < sig_size; i++) {
offset = rr3->irdata.sigdata[i];
+ if (offset >= RR3_DRIVER_MAXLENS) {
+ dev_err(dev, "offset %u is incorrect\n", offset);
+ return;
+ }
val = get_unaligned_be16(&rr3->irdata.lens[offset]);
/* we should always get pulse/space/pulse/space samples */
@@ -757,17 +773,9 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf,
u8 curlencheck = 0;
unsigned i, sendbuf_len;
- if (rr3->transmitting) {
- dev_warn(dev, "%s: transmitter already in use\n", __func__);
- return -EAGAIN;
- }
-
if (count > RR3_MAX_SIG_SIZE - RR3_TX_TRAILER_LEN)
return -EINVAL;
- /* rr3 will disable rc detector on transmit */
- rr3->transmitting = true;
-
sample_lens = kzalloc_objs(*sample_lens, RR3_DRIVER_MAXLENS);
if (!sample_lens)
return -ENOMEM;
@@ -778,6 +786,9 @@ static int redrat3_transmit_ir(struct rc_dev *rcdev, unsigned *txbuf,
goto out;
}
+ /* rr3 will disable rc detector on transmit */
+ rr3->transmitting = true;
+
for (i = 0; i < count; i++) {
cur_sample_len = redrat3_us_to_len(txbuf[i]);
if (cur_sample_len > 0xffff) {
@@ -975,6 +986,7 @@ static int redrat3_dev_probe(struct usb_interface *intf,
struct device *dev = &intf->dev;
struct usb_host_interface *uhi;
struct redrat3_dev *rr3;
+ struct rc_dev *rc;
struct usb_endpoint_descriptor *ep;
struct usb_endpoint_descriptor *ep_narrow = NULL;
struct usb_endpoint_descriptor *ep_wide = NULL;
@@ -1119,9 +1131,12 @@ static int redrat3_dev_probe(struct usb_interface *intf,
return 0;
led_free:
+ rc_unregister_device(rr3->rc);
led_classdev_unregister(&rr3->led);
redrat_free:
+ rc = rr3->rc;
redrat3_delete(rr3, rr3->udev);
+ rc_free_device(rc);
no_endpoints:
return retval;
@@ -1148,6 +1163,7 @@ static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
usb_kill_urb(rr3->narrow_urb);
usb_kill_urb(rr3->wide_urb);
usb_kill_urb(rr3->flash_urb);
+ usb_kill_urb(rr3->learn_urb);
return 0;
}
diff --git a/drivers/media/rc/serial_ir.c b/drivers/media/rc/serial_ir.c
index 992fff82b524..f6702b3df9a0 100644
--- a/drivers/media/rc/serial_ir.c
+++ b/drivers/media/rc/serial_ir.c
@@ -798,7 +798,7 @@ static int __init serial_ir_init_module(void)
static void __exit serial_ir_exit_module(void)
{
- timer_delete_sync(&serial_ir.timeout_timer);
+ timer_shutdown_sync(&serial_ir.timeout_timer);
serial_ir_exit();
}
diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c
index 307985d74fe8..41195ad82734 100644
--- a/drivers/media/rc/streamzap.c
+++ b/drivers/media/rc/streamzap.c
@@ -365,6 +365,7 @@ static int streamzap_probe(struct usb_interface *intf,
return 0;
rc_submit_fail:
+ rc_unregister_device(sz->rdev);
rc_free_device(sz->rdev);
usb_set_intfdata(intf, NULL);
rc_dev_fail:
diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c
index 28e840a7e5b8..af1ee08ffdbe 100644
--- a/drivers/media/rc/sunxi-cir.c
+++ b/drivers/media/rc/sunxi-cir.c
@@ -374,8 +374,8 @@ static void sunxi_ir_remove(struct platform_device *pdev)
struct sunxi_ir *ir = platform_get_drvdata(pdev);
rc_unregister_device(ir->rc);
- rc_free_device(ir->rc);
sunxi_ir_hw_exit(&pdev->dev);
+ rc_free_device(ir->rc);
}
static void sunxi_ir_shutdown(struct platform_device *pdev)
diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig
index 4656afae5bb4..da3e225d420d 100644
--- a/drivers/media/spi/Kconfig
+++ b/drivers/media/spi/Kconfig
@@ -1,15 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
if VIDEO_DEV && SPI
-comment "SPI I2C drivers auto-selected by 'Autoselect ancillary drivers'"
- depends on MEDIA_HIDE_ANCILLARY_SUBDRV && SPI
-
menu "Media SPI Adapters"
config CXD2880_SPI_DRV
tristate "Sony CXD2880 SPI support"
depends on DVB_CORE && SPI
- default m if !MEDIA_SUBDRV_AUTOSELECT
help
Choose if you would like to have SPI interface support for Sony CXD2880.
diff --git a/drivers/media/test-drivers/vicodec/codec-fwht.h b/drivers/media/test-drivers/vicodec/codec-fwht.h
index 0eab24020e9e..4b4d39031089 100644
--- a/drivers/media/test-drivers/vicodec/codec-fwht.h
+++ b/drivers/media/test-drivers/vicodec/codec-fwht.h
@@ -61,7 +61,7 @@
* both luma and chroma components resolutions are rounded up to
* a multiple of 8
*/
-#define vic_round_dim(dim, div) (round_up((dim) / (div), 8) * (div))
+#define vic_round_dim(dim, div) round_up(dim, 8 * (div))
struct fwht_cframe_hdr {
u32 magic1;
diff --git a/drivers/media/test-drivers/vicodec/vicodec-core.c b/drivers/media/test-drivers/vicodec/vicodec-core.c
index ff9d50fb05fd..7ea024b14f0e 100644
--- a/drivers/media/test-drivers/vicodec/vicodec-core.c
+++ b/drivers/media/test-drivers/vicodec/vicodec-core.c
@@ -890,6 +890,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vicodec_ctx *ctx = file2ctx(file);
+ struct vicodec_q_data *q_data_out =
+ get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
struct v4l2_pix_format_mplane *pix_mp;
struct v4l2_pix_format *pix;
@@ -900,6 +902,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
pix = &f->fmt.pix;
pix->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT :
find_fmt(f->fmt.pix.pixelformat)->id;
+ if (ctx->is_enc) {
+ pix->width = q_data_out->coded_width;
+ pix->height = q_data_out->coded_height;
+ }
pix->colorspace = ctx->state.colorspace;
pix->xfer_func = ctx->state.xfer_func;
pix->ycbcr_enc = ctx->state.ycbcr_enc;
@@ -911,6 +917,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
pix_mp = &f->fmt.pix_mp;
pix_mp->pixelformat = ctx->is_enc ? V4L2_PIX_FMT_FWHT :
find_fmt(pix_mp->pixelformat)->id;
+ if (ctx->is_enc) {
+ pix_mp->width = q_data_out->coded_width;
+ pix_mp->height = q_data_out->coded_height;
+ }
pix_mp->colorspace = ctx->state.colorspace;
pix_mp->xfer_func = ctx->state.xfer_func;
pix_mp->ycbcr_enc = ctx->state.ycbcr_enc;
@@ -1595,9 +1605,9 @@ static int vicodec_start_streaming(struct vb2_queue *q,
}
state->ref_stride = q_data->coded_width * info->luma_alpha_step;
- state->ref_frame.buf = kvmalloc(total_planes_size, GFP_KERNEL);
+ state->ref_frame.buf = kvzalloc(total_planes_size, GFP_KERNEL);
state->ref_frame.luma = state->ref_frame.buf;
- new_comp_frame = kvmalloc(ctx->comp_max_size, GFP_KERNEL);
+ new_comp_frame = kvzalloc(ctx->comp_max_size, GFP_KERNEL);
if (!state->ref_frame.luma || !new_comp_frame) {
kvfree(state->ref_frame.luma);
diff --git a/drivers/media/test-drivers/vidtv/vidtv_bridge.c b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
index fd69b4ee16f4..4c98dfbd406f 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_bridge.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_bridge.c
@@ -44,6 +44,10 @@
#define LNB_LOW_FREQ 9750000 /* low IF frequency */
#define LNB_HIGH_FREQ 10600000 /* transition frequency */
+/* The Ku-band range covered by such an LNBf, in kHz */
+#define LNB_KU_BAND_MIN_FREQ 10700000
+#define LNB_KU_BAND_MAX_FREQ 12750000
+
static unsigned int drop_tslock_prob_on_low_snr;
module_param(drop_tslock_prob_on_low_snr, uint, 0444);
MODULE_PARM_DESC(drop_tslock_prob_on_low_snr,
@@ -367,6 +371,36 @@ static int vidtv_bridge_probe_demod(struct vidtv_dvb *dvb, u32 n)
return 0;
}
+/*
+ * Reject frequencies the simulation could never tune into, as the module
+ * would otherwise load just fine and then never lock on anything, leaving
+ * no clue about what went wrong.
+ */
+static int vidtv_bridge_check_freqs(struct vidtv_dvb *dvb,
+ const unsigned int *freqs,
+ u32 array_sz,
+ u32 min_freq,
+ u32 max_freq,
+ const char *name)
+{
+ u32 i;
+
+ for (i = 0; i < array_sz; i++) {
+ /* a zeroed entry means an unused slot */
+ if (!freqs[i])
+ continue;
+
+ if (freqs[i] < min_freq || freqs[i] > max_freq) {
+ dev_err(&dvb->pdev->dev,
+ "%s[%u]: %u is out of range (%u..%u)\n",
+ name, i, freqs[i], min_freq, max_freq);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static int vidtv_bridge_probe_tuner(struct vidtv_dvb *dvb, u32 n)
{
struct vidtv_tuner_config cfg = {
@@ -374,10 +408,45 @@ static int vidtv_bridge_probe_tuner(struct vidtv_dvb *dvb, u32 n)
.mock_power_up_delay_msec = mock_power_up_delay_msec,
.mock_tune_delay_msec = mock_tune_delay_msec,
};
+ u32 min_freq = dvb->fe[n]->ops.info.frequency_min_hz;
+ u32 max_freq = dvb->fe[n]->ops.info.frequency_max_hz;
u32 freq;
+ int ret;
int i;
- /* TODO: check if the frequencies are at a valid range */
+ /*
+ * Terrestrial and cable frequencies are given in Hz and are used as
+ * is, so they have to fit within the range the demod reports to the
+ * DVB core: the core rejects a tuning request outside of it before
+ * the tuner is ever asked about the frequency.
+ */
+ ret = vidtv_bridge_check_freqs(dvb, vidtv_valid_dvb_t_freqs,
+ ARRAY_SIZE(vidtv_valid_dvb_t_freqs),
+ min_freq, max_freq,
+ "vidtv_valid_dvb_t_freqs");
+ if (ret)
+ return ret;
+
+ ret = vidtv_bridge_check_freqs(dvb, vidtv_valid_dvb_c_freqs,
+ ARRAY_SIZE(vidtv_valid_dvb_c_freqs),
+ min_freq, max_freq,
+ "vidtv_valid_dvb_c_freqs");
+ if (ret)
+ return ret;
+
+ /*
+ * Satellite frequencies are given in kHz at Ku-band and are
+ * downconverted below, so check them against the band the simulated
+ * LNBf covers instead. Doing so also ensures that the frequencies
+ * are above the LNBf local oscillators.
+ */
+ ret = vidtv_bridge_check_freqs(dvb, vidtv_valid_dvb_s_freqs,
+ ARRAY_SIZE(vidtv_valid_dvb_s_freqs),
+ LNB_KU_BAND_MIN_FREQ,
+ LNB_KU_BAND_MAX_FREQ,
+ "vidtv_valid_dvb_s_freqs");
+ if (ret)
+ return ret;
memcpy(cfg.vidtv_valid_dvb_t_freqs,
vidtv_valid_dvb_t_freqs,
@@ -474,6 +543,7 @@ fail_dmx:
fail_demod_probe:
for (i = i - 1; i >= 0; --i) {
dvb_unregister_frontend(dvb->fe[i]);
+ dvb_frontend_detach(dvb->fe[i]);
fail_fe:
dvb_module_release(dvb->i2c_client_tuner[i]);
fail_tuner_probe:
@@ -550,8 +620,11 @@ static void vidtv_bridge_remove(struct platform_device *pdev)
mutex_destroy(&dvb->feed_lock);
+ vidtv_stop_streaming(dvb);
+
for (i = 0; i < NUM_FE; ++i) {
dvb_unregister_frontend(dvb->fe[i]);
+ dvb_frontend_detach(dvb->fe[i]);
dvb_module_release(dvb->i2c_client_tuner[i]);
dvb_module_release(dvb->i2c_client_demod[i]);
}
diff --git a/drivers/media/test-drivers/vidtv/vidtv_demod.c b/drivers/media/test-drivers/vidtv/vidtv_demod.c
index 6e5fe402976b..3aa586004638 100644
--- a/drivers/media/test-drivers/vidtv/vidtv_demod.c
+++ b/drivers/media/test-drivers/vidtv/vidtv_demod.c
@@ -343,13 +343,6 @@ static int vidtv_diseqc_send_burst(struct dvb_frontend *fe,
return 0;
}
-static void vidtv_demod_release(struct dvb_frontend *fe)
-{
- struct vidtv_demod_state *state = fe->demodulator_priv;
-
- kfree(state);
-}
-
static const struct dvb_frontend_ops vidtv_demod_ops = {
.delsys = {
SYS_DVBT,
@@ -390,8 +383,6 @@ static const struct dvb_frontend_ops vidtv_demod_ops = {
FE_CAN_HIERARCHY_AUTO,
},
- .release = vidtv_demod_release,
-
.set_frontend = vidtv_demod_set_frontend,
.get_frontend = vidtv_demod_get_frontend,
diff --git a/drivers/media/test-drivers/vim2m.c b/drivers/media/test-drivers/vim2m.c
index bb2dd11eef0e..459fd4aedf30 100644
--- a/drivers/media/test-drivers/vim2m.c
+++ b/drivers/media/test-drivers/vim2m.c
@@ -150,8 +150,8 @@ enum {
V4L2_M2M_DST = 1,
};
-#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_BASE + 0x1000)
-#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_BASE + 0x1001)
+#define V4L2_CID_TRANS_TIME_MSEC (V4L2_CID_USER_VIM2M_BASE + 0)
+#define V4L2_CID_TRANS_NUM_BUFS (V4L2_CID_USER_VIM2M_BASE + 1)
static struct vim2m_fmt *find_format(u32 fourcc)
{
@@ -205,6 +205,7 @@ struct vim2m_ctx {
struct vim2m_dev *dev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_ctrl *trans_num_bufs_ctrl;
/* Processed buffers in this transaction */
u8 num_processed;
@@ -1258,9 +1259,27 @@ static int vim2m_start_streaming(struct vb2_queue *q, unsigned int count)
ctx->aborting = 0;
q_data->sequence = 0;
+ v4l2_ctrl_grab(ctx->trans_num_bufs_ctrl, true);
+
return 0;
}
+static bool vim2m_other_queue_is_streaming(struct vim2m_ctx *ctx,
+ struct vb2_queue *q)
+{
+ struct vb2_queue *other_vq;
+
+ if (!ctx->fh.m2m_ctx)
+ return false;
+
+ if (V4L2_TYPE_IS_OUTPUT(q->type))
+ other_vq = v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx);
+ else
+ other_vq = v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx);
+
+ return vb2_is_streaming(other_vq);
+}
+
static void vim2m_stop_streaming(struct vb2_queue *q)
{
struct vim2m_ctx *ctx = vb2_get_drv_priv(q);
@@ -1274,11 +1293,14 @@ static void vim2m_stop_streaming(struct vb2_queue *q)
else
vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
if (!vbuf)
- return;
+ break;
v4l2_ctrl_request_complete(vbuf->vb2_buf.req_obj.req,
&ctx->hdl);
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
}
+
+ if (!vim2m_other_queue_is_streaming(ctx, q))
+ v4l2_ctrl_grab(ctx->trans_num_bufs_ctrl, false);
}
static void vim2m_buf_request_complete(struct vb2_buffer *vb)
@@ -1380,7 +1402,8 @@ static int vim2m_open(struct file *file)
vim2m_ctrl_trans_time_msec.def = default_transtime;
v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_time_msec, NULL);
- v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
+ ctx->trans_num_bufs_ctrl =
+ v4l2_ctrl_new_custom(hdl, &vim2m_ctrl_trans_num_bufs, NULL);
if (hdl->error) {
rc = hdl->error;
v4l2_ctrl_handler_free(hdl);
@@ -1435,10 +1458,10 @@ static int vim2m_release(struct file *file)
v4l2_fh_del(&ctx->fh, file);
v4l2_fh_exit(&ctx->fh);
- v4l2_ctrl_handler_free(&ctx->hdl);
mutex_lock(&dev->dev_mutex);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
mutex_unlock(&dev->dev_mutex);
+ v4l2_ctrl_handler_free(&ctx->hdl);
kfree(ctx);
atomic_dec(&dev->num_inst);
diff --git a/drivers/media/test-drivers/vimc/vimc-debayer.c b/drivers/media/test-drivers/vimc/vimc-debayer.c
index 0c2e715a8a16..cfda447a5b76 100644
--- a/drivers/media/test-drivers/vimc/vimc-debayer.c
+++ b/drivers/media/test-drivers/vimc/vimc-debayer.c
@@ -240,6 +240,7 @@ static void vimc_debayer_adjust_sink_fmt(struct v4l2_mbus_framefmt *fmt)
}
static int vimc_debayer_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/test-drivers/vimc/vimc-scaler.c b/drivers/media/test-drivers/vimc/vimc-scaler.c
index e2037c67e423..d8dc65d33f62 100644
--- a/drivers/media/test-drivers/vimc/vimc-scaler.c
+++ b/drivers/media/test-drivers/vimc/vimc-scaler.c
@@ -140,6 +140,7 @@ static int vimc_scaler_enum_frame_size(struct v4l2_subdev *sd,
}
static int vimc_scaler_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
@@ -204,6 +205,7 @@ static int vimc_scaler_set_fmt(struct v4l2_subdev *sd,
}
static int vimc_scaler_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -239,6 +241,7 @@ static void vimc_scaler_adjust_sink_crop(struct v4l2_rect *r,
}
static int vimc_scaler_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/media/test-drivers/vimc/vimc-sensor.c b/drivers/media/test-drivers/vimc/vimc-sensor.c
index 83dcc9d61ee0..f07f4237f6ad 100644
--- a/drivers/media/test-drivers/vimc/vimc-sensor.c
+++ b/drivers/media/test-drivers/vimc/vimc-sensor.c
@@ -97,18 +97,14 @@ static void vimc_sensor_update_frame_timing(struct v4l2_subdev *sd,
{
struct vimc_sensor_device *vsensor =
container_of(sd, struct vimc_sensor_device, sd);
- u64 pixel_rate = vsensor->pixel_rate->val;
+ u32 pixel_rate = vsensor->pixel_rate->val;
u32 hts = width + vsensor->hblank->val;
u32 vts = height + vsensor->vblank->val;
u64 total_pixels = (u64)hts * vts;
u64 frame_interval_ns;
- /* Sanity check, pixel rate is fixed and fits in 32 bits. */
- if (WARN_ON(pixel_rate >= 0x100000000))
- return;
-
frame_interval_ns = total_pixels * NSEC_PER_SEC;
- do_div(frame_interval_ns, (u32)pixel_rate);
+ do_div(frame_interval_ns, pixel_rate);
vsensor->hw.fps_jiffies = nsecs_to_jiffies(frame_interval_ns);
if (vsensor->hw.fps_jiffies == 0)
vsensor->hw.fps_jiffies = 1;
@@ -154,6 +150,7 @@ static u32 vimc_calc_vblank(u32 width, u32 height,
}
static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/media/test-drivers/vivid/vivid-cec.c b/drivers/media/test-drivers/vivid/vivid-cec.c
index 2d15fdd5d999..8d301578e2eb 100644
--- a/drivers/media/test-drivers/vivid/vivid-cec.c
+++ b/drivers/media/test-drivers/vivid/vivid-cec.c
@@ -23,7 +23,7 @@ struct xfer_on_bus {
static bool find_dest_adap(struct vivid_dev *dev,
struct cec_adapter *adap, u8 dest)
{
- unsigned int i, j;
+ unsigned int i;
if (dest >= 0xf)
return false;
@@ -33,13 +33,12 @@ static bool find_dest_adap(struct vivid_dev *dev,
cec_has_log_addr(dev->cec_rx_adap, dest))
return true;
- for (i = 0, j = 0; i < dev->num_inputs; i++) {
+ for (i = 0; i < dev->num_inputs; i++) {
unsigned int menu_idx =
dev->input_is_connected_to_output[i];
if (dev->input_type[i] != HDMI)
continue;
- j++;
if (menu_idx < FIXED_MENU_ITEMS)
continue;
@@ -113,7 +112,7 @@ static void adjust_sfts(struct vivid_dev *dev)
int vivid_cec_bus_thread(void *_dev)
{
u32 last_sft;
- unsigned int i, j;
+ unsigned int i;
unsigned int dest;
ktime_t start, end;
s64 delta_us, retry_us;
@@ -210,13 +209,12 @@ int vivid_cec_bus_thread(void *_dev)
if (first_status == CEC_TX_STATUS_OK) {
if (xfers_on_bus[first_idx].adap != dev->cec_rx_adap)
cec_received_msg(dev->cec_rx_adap, &first_msg);
- for (i = 0, j = 0; i < dev->num_inputs; i++) {
+ for (i = 0; i < dev->num_inputs; i++) {
unsigned int menu_idx =
dev->input_is_connected_to_output[i];
if (dev->input_type[i] != HDMI)
continue;
- j++;
if (menu_idx < FIXED_MENU_ITEMS)
continue;
diff --git a/drivers/media/test-drivers/vivid/vivid-core.c b/drivers/media/test-drivers/vivid/vivid-core.c
index 62cfb5feb2cf..c042d92db175 100644
--- a/drivers/media/test-drivers/vivid/vivid-core.c
+++ b/drivers/media/test-drivers/vivid/vivid-core.c
@@ -854,6 +854,7 @@ static void vivid_dev_release(struct v4l2_device *v4l2_dev)
struct vivid_dev *dev = container_of(v4l2_dev, struct vivid_dev, v4l2_dev);
cancel_work_sync(&dev->update_hdmi_ctrl_work);
+ cancel_work_sync(&dev->update_svid_ctrl_work);
vivid_free_controls(dev);
v4l2_device_unregister(&dev->v4l2_dev);
#ifdef CONFIG_MEDIA_CONTROLLER
diff --git a/drivers/media/test-drivers/vivid/vivid-vid-cap.c b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
index e20449084709..147af0f9b077 100644
--- a/drivers/media/test-drivers/vivid/vivid-vid-cap.c
+++ b/drivers/media/test-drivers/vivid/vivid-vid-cap.c
@@ -570,6 +570,7 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
const struct vivid_fmt *fmt;
unsigned bytesperline, max_bpl;
unsigned factor = 1;
+ unsigned int vdiv = 1;
unsigned w, h;
unsigned p;
bool user_set_csc = !!(mp->flags & V4L2_PIX_FMT_FLAG_SET_CSC);
@@ -622,6 +623,18 @@ int vivid_try_fmt_vid_cap(struct file *file, void *priv,
mp->height = r.height / factor;
}
+ /*
+ * The chroma planes of vertically subsampled formats hold
+ * height / vdownsampling lines. If the height is not a multiple of
+ * the subsampling factor, then the buffer size calculations round
+ * that number down while the test pattern generator rounds it up,
+ * so the generator writes one line past the end of the buffer.
+ * Round the height down to keep both in sync.
+ */
+ for (p = 0; p < fmt->planes; p++)
+ vdiv = max(vdiv, fmt->vdownsampling[p]);
+ mp->height = rounddown(mp->height, vdiv);
+
/* This driver supports custom bytesperline values */
mp->num_planes = fmt->buffers;
diff --git a/drivers/media/tuners/tda18250.c b/drivers/media/tuners/tda18250.c
index bd7ed50fb6e8..e4b4106e91b6 100644
--- a/drivers/media/tuners/tda18250.c
+++ b/drivers/media/tuners/tda18250.c
@@ -804,8 +804,7 @@ static int tda18250_probe(struct i2c_client *client)
/* read the three chip ID registers */
regmap_bulk_read(dev->regmap, R00_ID1, &chip_id, 3);
- dev_dbg(&client->dev, "chip_id=%02x:%02x:%02x",
- chip_id[0], chip_id[1], chip_id[2]);
+ dev_dbg(&client->dev, "chip_id=%3phC", chip_id);
switch (chip_id[0]) {
case 0xc7:
diff --git a/drivers/media/usb/au0828/au0828-core.c b/drivers/media/usb/au0828/au0828-core.c
index 445cbeb7abae..c3826b3b8a2e 100644
--- a/drivers/media/usb/au0828/au0828-core.c
+++ b/drivers/media/usb/au0828/au0828-core.c
@@ -697,6 +697,10 @@ static int au0828_usb_probe(struct usb_interface *interface,
retval = au0828_v4l2_device_register(interface, dev);
if (retval) {
au0828_usb_v4l2_media_release(dev);
+#ifdef CONFIG_MEDIA_CONTROLLER
+ media_device_delete(dev->media_dev, KBUILD_MODNAME,
+ THIS_MODULE);
+#endif
mutex_unlock(&dev->lock);
kfree(dev);
return retval;
diff --git a/drivers/media/usb/au0828/au0828-dvb.c b/drivers/media/usb/au0828/au0828-dvb.c
index 9c95b7ceaecd..601a12e122f1 100644
--- a/drivers/media/usb/au0828/au0828-dvb.c
+++ b/drivers/media/usb/au0828/au0828-dvb.c
@@ -163,9 +163,6 @@ static int stop_urb_transfer(struct au0828_dev *dev)
dprintk(2, "%s()\n", __func__);
- if (!dev->urb_streaming)
- return 0;
-
if (dev->bulk_timeout_running == 1) {
dev->bulk_timeout_running = 0;
timer_delete(&dev->bulk_timeout);
@@ -179,6 +176,7 @@ static int stop_urb_transfer(struct au0828_dev *dev)
kfree(dev->urbs[i]->transfer_buffer);
usb_free_urb(dev->urbs[i]);
+ dev->urbs[i] = NULL;
}
}
@@ -200,8 +198,10 @@ static int start_urb_transfer(struct au0828_dev *dev)
for (i = 0; i < URB_COUNT; i++) {
dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->urbs[i])
- return -ENOMEM;
+ if (!dev->urbs[i]) {
+ ret = -ENOMEM;
+ goto err;
+ }
purb = dev->urbs[i];
@@ -217,7 +217,7 @@ static int start_urb_transfer(struct au0828_dev *dev)
ret = -ENOMEM;
pr_err("%s: failed big buffer allocation, err = %d\n",
__func__, ret);
- return ret;
+ goto err;
}
purb->status = -EINPROGRESS;
@@ -235,10 +235,9 @@ static int start_urb_transfer(struct au0828_dev *dev)
for (i = 0; i < URB_COUNT; i++) {
ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
if (ret != 0) {
- stop_urb_transfer(dev);
pr_err("%s: failed urb submission, err = %d\n",
__func__, ret);
- return ret;
+ goto err;
}
}
@@ -249,6 +248,10 @@ static int start_urb_transfer(struct au0828_dev *dev)
dev->bulk_timeout_running = 1;
return 0;
+
+err:
+ stop_urb_transfer(dev);
+ return ret;
}
static void au0828_start_transport(struct au0828_dev *dev)
@@ -537,6 +540,7 @@ void au0828_dvb_unregister(struct au0828_dev *dev)
if (dvb->frontend == NULL)
return;
+ timer_shutdown_sync(&dev->bulk_timeout);
cancel_work_sync(&dev->restart_streaming);
dvb_net_release(&dvb->net);
diff --git a/drivers/media/usb/cx231xx/cx231xx-417.c b/drivers/media/usb/cx231xx/cx231xx-417.c
index c695a97e202b..d0955ea18195 100644
--- a/drivers/media/usb/cx231xx/cx231xx-417.c
+++ b/drivers/media/usb/cx231xx/cx231xx-417.c
@@ -1666,7 +1666,7 @@ static int cx231xx_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
format.format.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
format.format.height = cxhdl->height;
format.format.code = MEDIA_BUS_FMT_FIXED;
- v4l2_subdev_call(dev->sd_cx25840, pad, set_fmt, NULL, &format);
+ v4l2_subdev_call(dev->sd_cx25840, pad, set_fmt, NULL, NULL, &format);
return 0;
}
diff --git a/drivers/media/usb/cx231xx/cx231xx-audio.c b/drivers/media/usb/cx231xx/cx231xx-audio.c
index b24ceef497e4..af04fb104cc4 100644
--- a/drivers/media/usb/cx231xx/cx231xx-audio.c
+++ b/drivers/media/usb/cx231xx/cx231xx-audio.c
@@ -581,12 +581,19 @@ static int cx231xx_audio_init(struct cx231xx *dev)
dev_dbg(dev->dev,
"probing for cx231xx non standard usbaudio\n");
+ /*
+ * Extension init errors are ignored by the cx231xx core, so fini()
+ * must be safe even if initialization fails part way through.
+ */
+ spin_lock_init(&adev->slock);
+ INIT_WORK(&dev->wq_trigger, audio_trigger);
+ atomic_set(&dev->stream_started, 0);
+
err = snd_card_new(dev->dev, index[devnr], "Cx231xx Audio",
THIS_MODULE, 0, &card);
if (err < 0)
return err;
- spin_lock_init(&adev->slock);
err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
if (err < 0)
goto err_free_card;
@@ -601,8 +608,6 @@ static int cx231xx_audio_init(struct cx231xx *dev)
strscpy(card->shortname, "Cx231xx Audio", sizeof(card->shortname));
strscpy(card->longname, "Conexant cx231xx Audio", sizeof(card->longname));
- INIT_WORK(&dev->wq_trigger, audio_trigger);
-
err = snd_card_register(card);
if (err < 0)
goto err_free_card;
@@ -656,8 +661,10 @@ static int cx231xx_audio_init(struct cx231xx *dev)
err_free_pkt_size:
kfree(adev->alt_max_pkt_size);
+ adev->alt_max_pkt_size = NULL;
err_free_card:
snd_card_free(card);
+ adev->sndcard = NULL;
return err;
}
@@ -674,6 +681,8 @@ static int cx231xx_audio_fini(struct cx231xx *dev)
return 0;
}
+ disable_work_sync(&dev->wq_trigger);
+
if (dev->adev.sndcard) {
snd_card_free_when_closed(dev->adev.sndcard);
kfree(dev->adev.alt_max_pkt_size);
diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c
index 69b24205bc56..b0941cd2a2d8 100644
--- a/drivers/media/usb/cx231xx/cx231xx-cards.c
+++ b/drivers/media/usb/cx231xx/cx231xx-cards.c
@@ -1826,7 +1826,7 @@ static int cx231xx_usb_probe(struct usb_interface *interface,
retval = cx231xx_init_v4l2(dev, udev, interface, isoc_pipe);
if (retval)
- goto err_init;
+ goto err_video_alt;
if (dev->current_pcb_config.ts1_source != 0xff) {
/* compute alternate max packet sizes for TS1 */
diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c
index 70aa99fead27..e8c9c74b4b8a 100644
--- a/drivers/media/usb/cx231xx/cx231xx-video.c
+++ b/drivers/media/usb/cx231xx/cx231xx-video.c
@@ -909,7 +909,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
dev->format = format_by_fourcc(f->fmt.pix.pixelformat);
v4l2_fill_mbus_format(&format.format, &f->fmt.pix, MEDIA_BUS_FMT_FIXED);
- call_all(dev, pad, set_fmt, NULL, &format);
+ call_all(dev, pad, set_fmt, NULL, NULL, &format);
v4l2_fill_pix_format(&f->fmt.pix, &format.format);
return rc;
@@ -950,7 +950,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm)
format.format.code = MEDIA_BUS_FMT_FIXED;
format.format.width = dev->width;
format.format.height = dev->height;
- call_all(dev, pad, set_fmt, NULL, &format);
+ call_all(dev, pad, set_fmt, NULL, NULL, &format);
/* do mode control overrides */
cx231xx_do_mode_ctrl_overrides(dev);
diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h
index 19f5036a78d7..f85d054fbb5c 100644
--- a/drivers/media/usb/cx231xx/cx231xx.h
+++ b/drivers/media/usb/cx231xx/cx231xx.h
@@ -463,7 +463,7 @@ struct cx231xx_i2c_xfer_data {
u8 direction; /* 1 - IN, 0 - OUT */
u8 saddr_len; /* sub address len */
u16 saddr_dat; /* sub addr data */
- u8 buf_size; /* buffer size */
+ u16 buf_size; /* buffer size */
u8 *p_buffer; /* pointer to the buffer */
};
diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
index 100a1052dcbc..1a7135b03c33 100644
--- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
+++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c
@@ -755,7 +755,7 @@ exit:
buf[0] = USB_WRITE_I2C_CMD;
buf[1] = 0x00;
- /* de-initilize I2C BUS */
+ /* de-initialize I2C BUS */
buf[5] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
@@ -769,7 +769,7 @@ exit:
buf[6] = 0x00;
buf[7] = 0x00;
- /* de-initilize I2C BUS */
+ /* de-initialize I2C BUS */
buf[8] = USB_END_I2C_CMD;
mxl111sf_i2c_send_data(state, 0, buf);
diff --git a/drivers/media/usb/dvb-usb/cxusb-analog.c b/drivers/media/usb/dvb-usb/cxusb-analog.c
index 3bbee1fcbc8d..8c7d0888c721 100644
--- a/drivers/media/usb/dvb-usb/cxusb-analog.c
+++ b/drivers/media/usb/dvb-usb/cxusb-analog.c
@@ -1031,7 +1031,8 @@ static int cxusb_medion_try_s_fmt_vid_cap(struct file *file,
subfmt.format.field = field;
subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M;
- ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt);
+ ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, NULL,
+ &subfmt);
if (ret != 0)
return ret;
@@ -1513,7 +1514,8 @@ int cxusb_medion_analog_init(struct dvb_usb_device *dvbdev)
subfmt.format.field = V4L2_FIELD_SEQ_TB;
subfmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M;
- ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, &subfmt);
+ ret = v4l2_subdev_call(cxdev->cx25840, pad, set_fmt, NULL, NULL,
+ &subfmt);
if (ret != 0)
dev_warn(&dvbdev->udev->dev,
"cx25840 format set failed (%d)\n", ret);
diff --git a/drivers/media/usb/dvb-usb/dib0700_core.c b/drivers/media/usb/dvb-usb/dib0700_core.c
index 1caabb51ea47..5d2fa7037c00 100644
--- a/drivers/media/usb/dvb-usb/dib0700_core.c
+++ b/drivers/media/usb/dvb-usb/dib0700_core.c
@@ -560,7 +560,7 @@ int dib0700_download_firmware(struct usb_device *udev, const struct firmware *fw
fw_version = (buf[8] << 24) | (buf[9] << 16) | (buf[10] << 8) | buf[11];
/* set the buffer size - DVB-USB is allocating URB buffers
- * only after the firwmare download was successful */
+ * only after the firmware download was successful */
for (i = 0; i < dib0700_device_count; i++) {
for (adap_num = 0; adap_num < dib0700_devices[i].num_adapters;
adap_num++) {
diff --git a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
index 0fb3fa6100e4..675d9d1d4f47 100644
--- a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
+++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c
@@ -141,6 +141,8 @@ int dvb_usb_get_hexline(const struct firmware *fw, struct hexline *hx,
if (hx->type == 0x04) {
/* b[4] and b[5] are the Extended linear address record data field */
+ if (hx->len != 2)
+ return -EINVAL;
hx->addr |= (b[4] << 24) | (b[5] << 16);
/* hx->len -= 2;
data_offs += 2; */
diff --git a/drivers/media/usb/em28xx/em28xx-camera.c b/drivers/media/usb/em28xx/em28xx-camera.c
index b5f58dc6dd0f..bc55ad66658c 100644
--- a/drivers/media/usb/em28xx/em28xx-camera.c
+++ b/drivers/media/usb/em28xx/em28xx-camera.c
@@ -392,7 +392,7 @@ int em28xx_init_camera(struct em28xx *dev)
format.format.code = MEDIA_BUS_FMT_YUYV8_2X8;
format.format.width = 640;
format.format.height = 480;
- v4l2_subdev_call(subdev, pad, set_fmt, NULL, &format);
+ v4l2_subdev_call(subdev, pad, set_fmt, NULL, NULL, &format);
/* NOTE: for UXGA=1600x1200 switch to 12MHz */
dev->board.xclk = EM28XX_XCLK_FREQUENCY_24MHZ;
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c
index 79af154767c1..ba992b2055dc 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -431,6 +431,7 @@ static void em2828X_decoder_set_std(struct em28xx *dev, v4l2_std_id norm)
} else if (INPUT(dev->ctl_input)->vmux == EM2828X_TELEVISION) {
em28xx_write_reg(dev, 0x7A00, 0x32);
em28xx_write_reg(dev, 0x7A03, 0x09);
+ em28xx_write_reg(dev, 0x7A07, 0x2f);
em28xx_write_reg(dev, 0x7A30, 0x2a);
em28xx_write_reg(dev, 0x7A80, 0x03);
em28xx_write_reg(dev, 0x7A20, 0x35);
@@ -1410,6 +1411,9 @@ static int em28xx_vb2_setup(struct em28xx *dev)
if (rc < 0)
return rc;
+ if (!em28xx_vbi_supported(dev))
+ return 0;
+
/* Setup Videobuf2 for VBI capture */
q = &v4l2->vb_vbiq;
q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
diff --git a/drivers/media/usb/go7007/go7007-driver.c b/drivers/media/usb/go7007/go7007-driver.c
index 453ab5c3aa03..7db2440886b0 100644
--- a/drivers/media/usb/go7007/go7007-driver.c
+++ b/drivers/media/usb/go7007/go7007-driver.c
@@ -306,10 +306,9 @@ int go7007_register_encoder(struct go7007 *go, unsigned num_i2c_devs)
if (ret < 0)
goto err_free_controls;
- if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) {
+ if ((go->board_info->flags & GO7007_BOARD_HAS_AUDIO) &&
+ go7007_snd_init(go) == 0)
go->audio_enabled = 1;
- go7007_snd_init(go);
- }
return 0;
err_free_controls:
diff --git a/drivers/media/usb/go7007/go7007-usb.c b/drivers/media/usb/go7007/go7007-usb.c
index c0cb92fa6ab9..dc5b8750a110 100644
--- a/drivers/media/usb/go7007/go7007-usb.c
+++ b/drivers/media/usb/go7007/go7007-usb.c
@@ -1029,10 +1029,16 @@ static const struct i2c_algorithm go7007_usb_algo = {
.functionality = go7007_usb_functionality,
};
+static const struct i2c_adapter_quirks go7007_usb_quirks = {
+ .max_write_len = 12,
+ .max_read_len = 15,
+};
+
static struct i2c_adapter go7007_usb_adap_templ = {
.owner = THIS_MODULE,
.name = "WIS GO7007SB EZ-USB",
.algo = &go7007_usb_algo,
+ .quirks = &go7007_usb_quirks,
};
/********************* USB add/remove functions *********************/
@@ -1320,6 +1326,8 @@ static int go7007_usb_probe(struct usb_interface *intf,
return 0;
allocfail:
+ if (go->i2c_adapter_online)
+ i2c_del_adapter(&go->i2c_adapter);
go7007_usb_release(go);
kfree(go);
return -ENOMEM;
diff --git a/drivers/media/usb/go7007/go7007-v4l2.c b/drivers/media/usb/go7007/go7007-v4l2.c
index 2087ffcb85a5..86b853e09ecb 100644
--- a/drivers/media/usb/go7007/go7007-v4l2.c
+++ b/drivers/media/usb/go7007/go7007-v4l2.c
@@ -252,7 +252,7 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
go->encoder_h_halve = 0;
go->encoder_v_halve = 0;
go->encoder_subsample = 0;
- call_all(&go->v4l2_dev, pad, set_fmt, NULL, &format);
+ call_all(&go->v4l2_dev, pad, set_fmt, NULL, NULL, &format);
} else {
if (width <= sensor_width / 4) {
go->encoder_h_halve = 1;
diff --git a/drivers/media/usb/go7007/s2250-board.c b/drivers/media/usb/go7007/s2250-board.c
index d11f8e723624..a57ae661c02b 100644
--- a/drivers/media/usb/go7007/s2250-board.c
+++ b/drivers/media/usb/go7007/s2250-board.c
@@ -410,6 +410,7 @@ static int s2250_s_ctrl(struct v4l2_ctrl *ctrl)
}
static int s2250_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c
index 594d73e50b9f..4185c9047241 100644
--- a/drivers/media/usb/gspca/gspca.c
+++ b/drivers/media/usb/gspca/gspca.c
@@ -1636,9 +1636,8 @@ void gspca_disconnect(struct usb_interface *intf)
#endif
v4l2_device_disconnect(&gspca_dev->v4l2_dev);
- video_unregister_device(&gspca_dev->vdev);
-
mutex_unlock(&gspca_dev->usb_lock);
+ vb2_video_unregister_device(&gspca_dev->vdev);
/* (this will call gspca_release() immediately or on last close) */
v4l2_device_put(&gspca_dev->v4l2_dev);
diff --git a/drivers/media/usb/gspca/ov519.c b/drivers/media/usb/gspca/ov519.c
index bffa94e76da5..57219a738c73 100644
--- a/drivers/media/usb/gspca/ov519.c
+++ b/drivers/media/usb/gspca/ov519.c
@@ -13,7 +13,7 @@
* Copyright (c) 1999-2006 Mark W. McClelland
* Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
* Many improvements by Bret Wallach <bwallac1@san.rr.com>
- * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
+ * Color fixes by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
* OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
* Changes by Claudio Matsuoka <claudio@conectiva.com>
*
diff --git a/drivers/media/usb/gspca/w996Xcf.c b/drivers/media/usb/gspca/w996Xcf.c
index 79baa0c1a031..700c39fe307c 100644
--- a/drivers/media/usb/gspca/w996Xcf.c
+++ b/drivers/media/usb/gspca/w996Xcf.c
@@ -407,7 +407,7 @@ static void w9968cf_set_crop_window(struct sd *sd)
if (sd->sensor == SEN_OV7620) {
/*
- * Sigh, this is dependend on the clock / framerate changes
+ * Sigh, this is dependent on the clock / framerate changes
* made by the frequency control, sick.
*
* Note we cannot use v4l2_ctrl_g_ctrl here, as we get called
diff --git a/drivers/media/usb/hackrf/hackrf.c b/drivers/media/usb/hackrf/hackrf.c
index a15829a60e88..70fd95f3e97d 100644
--- a/drivers/media/usb/hackrf/hackrf.c
+++ b/drivers/media/usb/hackrf/hackrf.c
@@ -665,7 +665,7 @@ static int hackrf_free_urbs(struct hackrf_dev *dev)
static int hackrf_alloc_urbs(struct hackrf_dev *dev, bool rcv)
{
- int i, j;
+ int i;
unsigned int pipe;
usb_complete_t complete;
@@ -681,11 +681,8 @@ static int hackrf_alloc_urbs(struct hackrf_dev *dev, bool rcv)
for (i = 0; i < MAX_BULK_BUFS; i++) {
dev_dbg(dev->dev, "alloc urb=%d\n", i);
dev->urb_list[i] = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->urb_list[i]) {
- for (j = 0; j < i; j++)
- usb_free_urb(dev->urb_list[j]);
+ if (!dev->urb_list[i])
return -ENOMEM;
- }
usb_fill_bulk_urb(dev->urb_list[i],
dev->udev,
pipe,
diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
index 3c270ef00752..c749ef7e490a 100644
--- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c
@@ -2924,7 +2924,7 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw)
format.format.code = MEDIA_BUS_FMT_FIXED;
pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
format.format.width, format.format.height);
- v4l2_device_call_all(&hdw->v4l2_dev, 0, pad, set_fmt,
+ v4l2_device_call_all(&hdw->v4l2_dev, 0, pad, set_fmt, NULL,
NULL, &format);
}
@@ -3669,7 +3669,9 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Invalid write control endpoint");
- return -EINVAL;
+ hdw->ctl_write_pend_flag = 0;
+ status = -EINVAL;
+ goto done;
}
status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
if (status < 0) {
@@ -3699,7 +3701,13 @@ status);
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Invalid read control endpoint");
- return -EINVAL;
+ hdw->ctl_read_pend_flag = 0;
+ status = -EINVAL;
+ if (hdw->ctl_write_pend_flag) {
+ usb_unlink_urb(hdw->ctl_write_urb);
+ wait_for_completion(&hdw->ctl_done);
+ }
+ goto done;
}
status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
if (status < 0) {
diff --git a/drivers/media/usb/usbtv/usbtv-core.c b/drivers/media/usb/usbtv/usbtv-core.c
index 4f10f6613bc4..89c3424e7685 100644
--- a/drivers/media/usb/usbtv/usbtv-core.c
+++ b/drivers/media/usb/usbtv/usbtv-core.c
@@ -139,8 +139,6 @@ static void usbtv_disconnect(struct usb_interface *intf)
usbtv_audio_free(usbtv);
usbtv_video_free(usbtv);
- usbtv->udev = NULL;
-
/* the usbtv structure will be deallocated when v4l2 will be
done using it */
v4l2_device_put(&usbtv->v4l2_dev);
diff --git a/drivers/media/usb/usbtv/usbtv-video.c b/drivers/media/usb/usbtv/usbtv-video.c
index 92bc7a2509c3..2a91af8d1e30 100644
--- a/drivers/media/usb/usbtv/usbtv-video.c
+++ b/drivers/media/usb/usbtv/usbtv-video.c
@@ -966,5 +966,9 @@ void usbtv_video_free(struct usbtv *usbtv)
vb2_video_unregister_device(&usbtv->vdev);
v4l2_device_disconnect(&usbtv->v4l2_dev);
+ mutex_lock(&usbtv->v4l2_lock);
+ usbtv->udev = NULL;
+ mutex_unlock(&usbtv->v4l2_lock);
+
v4l2_device_put(&usbtv->v4l2_dev);
}
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 65db7340ad38..c825820fd481 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -333,6 +333,7 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_YVU420, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
{ .format = V4L2_PIX_FMT_YUV422P, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 3, .bpp = { 1, 1, 1, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_GREY, .pixel_enc = V4L2_PIXEL_ENC_YUV, .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_Y12, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
/* Tiled YUV formats */
{ .format = V4L2_PIX_FMT_NV12_4L4, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 2, .bpp = { 1, 2, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 2 },
@@ -537,16 +538,8 @@ int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt,
}
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,
- u32 width, u32 height)
+int v4l2_fill_pixfmt_aligned(struct v4l2_pix_format *pixfmt, u32 pixelformat,
+ u32 width, u32 height, u8 stride_alignment)
{
const struct v4l2_format_info *info;
int i;
@@ -562,15 +555,17 @@ 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, 1);
+ pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width,
+ stride_alignment);
pixfmt->sizeimage = 0;
for (i = 0; i < info->comp_planes; i++)
pixfmt->sizeimage +=
- v4l2_format_plane_size(info, i, width, height, 1);
+ v4l2_format_plane_size(info, i, width, height,
+ stride_alignment);
return 0;
}
-EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt);
+EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_aligned);
#ifdef CONFIG_MEDIA_CONTROLLER
static s64 v4l2_get_link_freq_ctrl(struct v4l2_ctrl_handler *handler,
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 648b88c868bc..6e751a043a21 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -115,6 +115,7 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
struct v4l2_ctrl_fwht_params *p_fwht_params;
struct v4l2_ctrl_h264_scaling_matrix *p_h264_scaling_matrix;
struct v4l2_ctrl_av1_sequence *p_av1_sequence;
+ struct v4l2_ctrl_hevc_sps *p_hevc_sps;
void *p = ptr.p + idx * ctrl->elem_size;
if (ctrl->p_def.p_const)
@@ -188,6 +189,12 @@ static void std_init_compound(const struct v4l2_ctrl *ctrl, u32 idx,
*/
memset(p_h264_scaling_matrix, 16, sizeof(*p_h264_scaling_matrix));
break;
+ case V4L2_CTRL_TYPE_HEVC_SPS:
+ p_hevc_sps = p;
+
+ /* 4:2:0 */
+ p_hevc_sps->chroma_format_idc = 1;
+ break;
}
}
@@ -2554,7 +2561,10 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
int i;
/* The first control is the master control and it must not be NULL */
- if (WARN_ON(ncontrols == 0 || controls[0] == NULL))
+ if (WARN_ON(ncontrols == 0))
+ return;
+
+ if (!controls[0])
return;
for (i = 0; i < ncontrols; i++) {
@@ -2576,8 +2586,13 @@ void v4l2_ctrl_auto_cluster(unsigned ncontrols, struct v4l2_ctrl **controls,
u32 flag = 0;
int i;
+ if (WARN_ON(ncontrols <= 1))
+ return;
+
+ if (!master)
+ return;
+
v4l2_ctrl_cluster(ncontrols, controls);
- WARN_ON(ncontrols <= 1);
WARN_ON(manual_val < master->minimum || manual_val > master->maximum);
WARN_ON(set_volatile && !has_op(master, g_volatile_ctrl));
master->is_auto = true;
diff --git a/drivers/media/v4l2-core/v4l2-mc.c b/drivers/media/v4l2-core/v4l2-mc.c
index 937d358697e1..5d7fcd67dc42 100644
--- a/drivers/media/v4l2-core/v4l2-mc.c
+++ b/drivers/media/v4l2-core/v4l2-mc.c
@@ -324,12 +324,10 @@ EXPORT_SYMBOL_GPL(v4l_vb2q_enable_media_source);
int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
struct media_pad *sink, u32 flags)
{
- struct fwnode_handle *endpoint;
-
if (!(sink->flags & MEDIA_PAD_FL_SINK))
return -EINVAL;
- fwnode_graph_for_each_endpoint(src_sd->fwnode, endpoint) {
+ fwnode_graph_for_each_endpoint_scoped(src_sd->fwnode, endpoint) {
struct fwnode_handle *remote_ep;
int src_idx, sink_idx, ret;
struct media_pad *src;
@@ -397,7 +395,6 @@ int v4l2_create_fwnode_links_to_pad(struct v4l2_subdev *src_sd,
src_sd->entity.name, src_idx,
sink->entity->name, sink_idx, ret);
- fwnode_handle_put(endpoint);
return ret;
}
}
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index e9f81b9be9e2..a07d77e584c3 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -244,20 +244,44 @@ static inline int check_format(struct v4l2_subdev *sd,
check_state(sd, state, format->which, format->pad, format->stream);
}
+#define do_subdev_call(sd, check, o, f, args...) \
+ (!(sd)->ops->o->f ? -ENOIOCTLCMD : (check) ? : \
+ (sd)->ops->o->f(sd, ##args))
+
static int call_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- return check_format(sd, state, format) ? :
- sd->ops->pad->get_fmt(sd, state, format);
+ return do_subdev_call(sd, check_format(sd, state, format), pad, get_fmt,
+ state, format);
}
static int call_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
- return check_format(sd, state, format) ? :
- sd->ops->pad->set_fmt(sd, state, format);
+ int ret;
+
+ if (!sd->ops->pad->set_fmt && !sd->ops->pad->get_fmt)
+ return -ENOIOCTLCMD;
+
+ ret = check_format(sd, state, format);
+ if (ret)
+ return ret;
+
+ if (sd->ops->pad->set_fmt)
+ return sd->ops->pad->set_fmt(sd, ci, state, format);
+
+ return sd->ops->pad->get_fmt(sd, state, format);
+}
+
+static int check_which_pad_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 which,
+ u32 pad, u32 stream)
+{
+ return check_which(which) ? : check_pad(sd, pad) ? :
+ check_state(sd, state, which, pad, stream);
}
static int call_enum_mbus_code(struct v4l2_subdev *sd,
@@ -267,9 +291,9 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd,
if (!code)
return -EINVAL;
- return check_which(code->which) ? : check_pad(sd, code->pad) ? :
- check_state(sd, state, code->which, code->pad, code->stream) ? :
- sd->ops->pad->enum_mbus_code(sd, state, code);
+ return do_subdev_call(sd, check_which_pad_state(sd, state, code->which,
+ code->pad, code->stream),
+ pad, enum_mbus_code, state, code);
}
static int call_enum_frame_size(struct v4l2_subdev *sd,
@@ -279,9 +303,9 @@ static int call_enum_frame_size(struct v4l2_subdev *sd,
if (!fse)
return -EINVAL;
- return check_which(fse->which) ? : check_pad(sd, fse->pad) ? :
- check_state(sd, state, fse->which, fse->pad, fse->stream) ? :
- sd->ops->pad->enum_frame_size(sd, state, fse);
+ return do_subdev_call(sd, check_which_pad_state(sd, state, fse->which,
+ fse->pad, fse->stream),
+ pad, enum_frame_size, state, fse);
}
static int call_enum_frame_interval(struct v4l2_subdev *sd,
@@ -291,9 +315,9 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd,
if (!fie)
return -EINVAL;
- return check_which(fie->which) ? : check_pad(sd, fie->pad) ? :
- check_state(sd, state, fie->which, fie->pad, fie->stream) ? :
- sd->ops->pad->enum_frame_interval(sd, state, fie);
+ return do_subdev_call(sd, check_which_pad_state(sd, state, fie->which,
+ fie->pad, fie->stream),
+ pad, enum_frame_interval, state, fie);
}
static inline int check_selection(struct v4l2_subdev *sd,
@@ -308,19 +332,21 @@ static inline int check_selection(struct v4l2_subdev *sd,
}
static int call_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
- return check_selection(sd, state, sel) ? :
- sd->ops->pad->get_selection(sd, state, sel);
+ return do_subdev_call(sd, check_selection(sd, state, sel),
+ pad, get_selection, ci, state, sel);
}
static int call_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
- return check_selection(sd, state, sel) ? :
- sd->ops->pad->set_selection(sd, state, sel);
+ return do_subdev_call(sd, check_selection(sd, state, sel),
+ pad, set_selection, ci, state, sel);
}
static inline int check_frame_interval(struct v4l2_subdev *sd,
@@ -338,16 +364,16 @@ static int call_get_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_interval *fi)
{
- return check_frame_interval(sd, state, fi) ? :
- sd->ops->pad->get_frame_interval(sd, state, fi);
+ return do_subdev_call(sd, check_frame_interval(sd, state, fi),
+ pad, get_frame_interval, state, fi);
}
static int call_set_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_interval *fi)
{
- return check_frame_interval(sd, state, fi) ? :
- sd->ops->pad->set_frame_interval(sd, state, fi);
+ return do_subdev_call(sd, check_frame_interval(sd, state, fi),
+ pad, set_frame_interval, state, fi);
}
static int call_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
@@ -361,6 +387,9 @@ static int call_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
return -EOPNOTSUPP;
#endif
+ if (!sd->ops->pad->get_frame_desc)
+ return -ENOIOCTLCMD;
+
memset(fd, 0, sizeof(*fd));
ret = sd->ops->pad->get_frame_desc(sd, pad, fd);
@@ -405,12 +434,12 @@ static inline int check_edid(struct v4l2_subdev *sd,
static int call_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
{
- return check_edid(sd, edid) ? : sd->ops->pad->get_edid(sd, edid);
+ return do_subdev_call(sd, check_edid(sd, edid), pad, get_edid, edid);
}
static int call_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
{
- return check_edid(sd, edid) ? : sd->ops->pad->set_edid(sd, edid);
+ return do_subdev_call(sd, check_edid(sd, edid), pad, set_edid, edid);
}
static int call_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
@@ -419,8 +448,8 @@ static int call_s_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
if (!timings)
return -EINVAL;
- return check_pad(sd, pad) ? :
- sd->ops->pad->s_dv_timings(sd, pad, timings);
+ return do_subdev_call(sd, check_pad(sd, pad),
+ pad, s_dv_timings, pad, timings);
}
static int call_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
@@ -429,8 +458,8 @@ static int call_g_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
if (!timings)
return -EINVAL;
- return check_pad(sd, pad) ? :
- sd->ops->pad->g_dv_timings(sd, pad, timings);
+ return do_subdev_call(sd, check_pad(sd, pad),
+ pad, g_dv_timings, pad, timings);
}
static int call_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
@@ -439,8 +468,8 @@ static int call_query_dv_timings(struct v4l2_subdev *sd, unsigned int pad,
if (!timings)
return -EINVAL;
- return check_pad(sd, pad) ? :
- sd->ops->pad->query_dv_timings(sd, pad, timings);
+ return do_subdev_call(sd, check_pad(sd, pad),
+ pad, query_dv_timings, pad, timings);
}
static int call_dv_timings_cap(struct v4l2_subdev *sd,
@@ -449,8 +478,8 @@ static int call_dv_timings_cap(struct v4l2_subdev *sd,
if (!cap)
return -EINVAL;
- return check_pad(sd, cap->pad) ? :
- sd->ops->pad->dv_timings_cap(sd, cap);
+ return do_subdev_call(sd, check_pad(sd, cap->pad),
+ pad, dv_timings_cap, cap);
}
static int call_enum_dv_timings(struct v4l2_subdev *sd,
@@ -459,8 +488,8 @@ static int call_enum_dv_timings(struct v4l2_subdev *sd,
if (!dvt)
return -EINVAL;
- return check_pad(sd, dvt->pad) ? :
- sd->ops->pad->enum_dv_timings(sd, dvt);
+ return do_subdev_call(sd, check_pad(sd, dvt->pad),
+ pad, enum_dv_timings, dvt);
}
static int call_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
@@ -468,14 +497,17 @@ static int call_get_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
{
memset(config, 0, sizeof(*config));
- return check_pad(sd, pad) ? :
- sd->ops->pad->get_mbus_config(sd, pad, config);
+ return do_subdev_call(sd, check_pad(sd, pad), pad, get_mbus_config,
+ pad, config);
}
static int call_s_stream(struct v4l2_subdev *sd, int enable)
{
int ret;
+ if (!sd->ops->video->s_stream)
+ return -ENOIOCTLCMD;
+
/*
* The .s_stream() operation must never be called to start or stop an
* already started or stopped subdev. Catch offenders but don't return
@@ -509,7 +541,7 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
* wrapper handles the case where the caller does not provide the called
* subdev's state. This should be removed when all the callers are fixed.
*/
-#define DEFINE_STATE_WRAPPER(f, arg_type) \
+#define DEFINE_STATE_WRAPPER(f, arg_type) \
static int call_##f##_state(struct v4l2_subdev *sd, \
struct v4l2_subdev_state *_state, \
arg_type *arg) \
@@ -523,10 +555,25 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
v4l2_subdev_unlock_state(state); \
return ret; \
}
+#define DEFINE_CI_STATE_WRAPPER(f, arg_type) \
+ static int call_##f##_state(struct v4l2_subdev *sd, \
+ const struct v4l2_subdev_client_info *ci, \
+ struct v4l2_subdev_state *_state, \
+ arg_type *arg) \
+ { \
+ struct v4l2_subdev_state *state = _state; \
+ int ret; \
+ if (!_state) \
+ state = v4l2_subdev_lock_and_get_active_state(sd); \
+ ret = call_##f(sd, ci, state, arg); \
+ if (!_state && state) \
+ v4l2_subdev_unlock_state(state); \
+ return ret; \
+ }
#else /* CONFIG_MEDIA_CONTROLLER */
-#define DEFINE_STATE_WRAPPER(f, arg_type) \
+#define DEFINE_STATE_WRAPPER(f, arg_type) \
static int call_##f##_state(struct v4l2_subdev *sd, \
struct v4l2_subdev_state *state, \
arg_type *arg) \
@@ -534,15 +581,24 @@ static int call_s_stream(struct v4l2_subdev *sd, int enable)
return call_##f(sd, state, arg); \
}
+#define DEFINE_CI_STATE_WRAPPER(f, arg_type) \
+ static int call_##f##_state(struct v4l2_subdev *sd, \
+ const struct v4l2_subdev_client_info *ci, \
+ struct v4l2_subdev_state *state, \
+ arg_type *arg) \
+ { \
+ return call_##f(sd, ci, state, arg); \
+ }
+
#endif /* CONFIG_MEDIA_CONTROLLER */
DEFINE_STATE_WRAPPER(get_fmt, struct v4l2_subdev_format);
-DEFINE_STATE_WRAPPER(set_fmt, struct v4l2_subdev_format);
+DEFINE_CI_STATE_WRAPPER(set_fmt, struct v4l2_subdev_format);
DEFINE_STATE_WRAPPER(enum_mbus_code, struct v4l2_subdev_mbus_code_enum);
DEFINE_STATE_WRAPPER(enum_frame_size, struct v4l2_subdev_frame_size_enum);
DEFINE_STATE_WRAPPER(enum_frame_interval, struct v4l2_subdev_frame_interval_enum);
-DEFINE_STATE_WRAPPER(get_selection, struct v4l2_subdev_selection);
-DEFINE_STATE_WRAPPER(set_selection, struct v4l2_subdev_selection);
+DEFINE_CI_STATE_WRAPPER(get_selection, struct v4l2_subdev_selection);
+DEFINE_CI_STATE_WRAPPER(set_selection, struct v4l2_subdev_selection);
static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = {
.get_fmt = call_get_fmt_state,
@@ -611,7 +667,7 @@ subdev_ioctl_get_state(struct v4l2_subdev *sd, struct v4l2_subdev_fh *subdev_fh,
case VIDIOC_SUBDEV_S_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval *fi = arg;
- if (!(subdev_fh->client_caps &
+ if (!(subdev_fh->ci.caps &
V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH))
fi->which = V4L2_SUBDEV_FORMAT_ACTIVE;
@@ -650,7 +706,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh);
bool ro_subdev = test_bit(V4L2_FL_SUBDEV_RO_DEVNODE, &vdev->flags);
bool streams_subdev = sd->flags & V4L2_SUBDEV_FL_STREAMS;
- bool client_supports_streams = subdev_fh->client_caps &
+ bool client_supports_streams = subdev_fh->ci.caps &
V4L2_SUBDEV_CLIENT_CAP_STREAMS;
int rval;
@@ -817,7 +873,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
memset(format->reserved, 0, sizeof(format->reserved));
memset(format->format.reserved, 0, sizeof(format->format.reserved));
- return v4l2_subdev_call(sd, pad, set_fmt, state, format);
+ return v4l2_subdev_call(sd, pad, set_fmt, &subdev_fh->ci, state,
+ format);
}
case VIDIOC_SUBDEV_G_CROP: {
@@ -834,8 +891,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
sel.stream = crop->stream;
sel.target = V4L2_SEL_TGT_CROP;
- rval = v4l2_subdev_call(
- sd, pad, get_selection, state, &sel);
+ rval = v4l2_subdev_call(sd, pad, get_selection, &subdev_fh->ci,
+ state, &sel);
crop->rect = sel.r;
@@ -860,8 +917,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
sel.target = V4L2_SEL_TGT_CROP;
sel.r = crop->rect;
- rval = v4l2_subdev_call(
- sd, pad, set_selection, state, &sel);
+ rval = v4l2_subdev_call(sd, pad, set_selection, &subdev_fh->ci,
+ state, &sel);
crop->rect = sel.r;
@@ -931,8 +988,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
sel->stream = 0;
memset(sel->reserved, 0, sizeof(sel->reserved));
- return v4l2_subdev_call(
- sd, pad, get_selection, state, sel);
+ return v4l2_subdev_call(sd, pad, get_selection, &subdev_fh->ci,
+ state, sel);
}
case VIDIOC_SUBDEV_S_SELECTION: {
@@ -945,8 +1002,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
sel->stream = 0;
memset(sel->reserved, 0, sizeof(sel->reserved));
- return v4l2_subdev_call(
- sd, pad, set_selection, state, sel);
+ return v4l2_subdev_call(sd, pad, set_selection, &subdev_fh->ci,
+ state, sel);
}
case VIDIOC_G_EDID: {
@@ -1117,7 +1174,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
case VIDIOC_SUBDEV_G_CLIENT_CAP: {
struct v4l2_subdev_client_capability *client_cap = arg;
- client_cap->capabilities = subdev_fh->client_caps;
+ client_cap->capabilities = subdev_fh->ci.caps;
return 0;
}
@@ -1137,7 +1194,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg,
client_cap->capabilities &= (V4L2_SUBDEV_CLIENT_CAP_STREAMS |
V4L2_SUBDEV_CLIENT_CAP_INTERVAL_USES_WHICH);
- subdev_fh->client_caps = client_cap->capabilities;
+ subdev_fh->ci.caps = client_cap->capabilities;
return 0;
}
diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c
index 6c729fcfce5d..b48cf1b7fd5f 100644
--- a/drivers/platform/x86/intel/int3472/discrete.c
+++ b/drivers/platform/x86/intel/int3472/discrete.c
@@ -15,6 +15,7 @@
#include <linux/platform_data/x86/int3472.h>
#include <linux/platform_device.h>
#include <linux/string_choices.h>
+#include <linux/time64.h>
#include <linux/uuid.h>
/*
@@ -143,6 +144,11 @@ static const char * const power_enable_hids_enable[] = {
NULL
};
+static const char * const power_enable_hids_vdda[] = {
+ "INT347E", /* ov7251 */
+ NULL
+};
+
/**
* struct int3472_gpio_map - Map GPIOs to whatever is expected by the
* sensor driver (as in DT bindings)
@@ -178,12 +184,12 @@ static const struct int3472_gpio_map int3472_gpio_map[] = {
.type_to = INT3472_GPIO_TYPE_RESET,
.con_id = "enable",
},
- { /* ov08x40's handshake pin needs a 45 ms delay on some HP laptops */
- .hids = (const char * const[]) { "OVTI08F4", NULL },
- .type_from = INT3472_GPIO_TYPE_HANDSHAKE,
- .type_to = INT3472_GPIO_TYPE_HANDSHAKE,
- .con_id = "dvdd",
- .enable_time_us = 45 * USEC_PER_MSEC,
+ { /* Sensors which expect "vdda" as con_id for power enable */
+ .hids = power_enable_hids_vdda,
+ .type_from = INT3472_GPIO_TYPE_POWER_ENABLE,
+ .type_to = INT3472_GPIO_TYPE_POWER_ENABLE,
+ .con_id = "vdda",
+ .enable_time_us = GPIO_REGULATOR_ENABLE_TIME,
},
{ /* Sensors which expect "vana" as con_id for power enable */
.hids = power_enable_hids_vana,
@@ -266,6 +272,10 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3
*con_id = "avdd";
*gpio_flags = GPIO_ACTIVE_HIGH;
break;
+ case INT3472_GPIO_TYPE_POWER1:
+ *con_id = "dvdd";
+ *gpio_flags = GPIO_ACTIVE_HIGH;
+ break;
case INT3472_GPIO_TYPE_DOVDD:
*con_id = "dovdd";
*gpio_flags = GPIO_ACTIVE_HIGH;
@@ -273,8 +283,8 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3
case INT3472_GPIO_TYPE_HANDSHAKE:
*con_id = "dvdd";
*gpio_flags = GPIO_ACTIVE_HIGH;
- /* Setups using a handshake pin need 25 ms enable delay */
- *enable_time_us = 25 * USEC_PER_MSEC;
+ /* Powering up the sensor through the handshake pin takes up to 200 ms */
+ *enable_time_us = 200 * USEC_PER_MSEC;
break;
default:
*con_id = "unknown";
@@ -296,6 +306,8 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3
* 0x00 Reset
* 0x01 Power down
* 0x02 Strobe
+ * 0x07 Power 0
+ * 0x08 Power 1
* 0x0b Power enable
* 0x0c Clock enable
* 0x0d Privacy LED
@@ -328,8 +340,8 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
u8 active_value, pin, type;
unsigned long gpio_flags;
union acpi_object *obj;
+ unsigned int obj_value;
struct gpio_desc *gpio;
- const char *err_msg;
const char *con_id;
int ret;
@@ -344,24 +356,27 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
&int3472_gpio_guid, 0x00,
int3472->ngpios + 2,
NULL, ACPI_TYPE_INTEGER);
-
if (!obj) {
dev_warn(int3472->dev, "No _DSM entry for GPIO pin %u\n",
agpio->pin_table[0]);
return 1;
}
- type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value);
+ obj_value = obj->integer.value;
+
+ ACPI_FREE(obj);
+
+ type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj_value);
int3472_get_con_id_and_polarity(int3472, &type, &con_id, &gpio_flags, &enable_time_us);
- pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value);
+ pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj_value);
/* Pin field is not really used under Windows and wraps around at 8 bits */
if (pin != (agpio->pin_table[0] & 0xff))
dev_dbg(int3472->dev, FW_BUG "%s %s pin number mismatch _DSM %d resource %d\n",
con_id, agpio->resource_source.string_ptr, pin, agpio->pin_table[0]);
- active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj->integer.value);
+ active_value = FIELD_GET(INT3472_GPIO_DSM_SENSOR_ON_VAL, obj_value);
if (!active_value)
gpio_flags ^= GPIO_ACTIVE_LOW;
@@ -369,51 +384,57 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
agpio->resource_source.string_ptr, agpio->pin_table[0],
str_high_low(gpio_flags == GPIO_ACTIVE_HIGH));
+ int3472->ngpios++;
+
switch (type) {
case INT3472_GPIO_TYPE_RESET:
case INT3472_GPIO_TYPE_POWERDOWN:
case INT3472_GPIO_TYPE_HOTPLUG_DETECT:
ret = skl_int3472_map_gpio_to_sensor(int3472, agpio, con_id, gpio_flags);
if (ret)
- err_msg = "Failed to map GPIO pin to sensor\n";
+ return dev_err_probe(int3472->dev, ret,
+ "Failed to map GPIO pin to sensor\n");
- break;
+ return 1;
case INT3472_GPIO_TYPE_CLK_ENABLE:
case INT3472_GPIO_TYPE_PRIVACY_LED:
case INT3472_GPIO_TYPE_STROBE:
case INT3472_GPIO_TYPE_POWER_ENABLE:
+ case INT3472_GPIO_TYPE_POWER1:
case INT3472_GPIO_TYPE_DOVDD:
case INT3472_GPIO_TYPE_HANDSHAKE:
gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags);
- if (IS_ERR(gpio)) {
- ret = PTR_ERR(gpio);
- err_msg = "Failed to get GPIO\n";
- break;
- }
+ if (IS_ERR(gpio))
+ return dev_err_probe(int3472->dev, PTR_ERR(gpio),
+ "Failed to get GPIO\n");
switch (type) {
case INT3472_GPIO_TYPE_CLK_ENABLE:
ret = skl_int3472_register_gpio_clock(int3472, gpio);
if (ret)
- err_msg = "Failed to register clock\n";
+ dev_err_probe(int3472->dev, ret,
+ "Failed to register clock\n");
break;
case INT3472_GPIO_TYPE_PRIVACY_LED:
case INT3472_GPIO_TYPE_STROBE:
ret = skl_int3472_register_led(int3472, gpio, con_id);
if (ret)
- err_msg = "Failed to register LED\n";
+ dev_err_probe(int3472->dev, ret,
+ "Failed to register LED\n");
break;
case INT3472_GPIO_TYPE_POWER_ENABLE:
second_sensor = int3472->quirks.avdd_second_sensor;
fallthrough;
+ case INT3472_GPIO_TYPE_POWER1:
case INT3472_GPIO_TYPE_DOVDD:
case INT3472_GPIO_TYPE_HANDSHAKE:
ret = skl_int3472_register_regulator(int3472, gpio, enable_time_us,
con_id, second_sensor);
if (ret)
- err_msg = "Failed to register regulator\n";
+ dev_err_probe(int3472->dev, ret,
+ "Failed to register regulator\n");
break;
default: /* Never reached */
@@ -424,23 +445,13 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares,
if (ret)
gpiod_put(gpio);
- break;
+ return ret < 0 ? ret : 1;
default:
dev_warn(int3472->dev,
"GPIO type 0x%02x unknown; the sensor may not work\n",
type);
- ret = 1;
- break;
+ return 1;
}
-
- int3472->ngpios++;
- ACPI_FREE(obj);
-
- if (ret < 0)
- return dev_err_probe(int3472->dev, ret, err_msg);
-
- /* Tell acpi_dev_get_resources() to not make a copy of the resource */
- return 1;
}
int int3472_discrete_parse_crs(struct int3472_discrete_device *int3472)
diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c
index a77ed32abe55..dc777dbac61f 100644
--- a/drivers/platform/x86/intel/int3472/tps68470.c
+++ b/drivers/platform/x86/intel/int3472/tps68470.c
@@ -161,7 +161,7 @@ static int skl_int3472_tps68470_probe(struct i2c_client *client)
regmap = devm_regmap_init_i2c(client, &tps68470_regmap_config);
if (IS_ERR(regmap)) {
- dev_err(&client->dev, "Failed to create regmap: %ld\n", PTR_ERR(regmap));
+ dev_err(&client->dev, "Failed to create regmap: %pe\n", regmap);
return PTR_ERR(regmap);
}
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
index f9cc5f45fe00..38f69582c174 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-gc2235.c
@@ -520,6 +520,7 @@ static int gc2235_startup(struct v4l2_subdev *sd)
}
static int gc2235_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
index 2c41c496daa6..e3137fbc5802 100644
--- a/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
+++ b/drivers/staging/media/atomisp/i2c/atomisp-ov2722.c
@@ -623,6 +623,7 @@ static int ov2722_startup(struct v4l2_subdev *sd)
}
static int ov2722_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/staging/media/atomisp/pci/atomisp_cmd.c b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
index 6cd500d9fd26..52c71cd86dec 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_cmd.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_cmd.c
@@ -3745,14 +3745,16 @@ static int atomisp_set_sensor_crop_and_fmt(struct atomisp_device *isp,
sel.r.left = ((input->native_rect.width - sel.r.width) / 2) & ~1;
sel.r.top = ((input->native_rect.height - sel.r.height) / 2) & ~1;
- ret = v4l2_subdev_call(input->sensor, pad, set_selection, sd_state, &sel);
+ ret = v4l2_subdev_call(input->sensor, pad, set_selection, NULL,
+ sd_state, &sel);
if (ret)
dev_err(isp->dev, "Error setting crop to (%d,%d)/%ux%u: %d\n",
sel.r.left, sel.r.top, sel.r.width, sel.r.height, ret);
set_fmt:
if (ret == 0) {
- ret = v4l2_subdev_call(input->sensor, pad, set_fmt, sd_state, &format);
+ ret = v4l2_subdev_call(input->sensor, pad, set_fmt, NULL,
+ sd_state, &format);
dev_dbg(isp->dev, "Set sensor format ret: %d size %dx%d\n",
ret, format.format.width, format.format.height);
}
@@ -3765,13 +3767,16 @@ set_fmt:
sd_state = v4l2_subdev_lock_and_get_active_state(input->sensor_isp);
format.pad = SENSOR_ISP_PAD_SINK;
- ret = v4l2_subdev_call(input->sensor_isp, pad, set_fmt, sd_state, &format);
+ ret = v4l2_subdev_call(input->sensor_isp, pad, set_fmt, NULL,
+ sd_state, &format);
dev_dbg(isp->dev, "Set sensor ISP sink format ret: %d size %dx%d\n",
ret, format.format.width, format.format.height);
if (ret == 0) {
format.pad = SENSOR_ISP_PAD_SOURCE;
- ret = v4l2_subdev_call(input->sensor_isp, pad, set_fmt, sd_state, &format);
+ ret = v4l2_subdev_call(input->sensor_isp, pad,
+ set_fmt, NULL, sd_state,
+ &format);
dev_dbg(isp->dev, "Set sensor ISP source format ret: %d size %dx%d\n",
ret, format.format.width, format.format.height);
}
@@ -3783,7 +3788,8 @@ set_fmt:
/* Propagate new fmt to CSI port */
if (ret == 0 && which == V4L2_SUBDEV_FORMAT_ACTIVE) {
format.pad = CSI2_PAD_SINK;
- ret = v4l2_subdev_call(input->csi_port, pad, set_fmt, NULL, &format);
+ ret = v4l2_subdev_call(input->csi_port, pad, set_fmt, NULL,
+ NULL, &format);
if (ret)
return ret;
}
diff --git a/drivers/staging/media/atomisp/pci/atomisp_csi2.c b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
index 95b9113d75e9..71df4ef629c2 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_csi2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_csi2.c
@@ -123,6 +123,7 @@ int atomisp_csi2_set_ffmt(struct v4l2_subdev *sd,
* return -EINVAL or zero on success
*/
static int csi2_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/staging/media/atomisp/pci/atomisp_subdev.c b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
index 9de9cd884d99..bdd2d9b18da2 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_subdev.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_subdev.c
@@ -259,6 +259,7 @@ static void isp_get_fmt_rect(struct v4l2_subdev *sd,
}
static int isp_subdev_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -452,6 +453,7 @@ get_rect:
}
static int isp_subdev_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -555,6 +557,7 @@ static int isp_subdev_get_format(struct v4l2_subdev *sd,
* to the format type.
*/
static int isp_subdev_set_format(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
index 812230397409..476fe143a591 100644
--- a/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
+++ b/drivers/staging/media/atomisp/pci/atomisp_v4l2.c
@@ -913,7 +913,7 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input)
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.target = V4L2_SEL_TGT_NATIVE_SIZE;
- err = v4l2_subdev_call(input->sensor, pad, get_selection,
+ err = v4l2_subdev_call(input->sensor, pad, get_selection, NULL,
act_sd_state, &sel);
if (err)
goto unlock_act_sd_state;
@@ -922,7 +922,7 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input)
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.target = V4L2_SEL_TGT_CROP_DEFAULT;
- err = v4l2_subdev_call(input->sensor, pad, get_selection,
+ err = v4l2_subdev_call(input->sensor, pad, get_selection, NULL,
act_sd_state, &sel);
if (err)
goto unlock_act_sd_state;
@@ -968,7 +968,7 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input)
if (!input->sensor->state_lock)
v4l2_subdev_lock_state(input->try_sd_state);
- err = v4l2_subdev_call(input->sensor, pad, set_selection,
+ err = v4l2_subdev_call(input->sensor, pad, set_selection, NULL,
input->try_sd_state, &sel);
if (!input->sensor->state_lock)
@@ -980,7 +980,7 @@ static void atomisp_init_sensor(struct atomisp_input_subdev *input)
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.target = V4L2_SEL_TGT_CROP;
sel.r = input->native_rect;
- err = v4l2_subdev_call(input->sensor, pad, set_selection,
+ err = v4l2_subdev_call(input->sensor, pad, set_selection, NULL,
act_sd_state, &sel);
if (err)
goto unlock_act_sd_state;
diff --git a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h
index b8206d2f3d31..97e63082d254 100644
--- a/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h
+++ b/drivers/staging/media/atomisp/pci/isp/kernels/s3a/s3a_1.0/ia_css_s3a_types.h
@@ -102,8 +102,8 @@ struct ia_css_3a_grid_info {
* awb_lg_*: Thresholds to check the saturated bayer pixels for AWB.
* Condition of effective pixel for AWB level gate check:
* bayer(sensor) <= awb_lg_high_raw &&
- * bayer(when AWB statisitcs is calculated) >= awb_lg_low &&
- * bayer(when AWB statisitcs is calculated) <= awb_lg_high
+ * bayer(when AWB statistics is calculated) >= awb_lg_low &&
+ * bayer(when AWB statistics is calculated) <= awb_lg_high
* af_fir*: Coefficients of high pass filter to calculate AF statistics.
*
* ISP block: S3A1(ae_y_* for AE/AF, awb_lg_* for AWB)
diff --git a/drivers/staging/media/av7110/av7110.c b/drivers/staging/media/av7110/av7110.c
index 06c43b5f8a87..432404eceaea 100644
--- a/drivers/staging/media/av7110/av7110.c
+++ b/drivers/staging/media/av7110/av7110.c
@@ -2347,7 +2347,7 @@ static int av7110_attach(struct saa7146_dev *dev,
/* RESET SAA7146 */
saa7146_write(dev, MC1, MASK_31);
- /* autodetection success seems to be time-dependend after reset */
+ /* autodetection success seems to be time-dependent after reset */
/* Fix VSYNC level */
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
diff --git a/drivers/staging/media/av7110/av7110_ir.c b/drivers/staging/media/av7110/av7110_ir.c
index 9b7bf1857868..d0948a80d387 100644
--- a/drivers/staging/media/av7110/av7110_ir.c
+++ b/drivers/staging/media/av7110/av7110_ir.c
@@ -154,5 +154,3 @@ void av7110_ir_exit(struct av7110 *av7110)
rc_free_device(av7110->ir.rcdev);
}
-//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>");
-//MODULE_LICENSE("GPL");
diff --git a/drivers/staging/media/av7110/sp8870.c b/drivers/staging/media/av7110/sp8870.c
index 29fb4934c039..aecdbd87976f 100644
--- a/drivers/staging/media/av7110/sp8870.c
+++ b/drivers/staging/media/av7110/sp8870.c
@@ -92,7 +92,7 @@ static int sp8870_readreg(struct sp8870_state *state, u16 reg)
return -1;
}
- return (b1[0] << 8 | b1[1]);
+ return b1[0] << 8 | b1[1];
}
static int sp8870_firmware_upload(struct sp8870_state *state, const struct firmware *fw)
@@ -167,7 +167,7 @@ static void sp8870_microcontroller_start(struct sp8870_state *state)
static int sp8870_read_data_valid_signal(struct sp8870_state *state)
{
- return (sp8870_readreg(state, 0x0D02) > 0);
+ return sp8870_readreg(state, 0x0D02) > 0;
}
static int configure_reg0xc05(struct dtv_frontend_properties *p, u16 *reg0xc05)
@@ -315,7 +315,6 @@ static int sp8870_init(struct dvb_frontend *fe)
sp8870_wake_up(state);
if (state->initialised)
return 0;
- state->initialised = 1;
dprintk("initialising frontend...\n");
@@ -353,6 +352,8 @@ static int sp8870_init(struct dvb_frontend *fe)
sp8870_writereg(state, 0x0D00, 0x010);
sp8870_writereg(state, 0x0D01, 0x000);
+ state->initialised = 1;
+
return 0;
}
@@ -401,7 +402,7 @@ static int sp8870_read_ber(struct dvb_frontend *fe, u32 *ber)
if (ret < 0)
return -EIO;
- tmp = ret << 6;
+ tmp |= ret << 6;
if (tmp >= 0x3FFF0)
tmp = ~0;
diff --git a/drivers/staging/media/imx/imx-ic-prp.c b/drivers/staging/media/imx/imx-ic-prp.c
index 2b80d54006b3..b2c648149964 100644
--- a/drivers/staging/media/imx/imx-ic-prp.c
+++ b/drivers/staging/media/imx/imx-ic-prp.c
@@ -11,7 +11,6 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/module.h>
-#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
@@ -151,6 +150,7 @@ out:
}
static int prp_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/staging/media/imx/imx-ic-prpencvf.c b/drivers/staging/media/imx/imx-ic-prpencvf.c
index 77360bfe081a..3430ddeac356 100644
--- a/drivers/staging/media/imx/imx-ic-prpencvf.c
+++ b/drivers/staging/media/imx/imx-ic-prpencvf.c
@@ -9,9 +9,8 @@
* Copyright (c) 2012-2017 Mentor Graphics Inc.
*/
#include <linux/delay.h>
-#include <linux/interrupt.h>
#include <linux/module.h>
-#include <linux/sched.h>
+#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
@@ -919,6 +918,7 @@ static void prp_try_fmt(struct prp_priv *priv,
}
static int prp_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/staging/media/imx/imx-media-capture.c b/drivers/staging/media/imx/imx-media-capture.c
index bfd71d25facc..9216a221b72f 100644
--- a/drivers/staging/media/imx/imx-media-capture.c
+++ b/drivers/staging/media/imx/imx-media-capture.c
@@ -5,7 +5,6 @@
* Copyright (c) 2012-2016 Mentor Graphics Inc.
*/
#include <linux/delay.h>
-#include <linux/fs.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
diff --git a/drivers/staging/media/imx/imx-media-csc-scaler.c b/drivers/staging/media/imx/imx-media-csc-scaler.c
index 00fcdd4d0487..6f4c0b09bb36 100644
--- a/drivers/staging/media/imx/imx-media-csc-scaler.c
+++ b/drivers/staging/media/imx/imx-media-csc-scaler.c
@@ -7,8 +7,6 @@
*/
#include <linux/module.h>
#include <linux/delay.h>
-#include <linux/fs.h>
-#include <linux/sched.h>
#include <linux/slab.h>
#include <video/imx-ipu-v3.h>
#include <video/imx-ipu-image-convert.h>
@@ -147,6 +145,7 @@ err:
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
v4l2_m2m_job_finish(priv->m2m_dev, ctx->fh.m2m_ctx);
+ kfree(run);
}
/*
diff --git a/drivers/staging/media/imx/imx-media-csi.c b/drivers/staging/media/imx/imx-media-csi.c
index ef22a083f8eb..43c637b333f2 100644
--- a/drivers/staging/media/imx/imx-media-csi.c
+++ b/drivers/staging/media/imx/imx-media-csi.c
@@ -1525,6 +1525,7 @@ static void csi_try_fmt(struct csi_priv *priv,
}
static int csi_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
@@ -1593,6 +1594,7 @@ out:
}
static int csi_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -1657,6 +1659,7 @@ static int csi_set_scale(u32 *compose, u32 crop, u32 flags)
}
static int csi_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/staging/media/imx/imx-media-dev.c b/drivers/staging/media/imx/imx-media-dev.c
index a08389b99d14..a8f39319bd8a 100644
--- a/drivers/staging/media/imx/imx-media-dev.c
+++ b/drivers/staging/media/imx/imx-media-dev.c
@@ -4,7 +4,6 @@
*
* Copyright (c) 2016-2019 Mentor Graphics Inc.
*/
-#include <linux/fs.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <media/v4l2-async.h>
diff --git a/drivers/staging/media/imx/imx-media-vdic.c b/drivers/staging/media/imx/imx-media-vdic.c
index 58f1112e28e5..8dcbb6fe7649 100644
--- a/drivers/staging/media/imx/imx-media-vdic.c
+++ b/drivers/staging/media/imx/imx-media-vdic.c
@@ -566,6 +566,7 @@ static void vdic_try_fmt(struct vdic_priv *priv,
}
static int vdic_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
index 211f67fb92b5..f6cbf0310e89 100644
--- a/drivers/staging/media/imx/imx6-mipi-csi2.c
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -525,6 +525,7 @@ static int csi2_get_fmt(struct v4l2_subdev *sd,
}
static int csi2_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *sdformat)
{
diff --git a/drivers/staging/media/ipu3/ipu3-css.c b/drivers/staging/media/ipu3/ipu3-css.c
index 8063401246fb..b2c8fb7d6813 100644
--- a/drivers/staging/media/ipu3/ipu3-css.c
+++ b/drivers/staging/media/ipu3/ipu3-css.c
@@ -290,7 +290,6 @@ fail:
void imgu_css_set_powerdown(struct device *dev, void __iomem *base)
{
- dev_dbg(dev, "%s\n", __func__);
/* wait for cio idle signal */
if (imgu_hw_wait(base, IMGU_REG_CIO_GATE_BURST_STATE,
IMGU_CIO_GATE_BURST_MASK, 0))
diff --git a/drivers/staging/media/ipu3/ipu3-v4l2.c b/drivers/staging/media/ipu3/ipu3-v4l2.c
index 2f6041d342f4..61fdefe78c3d 100644
--- a/drivers/staging/media/ipu3/ipu3-v4l2.c
+++ b/drivers/staging/media/ipu3/ipu3-v4l2.c
@@ -145,6 +145,7 @@ static int imgu_subdev_get_fmt(struct v4l2_subdev *sd,
}
static int imgu_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
@@ -212,6 +213,7 @@ imgu_subdev_get_compose(struct imgu_v4l2_subdev *sd,
}
static int imgu_subdev_get_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
@@ -236,6 +238,7 @@ static int imgu_subdev_get_selection(struct v4l2_subdev *sd,
}
static int imgu_subdev_set_selection(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/staging/media/ipu7/TODO b/drivers/staging/media/ipu7/TODO
index dc27bb6463da..57cca4e4033f 100644
--- a/drivers/staging/media/ipu7/TODO
+++ b/drivers/staging/media/ipu7/TODO
@@ -1,26 +1,2 @@
-This is a list of things that need to be done to get this driver out of the
-staging directory.
-
-- ABI headers cleanup
- Cleanup the firmware ABI headers
-
-- Add metadata capture support
- The IPU7 hardware should support metadata capture, but it is not
- fully verified with IPU7 firmware ABI so far, need to add the metadata
- capture support.
-
-- Refine CSI2 PHY code
- Refine the ipu7-isys-csi2-phy.c, move the hardware specific variant
- into structure, clarify and explain the PHY registers to make it more
- readable.
-
-- Work with the common IPU module
- Sakari commented much of the driver code is the same than the IPU6 driver.
- IPU7 driver is expected to work with the common IPU module in future.
-
-- Register definition cleanup
- Add IPU7 prefix for IPU7 specific registers and related macros. Some
- ISYS IO sub-blocks register definitions are offset values from specific
- sub-block base, but it is not clear and well suited for driver to use,
- need to update the register definitions to make it more clear and
- readable.
+This driver will be removed soon in favour of supporting IPU7 and later
+in the ipu6 driver.
diff --git a/drivers/staging/media/ipu7/ipu7-isys-csi2.c b/drivers/staging/media/ipu7/ipu7-isys-csi2.c
index f34eabfe8a98..fcc7efcf69c3 100644
--- a/drivers/staging/media/ipu7/ipu7-isys-csi2.c
+++ b/drivers/staging/media/ipu7/ipu7-isys-csi2.c
@@ -190,6 +190,7 @@ static int ipu7_isys_csi2_enable_stream(struct ipu7_isys_csi2 *csi2)
}
static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
@@ -241,6 +242,7 @@ static int ipu7_isys_csi2_set_sel(struct v4l2_subdev *sd,
}
static int ipu7_isys_csi2_get_sel(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel)
{
diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.c b/drivers/staging/media/ipu7/ipu7-isys-subdev.c
index 67a776033d5b..2cb0521b4240 100644
--- a/drivers/staging/media/ipu7/ipu7-isys-subdev.c
+++ b/drivers/staging/media/ipu7/ipu7-isys-subdev.c
@@ -99,6 +99,7 @@ u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y)
}
int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/staging/media/ipu7/ipu7-isys-subdev.h b/drivers/staging/media/ipu7/ipu7-isys-subdev.h
index faa50031cf24..c3585d1c4128 100644
--- a/drivers/staging/media/ipu7/ipu7-isys-subdev.h
+++ b/drivers/staging/media/ipu7/ipu7-isys-subdev.h
@@ -31,6 +31,7 @@ bool ipu7_isys_is_bayer_format(u32 code);
u32 ipu7_isys_convert_bayer_order(u32 code, int x, int y);
int ipu7_isys_subdev_set_fmt(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format);
int ipu7_isys_subdev_enum_mbus_code(struct v4l2_subdev *sd,
diff --git a/drivers/staging/media/ipu7/ipu7-isys.c b/drivers/staging/media/ipu7/ipu7-isys.c
index 601e5a79ef8e..a54393fbb9db 100644
--- a/drivers/staging/media/ipu7/ipu7-isys.c
+++ b/drivers/staging/media/ipu7/ipu7-isys.c
@@ -590,6 +590,7 @@ static void isys_remove(struct auxiliary_device *auxdev)
isys_notifier_cleanup(isys);
isys_unregister_devices(isys);
+ ipu7_fw_isys_release(isys);
cpu_latency_qos_remove_request(&isys->pm_qos);
diff --git a/drivers/staging/media/ipu7/ipu7.c b/drivers/staging/media/ipu7/ipu7.c
index 48a35bda4237..88e362c7ae35 100644
--- a/drivers/staging/media/ipu7/ipu7.c
+++ b/drivers/staging/media/ipu7/ipu7.c
@@ -39,6 +39,10 @@
#define IPU_PCI_BAR 0
#define IPU_PCI_PBBAR 4
+static int force_probe = !IS_BUILTIN(CONFIG_VIDEO_INTEL_IPU6_IPU7);
+module_param(force_probe, int, 0644);
+MODULE_PARM_DESC(force_probe, "Probe ipu7 and ipu7.5 with this driver instead of ipu6");
+
static const unsigned int ipu7_csi_offsets[] = {
IPU_CSI_PORT_A_ADDR_OFFSET,
IPU_CSI_PORT_B_ADDR_OFFSET,
@@ -2414,6 +2418,9 @@ static int ipu7_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
u32 is_es;
int ret;
+ if (!force_probe)
+ return -ENODEV;
+
if (!fwnode || fwnode_property_read_u32(fwnode, "is_es", &is_es))
is_es = 0;
diff --git a/drivers/staging/media/max96712/max96712.c b/drivers/staging/media/max96712/max96712.c
index 0751b2e04895..94ae304ac85f 100644
--- a/drivers/staging/media/max96712/max96712.c
+++ b/drivers/staging/media/max96712/max96712.c
@@ -264,7 +264,6 @@ static const struct v4l2_subdev_internal_ops max96712_internal_ops = {
static const struct v4l2_subdev_pad_ops max96712_pad_ops = {
.get_fmt = v4l2_subdev_get_fmt,
- .set_fmt = v4l2_subdev_get_fmt,
};
static const struct v4l2_subdev_ops max96712_subdev_ops = {
diff --git a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
index 46a334b602f1..3f376af6c228 100644
--- a/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
+++ b/drivers/staging/media/sunxi/sun6i-isp/sun6i_isp_proc.c
@@ -313,6 +313,7 @@ static int sun6i_isp_proc_get_fmt(struct v4l2_subdev *subdev,
}
static int sun6i_isp_proc_set_fmt(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
diff --git a/drivers/staging/media/tegra-video/csi.c b/drivers/staging/media/tegra-video/csi.c
index 41d57dac61a7..5a5fca378ae6 100644
--- a/drivers/staging/media/tegra-video/csi.c
+++ b/drivers/staging/media/tegra-video/csi.c
@@ -170,6 +170,7 @@ static int csi_enum_frameintervals(struct v4l2_subdev *subdev,
}
static int csi_set_format(struct v4l2_subdev *subdev,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
{
diff --git a/drivers/staging/media/tegra-video/vi.c b/drivers/staging/media/tegra-video/vi.c
index 01622013c109..6a5f66da348b 100644
--- a/drivers/staging/media/tegra-video/vi.c
+++ b/drivers/staging/media/tegra-video/vi.c
@@ -477,7 +477,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
ret = v4l2_subdev_call(subdev, pad, enum_frame_size, sd_state, &fse);
if (ret) {
if (!v4l2_subdev_has_op(subdev, pad, get_selection) ||
- v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel)) {
+ v4l2_subdev_call(subdev, pad, get_selection, NULL, NULL, &sdsel)) {
try_crop->width = 0;
try_crop->height = 0;
} else {
@@ -489,7 +489,7 @@ static int __tegra_channel_try_format(struct tegra_vi_channel *chan,
try_crop->height = fse.max_height;
}
- ret = v4l2_subdev_call(subdev, pad, set_fmt, sd_state, &fmt);
+ ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, sd_state, &fmt);
if (ret < 0)
goto out_free;
@@ -543,7 +543,7 @@ static int tegra_channel_set_format(struct file *file, void *fh,
fmt.pad = 0;
v4l2_fill_mbus_format(&fmt.format, pix, fmtinfo->code);
subdev = tegra_channel_get_remote_source_subdev(chan);
- ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, &fmt);
+ ret = v4l2_subdev_call(subdev, pad, set_fmt, NULL, NULL, &fmt);
if (ret < 0)
return ret;
@@ -626,7 +626,7 @@ static int tegra_channel_g_selection(struct file *file, void *priv,
* Try the get selection operation and fallback to get format if not
* implemented.
*/
- ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, &sdsel);
+ ret = v4l2_subdev_call(subdev, pad, get_selection, NULL, NULL, &sdsel);
if (!ret)
sel->r = sdsel.r;
if (ret != -ENOIOCTLCMD)
@@ -667,7 +667,7 @@ static int tegra_channel_s_selection(struct file *file, void *fh,
if (vb2_is_busy(&chan->queue))
return -EBUSY;
- ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, &sdsel);
+ ret = v4l2_subdev_call(subdev, pad, set_selection, NULL, NULL, &sdsel);
if (!ret) {
sel->r = sdsel.r;
/*
@@ -1247,17 +1247,54 @@ static int tegra_vi_tpg_channels_alloc(struct tegra_vi *vi)
return 0;
}
+static int tegra_vi_port_channel_alloc(struct tegra_vi *vi,
+ struct device_node *port)
+{
+ struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 };
+ struct device_node *parent;
+ struct device_node *ep;
+ unsigned int port_num;
+ unsigned int lanes;
+ int ret;
+
+ if (!of_node_name_eq(port, "port"))
+ return 0;
+
+ if (of_property_read_u32(port, "reg", &port_num) < 0)
+ return 0;
+
+ if (port_num > vi->soc->vi_max_channels) {
+ dev_err(vi->dev, "invalid port num %d for %pOF\n",
+ port_num, port);
+ return -EINVAL;
+ }
+
+ ep = of_get_child_by_name(port, "endpoint");
+ if (!ep)
+ return 0;
+
+ parent = of_graph_get_remote_port_parent(ep);
+ of_node_put(ep);
+ if (!parent)
+ return 0;
+
+ ep = of_graph_get_endpoint_by_regs(parent, 0, 0);
+ of_node_put(parent);
+ ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &v4l2_ep);
+ of_node_put(ep);
+ if (ret)
+ return 0;
+
+ lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
+
+ return tegra_vi_channel_alloc(vi, port_num, port, lanes);
+}
+
static int tegra_vi_channels_alloc(struct tegra_vi *vi)
{
struct device_node *node = vi->dev->of_node;
- struct device_node *ep = NULL;
struct device_node *ports;
- struct device_node *port = NULL;
- unsigned int port_num;
- struct device_node *parent;
- struct v4l2_fwnode_endpoint v4l2_ep = { .bus_type = 0 };
- unsigned int lanes;
- int err;
+ struct device_node *port;
int ret = 0;
ports = of_get_child_by_name(node, "ports");
@@ -1265,46 +1302,15 @@ static int tegra_vi_channels_alloc(struct tegra_vi *vi)
return dev_err_probe(vi->dev, -ENODEV, "%pOF: missing 'ports' node\n", node);
for_each_child_of_node(ports, port) {
- if (!of_node_name_eq(port, "port"))
- continue;
-
- err = of_property_read_u32(port, "reg", &port_num);
- if (err < 0)
- continue;
-
- if (port_num > vi->soc->vi_max_channels) {
- dev_err(vi->dev, "invalid port num %d for %pOF\n",
- port_num, port);
- ret = -EINVAL;
- goto cleanup;
+ ret = tegra_vi_port_channel_alloc(vi, port);
+ if (ret) {
+ of_node_put(port);
+ break;
}
-
- ep = of_get_child_by_name(port, "endpoint");
- if (!ep)
- continue;
-
- parent = of_graph_get_remote_port_parent(ep);
- of_node_put(ep);
- if (!parent)
- continue;
-
- ep = of_graph_get_endpoint_by_regs(parent, 0, 0);
- of_node_put(parent);
- err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep),
- &v4l2_ep);
- of_node_put(ep);
- if (err)
- continue;
-
- lanes = v4l2_ep.bus.mipi_csi2.num_data_lanes;
- ret = tegra_vi_channel_alloc(vi, port_num, port, lanes);
- if (ret < 0)
- goto cleanup;
}
-cleanup:
- of_node_put(port);
of_node_put(ports);
+
return ret;
}
diff --git a/include/dt-bindings/media/video-interface-devices.h b/include/dt-bindings/media/video-interface-devices.h
new file mode 100644
index 000000000000..d2340b457292
--- /dev/null
+++ b/include/dt-bindings/media/video-interface-devices.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR MIT) */
+/*
+ * Copyright (C) 2026 Kieran Bingham <kieran.bingham@ideasonboard.com>
+ */
+
+#ifndef __DT_BINDINGS_MEDIA_VIDEO_INTERFACE_DEVICES_H__
+#define __DT_BINDINGS_MEDIA_VIDEO_INTERFACE_DEVICES_H__
+
+#define MEDIA_ORIENTATION_FRONT 0
+#define MEDIA_ORIENTATION_BACK 1
+#define MEDIA_ORIENTATION_EXTERNAL 2
+
+#endif /* __DT_BINDINGS_MEDIA_VIDEO_INTERFACE_DEVICES_H__ */
diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h
index a73841dfae27..b1040e36deb8 100644
--- a/include/linux/platform_data/x86/int3472.h
+++ b/include/linux/platform_data/x86/int3472.h
@@ -25,6 +25,8 @@
#define INT3472_GPIO_TYPE_RESET 0x00
#define INT3472_GPIO_TYPE_POWERDOWN 0x01
#define INT3472_GPIO_TYPE_STROBE 0x02
+#define INT3472_GPIO_TYPE_POWER0 0x07
+#define INT3472_GPIO_TYPE_POWER1 0x08
#define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b
#define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c
#define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d
diff --git a/include/linux/property.h b/include/linux/property.h
index 907c790a3f01..9143fe4f5c05 100644
--- a/include/linux/property.h
+++ b/include/linux/property.h
@@ -547,6 +547,11 @@ unsigned int fwnode_graph_get_endpoint_count(const struct fwnode_handle *fwnode,
for (child = fwnode_graph_get_next_endpoint(fwnode, NULL); child; \
child = fwnode_graph_get_next_endpoint(fwnode, child))
+#define fwnode_graph_for_each_endpoint_scoped(fwnode, child) \
+ for (struct fwnode_handle *child __free(fwnode_handle) = \
+ fwnode_graph_get_next_endpoint(fwnode, NULL); \
+ child; child = fwnode_graph_get_next_endpoint(fwnode, child))
+
int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint);
diff --git a/include/media/ipu-bridge.h b/include/media/ipu-bridge.h
index 16fac765456e..760f0767ce58 100644
--- a/include/media/ipu-bridge.h
+++ b/include/media/ipu-bridge.h
@@ -17,13 +17,28 @@
#define IPU_SENSOR_ROTATION_NORMAL 0
#define IPU_SENSOR_ROTATION_INVERTED 1
-#define IPU_SENSOR_CONFIG(_HID, _NR, ...) \
- (const struct ipu_sensor_config) { \
- .hid = _HID, \
- .nr_link_freqs = _NR, \
- .link_freqs = { __VA_ARGS__ } \
+/* Flags for struct ipu_sensor_config */
+/* The sensor's CSI-2 transmitter needs a non-continuous clock */
+#define IPU_BR_FL_CSI2_CLK_NONCONTINUOUS BIT(0)
+
+/*
+ * Sensor config specific to one or more IPUs, identified by their PCI product
+ * IDs, with flags describing what the sensor needs there. Entries for one HID
+ * must be adjacent in ipu_supported_sensors[], with the IPU-specific ones
+ * before the generic one.
+ */
+#define IPU_SENSOR_CONFIG_MATCH_FL(_HID, _IDS, _FLAGS, _NR, ...) \
+ (const struct ipu_sensor_config) { \
+ .hid = _HID, \
+ .pci_ids = _IDS, \
+ .flags = _FLAGS, \
+ .nr_link_freqs = _NR, \
+ .link_freqs = { __VA_ARGS__ } \
}
+#define IPU_SENSOR_CONFIG(_HID, _NR, ...) \
+ IPU_SENSOR_CONFIG_MATCH_FL(_HID, NULL, 0, _NR, __VA_ARGS__)
+
#define NODE_SENSOR(_HID, _PROPS) \
(const struct software_node) { \
.name = _HID, \
@@ -64,6 +79,24 @@ enum ipu_sensor_swnodes {
SWNODE_COUNT
};
+enum ipu_bridge_ep_props {
+ IPU_BRIDGE_EP_BUS_TYPE,
+ IPU_BRIDGE_EP_DATA_LANES,
+ IPU_BRIDGE_EP_REMOTE_EP,
+ IPU_BRIDGE_EP_LINK_FREQUENCIES,
+ IPU_BRIDGE_EP_CLOCK_NONCONTINUOUS,
+ IPU_BRIDGE_EP_NUM_OF,
+ IPU_BRIDGE_EP_NUM_ENTRIES
+};
+
+/*
+ * Get the index of the next property in a property array, with a given maximum
+ * value.
+ */
+#define IPU_BRIDGE_NEXT_PROPERTY(index, max) \
+ (WARN_ON((index) > IPU_BRIDGE_##max) ? \
+ IPU_BRIDGE_##max : (index)++)
+
/* Data representation as it is in ACPI SSDB buffer */
struct ipu_sensor_ssdb {
u8 version;
@@ -115,6 +148,9 @@ struct ipu_node_names {
struct ipu_sensor_config {
const char *hid;
+ /* Zero-terminated list of IPU PCI product IDs, NULL for any IPU */
+ const u16 *pci_ids;
+ const u32 flags;
const u8 nr_link_freqs;
const u64 link_freqs[MAX_NUM_LINK_FREQS];
};
@@ -141,7 +177,7 @@ struct ipu_sensor {
const char *vcm_type;
struct ipu_property_names prop_names;
- struct property_entry ep_properties[5];
+ struct property_entry ep_properties[IPU_BRIDGE_EP_NUM_ENTRIES];
struct property_entry dev_properties[5];
struct property_entry ipu_properties[3];
struct property_entry ivsc_properties[1];
@@ -160,6 +196,8 @@ typedef int (*ipu_parse_sensor_fwnode_t)(struct acpi_device *adev,
struct ipu_bridge {
struct device *dev;
+ /* PCI product ID of the IPU, 0 if it is not a PCI device */
+ u16 pci_id;
ipu_parse_sensor_fwnode_t parse_sensor_fwnode;
char ipu_node_name[ACPI_ID_LEN];
struct software_node ipu_hid_node;
@@ -169,11 +207,13 @@ struct ipu_bridge {
};
#if IS_ENABLED(CONFIG_IPU_BRIDGE)
+struct pci_dev *ipu_bridge_get_ipu6(void);
int ipu_bridge_init(struct device *dev,
ipu_parse_sensor_fwnode_t parse_sensor_fwnode);
int ipu_bridge_parse_ssdb(struct acpi_device *adev, struct ipu_sensor *sensor);
int ipu_bridge_instantiate_vcm(struct device *sensor);
#else
+static inline struct pci_dev *ipu_bridge_get_ipu6(void) { return NULL; }
/* Use a define to avoid the @parse_sensor_fwnode argument getting evaluated */
#define ipu_bridge_init(dev, parse_sensor_fwnode) (0)
static inline int ipu_bridge_instantiate_vcm(struct device *s) { return 0; }
diff --git a/include/media/ipu6-pci-table.h b/include/media/ipu6-pci-table.h
index 0899d9d2f978..cacb8a3170d0 100644
--- a/include/media/ipu6-pci-table.h
+++ b/include/media/ipu6-pci-table.h
@@ -14,6 +14,8 @@
#define PCI_DEVICE_ID_INTEL_IPU6EP_ADLN 0x462e
#define PCI_DEVICE_ID_INTEL_IPU6EP_RPLP 0xa75d
#define PCI_DEVICE_ID_INTEL_IPU6EP_MTL 0x7d19
+#define PCI_DEVICE_ID_INTEL_IPU7 0x645d
+#define PCI_DEVICE_ID_INTEL_IPU7P5 0xb05d
static const struct pci_device_id ipu6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6) },
@@ -22,6 +24,8 @@ static const struct pci_device_id ipu6_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_ADLN) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_RPLP) },
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU6EP_MTL) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7) },
+ { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_IPU7P5) },
{ }
};
diff --git a/include/media/rc-map.h b/include/media/rc-map.h
index d95ed3e96de2..f167c37179c8 100644
--- a/include/media/rc-map.h
+++ b/include/media/rc-map.h
@@ -148,7 +148,6 @@ struct rc_map_table {
* @scan: pointer to struct &rc_map_table
* @size: Max number of entries
* @len: Number of entries that are in use
- * @alloc: size of \*scan, in bytes
* @rc_proto: type of the remote controller protocol, as defined at
* enum &rc_proto
* @name: name of the key map table
@@ -158,7 +157,6 @@ struct rc_map {
struct rc_map_table *scan;
unsigned int size;
unsigned int len;
- unsigned int alloc;
enum rc_proto rc_proto;
const char *name;
spinlock_t lock;
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index edd416178c33..33f5713734cb 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -554,15 +554,80 @@ static inline bool v4l2_is_format_bayer(const struct v4l2_format_info *f)
const struct v4l2_format_info *v4l2_format_info(u32 format);
void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
const struct v4l2_frmsize_stepwise *frmsize);
-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 */
+
+/**
+ * v4l2_fill_pixfmt_aligned - Fill in a &struct v4l2_pix_format with stride
+ * alignment requirements
+ *
+ * @pixfmt: pointer to the &struct v4l2_pix_format to be filled
+ * @pixelformat: the V4L2 pixel format (V4L2_PIX_FMT_*)
+ * @width: image width in pixels
+ * @height: image height in pixels
+ * @stride_alignment: stride alignment in bytes, must be a power of 2
+ *
+ * Fills all fields of @pixfmt for the given pixel format, dimensions, and
+ * stride alignment. Only formats stored in a single memory plane are
+ * supported; returns -EINVAL for multi-memory-plane formats.
+ *
+ * @pixfmt->bytesperline is set to the stride of the primary (plane 0) plane,
+ * rounded up to a multiple of @stride_alignment. For formats that store
+ * multiple component planes in a single memory buffer (e.g. YUV420), the
+ * alignment applied to each component plane's stride is scaled relative to
+ * @stride_alignment so that the chroma stride remains consistently derivable
+ * from the luma stride. @pixfmt->bytesperline therefore reflects only the
+ * primary plane stride.
+ *
+ * @pixfmt->sizeimage is set to the total size in bytes of all planes.
+ *
+ * Return: 0 on success, -EINVAL if @pixelformat is unknown or uses multiple
+ * memory planes.
+ */
+int v4l2_fill_pixfmt_aligned(struct v4l2_pix_format *pixfmt, u32 pixelformat,
+ u32 width, u32 height, u8 stride_alignment);
+
+static inline int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt,
+ u32 pixelformat, u32 width, u32 height)
+{
+ return v4l2_fill_pixfmt_aligned(pixfmt, pixelformat, width, height, 1);
+}
+
+/**
+ * v4l2_fill_pixfmt_mp_aligned - Fill in a &struct v4l2_pix_format_mplane with
+ * stride alignment requirements.
+ *
+ * @pixfmt: pointer to the &struct v4l2_pix_format_mplane to be filled
+ * @pixelformat: the V4L2 pixel format (V4L2_PIX_FMT_*)
+ * @width: image width in pixels
+ * @height: image height in pixels
+ * @stride_alignment: stride alignment in bytes; must be a power of 2
+ *
+ * Fills all fields of @pixfmt for the given pixel format, dimensions, and
+ * stride alignment.
+ *
+ * For formats stored in a single memory plane (mem_planes == 1), the
+ * behaviour matches v4l2_fill_pixfmt_aligned(): plane_fmt[0].bytesperline
+ * is set to the primary plane stride. The strides of all components are
+ * aligned to the @stride_alignment. To keep the chroma strides consistently
+ * derivable from the luma stride, strides may be aligned to a multiple of
+ * the @stride_alignment instead. plane_fmt[0].sizeimage covers all
+ * component planes.
+ *
+ * For formats with multiple memory planes (mem_planes > 1), each plane's
+ * bytesperline is independently rounded up to @stride_alignment, and each
+ * plane's sizeimage is set to bytesperline multiplied by the plane height.
+ *
+ * Return: 0 on success, -EINVAL if @pixelformat is unknown.
+ */
int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt,
u32 pixelformat, u32 width, u32 height,
u8 stride_alignment);
+static inline 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);
+}
+
/**
* v4l2_get_link_freq - Get link rate from transmitter
*
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 327976b14d50..cec9217d97ac 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -834,6 +834,9 @@ bool v4l2_ctrl_radio_filter(const struct v4l2_ctrl *ctrl);
*
* @ncontrols: The number of controls in this cluster.
* @controls: The cluster control array of size @ncontrols.
+ *
+ * If controls[0] is NULL, then this function does nothing and just
+ * returns.
*/
void v4l2_ctrl_cluster(unsigned int ncontrols, struct v4l2_ctrl **controls);
@@ -845,6 +848,9 @@ void v4l2_ctrl_cluster(unsigned int ncontrols, struct v4l2_ctrl **controls);
* @ncontrols: The number of controls in this cluster.
* @controls: The cluster control array of size @ncontrols. The first control
* must be the 'auto' control (e.g. autogain, autoexposure, etc.)
+ *
+ * If controls[0] is NULL, then this function does nothing and just
+ * returns.
* @manual_val: The value for the first control in the cluster that equals the
* manual setting.
* @set_volatile: If true, then all controls except the first auto control will
diff --git a/include/media/v4l2-dv-timings.h b/include/media/v4l2-dv-timings.h
index 2b42e5d81f9e..de5ef825a6c6 100644
--- a/include/media/v4l2-dv-timings.h
+++ b/include/media/v4l2-dv-timings.h
@@ -252,6 +252,20 @@ v4l2_hdmi_rx_colorimetry(const struct hdmi_avi_infoframe *avi,
const struct hdmi_vendor_infoframe *hdmi,
unsigned int height);
+/*
+ * The time in milliseconds that the HPD should be pulled low when writing
+ * a new EDID. This will tell the HDMI source that the EDID was changed and
+ * that it has to be re-read.
+ *
+ * The source is supposed to re-read the EDID if the HPD is low for more than
+ * 100 ms, but in practice the sink should pull it low for a bit longer due
+ * to clock differences and imprecise video source implementations.
+ *
+ * Practice has shown that setting the delay to HZ / 7 (approx 143 ms) works
+ * well.
+ */
+#define V4L2_SET_EDID_HPD_LOW_JIFFIES (HZ / 7)
+
unsigned int v4l2_num_edid_blocks(const u8 *edid, unsigned int max_blocks);
u16 v4l2_get_edid_phys_addr(const u8 *edid, unsigned int size,
unsigned int *offset);
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index d256b7ec8f84..5796e52678b9 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -735,6 +735,14 @@ struct v4l2_subdev_state {
};
/**
+ * struct v4l2_subdev_client_info - Sub-device client information
+ * @caps: bitmask of ``V4L2_SUBDEV_CLIENT_CAP_*``
+ */
+struct v4l2_subdev_client_info {
+ u64 caps;
+};
+
+/**
* struct v4l2_subdev_pad_ops - v4l2-subdev pad level operations
*
* @enum_mbus_code: callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE() ioctl handler
@@ -747,11 +755,14 @@ struct v4l2_subdev_state {
*
* @get_fmt: callback for VIDIOC_SUBDEV_G_FMT() ioctl handler code.
*
- * @set_fmt: callback for VIDIOC_SUBDEV_S_FMT() ioctl handler code.
+ * @set_fmt: callback for VIDIOC_SUBDEV_S_FMT() ioctl handler code. The ci
+ * pointer may be NULL for in-kernel calls.
*
* @get_selection: callback for VIDIOC_SUBDEV_G_SELECTION() ioctl handler code.
+ * The ci pointer may be NULL for in-kernel calls.
*
* @set_selection: callback for VIDIOC_SUBDEV_S_SELECTION() ioctl handler code.
+ * The ci pointer may be NULL for in-kernel calls.
*
* @get_frame_interval: callback for VIDIOC_SUBDEV_G_FRAME_INTERVAL()
* ioctl handler code.
@@ -814,6 +825,11 @@ struct v4l2_subdev_state {
* V4L2_SUBDEV_CAP_STREAMS sub-device capability flag can ignore the mask
* argument.
*
+ * Due to device constraints, starting the requested streams may result in
+ * additional streams also being started by the driver. Streams that are
+ * started and stopped together due to the nature of the hardware are
+ * called a stream group.
+ *
* @disable_streams: Disable the streams defined in streams_mask on the given
* source pad. Subdevs that implement this operation must use the active
* state management provided by the subdev core (enabled through a call to
@@ -823,6 +839,9 @@ struct v4l2_subdev_state {
* Drivers that support only a single stream without setting the
* V4L2_SUBDEV_CAP_STREAMS sub-device capability flag can ignore the mask
* argument.
+ *
+ * When the requested stream are part of a stream group, they will be
+ * stopped once all streams in the group are stopped.
*/
struct v4l2_subdev_pad_ops {
int (*enum_mbus_code)(struct v4l2_subdev *sd,
@@ -838,12 +857,15 @@ struct v4l2_subdev_pad_ops {
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format);
int (*set_fmt)(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format);
int (*get_selection)(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel);
int (*set_selection)(struct v4l2_subdev *sd,
+ const struct v4l2_subdev_client_info *ci,
struct v4l2_subdev_state *state,
struct v4l2_subdev_selection *sel);
int (*get_frame_interval)(struct v4l2_subdev *sd,
@@ -1122,14 +1144,14 @@ struct v4l2_subdev {
* @vfh: pointer to &struct v4l2_fh
* @state: pointer to &struct v4l2_subdev_state
* @owner: module pointer to the owner of this file handle
- * @client_caps: bitmask of ``V4L2_SUBDEV_CLIENT_CAP_*``
+ * @ci: sub-device client info related to this file handle
*/
struct v4l2_subdev_fh {
struct v4l2_fh vfh;
struct module *owner;
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
struct v4l2_subdev_state *state;
- u64 client_caps;
+ struct v4l2_subdev_client_info ci;
#endif
};
@@ -1935,14 +1957,16 @@ extern const struct v4l2_subdev_ops v4l2_subdev_call_wrappers;
int __result; \
if (!__sd) \
__result = -ENODEV; \
- else if (!(__sd->ops->o && __sd->ops->o->f)) \
+ else if (!__sd->ops->o) \
__result = -ENOIOCTLCMD; \
else if (v4l2_subdev_call_wrappers.o && \
v4l2_subdev_call_wrappers.o->f) \
__result = v4l2_subdev_call_wrappers.o->f( \
__sd, ##args); \
- else \
+ else if (__sd->ops->o->f) \
__result = __sd->ops->o->f(__sd, ##args); \
+ else \
+ __result = -ENOIOCTLCMD; \
__result; \
})
diff --git a/include/uapi/linux/it6625.h b/include/uapi/linux/it6625.h
new file mode 100644
index 000000000000..a895658aaf6a
--- /dev/null
+++ b/include/uapi/linux/it6625.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Controls header for IT6625/IT6626 driver
+ */
+
+#ifndef _UAPI_LINUX_IT6625_H
+#define _UAPI_LINUX_IT6625_H
+
+#include <linux/v4l2-controls.h>
+
+/*
+ * Currently detected HDMI audio sampling rate, in Hz. Read-only.
+ * 0 means the rate is unavailable/unknown: no audio is currently
+ * present on the input, the hardware reported a sample-rate id this
+ * driver doesn't recognize, or the status read itself failed. Never a
+ * literal 0 Hz sample rate.
+ */
+#define V4L2_CID_IT6625_AUDIO_SAMPLING_RATE (V4L2_CID_USER_IT6625_BASE + 0)
+
+/*
+ * Whether HDMI audio is currently present on the input. Read-only.
+ */
+#define V4L2_CID_IT6625_AUDIO_PRESENT (V4L2_CID_USER_IT6625_BASE + 1)
+
+#endif /* _UAPI_LINUX_IT6625_H */
diff --git a/include/uapi/linux/media/st/dcmipp_config.h b/include/uapi/linux/media/st/dcmipp_config.h
new file mode 100644
index 000000000000..ad8d146820ea
--- /dev/null
+++ b/include/uapi/linux/media/st/dcmipp_config.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * ST DCMIPP Driver - Userspace API
+ *
+ * Copyright (C) STMicroelectronics SA 2026
+ */
+
+#ifndef __UAPI_DCMIPP_CONFIG_H
+#define __UAPI_DCMIPP_CONFIG_H
+
+#include <linux/types.h>
+#include <linux/v4l2-controls.h>
+
+#define V4L2_CID_DCMIPP_PIXELPROC_GAMMA_CORRECTION_ENABLE (V4L2_CID_USER_DCMIPP_BASE + 0x0)
+
+#endif /* __UAPI_DCMIPP_CONFIG_H */
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index affec0ab4781..d17e41d51d2e 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -123,6 +123,15 @@ enum v4l2_colorfx {
#define V4L2_CID_USER_MEYE_BASE (V4L2_CID_USER_BASE + 0x1000)
#endif
+/*
+ * The base for the vim2m driver controls.
+ * We reserve 16 controls for this driver.
+ * The vim2m control range clashed with the meye control range, which was not
+ * intended, but since the meye driver has been removed, we can just keep the
+ * vim2m control range.
+ */
+#define V4L2_CID_USER_VIM2M_BASE (V4L2_CID_USER_BASE + 0x1000)
+
/* The base for the bttv driver controls.
* We reserve 32 controls for this driver. */
#define V4L2_CID_USER_BTTV_BASE (V4L2_CID_USER_BASE + 0x1010)
@@ -234,6 +243,18 @@ enum v4l2_colorfx {
*/
#define V4L2_CID_USER_MALI_C55_BASE (V4L2_CID_USER_BASE + 0x1230)
+/*
+ * The base for the ST DCMIPP driver controls.
+ * We reserve 16 controls for this driver
+ */
+#define V4L2_CID_USER_DCMIPP_BASE (V4L2_CID_USER_BASE + 0x1240)
+
+/*
+ * The base for IT6625/IT6626 driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_IT6625_BASE (V4L2_CID_USER_BASE + 0x1250)
+
/* MPEG-class control IDs */
/* The MPEG controls are applicable to all codec controls
* and the 'MPEG' part of the define is historical */